summaryrefslogtreecommitdiff
path: root/cli/lsp/tsc.rs
diff options
context:
space:
mode:
authorNayeem Rahman <nayeemrmn99@gmail.com>2024-06-26 23:47:01 +0100
committerGitHub <noreply@github.com>2024-06-26 23:47:01 +0100
commit67dcd6db518446574d3a1e33f4ce536fcdc4fd25 (patch)
tree23d6aa1d867b1efded9c0e638c89513ca492a44f /cli/lsp/tsc.rs
parent2a2ff96be13047cb50612fde0f12e5f6df374ad3 (diff)
feat(lsp): ts language service scopes (#24345)
Diffstat (limited to 'cli/lsp/tsc.rs')
-rw-r--r--cli/lsp/tsc.rs715
1 files changed, 476 insertions, 239 deletions
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 5659decbf..3239ba700 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -3,6 +3,7 @@
use super::analysis::CodeActionData;
use super::code_lens;
use super::config;
+use super::config::LspTsConfig;
use super::documents::AssetOrDocument;
use super::documents::Document;
use super::documents::DocumentsFilter;
@@ -34,6 +35,8 @@ use crate::util::v8::convert;
use deno_core::convert::Smi;
use deno_core::convert::ToV8;
use deno_core::error::StdAnyError;
+use deno_core::futures::stream::FuturesOrdered;
+use deno_core::futures::StreamExt;
use deno_runtime::fs_util::specifier_to_file_path;
use dashmap::DashMap;
@@ -60,6 +63,8 @@ use deno_core::PollEventLoopOptions;
use deno_core::RuntimeOptions;
use deno_runtime::inspector_server::InspectorServer;
use deno_runtime::tokio_util::create_basic_runtime;
+use indexmap::IndexMap;
+use indexmap::IndexSet;
use lazy_regex::lazy_regex;
use log::error;
use once_cell::sync::Lazy;
@@ -67,9 +72,9 @@ use regex::Captures;
use regex::Regex;
use serde_repr::Deserialize_repr;
use serde_repr::Serialize_repr;
-use std::borrow::Cow;
use std::cell::RefCell;
use std::cmp;
+use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::HashSet;
use std::convert::Infallible;
@@ -108,6 +113,7 @@ const FILE_EXTENSION_KIND_MODIFIERS: &[&str] =
type Request = (
TscRequest,
+ Option<ModuleSpecifier>,
Arc<StateSnapshot>,
oneshot::Sender<Result<String, AnyError>>,
CancellationToken,
@@ -270,11 +276,12 @@ impl Serialize for ChangeKind {
}
}
-#[derive(Debug, PartialEq)]
+#[derive(Debug)]
+#[cfg_attr(test, derive(Serialize))]
pub struct PendingChange {
pub modified_scripts: Vec<(String, ChangeKind)>,
pub project_version: usize,
- pub config_changed: bool,
+ pub new_configs_by_scope: Option<BTreeMap<ModuleSpecifier, Arc<LspTsConfig>>>,
}
impl<'a> ToV8<'a> for PendingChange {
@@ -297,12 +304,24 @@ impl<'a> ToV8<'a> for PendingChange {
};
let project_version =
v8::Integer::new_from_unsigned(scope, self.project_version as u32).into();
- let config_changed = v8::Boolean::new(scope, self.config_changed).into();
+ let new_configs_by_scope =
+ if let Some(new_configs_by_scope) = self.new_configs_by_scope {
+ serde_v8::to_v8(
+ scope,
+ new_configs_by_scope.into_iter().collect::<Vec<_>>(),
+ )
+ .unwrap_or_else(|err| {
+ lsp_warn!("Couldn't serialize ts configs: {err}");
+ v8::null(scope).into()
+ })
+ } else {
+ v8::null(scope).into()
+ };
Ok(
v8::Array::new_with_elements(
scope,
- &[modified_scripts, project_version, config_changed],
+ &[modified_scripts, project_version, new_configs_by_scope],
)
.into(),
)
@@ -314,11 +333,13 @@ impl PendingChange {
&mut self,
new_version: usize,
modified_scripts: Vec<(String, ChangeKind)>,
- config_changed: bool,
+ new_configs_by_scope: Option<BTreeMap<ModuleSpecifier, Arc<LspTsConfig>>>,
) {
use ChangeKind::*;
self.project_version = self.project_version.max(new_version);
- self.config_changed |= config_changed;
+ if let Some(new_configs_by_scope) = new_configs_by_scope {
+ self.new_configs_by_scope = Some(new_configs_by_scope);
+ }
for (spec, new) in modified_scripts {
if let Some((_, current)) =
self.modified_scripts.iter_mut().find(|(s, _)| s == &spec)
@@ -408,7 +429,7 @@ impl TsServer {
&self,
snapshot: Arc<StateSnapshot>,
modified_scripts: impl IntoIterator<Item = (&'a ModuleSpecifier, ChangeKind)>,
- config_changed: bool,
+ new_configs_by_scope: Option<BTreeMap<ModuleSpecifier, Arc<LspTsConfig>>>,
) {
let modified_scripts = modified_scripts
.into_iter()
@@ -419,14 +440,14 @@ impl TsServer {
pending_change.coalesce(
snapshot.project_version,
modified_scripts,
- config_changed,
+ new_configs_by_scope,
);
}
pending => {
let pending_change = PendingChange {
modified_scripts,
project_version: snapshot.project_version,
- config_changed,
+ new_configs_by_scope,
};
*pending = Some(pending_change);
}
@@ -438,36 +459,69 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifiers: Vec<ModuleSpecifier>,
token: CancellationToken,
- ) -> Result<HashMap<String, Vec<crate::tsc::Diagnostic>>, AnyError> {
- let req = TscRequest::GetDiagnostics((
- specifiers
- .into_iter()
- .map(|s| self.specifier_map.denormalize(&s))
- .collect::<Vec<String>>(),
- snapshot.project_version,
- ));
- let raw_diagnostics = self.request_with_cancellation::<HashMap<String, Vec<crate::tsc::Diagnostic>>>(snapshot, req, token).await?;
- let mut diagnostics_map = HashMap::with_capacity(raw_diagnostics.len());
- for (mut specifier, mut diagnostics) in raw_diagnostics {
- specifier = self.specifier_map.normalize(&specifier)?.to_string();
- for diagnostic in &mut diagnostics {
- normalize_diagnostic(diagnostic, &self.specifier_map)?;
+ ) -> Result<IndexMap<String, Vec<crate::tsc::Diagnostic>>, AnyError> {
+ let mut diagnostics_map = IndexMap::with_capacity(specifiers.len());
+ let mut specifiers_by_scope = BTreeMap::new();
+ for specifier in specifiers {
+ let scope = if specifier.scheme() == "file" {
+ snapshot
+ .config
+ .tree
+ .scope_for_specifier(&specifier)
+ .cloned()
+ } else {
+ snapshot
+ .documents
+ .get(&specifier)
+ .and_then(|d| d.scope().cloned())
+ };
+ let specifiers = specifiers_by_scope.entry(scope).or_insert(vec![]);
+ specifiers.push(self.specifier_map.denormalize(&specifier));
+ }
+ let mut results = FuturesOrdered::new();
+ for (scope, specifiers) in specifiers_by_scope {
+ let req =
+ TscRequest::GetDiagnostics((specifiers, snapshot.project_version));
+ results.push_back(self.request_with_cancellation::<IndexMap<String, Vec<crate::tsc::Diagnostic>>>(snapshot.clone(), req, scope, token.clone()));
+ }
+ while let Some(raw_diagnostics) = results.next().await {
+ let raw_diagnostics = raw_diagnostics
+ .inspect_err(|err| {
+ if !token.is_cancelled() {
+ lsp_warn!("Error generating TypeScript diagnostics: {err}");
+ }
+ })
+ .unwrap_or_default();
+ for (mut specifier, mut diagnostics) in raw_diagnostics {
+ specifier = self.specifier_map.normalize(&specifier)?.to_string();
+ for diagnostic in &mut diagnostics {
+ normalize_diagnostic(diagnostic, &self.specifier_map)?;
+ }
+ diagnostics_map.insert(specifier, diagnostics);
}
- diagnostics_map.insert(specifier, diagnostics);
}
Ok(diagnostics_map)
}
pub async fn cleanup_semantic_cache(&self, snapshot: Arc<StateSnapshot>) {
- let req = TscRequest::CleanupSemanticCache;
- self
- .request::<()>(snapshot, req)
- .await
- .map_err(|err| {
- log::error!("Failed to request to tsserver {}", err);
- LspError::invalid_request()
- })
- .ok();
+ for scope in snapshot
+ .config
+ .tree
+ .data_by_scope()
+ .keys()
+ .map(Some)
+ .chain(std::iter::once(None))
+ {
+ let req = TscRequest::CleanupSemanticCache;
+ self
+ .request::<()>(snapshot.clone(), req, scope.cloned())
+ .await
+ .map_err(|err| {
+ log::error!("Failed to request to tsserver {}", err);
+ LspError::invalid_request()
+ })
+ .ok();
+ }
}
pub async fn find_references(
@@ -475,35 +529,60 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
position: u32,
- ) -> Result<Option<Vec<ReferencedSymbol>>, LspError> {
+ ) -> Result<Option<Vec<ReferencedSymbol>>, AnyError> {
let req = TscRequest::FindReferences((
self.specifier_map.denormalize(&specifier),
position,
));
- self
- .request::<Option<Vec<ReferencedSymbol>>>(snapshot, req)
- .await
- .and_then(|mut symbols| {
- for symbol in symbols.iter_mut().flatten() {
- symbol.normalize(&self.specifier_map)?;
- }
- Ok(symbols)
- })
- .map_err(|err| {
- log::error!("Unable to get references from TypeScript: {}", err);
- LspError::internal_error()
- })
+ let mut results = FuturesOrdered::new();
+ for scope in snapshot
+ .config
+ .tree
+ .data_by_scope()
+ .keys()
+ .map(Some)
+ .chain(std::iter::once(None))
+ {
+ results.push_back(self.request::<Option<Vec<ReferencedSymbol>>>(
+ snapshot.clone(),
+ req.clone(),
+ scope.cloned(),
+ ));
+ }
+ let mut all_symbols = IndexSet::new();
+ while let Some(symbols) = results.next().await {
+ let symbols = symbols
+ .inspect_err(|err| {
+ let err = err.to_string();
+ if !err.contains("Could not find source file") {
+ lsp_warn!("Unable to get references from TypeScript: {err}");
+ }
+ })
+ .unwrap_or_default();
+ let Some(mut symbols) = symbols else {
+ continue;
+ };
+ for symbol in &mut symbols {
+ symbol.normalize(&self.specifier_map)?;
+ }
+ all_symbols.extend(symbols);
+ }
+ if all_symbols.is_empty() {
+ return Ok(None);
+ }
+ Ok(Some(all_symbols.into_iter().collect()))
}
pub async fn get_navigation_tree(
&self,
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
+ scope: Option<ModuleSpecifier>,
) -> Result<NavigationTree, AnyError> {
let req = TscRequest::GetNavigationTree((self
.specifier_map
.denormalize(&specifier),));
- self.request(snapshot, req).await
+ self.request(snapshot, req, scope).await
}
pub async fn get_supported_code_fixes(
@@ -511,7 +590,7 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
) -> Result<Vec<String>, LspError> {
let req = TscRequest::GetSupportedCodeFixes;
- self.request(snapshot, req).await.map_err(|err| {
+ self.request(snapshot, req, None).await.map_err(|err| {
log::error!("Unable to get fixable diagnostics: {}", err);
LspError::internal_error()
})
@@ -522,17 +601,19 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
position: u32,
+ scope: Option<ModuleSpecifier>,
) -> Result<Option<QuickInfo>, LspError> {
let req = TscRequest::GetQuickInfoAtPosition((
self.specifier_map.denormalize(&specifier),
position,
));
- self.request(snapshot, req).await.map_err(|err| {
+ self.request(snapshot, req, scope).await.map_err(|err| {
log::error!("Unable to get quick info: {}", err);
LspError::internal_error()
})
}
+ #[allow(clippy::too_many_arguments)]
pub async fn get_code_fixes(
&self,
snapshot: Arc<StateSnapshot>,
@@ -541,6 +622,7 @@ impl TsServer {
codes: Vec<i32>,
format_code_settings: FormatCodeSettings,
preferences: UserPreferences,
+ scope: Option<ModuleSpecifier>,
) -> Vec<CodeFixAction> {
let req = TscRequest::GetCodeFixesAtPosition(Box::new((
self.specifier_map.denormalize(&specifier),
@@ -551,7 +633,7 @@ impl TsServer {
preferences,
)));
let result = self
- .request::<Vec<CodeFixAction>>(snapshot, req)
+ .request::<Vec<CodeFixAction>>(snapshot, req, scope)
.await
.and_then(|mut actions| {
for action in &mut actions {
@@ -572,6 +654,7 @@ impl TsServer {
}
}
+ #[allow(clippy::too_many_arguments)]
pub async fn get_applicable_refactors(
&self,
snapshot: Arc<StateSnapshot>,
@@ -580,6 +663,7 @@ impl TsServer {
preferences: Option<UserPreferences>,
trigger_kind: Option<lsp::CodeActionTriggerKind>,
only: String,
+ scope: Option<ModuleSpecifier>,
) -> Result<Vec<ApplicableRefactorInfo>, LspError> {
let trigger_kind = trigger_kind.map(|reason| match reason {
lsp::CodeActionTriggerKind::INVOKED => "invoked",
@@ -593,7 +677,7 @@ impl TsServer {
trigger_kind,
only,
)));
- self.request(snapshot, req).await.map_err(|err| {
+ self.request(snapshot, req, scope).await.map_err(|err| {
log::error!("Failed to request to tsserver {}", err);
LspError::invalid_request()
})
@@ -605,6 +689,7 @@ impl TsServer {
code_action_data: &CodeActionData,
format_code_settings: FormatCodeSettings,
preferences: UserPreferences,
+ scope: Option<ModuleSpecifier>,
) -> Result<CombinedCodeActions, LspError> {
let req = TscRequest::GetCombinedCodeFix(Box::new((
CombinedCodeFixScope {
@@ -616,7 +701,7 @@ impl TsServer {
preferences,
)));
self
- .request::<CombinedCodeActions>(snapshot, req)
+ .request::<CombinedCodeActions>(snapshot, req, scope)
.await
.and_then(|mut actions| {
actions.normalize(&self.specifier_map)?;
@@ -638,6 +723,7 @@ impl TsServer {
refactor_name: String,
action_name: String,
preferences: Option<UserPreferences>,
+ scope: Option<ModuleSpecifier>,
) -> Result<RefactorEditInfo, LspError> {
let req = TscRequest::GetEditsForRefactor(Box::new((
self.specifier_map.denormalize(&specifier),
@@ -648,7 +734,7 @@ impl TsServer {
preferences,
)));
self
- .request::<RefactorEditInfo>(snapshot, req)
+ .request::<RefactorEditInfo>(snapshot, req, scope)
.await
.and_then(|mut info| {
info.normalize(&self.specifier_map)?;
@@ -667,30 +753,47 @@ impl TsServer {
new_specifier: ModuleSpecifier,
format_code_settings: FormatCodeSettings,
user_preferences: UserPreferences,
- ) -> Result<Vec<FileTextChanges>, LspError> {
+ ) -> Result<Vec<FileTextChanges>, AnyError> {
let req = TscRequest::GetEditsForFileRename(Box::new((
self.specifier_map.denormalize(&old_specifier),
self.specifier_map.denormalize(&new_specifier),
format_code_settings,
user_preferences,
)));
- self
- .request::<Vec<FileTextChanges>>(snapshot, req)
- .await
- .and_then(|mut changes| {
- for changes in &mut changes {
- changes.normalize(&self.specifier_map)?;
- for text_changes in &mut changes.text_changes {
- text_changes.new_text =
- to_percent_decoded_str(&text_changes.new_text);
- }
+ let mut results = FuturesOrdered::new();
+ for scope in snapshot
+ .config
+ .tree
+ .data_by_scope()
+ .keys()
+ .map(Some)
+ .chain(std::iter::once(None))
+ {
+ results.push_back(self.request::<Vec<FileTextChanges>>(
+ snapshot.clone(),
+ req.clone(),
+ scope.cloned(),
+ ));
+ }
+ let mut all_changes = IndexSet::new();
+ while let Some(changes) = results.next().await {
+ let mut changes = changes
+ .inspect_err(|err| {
+ lsp_warn!(
+ "Unable to get edits for file rename from TypeScript: {err}"
+ );
+ })
+ .unwrap_or_default();
+ for changes in &mut changes {
+ changes.normalize(&self.specifier_map)?;
+ for text_changes in &mut changes.text_changes {
+ text_changes.new_text =
+ to_percent_decoded_str(&text_changes.new_text);
}
- Ok(changes)
- })
- .map_err(|err| {
- log::error!("Failed to request to tsserver {}", err);
- LspError::invalid_request()
- })
+ }
+ all_changes.extend(changes);
+ }
+ Ok(all_changes.into_iter().collect())
}
pub async fn get_document_highlights(
@@ -699,6 +802,7 @@ impl TsServer {
specifier: ModuleSpecifier,
position: u32,
files_to_search: Vec<ModuleSpecifier>,
+ scope: Option<ModuleSpecifier>,
) -> Result<Option<Vec<DocumentHighlights>>, LspError> {
let req = TscRequest::GetDocumentHighlights(Box::new((
self.specifier_map.denormalize(&specifier),
@@ -708,7 +812,7 @@ impl TsServer {
.map(|s| self.specifier_map.denormalize(&s))
.collect::<Vec<_>>(),
)));
- self.request(snapshot, req).await.map_err(|err| {
+ self.request(snapshot, req, scope).await.map_err(|err| {
log::error!("Unable to get document highlights from TypeScript: {}", err);
LspError::internal_error()
})
@@ -719,13 +823,14 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
position: u32,
+ scope: Option<ModuleSpecifier>,
) -> Result<Option<DefinitionInfoAndBoundSpan>, LspError> {
let req = TscRequest::GetDefinitionAndBoundSpan((
self.specifier_map.denormalize(&specifier),
position,
));
self
- .request::<Option<DefinitionInfoAndBoundSpan>>(snapshot, req)
+ .request::<Option<DefinitionInfoAndBoundSpan>>(snapshot, req, scope)
.await
.and_then(|mut info| {
if let Some(info) = &mut info {
@@ -744,13 +849,14 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
position: u32,
+ scope: Option<ModuleSpecifier>,
) -> Result<Option<Vec<DefinitionInfo>>, LspError> {
let req = TscRequest::GetTypeDefinitionAtPosition((
self.specifier_map.denormalize(&specifier),
position,
));
self
- .request::<Option<Vec<DefinitionInfo>>>(snapshot, req)
+ .request::<Option<Vec<DefinitionInfo>>>(snapshot, req, scope)
.await
.and_then(|mut infos| {
for info in infos.iter_mut().flatten() {
@@ -771,6 +877,7 @@ impl TsServer {
position: u32,
options: GetCompletionsAtPositionOptions,
format_code_settings: FormatCodeSettings,
+ scope: Option<ModuleSpecifier>,
) -> Option<CompletionInfo> {
let req = TscRequest::GetCompletionsAtPosition(Box::new((
self.specifier_map.denormalize(&specifier),
@@ -778,7 +885,7 @@ impl TsServer {
options,
format_code_settings,
)));
- match self.request(snapshot, req).await {
+ match self.request(snapshot, req, scope).await {
Ok(maybe_info) => maybe_info,
Err(err) => {
log::error!("Unable to get completion info from TypeScript: {:#}", err);
@@ -791,6 +898,7 @@ impl TsServer {
&self,
snapshot: Arc<StateSnapshot>,
args: GetCompletionDetailsArgs,
+ scope: Option<ModuleSpecifier>,
) -> Result<Option<CompletionEntryDetails>, AnyError> {
let req = TscRequest::GetCompletionEntryDetails(Box::new((
self.specifier_map.denormalize(&args.specifier),
@@ -802,7 +910,7 @@ impl TsServer {
args.data,
)));
self
- .request::<Option<CompletionEntryDetails>>(snapshot, req)
+ .request::<Option<CompletionEntryDetails>>(snapshot, req, scope)
.await
.and_then(|mut details| {
if let Some(details) = &mut details {
@@ -817,35 +925,60 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
position: u32,
- ) -> Result<Option<Vec<ImplementationLocation>>, LspError> {
+ ) -> Result<Option<Vec<ImplementationLocation>>, AnyError> {
let req = TscRequest::GetImplementationAtPosition((
self.specifier_map.denormalize(&specifier),
position,
));
- self
- .request::<Option<Vec<ImplementationLocation>>>(snapshot, req)
- .await
- .and_then(|mut locations| {
- for location in locations.iter_mut().flatten() {
- location.normalize(&self.specifier_map)?;
- }
- Ok(locations)
- })
- .map_err(|err| {
- log::error!("Failed to request to tsserver {}", err);
- LspError::invalid_request()
- })
+ let mut results = FuturesOrdered::new();
+ for scope in snapshot
+ .config
+ .tree
+ .data_by_scope()
+ .keys()
+ .map(Some)
+ .chain(std::iter::once(None))
+ {
+ results.push_back(self.request::<Option<Vec<ImplementationLocation>>>(
+ snapshot.clone(),
+ req.clone(),
+ scope.cloned(),
+ ));
+ }
+ let mut all_locations = IndexSet::new();
+ while let Some(locations) = results.next().await {
+ let locations = locations
+ .inspect_err(|err| {
+ let err = err.to_string();
+ if !err.contains("Could not find source file") {
+ lsp_warn!("Unable to get implementations from TypeScript: {err}");
+ }
+ })
+ .unwrap_or_default();
+ let Some(mut locations) = locations else {
+ continue;
+ };
+ for location in &mut locations {
+ location.normalize(&self.specifier_map)?;
+ }
+ all_locations.extend(locations);
+ }
+ if all_locations.is_empty() {
+ return Ok(None);
+ }
+ Ok(Some(all_locations.into_iter().collect()))
}
pub async fn get_outlining_spans(
&self,
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
+ scope: Option<ModuleSpecifier>,
) -> Result<Vec<OutliningSpan>, LspError> {
let req = TscRequest::GetOutliningSpans((self
.specifier_map
.denormalize(&specifier),));
- self.request(snapshot, req).await.map_err(|err| {
+ self.request(snapshot, req, scope).await.map_err(|err| {
log::error!("Failed to request to tsserver {}", err);
LspError::invalid_request()
})
@@ -856,24 +989,42 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
position: u32,
- ) -> Result<Vec<CallHierarchyIncomingCall>, LspError> {
+ ) -> Result<Vec<CallHierarchyIncomingCall>, AnyError> {
let req = TscRequest::ProvideCallHierarchyIncomingCalls((
self.specifier_map.denormalize(&specifier),
position,
));
- self
- .request::<Vec<CallHierarchyIncomingCall>>(snapshot, req)
- .await
- .and_then(|mut calls| {
- for call in &mut calls {
- call.normalize(&self.specifier_map)?;
- }
- Ok(calls)
- })
- .map_err(|err| {
- log::error!("Failed to request to tsserver {}", err);
- LspError::invalid_request()
- })
+ let mut results = FuturesOrdered::new();
+ for scope in snapshot
+ .config
+ .tree
+ .data_by_scope()
+ .keys()
+ .map(Some)
+ .chain(std::iter::once(None))
+ {
+ results.push_back(self.request::<Vec<CallHierarchyIncomingCall>>(
+ snapshot.clone(),
+ req.clone(),
+ scope.cloned(),
+ ));
+ }
+ let mut all_calls = IndexSet::new();
+ while let Some(calls) = results.next().await {
+ let mut calls = calls
+ .inspect_err(|err| {
+ let err = err.to_string();
+ if !err.contains("Could not find source file") {
+ lsp_warn!("Unable to get incoming calls from TypeScript: {err}");
+ }
+ })
+ .unwrap_or_default();
+ for call in &mut calls {
+ call.normalize(&self.specifier_map)?;
+ }
+ all_calls.extend(calls)
+ }
+ Ok(all_calls.into_iter().collect())
}
pub async fn provide_call_hierarchy_outgoing_calls(
@@ -881,13 +1032,14 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
position: u32,
+ scope: Option<ModuleSpecifier>,
) -> Result<Vec<CallHierarchyOutgoingCall>, LspError> {
let req = TscRequest::ProvideCallHierarchyOutgoingCalls((
self.specifier_map.denormalize(&specifier),
position,
));
self
- .request::<Vec<CallHierarchyOutgoingCall>>(snapshot, req)
+ .request::<Vec<CallHierarchyOutgoingCall>>(snapshot, req, scope)
.await
.and_then(|mut calls| {
for call in &mut calls {
@@ -906,13 +1058,14 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
position: u32,
+ scope: Option<ModuleSpecifier>,
) -> Result<Option<OneOrMany<CallHierarchyItem>>, LspError> {
let req = TscRequest::PrepareCallHierarchy((
self.specifier_map.denormalize(&specifier),
position,
));
self
- .request::<Option<OneOrMany<CallHierarchyItem>>>(snapshot, req)
+ .request::<Option<OneOrMany<CallHierarchyItem>>>(snapshot, req, scope)
.await
.and_then(|mut items| {
match &mut items {
@@ -939,7 +1092,7 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
position: u32,
- ) -> Result<Option<Vec<RenameLocation>>, LspError> {
+ ) -> Result<Option<Vec<RenameLocation>>, AnyError> {
let req = TscRequest::FindRenameLocations((
self.specifier_map.denormalize(&specifier),
position,
@@ -947,19 +1100,43 @@ impl TsServer {
false,
false,
));
- self
- .request::<Option<Vec<RenameLocation>>>(snapshot, req)
- .await
- .and_then(|mut locations| {
- for location in locations.iter_mut().flatten() {
- location.normalize(&self.specifier_map)?;
- }
- Ok(locations)
- })
- .map_err(|err| {
- log::error!("Failed to request to tsserver {}", err);
- LspError::invalid_request()
- })
+ let mut results = FuturesOrdered::new();
+ for scope in snapshot
+ .config
+ .tree
+ .data_by_scope()
+ .keys()
+ .map(Some)
+ .chain(std::iter::once(None))
+ {
+ results.push_back(self.request::<Option<Vec<RenameLocation>>>(
+ snapshot.clone(),
+ req.clone(),
+ scope.cloned(),
+ ));
+ }
+ let mut all_locations = IndexSet::new();
+ while let Some(locations) = results.next().await {
+ let locations = locations
+ .inspect_err(|err| {
+ let err = err.to_string();
+ if !err.contains("Could not find source file") {
+ lsp_warn!("Unable to get rename locations from TypeScript: {err}");
+ }
+ })
+ .unwrap_or_default();
+ let Some(mut locations) = locations else {
+ continue;
+ };
+ for symbol in &mut locations {
+ symbol.normalize(&self.specifier_map)?;
+ }
+ all_locations.extend(locations);
+ }
+ if all_locations.is_empty() {
+ return Ok(None);
+ }
+ Ok(Some(all_locations.into_iter().collect()))
}
pub async fn get_smart_selection_range(
@@ -967,12 +1144,13 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
position: u32,
+ scope: Option<ModuleSpecifier>,
) -> Result<SelectionRange, LspError> {
let req = TscRequest::GetSmartSelectionRange((
self.specifier_map.denormalize(&specifier),
position,
));
- self.request(snapshot, req).await.map_err(|err| {
+ self.request(snapshot, req, scope).await.map_err(|err| {
log::error!("Failed to request to tsserver {}", err);
LspError::invalid_request()
})
@@ -983,6 +1161,7 @@ impl TsServer {
snapshot: Arc<StateSnapshot>,
specifier: ModuleSpecifier,
range: Range<u32>,
+ scope: Option<ModuleSpecifier>,
) -> Result<Classifications, LspError> {
let req = TscRequest::GetEncodedSemanticClassifications((
self.specifier_map.denormalize(&specifier),
@@ -992,7 +1171,7 @@ impl TsServer {
},
"2020",
));
- self.request(snapshot, req).await.map_err(|err| {
+ self.request(snapshot, req, scope).await.map_err(|err| {
log::error!("Failed to request to tsserver {}", err);
LspError::invalid_request()
})
@@ -1004,13 +1183,14 @@ impl TsServer {
specifier: ModuleSpecifier,
position: u32,
options: SignatureHelpItemsOptions,
+ scope: Option<ModuleSpecifier>,
) -> Result<Option<SignatureHelpItems>, LspError> {
let req = TscRequest::GetSignatureHelpItems((
self.specifier_map.denormalize(&specifier),
position,
options,
));
- self.request(snapshot, req).await.map_err(|err| {
+ self.request(snapshot, req, scope).await.map_err(|err| {
log::error!("Failed to request to tsserver: {}", err);
LspError::invalid_request()
})
@@ -1020,7 +1200,7 @@ impl TsServer {
&self,
snapshot: Arc<StateSnapshot>,
args: GetNavigateToItemsArgs,
- ) -> Result<Vec<NavigateToItem>, LspError> {
+ ) -> Result<Vec<NavigateToItem>, AnyError> {
let req = TscRequest::GetNavigateToItems((
args.search,
args.max_result_count,
@@ -1029,19 +1209,34 @@ impl TsServer {
Err(_) => f,
}),
));
- self
- .request::<Vec<NavigateToItem>>(snapshot, req)
- .await
- .and_then(|mut items| {
- for items in &mut items {
- items.normalize(&self.specifier_map)?;
- }
- Ok(items)
- })
- .map_err(|err| {
- log::error!("Failed request to tsserver: {}", err);
- LspError::invalid_request()
- })
+ let mut results = FuturesOrdered::new();
+ for scope in snapshot
+ .config
+ .tree
+ .data_by_scope()
+ .keys()
+ .map(Some)
+ .chain(std::iter::once(None))
+ {
+ results.push_back(self.request::<Vec<NavigateToItem>>(
+ snapshot.clone(),
+ req.clone(),
+ scope.cloned(),
+ ));
+ }
+ let mut all_items = IndexSet::new();
+ while let Some(items) = results.next().await {
+ let mut items = items
+ .inspect_err(|err| {
+ lsp_warn!("Unable to get 'navigate to' items from TypeScript: {err}");
+ })
+ .unwrap_or_default();
+ for item in &mut items {
+ item.normalize(&self.specifier_map)?;
+ }
+ all_items.extend(items)
+ }
+ Ok(all_items.into_iter().collect())
}
pub async fn provide_inlay_hints(
@@ -1050,13 +1245,14 @@ impl TsServer {
specifier: ModuleSpecifier,
text_span: TextSpan,
user_preferences: UserPreferences,
+ scope: Option<ModuleSpecifier>,
) -> Result<Option<Vec<InlayHint>>, LspError> {
let req = TscRequest::ProvideInlayHints((
self.specifier_map.denormalize(&specifier),
text_span,
user_preferences,
));
- self.request(snapshot, req).await.map_err(|err| {
+ self.request(snapshot, req, scope).await.map_err(|err| {
log::error!("Unable to get inlay hints: {}", err);
LspError::internal_error()
})
@@ -1066,6 +1262,7 @@ impl TsServer {
&self,
snapshot: Arc<StateSnapshot>,
req: TscRequest,
+ scope: Option<ModuleSpecifier>,
) -> Result<R, AnyError>
where
R: de::DeserializeOwned,
@@ -1074,7 +1271,7 @@ impl TsServer {
.performance
.mark(format!("tsc.request.{}", req.method()));
let r = self
- .request_with_cancellation(snapshot, req, Default::default())
+ .request_with_cancellation(snapshot, req, scope, Default::default())
.await;
self.performance.measure(mark);
r
@@ -1084,6 +1281,7 @@ impl TsServer {
&self,
snapshot: Arc<StateSnapshot>,
req: TscRequest,
+ scope: Option<ModuleSpecifier>,
token: CancellationToken,
) -> Result<R, AnyError>
where
@@ -1106,7 +1304,7 @@ impl TsServer {
if self
.sender
- .send((req, snapshot, tx, token.clone(), change))
+ .send((req, scope, snapshot, tx, token.clone(), change))
.is_err()
{
return Err(anyhow!("failed to send request to tsc thread"));
@@ -1261,7 +1459,7 @@ async fn get_isolate_assets(
state_snapshot: Arc<StateSnapshot>,
) -> Vec<AssetDocument> {
let req = TscRequest::GetAssets;
- let res: Value = ts_server.request(state_snapshot, req).await.unwrap();
+ let res: Value = ts_server.request(state_snapshot, req, None).await.unwrap();
let response_assets = match res {
Value::Array(value) => value,
_ => unreachable!(),
@@ -1401,7 +1599,7 @@ pub enum OneOrMany<T> {
}
/// Aligns with ts.ScriptElementKind
-#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
pub enum ScriptElementKind {
#[serde(rename = "")]
Unknown,
@@ -1591,7 +1789,7 @@ impl From<ScriptElementKind> for lsp::SymbolKind {
}
}
-#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct TextSpan {
pub start: u32,
@@ -1812,7 +2010,7 @@ impl QuickInfo {
}
}
-#[derive(Debug, Clone, Deserialize, Serialize)]
+#[derive(Debug, Eq, PartialEq, Hash, Clone, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DocumentSpan {
text_span: TextSpan,
@@ -1920,7 +2118,7 @@ pub enum MatchKind {
CamelCase,
}
-#[derive(Debug, Clone, Deserialize)]
+#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NavigateToItem {
name: String,
@@ -2186,7 +2384,7 @@ impl NavigationTree {
}
}
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Eq, PartialEq, Hash, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ImplementationLocation {
#[serde(flatten)]
@@ -2234,7 +2432,7 @@ impl ImplementationLocation {
}
}
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Eq, PartialEq, Hash, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RenameLocation {
#[serde(flatten)]
@@ -2264,10 +2462,7 @@ impl RenameLocations {
new_name: &str,
language_server: &language_server::Inner,
) -> Result<lsp::WorkspaceEdit, AnyError> {
- let mut text_document_edit_map: HashMap<
- LspClientUrl,
- lsp::TextDocumentEdit,
- > = HashMap::new();
+ let mut text_document_edit_map = IndexMap::new();
let mut includes_non_files = false;
for location in self.locations.iter() {
let specifier = resolve_url(&location.document_span.file_name)?;
@@ -2344,7 +2539,7 @@ pub struct HighlightSpan {
kind: HighlightSpanKind,
}
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DefinitionInfo {
// kind: ScriptElementKind,
@@ -2433,7 +2628,7 @@ impl DocumentHighlights {
}
}
-#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct TextChange {
pub span: TextSpan,
@@ -2459,7 +2654,7 @@ impl TextChange {
}
}
-#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash)]
#[serde(rename_all = "camelCase")]
pub struct FileTextChanges {
pub file_name: String,
@@ -2846,7 +3041,7 @@ impl CombinedCodeActions {
}
}
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ReferencedSymbol {
pub definition: ReferencedSymbolDefinitionInfo,
@@ -2866,7 +3061,7 @@ impl ReferencedSymbol {
}
}
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ReferencedSymbolDefinitionInfo {
#[serde(flatten)]
@@ -2883,7 +3078,7 @@ impl ReferencedSymbolDefinitionInfo {
}
}
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ReferencedSymbolEntry {
#[serde(default)]
@@ -2902,7 +3097,7 @@ impl ReferencedSymbolEntry {
}
}
-#[derive(Debug, Deserialize, Serialize)]
+#[derive(Debug, Eq, PartialEq, Hash, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ReferenceEntry {
// is_write_access: bool,
@@ -2941,7 +3136,7 @@ impl ReferenceEntry {
}
}
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Eq, PartialEq, Hash, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CallHierarchyItem {
name: String,
@@ -3058,7 +3253,7 @@ impl CallHierarchyItem {
}
}
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Eq, PartialEq, Hash, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CallHierarchyIncomingCall {
from: CallHierarchyItem,
@@ -3098,7 +3293,7 @@ impl CallHierarchyIncomingCall {
}
}
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Eq, PartialEq, Hash, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CallHierarchyOutgoingCall {
to: CallHierarchyItem,
@@ -4166,6 +4361,7 @@ fn op_resolve(
struct TscRequestArray {
request: TscRequest,
+ scope: Option<String>,
id: Smi<usize>,
change: convert::OptionNull<PendingChange>,
}
@@ -4185,12 +4381,18 @@ impl<'a> ToV8<'a> for TscRequestArray {
.v8_string(scope)
.into();
let args = args.unwrap_or_else(|| v8::Array::new(scope, 0).into());
+ let scope_url = serde_v8::to_v8(scope, self.scope)
+ .map_err(AnyError::from)
+ .map_err(StdAnyError::from)?;
let change = self.change.to_v8(scope).unwrap_infallible();
Ok(
- v8::Array::new_with_elements(scope, &[id, method_name, args, change])
- .into(),
+ v8::Array::new_with_elements(
+ scope,
+ &[id, method_name, args, scope_url, change],
+ )
+ .into(),
)
}
}
@@ -4206,7 +4408,7 @@ async fn op_poll_requests(
state.pending_requests.take().unwrap()
};
- let Some((request, snapshot, response_tx, token, change)) =
+ let Some((request, scope, snapshot, response_tx, token, change)) =
pending_requests.recv().await
else {
return None.into();
@@ -4227,6 +4429,7 @@ async fn op_poll_requests(
Some(TscRequestArray {
request,
+ scope: scope.map(|s| s.into()),
id: Smi(id),
change: change.into(),
})
@@ -4283,36 +4486,58 @@ fn op_respond(
}
}
+#[derive(Debug, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct ScriptNames {
+ unscoped: IndexSet<String>,
+ by_scope: BTreeMap<ModuleSpecifier, IndexSet<String>>,
+}
+
#[op2]
#[serde]
-fn op_script_names(state: &mut OpState) -> Vec<String> {
+fn op_script_names(state: &mut OpState) -> ScriptNames {
let state = state.borrow_mut::<State>();
let mark = state.performance.mark("tsc.op.op_script_names");
- let mut seen = HashSet::new();
- let mut result = Vec::new();
+ let mut result = ScriptNames {
+ unscoped: IndexSet::new(),
+ by_scope: BTreeMap::from_iter(
+ state
+ .state_snapshot
+ .config
+ .tree
+ .data_by_scope()
+ .keys()
+ .map(|s| (s.clone(), IndexSet::new())),
+ ),
+ };
- if state
- .state_snapshot
- .documents
- .has_injected_types_node_package()
- {
- // ensure this is first so it resolves the node types first
- let specifier = "asset:///node_types.d.ts";
- result.push(specifier.to_string());
- seen.insert(Cow::Borrowed(specifier));
+ let scopes_with_node_specifier =
+ state.state_snapshot.documents.scopes_with_node_specifier();
+ if scopes_with_node_specifier.contains(&None) {
+ result
+ .unscoped
+ .insert("asset:///node_types.d.ts".to_string());
+ }
+ for (scope, script_names) in &mut result.by_scope {
+ if scopes_with_node_specifier.contains(&Some(scope.clone())) {
+ script_names.insert("asset:///node_types.d.ts".to_string());
+ }
}
// inject these next because they're global
- for (referrer, specifiers) in
- state.state_snapshot.resolver.graph_imports_by_referrer()
- {
- for specifier in specifiers {
- if seen.insert(Cow::Borrowed(specifier.as_str())) {
- result.push(specifier.to_string());
+ for (scope, script_names) in &mut result.by_scope {
+ for (referrer, specifiers) in state
+ .state_snapshot
+ .resolver
+ .graph_imports_by_referrer(scope)
+ {
+ for specifier in specifiers {
+ script_names.insert(specifier.to_string());
+ state
+ .root_referrers
+ .entry(specifier.clone())
+ .or_insert(referrer.clone());
}
- state
- .root_referrers
- .insert(specifier.clone(), referrer.clone());
}
}
@@ -4324,9 +4549,11 @@ fn op_script_names(state: &mut OpState) -> Vec<String> {
for doc in &docs {
let specifier = doc.specifier();
let is_open = doc.is_open();
- if seen.insert(Cow::Borrowed(specifier.as_str()))
- && (is_open || specifier.scheme() == "file")
- {
+ if is_open || specifier.scheme() == "file" {
+ let script_names = doc
+ .scope()
+ .and_then(|s| result.by_scope.get_mut(s))
+ .unwrap_or(&mut result.unscoped);
let types_specifier = (|| {
let documents = &state.state_snapshot.documents;
let types = doc.maybe_types_dependency().maybe_specifier()?;
@@ -4341,25 +4568,29 @@ fn op_script_names(state: &mut OpState) -> Vec<String> {
// If there is a types dep, use that as the root instead. But if the doc
// is open, include both as roots.
if let Some(types_specifier) = &types_specifier {
- if seen.insert(Cow::Owned(types_specifier.to_string())) {
- result.push(types_specifier.to_string());
- }
+ script_names.insert(types_specifier.to_string());
}
if types_specifier.is_none() || is_open {
- result.push(specifier.to_string());
+ script_names.insert(specifier.to_string());
}
}
}
- let r = result
- .into_iter()
- .map(|s| match ModuleSpecifier::parse(&s) {
- Ok(s) => state.specifier_map.denormalize(&s),
- Err(_) => s,
- })
- .collect();
+ for script_names in result
+ .by_scope
+ .values_mut()
+ .chain(std::iter::once(&mut result.unscoped))
+ {
+ *script_names = std::mem::take(script_names)
+ .into_iter()
+ .map(|s| match ModuleSpecifier::parse(&s) {
+ Ok(s) => state.specifier_map.denormalize(&s),
+ Err(_) => s,
+ })
+ .collect();
+ }
state.performance.measure(mark);
- r
+ result
}
#[op2]
@@ -4376,16 +4607,6 @@ fn op_script_version(
Ok(r)
}
-#[op2]
-#[serde]
-fn op_ts_config(state: &mut OpState) -> serde_json::Value {
- let state = state.borrow_mut::<State>();
- let mark = state.performance.mark("tsc.op.op_ts_config");
- let r = json!(state.state_snapshot.config.tree.root_ts_config());
- state.performance.measure(mark);
- r
-}
-
#[op2(fast)]
#[number]
fn op_project_version(state: &mut OpState) -> usize {
@@ -4522,7 +4743,6 @@ deno_core::extension!(deno_tsc,
op_respond,
op_script_names,
op_script_version,
- op_ts_config,
op_project_version,
op_poll_requests,
],
@@ -4541,7 +4761,7 @@ deno_core::extension!(deno_tsc,
},
);
-#[derive(Debug, Deserialize_repr, Serialize_repr)]
+#[derive(Debug, Clone, Deserialize_repr, Serialize_repr)]
#[repr(u32)]
pub enum CompletionTriggerKind {
Invoked = 1,
@@ -4566,7 +4786,7 @@ pub type QuotePreference = config::QuoteStyle;
pub type ImportModuleSpecifierPreference = config::ImportModuleSpecifier;
-#[derive(Debug, Serialize)]
+#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "kebab-case")]
#[allow(dead_code)]
pub enum ImportModuleSpecifierEnding {
@@ -4576,7 +4796,7 @@ pub enum ImportModuleSpecifierEnding {
Js,
}
-#[derive(Debug, Serialize)]
+#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "kebab-case")]
#[allow(dead_code)]
pub enum IncludeInlayParameterNameHints {
@@ -4597,7 +4817,7 @@ impl From<&config::InlayHintsParamNamesEnabled>
}
}
-#[derive(Debug, Serialize)]
+#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "kebab-case")]
#[allow(dead_code)]
pub enum IncludePackageJsonAutoImports {
@@ -4608,7 +4828,7 @@ pub enum IncludePackageJsonAutoImports {
pub type JsxAttributeCompletionStyle = config::JsxAttributeCompletionStyle;
-#[derive(Debug, Default, Serialize)]
+#[derive(Debug, Default, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GetCompletionsAtPositionOptions {
#[serde(flatten)]
@@ -4619,7 +4839,7 @@ pub struct GetCompletionsAtPositionOptions {
pub trigger_kind: Option<CompletionTriggerKind>,
}
-#[derive(Debug, Default, Serialize)]
+#[derive(Debug, Default, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UserPreferences {
#[serde(skip_serializing_if = "Option::is_none")]
@@ -4807,14 +5027,14 @@ impl UserPreferences {
}
}
-#[derive(Debug, Serialize)]
+#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignatureHelpItemsOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_reason: Option<SignatureHelpTriggerReason>,
}
-#[derive(Debug, Serialize)]
+#[derive(Debug, Clone, Serialize)]
pub enum SignatureHelpTriggerKind {
#[serde(rename = "characterTyped")]
CharacterTyped,
@@ -4837,7 +5057,7 @@ impl From<lsp::SignatureHelpTriggerKind> for SignatureHelpTriggerKind {
}
}
-#[derive(Debug, Serialize)]
+#[derive(Debug, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SignatureHelpTriggerReason {
pub kind: SignatureHelpTriggerKind,
@@ -4882,7 +5102,7 @@ pub struct GetNavigateToItemsArgs {
pub file: Option<String>,
}
-#[derive(Serialize, Clone, Copy)]
+#[derive(Debug, Serialize, Clone, Copy)]
pub struct TscTextRange {
pos: u32,
end: u32,
@@ -4897,7 +5117,7 @@ impl From<Range<u32>> for TscTextRange {
}
}
-#[derive(Serialize, Clone)]
+#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "camelCase")]
pub struct CombinedCodeFixScope {
r#type: &'static str,
@@ -4907,7 +5127,7 @@ pub struct CombinedCodeFixScope {
#[derive(Serialize, Clone, Copy)]
pub struct JsNull;
-#[derive(Serialize)]
+#[derive(Debug, Clone, Serialize)]
pub enum TscRequest {
GetDiagnostics((Vec<String>, usize)),
GetAssets,
@@ -5226,6 +5446,19 @@ mod tests {
let performance = Arc::new(Performance::default());
let ts_server = TsServer::new(performance);
ts_server.start(None).unwrap();
+ ts_server.project_changed(
+ snapshot.clone(),
+ [],
+ Some(
+ snapshot
+ .config
+ .tree
+ .data_by_scope()
+ .iter()
+ .map(|(s, d)| (s.clone(), d.ts_config.clone()))
+ .collect(),
+ ),
+ );
(ts_server, snapshot, cache)
}
@@ -5654,7 +5887,7 @@ mod tests {
ts_server.project_changed(
snapshot.clone(),
[(&specifier_dep, ChangeKind::Opened)],
- false,
+ None,
);
let specifier = resolve_url("file:///a.ts").unwrap();
let diagnostics = ts_server
@@ -5739,6 +5972,7 @@ mod tests {
trigger_kind: None,
},
Default::default(),
+ Some(ModuleSpecifier::parse("file:///").unwrap()),
)
.await
.unwrap();
@@ -5755,6 +5989,7 @@ mod tests {
preferences: None,
data: None,
},
+ Some(ModuleSpecifier::parse("file:///").unwrap()),
)
.await
.unwrap()
@@ -5897,6 +6132,7 @@ mod tests {
..Default::default()
},
FormatCodeSettings::from(&fmt_options_config),
+ Some(ModuleSpecifier::parse("file:///").unwrap()),
)
.await
.unwrap();
@@ -5922,6 +6158,7 @@ mod tests {
}),
data: entry.data.clone(),
},
+ Some(ModuleSpecifier::parse("file:///").unwrap()),
)
.await
.unwrap()
@@ -6080,7 +6317,7 @@ mod tests {
fn change<S: AsRef<str>>(
project_version: usize,
scripts: impl IntoIterator<Item = (S, ChangeKind)>,
- config_changed: bool,
+ new_configs_by_scope: Option<BTreeMap<ModuleSpecifier, Arc<LspTsConfig>>>,
) -> PendingChange {
PendingChange {
project_version,
@@ -6088,20 +6325,20 @@ mod tests {
.into_iter()
.map(|(s, c)| (s.as_ref().into(), c))
.collect(),
- config_changed,
+ new_configs_by_scope,
}
}
let cases = [
(
// start
- change(1, [("file:///a.ts", Closed)], false),
+ change(1, [("file:///a.ts", Closed)], None),
// new
- change(2, Some(("file:///b.ts", Opened)), false),
+ change(2, Some(("file:///b.ts", Opened)), None),
// expected
change(
2,
[("file:///a.ts", Closed), ("file:///b.ts", Opened)],
- false,
+ None,
),
),
(
@@ -6109,48 +6346,48 @@ mod tests {
change(
1,
[("file:///a.ts", Closed), ("file:///b.ts", Opened)],
- false,
+ None,
),
// new
change(
2,
// a gets closed then reopened, b gets opened then closed
[("file:///a.ts", Opened), ("file:///b.ts", Closed)],
- false,
+ None,
),
// expected
change(
2,
[("file:///a.ts", Opened), ("file:///b.ts", Closed)],
- false,
+ None,
),
),
(
change(
1,
[("file:///a.ts", Opened), ("file:///b.ts", Modified)],
- false,
+ None,
),
// new
change(
2,
// a gets opened then modified, b gets modified then closed
[("file:///a.ts", Opened), ("file:///b.ts", Closed)],
- false,
+ None,
),
// expected
change(
2,
[("file:///a.ts", Opened), ("file:///b.ts", Closed)],
- false,
+ None,
),
),
];
for (start, new, expected) in cases {
let mut pending = start;
- pending.coalesce(new.project_version, new.modified_scripts, false);
- assert_eq!(pending, expected);
+ pending.coalesce(new.project_version, new.modified_scripts, None);
+ assert_eq!(json!(pending), json!(expected));
}
}
}