Skip to content

Commit 4e61c25

Browse files
committed
completion: add test
1 parent 4b543a8 commit 4e61c25

File tree

3 files changed

+164
-1
lines changed

3 files changed

+164
-1
lines changed

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ pub fn completion(
5454
trigger_kind: CompletionTriggerKind,
5555
cancel_token: CancellationToken,
5656
) -> Option<CompletionResponse> {
57-
dbg!(position);
5857
let semantic_model = analysis.compilation.get_semantic_model(file_id)?;
5958
if !semantic_model.get_emmyrc().completion.enable {
6059
return None;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#[cfg(test)]
2+
mod tests {
3+
4+
use lsp_types::CompletionItemKind;
5+
6+
use crate::handlers::completion::test::{CompletionVirtualWorkspace, VirtualCompletionItem};
7+
8+
#[test]
9+
fn test_basic() {
10+
let mut ws = CompletionVirtualWorkspace::new();
11+
12+
assert!(ws.check_completion(
13+
r#"
14+
local zabcde
15+
za<??>
16+
"#,
17+
vec![VirtualCompletionItem {
18+
label: "zabcde".to_string(),
19+
kind: CompletionItemKind::VARIABLE,
20+
}],
21+
));
22+
}
23+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
use emmylua_code_analysis::{EmmyLuaAnalysis, FileId, VirtualUrlGenerator};
2+
use emmylua_parser::LuaAstNode;
3+
use lsp_types::{CompletionItemKind, CompletionResponse, CompletionTriggerKind, Position};
4+
use tokio_util::sync::CancellationToken;
5+
6+
mod completion_test;
7+
use super::completion;
8+
9+
/// A virtual workspace for testing.
10+
#[allow(unused)]
11+
#[derive(Debug)]
12+
struct CompletionVirtualWorkspace {
13+
pub virtual_url_generator: VirtualUrlGenerator,
14+
pub analysis: EmmyLuaAnalysis,
15+
id_counter: u32,
16+
}
17+
18+
#[derive(Debug)]
19+
struct VirtualCompletionItem {
20+
pub label: String,
21+
pub kind: CompletionItemKind,
22+
}
23+
24+
#[allow(unused)]
25+
impl CompletionVirtualWorkspace {
26+
pub fn new() -> Self {
27+
let gen = VirtualUrlGenerator::new();
28+
let mut analysis = EmmyLuaAnalysis::new();
29+
let base = &gen.base;
30+
analysis.add_main_workspace(base.clone());
31+
CompletionVirtualWorkspace {
32+
virtual_url_generator: gen,
33+
analysis,
34+
id_counter: 0,
35+
}
36+
}
37+
38+
pub fn new_with_init_std_lib() -> Self {
39+
let gen = VirtualUrlGenerator::new();
40+
let mut analysis = EmmyLuaAnalysis::new();
41+
analysis.init_std_lib(false);
42+
let base = &gen.base;
43+
analysis.add_main_workspace(base.clone());
44+
CompletionVirtualWorkspace {
45+
virtual_url_generator: gen,
46+
analysis,
47+
id_counter: 0,
48+
}
49+
}
50+
51+
pub fn def(&mut self, content: &str) -> FileId {
52+
let id = self.id_counter;
53+
self.id_counter += 1;
54+
self.def_file(&format!("virtual_{}.lua", id), content)
55+
}
56+
57+
pub fn def_file(&mut self, file_name: &str, content: &str) -> FileId {
58+
let uri = self.virtual_url_generator.new_uri(file_name);
59+
let file_id = self
60+
.analysis
61+
.update_file_by_uri(&uri, Some(content.to_string()))
62+
.unwrap();
63+
file_id
64+
}
65+
66+
pub fn get_node<Ast: LuaAstNode>(&self, file_id: FileId) -> Ast {
67+
let tree = self
68+
.analysis
69+
.compilation
70+
.get_db()
71+
.get_vfs()
72+
.get_syntax_tree(&file_id)
73+
.unwrap();
74+
tree.get_chunk_node().descendants::<Ast>().next().unwrap()
75+
}
76+
77+
/// 处理文件内容
78+
fn handle_file_content(content: &str) -> Option<(String, Position)> {
79+
let mut content = content.to_string();
80+
let cursor_pos = content.find("<??>");
81+
if let Some(cursor_pos) = cursor_pos {
82+
// 确保只有一个 <??> 标记
83+
if content.matches("<??>").count() > 1 {
84+
return None;
85+
}
86+
87+
let mut line = 0;
88+
let mut column = 0;
89+
for (i, c) in content.chars().take(cursor_pos).enumerate() {
90+
if c == '\n' {
91+
line += 1;
92+
column = 0;
93+
} else {
94+
column += 1;
95+
}
96+
}
97+
98+
content = content.replace("<??>", "");
99+
100+
return Some((content, Position::new(line as u32, column as u32)));
101+
}
102+
None
103+
}
104+
105+
pub fn check_completion(
106+
&mut self,
107+
block_str: &str,
108+
expect: Vec<VirtualCompletionItem>,
109+
) -> bool {
110+
let content = Self::handle_file_content(block_str);
111+
let Some((content, position)) = content else {
112+
return false;
113+
};
114+
let file_id = self.def(&content);
115+
let result = completion(
116+
&self.analysis,
117+
file_id,
118+
position,
119+
CompletionTriggerKind::INVOKED,
120+
CancellationToken::new(),
121+
);
122+
let Some(result) = result else {
123+
return false;
124+
};
125+
// 对比
126+
let items = match result {
127+
CompletionResponse::Array(items) => items,
128+
CompletionResponse::List(list) => list.items,
129+
};
130+
if items.len() != expect.len() {
131+
return false;
132+
}
133+
// 需要顺序一致
134+
for (item, expect) in items.iter().zip(expect.iter()) {
135+
if item.label != expect.label || item.kind != Some(expect.kind) {
136+
return false;
137+
}
138+
}
139+
true
140+
}
141+
}

0 commit comments

Comments
 (0)