on master help sign in
Detect workspace root using language markers (#1370)

* Detect workspace root using language markers * Avoid allocating root_markers * Update helix-core/src/lib.rs Co-authored-by: Blaž Hrastnik <blaz@mxxn.io> * Update helix-core/src/lib.rs Co-authored-by: Kirawi <67773714+kirawi@users.noreply.github.com> Co-authored-by: Blaž Hrastnik <blaz@mxxn.io> Co-authored-by: Kirawi <67773714+kirawi@users.noreply.github.com>

commit8a019b423f5d51bcfc107da9cb50aa90eacff19d authorAlexis Mousset <contact@amousset.me> dateFri 31 Dec 2021, 09:06 (5 years ago) parent8fda87a
5 files changed+35−9
helix-core/src/lib.rs+26−6 helix-lsp/src/client.rs+5−1 helix-lsp/src/lib.rs+1 helix-term/src/commands.rs+2−1 languages.toml+1−1
helix-core/src/lib.rs+26−6
@@ -39,8 +39,14 @@ pub fn find_first_non_whitespace_char(line: RopeSlice) -> Option<usize> {
3939 line.chars().position(|ch| !ch.is_whitespace())
4040}
4141
42−/// Find `.git` root.
43−pub fn find_root(root: Option<&str>) -> Option<std::path::PathBuf> {
42+/// Find project root.
43+///
44+/// Order of detection:
45+/// * Top-most folder containing a root marker in current git repository
46+/// * Git repostory root if no marker detected
47+/// * Top-most folder containing a root marker if not git repository detected
48+/// * Current working directory as fallback
49+pub fn find_root(root: Option<&str>, root_markers: &[String]) -> Option<std::path::PathBuf> {
4450 let current_dir = std::env::current_dir().expect("unable to determine current directory");
4551
4652 let root = match root {
@@ -52,16 +58,30 @@ pub fn find_root(root: Option<&str>) -> Option<std::path::PathBuf> {
5258 current_dir.join(root)
5359 }
5460 }
55− None => current_dir,
61+ None => current_dir.clone(),
5662 };
5763
64+ let mut top_marker = None;
5865 for ancestor in root.ancestors() {
59− // TODO: also use defined roots if git isn't found
66+ for marker in root_markers {
67+ if ancestor.join(marker).exists() {
68+ top_marker = Some(ancestor);
69+ break;
70+ }
71+ }
72+ // don't go higher than repo
6073 if ancestor.join(".git").is_dir() {
61− return Some(ancestor.to_path_buf());
74+ // Use workspace if detected from marker
75+ return Some(top_marker.unwrap_or(ancestor).to_path_buf());
6276 }
6377 }
64− None
78+
79+ // In absence of git repo, use workspace if detected
80+ if top_marker.is_some() {
81+ top_marker.map(|a| a.to_path_buf())
82+ } else {
83+ Some(current_dir)
84+ }
6585}
6686
6787pub fn runtime_dir() -> std::path::PathBuf {
helix-lsp/src/client.rs+5−1
@@ -31,6 +31,7 @@ pub struct Client {
3131 pub(crate) capabilities: OnceCell<lsp::ServerCapabilities>,
3232 offset_encoding: OffsetEncoding,
3333 config: Option<Value>,
34+ root_markers: Vec<String>,
3435}
3536
3637impl Client {
@@ -39,6 +40,7 @@ impl Client {
3940 cmd: &str,
4041 args: &[String],
4142 config: Option<Value>,
43+ root_markers: Vec<String>,
4244 id: usize,
4345 ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc<Notify>)> {
4446 let process = Command::new(cmd)
@@ -68,6 +70,7 @@ impl Client {
6870 capabilities: OnceCell::new(),
6971 offset_encoding: OffsetEncoding::Utf8,
7072 config,
73+ root_markers,
7174 };
7275
7376 Ok((client, server_rx, initialize_notify))
@@ -225,7 +228,8 @@ impl Client {
225228
226229 pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> {
227230 // TODO: delay any requests that are triggered prior to initialize
228− let root = find_root(None).and_then(|root| lsp::Url::from_file_path(root).ok());
231+ let root = find_root(None, &self.root_markers)
232+ .and_then(|root| lsp::Url::from_file_path(root).ok());
229233
230234 if self.config.is_some() {
231235 log::info!("Using custom LSP config: {}", self.config.as_ref().unwrap());
helix-lsp/src/lib.rs+1
@@ -326,6 +326,7 @@ impl Registry {
326326 &config.command,
327327 &config.args,
328328 language_config.config.clone(),
329+ language_config.roots.clone(),
329330 id,
330331 )?;
331332 self.incoming.push(UnboundedReceiverStream::new(incoming));
helix-term/src/commands.rs+2−1
@@ -3154,7 +3154,8 @@ fn command_mode(cx: &mut Context) {
31543154}
31553155
31563156fn file_picker(cx: &mut Context) {
3157− let root = find_root(None).unwrap_or_else(|| PathBuf::from("./"));
3157+ // We don't specify language markers, root will be the root of the current git repo
3158+ let root = find_root(None, &[]).unwrap_or_else(|| PathBuf::from("./"));
31583159 let picker = ui::file_picker(root, &cx.editor.config);
31593160 cx.push_layer(Box::new(picker));
31603161}
languages.toml+1−1
@@ -3,7 +3,7 @@ name = "rust"
33scope = "source.rust"
44injection-regex = "rust"
55file-types = ["rs"]
6−roots = []
6+roots = ["Cargo.toml", "Cargo.lock"]
77auto-format = true
88comment-token = "//"
99language-server = { command = "rust-analyzer" }