Add initial support for defining language server adapters in WebAssembly-based extensions (#8645)
This PR adds **internal** ability to run arbitrary language servers via WebAssembly extensions. The functionality isn't exposed yet - we're just landing this in this early state because there have been a lot of changes to the `LspAdapter` trait, and other language server logic. ## Next steps * Currently, wasm extensions can only define how to *install* and run a language server, they can't yet implement the other LSP adapter methods, such as formatting completion labels and workspace symbols. * We don't have an automatic way to install or develop these types of extensions * We don't have a way to package these types of extensions in our extensions repo, to make them available via our extensions API. * The Rust extension API crate, `zed-extension-api` has not yet been published to crates.io, because we still consider the API a work in progress. Release Notes: - N/A --------- Co-authored-by: Marshall <marshall@zed.dev> Co-authored-by: Nathan <nathan@zed.dev> Co-authored-by: Marshall Bowers <elliott.codes@gmail.com>
This commit is contained in:
parent
f3f2225a8e
commit
268fa1cbaf
84 changed files with 3714 additions and 1973 deletions
|
@ -1472,6 +1472,12 @@ impl LspCommand for GetCompletions {
|
|||
Default::default()
|
||||
};
|
||||
|
||||
let language_server_adapter = project
|
||||
.update(&mut cx, |project, _cx| {
|
||||
project.language_server_adapter_for_id(server_id)
|
||||
})?
|
||||
.ok_or_else(|| anyhow!("no such language server"))?;
|
||||
|
||||
let completions = buffer.update(&mut cx, |buffer, cx| {
|
||||
let language_registry = project.read(cx).languages().clone();
|
||||
let language = buffer.language().cloned();
|
||||
|
@ -1559,12 +1565,17 @@ impl LspCommand for GetCompletions {
|
|||
|
||||
let language_registry = language_registry.clone();
|
||||
let language = language.clone();
|
||||
let language_server_adapter = language_server_adapter.clone();
|
||||
LineEnding::normalize(&mut new_text);
|
||||
Some(async move {
|
||||
let mut label = None;
|
||||
if let Some(language) = language.as_ref() {
|
||||
language.process_completion(&mut lsp_completion).await;
|
||||
label = language.label_for_completion(&lsp_completion).await;
|
||||
if let Some(language) = &language {
|
||||
language_server_adapter
|
||||
.process_completion(&mut lsp_completion)
|
||||
.await;
|
||||
label = language_server_adapter
|
||||
.label_for_completion(&lsp_completion, language)
|
||||
.await;
|
||||
}
|
||||
|
||||
let documentation = if let Some(lsp_docs) = &lsp_completion.documentation {
|
||||
|
@ -1651,7 +1662,7 @@ impl LspCommand for GetCompletions {
|
|||
async fn response_from_proto(
|
||||
self,
|
||||
message: proto::GetCompletionsResponse,
|
||||
_: Model<Project>,
|
||||
project: Model<Project>,
|
||||
buffer: Model<Buffer>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<Vec<Completion>> {
|
||||
|
@ -1662,8 +1673,13 @@ impl LspCommand for GetCompletions {
|
|||
.await?;
|
||||
|
||||
let language = buffer.update(&mut cx, |buffer, _| buffer.language().cloned())?;
|
||||
let language_registry = project.update(&mut cx, |project, _| project.languages.clone())?;
|
||||
let completions = message.completions.into_iter().map(|completion| {
|
||||
language::proto::deserialize_completion(completion, language.clone())
|
||||
language::proto::deserialize_completion(
|
||||
completion,
|
||||
language.clone(),
|
||||
&language_registry,
|
||||
)
|
||||
});
|
||||
future::try_join_all(completions).await
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use futures::{
|
|||
use gpui::{AsyncAppContext, Model, ModelContext, Task, WeakModel};
|
||||
use language::{
|
||||
language_settings::{Formatter, LanguageSettings},
|
||||
Buffer, Language, LanguageServerName, LocalFile,
|
||||
Buffer, Language, LanguageRegistry, LanguageServerName, LocalFile,
|
||||
};
|
||||
use lsp::{LanguageServer, LanguageServerId};
|
||||
use node_runtime::NodeRuntime;
|
||||
|
@ -26,7 +26,8 @@ use crate::{
|
|||
};
|
||||
|
||||
pub fn prettier_plugins_for_language(
|
||||
language: &Language,
|
||||
language_registry: &Arc<LanguageRegistry>,
|
||||
language: &Arc<Language>,
|
||||
language_settings: &LanguageSettings,
|
||||
) -> Option<HashSet<&'static str>> {
|
||||
match &language_settings.formatter {
|
||||
|
@ -38,8 +39,8 @@ pub fn prettier_plugins_for_language(
|
|||
prettier_plugins
|
||||
.get_or_insert_with(|| HashSet::default())
|
||||
.extend(
|
||||
language
|
||||
.lsp_adapters()
|
||||
language_registry
|
||||
.lsp_adapters(language)
|
||||
.iter()
|
||||
.flat_map(|adapter| adapter.prettier_plugins()),
|
||||
)
|
||||
|
@ -303,15 +304,20 @@ fn start_prettier(
|
|||
) -> PrettierTask {
|
||||
cx.spawn(|project, mut cx| async move {
|
||||
log::info!("Starting prettier at path {prettier_dir:?}");
|
||||
let new_server_id = project.update(&mut cx, |project, _| {
|
||||
project.languages.next_language_server_id()
|
||||
})?;
|
||||
let language_registry = project.update(&mut cx, |project, _| project.languages.clone())?;
|
||||
let new_server_id = language_registry.next_language_server_id();
|
||||
|
||||
let new_prettier = Prettier::start(new_server_id, prettier_dir, node, cx.clone())
|
||||
.await
|
||||
.context("default prettier spawn")
|
||||
.map(Arc::new)
|
||||
.map_err(Arc::new)?;
|
||||
let new_prettier = Prettier::start(
|
||||
new_server_id,
|
||||
prettier_dir,
|
||||
node,
|
||||
language_registry,
|
||||
cx.clone(),
|
||||
)
|
||||
.await
|
||||
.context("default prettier spawn")
|
||||
.map(Arc::new)
|
||||
.map_err(Arc::new)?;
|
||||
register_new_prettier(&project, &new_prettier, worktree_id, new_server_id, &mut cx);
|
||||
Ok(new_prettier)
|
||||
})
|
||||
|
|
|
@ -10,6 +10,7 @@ pub mod terminals;
|
|||
mod project_tests;
|
||||
|
||||
use anyhow::{anyhow, bail, Context as _, Result};
|
||||
use async_trait::async_trait;
|
||||
use client::{proto, Client, Collaborator, TypedEnvelope, UserStore};
|
||||
use clock::ReplicaId;
|
||||
use collections::{hash_map, BTreeMap, HashMap, HashSet, VecDeque};
|
||||
|
@ -847,10 +848,12 @@ impl Project {
|
|||
let current_lsp_settings = &self.current_lsp_settings;
|
||||
for (worktree_id, started_lsp_name) in self.language_server_ids.keys() {
|
||||
let language = languages.iter().find_map(|l| {
|
||||
let adapter = l
|
||||
.lsp_adapters()
|
||||
let adapter = self
|
||||
.languages
|
||||
.lsp_adapters(l)
|
||||
.iter()
|
||||
.find(|adapter| &adapter.name == started_lsp_name)?;
|
||||
.find(|adapter| &adapter.name == started_lsp_name)?
|
||||
.clone();
|
||||
Some((l, adapter))
|
||||
});
|
||||
if let Some((language, adapter)) = language {
|
||||
|
@ -889,9 +892,11 @@ impl Project {
|
|||
|
||||
let mut prettier_plugins_by_worktree = HashMap::default();
|
||||
for (worktree, language, settings) in language_formatters_to_check {
|
||||
if let Some(plugins) =
|
||||
prettier_support::prettier_plugins_for_language(&language, &settings)
|
||||
{
|
||||
if let Some(plugins) = prettier_support::prettier_plugins_for_language(
|
||||
&self.languages,
|
||||
&language,
|
||||
&settings,
|
||||
) {
|
||||
prettier_plugins_by_worktree
|
||||
.entry(worktree)
|
||||
.or_insert_with(|| HashSet::default())
|
||||
|
@ -2047,7 +2052,7 @@ impl Project {
|
|||
}
|
||||
|
||||
if let Some(language) = language {
|
||||
for adapter in language.lsp_adapters() {
|
||||
for adapter in self.languages.lsp_adapters(&language) {
|
||||
let language_id = adapter.language_ids.get(language.name().as_ref()).cloned();
|
||||
let server = self
|
||||
.language_server_ids
|
||||
|
@ -2118,10 +2123,12 @@ impl Project {
|
|||
let worktree_id = old_file.worktree_id(cx);
|
||||
let ids = &self.language_server_ids;
|
||||
|
||||
let language = buffer.language().cloned();
|
||||
let adapters = language.iter().flat_map(|language| language.lsp_adapters());
|
||||
for &server_id in adapters.flat_map(|a| ids.get(&(worktree_id, a.name.clone()))) {
|
||||
buffer.update_diagnostics(server_id, Default::default(), cx);
|
||||
if let Some(language) = buffer.language().cloned() {
|
||||
for adapter in self.languages.lsp_adapters(&language) {
|
||||
if let Some(server_id) = ids.get(&(worktree_id, adapter.name.clone())) {
|
||||
buffer.update_diagnostics(*server_id, Default::default(), cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.buffer_snapshots.remove(&buffer.remote_id());
|
||||
|
@ -2701,9 +2708,11 @@ impl Project {
|
|||
let settings = language_settings(Some(&new_language), buffer_file.as_ref(), cx).clone();
|
||||
let buffer_file = File::from_dyn(buffer_file.as_ref());
|
||||
let worktree = buffer_file.as_ref().map(|f| f.worktree_id(cx));
|
||||
if let Some(prettier_plugins) =
|
||||
prettier_support::prettier_plugins_for_language(&new_language, &settings)
|
||||
{
|
||||
if let Some(prettier_plugins) = prettier_support::prettier_plugins_for_language(
|
||||
&self.languages,
|
||||
&new_language,
|
||||
&settings,
|
||||
) {
|
||||
self.install_default_prettier(worktree, prettier_plugins, cx);
|
||||
};
|
||||
if let Some(file) = buffer_file {
|
||||
|
@ -2726,7 +2735,7 @@ impl Project {
|
|||
return;
|
||||
}
|
||||
|
||||
for adapter in language.lsp_adapters() {
|
||||
for adapter in self.languages.clone().lsp_adapters(&language) {
|
||||
self.start_language_server(worktree, adapter.clone(), language.clone(), cx);
|
||||
}
|
||||
}
|
||||
|
@ -3240,7 +3249,11 @@ impl Project {
|
|||
};
|
||||
|
||||
if file.worktree.read(cx).id() != key.0
|
||||
|| !language.lsp_adapters().iter().any(|a| a.name == key.1)
|
||||
|| !self
|
||||
.languages
|
||||
.lsp_adapters(&language)
|
||||
.iter()
|
||||
.any(|a| a.name == key.1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -3433,8 +3446,10 @@ impl Project {
|
|||
) {
|
||||
let worktree_id = worktree.read(cx).id();
|
||||
|
||||
let stop_tasks = language
|
||||
.lsp_adapters()
|
||||
let stop_tasks = self
|
||||
.languages
|
||||
.clone()
|
||||
.lsp_adapters(&language)
|
||||
.iter()
|
||||
.map(|adapter| {
|
||||
let stop_task = self.stop_language_server(worktree_id, adapter.name.clone(), cx);
|
||||
|
@ -4785,14 +4800,15 @@ impl Project {
|
|||
.languages
|
||||
.language_for_file(&project_path.path, None)
|
||||
.unwrap_or_else(move |_| adapter_language);
|
||||
let language_server_name = adapter.name.clone();
|
||||
let adapter = adapter.clone();
|
||||
Some(async move {
|
||||
let language = language.await;
|
||||
let label =
|
||||
language.label_for_symbol(&symbol_name, symbol_kind).await;
|
||||
let label = adapter
|
||||
.label_for_symbol(&symbol_name, symbol_kind, &language)
|
||||
.await;
|
||||
|
||||
Symbol {
|
||||
language_server_name,
|
||||
language_server_name: adapter.name.clone(),
|
||||
source_worktree_id,
|
||||
path: project_path,
|
||||
label: label.unwrap_or_else(|| {
|
||||
|
@ -7972,6 +7988,7 @@ impl Project {
|
|||
_: Arc<Client>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<proto::ApplyCompletionAdditionalEditsResponse> {
|
||||
let languages = this.update(&mut cx, |this, _| this.languages.clone())?;
|
||||
let (buffer, completion) = this.update(&mut cx, |this, cx| {
|
||||
let buffer_id = BufferId::new(envelope.payload.buffer_id)?;
|
||||
let buffer = this
|
||||
|
@ -7986,6 +8003,7 @@ impl Project {
|
|||
.completion
|
||||
.ok_or_else(|| anyhow!("invalid completion"))?,
|
||||
language.cloned(),
|
||||
&languages,
|
||||
);
|
||||
Ok::<_, anyhow::Error>((buffer, completion))
|
||||
})??;
|
||||
|
@ -8713,6 +8731,9 @@ impl Project {
|
|||
.language_for_file(&path.path, None)
|
||||
.await
|
||||
.log_err();
|
||||
let adapter = language
|
||||
.as_ref()
|
||||
.and_then(|language| languages.lsp_adapters(language).first().cloned());
|
||||
Ok(Symbol {
|
||||
language_server_name: LanguageServerName(
|
||||
serialized_symbol.language_server_name.into(),
|
||||
|
@ -8720,10 +8741,10 @@ impl Project {
|
|||
source_worktree_id,
|
||||
path,
|
||||
label: {
|
||||
match language {
|
||||
Some(language) => {
|
||||
language
|
||||
.label_for_symbol(&serialized_symbol.name, kind)
|
||||
match language.as_ref().zip(adapter.as_ref()) {
|
||||
Some((language, adapter)) => {
|
||||
adapter
|
||||
.label_for_symbol(&serialized_symbol.name, kind, language)
|
||||
.await
|
||||
}
|
||||
None => None,
|
||||
|
@ -8975,6 +8996,17 @@ impl Project {
|
|||
self.supplementary_language_servers.iter()
|
||||
}
|
||||
|
||||
pub fn language_server_adapter_for_id(
|
||||
&self,
|
||||
id: LanguageServerId,
|
||||
) -> Option<Arc<CachedLspAdapter>> {
|
||||
if let Some(LanguageServerState::Running { adapter, .. }) = self.language_servers.get(&id) {
|
||||
Some(adapter.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn language_server_for_id(&self, id: LanguageServerId) -> Option<Arc<LanguageServer>> {
|
||||
if let Some(LanguageServerState::Running { server, .. }) = self.language_servers.get(&id) {
|
||||
Some(server.clone())
|
||||
|
@ -9025,8 +9057,8 @@ impl Project {
|
|||
) -> Vec<LanguageServerId> {
|
||||
if let Some((file, language)) = File::from_dyn(buffer.file()).zip(buffer.language()) {
|
||||
let worktree_id = file.worktree_id(cx);
|
||||
language
|
||||
.lsp_adapters()
|
||||
self.languages
|
||||
.lsp_adapters(&language)
|
||||
.iter()
|
||||
.flat_map(|adapter| {
|
||||
let key = (worktree_id, adapter.name.clone());
|
||||
|
@ -9190,20 +9222,25 @@ impl<P: AsRef<Path>> From<(WorktreeId, P)> for ProjectPath {
|
|||
|
||||
struct ProjectLspAdapterDelegate {
|
||||
project: Model<Project>,
|
||||
worktree: Model<Worktree>,
|
||||
worktree: worktree::Snapshot,
|
||||
fs: Arc<dyn Fs>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
language_registry: Arc<LanguageRegistry>,
|
||||
}
|
||||
|
||||
impl ProjectLspAdapterDelegate {
|
||||
fn new(project: &Project, worktree: &Model<Worktree>, cx: &ModelContext<Project>) -> Arc<Self> {
|
||||
Arc::new(Self {
|
||||
project: cx.handle(),
|
||||
worktree: worktree.clone(),
|
||||
worktree: worktree.read(cx).snapshot(),
|
||||
fs: project.fs.clone(),
|
||||
http_client: project.client.http_client(),
|
||||
language_registry: project.languages.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl LspAdapterDelegate for ProjectLspAdapterDelegate {
|
||||
fn show_notification(&self, message: &str, cx: &mut AppContext) {
|
||||
self.project
|
||||
|
@ -9214,41 +9251,50 @@ impl LspAdapterDelegate for ProjectLspAdapterDelegate {
|
|||
self.http_client.clone()
|
||||
}
|
||||
|
||||
fn which_command(
|
||||
&self,
|
||||
command: OsString,
|
||||
cx: &AppContext,
|
||||
) -> Task<Option<(PathBuf, HashMap<String, String>)>> {
|
||||
let worktree_abs_path = self.worktree.read(cx).abs_path();
|
||||
let command = command.to_owned();
|
||||
async fn which_command(&self, command: OsString) -> Option<(PathBuf, HashMap<String, String>)> {
|
||||
let worktree_abs_path = self.worktree.abs_path();
|
||||
|
||||
cx.background_executor().spawn(async move {
|
||||
let shell_env = load_shell_environment(&worktree_abs_path)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"failed to determine load login shell environment in {worktree_abs_path:?}"
|
||||
)
|
||||
})
|
||||
.log_err();
|
||||
let shell_env = load_shell_environment(&worktree_abs_path)
|
||||
.await
|
||||
.with_context(|| {
|
||||
format!("failed to determine load login shell environment in {worktree_abs_path:?}")
|
||||
})
|
||||
.log_err();
|
||||
|
||||
if let Some(shell_env) = shell_env {
|
||||
let shell_path = shell_env.get("PATH");
|
||||
match which::which_in(&command, shell_path, &worktree_abs_path) {
|
||||
Ok(command_path) => Some((command_path, shell_env)),
|
||||
Err(error) => {
|
||||
log::warn!(
|
||||
"failed to determine path for command {:?} in shell PATH {:?}: {error}",
|
||||
command.to_string_lossy(),
|
||||
shell_path.map(String::as_str).unwrap_or("")
|
||||
);
|
||||
None
|
||||
}
|
||||
if let Some(shell_env) = shell_env {
|
||||
let shell_path = shell_env.get("PATH");
|
||||
match which::which_in(&command, shell_path, &worktree_abs_path) {
|
||||
Ok(command_path) => Some((command_path, shell_env)),
|
||||
Err(error) => {
|
||||
log::warn!(
|
||||
"failed to determine path for command {:?} in shell PATH {:?}: {error}",
|
||||
command.to_string_lossy(),
|
||||
shell_path.map(String::as_str).unwrap_or("")
|
||||
);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn update_status(
|
||||
&self,
|
||||
server_name: LanguageServerName,
|
||||
status: language::LanguageServerBinaryStatus,
|
||||
) {
|
||||
self.language_registry
|
||||
.update_lsp_status(server_name, status);
|
||||
}
|
||||
|
||||
async fn read_text_file(&self, path: PathBuf) -> Result<String> {
|
||||
if self.worktree.entry_for_path(&path).is_none() {
|
||||
return Err(anyhow!("no such path {path:?}"));
|
||||
}
|
||||
let path = self.worktree.absolutize(path.as_ref())?;
|
||||
let content = self.fs.load(&path).await?;
|
||||
Ok(content)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -189,55 +189,6 @@ async fn test_managing_project_specific_settings(cx: &mut gpui::TestAppContext)
|
|||
async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut rust_language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_rust::language()),
|
||||
);
|
||||
let mut json_language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "JSON".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["json".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
);
|
||||
let mut fake_rust_servers = rust_language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
name: "the-rust-language-server",
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![".".to_string(), "::".to_string()]),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
let mut fake_json_servers = json_language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
name: "the-json-language-server",
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![":".to_string()]),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/the-root",
|
||||
|
@ -251,6 +202,36 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
|
|||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await;
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
|
||||
let mut fake_rust_servers = language_registry.register_fake_lsp_adapter(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
name: "the-rust-language-server",
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![".".to_string(), "::".to_string()]),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let mut fake_json_servers = language_registry.register_fake_lsp_adapter(
|
||||
"JSON",
|
||||
FakeLspAdapter {
|
||||
name: "the-json-language-server",
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![":".to_string()]),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
// Open a buffer without an associated language server.
|
||||
let toml_buffer = project
|
||||
|
@ -273,10 +254,8 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
|
|||
|
||||
// Now we add the languages to the project, and ensure they get assigned to all
|
||||
// the relevant open buffers.
|
||||
project.update(cx, |project, _| {
|
||||
project.languages.add(Arc::new(json_language));
|
||||
project.languages.add(Arc::new(rust_language));
|
||||
});
|
||||
language_registry.add(json_lang());
|
||||
language_registry.add(rust_lang());
|
||||
cx.executor().run_until_parked();
|
||||
rust_buffer.update(cx, |buffer, _| {
|
||||
assert_eq!(buffer.language().map(|l| l.name()), Some("Rust".into()));
|
||||
|
@ -581,24 +560,6 @@ async fn test_managing_language_servers(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_rust::language()),
|
||||
);
|
||||
let mut fake_servers = language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
name: "the-language-server",
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/the-root",
|
||||
|
@ -630,9 +591,16 @@ async fn test_reporting_fs_changes_to_language_servers(cx: &mut gpui::TestAppCon
|
|||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/the-root".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| {
|
||||
project.languages.add(Arc::new(language));
|
||||
});
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(rust_lang());
|
||||
let mut fake_servers = language_registry.register_fake_lsp_adapter(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
name: "the-language-server",
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
cx.executor().run_until_parked();
|
||||
|
||||
// Start the language server by opening a buffer with a compatible file extension.
|
||||
|
@ -1019,24 +987,6 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
|
|||
init_test(cx);
|
||||
|
||||
let progress_token = "the-progress-token";
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_rust::language()),
|
||||
);
|
||||
let mut fake_servers = language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
disk_based_diagnostics_progress_token: Some(progress_token.into()),
|
||||
disk_based_diagnostics_sources: vec!["disk".into()],
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
|
@ -1049,7 +999,18 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) {
|
|||
.await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
|
||||
language_registry.add(rust_lang());
|
||||
let mut fake_servers = language_registry.register_fake_lsp_adapter(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
disk_based_diagnostics_progress_token: Some(progress_token.into()),
|
||||
disk_based_diagnostics_sources: vec!["disk".into()],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let worktree_id = project.update(cx, |p, cx| p.worktrees().next().unwrap().read(cx).id());
|
||||
|
||||
// Cause worktree to start the fake language server
|
||||
|
@ -1155,29 +1116,23 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
|
|||
init_test(cx);
|
||||
|
||||
let progress_token = "the-progress-token";
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
);
|
||||
let mut fake_servers = language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
disk_based_diagnostics_sources: vec!["disk".into()],
|
||||
disk_based_diagnostics_progress_token: Some(progress_token.into()),
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
|
||||
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(rust_lang());
|
||||
let mut fake_servers = language_registry.register_fake_lsp_adapter(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
name: "the-language-server",
|
||||
disk_based_diagnostics_sources: vec!["disk".into()],
|
||||
disk_based_diagnostics_progress_token: Some(progress_token.into()),
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
|
@ -1239,27 +1194,15 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC
|
|||
async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
);
|
||||
let mut fake_servers = language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": "x" })).await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
|
||||
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(rust_lang());
|
||||
let mut fake_servers =
|
||||
language_registry.register_fake_lsp_adapter("Rust", FakeLspAdapter::default());
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
|
@ -1331,28 +1274,15 @@ async fn test_restarting_server_with_diagnostics_published(cx: &mut gpui::TestAp
|
|||
async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
);
|
||||
let mut fake_servers = language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
name: "the-lsp",
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": "" })).await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
|
||||
language_registry.add(rust_lang());
|
||||
let mut fake_servers =
|
||||
language_registry.register_fake_lsp_adapter("Rust", FakeLspAdapter::default());
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
|
@ -1383,50 +1313,29 @@ async fn test_restarted_server_reporting_invalid_buffer_version(cx: &mut gpui::T
|
|||
async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut rust = Language::new(
|
||||
LanguageConfig {
|
||||
name: Arc::from("Rust"),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
);
|
||||
let mut fake_rust_servers = rust
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
name: "rust-lsp",
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
let mut js = Language::new(
|
||||
LanguageConfig {
|
||||
name: Arc::from("JavaScript"),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["js".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
);
|
||||
let mut fake_js_servers = js
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
name: "js-lsp",
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree("/dir", json!({ "a.rs": "", "b.js": "" }))
|
||||
.await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| {
|
||||
project.languages.add(Arc::new(rust));
|
||||
project.languages.add(Arc::new(js));
|
||||
});
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
|
||||
let mut fake_rust_servers = language_registry.register_fake_lsp_adapter(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
name: "rust-lsp",
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
let mut fake_js_servers = language_registry.register_fake_lsp_adapter(
|
||||
"JavaScript",
|
||||
FakeLspAdapter {
|
||||
name: "js-lsp",
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
language_registry.add(rust_lang());
|
||||
language_registry.add(js_lang());
|
||||
|
||||
let _rs_buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
|
@ -1518,24 +1427,6 @@ async fn test_toggling_enable_language_server(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_rust::language()),
|
||||
);
|
||||
let mut fake_servers = language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
disk_based_diagnostics_sources: vec!["disk".into()],
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
let text = "
|
||||
fn a() { A }
|
||||
fn b() { BB }
|
||||
|
@ -1547,7 +1438,16 @@ async fn test_transforming_diagnostics(cx: &mut gpui::TestAppContext) {
|
|||
fs.insert_tree("/dir", json!({ "a.rs": text })).await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
|
||||
language_registry.add(rust_lang());
|
||||
let mut fake_servers = language_registry.register_fake_lsp_adapter(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
disk_based_diagnostics_sources: vec!["disk".into()],
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
|
@ -1932,19 +1832,6 @@ async fn test_diagnostics_from_multiple_language_servers(cx: &mut gpui::TestAppC
|
|||
async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_rust::language()),
|
||||
);
|
||||
let mut fake_servers = language.set_fake_lsp_adapter(Default::default()).await;
|
||||
|
||||
let text = "
|
||||
fn a() {
|
||||
f1();
|
||||
|
@ -1968,7 +1855,12 @@ async fn test_edits_from_lsp2_with_past_version(cx: &mut gpui::TestAppContext) {
|
|||
.await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
|
||||
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(rust_lang());
|
||||
let mut fake_servers =
|
||||
language_registry.register_fake_lsp_adapter("Rust", FakeLspAdapter::default());
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/a.rs", cx))
|
||||
.await
|
||||
|
@ -2322,19 +2214,6 @@ fn chunks_with_diagnostics<T: ToOffset + ToPoint>(
|
|||
async fn test_definition(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_rust::language()),
|
||||
);
|
||||
let mut fake_servers = language.set_fake_lsp_adapter(Default::default()).await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
|
@ -2346,7 +2225,11 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
|
|||
.await;
|
||||
|
||||
let project = Project::test(fs, ["/dir/b.rs".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
|
||||
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(rust_lang());
|
||||
let mut fake_servers =
|
||||
language_registry.register_fake_lsp_adapter("Rust", FakeLspAdapter::default());
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| project.open_local_buffer("/dir/b.rs", cx))
|
||||
|
@ -2426,30 +2309,6 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "TypeScript".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["ts".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_typescript::language_typescript()),
|
||||
);
|
||||
let mut fake_language_servers = language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![":".to_string()]),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
|
@ -2460,7 +2319,23 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
|||
.await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
|
||||
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(typescript_lang());
|
||||
let mut fake_language_servers = language_registry.register_fake_lsp_adapter(
|
||||
"TypeScript",
|
||||
FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![":".to_string()]),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
|
||||
.await
|
||||
|
@ -2526,30 +2401,6 @@ async fn test_completions_without_edit_ranges(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "TypeScript".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["ts".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_typescript::language_typescript()),
|
||||
);
|
||||
let mut fake_language_servers = language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![":".to_string()]),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
|
@ -2560,7 +2411,23 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) {
|
|||
.await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
|
||||
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(typescript_lang());
|
||||
let mut fake_language_servers = language_registry.register_fake_lsp_adapter(
|
||||
"TypeScript",
|
||||
FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
completion_provider: Some(lsp::CompletionOptions {
|
||||
trigger_characters: Some(vec![":".to_string()]),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
|
||||
.await
|
||||
|
@ -2595,19 +2462,6 @@ async fn test_completions_with_carriage_returns(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "TypeScript".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["ts".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
);
|
||||
let mut fake_language_servers = language.set_fake_lsp_adapter(Default::default()).await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
|
@ -2618,7 +2472,12 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
|
|||
.await;
|
||||
|
||||
let project = Project::test(fs, ["/dir".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
|
||||
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(typescript_lang());
|
||||
let mut fake_language_servers =
|
||||
language_registry.register_fake_lsp_adapter("TypeScript", Default::default());
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |p, cx| p.open_local_buffer("/dir/a.ts", cx))
|
||||
.await
|
||||
|
@ -2904,16 +2763,7 @@ async fn test_save_as(cx: &mut gpui::TestAppContext) {
|
|||
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||
|
||||
let languages = project.update(cx, |project, _| project.languages().clone());
|
||||
languages.register_native_grammars([("rust", tree_sitter_rust::language())]);
|
||||
languages.register_test_language(LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
grammar: Some("rust".into()),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".into()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
languages.add(rust_lang());
|
||||
|
||||
let buffer = project.update(cx, |project, cx| {
|
||||
project.create_buffer("", None, cx).unwrap()
|
||||
|
@ -3733,30 +3583,6 @@ async fn test_grouped_diagnostics(cx: &mut gpui::TestAppContext) {
|
|||
async fn test_rename(cx: &mut gpui::TestAppContext) {
|
||||
init_test(cx);
|
||||
|
||||
let mut language = Language::new(
|
||||
LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_rust::language()),
|
||||
);
|
||||
let mut fake_servers = language
|
||||
.set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
|
||||
prepare_provider: Some(true),
|
||||
work_done_progress_options: Default::default(),
|
||||
})),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}))
|
||||
.await;
|
||||
|
||||
let fs = FakeFs::new(cx.executor());
|
||||
fs.insert_tree(
|
||||
"/dir",
|
||||
|
@ -3768,7 +3594,23 @@ async fn test_rename(cx: &mut gpui::TestAppContext) {
|
|||
.await;
|
||||
|
||||
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||
project.update(cx, |project, _| project.languages.add(Arc::new(language)));
|
||||
|
||||
let language_registry = project.read_with(cx, |project, _| project.languages().clone());
|
||||
language_registry.add(rust_lang());
|
||||
let mut fake_servers = language_registry.register_fake_lsp_adapter(
|
||||
"Rust",
|
||||
FakeLspAdapter {
|
||||
capabilities: lsp::ServerCapabilities {
|
||||
rename_provider: Some(lsp::OneOf::Right(lsp::RenameOptions {
|
||||
prepare_provider: Some(true),
|
||||
work_done_progress_options: Default::default(),
|
||||
})),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
);
|
||||
|
||||
let buffer = project
|
||||
.update(cx, |project, cx| {
|
||||
project.open_local_buffer("/dir/one.rs", cx)
|
||||
|
@ -4475,3 +4317,59 @@ fn init_test(cx: &mut gpui::TestAppContext) {
|
|||
Project::init_settings(cx);
|
||||
});
|
||||
}
|
||||
|
||||
fn json_lang() -> Arc<Language> {
|
||||
Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
name: "JSON".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["json".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
fn js_lang() -> Arc<Language> {
|
||||
Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
name: Arc::from("JavaScript"),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["js".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
fn rust_lang() -> Arc<Language> {
|
||||
Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
name: "Rust".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["rs".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_rust::language()),
|
||||
))
|
||||
}
|
||||
|
||||
fn typescript_lang() -> Arc<Language> {
|
||||
Arc::new(Language::new(
|
||||
LanguageConfig {
|
||||
name: "TypeScript".into(),
|
||||
matcher: LanguageMatcher {
|
||||
path_suffixes: vec!["ts".to_string()],
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
Some(tree_sitter_typescript::language_typescript()),
|
||||
))
|
||||
}
|
||||
|
|
|
@ -219,7 +219,7 @@ impl Inventory {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-support")]
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub mod test_inventory {
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue