Skip to content

Commit d8cb046

Browse files
committed
implemented file-local find-refs for functions
1 parent 3b865df commit d8cb046

File tree

3 files changed

+239
-41
lines changed

3 files changed

+239
-41
lines changed

server/main/src/linemap.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use rust_lsp::lsp_types::Position;
2+
3+
pub struct LineMap {
4+
positions: Vec<usize>,
5+
}
6+
7+
impl LineMap {
8+
pub fn new(source: &str) -> Self {
9+
let mut positions = vec![0];
10+
for (i, char) in source.char_indices() {
11+
if char == '\n' {
12+
positions.push(i + 1);
13+
}
14+
}
15+
16+
LineMap { positions }
17+
}
18+
19+
pub fn offset_for_position(&self, position: Position) -> usize {
20+
self.positions[position.line as usize] + (position.character as usize)
21+
}
22+
}
23+
24+
#[cfg(test)]
25+
mod test {
26+
use rust_lsp::lsp_types::Position;
27+
28+
use crate::linemap::LineMap;
29+
30+
#[test]
31+
#[logging_macro::log_scope]
32+
fn test_linemap() {
33+
struct Test {
34+
string: &'static str,
35+
pos: Position,
36+
offset: usize,
37+
}
38+
39+
let cases = vec![
40+
Test {
41+
string: "sample\ntext",
42+
pos: Position { line: 1, character: 2 },
43+
offset: 9,
44+
},
45+
Test {
46+
string: "banana",
47+
pos: Position { line: 0, character: 0 },
48+
offset: 0,
49+
},
50+
Test {
51+
string: "banana",
52+
pos: Position { line: 0, character: 1 },
53+
offset: 1,
54+
},
55+
Test {
56+
string: "sample\ntext",
57+
pos: Position { line: 1, character: 0 },
58+
offset: 7,
59+
},
60+
Test {
61+
string: "sample\n\ttext",
62+
pos: Position { line: 1, character: 2 },
63+
offset: 9,
64+
},
65+
Test {
66+
string: "sample\r\ntext",
67+
pos: Position { line: 1, character: 0 },
68+
offset: 8,
69+
},
70+
];
71+
72+
for case in cases {
73+
let linemap = LineMap::new(case.string);
74+
75+
let offset = linemap.offset_for_position(case.pos);
76+
77+
assert_eq!(offset, case.offset, "{:?}", case.string);
78+
}
79+
}
80+
}

