Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
32a9f32
Add new feature `attribute` AST parsing
xuhuanzy Sep 22, 2025
6942c4e
优化结构
xuhuanzy Sep 23, 2025
7dd1eb7
impl analyzer `attribute`
xuhuanzy Sep 24, 2025
dc2fae2
Add built-in feature impl: deprecated, skip_diagnostic
xuhuanzy Sep 24, 2025
6dd62de
修改索引别名实现
xuhuanzy Sep 24, 2025
300e7bf
删除原索引别名(index field alias)实现
xuhuanzy Sep 24, 2025
e530d7c
`attribute` add hover and completion
xuhuanzy Sep 24, 2025
0d81fee
整理 hover
xuhuanzy Sep 24, 2025
812f0f2
`attribute` add diagnostic
xuhuanzy Sep 24, 2025
30c9c99
optimize `attribute` completion
xuhuanzy Sep 24, 2025
a3f535a
update CHANGELOG.md
xuhuanzy Sep 24, 2025
c130dc9
add `attribute` semantic_token
xuhuanzy Sep 25, 2025
abd0207
update `attribute` ast
xuhuanzy Sep 25, 2025
27082f5
Change `DocAttribute` to `DocTypeFlag`
xuhuanzy Sep 26, 2025
ef9a937
update `attribute` ast, allow embedding of @param and @generic
xuhuanzy Sep 26, 2025
fafa7fe
Add `attribute` to function generic parameters
xuhuanzy Sep 26, 2025
b9593ef
Add `attribute` to function parameters and class generics
xuhuanzy Sep 27, 2025
10c8239
updated the `attribute` AST to allow usage in `@return`
xuhuanzy Sep 27, 2025
28b8c5b
添加`---@attribute class_ctor`
xuhuanzy Sep 28, 2025
482e20e
remove `runtime.class_default_call`
xuhuanzy Sep 29, 2025
5de19d2
fix Clippy
xuhuanzy Sep 30, 2025
ba9d4d3
Prevent Potential Subtraction Overflow
xuhuanzy Sep 30, 2025
8b6167b
fix typos
xuhuanzy Oct 9, 2025
00885f1
Merge remote-tracking branch 'EmmyLuaLs/main' into attribute
xuhuanzy Oct 11, 2025
05eb741
将特性`class_ctor`命名调整为`constructor`
xuhuanzy Oct 13, 2025
07b7bd0
rename `LuaTypeAttribute` to `LuaTypeFlag`
xuhuanzy Oct 14, 2025
7eb8cf4
update attribute
xuhuanzy Oct 14, 2025
e642ebf
attribute: add field_accessor
xuhuanzy Oct 15, 2025
f193a60
update attribute `constructor`
xuhuanzy Oct 16, 2025
7c249c2
Merge remote-tracking branch 'EmmyLuaLs/main' into attribute
xuhuanzy Oct 20, 2025
a737ce9
rename attribute `skip_diagnostic` to `lsp_perf_optim`
xuhuanzy Oct 20, 2025
fbd05b1
attribute: 移除内嵌使用
xuhuanzy Oct 20, 2025
a8ec1af
Merge remote-tracking branch 'EmmyLuaLs/main' into attribute
xuhuanzy Oct 24, 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
51 changes: 51 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,57 @@
*All notable changes to the EmmyLua Analyzer Rust project will be documented in this file.*

---
## [0.17.0] - Unreleased

### 🔧 Changed
- **Refactor IndexAliasName**: 删除原先的索引别名实现(`-- [IndexAliasName]`), 现在使用`---@[index_alias("name")]`
- **Refactor ClassDefaultCall**: 删除配置项`runtime.class_default_call`, 转为使用`---@[constructor("<constructor_method_name>")]`

