summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2021-11-12 11:42:04 -0500
committerGitHub <noreply@github.com>2021-11-12 11:42:04 -0500
commit28dbb4a95e569772600ae28ae4fe8b6fa2a4d2af (patch)
tree328904cc938b6c3bb3ce9b63d325115284cb5aed /cli
parentfdf890a68d3d54d40c766fd78faeccb20bd2e2c6 (diff)
refactor(lsp): prefer using document instead of documents collection (#12720)
Diffstat (limited to 'cli')
-rw-r--r--cli/lsp/code_lens.rs21
-rw-r--r--cli/lsp/completions.rs18
-rw-r--r--cli/lsp/diagnostics.rs85
-rw-r--r--cli/lsp/documents.rs705
-rw-r--r--cli/lsp/language_server.rs493
-rw-r--r--cli/lsp/registries.rs82
-rw-r--r--cli/lsp/tsc.rs147
7 files changed, 691 insertions, 860 deletions
diff --git a/cli/lsp/code_lens.rs b/cli/lsp/code_lens.rs
index 97ad8f260..75f46dba8 100644
--- a/cli/lsp/code_lens.rs
+++ b/cli/lsp/code_lens.rs
@@ -220,9 +220,9 @@ async fn resolve_implementation_code_lens(
data: CodeLensData,
language_server: &mut language_server::Inner,
) -> Result<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 {
diff --git a/cli/lsp/completions.rs b/cli/lsp/completions.rs
index 601ebb52b..07857fb48 100644
--- a/cli/lsp/completions.rs
+++ b/cli/lsp/completions.rs
@@ -114,9 +114,8 @@ pub(crate) async fn get_import_completions(
state_snapshot: &language_server::StateSnapshot,
client: lspower::Client,
) -> Option<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"
);
}
diff --git a/cli/lsp/diagnostics.rs b/cli/lsp/diagnostics.rs
index fdbf8b27b..f14d80cd8 100644
--- a/cli/lsp/diagnostics.rs
+++ b/cli/lsp/diagnostics.rs
@@ -303,20 +303,20 @@ async fn generate_lint_diagnostics(
snapshot: &language_server::StateSnapshot,
collection: Arc<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;
}
}
}
@@ -678,7 +697,7 @@ mod tests {
let (snapshot, collection, _) = setup(&[(
"file:///a.ts",
r#"import * as b from "./b.ts";
-
+
let a = "a";
console.log(a);
"#,
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index ce7e4e36f..9892eab1f 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -2,6 +2,7 @@
use super::text::LineIndex;
use super::tsc;
+use super::tsc::AssetDocument;
use crate::config_file::ConfigFile;
use crate::file_fetcher::get_source_from_bytes;
@@ -134,6 +135,62 @@ impl IndexValid {
}
}
+#[derive(Debug, Clone)]
+pub(crate) enum AssetOrDocument {
+ Document(Document),
+ Asset(AssetDocument),
+}
+
+impl AssetOrDocument {
+ pub fn document(&self) -> Option<&Document> {
+ match self {
+ AssetOrDocument::Asset(_) => None,
+ AssetOrDocument::Document(doc) => Some(doc),
+ }
+ }
+
+ pub fn text(&self) -> Arc<String> {
+ match self {
+ AssetOrDocument::Asset(a) => a.text(),
+ AssetOrDocument::Document(d) => d.0.text_info.text(),
+ }
+ }
+
+ pub fn line_index(&self) -> Arc<LineIndex> {
+ match self {
+ AssetOrDocument::Asset(a) => a.line_index(),
+ AssetOrDocument::Document(d) => d.line_index(),
+ }
+ }
+
+ pub fn maybe_navigation_tree(&self) -> Option<Arc<tsc::NavigationTree>> {
+ match self {
+ AssetOrDocument::Asset(a) => a.maybe_navigation_tree(),
+ AssetOrDocument::Document(d) => d.maybe_navigation_tree(),
+ }
+ }
+
+ pub fn get_maybe_dependency(
+ &self,
+ position: &lsp::Position,
+ ) -> Option<(String, deno_graph::Dependency, deno_graph::Range)> {
+ self
+ .document()
+ .map(|d| d.get_maybe_dependency(position))
+ .flatten()
+ }
+
+ pub fn maybe_parsed_source(
+ &self,
+ ) -> Option<Result<deno_ast::ParsedSource, deno_graph::ModuleGraphError>> {
+ self.document().map(|d| d.maybe_parsed_source()).flatten()
+ }
+
+ pub fn document_version(&self) -> Option<i32> {
+ self.document().map(|d| d.maybe_lsp_version()).flatten()
+ }
+}
+
// TODO(@kitsonk) expose the synthetic module from deno_graph
#[derive(Debug)]
struct SyntheticModule {
@@ -186,9 +243,9 @@ impl SyntheticModule {
}
}
}
-
-#[derive(Debug)]
-pub(crate) struct Document {
+#[derive(Debug, Clone)]
+struct DocumentInner {
+ fs_version: String,
line_index: Arc<LineIndex>,
maybe_language_id: Option<LanguageId>,
maybe_lsp_version: Option<i32>,
@@ -196,15 +253,17 @@ pub(crate) struct Document {
Option<Result<deno_graph::Module, deno_graph::ModuleGraphError>>,
maybe_navigation_tree: Option<Arc<tsc::NavigationTree>>,
maybe_warning: Option<String>,
- source: SourceTextInfo,
specifier: ModuleSpecifier,
- version: String,
+ text_info: SourceTextInfo,
}
+#[derive(Debug, Clone)]
+pub(crate) struct Document(Arc<DocumentInner>);
+
impl Document {
fn new(
specifier: ModuleSpecifier,
- version: String,
+ fs_version: String,
maybe_headers: Option<&HashMap<String, String>>,
content: Arc<String>,
maybe_resolver: Option<&dyn deno_graph::source::Resolver>,
@@ -223,29 +282,64 @@ impl Document {
maybe_resolver,
Some(&parser),
));
- let source = SourceTextInfo::new(content);
- let line_index = Arc::new(LineIndex::new(source.text_str()));
- Self {
+ let text_info = SourceTextInfo::new(content);
+ let line_index = Arc::new(LineIndex::new(text_info.text_str()));
+ Self(Arc::new(DocumentInner {
+ fs_version,
line_index,
maybe_language_id: None,
maybe_lsp_version: None,
maybe_module,
maybe_navigation_tree: None,
maybe_warning,
- source,
+ text_info,
specifier,
- version,
- }
+ }))
}
- fn change(
- &mut self,
+ fn open(
+ specifier: ModuleSpecifier,
+ version: i32,
+ language_id: LanguageId,
+ content: Arc<String>,
+ maybe_resolver: Option<&dyn deno_graph::source::Resolver>,
+ ) -> Self {
+ let maybe_headers = language_id.as_headers();
+ let parser = SourceParser::default();
+ let maybe_module = if language_id.is_diagnosable() {
+ Some(deno_graph::parse_module(
+ &specifier,
+ maybe_headers,
+ content.clone(),
+ maybe_resolver,
+ Some(&parser),
+ ))
+ } else {
+ None
+ };
+ let source = SourceTextInfo::new(content);
+ let line_index = Arc::new(LineIndex::new(source.text_str()));
+ Self(Arc::new(DocumentInner {
+ fs_version: "1".to_string(),
+ line_index,
+ maybe_language_id: Some(language_id),
+ maybe_lsp_version: Some(version),
+ maybe_module,
+ maybe_navigation_tree: None,
+ maybe_warning: None,
+ text_info: source,
+ specifier,
+ }))
+ }
+
+ fn with_change(
+ &self,
version: i32,
changes: Vec<lsp::TextDocumentContentChangeEvent>,
maybe_resolver: Option<&dyn deno_graph::source::Resolver>,
- ) -> Result<(), AnyError> {
- let mut content = self.source.text_str().to_string();
- let mut line_index = self.line_index.clone();
+ ) -> Result<Document, AnyError> {
+ let mut content = self.0.text_info.text_str().to_string();
+ let mut line_index = self.0.line_index.clone();
let mut index_valid = IndexValid::All;
for change in changes {
if let Some(range) = change.range {
@@ -261,49 +355,91 @@ impl Document {
}
}
let content = Arc::new(content);
- if self
+ let maybe_module = if self
+ .0
.maybe_language_id
.as_ref()
.map(|li| li.is_diagnosable())
.unwrap_or(false)
{
let maybe_headers = self
+ .0
.maybe_language_id
.as_ref()
.map(|li| li.as_headers())
.flatten();
let parser = SourceParser::default();
- self.maybe_module = Some(deno_graph::parse_module(
- &self.specifier,
+ Some(deno_graph::parse_module(
+ &self.0.specifier,
maybe_headers,
content.clone(),
maybe_resolver,
Some(&parser),
- ));
+ ))
} else {
- self.maybe_module = None;
- }
- self.source = SourceTextInfo::new(content);
- self.line_index = if index_valid == IndexValid::All {
+ None
+ };
+ let source = SourceTextInfo::new(content);
+ let line_index = if index_valid == IndexValid::All {
line_index
} else {
- Arc::new(LineIndex::new(self.source.text_str()))
+ Arc::new(LineIndex::new(source.text_str()))
};
- self.maybe_lsp_version = Some(version);
- self.maybe_navigation_tree = None;
- Ok(())
+ Ok(Document(Arc::new(DocumentInner {
+ text_info: source,
+ line_index,
+ maybe_module,
+ maybe_lsp_version: Some(version),
+ maybe_navigation_tree: None,
+ ..(*self.0).clone()
+ })))
+ }
+
+ fn with_closed(&self) -> Document {
+ Document(Arc::new(DocumentInner {
+ maybe_lsp_version: None,
+ maybe_language_id: None,
+ ..(*self.0).clone()
+ }))
+ }
+
+ fn with_navigation_tree(
+ &self,
+ navigation_tree: Arc<tsc::NavigationTree>,
+ ) -> Document {
+ Document(Arc::new(DocumentInner {
+ maybe_navigation_tree: Some(navigation_tree),
+ ..(*self.0).clone()
+ }))
}
- fn close(&mut self) {
- self.maybe_lsp_version = None;
- self.maybe_language_id = None;
+ pub fn specifier(&self) -> &ModuleSpecifier {
+ &self.0.specifier
}
- fn content(&self) -> Arc<String> {
- self.source.text()
+ pub fn content(&self) -> Arc<String> {
+ self.0.text_info.text()
}
- fn is_diagnosable(&self) -> bool {
+ pub fn text_info(&self) -> SourceTextInfo {
+ self.0.text_info.clone()
+ }
+
+ pub fn line_index(&self) -> Arc<LineIndex> {
+ self.0.line_index.clone()
+ }
+
+ fn fs_version(&self) -> &str {
+ self.0.fs_version.as_str()
+ }
+
+ pub fn script_version(&self) -> String {
+ self
+ .maybe_lsp_version()
+ .map_or_else(|| self.fs_version().to_string(), |v| v.to_string())
+ }
+
+ pub fn is_diagnosable(&self) -> bool {
matches!(
self.media_type(),
// todo(#12410): Update with new media types for TS 4.5
@@ -315,58 +451,82 @@ impl Document {
)
}
- fn is_open(&self) -> bool {
- self.maybe_lsp_version.is_some()
+ pub fn is_open(&self) -> bool {
+ self.0.maybe_lsp_version.is_some()
}
- fn maybe_types_dependency(&self) -> deno_graph::Resolved {
- let module_result = self.maybe_module.as_ref()?;
+ pub fn maybe_types_dependency(&self) -> deno_graph::Resolved {
+ let module_result = self.0.maybe_module.as_ref()?;
let module = module_result.as_ref().ok()?;
let (_, maybe_dep) = module.maybe_types_dependency.as_ref()?;
maybe_dep.clone()
}
- fn media_type(&self) -> MediaType {
- if let Some(Ok(module)) = &self.maybe_module {
+ pub fn media_type(&self) -> MediaType {
+ if let Some(Ok(module)) = &self.0.maybe_module {
module.media_type
} else {
- MediaType::from(&self.specifier)
+ MediaType::from(&self.0.specifier)
}
}
- fn open(
- specifier: ModuleSpecifier,
- version: i32,
- language_id: LanguageId,
- content: Arc<String>,
- maybe_resolver: Option<&dyn deno_graph::source::Resolver>,
- ) -> Self {
- let maybe_headers = language_id.as_headers();
- let parser = SourceParser::default();
- let maybe_module = if language_id.is_diagnosable() {
- Some(deno_graph::parse_module(
- &specifier,
- maybe_headers,
- content.clone(),
- maybe_resolver,
- Some(&parser),
- ))
- } else {
- None
+ /// Returns the current language server client version if any.
+ pub fn maybe_lsp_version(&self) -> Option<i32> {
+ self.0.maybe_lsp_version
+ }
+
+ fn maybe_module(
+ &self,
+ ) -> Option<&Result<deno_graph::Module, deno_graph::ModuleGraphError>> {
+ self.0.maybe_module.as_ref()
+ }
+
+ pub fn maybe_parsed_source(
+ &self,
+ ) -> Option<Result<deno_ast::ParsedSource, deno_graph::ModuleGraphError>> {
+ self.maybe_module().map(|r| {
+ r.as_ref()
+ .map(|m| m.parsed_source.clone())
+ .map_err(|err| err.clone())
+ })
+ }
+
+ pub fn maybe_navigation_tree(&self) -> Option<Arc<tsc::NavigationTree>> {
+ self.0.maybe_navigation_tree.clone()
+ }
+
+ pub fn maybe_warning(&self) -> Option<String> {
+ self.0.maybe_warning.clone()
+ }
+
+ pub fn dependencies(&self) -> Option<Vec<(String, deno_graph::Dependency)>> {
+ let module = self.maybe_module()?.as_ref().ok()?;
+ Some(
+ module
+ .dependencies
+ .iter()
+ .map(|(s, d)| (s.clone(), d.clone()))
+ .collect(),
+ )
+ }
+
+ /// If the supplied position is within a dependency range, return the resolved
+ /// string specifier for the dependency, the resolved dependency and the range
+ /// in the source document of the specifier.
+ pub fn get_maybe_dependency(
+ &self,
+ position: &lsp::Position,
+ ) -> Option<(String, deno_graph::Dependency, deno_graph::Range)> {
+ let module = self.maybe_module()?.as_ref().ok()?;
+ let position = deno_graph::Position {
+ line: position.line as usize,
+ character: position.character as usize,
};
- let source = SourceTextInfo::new(content);
- let line_index = Arc::new(LineIndex::new(source.text_str()));
- Self {
- line_index,
- maybe_language_id: Some(language_id),
- maybe_lsp_version: Some(version),
- maybe_module,
- maybe_navigation_tree: None,
- maybe_warning: None,
- source,
- specifier,
- version: "1".to_string(),
- }
+ module.dependencies.iter().find_map(|(s, dep)| {
+ dep
+ .includes(&position)
+ .map(|r| (s.clone(), dep.clone(), r.clone()))
+ })
}
}
@@ -446,7 +606,7 @@ fn recurse_dependents(
}
#[derive(Debug, Default)]
-struct Inner {
+struct DocumentsInner {
/// The DENO_DIR that the documents looks for non-file based modules.
cache: HttpCache,
/// A flag that indicates that stated data is potentially invalid and needs to
@@ -468,7 +628,7 @@ struct Inner {
redirects: HashMap<ModuleSpecifier, ModuleSpecifier>,
}
-impl Inner {
+impl DocumentsInner {
fn new(location: &Path) -> Self {
Self {
cache: HttpCache::new(location),
@@ -484,7 +644,7 @@ impl Inner {
/// Adds a document by reading the document from the file system.
fn add(&mut self, specifier: ModuleSpecifier) -> Option<Document> {
- let version = self.calculate_version(&specifier)?;
+ let fs_version = self.calculate_fs_version(&specifier)?;
let path = self.get_path(&specifier)?;
let bytes = fs::read(path).ok()?;
let doc = if specifier.scheme() == "file" {
@@ -493,7 +653,7 @@ impl Inner {
let content = Arc::new(get_source_from_bytes(bytes, maybe_charset).ok()?);
Document::new(
specifier.clone(),
- version,
+ fs_version,
None,
content,
self.get_maybe_resolver(),
@@ -507,7 +667,7 @@ impl Inner {
let content = Arc::new(get_source_from_bytes(bytes, maybe_charset).ok()?);
Document::new(
specifier.clone(),
- version,
+ fs_version,
maybe_headers,
content,
self.get_maybe_resolver(),
@@ -524,7 +684,7 @@ impl Inner {
let mut dependents_map: HashMap<ModuleSpecifier, HashSet<ModuleSpecifier>> =
HashMap::new();
for (specifier, doc) in &self.docs {
- if let Some(Ok(module)) = &doc.maybe_module {
+ if let Some(Ok(module)) = doc.maybe_module() {
for dependency in module.dependencies.values() {
if let Some(dep) = dependency.get_code() {
dependents_map
@@ -550,7 +710,10 @@ impl Inner {
self.dependents_map = dependents_map;
}
- fn calculate_version(&self, specifier: &ModuleSpecifier) -> Option<String> {
+ fn calculate_fs_version(
+ &self,
+ specifier: &ModuleSpecifier,
+ ) -> Option<String> {
let path = self.get_path(specifier)?;
let metadata = fs::metadata(path).ok()?;
if let Ok(modified) = metadata.modified() {
@@ -569,16 +732,8 @@ impl Inner {
specifier: &ModuleSpecifier,
version: i32,
changes: Vec<lsp::TextDocumentContentChangeEvent>,
- ) -> Result<(), AnyError> {
- // this duplicates the .get_resolver() method, because there is no easy
- // way to avoid the double borrow of self that occurs here with getting the
- // mut doc out.
- let maybe_resolver = if self.maybe_jsx_resolver.is_some() {
- self.maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver())
- } else {
- self.maybe_import_map.as_ref().map(|im| im.as_resolver())
- };
- let doc = self.docs.get_mut(specifier).map_or_else(
+ ) -> Result<Document, AnyError> {
+ let doc = self.docs.get(specifier).map_or_else(
|| {
Err(custom_error(
"NotFound",
@@ -588,7 +743,9 @@ impl Inner {
Ok,
)?;
self.dirty = true;
- doc.change(version, changes, maybe_resolver)
+ let doc = doc.with_change(version, changes, self.get_maybe_resolver())?;
+ self.docs.insert(doc.specifier().clone(), doc.clone());
+ Ok(doc)
}
fn close(&mut self, specifier: &ModuleSpecifier) -> Result<(), AnyError> {
@@ -601,7 +758,7 @@ impl Inner {
},
Ok,
)?;
- doc.close();
+ *doc = doc.with_closed();
self.dirty = true;
Ok(())
}
@@ -634,25 +791,6 @@ impl Inner {
self.docs.contains_key(&specifier)
}
- fn content(&mut self, specifier: &ModuleSpecifier) -> Option<Arc<String>> {
- self.get(specifier).map(|d| d.content())
- }
-
- fn dependencies(
- &mut self,
- specifier: &ModuleSpecifier,
- ) -> Option<Vec<(String, deno_graph::Dependency)>> {
- let doc = self.get(specifier)?;
- let module = doc.maybe_module.as_ref()?.as_ref().ok()?;
- Some(
- module
- .dependencies
- .iter()
- .map(|(s, d)| (s.clone(), d.clone()))
- .collect(),
- )
- }
-
fn dependents(
&mut self,
specifier: &ModuleSpecifier,
@@ -678,22 +816,13 @@ impl Inner {
self.docs.get(&specifier)
}
- fn get_maybe_dependency(
- &mut self,
- specifier: &ModuleSpecifier,
- position: &lsp::Position,
- ) -> Option<(String, deno_graph::Dependency, deno_graph::Range)> {
- let doc = self.get(specifier)?;
- let module = doc.maybe_module.as_ref()?.as_ref().ok()?;
- let position = deno_graph::Position {
- line: position.line as usize,
- character: position.character as usize,
- };
- module.dependencies.iter().find_map(|(s, dep)| {
- dep
- .includes(&position)
- .map(|r| (s.clone(), dep.clone(), r.clone()))
- })
+ fn get_cached(&mut self, specifier: &ModuleSpecifier) -> Option<&Document> {
+ let specifier = self
+ .resolve_specifier(specifier)
+ .unwrap_or_else(|| specifier.clone());
+ // this does not use `self.get` since that lazily adds documents, and we
+ // only care about documents already in the cache.
+ self.docs.get(&specifier)
}
fn get_maybe_resolver(&self) -> Option<&dyn deno_graph::source::Resolver> {
@@ -704,26 +833,6 @@ impl Inner {
}
}
- fn get_maybe_types_for_dependency(
- &mut self,
- dependency: &deno_graph::Dependency,
- ) -> deno_graph::Resolved {
- let code_dep = dependency.maybe_code.as_ref()?;
- let (specifier, _) = code_dep.as_ref().ok()?;
- let doc = self.get(specifier)?;
- doc.maybe_types_dependency()
- }
-
- fn get_navigation_tree(
- &mut self,
- specifier: &ModuleSpecifier,
- ) -> Option<Arc<tsc::NavigationTree>> {
- self
- .get(specifier)
- .map(|d| d.maybe_navigation_tree.clone())
- .flatten()
- }
-
fn get_path(&self, specifier: &ModuleSpecifier) -> Option<PathBuf> {
if specifier.scheme() == "file" {
specifier.to_file_path().ok()
@@ -737,38 +846,19 @@ impl Inner {
}
}
- fn is_diagnosable(&mut self, specifier: &ModuleSpecifier) -> bool {
- if let Some(doc) = self.get(specifier) {
- doc.is_diagnosable()
- } else {
- false
- }
- }
-
- fn is_formattable(&mut self, specifier: &ModuleSpecifier) -> bool {
- // currently any document that is open in the language server is formattable
- self.is_open(specifier)
- }
-
- fn is_open(&mut self, specifier: &ModuleSpecifier) -> bool {
- let specifier = self
- .resolve_specifier(specifier)
- .unwrap_or_else(|| specifier.clone());
- // this does not use `self.get` since that lazily adds documents, and we
- // only care about documents already in the cache.
- if let Some(doc) = self.docs.get(&specifier) {
- doc.is_open()
- } else {
- false
- }
- }
-
fn is_valid(&mut self, specifier: &ModuleSpecifier) -> bool {
- if self.is_open(specifier) {
+ if self
+ .get_cached(specifier)
+ .map(|d| d.is_open())
+ .unwrap_or(false)
+ {
true
} else if let Some(specifier) = self.resolve_specifier(specifier) {
- self.docs.get(&specifier).map(|d| d.version.clone())
- == self.calculate_version(&specifier)
+ self
+ .docs
+ .get(&specifier)
+ .map(|d| d.fs_version().to_string())
+ == self.calculate_fs_version(&specifier)
} else {
// even though it isn't valid, it just can't exist, so we will say it is
// valid
@@ -776,62 +866,44 @@ impl Inner {
}
}
- fn line_index(
- &mut self,
- specifier: &ModuleSpecifier,
- ) -> Option<Arc<LineIndex>> {
- let specifier = self.resolve_specifier(specifier)?;
- self.docs.get(&specifier).map(|doc| doc.line_index.clone())
- }
-
- fn lsp_version(&self, specifier: &ModuleSpecifier) -> Option<i32> {
- self
- .docs
- .get(specifier)
- .map(|doc| doc.maybe_lsp_version)
- .flatten()
- }
-
- fn maybe_warning(&mut self, specifier: &ModuleSpecifier) -> Option<String> {
- self
- .get(specifier)
- .map(|d| d.maybe_warning.clone())
- .flatten()
- }
-
fn open(
&mut self,
specifier: ModuleSpecifier,
version: i32,
language_id: LanguageId,
content: Arc<String>,
- ) {
+ ) -> Document {
let maybe_resolver = self.get_maybe_resolver();
- let document_data = Document::open(
+ let document = Document::open(
specifier.clone(),
version,
language_id,
content,
maybe_resolver,
);
- self.docs.insert(specifier, document_data);
+ self.docs.insert(specifier, document.clone());
self.dirty = true;
+ document
}
- fn parsed_source(
- &mut self,
- specifier: &ModuleSpecifier,
- ) -> Option<Result<deno_ast::ParsedSource, deno_graph::ModuleGraphError>> {
+ fn documents(
+ &self,
+ open_only: bool,
+ diagnosable_only: bool,
+ ) -> Vec<Document> {
self
- .get(specifier)
- .map(|doc| {
- doc.maybe_module.as_ref().map(|r| {
- r.as_ref()
- .map(|m| m.parsed_source.clone())
- .map_err(|err| err.clone())
- })
+ .docs
+ .values()
+ .filter_map(|doc| {
+ let open = open_only && doc.is_open();
+ let diagnosable = diagnosable_only && doc.is_diagnosable();
+ if (!open_only || open) && (!diagnosable_only || diagnosable) {
+ Some(doc.clone())
+ } else {
+ None
+ }
})
- .flatten()
+ .collect()
}
fn resolve(
@@ -841,7 +913,7 @@ impl Inner {
) -> Option<Vec<Option<(ModuleSpecifier, MediaType)>>> {
let doc = self.get(referrer)?;
let mut results = Vec::new();
- if let Some(Ok(module)) = &doc.maybe_module {
+ if let Some(Ok(module)) = doc.maybe_module() {
let dependencies = module.dependencies.clone();
for specifier in specifiers {
if specifier.starts_with("asset:") {
@@ -878,8 +950,7 @@ impl Inner {
specifier: &ModuleSpecifier,
) -> Option<(ModuleSpecifier, MediaType)> {
let doc = self.get(specifier)?;
- let maybe_module =
- doc.maybe_module.as_ref().map(|r| r.as_ref().ok()).flatten();
+ let maybe_module = doc.maybe_module().map(|r| r.as_ref().ok()).flatten();
let maybe_types_dependency = maybe_module
.map(|m| {
m.maybe_types_dependency
@@ -969,37 +1040,10 @@ impl Inner {
let doc = self.docs.get_mut(specifier).ok_or_else(|| {
custom_error("NotFound", format!("Specifier not found {}", specifier))
})?;
- doc.maybe_navigation_tree = Some(navigation_tree);
+ *doc = doc.with_navigation_tree(navigation_tree);
Ok(())
}
- fn specifiers(
- &self,
- open_only: bool,
- diagnosable_only: bool,
- ) -> Vec<ModuleSpecifier> {
- self
- .docs
- .iter()
- .filter_map(|(specifier, doc)| {
- let open = open_only && doc.is_open();
- let diagnosable = diagnosable_only && doc.is_diagnosable();
- if (!open_only || open) && (!diagnosable_only || diagnosable) {
- Some(specifier.clone())
- } else {
- None
- }
- })
- .collect()
- }
-
- fn text_info(
- &mut self,
- specifier: &ModuleSpecifier,
- ) -> Option<SourceTextInfo> {
- self.get(specifier).map(|d| d.source.clone())
- }
-
fn update_config(
&mut self,
maybe_import_map: Option<Arc<import_map::ImportMap>>,
@@ -1029,21 +1073,28 @@ impl Inner {
}
self.dirty = true;
}
-
- fn version(&mut self, specifier: &ModuleSpecifier) -> Option<String> {
- self.get(specifier).map(|d| {
- d.maybe_lsp_version
- .map_or_else(|| d.version.clone(), |v| v.to_string())
- })
- }
}
#[derive(Debug, Clone, Default)]
-pub(crate) struct Documents(Arc<Mutex<Inner>>);
+pub(crate) struct Documents(Arc<Mutex<DocumentsInner>>);
impl Documents {
pub fn new(location: &Path) -> Self {
- Self(Arc::new(Mutex::new(Inner::new(location))))
+ Self(Arc::new(Mutex::new(DocumentsInner::new(location))))
+ }
+
+ /// "Open" a document from the perspective of the editor, meaning that
+ /// requests for information from the document will come from the in-memory
+ /// representation received from the language server client, versus reading
+ /// information from the disk.
+ pub fn open(
+ &self,
+ specifier: ModuleSpecifier,
+ version: i32,
+ language_id: LanguageId,
+ content: Arc<String>,
+ ) -> Document {
+ self.0.lock().open(specifier, version, language_id, content)
}
/// Apply language server content changes to an open document.
@@ -1052,7 +1103,7 @@ impl Documents {
specifier: &ModuleSpecifier,
version: i32,
changes: Vec<lsp::TextDocumentContentChangeEvent>,
- ) -> Result<(), AnyError> {
+ ) -> Result<Document, AnyError> {
self.0.lock().change(specifier, version, changes)
}
@@ -1078,20 +1129,6 @@ impl Documents {
self.0.lock().contains_specifier(specifier)
}
- /// If the specifier can be resolved to a document, return its current
- /// content, otherwise none.
- pub fn content(&self, specifier: &ModuleSpecifier) -> Option<Arc<String>> {
- self.0.lock().content(specifier)
- }
-
- /// Return an optional vector of dependencies for a given specifier.
- pub fn dependencies(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<Vec<(String, deno_graph::Dependency)>> {
- self.0.lock().dependencies(specifier)
- }
-
/// Return an array of specifiers, if any, that are dependent upon the
/// supplied specifier. This is used to determine invalidation of diagnostics
/// when a module has been changed.
@@ -1102,85 +1139,21 @@ impl Documents {
self.0.lock().dependents(specifier)
}
- /// If the supplied position is within a dependency range, return the resolved
- /// string specifier for the dependency, the resolved dependency and the range
- /// in the source document of the specifier.
- pub fn get_maybe_dependency(
- &self,
- specifier: &ModuleSpecifier,
- position: &lsp::Position,
- ) -> Option<(String, deno_graph::Dependency, deno_graph::Range)> {
- self.0.lock().get_maybe_dependency(specifier, position)
- }
-
- /// For a given dependency, try to resolve the maybe_types_dependency for the
- /// dependency. This covers modules that assert their own types, like via the
- /// triple-slash reference, or the `X-TypeScript-Types` header.
- pub fn get_maybe_types_for_dependency(
- &self,
- dependency: &deno_graph::Dependency,
- ) -> deno_graph::Resolved {
- self.0.lock().get_maybe_types_for_dependency(dependency)
- }
-
- /// Get a reference to the navigation tree stored for a given specifier, if
- /// any.
- pub fn get_navigation_tree(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<Arc<tsc::NavigationTree>> {
- self.0.lock().get_navigation_tree(specifier)
- }
-
- /// Indicates that a specifier is able to be diagnosed by the language server
- pub fn is_diagnosable(&self, specifier: &ModuleSpecifier) -> bool {
- self.0.lock().is_diagnosable(specifier)
- }
-
- /// Indicates that a specifier is formattable.
- pub fn is_formattable(&self, specifier: &ModuleSpecifier) -> bool {
- self.0.lock().is_formattable(specifier)
- }
-
- /// Return a reference to the line index for a given specifiers, if any.
- pub fn line_index(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<Arc<LineIndex>> {
- self.0.lock().line_index(specifier)
- }
-
- /// Return the current language server client version for a given specifier,
- /// if any.
- pub fn lsp_version(&self, specifier: &ModuleSpecifier) -> Option<i32> {
- self.0.lock().lsp_version(specifier)
- }
-
- /// Return a warning header for a given specifier, if present.
- pub fn maybe_warning(&self, specifier: &ModuleSpecifier) -> Option<String> {
- self.0.lock().maybe_warning(specifier)
- }
-
- /// "Open" a document from the perspective of the editor, meaning that
- /// requests for information from the document will come from the in-memory
- /// representation received from the language server client, versus reading
- /// information from the disk.
- pub fn open(
+ /// Return a vector of documents that are contained in the document store,
+ /// where `open_only` flag would provide only those documents currently open
+ /// in the editor and `diagnosable_only` would provide only those documents
+ /// that the language server can provide diagnostics for.
+ pub fn documents(
&self,
- specifier: ModuleSpecifier,
- version: i32,
- language_id: LanguageId,
- content: Arc<String>,
- ) {
- self.0.lock().open(specifier, version, language_id, content)
+ open_only: bool,
+ diagnosable_only: bool,
+ ) -> Vec<Document> {
+ self.0.lock().documents(open_only, diagnosable_only)
}
- /// Return the parsed source or the module graph error for a given specifier.
- pub fn parsed_source(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<Result<deno_ast::ParsedSource, deno_graph::ModuleGraphError>> {
- self.0.lock().parsed_source(specifier)
+ /// Return a document for the specifier.
+ pub fn get(&self, specifier: &ModuleSpecifier) -> Option<Document> {
+ self.0.lock().get(specifier).cloned()
}
/// For a given set of string specifiers, resolve each one from the graph,
@@ -1211,26 +1184,6 @@ impl Documents {
.set_navigation_tree(specifier, navigation_tree)
}
- /// Return a vector of specifiers that are contained in the document store,
- /// where `open_only` flag would provide only those documents currently open
- /// in the editor and `diagnosable_only` would provide only those documents
- /// that the language server can provide diagnostics for.
- pub fn specifiers(
- &self,
- open_only: bool,
- diagnosable_only: bool,
- ) -> Vec<ModuleSpecifier> {
- self.0.lock().specifiers(open_only, diagnosable_only)
- }
-
- /// Return the current text info for a given specifier.
- pub fn text_info(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<SourceTextInfo> {
- self.0.lock().text_info(specifier)
- }
-
pub fn update_config(
&self,
maybe_import_map: Option<Arc<import_map::ImportMap>>,
@@ -1241,11 +1194,6 @@ impl Documents {
.lock()
.update_config(maybe_import_map, maybe_config_file)
}
-
- /// Return the version of a document in the document cache.
- pub fn version(&self, specifier: &ModuleSpecifier) -> Option<String> {
- self.0.lock().version(specifier)
- }
}
#[cfg(test)]
@@ -1270,15 +1218,10 @@ console.log(b);
"#
.to_string(),
);
- documents.open(
- specifier.clone(),
- 1,
- "javascript".parse().unwrap(),
- content,
- );
- assert!(documents.is_formattable(&specifier));
- assert!(documents.is_diagnosable(&specifier));
- assert!(documents.line_index(&specifier).is_some());
+ let document =
+ documents.open(specifier, 1, "javascript".parse().unwrap(), content);
+ assert!(document.is_open());
+ assert!(document.is_diagnosable());
}
#[test]
@@ -1318,7 +1261,7 @@ console.log(b);
)
.unwrap();
assert_eq!(
- documents.content(&specifier).unwrap().as_str(),
+ documents.get(&specifier).unwrap().content().as_str(),
r#"import * as b from "./b.ts";
console.log(b, "hello deno");
"#
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 73d028e76..10a69fadb 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -39,6 +39,7 @@ use super::diagnostics;
use super::diagnostics::DiagnosticSource;
use super::documents::to_hover_text;
use super::documents::to_lsp_range;
+use super::documents::AssetOrDocument;
use super::documents::Documents;
use super::documents::LanguageId;
use super::lsp_custom;
@@ -47,7 +48,6 @@ use super::performance::Performance;
use super::refactor;
use super::registries;
use super::text;
-use super::text::LineIndex;
use super::tsc;
use super::tsc::AssetDocument;
use super::tsc::Assets;
@@ -171,71 +171,82 @@ impl Inner {
}
}
- /// Searches assets, open documents and external sources for a line_index,
- /// which might be performed asynchronously, hydrating in memory caches for
- /// subsequent requests.
- pub(crate) async fn get_line_index(
+ /// Searches assets and open documents which might be performed asynchronously,
+ /// hydrating in memory caches for subsequent requests.
+ pub(crate) async fn get_asset_or_document(
&mut self,
- specifier: ModuleSpecifier,
- ) -> Result<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())
- } else {
- None
- }
+ let result = if specifier.scheme() == "asset" {
+ self.get_asset(specifier).await?.map(AssetOrDocument::Asset)
} else {
- self.documents.line_index(specifier)
+ self.documents.get(specifier).map(AssetOrDocument::Document)
};
self.performance.measure(mark);
- maybe_line_index
+ Ok(result)
}
- // TODO(@kitsonk) we really should find a better way to just return the
- // content as a `&str`, or be able to get the byte at a particular offset
- // which is all that this API that is consuming it is trying to do at the
- // moment
- /// Searches already cached assets and documents and returns its text
- /// content. If not found, `None` is returned.
- pub(crate) fn get_text_content(
+ /// Only searches already cached assets and documents. If
+ /// the asset or document cannot be found an error is returned.
+ pub(crate) fn get_cached_asset_or_document(
&self,
specifier: &ModuleSpecifier,
- ) -> Option<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,33 +258,30 @@ impl Inner {
"get_navigation_tree",
Some(json!({ "specifier": specifier })),
);
- let maybe_navigation_tree = if specifier.scheme() == "asset" {
- self.assets.get_navigation_tree(specifier)
- } else {
- self.documents.get_navigation_tree(specifier)
- };
- let navigation_tree = if let Some(navigation_tree) = maybe_navigation_tree {
- navigation_tree
- } else {
- let navigation_tree: tsc::NavigationTree = self
- .ts_server
- .request(
- self.snapshot()?,
- tsc::RequestMethod::GetNavigationTree(specifier.clone()),
- )
- .await?;
- let navigation_tree = Arc::new(navigation_tree);
- if specifier.scheme() == "asset" {
- self
- .assets
- .set_navigation_tree(specifier, navigation_tree.clone())?;
+ let asset_or_doc = self.get_cached_asset_or_document(specifier)?;
+ let navigation_tree =
+ if let Some(navigation_tree) = asset_or_doc.maybe_navigation_tree() {
+ navigation_tree
} else {
- self
- .documents
- .set_navigation_tree(specifier, navigation_tree.clone())?;
- }
- navigation_tree
- };
+ let navigation_tree: tsc::NavigationTree = self
+ .ts_server
+ .request(
+ self.snapshot()?,
+ tsc::RequestMethod::GetNavigationTree(specifier.clone()),
+ )
+ .await?;
+ let navigation_tree = Arc::new(navigation_tree);
+ if specifier.scheme() == "asset" {
+ self
+ .assets
+ .set_navigation_tree(specifier, navigation_tree.clone())?;
+ } else {
+ self
+ .documents
+ .set_navigation_tree(specifier, navigation_tree.clone())?;
+ }
+ navigation_tree
+ };
self.performance.measure(mark);
Ok(navigation_tree)
}
@@ -335,7 +343,11 @@ impl Inner {
| MediaType::Dts
)
} else {
- self.documents.is_diagnosable(specifier)
+ self
+ .documents
+ .get(specifier)
+ .map(|d| d.is_diagnosable())
+ .unwrap_or(false)
}
}
@@ -594,22 +606,20 @@ impl Inner {
Ok(())
}
- pub(crate) fn document_version(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Option<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,27 +2616,15 @@ impl Inner {
}
Some(contents)
} else {
- match specifier.scheme() {
- "asset" => {
- if let Some(asset) = self
- .get_asset(&specifier)
- .await
- .map_err(|_| LspError::internal_error())?
- {
- Some(asset.text.to_string())
- } else {
- error!("Missing asset: {}", specifier);
- None
- }
- }
- _ => {
- if let Some(source) = self.documents.content(&specifier) {
- Some(source.to_string())
- } else {
- error!("The cached source was not found: {}", specifier);
- None
- }
- }
+ let asset_or_doc = self
+ .get_maybe_asset_or_document(&specifier)
+ .await
+ .map_err(|_| LspError::internal_error())?;
+ if let Some(asset_or_doc) = asset_or_doc {
+ Some(asset_or_doc.text().to_string())
+ } else {
+ error!("The source was not found: {}", specifier);
+ None
}
};
self.performance.measure(mark);
diff --git a/cli/lsp/registries.rs b/cli/lsp/registries.rs
index b63850c32..bef55150d 100644
--- a/cli/lsp/registries.rs
+++ b/cli/lsp/registries.rs
@@ -1,6 +1,5 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-use super::language_server;
use super::path_to_regex::parse;
use super::path_to_regex::string_to_regex;
use super::path_to_regex::Compiler;
@@ -439,7 +438,7 @@ impl ModuleRegistry {
current_specifier: &str,
offset: usize,
range: &lsp::Range,
- state_snapshot: &language_server::StateSnapshot,
+ specifier_exists: impl Fn(&ModuleSpecifier) -> bool,
) -> Option<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();
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index fc3695a5b..9350d6f19 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -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);