Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

project: Allow running multiple instances of a single language server within a single worktree #22182

Open
wants to merge 76 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
db69230
Checkpoint
osiewicz Dec 13, 2024
5db0092
WIP, move ProjectTree into project
osiewicz Dec 14, 2024
bf8bbdc
Checkpoint
osiewicz Dec 16, 2024
c90753c
Checkpoint
osiewicz Dec 16, 2024
d55b992
Checkpoint
osiewicz Dec 17, 2024
95a9211
Checkpoint
osiewicz Dec 17, 2024
4e89e26
Start wiring LSPTree into LspStore
osiewicz Dec 17, 2024
d6776ba
:bug:
osiewicz Dec 17, 2024
d687d4b
Remove dbg!s
osiewicz Dec 18, 2024
cd15dac
Shredding for the new path trie
osiewicz Dec 19, 2024
78d908d
Merge branch 'main' into lsp-tree
osiewicz Dec 20, 2024
0c5f07c
Merge branch 'main' into lsp-tree
osiewicz Dec 20, 2024
909fb66
Renames
osiewicz Dec 23, 2024
cb24881
Add labels to pathtrie
osiewicz Dec 23, 2024
96cac12
Checkpoint, start wiring in pathtrie
osiewicz Dec 23, 2024
2a8bcc2
Checkpoint
osiewicz Dec 23, 2024
570839e
Document label presence
osiewicz Dec 23, 2024
b72fe37
Merge branch 'main' into lsp-tree
osiewicz Dec 25, 2024
2c77faf
Store known nodes in a tree
osiewicz Dec 26, 2024
89fa8df
Merge branch 'main' into lsp-tree
osiewicz Dec 27, 2024
bc79fea
Use lsp tree in language_servers_for_buffer
osiewicz Dec 27, 2024
e543059
Remove stray println
osiewicz Dec 27, 2024
b02fb3e
clippy
osiewicz Dec 27, 2024
3f6d00b
WIP
osiewicz Dec 28, 2024
2edaf52
Merge branch 'main' into lsp-tree
osiewicz Dec 30, 2024
64ef55d
Fix double invokation in ProjectTree::walk
osiewicz Dec 30, 2024
35338ce
Whitespace
osiewicz Dec 30, 2024
150cc03
Add partial support for changing workspace folders
osiewicz Dec 30, 2024
3524201
Split up server_id into two
osiewicz Dec 30, 2024
d3aad34
Use stored workspace folders in workspacefolder requests
osiewicz Dec 30, 2024
771da2f
Merge branch 'main' into lsp-tree
osiewicz Jan 2, 2025
39f5bfb
Add test for path trie
osiewicz Jan 2, 2025
b51c63c
Fix removing entries from path trie
osiewicz Jan 2, 2025
9cbd2c7
clippy
osiewicz Jan 2, 2025
e73c3ad
WIP
osiewicz Jan 2, 2025
4dc1565
Merge branch 'main' into lsp-tree
osiewicz Jan 4, 2025
79bec92
Checkpoint
osiewicz Jan 4, 2025
ac5dc48
Merge branch 'main' into lsp-tree
osiewicz Jan 7, 2025
37342d7
WIP, retract tracking root language servers on LspStore
osiewicz Jan 7, 2025
64553c7
Pass language server id in workspace symbols
osiewicz Jan 8, 2025
9a7099d
Compiling again with a bunch of TODOs sprinkled over
osiewicz Jan 8, 2025
ef29f3b
Get rid of path in RPC
osiewicz Jan 8, 2025
2818b3e
Send out workspace folder change notification only when the list of f…
osiewicz Jan 8, 2025
f9b4530
Merge branch 'main' into lsp-tree
osiewicz Jan 9, 2025
d12db9c
Compiling - again; (queries lsp adapters for paths to the root of a w…
osiewicz Jan 9, 2025
0d2ebfe
Remove a bunch of dbgs
osiewicz Jan 9, 2025
8497f5e
Merge branch 'main' into lsp-tree
osiewicz Jan 10, 2025
b5d0980
Query Rust adapter for worktree root paths
osiewicz Jan 11, 2025
5e4f9fb
Merge branch 'main' into lsp-tree
osiewicz Jan 13, 2025
d3750c5
Add rudimentary Rust project root detection, fix depth calculation fo…
osiewicz Jan 13, 2025
1cae50c
Remove unused import
osiewicz Jan 13, 2025
80a4ef0
Send didchangeworkspacefolders
osiewicz Jan 13, 2025
1fa944a
Allow one to save pending workspace folders even when server init is …
osiewicz Jan 13, 2025
5da7866
clippy
osiewicz Jan 13, 2025
b2242b2
Fix test build
osiewicz Jan 13, 2025
04c3858
fixup! Fix test build
osiewicz Jan 13, 2025
fceb0b5
clippy
osiewicz Jan 13, 2025
5d0b69b
clippy
osiewicz Jan 13, 2025
4aa02a4
WIP: settings refresh
osiewicz Jan 13, 2025
6fc1647
Disallow initializing the language server node if it is no longer a p…
osiewicz Jan 13, 2025
e2f89a4
Remove unused import
osiewicz Jan 13, 2025
fc0e189
Remove unused variable
osiewicz Jan 13, 2025
24cde09
fixup! Remove unused variable
osiewicz Jan 13, 2025
6d3e8aa
Add ProjectTreeEvent
osiewicz Jan 13, 2025
fb48b7b
:facepalm:
osiewicz Jan 13, 2025
0fa9656
Merge branch 'main' into lsp-tree
osiewicz Jan 13, 2025
b86add3
Merge branch 'main' into lsp-tree
osiewicz Jan 14, 2025
1acb514
Make server tree manage which adapters are queried
osiewicz Jan 14, 2025
6240663
Cleanups for clippy
osiewicz Jan 14, 2025
7cdebb6
Clippy and cleanups
osiewicz Jan 15, 2025
7b23497
fixup! Clippy and cleanups
osiewicz Jan 15, 2025
4420b53
clippy
osiewicz Jan 15, 2025
25edee6
clippy
osiewicz Jan 15, 2025
3a24cda
Come on clippy..
osiewicz Jan 15, 2025
42283ae
Make stop_local_language_server take just the server id as the argument
osiewicz Jan 15, 2025
019b285
Make Rust project finder consider outermost Cargo.toml, not the inner…
osiewicz Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions crates/copilot/src/copilot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -461,12 +461,14 @@ impl Copilot {
.on_notification::<StatusNotification, _>(|_, _| { /* Silence the notification */ })
.detach();

let initialize_params = None;
let configuration = lsp::DidChangeConfigurationParams {
settings: Default::default(),
};
let server = cx
.update(|cx| server.initialize(initialize_params, configuration.into(), cx))?
.update(|cx| {
let params = server.default_initialize_params(cx);
server.initialize(params, configuration.into(), cx)
})?
.await?;

let status = server
Expand Down
80 changes: 45 additions & 35 deletions crates/editor/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12336,28 +12336,27 @@ impl Editor {
cx.emit(SearchEvent::MatchesInvalidated);
if *singleton_buffer_edited {
if let Some(project) = &self.project {
let project = project.read(cx);
#[allow(clippy::mutable_key_type)]
let languages_affected = multibuffer
.read(cx)
.all_buffers()
.into_iter()
.filter_map(|buffer| {
let buffer = buffer.read(cx);
let language = buffer.language()?;
if project.is_local()
&& project
.language_servers_for_local_buffer(buffer, cx)
.count()
== 0
{
None
} else {
Some(language)
}
})
.cloned()
.collect::<HashSet<_>>();
let languages_affected = multibuffer.update(cx, |multibuffer, cx| {
multibuffer
.all_buffers()
.into_iter()
.filter_map(|buffer| {
buffer.update(cx, |buffer, cx| {
let language = buffer.language()?;
let should_discard = project.update(cx, |project, cx| {
project.is_local()
&& project.for_language_servers_for_local_buffer(
buffer,
|it| it.count() == 0,
cx,
)
});
should_discard.not().then_some(language.clone())
})
})
.collect::<HashSet<_>>()
});
if !languages_affected.is_empty() {
self.refresh_inlay_hints(
InlayHintRefreshReason::BufferEdited(languages_affected),
Expand Down Expand Up @@ -12910,15 +12909,18 @@ impl Editor {
self.handle_input(text, cx);
}

pub fn supports_inlay_hints(&self, cx: &AppContext) -> bool {
pub fn supports_inlay_hints(&self, cx: &mut AppContext) -> bool {
let Some(provider) = self.semantics_provider.as_ref() else {
return false;
};

let mut supports = false;
self.buffer().read(cx).for_each_buffer(|buffer| {
supports |= provider.supports_inlay_hints(buffer, cx);
self.buffer().update(cx, |this, cx| {
this.for_each_buffer(|buffer| {
supports |= provider.supports_inlay_hints(buffer, cx);
})
});

supports
}

Expand Down Expand Up @@ -13530,7 +13532,7 @@ pub trait SemanticsProvider {
cx: &mut AppContext,
) -> Option<Task<anyhow::Result<InlayHint>>>;

fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool;
fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &mut AppContext) -> bool;

fn document_highlights(
&self,
Expand Down Expand Up @@ -13915,17 +13917,25 @@ impl SemanticsProvider for Model<Project> {
}))
}

fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &mut AppContext) -> bool {
// TODO: make this work for remote projects
self.read(cx)
.language_servers_for_local_buffer(buffer.read(cx), cx)
.any(
|(_, server)| match server.capabilities().inlay_hint_provider {
Some(lsp::OneOf::Left(enabled)) => enabled,
Some(lsp::OneOf::Right(_)) => true,
None => false,
},
)
buffer.update(cx, |buffer, cx| {
self.update(cx, |this, cx| {
this.for_language_servers_for_local_buffer(
buffer,
|mut it| {
it.any(
|(_, server)| match server.capabilities().inlay_hint_provider {
Some(lsp::OneOf::Left(enabled)) => enabled,
Some(lsp::OneOf::Right(_)) => true,
None => false,
},
)
},
cx,
)
})
})
}

fn inlay_hints(
Expand Down
37 changes: 20 additions & 17 deletions crates/editor/src/lsp_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use multi_buffer::Anchor;

pub(crate) fn find_specific_language_server_in_selection<F>(
editor: &Editor,
cx: &WindowContext,
cx: &mut WindowContext,
filter_language: F,
language_server_name: &str,
) -> Option<(Anchor, Arc<Language>, LanguageServerId, Model<Buffer>)>
Expand All @@ -21,37 +21,40 @@ where
let Some(project) = &editor.project else {
return None;
};
let multibuffer = editor.buffer().read(cx);
let mut language_servers_for = HashMap::default();
editor
.selections
.disjoint_anchors()
.iter()
.filter(|selection| selection.start == selection.end)
.filter_map(|selection| Some((selection.start.buffer_id?, selection.start)))
.filter_map(|(buffer_id, trigger_anchor)| {
let buffer = multibuffer.buffer(buffer_id)?;
.find_map(|(buffer_id, trigger_anchor)| {
let buffer = editor.buffer().read(cx).buffer(buffer_id)?;
let server_id = *match language_servers_for.entry(buffer_id) {
Entry::Occupied(occupied_entry) => occupied_entry.into_mut(),
Entry::Vacant(vacant_entry) => {
let language_server_id = project
.read(cx)
.language_servers_for_local_buffer(buffer.read(cx), cx)
.find_map(|(adapter, server)| {
if adapter.name.0.as_ref() == language_server_name {
Some(server.server_id())
} else {
None
}
});
let language_server_id = buffer.update(cx, |buffer, cx| {
project.update(cx, |project, cx| {
project.for_language_servers_for_local_buffer(
buffer,
|mut it| {
it.find_map(|(adapter, server)| {
if adapter.name.0.as_ref() == language_server_name {
Some(server.server_id())
} else {
None
}
})
},
cx,
)
})
});
vacant_entry.insert(language_server_id)
}
}
.as_ref()?;

Some((buffer, trigger_anchor, server_id))
})
.find_map(|(buffer, trigger_anchor, server_id)| {
let language = buffer.read(cx).language_at(trigger_anchor.text_anchor)?;
if !filter_language(&language) {
return None;
Expand Down
2 changes: 1 addition & 1 deletion crates/editor/src/proposed_changes_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ impl SemanticsProvider for BranchBufferSemanticsProvider {
self.0.resolve_inlay_hint(hint, buffer, server_id, cx)
}

fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &AppContext) -> bool {
fn supports_inlay_hints(&self, buffer: &Model<Buffer>, cx: &mut AppContext) -> bool {
if let Some(buffer) = self.to_base(&buffer, &[], cx) {
self.0.supports_inlay_hints(&buffer, cx)
} else {
Expand Down
49 changes: 48 additions & 1 deletion crates/language/src/language.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use settings::WorktreeId;
use smol::future::FutureExt as _;
use std::num::NonZeroU32;
use std::{
any::Any,
ffi::OsStr,
Expand All @@ -61,6 +60,7 @@ use std::{
Arc, LazyLock,
},
};
use std::{num::NonZeroU32, sync::OnceLock};
use syntax_map::{QueryCursorHandle, SyntaxSnapshot};
use task::RunnableTag;
pub use task_context::{ContextProvider, RunnableRange};
Expand Down Expand Up @@ -163,6 +163,7 @@ pub struct CachedLspAdapter {
pub adapter: Arc<dyn LspAdapter>,
pub reinstall_attempt_count: AtomicU64,
cached_binary: futures::lock::Mutex<Option<LanguageServerBinary>>,
attach_kind: OnceLock<Attach>,
}

impl Debug for CachedLspAdapter {
Expand Down Expand Up @@ -198,6 +199,7 @@ impl CachedLspAdapter {
adapter,
cached_binary: Default::default(),
reinstall_attempt_count: AtomicU64::new(0),
attach_kind: Default::default(),
})
}

Expand Down Expand Up @@ -259,6 +261,38 @@ impl CachedLspAdapter {
.cloned()
.unwrap_or_else(|| language_name.lsp_id())
}
pub fn find_project_root(
&self,
path: &Path,
ancestor_depth: usize,
delegate: &Arc<dyn LspAdapterDelegate>,
) -> Option<Arc<Path>> {
self.adapter
.find_project_root(path, ancestor_depth, delegate)
}
pub fn attach_kind(&self) -> Attach {
*self.attach_kind.get_or_init(|| self.adapter.attach_kind())
}
}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Attach {
/// Create a single language server instance per subproject root.
InstancePerRoot,
/// Use one shared language server instance for all subprojects within a project.
Shared,
}

impl Attach {
pub fn root_path(
&self,
root_subproject_path: (WorktreeId, Arc<Path>),
) -> (WorktreeId, Arc<Path>) {
match self {
Attach::InstancePerRoot => root_subproject_path,
Attach::Shared => (root_subproject_path.0, Arc::from(Path::new(""))),
}
}
}

/// [`LspAdapterDelegate`] allows [`LspAdapter]` implementations to interface with the application
Expand Down Expand Up @@ -505,6 +539,19 @@ pub trait LspAdapter: 'static + Send + Sync {
fn prepare_initialize_params(&self, original: InitializeParams) -> Result<InitializeParams> {
Ok(original)
}
fn attach_kind(&self) -> Attach {
Attach::Shared
}
fn find_project_root(
&self,

_path: &Path,
_ancestor_depth: usize,
_: &Arc<dyn LspAdapterDelegate>,
) -> Option<Arc<Path>> {
// By default all language servers are rooted at the root of the worktree.
Some(Arc::from("".as_ref()))
}
}

async fn try_fetch_server_binary<L: LspAdapter + 'static + Send + Sync + ?Sized>(
Expand Down
2 changes: 0 additions & 2 deletions crates/language_tools/src/lsp_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,15 +730,13 @@ impl LspLogView {

* Binary: {BINARY:#?}

* Running in project: {PATH:?}

* Capabilities: {CAPABILITIES}

* Configuration: {CONFIGURATION}",
NAME = server.name(),
ID = server.server_id(),
BINARY = server.binary(),
PATH = server.root_path(),
CAPABILITIES = serde_json::to_string_pretty(&server.capabilities())
.unwrap_or_else(|e| format!("Failed to serialize capabilities: {e}")),
CONFIGURATION = serde_json::to_string_pretty(server.configuration())
Expand Down
15 changes: 15 additions & 0 deletions crates/languages/src/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,21 @@ impl LspAdapter for RustLspAdapter {
Self::SERVER_NAME.clone()
}

fn find_project_root(
&self,
path: &Path,
ancestor_depth: usize,
delegate: &Arc<dyn LspAdapterDelegate>,
) -> Option<Arc<Path>> {
let mut outermost_cargo_toml = None;
for path in path.ancestors().take(ancestor_depth) {
let p = path.join("Cargo.toml").to_path_buf();
if smol::block_on(delegate.read_text_file(p)).is_ok() {
outermost_cargo_toml = Some(Arc::from(path));
}
}
outermost_cargo_toml
}
async fn check_if_user_installed(
&self,
delegate: &dyn LspAdapterDelegate,
Expand Down
Loading
Loading