Skip to content

Commit 131947a

Browse files
committed
support enum completion
1 parent 6f229ee commit 131947a

File tree

2 files changed

+175
-11
lines changed

2 files changed

+175
-11
lines changed

crates/code_analysis/src/db_index/type/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ use flagset::FlagSet;
1010
use rowan::TextRange;
1111
use std::collections::HashMap;
1212
pub use type_assert::TypeAssertion;
13-
use type_decl::LuaDeclLocation;
14-
pub use type_decl::{LuaDeclTypeKind, LuaTypeAttribute, LuaTypeDecl, LuaTypeDeclId};
13+
pub use type_decl::{LuaDeclTypeKind, LuaTypeAttribute, LuaTypeDecl, LuaTypeDeclId, LuaDeclLocation};
1514
pub use types::*;
1615

1716
#[derive(Debug)]

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

Lines changed: 174 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
use code_analysis::{
2-
InferGuard, LuaFunctionType, LuaMemberId, LuaPropertyOwnerId, LuaType, LuaTypeDeclId,
3-
LuaUnionType,
2+
InferGuard, LuaDeclLocation, LuaFunctionType, LuaMemberId, LuaMemberKey, LuaMemberOwner,
3+
LuaPropertyOwnerId, LuaType, LuaTypeDeclId, LuaUnionType,
44
};
55
use emmylua_parser::{
6-
LuaAstNode, LuaCallArgList, LuaCallExpr, LuaExpr, LuaSyntaxKind, LuaSyntaxToken, LuaTokenKind,
6+
LuaAst, LuaAstNode, LuaAstToken, LuaCallArgList, LuaCallExpr, LuaComment, LuaExpr,
7+
LuaNameToken, LuaSyntaxId, LuaSyntaxKind, LuaSyntaxToken, LuaTokenKind, LuaVarExpr,
78
};
89
use lsp_types::CompletionItem;
910

