Skip to content

Commit 3d1af0d

Browse files
committed
support postfix completion
1 parent 74c34c4 commit 3d1af0d

File tree

2 files changed

+189
-0
lines changed

2 files changed

+189
-0
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod keywords_provider;
88
mod member_provider;
99
mod module_path_provider;
1010
mod auto_require_provider;
11+
mod postfix_provider;
1112

1213
use super::completion_builder::CompletionBuilder;
1314

@@ -22,6 +23,7 @@ pub fn add_completions(builder: &mut CompletionBuilder) -> Option<()> {
2223
doc_tag_provider::add_completion(builder);
2324
doc_type_provider::add_completion(builder);
2425
doc_name_token_provider::add_completion(builder);
26+
postfix_provider::add_completion(builder);
2527

2628
Some(())
2729
}
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
use code_analysis::Emmyrc;
2+
use emmylua_parser::{LuaAstNode, LuaTokenKind};
3+
use lsp_types::{CompletionItem, Range};
4+
use rowan::{TextRange, TokenAtOffset};
5+
6+
use crate::handlers::completion::completion_builder::CompletionBuilder;
7+
8+
pub fn add_completion(builder: &mut CompletionBuilder) -> Option<()> {
9+
if builder.is_cancelled() {
10+
return None;
11+
}
12+
13+
let emmyrc = builder.semantic_model.get_emmyrc();
14+
let trigger_kind = builder.trigger_token.kind();
15+
if !is_postfix_trigger(trigger_kind.into(), emmyrc) {
16+
return None;
17+
}
18+
19+
let trigger_pos = u32::from(builder.trigger_token.text_range().start());
20+
let left_pos = if trigger_pos > 0 {
21+
trigger_pos - 1
22+
} else {
23+
return None;
24+
};
25+
26+
let left_token = match builder
27+
.semantic_model
28+
.get_root()
29+
.syntax()
30+
.token_at_offset(left_pos.into())
31+
{
32+
TokenAtOffset::Single(token) => token,
33+
TokenAtOffset::Between(left, right) => {
34+
if left.kind() == LuaTokenKind::TkName.into() {
35+
left
36+
} else {
37+
right
38+
}
39+
},
40+
TokenAtOffset::None => return None,
41+
};
42+
43+
let left_token_text = left_token.text().to_string();
44+
let replace_range = TextRange::new(
45+
left_token.text_range().start(),
46+
builder.trigger_token.text_range().end(),
47+
);
48+
let repalce_lsp_range = builder
49+
.semantic_model
50+
.get_document()
51+
.to_lsp_range(replace_range)?;
52+
53+
add_postfix_completion(
54+
builder,
55+
repalce_lsp_range,
56+
"if",
57+
format!("if {} then\n\t$0\nend", left_token_text),
58+
);
59+
60+
add_postfix_completion(
61+
builder,
62+
repalce_lsp_range,
63+
"ifn",
64+
format!("if not {} then\n\t$0\nend", left_token_text),
65+
);
66+
67+
add_postfix_completion(
68+
builder,
69+
repalce_lsp_range,
70+
"while",
71+
format!("while {} do\n\t$0\nend", left_token_text),
72+
);
73+
74+
add_postfix_completion(
75+
builder,
76+
repalce_lsp_range,
77+
"forp",
78+
format!(
79+
"for ${{1:k}}, ${{2:v}} in pairs({}) do\n\t$0\nend",
80+
left_token_text
81+
),
82+
);
83+
84+
add_postfix_completion(
85+
builder,
86+
repalce_lsp_range,
87+
"forip",
88+
format!(
89+
"for ${{1:i}}, ${{2:v}} in ipairs({}) do\n\t$0\nend",
90+
left_token_text
91+
),
92+
);
93+
94+
add_postfix_completion(
95+
builder,
96+
repalce_lsp_range,
97+
"fori",
98+
format!("for ${{1:i}} = 1, {} do\n\t$0\nend", left_token_text),
99+
);
100+
101+
add_postfix_completion(
102+
builder,
103+
repalce_lsp_range,
104+
"function",
105+
format!("function {}(${{1:...}})\n\t$0\nend", left_token_text),
106+
);
107+
108+
add_postfix_completion(
109+
builder,
110+
repalce_lsp_range,
111+
"insert",
112+
format!("table.insert({}, ${{1:value}})", left_token_text),
113+
);
114+
115+
add_postfix_completion(
116+
builder,
117+
repalce_lsp_range,
118+
"remove",
119+
format!("table.remove({}, ${{1:index}})", left_token_text),
120+
);
121+
122+
add_postfix_completion(
123+
builder,
124+
repalce_lsp_range,
125+
"++",
126+
format!("{0} = {0} + 1", left_token_text),
127+
);
128+
129+
add_postfix_completion(
130+
builder,
131+
repalce_lsp_range,
132+
"--",
133+
format!("{0} = {0} - 1", left_token_text),
134+
);
135+
136+
add_postfix_completion(
137+
builder,
138+
repalce_lsp_range,
139+
"+n",
140+
format!("{0} = {0} + $1", left_token_text),
141+
);
142+
143+
add_postfix_completion(
144+
builder,
145+
repalce_lsp_range,
146+
"-n",
147+
format!("{0} = {0} - $1", left_token_text),
148+
);
149+
150+
Some(())
151+
}
152+
153+
fn is_postfix_trigger(trigger_kind: LuaTokenKind, emmyrc: &Emmyrc) -> bool {
154+
let trigger_string = &emmyrc.completion.postfix;
155+
if trigger_string.is_empty() {
156+
return false;
157+
}
158+
159+
let first_char = trigger_string.chars().next().unwrap();
160+
return match first_char {
161+
'.' => trigger_kind == LuaTokenKind::TkDot,
162+
'@' => trigger_kind == LuaTokenKind::TkAt,
163+
':' => trigger_kind == LuaTokenKind::TkColon,
164+
_ => false,
165+
};
166+
}
167+
168+
fn add_postfix_completion(
169+
builder: &mut CompletionBuilder,
170+
replace_range: Range,
171+
label: &str,
172+
text: String,
173+
) -> Option<()> {
174+
let item = CompletionItem {
175+
label: label.to_string(),
176+
insert_text: Some(text),
177+
additional_text_edits: Some(vec![lsp_types::TextEdit {
178+
range: replace_range,
179+
new_text: "".to_string(),
180+
}]),
181+
insert_text_format: Some(lsp_types::InsertTextFormat::SNIPPET),
182+
..Default::default()
183+
};
184+
185+
builder.add_completion_item(item);
186+
Some(())
187+
}

0 commit comments

Comments
 (0)