1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use lsp_types::InitializeParams;
use std::error::Error;
use tokio::sync::oneshot;
use crate::context;
use crate::util::set_locale;
use super::connection::AsyncConnection;
use super::message_processor::ServerMessageProcessor;
/// LSP Server manages the entire server lifecycle
pub(super) struct LspServer {
pub(super) connection: AsyncConnection,
pub(super) server_context: context::ServerContext,
pub(super) processor: ServerMessageProcessor,
}
impl LspServer {
/// Create a new LSP server instance
pub(super) fn new(
connection: AsyncConnection,
params: &InitializeParams,
init_rx: oneshot::Receiver<()>,
) -> Self {
// **The language, before anything is parsed.** `initialize.locale` is the client's own setting — VS Code
// sends `"locale": "zh-cn"` from `--locale`/the display language — and it is applied here because the very
// first parse error of the session is a message a reader may have to read.
//
// A project's `.cppls.toml` overrides it, and that override is applied when that file is read (see
// `AnalysisState`): the file in the repository is more specific than the editor's global preference.
if let Some(locale) = params.locale.as_deref() {
set_locale(locale);
}
// **Which editor asked**, from `initialize.clientInfo` (and the `--editor` argument, when a caller passed
// one). The capability list cannot answer this: a client names the standard semantic token types it knows,
// so a custom type is never in it even for a client whose theme can draw it.
let client_id = crate::context::get_client_id(¶ms.client_info);
let server_context = context::ServerContext::new(
connection.connection.clone(),
params.capabilities.clone(),
client_id,
);
Self {
connection,
server_context,
processor: ServerMessageProcessor::new(init_rx),
}
}
/// Run the main server loop
pub(super) async fn run(mut self) -> Result<(), Box<dyn Error + Sync + Send>> {
// First, wait for initialization to complete while handling allowed messages
self.wait_for_initialization().await?;
// Process all pending messages after initialization
if self
.processor
.process_pending_messages(&mut self.connection, &mut self.server_context)
.await?
{
self.server_context.close().await;
return Ok(()); // Shutdown requested during pending message processing
}
// Now focus on normal message processing
while let Some(msg) = self.connection.recv().await {
if self
.processor
.process_message(msg, &mut self.connection, &mut self.server_context)
.await?
{
break; // Shutdown requested
}
}
self.server_context.close().await;
Ok(())
}
/// Wait for initialization to complete while handling initialization-allowed messages
async fn wait_for_initialization(&mut self) -> Result<(), Box<dyn Error + Sync + Send>> {
loop {
// Check if initialization is complete
if self.processor.check_initialization_complete()? {
break; // Initialization completed
}
// Use a short timeout to check for messages during initialization
match tokio::time::timeout(
tokio::time::Duration::from_millis(50),
self.connection.recv(),
)
.await
{
Ok(Some(msg)) => {
// Process message if allowed during initialization, otherwise queue it
if self.processor.can_process_during_init(&msg) {
self.processor
.handle_message(msg, &mut self.connection, &mut self.server_context)
.await?;
} else {
self.processor.pending_messages.push(msg);
}
}
Ok(None) => {
// Connection closed during initialization
return Ok(());
}
Err(_) => {
// Timeout - continue checking for initialization completion
continue;
}
}
}
Ok(())
}
}