diff options
-rw-r--r-- | changelog.md | 6 | ||||
-rw-r--r-- | script/core/completion/postfix.lua | 2 | ||||
-rw-r--r-- | script/core/fix-indent.lua | 225 | ||||
-rw-r--r-- | script/core/type-formatting.lua | 195 | ||||
-rw-r--r-- | script/parser/guide.lua | 2 | ||||
-rw-r--r-- | test.lua | 1 | ||||
-rw-r--r-- | test/completion/common.lua | 26 | ||||
-rw-r--r-- | test/type_formatting/init.lua | 209 |
8 files changed, 154 insertions, 512 deletions
diff --git a/changelog.md b/changelog.md index 2d44a81e..d92faf22 100644 --- a/changelog.md +++ b/changelog.md @@ -16,6 +16,12 @@ * `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 * `NEW` You can now click on "References" in CodeLen to display the reference list +* `FIX` incorrect `CompletionItemKind` for postfix snippets [#2773](https://github.com/LuaLS/lua-language-server/pull/2773) +* `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). ## 3.9.3 `2024-6-11` 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/fix-indent.lua b/script/core/fix-indent.lua index 9389072d..f9564dd8 100644 --- a/script/core/fix-indent.lua +++ b/script/core/fix-indent.lua @@ -1,99 +1,152 @@ local files = require 'files' local guide = require 'parser.guide' -local lookBackward = require 'core.look-backward' local proto = require 'proto.proto' +local lookBackward = require 'core.look-backward' +local util = require 'utility' ---@param state parser.state -local function insertIndentation(state, position, edits) - local text = state.originText or state.lua - local lines = state.originLines or state.lines - local row = guide.rowColOf(position) - if not lines or not text then - return - end - local offset = lines[row] - local indent = text:match('^%s*', offset) - for _, edit in ipairs(edits) do - edit.text = edit.text:gsub('\n', '\n' .. indent) +---@param change table +local function removeSpacesAfterEnter(state, change) + if not change.text:match '^\r?\n[\t ]+\r?\n$' then + return false end -end - ----@param state parser.state -local function findForward(state, position, ...) local lines = state.originLines or state.lines - local offset = guide.positionToOffsetByLines(lines, position) - local firstOffset = state.originText:match('^[ \t]*()', offset + 1) - if not firstOffset then - return nil - end - for _, symbol in ipairs { ... } do - if state.originText:sub(firstOffset, firstOffset + #symbol - 1) == symbol then - return guide.offsetToPositionByLines(lines, firstOffset - 1), symbol + 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 - return nil -end ----@param state parser.state -local function findBackward(state, position, ...) - local text = state.originText or state.lua - local lines = state.originLines or state.lines - if not text or not lines then - return nil + if leftOffset and leftOffset < startOffset then + edits[#edits+1] = { + start = leftOffset, + finish = startOffset, + text = '', + } end - local offset = guide.positionToOffsetByLines(lines, position) - local lastOffset = lookBackward.findAnyOffset(text, offset) - if not lastOffset then + + -- 清除后置空格 + 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 - for _, symbol in ipairs { ... } do - if text:sub(lastOffset - #symbol + 1, lastOffset) == symbol then - return guide.offsetToPositionByLines(lines, lastOffset) + + 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 - end - return nil + 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 ----@param state parser.state ----@param change table ----@param result any[] -local function checkSplitOneLine(state, change, result) - if change.text ~= '\r\n' - and change.text ~= '\n' then +local function fixWrongIdent(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 lastIndent = getIndent(state, row) + if #myIndent <= #lastIndent then return end - - local lines = state.originLines or state.lines - local position = lines[change.range.start.line + 1] - - local fPosition, fSymbol = findForward(state, position, 'end', '}') - if not fPosition or not fSymbol then + if not util.stringStartWith(myIndent, lastIndent) then + return + end + local lastOffset = lookBackward.findAnyOffset(state.lua, guide.positionToOffset(state, position)) + if not lastOffset then return end - local bPosition = findBackward(state, position, 'then', 'do', ')', '{') - if not bPosition then + local lastPosition = guide.offsetToPosition(state, lastOffset) + if isInBlock(state, lastPosition) then return end + + local endOffset = guide.positionToOffset(state, position) + #change.text + local edits = {} edits[#edits+1] = { - start = bPosition, - finish = position, - text = '\n\t', - } - edits[#edits+1] = { - start = position, - finish = fPosition + 1, + start = endOffset - #myIndent + #lastIndent, + finish = endOffset, text = '', } - edits[#edits+1] = { - start = fPosition + 1, - finish = fPosition + 1, - text = '\n' .. fSymbol:sub(1, 1) - } - insertIndentation(state, bPosition, edits) - for _, edit in ipairs(edits) do - result[#result+1] = edit - end + + return edits end ---@param state parser.state @@ -129,42 +182,24 @@ local function applyEdits(state, edits) label = 'Fix Indent', edit = { changes = { - [state.uri] = { - { - range = { - start = { - line = 1, - character = 0, - }, - ['end'] = { - line = 1, - character = 0, - } - }, - newText = '\t', - }, - } + [state.uri] = results } }, }) - proto.notify('$/command', { - command = 'cursorMove', - }) end return function (uri, changes) - do return end local state = files.compileState(uri) if not state then return end - local edits = {} - for _, change in ipairs(changes) do - if change.range then - checkSplitOneLine(state, change, edits) + local firstChange = changes[1] + if firstChange.range then + local edits = removeSpacesAfterEnter(state, firstChange) + or fixWrongIdent(state, firstChange) + if edits then + applyEdits(state, edits) end end - - applyEdits(state, edits) end 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/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, @@ -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/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 -} |