summaryrefslogtreecommitdiff
path: root/cli/lsp/language_server.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/language_server.rs')
-rw-r--r--cli/lsp/language_server.rs121
1 files changed, 82 insertions, 39 deletions
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 8269dc851..2ce26c1f2 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -22,6 +22,7 @@ use deno_semver::jsr::JsrPackageReqReference;
use indexmap::Equivalent;
use indexmap::IndexSet;
use log::error;
+use node_resolver::NodeModuleKind;
use serde::Deserialize;
use serde_json::from_value;
use std::collections::BTreeMap;
@@ -77,6 +78,7 @@ use super::parent_process_checker;
use super::performance::Performance;
use super::refactor;
use super::registries::ModuleRegistry;
+use super::resolver::LspIsCjsResolver;
use super::resolver::LspResolver;
use super::testing;
use super::text;
@@ -144,6 +146,7 @@ pub struct StateSnapshot {
pub project_version: usize,
pub assets: AssetsSnapshot,
pub config: Arc<Config>,
+ pub is_cjs_resolver: Arc<LspIsCjsResolver>,
pub documents: Arc<Documents>,
pub resolver: Arc<LspResolver>,
}
@@ -203,6 +206,7 @@ pub struct Inner {
pub documents: Documents,
http_client_provider: Arc<HttpClientProvider>,
initial_cwd: PathBuf,
+ pub is_cjs_resolver: Arc<LspIsCjsResolver>,
jsr_search_api: CliJsrSearchApi,
/// Handles module registries, which allow discovery of modules
module_registry: ModuleRegistry,
@@ -480,6 +484,7 @@ impl Inner {
let initial_cwd = std::env::current_dir().unwrap_or_else(|_| {
panic!("Could not resolve current working directory")
});
+ let is_cjs_resolver = Arc::new(LspIsCjsResolver::new(&cache));
Self {
assets,
@@ -491,6 +496,7 @@ impl Inner {
documents,
http_client_provider,
initial_cwd: initial_cwd.clone(),
+ is_cjs_resolver,
jsr_search_api,
project_version: 0,
task_queue: Default::default(),
@@ -601,6 +607,7 @@ impl Inner {
project_version: self.project_version,
assets: self.assets.snapshot(),
config: Arc::new(self.config.clone()),
+ is_cjs_resolver: self.is_cjs_resolver.clone(),
documents: Arc::new(self.documents.clone()),
resolver: self.resolver.snapshot(),
})
@@ -622,6 +629,7 @@ impl Inner {
}
});
self.cache = LspCache::new(global_cache_url);
+ self.is_cjs_resolver = Arc::new(LspIsCjsResolver::new(&self.cache));
let deno_dir = self.cache.deno_dir();
let workspace_settings = self.config.workspace_settings();
let maybe_root_path = self
@@ -863,7 +871,10 @@ impl Inner {
// We ignore these directories by default because there is a
// high likelihood they aren't relevant. Someone can opt-into
// them by specifying one of them as an enabled path.
- if matches!(dir_name.as_str(), "vendor" | "node_modules" | ".git") {
+ if matches!(
+ dir_name.as_str(),
+ "vendor" | "coverage" | "node_modules" | ".git"
+ ) {
continue;
}
// ignore cargo target directories for anyone using Deno with Rust
@@ -904,7 +915,7 @@ impl Inner {
| MediaType::Tsx => {}
MediaType::Wasm
| MediaType::SourceMap
- | MediaType::TsBuildInfo
+ | MediaType::Css
| MediaType::Unknown => {
if path.extension().and_then(|s| s.to_str()) != Some("jsonc") {
continue;
@@ -963,6 +974,11 @@ impl Inner {
.tree
.refresh(&self.config.settings, &self.workspace_files, &file_fetcher)
.await;
+ self
+ .client
+ .send_did_refresh_deno_configuration_tree_notification(
+ self.config.tree.to_did_refresh_params(),
+ );
for config_file in self.config.tree.config_files() {
(|| {
let compiler_options = config_file.to_compiler_options().ok()?.options;
@@ -974,7 +990,7 @@ impl Inner {
spawn(async move {
let specifier = {
let inner = ls.inner.read().await;
- let resolver = inner.resolver.as_graph_resolver(Some(&referrer));
+ let resolver = inner.resolver.as_cli_resolver(Some(&referrer));
let Ok(specifier) = resolver.resolve(
&specifier,
&deno_graph::Range {
@@ -982,6 +998,7 @@ impl Inner {
start: deno_graph::Position::zeroed(),
end: deno_graph::Position::zeroed(),
},
+ NodeModuleKind::Esm,
deno_graph::source::ResolutionMode::Types,
) else {
return;
@@ -1019,7 +1036,7 @@ impl Inner {
// refresh the npm specifiers because it might have discovered
// a @types/node package and now's a good time to do that anyway
- self.refresh_npm_specifiers().await;
+ self.refresh_dep_info().await;
self.project_changed([], true);
}
@@ -1065,7 +1082,7 @@ impl Inner {
);
if document.is_diagnosable() {
self.project_changed([(document.specifier(), ChangeKind::Opened)], false);
- self.refresh_npm_specifiers().await;
+ self.refresh_dep_info().await;
self.diagnostics_server.invalidate(&[specifier]);
self.send_diagnostics_update();
self.send_testing_update();
@@ -1086,8 +1103,8 @@ impl Inner {
Ok(document) => {
if document.is_diagnosable() {
let old_scopes_with_node_specifier =
- self.documents.scopes_with_node_specifier().clone();
- self.refresh_npm_specifiers().await;
+ self.documents.scopes_with_node_specifier();
+ self.refresh_dep_info().await;
let mut config_changed = false;
if !self
.documents
@@ -1138,13 +1155,15 @@ impl Inner {
}));
}
- async fn refresh_npm_specifiers(&mut self) {
- let package_reqs = self.documents.npm_reqs_by_scope();
+ async fn refresh_dep_info(&mut self) {
+ let dep_info_by_scope = self.documents.dep_info_by_scope();
let resolver = self.resolver.clone();
// spawn due to the lsp's `Send` requirement
- spawn(async move { resolver.set_npm_reqs(&package_reqs).await })
- .await
- .ok();
+ spawn(
+ async move { resolver.set_dep_info_by_scope(&dep_info_by_scope).await },
+ )
+ .await
+ .ok();
}
async fn did_close(&mut self, params: DidCloseTextDocumentParams) {
@@ -1163,7 +1182,7 @@ impl Inner {
.uri_to_specifier(&params.text_document.uri, LspUrlKind::File);
self.diagnostics_state.clear(&specifier);
if self.is_diagnosable(&specifier) {
- self.refresh_npm_specifiers().await;
+ self.refresh_dep_info().await;
self.diagnostics_server.invalidate(&[specifier.clone()]);
self.send_diagnostics_update();
self.send_testing_update();
@@ -1377,16 +1396,14 @@ impl Inner {
.fmt_config_for_specifier(&specifier)
.options
.clone();
- fmt_options.use_tabs = Some(!params.options.insert_spaces);
- fmt_options.indent_width = Some(params.options.tab_size as u8);
- let maybe_workspace = self
- .config
- .tree
- .data_for_specifier(&specifier)
- .map(|d| &d.member_dir.workspace);
+ let config_data = self.config.tree.data_for_specifier(&specifier);
+ if !config_data.is_some_and(|d| d.maybe_deno_json().is_some()) {
+ fmt_options.use_tabs = Some(!params.options.insert_spaces);
+ fmt_options.indent_width = Some(params.options.tab_size as u8);
+ }
let unstable_options = UnstableFmtOptions {
- component: maybe_workspace
- .map(|w| w.has_unstable("fmt-component"))
+ component: config_data
+ .map(|d| d.unstable.contains("fmt-component"))
.unwrap_or(false),
};
let document = document.clone();
@@ -1618,6 +1635,10 @@ impl Inner {
let file_diagnostics = self
.diagnostics_server
.get_ts_diagnostics(&specifier, asset_or_doc.document_lsp_version());
+ let specifier_kind = asset_or_doc
+ .document()
+ .map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
+ .unwrap_or(NodeModuleKind::Esm);
let mut includes_no_cache = false;
for diagnostic in &fixable_diagnostics {
match diagnostic.source.as_deref() {
@@ -1656,7 +1677,13 @@ impl Inner {
.await;
for action in actions {
code_actions
- .add_ts_fix_action(&specifier, &action, diagnostic, self)
+ .add_ts_fix_action(
+ &specifier,
+ specifier_kind,
+ &action,
+ diagnostic,
+ self,
+ )
.map_err(|err| {
error!("Unable to convert fix: {:#}", err);
LspError::internal_error()
@@ -1802,10 +1829,9 @@ impl Inner {
error!("Unable to decode code action data: {:#}", err);
LspError::invalid_params("The CodeAction's data is invalid.")
})?;
- let scope = self
- .get_asset_or_document(&code_action_data.specifier)
- .ok()
- .and_then(|d| d.scope().cloned());
+ let maybe_asset_or_doc =
+ self.get_asset_or_document(&code_action_data.specifier).ok();
+ let scope = maybe_asset_or_doc.as_ref().and_then(|d| d.scope().cloned());
let combined_code_actions = self
.ts_server
.get_combined_code_fix(
@@ -1832,8 +1858,13 @@ impl Inner {
let changes = if code_action_data.fix_id == "fixMissingImport" {
fix_ts_import_changes(
&code_action_data.specifier,
+ maybe_asset_or_doc
+ .as_ref()
+ .and_then(|d| d.document())
+ .map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
+ .unwrap_or(NodeModuleKind::Esm),
&combined_code_actions.changes,
- &self.get_ts_response_import_mapper(&code_action_data.specifier),
+ self,
)
.map_err(|err| {
error!("Unable to remap changes: {:#}", err);
@@ -1885,8 +1916,12 @@ impl Inner {
if kind_suffix == ".rewrite.function.returnType" {
refactor_edit_info.edits = fix_ts_import_changes(
&action_data.specifier,
+ asset_or_doc
+ .document()
+ .map(|d| self.is_cjs_resolver.get_doc_module_kind(d))
+ .unwrap_or(NodeModuleKind::Esm),
&refactor_edit_info.edits,
- &self.get_ts_response_import_mapper(&action_data.specifier),
+ self,
)
.map_err(|err| {
error!("Unable to remap changes: {:#}", err);
@@ -1917,7 +1952,8 @@ impl Inner {
// todo(dsherret): this should probably just take the resolver itself
// as the import map is an implementation detail
.and_then(|d| d.resolver.maybe_import_map()),
- self.resolver.as_ref(),
+ &self.resolver,
+ &self.ts_server.specifier_map,
file_referrer,
)
}
@@ -2233,6 +2269,7 @@ impl Inner {
&self.jsr_search_api,
&self.npm_search_api,
&self.documents,
+ &self.is_cjs_resolver,
self.resolver.as_ref(),
self
.config
@@ -2280,7 +2317,11 @@ impl Inner {
.into(),
scope.cloned(),
)
- .await;
+ .await
+ .unwrap_or_else(|err| {
+ error!("Unable to get completion info from TypeScript: {:#}", err);
+ None
+ });
if let Some(completions) = maybe_completion_info {
response = Some(
@@ -3563,15 +3604,16 @@ impl Inner {
if byonm {
roots.retain(|s| s.scheme() != "npm");
- } else if let Some(npm_reqs) = self
+ } else if let Some(dep_info) = self
.documents
- .npm_reqs_by_scope()
+ .dep_info_by_scope()
.get(&config_data.map(|d| d.scope.as_ref().clone()))
{
// always include the npm packages since resolution of one npm package
// might affect the resolution of other npm packages
roots.extend(
- npm_reqs
+ dep_info
+ .npm_reqs
.iter()
.map(|req| ModuleSpecifier::parse(&format!("npm:{}", req)).unwrap()),
);
@@ -3592,9 +3634,8 @@ impl Inner {
deno_json_cache: None,
pkg_json_cache: None,
workspace_cache: None,
- config_parse_options: deno_config::deno_json::ConfigParseOptions {
- include_task_comments: false,
- },
+ config_parse_options:
+ deno_config::deno_json::ConfigParseOptions::default(),
additional_config_file_names: &[],
discover_pkg_json: !has_flag_env_var("DENO_NO_PACKAGE_JSON"),
maybe_vendor_override: if force_global_cache {
@@ -3649,7 +3690,7 @@ impl Inner {
async fn post_cache(&mut self) {
self.resolver.did_cache();
- self.refresh_npm_specifiers().await;
+ self.refresh_dep_info().await;
self.diagnostics_server.invalidate_all();
self.project_changed([], true);
self.ts_server.cleanup_semantic_cache(self.snapshot()).await;
@@ -3807,7 +3848,7 @@ impl Inner {
let maybe_inlay_hints = maybe_inlay_hints.map(|hints| {
hints
.iter()
- .map(|hint| hint.to_lsp(line_index.clone()))
+ .map(|hint| hint.to_lsp(line_index.clone(), self))
.collect()
});
self.performance.measure(mark);
@@ -3943,7 +3984,9 @@ mod tests {
fn test_walk_workspace() {
let temp_dir = TempDir::new();
temp_dir.create_dir_all("root1/vendor/");
+ temp_dir.create_dir_all("root1/coverage/");
temp_dir.write("root1/vendor/mod.ts", ""); // no, vendor
+ temp_dir.write("root1/coverage/mod.ts", ""); // no, coverage
temp_dir.create_dir_all("root1/node_modules/");
temp_dir.write("root1/node_modules/mod.ts", ""); // no, node_modules