Skip to content

Commit 95a69f7

Browse files
committed
feat: Read import path and produce import diagnostics
1 parent f5cda97 commit 95a69f7

15 files changed

+274
-154
lines changed

Cargo.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ hard-xml = "1.36.0"
2727
tempfile = "3.12.0"
2828
serde = { version = "1.0.209", features = ["derive"] }
2929
basic-toml = "0.1.9"
30+
uuid = { version = "1.11.1", features = ["v4"] }
3031

3132
[dev-dependencies]
3233
insta = { version = "1.39.0", features = ["yaml"] }

protols.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[config]
2+
include_paths = ["sample", "src/workspace/input"]

src/config/workspace.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{
22
collections::{HashMap, HashSet},
3-
path::Path,
3+
path::{Path, PathBuf},
44
};
55

66
use async_lsp::lsp_types::{Url, WorkspaceFolder};
@@ -61,6 +61,21 @@ impl WorkspaceProtoConfigs {
6161
.iter()
6262
.find(|&k| upath.starts_with(k.to_file_path().unwrap()))
6363
}
64+
65+
pub fn get_include_paths(&self, uri: &Url) -> Option<Vec<PathBuf>> {
66+
let c = self.get_config_for_uri(uri)?;
67+
let w = self.get_workspace_for_uri(uri)?.to_file_path().ok()?;
68+
let mut ipath: Vec<PathBuf> = c
69+
.config
70+
.include_paths
71+
.iter()
72+
.map(PathBuf::from)
73+
.map(|p| if p.is_relative() { w.join(p) } else { p })
74+
.collect();
75+
76+
ipath.push(w.to_path_buf());
77+
Some(ipath)
78+
}
6479
}
6580

6681
#[cfg(test)]

src/lsp.rs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
use std::ops::ControlFlow;
2-
use std::sync::mpsc;
3-
use std::thread;
42
use std::{collections::HashMap, fs::read_to_string};
53
use tracing::{error, info};
64

