summaryrefslogtreecommitdiff
path: root/script/core
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 /script/core
parentabd5daae1885cdf7a9e21a1cbdfea945385124fc (diff)
parentb71cb7aecd9337c9463a4dfbdb9d06cac7b825fd (diff)
downloadlua-language-server-2c798703ca854d670fb28f51adc85c2b41f08f37.zip
Merge branch 'master' into cast-table-to-class
Diffstat (limited to 'script/core')
-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
7 files changed, 298 insertions, 233 deletions
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