server/main/src/main.rs

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ mod consts;
4747
mod dfs;
4848
mod diagnostics_parser;
4949
mod graph;
50+
mod linemap;
5051
mod lsp_ext;
5152
mod merge_views;
5253
mod navigation;
@@ -515,6 +516,7 @@ impl LanguageServerHandling for MinecraftShaderLanguageServer {
515516

516517
let capabilities = ServerCapabilities {
517518
definition_provider: Some(OneOf::Left(true)),
519+
references_provider: Some(OneOf::Left(true)),
518520
document_link_provider: Some(DocumentLinkOptions {
519521
resolve_provider: None,
520522
work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None },
@@ -689,31 +691,60 @@ impl LanguageServerHandling for MinecraftShaderLanguageServer {
689691

690692
fn goto_definition(&mut self, params: TextDocumentPositionParams, completable: LSCompletable<Vec<Location>>) {
691693
logging::slog_with_trace_id(|| {
694+
let path = PathBuf::from_url(params.text_document.uri);
695+
if !path.starts_with(&self.root) {
696+
return;
697+
}
692698
let parser = &mut self.tree_sitter.borrow_mut();
693-
let parser_ctx = match navigation::ParserContext::new(parser, &params.text_document.uri) {
699+
let parser_ctx = match navigation::ParserContext::new(parser, &path) {
694700
Ok(ctx) => ctx,
695701
Err(e) => {
696702
return completable.complete(Err(MethodError {
697703
code: 42069,
698-
message: format!("error building parser context: {}", e.context(params.text_document.uri)),
704+
message: format!("error building parser context: error={}, path={:?}", e, path),
699705
data: (),
700706
}))
701707
}
702708
};
703709

704-
match parser_ctx.find_definitions(&params.text_document.uri, params.position) {
705-
Ok(locations) => completable.complete(Ok(locations)),
710+
match parser_ctx.find_definitions(&path, params.position) {
711+
Ok(locations) => completable.complete(Ok(locations.unwrap_or_default())),
706712
Err(e) => completable.complete(Err(MethodError {
707713
code: 42069,
708-
message: format!("error finding definitions: {}", e.context(params.text_document.uri)),
714+
message: format!("error finding definitions: error={}, path={:?}", e, path),
709715
data: (),
710716
})),
711717
}
712718
});
713719
}
714720

715-
fn references(&mut self, _: ReferenceParams, completable: LSCompletable<Vec<Location>>) {
716-
completable.complete(Err(Self::error_not_available(())));
721+
fn references(&mut self, params: ReferenceParams, completable: LSCompletable<Vec<Location>>) {
722+
logging::slog_with_trace_id(|| {
723+
let path = PathBuf::from_url(params.text_document_position.text_document.uri);
724+
if !path.starts_with(&self.root) {
725+
return;
726+
}
727+
let parser = &mut self.tree_sitter.borrow_mut();
728+
let parser_ctx = match navigation::ParserContext::new(parser, &path) {
729+
Ok(ctx) => ctx,
730+
Err(e) => {
731+
return completable.complete(Err(MethodError {
732+
code: 42069,
733+
message: format!("error building parser context: error={}, path={:?}", e, path),
734+
data: (),
735+
}))
736+
}
737+
};
738+
739+
match parser_ctx.find_references(&path, params.text_document_position.position) {
740+
Ok(locations) => completable.complete(Ok(locations.unwrap_or_default())),
741+
Err(e) => completable.complete(Err(MethodError {
742+
code: 42069,
743+
message: format!("error finding definitions: error={}, path={:?}", e, path),
744+
data: (),
745+
})),
746+
}
747+
});
717748
}
718749

719750
fn document_highlight(&mut self, _: TextDocumentPositionParams, completable: LSCompletable<Vec<DocumentHighlight>>) {
@@ -743,7 +774,7 @@ impl LanguageServerHandling for MinecraftShaderLanguageServer {
743774
fn document_link(&mut self, params: DocumentLinkParams, completable: LSCompletable<Vec<DocumentLink>>) {
744775
logging::slog_with_trace_id(|| {
745776
// node for current document
746-
let curr_doc = params.text_document.uri.to_file_path().unwrap();
777+
let curr_doc = PathBuf::from_url(params.text_document.uri);
747778
let node = match self.graph.borrow_mut().find_node(&curr_doc) {
748779
Some(n) => n,
749780
None => {

server/main/src/navigation.rs

Lines changed: 120 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,89 @@
1-
use std::fs::read_to_string;
1+
use std::{fs::read_to_string, path::Path};
22

33
use anyhow::Result;
44
use rust_lsp::lsp_types::{Location, Position, Range};
5-
use slog_scope::{info, debug};
6-
use tree_sitter::{Node, Parser, Point, Tree, Query, QueryCursor};
5+
use slog_scope::{debug, info, trace};
6+
use tree_sitter::{Node, Parser, Point, Query, QueryCursor, Tree};
77
use url::Url;
88

9-
macro_rules! find_function_str {
10-
() => {
9+
use crate::linemap::LineMap;
10+
11+
macro_rules! find_function_def_str {
12+
() => {
1113
r#"
1214
(
1315
(function_declarator
1416
(identifier) @function)
15-
(#match? @function "{}")
17+
(#match? @function "{}")
18+
)
19+
"#
20+
};
21+
}
22+
23+
macro_rules! find_function_refs_str {
24+
() => {
25+
r#"
26+
(
27+
(call_expression
28+
(identifier) @call)
29+
(#match? @call "{}")
1630
)
1731
"#
1832
};
1933
}
2034
pub struct ParserContext<'a> {
2135
source: String,
2236
tree: Tree,
37+
linemap: LineMap,
2338
parser: &'a mut Parser,
2439
}
2540

2641
impl<'a> ParserContext<'a> {
27-
pub fn new(parser: &'a mut Parser, document_uri: &Url) -> Result<Self> {
28-
let source = read_to_string(document_uri.path())?;
42+
pub fn new(parser: &'a mut Parser, path: &Path) -> Result<Self> {
43+
let source = read_to_string(path)?;
2944

3045
let tree = parser.parse(&source, None).unwrap();
3146

32-
Ok(ParserContext { source, tree, parser })
47+
let linemap = LineMap::new(&source);
48+
49+
Ok(ParserContext {
50+
source,
51+
tree,
52+
linemap,
53+
parser,
54+
})
3355
}
3456

35-
pub fn find_definitions(&self, document_uri: &Url, point: Position) -> Result<Vec<Location>> {
57+
pub fn find_definitions(&self, path: &Path, point: Position) -> Result<Option<Vec<Location>>> {
3658
let current_node = match self.find_node_at_point(point) {
3759
Some(node) => node,
38-
None => return Ok(vec![]),
60+
None => return Ok(None),
3961
};
4062

4163
let parent = match current_node.parent() {
4264
Some(parent) => parent,
43-
None => return Ok(vec![]),
65+
None => return Ok(None),
4466
};
4567

4668
let query = match (current_node.kind(), parent.kind()) {
4769
(_, "call_expression") => {
48-
format!(find_function_str!(), current_node.utf8_text(self.source.as_bytes())?)
70+
format!(find_function_def_str!(), current_node.utf8_text(self.source.as_bytes())?)
4971
}
50-
_ => return Ok(vec![]),
72+
_ => return Ok(None),
5173
};
5274

5375
let ts_query = Query::new(tree_sitter_glsl::language(), query.as_str())?;
54-
5576
let mut query_cursor = QueryCursor::new();
5677

5778
let mut locations = vec![];
5879

59-
for m in query_cursor.matches(&ts_query, self.tree.root_node(), self.source.as_bytes()) {
80+
for m in query_cursor.matches(&ts_query, self.root_node(), self.source.as_bytes()) {
6081
for capture in m.captures {
6182
let start = capture.node.start_position();
6283
let end = capture.node.end_position();
6384

6485
locations.push(Location {
65-
uri: document_uri.clone(),
86+
uri: Url::from_file_path(path).unwrap(),
6687
range: Range {
6788
start: Position {
6889
line: start.row as u32,
@@ -79,32 +100,98 @@ impl<'a> ParserContext<'a> {
79100

80101
info!("finished searching for definitions"; "definitions" => format!("{:?}", locations));
81102

82-
Ok(locations)
103+
Ok(Some(locations))
83104
}
84105

85-
pub fn find_references(&self) -> Result<Vec<Location>> {
86-
Ok(vec![])
106+
pub fn find_references(&self, path: &Path, point: Position) -> Result<Option<Vec<Location>>> {
107+
let current_node = match self.find_node_at_point(point) {
108+
Some(node) => node,
109+
None => return Ok(None),
110+
};
111+
112+
let parent = match current_node.parent() {
113+
Some(parent) => parent,
114+
None => return Ok(None),
115+
};
116+
117+
let query = match (current_node.kind(), parent.kind()) {
118+
(_, "function_declarator") => {
119+
format!(find_function_refs_str!(), current_node.utf8_text(self.source.as_bytes())?)
120+
}
121+
_ => return Ok(None),
122+
};
123+
124+
let ts_query = Query::new(tree_sitter_glsl::language(), query.as_str())?;
125+
let mut query_cursor = QueryCursor::new();
126+
127+
let mut locations = vec![];
128+
129+
for m in query_cursor.matches(&ts_query, self.root_node(), self.source.as_bytes()) {
130+
for capture in m.captures {
131+
let start = capture.node.start_position();
132+
let end = capture.node.end_position();
133+
134+
locations.push(Location {
135+
uri: Url::from_file_path(path).unwrap(),
136+
range: Range {
137+
start: Position {
138+
line: start.row as u32,
139+
character: start.column as u32,
140+
},
141+
end: Position {
142+
line: end.row as u32,
143+
character: end.column as u32,
144+
},
145+
},
146+
});
147+
}
148+
}
149+
150+
Ok(Some(locations))
87151
}
88152

89153
fn root_node(&self) -> Node {
90154
self.tree.root_node()
91155
}
92156

93-
fn find_node_at_point(&self, point: Position) -> Option<Node> {
94-
match self.root_node().named_descendant_for_point_range(
95-
Point {
96-
row: point.line as usize,
97-
column: (point.character - 1) as usize,
98-
},
99-
Point {
100-
row: point.line as usize,
101-
column: point.character as usize,
102-
},
103-
) {
157+
fn find_node_at_point(&self, pos: Position) -> Option<Node> {
158+
// if we're at the end of an ident, we need to look _back_ one char instead
159+
// for tree-sitter to find the right node.
160+
let look_behind = {
161+
let offset = self.linemap.offset_for_position(pos);
162+
let char_at = self.source.as_bytes()[offset];
163+
trace!("looking for non-alpha for point adjustment";
164+
"offset" => offset,
165+
"char" => char_at as char,
166+
"point" => format!("{:?}", pos),
167+
"look_behind" => !char_at.is_ascii_alphabetic());
168+
!char_at.is_ascii_alphabetic()
169+
};
170+
171+
let mut start = Point {
172+
row: pos.line as usize,
173+
column: pos.character as usize,
174+
};
175+
let mut end = Point {
176+
row: pos.line as usize,
177+
column: pos.character as usize,
178+
};
179+
180+
if look_behind {
181+
start.column -= 1;
182+
} else {
183+
end.column += 1;
184+
}
185+
186+
match self.root_node().named_descendant_for_point_range(start, end) {
104187
Some(node) => {
105-
debug!("found a node"; "node" => format!("{:?}", node));
188+
debug!("found a node";
189+
"node" => format!("{:?}", node),
190+
"text" => node.utf8_text(self.source.as_bytes()).unwrap(),
191+
"start" => format!("{}", start),
192+
"end" => format!("{}", end));
106193
Some(node)
107-
},
194+
}
108195
None => None,
109196
}
110197
}

0 commit comments

Comments
 (0)