### ✨ Added
- **Attribute**: 实现了新的特性`---@attribute`,用于定义附加元数据,内置多个特性:
```lua
--- Deprecated. Receives an optional message parameter.
---@attribute deprecated(message: string?)

--- Language Server Performance Optimization Items.
---
--- Receives a parameter, the options are:
--- - `check_table_field` - Skip the assign check for table fields. It is recommended to use this option for all large configuration tables.
---@attribute lsp_perf_optim(code: "check_table_field"|string)

--- Index field alias, will be displayed in `hint` and `completion`.
---
--- Receives a string parameter for the alias name.
---@attribute index_alias(name: string)

--- This attribute must be applied to function parameters, and the function parameter's type must be a string template generic,
--- used to specify the default constructor of a class.
---
--- Parameters:
--- - `name` - The name of the method as a constructor.
--- - `root_class` - Used to mark the root class, will implicitly inherit this class, such as `System.Object` in c#. Defaults to empty.
--- - `strip_self` - Whether the `self` parameter can be omitted when calling the constructor, defaults to `true`
--- - `return_self` - Whether the constructor is forced to return `self`, defaults to `true`
---@attribute constructor(name: string, root_class: string?, strip_self: boolean?, return_self: boolean?)

--- Associates `getter` and `setter` methods with a field. Currently provides only definition navigation functionality,
--- and the target methods must reside within the same class.
---
--- Parameters:
--- - convention: Naming convention, defaults to `camelCase`. Implicitly adds `get` and `set` prefixes. eg: `_age` -> `getAge`, `setAge`.
--- - getter: Getter method name. Takes precedence over `convention`.
--- - setter: Setter method name. Takes precedence over `convention`.
---@attribute field_accessor(convention: "camelCase"|"PascalCase"|"snake_case"|nil, getter: string?, setter: string?)
```

使用语法为 `---@[attribute_name_1(arg...), attribute_name_2(arg...), ...]`, 可以同时使用多个特性, 示例:
```lua
---@class A
---@[deprecated] # 如果特性可以省略参数, 则可以省略`()`
---@field b string # b 此时被标记为弃用
---@[index_alias("b")]
---@field [1] string # 此时在提示和补全中会显示为 `b`
```

## [0.16.0] - 2025-10-17
### ✨ Added
Expand Down
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions crates/emmylua_code_analysis/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ include = [
"src/**",
"resources/**",
]
[dev-dependencies]
googletest.workspace = true

