Skip to content

Commit dd71d54

Browse files
feat: Use builder pattern and log unhandled notifications (#84)
Potentially fixes #83 Prior to this, any unhandled notification not starting with `$` would have caused the main loop to exit with error resulting in server to crash. Moreover, this unhandled_notification handler only works with builder pattern which imo is cleaner and offers ability to separate out implementation of various requests into its own files (i'll maybe do that someday).
1 parent a15cfb7 commit dd71d54

File tree

5 files changed

+106
-72
lines changed

5 files changed

+106
-72
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "protols"
33
description = "Language server for proto3 files"
4-
version = "0.12.5"
4+
version = "0.12.6"
55
edition = "2024"
66
license = "MIT"
77
homepage = "https://github.com/coder3101/protols"
@@ -12,8 +12,8 @@ keywords = ["lsp", "proto"]
1212
exclude = ["assets/*", "sample/*"]
1313

1414
[dependencies]
15-
async-lsp = { version = "0.2.0", features = ["tokio"] }
16-
futures = "0.3.30"
15+
async-lsp = { version = "0.2.2", features = ["tokio"] }
16+
futures = "0.3.31"
1717
tokio = { version = "1.38.0", features = ["time", "full"] }
1818
tokio-util = { version = "0.7.11", features = ["compat"] }
1919
tower = "0.5.2"

src/lsp.rs

Lines changed: 54 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,34 +4,30 @@ use tracing::{error, info};
44

55
use async_lsp::lsp_types::{
66
CompletionItem, CompletionItemKind, CompletionOptions, CompletionParams, CompletionResponse,
7-
CreateFilesParams, DeleteFilesParams, DidChangeConfigurationParams,
8-
DidChangeTextDocumentParams, DidChangeWorkspaceFoldersParams, DidCloseTextDocumentParams,
9-
DidOpenTextDocumentParams, DidSaveTextDocumentParams, DocumentFormattingParams,
10-
DocumentRangeFormattingParams, DocumentSymbolParams, DocumentSymbolResponse, Documentation,
11-
FileOperationFilter, FileOperationPattern, FileOperationPatternKind,
12-
FileOperationRegistrationOptions, GotoDefinitionParams, GotoDefinitionResponse, Hover,
13-
HoverContents, HoverParams, HoverProviderCapability, InitializeParams, InitializeResult,
14-
Location, MarkupContent, MarkupKind, OneOf, PrepareRenameResponse, ReferenceParams,
15-
RenameFilesParams, RenameOptions, RenameParams, ServerCapabilities, ServerInfo,
16-
TextDocumentPositionParams, TextDocumentSyncCapability, TextDocumentSyncKind, TextEdit, Url,
17-
WorkspaceEdit, WorkspaceFileOperationsServerCapabilities, WorkspaceFoldersServerCapabilities,
7+
CreateFilesParams, DeleteFilesParams, DidChangeTextDocumentParams, DidOpenTextDocumentParams,
8+
DidSaveTextDocumentParams, DocumentFormattingParams, DocumentRangeFormattingParams,
9+
DocumentSymbolParams, DocumentSymbolResponse, Documentation, FileOperationFilter,
10+
FileOperationPattern, FileOperationPatternKind, FileOperationRegistrationOptions,
11+
GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams,
12+
HoverProviderCapability, InitializeParams, InitializeResult, Location, MarkupContent,
13+
MarkupKind, OneOf, PrepareRenameResponse, ReferenceParams, RenameFilesParams, RenameOptions,
14+
RenameParams, ServerCapabilities, ServerInfo, TextDocumentPositionParams,
15+
TextDocumentSyncCapability, TextDocumentSyncKind, TextEdit, Url, WorkspaceEdit,
16+
WorkspaceFileOperationsServerCapabilities, WorkspaceFoldersServerCapabilities,
1817
WorkspaceServerCapabilities,
1918
};
20-
use async_lsp::{LanguageClient, LanguageServer, ResponseError};
19+
use async_lsp::{LanguageClient, ResponseError};
2120
use futures::future::BoxFuture;
2221

2322
use crate::docs;
2423
use crate::formatter::ProtoFormatter;
2524
use crate::server::ProtoLanguageServer;
2625

27-
impl LanguageServer for ProtoLanguageServer {
28-
type Error = ResponseError;
29-
type NotifyResult = ControlFlow<async_lsp::Result<()>>;
30-
31-
fn initialize(
26+
impl ProtoLanguageServer {
27+
pub(super) fn initialize(
3228
&mut self,
3329
params: InitializeParams,
34-
) -> BoxFuture<'static, Result<InitializeResult, Self::Error>> {
30+
) -> BoxFuture<'static, Result<InitializeResult, ResponseError>> {
3531
let (cname, version) = params
3632
.client_info
3733
.as_ref()
@@ -122,10 +118,10 @@ impl LanguageServer for ProtoLanguageServer {
122118
Box::pin(async move { Ok(response) })
123119
}
124120

125-
fn hover(
121+
pub(super) fn hover(
126122
&mut self,
127123
param: HoverParams,
128-
) -> BoxFuture<'static, Result<Option<Hover>, Self::Error>> {
124+
) -> BoxFuture<'static, Result<Option<Hover>, ResponseError>> {
129125
let uri = param.text_document_position_params.text_document.uri;
130126
let pos = param.text_document_position_params.position;
131127

@@ -154,10 +150,10 @@ impl LanguageServer for ProtoLanguageServer {
154150
})
155151
}
156152

157-
fn completion(
153+
pub(super) fn completion(
158154
&mut self,
159155
params: CompletionParams,
160-
) -> BoxFuture<'static, Result<Option<CompletionResponse>, Self::Error>> {
156+
) -> BoxFuture<'static, Result<Option<CompletionResponse>, ResponseError>> {
161157
let uri = params.text_document_position.text_document.uri;
162158

163159
// All keywords in the language
@@ -203,10 +199,10 @@ impl LanguageServer for ProtoLanguageServer {
203199
Box::pin(async move { Ok(Some(CompletionResponse::Array(completions))) })
204200
}
205201

206-
fn prepare_rename(
202+
pub(super) fn prepare_rename(
207203
&mut self,
208204
params: TextDocumentPositionParams,
209-
) -> BoxFuture<'static, Result<Option<PrepareRenameResponse>, Self::Error>> {
205+
) -> BoxFuture<'static, Result<Option<PrepareRenameResponse>, ResponseError>> {
210206
let uri = params.text_document.uri;
211207
let pos = params.position;
212208

@@ -220,10 +216,10 @@ impl LanguageServer for ProtoLanguageServer {
220216
Box::pin(async move { Ok(response) })
221217
}
222218

223-
fn rename(
219+
pub(super) fn rename(
224220
&mut self,
225221
params: RenameParams,
226-
) -> BoxFuture<'static, Result<Option<WorkspaceEdit>, Self::Error>> {
222+
) -> BoxFuture<'static, Result<Option<WorkspaceEdit>, ResponseError>> {
227223
let uri = params.text_document_position.text_document.uri;
228224
let pos = params.text_document_position.position;
229225

@@ -271,7 +267,7 @@ impl LanguageServer for ProtoLanguageServer {
271267
Box::pin(async move { Ok(response) })
272268
}
273269

274-
fn references(
270+
pub(super) fn references(
275271
&mut self,
276272
param: ReferenceParams,
277273
) -> BoxFuture<'static, Result<Option<Vec<Location>>, ResponseError>> {
@@ -318,7 +314,7 @@ impl LanguageServer for ProtoLanguageServer {
318314
})
319315
}
320316

