diff options
author | Steven Arcangeli <506791+stevearc@users.noreply.github.com> | 2023-08-30 18:34:13 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-08-30 18:34:13 -0700 |
commit | 92393f02efadfb1d9f97c74c8feb853c1caea9de (patch) | |
tree | 980a43e5f6b70c419e089f82074d177aca9da7aa /lua/conform/init.lua | |
parent | c100b8548fd7262a1275bdb867186d0cd94e8b45 (diff) |
feat: apply changes as text edits using LSP utils (#18)
* feat: apply changes as text edits using LSP utils
This means we can leverage all of the work that was done in the LSP
client to preserve marks, cursor position, etc
* log: add trace logging to debug performance
* feat: use the same diff -> TextEdit technique for bad LSP servers
Some LSP servers simply return a single TextEdit that replaces the whole
buffer. This is bad for extmarks, cursor, and if the buffer is open in
multiple windows the non-active window will jump to the top. We can
detect that situation and apply the same vim.diff logic to convert it
into more granular TextEdits.
Diffstat (limited to 'lua/conform/init.lua')
-rw-r--r-- | lua/conform/init.lua | 37 |
1 files changed, 22 insertions, 15 deletions
diff --git a/lua/conform/init.lua b/lua/conform/init.lua index b47d43e..4a6b226 100644 --- a/lua/conform/init.lua +++ b/lua/conform/init.lua @@ -52,6 +52,25 @@ M.formatters = {} M.notify_on_error = true +---@private +M.original_apply_text_edits = vim.lsp.util.apply_text_edits + +local function apply_text_edits(text_edits, bufnr, offset_encoding) + if + #text_edits == 1 + and text_edits[1].range.start.line == 0 + and text_edits[1].range.start.character == 0 + and text_edits[1].range["end"].line == vim.api.nvim_buf_line_count(bufnr) + 1 + and text_edits[1].range["end"].character == 0 + then + local original_lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true) + local new_lines = vim.split(text_edits[1].newText, "\n", { plain = true }) + require("conform.runner").apply_format(bufnr, original_lines, new_lines, nil, false) + else + M.original_apply_text_edits(text_edits, bufnr, offset_encoding) + end +end + M.setup = function(opts) opts = opts or {} @@ -100,17 +119,9 @@ M.setup = function(opts) require("conform.health").show_window() end, { desc = "Show information about Conform formatters" }) - ---@diagnostic disable-next-line: duplicate-set-field - vim.lsp.handlers["textDocument/formatting"] = function(_, result, ctx, _) - if not result then - return - end - local client = vim.lsp.get_client_by_id(ctx.client_id) - assert(client) - local restore = require("conform.util").save_win_positions(ctx.bufnr) - vim.lsp.util.apply_text_edits(result, ctx.bufnr, client.offset_encoding) - restore() - end + -- Monkey patch lsp.util.apply_text_edits to handle LSP clients that replace the entire buffer + -- during formatting. This is unfortunately the best place to shim that logic in. + vim.lsp.util.apply_text_edits = apply_text_edits end ---@param bufnr integer @@ -289,11 +300,7 @@ M.format = function(opts) end elseif opts.lsp_fallback and supports_lsp_format(opts.bufnr) then log.debug("Running LSP formatter on %s", vim.api.nvim_buf_get_name(opts.bufnr)) - local restore = require("conform.util").save_win_positions(opts.bufnr) vim.lsp.buf.format(opts) - if not opts.async then - restore() - end elseif any_formatters_configured and not opts.quiet then vim.notify("No formatters found for buffer. See :ConformInfo", vim.log.levels.WARN) else |