# Inherit workspace lints configuration
[lints]
Expand Down
49 changes: 15 additions & 34 deletions crates/emmylua_code_analysis/resources/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,6 @@
"runtime": {
"$ref": "#/$defs/EmmyrcRuntime",
"default": {
"classDefaultCall": {
"forceNonColon": false,
"forceReturnSelf": false,
"functionName": ""
},
"extensions": [],
"frameworkVersions": [],
"nonstandardSymbol": [],
Expand Down Expand Up @@ -163,26 +158,6 @@
}
},
"$defs": {
"ClassDefaultCall": {
"type": "object",
"properties": {
"forceNonColon": {
"description": "Mandatory non`:` definition. When `function_name` is not empty, it takes effect.",
"type": "boolean",
"default": true
},
"forceReturnSelf": {
"description": "Force to return `self`.",
"type": "boolean",
"default": true
},
"functionName": {
"description": "class default overload function. eg. \"__init\".",
"type": "string",
"default": ""
}
}
},
"DiagnosticCode": {
"oneOf": [
{
Expand Down Expand Up @@ -435,6 +410,21 @@
"description": "Global variable defined in non-module scope",
"type": "string",
"const": "global-in-non-module"
},
{
"description": "attribute-param-type-mismatch",
"type": "string",
"const": "attribute-param-type-mismatch"
},
{
"description": "attribute-missing-parameter",
"type": "string",
"const": "attribute-missing-parameter"
},
{
"description": "attribute-redundant-parameter",
"type": "string",
"const": "attribute-redundant-parameter"
}
]
},
Expand Down Expand Up @@ -942,15 +932,6 @@
"EmmyrcRuntime": {
"type": "object",
"properties": {
"classDefaultCall": {
"description": "class default overload function.",
"$ref": "#/$defs/ClassDefaultCall",
"default": {
"forceNonColon": false,
"forceReturnSelf": false,
"functionName": ""
}
},
"extensions": {
"description": "file Extensions. eg: .lua, .lua.txt",
"type": "array",
Expand Down
35 changes: 35 additions & 0 deletions crates/emmylua_code_analysis/resources/std/builtin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,38 @@
---@alias TypeGuard<T> boolean

---@alias Language<T: string> string

--- attribute

--- Deprecated. Receives an optional message parameter.
---@attribute deprecated(message: string?)

--- Language Server Performance Optimization Items.
---
--- Receives a parameter, the options are:
--- - `check_table_field` - Skip the assign check for table fields. It is recommended to use this option for all large configuration tables.
---@attribute lsp_perf_optim(code: "check_table_field"|string)

--- Index field alias, will be displayed in `hint` and `completion`.
---
--- Receives a string parameter for the alias name.
---@attribute index_alias(name: string)

--- This attribute must be applied to function parameters, and the function parameter's type must be a string template generic,
--- used to specify the default constructor of a class.
---
--- Parameters:
--- - `name` - The name of the method as a constructor.
--- - `root_class` - Used to mark the root class, will implicitly inherit this class, such as `System.Object` in c#. Defaults to empty.
--- - `strip_self` - Whether the `self` parameter can be omitted when calling the constructor, defaults to `true`
--- - `return_self` - Whether the constructor is forced to return `self`, defaults to `true`
---@attribute constructor(name: string, root_class: string?, strip_self: boolean?, return_self: boolean?)

--- Associates `getter` and `setter` methods with a field. Currently provides only definition navigation functionality,
--- and the target methods must reside within the same class.
---
--- Parameters:
--- - convention: Naming convention, defaults to `camelCase`. Implicitly adds `get` and `set` prefixes. eg: `_age` -> `getAge`, `setAge`.
--- - getter: Getter method name. Takes precedence over `convention`.
--- - setter: Setter method name. Takes precedence over `convention`.
---@attribute field_accessor(convention: "camelCase"|"PascalCase"|"snake_case"|nil, getter: string?, setter: string?)
61 changes: 40 additions & 21 deletions crates/emmylua_code_analysis/src/compilation/analyzer/decl/docs.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use emmylua_parser::{
LuaAstNode, LuaAstToken, LuaComment, LuaDocAttribute, LuaDocTag, LuaDocTagAlias,
LuaAstNode, LuaAstToken, LuaComment, LuaDocTag, LuaDocTagAlias, LuaDocTagAttribute,
LuaDocTagClass, LuaDocTagEnum, LuaDocTagMeta, LuaDocTagNamespace, LuaDocTagUsing,
LuaDocTypeFlag,
};
use flagset::FlagSet;
use rowan::TextRange;

use crate::{
LuaTypeDecl, LuaTypeDeclId,
db_index::{LuaDeclTypeKind, LuaTypeAttribute},
db_index::{LuaDeclTypeKind, LuaTypeFlag},
};

use super::DeclAnalyzer;
Expand All @@ -16,39 +17,39 @@ pub fn analyze_doc_tag_class(analyzer: &mut DeclAnalyzer, class: LuaDocTagClass)
let name_token = class.get_name_token()?;
let name = name_token.get_name_text().to_string();
let range = name_token.syntax().text_range();
let attrib = get_attrib_value(analyzer, class.get_attrib());
let type_flag = get_type_flag_value(analyzer, class.get_type_flag());

add_type_decl(analyzer, &name, range, LuaDeclTypeKind::Class, attrib);
add_type_decl(analyzer, &name, range, LuaDeclTypeKind::Class, type_flag);
Some(())
}

fn get_attrib_value(
fn get_type_flag_value(
analyzer: &mut DeclAnalyzer,
attrib: Option<LuaDocAttribute>,
) -> FlagSet<LuaTypeAttribute> {
let mut attr: FlagSet<LuaTypeAttribute> = if analyzer.is_meta {
LuaTypeAttribute::Meta.into()
flag: Option<LuaDocTypeFlag>,
) -> FlagSet<LuaTypeFlag> {
let mut attr: FlagSet<LuaTypeFlag> = if analyzer.is_meta {
LuaTypeFlag::Meta.into()
} else {
LuaTypeAttribute::None.into()
LuaTypeFlag::None.into()
};

if let Some(attrib) = attrib {
for token in attrib.get_attrib_tokens() {
if let Some(flag) = flag {
for token in flag.get_attrib_tokens() {
match token.get_name_text() {
"partial" => {
attr |= LuaTypeAttribute::Partial;
attr |= LuaTypeFlag::Partial;
}
"key" => {
attr |= LuaTypeAttribute::Key;
attr |= LuaTypeFlag::Key;
}
// "global" => {
// attr |= LuaTypeAttribute::Global;
// }
"exact" => {
attr |= LuaTypeAttribute::Exact;
attr |= LuaTypeFlag::Exact;
}
"constructor" => {
attr |= LuaTypeAttribute::Constructor;
attr |= LuaTypeFlag::Constructor;
}
_ => {}
}
Expand All @@ -62,9 +63,9 @@ pub fn analyze_doc_tag_enum(analyzer: &mut DeclAnalyzer, enum_: LuaDocTagEnum) -
let name_token = enum_.get_name_token()?;
let name = name_token.get_name_text().to_string();
let range = name_token.syntax().text_range();
let attrib = get_attrib_value(analyzer, enum_.get_attrib());
let flag = get_type_flag_value(analyzer, enum_.get_type_flag());

add_type_decl(analyzer, &name, range, LuaDeclTypeKind::Enum, attrib);
add_type_decl(analyzer, &name, range, LuaDeclTypeKind::Enum, flag);
Some(())
}

Expand All @@ -78,7 +79,25 @@ pub fn analyze_doc_tag_alias(analyzer: &mut DeclAnalyzer, alias: LuaDocTagAlias)
&name,
range,
LuaDeclTypeKind::Alias,
LuaTypeAttribute::None.into(),
LuaTypeFlag::None.into(),
);
Some(())
}

pub fn analyze_doc_tag_attribute(
analyzer: &mut DeclAnalyzer,
attribute: LuaDocTagAttribute,
) -> Option<()> {
let name_token = attribute.get_name_token()?;
let name = name_token.get_name_text().to_string();
let range = name_token.syntax().text_range();

add_type_decl(
analyzer,
&name,
range,
LuaDeclTypeKind::Attribute,
LuaTypeFlag::None.into(),
);
Some(())
}
Expand Down Expand Up @@ -166,7 +185,7 @@ fn add_type_decl(
name: &str,
range: TextRange,
kind: LuaDeclTypeKind,
attrib: FlagSet<LuaTypeAttribute>,
flag: FlagSet<LuaTypeFlag>,
) {
let file_id = analyzer.get_file_id();
let type_index = analyzer.db.get_type_index_mut();
Expand All @@ -180,6 +199,6 @@ fn add_type_decl(
let simple_name = id.get_simple_name();
type_index.add_type_decl(
file_id,
LuaTypeDecl::new(file_id, range, simple_name.to_string(), kind, attrib, id),
LuaTypeDecl::new(file_id, range, simple_name.to_string(), kind, flag, id),
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ fn walk_node_enter(analyzer: &mut DeclAnalyzer, node: LuaAst) {
LuaAst::LuaDocTagAlias(doc_tag) => {
docs::analyze_doc_tag_alias(analyzer, doc_tag);
}
LuaAst::LuaDocTagAttribute(doc_tag) => {
docs::analyze_doc_tag_attribute(analyzer, doc_tag);
}
LuaAst::LuaDocTagNamespace(doc_tag) => {
docs::analyze_doc_tag_namespace(analyzer, doc_tag);
}
Expand Down
Loading