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,
language_server: &mut language_server::Inner,
) -> Result<lsp::CodeLens, AnyError> {
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<lsp::CodeLens, AnyError> {
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 {

View file

@ -114,9 +114,8 @@ pub(crate) async fn get_import_completions(
state_snapshot: &language_server::StateSnapshot,
client: lspower::Client,
) -> Option<lsp::CompletionResponse> {
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<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 =
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"
);
}

View file

@ -303,20 +303,20 @@ async fn generate_lint_diagnostics(
snapshot: &language_server::StateSnapshot,
collection: Arc<Mutex<DiagnosticCollection>>,
) -> Result<DiagnosticVec, AnyError> {
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;
}
}
}

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::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<Arc<LineIndex>, 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<AssetOrDocument> {
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<Arc<LineIndex>> {
) -> LspResult<Option<AssetOrDocument>> {
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())
let result = if specifier.scheme() == "asset" {
self.get_asset(specifier).await?.map(AssetOrDocument::Asset)
} else {
None
}
} 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<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" {
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,12 +258,9 @@ 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 {
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 {
let navigation_tree: tsc::NavigationTree = self
@ -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<i32> {
self.documents.lsp_version(specifier)
}
async fn get_asset(
&mut self,
specifier: &ModuleSpecifier,
) -> Result<Option<AssetDocument>, AnyError> {
) -> LspResult<Option<AssetDocument>> {
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(&params));
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<Option<Vec<TextEdit>>> {
let specifier = self.url_map.normalize_url(&params.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(&params));
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(&params));
let hover = if let Some((_, dep, range)) =
self.documents.get_maybe_dependency(
&specifier,
&params.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(&params.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(&params));
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(&params));
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(&params));
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(&params));
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(&params));
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(&params));
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) = &params.context {
context.trigger_character.clone()
} else {
@ -1801,15 +1747,8 @@ impl Inner {
}
let mark = self.performance.mark("goto_implementation", Some(&params));
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(&params));
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<tsc::OutliningSpan> = 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(&params));
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(&params));
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(&params));
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(&params));
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(&params));
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::<SelectionRange>::new();
for position in params.positions {
@ -2249,15 +2138,8 @@ impl Inner {
}
let mark = self.performance.mark("semantic_tokens_full", Some(&params));
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(&params));
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(&params));
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(&params.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::<Vec<_>>();
documents_specifiers.sort();
let measures = self.performance.to_vec();
let workspace_settings = self.config.get_workspace_settings();
@ -2743,28 +2616,16 @@ impl Inner {
}
Some(contents)
} else {
match specifier.scheme() {
"asset" => {
if let Some(asset) = self
.get_asset(&specifier)
let asset_or_doc = self
.get_maybe_asset_or_document(&specifier)
.await
.map_err(|_| LspError::internal_error())?
{
Some(asset.text.to_string())
.map_err(|_| LspError::internal_error())?;
if let Some(asset_or_doc) = asset_or_doc {
Some(asset_or_doc.text().to_string())
} else {
error!("Missing asset: {}", specifier);
error!("The source was not found: {}", 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);
Ok(contents)

View file

@ -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<Vec<lsp::CompletionItem>> {
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();

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
/// from static assets built into Rust, or static assets built into tsc.
#[derive(Debug, Clone)]
pub struct AssetDocument {
pub text: Arc<String>,
pub length: usize,
pub line_index: Arc<LineIndex>,
pub maybe_navigation_tree: Option<Arc<NavigationTree>>,
}
pub struct AssetDocument(Arc<AssetDocumentInner>);
impl AssetDocument {
pub fn new<T: AsRef<str>>(text: T) -> Self {
pub fn new(text: impl AsRef<str>) -> 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<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)
}
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(
&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<lsp::LocationLink> {
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::<lsp::OneOf<lsp::TextEdit, lsp::AnnotatedTextEdit>>::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<lsp::TextDocumentEdit, AnyError> {
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<Vec<lsp::DocumentChangeOperation>, AnyError> {
let mut ops = Vec::<lsp::DocumentChangeOperation>::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<lsp::CallHierarchyItem> {
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<lsp::CallHierarchyIncomingCall> {
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<lsp::CallHierarchyOutgoingCall> {
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<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)]
@ -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);