summaryrefslogtreecommitdiff
path: root/cli/lsp
diff options
context:
space:
mode:
Diffstat (limited to 'cli/lsp')
-rw-r--r--cli/lsp/analysis.rs67
-rw-r--r--cli/lsp/cache.rs86
-rw-r--r--cli/lsp/language_server.rs55
-rw-r--r--cli/lsp/mod.rs1
-rw-r--r--cli/lsp/sources.rs29
-rw-r--r--cli/lsp/tsc.rs11
6 files changed, 187 insertions, 62 deletions
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs
index d1cf14500..a0718e8b3 100644
--- a/cli/lsp/analysis.rs
+++ b/cli/lsp/analysis.rs
@@ -6,12 +6,12 @@ use super::tsc;
use crate::ast;
use crate::ast::Location;
use crate::lsp::documents::DocumentData;
-use crate::module_graph::parse_deno_types;
-use crate::module_graph::parse_ts_reference;
-use crate::module_graph::TypeScriptReference;
use crate::tools::lint::create_linter;
use deno_ast::swc::ast as swc_ast;
+use deno_ast::swc::common::comments::Comment;
+use deno_ast::swc::common::BytePos;
+use deno_ast::swc::common::Span;
use deno_ast::swc::common::DUMMY_SP;
use deno_ast::swc::visit::Node;
use deno_ast::swc::visit::Visit;
@@ -68,10 +68,71 @@ lazy_static::lazy_static! {
.collect();
static ref IMPORT_SPECIFIER_RE: Regex = Regex::new(r#"\sfrom\s+["']([^"']*)["']"#).unwrap();
+
+ static ref DENO_TYPES_RE: Regex =
+ Regex::new(r#"(?i)^\s*@deno-types\s*=\s*(?:["']([^"']+)["']|(\S+))"#)
+ .unwrap();
+ static ref TRIPLE_SLASH_REFERENCE_RE: Regex =
+ Regex::new(r"(?i)^/\s*<reference\s.*?/>").unwrap();
+ static ref PATH_REFERENCE_RE: Regex =
+ Regex::new(r#"(?i)\spath\s*=\s*["']([^"']*)["']"#).unwrap();
+ static ref TYPES_REFERENCE_RE: Regex =
+ Regex::new(r#"(?i)\stypes\s*=\s*["']([^"']*)["']"#).unwrap();
+
}
const SUPPORTED_EXTENSIONS: &[&str] = &[".ts", ".tsx", ".js", ".jsx", ".mjs"];
+// TODO(@kitsonk) remove after deno_graph migration
+#[derive(Debug, Clone, Eq, PartialEq)]
+enum TypeScriptReference {
+ Path(String),
+ Types(String),
+}
+
+fn match_to_span(comment: &Comment, m: &regex::Match) -> Span {
+ Span {
+ lo: comment.span.lo + BytePos((m.start() + 1) as u32),
+ hi: comment.span.lo + BytePos((m.end() + 1) as u32),
+ ctxt: comment.span.ctxt,
+ }
+}
+
+// TODO(@kitsonk) remove after deno_graph migration
+fn parse_deno_types(comment: &Comment) -> Option<(String, Span)> {
+ let captures = DENO_TYPES_RE.captures(&comment.text)?;
+ if let Some(m) = captures.get(1) {
+ Some((m.as_str().to_string(), match_to_span(comment, &m)))
+ } else if let Some(m) = captures.get(2) {
+ Some((m.as_str().to_string(), match_to_span(comment, &m)))
+ } else {
+ unreachable!();
+ }
+}
+
+// TODO(@kitsonk) remove after deno_graph migration
+fn parse_ts_reference(
+ comment: &Comment,
+) -> Option<(TypeScriptReference, Span)> {
+ if !TRIPLE_SLASH_REFERENCE_RE.is_match(&comment.text) {
+ None
+ } else if let Some(captures) = PATH_REFERENCE_RE.captures(&comment.text) {
+ let m = captures.get(1).unwrap();
+ Some((
+ TypeScriptReference::Path(m.as_str().to_string()),
+ match_to_span(comment, &m),
+ ))
+ } else {
+ TYPES_REFERENCE_RE.captures(&comment.text).map(|captures| {
+ let m = captures.get(1).unwrap();
+ (
+ TypeScriptReference::Types(m.as_str().to_string()),
+ match_to_span(comment, &m),
+ )
+ })
+ }
+}
+
/// Category of self-generated diagnostic messages (those not coming from)
/// TypeScript.
#[derive(Debug, PartialEq, Eq)]
diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs
new file mode 100644
index 000000000..39b9dca60
--- /dev/null
+++ b/cli/lsp/cache.rs
@@ -0,0 +1,86 @@
+// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license.
+
+use crate::cache::CacherLoader;
+use crate::cache::FetchCacher;
+use crate::flags::Flags;
+use crate::proc_state::ProcState;
+use crate::resolver::ImportMapResolver;
+use crate::tokio_util::create_basic_runtime;
+
+use deno_core::error::anyhow;
+use deno_core::error::AnyError;
+use deno_core::ModuleSpecifier;
+use deno_runtime::permissions::Permissions;
+use import_map::ImportMap;
+use std::path::PathBuf;
+use std::thread;
+use tokio::sync::mpsc;
+use tokio::sync::oneshot;
+
+type Request = (Vec<ModuleSpecifier>, oneshot::Sender<Result<(), AnyError>>);
+
+/// A "server" that handles requests from the language server to cache modules
+/// in its own thread.
+#[derive(Debug)]
+pub(crate) struct CacheServer(mpsc::UnboundedSender<Request>);
+
+impl CacheServer {
+ pub async fn new(
+ maybe_cache_path: Option<PathBuf>,
+ maybe_import_map: Option<ImportMap>,
+ ) -> Self {
+ let (tx, mut rx) = mpsc::unbounded_channel::<Request>();
+ let _join_handle = thread::spawn(move || {
+ let runtime = create_basic_runtime();
+ runtime.block_on(async {
+ let ps = ProcState::build(Flags {
+ cache_path: maybe_cache_path,
+ ..Default::default()
+ })
+ .await
+ .unwrap();
+ let maybe_resolver =
+ maybe_import_map.as_ref().map(ImportMapResolver::new);
+ let mut cache = FetchCacher::new(
+ ps.dir.gen_cache.clone(),
+ ps.file_fetcher.clone(),
+ Permissions::allow_all(),
+ Permissions::allow_all(),
+ );
+
+ while let Some((roots, tx)) = rx.recv().await {
+ let graph = deno_graph::create_graph(
+ roots,
+ false,
+ None,
+ cache.as_mut_loader(),
+ maybe_resolver.as_ref().map(|r| r.as_resolver()),
+ None,
+ None,
+ )
+ .await;
+
+ if tx.send(graph.valid().map_err(|err| err.into())).is_err() {
+ log::warn!("cannot send to client");
+ }
+ }
+ })
+ });
+
+ Self(tx)
+ }
+
+ /// Attempt to cache the supplied module specifiers and their dependencies in
+ /// the current DENO_DIR, returning any errors, so they can be returned to the
+ /// client.
+ pub async fn cache(
+ &self,
+ roots: Vec<ModuleSpecifier>,
+ ) -> Result<(), AnyError> {
+ let (tx, rx) = oneshot::channel::<Result<(), AnyError>>();
+ if self.0.send((roots, tx)).is_err() {
+ return Err(anyhow!("failed to send request to cache thread"));
+ }
+ rx.await?
+ }
+}
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index ef659cd05..524a59191 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -29,6 +29,7 @@ use super::analysis::ts_changes_to_edit;
use super::analysis::CodeActionCollection;
use super::analysis::CodeActionData;
use super::analysis::ResolvedDependency;
+use super::cache::CacheServer;
use super::capabilities;
use super::code_lens;
use super::completions;
@@ -44,7 +45,6 @@ use super::parent_process_checker;
use super::performance::Performance;
use super::refactor;
use super::registries;
-use super::sources;
use super::sources::Sources;
use super::text;
use super::text::LineIndex;
@@ -99,6 +99,8 @@ pub(crate) struct Inner {
/// An optional path to the DENO_DIR which has been specified in the client
/// options.
maybe_cache_path: Option<PathBuf>,
+ /// A lazily created "server" for handling cache requests.
+ maybe_cache_server: Option<CacheServer>,
/// An optional configuration file which has been specified in the client
/// options.
maybe_config_file: Option<ConfigFile>,
@@ -149,6 +151,7 @@ impl Inner {
diagnostics_server,
documents: Default::default(),
maybe_cache_path: None,
+ maybe_cache_server: None,
maybe_config_file: None,
maybe_config_uri: None,
maybe_import_map: None,
@@ -424,6 +427,7 @@ impl Inner {
pub fn update_cache(&mut self) -> Result<(), AnyError> {
let mark = self.performance.mark("update_cache", None::<()>);
self.performance.measure(mark);
+ self.maybe_cache_server = None;
let (maybe_cache, maybe_root_uri) = {
let config = &self.config;
(
@@ -479,6 +483,7 @@ impl Inner {
pub async fn update_import_map(&mut self) -> Result<(), AnyError> {
let mark = self.performance.mark("update_import_map", None::<()>);
+ self.maybe_cache_server = None;
let (maybe_import_map, maybe_root_uri) = {
let config = &self.config;
(
@@ -2630,34 +2635,30 @@ impl Inner {
}
let mark = self.performance.mark("cache", Some(&params));
- if !params.uris.is_empty() {
- for identifier in &params.uris {
- let specifier = self.url_map.normalize_url(&identifier.uri);
- sources::cache(
- &specifier,
- &self.maybe_import_map,
- &self.maybe_config_file,
- &self.maybe_cache_path,
- )
- .await
- .map_err(|err| {
- error!("{}", err);
- LspError::internal_error()
- })?;
- }
+ let roots = if !params.uris.is_empty() {
+ params
+ .uris
+ .iter()
+ .map(|t| self.url_map.normalize_url(&t.uri))
+ .collect()
} else {
- sources::cache(
- &referrer,
- &self.maybe_import_map,
- &self.maybe_config_file,
- &self.maybe_cache_path,
- )
- .await
- .map_err(|err| {
- error!("{}", err);
- LspError::internal_error()
- })?;
+ vec![referrer.clone()]
+ };
+
+ if self.maybe_cache_server.is_none() {
+ self.maybe_cache_server = Some(
+ CacheServer::new(
+ self.maybe_cache_path.clone(),
+ self.maybe_import_map.clone(),
+ )
+ .await,
+ );
}
+ let cache_server = self.maybe_cache_server.as_ref().unwrap();
+ if let Err(err) = cache_server.cache(roots).await {
+ self.client.show_message(MessageType::Warning, err).await;
+ }
+
// now that we have dependencies loaded, we need to re-analyze them and
// invalidate some diagnostics
if self.documents.contains_key(&referrer) {
diff --git a/cli/lsp/mod.rs b/cli/lsp/mod.rs
index fda2ac82b..e6cc88208 100644
--- a/cli/lsp/mod.rs
+++ b/cli/lsp/mod.rs
@@ -5,6 +5,7 @@ use lspower::LspService;
use lspower::Server;
mod analysis;
+mod cache;
mod capabilities;
mod code_lens;
mod completions;
diff --git a/cli/lsp/sources.rs b/cli/lsp/sources.rs
index bd5835cf2..69de6d976 100644
--- a/cli/lsp/sources.rs
+++ b/cli/lsp/sources.rs
@@ -6,18 +6,12 @@ use super::text::LineIndex;
use super::tsc;
use super::urls::INVALID_SPECIFIER;
-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::flags::Flags;
use crate::http_cache;
use crate::http_cache::HttpCache;
-use crate::module_graph::GraphBuilder;
-use crate::proc_state::ProcState;
-use crate::specifier_handler::FetchHandler;
use crate::text_encoding;
-use import_map::ImportMap;
use deno_ast::MediaType;
use deno_core::error::anyhow;
@@ -25,7 +19,7 @@ use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
use deno_core::serde_json;
use deno_core::ModuleSpecifier;
-use deno_runtime::permissions::Permissions;
+use import_map::ImportMap;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
@@ -34,27 +28,6 @@ use std::sync::Arc;
use std::time::SystemTime;
use tsc::NavigationTree;
-pub async fn cache(
- specifier: &ModuleSpecifier,
- maybe_import_map: &Option<ImportMap>,
- maybe_config_file: &Option<ConfigFile>,
- maybe_cache_path: &Option<PathBuf>,
-) -> Result<(), AnyError> {
- let ps = ProcState::build(Flags {
- cache_path: maybe_cache_path.clone(),
- ..Default::default()
- })
- .await?;
- let handler = Arc::new(Mutex::new(FetchHandler::new(
- &ps,
- Permissions::allow_all(),
- Permissions::allow_all(),
- )?));
- let mut builder = GraphBuilder::new(handler, maybe_import_map.clone(), None);
- builder.analyze_config_file(maybe_config_file).await?;
- builder.add(specifier, false).await
-}
-
fn get_remote_headers(
cache_filename: &Path,
) -> Option<HashMap<String, String>> {
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 05606cb79..9899fad72 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -44,12 +44,15 @@ use log::warn;
use lspower::lsp;
use regex::Captures;
use regex::Regex;
+use std::borrow::Cow;
+use std::cmp;
+use std::collections::HashMap;
use std::collections::HashSet;
+use std::path::Path;
use std::sync::Arc;
use std::thread;
-use std::{borrow::Cow, cmp};
-use std::{collections::HashMap, path::Path};
-use text_size::{TextRange, TextSize};
+use text_size::TextRange;
+use text_size::TextSize;
use tokio::sync::mpsc;
use tokio::sync::oneshot;
@@ -3375,7 +3378,7 @@ mod tests {
#[test]
fn test_modify_sources() {
let (mut runtime, state_snapshot, location) = setup(
- true,
+ false,
json!({
"target": "esnext",
"module": "esnext",