Skip to content

Commit 9f3f60b

Browse files
committed
support param/@return snippet
1 parent 6db7c41 commit 9f3f60b

File tree

1 file changed

+127
-3
lines changed

1 file changed

+127
-3
lines changed

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

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::handlers::completion::{completion_builder::CompletionBuilder, data::DOC_TAGS};
22
use crate::meta_text::meta_doc_tag;
3-
use emmylua_parser::LuaTokenKind;
3+
use emmylua_parser::{
4+
LuaAst, LuaAstToken, LuaComment, LuaDocTag, LuaExpr, LuaGeneralToken, LuaTokenKind,
5+
};
46
use lsp_types::{CompletionItem, MarkupContent};
57

68
pub fn add_completion(builder: &mut CompletionBuilder) -> Option<()> {
@@ -9,8 +11,9 @@ pub fn add_completion(builder: &mut CompletionBuilder) -> Option<()> {
911
}
1012

1113
let trigger_token = &builder.trigger_token;
14+
let trigger_token_kind: LuaTokenKind = trigger_token.kind().into();
1215
if !matches!(
13-
trigger_token.kind().into(),
16+
trigger_token_kind,
1417
LuaTokenKind::TkDocStart | LuaTokenKind::TkDocLongStart | LuaTokenKind::TkTagOther
1518
) {
1619
return None;
@@ -23,8 +26,15 @@ pub fn add_completion(builder: &mut CompletionBuilder) -> Option<()> {
2326
add_tag_completion(builder, sorted_index, tag);
2427
}
2528

26-
builder.stop_here();
29+
if matches!(
30+
trigger_token_kind,
31+
LuaTokenKind::TkDocStart | LuaTokenKind::TkTagOther
32+
) {
33+
let last_index = DOC_TAGS.len() + emmyrc.doc.known_tags.len();
34+
add_tag_param_return_completion(builder, last_index);
35+
}
2736

37+
builder.stop_here();
2838
Some(())
2939
}
3040

@@ -42,3 +52,117 @@ fn add_tag_completion(builder: &mut CompletionBuilder, sorted_index: usize, tag:
4252

4353
builder.add_completion_item(completion_item);
4454
}
55+
56+
fn add_tag_param_return_completion(
57+
builder: &mut CompletionBuilder,
58+
sorted_index: usize,
59+
) -> Option<()> {
60+
let token = LuaGeneralToken::cast(builder.trigger_token.clone())?;
61+
let comment = token.ancestors::<LuaComment>().next()?;
62+
let comment_owner = comment.get_owner()?;
63+
let closure = match comment_owner {
64+
LuaAst::LuaAssignStat(stat) => {
65+
let (_, expr_list) = stat.get_var_and_expr_list();
66+
let mut result_closure = None;
67+
for value_expr in expr_list {
68+
if let LuaExpr::ClosureExpr(closure) = value_expr {
69+
result_closure = Some(closure.clone());
70+
break;
71+
}
72+
}
73+
74+
result_closure
75+
}
76+
LuaAst::LuaLocalFuncStat(f) => f.get_closure(),
77+
LuaAst::LuaFuncStat(f) => f.get_closure(),
78+
LuaAst::LuaLocalStat(local_stat) => {
79+
let mut result_closure = None;
80+
let expr_list = local_stat.get_value_exprs();
81+
for value_expr in expr_list {
82+
if let LuaExpr::ClosureExpr(closure) = value_expr {
83+
result_closure = Some(closure.clone());
84+
break;
85+
}
86+
}
87+
88+
result_closure
89+
}
90+
LuaAst::LuaTableField(field) => {
91+
let value_expr = field.get_value_expr()?;
92+
if let LuaExpr::ClosureExpr(closure) = value_expr {
93+
Some(closure)
94+
} else {
95+
None
96+
}
97+
}
98+
_ => return None,
99+
}?;
100+
101+
let mut param_orders = vec![];
102+
103+
for param in closure.get_params_list()?.get_params() {
104+
if let Some(name_token) = param.get_name_token() {
105+
param_orders.push(Some(name_token.get_text().to_string()));
106+
} else {
107+
param_orders.push(Some("...".to_string()));
108+
}
109+
}
110+
111+
for doc_tag in comment.get_doc_tags() {
112+
if let LuaDocTag::Param(param_tag) = doc_tag {
113+
if let Some(param_name) = param_tag.get_name_token() {
114+
let name_text = param_name.get_text();
115+
for param_order in param_orders.iter_mut() {
116+
if let Some(name) = param_order {
117+
if name == name_text {
118+
*param_order = None;
119+
break;
120+
}
121+
}
122+
}
123+
}
124+
}
125+
}
126+
127+
if param_orders.iter().all(|p| p.is_none()) {
128+
return None;
129+
}
130+
131+
let prev_token_text = builder.trigger_token.text();
132+
let prefix = if prev_token_text.starts_with("--- ") {
133+
"--- @"
134+
} else {
135+
"---@"
136+
};
137+
138+
let mut insert_text = String::new();
139+
for (i, param_name) in param_orders.iter().enumerate() {
140+
let indent = if i == 0 { "" } else { prefix };
141+
142+
if let Some(name) = param_name {
143+
let insert_snippet = format!(
144+
"{}param {} ${{{}:any}}\n",
145+
indent,
146+
name,
147+
insert_text.len() + 1
148+
);
149+
insert_text.push_str(&insert_snippet);
150+
}
151+
}
152+
153+
let idx = insert_text.len() + 1;
154+
insert_text.push_str(&format!("{}return ${{{}:any}}", prefix, idx));
155+
156+
let completion_item = CompletionItem {
157+
label: "param/@return".to_string(),
158+
kind: Some(lsp_types::CompletionItemKind::EVENT),
159+
insert_text: Some(insert_text),
160+
insert_text_format: Some(lsp_types::InsertTextFormat::SNIPPET),
161+
sort_text: Some(format!("{:03}", sorted_index)),
162+
insert_text_mode: Some(lsp_types::InsertTextMode::ADJUST_INDENTATION),
163+
..Default::default()
164+
};
165+
166+
builder.add_completion_item(completion_item);
167+
Some(())
168+
}

0 commit comments

Comments
 (0)