1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-22 07:14:47 -05:00

refactor(lsp): prefer using document instead of documents collection (#12720)

This commit is contained in:
David Sherret 2021-11-12 11:42:04 -05:00 committed by GitHub
parent fdf890a68d
commit 28dbb4a95e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 728 additions and 897 deletions

View file

@ -220,9 +220,9 @@ async fn resolve_implementation_code_lens(
data: CodeLensData, data: CodeLensData,
language_server: &mut language_server::Inner, language_server: &mut language_server::Inner,
) -> Result<lsp::CodeLens, AnyError> { ) -> Result<lsp::CodeLens, AnyError> {
let line_index = language_server let asset_or_doc =
.get_line_index_sync(&data.specifier) language_server.get_cached_asset_or_document(&data.specifier)?;
.unwrap(); let line_index = asset_or_doc.line_index();
let req = tsc::RequestMethod::GetImplementation(( let req = tsc::RequestMethod::GetImplementation((
data.specifier.clone(), data.specifier.clone(),
line_index.offset_tsc(code_lens.range.start)?, line_index.offset_tsc(code_lens.range.start)?,
@ -289,9 +289,9 @@ async fn resolve_references_code_lens(
data: CodeLensData, data: CodeLensData,
language_server: &mut language_server::Inner, language_server: &mut language_server::Inner,
) -> Result<lsp::CodeLens, AnyError> { ) -> Result<lsp::CodeLens, AnyError> {
let line_index = language_server let asset_or_document =
.get_line_index_sync(&data.specifier) language_server.get_cached_asset_or_document(&data.specifier)?;
.unwrap(); let line_index = asset_or_document.line_index();
let req = tsc::RequestMethod::GetReferences(( let req = tsc::RequestMethod::GetReferences((
data.specifier.clone(), data.specifier.clone(),
line_index.offset_tsc(code_lens.range.start)?, line_index.offset_tsc(code_lens.range.start)?,
@ -307,9 +307,12 @@ async fn resolve_references_code_lens(
} }
let reference_specifier = let reference_specifier =
resolve_url(&reference.document_span.file_name)?; resolve_url(&reference.document_span.file_name)?;
let line_index = let asset_or_doc = language_server
language_server.get_line_index(reference_specifier).await?; .get_asset_or_document(&reference_specifier)
locations.push(reference.to_location(line_index, language_server)); .await?;
locations.push(
reference.to_location(asset_or_doc.line_index(), language_server),
);
} }
let command = if !locations.is_empty() { let command = if !locations.is_empty() {
let title = if locations.len() > 1 { let title = if locations.len() > 1 {

View file

@ -114,9 +114,8 @@ pub(crate) async fn get_import_completions(
state_snapshot: &language_server::StateSnapshot, state_snapshot: &language_server::StateSnapshot,
client: lspower::Client, client: lspower::Client,
) -> Option<lsp::CompletionResponse> { ) -> Option<lsp::CompletionResponse> {
let (text, _, range) = state_snapshot let document = state_snapshot.documents.get(specifier)?;
.documents let (text, _, range) = document.get_maybe_dependency(position)?;
.get_maybe_dependency(specifier, position)?;
let range = to_narrow_lsp_range(&range); let range = to_narrow_lsp_range(&range);
// completions for local relative modules // completions for local relative modules
if text.starts_with("./") || text.starts_with("../") { if text.starts_with("./") || text.starts_with("../") {
@ -134,7 +133,9 @@ pub(crate) async fn get_import_completions(
}; };
let maybe_items = state_snapshot let maybe_items = state_snapshot
.module_registries .module_registries
.get_completions(&text, offset, &range, state_snapshot) .get_completions(&text, offset, &range, |specifier| {
state_snapshot.documents.contains_specifier(specifier)
})
.await; .await;
let items = maybe_items.unwrap_or_else(|| { let items = maybe_items.unwrap_or_else(|| {
get_workspace_completions(specifier, &text, &range, state_snapshot) get_workspace_completions(specifier, &text, &range, state_snapshot)
@ -276,7 +277,12 @@ fn get_workspace_completions(
range: &lsp::Range, range: &lsp::Range,
state_snapshot: &language_server::StateSnapshot, state_snapshot: &language_server::StateSnapshot,
) -> Vec<lsp::CompletionItem> { ) -> Vec<lsp::CompletionItem> {
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 = let specifier_strings =
get_relative_specifiers(specifier, workspace_specifiers); get_relative_specifiers(specifier, workspace_specifiers);
specifier_strings specifier_strings
@ -449,7 +455,7 @@ mod tests {
.set(&specifier, HashMap::default(), source.as_bytes()) .set(&specifier, HashMap::default(), source.as_bytes())
.expect("could not cache file"); .expect("could not cache file");
assert!( assert!(
documents.content(&specifier).is_some(), documents.get(&specifier).is_some(),
"source could not be setup" "source could not be setup"
); );
} }

View file

@ -303,20 +303,20 @@ async fn generate_lint_diagnostics(
snapshot: &language_server::StateSnapshot, snapshot: &language_server::StateSnapshot,
collection: Arc<Mutex<DiagnosticCollection>>, collection: Arc<Mutex<DiagnosticCollection>>,
) -> Result<DiagnosticVec, AnyError> { ) -> Result<DiagnosticVec, AnyError> {
let documents = snapshot.documents.clone(); let documents = snapshot.documents.documents(true, true);
let workspace_settings = snapshot.config.settings.workspace.clone(); let workspace_settings = snapshot.config.settings.workspace.clone();
let maybe_lint_config = snapshot.maybe_lint_config.clone(); let maybe_lint_config = snapshot.maybe_lint_config.clone();
tokio::task::spawn(async move { tokio::task::spawn(async move {
let mut diagnostics_vec = Vec::new(); let mut diagnostics_vec = Vec::new();
if workspace_settings.lint { if workspace_settings.lint {
for specifier in documents.specifiers(true, true) { for document in documents {
let version = documents.lsp_version(&specifier); let version = document.maybe_lsp_version();
let current_version = collection let current_version = collection
.lock() .lock()
.await .await
.get_version(&specifier, &DiagnosticSource::DenoLint); .get_version(document.specifier(), &DiagnosticSource::DenoLint);
if version != current_version { if version != current_version {
let diagnostics = match documents.parsed_source(&specifier) { let diagnostics = match document.maybe_parsed_source() {
Some(Ok(parsed_source)) => { Some(Ok(parsed_source)) => {
if let Ok(references) = analysis::get_lint_references( if let Ok(references) = analysis::get_lint_references(
&parsed_source, &parsed_source,
@ -332,11 +332,15 @@ async fn generate_lint_diagnostics(
} }
Some(Err(_)) => Vec::new(), Some(Err(_)) => Vec::new(),
None => { None => {
error!("Missing file contents for: {}", specifier); error!("Missing file contents for: {}", document.specifier());
Vec::new() 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; let collection = collection.lock().await;
snapshot snapshot
.documents .documents
.specifiers(true, true) .documents(true, true)
.iter() .iter()
.filter_map(|s| { .filter_map(|d| {
let version = snapshot.documents.lsp_version(s); let version = d.maybe_lsp_version();
let current_version = let current_version =
collection.get_version(s, &DiagnosticSource::TypeScript); collection.get_version(d.specifier(), &DiagnosticSource::TypeScript);
if version != current_version { if version != current_version {
Some(s.clone()) Some(d.specifier().clone())
} else { } else {
None None
} }
@ -376,7 +380,11 @@ async fn generate_ts_diagnostics(
ts_server.request(snapshot.clone(), req).await?; ts_server.request(snapshot.clone(), req).await?;
for (specifier_str, ts_diagnostics) in ts_diagnostics_map { for (specifier_str, ts_diagnostics) in ts_diagnostics_map {
let specifier = resolve_url(&specifier_str)?; 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(( diagnostics_vec.push((
specifier, specifier,
version, version,
@ -421,7 +429,18 @@ fn diagnose_dependency(
) { ) {
match resolved { match resolved {
Some(Ok((specifier, range))) => { 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() { 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)), "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()), "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 })), data: Some(json!({ "specifier": specifier })),
..Default::default() ..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 { Some(Err(err)) => diagnostics.push(lsp::Diagnostic {
@ -471,18 +481,18 @@ async fn generate_deps_diagnostics(
tokio::task::spawn(async move { tokio::task::spawn(async move {
let mut diagnostics_vec = Vec::new(); let mut diagnostics_vec = Vec::new();
for specifier in documents.specifiers(true, true) { for document in documents.documents(true, true) {
if !config.specifier_enabled(&specifier) { if !config.specifier_enabled(document.specifier()) {
continue; continue;
} }
let version = documents.lsp_version(&specifier); let version = document.maybe_lsp_version();
let current_version = collection let current_version = collection
.lock() .lock()
.await .await
.get_version(&specifier, &DiagnosticSource::Deno); .get_version(document.specifier(), &DiagnosticSource::Deno);
if version != current_version { if version != current_version {
let mut diagnostics = Vec::new(); let mut diagnostics = Vec::new();
if let Some(dependencies) = documents.dependencies(&specifier) { if let Some(dependencies) = document.dependencies() {
for (_, dependency) in dependencies { for (_, dependency) in dependencies {
diagnose_dependency( diagnose_dependency(
&mut diagnostics, &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 diagnostics
.extend(collection.get(&specifier, DiagnosticSource::Deno).cloned()); .extend(collection.get(&specifier, DiagnosticSource::Deno).cloned());
} }
let uri = specifier.clone(); let version = snapshot
let version = snapshot.documents.lsp_version(&specifier); .documents
client.publish_diagnostics(uri, diagnostics, version).await; .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(&[( let (snapshot, collection, _) = setup(&[(
"file:///a.ts", "file:///a.ts",
r#"import * as b from "./b.ts"; r#"import * as b from "./b.ts";
let a = "a"; let a = "a";
console.log(a); console.log(a);
"#, "#,

File diff suppressed because it is too large Load diff

View file

@ -39,6 +39,7 @@ use super::diagnostics;
use super::diagnostics::DiagnosticSource; use super::diagnostics::DiagnosticSource;
use super::documents::to_hover_text; use super::documents::to_hover_text;
use super::documents::to_lsp_range; use super::documents::to_lsp_range;
use super::documents::AssetOrDocument;
use super::documents::Documents; use super::documents::Documents;
use super::documents::LanguageId; use super::documents::LanguageId;
use super::lsp_custom; use super::lsp_custom;
@ -47,7 +48,6 @@ use super::performance::Performance;
use super::refactor; use super::refactor;
use super::registries; use super::registries;
use super::text; use super::text;
use super::text::LineIndex;
use super::tsc; use super::tsc;
use super::tsc::AssetDocument; use super::tsc::AssetDocument;
use super::tsc::Assets; use super::tsc::Assets;
@ -171,71 +171,82 @@ impl Inner {
} }
} }
/// Searches assets, open documents and external sources for a line_index, /// Searches assets and open documents which might be performed asynchronously,
/// which might be performed asynchronously, hydrating in memory caches for /// hydrating in memory caches for subsequent requests.
/// subsequent requests. pub(crate) async fn get_asset_or_document(
pub(crate) async fn get_line_index(
&mut self, &mut self,
specifier: ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Result<Arc<LineIndex>, AnyError> { ) -> LspResult<AssetOrDocument> {
let mark = self self
.performance .get_maybe_asset_or_document(specifier)
.mark("get_line_index", Some(json!({ "specifier": specifier }))); .await?
let result = if specifier.scheme() == "asset" { .map_or_else(
if let Some(asset) = self.get_asset(&specifier).await? { || {
Ok(asset.line_index) Err(LspError::invalid_params(format!(
} else { "Unable to find asset or document for: {}",
Err(anyhow!("asset is missing: {}", specifier)) specifier
} )))
} else if let Some(line_index) = self.documents.line_index(&specifier) { },
Ok(line_index) Ok,
} else { )
Err(anyhow!("Unable to find line index for: {}", specifier))
};
self.performance.measure(mark);
result
} }
/// Only searches already cached assets and documents for a line index. If /// Searches assets and open documents which might be performed asynchronously,
/// the line index cannot be found, `None` is returned. /// hydrating in memory caches for subsequent requests.
pub fn get_line_index_sync( pub(crate) async fn get_maybe_asset_or_document(
&self, &mut self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Option<Arc<LineIndex>> { ) -> LspResult<Option<AssetOrDocument>> {
let mark = self.performance.mark( let mark = self.performance.mark(
"get_line_index_sync", "get_maybe_asset_or_document",
Some(json!({ "specifier": specifier })), Some(json!({ "specifier": specifier })),
); );
let maybe_line_index = if specifier.scheme() == "asset" { let result = if specifier.scheme() == "asset" {
if let Some(Some(asset)) = self.assets.get(specifier) { self.get_asset(specifier).await?.map(AssetOrDocument::Asset)
Some(asset.line_index.clone())
} else {
None
}
} else { } else {
self.documents.line_index(specifier) self.documents.get(specifier).map(AssetOrDocument::Document)
}; };
self.performance.measure(mark); self.performance.measure(mark);
maybe_line_index Ok(result)
} }
// TODO(@kitsonk) we really should find a better way to just return the /// Only searches already cached assets and documents. If
// content as a `&str`, or be able to get the byte at a particular offset /// the asset or document cannot be found an error is returned.
// which is all that this API that is consuming it is trying to do at the pub(crate) fn get_cached_asset_or_document(
// moment
/// Searches already cached assets and documents and returns its text
/// content. If not found, `None` is returned.
pub(crate) fn get_text_content(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Option<Arc<String>> { ) -> LspResult<AssetOrDocument> {
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<AssetOrDocument> {
if specifier.scheme() == "asset" { if specifier.scheme() == "asset" {
self self
.assets .assets
.get(specifier) .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 { } else {
self.documents.content(specifier) self.documents.get(specifier).map(AssetOrDocument::Document)
} }
} }
@ -247,33 +258,30 @@ impl Inner {
"get_navigation_tree", "get_navigation_tree",
Some(json!({ "specifier": specifier })), Some(json!({ "specifier": specifier })),
); );
let maybe_navigation_tree = if specifier.scheme() == "asset" { let asset_or_doc = self.get_cached_asset_or_document(specifier)?;
self.assets.get_navigation_tree(specifier) let navigation_tree =
} else { if let Some(navigation_tree) = asset_or_doc.maybe_navigation_tree() {
self.documents.get_navigation_tree(specifier) navigation_tree
};
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())?;
} else { } else {
self let navigation_tree: tsc::NavigationTree = self
.documents .ts_server
.set_navigation_tree(specifier, navigation_tree.clone())?; .request(
} self.snapshot()?,
navigation_tree 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); self.performance.measure(mark);
Ok(navigation_tree) Ok(navigation_tree)
} }
@ -335,7 +343,11 @@ impl Inner {
| MediaType::Dts | MediaType::Dts
) )
} else { } 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(()) Ok(())
} }
pub(crate) fn document_version(
&self,
specifier: &ModuleSpecifier,
) -> Option<i32> {
self.documents.lsp_version(specifier)
}
async fn get_asset( async fn get_asset(
&mut self, &mut self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Result<Option<AssetDocument>, AnyError> { ) -> LspResult<Option<AssetDocument>> {
if let Some(maybe_asset) = self.assets.get(specifier) { if let Some(maybe_asset) = self.assets.get(specifier) {
Ok(maybe_asset.clone()) Ok(maybe_asset.clone())
} else { } else {
let maybe_asset = 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()); self.assets.insert(specifier.clone(), maybe_asset.clone());
Ok(maybe_asset) Ok(maybe_asset)
} }
@ -783,14 +793,14 @@ impl Inner {
); );
} }
let content = Arc::new(params.text_document.text); let content = Arc::new(params.text_document.text);
self.documents.open( let document = self.documents.open(
specifier.clone(), specifier.clone(),
params.text_document.version, params.text_document.version,
params.text_document.language_id.parse().unwrap(), params.text_document.language_id.parse().unwrap(),
content, content,
); );
if self.is_diagnosable(&specifier) { if document.is_diagnosable() {
self self
.diagnostics_server .diagnostics_server
.invalidate(self.documents.dependents(&specifier)) .invalidate(self.documents.dependents(&specifier))
@ -811,8 +821,8 @@ impl Inner {
params.text_document.version, params.text_document.version,
params.content_changes, params.content_changes,
) { ) {
Ok(()) => { Ok(document) => {
if self.is_diagnosable(&specifier) { if document.is_diagnosable() {
self self
.diagnostics_server .diagnostics_server
.invalidate(self.documents.dependents(&specifier)) .invalidate(self.documents.dependents(&specifier))
@ -974,16 +984,8 @@ impl Inner {
} }
let mark = self.performance.mark("document_symbol", Some(&params)); let mark = self.performance.mark("document_symbol", Some(&params));
let asset_or_document = self.get_cached_asset_or_document(&specifier)?;
let line_index = self.get_line_index_sync(&specifier).map_or_else( let line_index = asset_or_document.line_index();
|| {
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let req = tsc::RequestMethod::GetNavigationTree(specifier); let req = tsc::RequestMethod::GetNavigationTree(specifier);
let navigation_tree: tsc::NavigationTree = self let navigation_tree: tsc::NavigationTree = self
@ -1014,9 +1016,10 @@ impl Inner {
params: DocumentFormattingParams, params: DocumentFormattingParams,
) -> LspResult<Option<Vec<TextEdit>>> { ) -> LspResult<Option<Vec<TextEdit>>> {
let specifier = self.url_map.normalize_url(&params.text_document.uri); let specifier = self.url_map.normalize_url(&params.text_document.uri);
if !self.documents.is_formattable(&specifier) { let document = match self.documents.get(&specifier) {
return Ok(None); Some(doc) if doc.is_open() => doc,
} _ => return Ok(None),
};
let mark = self.performance.mark("formatting", Some(&params)); let mark = self.performance.mark("formatting", Some(&params));
let file_path = let file_path =
if let Ok(file_path) = params.text_document.uri.to_file_path() { if let Ok(file_path) = params.text_document.uri.to_file_path() {
@ -1031,34 +1034,23 @@ impl Inner {
Default::default() 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 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)) => { Some(Ok(parsed_source)) => {
format_parsed_source(&parsed_source, fmt_options) format_parsed_source(&parsed_source, fmt_options)
} }
Some(Err(err)) => Err(err.to_string()), Some(Err(err)) => Err(err.to_string()),
None => { None => {
// it's not a js/ts file, so attempt to format its contents // 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 { match format_result {
Ok(new_text) => Some(text::get_edits( Ok(new_text) => Some(text::get_edits(
content.as_str(), document.content().as_str(),
&new_text, &new_text,
line_index.as_ref(), document.line_index().as_ref(),
)), )),
Err(err) => { Err(err) => {
// TODO(lucacasonato): handle error properly // TODO(lucacasonato): handle error properly
@ -1094,14 +1086,17 @@ impl Inner {
} }
let mark = self.performance.mark("hover", Some(&params)); let mark = self.performance.mark("hover", Some(&params));
let hover = if let Some((_, dep, range)) = let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
self.documents.get_maybe_dependency( let hover = if let Some((_, dep, range)) = asset_or_doc
&specifier, .get_maybe_dependency(&params.text_document_position_params.position)
&params.text_document_position_params.position, {
) { let dep_maybe_types_dependency = dep
let maybe_types_dependency = .get_code()
self.documents.get_maybe_types_for_dependency(&dep); .map(|s| self.documents.get(s))
let value = match (&dep.maybe_code, &dep.maybe_type, &maybe_types_dependency) { .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!( (Some(code_dep), Some(type_dep), None) => format!(
"**Resolved Dependency**\n\n**Code**: {}\n\n**Types**: {}\n", "**Resolved Dependency**\n\n**Code**: {}\n\n**Types**: {}\n",
to_hover_text(code_dep), to_hover_text(code_dep),
@ -1136,15 +1131,7 @@ impl Inner {
range: Some(to_lsp_range(&range)), range: Some(to_lsp_range(&range)),
}) })
} else { } else {
let line_index = self.get_line_index_sync(&specifier).map_or_else( let line_index = asset_or_doc.line_index();
|| {
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let req = tsc::RequestMethod::GetQuickInfo(( let req = tsc::RequestMethod::GetQuickInfo((
specifier, specifier,
line_index.offset_tsc(params.text_document_position_params.position)?, line_index.offset_tsc(params.text_document_position_params.position)?,
@ -1176,7 +1163,8 @@ impl Inner {
let mark = self.performance.mark("code_action", Some(&params)); let mark = self.performance.mark("code_action", Some(&params));
let mut all_actions = CodeActionResponse::new(); 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 // QuickFix
let fixable_diagnostics: Vec<&Diagnostic> = params let fixable_diagnostics: Vec<&Diagnostic> = params
@ -1266,12 +1254,8 @@ impl Inner {
.add_deno_lint_ignore_action( .add_deno_lint_ignore_action(
&specifier, &specifier,
diagnostic, diagnostic,
self.documents.text_info(&specifier), asset_or_doc.document().map(|d| d.text_info()),
self asset_or_doc.maybe_parsed_source().map(|r| r.ok()).flatten(),
.documents
.parsed_source(&specifier)
.map(|r| r.ok())
.flatten(),
) )
.map_err(|err| { .map_err(|err| {
error!("Unable to fix lint error: {}", err); error!("Unable to fix lint error: {}", err);
@ -1400,8 +1384,9 @@ impl Inner {
error!("Unable to decode code action data: {}", err); error!("Unable to decode code action data: {}", err);
LspError::invalid_params("The CodeAction's data is invalid.") LspError::invalid_params("The CodeAction's data is invalid.")
})?; })?;
let line_index = let asset_or_doc =
self.get_line_index_sync(&action_data.specifier).unwrap(); 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 start = line_index.offset_tsc(action_data.range.start)?;
let length = line_index.offset_tsc(action_data.range.end)? - start; let length = line_index.offset_tsc(action_data.range.end)? - start;
let req = tsc::RequestMethod::GetEditsForRefactor(( let req = tsc::RequestMethod::GetEditsForRefactor((
@ -1449,25 +1434,15 @@ impl Inner {
} }
let mark = self.performance.mark("code_lens", Some(&params)); let mark = self.performance.mark("code_lens", Some(&params));
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
let navigation_tree = let navigation_tree =
self.get_navigation_tree(&specifier).await.map_err(|err| { self.get_navigation_tree(&specifier).await.map_err(|err| {
error!("Error getting code lenses for \"{}\": {}", specifier, err); error!("Error getting code lenses for \"{}\": {}", specifier, err);
LspError::internal_error() LspError::internal_error()
})?; })?;
let parsed_source = self let parsed_source =
.documents asset_or_doc.maybe_parsed_source().map(|r| r.ok()).flatten();
.parsed_source(&specifier) let line_index = asset_or_doc.line_index();
.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 code_lenses = code_lens::collect( let code_lenses = code_lens::collect(
&specifier, &specifier,
parsed_source, parsed_source,
@ -1520,15 +1495,8 @@ impl Inner {
} }
let mark = self.performance.mark("document_highlight", Some(&params)); let mark = self.performance.mark("document_highlight", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let files_to_search = vec![specifier.clone()]; let files_to_search = vec![specifier.clone()];
let req = tsc::RequestMethod::GetDocumentHighlights(( let req = tsc::RequestMethod::GetDocumentHighlights((
specifier, specifier,
@ -1572,15 +1540,8 @@ impl Inner {
} }
let mark = self.performance.mark("references", Some(&params)); let mark = self.performance.mark("references", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let req = tsc::RequestMethod::GetReferences(( let req = tsc::RequestMethod::GetReferences((
specifier, specifier,
line_index.offset_tsc(params.text_document_position.position)?, line_index.offset_tsc(params.text_document_position.position)?,
@ -1602,10 +1563,9 @@ impl Inner {
} }
let reference_specifier = let reference_specifier =
resolve_url(&reference.document_span.file_name).unwrap(); resolve_url(&reference.document_span.file_name).unwrap();
// TODO(lucacasonato): handle error correctly let asset_or_doc =
let line_index = self.get_asset_or_document(&reference_specifier).await?;
self.get_line_index(reference_specifier).await.unwrap(); results.push(reference.to_location(asset_or_doc.line_index(), self));
results.push(reference.to_location(line_index, self));
} }
self.performance.measure(mark); self.performance.measure(mark);
@ -1630,15 +1590,8 @@ impl Inner {
} }
let mark = self.performance.mark("goto_definition", Some(&params)); let mark = self.performance.mark("goto_definition", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let req = tsc::RequestMethod::GetDefinition(( let req = tsc::RequestMethod::GetDefinition((
specifier, specifier,
line_index.offset_tsc(params.text_document_position_params.position)?, line_index.offset_tsc(params.text_document_position_params.position)?,
@ -1676,6 +1629,7 @@ impl Inner {
} }
let mark = self.performance.mark("completion", Some(&params)); let mark = self.performance.mark("completion", Some(&params));
let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
// Import specifiers are something wholly internal to Deno, so for // Import specifiers are something wholly internal to Deno, so for
// completions, we will use internal logic and if there are completions // 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 // for imports, we will return those and not send a message into tsc, where
@ -1690,15 +1644,7 @@ impl Inner {
{ {
Some(response) Some(response)
} else { } else {
let line_index = self.get_line_index_sync(&specifier).map_or_else( let line_index = asset_or_doc.line_index();
|| {
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let trigger_character = if let Some(context) = &params.context { let trigger_character = if let Some(context) = &params.context {
context.trigger_character.clone() context.trigger_character.clone()
} else { } else {
@ -1801,15 +1747,8 @@ impl Inner {
} }
let mark = self.performance.mark("goto_implementation", Some(&params)); let mark = self.performance.mark("goto_implementation", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let req = tsc::RequestMethod::GetImplementation(( let req = tsc::RequestMethod::GetImplementation((
specifier, specifier,
@ -1854,15 +1793,7 @@ impl Inner {
} }
let mark = self.performance.mark("folding_range", Some(&params)); let mark = self.performance.mark("folding_range", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| {
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let req = tsc::RequestMethod::GetOutliningSpans(specifier.clone()); let req = tsc::RequestMethod::GetOutliningSpans(specifier.clone());
let outlining_spans: Vec<tsc::OutliningSpan> = self let outlining_spans: Vec<tsc::OutliningSpan> = self
@ -1875,20 +1806,13 @@ impl Inner {
})?; })?;
let response = if !outlining_spans.is_empty() { 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( Some(
outlining_spans outlining_spans
.iter() .iter()
.map(|span| { .map(|span| {
span.to_folding_range( span.to_folding_range(
line_index.clone(), asset_or_doc.line_index(),
text_content.as_str().as_bytes(), asset_or_doc.text().as_str().as_bytes(),
self.config.client_capabilities.line_folding_only, self.config.client_capabilities.line_folding_only,
) )
}) })
@ -1913,15 +1837,8 @@ impl Inner {
} }
let mark = self.performance.mark("incoming_calls", Some(&params)); let mark = self.performance.mark("incoming_calls", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let req = tsc::RequestMethod::ProvideCallHierarchyIncomingCalls(( let req = tsc::RequestMethod::ProvideCallHierarchyIncomingCalls((
specifier.clone(), specifier.clone(),
@ -1969,15 +1886,8 @@ impl Inner {
} }
let mark = self.performance.mark("outgoing_calls", Some(&params)); let mark = self.performance.mark("outgoing_calls", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let req = tsc::RequestMethod::ProvideCallHierarchyOutgoingCalls(( let req = tsc::RequestMethod::ProvideCallHierarchyOutgoingCalls((
specifier.clone(), specifier.clone(),
@ -2030,15 +1940,8 @@ impl Inner {
let mark = self let mark = self
.performance .performance
.mark("prepare_call_hierarchy", Some(&params)); .mark("prepare_call_hierarchy", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let req = tsc::RequestMethod::PrepareCallHierarchy(( let req = tsc::RequestMethod::PrepareCallHierarchy((
specifier.clone(), specifier.clone(),
@ -2109,15 +2012,8 @@ impl Inner {
} }
let mark = self.performance.mark("rename", Some(&params)); let mark = self.performance.mark("rename", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let req = tsc::RequestMethod::FindRenameLocations { let req = tsc::RequestMethod::FindRenameLocations {
specifier, specifier,
@ -2204,15 +2100,8 @@ impl Inner {
} }
let mark = self.performance.mark("selection_range", Some(&params)); let mark = self.performance.mark("selection_range", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let mut selection_ranges = Vec::<SelectionRange>::new(); let mut selection_ranges = Vec::<SelectionRange>::new();
for position in params.positions { for position in params.positions {
@ -2249,15 +2138,8 @@ impl Inner {
} }
let mark = self.performance.mark("semantic_tokens_full", Some(&params)); let mark = self.performance.mark("semantic_tokens_full", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let req = tsc::RequestMethod::GetEncodedSemanticClassifications(( let req = tsc::RequestMethod::GetEncodedSemanticClassifications((
specifier.clone(), specifier.clone(),
@ -2300,15 +2182,8 @@ impl Inner {
let mark = self let mark = self
.performance .performance
.mark("semantic_tokens_range", Some(&params)); .mark("semantic_tokens_range", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let start = line_index.offset_tsc(params.range.start)?; let start = line_index.offset_tsc(params.range.start)?;
let length = line_index.offset_tsc(params.range.end)? - 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(&params)); let mark = self.performance.mark("signature_help", Some(&params));
let line_index = self.get_line_index_sync(&specifier).map_or_else( let asset_or_doc = self.get_cached_asset_or_document(&specifier)?;
|| { let line_index = asset_or_doc.line_index();
Err(LspError::invalid_params(format!(
"An unexpected specifier ({}) was provided.",
specifier
)))
},
Ok,
)?;
let options = if let Some(context) = params.context { let options = if let Some(context) = params.context {
tsc::SignatureHelpItemsOptions { tsc::SignatureHelpItemsOptions {
trigger_reason: Some(tsc::SignatureHelpTriggerReason { trigger_reason: Some(tsc::SignatureHelpTriggerReason {
@ -2689,7 +2557,12 @@ impl Inner {
let specifier = self.url_map.normalize_url(&params.text_document.uri); let specifier = self.url_map.normalize_url(&params.text_document.uri);
let contents = if specifier.as_str() == "deno:/status.md" { let contents = if specifier.as_str() == "deno:/status.md" {
let mut contents = String::new(); 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::<Vec<_>>();
documents_specifiers.sort(); documents_specifiers.sort();
let measures = self.performance.to_vec(); let measures = self.performance.to_vec();
let workspace_settings = self.config.get_workspace_settings(); let workspace_settings = self.config.get_workspace_settings();
@ -2743,27 +2616,15 @@ impl Inner {
} }
Some(contents) Some(contents)
} else { } else {
match specifier.scheme() { let asset_or_doc = self
"asset" => { .get_maybe_asset_or_document(&specifier)
if let Some(asset) = self .await
.get_asset(&specifier) .map_err(|_| LspError::internal_error())?;
.await if let Some(asset_or_doc) = asset_or_doc {
.map_err(|_| LspError::internal_error())? Some(asset_or_doc.text().to_string())
{ } else {
Some(asset.text.to_string()) error!("The source was not found: {}", specifier);
} else { None
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
}
}
} }
}; };
self.performance.measure(mark); self.performance.measure(mark);

View file

@ -1,6 +1,5 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. // 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::parse;
use super::path_to_regex::string_to_regex; use super::path_to_regex::string_to_regex;
use super::path_to_regex::Compiler; use super::path_to_regex::Compiler;
@ -439,7 +438,7 @@ impl ModuleRegistry {
current_specifier: &str, current_specifier: &str,
offset: usize, offset: usize,
range: &lsp::Range, range: &lsp::Range,
state_snapshot: &language_server::StateSnapshot, specifier_exists: impl Fn(&ModuleSpecifier) -> bool,
) -> Option<Vec<lsp::CompletionItem>> { ) -> Option<Vec<lsp::CompletionItem>> {
if let Ok(specifier) = Url::parse(current_specifier) { if let Ok(specifier) = Url::parse(current_specifier) {
let origin = base_url(&specifier); let origin = base_url(&specifier);
@ -529,9 +528,7 @@ impl ModuleRegistry {
}, },
)); ));
let command = if key.name == last_key_name let command = if key.name == last_key_name
&& !state_snapshot && !specifier_exists(&item_specifier)
.documents
.contains_specifier(&item_specifier)
{ {
Some(lsp::Command { Some(lsp::Command {
title: "".to_string(), title: "".to_string(),
@ -619,9 +616,7 @@ impl ModuleRegistry {
}), }),
); );
let command = if k.name == last_key_name let command = if k.name == last_key_name
&& !state_snapshot && !specifier_exists(&item_specifier)
.documents
.contains_specifier(&item_specifier)
{ {
Some(lsp::Command { Some(lsp::Command {
title: "".to_string(), title: "".to_string(),
@ -771,38 +766,8 @@ impl ModuleRegistry {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::lsp::documents::Documents;
use tempfile::TempDir; 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] #[test]
fn test_validate_registry_configuration() { fn test_validate_registry_configuration() {
assert!(validate_config(&RegistryConfigurationJson { assert!(validate_config(&RegistryConfigurationJson {
@ -920,9 +885,8 @@ mod tests {
character: 21, character: 21,
}, },
}; };
let state_snapshot = setup(&[]);
let completions = module_registry let completions = module_registry
.get_completions("h", 1, &range, &state_snapshot) .get_completions("h", 1, &range, |_| false)
.await; .await;
assert!(completions.is_some()); assert!(completions.is_some());
let completions = completions.unwrap(); let completions = completions.unwrap();
@ -946,7 +910,7 @@ mod tests {
}, },
}; };
let completions = module_registry let completions = module_registry
.get_completions("http://localhost", 16, &range, &state_snapshot) .get_completions("http://localhost", 16, &range, |_| false)
.await; .await;
assert!(completions.is_some()); assert!(completions.is_some());
let completions = completions.unwrap(); let completions = completions.unwrap();
@ -971,7 +935,6 @@ mod tests {
.enable("http://localhost:4545/") .enable("http://localhost:4545/")
.await .await
.expect("could not enable"); .expect("could not enable");
let state_snapshot = setup(&[]);
let range = lsp::Range { let range = lsp::Range {
start: lsp::Position { start: lsp::Position {
line: 0, line: 0,
@ -983,7 +946,7 @@ mod tests {
}, },
}; };
let completions = module_registry let completions = module_registry
.get_completions("http://localhost:4545", 21, &range, &state_snapshot) .get_completions("http://localhost:4545", 21, &range, |_| false)
.await; .await;
assert!(completions.is_some()); assert!(completions.is_some());
let completions = completions.unwrap(); let completions = completions.unwrap();
@ -1007,7 +970,7 @@ mod tests {
}, },
}; };
let completions = module_registry let completions = module_registry
.get_completions("http://localhost:4545/", 22, &range, &state_snapshot) .get_completions("http://localhost:4545/", 22, &range, |_| false)
.await; .await;
assert!(completions.is_some()); assert!(completions.is_some());
let completions = completions.unwrap(); let completions = completions.unwrap();
@ -1031,7 +994,7 @@ mod tests {
}, },
}; };
let completions = module_registry let completions = module_registry
.get_completions("http://localhost:4545/x/", 24, &range, &state_snapshot) .get_completions("http://localhost:4545/x/", 24, &range, |_| false)
.await; .await;
assert!(completions.is_some()); assert!(completions.is_some());
let completions = completions.unwrap(); let completions = completions.unwrap();
@ -1049,12 +1012,7 @@ mod tests {
}, },
}; };
let completions = module_registry let completions = module_registry
.get_completions( .get_completions("http://localhost:4545/x/a@", 26, &range, |_| false)
"http://localhost:4545/x/a@",
26,
&range,
&state_snapshot,
)
.await; .await;
assert!(completions.is_some()); assert!(completions.is_some());
let completions = completions.unwrap(); let completions = completions.unwrap();
@ -1070,12 +1028,9 @@ mod tests {
}, },
}; };
let completions = module_registry let completions = module_registry
.get_completions( .get_completions("http://localhost:4545/x/a@v1.0.0/", 33, &range, |_| {
"http://localhost:4545/x/a@v1.0.0/", false
33, })
&range,
&state_snapshot,
)
.await; .await;
assert!(completions.is_some()); assert!(completions.is_some());
let completions = completions.unwrap(); let completions = completions.unwrap();
@ -1098,7 +1053,6 @@ mod tests {
.enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-key-first.json") .enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-key-first.json")
.await .await
.expect("could not enable"); .expect("could not enable");
let state_snapshot = setup(&[]);
let range = lsp::Range { let range = lsp::Range {
start: lsp::Position { start: lsp::Position {
line: 0, line: 0,
@ -1110,7 +1064,7 @@ mod tests {
}, },
}; };
let completions = module_registry let completions = module_registry
.get_completions("http://localhost:4545/", 22, &range, &state_snapshot) .get_completions("http://localhost:4545/", 22, &range, |_| false)
.await; .await;
assert!(completions.is_some()); assert!(completions.is_some());
let completions = completions.unwrap(); let completions = completions.unwrap();
@ -1139,12 +1093,7 @@ mod tests {
}, },
}; };
let completions = module_registry let completions = module_registry
.get_completions( .get_completions("http://localhost:4545/cde@", 26, &range, |_| false)
"http://localhost:4545/cde@",
26,
&range,
&state_snapshot,
)
.await; .await;
assert!(completions.is_some()); assert!(completions.is_some());
let completions = completions.unwrap(); let completions = completions.unwrap();
@ -1173,7 +1122,6 @@ mod tests {
.enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-complex.json") .enable_custom("http://localhost:4545/lsp/registries/deno-import-intellisense-complex.json")
.await .await
.expect("could not enable"); .expect("could not enable");
let state_snapshot = setup(&[]);
let range = lsp::Range { let range = lsp::Range {
start: lsp::Position { start: lsp::Position {
line: 0, line: 0,
@ -1185,7 +1133,7 @@ mod tests {
}, },
}; };
let completions = module_registry let completions = module_registry
.get_completions("http://localhost:4545/", 22, &range, &state_snapshot) .get_completions("http://localhost:4545/", 22, &range, |_| false)
.await; .await;
assert!(completions.is_some()); assert!(completions.is_some());
let completions = completions.unwrap(); let completions = completions.unwrap();

View file

@ -119,25 +119,58 @@ impl TsServer {
} }
} }
#[derive(Debug, Clone)]
struct AssetDocumentInner {
text: Arc<String>,
length: usize,
line_index: Arc<LineIndex>,
maybe_navigation_tree: Option<Arc<NavigationTree>>,
}
/// An lsp representation of an asset in memory, that has either been retrieved /// 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. /// from static assets built into Rust, or static assets built into tsc.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AssetDocument { pub struct AssetDocument(Arc<AssetDocumentInner>);
pub text: Arc<String>,
pub length: usize,
pub line_index: Arc<LineIndex>,
pub maybe_navigation_tree: Option<Arc<NavigationTree>>,
}
impl AssetDocument { impl AssetDocument {
pub fn new<T: AsRef<str>>(text: T) -> Self { pub fn new(text: impl AsRef<str>) -> Self {
let text = text.as_ref(); let text = text.as_ref();
Self { Self(Arc::new(AssetDocumentInner {
text: Arc::new(text.to_string()), text: Arc::new(text.to_string()),
length: text.encode_utf16().count(), length: text.encode_utf16().count(),
line_index: Arc::new(LineIndex::new(text)), line_index: Arc::new(LineIndex::new(text)),
maybe_navigation_tree: None, maybe_navigation_tree: None,
} }))
}
pub fn with_navigation_tree(
&self,
tree: Arc<NavigationTree>,
) -> AssetDocument {
AssetDocument(Arc::new(AssetDocumentInner {
maybe_navigation_tree: Some(tree),
..(*self.0).clone()
}))
}
pub fn text(&self) -> Arc<String> {
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<LineIndex> {
self.0.line_index.clone()
}
pub fn maybe_navigation_tree(&self) -> Option<Arc<NavigationTree>> {
self.0.maybe_navigation_tree.clone()
} }
} }
@ -176,14 +209,6 @@ impl Assets {
self.0.insert(k, v) self.0.insert(k, v)
} }
pub fn get_navigation_tree(
&self,
specifier: &ModuleSpecifier,
) -> Option<Arc<NavigationTree>> {
let doc = self.0.get(specifier).map(|v| v.as_ref()).flatten()?;
doc.maybe_navigation_tree.as_ref().cloned()
}
pub fn set_navigation_tree( pub fn set_navigation_tree(
&mut self, &mut self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
@ -196,7 +221,7 @@ impl Assets {
let doc = maybe_doc let doc = maybe_doc
.as_mut() .as_mut()
.ok_or_else(|| anyhow!("Cannot get doc mutable"))?; .ok_or_else(|| anyhow!("Cannot get doc mutable"))?;
doc.maybe_navigation_tree = Some(navigation_tree); *doc = doc.with_navigation_tree(navigation_tree);
Ok(()) Ok(())
} }
} }
@ -605,10 +630,11 @@ impl DocumentSpan {
language_server: &mut language_server::Inner, language_server: &mut language_server::Inner,
) -> Option<lsp::LocationLink> { ) -> Option<lsp::LocationLink> {
let target_specifier = normalize_specifier(&self.file_name).ok()?; let target_specifier = normalize_specifier(&self.file_name).ok()?;
let target_line_index = language_server let target_asset_or_doc = language_server
.get_line_index(target_specifier.clone()) .get_asset_or_document(&target_specifier)
.await .await
.ok()?; .ok()?;
let target_line_index = target_asset_or_doc.line_index();
let target_uri = language_server let target_uri = language_server
.url_map .url_map
.normalize_specifier(&target_specifier) .normalize_specifier(&target_specifier)
@ -854,6 +880,8 @@ impl RenameLocations {
for location in self.locations.iter() { for location in self.locations.iter() {
let specifier = normalize_specifier(&location.document_span.file_name)?; let specifier = normalize_specifier(&location.document_span.file_name)?;
let uri = language_server.url_map.normalize_specifier(&specifier)?; 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`. // ensure TextDocumentEdit for `location.file_name`.
if text_document_edit_map.get(&uri).is_none() { if text_document_edit_map.get(&uri).is_none() {
@ -862,7 +890,7 @@ impl RenameLocations {
lsp::TextDocumentEdit { lsp::TextDocumentEdit {
text_document: lsp::OptionalVersionedTextDocumentIdentifier { text_document: lsp::OptionalVersionedTextDocumentIdentifier {
uri: uri.clone(), uri: uri.clone(),
version: language_server.document_version(&specifier), version: asset_or_doc.document_version(),
}, },
edits: edits:
Vec::<lsp::OneOf<lsp::TextEdit, lsp::AnnotatedTextEdit>>::new(), Vec::<lsp::OneOf<lsp::TextEdit, lsp::AnnotatedTextEdit>>::new(),
@ -876,7 +904,7 @@ impl RenameLocations {
range: location range: location
.document_span .document_span
.text_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(), new_text: new_name.to_string(),
})); }));
} }
@ -1018,16 +1046,17 @@ impl FileTextChanges {
language_server: &mut language_server::Inner, language_server: &mut language_server::Inner,
) -> Result<lsp::TextDocumentEdit, AnyError> { ) -> Result<lsp::TextDocumentEdit, AnyError> {
let specifier = normalize_specifier(&self.file_name)?; 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 let edits = self
.text_changes .text_changes
.iter() .iter()
.map(|tc| tc.as_text_edit(line_index.clone())) .map(|tc| tc.as_text_edit(asset_or_doc.line_index()))
.collect(); .collect();
Ok(lsp::TextDocumentEdit { Ok(lsp::TextDocumentEdit {
text_document: lsp::OptionalVersionedTextDocumentIdentifier { text_document: lsp::OptionalVersionedTextDocumentIdentifier {
uri: specifier.clone(), uri: specifier.clone(),
version: language_server.document_version(&specifier), version: asset_or_doc.document_version(),
}, },
edits, edits,
}) })
@ -1039,11 +1068,17 @@ impl FileTextChanges {
) -> Result<Vec<lsp::DocumentChangeOperation>, AnyError> { ) -> Result<Vec<lsp::DocumentChangeOperation>, AnyError> {
let mut ops = Vec::<lsp::DocumentChangeOperation>::new(); let mut ops = Vec::<lsp::DocumentChangeOperation>::new();
let specifier = normalize_specifier(&self.file_name)?; let specifier = normalize_specifier(&self.file_name)?;
let line_index = if !self.is_new_file.unwrap_or(false) { let maybe_asset_or_document = if !self.is_new_file.unwrap_or(false) {
language_server.get_line_index(specifier.clone()).await? let asset_or_doc =
language_server.get_asset_or_document(&specifier).await?;
Some(asset_or_doc)
} else { } 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) { if self.is_new_file.unwrap_or(false) {
ops.push(lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create( ops.push(lsp::DocumentChangeOperation::Op(lsp::ResourceOp::Create(
@ -1066,7 +1101,9 @@ impl FileTextChanges {
ops.push(lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit { ops.push(lsp::DocumentChangeOperation::Edit(lsp::TextDocumentEdit {
text_document: lsp::OptionalVersionedTextDocumentIdentifier { text_document: lsp::OptionalVersionedTextDocumentIdentifier {
uri: specifier.clone(), uri: specifier.clone(),
version: language_server.document_version(&specifier), version: maybe_asset_or_document
.map(|d| d.document_version())
.flatten(),
}, },
edits, edits,
})); }));
@ -1354,13 +1391,13 @@ impl CallHierarchyItem {
maybe_root_path: Option<&Path>, maybe_root_path: Option<&Path>,
) -> Option<lsp::CallHierarchyItem> { ) -> Option<lsp::CallHierarchyItem> {
let target_specifier = normalize_specifier(&self.file).ok()?; let target_specifier = normalize_specifier(&self.file).ok()?;
let target_line_index = language_server let target_asset_or_doc = language_server
.get_line_index(target_specifier) .get_asset_or_document(&target_specifier)
.await .await
.ok()?; .ok()?;
Some(self.to_call_hierarchy_item( Some(self.to_call_hierarchy_item(
target_line_index, target_asset_or_doc.line_index(),
language_server, language_server,
maybe_root_path, maybe_root_path,
)) ))
@ -1455,21 +1492,21 @@ impl CallHierarchyIncomingCall {
maybe_root_path: Option<&Path>, maybe_root_path: Option<&Path>,
) -> Option<lsp::CallHierarchyIncomingCall> { ) -> Option<lsp::CallHierarchyIncomingCall> {
let target_specifier = normalize_specifier(&self.from.file).ok()?; let target_specifier = normalize_specifier(&self.from.file).ok()?;
let target_line_index = language_server let target_asset_or_doc = language_server
.get_line_index(target_specifier) .get_asset_or_document(&target_specifier)
.await .await
.ok()?; .ok()?;
Some(lsp::CallHierarchyIncomingCall { Some(lsp::CallHierarchyIncomingCall {
from: self.from.to_call_hierarchy_item( from: self.from.to_call_hierarchy_item(
target_line_index.clone(), target_asset_or_doc.line_index(),
language_server, language_server,
maybe_root_path, maybe_root_path,
), ),
from_ranges: self from_ranges: self
.from_spans .from_spans
.iter() .iter()
.map(|span| span.to_range(target_line_index.clone())) .map(|span| span.to_range(target_asset_or_doc.line_index()))
.collect(), .collect(),
}) })
} }
@ -1490,14 +1527,14 @@ impl CallHierarchyOutgoingCall {
maybe_root_path: Option<&Path>, maybe_root_path: Option<&Path>,
) -> Option<lsp::CallHierarchyOutgoingCall> { ) -> Option<lsp::CallHierarchyOutgoingCall> {
let target_specifier = normalize_specifier(&self.to.file).ok()?; let target_specifier = normalize_specifier(&self.to.file).ok()?;
let target_line_index = language_server let target_asset_or_doc = language_server
.get_line_index(target_specifier) .get_asset_or_document(&target_specifier)
.await .await
.ok()?; .ok()?;
Some(lsp::CallHierarchyOutgoingCall { Some(lsp::CallHierarchyOutgoingCall {
to: self.to.to_call_hierarchy_item( to: self.to.to_call_hierarchy_item(
target_line_index, target_asset_or_doc.line_index(),
language_server, language_server,
maybe_root_path, maybe_root_path,
), ),
@ -2099,10 +2136,11 @@ fn cache_snapshot(
let content = state let content = state
.state_snapshot .state_snapshot
.documents .documents
.content(specifier) .get(specifier)
.ok_or_else(|| { .ok_or_else(|| {
anyhow!("Specifier unexpectedly doesn't have content: {}", specifier) anyhow!("Specifier unexpectedly doesn't exist: {}", specifier)
})?; })?
.content();
state state
.snapshots .snapshots
.insert((specifier.clone(), version.into()), content.to_string()); .insert((specifier.clone(), version.into()), content.to_string());
@ -2242,7 +2280,7 @@ fn op_get_length(
let specifier = state.normalize_specifier(args.specifier)?; let specifier = state.normalize_specifier(args.specifier)?;
let r = if let Some(Some(asset)) = state.state_snapshot.assets.get(&specifier) let r = if let Some(Some(asset)) = state.state_snapshot.assets.get(&specifier)
{ {
Ok(asset.length) Ok(asset.length())
} else { } else {
cache_snapshot(state, &specifier, args.version.clone())?; cache_snapshot(state, &specifier, args.version.clone())?;
let content = state let content = state
@ -2275,7 +2313,7 @@ fn op_get_text(
let specifier = state.normalize_specifier(args.specifier)?; let specifier = state.normalize_specifier(args.specifier)?;
let content = let content =
if let Some(Some(content)) = state.state_snapshot.assets.get(&specifier) { if let Some(Some(content)) = state.state_snapshot.assets.get(&specifier) {
content.text.as_str() content.text_str()
} else { } else {
cache_snapshot(state, &specifier, args.version.clone())?; cache_snapshot(state, &specifier, args.version.clone())?;
state state
@ -2296,9 +2334,9 @@ fn op_load(
.performance .performance
.mark("op_load", Some(&args)); .mark("op_load", Some(&args));
let specifier = state.normalize_specifier(args.specifier)?; 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); state.state_snapshot.performance.measure(mark);
Ok(result.map(|t| t.to_string())) Ok(document.map(|d| d.content().to_string()))
} }
fn op_resolve( fn op_resolve(
@ -2347,7 +2385,15 @@ fn op_script_names(
state: &mut State, state: &mut State,
_args: Value, _args: Value,
) -> Result<Vec<ModuleSpecifier>, AnyError> { ) -> Result<Vec<ModuleSpecifier>, 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)] #[derive(Debug, Deserialize, Serialize)]
@ -2372,7 +2418,12 @@ fn op_script_version(
Ok(None) Ok(None)
} }
} else { } 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); state.state_snapshot.performance.measure(mark);