0
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-03-09 21:57:40 -04:00

perf(lsp): lazily start the ts server (#28392)

This commit is contained in:
Nayeem Rahman 2025-03-06 18:05:25 +00:00 committed by GitHub
parent e579440170
commit 0ef3f6ba88
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 86 additions and 60 deletions

View file

@ -2022,7 +2022,6 @@ let c: number = "a";
.await;
let snapshot = Arc::new(snapshot);
let ts_server = TsServer::new(Default::default());
ts_server.start(None).unwrap();
// test enabled
{

View file

@ -213,9 +213,7 @@ pub struct Inner {
registered_semantic_tokens_capabilities: bool,
pub resolver: Arc<LspResolver>,
task_queue: LanguageServerTaskQueue,
/// A memoized version of fixable diagnostic codes retrieved from TypeScript.
ts_fixable_diagnostics: Vec<String>,
/// An abstraction that handles interactions with TypeScript.
ts_fixable_diagnostics: tokio::sync::OnceCell<Vec<String>>,
pub ts_server: Arc<TsServer>,
/// A map of specifiers and URLs used to translate over the LSP.
pub url_map: urls::LspUrlMap,
@ -621,6 +619,19 @@ impl Inner {
})
}
pub async fn ts_fixable_diagnostics(&self) -> &Vec<String> {
self
.ts_fixable_diagnostics
.get_or_init(|| async {
self
.ts_server
.get_supported_code_fixes(self.snapshot())
.await
.unwrap()
})
.await
}
pub fn update_tracing(&mut self) {
let tracing =
self
@ -777,7 +788,7 @@ impl Inner {
// lspower::LanguageServer methods. This file's LanguageServer delegates to us.
impl Inner {
async fn initialize(
fn initialize(
&mut self,
params: InitializeParams,
) -> LspResult<InitializeResult> {
@ -862,26 +873,13 @@ impl Inner {
}
self.diagnostics_server.start();
if let Err(e) = self
self
.ts_server
.start(self.config.internal_inspect().to_address())
{
lsp_warn!("{}", e);
self.client.show_message(MessageType::ERROR, e);
return Err(tower_lsp::jsonrpc::Error::internal_error());
};
.set_inspector_server_addr(self.config.internal_inspect().to_address());
self.update_tracing();
self.update_debug_flag();
if capabilities.code_action_provider.is_some() {
let fixable_diagnostics = self
.ts_server
.get_supported_code_fixes(self.snapshot())
.await?;
self.ts_fixable_diagnostics = fixable_diagnostics;
}
if capabilities.semantic_tokens_provider.is_some() {
self.registered_semantic_tokens_capabilities = true;
}
@ -1746,6 +1744,7 @@ impl Inner {
let line_index = asset_or_doc.line_index();
// QuickFix
let ts_fixable_diagnosics = self.ts_fixable_diagnostics().await;
let fixable_diagnostics: Vec<&Diagnostic> = params
.context
.diagnostics
@ -1754,10 +1753,10 @@ impl Inner {
Some(source) => match source.as_str() {
"deno-ts" => match &d.code {
Some(NumberOrString::String(code)) => {
self.ts_fixable_diagnostics.contains(code)
ts_fixable_diagnosics.contains(code)
}
Some(NumberOrString::Number(code)) => {
self.ts_fixable_diagnostics.contains(&code.to_string())
ts_fixable_diagnosics.contains(&code.to_string())
}
_ => false,
},
@ -3399,6 +3398,9 @@ impl Inner {
params: RenameFilesParams,
token: &CancellationToken,
) -> LspResult<Option<WorkspaceEdit>> {
if !self.ts_server.is_started() {
return Ok(None);
}
let mut changes = vec![];
for rename in params.files {
let old_specifier = self.url_map.uri_to_specifier(
@ -3461,6 +3463,10 @@ impl Inner {
params: WorkspaceSymbolParams,
token: &CancellationToken,
) -> LspResult<Option<Vec<SymbolInformation>>> {
if !self.ts_server.is_started() {
return Ok(None);
}
let mark = self.performance.mark_with_args("lsp.symbol", &params);
let navigate_to_items = self
@ -3588,7 +3594,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
&self,
params: InitializeParams,
) -> LspResult<InitializeResult> {
self.inner.write().await.initialize(params).await
self.inner.write().await.initialize(params)
}
async fn initialized(&self, _: InitializedParams) {

View file

@ -19,7 +19,6 @@ use std::thread;
use dashmap::DashMap;
use deno_ast::MediaType;
use deno_core::anyhow::anyhow;
use deno_core::anyhow::Context as _;
use deno_core::convert::Smi;
use deno_core::convert::ToV8;
use deno_core::error::AnyError;
@ -245,9 +244,11 @@ pub struct TsServer {
sender: mpsc::UnboundedSender<Request>,
receiver: Mutex<Option<mpsc::UnboundedReceiver<Request>>>,
pub specifier_map: Arc<TscSpecifierMap>,
inspector_server_addr: Mutex<Option<String>>,
inspector_server: Mutex<Option<Arc<InspectorServer>>>,
pending_change: Mutex<Option<PendingChange>>,
enable_tracing: Arc<AtomicBool>,
start_once: std::sync::Once,
}
impl std::fmt::Debug for TsServer {
@ -257,7 +258,9 @@ impl std::fmt::Debug for TsServer {
.field("sender", &self.sender)
.field("receiver", &self.receiver)
.field("specifier_map", &self.specifier_map)
.field("inspector_server_addr", &self.inspector_server_addr.lock())
.field("inspector_server", &self.inspector_server.lock().is_some())
.field("start_once", &self.start_once)
.finish()
}
}
@ -404,9 +407,11 @@ impl TsServer {
sender: tx,
receiver: Mutex::new(Some(request_rx)),
specifier_map: Arc::new(TscSpecifierMap::new()),
inspector_server_addr: Mutex::new(None),
inspector_server: Mutex::new(None),
pending_change: Mutex::new(None),
enable_tracing: Default::default(),
start_once: std::sync::Once::new(),
}
}
@ -416,40 +421,53 @@ impl TsServer {
.store(enabled, std::sync::atomic::Ordering::Relaxed);
}
pub fn start(
&self,
inspector_server_addr: Option<String>,
) -> Result<(), AnyError> {
let maybe_inspector_server = match inspector_server_addr {
Some(addr) => {
let addr: SocketAddr = addr.parse().with_context(|| {
format!("Invalid inspector server address \"{}\"", &addr)
})?;
let server = InspectorServer::new(addr, "deno-lsp-tsc")?;
Some(Arc::new(server))
}
None => None,
};
self
.inspector_server
.lock()
.clone_from(&maybe_inspector_server);
// TODO(bartlomieju): why is the join_handle ignored here? Should we store it
// on the `TsServer` struct.
let receiver = self.receiver.lock().take().unwrap();
let performance = self.performance.clone();
let specifier_map = self.specifier_map.clone();
let enable_tracing = self.enable_tracing.clone();
let _join_handle = thread::spawn(move || {
run_tsc_thread(
receiver,
performance,
specifier_map,
maybe_inspector_server,
enable_tracing,
)
/// This should be called before `self.ensure_started()`.
pub fn set_inspector_server_addr(&self, addr: Option<String>) {
*self.inspector_server_addr.lock() = addr;
}
pub fn ensure_started(&self) {
self.start_once.call_once(|| {
let maybe_inspector_server = self
.inspector_server_addr
.lock()
.as_ref()
.and_then(|addr| {
addr
.parse::<SocketAddr>()
.inspect_err(|err| {
lsp_warn!("Invalid inspector server address: {:#}", err);
})
.ok()
})
.map(|addr| {
Arc::new(InspectorServer::new(addr, "deno-lsp-tsc").unwrap())
});
self
.inspector_server
.lock()
.clone_from(&maybe_inspector_server);
// TODO(bartlomieju): why is the join_handle ignored here? Should we store it
// on the `TsServer` struct.
let receiver = self.receiver.lock().take().unwrap();
let performance = self.performance.clone();
let specifier_map = self.specifier_map.clone();
let enable_tracing = self.enable_tracing.clone();
let _join_handle = thread::spawn(move || {
run_tsc_thread(
receiver,
performance,
specifier_map,
maybe_inspector_server,
enable_tracing,
)
});
lsp_log!("TS server started.");
});
Ok(())
}
pub fn is_started(&self) -> bool {
self.start_once.is_completed()
}
pub fn project_changed<'a>(
@ -549,6 +567,9 @@ impl TsServer {
#[cfg_attr(feature = "lsp-tracing", tracing::instrument(skip_all))]
pub async fn cleanup_semantic_cache(&self, snapshot: Arc<StateSnapshot>) {
if !self.is_started() {
return;
}
for scope in snapshot
.config
.tree
@ -1365,6 +1386,7 @@ impl TsServer {
R: de::DeserializeOwned,
{
use super::trace::SpanExt;
self.ensure_started();
let context = super::trace::Span::current().context();
let mark = self
.performance
@ -5772,7 +5794,6 @@ mod tests {
});
let performance = Arc::new(Performance::default());
let ts_server = TsServer::new(performance);
ts_server.start(None).unwrap();
ts_server.project_changed(
snapshot.clone(),
[],

View file

@ -11488,13 +11488,11 @@ fn lsp_performance() {
"lsp.update_diagnostics_ts",
"lsp.update_global_cache",
"tsc.host.$getDiagnostics",
"tsc.host.$getSupportedCodeFixes",
"tsc.host.getQuickInfoAtPosition",
"tsc.op.op_is_node_file",
"tsc.op.op_load",
"tsc.op.op_script_names",
"tsc.request.$getDiagnostics",
"tsc.request.$getSupportedCodeFixes",
"tsc.request.getQuickInfoAtPosition",
]
);
@ -15135,6 +15133,7 @@ fn lsp_deno_json_scopes_file_rename_import_edits() {
);
let mut client = context.new_lsp_command().build();
client.initialize_default();
client.did_open_file(&file1);
let res = client.write_request(
"workspace/willRenameFiles",
json!({
@ -15395,6 +15394,7 @@ fn lsp_deno_json_scopes_search_symbol() {
);
let mut client = context.new_lsp_command().build();
client.initialize_default();
client.did_open_file(&file1);
let res =
client.write_request("workspace/symbol", json!({ "query": "someSymbol" }));
assert_eq!(