From f7970f793f174f9fa5f6ce7517b9c039249eb259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Mizio?= Date: Wed, 17 Jan 2024 09:47:10 +0100 Subject: [PATCH 1/4] feat: implement `Move to file` code action --- lua/typescript-tools/capabilities.lua | 1 + lua/typescript-tools/integrations.lua | 41 ++++++++++++++++++ lua/typescript-tools/internal_commands.lua | 42 +++++++++++++++++++ lua/typescript-tools/protocol/constants.lua | 1 + .../text_document/code_action/init.lua | 30 ++++++++++--- 5 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 lua/typescript-tools/integrations.lua diff --git a/lua/typescript-tools/capabilities.lua b/lua/typescript-tools/capabilities.lua index dce888e..e611247 100644 --- a/lua/typescript-tools/capabilities.lua +++ b/lua/typescript-tools/capabilities.lua @@ -16,6 +16,7 @@ local function make_capabilities() c.InternalCommands.CallApiFunction, c.InternalCommands.RequestReferences, c.InternalCommands.RequestImplementations, + c.InternalCommands.InteractiveCodeAction, }, }, renameProvider = { diff --git a/lua/typescript-tools/integrations.lua b/lua/typescript-tools/integrations.lua new file mode 100644 index 0000000..b90942a --- /dev/null +++ b/lua/typescript-tools/integrations.lua @@ -0,0 +1,41 @@ +local M = {} + +---@param picker function +---@param callback fun(file: string|nil, err: boolean?) +function M.telescope_picker(picker, callback) + local ok, actions = pcall(require, "telescope.actions") + + if not ok then + vim.notify("Telescope need to be installed to call this integration", vim.log.levels.WARN) + callback(nil, true) + return + end + + local action_state = require "telescope.actions.state" + picker = picker or require("telescope.builtin").find_files + + picker { + attach_mappings = function(prompt_bufnr) + local selected = nil + + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + selected = true + + if selection then + selected = vim.fs.joinpath(vim.loop.cwd(), selection.value) + end + + actions.close(prompt_bufnr) + end) + actions.close:enhance { + post = function() + callback(selected) + end, + } + return true + end, + } +end + +return M diff --git a/lua/typescript-tools/internal_commands.lua b/lua/typescript-tools/internal_commands.lua index 93d635b..f517e53 100644 --- a/lua/typescript-tools/internal_commands.lua +++ b/lua/typescript-tools/internal_commands.lua @@ -1,7 +1,10 @@ local api = vim.api +local a = require "plenary.async" local c = require "typescript-tools.protocol.constants" local plugin_api = require "typescript-tools.api" +local async = require "typescript-tools.async" +local integrations = require "typescript-tools.integrations" local M = {} @@ -57,4 +60,43 @@ M[c.InternalCommands.RequestImplementations] = function(params) vim.lsp.buf_request(0, c.LspMethods.Implementation, params.arguments) end +M[c.InternalCommands.InteractiveCodeAction] = function(params) + local request = unpack(params.arguments) + a.void(function() + ---@type string|boolean|nil + local target_file + + local file, telescope_err = a.wrap(integrations.telescope_picker, 2)() + + if telescope_err then + target_file = async.ui_input { prompt = "Move to file: " } + else + target_file = file + end + + if target_file == nil or not vim.fn.filereadable(target_file) then + vim.notify("This refactor require existing file", vim.log.levels.WARN) + return + end + + local err, result = async.buf_request_isomorphic( + false, + 0, + c.LspMethods.CodeActionResolve, + vim.tbl_deep_extend( + "force", + request, + { data = { interactiveRefactorArguments = { targetFile = target_file } } } + ) + ) + + if err or not result or not result.edit or (result.edit and vim.tbl_isempty(result.edit)) then + vim.notify("No refactors available", vim.log.levels.WARN) + return + end + + vim.lsp.util.apply_workspace_edit(result.edit, "utf-8") + end)() +end + return M diff --git a/lua/typescript-tools/protocol/constants.lua b/lua/typescript-tools/protocol/constants.lua index 714e70f..0bcfb42 100644 --- a/lua/typescript-tools/protocol/constants.lua +++ b/lua/typescript-tools/protocol/constants.lua @@ -9,6 +9,7 @@ return { CallApiFunction = "call_api_function", RequestReferences = "request_references", RequestImplementations = "request_implementations", + InteractiveCodeAction = "interactive_codeaction", }, ---@enum CommandTypes CommandTypes = { diff --git a/lua/typescript-tools/protocol/text_document/code_action/init.lua b/lua/typescript-tools/protocol/text_document/code_action/init.lua index 2e3d5ee..1531fdf 100644 --- a/lua/typescript-tools/protocol/text_document/code_action/init.lua +++ b/lua/typescript-tools/protocol/text_document/code_action/init.lua @@ -1,11 +1,18 @@ +local TsserverProvider = require "typescript-tools.tsserver_provider" +local proto_utils = require "typescript-tools.protocol.utils" local c = require "typescript-tools.protocol.constants" -local utils = require "typescript-tools.protocol.utils" local plugin_config = require "typescript-tools.config" +local utils = require "typescript-tools.utils" local M = {} local ALL_CODE_ACTIONS_KEY = "all" +local interactive_codeactions = { + "Move to file", +} +vim.tbl_add_reverse_lookup(interactive_codeactions) + local internal_commands_map = { fix_all = { name = "Fix all problems" }, remove_unused = { name = "Remove unused" }, @@ -33,9 +40,11 @@ end ---@type TsserverProtocolHandler function M.handler(request, response, params, ctx) + local tsserver_provider = TsserverProvider.get_instance() + local version = tsserver_provider:get_version() local text_document = params.textDocument - local range = utils.convert_lsp_range_to_tsserver(params.range) + local range = proto_utils.convert_lsp_range_to_tsserver(params.range) local request_range = { file = vim.uri_to_fname(text_document.uri), @@ -43,6 +52,7 @@ function M.handler(request, response, params, ctx) startOffset = range.start.offset, endLine = range["end"].line, endOffset = range["end"].offset, + includeInteractiveActions = utils.version_compare("gt", version, { 5, 1 }), } ctx.dependent_seq = { @@ -74,7 +84,7 @@ function M.handler(request, response, params, ctx) local kind = make_lsp_code_action_kind(action.kind or "") if kind and not action.notApplicableReason then - table.insert(code_actions, { + local code_action = { title = action.description, kind = kind, data = vim.tbl_extend("force", request_range, { @@ -82,7 +92,17 @@ function M.handler(request, response, params, ctx) kind = kind, refactor = refactor.name, }), - }) + } + + code_action.command = interactive_codeactions[action.description] + and { + title = action.description, + command = c.InternalCommands.InteractiveCodeAction, + arguments = { vim.tbl_deep_extend("force", {}, code_action) }, + } + or nil + + table.insert(code_actions, code_action) end end end @@ -96,7 +116,7 @@ function M.handler(request, response, params, ctx) title = fix.description, kind = c.CodeActionKind.QuickFix, edit = { - changes = utils.convert_tsserver_edits_to_lsp(fix.changes), + changes = proto_utils.convert_tsserver_edits_to_lsp(fix.changes), }, }) end From 025329665d41ec48ddce849b87cd899e937843c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Mizio?= Date: Wed, 17 Jan 2024 09:51:59 +0100 Subject: [PATCH 2/4] fix: cleanup --- lua/typescript-tools/integrations.lua | 6 +++--- lua/typescript-tools/internal_commands.lua | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/typescript-tools/integrations.lua b/lua/typescript-tools/integrations.lua index b90942a..f46d304 100644 --- a/lua/typescript-tools/integrations.lua +++ b/lua/typescript-tools/integrations.lua @@ -1,13 +1,13 @@ local M = {} ---@param picker function ----@param callback fun(file: string|nil, err: boolean?) +---@param callback fun(err: boolean|nil, file: string?) function M.telescope_picker(picker, callback) local ok, actions = pcall(require, "telescope.actions") if not ok then vim.notify("Telescope need to be installed to call this integration", vim.log.levels.WARN) - callback(nil, true) + callback(true, nil) return end @@ -30,7 +30,7 @@ function M.telescope_picker(picker, callback) end) actions.close:enhance { post = function() - callback(selected) + callback(nil, selected) end, } return true diff --git a/lua/typescript-tools/internal_commands.lua b/lua/typescript-tools/internal_commands.lua index f517e53..6fc203f 100644 --- a/lua/typescript-tools/internal_commands.lua +++ b/lua/typescript-tools/internal_commands.lua @@ -66,7 +66,7 @@ M[c.InternalCommands.InteractiveCodeAction] = function(params) ---@type string|boolean|nil local target_file - local file, telescope_err = a.wrap(integrations.telescope_picker, 2)() + local telescope_err, file = a.wrap(integrations.telescope_picker, 2)() if telescope_err then target_file = async.ui_input { prompt = "Move to file: " } From 9776e74f43d83294ad8135531f79c307fb2fb768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Mizio?= Date: Wed, 17 Jan 2024 09:58:10 +0100 Subject: [PATCH 3/4] fix: ci --- tests/requests_spec.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/requests_spec.lua b/tests/requests_spec.lua index a912644..600c856 100644 --- a/tests/requests_spec.lua +++ b/tests/requests_spec.lua @@ -541,9 +541,11 @@ describe("Lsp request", function() assert.is.same(result[1].title, "Infer function return type") assert.is.same(result[2].title, "Remove variable statement") elseif version and v.gt(version, { 5, 1 }) then - assert.is.same(2, #result) + print(vim.inspect(result)) + assert.is.same(3, #result) assert.is.same(result[1].title, "Move to a new file") - assert.is.same(result[2].title, "Remove variable statement") + assert.is.same(result[2].title, "Move to file") + assert.is.same(result[3].title, "Remove variable statement") else assert.is.same(1, #result) assert.is.same(result[1].title, "Remove variable statement") From c119df70ec68b03a8741cec9d9ab7d3da036551c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaros=C5=82aw=20Glego=C5=82a?= <35625949+KostkaBrukowa@users.noreply.github.com> Date: Mon, 19 Aug 2024 10:29:46 +0200 Subject: [PATCH 4/4] fix: fixes last line error --- lua/typescript-tools/internal_commands.lua | 2 +- .../protocol/text_document/code_action/init.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lua/typescript-tools/internal_commands.lua b/lua/typescript-tools/internal_commands.lua index 6fc203f..c8793b7 100644 --- a/lua/typescript-tools/internal_commands.lua +++ b/lua/typescript-tools/internal_commands.lua @@ -95,7 +95,7 @@ M[c.InternalCommands.InteractiveCodeAction] = function(params) return end - vim.lsp.util.apply_workspace_edit(result.edit, "utf-8") + vim.lsp.util.apply_workspace_edit(result.edit, "utf-16") end)() end diff --git a/lua/typescript-tools/protocol/text_document/code_action/init.lua b/lua/typescript-tools/protocol/text_document/code_action/init.lua index 1531fdf..1fd1c77 100644 --- a/lua/typescript-tools/protocol/text_document/code_action/init.lua +++ b/lua/typescript-tools/protocol/text_document/code_action/init.lua @@ -11,7 +11,7 @@ local ALL_CODE_ACTIONS_KEY = "all" local interactive_codeactions = { "Move to file", } -vim.tbl_add_reverse_lookup(interactive_codeactions) +utils.add_reverse_lookup(interactive_codeactions) local internal_commands_map = { fix_all = { name = "Fix all problems" },