├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ └── bug-report.yml └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── .luarc.json ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── changelog.md ├── debugger.lua ├── defold ├── game.project └── lsp-lua-language-server │ ├── ext.manifest │ ├── lsp.editor_script │ └── plugins │ └── share │ └── config.json ├── doc ├── en-us │ └── config.md ├── pt-br │ └── config.md ├── zh-cn │ └── config.md └── zh-tw │ └── config.md ├── locale ├── en-us │ ├── meta.lua │ ├── script.lua │ └── setting.lua ├── pt-br │ ├── meta.lua │ ├── script.lua │ └── setting.lua ├── zh-cn │ ├── meta.lua │ ├── script.lua │ └── setting.lua └── zh-tw │ ├── meta.lua │ ├── script.lua │ └── setting.lua ├── main.lua ├── make.bat ├── make.lua ├── make.sh ├── make ├── bootstrap.lua ├── code_format.lua ├── detect_platform.lua ├── icon.ico ├── lua-language-server.rc └── modules.cpp ├── meta ├── 3rd │ └── example │ │ ├── config.json │ │ ├── library │ │ └── love.lua │ │ └── plugin.lua ├── spell │ ├── dictionary.txt │ └── lua_dict.txt ├── template │ ├── basic.lua │ ├── bit.lua │ ├── bit32.lua │ ├── builtin.lua │ ├── coroutine.lua │ ├── debug.lua │ ├── ffi.lua │ ├── io.lua │ ├── jit.lua │ ├── jit.profile.lua │ ├── jit.util.lua │ ├── math.lua │ ├── os.lua │ ├── package.lua │ ├── string.buffer.lua │ ├── string.lua │ ├── table.clear.lua │ ├── table.lua │ ├── table.new.lua │ └── utf8.lua └── whimsical │ ├── basic.lua │ ├── builtin.lua │ └── rule.lua ├── new_release_tag.sh ├── script ├── SDBMHash.lua ├── await.lua ├── brave │ ├── brave.lua │ ├── init.lua │ ├── log.lua │ └── work.lua ├── cli │ ├── check.lua │ ├── doc.lua │ ├── doc2md.lua │ ├── init.lua │ ├── version.lua │ └── visualize.lua ├── client.lua ├── config │ ├── config.lua │ ├── init.lua │ ├── loader.lua │ └── template.lua ├── core │ ├── code-action.lua │ ├── code-lens.lua │ ├── color.lua │ ├── command │ │ ├── autoRequire.lua │ │ ├── exportDocument.lua │ │ ├── getConfig.lua │ │ ├── jsonToLua.lua │ │ ├── reloadFFIMeta.lua │ │ ├── removeSpace.lua │ │ ├── setConfig.lua │ │ └── solve.lua │ ├── completion │ │ ├── auto-require.lua │ │ ├── completion.lua │ │ ├── init.lua │ │ ├── keyword.lua │ │ └── postfix.lua │ ├── definition.lua │ ├── diagnostics │ │ ├── ambiguity-1.lua │ │ ├── assign-type-mismatch.lua │ │ ├── await-in-sync.lua │ │ ├── cast-local-type.lua │ │ ├── cast-type-mismatch.lua │ │ ├── circle-doc-class.lua │ │ ├── close-non-object.lua │ │ ├── code-after-break.lua │ │ ├── codestyle-check.lua │ │ ├── count-down-loop.lua │ │ ├── deprecated.lua │ │ ├── different-requires.lua │ │ ├── discard-returns.lua │ │ ├── doc-field-no-class.lua │ │ ├── duplicate-doc-alias.lua │ │ ├── duplicate-doc-field.lua │ │ ├── duplicate-doc-param.lua │ │ ├── duplicate-index.lua │ │ ├── duplicate-set-field.lua │ │ ├── empty-block.lua │ │ ├── global-element.lua │ │ ├── global-in-nil-env.lua │ │ ├── helper │ │ │ └── missing-doc-helper.lua │ │ ├── incomplete-signature-doc.lua │ │ ├── init.lua │ │ ├── inject-field.lua │ │ ├── invisible.lua │ │ ├── lowercase-global.lua │ │ ├── luacheck.lua │ │ ├── missing-fields.lua │ │ ├── missing-global-doc.lua │ │ ├── missing-local-export-doc.lua │ │ ├── missing-parameter.lua │ │ ├── missing-return-value.lua │ │ ├── missing-return.lua │ │ ├── name-style-check.lua │ │ ├── need-check-nil.lua │ │ ├── newfield-call.lua │ │ ├── newline-call.lua │ │ ├── no-unknown.lua │ │ ├── not-yieldable.lua │ │ ├── param-type-mismatch.lua │ │ ├── redefined-local.lua │ │ ├── redundant-parameter.lua │ │ ├── redundant-return-value.lua │ │ ├── redundant-return.lua │ │ ├── redundant-value.lua │ │ ├── return-type-mismatch.lua │ │ ├── spell-check.lua │ │ ├── trailing-space.lua │ │ ├── unbalanced-assignments.lua │ │ ├── undefined-doc-class.lua │ │ ├── undefined-doc-name.lua │ │ ├── undefined-doc-param.lua │ │ ├── undefined-env-child.lua │ │ ├── undefined-field.lua │ │ ├── undefined-global.lua │ │ ├── unknown-cast-variable.lua │ │ ├── unknown-diag-code.lua │ │ ├── unknown-operator.lua │ │ ├── unreachable-code.lua │ │ ├── unused-function.lua │ │ ├── unused-label.lua │ │ ├── unused-local.lua │ │ └── unused-vararg.lua │ ├── document-symbol.lua │ ├── find-source.lua │ ├── folding.lua │ ├── formatting.lua │ ├── highlight.lua │ ├── hint.lua │ ├── hover │ │ ├── args.lua │ │ ├── description.lua │ │ ├── init.lua │ │ ├── label.lua │ │ ├── name.lua │ │ ├── return.lua │ │ └── table.lua │ ├── jump-source.lua │ ├── look-backward.lua │ ├── matchkey.lua │ ├── modifyRequirePath.lua │ ├── rangeformatting.lua │ ├── reference.lua │ ├── rename.lua │ ├── semantic-tokens.lua │ ├── signature.lua │ ├── substring.lua │ ├── type-definition.lua │ ├── type-formatting.lua │ ├── view │ │ ├── psi-select.lua │ │ └── psi-view.lua │ └── workspace-symbol.lua ├── doctor.lua ├── encoder │ ├── ansi.lua │ ├── init.lua │ └── utf16.lua ├── file-uri.lua ├── files.lua ├── filewatch.lua ├── fs-utility.lua ├── gc.lua ├── glob │ ├── gitignore.lua │ ├── glob.lua │ ├── init.lua │ └── matcher.lua ├── global.d.lua ├── inspect.lua ├── json-beautify.lua ├── json-edit.lua ├── json.lua ├── jsonc.lua ├── jsonrpc.lua ├── language.lua ├── lazy-cacher.lua ├── lazytable.lua ├── lclient.lua ├── library.lua ├── linked-table.lua ├── locale-loader.lua ├── log.lua ├── luacheck │ ├── builtin_standards │ │ ├── init.lua │ │ ├── love.lua │ │ └── ngx.lua │ ├── cache.lua │ ├── check.lua │ ├── check_state.lua │ ├── config.lua │ ├── core_utils.lua │ ├── decoder.lua │ ├── expand_rockspec.lua │ ├── filter.lua │ ├── format.lua │ ├── fs.lua │ ├── globbing.lua │ ├── init.lua │ ├── lexer.lua │ ├── main.lua │ ├── multithreading.lua │ ├── options.lua │ ├── parser.lua │ ├── profiler.lua │ ├── runner.lua │ ├── serializer.lua │ ├── stages │ │ ├── detect_bad_whitespace.lua │ │ ├── detect_cyclomatic_complexity.lua │ │ ├── detect_empty_blocks.lua │ │ ├── detect_empty_statements.lua │ │ ├── detect_globals.lua │ │ ├── detect_reversed_fornum_loops.lua │ │ ├── detect_unbalanced_assignments.lua │ │ ├── detect_uninit_accesses.lua │ │ ├── detect_unreachable_code.lua │ │ ├── detect_unused_fields.lua │ │ ├── detect_unused_locals.lua │ │ ├── init.lua │ │ ├── linearize.lua │ │ ├── name_functions.lua │ │ ├── parse.lua │ │ ├── parse_inline_options.lua │ │ ├── resolve_locals.lua │ │ └── unwrap_parens.lua │ ├── standards.lua │ ├── unicode.lua │ ├── unicode_printability_boundaries.lua │ ├── utils.lua │ ├── vendor │ │ └── sha1 │ │ │ ├── LICENSE │ │ │ ├── bit32_ops.lua │ │ │ ├── bit_ops.lua │ │ │ ├── common.lua │ │ │ ├── init.lua │ │ │ ├── lua53_ops.lua │ │ │ └── pure_lua_ops.lua │ └── version.lua ├── meta │ └── bee │ │ ├── filesystem.lua │ │ ├── filewatch.lua │ │ ├── socket.lua │ │ └── thread.lua ├── parser │ ├── compile.lua │ ├── guide.lua │ ├── init.lua │ ├── lines.lua │ ├── luadoc.lua │ ├── relabel.lua │ └── tokens.lua ├── plugin.lua ├── plugins │ ├── astHelper.lua │ ├── ffi │ │ ├── c-parser │ │ │ ├── c99.lua │ │ │ ├── cdefines.lua │ │ │ ├── cdriver.lua │ │ │ ├── cpp.lua │ │ │ ├── ctypes.lua │ │ │ ├── typed.lua │ │ │ └── util.lua │ │ ├── cdefRerence.lua │ │ ├── init.lua │ │ └── searchCode.lua │ ├── init.lua │ └── nodeHelper.lua ├── progress.lua ├── proto │ ├── converter.lua │ ├── define.lua │ ├── diagnostic.lua │ ├── init.lua │ └── proto.lua ├── provider │ ├── build-meta.lua │ ├── capability.lua │ ├── code-lens.lua │ ├── completion.lua │ ├── diagnostic.lua │ ├── formatting.lua │ ├── init.lua │ ├── inlay-hint.lua │ ├── markdown.lua │ ├── name-style.lua │ ├── provider.lua │ ├── semantic-tokens.lua │ └── spell.lua ├── pub │ ├── init.lua │ ├── pub.lua │ └── report.lua ├── service │ ├── init.lua │ ├── net.lua │ └── service.lua ├── string-merger.lua ├── text-merger.lua ├── timer.lua ├── tracy.lua ├── utility.lua ├── version.lua ├── vm │ ├── compiler.lua │ ├── def.lua │ ├── doc.lua │ ├── field.lua │ ├── function.lua │ ├── generic.lua │ ├── global.lua │ ├── infer.lua │ ├── init.lua │ ├── library.lua │ ├── node.lua │ ├── operator.lua │ ├── precompile.lua │ ├── ref.lua │ ├── sign.lua │ ├── tracer.lua │ ├── type.lua │ ├── value.lua │ ├── variable.lua │ ├── visible.lua │ └── vm.lua ├── without-check-nil.lua └── workspace │ ├── init.lua │ ├── loading.lua │ ├── require-path.lua │ ├── scope.lua │ └── workspace.lua ├── test.lua ├── test ├── basic │ ├── filewatch.lua │ ├── init.lua │ └── textmerger.lua ├── catch.lua ├── cli │ ├── test.lua │ └── visualize │ │ ├── test.lua │ │ └── testdata │ │ ├── all-types-expected.txt │ │ ├── all-types.txt │ │ ├── shorten-names-expected.txt │ │ └── shorten-names.txt ├── code_action │ └── init.lua ├── command │ ├── auto-require.lua │ └── init.lua ├── completion │ ├── common.lua │ ├── continue.lua │ └── init.lua ├── crossfile │ ├── completion.lua │ ├── definition.lua │ ├── diagnostic.lua │ ├── hover.lua │ ├── infer.lua │ ├── init.lua │ └── references.lua ├── definition │ ├── arg.lua │ ├── bug.lua │ ├── field.lua │ ├── function.lua │ ├── init.lua │ ├── label.lua │ ├── local.lua │ ├── luadoc.lua │ ├── method.lua │ ├── set.lua │ ├── special.lua │ └── table.lua ├── diagnostics │ ├── ambiguity-1.lua │ ├── assign-type-mismatch.lua │ ├── await-in-sync.lua │ ├── cast-local-type.lua │ ├── cast-type-mismatch.lua │ ├── circle-doc-class.lua │ ├── close-non-object.lua │ ├── code-after-break.lua │ ├── count-down-loop.lua │ ├── deprecated.lua │ ├── discard-returns.lua │ ├── doc-field-no-class.lua │ ├── duplicate-doc-alias.lua │ ├── duplicate-doc-field.lua │ ├── duplicate-doc-param.lua │ ├── duplicate-index.lua │ ├── duplicate-set-field.lua │ ├── empty-block.lua │ ├── global-element.lua │ ├── global-in-nil-env.lua │ ├── incomplete-signature-doc.lua │ ├── init.lua │ ├── inject-field.lua │ ├── invisible.lua │ ├── lowercase-global.lua │ ├── missing-fields.lua │ ├── missing-global-doc.lua │ ├── missing-local-export-doc.lua │ ├── missing-parameter.lua │ ├── missing-return-value.lua │ ├── missing-return.lua │ ├── need-check-nil.lua │ ├── newfield-call.lua │ ├── newline-call.lua │ ├── not-yieldable.lua │ ├── param-type-mismatch.lua │ ├── redefined-local.lua │ ├── redundant-parameter.lua │ ├── redundant-return-value.lua │ ├── redundant-return.lua │ ├── redundant-value.lua │ ├── return-type-mismatch.lua │ ├── trailing-space.lua │ ├── unbalanced-assignments.lua │ ├── undefined-doc-class.lua │ ├── undefined-doc-name.lua │ ├── undefined-doc-param.lua │ ├── undefined-env-child.lua │ ├── undefined-field.lua │ ├── undefined-global.lua │ ├── unknown-cast-variable.lua │ ├── unknown-diag-code.lua │ ├── unknown-operator.lua │ ├── unreachable-code.lua │ ├── unused-function.lua │ ├── unused-label.lua │ ├── unused-local.lua │ └── unused-vararg.lua ├── document_symbol │ └── init.lua ├── example │ ├── guide.txt │ ├── jass-common.txt │ ├── largeGlobal.txt │ ├── meta.json │ └── vm.txt ├── full │ ├── dirty.lua │ ├── example.lua │ ├── init.lua │ ├── normal.lua │ ├── projects.lua │ └── self.lua ├── highlight │ └── init.lua ├── hover │ └── init.lua ├── other │ ├── filewatch.lua │ └── init.lua ├── plugins │ ├── ast │ │ ├── helper.lua │ │ └── test.lua │ ├── ffi │ │ ├── builder.lua │ │ ├── cdef.lua │ │ ├── parser.lua │ │ └── test.lua │ ├── node │ │ └── test.lua │ └── test.lua ├── references │ ├── all.lua │ ├── common.lua │ └── init.lua ├── rename │ └── init.lua ├── signature │ └── init.lua ├── tclient │ ├── init.lua │ └── tests │ │ ├── build-meta.lua │ │ ├── change-workspace-folder.lua │ │ ├── files-associations.lua │ │ ├── folders-with-single-file.lua │ │ ├── hover-pairs.lua │ │ ├── hover-set-local.lua │ │ ├── jump-source.lua │ │ ├── library-ignore-limit.lua │ │ ├── load-library.lua │ │ ├── load-relative-library.lua │ │ ├── modify-luarc.lua │ │ ├── multi-workspace.lua │ │ ├── performance-jass-common.lua │ │ ├── recursive-runner.lua │ │ ├── resolve-completion.lua │ │ ├── same-prefix.lua │ │ └── single-mode.lua ├── type_formatting │ └── init.lua └── type_inference │ └── init.lua ├── theme-tokens.md └── tools ├── build-3rd-meta.lua ├── build-doc.lua ├── configuration.lua ├── locale.lua ├── love-api.lua ├── lovr-api.lua ├── lua51.lua └── normalize-3rd-path.lua /.gitignore: -------------------------------------------------------------------------------- 1 | /log 2 | /build 3 | /test/temp.lua 4 | /meta/* 5 | !/meta/template 6 | !/meta/3rd 7 | !/meta/whimsical 8 | /bin* 9 | -------------------------------------------------------------------------------- /.luarc.json: -------------------------------------------------------------------------------- 1 | { 2 | "diagnostics": { 3 | "disable": [ 4 | "close-non-object", 5 | "incomplete-signature-doc", 6 | "missing-global-doc", 7 | "missing-local-export-doc" 8 | ], 9 | "groupFileStatus": { 10 | "ambiguity": "Any", 11 | "await": "Any", 12 | "duplicate": "Any", 13 | "global": "Any", 14 | "luadoc": "Any", 15 | "redefined": "Any", 16 | "strict": "Any", 17 | "type-check": "Any", 18 | "unbalanced": "Any", 19 | "unused": "Any" 20 | }, 21 | "ignoredFiles": "Opened", 22 | "libraryFiles": "Opened" 23 | }, 24 | "runtime": { 25 | "version": "Lua 5.4", 26 | "path": [ 27 | "script/?.lua", 28 | "script/?/init.lua", 29 | "test/?.lua", 30 | "test/?/init.lua", 31 | "script/meta/?.lua" 32 | ], 33 | "pathStrict": true 34 | }, 35 | "workspace": { 36 | "maxPreload": 1600, 37 | "preloadFileSize": 1000, 38 | "ignoreDir": [ 39 | "/locale/", 40 | "/libs/", 41 | "/3rd", 42 | "/.vscode", 43 | "/meta" 44 | ], 45 | "checkThirdParty": false 46 | }, 47 | "typeFormat": { 48 | "config": { 49 | "format_line": "false" 50 | } 51 | }, 52 | "type": { 53 | "castNumberToInteger": false 54 | }, 55 | "doc": { 56 | "privateName": [ 57 | "_*" 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "actboy168.extension-path", 4 | ] 5 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Just some comment 2 | { 3 | "Lua.misc.parameters": [ 4 | //"--preview", 5 | "--develop=true", 6 | "--dbgport=11413", 7 | "--loglevel=trace", 8 | "--shownode", 9 | //"--lazy", 10 | ], 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "type": "shell", 4 | "windows": { 5 | "options": { 6 | "shell": { 7 | "executable": "C:\\Windows\\System32\\cmd.exe", 8 | "args": [ 9 | "/c" 10 | ] 11 | } 12 | } 13 | }, 14 | "options": { 15 | "cwd": "${workspaceFolder}" 16 | }, 17 | "tasks": [ 18 | { 19 | "type": "shell", 20 | "label": "PreCompile", 21 | "windows": { 22 | "command": "chcp 65001 && compile\\install.bat" 23 | }, 24 | "linux": { 25 | "command": "compile/install.sh" 26 | }, 27 | "osx": { 28 | "command": "compile/install.sh" 29 | }, 30 | "options": { 31 | "cwd": "${workspaceFolder}/3rd/luamake" 32 | }, 33 | }, 34 | { 35 | "type": "shell", 36 | "label": "Compile", 37 | "windows": { 38 | "command": "chcp 65001 && ${workspaceFolder}/3rd/luamake/luamake rebuild" 39 | }, 40 | "linux": { 41 | "command": "${workspaceFolder}/3rd/luamake/luamake rebuild" 42 | }, 43 | "osx": { 44 | "command": "${workspaceFolder}/3rd/luamake/luamake rebuild" 45 | } 46 | }, 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 最萌小汐 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /debugger.lua: -------------------------------------------------------------------------------- 1 | if not DEVELOP then 2 | return 3 | end 4 | 5 | local fs = require 'bee.filesystem' 6 | local luaDebugs = {} 7 | 8 | local home = os.getenv 'USERPROFILE' or os.getenv 'HOME' 9 | if not home then 10 | log.error('Cannot find home directory') 11 | return 12 | end 13 | for _, vscodePath in ipairs { '.vscode', '.vscode-insiders', '.vscode-server-insiders' } do 14 | local extensionPath = fs.path(home) / vscodePath / 'extensions' 15 | log.debug('Search extensions at:', extensionPath:string()) 16 | 17 | if fs.exists(extensionPath) then 18 | for path in fs.pairs(extensionPath) do 19 | if fs.is_directory(path) then 20 | local name = path:filename():string() 21 | if name:find('actboy168.lua-debug-', 1, true) then 22 | luaDebugs[#luaDebugs+1] = path:string() 23 | end 24 | end 25 | end 26 | end 27 | end 28 | 29 | if #luaDebugs == 0 then 30 | log.debug('Cant find "actboy168.lua-debug"') 31 | return 32 | end 33 | 34 | local function getVer(filename) 35 | local a, b, c = filename:match('actboy168%.lua%-debug%-(%d+)%.(%d+)%.(%d+)') 36 | if not a then 37 | return 0 38 | end 39 | return a * 1000000 + b * 1000 + c 40 | end 41 | 42 | table.sort(luaDebugs, function (a, b) 43 | return getVer(a) > getVer(b) 44 | end) 45 | 46 | local debugPath = luaDebugs[1] 47 | local cpath = "/runtime/win64/lua54/?.dll;/runtime/win64/lua54/?.so" 48 | local path = "/script/?.lua" 49 | 50 | local function tryDebugger() 51 | local entry = assert(package.searchpath('debugger', debugPath .. path)) 52 | local root = debugPath 53 | local addr = ("127.0.0.1:%d"):format(DBGPORT) 54 | local dbg = loadfile(entry)(entry) 55 | dbg:start { 56 | address = addr, 57 | } 58 | log.debug('Debugger startup, listen port:', DBGPORT) 59 | log.debug('Debugger args:', addr, root, path, cpath) 60 | if DBGWAIT then 61 | dbg:event('wait') 62 | end 63 | return dbg 64 | end 65 | 66 | xpcall(tryDebugger, log.debug) 67 | -------------------------------------------------------------------------------- /defold/game.project: -------------------------------------------------------------------------------- 1 | [library] 2 | include_dirs = lsp-lua-language-server 3 | 4 | -------------------------------------------------------------------------------- /defold/lsp-lua-language-server/ext.manifest: -------------------------------------------------------------------------------- 1 | name: "lsp-lua-language-server" -------------------------------------------------------------------------------- /defold/lsp-lua-language-server/lsp.editor_script: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | function M.get_language_servers() 4 | local cmd = 'build/plugins/lsp-lua-language-server/plugins/bin/' .. editor.platform .. '/bin/lua-language-server' 5 | if editor.platform == 'x86_64-win32' then 6 | cmd = cmd .. '.exe' 7 | end 8 | local config_arg = '--configpath=build/plugins/lsp-lua-language-server/plugins/share/config.json' 9 | return { 10 | { 11 | languages = {'lua'}, 12 | watched_files = { 13 | { pattern = '**/.luacheckrc' } 14 | }, 15 | command = { cmd, config_arg } 16 | } 17 | } 18 | end 19 | 20 | return M 21 | -------------------------------------------------------------------------------- /defold/lsp-lua-language-server/plugins/share/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/sumneko/vscode-lua/master/setting/schema.json", 3 | "diagnostics": { 4 | "workspaceEvent": "None", 5 | "enable": true 6 | }, 7 | "Lua": { 8 | "runtime": { 9 | "version": "Lua 5.1" 10 | } 11 | }, 12 | "window": { 13 | "progressBar": false, 14 | "statusBar": false 15 | } 16 | } -------------------------------------------------------------------------------- /make.bat: -------------------------------------------------------------------------------- 1 | git submodule update --init --recursive 2 | cd 3rd\luamake 3 | call compile\install.bat 4 | cd ..\.. 5 | call 3rd\luamake\luamake.exe rebuild 6 | -------------------------------------------------------------------------------- /make.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | git submodule update --init --recursive 4 | pushd 3rd/luamake 5 | ./compile/build.sh 6 | popd 7 | ./3rd/luamake/luamake rebuild 8 | -------------------------------------------------------------------------------- /make/code_format.lua: -------------------------------------------------------------------------------- 1 | local lm = require 'luamake' 2 | 3 | lm.c = lm.compiler == 'msvc' and 'c89' or 'c11' 4 | lm.cxx = 'c++17' 5 | 6 | lm:source_set 'code_format' { 7 | rootdir = '../3rd/EmmyLuaCodeStyle', 8 | includes = { 9 | "Util/include", 10 | "CodeFormatCore/include", 11 | "LuaParser/include", 12 | "../bee.lua/3rd/lua", 13 | "3rd/wildcards/include" 14 | }, 15 | sources = { 16 | -- codeFormatLib 17 | "CodeFormatLib/src/*.cpp", 18 | -- LuaParser 19 | "LuaParser/src/**/*.cpp", 20 | -- Util 21 | "Util/src/StringUtil.cpp", 22 | "Util/src/Utf8.cpp", 23 | "Util/src/SymSpell/*.cpp", 24 | "Util/src/InfoTree/*.cpp", 25 | --CodeService 26 | "CodeFormatCore/src/**/*.cpp", 27 | }, 28 | windows = { 29 | flags = "/utf-8", 30 | }, 31 | macos = { 32 | flags = "-Wall -Werror", 33 | }, 34 | linux = { 35 | flags = "-Wall -Werror" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /make/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/defold/lua-language-server/076fc38808e35ade13cd44564cb9dd1ddee50078/make/icon.ico -------------------------------------------------------------------------------- /make/lua-language-server.rc: -------------------------------------------------------------------------------- 1 | #include "Windows.h" 2 | 10086 ICON "make/icon.ico" 3 | -------------------------------------------------------------------------------- /make/modules.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern "C" int luaopen_lpeglabel (lua_State *L); 4 | static ::bee::lua::callfunc _init(::bee::lua::register_module, "lpeglabel", luaopen_lpeglabel); 5 | 6 | extern "C" int luaopen_lfs (lua_State *L); 7 | static ::bee::lua::callfunc _init_lfs(::bee::lua::register_module, "lfs", luaopen_lfs); 8 | 9 | #ifdef CODE_FORMAT 10 | extern "C" int luaopen_code_format(lua_State *L); 11 | static ::bee::lua::callfunc _init_code_format(::bee::lua::register_module, "code_format", 12 | luaopen_code_format); 13 | #endif 14 | -------------------------------------------------------------------------------- /meta/3rd/example/config.json: -------------------------------------------------------------------------------- 1 | { 2 | // if not set, the folder name will be used 3 | "name" : "Example", 4 | // list of matched words 5 | "words" : ["thisIsAnExampleWord%.ifItExistsInFile%.thenTryLoadThisLibrary"], 6 | // list or matched file names. `.lua`, `.dll` and `.so` only 7 | "files" : ["thisIsAnExampleFile%.ifItExistsInWorkSpace%.thenTryLoadThisLibrary%.lua"], 8 | // list of settings to be changed 9 | "settings" : { 10 | "Lua.runtime.version" : "LuaJIT", 11 | "Lua.diagnostics.globals" : [ 12 | "global1", 13 | "global2" 14 | ], 15 | "Lua.runtime.special" : { 16 | "include" : "require" 17 | }, 18 | "Lua.runtime.builtin" : { 19 | "io" : "disable" 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /meta/3rd/example/library/love.lua: -------------------------------------------------------------------------------- 1 | ---@meta 2 | 3 | local m = {} 4 | 5 | function m.thisIsAnExampleLibrary() 6 | end 7 | 8 | return m 9 | -------------------------------------------------------------------------------- /meta/3rd/example/plugin.lua: -------------------------------------------------------------------------------- 1 | -- if this file exists, then change setting `Lua.runtime.plugin` 2 | -- see https://luals.github.io/wiki/plugins 3 | 4 | function OnSetText(uri, text) 5 | local diffs = {} 6 | 7 | for start, finish in text:gmatch '()pairs()' do 8 | diffs[#diffs+1] = { 9 | start = start, 10 | finish = finish - 1, 11 | text = 'safepairs' 12 | } 13 | end 14 | 15 | return diffs 16 | end 17 | -------------------------------------------------------------------------------- /meta/template/bit.lua: -------------------------------------------------------------------------------- 1 | ---#if not JIT then DISABLE() end 2 | ---@meta bit 3 | 4 | ---@version JIT 5 | ---@class bitlib 6 | bit = {} 7 | 8 | ---@param x integer 9 | ---@return integer y 10 | ---@nodiscard 11 | function bit.tobit(x) end 12 | 13 | ---@param x integer 14 | ---@param n? integer 15 | ---@return string y 16 | ---@nodiscard 17 | function bit.tohex(x, n) end 18 | 19 | ---@param x integer 20 | ---@return integer y 21 | ---@nodiscard 22 | function bit.bnot(x) end 23 | 24 | ---@param x integer 25 | ---@param x2 integer 26 | ---@param ... integer 27 | ---@return integer y 28 | ---@nodiscard 29 | function bit.bor(x, x2, ...) end 30 | 31 | ---@param x integer 32 | ---@param x2 integer 33 | ---@param ... integer 34 | ---@return integer y 35 | ---@nodiscard 36 | function bit.band(x, x2, ...) end 37 | 38 | ---@param x integer 39 | ---@param x2 integer 40 | ---@param ... integer 41 | ---@return integer y 42 | ---@nodiscard 43 | function bit.bxor(x, x2, ...) end 44 | 45 | ---@param x integer 46 | ---@param n integer 47 | ---@return integer y 48 | ---@nodiscard 49 | function bit.lshift(x, n) end 50 | 51 | ---@param x integer 52 | ---@param n integer 53 | ---@return integer y 54 | ---@nodiscard 55 | function bit.rshift(x, n) end 56 | 57 | ---@param x integer 58 | ---@param n integer 59 | ---@return integer y 60 | ---@nodiscard 61 | function bit.arshift(x, n) end 62 | 63 | ---@param x integer 64 | ---@param n integer 65 | ---@return integer y 66 | ---@nodiscard 67 | function bit.rol(x, n) end 68 | 69 | ---@param x integer 70 | ---@param n integer 71 | ---@return integer y 72 | ---@nodiscard 73 | function bit.ror(x, n) end 74 | 75 | ---@param x integer 76 | ---@return integer y 77 | ---@nodiscard 78 | function bit.bswap(x) end 79 | 80 | return bit 81 | -------------------------------------------------------------------------------- /meta/template/bit32.lua: -------------------------------------------------------------------------------- 1 | ---#if VERSION ~= 5.2 then DISABLE() end 2 | ---@meta bit32 3 | 4 | ---@version 5.2 5 | ---#DES 'bit32' 6 | ---@class bit32lib 7 | bit32 = {} 8 | 9 | ---#DES 'bit32.arshift' 10 | ---@param x integer 11 | ---@param disp integer 12 | ---@return integer 13 | ---@nodiscard 14 | function bit32.arshift(x, disp) end 15 | 16 | ---#DES 'bit32.band' 17 | ---@return integer 18 | ---@nodiscard 19 | function bit32.band(...) end 20 | 21 | ---#DES 'bit32.bnot' 22 | ---@param x integer 23 | ---@return integer 24 | ---@nodiscard 25 | function bit32.bnot(x) end 26 | 27 | ---#DES 'bit32.bor' 28 | ---@return integer 29 | ---@nodiscard 30 | function bit32.bor(...) end 31 | 32 | ---#DES 'bit32.btest' 33 | ---@return boolean 34 | ---@nodiscard 35 | function bit32.btest(...) end 36 | 37 | ---#DES 'bit32.bxor' 38 | ---@return integer 39 | ---@nodiscard 40 | function bit32.bxor(...) end 41 | 42 | ---#DES 'bit32.extract' 43 | ---@param n integer 44 | ---@param field integer 45 | ---@param width? integer 46 | ---@return integer 47 | ---@nodiscard 48 | function bit32.extract(n, field, width) end 49 | 50 | ---#DES 'bit32.replace' 51 | ---@param n integer 52 | ---@param v integer 53 | ---@param field integer 54 | ---@param width? integer 55 | ---@nodiscard 56 | function bit32.replace(n, v, field, width) end 57 | 58 | ---#DES 'bit32.lrotate' 59 | ---@param x integer 60 | ---@param distp integer 61 | ---@return integer 62 | ---@nodiscard 63 | function bit32.lrotate(x, distp) end 64 | 65 | ---#DES 'bit32.lshift' 66 | ---@param x integer 67 | ---@param distp integer 68 | ---@return integer 69 | ---@nodiscard 70 | function bit32.lshift(x, distp) end 71 | 72 | ---#DES 'bit32.rrotate' 73 | ---@param x integer 74 | ---@param distp integer 75 | ---@return integer 76 | ---@nodiscard 77 | function bit32.rrotate(x, distp) end 78 | 79 | ---#DES 'bit32.rshift' 80 | ---@param x integer 81 | ---@param distp integer 82 | ---@return integer 83 | ---@nodiscard 84 | function bit32.rshift(x, distp) end 85 | 86 | return bit32 87 | -------------------------------------------------------------------------------- /meta/template/builtin.lua: -------------------------------------------------------------------------------- 1 | ---@meta _ 2 | 3 | ---@class unknown 4 | ---@class any 5 | ---@class nil 6 | ---@class boolean 7 | ---@class true: boolean 8 | ---@class false: boolean 9 | ---@class number 10 | ---@class integer: number 11 | ---@class thread 12 | ---@class table: { [K]: V } 13 | ---@class string: stringlib 14 | ---@class userdata 15 | ---@class lightuserdata 16 | ---@class function 17 | -------------------------------------------------------------------------------- /meta/template/coroutine.lua: -------------------------------------------------------------------------------- 1 | ---@meta coroutine 2 | 3 | ---#DES 'coroutine' 4 | ---@class coroutinelib 5 | coroutine = {} 6 | 7 | ---#DES 'coroutine.create' 8 | ---@param f async fun(...):... 9 | ---@return thread 10 | ---@nodiscard 11 | function coroutine.create(f) end 12 | 13 | ---#if VERSION >= 5.4 then 14 | ---#DES 'coroutine.isyieldable>5.4' 15 | ---@param co? thread 16 | ---@return boolean 17 | ---@nodiscard 18 | function coroutine.isyieldable(co) end 19 | ---#else 20 | ---@version >5.2 21 | ---#DES 'coroutine.isyieldable' 22 | ---@return boolean 23 | ---@nodiscard 24 | function coroutine.isyieldable() end 25 | ---#end 26 | 27 | ---@version >5.4 28 | ---#DES 'coroutine.close' 29 | ---@param co thread 30 | ---@return boolean noerror 31 | ---@return any errorobject 32 | function coroutine.close(co) end 33 | 34 | ---#DES 'coroutine.resume' 35 | ---@param co thread 36 | ---@param val1? any 37 | ---@return boolean success 38 | ---@return any ... 39 | function coroutine.resume(co, val1, ...) end 40 | 41 | ---#DES 'coroutine.running' 42 | ---@return thread running 43 | ---@return boolean ismain 44 | ---@nodiscard 45 | function coroutine.running() end 46 | 47 | ---#DES 'coroutine.status' 48 | ---@param co thread 49 | ---@return 50 | ---| '"running"' # ---#DESTAIL 'costatus.running' 51 | ---| '"suspended"' # ---#DESTAIL 'costatus.suspended' 52 | ---| '"normal"' # ---#DESTAIL 'costatus.normal' 53 | ---| '"dead"' # ---#DESTAIL 'costatus.dead' 54 | ---@nodiscard 55 | function coroutine.status(co) end 56 | 57 | ---#DES 'coroutine.wrap' 58 | ---@param f async fun(...):... 59 | ---@return fun(...):... 60 | ---@nodiscard 61 | function coroutine.wrap(f) end 62 | 63 | ---#DES 'coroutine.yield' 64 | ---@async 65 | ---@return any ... 66 | function coroutine.yield(...) end 67 | 68 | return coroutine 69 | -------------------------------------------------------------------------------- /meta/template/jit.lua: -------------------------------------------------------------------------------- 1 | ---#if not JIT then DISABLE() end 2 | ---@meta jit 3 | 4 | ---@version JIT 5 | ---@class jitlib 6 | ---@field version string 7 | ---@field version_num number 8 | ---@field os 'Windows'|'Linux'|'OSX'|'BSD'|'POSIX'|'Other' 9 | ---@field arch 'x86'|'x64'|'arm'|'arm64'|'arm64be'|'ppc'|'ppc64'|'ppc64le'|'mips'|'mipsel'|'mips64'|'mips64el'|string 10 | jit = {} 11 | 12 | ---@overload fun(...):... 13 | ---@param func function|boolean 14 | ---@param recursive? boolean 15 | function jit.on(func, recursive) 16 | end 17 | 18 | ---@overload fun(...):... 19 | ---@param func function|boolean 20 | ---@param recursive? boolean 21 | function jit.off(func, recursive) 22 | end 23 | 24 | ---@overload fun(...):... 25 | ---@overload fun(tr: number) 26 | ---@param func function|boolean 27 | ---@param recursive? boolean 28 | function jit.flush(func, recursive) 29 | end 30 | 31 | ---@return boolean status 32 | ---@return string ... 33 | ---@nodiscard 34 | function jit.status() 35 | end 36 | 37 | jit.opt = {} 38 | 39 | ---@param ... any flags 40 | function jit.opt.start(...) 41 | end 42 | 43 | return jit 44 | -------------------------------------------------------------------------------- /meta/template/jit.profile.lua: -------------------------------------------------------------------------------- 1 | ---#if not JIT then DISABLE() end 2 | ---@meta jit.profile 3 | 4 | local profile = {} 5 | 6 | ---@param mode string 7 | ---@param func fun(L: thread, samples: integer, vmst: string) 8 | function profile.start(mode, func) 9 | end 10 | 11 | function profile.stop() 12 | end 13 | 14 | ---@overload fun(th: thread, fmt: string, depth: integer) 15 | ---@param fmt string 16 | ---@param depth integer 17 | function profile.dumpstack(fmt, depth) 18 | end 19 | 20 | return profile -------------------------------------------------------------------------------- /meta/template/package.lua: -------------------------------------------------------------------------------- 1 | ---@meta package 2 | 3 | ---#if VERSION >=5.4 then 4 | ---#DES 'require>5.4' 5 | ---@param modname string 6 | ---@return unknown 7 | ---@return unknown loaderdata 8 | function require(modname) end 9 | ---#else 10 | ---#DES 'require<5.3' 11 | ---@param modname string 12 | ---@return unknown 13 | function require(modname) end 14 | ---#end 15 | 16 | ---#DES 'package' 17 | ---@class packagelib 18 | ---#DES 'package.cpath' 19 | ---@field cpath string 20 | ---#DES 'package.loaded' 21 | ---@field loaded table 22 | ---#DES 'package.path' 23 | ---@field path string 24 | ---#DES 'package.preload' 25 | ---@field preload table 26 | package = {} 27 | 28 | ---#DES 'package.config' 29 | package.config = [[ 30 | / 31 | ; 32 | ? 33 | ! 34 | -]] 35 | 36 | ---@version <5.1 37 | ---#DES 'package.loaders' 38 | package.loaders = {} 39 | 40 | ---#DES 'package.loadlib' 41 | ---@param libname string 42 | ---@param funcname string 43 | ---@return any 44 | function package.loadlib(libname, funcname) end 45 | 46 | ---#DES 'package.searchers' 47 | ---@version >5.2 48 | package.searchers = {} 49 | 50 | ---#DES 'package.searchpath' 51 | ---@version >5.2,JIT 52 | ---@param name string 53 | ---@param path string 54 | ---@param sep? string 55 | ---@param rep? string 56 | ---@return string? filename 57 | ---@return string? errmsg 58 | ---@nodiscard 59 | function package.searchpath(name, path, sep, rep) end 60 | 61 | ---#DES 'package.seeall' 62 | ---@version <5.1 63 | ---@param module table 64 | function package.seeall(module) end 65 | 66 | return package 67 | -------------------------------------------------------------------------------- /meta/template/table.clear.lua: -------------------------------------------------------------------------------- 1 | ---#if not JIT then DISABLE() end 2 | ---@meta table.clear 3 | 4 | ---@version JIT 5 | ---#DES 'table.clear' 6 | ---@param tab table 7 | local function clear(tab) end 8 | 9 | return clear 10 | -------------------------------------------------------------------------------- /meta/template/table.new.lua: -------------------------------------------------------------------------------- 1 | ---#if not JIT then DISABLE() end 2 | ---@meta table.new 3 | 4 | ---@version JIT 5 | ---#DES 'table.new' 6 | ---@param narray integer 7 | ---@param nhash integer 8 | ---@return table 9 | local function new(narray, nhash) end 10 | 11 | return new 12 | -------------------------------------------------------------------------------- /meta/template/utf8.lua: -------------------------------------------------------------------------------- 1 | ---#if VERSION <= 5.2 then DISABLE() end 2 | ---@meta utf8 3 | 4 | ---@version >5.3 5 | ---#DES 'utf8' 6 | ---@class utf8lib 7 | ---#DES 'utf8.charpattern' 8 | ---@field charpattern string 9 | utf8 = {} 10 | 11 | ---#DES 'utf8.char' 12 | ---@param code integer 13 | ---@param ... integer 14 | ---@return string 15 | ---@nodiscard 16 | function utf8.char(code, ...) end 17 | 18 | ---#DES 'utf8.codes' 19 | ---#if VERSION <= 5.3 then 20 | ---@param s string 21 | ---@return fun(s: string, p: integer):integer, integer 22 | function utf8.codes(s) end 23 | ---#else 24 | ---@param s string 25 | ---@param lax? boolean 26 | ---@return fun(s: string, p: integer):integer, integer 27 | function utf8.codes(s, lax) end 28 | ---#end 29 | 30 | ---#DES 'utf8.codepoint' 31 | ---#if VERSION <= 5.3 then 32 | ---@param s string 33 | ---@param i? integer 34 | ---@param j? integer 35 | ---@return integer code 36 | ---@return integer ... 37 | ---@nodiscard 38 | function utf8.codepoint(s, i, j) end 39 | ---#else 40 | ---@param s string 41 | ---@param i? integer 42 | ---@param j? integer 43 | ---@param lax? boolean 44 | ---@return integer code 45 | ---@return integer ... 46 | ---@nodiscard 47 | function utf8.codepoint(s, i, j, lax) end 48 | ---#end 49 | 50 | ---#DES 'utf8.len' 51 | ---#if VERSION <= 5.3 then 52 | ---@param s string 53 | ---@param i? integer 54 | ---@param j? integer 55 | ---@return integer? 56 | ---@return integer? errpos 57 | ---@nodiscard 58 | function utf8.len(s, i, j) end 59 | ---#else 60 | ---@param s string 61 | ---@param i? integer 62 | ---@param j? integer 63 | ---@param lax? boolean 64 | ---@return integer? 65 | ---@return integer? errpos 66 | ---@nodiscard 67 | function utf8.len(s, i, j, lax) end 68 | ---#end 69 | 70 | ---#DES 'utf8.offset' 71 | ---@param s string 72 | ---@param n integer 73 | ---@param i? integer 74 | ---@return integer p 75 | ---@nodiscard 76 | function utf8.offset(s, n, i) end 77 | 78 | return utf8 79 | -------------------------------------------------------------------------------- /meta/whimsical/basic.lua: -------------------------------------------------------------------------------- 1 | ---@meta _ 2 | 3 | ---#DES 'arg' 4 | ---@type string[] 5 | arg = {} 6 | 7 | ---#DES 'assert' 8 | ---@generic T 9 | ---@param v? T 10 | ---@param message? any 11 | ---@param ... any 12 | ---@return T 13 | ---@return any ... => args[reti + 1] 14 | ---@throw => args[1].isFalsy 15 | ---@narrow v => args[1].truly 16 | function assert(v, message, ...) end 17 | 18 | --[[@@@ 19 | ---@overload fun(opt: 'collect') # ---#DESTAIL 'cgopt.collect' 20 | ---@overload fun(opt: 'stop') # ---#DESTAIL 'cgopt.stop' 21 | ---@overload fun(opt: 'restart') # ---#DESTAIL 'cgopt.restart' 22 | ---@overload fun(opt: 'count'): integer # ---#DESTAIL 'cgopt.count' 23 | ---@overload fun(opt: 'step'): boolean # ---#DESTAIL 'cgopt.step' 24 | ---@overload fun(opt: 'isrunning'): boolean # ---#DESTAIL 'cgopt.isrunning' 25 | ---#if VERSION >= 5.4 then 26 | ---@overload fun(opt: 'incremental' 27 | , pause?: integer 28 | , stepmul?: integer 29 | , stepsize?: integer) # ---#DESTAIL 'cgopt.incremental' 30 | ---@overload fun(opt: 'generational' 31 | , minor?: integer 32 | , major?: integer) # ---#DESTAIL 'cgopt.generational' 33 | ---#end 34 | ---@overload fun(opt: 'setpause', arg: integer) # ---#DESTAIL 'cgopt.setpause' 35 | ---@overload fun(opt: 'setstepmul', arg: integer) # ---#DESTAIL 'cgopt.setstepmul' 36 | ---@prototype 37 | ]] 38 | function collectgarbage(...) end 39 | 40 | ---#DES 'dofile' 41 | ---@param filename? string 42 | ---@return any 43 | ---@custom dofile 44 | function dofile(filename) end 45 | 46 | ---#DES 'error' 47 | ---@param message any 48 | ---@param level? integer 49 | ---@throw 50 | function error(message, level) end 51 | -------------------------------------------------------------------------------- /meta/whimsical/builtin.lua: -------------------------------------------------------------------------------- 1 | ---@meta _ 2 | 3 | ---@class nil 4 | ---@class never 5 | ---@class true 6 | ---@class false 7 | ---@class any: { [unknown]: any } 8 | ---@class truly: { [unknown]: any } 9 | ---@class unknown: truly | false 10 | ---@class boolean 11 | ---@class number 12 | ---@class thread 13 | ---@class table: { [unknown]: any } 14 | ---@class table: { [K]: V } 15 | ---@class string: stringlib 16 | ---@class userdata: { [unknown]: any } 17 | ---@class lightuserdata 18 | ---@class function: fun(...): ... 19 | -------------------------------------------------------------------------------- /new_release_tag.sh: -------------------------------------------------------------------------------- 1 | #/usr/bin/env bash 2 | set -euox pipefail 3 | VERSION="v1.$(git rev-list --count HEAD)" 4 | git tag "$VERSION" 5 | git push origin "$VERSION" -------------------------------------------------------------------------------- /script/SDBMHash.lua: -------------------------------------------------------------------------------- 1 | local byte = string.byte 2 | local max = 0x7fffffff 3 | 4 | ---@class SDBMHash 5 | local mt = {} 6 | mt.__index = mt 7 | 8 | mt.cache = nil 9 | 10 | ---@param str string 11 | ---@return integer 12 | function mt:rawHash(str) 13 | local id = 0 14 | for i = 1, #str do 15 | local b = byte(str, i, i) 16 | id = id * 65599 + b 17 | end 18 | return id & max 19 | end 20 | 21 | ---@param str string 22 | ---@return integer 23 | function mt:hash(str) 24 | local id = self:rawHash(str) 25 | local other = self.cache[id] 26 | if other == nil or str == other then 27 | self.cache[id] = str 28 | self.cache[str] = id 29 | return id 30 | else 31 | log.warn(('哈希碰撞:[%s] -> [%s]: [%d]'):format(str, other, id)) 32 | for i = 1, max do 33 | local newId = (id + i) % max 34 | if not self.cache[newId] then 35 | self.cache[newId] = str 36 | self.cache[str] = newId 37 | return newId 38 | end 39 | end 40 | error(('哈希碰撞解决失败:[%s] -> [%s]: [%d]'):format(str, other, id)) 41 | end 42 | end 43 | 44 | function mt:setCache(t) 45 | self.cache = t 46 | end 47 | 48 | function mt:getCache() 49 | return self.cache 50 | end 51 | 52 | mt.__call = mt.hash 53 | 54 | ---@return SDBMHash 55 | return function () 56 | local self = setmetatable({ 57 | cache = {} 58 | }, mt) 59 | return self 60 | end 61 | -------------------------------------------------------------------------------- /script/brave/brave.lua: -------------------------------------------------------------------------------- 1 | local thread = require 'bee.thread' 2 | 3 | local taskPad = thread.channel('taskpad') 4 | local waiter = thread.channel('waiter') 5 | 6 | ---@class pub_brave 7 | local m = {} 8 | m.type = 'brave' 9 | m.ability = {} 10 | m.queue = {} 11 | 12 | --- 注册成为勇者 13 | function m.register(id, privatePad) 14 | m.id = id 15 | 16 | if #m.queue > 0 then 17 | for _, info in ipairs(m.queue) do 18 | waiter:push(m.id, info.name, info.params) 19 | end 20 | end 21 | m.queue = nil 22 | 23 | m.start(privatePad) 24 | end 25 | 26 | --- 注册能力 27 | function m.on(name, callback) 28 | m.ability[name] = callback 29 | end 30 | 31 | --- 报告 32 | function m.push(name, params) 33 | if m.id then 34 | waiter:push(m.id, name, params) 35 | else 36 | m.queue[#m.queue+1] = { 37 | name = name, 38 | params = params, 39 | } 40 | end 41 | end 42 | 43 | --- 开始找工作 44 | function m.start(privatePad) 45 | local reqPad = privatePad and thread.channel('req:' .. privatePad) or taskPad 46 | local resPad = privatePad and thread.channel('res:' .. privatePad) or waiter 47 | m.push('mem', collectgarbage 'count') 48 | while true do 49 | local name, id, params = reqPad:bpop() 50 | local ability = m.ability[name] 51 | -- TODO 52 | if not ability then 53 | resPad:push(m.id, id) 54 | log.error('Brave can not handle this work: ' .. name) 55 | goto CONTINUE 56 | end 57 | local ok, res = xpcall(ability, log.error, params) 58 | if ok then 59 | resPad:push(m.id, id, res) 60 | else 61 | resPad:push(m.id, id) 62 | end 63 | m.push('mem', collectgarbage 'count') 64 | ::CONTINUE:: 65 | end 66 | end 67 | 68 | return m 69 | -------------------------------------------------------------------------------- /script/brave/init.lua: -------------------------------------------------------------------------------- 1 | local brave = require 'brave.brave' 2 | require 'brave.work' 3 | 4 | return brave 5 | -------------------------------------------------------------------------------- /script/brave/log.lua: -------------------------------------------------------------------------------- 1 | local brave = require 'brave' 2 | local time = require 'bee.time' 3 | 4 | local tablePack = table.pack 5 | local tostring = tostring 6 | local tableConcat = table.concat 7 | local debugTraceBack = debug.traceback 8 | local debugGetInfo = debug.getinfo 9 | local monotonic = time.monotonic 10 | 11 | _ENV = nil 12 | 13 | local function pushLog(level, ...) 14 | local t = tablePack(...) 15 | for i = 1, t.n do 16 | t[i] = tostring(t[i]) 17 | end 18 | local str = tableConcat(t, '\t', 1, t.n) 19 | if level == 'error' then 20 | str = str .. '\n' .. debugTraceBack(nil, 3) 21 | end 22 | local info = debugGetInfo(3, 'Sl') 23 | brave.push('log', { 24 | level = level, 25 | msg = str, 26 | src = info.source, 27 | line = info.currentline, 28 | clock = monotonic(), 29 | }) 30 | return str 31 | end 32 | 33 | local m = {} 34 | 35 | function m.info(...) 36 | pushLog('info', ...) 37 | end 38 | 39 | function m.debug(...) 40 | pushLog('debug', ...) 41 | end 42 | 43 | function m.trace(...) 44 | pushLog('trace', ...) 45 | end 46 | 47 | function m.warn(...) 48 | pushLog('warn', ...) 49 | end 50 | 51 | function m.error(...) 52 | pushLog('error', ...) 53 | end 54 | 55 | return m 56 | -------------------------------------------------------------------------------- /script/cli/doc2md.lua: -------------------------------------------------------------------------------- 1 | -- This is an example of how to process the generated `doc.json` file. 2 | -- You can use it to generate a markdown file or a html file. 3 | 4 | local jsonc = require 'jsonc' 5 | local util = require 'utility' 6 | local markdown = require 'provider.markdown' 7 | 8 | local export = {} 9 | 10 | function export.buildMD(outputPath) 11 | local doc = jsonc.decode_jsonc(util.loadFile(outputPath .. '/doc.json')) 12 | local md = markdown() 13 | 14 | assert(type(doc) == 'table') 15 | 16 | for _, class in ipairs(doc) do 17 | md:add('md', '# ' .. class.name) 18 | md:emptyLine() 19 | md:add('md', class.desc) 20 | md:emptyLine() 21 | if class.defines then 22 | for _, define in ipairs(class.defines) do 23 | if define.extends then 24 | md:add('lua', define.extends.view) 25 | md:emptyLine() 26 | end 27 | end 28 | end 29 | if class.fields then 30 | local mark = {} 31 | for _, field in ipairs(class.fields) do 32 | if not mark[field.name] then 33 | mark[field.name] = true 34 | md:add('md', '## ' .. field.name) 35 | md:emptyLine() 36 | md:add('lua', field.extends.view) 37 | md:emptyLine() 38 | md:add('md', field.desc) 39 | md:emptyLine() 40 | end 41 | end 42 | end 43 | md:splitLine() 44 | end 45 | 46 | local mdPath = outputPath .. '/doc.md' 47 | 48 | util.saveFile(mdPath, md:string()) 49 | 50 | return mdPath 51 | end 52 | 53 | return export 54 | -------------------------------------------------------------------------------- /script/cli/init.lua: -------------------------------------------------------------------------------- 1 | if _G['VERSION'] then 2 | require 'cli.version' 3 | os.exit(0, true) 4 | end 5 | 6 | if _G['CHECK'] then 7 | require 'cli.check' 8 | os.exit(0, true) 9 | end 10 | 11 | if _G['DOC_UPDATE'] then 12 | require 'cli.doc' .runCLI() 13 | os.exit(0, true) 14 | end 15 | 16 | if _G['DOC'] then 17 | require 'cli.doc' .runCLI() 18 | os.exit(0, true) 19 | end 20 | 21 | if _G['VISUALIZE'] then 22 | local ret = require 'cli.visualize' .runCLI() 23 | os.exit(ret or 0, true) 24 | end 25 | -------------------------------------------------------------------------------- /script/cli/version.lua: -------------------------------------------------------------------------------- 1 | local version = require 'version' 2 | print(version.getVersion()) 3 | -------------------------------------------------------------------------------- /script/config/init.lua: -------------------------------------------------------------------------------- 1 | local config = require 'config.config' 2 | 3 | return config 4 | -------------------------------------------------------------------------------- /script/core/command/exportDocument.lua: -------------------------------------------------------------------------------- 1 | local doc = require 'cli.doc' 2 | local client = require 'client' 3 | local furi = require 'file-uri' 4 | local lang = require 'language' 5 | local ws = require 'workspace' 6 | local files = require 'files' 7 | 8 | ---@async 9 | return function (args) 10 | local outputPath = args[1] and furi.decode(args[1]) or LOGPATH 11 | local docPath, mdPath = doc.makeDoc(outputPath) 12 | client.showMessage('Info', lang.script('CLI_DOC_DONE' 13 | , ('[%s](%s)'):format(files.normalize(docPath), furi.encode(docPath)) 14 | , ('[%s](%s)'):format(files.normalize(mdPath), furi.encode(mdPath)) 15 | )) 16 | end 17 | -------------------------------------------------------------------------------- /script/core/command/getConfig.lua: -------------------------------------------------------------------------------- 1 | local config = require 'config' 2 | local client = require 'client' 3 | local await = require 'await' 4 | 5 | ---@async 6 | return function (data) 7 | local uri = data[1].uri 8 | local key = data[1].key 9 | while not client:isReady() do 10 | await.sleep(0.1) 11 | end 12 | return config.get(uri, key) 13 | end 14 | -------------------------------------------------------------------------------- /script/core/command/jsonToLua.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local util = require 'utility' 3 | local proto = require 'proto' 4 | local define = require 'proto.define' 5 | local lang = require 'language' 6 | local converter = require 'proto.converter' 7 | local guide = require 'parser.guide' 8 | local json = require 'json' 9 | local jsonc = require 'jsonc' 10 | 11 | ---@async 12 | return function (data) 13 | local state = files.getState(data.uri) 14 | local text = files.getText(data.uri) 15 | if not text or not state then 16 | return 17 | end 18 | local start = guide.positionToOffset(state, data.start) 19 | local finish = guide.positionToOffset(state, data.finish) 20 | local jsonStr = text:sub(start + 1, finish) 21 | local suc, res = pcall(jsonc.decode_jsonc, jsonStr:match '[%{%[].+') 22 | if not suc or res == json.null then 23 | proto.notify('window/showMessage', { 24 | type = define.MessageType.Warning, 25 | message = lang.script('COMMAND_JSON_TO_LUA_FAILED', res:match '%:%d+%:(.+)'), 26 | }) 27 | return 28 | end 29 | ---@cast res table 30 | local luaStr = util.dump(res) 31 | if jsonStr:sub(1, 1) == '"' then 32 | local key = jsonStr:match '^"([^\r\n]+)"' 33 | if key then 34 | if key:match '^[%a_]%w*$' then 35 | luaStr = ('%s = %s'):format(key, luaStr) 36 | else 37 | luaStr = ('[%q] = %s'):format(key, luaStr) 38 | end 39 | end 40 | end 41 | proto.awaitRequest('workspace/applyEdit', { 42 | label = 'json to lua', 43 | edit = { 44 | changes = { 45 | [data.uri] = { 46 | { 47 | range = converter.packRange(state, data.start, data.finish), 48 | newText = luaStr, 49 | } 50 | } 51 | } 52 | } 53 | }) 54 | end 55 | -------------------------------------------------------------------------------- /script/core/command/reloadFFIMeta.lua: -------------------------------------------------------------------------------- 1 | local config = require 'config' 2 | local ws = require 'workspace' 3 | local fs = require 'bee.filesystem' 4 | local scope = require 'workspace.scope' 5 | local SDBMHash = require 'SDBMHash' 6 | local searchCode = require 'plugins.ffi.searchCode' 7 | local cdefRerence = require 'plugins.ffi.cdefRerence' 8 | local ffi = require 'plugins.ffi' 9 | 10 | local function createDir(uri) 11 | local dir = scope.getScope(uri).uri or 'default' 12 | local fileDir = fs.path(METAPATH) / ('%08x'):format(SDBMHash():hash(dir)) 13 | if fs.exists(fileDir) then 14 | return fileDir, true 15 | end 16 | fs.create_directories(fileDir) 17 | return fileDir 18 | end 19 | 20 | ---@async 21 | return function (uri) 22 | if config.get(uri, 'Lua.runtime.version') ~= 'LuaJIT' then 23 | return 24 | end 25 | 26 | ws.awaitReady(uri) 27 | 28 | local fileDir, exists = createDir(uri) 29 | 30 | local refs = cdefRerence() 31 | if not refs or #refs == 0 then 32 | return 33 | end 34 | 35 | for i, v in ipairs(refs) do 36 | local target_uri = v.uri 37 | local codes = searchCode(refs, target_uri) 38 | if not codes then 39 | return 40 | end 41 | 42 | ffi.build_single(codes, fileDir, target_uri) 43 | end 44 | 45 | if not exists then 46 | local client = require 'client' 47 | client.setConfig { 48 | { 49 | key = 'Lua.workspace.library', 50 | action = 'add', 51 | value = tostring(fileDir), 52 | uri = uri, 53 | } 54 | } 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /script/core/command/removeSpace.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local proto = require 'proto' 4 | local lang = require 'language' 5 | local converter = require 'proto.converter' 6 | 7 | ---@async 8 | return function (data) 9 | local uri = data.uri 10 | local text = files.getText(uri) 11 | local state = files.getState(uri) 12 | if not state or not text then 13 | return 14 | end 15 | 16 | local lines = state.lines 17 | local textEdit = {} 18 | for i = 0, #lines do 19 | local startOffset = lines[i] 20 | local finishOffset = text:find('[\r\n]', startOffset) or (#text + 1) 21 | local lastOffset = finishOffset - 1 22 | local lastChar = text:sub(lastOffset, lastOffset) 23 | if lastChar ~= ' ' and lastChar ~= '\t' then 24 | goto NEXT_LINE 25 | end 26 | local lastPos = guide.offsetToPosition(state, lastOffset) 27 | if guide.isInString(state.ast, lastPos) 28 | or guide.isInComment(state.ast, lastPos) then 29 | goto NEXT_LINE 30 | end 31 | local firstOffset = startOffset 32 | for n = lastOffset - 1, startOffset, -1 do 33 | local char = text:sub(n, n) 34 | if char ~= ' ' and char ~= '\t' then 35 | firstOffset = n + 1 36 | break 37 | end 38 | end 39 | local firstPos = guide.offsetToPosition(state, firstOffset) - 1 40 | textEdit[#textEdit+1] = { 41 | range = converter.packRange(state, firstPos, lastPos), 42 | newText = '', 43 | } 44 | 45 | ::NEXT_LINE:: 46 | end 47 | 48 | if #textEdit == 0 then 49 | return 50 | end 51 | 52 | proto.awaitRequest('workspace/applyEdit', { 53 | label = lang.script.COMMAND_REMOVE_SPACE, 54 | edit = { 55 | changes = { 56 | [uri] = textEdit, 57 | } 58 | }, 59 | }) 60 | end 61 | -------------------------------------------------------------------------------- /script/core/command/setConfig.lua: -------------------------------------------------------------------------------- 1 | local client = require 'client' 2 | local await = require 'await' 3 | 4 | ---@async 5 | ---@param changes config.change[] 6 | return function (changes) 7 | while not client:isReady() do 8 | await.sleep(0.1) 9 | end 10 | client.setConfig(changes) 11 | end 12 | -------------------------------------------------------------------------------- /script/core/completion/init.lua: -------------------------------------------------------------------------------- 1 | return require 'core.completion.completion' 2 | -------------------------------------------------------------------------------- /script/core/diagnostics/await-in-sync.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local vm = require 'vm' 4 | local lang = require 'language' 5 | local await = require 'await' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | 14 | ---@async 15 | guide.eachSourceType(state.ast, 'call', function (source) 16 | local currentFunc = guide.getParentFunction(source) 17 | if currentFunc and vm.isAsync(currentFunc, false) then 18 | return 19 | end 20 | await.delay() 21 | if vm.isAsyncCall(source) then 22 | callback { 23 | start = source.node.start, 24 | finish = source.node.finish, 25 | message = lang.script('DIAG_AWAIT_IN_SYNC'), 26 | } 27 | return 28 | end 29 | end) 30 | end 31 | -------------------------------------------------------------------------------- /script/core/diagnostics/cast-local-type.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local lang = require 'language' 3 | local guide = require 'parser.guide' 4 | local vm = require 'vm' 5 | local await = require 'await' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | 14 | ---@async 15 | guide.eachSourceType(state.ast, 'local', function (loc) 16 | if not loc.ref then 17 | return 18 | end 19 | if loc[1] == '_' then 20 | return 21 | end 22 | await.delay() 23 | local locNode = vm.compileNode(loc) 24 | if not locNode.hasDefined then 25 | return 26 | end 27 | for _, ref in ipairs(loc.ref) do 28 | if ref.type == 'setlocal' and ref.value then 29 | await.delay() 30 | local refNode = vm.compileNode(ref) 31 | local value = ref.value 32 | 33 | if value.type == 'getfield' 34 | or value.type == 'getindex' then 35 | -- 由于无法对字段进行类型收窄, 36 | -- 因此将假值移除再进行检查 37 | refNode = refNode:copy():setTruthy() 38 | end 39 | 40 | local errs = {} 41 | if not vm.canCastType(uri, locNode, refNode, errs) then 42 | callback { 43 | start = ref.start, 44 | finish = ref.finish, 45 | message = lang.script('DIAG_CAST_LOCAL_TYPE', { 46 | def = vm.getInfer(locNode):view(uri), 47 | ref = vm.getInfer(refNode):view(uri), 48 | }) .. '\n' .. vm.viewTypeErrorMessage(uri, errs), 49 | } 50 | end 51 | end 52 | end 53 | end) 54 | end 55 | -------------------------------------------------------------------------------- /script/core/diagnostics/cast-type-mismatch.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local vm = require 'vm' 5 | local await = require 'await' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | 14 | if not state.ast.docs then 15 | return 16 | end 17 | 18 | for _, doc in ipairs(state.ast.docs) do 19 | if doc.type == 'doc.cast' and doc.name then 20 | await.delay() 21 | local defs = vm.getDefs(doc.name) 22 | local loc = defs[1] 23 | if loc then 24 | local defNode = vm.compileNode(loc) 25 | if defNode.hasDefined then 26 | for _, cast in ipairs(doc.casts) do 27 | if not cast.mode and cast.extends then 28 | local refNode = vm.compileNode(cast.extends) 29 | local errs = {} 30 | if not vm.canCastType(uri, defNode, refNode, errs) then 31 | assert(errs) 32 | callback { 33 | start = cast.extends.start, 34 | finish = cast.extends.finish, 35 | message = lang.script('DIAG_CAST_TYPE_MISMATCH', { 36 | def = vm.getInfer(defNode):view(uri), 37 | ref = vm.getInfer(refNode):view(uri), 38 | }) .. '\n' .. vm.viewTypeErrorMessage(uri, errs), 39 | } 40 | end 41 | end 42 | end 43 | end 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /script/core/diagnostics/close-non-object.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local vm = require 'vm' 5 | 6 | return function (uri, callback) 7 | local state = files.getState(uri) 8 | if not state then 9 | return 10 | end 11 | 12 | guide.eachSourceType(state.ast, 'local', function (source) 13 | if not source.attrs then 14 | return 15 | end 16 | if source.attrs[1][1] ~= 'close' then 17 | return 18 | end 19 | if not source.value then 20 | callback { 21 | start = source.start, 22 | finish = source.finish, 23 | message = lang.script.DIAG_COSE_NON_OBJECT, 24 | } 25 | return 26 | end 27 | local infer = vm.getInfer(source.value) 28 | if not infer:hasClass(uri) 29 | and not infer:hasType(uri, 'nil') 30 | and not infer:hasType(uri, 'table') 31 | and not infer:hasUnknown(uri) 32 | and not infer:hasAny(uri) then 33 | callback { 34 | start = source.value.start, 35 | finish = source.value.finish, 36 | message = lang.script.DIAG_COSE_NON_OBJECT, 37 | } 38 | end 39 | end) 40 | end 41 | -------------------------------------------------------------------------------- /script/core/diagnostics/code-after-break.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local define = require 'proto.define' 5 | local await = require 'await' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | 14 | local mark = {} 15 | ---@async 16 | guide.eachSourceType(state.ast, 'break', function (source) 17 | local list = source.parent 18 | if mark[list] then 19 | return 20 | end 21 | mark[list] = true 22 | await.delay() 23 | for i = #list, 1, -1 do 24 | local src = list[i] 25 | if src == source then 26 | if i == #list then 27 | return 28 | end 29 | callback { 30 | start = list[i+1].start, 31 | finish = list[#list].range or list[#list].finish, 32 | tags = { define.DiagnosticTag.Unnecessary }, 33 | message = lang.script.DIAG_CODE_AFTER_BREAK, 34 | } 35 | end 36 | end 37 | end) 38 | end 39 | -------------------------------------------------------------------------------- /script/core/diagnostics/codestyle-check.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local converter = require 'proto.converter' 3 | local log = require 'log' 4 | local pformatting = require 'provider.formatting' 5 | 6 | 7 | ---@async 8 | return function(uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | local text = state.originText 14 | 15 | local suc, codeFormat = pcall(require, 'code_format') 16 | if not suc then 17 | return 18 | end 19 | 20 | pformatting.updateConfig(uri) 21 | 22 | local status, diagnosticInfos = codeFormat.diagnose_file(uri, text) 23 | 24 | if not status then 25 | if diagnosticInfos ~= nil then 26 | log.error(diagnosticInfos) 27 | end 28 | 29 | return 30 | end 31 | 32 | if diagnosticInfos then 33 | for _, diagnosticInfo in ipairs(diagnosticInfos) do 34 | callback { 35 | start = converter.unpackPosition(state, diagnosticInfo.range.start), 36 | finish = converter.unpackPosition(state, diagnosticInfo.range["end"]), 37 | message = diagnosticInfo.message 38 | } 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /script/core/diagnostics/count-down-loop.lua: -------------------------------------------------------------------------------- 1 | local files = require "files" 2 | local guide = require "parser.guide" 3 | local lang = require 'language' 4 | 5 | return function (uri, callback) 6 | local state = files.getState(uri) 7 | local text = files.getText(uri) 8 | if not state or not text then 9 | return 10 | end 11 | 12 | guide.eachSourceType(state.ast, 'loop', function (source) 13 | local maxNumber = source.max and tonumber(source.max[1]) 14 | if not maxNumber then 15 | return 16 | end 17 | local minNumber = source.init and tonumber(source.init[1]) 18 | if minNumber and maxNumber and minNumber <= maxNumber then 19 | return 20 | end 21 | if not minNumber and maxNumber ~= 1 then 22 | return 23 | end 24 | if not source.step then 25 | callback { 26 | start = source.init.start, 27 | finish = source.max.finish, 28 | message = lang.script('DIAG_COUNT_DOWN_LOOP' 29 | , ('%s, %s'):format(text:sub( 30 | guide.positionToOffset(state, source.init.start + 1), 31 | guide.positionToOffset(state, source.max.finish) 32 | ), '-1') 33 | ) 34 | } 35 | else 36 | local stepNumber = tonumber(source.step[1]) 37 | if stepNumber and stepNumber > 0 then 38 | callback { 39 | start = source.init.start, 40 | finish = source.step.finish, 41 | message = lang.script('DIAG_COUNT_DOWN_LOOP' 42 | , ('%s, -%s'):format(text:sub( 43 | guide.positionToOffset(state, source.init.start + 1), 44 | guide.positionToOffset(state, source.max.finish) 45 | ), source.step[1]) 46 | ) 47 | } 48 | end 49 | end 50 | end) 51 | end 52 | -------------------------------------------------------------------------------- /script/core/diagnostics/different-requires.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local config = require 'config' 5 | local vm = require 'vm' 6 | local rpath = require 'workspace.require-path' 7 | 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | local cache = vm.getCache 'different-requires' 14 | guide.eachSpecialOf(state.ast, 'require', function (source) 15 | local call = source.parent 16 | if not call or call.type ~= 'call' then 17 | return 18 | end 19 | local arg1 = call.args and call.args[1] 20 | if not arg1 or arg1.type ~= 'string' then 21 | return 22 | end 23 | local literal = arg1[1] 24 | local results = rpath.findUrisByRequireName(uri, literal) 25 | if not results or #results ~= 1 then 26 | return 27 | end 28 | local result = results[1] 29 | if not files.isLua(result) then 30 | return 31 | end 32 | local other = cache[result] 33 | if not other then 34 | cache[result] = { 35 | source = arg1, 36 | require = literal, 37 | } 38 | return 39 | end 40 | if other.require ~= literal then 41 | callback { 42 | start = arg1.start, 43 | finish = arg1.finish, 44 | related = { 45 | { 46 | start = other.source.start, 47 | finish = other.source.finish, 48 | uri = guide.getUri(other.source), 49 | } 50 | }, 51 | message = lang.script('DIAG_DIFFERENT_REQUIRES'), 52 | } 53 | end 54 | end) 55 | end 56 | -------------------------------------------------------------------------------- /script/core/diagnostics/discard-returns.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local vm = require 'vm' 4 | local await = require 'await' 5 | local lang = require 'language' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | ---@async 14 | guide.eachSourceType(state.ast, 'call', function (source) 15 | if not guide.isBlockType(source.parent) then 16 | return 17 | end 18 | if source.parent.filter == source then 19 | return 20 | end 21 | await.delay() 22 | if vm.isNoDiscard(source.node, true) then 23 | callback { 24 | start = source.start, 25 | finish = source.finish, 26 | message = lang.script('DIAG_DISCARD_RETURNS'), 27 | } 28 | end 29 | end) 30 | end 31 | -------------------------------------------------------------------------------- /script/core/diagnostics/doc-field-no-class.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local lang = require 'language' 3 | 4 | return function (uri, callback) 5 | local state = files.getState(uri) 6 | if not state then 7 | return 8 | end 9 | 10 | if not state.ast.docs then 11 | return 12 | end 13 | 14 | for _, doc in ipairs(state.ast.docs) do 15 | if doc.type ~= 'doc.field' then 16 | goto CONTINUE 17 | end 18 | local bindGroup = doc.bindGroup 19 | if not bindGroup then 20 | goto CONTINUE 21 | end 22 | local ok 23 | for _, other in ipairs(bindGroup) do 24 | if other.type == 'doc.class' then 25 | ok = true 26 | break 27 | end 28 | if other == doc then 29 | break 30 | end 31 | end 32 | if not ok then 33 | callback { 34 | start = doc.start, 35 | finish = doc.finish, 36 | message = lang.script('DIAG_DOC_FIELD_NO_CLASS'), 37 | } 38 | end 39 | ::CONTINUE:: 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /script/core/diagnostics/duplicate-doc-alias.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local lang = require 'language' 3 | local vm = require 'vm' 4 | local guide = require 'parser.guide' 5 | local await = require 'await' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | 14 | if not state.ast.docs then 15 | return 16 | end 17 | 18 | local merged = {} 19 | local cache = {} 20 | for _, doc in ipairs(state.ast.docs) do 21 | if doc.type == 'doc.alias' 22 | or doc.type == 'doc.enum' then 23 | local name = guide.getKeyName(doc) 24 | if not name then 25 | return 26 | end 27 | await.delay() 28 | if not cache[name] then 29 | local docs = vm.getDocSets(uri, name) 30 | cache[name] = {} 31 | for _, otherDoc in ipairs(docs) do 32 | if otherDoc.type == 'doc.alias' 33 | or otherDoc.type == 'doc.class' 34 | or otherDoc.type == 'doc.enum' then 35 | cache[name][#cache[name]+1] = { 36 | start = otherDoc.start, 37 | finish = otherDoc.finish, 38 | uri = guide.getUri(otherDoc), 39 | } 40 | merged[name] = merged[name] or vm.docHasAttr(otherDoc, 'partial') 41 | end 42 | end 43 | end 44 | if not merged[name] and #cache[name] > 1 then 45 | callback { 46 | start = (doc.alias or doc.enum).start, 47 | finish = (doc.alias or doc.enum).finish, 48 | related = cache, 49 | message = lang.script('DIAG_DUPLICATE_DOC_ALIAS', name) 50 | } 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /script/core/diagnostics/duplicate-doc-param.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local lang = require 'language' 3 | 4 | return function (uri, callback) 5 | local state = files.getState(uri) 6 | if not state then 7 | return 8 | end 9 | 10 | if not state.ast.docs then 11 | return 12 | end 13 | 14 | for _, doc in ipairs(state.ast.docs) do 15 | if doc.type ~= 'doc.param' then 16 | goto CONTINUE 17 | end 18 | local name = doc.param[1] 19 | local bindGroup = doc.bindGroup 20 | if not bindGroup then 21 | goto CONTINUE 22 | end 23 | for _, other in ipairs(bindGroup) do 24 | if other ~= doc 25 | and other.type == 'doc.param' 26 | and other.param[1] == name then 27 | callback { 28 | start = doc.param.start, 29 | finish = doc.param.finish, 30 | message = lang.script('DIAG_DUPLICATE_DOC_PARAM', name) 31 | } 32 | goto CONTINUE 33 | end 34 | end 35 | ::CONTINUE:: 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /script/core/diagnostics/empty-block.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local define = require 'proto.define' 5 | local await = require 'await' 6 | 7 | -- 检查空代码块 8 | -- 但是排除忙等待(repeat/while) 9 | ---@async 10 | return function (uri, callback) 11 | local ast = files.getState(uri) 12 | if not ast then 13 | return 14 | end 15 | 16 | await.delay() 17 | guide.eachSourceType(ast.ast, 'if', function (source) 18 | for _, block in ipairs(source) do 19 | if #block > 0 then 20 | return 21 | end 22 | end 23 | callback { 24 | start = source.start, 25 | finish = source.finish, 26 | tags = { define.DiagnosticTag.Unnecessary }, 27 | message = lang.script.DIAG_EMPTY_BLOCK, 28 | } 29 | end) 30 | await.delay() 31 | guide.eachSourceType(ast.ast, 'loop', function (source) 32 | if #source > 0 then 33 | return 34 | end 35 | callback { 36 | start = source.start, 37 | finish = source.finish, 38 | tags = { define.DiagnosticTag.Unnecessary }, 39 | message = lang.script.DIAG_EMPTY_BLOCK, 40 | } 41 | end) 42 | await.delay() 43 | guide.eachSourceType(ast.ast, 'in', function (source) 44 | if #source > 0 then 45 | return 46 | end 47 | callback { 48 | start = source.start, 49 | finish = source.finish, 50 | tags = { define.DiagnosticTag.Unnecessary }, 51 | message = lang.script.DIAG_EMPTY_BLOCK, 52 | } 53 | end) 54 | end 55 | -------------------------------------------------------------------------------- /script/core/diagnostics/global-element.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local config = require 'config' 5 | local vm = require 'vm' 6 | local util = require 'utility' 7 | 8 | local function isDocClass(source) 9 | if not source.bindDocs then 10 | return false 11 | end 12 | for _, doc in ipairs(source.bindDocs) do 13 | if doc.type == 'doc.class' then 14 | return true 15 | end 16 | end 17 | return false 18 | end 19 | 20 | -- If global elements are discouraged by coding convention, this diagnostic helps with reminding about that 21 | -- Exceptions may be added to Lua.diagnostics.globals 22 | return function (uri, callback) 23 | local ast = files.getState(uri) 24 | if not ast then 25 | return 26 | end 27 | 28 | local definedGlobal = util.arrayToHash(config.get(uri, 'Lua.diagnostics.globals')) 29 | 30 | guide.eachSourceType(ast.ast, 'setglobal', function (source) 31 | local name = guide.getKeyName(source) 32 | if not name or definedGlobal[name] then 33 | return 34 | end 35 | -- If the assignment is marked as doc.class, then it is considered allowed 36 | if isDocClass(source) then 37 | return 38 | end 39 | if definedGlobal[name] == nil then 40 | definedGlobal[name] = false 41 | local global = vm.getGlobal('variable', name) 42 | if global then 43 | for _, set in ipairs(global:getSets(uri)) do 44 | if vm.isMetaFile(guide.getUri(set)) then 45 | definedGlobal[name] = true 46 | return 47 | end 48 | end 49 | end 50 | end 51 | callback { 52 | start = source.start, 53 | finish = source.finish, 54 | message = lang.script.DIAG_GLOBAL_ELEMENT, 55 | } 56 | end) 57 | end 58 | -------------------------------------------------------------------------------- /script/core/diagnostics/global-in-nil-env.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | 5 | return function (uri, callback) 6 | local state = files.getState(uri) 7 | if not state then 8 | return 9 | end 10 | 11 | local function check(source) 12 | local node = source.node 13 | if node.tag == '_ENV' then 14 | return 15 | end 16 | if guide.isParam(node) then 17 | return 18 | end 19 | 20 | if not node.value or node.value.type == 'nil' then 21 | callback { 22 | start = source.start, 23 | finish = source.finish, 24 | uri = uri, 25 | message = lang.script.DIAG_GLOBAL_IN_NIL_ENV, 26 | related = { 27 | { 28 | start = node.start, 29 | finish = node.finish, 30 | uri = uri, 31 | } 32 | } 33 | } 34 | end 35 | end 36 | 37 | guide.eachSourceType(state.ast, 'getglobal', check) 38 | guide.eachSourceType(state.ast, 'setglobal', check) 39 | end 40 | -------------------------------------------------------------------------------- /script/core/diagnostics/lowercase-global.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local config = require 'config' 5 | local vm = require 'vm' 6 | local util = require 'utility' 7 | 8 | local function isDocClass(source) 9 | if not source.bindDocs then 10 | return false 11 | end 12 | for _, doc in ipairs(source.bindDocs) do 13 | if doc.type == 'doc.class' then 14 | return true 15 | end 16 | end 17 | return false 18 | end 19 | 20 | -- 不允许定义首字母小写的全局变量(很可能是拼错或者漏删) 21 | return function (uri, callback) 22 | local ast = files.getState(uri) 23 | if not ast then 24 | return 25 | end 26 | 27 | local definedGlobal = util.arrayToHash(config.get(uri, 'Lua.diagnostics.globals')) 28 | 29 | guide.eachSourceType(ast.ast, 'setglobal', function (source) 30 | local name = guide.getKeyName(source) 31 | if not name or definedGlobal[name] then 32 | return 33 | end 34 | local first = name:match '%w' 35 | if not first then 36 | return 37 | end 38 | if not first:match '%l' then 39 | return 40 | end 41 | -- 如果赋值被标记为 doc.class ,则认为是允许的 42 | if isDocClass(source) then 43 | return 44 | end 45 | if definedGlobal[name] == nil then 46 | definedGlobal[name] = false 47 | local global = vm.getGlobal('variable', name) 48 | if global then 49 | for _, set in ipairs(global:getSets(uri)) do 50 | if vm.isMetaFile(guide.getUri(set)) then 51 | definedGlobal[name] = true 52 | return 53 | end 54 | end 55 | end 56 | end 57 | callback { 58 | start = source.start, 59 | finish = source.finish, 60 | message = lang.script.DIAG_LOWERCASE_GLOBAL, 61 | } 62 | end) 63 | end 64 | -------------------------------------------------------------------------------- /script/core/diagnostics/missing-global-doc.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require "parser.guide" 3 | local await = require 'await' 4 | local helper = require 'core.diagnostics.helper.missing-doc-helper' 5 | 6 | ---@async 7 | return function (uri, callback) 8 | local state = files.getState(uri) 9 | if not state then 10 | return 11 | end 12 | 13 | if not state.ast then 14 | return 15 | end 16 | 17 | ---@async 18 | guide.eachSourceType(state.ast, 'function', function (source) 19 | await.delay() 20 | 21 | if source.parent.type ~= 'setglobal' then 22 | return 23 | end 24 | 25 | helper.CheckFunction(source, callback, 'DIAG_MISSING_GLOBAL_DOC_COMMENT', 'DIAG_MISSING_GLOBAL_DOC_PARAM', 'DIAG_MISSING_GLOBAL_DOC_RETURN') 26 | end) 27 | end 28 | -------------------------------------------------------------------------------- /script/core/diagnostics/missing-local-export-doc.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require "parser.guide" 3 | local await = require 'await' 4 | local helper = require 'core.diagnostics.helper.missing-doc-helper' 5 | 6 | ---@async 7 | local function findSetField(ast, name, callback) 8 | ---@async 9 | guide.eachSourceType(ast, 'setfield', function (source) 10 | await.delay() 11 | if source.node[1] == name then 12 | local funcPtr = source.value.node 13 | if not funcPtr then 14 | return 15 | end 16 | local func = funcPtr.value 17 | if not func then 18 | return 19 | end 20 | if funcPtr.type == 'local' and func.type == 'function' then 21 | helper.CheckFunction(func, callback, 'DIAG_MISSING_LOCAL_EXPORT_DOC_COMMENT', 'DIAG_MISSING_LOCAL_EXPORT_DOC_PARAM', 'DIAG_MISSING_LOCAL_EXPORT_DOC_RETURN') 22 | end 23 | end 24 | end) 25 | end 26 | 27 | ---@async 28 | return function (uri, callback) 29 | local state = files.getState(uri) 30 | 31 | if not state then 32 | return 33 | end 34 | 35 | if not state.ast then 36 | return 37 | end 38 | 39 | ---@async 40 | guide.eachSourceType(state.ast, 'return', function (source) 41 | await.delay() 42 | --table 43 | 44 | for i, ret in ipairs(source) do 45 | if ret.type == 'getlocal' then 46 | if ret.node.value and ret.node.value.type == 'table' then 47 | findSetField(state.ast, ret[1], callback) 48 | end 49 | end 50 | end 51 | end) 52 | end 53 | -------------------------------------------------------------------------------- /script/core/diagnostics/missing-parameter.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local vm = require 'vm' 4 | local lang = require 'language' 5 | local await = require 'await' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | 14 | ---@async 15 | guide.eachSourceType(state.ast, 'call', function (source) 16 | await.delay() 17 | local _, callArgs = vm.countList(source.args) 18 | 19 | local funcNode = vm.compileNode(source.node) 20 | local funcArgs = vm.countParamsOfNode(funcNode) 21 | 22 | if callArgs >= funcArgs then 23 | return 24 | end 25 | 26 | callback { 27 | start = source.start, 28 | finish = source.finish, 29 | message = lang.script('DIAG_MISS_ARGS', funcArgs, callArgs), 30 | } 31 | end) 32 | end 33 | -------------------------------------------------------------------------------- /script/core/diagnostics/missing-return-value.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local vm = require 'vm' 4 | local lang = require 'language' 5 | local await = require 'await' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | 14 | ---@async 15 | guide.eachSourceType(state.ast, 'function', function (source) 16 | await.delay() 17 | local returns = source.returns 18 | if not returns then 19 | return 20 | end 21 | local min = vm.countReturnsOfSource(source) 22 | if min == 0 then 23 | return 24 | end 25 | for _, ret in ipairs(returns) do 26 | local rmin, rmax = vm.countList(ret) 27 | if rmax < min then 28 | if rmin == rmax then 29 | callback { 30 | start = ret.start, 31 | finish = ret.start + #'return', 32 | message = lang.script('DIAG_MISSING_RETURN_VALUE', { 33 | min = min, 34 | rmax = rmax, 35 | }), 36 | } 37 | else 38 | callback { 39 | start = ret.start, 40 | finish = ret.start + #'return', 41 | message = lang.script('DIAG_MISSING_RETURN_VALUE_RANGE', { 42 | min = min, 43 | rmin = rmin, 44 | rmax = rmax, 45 | }), 46 | } 47 | end 48 | end 49 | end 50 | end) 51 | end 52 | -------------------------------------------------------------------------------- /script/core/diagnostics/name-style-check.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local converter = require 'proto.converter' 3 | local log = require 'log' 4 | local nameStyle = require 'provider.name-style' 5 | 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | local text = state.originText 14 | 15 | local status, diagnosticInfos = nameStyle.nameStyleCheck(uri, text) 16 | 17 | if not status then 18 | if diagnosticInfos ~= nil then 19 | log.error(diagnosticInfos) 20 | end 21 | 22 | return 23 | end 24 | 25 | if diagnosticInfos then 26 | for _, diagnosticInfo in ipairs(diagnosticInfos) do 27 | callback { 28 | start = converter.unpackPosition(state, diagnosticInfo.range.start), 29 | finish = converter.unpackPosition(state, diagnosticInfo.range["end"]), 30 | message = diagnosticInfo.message, 31 | data = diagnosticInfo.data 32 | } 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /script/core/diagnostics/need-check-nil.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local vm = require 'vm' 4 | local lang = require 'language' 5 | local await = require 'await' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | 14 | ---@async 15 | guide.eachSourceType(state.ast, 'getlocal', function (src) 16 | await.delay() 17 | local checkNil 18 | local nxt = src.next 19 | if nxt then 20 | if nxt.type == 'getfield' 21 | or nxt.type == 'getmethod' 22 | or nxt.type == 'getindex' 23 | or nxt.type == 'call' then 24 | checkNil = true 25 | end 26 | end 27 | local call = src.parent 28 | if call and call.type == 'call' and call.node == src then 29 | checkNil = true 30 | end 31 | local setIndex = src.parent 32 | if setIndex and setIndex.type == 'setindex' and setIndex.index == src then 33 | checkNil = true 34 | end 35 | if not checkNil then 36 | return 37 | end 38 | local node = vm.compileNode(src) 39 | if node:hasFalsy() and not vm.getInfer(src):hasType(uri, 'any') then 40 | callback { 41 | start = src.start, 42 | finish = src.finish, 43 | message = lang.script('DIAG_NEED_CHECK_NIL'), 44 | } 45 | end 46 | end) 47 | end 48 | -------------------------------------------------------------------------------- /script/core/diagnostics/newfield-call.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local await = require 'await' 5 | local sub = require 'core.substring' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | local text = files.getText(uri) 11 | if not state or not text then 12 | return 13 | end 14 | 15 | ---@async 16 | guide.eachSourceType(state.ast, 'table', function (source) 17 | await.delay() 18 | for i = 1, #source do 19 | local field = source[i] 20 | if field.type ~= 'tableexp' then 21 | goto CONTINUE 22 | end 23 | local call = field.value 24 | if not call then 25 | goto CONTINUE 26 | end 27 | if call.type ~= 'call' then 28 | return 29 | end 30 | local func = call.node 31 | local args = call.args 32 | if args then 33 | local funcLine = guide.rowColOf(func.finish) 34 | local argsLine = guide.rowColOf(args.start) 35 | if argsLine > funcLine then 36 | callback { 37 | start = call.start, 38 | finish = call.finish, 39 | message = lang.script('DIAG_PREFIELD_CALL' 40 | , sub(state)(func.start + 1, func.finish) 41 | , sub(state)(args.start + 1, args.finish) 42 | ) 43 | } 44 | end 45 | end 46 | ::CONTINUE:: 47 | end 48 | end) 49 | end 50 | -------------------------------------------------------------------------------- /script/core/diagnostics/newline-call.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local await = require 'await' 5 | local sub = require 'core.substring' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | local text = files.getText(uri) 11 | if not state or not text then 12 | return 13 | end 14 | 15 | ---@async 16 | guide.eachSourceType(state.ast, 'call', function (source) 17 | local node = source.node 18 | local args = source.args 19 | if not args then 20 | return 21 | end 22 | 23 | -- 必须有其他人在继续使用当前对象 24 | if not source.next then 25 | return 26 | end 27 | 28 | await.delay() 29 | 30 | local startOffset = guide.positionToOffset(state, args.start) + 1 31 | local finishOffset = guide.positionToOffset(state, args.finish) 32 | if text:sub(startOffset, startOffset) ~= '(' 33 | or text:sub(finishOffset, finishOffset) ~= ')' then 34 | return 35 | end 36 | 37 | local nodeRow = guide.rowColOf(node.finish) 38 | local argRow = guide.rowColOf(args.start) 39 | if nodeRow == argRow then 40 | return 41 | end 42 | 43 | if #args == 1 then 44 | callback { 45 | start = node.start, 46 | finish = args.finish, 47 | message = lang.script('DIAG_PREVIOUS_CALL' 48 | , sub(state)(node.start + 1, node.finish) 49 | , sub(state)(args.start + 1, args.finish) 50 | ), 51 | } 52 | end 53 | end) 54 | end 55 | -------------------------------------------------------------------------------- /script/core/diagnostics/no-unknown.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local vm = require 'vm' 5 | local await = require 'await' 6 | 7 | local types = { 8 | 'local', 9 | 'setlocal', 10 | 'setglobal', 11 | 'getglobal', 12 | 'setfield', 13 | 'setindex', 14 | 'tablefield', 15 | 'tableindex', 16 | } 17 | 18 | ---@async 19 | return function (uri, callback) 20 | local ast = files.getState(uri) 21 | if not ast then 22 | return 23 | end 24 | 25 | ---@async 26 | guide.eachSourceTypes(ast.ast, types, function (source) 27 | await.delay() 28 | if vm.getInfer(source):view(uri) == 'unknown' then 29 | callback { 30 | start = source.start, 31 | finish = source.finish, 32 | message = lang.script('DIAG_UNKNOWN'), 33 | } 34 | end 35 | end) 36 | end 37 | -------------------------------------------------------------------------------- /script/core/diagnostics/redefined-local.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local await = require 'await' 5 | 6 | ---@async 7 | return function (uri, callback) 8 | local ast = files.getState(uri) 9 | if not ast then 10 | return 11 | end 12 | 13 | ---@async 14 | guide.eachSourceType(ast.ast, 'local', function (source) 15 | local name = source[1] 16 | if name == '_' 17 | or name == ast.ENVMode then 18 | return 19 | end 20 | await.delay() 21 | local exist = guide.getLocal(source, name, source.start-1) 22 | if exist then 23 | callback { 24 | start = source.start, 25 | finish = source.finish, 26 | message = lang.script('DIAG_REDEFINED_LOCAL', name), 27 | related = { 28 | { 29 | start = exist.start, 30 | finish = exist.finish, 31 | uri = uri, 32 | } 33 | }, 34 | } 35 | end 36 | end) 37 | end 38 | -------------------------------------------------------------------------------- /script/core/diagnostics/redundant-return.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local define = require 'proto.define' 5 | 6 | -- reports 'return' without any return values at the end of functions 7 | return function (uri, callback) 8 | local ast = files.getState(uri) 9 | if not ast then 10 | return 11 | end 12 | 13 | guide.eachSourceType(ast.ast, 'return', function (source) 14 | if not source.parent or source.parent.type ~= "function" then 15 | return 16 | end 17 | if #source > 0 then 18 | return 19 | end 20 | callback { 21 | start = source.start, 22 | finish = source.finish, 23 | tags = { define.DiagnosticTag.Unnecessary }, 24 | message = lang.script.DIAG_REDUNDANT_RETURN, 25 | } 26 | end) 27 | end 28 | -------------------------------------------------------------------------------- /script/core/diagnostics/redundant-value.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local define = require 'proto.define' 3 | local lang = require 'language' 4 | local guide = require 'parser.guide' 5 | local await = require 'await' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | 14 | guide.eachSource(state.ast, function (src) ---@async 15 | await.delay() 16 | if src.redundant then 17 | callback { 18 | start = src.start, 19 | finish = src.finish, 20 | tags = { define.DiagnosticTag.Unnecessary }, 21 | message = lang.script('DIAG_OVER_MAX_VALUES', src.redundant.max, src.redundant.passed) 22 | } 23 | end 24 | end) 25 | end 26 | -------------------------------------------------------------------------------- /script/core/diagnostics/spell-check.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local converter = require 'proto.converter' 3 | local log = require 'log' 4 | local spell = require 'provider.spell' 5 | 6 | 7 | ---@async 8 | return function(uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | local text = state.originText 14 | 15 | local status, diagnosticInfos = spell.spellCheck(uri, text) 16 | 17 | if not status then 18 | if diagnosticInfos ~= nil then 19 | log.error(diagnosticInfos) 20 | end 21 | 22 | return 23 | end 24 | 25 | if diagnosticInfos then 26 | for _, diagnosticInfo in ipairs(diagnosticInfos) do 27 | callback { 28 | start = converter.unpackPosition(state, diagnosticInfo.range.start), 29 | finish = converter.unpackPosition(state, diagnosticInfo.range["end"]), 30 | message = diagnosticInfo.message, 31 | data = diagnosticInfo.data 32 | } 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /script/core/diagnostics/trailing-space.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local lang = require 'language' 3 | local guide = require 'parser.guide' 4 | local await = require 'await' 5 | 6 | ---@async 7 | return function (uri, callback) 8 | local state = files.getState(uri) 9 | local text = files.getText(uri) 10 | if not state or not text then 11 | return 12 | end 13 | local lines = state.lines 14 | for i = 0, #lines do 15 | await.delay() 16 | local startOffset = lines[i] 17 | local finishOffset = text:find('[\r\n]', startOffset) or (#text + 1) 18 | local lastOffset = finishOffset - 1 19 | local lastChar = text:sub(lastOffset, lastOffset) 20 | if lastChar ~= ' ' and lastChar ~= '\t' then 21 | goto NEXT_LINE 22 | end 23 | local lastPos = guide.offsetToPosition(state, lastOffset) 24 | if guide.isInString(state.ast, lastPos) 25 | or guide.isInComment(state.ast, lastPos) then 26 | goto NEXT_LINE 27 | end 28 | local firstOffset = startOffset 29 | for n = lastOffset - 1, startOffset, -1 do 30 | local char = text:sub(n, n) 31 | if char ~= ' ' and char ~= '\t' then 32 | firstOffset = n + 1 33 | break 34 | end 35 | end 36 | local firstPos = guide.offsetToPosition(state, firstOffset) - 1 37 | if firstOffset == startOffset then 38 | callback { 39 | start = firstPos, 40 | finish = lastPos, 41 | message = lang.script.DIAG_LINE_ONLY_SPACE, 42 | } 43 | else 44 | callback { 45 | start = firstPos, 46 | finish = lastPos, 47 | message = lang.script.DIAG_LINE_POST_SPACE, 48 | } 49 | end 50 | ::NEXT_LINE:: 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /script/core/diagnostics/unbalanced-assignments.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local define = require 'proto.define' 3 | local lang = require 'language' 4 | local guide = require 'parser.guide' 5 | local await = require 'await' 6 | 7 | local types = { 8 | 'local', 9 | 'setlocal', 10 | 'setglobal', 11 | 'setfield', 12 | 'setindex' , 13 | } 14 | 15 | ---@async 16 | return function (uri, callback, code) 17 | local ast = files.getState(uri) 18 | if not ast then 19 | return 20 | end 21 | 22 | local last 23 | 24 | local function checkSet(source) 25 | if source.value then 26 | last = source 27 | else 28 | if not last then 29 | return 30 | end 31 | if last.start <= source.start 32 | and last.value.start >= source.finish then 33 | callback { 34 | start = source.start, 35 | finish = source.finish, 36 | message = lang.script('DIAG_UNBALANCED_ASSIGNMENTS') 37 | } 38 | else 39 | last = nil 40 | end 41 | end 42 | end 43 | 44 | ---@async 45 | guide.eachSourceTypes(ast.ast, types, function (source) 46 | await.delay() 47 | checkSet(source) 48 | end) 49 | end 50 | -------------------------------------------------------------------------------- /script/core/diagnostics/undefined-doc-class.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local lang = require 'language' 3 | local vm = require 'vm' 4 | 5 | return function (uri, callback) 6 | local state = files.getState(uri) 7 | if not state then 8 | return 9 | end 10 | 11 | if not state.ast.docs then 12 | return 13 | end 14 | 15 | local cache = {} 16 | 17 | for _, doc in ipairs(state.ast.docs) do 18 | if doc.type == 'doc.class' then 19 | if not doc.extends then 20 | goto CONTINUE 21 | end 22 | for _, ext in ipairs(doc.extends) do 23 | local name = ext.type == 'doc.extends.name' and ext[1] 24 | if name then 25 | local docs = vm.getDocSets(uri, name) 26 | if cache[name] == nil then 27 | cache[name] = false 28 | for _, otherDoc in ipairs(docs) do 29 | if otherDoc.type == 'doc.class' then 30 | cache[name] = true 31 | break 32 | end 33 | end 34 | end 35 | if not cache[name] then 36 | callback { 37 | start = ext.start, 38 | finish = ext.finish, 39 | related = cache, 40 | message = lang.script('DIAG_UNDEFINED_DOC_CLASS', name) 41 | } 42 | end 43 | end 44 | end 45 | end 46 | ::CONTINUE:: 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /script/core/diagnostics/undefined-doc-name.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local vm = require 'vm' 5 | 6 | return function (uri, callback) 7 | local state = files.getState(uri) 8 | if not state then 9 | return 10 | end 11 | 12 | if not state.ast.docs then 13 | return 14 | end 15 | 16 | guide.eachSource(state.ast.docs, function (source) 17 | if source.type ~= 'doc.extends.name' 18 | and source.type ~= 'doc.type.name' then 19 | return 20 | end 21 | if source.parent.type == 'doc.class' then 22 | return 23 | end 24 | local name = source[1] 25 | if name == '...' or name == '_' or name == 'self' then 26 | return 27 | end 28 | if #vm.getDocSets(uri, name) > 0 then 29 | return 30 | end 31 | callback { 32 | start = source.start, 33 | finish = source.finish, 34 | message = lang.script('DIAG_UNDEFINED_DOC_NAME', name) 35 | } 36 | end) 37 | end 38 | -------------------------------------------------------------------------------- /script/core/diagnostics/undefined-doc-param.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local lang = require 'language' 3 | 4 | return function (uri, callback) 5 | local state = files.getState(uri) 6 | if not state then 7 | return 8 | end 9 | 10 | if not state.ast.docs then 11 | return 12 | end 13 | 14 | for _, doc in ipairs(state.ast.docs) do 15 | if doc.type == 'doc.param' 16 | and not doc.bindSource then 17 | callback { 18 | start = doc.param.start, 19 | finish = doc.param.finish, 20 | message = lang.script('DIAG_UNDEFINED_DOC_PARAM', doc.param[1]) 21 | } 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /script/core/diagnostics/undefined-env-child.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local vm = require "vm.vm" 5 | 6 | ---@param source parser.object 7 | ---@return boolean 8 | local function isBindDoc(source) 9 | if not source.bindDocs then 10 | return false 11 | end 12 | for _, doc in ipairs(source.bindDocs) do 13 | if doc.type == 'doc.type' 14 | or doc.type == 'doc.class' then 15 | return true 16 | end 17 | end 18 | return false 19 | end 20 | 21 | return function (uri, callback) 22 | local state = files.getState(uri) 23 | if not state then 24 | return 25 | end 26 | 27 | guide.eachSourceType(state.ast, 'getglobal', function (source) 28 | if source.node.tag == '_ENV' then 29 | return 30 | end 31 | 32 | if not isBindDoc(source.node) then 33 | return 34 | end 35 | 36 | if #vm.getDefs(source) > 0 then 37 | return 38 | end 39 | 40 | local key = source[1] 41 | callback { 42 | start = source.start, 43 | finish = source.finish, 44 | message = lang.script('DIAG_UNDEF_ENV_CHILD', key), 45 | } 46 | end) 47 | end 48 | -------------------------------------------------------------------------------- /script/core/diagnostics/undefined-field.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local vm = require 'vm' 3 | local lang = require 'language' 4 | local guide = require 'parser.guide' 5 | local await = require 'await' 6 | 7 | local skipCheckClass = { 8 | ['unknown'] = true, 9 | ['any'] = true, 10 | ['table'] = true, 11 | } 12 | 13 | ---@async 14 | return function (uri, callback) 15 | local ast = files.getState(uri) 16 | if not ast then 17 | return 18 | end 19 | 20 | ---@async 21 | local function checkUndefinedField(src) 22 | await.delay() 23 | 24 | if #vm.getDefs(src) > 0 then 25 | return 26 | end 27 | local node = src.node 28 | if node then 29 | local ok 30 | for view in vm.getInfer(node):eachView(uri) do 31 | if skipCheckClass[view] then 32 | return 33 | end 34 | ok = true 35 | end 36 | if not ok then 37 | return 38 | end 39 | end 40 | local message = lang.script('DIAG_UNDEF_FIELD', guide.getKeyName(src)) 41 | if src.type == 'getfield' and src.field then 42 | callback { 43 | start = src.field.start, 44 | finish = src.field.finish, 45 | message = message, 46 | } 47 | elseif src.type == 'getmethod' and src.method then 48 | callback { 49 | start = src.method.start, 50 | finish = src.method.finish, 51 | message = message, 52 | } 53 | end 54 | end 55 | guide.eachSourceType(ast.ast, 'getfield', checkUndefinedField) 56 | guide.eachSourceType(ast.ast, 'getmethod', checkUndefinedField) 57 | end 58 | -------------------------------------------------------------------------------- /script/core/diagnostics/undefined-global.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local vm = require 'vm' 3 | local lang = require 'language' 4 | local config = require 'config' 5 | local guide = require 'parser.guide' 6 | local await = require 'await' 7 | local util = require 'utility' 8 | 9 | local requireLike = { 10 | ['include'] = true, 11 | ['import'] = true, 12 | ['require'] = true, 13 | ['load'] = true, 14 | } 15 | 16 | ---@async 17 | return function (uri, callback) 18 | local state = files.getState(uri) 19 | if not state then 20 | return 21 | end 22 | 23 | -- 遍历全局变量,检查所有没有 set 模式的全局变量 24 | guide.eachSourceType(state.ast, 'getglobal', function (src) ---@async 25 | if vm.isUndefinedGlobal(src) then 26 | local key = src[1] 27 | local message = lang.script('DIAG_UNDEF_GLOBAL', key) 28 | if requireLike[key:lower()] then 29 | message = ('%s(%s)'):format(message, lang.script('DIAG_REQUIRE_LIKE', key)) 30 | end 31 | 32 | callback { 33 | start = src.start, 34 | finish = src.finish, 35 | message = message, 36 | undefinedGlobal = src[1] 37 | } 38 | end 39 | end) 40 | end 41 | -------------------------------------------------------------------------------- /script/core/diagnostics/unknown-cast-variable.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local vm = require 'vm' 5 | local await = require 'await' 6 | 7 | ---@async 8 | return function (uri, callback) 9 | local state = files.getState(uri) 10 | if not state then 11 | return 12 | end 13 | 14 | if not state.ast.docs then 15 | return 16 | end 17 | 18 | for _, doc in ipairs(state.ast.docs) do 19 | if doc.type == 'doc.cast' and doc.name then 20 | await.delay() 21 | local defs = vm.getDefs(doc.name) 22 | local loc = defs[1] 23 | if not loc then 24 | callback { 25 | start = doc.name.start, 26 | finish = doc.name.finish, 27 | message = lang.script('DIAG_UNKNOWN_CAST_VARIABLE', doc.name[1]) 28 | } 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /script/core/diagnostics/unknown-diag-code.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local lang = require 'language' 3 | local diag = require 'proto.diagnostic' 4 | 5 | return function (uri, callback) 6 | local state = files.getState(uri) 7 | if not state then 8 | return 9 | end 10 | 11 | if not state.ast.docs then 12 | return 13 | end 14 | 15 | for _, doc in ipairs(state.ast.docs) do 16 | if doc.type == 'doc.diagnostic' then 17 | if doc.names then 18 | for _, nameUnit in ipairs(doc.names) do 19 | local code = nameUnit[1] 20 | if not diag.getDiagAndErrNameMap()[code] then 21 | callback { 22 | start = nameUnit.start, 23 | finish = nameUnit.finish, 24 | message = lang.script('DIAG_UNKNOWN_DIAG_CODE', code), 25 | } 26 | end 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /script/core/diagnostics/unknown-operator.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local lang = require 'language' 4 | local vm = require 'vm' 5 | local await = require 'await' 6 | local util = require 'utility' 7 | 8 | ---@async 9 | return function (uri, callback) 10 | local state = files.getState(uri) 11 | if not state then 12 | return 13 | end 14 | 15 | if not state.ast.docs then 16 | return 17 | end 18 | 19 | for _, doc in ipairs(state.ast.docs) do 20 | if doc.type == 'doc.operator' then 21 | local op = doc.op 22 | if op then 23 | local opName = op[1] 24 | if not vm.OP_BINARY_MAP[opName] 25 | and not vm.OP_UNARY_MAP[opName] 26 | and not vm.OP_OTHER_MAP[opName] then 27 | callback { 28 | start = doc.op.start, 29 | finish = doc.op.finish, 30 | message = lang.script('DIAG_UNKNOWN_OPERATOR', opName) 31 | } 32 | end 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /script/core/diagnostics/unused-label.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local define = require 'proto.define' 4 | local lang = require 'language' 5 | 6 | return function (uri, callback) 7 | local ast = files.getState(uri) 8 | if not ast then 9 | return 10 | end 11 | 12 | guide.eachSourceType(ast.ast, 'label', function (source) 13 | if not source.ref then 14 | callback { 15 | start = source.start, 16 | finish = source.finish, 17 | tags = { define.DiagnosticTag.Unnecessary }, 18 | message = lang.script('DIAG_UNUSED_LABEL', source[1]), 19 | } 20 | end 21 | end) 22 | end 23 | -------------------------------------------------------------------------------- /script/core/diagnostics/unused-vararg.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local define = require 'proto.define' 4 | local lang = require 'language' 5 | local vm = require 'vm' 6 | 7 | return function (uri, callback) 8 | local ast = files.getState(uri) 9 | if not ast then 10 | return 11 | end 12 | 13 | if vm.isMetaFile(uri) then 14 | return 15 | end 16 | 17 | guide.eachSourceType(ast.ast, 'function', function (source) 18 | if #source == 0 then 19 | return 20 | end 21 | local args = source.args 22 | if not args then 23 | return 24 | end 25 | 26 | for _, arg in ipairs(args) do 27 | if arg.type == '...' then 28 | if not arg.ref then 29 | callback { 30 | start = arg.start, 31 | finish = arg.finish, 32 | tags = { define.DiagnosticTag.Unnecessary }, 33 | message = lang.script.DIAG_UNUSED_VARARG, 34 | } 35 | end 36 | end 37 | end 38 | end) 39 | end 40 | -------------------------------------------------------------------------------- /script/core/find-source.lua: -------------------------------------------------------------------------------- 1 | local guide = require 'parser.guide' 2 | 3 | local function isValidFunctionPos(source, offset) 4 | for i = 1, #source.keyword // 2 do 5 | local start = source.keyword[i * 2 - 1] 6 | local finish = source.keyword[i * 2] 7 | if offset >= start and offset <= finish then 8 | return true 9 | end 10 | end 11 | return false 12 | end 13 | 14 | return function (state, position, accept) 15 | local len = math.huge 16 | local result 17 | guide.eachSourceContain(state.ast, position, function (source) 18 | if source.type == 'function' then 19 | if not isValidFunctionPos(source, position) then 20 | return 21 | end 22 | end 23 | local start, finish = guide.getStartFinish(source) 24 | if finish - start <= len and accept[source.type] then 25 | result = source 26 | len = finish - start 27 | end 28 | end) 29 | return result 30 | end 31 | -------------------------------------------------------------------------------- /script/core/formatting.lua: -------------------------------------------------------------------------------- 1 | local files = require("files") 2 | local log = require("log") 3 | 4 | return function(uri, options) 5 | local suc, codeFormat = pcall(require, "code_format") 6 | if not suc then 7 | return 8 | end 9 | local text = files.getOriginText(uri) 10 | local state = files.getState(uri) 11 | if not state then 12 | return 13 | end 14 | local status, formattedText = codeFormat.format(uri, text, options) 15 | 16 | if not status then 17 | if formattedText ~= nil then 18 | log.error(formattedText) 19 | end 20 | 21 | return 22 | end 23 | 24 | return { 25 | { 26 | start = state.ast.start, 27 | finish = state.ast.finish, 28 | text = formattedText, 29 | } 30 | } 31 | end 32 | -------------------------------------------------------------------------------- /script/core/rangeformatting.lua: -------------------------------------------------------------------------------- 1 | local files = require("files") 2 | local log = require("log") 3 | local converter = require("proto.converter") 4 | 5 | return function(uri, range, options) 6 | local state = files.getState(uri) 7 | if not state then 8 | return 9 | end 10 | local suc, codeFormat = pcall(require, "code_format") 11 | if not suc then 12 | return 13 | end 14 | local text = state.originText 15 | local status, formattedText, startLine, endLine = codeFormat.range_format( 16 | uri, text, range.start.line, range["end"].line, options) 17 | 18 | if not status then 19 | if formattedText ~= nil then 20 | log.error(formattedText) 21 | end 22 | 23 | return 24 | end 25 | 26 | return { 27 | { 28 | start = converter.unpackPosition(state, { line = startLine, character = 0 }), 29 | finish = converter.unpackPosition(state, { line = endLine + 1, character = 0 }), 30 | text = formattedText, 31 | } 32 | } 33 | end 34 | -------------------------------------------------------------------------------- /script/core/substring.lua: -------------------------------------------------------------------------------- 1 | local guide = require 'parser.guide' 2 | 3 | return function (state) 4 | ---@param pos1 parser.position 5 | ---@param pos2 parser.position 6 | ---@return string 7 | return function (pos1, pos2) 8 | return state.lua:sub( 9 | guide.positionToOffset(state, pos1), 10 | guide.positionToOffset(state, pos2) 11 | ) 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /script/core/view/psi-select.lua: -------------------------------------------------------------------------------- 1 | local files = require("files") 2 | local guide = require("parser.guide") 3 | local converter = require("proto.converter") 4 | 5 | return function(uri, position) 6 | local state = files.getState(uri) 7 | if not state then 8 | return 9 | end 10 | 11 | local pos = converter.unpackPosition(state, position) 12 | return { data = guide.positionToOffset(state, pos) } 13 | end 14 | -------------------------------------------------------------------------------- /script/encoder/ansi.lua: -------------------------------------------------------------------------------- 1 | local platform = require 'bee.platform' 2 | local windows 3 | 4 | if platform.os == 'windows' then 5 | windows = require 'bee.windows' 6 | end 7 | 8 | local m = {} 9 | 10 | function m.toutf8(text) 11 | if not windows then 12 | return text 13 | end 14 | return windows.a2u(text) 15 | end 16 | 17 | function m.fromutf8(text) 18 | if not windows then 19 | return text 20 | end 21 | return windows.u2a(text) 22 | end 23 | 24 | return m 25 | -------------------------------------------------------------------------------- /script/glob/init.lua: -------------------------------------------------------------------------------- 1 | return { 2 | glob = require 'glob.glob', 3 | gitignore = require 'glob.gitignore', 4 | } 5 | -------------------------------------------------------------------------------- /script/jsonrpc.lua: -------------------------------------------------------------------------------- 1 | local json = require 'json' 2 | local inspect = require 'inspect' 3 | local pcall = pcall 4 | local tonumber = tonumber 5 | 6 | ---@class jsonrpc 7 | local m = {} 8 | m.type = 'jsonrpc' 9 | 10 | function m.encode(pack) 11 | pack.jsonrpc = '2.0' 12 | local content = json.encode(pack) 13 | local buf = ('Content-Length: %d\r\n\r\n%s'):format(#content, content) 14 | return buf 15 | end 16 | 17 | ---@param reader fun(arg: integer):string 18 | local function readProtoHead(reader) 19 | local head = {} 20 | local line = '' 21 | while true do 22 | local char = reader(1) 23 | if char == nil then 24 | -- 说明管道已经关闭了 25 | return nil, 'Disconnected!' 26 | end 27 | line = line .. char 28 | if line == '\r\n' then 29 | break 30 | end 31 | if line:sub(-2) ~= '\r\n' then 32 | goto continue 33 | end 34 | local k, v = line:match '^([^:]+)%s*%:%s*(.+)\r\n$' 35 | if not k then 36 | return nil, 'Proto header error: ' .. line 37 | end 38 | if k == 'Content-Length' then 39 | v = tonumber(v) 40 | end 41 | head[k] = v 42 | line = '' 43 | ::continue:: 44 | end 45 | return head 46 | end 47 | 48 | ---@param reader fun(arg: integer):string 49 | function m.decode(reader) 50 | local head, err = readProtoHead(reader) 51 | if not head then 52 | return nil, err 53 | end 54 | local len = head['Content-Length'] 55 | if not len then 56 | return nil, 'Proto header error: ' .. inspect(head) 57 | end 58 | local content = reader(len) 59 | if not content then 60 | return nil, 'Proto read error' 61 | end 62 | ---@type any 63 | local null = json.null 64 | json.null = nil 65 | local suc, res = pcall(json.decode, content) 66 | json.null = null 67 | if not suc then 68 | return nil, 'Proto parse error: ' .. res 69 | end 70 | return res 71 | end 72 | 73 | return m 74 | -------------------------------------------------------------------------------- /script/locale-loader.lua: -------------------------------------------------------------------------------- 1 | local function mergeKey(key, k) 2 | if not key then 3 | return k 4 | end 5 | if k:sub(1, 1):match '%w' then 6 | return key .. '.' .. k 7 | else 8 | return key .. k 9 | end 10 | end 11 | 12 | local function proxy(results, key) 13 | return setmetatable({}, { 14 | __index = function (_, k) 15 | return proxy(results, mergeKey(key, k)) 16 | end, 17 | __newindex = function (_, k, v) 18 | results[mergeKey(key, k)] = v 19 | end 20 | }) 21 | end 22 | 23 | return function (text, path, results) 24 | results = results or {} 25 | assert(load(text, '@' .. path, "t", proxy(results)))() 26 | return results 27 | end 28 | -------------------------------------------------------------------------------- /script/luacheck/check_state.lua: -------------------------------------------------------------------------------- 1 | local utils = require "luacheck.utils" 2 | 3 | local check_state = {} 4 | 5 | local CheckState = utils.class() 6 | 7 | function CheckState:__init(source_bytes) 8 | self.source_bytes = source_bytes 9 | self.warnings = {} 10 | end 11 | 12 | -- Returns column of a character in a line given its offset. 13 | -- The column is never larger than the line length. 14 | -- This can be called if line length is not yet known. 15 | function CheckState:offset_to_column(line, offset) 16 | local line_length = self.line_lengths[line] 17 | local column = offset - self.line_offsets[line] + 1 18 | 19 | if not line_length then 20 | return column 21 | end 22 | 23 | return math.max(1, math.min(line_length, column)) 24 | end 25 | 26 | function CheckState:warn_column_range(code, range, warning) 27 | warning = warning or {} 28 | warning.code = code 29 | warning.line = range.line 30 | warning.column = range.column 31 | warning.end_column = range.end_column 32 | table.insert(self.warnings, warning) 33 | return warning 34 | end 35 | 36 | function CheckState:warn(code, line, offset, end_offset, warning) 37 | warning = warning or {} 38 | warning.code = code 39 | warning.line = line 40 | warning.column = self:offset_to_column(line, offset) 41 | warning.end_column = self:offset_to_column(line, end_offset) 42 | table.insert(self.warnings, warning) 43 | return warning 44 | end 45 | 46 | function CheckState:warn_range(code, range, warning) 47 | return self:warn(code, range.line, range.offset, range.end_offset, warning) 48 | end 49 | 50 | function CheckState:warn_var(code, var, warning) 51 | warning = self:warn_range(code, var.node, warning) 52 | warning.name = var.name 53 | return warning 54 | end 55 | 56 | function CheckState:warn_value(code, value, warning) 57 | warning = self:warn_range(code, value.var_node, warning) 58 | warning.name = value.var.name 59 | return warning 60 | end 61 | 62 | function check_state.new(source_bytes) 63 | return CheckState(source_bytes) 64 | end 65 | 66 | return check_state 67 | -------------------------------------------------------------------------------- /script/luacheck/stages/detect_empty_blocks.lua: -------------------------------------------------------------------------------- 1 | local core_utils = require "luacheck.core_utils" 2 | 3 | local stage = {} 4 | 5 | stage.warnings = { 6 | ["541"] = {message_format = "empty do..end block", fields = {}}, 7 | ["542"] = {message_format = "empty if branch", fields = {}} 8 | } 9 | 10 | local function check_block(chstate, block, code) 11 | if #block == 0 then 12 | chstate:warn_range(code, block) 13 | end 14 | end 15 | 16 | 17 | local function check_node(chstate, node) 18 | if node.tag == "Do" then 19 | check_block(chstate, node, "541") 20 | return 21 | end 22 | 23 | for index = 2, #node, 2 do 24 | check_block(chstate, node[index], "542") 25 | end 26 | 27 | if #node % 2 == 1 then 28 | check_block(chstate, node[#node], "542") 29 | end 30 | end 31 | 32 | function stage.run(chstate) 33 | core_utils.each_statement(chstate, {"Do", "If"}, check_node) 34 | end 35 | 36 | return stage 37 | -------------------------------------------------------------------------------- /script/luacheck/stages/detect_empty_statements.lua: -------------------------------------------------------------------------------- 1 | local stage = {} 2 | 3 | stage.warnings = { 4 | ["551"] = {message_format = "empty statement", fields = {}} 5 | } 6 | 7 | function stage.run(chstate) 8 | for _, range in ipairs(chstate.useless_semicolons) do 9 | chstate:warn_range("551", range) 10 | end 11 | end 12 | 13 | return stage 14 | -------------------------------------------------------------------------------- /script/luacheck/stages/detect_reversed_fornum_loops.lua: -------------------------------------------------------------------------------- 1 | local core_utils = require "luacheck.core_utils" 2 | 3 | local stage = {} 4 | 5 | stage.warnings = { 6 | ["571"] = {message_format = "numeric for loop goes from #(expr) down to {limit} but loop step is not negative", 7 | fields = {"limit"}} 8 | } 9 | 10 | local function check_fornum(chstate, node) 11 | if node[2].tag ~= "Op" or node[2][1] ~= "len" then 12 | return 13 | end 14 | 15 | local limit, limit_repr = core_utils.eval_const_node(node[3]) 16 | 17 | if not limit or limit > 1 then 18 | return 19 | end 20 | 21 | local step = 1 22 | 23 | if node[5] then 24 | step = core_utils.eval_const_node(node[4]) 25 | end 26 | 27 | if step and step >= 0 then 28 | chstate:warn_range("571", node, { 29 | limit = limit_repr 30 | }) 31 | end 32 | end 33 | 34 | -- Warns about loops trying to go from `#(expr)` to `1` with positive step. 35 | function stage.run(chstate) 36 | core_utils.each_statement(chstate, {"Fornum"}, check_fornum) 37 | end 38 | 39 | return stage 40 | -------------------------------------------------------------------------------- /script/luacheck/stages/detect_unbalanced_assignments.lua: -------------------------------------------------------------------------------- 1 | local core_utils = require "luacheck.core_utils" 2 | 3 | local stage = {} 4 | 5 | stage.warnings = { 6 | ["531"] = {message_format = "right side of assignment has more values than left side expects", fields = {}}, 7 | ["532"] = {message_format = "right side of assignment has less values than left side expects", fields = {}} 8 | } 9 | 10 | local function is_unpacking(node) 11 | return node.tag == "Dots" or node.tag == "Call" or node.tag == "Invoke" 12 | end 13 | 14 | local function check_assignment(chstate, node) 15 | local rhs = node[2] 16 | 17 | if not rhs then 18 | return 19 | end 20 | 21 | local lhs = node[1] 22 | 23 | if #rhs > #lhs then 24 | chstate:warn_range("531", node) 25 | elseif #rhs < #lhs and node.tag == "Set" and not is_unpacking(rhs[#rhs]) then 26 | chstate:warn_range("532", node) 27 | end 28 | end 29 | 30 | function stage.run(chstate) 31 | core_utils.each_statement(chstate, {"Set", "Local"}, check_assignment) 32 | end 33 | 34 | return stage 35 | -------------------------------------------------------------------------------- /script/luacheck/stages/detect_unreachable_code.lua: -------------------------------------------------------------------------------- 1 | local stage = {} 2 | 3 | stage.warnings = { 4 | ["511"] = {message_format = "unreachable code", fields = {}}, 5 | ["512"] = {message_format = "loop is executed at most once", fields = {}} 6 | } 7 | 8 | local function noop_callback() end 9 | 10 | local function detect_unreachable_code(chstate, line) 11 | local reachable_indexes = {} 12 | 13 | -- Mark all items reachable from the function start. 14 | line:walk(reachable_indexes, 1, noop_callback) 15 | 16 | -- All remaining items are unreachable. 17 | -- However, there is no point in reporting all of them. 18 | -- Only report those that are not reachable from any already reported ones. 19 | for item_index, item in ipairs(line.items) do 20 | if not reachable_indexes[item_index] then 21 | if item.node then 22 | chstate:warn_range(item.loop_end and "512" or "511", item.node) 23 | -- Mark all items reachable from the item just reported. 24 | line:walk(reachable_indexes, item_index, noop_callback) 25 | end 26 | end 27 | end 28 | end 29 | 30 | function stage.run(chstate) 31 | for _, line in ipairs(chstate.lines) do 32 | detect_unreachable_code(chstate, line) 33 | end 34 | end 35 | 36 | return stage 37 | -------------------------------------------------------------------------------- /script/luacheck/stages/parse.lua: -------------------------------------------------------------------------------- 1 | local decoder = require "luacheck.decoder" 2 | local parser = require "luacheck.parser" 3 | 4 | local stage = {} 5 | 6 | function stage.run(chstate) 7 | chstate.source = decoder.decode(chstate.source_bytes) 8 | chstate.line_offsets = {} 9 | chstate.line_lengths = {} 10 | local ast, comments, code_lines, line_endings, useless_semicolons = parser.parse( 11 | chstate.source, chstate.line_offsets, chstate.line_lengths) 12 | chstate.ast = ast 13 | chstate.comments = comments 14 | chstate.code_lines = code_lines 15 | chstate.line_endings = line_endings 16 | chstate.useless_semicolons = useless_semicolons 17 | end 18 | 19 | return stage 20 | -------------------------------------------------------------------------------- /script/luacheck/stages/unwrap_parens.lua: -------------------------------------------------------------------------------- 1 | local stage = {} 2 | 3 | -- Mutates an array of nodes and non-tables, unwrapping Paren nodes. 4 | -- If list_start is given, tail Paren is not unwrapped if it's unpacking and past list_start index. 5 | local function handle_nodes(nodes, list_start) 6 | local num_nodes = #nodes 7 | 8 | for index = 1, num_nodes do 9 | local node = nodes[index] 10 | 11 | if type(node) == "table" then 12 | local tag = node.tag 13 | 14 | if tag == "Table" or tag == "Return" then 15 | handle_nodes(node, 1) 16 | elseif tag == "Call" then 17 | handle_nodes(node, 2) 18 | elseif tag == "Invoke" then 19 | handle_nodes(node, 3) 20 | elseif tag == "Forin" then 21 | handle_nodes(node[2], 1) 22 | handle_nodes(node[3]) 23 | elseif tag == "Local" then 24 | if node[2] then 25 | handle_nodes(node[2]) 26 | end 27 | elseif tag == "Set" then 28 | handle_nodes(node[1]) 29 | handle_nodes(node[2], 1) 30 | else 31 | handle_nodes(node) 32 | 33 | if tag == "Paren" and (not list_start or index < list_start or index ~= num_nodes) then 34 | local inner_node = node[1] 35 | 36 | if inner_node.tag ~= "Call" and inner_node.tag ~= "Invoke" and inner_node.tag ~= "Dots" then 37 | nodes[index] = inner_node 38 | end 39 | end 40 | end 41 | end 42 | end 43 | end 44 | 45 | -- Mutates AST, unwrapping Paren nodes. 46 | -- Paren nodes are preserved only when they matter: 47 | -- at the ends of expression lists with potentially multi-value inner expressions. 48 | function stage.run(chstate) 49 | handle_nodes(chstate.ast) 50 | end 51 | 52 | return stage 53 | -------------------------------------------------------------------------------- /script/luacheck/unicode.lua: -------------------------------------------------------------------------------- 1 | local unicode_printability_boundaries = require "luacheck.unicode_printability_boundaries" 2 | 3 | local unicode = {} 4 | 5 | -- unicode_printability_boundaries is an array of first codepoints of 6 | -- each continuous block of codepoints that are all printable or all not printable. 7 | 8 | function unicode.is_printable(codepoint) 9 | -- Binary search for index of the first boundary less than or equal to given codepoint. 10 | local floor_boundary_index 11 | 12 | -- Target index is always in [begin_index..end_index). 13 | local begin_index = 1 14 | local end_index = #unicode_printability_boundaries + 1 15 | 16 | while end_index - begin_index > 1 do 17 | local mid_index = math.floor((begin_index + end_index) / 2) 18 | local mid_codepoint = unicode_printability_boundaries[mid_index] 19 | 20 | if codepoint < mid_codepoint then 21 | end_index = mid_index 22 | elseif codepoint > mid_codepoint then 23 | begin_index = mid_index 24 | else 25 | floor_boundary_index = mid_index 26 | break 27 | end 28 | end 29 | 30 | floor_boundary_index = floor_boundary_index or begin_index 31 | -- floor_boundary_index is the number of the block containing codepoint. 32 | -- Printable and not printable blocks alternate and the first one is not printable (zero is not printable). 33 | return floor_boundary_index % 2 == 0 34 | end 35 | 36 | return unicode 37 | -------------------------------------------------------------------------------- /script/luacheck/vendor/sha1/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Enrique García Cota, Eike Decker, Jeffrey Friedl 2 | Copyright (c) 2018 Peter Melnichenko 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a 5 | copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included 13 | in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /script/luacheck/vendor/sha1/bit32_ops.lua: -------------------------------------------------------------------------------- 1 | local bit32 = require "bit32" 2 | 3 | local ops = {} 4 | 5 | local band = bit32.band 6 | local bor = bit32.bor 7 | local bxor = bit32.bxor 8 | 9 | ops.uint32_lrot = bit32.lrotate 10 | ops.byte_xor = bxor 11 | ops.uint32_xor_3 = bxor 12 | ops.uint32_xor_4 = bxor 13 | 14 | function ops.uint32_ternary(a, b, c) 15 | -- c ~ (a & (b ~ c)) has less bitwise operations than (a & b) | (~a & c). 16 | return bxor(c, band(a, bxor(b, c))) 17 | end 18 | 19 | function ops.uint32_majority(a, b, c) 20 | -- (a & (b | c)) | (b & c) has less bitwise operations than (a & b) | (a & c) | (b & c). 21 | return bor(band(a, bor(b, c)), band(b, c)) 22 | end 23 | 24 | return ops 25 | -------------------------------------------------------------------------------- /script/luacheck/vendor/sha1/bit_ops.lua: -------------------------------------------------------------------------------- 1 | local bit = require "bit" 2 | 3 | local ops = {} 4 | 5 | local band = bit.band 6 | local bor = bit.bor 7 | local bxor = bit.bxor 8 | 9 | ops.uint32_lrot = bit.rol 10 | ops.byte_xor = bxor 11 | ops.uint32_xor_3 = bxor 12 | ops.uint32_xor_4 = bxor 13 | 14 | function ops.uint32_ternary(a, b, c) 15 | -- c ~ (a & (b ~ c)) has less bitwise operations than (a & b) | (~a & c). 16 | return bxor(c, band(a, bxor(b, c))) 17 | end 18 | 19 | function ops.uint32_majority(a, b, c) 20 | -- (a & (b | c)) | (b & c) has less bitwise operations than (a & b) | (a & c) | (b & c). 21 | return bor(band(a, bor(b, c)), band(b, c)) 22 | end 23 | 24 | return ops 25 | -------------------------------------------------------------------------------- /script/luacheck/vendor/sha1/common.lua: -------------------------------------------------------------------------------- 1 | local common = {} 2 | 3 | -- Merges four bytes into a uint32 number. 4 | function common.bytes_to_uint32(a, b, c, d) 5 | return a * 0x1000000 + b * 0x10000 + c * 0x100 + d 6 | end 7 | 8 | -- Splits a uint32 number into four bytes. 9 | function common.uint32_to_bytes(a) 10 | local a4 = a % 256 11 | a = (a - a4) / 256 12 | local a3 = a % 256 13 | a = (a - a3) / 256 14 | local a2 = a % 256 15 | local a1 = (a - a2) / 256 16 | return a1, a2, a3, a4 17 | end 18 | 19 | 20 | return common 21 | -------------------------------------------------------------------------------- /script/luacheck/vendor/sha1/lua53_ops.lua: -------------------------------------------------------------------------------- 1 | local ops = {} 2 | 3 | function ops.uint32_lrot(a, bits) 4 | return ((a << bits) & 0xFFFFFFFF) | (a >> (32 - bits)) 5 | end 6 | 7 | function ops.byte_xor(a, b) 8 | return a ~ b 9 | end 10 | 11 | function ops.uint32_xor_3(a, b, c) 12 | return a ~ b ~ c 13 | end 14 | 15 | function ops.uint32_xor_4(a, b, c, d) 16 | return a ~ b ~ c ~ d 17 | end 18 | 19 | function ops.uint32_ternary(a, b, c) 20 | -- c ~ (a & (b ~ c)) has less bitwise operations than (a & b) | (~a & c). 21 | return c ~ (a & (b ~ c)) 22 | end 23 | 24 | function ops.uint32_majority(a, b, c) 25 | -- (a & (b | c)) | (b & c) has less bitwise operations than (a & b) | (a & c) | (b & c). 26 | return (a & (b | c)) | (b & c) 27 | end 28 | 29 | return ops 30 | -------------------------------------------------------------------------------- /script/luacheck/version.lua: -------------------------------------------------------------------------------- 1 | local argparse = require "argparse" 2 | local lfs = require "lfs" 3 | local luacheck = require "luacheck" 4 | local multithreading = require "luacheck.multithreading" 5 | local utils = require "luacheck.utils" 6 | 7 | local version = {} 8 | 9 | version.luacheck = luacheck._VERSION 10 | 11 | if rawget(_G, "jit") then 12 | version.lua = rawget(_G, "jit").version 13 | elseif _VERSION:find("^Lua ") then 14 | version.lua = "PUC-Rio " .. _VERSION 15 | else 16 | version.lua = _VERSION 17 | end 18 | 19 | version.argparse = argparse.version 20 | 21 | version.lfs = utils.unprefix(lfs._VERSION, "LuaFileSystem ") 22 | 23 | if multithreading.has_lanes then 24 | version.lanes = multithreading.lanes.ABOUT.version 25 | else 26 | version.lanes = "Not found" 27 | end 28 | 29 | version.string = ([[ 30 | Luacheck: %s 31 | Lua: %s 32 | Argparse: %s 33 | LuaFileSystem: %s 34 | LuaLanes: %s]]):format(version.luacheck, version.lua, version.argparse, version.lfs, version.lanes) 35 | 36 | return version 37 | -------------------------------------------------------------------------------- /script/meta/bee/filewatch.lua: -------------------------------------------------------------------------------- 1 | ---@meta 2 | 3 | ---@class bee.filewatch.instance 4 | local instance = {} 5 | 6 | ---@param path string 7 | function instance:add(path) 8 | end 9 | 10 | ---@param enable boolean 11 | ---@return boolean 12 | function instance:set_recursive(enable) 13 | end 14 | 15 | ---@param enable boolean 16 | ---@return boolean 17 | function instance:set_follow_symlinks(enable) 18 | end 19 | 20 | ---@param callback? fun(path: string):boolean 21 | ---@return boolean 22 | function instance:set_filter(callback) 23 | end 24 | 25 | ---@class bee.filewatch 26 | local fw = {} 27 | 28 | ---@return bee.filewatch.instance 29 | function fw.create() 30 | end 31 | 32 | return fw 33 | -------------------------------------------------------------------------------- /script/meta/bee/socket.lua: -------------------------------------------------------------------------------- 1 | ---@meta 2 | 3 | ---@alias bee.socket.protocol 4 | ---| 'tcp' 5 | ---| 'udp' 6 | ---| 'unix' 7 | ---| 'tcp6' 8 | ---| 'udp6' 9 | 10 | ---@class bee.socket 11 | local socket = {} 12 | 13 | ---@param protocol bee.socket.protocol 14 | ---@return bee.socket.fd? 15 | ---@return string? 16 | function socket.create(protocol) end 17 | 18 | ---@param readfds? bee.socket.fd[] 19 | ---@param writefds? bee.socket.fd[] 20 | ---@param timeout number 21 | ---@return bee.socket.fd[] # readfds 22 | ---@return bee.socket.fd[] # writefds 23 | function socket.select(readfds, writefds, timeout) end 24 | 25 | ---@param handle lightuserdata 26 | ---@return bee.socket.fd 27 | function socket.fd(handle) end 28 | 29 | ---@return bee.socket.fd 30 | ---@return bee.socket.fd 31 | function socket.pair() end 32 | 33 | ---@class bee.socket.fd 34 | local fd = {} 35 | 36 | ---@param addr string 37 | ---@param port? integer 38 | ---@return boolean 39 | ---@return string? 40 | function fd:bind(addr, port) end 41 | 42 | function fd:close() end 43 | 44 | ---@return boolean 45 | ---@return string? 46 | function fd:listen() end 47 | 48 | ---@param addr string 49 | ---@param port integer 50 | ---@return boolean 51 | ---@return string? 52 | function fd:connect(addr, port) end 53 | 54 | ---@param len? integer 55 | ---@return string | false 56 | function fd:recv(len) end 57 | 58 | ---@param content string 59 | function fd:send(content) end 60 | 61 | ---@return lightuserdata 62 | function fd:handle() end 63 | 64 | ---@return lightuserdata 65 | function fd:detach() end 66 | 67 | ---@return boolean 68 | function fd:status() end 69 | 70 | ---@return bee.socket.fd 71 | function fd:accept() end 72 | 73 | return socket 74 | -------------------------------------------------------------------------------- /script/meta/bee/thread.lua: -------------------------------------------------------------------------------- 1 | ---@meta 2 | 3 | ---@class bee.thread 4 | local thread = {} 5 | 6 | ---@param time number 7 | function thread.sleep(time) end 8 | 9 | ---@param name string 10 | function thread.newchannel(name) end 11 | 12 | ---@param name string 13 | ---@return bee.thread.channel 14 | function thread.channel(name) end 15 | 16 | ---@param script string 17 | ---@return bee.thread.thread 18 | function thread.thread(script) end 19 | 20 | ---@class bee.thread.channel 21 | local channel = {} 22 | 23 | function channel:push(...) end 24 | 25 | ---@return ... 26 | function channel:pop() end 27 | 28 | ---@return ... 29 | function channel:bpop() end 30 | 31 | ---@class bee.thread.thread 32 | 33 | return thread 34 | -------------------------------------------------------------------------------- /script/parser/init.lua: -------------------------------------------------------------------------------- 1 | local api = { 2 | compile = require 'parser.compile', 3 | lines = require 'parser.lines', 4 | guide = require 'parser.guide', 5 | luadoc = require 'parser.luadoc'.luadoc, 6 | } 7 | 8 | return api 9 | -------------------------------------------------------------------------------- /script/parser/lines.lua: -------------------------------------------------------------------------------- 1 | local sfind = string.find 2 | local ssub = string.sub 3 | 4 | ---@param text string 5 | return function (text) 6 | local current = 1 7 | local lines = {} 8 | lines[0] = 1 9 | lines.size = #text 10 | local i = 0 11 | while true do 12 | local pos = sfind(text,'[\r\n]', current) 13 | if not pos then 14 | break 15 | end 16 | i = i + 1 17 | if ssub(text, pos, pos + 1) == '\r\n' then 18 | current = pos + 2 19 | else 20 | current = pos + 1 21 | end 22 | lines[i] = current 23 | end 24 | return lines 25 | end 26 | -------------------------------------------------------------------------------- /script/parser/tokens.lua: -------------------------------------------------------------------------------- 1 | local m = require 'lpeglabel' 2 | 3 | local Sp = m.S' \t\v\f' 4 | local Nl = m.P'\r\n' + m.S'\r\n' 5 | local Number = m.R'09'^1 6 | local Word = m.R('AZ', 'az', '__', '\x80\xff') * m.R('AZ', 'az', '09', '__', '\x80\xff')^0 7 | local Symbol = m.P'==' 8 | + m.P'~=' 9 | + m.P'--' 10 | -- non-standard: 11 | + m.P'<<=' 12 | + m.P'>>=' 13 | + m.P'//=' 14 | -- end non-standard 15 | + m.P'<<' 16 | + m.P'>>' 17 | + m.P'<=' 18 | + m.P'>=' 19 | + m.P'//' 20 | + m.P'...' 21 | + m.P'..' 22 | + m.P'::' 23 | -- non-standard: 24 | + m.P'!=' 25 | + m.P'&&' 26 | + m.P'||' 27 | + m.P'/*' 28 | + m.P'*/' 29 | + m.P'+=' 30 | + m.P'-=' 31 | + m.P'*=' 32 | + m.P'%=' 33 | + m.P'&=' 34 | + m.P'|=' 35 | + m.P'^=' 36 | + m.P'/=' 37 | -- end non-standard 38 | -- singles 39 | + m.S'+-*/!#%^&()={}[]|\\\'":;<>,.?~`' 40 | local Unknown = (1 - Number - Word - Symbol - Sp - Nl)^1 41 | local Token = m.Cp() * m.C(Nl + Number + Word + Symbol + Unknown) 42 | 43 | local Parser = m.Ct((Sp^1 + Token)^0) 44 | 45 | return function (lua) 46 | local results = Parser:match(lua) 47 | return results 48 | end 49 | -------------------------------------------------------------------------------- /script/plugins/ffi/c-parser/cdriver.lua: -------------------------------------------------------------------------------- 1 | local cdriver = {} 2 | 3 | local cpp = require("plugins.ffi.c-parser.cpp") 4 | local c99 = require("plugins.ffi.c-parser.c99") 5 | local ctypes = require("plugins.ffi.c-parser.ctypes") 6 | local cdefines = require("plugins.ffi.c-parser.cdefines") 7 | 8 | function cdriver.process_file(filename) 9 | local ctx, err = cpp.parse_file(filename) 10 | if not ctx then 11 | return nil, "failed preprocessing '"..filename.."': " .. err 12 | end 13 | 14 | local srccode = table.concat(ctx.output, "\n").." $EOF$" 15 | 16 | local res, err, line, col, fragment = c99.match_language_grammar(srccode) 17 | if not res then 18 | return nil, ("failed parsing: %s:%d:%d: %s\n%s"):format(filename, line, col, err, fragment) 19 | end 20 | 21 | local ffi_types, err = ctypes.register_types(res) 22 | if not ffi_types then 23 | return nil, err 24 | end 25 | 26 | cdefines.register_defines(ffi_types, ctx.defines) 27 | 28 | return ffi_types 29 | end 30 | 31 | function cdriver.process_context(context) 32 | local ctx, err = cpp.parse_context(context) 33 | if not ctx then 34 | return nil, "failed preprocessing '"..context.."': " .. err 35 | end 36 | 37 | local srccode = table.concat(ctx.output, "\n").." $EOF$" 38 | 39 | local res, err, line, col, fragment = c99.match_language_grammar(srccode) 40 | if not res then 41 | return nil, ("failed parsing: %s:%d:%d: %s\n%s"):format(context, line, col, err, fragment) 42 | end 43 | 44 | local ffi_types, err = ctypes.register_types(res) 45 | if not ffi_types then 46 | return nil, err 47 | end 48 | 49 | cdefines.register_defines(ffi_types, ctx.defines) 50 | 51 | return ffi_types 52 | end 53 | 54 | return cdriver 55 | -------------------------------------------------------------------------------- /script/plugins/ffi/c-parser/util.lua: -------------------------------------------------------------------------------- 1 | local m = {} 2 | 3 | local function tableLenEqual(t, len) 4 | for key, value in pairs(t) do 5 | len = len - 1 6 | if len < 0 then 7 | return false 8 | end 9 | end 10 | return true 11 | end 12 | 13 | local function isSingleNode(ast) 14 | if type(ast) ~= 'table' then 15 | return false 16 | end 17 | local len = #ast 18 | return len == 1 and tableLenEqual(ast, len) 19 | end 20 | 21 | function m.expandSingle(ast) 22 | if isSingleNode(ast) then 23 | return ast[1] 24 | end 25 | return ast 26 | end 27 | 28 | return m 29 | -------------------------------------------------------------------------------- /script/plugins/ffi/cdefRerence.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local guide = require 'parser.guide' 3 | local vm = require 'vm' 4 | local reference = require 'core.reference' 5 | local find = string.find 6 | local remove = table.remove 7 | 8 | local function getCdefSourcePosition(ffi_state) 9 | local cdef_position = ffi_state.ast.returns[1][1] 10 | local source = vm.getFields(cdef_position) 11 | for index, value in ipairs(source) do 12 | local name = guide.getKeyName(value) 13 | if name == 'cdef' then 14 | return value.field.start 15 | end 16 | end 17 | end 18 | 19 | ---@async 20 | return function () 21 | local ffi_state 22 | for uri in files.eachFile() do 23 | if find(uri, "ffi.lua", 0, true) and find(uri, "meta", 0, true) then 24 | ffi_state = files.getState(uri) 25 | break 26 | end 27 | end 28 | if ffi_state then 29 | local res = reference(ffi_state.uri, getCdefSourcePosition(ffi_state), true) 30 | if res then 31 | if res[1].uri == ffi_state.uri then 32 | remove(res, 1) 33 | end 34 | return res 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /script/plugins/ffi/searchCode.lua: -------------------------------------------------------------------------------- 1 | local vm = require 'vm' 2 | 3 | local function getLiterals(arg) 4 | local literals = vm.getLiterals(arg) 5 | local res = {} 6 | if not literals then 7 | return res 8 | end 9 | for k, v in pairs(literals) do 10 | if type(k) == 'string' then 11 | res[#res+1] = k 12 | end 13 | end 14 | return res 15 | end 16 | 17 | ---@return string[]? 18 | local function getCode(CdefReference) 19 | local target = CdefReference.target 20 | if not (target.type == 'field' and target.parent.type == 'getfield') then 21 | return 22 | end 23 | target = target.parent.parent 24 | if target.type == 'call' then 25 | return getLiterals(target.args and target.args[1]) 26 | elseif target.type == 'local' then 27 | local res = {} 28 | for _, o in ipairs(target.ref) do 29 | if o.parent.type ~= 'call' then 30 | goto CONTINUE 31 | end 32 | local target = o.parent 33 | local literals = vm.getLiterals(target.args and target.args[1]) 34 | if not literals then 35 | goto CONTINUE 36 | end 37 | for k, v in pairs(literals) do 38 | if type(k) == 'string' then 39 | res[#res+1] = k 40 | end 41 | end 42 | ::CONTINUE:: 43 | end 44 | return res 45 | end 46 | end 47 | 48 | ---@async 49 | return function (CdefReference, target_uri) 50 | if not CdefReference then 51 | return nil 52 | end 53 | local codeResults 54 | for i, v in ipairs(CdefReference) do 55 | if v.uri ~= target_uri then 56 | goto continue 57 | end 58 | local codes = getCode(v) 59 | if not codes then 60 | goto continue 61 | end 62 | for i, v in ipairs(codes) do 63 | codeResults = codeResults or {} 64 | codeResults[#codeResults+1] = v 65 | end 66 | ::continue:: 67 | end 68 | return codeResults 69 | end 70 | -------------------------------------------------------------------------------- /script/plugins/init.lua: -------------------------------------------------------------------------------- 1 | require 'plugins.ffi' -------------------------------------------------------------------------------- /script/proto/init.lua: -------------------------------------------------------------------------------- 1 | local proto = require 'proto.proto' 2 | 3 | return proto 4 | -------------------------------------------------------------------------------- /script/provider/code-lens.lua: -------------------------------------------------------------------------------- 1 | local proto = require 'proto' 2 | local client = require 'client' 3 | local json = require 'json' 4 | local config = require 'config' 5 | 6 | local function refresh() 7 | if not client.isReady() then 8 | return 9 | end 10 | if not client.getAbility 'workspace.codeLens.refreshSupport' then 11 | return 12 | end 13 | log.debug('Refresh codeLens.') 14 | proto.request('workspace/codeLens/refresh', json.null) 15 | end 16 | 17 | config.watch(function (uri, key, value, oldValue) 18 | if key == '' then 19 | refresh() 20 | end 21 | if key:find '^Lua.runtime' 22 | or key:find '^Lua.workspace' 23 | or key:find '^Lua.codeLens' 24 | or key:find '^files' then 25 | refresh() 26 | end 27 | end) 28 | 29 | return {} 30 | -------------------------------------------------------------------------------- /script/provider/init.lua: -------------------------------------------------------------------------------- 1 | require 'provider.diagnostic' 2 | return require 'provider.provider' 3 | -------------------------------------------------------------------------------- /script/provider/inlay-hint.lua: -------------------------------------------------------------------------------- 1 | local proto = require 'proto' 2 | local client = require 'client' 3 | local json = require "json" 4 | local config = require 'config' 5 | 6 | local function refresh() 7 | if not client.isReady() then 8 | return 9 | end 10 | if not client.getAbility 'workspace.inlayHint.refreshSupport' then 11 | return 12 | end 13 | log.debug('Refresh inlay hints.') 14 | proto.request('workspace/inlayHint/refresh', json.null) 15 | end 16 | 17 | config.watch(function (uri, key, value, oldValue) 18 | if key == '' then 19 | refresh() 20 | end 21 | if key:find '^Lua.runtime' 22 | or key:find '^Lua.workspace' 23 | or key:find '^Lua.hint' 24 | or key:find '^files' then 25 | refresh() 26 | end 27 | end) 28 | 29 | return {} 30 | -------------------------------------------------------------------------------- /script/provider/name-style.lua: -------------------------------------------------------------------------------- 1 | local suc, codeFormat = pcall(require, 'code_format') 2 | if not suc then 3 | return 4 | end 5 | 6 | local config = require 'config' 7 | 8 | local m = {} 9 | 10 | m.loaded = false 11 | 12 | function m.nameStyleCheck(uri, text) 13 | if not m.loaded then 14 | local value = config.get(nil, "Lua.nameStyle.config") 15 | codeFormat.update_name_style_config(value) 16 | m.loaded = true 17 | end 18 | 19 | return codeFormat.name_style_analysis(uri, text) 20 | end 21 | 22 | config.watch(function (uri, key, value) 23 | if key == "Lua.nameStyle.config" then 24 | codeFormat.update_name_style_config(value) 25 | end 26 | end) 27 | 28 | return m 29 | -------------------------------------------------------------------------------- /script/provider/semantic-tokens.lua: -------------------------------------------------------------------------------- 1 | local proto = require 'proto' 2 | local client = require 'client' 3 | local json = require "json" 4 | local config = require 'config' 5 | 6 | local function refresh() 7 | if not client.isReady() then 8 | return 9 | end 10 | if not client.getAbility 'workspace.semanticTokens.refreshSupport' then 11 | return 12 | end 13 | log.debug('Refresh semantic tokens.') 14 | proto.request('workspace/semanticTokens/refresh', json.null) 15 | end 16 | 17 | config.watch(function (uri, key, value, oldValue) 18 | if key == '' then 19 | refresh() 20 | end 21 | if key:find '^Lua.runtime' 22 | or key:find '^Lua.workspace' 23 | or key:find '^Lua.semantic' 24 | or key:find '^files' then 25 | refresh() 26 | end 27 | end) 28 | 29 | return {} 30 | -------------------------------------------------------------------------------- /script/provider/spell.lua: -------------------------------------------------------------------------------- 1 | local suc, codeFormat = pcall(require, 'code_format') 2 | if not suc then 3 | return 4 | end 5 | 6 | local fs = require 'bee.filesystem' 7 | local config = require 'config' 8 | local diagnostics = require 'provider.diagnostic' 9 | local pformatting = require 'provider.formatting' 10 | local util = require 'utility' 11 | 12 | local m = {} 13 | 14 | function m.loadDictionaryFromFile(filePath) 15 | return codeFormat.spell_load_dictionary_from_path(filePath) 16 | end 17 | 18 | function m.loadDictionaryFromBuffer(buffer) 19 | return codeFormat.spell_load_dictionary_from_buffer(buffer) 20 | end 21 | 22 | function m.addWord(word) 23 | return codeFormat.spell_load_dictionary_from_buffer(word) 24 | end 25 | 26 | function m.spellCheck(uri, text) 27 | if not m._dictionaryLoaded then 28 | m.initDictionary() 29 | m._dictionaryLoaded = true 30 | end 31 | 32 | local tempDict = config.get(uri, 'Lua.spell.dict') 33 | 34 | return codeFormat.spell_analysis(uri, text, tempDict) 35 | end 36 | 37 | function m.getSpellSuggest(word) 38 | local status, result = codeFormat.spell_suggest(word) 39 | if status then 40 | return result 41 | end 42 | end 43 | 44 | function m.initDictionary() 45 | local basicDictionary = fs.path(METAPATH) / "spell/dictionary.txt" 46 | local luaDictionary = fs.path(METAPATH) / "spell/lua_dict.txt" 47 | 48 | m.loadDictionaryFromFile(basicDictionary:string()) 49 | m.loadDictionaryFromFile(luaDictionary:string()) 50 | pformatting.updateNonStandardSymbols(config.get(nil, "Lua.runtime.nonstandardSymbol")) 51 | end 52 | 53 | return m 54 | -------------------------------------------------------------------------------- /script/pub/init.lua: -------------------------------------------------------------------------------- 1 | local pub = require 'pub.pub' 2 | require 'pub.report' 3 | 4 | return pub 5 | -------------------------------------------------------------------------------- /script/pub/report.lua: -------------------------------------------------------------------------------- 1 | local pub = require 'pub.pub' 2 | local await = require 'await' 3 | local util = require 'utility' 4 | 5 | pub.on('log', function (params, brave) 6 | log.raw(brave.id, params.level, params.msg, params.src, params.line, params.clock) 7 | end) 8 | 9 | pub.on('mem', function (count, brave) 10 | brave.memory = count 11 | end) 12 | 13 | pub.on('proto', function (params) 14 | local proto = require 'proto' 15 | await.call(function () 16 | if params.method then 17 | proto.doMethod(params) 18 | else 19 | proto.doResponse(params) 20 | end 21 | end) 22 | end) 23 | 24 | pub.on('protoerror', function (err) 25 | log.warn('Load proto error:', err) 26 | os.exit(0, true) 27 | end) 28 | 29 | pub.on('wakeup', function () end) 30 | -------------------------------------------------------------------------------- /script/service/init.lua: -------------------------------------------------------------------------------- 1 | local service = require 'service.service' 2 | 3 | return service 4 | -------------------------------------------------------------------------------- /script/tracy.lua: -------------------------------------------------------------------------------- 1 | local originTracy 2 | 3 | local function enable() 4 | if not originTracy then 5 | local suc = pcall(require, 'luatracy') 6 | if suc then 7 | originTracy = tracy 8 | else 9 | originTracy = { 10 | ZoneBeginN = function (info) end, 11 | ZoneEnd = function () end, 12 | } 13 | end 14 | end 15 | ---@diagnostic disable-next-line: lowercase-global 16 | tracy = originTracy 17 | end 18 | 19 | local function disable() 20 | ---@diagnostic disable-next-line: lowercase-global 21 | tracy = { 22 | ZoneBeginN = function (info) end, 23 | ZoneEnd = function () end, 24 | } 25 | end 26 | 27 | disable() 28 | 29 | return { 30 | enable = enable, 31 | disable = disable, 32 | } 33 | -------------------------------------------------------------------------------- /script/version.lua: -------------------------------------------------------------------------------- 1 | local fsu = require 'fs-utility' 2 | 3 | local function loadVersion() 4 | local changelog = fsu.loadFile(ROOT / 'changelog.md') 5 | if not changelog then 6 | return 7 | end 8 | 9 | local version, pos = changelog:match '%#%# (%d+%.%d+%.%d+)()' 10 | if not version then 11 | return 12 | end 13 | 14 | if not changelog:find('^[\r\n]+`', pos) then 15 | version = version .. '-dev' 16 | end 17 | return version 18 | end 19 | 20 | local m = {} 21 | 22 | function m.getVersion() 23 | if not m.version then 24 | m.version = loadVersion() or '' 25 | end 26 | 27 | return m.version 28 | end 29 | 30 | return m 31 | -------------------------------------------------------------------------------- /script/vm/field.lua: -------------------------------------------------------------------------------- 1 | ---@class vm 2 | local vm = require 'vm.vm' 3 | local util = require 'utility' 4 | local guide = require 'parser.guide' 5 | 6 | local searchByNodeSwitch = util.switch() 7 | : case 'global' 8 | ---@param global vm.global 9 | : call(function (suri, global, pushResult) 10 | for _, set in ipairs(global:getSets(suri)) do 11 | pushResult(set) 12 | end 13 | end) 14 | : default(function (suri, source, pushResult) 15 | pushResult(source) 16 | end) 17 | 18 | local function searchByLocalID(source, pushResult) 19 | local fields = vm.getVariableFields(source, true) 20 | if fields then 21 | for _, field in ipairs(fields) do 22 | pushResult(field) 23 | end 24 | end 25 | end 26 | 27 | local function searchByNode(source, pushResult, mark) 28 | mark = mark or {} 29 | if mark[source] then 30 | return 31 | end 32 | mark[source] = true 33 | local uri = guide.getUri(source) 34 | vm.compileByParentNode(source, vm.ANY, function (field) 35 | searchByNodeSwitch(field.type, uri, field, pushResult) 36 | end) 37 | vm.compileByNodeChain(source, function (src) 38 | searchByNode(src, pushResult, mark) 39 | end) 40 | end 41 | 42 | ---@param source parser.object 43 | ---@return parser.object[] 44 | function vm.getFields(source) 45 | local results = {} 46 | local mark = {} 47 | 48 | local function pushResult(src) 49 | if not mark[src] then 50 | mark[src] = true 51 | results[#results+1] = src 52 | end 53 | end 54 | 55 | searchByLocalID(source, pushResult) 56 | searchByNode(source, pushResult) 57 | 58 | return results 59 | end 60 | -------------------------------------------------------------------------------- /script/vm/init.lua: -------------------------------------------------------------------------------- 1 | local vm = require 'vm.vm' 2 | 3 | ---@alias vm.object parser.object | vm.generic 4 | 5 | require 'vm.compiler' 6 | require 'vm.value' 7 | require 'vm.node' 8 | require 'vm.def' 9 | require 'vm.ref' 10 | require 'vm.field' 11 | require 'vm.doc' 12 | require 'vm.type' 13 | require 'vm.library' 14 | require 'vm.tracer' 15 | require 'vm.infer' 16 | require 'vm.generic' 17 | require 'vm.sign' 18 | require 'vm.variable' 19 | require 'vm.global' 20 | require 'vm.function' 21 | require 'vm.operator' 22 | require 'vm.visible' 23 | require 'vm.precompile' 24 | 25 | return vm 26 | -------------------------------------------------------------------------------- /script/vm/library.lua: -------------------------------------------------------------------------------- 1 | ---@class vm 2 | local vm = require 'vm.vm' 3 | 4 | function vm.getLibraryName(source) 5 | if source.special then 6 | return source.special 7 | end 8 | local defs = vm.getDefs(source) 9 | for _, def in ipairs(defs) do 10 | if def.special then 11 | return def.special 12 | end 13 | end 14 | return nil 15 | end 16 | -------------------------------------------------------------------------------- /script/vm/precompile.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local global = require 'vm.global' 3 | local variable = require 'vm.variable' 4 | 5 | ---@async 6 | files.watch(function (ev, uri) 7 | if ev == 'update' then 8 | global.dropUri(uri) 9 | end 10 | if ev == 'remove' then 11 | global.dropUri(uri) 12 | end 13 | if ev == 'compile' then 14 | local state = files.getLastState(uri) 15 | if state then 16 | global.compileAst(state.ast) 17 | variable.compileAst(state.ast) 18 | end 19 | end 20 | end) 21 | -------------------------------------------------------------------------------- /script/workspace/init.lua: -------------------------------------------------------------------------------- 1 | local workspace = require 'workspace.workspace' 2 | 3 | return workspace 4 | -------------------------------------------------------------------------------- /test/basic/filewatch.lua: -------------------------------------------------------------------------------- 1 | require 'bee.filewatch'.create():add('中文文件夹') 2 | -------------------------------------------------------------------------------- /test/basic/init.lua: -------------------------------------------------------------------------------- 1 | require 'basic.textmerger' 2 | require 'basic.filewatch' 3 | -------------------------------------------------------------------------------- /test/cli/test.lua: -------------------------------------------------------------------------------- 1 | require 'cli.visualize.test' 2 | -------------------------------------------------------------------------------- /test/cli/visualize/test.lua: -------------------------------------------------------------------------------- 1 | local visualize = require 'cli.visualize' 2 | 3 | local testDataDir = 'test/cli/visualize/testdata/' 4 | 5 | local function TestVisualize(fileName) 6 | local inputFile = testDataDir .. fileName .. '.txt' 7 | local outputFile = testDataDir .. fileName .. '-expected.txt' 8 | local output = '' 9 | local writer = {} 10 | function writer:write(text) 11 | output = output .. text 12 | end 13 | visualize.visualizeAst(io.open(inputFile):read('a'), writer) 14 | local expectedOutput = io.open(outputFile):read('a') 15 | if expectedOutput ~= output then 16 | -- uncomment this to update reference output 17 | --io.open(outputFile, "w+"):write(output):close() 18 | error('output mismatch for test file ' .. inputFile) 19 | end 20 | end 21 | 22 | TestVisualize('all-types') 23 | TestVisualize('shorten-names') 24 | -------------------------------------------------------------------------------- /test/cli/visualize/testdata/all-types.txt: -------------------------------------------------------------------------------- 1 | foo = {x = 5, bar = 6, ["baz"] = 7} 2 | foo.y = foo.x + 1 3 | foo[1] = foo[0] 4 | function foo:someMethod() return false end 5 | local s = 0 6 | while s < 10 do 7 | s = s + (foo:someMethod(i, 10) or 10) 8 | end 9 | print(s) 10 | for j = 10, 1, -1 do 11 | for i in ipairs(s) do 12 | goto foolabel 13 | ::foolabel:: 14 | end 15 | end 16 | 17 | function foo() 18 | return function(x, ...) 19 | repeat 20 | until select('#', ...) > 0 21 | end 22 | end 23 | 24 | if x then 25 | return x < 0 or x >= 0 or not x <= 0 and -x > 7 26 | elseif y then 27 | return 5.7 28 | else 29 | return "a very long string that should get shortened to not destroy the layout completely" 30 | end 31 | -------------------------------------------------------------------------------- /test/cli/visualize/testdata/shorten-names.txt: -------------------------------------------------------------------------------- 1 | s = "very long string that is very long and would be annoying if it was fully included in the output" 2 | s = "string\nwith\n\new\nlines" 3 | x = x + 1 4 | x = x + 1 5 | x = x + 1 6 | x = x + 1 7 | x = x + 1 8 | x = x + 1 9 | x = x + 1 10 | x = x + 1 11 | x = x + 1 12 | x = x + 1 13 | x = x + 1 14 | x = x + 1 15 | x = x + 1 16 | -- more than 15 lines should be shortened for tooltip in main 17 | x = x + 1 18 | x = x + 1 -------------------------------------------------------------------------------- /test/command/init.lua: -------------------------------------------------------------------------------- 1 | require 'command.auto-require' 2 | -------------------------------------------------------------------------------- /test/completion/continue.lua: -------------------------------------------------------------------------------- 1 | local define = require 'proto.define' 2 | local config = require 'config' 3 | 4 | config.set(nil, 'Lua.completion.callSnippet', 'Disable') 5 | config.set(nil, 'Lua.completion.keywordSnippet', 'Disable') 6 | config.set(nil, 'Lua.completion.workspaceWord', false) 7 | 8 | ContinueTyping = true 9 | 10 | TEST [[ 11 | local zabcde 12 | za 13 | ]] 14 | { 15 | { 16 | label = 'zabcde', 17 | kind = define.CompletionItemKind.Variable, 18 | } 19 | } 20 | 21 | TEST [[ 22 | -- zabcde 23 | io.z 24 | ]] 25 | { 26 | { 27 | label = 'zabcde', 28 | kind = define.CompletionItemKind.Text, 29 | } 30 | } 31 | 32 | 33 | TEST [[ 34 | -- provider 35 | pro 36 | ]] 37 | { 38 | { 39 | label = 'provider', 40 | kind = define.CompletionItemKind.Text, 41 | } 42 | } 43 | 44 | TEST [[ 45 | ---@param n '"abcdefg"' 46 | local function f(n) end 47 | 48 | f 'abc' 49 | ]] 50 | { 51 | { 52 | label = "'abcdefg'", 53 | kind = define.CompletionItemKind.EnumMember, 54 | textEdit = EXISTS, 55 | } 56 | } 57 | 58 | TEST [[ 59 | ---@type '"abcdefg"' 60 | local t 61 | 62 | if t == 'abc' 63 | ]] 64 | { 65 | { 66 | label = "'abcdefg'", 67 | kind = define.CompletionItemKind.EnumMember, 68 | textEdit = EXISTS, 69 | } 70 | } 71 | 72 | ContinueTyping = false 73 | -------------------------------------------------------------------------------- /test/crossfile/init.lua: -------------------------------------------------------------------------------- 1 | require 'crossfile.definition' 2 | require 'crossfile.infer' 3 | require 'crossfile.references' 4 | require 'crossfile.hover' 5 | require 'crossfile.completion' 6 | require 'crossfile.diagnostic' 7 | -------------------------------------------------------------------------------- /test/definition/arg.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local function xx () 3 | () 4 | end 5 | ]] 6 | 7 | TEST [[ 8 | local 9 | function mt:x() 10 | () 11 | end 12 | mt:x() 13 | ]] 14 | 15 | TEST [[ 16 | function mt:x() 17 | () 18 | end 19 | ]] 20 | -------------------------------------------------------------------------------- /test/definition/field.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | X. = 1 3 | 4 | local t = X 5 | 6 | print(t.) 7 | ]] 8 | 9 | TEST [[ 10 | X.x. = 1 11 | 12 | local t = X.x 13 | 14 | print(t.) 15 | ]] 16 | 17 | TEST [[ 18 | X.x. = 1 19 | 20 | local t = X 21 | 22 | print(t.x.) 23 | ]] 24 | -------------------------------------------------------------------------------- /test/definition/function.lua: -------------------------------------------------------------------------------- 1 | 2 | TEST [[ 3 | function () end 4 | () 5 | ]] 6 | 7 | TEST [[ 8 | local function () end 9 | () 10 | ]] 11 | 12 | TEST [[ 13 | local x 14 | local function () 15 | () 16 | end 17 | ]] 18 | 19 | TEST [[ 20 | local 21 | function () 22 | end 23 | () 24 | ]] 25 | 26 | TEST [[ 27 | local = 28 | () 29 | ]] 30 | -------------------------------------------------------------------------------- /test/definition/init.lua: -------------------------------------------------------------------------------- 1 | local core = require 'core.definition' 2 | local files = require 'files' 3 | local vm = require 'vm' 4 | local catch = require 'catch' 5 | 6 | rawset(_G, 'TEST', true) 7 | 8 | local function founded(targets, results) 9 | if #targets ~= #results then 10 | return false 11 | end 12 | for _, target in ipairs(targets) do 13 | for _, result in ipairs(results) do 14 | if target[1] == result[1] and target[2] == result[2] then 15 | goto NEXT 16 | end 17 | end 18 | do return false end 19 | ::NEXT:: 20 | end 21 | return true 22 | end 23 | 24 | ---@async 25 | function TEST(script) 26 | local newScript, catched = catch(script, '!?') 27 | 28 | files.setText(TESTURI, newScript) 29 | 30 | local results = core(TESTURI, catched['?'][1][1]) 31 | if results then 32 | local positions = {} 33 | for i, result in ipairs(results) do 34 | if not vm.isMetaFile(result.uri) then 35 | positions[#positions+1] = { result.target.start, result.target.finish } 36 | end 37 | end 38 | assert(founded(catched['!'], positions)) 39 | else 40 | assert(#catched['!'] == 0) 41 | end 42 | 43 | files.remove(TESTURI) 44 | end 45 | 46 | require 'definition.local' 47 | require 'definition.set' 48 | require 'definition.field' 49 | require 'definition.arg' 50 | require 'definition.function' 51 | require 'definition.table' 52 | require 'definition.method' 53 | require 'definition.label' 54 | require 'definition.special' 55 | require 'definition.bug' 56 | require 'definition.luadoc' 57 | -------------------------------------------------------------------------------- /test/definition/label.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | :::: 3 | goto 4 | ]] 5 | 6 | TEST [[ 7 | goto 8 | :::: 9 | ]] 10 | 11 | TEST [[ 12 | ::LABEL:: 13 | function _() 14 | goto 15 | end 16 | ]] 17 | 18 | TEST [[ 19 | do 20 | goto 21 | end 22 | :::: 23 | ]] 24 | 25 | TEST [[ 26 | ::LABEL:: 27 | goto 28 | :::: 29 | ]] 30 | -------------------------------------------------------------------------------- /test/definition/method.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | function mt:() 3 | end 4 | function mt:b() 5 | mt:() 6 | end 7 | ]] 8 | 9 | TEST [[ 10 | function mt:() 11 | end 12 | function mt:m2() 13 | self:() 14 | end 15 | ]] 16 | 17 | TEST [[ 18 | function mt:m3() 19 | mt:() 20 | end 21 | function mt:() 22 | end 23 | ]] 24 | 25 | TEST [[ 26 | function mt:m3() 27 | self:() 28 | end 29 | function mt:() 30 | end 31 | ]] 32 | 33 | TEST [[ 34 | local mt 35 | 36 | function mt:f() 37 | self. = 1 38 | end 39 | 40 | mt. 41 | ]] 42 | 43 | TEST [[ 44 | function G:f() 45 | self. = 1 46 | end 47 | 48 | G. 49 | ]] 50 | 51 | TEST [[ 52 | function G.H:f() 53 | self. = 1 54 | end 55 | 56 | G.H. 57 | ]] 58 | -------------------------------------------------------------------------------- /test/definition/set.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | = 1 3 | () 4 | ]] 5 | 6 | TEST [[ 7 | do 8 | = 1 9 | end 10 | () 11 | ]] 12 | 13 | TEST [[ 14 | = 1 15 | do 16 | local x = 1 17 | end 18 | () 19 | ]] 20 | 21 | TEST [[ 22 | x = 1 23 | do 24 | local = 1 25 | do 26 | = 2 27 | end 28 | () 29 | end 30 | ]] 31 | 32 | TEST [[ 33 | = 1 34 | if y then 35 | = 2 36 | else 37 | = 3 38 | end 39 | print() 40 | ]] 41 | -------------------------------------------------------------------------------- /test/diagnostics/ambiguity-1.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local x 3 | x = 4 | ]] 5 | 6 | TEST [[ 7 | local x, y 8 | x = 9 | ]] 10 | 11 | TEST [[ 12 | local x, y, z 13 | x = x and y or '' .. z 14 | ]] 15 | 16 | TEST [[ 17 | local x 18 | x = x or -1 19 | ]] 20 | 21 | TEST [[ 22 | local x 23 | x = x or (0 + 1) 24 | ]] 25 | 26 | TEST [[ 27 | local x, y 28 | x = (x + y) or 0 29 | ]] 30 | -------------------------------------------------------------------------------- /test/diagnostics/cast-type-mismatch.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@type string|boolean 3 | local t 4 | 5 | ---@cast t string 6 | ]] 7 | 8 | TEST [[ 9 | ---@type string|boolean 10 | local t 11 | 12 | ---@cast t 13 | ]] 14 | -------------------------------------------------------------------------------- /test/diagnostics/circle-doc-class.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@class 3 | ---@class 4 | ---@class 5 | ---@class 6 | ]] 7 | 8 | TEST [[ 9 | ---@class A : B 10 | ---@class B : C 11 | ---@class C : D 12 | ---@class D 13 | ]] 14 | 15 | -------------------------------------------------------------------------------- /test/diagnostics/close-non-object.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local _ = 3 | ]] 4 | 5 | TEST [[ 6 | local _ = 7 | ]] 8 | 9 | TEST [[ 10 | local c = 11 | ]] 12 | 13 | TEST [[ 14 | ---@type unknown 15 | local t 16 | 17 | local _ = t 18 | ]] 19 | -------------------------------------------------------------------------------- /test/diagnostics/code-after-break.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | while true do 3 | break 4 | 6 | end 7 | ]] 8 | -------------------------------------------------------------------------------- /test/diagnostics/count-down-loop.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | for i = do 3 | print(i) 4 | end 5 | ]] 6 | 7 | TEST [[ 8 | for i = do 9 | print(i) 10 | end 11 | ]] 12 | 13 | TEST [[ 14 | for i = do 15 | print(i) 16 | end 17 | ]] 18 | 19 | TEST [[ 20 | for i = do 21 | print(i) 22 | end 23 | ]] 24 | 25 | TEST [[ 26 | for i = 1, 1 do 27 | print(i) 28 | end 29 | ]] 30 | -------------------------------------------------------------------------------- /test/diagnostics/deprecated.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local _ = 3 | ]] 4 | 5 | TEST [[ 6 | T = {} 7 | ---@deprecated # comment 8 | T.x = 1 9 | 10 | print() 11 | ]] 12 | 13 | TEST [[ 14 | T = {} 15 | 16 | ---@deprecated 17 | function T:ff() 18 | end 19 | 20 | () 21 | ]] 22 | -------------------------------------------------------------------------------- /test/diagnostics/doc-field-no-class.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@field 3 | ---@class Class 4 | ]] 5 | 6 | TEST [[ 7 | ---@class Class 8 | 9 | ---@field 10 | ]] 11 | 12 | TEST [[ 13 | ---@class Class 14 | --- 15 | ---@field x Class 16 | ]] 17 | -------------------------------------------------------------------------------- /test/diagnostics/duplicate-doc-alias.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@alias integer 3 | ---@alias integer 4 | ]] 5 | 6 | TEST [[ 7 | ---@class A 8 | ---@class B 9 | ---@alias B 10 | ]] 11 | 12 | TEST [[ 13 | ---@alias A integer 14 | ---@alias(partial) A integer 15 | 16 | ---@enum B 17 | ---@enum(partial) B 18 | 19 | ---@enum(key) C 20 | ---@enum(key, partial) C 21 | ]] 22 | -------------------------------------------------------------------------------- /test/diagnostics/duplicate-doc-field.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@class Class 3 | ---@field Class 4 | ---@field Class 5 | ]] 6 | 7 | TEST [[ 8 | --- @class Emit 9 | --- @field on fun(eventName: string, cb: function) 10 | --- @field on fun(eventName: '"died"', cb: fun(i: integer)) 11 | --- @field on fun(eventName: '"won"', cb: fun(s: string)) 12 | local emit = {} 13 | ]] 14 | 15 | TEST [[ 16 | --- @class Emit 17 | --- @field on fun(eventName: string, cb: function) 18 | --- @field fun(eventName: '"died"', cb: fun(i: integer)) 19 | --- @field on fun(eventName: '"won"', cb: fun(s: string)) 20 | --- @field fun(eventName: '"died"', cb: fun(i: integer)) 21 | local emit = {} 22 | ]] 23 | 24 | TEST [[ 25 | ---@class A 26 | 27 | ---@class B 28 | ---@field [integer] A 29 | ---@field [A] true 30 | ]] 31 | 32 | TEST [[ 33 | ---@class A 34 | 35 | ---@class B 36 | ---@field [] A 37 | ---@field [] true 38 | ]] 39 | -------------------------------------------------------------------------------- /test/diagnostics/duplicate-doc-param.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@class Class 3 | ---@param Class 4 | ---@param y Class 5 | ---@param Class 6 | local function f(x, y) 7 | return x, y 8 | end 9 | 10 | local _ 11 | f(_, _) 12 | ]] 13 | -------------------------------------------------------------------------------- /test/diagnostics/duplicate-index.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | return { 3 | = 1, 4 | y = 2, 5 | = 3, 6 | } 7 | ]] 8 | 9 | TEST [[ 10 | return { 11 | x = 1, 12 | y = 2, 13 | }, { 14 | x = 1, 15 | y = 2, 16 | } 17 | ]] 18 | 19 | TEST [[ 20 | return { 21 | 1, , 3, 22 | [] = 4, 23 | } 24 | ]] 25 | -------------------------------------------------------------------------------- /test/diagnostics/duplicate-set-field.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local m = {} 3 | 4 | function () 5 | end 6 | 7 | function () 8 | end 9 | 10 | return m 11 | ]] 12 | 13 | TEST [[ 14 | local m = {} 15 | 16 | function () 17 | end 18 | 19 | do 20 | function () 21 | end 22 | end 23 | 24 | return m 25 | ]] 26 | 27 | TEST [[ 28 | local m = {} 29 | 30 | m.x = true 31 | m.x = false 32 | 33 | return m 34 | ]] 35 | 36 | TEST [[ 37 | local m = {} 38 | 39 | m.x = io.open('') 40 | m.x = nil 41 | 42 | return m 43 | ]] 44 | 45 | TEST [[ 46 | ---@class A 47 | X = {} 48 | 49 | function () end 50 | 51 | function () end 52 | ]] 53 | 54 | TEST [[ 55 | ---@meta 56 | 57 | ---@class A 58 | X = {} 59 | 60 | function X.f() end 61 | 62 | function X.f() end 63 | ]] 64 | 65 | TEST [[ 66 | ---@class A 67 | X = {} 68 | 69 | if true then 70 | function X.f() end 71 | else 72 | function X.f() end 73 | end 74 | ]] 75 | -------------------------------------------------------------------------------- /test/diagnostics/empty-block.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | 4 | ]] 5 | 6 | TEST [[ 7 | 10 | ]] 11 | 12 | TEST [[ 13 | if true then 14 | else 15 | return 16 | end 17 | ]] 18 | 19 | TEST [[ 20 | while true do 21 | end 22 | ]] 23 | 24 | TEST [[ 25 | 27 | ]] 28 | 29 | TEST [[ 30 | 32 | ]] 33 | -------------------------------------------------------------------------------- /test/diagnostics/global-element.lua: -------------------------------------------------------------------------------- 1 | local config = require 'config' 2 | 3 | TEST [[ 4 | local x = 123 5 | x = 321 6 | = "global" 7 | = "global" 8 | ]] 9 | 10 | TEST [[ 11 | local function test1() 12 | print() 13 | end 14 | 15 | function () 16 | print() 17 | end 18 | ]] 19 | 20 | TEST [[ 21 | local function closure1() 22 | local elem1 = 1 23 | = 2 24 | end 25 | 26 | function () 27 | local elem1 = 1 28 | = 2 29 | end 30 | ]] 31 | 32 | -- add elements to exemption list 33 | config.set(nil, 'Lua.diagnostics.globals', 34 | { 35 | 'GLOBAL1', 36 | 'GLOBAL2', 37 | 'GLOBAL_CLOSURE' 38 | }) 39 | 40 | TEST [[ 41 | GLOBAL1 = "allowed" 42 | = "not allowed" 43 | = "not allowed" 44 | ]] 45 | 46 | TEST [[ 47 | function GLOBAL1() 48 | print() 49 | end 50 | ]] 51 | 52 | TEST [[ 53 | local function closure1() 54 | local elem1 = 1 55 | GLOBAL1 = 2 56 | end 57 | 58 | function GLOBAL_CLOSURE() 59 | local elem1 = 1 60 | GLOBAL2 = 2 61 | = 2 62 | end 63 | ]] 64 | -------------------------------------------------------------------------------- /test/diagnostics/global-in-nil-env.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local _ 3 | print(_) 4 | local _ 5 | print(_) 6 | local _ENV 7 | (_ENV) -- 由于重定义了_ENV,因此print变为了未定义全局变量 8 | ]] 9 | 10 | TEST [[ 11 | _ENV = nil 12 | () -- `print` and `A` should warning 13 | ]] 14 | 15 | TEST [[ 16 | local _ENV = nil 17 | () -- `print` and `A` should warning 18 | ]] 19 | 20 | TEST [[ 21 | _ENV = {} 22 | print(A) -- no warning 23 | ]] 24 | 25 | TEST [[ 26 | local _ENV = {} 27 | print(A) -- no warning 28 | ]] 29 | 30 | TEST [[ 31 | _ENV = nil 32 | = 1 --> _ENV.GLOBAL = 1 33 | ]] 34 | 35 | TEST [[ 36 | _ENV = nil 37 | local _ = --> local _ = _ENV.print 38 | ]] 39 | 40 | TEST [[ 41 | local function foo(_ENV) 42 | Joe = "human" 43 | end 44 | ]] 45 | -------------------------------------------------------------------------------- /test/diagnostics/inject-field.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@class Class 3 | local m = {} 4 | 5 | m.xx = 1 -- OK 6 | 7 | ---@type Class 8 | local m 9 | 10 | m.xx = 1 -- OK 11 | m. = 1 -- Warning 12 | ]] 13 | 14 | TEST [[ 15 | ---@class Class 16 | local m = {} 17 | 18 | m.xx = 1 -- OK 19 | 20 | ---@class Class 21 | local m 22 | 23 | m.xx = 1 -- OK 24 | m.yy = 1 -- OK 25 | ]] 26 | 27 | TEST [[ 28 | ---@type { xx: number } 29 | local m 30 | 31 | m.xx = 1 -- OK 32 | m. = 1 -- Warning 33 | ]] 34 | 35 | TEST [[ 36 | ---@type { xx: number, [any]: any } 37 | local m 38 | 39 | m.xx = 1 -- OK 40 | m.yy = 1 -- OK 41 | ]] 42 | 43 | TEST [[ 44 | ---@class Class 45 | ---@field x number 46 | 47 | ---@type Class 48 | local t 49 | 50 | t.x = 1 -- OK 51 | t. = 2 -- Warning 52 | ]] 53 | 54 | TEST [[ 55 | ---@class Class 56 | ---@field x number 57 | ---@field [any] any 58 | 59 | ---@type Class 60 | local t 61 | 62 | t.x = 1 -- OK 63 | t.y = 2 -- OK 64 | ]] 65 | 66 | 67 | TEST [[ 68 | ---@class (exact) Class 69 | ---@field x number 70 | local m = { 71 | x = 1, -- OK 72 | = 2, -- Warning 73 | } 74 | 75 | m.x = 1 -- OK 76 | m. = 2 -- Warning 77 | 78 | function m:init() -- OK 79 | self.x = 1 -- OK 80 | self. = 2 -- Warning 81 | function self:() -- Warning 82 | end 83 | end 84 | ]] 85 | -------------------------------------------------------------------------------- /test/diagnostics/lowercase-global.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | = 1 3 | tostring = 1 4 | ROOT = 1 5 | _G.bb = 1 6 | ]] 7 | 8 | TEST [[ 9 | ---@diagnostic disable-next-line 10 | x = 1 11 | ]] 12 | 13 | TEST [[ 14 | ---@diagnostic disable-next-line: lowercase-global 15 | x = 1 16 | ]] 17 | 18 | TEST [[ 19 | ---@diagnostic disable-next-line: unused-local 20 | = 1 21 | ]] 22 | 23 | TEST [[ 24 | ---@diagnostic disable 25 | x = 1 26 | ]] 27 | 28 | TEST [[ 29 | ---@diagnostic disable 30 | ---@diagnostic enable 31 | = 1 32 | ]] 33 | 34 | TEST [[ 35 | ---@diagnostic disable 36 | ---@diagnostic disable 37 | ---@diagnostic enable 38 | x = 1 39 | ]] 40 | -------------------------------------------------------------------------------- /test/diagnostics/missing-parameter.lua: -------------------------------------------------------------------------------- 1 | 2 | TEST [[ 3 | local function x(a, b) 4 | return a, b 5 | end 6 | x(1) 7 | ]] 8 | 9 | TEST [[ 10 | ---@param a integer 11 | ---@param b integer 12 | local function x(a, b) 13 | return a, b 14 | end 15 | 16 | ]] 17 | 18 | TEST [[ 19 | ---@param a integer 20 | ---@param b integer 21 | local function x(a, b) 22 | return a, b 23 | end 24 | 25 | ]] 26 | 27 | TEST [[ 28 | ---@param a integer 29 | ---@param b integer 30 | ---@param ... integer 31 | local function x(a, b, ...) 32 | return a, b, ... 33 | end 34 | x(1, 2) 35 | ]] 36 | 37 | TEST [[ 38 | ---@param a integer 39 | ---@param b integer 40 | local function f(a, b) 41 | end 42 | 43 | f(...) 44 | ]] 45 | 46 | TEST [[ 47 | ---@param a integer 48 | ---@param b integer 49 | local function f(a, b) 50 | end 51 | 52 | local function return2Numbers() 53 | return 1, 2 54 | end 55 | 56 | f(return2Numbers()) 57 | ]] 58 | 59 | TEST [[ 60 | ---@param a integer 61 | ---@param b? integer 62 | local function x(a, b) 63 | return a, b 64 | end 65 | x(1) 66 | ]] 67 | 68 | TEST [[ 69 | ---@param b integer? 70 | local function x(a, b) 71 | return a, b 72 | end 73 | x(1) 74 | ]] 75 | 76 | TEST [[ 77 | ---@param b integer|nil 78 | local function x(a, b) 79 | return a, b 80 | end 81 | x(1) 82 | ]] 83 | 84 | TEST [[ 85 | local t = {} 86 | 87 | function t:init() end 88 | 89 | 90 | ]] 91 | -------------------------------------------------------------------------------- /test/diagnostics/missing-return-value.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@type fun():number 3 | local function f() 4 | 5 | end 6 | ]] 7 | 8 | TEST [[ 9 | ---@return number 10 | function F() 11 | 12 | end 13 | ]] 14 | 15 | TEST [[ 16 | ---@return number, number 17 | function F() 18 | 1 19 | end 20 | ]] 21 | 22 | TEST [[ 23 | ---@return number, number? 24 | function F() 25 | return 1 26 | end 27 | ]] 28 | 29 | TEST [[ 30 | ---@return ... 31 | function F() 32 | return 33 | end 34 | ]] 35 | -------------------------------------------------------------------------------- /test/diagnostics/need-check-nil.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@type string? 3 | local x 4 | 5 | local s = :upper() 6 | ]] 7 | 8 | TEST [[ 9 | ---@type string? 10 | local x 11 | 12 | S = :upper() 13 | ]] 14 | 15 | TEST [[ 16 | ---@type string? 17 | local x 18 | 19 | if x then 20 | S = x:upper() 21 | end 22 | ]] 23 | 24 | TEST [[ 25 | ---@type string? 26 | local x 27 | 28 | if not x then 29 | x = '' 30 | end 31 | 32 | S = x:upper() 33 | ]] 34 | 35 | TEST [[ 36 | ---@type fun()? 37 | local x 38 | 39 | S = () 40 | ]] 41 | 42 | TEST [[ 43 | ---@type integer? 44 | local x 45 | 46 | T = {} 47 | T[] = 1 48 | ]] 49 | 50 | TEST [[ 51 | local x, y 52 | local z = x and y 53 | 54 | print(z.y) 55 | ]] 56 | 57 | TEST [[ 58 | local x, y 59 | function x() 60 | y() 61 | end 62 | 63 | function y() 64 | x() 65 | end 66 | 67 | x() 68 | ]] 69 | -------------------------------------------------------------------------------- /test/diagnostics/newfield-call.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | return { 3 | 5 | } 6 | ]] 7 | 8 | TEST [[ 9 | return { 10 | 14 | } 15 | ]] 16 | -------------------------------------------------------------------------------- /test/diagnostics/newline-call.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | :sub(1, 1) 4 | ]] 5 | 6 | TEST [[ 7 | print() 8 | ('string') 9 | ]] 10 | 11 | TEST [[ 12 | print 13 | {} 14 | {} 15 | ]] 16 | 17 | TEST [[ 18 | local x 19 | return x 20 | : f(1) 21 | : f(1) 22 | ]] 23 | 24 | TEST [[ 25 | print() 26 | 'string' 27 | ]] 28 | 29 | TEST [[ 30 | print 31 | { 32 | x = 1, 33 | } 34 | ]] 35 | -------------------------------------------------------------------------------- /test/diagnostics/not-yieldable.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@param cb fun() 3 | local function f(cb) 4 | return cb 5 | end 6 | 7 | ---@async 8 | local function af() 9 | return nil 10 | end 11 | 12 | f() 13 | ]] 14 | 15 | TEST [[ 16 | ---@param cb async fun() 17 | local function f(cb) 18 | return cb 19 | end 20 | 21 | ---@async 22 | local function af() 23 | return nil 24 | end 25 | 26 | f(af) 27 | ]] 28 | 29 | TEST [[ 30 | local function f(cb) 31 | cb() 32 | end 33 | 34 | ---@async 35 | local function af() 36 | f(function () ---@async 37 | return nil 38 | end) 39 | end 40 | 41 | return af 42 | ]] 43 | 44 | TEST [[ 45 | local _ = type(function () ---@async 46 | return nil 47 | end) 48 | ]] 49 | -------------------------------------------------------------------------------- /test/diagnostics/redefined-local.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local x 3 | print(x) 4 | local 5 | print(x) 6 | ]] 7 | 8 | TEST [[ 9 | local x 10 | print(x) 11 | local 12 | print(x) 13 | local 14 | print(x) 15 | ]] 16 | 17 | TEST [[ 18 | local x 19 | return x, function () 20 | return x 21 | end 22 | ]] 23 | -------------------------------------------------------------------------------- /test/diagnostics/redundant-return-value.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@type fun():number 3 | local function f() 4 | return 1, 5 | end 6 | ]] 7 | 8 | TEST [[ 9 | ---@return number, number? 10 | function F() 11 | return 1, 1, 12 | end 13 | ]] 14 | 15 | TEST [[ 16 | ---@return number, number? 17 | function F() 18 | return 1, 1, , , 19 | end 20 | ]] 21 | 22 | TEST [[ 23 | ---@meta 24 | 25 | ---@return number, number 26 | local function r2() end 27 | 28 | ---@return number, number? 29 | function F() 30 | return 1, 31 | end 32 | ]] 33 | -------------------------------------------------------------------------------- /test/diagnostics/redundant-return.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local function f() 3 | 4 | end 5 | f() 6 | ]] 7 | 8 | TEST [[ 9 | local function f() 10 | return nil 11 | end 12 | f() 13 | ]] 14 | 15 | TEST [[ 16 | local function f() 17 | local function x() 18 | 19 | end 20 | x() 21 | return true 22 | end 23 | f() 24 | ]] 25 | 26 | TEST [[ 27 | local function f() 28 | local function x() 29 | return true 30 | end 31 | return x() 32 | end 33 | f() 34 | ]] 35 | -------------------------------------------------------------------------------- /test/diagnostics/redundant-value.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local _ = 1, 3 | ]] 4 | 5 | TEST [[ 6 | _ = 1, 7 | ]] 8 | -------------------------------------------------------------------------------- /test/diagnostics/trailing-space.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | 3 | ]] 4 | 5 | TEST [[ 6 | 7 | 8 | ]] 9 | 10 | TEST [[ 11 | X = 1 12 | ]] 13 | 14 | TEST [[ 15 | X = [=[ 16 | ]=] 17 | ]] 18 | 19 | TEST [[ 20 | -- xxxx 21 | ]] 22 | 23 | TEST [[ 24 | -- [=[ 25 | ]=] 26 | ]] 27 | 28 | TEST [=[ 29 | return [[ 30 | 31 | ]] 32 | ]=] 33 | -------------------------------------------------------------------------------- /test/diagnostics/unbalanced-assignments.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local x, , = 1 3 | ]] 4 | 5 | TEST [[ 6 | local x, y, = 1, 2 7 | ]] 8 | 9 | TEST [[ 10 | local x, y, z = print() 11 | ]] 12 | 13 | TEST [[ 14 | local x, y, z 15 | ]] 16 | 17 | TEST [[ 18 | local x, y, z 19 | x, , = 1 20 | ]] 21 | 22 | TEST [[ 23 | X, , = 1 24 | ]] 25 | 26 | TEST [[ 27 | T = {} 28 | T.x, , = 1 29 | ]] 30 | 31 | TEST [[ 32 | T = {} 33 | T['x'], , = 1 34 | ]] 35 | -------------------------------------------------------------------------------- /test/diagnostics/undefined-doc-class.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@class A : 3 | ]] 4 | -------------------------------------------------------------------------------- /test/diagnostics/undefined-doc-name.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@type 3 | ]] 4 | 5 | TEST [[ 6 | ---@class A 7 | ---@type A|| 8 | ]] 9 | 10 | TEST [[ 11 | ---@class AAA 12 | ---@alias B AAA 13 | 14 | ---@type B 15 | ]] 16 | 17 | TEST [[ 18 | ---@alias B 19 | ]] 20 | -------------------------------------------------------------------------------- /test/diagnostics/undefined-doc-param.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@param Class 3 | ]] 4 | 5 | TEST [[ 6 | ---@class Class 7 | ---@param Class 8 | local function f(x) 9 | return x 10 | end 11 | f() 12 | ]] 13 | 14 | TEST [[ 15 | ---@class Class 16 | ---@param Class 17 | function F(x) 18 | return x 19 | end 20 | F() 21 | ]] 22 | -------------------------------------------------------------------------------- /test/diagnostics/undefined-env-child.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@type iolib 3 | _ENV = io 4 | (stderr) -- `print` is warning but `stderr` is not 5 | ]] 6 | 7 | TEST [[ 8 | ---@type iolib 9 | local _ENV = io 10 | (stderr) -- `print` is warning but `stderr` is not 11 | ]] 12 | 13 | TEST [[ 14 | local _ENV = { print = print } 15 | print(1) 16 | ]] 17 | 18 | TEST [[ 19 | _ENV = {} 20 | GLOBAL = 1 --> _ENV.GLOBAL = 1 21 | ]] 22 | 23 | TEST [[ 24 | _ENV = {} 25 | local _ = print --> local _ = _ENV.print 26 | ]] 27 | 28 | TEST [[ 29 | GLOBAL = 1 30 | _ENV = nil 31 | ]] 32 | 33 | TEST [[ 34 | print(1) 35 | _ENV = nil 36 | ]] 37 | -------------------------------------------------------------------------------- /test/diagnostics/undefined-global.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local print, _G 3 | print() 4 | print() 5 | print() 6 | print() 7 | print() 8 | print(Z) 9 | print(_G) 10 | Z = 1 11 | ]] 12 | 13 | TEST [[ 14 | X = table[] 15 | ]] 16 | TEST [[ 17 | T1 = 1 18 | _ENV.T2 = 1 19 | _G.T3 = 1 20 | _ENV._G.T4 = 1 21 | _G._G._G.T5 = 1 22 | rawset(_G, 'T6', 1) 23 | rawset(_ENV, 'T7', 1) 24 | print(T1) 25 | print(T2) 26 | print(T3) 27 | print(T4) 28 | print(T5) 29 | print(T6) 30 | print(T7) 31 | ]] 32 | 33 | TEST [[ 34 | ---@class c 35 | c = {} 36 | ]] 37 | -------------------------------------------------------------------------------- /test/diagnostics/unknown-cast-variable.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@cast integer 3 | ]] 4 | 5 | TEST [[ 6 | local x, y 7 | ---@cast y number 8 | ]] 9 | -------------------------------------------------------------------------------- /test/diagnostics/unknown-diag-code.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@diagnostic disable-next-line: 3 | ]] 4 | -------------------------------------------------------------------------------- /test/diagnostics/unknown-operator.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | ---@class A 3 | ---@operator : A 4 | ]] 5 | -------------------------------------------------------------------------------- /test/diagnostics/unreachable-code.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | if X then 3 | return false 4 | elseif X then 5 | return false 6 | else 7 | return false 8 | end 9 | 10 | ]] 11 | 12 | TEST [[ 13 | function X() 14 | if X then 15 | return false 16 | elseif X then 17 | return false 18 | else 19 | return false 20 | end 21 | 22 | end 23 | ]] 24 | 25 | TEST [[ 26 | while true do 27 | end 28 | 29 | 30 | ]] 31 | 32 | TEST [[ 33 | while true do 34 | end 35 | 36 | 37 | ]] 38 | 39 | TEST [[ 40 | while X do 41 | X = 1 42 | end 43 | 44 | print(1) 45 | ]] 46 | 47 | TEST [[ 48 | while true do 49 | if not X then 50 | break 51 | end 52 | end 53 | 54 | print(1) 55 | 56 | do return end 57 | ]] 58 | 59 | TEST [[ 60 | local done = false 61 | 62 | local function set_done() 63 | done = true 64 | end 65 | 66 | while not done do 67 | set_done() 68 | end 69 | 70 | print(1) 71 | ]] 72 | -------------------------------------------------------------------------------- /test/diagnostics/unused-function.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local 4 | ]] 5 | 6 | TEST [[ 7 | local x = 8 | ]] 9 | 10 | TEST [[ 11 | local x 12 | x = 13 | ]] 14 | 15 | TEST [[ 16 | local 18 | local 21 | ]] 22 | 23 | TEST [[ 24 | local f = 25 | ]] 26 | 27 | TEST [[ 28 | local f;f = 29 | ]] 30 | 31 | TEST [[ 32 | local 33 | ]] 34 | 35 | TEST [[ 36 | local 39 | ]] 40 | 41 | 42 | TEST [[ 43 | local 45 | 46 | local 48 | ]] 49 | -------------------------------------------------------------------------------- /test/diagnostics/unused-label.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | :::: 3 | ]] 4 | -------------------------------------------------------------------------------- /test/diagnostics/unused-local.lua: -------------------------------------------------------------------------------- 1 | local config = require 'config' 2 | 3 | TEST [[ 4 | local 5 | ]] 6 | 7 | TEST [[ 8 | local y 9 | local x = y 10 | ]] 11 | 12 | TEST [[ 13 | local function x() 14 | end 15 | x() 16 | ]] 17 | 18 | TEST [[ 19 | return function (x) 20 | x.a = 1 21 | end 22 | ]] 23 | 24 | TEST [[ 25 | local = {} 26 | .a = 1 27 | ]] 28 | 29 | TEST [[ 30 | InstanceName = 1 31 | Instance = _G[InstanceName] 32 | ]] 33 | 34 | TEST [[ 35 | local _ = (''):sub(1, 2) 36 | ]] 37 | 38 | TEST [[ 39 | local mt, x 40 | function mt:m() 41 | function x:m() 42 | end 43 | end 44 | return mt, x 45 | ]] 46 | 47 | TEST [[ 48 | local mt = {} 49 | function mt:f() 50 | end 51 | return mt 52 | ]] 53 | 54 | TEST [[ 55 | local = {} 56 | function :f() 57 | end 58 | ]] 59 | 60 | TEST [[ 61 | local = {} 62 | .a = 1 63 | ]] 64 | 65 | TEST [[ 66 | local = {} 67 | ['a'] = 1 68 | ]] 69 | 70 | TEST [[ 71 | local function f() 72 | return 'something' 73 | end 74 | f() 75 | ]] 76 | 77 | TEST [[ 78 | local function f(var) 79 | print(var) 80 | end 81 | local var 82 | f(var) 83 | ]] 84 | 85 | TEST [[ 86 | local = {} 87 | [1] = 1 88 | ]] 89 | 90 | config.add(nil, 'Lua.diagnostics.unusedLocalExclude', 'll_*') 91 | 92 | TEST [[ 93 | local 94 | local ll_1 95 | local ll_2 96 | local 97 | ]] 98 | 99 | config.remove(nil, 'Lua.diagnostics.unusedLocalExclude', 'll_*') 100 | -------------------------------------------------------------------------------- /test/diagnostics/unused-vararg.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | local function f() 3 | return 'something' 4 | end 5 | f() 6 | ]] 7 | -------------------------------------------------------------------------------- /test/full/dirty.lua: -------------------------------------------------------------------------------- 1 | TEST [[ 2 | a. 3 | ]] 4 | 5 | TEST [[ 6 | a: 7 | ]] 8 | 9 | TEST [[ 10 | end 11 | ]] 12 | 13 | TEST [[ 14 | table.02X 15 | ]] 16 | -------------------------------------------------------------------------------- /test/full/init.lua: -------------------------------------------------------------------------------- 1 | local parser = require 'parser' 2 | local config = require 'config' 3 | local util = require 'utility' 4 | 5 | rawset(_G, 'TEST', true) 6 | 7 | function TEST(script) 8 | local clock = os.clock() 9 | local state = parser.compile(script, 'Lua', 'Lua 5.4') 10 | state.compileClock = os.clock() - clock 11 | return state 12 | end 13 | 14 | local function startCollectDiagTimes() 15 | for name in pairs(config.get(nil, 'Lua.diagnostics.neededFileStatus')) do 16 | if name ~= 'no-implicit-any' then 17 | --config.get(nil, 'Lua.diagnostics.neededFileStatus')[name] = 'Any' 18 | end 19 | end 20 | DIAGTIMES = {} 21 | end 22 | 23 | startCollectDiagTimes() 24 | require 'full.normal' 25 | require 'full.example' 26 | require 'full.dirty' 27 | require 'full.projects' 28 | require 'full.self' 29 | 30 | local times = {} 31 | for name, time in util.sortPairs(DIAGTIMES, function (k1, k2) 32 | return DIAGTIMES[k1] > DIAGTIMES[k2] 33 | end) do 34 | times[#times+1] = ('诊断任务耗时:%05.3f [%s]'):format(time, name) 35 | if #times >= 10 then 36 | break 37 | end 38 | end 39 | 40 | util.revertArray(times) 41 | for _, time in ipairs(times) do 42 | print(time) 43 | end 44 | -------------------------------------------------------------------------------- /test/full/projects.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local fsu = require 'fs-utility' 3 | local furi = require 'file-uri' 4 | local diag = require 'provider.diagnostic' 5 | local config = require 'config' 6 | local ws = require 'workspace' 7 | local fs = require 'bee.filesystem' 8 | 9 | config.set(nil, 'Lua.workspace.preloadFileSize', 1000000) 10 | config.set(nil, 'Lua.diagnostics.neededFileStatus', { 11 | ['await-in-sync'] = 'Any', 12 | ['not-yieldable'] = 'Any', 13 | }) 14 | 15 | ---@diagnostic disable: await-in-sync 16 | local function doProjects(pathname) 17 | local path = fs.path(pathname) 18 | if not fs.exists(path) then 19 | return 20 | end 21 | 22 | local uris = {} 23 | 24 | print('基准诊断目录:', path) 25 | fsu.scanDirectory(path, function (path) 26 | if path:extension() ~= '.lua' then 27 | return 28 | end 29 | local uri = furi.encode(path:string()) 30 | local text = fsu.loadFile(path) 31 | files.setText(uri, text) 32 | files.open(uri) 33 | uris[#uris+1] = uri 34 | end) 35 | 36 | local _ = function () 37 | for _, uri in ipairs(uris) do 38 | files.remove(uri) 39 | end 40 | end 41 | 42 | print('开始诊断...') 43 | 44 | furi.encode(path:string()) 45 | diag.diagnosticsScope(furi.encode(path:string())) 46 | 47 | local clock = os.clock() 48 | 49 | for uri in files.eachFile() do 50 | local fileClock = os.clock() 51 | diag.doDiagnostic(uri, true) 52 | print('诊断文件耗时:', os.clock() - fileClock, uri) 53 | end 54 | 55 | local passed = os.clock() - clock 56 | print('基准全量诊断用时:', passed) 57 | end 58 | 59 | --doProjects [[C:\SSSEditor\client\Output\Lua]] 60 | --doProjects [[C:\W3-Server\script]] 61 | -------------------------------------------------------------------------------- /test/other/filewatch.lua: -------------------------------------------------------------------------------- 1 | local thread = require 'bee.thread' 2 | local fw = require 'filewatch' 3 | local fs = require 'bee.filesystem' 4 | local fsu = require 'fs-utility' 5 | 6 | local path = fs.path(LOGPATH) / 'fw' 7 | 8 | fs.create_directories(path) 9 | 10 | os.remove((path / 'test.txt'):string()) 11 | 12 | local _ = fw.watch(path:string(), true) 13 | fsu.saveFile(path / 'test.txt', 'test') 14 | 15 | local events 16 | fw.event(function (ev, filename) 17 | events[#events+1] = {ev, filename} 18 | end) 19 | 20 | thread.sleep(1000) 21 | events = {} 22 | fw.update() 23 | assert(#events == 1) 24 | assert(events[1][1] == 'create') 25 | 26 | fsu.saveFile(path / 'test.txt', 'modify') 27 | 28 | thread.sleep(1000) 29 | events = {} 30 | fw.update() 31 | assert(#events == 1) 32 | assert(events[1][1] == 'change') 33 | 34 | local f = io.open((path / 'test.txt'):string(), 'w') 35 | assert(f) 36 | f:write('xxx') 37 | f:flush() 38 | f:close() 39 | 40 | thread.sleep(1000) 41 | events = {} 42 | fw.update() 43 | assert(#events == 1) 44 | assert(events[1][1] == 'change') 45 | -------------------------------------------------------------------------------- /test/other/init.lua: -------------------------------------------------------------------------------- 1 | --require 'other.filewatch' 2 | -------------------------------------------------------------------------------- /test/plugins/ast/helper.lua: -------------------------------------------------------------------------------- 1 | local helper = require 'plugins.astHelper' 2 | local parser = require 'parser' 3 | 4 | function Run(script, plugin) 5 | local state = parser.compile(script, "Lua", "Lua 5.4") 6 | plugin(state) 7 | parser.luadoc(state) 8 | return state 9 | end 10 | 11 | local function TestInsertDoc(script) 12 | local state = Run(script, function (state) 13 | local comment = assert(helper.buildComment("class", "AA", state.ast[1].start)) 14 | helper.InsertDoc(state.ast, comment) 15 | end) 16 | assert(state.ast[1].bindDocs) 17 | end 18 | 19 | TestInsertDoc("A={}") 20 | 21 | local function TestaddClassDoc(script) 22 | local state = Run(script, function (state) 23 | assert(helper.addClassDoc(state.ast, state.ast[1], "AA")) 24 | end) 25 | assert(state.ast[1].bindDocs) 26 | end 27 | 28 | TestaddClassDoc [[a={}]] 29 | 30 | TestaddClassDoc [[local a={}]] 31 | 32 | local function TestaddClassDocAtParam(script, index) 33 | index = index or 1 34 | local arg 35 | local state = Run(script, function (state) 36 | local func = state.ast[1].value 37 | local ok 38 | ok, arg = helper.addClassDocAtParam(state.ast, "AA", func, index) 39 | assert(ok) 40 | end) 41 | assert(arg.bindDocs) 42 | end 43 | 44 | TestaddClassDocAtParam [[ 45 | function a(b) end 46 | ]] 47 | 48 | local function TestaddParamTypeDoc(script, index) 49 | index = index or 1 50 | local func 51 | Run(script, function (state) 52 | func = state.ast[1].value 53 | assert(helper.addParamTypeDoc(state.ast, "string", func.args[index])) 54 | end) 55 | assert(func.args[index].bindDocs) 56 | end 57 | 58 | TestaddParamTypeDoc [[ 59 | local function t(a)end 60 | ]] 61 | 62 | TestaddParamTypeDoc([[ 63 | local function t(a,b,c,d)end 64 | ]], 4) 65 | -------------------------------------------------------------------------------- /test/plugins/ffi/cdef.lua: -------------------------------------------------------------------------------- 1 | 2 | local files = require 'files' 3 | local code = require 'plugins.ffi.searchCode' 4 | local cdefRerence = require 'plugins.ffi.cdefRerence' 5 | 6 | rawset(_G, 'TEST', true) 7 | 8 | function TEST(wanted) 9 | ---@async 10 | return function (script) 11 | files.setText(TESTURI, script) 12 | local codeResults = code(cdefRerence(), TESTURI) 13 | assert(codeResults) 14 | table.sort(codeResults) 15 | assert(table.concat(codeResults, '|') == wanted, table.concat(codeResults, '|') .. ' ~= ' .. wanted) 16 | files.remove(TESTURI) 17 | end 18 | end 19 | 20 | TEST 'aaa|bbb' [[ 21 | local ffi = require 'ffi' 22 | local cdef = ffi.cdef 23 | cdef('aaa') 24 | cdef = function () 25 | end 26 | cdef('bbb') 27 | ]] 28 | 29 | TEST 'aaa' [[ 30 | local ffi = require 'ffi' 31 | 32 | ffi.cdef('aaa') 33 | ]] 34 | 35 | TEST 'aa.aa' [[ 36 | local ffi = require 'ffi' 37 | local t1 = ffi 38 | 39 | t1.cdef"aa.aa" 40 | ]] 41 | 42 | TEST 'aaa' [[ 43 | local ffi = require 'ffi' 44 | local code = 'aaa' 45 | ffi.cdef(code) 46 | ]] 47 | 48 | TEST 'aaa|bbb' [[ 49 | local ffi = require 'ffi' 50 | local code = 'aaa' 51 | code = 'bbb' 52 | local t1 = ffi 53 | t1.cdef(code) 54 | ]] 55 | 56 | TEST 'aa.aa' [[ 57 | local ffi = require 'ffi' 58 | local cdef = ffi.cdef 59 | 60 | cdef"aa.aa" 61 | ]] 62 | -------------------------------------------------------------------------------- /test/plugins/ffi/test.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local ws = require 'workspace' 3 | local furi = require 'file-uri' 4 | local files = require 'files' 5 | local diagnostic = require 'provider.diagnostic' 6 | 7 | --TODO how to changed the runtime version? 8 | local template = require 'config.template' 9 | 10 | template['Lua.runtime.version'].default = 'LuaJIT' 11 | 12 | TESTURI = furi.encode(TESTROOT .. 'unittest.ffi.lua') 13 | 14 | ---@async 15 | local function TestBuilder() 16 | local builder = require 'core.command.reloadFFIMeta' 17 | files.setText(TESTURI, [[ 18 | local ffi = require 'ffi' 19 | ffi.cdef 'void test();' 20 | ]]) 21 | local uri = ws.getFirstScope().uri 22 | builder(uri) 23 | end 24 | 25 | ---@async 26 | lclient():start(function (languageClient) 27 | languageClient:registerFakers() 28 | local rootUri = TESTURI 29 | languageClient:initialize { 30 | rootUri = rootUri, 31 | } 32 | 33 | diagnostic.pause() 34 | 35 | ws.awaitReady(rootUri) 36 | 37 | require 'plugins.ffi.cdef' 38 | require 'plugins.ffi.parser' 39 | require 'plugins.ffi.builder' 40 | TestBuilder() 41 | 42 | diagnostic.resume() 43 | end) 44 | -------------------------------------------------------------------------------- /test/plugins/node/test.lua: -------------------------------------------------------------------------------- 1 | local files = require 'files' 2 | local scope = require 'workspace.scope' 3 | local nodeHelper = require 'plugins.nodeHelper' 4 | local vm = require 'vm' 5 | local guide = require 'parser.guide' 6 | 7 | 8 | local pattern, msg = nodeHelper.createFieldPattern("*.components") 9 | assert(pattern, msg) 10 | 11 | ---@param source parser.object 12 | function OnCompileFunctionParam(next, func, source) 13 | if next(func, source) then 14 | return true 15 | end 16 | --从该参数的使用模式来推导该类型 17 | if nodeHelper.matchPattern(source, pattern) then 18 | local type = vm.declareGlobal('type', 'TestClass', TESTURI) 19 | vm.setNode(source, vm.createNode(type, source)) 20 | return true 21 | end 22 | end 23 | 24 | local myplugin = { OnCompileFunctionParam = OnCompileFunctionParam } 25 | 26 | ---@diagnostic disable: await-in-sync 27 | local function TestPlugin(script) 28 | local prefix = [[ 29 | ---@class TestClass 30 | ---@field b string 31 | ]] 32 | ---@param checker fun(state:parser.state) 33 | return function (plugin, checker) 34 | files.open(TESTURI) 35 | files.setText(TESTURI, prefix .. script, true) 36 | scope.getScope(TESTURI):set('pluginInterfaces', plugin) 37 | local state = files.getState(TESTURI) 38 | assert(state) 39 | checker(state) 40 | files.remove(TESTURI) 41 | end 42 | end 43 | 44 | TestPlugin [[ 45 | local function t(a) 46 | a.components:test() 47 | end 48 | ]](myplugin, function (state) 49 | guide.eachSourceType(state.ast, 'local', function (src) 50 | if guide.getKeyName(src) == 'a' then 51 | local node = vm.compileNode(src) 52 | assert(node) 53 | assert(not vm.isUnknown(node)) 54 | end 55 | end) 56 | end) 57 | -------------------------------------------------------------------------------- /test/plugins/test.lua: -------------------------------------------------------------------------------- 1 | require 'plugins.ast.test' 2 | require 'plugins.ffi.test' 3 | require 'plugins.node.test' 4 | -------------------------------------------------------------------------------- /test/references/init.lua: -------------------------------------------------------------------------------- 1 | ---@diagnostic disable: await-in-sync 2 | local core = require 'core.reference' 3 | local files = require 'files' 4 | local catch = require 'catch' 5 | 6 | local function founded(targets, results) 7 | if #targets ~= #results then 8 | return false 9 | end 10 | for _, target in ipairs(targets) do 11 | for _, result in ipairs(results) do 12 | if target[1] == result[1] and target[2] == result[2] then 13 | goto NEXT 14 | end 15 | end 16 | do return false end 17 | ::NEXT:: 18 | end 19 | return true 20 | end 21 | 22 | function TEST(script) 23 | local newScript, catched = catch(script, '!?~') 24 | files.setText(TESTURI, newScript) 25 | 26 | local input = catched['?'] + catched['~'] 27 | local expect = catched['!'] + catched['~'] 28 | local results = core(TESTURI, input[1][1], true) 29 | if results then 30 | local positions = {} 31 | for i, result in ipairs(results) do 32 | positions[i] = { result.target.start, result.target.finish } 33 | end 34 | assert(founded(expect, positions)) 35 | else 36 | assert(#expect == 0) 37 | end 38 | files.remove(TESTURI) 39 | end 40 | 41 | require 'references.common' 42 | require 'references.all' 43 | -------------------------------------------------------------------------------- /test/tclient/init.lua: -------------------------------------------------------------------------------- 1 | require 'tclient.tests.single-mode' 2 | require 'tclient.tests.library-ignore-limit' 3 | require 'tclient.tests.multi-workspace' 4 | require 'tclient.tests.folders-with-single-file' 5 | require 'tclient.tests.load-library' 6 | require 'tclient.tests.files-associations' 7 | require 'tclient.tests.resolve-completion' 8 | require 'tclient.tests.hover-pairs' 9 | require 'tclient.tests.change-workspace-folder' 10 | require 'tclient.tests.jump-source' 11 | require 'tclient.tests.load-relative-library' 12 | require 'tclient.tests.hover-set-local' 13 | require 'tclient.tests.same-prefix' 14 | require 'tclient.tests.recursive-runner' 15 | require 'tclient.tests.modify-luarc' 16 | 17 | require 'tclient.tests.performance-jass-common' 18 | require 'tclient.tests.build-meta' 19 | -------------------------------------------------------------------------------- /test/tclient/tests/build-meta.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local util = require 'utility' 3 | local ws = require 'workspace' 4 | local json = require 'json' 5 | 6 | ---@async 7 | lclient():start(function (client) 8 | client:registerFakers() 9 | 10 | client:initialize() 11 | 12 | local text = util.loadFile((ROOT / 'test' / 'example' / 'meta.json'):string()) 13 | local meta = json.decode(text) 14 | 15 | client:notify('$/api/report', meta) 16 | 17 | ws.awaitReady() 18 | end) 19 | -------------------------------------------------------------------------------- /test/tclient/tests/files-associations.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local ws = require 'workspace' 3 | local files = require 'files' 4 | local furi = require 'file-uri' 5 | local util = require 'utility' 6 | local fs = require 'bee.filesystem' 7 | 8 | local rootPath = LOGPATH .. '/files-associations' 9 | local rootUri = furi.encode(rootPath) 10 | 11 | fs.create_directories(fs.path(rootPath)) 12 | 13 | local filePath = rootPath .. '/test.lua.txt' 14 | local fileUri = furi.encode(filePath) 15 | util.saveFile(filePath, '') 16 | 17 | ---@async 18 | lclient():start(function (client) 19 | client:registerFakers() 20 | 21 | client:register('workspace/configuration', function () 22 | return { 23 | {}, 24 | { 25 | ["*.lua.txt"] = "lua", 26 | } 27 | } 28 | end) 29 | 30 | client:initialize { 31 | rootPath = rootPath, 32 | rootUri = rootUri, 33 | workspaceFolders = { 34 | { 35 | name = 'ws', 36 | uri = rootUri, 37 | }, 38 | } 39 | } 40 | 41 | ws.awaitReady(rootUri) 42 | 43 | assert(files.isLua(furi.encode 'aaa.lua.txt') == true) 44 | assert(files.isLua(furi.encode '/aaa.lua.txt') == true) 45 | assert(files.isLua(furi.encode 'D:\\aaa.lua.txt') == true) 46 | assert(files.isLua(fileUri) == true) 47 | assert(files.exists(fileUri) == true) 48 | end) 49 | -------------------------------------------------------------------------------- /test/tclient/tests/folders-with-single-file.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local fs = require 'bee.filesystem' 3 | local util = require 'utility' 4 | local furi = require 'file-uri' 5 | local ws = require 'workspace' 6 | local files = require 'files' 7 | local scope = require 'workspace.scope' 8 | 9 | ---@async 10 | lclient():start(function (client) 11 | client:registerFakers() 12 | 13 | client:initialize { 14 | rootUri = 'abc', 15 | } 16 | 17 | client:notify('textDocument/didOpen', { 18 | textDocument = { 19 | uri = furi.encode('abc/1.lua'), 20 | languageId = 'lua', 21 | version = 0, 22 | text = [[ 23 | local x 24 | print(x) 25 | ]] 26 | } 27 | }) 28 | 29 | ws.awaitReady('abc') 30 | 31 | local locations = client:awaitRequest('textDocument/definition', { 32 | textDocument = { uri = furi.encode('abc/1.lua') }, 33 | position = { line = 1, character = 7 }, 34 | }) 35 | 36 | assert(util.equal(locations, { 37 | { 38 | uri = furi.encode('abc/1.lua'), 39 | range = { 40 | start = { line = 0, character = 6 }, 41 | ['end'] = { line = 0, character = 7 }, 42 | } 43 | } 44 | })) 45 | 46 | client:notify('textDocument/didOpen', { 47 | textDocument = { 48 | uri = 'file://single-file.lua', 49 | languageId = 'lua', 50 | version = 0, 51 | text = [[ 52 | local x 53 | print(x) 54 | ]] 55 | } 56 | }) 57 | 58 | ws.awaitReady(nil) 59 | 60 | local locations = client:awaitRequest('textDocument/definition', { 61 | textDocument = { uri = 'file://single-file.lua' }, 62 | position = { line = 1, character = 0 }, 63 | }) 64 | 65 | assert(#locations > 0) 66 | end) 67 | -------------------------------------------------------------------------------- /test/tclient/tests/hover-pairs.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local ws = require 'workspace' 3 | local await = require 'await' 4 | 5 | ---@async 6 | lclient():start(function (client) 7 | client:registerFakers() 8 | client:initialize() 9 | 10 | client:notify('textDocument/didOpen', { 11 | textDocument = { 12 | uri = 'file://test.lua', 13 | languageId = 'lua', 14 | version = 0, 15 | text = [[ 16 | local t ---@type table 17 | for key, value in pairs(t) do 18 | print(key, value) --key or value is unknown 19 | end 20 | ]] 21 | } 22 | }) 23 | 24 | ws.awaitReady() 25 | 26 | await.sleep(0.1) 27 | 28 | local hover1 = client:awaitRequest('textDocument/hover', { 29 | textDocument = { uri = 'file://test.lua' }, 30 | position = { line = 2, character = 11 }, 31 | }) 32 | 33 | local hover2 = client:awaitRequest('textDocument/hover', { 34 | textDocument = { uri = 'file://test.lua' }, 35 | position = { line = 2, character = 17 }, 36 | }) 37 | 38 | assert(hover1.contents.value:find 'string') 39 | assert(hover2.contents.value:find 'number') 40 | end) 41 | -------------------------------------------------------------------------------- /test/tclient/tests/hover-set-local.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local ws = require 'workspace' 3 | local await = require 'await' 4 | 5 | ---@async 6 | lclient():start(function (client) 7 | client:registerFakers() 8 | client:initialize() 9 | 10 | client:notify('textDocument/didOpen', { 11 | textDocument = { 12 | uri = 'file://test.lua', 13 | languageId = 'lua', 14 | version = 0, 15 | text = [[ 16 | ---@class Class 17 | local m 18 | 19 | ---@return Class 20 | function m:f() end 21 | 22 | ---@type Class 23 | local v 24 | v = v:f() 25 | ]] 26 | } 27 | }) 28 | 29 | ws.awaitReady() 30 | 31 | await.sleep(0.1) 32 | 33 | local hover1 = client:awaitRequest('textDocument/hover', { 34 | textDocument = { uri = 'file://test.lua' }, 35 | position = { line = 7, character = 6 }, 36 | }) 37 | 38 | local hover2 = client:awaitRequest('textDocument/hover', { 39 | textDocument = { uri = 'file://test.lua' }, 40 | position = { line = 8, character = 0 }, 41 | }) 42 | 43 | assert(hover1.contents.value:find 'Class') 44 | assert(hover2.contents.value:find 'Class') 45 | end) 46 | -------------------------------------------------------------------------------- /test/tclient/tests/library-ignore-limit.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local util = require 'utility' 3 | local ws = require 'workspace' 4 | local files = require 'files' 5 | local furi = require 'file-uri' 6 | local fs = require 'bee.filesystem' 7 | 8 | local libraryPath = LOGPATH .. '/large-file-library' 9 | local largeFilePath = LOGPATH .. '/large-file-library/large-file.lua' 10 | 11 | ---@async 12 | lclient():start(function (client) 13 | client:registerFakers() 14 | 15 | client:register('workspace/configuration', function () 16 | return { 17 | { 18 | ['workspace.library'] = { libraryPath } 19 | }, 20 | } 21 | end) 22 | 23 | fs.create_directories(fs.path(libraryPath)) 24 | if not fs.exists(fs.path(largeFilePath)) then 25 | util.saveFile(largeFilePath, string.rep('--this is a large file\n', 100000)) 26 | end 27 | 28 | client:initialize() 29 | 30 | ws.awaitReady() 31 | 32 | assert(files.getState(furi.encode(largeFilePath)) ~= nil) 33 | end) 34 | -------------------------------------------------------------------------------- /test/tclient/tests/load-library.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local util = require 'utility' 3 | local ws = require 'workspace' 4 | local files = require 'files' 5 | local furi = require 'file-uri' 6 | local fs = require 'bee.filesystem' 7 | 8 | local libraryPath = LOGPATH .. '/load-library' 9 | local libraryFilePath = LOGPATH .. '/load-library/library-file.lua' 10 | 11 | ---@async 12 | lclient():start(function (client) 13 | client:registerFakers() 14 | 15 | client:register('workspace/configuration', function () 16 | return { 17 | { 18 | ['workspace.library'] = { libraryPath } 19 | }, 20 | } 21 | end) 22 | 23 | fs.create_directories(fs.path(libraryPath)) 24 | if not fs.exists(fs.path(libraryFilePath)) then 25 | util.saveFile(libraryFilePath, 'LIBRARY_FILE = true') 26 | end 27 | 28 | client:initialize() 29 | 30 | client:notify('textDocument/didOpen', { 31 | textDocument = { 32 | uri = furi.encode('abc/1.lua'), 33 | languageId = 'lua', 34 | version = 0, 35 | text = [[ 36 | require 'library-file' 37 | print(LIBRARY_FILE) 38 | ]] 39 | } 40 | }) 41 | 42 | ws.awaitReady() 43 | 44 | local locations = client:awaitRequest('textDocument/definition', { 45 | textDocument = { uri = furi.encode('abc/1.lua') }, 46 | position = { line = 0, character = 10 }, 47 | }) 48 | 49 | assert(util.equal(locations, { 50 | { 51 | uri = furi.encode(libraryFilePath), 52 | range = { 53 | start = { line = 0, character = 0 }, 54 | ['end'] = { line = 0, character = 0 }, 55 | } 56 | } 57 | })) 58 | end) 59 | -------------------------------------------------------------------------------- /test/tclient/tests/load-relative-library.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local util = require 'utility' 3 | local ws = require 'workspace' 4 | local files = require 'files' 5 | local furi = require 'file-uri' 6 | local fs = require 'bee.filesystem' 7 | 8 | local workspacePath = LOGPATH .. '/not-exist/not-exist/not-exist' 9 | local libraryPath = '../../../load-relative-library' 10 | local libraryFilePath = LOGPATH .. '/load-relative-library/library-file.lua' 11 | 12 | ---@async 13 | lclient():start(function (client) 14 | client:registerFakers() 15 | 16 | client:initialize { 17 | rootUri = furi.encode(workspacePath), 18 | } 19 | 20 | client:register('workspace/configuration', function () 21 | return { 22 | { 23 | ['workspace.library'] = { libraryPath } 24 | }, 25 | } 26 | end) 27 | 28 | fs.create_directories(fs.path(libraryFilePath):parent_path()) 29 | if not fs.exists(fs.path(libraryFilePath)) then 30 | util.saveFile(libraryFilePath, 'LIBRARY_FILE = true') 31 | end 32 | 33 | client:initialize() 34 | 35 | client:notify('textDocument/didOpen', { 36 | textDocument = { 37 | uri = furi.encode(workspacePath .. '/abc/1.lua'), 38 | languageId = 'lua', 39 | version = 0, 40 | text = [[ 41 | require 'library-file' 42 | print(LIBRARY_FILE) 43 | ]] 44 | } 45 | }) 46 | 47 | ws.awaitReady() 48 | 49 | local locations = client:awaitRequest('textDocument/definition', { 50 | textDocument = { uri = furi.encode(workspacePath .. '/abc/1.lua') }, 51 | position = { line = 0, character = 10 }, 52 | }) 53 | 54 | assert(util.equal(locations, { 55 | { 56 | uri = furi.encode(libraryFilePath), 57 | range = { 58 | start = { line = 0, character = 0 }, 59 | ['end'] = { line = 0, character = 0 }, 60 | } 61 | } 62 | })) 63 | end) 64 | -------------------------------------------------------------------------------- /test/tclient/tests/resolve-completion.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local ws = require 'workspace' 3 | local util = require 'utility' 4 | 5 | ---@async 6 | lclient():start(function (client) 7 | client:registerFakers() 8 | client:initialize() 9 | 10 | client:notify('textDocument/didOpen', { 11 | textDocument = { 12 | uri = 'file://test.lua', 13 | languageId = 'lua', 14 | version = 0, 15 | text = [[ 16 | ---@type integer 17 | local xxxx 18 | 19 | x 20 | ]] 21 | } 22 | }) 23 | 24 | ws.awaitReady() 25 | 26 | local completions = client:awaitRequest('textDocument/completion', { 27 | textDocument = { uri = 'file://test.lua' }, 28 | position = { line = 3, character = 1 }, 29 | }) 30 | 31 | client:awaitRequest('textDocument/didChange', 32 | { 33 | textDocument = { uri = 'file://test.lua' }, 34 | contentChanges = { 35 | { 36 | range = { 37 | start = { line = 3, character = 1 }, 38 | ['end'] = { line = 3, character = 1 }, 39 | }, 40 | text = 'x' 41 | } 42 | } 43 | }) 44 | 45 | local targetItem 46 | for _, item in ipairs(completions.items) do 47 | if item.label == 'xxxx' then 48 | targetItem = item 49 | break 50 | end 51 | end 52 | 53 | assert(targetItem ~= nil) 54 | 55 | local newItem = client:awaitRequest('completionItem/resolve', targetItem) 56 | 57 | assert(newItem.detail == 'integer') 58 | end) 59 | -------------------------------------------------------------------------------- /test/tclient/tests/same-prefix.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local fs = require 'bee.filesystem' 3 | local util = require 'utility' 4 | local furi = require 'file-uri' 5 | local ws = require 'workspace' 6 | local files = require 'files' 7 | local scope = require 'workspace.scope' 8 | 9 | local rootPath = LOGPATH .. '/same-prefix' 10 | local rootUri = furi.encode(rootPath) 11 | 12 | for _, name in ipairs { 'ws', 'ws1' } do 13 | fs.create_directories(fs.path(rootPath .. '/' .. name)) 14 | end 15 | 16 | ---@async 17 | lclient():start(function (client) 18 | client:registerFakers() 19 | 20 | client:initialize { 21 | rootPath = rootPath, 22 | rootUri = rootUri, 23 | workspaceFolders = { 24 | { 25 | name = 'ws', 26 | uri = rootUri .. '/ws', 27 | }, 28 | { 29 | name = 'ws1', 30 | uri = rootUri .. '/ws1', 31 | }, 32 | } 33 | } 34 | 35 | ws.awaitReady(rootUri .. '/ws') 36 | ws.awaitReady(rootUri .. '/ws1') 37 | 38 | files.setText(rootUri .. '/ws1/test.lua', [[ 39 | ]]) 40 | 41 | files.setText(rootUri .. '/ws/main.lua', [[ 42 | require '' 43 | ]]) 44 | 45 | local comps1 = client:awaitRequest('textDocument/completion', { 46 | textDocument = { 47 | uri = rootUri .. '/ws/main.lua', 48 | }, 49 | position = { 50 | line = 0, 51 | character = 9, 52 | }, 53 | }) 54 | for _, item in ipairs(comps1.items) do 55 | assert(item.label ~= 'test') 56 | end 57 | end) 58 | -------------------------------------------------------------------------------- /test/tclient/tests/single-mode.lua: -------------------------------------------------------------------------------- 1 | local lclient = require 'lclient' 2 | local ws = require 'workspace' 3 | local util = require 'utility' 4 | 5 | ---@async 6 | lclient():start(function (client) 7 | client:registerFakers() 8 | client:initialize() 9 | 10 | client:notify('textDocument/didOpen', { 11 | textDocument = { 12 | uri = 'file:///single-file.lua', 13 | languageId = 'lua', 14 | version = 0, 15 | text = [[ 16 | local x 17 | print(x) 18 | 19 | TEST = 1 20 | print(TEST) 21 | ]] 22 | } 23 | }) 24 | 25 | ws.awaitReady() 26 | 27 | local locations = client:awaitRequest('textDocument/definition', { 28 | textDocument = { uri = 'file:///single-file.lua' }, 29 | position = { line = 1, character = 7 }, 30 | }) 31 | 32 | assert(util.equal(locations, { 33 | { 34 | uri = 'file:///single-file.lua', 35 | range = { 36 | start = { line = 0, character = 6 }, 37 | ['end'] = { line = 0, character = 7 }, 38 | } 39 | } 40 | })) 41 | 42 | local locations = client:awaitRequest('textDocument/definition', { 43 | textDocument = { uri = 'file:///single-file.lua' }, 44 | position = { line = 1, character = 0 }, 45 | }) 46 | 47 | assert(#locations > 0) 48 | 49 | local locations = client:awaitRequest('textDocument/definition', { 50 | textDocument = { uri = 'file:///single-file.lua' }, 51 | position = { line = 3, character = 0 }, 52 | }) 53 | 54 | assert(util.equal(locations, { 55 | { 56 | uri = 'file:///single-file.lua', 57 | range = { 58 | start = { line = 3, character = 0 }, 59 | ['end'] = { line = 3, character = 4 }, 60 | } 61 | } 62 | })) 63 | end) 64 | -------------------------------------------------------------------------------- /tools/build-3rd-meta.lua: -------------------------------------------------------------------------------- 1 | package.path = package.path .. ';script/?.lua;tools/?.lua' 2 | 3 | dofile 'tools/love-api.lua' 4 | dofile 'tools/lovr-api.lua' 5 | dofile 'tools/normalize-3rd-path.lua' 6 | -------------------------------------------------------------------------------- /tools/normalize-3rd-path.lua: -------------------------------------------------------------------------------- 1 | local fs = require 'bee.filesystem' 2 | local fsu = require 'fs-utility' 3 | 4 | local thirdPath = fs.path 'meta/3rd' 5 | 6 | for dir in fs.pairs(thirdPath) do 7 | local libraryPath = dir / 'library' 8 | if fs.is_directory(libraryPath) then 9 | fsu.scanDirectory(libraryPath, function (fullPath) 10 | if fullPath:stem():string():find '%.' then 11 | local newPath = fullPath:parent_path() / (fullPath:stem():string():gsub('%.', '/') .. '.lua') 12 | fs.create_directories(newPath:parent_path()) 13 | fs.rename(fullPath, newPath) 14 | end 15 | end) 16 | end 17 | end 18 | --------------------------------------------------------------------------------