Skip to content

update #539

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 59 commits into from
Jun 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
88cb20e
CHG: 调用函数跳转将会尝试寻找最匹配的定义
xuhuanzy Jun 14, 2025
4450079
fix #502
xuhuanzy Jun 14, 2025
bcee81a
`查找所有实现`会将`signature`放至最前
xuhuanzy Jun 14, 2025
1baeebf
update goto_def_definition
xuhuanzy Jun 14, 2025
a0138cd
optimize class function completion
xuhuanzy Jun 15, 2025
f6c2c47
optimize class field docfunction completion
xuhuanzy Jun 15, 2025
25f2ebc
update .gitignore
xuhuanzy Jun 16, 2025
feed1a9
optimize function completion: handling nullable functions
xuhuanzy Jun 16, 2025
29f635c
hover: signature description not removing first "#" "@"
xuhuanzy Jun 16, 2025
6e3cbec
fix hover text
xuhuanzy Jun 17, 2025
df5ac21
fix hover alias field
xuhuanzy Jun 17, 2025
eee5b44
add meta __call hint
xuhuanzy Jun 19, 2025
f1f0c0c
optimize local var hint
xuhuanzy Jun 19, 2025
0722f59
impl --[[@cast -?]]
xuhuanzy Jun 19, 2025
e9b2291
add fix need_check_nil code action
xuhuanzy Jun 19, 2025
1e45292
update parser, 表字段解析失败不会中止整个表的解析
xuhuanzy Jun 20, 2025
af8cce5
fix test
xuhuanzy Jun 20, 2025
214c075
optimize semantic_token
xuhuanzy Jun 20, 2025
15add0d
completion: add config baseFunctionIncludesName
xuhuanzy Jun 20, 2025
03c165e
update ast. `@cast` name 部分解析为表达式而不是字面量
xuhuanzy Jun 20, 2025
05ce393
Fix tests for @cast parsing changes
xuhuanzy Jun 20, 2025
2c87ca4
fix @cast parse
xuhuanzy Jun 20, 2025
947f916
add diagnostic: CastTypeMismatch
xuhuanzy Jun 20, 2025
f899e8a
update diagnostic: CastTypeMismatch
xuhuanzy Jun 21, 2025
bede0ee
optimize diagnostic: AssignTypeMismatch table
xuhuanzy Jun 22, 2025
461033a
fix #525
xuhuanzy Jun 22, 2025
eb6a24f
fix hover function
xuhuanzy Jun 22, 2025
1e7ebb4
add config inline_values
xuhuanzy Jun 22, 2025
3e1648d
fix hover function in table
xuhuanzy Jun 23, 2025
25deb6d
诊断时 class name 不再是最简名称
xuhuanzy Jun 23, 2025
8b45366
update semantic_tokens
xuhuanzy Jun 23, 2025
6061de4
hover function 也需要处理 union
xuhuanzy Jun 23, 2025
d498b2a
调整 hover function 代码位置
xuhuanzy Jun 23, 2025
3554b8c
fix hover function duplicate description
xuhuanzy Jun 23, 2025
36998a3
update semantic_tokens
xuhuanzy Jun 23, 2025
7f10876
fix hover function 跨文件追踪问题
xuhuanzy Jun 23, 2025
e5425d5
hover function: 移除 find_origin semantic_model 缓存
xuhuanzy Jun 23, 2025
1f04ccb
fix implementation_searcher local var
xuhuanzy Jun 23, 2025
83f7f28
fix #550
xuhuanzy Jun 23, 2025
26ab3a7
fix #549 文件删除时移除该文件的诊断报错
xuhuanzy Jun 23, 2025
d08b109
optimize search_decl_references
xuhuanzy Jun 24, 2025
9a57f66
update definition. 函数调用处跳转定义的目标位于返回语句则尝试寻找原始定义
xuhuanzy Jun 24, 2025
f60a9f0
add config: completion.autoRequireNamingConvention.keep-class
xuhuanzy Jun 24, 2025
6b496df
optimize auto-require: 第一层key的值为类定义时提供自动导入
xuhuanzy Jun 25, 2025
4c9b546
update semantic_token
xuhuanzy Jun 25, 2025
275e929
fix completion function
xuhuanzy Jun 25, 2025
6c5648b
diagnostic: UndefinedField 检查位于判断语句时不再无条件跳过
xuhuanzy Jun 25, 2025
be2cb1b
optimize hint: 不显示当前文件的命名空间
xuhuanzy Jun 25, 2025
fe0391f
fix: 文件夹被重命名时没有移除掉旧文件
xuhuanzy Jun 25, 2025
3fc2a0d
feature: 重命名时添加提示是否修改导入
xuhuanzy Jun 26, 2025
cb4d953
重命名时更新db
xuhuanzy Jun 26, 2025
18a259a
goto_module_file 确保目标文件存在
xuhuanzy Jun 26, 2025
e83dc68
fix DidRenameFiles
xuhuanzy Jun 26, 2025
fd88481
update auto-require
xuhuanzy Jun 26, 2025
7004189
semantic_token: 如果导入的变量是类定义则会染为 class
xuhuanzy Jun 26, 2025
56246c4
update auto-require
xuhuanzy Jun 26, 2025
8be906a
fix hint
xuhuanzy Jun 26, 2025
8e6e437
update semantic_token
xuhuanzy Jun 27, 2025
4b39f83
completion: add @namespace @using
xuhuanzy Jun 27, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/target
.idea
dhat-heap.json
.cursor
dhat-heap.json
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
local a = {}
```

### 🔧 Changed
- **Class Method Completion**: When a function call jumps, if there are multiple declarations, It will then attempt to return the most matching definition along with all actual code declarations, rather than returning all definitions.

### 🐛 Fixed
- **Enum Variable Parameter Issue**: Fixed a crash issue when checking enum variable as parameter
Expand Down
10 changes: 9 additions & 1 deletion crates/emmylua_code_analysis/locales/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,12 @@ Cannot use `...` outside a vararg function.:
"the string template type must be a string constant":
en: "the string template type must be a string constant"
zh_CN: "字符串模板类型必须是字符串常量"
zh_HK: "字串模板類型必須是字串常量"
zh_HK: "字串模板類型必須是字串常量"
"Cannot cast `%{original}` to `%{target}`. %{reason}":
en: "Cannot cast `%{original}` to `%{target}`. %{reason}"
zh_CN: "不能将 `%{original}` 转换为 `%{target}`。%{reason}"
zh_HK: "不能將 `%{original}` 轉換為 `%{target}`。%{reason}"
"type recursion":
en: "type recursion"
zh_CN: "类型递归"
zh_HK: "類型遞歸"
46 changes: 46 additions & 0 deletions crates/emmylua_code_analysis/resources/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"autoRequireFunction": "require",
"autoRequireNamingConvention": "keep",
"autoRequireSeparator": ".",
"baseFunctionIncludesName": true,
"callSnippet": false,
"enable": true,
"postfix": "@"
Expand Down Expand Up @@ -76,6 +77,7 @@
"enable": true,
"indexHint": true,
"localHint": true,
"metaCallHint": true,
"overrideHint": true,
"paramHint": true
},
Expand All @@ -95,6 +97,16 @@
}
]
},
"inlineValues": {
"default": {
"enable": true
},
"allOf": [
{
"$ref": "#/definitions/EmmyrcInlineValues"
}
]
},
"references": {
"default": {
"enable": true,
Expand Down Expand Up @@ -511,6 +523,13 @@
"enum": [
"generic-constraint-mismatch"
]
},
{
"description": "cast-type-mismatch",
"type": "string",
"enum": [
"cast-type-mismatch"
]
}
]
},
Expand Down Expand Up @@ -594,6 +613,11 @@
"default": ".",
"type": "string"
},
"baseFunctionIncludesName": {
"description": "Whether to include the name in the base function completion. effect: `function () end` -> `function name() end`.",
"default": true,
"type": "boolean"
},
"callSnippet": {
"description": "Whether to use call snippets in completions.",
"default": false,
Expand Down Expand Up @@ -710,6 +734,13 @@
"enum": [
"camel-case"
]
},
{
"description": "When returning class definition, use class name, otherwise keep original name.",
"type": "string",
"enum": [
"keep-class"
]
}
]
},
Expand Down Expand Up @@ -741,6 +772,11 @@
"default": true,
"type": "boolean"
},
"metaCallHint": {
"description": "Whether to enable meta __call operator hints.",
"default": true,
"type": "boolean"
},
"overrideHint": {
"description": "Whether to enable override hints.",
"default": true,
Expand All @@ -753,6 +789,16 @@
}
}
},
"EmmyrcInlineValues": {
"type": "object",
"properties": {
"enable": {
"description": "Whether to enable inline values.",
"default": true,
"type": "boolean"
}
}
},
"EmmyrcLuaVersion": {
"oneOf": [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use emmylua_parser::{
LuaAst, LuaAstNode, LuaAstToken, LuaCallExpr, LuaClosureExpr, LuaExpr, LuaIndexExpr,
LuaIndexKey, LuaLiteralExpr, LuaLiteralToken, LuaNameExpr, LuaTableExpr, LuaVarExpr,
LuaAst, LuaAstNode, LuaAstToken, LuaCallExpr, LuaClosureExpr, LuaDocTagCast, LuaExpr,
LuaIndexExpr, LuaIndexKey, LuaLiteralExpr, LuaLiteralToken, LuaNameExpr, LuaTableExpr,
LuaVarExpr,
};

use crate::{
Expand Down Expand Up @@ -50,6 +51,9 @@ pub fn analyze_name_expr(analyzer: &mut DeclAnalyzer, expr: LuaNameExpr) -> Opti
}

pub fn analyze_index_expr(analyzer: &mut DeclAnalyzer, index_expr: LuaIndexExpr) -> Option<()> {
if index_expr.ancestors::<LuaDocTagCast>().next().is_some() {
return Some(());
}
let index_key = index_expr.get_index_key()?;
let key = match index_key {
LuaIndexKey::Name(name) => LuaMemberKey::Name(name.get_name_text().to_string().into()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ pub fn analyze_field(analyzer: &mut DocAnalyzer, tag: LuaDocTagField) -> Option<
for desc in tag.get_descriptions() {
let mut desc_text = desc.get_description_text().to_string();
if !desc_text.is_empty() {
let text = preprocess_description(&mut desc_text);
let text = preprocess_description(&mut desc_text, Some(&property_owner));
if !description.is_empty() {
description.push_str("\n\n");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::sync::Arc;

use emmylua_parser::{
LuaAst, LuaAstNode, LuaDocBinaryType, LuaDocFuncType, LuaDocGenericType,
LuaDocMultiLineUnionType, LuaDocObjectFieldKey, LuaDocObjectType, LuaDocStrTplType, LuaDocType,
LuaDocUnaryType, LuaDocVariadicType, LuaLiteralToken, LuaSyntaxKind, LuaTypeBinaryOperator,
LuaTypeUnaryOperator, LuaVarExpr,
LuaAst, LuaAstNode, LuaDocBinaryType, LuaDocDescriptionOwner, LuaDocFuncType,
LuaDocGenericType, LuaDocMultiLineUnionType, LuaDocObjectFieldKey, LuaDocObjectType,
LuaDocStrTplType, LuaDocType, LuaDocUnaryType, LuaDocVariadicType, LuaLiteralToken,
LuaSyntaxKind, LuaTypeBinaryOperator, LuaTypeUnaryOperator, LuaVarExpr,
};
use rowan::TextRange;
use smol_str::SmolStr;
Expand Down Expand Up @@ -588,7 +588,8 @@ fn infer_multi_line_union_type(
};

let description = if let Some(description) = field.get_description() {
let description_text = preprocess_description(&description.get_description_text());
let description_text =
preprocess_description(&description.get_description_text(), None);
if !description_text.is_empty() {
Some(description_text)
} else {
Expand Down
21 changes: 15 additions & 6 deletions crates/emmylua_code_analysis/src/compilation/analyzer/doc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use super::AnalyzeContext;
use crate::{
db_index::{DbIndex, LuaTypeDeclId},
profile::Profile,
FileId,
FileId, LuaSemanticDeclId,
};
use emmylua_parser::{LuaAstNode, LuaComment, LuaSyntaxNode};
use file_generic_index::FileGenericIndex;
Expand Down Expand Up @@ -44,8 +44,10 @@ fn analyze_comment(analyzer: &mut DocAnalyzer) -> Option<()> {
}

let owenr = get_owner_id(analyzer)?;
let comment_description =
preprocess_description(&comment.get_description()?.get_description_text());
let comment_description = preprocess_description(
&comment.get_description()?.get_description_text(),
Some(&owenr),
);
analyzer.db.get_property_index_mut().add_description(
analyzer.file_id,
owenr,
Expand Down Expand Up @@ -90,9 +92,16 @@ impl<'a> DocAnalyzer<'a> {
}
}

pub fn preprocess_description(mut description: &str) -> String {
if description.starts_with(['#', '@']) {
description = description.trim_start_matches(|c| c == '#' || c == '@');
pub fn preprocess_description(mut description: &str, owner: Option<&LuaSemanticDeclId>) -> String {
let has_remove_start_char = if let Some(owner) = owner {
!matches!(owner, LuaSemanticDeclId::Signature(_))
} else {
true
};
if has_remove_start_char {
if description.starts_with(['#', '@']) {
description = description.trim_start_matches(|c| c == '#' || c == '@');
}
}

let mut result = String::new();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,16 @@ fn add_description_for_type_decl(
) {
let mut description_text = String::new();

// let comment = analyzer.comment.clone();
// if let Some(description) = comment.get_description() {
// let description = preprocess_description(&description.get_description_text(), None);
// if !description.is_empty() {
// description_text.push_str(&description);
// }
// }

for description in descriptions {
let description = preprocess_description(&description.get_description_text());
let description = preprocess_description(&description.get_description_text(), None);
if !description.is_empty() {
if !description_text.is_empty() {
description_text.push_str("\n\n");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub fn analyze_param(analyzer: &mut DocAnalyzer, tag: LuaDocTagParam) -> Option<
}

let description = if let Some(des) = tag.get_description() {
Some(preprocess_description(&des.get_description_text()))
Some(preprocess_description(&des.get_description_text(), None))
} else {
None
};
Expand Down Expand Up @@ -155,7 +155,7 @@ pub fn analyze_param(analyzer: &mut DocAnalyzer, tag: LuaDocTagParam) -> Option<

pub fn analyze_return(analyzer: &mut DocAnalyzer, tag: LuaDocTagReturn) -> Option<()> {
let description = if let Some(des) = tag.get_description() {
Some(preprocess_description(&des.get_description_text()))
Some(preprocess_description(&des.get_description_text(), None))
} else {
None
};
Expand Down Expand Up @@ -383,7 +383,7 @@ pub fn analyze_other(analyzer: &mut DocAnalyzer, other: LuaDocTagOther) -> Optio
let owner = get_owner_id(analyzer)?;
let tag_name = other.get_tag_name()?;
let description = if let Some(des) = other.get_description() {
let description = preprocess_description(&des.get_description_text());
let description = preprocess_description(&des.get_description_text(), None);
format!("@*{}* {}", tag_name, description)
} else {
format!("@*{}*", tag_name)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
use std::collections::HashMap;

use emmylua_parser::{
LuaAst, LuaAstNode, LuaAstToken, LuaBlock, LuaBreakStat, LuaChunk, LuaDocTagCast, LuaGotoStat,
LuaIndexExpr, LuaLabelStat, LuaLoopStat, LuaNameExpr, LuaStat, LuaSyntaxKind, LuaTokenKind,
PathTrait,
LuaAst, LuaAstNode, LuaAstToken, LuaBlock, LuaBreakStat, LuaChunk, LuaComment, LuaDocTagCast,
LuaExpr, LuaGotoStat, LuaIndexExpr, LuaLabelStat, LuaLoopStat, LuaNameExpr, LuaStat,
LuaSyntaxKind, LuaTokenKind, PathTrait,
};
use rowan::{TextRange, TextSize, WalkEvent};
use smol_str::SmolStr;

use crate::{
AnalyzeError, DbIndex, DiagnosticCode, FileId, LuaDeclId, LuaFlowId, LuaVarRefId, LuaVarRefNode,
AnalyzeError, DbIndex, DiagnosticCode, FileId, InFiled, LuaDeclId, LuaFlowId, LuaVarRefId,
LuaVarRefNode,
};

use super::flow_node::{BlockId, FlowNode};
Expand Down Expand Up @@ -285,26 +286,59 @@ fn build_cast_flow(
file_id: FileId,
tag_cast: LuaDocTagCast,
) -> Option<()> {
let name_token = tag_cast.get_name_token()?;
let decl_tree = db.get_decl_index().get_decl_tree(&file_id)?;
let text = name_token.get_name_text();
if let Some(decl) = decl_tree.find_local_decl(text, name_token.get_position()) {
let decl_id = decl.get_id();
builder.add_flow_node(
LuaVarRefId::DeclId(decl_id),
LuaVarRefNode::CastRef(tag_cast.clone()),
);
} else {
let ref_id = LuaVarRefId::Name(SmolStr::new(text));
if db
.get_decl_index()
.get_decl(&LuaDeclId::new(file_id, name_token.get_position()))
.is_none()
{
builder.add_flow_node(ref_id, LuaVarRefNode::CastRef(tag_cast.clone()));
match tag_cast.get_key_expr() {
Some(target_expr) => {
let text = match &target_expr {
LuaExpr::NameExpr(name_expr) => name_expr.get_name_text()?,
LuaExpr::IndexExpr(index_expr) => index_expr.get_access_path()?,
_ => {
return None;
}
};

let decl_tree = db.get_decl_index().get_decl_tree(&file_id)?;
if let Some(decl) = decl_tree.find_local_decl(&text, target_expr.get_position()) {
let decl_id = decl.get_id();
builder.add_flow_node(
LuaVarRefId::DeclId(decl_id),
LuaVarRefNode::CastRef(tag_cast.clone()),
);
} else {
let ref_id = LuaVarRefId::Name(SmolStr::new(text));
if db
.get_decl_index()
.get_decl(&LuaDeclId::new(file_id, target_expr.get_position()))
.is_none()
{
builder.add_flow_node(ref_id, LuaVarRefNode::CastRef(tag_cast.clone()));
}
}
}
}
None => {
// 没有指定名称, 则附加到最近的表达式上
let comment = tag_cast.get_parent::<LuaComment>()?;
let mut left_token = comment.syntax().first_token()?.prev_token()?;
if left_token.kind() == LuaTokenKind::TkWhitespace.into() {
left_token = left_token.prev_token()?;
}

let mut ast_node = left_token.parent()?;
loop {
if LuaExpr::can_cast(ast_node.kind().into()) {
break;
} else if LuaBlock::can_cast(ast_node.kind().into()) {
return None;
}
ast_node = ast_node.parent()?;
}
let expr = LuaExpr::cast(ast_node)?;
let in_filed_syntax_id = InFiled::new(file_id, expr.get_syntax_id());
builder.add_flow_node(
LuaVarRefId::SyntaxId(in_filed_syntax_id),
LuaVarRefNode::CastRef(tag_cast.clone()),
);
}
}
Some(())
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use emmylua_parser::{
BinaryOperator, LuaAssignStat, LuaAstNode, LuaExpr, LuaFuncStat, LuaIndexExpr,
BinaryOperator, LuaAssignStat, LuaAstNode, LuaAstToken, LuaExpr, LuaFuncStat, LuaIndexExpr,
LuaLocalFuncStat, LuaLocalStat, LuaTableField, LuaVarExpr, PathTrait,
};

Expand Down Expand Up @@ -542,7 +542,8 @@ pub fn try_add_class_default_call(
decl_id.into(),
LuaOperatorMetaMethod::Call,
analyzer.file_id,
index_expr.get_range(),
// 必须指向名称, 使用 index_expr 的完整范围不会跳转到函数上
index_expr.get_name_token()?.syntax().text_range(),
OperatorFunction::DefaultCall(signature_id),
);
analyzer.db.get_operator_index_mut().add_operator(operator);
Expand Down
Loading