Skip to content

Commit 0ef3f6b

Browse files
authored
perf(lsp): lazily start the ts server (#28392)
1 parent e579440 commit 0ef3f6b

File tree

4 files changed

+86
-60
lines changed

4 files changed

+86
-60
lines changed

cli/lsp/diagnostics.rs

-1
Original file line numberDiff line numberDiff line change
@@ -2022,7 +2022,6 @@ let c: number = "a";
20222022
.await;
20232023
let snapshot = Arc::new(snapshot);
20242024
let ts_server = TsServer::new(Default::default());
2025-
ts_server.start(None).unwrap();
20262025

20272026
// test enabled
20282027
{

cli/lsp/language_server.rs

+28-22
Original file line numberDiff line numberDiff line change
@@ -213,9 +213,7 @@ pub struct Inner {
213213
registered_semantic_tokens_capabilities: bool,
214214
pub resolver: Arc<LspResolver>,
215215
task_queue: LanguageServerTaskQueue,
216-
/// A memoized version of fixable diagnostic codes retrieved from TypeScript.
217-
ts_fixable_diagnostics: Vec<String>,
218-
/// An abstraction that handles interactions with TypeScript.
216+
ts_fixable_diagnostics: tokio::sync::OnceCell<Vec<String>>,
219217
pub ts_server: Arc<TsServer>,
220218
/// A map of specifiers and URLs used to translate over the LSP.
221219
pub url_map: urls::LspUrlMap,
@@ -621,6 +619,19 @@ impl Inner {
621619
})
622620
}
623621

622+
pub async fn ts_fixable_diagnostics(&self) -> &Vec<String> {
623+
self
624+
.ts_fixable_diagnostics
625+
.get_or_init(|| async {
626+
self
627+
.ts_server
628+
.get_supported_code_fixes(self.snapshot())
629+
.await
630+
.unwrap()
631+
})
632+
.await
633+
}
634+
624635
pub fn update_tracing(&mut self) {
625636
let tracing =
626637
self
@@ -777,7 +788,7 @@ impl Inner {
777788

778789
// lspower::LanguageServer methods. This file's LanguageServer delegates to us.
779790
impl Inner {
780-
async fn initialize(
791+
fn initialize(
781792
&mut self,
782793
params: InitializeParams,
783794
) -> LspResult<InitializeResult> {
@@ -862,26 +873,13 @@ impl Inner {
862873
}
863874

864875
self.diagnostics_server.start();
865-
if let Err(e) = self
876+
self
866877
.ts_server
867-
.start(self.config.internal_inspect().to_address())
868-
{
869-
lsp_warn!("{}", e);
870-
self.client.show_message(MessageType::ERROR, e);
871-
return Err(tower_lsp::jsonrpc::Error::internal_error());
872-
};
878+
.set_inspector_server_addr(self.config.internal_inspect().to_address());
873879

874880
self.update_tracing();
875881
self.update_debug_flag();
876882

877-
if capabilities.code_action_provider.is_some() {
878-
let fixable_diagnostics = self
879-
.ts_server
880-
.get_supported_code_fixes(self.snapshot())
881-
.await?;
882-
self.ts_fixable_diagnostics = fixable_diagnostics;
883-
}
884-
885883
if capabilities.semantic_tokens_provider.is_some() {
886884
self.registered_semantic_tokens_capabilities = true;
887885
}
@@ -1746,6 +1744,7 @@ impl Inner {
17461744
let line_index = asset_or_doc.line_index();
17471745

17481746
// QuickFix
1747+
let ts_fixable_diagnosics = self.ts_fixable_diagnostics().await;
17491748
let fixable_diagnostics: Vec<&Diagnostic> = params
17501749
.context
17511750
.diagnostics
@@ -1754,10 +1753,10 @@ impl Inner {
17541753
Some(source) => match source.as_str() {
17551754
"deno-ts" => match &d.code {
17561755
Some(NumberOrString::String(code)) => {
1757-
self.ts_fixable_diagnostics.contains(code)
1756+
ts_fixable_diagnosics.contains(code)
17581757
}
17591758
Some(NumberOrString::Number(code)) => {
1760-
self.ts_fixable_diagnostics.contains(&code.to_string())
1759+
ts_fixable_diagnosics.contains(&code.to_string())
17611760
}
17621761
_ => false,
17631762
},
@@ -3399,6 +3398,9 @@ impl Inner {
33993398
params: RenameFilesParams,
34003399
token: &CancellationToken,
34013400
) -> LspResult<Option<WorkspaceEdit>> {
3401+
if !self.ts_server.is_started() {
3402+
return Ok(None);
3403+
}
34023404
let mut changes = vec![];
34033405
for rename in params.files {
34043406
let old_specifier = self.url_map.uri_to_specifier(
@@ -3461,6 +3463,10 @@ impl Inner {
34613463
params: WorkspaceSymbolParams,
34623464
token: &CancellationToken,
34633465
) -> LspResult<Option<Vec<SymbolInformation>>> {
3466+
if !self.ts_server.is_started() {
3467+
return Ok(None);
3468+
}
3469+
34643470
let mark = self.performance.mark_with_args("lsp.symbol", &params);
34653471

34663472
let navigate_to_items = self
@@ -3588,7 +3594,7 @@ impl tower_lsp::LanguageServer for LanguageServer {
35883594
&self,
35893595
params: InitializeParams,
35903596
) -> LspResult<InitializeResult> {
3591-
self.inner.write().await.initialize(params).await
3597+
self.inner.write().await.initialize(params)
35923598
}
35933599

35943600
async fn initialized(&self, _: InitializedParams) {

cli/lsp/tsc.rs

+56-35
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use std::thread;
1919
use dashmap::DashMap;
2020
use deno_ast::MediaType;
2121
use deno_core::anyhow::anyhow;
22-
use deno_core::anyhow::Context as _;
2322
use deno_core::convert::Smi;
2423
use deno_core::convert::ToV8;
2524
use deno_core::error::AnyError;
@@ -245,9 +244,11 @@ pub struct TsServer {
245244
sender: mpsc::UnboundedSender<Request>,
246245
receiver: Mutex<Option<mpsc::UnboundedReceiver<Request>>>,
247246
pub specifier_map: Arc<TscSpecifierMap>,
247+
inspector_server_addr: Mutex<Option<String>>,
248248
inspector_server: Mutex<Option<Arc<InspectorServer>>>,
249249
pending_change: Mutex<Option<PendingChange>>,
250250
enable_tracing: Arc<AtomicBool>,
251+
start_once: std::sync::Once,
251252
}
252253

253254
impl std::fmt::Debug for TsServer {
@@ -257,7 +258,9 @@ impl std::fmt::Debug for TsServer {
257258
.field("sender", &self.sender)
258259
.field("receiver", &self.receiver)
259260
.field("specifier_map", &self.specifier_map)
261+
.field("inspector_server_addr", &self.inspector_server_addr.lock())
260262
.field("inspector_server", &self.inspector_server.lock().is_some())
263+
.field("start_once", &self.start_once)
261264
.finish()
262265
}
263266
}
@@ -404,9 +407,11 @@ impl TsServer {
404407
sender: tx,
405408
receiver: Mutex::new(Some(request_rx)),
406409
specifier_map: Arc::new(TscSpecifierMap::new()),
410+
inspector_server_addr: Mutex::new(None),
407411
inspector_server: Mutex::new(None),
408412
pending_change: Mutex::new(None),
409413
enable_tracing: Default::default(),
414+
start_once: std::sync::Once::new(),
410415
}
411416
}
412417

@@ -416,40 +421,53 @@ impl TsServer {
416421
.store(enabled, std::sync::atomic::Ordering::Relaxed);
417422
}
418423

419-
pub fn start(
420-
&self,
421-
inspector_server_addr: Option<String>,
422-
) -> Result<(), AnyError> {
423-
let maybe_inspector_server = match inspector_server_addr {
424-
Some(addr) => {
425-
let addr: SocketAddr = addr.parse().with_context(|| {
426-
format!("Invalid inspector server address \"{}\"", &addr)
427-
})?;
428-
let server = InspectorServer::new(addr, "deno-lsp-tsc")?;
429-
Some(Arc::new(server))
430-
}
431-
None => None,
432-
};
433-
self
434-
.inspector_server
435-
.lock()
436-
.clone_from(&maybe_inspector_server);
437-
// TODO(bartlomieju): why is the join_handle ignored here? Should we store it
438-
// on the `TsServer` struct.
439-
let receiver = self.receiver.lock().take().unwrap();
440-
let performance = self.performance.clone();
441-
let specifier_map = self.specifier_map.clone();
442-
let enable_tracing = self.enable_tracing.clone();
443-
let _join_handle = thread::spawn(move || {
444-
run_tsc_thread(
445-
receiver,
446-
performance,
447-
specifier_map,
448-
maybe_inspector_server,
449-
enable_tracing,
450-
)
424+
/// This should be called before `self.ensure_started()`.
425+
pub fn set_inspector_server_addr(&self, addr: Option<String>) {
426+
*self.inspector_server_addr.lock() = addr;
427+
}
428+
429+
pub fn ensure_started(&self) {
430+
self.start_once.call_once(|| {
431+
let maybe_inspector_server = self
432+
.inspector_server_addr
433+
.lock()
434+
.as_ref()
435+
.and_then(|addr| {
436+
addr
437+
.parse::<SocketAddr>()
438+
.inspect_err(|err| {
439+
lsp_warn!("Invalid inspector server address: {:#}", err);
440+
})
441+
.ok()
442+
})
443+
.map(|addr| {
444+
Arc::new(InspectorServer::new(addr, "deno-lsp-tsc").unwrap())
445+
});
446+
self
447+
.inspector_server
448+
.lock()
449+
.clone_from(&maybe_inspector_server);
450+
// TODO(bartlomieju): why is the join_handle ignored here? Should we store it
451+
// on the `TsServer` struct.
452+
let receiver = self.receiver.lock().take().unwrap();
453+
let performance = self.performance.clone();
454+
let specifier_map = self.specifier_map.clone();
455+
let enable_tracing = self.enable_tracing.clone();
456+
let _join_handle = thread::spawn(move || {
457+
run_tsc_thread(
458+
receiver,
459+
performance,
460+
specifier_map,
461+
maybe_inspector_server,
462+
enable_tracing,
463+
)
464+
});
465+
lsp_log!("TS server started.");
451466
});
452-
Ok(())
467+
}
468+
469+
pub fn is_started(&self) -> bool {
470+
self.start_once.is_completed()
453471
}
454472

455473
pub fn project_changed<'a>(
@@ -549,6 +567,9 @@ impl TsServer {
549567

550568
#[cfg_attr(feature = "lsp-tracing", tracing::instrument(skip_all))]
551569
pub async fn cleanup_semantic_cache(&self, snapshot: Arc<StateSnapshot>) {
570+
if !self.is_started() {
571+
return;
572+
}
552573
for scope in snapshot
553574
.config
554575
.tree
@@ -1365,6 +1386,7 @@ impl TsServer {
13651386
R: de::DeserializeOwned,
13661387
{
13671388
use super::trace::SpanExt;
1389+
self.ensure_started();
13681390
let context = super::trace::Span::current().context();
13691391
let mark = self
13701392
.performance
@@ -5772,7 +5794,6 @@ mod tests {
57725794
});
57735795
let performance = Arc::new(Performance::default());
57745796
let ts_server = TsServer::new(performance);
5775-
ts_server.start(None).unwrap();
57765797
ts_server.project_changed(
57775798
snapshot.clone(),
57785799
[],

tests/integration/lsp_tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11488,13 +11488,11 @@ fn lsp_performance() {
1148811488
"lsp.update_diagnostics_ts",
1148911489
"lsp.update_global_cache",
1149011490
"tsc.host.$getDiagnostics",
11491-
"tsc.host.$getSupportedCodeFixes",
1149211491
"tsc.host.getQuickInfoAtPosition",
1149311492
"tsc.op.op_is_node_file",
1149411493
"tsc.op.op_load",
1149511494
"tsc.op.op_script_names",
1149611495
"tsc.request.$getDiagnostics",
11497-
"tsc.request.$getSupportedCodeFixes",
1149811496
"tsc.request.getQuickInfoAtPosition",
1149911497
]
1150011498
);
@@ -15135,6 +15133,7 @@ fn lsp_deno_json_scopes_file_rename_import_edits() {
1513515133
);
1513615134
let mut client = context.new_lsp_command().build();
1513715135
client.initialize_default();
15136+
client.did_open_file(&file1);
1513815137
let res = client.write_request(
1513915138
"workspace/willRenameFiles",
1514015139
json!({
@@ -15395,6 +15394,7 @@ fn lsp_deno_json_scopes_search_symbol() {
1539515394
);
1539615395
let mut client = context.new_lsp_command().build();
1539715396
client.initialize_default();
15397+
client.did_open_file(&file1);
1539815398
let res =
1539915399
client.write_request("workspace/symbol", json!({ "query": "someSymbol" }));
1540015400
assert_eq!(

0 commit comments

Comments
 (0)