321-
fn definition(
317+
pub(super) fn definition(
322318
&mut self,
323319
param: GotoDefinitionParams,
324320
) -> BoxFuture<'static, Result<Option<GotoDefinitionResponse>, ResponseError>> {
@@ -353,10 +349,10 @@ impl LanguageServer for ProtoLanguageServer {
353349
Box::pin(async move { Ok(response) })
354350
}
355351

356-
fn document_symbol(
352+
pub(super) fn document_symbol(
357353
&mut self,
358354
params: DocumentSymbolParams,
359-
) -> BoxFuture<'static, Result<Option<DocumentSymbolResponse>, Self::Error>> {
355+
) -> BoxFuture<'static, Result<Option<DocumentSymbolResponse>, ResponseError>> {
360356
let uri = params.text_document.uri;
361357

362358
let Some(tree) = self.state.get_tree(&uri) else {
@@ -371,10 +367,10 @@ impl LanguageServer for ProtoLanguageServer {
371367
Box::pin(async move { Ok(Some(response)) })
372368
}
373369

374-
fn formatting(
370+
pub(super) fn formatting(
375371
&mut self,
376372
params: DocumentFormattingParams,
377-
) -> BoxFuture<'static, Result<Option<Vec<TextEdit>>, Self::Error>> {
373+
) -> BoxFuture<'static, Result<Option<Vec<TextEdit>>, ResponseError>> {
378374
let uri = params.text_document.uri;
379375
let content = self.state.get_content(&uri);
380376

@@ -386,10 +382,10 @@ impl LanguageServer for ProtoLanguageServer {
386382
Box::pin(async move { Ok(response) })
387383
}
388384

389-
fn range_formatting(
385+
pub(super) fn range_formatting(
390386
&mut self,
391387
params: DocumentRangeFormattingParams,
392-
) -> BoxFuture<'static, Result<Option<Vec<TextEdit>>, Self::Error>> {
388+
) -> BoxFuture<'static, Result<Option<Vec<TextEdit>>, ResponseError>> {
393389
let uri = params.text_document.uri;
394390
let content = self.state.get_content(&uri);
395391

@@ -401,7 +397,10 @@ impl LanguageServer for ProtoLanguageServer {
401397
Box::pin(async move { Ok(response) })
402398
}
403399

404-
fn did_save(&mut self, params: DidSaveTextDocumentParams) -> Self::NotifyResult {
400+
pub(super) fn did_save(
401+
&mut self,
402+
params: DidSaveTextDocumentParams,
403+
) -> ControlFlow<async_lsp::Result<()>> {
405404
let uri = params.text_document.uri;
406405
let content = self.state.get_content(&uri);
407406

@@ -424,11 +423,10 @@ impl LanguageServer for ProtoLanguageServer {
424423
ControlFlow::Continue(())
425424
}
426425

427-
fn did_close(&mut self, _params: DidCloseTextDocumentParams) -> Self::NotifyResult {
428-
ControlFlow::Continue(())
429-
}
430-
431-
fn did_open(&mut self, params: DidOpenTextDocumentParams) -> Self::NotifyResult {
426+
pub(super) fn did_open(
427+
&mut self,
428+
params: DidOpenTextDocumentParams,
429+
) -> ControlFlow<async_lsp::Result<()>> {
432430
let uri = params.text_document.uri;
433431
let content = params.text_document.text;
434432

@@ -451,7 +449,10 @@ impl LanguageServer for ProtoLanguageServer {
451449
ControlFlow::Continue(())
452450
}
453451

454-
fn did_change(&mut self, params: DidChangeTextDocumentParams) -> Self::NotifyResult {
452+
pub(super) fn did_change(
453+
&mut self,
454+
params: DidChangeTextDocumentParams,
455+
) -> ControlFlow<async_lsp::Result<()>> {
455456
let uri = params.text_document.uri;
456457
let content = params.content_changes[0].text.clone();
457458

@@ -474,7 +475,10 @@ impl LanguageServer for ProtoLanguageServer {
474475
ControlFlow::Continue(())
475476
}
476477

477-
fn did_create_files(&mut self, params: CreateFilesParams) -> Self::NotifyResult {
478+
pub(super) fn did_create_files(
479+
&mut self,
480+
params: CreateFilesParams,
481+
) -> ControlFlow<async_lsp::Result<()>> {
478482
for file in params.files {
479483
if let Ok(uri) = Url::from_file_path(&file.uri) {
480484
// Safety: The uri is always a file type
@@ -488,7 +492,10 @@ impl LanguageServer for ProtoLanguageServer {
488492
ControlFlow::Continue(())
489493
}
490494

491-
fn did_rename_files(&mut self, params: RenameFilesParams) -> Self::NotifyResult {
495+
pub(super) fn did_rename_files(
496+
&mut self,
497+
params: RenameFilesParams,
498+
) -> ControlFlow<async_lsp::Result<()>> {
492499
for file in params.files {
493500
let Ok(new_uri) = Url::from_file_path(&file.new_uri) else {
494501
error!(uri = file.new_uri, "failed to parse uri");
@@ -505,7 +512,10 @@ impl LanguageServer for ProtoLanguageServer {
505512
ControlFlow::Continue(())
506513
}
507514

508-
fn did_delete_files(&mut self, params: DeleteFilesParams) -> Self::NotifyResult {
515+
pub(super) fn did_delete_files(
516+
&mut self,
517+
params: DeleteFilesParams,
518+
) -> ControlFlow<async_lsp::Result<()>> {
509519
for file in params.files {
510520
if let Ok(uri) = Url::from_file_path(&file.uri) {
511521
self.state.delete_file(&uri);
@@ -515,17 +525,4 @@ impl LanguageServer for ProtoLanguageServer {
515525
}
516526
ControlFlow::Continue(())
517527
}
518-
519-
// Required because of: https://github.com/coder3101/protols/issues/32
520-
fn did_change_configuration(&mut self, _: DidChangeConfigurationParams) -> Self::NotifyResult {
521-
ControlFlow::Continue(())
522-
}
523-
524-
// Required because when jumping to outside the workspace; this is triggered
525-
fn did_change_workspace_folders(
526-
&mut self,
527-
_: DidChangeWorkspaceFoldersParams,
528-
) -> Self::NotifyResult {
529-
ControlFlow::Continue(())
530-
}
531528
}

src/main.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ async fn main() {
3737
let cli = Cli::parse();
3838

3939
let dir = std::env::temp_dir();
40-
eprintln!("Rolling file based logging at directory: {dir:?}");
40+
eprintln!("file logging at directory: {dir:?}");
4141

4242
let file_appender = tracing_appender::rolling::daily(dir.clone(), "protols.log");
4343
let file_appender = tracing_appender::non_blocking(file_appender);
@@ -48,9 +48,10 @@ async fn main() {
4848
.with_writer(file_appender.0)
4949
.init();
5050

51+
tracing::info!("server version: {}", env!("CARGO_PKG_VERSION"));
5152
let (server, _) = async_lsp::MainLoop::new_server(|client| {
5253
tracing::info!("Using CLI options: {:?}", cli);
53-
let server = ProtoLanguageServer::new_router(
54+
let router = ProtoLanguageServer::new_router(
5455
client.clone(),
5556
cli.include_paths
5657
.map(|ic| ic.into_iter().map(std::path::PathBuf::from).collect())
@@ -76,7 +77,7 @@ async fn main() {
7677
.layer(CatchUnwindLayer::default())
7778
.layer(ConcurrencyLayer::default())
7879
.layer(ClientProcessMonitorLayer::new(client.clone()))
79-
.service(server)
80+
.service(router)
8081
});
8182

8283
// Prefer truly asynchronous piped stdin/stdout without blocking tasks.

src/server.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
use async_lsp::{
22
ClientSocket, LanguageClient,
3-
lsp_types::{NumberOrString, ProgressParams, ProgressParamsValue},
3+
lsp_types::{
4+
NumberOrString, ProgressParams, ProgressParamsValue,
5+
notification::{
6+
DidChangeTextDocument, DidCreateFiles, DidDeleteFiles, DidOpenTextDocument,
7+
DidRenameFiles, DidSaveTextDocument,
8+
},
9+
request::{
10+
Completion, DocumentSymbolRequest, Formatting, GotoDefinition, HoverRequest,
11+
Initialize, PrepareRenameRequest, RangeFormatting, References, Rename,
12+
},
13+
},
414
router::Router,
515
};
616
use std::{
@@ -22,19 +32,45 @@ pub struct ProtoLanguageServer {
2232

2333
impl ProtoLanguageServer {
2434
pub fn new_router(client: ClientSocket, cli_include_paths: Vec<PathBuf>) -> Router<Self> {
25-
let mut router = Router::from_language_server(Self {
35+
let mut router = Router::new(Self {
2636
client,
2737
counter: 0,
2838
state: ProtoLanguageState::new(),
2939
configs: WorkspaceProtoConfigs::new(cli_include_paths),
3040
});
31-
router.event(Self::on_tick);
32-
router
33-
}
3441

35-
fn on_tick(&mut self, _: TickEvent) -> ControlFlow<async_lsp::Result<()>> {
36-
self.counter += 1;
37-
ControlFlow::Continue(())
42+
router.event::<TickEvent>(|st, _| {
43+
st.counter += 1;
44+
ControlFlow::Continue(())
45+
});
46+
47+
// Ignore any unknown notification.
48+
router.unhandled_notification(|_, notif| {
49+
tracing::info!(notif.method, "ignored unknown notification");
50+
ControlFlow::Continue(())
51+
});
52+
53+
// Handling request
54+
router.request::<Initialize, _>(|st, params| st.initialize(params));
55+
router.request::<HoverRequest, _>(|st, params| st.hover(params));
56+
router.request::<Completion, _>(|st, params| st.completion(params));
57+
router.request::<PrepareRenameRequest, _>(|st, params| st.prepare_rename(params));
58+
router.request::<Rename, _>(|st, params| st.rename(params));
59+
router.request::<References, _>(|st, params| st.references(params));
60+
router.request::<GotoDefinition, _>(|st, params| st.definition(params));
61+
router.request::<DocumentSymbolRequest, _>(|st, params| st.document_symbol(params));
62+
router.request::<Formatting, _>(|st, params| st.formatting(params));
63+
router.request::<RangeFormatting, _>(|st, params| st.range_formatting(params));
64+
65+
// Handling notification
66+
router.notification::<DidSaveTextDocument>(|st, params| st.did_save(params));
67+
router.notification::<DidOpenTextDocument>(|st, params| st.did_open(params));
68+
router.notification::<DidChangeTextDocument>(|st, params| st.did_change(params));
69+
router.notification::<DidCreateFiles>(|st, params| st.did_create_files(params));
70+
router.notification::<DidRenameFiles>(|st, params| st.did_rename_files(params));
71+
router.notification::<DidDeleteFiles>(|st, params| st.did_delete_files(params));
72+
73+
router
3874
}
3975

4076
pub fn with_report_progress(&self, token: NumberOrString) -> Sender<ProgressParamsValue> {

0 commit comments

Comments
 (0)