10-
use crate::handlers::{
11-
completion::completion_builder::CompletionBuilder, signature_helper::get_current_param_index,
11+
use crate::{
12+
handlers::{
13+
completion::completion_builder::CompletionBuilder,
14+
signature_helper::get_current_param_index,
15+
},
16+
util::humanize_type,
1217
};
1318

1419
pub fn add_completion(builder: &mut CompletionBuilder) -> Option<()> {
@@ -68,10 +73,43 @@ fn add_type_ref_completion(
6873
}
6974
builder.stop_here();
7075
} else if type_decl.is_enum() {
71-
// let member_ids = type_decl.get_enum_members()?.to_vec();
72-
// for member_id in member_ids {
73-
// add_alias_member_completion(builder, &member_id);
74-
// }
76+
let owner_id = LuaMemberOwner::Type(type_ref_id.clone());
77+
let member_map = builder
78+
.semantic_model
79+
.get_db()
80+
.get_member_index()
81+
.get_member_map(owner_id)?;
82+
83+
if type_decl.is_enum_key() {
84+
let mut completion_items = Vec::new();
85+
for member_key in member_map.keys() {
86+
let label = match member_key {
87+
LuaMemberKey::Name(str) => to_enum_label(builder, str.as_str()),
88+
LuaMemberKey::Integer(i) => i.to_string(),
89+
LuaMemberKey::None => continue,
90+
};
91+
92+
let completion_item = CompletionItem {
93+
label,
94+
kind: Some(lsp_types::CompletionItemKind::ENUM_MEMBER),
95+
..Default::default()
96+
};
97+
98+
completion_items.push(completion_item);
99+
}
100+
for completion_item in completion_items {
101+
builder.add_completion_item(completion_item);
102+
}
103+
} else {
104+
let locations = type_decl
105+
.get_locations()
106+
.iter()
107+
.map(|it| it.clone())
108+
.collect::<Vec<_>>();
109+
let member_ids = member_map.values().map(|it| it.clone()).collect::<Vec<_>>();
110+
add_enum_members_completion(builder, member_ids, &type_ref_id, locations);
111+
}
112+
75113
builder.stop_here();
76114
}
77115

@@ -220,3 +258,130 @@ fn add_lambda_completion(builder: &mut CompletionBuilder, func: &LuaFunctionType
220258
builder.add_completion_item(completion_item);
221259
Some(())
222260
}
261+
262+
fn add_enum_members_completion(
263+
builder: &mut CompletionBuilder,
264+
member_ids: Vec<LuaMemberId>,
265+
type_id: &LuaTypeDeclId,
266+
locations: Vec<LuaDeclLocation>,
267+
) -> Option<()> {
268+
let file_id = builder.semantic_model.get_file_id();
269+
let is_same_file = locations.iter().all(|it| it.file_id == file_id);
270+
if let Some(variable_name) = get_enum_decl_variable_name(builder, locations, is_same_file) {
271+
for member_id in member_ids {
272+
let member = builder
273+
.semantic_model
274+
.get_db()
275+
.get_member_index()
276+
.get_member(&member_id)?;
277+
let key = member.get_key();
278+
let label = match key {
279+
LuaMemberKey::Name(str) => format!("{}.{}", variable_name, str.to_string()),
280+
LuaMemberKey::Integer(i) => format!("{}[{}]", variable_name, i),
281+
LuaMemberKey::None => continue,
282+
};
283+
284+
let description = format!("{}", type_id.get_name());
285+
let completion_item = CompletionItem {
286+
label,
287+
kind: Some(lsp_types::CompletionItemKind::ENUM_MEMBER),
288+
label_details: Some(lsp_types::CompletionItemLabelDetails {
289+
detail: None,
290+
description: Some(description.clone()),
291+
}),
292+
..Default::default()
293+
};
294+
295+
builder.add_completion_item(completion_item);
296+
}
297+
} else {
298+
for member_id in member_ids {
299+
let member = builder
300+
.semantic_model
301+
.get_db()
302+
.get_member_index()
303+
.get_member(&member_id)?;
304+
let label = humanize_type(builder.semantic_model.get_db(), member.get_decl_type());
305+
let description = format!("{}", type_id.get_name());
306+
let completion_item = CompletionItem {
307+
label,
308+
kind: Some(lsp_types::CompletionItemKind::ENUM_MEMBER),
309+
label_details: Some(lsp_types::CompletionItemLabelDetails {
310+
detail: None,
311+
description: Some(description.clone()),
312+
}),
313+
..Default::default()
314+
};
315+
316+
builder.add_completion_item(completion_item);
317+
}
318+
}
319+
Some(())
320+
}
321+
322+
fn get_enum_decl_variable_name(
323+
builder: &CompletionBuilder,
324+
locations: Vec<LuaDeclLocation>,
325+
is_same_file: bool,
326+
) -> Option<String> {
327+
let completion_file_id = builder.semantic_model.get_file_id();
328+
if is_same_file {
329+
let same_location = locations
330+
.iter()
331+
.find(|it| it.file_id == completion_file_id)?;
332+
let root = builder
333+
.semantic_model
334+
.get_root_by_file_id(same_location.file_id)?;
335+
let syntax_id = LuaSyntaxId::new(LuaTokenKind::TkName.into(), same_location.range);
336+
let token = LuaNameToken::cast(syntax_id.to_token_from_root(root.syntax())?)?;
337+
let comment = token.ancestors::<LuaComment>().next()?;
338+
let comment_owner = comment.get_owner()?;
339+
match comment_owner {
340+
LuaAst::LuaLocalStat(local_stat) => {
341+
return Some(
342+
local_stat
343+
.get_local_name_list()
344+
.next()?
345+
.get_name_token()?
346+
.get_name_text()
347+
.to_string(),
348+
)
349+
}
350+
LuaAst::LuaAssignStat(assign_stat) => {
351+
return Some(
352+
assign_stat
353+
.child::<LuaVarExpr>()?
354+
.syntax()
355+
.text()
356+
.to_string(),
357+
)
358+
}
359+
_ => {}
360+
}
361+
} else {
362+
for location in locations {
363+
let root = builder
364+
.semantic_model
365+
.get_root_by_file_id(location.file_id)?;
366+
let syntax_id = LuaSyntaxId::new(LuaTokenKind::TkName.into(), location.range);
367+
let token = LuaNameToken::cast(syntax_id.to_token_from_root(root.syntax())?)?;
368+
let comment = token.ancestors::<LuaComment>().next()?;
369+
let comment_owner = comment.get_owner()?;
370+
match comment_owner {
371+
LuaAst::LuaLocalStat(_) => return None,
372+
LuaAst::LuaAssignStat(assign_stat) => {
373+
return Some(
374+
assign_stat
375+
.child::<LuaVarExpr>()?
376+
.syntax()
377+
.text()
378+
.to_string(),
379+
);
380+
}
381+
_ => {}
382+
}
383+
}
384+
}
385+
386+
None
387+
}

0 commit comments

Comments
 (0)