summaryrefslogtreecommitdiff
path: root/script/core/code-action.lua
diff options
context:
space:
mode:
Diffstat (limited to 'script/core/code-action.lua')
-rw-r--r--script/core/code-action.lua269
1 files changed, 269 insertions, 0 deletions
diff --git a/script/core/code-action.lua b/script/core/code-action.lua
new file mode 100644
index 00000000..69304f98
--- /dev/null
+++ b/script/core/code-action.lua
@@ -0,0 +1,269 @@
+local files = require 'files'
+local lang = require 'language'
+local define = require 'proto.define'
+local guide = require 'parser.guide'
+local util = require 'utility'
+local sp = require 'bee.subprocess'
+
+local function disableDiagnostic(uri, code, results)
+ results[#results+1] = {
+ title = lang.script('ACTION_DISABLE_DIAG', code),
+ kind = 'quickfix',
+ command = {
+ title = lang.script.COMMAND_DISABLE_DIAG,
+ command = 'lua.config',
+ arguments = {
+ {
+ key = 'Lua.diagnostics.disable',
+ action = 'add',
+ value = code,
+ uri = uri,
+ }
+ }
+ }
+ }
+end
+
+local function markGlobal(uri, name, results)
+ results[#results+1] = {
+ title = lang.script('ACTION_MARK_GLOBAL', name),
+ kind = 'quickfix',
+ command = {
+ title = lang.script.COMMAND_MARK_GLOBAL,
+ command = 'lua.config',
+ arguments = {
+ {
+ key = 'Lua.diagnostics.globals',
+ action = 'add',
+ value = name,
+ uri = uri,
+ }
+ }
+ }
+ }
+end
+
+local function changeVersion(uri, version, results)
+ results[#results+1] = {
+ title = lang.script('ACTION_RUNTIME_VERSION', version),
+ kind = 'quickfix',
+ command = {
+ title = lang.script.COMMAND_RUNTIME_VERSION,
+ command = 'lua.config',
+ arguments = {
+ {
+ key = 'Lua.runtime.version',
+ action = 'set',
+ value = version,
+ uri = uri,
+ }
+ }
+ },
+ }
+end
+
+local function solveUndefinedGlobal(uri, diag, results)
+ local ast = files.getAst(uri)
+ local text = files.getText(uri)
+ local lines = files.getLines(uri)
+ local offset = define.offsetOfWord(lines, text, diag.range.start)
+ guide.eachSourceContain(ast.ast, offset, function (source)
+ if source.type ~= 'getglobal' then
+ return
+ end
+
+ local name = guide.getName(source)
+ markGlobal(uri, name, results)
+
+ -- TODO check other version
+ end)
+end
+
+local function solveLowercaseGlobal(uri, diag, results)
+ local ast = files.getAst(uri)
+ local text = files.getText(uri)
+ local lines = files.getLines(uri)
+ local offset = define.offsetOfWord(lines, text, diag.range.start)
+ guide.eachSourceContain(ast.ast, offset, function (source)
+ if source.type ~= 'setglobal' then
+ return
+ end
+
+ local name = guide.getName(source)
+ markGlobal(uri, name, results)
+ end)
+end
+
+local function findSyntax(uri, diag)
+ local ast = files.getAst(uri)
+ local text = files.getText(uri)
+ local lines = files.getLines(uri)
+ for _, err in ipairs(ast.errs) do
+ if err.type:lower():gsub('_', '-') == diag.code then
+ local range = define.range(lines, text, err.start, err.finish)
+ if util.equal(range, diag.range) then
+ return err
+ end
+ end
+ end
+ return nil
+end
+
+local function solveSyntaxByChangeVersion(uri, err, results)
+ if type(err.version) == 'table' then
+ for _, version in ipairs(err.version) do
+ changeVersion(uri, version, results)
+ end
+ else
+ changeVersion(uri, err.version, results)
+ end
+end
+
+local function solveSyntaxByAddDoEnd(uri, err, results)
+ local text = files.getText(uri)
+ local lines = files.getLines(uri)
+ results[#results+1] = {
+ title = lang.script.ACTION_ADD_DO_END,
+ kind = 'quickfix',
+ edit = {
+ changes = {
+ [uri] = {
+ {
+ range = define.range(lines, text, err.start, err.finish),
+ newText = ('do %s end'):format(text:sub(err.start, err.finish)),
+ },
+ }
+ }
+ }
+ }
+end
+
+local function solveSyntaxByFix(uri, err, results)
+ local text = files.getText(uri)
+ local lines = files.getLines(uri)
+ local changes = {}
+ for _, fix in ipairs(err.fix) do
+ changes[#changes+1] = {
+ range = define.range(lines, text, fix.start, fix.finish),
+ newText = fix.text,
+ }
+ end
+ results[#results+1] = {
+ title = lang.script['ACTION_' .. err.fix.title],
+ kind = 'quickfix',
+ edit = {
+ changes = {
+ [uri] = changes,
+ }
+ }
+ }
+end
+
+local function solveSyntax(uri, diag, results)
+ local err = findSyntax(uri, diag)
+ if not err then
+ return
+ end
+ if err.version then
+ solveSyntaxByChangeVersion(uri, err, results)
+ end
+ if err.type == 'ACTION_AFTER_BREAK' or err.type == 'ACTION_AFTER_RETURN' then
+ solveSyntaxByAddDoEnd(uri, err, results)
+ end
+ if err.fix then
+ solveSyntaxByFix(uri, err, results)
+ end
+end
+
+local function solveNewlineCall(uri, diag, results)
+ local text = files.getText(uri)
+ local lines = files.getLines(uri)
+ results[#results+1] = {
+ title = lang.script.ACTION_ADD_SEMICOLON,
+ kind = 'quickfix',
+ edit = {
+ changes = {
+ [uri] = {
+ {
+ range = {
+ start = diag.range.start,
+ ['end'] = diag.range.start,
+ },
+ newText = ';',
+ }
+ }
+ }
+ }
+ }
+end
+
+local function solveAmbiguity1(uri, diag, results)
+ results[#results+1] = {
+ title = lang.script.ACTION_ADD_BRACKETS,
+ kind = 'quickfix',
+ command = {
+ title = lang.script.COMMAND_ADD_BRACKETS,
+ command = 'lua.solve:' .. sp:get_id(),
+ arguments = {
+ {
+ name = 'ambiguity-1',
+ uri = uri,
+ range = diag.range,
+ }
+ }
+ },
+ }
+end
+
+local function solveTrailingSpace(uri, diag, results)
+ results[#results+1] = {
+ title = lang.script.ACTION_REMOVE_SPACE,
+ kind = 'quickfix',
+ command = {
+ title = lang.script.COMMAND_REMOVE_SPACE,
+ command = 'lua.removeSpace:' .. sp:get_id(),
+ arguments = {
+ {
+ uri = uri,
+ }
+ }
+ },
+ }
+end
+
+local function solveDiagnostic(uri, diag, results)
+ if diag.source == lang.script.DIAG_SYNTAX_CHECK then
+ solveSyntax(uri, diag, results)
+ return
+ end
+ if not diag.code then
+ return
+ end
+ if diag.code == 'undefined-global' then
+ solveUndefinedGlobal(uri, diag, results)
+ elseif diag.code == 'lowercase-global' then
+ solveLowercaseGlobal(uri, diag, results)
+ elseif diag.code == 'newline-call' then
+ solveNewlineCall(uri, diag, results)
+ elseif diag.code == 'ambiguity-1' then
+ solveAmbiguity1(uri, diag, results)
+ elseif diag.code == 'trailing-space' then
+ solveTrailingSpace(uri, diag, results)
+ end
+ disableDiagnostic(uri, diag.code, results)
+end
+
+return function (uri, range, diagnostics)
+ local ast = files.getAst(uri)
+ if not ast then
+ return nil
+ end
+
+ local results = {}
+
+ for _, diag in ipairs(diagnostics) do
+ solveDiagnostic(uri, diag, results)
+ end
+
+ return results
+end