From 026e8391e12246ae6a8e9a12bdf5029d30a5af86 Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Sat, 25 Dec 2021 18:04:31 +0100 Subject: [PATCH 1/4] Detect workspace root using language markers --- helix-core/src/lib.rs | 36 ++++++++++++++++++++++++++++++------ helix-lsp/src/client.rs | 6 +++++- helix-lsp/src/lib.rs | 1 + helix-term/src/commands.rs | 3 ++- languages.toml | 2 +- 5 files changed, 39 insertions(+), 9 deletions(-) diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs index 1c78e7c0ba65..83e7544c7f42 100644 --- a/helix-core/src/lib.rs +++ b/helix-core/src/lib.rs @@ -39,8 +39,14 @@ pub fn find_first_non_whitespace_char(line: RopeSlice) -> Option { line.chars().position(|ch| !ch.is_whitespace()) } -/// Find `.git` root. -pub fn find_root(root: Option<&str>) -> Option { +/// Find project root. +/// +/// Order of detection: +/// * Top-most folder containing a root marker in current git repository +/// * Git repostory root if no marker detected +/// * Top-most folder containing a root marker if not git repository detected +/// * Current working directory as fallback +pub fn find_root(root: Option<&str>, root_markers: Vec) -> Option { let current_dir = std::env::current_dir().expect("unable to determine current directory"); let root = match root { @@ -52,16 +58,34 @@ pub fn find_root(root: Option<&str>) -> Option { current_dir.join(root) } } - None => current_dir, + None => current_dir.clone(), }; + let mut top_marker = None; for ancestor in root.ancestors() { - // TODO: also use defined roots if git isn't found + for marker in &root_markers { + if ancestor.join(marker).exists() { + top_marker = Some(ancestor); + break; + } + } + // don't go higher than repo if ancestor.join(".git").is_dir() { - return Some(ancestor.to_path_buf()); + // Use workspace if detected from marker + if top_marker.is_some() { + return top_marker.map(|a| a.to_path_buf()); + } else { + return Some(ancestor.to_path_buf()); + } } } - None + + // In absence of git repo, use workspace if detected + if top_marker.is_some() { + top_marker.map(|a| a.to_path_buf()) + } else { + Some(current_dir) + } } pub fn runtime_dir() -> std::path::PathBuf { diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 43804daa97b4..8cf5fc3a03aa 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -31,6 +31,7 @@ pub struct Client { pub(crate) capabilities: OnceCell, offset_encoding: OffsetEncoding, config: Option, + root_markers: Vec, } impl Client { @@ -39,6 +40,7 @@ impl Client { cmd: &str, args: &[String], config: Option, + root_markers: Vec, id: usize, ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc)> { let process = Command::new(cmd) @@ -68,6 +70,7 @@ impl Client { capabilities: OnceCell::new(), offset_encoding: OffsetEncoding::Utf8, config, + root_markers, }; Ok((client, server_rx, initialize_notify)) @@ -225,7 +228,8 @@ impl Client { pub(crate) async fn initialize(&self) -> Result { // TODO: delay any requests that are triggered prior to initialize - let root = find_root(None).and_then(|root| lsp::Url::from_file_path(root).ok()); + let root = find_root(None, self.root_markers.clone()) + .and_then(|root| lsp::Url::from_file_path(root).ok()); if self.config.is_some() { log::info!("Using custom LSP config: {}", self.config.as_ref().unwrap()); diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 8fb321bcc202..1eb1c151ab0c 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -326,6 +326,7 @@ impl Registry { &config.command, &config.args, language_config.config.clone(), + language_config.roots.clone(), id, )?; self.incoming.push(UnboundedReceiverStream::new(incoming)); diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index ee6a59894abb..cb2523f97119 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3026,7 +3026,8 @@ fn command_mode(cx: &mut Context) { } fn file_picker(cx: &mut Context) { - let root = find_root(None).unwrap_or_else(|| PathBuf::from("./")); + // We don't specify language markers, root will be the root of the current git repo + let root = find_root(None, vec![]).unwrap_or_else(|| PathBuf::from("./")); let picker = ui::file_picker(root, &cx.editor.config); cx.push_layer(Box::new(picker)); } diff --git a/languages.toml b/languages.toml index 616ef234e401..aca638bf3aa3 100644 --- a/languages.toml +++ b/languages.toml @@ -3,7 +3,7 @@ name = "rust" scope = "source.rust" injection-regex = "rust" file-types = ["rs"] -roots = [] +roots = ["Cargo.toml", "Cargo.lock"] auto-format = true comment-token = "//" language-server = { command = "rust-analyzer" } From af0e64bb9e2375dfe458660d4cd08ead27bc7c79 Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Tue, 28 Dec 2021 10:06:22 +0100 Subject: [PATCH 2/4] Avoid allocating root_markers --- helix-core/src/lib.rs | 4 ++-- helix-lsp/src/client.rs | 2 +- helix-term/src/commands.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs index 83e7544c7f42..c2c244dc2058 100644 --- a/helix-core/src/lib.rs +++ b/helix-core/src/lib.rs @@ -46,7 +46,7 @@ pub fn find_first_non_whitespace_char(line: RopeSlice) -> Option { /// * Git repostory root if no marker detected /// * Top-most folder containing a root marker if not git repository detected /// * Current working directory as fallback -pub fn find_root(root: Option<&str>, root_markers: Vec) -> Option { +pub fn find_root(root: Option<&str>, root_markers: &[String]) -> Option { let current_dir = std::env::current_dir().expect("unable to determine current directory"); let root = match root { @@ -63,7 +63,7 @@ pub fn find_root(root: Option<&str>, root_markers: Vec) -> Option Result { // TODO: delay any requests that are triggered prior to initialize - let root = find_root(None, self.root_markers.clone()) + let root = find_root(None, &self.root_markers) .and_then(|root| lsp::Url::from_file_path(root).ok()); if self.config.is_some() { diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index cb2523f97119..97f38c4874f5 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3027,7 +3027,7 @@ fn command_mode(cx: &mut Context) { fn file_picker(cx: &mut Context) { // We don't specify language markers, root will be the root of the current git repo - let root = find_root(None, vec![]).unwrap_or_else(|| PathBuf::from("./")); + let root = find_root(None, &[]).unwrap_or_else(|| PathBuf::from("./")); let picker = ui::file_picker(root, &cx.editor.config); cx.push_layer(Box::new(picker)); } From f57a0abada0304489bb3d5ee88ad744552dc183b Mon Sep 17 00:00:00 2001 From: Alexis Mousset Date: Wed, 29 Dec 2021 10:21:10 +0100 Subject: [PATCH 3/4] Update helix-core/src/lib.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Blaž Hrastnik --- helix-core/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs index c2c244dc2058..c7ba87ff7ebc 100644 --- a/helix-core/src/lib.rs +++ b/helix-core/src/lib.rs @@ -72,11 +72,7 @@ pub fn find_root(root: Option<&str>, root_markers: &[String]) -> Option Date: Wed, 29 Dec 2021 17:27:39 +0100 Subject: [PATCH 4/4] Update helix-core/src/lib.rs Co-authored-by: Kirawi <67773714+kirawi@users.noreply.github.com> --- helix-core/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-core/src/lib.rs b/helix-core/src/lib.rs index c7ba87ff7ebc..7fd23b977e83 100644 --- a/helix-core/src/lib.rs +++ b/helix-core/src/lib.rs @@ -72,7 +72,7 @@ pub fn find_root(root: Option<&str>, root_markers: &[String]) -> Option