Skip to content

Commit 41bc0f5

Browse files
committed
optimize hover and add test
1 parent 7a9992e commit 41bc0f5

File tree

3 files changed

+194
-62
lines changed

3 files changed

+194
-62
lines changed

crates/emmylua_ls/src/handlers/hover/build_hover.rs

Lines changed: 45 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -110,39 +110,39 @@ fn build_decl_hover(
110110
) -> Option<()> {
111111
let decl = db.get_decl_index().get_decl(&decl_id)?;
112112

113-
let mut owner_member = None;
114-
let mut owner_decl = None;
113+
let mut origin_member = None;
114+
let mut origin_decl = None;
115115

116116
// 处理类型签名
117-
if typ.is_function() {
118-
let property_owner = get_decl_owner(builder.semantic_model, decl_id.clone());
119-
match property_owner {
117+
if is_function(&typ) {
118+
let origin_decl_id = get_function_decl_owner(builder.semantic_model, decl_id.clone());
119+
match origin_decl_id {
120120
Some(LuaSemanticDeclId::Member(member_id)) => {
121-
owner_member = Some(db.get_member_index().get_member(&member_id).unwrap());
121+
origin_member = Some(db.get_member_index().get_member(&member_id).unwrap());
122122
}
123123
Some(LuaSemanticDeclId::LuaDecl(decl_id)) => {
124-
owner_decl = Some(db.get_decl_index().get_decl(&decl_id).unwrap());
124+
origin_decl = Some(db.get_decl_index().get_decl(&decl_id).unwrap());
125125
}
126126
_ => {}
127127
}
128128
hover_function_type(
129129
builder,
130130
db,
131131
&typ,
132-
owner_member,
133-
if let Some(owner_decl) = owner_decl {
132+
origin_member,
133+
if let Some(owner_decl) = origin_decl {
134134
owner_decl.get_name()
135135
} else {
136136
decl.get_name()
137137
},
138-
if let Some(owner_decl) = owner_decl {
138+
if let Some(owner_decl) = origin_decl {
139139
owner_decl.is_local()
140140
} else {
141141
decl.is_local()
142142
},
143143
);
144144

145-
builder.set_location_path(owner_member);
145+
builder.set_location_path(origin_member);
146146
} else if typ.is_const() {
147147
let const_value = hover_const_type(db, &typ);
148148
let prefix = if decl.is_local() {
@@ -174,12 +174,13 @@ fn build_decl_hover(
174174
builder
175175
.add_description(LuaSemanticDeclId::LuaDecl(decl_id))
176176
.or_else(|| {
177-
owner_member.and_then(|m: &LuaMember| {
177+
origin_member.and_then(|m: &LuaMember| {
178178
builder.add_description(LuaSemanticDeclId::Member(m.get_id()))
179179
})
180180
})
181181
.or_else(|| {
182-
owner_decl.and_then(|d| builder.add_description(LuaSemanticDeclId::LuaDecl(d.get_id())))
182+
origin_decl
183+
.and_then(|d| builder.add_description(LuaSemanticDeclId::LuaDecl(d.get_id())))
183184
});
184185

185186
builder.add_signature_params_rets_description(typ);
@@ -199,54 +200,44 @@ fn build_member_hover(
199200
_ => return None,
200201
};
201202

202-
let mut function_member = None;
203-
let mut owner_decl = None;
204-
if typ.is_function()
205-
|| match &typ {
206-
LuaType::Union(union) => {
207-
union.get_types().len() > 1
208-
&& union
209-
.get_types()
210-
.iter()
211-
.all(|t| matches!(t, LuaType::DocFunction(_)))
212-
}
213-
_ => false,
214-
}
215-
{
216-
let property_owner = get_member_owner(&builder.semantic_model, member_id);
217-
match property_owner {
203+
let mut origin_function_member = None;
204+
let mut origin_decl = None;
205+
if is_function(&typ) {
206+
let origin_decl_id = get_function_member_owner(&builder.semantic_model, member_id);
207+
match origin_decl_id {
218208
Some(LuaSemanticDeclId::Member(member_id)) => {
219-
function_member = Some(db.get_member_index().get_member(&member_id).unwrap());
209+
origin_function_member =
210+
Some(db.get_member_index().get_member(&member_id).unwrap());
220211
}
221212
Some(LuaSemanticDeclId::LuaDecl(decl_id)) => {
222-
owner_decl = Some(db.get_decl_index().get_decl(&decl_id).unwrap());
213+
origin_decl = Some(db.get_decl_index().get_decl(&decl_id).unwrap());
223214
}
224215
_ => {}
225216
}
226217
hover_function_type(
227218
builder,
228219
db,
229220
&typ,
230-
function_member.or_else(|| {
231-
if owner_decl.is_none() {
221+
origin_function_member.or_else(|| {
222+
if origin_decl.is_none() {
232223
Some(&member)
233224
} else {
234225
None
235226
}
236227
}),
237-
if let Some(owner_decl) = owner_decl {
228+
if let Some(owner_decl) = origin_decl {
238229
owner_decl.get_name()
239230
} else {
240231
&member_name
241232
},
242-
if let Some(owner_decl) = owner_decl {
233+
if let Some(owner_decl) = origin_decl {
243234
owner_decl.is_local()
244235
} else {
245236
false
246237
},
247238
);
248239

249-
builder.set_location_path(Some(&function_member.as_ref().unwrap_or(&member)));
240+
builder.set_location_path(Some(&origin_function_member.as_ref().unwrap_or(&member)));
250241
} else if typ.is_const() {
251242
let const_value = hover_const_type(db, &typ);
252243
builder.set_type_description(format!("(field) {}: {}", member_name, const_value));
@@ -265,7 +256,7 @@ fn build_member_hover(
265256
builder
266257
.add_description(LuaSemanticDeclId::Member(member_id))
267258
.or_else(|| {
268-
function_member
259+
origin_function_member
269260
.and_then(|m| builder.add_description(LuaSemanticDeclId::Member(m.get_id())))
270261
});
271262

@@ -355,7 +346,10 @@ pub fn add_signature_ret_description(
355346
}
356347

357348
// 获取`decl`可能的来源
358-
fn get_decl_owner(semantic_model: &SemanticModel, decl_id: LuaDeclId) -> Option<LuaSemanticDeclId> {
349+
fn get_function_decl_owner(
350+
semantic_model: &SemanticModel,
351+
decl_id: LuaDeclId,
352+
) -> Option<LuaSemanticDeclId> {
359353
let root = semantic_model
360354
.get_db()
361355
.get_vfs()
@@ -368,18 +362,17 @@ fn get_decl_owner(semantic_model: &SemanticModel, decl_id: LuaDeclId) -> Option<
368362
.get_value_syntax_id()?
369363
.to_node_from_root(&root)?;
370364
let semantic_decl = semantic_model.find_decl(node.into(), SemanticDeclLevel::default());
371-
// 似乎在`get_property_owner_id`推断时就已递归处理了, 但还是再处理一次
372365
match semantic_decl {
373366
Some(LuaSemanticDeclId::Member(member_id)) => {
374-
get_member_owner(semantic_model, member_id).or(semantic_decl)
367+
get_function_member_owner(semantic_model, member_id).or(semantic_decl)
375368
}
376369
Some(LuaSemanticDeclId::LuaDecl(_)) => semantic_decl,
377370
_ => None,
378371
}
379372
}
380373

381374
// 获取`member_id`可能的来源
382-
fn get_member_owner(
375+
fn get_function_member_owner(
383376
semantic_model: &SemanticModel,
384377
member_id: LuaMemberId,
385378
) -> Option<LuaSemanticDeclId> {
@@ -480,3 +473,14 @@ pub fn get_hover_type(builder: &HoverBuilder, semantic_model: &SemanticModel) ->
480473

481474
None
482475
}
476+
477+
fn is_function(typ: &LuaType) -> bool {
478+
typ.is_function()
479+
|| match &typ {
480+
LuaType::Union(union) => union
481+
.get_types()
482+
.iter()
483+
.all(|t| matches!(t, LuaType::DocFunction(_))),
484+
_ => false,
485+
}
486+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#[cfg(test)]
2+
mod tests {
3+
4+
use crate::handlers::hover::test::{HoverVirtualWorkspace, VirtualHoverResult};
5+
6+
#[test]
7+
fn test_1() {
8+
let mut ws = HoverVirtualWorkspace::new();
9+
assert!(ws.check_hover(
10+
r#"
11+
---@param a number 参数a
12+
---@return number a 返回值a
13+
local function delete(a)
14+
end
15+
16+
local delete2 = delete
17+
local delete3 = delete2
18+
local <??>delete4 = delete3
19+
"#,
20+
VirtualHoverResult {
21+
value: "\n```lua\nlocal function delete(a: number)\n -> a: number\n\n```\n\n---\n\n@*param* `a` — 参数a\n\n\n\n@*return* `a` — 返回值a\n\n\n".to_string(),
22+
},
23+
));
24+
25+
assert!(ws.check_hover(
26+
r#"
27+
---@param a number 参数a
28+
---@return number a 返回值a
29+
local function delete(a)
30+
end
31+
32+
local delete2 = delete
33+
local delete3 = delete2
34+
local delete4 = delete3
35+
local deleteObj = {
36+
<??>aaa = delete4
37+
}
38+
"#,
39+
VirtualHoverResult {
40+
value: "\n```lua\nlocal function delete(a: number)\n -> a: number\n\n```\n\n---\n\n@*param* `a` — 参数a\n\n\n\n@*return* `a` — 返回值a\n\n\n".to_string(),
41+
},
42+
));
43+
44+
assert!(ws.check_hover(
45+
r#"
46+
---@param a number 参数a
47+
---@return number a 返回值a
48+
local function delete(a)
49+
end
50+
51+
local delete2 = delete
52+
local delete3 = delete2
53+
local delete4 = delete3
54+
local deleteObj = {
55+
aa = delete4
56+
}
57+
58+
local deleteObj2 = {
59+
<??>aa = deleteObj.aa
60+
}
61+
"#,
62+
VirtualHoverResult {
63+
value: "\n```lua\nlocal function delete(a: number)\n -> a: number\n\n```\n\n---\n\n@*param* `a` — 参数a\n\n\n\n@*return* `a` — 返回值a\n\n\n".to_string(),
64+
},
65+
));
66+
}
67+
68+
#[test]
69+
fn test_2() {
70+
let mut ws = HoverVirtualWorkspace::new();
71+
ws.def(
72+
r#"
73+
---@class Game
74+
---@field event fun(self: self, owner: "abc"): any 测试1
75+
---@field event fun(self: self, owner: "def"): any 测试2
76+
local Game = {}
77+
78+
---说明
79+
---@param key string 参数key
80+
---@param value string 参数value
81+
---@return number ret @返回值
82+
function Game:add(key, value)
83+
self.aaa = 1
84+
end
85+
"#,
86+
);
87+
88+
assert!(ws.check_hover(
89+
r#"
90+
91+
92+
---@type Game
93+
local game
94+
95+
local local_a = game.add
96+
local <??>local_b = local_a
97+
"#,
98+
VirtualHoverResult {
99+
value: "\n```lua\n(method) Game:add(key: string, value: string)\n -> ret: number\n\n```\n\n---\n\n说明\n\n@*param* `key` — 参数key\n\n@*param* `value` — 参数value\n\n\n\n@*return* `ret` — 返回值\n\n\n" .to_string(),
100+
},
101+
));
102+
}
103+
104+
#[test]
105+
fn test_3() {
106+
let mut ws = HoverVirtualWorkspace::new();
107+
ws.def(
108+
r#"
109+
---@class Hover.Test3<T>
110+
---@field event fun(self: self, event: "A", key: T)
111+
---@field event fun(self: self, event: "B", key: T)
112+
local Test3 = {}
113+
"#,
114+
);
115+
116+
assert!(ws.check_hover(
117+
r#"
118+
---@type Hover.Test3<string>
119+
local test3
120+
121+
local <??>event = test3.event
122+
"#,
123+
VirtualHoverResult {
124+
value: "\n```lua\n(method) Test3:event(event: \"B\", key: string)\n```\n\n&nbsp;&nbsp;in class `Hover.Test3`\n\n---\n\n---\n\n```lua\n(method) Test3:event(event: \"A\", key: string)\n```\n".to_string(),
125+
},
126+
));
127+
}
128+
}

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

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use emmylua_code_analysis::{EmmyLuaAnalysis, FileId, VirtualUrlGenerator};
22
use lsp_types::{Hover, HoverContents, MarkupContent, Position};
33

4+
mod hover_function_test;
45
mod hover_test;
56
use super::hover;
67

@@ -62,30 +63,29 @@ impl HoverVirtualWorkspace {
6263

6364
/// 处理文件内容
6465
fn handle_file_content(content: &str) -> Option<(String, Position)> {
65-
let mut content = content.to_string();
66-
let cursor_pos = content.find("<??>");
67-
if let Some(cursor_pos) = cursor_pos {
68-
// 确保只有一个 <??> 标记
69-
if content.matches("<??>").count() > 1 {
70-
return None;
71-
}
72-
73-
let mut line = 0;
74-
let mut column = 0;
75-
for (i, c) in content.chars().take(cursor_pos).enumerate() {
76-
if c == '\n' {
77-
line += 1;
78-
column = 0;
79-
} else {
80-
column += 1;
81-
}
82-
}
66+
let content = content.to_string();
67+
let cursor_byte_pos = content.find("<??>")?;
68+
if content.matches("<??>").count() > 1 {
69+
return None;
70+
}
8371

84-
content = content.replace("<??>", "");
72+
let mut line = 0;
73+
let mut column = 0;
8574

86-
return Some((content, Position::new(line as u32, column as u32)));
75+
for (byte_pos, c) in content.char_indices() {
76+
if byte_pos >= cursor_byte_pos {
77+
break;
78+
}
79+
if c == '\n' {
80+
line += 1;
81+
column = 0;
82+
} else {
83+
column += 1;
84+
}
8785
}
88-
None
86+
87+
let new_content = content.replace("<??>", "");
88+
Some((new_content, Position::new(line as u32, column as u32)))
8989
}
9090

9191
pub fn check_hover(&mut self, block_str: &str, expect: VirtualHoverResult) -> bool {

0 commit comments

Comments
 (0)