diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs index 97ad8f260d..75f46dba88 100644 --- a/cli/lsp/code_lens.rs +++ b/cli/lsp/code_lens.rs @@ -220,9 +220,9 @@ async fn resolve_implementation_code_lens( data: CodeLensData, language_server: &mut language_server::Inner, ) -> Result { - let line_index = language_server - .get_line_index_sync(&data.specifier) - .unwrap(); + let asset_or_doc = + language_server.get_cached_asset_or_document(&data.specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetImplementation(( data.specifier.clone(), line_index.offset_tsc(code_lens.range.start)?, @@ -289,9 +289,9 @@ async fn resolve_references_code_lens( data: CodeLensData, language_server: &mut language_server::Inner, ) -> Result { - let line_index = language_server - .get_line_index_sync(&data.specifier) - .unwrap(); + let asset_or_document = + language_server.get_cached_asset_or_document(&data.specifier)?; + let line_index = asset_or_document.line_index(); let req = tsc::RequestMethod::GetReferences(( data.specifier.clone(), line_index.offset_tsc(code_lens.range.start)?, @@ -307,9 +307,12 @@ async fn resolve_references_code_lens( } let reference_specifier = resolve_url(&reference.document_span.file_name)?; - let line_index = - language_server.get_line_index(reference_specifier).await?; - locations.push(reference.to_location(line_index, language_server)); + let asset_or_doc = language_server + .get_asset_or_document(&reference_specifier) + .await?; + locations.push( + reference.to_location(asset_or_doc.line_index(), language_server), + ); } let command = if !locations.is_empty() { let title = if locations.len() > 1 { diff --git a/cli/lsp/completions.rs b/cli/lsp/completions.rs index 601ebb52b9..07857fb480 100644 --- a/cli/lsp/completions.rs +++ b/cli/lsp/completions.rs @@ -114,9 +114,8 @@ pub(crate) async fn get_import_completions( state_snapshot: &language_server::StateSnapshot, client: lspower::Client, ) -> Option { - let (text, _, range) = state_snapshot - .documents - .get_maybe_dependency(specifier, position)?; + let document = state_snapshot.documents.get(specifier)?; + let (text, _, range) = document.get_maybe_dependency(position)?; let range = to_narrow_lsp_range(&range); // completions for local relative modules if text.starts_with("./") || text.starts_with("../") { @@ -134,7 +133,9 @@ pub(crate) async fn get_import_completions( }; let maybe_items = state_snapshot .module_registries - .get_completions(&text, offset, &range, state_snapshot) + .get_completions(&text, offset, &range, |specifier| { + state_snapshot.documents.contains_specifier(specifier) + }) .await; let items = maybe_items.unwrap_or_else(|| { get_workspace_completions(specifier, &text, &range, state_snapshot) @@ -276,7 +277,12 @@ fn get_workspace_completions( range: &lsp::Range, state_snapshot: &language_server::StateSnapshot, ) -> Vec { - let workspace_specifiers = state_snapshot.documents.specifiers(false, true); + let workspace_specifiers = state_snapshot + .documents + .documents(false, true) + .into_iter() + .map(|d| d.specifier().clone()) + .collect(); let specifier_strings = get_relative_specifiers(specifier, workspace_specifiers); specifier_strings @@ -449,7 +455,7 @@ mod tests { .set(&specifier, HashMap::default(), source.as_bytes()) .expect("could not cache file"); assert!( - documents.content(&specifier).is_some(), + documents.get(&specifier).is_some(), "source could not be setup" ); } diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs index fdbf8b27bc..f14d80cd83 100644 --- a/cli/lsp/diagnostics.rs +++ b/cli/lsp/diagnostics.rs @@ -303,20 +303,20 @@ async fn generate_lint_diagnostics( snapshot: &language_server::StateSnapshot, collection: Arc>, ) -> Result { - let documents = snapshot.documents.clone(); + let documents = snapshot.documents.documents(true, true); let workspace_settings = snapshot.config.settings.workspace.clone(); let maybe_lint_config = snapshot.maybe_lint_config.clone(); tokio::task::spawn(async move { let mut diagnostics_vec = Vec::new(); if workspace_settings.lint { - for specifier in documents.specifiers(true, true) { - let version = documents.lsp_version(&specifier); + for document in documents { + let version = document.maybe_lsp_version(); let current_version = collection .lock() .await - .get_version(&specifier, &DiagnosticSource::DenoLint); + .get_version(document.specifier(), &DiagnosticSource::DenoLint); if version != current_version { - let diagnostics = match documents.parsed_source(&specifier) { + let diagnostics = match document.maybe_parsed_source() { Some(Ok(parsed_source)) => { if let Ok(references) = analysis::get_lint_references( &parsed_source, @@ -332,11 +332,15 @@ async fn generate_lint_diagnostics( } Some(Err(_)) => Vec::new(), None => { - error!("Missing file contents for: {}", specifier); + error!("Missing file contents for: {}", document.specifier()); Vec::new() } }; - diagnostics_vec.push((specifier.clone(), version, diagnostics)); + diagnostics_vec.push(( + document.specifier().clone(), + version, + diagnostics, + )); } } } @@ -356,14 +360,14 @@ async fn generate_ts_diagnostics( let collection = collection.lock().await; snapshot .documents - .specifiers(true, true) + .documents(true, true) .iter() - .filter_map(|s| { - let version = snapshot.documents.lsp_version(s); + .filter_map(|d| { + let version = d.maybe_lsp_version(); let current_version = - collection.get_version(s, &DiagnosticSource::TypeScript); + collection.get_version(d.specifier(), &DiagnosticSource::TypeScript); if version != current_version { - Some(s.clone()) + Some(d.specifier().clone()) } else { None } @@ -376,7 +380,11 @@ async fn generate_ts_diagnostics( ts_server.request(snapshot.clone(), req).await?; for (specifier_str, ts_diagnostics) in ts_diagnostics_map { let specifier = resolve_url(&specifier_str)?; - let version = snapshot.documents.lsp_version(&specifier); + let version = snapshot + .documents + .get(&specifier) + .map(|d| d.maybe_lsp_version()) + .flatten(); diagnostics_vec.push(( specifier, version, @@ -421,7 +429,18 @@ fn diagnose_dependency( ) { match resolved { Some(Ok((specifier, range))) => { - if !documents.contains_specifier(specifier) { + if let Some(doc) = documents.get(specifier) { + if let Some(message) = doc.maybe_warning() { + diagnostics.push(lsp::Diagnostic { + range: documents::to_lsp_range(range), + severity: Some(lsp::DiagnosticSeverity::Warning), + code: Some(lsp::NumberOrString::String("deno-warn".to_string())), + source: Some("deno".to_string()), + message, + ..Default::default() + }) + } + } else { let (code, message) = match specifier.scheme() { "file" => (Some(lsp::NumberOrString::String("no-local".to_string())), format!("Unable to load a local module: \"{}\".\n Please check the file path.", specifier)), "data" => (Some(lsp::NumberOrString::String("no-cache-data".to_string())), "Uncached data URL.".to_string()), @@ -437,15 +456,6 @@ fn diagnose_dependency( data: Some(json!({ "specifier": specifier })), ..Default::default() }); - } else if let Some(message) = documents.maybe_warning(specifier) { - diagnostics.push(lsp::Diagnostic { - range: documents::to_lsp_range(range), - severity: Some(lsp::DiagnosticSeverity::Warning), - code: Some(lsp::NumberOrString::String("deno-warn".to_string())), - source: Some("deno".to_string()), - message, - ..Default::default() - }) } } Some(Err(err)) => diagnostics.push(lsp::Diagnostic { @@ -471,18 +481,18 @@ async fn generate_deps_diagnostics( tokio::task::spawn(async move { let mut diagnostics_vec = Vec::new(); - for specifier in documents.specifiers(true, true) { - if !config.specifier_enabled(&specifier) { + for document in documents.documents(true, true) { + if !config.specifier_enabled(document.specifier()) { continue; } - let version = documents.lsp_version(&specifier); + let version = document.maybe_lsp_version(); let current_version = collection .lock() .await - .get_version(&specifier, &DiagnosticSource::Deno); + .get_version(document.specifier(), &DiagnosticSource::Deno); if version != current_version { let mut diagnostics = Vec::new(); - if let Some(dependencies) = documents.dependencies(&specifier) { + if let Some(dependencies) = document.dependencies() { for (_, dependency) in dependencies { diagnose_dependency( &mut diagnostics, @@ -496,7 +506,11 @@ async fn generate_deps_diagnostics( ); } } - diagnostics_vec.push((specifier.clone(), version, diagnostics)); + diagnostics_vec.push(( + document.specifier().clone(), + version, + diagnostics, + )); } } @@ -533,9 +547,14 @@ async fn publish_diagnostics( diagnostics .extend(collection.get(&specifier, DiagnosticSource::Deno).cloned()); } - let uri = specifier.clone(); - let version = snapshot.documents.lsp_version(&specifier); - client.publish_diagnostics(uri, diagnostics, version).await; + let version = snapshot + .documents + .get(&specifier) + .map(|d| d.maybe_lsp_version()) + .flatten(); + client + .publish_diagnostics(specifier.clone(), diagnostics, version) + .await; } } } @@ -678,7 +697,7 @@ mod tests { let (snapshot, collection, _) = setup(&[( "file:///a.ts", r#"import * as b from "./b.ts"; - + let a = "a"; console.log(a); "#, diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index ce7e4e36f7..9892eab1fe 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -2,6 +2,7 @@ use super::text::LineIndex; use super::tsc; +use super::tsc::AssetDocument; use crate::config_file::ConfigFile; use crate::file_fetcher::get_source_from_bytes; @@ -134,6 +135,62 @@ impl IndexValid { } } +#[derive(Debug, Clone)] +pub(crate) enum AssetOrDocument { + Document(Document), + Asset(AssetDocument), +} + +impl AssetOrDocument { + pub fn document(&self) -> Option<&Document> { + match self { + AssetOrDocument::Asset(_) => None, + AssetOrDocument::Document(doc) => Some(doc), + } + } + + pub fn text(&self) -> Arc { + match self { + AssetOrDocument::Asset(a) => a.text(), + AssetOrDocument::Document(d) => d.0.text_info.text(), + } + } + + pub fn line_index(&self) -> Arc { + match self { + AssetOrDocument::Asset(a) => a.line_index(), + AssetOrDocument::Document(d) => d.line_index(), + } + } + + pub fn maybe_navigation_tree(&self) -> Option> { + match self { + AssetOrDocument::Asset(a) => a.maybe_navigation_tree(), + AssetOrDocument::Document(d) => d.maybe_navigation_tree(), + } + } + + pub fn get_maybe_dependency( + &self, + position: &lsp::Position, + ) -> Option<(String, deno_graph::Dependency, deno_graph::Range)> { + self + .document() + .map(|d| d.get_maybe_dependency(position)) + .flatten() + } + + pub fn maybe_parsed_source( + &self, + ) -> Option> { + self.document().map(|d| d.maybe_parsed_source()).flatten() + } + + pub fn document_version(&self) -> Option { + self.document().map(|d| d.maybe_lsp_version()).flatten() + } +} + // TODO(@kitsonk) expose the synthetic module from deno_graph #[derive(Debug)] struct SyntheticModule { @@ -186,9 +243,9 @@ impl SyntheticModule { } } } - -#[derive(Debug)] -pub(crate) struct Document { +#[derive(Debug, Clone)] +struct DocumentInner { + fs_version: String, line_index: Arc, maybe_language_id: Option, maybe_lsp_version: Option, @@ -196,15 +253,17 @@ pub(crate) struct Document { Option>, maybe_navigation_tree: Option>, maybe_warning: Option, - source: SourceTextInfo, specifier: ModuleSpecifier, - version: String, + text_info: SourceTextInfo, } +#[derive(Debug, Clone)] +pub(crate) struct Document(Arc); + impl Document { fn new( specifier: ModuleSpecifier, - version: String, + fs_version: String, maybe_headers: Option<&HashMap>, content: Arc, maybe_resolver: Option<&dyn deno_graph::source::Resolver>, @@ -223,115 +282,19 @@ impl Document { maybe_resolver, Some(&parser), )); - let source = SourceTextInfo::new(content); - let line_index = Arc::new(LineIndex::new(source.text_str())); - Self { + let text_info = SourceTextInfo::new(content); + let line_index = Arc::new(LineIndex::new(text_info.text_str())); + Self(Arc::new(DocumentInner { + fs_version, line_index, maybe_language_id: None, maybe_lsp_version: None, maybe_module, maybe_navigation_tree: None, maybe_warning, - source, + text_info, specifier, - version, - } - } - - fn change( - &mut self, - version: i32, - changes: Vec, - maybe_resolver: Option<&dyn deno_graph::source::Resolver>, - ) -> Result<(), AnyError> { - let mut content = self.source.text_str().to_string(); - let mut line_index = self.line_index.clone(); - let mut index_valid = IndexValid::All; - for change in changes { - if let Some(range) = change.range { - if !index_valid.covers(range.start.line) { - line_index = Arc::new(LineIndex::new(&content)); - } - index_valid = IndexValid::UpTo(range.start.line); - let range = line_index.get_text_range(range)?; - content.replace_range(Range::::from(range), &change.text); - } else { - content = change.text; - index_valid = IndexValid::UpTo(0); - } - } - let content = Arc::new(content); - if self - .maybe_language_id - .as_ref() - .map(|li| li.is_diagnosable()) - .unwrap_or(false) - { - let maybe_headers = self - .maybe_language_id - .as_ref() - .map(|li| li.as_headers()) - .flatten(); - let parser = SourceParser::default(); - self.maybe_module = Some(deno_graph::parse_module( - &self.specifier, - maybe_headers, - content.clone(), - maybe_resolver, - Some(&parser), - )); - } else { - self.maybe_module = None; - } - self.source = SourceTextInfo::new(content); - self.line_index = if index_valid == IndexValid::All { - line_index - } else { - Arc::new(LineIndex::new(self.source.text_str())) - }; - self.maybe_lsp_version = Some(version); - self.maybe_navigation_tree = None; - Ok(()) - } - - fn close(&mut self) { - self.maybe_lsp_version = None; - self.maybe_language_id = None; - } - - fn content(&self) -> Arc { - self.source.text() - } - - fn is_diagnosable(&self) -> bool { - matches!( - self.media_type(), - // todo(#12410): Update with new media types for TS 4.5 - MediaType::JavaScript - | MediaType::Jsx - | MediaType::TypeScript - | MediaType::Tsx - | MediaType::Dts - ) - } - - fn is_open(&self) -> bool { - self.maybe_lsp_version.is_some() - } - - fn maybe_types_dependency(&self) -> deno_graph::Resolved { - let module_result = self.maybe_module.as_ref()?; - let module = module_result.as_ref().ok()?; - let (_, maybe_dep) = module.maybe_types_dependency.as_ref()?; - maybe_dep.clone() - } - - fn media_type(&self) -> MediaType { - if let Some(Ok(module)) = &self.maybe_module { - module.media_type - } else { - MediaType::from(&self.specifier) - } + })) } fn open( @@ -356,17 +319,214 @@ impl Document { }; let source = SourceTextInfo::new(content); let line_index = Arc::new(LineIndex::new(source.text_str())); - Self { + Self(Arc::new(DocumentInner { + fs_version: "1".to_string(), line_index, maybe_language_id: Some(language_id), maybe_lsp_version: Some(version), maybe_module, maybe_navigation_tree: None, maybe_warning: None, - source, + text_info: source, specifier, - version: "1".to_string(), + })) + } + + fn with_change( + &self, + version: i32, + changes: Vec, + maybe_resolver: Option<&dyn deno_graph::source::Resolver>, + ) -> Result { + let mut content = self.0.text_info.text_str().to_string(); + let mut line_index = self.0.line_index.clone(); + let mut index_valid = IndexValid::All; + for change in changes { + if let Some(range) = change.range { + if !index_valid.covers(range.start.line) { + line_index = Arc::new(LineIndex::new(&content)); + } + index_valid = IndexValid::UpTo(range.start.line); + let range = line_index.get_text_range(range)?; + content.replace_range(Range::::from(range), &change.text); + } else { + content = change.text; + index_valid = IndexValid::UpTo(0); + } } + let content = Arc::new(content); + let maybe_module = if self + .0 + .maybe_language_id + .as_ref() + .map(|li| li.is_diagnosable()) + .unwrap_or(false) + { + let maybe_headers = self + .0 + .maybe_language_id + .as_ref() + .map(|li| li.as_headers()) + .flatten(); + let parser = SourceParser::default(); + Some(deno_graph::parse_module( + &self.0.specifier, + maybe_headers, + content.clone(), + maybe_resolver, + Some(&parser), + )) + } else { + None + }; + let source = SourceTextInfo::new(content); + let line_index = if index_valid == IndexValid::All { + line_index + } else { + Arc::new(LineIndex::new(source.text_str())) + }; + Ok(Document(Arc::new(DocumentInner { + text_info: source, + line_index, + maybe_module, + maybe_lsp_version: Some(version), + maybe_navigation_tree: None, + ..(*self.0).clone() + }))) + } + + fn with_closed(&self) -> Document { + Document(Arc::new(DocumentInner { + maybe_lsp_version: None, + maybe_language_id: None, + ..(*self.0).clone() + })) + } + + fn with_navigation_tree( + &self, + navigation_tree: Arc, + ) -> Document { + Document(Arc::new(DocumentInner { + maybe_navigation_tree: Some(navigation_tree), + ..(*self.0).clone() + })) + } + + pub fn specifier(&self) -> &ModuleSpecifier { + &self.0.specifier + } + + pub fn content(&self) -> Arc { + self.0.text_info.text() + } + + pub fn text_info(&self) -> SourceTextInfo { + self.0.text_info.clone() + } + + pub fn line_index(&self) -> Arc { + self.0.line_index.clone() + } + + fn fs_version(&self) -> &str { + self.0.fs_version.as_str() + } + + pub fn script_version(&self) -> String { + self + .maybe_lsp_version() + .map_or_else(|| self.fs_version().to_string(), |v| v.to_string()) + } + + pub fn is_diagnosable(&self) -> bool { + matches!( + self.media_type(), + // todo(#12410): Update with new media types for TS 4.5 + MediaType::JavaScript + | MediaType::Jsx + | MediaType::TypeScript + | MediaType::Tsx + | MediaType::Dts + ) + } + + pub fn is_open(&self) -> bool { + self.0.maybe_lsp_version.is_some() + } + + pub fn maybe_types_dependency(&self) -> deno_graph::Resolved { + let module_result = self.0.maybe_module.as_ref()?; + let module = module_result.as_ref().ok()?; + let (_, maybe_dep) = module.maybe_types_dependency.as_ref()?; + maybe_dep.clone() + } + + pub fn media_type(&self) -> MediaType { + if let Some(Ok(module)) = &self.0.maybe_module { + module.media_type + } else { + MediaType::from(&self.0.specifier) + } + } + + /// Returns the current language server client version if any. + pub fn maybe_lsp_version(&self) -> Option { + self.0.maybe_lsp_version + } + + fn maybe_module( + &self, + ) -> Option<&Result> { + self.0.maybe_module.as_ref() + } + + pub fn maybe_parsed_source( + &self, + ) -> Option> { + self.maybe_module().map(|r| { + r.as_ref() + .map(|m| m.parsed_source.clone()) + .map_err(|err| err.clone()) + }) + } + + pub fn maybe_navigation_tree(&self) -> Option> { + self.0.maybe_navigation_tree.clone() + } + + pub fn maybe_warning(&self) -> Option { + self.0.maybe_warning.clone() + } + + pub fn dependencies(&self) -> Option> { + let module = self.maybe_module()?.as_ref().ok()?; + Some( + module + .dependencies + .iter() + .map(|(s, d)| (s.clone(), d.clone())) + .collect(), + ) + } + + /// If the supplied position is within a dependency range, return the resolved + /// string specifier for the dependency, the resolved dependency and the range + /// in the source document of the specifier. + pub fn get_maybe_dependency( + &self, + position: &lsp::Position, + ) -> Option<(String, deno_graph::Dependency, deno_graph::Range)> { + let module = self.maybe_module()?.as_ref().ok()?; + let position = deno_graph::Position { + line: position.line as usize, + character: position.character as usize, + }; + module.dependencies.iter().find_map(|(s, dep)| { + dep + .includes(&position) + .map(|r| (s.clone(), dep.clone(), r.clone())) + }) } } @@ -446,7 +606,7 @@ fn recurse_dependents( } #[derive(Debug, Default)] -struct Inner { +struct DocumentsInner { /// The DENO_DIR that the documents looks for non-file based modules. cache: HttpCache, /// A flag that indicates that stated data is potentially invalid and needs to @@ -468,7 +628,7 @@ struct Inner { redirects: HashMap, } -impl Inner { +impl DocumentsInner { fn new(location: &Path) -> Self { Self { cache: HttpCache::new(location), @@ -484,7 +644,7 @@ impl Inner { /// Adds a document by reading the document from the file system. fn add(&mut self, specifier: ModuleSpecifier) -> Option { - let version = self.calculate_version(&specifier)?; + let fs_version = self.calculate_fs_version(&specifier)?; let path = self.get_path(&specifier)?; let bytes = fs::read(path).ok()?; let doc = if specifier.scheme() == "file" { @@ -493,7 +653,7 @@ impl Inner { let content = Arc::new(get_source_from_bytes(bytes, maybe_charset).ok()?); Document::new( specifier.clone(), - version, + fs_version, None, content, self.get_maybe_resolver(), @@ -507,7 +667,7 @@ impl Inner { let content = Arc::new(get_source_from_bytes(bytes, maybe_charset).ok()?); Document::new( specifier.clone(), - version, + fs_version, maybe_headers, content, self.get_maybe_resolver(), @@ -524,7 +684,7 @@ impl Inner { let mut dependents_map: HashMap> = HashMap::new(); for (specifier, doc) in &self.docs { - if let Some(Ok(module)) = &doc.maybe_module { + if let Some(Ok(module)) = doc.maybe_module() { for dependency in module.dependencies.values() { if let Some(dep) = dependency.get_code() { dependents_map @@ -550,7 +710,10 @@ impl Inner { self.dependents_map = dependents_map; } - fn calculate_version(&self, specifier: &ModuleSpecifier) -> Option { + fn calculate_fs_version( + &self, + specifier: &ModuleSpecifier, + ) -> Option { let path = self.get_path(specifier)?; let metadata = fs::metadata(path).ok()?; if let Ok(modified) = metadata.modified() { @@ -569,16 +732,8 @@ impl Inner { specifier: &ModuleSpecifier, version: i32, changes: Vec, - ) -> Result<(), AnyError> { - // this duplicates the .get_resolver() method, because there is no easy - // way to avoid the double borrow of self that occurs here with getting the - // mut doc out. - let maybe_resolver = if self.maybe_jsx_resolver.is_some() { - self.maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver()) - } else { - self.maybe_import_map.as_ref().map(|im| im.as_resolver()) - }; - let doc = self.docs.get_mut(specifier).map_or_else( + ) -> Result { + let doc = self.docs.get(specifier).map_or_else( || { Err(custom_error( "NotFound", @@ -588,7 +743,9 @@ impl Inner { Ok, )?; self.dirty = true; - doc.change(version, changes, maybe_resolver) + let doc = doc.with_change(version, changes, self.get_maybe_resolver())?; + self.docs.insert(doc.specifier().clone(), doc.clone()); + Ok(doc) } fn close(&mut self, specifier: &ModuleSpecifier) -> Result<(), AnyError> { @@ -601,7 +758,7 @@ impl Inner { }, Ok, )?; - doc.close(); + *doc = doc.with_closed(); self.dirty = true; Ok(()) } @@ -634,25 +791,6 @@ impl Inner { self.docs.contains_key(&specifier) } - fn content(&mut self, specifier: &ModuleSpecifier) -> Option> { - self.get(specifier).map(|d| d.content()) - } - - fn dependencies( - &mut self, - specifier: &ModuleSpecifier, - ) -> Option> { - let doc = self.get(specifier)?; - let module = doc.maybe_module.as_ref()?.as_ref().ok()?; - Some( - module - .dependencies - .iter() - .map(|(s, d)| (s.clone(), d.clone())) - .collect(), - ) - } - fn dependents( &mut self, specifier: &ModuleSpecifier, @@ -678,22 +816,13 @@ impl Inner { self.docs.get(&specifier) } - fn get_maybe_dependency( - &mut self, - specifier: &ModuleSpecifier, - position: &lsp::Position, - ) -> Option<(String, deno_graph::Dependency, deno_graph::Range)> { - let doc = self.get(specifier)?; - let module = doc.maybe_module.as_ref()?.as_ref().ok()?; - let position = deno_graph::Position { - line: position.line as usize, - character: position.character as usize, - }; - module.dependencies.iter().find_map(|(s, dep)| { - dep - .includes(&position) - .map(|r| (s.clone(), dep.clone(), r.clone())) - }) + fn get_cached(&mut self, specifier: &ModuleSpecifier) -> Option<&Document> { + let specifier = self + .resolve_specifier(specifier) + .unwrap_or_else(|| specifier.clone()); + // this does not use `self.get` since that lazily adds documents, and we + // only care about documents already in the cache. + self.docs.get(&specifier) } fn get_maybe_resolver(&self) -> Option<&dyn deno_graph::source::Resolver> { @@ -704,26 +833,6 @@ impl Inner { } } - fn get_maybe_types_for_dependency( - &mut self, - dependency: &deno_graph::Dependency, - ) -> deno_graph::Resolved { - let code_dep = dependency.maybe_code.as_ref()?; - let (specifier, _) = code_dep.as_ref().ok()?; - let doc = self.get(specifier)?; - doc.maybe_types_dependency() - } - - fn get_navigation_tree( - &mut self, - specifier: &ModuleSpecifier, - ) -> Option> { - self - .get(specifier) - .map(|d| d.maybe_navigation_tree.clone()) - .flatten() - } - fn get_path(&self, specifier: &ModuleSpecifier) -> Option { if specifier.scheme() == "file" { specifier.to_file_path().ok() @@ -737,38 +846,19 @@ impl Inner { } } - fn is_diagnosable(&mut self, specifier: &ModuleSpecifier) -> bool { - if let Some(doc) = self.get(specifier) { - doc.is_diagnosable() - } else { - false - } - } - - fn is_formattable(&mut self, specifier: &ModuleSpecifier) -> bool { - // currently any document that is open in the language server is formattable - self.is_open(specifier) - } - - fn is_open(&mut self, specifier: &ModuleSpecifier) -> bool { - let specifier = self - .resolve_specifier(specifier) - .unwrap_or_else(|| specifier.clone()); - // this does not use `self.get` since that lazily adds documents, and we - // only care about documents already in the cache. - if let Some(doc) = self.docs.get(&specifier) { - doc.is_open() - } else { - false - } - } - fn is_valid(&mut self, specifier: &ModuleSpecifier) -> bool { - if self.is_open(specifier) { + if self + .get_cached(specifier) + .map(|d| d.is_open()) + .unwrap_or(false) + { true } else if let Some(specifier) = self.resolve_specifier(specifier) { - self.docs.get(&specifier).map(|d| d.version.clone()) - == self.calculate_version(&specifier) + self + .docs + .get(&specifier) + .map(|d| d.fs_version().to_string()) + == self.calculate_fs_version(&specifier) } else { // even though it isn't valid, it just can't exist, so we will say it is // valid @@ -776,62 +866,44 @@ impl Inner { } } - fn line_index( - &mut self, - specifier: &ModuleSpecifier, - ) -> Option> { - let specifier = self.resolve_specifier(specifier)?; - self.docs.get(&specifier).map(|doc| doc.line_index.clone()) - } - - fn lsp_version(&self, specifier: &ModuleSpecifier) -> Option { - self - .docs - .get(specifier) - .map(|doc| doc.maybe_lsp_version) - .flatten() - } - - fn maybe_warning(&mut self, specifier: &ModuleSpecifier) -> Option { - self - .get(specifier) - .map(|d| d.maybe_warning.clone()) - .flatten() - } - fn open( &mut self, specifier: ModuleSpecifier, version: i32, language_id: LanguageId, content: Arc, - ) { + ) -> Document { let maybe_resolver = self.get_maybe_resolver(); - let document_data = Document::open( + let document = Document::open( specifier.clone(), version, language_id, content, maybe_resolver, ); - self.docs.insert(specifier, document_data); + self.docs.insert(specifier, document.clone()); self.dirty = true; + document } - fn parsed_source( - &mut self, - specifier: &ModuleSpecifier, - ) -> Option> { + fn documents( + &self, + open_only: bool, + diagnosable_only: bool, + ) -> Vec { self - .get(specifier) - .map(|doc| { - doc.maybe_module.as_ref().map(|r| { - r.as_ref() - .map(|m| m.parsed_source.clone()) - .map_err(|err| err.clone()) - }) + .docs + .values() + .filter_map(|doc| { + let open = open_only && doc.is_open(); + let diagnosable = diagnosable_only && doc.is_diagnosable(); + if (!open_only || open) && (!diagnosable_only || diagnosable) { + Some(doc.clone()) + } else { + None + } }) - .flatten() + .collect() } fn resolve( @@ -841,7 +913,7 @@ impl Inner { ) -> Option>> { let doc = self.get(referrer)?; let mut results = Vec::new(); - if let Some(Ok(module)) = &doc.maybe_module { + if let Some(Ok(module)) = doc.maybe_module() { let dependencies = module.dependencies.clone(); for specifier in specifiers { if specifier.starts_with("asset:") { @@ -878,8 +950,7 @@ impl Inner { specifier: &ModuleSpecifier, ) -> Option<(ModuleSpecifier, MediaType)> { let doc = self.get(specifier)?; - let maybe_module = - doc.maybe_module.as_ref().map(|r| r.as_ref().ok()).flatten(); + let maybe_module = doc.maybe_module().map(|r| r.as_ref().ok()).flatten(); let maybe_types_dependency = maybe_module .map(|m| { m.maybe_types_dependency @@ -969,37 +1040,10 @@ impl Inner { let doc = self.docs.get_mut(specifier).ok_or_else(|| { custom_error("NotFound", format!("Specifier not found {}", specifier)) })?; - doc.maybe_navigation_tree = Some(navigation_tree); + *doc = doc.with_navigation_tree(navigation_tree); Ok(()) } - fn specifiers( - &self, - open_only: bool, - diagnosable_only: bool, - ) -> Vec { - self - .docs - .iter() - .filter_map(|(specifier, doc)| { - let open = open_only && doc.is_open(); - let diagnosable = diagnosable_only && doc.is_diagnosable(); - if (!open_only || open) && (!diagnosable_only || diagnosable) { - Some(specifier.clone()) - } else { - None - } - }) - .collect() - } - - fn text_info( - &mut self, - specifier: &ModuleSpecifier, - ) -> Option { - self.get(specifier).map(|d| d.source.clone()) - } - fn update_config( &mut self, maybe_import_map: Option>, @@ -1029,21 +1073,28 @@ impl Inner { } self.dirty = true; } - - fn version(&mut self, specifier: &ModuleSpecifier) -> Option { - self.get(specifier).map(|d| { - d.maybe_lsp_version - .map_or_else(|| d.version.clone(), |v| v.to_string()) - }) - } } #[derive(Debug, Clone, Default)] -pub(crate) struct Documents(Arc>); +pub(crate) struct Documents(Arc>); impl Documents { pub fn new(location: &Path) -> Self { - Self(Arc::new(Mutex::new(Inner::new(location)))) + Self(Arc::new(Mutex::new(DocumentsInner::new(location)))) + } + + /// "Open" a document from the perspective of the editor, meaning that + /// requests for information from the document will come from the in-memory + /// representation received from the language server client, versus reading + /// information from the disk. + pub fn open( + &self, + specifier: ModuleSpecifier, + version: i32, + language_id: LanguageId, + content: Arc, + ) -> Document { + self.0.lock().open(specifier, version, language_id, content) } /// Apply language server content changes to an open document. @@ -1052,7 +1103,7 @@ impl Documents { specifier: &ModuleSpecifier, version: i32, changes: Vec, - ) -> Result<(), AnyError> { + ) -> Result { self.0.lock().change(specifier, version, changes) } @@ -1078,20 +1129,6 @@ impl Documents { self.0.lock().contains_specifier(specifier) } - /// If the specifier can be resolved to a document, return its current - /// content, otherwise none. - pub fn content(&self, specifier: &ModuleSpecifier) -> Option> { - self.0.lock().content(specifier) - } - - /// Return an optional vector of dependencies for a given specifier. - pub fn dependencies( - &self, - specifier: &ModuleSpecifier, - ) -> Option> { - self.0.lock().dependencies(specifier) - } - /// Return an array of specifiers, if any, that are dependent upon the /// supplied specifier. This is used to determine invalidation of diagnostics /// when a module has been changed. @@ -1102,85 +1139,21 @@ impl Documents { self.0.lock().dependents(specifier) } - /// If the supplied position is within a dependency range, return the resolved - /// string specifier for the dependency, the resolved dependency and the range - /// in the source document of the specifier. - pub fn get_maybe_dependency( + /// Return a vector of documents that are contained in the document store, + /// where `open_only` flag would provide only those documents currently open + /// in the editor and `diagnosable_only` would provide only those documents + /// that the language server can provide diagnostics for. + pub fn documents( &self, - specifier: &ModuleSpecifier, - position: &lsp::Position, - ) -> Option<(String, deno_graph::Dependency, deno_graph::Range)> { - self.0.lock().get_maybe_dependency(specifier, position) + open_only: bool, + diagnosable_only: bool, + ) -> Vec { + self.0.lock().documents(open_only, diagnosable_only) } - /// For a given dependency, try to resolve the maybe_types_dependency for the - /// dependency. This covers modules that assert their own types, like via the - /// triple-slash reference, or the `X-TypeScript-Types` header. - pub fn get_maybe_types_for_dependency( - &self, - dependency: &deno_graph::Dependency, - ) -> deno_graph::Resolved { - self.0.lock().get_maybe_types_for_dependency(dependency) - } - - /// Get a reference to the navigation tree stored for a given specifier, if - /// any. - pub fn get_navigation_tree( - &self, - specifier: &ModuleSpecifier, - ) -> Option> { - self.0.lock().get_navigation_tree(specifier) - } - - /// Indicates that a specifier is able to be diagnosed by the language server - pub fn is_diagnosable(&self, specifier: &ModuleSpecifier) -> bool { - self.0.lock().is_diagnosable(specifier) - } - - /// Indicates that a specifier is formattable. - pub fn is_formattable(&self, specifier: &ModuleSpecifier) -> bool { - self.0.lock().is_formattable(specifier) - } - - /// Return a reference to the line index for a given specifiers, if any. - pub fn line_index( - &self, - specifier: &ModuleSpecifier, - ) -> Option> { - self.0.lock().line_index(specifier) - } - - /// Return the current language server client version for a given specifier, - /// if any. - pub fn lsp_version(&self, specifier: &ModuleSpecifier) -> Option { - self.0.lock().lsp_version(specifier) - } - - /// Return a warning header for a given specifier, if present. - pub fn maybe_warning(&self, specifier: &ModuleSpecifier) -> Option { - self.0.lock().maybe_warning(specifier) - } - - /// "Open" a document from the perspective of the editor, meaning that - /// requests for information from the document will come from the in-memory - /// representation received from the language server client, versus reading - /// information from the disk. - pub fn open( - &self, - specifier: ModuleSpecifier, - version: i32, - language_id: LanguageId, - content: Arc, - ) { - self.0.lock().open(specifier, version, language_id, content) - } - - /// Return the parsed source or the module graph error for a given specifier. - pub fn parsed_source( - &self, - specifier: &ModuleSpecifier, - ) -> Option> { - self.0.lock().parsed_source(specifier) + /// Return a document for the specifier. + pub fn get(&self, specifier: &ModuleSpecifier) -> Option { + self.0.lock().get(specifier).cloned() } /// For a given set of string specifiers, resolve each one from the graph, @@ -1211,26 +1184,6 @@ impl Documents { .set_navigation_tree(specifier, navigation_tree) } - /// Return a vector of specifiers that are contained in the document store, - /// where `open_only` flag would provide only those documents currently open - /// in the editor and `diagnosable_only` would provide only those documents - /// that the language server can provide diagnostics for. - pub fn specifiers( - &self, - open_only: bool, - diagnosable_only: bool, - ) -> Vec { - self.0.lock().specifiers(open_only, diagnosable_only) - } - - /// Return the current text info for a given specifier. - pub fn text_info( - &self, - specifier: &ModuleSpecifier, - ) -> Option { - self.0.lock().text_info(specifier) - } - pub fn update_config( &self, maybe_import_map: Option>, @@ -1241,11 +1194,6 @@ impl Documents { .lock() .update_config(maybe_import_map, maybe_config_file) } - - /// Return the version of a document in the document cache. - pub fn version(&self, specifier: &ModuleSpecifier) -> Option { - self.0.lock().version(specifier) - } } #[cfg(test)] @@ -1270,15 +1218,10 @@ console.log(b); "# .to_string(), ); - documents.open( - specifier.clone(), - 1, - "javascript".parse().unwrap(), - content, - ); - assert!(documents.is_formattable(&specifier)); - assert!(documents.is_diagnosable(&specifier)); - assert!(documents.line_index(&specifier).is_some()); + let document = + documents.open(specifier, 1, "javascript".parse().unwrap(), content); + assert!(document.is_open()); + assert!(document.is_diagnosable()); } #[test] @@ -1318,7 +1261,7 @@ console.log(b); ) .unwrap(); assert_eq!( - documents.content(&specifier).unwrap().as_str(), + documents.get(&specifier).unwrap().content().as_str(), r#"import * as b from "./b.ts"; console.log(b, "hello deno"); "# diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 73d028e766..10a69fadbb 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -39,6 +39,7 @@ use super::diagnostics; use super::diagnostics::DiagnosticSource; use super::documents::to_hover_text; use super::documents::to_lsp_range; +use super::documents::AssetOrDocument; use super::documents::Documents; use super::documents::LanguageId; use super::lsp_custom; @@ -47,7 +48,6 @@ use super::performance::Performance; use super::refactor; use super::registries; use super::text; -use super::text::LineIndex; use super::tsc; use super::tsc::AssetDocument; use super::tsc::Assets; @@ -171,71 +171,82 @@ impl Inner { } } - /// Searches assets, open documents and external sources for a line_index, - /// which might be performed asynchronously, hydrating in memory caches for - /// subsequent requests. - pub(crate) async fn get_line_index( + /// Searches assets and open documents which might be performed asynchronously, + /// hydrating in memory caches for subsequent requests. + pub(crate) async fn get_asset_or_document( &mut self, - specifier: ModuleSpecifier, - ) -> Result, AnyError> { - let mark = self - .performance - .mark("get_line_index", Some(json!({ "specifier": specifier }))); - let result = if specifier.scheme() == "asset" { - if let Some(asset) = self.get_asset(&specifier).await? { - Ok(asset.line_index) - } else { - Err(anyhow!("asset is missing: {}", specifier)) - } - } else if let Some(line_index) = self.documents.line_index(&specifier) { - Ok(line_index) - } else { - Err(anyhow!("Unable to find line index for: {}", specifier)) - }; - self.performance.measure(mark); - result + specifier: &ModuleSpecifier, + ) -> LspResult { + self + .get_maybe_asset_or_document(specifier) + .await? + .map_or_else( + || { + Err(LspError::invalid_params(format!( + "Unable to find asset or document for: {}", + specifier + ))) + }, + Ok, + ) } - /// Only searches already cached assets and documents for a line index. If - /// the line index cannot be found, `None` is returned. - pub fn get_line_index_sync( - &self, + /// Searches assets and open documents which might be performed asynchronously, + /// hydrating in memory caches for subsequent requests. + pub(crate) async fn get_maybe_asset_or_document( + &mut self, specifier: &ModuleSpecifier, - ) -> Option> { + ) -> LspResult> { let mark = self.performance.mark( - "get_line_index_sync", + "get_maybe_asset_or_document", Some(json!({ "specifier": specifier })), ); - let maybe_line_index = if specifier.scheme() == "asset" { - if let Some(Some(asset)) = self.assets.get(specifier) { - Some(asset.line_index.clone()) - } else { - None - } + let result = if specifier.scheme() == "asset" { + self.get_asset(specifier).await?.map(AssetOrDocument::Asset) } else { - self.documents.line_index(specifier) + self.documents.get(specifier).map(AssetOrDocument::Document) }; self.performance.measure(mark); - maybe_line_index + Ok(result) } - // TODO(@kitsonk) we really should find a better way to just return the - // content as a `&str`, or be able to get the byte at a particular offset - // which is all that this API that is consuming it is trying to do at the - // moment - /// Searches already cached assets and documents and returns its text - /// content. If not found, `None` is returned. - pub(crate) fn get_text_content( + /// Only searches already cached assets and documents. If + /// the asset or document cannot be found an error is returned. + pub(crate) fn get_cached_asset_or_document( &self, specifier: &ModuleSpecifier, - ) -> Option> { + ) -> LspResult { + self + .get_maybe_cached_asset_or_document(specifier) + .map_or_else( + || { + Err(LspError::invalid_params(format!( + "An unexpected specifier ({}) was provided.", + specifier + ))) + }, + Ok, + ) + } + + /// Only searches already cached assets and documents. If + /// the asset or document cannot be found, `None` is returned. + pub(crate) fn get_maybe_cached_asset_or_document( + &self, + specifier: &ModuleSpecifier, + ) -> Option { if specifier.scheme() == "asset" { self .assets .get(specifier) - .map(|o| o.clone().map(|a| a.text))? + .map(|maybe_asset| { + maybe_asset + .as_ref() + .map(|asset| AssetOrDocument::Asset(asset.clone())) + }) + .flatten() } else { - self.documents.content(specifier) + self.documents.get(specifier).map(AssetOrDocument::Document) } } @@ -247,33 +258,30 @@ impl Inner { "get_navigation_tree", Some(json!({ "specifier": specifier })), ); - let maybe_navigation_tree = if specifier.scheme() == "asset" { - self.assets.get_navigation_tree(specifier) - } else { - self.documents.get_navigation_tree(specifier) - }; - let navigation_tree = if let Some(navigation_tree) = maybe_navigation_tree { - navigation_tree - } else { - let navigation_tree: tsc::NavigationTree = self - .ts_server - .request( - self.snapshot()?, - tsc::RequestMethod::GetNavigationTree(specifier.clone()), - ) - .await?; - let navigation_tree = Arc::new(navigation_tree); - if specifier.scheme() == "asset" { - self - .assets - .set_navigation_tree(specifier, navigation_tree.clone())?; + let asset_or_doc = self.get_cached_asset_or_document(specifier)?; + let navigation_tree = + if let Some(navigation_tree) = asset_or_doc.maybe_navigation_tree() { + navigation_tree } else { - self - .documents - .set_navigation_tree(specifier, navigation_tree.clone())?; - } - navigation_tree - }; + let navigation_tree: tsc::NavigationTree = self + .ts_server + .request( + self.snapshot()?, + tsc::RequestMethod::GetNavigationTree(specifier.clone()), + ) + .await?; + let navigation_tree = Arc::new(navigation_tree); + if specifier.scheme() == "asset" { + self + .assets + .set_navigation_tree(specifier, navigation_tree.clone())?; + } else { + self + .documents + .set_navigation_tree(specifier, navigation_tree.clone())?; + } + navigation_tree + }; self.performance.measure(mark); Ok(navigation_tree) } @@ -335,7 +343,11 @@ impl Inner { | MediaType::Dts ) } else { - self.documents.is_diagnosable(specifier) + self + .documents + .get(specifier) + .map(|d| d.is_diagnosable()) + .unwrap_or(false) } } @@ -594,22 +606,20 @@ impl Inner { Ok(()) } - pub(crate) fn document_version( - &self, - specifier: &ModuleSpecifier, - ) -> Option { - self.documents.lsp_version(specifier) - } - async fn get_asset( &mut self, specifier: &ModuleSpecifier, - ) -> Result, AnyError> { + ) -> LspResult> { if let Some(maybe_asset) = self.assets.get(specifier) { Ok(maybe_asset.clone()) } else { let maybe_asset = - tsc::get_asset(specifier, &self.ts_server, self.snapshot()?).await?; + tsc::get_asset(specifier, &self.ts_server, self.snapshot()?) + .await + .map_err(|err| { + error!("Error getting asset {}: {}", specifier, err); + LspError::internal_error() + })?; self.assets.insert(specifier.clone(), maybe_asset.clone()); Ok(maybe_asset) } @@ -783,14 +793,14 @@ impl Inner { ); } let content = Arc::new(params.text_document.text); - self.documents.open( + let document = self.documents.open( specifier.clone(), params.text_document.version, params.text_document.language_id.parse().unwrap(), content, ); - if self.is_diagnosable(&specifier) { + if document.is_diagnosable() { self .diagnostics_server .invalidate(self.documents.dependents(&specifier)) @@ -811,8 +821,8 @@ impl Inner { params.text_document.version, params.content_changes, ) { - Ok(()) => { - if self.is_diagnosable(&specifier) { + Ok(document) => { + if document.is_diagnosable() { self .diagnostics_server .invalidate(self.documents.dependents(&specifier)) @@ -974,16 +984,8 @@ impl Inner { } let mark = self.performance.mark("document_symbol", Some(¶ms)); - - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_document = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_document.line_index(); let req = tsc::RequestMethod::GetNavigationTree(specifier); let navigation_tree: tsc::NavigationTree = self @@ -1014,9 +1016,10 @@ impl Inner { params: DocumentFormattingParams, ) -> LspResult>> { let specifier = self.url_map.normalize_url(¶ms.text_document.uri); - if !self.documents.is_formattable(&specifier) { - return Ok(None); - } + let document = match self.documents.get(&specifier) { + Some(doc) if doc.is_open() => doc, + _ => return Ok(None), + }; let mark = self.performance.mark("formatting", Some(¶ms)); let file_path = if let Ok(file_path) = params.text_document.uri.to_file_path() { @@ -1031,34 +1034,23 @@ impl Inner { Default::default() }; - let content = self.documents.content(&specifier).map_or_else( - || { - Err(LspError::invalid_params( - "The specified file could not be found in memory.", - )) - }, - Ok, - )?; - let line_index = self.documents.line_index(&specifier).unwrap(); - let maybe_parsed_source = self.documents.parsed_source(&specifier); - let text_edits = tokio::task::spawn_blocking(move || { - let format_result = match maybe_parsed_source { + let format_result = match document.maybe_parsed_source() { Some(Ok(parsed_source)) => { format_parsed_source(&parsed_source, fmt_options) } Some(Err(err)) => Err(err.to_string()), None => { // it's not a js/ts file, so attempt to format its contents - format_file(&file_path, content.as_str(), fmt_options) + format_file(&file_path, document.content().as_str(), fmt_options) } }; match format_result { Ok(new_text) => Some(text::get_edits( - content.as_str(), + document.content().as_str(), &new_text, - line_index.as_ref(), + document.line_index().as_ref(), )), Err(err) => { // TODO(lucacasonato): handle error properly @@ -1094,14 +1086,17 @@ impl Inner { } let mark = self.performance.mark("hover", Some(¶ms)); - let hover = if let Some((_, dep, range)) = - self.documents.get_maybe_dependency( - &specifier, - ¶ms.text_document_position_params.position, - ) { - let maybe_types_dependency = - self.documents.get_maybe_types_for_dependency(&dep); - let value = match (&dep.maybe_code, &dep.maybe_type, &maybe_types_dependency) { + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let hover = if let Some((_, dep, range)) = asset_or_doc + .get_maybe_dependency(¶ms.text_document_position_params.position) + { + let dep_maybe_types_dependency = dep + .get_code() + .map(|s| self.documents.get(s)) + .flatten() + .map(|d| d.maybe_types_dependency()) + .flatten(); + let value = match (&dep.maybe_code, &dep.maybe_type, &dep_maybe_types_dependency) { (Some(code_dep), Some(type_dep), None) => format!( "**Resolved Dependency**\n\n**Code**: {}\n\n**Types**: {}\n", to_hover_text(code_dep), @@ -1136,15 +1131,7 @@ impl Inner { range: Some(to_lsp_range(&range)), }) } else { - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetQuickInfo(( specifier, line_index.offset_tsc(params.text_document_position_params.position)?, @@ -1176,7 +1163,8 @@ impl Inner { let mark = self.performance.mark("code_action", Some(¶ms)); let mut all_actions = CodeActionResponse::new(); - let line_index = self.get_line_index_sync(&specifier).unwrap(); + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); // QuickFix let fixable_diagnostics: Vec<&Diagnostic> = params @@ -1266,12 +1254,8 @@ impl Inner { .add_deno_lint_ignore_action( &specifier, diagnostic, - self.documents.text_info(&specifier), - self - .documents - .parsed_source(&specifier) - .map(|r| r.ok()) - .flatten(), + asset_or_doc.document().map(|d| d.text_info()), + asset_or_doc.maybe_parsed_source().map(|r| r.ok()).flatten(), ) .map_err(|err| { error!("Unable to fix lint error: {}", err); @@ -1400,8 +1384,9 @@ impl Inner { error!("Unable to decode code action data: {}", err); LspError::invalid_params("The CodeAction's data is invalid.") })?; - let line_index = - self.get_line_index_sync(&action_data.specifier).unwrap(); + let asset_or_doc = + self.get_cached_asset_or_document(&action_data.specifier)?; + let line_index = asset_or_doc.line_index(); let start = line_index.offset_tsc(action_data.range.start)?; let length = line_index.offset_tsc(action_data.range.end)? - start; let req = tsc::RequestMethod::GetEditsForRefactor(( @@ -1449,25 +1434,15 @@ impl Inner { } let mark = self.performance.mark("code_lens", Some(¶ms)); + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; let navigation_tree = self.get_navigation_tree(&specifier).await.map_err(|err| { error!("Error getting code lenses for \"{}\": {}", specifier, err); LspError::internal_error() })?; - let parsed_source = self - .documents - .parsed_source(&specifier) - .map(|r| r.ok()) - .flatten(); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let parsed_source = + asset_or_doc.maybe_parsed_source().map(|r| r.ok()).flatten(); + let line_index = asset_or_doc.line_index(); let code_lenses = code_lens::collect( &specifier, parsed_source, @@ -1520,15 +1495,8 @@ impl Inner { } let mark = self.performance.mark("document_highlight", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let files_to_search = vec![specifier.clone()]; let req = tsc::RequestMethod::GetDocumentHighlights(( specifier, @@ -1572,15 +1540,8 @@ impl Inner { } let mark = self.performance.mark("references", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetReferences(( specifier, line_index.offset_tsc(params.text_document_position.position)?, @@ -1602,10 +1563,9 @@ impl Inner { } let reference_specifier = resolve_url(&reference.document_span.file_name).unwrap(); - // TODO(lucacasonato): handle error correctly - let line_index = - self.get_line_index(reference_specifier).await.unwrap(); - results.push(reference.to_location(line_index, self)); + let asset_or_doc = + self.get_asset_or_document(&reference_specifier).await?; + results.push(reference.to_location(asset_or_doc.line_index(), self)); } self.performance.measure(mark); @@ -1630,15 +1590,8 @@ impl Inner { } let mark = self.performance.mark("goto_definition", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetDefinition(( specifier, line_index.offset_tsc(params.text_document_position_params.position)?, @@ -1676,6 +1629,7 @@ impl Inner { } let mark = self.performance.mark("completion", Some(¶ms)); + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; // Import specifiers are something wholly internal to Deno, so for // completions, we will use internal logic and if there are completions // for imports, we will return those and not send a message into tsc, where @@ -1690,15 +1644,7 @@ impl Inner { { Some(response) } else { - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let line_index = asset_or_doc.line_index(); let trigger_character = if let Some(context) = ¶ms.context { context.trigger_character.clone() } else { @@ -1801,15 +1747,8 @@ impl Inner { } let mark = self.performance.mark("goto_implementation", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetImplementation(( specifier, @@ -1854,15 +1793,7 @@ impl Inner { } let mark = self.performance.mark("folding_range", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; let req = tsc::RequestMethod::GetOutliningSpans(specifier.clone()); let outlining_spans: Vec = self @@ -1875,20 +1806,13 @@ impl Inner { })?; let response = if !outlining_spans.is_empty() { - let text_content = - self.get_text_content(&specifier).ok_or_else(|| { - LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - )) - })?; Some( outlining_spans .iter() .map(|span| { span.to_folding_range( - line_index.clone(), - text_content.as_str().as_bytes(), + asset_or_doc.line_index(), + asset_or_doc.text().as_str().as_bytes(), self.config.client_capabilities.line_folding_only, ) }) @@ -1913,15 +1837,8 @@ impl Inner { } let mark = self.performance.mark("incoming_calls", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::ProvideCallHierarchyIncomingCalls(( specifier.clone(), @@ -1969,15 +1886,8 @@ impl Inner { } let mark = self.performance.mark("outgoing_calls", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::ProvideCallHierarchyOutgoingCalls(( specifier.clone(), @@ -2030,15 +1940,8 @@ impl Inner { let mark = self .performance .mark("prepare_call_hierarchy", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::PrepareCallHierarchy(( specifier.clone(), @@ -2109,15 +2012,8 @@ impl Inner { } let mark = self.performance.mark("rename", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::FindRenameLocations { specifier, @@ -2204,15 +2100,8 @@ impl Inner { } let mark = self.performance.mark("selection_range", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let mut selection_ranges = Vec::::new(); for position in params.positions { @@ -2249,15 +2138,8 @@ impl Inner { } let mark = self.performance.mark("semantic_tokens_full", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let req = tsc::RequestMethod::GetEncodedSemanticClassifications(( specifier.clone(), @@ -2300,15 +2182,8 @@ impl Inner { let mark = self .performance .mark("semantic_tokens_range", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let start = line_index.offset_tsc(params.range.start)?; let length = line_index.offset_tsc(params.range.end)? - start; @@ -2350,15 +2225,8 @@ impl Inner { } let mark = self.performance.mark("signature_help", Some(¶ms)); - let line_index = self.get_line_index_sync(&specifier).map_or_else( - || { - Err(LspError::invalid_params(format!( - "An unexpected specifier ({}) was provided.", - specifier - ))) - }, - Ok, - )?; + let asset_or_doc = self.get_cached_asset_or_document(&specifier)?; + let line_index = asset_or_doc.line_index(); let options = if let Some(context) = params.context { tsc::SignatureHelpItemsOptions { trigger_reason: Some(tsc::SignatureHelpTriggerReason { @@ -2689,7 +2557,12 @@ impl Inner { let specifier = self.url_map.normalize_url(¶ms.text_document.uri); let contents = if specifier.as_str() == "deno:/status.md" { let mut contents = String::new(); - let mut documents_specifiers = self.documents.specifiers(false, false); + let mut documents_specifiers = self + .documents + .documents(false, false) + .into_iter() + .map(|d| d.specifier().clone()) + .collect::>(); documents_specifiers.sort(); let measures = self.performance.to_vec(); let workspace_settings = self.config.get_workspace_settings(); @@ -2743,27 +2616,15 @@ impl Inner { } Some(contents) } else { - match specifier.scheme() { - "asset" => { - if let Some(asset) = self - .get_asset(&specifier) - .await - .map_err(|_| LspError::internal_error())? - { - Some(asset.text.to_string()) - } else { - error!("Missing asset: {}", specifier); - None - } - } - _ => { - if let Some(source) = self.documents.content(&specifier) { - Some(source.to_string()) - } else { - error!("The cached source was not found: {}", specifier); - None - } - } + let asset_or_doc = self + .get_maybe_asset_or_document(&specifier) + .await + .map_err(|_| LspError::internal_error())?; + if let Some(asset_or_doc) = asset_or_doc { + Some(asset_or_doc.text().to_string()) + } else { + error!("The source was not found: {}", specifier); + None } }; self.performance.measure(mark); diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs index b63850c32b..bef55150de 100644 --- a/cli/lsp/registries.rs +++ b/cli/lsp/registries.rs @@ -1,6 +1,5 @@ // Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. -use super::language_server; use super::path_to_regex::parse; use super::path_to_regex::string_to_regex; use super::path_to_regex::Compiler; @@ -439,7 +438,7 @@ impl ModuleRegistry { current_specifier: &str, offset: usize, range: &lsp::Range, - state_snapshot: &language_server::StateSnapshot, + specifier_exists: impl Fn(&ModuleSpecifier) -> bool, ) -> Option> { if let Ok(specifier) = Url::parse(current_specifier) { let origin = base_url(&specifier); @@ -529,9 +528,7 @@ impl ModuleRegistry { }, )); let command = if key.name == last_key_name - && !state_snapshot - .documents - .contains_specifier(&item_specifier) + && !specifier_exists(&item_specifier) { Some(lsp::Command { title: "".to_string(), @@ -619,9 +616,7 @@ impl ModuleRegistry { }), ); let command = if k.name == last_key_name - && !state_snapshot - .documents - .contains_specifier(&item_specifier) + && !specifier_exists(&item_specifier) { Some(lsp::Command { title: "".to_string(), @@ -771,38 +766,8 @@ impl ModuleRegistry { #[cfg(test)] mod tests { use super::*; - use crate::lsp::documents::Documents; use tempfile::TempDir; - fn mock_state_snapshot( - source_fixtures: &[(&str, &str)], - location: &Path, - ) -> language_server::StateSnapshot { - let documents = Documents::new(location); - let http_cache = HttpCache::new(location); - for (specifier, source) in source_fixtures { - let specifier = - resolve_url(specifier).expect("failed to create specifier"); - http_cache - .set(&specifier, HashMap::default(), source.as_bytes()) - .expect("could not cache file"); - assert!( - documents.content(&specifier).is_some(), - "source could not be setup" - ); - } - language_server::StateSnapshot { - documents, - ..Default::default() - } - } - - fn setup(sources: &[(&str, &str)]) -> language_server::StateSnapshot { - let temp_dir = TempDir::new().expect("could not create temp dir"); - let location = temp_dir.path().join("deps"); - mock_state_snapshot(sources, &location) - } - #[test] fn test_validate_registry_configuration() { assert!(validate_config(&RegistryConfigurationJson { @@ -920,9 +885,8 @@ mod tests { character: 21, }, }; - let state_snapshot = setup(&[]); let completions = module_registry - .get_completions("h", 1, &range, &state_snapshot) + .get_completions("h", 1, &range, |_| false) .await; assert!(completions.is_some()); let completions = completions.unwrap(); @@ -946,7 +910,7 @@ mod tests { }, }; let completions = module_registry - .get_completions("http://localhost", 16, &range, &state_snapshot) + .get_completions("http://localhost", 16, &range, |_| false) .await; assert!(completions.is_some()); let completions = completions.unwrap(); @@ -971,7 +935,6 @@ mod tests { .enable("http://localhost:4545/") .await .expect("could not enable"); - let state_snapshot = setup(&[]); let range = lsp::Range { start: lsp::Position { line: 0, @@ -983,7 +946,7 @@ mod tests { }, }; let completions = module_registry - .get_completions("http://localhost:4545", 21, &range, &state_snapshot) + .get_completions("http://localhost:4545", 21, &range, |_| false) .await; assert!(completions.is_some()); let completions = completions.unwrap(); @@ -1007,7 +970,7 @@ mod tests { }, }; let completions = module_registry - .get_completions("http://localhost:4545/", 22, &range, &state_snapshot) + .get_completions("http://localhost:4545/", 22, &range, |_| false) .await; assert!(completions.is_some()); let completions = completions.unwrap(); @@ -1031,7 +994,7 @@ mod tests { }, }; let completions = module_registry - .get_completions("http://localhost:4545/x/", 24, &range, &state_snapshot) + .get_completions("http://localhost:4545/x/", 24, &range, |_| false) .await; assert!(completions.is_some()); let completions = completions.unwrap(); @@ -1049,12 +1012,7 @@ mod tests { }, }; let completions = module_registry - .get_completions( - "http://localhost:4545/x/a@", - 26, - &range, - &state_snapshot, - ) + .get_completions("http://localhost:4545/x/a@", 26, &range, |_| false) .await; assert!(completions.is_some()); let completions = completions.unwrap(); @@ -1070,12 +1028,9 @@ mod tests { }, }; let completions = module_registry - .get_completions( - "http://localhost:4545/x/a@v1.0.0/", - 33, - &range, - &state_snapshot, - ) + .get_completions("http://localhost:4545/x/a@v1.0.0/", 33, &range, |_| { + false + }) .await; assert!(completions.is_some()); let completions = completions.unwrap(); @@ -1098,7 +1053,6 @@ mod tests { .enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-key-first.json") .await .expect("could not enable"); - let state_snapshot = setup(&[]); let range = lsp::Range { start: lsp::Position { line: 0, @@ -1110,7 +1064,7 @@ mod tests { }, }; let completions = module_registry - .get_completions("http://localhost:4545/", 22, &range, &state_snapshot) + .get_completions("http://localhost:4545/", 22, &range, |_| false) .await; assert!(completions.is_some()); let completions = completions.unwrap(); @@ -1139,12 +1093,7 @@ mod tests { }, }; let completions = module_registry - .get_completions( - "http://localhost:4545/cde@", - 26, - &range, - &state_snapshot, - ) + .get_completions("http://localhost:4545/cde@", 26, &range, |_| false) .await; assert!(completions.is_some()); let completions = completions.unwrap(); @@ -1173,7 +1122,6 @@ mod tests { .enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-complex.json") .await .expect("could not enable"); - let state_snapshot = setup(&[]); let range = lsp::Range { start: lsp::Position { line: 0, @@ -1185,7 +1133,7 @@ mod tests { }, }; let completions = module_registry - .get_completions("http://localhost:4545/", 22, &range, &state_snapshot) + .get_completions("http://localhost:4545/", 22, &range, |_| false) .await; assert!(completions.is_some()); let completions = completions.unwrap(); diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index fc3695a5bc..9350d6f195 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -119,25 +119,58 @@ impl TsServer { } } +#[derive(Debug, Clone)] +struct AssetDocumentInner { + text: Arc, + length: usize, + line_index: Arc, + maybe_navigation_tree: Option>, +} + /// An lsp representation of an asset in memory, that has either been retrieved /// from static assets built into Rust, or static assets built into tsc. #[derive(Debug, Clone)] -pub struct AssetDocument { - pub text: Arc, - pub length: usize, - pub line_index: Arc, - pub maybe_navigation_tree: Option>, -} +pub struct AssetDocument(Arc); impl AssetDocument { - pub fn new>(text: T) -> Self { + pub fn new(text: impl AsRef) -> Self { let text = text.as_ref(); - Self { + Self(Arc::new(AssetDocumentInner { text: Arc::new(text.to_string()), length: text.encode_utf16().count(), line_index: Arc::new(LineIndex::new(text)), maybe_navigation_tree: None, - } + })) + } + + pub fn with_navigation_tree( + &self, + tree: Arc, + ) -> AssetDocument { + AssetDocument(Arc::new(AssetDocumentInner { + maybe_navigation_tree: Some(tree), + ..(*self.0).clone() + })) + } + + pub fn text(&self) -> Arc { + self.0.text.clone() + } + + pub fn text_str(&self) -> &str { + self.0.text.as_str() + } + + pub fn length(&self) -> usize { + self.0.length + } + + pub fn line_index(&self) -> Arc { + self.0.line_index.clone() + } + + pub fn maybe_navigation_tree(&self) -> Option> { + self.0.maybe_navigation_tree.clone() } } @@ -176,14 +209,6 @@ impl Assets { self.0.insert(k, v) } - pub fn get_navigation_tree( - &self, - specifier: &ModuleSpecifier, - ) -> Option> { - let doc = self.0.get(specifier).map(|v| v.as_ref()).flatten()?; - doc.maybe_navigation_tree.as_ref().cloned() - } - pub fn set_navigation_tree( &mut self, specifier: &ModuleSpecifier, @@ -196,7 +221,7 @@ impl Assets { let doc = maybe_doc .as_mut() .ok_or_else(|| anyhow!("Cannot get doc mutable"))?; - doc.maybe_navigation_tree = Some(navigation_tree); + *doc = doc.with_navigation_tree(navigation_tree); Ok(()) } } @@ -605,10 +630,11 @@ impl DocumentSpan { language_server: &mut language_server::Inner, ) -> Option { let target_specifier = normalize_specifier(&self.file_name).ok()?; - let target_line_index = language_server - .get_line_index(target_specifier.clone()) + let target_asset_or_doc = language_server + .get_asset_or_document(&target_specifier) .await .ok()?; + let target_line_index = target_asset_or_doc.line_index(); let target_uri = language_server .url_map .normalize_specifier(&target_specifier) @@ -854,6 +880,8 @@ impl RenameLocations { for location in self.locations.iter() { let specifier = normalize_specifier(&location.document_span.file_name)?; let uri = language_server.url_map.normalize_specifier(&specifier)?; + let asset_or_doc = + language_server.get_asset_or_document(&specifier).await?; // ensure TextDocumentEdit for `location.file_name`. if text_document_edit_map.get(&uri).is_none() { @@ -862,7 +890,7 @@ impl RenameLocations { lsp::TextDocumentEdit { text_document: lsp::OptionalVersionedTextDocumentIdentifier { uri: uri.clone(), - version: language_server.document_version(&specifier), + version: asset_or_doc.document_version(), }, edits: Vec::>::new(), @@ -876,7 +904,7 @@ impl RenameLocations { range: location .document_span .text_span - .to_range(language_server.get_line_index(specifier.clone()).await?), + .to_range(asset_or_doc.line_index()), new_text: new_name.to_string(), })); } @@ -1018,16 +1046,17 @@ impl FileTextChanges { language_server: &mut language_server::Inner, ) -> Result { let specifier = normalize_specifier(&self.file_name)?; - let line_index = language_server.get_line_index(specifier.clone()).await?; + let asset_or_doc = + language_server.get_asset_or_document(&specifier).await?; let edits = self .text_changes .iter() - .map(|tc| tc.as_text_edit(line_index.clone())) + .map(|tc| tc.as_text_edit(asset_or_doc.line_index())) .collect(); Ok(lsp::TextDocumentEdit { text_document: lsp::OptionalVersionedTextDocumentIdentifier { uri: specifier.clone(), - version: language_server.document_version(&specifier), + version: asset_or_doc.document_version(), }, edits, }) @@ -1039,11 +1068,17 @@ impl FileTextChanges { ) -> Result, AnyError> { let mut ops = Vec::::new(); let specifier = normalize_specifier(&self.file_name)?; - let line_index = if !self.is_new_file.unwrap_or(false) { - language_server.get_line_index(specifier.clone()).await? + let maybe_asset_or_document = if !self.is_new_file.unwrap_or(false) { + let asset_or_doc = + language_server.get_asset_or_document(&specifier).await?; + Some(asset_or_doc) } else { - Arc::new(LineIndex::new("")) + None }; + let line_index = maybe_asset_or_document + .as_ref() + .map(|d| d.line_index()) + .unwrap_or_else(|| Arc::new(LineIndex::new(""))); if self.is_new_file.unwrap_or(false) { ops.push(lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create( @@ -1066,7 +1101,9 @@ impl FileTextChanges { ops.push(lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit { text_document: lsp::OptionalVersionedTextDocumentIdentifier { uri: specifier.clone(), - version: language_server.document_version(&specifier), + version: maybe_asset_or_document + .map(|d| d.document_version()) + .flatten(), }, edits, })); @@ -1354,13 +1391,13 @@ impl CallHierarchyItem { maybe_root_path: Option<&Path>, ) -> Option { let target_specifier = normalize_specifier(&self.file).ok()?; - let target_line_index = language_server - .get_line_index(target_specifier) + let target_asset_or_doc = language_server + .get_asset_or_document(&target_specifier) .await .ok()?; Some(self.to_call_hierarchy_item( - target_line_index, + target_asset_or_doc.line_index(), language_server, maybe_root_path, )) @@ -1455,21 +1492,21 @@ impl CallHierarchyIncomingCall { maybe_root_path: Option<&Path>, ) -> Option { let target_specifier = normalize_specifier(&self.from.file).ok()?; - let target_line_index = language_server - .get_line_index(target_specifier) + let target_asset_or_doc = language_server + .get_asset_or_document(&target_specifier) .await .ok()?; Some(lsp::CallHierarchyIncomingCall { from: self.from.to_call_hierarchy_item( - target_line_index.clone(), + target_asset_or_doc.line_index(), language_server, maybe_root_path, ), from_ranges: self .from_spans .iter() - .map(|span| span.to_range(target_line_index.clone())) + .map(|span| span.to_range(target_asset_or_doc.line_index())) .collect(), }) } @@ -1490,14 +1527,14 @@ impl CallHierarchyOutgoingCall { maybe_root_path: Option<&Path>, ) -> Option { let target_specifier = normalize_specifier(&self.to.file).ok()?; - let target_line_index = language_server - .get_line_index(target_specifier) + let target_asset_or_doc = language_server + .get_asset_or_document(&target_specifier) .await .ok()?; Some(lsp::CallHierarchyOutgoingCall { to: self.to.to_call_hierarchy_item( - target_line_index, + target_asset_or_doc.line_index(), language_server, maybe_root_path, ), @@ -2099,10 +2136,11 @@ fn cache_snapshot( let content = state .state_snapshot .documents - .content(specifier) + .get(specifier) .ok_or_else(|| { - anyhow!("Specifier unexpectedly doesn't have content: {}", specifier) - })?; + anyhow!("Specifier unexpectedly doesn't exist: {}", specifier) + })? + .content(); state .snapshots .insert((specifier.clone(), version.into()), content.to_string()); @@ -2242,7 +2280,7 @@ fn op_get_length( let specifier = state.normalize_specifier(args.specifier)?; let r = if let Some(Some(asset)) = state.state_snapshot.assets.get(&specifier) { - Ok(asset.length) + Ok(asset.length()) } else { cache_snapshot(state, &specifier, args.version.clone())?; let content = state @@ -2275,7 +2313,7 @@ fn op_get_text( let specifier = state.normalize_specifier(args.specifier)?; let content = if let Some(Some(content)) = state.state_snapshot.assets.get(&specifier) { - content.text.as_str() + content.text_str() } else { cache_snapshot(state, &specifier, args.version.clone())?; state @@ -2296,9 +2334,9 @@ fn op_load( .performance .mark("op_load", Some(&args)); let specifier = state.normalize_specifier(args.specifier)?; - let result = state.state_snapshot.documents.content(&specifier); + let document = state.state_snapshot.documents.get(&specifier); state.state_snapshot.performance.measure(mark); - Ok(result.map(|t| t.to_string())) + Ok(document.map(|d| d.content().to_string())) } fn op_resolve( @@ -2347,7 +2385,15 @@ fn op_script_names( state: &mut State, _args: Value, ) -> Result, AnyError> { - Ok(state.state_snapshot.documents.specifiers(true, true)) + Ok( + state + .state_snapshot + .documents + .documents(true, true) + .into_iter() + .map(|d| d.specifier().clone()) + .collect(), + ) } #[derive(Debug, Deserialize, Serialize)] @@ -2372,7 +2418,12 @@ fn op_script_version( Ok(None) } } else { - Ok(state.state_snapshot.documents.version(&specifier)) + let script_version = state + .state_snapshot + .documents + .get(&specifier) + .map(|d| d.script_version()); + Ok(script_version) }; state.state_snapshot.performance.measure(mark);