Skip to content

Commit 0476e17

Browse files
committed
feature: 索引成员允许定义一个别名, 补全时将使用该别名作为显示
如果注释以 [xxx] 开头则认为为索引添加了一个补全用别名, 在`[]`内部也允许前后存在空白字符. ```lua ---@Class Point ---@field [1] number # [ x ] ---@field [2] number # [y] ---@field [3] number # [z] ---@field [4] number # 前面存在非空白字符则不是在定义补全用别名. [error] local test = { [1] = 1, -- [nameA] } ```
1 parent 19c946a commit 0476e17

File tree

8 files changed

+145
-46
lines changed

8 files changed

+145
-46
lines changed

crates/emmylua_ls/locales/misc.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
_version: 2
2+
completion.index %{label}:
3+
en: index %{label}
4+
zh_CN: 索引 %{label}
5+
zh_HK: 索引 %{label}

crates/emmylua_ls/src/handlers/completion/add_completions/add_decl_completion.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ pub fn add_decl_completion(
1818
let property_owner = LuaSemanticDeclId::LuaDecl(decl_id);
1919
check_visibility(builder, property_owner.clone())?;
2020

21-
let function_overload_count = count_function_overloads(builder.semantic_model.get_db(), typ);
21+
let overload_count = count_function_overloads(builder.semantic_model.get_db(), typ);
2222
let mut completion_item = CompletionItem {
2323
label: name.to_string(),
2424
kind: Some(get_completion_kind(&typ)),
2525
data: CompletionData::from_property_owner_id(
2626
builder,
2727
decl_id.into(),
28-
function_overload_count,
28+
overload_count,
2929
),
3030
label_details: Some(lsp_types::CompletionItemLabelDetails {
3131
detail: get_detail(builder, &typ, CallDisplay::None),

crates/emmylua_ls/src/handlers/completion/add_completions/add_member_completion.rs

Lines changed: 91 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub fn add_member_completion(
2626
builder: &mut CompletionBuilder,
2727
member_info: LuaMemberInfo,
2828
status: CompletionTriggerStatus,
29-
function_overload_count: Option<usize>,
29+
overload_count: Option<usize>,
3030
) -> Option<()> {
3131
if builder.is_cancelled() {
3232
return None;
@@ -58,28 +58,19 @@ pub fn add_member_completion(
5858
},
5959
};
6060

61-
let typ = member_info.typ;
61+
let typ = &member_info.typ;
6262
let remove_nil_type =
63-
get_function_remove_nil(&builder.semantic_model.get_db(), &typ).unwrap_or(typ);
63+
get_function_remove_nil(&builder.semantic_model.get_db(), typ).unwrap_or(typ.clone());
6464
if status == CompletionTriggerStatus::Colon && !remove_nil_type.is_function() {
6565
return None;
6666
}
6767

6868
// 附加数据, 用于在`resolve`时进一步处理
6969
let completion_data = if let Some(id) = &property_owner {
7070
if let Some(index) = member_info.overload_index {
71-
CompletionData::from_overload(
72-
builder,
73-
id.clone().into(),
74-
index,
75-
function_overload_count,
76-
)
71+
CompletionData::from_overload(builder, id.clone().into(), index, overload_count)
7772
} else {
78-
CompletionData::from_property_owner_id(
79-
builder,
80-
id.clone().into(),
81-
function_overload_count,
82-
)
73+
CompletionData::from_property_owner_id(builder, id.clone().into(), overload_count)
8374
}
8475
} else {
8576
None
@@ -137,7 +128,12 @@ pub fn add_member_completion(
137128
);
138129
}
139130

140-
builder.add_completion_item(completion_item)?;
131+
// 尝试添加别名补全项, 如果添加成功, 则不添加原本 `[index]` 补全项
132+
if !try_add_alias_completion_item(builder, &member_info, &completion_item, &label)
133+
.unwrap_or(false)
134+
{
135+
builder.add_completion_item(completion_item)?;
136+
}
141137

142138
// add overloads if the type is function
143139
add_signature_overloads(
@@ -147,7 +143,7 @@ pub fn add_member_completion(
147143
call_display,
148144
deprecated,
149145
label,
150-
function_overload_count,
146+
overload_count,
151147
);
152148

153149
Some(())
@@ -312,3 +308,82 @@ fn get_resolve_function_params_str(typ: &LuaType, display: CallDisplay) -> Optio
312308
_ => None,
313309
}
314310
}
311+
312+
/// 添加索引成员的别名补全项
313+
fn try_add_alias_completion_item(
314+
builder: &mut CompletionBuilder,
315+
member_info: &LuaMemberInfo,
316+
completion_item: &CompletionItem,
317+
label: &String,
318+
) -> Option<bool> {
319+
let alias_label = extract_index_member_alias(builder, member_info)?;
320+
321+
let mut alias_completion_item = completion_item.clone();
322+
alias_completion_item.label = alias_label;
323+
alias_completion_item.insert_text = Some(label.clone());
324+
325+
// 更新 label_details 添加别名提示
326+
let index_hint = t!("completion.index %{label}", label = label).to_string();
327+
let label_details = alias_completion_item
328+
.label_details
329+
.get_or_insert_with(Default::default);
330+
label_details.description = match label_details.description.take() {
331+
Some(desc) => Some(format!("({}) {} ", index_hint, desc)),
332+
None => Some(index_hint),
333+
};
334+
builder.add_completion_item(alias_completion_item)?;
335+
Some(true)
336+
}
337+
338+
/// 从注释中提取索引成员的别名, 只处理整数成员.
339+
/// 格式为`-- [nameX]`.
340+
fn extract_index_member_alias(
341+
builder: &mut CompletionBuilder,
342+
member_info: &LuaMemberInfo,
343+
) -> Option<String> {
344+
let LuaMemberKey::Integer(_) = member_info.key else {
345+
return None;
346+
};
347+
348+
let property_owner_id = member_info.property_owner_id.as_ref()?;
349+
let LuaSemanticDeclId::Member(_) = property_owner_id else {
350+
return None;
351+
};
352+
353+
let description = builder
354+
.semantic_model
355+
.get_db()
356+
.get_property_index()
357+
.get_property(property_owner_id)?
358+
.description
359+
.as_ref()?;
360+
361+
// 只去掉左侧空白字符,保留右侧内容以支持后续文本
362+
let left_trimmed = description.trim_start();
363+
if !left_trimmed.starts_with('[') {
364+
return None;
365+
}
366+
367+
// 找到对应的右方括号
368+
let close_bracket_pos = left_trimmed.find(']')?;
369+
370+
let content = left_trimmed[1..close_bracket_pos].trim();
371+
372+
if content.is_empty() {
373+
return None;
374+
}
375+
376+
let first_char = content.chars().next()?;
377+
if !first_char.is_alphabetic() && first_char != '_' {
378+
return None;
379+
}
380+
381+
if !content.chars().all(|c| c.is_alphanumeric() || c == '_') {
382+
return None;
383+
}
384+
if content.parse::<i64>().is_ok() || content.parse::<f64>().is_ok() {
385+
return None;
386+
}
387+
388+
Some(content.to_string())
389+
}

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

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@ pub struct CompletionData {
99
pub field_id: FileId,
1010
pub typ: CompletionDataType,
1111
/// Total count of function overloads
12-
pub function_overload_count: Option<usize>,
12+
pub overload_count: Option<usize>,
1313
}
1414

1515
#[allow(unused)]
1616
impl CompletionData {
1717
pub fn from_property_owner_id(
1818
builder: &CompletionBuilder,
1919
id: LuaSemanticDeclId,
20-
function_overload_count: Option<usize>,
20+
overload_count: Option<usize>,
2121
) -> Option<Value> {
2222
let data = Self {
2323
field_id: builder.semantic_model.get_file_id(),
2424
typ: CompletionDataType::PropertyOwnerId(id),
25-
function_overload_count,
25+
overload_count,
2626
};
2727
Some(serde_json::to_value(data).unwrap())
2828
}
@@ -31,12 +31,12 @@ impl CompletionData {
3131
builder: &CompletionBuilder,
3232
id: LuaSemanticDeclId,
3333
index: usize,
34-
function_overload_count: Option<usize>,
34+
overload_count: Option<usize>,
3535
) -> Option<Value> {
3636
let data = Self {
3737
field_id: builder.semantic_model.get_file_id(),
3838
typ: CompletionDataType::Overload((id, index)),
39-
function_overload_count,
39+
overload_count,
4040
};
4141
Some(serde_json::to_value(data).unwrap())
4242
}
@@ -45,7 +45,7 @@ impl CompletionData {
4545
let data = Self {
4646
field_id: builder.semantic_model.get_file_id(),
4747
typ: CompletionDataType::Module(module),
48-
function_overload_count: None,
48+
overload_count: None,
4949
};
5050
Some(serde_json::to_value(data).unwrap())
5151
}
@@ -81,7 +81,7 @@ pub enum CompletionDataType {
8181
// },
8282
// };
8383

84-
// let overload_part = match self.function_overload_count {
84+
// let overload_part = match self.overload_count {
8585
// Some(count) => format!("|{}", count),
8686
// None => String::new(),
8787
// };
@@ -160,8 +160,8 @@ pub enum CompletionDataType {
160160
// return Err(E::custom("expected ':' separator in type part"));
161161
// };
162162

163-
// // Parse function_overload_count
164-
// let function_overload_count = if parts.len() == 3 {
163+
// // Parse overload_count
164+
// let overload_count = if parts.len() == 3 {
165165
// if parts[2].is_empty() {
166166
// None
167167
// } else {
@@ -178,7 +178,7 @@ pub enum CompletionDataType {
178178
// Ok(CompletionData {
179179
// field_id,
180180
// typ,
181-
// function_overload_count,
181+
// overload_count,
182182
// })
183183
// }
184184
// }
@@ -199,7 +199,7 @@ pub enum CompletionDataType {
199199
// let data = CompletionData {
200200
// field_id: FileId::new(1),
201201
// typ: CompletionDataType::PropertyOwnerId(LuaSemanticDeclId::TypeDecl(type_id)),
202-
// function_overload_count: Some(3),
202+
// overload_count: Some(3),
203203
// };
204204

205205
// // Test serialization
@@ -219,7 +219,7 @@ pub enum CompletionDataType {
219219
// let data = CompletionData {
220220
// field_id: FileId::new(42),
221221
// typ: CompletionDataType::Module("socket.core".to_string()),
222-
// function_overload_count: None,
222+
// overload_count: None,
223223
// };
224224

225225
// let json = serde_json::to_string(&data).unwrap();
@@ -235,7 +235,7 @@ pub enum CompletionDataType {
235235
// let data = CompletionData {
236236
// field_id: FileId::new(10),
237237
// typ: CompletionDataType::Overload((LuaSemanticDeclId::TypeDecl(type_id), 2)),
238-
// function_overload_count: Some(5),
238+
// overload_count: Some(5),
239239
// };
240240

241241
// let json = serde_json::to_string(&data).unwrap();
@@ -251,7 +251,7 @@ pub enum CompletionDataType {
251251
// let data = CompletionData {
252252
// field_id: FileId::new(999),
253253
// typ: CompletionDataType::PropertyOwnerId(LuaSemanticDeclId::TypeDecl(type_id.clone())),
254-
// function_overload_count: Some(10),
254+
// overload_count: Some(10),
255255
// };
256256

257257
// // Our compact serialization
@@ -262,13 +262,13 @@ pub enum CompletionDataType {
262262
// struct DefaultSerialized {
263263
// field_id: u32,
264264
// typ: CompletionDataType,
265-
// function_overload_count: Option<usize>,
265+
// overload_count: Option<usize>,
266266
// }
267267

268268
// let default_data = DefaultSerialized {
269269
// field_id: data.field_id.id,
270270
// typ: data.typ.clone(),
271-
// function_overload_count: data.function_overload_count,
271+
// overload_count: data.overload_count,
272272
// };
273273

274274
// let default_json = serde_json::to_string(&default_data).unwrap();

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ fn add_resolve_member_infos(
104104
.iter()
105105
.all(|info| matches!(info.typ, LuaType::DocFunction(_)));
106106

107-
let function_count = count_function_overloads(builder.semantic_model.get_db(), &member_infos);
107+
let overload_count = count_function_overloads(builder.semantic_model.get_db(), &member_infos);
108108

109109
for member_info in member_infos {
110110
match resolve_state {
@@ -113,7 +113,7 @@ fn add_resolve_member_infos(
113113
builder,
114114
member_info.clone(),
115115
completion_status,
116-
function_count,
116+
overload_count,
117117
);
118118
if limit_doc_function {
119119
break;
@@ -126,7 +126,7 @@ fn add_resolve_member_infos(
126126
builder,
127127
member_info.clone(),
128128
completion_status,
129-
function_count,
129+
overload_count,
130130
);
131131
if limit_doc_function {
132132
break;
@@ -141,7 +141,7 @@ fn add_resolve_member_infos(
141141
builder,
142142
member_info.clone(),
143143
completion_status,
144-
function_count,
144+
overload_count,
145145
);
146146
if limit_doc_function {
147147
break;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ pub fn add_completions(builder: &mut CompletionBuilder) -> Option<()> {
3939

4040
for (index, item) in builder.get_completion_items_mut().iter_mut().enumerate() {
4141
if item.sort_text.is_none() {
42-
item.sort_text = Some(format!("{:04}", index + 1));
42+
item.sort_text = Some(format!("{:04}", index + 32));
4343
}
4444
}
4545

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub fn resolve_completion(
2424
if let Some(mut hover_builder) = hover_builder {
2525
update_function_signature_info(
2626
&mut hover_builder,
27-
completion_data.function_overload_count,
27+
completion_data.overload_count,
2828
);
2929
if client_id.is_vscode() {
3030
build_vscode_completion_item(completion_item, hover_builder, None);
@@ -39,7 +39,7 @@ pub fn resolve_completion(
3939
if let Some(mut hover_builder) = hover_builder {
4040
update_function_signature_info(
4141
&mut hover_builder,
42-
completion_data.function_overload_count,
42+
completion_data.overload_count,
4343
);
4444
if client_id.is_vscode() {
4545
build_vscode_completion_item(completion_item, hover_builder, Some(index));
@@ -55,19 +55,19 @@ pub fn resolve_completion(
5555

5656
pub fn update_function_signature_info(
5757
hover_builder: &mut HoverBuilder,
58-
function_overload_count: Option<usize>,
58+
overload_count: Option<usize>,
5959
) {
60-
if let Some(function_overload_count) = function_overload_count {
61-
if function_overload_count > 0 {
60+
if let Some(overload_count) = overload_count {
61+
if overload_count > 0 {
6262
if let Some(signature_overload) = &mut hover_builder.signature_overload {
6363
for signature in signature_overload.iter_mut() {
6464
if let MarkedString::LanguageString(s) = signature {
65-
s.value = format!("{} (+{} overloads)", s.value, function_overload_count);
65+
s.value = format!("{} (+{} overloads)", s.value, overload_count);
6666
}
6767
}
6868
}
6969
if let MarkedString::LanguageString(s) = &mut hover_builder.type_description {
70-
s.value = format!("{} (+{} overloads)", s.value, function_overload_count);
70+
s.value = format!("{} (+{} overloads)", s.value, overload_count);
7171
}
7272
}
7373
}

0 commit comments

Comments
 (0)