summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexCai2019 <89138532+AlexCai2019@users.noreply.github.com>2022-05-08 01:43:28 +0800
committerGitHub <noreply@github.com>2022-05-08 01:43:28 +0800
commit0fd83c4ca9f82a02becab6c304a8a7de75098507 (patch)
treebe9790d9d4823fe728c5b36e94093fe5f42b7725
parent89203efad8c9b5513e05ca4d5696107924865b10 (diff)
parent67b4c574849d1667e0ecb39c51aeed8e30b43056 (diff)
downloadlua-language-server-0fd83c4ca9f82a02becab6c304a8a7de75098507.zip
Merge branch 'sumneko:master' into master
-rw-r--r--.luarc.json7
m---------3rd/EmmyLuaCodeStyle0
m---------3rd/bee.lua0
m---------3rd/lovr-api0
m---------3rd/luamake0
-rw-r--r--changelog.md63
-rw-r--r--debugger.lua2
-rw-r--r--locale/en-us/meta.lua4
-rw-r--r--locale/en-us/script.lua8
-rw-r--r--locale/pt-br/script.lua6
-rw-r--r--locale/zh-cn/script.lua12
-rw-r--r--locale/zh-tw/meta.lua1132
-rw-r--r--locale/zh-tw/script.lua770
-rw-r--r--locale/zh-tw/setting.lua344
-rw-r--r--make.lua76
-rw-r--r--make/bootstrap.lua3
-rw-r--r--make/copy_vcrt.lua6
-rw-r--r--make/detect_platform.lua64
-rw-r--r--meta/3rd/OpenResty/library/ngx.lua14
-rw-r--r--meta/template/basic.lua22
-rw-r--r--meta/template/debug.lua10
-rw-r--r--meta/template/io.lua4
-rw-r--r--meta/template/os.lua6
-rw-r--r--meta/template/string.lua2
-rw-r--r--meta/template/table.lua5
-rw-r--r--meta/template/utf8.lua4
-rw-r--r--script/client.lua3
-rw-r--r--script/config/loader.lua6
-rw-r--r--script/core/completion/completion.lua88
-rw-r--r--script/core/definition.lua4
-rw-r--r--script/core/diagnostics/close-non-object.lua13
-rw-r--r--script/core/diagnostics/duplicate-doc-field.lua1
-rw-r--r--script/core/diagnostics/global-in-nil-env.lua2
-rw-r--r--script/core/diagnostics/init.lua2
-rw-r--r--script/core/diagnostics/lowercase-global.lua25
-rw-r--r--script/core/diagnostics/missing-parameter.lua73
-rw-r--r--script/core/diagnostics/need-check-nil.lua39
-rw-r--r--script/core/diagnostics/no-unknown.lua4
-rw-r--r--script/core/diagnostics/not-yieldable.lua5
-rw-r--r--script/core/diagnostics/redundant-parameter.lua48
-rw-r--r--script/core/diagnostics/undefined-field.lua3
-rw-r--r--script/core/diagnostics/undefined-global.lua3
-rw-r--r--script/core/diagnostics/unused-function.lua152
-rw-r--r--script/core/diagnostics/unused-vararg.lua5
-rw-r--r--script/core/formatting.lua2
-rw-r--r--script/core/hint.lua3
-rw-r--r--script/core/hover/args.lua33
-rw-r--r--script/core/hover/description.lua5
-rw-r--r--script/core/hover/init.lua24
-rw-r--r--script/core/hover/label.lua9
-rw-r--r--script/core/hover/name.lua4
-rw-r--r--script/core/hover/return.lua12
-rw-r--r--script/core/hover/table.lua116
-rw-r--r--script/core/look-backward.lua7
-rw-r--r--script/core/matchkey.lua2
-rw-r--r--script/core/rangeformatting.lua2
-rw-r--r--script/core/rename.lua5
-rw-r--r--script/core/semantic-tokens.lua105
-rw-r--r--script/core/signature.lua3
-rw-r--r--script/core/type-definition.lua1
-rw-r--r--script/doctor.lua3
-rw-r--r--script/encoder/init.lua12
-rw-r--r--script/files.lua4
-rw-r--r--script/fs-utility.lua13
-rw-r--r--script/glob/gitignore.lua2
-rw-r--r--script/jsonc.lua603
-rw-r--r--script/language.lua4
-rw-r--r--script/log.lua5
-rw-r--r--script/parser/guide.lua52
-rw-r--r--script/parser/luadoc.lua1023
-rw-r--r--script/parser/newparser.lua50
-rw-r--r--script/proto/define.lua18
-rw-r--r--script/provider/diagnostic.lua18
-rw-r--r--script/provider/provider.lua23
-rw-r--r--script/pub/pub.lua2
-rw-r--r--script/service/telemetry.lua2
-rw-r--r--script/utility.lua12
-rw-r--r--script/vm/compiler.lua459
-rw-r--r--script/vm/def.lua15
-rw-r--r--script/vm/doc.lua11
-rw-r--r--script/vm/field.lua10
-rw-r--r--script/vm/generic.lua5
-rw-r--r--script/vm/global-manager.lua364
-rw-r--r--script/vm/global.lua431
-rw-r--r--script/vm/infer.lua115
-rw-r--r--script/vm/init.lua10
-rw-r--r--script/vm/library.lua21
-rw-r--r--script/vm/local-id.lua62
-rw-r--r--script/vm/local-manager.lua40
-rw-r--r--script/vm/manager.lua26
-rw-r--r--script/vm/node.lua260
-rw-r--r--script/vm/ref.lua6
-rw-r--r--script/vm/runner.lua444
-rw-r--r--script/vm/sign.lua29
-rw-r--r--script/vm/type.lua11
-rw-r--r--script/vm/value.lua30
-rw-r--r--script/vm/vm.lua1
-rw-r--r--script/workspace/loading.lua2
-rw-r--r--script/workspace/workspace.lua9
-rw-r--r--test.lua2
-rw-r--r--test/command/auto-require.lua5
-rw-r--r--test/completion/common.lua37
-rw-r--r--test/definition/luadoc.lua6
-rw-r--r--test/definition/method.lua26
-rw-r--r--test/diagnostics/common.lua185
-rw-r--r--test/full/init.lua13
-rw-r--r--test/full/self.lua39
-rw-r--r--test/hover/init.lua55
-rw-r--r--test/tclient/init.lua1
-rw-r--r--test/tclient/tests/resolve-completion.lua58
-rw-r--r--test/type_inference/init.lua837
111 files changed, 5861 insertions, 2983 deletions
diff --git a/.luarc.json b/.luarc.json
index 993be427..32fa50a7 100644
--- a/.luarc.json
+++ b/.luarc.json
@@ -7,7 +7,12 @@
"undefined-field": "Any",
"await-in-sync": "Any",
"not-yieldable": "Any",
- "discard-returns": "Any"
+ "discard-returns": "Any",
+ "redundant-parameter": "Any",
+ "missing-parameter": "Any",
+ "need-check-nil": "Any",
+ "redundant-value": "Any",
+ "deprecated": "Any"
},
"ignoredFiles": "Opened",
"libraryFiles": "Opened"
diff --git a/3rd/EmmyLuaCodeStyle b/3rd/EmmyLuaCodeStyle
-Subproject 8246ca187f166f9fa88c08076b4c1e16817de11
+Subproject 8a7079dc9bfb50f62deda7c61a988767ea45d5b
diff --git a/3rd/bee.lua b/3rd/bee.lua
-Subproject c117563a62be1b5704d6d76320cfc5e23df782a
+Subproject e5d2778ecf9181058f1d107a06487850a6d59ce
diff --git a/3rd/lovr-api b/3rd/lovr-api
-Subproject d0d8e4e6e29b24edcc0ac7c3b7406225a9ec925
+Subproject f20546911d416d825294d39fce9446dd9522e0f
diff --git a/3rd/luamake b/3rd/luamake
-Subproject 296c430808afd1a9e10162e89f94a3a14356af0
+Subproject 096245513b530f57b0b5cfd3c9d21fd0f06ecd6
diff --git a/changelog.md b/changelog.md
index 772be9e4..5c488788 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,10 +1,73 @@
# changelog
+## 3.2.3
+* `CHG` parse `.luarc.json` as jsonc. In order to please the editor, it also supports `.luarc.jsonc` as the file name.
+* `CHG` dose not load files in symbol links
+* `FIX` diagnostic: send empty results to every file after startup
+* `FIX` [#1103](https://github.com/sumneko/lua-language-server/issues/1103)
+* `FIX` [#1107](https://github.com/sumneko/lua-language-server/issues/1107)
+
+## 3.2.2
+`2022-4-26`
+* `FIX` diagnostic: `unused-function` cannot handle recursion correctly
+* `FIX` [#1092](https://github.com/sumneko/lua-language-server/issues/1092)
+* `FIX` [#1093](https://github.com/sumneko/lua-language-server/issues/1093)
+* `FIX` runtime errors reported by telemetry, see [#1091](https://github.com/sumneko/lua-language-server/issues/1091)
+
+## 3.2.1
+`2022-4-25`
+* `FIX` broken in VSCode
+
+## 3.2.0
+`2022-4-25`
+* `NEW` supports infer of callback parameter
+ ```lua
+ ---@type string[]
+ local t
+
+ table.sort(t, function (a, b)
+ -- `a` and `b` is `string` here
+ end)
+ ```
+* `NEW` using `---@overload` as class constructor
+ ```lua
+ ---@class Class
+ ---@overload fun():Class
+ local mt
+
+ local x = mt() --> x is `Class` here
+ ```
+* `NEW` add `--[[@as type]]`
+ ```lua
+ local x = true
+ local y = x--[[@as integer]] -- y is `integer` here
+ ```
+* `NEW` add `---@cast`
+ * `---@cast localname type`
+ * `---@cast localname +type`
+ * `---@cast localname -type`
+ * `---@cast localname +?`
+ * `---@cast localname -?`
+* `NEW` generic: resolve `T[]` by `table<integer, type>` or `---@field [integer] type`
+* `NEW` resolve `class[1]` by `---@field [integer] type`
+* `NEW` diagnostic: `missing-parameter`
+* `NEW` diagnostic: `need-check-nil`
+* `CHG` diagnostic: no longer mark `redundant-parameter` as `Unnecessary`
+* `FIX` diagnostic: `unused-function` does not recognize recursion
+* `FIX` [#1051](https://github.com/sumneko/lua-language-server/issues/1051)
+* `FIX` [#1072](https://github.com/sumneko/lua-language-server/issues/1072)
+* `FIX` [#1077](https://github.com/sumneko/lua-language-server/issues/1077)
+* `FIX` [#1088](https://github.com/sumneko/lua-language-server/issues/1088)
+* `FIX` runtime errors
+
## 3.1.0
+`2022-4-17`
+* `NEW` support find definition in method
* `CHG` hint: move to LSP. Its font is now controlled by the client.
* `CHG` hover: split `local` into `local` / `parameter` / `upvalue` / `self`.
* `CHG` hover: added parentheses to some words, such as `global` / `field` / `class`.
* `FIX` definition of `table<k, v>`
+* `FIX` [#994](https://github.com/sumneko/lua-language-server/issues/994)
* `FIX` [#1057](https://github.com/sumneko/lua-language-server/issues/1057)
* `FIX` runtime errors reported by telemetry, see [#1058](https://github.com/sumneko/lua-language-server/issues/1058)
diff --git a/debugger.lua b/debugger.lua
index 5d2c540f..e72ce3de 100644
--- a/debugger.lua
+++ b/debugger.lua
@@ -27,7 +27,7 @@ if #luaDebugs == 0 then
end
local function getVer(filename)
- local a, b, c = filename:match('(%d+)%.(%d+)%.(%d+)$')
+ local a, b, c = filename:match('actboy168%.lua%-debug%-(%d+)%.(%d+)%.(%d+)')
if not a then
return 0
end
diff --git a/locale/en-us/meta.lua b/locale/en-us/meta.lua
index 09612d76..8e4a2fb1 100644
--- a/locale/en-us/meta.lua
+++ b/locale/en-us/meta.lua
@@ -135,7 +135,7 @@ select =
'If `index` is a number, returns all arguments after argument number `index`; a negative number indexes from the end (`-1` is the last argument). Otherwise, `index` must be the string `"#"`, and `select` returns the total number of extra arguments it received.'
setfenv =
-'Sets the environment to be used by the given function. '
+'Sets the environment to be used by the given function.'
setmetatable =
[[
@@ -653,7 +653,7 @@ string.format =
'Returns a formatted version of its variable number of arguments following the description given in its first argument.'
string.gmatch =
[[
-Returns an iterator function that, each time it is called, returns the next captures from `pattern` (see §6.4.1) over the string s.
+Returns an iterator function that, each time it is called, returns the next captures from `pattern` (see §6.4.1) over the string s.
As an example, the following loop will iterate over all the words from string s, printing one per line:
```lua
diff --git a/locale/en-us/script.lua b/locale/en-us/script.lua
index a166cf47..1cba9dd5 100644
--- a/locale/en-us/script.lua
+++ b/locale/en-us/script.lua
@@ -33,7 +33,9 @@ DIAG_PREVIOUS_CALL =
DIAG_PREFIELD_CALL =
'Will be interpreted as `{}{}`. It may be necessary to add a `,` or `;`.'
DIAG_OVER_MAX_ARGS =
-'The function takes only {:d} parameters, but you passed {:d}.'
+'The function received a maximum of {:d} arguments, but got {:d}.'
+DIAG_MISS_ARGS =
+'the function received at least {:d} arguments, but got {:d}.'
DIAG_OVER_MAX_VALUES =
'Only has {} variables, but you set {} values.'
DIAG_AMBIGUITY_1 =
@@ -94,6 +96,8 @@ DIAG_NOT_YIELDABLE =
'The {}th parameter of this function was not marked as yieldable, but an async function was passed in. (Use `---@param name async fun()` to mark as yieldable)'
DIAG_DISCARD_RETURNS =
'The return values of this function cannot be discarded.'
+DIAG_NEED_CHECK_NIL =
+'Need check nil.'
DIAG_CIRCLE_DOC_CLASS =
'Circularly inherited classes.'
DIAG_DOC_FIELD_NO_CLASS =
@@ -271,6 +275,8 @@ PARSER_LUADOC_MISS_DIAG_MODE =
'<diagnostic mode> expected.'
PARSER_LUADOC_ERROR_DIAG_MODE =
'<diagnostic mode> incorrect.'
+PARSER_LUADOC_MISS_LOCAL_NAME =
+'<local name> expected.'
SYMBOL_ANONYMOUS =
'<Anonymous>'
diff --git a/locale/pt-br/script.lua b/locale/pt-br/script.lua
index 4076c223..21d8ea55 100644
--- a/locale/pt-br/script.lua
+++ b/locale/pt-br/script.lua
@@ -34,6 +34,8 @@ DIAG_PREFIELD_CALL =
'Será interpretado como `{}{}`. Pode ser necessário adicionar uma `,` ou `;`.'
DIAG_OVER_MAX_ARGS =
'A função aceita apenas os parâmetros {:d}, mas você passou {:d}.'
+DIAG_MISS_ARGS = -- TODO: need translate!
+'the function received at least {:d} arguments, but got {:d}.'
DIAG_OVER_MAX_VALUES = -- TODO: need translate!
'Only has {} variables, but you set {} values.'
DIAG_AMBIGUITY_1 =
@@ -94,6 +96,8 @@ DIAG_NOT_YIELDABLE = -- TODO: need translate!
'The {}th parameter of this function was not marked as yieldable, but an async function was passed in. (Use `---@param name async fun()` to mark as yieldable)'
DIAG_DISCARD_RETURNS = -- TODO: need translate!
'The return values of this function cannot be discarded.'
+DIAG_NEED_CHECK_NIL = -- TODO: need translate!
+'Need check nil.'
DIAG_CIRCLE_DOC_CLASS =
'Classes com herança cíclica.'
DIAG_DOC_FIELD_NO_CLASS =
@@ -271,6 +275,8 @@ PARSER_LUADOC_MISS_DIAG_MODE =
'Esperado <diagnostic mode>.'
PARSER_LUADOC_ERROR_DIAG_MODE =
'<diagnostic mode> incorreto.'
+PARSER_LUADOC_MISS_LOCAL_NAME = -- TODO: need translate!
+'<local name> expected.'
SYMBOL_ANONYMOUS = -- TODO: need translate!
'<Anonymous>'
diff --git a/locale/zh-cn/script.lua b/locale/zh-cn/script.lua
index 9c5b5ec7..10a703c7 100644
--- a/locale/zh-cn/script.lua
+++ b/locale/zh-cn/script.lua
@@ -33,7 +33,9 @@ DIAG_PREVIOUS_CALL =
DIAG_PREFIELD_CALL =
'会被解释为 `{}{}`。你可能需要加一个`,`或`;`。'
DIAG_OVER_MAX_ARGS =
-'函数只接收 {:d} 个参数,但你传了 {:d} 个。'
+'函数最多接收 {:d} 个参数,但获得了 {:d} 个。'
+DIAG_MISS_ARGS =
+'函数最少接收 {:d} 个参数,但获得了 {:d} 个。'
DIAG_OVER_MAX_VALUES =
'只有 {} 个变量,但你设置了 {} 个值。'
DIAG_AMBIGUITY_1 =
@@ -94,6 +96,8 @@ DIAG_NOT_YIELDABLE =
'此函数的第 {} 个参数没有被标记为可让出,但是传入了异步函数。(使用 `---@param name async fun()` 来标记为可让出)'
DIAG_DISCARD_RETURNS =
'不能丢弃此函数的返回值。'
+DIAG_NEED_CHECK_NIL =
+'需要判空。'
DIAG_CIRCLE_DOC_CLASS =
'循环继承的类。'
DIAG_DOC_FIELD_NO_CLASS =
@@ -268,9 +272,11 @@ PARSER_LUADOC_MISS_FUN_AFTER_OVERLOAD =
PARSER_LUADOC_MISS_CATE_NAME =
'缺少文档类型名称。'
PARSER_LUADOC_MISS_DIAG_MODE =
-'缺少诊断模式'
+'缺少诊断模式。'
PARSER_LUADOC_ERROR_DIAG_MODE =
-'诊断模式不正确'
+'诊断模式不正确。'
+PARSER_LUADOC_MISS_LOCAL_NAME =
+'缺少变量名。'
SYMBOL_ANONYMOUS =
'<匿名函数>'
diff --git a/locale/zh-tw/meta.lua b/locale/zh-tw/meta.lua
index b7cc18a8..bf38912b 100644
--- a/locale/zh-tw/meta.lua
+++ b/locale/zh-tw/meta.lua
@@ -1,222 +1,222 @@
---@diagnostic disable: undefined-global, lowercase-global
-arg =
-'獨立版Lua的啟動參數。 '
-
-assert =
-'如果其參數`v` 的值為假(`nil` 或`false`), 它就呼叫$error; 否則,回傳所有的參數。在錯誤情況時, `message` 指那個錯誤對象; 如果不提供這個參數,參數預設為`"assertion failed!"` 。 '
-
-cgopt.collect =
-'做一次完整的垃圾回收循環。 '
-cgopt.stop =
-'停止垃圾回收器的執行。 '
-cgopt.restart =
-'重啟垃圾回收器的自動執行。 '
-cgopt.count =
-'以K 位元組數為單位回傳Lua 使用的總記憶體數。 '
-cgopt.step =
-'單步執行垃圾回收器。步長“大小”由`arg` 控制。 '
-cgopt.setpause =
-'將`arg` 設為回收器的*間歇率* 。 '
-cgopt.setstepmul =
-'將`arg` 設為回收器的*步進倍率* 。 '
-cgopt.incremental =
-'改變回收器模式為增量模式。 '
-cgopt.generational =
-'改變回收器模式為分代模式。 '
-cgopt.isrunning =
-'回傳表示回收器是否在工作的布林值。 '
-
-collectgarbage =
-'這個函式是垃圾回收器的通用介面。通過參數opt 它提供了一組不同的功能。 '
-
-dofile =
-'打開該名字的檔案,並執行檔案中的Lua 程式碼區塊。不帶參數呼叫時, `dofile` 執行標準輸入的內容(`stdin`)。回傳該程式碼區塊的所有回傳值。對於有錯誤的情況,`dofile` 將錯誤反饋給呼叫者(即,`dofile` 沒有執行在保護模式下)。 '
-
-error =
+arg =
+'獨立版Lua的啟動參數。'
+
+assert =
+'如果其參數 `v` 的值為假(`nil` 或 `false`), 它就呼叫 $error; 否則,回傳所有的參數。 在錯誤情況時, `message` 指那個錯誤對象; 如果不提供這個參數,參數預設為 `"assertion failed!"` 。'
+
+cgopt.collect =
+'做一次完整的垃圾回收循環。'
+cgopt.stop =
+'停止垃圾回收器的執行。'
+cgopt.restart =
+'重啟垃圾回收器的自動執行。'
+cgopt.count =
+'以 K 位元組數為單位回傳 Lua 使用的總記憶體數。'
+cgopt.step =
+'單步執行垃圾回收器。 步長“大小”由 `arg` 控制。'
+cgopt.setpause =
+'將 `arg` 設為回收器的 *間歇率* 。'
+cgopt.setstepmul =
+'將 `arg` 設為回收器的 *步進倍率* 。'
+cgopt.incremental =
+'改變回收器模式為增量模式。'
+cgopt.generational =
+'改變回收器模式為分代模式。'
+cgopt.isrunning =
+'回傳表示回收器是否在工作的布林值。'
+
+collectgarbage =
+'這個函式是垃圾回收器的通用接口。 通過參數 opt 它提供了一組不同的功能。'
+
+dofile =
+'打開該名字的檔案,並執行檔案中的 Lua 程式碼區塊。 不帶參數呼叫時, `dofile` 執行標準輸入的內容(`stdin`)。 回傳該程式碼區塊的所有回傳值。 對於有錯誤的情況,`dofile` 將錯誤反饋給呼叫者 (即,`dofile` 沒有執行在保護模式下)。'
+
+error =
[[
-中止上一次保護函式呼叫, 將錯誤對象`message` 回傳。函式`error` 永遠不會回傳。
+中止上一次保護函式呼叫, 將錯誤對象 `message` 回傳。 函式 `error` 永遠不會回傳。
-當`message` 是一個字串時,通常`error` 會把一些有關出錯位置的訊息附加在訊息的前頭。 level 參數指明了怎樣獲得出錯位置。
+當 `message` 是一個字串時,通常 `error` 會把一些有關出錯位置的訊息附加在訊息的前頭。 level 參數指明了怎樣獲得出錯位置。
]]
-_G =
-'一個全域變數(非函式), 內部儲存有全域環境(參見§2.2)。 Lua 自己不使用這個變數; 改變這個變數的值不會對任何環境造成影響,反之亦然。 '
+_G =
+'一個全域變數(非函式), 內部儲存有全域環境(參見 §2.2)。 Lua 自己不使用這個變數; 改變這個變數的值不會對任何環境造成影響,反之亦然。'
-getfenv =
-'回傳給定函式的環境。 `f` 可以是一個Lua函式,也可是一個表示呼叫堆疊層級的數字。 '
+getfenv =
+'回傳給定函式的環境。`f` 可以是一個Lua函式,也可是一個表示呼叫堆疊層級的數字。'
-getmetatable =
-'如果`object` 不包含元表,回傳`nil` 。否則,如果在該對象的元表中有`"__metatable"` 域時回傳其關聯值, 沒有時回傳該對象的元表。 '
+getmetatable =
+'如果 `object` 不包含元表,回傳 `nil` 。 否則,如果在該對象的元表中有 `"__metatable"` 域時回傳其關聯值, 沒有時回傳該對象的元表。'
-ipairs =
+ipairs =
[[
-回傳三個值(疊代函式、表`t` 以及`0` ), 如此,以下程式碼
+回傳三個值(疊代函式、表 `t` 以及 `0` ), 如此,以下程式碼
```lua
for i,v in ipairs(t) do body end
```
-將疊代鍵值對`(1,t[1]) ,(2,t[2]), ...` ,直到第一個空值。
+將疊代鍵值對 `(1,t[1]) ,(2,t[2]), ...` ,直到第一個空值。
]]
-loadmode.b =
-'只能是二進制程式碼區塊。 '
-loadmode.t =
-'只能是文字程式碼區塊。 '
-loadmode.bt =
-'可以是二進制也可以是文字。 '
+loadmode.b =
+'只能是二進制程式碼區塊。'
+loadmode.t =
+'只能是文字程式碼區塊。'
+loadmode.bt =
+'可以是二進制也可以是文字。'
-load['<5.1'] =
-'使用`func` 分段載入程式碼區塊。每次呼叫`func` 必須回傳一個字串用於連接前文。 '
-load['>5.2'] =
+load['<5.1'] =
+'使用 `func` 分段載入程式碼區塊。 每次呼叫 `func` 必須回傳一個字串用於連接前文。'
+load['>5.2'] =
[[
載入一個程式碼區塊。
-如果`chunk` 是一個字串,程式碼區塊指這個字串。如果`chunk` 是一個函式, `load` 不斷地呼叫它獲取程式碼區塊的片段。每次對`chunk` 的呼叫都必須回傳一個字串緊緊連接在上次呼叫的回傳串之後。當回傳空串、`nil`、或是不回傳值時,都表示程式碼區塊結束。
+如果 `chunk` 是一個字串,程式碼區塊指這個字串。 如果 `chunk` 是一個函式, `load` 不斷地呼叫它獲取程式碼區塊的片段。 每次對 `chunk` 的呼叫都必須回傳一個字串緊緊連接在上次呼叫的回傳串之後。 當回傳空串、`nil`、或是不回傳值時,都表示程式碼區塊結束。
]]
-loadfile =
-'從檔案`filename` 或標準輸入(如果檔名未提供)中獲取程式碼區塊。 '
+loadfile =
+'從檔案 `filename` 或標準輸入(如果檔名未提供)中獲取程式碼區塊。'
-loadstring =
-'使用給定字串載入程式碼區塊。 '
+loadstring =
+'使用給定字串載入程式碼區塊。'
-module =
-'創建一個模組。 '
+module =
+'新增一個模組。'
-next =
+next =
[[
-執行程式來走訪表中的所有域。第一個參數是要走訪的表,第二個參數是表中的某個鍵。 `next` 回傳該鍵的下一個鍵及其關聯的值。如果用`nil` 作為第二個參數呼叫`next` 將回傳初始鍵及其關聯值。當以最後一個鍵去呼叫,或是以`nil` 呼叫一張空表時, `next` 回傳`nil`。如果不提供第二個參數,將認為它就是`nil`。特別指出,你可以用`next(t)` 來判斷一張表是否是空的。
+執行程式來走訪表中的所有域。 第一個參數是要走訪的表,第二個參數是表中的某個鍵。 `next` 回傳該鍵的下一個鍵及其關聯的值。 如果用 `nil` 作為第二個參數呼叫 `next` 將回傳初始鍵及其關聯值。 當以最後一個鍵去呼叫,或是以 `nil` 呼叫一張空表時, `next` 回傳 `nil`。 如果不提供第二個參數,將認為它就是 `nil`。 特別指出,你可以用 `next(t)` 來判斷一張表是否是空的。
-索引在走訪過程中的順序無定義, 即使是數字索引也是這樣。 (如果想按數字順序走訪表,可以使用數字形式的`for` 。)
+索引在走訪過程中的順序無定義, 即使是數字索引也是這樣。 (如果想按數字順序走訪表,可以使用數字形式的 `for` 。)
-當在走訪過程中你給表中並不存在的域賦值, `next` 的行為是未定義的。然而你可以去修改那些已存在的域。特別指出,你可以清除一些已存在的域。
+當在走訪過程中你給表中並不存在的域賦值, `next` 的行為是未定義的。 然而你可以去修改那些已存在的域。 特別指出,你可以清除一些已存在的域。
]]
-pairs =
+pairs =
[[
-如果`t` 有元方法`__pairs`, 以`t` 為參數呼叫它,並回傳其回傳的前三個值。
+如果 `t` 有元方法 `__pairs`, 以 `t` 為參數呼叫它,並回傳其回傳的前三個值。
-否則,回傳三個值:`next` 函式, 表`t`,以及`nil`。因此以下程式碼
+否則,回傳三個值:`next` 函式, 表 `t`,以及 `nil`。 因此以下程式碼
```lua
for k,v in pairs(t) do body end
```
-能疊代表`t` 中的所有鍵值對。
+能疊代表 `t` 中的所有鍵值對。
-參見函式$next 中關於疊代過程中修改表的風險。
+參見函式 $next 中關於疊代過程中修改表的風險。
]]
-pcall =
-'傳入參數,以*保護模式* 呼叫函式`f` 。這意味著`f` 中的任何錯誤不會拋出; 取而代之的是,`pcall` 會將錯誤捕獲到,並回傳一個狀態碼。第一個回傳值是狀態碼(一個布林值), 當沒有錯誤時,其為真。此時,`pcall` 同樣會在狀態碼後回傳所有呼叫的結果。在有錯誤時,`pcall` 回傳`false` 加錯誤訊息。 '
+pcall =
+'傳入參數,以 *保護模式* 呼叫函式 `f` 。 這意味著 `f` 中的任何錯誤不會拋出; 取而代之的是,`pcall` 會將錯誤捕獲到,並回傳一個狀態碼。 第一個回傳值是狀態碼(一個布林值), 當沒有錯誤時,其為真。 此時,`pcall` 同樣會在狀態碼後回傳所有呼叫的結果。 在有錯誤時,`pcall` 回傳 `false` 加錯誤訊息。'
-print =
-'接收任意數量的參數,並將它們的值輸出到`stdout`。它用`tostring` 函式將每個參數都轉換為字串。 `print` 不用於做格式化輸出。僅作為看一下某個值的快捷方式。多用於除錯。完整的對輸出的控制,請使用$string.format 以及$io.write。 '
+print =
+'接收任意數量的參數,並將它們的值輸出到 `stdout`。 它用 `tostring` 函式將每個參數都轉換為字串。 `print` 不用於做格式化輸出。僅作為看一下某個值的快捷方式。 多用於除錯。 完整的對輸出的控制,請使用 $string.format 以及 $io.write。'
-rawequal =
-'在不觸發任何元方法的情況下檢查`v1` 是否和`v2` 相等。回傳一個布林值。 '
+rawequal =
+'在不觸發任何元方法的情況下 檢查 `v1` 是否和 `v2` 相等。 回傳一個布林值。'
-rawget =
-'在不觸發任何元方法的情況下獲取`table[index]` 的值。 `table` 必須是一張表; `index` 可以是任何值。 '
+rawget =
+'在不觸發任何元方法的情況下 獲取 `table[index]` 的值。 `table` 必須是一張表; `index` 可以是任何值。'
-rawlen =
-'在不觸發任何元方法的情況下回傳對象`v` 的長度。 `v` 可以是表或字串。它回傳一個整數。 '
+rawlen =
+'在不觸發任何元方法的情況下 回傳對象 `v` 的長度。 `v` 可以是表或字串。 它回傳一個整數。'
-rawset =
+rawset =
[[
-在不觸發任何元方法的情況下將`table[index]` 設為`value。 ` `table` 必須是一張表, `index` 可以是`nil` 與`NaN` 之外的任何值。 `value` 可以是任何Lua 值。
-這個函式回傳`table`。
+在不觸發任何元方法的情況下 將 `table[index]` 設為 `value。` `table` 必須是一張表, `index` 可以是 `nil` 與 `NaN` 之外的任何值。 `value` 可以是任何 Lua 值。
+這個函式回傳 `table`。
]]
-select =
-'如果`index` 是個數字, 那麼回傳參數中第`index` 個之後的部分; 負的數字會從後向前索引(`-1` 指最後一個參數)。否則,`index` 必須是字串`"#"`, 此時`select` 回傳參數的個數。 '
+select =
+'如果 `index` 是個數字, 那麼回傳參數中第 `index` 個之後的部分; 負的數字會從後向前索引(`-1` 指最後一個參數)。 否則,`index` 必須是字串 `"#"`, 此時 `select` 回傳參數的個數。'
-setfenv =
-'設定給定函式的環境。 '
+setfenv =
+'設定給定函式的環境。'
-setmetatable =
+setmetatable =
[[
-給指定表設定元表。 (你不能在Lua 中改變其它類型值的元表,那些只能在C 裡做。) 如果`metatable` 是`nil`, 將指定表的元表移除。如果原來那張元表有`"__metatable"` 域,拋出一個錯誤。
+給指定表設定元表。 (你不能在 Lua 中改變其它類型值的元表,那些只能在 C 裡做。) 如果 `metatable` 是 `nil`, 將指定表的元表移除。 如果原來那張元表有 `"__metatable"` 域,拋出一個錯誤。
]]
-tonumber =
+tonumber =
[[
-如果呼叫的時候沒有`base`, `tonumber` 嘗試把參數轉換為一個數字。如果參數已經是一個數字,或是一個可以轉換為數字的字串, `tonumber` 就回傳這個數字; 否則回傳`nil`。
+如果呼叫的時候沒有 `base`, `tonumber` 嘗試把參數轉換為一個數字。 如果參數已經是一個數字,或是一個可以轉換為數字的字串, `tonumber` 就回傳這個數字; 否則回傳 `nil`。
-字串的轉換結果可能是整數也可能是浮點數, 這取決於Lua 的轉換文法(參見§3.1)。 (字串可以有前置和後置的空格,可以帶符號。)
+字串的轉換結果可能是整數也可能是浮點數, 這取決於 Lua 的轉換文法(參見 §3.1)。 (字串可以有前置和後置的空格,可以帶符號。)
]]
-tostring =
+tostring =
[[
-可以接收任何類型,它將其轉換為人可閱讀的字串形式。浮點數總被轉換為浮點數的表現形式(小數點形式或是指數形式)。 (如果想完全控制數字如何被轉換,可以使用$string.format。)
-如果`v` 有`"__tostring"` 域的元表, `tostring` 會以`v` 為參數呼叫它。並用它的結果作為回傳值。
+可以接收任何類型,它將其轉換為人可閲讀的字串形式。 浮點數總被轉換為浮點數的表現形式(小數點形式或是指數形式)。 (如果想完全控制數字如何被轉換,可以使用 $string.format。)
+如果 `v` 有 `"__tostring"` 域的元表, `tostring` 會以 `v` 為參數呼叫它。 並用它的結果作為回傳值。
]]
-type =
+type =
[[
-將參數的類型編碼為一個字串回傳。函式可能的回傳值有`"nil"` (一個字串,而不是`nil` 值), `"number"`, `"string"`, `"boolean"`, `"table"`, `"function"`, `"thread"`, `"userdata"`。
+將參數的類型編碼為一個字串回傳。 函式可能的回傳值有 `"nil"` (一個字串,而不是 `nil` 值), `"number"`, `"string"`, `"boolean"`, `"table"`, `"function"`, `"thread"`, `"userdata"`。
]]
-_VERSION =
-'一個包含有目前直譯器版本號的全域變數(並非函式)。 '
+_VERSION =
+'一個包含有目前直譯器版本號的全域變數(並非函式)。'
-warn =
-'使用所有參數組成的字串訊息來發送警告。 '
+warn =
+'使用所有參數組成的字串訊息來發送警告。'
-xpcall['=5.1'] =
-'傳入參數,以*保護模式* 呼叫函式`f` 。這個函式和`pcall` 類似。不過它可以額外設定一個訊息處理器`err`。 '
-xpcall['>5.2'] =
-'傳入參數,以*保護模式* 呼叫函式`f` 。這個函式和`pcall` 類似。不過它可以額外設定一個訊息處理器`msgh`。 '
+xpcall['=5.1'] =
+'傳入參數,以 *保護模式* 呼叫函式 `f` 。這個函式和 `pcall` 類似。 不過它可以額外設定一個訊息處理器 `err`。'
+xpcall['>5.2'] =
+'傳入參數,以 *保護模式* 呼叫函式 `f` 。這個函式和 `pcall` 類似。 不過它可以額外設定一個訊息處理器 `msgh`。'
-unpack =
+unpack =
[[
-回傳給定`list` 中的所有元素。改函式等價於
+回傳給定 `list` 中的所有元素。 該函式等價於
```lua
return list[i], list[i+1], ···, list[j]
```
]]
-bit32 =
+bit32 =
''
-bit32.arshift =
+bit32.arshift =
[[
-回傳`x` 向右位移`disp` 位的結果。 `disp` 為負時向左位移。這是算數位移操作,左側的空位使用`x` 的高位填充,右側空位使用`0` 填充。
+回傳 `x` 向右位移 `disp` 位的結果。`disp` 為負時向左位移。這是算數位移操作,左側的空位使用 `x` 的高位填充,右側空位使用 `0` 填充。
]]
-bit32.band =
-'回傳參數按位與的結果。 '
-bit32.bnot =
+bit32.band =
+'回傳參數按位元及的結果。'
+bit32.bnot =
[[
-回傳`x` 按位取反的結果。
+回傳 `x` 按位元取反的結果。
```lua
assert(bit32.bnot(x) ==
(-1 - x) % 2^32)
```
]]
-bit32.bor =
-'回傳參數按位或的結果。 '
-bit32.btest =
-'參數按位與的結果不為0時,回傳`true` 。 '
-bit32.bxor =
-'回傳參數按位異或的結果。 '
-bit32.extract =
-'回傳`n` 中第`field` 到第`field + width - 1` 位組成的結果。 '
-bit32.replace =
-'回傳`v` 的第`field` 到第`field + width - 1` 位替換`n` 的對應位後的結果。 '
-bit32.lrotate =
-'回傳`x` 向左旋轉`disp` 位的結果。 `disp` 為負時向右旋轉。 '
-bit32.lshift =
+bit32.bor =
+'回傳參數按位元或的結果。'
+bit32.btest =
+'參數按位元與的結果不為0時,回傳 `true` 。'
+bit32.bxor =
+'回傳參數按位元互斥或的結果。'
+bit32.extract =
+'回傳 `n` 中第 `field` 到第 `field + width - 1` 位組成的結果。'
+bit32.replace =
+'回傳 `v` 的第 `field` 到第 `field + width - 1` 位替換 `n` 的對應位後的結果。'
+bit32.lrotate =
+'回傳 `x` 向左旋轉 `disp` 位的結果。`disp` 為負時向右旋轉。'
+bit32.lshift =
[[
-回傳`x` 向左位移`disp` 位的結果。 `disp` 為負時向右位移。空位總是使用`0` 填充。
+回傳 `x` 向左位移 `disp` 位的結果。`disp` 為負時向右位移。空位總是使用 `0` 填充。
```lua
assert(bit32.lshift(b, disp) ==
(b * 2^disp) % 2^32)
```
]]
-bit32.rrotate =
-'回傳`x` 向右旋轉`disp` 位的結果。 `disp` 為負時向左旋轉。 '
-bit32.rshift =
+bit32.rrotate =
+'回傳 `x` 向右旋轉 `disp` 位的結果。`disp` 為負時向左旋轉。'
+bit32.rshift =
[[
-回傳`x` 向右位移`disp` 位的結果。 `disp` 為負時向左位移。空位總是使用`0` 填充。
+回傳 `x` 向右位移 `disp` 位的結果。`disp` 為負時向左位移。空位總是使用 `0` 填充。
```lua
assert(bit32.lshift(b, disp) ==
@@ -224,122 +224,122 @@ assert(bit32.lshift(b, disp) ==
```
]]
-coroutine =
+coroutine =
''
-coroutine.create =
-'創建一個主體函式為`f` 的新共常式。 f 必須是一個Lua 的函式。回傳這個新共常式,它是一個類型為`"thread"` 的對象。 '
-coroutine.isyieldable =
-'如果正在執行的共常式可以讓出,則回傳真。 '
+coroutine.create =
+'新增一個主體函式為 `f` 的新共常式。 f 必須是一個 Lua 的函式。 回傳這個新共常式,它是一個類型為 `"thread"` 的對象。'
+coroutine.isyieldable =
+'如果正在執行的共常式可以讓出,則回傳真。'
coroutine.isyieldable['>5.4'] =
-'如果共常式`co` 可以讓出,則回傳真。 `co` 預設為正在執行的共常式。 '
-coroutine.close =
-'關閉共常式`co`,並關閉它所有等待*to-be-closed* 的變數,並將共常式狀態設為`dead` 。 '
-coroutine.resume =
-'開始或繼續共常式`co` 的執行。 '
-coroutine.running =
-'回傳目前正在執行的共常式加一個布林值。如果目前執行的共常式是主執行緒,其為真。 '
-coroutine.status =
-'以字串形式回傳共常式`co` 的狀態。 '
-coroutine.wrap =
-'創建一個主體函式為`f` 的新共常式。 f 必須是一個Lua 的函式。回傳一個函式, 每次呼叫該函式都會延續該共常式。 '
-coroutine.yield =
-'掛起正在呼叫的共常式的執行。 '
-
-costatus.running =
-'正在執行。 '
-costatus.suspended =
-'掛起或是還沒有開始執行。 '
-costatus.normal =
-'是活動的,但並不在執行。 '
-costatus.dead =
-'執行完主體函式或因錯誤停止。 '
-
-debug =
+'如果共常式 `co` 可以讓出,則回傳真。`co` 預設為正在執行的共常式。'
+coroutine.close =
+'關閉共常式 `co`,並關閉它所有等待 *to-be-closed* 的變數,並將共常式狀態設為 `dead` 。'
+coroutine.resume =
+'開始或繼續共常式 `co` 的執行。'
+coroutine.running =
+'回傳目前正在執行的共常式加一個布林值。 如果目前執行的共常式是主執行緒,其為真。'
+coroutine.status =
+'以字串形式回傳共常式 `co` 的狀態。'
+coroutine.wrap =
+'新增一個主體函式為 `f` 的新共常式。 f 必須是一個 Lua 的函式。 回傳一個函式, 每次呼叫該函式都會延續該共常式。'
+coroutine.yield =
+'懸置正在呼叫的共常式的執行。'
+
+costatus.running =
+'正在執行。'
+costatus.suspended =
+'懸置或是還沒有開始執行。'
+costatus.normal =
+'是活動的,但並不在執行。'
+costatus.dead =
+'執行完主體函式或因錯誤停止。'
+
+debug =
''
-debug.debug =
-'進入一個使用者交互模式,執行使用者輸入的每個字串。 '
-debug.getfenv =
-'回傳對象`o` 的環境。 '
-debug.gethook =
-'回傳三個表示執行緒鉤子設定的值: 目前鉤子函式,目前鉤子遮罩,目前鉤子計數。 '
-debug.getinfo =
-'回傳關於一個函式訊息的表。 '
-debug.getlocal['<5.1'] =
-'回傳在堆疊的`level` 層處函式的索引為`index` 的區域變數的名字和值。 '
-debug.getlocal['>5.2'] =
-'回傳在堆疊的`f` 層處函式的索引為`index` 的區域變數的名字和值。 '
-debug.getmetatable =
-'回傳給定`value` 的元表。 '
-debug.getregistry =
-'回傳註冊表。 '
-debug.getupvalue =
-'回傳函式`f` 的第`up` 個上值的名字和值。 '
+debug.debug =
+'進入一個使用者交互模式,執行使用者輸入的每個字串。'
+debug.getfenv =
+'回傳對象 `o` 的環境。'
+debug.gethook =
+'回傳三個表示執行緒鉤子設定的值: 目前鉤子函式,目前鉤子掩碼,目前鉤子計數 。'
+debug.getinfo =
+'回傳關於一個函式訊息的表。'
+debug.getlocal['<5.1'] =
+'回傳在堆疊的 `level` 層處函式的索引為 `index` 的局部變數的名字和值。'
+debug.getlocal['>5.2'] =
+'回傳在堆疊的 `f` 層處函式的索引為 `index` 的局部變數的名字和值。'
+debug.getmetatable =
+'回傳給定 `value` 的元表。'
+debug.getregistry =
+'回傳註冊表。'
+debug.getupvalue =
+'回傳函式 `f` 的第 `up` 個上值的名字和值。'
debug.getuservalue['<5.3']=
-'回傳關聯在`u` 上的`Lua` 值。 '
+'回傳關聯在 `u` 上的 `Lua` 值。'
debug.getuservalue['>5.4']=
-'回傳關聯在`u` 上的第`n` 個`Lua` 值,以及一個布林,`false`表示值不存在。 '
-debug.setcstacklimit =
+'回傳關聯在 `u` 上的第 `n` 個 `Lua` 值,以及一個布林,`false`表示值不存在。'
+debug.setcstacklimit =
[[
-### **已在`Lua 5.4.2` 中廢棄**
+### **已在 `Lua 5.4.2` 中廢棄**
設定新的C堆疊限制。該限制控制Lua中嵌套呼叫的深度,以避免堆疊溢出。
如果設定成功,該函式回傳之前的限制;否則回傳`false`。
]]
-debug.setfenv =
-'將`table` 設定為`object` 的環境。 '
-debug.sethook =
-'將一個函式作為鉤子函式設入。 '
-debug.setlocal =
-'將`value` 賦給堆疊上第`level` 層函式的第`local` 個區域變數。 '
-debug.setmetatable =
-'將`value` 的元表設為`table` (可以是`nil`)。 '
-debug.setupvalue =
-'將`value` 設為函式`f` 的第`up` 個上值。 '
+debug.setfenv =
+'將 `table` 設定為 `object` 的環境。'
+debug.sethook =
+'將一個函式作為鉤子函式設入。'
+debug.setlocal =
+'將 `value` 賦給 堆疊上第 `level` 層函式的第 `local` 個局部變數。'
+debug.setmetatable =
+'將 `value` 的元表設為 `table` (可以是 `nil`)。'
+debug.setupvalue =
+'將 `value` 設為函式 `f` 的第 `up` 個上值。'
debug.setuservalue['<5.3']=
-'將`value` 設為`udata` 的關聯值。 '
+'將 `value` 設為 `udata` 的關聯值。'
debug.setuservalue['>5.4']=
-'將`value` 設為`udata` 的第`n` 個關聯值。 '
-debug.traceback =
-'回傳呼叫堆疊的堆疊回溯訊息。字串可選項`message` 被添加在堆疊回溯訊息的開頭。 '
-debug.upvalueid =
-'回傳指定函式第`n` 個上值的唯一標識符(一個輕量使用者資料)。 '
-debug.upvaluejoin =
-'讓Lua 閉包`f1` 的第`n1` 個上值引用`Lua` 閉包`f2` 的第`n2` 個上值。 '
-
-infowhat.n =
-'`name` 和`namewhat`'
-infowhat.S =
-'`source`,`short_src`,`linedefined`,`lalinedefined`,和`what`'
-infowhat.l = -- TODO: need translate!
+'將 `value` 設為 `udata` 的第 `n` 個關聯值。'
+debug.traceback =
+'回傳呼叫堆疊的堆疊回溯訊息。 字串可選項 `message` 被添加在堆疊回溯訊息的開頭。'
+debug.upvalueid =
+'回傳指定函式第 `n` 個上值的唯一識別符(一個輕量使用者資料)。'
+debug.upvaluejoin =
+'讓 Lua 閉包 `f1` 的第 `n1` 個上值 引用 `Lua` 閉包 `f2` 的第 `n2` 個上值。'
+
+infowhat.n =
+'`name` 和 `namewhat`'
+infowhat.S =
+'`source`,`short_src`,`linedefined`,`lalinedefined`,和 `what`'
+infowhat.l =
'`currentline`'
-infowhat.t = -- TODO: need translate!
+infowhat.t =
'`istailcall`'
-infowhat.u['<5.1'] = -- TODO: need translate!
+infowhat.u['<5.1'] =
'`nups`'
-infowhat.u['>5.2'] =
-'`nups`、`nparams` 和`isvararg`'
-infowhat.f = -- TODO: need translate!
+infowhat.u['>5.2'] =
+'`nups`、`nparams` 和 `isvararg`'
+infowhat.f =
'`func`'
-infowhat.r =
-'`ftransfer` 和`ntransfer`'
-infowhat.L = -- TODO: need translate!
+infowhat.r =
+'`ftransfer` 和 `ntransfer`'
+infowhat.L =
'`activelines`'
-hookmask.c =
-'每當Lua 呼叫一個函式時,呼叫鉤子。 '
-hookmask.r =
-'每當Lua 從一個函式內回傳時,呼叫鉤子。 '
-hookmask.l =
-'每當Lua 進入新的一行時,呼叫鉤子。 '
+hookmask.c =
+'每當 Lua 呼叫一個函式時,呼叫鉤子。'
+hookmask.r =
+'每當 Lua 從一個函式內回傳時,呼叫鉤子。'
+hookmask.l =
+'每當 Lua 進入新的一行時,呼叫鉤子。'
-file =
+file =
''
-file[':close'] =
-'關閉`file`。 '
-file[':flush'] =
-'將寫入的資料儲存到`file` 中。 '
-file[':lines'] = -- TODO: need translate!
+file[':close'] =
+'關閉 `file`。'
+file[':flush'] =
+'將寫入的資料儲存到 `file` 中。'
+file[':lines'] =
[[
------
```lua
@@ -348,53 +348,53 @@ for c in file:lines(...) do
end
```
]]
-file[':read'] =
-'讀檔案`file`, 指定的格式決定了要讀什麼。 '
-file[':seek'] =
-'設定及獲取基於檔案開頭處計算出的位置。 '
-file[':setvbuf'] =
-'設定輸出檔案的緩衝模式。 '
-file[':write'] =
-'將參數的值逐個寫入`file`。 '
-
-readmode.n =
-'讀取一個數字,根據Lua 的轉換文法回傳浮點數或整數。 '
-readmode.a =
-'從目前位置開始讀取整個檔案。 '
-readmode.l =
-'讀取一行並忽略行結束標記。 '
-readmode.L =
-'讀取一行並保留行結束標記。 '
-
-seekwhence.set =
-'基點為0 (檔案開頭)。 '
-seekwhence.cur =
-'基點為目前位置。 '
-seekwhence['.end'] =
-'基點為檔案尾。 '
-
-vbuf.no =
-'不緩衝;輸出操作立刻生效。 '
-vbuf.full =
-'完全緩衝;只有在快取滿或呼叫flush 時才做輸出操作。 '
-vbuf.line =
-'行緩衝;輸出將緩衝到每次換行前。 '
-
-io =
+file[':read'] =
+'讀檔案 `file`, 指定的格式決定了要讀什麼。'
+file[':seek'] =
+'設定及獲取基於檔案開頭處計算出的位置。'
+file[':setvbuf'] =
+'設定輸出檔案的緩衝模式。'
+file[':write'] =
+'將參數的值逐個寫入 `file`。'
+
+readmode.n =
+'讀取一個數字,根據 Lua 的轉換文法回傳浮點數或整數。'
+readmode.a =
+'從目前位置開始讀取整個檔案。'
+readmode.l =
+'讀取一行並忽略行結束標記。'
+readmode.L =
+'讀取一行並保留行結束標記。'
+
+seekwhence.set =
+'基點為 0 (檔案開頭)。'
+seekwhence.cur =
+'基點為目前位置。'
+seekwhence['.end'] =
+'基點為檔案尾。'
+
+vbuf.no =
+'不緩衝;輸出操作立刻生效。'
+vbuf.full =
+'完全緩衝;只有在快取滿或呼叫 flush 時才做輸出操作。'
+vbuf.line =
+'行緩衝;輸出將緩衝到每次換行前。'
+
+io =
''
-io.stdin =
-'標準輸入。 '
-io.stdout =
-'標準輸出。 '
-io.stderr =
-'標準錯誤。 '
-io.close =
-'關閉`file` 或預設輸出檔案。 '
-io.flush =
-'將寫入的資料儲存到預設輸出檔案中。 '
-io.input =
-'設定`file` 為預設輸入檔案。 '
-io.lines = -- TODO: need translate!
+io.stdin =
+'標準輸入。'
+io.stdout =
+'標準輸出。'
+io.stderr =
+'標準錯誤。'
+io.close =
+'關閉 `file` 或預設輸出檔案。'
+io.flush =
+'將寫入的資料儲存到預設輸出檔案中。'
+io.input =
+'設定 `file` 為預設輸入檔案。'
+io.lines =
[[
------
```lua
@@ -403,237 +403,237 @@ for c in io.lines(filename, ...) do
end
```
]]
-io.open =
-'用字串`mode` 指定的模式打開一個檔案。 '
-io.output =
-'設定`file` 為預設輸出檔案。 '
-io.popen =
-'用一個分離程序開啟程式`prog` 。 '
-io.read =
-'讀檔案`file`, 指定的格式決定了要讀什麼。 '
-io.tmpfile =
-'如果成功,回傳一個臨時檔案的控制代碼。 '
-io.type =
-'檢查`obj` 是否是合法的檔案控制代碼。 '
-io.write =
-'將參數的值逐個寫入預設輸出檔案。 '
-
-openmode.r =
-'讀模式。 '
-openmode.w =
-'寫模式。 '
-openmode.a =
-'追加模式。 '
-openmode['.r+'] =
-'更新模式,所有之前的資料都保留。 '
-openmode['.w+'] =
-'更新模式,所有之前的資料都刪除。 '
-openmode['.a+'] =
-'追加更新模式,所有之前的資料都保留,只允許在檔案尾部做寫入。 '
-openmode.rb =
-'讀模式。 (二進制方式)'
-openmode.wb =
-'寫模式。 (二進制方式)'
-openmode.ab =
-'追加模式。 (二進制方式)'
-openmode['.r+b'] =
-'更新模式,所有之前的資料都保留。 (二進制方式)'
-openmode['.w+b'] =
-'更新模式,所有之前的資料都刪除。 (二進制方式)'
-openmode['.a+b'] =
-'追加更新模式,所有之前的資料都保留,只允許在檔案尾部做寫入。 (二進制方式)'
-
-popenmode.r =
-'從這個程式中讀取資料。 (二進制方式)'
-popenmode.w =
-'向這個程式寫入輸入。 (二進制方式)'
-
-filetype.file =
-'是一個打開的檔案控制代碼。 '
-filetype['.closed file'] =
-'是一個關閉的檔案控制代碼。 '
-filetype['.nil'] =
-'不是檔案控制代碼。 '
-
-math =
+io.open =
+'用字串 `mode` 指定的模式打開一個檔案。'
+io.output =
+'設定 `file` 為預設輸出檔案。'
+io.popen =
+'用一個分離程序開啟程式 `prog` 。'
+io.read =
+'讀檔案 `file`, 指定的格式決定了要讀什麼。'
+io.tmpfile =
+'如果成功,回傳一個臨時檔案的控制代碼。'
+io.type =
+'檢查 `obj` 是否是合法的檔案控制代碼。'
+io.write =
+'將參數的值逐個寫入預設輸出檔案。'
+
+openmode.r =
+'讀模式。'
+openmode.w =
+'寫模式。'
+openmode.a =
+'追加模式。'
+openmode['.r+'] =
+'更新模式,所有之前的資料都保留。'
+openmode['.w+'] =
+'更新模式,所有之前的資料都刪除。'
+openmode['.a+'] =
+'追加更新模式,所有之前的資料都保留,只允許在檔案尾部做寫入。'
+openmode.rb =
+'讀模式。(二進制方式)'
+openmode.wb =
+'寫模式。(二進制方式)'
+openmode.ab =
+'追加模式。(二進制方式)'
+openmode['.r+b'] =
+'更新模式,所有之前的資料都保留。(二進制方式)'
+openmode['.w+b'] =
+'更新模式,所有之前的資料都刪除。(二進制方式)'
+openmode['.a+b'] =
+'追加更新模式,所有之前的資料都保留,只允許在檔案尾部做寫入。(二進制方式)'
+
+popenmode.r =
+'從這個程式中讀取資料。(二進制方式)'
+popenmode.w =
+'向這個程式寫入輸入。(二進制方式)'
+
+filetype.file =
+'是一個打開的檔案控制代碼。'
+filetype['.closed file'] =
+'是一個關閉的檔案控制代碼。'
+filetype['.nil'] =
+'不是檔案控制代碼。'
+
+math =
''
-math.abs =
-'回傳`x` 的絕對值。 '
-math.acos =
-'回傳`x` 的反餘弦值(用弧度表示)。 '
-math.asin =
-'回傳`x` 的反正弦值(用弧度表示)。 '
-math.atan['<5.2'] =
-'回傳`x` 的反正切值(用弧度表示)。 '
-math.atan['>5.3'] =
-'回傳`y/x` 的反正切值(用弧度表示)。 '
-math.atan2 =
-'回傳`y/x` 的反正切值(用弧度表示)。 '
-math.ceil =
-'回傳不小於`x` 的最小整數值。 '
-math.cos =
-'回傳`x` 的餘弦(假定參數是弧度)。 '
-math.cosh =
-'回傳`x` 的雙曲餘弦(假定參數是弧度)。 '
-math.deg =
-'將角`x` 從弧度轉換為角度。 '
-math.exp =
-'回傳`e^x` 的值(e 為自然對數的底)。 '
-math.floor =
-'回傳不大於`x` 的最大整數值。 '
-math.fmod =
-'回傳`x` 除以`y`,將商向零圓整後的餘數。 '
-math.frexp =
-'將`x` 分解為尾數與指數,回傳值符合`x = m * (2 ^ e)` 。 `e` 是一個整數,`m` 是[0.5, 1) 之間的規格化小數(`x` 為0時`m` 為0)。 '
-math.huge =
-'一個比任何數字值都大的浮點數。 '
-math.ldexp =
-'回傳`m * (2 ^ e)` 。 '
-math.log['<5.1'] =
-'回傳`x` 的自然對數。 '
-math.log['>5.2'] =
-'回以指定底的`x` 的對數。 '
-math.log10 =
-'回傳`x` 的以10為底的對數。 '
-math.max =
-'回傳參數中最大的值, 大小由Lua 操作`<` 決定。 '
-math.maxinteger =
-'最大值的整數。 '
-math.min =
-'回傳參數中最小的值, 大小由Lua 操作`<` 決定。 '
-math.mininteger =
-'最小值的整數。 '
-math.modf =
-'回傳`x` 的整數部分和小數部分。 '
-math.pi =
-'*π* 的值。 '
-math.pow =
-'回傳`x ^ y` 。 '
-math.rad =
-'將角`x` 從角度轉換為弧度。 '
-math.random =
+math.abs =
+'回傳 `x` 的絕對值。'
+math.acos =
+'回傳 `x` 的反餘弦值(用弧度表示)。'
+math.asin =
+'回傳 `x` 的反正弦值(用弧度表示)。'
+math.atan['<5.2'] =
+'回傳 `x` 的反正切值(用弧度表示)。'
+math.atan['>5.3'] =
+'回傳 `y/x` 的反正切值(用弧度表示)。'
+math.atan2 =
+'回傳 `y/x` 的反正切值(用弧度表示)。'
+math.ceil =
+'回傳不小於 `x` 的最小整數值。'
+math.cos =
+'回傳 `x` 的餘弦(假定參數是弧度)。'
+math.cosh =
+'回傳 `x` 的雙曲餘弦(假定參數是弧度)。'
+math.deg =
+'將角 `x` 從弧度轉換為角度。'
+math.exp =
+'回傳 `e^x` 的值 (e 為自然對數的底)。'
+math.floor =
+'回傳不大於 `x` 的最大整數值。'
+math.fmod =
+'回傳 `x` 除以 `y`,將商向零捨入後的餘數。'
+math.frexp =
+'將 `x` 分解為尾數與指數,回傳值符合 `x = m * (2 ^ e)` 。`e` 是一個整數,`m` 是 [0.5, 1) 之間的規格化小數 (`x` 為0時 `m` 為0)。'
+math.huge =
+'一個比任何數字值都大的浮點數。'
+math.ldexp =
+'回傳 `m * (2 ^ e)` 。'
+math.log['<5.1'] =
+'回傳 `x` 的自然對數。'
+math.log['>5.2'] =
+'回以指定底的 `x` 的對數。'
+math.log10 =
+'回傳 `x` 的以10為底的對數。'
+math.max =
+'回傳參數中最大的值, 大小由 Lua 操作 `<` 決定。'
+math.maxinteger =
+'最大值的整數。'
+math.min =
+'回傳參數中最小的值, 大小由 Lua 操作 `<` 決定。'
+math.mininteger =
+'最小值的整數。'
+math.modf =
+'回傳 `x` 的整數部分和小數部分。'
+math.pi =
+'*π* 的值。'
+math.pow =
+'回傳 `x ^ y` 。'
+math.rad =
+'將角 `x` 從角度轉換為弧度。'
+math.random =
[[
-* `math.random()`: 回傳[0,1) 區間內一致分佈的浮點偽隨機數。
-* `math.random(n)`: 回傳[1, n] 區間內一致分佈的整數偽隨機數。
-* `math.random(m, n)`: 回傳[m, n] 區間內一致分佈的整數偽隨機數。
+* `math.random()`: 回傳 [0,1) 區間內均勻分佈的浮點偽隨機數。
+* `math.random(n)`: 回傳 [1, n] 區間內均勻分佈的整數偽隨機數。
+* `math.random(m, n)`: 回傳 [m, n] 區間內均勻分佈的整數偽隨機數。
]]
-math.randomseed['<5.3'] =
-'把`x` 設為偽隨機數發生器的“種子”: 相同的種子產生相同的隨機數列。 '
-math.randomseed['>5.4'] =
+math.randomseed['<5.3'] =
+'把 `x` 設為偽隨機數發生器的“種子”: 相同的種子產生相同的隨機數列。'
+math.randomseed['>5.4'] =
[[
-* `math.randomseed(x, y)`: 將`x` 與`y` 連接為128位的種子來重新初始化偽隨機生成器。
-* `math.randomseed(x)`: 等同於`math.randomseed(x, 0)` 。
+* `math.randomseed(x, y)`: 將 `x` 與 `y` 連接為128位的種子來重新初始化偽隨機生成器。
+* `math.randomseed(x)`: 等同於 `math.randomseed(x, 0)` 。
* `math.randomseed()`: 生成一個較弱的隨機種子。
]]
-math.sin =
-'回傳`x` 的正弦值(假定參數是弧度)。 '
-math.sinh =
-'回傳`x` 的雙曲正弦值(假定參數是弧度)。 '
-math.sqrt =
-'回傳`x` 的平方根。 '
-math.tan =
-'回傳`x` 的正切值(假定參數是弧度)。 '
-math.tanh =
-'回傳`x` 的雙曲正切值(假定參數是弧度)。 '
-math.tointeger =
-'如果`x` 可以轉換為一個整數, 回傳該整數。 '
-math.type =
-'如果`x` 是整數,回傳`"integer"`, 如果它是浮點數,回傳`"float"`, 如果`x` 不是數字,回傳`nil`。 '
-math.ult =
-'如果整數`m` 和`n` 以無符號整數形式比較, `m` 在`n` 之下,回傳布林真否則回傳假。 '
-
-os =
+math.sin =
+'回傳 `x` 的正弦值(假定參數是弧度)。'
+math.sinh =
+'回傳 `x` 的雙曲正弦值(假定參數是弧度)。'
+math.sqrt =
+'回傳 `x` 的平方根。'
+math.tan =
+'回傳 `x` 的正切值(假定參數是弧度)。'
+math.tanh =
+'回傳 `x` 的雙曲正切值(假定參數是弧度)。'
+math.tointeger =
+'如果 `x` 可以轉換為一個整數, 回傳該整數。'
+math.type =
+'如果 `x` 是整數,回傳 `"integer"`, 如果它是浮點數,回傳 `"float"`, 如果 `x` 不是數字,回傳 `nil`。'
+math.ult =
+'如果整數 `m` 和 `n` 以無符號整數形式比較, `m` 在 `n` 之下,回傳布林真否則回傳假。'
+
+os =
''
-os.clock =
-'回傳程式使用的按秒計CPU 時間的近似值。 '
-os.date =
-'回傳一個包含日期及時刻的字串或表。格式化方法取決於所給字串`format`。 '
-os.difftime =
-'回傳以秒計算的時刻`t1` 到`t2` 的差值。 '
-os.execute =
-'呼叫系統直譯器執行`command`。 '
-os.exit['<5.1'] =
-'呼叫C 函式`exit` 終止宿主程式。 '
-os.exit['>5.2'] =
-'呼叫ISO C 函式`exit` 終止宿主程式。 '
-os.getenv =
-'回傳程序環境變數`varname` 的值。 '
-os.remove =
-'刪除指定名字的檔案。 '
-os.rename =
-'將名字為`oldname` 的檔案或目錄更名為`newname`。 '
-os.setlocale =
-'設定程式的目前區域。 '
-os.time =
-'當不傳參數時,回傳目前時刻。如果傳入一張表,就回傳由這張表表示的時刻。 '
-os.tmpname =
-'回傳一個可用於臨時檔案的檔名字串。 '
-
-osdate.year =
+os.clock =
+'回傳程式使用的按秒計 CPU 時間的近似值。'
+os.date =
+'回傳一個包含日期及時刻的字串或表。 格式化方法取決於所給字串 `format`。'
+os.difftime =
+'回傳以秒計算的時刻 `t1` 到 `t2` 的差值。'
+os.execute =
+'呼叫系統直譯器執行 `command`。'
+os.exit['<5.1'] =
+'呼叫 C 函式 `exit` 終止宿主程式。'
+os.exit['>5.2'] =
+'呼叫 ISO C 函式 `exit` 終止宿主程式。'
+os.getenv =
+'回傳程序環境變數 `varname` 的值。'
+os.remove =
+'刪除指定名字的檔案。'
+os.rename =
+'將名字為 `oldname` 的檔案或目錄更名為 `newname`。'
+os.setlocale =
+'設定程式的目前區域。'
+os.time =
+'當不傳參數時,回傳目前時刻。 如果傳入一張表,就回傳由這張表表示的時刻。'
+os.tmpname =
+'回傳一個可用於臨時檔案的檔名字串。'
+
+osdate.year =
'四位數字'
-osdate.month = -- TODO: need translate!
+osdate.month =
'1-12'
-osdate.day = -- TODO: need translate!
+osdate.day =
'1-31'
-osdate.hour = -- TODO: need translate!
+osdate.hour =
'0-23'
-osdate.min = -- TODO: need translate!
+osdate.min =
'0-59'
-osdate.sec = -- TODO: need translate!
+osdate.sec =
'0-61'
-osdate.wday =
-'星期幾,1-7,星期天為1'
-osdate.yday =
+osdate.wday =
+'星期幾,1-7,星期天為 1'
+osdate.yday =
'當年的第幾天,1-366'
-osdate.isdst =
+osdate.isdst =
'夏令時標記,一個布林值'
-package =
+package =
''
-require['<5.3'] =
-'載入一個模組,回傳該模組的回傳值(`nil`時為`true`)。 '
-require['>5.4'] =
-'載入一個模組,回傳該模組的回傳值(`nil`時為`true`)與搜尋器回傳的載入資料。預設搜尋器的載入資料指示了載入位置,對於檔案來說就是檔案路徑。 '
-
-package.config =
-'一個描述有一些為包管理準備的編譯期配置訊息的串。 '
-package.cpath =
-'這個路徑被`require` 在C 載入器中做搜尋時用到。 '
-package.loaded =
-'用於`require` 控制哪些模組已經被載入的表。 '
-package.loaders =
-'用於`require` 控制如何載入模組的表。 '
-package.loadlib =
-'讓宿主程式動態連接C 庫`libname` 。 '
-package.path =
-'這個路徑被`require` 在Lua 載入器中做搜尋時用到。 '
-package.preload =
-'儲存有一些特殊模組的載入器。 '
-package.searchers =
-'用於`require` 控制如何載入模組的表。 '
-package.searchpath =
-'在指定`path` 中搜尋指定的`name` 。 '
-package.seeall =
-'給`module` 設定一個元表,該元表的`__index` 域為全域環境,這樣模組便會繼承全域環境的值。可作為`module` 函式的選項。 '
-
-string =
+require['<5.3'] =
+'載入一個模組,回傳該模組的回傳值(`nil`時為`true`)。'
+require['>5.4'] =
+'載入一個模組,回傳該模組的回傳值(`nil`時為`true`)與搜尋器回傳的載入資料。預設搜尋器的載入資料指示了載入位置,對於檔案來説就是檔案路徑。'
+
+package.config =
+'一個描述有一些為包管理準備的編譯期組態訊息的串。'
+package.cpath =
+'這個路徑被 `require` 在 C 載入器中做搜尋時用到。'
+package.loaded =
+'用於 `require` 控制哪些模組已經被載入的表。'
+package.loaders =
+'用於 `require` 控制如何載入模組的表。'
+package.loadlib =
+'讓宿主程式動態連接 C 庫 `libname` 。'
+package.path =
+'這個路徑被 `require` 在 Lua 載入器中做搜尋時用到。'
+package.preload =
+'儲存有一些特殊模組的載入器。'
+package.searchers =
+'用於 `require` 控制如何載入模組的表。'
+package.searchpath =
+'在指定 `path` 中搜尋指定的 `name` 。'
+package.seeall =
+'給 `module` 設定一個元表,該元表的 `__index` 域為全域環境,這樣模組便會繼承全域環境的值。可作為 `module` 函式的選項。'
+
+string =
''
-string.byte =
-'回傳字元`s[i]`, `s[i+1]`, ... ,`s[j]` 的內部數字編碼。 '
-string.char =
-'接收零或更多的整數。回傳和參數數量相同長度的字串。其中每個字元的內部編碼值等於對應的參數值。 '
-string.dump =
-'回傳包含有以二進制方式表示的(一個*二進制程式碼區塊* )指定函式的字串。 '
-string.find =
-'查找第一個字串中匹配到的`pattern`(參見§6.4.1)。 '
-string.format =
-'回傳不定數量參數的格式化版本,格式化串為第一個參數。 '
-string.gmatch =
+string.byte =
+'回傳字元 `s[i]`, `s[i+1]`, ... ,`s[j]` 的內部數字編碼。'
+string.char =
+'接收零或更多的整數。 回傳和參數數量相同長度的字串。 其中每個字元的內部編碼值等於對應的參數值。'
+string.dump =
+'回傳包含有以二進制方式表示的(一個 *二進制程式碼區塊* )指定函式的字串。'
+string.find =
+'查找第一個字串中匹配到的 `pattern`(參見 §6.4.1)。'
+string.format =
+'回傳不定數量參數的格式化版本,格式化串為第一個參數。'
+string.gmatch =
[[
-回傳一個疊代器函式。每次呼叫這個函式都會繼續以`pattern` (參見§6.4.1) 對s 做匹配,並回傳所有捕獲到的值。
+回傳一個疊代器函式。 每次呼叫這個函式都會繼續以 `pattern` (參見 §6.4.1) 對 s 做匹配,並回傳所有捕獲到的值。
-下面這個例子會循環疊代字串s 中所有的單詞, 並逐行輸出:
+下面這個例子會循環疊代字串 s 中所有的單詞, 並逐行列印:
```lua
s =
"hello world from Lua"
@@ -642,106 +642,106 @@ string.gmatch =
end
```
]]
-string.gsub =
-'將字串s 中,所有的(或是在n 給出時的前n 個) pattern (參見§6.4.1)都替換成repl ,並回傳其副本。 '
-string.len =
-'回傳其長度。 '
-string.lower =
-'將其中的大寫字元都轉為小寫後回傳其副本。 '
-string.match =
-'在字串s 中找到第一個能用pattern (參見§6.4.1)匹配到的部分。如果能找到,match 回傳其中的捕獲物; 否則回傳nil 。 '
-string.pack =
-'回傳一個打包了(即以二進制形式序列化) v1, v2 等值的二進制字串。字串fmt 為打包格式(參見§6.4.2)。 '
-string.packsize =
-[[回傳以指定格式用$string.pack 打包的字串的長度。格式化字串中不可以有變長選項's' 或'z' (參見§6.4.2)。 ]]
-string.rep['<5.1'] =
-'回傳`n` 個字串`s` 連在一起的字串。如果`n` 不是正數則回傳空串。 '
-string.rep['>5.2'] =
-'回傳`n` 個字串`s` 以字串`sep` 為分割符連在一起的字串。預設的`sep` 值為空字串(即沒有分割符)。如果`n` 不是正數則回傳空字串。 '
-string.reverse =
-'回傳字串s 的翻轉串。 '
-string.sub =
-'回傳字串的子串, 該子串從`i` 開始到`j` 為止。 '
-string.unpack =
-'回傳以格式fmt (參見§6.4.2) 打包在字串s (參見string.pack) 中的值。 '
-string.upper =
-'接收一個字串,將其中的小寫字元都轉為大寫後回傳其副本。 '
-
-table =
+string.gsub =
+'將字串 s 中,所有的(或是在 n 給出時的前 n 個) pattern (參見 §6.4.1)都替換成 repl ,並回傳其副本。'
+string.len =
+'回傳其長度。'
+string.lower =
+'將其中的大寫字元都轉為小寫後回傳其副本。'
+string.match =
+'在字串 s 中找到第一個能用 pattern (參見 §6.4.1)匹配到的部分。 如果能找到,match 回傳其中的捕獲物; 否則回傳 nil 。'
+string.pack =
+'回傳一個打包了(即以二進制形式序列化) v1, v2 等值的二進制字串。 字串 fmt 為打包格式(參見 §6.4.2)。'
+string.packsize =
+[[回傳以指定格式用 $string.pack 打包的字串的長度。 格式化字串中不可以有變長選項 's' 或 'z' (參見 §6.4.2)。]]
+string.rep['<5.1'] =
+'回傳 `n` 個字串 `s` 連在一起的字串。 如果 `n` 不是正數則回傳空串。'
+string.rep['>5.2'] =
+'回傳 `n` 個字串 `s` 以字串 `sep` 為分割符連在一起的字串。 預設的 `sep` 值為空字串(即沒有分割符)。 如果 `n` 不是正數則回傳空串。'
+string.reverse =
+'回傳字串 s 的翻轉串。'
+string.sub =
+'回傳字串的子串, 該子串從 `i` 開始到 `j` 為止。'
+string.unpack =
+'回傳以格式 fmt (參見 §6.4.2) 打包在字串 s (參見 string.pack) 中的值。'
+string.upper =
+'接收一個字串,將其中的小寫字元都轉為大寫後回傳其副本。'
+
+table =
''
-table.concat =
-'提供一個列表,其所有元素都是字串或數字,回傳字串`list[i]..sep..list[i+1] ··· sep..list[j]`。 '
-table.insert =
-'在`list` 的位置`pos` 處插入元素`value`。 '
-table.maxn =
-'回傳給定表的最大正數索引,如果表沒有正數索引,則回傳零。 '
-table.move =
+table.concat =
+'提供一個列表,其所有元素都是字串或數字,回傳字串 `list[i]..sep..list[i+1] ··· sep..list[j]`。'
+table.insert =
+'在 `list` 的位置 `pos` 處插入元素 `value`。'
+table.maxn =
+'回傳給定表的最大正數索引,如果表沒有正數索引,則回傳零。'
+table.move =
[[
-將元素從表`a1` 移到表`a2`。
+將元素從表 `a1` 移到表 `a2`。
```lua
a2[t],··· =
a1[f],···,a1[e]
return a2
```
]]
-string.rep['>5.2'] = -- TODO: need translate!
-'Returns a string that is the concatenation of `n` copies of the string `s` separated by the string `sep`.'
-string.rep['<5.1'] = -- TODO: need translate!
-'Returns a string that is the concatenation of `n` copies of the string `s` .'
-string.reverse = -- TODO: need translate!
-'Returns a string that is the string `s` reversed.'
-string.sub = -- TODO: need translate!
-'Returns the substring of the string that starts at `i` and continues until `j`.'
-string.unpack = -- TODO: need translate!
-'Returns the values packed in string according to the format string `fmt` (see §6.4.2) .'
-string.upper = -- TODO: need translate!
-'Returns a copy of this string with all lowercase letters changed to uppercase.'
-
-table = -- TODO: need translate!
+string.rep['>5.2'] =
+'回傳一個由 `n` 份被 `sep` 分割的字串 `s` 所序連而成的字串。'
+string.rep['<5.1'] =
+'回傳一個由 `n` 份字串 `s` 序連而成的字串。'
+string.reverse =
+'回傳一個字串 `s` 的反轉字串。'
+string.sub =
+'回傳一個從 `i` 開始並在 `j` 結束的子字串。'
+string.unpack =
+'根據格式字串 `fmt` 回傳打包在字串內的值 (參見 §6.4.2) 。'
+string.upper =
+'回傳此字串的副本,其中所有小寫字母都更改為大寫。'
+
+table =
''
-table.concat = -- TODO: need translate!
-'Given a list where all elements are strings or numbers, returns the string `list[i]..sep..list[i+1] ··· sep..list[j]`.'
-table.insert = -- TODO: need translate!
-'Inserts element `value` at position `pos` in `list`.'
-table.maxn = -- TODO: need translate!
-'Returns the largest positive numerical index of the given table, or zero if the table has no positive numerical indices.'
-table.move = -- TODO: need translate!
+table.concat =
+'給定一個所有元素都是字串或數字的清單,回傳字串 `list[i]..sep..list[i+1] ··· sep..list[j]`。'
+table.insert =
+'在 `list` 內的 `pos` 位置插入元素 `value`。'
+table.maxn =
+'回傳表內最大正數索引,若不存在正數索引則回傳0'
+table.move =
[[
-Moves elements from table `a1` to table `a2`.
+從表 `a1` 移動元素至表 `a2`。
```lua
a2[t],··· =
a1[f],···,a1[e]
return a2
```
]]
-table.pack =
-'回傳用所有參數以鍵`1`,`2`, 等填充的新表, 並將`"n"` 這個域設為參數的總數。 '
-table.remove =
-'移除`list` 中`pos` 位置上的元素,並回傳這個被移除的值。 '
-table.sort =
-'在表內從`list[1]` 到`list[#list]` *原地* 對其間元素按指定順序排序。 '
-table.unpack =
+table.pack =
+'回傳用所有參數以鍵 `1`,`2`, 等填充的新表, 並將 `"n"` 這個域設為參數的總數。'
+table.remove =
+'移除 `list` 中 `pos` 位置上的元素,並回傳這個被移除的值。'
+table.sort =
+'在表內從 `list[1]` 到 `list[#list]` *原地* 對其間元素按指定順序排序。'
+table.unpack =
[[
-回傳列表中的元素。這個函式等價於
+回傳列表中的元素。 這個函式等價於
```lua
return list[i], list[i+1], ···, list[j]
```
-i 預設為1 ,j 預設為#list。
+i 預設為 1 ,j 預設為 #list。
]]
-table.foreach =
+table.foreach =
'走訪表中的每一個元素,並以key和value執行回呼函式。如果回呼函式回傳一個非nil值則循環終止,並且回傳這個值。該函式等同pair(list),比pair(list)更慢。不推薦使用'
-table.foreachi =
-'走訪數組中的每一個元素,並以索引號index和value執行回呼函式。如果回呼函式回傳一個非nil值則循環終止,並且回傳這個值。該函式等同ipair(list),比ipair(list)更慢。不推薦使用'
-table.getn =
-'回傳表的長度。該函式等價於#list。 '
+table.foreachi =
+'走訪表中的每一個元素,並以索引號index和value執行回呼函式。如果回呼函式回傳一個非nil值則循環終止,並且回傳這個值。該函式等同ipair(list),比ipair(list)更慢。不推薦使用'
+table.getn =
+'回傳表的長度。該函式等價於#list。'
-utf8 =
+utf8 =
''
-utf8.char =
-'接收零或多個整數, 將每個整數轉換成對應的UTF-8 位元組序列,並回傳這些序列連接到一起的字串。 '
-utf8.charpattern =
-'用於精確匹配到一個UTF-8 位元組序列的模式,它假定處理的對像是一個合法的UTF-8 字串。 '
-utf8.codes =
+utf8.char =
+'接收零或多個整數, 將每個整數轉換成對應的 UTF-8 位元組序列,並回傳這些序列連接到一起的字串。'
+utf8.charpattern =
+'用於精確匹配到一個 UTF-8 位元組序列的模式,它假定處理的對象是一個合法的 UTF-8 字串。'
+utf8.codes =
[[
回傳一系列的值,可以讓
```lua
@@ -749,11 +749,11 @@ for p, c in utf8.codes(s) do
body
end
```
-疊代出字串s 中所有的字元。這裡的p 是位置(按位元組數)而c 是每個字元的編號。如果處理到一個不合法的位元組序列,將拋出一個錯誤。
+疊代出字串 s 中所有的字元。 這裡的 p 是位置(按位元組數)而 c 是每個字元的編號。 如果處理到一個不合法的位元組序列,將拋出一個錯誤。
]]
-utf8.codepoint =
-'以整數形式回傳`s` 中從位置`i` 到`j` 間(包括兩端) 所有字元的編號。 '
-utf8.len =
-'回傳字串`s` 中從位置`i` 到`j` 間(包括兩端) UTF-8 字元的個數。 '
-utf8.offset =
-'回傳編碼在`s` 中的第`n` 個字元的開始位置(按位元組數) (從位置`i` 處開始統計)。 '
+utf8.codepoint =
+'以整數形式回傳 `s` 中 從位置 `i` 到 `j` 間(包括兩端) 所有字元的編號。'
+utf8.len =
+'回傳字串 `s` 中 從位置 `i` 到 `j` 間 (包括兩端) UTF-8 字元的個數。'
+utf8.offset =
+'回傳編碼在 `s` 中的第 `n` 個字元的開始位置(按位元組數) (從位置 `i` 處開始統計)。'
diff --git a/locale/zh-tw/script.lua b/locale/zh-tw/script.lua
index 5cbc2b26..62a5f6e5 100644
--- a/locale/zh-tw/script.lua
+++ b/locale/zh-tw/script.lua
@@ -1,529 +1,535 @@
-DIAG_LINE_ONLY_SPACE =
-'只有空格的空行。 '
-DIAG_LINE_POST_SPACE =
-'後置空格。 '
-DIAG_UNUSED_LOCAL =
-'未使用的區域變數`{}`。 '
-DIAG_UNDEF_GLOBAL =
-'未定義的全域變數`{}`。 '
-DIAG_UNDEF_FIELD =
-'未定義的屬性/欄位`{}`。 '
-DIAG_UNDEF_ENV_CHILD =
-'未定義的變數`{}`(重載了`_ENV` )。 '
-DIAG_UNDEF_FENV_CHILD =
-'未定義的變數`{}`(處於模組中)。 '
-DIAG_GLOBAL_IN_NIL_ENV =
-'不能使用全域變數(`_ENV`被置為了`nil`)。 '
+DIAG_LINE_ONLY_SPACE =
+'只有空格的空行。'
+DIAG_LINE_POST_SPACE =
+'後置空格。'
+DIAG_UNUSED_LOCAL =
+'未使用的局部變數 `{}`。'
+DIAG_UNDEF_GLOBAL =
+'未定義的全域變數 `{}`。'
+DIAG_UNDEF_FIELD =
+'未定義的屬性/欄位 `{}`。'
+DIAG_UNDEF_ENV_CHILD =
+'未定義的變數 `{}`(多載了 `_ENV` )。'
+DIAG_UNDEF_FENV_CHILD =
+'未定義的變數 `{}`(處於模組中)。'
+DIAG_GLOBAL_IN_NIL_ENV =
+'不能使用全域變數(`_ENV`被置為了`nil`)。'
DIAG_GLOBAL_IN_NIL_FENV =
-'不能使用全域變數(模組被置為了`nil`)。 '
-DIAG_UNUSED_LABEL =
-'未使用的標籤`{}`。 '
-DIAG_UNUSED_FUNCTION =
-'未使用的函式。 '
-DIAG_UNUSED_VARARG =
-'未使用的不定參數。 '
-DIAG_REDEFINED_LOCAL =
-'重定義區域變數`{}`。 '
-DIAG_DUPLICATE_INDEX =
-'重複的索引`{}`。 '
-DIAG_DUPLICATE_METHOD =
-'重複的方法`{}`。 '
-DIAG_PREVIOUS_CALL =
-'會被解釋為`{}{}`。你可能需要加一個`;`。 '
-DIAG_PREFIELD_CALL =
-'會被解釋為`{}{}`。你可能需要加一個`,`或`;`。 '
-DIAG_OVER_MAX_ARGS =
-'函式只接收{:d} 個參數,但你傳了{:d} 個。 '
-DIAG_OVER_MAX_VALUES =
-'只有{} 個變數,但你設定了{} 個值。 '
-DIAG_AMBIGUITY_1 =
-'會優先運算`{}`,你可能需要加個括號。 '
-DIAG_LOWERCASE_GLOBAL =
-'首字母小寫的全域變數,你是否漏了`local` 或是有拼寫錯誤? '
-DIAG_EMPTY_BLOCK =
+'不能使用全域變數(模組被置為了`nil`)。'
+DIAG_UNUSED_LABEL =
+'未使用的標籤 `{}`。'
+DIAG_UNUSED_FUNCTION =
+'未使用的函式。'
+DIAG_UNUSED_VARARG =
+'未使用的不定參數。'
+DIAG_REDEFINED_LOCAL =
+'重定義局部變數 `{}`。'
+DIAG_DUPLICATE_INDEX =
+'重複的索引 `{}`。'
+DIAG_DUPLICATE_METHOD =
+'重複的方法 `{}`。'
+DIAG_PREVIOUS_CALL =
+'會被直譯為 `{}{}`。你可能需要加一個 `;`。'
+DIAG_PREFIELD_CALL =
+'會被直譯為 `{}{}`。你可能需要加一個`,`或`;`。'
+DIAG_OVER_MAX_ARGS =
+'函式只接收 {:d} 個參數,但你傳了 {:d} 個。'
+DIAG_MISS_ARGS = -- TODO: need translate!
+'the function received at least {:d} arguments, but got {:d}.'
+DIAG_OVER_MAX_VALUES =
+'只有 {} 個變數,但你設定了 {} 個值。'
+DIAG_AMBIGUITY_1 =
+'會優先運算 `{}`,你可能需要加個括號。'
+DIAG_LOWERCASE_GLOBAL =
+'首字母小寫的全域變數,你是否漏了 `local` 或是有拼寫錯誤?'
+DIAG_EMPTY_BLOCK =
'空程式碼區塊'
-DIAG_DIAGNOSTICS =
+DIAG_DIAGNOSTICS =
'Lua 診斷'
-DIAG_SYNTAX_CHECK =
+DIAG_SYNTAX_CHECK =
'Lua 語法檢查'
-DIAG_NEED_VERSION =
-'在{} 中是合法的,目前為{}'
-DIAG_DEFINED_VERSION =
-'在{} 中有定義,目前為{}'
-DIAG_DEFINED_CUSTOM =
-'在{} 中有定義'
-DIAG_DUPLICATE_CLASS =
-'重複定義的Class `{}`。 '
-DIAG_UNDEFINED_CLASS =
-'未定義的Class `{}`。 '
-DIAG_CYCLIC_EXTENDS =
-'循環繼承。 '
-DIAG_INEXISTENT_PARAM =
-'不存在的參數。 '
-DIAG_DUPLICATE_PARAM =
-'重複的參數。 '
-DIAG_NEED_CLASS =
-'需要先定義Class 。 '
+DIAG_NEED_VERSION =
+'在 {} 中是合法的,目前為 {}'
+DIAG_DEFINED_VERSION =
+'在 {} 中有定義,目前為 {}'
+DIAG_DEFINED_CUSTOM =
+'在 {} 中有定義'
+DIAG_DUPLICATE_CLASS =
+'重複定義的 Class `{}`。'
+DIAG_UNDEFINED_CLASS =
+'未定義的 Class `{}`。'
+DIAG_CYCLIC_EXTENDS =
+'循環繼承。'
+DIAG_INEXISTENT_PARAM =
+'不存在的參數。'
+DIAG_DUPLICATE_PARAM =
+'重複的參數。'
+DIAG_NEED_CLASS =
+'需要先定義 Class 。'
DIAG_DUPLICATE_SET_FIELD=
-'重複定義的欄位`{}`。 '
-DIAG_SET_CONST =
-'不能對常數賦值。 '
-DIAG_SET_FOR_STATE =
-'修改了循環變數。 '
-DIAG_CODE_AFTER_BREAK =
-'無法執行到`break` 後的程式碼。 '
+'重複定義的欄位 `{}`。'
+DIAG_SET_CONST =
+'不能對常數賦值。'
+DIAG_SET_FOR_STATE =
+'修改了循環變數。'
+DIAG_CODE_AFTER_BREAK =
+'無法執行到 `break` 後的程式碼。'
DIAG_UNBALANCED_ASSIGNMENTS =
-'由於值的數量不夠而被賦值為了`nil` 。在Lua中, `x, y = 1` 等價於`x, y = 1, nil` 。 '
-DIAG_REQUIRE_LIKE =
-'你可以在設定中將`{}` 視為`require`。 '
-DIAG_COSE_NON_OBJECT =
-'無法close 此類型的值。 (除非給此類型設定`__close` 元方法)'
-DIAG_COUNT_DOWN_LOOP =
-'你的意思是`{}` 嗎? '
-DIAG_UNKNOWN =
-'無法推測出類型。 '
-DIAG_DEPRECATED =
-'已廢棄。 '
+'由於值的數量不夠而被賦值為了 `nil` 。在Lua中, `x, y = 1` 等價於 `x, y = 1, nil` 。'
+DIAG_REQUIRE_LIKE =
+'你可以在設定中將 `{}` 視為 `require`。'
+DIAG_COSE_NON_OBJECT =
+'無法 close 此類型的值。(除非給此類型設定 `__close` 元方法)'
+DIAG_COUNT_DOWN_LOOP =
+'你的意思是 `{}` 嗎?'
+DIAG_UNKNOWN =
+'無法推測出類型。'
+DIAG_DEPRECATED =
+'已廢棄。'
DIAG_DIFFERENT_REQUIRES =
-'使用了不同的名字require 了同一個檔案。 '
-DIAG_REDUNDANT_RETURN =
-'冗餘回傳。 '
-DIAG_AWAIT_IN_SYNC =
-'只能在標記為異步的函式中呼叫異步函式。 '
-DIAG_NOT_YIELDABLE =
-'此函式的第{} 個參數沒有被標記為可讓出,但是傳入了異步函式。 (使用`---@param name async fun()` 來標記為可讓出)'
-DIAG_DISCARD_RETURNS =
-'不能丟棄此函式的回傳值。 '
-DIAG_CIRCLE_DOC_CLASS =
-'循環繼承的類。 '
-DIAG_DOC_FIELD_NO_CLASS =
-'欄位必須定義在類別之後。 '
-DIAG_DUPLICATE_DOC_CLASS =
-'重複定義的類別`{}`。 '
-DIAG_DUPLICATE_DOC_FIELD =
-'重複定義的欄位`{}`。 '
-DIAG_DUPLICATE_DOC_PARAM =
-'重複指向的參數`{}`。 '
-DIAG_UNDEFINED_DOC_CLASS =
-'未定義的類別`{}`。 '
-DIAG_UNDEFINED_DOC_NAME =
-'未定義的類型或別名`{}`。 '
-DIAG_UNDEFINED_DOC_PARAM =
-'指向了未定義的參數`{}`。 '
-DIAG_UNKNOWN_DIAG_CODE =
-'未知的診斷代碼`{}`。 '
+'使用了不同的名字 require 了同一個檔案。'
+DIAG_REDUNDANT_RETURN =
+'冗餘回傳。'
+DIAG_AWAIT_IN_SYNC =
+'只能在標記為異步的函式中呼叫異步函式。'
+DIAG_NOT_YIELDABLE =
+'此函式的第 {} 個參數沒有被標記為可讓出,但是傳入了異步函式。(使用 `---@param name async fun()` 來標記為可讓出)'
+DIAG_DISCARD_RETURNS =
+'不能丟棄此函式的回傳值。'
+DIAG_NEED_CHECK_NIL = -- TODO: need translate!
+'Need check nil.'
+DIAG_CIRCLE_DOC_CLASS =
+'循環繼承的類別。'
+DIAG_DOC_FIELD_NO_CLASS =
+'欄位必須定義在類別之後。'
+DIAG_DUPLICATE_DOC_CLASS =
+'重複定義的類別 `{}`。'
+DIAG_DUPLICATE_DOC_FIELD =
+'重複定義的欄位 `{}`。'
+DIAG_DUPLICATE_DOC_PARAM =
+'重複指向的參數 `{}`。'
+DIAG_UNDEFINED_DOC_CLASS =
+'未定義的類別 `{}`。'
+DIAG_UNDEFINED_DOC_NAME =
+'未定義的類型或別名 `{}`。'
+DIAG_UNDEFINED_DOC_PARAM =
+'指向了未定義的參數 `{}`。'
+DIAG_UNKNOWN_DIAG_CODE =
+'未知的診斷代碼 `{}`。'
-MWS_NOT_SUPPORT =
+MWS_NOT_SUPPORT =
'{} 目前還不支援多工作目錄,我可能需要重啟才能支援新的工作目錄...'
-MWS_RESTART =
+MWS_RESTART =
'重啟'
-MWS_NOT_COMPLETE =
+MWS_NOT_COMPLETE =
'工作目錄還沒有準備好,你可以稍後再試一下...'
-MWS_COMPLETE =
+MWS_COMPLETE =
'工作目錄準備好了,你可以再試一下了...'
-MWS_MAX_PRELOAD =
-'預載入檔案數已達上限({}),你需要手動打開需要載入的檔案。 '
-MWS_UCONFIG_FAILED =
-'使用者配置儲存失敗。 '
-MWS_UCONFIG_UPDATED =
-'使用者配置已更新。 '
-MWS_WCONFIG_UPDATED =
-'工作區配置已更新。 '
+MWS_MAX_PRELOAD =
+'預載入檔案數已達上限({}),你需要手動打開需要載入的檔案。'
+MWS_UCONFIG_FAILED =
+'使用者設定檔儲存失敗。'
+MWS_UCONFIG_UPDATED =
+'使用者設定檔已更新。'
+MWS_WCONFIG_UPDATED =
+'工作區設定檔已更新。'
WORKSPACE_SKIP_LARGE_FILE =
'已跳過過大的檔案:{}。目前設定的大小限制為:{} KB,該檔案大小為:{} KB'
-WORKSPACE_LOADING =
+WORKSPACE_LOADING =
'正在載入工作目錄'
-WORKSPACE_DIAGNOSTIC =
+WORKSPACE_DIAGNOSTIC =
'正在對工作目錄進行診斷'
-WORKSPACE_SKIP_HUGE_FILE =
+WORKSPACE_SKIP_HUGE_FILE =
'出於效能考慮,已停止對此檔案解析:{}'
-PARSER_CRASH =
+PARSER_CRASH =
'語法解析崩潰了!遺言:{}'
-PARSER_UNKNOWN =
+PARSER_UNKNOWN =
'未知語法錯誤...'
-PARSER_MISS_NAME =
-'缺少名稱。 '
-PARSER_UNKNOWN_SYMBOL =
-'未知符號`{symbol}`。 '
-PARSER_MISS_SYMBOL =
-'缺少符號`{symbol}`。 '
-PARSER_MISS_ESC_X =
-'必須是2個16進制字元。 '
-PARSER_UTF8_SMALL =
-'至少有1個字元。 '
-PARSER_UTF8_MAX =
-'必須在{min} 與{max} 之間。 '
-PARSER_ERR_ESC =
-'錯誤的轉義符。 '
-PARSER_MUST_X16 =
-'必須是16進制字元。 '
-PARSER_MISS_EXPONENT =
-'缺少指數部分。 '
-PARSER_MISS_EXP =
-'缺少表達式。 '
-PARSER_MISS_FIELD =
-'缺少欄位/屬性名。 '
-PARSER_MISS_METHOD =
-'缺少方法名。 '
-PARSER_ARGS_AFTER_DOTS =
-'`...`必須是最後一個參數。 '
-PARSER_KEYWORD =
-'關鍵字無法作為名稱。 '
-PARSER_EXP_IN_ACTION =
-'該表達式不能作為語句。 '
-PARSER_BREAK_OUTSIDE =
-'`break`必須在循環內部。 '
+PARSER_MISS_NAME =
+'缺少名稱。'
+PARSER_UNKNOWN_SYMBOL =
+'未知符號`{symbol}`。'
+PARSER_MISS_SYMBOL =
+'缺少符號`{symbol}`。'
+PARSER_MISS_ESC_X =
+'必須是2個16進制字元。'
+PARSER_UTF8_SMALL =
+'至少有1個字元。'
+PARSER_UTF8_MAX =
+'必須在 {min} 與 {max} 之間。'
+PARSER_ERR_ESC =
+'錯誤的跳脫字元。'
+PARSER_MUST_X16 =
+'必須是16進制字元。'
+PARSER_MISS_EXPONENT =
+'缺少指數部分。'
+PARSER_MISS_EXP =
+'缺少表達式。'
+PARSER_MISS_FIELD =
+'缺少欄位/屬性名。'
+PARSER_MISS_METHOD =
+'缺少方法名。'
+PARSER_ARGS_AFTER_DOTS =
+'`...`必須是最後一個參數。'
+PARSER_KEYWORD =
+'關鍵字無法作為名稱。'
+PARSER_EXP_IN_ACTION =
+'該表達式不能作為語句。'
+PARSER_BREAK_OUTSIDE =
+'`break`必須在循環內部。'
PARSER_MALFORMED_NUMBER =
-'無法構成有效數字。 '
+'無法構成有效數字。'
PARSER_ACTION_AFTER_RETURN =
-'`return`之後不能再執行程式碼。 '
+'`return`之後不能再執行程式碼。'
PARSER_ACTION_AFTER_BREAK =
-'`break`之後不能再執行程式碼。 '
+'`break`之後不能再執行程式碼。'
PARSER_NO_VISIBLE_LABEL =
-'標籤`{label}`不可見。 '
-PARSER_REDEFINE_LABEL =
-'標籤`{label}`重複定義。 '
+'標籤`{label}`不可見。'
+PARSER_REDEFINE_LABEL =
+'標籤`{label}`重複定義。'
PARSER_UNSUPPORT_SYMBOL =
-'{version} 不支援該符號。 '
-PARSER_UNEXPECT_DOTS =
-'`...`只能在不定參函式中使用。 '
-PARSER_UNEXPECT_SYMBOL =
-'未知的符號`{symbol}` 。 '
-PARSER_UNKNOWN_TAG =
-'不支援的屬性。 '
-PARSER_MULTI_TAG =
-'只能設定一個屬性。 '
+'{version} 不支援該符號。'
+PARSER_UNEXPECT_DOTS =
+'`...`只能在不定參函式中使用。'
+PARSER_UNEXPECT_SYMBOL =
+'未知的符號 `{symbol}` 。'
+PARSER_UNKNOWN_TAG =
+'不支援的屬性。'
+PARSER_MULTI_TAG =
+'只能設定一個屬性。'
PARSER_UNEXPECT_LFUNC_NAME =
-'區域函式只能使用標識符作為名稱。 '
+'局部函式只能使用識別符作為名稱。'
PARSER_UNEXPECT_EFUNC_NAME =
-'函式作為表達式時不能命名。 '
+'函式作為表達式時不能命名。'
PARSER_ERR_LCOMMENT_END =
-'應使用`{symbol}`來關閉多行註解。 '
+'應使用`{symbol}`來關閉多行註解。'
PARSER_ERR_C_LONG_COMMENT =
-'Lua應使用`--[[ ]]`來進行多行註解。 '
-PARSER_ERR_LSTRING_END =
-'應使用`{symbol}`來關閉長字串。 '
+'Lua應使用`--[[ ]]`來進行多行註解。'
+PARSER_ERR_LSTRING_END =
+'應使用`{symbol}`來關閉長字串。'
PARSER_ERR_ASSIGN_AS_EQ =
-'應使用`=`來進行賦值操作。 '
+'應使用`=`來進行賦值操作。'
PARSER_ERR_EQ_AS_ASSIGN =
-'應使用`==`來進行等於判斷。 '
-PARSER_ERR_UEQ =
-'應使用`~=`來進行不等於判斷。 '
-PARSER_ERR_THEN_AS_DO =
-'應使用`then`。 '
-PARSER_ERR_DO_AS_THEN =
-'應使用`do`。 '
-PARSER_MISS_END =
-'缺少對應的`end`。 '
+'應使用`==`來進行等於判斷。'
+PARSER_ERR_UEQ =
+'應使用`~=`來進行不等於判斷。'
+PARSER_ERR_THEN_AS_DO =
+'應使用`then`。'
+PARSER_ERR_DO_AS_THEN =
+'應使用`do`。'
+PARSER_MISS_END =
+'缺少對應的`end`。'
PARSER_ERR_COMMENT_PREFIX =
-'Lua應使用`--`來進行註解。 '
+'Lua應使用`--`來進行註解。'
PARSER_MISS_SEP_IN_TABLE =
-'需要用`,`或`;`進行分割。 '
-PARSER_SET_CONST =
-'不能對常數賦值。 '
-PARSER_UNICODE_NAME =
-'包含了Unicode 字元。 '
+'需要用`,`或`;`進行分割。'
+PARSER_SET_CONST =
+'不能對常數賦值。'
+PARSER_UNICODE_NAME =
+'包含了 Unicode 字元。'
PARSER_ERR_NONSTANDARD_SYMBOL =
-'Lua中應使用符號`{symbol}`。 '
+'Lua中應使用符號 `{symbol}`。'
PARSER_MISS_SPACE_BETWEEN =
'符號之間必須保留空格'
PARSER_INDEX_IN_FUNC_NAME =
-'命名函式的名稱中不能使用`[name]` 形式。 '
-PARSER_UNKNOWN_ATTRIBUTE =
-'區域變數屬性應該是`const` 或`close`'
-PARSER_LUADOC_MISS_CLASS_NAME =
-'缺少類別名稱。 '
-PARSER_LUADOC_MISS_EXTENDS_SYMBOL =
-'缺少符號`:`。 '
-PARSER_LUADOC_MISS_CLASS_EXTENDS_NAME =
-'缺少要繼承的類名稱。 '
-PARSER_LUADOC_MISS_SYMBOL =
-'缺少符號`{symbol}`。 '
-PARSER_LUADOC_MISS_ARG_NAME =
-'缺少參數名稱。 '
-PARSER_LUADOC_MISS_TYPE_NAME =
-'缺少類型名。 '
-PARSER_LUADOC_MISS_ALIAS_NAME =
-'缺少別名。 '
-PARSER_LUADOC_MISS_ALIAS_EXTENDS =
-'缺少別名定義。 '
-PARSER_LUADOC_MISS_PARAM_NAME =
-'缺少要指向的參數名稱。 '
-PARSER_LUADOC_MISS_PARAM_EXTENDS =
-'缺少參數的類型定義。 '
-PARSER_LUADOC_MISS_FIELD_NAME =
-'缺少欄位名稱。 '
-PARSER_LUADOC_MISS_FIELD_EXTENDS =
-'缺少欄位的類型定義。 '
-PARSER_LUADOC_MISS_GENERIC_NAME =
-'缺少泛型名稱。 '
+'命名函式的名稱中不能使用 `[name]` 形式。'
+PARSER_UNKNOWN_ATTRIBUTE =
+'局部變數屬性應該是 `const` 或 `close`'
+PARSER_LUADOC_MISS_CLASS_NAME =
+'缺少類別名稱。'
+PARSER_LUADOC_MISS_EXTENDS_SYMBOL =
+'缺少符號 `:`。'
+PARSER_LUADOC_MISS_CLASS_EXTENDS_NAME =
+'缺少要繼承的類別名稱。'
+PARSER_LUADOC_MISS_SYMBOL =
+'缺少符號 `{symbol}`。'
+PARSER_LUADOC_MISS_ARG_NAME =
+'缺少參數名稱。'
+PARSER_LUADOC_MISS_TYPE_NAME =
+'缺少類型名。'
+PARSER_LUADOC_MISS_ALIAS_NAME =
+'缺少別名。'
+PARSER_LUADOC_MISS_ALIAS_EXTENDS =
+'缺少別名定義。'
+PARSER_LUADOC_MISS_PARAM_NAME =
+'缺少要指向的參數名稱。'
+PARSER_LUADOC_MISS_PARAM_EXTENDS =
+'缺少參數的類型定義。'
+PARSER_LUADOC_MISS_FIELD_NAME =
+'缺少欄位名稱。'
+PARSER_LUADOC_MISS_FIELD_EXTENDS =
+'缺少欄位的類型定義。'
+PARSER_LUADOC_MISS_GENERIC_NAME =
+'缺少泛型名稱。'
PARSER_LUADOC_MISS_GENERIC_EXTENDS_NAME =
-'缺少泛型要繼承的類別名稱。 '
-PARSER_LUADOC_MISS_VARARG_TYPE =
-'缺少不定參的類型定義。 '
-PARSER_LUADOC_MISS_FUN_AFTER_OVERLOAD =
-'缺少關鍵字`fun`。 '
-PARSER_LUADOC_MISS_CATE_NAME =
-'缺少文件類型名稱。 '
-PARSER_LUADOC_MISS_DIAG_MODE =
+'缺少泛型要繼承的類別名稱。'
+PARSER_LUADOC_MISS_VARARG_TYPE =
+'缺少不定參的類型定義。'
+PARSER_LUADOC_MISS_FUN_AFTER_OVERLOAD =
+'缺少關鍵字 `fun`。'
+PARSER_LUADOC_MISS_CATE_NAME =
+'缺少文件類型名稱。'
+PARSER_LUADOC_MISS_DIAG_MODE =
'缺少診斷模式'
-PARSER_LUADOC_ERROR_DIAG_MODE =
+PARSER_LUADOC_ERROR_DIAG_MODE =
'診斷模式不正確'
+PARSER_LUADOC_MISS_LOCAL_NAME = -- TODO: need translate!
+'<local name> expected.'
-SYMBOL_ANONYMOUS =
+SYMBOL_ANONYMOUS =
'<匿名函式>'
-HOVER_VIEW_DOCUMENTS =
+HOVER_VIEW_DOCUMENTS =
'查看文件'
-HOVER_DOCUMENT_LUA51 = -- TODO: need translate!
+HOVER_DOCUMENT_LUA51 =
'http://www.lua.org/manual/5.1/manual.html#{}'
-HOVER_DOCUMENT_LUA52 = -- TODO: need translate!
+HOVER_DOCUMENT_LUA52 =
'http://www.lua.org/manual/5.2/manual.html#{}'
-HOVER_DOCUMENT_LUA53 =
+HOVER_DOCUMENT_LUA53 =
'http://cloudwu.github.io/lua53doc/manual.html#{}'
-HOVER_DOCUMENT_LUA54 = -- TODO: need translate!
+HOVER_DOCUMENT_LUA54 =
'http://www.lua.org/manual/5.4/manual.html#{}'
-HOVER_DOCUMENT_LUAJIT = -- TODO: need translate!
+HOVER_DOCUMENT_LUAJIT =
'http://www.lua.org/manual/5.1/manual.html#{}'
-HOVER_NATIVE_DOCUMENT_LUA51 = -- TODO: need translate!
+HOVER_NATIVE_DOCUMENT_LUA51 =
'command:extension.lua.doc?["en-us/51/manual.html/{}"]'
-HOVER_NATIVE_DOCUMENT_LUA52 = -- TODO: need translate!
+HOVER_NATIVE_DOCUMENT_LUA52 =
'command:extension.lua.doc?["en-us/52/manual.html/{}"]'
-HOVER_NATIVE_DOCUMENT_LUA53 =
+HOVER_NATIVE_DOCUMENT_LUA53 =
'command:extension.lua.doc?["zh-cn/53/manual.html/{}"]'
-HOVER_NATIVE_DOCUMENT_LUA54 = -- TODO: need translate!
+HOVER_NATIVE_DOCUMENT_LUA54 =
'command:extension.lua.doc?["en-us/54/manual.html/{}"]'
-HOVER_NATIVE_DOCUMENT_LUAJIT = -- TODO: need translate!
+HOVER_NATIVE_DOCUMENT_LUAJIT =
'command:extension.lua.doc?["en-us/51/manual.html/{}"]'
-HOVER_MULTI_PROTOTYPE =
+HOVER_MULTI_PROTOTYPE =
'({} 個原型)'
-HOVER_STRING_BYTES =
+HOVER_STRING_BYTES =
'{} 個位元組'
-HOVER_STRING_CHARACTERS =
+HOVER_STRING_CHARACTERS =
'{} 個位元組,{} 個字元'
-HOVER_MULTI_DEF_PROTO =
+HOVER_MULTI_DEF_PROTO =
'({} 個定義,{} 個原型)'
HOVER_MULTI_PROTO_NOT_FUNC =
'({} 個非函式定義)'
-HOVER_USE_LUA_PATH =
+HOVER_USE_LUA_PATH =
'(搜尋路徑: `{}`)'
-HOVER_EXTENDS =
-'展開為{}'
-HOVER_TABLE_TIME_UP =
-'出於效能考慮,已禁用了部分類型推斷。 '
-HOVER_WS_LOADING =
+HOVER_EXTENDS =
+'展開為 {}'
+HOVER_TABLE_TIME_UP =
+'出於效能考慮,已禁用了部分類型推斷。'
+HOVER_WS_LOADING =
'正在載入工作目錄:{} / {}'
-HOVER_AWAIT_TOOLTIP = -- TODO: need translate!
-'正在调用异步函数,可能会让出当前协程'
+HOVER_AWAIT_TOOLTIP =
+'正在呼叫異步函式,可能會讓出目前共常式'
-ACTION_DISABLE_DIAG =
-'在工作區禁用診斷({})。 '
-ACTION_MARK_GLOBAL =
-'標記`{}` 為已定義的全域變數。 '
-ACTION_REMOVE_SPACE =
-'清除所有後置空格。 '
-ACTION_ADD_SEMICOLON =
-'添加`;` 。 '
-ACTION_ADD_BRACKETS =
-'添加括號。 '
-ACTION_RUNTIME_VERSION =
-'修改執行版本為{} 。 '
-ACTION_OPEN_LIBRARY =
-'載入{} 中的全域變數。 '
-ACTION_ADD_DO_END =
-'添加`do ... end` 。 '
+ACTION_DISABLE_DIAG =
+'在工作區禁用診斷 ({})。'
+ACTION_MARK_GLOBAL =
+'標記 `{}` 為已定義的全域變數。'
+ACTION_REMOVE_SPACE =
+'清除所有後置空格。'
+ACTION_ADD_SEMICOLON =
+'添加 `;` 。'
+ACTION_ADD_BRACKETS =
+'添加括號。'
+ACTION_RUNTIME_VERSION =
+'修改執行版本為 {} 。'
+ACTION_OPEN_LIBRARY =
+'載入 {} 中的全域變數。'
+ACTION_ADD_DO_END =
+'添加 `do ... end` 。'
ACTION_FIX_LCOMMENT_END =
-'改用正確的多行註解關閉符號。 '
+'改用正確的多行註解關閉符號。'
ACTION_ADD_LCOMMENT_END =
-'關閉多行註解。 '
+'關閉多行註解。'
ACTION_FIX_C_LONG_COMMENT =
-'修改為Lua 的多行註解格式。 '
-ACTION_FIX_LSTRING_END =
-'改用正確的長字串關閉符號。 '
-ACTION_ADD_LSTRING_END =
-'關閉長字串。 '
+'修改為 Lua 的多行註解格式。'
+ACTION_FIX_LSTRING_END =
+'改用正確的長字串關閉符號。'
+ACTION_ADD_LSTRING_END =
+'關閉長字串。'
ACTION_FIX_ASSIGN_AS_EQ =
-'改為`=` 。 '
+'改為 `=` 。'
ACTION_FIX_EQ_AS_ASSIGN =
-'改為`==` 。 '
-ACTION_FIX_UEQ =
-'改為`~=` 。 '
-ACTION_FIX_THEN_AS_DO =
-'改為`then` 。 '
-ACTION_FIX_DO_AS_THEN =
-'改為`do` 。 '
-ACTION_ADD_END =
-'添加`end` (根據縮排推測添加位置)。 '
+'改為 `==` 。'
+ACTION_FIX_UEQ =
+'改為 `~=` 。'
+ACTION_FIX_THEN_AS_DO =
+'改為 `then` 。'
+ACTION_FIX_DO_AS_THEN =
+'改為 `do` 。'
+ACTION_ADD_END =
+'添加 `end` (根據縮排推測添加位置)。'
ACTION_FIX_COMMENT_PREFIX =
-'改為`--` 。 '
+'改為 `--` 。'
ACTION_FIX_NONSTANDARD_SYMBOL =
-'改為`{symbol}`'
+'改為 `{symbol}`'
ACTION_RUNTIME_UNICODE_NAME =
-'允許使用Unicode 字元。 '
-ACTION_SWAP_PARAMS =
-'將其改為`{node}` 的第{index} 個參數'
+'允許使用 Unicode 字元。'
+ACTION_SWAP_PARAMS =
+'將其改為 `{node}` 的第 {index} 個參數'
ACTION_FIX_INSERT_SPACE =
'插入空格'
-ACTION_JSON_TO_LUA =
-'把JSON 轉成Lua'
+ACTION_JSON_TO_LUA =
+'把 JSON 轉成 Lua'
ACTION_DISABLE_DIAG_LINE=
-'在此行禁用診斷({})。 '
+'在此行禁用診斷 ({})。'
ACTION_DISABLE_DIAG_FILE=
-'在此檔案禁用診斷({})。 '
-ACTION_MARK_ASYNC =
-'將目前函式標記為異步。 '
+'在此檔案禁用診斷 ({})。'
+ACTION_MARK_ASYNC =
+'將目前函式標記為異步。'
-COMMAND_DISABLE_DIAG =
+COMMAND_DISABLE_DIAG =
'禁用診斷'
-COMMAND_MARK_GLOBAL =
+COMMAND_MARK_GLOBAL =
'標記全域變數'
-COMMAND_REMOVE_SPACE =
+COMMAND_REMOVE_SPACE =
'清除所有後置空格'
-COMMAND_ADD_BRACKETS =
+COMMAND_ADD_BRACKETS =
'添加括號'
-COMMAND_RUNTIME_VERSION =
+COMMAND_RUNTIME_VERSION =
'修改執行版本'
-COMMAND_OPEN_LIBRARY =
+COMMAND_OPEN_LIBRARY =
'載入第三方庫中的全域變數'
-COMMAND_UNICODE_NAME =
-'允許使用Unicode 字元'
-COMMAND_JSON_TO_LUA =
-'JSON 轉Lua'
+COMMAND_UNICODE_NAME =
+'允許使用 Unicode 字元'
+COMMAND_JSON_TO_LUA =
+'JSON 轉 Lua'
COMMAND_JSON_TO_LUA_FAILED =
-'JSON 轉Lua 失敗:{}'
+'JSON 轉 Lua 失敗:{}'
-COMPLETION_IMPORT_FROM =
-'從{} 中導入'
-COMPLETION_DISABLE_AUTO_REQUIRE =
+COMPLETION_IMPORT_FROM =
+'從 {} 中導入'
+COMPLETION_DISABLE_AUTO_REQUIRE =
'禁用自動require'
-COMPLETION_ASK_AUTO_REQUIRE =
-'在檔案頂部添加程式碼require 此檔案? '
+COMPLETION_ASK_AUTO_REQUIRE =
+'在檔案頂部添加程式碼 require 此檔案?'
-DEBUG_MEMORY_LEAK =
-'{} 很抱歉發生了嚴重的記憶體漏失,語言服務即將重啟。 '
-DEBUG_RESTART_NOW =
+DEBUG_MEMORY_LEAK =
+'{} 很抱歉發生了嚴重的記憶體漏失,語言服務即將重啟。'
+DEBUG_RESTART_NOW =
'立即重啟'
-WINDOW_COMPILING =
+WINDOW_COMPILING =
'正在編譯'
-WINDOW_DIAGNOSING =
+WINDOW_DIAGNOSING =
'正在診斷'
-WINDOW_INITIALIZING =
+WINDOW_INITIALIZING =
'正在初始化...'
-WINDOW_PROCESSING_HOVER =
+WINDOW_PROCESSING_HOVER =
'正在處理懸浮提示...'
-WINDOW_PROCESSING_DEFINITION =
+WINDOW_PROCESSING_DEFINITION =
'正在處理轉到定義...'
-WINDOW_PROCESSING_REFERENCE =
+WINDOW_PROCESSING_REFERENCE =
'正在處理轉到引用...'
-WINDOW_PROCESSING_RENAME =
+WINDOW_PROCESSING_RENAME =
'正在處理重命名...'
-WINDOW_PROCESSING_COMPLETION =
+WINDOW_PROCESSING_COMPLETION =
'正在處理自動完成...'
-WINDOW_PROCESSING_SIGNATURE =
+WINDOW_PROCESSING_SIGNATURE =
'正在處理參數提示...'
-WINDOW_PROCESSING_SYMBOL =
+WINDOW_PROCESSING_SYMBOL =
'正在處理檔案符號...'
-WINDOW_PROCESSING_WS_SYMBOL =
+WINDOW_PROCESSING_WS_SYMBOL =
'正在處理工作區符號...'
-WINDOW_PROCESSING_SEMANTIC_FULL =
+WINDOW_PROCESSING_SEMANTIC_FULL =
'正在處理全量語義著色...'
WINDOW_PROCESSING_SEMANTIC_RANGE =
'正在處理差量語義著色...'
-WINDOW_PROCESSING_HINT =
+WINDOW_PROCESSING_HINT =
'正在處理內嵌提示...'
-WINDOW_INCREASE_UPPER_LIMIT =
+WINDOW_INCREASE_UPPER_LIMIT =
'增加上限'
-WINDOW_CLOSE =
+WINDOW_CLOSE =
'關閉'
-WINDOW_SETTING_WS_DIAGNOSTIC =
+WINDOW_SETTING_WS_DIAGNOSTIC =
'你可以在設定中延遲或禁用工作目錄診斷'
-WINDOW_DONT_SHOW_AGAIN =
+WINDOW_DONT_SHOW_AGAIN =
'不再提示'
-WINDOW_DELAY_WS_DIAGNOSTIC =
+WINDOW_DELAY_WS_DIAGNOSTIC =
'空閒時診斷(延遲{}秒)'
-WINDOW_DISABLE_DIAGNOSTIC =
+WINDOW_DISABLE_DIAGNOSTIC =
'禁用工作區診斷'
-WINDOW_LUA_STATUS_WORKSPACE =
+WINDOW_LUA_STATUS_WORKSPACE =
'工作區:{}'
-WINDOW_LUA_STATUS_CACHED_FILES =
+WINDOW_LUA_STATUS_CACHED_FILES =
'已快取檔案:{ast}/{max}'
-WINDOW_LUA_STATUS_MEMORY_COUNT =
+WINDOW_LUA_STATUS_MEMORY_COUNT =
'記憶體佔用:{mem:.f}M'
-WINDOW_LUA_STATUS_TIP =
+WINDOW_LUA_STATUS_TIP =
[[
這個圖標是貓,
不是狗也不是狐狸!
↓↓↓
]]
-WINDOW_LUA_STATUS_DIAGNOSIS_TITLE= -- TODO: need translate!
-'Perform workspace diagnosis'
-WINDOW_LUA_STATUS_DIAGNOSIS_MSG = -- TODO: need translate!
-'Do you want to perform workspace diagnosis?'
-WINDOW_APPLY_SETTING =
-'套用設定'
-WINDOW_CHECK_SEMANTIC =
-'如果你正在使用商城中的顏色主題,你可能需要同時修改`editor.semanticHighlighting.enabled` 選項為`true` 才會使語義著色生效。 '
-WINDOW_TELEMETRY_HINT =
-'請允許發送匿名的使用資料與錯誤報告,幫助我們進一步完善此延伸模組。在[此處](https://github.com/sumneko/lua-language-server/wiki/%E9%9A%90%E7%A7%81%E5%A3%B0%E6%98%8E)閱讀我們的隱私聲明。 '
-WINDOW_TELEMETRY_ENABLE =
+WINDOW_LUA_STATUS_DIAGNOSIS_TITLE=
+'進行工作區診斷'
+WINDOW_LUA_STATUS_DIAGNOSIS_MSG =
+'是否進行工作區診斷?'
+WINDOW_APPLY_SETTING =
+'應用設定'
+WINDOW_CHECK_SEMANTIC =
+'如果你正在使用市場中的顏色主題,你可能需要同時修改 `editor.semanticHighlighting.enabled` 選項為 `true` 才會使語義著色生效。'
+WINDOW_TELEMETRY_HINT =
+'請允許發送匿名的使用資料與錯誤報告,幫助我們進一步完善此延伸模組。在[此處](https://github.com/sumneko/lua-language-server/wiki/%E9%9A%90%E7%A7%81%E5%A3%B0%E6%98%8E)閲讀我們的隱私聲明。'
+WINDOW_TELEMETRY_ENABLE =
'允許'
-WINDOW_TELEMETRY_DISABLE =
+WINDOW_TELEMETRY_DISABLE =
'禁止'
WINDOW_CLIENT_NOT_SUPPORT_CONFIG =
-'你的客戶端不支援從服務側修改設定,請手動修改如下設定:'
+'你的客戶端不支援從伺服端修改設定,請手動修改如下設定:'
WINDOW_LCONFIG_NOT_SUPPORT_CONFIG=
'暫不支援自動修改本地設定,請手動修改如下設定:'
-WINDOW_MANUAL_CONFIG_ADD =
-'為`{key}` 添加值`{value:q}`;'
-WINDOW_MANUAL_CONFIG_SET =
-'將`{key}` 的值設定為`{value:q}`;'
-WINDOW_MANUAL_CONFIG_PROP =
-'將`{key}` 的屬性`{prop}` 設定為`{value:q}`;'
-WINDOW_APPLY_WHIT_SETTING =
-'套用並修改設定'
-WINDOW_APPLY_WHITOUT_SETTING =
-'套用但不修改設定'
-WINDOW_ASK_APPLY_LIBRARY =
-'是否需要將你的工作環境配置為`{}` ? '
-WINDOW_SEARCHING_IN_FILES =
+WINDOW_MANUAL_CONFIG_ADD =
+'為 `{key}` 添加值 `{value:q}`;'
+WINDOW_MANUAL_CONFIG_SET =
+'將 `{key}` 的值設定為 `{value:q}`;'
+WINDOW_MANUAL_CONFIG_PROP =
+'將 `{key}` 的屬性 `{prop}` 設定為 `{value:q}`;'
+WINDOW_APPLY_WHIT_SETTING =
+'應用並修改設定'
+WINDOW_APPLY_WHITOUT_SETTING =
+'應用但不修改設定'
+WINDOW_ASK_APPLY_LIBRARY =
+'是否需要將你的工作環境配置為 `{}` ?'
+WINDOW_SEARCHING_IN_FILES =
'正在檔案中搜尋...'
-CONFIG_LOAD_FAILED =
-'無法讀取設定檔:{}'
-CONFIG_LOAD_ERROR =
-'設定檔載入錯誤:{}'
-CONFIG_TYPE_ERROR =
-'設定檔必須是lua或json格式:{}'
+CONFIG_LOAD_FAILED =
+'無法讀取設定檔案:{}'
+CONFIG_LOAD_ERROR =
+'設定檔案載入錯誤:{}'
+CONFIG_TYPE_ERROR =
+'設定檔案必須是lua或json格式:{}'
-PLUGIN_RUNTIME_ERROR =
+PLUGIN_RUNTIME_ERROR =
[[
-延伸模組發生錯誤,請匯報給延伸模組作者。
+延伸模組發生錯誤,請彙報給延伸模組作者。
請在輸出或日誌中查看詳細訊息。
延伸模組路徑:{}
]]
-PLUGIN_TRUST_LOAD =
+PLUGIN_TRUST_LOAD =
[[
目前設定試圖載入位於此位置的延伸模組:{}
注意,惡意的延伸模組可能會危害您的電腦
]]
-PLUGIN_TRUST_YES =
+PLUGIN_TRUST_YES =
[[
信任並載入延伸模組
]]
-PLUGIN_TRUST_NO =
+PLUGIN_TRUST_NO =
[[
不要載入此延伸模組
]]
CLI_CHECK_ERROR_TYPE =
-'check 必須是一個字串,但是是一個{}'
+'check 必須是一個字串,但是是一個 {}'
CLI_CHECK_ERROR_URI =
-'check 必須是一個有效的URI,但是是{}'
+'check 必須是一個有效的 URI,但是是 {}'
CLI_CHECK_ERROR_LEVEL =
'checklevel 必須是這些值之一:{}'
CLI_CHECK_INITING =
@@ -531,4 +537,4 @@ CLI_CHECK_INITING =
CLI_CHECK_SUCCESS =
'診斷完成,沒有發現問題'
CLI_CHECK_RESULTS =
-'診斷完成,共有{} 個問題,請查看{}'
+'診斷完成,共有 {} 個問題,請查看 {}'
diff --git a/locale/zh-tw/setting.lua b/locale/zh-tw/setting.lua
index e5ce01af..fec64c9e 100644
--- a/locale/zh-tw/setting.lua
+++ b/locale/zh-tw/setting.lua
@@ -1,84 +1,84 @@
---@diagnostic disable: undefined-global
-config.runtime.version =
+config.runtime.version =
"Lua執行版本。"
-config.runtime.path =
+config.runtime.path =
[[
-當使用`require` 時,如何根據輸入的名字來查找檔案。
-此選項設定為`?/init.lua` 意味著當你輸入`require 'myfile'` 時,會從已載入的檔案中搜尋`{workspace}/myfile/init.lua`。
-當`runtime.pathStrict` 設定為`false` 時,還會嘗試搜尋`${workspace}/**/myfile/init.lua`。
-如果你想要載入工作區以外的檔案,你需要先設定`Lua.workspace.library`。
+當使用 `require` 時,如何根據輸入的名字來查找檔案。
+此選項設定為 `?/init.lua` 意味著當你輸入 `require 'myfile'` 時,會從已載入的檔案中搜尋 `{workspace}/myfile/init.lua`。
+當 `runtime.pathStrict` 設定為 `false` 時,還會嘗試搜尋 `${workspace}/**/myfile/init.lua`。
+如果你想要載入工作區以外的檔案,你需要先設定 `Lua.workspace.library`。
]]
-config.runtime.pathStrict =
-'啟用後`runtime.path` 將只搜尋第一層目錄,見`runtime.path` 的說明。 '
-config.runtime.special =
-[[將自定義全域變數視為一些特殊的內置變數,語言服務將提供特殊的支援。
-下面這個例子表示將`include` 視為`require` 。
+config.runtime.pathStrict =
+'啟用後 `runtime.path` 將只搜尋第一層目錄,見 `runtime.path` 的説明。'
+config.runtime.special =
+[[將自定義全域變數視為一些特殊的內建變數,語言服務將提供特殊的支援。
+下面這個例子表示將 `include` 視為 `require` 。
```json
"Lua.runtime.special" : {
"include" : "require"
}
```
]]
-config.runtime.unicodeName =
-"允許在名字中使用Unicode 字元。"
-config.runtime.nonstandardSymbol =
+config.runtime.unicodeName =
+"允許在名字中使用 Unicode 字元。"
+config.runtime.nonstandardSymbol =
"支援非標準的符號。請務必確認你的執行環境支援這些符號。"
-config.runtime.plugin =
-"延伸模組路徑,請查閱[文件](https://github.com/sumneko/lua-language-server/wiki/Plugin)了解用法。"
-config.runtime.fileEncoding =
-"檔案編碼,`ansi` 選項只在`Windows` 平台下有效。"
-config.runtime.builtin =
+config.runtime.plugin =
+"延伸模組路徑,請查閲[文件](https://github.com/sumneko/lua-language-server/wiki/Plugin)瞭解用法。"
+config.runtime.fileEncoding =
+"檔案編碼,`ansi` 選項只在 `Windows` 平台下有效。"
+config.runtime.builtin =
[[
-調整內置庫的啟用狀態,你可以根據實際執行環境禁用掉不存在的庫(或重新定義)。
+調整內建庫的啟用狀態,你可以根據實際執行環境禁用掉不存在的庫(或重新定義)。
* `default`: 表示庫會根據執行版本啟用或禁用
* `enable`: 總是啟用
* `disable`: 總是禁用
]]
-config.diagnostics.enable =
+config.diagnostics.enable =
"啟用診斷。"
-config.diagnostics.disable =
+config.diagnostics.disable =
"禁用的診斷(使用浮框括號內的程式碼)。"
-config.diagnostics.globals =
+config.diagnostics.globals =
"已定義的全域變數。"
-config.diagnostics.severity =
+config.diagnostics.severity =
"修改診斷等級。"
config.diagnostics.neededFileStatus =
[[
-* Opened: 只診斷打開的檔案
-* Any: 診斷任何檔案
+* Opened: 只診斷打開的檔案
+* Any: 診斷任何檔案
* Disable: 禁用此診斷
]]
config.diagnostics.workspaceDelay =
"進行工作區診斷的延遲(毫秒)。當你啟動工作區,或編輯了任意檔案後,將會在後台對整個工作區進行重新診斷。設定為負數可以禁用工作區診斷。"
-config.diagnostics.workspaceRate =
+config.diagnostics.workspaceRate =
"工作區診斷的執行速率(百分比)。降低該值會減少CPU佔用,但是也會降低工作區診斷的速度。你目前正在編輯的檔案的診斷總是全速完成,不受該選項影響。"
-config.diagnostics.libraryFiles =
-"如何診斷通過`Lua.workspace.library` 載入的檔案。"
-config.diagnostics.ignoredFiles =
+config.diagnostics.libraryFiles =
+"如何診斷通過 `Lua.workspace.library` 載入的檔案。"
+config.diagnostics.ignoredFiles =
"如何診斷被忽略的檔案。"
-config.diagnostics.files.Enable =
+config.diagnostics.files.Enable =
"總是診斷這些檔案。"
-config.diagnostics.files.Opened =
+config.diagnostics.files.Opened =
"只有打開這些檔案時才會診斷。"
-config.diagnostics.files.Disable =
+config.diagnostics.files.Disable =
"不診斷這些檔案。"
-config.workspace.ignoreDir =
-"忽略的檔案與目錄(使用`.gitignore` 語法)。"
+config.workspace.ignoreDir =
+"忽略的檔案與目錄(使用 `.gitignore` 語法)。"
config.workspace.ignoreSubmodules =
"忽略子模組。"
-config.workspace.useGitIgnore =
-"忽略`.gitignore` 中列舉的檔案。"
-config.workspace.maxPreload =
-"最大預載入檔案數。 "
-config.workspace.preloadFileSize =
-"預載入時跳過大小大於該值(KB)的檔案。 "
-config.workspace.library =
-"除了目前工作區以外,還會從哪些目錄中載入檔案。這些目錄中的檔案將被視作外部提供的程式碼庫,部分操作(如重命名欄位)不會修改這些檔案。 "
-config.workspace.checkThirdParty =
+config.workspace.useGitIgnore =
+"忽略 `.gitignore` 中列舉的檔案。"
+config.workspace.maxPreload =
+"最大預載入檔案數。"
+config.workspace.preloadFileSize =
+"預載入時跳過大小大於該值(KB)的檔案。"
+config.workspace.library =
+"除了目前工作區以外,還會從哪些目錄中載入檔案。這些目錄中的檔案將被視作外部提供的程式碼庫,部分操作(如重命名欄位)不會修改這些檔案。"
+config.workspace.checkThirdParty =
[[
-自動檢測與適配第三方庫,目前支援的庫為:
+自動檢測與適應第三方庫,目前支援的庫為:
* OpenResty
* Cocos4.0
@@ -87,161 +87,161 @@ config.workspace.checkThirdParty =
* skynet
* Jass
]]
-config.workspace.userThirdParty =
-'在這裡添加私有的第三方庫適配檔案路徑,請參考內置的[配置檔案路徑](https://github.com/sumneko/lua-language-server/tree/master/meta/3rd)'
-config.completion.enable =
+config.workspace.userThirdParty =
+'在這裡添加私有的第三方庫適應檔案路徑,請參考內建的[組態檔案路徑](https://github.com/sumneko/lua-language-server/tree/master/meta/3rd)'
+config.completion.enable =
'啟用自動完成。'
-config.completion.callSnippet =
+config.completion.callSnippet =
'顯示函式呼叫片段。'
-config.completion.callSnippet.Disable =
-"只顯示`函式名`。 "
-config.completion.callSnippet.Both =
-"顯示`函式名` 與`呼叫片段`。 "
-config.completion.callSnippet.Replace =
-"只顯示`呼叫片段`。 "
-config.completion.keywordSnippet =
+config.completion.callSnippet.Disable =
+"只顯示 `函式名`。"
+config.completion.callSnippet.Both =
+"顯示 `函式名` 與 `呼叫片段`。"
+config.completion.callSnippet.Replace =
+"只顯示 `呼叫片段`。"
+config.completion.keywordSnippet =
'顯示關鍵字語法片段'
config.completion.keywordSnippet.Disable =
-"只顯示`關鍵字`。 "
-config.completion.keywordSnippet.Both =
-"顯示`關鍵字` 與`語法片段`。 "
+"只顯示 `關鍵字`。"
+config.completion.keywordSnippet.Both =
+"顯示 `關鍵字` 與 `語法片段`。"
config.completion.keywordSnippet.Replace =
-"只顯示`語法片段`。 "
-config.completion.displayContext =
-"預覽建議的相關程式碼片段,可能可以幫助你了解這項建議的用法。設定的數字表示程式碼片段的截取行數,設定為`0`可以禁用此功能。 "
-config.completion.workspaceWord =
-"顯示的上下文單詞是否包含工作區中其他檔案的內容。 "
-config.completion.showWord =
-"在建議中顯示上下文單詞。 "
-config.completion.showWord.Enable =
-"總是在建議中顯示上下文單詞。 "
-config.completion.showWord.Fallback =
-"無法根據語義提供建議時才顯示上下文單詞。 "
-config.completion.showWord.Disable =
-"不顯示上下文單詞。 "
-config.completion.autoRequire =
-"輸入內容看起來是個檔名時,自動`require` 此檔案。 "
-config.completion.showParams =
-"在建議列表中顯示函式的參數訊息,函式擁有多個定義時會分開顯示。 "
-config.completion.requireSeparator =
-"`require` 時使用的分隔符。 "
-config.completion.postfix =
-"用於觸發後綴建議的符號。 "
-config.color.mode =
-"著色模式。 "
-config.color.mode.Semantic =
-"語義著色。你可能需要同時將`editor.semanticHighlighting.enabled` 設定為`true` 才能生效。 "
-config.color.mode.SemanticEnhanced =
-"增強的語義顏色。類似於`Semantic`,但會進行額外的分析(也會帶來額外的開銷)。 "
-config.color.mode.Grammar =
-"語法著色。 "
-config.semantic.enable =
-"啟用語義著色。你可能需要同時將`editor.semanticHighlighting.enabled` 設定為`true` 才能生效。 "
-config.semantic.variable =
-"對變數/欄位/參數進行語義著色。 "
-config.semantic.annotation =
-"對類型註解進行語義著色。 "
-config.semantic.keyword =
-"對關鍵字/字面常數/運算符進行語義著色。只有當你的編輯器無法進行語法著色時才需要啟用此功能。 "
-config.signatureHelp.enable =
-"啟用參數提示。 "
-config.hover.enable =
-"啟用懸浮提示。 "
-config.hover.viewString =
-"懸浮提示查看字串內容(僅當字面常數包含轉義符時)。 "
-config.hover.viewStringMax =
-"懸浮提示查看字串內容時的最大長度。 "
-config.hover.viewNumber =
-"懸浮提示查看數字內容(僅當字面常數不是十進制時)。 "
-config.hover.fieldInfer =
-"懸浮提示查看表時,會對錶的每個欄位進行類型推測,當類型推測的用時累計達到該設定值(毫秒)時,將跳過後續欄位的類型推測。 "
-config.hover.previewFields =
-"懸浮提示查看表時,限製表內欄位的最大預覽數量。 "
-config.hover.enumsLimit =
-"當值對應多個類型時,限制類型的顯示數量。 "
-config.develop.enable =
+"只顯示 `語法片段`。"
+config.completion.displayContext =
+"預覽建議的相關程式碼片段,可能可以幫助你瞭解這項建議的用法。設定的數字表示程式碼片段的截取行數,設定為`0`可以禁用此功能。"
+config.completion.workspaceWord =
+"顯示的上下文單詞是否包含工作區中其他檔案的內容。"
+config.completion.showWord =
+"在建議中顯示上下文單詞。"
+config.completion.showWord.Enable =
+"總是在建議中顯示上下文單詞。"
+config.completion.showWord.Fallback =
+"無法根據語義提供建議時才顯示上下文單詞。"
+config.completion.showWord.Disable =
+"不顯示上下文單詞。"
+config.completion.autoRequire =
+"輸入內容看起來是個檔名時,自動 `require` 此檔案。"
+config.completion.showParams =
+"在建議列表中顯示函式的參數訊息,函式擁有多個定義時會分開顯示。"
+config.completion.requireSeparator =
+"`require` 時使用的分隔符。"
+config.completion.postfix =
+"用於觸發後綴建議的符號。"
+config.color.mode =
+"著色模式。"
+config.color.mode.Semantic =
+"語義著色。你可能需要同時將 `editor.semanticHighlighting.enabled` 設定為 `true` 才能生效。"
+config.color.mode.SemanticEnhanced =
+"增強的語義顏色。 類似於`Semantic`,但會進行額外的分析(也會帶來額外的開銷)。"
+config.color.mode.Grammar =
+"語法著色。"
+config.semantic.enable =
+"啟用語義著色。你可能需要同時將 `editor.semanticHighlighting.enabled` 設定為 `true` 才能生效。"
+config.semantic.variable =
+"對變數/欄位/參數進行語義著色。"
+config.semantic.annotation =
+"對類型註解進行語義著色。"
+config.semantic.keyword =
+"對關鍵字/字面常數/運算符進行語義著色。只有當你的編輯器無法進行語法著色時才需要啟用此功能。"
+config.signatureHelp.enable =
+"啟用參數提示。"
+config.hover.enable =
+"啟用懸浮提示。"
+config.hover.viewString =
+"懸浮提示查看字串內容(僅當字面常數包含跳脫字元時)。"
+config.hover.viewStringMax =
+"懸浮提示查看字串內容時的最大長度。"
+config.hover.viewNumber =
+"懸浮提示查看數字內容(僅當字面常數不是十進制時)。"
+config.hover.fieldInfer =
+"懸浮提示查看表時,會對表的每個欄位進行類型推測,當類型推測的用時累計達到該設定值(毫秒)時,將跳過後續欄位的類型推測。"
+config.hover.previewFields =
+"懸浮提示查看表時,限制表內欄位的最大預覽數量。"
+config.hover.enumsLimit =
+"當值對應多個類型時,限制類型的顯示數量。"
+config.develop.enable =
'開發者模式。請勿開啟,會影響效能。'
-config.develop.debuggerPort =
+config.develop.debuggerPort =
'除錯器監聽埠。'
-config.develop.debuggerWait =
-'除錯器連接之前掛起。'
-config.intelliSense.searchDepth =
-'設定智慧感知的搜尋深度。增大該值可以增加準確度,但會降低效能。不同的項目對該設定的容忍度差異較大,請自己調整為合適的值。'
-config.intelliSense.fastGlobal =
-'在對全域變數進行補全,及查看`_G` 的懸浮提示時進行最佳化。這會略微降低類型推測的準確度,但是對於大量使用全域變數的項目會有大幅的效能提升。'
-config.window.statusBar =
+config.develop.debuggerWait =
+'除錯器連接之前懸置。'
+config.intelliSense.searchDepth =
+'設定智能感知的搜尋深度。增大該值可以增加準確度,但會降低效能。不同的項目對該設定的容忍度差異較大,請自己調整為合適的值。'
+config.intelliSense.fastGlobal =
+'在對全域變數進行補全,及查看 `_G` 的懸浮提示時進行最佳化。這會略微降低類型推測的準確度,但是對於大量使用全域變數的項目會有大幅的效能提升。'
+config.window.statusBar =
'在狀態欄顯示延伸模組狀態。'
-config.window.progressBar =
+config.window.progressBar =
'在狀態欄顯示進度條。'
-config.hint.enable =
+config.hint.enable =
'啟用內嵌提示。'
-config.hint.paramType =
+config.hint.paramType =
'在函式的參數位置提示類型。'
-config.hint.setType =
+config.hint.setType =
'在賦值操作位置提示類型。'
-config.hint.paramName =
+config.hint.paramName =
'在函式呼叫處提示參數名。'
-config.hint.paramName.All =
+config.hint.paramName.All =
'所有類型的參數均進行提示。'
-config.hint.paramName.Literal =
+config.hint.paramName.Literal =
'只有字面常數類型的參數進行提示。'
-config.hint.paramName.Disable =
+config.hint.paramName.Disable =
'禁用參數提示。'
-config.hint.arrayIndex =
-'在構造表時提示數組索引。'
-config.hint.arrayIndex.Enable =
-'所有的表中都提示數組索引。'
-config.hint.arrayIndex.Auto =
+config.hint.arrayIndex =
+'在構造表時提示陣列索引。'
+config.hint.arrayIndex.Enable =
+'所有的表中都提示陣列索引。'
+config.hint.arrayIndex.Auto =
'只有表大於3項,或者表是混合類型時才進行提示。'
-config.hint.arrayIndex.Disable =
-'禁用數組索引提示。'
-config.format.enable =
+config.hint.arrayIndex.Disable =
+'禁用陣列索引提示。'
+config.format.enable =
'啟用程式碼格式化程式。'
-config.telemetry.enable =
+config.telemetry.enable =
[[
-啟用遙測,通過網路發送你的編輯器訊息與錯誤日誌。在[此處](https://github.com/s umneko/lua-language-server/wiki/%E9%9A%90%E7%A7%81%E5%A3%B0%E6%98%8E)閱讀我們的隱私聲明。
+啟用遙測,通過網路發送你的編輯器訊息與錯誤日誌。在[此處](https://github.com/sumneko/lua-language-server/wiki/%E9%9A%90%E7%A7%81%E5%A3%B0%E6%98%8E)閲讀我們的隱私聲明。
]]
-config.misc.parameters =
-'VSCode中啟動語言服務時的[命令列參數](https://github.com/sumneko/lua-language-server/wiki/Command-line)。 '
-config.IntelliSense.traceLocalSet =
-'請查閱[文件](https://github.com/sumneko/lua-language-server/wiki/IntelliSense-optional-features)了解用法。 '
-config.IntelliSense.traceReturn =
-'請查閱[文件](https://github.com/sumneko/lua-language-server/wiki/IntelliSense-optional-features)了解用法。 '
-config.IntelliSense.traceBeSetted =
-'請查閱[文件](https://github.com/sumneko/lua-language-server/wiki/IntelliSense-optional-features)了解用法。 '
-config.IntelliSense.traceFieldInject =
-'請查閱[文件](https://github.com/sumneko/lua-language-server/wiki/IntelliSense-optional-features)了解用法。 '
-config.diagnostics['unused-local'] =
-'未使用的區域變數'
-config.diagnostics['unused-function'] =
+config.misc.parameters =
+'VSCode中啟動語言服務時的[命令列參數](https://github.com/sumneko/lua-language-server/wiki/Command-line)。'
+config.IntelliSense.traceLocalSet =
+'請查閲[文件](https://github.com/sumneko/lua-language-server/wiki/IntelliSense-optional-features)瞭解用法。'
+config.IntelliSense.traceReturn =
+'請查閲[文件](https://github.com/sumneko/lua-language-server/wiki/IntelliSense-optional-features)瞭解用法。'
+config.IntelliSense.traceBeSetted =
+'請查閲[文件](https://github.com/sumneko/lua-language-server/wiki/IntelliSense-optional-features)瞭解用法。'
+config.IntelliSense.traceFieldInject =
+'請查閲[文件](https://github.com/sumneko/lua-language-server/wiki/IntelliSense-optional-features)瞭解用法。'
+config.diagnostics['unused-local'] =
+'未使用的局部變數'
+config.diagnostics['unused-function'] =
'未使用的函式'
-config.diagnostics['undefined-global'] =
+config.diagnostics['undefined-global'] =
'未定義的全域變數'
-config.diagnostics['global-in-nil-env'] =
-'不能使用全域變數( `_ENV` 被設定為了`nil`)'
-config.diagnostics['unused-label'] =
+config.diagnostics['global-in-nil-env'] =
+'不能使用全域變數( `_ENV` 被設定為了 `nil`)'
+config.diagnostics['unused-label'] =
'未使用的標籤'
-config.diagnostics['unused-vararg'] =
+config.diagnostics['unused-vararg'] =
'未使用的不定參數'
-config.diagnostics['trailing-space'] =
+config.diagnostics['trailing-space'] =
'後置空格'
-config.diagnostics['redefined-local'] =
-'重複定義的區域變數'
-config.diagnostics['newline-call'] =
-'以`(` 開始的新行,在語法上被解析為了上一行的函式呼叫'
-config.diagnostics['newfield-call'] =
+config.diagnostics['redefined-local'] =
+'重複定義的局部變數'
+config.diagnostics['newline-call'] =
+'以 `(` 開始的新行,在語法上被解析為了上一行的函式呼叫'
+config.diagnostics['newfield-call'] =
'在字面常數表中,2行程式碼之間缺少分隔符,在語法上被解析為了一次索引操作'
-config.diagnostics['redundant-parameter'] =
+config.diagnostics['redundant-parameter'] =
'函式呼叫時,傳入了多餘的參數'
-config.diagnostics['ambiguity-1'] =
-'優先級歧義,如:`num or 0 + 1`,推測使用者的實際期望為`(num or 0) + 1` '
-config.diagnostics['lowercase-global'] =
+config.diagnostics['ambiguity-1'] =
+'優先級歧義,如:`num or 0 + 1`,推測使用者的實際期望為 `(num or 0) + 1` '
+config.diagnostics['lowercase-global'] =
'首字母小寫的全域變數定義'
-config.diagnostics['undefined-env-child'] =
+config.diagnostics['undefined-env-child'] =
'`_ENV` 被設定為了新的字面常數表,但是試圖獲取的全域變數不在這張表中'
-config.diagnostics['duplicate-index'] =
+config.diagnostics['duplicate-index'] =
'在字面常數表中重複定義了索引'
-config.diagnostics['empty-block'] =
+config.diagnostics['empty-block'] =
'空程式碼區塊'
-config.diagnostics['redundant-value'] =
-'賦值操作時,值的數量比被賦值的對像多'
+config.diagnostics['redundant-value'] =
+'賦值操作時,值的數量比被賦值的對象多'
diff --git a/make.lua b/make.lua
index 0b65c67c..7213547d 100644
--- a/make.lua
+++ b/make.lua
@@ -1,6 +1,4 @@
-local lm = require 'luamake'
-local platform = require 'bee.platform'
-local exe = platform.OS == 'Windows' and ".exe" or ""
+local lm = require 'luamake'
lm.bindir = "bin"
lm.c = lm.compiler == 'msvc' and 'c89' or 'c11'
@@ -11,35 +9,9 @@ lm.EXE_DIR = ""
local includeCodeFormat = true
-if platform.OS == 'macOS' then
- if lm.platform == nil then
- elseif lm.platform == "darwin-arm64" then
- lm.target = "arm64-apple-macos11"
- elseif lm.platform == "darwin-x64" then
- lm.target = "x86_64-apple-macos10.12"
- else
- error "unknown platform"
- end
-elseif platform.OS == 'Windows' then
- if lm.platform == nil then
- elseif lm.platform == "win32-ia32" then
- lm.arch = "x86"
- elseif lm.platform == "win32-x64" then
- lm.arch = "x86_64"
- else
- error "unknown platform"
- end
-elseif platform.OS == 'Linux' then
- if lm.platform == nil then
- elseif lm.platform == "linux-x64" then
- elseif lm.platform == "linux-arm64" then
- lm.cc = 'aarch64-linux-gnu-gcc'
- else
- error "unknown platform"
- end
-end
+require "make.detect_platform"
-lm:import "3rd/bee.lua/make.lua"
+lm:import "3rd/bee.lua"
lm:import "make/code_format.lua"
lm:source_set 'lpeglabel' {
@@ -80,8 +52,8 @@ lm:copy "copy_bootstrap" {
output = lm.bindir .. "/main.lua",
}
-lm:build 'copy_vcrt' {
- '$luamake', 'lua', 'make/copy_vcrt.lua', lm.bindir, lm.arch,
+lm:msvc_copy_vcrt 'copy_vcrt' {
+ output = lm.bindir,
}
lm:phony "all" {
@@ -96,46 +68,16 @@ lm:phony "all" {
}
}
-local function detectWindowsArch()
- if os.getenv "PROCESSOR_ARCHITECTURE" == "ARM64" then
- return "arm64"
- end
- if os.getenv "PROCESSOR_ARCHITECTURE" == "AMD64" or os.getenv "PROCESSOR_ARCHITEW6432" == "AMD64" then
- return "x64"
- end
- return "ia32"
-end
-
-local function detectPosixArch()
- local f <close> = assert(io.popen("uname -m", 'r'))
- return f:read 'l':lower()
-end
-
-local function detectArch()
- if platform.OS == 'Windows' then
- return detectWindowsArch()
- end
- return detectPosixArch()
-end
-
-local function targetPlatformArch()
- if lm.platform == nil then
- return detectArch()
- end
- return lm.platform:match "^[^-]*-(.*)$"
-end
-
-local notest = (platform.OS == 'macOS' or platform.OS == 'Linux')
- and targetPlatformArch() == "arm64"
- and detectArch() == "x86_64"
-
-if notest then
+if lm.notest then
lm:default {
"all",
}
return
end
+local platform = require 'bee.platform'
+local exe = platform.OS == 'Windows' and ".exe" or ""
+
lm:build "bee-test" {
lm.bindir .. "/lua-language-server" .. exe, "3rd/bee.lua/test/test.lua",
pool = "console",
diff --git a/make/bootstrap.lua b/make/bootstrap.lua
index 6c27cf95..00036f34 100644
--- a/make/bootstrap.lua
+++ b/make/bootstrap.lua
@@ -69,6 +69,9 @@ package.searchers[2] = function (name)
return err
end
local f = io.open(filename)
+ if not f then
+ return 'cannot open file:' .. filename
+ end
local buf = f:read '*a'
f:close()
local relative = filename:sub(1, #root) == root and filename:sub(#root + 2) or filename
diff --git a/make/copy_vcrt.lua b/make/copy_vcrt.lua
deleted file mode 100644
index bd08a9fa..00000000
--- a/make/copy_vcrt.lua
+++ /dev/null
@@ -1,6 +0,0 @@
-local output, arch = ...
-local fs = require 'bee.filesystem'
-require 'msvc'.copy_vcrt(
- arch == "x86" and 'x86' or 'x64',
- fs.current_path() / output
-)
diff --git a/make/detect_platform.lua b/make/detect_platform.lua
new file mode 100644
index 00000000..8dba298c
--- /dev/null
+++ b/make/detect_platform.lua
@@ -0,0 +1,64 @@
+local lm = require 'luamake'
+
+local platform = require 'bee.platform'
+
+if platform.OS == 'macOS' then
+ if lm.platform == nil then
+ elseif lm.platform == "darwin-arm64" then
+ lm.target = "arm64-apple-macos11"
+ elseif lm.platform == "darwin-x64" then
+ lm.target = "x86_64-apple-macos10.12"
+ else
+ error "unknown platform"
+ end
+elseif platform.OS == 'Windows' then
+ if lm.platform == nil then
+ elseif lm.platform == "win32-ia32" then
+ lm.arch = "x86"
+ elseif lm.platform == "win32-x64" then
+ lm.arch = "x86_64"
+ else
+ error "unknown platform"
+ end
+elseif platform.OS == 'Linux' then
+ if lm.platform == nil then
+ elseif lm.platform == "linux-x64" then
+ elseif lm.platform == "linux-arm64" then
+ lm.cc = 'aarch64-linux-gnu-gcc'
+ else
+ error "unknown platform"
+ end
+end
+
+local function detectWindowsArch()
+ if os.getenv "PROCESSOR_ARCHITECTURE" == "ARM64" then
+ return "arm64"
+ end
+ if os.getenv "PROCESSOR_ARCHITECTURE" == "AMD64" or os.getenv "PROCESSOR_ARCHITEW6432" == "AMD64" then
+ return "x64"
+ end
+ return "ia32"
+end
+
+local function detectPosixArch()
+ local f <close> = assert(io.popen("uname -m", 'r'))
+ return f:read 'l':lower()
+end
+
+local function detectArch()
+ if platform.OS == 'Windows' then
+ return detectWindowsArch()
+ end
+ return detectPosixArch()
+end
+
+local function targetPlatformArch()
+ if lm.platform == nil then
+ return detectArch()
+ end
+ return lm.platform:match "^[^-]*-(.*)$"
+end
+
+if not lm.notest then
+ lm.notest = (platform.OS ~= 'Windows' and targetPlatformArch() ~= detectArch())
+end
diff --git a/meta/3rd/OpenResty/library/ngx.lua b/meta/3rd/OpenResty/library/ngx.lua
index f8f5eabf..b85593bf 100644
--- a/meta/3rd/OpenResty/library/ngx.lua
+++ b/meta/3rd/OpenResty/library/ngx.lua
@@ -2134,7 +2134,7 @@ function ngx.req.get_post_args(max_args) end
---
--- Removing the `max_args` cap is strongly discouraged.
---
----@param max_args number
+---@param max_args? number
---@return table args
---@return string|'"truncated"' error
function ngx.req.get_uri_args(max_args) end
@@ -2228,8 +2228,8 @@ function ngx.req.get_uri_args(max_args) end
--- in "\x00foo" (maybe you want to set the 'binary' argument?)
---
---@param uri string
----@param jump boolean
----@param binary boolean
+---@param jump? boolean
+---@param binary? boolean
function ngx.req.set_uri(uri, jump, binary) end
--- Append new data chunk specified by the `data_chunk` argument onto the existing request body created by the `ngx.req.init_body` call.
@@ -2438,8 +2438,8 @@ function ngx.req.clear_header(header_name) end
---
--- The `__index` metamethod will not be added when the `raw` argument is set to `true`.
---
----@param max_headers number
----@param raw boolean
+---@param max_headers? number
+---@param raw? boolean
---@return table<string, string|string[]> headers
---@return string|'"truncated"' error
function ngx.req.get_headers(max_headers, raw) end
@@ -4211,8 +4211,8 @@ ngx.resp = {}
---
--- Note that a maximum of 100 response headers are parsed by default (including those with the same name) and that additional response headers are silently discarded to guard against potential denial of service attacks. When the limit is exceeded, it will return a second value which is the string `"truncated"`.
---
----@param max_headers number
----@param raw boolean
+---@param max_headers? number
+---@param raw? boolean
---@return table<string, string|string[]>
---@return string|'"truncated"' error
function ngx.resp.get_headers(max_headers, raw) end
diff --git a/meta/template/basic.lua b/meta/template/basic.lua
index aed3085e..b4913cc2 100644
--- a/meta/template/basic.lua
+++ b/meta/template/basic.lua
@@ -7,7 +7,7 @@ arg = {}
---#DES 'assert'
---@generic T
---@param v T
----@param message any
+---@param message? any
---@return T
function assert(v, message) end
@@ -83,8 +83,8 @@ function ipairs(t) end
---#DES 'load<5.1'
---@param func function
---@param chunkname? string
----@return function
----@return string error_message
+---@return function?
+---@return string? error_message
---@nodiscard
function load(func, chunkname) end
---#else
@@ -93,8 +93,8 @@ function load(func, chunkname) end
---@param chunkname? string
---@param mode? loadmode
---@param env? table
----@return function
----@return string error_message
+---@return function?
+---@return string? error_message
---@nodiscard
function load(chunk, chunkname, mode, env) end
---#end
@@ -102,8 +102,8 @@ function load(chunk, chunkname, mode, env) end
---#if VERSION <= 5.1 and not JIT then
---#DES 'loadfile'
---@param filename? string
----@return function
----@return string error_message
+---@return function?
+---@return string? error_message
---@nodiscard
function loadfile(filename) end
---#else
@@ -111,8 +111,8 @@ function loadfile(filename) end
---@param filename? string
---@param mode? loadmode
---@param env? table
----@return function
----@return string error_message
+---@return function?
+---@return string? error_message
---@nodiscard
function loadfile(filename, mode, env) end
---#end
@@ -121,8 +121,8 @@ function loadfile(filename, mode, env) end
---#DES 'loadstring'
---@param text string
---@param chunkname? string
----@return function
----@return string error_message
+---@return function?
+---@return string? error_message
---@nodiscard
function loadstring(text, chunkname) end
diff --git a/meta/template/debug.lua b/meta/template/debug.lua
index 6e74e1f6..f62aca6e 100644
--- a/meta/template/debug.lua
+++ b/meta/template/debug.lua
@@ -112,8 +112,8 @@ function debug.getupvalue(f, up) end
---#if VERSION >= 5.4 then
---#DES 'debug.getuservalue>5.4'
----@param u userdata
----@param n integer
+---@param u userdata
+---@param n? integer
---@return any
---@return boolean
---@nodiscard
@@ -146,7 +146,9 @@ function debug.setfenv(object, env) end
---|+'"l"' # ---#DESTAIL 'hookmask.l'
---#DES 'debug.sethook'
----@overload fun(hook: function, mask: hookmask, count?: integer)
+---@overload fun(hook: async fun(), mask: hookmask, count?: integer)
+---@overload fun(thread: thread)
+---@overload fun()
---@param thread thread
---@param hook async fun()
---@param mask hookmask
@@ -180,7 +182,7 @@ function debug.setupvalue(f, up, value) end
---#DES 'debug.setuservalue>5.4'
---@param udata userdata
---@param value any
----@param n integer
+---@param n? integer
---@return userdata udata
function debug.setuservalue(udata, value, n) end
---#elseif VERSION >= 5.2 or JIT then
diff --git a/meta/template/io.lua b/meta/template/io.lua
index b2846878..b1fe5a9e 100644
--- a/meta/template/io.lua
+++ b/meta/template/io.lua
@@ -47,7 +47,7 @@ function io.lines(filename, ...) end
---#DES 'io.open'
---@param filename string
----@param mode openmode
+---@param mode? openmode
---@return file*?
---@return string? errmsg
---@nodiscard
@@ -157,7 +157,7 @@ function file:seek(whence, offset) end
---#DES 'file:setvbuf'
---@param mode vbuf
----@param size integer
+---@param size? integer
function file:setvbuf(mode, size) end
---#DES 'file:write'
diff --git a/meta/template/os.lua b/meta/template/os.lua
index af9b3278..c6af18cd 100644
--- a/meta/template/os.lua
+++ b/meta/template/os.lua
@@ -45,11 +45,11 @@ function os.difftime(t2, t1) end
---#DES 'os.execute'
---#if VERSION <= 5.1 then
----@param command string
+---@param command? string
---@return integer code
function os.execute(command) end
---#else
----@param command string
+---@param command? string
---@return boolean? suc
---@return exitcode? exitcode
---@return integer? code
@@ -69,7 +69,7 @@ function os.exit(code, close) end
---#DES 'os.getenv'
---@param varname string
----@return string
+---@return string?
---@nodiscard
function os.getenv(varname) end
diff --git a/meta/template/string.lua b/meta/template/string.lua
index 0e0d0537..a115648e 100644
--- a/meta/template/string.lua
+++ b/meta/template/string.lua
@@ -65,7 +65,7 @@ function string.gmatch(s, pattern, init) end
---@param s string
---@param pattern string
---@param repl string|table|function
----@param n integer
+---@param n? integer
---@return string
---@return integer count
---@nodiscard
diff --git a/meta/template/table.lua b/meta/template/table.lua
index 21c8b619..a7fe68d2 100644
--- a/meta/template/table.lua
+++ b/meta/template/table.lua
@@ -50,8 +50,9 @@ function table.pack(...) end
function table.remove(list, pos) end
---#DES 'table.sort'
----@param list table
----@param comp fun(a: any, b: any):boolean
+---@generic T
+---@param list T[]
+---@param comp? fun(a: T, b: T):boolean
function table.sort(list, comp) end
---@version >5.2, JIT
diff --git a/meta/template/utf8.lua b/meta/template/utf8.lua
index bd474710..a00a3238 100644
--- a/meta/template/utf8.lua
+++ b/meta/template/utf8.lua
@@ -18,12 +18,12 @@ function utf8.char(code, ...) end
---#DES 'utf8.codes'
---#if VERSION <= 5.3 then
---@param s string
----@return fun():integer, integer
+---@return fun(s: string, p: integer):integer, integer
function utf8.codes(s) end
---#else
---@param s string
---@param lax? boolean
----@return fun():integer, integer
+---@return fun(s: string, p: integer):integer, integer
function utf8.codes(s, lax) end
---#end
diff --git a/script/client.lua b/script/client.lua
index daa9bc52..d86fb4f2 100644
--- a/script/client.lua
+++ b/script/client.lua
@@ -248,6 +248,7 @@ local function tryModifyRC(uri, finalChanges, create)
end
local workspace = require 'workspace'
local path = workspace.getAbsolutePath(uri, '.luarc.json')
+ or workspace.getAbsolutePath(uri, '.luarc.jsonc')
if not path then
return false
end
@@ -318,7 +319,7 @@ local function tryModifyClientGlobal(finalChanges)
end
---@param changes config.change[]
----@param onlyMemory boolean
+---@param onlyMemory? boolean
function m.setConfig(changes, onlyMemory)
local finalChanges = {}
for _, change in ipairs(changes) do
diff --git a/script/config/loader.lua b/script/config/loader.lua
index c53f9399..30711dde 100644
--- a/script/config/loader.lua
+++ b/script/config/loader.lua
@@ -1,10 +1,10 @@
-local json = require 'json'
local proto = require 'proto'
local lang = require 'language'
local util = require 'utility'
local workspace = require 'workspace'
local scope = require 'workspace.scope'
local inspect = require 'inspect'
+local jsonc = require 'jsonc'
local function errorMessage(msg)
proto.notify('window/showMessage', {
@@ -29,7 +29,7 @@ function m.loadRCConfig(uri, filename)
scp:set('lastRCConfig', nil)
return nil
end
- local suc, res = pcall(json.decode, buf)
+ local suc, res = pcall(jsonc.decode, buf)
if not suc then
errorMessage(lang.script('CONFIG_LOAD_ERROR', res))
return scp:get('lastRCConfig')
@@ -55,7 +55,7 @@ function m.loadLocalConfig(uri, filename)
end
local firstChar = buf:match '%S'
if firstChar == '{' then
- local suc, res = pcall(json.decode, buf)
+ local suc, res = pcall(jsonc.decode, buf)
if not suc then
errorMessage(lang.script('CONFIG_LOAD_ERROR', res))
return scp:get('lastLocalConfig')
diff --git a/script/core/completion/completion.lua b/script/core/completion/completion.lua
index beff594c..d4c20c60 100644
--- a/script/core/completion/completion.lua
+++ b/script/core/completion/completion.lua
@@ -16,10 +16,8 @@ local rpath = require 'workspace.require-path'
local lang = require 'language'
local lookBackward = require 'core.look-backward'
local guide = require 'parser.guide'
-local infer = require 'vm.infer'
local await = require 'await'
local postfix = require 'core.completion.postfix'
-local globalMgr = require 'vm.global-manager'
local diagnosticModes = {
'disable-next-line',
@@ -186,11 +184,8 @@ local function buildFunctionSnip(source, value, oop)
end
local function buildDetail(source)
- if source.type == 'dummy' then
- return
- end
- local types = infer.getInfer(source):view()
- local literals = infer.getInfer(source):viewLiterals()
+ local types = vm.getInfer(source):view()
+ local literals = vm.getInfer(source):viewLiterals()
if literals then
return types .. ' = ' .. literals
else
@@ -228,9 +223,6 @@ end
---@async
local function buildDesc(source)
- if source.type == 'dummy' then
- return
- end
local desc = markdown()
local hover = getHover.get(source)
desc:add('md', hover)
@@ -310,8 +302,23 @@ local function checkLocal(state, word, position, results)
if name:sub(1, 1) == '@' then
goto CONTINUE
end
- if infer.getInfer(source):hasFunction() then
- for _, def in ipairs(vm.getDefs(source)) do
+ if vm.getInfer(source):hasFunction() then
+ local defs = vm.getDefs(source)
+ -- make sure `function` is before `doc.type.function`
+ local orders = {}
+ for i, def in ipairs(defs) do
+ if def.type == 'function' then
+ orders[def] = i - 20000
+ elseif def.type == 'doc.type.function' then
+ orders[def] = i - 10000
+ else
+ orders[def] = i
+ end
+ end
+ table.sort(defs, function (a, b)
+ return orders[a] < orders[b]
+ end)
+ for _, def in ipairs(defs) do
if def.type == 'function'
or def.type == 'doc.type.function' then
local funcLabel = name .. getParams(def, false)
@@ -358,7 +365,7 @@ local function checkModule(state, word, position, results)
local fileName = path:match '[^/\\]*$'
local stemName = fileName:gsub('%..+', '')
if not locals[stemName]
- and not globalMgr.hasGlobalSets(state.uri, 'variable', stemName)
+ and not vm.hasGlobalSets(state.uri, 'variable', stemName)
and not config.get(state.uri, 'Lua.diagnostics.globals')[stemName]
and stemName:match '^[%a_][%w_]*$'
and matchKey(word, stemName) then
@@ -505,7 +512,7 @@ local function checkFieldThen(state, name, src, word, startPos, position, parent
})
return
end
- if oop and not infer.getInfer(src):hasFunction() then
+ if oop and not vm.getInfer(src):hasFunction() then
return
end
local literal = guide.getLiteral(value)
@@ -608,14 +615,14 @@ end
---@async
local function checkGlobal(state, word, startPos, position, parent, oop, results)
local locals = guide.getVisibleLocals(state.ast, position)
- local globals = globalMgr.getGlobalSets(state.uri, 'variable')
+ local globals = vm.getGlobalSets(state.uri, 'variable')
checkFieldOfRefs(globals, state, word, startPos, position, parent, oop, results, locals, 'global')
end
---@async
local function checkField(state, word, start, position, parent, oop, results)
if parent.tag == '_ENV' or parent.special == '_G' then
- local globals = globalMgr.getGlobalSets(state.uri, 'variable')
+ local globals = vm.getGlobalSets(state.uri, 'variable')
checkFieldOfRefs(globals, state, word, start, position, parent, oop, results)
else
local refs = vm.getFields(parent)
@@ -1124,7 +1131,7 @@ local function checkTypingEnum(state, position, defs, str, results)
or def.type == 'doc.type.integer'
or def.type == 'doc.type.boolean' then
enums[#enums+1] = {
- label = infer.viewObject(def),
+ label = vm.viewObject(def),
description = def.comment and def.comment.text,
kind = define.CompletionItemKind.EnumMember,
}
@@ -1413,7 +1420,7 @@ local function tryCallArg(state, position, results)
or src.type == 'doc.type.integer'
or src.type == 'doc.type.boolean' then
enums[#enums+1] = {
- label = infer.viewObject(src),
+ label = vm.viewObject(src),
description = src.comment,
kind = define.CompletionItemKind.EnumMember,
}
@@ -1432,7 +1439,7 @@ local function tryCallArg(state, position, results)
: string()
end
enums[#enums+1] = {
- label = infer.getInfer(src):view(),
+ label = vm.getInfer(src):view(),
description = description,
kind = define.CompletionItemKind.Function,
insertText = insertText,
@@ -1520,6 +1527,7 @@ local function tryluaDocCate(word, results)
'module',
'async',
'nodiscard',
+ 'cast',
} do
if matchKey(word, docType) then
results[#results+1] = {
@@ -1668,8 +1676,27 @@ local function tryluaDocBySource(state, position, source, results)
}
end
end
+ return true
elseif source.type == 'doc.module' then
collectRequireNames('require', state.uri, source.module or '', source, source.smark, position, results)
+ return true
+ elseif source.type == 'doc.cast.name' then
+ local locals = guide.getVisibleLocals(state.ast, position)
+ for name, loc in util.sortPairs(locals) do
+ if matchKey(source[1], name) then
+ results[#results+1] = {
+ label = name,
+ kind = define.CompletionItemKind.Variable,
+ id = stack(function () ---@async
+ return {
+ detail = buildDetail(loc),
+ description = buildDesc(loc),
+ }
+ end),
+ }
+ end
+ end
+ return true
end
return false
end
@@ -1764,6 +1791,22 @@ local function tryluaDocByErr(state, position, err, docState, results)
end
elseif err.type == 'LUADOC_MISS_MODULE_NAME' then
collectRequireNames('require', state.uri, '', docState, nil, position, results)
+ elseif err.type == 'LUADOC_MISS_LOCAL_NAME' then
+ local locals = guide.getVisibleLocals(state.ast, position)
+ for name, loc in util.sortPairs(locals) do
+ if name ~= '_ENV' then
+ results[#results+1] = {
+ label = name,
+ kind = define.CompletionItemKind.Variable,
+ id = stack(function () ---@async
+ return {
+ detail = buildDetail(loc),
+ description = buildDesc(loc),
+ }
+ end),
+ }
+ end
+ end
end
end
@@ -1775,14 +1818,14 @@ local function buildluaDocOfFunction(func)
local returns = {}
if func.args then
for _, arg in ipairs(func.args) do
- args[#args+1] = infer.getInfer(arg):view()
+ args[#args+1] = vm.getInfer(arg):view()
end
end
if func.returns then
for _, rtns in ipairs(func.returns) do
for n = 1, #rtns do
if not returns[n] then
- returns[n] = infer.getInfer(rtns[n]):view()
+ returns[n] = vm.getInfer(rtns[n]):view()
end
end
end
@@ -1882,6 +1925,9 @@ local function tryComment(state, position, results)
local doc = getluaDoc(state, position)
if not word then
local comment = getComment(state, position)
+ if not comment then
+ return
+ end
if comment.type == 'comment.short'
or comment.type == 'comment.cshort' then
if comment.text == '' then
diff --git a/script/core/definition.lua b/script/core/definition.lua
index b89aa751..e4868532 100644
--- a/script/core/definition.lua
+++ b/script/core/definition.lua
@@ -53,6 +53,7 @@ local accept = {
['doc.alias.name'] = true,
['doc.see.name'] = true,
['doc.see.field'] = true,
+ ['doc.cast.name'] = true,
}
local function checkRequire(source, offset)
@@ -133,6 +134,9 @@ return function (uri, offset)
local defs = vm.getDefs(source)
for _, src in ipairs(defs) do
+ if src.type == 'global' then
+ goto CONTINUE
+ end
local root = guide.getRoot(src)
if not root then
goto CONTINUE
diff --git a/script/core/diagnostics/close-non-object.lua b/script/core/diagnostics/close-non-object.lua
index b9d3c485..c97014fa 100644
--- a/script/core/diagnostics/close-non-object.lua
+++ b/script/core/diagnostics/close-non-object.lua
@@ -1,6 +1,7 @@
local files = require 'files'
local guide = require 'parser.guide'
local lang = require 'language'
+local vm = require 'vm'
return function (uri, callback)
local state = files.getState(uri)
@@ -23,18 +24,16 @@ return function (uri, callback)
}
return
end
- if source.value.type == 'nil'
- or source.value.type == 'number'
- or source.value.type == 'integer'
- or source.value.type == 'boolean'
- or source.value.type == 'table'
- or source.value.type == 'function' then
+ local infer = vm.getInfer(source.value)
+ if not infer:hasClass()
+ and not infer:hasType 'nil'
+ and not infer:hasType 'table'
+ and infer:view('any', uri) ~= 'any' then
callback {
start = source.value.start,
finish = source.value.finish,
message = lang.script.DIAG_COSE_NON_OBJECT,
}
- return
end
end)
end
diff --git a/script/core/diagnostics/duplicate-doc-field.lua b/script/core/diagnostics/duplicate-doc-field.lua
index 8d355aac..d4116b9b 100644
--- a/script/core/diagnostics/duplicate-doc-field.lua
+++ b/script/core/diagnostics/duplicate-doc-field.lua
@@ -1,6 +1,5 @@
local files = require 'files'
local lang = require 'language'
-local infer = require 'vm.infer'
local function getFieldEventName(doc)
if not doc.extends then
diff --git a/script/core/diagnostics/global-in-nil-env.lua b/script/core/diagnostics/global-in-nil-env.lua
index d95963e4..334fd81a 100644
--- a/script/core/diagnostics/global-in-nil-env.lua
+++ b/script/core/diagnostics/global-in-nil-env.lua
@@ -16,7 +16,7 @@ return function (uri, callback)
local env = guide.getENV(root)
local nilDefs = {}
- if not env.ref then
+ if not env or not env.ref then
return
end
for _, ref in ipairs(env.ref) do
diff --git a/script/core/diagnostics/init.lua b/script/core/diagnostics/init.lua
index 369a6ba2..b4ae3715 100644
--- a/script/core/diagnostics/init.lua
+++ b/script/core/diagnostics/init.lua
@@ -105,7 +105,7 @@ end
---@param uri uri
---@param isScopeDiag boolean
---@param response async fun(result: any)
----@param checked async fun(name: string)
+---@param checked? async fun(name: string)
return function (uri, isScopeDiag, response, checked)
local ast = files.getState(uri)
if not ast then
diff --git a/script/core/diagnostics/lowercase-global.lua b/script/core/diagnostics/lowercase-global.lua
index d7032c13..d03e8c70 100644
--- a/script/core/diagnostics/lowercase-global.lua
+++ b/script/core/diagnostics/lowercase-global.lua
@@ -1,8 +1,8 @@
-local files = require 'files'
-local guide = require 'parser.guide'
-local lang = require 'language'
-local config = require 'config'
-local vm = require 'vm'
+local files = require 'files'
+local guide = require 'parser.guide'
+local lang = require 'language'
+local config = require 'config'
+local vm = require 'vm'
local function isDocClass(source)
if not source.bindDocs then
@@ -30,7 +30,7 @@ return function (uri, callback)
guide.eachSourceType(ast.ast, 'setglobal', function (source)
local name = guide.getKeyName(source)
- if definedGlobal[name] then
+ if not name or definedGlobal[name] then
return
end
local first = name:match '%w'
@@ -44,8 +44,17 @@ return function (uri, callback)
if isDocClass(source) then
return
end
- if vm.isGlobalLibraryName(name) then
- return
+ if definedGlobal[name] == nil then
+ definedGlobal[name] = false
+ local global = vm.getGlobal('variable', name)
+ if global then
+ for _, set in ipairs(global:getSets(uri)) do
+ if vm.isMetaFile(guide.getUri(set)) then
+ definedGlobal[name] = true
+ return
+ end
+ end
+ end
end
callback {
start = source.start,
diff --git a/script/core/diagnostics/missing-parameter.lua b/script/core/diagnostics/missing-parameter.lua
new file mode 100644
index 00000000..698680ca
--- /dev/null
+++ b/script/core/diagnostics/missing-parameter.lua
@@ -0,0 +1,73 @@
+local files = require 'files'
+local guide = require 'parser.guide'
+local vm = require 'vm'
+local lang = require 'language'
+
+local function countCallArgs(source)
+ local result = 0
+ if not source.args then
+ return 0
+ end
+ result = result + #source.args
+ return result
+end
+
+---@return integer
+local function countFuncArgs(source)
+ if not source.args or #source.args == 0 then
+ return 0
+ end
+ local count = 0
+ for i = #source.args, 1, -1 do
+ local arg = source.args[i]
+ if arg.type ~= '...'
+ and not (arg.name and arg.name[1] =='...')
+ and not vm.compileNode(arg):isNullable() then
+ return i
+ end
+ end
+ return count
+end
+
+local function getFuncArgs(func)
+ local funcArgs
+ local defs = vm.getDefs(func)
+ for _, def in ipairs(defs) do
+ if def.type == 'function'
+ or def.type == 'doc.type.function' then
+ local args = countFuncArgs(def)
+ if not funcArgs or args < funcArgs then
+ funcArgs = args
+ end
+ end
+ end
+ return funcArgs
+end
+
+return function (uri, callback)
+ local state = files.getState(uri)
+ if not state then
+ return
+ end
+
+ guide.eachSourceType(state.ast, 'call', function (source)
+ local callArgs = countCallArgs(source)
+
+ local func = source.node
+ local funcArgs = getFuncArgs(func)
+
+ if not funcArgs then
+ return
+ end
+
+ local delta = callArgs - funcArgs
+ if delta >= 0 then
+ return
+ end
+ callback {
+ start = source.start,
+ finish = source.finish,
+ message = lang.script('DIAG_MISS_ARGS', funcArgs, callArgs),
+ }
+ end)
+end
diff --git a/script/core/diagnostics/need-check-nil.lua b/script/core/diagnostics/need-check-nil.lua
new file mode 100644
index 00000000..98fdfd08
--- /dev/null
+++ b/script/core/diagnostics/need-check-nil.lua
@@ -0,0 +1,39 @@
+local files = require 'files'
+local guide = require 'parser.guide'
+local vm = require 'vm'
+local lang = require 'language'
+
+return function (uri, callback)
+ local state = files.getState(uri)
+ if not state then
+ return
+ end
+
+ guide.eachSourceType(state.ast, 'getlocal', function (src)
+ local checkNil
+ local nxt = src.next
+ if nxt then
+ if nxt.type == 'getfield'
+ or nxt.type == 'getmethod'
+ or nxt.type == 'getindex'
+ or nxt.type == 'call' then
+ checkNil = true
+ end
+ end
+ local call = src.parent
+ if call and call.type == 'call' and call.node == src then
+ checkNil = true
+ end
+ if not checkNil then
+ return
+ end
+ local node = vm.compileNode(src)
+ if node:hasFalsy() then
+ callback {
+ start = src.start,
+ finish = src.finish,
+ message = lang.script('DIAG_NEED_CHECK_NIL'),
+ }
+ end
+ end)
+end
diff --git a/script/core/diagnostics/no-unknown.lua b/script/core/diagnostics/no-unknown.lua
index 2199b6a8..48aab5da 100644
--- a/script/core/diagnostics/no-unknown.lua
+++ b/script/core/diagnostics/no-unknown.lua
@@ -1,7 +1,7 @@
local files = require 'files'
local guide = require 'parser.guide'
local lang = require 'language'
-local infer = require 'vm.infer'
+local vm = require 'vm'
return function (uri, callback)
local ast = files.getState(uri)
@@ -20,7 +20,7 @@ return function (uri, callback)
and source.type ~= 'tableindex' then
return
end
- if infer.getInfer(source):view() == 'unknown' then
+ if vm.getInfer(source):view() == 'unknown' then
callback {
start = source.start,
finish = source.finish,
diff --git a/script/core/diagnostics/not-yieldable.lua b/script/core/diagnostics/not-yieldable.lua
index 0588bbde..a1c84276 100644
--- a/script/core/diagnostics/not-yieldable.lua
+++ b/script/core/diagnostics/not-yieldable.lua
@@ -3,7 +3,6 @@ local await = require 'await'
local guide = require 'parser.guide'
local vm = require 'vm'
local lang = require 'language'
-local infer = require 'vm.infer'
local function isYieldAble(defs, i)
local hasFuncDef
@@ -12,7 +11,7 @@ local function isYieldAble(defs, i)
local arg = def.args and def.args[i]
if arg then
hasFuncDef = true
- if infer.getInfer(arg):hasType 'any'
+ if vm.getInfer(arg):hasType 'any'
or vm.isAsync(arg, true)
or arg.type == '...' then
return true
@@ -23,7 +22,7 @@ local function isYieldAble(defs, i)
local arg = def.args and def.args[i]
if arg then
hasFuncDef = true
- if infer.getInfer(arg.extends):hasType 'any'
+ if vm.getInfer(arg.extends):hasType 'any'
or vm.isAsync(arg.extends, true) then
return true
end
diff --git a/script/core/diagnostics/redundant-parameter.lua b/script/core/diagnostics/redundant-parameter.lua
index 4adf169e..41781df8 100644
--- a/script/core/diagnostics/redundant-parameter.lua
+++ b/script/core/diagnostics/redundant-parameter.lua
@@ -2,7 +2,6 @@ local files = require 'files'
local guide = require 'parser.guide'
local vm = require 'vm'
local lang = require 'language'
-local define = require 'proto.define'
local function countCallArgs(source)
local result = 0
@@ -14,64 +13,40 @@ local function countCallArgs(source)
end
local function countFuncArgs(source)
- local result = 0
if not source.args or #source.args == 0 then
- return result
- end
- if source.args[#source.args].type == '...' then
- return math.maxinteger
- end
- result = result + #source.args
- return result
-end
-
-local function countOverLoadArgs(source, doc)
- local result = 0
- local func = doc.overload
- if not func.args or #func.args == 0 then
- return result
+ return 0
end
- if func.args[#func.args].type == '...' then
+ local lastArg = source.args[#source.args]
+ if lastArg.type == '...'
+ or (lastArg.name and lastArg.name[1] == '...') then
return math.maxinteger
+ else
+ return #source.args
end
- result = result + #func.args
- return result
end
local function getFuncArgs(func)
local funcArgs
local defs = vm.getDefs(func)
for _, def in ipairs(defs) do
- if def.value then
- def = def.value
- end
- if def.type == 'function' then
+ if def.type == 'function'
+ or def.type == 'doc.type.function' then
local args = countFuncArgs(def)
if not funcArgs or args > funcArgs then
funcArgs = args
end
- if def.bindDocs then
- for _, doc in ipairs(def.bindDocs) do
- if doc.type == 'doc.overload' then
- args = countOverLoadArgs(def, doc)
- if not funcArgs or args > funcArgs then
- funcArgs = args
- end
- end
- end
- end
end
end
return funcArgs
end
return function (uri, callback)
- local ast = files.getState(uri)
- if not ast then
+ local state = files.getState(uri)
+ if not state then
return
end
- guide.eachSourceType(ast.ast, 'call', function (source)
+ guide.eachSourceType(state.ast, 'call', function (source)
local callArgs = countCallArgs(source)
if callArgs == 0 then
return
@@ -97,7 +72,6 @@ return function (uri, callback)
callback {
start = arg.start,
finish = arg.finish,
- tags = { define.DiagnosticTag.Unnecessary },
message = lang.script('DIAG_OVER_MAX_ARGS', funcArgs, callArgs)
}
end
diff --git a/script/core/diagnostics/undefined-field.lua b/script/core/diagnostics/undefined-field.lua
index 025c217a..41fcda48 100644
--- a/script/core/diagnostics/undefined-field.lua
+++ b/script/core/diagnostics/undefined-field.lua
@@ -3,7 +3,6 @@ local vm = require 'vm'
local lang = require 'language'
local guide = require 'parser.guide'
local await = require 'await'
-local infer = require 'vm.infer'
local skipCheckClass = {
['unknown'] = true,
@@ -35,7 +34,7 @@ return function (uri, callback)
local node = src.node
if node then
local ok
- for view in infer.getInfer(node):eachView() do
+ for view in vm.getInfer(node):eachView() do
if not skipCheckClass[view] then
ok = true
break
diff --git a/script/core/diagnostics/undefined-global.lua b/script/core/diagnostics/undefined-global.lua
index 139fa74f..bd0aae69 100644
--- a/script/core/diagnostics/undefined-global.lua
+++ b/script/core/diagnostics/undefined-global.lua
@@ -4,7 +4,6 @@ local lang = require 'language'
local config = require 'config'
local guide = require 'parser.guide'
local await = require 'await'
-local globalMgr = require 'vm.global-manager'
local requireLike = {
['include'] = true,
@@ -41,7 +40,7 @@ return function (uri, callback)
return
end
if cache[key] == nil then
- cache[key] = globalMgr.hasGlobalSets(uri, 'variable', key)
+ cache[key] = vm.hasGlobalSets(uri, 'variable', key)
end
if cache[key] then
return
diff --git a/script/core/diagnostics/unused-function.lua b/script/core/diagnostics/unused-function.lua
index 79cb16e2..813ac804 100644
--- a/script/core/diagnostics/unused-function.lua
+++ b/script/core/diagnostics/unused-function.lua
@@ -18,75 +18,107 @@ local function isToBeClosed(source)
return false
end
----@async
-return function (uri, callback)
- local ast = files.getState(uri)
- if not ast then
- return
+---@param source parser.object
+local function isValidFunction(source)
+ if not source then
+ return false
+ end
+ if source.type == 'main' then
+ return false
+ end
+ local parent = source.parent
+ if not parent then
+ return false
+ end
+ if parent.type ~= 'local'
+ and parent.type ~= 'setlocal' then
+ return false
+ end
+ if isToBeClosed(parent) then
+ return false
end
+ return true
+end
- local cache = {}
+---@async
+local function collect(ast, white, roots, links)
---@async
- local function checkFunction(source)
- if not source then
+ guide.eachSourceType(ast, 'function', function (src)
+ await.delay()
+ if not isValidFunction(src) then
return
end
- if cache[source] ~= nil then
- return cache[source]
- end
- cache[source] = false
- local parent = source.parent
- if not parent then
- return false
- end
- if parent.type ~= 'local'
- and parent.type ~= 'setlocal' then
- return false
- end
- if isToBeClosed(parent) then
- return false
+ local loc = src.parent
+ if loc.type == 'setlocal' then
+ loc = loc.node
end
- await.delay()
- if parent.type == 'setlocal' then
- parent = parent.node
- end
- local refs = parent.ref
- local hasGet
- if refs then
- for _, src in ipairs(refs) do
- if guide.isGet(src) then
- local func = guide.getParentFunction(src)
- if not checkFunction(func) then
- hasGet = true
- break
- end
+ for _, ref in ipairs(loc.ref or {}) do
+ if ref.type == 'getlocal' then
+ local func = guide.getParentFunction(ref)
+ if not isValidFunction(func) or roots[func] then
+ roots[src] = true
+ return
end
+ if not links[func] then
+ links[func] = {}
+ end
+ links[func][#links[func]+1] = src
end
end
- if not hasGet then
- if client.isVSCode() then
- callback {
- start = source.start,
- finish = source.finish,
- tags = { define.DiagnosticTag.Unnecessary },
- message = lang.script.DIAG_UNUSED_FUNCTION,
- }
- else
- callback {
- start = source.keyword[1],
- finish = source.keyword[2],
- tags = { define.DiagnosticTag.Unnecessary },
- message = lang.script.DIAG_UNUSED_FUNCTION,
- }
- end
- cache[source] = true
- return true
- end
- return false
+ white[src] = true
+ end)
+
+ return white, roots, links
+end
+
+local function turnBlack(source, black, white, links)
+ if black[source] then
+ return
end
+ black[source] = true
+ white[source] = nil
+ for _, link in ipairs(links[source] or {}) do
+ turnBlack(link, black, white, links)
+ end
+end
- -- 只检查局部函数
- guide.eachSourceType(ast.ast, 'function', function (source) ---@async
- checkFunction(source)
- end)
+---@async
+return function (uri, callback)
+ local state = files.getState(uri)
+ if not state then
+ return
+ end
+
+ if vm.isMetaFile(uri) then
+ return
+ end
+
+ local black = {}
+ local white = {}
+ local roots = {}
+ local links = {}
+
+ collect(state.ast, white, roots, links)
+
+ for source in pairs(roots) do
+ turnBlack(source, black, white, links)
+ end
+
+ for source in pairs(white) do
+ if client.isVSCode() then
+ callback {
+ start = source.start,
+ finish = source.finish,
+ tags = { define.DiagnosticTag.Unnecessary },
+ message = lang.script.DIAG_UNUSED_FUNCTION,
+ }
+ else
+ callback {
+ start = source.keyword[1],
+ finish = source.keyword[2],
+ tags = { define.DiagnosticTag.Unnecessary },
+ message = lang.script.DIAG_UNUSED_FUNCTION,
+ }
+ end
+ end
end
diff --git a/script/core/diagnostics/unused-vararg.lua b/script/core/diagnostics/unused-vararg.lua
index 2e07e1ee..ce033cf3 100644
--- a/script/core/diagnostics/unused-vararg.lua
+++ b/script/core/diagnostics/unused-vararg.lua
@@ -2,6 +2,7 @@ local files = require 'files'
local guide = require 'parser.guide'
local define = require 'proto.define'
local lang = require 'language'
+local vm = require 'vm'
return function (uri, callback)
local ast = files.getState(uri)
@@ -9,6 +10,10 @@ return function (uri, callback)
return
end
+ if vm.isMetaFile(uri) then
+ return
+ end
+
guide.eachSourceType(ast.ast, 'function', function (source)
local args = source.args
if not args then
diff --git a/script/core/formatting.lua b/script/core/formatting.lua
index 49da6861..b52854a4 100644
--- a/script/core/formatting.lua
+++ b/script/core/formatting.lua
@@ -3,7 +3,7 @@ local files = require("files")
local log = require("log")
return function(uri, options)
- local text = files.getText(uri)
+ local text = files.getOriginText(uri)
local ast = files.getState(uri)
local status, formattedText = codeFormat.format(uri, text, options)
diff --git a/script/core/hint.lua b/script/core/hint.lua
index f6774d2a..f97cdcec 100644
--- a/script/core/hint.lua
+++ b/script/core/hint.lua
@@ -1,5 +1,4 @@
local files = require 'files'
-local infer = require 'vm.infer'
local vm = require 'vm'
local config = require 'config'
local guide = require 'parser.guide'
@@ -39,7 +38,7 @@ local function typeHint(uri, results, start, finish)
end
end
await.delay()
- local view = infer.getInfer(source):view()
+ local view = vm.getInfer(source):view()
if view == 'any'
or view == 'unknown'
or view == 'nil' then
diff --git a/script/core/hover/args.lua b/script/core/hover/args.lua
index a53136b0..c485d9b9 100644
--- a/script/core/hover/args.lua
+++ b/script/core/hover/args.lua
@@ -1,17 +1,5 @@
local guide = require 'parser.guide'
-local infer = require 'vm.infer'
-
-local function optionalArg(arg)
- if not arg.bindDocs then
- return false
- end
- local name = arg[1]
- for _, doc in ipairs(arg.bindDocs) do
- if doc.type == 'doc.param' and doc.param[1] == name then
- return doc.optional
- end
- end
-end
+local vm = require 'vm'
local function asFunction(source)
local args = {}
@@ -21,7 +9,7 @@ local function asFunction(source)
methodDef = true
end
if methodDef then
- args[#args+1] = ('self: %s'):format(infer.getInfer(parent.node):view 'any')
+ args[#args+1] = ('self: %s'):format(vm.getInfer(parent.node):view 'any')
end
if source.args then
for i = 1, #source.args do
@@ -31,18 +19,25 @@ local function asFunction(source)
end
local name = arg.name or guide.getKeyName(arg)
if name then
+ local argNode = vm.compileNode(arg)
+ local optional
+ if argNode:isOptional() then
+ optional = true
+ argNode = argNode:copy()
+ argNode:removeOptional()
+ end
args[#args+1] = ('%s%s: %s'):format(
name,
- optionalArg(arg) and '?' or '',
- infer.getInfer(arg):view 'any'
+ optional and '?' or '',
+ vm.getInfer(argNode):view('any', guide.getUri(source))
)
elseif arg.type == '...' then
args[#args+1] = ('%s: %s'):format(
'...',
- infer.getInfer(arg):view 'any'
+ vm.getInfer(arg):view 'any'
)
else
- args[#args+1] = ('%s'):format(infer.getInfer(arg):view 'any')
+ args[#args+1] = ('%s'):format(vm.getInfer(arg):view 'any')
end
::CONTINUE::
end
@@ -61,7 +56,7 @@ local function asDocFunction(source)
args[i] = ('%s%s: %s'):format(
name,
arg.optional and '?' or '',
- arg.extends and infer.getInfer(arg.extends):view 'any' or 'any'
+ arg.extends and vm.getInfer(arg.extends):view 'any' or 'any'
)
end
return args
diff --git a/script/core/hover/description.lua b/script/core/hover/description.lua
index 03f6128a..e9267c0f 100644
--- a/script/core/hover/description.lua
+++ b/script/core/hover/description.lua
@@ -6,7 +6,6 @@ local lang = require 'language'
local util = require 'utility'
local guide = require 'parser.guide'
local rpath = require 'workspace.require-path'
-local infer = require 'vm.infer'
local function collectRequire(mode, literal, uri)
local result, searchers
@@ -153,7 +152,7 @@ local function buildEnumChunk(docType, name)
local types = {}
local lines = {}
for _, tp in ipairs(vm.getDefs(docType)) do
- types[#types+1] = infer.getInfer(tp):view()
+ types[#types+1] = vm.getInfer(tp):view()
if tp.type == 'doc.type.string'
or tp.type == 'doc.type.integer'
or tp.type == 'doc.type.boolean' then
@@ -175,7 +174,7 @@ local function buildEnumChunk(docType, name)
(enum.default and '->')
or (enum.additional and '+>')
or ' |',
- infer.viewObject(enum)
+ vm.viewObject(enum)
)
if enum.comment then
local first = true
diff --git a/script/core/hover/init.lua b/script/core/hover/init.lua
index bc2f40eb..7231944a 100644
--- a/script/core/hover/init.lua
+++ b/script/core/hover/init.lua
@@ -5,7 +5,6 @@ local getDesc = require 'core.hover.description'
local util = require 'utility'
local findSource = require 'core.find-source'
local markdown = require 'provider.markdown'
-local infer = require 'vm.infer'
local guide = require 'parser.guide'
---@async
@@ -40,9 +39,24 @@ local function getHover(source)
end
local oop
- if infer.getInfer(source):view() == 'function' then
+ if vm.getInfer(source):view() == 'function' then
+ local defs = vm.getDefs(source)
+ -- make sure `function` is before `doc.type.function`
+ local orders = {}
+ for i, def in ipairs(defs) do
+ if def.type == 'function' then
+ orders[def] = i - 20000
+ elseif def.type == 'doc.type.function' then
+ orders[def] = i - 10000
+ else
+ orders[def] = i
+ end
+ end
+ table.sort(defs, function (a, b)
+ return orders[a] < orders[b]
+ end)
local hasFunc
- for _, def in ipairs(vm.getDefs(source)) do
+ for _, def in ipairs(defs) do
if guide.isOOP(def) then
oop = true
end
@@ -58,6 +72,9 @@ local function getHover(source)
else
addHover(source, true, oop)
for _, def in ipairs(vm.getDefs(source)) do
+ if def.type == 'global' then
+ goto CONTINUE
+ end
if guide.isOOP(def) then
oop = true
end
@@ -67,6 +84,7 @@ local function getHover(source)
isFunction = true
end
addHover(def, isFunction, oop)
+ ::CONTINUE::
end
end
diff --git a/script/core/hover/label.lua b/script/core/hover/label.lua
index 8224e9d3..2bbfe806 100644
--- a/script/core/hover/label.lua
+++ b/script/core/hover/label.lua
@@ -2,7 +2,6 @@ local buildName = require 'core.hover.name'
local buildArgs = require 'core.hover.args'
local buildReturn = require 'core.hover.return'
local buildTable = require 'core.hover.table'
-local infer = require 'vm.infer'
local vm = require 'vm'
local util = require 'utility'
local lang = require 'language'
@@ -34,7 +33,7 @@ local function asDocTypeName(source)
return '(class) ' .. doc.class[1]
end
if doc.type == 'doc.alias' then
- return '(alias) ' .. doc.alias[1] .. ' ' .. lang.script('HOVER_EXTENDS', infer.getInfer(doc.extends):view())
+ return '(alias) ' .. doc.alias[1] .. ' ' .. lang.script('HOVER_EXTENDS', vm.getInfer(doc.extends):view())
end
end
end
@@ -42,7 +41,7 @@ end
---@async
local function asValue(source, title)
local name = buildName(source, false) or ''
- local ifr = infer.getInfer(source)
+ local ifr = vm.getInfer(source)
local type = ifr:view()
local literal = ifr:viewLiterals()
local cont = buildTable(source)
@@ -140,7 +139,7 @@ local function asDocFieldName(source)
break
end
end
- local view = infer.getInfer(source.extends):view()
+ local view = vm.getInfer(source.extends):view()
if not class then
return ('(field) ?.%s: %s'):format(name, view)
end
@@ -180,7 +179,7 @@ local function asNumber(source)
if not text then
return nil
end
- local raw = text:sub(source.start, source.finish)
+ local raw = text:sub(source.start + 1, source.finish)
if not raw or not raw:find '[^%-%d%.]' then
return nil
end
diff --git a/script/core/hover/name.lua b/script/core/hover/name.lua
index 905c5ec7..f8473638 100644
--- a/script/core/hover/name.lua
+++ b/script/core/hover/name.lua
@@ -1,5 +1,5 @@
-local infer = require 'vm.infer'
local guide = require 'parser.guide'
+local vm = require 'vm'
local buildName
@@ -19,7 +19,7 @@ end
local function asField(source, oop)
local class
if source.node.type ~= 'getglobal' then
- class = infer.getInfer(source.node):viewClass()
+ class = vm.getInfer(source.node):viewClass()
end
local node = class
or buildName(source.node, false)
diff --git a/script/core/hover/return.lua b/script/core/hover/return.lua
index 77710148..3d8a94a5 100644
--- a/script/core/hover/return.lua
+++ b/script/core/hover/return.lua
@@ -1,5 +1,3 @@
-local infer = require 'vm.infer'
-local guide = require 'parser.guide'
local vm = require 'vm.vm'
---@param source parser.object
@@ -65,10 +63,9 @@ local function asFunction(source)
local rtn = vm.getReturnOfFunction(source, i)
local doc = docs[i]
local name = doc and doc.name and doc.name[1] and (doc.name[1] .. ': ')
- local text = ('%s%s%s'):format(
+ local text = ('%s%s'):format(
name or '',
- infer.getInfer(rtn):view(),
- doc and doc.optional and '?' or ''
+ vm.getInfer(rtn):view()
)
if i == 1 then
returns[i] = (' -> %s'):format(text)
@@ -86,10 +83,7 @@ local function asDocFunction(source)
end
local returns = {}
for i, rtn in ipairs(source.returns) do
- local rtnText = ('%s%s'):format(
- infer.getInfer(rtn):view(),
- rtn.optional and '?' or ''
- )
+ local rtnText = vm.getInfer(rtn):view()
if i == 1 then
returns[#returns+1] = (' -> %s'):format(rtnText)
else
diff --git a/script/core/hover/table.lua b/script/core/hover/table.lua
index 31036edd..16874101 100644
--- a/script/core/hover/table.lua
+++ b/script/core/hover/table.lua
@@ -1,7 +1,6 @@
local vm = require 'vm'
local util = require 'utility'
local config = require 'config'
-local infer = require 'vm.infer'
local await = require 'await'
local guide = require 'parser.guide'
@@ -16,22 +15,34 @@ local function formatKey(key)
return ('[%s]'):format(key)
end
-local function buildAsHash(keys, typeMap, literalMap, optMap, reachMax)
+---@param uri uri
+---@param keys string[]
+---@param nodeMap table<string, vm.node>
+---@param reachMax integer
+local function buildAsHash(uri, keys, nodeMap, reachMax)
local lines = {}
lines[#lines+1] = '{'
for _, key in ipairs(keys) do
- local typeView = typeMap[key]
- local literalView = literalMap[key]
+ local node = nodeMap[key]
+ local isOptional = node:isOptional()
+ if isOptional then
+ node = node:copy()
+ node:removeOptional()
+ end
+ local ifr = vm.getInfer(node)
+ local typeView = ifr:view('unknown', uri)
+ local literalView = ifr:viewLiterals()
if literalView then
lines[#lines+1] = (' %s%s: %s = %s,'):format(
formatKey(key),
- optMap[key] and '?' or '',
+ isOptional and '?' or '',
typeView,
- literalView)
+ literalView
+ )
else
lines[#lines+1] = (' %s%s: %s,'):format(
formatKey(key),
- optMap[key] and '?' or '',
+ isOptional and '?' or '',
typeView
)
end
@@ -43,26 +54,40 @@ local function buildAsHash(keys, typeMap, literalMap, optMap, reachMax)
return table.concat(lines, '\n')
end
-local function buildAsConst(keys, typeMap, literalMap, optMap, reachMax)
+---@param uri uri
+---@param keys string[]
+---@param nodeMap table<string, vm.node>
+---@param reachMax integer
+local function buildAsConst(uri, keys, nodeMap, reachMax)
+ local literalMap = {}
+ for _, key in ipairs(keys) do
+ literalMap[key] = vm.getInfer(nodeMap[key]):viewLiterals()
+ end
table.sort(keys, function (a, b)
return tonumber(literalMap[a]) < tonumber(literalMap[b])
end)
local lines = {}
lines[#lines+1] = '{'
for _, key in ipairs(keys) do
- local typeView = typeMap[key]
+ local node = nodeMap[key]
+ local isOptional = node:isOptional()
+ if isOptional then
+ node = node:copy()
+ node:removeOptional()
+ end
+ local typeView = vm.getInfer(node):view('unknown', uri)
local literalView = literalMap[key]
if literalView then
lines[#lines+1] = (' %s%s: %s = %s,'):format(
formatKey(key),
- optMap[key] and '?' or '',
+ isOptional and '?' or '',
typeView,
literalView
)
else
lines[#lines+1] = (' %s%s: %s,'):format(
formatKey(key),
- optMap[key] and '?' or '',
+ isOptional and '?' or '',
typeView
)
end
@@ -102,6 +127,19 @@ local function getKeyMap(fields)
if ta == 'boolean' then
return a == true
end
+ if ta == 'string' then
+ if a:sub(1, 1) == '_' then
+ if b:sub(1, 1) == '_' then
+ return a < b
+ else
+ return false
+ end
+ elseif b:sub(1, 1) == '_' then
+ return true
+ else
+ return a < b
+ end
+ end
return a < b
else
return tsa < tsb
@@ -110,48 +148,25 @@ local function getKeyMap(fields)
return keys, map
end
-local function getOptMap(fields, keyMap)
- local optMap = {}
- for _, field in ipairs(fields) do
- if field.type == 'doc.field' then
- if field.optional then
- local key = vm.getKeyName(field)
- if keyMap[key] then
- optMap[key] = true
- end
- end
- end
- if field.type == 'doc.type.field' then
- if field.optional then
- local key = vm.getKeyName(field)
- if keyMap[key] then
- optMap[key] = true
- end
- end
- end
- end
- return optMap
-end
-
---@async
-local function getInferMap(fields, keyMap)
- ---@type table<string, vm.infer>
- local inferMap = {}
+local function getNodeMap(fields, keyMap)
+ ---@type table<string, vm.node>
+ local nodeMap = {}
for _, field in ipairs(fields) do
local key = vm.getKeyName(field)
if not keyMap[key] then
goto CONTINUE
end
await.delay()
- local ifr = infer.getInfer(field)
- if inferMap[key] then
- inferMap[key] = inferMap[key]:merge(ifr)
+ local node = vm.compileNode(field)
+ if nodeMap[key] then
+ nodeMap[key]:merge(node)
else
- inferMap[key] = ifr
+ nodeMap[key] = node:copy()
end
::CONTINUE::
end
- return inferMap
+ return nodeMap
end
---@async
@@ -163,7 +178,7 @@ return function (source)
return nil
end
- for view in infer.getInfer(source):eachView() do
+ for view in vm.getInfer(source):eachView() do
if view == 'string'
or vm.isSubType(uri, view, 'string') then
return nil
@@ -184,19 +199,14 @@ return function (source)
end
end
- local optMap = getOptMap(fields, map)
- local inferMap = getInferMap(fields, map)
+ local nodeMap = getNodeMap(fields, map)
- local typeMap = {}
- local literalMap = {}
local isConsts = true
for i = 1, #keys do
await.delay()
local key = keys[i]
-
- typeMap[key] = inferMap[key]:view('unknown', uri)
- literalMap[key] = inferMap[key]:viewLiterals()
- if not tonumber(literalMap[key]) then
+ local literal = vm.getInfer(nodeMap[key]):viewLiterals()
+ if not tonumber(literal) then
isConsts = false
end
end
@@ -204,9 +214,9 @@ return function (source)
local result
if isConsts then
- result = buildAsConst(keys, typeMap, literalMap, optMap, reachMax)
+ result = buildAsConst(uri, keys, nodeMap, reachMax)
else
- result = buildAsHash(keys, typeMap, literalMap, optMap, reachMax)
+ result = buildAsHash(uri, keys, nodeMap, reachMax)
end
--if timeUp then
diff --git a/script/core/look-backward.lua b/script/core/look-backward.lua
index eea089bc..eeee6017 100644
--- a/script/core/look-backward.lua
+++ b/script/core/look-backward.lua
@@ -2,7 +2,8 @@
local m = {}
--- 是否是空白符
----@param inline boolean # 必须在同一行中(排除换行符)
+---@param char string
+---@param inline? boolean # 必须在同一行中(排除换行符)
function m.isSpace(char, inline)
if inline then
if char == ' '
@@ -21,7 +22,9 @@ function m.isSpace(char, inline)
end
--- 跳过空白符
----@param inline boolean # 必须在同一行中(排除换行符)
+---@param text string
+---@param offset integer
+---@param inline? boolean # 必须在同一行中(排除换行符)
function m.skipSpace(text, offset, inline)
for i = offset, 1, -1 do
local char = text:sub(i, i)
diff --git a/script/core/matchkey.lua b/script/core/matchkey.lua
index 3c6a54a8..4db9d764 100644
--- a/script/core/matchkey.lua
+++ b/script/core/matchkey.lua
@@ -59,7 +59,7 @@ end
---@param input string
---@param other string
----@param fast boolean
+---@param fast? boolean
---@return boolean isMatch
---@return number deviation
return function (input, other, fast)
diff --git a/script/core/rangeformatting.lua b/script/core/rangeformatting.lua
index ccf2d21f..f64e9cda 100644
--- a/script/core/rangeformatting.lua
+++ b/script/core/rangeformatting.lua
@@ -4,7 +4,7 @@ local log = require("log")
local converter = require("proto.converter")
return function(uri, range, options)
- local text = files.getText(uri)
+ local text = files.getOriginText(uri)
local status, formattedText, startLine, endLine = codeFormat.range_format(
uri, text, range.start.line, range["end"].line, options)
diff --git a/script/core/rename.lua b/script/core/rename.lua
index ec21e87c..7599fad6 100644
--- a/script/core/rename.lua
+++ b/script/core/rename.lua
@@ -3,7 +3,6 @@ local vm = require 'vm'
local util = require 'utility'
local findSource = require 'core.find-source'
local guide = require 'parser.guide'
-local globalMgr = require 'vm.global-manager'
local Forcing
@@ -191,7 +190,7 @@ end
---@async
local function ofGlobal(source, newname, callback)
local key = guide.getKeyName(source)
- local global = globalMgr.getGlobal('variable', key)
+ local global = vm.getGlobal('variable', key)
if not global then
return
end
@@ -214,7 +213,7 @@ end
---@async
local function ofDocTypeName(source, newname, callback)
local oldname = source[1]
- local global = globalMgr.getGlobal('type', oldname)
+ local global = vm.getGlobal('type', oldname)
if not global then
return
end
diff --git a/script/core/semantic-tokens.lua b/script/core/semantic-tokens.lua
index 568bb222..33449013 100644
--- a/script/core/semantic-tokens.lua
+++ b/script/core/semantic-tokens.lua
@@ -5,7 +5,6 @@ local vm = require 'vm'
local util = require 'utility'
local guide = require 'parser.guide'
local converter = require 'proto.converter'
-local infer = require 'vm.infer'
local config = require 'config'
local linkedTable = require 'linked-table'
@@ -16,8 +15,24 @@ local Care = util.switch()
if not options.variable then
return
end
- local isLib = vm.isGlobalLibraryName(source[1])
- local isFunc = infer.getInfer(source):hasFunction()
+
+ local name = source[1]
+ local isLib = options.libGlobals[name]
+ if isLib == nil then
+ isLib = false
+ local global = vm.getGlobal('variable', name)
+ if global then
+ local uri = guide.getUri(source)
+ for _, set in ipairs(global:getSets(uri)) do
+ if vm.isMetaFile(guide.getUri(set)) then
+ isLib = true
+ break
+ end
+ end
+ end
+ options.libGlobals[name] = isLib
+ end
+ local isFunc = vm.getInfer(source):hasFunction()
local type = isFunc and define.TokenTypes['function'] or define.TokenTypes.variable
local modifier = isLib and define.TokenModifiers.defaultLibrary or define.TokenModifiers.static
@@ -66,7 +81,7 @@ local Care = util.switch()
return
end
end
- if infer.getInfer(source):hasFunction() then
+ if vm.getInfer(source):hasFunction() then
results[#results+1] = {
start = source.start,
finish = source.finish,
@@ -165,27 +180,23 @@ local Care = util.switch()
-- 5. Class declaration
-- only search this local
if loc.bindDocs then
- for i = #loc.bindDocs, 1, -1 do
- local doc = loc.bindDocs[i]
- if doc.type == 'doc.type' then
- break
- end
- if doc.type == "doc.class" and doc.bindSources then
- for _, src in ipairs(doc.bindSources) do
- if src == loc then
- results[#results+1] = {
- start = source.start,
- finish = source.finish,
- type = define.TokenTypes.class,
- }
- return
- end
+ local isParam = source.parent.type == 'funcargs'
+ or source.parent.type == 'in'
+ if not isParam then
+ for _, doc in ipairs(loc.bindDocs) do
+ if doc.type == 'doc.class' then
+ results[#results+1] = {
+ start = source.start,
+ finish = source.finish,
+ type = define.TokenTypes.class,
+ }
+ return
end
end
end
end
-- 6. References to other functions
- if infer.getInfer(loc):hasFunction() then
+ if vm.getInfer(loc):hasFunction() then
results[#results+1] = {
start = source.start,
finish = source.finish,
@@ -656,6 +667,14 @@ local Care = util.switch()
type = define.TokenTypes.keyword,
}
end)
+ : case 'doc.cast.name'
+ : call(function (source, options, results)
+ results[#results+1] = {
+ start = source.start,
+ finish = source.finish,
+ type = define.TokenTypes.variable,
+ }
+ end)
local function buildTokens(uri, results)
local tokens = {}
@@ -773,24 +792,25 @@ end
---@async
return function (uri, start, finish)
+ local results = {}
if not config.get(uri, 'Lua.semantic.enable') then
- return nil
+ return results
end
local state = files.getState(uri)
if not state then
- return nil
+ return results
end
local options = {
uri = uri,
state = state,
text = files.getText(uri),
+ libGlobals = {},
variable = config.get(uri, 'Lua.semantic.variable'),
annotation = config.get(uri, 'Lua.semantic.annotation'),
keyword = config.get(uri, 'Lua.semantic.keyword'),
}
- local results = {}
guide.eachSourceBetween(state.ast, start, finish, function (source) ---@async
Care(source.type, source, options, results)
await.delay()
@@ -798,27 +818,26 @@ return function (uri, start, finish)
for _, comm in ipairs(state.comms) do
if start <= comm.start and comm.finish <= finish then
- if comm.type == 'comment.short' then
- local head = comm.text:match '^%-%s*[@|]'
- if head then
- results[#results+1] = {
- start = comm.start,
- finish = comm.start + #head + 1,
- type = define.TokenTypes.comment,
- }
- results[#results+1] = {
- start = comm.start + #head + 1,
- finish = comm.start + #head + 2 + #comm.text:match('%S*', #head + 1),
- type = define.TokenTypes.keyword,
- modifieres = define.TokenModifiers.documentation,
- }
+ local headPos = (comm.type == 'comment.short' and comm.text:match '^%-%s*[@|]()')
+ or (comm.type == 'comment.long' and comm.text:match '^@()')
+ if headPos then
+ local atPos
+ if comm.type == 'comment.short' then
+ atPos = headPos + 2
else
- results[#results+1] = {
- start = comm.start,
- finish = comm.finish,
- type = define.TokenTypes.comment,
- }
+ atPos = headPos + #comm.mark
end
+ results[#results+1] = {
+ start = comm.start,
+ finish = comm.start + atPos - 2,
+ type = define.TokenTypes.comment,
+ }
+ results[#results+1] = {
+ start = comm.start + atPos - 2,
+ finish = comm.start + atPos - 1 + #comm.text:match('%S*', headPos),
+ type = define.TokenTypes.keyword,
+ modifieres = define.TokenModifiers.documentation,
+ }
else
results[#results+1] = {
start = comm.start,
@@ -830,7 +849,7 @@ return function (uri, start, finish)
end
if #results == 0 then
- return {}
+ return results
end
results = solveMultilineAndOverlapping(state, results)
diff --git a/script/core/signature.lua b/script/core/signature.lua
index ab7268dd..025e70b7 100644
--- a/script/core/signature.lua
+++ b/script/core/signature.lua
@@ -41,6 +41,9 @@ end
---@async
local function makeOneSignature(source, oop, index)
local label = hoverLabel(source, oop)
+ if not label then
+ return nil
+ end
-- 去掉返回值
label = label:gsub('%s*->.+', '')
local params = {}
diff --git a/script/core/type-definition.lua b/script/core/type-definition.lua
index 92f81997..d8434c8c 100644
--- a/script/core/type-definition.lua
+++ b/script/core/type-definition.lua
@@ -3,7 +3,6 @@ local files = require 'files'
local vm = require 'vm'
local findSource = require 'core.find-source'
local guide = require 'parser.guide'
-local infer = require 'vm.infer'
local rpath = require 'workspace.require-path'
local function sortResults(results)
diff --git a/script/doctor.lua b/script/doctor.lua
index 91a7e4b8..87cdcfcb 100644
--- a/script/doctor.lua
+++ b/script/doctor.lua
@@ -175,6 +175,9 @@ m.snapshot = private(function ()
exclude[o] = true
end
end
+ ---@generic T
+ ---@param o T
+ ---@return T
local function private(o)
if not o then
return nil
diff --git a/script/encoder/init.lua b/script/encoder/init.lua
index 0011265a..3c8a58e0 100644
--- a/script/encoder/init.lua
+++ b/script/encoder/init.lua
@@ -10,9 +10,9 @@ local utf16be = utf16('be', utf8.codepoint '�')
local m = {}
---@param encoding encoder.encoding
----@param s string
----@param i integer
----@param j integer
+---@param s string
+---@param i? integer
+---@param j? integer
function m.len(encoding, s, i, j)
i = i or 1
j = j or #s
@@ -33,9 +33,9 @@ function m.len(encoding, s, i, j)
end
---@param encoding encoder.encoding
----@param s string
----@param n integer
----@param i integer
+---@param s string
+---@param n integer
+---@param i? integer
function m.offset(encoding, s, n, i)
i = i or 1
if encoding == 'utf16'
diff --git a/script/files.lua b/script/files.lua
index d16474fd..22c9ae31 100644
--- a/script/files.lua
+++ b/script/files.lua
@@ -165,8 +165,8 @@ end
--- 设置文件文本
---@param uri uri
---@param text string
----@param isTrust boolean
----@param callback function
+---@param isTrust? boolean
+---@param callback? function
function m.setText(uri, text, isTrust, callback)
if not text then
return
diff --git a/script/fs-utility.lua b/script/fs-utility.lua
index c845c769..08aae98a 100644
--- a/script/fs-utility.lua
+++ b/script/fs-utility.lua
@@ -281,12 +281,8 @@ local function fsIsDirectory(path, option)
if path.type == 'dummy' then
return path:isDirectory()
end
- local suc, res = pcall(fs.is_directory, path)
- if not suc then
- option.err[#option.err+1] = res
- return false
- end
- return res
+ local status = fs.symlink_status(path):type()
+ return status == 'directory'
end
local function fsPairs(path, option)
@@ -616,9 +612,10 @@ end
function m.scanDirectory(dir, callback)
for fullpath in fs.pairs(dir) do
- if fs.is_directory(fullpath) then
+ local status = fs.symlink_status(fullpath):type()
+ if status == 'directory' then
m.scanDirectory(fullpath, callback)
- else
+ elseif status == 'regular' then
callback(fullpath)
end
end
diff --git a/script/glob/gitignore.lua b/script/glob/gitignore.lua
index 09be1415..4dad2747 100644
--- a/script/glob/gitignore.lua
+++ b/script/glob/gitignore.lua
@@ -163,7 +163,7 @@ function mt:getRelativePath(path)
return path
end
----@param callback async fun()
+---@param callback async fun(path: string)
---@async
function mt:scan(path, callback)
local files = {}
diff --git a/script/jsonc.lua b/script/jsonc.lua
new file mode 100644
index 00000000..0361d99b
--- /dev/null
+++ b/script/jsonc.lua
@@ -0,0 +1,603 @@
+local type = type
+local next = next
+local error = error
+local tonumber = tonumber
+local tostring = tostring
+local table_concat = table.concat
+local table_sort = table.sort
+local string_char = string.char
+local string_byte = string.byte
+local string_find = string.find
+local string_match = string.match
+local string_gsub = string.gsub
+local string_sub = string.sub
+local string_rep = string.rep
+local string_format = string.format
+local setmetatable = setmetatable
+local getmetatable = getmetatable
+local huge = math.huge
+local tiny = -huge
+
+local utf8_char
+local math_type
+
+if _VERSION == "Lua 5.1" or _VERSION == "Lua 5.2" then
+ local math_floor = math.floor
+ function utf8_char(c)
+ if c <= 0x7f then
+ return string_char(c)
+ elseif c <= 0x7ff then
+ return string_char(math_floor(c / 64) + 192, c % 64 + 128)
+ elseif c <= 0xffff then
+ return string_char(
+ math_floor(c / 4096) + 224,
+ math_floor(c % 4096 / 64) + 128,
+ c % 64 + 128
+ )
+ elseif c <= 0x10ffff then
+ return string_char(
+ math_floor(c / 262144) + 240,
+ math_floor(c % 262144 / 4096) + 128,
+ math_floor(c % 4096 / 64) + 128,
+ c % 64 + 128
+ )
+ end
+ error(string.format("invalid UTF-8 code '%x'", c))
+ end
+ function math_type(v)
+ if v >= -2147483648 and v <= 2147483647 and math_floor(v) == v then
+ return "integer"
+ end
+ return "float"
+ end
+else
+ utf8_char = utf8.char
+ math_type = math.type
+end
+
+local json = {}
+
+json.supportSparseArray = true
+
+local objectMt = {}
+
+function json.createEmptyObject()
+ return setmetatable({}, objectMt)
+end
+
+function json.isObject(t)
+ if t[1] ~= nil then
+ return false
+ end
+ return next(t) ~= nil or getmetatable(t) == objectMt
+end
+
+if debug and debug.upvalueid then
+ -- Generate a lightuserdata
+ json.null = debug.upvalueid(json.createEmptyObject, 1)
+else
+ json.null = function() end
+end
+
+-- json.encode --
+
+local statusVisited
+local statusBuilder
+local statusDep
+local statusOpt
+
+local defaultOpt = {
+ newline = "",
+ indent = "",
+}
+defaultOpt.__index = defaultOpt
+
+local encode_map = {}
+
+local encode_escape_map = {
+ [ "\"" ] = "\\\"",
+ [ "\\" ] = "\\\\",
+ [ "/" ] = "\\/",
+ [ "\b" ] = "\\b",
+ [ "\f" ] = "\\f",
+ [ "\n" ] = "\\n",
+ [ "\r" ] = "\\r",
+ [ "\t" ] = "\\t",
+}
+
+local decode_escape_set = {}
+local decode_escape_map = {}
+for k, v in next, encode_escape_map do
+ decode_escape_map[v] = k
+ decode_escape_set[string_byte(v, 2)] = true
+end
+
+for i = 0, 31 do
+ local c = string_char(i)
+ if not encode_escape_map[c] then
+ encode_escape_map[c] = string_format("\\u%04x", i)
+ end
+end
+
+encode_map["nil"] = function ()
+ return "null"
+end
+
+local function encode_string(v)
+ return string_gsub(v, '[%z\1-\31\\"]', encode_escape_map)
+end
+
+local function convertreal(v)
+ local g = string_format('%.16g', v)
+ if tonumber(g) == v then
+ return g
+ end
+ return string_format('%.17g', v)
+end
+
+if string_match(tostring(1/2), "%p") == "," then
+ local _convertreal = convertreal
+ function convertreal(v)
+ return string_gsub(_convertreal(v), ',', '.')
+ end
+end
+
+function encode_map.number(v)
+ if v ~= v or v <= tiny or v >= huge then
+ error("unexpected number value '" .. tostring(v) .. "'")
+ end
+ if math_type(v) == "integer" then
+ return string_format('%d', v)
+ end
+ return convertreal(v)
+end
+
+function encode_map.boolean(v)
+ if v then
+ return "true"
+ else
+ return "false"
+ end
+end
+
+local function encode_unexpected(v)
+ if v == json.null then
+ return "null"
+ else
+ error("unexpected type '"..type(v).."'")
+ end
+end
+encode_map[ "function" ] = encode_unexpected
+encode_map[ "userdata" ] = encode_unexpected
+encode_map[ "thread" ] = encode_unexpected
+
+local function encode_newline()
+ statusBuilder[#statusBuilder+1] = statusOpt.newline..string_rep(statusOpt.indent, statusDep)
+end
+
+local function encode(v)
+ local res = encode_map[type(v)](v)
+ statusBuilder[#statusBuilder+1] = res
+end
+
+function encode_map.string(v)
+ statusBuilder[#statusBuilder+1] = '"'
+ statusBuilder[#statusBuilder+1] = encode_string(v)
+ return '"'
+end
+
+function encode_map.table(t)
+ local first_val = next(t)
+ if first_val == nil then
+ if getmetatable(t) == objectMt then
+ return "{}"
+ else
+ return "[]"
+ end
+ end
+ if statusVisited[t] then
+ error("circular reference")
+ end
+ statusVisited[t] = true
+ if type(first_val) == 'string' then
+ local key = {}
+ for k in next, t do
+ if type(k) ~= "string" then
+ error("invalid table: mixed or invalid key types")
+ end
+ key[#key+1] = k
+ end
+ table_sort(key)
+ statusBuilder[#statusBuilder+1] = "{"
+ statusDep = statusDep + 1
+ encode_newline()
+ local k = key[1]
+ statusBuilder[#statusBuilder+1] = '"'
+ statusBuilder[#statusBuilder+1] = encode_string(k)
+ statusBuilder[#statusBuilder+1] = '": '
+ encode(t[k])
+ for i = 2, #key do
+ local k = key[i]
+ statusBuilder[#statusBuilder+1] = ","
+ encode_newline()
+ statusBuilder[#statusBuilder+1] = '"'
+ statusBuilder[#statusBuilder+1] = encode_string(k)
+ statusBuilder[#statusBuilder+1] = '": '
+ encode(t[k])
+ end
+ statusDep = statusDep - 1
+ encode_newline()
+ statusVisited[t] = nil
+ return "}"
+ elseif json.supportSparseArray then
+ local max = 0
+ for k in next, t do
+ if math_type(k) ~= "integer" or k <= 0 then
+ error("invalid table: mixed or invalid key types")
+ end
+ if max < k then
+ max = k
+ end
+ end
+ statusBuilder[#statusBuilder+1] = "["
+ statusDep = statusDep + 1
+ encode_newline()
+ encode(t[1])
+ for i = 2, max do
+ statusBuilder[#statusBuilder+1] = ","
+ encode_newline()
+ encode(t[i])
+ end
+ statusDep = statusDep - 1
+ encode_newline()
+ statusVisited[t] = nil
+ return "]"
+ else
+ if t[1] == nil then
+ error("invalid table: mixed or invalid key types")
+ end
+ statusBuilder[#statusBuilder+1] = "["
+ statusDep = statusDep + 1
+ encode_newline()
+ encode(t[1])
+ local count = 2
+ while t[count] ~= nil do
+ statusBuilder[#statusBuilder+1] = ","
+ encode_newline()
+ encode(t[count])
+ count = count + 1
+ end
+ if next(t, count-1) ~= nil then
+ error("invalid table: mixed or invalid key types")
+ end
+ statusDep = statusDep - 1
+ encode_newline()
+ statusVisited[t] = nil
+ return "]"
+ end
+end
+
+function json.encode(v, option)
+ statusVisited = {}
+ statusBuilder = {}
+ statusDep = 0
+ statusOpt = option and setmetatable(option, defaultOpt) or defaultOpt
+ encode(v)
+ return table_concat(statusBuilder)
+end
+
+-- json.decode --
+
+local statusBuf
+local statusPos
+local statusTop
+local statusAry = {}
+local statusRef = {}
+
+local function find_line()
+ local line = 1
+ local pos = 1
+ while true do
+ local f, _, nl1, nl2 = string_find(statusBuf, '([\n\r])([\n\r]?)', pos)
+ if not f then
+ return line, statusPos - pos + 1
+ end
+ local newpos = f + ((nl1 == nl2 or nl2 == '') and 1 or 2)
+ if newpos > statusPos then
+ return line, statusPos - pos + 1
+ end
+ pos = newpos
+ line = line + 1
+ end
+end
+
+local function decode_error(msg)
+ error(string_format("ERROR: %s at line %d col %d", msg, find_line()), 2)
+end
+
+local function get_word()
+ return string_match(statusBuf, "^[^ \t\r\n%]},]*", statusPos)
+end
+
+local function skip_comment(b)
+ if b ~= 47 --[[ '/' ]] then
+ return
+ end
+ local c = string_byte(statusBuf, statusPos+1)
+ if c == 42 --[[ '*' ]] then
+ -- block comment
+ local pos = string_find(statusBuf, "*/", statusPos)
+ if pos then
+ statusPos = pos + 2
+ else
+ statusPos = #statusBuf + 1
+ end
+ return true
+ elseif c == 47 --[[ '/' ]] then
+ -- line comment
+ local pos = string_find(statusBuf, "[\r\n]", statusPos)
+ if pos then
+ statusPos = pos
+ else
+ statusPos = #statusBuf + 1
+ end
+ return true
+ end
+end
+
+local function next_byte()
+ local pos = string_find(statusBuf, "[^ \t\r\n]", statusPos)
+ if pos then
+ statusPos = pos
+ local b = string_byte(statusBuf, pos)
+ if not skip_comment(b) then
+ return b
+ end
+ return next_byte()
+ end
+ return -1
+end
+
+local function decode_unicode_surrogate(s1, s2)
+ return utf8_char(0x10000 + (tonumber(s1, 16) - 0xd800) * 0x400 + (tonumber(s2, 16) - 0xdc00))
+end
+
+local function decode_unicode_escape(s)
+ return utf8_char(tonumber(s, 16))
+end
+
+local function decode_string()
+ local has_unicode_escape = false
+ local has_escape = false
+ local i = statusPos + 1
+ while true do
+ i = string_find(statusBuf, '[%z\1-\31\\"]', i)
+ if not i then
+ decode_error "expected closing quote for string"
+ end
+ local x = string_byte(statusBuf, i)
+ if x < 32 then
+ statusPos = i
+ decode_error "control character in string"
+ end
+ if x == 34 --[[ '"' ]] then
+ local s = string_sub(statusBuf, statusPos + 1, i - 1)
+ if has_unicode_escape then
+ s = string_gsub(string_gsub(s
+ , "\\u([dD][89aAbB]%x%x)\\u([dD][c-fC-F]%x%x)", decode_unicode_surrogate)
+ , "\\u(%x%x%x%x)", decode_unicode_escape)
+ end
+ if has_escape then
+ s = string_gsub(s, "\\.", decode_escape_map)
+ end
+ statusPos = i + 1
+ return s
+ end
+ --assert(x == 92 --[[ "\\" ]])
+ local nx = string_byte(statusBuf, i+1)
+ if nx == 117 --[[ "u" ]] then
+ if not string_match(statusBuf, "^%x%x%x%x", i+2) then
+ statusPos = i
+ decode_error "invalid unicode escape in string"
+ end
+ has_unicode_escape = true
+ i = i + 6
+ else
+ if not decode_escape_set[nx] then
+ statusPos = i
+ decode_error("invalid escape char '" .. (nx and string_char(nx) or "<eol>") .. "' in string")
+ end
+ has_escape = true
+ i = i + 2
+ end
+ end
+end
+
+local function decode_number()
+ local num, c = string_match(statusBuf, '^([0-9]+%.?[0-9]*)([eE]?)', statusPos)
+ if not num or string_byte(num, -1) == 0x2E --[[ "." ]] then
+ decode_error("invalid number '" .. get_word() .. "'")
+ end
+ if c ~= '' then
+ num = string_match(statusBuf, '^([^eE]*[eE][-+]?[0-9]+)[ \t\r\n%]},/]', statusPos)
+ if not num then
+ decode_error("invalid number '" .. get_word() .. "'")
+ end
+ end
+ statusPos = statusPos + #num
+ return tonumber(num)
+end
+
+local function decode_number_zero()
+ local num, c = string_match(statusBuf, '^(.%.?[0-9]*)([eE]?)', statusPos)
+ if not num or string_byte(num, -1) == 0x2E --[[ "." ]] or string_match(statusBuf, '^.[0-9]+', statusPos) then
+ decode_error("invalid number '" .. get_word() .. "'")
+ end
+ if c ~= '' then
+ num = string_match(statusBuf, '^([^eE]*[eE][-+]?[0-9]+)[ \t\r\n%]},/]', statusPos)
+ if not num then
+ decode_error("invalid number '" .. get_word() .. "'")
+ end
+ end
+ statusPos = statusPos + #num
+ return tonumber(num)
+end
+
+local function decode_number_negative()
+ statusPos = statusPos + 1
+ local c = string_byte(statusBuf, statusPos)
+ if c then
+ if c == 0x30 then
+ return -decode_number_zero()
+ elseif c > 0x30 and c < 0x3A then
+ return -decode_number()
+ end
+ end
+ decode_error("invalid number '" .. get_word() .. "'")
+end
+
+local function decode_true()
+ if string_sub(statusBuf, statusPos, statusPos+3) ~= "true" then
+ decode_error("invalid literal '" .. get_word() .. "'")
+ end
+ statusPos = statusPos + 4
+ return true
+end
+
+local function decode_false()
+ if string_sub(statusBuf, statusPos, statusPos+4) ~= "false" then
+ decode_error("invalid literal '" .. get_word() .. "'")
+ end
+ statusPos = statusPos + 5
+ return false
+end
+
+local function decode_null()
+ if string_sub(statusBuf, statusPos, statusPos+3) ~= "null" then
+ decode_error("invalid literal '" .. get_word() .. "'")
+ end
+ statusPos = statusPos + 4
+ return json.null
+end
+
+local function decode_array()
+ statusPos = statusPos + 1
+ local res = {}
+ local chr = next_byte()
+ if chr == 93 --[[ ']' ]] then
+ statusPos = statusPos + 1
+ return res
+ end
+ statusTop = statusTop + 1
+ statusAry[statusTop] = true
+ statusRef[statusTop] = res
+ return res
+end
+
+local function decode_object()
+ statusPos = statusPos + 1
+ local res = {}
+ local chr = next_byte()
+ if chr == 125 --[[ ']' ]] then
+ statusPos = statusPos + 1
+ return json.createEmptyObject()
+ end
+ statusTop = statusTop + 1
+ statusAry[statusTop] = false
+ statusRef[statusTop] = res
+ return res
+end
+
+local decode_uncompleted_map = {
+ [ string_byte '"' ] = decode_string,
+ [ string_byte "0" ] = decode_number_zero,
+ [ string_byte "1" ] = decode_number,
+ [ string_byte "2" ] = decode_number,
+ [ string_byte "3" ] = decode_number,
+ [ string_byte "4" ] = decode_number,
+ [ string_byte "5" ] = decode_number,
+ [ string_byte "6" ] = decode_number,
+ [ string_byte "7" ] = decode_number,
+ [ string_byte "8" ] = decode_number,
+ [ string_byte "9" ] = decode_number,
+ [ string_byte "-" ] = decode_number_negative,
+ [ string_byte "t" ] = decode_true,
+ [ string_byte "f" ] = decode_false,
+ [ string_byte "n" ] = decode_null,
+ [ string_byte "[" ] = decode_array,
+ [ string_byte "{" ] = decode_object,
+}
+local function unexpected_character()
+ decode_error("unexpected character '" .. string_sub(statusBuf, statusPos, statusPos) .. "'")
+end
+local function unexpected_eol()
+ decode_error("unexpected character '<eol>'")
+end
+
+local decode_map = {}
+for i = 0, 255 do
+ decode_map[i] = decode_uncompleted_map[i] or unexpected_character
+end
+decode_map[-1] = unexpected_eol
+
+local function decode()
+ return decode_map[next_byte()]()
+end
+
+local function decode_item()
+ local top = statusTop
+ local ref = statusRef[top]
+ if statusAry[top] then
+ ref[#ref+1] = decode()
+ else
+ local key = decode_string()
+ if next_byte() ~= 58 --[[ ':' ]] then
+ decode_error "expected ':'"
+ end
+ statusPos = statusPos + 1
+ ref[key] = decode()
+ end
+ if top == statusTop then
+ repeat
+ local chr = next_byte(); statusPos = statusPos + 1
+ if chr == 44 --[[ "," ]] then
+ local c = next_byte()
+ if statusAry[statusTop] then
+ if c ~= 93 --[[ "]" ]] then return end
+ else
+ if c ~= 125 --[[ "}" ]] then return end
+ end
+ statusPos = statusPos + 1
+ else
+ if statusAry[statusTop] then
+ if chr ~= 93 --[[ "]" ]] then decode_error "expected ']' or ','" end
+ else
+ if chr ~= 125 --[[ "}" ]] then decode_error "expected '}' or ','" end
+ end
+ end
+ statusTop = statusTop - 1
+ until statusTop == 0
+ end
+end
+
+function json.decode(str)
+ if type(str) ~= "string" then
+ error("expected argument of type string, got " .. type(str))
+ end
+ statusBuf = str
+ statusPos = 1
+ statusTop = 0
+ if next_byte() == -1 then
+ return json.null
+ end
+ local res = decode()
+ while statusTop > 0 do
+ decode_item()
+ end
+ if string_find(statusBuf, "[^ \t\r\n]", statusPos) then
+ decode_error "trailing garbage"
+ end
+ return res
+end
+
+return json
diff --git a/script/language.lua b/script/language.lua
index 771dc948..22546fb8 100644
--- a/script/language.lua
+++ b/script/language.lua
@@ -6,7 +6,9 @@ local function supportLanguage()
local list = {}
for path in fs.pairs(ROOT / 'locale') do
if fs.is_directory(path) then
- list[#list+1] = path:filename():string():lower()
+ local id = path:filename():string():lower()
+ list[#list+1] = id
+ list[id] = true
end
end
return list
diff --git a/script/log.lua b/script/log.lua
index 597bdc4e..6cb865c3 100644
--- a/script/log.lua
+++ b/script/log.lua
@@ -85,7 +85,10 @@ function m.warn(...)
end
function m.error(...)
- return pushLog('error', ...)
+ -- Don't use tail calls,
+ -- Otherwise, the count of `debug.getinfo` will be wrong
+ local msg = pushLog('error', ...)
+ return msg
end
function m.raw(thd, level, msg, source, currentline, clock)
diff --git a/script/parser/guide.lua b/script/parser/guide.lua
index 0ece65fc..06169b09 100644
--- a/script/parser/guide.lua
+++ b/script/parser/guide.lua
@@ -16,6 +16,7 @@ local type = type
---@field uri uri
---@field start integer
---@field finish integer
+---@field range integer
---@field effect integer
---@field attrs string[]
---@field specials parser.object[]
@@ -56,6 +57,14 @@ local type = type
---@field init parser.object
---@field step parser.object
---@field redundant { max: integer, passed: integer }
+---@field filter parser.object
+---@field loc parser.object
+---@field keyword integer[]
+---@field casts parser.object[]
+---@field mode? '+' | '-'
+---@field hasGoTo? true
+---@field hasReturn? true
+---@field hasBreak? true
---@field _root parser.object
---@class guide
@@ -71,6 +80,7 @@ local blockTypes = {
['repeat'] = true,
['do'] = true,
['function'] = true,
+ ['if'] = true,
['ifblock'] = true,
['elseblock'] = true,
['elseifblock'] = true,
@@ -141,6 +151,9 @@ local childMap = {
['doc.see'] = {'name', 'field'},
['doc.version'] = {'#versions'},
['doc.diagnostic'] = {'#names'},
+ ['doc.as'] = {'as'},
+ ['doc.cast'] = {'loc', '#casts'},
+ ['doc.cast.block'] = {'extends'},
}
---@type table<string, fun(obj: parser.object, list: parser.object[])>
@@ -393,6 +406,7 @@ function m.getRoot(obj)
end
local parent = obj.parent
if not parent then
+ log.error('Can not find out root:', obj.type)
return nil
end
obj = parent
@@ -413,6 +427,7 @@ function m.getUri(obj)
return ''
end
+---@return parser.object?
function m.getENV(source, start)
if not start then
start = 1
@@ -446,19 +461,17 @@ function m.getFunctionVarArgs(func)
end
--- 获取指定区块中可见的局部变量
----@param block table
----@param name string {comment = '变量名'}
----@param pos integer {comment = '可见位置'}
-function m.getLocal(block, name, pos)
- block = m.getBlock(block)
- for _ = 1, 10000 do
- if not block then
- return nil
- end
- local locals = block.locals
- local res
+---@param source parser.object
+---@param name string # 变量名
+---@param pos integer # 可见位置
+---@return parser.object?
+function m.getLocal(source, name, pos)
+ local root = m.getRoot(source)
+ local res
+ m.eachSourceContain(root, pos, function (src)
+ local locals = src.locals
if not locals then
- goto CONTINUE
+ return
end
for i = 1, #locals do
local loc = locals[i]
@@ -471,13 +484,8 @@ function m.getLocal(block, name, pos)
end
end
end
- if res then
- return res, res
- end
- ::CONTINUE::
- block = m.getParentBlock(block)
- end
- error('guide.getLocal overstack')
+ end)
+ return res
end
--- 获取指定区块中所有的可见局部变量名称
@@ -602,6 +610,9 @@ local function addChilds(list, obj)
end
--- 遍历所有包含position的source
+---@param ast parser.object
+---@param position integer
+---@param callback fun(src: parser.object)
function m.eachSourceContain(ast, position, callback)
local list = { ast }
local mark = {}
@@ -922,6 +933,7 @@ function m.getKeyNameOfLiteral(obj)
end
end
+---@return string?
function m.getKeyName(obj)
if not obj then
return nil
@@ -1027,8 +1039,6 @@ function m.getKeyType(obj)
return type(obj.field[1])
elseif tp == 'doc.type.field' then
return type(obj.name[1])
- elseif tp == 'dummy' then
- return 'string'
end
if tp == 'doc.field.name' then
return type(obj[1])
diff --git a/script/parser/luadoc.lua b/script/parser/luadoc.lua
index 5a2e1d09..d8e31950 100644
--- a/script/parser/luadoc.lua
+++ b/script/parser/luadoc.lua
@@ -2,10 +2,11 @@ local m = require 'lpeglabel'
local re = require 'parser.relabel'
local guide = require 'parser.guide'
local parser = require 'parser.newparser'
+local util = require 'utility'
local TokenTypes, TokenStarts, TokenFinishs, TokenContents, TokenMarks
local Ci, Offset, pushWarning, NextComment, Lines
-local parseType
+local parseType, parseTypeUnit
---@type any
local Parser = re.compile([[
Main <- (Token / Sp)*
@@ -52,6 +53,7 @@ Symbol <- ({} {
/ '...'
/ '['
/ ']'
+ / '-' !'-'
} {})
-> Symbol
]], {
@@ -124,6 +126,8 @@ Symbol <- ({} {
---@class parser.object
---@field literal boolean
---@field signs parser.object[]
+---@field originalComment parser.object
+---@field as? parser.object
local function trim(str)
return str:match '^%s*(%S+)%s*$'
@@ -336,104 +340,6 @@ local function parseSigns(parent)
return signs
end
-local function parseClass(parent)
- local result = {
- type = 'doc.class',
- parent = parent,
- fields = {},
- }
- result.class = parseName('doc.class.name', result)
- if not result.class then
- pushWarning {
- type = 'LUADOC_MISS_CLASS_NAME',
- start = getFinish(),
- finish = getFinish(),
- }
- return nil
- end
- result.start = getStart()
- result.finish = getFinish()
- result.signs = parseSigns(result)
- if not checkToken('symbol', ':', 1) then
- return result
- end
- nextToken()
-
- result.extends = {}
-
- while true do
- local extend = parseName('doc.extends.name', result)
- or parseTable(result)
- if not extend then
- pushWarning {
- type = 'LUADOC_MISS_CLASS_EXTENDS_NAME',
- start = getFinish(),
- finish = getFinish(),
- }
- return result
- end
- result.extends[#result.extends+1] = extend
- result.finish = getFinish()
- if not checkToken('symbol', ',', 1) then
- break
- end
- nextToken()
- end
- return result
-end
-
-local function parseTypeUnitArray(parent, node)
- if not checkToken('symbol', '[]', 1) then
- return nil
- end
- nextToken()
- local result = {
- type = 'doc.type.array',
- start = node.start,
- finish = getFinish(),
- node = node,
- parent = parent,
- }
- node.parent = result
- return result
-end
-
-local function parseTypeUnitSign(parent, node)
- if not checkToken('symbol', '<', 1) then
- return nil
- end
- nextToken()
- local result = {
- type = 'doc.type.sign',
- start = node.start,
- finish = getFinish(),
- node = node,
- parent = parent,
- signs = {},
- }
- node.parent = result
- while true do
- local sign = parseType(result)
- if not sign then
- pushWarning {
- type = 'LUA_DOC_MISS_SIGN',
- start = getFinish(),
- finish = getFinish(),
- }
- break
- end
- result.signs[#result.signs+1] = sign
- if checkToken('symbol', ',', 1) then
- nextToken()
- else
- break
- end
- end
- nextSymbolOrError '>'
- result.finish = getFinish()
- return result
-end
-
local function parseDots(tp, parent)
if not checkToken('symbol', '...', 1) then
return
@@ -527,8 +433,6 @@ local function parseTypeUnitFunction(parent)
return typeUnit
end
-local parseTypeUnit
-
local function parseFunction(parent)
local _, content = peekToken()
if content == 'async' then
@@ -551,6 +455,58 @@ local function parseFunction(parent)
end
end
+local function parseTypeUnitArray(parent, node)
+ if not checkToken('symbol', '[]', 1) then
+ return nil
+ end
+ nextToken()
+ local result = {
+ type = 'doc.type.array',
+ start = node.start,
+ finish = getFinish(),
+ node = node,
+ parent = parent,
+ }
+ node.parent = result
+ return result
+end
+
+local function parseTypeUnitSign(parent, node)
+ if not checkToken('symbol', '<', 1) then
+ return nil
+ end
+ nextToken()
+ local result = {
+ type = 'doc.type.sign',
+ start = node.start,
+ finish = getFinish(),
+ node = node,
+ parent = parent,
+ signs = {},
+ }
+ node.parent = result
+ while true do
+ local sign = parseType(result)
+ if not sign then
+ pushWarning {
+ type = 'LUA_DOC_MISS_SIGN',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ break
+ end
+ result.signs[#result.signs+1] = sign
+ if checkToken('symbol', ',', 1) then
+ nextToken()
+ else
+ break
+ end
+ end
+ nextSymbolOrError '>'
+ result.finish = getFinish()
+ return result
+end
+
local function parseString(parent)
local tp, content = peekToken()
if not tp or tp ~= 'string' then
@@ -709,6 +665,10 @@ function parseType(parent)
if not result.start then
result.start = getFinish()
end
+ if checkToken('symbol', '?', 1) then
+ nextToken()
+ result.optional = true
+ end
result.finish = getFinish()
result.firstFinish = result.finish
@@ -785,405 +745,534 @@ function parseType(parent)
return result
end
-local function parseAlias()
- local result = {
- type = 'doc.alias',
- }
- result.alias = parseName('doc.alias.name', result)
- if not result.alias then
- pushWarning {
- type = 'LUADOC_MISS_ALIAS_NAME',
- start = getFinish(),
- finish = getFinish(),
- }
- return nil
- end
- result.start = getStart()
- result.signs = parseSigns(result)
- result.extends = parseType(result)
- if not result.extends then
- pushWarning {
- type = 'LUADOC_MISS_ALIAS_EXTENDS',
- start = getFinish(),
- finish = getFinish(),
- }
- return nil
- end
- result.finish = getFinish()
- return result
-end
-
-local function parseParam()
- local result = {
- type = 'doc.param',
- }
- result.param = parseName('doc.param.name', result)
- or parseDots('doc.param.name', result)
- if not result.param then
- pushWarning {
- type = 'LUADOC_MISS_PARAM_NAME',
- start = getFinish(),
- finish = getFinish(),
+local docSwitch = util.switch()
+ : case 'class'
+ : call(function ()
+ local result = {
+ type = 'doc.class',
+ fields = {},
}
- return nil
- end
- if checkToken('symbol', '?', 1) then
- nextToken()
- result.optional = true
- end
- result.start = result.param.start
- result.finish = getFinish()
- result.extends = parseType(result)
- if not result.extends then
- pushWarning {
- type = 'LUADOC_MISS_PARAM_EXTENDS',
- start = getFinish(),
- finish = getFinish(),
- }
- return result
- end
- result.finish = getFinish()
- result.firstFinish = result.extends.firstFinish
- return result
-end
-
-local function parseReturn()
- local result = {
- type = 'doc.return',
- returns = {},
- }
- while true do
- local docType = parseType(result)
- if not docType then
- break
- end
- if not result.start then
- result.start = docType.start
- end
- if checkToken('symbol', '?', 1) then
- nextToken()
- docType.optional = true
+ result.class = parseName('doc.class.name', result)
+ if not result.class then
+ pushWarning {
+ type = 'LUADOC_MISS_CLASS_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
end
- docType.name = parseName('doc.return.name', docType)
- result.returns[#result.returns+1] = docType
- if not checkToken('symbol', ',', 1) then
- break
+ result.start = getStart()
+ result.finish = getFinish()
+ result.signs = parseSigns(result)
+ if not checkToken('symbol', ':', 1) then
+ return result
end
nextToken()
- end
- if #result.returns == 0 then
- return nil
- end
- result.finish = getFinish()
- return result
-end
-local function parseField()
- local result = {
- type = 'doc.field',
- }
- try(function ()
- local tp, value = nextToken()
- if tp == 'name' then
- if value == 'public'
- or value == 'protected'
- or value == 'private' then
- result.visible = value
- result.start = getStart()
- return true
+ result.extends = {}
+
+ while true do
+ local extend = parseName('doc.extends.name', result)
+ or parseTable(result)
+ if not extend then
+ pushWarning {
+ type = 'LUADOC_MISS_CLASS_EXTENDS_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return result
end
+ result.extends[#result.extends+1] = extend
+ result.finish = getFinish()
+ if not checkToken('symbol', ',', 1) then
+ break
+ end
+ nextToken()
end
- return false
+ return result
end)
- result.field = parseName('doc.field.name', result)
- or parseIndexField('doc.field.name', result)
- if not result.field then
- pushWarning {
- type = 'LUADOC_MISS_FIELD_NAME',
- start = getFinish(),
- finish = getFinish(),
- }
- return nil
- end
- if not result.start then
- result.start = result.field.start
- end
- if checkToken('symbol', '?', 1) then
- nextToken()
- result.optional = true
- end
- result.extends = parseType(result)
- if not result.extends then
- pushWarning {
- type = 'LUADOC_MISS_FIELD_EXTENDS',
- start = getFinish(),
- finish = getFinish(),
- }
- return nil
- end
- result.finish = getFinish()
- return result
-end
-
-local function parseGeneric()
- local result = {
- type = 'doc.generic',
- generics = {},
- }
- while true do
- local object = {
- type = 'doc.generic.object',
- parent = result,
+ : case 'type'
+ : call(function ()
+ return parseType()
+ end)
+ : case 'alias'
+ : call(function ()
+ local result = {
+ type = 'doc.alias',
}
- object.generic = parseName('doc.generic.name', object)
- if not object.generic then
+ result.alias = parseName('doc.alias.name', result)
+ if not result.alias then
pushWarning {
- type = 'LUADOC_MISS_GENERIC_NAME',
+ type = 'LUADOC_MISS_ALIAS_NAME',
start = getFinish(),
finish = getFinish(),
}
return nil
end
- object.start = object.generic.start
- if not result.start then
- result.start = object.start
+ result.start = getStart()
+ result.signs = parseSigns(result)
+ result.extends = parseType(result)
+ if not result.extends then
+ pushWarning {
+ type = 'LUADOC_MISS_ALIAS_EXTENDS',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
end
- if checkToken('symbol', ':', 1) then
+ result.finish = getFinish()
+ return result
+ end)
+ : case 'param'
+ : call(function ()
+ local result = {
+ type = 'doc.param',
+ }
+ result.param = parseName('doc.param.name', result)
+ or parseDots('doc.param.name', result)
+ if not result.param then
+ pushWarning {
+ type = 'LUADOC_MISS_PARAM_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ if checkToken('symbol', '?', 1) then
nextToken()
- object.extends = parseType(object)
+ result.optional = true
end
- object.finish = getFinish()
- result.generics[#result.generics+1] = object
- if not checkToken('symbol', ',', 1) then
- break
+ result.start = result.param.start
+ result.finish = getFinish()
+ result.extends = parseType(result)
+ if not result.extends then
+ pushWarning {
+ type = 'LUADOC_MISS_PARAM_EXTENDS',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return result
end
- nextToken()
- end
- result.finish = getFinish()
- return result
-end
-
-local function parseVararg()
- local result = {
- type = 'doc.vararg',
- }
- result.vararg = parseType(result)
- if not result.vararg then
- pushWarning {
- type = 'LUADOC_MISS_VARARG_TYPE',
- start = getFinish(),
- finish = getFinish(),
+ result.finish = getFinish()
+ result.firstFinish = result.extends.firstFinish
+ return result
+ end)
+ : case 'return'
+ : call(function ()
+ local result = {
+ type = 'doc.return',
+ returns = {},
}
- return
- end
- result.start = result.vararg.start
- result.finish = result.vararg.finish
- return result
-end
-
-local function parseOverload()
- local tp, name = peekToken()
- if tp ~= 'name'
- or (name ~= 'fun' and name ~= 'async') then
- pushWarning {
- type = 'LUADOC_MISS_FUN_AFTER_OVERLOAD',
- start = getFinish(),
- finish = getFinish(),
+ while true do
+ local docType = parseType(result)
+ if not docType then
+ break
+ end
+ if not result.start then
+ result.start = docType.start
+ end
+ if checkToken('symbol', '?', 1) then
+ nextToken()
+ docType.optional = true
+ end
+ docType.name = parseName('doc.return.name', docType)
+ result.returns[#result.returns+1] = docType
+ if not checkToken('symbol', ',', 1) then
+ break
+ end
+ nextToken()
+ end
+ if #result.returns == 0 then
+ return nil
+ end
+ result.finish = getFinish()
+ return result
+ end)
+ : case 'field'
+ : call(function ()
+ local result = {
+ type = 'doc.field',
}
- return nil
- end
- local result = {
- type = 'doc.overload',
- }
- result.overload = parseFunction(result)
- if not result.overload then
- return nil
- end
- result.overload.parent = result
- result.start = result.overload.start
- result.finish = result.overload.finish
- return result
-end
-
-local function parseDeprecated()
- return {
- type = 'doc.deprecated',
- start = getFinish(),
- finish = getFinish(),
- }
-end
-
-local function parseMeta()
- return {
- type = 'doc.meta',
- start = getFinish(),
- finish = getFinish(),
- }
-end
-
-local function parseVersion()
- local result = {
- type = 'doc.version',
- versions = {},
- }
- while true do
- local tp, text = nextToken()
- if not tp then
+ try(function ()
+ local tp, value = nextToken()
+ if tp == 'name' then
+ if value == 'public'
+ or value == 'protected'
+ or value == 'private' then
+ result.visible = value
+ result.start = getStart()
+ return true
+ end
+ end
+ return false
+ end)
+ result.field = parseName('doc.field.name', result)
+ or parseIndexField('doc.field.name', result)
+ if not result.field then
pushWarning {
- type = 'LUADOC_MISS_VERSION',
+ type = 'LUADOC_MISS_FIELD_NAME',
start = getFinish(),
finish = getFinish(),
}
- break
+ return nil
end
if not result.start then
- result.start = getStart()
+ result.start = result.field.start
end
- local version = {
- type = 'doc.version.unit',
- parent = result,
- start = getStart(),
+ if checkToken('symbol', '?', 1) then
+ nextToken()
+ result.optional = true
+ end
+ result.extends = parseType(result)
+ if not result.extends then
+ pushWarning {
+ type = 'LUADOC_MISS_FIELD_EXTENDS',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ result.finish = getFinish()
+ return result
+ end)
+ : case 'generic'
+ : call(function ()
+ local result = {
+ type = 'doc.generic',
+ generics = {},
}
- if tp == 'symbol' then
- if text == '>' then
- version.ge = true
- elseif text == '<' then
- version.le = true
+ while true do
+ local object = {
+ type = 'doc.generic.object',
+ parent = result,
+ }
+ object.generic = parseName('doc.generic.name', object)
+ if not object.generic then
+ pushWarning {
+ type = 'LUADOC_MISS_GENERIC_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ object.start = object.generic.start
+ if not result.start then
+ result.start = object.start
end
- tp, text = nextToken()
+ if checkToken('symbol', ':', 1) then
+ nextToken()
+ object.extends = parseType(object)
+ end
+ object.finish = getFinish()
+ result.generics[#result.generics+1] = object
+ if not checkToken('symbol', ',', 1) then
+ break
+ end
+ nextToken()
end
- if tp ~= 'name' then
+ result.finish = getFinish()
+ return result
+ end)
+ : case 'vararg'
+ : call(function ()
+ local result = {
+ type = 'doc.vararg',
+ }
+ result.vararg = parseType(result)
+ if not result.vararg then
pushWarning {
- type = 'LUADOC_MISS_VERSION',
- start = getStart(),
+ type = 'LUADOC_MISS_VARARG_TYPE',
+ start = getFinish(),
finish = getFinish(),
}
- break
+ return
end
- version.version = tonumber(text) or text
- version.finish = getFinish()
- result.versions[#result.versions+1] = version
- if not checkToken('symbol', ',', 1) then
- break
+ result.start = result.vararg.start
+ result.finish = result.vararg.finish
+ return result
+ end)
+ : case 'overload'
+ : call(function ()
+ local tp, name = peekToken()
+ if tp ~= 'name'
+ or (name ~= 'fun' and name ~= 'async') then
+ pushWarning {
+ type = 'LUADOC_MISS_FUN_AFTER_OVERLOAD',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
end
- nextToken()
- end
- if #result.versions == 0 then
- return nil
- end
- result.finish = getFinish()
- return result
-end
-
-local function parseSee()
- local result = {
- type = 'doc.see',
- }
- result.name = parseName('doc.see.name', result)
- if not result.name then
- return nil
- end
- result.start = result.name.start
- result.finish = result.name.finish
- if checkToken('symbol', '#', 1) then
- nextToken()
- result.field = parseName('doc.see.field', result)
- result.finish = getFinish()
- end
- return result
-end
-
-local function parseDiagnostic()
- local result = {
- type = 'doc.diagnostic',
- }
- local nextTP, mode = nextToken()
- if nextTP ~= 'name' then
- pushWarning {
- type = 'LUADOC_MISS_DIAG_MODE',
+ local result = {
+ type = 'doc.overload',
+ }
+ result.overload = parseFunction(result)
+ if not result.overload then
+ return nil
+ end
+ result.overload.parent = result
+ result.start = result.overload.start
+ result.finish = result.overload.finish
+ return result
+ end)
+ : case 'deprecated'
+ : call(function ()
+ return {
+ type = 'doc.deprecated',
start = getFinish(),
finish = getFinish(),
}
- return nil
- end
- result.mode = mode
- result.start = getStart()
- result.finish = getFinish()
- if mode ~= 'disable-next-line'
- and mode ~= 'disable-line'
- and mode ~= 'disable'
- and mode ~= 'enable' then
- pushWarning {
- type = 'LUADOC_ERROR_DIAG_MODE',
- start = result.start,
- finish = result.finish,
+ end)
+ : case 'meta'
+ : call(function ()
+ return {
+ type = 'doc.meta',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ end)
+ : case 'version'
+ : call(function ()
+ local result = {
+ type = 'doc.version',
+ versions = {},
}
- end
-
- if checkToken('symbol', ':', 1) then
- nextToken()
- result.names = {}
while true do
- local name = parseName('doc.diagnostic.name', result)
- if not name then
+ local tp, text = nextToken()
+ if not tp then
pushWarning {
- type = 'LUADOC_MISS_DIAG_NAME',
+ type = 'LUADOC_MISS_VERSION',
start = getFinish(),
finish = getFinish(),
}
- return result
+ break
+ end
+ if not result.start then
+ result.start = getStart()
end
- result.names[#result.names+1] = name
+ local version = {
+ type = 'doc.version.unit',
+ parent = result,
+ start = getStart(),
+ }
+ if tp == 'symbol' then
+ if text == '>' then
+ version.ge = true
+ elseif text == '<' then
+ version.le = true
+ end
+ tp, text = nextToken()
+ end
+ if tp ~= 'name' then
+ pushWarning {
+ type = 'LUADOC_MISS_VERSION',
+ start = getStart(),
+ finish = getFinish(),
+ }
+ break
+ end
+ version.version = tonumber(text) or text
+ version.finish = getFinish()
+ result.versions[#result.versions+1] = version
if not checkToken('symbol', ',', 1) then
break
end
nextToken()
end
- end
+ if #result.versions == 0 then
+ return nil
+ end
+ result.finish = getFinish()
+ return result
+ end)
+ : case 'see'
+ : call(function ()
+ local result = {
+ type = 'doc.see',
+ }
+ result.name = parseName('doc.see.name', result)
+ if not result.name then
+ return nil
+ end
+ result.start = result.name.start
+ result.finish = result.name.finish
+ if checkToken('symbol', '#', 1) then
+ nextToken()
+ result.field = parseName('doc.see.field', result)
+ result.finish = getFinish()
+ end
+ return result
+ end)
+ : case 'diagnostic'
+ : call(function ()
+ local result = {
+ type = 'doc.diagnostic',
+ }
+ local nextTP, mode = nextToken()
+ if nextTP ~= 'name' then
+ pushWarning {
+ type = 'LUADOC_MISS_DIAG_MODE',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return nil
+ end
+ result.mode = mode
+ result.start = getStart()
+ result.finish = getFinish()
+ if mode ~= 'disable-next-line'
+ and mode ~= 'disable-line'
+ and mode ~= 'disable'
+ and mode ~= 'enable' then
+ pushWarning {
+ type = 'LUADOC_ERROR_DIAG_MODE',
+ start = result.start,
+ finish = result.finish,
+ }
+ end
- result.finish = getFinish()
+ if checkToken('symbol', ':', 1) then
+ nextToken()
+ result.names = {}
+ while true do
+ local name = parseName('doc.diagnostic.name', result)
+ if not name then
+ pushWarning {
+ type = 'LUADOC_MISS_DIAG_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return result
+ end
+ result.names[#result.names+1] = name
+ if not checkToken('symbol', ',', 1) then
+ break
+ end
+ nextToken()
+ end
+ end
- return result
-end
+ result.finish = getFinish()
-local function parseModule()
- local result = {
- type = 'doc.module',
- start = getFinish(),
- finish = getFinish(),
- }
- local tp, content = peekToken()
- if tp == 'string' then
- result.module = content
- nextToken()
- result.start = getStart()
+ return result
+ end)
+ : case 'module'
+ : call(function ()
+ local result = {
+ type = 'doc.module',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ local tp, content = peekToken()
+ if tp == 'string' then
+ result.module = content
+ nextToken()
+ result.start = getStart()
+ result.finish = getFinish()
+ result.smark = getMark()
+ else
+ pushWarning {
+ type = 'LUADOC_MISS_MODULE_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ end
+ return result
+ end)
+ : case 'async'
+ : call(function ()
+ return {
+ type = 'doc.async',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ end)
+ : case 'nodiscard'
+ : call(function ()
+ return {
+ type = 'doc.nodiscard',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ end)
+ : case 'as'
+ : call(function ()
+ local result = {
+ type = 'doc.as',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ result.as = parseType(result)
result.finish = getFinish()
- result.smark = getMark()
- else
- pushWarning {
- type = 'LUADOC_MISS_MODULE_NAME',
+ return result
+ end)
+ : case 'cast'
+ : call(function ()
+ local result = {
+ type = 'doc.cast',
start = getFinish(),
finish = getFinish(),
+ casts = {},
}
- end
- return result
-end
-local function parseAsync()
- return {
- type = 'doc.async',
- start = getFinish(),
- finish = getFinish(),
- }
-end
+ local loc = parseName('doc.cast.name', result)
+ if not loc then
+ pushWarning {
+ type = 'LUADOC_MISS_LOCAL_NAME',
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ return result
+ end
-local function parseNoDiscard()
- return {
- type = 'doc.nodiscard',
- start = getFinish(),
- finish = getFinish(),
- }
-end
+ result.loc = loc
+ result.finish = loc.finish
+
+ while true do
+ local block = {
+ type = 'doc.cast.block',
+ parent = result,
+ start = getFinish(),
+ finish = getFinish(),
+ }
+ if checkToken('symbol', '+', 1) then
+ block.mode = '+'
+ nextToken()
+ block.start = getStart()
+ block.finish = getFinish()
+ elseif checkToken('symbol', '-', 1) then
+ block.mode = '-'
+ nextToken()
+ block.start = getStart()
+ block.finish = getFinish()
+ end
+
+ if checkToken('symbol', '?', 1) then
+ block.optional = true
+ nextToken()
+ block.start = block.start or getStart()
+ block.finish = block.finish
+ else
+ block.extends = parseType(block)
+ if block.extends then
+ block.start = block.start or block.extends.start
+ block.finish = block.extends.finish
+ end
+ end
+
+ if block.optional or block.extends then
+ result.casts[#result.casts+1] = block
+ end
+
+ if checkToken('symbol', ',', 1) then
+ nextToken()
+ else
+ break
+ end
+ end
+
+ return result
+ end)
local function convertTokens()
local tp, text = nextToken()
@@ -1198,41 +1287,7 @@ local function convertTokens()
}
return nil
end
- if text == 'class' then
- return parseClass()
- elseif text == 'type' then
- return parseType()
- elseif text == 'alias' then
- return parseAlias()
- elseif text == 'param' then
- return parseParam()
- elseif text == 'return' then
- return parseReturn()
- elseif text == 'field' then
- return parseField()
- elseif text == 'generic' then
- return parseGeneric()
- elseif text == 'vararg' then
- return parseVararg()
- elseif text == 'overload' then
- return parseOverload()
- elseif text == 'deprecated' then
- return parseDeprecated()
- elseif text == 'meta' then
- return parseMeta()
- elseif text == 'version' then
- return parseVersion()
- elseif text == 'see' then
- return parseSee()
- elseif text == 'diagnostic' then
- return parseDiagnostic()
- elseif text == 'module' then
- return parseModule()
- elseif text == 'async' then
- return parseAsync()
- elseif text == 'nodiscard' then
- return parseNoDiscard()
- end
+ return docSwitch(text)
end
local function trimTailComment(text)
@@ -1257,7 +1312,8 @@ end
local function buildLuaDoc(comment)
local text = comment.text
- local _, startPos = text:find('^%-%s*@')
+ local startPos = (comment.type == 'comment.short' and text:match '^%-%s*@()')
+ or (comment.type == 'comment.long' and text:match '^@()')
if not startPos then
return {
type = 'doc.comment',
@@ -1268,9 +1324,9 @@ local function buildLuaDoc(comment)
}
end
- local doc = text:sub(startPos + 1)
+ local doc = text:sub(startPos)
- parseTokens(doc, comment.start + startPos + 1)
+ parseTokens(doc, comment.start + startPos)
local result = convertTokens()
if result then
result.range = comment.finish
@@ -1313,16 +1369,21 @@ local function isNextLine(binded, doc)
return false
end
local lastDoc = binded[#binded]
- if lastDoc.type == 'doc.type' then
+ if lastDoc.type == 'doc.type'
+ or lastDoc.type == 'doc.module' then
return false
end
if lastDoc.type == 'doc.class'
or lastDoc.type == 'doc.field' then
if doc.type ~= 'doc.field'
- and doc.type ~= 'doc.comment' then
+ and doc.type ~= 'doc.comment'
+ and doc.type ~= 'doc.overload' then
return false
end
end
+ if doc.type == 'doc.cast' then
+ return false
+ end
local lastRow = guide.rowColOf(lastDoc.finish)
local newRow = guide.rowColOf(doc.start)
return newRow - lastRow == 1
@@ -1400,11 +1461,13 @@ local function bindDocsBetween(sources, binded, bindSources, start, finish)
if src.start >= start then
if src.type == 'local'
or src.type == 'self'
+ or src.type == 'setlocal'
or src.type == 'setglobal'
or src.type == 'tablefield'
or src.type == 'tableindex'
or src.type == 'setfield'
or src.type == 'setindex'
+ or src.type == 'setmethod'
or src.type == 'function' then
src.bindDocs = binded
bindSources[#bindSources+1] = src
diff --git a/script/parser/newparser.lua b/script/parser/newparser.lua
index e226417f..630c12c2 100644
--- a/script/parser/newparser.lua
+++ b/script/parser/newparser.lua
@@ -117,6 +117,7 @@ local Specials = {
['xpcall'] = true,
['pairs'] = true,
['ipairs'] = true,
+ ['assert'] = true,
}
local UnarySymbol = {
@@ -537,6 +538,7 @@ local function skipComment(isAction)
if longComment then
longComment.type = 'comment.long'
longComment.text = longComment[1]
+ longComment.mark = longComment[2]
longComment[1] = nil
longComment[2] = nil
State.comms[#State.comms+1] = longComment
@@ -689,9 +691,6 @@ local function parseLocalAttrs()
end
local function createLocal(obj, attrs)
- if not obj then
- return nil
- end
obj.type = 'local'
obj.effect = obj.finish
@@ -2891,7 +2890,11 @@ local function parseLocal()
pushActionIntoCurrentChunk(loc)
skipSpace()
parseMultiVars(loc, parseName, true)
- loc.effect = lastRightPosition()
+ if loc.value then
+ loc.effect = loc.value.finish
+ else
+ loc.effect = loc.finish
+ end
return loc
end
@@ -2946,13 +2949,22 @@ local function parseReturn()
end
pushActionIntoCurrentChunk(rtn)
for i = #Chunk, 1, -1 do
- local func = Chunk[i]
- if func.type == 'function'
- or func.type == 'main' then
- if not func.returns then
- func.returns = {}
+ local block = Chunk[i]
+ if block.type == 'function'
+ or block.type == 'main' then
+ if not block.returns then
+ block.returns = {}
end
- func.returns[#func.returns+1] = rtn
+ block.returns[#block.returns+1] = rtn
+ break
+ end
+ end
+ for i = #Chunk, 1, -1 do
+ local block = Chunk[i]
+ if block.type == 'ifblock'
+ or block.type == 'elseifblock'
+ or block.type == 'else' then
+ block.hasReturn = true
break
end
end
@@ -3052,6 +3064,15 @@ local function parseGoTo()
break
end
end
+ for i = #Chunk, 1, -1 do
+ local chunk = Chunk[i]
+ if chunk.type == 'ifblock'
+ or chunk.type == 'elseifblock'
+ or chunk.type == 'elseblock' then
+ chunk.hasGoTo = true
+ break
+ end
+ end
pushActionIntoCurrentChunk(action)
return action
@@ -3586,6 +3607,15 @@ local function parseBreak()
break
end
end
+ for i = #Chunk, 1, -1 do
+ local chunk = Chunk[i]
+ if chunk.type == 'ifblock'
+ or chunk.type == 'elseifblock'
+ or chunk.type == 'elseblock' then
+ chunk.hasBreak = true
+ break
+ end
+ end
if not ok and Mode == 'Lua' then
pushError {
type = 'BREAK_OUTSIDE',
diff --git a/script/proto/define.lua b/script/proto/define.lua
index 389cdf88..fb60c56c 100644
--- a/script/proto/define.lua
+++ b/script/proto/define.lua
@@ -9,10 +9,10 @@ m.DiagnosticSeverity = {
}
---@alias DiagnosticDefaultSeverity
----| '"Hint"'
----| '"Information"'
----| '"Warning"'
----| '"Error"'
+---| 'Hint'
+---| 'Information'
+---| 'Warning'
+---| 'Error'
--- 诊断类型与默认等级
---@type table<string, DiagnosticDefaultSeverity>
@@ -29,6 +29,7 @@ m.DiagnosticDefaultSeverity = {
['newline-call'] = 'Information',
['newfield-call'] = 'Warning',
['redundant-parameter'] = 'Warning',
+ ['missing-parameter'] = 'Warning',
['redundant-return'] = 'Warning',
['ambiguity-1'] = 'Warning',
['lowercase-global'] = 'Information',
@@ -47,6 +48,7 @@ m.DiagnosticDefaultSeverity = {
['await-in-sync'] = 'Warning',
['not-yieldable'] = 'Warning',
['discard-returns'] = 'Warning',
+ ['need-check-nil'] = 'Warning',
['type-check'] = 'Warning',
['duplicate-doc-alias'] = 'Warning',
@@ -63,9 +65,9 @@ m.DiagnosticDefaultSeverity = {
}
---@alias DiagnosticDefaultNeededFileStatus
----| '"Any"'
----| '"Opened"'
----| '"None"'
+---| 'Any'
+---| 'Opened'
+---| 'None'
-- 文件状态
m.FileStatus = {
@@ -88,6 +90,7 @@ m.DiagnosticDefaultNeededFileStatus = {
['newline-call'] = 'Any',
['newfield-call'] = 'Any',
['redundant-parameter'] = 'Opened',
+ ['missing-parameter'] = 'Opened',
['redundant-return'] = 'Opened',
['ambiguity-1'] = 'Any',
['lowercase-global'] = 'Any',
@@ -106,6 +109,7 @@ m.DiagnosticDefaultNeededFileStatus = {
['await-in-sync'] = 'None',
['not-yieldable'] = 'None',
['discard-returns'] = 'Opened',
+ ['need-check-nil'] = 'Opened',
['type-check'] = 'None',
['duplicate-doc-alias'] = 'Any',
diff --git a/script/provider/diagnostic.lua b/script/provider/diagnostic.lua
index b359c21c..15b08d49 100644
--- a/script/provider/diagnostic.lua
+++ b/script/provider/diagnostic.lua
@@ -128,12 +128,17 @@ local function mergeDiags(a, b, c)
merge(b)
merge(c)
+ if #t == 0 then
+ return nil
+ end
+
return t
end
+-- enable `push`, disable `clear`
function m.clear(uri)
await.close('diag:' .. uri)
- if not m.cache[uri] then
+ if m.cache[uri] == nil then
return
end
m.cache[uri] = nil
@@ -144,6 +149,7 @@ function m.clear(uri)
log.info('clearDiagnostics', uri)
end
+-- enable `push` and `send`
function m.clearCache(uri)
m.cache[uri] = false
end
@@ -251,14 +257,7 @@ function m.doDiagnostic(uri, isScopeDiag)
version = version,
diagnostics = full,
})
- if #full > 0 then
- log.debug('publishDiagnostics', uri, #full)
- end
- end
-
- -- always re-sent diagnostics of current file
- if not isScopeDiag then
- m.cache[uri] = nil
+ log.debug('publishDiagnostics', uri, #full)
end
pushResult()
@@ -435,6 +434,7 @@ files.watch(function (ev, uri) ---@async
m.refresh(uri)
elseif ev == 'open' then
if ws.isReady(uri) then
+ m.clearCache(uri)
xpcall(m.doDiagnostic, log.error, uri)
end
elseif ev == 'close' then
diff --git a/script/provider/provider.lua b/script/provider/provider.lua
index b8b101ed..08b6ca93 100644
--- a/script/provider/provider.lua
+++ b/script/provider/provider.lua
@@ -42,8 +42,9 @@ local function updateConfig(uri)
end
local rc = cfgLoader.loadRCConfig(folder.uri, '.luarc.json')
+ or cfgLoader.loadRCConfig(folder.uri, '.luarc.jsonc')
if rc then
- log.info('Load config from luarc.json', folder.uri)
+ log.info('Load config from .luarc.json/.luarc.jsonc', folder.uri)
log.debug(inspect(rc))
end
@@ -91,6 +92,14 @@ filewatch.event(function (ev, path) ---@async
end
end
end
+ if util.stringEndWith(path, '.luarc.jsonc') then
+ for _, scp in ipairs(workspace.folders) do
+ local rcPath = workspace.getAbsolutePath(scp.uri, '.luarc.jsonc')
+ if path == rcPath then
+ updateConfig(scp.uri)
+ end
+ end
+ end
end)
m.register 'initialize' {
@@ -226,7 +235,6 @@ m.register 'workspace/didRenameFiles' {
}
m.register 'textDocument/didOpen' {
- ---@async
function (params)
local doc = params.textDocument
local scheme = furi.split(doc.uri)
@@ -235,7 +243,6 @@ m.register 'textDocument/didOpen' {
end
local uri = files.getRealUri(doc.uri)
log.debug('didOpen', uri)
- workspace.awaitReady(uri)
local text = doc.text
files.setText(uri, text, true, function (file)
file.version = doc.version
@@ -257,13 +264,14 @@ m.register 'textDocument/didClose' {
}
m.register 'textDocument/didChange' {
- ---@async
function (params)
local doc = params.textDocument
+ local scheme = furi.split(doc.uri)
+ if scheme ~= 'file' then
+ return
+ end
local changes = params.contentChanges
local uri = files.getRealUri(doc.uri)
- workspace.awaitReady(uri)
- --log.debug('changes', util.dump(changes))
local text = files.getOriginText(uri) or ''
local rows = files.getCachedRows(uri)
text, rows = tm(text, rows, changes)
@@ -521,7 +529,8 @@ m.register 'textDocument/completion' {
local count, max = workspace.getLoadingProcess(uri)
return {
{
- label = lang.script('HOVER_WS_LOADING', count, max),textEdit = {
+ label = lang.script('HOVER_WS_LOADING', count, max),
+ textEdit = {
range = {
start = params.position,
['end'] = params.position,
diff --git a/script/pub/pub.lua b/script/pub/pub.lua
index e73aea51..47591ee6 100644
--- a/script/pub/pub.lua
+++ b/script/pub/pub.lua
@@ -124,7 +124,7 @@ end
--- 通过 jumpQueue 可以插队
---@param name string
---@param params any
----@param callback function
+---@param callback? function
function m.task(name, params, callback)
local info = {
id = counter(),
diff --git a/script/service/telemetry.lua b/script/service/telemetry.lua
index 50af39b1..2e52def2 100644
--- a/script/service/telemetry.lua
+++ b/script/service/telemetry.lua
@@ -99,7 +99,7 @@ timer.wait(5, function ()
end
local suc, link = pcall(net.connect, 'tcp', 'moe-moe.love', 11577)
if not suc then
- suc, link = pcall(net.connect, 'tcp', '154.23.191.94', 11577)
+ suc, link = pcall(net.connect, 'tcp', '154.23.191.39', 11577)
end
if not suc or not link then
return
diff --git a/script/utility.lua b/script/utility.lua
index 5a52e417..47b0c8d8 100644
--- a/script/utility.lua
+++ b/script/utility.lua
@@ -83,7 +83,7 @@ local m = {}
--- 打印表的结构
---@param tbl table
----@param option table {optional = 'self'}
+---@param option? table
---@return string
function m.dump(tbl, option)
if not option then
@@ -315,8 +315,8 @@ function m.saveFile(path, content)
end
--- 计数器
----@param init integer {optional = 'after'}
----@param step integer {optional = 'after'}
+---@param init? integer
+---@param step? integer
---@return fun():integer
function m.counter(init, step)
if not step then
@@ -346,8 +346,8 @@ function m.sortPairs(t, sorter)
end
--- 深拷贝(不处理元表)
----@param source table
----@param target table {optional = 'self'}
+---@param source table
+---@param target? table
function m.deepCopy(source, target)
local mark = {}
local function copy(a, b)
@@ -566,7 +566,7 @@ end
---遍历文本的每一行
---@param text string
----@param keepNL boolean # 保留换行符
+---@param keepNL? boolean # 保留换行符
---@return fun(text:string):string, integer
function m.eachLine(text, keepNL)
local offset = 1
diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua
index 8126f393..75620d19 100644
--- a/script/vm/compiler.lua
+++ b/script/vm/compiler.lua
@@ -1,10 +1,6 @@
local guide = require 'parser.guide'
local util = require 'utility'
-local localID = require 'vm.local-id'
-local globalMgr = require 'vm.global-manager'
-local signMgr = require 'vm.sign'
local config = require 'config'
-local genericMgr = require 'vm.generic'
local rpath = require 'workspace.require-path'
local files = require 'files'
---@class vm
@@ -13,7 +9,6 @@ local vm = require 'vm.vm'
---@class parser.object
---@field _compiledNodes boolean
---@field _node vm.node
----@field _localBase table
---@field _globalBase table
local searchFieldSwitch = util.switch()
@@ -54,7 +49,7 @@ local searchFieldSwitch = util.switch()
: case 'string'
: call(function (suri, source, key, ref, pushResult)
-- change to `string: stringlib` ?
- local stringlib = globalMgr.getGlobal('type', 'stringlib')
+ local stringlib = vm.getGlobal('type', 'stringlib')
if stringlib then
vm.getClassFields(suri, stringlib, key, ref, pushResult)
end
@@ -64,9 +59,9 @@ local searchFieldSwitch = util.switch()
: call(function (suri, node, key, ref, pushResult)
local fields
if key then
- fields = localID.getSources(node, key)
+ fields = vm.getLocalSources(node, key)
else
- fields = localID.getFields(node)
+ fields = vm.getLocalFields(node)
end
if fields then
for _, src in ipairs(fields) do
@@ -119,7 +114,7 @@ local searchFieldSwitch = util.switch()
if type(key) ~= 'string' then
return
end
- local global = globalMgr.getGlobal('variable', node.name, key)
+ local global = vm.getGlobal('variable', node.name, key)
if global then
for _, set in ipairs(global:getSets(suri)) do
pushResult(set)
@@ -131,7 +126,7 @@ local searchFieldSwitch = util.switch()
end
end
else
- local globals = globalMgr.getFields('variable', node.name)
+ local globals = vm.getGlobalFields('variable', node.name)
for _, global in ipairs(globals) do
for _, set in ipairs(global:getSets(suri)) do
pushResult(set)
@@ -158,7 +153,7 @@ local searchFieldSwitch = util.switch()
if type(key) ~= 'string' then
return
end
- local global = globalMgr.getGlobal('variable', node.name, key)
+ local global = vm.getGlobal('variable', node.name, key)
if global then
for _, set in ipairs(global:getSets(suri)) do
pushResult(set)
@@ -168,7 +163,7 @@ local searchFieldSwitch = util.switch()
end
end
else
- local globals = globalMgr.getFields('variable', node.name)
+ local globals = vm.getGlobalFields('variable', node.name)
for _, global in ipairs(globals) do
for _, set in ipairs(global:getSets(suri)) do
pushResult(set)
@@ -185,7 +180,7 @@ local searchFieldSwitch = util.switch()
end)
-function vm.getClassFields(suri, node, key, ref, pushResult)
+function vm.getClassFields(suri, object, key, ref, pushResult)
local mark = {}
local function searchClass(class, searchedFields)
@@ -201,11 +196,51 @@ function vm.getClassFields(suri, node, key, ref, pushResult)
local hasFounded = {}
for _, field in ipairs(set.fields) do
local fieldKey = guide.getKeyName(field)
- if key == nil
- or fieldKey == key then
- if not searchedFields[fieldKey] then
- pushResult(field)
- hasFounded[fieldKey] = true
+ if fieldKey then
+ -- ---@field x boolean -> class.x
+ if key == nil
+ or fieldKey == key then
+ if not searchedFields[fieldKey] then
+ pushResult(field)
+ hasFounded[fieldKey] = true
+ end
+ end
+ end
+ if not hasFounded[fieldKey] then
+ local keyType = type(key)
+ if keyType == 'table' then
+ -- ---@field [integer] boolean -> class[integer]
+ local fieldNode = vm.compileNode(field.field)
+ if vm.isSubType(suri, key.name, fieldNode) then
+ local nkey = '|' .. key.name
+ if not searchedFields[nkey] then
+ pushResult(field)
+ hasFounded[nkey] = true
+ end
+ end
+ else
+ local typeName
+ if keyType == 'number' then
+ if math.tointeger(key) then
+ typeName = 'integer'
+ else
+ typeName = 'number'
+ end
+ elseif keyType == 'boolean'
+ or keyType == 'string' then
+ typeName = keyType
+ end
+ if typeName then
+ -- ---@field [integer] boolean -> class[1]
+ local fieldNode = vm.compileNode(field.field)
+ if vm.isSubType(suri, typeName, fieldNode) then
+ local nkey = '|' .. typeName
+ if not searchedFields[nkey] then
+ pushResult(field)
+ hasFounded[nkey] = true
+ end
+ end
+ end
end
end
end
@@ -214,19 +249,23 @@ function vm.getClassFields(suri, node, key, ref, pushResult)
for _, src in ipairs(set.bindSources) do
searchFieldSwitch(src.type, suri, src, key, ref, function (field)
local fieldKey = guide.getKeyName(field)
- if not searchedFields[fieldKey]
- and guide.isSet(field) then
- hasFounded[fieldKey] = true
- pushResult(field)
+ if fieldKey then
+ if not searchedFields[fieldKey]
+ and guide.isSet(field) then
+ hasFounded[fieldKey] = true
+ pushResult(field)
+ end
end
end)
if src.value and src.value.type == 'table' then
searchFieldSwitch('table', suri, src.value, key, ref, function (field)
local fieldKey = guide.getKeyName(field)
- if not searchedFields[fieldKey]
- and guide.isSet(field) then
- hasFounded[fieldKey] = true
- pushResult(field)
+ if fieldKey then
+ if not searchedFields[fieldKey]
+ and guide.isSet(field) then
+ hasFounded[fieldKey] = true
+ pushResult(field)
+ end
end
end)
end
@@ -239,7 +278,7 @@ function vm.getClassFields(suri, node, key, ref, pushResult)
end
for _, extend in ipairs(set.extends) do
if extend.type == 'doc.extends.name' then
- local extendType = globalMgr.getGlobal('type', extend[1])
+ local extendType = vm.getGlobal('type', extend[1])
if extendType then
searchClass(extendType, searchedFields)
end
@@ -253,12 +292,12 @@ function vm.getClassFields(suri, node, key, ref, pushResult)
local function searchGlobal(class)
if class.cate == 'type' and class.name == '_G' then
if key == nil then
- local sets = globalMgr.getGlobalSets(suri, 'variable')
+ local sets = vm.getGlobalSets(suri, 'variable')
for _, set in ipairs(sets) do
pushResult(set)
end
else
- local global = globalMgr.getGlobal('variable', key)
+ local global = vm.getGlobal('variable', key)
if global then
for _, set in ipairs(global:getSets(suri)) do
pushResult(set)
@@ -268,8 +307,8 @@ function vm.getClassFields(suri, node, key, ref, pushResult)
end
end
- searchClass(node)
- searchGlobal(node)
+ searchClass(object)
+ searchGlobal(object)
end
---@class parser.object
@@ -283,10 +322,13 @@ local function getObjectSign(source)
end
source._sign = false
if source.type == 'function' then
+ if not source.bindDocs then
+ return false
+ end
for _, doc in ipairs(source.bindDocs) do
if doc.type == 'doc.generic' then
if not source._sign then
- source._sign = signMgr()
+ source._sign = vm.createSign()
break
end
end
@@ -314,14 +356,18 @@ local function getObjectSign(source)
if not hasGeneric then
return false
end
- source._sign = signMgr()
+ source._sign = vm.createSign()
if source.type == 'doc.type.function' then
for _, arg in ipairs(source.args) do
- local argNode = vm.compileNode(arg.extends)
- if arg.optional then
- argNode:addOptional()
+ if arg.extends then
+ local argNode = vm.compileNode(arg.extends)
+ if arg.optional then
+ argNode:addOptional()
+ end
+ source._sign:addSign(argNode)
+ else
+ source._sign:addSign(vm.createNode())
end
- source._sign:addSign(argNode)
end
end
end
@@ -354,7 +400,7 @@ function vm.getReturnOfFunction(func, index)
if not sign then
return rtn
end
- return genericMgr(rtn, sign)
+ return vm.createGeneric(rtn, sign)
end
end
@@ -455,6 +501,9 @@ local function getReturn(func, index, args)
result:merge(rnode)
end
end
+ if result and returnNode:isOptional() then
+ result:addOptional()
+ end
end
end
end
@@ -462,6 +511,25 @@ local function getReturn(func, index, args)
return result
end
+---@param source parser.object
+---@return boolean
+local function bindAs(source)
+ local root = guide.getRoot(source)
+ local docs = root.docs
+ if not docs then
+ return
+ end
+ for _, doc in ipairs(docs) do
+ if doc.type == 'doc.as' and doc.originalComment.start == source.finish + 2 then
+ if doc.as then
+ vm.setNode(source, vm.compileNode(doc.as), true)
+ end
+ return true
+ end
+ end
+ return false
+end
+
local function bindDocs(source)
local isParam = source.parent.type == 'funcargs'
or source.parent.type == 'in'
@@ -485,7 +553,11 @@ local function bindDocs(source)
end
if doc.type == 'doc.param' then
if isParam and source[1] == doc.param[1] then
- vm.setNode(source, vm.compileNode(doc))
+ local node = vm.compileNode(doc)
+ if doc.optional then
+ node:addOptional()
+ end
+ vm.setNode(source, node)
return true
end
end
@@ -503,12 +575,17 @@ local function bindDocs(source)
vm.setNode(source, vm.compileNode(ast))
return true
end
+ if doc.type == 'doc.overload' then
+ if not isParam then
+ vm.setNode(source, vm.compileNode(doc))
+ end
+ end
end
return false
end
local function compileByLocalID(source)
- local sources = localID.getSources(source)
+ local sources = vm.getLocalSources(source)
if not sources then
return
end
@@ -571,7 +648,7 @@ local function selectNode(source, list, index)
if exp.type == 'call' then
result = getReturn(exp.node, index, exp.args)
if not result then
- vm.setNode(source, globalMgr.getGlobal('type', 'unknown'))
+ vm.setNode(source, vm.declareGlobal('type', 'unknown'))
return vm.getNode(source)
end
else
@@ -597,7 +674,7 @@ local function selectNode(source, list, index)
end
end
if not hasKnownType then
- rtnNode:merge(globalMgr.getGlobal('type', 'unknown'))
+ rtnNode:merge(vm.declareGlobal('type', 'unknown'))
end
vm.setNode(source, rtnNode)
return rtnNode
@@ -664,10 +741,21 @@ local function compileCallArgNode(arg, call, callNode, fixIndex, myIndex)
for n in callNode:eachObject() do
if n.type == 'function' then
+ local sign = getObjectSign(n)
local farg = getFuncArg(n, myIndex)
if farg then
for fn in vm.compileNode(farg):eachObject() do
if isValidCallArgNode(arg, fn) then
+ if fn.type == 'doc.type.function' then
+ if sign then
+ local generic = vm.createGeneric(fn, sign)
+ local args = {}
+ for i = fixIndex + 1, myIndex - 1 do
+ args[#args+1] = call.args[i]
+ end
+ fn = generic:resolve(guide.getUri(call), args)
+ end
+ end
vm.setNode(arg, fn)
end
end
@@ -716,29 +804,19 @@ function vm.compileCallArg(arg, call, index)
if call.node.special == 'pcall'
or call.node.special == 'xpcall' then
local fixIndex = call.node.special == 'pcall' and 1 or 2
- callNode = vm.compileNode(call.args[1])
- compileCallArgNode(arg, call, callNode, fixIndex, index - fixIndex)
+ if call.args and call.args[1] then
+ callNode = vm.compileNode(call.args[1])
+ compileCallArgNode(arg, call, callNode, fixIndex, index - fixIndex)
+ end
end
return vm.getNode(arg)
end
---@param source parser.object
---@return vm.node
-local function compileLocalBase(source)
- if not source._localBase then
- source._localBase = {
- type = 'localbase',
- parent = source,
- }
- end
- local baseNode = vm.getNode(source._localBase)
- if baseNode then
- return baseNode
- end
- baseNode = vm.createNode()
- vm.setNode(source._localBase, baseNode, true)
-
+local function compileLocal(source)
vm.setNode(source, source)
+
local hasMarkDoc
if source.bindDocs then
hasMarkDoc = bindDocs(source)
@@ -788,14 +866,19 @@ local function compileLocalBase(source)
if n.type == 'doc.type.function' then
for index, arg in ipairs(n.args) do
if func.args[index] == source then
- vm.setNode(source, vm.compileNode(arg))
+ local argNode = vm.compileNode(arg)
+ for an in argNode:eachObject() do
+ if an.type ~= 'doc.generic.name' then
+ vm.setNode(source, an)
+ end
+ end
hasDocArg = true
end
end
end
end
if not hasDocArg then
- vm.setNode(source, globalMgr.getGlobal('type', 'any'))
+ vm.setNode(source, vm.declareGlobal('type', 'any'))
end
end
-- for x in ... do
@@ -805,15 +888,10 @@ local function compileLocalBase(source)
-- for x = ... do
if source.parent.type == 'loop' then
- vm.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ vm.compileNode(source.parent)
end
- baseNode:merge(vm.getNode(source))
- vm.removeNode(source)
-
- baseNode:setData('hasDefined', hasMarkDoc or hasMarkParam or hasMarkValue)
-
- return baseNode
+ vm.getNode(source):setData('hasDefined', hasMarkDoc or hasMarkParam or hasMarkValue)
end
local compilerSwitch = util.switch()
@@ -867,41 +945,79 @@ local compilerSwitch = util.switch()
end)
: case 'paren'
: call(function (source)
+ if bindAs(source) then
+ return
+ end
if source.exp then
vm.setNode(source, vm.compileNode(source.exp))
end
end)
: case 'local'
: case 'self'
+ ---@param source parser.object
: call(function (source)
- local baseNode = compileLocalBase(source)
- vm.setNode(source, baseNode, true)
- if not baseNode:getData 'hasDefined' and source.ref then
+ compileLocal(source)
+ local refs = source.ref
+ if not refs then
+ return
+ end
+
+ local hasMark = vm.getNode(source):getData 'hasDefined'
+
+ local runner = vm.createRunner(source)
+ runner:launch(function (src, node)
+ if src.type == 'setlocal' then
+ if src.bindDocs then
+ for _, doc in ipairs(src.bindDocs) do
+ if doc.type == 'doc.type' then
+ vm.setNode(src, vm.compileNode(doc), true)
+ return vm.getNode(src)
+ end
+ end
+ end
+ if src.value and guide.isLiteral(src.value) then
+ if src.value.type == 'table' then
+ vm.setNode(src, vm.createNode(src.value), true)
+ else
+ vm.setNode(src, vm.compileNode(src.value), true)
+ end
+ elseif src.value
+ and src.value.type == 'binary'
+ and src.value.op and src.value.op.type == 'or'
+ and src.value[1] and src.value[1].type == 'getlocal' and src.value[1].node == source then
+ -- x = x or 1
+ vm.setNode(src, vm.compileNode(src.value))
+ else
+ vm.setNode(src, node, true)
+ end
+ return vm.getNode(src)
+ elseif src.type == 'getlocal' then
+ if bindAs(src) then
+ return
+ end
+ vm.setNode(src, node, true)
+ end
+ end)
+
+ if not hasMark then
+ local parentFunc = guide.getParentFunction(source)
for _, ref in ipairs(source.ref) do
- if ref.type == 'setlocal' then
- vm.setNode(source, vm.compileNode(ref))
+ if ref.type == 'setlocal'
+ and guide.getParentFunction(ref) == parentFunc then
+ vm.setNode(source, vm.getNode(ref))
end
end
end
end)
: case 'setlocal'
: call(function (source)
- local baseNode = compileLocalBase(source.node)
- if not baseNode:getData 'hasDefined' and source.value then
- if source.value.type == 'table' then
- vm.setNode(source, source.value)
- else
- vm.setNode(source, vm.compileNode(source.value))
- end
- end
- baseNode:merge(vm.getNode(source))
- vm.setNode(source, baseNode, true)
vm.compileNode(source.node)
end)
: case 'getlocal'
: call(function (source)
- local baseNode = compileLocalBase(source.node)
- vm.setNode(source, baseNode, true)
+ if bindAs(source) then
+ return
+ end
vm.compileNode(source.node)
end)
: case 'setfield'
@@ -924,6 +1040,9 @@ local compilerSwitch = util.switch()
: case 'getmethod'
: case 'getindex'
: call(function (source)
+ if bindAs(source) then
+ return
+ end
compileByLocalID(source)
local key = guide.getKeyName(source)
if key == nil and source.index then
@@ -959,6 +1078,9 @@ local compilerSwitch = util.switch()
end)
: case 'getglobal'
: call(function (source)
+ if bindAs(source) then
+ return
+ end
if source.node[1] ~= '_ENV' then
return
end
@@ -1019,7 +1141,7 @@ local compilerSwitch = util.switch()
end)
end
if hasGeneric then
- vm.setNode(source, genericMgr(rtn, sign))
+ vm.setNode(source, vm.createGeneric(rtn, sign))
else
vm.setNode(source, vm.compileNode(rtn))
end
@@ -1092,29 +1214,44 @@ local compilerSwitch = util.switch()
-- for k, v in pairs(t) do
--> for k, v in iterator, status, initValue do
--> local k, v = iterator(status, initValue)
- source._iterator = {}
- source._iterArgs = {{}, {}}
- -- iterator
- selectNode(source._iterator, source.exps, 1)
- -- status
- selectNode(source._iterArgs[1], source.exps, 2)
- -- initValue
- selectNode(source._iterArgs[2], source.exps, 3)
- end
+ source._iterator = {
+ type = 'dummyfunc',
+ parent = source,
+ }
+ source._iterArgs = {{},{}}
+ end
+ -- iterator
+ selectNode(source._iterator, source.exps, 1)
+ -- status
+ selectNode(source._iterArgs[1], source.exps, 2)
+ -- initValue
+ selectNode(source._iterArgs[2], source.exps, 3)
if source.keys then
for i, loc in ipairs(source.keys) do
local node = getReturn(source._iterator, i, source._iterArgs)
if node then
+ if i == 1 then
+ node:removeOptional()
+ end
vm.setNode(loc, node)
end
end
end
end)
+ : case 'loop'
+ : call(function (source)
+ if source.loc then
+ vm.setNode(source.loc, vm.declareGlobal('type', 'integer'))
+ end
+ end)
: case 'doc.type'
: call(function (source)
for _, typeUnit in ipairs(source.types) do
vm.setNode(source, vm.compileNode(typeUnit))
end
+ if source.optional then
+ vm.getNode(source):addOptional()
+ end
end)
: case 'doc.type.integer'
: case 'doc.type.string'
@@ -1130,7 +1267,13 @@ local compilerSwitch = util.switch()
: call(function (source)
local uri = guide.getUri(source)
vm.setNode(source, source)
- local global = globalMgr.getGlobal('type', source.node[1])
+ if not source.node[1] then
+ return
+ end
+ local global = vm.getGlobal('type', source.node[1])
+ if not global then
+ return
+ end
for _, set in ipairs(global:getSets(uri)) do
if set.type == 'doc.class' then
if set.extends then
@@ -1161,14 +1304,22 @@ local compilerSwitch = util.switch()
if not source.extends then
return
end
- vm.setNode(source, vm.compileNode(source.extends))
+ local fieldNode = vm.compileNode(source.extends)
+ if source.optional then
+ fieldNode:addOptional()
+ end
+ vm.setNode(source, fieldNode)
end)
: case 'doc.type.field'
: call(function (source)
if not source.extends then
return
end
- vm.setNode(source, vm.compileNode(source.extends))
+ local fieldNode = vm.compileNode(source.extends)
+ if source.optional then
+ fieldNode:addOptional()
+ end
+ vm.setNode(source, fieldNode)
end)
: case 'doc.param'
: call(function (source)
@@ -1208,7 +1359,7 @@ local compilerSwitch = util.switch()
end)
: case 'doc.see.name'
: call(function (source)
- local type = globalMgr.getGlobal('type', source[1])
+ local type = vm.getGlobal('type', source[1])
if type then
vm.setNode(source, vm.compileNode(type))
end
@@ -1218,7 +1369,10 @@ local compilerSwitch = util.switch()
if source.extends then
vm.setNode(source, vm.compileNode(source.extends))
else
- vm.setNode(source, globalMgr.getGlobal('type', 'any'))
+ vm.setNode(source, vm.declareGlobal('type', 'any'))
+ end
+ if source.optional then
+ vm.getNode(source):addOptional()
end
end)
: case 'generic'
@@ -1227,10 +1381,16 @@ local compilerSwitch = util.switch()
end)
: case 'unary'
: call(function (source)
+ if bindAs(source) then
+ return
+ end
+ if not source[1] then
+ return
+ end
if source.op.type == 'not' then
local result = vm.test(source[1])
if result == nil then
- vm.setNode(source, globalMgr.getGlobal('type', 'boolean'))
+ vm.setNode(source, vm.declareGlobal('type', 'boolean'))
return
else
vm.setNode(source, {
@@ -1244,13 +1404,13 @@ local compilerSwitch = util.switch()
end
end
if source.op.type == '#' then
- vm.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ vm.setNode(source, vm.declareGlobal('type', 'integer'))
return
end
if source.op.type == '-' then
local v = vm.getNumber(source[1])
if v == nil then
- vm.setNode(source, globalMgr.getGlobal('type', 'number'))
+ vm.setNode(source, vm.declareGlobal('type', 'number'))
return
else
vm.setNode(source, {
@@ -1266,7 +1426,7 @@ local compilerSwitch = util.switch()
if source.op.type == '~' then
local v = vm.getInteger(source[1])
if v == nil then
- vm.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ vm.setNode(source, vm.declareGlobal('type', 'integer'))
return
else
vm.setNode(source, {
@@ -1282,34 +1442,42 @@ local compilerSwitch = util.switch()
end)
: case 'binary'
: call(function (source)
+ if bindAs(source) then
+ return
+ end
+ if not source[1] or not source[2] then
+ return
+ end
if source.op.type == 'and' then
+ local node1 = vm.compileNode(source[1])
+ local node2 = vm.compileNode(source[2])
local r1 = vm.test(source[1])
if r1 == true then
- vm.setNode(source, vm.compileNode(source[2]))
- return
- end
- if r1 == false then
- vm.setNode(source, vm.compileNode(source[1]))
- return
+ vm.setNode(source, node2)
+ elseif r1 == false then
+ vm.setNode(source, node1)
+ else
+ vm.setNode(source, node2)
end
- return
end
if source.op.type == 'or' then
+ local node1 = vm.compileNode(source[1])
+ local node2 = vm.compileNode(source[2])
local r1 = vm.test(source[1])
if r1 == true then
- vm.setNode(source, vm.compileNode(source[1]))
- return
- end
- if r1 == false then
- vm.setNode(source, vm.compileNode(source[2]))
- return
+ vm.setNode(source, node1)
+ elseif r1 == false then
+ vm.setNode(source, node2)
+ else
+ vm.getNode(source):merge(node1)
+ vm.getNode(source):setTruthy()
+ vm.getNode(source):merge(node2)
end
- return
end
if source.op.type == '==' then
local result = vm.equal(source[1], source[2])
if result == nil then
- vm.setNode(source, globalMgr.getGlobal('type', 'boolean'))
+ vm.setNode(source, vm.declareGlobal('type', 'boolean'))
return
else
vm.setNode(source, {
@@ -1325,7 +1493,7 @@ local compilerSwitch = util.switch()
if source.op.type == '~=' then
local result = vm.equal(source[1], source[2])
if result == nil then
- vm.setNode(source, globalMgr.getGlobal('type', 'boolean'))
+ vm.setNode(source, vm.declareGlobal('type', 'boolean'))
return
else
vm.setNode(source, {
@@ -1351,7 +1519,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ vm.setNode(source, vm.declareGlobal('type', 'integer'))
return
end
end
@@ -1368,7 +1536,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ vm.setNode(source, vm.declareGlobal('type', 'integer'))
return
end
end
@@ -1385,7 +1553,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ vm.setNode(source, vm.declareGlobal('type', 'integer'))
return
end
end
@@ -1402,7 +1570,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ vm.setNode(source, vm.declareGlobal('type', 'integer'))
return
end
end
@@ -1419,7 +1587,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'integer'))
+ vm.setNode(source, vm.declareGlobal('type', 'integer'))
return
end
end
@@ -1437,7 +1605,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'number'))
+ vm.setNode(source, vm.declareGlobal('type', 'number'))
return
end
end
@@ -1455,7 +1623,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'number'))
+ vm.setNode(source, vm.declareGlobal('type', 'number'))
return
end
end
@@ -1473,7 +1641,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'number'))
+ vm.setNode(source, vm.declareGlobal('type', 'number'))
return
end
end
@@ -1490,14 +1658,14 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'number'))
+ vm.setNode(source, vm.declareGlobal('type', 'number'))
return
end
end
if source.op.type == '%' then
local a = vm.getNumber(source[1])
local b = vm.getNumber(source[2])
- if a and b then
+ if a and b and b ~= 0 then
local result = a % b
vm.setNode(source, {
type = math.type(result) == 'integer' and 'integer' or 'number',
@@ -1508,7 +1676,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'number'))
+ vm.setNode(source, vm.declareGlobal('type', 'number'))
return
end
end
@@ -1525,7 +1693,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'number'))
+ vm.setNode(source, vm.declareGlobal('type', 'number'))
return
end
end
@@ -1543,7 +1711,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'number'))
+ vm.setNode(source, vm.declareGlobal('type', 'number'))
return
end
end
@@ -1580,7 +1748,7 @@ local compilerSwitch = util.switch()
})
return
else
- vm.setNode(source, globalMgr.getGlobal('type', 'string'))
+ vm.setNode(source, vm.declareGlobal('type', 'string'))
return
end
end
@@ -1614,17 +1782,20 @@ local function compileByGlobal(source)
vm.setNode(source, globalNode, true)
return
end
+ ---@type vm.node
globalNode = vm.createNode(global)
vm.setNode(root._globalBase[name], globalNode, true)
+ vm.setNode(source, globalNode, true)
- local sets = global.links[uri].sets or {}
- local gets = global.links[uri].gets or {}
- for _, set in ipairs(sets) do
- vm.setNode(set, globalNode, true)
- end
- for _, get in ipairs(gets) do
- vm.setNode(get, globalNode, true)
- end
+ -- TODO:don't mix
+ --local sets = global.links[uri].sets or {}
+ --local gets = global.links[uri].gets or {}
+ --for _, set in ipairs(sets) do
+ -- vm.setNode(set, globalNode, true)
+ --end
+ --for _, get in ipairs(gets) do
+ -- vm.setNode(get, globalNode, true)
+ --end
if global.cate == 'variable' then
local hasMarkDoc
@@ -1672,7 +1843,11 @@ end
---@return vm.node
function vm.compileNode(source)
if not source then
- error('Can not compile nil node')
+ if TEST then
+ error('Can not compile nil source')
+ else
+ log.error('Can not compile nil source')
+ end
end
if source.type == 'global' then
diff --git a/script/vm/def.lua b/script/vm/def.lua
index b66e8fda..83e92686 100644
--- a/script/vm/def.lua
+++ b/script/vm/def.lua
@@ -2,8 +2,6 @@
local vm = require 'vm.vm'
local util = require 'utility'
local guide = require 'parser.guide'
-local localID = require 'vm.local-id'
-local globalMgr = require 'vm.global-manager'
local simpleSwitch
@@ -79,6 +77,13 @@ simpleSwitch = util.switch()
pushResult(source.node)
end
end)
+ : case 'doc.cast.name'
+ : call(function (source, pushResult)
+ local loc = guide.getLocal(source, source[1], source.start)
+ if loc then
+ pushResult(loc)
+ end
+ end)
local searchFieldSwitch = util.switch()
: case 'table'
@@ -97,7 +102,7 @@ local searchFieldSwitch = util.switch()
---@param key string
: call(function (suri, obj, key, pushResult)
if obj.cate == 'variable' then
- local newGlobal = globalMgr.getGlobal('variable', obj.name, key)
+ local newGlobal = vm.getGlobal('variable', obj.name, key)
if newGlobal then
for _, set in ipairs(newGlobal:getSets(suri)) do
pushResult(set)
@@ -110,7 +115,7 @@ local searchFieldSwitch = util.switch()
end)
: case 'local'
: call(function (suri, obj, key, pushResult)
- local sources = localID.getSources(obj, key)
+ local sources = vm.getLocalSources(obj, key)
if sources then
for _, src in ipairs(sources) do
if guide.isSet(src) then
@@ -189,7 +194,7 @@ end
---@param source parser.object
---@param pushResult fun(src: parser.object)
local function searchByLocalID(source, pushResult)
- local idSources = localID.getSources(source)
+ local idSources = vm.getLocalSources(source)
if not idSources then
return
end
diff --git a/script/vm/doc.lua b/script/vm/doc.lua
index 5a92a103..e2b383b6 100644
--- a/script/vm/doc.lua
+++ b/script/vm/doc.lua
@@ -3,7 +3,6 @@ local guide = require 'parser.guide'
---@class vm
local vm = require 'vm.vm'
local config = require 'config'
-local globalMgr = require 'vm.global-manager'
---获取class与alias
---@param suri uri
@@ -11,13 +10,13 @@ local globalMgr = require 'vm.global-manager'
---@return parser.object[]
function vm.getDocSets(suri, name)
if name then
- local global = globalMgr.getGlobal('type', name)
+ local global = vm.getGlobal('type', name)
if not global then
return {}
end
return global:getSets(suri)
else
- return globalMgr.getGlobalSets(suri, 'type')
+ return vm.getGlobalSets(suri, 'type')
end
end
@@ -27,6 +26,9 @@ function vm.isMetaFile(uri)
return false
end
local cache = files.getCache(uri)
+ if not cache then
+ return false
+ end
if cache.isMeta ~= nil then
return cache.isMeta
end
@@ -332,6 +334,9 @@ function vm.isDiagDisabledAt(uri, position, name)
return false
end
local cache = files.getCache(uri)
+ if not cache then
+ return false
+ end
if not cache.diagnosticRanges then
cache.diagnosticRanges = {}
for _, doc in ipairs(status.ast.docs) do
diff --git a/script/vm/field.lua b/script/vm/field.lua
index ba7cd4c1..5de838be 100644
--- a/script/vm/field.lua
+++ b/script/vm/field.lua
@@ -15,6 +15,15 @@ local searchByNodeSwitch = util.switch()
pushResult(source)
end)
+local function searchByLocalID(source, pushResult)
+ local fields = vm.getLocalFields(source)
+ if fields then
+ for _, field in ipairs(fields) do
+ pushResult(field)
+ end
+ end
+end
+
local function searchByNode(source, pushResult)
local uri = guide.getUri(source)
vm.compileByParentNode(source, nil, true, function (field)
@@ -35,6 +44,7 @@ function vm.getFields(source)
end
end
+ searchByLocalID(source, pushResult)
searchByNode(source, pushResult)
return results
diff --git a/script/vm/generic.lua b/script/vm/generic.lua
index b3981ff8..6462028e 100644
--- a/script/vm/generic.lua
+++ b/script/vm/generic.lua
@@ -1,3 +1,4 @@
+---@class vm
local vm = require 'vm.vm'
---@class parser.object
@@ -114,7 +115,7 @@ end
---@param uri uri
---@param args parser.object
----@return parser.object
+---@return vm.node
function mt:resolve(uri, args)
local resolved = self.sign:resolve(uri, args)
local protoNode = vm.compileNode(self.proto)
@@ -129,7 +130,7 @@ end
---@param proto vm.object
---@param sign vm.sign
---@return vm.generic
-return function (proto, sign)
+function vm.createGeneric(proto, sign)
local generic = setmetatable({
sign = sign,
proto = proto,
diff --git a/script/vm/global-manager.lua b/script/vm/global-manager.lua
deleted file mode 100644
index f25bb5a0..00000000
--- a/script/vm/global-manager.lua
+++ /dev/null
@@ -1,364 +0,0 @@
-local util = require 'utility'
-local guide = require 'parser.guide'
-local globalBuilder = require 'vm.global'
-local signMgr = require 'vm.sign'
-local genericMgr = require 'vm.generic'
----@class vm
-local vm = require 'vm.vm'
-
----@class parser.object
----@field _globalNode vm.global
-
----@class vm.global-manager
-local m = {}
----@type table<string, vm.global>
-m.globals = {}
----@type table<uri, table<string, boolean>>
-m.globalSubs = util.multiTable(2)
-
-local compilerGlobalSwitch = util.switch()
- : case 'local'
- : call(function (source)
- if source.special ~= '_G' then
- return
- end
- if source.ref then
- for _, ref in ipairs(source.ref) do
- m.compileObject(ref)
- end
- end
- end)
- : case 'getlocal'
- : call(function (source)
- if source.special ~= '_G' then
- return
- end
- if not source.next then
- return
- end
- m.compileObject(source.next)
- end)
- : case 'setglobal'
- : call(function (source)
- local uri = guide.getUri(source)
- local name = guide.getKeyName(source)
- local global = m.declareGlobal('variable', name, uri)
- global:addSet(uri, source)
- source._globalNode = global
- end)
- : case 'getglobal'
- : call(function (source)
- local uri = guide.getUri(source)
- local name = guide.getKeyName(source)
- local global = m.declareGlobal('variable', name, uri)
- global:addGet(uri, source)
- source._globalNode = global
-
- local nxt = source.next
- if nxt then
- m.compileObject(nxt)
- end
- end)
- : case 'setfield'
- : case 'setmethod'
- : case 'setindex'
- ---@param source parser.object
- : call(function (source)
- local name
- local keyName = guide.getKeyName(source)
- if not keyName then
- return
- end
- if source.node._globalNode then
- local parentName = source.node._globalNode:getName()
- if parentName == '_G' then
- name = keyName
- else
- name = ('%s%s%s'):format(parentName, vm.ID_SPLITE, keyName)
- end
- elseif source.node.special == '_G' then
- name = keyName
- end
- if not name then
- return
- end
- local uri = guide.getUri(source)
- local global = m.declareGlobal('variable', name, uri)
- global:addSet(uri, source)
- source._globalNode = global
- end)
- : case 'getfield'
- : case 'getmethod'
- : case 'getindex'
- ---@param source parser.object
- : call(function (source)
- local name
- local keyName = guide.getKeyName(source)
- if not keyName then
- return
- end
- if source.node._globalNode then
- local parentName = source.node._globalNode:getName()
- if parentName == '_G' then
- name = keyName
- else
- name = ('%s%s%s'):format(parentName, vm.ID_SPLITE, keyName)
- end
- elseif source.node.special == '_G' then
- name = keyName
- end
- local uri = guide.getUri(source)
- local global = m.declareGlobal('variable', name, uri)
- global:addGet(uri, source)
- source._globalNode = global
-
- local nxt = source.next
- if nxt then
- m.compileObject(nxt)
- end
- end)
- : case 'call'
- : call(function (source)
- if source.node.special == 'rawset'
- or source.node.special == 'rawget' then
- if not source.args then
- return
- end
- local g = source.args[1]
- local key = source.args[2]
- if g and key and g.special == '_G' then
- local name = guide.getKeyName(key)
- if name then
- local uri = guide.getUri(source)
- local global = m.declareGlobal('variable', name, uri)
- if source.node.special == 'rawset' then
- global:addSet(uri, source)
- source.value = source.args[3]
- else
- global:addGet(uri, source)
- end
- source._globalNode = global
-
- local nxt = source.next
- if nxt then
- m.compileObject(nxt)
- end
- end
- end
- end
- end)
- : case 'doc.class'
- ---@param source parser.object
- : call(function (source)
- local uri = guide.getUri(source)
- local name = guide.getKeyName(source)
- local class = m.declareGlobal('type', name, uri)
- class:addSet(uri, source)
- source._globalNode = class
-
- if source.signs then
- source._sign = signMgr()
- for _, sign in ipairs(source.signs) do
- source._sign:addSign(vm.compileNode(sign))
- end
- if source.extends then
- for _, ext in ipairs(source.extends) do
- if ext.type == 'doc.type.table' then
- ext._generic = genericMgr(ext, source._sign)
- end
- end
- end
- end
- end)
- : case 'doc.alias'
- : call(function (source)
- local uri = guide.getUri(source)
- local name = guide.getKeyName(source)
- local alias = m.declareGlobal('type', name, uri)
- alias:addSet(uri, source)
- source._globalNode = alias
-
- if source.signs then
- source._sign = signMgr()
- for _, sign in ipairs(source.signs) do
- source._sign:addSign(vm.compileNode(sign))
- end
- source.extends._generic = genericMgr(source.extends, source._sign)
- end
- end)
- : case 'doc.type.name'
- : call(function (source)
- local uri = guide.getUri(source)
- local name = source[1]
- local type = m.declareGlobal('type', name, uri)
- type:addGet(uri, source)
- source._globalNode = type
- end)
- : case 'doc.extends.name'
- : call(function (source)
- local uri = guide.getUri(source)
- local name = source[1]
- local class = m.declareGlobal('type', name, uri)
- class:addGet(uri, source)
- source._globalNode = class
- end)
-
-
----@alias vm.global.cate '"variable"' | '"type"'
-
----@param cate vm.global.cate
----@param name string
----@param uri uri
----@return vm.global
-function m.declareGlobal(cate, name, uri)
- local key = cate .. '|' .. name
- m.globalSubs[uri][key] = true
- if not m.globals[key] then
- m.globals[key] = globalBuilder(name, cate)
- end
- return m.globals[key]
-end
-
----@param cate vm.global.cate
----@param name string
----@param field? string
----@return vm.global?
-function m.getGlobal(cate, name, field)
- local key = cate .. '|' .. name
- if field then
- key = key .. vm.ID_SPLITE .. field
- end
- return m.globals[key]
-end
-
----@param cate vm.global.cate
----@param name string
----@return vm.global[]
-function m.getFields(cate, name)
- local globals = {}
- local key = cate .. '|' .. name
-
- -- TODO: optimize
- local clock = os.clock()
- for gid, global in pairs(m.globals) do
- if gid ~= key
- and util.stringStartWith(gid, key)
- and gid:sub(#key + 1, #key + 1) == vm.ID_SPLITE
- and not gid:find(vm.ID_SPLITE, #key + 2) then
- globals[#globals+1] = global
- end
- end
- local cost = os.clock() - clock
- if cost > 0.1 then
- log.warn('global-manager getFields cost %.3f', cost)
- end
-
- return globals
-end
-
----@param cate vm.global.cate
----@return vm.global[]
-function m.getGlobals(cate)
- local globals = {}
-
- -- TODO: optimize
- local clock = os.clock()
- for gid, global in pairs(m.globals) do
- if util.stringStartWith(gid, cate)
- and not gid:find(vm.ID_SPLITE) then
- globals[#globals+1] = global
- end
- end
- local cost = os.clock() - clock
- if cost > 0.1 then
- log.warn('global-manager getGlobals cost %.3f', cost)
- end
-
- return globals
-end
-
----@param suri uri
----@param cate vm.global.cate
----@return parser.object[]
-function m.getGlobalSets(suri, cate)
- local globals = m.getGlobals(cate)
- local result = {}
- for _, global in ipairs(globals) do
- local sets = global:getSets(suri)
- for _, set in ipairs(sets) do
- result[#result+1] = set
- end
- end
- return result
-end
-
----@param suri uri
----@param cate vm.global.cate
----@param name string
----@return boolean
-function m.hasGlobalSets(suri, cate, name)
- local global = m.getGlobal(cate, name)
- if not global then
- return false
- end
- local sets = global:getSets(suri)
- if #sets == 0 then
- return false
- end
- return true
-end
-
----@param source parser.object
-function m.compileObject(source)
- if source._globalNode ~= nil then
- return
- end
- source._globalNode = false
- compilerGlobalSwitch(source.type, source)
-end
-
----@param source parser.object
-function m.compileAst(source)
- local env = guide.getENV(source)
- m.compileObject(env)
- guide.eachSpecialOf(source, 'rawset', function (src)
- m.compileObject(src.parent)
- end)
- guide.eachSpecialOf(source, 'rawget', function (src)
- m.compileObject(src.parent)
- end)
- guide.eachSourceTypes(source.docs, {
- 'doc.class',
- 'doc.alias',
- 'doc.type.name',
- 'doc.extends.name',
- }, function (src)
- m.compileObject(src)
- end)
-end
-
----@return vm.global
-function m.getNode(source)
- if source.type == 'field'
- or source.type == 'method' then
- source = source.parent
- end
- return source._globalNode
-end
-
----@param uri uri
-function m.dropUri(uri)
- local globalSub = m.globalSubs[uri]
- m.globalSubs[uri] = nil
- for key in pairs(globalSub) do
- local global = m.globals[key]
- if global then
- global:dropUri(uri)
- if not global:isAlive() then
- m.globals[key] = nil
- end
- end
- end
-end
-
-return m
diff --git a/script/vm/global.lua b/script/vm/global.lua
index 1c46c9a3..a54ab552 100644
--- a/script/vm/global.lua
+++ b/script/vm/global.lua
@@ -1,5 +1,9 @@
-local util = require 'utility'
-local scope= require 'workspace.scope'
+local util = require 'utility'
+local scope = require 'workspace.scope'
+local guide = require 'parser.guide'
+local files = require 'files'
+---@class vm
+local vm = require 'vm.vm'
---@class vm.global.link
---@field gets parser.object[]
@@ -15,8 +19,6 @@ mt.__index = mt
mt.type = 'global'
mt.name = ''
-local ID_SPLITE = '\x1F'
-
---@param uri uri
---@param source parser.object
function mt:addSet(uri, source)
@@ -106,7 +108,7 @@ end
---@return string
function mt:getKeyName()
- return self.name:match('[^' .. ID_SPLITE .. ']+$')
+ return self.name:match('[^' .. vm.ID_SPLITE .. ']+$')
end
---@return boolean
@@ -116,10 +118,427 @@ end
---@param cate vm.global.cate
---@return vm.global
-return function (name, cate)
+local function createGlobal(name, cate)
return setmetatable({
name = name,
cate = cate,
links = util.multiTable(2),
}, mt)
end
+
+---@class parser.object
+---@field _globalNode vm.global
+
+---@type table<string, vm.global>
+local allGlobals = {}
+---@type table<uri, table<string, boolean>>
+local globalSubs = util.multiTable(2)
+
+local compileObject
+local compilerGlobalSwitch = util.switch()
+ : case 'local'
+ : call(function (source)
+ if source.special ~= '_G' then
+ return
+ end
+ if source.ref then
+ for _, ref in ipairs(source.ref) do
+ compileObject(ref)
+ end
+ end
+ end)
+ : case 'getlocal'
+ : call(function (source)
+ if source.special ~= '_G' then
+ return
+ end
+ if not source.next then
+ return
+ end
+ compileObject(source.next)
+ end)
+ : case 'setglobal'
+ : call(function (source)
+ local uri = guide.getUri(source)
+ local name = guide.getKeyName(source)
+ local global = vm.declareGlobal('variable', name, uri)
+ global:addSet(uri, source)
+ source._globalNode = global
+ end)
+ : case 'getglobal'
+ : call(function (source)
+ local uri = guide.getUri(source)
+ local name = guide.getKeyName(source)
+ local global = vm.declareGlobal('variable', name, uri)
+ global:addGet(uri, source)
+ source._globalNode = global
+
+ local nxt = source.next
+ if nxt then
+ compileObject(nxt)
+ end
+ end)
+ : case 'setfield'
+ : case 'setmethod'
+ : case 'setindex'
+ ---@param source parser.object
+ : call(function (source)
+ local name
+ local keyName = guide.getKeyName(source)
+ if not keyName then
+ return
+ end
+ if source.node._globalNode then
+ local parentName = source.node._globalNode:getName()
+ if parentName == '_G' then
+ name = keyName
+ else
+ name = ('%s%s%s'):format(parentName, vm.ID_SPLITE, keyName)
+ end
+ elseif source.node.special == '_G' then
+ name = keyName
+ end
+ if not name then
+ return
+ end
+ local uri = guide.getUri(source)
+ local global = vm.declareGlobal('variable', name, uri)
+ global:addSet(uri, source)
+ source._globalNode = global
+ end)
+ : case 'getfield'
+ : case 'getmethod'
+ : case 'getindex'
+ ---@param source parser.object
+ : call(function (source)
+ local name
+ local keyName = guide.getKeyName(source)
+ if not keyName then
+ return
+ end
+ if source.node._globalNode then
+ local parentName = source.node._globalNode:getName()
+ if parentName == '_G' then
+ name = keyName
+ else
+ name = ('%s%s%s'):format(parentName, vm.ID_SPLITE, keyName)
+ end
+ elseif source.node.special == '_G' then
+ name = keyName
+ end
+ local uri = guide.getUri(source)
+ local global = vm.declareGlobal('variable', name, uri)
+ global:addGet(uri, source)
+ source._globalNode = global
+
+ local nxt = source.next
+ if nxt then
+ compileObject(nxt)
+ end
+ end)
+ : case 'call'
+ : call(function (source)
+ if source.node.special == 'rawset'
+ or source.node.special == 'rawget' then
+ if not source.args then
+ return
+ end
+ local g = source.args[1]
+ local key = source.args[2]
+ if g and key and g.special == '_G' then
+ local name = guide.getKeyName(key)
+ if name then
+ local uri = guide.getUri(source)
+ local global = vm.declareGlobal('variable', name, uri)
+ if source.node.special == 'rawset' then
+ global:addSet(uri, source)
+ source.value = source.args[3]
+ else
+ global:addGet(uri, source)
+ end
+ source._globalNode = global
+
+ local nxt = source.next
+ if nxt then
+ compileObject(nxt)
+ end
+ end
+ end
+ end
+ end)
+ : case 'doc.class'
+ ---@param source parser.object
+ : call(function (source)
+ local uri = guide.getUri(source)
+ local name = guide.getKeyName(source)
+ local class = vm.declareGlobal('type', name, uri)
+ class:addSet(uri, source)
+ source._globalNode = class
+
+ if source.signs then
+ source._sign = vm.createSign()
+ for _, sign in ipairs(source.signs) do
+ source._sign:addSign(vm.compileNode(sign))
+ end
+ if source.extends then
+ for _, ext in ipairs(source.extends) do
+ if ext.type == 'doc.type.table' then
+ ext._generic = vm.createGeneric(ext, source._sign)
+ end
+ end
+ end
+ end
+ end)
+ : case 'doc.alias'
+ : call(function (source)
+ local uri = guide.getUri(source)
+ local name = guide.getKeyName(source)
+ local alias = vm.declareGlobal('type', name, uri)
+ alias:addSet(uri, source)
+ source._globalNode = alias
+
+ if source.signs then
+ source._sign = vm.createSign()
+ for _, sign in ipairs(source.signs) do
+ source._sign:addSign(vm.compileNode(sign))
+ end
+ source.extends._generic = vm.createGeneric(source.extends, source._sign)
+ end
+ end)
+ : case 'doc.type.name'
+ : call(function (source)
+ local uri = guide.getUri(source)
+ local name = source[1]
+ local type = vm.declareGlobal('type', name, uri)
+ type:addGet(uri, source)
+ source._globalNode = type
+ end)
+ : case 'doc.extends.name'
+ : call(function (source)
+ local uri = guide.getUri(source)
+ local name = source[1]
+ local class = vm.declareGlobal('type', name, uri)
+ class:addGet(uri, source)
+ source._globalNode = class
+ end)
+
+
+---@alias vm.global.cate '"variable"' | '"type"'
+
+---@param cate vm.global.cate
+---@param name string
+---@param uri? uri
+---@return vm.global
+function vm.declareGlobal(cate, name, uri)
+ local key = cate .. '|' .. name
+ if uri then
+ globalSubs[uri][key] = true
+ end
+ if not allGlobals[key] then
+ allGlobals[key] = createGlobal(name, cate)
+ end
+ return allGlobals[key]
+end
+
+---@param cate vm.global.cate
+---@param name string
+---@param field? string
+---@return vm.global?
+function vm.getGlobal(cate, name, field)
+ local key = cate .. '|' .. name
+ if field then
+ key = key .. vm.ID_SPLITE .. field
+ end
+ return allGlobals[key]
+end
+
+---@param cate vm.global.cate
+---@param name string
+---@return vm.global[]
+function vm.getGlobalFields(cate, name)
+ local globals = {}
+ local key = cate .. '|' .. name
+
+ local clock = os.clock()
+ for gid, global in pairs(allGlobals) do
+ if gid ~= key
+ and util.stringStartWith(gid, key)
+ and gid:sub(#key + 1, #key + 1) == vm.ID_SPLITE
+ and not gid:find(vm.ID_SPLITE, #key + 2) then
+ globals[#globals+1] = global
+ end
+ end
+ local cost = os.clock() - clock
+ if cost > 0.1 then
+ log.warn('global-manager getFields cost %.3f', cost)
+ end
+
+ return globals
+end
+
+---@param cate vm.global.cate
+---@return vm.global[]
+function vm.getGlobals(cate)
+ local globals = {}
+
+ local clock = os.clock()
+ for gid, global in pairs(allGlobals) do
+ if util.stringStartWith(gid, cate)
+ and not gid:find(vm.ID_SPLITE) then
+ globals[#globals+1] = global
+ end
+ end
+ local cost = os.clock() - clock
+ if cost > 0.1 then
+ log.warn('global-manager getGlobals cost %.3f', cost)
+ end
+
+ return globals
+end
+
+---@param suri uri
+---@param cate vm.global.cate
+---@return parser.object[]
+function vm.getGlobalSets(suri, cate)
+ local globals = vm.getGlobals(cate)
+ local result = {}
+ for _, global in ipairs(globals) do
+ local sets = global:getSets(suri)
+ for _, set in ipairs(sets) do
+ result[#result+1] = set
+ end
+ end
+ return result
+end
+
+---@param suri uri
+---@param cate vm.global.cate
+---@param name string
+---@return boolean
+function vm.hasGlobalSets(suri, cate, name)
+ local global = vm.getGlobal(cate, name)
+ if not global then
+ return false
+ end
+ local sets = global:getSets(suri)
+ if #sets == 0 then
+ return false
+ end
+ return true
+end
+
+---@param source parser.object
+function compileObject(source)
+ if source._globalNode ~= nil then
+ return
+ end
+ source._globalNode = false
+ compilerGlobalSwitch(source.type, source)
+end
+
+---@param source parser.object
+local function compileSelf(source)
+ if source.parent.type ~= 'funcargs' then
+ return
+ end
+ ---@type parser.object
+ local node = source.parent.parent and source.parent.parent.parent and source.parent.parent.parent.node
+ if not node then
+ return
+ end
+ local fields = vm.getLocalFields(source)
+ if not fields then
+ return
+ end
+ local nodeLocalID = vm.getLocalID(node)
+ local globalNode = node._globalNode
+ if not nodeLocalID and not globalNode then
+ return
+ end
+ for _, field in ipairs(fields) do
+ if field.type == 'setfield' then
+ local key = guide.getKeyName(field)
+ if key then
+ if nodeLocalID then
+ local myID = nodeLocalID .. vm.ID_SPLITE .. key
+ vm.insertLocalID(myID, field)
+ end
+ if globalNode then
+ local myID = globalNode:getName() .. vm.ID_SPLITE .. key
+ local myGlobal = vm.declareGlobal('variable', myID, guide.getUri(node))
+ myGlobal:addSet(guide.getUri(node), field)
+ end
+ end
+ end
+ end
+end
+
+---@param source parser.object
+local function compileAst(source)
+ local env = guide.getENV(source)
+ if not env then
+ return
+ end
+ compileObject(env)
+ guide.eachSpecialOf(source, 'rawset', function (src)
+ compileObject(src.parent)
+ end)
+ guide.eachSpecialOf(source, 'rawget', function (src)
+ compileObject(src.parent)
+ end)
+ guide.eachSourceTypes(source.docs, {
+ 'doc.class',
+ 'doc.alias',
+ 'doc.type.name',
+ 'doc.extends.name',
+ }, function (src)
+ compileObject(src)
+ end)
+
+ --[[
+ local mt
+ function mt:xxx()
+ self.a = 1
+ end
+
+ mt.a --> find this definition
+ ]]
+ guide.eachSourceType(source, 'self', function (src)
+ compileSelf(src)
+ end)
+end
+
+---@param uri uri
+local function dropUri(uri)
+ local globalSub = globalSubs[uri]
+ globalSubs[uri] = nil
+ for key in pairs(globalSub) do
+ local global = allGlobals[key]
+ if global then
+ global:dropUri(uri)
+ if not global:isAlive() then
+ allGlobals[key] = nil
+ end
+ end
+ end
+end
+
+for uri in files.eachFile() do
+ local state = files.getState(uri)
+ if state then
+ compileAst(state.ast)
+ end
+end
+
+files.watch(function (ev, uri)
+ if ev == 'update' then
+ dropUri(uri)
+ local state = files.getState(uri)
+ if state then
+ compileAst(state.ast)
+ end
+ end
+ if ev == 'remove' then
+ dropUri(uri)
+ end
+end)
diff --git a/script/vm/infer.lua b/script/vm/infer.lua
index 2a64ed52..fabc9828 100644
--- a/script/vm/infer.lua
+++ b/script/vm/infer.lua
@@ -1,11 +1,9 @@
local util = require 'utility'
local config = require 'config'
local guide = require 'parser.guide'
+---@class vm
local vm = require 'vm.vm'
----@class vm.infer-manager
-local m = {}
-
---@class vm.infer
---@field views table<string, boolean>
---@field cachedView? string
@@ -21,7 +19,7 @@ mt._hasDocFunction = false
mt._isParam = false
mt._isLocal = false
-m.NULL = setmetatable({}, mt)
+vm.NULL = setmetatable({}, mt)
local inferSorted = {
['boolean'] = - 100,
@@ -52,7 +50,7 @@ local viewNodeSwitch = util.switch()
: call(function (source, infer)
if source.type == 'table' then
if #source == 1 and source[1].type == 'varargs' then
- local node = m.getInfer(source[1]):view()
+ local node = vm.getInfer(source[1]):view()
return ('%s[]'):format(node)
end
end
@@ -90,7 +88,7 @@ local viewNodeSwitch = util.switch()
if source.signs then
local buf = {}
for i, sign in ipairs(source.signs) do
- buf[i] = m.getInfer(sign):view()
+ buf[i] = vm.getInfer(sign):view()
end
return ('%s<%s>'):format(source[1], table.concat(buf, ', '))
else
@@ -99,7 +97,7 @@ local viewNodeSwitch = util.switch()
end)
: case 'generic'
: call(function (source, infer)
- return m.getInfer(source.proto):view()
+ return vm.getInfer(source.proto):view()
end)
: case 'doc.generic.name'
: call(function (source, infer)
@@ -108,7 +106,7 @@ local viewNodeSwitch = util.switch()
: case 'doc.type.array'
: call(function (source, infer)
infer._hasClass = true
- local view = m.getInfer(source.node):view()
+ local view = vm.getInfer(source.node):view()
if source.node.type == 'doc.type' then
view = '(' .. view .. ')'
end
@@ -119,7 +117,7 @@ local viewNodeSwitch = util.switch()
infer._hasClass = true
local buf = {}
for i, sign in ipairs(source.signs) do
- buf[i] = m.getInfer(sign):view()
+ buf[i] = vm.getInfer(sign):view()
end
return ('%s<%s>'):format(source.node[1], table.concat(buf, ', '))
end)
@@ -144,20 +142,23 @@ local viewNodeSwitch = util.switch()
local argView = ''
local regView = ''
for i, arg in ipairs(source.args) do
+ local argNode = vm.compileNode(arg)
+ local isOptional = argNode:isOptional()
+ if isOptional then
+ argNode = argNode:copy()
+ argNode:removeOptional()
+ end
args[i] = string.format('%s%s: %s'
, arg.name[1]
- , arg.optional and '?' or ''
- , m.getInfer(arg):view()
+ , isOptional and '?' or ''
+ , vm.getInfer(argNode):view()
)
end
if #args > 0 then
argView = table.concat(args, ', ')
end
for i, ret in ipairs(source.returns) do
- rets[i] = string.format('%s%s'
- , m.getInfer(ret):view()
- , ret.optional and '?' or ''
- )
+ rets[i] = vm.getInfer(ret):view()
end
if #rets > 0 then
regView = ':' .. table.concat(rets, ', ')
@@ -165,16 +166,21 @@ local viewNodeSwitch = util.switch()
return ('fun(%s)%s'):format(argView, regView)
end)
----@param source parser.object
+---@param source parser.object | vm.node
---@return vm.infer
-function m.getInfer(source)
- local node = vm.compileNode(source)
+function vm.getInfer(source)
+ local node
+ if source.type == 'vm.node' then
+ node = source
+ else
+ node = vm.compileNode(source)
+ end
if node.lastInfer then
return node.lastInfer
end
local infer = setmetatable({
node = node,
- uri = guide.getUri(source),
+ uri = source.type ~= 'vm.node' and guide.getUri(source),
}, mt)
node.lastInfer = infer
@@ -199,24 +205,24 @@ function mt:_trim()
if self._hasTable and not self._hasClass then
self.views['table'] = true
end
- if self._hasClass then
- self:_eraseAlias()
- end
end
-function mt:_eraseAlias()
- local expandAlias = config.get(self.uri, 'Lua.hover.expandAlias')
+---@param uri uri
+---@return table<string, true>
+function mt:_eraseAlias(uri)
+ local drop = {}
+ local expandAlias = config.get(uri, 'Lua.hover.expandAlias')
for n in self.node:eachObject() do
if n.type == 'global' and n.cate == 'type' then
- for _, set in ipairs(n:getSets(self.uri)) do
+ for _, set in ipairs(n:getSets(uri)) do
if set.type == 'doc.alias' then
if expandAlias then
- self.views[n.name] = nil
+ drop[n.name] = true
else
for _, ext in ipairs(set.extends.types) do
local view = viewNodeSwitch(ext.type, ext, {})
if view and view ~= n.name then
- self.views[view] = nil
+ drop[view] = true
end
end
end
@@ -224,6 +230,7 @@ function mt:_eraseAlias()
end
end
end
+ return drop
end
---@param tp string
@@ -273,17 +280,16 @@ function mt:view(default, uri)
return 'any'
end
- if not next(self.views) then
- return default or 'unknown'
- end
-
- if self.cachedView then
- return self.cachedView
+ local drop
+ if self._hasClass then
+ drop = self:_eraseAlias(uri or self.uri)
end
local array = {}
for view in pairs(self.views) do
- array[#array+1] = view
+ if not drop or not drop[view] then
+ array[#array+1] = view
+ end
end
table.sort(array, function (a, b)
@@ -298,22 +304,29 @@ function mt:view(default, uri)
local max = #array
local limit = config.get(uri or self.uri, 'Lua.hover.enumsLimit')
- if max > limit then
- local view = string.format('%s...(+%d)'
- , table.concat(array, '|', 1, limit)
- , max - limit
- )
-
- self.cachedView = view
-
- return view
+ local view
+ if #array == 0 then
+ view = default or 'unknown'
else
- local view = table.concat(array, '|')
-
- self.cachedView = view
+ if max > limit then
+ view = string.format('%s...(+%d)'
+ , table.concat(array, '|', 1, limit)
+ , max - limit
+ )
+ else
+ view = table.concat(array, '|')
+ end
+ end
- return view
+ if self.node:isOptional() then
+ if max > 1 then
+ view = '(' .. view .. ')?'
+ else
+ view = view .. '?'
+ end
end
+
+ return view
end
function mt:eachView()
@@ -324,10 +337,10 @@ end
---@param other vm.infer
---@return vm.infer
function mt:merge(other)
- if self == m.NULL then
+ if self == vm.NULL then
return other
end
- if other == m.NULL then
+ if other == vm.NULL then
return self
end
@@ -390,8 +403,6 @@ end
---@param source parser.object
---@return string?
-function m.viewObject(source)
+function vm.viewObject(source)
return viewNodeSwitch(source.type, source, {})
end
-
-return m
diff --git a/script/vm/init.lua b/script/vm/init.lua
index 0058c698..f5003c11 100644
--- a/script/vm/init.lua
+++ b/script/vm/init.lua
@@ -1,4 +1,7 @@
local vm = require 'vm.vm'
+
+---@alias vm.object parser.object | vm.global | vm.generic
+
require 'vm.compiler'
require 'vm.value'
require 'vm.node'
@@ -8,5 +11,10 @@ require 'vm.field'
require 'vm.doc'
require 'vm.type'
require 'vm.library'
-require 'vm.manager'
+require 'vm.runner'
+require 'vm.infer'
+require 'vm.generic'
+require 'vm.sign'
+require 'vm.local-id'
+require 'vm.global'
return vm
diff --git a/script/vm/library.lua b/script/vm/library.lua
index 49f7adb0..e7bf4f42 100644
--- a/script/vm/library.lua
+++ b/script/vm/library.lua
@@ -13,24 +13,3 @@ function vm.getLibraryName(source)
end
return nil
end
-
-local globalLibraryNames = {
- 'arg', 'assert', 'error', 'collectgarbage', 'dofile', '_G', 'getfenv',
- 'getmetatable', 'ipairs', 'load', 'loadfile', 'loadstring',
- 'module', 'next', 'pairs', 'pcall', 'print', 'rawequal',
- 'rawget', 'rawlen', 'rawset', 'select', 'setfenv',
- 'setmetatable', 'tonumber', 'tostring', 'type', '_VERSION',
- 'warn', 'xpcall', 'require', 'unpack', 'bit32', 'coroutine',
- 'debug', 'io', 'math', 'os', 'package', 'string', 'table',
- 'utf8', 'newproxy',
-}
-local globalLibraryNamesMap
-function vm.isGlobalLibraryName(name)
- if not globalLibraryNamesMap then
- globalLibraryNamesMap = {}
- for _, v in ipairs(globalLibraryNames) do
- globalLibraryNamesMap[v] = true
- end
- end
- return globalLibraryNamesMap[name] or false
-end
diff --git a/script/vm/local-id.lua b/script/vm/local-id.lua
index 728de301..80c68769 100644
--- a/script/vm/local-id.lua
+++ b/script/vm/local-id.lua
@@ -1,13 +1,13 @@
local util = require 'utility'
local guide = require 'parser.guide'
+---@class vm
local vm = require 'vm.vm'
---@class parser.object
---@field _localID string
---@field _localIDs table<string, parser.object[]>
----@class vm.local-id
-local m = {}
+local compileLocalID, getLocal
local compileSwitch = util.switch()
: case 'local'
@@ -18,13 +18,13 @@ local compileSwitch = util.switch()
return
end
for _, ref in ipairs(source.ref) do
- m.compileLocalID(ref)
+ compileLocalID(ref)
end
end)
: case 'getlocal'
: call(function (source)
source._localID = ('%d'):format(source.node.start)
- m.compileLocalID(source.next)
+ compileLocalID(source.next)
end)
: case 'getfield'
: case 'setfield'
@@ -40,7 +40,7 @@ local compileSwitch = util.switch()
source._localID = parentID .. vm.ID_SPLITE .. key
source.field._localID = source._localID
if source.type == 'getfield' then
- m.compileLocalID(source.next)
+ compileLocalID(source.next)
end
end)
: case 'getmethod'
@@ -57,7 +57,7 @@ local compileSwitch = util.switch()
source._localID = parentID .. vm.ID_SPLITE .. key
source.method._localID = source._localID
if source.type == 'getmethod' then
- m.compileLocalID(source.next)
+ compileLocalID(source.next)
end
end)
: case 'getindex'
@@ -74,7 +74,7 @@ local compileSwitch = util.switch()
source._localID = parentID .. vm.ID_SPLITE .. key
source.index._localID = source._localID
if source.type == 'setindex' then
- m.compileLocalID(source.next)
+ compileLocalID(source.next)
end
end)
@@ -82,7 +82,7 @@ local leftSwitch = util.switch()
: case 'field'
: case 'method'
: call(function (source)
- return m.getLocal(source.parent)
+ return getLocal(source.parent)
end)
: case 'getfield'
: case 'setfield'
@@ -91,24 +91,36 @@ local leftSwitch = util.switch()
: case 'getindex'
: case 'setindex'
: call(function (source)
- return m.getLocal(source.node)
+ return getLocal(source.node)
end)
: case 'getlocal'
: call(function (source)
return source.node
end)
: case 'local'
+ : case 'self'
: call(function (source)
return source
end)
---@param source parser.object
---@return parser.object?
-function m.getLocal(source)
+function getLocal(source)
return leftSwitch(source.type, source)
end
-function m.compileLocalID(source)
+---@param id string
+---@param source parser.object
+function vm.insertLocalID(id, source)
+ local root = guide.getRoot(source)
+ if not root._localIDs then
+ root._localIDs = util.multiTable(2)
+ end
+ local sources = root._localIDs[id]
+ sources[#sources+1] = source
+end
+
+function compileLocalID(source)
if not source then
return
end
@@ -117,37 +129,33 @@ function m.compileLocalID(source)
return
end
compileSwitch(source.type, source)
- if not source._localID then
+ local id = source._localID
+ if not id then
return
end
- local root = guide.getRoot(source)
- if not root._localIDs then
- root._localIDs = util.multiTable(2)
- end
- local sources = root._localIDs[source._localID]
- sources[#sources+1] = source
+ vm.insertLocalID(id, source)
end
---@param source parser.object
----@return string|boolean
-function m.getID(source)
+---@return string?
+function vm.getLocalID(source)
if source._localID ~= nil then
return source._localID
end
source._localID = false
- local loc = m.getLocal(source)
+ local loc = getLocal(source)
if not loc then
return source._localID
end
- m.compileLocalID(loc)
+ compileLocalID(loc)
return source._localID
end
---@param source parser.object
---@param key? string
---@return parser.object[]?
-function m.getSources(source, key)
- local id = m.getID(source)
+function vm.getLocalSources(source, key)
+ local id = vm.getLocalID(source)
if not id then
return nil
end
@@ -166,8 +174,8 @@ end
---@param source parser.object
---@return parser.object[]
-function m.getFields(source)
- local id = m.getID(source)
+function vm.getLocalFields(source)
+ local id = vm.getLocalID(source)
if not id then
return nil
end
@@ -195,5 +203,3 @@ function m.getFields(source)
end
return fields
end
-
-return m
diff --git a/script/vm/local-manager.lua b/script/vm/local-manager.lua
deleted file mode 100644
index 51bafb24..00000000
--- a/script/vm/local-manager.lua
+++ /dev/null
@@ -1,40 +0,0 @@
-local util = require 'utility'
-local guide = require 'parser.guide'
-
----@class vm.local-node
-local m = {}
----@type table<uri, parser.object[]>
-m.locals = util.multiTable(2)
----@type table<parser.object, table<parser.object, boolean>>
-m.localSubs = util.multiTable(2, function ()
- return setmetatable({}, util.MODE_K)
-end)
----@type table<parser.object, boolean>
-m.allLocals = {}
-
----@param source parser.object
-function m.declareLocal(source)
- if m.allLocals[source] then
- return
- end
- m.allLocals[source] = true
- local uri = guide.getUri(source)
- local locals = m.locals[uri]
- locals[#locals+1] = source
-end
-
----@param uri uri
-function m.dropUri(uri)
- local locals = m.locals[uri]
- m.locals[uri] = nil
- for _, loc in ipairs(locals) do
- m.allLocals[loc] = nil
- local localSubs = m.localSubs[loc]
- m.localSubs[loc] = nil
- for source in pairs(localSubs) do
- source._node = nil
- end
- end
-end
-
-return m
diff --git a/script/vm/manager.lua b/script/vm/manager.lua
deleted file mode 100644
index 58255fca..00000000
--- a/script/vm/manager.lua
+++ /dev/null
@@ -1,26 +0,0 @@
-
-local files = require 'files'
-local globalManager = require 'vm.global-manager'
-local localManager = require 'vm.local-manager'
-
----@alias vm.object parser.object | vm.global | vm.generic
-
----@class vm.state
-local m = {}
-
-files.watch(function (ev, uri)
- if ev == 'update' then
- globalManager.dropUri(uri)
- localManager.dropUri(uri)
- local state = files.getState(uri)
- if state then
- globalManager.compileAst(state.ast)
- end
- end
- if ev == 'remove' then
- globalManager.dropUri(uri)
- localManager.dropUri(uri)
- end
-end)
-
-return m
diff --git a/script/vm/node.lua b/script/vm/node.lua
index 6906da7e..e76542aa 100644
--- a/script/vm/node.lua
+++ b/script/vm/node.lua
@@ -1,5 +1,4 @@
local files = require 'files'
-local localMgr = require 'vm.local-manager'
---@class vm
local vm = require 'vm.vm'
local ws = require 'workspace.workspace'
@@ -8,15 +7,14 @@ local ws = require 'workspace.workspace'
vm.nodeCache = {}
---@class vm.node
+---@field [integer] vm.object
local mt = {}
mt.__index = mt
+mt.id = 0
mt.type = 'vm.node'
mt.optional = nil
mt.lastInfer = nil
mt.data = nil
----@type vm.node[]
-mt._childs = nil
-mt._locked = false
---@param node vm.node | vm.object
function mt:merge(node)
@@ -30,20 +28,10 @@ function mt:merge(node)
if node:isOptional() then
self.optional = true
end
- if node._locked then
- if not self._childs then
- self._childs = {}
- end
- if not self._childs[node] then
- self._childs[#self._childs+1] = node
- self._childs[node] = true
- end
- else
- for _, obj in ipairs(node) do
- if not self[obj] then
- self[obj] = true
- self[#self+1] = obj
- end
+ for _, obj in ipairs(node) do
+ if not self[obj] then
+ self[obj] = true
+ self[#self+1] = obj
end
end
else
@@ -54,84 +42,25 @@ function mt:merge(node)
end
end
-function mt:_each(mark, callback)
- if mark[self] then
- return
- end
- mark[self] = true
- for i = 1, #self do
- callback(self[i])
- end
- local childs = self._childs
- if not childs then
- return
- end
- for i = 1, #childs do
- local child = childs[i]
- if not child:isLocked() then
- child:_each(mark, callback)
- end
- end
-end
-
-function mt:_expand()
- local childs = self._childs
- if not childs then
- return
- end
- self._childs = nil
-
- local mark = {}
- mark[self] = true
-
- local function insert(obj)
- if not self[obj] then
- self[obj] = true
- self[#self+1] = obj
- end
- end
-
- for i = 1, #childs do
- local child = childs[i]
- if child:isLocked() then
- if not self._childs then
- self._childs = {}
- end
- if not self._childs[child] then
- self._childs[#self._childs+1] = child
- self._childs[child] = true
- end
- else
- child:_each(mark, insert)
- end
- end
-end
-
---@return boolean
function mt:isEmpty()
- self:_expand()
return #self == 0
end
+function mt:clear()
+ self.optional = nil
+ for i, c in ipairs(self) do
+ self[i] = nil
+ self[c] = nil
+ end
+end
+
---@param n integer
---@return vm.object?
function mt:get(n)
- self:_expand()
return self[n]
end
-function mt:lock()
- self._locked = true
-end
-
-function mt:unlock()
- self._locked = false
-end
-
-function mt:isLocked()
- return self._locked == true
-end
-
function mt:setData(k, v)
if not self.data then
self.data = {}
@@ -147,49 +76,143 @@ function mt:getData(k)
end
function mt:addOptional()
- if self:isOptional() then
- return self
- end
self.optional = true
end
function mt:removeOptional()
- if not self:isOptional() then
- return self
- end
- self:_expand()
- for i = #self, 1, -1 do
- local n = self[i]
- if n.type == 'nil'
- or (n.type == 'boolean' and n[1] == false)
- or (n.type == 'doc.type.boolean' and n[1] == false) then
- self[i] = self[#self]
- self[#self] = nil
- end
- end
+ self:remove 'nil'
end
---@return boolean
function mt:isOptional()
- if self.optional ~= nil then
- return self.optional
+ return self.optional == true
+end
+
+---@return boolean
+function mt:hasFalsy()
+ if self.optional then
+ return true
end
- self:_expand()
for _, c in ipairs(self) do
if c.type == 'nil'
+ or (c.type == 'global' and c.cate == 'type' and c.name == 'nil')
+ or (c.type == 'global' and c.cate == 'type' and c.name == 'false')
or (c.type == 'boolean' and c[1] == false)
or (c.type == 'doc.type.boolean' and c[1] == false) then
- self.optional = true
return true
end
end
- self.optional = false
return false
end
+---@return boolean
+function mt:isNullable()
+ if self.optional then
+ return true
+ end
+ if #self == 0 then
+ return true
+ end
+ for _, c in ipairs(self) do
+ if c.type == 'nil'
+ or (c.type == 'global' and c.cate == 'type' and c.name == 'nil')
+ or (c.type == 'global' and c.cate == 'type' and c.name == 'any') then
+ return true
+ end
+ end
+ return false
+end
+
+---@return vm.node
+function mt:setTruthy()
+ if self.optional == true then
+ self.optional = nil
+ end
+ local hasBoolean
+ for index = #self, 1, -1 do
+ local c = self[index]
+ if c.type == 'nil'
+ or (c.type == 'global' and c.cate == 'type' and c.name == 'nil')
+ or (c.type == 'global' and c.cate == 'type' and c.name == 'false')
+ or (c.type == 'boolean' and c[1] == false)
+ or (c.type == 'doc.type.boolean' and c[1] == false) then
+ table.remove(self, index)
+ self[c] = nil
+ goto CONTINUE
+ end
+ if (c.type == 'global' and c.cate == 'type' and c.name == 'boolean')
+ or (c.type == 'boolean' or c.type == 'doc.type.boolean') then
+ hasBoolean = true
+ table.remove(self, index)
+ self[c] = nil
+ goto CONTINUE
+ end
+ ::CONTINUE::
+ end
+ if hasBoolean then
+ self[#self+1] = vm.declareGlobal('type', 'true')
+ end
+end
+
+---@return vm.node
+function mt:setFalsy()
+ if self.optional == false then
+ self.optional = nil
+ end
+ local hasBoolean
+ for index = #self, 1, -1 do
+ local c = self[index]
+ if c.type == 'nil'
+ or (c.type == 'global' and c.cate == 'type' and c.name == 'nil')
+ or (c.type == 'global' and c.cate == 'type' and c.name == 'false')
+ or (c.type == 'boolean' and c[1] == true)
+ or (c.type == 'doc.type.boolean' and c[1] == true) then
+ goto CONTINUE
+ end
+ if (c.type == 'global' and c.cate == 'type' and c.name == 'boolean')
+ or (c.type == 'boolean' or c.type == 'doc.type.boolean') then
+ hasBoolean = true
+ table.remove(self, index)
+ self[c] = nil
+ end
+ ::CONTINUE::
+ end
+ if hasBoolean then
+ self[#self+1] = vm.declareGlobal('type', 'false')
+ end
+end
+
+---@param name string
+function mt:remove(name)
+ if name == 'nil' and self.optional == true then
+ self.optional = nil
+ end
+ for index = #self, 1, -1 do
+ local c = self[index]
+ if (c.type == 'global' and c.cate == 'type' and c.name == name)
+ or (c.type == name)
+ or (c.type == 'doc.type.integer' and (name == 'number' or name == 'integer'))
+ or (c.type == 'doc.type.boolean' and name == 'boolean')
+ or (c.type == 'doc.type.table' and name == 'table')
+ or (c.type == 'doc.type.array' and name == 'table')
+ or (c.type == 'doc.type.function' and name == 'function') then
+ table.remove(self, index)
+ self[c] = nil
+ end
+ end
+end
+
+---@param node vm.node
+function mt:removeNode(node)
+ for _, c in ipairs(node) do
+ if c.type == 'global' and c.cate == 'type' then
+ self:remove(c.name)
+ end
+ end
+end
+
---@return fun():vm.object
function mt:eachObject()
- self:_expand()
local i = 0
return function ()
i = i + 1
@@ -197,12 +220,21 @@ function mt:eachObject()
end
end
----@param source parser.object | vm.generic
+---@return vm.node
+function mt:copy()
+ return vm.createNode(self)
+end
+
+---@param source vm.object
---@param node vm.node | vm.object
---@param cover? boolean
function vm.setNode(source, node, cover)
if not node then
- error('Can not set nil node')
+ if TEST then
+ error('Can not set nil node')
+ else
+ log.error('Can not set nil node')
+ end
end
if source.type == 'global' then
error('Can not set node to global')
@@ -216,13 +248,14 @@ function vm.setNode(source, node, cover)
me:merge(node)
else
if node.type == 'vm.node' then
- vm.nodeCache[source] = node
+ vm.nodeCache[source] = node:copy()
else
vm.nodeCache[source] = vm.createNode(node)
end
end
end
+---@param source vm.object
---@return vm.node?
function vm.getNode(source)
return vm.nodeCache[source]
@@ -256,11 +289,16 @@ function vm.clearNodeCache()
vm.nodeCache = {}
end
+local ID = 0
+
---@param a? vm.node | vm.object
---@param b? vm.node | vm.object
---@return vm.node
function vm.createNode(a, b)
- local node = setmetatable({}, mt)
+ ID = ID + 1
+ local node = setmetatable({
+ id = ID,
+ }, mt)
if a then
node:merge(a)
end
diff --git a/script/vm/ref.lua b/script/vm/ref.lua
index 65e8fdab..545c294a 100644
--- a/script/vm/ref.lua
+++ b/script/vm/ref.lua
@@ -2,8 +2,6 @@
local vm = require 'vm.vm'
local util = require 'utility'
local guide = require 'parser.guide'
-local localID = require 'vm.local-id'
-local globalMgr = require 'vm.global-manager'
local files = require 'files'
local await = require 'await'
local progress = require 'progress'
@@ -242,7 +240,7 @@ end
---@param source parser.object
---@param pushResult fun(src: parser.object)
local function searchByLocalID(source, pushResult)
- local idSources = localID.getSources(source)
+ local idSources = vm.getLocalSources(source)
if not idSources then
return
end
@@ -291,7 +289,7 @@ end
---@async
---@param source parser.object
----@param fileNotify fun(uri: uri): boolean
+---@param fileNotify? fun(uri: uri): boolean
function vm.getRefs(source, fileNotify)
local results = {}
local mark = {}
diff --git a/script/vm/runner.lua b/script/vm/runner.lua
new file mode 100644
index 00000000..9fe0f172
--- /dev/null
+++ b/script/vm/runner.lua
@@ -0,0 +1,444 @@
+---@class vm
+local vm = require 'vm.vm'
+local guide = require 'parser.guide'
+
+---@class vm.runner
+---@field loc parser.object
+---@field mainBlock parser.object
+---@field blocks table<parser.object, true>
+---@field steps vm.runner.step[]
+local mt = {}
+mt.__index = mt
+mt.index = 1
+
+---@class parser.object
+---@field _casts parser.object[]
+
+---@class vm.runner.step
+---@field type 'truthy' | 'falsy' | 'as' | 'add' | 'remove' | 'object' | 'save' | 'push' | 'merge' | 'cast'
+---@field pos integer
+---@field order? integer
+---@field node? vm.node
+---@field object? parser.object
+---@field name? string
+---@field cast? parser.object
+---@field tag? string
+---@field copy? boolean
+---@field new? boolean
+---@field ref1? vm.runner.step
+---@field ref2? vm.runner.step
+
+---@param filter parser.object
+---@param outStep vm.runner.step
+---@param blockStep vm.runner.step
+function mt:_compileNarrowByFilter(filter, outStep, blockStep)
+ if not filter then
+ return
+ end
+ if filter.type == 'paren' then
+ if filter.exp then
+ self:_compileNarrowByFilter(filter.exp, outStep, blockStep)
+ end
+ return
+ end
+ if filter.type == 'unary' then
+ if not filter.op
+ or not filter[1] then
+ return
+ end
+ if filter.op.type == 'not' then
+ local exp = filter[1]
+ if exp.type == 'getlocal' and exp.node == self.loc then
+ self.steps[#self.steps+1] = {
+ type = 'falsy',
+ pos = filter.finish,
+ new = true,
+ }
+ self.steps[#self.steps+1] = {
+ type = 'truthy',
+ pos = filter.finish,
+ ref1 = outStep,
+ }
+ end
+ end
+ elseif filter.type == 'binary' then
+ if not filter.op
+ or not filter[1]
+ or not filter[2] then
+ return
+ end
+ if filter.op.type == 'and' then
+ local dummyStep = {
+ type = 'save',
+ copy = true,
+ ref1 = outStep,
+ pos = filter.start - 1,
+ }
+ self.steps[#self.steps+1] = dummyStep
+ self:_compileNarrowByFilter(filter[1], dummyStep, blockStep)
+ self:_compileNarrowByFilter(filter[2], dummyStep, blockStep)
+ end
+ if filter.op.type == 'or' then
+ self:_compileNarrowByFilter(filter[1], outStep, blockStep)
+ local dummyStep = {
+ type = 'push',
+ copy = true,
+ ref1 = outStep,
+ pos = filter.op.finish,
+ }
+ self.steps[#self.steps+1] = dummyStep
+ self:_compileNarrowByFilter(filter[2], outStep, dummyStep)
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ tag = 'or reset',
+ ref1 = blockStep,
+ pos = filter.finish,
+ }
+ end
+ if filter.op.type == '=='
+ or filter.op.type == '~=' then
+ local loc, exp
+ for i = 1, 2 do
+ loc = filter[i]
+ if loc.type == 'getlocal' and loc.node == self.loc then
+ exp = filter[i % 2 + 1]
+ break
+ end
+ end
+ if not loc or not exp then
+ return
+ end
+ if guide.isLiteral(exp) then
+ if filter.op.type == '==' then
+ self.steps[#self.steps+1] = {
+ type = 'remove',
+ name = exp.type,
+ pos = filter.finish,
+ ref1 = outStep,
+ }
+ self.steps[#self.steps+1] = {
+ type = 'as',
+ name = exp.type,
+ pos = filter.finish,
+ new = true,
+ }
+ end
+ if filter.op.type == '~=' then
+ self.steps[#self.steps+1] = {
+ type = 'as',
+ name = exp.type,
+ pos = filter.finish,
+ ref1 = outStep,
+ }
+ self.steps[#self.steps+1] = {
+ type = 'remove',
+ name = exp.type,
+ pos = filter.finish,
+ new = true,
+ }
+ end
+ end
+ end
+ else
+ if filter.type == 'getlocal' and filter.node == self.loc then
+ self.steps[#self.steps+1] = {
+ type = 'truthy',
+ pos = filter.finish,
+ new = true,
+ }
+ self.steps[#self.steps+1] = {
+ type = 'falsy',
+ pos = filter.finish,
+ ref1 = outStep,
+ }
+ end
+ end
+end
+
+---@param block parser.object
+function mt:_compileBlock(block)
+ if self.blocks[block] then
+ return
+ end
+ self.blocks[block] = true
+ if block == self.mainBlock then
+ return
+ end
+
+ local parentBlock = guide.getParentBlock(block)
+ self:_compileBlock(parentBlock)
+
+ if block.type == 'if' then
+ ---@type vm.runner.step[]
+ local finals = {}
+ for i, childBlock in ipairs(block) do
+ local blockStep = {
+ type = 'save',
+ tag = 'block',
+ copy = true,
+ pos = childBlock.start,
+ }
+ local outStep = {
+ type = 'save',
+ tag = 'out',
+ copy = true,
+ pos = childBlock.start,
+ }
+ self.steps[#self.steps+1] = blockStep
+ self.steps[#self.steps+1] = outStep
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ ref1 = blockStep,
+ pos = childBlock.start,
+ }
+ self:_compileNarrowByFilter(childBlock.filter, outStep, blockStep)
+ if not childBlock.hasReturn
+ and not childBlock.hasGoTo
+ and not childBlock.hasBreak then
+ local finalStep = {
+ type = 'save',
+ pos = childBlock.finish,
+ tag = 'final #' .. i,
+ }
+ finals[#finals+1] = finalStep
+ self.steps[#self.steps+1] = finalStep
+ end
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ tag = 'reset child',
+ ref1 = outStep,
+ pos = childBlock.finish,
+ }
+ end
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ tag = 'reset if',
+ pos = block.finish,
+ copy = true,
+ }
+ for _, final in ipairs(finals) do
+ self.steps[#self.steps+1] = {
+ type = 'merge',
+ ref2 = final,
+ pos = block.finish,
+ }
+ end
+ end
+
+ if block.type == 'function'
+ or block.type == 'while'
+ or block.type == 'loop'
+ or block.type == 'in'
+ or block.type == 'repeat'
+ or block.type == 'for' then
+ local savePoint = {
+ type = 'save',
+ copy = true,
+ pos = block.start,
+ }
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ copy = true,
+ pos = block.start,
+ }
+ self.steps[#self.steps+1] = savePoint
+ self.steps[#self.steps+1] = {
+ type = 'push',
+ pos = block.finish,
+ ref1 = savePoint,
+ }
+ end
+end
+
+---@return parser.object[]
+function mt:_getCasts()
+ local root = guide.getRoot(self.loc)
+ if not root._casts then
+ root._casts = {}
+ local docs = root.docs
+ for _, doc in ipairs(docs) do
+ if doc.type == 'doc.cast' and doc.loc then
+ root._casts[#root._casts+1] = doc
+ end
+ end
+ end
+ return root._casts
+end
+
+function mt:_preCompile()
+ local startPos = self.loc.start
+ local finishPos = 0
+
+ for _, ref in ipairs(self.loc.ref) do
+ self.steps[#self.steps+1] = {
+ type = 'object',
+ object = ref,
+ pos = ref.range or ref.start,
+ }
+ if ref.start > finishPos then
+ finishPos = ref.start
+ end
+ local block = guide.getParentBlock(ref)
+ self:_compileBlock(block)
+ end
+
+ for i, step in ipairs(self.steps) do
+ if step.type ~= 'object' then
+ step.order = i
+ end
+ end
+
+ local casts = self:_getCasts()
+ for _, cast in ipairs(casts) do
+ if cast.loc[1] == self.loc[1]
+ and cast.start > startPos
+ and cast.finish < finishPos
+ and guide.getLocal(self.loc, self.loc[1], cast.start) == self.loc then
+ self.steps[#self.steps+1] = {
+ type = 'cast',
+ cast = cast,
+ pos = cast.start,
+ }
+ end
+ end
+
+ table.sort(self.steps, function (a, b)
+ if a.pos == b.pos then
+ return (a.order or 0) < (b.order or 0)
+ else
+ return a.pos < b.pos
+ end
+ end)
+end
+
+---@param loc parser.object
+---@param node vm.node
+---@return vm.node
+local function checkAssert(loc, node)
+ local parent = loc.parent
+ if parent.type == 'binary' then
+ if parent.op and (parent.op.type == '~=' or parent.op.type == '==') then
+ local exp
+ for i = 1, 2 do
+ if parent[i] == loc then
+ exp = parent[i % 2 + 1]
+ end
+ end
+ if exp and guide.isLiteral(exp) then
+ local callargs = parent.parent
+ if callargs.type == 'callargs'
+ and callargs.parent.node.special == 'assert'
+ and callargs[1] == parent then
+ if parent.op.type == '~=' then
+ node:remove(exp.type)
+ end
+ if parent.op.type == '==' then
+ node = vm.compileNode(exp)
+ end
+ end
+ end
+ end
+ end
+ if parent.type == 'callargs'
+ and parent.parent.node.special == 'assert'
+ and parent[1] == loc then
+ node:setTruthy()
+ end
+ return node
+end
+
+---@param callback fun(src: parser.object, node: vm.node)
+function mt:launch(callback)
+ local topNode = vm.getNode(self.loc):copy()
+ for _, step in ipairs(self.steps) do
+ local node = step.ref1 and step.ref1.node or topNode
+ if step.type == 'truthy' then
+ if step.new then
+ node = node:copy()
+ topNode = node
+ end
+ node:setTruthy()
+ elseif step.type == 'falsy' then
+ if step.new then
+ node = node:copy()
+ topNode = node
+ end
+ node:setFalsy()
+ elseif step.type == 'as' then
+ if step.new then
+ topNode = vm.createNode(vm.getGlobal('type', step.name))
+ else
+ node:clear()
+ node:merge(vm.getGlobal('type', step.name))
+ end
+ elseif step.type == 'add' then
+ if step.new then
+ node = node:copy()
+ topNode = node
+ end
+ node:merge(vm.getGlobal('type', step.name))
+ elseif step.type == 'remove' then
+ if step.new then
+ node = node:copy()
+ topNode = node
+ end
+ node:remove(step.name)
+ elseif step.type == 'object' then
+ topNode = callback(step.object, node) or node
+ if step.object.type == 'getlocal' then
+ topNode = checkAssert(step.object, node)
+ end
+ elseif step.type == 'save' then
+ if step.copy then
+ node = node:copy()
+ end
+ step.node = node
+ elseif step.type == 'push' then
+ if step.copy then
+ node = node:copy()
+ end
+ topNode = node
+ elseif step.type == 'merge' then
+ node:merge(step.ref2.node)
+ elseif step.type == 'cast' then
+ topNode = node:copy()
+ for _, cast in ipairs(step.cast.casts) do
+ if cast.mode == '+' then
+ if cast.optional then
+ topNode:addOptional()
+ end
+ if cast.extends then
+ topNode:merge(vm.compileNode(cast.extends))
+ end
+ elseif cast.mode == '-' then
+ if cast.optional then
+ topNode:removeOptional()
+ end
+ if cast.extends then
+ topNode:removeNode(vm.compileNode(cast.extends))
+ end
+ else
+ if cast.extends then
+ topNode:clear()
+ topNode:merge(vm.compileNode(cast.extends))
+ end
+ end
+ end
+ end
+ end
+end
+
+---@param loc parser.object
+---@return vm.runner
+function vm.createRunner(loc)
+ local self = setmetatable({
+ loc = loc,
+ mainBlock = guide.getParentBlock(loc),
+ blocks = {},
+ steps = {},
+ }, mt)
+
+ self:_preCompile()
+
+ return self
+end
diff --git a/script/vm/sign.lua b/script/vm/sign.lua
index 2d45a5a7..fe112bc2 100644
--- a/script/vm/sign.lua
+++ b/script/vm/sign.lua
@@ -1,6 +1,6 @@
local guide = require 'parser.guide'
+---@class vm
local vm = require 'vm.vm'
-local infer = require 'vm.infer'
---@class vm.sign
---@field parent parser.object
@@ -16,12 +16,12 @@ end
---@param uri uri
---@param args parser.object
+---@param removeGeneric true?
---@return table<string, vm.node>
-function mt:resolve(uri, args)
+function mt:resolve(uri, args, removeGeneric)
if not args then
return nil
end
- local globalMgr = require 'vm.global-manager'
local resolved = {}
---@param object parser.object
@@ -33,7 +33,7 @@ function mt:resolve(uri, args)
-- 'number' -> `T`
for n in node:eachObject() do
if n.type == 'string' then
- local type = globalMgr.declareGlobal('type', n[1], guide.getUri(n))
+ local type = vm.declareGlobal('type', n[1], guide.getUri(n))
resolved[key] = vm.createNode(type, resolved[key])
end
end
@@ -48,6 +48,19 @@ function mt:resolve(uri, args)
-- number[] -> T[]
resolve(object.node, vm.compileNode(n.node))
end
+ if n.type == 'doc.type.table' then
+ -- { [integer]: number } -> T[]
+ local tvalueNode = vm.getTableValue(uri, node, 'integer')
+ if tvalueNode then
+ resolve(object.node, tvalueNode)
+ end
+ end
+ if n.type == 'global' and n.cate == 'type' then
+ -- ---@field [integer]: number -> T[]
+ vm.getClassFields(uri, n, vm.declareGlobal('type', 'integer'), false, function (field)
+ resolve(object.node, vm.compileNode(field.extends))
+ end)
+ end
end
end
if object.type == 'doc.type.table' then
@@ -98,7 +111,7 @@ function mt:resolve(uri, args)
goto CONTINUE
end
end
- local view = infer.viewObject(obj)
+ local view = vm.viewObject(obj)
if view then
knownTypes[view] = true
end
@@ -114,10 +127,10 @@ function mt:resolve(uri, args)
local function buildArgNode(argNode, knownTypes)
local newArgNode = vm.createNode()
for n in argNode:eachObject() do
- if argNode:isOptional() and vm.isFalsy(n) then
+ if argNode:hasFalsy() then
goto CONTINUE
end
- local view = infer.viewObject(n)
+ local view = vm.viewObject(n)
if knownTypes[view] then
goto CONTINUE
end
@@ -156,7 +169,7 @@ function mt:resolve(uri, args)
end
---@return vm.sign
-return function ()
+function vm.createSign()
local genericMgr = setmetatable({
signList = {},
}, mt)
diff --git a/script/vm/type.lua b/script/vm/type.lua
index fa02d19e..c3264993 100644
--- a/script/vm/type.lua
+++ b/script/vm/type.lua
@@ -1,4 +1,3 @@
-local globalMgr = require 'vm.global-manager'
---@class vm
local vm = require 'vm.vm'
@@ -9,10 +8,10 @@ local vm = require 'vm.vm'
---@return boolean
function vm.isSubType(uri, child, parent, mark)
if type(parent) == 'string' then
- parent = vm.createNode(globalMgr.getGlobal('type', parent))
+ parent = vm.createNode(vm.getGlobal('type', parent))
end
if type(child) == 'string' then
- child = vm.createNode(globalMgr.getGlobal('type', child))
+ child = vm.createNode(vm.getGlobal('type', child))
end
if not child or not parent then
@@ -134,7 +133,7 @@ function vm.getTableKey(uri, tnode, vnode)
end
end
if tn.type == 'doc.type.array' then
- result:merge(globalMgr.getGlobal('type', 'integer'))
+ result:merge(vm.declareGlobal('type', 'integer'))
end
if tn.type == 'table' then
for _, field in ipairs(tn) do
@@ -144,10 +143,10 @@ function vm.getTableKey(uri, tnode, vnode)
end
end
if field.type == 'tablefield' then
- result:merge(globalMgr.getGlobal('type', 'string'))
+ result:merge(vm.declareGlobal('type', 'string'))
end
if field.type == 'tableexp' then
- result:merge(globalMgr.getGlobal('type', 'integer'))
+ result:merge(vm.declareGlobal('type', 'integer'))
end
end
end
diff --git a/script/vm/value.lua b/script/vm/value.lua
index a784be2a..d29ca9d0 100644
--- a/script/vm/value.lua
+++ b/script/vm/value.lua
@@ -17,7 +17,16 @@ function vm.test(source)
hasTrue = true
end
if n[1] == false then
- hasTrue = false
+ hasFalse = true
+ end
+ end
+ if n.type == 'global' and n.cate == 'type' then
+ if n.name == 'true' then
+ hasTrue = true
+ end
+ if n.name == 'false'
+ or n.name == 'nil' then
+ hasFalse = true
end
end
if n.type == 'nil' then
@@ -41,28 +50,9 @@ function vm.test(source)
end
end
----@param source parser.object
----@return boolean
-function vm.isFalsy(source)
- if source.type == 'nil' then
- return true
- end
- if source.type == 'boolean'
- or source.type == 'doc.type.boolean' then
- return source[1] == false
- end
- return false
-end
-
---@param v vm.object
---@return string?
local function getUnique(v)
- if v.type == 'local' then
- return ('loc:%s@%d'):format(guide.getUri(v), v.start)
- end
- if v.type == 'global' then
- return ('%s:%s'):format(v.cate, v.name)
- end
if v.type == 'boolean' then
if v[1] == nil then
return false
diff --git a/script/vm/vm.lua b/script/vm/vm.lua
index 3c1762bf..8117d311 100644
--- a/script/vm/vm.lua
+++ b/script/vm/vm.lua
@@ -23,6 +23,7 @@ function m.getSpecial(source)
return source.special
end
+---@return string?
function m.getKeyName(source)
if not source then
return nil
diff --git a/script/workspace/loading.lua b/script/workspace/loading.lua
index f40c08c6..66e0a3aa 100644
--- a/script/workspace/loading.lua
+++ b/script/workspace/loading.lua
@@ -65,7 +65,7 @@ function mt:checkMaxPreload(uri)
end
---@param uri uri
----@param libraryUri boolean
+---@param libraryUri? uri
---@async
function mt:loadFile(uri, libraryUri)
if files.isLua(uri) then
diff --git a/script/workspace/workspace.lua b/script/workspace/workspace.lua
index 91923bb8..33f8784d 100644
--- a/script/workspace/workspace.lua
+++ b/script/workspace/workspace.lua
@@ -68,9 +68,10 @@ local globInteferFace = {
type = function (path)
local result
pcall(function ()
- if fs.is_directory(fs.path(path)) then
+ local status = fs.symlink_status(path):type()
+ if status == 'directory' then
result = 'directory'
- else
+ elseif status == 'regular' then
result = 'file'
end
end)
@@ -78,7 +79,7 @@ local globInteferFace = {
end,
list = function (path)
local fullPath = fs.path(path)
- if not fs.exists(fullPath) then
+ if fs.symlink_status(fullPath):type() ~= 'directory' then
return nil
end
local paths = {}
@@ -332,6 +333,8 @@ function m.findUrisByFilePath(path)
return results
end
+---@param path string
+---@return string
function m.normalize(path)
if not path then
return nil
diff --git a/test.lua b/test.lua
index c22bd18c..52a525ac 100644
--- a/test.lua
+++ b/test.lua
@@ -77,7 +77,7 @@ local function main()
local lclient = require 'lclient'
local ws = require 'workspace'
- log.print = true
+ --log.print = true
---@async
lclient():start(function (client)
diff --git a/test/command/auto-require.lua b/test/command/auto-require.lua
index 9dd19bd2..38cc0012 100644
--- a/test/command/auto-require.lua
+++ b/test/command/auto-require.lua
@@ -3,9 +3,12 @@ local files = require 'files'
local autoRequire = require 'core.command.autoRequire'
local client = require 'client'
-local findInsertRow = util.getUpvalue(autoRequire, 'findInsertRow')
+local findInsertRow = util.getUpvalue(autoRequire, 'findInsertRow')
local applyAutoRequire = util.getUpvalue(autoRequire, 'applyAutoRequire')
+assert(findInsertRow)
+assert(applyAutoRequire)
+
local originEditText = client.editText
local EditResult
diff --git a/test/completion/common.lua b/test/completion/common.lua
index 538ac401..613f9b0c 100644
--- a/test/completion/common.lua
+++ b/test/completion/common.lua
@@ -3311,3 +3311,40 @@ TEST [[
end
assert(count == 1)
end)
+
+TEST [[
+local x
+x.y.z = xxx
+
+x.y.<??>
+]]
+{
+ {
+ label = 'z',
+ kind = define.CompletionItemKind.Field,
+ }
+}
+
+TEST [[
+local xyz
+
+---@cast <??>
+]]
+{
+ {
+ label = 'xyz',
+ kind = define.CompletionItemKind.Variable,
+ },
+}
+
+TEST [[
+local xyz
+
+---@cast x<??>
+]]
+{
+ {
+ label = 'xyz',
+ kind = define.CompletionItemKind.Variable,
+ }
+}
diff --git a/test/definition/luadoc.lua b/test/definition/luadoc.lua
index 19b4421b..47859b15 100644
--- a/test/definition/luadoc.lua
+++ b/test/definition/luadoc.lua
@@ -893,3 +893,9 @@ TEST [[
---@type XXX<<?YYY?>>
]]
+
+TEST [[
+local <!x!>
+
+---@cast <?x?> integer
+]]
diff --git a/test/definition/method.lua b/test/definition/method.lua
index aa7aacdc..e4368edb 100644
--- a/test/definition/method.lua
+++ b/test/definition/method.lua
@@ -29,3 +29,29 @@ end
function mt:<!m4!>()
end
]]
+
+TEST [[
+local mt
+
+function mt:f()
+ self.<!x!> = 1
+end
+
+mt.<?x?>
+]]
+
+TEST [[
+function G:f()
+ self.<!x!> = 1
+end
+
+G.<?x?>
+]]
+
+TEST [[
+function G.H:f()
+ self.<!x!> = 1
+end
+
+G.H.<?x?>
+]]
diff --git a/test/diagnostics/common.lua b/test/diagnostics/common.lua
index 56ad0d59..56d1cbcc 100644
--- a/test/diagnostics/common.lua
+++ b/test/diagnostics/common.lua
@@ -163,7 +163,7 @@ print()
]]
TEST [[
-pairs
+print
{}
{}
]]
@@ -218,6 +218,12 @@ x(1, 2, 3, 4, 5)
]]
TEST [[
+---@type fun(a, b, ...)
+local x
+x(1, 2, 3, 4, 5)
+]]
+
+TEST [[
local m = {}
function m:x(a, b)
return a, b
@@ -242,6 +248,66 @@ m:x(1, <!2!>, <!3!>, <!4!>)
]]
TEST [[
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+local function x(a, b)
+ return a, b
+end
+<!x(1)!>
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+local function x(a, b)
+ return a, b
+end
+<!x()!>
+]]
+
+TEST [[
+---@param a integer
+---@param b integer
+---@param ... integer
+local function x(a, b, ...)
+ return a, b, ...
+end
+x(1, 2)
+]]
+
+TEST [[
+---@param a integer
+---@param b? integer
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
+---@param b integer?
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
+---@param b integer|nil
+local function x(a, b)
+ return a, b
+end
+x(1)
+]]
+
+TEST [[
local m = {}
function m.x()
end
@@ -273,6 +339,14 @@ TEST [[
local _ <close> = <!1!>
]]
+TEST [[
+local _ <close> = <!''!>
+]]
+
+TEST [[
+local c <close> = <!(function () return 1 end)()!>
+]]
+
config.get(nil, 'Lua.diagnostics.disable')['unused-local'] = true
TEST [[
local f = <!function () end!>
@@ -286,6 +360,21 @@ TEST [[
local <!function f() end!>
]]
+TEST [[
+local <!function f()
+ f()
+end!>
+]]
+
+
+TEST [[
+local <!function test()
+end!>
+
+local <!function foo ()
+end!>
+]]
+
config.get(nil, 'Lua.diagnostics.disable')['unused-local'] = nil
TEST [[
local mt, x
@@ -407,11 +496,13 @@ _G.bb = 1
TEST [[
local f = load('')
-f(1, 2, 3)
+if f then
+ f(1, 2, 3)
+end
]]
TEST [[
-local _ = <!unpack!>()
+local _ = <!unpack!>
]]
TEST [[
@@ -494,11 +585,10 @@ _ = 1, <!2!>
]]
TEST [[
-local function x()
+function X()
do
local k
print(k)
- x()
end
local k = 1
print(k)
@@ -506,9 +596,8 @@ end
]]
TEST [[
-local function x()
+function X()
local loc
- x()
print(loc)
end
]]
@@ -708,7 +797,9 @@ TEST [[
local function f(x, y)
return x, y
end
-f()
+
+local _
+f(_, _)
]]
TEST [[
@@ -741,7 +832,7 @@ TEST [[
TEST [[
---@type fun(a: integer)
local f
-f()
+f(1)
]]
TEST [[
@@ -792,7 +883,7 @@ local mt2 = {}
---@type Foo
local v
print(v.field1 + 1)
-print(v.<!field2!> + 1)
+print(v.field2 + 1)
print(v.<!field3!> + 1)
print(v:method1())
print(v.method2())
@@ -801,7 +892,7 @@ print(v:<!method3!>())
---@type Bar
local v2
print(v2.field1 + 1)
-print(v2.<!field2!> + 1)
+print(v2.field2 + 1)
print(v2.<!field3!> + 1)
print(v2.field4 + 1)
print(v2:method1())
@@ -868,7 +959,7 @@ TEST [[
local mt
function mt:method1()
mt.<!method2!>() -- doc.class
- self.method1()
+ self:method1()
return self.<!method2!>() -- doc.class.name
end
]]
@@ -978,7 +1069,7 @@ return m
TEST [[
local m = {}
-m.x = io.open()
+m.x = io.open('')
m.x = nil
return m
@@ -1379,13 +1470,13 @@ TEST [[
]]
TEST [[
-return ('1'):gsub()
+return ('1'):upper()
]]
TEST [[
local value
value = '1'
-value = value:gsub()
+value = value:upper()
]]
TEST [[
@@ -1395,3 +1486,67 @@ T.x = 1
print(<!T.x!>)
]]
+
+TEST [[
+T = {}
+
+---@deprecated
+function T:ff()
+end
+
+<!T:ff!>()
+]]
+
+TEST [[
+---@type string?
+local x
+
+S = <!x!>:upper()
+]]
+
+TEST [[
+---@type string?
+local x
+
+if x then
+ S = x:upper()
+end
+]]
+
+TEST [[
+---@type string?
+local x
+
+if not x then
+ x = ''
+end
+
+S = x:upper()
+]]
+
+TEST [[
+---@type fun()?
+local x
+
+S = <!x!>()
+]]
+
+TEST [[
+local x, y
+local z = x and y
+
+print(z.y)
+]]
+
+TEST [[
+local x, y
+function x()
+ y()
+end
+
+function y()
+ x()
+end
+
+x()
+]]
diff --git a/test/full/init.lua b/test/full/init.lua
index e83a7d6d..3b1d2fe2 100644
--- a/test/full/init.lua
+++ b/test/full/init.lua
@@ -27,8 +27,17 @@ require 'full.dirty'
require 'full.projects'
require 'full.self'
+local times = {}
for name, time in util.sortPairs(DIAGTIMES, function (k1, k2)
- return DIAGTIMES[k1] < DIAGTIMES[k2]
+ return DIAGTIMES[k1] > DIAGTIMES[k2]
end) do
- print('诊断任务耗时:', name, time)
+ times[#times+1] = ('诊断任务耗时:%05.3f [%s]'):format(time, name)
+ if #times >= 10 then
+ break
+ end
+end
+
+util.revertTable(times)
+for _, time in ipairs(times) do
+ print(time)
end
diff --git a/test/full/self.lua b/test/full/self.lua
index 93cfe715..cfa6b710 100644
--- a/test/full/self.lua
+++ b/test/full/self.lua
@@ -5,11 +5,14 @@ local diag = require 'provider.diagnostic'
local config = require 'config'
local ws = require 'workspace'
local guide = require 'parser.guide'
+local vm = require 'vm'
+local util = require 'utility'
local path = ROOT / 'script'
local uris = {}
+files.reset()
fsu.scanDirectory(path, function (path)
if path:extension():string() ~= '.lua' then
return
@@ -47,3 +50,39 @@ end
local passed = os.clock() - clock
print('基准全量诊断用时:', passed)
+
+vm.clearNodeCache()
+
+local clock = os.clock()
+local compileDatas = {}
+
+for uri in files.eachFile() do
+ local state = files.getState(uri)
+ local clock = os.clock()
+ guide.eachSource(state.ast, function (src)
+ vm.compileNode(src)
+ end)
+ compileDatas[uri] = {
+ passed = os.clock() - clock,
+ uri = uri,
+ }
+end
+
+local printTexts = {}
+for uri, data in util.sortPairs(compileDatas, function (a, b)
+ return compileDatas[a].passed > compileDatas[b].passed
+end) do
+ printTexts[#printTexts+1] = ('全量编译耗时:%05.3f [%s]'):format(data.passed, uri)
+ if #printTexts >= 10 then
+ break
+ end
+end
+
+util.revertTable(printTexts)
+
+for _, text in ipairs(printTexts) do
+ print(text)
+end
+
+local passed = os.clock() - clock
+print('基准全量编译用时:', passed)
diff --git a/test/hover/init.lua b/test/hover/init.lua
index ee66ef2b..dc725f6c 100644
--- a/test/hover/init.lua
+++ b/test/hover/init.lua
@@ -280,8 +280,8 @@ TEST [[
]]
[=[
function load(chunk: string|function, chunkname?: string, mode?: "b"|"bt"|"t", env?: table)
- -> function
- 2. error_message: string
+ -> function?
+ 2. error_message: string?
]=]
TEST [[
@@ -504,10 +504,10 @@ local <?self?> = setmetatable({
]]
[[
local self: {
- __index: table,
- __name: string = "obj",
id: integer = 1,
remove: function,
+ __index: table,
+ __name: string = "obj",
}
]]
@@ -772,7 +772,7 @@ local <?t?> = {
]]
[[
local t: {
- f: file*,
+ f?: file*,
}
]]
@@ -790,8 +790,6 @@ TEST [[
]]
[[
(global) _G: _G {
- _G: _G,
- _VERSION: string = "Lua 5.4",
arg: string[],
assert: function,
collectgarbage: function,
@@ -810,6 +808,8 @@ TEST [[
module: function,
newproxy: function,
next: function,
+ os: oslib,
+ package: packagelib,
...(+22)
}
]]
@@ -1733,18 +1733,18 @@ t.<?x?>()
(field) t.x: unknown
]]
-TEST [[
----@class A
-local a
-
-local b
-b = a
-
-print(b.<?x?>)
-]]
-[[
-(field) A.x: unknown
-]]
+--TEST [[
+-----@class A
+--local a
+--
+--local b
+--b = a
+--
+--print(b.<?x?>)
+--]]
+--[[
+--(field) A.x: unknown
+--]]
TEST [[
---@return nil
@@ -1851,6 +1851,23 @@ local x: {
]]
TEST [[
+local <?x?> = {
+ _x = '',
+ _y = '',
+ x = '',
+ y = '',
+}
+]]
+[[
+local x: {
+ x: string = "",
+ y: string = "",
+ _x: string = "",
+ _y: string = "",
+}
+]]
+
+TEST [[
---@class A
---@field x string
diff --git a/test/tclient/init.lua b/test/tclient/init.lua
index 7c8d70ef..9e1db8d4 100644
--- a/test/tclient/init.lua
+++ b/test/tclient/init.lua
@@ -4,3 +4,4 @@ require 'tclient.tests.multi-workspace'
require 'tclient.tests.folders-with-single-file'
require 'tclient.tests.load-library'
require 'tclient.tests.files-associations'
+require 'tclient.tests.resolve-completion'
diff --git a/test/tclient/tests/resolve-completion.lua b/test/tclient/tests/resolve-completion.lua
new file mode 100644
index 00000000..a7cf2c2f
--- /dev/null
+++ b/test/tclient/tests/resolve-completion.lua
@@ -0,0 +1,58 @@
+local lclient = require 'lclient'
+local ws = require 'workspace'
+local util = require 'utility'
+
+---@async
+lclient():start(function (client)
+ client:registerFakers()
+ client:initialize()
+
+ client:notify('textDocument/didOpen', {
+ textDocument = {
+ uri = 'file://test.lua',
+ languageId = 'lua',
+ version = 0,
+ text = [[
+---@type integer
+local xxxx
+
+x
+]]
+ }
+ })
+
+ ws.awaitReady()
+
+ local completions = client:awaitRequest('textDocument/completion', {
+ textDocument = { uri = 'file://test.lua' },
+ position = { line = 3, character = 1 },
+ })
+
+ client:awaitRequest('textDocument/didChange',
+ {
+ textDocument = { uri = 'file://test.lua' },
+ contentChanges = {
+ {
+ range = {
+ start = { line = 3, character = 1 },
+ ['end'] = { line = 3, character = 1 },
+ },
+ text = 'x'
+ }
+ }
+ })
+
+ local targetItem
+ for _, item in ipairs(completions.items) do
+ if item.label == 'xxxx' then
+ targetItem = item
+ break
+ end
+ end
+
+ assert(targetItem ~= nil)
+
+ local newItem = client:awaitRequest('completionItem/resolve', targetItem)
+
+ assert(newItem.detail == 'integer')
+end)
diff --git a/test/type_inference/init.lua b/test/type_inference/init.lua
index 9ead2861..b71cf987 100644
--- a/test/type_inference/init.lua
+++ b/test/type_inference/init.lua
@@ -1,8 +1,8 @@
local files = require 'files'
local guide = require 'parser.guide'
-local infer = require 'vm.infer'
local config = require 'config'
local catch = require 'catch'
+local vm = require 'vm'
rawset(_G, 'TEST', true)
@@ -31,9 +31,9 @@ function TEST(wanted)
files.setText('', newScript)
local source = getSource(catched['?'][1][1])
assert(source)
- local result = infer.getInfer(source):view()
+ local result = vm.getInfer(source):view()
if wanted ~= result then
- infer.getInfer(source):view()
+ vm.getInfer(source):view()
end
assert(wanted == result)
files.remove('')
@@ -77,12 +77,6 @@ function f(<?x?>)
end
]]
-TEST 'number' [[
-local <?var?>
-var = 1
-var = 1.0
-]]
-
TEST 'string' [[
local var = '111'
t.<?x?> = var
@@ -95,6 +89,11 @@ var = '111'
TEST 'string' [[
local var
+<?var?> = '111'
+]]
+
+TEST 'string' [[
+local var
var = '111'
print(<?var?>)
]]
@@ -872,6 +871,37 @@ end
]]
TEST 'boolean' [[
+---@generic T: table, V
+---@param t T
+---@return fun(table: V[], i?: integer):integer, V
+---@return T
+---@return integer i
+local function ipairs(t) end
+
+---@type table<integer, boolean>
+local t
+
+for _, <?v?> in ipairs(t) do
+end
+]]
+
+TEST 'boolean' [[
+---@generic T: table, V
+---@param t T
+---@return fun(table: V[], i?: integer):integer, V
+---@return T
+---@return integer i
+local function ipairs(t) end
+
+---@class MyClass
+---@field [integer] boolean
+local t
+
+for _, <?v?> in ipairs(t) do
+end
+]]
+
+TEST 'boolean' [[
---@generic T: table, K, V
---@param t T
---@return fun(table: table<K, V>, index: K):K, V
@@ -1127,6 +1157,29 @@ local t = f('')
print(t.<?x?>)
]]
+TEST 'string' [[
+---@generic T
+---@param t T[]
+---@param callback fun(v: T)
+local function f(t, callback) end
+
+---@type string[]
+local t
+
+f(t, function (<?v?>) end)
+]]
+
+TEST 'unknown' [[
+---@generic T
+---@param t T[]
+---@param callback fun(v: T)
+local function f(t, callback) end
+
+local t = {}
+
+f(t, function (<?v?>) end)
+]]
+
TEST 'table' [[
local <?t?> = setmetatable({}, { __index = function () end })
]]
@@ -1216,14 +1269,14 @@ TEST '👍' [[
local <?x?>
]]
-TEST 'boolean' [[
+TEST 'integer' [[
---@type boolean
local x
<?x?> = 1
]]
-TEST 'Class' [[
+TEST 'integer' [[
---@class Class
local x
@@ -1482,3 +1535,765 @@ function mt:f()
print(<?self?>)
end
]]
+
+TEST 'string?' [[
+---@return string?
+local function f() end
+
+local <?x?> = f()
+]]
+
+TEST 'AA' [[
+---@class AA
+---@overload fun():AA
+local AAA
+
+
+local <?x?> = AAA()
+]]
+
+TEST 'AA' [[
+---@class AA
+---@overload fun():AA
+AAA = {}
+
+
+local <?x?> = AAA()
+]]
+
+TEST 'AA' [[
+---@overload fun():AA
+AAA.BBB = {}
+
+
+local <?x?> = AAA.BBB()
+]]
+
+TEST 'AA' [[
+local AAA
+
+---@overload fun():AA
+AAA.BBB = {}
+
+
+local <?x?> = AAA.BBB()
+]]
+
+TEST 'string|integer' [[
+local <?x?>
+x = '1'
+x = 1
+]]
+
+TEST 'string' [[
+local x
+<?x?> = '1'
+x = 1
+]]
+
+TEST 'integer' [[
+local x
+x = '1'
+<?x?> = 1
+]]
+
+TEST 'unknown' [[
+local x
+print(<?x?>)
+x = '1'
+x = 1
+]]
+
+TEST 'string' [[
+local x
+x = '1'
+print(<?x?>)
+x = 1
+]]
+
+TEST 'integer' [[
+local x
+x = '1'
+x = 1
+print(<?x?>)
+]]
+
+TEST 'unknown' [[
+local x
+
+function A()
+ print(<?x?>)
+end
+]]
+
+TEST 'unknown' [[
+local x
+
+function A()
+ print(<?x?>)
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'string' [[
+local x
+
+x = '1'
+
+function A()
+ print(<?x?>)
+end
+
+x = 1
+]]
+
+TEST 'integer' [[
+local x
+
+x = '1'
+x = 1
+
+function A()
+ print(<?x?>)
+end
+
+]]
+
+TEST 'boolean' [[
+local x
+
+function A()
+ x = true
+ print(<?x?>)
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'unknown' [[
+local x
+
+function A()
+ x = true
+end
+
+print(<?x?>)
+x = '1'
+x = 1
+]]
+
+TEST 'boolean' [[
+local x
+
+function A()
+ x = true
+ function B()
+ print(<?x?>)
+ end
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'table' [[
+local x
+
+function A()
+ x = true
+ function B()
+ x = {}
+ print(<?x?>)
+ end
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'boolean' [[
+local x
+
+function A()
+ x = true
+ function B()
+ x = {}
+ end
+ print(<?x?>)
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'unknown' [[
+local x
+
+function A()
+ x = true
+ function B()
+ x = {}
+ end
+end
+
+function C()
+ print(<?x?>)
+end
+
+x = '1'
+x = 1
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local <?x?>
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if <?x?> then
+ print(x)
+end
+]]
+--[[
+context 0 integer?
+
+save copy 'block'
+save copy 'out'
+push 'block'
+get
+push copy
+truthy
+falsy ref 'out'
+get
+save HEAD 'final'
+push 'out'
+
+push copy HEAD
+merge 'final'
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if x then
+ print(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x then
+ print(x)
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not x then
+ x = 1
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if xxx and x then
+ print(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if xxx and x then
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if xxx and x then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if x ~= nil then
+ print(<?x?>)
+end
+
+print(x)
+]]
+
+TEST 'integer|nil' [[
+---@type integer?
+local x
+
+if x ~= nil then
+ print(x)
+end
+
+print(<?x?>)
+]]
+
+TEST 'nil' [[
+---@type integer?
+local x
+
+if x == nil then
+ print(<?x?>)
+end
+
+print(x)
+]]
+
+TEST 'integer|nil' [[
+---@type integer?
+local x
+
+if x == nil then
+ print(x)
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+<?x?> = x or 1
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+<?x?> = x or y
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not x then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not x then
+ goto ANYWHERE
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [=[
+local x
+
+print(<?x?>--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+print(<?io?>--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+print(io.<?open?>--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+local <?x?> = io['open']--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+local <?x?> = 1 + 1--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+local <?x?> = not 1--[[@as integer]])
+]=]
+
+TEST 'integer' [=[
+local <?x?> = ()--[[@as integer]])
+]=]
+
+TEST 'integer?' [[
+---@param x? integer
+local function f(<?x?>)
+
+end
+]]
+
+TEST 'integer' [[
+local x = 1
+x = <?x?>
+]]
+
+TEST 'integer?' [[
+---@class A
+---@field x? integer
+local t
+
+t.<?x?>
+]]
+
+TEST 'integer?' [[
+---@type { x?: integer }
+local t
+
+t.<?x?>
+]]
+
+TEST 'boolean' [[
+---@class A
+---@field [integer] boolean
+local t
+
+local <?x?> = t[1]
+]]
+
+TEST 'unknown' [[
+local <?x?> = y and z
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+assert(x)
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+assert(x ~= nil)
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer | nil
+local x
+
+assert(x)
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer | nil
+local x
+
+assert(x ~= nil)
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+local x
+
+assert(x == 1)
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if x and <?x?>.y then
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x and x.y then
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x and x.y then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not x or <?x?>.y then
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if not x or x.y then
+ print(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x or x.y then
+ print(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x.y or x then
+ print(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+if x.y or not x then
+ print(<?x?>)
+end
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not x or not y then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+if not y or not x then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type integer?
+local x
+
+while true do
+ if not x then
+ break
+ end
+ print(<?x?>)
+end
+]]
+
+TEST 'integer?' [[
+---@type integer?
+local x
+
+while true do
+ if not x then
+ break
+ end
+end
+
+print(<?x?>)
+]]
+
+TEST 'integer' [[
+---@type fun():integer?
+local iter
+
+for <?x?> in iter do
+end
+]]
+
+TEST 'integer' [[
+local x
+
+---@type integer
+<?x?> = XXX
+]]
+
+TEST 'unknown' [[
+for _ = 1, 999 do
+ local <?x?>
+end
+]]
+
+TEST 'integer' [[
+local x
+
+---@cast x integer
+
+print(<?x?>)
+]]
+
+TEST 'unknown' [[
+local x
+
+---@cast x integer
+
+local x
+print(<?x?>)
+]]
+
+TEST 'unknown' [[
+local x
+
+if true then
+ local x
+ ---@cast x integer
+ print(x)
+end
+
+print(<?x?>)
+]]
+
+TEST 'boolean|integer' [[
+local x = 1
+
+---@cast x +boolean
+
+print(<?x?>)
+]]
+
+TEST 'boolean' [[
+---@type integer|boolean
+local x
+
+---@cast x -integer
+
+print(<?x?>)
+]]
+
+TEST 'boolean?' [[
+---@type boolean
+local x
+
+---@cast x +?
+
+print(<?x?>)
+]]
+
+TEST 'boolean' [[
+---@type boolean?
+local x
+
+---@cast x -?
+
+print(<?x?>)
+]]
+
+TEST 'string' [[
+---@type string?
+local x
+
+if not x then
+ return
+else
+ print(<?x?>)
+end
+
+print(x)
+]]
+
+TEST 'string' [[
+---@type string?
+local x
+
+if not x then
+ return
+else
+ print(x)
+end
+
+print(<?x?>)
+]]
+
+TEST 'true' [[
+---@type boolean | nil
+local x
+
+if not x then
+ return
+end
+
+print(<?x?>)
+]]
+
+TEST 'true' [[
+---@type boolean
+local t
+
+if t then
+ print(<?t?>)
+ return
+end
+
+print(t)
+]]
+
+TEST 'false' [[
+---@type boolean
+local t
+
+if t then
+ print(t)
+ return
+end
+
+print(<?t?>)
+]]