1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-25 15:29:32 -05:00

perf(lsp): concurrent reads and exclusive writes (#17135)

This commit is contained in:
David Sherret 2022-12-19 20:22:17 -05:00 committed by GitHub
parent 1e0017d8fc
commit 54d40e008a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 202 additions and 163 deletions

View file

@ -244,16 +244,18 @@ type MaybeModuleResult =
type MaybeParsedSourceResult = type MaybeParsedSourceResult =
Option<Result<ParsedSource, deno_ast::Diagnostic>>; Option<Result<ParsedSource, deno_ast::Diagnostic>>;
#[derive(Debug, Clone)] #[derive(Debug)]
struct DocumentInner { struct DocumentInner {
/// contains the last-known-good set of dependencies from parsing the module /// Contains the last-known-good set of dependencies from parsing the module.
dependencies: Arc<DocumentDependencies>, dependencies: Arc<DocumentDependencies>,
fs_version: String, fs_version: String,
line_index: Arc<LineIndex>, line_index: Arc<LineIndex>,
maybe_language_id: Option<LanguageId>, maybe_language_id: Option<LanguageId>,
maybe_lsp_version: Option<i32>, maybe_lsp_version: Option<i32>,
maybe_module: MaybeModuleResult, maybe_module: MaybeModuleResult,
maybe_navigation_tree: Option<Arc<tsc::NavigationTree>>, // this is a lazily constructed value based on the state of the document,
// so having a mutex to hold it is ok
maybe_navigation_tree: Mutex<Option<Arc<tsc::NavigationTree>>>,
maybe_parsed_source: MaybeParsedSourceResult, maybe_parsed_source: MaybeParsedSourceResult,
specifier: ModuleSpecifier, specifier: ModuleSpecifier,
text_info: SourceTextInfo, text_info: SourceTextInfo,
@ -291,7 +293,7 @@ impl Document {
maybe_language_id: None, maybe_language_id: None,
maybe_lsp_version: None, maybe_lsp_version: None,
maybe_module, maybe_module,
maybe_navigation_tree: None, maybe_navigation_tree: Mutex::new(None),
maybe_parsed_source, maybe_parsed_source,
text_info, text_info,
specifier, specifier,
@ -327,7 +329,7 @@ impl Document {
maybe_language_id: Some(language_id), maybe_language_id: Some(language_id),
maybe_lsp_version: Some(version), maybe_lsp_version: Some(version),
maybe_module, maybe_module,
maybe_navigation_tree: None, maybe_navigation_tree: Mutex::new(None),
maybe_parsed_source, maybe_parsed_source,
text_info: source, text_info: source,
specifier, specifier,
@ -390,27 +392,19 @@ impl Document {
Arc::new(LineIndex::new(text_info.text_str())) Arc::new(LineIndex::new(text_info.text_str()))
}; };
Ok(Document(Arc::new(DocumentInner { Ok(Document(Arc::new(DocumentInner {
specifier: self.0.specifier.clone(),
fs_version: self.0.fs_version.clone(),
maybe_language_id: self.0.maybe_language_id.clone(),
dependencies, dependencies,
text_info, text_info,
line_index, line_index,
maybe_module, maybe_module,
maybe_parsed_source, maybe_parsed_source,
maybe_lsp_version: Some(version), maybe_lsp_version: Some(version),
maybe_navigation_tree: None, maybe_navigation_tree: Mutex::new(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()
}))
}
pub fn specifier(&self) -> &ModuleSpecifier { pub fn specifier(&self) -> &ModuleSpecifier {
&self.0.specifier &self.0.specifier
} }
@ -494,7 +488,21 @@ impl Document {
} }
pub fn maybe_navigation_tree(&self) -> Option<Arc<tsc::NavigationTree>> { pub fn maybe_navigation_tree(&self) -> Option<Arc<tsc::NavigationTree>> {
self.0.maybe_navigation_tree.clone() self.0.maybe_navigation_tree.lock().clone()
}
pub fn update_navigation_tree_if_version(
&self,
tree: Arc<tsc::NavigationTree>,
script_version: &str,
) {
// Ensure we are updating the same document that the navigation tree was
// created for. Note: this should not be racy between the version check
// and setting the navigation tree, because the document is immutable
// and this is enforced by it being wrapped in an Arc.
if self.script_version() == script_version {
*self.0.maybe_navigation_tree.lock() = Some(tree);
}
} }
pub fn dependencies(&self) -> &BTreeMap<String, deno_graph::Dependency> { pub fn dependencies(&self) -> &BTreeMap<String, deno_graph::Dependency> {
@ -1018,23 +1026,17 @@ impl Documents {
/// Tries to cache a navigation tree that is associated with the provided specifier /// Tries to cache a navigation tree that is associated with the provided specifier
/// if the document stored has the same script version. /// if the document stored has the same script version.
pub fn try_cache_navigation_tree( pub fn try_cache_navigation_tree(
&mut self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
script_version: &str, script_version: &str,
navigation_tree: Arc<tsc::NavigationTree>, navigation_tree: Arc<tsc::NavigationTree>,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
if let Some(doc) = self.open_docs.get_mut(specifier) { if let Some(doc) = self.open_docs.get(specifier) {
if doc.script_version() == script_version { doc.update_navigation_tree_if_version(navigation_tree, script_version)
*doc = doc.with_navigation_tree(navigation_tree);
}
} else { } else {
let mut file_system_docs = self.file_system_docs.lock(); let mut file_system_docs = self.file_system_docs.lock();
if let Some(doc) = file_system_docs.docs.get_mut(specifier) { if let Some(doc) = file_system_docs.docs.get_mut(specifier) {
// ensure we are updating the same document doc.update_navigation_tree_if_version(navigation_tree, script_version);
// that the navigation tree was created for
if doc.script_version() == script_version {
*doc = doc.with_navigation_tree(navigation_tree);
}
} else { } else {
return Err(custom_error( return Err(custom_error(
"NotFound", "NotFound",

View file

@ -9,7 +9,6 @@ use deno_core::serde_json::json;
use deno_core::serde_json::Value; use deno_core::serde_json::Value;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_graph::ModuleKind; use deno_graph::ModuleKind;
use deno_runtime::tokio_util::run_local;
use import_map::ImportMap; use import_map::ImportMap;
use log::error; use log::error;
use log::warn; use log::warn;
@ -48,6 +47,7 @@ use super::logging::lsp_log;
use super::lsp_custom; use super::lsp_custom;
use super::parent_process_checker; use super::parent_process_checker;
use super::performance::Performance; use super::performance::Performance;
use super::performance::PerformanceMark;
use super::refactor; use super::refactor;
use super::registries::ModuleRegistry; use super::registries::ModuleRegistry;
use super::testing; use super::testing;
@ -83,7 +83,7 @@ use crate::util::progress_bar::ProgressBar;
use crate::util::progress_bar::ProgressBarStyle; use crate::util::progress_bar::ProgressBarStyle;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct LanguageServer(Arc<tokio::sync::Mutex<Inner>>); pub struct LanguageServer(Arc<tokio::sync::RwLock<Inner>>);
/// Snapshot of the state used by TSC. /// Snapshot of the state used by TSC.
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -146,39 +146,90 @@ pub struct Inner {
impl LanguageServer { impl LanguageServer {
pub fn new(client: Client) -> Self { pub fn new(client: Client) -> Self {
Self(Arc::new(tokio::sync::Mutex::new(Inner::new(client)))) Self(Arc::new(tokio::sync::RwLock::new(Inner::new(client))))
} }
/// Similar to `deno cache` on the command line, where modules will be cached
/// in the Deno cache, including any of their dependencies.
pub async fn cache_request( pub async fn cache_request(
&self, &self,
params: Option<Value>, params: Option<Value>,
) -> LspResult<Option<Value>> { ) -> LspResult<Option<Value>> {
async fn create_graph_for_caching(
cli_options: CliOptions,
roots: Vec<(ModuleSpecifier, ModuleKind)>,
open_docs: Vec<Document>,
) -> Result<(), AnyError> {
let open_docs = open_docs
.into_iter()
.map(|d| (d.specifier().clone(), d))
.collect::<HashMap<_, _>>();
let ps = ProcState::from_options(Arc::new(cli_options)).await?;
let mut inner_loader = ps.create_graph_loader();
let mut loader = crate::lsp::documents::OpenDocumentsGraphLoader {
inner_loader: &mut inner_loader,
open_docs: &open_docs,
};
let graph = ps.create_graph_with_loader(roots, &mut loader).await?;
graph_valid(&graph, true, false)?;
Ok(())
}
match params.map(serde_json::from_value) { match params.map(serde_json::from_value) {
Some(Ok(params)) => self.0.lock().await.cache(params).await, Some(Ok(params)) => {
// do as much as possible in a read, then do a write outside
let result = {
let inner = self.0.read().await; // ensure dropped
inner.prepare_cache(params)?
};
if let Some(result) = result {
let cli_options = result.cli_options;
let roots = result.roots;
let open_docs = result.open_docs;
let handle = tokio::task::spawn_local(async move {
create_graph_for_caching(cli_options, roots, open_docs).await
});
if let Err(err) = handle.await.unwrap() {
self
.0
.read()
.await
.client
.show_message(MessageType::WARNING, err)
.await;
}
// do npm resolution in a write—we should have everything
// cached by this point anyway
self.0.write().await.refresh_npm_specifiers().await;
// now refresh the data in a read
self.0.read().await.post_cache(result.mark).await;
}
Ok(Some(json!(true)))
}
Some(Err(err)) => Err(LspError::invalid_params(err.to_string())), Some(Err(err)) => Err(LspError::invalid_params(err.to_string())),
None => Err(LspError::invalid_params("Missing parameters")), None => Err(LspError::invalid_params("Missing parameters")),
} }
} }
pub async fn performance_request(&self) -> LspResult<Option<Value>> { pub async fn performance_request(&self) -> LspResult<Option<Value>> {
Ok(Some(self.0.lock().await.get_performance())) Ok(Some(self.0.read().await.get_performance()))
} }
pub async fn reload_import_registries_request( pub async fn reload_import_registries_request(
&self, &self,
) -> LspResult<Option<Value>> { ) -> LspResult<Option<Value>> {
self.0.lock().await.reload_import_registries().await self.0.write().await.reload_import_registries().await
} }
pub async fn task_request(&self) -> LspResult<Option<Value>> { pub async fn task_request(&self) -> LspResult<Option<Value>> {
self.0.lock().await.get_tasks() self.0.read().await.get_tasks()
} }
pub async fn test_run_request( pub async fn test_run_request(
&self, &self,
params: Option<Value>, params: Option<Value>,
) -> LspResult<Option<Value>> { ) -> LspResult<Option<Value>> {
let inner = self.0.lock().await; let inner = self.0.read().await;
if let Some(testing_server) = &inner.maybe_testing_server { if let Some(testing_server) = &inner.maybe_testing_server {
match params.map(serde_json::from_value) { match params.map(serde_json::from_value) {
Some(Ok(params)) => testing_server Some(Ok(params)) => testing_server
@ -195,7 +246,7 @@ impl LanguageServer {
&self, &self,
params: Option<Value>, params: Option<Value>,
) -> LspResult<Option<Value>> { ) -> LspResult<Option<Value>> {
if let Some(testing_server) = &self.0.lock().await.maybe_testing_server { if let Some(testing_server) = &self.0.read().await.maybe_testing_server {
match params.map(serde_json::from_value) { match params.map(serde_json::from_value) {
Some(Ok(params)) => testing_server.run_cancel_request(params), Some(Ok(params)) => testing_server.run_cancel_request(params),
Some(Err(err)) => Err(LspError::invalid_params(err.to_string())), Some(Err(err)) => Err(LspError::invalid_params(err.to_string())),
@ -210,7 +261,7 @@ impl LanguageServer {
&self, &self,
params: InlayHintParams, params: InlayHintParams,
) -> LspResult<Option<Vec<InlayHint>>> { ) -> LspResult<Option<Vec<InlayHint>>> {
self.0.lock().await.inlay_hint(params).await self.0.read().await.inlay_hint(params).await
} }
pub async fn virtual_text_document( pub async fn virtual_text_document(
@ -220,7 +271,7 @@ impl LanguageServer {
match params.map(serde_json::from_value) { match params.map(serde_json::from_value) {
Some(Ok(params)) => Ok(Some( Some(Ok(params)) => Ok(Some(
serde_json::to_value( serde_json::to_value(
self.0.lock().await.virtual_text_document(params)?, self.0.read().await.virtual_text_document(params)?,
) )
.map_err(|err| { .map_err(|err| {
error!( error!(
@ -339,7 +390,7 @@ impl Inner {
} }
pub async fn get_navigation_tree( pub async fn get_navigation_tree(
&mut self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
) -> Result<Arc<tsc::NavigationTree>, AnyError> { ) -> Result<Arc<tsc::NavigationTree>, AnyError> {
let mark = self.performance.mark( let mark = self.performance.mark(
@ -1093,7 +1144,7 @@ impl Inner {
} }
async fn document_symbol( async fn document_symbol(
&mut self, &self,
params: DocumentSymbolParams, params: DocumentSymbolParams,
) -> LspResult<Option<DocumentSymbolResponse>> { ) -> LspResult<Option<DocumentSymbolResponse>> {
let specifier = self.url_map.normalize_url(&params.text_document.uri); let specifier = self.url_map.normalize_url(&params.text_document.uri);
@ -1155,34 +1206,30 @@ impl Inner {
Default::default() Default::default()
}; };
let text_edits = tokio::task::spawn_blocking(move || { let format_result = match document.maybe_parsed_source() {
let format_result = match document.maybe_parsed_source() { Some(Ok(parsed_source)) => {
Some(Ok(parsed_source)) => { format_parsed_source(&parsed_source, fmt_options)
format_parsed_source(&parsed_source, fmt_options)
}
Some(Err(err)) => Err(anyhow!("{}", err)),
None => {
// it's not a js/ts file, so attempt to format its contents
format_file(&file_path, &document.content(), &fmt_options)
}
};
match format_result {
Ok(Some(new_text)) => Some(text::get_edits(
&document.content(),
&new_text,
document.line_index().as_ref(),
)),
Ok(None) => Some(Vec::new()),
Err(err) => {
// TODO(lucacasonato): handle error properly
warn!("Format error: {:#}", err);
None
}
} }
}) Some(Err(err)) => Err(anyhow!("{}", err)),
.await None => {
.unwrap(); // it's not a js/ts file, so attempt to format its contents
format_file(&file_path, &document.content(), &fmt_options)
}
};
let text_edits = match format_result {
Ok(Some(new_text)) => Some(text::get_edits(
&document.content(),
&new_text,
document.line_index().as_ref(),
)),
Ok(None) => Some(Vec::new()),
Err(err) => {
// TODO(lucacasonato): handle error properly
warn!("Format error: {:#}", err);
None
}
};
self.performance.measure(mark); self.performance.measure(mark);
if let Some(text_edits) = text_edits { if let Some(text_edits) = text_edits {
@ -1542,7 +1589,7 @@ impl Inner {
} }
async fn code_lens( async fn code_lens(
&mut self, &self,
params: CodeLensParams, params: CodeLensParams,
) -> LspResult<Option<Vec<CodeLens>>> { ) -> LspResult<Option<Vec<CodeLens>>> {
let specifier = self.url_map.normalize_url(&params.text_document.uri); let specifier = self.url_map.normalize_url(&params.text_document.uri);
@ -2247,7 +2294,7 @@ impl Inner {
} }
async fn selection_range( async fn selection_range(
&mut self, &self,
params: SelectionRangeParams, params: SelectionRangeParams,
) -> LspResult<Option<Vec<SelectionRange>>> { ) -> LspResult<Option<Vec<SelectionRange>>> {
let specifier = self.url_map.normalize_url(&params.text_document.uri); let specifier = self.url_map.normalize_url(&params.text_document.uri);
@ -2285,7 +2332,7 @@ impl Inner {
} }
async fn semantic_tokens_full( async fn semantic_tokens_full(
&mut self, &self,
params: SemanticTokensParams, params: SemanticTokensParams,
) -> LspResult<Option<SemanticTokensResult>> { ) -> LspResult<Option<SemanticTokensResult>> {
let specifier = self.url_map.normalize_url(&params.text_document.uri); let specifier = self.url_map.normalize_url(&params.text_document.uri);
@ -2327,7 +2374,7 @@ impl Inner {
} }
async fn semantic_tokens_range( async fn semantic_tokens_range(
&mut self, &self,
params: SemanticTokensRangeParams, params: SemanticTokensRangeParams,
) -> LspResult<Option<SemanticTokensRangeResult>> { ) -> LspResult<Option<SemanticTokensRangeResult>> {
let specifier = self.url_map.normalize_url(&params.text_document.uri); let specifier = self.url_map.normalize_url(&params.text_document.uri);
@ -2370,7 +2417,7 @@ impl Inner {
} }
async fn signature_help( async fn signature_help(
&mut self, &self,
params: SignatureHelpParams, params: SignatureHelpParams,
) -> LspResult<Option<SignatureHelp>> { ) -> LspResult<Option<SignatureHelp>> {
let specifier = self let specifier = self
@ -2422,7 +2469,7 @@ impl Inner {
} }
async fn symbol( async fn symbol(
&mut self, &self,
params: WorkspaceSymbolParams, params: WorkspaceSymbolParams,
) -> LspResult<Option<Vec<SymbolInformation>>> { ) -> LspResult<Option<Vec<SymbolInformation>>> {
let mark = self.performance.mark("symbol", Some(&params)); let mark = self.performance.mark("symbol", Some(&params));
@ -2487,17 +2534,17 @@ impl tower_lsp::LanguageServer for LanguageServer {
&self, &self,
params: InitializeParams, params: InitializeParams,
) -> LspResult<InitializeResult> { ) -> LspResult<InitializeResult> {
let mut language_server = self.0.lock().await; let mut language_server = self.0.write().await;
language_server.diagnostics_server.start(); language_server.diagnostics_server.start();
language_server.initialize(params).await language_server.initialize(params).await
} }
async fn initialized(&self, params: InitializedParams) { async fn initialized(&self, params: InitializedParams) {
self.0.lock().await.initialized(params).await self.0.write().await.initialized(params).await
} }
async fn shutdown(&self) -> LspResult<()> { async fn shutdown(&self) -> LspResult<()> {
self.0.lock().await.shutdown().await self.0.write().await.shutdown().await
} }
async fn did_open(&self, params: DidOpenTextDocumentParams) { async fn did_open(&self, params: DidOpenTextDocumentParams) {
@ -2509,7 +2556,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
} }
let (client, uri, specifier, had_specifier_settings) = { let (client, uri, specifier, had_specifier_settings) = {
let mut inner = self.0.lock().await; let mut inner = self.0.write().await;
let client = inner.client.clone(); let client = inner.client.clone();
let uri = params.text_document.uri.clone(); let uri = params.text_document.uri.clone();
let specifier = inner.url_map.normalize_url(&uri); let specifier = inner.url_map.normalize_url(&uri);
@ -2535,7 +2582,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
let language_server = self.clone(); let language_server = self.clone();
tokio::spawn(async move { tokio::spawn(async move {
let response = client.specifier_configuration(&uri).await; let response = client.specifier_configuration(&uri).await;
let mut inner = language_server.0.lock().await; let mut inner = language_server.0.write().await;
match response { match response {
Ok(specifier_settings) => { Ok(specifier_settings) => {
// now update the config and send a diagnostics update // now update the config and send a diagnostics update
@ -2563,7 +2610,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
} }
async fn did_change(&self, params: DidChangeTextDocumentParams) { async fn did_change(&self, params: DidChangeTextDocumentParams) {
self.0.lock().await.did_change(params).await self.0.write().await.did_change(params).await
} }
async fn did_save(&self, _params: DidSaveTextDocumentParams) { async fn did_save(&self, _params: DidSaveTextDocumentParams) {
@ -2572,7 +2619,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
} }
async fn did_close(&self, params: DidCloseTextDocumentParams) { async fn did_close(&self, params: DidCloseTextDocumentParams) {
self.0.lock().await.did_close(params).await self.0.write().await.did_close(params).await
} }
async fn did_change_configuration( async fn did_change_configuration(
@ -2580,7 +2627,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
params: DidChangeConfigurationParams, params: DidChangeConfigurationParams,
) { ) {
let (has_workspace_capability, client, specifiers, mark) = { let (has_workspace_capability, client, specifiers, mark) = {
let inner = self.0.lock().await; let inner = self.0.write().await;
let mark = inner let mark = inner
.performance .performance
.mark("did_change_configuration", Some(&params)); .mark("did_change_configuration", Some(&params));
@ -2611,7 +2658,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
) )
.await .await
{ {
let mut inner = language_server.0.lock().await; let mut inner = language_server.0.write().await;
for (i, value) in configs.into_iter().enumerate() { for (i, value) in configs.into_iter().enumerate() {
match value { match value {
Ok(specifier_settings) => { Ok(specifier_settings) => {
@ -2628,7 +2675,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
} }
} }
} }
let mut ls = language_server.0.lock().await; let mut ls = language_server.0.write().await;
if ls.config.update_enabled_paths(client).await { if ls.config.update_enabled_paths(client).await {
ls.diagnostics_server.invalidate_all(); ls.diagnostics_server.invalidate_all();
// this will be called in the inner did_change_configuration, but the // this will be called in the inner did_change_configuration, but the
@ -2661,7 +2708,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
}; };
// now update the inner state // now update the inner state
let mut inner = self.0.lock().await; let mut inner = self.0.write().await;
inner inner
.did_change_configuration(client_workspace_config, params) .did_change_configuration(client_workspace_config, params)
.await; .await;
@ -2672,7 +2719,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
&self, &self,
params: DidChangeWatchedFilesParams, params: DidChangeWatchedFilesParams,
) { ) {
self.0.lock().await.did_change_watched_files(params).await self.0.write().await.did_change_watched_files(params).await
} }
async fn did_change_workspace_folders( async fn did_change_workspace_folders(
@ -2680,13 +2727,13 @@ impl tower_lsp::LanguageServer for LanguageServer {
params: DidChangeWorkspaceFoldersParams, params: DidChangeWorkspaceFoldersParams,
) { ) {
let client = { let client = {
let mut inner = self.0.lock().await; let mut inner = self.0.write().await;
inner.did_change_workspace_folders(params).await; inner.did_change_workspace_folders(params).await;
inner.client.clone() inner.client.clone()
}; };
let language_server = self.clone(); let language_server = self.clone();
tokio::spawn(async move { tokio::spawn(async move {
let mut ls = language_server.0.lock().await; let mut ls = language_server.0.write().await;
if ls.config.update_enabled_paths(client).await { if ls.config.update_enabled_paths(client).await {
ls.diagnostics_server.invalidate_all(); ls.diagnostics_server.invalidate_all();
ls.send_diagnostics_update(); ls.send_diagnostics_update();
@ -2698,193 +2745,178 @@ impl tower_lsp::LanguageServer for LanguageServer {
&self, &self,
params: DocumentSymbolParams, params: DocumentSymbolParams,
) -> LspResult<Option<DocumentSymbolResponse>> { ) -> LspResult<Option<DocumentSymbolResponse>> {
self.0.lock().await.document_symbol(params).await self.0.read().await.document_symbol(params).await
} }
async fn formatting( async fn formatting(
&self, &self,
params: DocumentFormattingParams, params: DocumentFormattingParams,
) -> LspResult<Option<Vec<TextEdit>>> { ) -> LspResult<Option<Vec<TextEdit>>> {
self.0.lock().await.formatting(params).await self.0.read().await.formatting(params).await
} }
async fn hover(&self, params: HoverParams) -> LspResult<Option<Hover>> { async fn hover(&self, params: HoverParams) -> LspResult<Option<Hover>> {
self.0.lock().await.hover(params).await self.0.read().await.hover(params).await
} }
async fn code_action( async fn code_action(
&self, &self,
params: CodeActionParams, params: CodeActionParams,
) -> LspResult<Option<CodeActionResponse>> { ) -> LspResult<Option<CodeActionResponse>> {
self.0.lock().await.code_action(params).await self.0.read().await.code_action(params).await
} }
async fn code_action_resolve( async fn code_action_resolve(
&self, &self,
params: CodeAction, params: CodeAction,
) -> LspResult<CodeAction> { ) -> LspResult<CodeAction> {
self.0.lock().await.code_action_resolve(params).await self.0.read().await.code_action_resolve(params).await
} }
async fn code_lens( async fn code_lens(
&self, &self,
params: CodeLensParams, params: CodeLensParams,
) -> LspResult<Option<Vec<CodeLens>>> { ) -> LspResult<Option<Vec<CodeLens>>> {
self.0.lock().await.code_lens(params).await self.0.read().await.code_lens(params).await
} }
async fn code_lens_resolve(&self, params: CodeLens) -> LspResult<CodeLens> { async fn code_lens_resolve(&self, params: CodeLens) -> LspResult<CodeLens> {
self.0.lock().await.code_lens_resolve(params).await self.0.read().await.code_lens_resolve(params).await
} }
async fn document_highlight( async fn document_highlight(
&self, &self,
params: DocumentHighlightParams, params: DocumentHighlightParams,
) -> LspResult<Option<Vec<DocumentHighlight>>> { ) -> LspResult<Option<Vec<DocumentHighlight>>> {
self.0.lock().await.document_highlight(params).await self.0.read().await.document_highlight(params).await
} }
async fn references( async fn references(
&self, &self,
params: ReferenceParams, params: ReferenceParams,
) -> LspResult<Option<Vec<Location>>> { ) -> LspResult<Option<Vec<Location>>> {
self.0.lock().await.references(params).await self.0.read().await.references(params).await
} }
async fn goto_definition( async fn goto_definition(
&self, &self,
params: GotoDefinitionParams, params: GotoDefinitionParams,
) -> LspResult<Option<GotoDefinitionResponse>> { ) -> LspResult<Option<GotoDefinitionResponse>> {
self.0.lock().await.goto_definition(params).await self.0.read().await.goto_definition(params).await
} }
async fn goto_type_definition( async fn goto_type_definition(
&self, &self,
params: GotoTypeDefinitionParams, params: GotoTypeDefinitionParams,
) -> LspResult<Option<GotoTypeDefinitionResponse>> { ) -> LspResult<Option<GotoTypeDefinitionResponse>> {
self.0.lock().await.goto_type_definition(params).await self.0.read().await.goto_type_definition(params).await
} }
async fn completion( async fn completion(
&self, &self,
params: CompletionParams, params: CompletionParams,
) -> LspResult<Option<CompletionResponse>> { ) -> LspResult<Option<CompletionResponse>> {
self.0.lock().await.completion(params).await self.0.read().await.completion(params).await
} }
async fn completion_resolve( async fn completion_resolve(
&self, &self,
params: CompletionItem, params: CompletionItem,
) -> LspResult<CompletionItem> { ) -> LspResult<CompletionItem> {
self.0.lock().await.completion_resolve(params).await self.0.read().await.completion_resolve(params).await
} }
async fn goto_implementation( async fn goto_implementation(
&self, &self,
params: GotoImplementationParams, params: GotoImplementationParams,
) -> LspResult<Option<GotoImplementationResponse>> { ) -> LspResult<Option<GotoImplementationResponse>> {
self.0.lock().await.goto_implementation(params).await self.0.read().await.goto_implementation(params).await
} }
async fn folding_range( async fn folding_range(
&self, &self,
params: FoldingRangeParams, params: FoldingRangeParams,
) -> LspResult<Option<Vec<FoldingRange>>> { ) -> LspResult<Option<Vec<FoldingRange>>> {
self.0.lock().await.folding_range(params).await self.0.read().await.folding_range(params).await
} }
async fn incoming_calls( async fn incoming_calls(
&self, &self,
params: CallHierarchyIncomingCallsParams, params: CallHierarchyIncomingCallsParams,
) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> { ) -> LspResult<Option<Vec<CallHierarchyIncomingCall>>> {
self.0.lock().await.incoming_calls(params).await self.0.read().await.incoming_calls(params).await
} }
async fn outgoing_calls( async fn outgoing_calls(
&self, &self,
params: CallHierarchyOutgoingCallsParams, params: CallHierarchyOutgoingCallsParams,
) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> { ) -> LspResult<Option<Vec<CallHierarchyOutgoingCall>>> {
self.0.lock().await.outgoing_calls(params).await self.0.read().await.outgoing_calls(params).await
} }
async fn prepare_call_hierarchy( async fn prepare_call_hierarchy(
&self, &self,
params: CallHierarchyPrepareParams, params: CallHierarchyPrepareParams,
) -> LspResult<Option<Vec<CallHierarchyItem>>> { ) -> LspResult<Option<Vec<CallHierarchyItem>>> {
self.0.lock().await.prepare_call_hierarchy(params).await self.0.read().await.prepare_call_hierarchy(params).await
} }
async fn rename( async fn rename(
&self, &self,
params: RenameParams, params: RenameParams,
) -> LspResult<Option<WorkspaceEdit>> { ) -> LspResult<Option<WorkspaceEdit>> {
self.0.lock().await.rename(params).await self.0.read().await.rename(params).await
} }
async fn selection_range( async fn selection_range(
&self, &self,
params: SelectionRangeParams, params: SelectionRangeParams,
) -> LspResult<Option<Vec<SelectionRange>>> { ) -> LspResult<Option<Vec<SelectionRange>>> {
self.0.lock().await.selection_range(params).await self.0.read().await.selection_range(params).await
} }
async fn semantic_tokens_full( async fn semantic_tokens_full(
&self, &self,
params: SemanticTokensParams, params: SemanticTokensParams,
) -> LspResult<Option<SemanticTokensResult>> { ) -> LspResult<Option<SemanticTokensResult>> {
self.0.lock().await.semantic_tokens_full(params).await self.0.read().await.semantic_tokens_full(params).await
} }
async fn semantic_tokens_range( async fn semantic_tokens_range(
&self, &self,
params: SemanticTokensRangeParams, params: SemanticTokensRangeParams,
) -> LspResult<Option<SemanticTokensRangeResult>> { ) -> LspResult<Option<SemanticTokensRangeResult>> {
self.0.lock().await.semantic_tokens_range(params).await self.0.read().await.semantic_tokens_range(params).await
} }
async fn signature_help( async fn signature_help(
&self, &self,
params: SignatureHelpParams, params: SignatureHelpParams,
) -> LspResult<Option<SignatureHelp>> { ) -> LspResult<Option<SignatureHelp>> {
self.0.lock().await.signature_help(params).await self.0.read().await.signature_help(params).await
} }
async fn symbol( async fn symbol(
&self, &self,
params: WorkspaceSymbolParams, params: WorkspaceSymbolParams,
) -> LspResult<Option<Vec<SymbolInformation>>> { ) -> LspResult<Option<Vec<SymbolInformation>>> {
self.0.lock().await.symbol(params).await self.0.read().await.symbol(params).await
} }
} }
struct PrepareCacheResult {
cli_options: CliOptions,
roots: Vec<(ModuleSpecifier, ModuleKind)>,
open_docs: Vec<Document>,
mark: PerformanceMark,
}
// These are implementations of custom commands supported by the LSP // These are implementations of custom commands supported by the LSP
impl Inner { impl Inner {
/// Similar to `deno cache` on the command line, where modules will be cached fn prepare_cache(
/// in the Deno cache, including any of their dependencies. &self,
async fn cache(
&mut self,
params: lsp_custom::CacheParams, params: lsp_custom::CacheParams,
) -> LspResult<Option<Value>> { ) -> LspResult<Option<PrepareCacheResult>> {
async fn create_graph_for_caching(
cli_options: CliOptions,
roots: Vec<(ModuleSpecifier, ModuleKind)>,
open_docs: Vec<Document>,
) -> Result<(), AnyError> {
let open_docs = open_docs
.into_iter()
.map(|d| (d.specifier().clone(), d))
.collect::<HashMap<_, _>>();
let ps = ProcState::from_options(Arc::new(cli_options)).await?;
let mut inner_loader = ps.create_graph_loader();
let mut loader = crate::lsp::documents::OpenDocumentsGraphLoader {
inner_loader: &mut inner_loader,
open_docs: &open_docs,
};
let graph = ps.create_graph_with_loader(roots, &mut loader).await?;
graph_valid(&graph, true, false)?;
Ok(())
}
let referrer = self.url_map.normalize_url(&params.referrer.uri); let referrer = self.url_map.normalize_url(&params.referrer.uri);
if !self.is_diagnosable(&referrer) { if !self.is_diagnosable(&referrer) {
return Ok(None); return Ok(None);
@ -2903,7 +2935,7 @@ impl Inner {
}) })
.collect() .collect()
} else { } else {
vec![(referrer.clone(), deno_graph::ModuleKind::Esm)] vec![(referrer, deno_graph::ModuleKind::Esm)]
}; };
let mut cli_options = CliOptions::new( let mut cli_options = CliOptions::new(
@ -2923,23 +2955,20 @@ impl Inner {
); );
cli_options.set_import_map_specifier(self.maybe_import_map_uri.clone()); cli_options.set_import_map_specifier(self.maybe_import_map_uri.clone());
// todo(dsherret): why is running this on a new thread necessary? It does
// a compile error otherwise.
let open_docs = self.documents.documents(true, true); let open_docs = self.documents.documents(true, true);
let handle = tokio::task::spawn_blocking(|| { Ok(Some(PrepareCacheResult {
run_local(async move { cli_options,
create_graph_for_caching(cli_options, roots, open_docs).await open_docs,
}) roots,
}); mark,
if let Err(err) = handle.await.unwrap() { }))
self.client.show_message(MessageType::WARNING, err).await; }
}
async fn post_cache(&self, mark: PerformanceMark) {
// Now that we have dependencies loaded, we need to re-analyze all the files. // Now that we have dependencies loaded, we need to re-analyze all the files.
// For that we're invalidating all the existing diagnostics and restarting // For that we're invalidating all the existing diagnostics and restarting
// the language server for TypeScript (as it might hold to some stale // the language server for TypeScript (as it might hold to some stale
// documents). // documents).
self.refresh_npm_specifiers().await;
self.diagnostics_server.invalidate_all(); self.diagnostics_server.invalidate_all();
let _: bool = self let _: bool = self
.ts_server .ts_server
@ -2950,7 +2979,6 @@ impl Inner {
self.send_testing_update(); self.send_testing_update();
self.performance.measure(mark); self.performance.measure(mark);
Ok(Some(json!(true)))
} }
fn get_performance(&self) -> Value { fn get_performance(&self) -> Value {
@ -3026,7 +3054,7 @@ impl Inner {
} }
fn virtual_text_document( fn virtual_text_document(
&mut self, &self,
params: lsp_custom::VirtualTextDocumentParams, params: lsp_custom::VirtualTextDocumentParams,
) -> LspResult<Option<String>> { ) -> LspResult<Option<String>> {
let mark = self let mark = self

View file

@ -907,7 +907,7 @@ pub struct NavigateToItem {
impl NavigateToItem { impl NavigateToItem {
pub fn to_symbol_information( pub fn to_symbol_information(
&self, &self,
language_server: &mut language_server::Inner, language_server: &language_server::Inner,
) -> Option<lsp::SymbolInformation> { ) -> Option<lsp::SymbolInformation> {
let specifier = normalize_specifier(&self.file_name).ok()?; let specifier = normalize_specifier(&self.file_name).ok()?;
let asset_or_doc = let asset_or_doc =

View file

@ -52,6 +52,8 @@ pub trait InnerNpmPackageResolver: Send + Sync {
packages: HashSet<NpmPackageReq>, packages: HashSet<NpmPackageReq>,
) -> BoxFuture<'static, Result<(), AnyError>>; ) -> BoxFuture<'static, Result<(), AnyError>>;
fn cache_packages(&self) -> BoxFuture<'static, Result<(), AnyError>>;
fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError>; fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError>;
fn snapshot(&self) -> NpmResolutionSnapshot; fn snapshot(&self) -> NpmResolutionSnapshot;

View file

@ -138,11 +138,7 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver {
packages: Vec<NpmPackageReq>, packages: Vec<NpmPackageReq>,
) -> BoxFuture<'static, Result<(), AnyError>> { ) -> BoxFuture<'static, Result<(), AnyError>> {
let resolver = self.clone(); let resolver = self.clone();
async move { async move { resolver.resolution.add_package_reqs(packages).await }.boxed()
resolver.resolution.add_package_reqs(packages).await?;
cache_packages_in_resolver(&resolver).await
}
.boxed()
} }
fn set_package_reqs( fn set_package_reqs(
@ -150,11 +146,12 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver {
packages: HashSet<NpmPackageReq>, packages: HashSet<NpmPackageReq>,
) -> BoxFuture<'static, Result<(), AnyError>> { ) -> BoxFuture<'static, Result<(), AnyError>> {
let resolver = self.clone(); let resolver = self.clone();
async move { async move { resolver.resolution.set_package_reqs(packages).await }.boxed()
resolver.resolution.set_package_reqs(packages).await?; }
cache_packages_in_resolver(&resolver).await
} fn cache_packages(&self) -> BoxFuture<'static, Result<(), AnyError>> {
.boxed() let resolver = self.clone();
async move { cache_packages_in_resolver(&resolver).await }.boxed()
} }
fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError> { fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError> {

View file

@ -219,7 +219,6 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
let resolver = self.clone(); let resolver = self.clone();
async move { async move {
resolver.resolution.add_package_reqs(packages).await?; resolver.resolution.add_package_reqs(packages).await?;
sync_resolver_with_fs(&resolver).await?;
Ok(()) Ok(())
} }
.boxed() .boxed()
@ -232,6 +231,14 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
let resolver = self.clone(); let resolver = self.clone();
async move { async move {
resolver.resolution.set_package_reqs(packages).await?; resolver.resolution.set_package_reqs(packages).await?;
Ok(())
}
.boxed()
}
fn cache_packages(&self) -> BoxFuture<'static, Result<(), AnyError>> {
let resolver = self.clone();
async move {
sync_resolver_with_fs(&resolver).await?; sync_resolver_with_fs(&resolver).await?;
Ok(()) Ok(())
} }

View file

@ -276,6 +276,7 @@ impl NpmPackageResolver {
} }
self.inner.add_package_reqs(packages).await?; self.inner.add_package_reqs(packages).await?;
self.inner.cache_packages().await?;
// If there's a lock file, update it with all discovered npm packages // If there's a lock file, update it with all discovered npm packages
if let Some(lockfile_mutex) = &self.maybe_lockfile { if let Some(lockfile_mutex) = &self.maybe_lockfile {
@ -287,6 +288,8 @@ impl NpmPackageResolver {
} }
/// Sets package requirements to the resolver, removing old requirements and adding new ones. /// Sets package requirements to the resolver, removing old requirements and adding new ones.
///
/// This will retrieve and resolve package information, but not cache any package files.
pub async fn set_package_reqs( pub async fn set_package_reqs(
&self, &self,
packages: HashSet<NpmPackageReq>, packages: HashSet<NpmPackageReq>,