WIP and merge
This commit is contained in:
parent
97f4406ef6
commit
1bdde8b2e4
584 changed files with 33536 additions and 17400 deletions
|
@ -131,8 +131,16 @@ impl super::LspAdapter for CLspAdapter {
|
|||
let text = format!("{} {}", detail, label);
|
||||
let source = Rope::from(format!("struct S {{ {} }}", text).as_str());
|
||||
let runs = language.highlight_text(&source, 11..11 + text.len());
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter_text| {
|
||||
text.find(filter_text)
|
||||
.map(|start| start..start + filter_text.len())
|
||||
})
|
||||
.unwrap_or(detail.len() + 1..text.len());
|
||||
return Some(CodeLabel {
|
||||
filter_range: detail.len() + 1..text.len(),
|
||||
filter_range,
|
||||
text,
|
||||
runs,
|
||||
});
|
||||
|
@ -143,8 +151,16 @@ impl super::LspAdapter for CLspAdapter {
|
|||
let detail = completion.detail.as_ref().unwrap();
|
||||
let text = format!("{} {}", detail, label);
|
||||
let runs = language.highlight_text(&Rope::from(text.as_str()), 0..text.len());
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter_text| {
|
||||
text.find(filter_text)
|
||||
.map(|start| start..start + filter_text.len())
|
||||
})
|
||||
.unwrap_or(detail.len() + 1..text.len());
|
||||
return Some(CodeLabel {
|
||||
filter_range: detail.len() + 1..text.len(),
|
||||
filter_range,
|
||||
text,
|
||||
runs,
|
||||
});
|
||||
|
@ -155,16 +171,24 @@ impl super::LspAdapter for CLspAdapter {
|
|||
let detail = completion.detail.as_ref().unwrap();
|
||||
let text = format!("{} {}", detail, label);
|
||||
let runs = language.highlight_text(&Rope::from(text.as_str()), 0..text.len());
|
||||
let filter_start = detail.len() + 1;
|
||||
let filter_end =
|
||||
if let Some(end) = text.rfind('(').filter(|end| *end > filter_start) {
|
||||
end
|
||||
} else {
|
||||
text.len()
|
||||
};
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter_text| {
|
||||
text.find(filter_text)
|
||||
.map(|start| start..start + filter_text.len())
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
let filter_start = detail.len() + 1;
|
||||
let filter_end = text
|
||||
.rfind('(')
|
||||
.filter(|end| *end > filter_start)
|
||||
.unwrap_or(text.len());
|
||||
filter_start..filter_end
|
||||
});
|
||||
|
||||
return Some(CodeLabel {
|
||||
filter_range: filter_start..filter_end,
|
||||
filter_range,
|
||||
text,
|
||||
runs,
|
||||
});
|
||||
|
@ -186,7 +210,8 @@ impl super::LspAdapter for CLspAdapter {
|
|||
.grammar()
|
||||
.and_then(|g| g.highlight_id_for_name(highlight_name?))
|
||||
{
|
||||
let mut label = CodeLabel::plain(label.to_string(), None);
|
||||
let mut label =
|
||||
CodeLabel::plain(label.to_string(), completion.filter_text.as_deref());
|
||||
label.runs.push((
|
||||
0..label.text.rfind('(').unwrap_or(label.text.len()),
|
||||
highlight_id,
|
||||
|
@ -196,7 +221,10 @@ impl super::LspAdapter for CLspAdapter {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
Some(CodeLabel::plain(label.to_string(), None))
|
||||
Some(CodeLabel::plain(
|
||||
label.to_string(),
|
||||
completion.filter_text.as_deref(),
|
||||
))
|
||||
}
|
||||
|
||||
async fn label_for_symbol(
|
||||
|
|
|
@ -233,10 +233,18 @@ impl super::LspAdapter for GoLspAdapter {
|
|||
let text = format!("{label} {detail}");
|
||||
let source = Rope::from(format!("import {text}").as_str());
|
||||
let runs = language.highlight_text(&source, 7..7 + text.len());
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter_text| {
|
||||
text.find(filter_text)
|
||||
.map(|start| start..start + filter_text.len())
|
||||
})
|
||||
.unwrap_or(0..label.len());
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..label.len(),
|
||||
filter_range,
|
||||
});
|
||||
}
|
||||
Some((
|
||||
|
@ -250,10 +258,18 @@ impl super::LspAdapter for GoLspAdapter {
|
|||
name_offset,
|
||||
language.highlight_text(&source, 4..4 + text.len()),
|
||||
);
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter_text| {
|
||||
text.find(filter_text)
|
||||
.map(|start| start..start + filter_text.len())
|
||||
})
|
||||
.unwrap_or(0..label.len());
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..label.len(),
|
||||
filter_range,
|
||||
});
|
||||
}
|
||||
Some((lsp::CompletionItemKind::STRUCT, _)) => {
|
||||
|
@ -263,10 +279,18 @@ impl super::LspAdapter for GoLspAdapter {
|
|||
name_offset,
|
||||
language.highlight_text(&source, 5..5 + text.len()),
|
||||
);
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter_text| {
|
||||
text.find(filter_text)
|
||||
.map(|start| start..start + filter_text.len())
|
||||
})
|
||||
.unwrap_or(0..label.len());
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..label.len(),
|
||||
filter_range,
|
||||
});
|
||||
}
|
||||
Some((lsp::CompletionItemKind::INTERFACE, _)) => {
|
||||
|
@ -276,10 +300,18 @@ impl super::LspAdapter for GoLspAdapter {
|
|||
name_offset,
|
||||
language.highlight_text(&source, 5..5 + text.len()),
|
||||
);
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter_text| {
|
||||
text.find(filter_text)
|
||||
.map(|start| start..start + filter_text.len())
|
||||
})
|
||||
.unwrap_or(0..label.len());
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..label.len(),
|
||||
filter_range,
|
||||
});
|
||||
}
|
||||
Some((lsp::CompletionItemKind::FIELD, detail)) => {
|
||||
|
@ -290,10 +322,18 @@ impl super::LspAdapter for GoLspAdapter {
|
|||
name_offset,
|
||||
language.highlight_text(&source, 16..16 + text.len()),
|
||||
);
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter_text| {
|
||||
text.find(filter_text)
|
||||
.map(|start| start..start + filter_text.len())
|
||||
})
|
||||
.unwrap_or(0..label.len());
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..label.len(),
|
||||
filter_range,
|
||||
});
|
||||
}
|
||||
Some((lsp::CompletionItemKind::FUNCTION | lsp::CompletionItemKind::METHOD, detail)) => {
|
||||
|
@ -304,8 +344,16 @@ impl super::LspAdapter for GoLspAdapter {
|
|||
name_offset,
|
||||
language.highlight_text(&source, 5..5 + text.len()),
|
||||
);
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter_text| {
|
||||
text.find(filter_text)
|
||||
.map(|start| start..start + filter_text.len())
|
||||
})
|
||||
.unwrap_or(0..label.len());
|
||||
return Some(CodeLabel {
|
||||
filter_range: 0..label.len(),
|
||||
filter_range,
|
||||
text,
|
||||
runs,
|
||||
});
|
||||
|
|
26
crates/languages/src/go/debugger.scm
Normal file
26
crates/languages/src/go/debugger.scm
Normal file
|
@ -0,0 +1,26 @@
|
|||
(parameter_declaration (identifier) @debug-variable)
|
||||
|
||||
(short_var_declaration (expression_list (identifier) @debug-variable))
|
||||
|
||||
(var_declaration (var_spec (identifier) @debug-variable))
|
||||
|
||||
(const_declaration (const_spec (identifier) @debug-variable))
|
||||
|
||||
(assignment_statement (expression_list (identifier) @debug-variable))
|
||||
|
||||
(binary_expression (identifier) @debug-variable
|
||||
(#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(call_expression (argument_list (identifier) @debug-variable
|
||||
(#not-match? @debug-variable "^[A-Z]")))
|
||||
|
||||
(return_statement (expression_list (identifier) @debug-variable
|
||||
(#not-match? @debug-variable "^[A-Z]")))
|
||||
|
||||
(range_clause (expression_list (identifier) @debug-variable))
|
||||
|
||||
(parenthesized_expression (identifier) @debug-variable
|
||||
(#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(block) @debug-scope
|
||||
(function_declaration) @debug-scope
|
|
@ -5,12 +5,14 @@ use async_trait::async_trait;
|
|||
use collections::HashMap;
|
||||
use dap::DapRegistry;
|
||||
use futures::StreamExt;
|
||||
use gpui::{App, AsyncApp};
|
||||
use gpui::{App, AsyncApp, Task};
|
||||
use http_client::github::{GitHubLspBinaryVersion, latest_github_release};
|
||||
use language::{LanguageRegistry, LanguageToolchainStore, LspAdapter, LspAdapterDelegate};
|
||||
use language::{
|
||||
ContextProvider, LanguageRegistry, LanguageToolchainStore, LspAdapter, LspAdapterDelegate,
|
||||
};
|
||||
use lsp::{LanguageServerBinary, LanguageServerName};
|
||||
use node_runtime::NodeRuntime;
|
||||
use project::{ContextProviderWithTasks, Fs, lsp_store::language_server_settings};
|
||||
use project::{Fs, lsp_store::language_server_settings};
|
||||
use serde_json::{Value, json};
|
||||
use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};
|
||||
use smol::{
|
||||
|
@ -29,6 +31,8 @@ use std::{
|
|||
use task::{AdapterSchemas, TaskTemplate, TaskTemplates, VariableName};
|
||||
use util::{ResultExt, archive::extract_zip, fs::remove_matching, maybe, merge_json_value_into};
|
||||
|
||||
use crate::PackageJsonData;
|
||||
|
||||
const SERVER_PATH: &str =
|
||||
"node_modules/vscode-langservers-extracted/bin/vscode-json-language-server";
|
||||
|
||||
|
@ -36,23 +40,92 @@ const SERVER_PATH: &str =
|
|||
const TSCONFIG_SCHEMA: &str = include_str!("json/schemas/tsconfig.json");
|
||||
const PACKAGE_JSON_SCHEMA: &str = include_str!("json/schemas/package.json");
|
||||
|
||||
pub(super) fn json_task_context() -> ContextProviderWithTasks {
|
||||
ContextProviderWithTasks::new(TaskTemplates(vec![
|
||||
TaskTemplate {
|
||||
label: "package script $ZED_CUSTOM_script".to_owned(),
|
||||
command: "npm --prefix $ZED_DIRNAME run".to_owned(),
|
||||
args: vec![VariableName::Custom("script".into()).template_value()],
|
||||
tags: vec!["package-script".into()],
|
||||
..TaskTemplate::default()
|
||||
},
|
||||
TaskTemplate {
|
||||
label: "composer script $ZED_CUSTOM_script".to_owned(),
|
||||
command: "composer -d $ZED_DIRNAME".to_owned(),
|
||||
args: vec![VariableName::Custom("script".into()).template_value()],
|
||||
tags: vec!["composer-script".into()],
|
||||
..TaskTemplate::default()
|
||||
},
|
||||
]))
|
||||
pub(crate) struct JsonTaskProvider;
|
||||
|
||||
impl ContextProvider for JsonTaskProvider {
|
||||
fn associated_tasks(
|
||||
&self,
|
||||
_: Arc<dyn Fs>,
|
||||
file: Option<Arc<dyn language::File>>,
|
||||
cx: &App,
|
||||
) -> gpui::Task<Option<TaskTemplates>> {
|
||||
let Some(file) = project::File::from_dyn(file.as_ref()).cloned() else {
|
||||
return Task::ready(None);
|
||||
};
|
||||
let is_package_json = file.path.ends_with("package.json");
|
||||
let is_composer_json = file.path.ends_with("composer.json");
|
||||
if !is_package_json && !is_composer_json {
|
||||
return Task::ready(None);
|
||||
}
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let contents = file
|
||||
.worktree
|
||||
.update(cx, |this, cx| this.load_file(&file.path, cx))
|
||||
.ok()?
|
||||
.await
|
||||
.ok()?;
|
||||
|
||||
let task_templates = if is_package_json {
|
||||
let package_json = serde_json_lenient::from_str::<
|
||||
HashMap<String, serde_json_lenient::Value>,
|
||||
>(&contents.text)
|
||||
.ok()?;
|
||||
let package_json = PackageJsonData::new(file.path.clone(), package_json);
|
||||
let command = package_json.package_manager.unwrap_or("npm").to_owned();
|
||||
package_json
|
||||
.scripts
|
||||
.into_iter()
|
||||
.map(|(_, key)| TaskTemplate {
|
||||
label: format!("run {key}"),
|
||||
command: command.clone(),
|
||||
args: vec!["run".into(), key],
|
||||
cwd: Some(VariableName::Dirname.template_value()),
|
||||
..TaskTemplate::default()
|
||||
})
|
||||
.chain([TaskTemplate {
|
||||
label: "package script $ZED_CUSTOM_script".to_owned(),
|
||||
command: command.clone(),
|
||||
args: vec![
|
||||
"run".into(),
|
||||
VariableName::Custom("script".into()).template_value(),
|
||||
],
|
||||
cwd: Some(VariableName::Dirname.template_value()),
|
||||
tags: vec!["package-script".into()],
|
||||
..TaskTemplate::default()
|
||||
}])
|
||||
.collect()
|
||||
} else if is_composer_json {
|
||||
serde_json_lenient::Value::from_str(&contents.text)
|
||||
.ok()?
|
||||
.get("scripts")?
|
||||
.as_object()?
|
||||
.keys()
|
||||
.map(|key| TaskTemplate {
|
||||
label: format!("run {key}"),
|
||||
command: "composer".to_owned(),
|
||||
args: vec!["-d".into(), "$ZED_DIRNAME".into(), key.into()],
|
||||
..TaskTemplate::default()
|
||||
})
|
||||
.chain([TaskTemplate {
|
||||
label: "composer script $ZED_CUSTOM_script".to_owned(),
|
||||
command: "composer".to_owned(),
|
||||
args: vec![
|
||||
"-d".into(),
|
||||
"$ZED_DIRNAME".into(),
|
||||
VariableName::Custom("script".into()).template_value(),
|
||||
],
|
||||
tags: vec!["composer-script".into()],
|
||||
..TaskTemplate::default()
|
||||
}])
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
Some(TaskTemplates(task_templates))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn server_binary_arguments(server_path: &Path) -> Vec<OsString> {
|
||||
|
|
|
@ -10,6 +10,6 @@ brackets = [
|
|||
]
|
||||
tab_size = 2
|
||||
prettier_parser_name = "json"
|
||||
|
||||
debuggers = ["JavaScript"]
|
||||
[overrides.string]
|
||||
completion_query_characters = [":", " "]
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use anyhow::Context as _;
|
||||
use gpui::{App, UpdateGlobal};
|
||||
use json::json_task_context;
|
||||
use node_runtime::NodeRuntime;
|
||||
use python::PyprojectTomlManifestProvider;
|
||||
use rust::CargoManifestProvider;
|
||||
|
@ -12,11 +11,14 @@ use util::{ResultExt, asset_str};
|
|||
|
||||
pub use language::*;
|
||||
|
||||
use crate::json::JsonTaskProvider;
|
||||
|
||||
mod bash;
|
||||
mod c;
|
||||
mod css;
|
||||
mod go;
|
||||
mod json;
|
||||
mod package_json;
|
||||
mod python;
|
||||
mod rust;
|
||||
mod tailwind;
|
||||
|
@ -24,6 +26,8 @@ mod typescript;
|
|||
mod vtsls;
|
||||
mod yaml;
|
||||
|
||||
pub(crate) use package_json::{PackageJson, PackageJsonData};
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "src/"]
|
||||
#[exclude = "*.rs"]
|
||||
|
@ -78,7 +82,7 @@ pub fn init(languages: Arc<LanguageRegistry>, node: NodeRuntime, cx: &mut App) {
|
|||
let eslint_adapter = Arc::new(typescript::EsLintLspAdapter::new(node.clone()));
|
||||
let go_context_provider = Arc::new(go::GoContextProvider);
|
||||
let go_lsp_adapter = Arc::new(go::GoLspAdapter);
|
||||
let json_context_provider = Arc::new(json_task_context());
|
||||
let json_context_provider = Arc::new(JsonTaskProvider);
|
||||
let json_lsp_adapter = Arc::new(json::JsonLspAdapter::new(node.clone(), languages.clone()));
|
||||
let node_version_lsp_adapter = Arc::new(json::NodeVersionAdapter);
|
||||
let py_lsp_adapter = Arc::new(python::PyLspAdapter::new());
|
||||
|
|
106
crates/languages/src/package_json.rs
Normal file
106
crates/languages/src/package_json.rs
Normal file
|
@ -0,0 +1,106 @@
|
|||
use chrono::{DateTime, Local};
|
||||
use collections::{BTreeSet, HashMap};
|
||||
use serde_json_lenient::Value;
|
||||
use std::{path::Path, sync::Arc};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PackageJson {
|
||||
pub mtime: DateTime<Local>,
|
||||
pub data: PackageJsonData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct PackageJsonData {
|
||||
pub jest_package_path: Option<Arc<Path>>,
|
||||
pub mocha_package_path: Option<Arc<Path>>,
|
||||
pub vitest_package_path: Option<Arc<Path>>,
|
||||
pub jasmine_package_path: Option<Arc<Path>>,
|
||||
pub scripts: BTreeSet<(Arc<Path>, String)>,
|
||||
pub package_manager: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl PackageJsonData {
|
||||
pub fn new(path: Arc<Path>, package_json: HashMap<String, Value>) -> Self {
|
||||
let mut scripts = BTreeSet::new();
|
||||
if let Some(Value::Object(package_json_scripts)) = package_json.get("scripts") {
|
||||
scripts.extend(
|
||||
package_json_scripts
|
||||
.keys()
|
||||
.cloned()
|
||||
.map(|name| (path.clone(), name)),
|
||||
);
|
||||
}
|
||||
|
||||
let mut jest_package_path = None;
|
||||
let mut mocha_package_path = None;
|
||||
let mut vitest_package_path = None;
|
||||
let mut jasmine_package_path = None;
|
||||
if let Some(Value::Object(dependencies)) = package_json.get("devDependencies") {
|
||||
if dependencies.contains_key("jest") {
|
||||
jest_package_path.get_or_insert_with(|| path.clone());
|
||||
}
|
||||
if dependencies.contains_key("mocha") {
|
||||
mocha_package_path.get_or_insert_with(|| path.clone());
|
||||
}
|
||||
if dependencies.contains_key("vitest") {
|
||||
vitest_package_path.get_or_insert_with(|| path.clone());
|
||||
}
|
||||
if dependencies.contains_key("jasmine") {
|
||||
jasmine_package_path.get_or_insert_with(|| path.clone());
|
||||
}
|
||||
}
|
||||
if let Some(Value::Object(dev_dependencies)) = package_json.get("dependencies") {
|
||||
if dev_dependencies.contains_key("jest") {
|
||||
jest_package_path.get_or_insert_with(|| path.clone());
|
||||
}
|
||||
if dev_dependencies.contains_key("mocha") {
|
||||
mocha_package_path.get_or_insert_with(|| path.clone());
|
||||
}
|
||||
if dev_dependencies.contains_key("vitest") {
|
||||
vitest_package_path.get_or_insert_with(|| path.clone());
|
||||
}
|
||||
if dev_dependencies.contains_key("jasmine") {
|
||||
jasmine_package_path.get_or_insert_with(|| path.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let package_manager = package_json
|
||||
.get("packageManager")
|
||||
.and_then(|value| value.as_str())
|
||||
.and_then(|value| {
|
||||
if value.starts_with("pnpm") {
|
||||
Some("pnpm")
|
||||
} else if value.starts_with("yarn") {
|
||||
Some("yarn")
|
||||
} else if value.starts_with("npm") {
|
||||
Some("npm")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
jest_package_path,
|
||||
mocha_package_path,
|
||||
vitest_package_path,
|
||||
jasmine_package_path,
|
||||
scripts,
|
||||
package_manager,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn merge(&mut self, other: Self) {
|
||||
self.jest_package_path = self.jest_package_path.take().or(other.jest_package_path);
|
||||
self.mocha_package_path = self.mocha_package_path.take().or(other.mocha_package_path);
|
||||
self.vitest_package_path = self
|
||||
.vitest_package_path
|
||||
.take()
|
||||
.or(other.vitest_package_path);
|
||||
self.jasmine_package_path = self
|
||||
.jasmine_package_path
|
||||
.take()
|
||||
.or(other.jasmine_package_path);
|
||||
self.scripts.extend(other.scripts);
|
||||
self.package_manager = self.package_manager.or(other.package_manager);
|
||||
}
|
||||
}
|
|
@ -268,10 +268,15 @@ impl LspAdapter for PythonLspAdapter {
|
|||
lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
|
||||
_ => return None,
|
||||
};
|
||||
let filter_range = item
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter| label.find(filter).map(|ix| ix..ix + filter.len()))
|
||||
.unwrap_or(0..label.len());
|
||||
Some(language::CodeLabel {
|
||||
text: label.clone(),
|
||||
runs: vec![(0..label.len(), highlight_id)],
|
||||
filter_range: 0..label.len(),
|
||||
filter_range,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1152,10 +1157,15 @@ impl LspAdapter for PyLspAdapter {
|
|||
lsp::CompletionItemKind::CONSTANT => grammar.highlight_id_for_name("constant")?,
|
||||
_ => return None,
|
||||
};
|
||||
let filter_range = item
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter| label.find(filter).map(|ix| ix..ix + filter.len()))
|
||||
.unwrap_or(0..label.len());
|
||||
Some(language::CodeLabel {
|
||||
text: label.clone(),
|
||||
runs: vec![(0..label.len(), highlight_id)],
|
||||
filter_range: 0..label.len(),
|
||||
filter_range,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
43
crates/languages/src/python/debugger.scm
Normal file
43
crates/languages/src/python/debugger.scm
Normal file
|
@ -0,0 +1,43 @@
|
|||
(identifier) @debug-variable
|
||||
(#eq? @debug-variable "self")
|
||||
|
||||
(assignment left: (identifier) @debug-variable)
|
||||
(assignment left: (pattern_list (identifier) @debug-variable))
|
||||
(assignment left: (tuple_pattern (identifier) @debug-variable))
|
||||
|
||||
(augmented_assignment left: (identifier) @debug-variable)
|
||||
|
||||
(for_statement left: (identifier) @debug-variable)
|
||||
(for_statement left: (pattern_list (identifier) @debug-variable))
|
||||
(for_statement left: (tuple_pattern (identifier) @debug-variable))
|
||||
|
||||
(for_in_clause left: (identifier) @debug-variable)
|
||||
(for_in_clause left: (pattern_list (identifier) @debug-variable))
|
||||
(for_in_clause left: (tuple_pattern (identifier) @debug-variable))
|
||||
|
||||
(as_pattern (identifier) @debug-variable)
|
||||
|
||||
(binary_operator left: (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
(binary_operator right: (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
(comparison_operator (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(list (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
(tuple (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
(set (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(subscript value: (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(attribute object: (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(return_statement (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(parenthesized_expression (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(argument_list (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(if_statement condition: (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(while_statement condition: (identifier) @debug-variable (#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(block) @debug-scope
|
||||
(module) @debug-scope
|
|
@ -25,7 +25,11 @@ use std::{
|
|||
use task::{TaskTemplate, TaskTemplates, TaskVariables, VariableName};
|
||||
use util::archive::extract_zip;
|
||||
use util::merge_json_value_into;
|
||||
use util::{ResultExt, fs::remove_matching, maybe};
|
||||
use util::{
|
||||
ResultExt,
|
||||
fs::{make_file_executable, remove_matching},
|
||||
maybe,
|
||||
};
|
||||
|
||||
use crate::language_settings::language_settings;
|
||||
|
||||
|
@ -226,14 +230,7 @@ impl LspAdapter for RustLspAdapter {
|
|||
};
|
||||
|
||||
// todo("windows")
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
fs::set_permissions(
|
||||
&server_path,
|
||||
<fs::Permissions as fs::unix::PermissionsExt>::from_mode(0o755),
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
make_file_executable(&server_path).await?;
|
||||
}
|
||||
|
||||
Ok(LanguageServerBinary {
|
||||
|
@ -313,10 +310,15 @@ impl LspAdapter for RustLspAdapter {
|
|||
let source = Rope::from(format!("{prefix}{text} }}"));
|
||||
let runs =
|
||||
language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
|
||||
.unwrap_or(0..name.len());
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..name.len(),
|
||||
filter_range,
|
||||
});
|
||||
}
|
||||
(
|
||||
|
@ -333,10 +335,15 @@ impl LspAdapter for RustLspAdapter {
|
|||
let source = Rope::from(format!("{prefix}{text} = ();"));
|
||||
let runs =
|
||||
language.highlight_text(&source, prefix.len()..prefix.len() + text.len());
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
|
||||
.unwrap_or(0..name.len());
|
||||
return Some(CodeLabel {
|
||||
text,
|
||||
runs,
|
||||
filter_range: 0..name.len(),
|
||||
filter_range,
|
||||
});
|
||||
}
|
||||
(
|
||||
|
@ -370,9 +377,13 @@ impl LspAdapter for RustLspAdapter {
|
|||
text.push(' ');
|
||||
text.push_str(&detail);
|
||||
}
|
||||
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
|
||||
.unwrap_or(0..completion.label.find('(').unwrap_or(text.len()));
|
||||
return Some(CodeLabel {
|
||||
filter_range: 0..completion.label.find('(').unwrap_or(text.len()),
|
||||
filter_range,
|
||||
text,
|
||||
runs,
|
||||
});
|
||||
|
@ -381,12 +392,18 @@ impl LspAdapter for RustLspAdapter {
|
|||
.as_ref()
|
||||
.map_or(false, |detail| detail.starts_with("macro_rules! "))
|
||||
{
|
||||
let source = Rope::from(completion.label.as_str());
|
||||
let runs = language.highlight_text(&source, 0..completion.label.len());
|
||||
|
||||
let text = completion.label.clone();
|
||||
let len = text.len();
|
||||
let source = Rope::from(text.as_str());
|
||||
let runs = language.highlight_text(&source, 0..len);
|
||||
let filter_range = completion
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
|
||||
.unwrap_or(0..len);
|
||||
return Some(CodeLabel {
|
||||
filter_range: 0..completion.label.len(),
|
||||
text: completion.label.clone(),
|
||||
filter_range,
|
||||
text,
|
||||
runs,
|
||||
});
|
||||
}
|
||||
|
@ -409,7 +426,7 @@ impl LspAdapter for RustLspAdapter {
|
|||
label.push(' ');
|
||||
label.push_str(detail);
|
||||
}
|
||||
let mut label = CodeLabel::plain(label, None);
|
||||
let mut label = CodeLabel::plain(label, completion.filter_text.as_deref());
|
||||
if let Some(highlight_name) = highlight_name {
|
||||
let highlight_id = language.grammar()?.highlight_id_for_name(highlight_name)?;
|
||||
label.runs.push((
|
||||
|
@ -1184,6 +1201,49 @@ mod tests {
|
|||
],
|
||||
})
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
adapter
|
||||
.label_for_completion(
|
||||
&lsp::CompletionItem {
|
||||
kind: Some(lsp::CompletionItemKind::METHOD),
|
||||
label: "await.as_deref_mut()".to_string(),
|
||||
filter_text: Some("as_deref_mut".to_string()),
|
||||
label_details: Some(CompletionItemLabelDetails {
|
||||
detail: None,
|
||||
description: Some("fn(&mut self) -> IterMut<'_, T>".to_string()),
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
&language
|
||||
)
|
||||
.await,
|
||||
Some(CodeLabel {
|
||||
text: "await.as_deref_mut()".to_string(),
|
||||
filter_range: 6..18,
|
||||
runs: vec![],
|
||||
})
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
adapter
|
||||
.label_for_completion(
|
||||
&lsp::CompletionItem {
|
||||
kind: Some(lsp::CompletionItemKind::FIELD),
|
||||
label: "inner_value".to_string(),
|
||||
filter_text: Some("value".to_string()),
|
||||
detail: Some("String".to_string()),
|
||||
..Default::default()
|
||||
},
|
||||
&language,
|
||||
)
|
||||
.await,
|
||||
Some(CodeLabel {
|
||||
text: "inner_value: String".to_string(),
|
||||
filter_range: 6..11,
|
||||
runs: vec![(0..11, HighlightId(3)), (13..19, HighlightId(0))],
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
|
|
50
crates/languages/src/rust/debugger.scm
Normal file
50
crates/languages/src/rust/debugger.scm
Normal file
|
@ -0,0 +1,50 @@
|
|||
(metavariable) @debug-variable
|
||||
|
||||
(parameter (identifier) @debug-variable)
|
||||
|
||||
(self) @debug-variable
|
||||
|
||||
(static_item (identifier) @debug-variable)
|
||||
(const_item (identifier) @debug-variable)
|
||||
|
||||
(let_declaration pattern: (identifier) @debug-variable)
|
||||
|
||||
(let_condition (identifier) @debug-variable)
|
||||
|
||||
(match_arm (identifier) @debug-variable)
|
||||
|
||||
(for_expression (identifier) @debug-variable)
|
||||
|
||||
(closure_parameters (identifier) @debug-variable)
|
||||
|
||||
(assignment_expression (identifier) @debug-variable)
|
||||
|
||||
(field_expression (identifier) @debug-variable)
|
||||
|
||||
(binary_expression (identifier) @debug-variable
|
||||
(#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(reference_expression (identifier) @debug-variable
|
||||
(#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(array_expression (identifier) @debug-variable)
|
||||
(tuple_expression (identifier) @debug-variable)
|
||||
(return_expression (identifier) @debug-variable)
|
||||
(await_expression (identifier) @debug-variable)
|
||||
(try_expression (identifier) @debug-variable)
|
||||
(index_expression (identifier) @debug-variable)
|
||||
(range_expression (identifier) @debug-variable)
|
||||
(unary_expression (identifier) @debug-variable)
|
||||
|
||||
(if_expression (identifier) @debug-variable)
|
||||
(while_expression (identifier) @debug-variable)
|
||||
|
||||
(parenthesized_expression (identifier) @debug-variable)
|
||||
|
||||
(arguments (identifier) @debug-variable
|
||||
(#not-match? @debug-variable "^[A-Z]"))
|
||||
|
||||
(macro_invocation (token_tree (identifier) @debug-variable
|
||||
(#not-match? @debug-variable "^[A-Z]")))
|
||||
|
||||
(block) @debug-scope
|
|
@ -83,7 +83,30 @@
|
|||
] @context
|
||||
(#any-of? @_name "it" "test" "describe" "context" "suite")
|
||||
arguments: (
|
||||
arguments . (string (string_fragment) @name)
|
||||
arguments . [
|
||||
(string (string_fragment) @name)
|
||||
(identifier) @name
|
||||
]
|
||||
)
|
||||
)
|
||||
) @item
|
||||
|
||||
; Add support for parameterized tests
|
||||
(
|
||||
(call_expression
|
||||
function: (call_expression
|
||||
function: (member_expression
|
||||
object: [(identifier) @_name (member_expression object: (identifier) @_name)]
|
||||
property: (property_identifier) @_property
|
||||
)
|
||||
(#any-of? @_name "it" "test" "describe" "context" "suite")
|
||||
(#any-of? @_property "each")
|
||||
)
|
||||
arguments: (
|
||||
arguments . [
|
||||
(string (string_fragment) @name)
|
||||
(identifier) @name
|
||||
]
|
||||
)
|
||||
)
|
||||
) @item
|
||||
|
|
|
@ -13,7 +13,32 @@
|
|||
]
|
||||
(#any-of? @_name "it" "test" "describe" "context" "suite")
|
||||
arguments: (
|
||||
arguments . (string (string_fragment) @run)
|
||||
arguments . [
|
||||
(string (string_fragment) @run)
|
||||
(identifier) @run
|
||||
]
|
||||
)
|
||||
) @_js-test
|
||||
|
||||
(#set! tag js-test)
|
||||
)
|
||||
|
||||
; Add support for parameterized tests
|
||||
(
|
||||
(call_expression
|
||||
function: (call_expression
|
||||
function: (member_expression
|
||||
object: [(identifier) @_name (member_expression object: (identifier) @_name)]
|
||||
property: (property_identifier) @_property
|
||||
)
|
||||
(#any-of? @_name "it" "test" "describe" "context" "suite")
|
||||
(#any-of? @_property "each")
|
||||
)
|
||||
arguments: (
|
||||
arguments . [
|
||||
(string (string_fragment) @run)
|
||||
(identifier) @run
|
||||
]
|
||||
)
|
||||
) @_js-test
|
||||
|
||||
|
|
|
@ -8,8 +8,7 @@ use futures::future::join_all;
|
|||
use gpui::{App, AppContext, AsyncApp, Task};
|
||||
use http_client::github::{AssetKind, GitHubLspBinaryVersion, build_asset_url};
|
||||
use language::{
|
||||
ContextLocation, ContextProvider, File, LanguageToolchainStore, LocalFile, LspAdapter,
|
||||
LspAdapterDelegate,
|
||||
ContextLocation, ContextProvider, File, LanguageToolchainStore, LspAdapter, LspAdapterDelegate,
|
||||
};
|
||||
use lsp::{CodeActionKind, LanguageServerBinary, LanguageServerName};
|
||||
use node_runtime::NodeRuntime;
|
||||
|
@ -19,7 +18,6 @@ use smol::{fs, io::BufReader, lock::RwLock, stream::StreamExt};
|
|||
use std::{
|
||||
any::Any,
|
||||
borrow::Cow,
|
||||
collections::BTreeSet,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
|
@ -29,6 +27,9 @@ use util::archive::extract_zip;
|
|||
use util::merge_json_value_into;
|
||||
use util::{ResultExt, fs::remove_matching, maybe};
|
||||
|
||||
use crate::{PackageJson, PackageJsonData};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct TypeScriptContextProvider {
|
||||
last_package_json: PackageJsonContents,
|
||||
}
|
||||
|
@ -42,126 +43,76 @@ const TYPESCRIPT_JEST_TEST_NAME_VARIABLE: VariableName =
|
|||
const TYPESCRIPT_VITEST_TEST_NAME_VARIABLE: VariableName =
|
||||
VariableName::Custom(Cow::Borrowed("TYPESCRIPT_VITEST_TEST_NAME"));
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
const TYPESCRIPT_JEST_PACKAGE_PATH_VARIABLE: VariableName =
|
||||
VariableName::Custom(Cow::Borrowed("TYPESCRIPT_JEST_PACKAGE_PATH"));
|
||||
|
||||
const TYPESCRIPT_MOCHA_PACKAGE_PATH_VARIABLE: VariableName =
|
||||
VariableName::Custom(Cow::Borrowed("TYPESCRIPT_MOCHA_PACKAGE_PATH"));
|
||||
|
||||
const TYPESCRIPT_VITEST_PACKAGE_PATH_VARIABLE: VariableName =
|
||||
VariableName::Custom(Cow::Borrowed("TYPESCRIPT_VITEST_PACKAGE_PATH"));
|
||||
|
||||
const TYPESCRIPT_JASMINE_PACKAGE_PATH_VARIABLE: VariableName =
|
||||
VariableName::Custom(Cow::Borrowed("TYPESCRIPT_JASMINE_PACKAGE_PATH"));
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
struct PackageJsonContents(Arc<RwLock<HashMap<PathBuf, PackageJson>>>);
|
||||
|
||||
struct PackageJson {
|
||||
mtime: DateTime<Local>,
|
||||
data: PackageJsonData,
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
struct PackageJsonData {
|
||||
jest: bool,
|
||||
mocha: bool,
|
||||
vitest: bool,
|
||||
jasmine: bool,
|
||||
scripts: BTreeSet<String>,
|
||||
package_manager: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl PackageJsonData {
|
||||
fn new(package_json: HashMap<String, Value>) -> Self {
|
||||
let mut scripts = BTreeSet::new();
|
||||
if let Some(serde_json::Value::Object(package_json_scripts)) = package_json.get("scripts") {
|
||||
scripts.extend(package_json_scripts.keys().cloned());
|
||||
}
|
||||
|
||||
let mut jest = false;
|
||||
let mut mocha = false;
|
||||
let mut vitest = false;
|
||||
let mut jasmine = false;
|
||||
if let Some(serde_json::Value::Object(dependencies)) = package_json.get("devDependencies") {
|
||||
jest |= dependencies.contains_key("jest");
|
||||
mocha |= dependencies.contains_key("mocha");
|
||||
vitest |= dependencies.contains_key("vitest");
|
||||
jasmine |= dependencies.contains_key("jasmine");
|
||||
}
|
||||
if let Some(serde_json::Value::Object(dev_dependencies)) = package_json.get("dependencies")
|
||||
{
|
||||
jest |= dev_dependencies.contains_key("jest");
|
||||
mocha |= dev_dependencies.contains_key("mocha");
|
||||
vitest |= dev_dependencies.contains_key("vitest");
|
||||
jasmine |= dev_dependencies.contains_key("jasmine");
|
||||
}
|
||||
|
||||
let package_manager = package_json
|
||||
.get("packageManager")
|
||||
.and_then(|value| value.as_str())
|
||||
.and_then(|value| {
|
||||
if value.starts_with("pnpm") {
|
||||
Some("pnpm")
|
||||
} else if value.starts_with("yarn") {
|
||||
Some("yarn")
|
||||
} else if value.starts_with("npm") {
|
||||
Some("npm")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
jest,
|
||||
mocha,
|
||||
vitest,
|
||||
jasmine,
|
||||
scripts,
|
||||
package_manager,
|
||||
}
|
||||
}
|
||||
|
||||
fn merge(&mut self, other: Self) {
|
||||
self.jest |= other.jest;
|
||||
self.mocha |= other.mocha;
|
||||
self.vitest |= other.vitest;
|
||||
self.jasmine |= other.jasmine;
|
||||
self.scripts.extend(other.scripts);
|
||||
}
|
||||
|
||||
fn fill_task_templates(&self, task_templates: &mut TaskTemplates) {
|
||||
if self.jest {
|
||||
if self.jest_package_path.is_some() {
|
||||
task_templates.0.push(TaskTemplate {
|
||||
label: "jest file test".to_owned(),
|
||||
command: TYPESCRIPT_RUNNER_VARIABLE.template_value(),
|
||||
args: vec![
|
||||
"exec".to_owned(),
|
||||
"--".to_owned(),
|
||||
"jest".to_owned(),
|
||||
VariableName::RelativeFile.template_value(),
|
||||
"--runInBand".to_owned(),
|
||||
VariableName::File.template_value(),
|
||||
],
|
||||
cwd: Some(VariableName::WorktreeRoot.template_value()),
|
||||
cwd: Some(TYPESCRIPT_JEST_PACKAGE_PATH_VARIABLE.template_value()),
|
||||
..TaskTemplate::default()
|
||||
});
|
||||
task_templates.0.push(TaskTemplate {
|
||||
label: format!("jest test {}", VariableName::Symbol.template_value()),
|
||||
command: TYPESCRIPT_RUNNER_VARIABLE.template_value(),
|
||||
args: vec![
|
||||
"exec".to_owned(),
|
||||
"--".to_owned(),
|
||||
"jest".to_owned(),
|
||||
"--runInBand".to_owned(),
|
||||
"--testNamePattern".to_owned(),
|
||||
format!(
|
||||
"\"{}\"",
|
||||
TYPESCRIPT_JEST_TEST_NAME_VARIABLE.template_value()
|
||||
),
|
||||
VariableName::RelativeFile.template_value(),
|
||||
VariableName::File.template_value(),
|
||||
],
|
||||
tags: vec![
|
||||
"ts-test".to_owned(),
|
||||
"js-test".to_owned(),
|
||||
"tsx-test".to_owned(),
|
||||
],
|
||||
cwd: Some(VariableName::WorktreeRoot.template_value()),
|
||||
cwd: Some(TYPESCRIPT_JEST_PACKAGE_PATH_VARIABLE.template_value()),
|
||||
..TaskTemplate::default()
|
||||
});
|
||||
}
|
||||
|
||||
if self.vitest {
|
||||
if self.vitest_package_path.is_some() {
|
||||
task_templates.0.push(TaskTemplate {
|
||||
label: format!("{} file test", "vitest".to_owned()),
|
||||
command: TYPESCRIPT_RUNNER_VARIABLE.template_value(),
|
||||
args: vec![
|
||||
"exec".to_owned(),
|
||||
"--".to_owned(),
|
||||
"vitest".to_owned(),
|
||||
"run".to_owned(),
|
||||
VariableName::RelativeFile.template_value(),
|
||||
"--poolOptions.forks.minForks=0".to_owned(),
|
||||
"--poolOptions.forks.maxForks=1".to_owned(),
|
||||
VariableName::File.template_value(),
|
||||
],
|
||||
cwd: Some(VariableName::WorktreeRoot.template_value()),
|
||||
cwd: Some(TYPESCRIPT_VITEST_PACKAGE_PATH_VARIABLE.template_value()),
|
||||
..TaskTemplate::default()
|
||||
});
|
||||
task_templates.0.push(TaskTemplate {
|
||||
|
@ -172,31 +123,40 @@ impl PackageJsonData {
|
|||
),
|
||||
command: TYPESCRIPT_RUNNER_VARIABLE.template_value(),
|
||||
args: vec![
|
||||
"exec".to_owned(),
|
||||
"--".to_owned(),
|
||||
"vitest".to_owned(),
|
||||
"run".to_owned(),
|
||||
"--poolOptions.forks.minForks=0".to_owned(),
|
||||
"--poolOptions.forks.maxForks=1".to_owned(),
|
||||
"--testNamePattern".to_owned(),
|
||||
format!("\"{}\"", "vitest".to_owned()),
|
||||
VariableName::RelativeFile.template_value(),
|
||||
format!(
|
||||
"\"{}\"",
|
||||
TYPESCRIPT_VITEST_TEST_NAME_VARIABLE.template_value()
|
||||
),
|
||||
VariableName::File.template_value(),
|
||||
],
|
||||
tags: vec![
|
||||
"ts-test".to_owned(),
|
||||
"js-test".to_owned(),
|
||||
"tsx-test".to_owned(),
|
||||
],
|
||||
cwd: Some(VariableName::WorktreeRoot.template_value()),
|
||||
cwd: Some(TYPESCRIPT_VITEST_PACKAGE_PATH_VARIABLE.template_value()),
|
||||
..TaskTemplate::default()
|
||||
});
|
||||
}
|
||||
|
||||
if self.mocha {
|
||||
if self.mocha_package_path.is_some() {
|
||||
task_templates.0.push(TaskTemplate {
|
||||
label: format!("{} file test", "mocha".to_owned()),
|
||||
command: TYPESCRIPT_RUNNER_VARIABLE.template_value(),
|
||||
args: vec![
|
||||
"exec".to_owned(),
|
||||
"--".to_owned(),
|
||||
"mocha".to_owned(),
|
||||
VariableName::RelativeFile.template_value(),
|
||||
VariableName::File.template_value(),
|
||||
],
|
||||
cwd: Some(VariableName::WorktreeRoot.template_value()),
|
||||
cwd: Some(TYPESCRIPT_MOCHA_PACKAGE_PATH_VARIABLE.template_value()),
|
||||
..TaskTemplate::default()
|
||||
});
|
||||
task_templates.0.push(TaskTemplate {
|
||||
|
@ -207,30 +167,34 @@ impl PackageJsonData {
|
|||
),
|
||||
command: TYPESCRIPT_RUNNER_VARIABLE.template_value(),
|
||||
args: vec![
|
||||
"exec".to_owned(),
|
||||
"--".to_owned(),
|
||||
"mocha".to_owned(),
|
||||
"--grep".to_owned(),
|
||||
format!("\"{}\"", VariableName::Symbol.template_value()),
|
||||
VariableName::RelativeFile.template_value(),
|
||||
VariableName::File.template_value(),
|
||||
],
|
||||
tags: vec![
|
||||
"ts-test".to_owned(),
|
||||
"js-test".to_owned(),
|
||||
"tsx-test".to_owned(),
|
||||
],
|
||||
cwd: Some(VariableName::WorktreeRoot.template_value()),
|
||||
cwd: Some(TYPESCRIPT_MOCHA_PACKAGE_PATH_VARIABLE.template_value()),
|
||||
..TaskTemplate::default()
|
||||
});
|
||||
}
|
||||
|
||||
if self.jasmine {
|
||||
if self.jasmine_package_path.is_some() {
|
||||
task_templates.0.push(TaskTemplate {
|
||||
label: format!("{} file test", "jasmine".to_owned()),
|
||||
command: TYPESCRIPT_RUNNER_VARIABLE.template_value(),
|
||||
args: vec![
|
||||
"exec".to_owned(),
|
||||
"--".to_owned(),
|
||||
"jasmine".to_owned(),
|
||||
VariableName::RelativeFile.template_value(),
|
||||
VariableName::File.template_value(),
|
||||
],
|
||||
cwd: Some(VariableName::WorktreeRoot.template_value()),
|
||||
cwd: Some(TYPESCRIPT_JASMINE_PACKAGE_PATH_VARIABLE.template_value()),
|
||||
..TaskTemplate::default()
|
||||
});
|
||||
task_templates.0.push(TaskTemplate {
|
||||
|
@ -241,32 +205,34 @@ impl PackageJsonData {
|
|||
),
|
||||
command: TYPESCRIPT_RUNNER_VARIABLE.template_value(),
|
||||
args: vec![
|
||||
"exec".to_owned(),
|
||||
"--".to_owned(),
|
||||
"jasmine".to_owned(),
|
||||
format!("--filter={}", VariableName::Symbol.template_value()),
|
||||
VariableName::RelativeFile.template_value(),
|
||||
VariableName::File.template_value(),
|
||||
],
|
||||
tags: vec![
|
||||
"ts-test".to_owned(),
|
||||
"js-test".to_owned(),
|
||||
"tsx-test".to_owned(),
|
||||
],
|
||||
cwd: Some(VariableName::WorktreeRoot.template_value()),
|
||||
cwd: Some(TYPESCRIPT_JASMINE_PACKAGE_PATH_VARIABLE.template_value()),
|
||||
..TaskTemplate::default()
|
||||
});
|
||||
}
|
||||
|
||||
for script in &self.scripts {
|
||||
for (path, script) in &self.scripts {
|
||||
task_templates.0.push(TaskTemplate {
|
||||
label: format!("package.json > {script}",),
|
||||
command: TYPESCRIPT_RUNNER_VARIABLE.template_value(),
|
||||
args: vec![
|
||||
"--prefix".to_owned(),
|
||||
VariableName::WorktreeRoot.template_value(),
|
||||
"run".to_owned(),
|
||||
script.to_owned(),
|
||||
],
|
||||
args: vec!["run".to_owned(), script.to_owned()],
|
||||
tags: vec!["package-script".into()],
|
||||
cwd: Some(VariableName::WorktreeRoot.template_value()),
|
||||
cwd: Some(
|
||||
path.parent()
|
||||
.unwrap_or(Path::new(""))
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
),
|
||||
..TaskTemplate::default()
|
||||
});
|
||||
}
|
||||
|
@ -284,13 +250,9 @@ impl TypeScriptContextProvider {
|
|||
&self,
|
||||
fs: Arc<dyn Fs>,
|
||||
worktree_root: &Path,
|
||||
file_abs_path: &Path,
|
||||
file_relative_path: &Path,
|
||||
cx: &App,
|
||||
) -> Task<anyhow::Result<PackageJsonData>> {
|
||||
let Some(file_relative_path) = file_abs_path.strip_prefix(&worktree_root).ok() else {
|
||||
log::debug!("No package json data for off-worktree files");
|
||||
return Task::ready(Ok(PackageJsonData::default()));
|
||||
};
|
||||
let new_json_data = file_relative_path
|
||||
.ancestors()
|
||||
.map(|path| worktree_root.join(path))
|
||||
|
@ -338,11 +300,12 @@ impl TypeScriptContextProvider {
|
|||
fs.load(&package_json_path).await.with_context(|| {
|
||||
format!("loading package.json from {package_json_path:?}")
|
||||
})?;
|
||||
let package_json: HashMap<String, serde_json::Value> =
|
||||
serde_json::from_str(&package_json_string).with_context(|| {
|
||||
let package_json: HashMap<String, serde_json_lenient::Value> =
|
||||
serde_json_lenient::from_str(&package_json_string).with_context(|| {
|
||||
format!("parsing package.json from {package_json_path:?}")
|
||||
})?;
|
||||
let new_data = PackageJsonData::new(package_json);
|
||||
let new_data =
|
||||
PackageJsonData::new(package_json_path.as_path().into(), package_json);
|
||||
{
|
||||
let mut contents = existing_package_json.0.write().await;
|
||||
contents.insert(
|
||||
|
@ -358,31 +321,25 @@ impl TypeScriptContextProvider {
|
|||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_package_manager(
|
||||
&self,
|
||||
worktree_root: PathBuf,
|
||||
fs: Arc<dyn Fs>,
|
||||
cx: &App,
|
||||
) -> Task<&'static str> {
|
||||
let last_package_json = self.last_package_json.clone();
|
||||
let package_json_data =
|
||||
self.package_json_data(&worktree_root, last_package_json, fs.clone(), cx);
|
||||
cx.background_spawn(async move {
|
||||
if let Ok(package_json_data) = package_json_data.await {
|
||||
if let Some(package_manager) = package_json_data.package_manager {
|
||||
return package_manager;
|
||||
}
|
||||
}
|
||||
if fs.is_file(&worktree_root.join("pnpm-lock.yaml")).await {
|
||||
return "pnpm";
|
||||
}
|
||||
if fs.is_file(&worktree_root.join("yarn.lock")).await {
|
||||
return "yarn";
|
||||
}
|
||||
"npm"
|
||||
})
|
||||
async fn detect_package_manager(
|
||||
worktree_root: PathBuf,
|
||||
fs: Arc<dyn Fs>,
|
||||
package_json_data: Option<PackageJsonData>,
|
||||
) -> &'static str {
|
||||
if let Some(package_json_data) = package_json_data {
|
||||
if let Some(package_manager) = package_json_data.package_manager {
|
||||
return package_manager;
|
||||
}
|
||||
}
|
||||
if fs.is_file(&worktree_root.join("pnpm-lock.yaml")).await {
|
||||
return "pnpm";
|
||||
}
|
||||
if fs.is_file(&worktree_root.join("yarn.lock")).await {
|
||||
return "yarn";
|
||||
}
|
||||
"npm"
|
||||
}
|
||||
|
||||
impl ContextProvider for TypeScriptContextProvider {
|
||||
|
@ -398,9 +355,9 @@ impl ContextProvider for TypeScriptContextProvider {
|
|||
let Some(worktree_root) = file.worktree.read(cx).root_dir() else {
|
||||
return Task::ready(None);
|
||||
};
|
||||
let file_abs_path = file.abs_path(cx);
|
||||
let file_relative_path = file.path().clone();
|
||||
let package_json_data =
|
||||
self.combined_package_json_data(fs.clone(), &worktree_root, &file_abs_path, cx);
|
||||
self.combined_package_json_data(fs.clone(), &worktree_root, &file_relative_path, cx);
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let mut task_templates = TaskTemplates(Vec::new());
|
||||
|
@ -423,7 +380,7 @@ impl ContextProvider for TypeScriptContextProvider {
|
|||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Failed to read package.json for worktree {file_abs_path:?}: {e:#}"
|
||||
"Failed to read package.json for worktree {file_relative_path:?}: {e:#}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -452,14 +409,73 @@ impl ContextProvider for TypeScriptContextProvider {
|
|||
replace_test_name_parameters(symbol),
|
||||
);
|
||||
}
|
||||
let file_path = location
|
||||
.file_location
|
||||
.buffer
|
||||
.read(cx)
|
||||
.file()
|
||||
.map(|file| file.path());
|
||||
|
||||
let task = location
|
||||
.worktree_root
|
||||
.zip(location.fs)
|
||||
.map(|(worktree_root, fs)| self.detect_package_manager(worktree_root, fs, cx));
|
||||
let args = location.worktree_root.zip(location.fs).zip(file_path).map(
|
||||
|((worktree_root, fs), file_path)| {
|
||||
(
|
||||
self.combined_package_json_data(fs.clone(), &worktree_root, file_path, cx),
|
||||
worktree_root,
|
||||
fs,
|
||||
)
|
||||
},
|
||||
);
|
||||
cx.background_spawn(async move {
|
||||
if let Some(task) = task {
|
||||
vars.insert(TYPESCRIPT_RUNNER_VARIABLE, task.await.to_owned());
|
||||
if let Some((task, worktree_root, fs)) = args {
|
||||
let package_json_data = task.await.log_err();
|
||||
vars.insert(
|
||||
TYPESCRIPT_RUNNER_VARIABLE,
|
||||
detect_package_manager(worktree_root, fs, package_json_data.clone())
|
||||
.await
|
||||
.to_owned(),
|
||||
);
|
||||
|
||||
if let Some(package_json_data) = package_json_data {
|
||||
if let Some(path) = package_json_data.jest_package_path {
|
||||
vars.insert(
|
||||
TYPESCRIPT_JEST_PACKAGE_PATH_VARIABLE,
|
||||
path.parent()
|
||||
.unwrap_or(Path::new(""))
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(path) = package_json_data.mocha_package_path {
|
||||
vars.insert(
|
||||
TYPESCRIPT_MOCHA_PACKAGE_PATH_VARIABLE,
|
||||
path.parent()
|
||||
.unwrap_or(Path::new(""))
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(path) = package_json_data.vitest_package_path {
|
||||
vars.insert(
|
||||
TYPESCRIPT_VITEST_PACKAGE_PATH_VARIABLE,
|
||||
path.parent()
|
||||
.unwrap_or(Path::new(""))
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(path) = package_json_data.jasmine_package_path {
|
||||
vars.insert(
|
||||
TYPESCRIPT_JASMINE_PACKAGE_PATH_VARIABLE,
|
||||
path.parent()
|
||||
.unwrap_or(Path::new(""))
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(vars)
|
||||
})
|
||||
|
@ -652,11 +668,15 @@ impl LspAdapter for TypeScriptLspAdapter {
|
|||
} else {
|
||||
item.label.clone()
|
||||
};
|
||||
|
||||
let filter_range = item
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
|
||||
.unwrap_or(0..len);
|
||||
Some(language::CodeLabel {
|
||||
text,
|
||||
runs: vec![(0..len, highlight_id)],
|
||||
filter_range: 0..len,
|
||||
filter_range,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -747,8 +767,8 @@ pub struct EsLintLspAdapter {
|
|||
}
|
||||
|
||||
impl EsLintLspAdapter {
|
||||
const CURRENT_VERSION: &'static str = "2.4.4";
|
||||
const CURRENT_VERSION_TAG_NAME: &'static str = "release/2.4.4";
|
||||
const CURRENT_VERSION: &'static str = "3.0.10";
|
||||
const CURRENT_VERSION_TAG_NAME: &'static str = "release/3.0.10";
|
||||
|
||||
#[cfg(not(windows))]
|
||||
const GITHUB_ASSET_KIND: AssetKind = AssetKind::TarGz;
|
||||
|
@ -826,9 +846,7 @@ impl LspAdapter for EsLintLspAdapter {
|
|||
"enable": true
|
||||
}
|
||||
},
|
||||
"experimental": {
|
||||
"useFlatConfig": use_flat_config,
|
||||
},
|
||||
"useFlatConfig": use_flat_config,
|
||||
});
|
||||
|
||||
let override_options = cx.update(|cx| {
|
||||
|
@ -988,8 +1006,16 @@ async fn handle_symlink(src_dir: PathBuf, dest_dir: PathBuf) -> Result<()> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use gpui::{AppContext as _, TestAppContext};
|
||||
use std::path::Path;
|
||||
|
||||
use gpui::{AppContext as _, BackgroundExecutor, TestAppContext};
|
||||
use language::language_settings;
|
||||
use project::{FakeFs, Project};
|
||||
use serde_json::json;
|
||||
use unindent::Unindent;
|
||||
use util::path;
|
||||
|
||||
use crate::typescript::{PackageJsonData, TypeScriptContextProvider};
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_outline(cx: &mut TestAppContext) {
|
||||
|
@ -1030,4 +1056,82 @@ mod tests {
|
|||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_package_json_discovery(executor: BackgroundExecutor, cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
settings::init(cx);
|
||||
Project::init_settings(cx);
|
||||
language_settings::init(cx);
|
||||
});
|
||||
|
||||
let package_json_1 = json!({
|
||||
"dependencies": {
|
||||
"mocha": "1.0.0",
|
||||
"vitest": "1.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": ""
|
||||
}
|
||||
})
|
||||
.to_string();
|
||||
|
||||
let package_json_2 = json!({
|
||||
"devDependencies": {
|
||||
"vitest": "2.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": ""
|
||||
}
|
||||
})
|
||||
.to_string();
|
||||
|
||||
let fs = FakeFs::new(executor);
|
||||
fs.insert_tree(
|
||||
path!("/root"),
|
||||
json!({
|
||||
"package.json": package_json_1,
|
||||
"sub": {
|
||||
"package.json": package_json_2,
|
||||
"file.js": "",
|
||||
}
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
let provider = TypeScriptContextProvider::new();
|
||||
let package_json_data = cx
|
||||
.update(|cx| {
|
||||
provider.combined_package_json_data(
|
||||
fs.clone(),
|
||||
path!("/root").as_ref(),
|
||||
"sub/file1.js".as_ref(),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
pretty_assertions::assert_eq!(
|
||||
package_json_data,
|
||||
PackageJsonData {
|
||||
jest_package_path: None,
|
||||
mocha_package_path: Some(Path::new(path!("/root/package.json")).into()),
|
||||
vitest_package_path: Some(Path::new(path!("/root/sub/package.json")).into()),
|
||||
jasmine_package_path: None,
|
||||
scripts: [
|
||||
(
|
||||
Path::new(path!("/root/package.json")).into(),
|
||||
"test".to_owned()
|
||||
),
|
||||
(
|
||||
Path::new(path!("/root/sub/package.json")).into(),
|
||||
"test".to_owned()
|
||||
)
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
package_manager: None,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -195,11 +195,15 @@ impl LspAdapter for VtslsLspAdapter {
|
|||
} else {
|
||||
item.label.clone()
|
||||
};
|
||||
|
||||
let filter_range = item
|
||||
.filter_text
|
||||
.as_deref()
|
||||
.and_then(|filter| text.find(filter).map(|ix| ix..ix + filter.len()))
|
||||
.unwrap_or(0..len);
|
||||
Some(language::CodeLabel {
|
||||
text,
|
||||
runs: vec![(0..len, highlight_id)],
|
||||
filter_range: 0..len,
|
||||
filter_range,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue