Skip to content
Open

update #1017

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a03d71e
refactor(visibility): improve type visibility
xuhuanzy Apr 2, 2026
0fb0552
refactor(conf): `workspace.packageDirs` rename to `workspace.packages`
xuhuanzy Apr 2, 2026
ac8753b
fix(workspace): correctly resolve `package`
xuhuanzy Apr 2, 2026
c6b578d
feat(type): add LuaTypeIdentifier::Internal
xuhuanzy Apr 3, 2026
0ea6e75
perf: update find_type_decls
xuhuanzy Apr 3, 2026
168f34f
feat(diagnostic): add InconsistentTypeAccessModifier
xuhuanzy Apr 3, 2026
41b929e
fix(completion): add internal type flag
xuhuanzy Apr 3, 2026
71bb27a
Merge remote-tracking branch 'EmmyLuaLs/main' into update
xuhuanzy Apr 3, 2026
0e23dac
fix: default visibility of the property should be `public`
xuhuanzy Apr 3, 2026
bd1dcec
fix: #1007
xuhuanzy Apr 5, 2026
ec80f0e
fix: #1008
xuhuanzy Apr 5, 2026
cf70a39
refactor: `constructor`特性参数从`return_self`更改为`return_mode`
xuhuanzy Apr 5, 2026
044ccfe
fix(parse): attributeuse supports more types
xuhuanzy Apr 5, 2026
b1d4bd6
update: IncompleteSignatureDoc
xuhuanzy Apr 6, 2026
de897d5
fix: `constructor` attribute
xuhuanzy Apr 6, 2026
13b57fe
fix: correct TypeGuard narrowing for inherited types
xuhuanzy Apr 6, 2026
2369877
fix: test_generic_infer_function
xuhuanzy Apr 6, 2026
8707614
refactor: instantiate conditional generic
xuhuanzy Apr 7, 2026
7b08378
fix: conditional generic
xuhuanzy Apr 7, 2026
23eb092
Merge remote-tracking branch 'EmmyLuaLs/main' into fix
xuhuanzy Apr 7, 2026
952f1de
ci: update test.yml
xuhuanzy Apr 7, 2026
e35dad1
Merge remote-tracking branch 'EmmyLuaLs/main' into fix
xuhuanzy Apr 10, 2026
f9df1d2
update i18n
xuhuanzy Apr 10, 2026
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
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
- name: Run Tests
run: cargo test
run: cargo test --workspace --features emmylua_ls/full-test

check-schema:
name: Check schema generation
Expand Down
24 changes: 10 additions & 14 deletions crates/emmylua_check/src/init.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use emmylua_code_analysis::{
EmmyLuaAnalysis, WorkspaceFolder, collect_workspace_files, load_configs, update_code_style,
EmmyLuaAnalysis, WorkspaceFolder, build_workspace_folders, collect_workspace_files,
load_configs, update_code_style,
};
use fern::Dispatch;
use log::LevelFilter;
Expand Down Expand Up @@ -90,26 +91,21 @@ pub async fn load_workspace(
);
emmyrc.pre_process_emmyrc(&config_root);

let mut workspace_folders = cmd_workspace_folders
let workspace_folders = cmd_workspace_folders
.iter()
.map(|path| WorkspaceFolder::new(path.clone(), false))
.collect::<Vec<WorkspaceFolder>>();
let mut analysis = EmmyLuaAnalysis::new();
analysis.update_config(emmyrc.clone().into());
analysis.init_std_lib(None);

for lib in &emmyrc.workspace.library {
let path = PathBuf::from(lib.get_path().clone());
analysis.add_library_workspace(path.clone());
workspace_folders.push(WorkspaceFolder::new(path.clone(), true));
}

for path in &workspace_folders {
analysis.add_main_workspace(path.root.clone());
}

for root in &emmyrc.workspace.workspace_roots {
analysis.add_main_workspace(PathBuf::from(root));
let workspace_folders = build_workspace_folders(&workspace_folders, &emmyrc);
for workspace in &workspace_folders {
if workspace.is_library {
analysis.add_library_workspace(workspace);
} else {
analysis.add_main_workspace(workspace.root.clone());
}
}

let file_infos = collect_workspace_files(&workspace_folders, &analysis.emmyrc, None, ignore);
Expand Down
13 changes: 9 additions & 4 deletions crates/emmylua_code_analysis/locales/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,10 @@ Cannot use `...` outside a vararg function.:
en: "type recursion"
zh_CN: "类型递归"
zh_HK: "類型遞歸"
"Module '%{module}' is not visible. It has @export restrictions.":
en: "Module '%{module}' is not visible. It has @export restrictions."
zh_CN: "模块 '%{module}' 不可见。它有 @export 限制。"
zh_HK: "模組 '%{module}' 不可見。它有 @export 限制。"
"module '%{module}' visibility is not `public`":
en: "module '%{module}' visibility is not `public`"
zh_CN: "模块 '%{module}' 可见性不为 `public`"
zh_HK: "模組 '%{module}' 可見性不為 `public`"
"Unknown doc tag: `%{name}`":
en: "Unknown doc tag: `%{name}`"
# TODO: translate
Expand All @@ -273,3 +273,8 @@ Cannot use `...` outside a vararg function.:
en: "Value '%{value}' does not match any enum value. Expected one of: %{enum_values}"
zh_CN: "值 '%{value}' 与任何枚举值都不匹配。应为以下之一: %{enum_values}"
zh_HK: "值 '%{value}' 與任何枚舉值都不匹配。應為以下之一: %{enum_values}"

"Type '%{name}' has inconsistent access modifiers: %{modifiers}.":
en: "Type '%{name}' has inconsistent access modifiers: %{modifiers}."
zh_CN: "类型 '%{name}' 具有不一致的访问修饰符: %{modifiers}."
zh_HK: "類型 '%{name}' 具有不一致的訪問修飾符: %{modifiers}."
101 changes: 50 additions & 51 deletions crates/emmylua_code_analysis/resources/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@
"arrayIndex": true,
"docBaseConstMatchBaseType": true,
"metaOverrideFileDefine": true,
"requireExportGlobal": false,
"requirePath": false,
"typeCall": false
}
Expand All @@ -152,7 +151,7 @@
"ignoreGlobs": [],
"library": [],
"moduleMap": [],
"packageDirs": [],
"packages": [],
"preloadFileSize": 0,
"reindexDuration": 5000,
"workspaceRoots": []
Expand Down Expand Up @@ -442,6 +441,11 @@
"description": "Call to a non-callable value",
"type": "string",
"const": "call-non-callable"
},
{
"description": "inconsistent-type-access-modifier",
"type": "string",
"const": "inconsistent-type-access-modifier"
}
]
},
Expand Down Expand Up @@ -478,46 +482,6 @@
"rst"
]
},
"EmmyLibraryConfig": {
"type": "object",
"properties": {
"ignoreDir": {
"description": "Ignore directories within this library",
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"ignoreGlobs": {
"description": "Ignore globs within this library. eg: [\"**/*.lua\"]",
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"path": {
"description": "Library path",
"type": "string"
}
},
"required": [
"path"
]
},
"EmmyLibraryItem": {
"anyOf": [
{
"description": "Simple library path string",
"type": "string"
},
{
"description": "Library configuration with path and ignore rules",
"$ref": "#/$defs/EmmyLibraryConfig"
}
]
},
"EmmyrcCodeAction": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -1100,11 +1064,6 @@
"type": "boolean",
"default": true
},
"requireExportGlobal": {
"description": "This option limits the visibility of third-party libraries.\n\nWhen enabled, third-party libraries must use `---@export global` annotation to be importable (i.e., no diagnostic errors and visible in auto-import).",
"type": "boolean",
"default": false
},
"requirePath": {
"description": "Whether to enable strict mode require path.",
"type": "boolean",
Expand Down Expand Up @@ -1151,7 +1110,7 @@
"type": "array",
"default": [],
"items": {
"$ref": "#/$defs/EmmyLibraryItem"
"$ref": "#/$defs/EmmyrcWorkspacePathItem"
}
},
"moduleMap": {
Expand All @@ -1162,12 +1121,12 @@
"$ref": "#/$defs/EmmyrcWorkspaceModuleMap"
}
},
"packageDirs": {
"description": "Package directories. Treat the parent directory as a `library`, but only add files from the specified directory.\neg: `/usr/local/share/lua/5.1/module`",
"packages": {
"description": "Package directories. Can be a string path or an object with path and ignore rules.\nTreat the parent directory as a `library`, but only add files from the specified directory.\neg: [\"/usr/local/share/lua/5.1/module\"] or [{\"path\": \"/usr/local/share/lua/5.1/module\", \"ignoreDir\": [\"test\"], \"ignoreGlobs\": [\"**/*.spec.lua\"]}]",
"type": "array",
"default": [],
"items": {
"type": "string"
"$ref": "#/$defs/EmmyrcWorkspacePathItem"
}
},
"preloadFileSize": {
Expand Down Expand Up @@ -1207,6 +1166,46 @@
"pattern",
"replace"
]
},
"EmmyrcWorkspacePathConfig": {
"type": "object",
"properties": {
"ignoreDir": {
"description": "Ignore directories within this entry",
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"ignoreGlobs": {
"description": "Ignore globs within this entry. eg: [\"**/*.lua\"]",
"type": "array",
"default": [],
"items": {
"type": "string"
}
},
"path": {
"description": "Workspace entry path",
"type": "string"
}
},
"required": [
"path"
]
},
"EmmyrcWorkspacePathItem": {
"anyOf": [
{
"description": "Simple workspace entry path string",
"type": "string"
},
{
"description": "Workspace entry configuration with path and ignore rules",
"$ref": "#/$defs/EmmyrcWorkspacePathConfig"
}
]
}
}
}
6 changes: 4 additions & 2 deletions crates/emmylua_code_analysis/resources/std/builtin.lua
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,10 @@
--- - `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?)
--- - `return_mode`: Constructor return strategy. `"self"` forces `self`, `"doc"` uses the documented return type,
--- and `"default"` prefers the documented return type and falls back to `self`.
--- Defaults to `"default"`
---@attribute constructor(name: string, root_class: string?, strip_self: boolean?, return_mode: "self"|"doc"|"default"?)

---
--- Associates `getter` and `setter` methods with a field. Currently provides only definition navigation functionality,
Expand Down
26 changes: 26 additions & 0 deletions crates/emmylua_code_analysis/resources/std/string.lua
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,32 @@ function string.format(fmt, ...) end
---@return fun():string?...
function string.gmatch(s, pattern) end

---
--- Returns an iterator function that, each time it is called, returns the
--- next captures from `pattern` over the string `s`. If `pattern` specifies no
--- captures, then the whole match is produced in each call. A third,
--- optional numeric argument init specifies where to start the search;
--- its default value is 1 and can be negative.
---
--- As an example, the following loop will iterate over all the words from
--- string `s`, printing one per line:
---
--- `s = "hello world from Lua"`
--- `for w in string.gmatch(s, "%a+") do`
--- > `print(w)`
--- `end`
---
--- The next example collects all pairs `key=value` from the given string into a
--- table:
---
--- `t = {}`
--- s = "from=world, to=Lua"`
--- `for k, v in string.gmatch(s, "(%w+)=(%w+)") do`
--- > `t[k] = v`
--- `end`
---
--- For this function, a caret '`^`' at the start of a pattern does not work as
--- an anchor, as this would prevent the iteration.
---@version > 5.4
---@param s string
---@param pattern string
Expand Down
24 changes: 19 additions & 5 deletions crates/emmylua_code_analysis/src/compilation/analyzer/decl/docs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ use flagset::FlagSet;
use rowan::TextRange;

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

use super::DeclAnalyzer;
Expand All @@ -30,7 +30,7 @@ fn get_type_flag_value(
let mut attr: FlagSet<LuaTypeFlag> = if analyzer.is_meta {
LuaTypeFlag::Meta.into()
} else {
LuaTypeFlag::None.into()
FlagSet::default()
};

if let Some(flag) = flag {
Expand All @@ -48,6 +48,12 @@ fn get_type_flag_value(
"constructor" => {
attr |= LuaTypeFlag::Constructor;
}
"public" => {
attr |= LuaTypeFlag::Public;
}
"internal" => {
attr |= LuaTypeFlag::Internal;
}
"private" => {
attr |= LuaTypeFlag::Private;
}
Expand Down Expand Up @@ -91,7 +97,7 @@ pub fn analyze_doc_tag_attribute(
&name,
range,
LuaDeclTypeKind::Attribute,
LuaTypeFlag::None.into(),
FlagSet::default(),
);
Some(())
}
Expand Down Expand Up @@ -136,7 +142,7 @@ pub fn analyze_doc_tag_meta(analyzer: &mut DeclAnalyzer, tag: LuaDocTagMeta) ->
analyzer
.db
.get_module_index_mut()
.set_module_visibility(file_id, false);
.set_module_visibility(file_id, ModuleVisibility::Hide);
} else {
let workspace_id = analyzer
.db
Expand Down Expand Up @@ -183,6 +189,12 @@ fn add_type_decl(
flag: FlagSet<LuaTypeFlag>,
) {
let file_id = analyzer.get_file_id();
let workspace_id = analyzer
.db
.get_module_index()
.get_workspace_id(file_id)
.or(analyzer.context.workspace_id)
.unwrap_or(WorkspaceId::MAIN);
let type_index = analyzer.db.get_type_index_mut();

let basic_name = name;
Expand All @@ -192,6 +204,8 @@ fn add_type_decl(
.unwrap_or(basic_name.to_string());
let id = if flag.contains(LuaTypeFlag::Private) {
LuaTypeDeclId::local(file_id, &full_name)
} else if flag.contains(LuaTypeFlag::Internal) {
LuaTypeDeclId::internal(workspace_id, &full_name)
} else {
LuaTypeDeclId::global(&full_name)
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,11 @@ fn infer_buildin_or_ref_type(
}

let mut founded = false;
let type_id = if let Some(name_type_decl) = analyzer
.db
.get_type_index_mut()
.find_type_decl(analyzer.file_id, name)
{
let type_id = if let Some(name_type_decl) = analyzer.db.get_type_index().find_type_decl(
analyzer.file_id,
name,
Some(analyzer.workspace_id),
) {
founded = true;
name_type_decl.get_id()
} else {
Expand Down Expand Up @@ -233,11 +233,11 @@ fn infer_generic_type(analyzer: &mut DocAnalyzer, generic_type: &LuaDocGenericTy
return typ;
}

let id = if let Some(name_type_decl) = analyzer
.db
.get_type_index_mut()
.find_type_decl(analyzer.file_id, &name)
{
let id = if let Some(name_type_decl) = analyzer.db.get_type_index().find_type_decl(
analyzer.file_id,
&name,
Some(analyzer.workspace_id),
) {
name_type_decl.get_id()
} else {
analyzer.db.get_diagnostic_index_mut().add_diagnostic(
Expand Down
Loading
Loading