diff options
author | 最萌小汐 <sumneko@hotmail.com> | 2024-08-15 15:16:03 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-15 15:16:03 +0800 |
commit | 2c798703ca854d670fb28f51adc85c2b41f08f37 (patch) | |
tree | aa192cf6d6ed02d02dda8c8b7a4c240dded64efb | |
parent | abd5daae1885cdf7a9e21a1cbdfea945385124fc (diff) | |
parent | b71cb7aecd9337c9463a4dfbdb9d06cac7b825fd (diff) | |
download | lua-language-server-2c798703ca854d670fb28f51adc85c2b41f08f37.zip |
Merge branch 'master' into cast-table-to-class
32 files changed, 952 insertions, 483 deletions
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..b39e5efb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: true +contact_links: + - name: Report LuaLS.github.io issues + url: https://github.com/LuaLS/LuaLS.github.io/issues + about: Please report issues regarding our website (and the documentation on it) in the website repository. diff --git a/.github/ISSUE_TEMPLATE/doc-report.yml b/.github/ISSUE_TEMPLATE/doc-report.yml new file mode 100644 index 00000000..3f0fa2b7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/doc-report.yml @@ -0,0 +1,34 @@ +name: Documentation Report +description: Report an issue with documentation in the lua-language-server. +labels: + - documentation +assignees: + - carsakiller +body: + - type: markdown + attributes: + value: > + For issues with our [website](https://luals.github.io) and its wiki, + please visit the [correct repository](https://github.com/LuaLS/LuaLS.github.io/issues). + - type: markdown + attributes: + value: > + **Please check for similar issues before continuing!** + - type: textarea + id: expected + attributes: + label: Issue Description + description: Please describe the documentation issue. Is something incorrect, missing or improveable? + validations: + required: true + - type: textarea + id: additional-notes + attributes: + label: Additional Notes + description: > + Please provide any additional notes, context, + and media you have. + - type: markdown + attributes: + value: | + Thank you very much for helping improve the language server! ❤️ diff --git a/changelog.md b/changelog.md index c3655b48..d9c15e7b 100644 --- a/changelog.md +++ b/changelog.md @@ -2,18 +2,44 @@ ## Unreleased <!-- Add all new changes here. They will be moved under a version at release --> +* `NEW` Add matching checks between the shape of tables and classes, during type checking. [#2768](https://github.com/LuaLS/lua-language-server/pull/2768 +* `FIX` Error `attempt to index a nil value` when `Lua.hint.semicolon == 'All'` [#2788](https://github.com/LuaLS/lua-language-server/issues/2788) + +## 3.10.3 +`2024-8-8` +* `FIX` Memory leak with `---@enum(partical)` + +## 3.10.2 +`2024-8-7` +* `NEW` Add support for binary metamethod on right operand [#2777](https://github.com/LuaLS/lua-language-server/pull/2777) +* `FIX` Incorrect indentation fixing in some case + +## 3.10.1 +`2024-8-2` +* `FIX` Runtime error +* `FIX` Disable indentation fixing for Non-VSCode + +## 3.10.0 +`2024-8-1` * `NEW` Add postfix snippet for `unpack` -* `FIX` `diagnostics.severity` defaulting to "Warning" when run using `--check` [#2730](https://github.com/LuaLS/lua-language-server/issues/2730) * `NEW` Add support for lambda style functions, `|paramList| expr` is syntactic sugar for `function(paramList) return expr end` -* `FIX` Respect `completion.showParams` config for local function completion +* `NEW` Added lua regular expression support for `Lua.doc.<scope>Name` [#2753](https://github.com/LuaLS/lua-language-server/pull/2753) +* `NEW` You can now click on "References" in CodeLen to display the reference list(VSCode) +* `NEW` Improved behavior for inserting new lines: + + When inside an annotation, an annotation tag will be added at the beginning of the line (VSCode). + + When between `function () end` or similar constructs, the format will be adjusted to a more reasonable one (VSCode) and leading/trailing spaces will be removed (generic). + + Attempts to semantically fix improper indentation (generic). * `CHG` Improve performance of multithreaded `--check` and `undefined-field` diagnostic +* `CHG` Change spacing of parameter inlay hints to match other LSPs, like `rust-analyzer` +* `FIX` `diagnostics.severity` defaulting to "Warning" when run using `--check` [#2730](https://github.com/LuaLS/lua-language-server/issues/2730) +* `FIX` Respect `completion.showParams` config for local function completion * `FIX` Addons can now self-recommend as expected. Fixed by correcting the `wholeMatch` function * `FIX` Now correctly evaluates the visibility of fields in a class when they are defined directly in the object. use for completion and invisible dianostic. [#2752](https://github.com/LuaLS/lua-language-server/issues/2752) -* `NEW` added lua regular expression support for Lua.doc.<scope>Name [#2753](https://github.com/LuaLS/lua-language-server/pull/2753) * `FIX` Bad triggering of the `inject-field` diagnostic, when the fields are declared at the creation of the object [#2746](https://github.com/LuaLS/lua-language-server/issues/2746) -* `CHG` Change spacing of parameter inlay hints to match other LSPs, like `rust-analyzer` * `FIX` Inconsistent type narrow behavior of function call args [#2758](https://github.com/LuaLS/lua-language-server/issues/2758) -* `NEW` Add matching checks between the shape of tables and classes, during type checking. [#2768](https://github.com/LuaLS/lua-language-server/pull/2768 +* `FIX` Improve the `missing-fields` logic to be able to correctly handle classes defined several times [#22770](https://github.com/LuaLS/lua-language-server/pull/2770) +* `FIX` Typos in annotation descriptions +* `FIX` incorrect `CompletionItemKind` for postfix snippets [#2773](https://github.com/LuaLS/lua-language-server/pull/2773) ## 3.9.3 `2024-6-11` diff --git a/locale/en-us/script.lua b/locale/en-us/script.lua index 6fc488d8..cf2fbe8e 100644 --- a/locale/en-us/script.lua +++ b/locale/en-us/script.lua @@ -1167,7 +1167,7 @@ Provide type declaration for [operator metamethods](http://lua-users.org/wiki/Me ### Vector Add Metamethod ``` ---@class Vector ----@operation add(Vector):Vector +---@operator add(Vector):Vector vA = Vector.new(1, 2, 3) vB = Vector.new(10, 20, 30) @@ -1178,7 +1178,7 @@ vC = vA + vB ### Unary Minus ``` ---@class Passcode ----@operation unm:integer +---@operator unm:integer pA = Passcode.new(1234) pB = -pA diff --git a/locale/pt-br/script.lua b/locale/pt-br/script.lua index 468812cc..50568aeb 100644 --- a/locale/pt-br/script.lua +++ b/locale/pt-br/script.lua @@ -1167,7 +1167,7 @@ Provide type declaration for [operator metamethods](http://lua-users.org/wiki/Me ### Vector Add Metamethod ``` ---@class Vector ----@operation add(Vector):Vector +---@operator add(Vector):Vector vA = Vector.new(1, 2, 3) vB = Vector.new(10, 20, 30) @@ -1178,7 +1178,7 @@ vC = vA + vB ### Unary Minus ``` ---@class Passcode ----@operation unm:integer +---@operator unm:integer pA = Passcode.new(1234) pB = -pA @@ -1216,7 +1216,7 @@ setColor(colors.green) LUADOC_DESC_SOURCE = -- TODO: need translate! [=[ Provide a reference to some source code which lives in another file. When -searching for the defintion of an item, its `@source` will be used. +searching for the definition of an item, its `@source` will be used. ## Syntax `@source <path>` @@ -1264,7 +1264,7 @@ end LUADOC_DESC_PRIVATE = -- TODO: need translate! [=[ Mark a function as private to a @class. Private functions can be accessed only -from within their class and are not accessable from child classes. +from within their class and are not accessible from child classes. ## Syntax `@private` diff --git a/locale/zh-cn/script.lua b/locale/zh-cn/script.lua index a4d20628..9cea601a 100644 --- a/locale/zh-cn/script.lua +++ b/locale/zh-cn/script.lua @@ -1167,7 +1167,7 @@ Provide type declaration for [operator metamethods](http://lua-users.org/wiki/Me ### Vector Add Metamethod ``` ---@class Vector ----@operation add(Vector):Vector +---@operator add(Vector):Vector vA = Vector.new(1, 2, 3) vB = Vector.new(10, 20, 30) @@ -1178,7 +1178,7 @@ vC = vA + vB ### Unary Minus ``` ---@class Passcode ----@operation unm:integer +---@operator unm:integer pA = Passcode.new(1234) pB = -pA @@ -1216,7 +1216,7 @@ setColor(colors.green) LUADOC_DESC_SOURCE = -- TODO: need translate! [=[ Provide a reference to some source code which lives in another file. When -searching for the defintion of an item, its `@source` will be used. +searching for the definition of an item, its `@source` will be used. ## Syntax `@source <path>` @@ -1264,7 +1264,7 @@ end LUADOC_DESC_PRIVATE = -- TODO: need translate! [=[ Mark a function as private to a @class. Private functions can be accessed only -from within their class and are not accessable from child classes. +from within their class and are not accessible from child classes. ## Syntax `@private` diff --git a/locale/zh-tw/script.lua b/locale/zh-tw/script.lua index c17c41fb..1feaf2ad 100644 --- a/locale/zh-tw/script.lua +++ b/locale/zh-tw/script.lua @@ -1161,7 +1161,7 @@ Provide type declaration for [operator metamethods](http://lua-users.org/wiki/Me ### Vector Add Metamethod ``` ---@class Vector ----@operation add(Vector):Vector +---@operator add(Vector):Vector vA = Vector.new(1, 2, 3) vB = Vector.new(10, 20, 30) @@ -1172,7 +1172,7 @@ vC = vA + vB ### Unary Minus ``` ---@class Passcode ----@operation unm:integer +---@operator unm:integer pA = Passcode.new(1234) pB = -pA @@ -1210,7 +1210,7 @@ setColor(colors.green) LUADOC_DESC_SOURCE = -- TODO: need translate! [=[ Provide a reference to some source code which lives in another file. When -searching for the defintion of an item, its `@source` will be used. +searching for the definition of an item, its `@source` will be used. ## Syntax `@source <path>` @@ -1258,7 +1258,7 @@ end LUADOC_DESC_PRIVATE = -- TODO: need translate! [=[ Mark a function as private to a @class. Private functions can be accessed only -from within their class and are not accessable from child classes. +from within their class and are not accessible from child classes. ## Syntax `@private` diff --git a/script/cli/check.lua b/script/cli/check.lua index 8b314f24..c3aac0e3 100644 --- a/script/cli/check.lua +++ b/script/cli/check.lua @@ -8,7 +8,12 @@ local util = require 'utility' local numThreads = tonumber(NUM_THREADS or 1) -local exe = arg[-1] +local exe +local minIndex = -1 +while arg[minIndex] do + exe = arg[minIndex] + minIndex = minIndex - 1 +end -- TODO: is this necessary? got it from the shell.lua helper in bee.lua tests if platform.os == 'windows' and not exe:match('%.[eE][xX][eE]$') then exe = exe..'.exe' diff --git a/script/core/code-lens.lua b/script/core/code-lens.lua index bc39ec86..bebfeedf 100644 --- a/script/core/code-lens.lua +++ b/script/core/code-lens.lua @@ -4,6 +4,7 @@ local await = require 'await' local conv = require 'proto.converter' local getRef = require 'core.reference' local lang = require 'language' +local client = require 'client' ---@class parser.state ---@field package _codeLens? codeLens @@ -88,12 +89,35 @@ end function mt:resolveReference(source) local refs = getRef(self.uri, source.finish, false) local count = refs and #refs or 0 - local command = conv.command( - lang.script('COMMAND_REFERENCE_COUNT', count), - '', - {} - ) - return command + if client.getOption('codeLensViewReferences') then + local locations = {} + for _, ref in ipairs(refs or {}) do + local state = files.getState(ref.uri) + if state then + locations[#locations+1] = conv.location( + ref.uri, + conv.packRange(state, ref.target.start, ref.target.finish) + ) + end + end + local command = conv.command( + lang.script('COMMAND_REFERENCE_COUNT', count), + 'lua.showReferences', + { + self.uri, + conv.packPosition(self.state, source.start), + locations, + } + ) + return command + else + local command = conv.command( + lang.script('COMMAND_REFERENCE_COUNT', count), + '', + {} + ) + return command + end end ---@async diff --git a/script/core/completion/postfix.lua b/script/core/completion/postfix.lua index b5f33315..46c24b8e 100644 --- a/script/core/completion/postfix.lua +++ b/script/core/completion/postfix.lua @@ -353,7 +353,7 @@ local function checkPostFix(state, word, wordPosition, position, symbol, results end):gsub('%$%{?%d+%}?', '') results[#results+1] = { label = action.key, - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, description = markdown() : add('lua', descText) : string(), diff --git a/script/core/diagnostics/missing-fields.lua b/script/core/diagnostics/missing-fields.lua index 210920fd..5ce650ec 100644 --- a/script/core/diagnostics/missing-fields.lua +++ b/script/core/diagnostics/missing-fields.lua @@ -15,61 +15,75 @@ return function (uri, callback) guide.eachSourceType(state.ast, 'table', function (src) await.delay() + vm.removeNode(src) -- the node is not updated correctly, reason still unknown local defs = vm.getDefs(src) + local sortedDefs = {} for _, def in ipairs(defs) do - if def.type == 'doc.class' and def.bindSource then - if guide.isInRange(def.bindSource, src.start) then + if def.type == 'doc.class' then + if def.bindSource and guide.isInRange(def.bindSource, src.start) then return end + local className = def.class[1] + if not sortedDefs[className] then + sortedDefs[className] = {} + end + local samedefs = sortedDefs[className] + samedefs[#samedefs+1] = def end if def.type == 'doc.type.array' or def.type == 'doc.type.table' then return end end + + local myKeys local warnings = {} - for _, def in ipairs(defs) do - if def.type == 'doc.class' then - if not def.fields then - return + for className, samedefs in pairs(sortedDefs) do + local missedKeys = {} + for _, def in ipairs(samedefs) do + if not def.fields or #def.fields == 0 then + goto continue + end + + if not myKeys then + myKeys = {} + for _, field in ipairs(src) do + local key = vm.getKeyName(field) or field.tindex + if key then + myKeys[key] = true + end + end end - local requiresKeys = {} for _, field in ipairs(def.fields) do if not field.optional and not vm.compileNode(field):isNullable() then local key = vm.getKeyName(field) - if key and not requiresKeys[key] then - requiresKeys[key] = true - requiresKeys[#requiresKeys+1] = key + if not key then + local fieldnode = vm.compileNode(field.field)[1] + if fieldnode and fieldnode.type == 'doc.type.integer' then + ---@cast fieldnode parser.object + key = vm.getKeyName(fieldnode) + end end - end - end - if #requiresKeys == 0 then - return - end - local myKeys = {} - for _, field in ipairs(src) do - local key = vm.getKeyName(field) - if key then - myKeys[key] = true - end - end - - local missedKeys = {} - for _, key in ipairs(requiresKeys) do - if not myKeys[key] then - missedKeys[#missedKeys+1] = ('`%s`'):format(key) + if key and not myKeys[key] then + if type(key) == "number" then + missedKeys[#missedKeys+1] = ('`[%s]`'):format(key) + else + missedKeys[#missedKeys+1] = ('`%s`'):format(key) + end + end end end + ::continue:: + end - if #missedKeys == 0 then - return - end - - warnings[#warnings+1] = lang.script('DIAG_MISSING_FIELDS', def.class[1], table.concat(missedKeys, ', ')) + if #missedKeys == 0 then + return end + + warnings[#warnings+1] = lang.script('DIAG_MISSING_FIELDS', className, table.concat(missedKeys, ', ')) end if #warnings == 0 then diff --git a/script/core/diagnostics/unused-function.lua b/script/core/diagnostics/unused-function.lua index a873375f..1145036d 100644 --- a/script/core/diagnostics/unused-function.lua +++ b/script/core/diagnostics/unused-function.lua @@ -5,6 +5,7 @@ local define = require 'proto.define' local lang = require 'language' local await = require 'await' local client = require 'client' +local util = require 'utility' local function isToBeClosed(source) if not source.attrs then @@ -105,8 +106,11 @@ return function (uri, callback) turnBlack(source, black, white, links) end + local tagSupports = client.getAbility('textDocument.completion.completionItem.tagSupport.valueSet') + local supportUnnecessary = tagSupports and util.arrayHas(tagSupports, define.DiagnosticTag.Unnecessary) + for source in pairs(white) do - if client.isVSCode() then + if supportUnnecessary then callback { start = source.start, finish = source.finish, diff --git a/script/core/fix-indent.lua b/script/core/fix-indent.lua new file mode 100644 index 00000000..59adfb7b --- /dev/null +++ b/script/core/fix-indent.lua @@ -0,0 +1,211 @@ +local files = require 'files' +local guide = require 'parser.guide' +local proto = require 'proto.proto' +local lookBackward = require 'core.look-backward' +local util = require 'utility' +local client = require 'client' + +---@param state parser.state +---@param change table +local function removeSpacesAfterEnter(state, change) + if not change.text:match '^\r?\n[\t ]+\r?\n$' then + return false + end + local lines = state.originLines or state.lines + local text = state.originText or state.lua + ---@cast text -? + + local edits = {} + -- 清除前置空格 + local startPos = guide.positionOf(change.range.start.line, change.range.start.character) + local startOffset = guide.positionToOffsetByLines(lines, startPos) + local leftOffset + for offset = startOffset, lines[change.range.start.line], -1 do + leftOffset = offset + local char = text:sub(offset, offset) + if char ~= ' ' and char ~= '\t' then + break + end + end + + if leftOffset and leftOffset < startOffset then + edits[#edits+1] = { + start = leftOffset, + finish = startOffset, + text = '', + } + end + + -- 清除后置空格 + local endOffset = startOffset + #change.text + local _, rightOffset = text:find('^[\t ]+', endOffset + 1) + if rightOffset then + edits[#edits+1] = { + start = endOffset, + finish = rightOffset, + text = '', + } + end + + if #edits == 0 then + return nil + end + + return edits +end + +local function getIndent(state, row) + local offset = state.lines[row] + local indent = state.lua:match('^[\t ]*', offset) + return indent +end + +local function isInBlock(state, position) + local block = guide.eachSourceContain(state.ast, position, function(source) + if source.type == 'ifblock' + or source.type == 'elseifblock' then + if source.keyword[4] and source.keyword[4] <= position then + return true + end + end + if source.type == 'else' then + if source.keyword[2] and source.keyword[2] <= position then + return true + end + end + if source.type == 'while' then + if source.keyword[4] and source.keyword[4] <= position then + return true + end + end + if source.type == 'repeat' then + if source.keyword[2] and source.keyword[2] <= position then + return true + end + end + if source.type == 'loop' then + if source.keyword[4] and source.keyword[4] <= position then + return true + end + end + if source.type == 'in' then + if source.keyword[6] and source.keyword[6] <= position then + return true + end + end + if source.type == 'do' then + if source.keyword[2] and source.keyword[2] <= position then + return true + end + end + if source.type == 'function' then + if source.args and source.args.finish <= position then + return true + end + if not source.keyword[3] or source.keyword[3] >= position then + return true + end + end + if source.type == 'table' then + if source.start + 1 == position then + return true + end + end + end) + return block ~= nil +end + +local function fixWrongIndent(state, change) + if not change.text:match '^\r?\n[\t ]+$' then + return false + end + local position = guide.positionOf(change.range.start.line, change.range.start.character) + local row = guide.rowColOf(position) + local myIndent = getIndent(state, row + 1) + local lastOffset = lookBackward.findAnyOffset(state.lua, guide.positionToOffset(state, position)) + if not lastOffset then + return + end + local lastPosition = guide.offsetToPosition(state, lastOffset) + local lastRow = guide.rowColOf(lastPosition) + local lastIndent = getIndent(state, lastRow) + if #myIndent <= #lastIndent then + return + end + if not util.stringStartWith(myIndent, lastIndent) then + return + end + if isInBlock(state, lastPosition) then + return + end + + local endPosition = guide.positionOf(change.range.start.line + 1, #myIndent) + local endOffset = guide.positionToOffset(state, endPosition) + + local edits = {} + edits[#edits+1] = { + start = endOffset - #myIndent + #lastIndent, + finish = endOffset, + text = '', + } + + return edits +end + +---@param state parser.state +local function applyEdits(state, edits) + if #edits == 0 then + return + end + + local lines = state.originLines or state.lines + + local results = {} + for i, edit in ipairs(edits) do + local startPos = guide.offsetToPositionByLines(lines, edit.start) + local endPos = guide.offsetToPositionByLines(lines, edit.finish) + local startRow, startCol = guide.rowColOf(startPos) + local endRow, endCol = guide.rowColOf(endPos) + results[i] = { + range = { + start = { + line = startRow, + character = startCol, + }, + ['end'] = { + line = endRow, + character = endCol, + } + }, + newText = edit.text, + } + end + + proto.request('workspace/applyEdit', { + label = 'Fix Indent', + edit = { + changes = { + [state.uri] = results + } + }, + }) +end + +return function (uri, changes) + if not client.getOption('fixIndents') then + return + end + local state = files.compileState(uri) + if not state then + return + end + + local firstChange = changes[1] + if firstChange.range then + local edits = removeSpacesAfterEnter(state, firstChange) + or fixWrongIndent(state, firstChange) + if edits then + applyEdits(state, edits) + end + end +end diff --git a/script/core/hint.lua b/script/core/hint.lua index 9d098aa9..b0ff5aa7 100644 --- a/script/core/hint.lua +++ b/script/core/hint.lua @@ -287,6 +287,8 @@ local function semicolonHint(uri, results, start, finish) ---@async guide.eachSourceTypes(state.ast, blockTypes, function (src) await.delay() + if #src < 1 then return end + for i = 1, #src - 1 do local current = src[i] local next = src[i+1] @@ -313,6 +315,7 @@ local function semicolonHint(uri, results, start, finish) end end end + if mode == 'All' then local last = src[#src] results[#results+1] = { diff --git a/script/core/type-formatting.lua b/script/core/type-formatting.lua index 419cb56b..f6080650 100644 --- a/script/core/type-formatting.lua +++ b/script/core/type-formatting.lua @@ -4,187 +4,6 @@ local guide = require 'parser.guide' local config = require 'config' local util = require 'utility' - -local function insertIndentation(uri, position, edits) - local text = files.getText(uri) - local state = files.getState(uri) - local row = guide.rowColOf(position) - if not state or not text then - return - end - local offset = state.lines[row] - local indent = text:match('^%s*', offset) - for _, edit in ipairs(edits) do - edit.text = edit.text:gsub('\n', '\n' .. indent) - end -end - -local function findForward(uri, position, ...) - local text = files.getText(uri) - local state = files.getState(uri) - if not state or not text then - return nil - end - local offset = guide.positionToOffset(state, position) - local firstOffset = text:match('^[ \t]*()', offset + 1) - if not firstOffset then - return nil - end - for _, symbol in ipairs { ... } do - if text:sub(firstOffset, firstOffset + #symbol - 1) == symbol then - return guide.offsetToPosition(state, firstOffset - 1), symbol - end - end - return nil -end - -local function findBackward(uri, position, ...) - local text = files.getText(uri) - local state = files.getState(uri) - if not state or not text then - return nil - end - local offset = guide.positionToOffset(state, position) - local lastOffset = lookBackward.findAnyOffset(text, offset) - for _, symbol in ipairs { ... } do - if text:sub(lastOffset - #symbol + 1, lastOffset) == symbol then - return guide.offsetToPosition(state, lastOffset) - end - end - return nil -end - -local function checkSplitOneLine(results, uri, position, ch) - if ch ~= '\n' then - return - end - - local fPosition, fSymbol = findForward(uri, position, 'end', '}') - if not fPosition or not fSymbol then - return - end - local bPosition = findBackward(uri, position, 'then', 'do', ')', '{') - if not bPosition then - return - end - local edits = {} - edits[#edits+1] = { - start = bPosition, - finish = position, - text = '\n\t', - } - edits[#edits+1] = { - start = position, - finish = fPosition + 1, - text = '', - } - edits[#edits+1] = { - start = fPosition + 1, - finish = fPosition + 1, - text = '\n' .. fSymbol:sub(1, 1) - } - insertIndentation(uri, bPosition, edits) - for _, edit in ipairs(edits) do - results[#results+1] = edit - end -end - -local function getIndent(state, row) - local offset = state.lines[row] - local indent = state.lua:match('^[\t ]*', offset) - return indent -end - -local function isInBlock(state, position) - local block = guide.eachSourceContain(state.ast, position, function(source) - if source.type == 'ifblock' - or source.type == 'elseifblock' then - if source.keyword[4] and source.keyword[4] <= position then - return true - end - end - if source.type == 'else' then - if source.keyword[2] and source.keyword[2] <= position then - return true - end - end - if source.type == 'while' then - if source.keyword[4] and source.keyword[4] <= position then - return true - end - end - if source.type == 'repeat' then - if source.keyword[2] and source.keyword[2] <= position then - return true - end - end - if source.type == 'loop' then - if source.keyword[4] and source.keyword[4] <= position then - return true - end - end - if source.type == 'in' then - if source.keyword[6] and source.keyword[6] <= position then - return true - end - end - if source.type == 'do' then - if source.keyword[2] and source.keyword[2] <= position then - return true - end - end - if source.type == 'function' then - if source.args and source.args.finish <= position then - return true - end - if not source.keyword[3] or source.keyword[3] >= position then - return true - end - end - if source.type == 'table' then - if source.start + 1 == position then - return true - end - end - end) - return block ~= nil -end - -local function checkWrongIndentation(results, uri, position, ch) - if ch ~= '\n' then - return - end - local state = files.getState(uri) - if not state then - return - end - local row = guide.rowColOf(position) - if row <= 0 then - return - end - local myIndent = getIndent(state, row) - local lastIndent = getIndent(state, row - 1) - if #myIndent <= #lastIndent then - return - end - if not util.stringStartWith(myIndent, lastIndent) then - return - end - local lastOffset = lookBackward.findAnyOffset(state.lua, guide.positionToOffset(state, position) - 1) - if not lastOffset then - return - end - local lastPosition = guide.offsetToPosition(state, lastOffset) - if isInBlock(state, lastPosition) then - return - end - results[#results+1] = { - start = position - #myIndent + #lastIndent, - finish = position, - text = '', - } -end - local function typeFormat(results, uri, position, ch, options) if ch ~= '\n' then return @@ -218,22 +37,12 @@ return function (uri, position, ch, options) return nil end - local results = {} - -- split `function () $ end` - checkSplitOneLine(results, uri, position, ch) - if #results > 0 then - return results - end - - checkWrongIndentation(results, uri, position, ch) - if #results > 0 then - return results - end - if TEST then return nil end + local results = {} + typeFormat(results, uri, position, ch, options) if #results > 0 then return results diff --git a/script/lclient.lua b/script/lclient.lua index 13b431b0..96a6c16f 100644 --- a/script/lclient.lua +++ b/script/lclient.lua @@ -5,6 +5,8 @@ local await = require 'await' local timer = require 'timer' local pub = require 'pub' local json = require 'json' +local client = require 'client' +local define = require 'proto.define' require 'provider' @@ -61,9 +63,33 @@ function mt:_localLoadFile() end) end +local defaultClientOptions = { + initializationOptions = { + changeConfiguration = true, + viewDocument = true, + trustByClient = true, + useSemanticByRange = true, + }, + capabilities = { + textDocument = { + completion = { + completionItem = { + tagSupport = { + valueSet = { + define.DiagnosticTag.Unnecessary, + define.DiagnosticTag.Deprecated, + }, + }, + }, + }, + }, + }, +} + ---@async function mt:initialize(params) - self:awaitRequest('initialize', params or {}) + local initParams = util.tableMerge(params or {}, defaultClientOptions) + self:awaitRequest('initialize', initParams) self:notify('initialized') end diff --git a/script/library.lua b/script/library.lua index cfc7e328..49e39470 100644 --- a/script/library.lua +++ b/script/library.lua @@ -22,7 +22,7 @@ m.metaPaths = {} local function getDocFormater(uri) local version = config.get(uri, 'Lua.runtime.version') - if client.isVSCode() then + if client.getOption('viewDocument') then if version == 'Lua 5.1' then return 'HOVER_NATIVE_DOCUMENT_LUA51' elseif version == 'Lua 5.2' then diff --git a/script/parser/guide.lua b/script/parser/guide.lua index e42f2acd..768d7dec 100644 --- a/script/parser/guide.lua +++ b/script/parser/guide.lua @@ -105,6 +105,8 @@ local blockTypes = { ['main'] = true, } +m.blockTypes = blockTypes + local topBlockTypes = { ['while'] = true, ['function'] = true, diff --git a/script/plugin.lua b/script/plugin.lua index b8ecfb6a..ec55875e 100644 --- a/script/plugin.lua +++ b/script/plugin.lua @@ -195,7 +195,7 @@ local function initPlugin(uri) m.showError(scp, err) return end - if not client.isVSCode() and not checkTrustLoad(scp) then + if not client.getOption('trustByClient') and not checkTrustLoad(scp) then return end local suc, err = xpcall(f, log.error, f, uri, myArgs) diff --git a/script/provider/language-configuration.lua b/script/provider/language-configuration.lua new file mode 100644 index 00000000..1d34f765 --- /dev/null +++ b/script/provider/language-configuration.lua @@ -0,0 +1,86 @@ +-- Enumeration of commonly encountered syntax token types. +local SyntaxTokenType = { + Other = 0, -- Everything except tokens that are part of comments, string literals and regular expressions. + Comment = 1, -- A comment. + String = 2, -- A string literal. + RegEx = 3 -- A regular expression. +} + +-- Describes what to do with the indentation when pressing Enter. +local IndentAction = { + None = 0, -- Insert new line and copy the previous line's indentation. + Indent = 1, -- Insert new line and indent once (relative to the previous line's indentation). + IndentOutdent = 2, -- Insert two new lines: the first one indented which will hold the cursor, and the second one at the same indentation level. + Outdent = 3 -- Insert new line and outdent once (relative to the previous line's indentation). +} + +local languageConfiguration = { + id = 'lua', + configuration = { + autoClosingPairs = { + { open = "{", close = "}" }, + { open = "[", close = "]" }, + { open = "(", close = ")" }, + { open = "'", close = "'", notIn = { SyntaxTokenType.String } }, + { open = '"', close = '"', notIn = { SyntaxTokenType.String } }, + { open = "[=", close = "=]" }, + { open = "[==", close = "==]" }, + { open = "[===", close = "===]" }, + { open = "[====", close = "====]" }, + { open = "[=====", close = "=====]" }, + }, + onEnterRules = { + { + beforeText = [[\)\s*$]], + afterText = [[^\s*end\b]], + action = { + indentAction = IndentAction.IndentOutdent, + } + }, + { + beforeText = [[\b()\s*$]], + afterText = [[^\s*end\b]], + action = { + indentAction = IndentAction.IndentOutdent, + } + }, + { + beforeText = [[\b(repeat)\s*$]], + afterText = [[^\s*until\b]], + action = { + indentAction = IndentAction.IndentOutdent, + } + }, + { + beforeText = [[^\s*---@]], + action = { + indentAction = IndentAction.None, + appendText = "---@" + } + }, + { + beforeText = [[^\s*--- @]], + action = { + indentAction = IndentAction.None, + appendText = "--- @" + } + }, + { + beforeText = [[^\s*--- ]], + action = { + indentAction = IndentAction.None, + appendText = "--- " + } + }, + { + beforeText = [[^\s*---]], + action = { + indentAction = IndentAction.None, + appendText = "---" + } + }, + }, + }, +} + +return languageConfiguration diff --git a/script/provider/provider.lua b/script/provider/provider.lua index 2e2fb5eb..6a4b2406 100644 --- a/script/provider/provider.lua +++ b/script/provider/provider.lua @@ -292,6 +292,7 @@ m.register 'textDocument/didClose' { m.register 'textDocument/didChange' { ---@async function (params) + local fixIndent = require 'core.fix-indent' local doc = params.textDocument local changes = params.contentChanges local uri = files.getRealUri(doc.uri) @@ -299,6 +300,7 @@ m.register 'textDocument/didChange' { if not text then text = util.loadFile(furi.decode(uri)) files.setText(uri, text, false) + fixIndent(uri, changes) return end local rows = files.getCachedRows(uri) @@ -307,6 +309,8 @@ m.register 'textDocument/didChange' { file.version = doc.version end) files.setCachedRows(uri, rows) + + fixIndent(uri, changes) end } @@ -1059,7 +1063,7 @@ end client.event(function (ev) if ev == 'init' then - if not client.isVSCode() then + if not client.getOption('useSemanticByRange') then m.register 'textDocument/semanticTokens/full' { capability = { semanticTokensProvider = { @@ -1593,8 +1597,23 @@ m.register '$/psi/select' { end } +local function refreshLanguageConfiguration() + if not client.getOption('languageConfiguration') then + return + end + proto.notify('$/languageConfiguration', require 'provider.language-configuration') +end + +config.watch(function (uri, key, value) + if key == '' then + refreshLanguageConfiguration() + end +end) local function refreshStatusBar() + if not client.getOption('statusBar') then + return + end local valid = config.get(nil, 'Lua.window.statusBar') for _, scp in ipairs(workspace.folders) do if not config.get(scp.uri, 'Lua.window.statusBar') then diff --git a/script/service/service.lua b/script/service/service.lua index c3afd4cf..c7675f1b 100644 --- a/script/service/service.lua +++ b/script/service/service.lua @@ -13,6 +13,7 @@ local time = require 'bee.time' local fw = require 'filewatch' local furi = require 'file-uri' local net = require 'service.net' +local client = require 'client' require 'jsonc' require 'json-beautify' @@ -202,6 +203,9 @@ end local showStatusTip = math.random(100) == 1 function m.reportStatus() + if not client.getOption('statusBar') then + return + end local info = {} if m.workingClock and time.monotonic() - m.workingClock > 100 then info.text = '$(loading~spin)Lua' @@ -245,6 +249,10 @@ function m.testVersion() end end +function m.sayHello() + proto.notify('$/hello', {'world'}) +end + function m.lockCache() local fs = require 'bee.filesystem' local sp = require 'bee.subprocess' @@ -280,6 +288,8 @@ function m.start() require 'provider' + m.sayHello() + m.eventLoop() end diff --git a/script/vm/compiler.lua b/script/vm/compiler.lua index f3655123..54390450 100644 --- a/script/vm/compiler.lua +++ b/script/vm/compiler.lua @@ -578,7 +578,17 @@ local function matchCall(source) if call.args then -- clear node caches of args to allow recomputation with the type narrowed call for _, arg in ipairs(call.args) do - vm.removeNode(arg) + vm.setNode(arg, vm.createNode(), true) + end + for n in newNode:eachObject() do + if n.type == 'function' + or n.type == 'doc.type.function' then + for i, arg in ipairs(call.args) do + if n.args[i] then + vm.setNode(arg, vm.compileNode(n.args[i])) + end + end + end end end end diff --git a/script/vm/def.lua b/script/vm/def.lua index 669d39c2..41f735b2 100644 --- a/script/vm/def.lua +++ b/script/vm/def.lua @@ -93,7 +93,7 @@ function vm.getDefs(source) return results end -local HAS_DEF_ERR = {} -- the error object for comparing +local HAS_DEF_ERR = false -- the error object for comparing local function checkHasDef(checkFunc, source, pushResult) local _, err = pcall(checkFunc, source, pushResult) return err == HAS_DEF_ERR diff --git a/script/vm/operator.lua b/script/vm/operator.lua index 7ce2b30d..07ce19eb 100644 --- a/script/vm/operator.lua +++ b/script/vm/operator.lua @@ -261,6 +261,9 @@ vm.binarySwitch = util.switch() }) else local node = vm.runOperator(binaryMap[op], source[1], source[2]) + if not node then + node = vm.runOperator(binaryMap[op], source[2], source[1]) + end if node then vm.setNode(source, node) end @@ -300,6 +303,9 @@ vm.binarySwitch = util.switch() }) else local node = vm.runOperator(binaryMap[op], source[1], source[2]) + if not node then + node = vm.runOperator(binaryMap[op], source[2], source[1]) + end if node then vm.setNode(source, node) return @@ -396,6 +402,9 @@ vm.binarySwitch = util.switch() return end local node = vm.runOperator(binaryMap[source.op.type], source[1], source[2]) + if not node then + node = vm.runOperator(binaryMap[source.op.type], source[2], source[1]) + end if node then vm.setNode(source, node) end diff --git a/script/vm/type.lua b/script/vm/type.lua index afc19984..4835065a 100644 --- a/script/vm/type.lua +++ b/script/vm/type.lua @@ -70,7 +70,7 @@ local function checkParentEnum(parentName, child, uri, mark, errs) if enums then enums = util.arrayMerge(enums, denums) else - enums = denums + enums = util.arrayMerge({}, denums) end end end @@ -66,7 +66,6 @@ local function testAll() test 'command' test 'document_symbol' test 'code_action' - test 'type_formatting' test 'other' end diff --git a/test/completion/common.lua b/test/completion/common.lua index ec2372a0..30350642 100644 --- a/test/completion/common.lua +++ b/test/completion/common.lua @@ -3235,7 +3235,7 @@ xx@pcall<??> { [1] = { label = 'pcall', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 3, finish = 8, @@ -3257,7 +3257,7 @@ xx()@pcall<??> { [1] = { label = 'pcall', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 5, finish = 10, @@ -3279,7 +3279,7 @@ xx(1, 2, 3)@pcall<??> { [1] = { label = 'pcall', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 12, finish = 17, @@ -3301,7 +3301,7 @@ xx@xpcall<??> { [1] = { label = 'xpcall', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 3, finish = 9, @@ -3323,7 +3323,7 @@ xx()@xpcall<??> { [1] = { label = 'xpcall', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 5, finish = 11, @@ -3345,7 +3345,7 @@ xx(1, 2, 3)@xpcall<??> { [1] = { label = 'xpcall', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 12, finish = 18, @@ -3367,7 +3367,7 @@ xx@function<??> { [1] = { label = 'function', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 3, finish = 11, @@ -3389,7 +3389,7 @@ xx.yy@method<??> { [1] = { label = 'method', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 6, finish = 12, @@ -3411,7 +3411,7 @@ xx:yy@method<??> { [1] = { label = 'method', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 6, finish = 12, @@ -3433,7 +3433,7 @@ xx@insert<??> { [1] = { label = 'insert', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 3, finish = 9, @@ -3455,7 +3455,7 @@ xx++<??> { [1] = { label = '++', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 2, finish = 4, @@ -3471,7 +3471,7 @@ xx++<??> }, [2] = { label = '++?', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 2, finish = 4, @@ -3495,7 +3495,7 @@ end) { [1] = { label = 'xpcall', - kind = define.CompletionItemKind.Event, + kind = define.CompletionItemKind.Snippet, textEdit = { start = 10007, finish = 10013, diff --git a/test/diagnostics/missing-fields.lua b/test/diagnostics/missing-fields.lua index ab87f81d..8c1ffbbb 100644 --- a/test/diagnostics/missing-fields.lua +++ b/test/diagnostics/missing-fields.lua @@ -231,3 +231,125 @@ local t = { y = 1, } ]] + +TEST [[ +---@diagnostic disable: unused-local + +---@class Foo +---@field a number +---@field b number +---@field c number + +---@class Foo + +---@class Bar +---@field ba number +---@field bb number +---@field bc number + +---@class Bar +---@field bd number + +---@type Foo|Bar +local x = { + ba = 1, + bb = 2, + bc = 3, + bd = 4, +} +]] + +TEST [[ +---@diagnostic disable: unused-local + +---@class Foo +---@field a number +---@field b number +---@field c number + +---@class Foo + +---@class Bar +---@field ba number +---@field bb number +---@field bc number + +---@class Bar +---@field bd number + +---@type Foo|Bar +local x = { + a = 1, + b = 2, + c = 3, +} +]] + +TEST [[ +---@diagnostic disable: unused-local + +---@class Foo +---@field a number +---@field b number +---@field c number + +---@class Foo + +---@class Bar +---@field ba number +---@field bb number +---@field bc number + +---@class Bar +---@field bd number + +---@type Foo|Bar +local x = <!{ + a = 1, + b = 2, +}!> +]] + +TEST [[ +---@diagnostic disable: unused-local + +---@class Foo +---@field a number +---@field b number +---@field c number + +---@class Foo + +---@class Bar +---@field ba number +---@field bb number +---@field bc number + +---@class Bar +---@field bd number + +---@type Foo|Bar +local x = <!{ + ba = 1, + bb = 2, + bd = 4, +}!> +]] + +TEST[[ +---@class A +---@field [1] string +---@field x number + +---@type A +local t = {x = 1, ""} +]] + +TEST[[ +---@class A +---@field [1] string +---@field x number + +---@type A +local t = <!{x = 1}!> +]]
\ No newline at end of file diff --git a/test/type_formatting/init.lua b/test/type_formatting/init.lua deleted file mode 100644 index 4e9ce556..00000000 --- a/test/type_formatting/init.lua +++ /dev/null @@ -1,209 +0,0 @@ -local core = require 'core.type-formatting' -local files = require 'files' -local util = require 'utility' -local catch = require 'catch' - -rawset(_G, 'TEST', true) - -function TEST(script) - return function(expect) - local newScript, catched = catch(script, '?') - files.setText(TESTURI, newScript) - local edits = core(TESTURI, catched['?'][1][1], expect.ch) - if edits then - assert(expect.edits) - assert(util.equal(edits, expect.edits)) - else - assert(expect.edits == nil) - end - files.remove(TESTURI) - end -end - -TEST [[ -if true then <??> end -]] -{ - ch = '\n', - edits = { - { - start = 12, - finish = 13, - text = '\n\t', - }, - { - start = 13, - finish = 15, - text = '', - }, - { - start = 15, - finish = 15, - text = '\ne', - }, - } -} - -TEST [[ -if true then <??>end -]] -{ - ch = '\n', - edits = { - { - start = 12, - finish = 13, - text = '\n\t', - }, - { - start = 13, - finish = 14, - text = '', - }, - { - start = 14, - finish = 14, - text = '\ne', - }, - } -} - -TEST [[ -if true then<??>end -]] -{ - ch = '\n', - edits = { - { - start = 12, - finish = 12, - text = '\n\t', - }, - { - start = 12, - finish = 13, - text = '', - }, - { - start = 13, - finish = 13, - text = '\ne', - }, - } -} - -TEST [[ - if true then<??>end -]] -{ - ch = '\n', - edits = { - { - start = 16, - finish = 16, - text = '\n \t', - }, - { - start = 16, - finish = 17, - text = '', - }, - { - start = 17, - finish = 17, - text = '\n e', - }, - } -} - -TEST [[ -local x = 1 -<??> -]] -{ - ch = '\n', - edits = nil, -} - -TEST [[ -local x = 'if 1 then' - <??> -]] -{ - ch = '\n', - edits = { - { - start = 10000, - finish = 10004, - text = '', - } - } -} - -TEST [[ -local x = 'do' - <??> -]] -{ - ch = '\n', - edits = { - { - start = 10000, - finish = 10004, - text = '', - } - } -} - -TEST [[ -local x = 'function' - <??> -]] -{ - ch = '\n', - edits = { - { - start = 10000, - finish = 10004, - text = '', - } - } -} - -TEST [[ -do - <??> -]] -{ - ch = '\n', - edits = nil -} - -TEST [[ -do - <??> -end -]] -{ - ch = '\n', - edits = nil -} - -TEST [[ -function () - <??> -]] -{ - ch = '\n', - edits = nil -} - -TEST [[ -function () - <??> -end -]] -{ - ch = '\n', - edits = nil -} diff --git a/test/type_inference/common.lua b/test/type_inference/common.lua index 5922832b..5fcec805 100644 --- a/test/type_inference/common.lua +++ b/test/type_inference/common.lua @@ -4192,3 +4192,239 @@ TEST 'boolean|number' [[ ---@type A local <?x?> ]] + +--reverse binary operator tests + +TEST 'A' [[ +---@class A +---@operator add(number): A + +---@type A +local x +local <?y?> = x + 1 +]] + +TEST 'A' [[ +---@class A +---@operator add(number): A + +---@type A +local x +local <?y?> = 1 + x +]] + +TEST 'A' [[ +---@class A +---@operator sub(number): A + +---@type A +local x +local <?y?> = x - 1 +]] + +TEST 'A' [[ +---@class A +---@operator sub(number): A + +---@type A +local x +local <?y?> = 1 - x +]] + +TEST 'A' [[ +---@class A +---@operator mul(number): A + +---@type A +local x +local <?y?> = x * 1 +]] + +TEST 'A' [[ +---@class A +---@operator mul(number): A + +---@type A +local x +local <?y?> = 1 * x +]] + +TEST 'A' [[ +---@class A +---@operator div(number): A + +---@type A +local x +local <?y?> = x / 1 +]] + +TEST 'A' [[ +---@class A +---@operator div(number): A + +---@type A +local x +local <?y?> = 1 / x +]] + +TEST 'A' [[ +---@class A +---@operator idiv(number): A + +---@type A +local x +local <?y?> = x // 1 +]] + +TEST 'A' [[ +---@class A +---@operator idiv(number): A + +---@type A +local x +local <?y?> = 1 // x +]] + +TEST 'A' [[ +---@class A +---@operator mod(number): A + +---@type A +local x +local <?y?> = x % 1 +]] + +TEST 'A' [[ +---@class A +---@operator mod(number): A + +---@type A +local x +local <?y?> = 1 % x +]] + +TEST 'A' [[ +---@class A +---@operator pow(number): A + +---@type A +local x +local <?y?> = x ^ 1 +]] + +TEST 'A' [[ +---@class A +---@operator pow(number): A + +---@type A +local x +local <?y?> = 1 ^ x +]] + +TEST 'A' [[ +---@class A +---@operator concat(number): A + +---@type A +local x +local <?y?> = x .. 1 +]] + +TEST 'A' [[ +---@class A +---@operator concat(number): A + +---@type A +local x +local <?y?> = 1 .. x +]] + +TEST 'A' [[ +---@class A +---@operator band(number): A + +---@type A +local x +local <?y?> = x & 1 +]] + +TEST 'A' [[ +---@class A +---@operator band(number): A + +---@type A +local x +local <?y?> = 1 & x +]] + +TEST 'A' [[ +---@class A +---@operator bor(number): A + +---@type A +local x +local <?y?> = x | 1 +]] + +TEST 'A' [[ +---@class A +---@operator bor(number): A + +---@type A +local x +local <?y?> = 1 | x +]] + +TEST 'A' [[ +---@class A +---@operator bxor(number): A + +---@type A +local x +local <?y?> = x ~ 1 +]] + +TEST 'A' [[ +---@class A +---@operator bxor(number): A + +---@type A +local x +local <?y?> = 1 ~ x +]] + +TEST 'A' [[ +---@class A +---@operator shl(number): A + +---@type A +local x +local <?y?> = x << 1 +]] + +TEST 'A' [[ +---@class A +---@operator shl(number): A + +---@type A +local x +local <?y?> = 1 << x +]] + +TEST 'A' [[ +---@class A +---@operator shr(number): A + +---@type A +local x +local <?y?> = x >> 1 +]] + +TEST 'A' [[ +---@class A +---@operator shr(number): A + +---@type A +local x +local <?y?> = 1 >> x +]] diff --git a/test/type_inference/param_match.lua b/test/type_inference/param_match.lua index 8ead05ef..1079e433 100644 --- a/test/type_inference/param_match.lua +++ b/test/type_inference/param_match.lua @@ -137,3 +137,27 @@ local function f(...) end local <?r?> = f(10) ]] + +TEST 'number' [[ +---@overload fun(a: 1, c: fun(x: number)) +---@overload fun(a: 2, c: fun(x: string)) +local function f(...) end + +f(1, function (<?a?>) end) +]] + +TEST 'string' [[ +---@overload fun(a: 1, c: fun(x: number)) +---@overload fun(a: 2, c: fun(x: string)) +local function f(...) end + +f(2, function (<?a?>) end) +]] + +TEST 'any' [[ +---@overload fun(a: 1) +---@overload fun(a: 2) +local function f(...) end + +f(1, function (<?a?>) end) +]] |