Skip to content

Commit 2ebbb0f

Browse files
protols: publish to crates.io (#6)
Closes #5 --------- Co-authored-by: coder3101 <[email protected]>
1 parent 59dc1ab commit 2ebbb0f

File tree

6 files changed

+219
-25
lines changed

6 files changed

+219
-25
lines changed

Cargo.lock

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

Cargo.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
[package]
22
name = "protols"
3+
description = "Language server for proto3 files"
34
version = "0.1.0"
45
edition = "2021"
6+
license = "MIT"
7+
homepage = "https://github.com/coder3101/protols"
8+
repository = "https://github.com/coder3101/protols"
9+
readme = "README.md"
10+
keywords = ["lsp", "proto3"]
511

612
[dependencies]
713
async-lsp = { version = "0.2.0", features = ["tokio"] }
@@ -11,6 +17,6 @@ tokio-util = { version = "0.7.11", features = ["compat"] }
1117
tower = "0.4.13"
1218
tracing = "0.1.40"
1319
tracing-subscriber = "0.3.18"
14-
tree-sitter-proto = { git = "https://github.com/coder3101/tree-sitter-proto", branch = "main" }
1520
tree-sitter = "0.22.6"
1621
tracing-appender = "0.2.3"
22+
protols-tree-sitter-proto = "0.1.0"

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# protols
2-
A Language Server for **proto3** files. It only uses tree-sitter parser for all operations and always runs in **single file mode**.
2+
A Language Server for **proto3** files. It uses tree-sitter parser for all operations and always runs in **single file mode**.
33

44
## Features
55
- [x] Hover
@@ -9,7 +9,7 @@ A Language Server for **proto3** files. It only uses tree-sitter parser for all
99

1010
## Installation and testing
1111

12-
Clone the repository and run `cargo install --path .` to install locally in your `~/.cargo/bin` and the below to your `init.lua` until we start shipping this via Mason.
12+
Clone the repository and run `cargo install protols` to install locally in your `~/.cargo/bin` and the below to your `init.lua` until we start shipping this via Mason.
1313

1414
```lua
1515
local client = vim.lsp.start_client({

src/lsp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl LanguageServer for ServerState {
2323
let (cname, version) = params
2424
.client_info
2525
.as_ref()
26-
.map(|c| (c.name.as_str(), c.version.as_ref().map(|x| x.as_str())))
26+
.map(|c| (c.name.as_str(), c.version.as_deref()))
2727
.unwrap_or(("<unknown>", None));
2828

2929
let cversion = version.unwrap_or("<unknown>");

src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use async_lsp::server::LifecycleLayer;
77
use async_lsp::tracing::TracingLayer;
88
use server::{ServerState, TickEvent};
99
use tower::ServiceBuilder;
10-
use tracing::{info, Level};
10+
use tracing::Level;
1111

1212
mod lsp;
1313
mod parser;

src/parser.rs

Lines changed: 197 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ pub struct ParsedTree {
1818
impl ProtoParser {
1919
pub fn new() -> Self {
2020
let mut parser = tree_sitter::Parser::new();
21-
if let Err(e) = parser.set_language(&tree_sitter_proto::language()) {
21+
if let Err(e) = parser.set_language(&protols_tree_sitter_proto::language()) {
2222
panic!("failed to set ts language parser {:?}", e);
2323
}
2424
Self { parser }
@@ -33,7 +33,6 @@ impl ProtoParser {
3333

3434
impl ParsedTree {
3535
fn walk_and_collect_kinds<'a>(
36-
&self,
3736
cursor: &mut TreeCursor<'a>,
3837
kinds: &[&str],
3938
) -> Vec<Node<'a>> {
@@ -47,7 +46,7 @@ impl ParsedTree {
4746
}
4847

4948
if cursor.goto_first_child() {
50-
v.extend(self.walk_and_collect_kinds(cursor, kinds));
49+
v.extend(Self::walk_and_collect_kinds(cursor, kinds));
5150
cursor.goto_parent();
5251
}
5352

@@ -59,14 +58,14 @@ impl ParsedTree {
5958
v
6059
}
6160

62-
fn advance_cursor_to<'a>(&self, cursor: &mut TreeCursor<'a>, nid: usize) -> bool {
61+
fn advance_cursor_to(cursor: &mut TreeCursor<'_>, nid: usize) -> bool {
6362
loop {
6463
let node = cursor.node();
6564
if node.id() == nid {
6665
return true;
6766
}
6867
if cursor.goto_first_child() {
69-
if self.advance_cursor_to(cursor, nid) {
68+
if Self::advance_cursor_to(cursor, nid) {
7069
return true;
7170
}
7271
cursor.goto_parent();
@@ -83,7 +82,7 @@ impl ParsedTree {
8382

8483
info!("Looking for node with id: {nid}");
8584

86-
self.advance_cursor_to(&mut cursor, nid);
85+
Self::advance_cursor_to(&mut cursor, nid);
8786
if !cursor.goto_parent() {
8887
return None;
8988
}
@@ -108,12 +107,12 @@ impl ParsedTree {
108107
break;
109108
}
110109
}
111-
return if comments.len() != 0 {
110+
if !comments.is_empty() {
112111
comments.reverse();
113112
Some(comments.join("\n"))
114113
} else {
115114
None
116-
};
115+
}
117116
}
118117
}
119118

@@ -132,7 +131,7 @@ impl ParsedTree {
132131

133132
pub fn find_childrens_by_kinds(&self, kinds: &[&str]) -> Vec<Node> {
134133
let mut cursor = self.tree.root_node().walk();
135-
self.walk_and_collect_kinds(&mut cursor, kinds)
134+
Self::walk_and_collect_kinds(&mut cursor, kinds)
136135
}
137136

138137
pub fn definition(
@@ -170,7 +169,7 @@ impl ParsedTree {
170169
.into_iter()
171170
.filter(|n| n.utf8_text(content.as_ref()).expect("utf-8 parse error") == text)
172171
.filter_map(|n| self.find_preceeding_comments(n.id(), content.as_ref()))
173-
.map(|s| MarkedString::String(s))
172+
.map(MarkedString::String)
174173
.collect(),
175174
None => vec![],
176175
}
@@ -198,3 +197,191 @@ impl ParsedTree {
198197
}
199198
}
200199
}
200+
201+
#[cfg(test)]
202+
mod test {
203+
use async_lsp::lsp_types::{DiagnosticSeverity, MarkedString, Position, Range, Url};
204+
205+
use super::ProtoParser;
206+
207+
#[test]
208+
fn test_find_children_by_kind() {
209+
let contents = r#"syntax = "proto3";
210+
211+
package com.book;
212+
213+
message Book {
214+
215+
message Author {
216+
string name = 1;
217+
string country = 2;
218+
};
219+
// This is a multi line comment on the field name
220+
// Of a message called Book
221+
int64 isbn = 1;
222+
string title = 2;
223+
Author author = 3;
224+
}
225+
"#;
226+
let parsed = ProtoParser::new().parse(contents);
227+
assert!(parsed.is_some());
228+
let tree = parsed.unwrap();
229+
let nodes = tree.find_childrens_by_kinds(&["message_name"]);
230+
231+
assert_eq!(nodes.len(), 2);
232+
233+
let names: Vec<_> = nodes
234+
.into_iter()
235+
.map(|n| n.utf8_text(contents.as_ref()).unwrap())
236+
.collect();
237+
assert_eq!(names[0], "Book");
238+
assert_eq!(names[1], "Author");
239+
}
240+
241+
#[test]
242+
fn test_collect_parse_error() {
243+
let url = "file://foo/bar.proto";
244+
let contents = r#"syntax = "proto3";
245+
246+
package com.book;
247+
248+
message Book {
249+
message Author {
250+
string name;
251+
string country = 2;
252+
};
253+
}
254+
"#;
255+
let parsed = ProtoParser::new().parse(contents);
256+
assert!(parsed.is_some());
257+
let tree = parsed.unwrap();
258+
let diagnostics = tree.collect_parse_errors(&url.parse().unwrap());
259+
260+
assert_eq!(diagnostics.uri, Url::parse(url).unwrap());
261+
assert_eq!(diagnostics.diagnostics.len(), 1);
262+
263+
let error = &diagnostics.diagnostics[0];
264+
assert_eq!(error.severity, Some(DiagnosticSeverity::ERROR));
265+
assert_eq!(error.source, Some("protols".to_owned()));
266+
assert_eq!(error.message, "Syntax error");
267+
assert_eq!(
268+
error.range,
269+
Range {
270+
start: Position {
271+
line: 6,
272+
character: 8
273+
},
274+
end: Position {
275+
line: 6,
276+
character: 19
277+
}
278+
}
279+
);
280+
}
281+
282+
#[test]
283+
fn test_hover() {
284+
let posbook = Position {
285+
line: 5,
286+
character: 9,
287+
};
288+
let posinvalid = Position {
289+
line: 0,
290+
character: 1,
291+
};
292+
let posauthor = Position {
293+
line: 11,
294+
character: 14,
295+
};
296+
let contents = r#"syntax = "proto3";
297+
298+
package com.book;
299+
300+
// A Book is book
301+
message Book {
302+
303+
// This is represents author
304+
// A author is a someone who writes books
305+
//
306+
// Author has a name and a country where they were born
307+
message Author {
308+
string name = 1;
309+
string country = 2;
310+
};
311+
}
312+
"#;
313+
let parsed = ProtoParser::new().parse(contents);
314+
assert!(parsed.is_some());
315+
let tree = parsed.unwrap();
316+
let res = tree.hover(&posbook, contents);
317+
318+
assert_eq!(res.len(), 1);
319+
assert_eq!(res[0], MarkedString::String("A Book is book".to_owned()));
320+
321+
let res = tree.hover(&posinvalid, contents);
322+
assert_eq!(res.len(), 0);
323+
324+
let res = tree.hover(&posauthor, contents);
325+
assert_eq!(res.len(), 1);
326+
assert_eq!(
327+
res[0],
328+
MarkedString::String(
329+
r#"This is represents author
330+
A author is a someone who writes books
331+
332+
Author has a name and a country where they were born"#
333+
.to_owned()
334+
)
335+
);
336+
}
337+
338+
#[test]
339+
fn test_goto_definition() {
340+
let url = "file://foo/bar.proto";
341+
let posinvalid = Position {
342+
line: 0,
343+
character: 1,
344+
};
345+
let posauthor = Position {
346+
line: 10,
347+
character: 5,
348+
};
349+
let contents = r#"syntax = "proto3";
350+
351+
package com.book;
352+
353+
message Book {
354+
message Author {
355+
string name = 1;
356+
string country = 2;
357+
};
358+
359+
Author author = 1;
360+
string isbn = 2;
361+
}
362+
"#;
363+
let parsed = ProtoParser::new().parse(contents);
364+
assert!(parsed.is_some());
365+
let tree = parsed.unwrap();
366+
let res = tree.definition(&posauthor, &url.parse().unwrap(), contents);
367+
368+
assert_eq!(res.len(), 1);
369+
assert_eq!(res[0].uri, Url::parse(url).unwrap());
370+
assert_eq!(
371+
res[0].range,
372+
Range {
373+
start: Position {
374+
line: 5,
375+
character: 12
376+
},
377+
end: Position {
378+
line: 5,
379+
character: 18
380+
},
381+
}
382+
);
383+
384+
let res = tree.definition(&posinvalid, &url.parse().unwrap(), contents);
385+
assert_eq!(res.len(), 0);
386+
}
387+
}

0 commit comments

Comments
 (0)