summaryrefslogtreecommitdiff
path: root/cli/lsp/documents.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp/documents.rs')
-rw-r--r--cli/lsp/documents.rs203
1 files changed, 175 insertions, 28 deletions
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs
index dc5b0f004..ce7e4e36f 100644
--- a/cli/lsp/documents.rs
+++ b/cli/lsp/documents.rs
@@ -1,14 +1,16 @@
// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
-use super::resolver::ImportMapResolver;
use super::text::LineIndex;
use super::tsc;
+use crate::config_file::ConfigFile;
use crate::file_fetcher::get_source_from_bytes;
use crate::file_fetcher::map_content_type;
use crate::file_fetcher::SUPPORTED_SCHEMES;
use crate::http_cache;
use crate::http_cache::HttpCache;
+use crate::resolver::ImportMapResolver;
+use crate::resolver::JsxResolver;
use crate::text_encoding;
use deno_ast::MediaType;
@@ -19,6 +21,7 @@ use deno_core::parking_lot::Mutex;
use deno_core::url;
use deno_core::ModuleSpecifier;
use lspower::lsp;
+use std::collections::BTreeMap;
use std::collections::HashMap;
use std::collections::HashSet;
use std::fs;
@@ -131,6 +134,59 @@ impl IndexValid {
}
}
+// TODO(@kitsonk) expose the synthetic module from deno_graph
+#[derive(Debug)]
+struct SyntheticModule {
+ dependencies: BTreeMap<String, deno_graph::Resolved>,
+ specifier: ModuleSpecifier,
+}
+
+impl SyntheticModule {
+ pub fn new(
+ specifier: ModuleSpecifier,
+ dependencies: Vec<(String, Option<lsp::Range>)>,
+ maybe_resolver: Option<&dyn deno_graph::source::Resolver>,
+ ) -> Self {
+ let dependencies = dependencies
+ .iter()
+ .map(|(dep, maybe_range)| {
+ let range = to_deno_graph_range(&specifier, maybe_range.as_ref());
+ let result = if let Some(resolver) = maybe_resolver {
+ resolver.resolve(dep, &specifier).map_err(|err| {
+ if let Some(specifier_error) =
+ err.downcast_ref::<deno_graph::SpecifierError>()
+ {
+ deno_graph::ResolutionError::InvalidSpecifier(
+ specifier_error.clone(),
+ range.clone(),
+ )
+ } else {
+ deno_graph::ResolutionError::ResolverError(
+ Arc::new(err),
+ dep.to_string(),
+ range.clone(),
+ )
+ }
+ })
+ } else {
+ deno_core::resolve_import(dep, specifier.as_str()).map_err(|err| {
+ deno_graph::ResolutionError::ResolverError(
+ Arc::new(err.into()),
+ dep.to_string(),
+ range.clone(),
+ )
+ })
+ };
+ (dep.to_string(), Some(result.map(|s| (s, range))))
+ })
+ .collect();
+ Self {
+ dependencies,
+ specifier,
+ }
+ }
+}
+
#[derive(Debug)]
pub(crate) struct Document {
line_index: Arc<LineIndex>,
@@ -347,6 +403,32 @@ pub(crate) fn to_lsp_range(range: &deno_graph::Range) -> lsp::Range {
}
}
+fn to_deno_graph_range(
+ specifier: &ModuleSpecifier,
+ maybe_range: Option<&lsp::Range>,
+) -> deno_graph::Range {
+ let specifier = specifier.clone();
+ if let Some(range) = maybe_range {
+ deno_graph::Range {
+ specifier,
+ start: deno_graph::Position {
+ line: range.start.line as usize,
+ character: range.start.character as usize,
+ },
+ end: deno_graph::Position {
+ line: range.end.line as usize,
+ character: range.end.character as usize,
+ },
+ }
+ } else {
+ deno_graph::Range {
+ specifier,
+ start: deno_graph::Position::zeroed(),
+ end: deno_graph::Position::zeroed(),
+ }
+ }
+}
+
/// Recurse and collect specifiers that appear in the dependent map.
fn recurse_dependents(
specifier: &ModuleSpecifier,
@@ -376,8 +458,13 @@ struct Inner {
/// A map of documents that can either be "open" in the language server, or
/// just present on disk.
docs: HashMap<ModuleSpecifier, Document>,
+ /// Any imports to the context supplied by configuration files. This is like
+ /// the imports into the a module graph in CLI.
+ imports: HashMap<ModuleSpecifier, SyntheticModule>,
/// The optional import map that should be used when resolving dependencies.
maybe_import_map: Option<ImportMapResolver>,
+ /// The optional JSX resolver, which is used when JSX imports are configured.
+ maybe_jsx_resolver: Option<JsxResolver>,
redirects: HashMap<ModuleSpecifier, ModuleSpecifier>,
}
@@ -388,7 +475,9 @@ impl Inner {
dirty: true,
dependents_map: HashMap::default(),
docs: HashMap::default(),
+ imports: HashMap::default(),
maybe_import_map: None,
+ maybe_jsx_resolver: None,
redirects: HashMap::default(),
}
}
@@ -407,7 +496,7 @@ impl Inner {
version,
None,
content,
- self.maybe_import_map.as_ref().map(|r| r.as_resolver()),
+ self.get_maybe_resolver(),
)
} else {
let cache_filename = self.cache.get_cache_filename(&specifier)?;
@@ -421,7 +510,7 @@ impl Inner {
version,
maybe_headers,
content,
- self.maybe_import_map.as_ref().map(|r| r.as_resolver()),
+ self.get_maybe_resolver(),
)
};
self.dirty = true;
@@ -481,6 +570,14 @@ impl Inner {
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(
|| {
Err(custom_error(
@@ -491,11 +588,7 @@ impl Inner {
Ok,
)?;
self.dirty = true;
- doc.change(
- version,
- changes,
- self.maybe_import_map.as_ref().map(|r| r.as_resolver()),
- )
+ doc.change(version, changes, maybe_resolver)
}
fn close(&mut self, specifier: &ModuleSpecifier) -> Result<(), AnyError> {
@@ -518,8 +611,7 @@ impl Inner {
specifier: &str,
referrer: &ModuleSpecifier,
) -> bool {
- let maybe_resolver =
- self.maybe_import_map.as_ref().map(|im| im.as_resolver());
+ let maybe_resolver = self.get_maybe_resolver();
let maybe_specifier = if let Some(resolver) = maybe_resolver {
resolver.resolve(specifier, referrer).ok()
} else {
@@ -604,6 +696,14 @@ impl Inner {
})
}
+ fn get_maybe_resolver(&self) -> Option<&dyn deno_graph::source::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())
+ }
+ }
+
fn get_maybe_types_for_dependency(
&mut self,
dependency: &deno_graph::Dependency,
@@ -706,12 +806,13 @@ impl Inner {
language_id: LanguageId,
content: Arc<String>,
) {
+ let maybe_resolver = self.get_maybe_resolver();
let document_data = Document::open(
specifier.clone(),
version,
language_id,
content,
- self.maybe_import_map.as_ref().map(|r| r.as_resolver()),
+ maybe_resolver,
);
self.docs.insert(specifier, document_data);
self.dirty = true;
@@ -758,6 +859,12 @@ impl Inner {
} else {
results.push(None);
}
+ } else if let Some(Some(Ok((specifier, _)))) =
+ self.resolve_imports_dependency(&specifier)
+ {
+ // clone here to avoid double borrow of self
+ let specifier = specifier.clone();
+ results.push(self.resolve_dependency(&specifier));
} else {
results.push(None);
}
@@ -790,6 +897,22 @@ impl Inner {
}
}
+ /// Iterate through any "imported" modules, checking to see if a dependency
+ /// is available. This is used to provide "global" imports like the JSX import
+ /// source.
+ fn resolve_imports_dependency(
+ &self,
+ specifier: &str,
+ ) -> Option<&deno_graph::Resolved> {
+ for module in self.imports.values() {
+ let maybe_dep = module.dependencies.get(specifier);
+ if maybe_dep.is_some() {
+ return maybe_dep;
+ }
+ }
+ None
+ }
+
fn resolve_remote_specifier(
&self,
specifier: &ModuleSpecifier,
@@ -832,15 +955,6 @@ impl Inner {
}
}
- fn set_import_map(
- &mut self,
- maybe_import_map: Option<Arc<import_map::ImportMap>>,
- ) {
- // TODO update resolved dependencies?
- self.maybe_import_map = maybe_import_map.map(ImportMapResolver::new);
- self.dirty = true;
- }
-
fn set_location(&mut self, location: PathBuf) {
// TODO update resolved dependencies?
self.cache = HttpCache::new(&location);
@@ -886,6 +1000,36 @@ impl Inner {
self.get(specifier).map(|d| d.source.clone())
}
+ fn update_config(
+ &mut self,
+ maybe_import_map: Option<Arc<import_map::ImportMap>>,
+ maybe_config_file: Option<&ConfigFile>,
+ ) {
+ // TODO(@kitsonk) update resolved dependencies?
+ self.maybe_import_map = maybe_import_map.map(ImportMapResolver::new);
+ self.maybe_jsx_resolver = maybe_config_file
+ .map(|cf| {
+ cf.to_maybe_jsx_import_source_module()
+ .map(|im| JsxResolver::new(im, self.maybe_import_map.clone()))
+ })
+ .flatten();
+ if let Some(Ok(Some(imports))) =
+ maybe_config_file.map(|cf| cf.to_maybe_imports())
+ {
+ for (referrer, dependencies) in imports {
+ let dependencies =
+ dependencies.into_iter().map(|s| (s, None)).collect();
+ let module = SyntheticModule::new(
+ referrer.clone(),
+ dependencies,
+ self.get_maybe_resolver(),
+ );
+ self.imports.insert(referrer, module);
+ }
+ }
+ self.dirty = true;
+ }
+
fn version(&mut self, specifier: &ModuleSpecifier) -> Option<String> {
self.get(specifier).map(|d| {
d.maybe_lsp_version
@@ -1050,14 +1194,6 @@ impl Documents {
self.0.lock().resolve(specifiers, referrer)
}
- /// Set the optional import map for the document cache.
- pub fn set_import_map(
- &self,
- maybe_import_map: Option<Arc<import_map::ImportMap>>,
- ) {
- self.0.lock().set_import_map(maybe_import_map);
- }
-
/// Update the location of the on disk cache for the document store.
pub fn set_location(&self, location: PathBuf) {
self.0.lock().set_location(location)
@@ -1095,6 +1231,17 @@ impl Documents {
self.0.lock().text_info(specifier)
}
+ pub fn update_config(
+ &self,
+ maybe_import_map: Option<Arc<import_map::ImportMap>>,
+ maybe_config_file: Option<&ConfigFile>,
+ ) {
+ self
+ .0
+ .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)