Skip to content

Commit 5d739fb

Browse files
refactor: reuse code and fix a didClose missing crash (#10)
Some housekeeping!
1 parent 5427b3b commit 5d739fb

File tree

7 files changed

+103
-104
lines changed

7 files changed

+103
-104
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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
[package]
22
name = "protols"
33
description = "Language server for proto3 files"
4-
version = "0.1.1"
4+
version = "0.2.0"
55
edition = "2021"
66
license = "MIT"
77
homepage = "https://github.com/coder3101/protols"
88
repository = "https://github.com/coder3101/protols"
99
readme = "README.md"
1010
keywords = ["lsp", "proto3"]
1111

12-
exclude = ["assets/*"]
12+
exclude = ["assets/*", "sample/*"]
1313

1414
[dependencies]
1515
async-lsp = { version = "0.2.0", features = ["tokio"] }

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ A Language Server for **proto3** files. It uses tree-sitter parser for all opera
77
- [x] Hover
88
- [x] Go to definition
99
- [x] Diagnostics
10-
- [x] Document symbols outline for message and enums
10+
- [x] Document Symbols for message and enums
1111

1212
## Installation
1313

src/simple.proto renamed to sample/simple.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,4 @@ enum EnumSample {
5454
STARTED = 1;
5555
RUNNING = 1;
5656
}
57+

src/lsp.rs

Lines changed: 67 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use std::ops::ControlFlow;
2-
use tracing::{debug, info};
2+
use tracing::{error, info};
33

44
use async_lsp::lsp_types::{
5-
DidChangeTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams,
6-
DocumentSymbolParams, DocumentSymbolResponse, GotoDefinitionParams, GotoDefinitionResponse,
7-
Hover, HoverContents, HoverParams, HoverProviderCapability, InitializeParams, InitializeResult,
8-
OneOf, ServerCapabilities, ServerInfo, TextDocumentSyncCapability, TextDocumentSyncKind,
5+
DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
6+
DidSaveTextDocumentParams, DocumentSymbolParams, DocumentSymbolResponse, GotoDefinitionParams,
7+
GotoDefinitionResponse, Hover, HoverContents, HoverParams, HoverProviderCapability,
8+
InitializeParams, InitializeResult, OneOf, ServerCapabilities, ServerInfo,
9+
TextDocumentSyncCapability, TextDocumentSyncKind,
910
};
10-
use async_lsp::{ErrorCode, LanguageClient, LanguageServer, ResponseError};
11+
use async_lsp::{LanguageClient, LanguageServer, ResponseError};
1112
use futures::future::BoxFuture;
1213

1314
use crate::server::ServerState;
@@ -29,7 +30,6 @@ impl LanguageServer for ServerState {
2930
let cversion = version.unwrap_or("<unknown>");
3031

3132
info!("Connected with client {cname} {cversion}");
32-
debug!("Initialize with {params:?}");
3333

3434
let response = InitializeResult {
3535
capabilities: ServerCapabilities {
@@ -58,39 +58,26 @@ impl LanguageServer for ServerState {
5858
let uri = param.text_document_position_params.text_document.uri;
5959
let pos = param.text_document_position_params.position;
6060

61-
let Some(contents) = self.documents.get(&uri) else {
62-
return Box::pin(async move {
63-
Err(ResponseError::new(
64-
ErrorCode::INVALID_REQUEST,
65-
"uri was never opened",
66-
))
67-
});
68-
};
69-
70-
let Some(parsed) = self.parser.parse(contents.as_bytes()) else {
71-
return Box::pin(async move {
72-
Err(ResponseError::new(
73-
ErrorCode::REQUEST_FAILED,
74-
"ts failed to parse contents",
75-
))
76-
});
77-
};
78-
79-
let comments = parsed.hover(&pos, contents.as_bytes());
80-
info!("Found {} node comments in the document", comments.len());
81-
let response = match comments.len() {
82-
0 => None,
83-
1 => Some(Hover {
84-
contents: HoverContents::Scalar(comments[0].clone()),
85-
range: None,
86-
}),
87-
2.. => Some(Hover {
88-
contents: HoverContents::Array(comments),
89-
range: None,
90-
}),
91-
};
92-
93-
Box::pin(async move { Ok(response) })
61+
match self.get_parsed_tree_and_content(&uri) {
62+
Err(e) => Box::pin(async move { Err(e) }),
63+
Ok((tree, content)) => {
64+
let comments = tree.hover(&pos, content.as_bytes());
65+
66+
let response = match comments.len() {
67+
0 => None,
68+
1 => Some(Hover {
69+
contents: HoverContents::Scalar(comments[0].clone()),
70+
range: None,
71+
}),
72+
2.. => Some(Hover {
73+
contents: HoverContents::Array(comments),
74+
range: None,
75+
}),
76+
};
77+
78+
Box::pin(async move { Ok(response) })
79+
}
80+
}
9481
}
9582

9683
fn definition(
@@ -100,34 +87,20 @@ impl LanguageServer for ServerState {
10087
let uri = param.text_document_position_params.text_document.uri;
10188
let pos = param.text_document_position_params.position;
10289

103-
let Some(contents) = self.documents.get(&uri) else {
104-
return Box::pin(async move {
105-
Err(ResponseError::new(
106-
ErrorCode::INVALID_REQUEST,
107-
"uri was never opened",
108-
))
109-
});
110-
};
111-
112-
let Some(parsed) = self.parser.parse(contents.as_bytes()) else {
113-
return Box::pin(async move {
114-
Err(ResponseError::new(
115-
ErrorCode::REQUEST_FAILED,
116-
"ts failed to parse contents",
117-
))
118-
});
119-
};
120-
121-
let locations = parsed.definition(&pos, &uri, contents.as_bytes());
122-
info!("Found {} matching nodes in the document", locations.len());
90+
match self.get_parsed_tree_and_content(&uri) {
91+
Err(e) => Box::pin(async move { Err(e) }),
92+
Ok((tree, content)) => {
93+
let locations = tree.definition(&pos, &uri, content.as_bytes());
12394

124-
let response = match locations.len() {
125-
0 => None,
126-
1 => Some(GotoDefinitionResponse::Scalar(locations[0].clone())),
127-
2.. => Some(GotoDefinitionResponse::Array(locations)),
128-
};
95+
let response = match locations.len() {
96+
0 => None,
97+
1 => Some(GotoDefinitionResponse::Scalar(locations[0].clone())),
98+
2.. => Some(GotoDefinitionResponse::Array(locations)),
99+
};
129100

130-
Box::pin(async move { Ok(response) })
101+
Box::pin(async move { Ok(response) })
102+
}
103+
}
131104
}
132105

133106
fn did_save(&mut self, _: DidSaveTextDocumentParams) -> Self::NotifyResult {
@@ -137,34 +110,45 @@ impl LanguageServer for ServerState {
137110
fn did_open(&mut self, params: DidOpenTextDocumentParams) -> Self::NotifyResult {
138111
let uri = params.text_document.uri;
139112
let contents = params.text_document.text;
140-
info!("Opened file at: {:}", uri);
113+
114+
info!("opened file at: {uri}");
141115
self.documents.insert(uri.clone(), contents.clone());
142116

143-
let Some(parsed) = self.parser.parse(contents.as_bytes()) else {
144-
tracing::error!("failed to parse content");
117+
let Some(tree) = self.parser.parse(contents.as_bytes()) else {
118+
error!("failed to parse content at {uri}");
145119
return ControlFlow::Continue(());
146120
};
147121

148-
let diagnostics = parsed.collect_parse_errors(&uri);
122+
let diagnostics = tree.collect_parse_errors(&uri);
149123
if let Err(e) = self.client.publish_diagnostics(diagnostics) {
150-
tracing::error!("failed to publish diagnostics. {:?}", e)
124+
error!(error=%e, "failed to publish diagnostics")
151125
}
152126
ControlFlow::Continue(())
153127
}
154128

129+
fn did_close(&mut self, params: DidCloseTextDocumentParams) -> Self::NotifyResult {
130+
let uri = params.text_document.uri;
131+
132+
info!("closed file at {uri}");
133+
self.documents.remove(&uri);
134+
135+
ControlFlow::Continue(())
136+
}
137+
155138
fn did_change(&mut self, params: DidChangeTextDocumentParams) -> Self::NotifyResult {
156139
let uri = params.text_document.uri;
157140
let contents = params.content_changes[0].text.clone();
141+
158142
self.documents.insert(uri.clone(), contents.clone());
159143

160-
let Some(parsed) = self.parser.parse(contents.as_bytes()) else {
161-
tracing::error!("failed to parse content");
144+
let Some(tree) = self.parser.parse(contents.as_bytes()) else {
145+
error!("failed to parse content at {uri}");
162146
return ControlFlow::Continue(());
163147
};
164148

165-
let diagnostics = parsed.collect_parse_errors(&uri);
149+
let diagnostics = tree.collect_parse_errors(&uri);
166150
if let Err(e) = self.client.publish_diagnostics(diagnostics) {
167-
tracing::error!("failed to publish diagnostics. {:?}", e)
151+
error!(error=%e, "failed to publish diagnostics")
168152
}
169153
ControlFlow::Continue(())
170154
}
@@ -175,28 +159,14 @@ impl LanguageServer for ServerState {
175159
) -> BoxFuture<'static, Result<Option<DocumentSymbolResponse>, Self::Error>> {
176160
let uri = params.text_document.uri;
177161

178-
let Some(contents) = self.documents.get(&uri) else {
179-
return Box::pin(async move {
180-
Err(ResponseError::new(
181-
ErrorCode::INVALID_REQUEST,
182-
"uri was never opened",
183-
))
184-
});
185-
};
186-
187-
let Some(parsed) = self.parser.parse(contents.as_bytes()) else {
188-
return Box::pin(async move {
189-
Err(ResponseError::new(
190-
ErrorCode::REQUEST_FAILED,
191-
"ts failed to parse contents",
192-
))
193-
});
194-
};
162+
match self.get_parsed_tree_and_content(&uri) {
163+
Err(e) => Box::pin(async move { Err(e) }),
164+
Ok((tree, content)) => {
165+
let locations = tree.find_document_locations(content.as_bytes());
166+
let response = DocumentSymbolResponse::Nested(locations);
195167

196-
let locations = parsed.find_document_locations(contents.as_bytes());
197-
198-
let response = DocumentSymbolResponse::Nested(locations);
199-
200-
Box::pin(async move { Ok(Some(response)) })
168+
Box::pin(async move { Ok(Some(response)) })
169+
}
170+
}
201171
}
202172
}

src/parser.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ struct DocumentSymbolTreeBuilder {
2424
// The found are things we've finished processing/parsing, at the top level of the stack.
2525
found: Vec<DocumentSymbol>,
2626
}
27+
2728
impl DocumentSymbolTreeBuilder {
2829
fn push(&mut self, node: usize, symbol: DocumentSymbol) {
2930
self.stack.push((node, symbol));
@@ -165,7 +166,6 @@ impl ParsedTree {
165166
let content = content.as_ref();
166167

167168
let mut cursor = self.tree.root_node().walk();
168-
169169
self.find_document_locations_inner(&mut builder, &mut cursor, content);
170170

171171
builder.build()
@@ -191,6 +191,8 @@ impl ParsedTree {
191191
let detail = self.find_preceding_comments(node.id(), content);
192192
let message = node.parent().unwrap();
193193

194+
// https://github.com/rust-lang/rust/issues/102777
195+
#[allow(deprecated)]
194196
let new_symbol = DocumentSymbol {
195197
name: name.to_string(),
196198
detail,
@@ -252,6 +254,7 @@ impl ParsedTree {
252254
pub fn hover(&self, pos: &Position, content: impl AsRef<[u8]>) -> Vec<MarkedString> {
253255
let text = self.get_node_text_at_position(pos, content.as_ref());
254256
info!("Looking for hover response on: {:?}", text);
257+
255258
match text {
256259
Some(text) => self
257260
.find_childrens_by_kinds(&["message_name", "enum_name", "service_name", "rpc_name"])
@@ -427,6 +430,7 @@ Author has a name and a country where they were born"#
427430
}
428431

429432
#[test]
433+
#[allow(deprecated)]
430434
fn test_document_symbols() {
431435
let contents = r#"syntax = "proto3";
432436

src/server.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use async_lsp::{lsp_types::Url, router::Router, ClientSocket};
1+
use async_lsp::{lsp_types::Url, router::Router, ClientSocket, ErrorCode, ResponseError};
22
use std::{collections::HashMap, ops::ControlFlow};
3+
use tracing::error;
34

4-
use crate::parser::ProtoParser;
5+
use crate::parser::{ParsedTree, ProtoParser};
56

67
pub struct TickEvent;
78
pub struct ServerState {
@@ -27,4 +28,27 @@ impl ServerState {
2728
self.counter += 1;
2829
ControlFlow::Continue(())
2930
}
31+
32+
pub fn get_parsed_tree_and_content(
33+
&mut self,
34+
uri: &Url,
35+
) -> Result<(ParsedTree, &str), ResponseError> {
36+
let Some(content) = self.documents.get(uri) else {
37+
error!("failed to get document at {uri}");
38+
return Err(ResponseError::new(
39+
ErrorCode::INVALID_REQUEST,
40+
"uri was never opened",
41+
));
42+
};
43+
44+
let Some(parsed) = self.parser.parse(content.as_bytes()) else {
45+
error!("failed to parse content at {uri}");
46+
return Err(ResponseError::new(
47+
ErrorCode::REQUEST_FAILED,
48+
"ts failed to parse contents",
49+
));
50+
};
51+
52+
Ok((parsed, content))
53+
}
3054
}

0 commit comments

Comments
 (0)