summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2024-08-15 15:16:03 +0800
committerGitHub <noreply@github.com>2024-08-15 15:16:03 +0800
commit2c798703ca854d670fb28f51adc85c2b41f08f37 (patch)
treeaa192cf6d6ed02d02dda8c8b7a4c240dded64efb
parentabd5daae1885cdf7a9e21a1cbdfea945385124fc (diff)
parentb71cb7aecd9337c9463a4dfbdb9d06cac7b825fd (diff)
downloadlua-language-server-2c798703ca854d670fb28f51adc85c2b41f08f37.zip
Merge branch 'master' into cast-table-to-class
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml5
-rw-r--r--.github/ISSUE_TEMPLATE/doc-report.yml34
-rw-r--r--changelog.md36
-rw-r--r--locale/en-us/script.lua4
-rw-r--r--locale/pt-br/script.lua8
-rw-r--r--locale/zh-cn/script.lua8
-rw-r--r--locale/zh-tw/script.lua8
-rw-r--r--script/cli/check.lua7
-rw-r--r--script/core/code-lens.lua36
-rw-r--r--script/core/completion/postfix.lua2
-rw-r--r--script/core/diagnostics/missing-fields.lua78
-rw-r--r--script/core/diagnostics/unused-function.lua6
-rw-r--r--script/core/fix-indent.lua211
-rw-r--r--script/core/hint.lua3
-rw-r--r--script/core/type-formatting.lua195
-rw-r--r--script/lclient.lua28
-rw-r--r--script/library.lua2
-rw-r--r--script/parser/guide.lua2
-rw-r--r--script/plugin.lua2
-rw-r--r--script/provider/language-configuration.lua86
-rw-r--r--script/provider/provider.lua21
-rw-r--r--script/service/service.lua10
-rw-r--r--script/vm/compiler.lua12
-rw-r--r--script/vm/def.lua2
-rw-r--r--script/vm/operator.lua9
-rw-r--r--script/vm/type.lua2
-rw-r--r--test.lua1
-rw-r--r--test/completion/common.lua26
-rw-r--r--test/diagnostics/missing-fields.lua122
-rw-r--r--test/type_formatting/init.lua209
-rw-r--r--test/type_inference/common.lua236
-rw-r--r--test/type_inference/param_match.lua24
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
diff --git a/test.lua b/test.lua
index fa49655f..986b0a06 100644
--- a/test.lua
+++ b/test.lua
@@ -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)
+]]