Skip to content

Commit 673a766

Browse files
committed
update references: 本地函数现在能够搜索导出的引用
1 parent 39fff0c commit 673a766

File tree

7 files changed

+156
-12
lines changed

7 files changed

+156
-12
lines changed

crates/emmylua_ls/src/handlers/call_hierarchy/build_call_hierarchy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub fn build_incoming_hierarchy(
100100
let mut locations = vec![];
101101
match semantic_decl {
102102
LuaSemanticDeclId::LuaDecl(decl_id) => {
103-
search_decl_references(semantic_model, decl_id, &mut locations);
103+
search_decl_references(semantic_model, compilation, decl_id, &mut locations);
104104
}
105105
LuaSemanticDeclId::Member(member_id) => {
106106
search_member_references(semantic_model, compilation, member_id, &mut locations);

crates/emmylua_ls/src/handlers/code_lens/resolve_code_lens.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ pub fn resolve_code_lens(
4444
let file_id = decl_id.file_id;
4545
let mut semantic_model = compilation.get_semantic_model(file_id)?;
4646
let mut results = Vec::new();
47-
search_decl_references(&mut semantic_model, decl_id, &mut results);
47+
search_decl_references(&mut semantic_model, compilation, decl_id, &mut results);
4848
let ref_count = results.len();
4949
let uri = semantic_model.get_document().get_uri();
5050
let command = make_usage_command(uri, code_lens.range, ref_count, client_id, results);

crates/emmylua_ls/src/handlers/references/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
mod reference_seacher;
22

33
use crate::context::ServerContextSnapshot;
4+
use emmylua_code_analysis::{EmmyLuaAnalysis, FileId};
45
use emmylua_parser::{LuaAstNode, LuaTokenKind};
5-
use lsp_types::{ClientCapabilities, Location, OneOf, ReferenceParams, ServerCapabilities};
6+
use lsp_types::{
7+
ClientCapabilities, Location, OneOf, Position, ReferenceParams, ServerCapabilities,
8+
};
69
use reference_seacher::search_references;
710
pub use reference_seacher::{search_decl_references, search_member_references};
811
use rowan::TokenAtOffset;
@@ -19,6 +22,15 @@ pub async fn on_references_handler(
1922
let analysis = context.analysis.read().await;
2023
let file_id = analysis.get_file_id(&uri)?;
2124
let position = params.text_document_position.position;
25+
26+
references(&analysis, file_id, position)
27+
}
28+
29+
pub fn references(
30+
analysis: &EmmyLuaAnalysis,
31+
file_id: FileId,
32+
position: Position,
33+
) -> Option<Vec<Location>> {
2234
let mut semantic_model = analysis.compilation.get_semantic_model(file_id)?;
2335
if !semantic_model.get_emmyrc().references.enable {
2436
return None;

crates/emmylua_ls/src/handlers/references/reference_seacher.rs

Lines changed: 98 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
use std::collections::HashMap;
1+
use std::collections::{HashMap, HashSet};
22

33
use emmylua_code_analysis::{
4-
LuaCompilation, LuaDeclId, LuaMemberId, LuaMemberKey, LuaSemanticDeclId, LuaTypeDeclId,
5-
SemanticDeclLevel, SemanticModel,
4+
DeclReference, LuaCompilation, LuaDeclId, LuaMemberId, LuaMemberKey, LuaSemanticDeclId,
5+
LuaTypeDeclId, SemanticDeclLevel, SemanticModel,
66
};
77
use emmylua_parser::{
8-
LuaAst, LuaAstNode, LuaAstToken, LuaNameToken, LuaStringToken, LuaSyntaxNode, LuaSyntaxToken,
8+
LuaAssignStat, LuaAst, LuaAstNode, LuaAstToken, LuaNameToken, LuaStringToken, LuaSyntaxNode,
9+
LuaSyntaxToken,
910
};
1011
use lsp_types::Location;
1112

@@ -20,7 +21,7 @@ pub fn search_references(
2021
{
2122
match semantic_decl {
2223
LuaSemanticDeclId::LuaDecl(decl_id) => {
23-
search_decl_references(semantic_model, decl_id, &mut result);
24+
search_decl_references(semantic_model, compilation, decl_id, &mut result);
2425
}
2526
LuaSemanticDeclId::Member(member_id) => {
2627
search_member_references(semantic_model, compilation, member_id, &mut result);
@@ -36,11 +37,16 @@ pub fn search_references(
3637
fuzzy_search_references(compilation, token, &mut result);
3738
}
3839

40+
// 简单过滤, 同行的多个引用只保留一个
41+
// let filtered_result = filter_duplicate_and_covered_locations(result);
42+
// Some(filtered_result)
43+
3944
Some(result)
4045
}
4146

4247
pub fn search_decl_references(
4348
semantic_model: &SemanticModel,
49+
compilation: &LuaCompilation,
4450
decl_id: LuaDeclId,
4551
result: &mut Vec<Location>,
4652
) -> Option<()> {
@@ -58,9 +64,15 @@ pub fn search_decl_references(
5864
if let Some(location) = document.to_lsp_location(decl.get_range()) {
5965
result.push(location);
6066
}
67+
let typ = semantic_model.get_type(decl.get_id().into());
68+
let is_signature = typ.is_signature();
69+
6170
for decl_ref in decl_refs {
6271
let location = document.to_lsp_location(decl_ref.range.clone())?;
6372
result.push(location);
73+
if is_signature {
74+
get_signature_decl_member_references(semantic_model, compilation, result, decl_ref);
75+
}
6476
}
6577

6678
return Some(());
@@ -119,7 +131,7 @@ pub fn search_member_references(
119131
let range = in_filed_syntax_id.value.get_range();
120132
let location = document.to_lsp_location(range)?;
121133
result.push(location);
122-
search_member_secondary_references(semantic_model, node, result);
134+
search_member_secondary_references(semantic_model, compilation, node, result);
123135
}
124136
}
125137

@@ -128,6 +140,7 @@ pub fn search_member_references(
128140

129141
fn search_member_secondary_references(
130142
semantic_model: &SemanticModel,
143+
compilation: &LuaCompilation,
131144
node: LuaSyntaxNode,
132145
result: &mut Vec<Location>,
133146
) -> Option<()> {
@@ -141,7 +154,7 @@ fn search_member_secondary_references(
141154
.position(|value| value.get_position() == position)?;
142155
let var = vars.get(idx)?;
143156
let decl_id = LuaDeclId::new(semantic_model.get_file_id(), var.get_position());
144-
search_decl_references(semantic_model, decl_id, result);
157+
search_decl_references(semantic_model, compilation, decl_id, result);
145158
let document = semantic_model.get_document();
146159
let range = document.to_lsp_location(var.get_range())?;
147160
result.push(range);
@@ -152,7 +165,7 @@ fn search_member_secondary_references(
152165
let idx = values.position(|value| value.get_position() == position)?;
153166
let name = local_names.get(idx)?;
154167
let decl_id = LuaDeclId::new(semantic_model.get_file_id(), name.get_position());
155-
search_decl_references(semantic_model, decl_id, result);
168+
search_decl_references(semantic_model, compilation, decl_id, result);
156169
let document = semantic_model.get_document();
157170
let range = document.to_lsp_location(name.get_range())?;
158171
result.push(range);
@@ -241,3 +254,80 @@ fn search_type_decl_references(
241254

242255
Some(())
243256
}
257+
258+
fn get_signature_decl_member_references(
259+
semantic_model: &SemanticModel,
260+
compilation: &LuaCompilation,
261+
result: &mut Vec<Location>,
262+
decl_ref: &DeclReference,
263+
) -> Option<Vec<Location>> {
264+
let root = semantic_model.get_root();
265+
let position = decl_ref.range.start();
266+
let token = root.syntax().token_at_offset(position).right_biased()?;
267+
let parent = token.parent()?;
268+
269+
match parent.parent()? {
270+
assign_stat_node if LuaAssignStat::can_cast(assign_stat_node.kind().into()) => {
271+
let assign_stat = LuaAssignStat::cast(assign_stat_node)?;
272+
let (vars, values) = assign_stat.get_var_and_expr_list();
273+
let idx = values
274+
.iter()
275+
.position(|value| value.get_position() == position)?;
276+
let var = vars.get(idx)?;
277+
let decl_id = semantic_model
278+
.find_decl(var.syntax().clone().into(), SemanticDeclLevel::default())?;
279+
if let LuaSemanticDeclId::Member(member_id) = decl_id {
280+
search_member_references(semantic_model, compilation, member_id, result);
281+
}
282+
}
283+
284+
_ => {}
285+
}
286+
None
287+
}
288+
289+
#[allow(unused)]
290+
fn filter_duplicate_and_covered_locations(locations: Vec<Location>) -> Vec<Location> {
291+
if locations.is_empty() {
292+
return locations;
293+
}
294+
let mut sorted_locations = locations;
295+
sorted_locations.sort_by(|a, b| {
296+
a.uri
297+
.to_string()
298+
.cmp(&b.uri.to_string())
299+
.then_with(|| a.range.start.line.cmp(&b.range.start.line))
300+
.then_with(|| b.range.end.line.cmp(&a.range.end.line))
301+
});
302+
303+
let mut result = Vec::new();
304+
let mut seen_lines_by_uri: HashMap<String, HashSet<u32>> = HashMap::new();
305+
306+
for location in sorted_locations {
307+
let uri_str = location.uri.to_string();
308+
let seen_lines = seen_lines_by_uri.entry(uri_str).or_default();
309+
310+
let start_line = location.range.start.line;
311+
let end_line = location.range.end.line;
312+
313+
let is_covered = (start_line..=end_line).any(|line| seen_lines.contains(&line));
314+
315+
if !is_covered {
316+
for line in start_line..=end_line {
317+
seen_lines.insert(line);
318+
}
319+
result.push(location);
320+
}
321+
}
322+
323+
// 最终按位置排序
324+
result.sort_by(|a, b| {
325+
a.uri
326+
.to_string()
327+
.cmp(&b.uri.to_string())
328+
.then_with(|| a.range.start.line.cmp(&b.range.start.line))
329+
.then_with(|| a.range.start.character.cmp(&b.range.start.character))
330+
});
331+
332+
result
333+
}

crates/emmylua_ls/src/handlers/test/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod hover_function_test;
66
mod hover_test;
77
mod implementation_test;
88
mod inlay_hint_test;
9+
mod references_test;
910
mod rename_test;
1011
mod semantic_token_test;
1112
mod signature_helper_test;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#[cfg(test)]
2+
mod tests {
3+
4+
use crate::handlers::test_lib::ProviderVirtualWorkspace;
5+
6+
#[test]
7+
fn test_function_references() {
8+
let mut ws = ProviderVirtualWorkspace::new();
9+
ws.def_file(
10+
"1.lua",
11+
r#"
12+
local flush = require("virtual_0").flush
13+
flush()
14+
"#,
15+
);
16+
let result = ws.check_references(
17+
r#"
18+
local export = {}
19+
local function fl<??>ush()
20+
end
21+
export.flush = flush
22+
return export
23+
"#,
24+
);
25+
assert!(result.is_some());
26+
let locations = result.unwrap();
27+
assert!(locations.len() >= 4);
28+
}
29+
}

crates/emmylua_ls/src/handlers/test_lib/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
},
2121
};
2222

23-
use super::{hover::hover, implementation::implementation};
23+
use super::{hover::hover, implementation::implementation, references::references};
2424

2525
/// A virtual workspace for testing.
2626
#[allow(unused)]
@@ -375,4 +375,16 @@ impl ProviderVirtualWorkspace {
375375

376376
true
377377
}
378+
379+
pub fn check_references(&mut self, block_str: &str) -> Option<Vec<lsp_types::Location>> {
380+
let content = Self::handle_file_content(block_str);
381+
let Some((content, position)) = content else {
382+
return None;
383+
};
384+
let file_id = self.def(&content);
385+
let result = references(&self.analysis, file_id, position);
386+
// dbg!(&result);
387+
dbg!(&result.as_ref().unwrap().len());
388+
result
389+
}
378390
}

0 commit comments

Comments
 (0)