@@ -12,11 +10,12 @@ use async_lsp::lsp_types::{
1210
DocumentSymbolParams, DocumentSymbolResponse, FileOperationFilter, FileOperationPattern,
1311
FileOperationPatternKind, FileOperationRegistrationOptions, GotoDefinitionParams,
1412
GotoDefinitionResponse, Hover, HoverContents, HoverParams, HoverProviderCapability,
15-
InitializeParams, InitializeResult, Location, OneOf, PrepareRenameResponse, ProgressParams,
16-
ReferenceParams, RenameFilesParams, RenameOptions, RenameParams, ServerCapabilities,
17-
ServerInfo, TextDocumentPositionParams, TextDocumentSyncCapability, TextDocumentSyncKind,
18-
TextEdit, Url, WorkspaceEdit, WorkspaceFileOperationsServerCapabilities,
19-
WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
13+
InitializeParams, InitializeResult, Location, OneOf, PrepareRenameResponse,
14+
ReferenceParams, RenameFilesParams, RenameOptions, RenameParams,
15+
ServerCapabilities, ServerInfo, TextDocumentPositionParams, TextDocumentSyncCapability,
16+
TextDocumentSyncKind, TextEdit, Url, WorkspaceEdit,
17+
WorkspaceFileOperationsServerCapabilities, WorkspaceFoldersServerCapabilities,
18+
WorkspaceServerCapabilities,
2019
};
2120
use async_lsp::{LanguageClient, LanguageServer, ResponseError};
2221
use futures::future::BoxFuture;
@@ -377,15 +376,19 @@ impl LanguageServer for ProtoLanguageServer {
377376
let uri = params.text_document.uri;
378377
let content = params.text_document.text;
379378

380-
let Some(diagnostics) = self.state.upsert_file(&uri, content) else {
379+
let Some(ipath) = self.configs.get_include_paths(&uri) else {
381380
return ControlFlow::Continue(());
382381
};
383382

384-
let Some(ws) = self.configs.get_config_for_uri(&uri) else {
383+
let Some(diagnostics) = self.state.upsert_file(&uri, content.clone(), &ipath) else {
385384
return ControlFlow::Continue(());
386385
};
387386

388-
if !ws.config.disable_parse_diagnostics {
387+
let Some(pconf) = self.configs.get_config_for_uri(&uri) else {
388+
return ControlFlow::Continue(());
389+
};
390+
391+
if !pconf.config.disable_parse_diagnostics {
389392
if let Err(e) = self.client.publish_diagnostics(diagnostics) {
390393
error!(error=%e, "failed to publish diagnostics")
391394
}
@@ -397,7 +400,11 @@ impl LanguageServer for ProtoLanguageServer {
397400
let uri = params.text_document.uri;
398401
let content = params.content_changes[0].text.clone();
399402

400-
let Some(diagnostics) = self.state.upsert_file(&uri, content) else {
403+
let Some(ipath) = self.configs.get_include_paths(&uri) else {
404+
return ControlFlow::Continue(());
405+
};
406+
407+
let Some(diagnostics) = self.state.upsert_file(&uri, content, &ipath) else {
401408
return ControlFlow::Continue(());
402409
};
403410

@@ -419,7 +426,7 @@ impl LanguageServer for ProtoLanguageServer {
419426
if let Ok(uri) = Url::from_file_path(&file.uri) {
420427
// Safety: The uri is always a file type
421428
let content = read_to_string(uri.to_file_path().unwrap()).unwrap_or_default();
422-
self.state.upsert_content(&uri, content);
429+
self.state.upsert_content(&uri, content, &vec![]);
423430
} else {
424431
error!(uri=%file.uri, "failed parse uri");
425432
}

src/parser/diagnostics.rs

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
use async_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, PublishDiagnosticsParams, Range};
1+
use async_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Range};
22

33
use crate::{nodekind::NodeKind, utils::ts_to_lsp_position};
44

55
use super::ParsedTree;
66

77
impl ParsedTree {
8-
pub fn collect_parse_errors(&self) -> PublishDiagnosticsParams {
9-
let diagnostics = self
10-
.find_all_nodes(NodeKind::is_error)
8+
pub fn collect_parse_diagnostics(&self) -> Vec<Diagnostic> {
9+
self.find_all_nodes(NodeKind::is_error)
1110
.into_iter()
1211
.map(|n| Diagnostic {
1312
range: Range {
@@ -19,12 +18,24 @@ impl ParsedTree {
1918
message: "Syntax error".to_string(),
2019
..Default::default()
2120
})
22-
.collect();
23-
PublishDiagnosticsParams {
24-
uri: self.uri.clone(),
25-
diagnostics,
26-
version: None,
27-
}
21+
.collect()
22+
}
23+
24+
pub fn collect_import_diagnostics(
25+
&self,
26+
content: &[u8],
27+
import: Vec<String>,
28+
) -> Vec<Diagnostic> {
29+
self.get_import_path_range(content, import)
30+
.into_iter()
31+
.map(|r| Diagnostic {
32+
range: r,
33+
severity: Some(DiagnosticSeverity::ERROR),
34+
source: Some(String::from("protols")),
35+
message: "failed to find proto file".to_string(),
36+
..Default::default()
37+
})
38+
.collect()
2839
}
2940
}
3041

@@ -42,12 +53,12 @@ mod test {
4253

4354
let parsed = ProtoParser::new().parse(url.clone(), contents);
4455
assert!(parsed.is_some());
45-
assert_yaml_snapshot!(parsed.unwrap().collect_parse_errors());
56+
assert_yaml_snapshot!(parsed.unwrap().collect_parse_diagnostics());
4657

4758
let contents = include_str!("input/test_collect_parse_error2.proto");
4859

4960
let parsed = ProtoParser::new().parse(url.clone(), contents);
5061
assert!(parsed.is_some());
51-
assert_yaml_snapshot!(parsed.unwrap().collect_parse_errors());
62+
assert_yaml_snapshot!(parsed.unwrap().collect_parse_diagnostics());
5263
}
5364
}
Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
---
22
source: src/parser/diagnostics.rs
3-
expression: parsed.unwrap().collect_parse_errors()
3+
expression: parsed.unwrap().collect_parse_diagnostics()
4+
snapshot_kind: text
45
---
5-
uri: "file://foo/bar.proto"
6-
diagnostics:
7-
- range:
8-
start:
9-
line: 6
10-
character: 8
11-
end:
12-
line: 6
13-
character: 19
14-
severity: 1
15-
source: protols
16-
message: Syntax error
6+
- range:
7+
start:
8+
line: 6
9+
character: 8
10+
end:
11+
line: 6
12+
character: 19
13+
severity: 1
14+
source: protols
15+
message: Syntax error
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
source: src/parser/diagnostics.rs
3-
expression: parsed.unwrap().collect_parse_errors()
3+
expression: parsed.unwrap().collect_parse_diagnostics()
4+
snapshot_kind: text
45
---
5-
uri: "file://foo/bar.proto"
6-
diagnostics: []
6+
[]

src/parser/tree.rs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
use async_lsp::lsp_types::Position;
1+
use async_lsp::lsp_types::{Position, Range};
22
use tree_sitter::{Node, TreeCursor};
33

4-
use crate::{nodekind::NodeKind, utils::lsp_to_ts_point};
4+
use crate::{
5+
nodekind::NodeKind,
6+
utils::{lsp_to_ts_point, ts_to_lsp_position},
7+
};
58

69
use super::ParsedTree;
710

@@ -133,15 +136,38 @@ impl ParsedTree {
133136
.first()
134137
.map(|n| n.utf8_text(content).expect("utf-8 parse error"))
135138
}
136-
pub fn get_import_path<'a>(&self, content: &'a [u8]) -> Vec<&'a str> {
139+
140+
pub fn get_import_node(&self) -> Vec<Node> {
137141
self.find_all_nodes(NodeKind::is_import_path)
138142
.into_iter()
139-
.filter_map(|n| {
140-
n.child_by_field_name("path").map(|c| {
141-
c.utf8_text(content)
142-
.expect("utf-8 parse error")
143-
.trim_matches('"')
144-
})
143+
.filter_map(|n| n.child_by_field_name("path"))
144+
.collect()
145+
}
146+
147+
pub fn get_import_path<'a>(&self, content: &'a [u8]) -> Vec<&'a str> {
148+
self.get_import_node()
149+
.into_iter()
150+
.map(|n| {
151+
n.utf8_text(content)
152+
.expect("utf-8 parse error")
153+
.trim_matches('"')
154+
})
155+
.collect()
156+
}
157+
158+
pub fn get_import_path_range<'a>(&self, content: &'a [u8], import: Vec<String>) -> Vec<Range> {
159+
self.get_import_node()
160+
.into_iter()
161+
.filter(|n| {
162+
let t = n
163+
.utf8_text(content)
164+
.expect("utf8-parse error")
165+
.trim_matches('"');
166+
import.iter().any(|i| i == t)
167+
})
168+
.map(|n| Range {
169+
start: ts_to_lsp_position(&n.start_position()),
170+
end: ts_to_lsp_position(&n.end_position()),
145171
})
146172
.collect()
147173
}

0 commit comments

Comments
 (0)