oak_lsp/
workspace.rs

1use crate::types::InitializeParams;
2use dashmap::DashMap;
3use std::path::PathBuf;
4use url::Url;
5
6/// A manager for workspace folders and path resolution.
7pub struct WorkspaceManager {
8    folders: DashMap<String, PathBuf>,
9}
10
11impl WorkspaceManager {
12    pub fn new() -> Self {
13        Self { folders: DashMap::new() }
14    }
15
16    /// Initialize the workspace manager with parameters from the client.
17    pub fn initialize(&self, params: &InitializeParams) {
18        if let Some(uri_str) = &params.root_uri {
19            if let Ok(uri) = Url::parse(uri_str) {
20                if let Ok(path) = uri.to_file_path() {
21                    self.folders.insert(uri_str.clone(), path);
22                }
23            }
24        }
25
26        for folder in &params.workspace_folders {
27            if let Ok(uri) = Url::parse(&folder.uri) {
28                if let Ok(path) = uri.to_file_path() {
29                    self.folders.insert(folder.uri.clone(), path);
30                }
31            }
32        }
33    }
34
35    /// Add a workspace folder.
36    pub fn add_folder(&self, uri: String, _name: String) {
37        if let Ok(url) = Url::parse(&uri) {
38            if let Ok(path) = url.to_file_path() {
39                self.folders.insert(uri, path);
40            }
41        }
42    }
43
44    /// Remove a workspace folder.
45    pub fn remove_folder(&self, uri: &str) {
46        self.folders.remove(uri);
47    }
48
49    /// Get the local path for a URI if it's within a workspace folder.
50    pub fn get_path(&self, uri: &str) -> Option<PathBuf> {
51        if let Ok(url) = Url::parse(uri) {
52            if let Ok(path) = url.to_file_path() {
53                return Some(path);
54            }
55        }
56        None
57    }
58
59    /// Convert a local path to a file URI.
60    pub fn path_to_uri(&self, path: PathBuf) -> Option<String> {
61        Url::from_file_path(path).ok().map(|url| url.to_string())
62    }
63
64    /// Find the workspace folder URI that contains the given URI.
65    pub fn find_root(&self, uri: &str) -> Option<String> {
66        let path = self.get_path(uri)?;
67        let mut best_match: Option<(String, usize)> = None;
68
69        for entry in self.folders.iter() {
70            let folder_uri = entry.key();
71            let folder_path = entry.value();
72
73            if path.starts_with(folder_path) {
74                let len = folder_path.as_os_str().len();
75                if best_match.as_ref().map_or(true, |(_, best_len)| len > *best_len) {
76                    best_match = Some((folder_uri.clone(), len));
77                }
78            }
79        }
80
81        best_match.map(|(uri, _)| uri)
82    }
83
84    /// Check if a URI is within any workspace folder.
85    pub fn is_within_workspace(&self, uri: &str) -> bool {
86        self.find_root(uri).is_some()
87    }
88
89    /// List all workspace folders.
90    pub fn list_folders(&self) -> Vec<(String, PathBuf)> {
91        self.folders.iter().map(|entry| (entry.key().clone(), entry.value().clone())).collect()
92    }
93}
94
95impl Default for WorkspaceManager {
96    fn default() -> Self {
97        Self::new()
98    }
99}