aboutsummaryrefslogtreecommitdiff
path: root/helix-lsp
diff options
context:
space:
mode:
Diffstat (limited to 'helix-lsp')
-rw-r--r--helix-lsp/src/client.rs12
-rw-r--r--helix-lsp/src/lib.rs56
2 files changed, 61 insertions, 7 deletions
diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs
index f93e5826..34e4c346 100644
--- a/helix-lsp/src/client.rs
+++ b/helix-lsp/src/client.rs
@@ -1,22 +1,22 @@
use crate::{
- jsonrpc,
+ find_root, jsonrpc,
transport::{Payload, Transport},
Call, Error, OffsetEncoding, Result,
};
-use helix_core::{find_root, ChangeSet, Rope};
+use helix_core::{ChangeSet, Rope};
use helix_loader::{self, VERSION_AND_GIT_HASH};
use lsp::PositionEncodingKind;
use lsp_types as lsp;
use serde::Deserialize;
use serde_json::Value;
-use std::collections::HashMap;
use std::future::Future;
use std::process::Stdio;
use std::sync::{
atomic::{AtomicU64, Ordering},
Arc,
};
+use std::{collections::HashMap, path::PathBuf};
use tokio::{
io::{BufReader, BufWriter},
process::{Child, Command},
@@ -49,6 +49,7 @@ impl Client {
config: Option<Value>,
server_environment: HashMap<String, String>,
root_markers: &[String],
+ manual_roots: &[PathBuf],
id: usize,
req_timeout: u64,
doc_path: Option<&std::path::PathBuf>,
@@ -77,8 +78,11 @@ impl Client {
Transport::start(reader, writer, stderr, id);
let root_path = find_root(
- doc_path.and_then(|x| x.parent().and_then(|x| x.to_str())),
+ doc_path
+ .and_then(|x| x.parent().and_then(|x| x.to_str()))
+ .unwrap_or("."),
root_markers,
+ manual_roots,
);
let root_uri = lsp::Url::from_file_path(root_path.clone()).ok();
diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs
index 5609a624..e4b00946 100644
--- a/helix-lsp/src/lib.rs
+++ b/helix-lsp/src/lib.rs
@@ -10,11 +10,15 @@ pub use lsp::{Position, Url};
pub use lsp_types as lsp;
use futures_util::stream::select_all::SelectAll;
-use helix_core::syntax::{LanguageConfiguration, LanguageServerConfiguration};
+use helix_core::{
+ find_workspace,
+ syntax::{LanguageConfiguration, LanguageServerConfiguration},
+};
use tokio::sync::mpsc::UnboundedReceiver;
use std::{
collections::{hash_map::Entry, HashMap},
+ path::PathBuf,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
@@ -641,6 +645,7 @@ impl Registry {
&mut self,
language_config: &LanguageConfiguration,
doc_path: Option<&std::path::PathBuf>,
+ root_dirs: &[PathBuf],
) -> Result<Option<Arc<Client>>> {
let config = match &language_config.language_server {
Some(config) => config,
@@ -656,7 +661,7 @@ impl Registry {
let id = self.counter.fetch_add(1, Ordering::Relaxed);
let NewClientResult(client, incoming) =
- start_client(id, language_config, config, doc_path)?;
+ start_client(id, language_config, config, doc_path, root_dirs)?;
self.incoming.push(UnboundedReceiverStream::new(incoming));
let (_, old_client) = entry.insert((id, client.clone()));
@@ -684,6 +689,7 @@ impl Registry {
&mut self,
language_config: &LanguageConfiguration,
doc_path: Option<&std::path::PathBuf>,
+ root_dirs: &[PathBuf],
) -> Result<Option<Arc<Client>>> {
let config = match &language_config.language_server {
Some(config) => config,
@@ -697,7 +703,7 @@ impl Registry {
let id = self.counter.fetch_add(1, Ordering::Relaxed);
let NewClientResult(client, incoming) =
- start_client(id, language_config, config, doc_path)?;
+ start_client(id, language_config, config, doc_path, root_dirs)?;
self.incoming.push(UnboundedReceiverStream::new(incoming));
entry.insert((id, client.clone()));
@@ -798,6 +804,7 @@ fn start_client(
config: &LanguageConfiguration,
ls_config: &LanguageServerConfiguration,
doc_path: Option<&std::path::PathBuf>,
+ root_dirs: &[PathBuf],
) -> Result<NewClientResult> {
let (client, incoming, initialize_notify) = Client::start(
&ls_config.command,
@@ -805,6 +812,7 @@ fn start_client(
config.config.clone(),
ls_config.environment.clone(),
&config.roots,
+ config.workspace_lsp_roots.as_deref().unwrap_or(root_dirs),
id,
ls_config.timeout,
doc_path,
@@ -842,6 +850,48 @@ fn start_client(
Ok(NewClientResult(client, incoming))
}
+/// Find an LSP root of a file using the following mechansim:
+/// * start at `file` (either an absolute path or relative to CWD)
+/// * find the top most directory containing a root_marker
+/// * inside the current workspace
+/// * stop the search at the first root_dir that contains `file` or the workspace (obtained from `helix_core::find_workspace`)
+/// * root_dirs only apply inside the workspace. For files outside of the workspace they are ignored
+/// * outside the current workspace: keep searching to the top of the file hiearchy
+pub fn find_root(file: &str, root_markers: &[String], root_dirs: &[PathBuf]) -> PathBuf {
+ let file = std::path::Path::new(file);
+ let workspace = find_workspace();
+ let file = if file.is_absolute() {
+ file.to_path_buf()
+ } else {
+ let current_dir = std::env::current_dir().expect("unable to determine current directory");
+ current_dir.join(file)
+ };
+
+ let inside_workspace = file.strip_prefix(&workspace).is_ok();
+
+ let mut top_marker = None;
+ for ancestor in file.ancestors() {
+ if root_markers
+ .iter()
+ .any(|marker| ancestor.join(marker).exists())
+ {
+ top_marker = Some(ancestor);
+ }
+
+ if inside_workspace
+ && (ancestor == workspace
+ || root_dirs
+ .iter()
+ .any(|root_dir| root_dir == ancestor.strip_prefix(&workspace).unwrap()))
+ {
+ return top_marker.unwrap_or(ancestor).to_owned();
+ }
+ }
+
+ // If no root was found use the workspace as a fallback
+ workspace
+}
+
#[cfg(test)]
mod tests {
use super::{lsp, util::*, OffsetEncoding};