aboutsummaryrefslogtreecommitdiffstats
path: root/lua/conform/init.lua
diff options
context:
space:
mode:
authorSteven Arcangeli <506791+stevearc@users.noreply.github.com>2023-08-30 18:34:13 -0700
committerGitHub <noreply@github.com>2023-08-30 18:34:13 -0700
commit92393f02efadfb1d9f97c74c8feb853c1caea9de (patch)
tree980a43e5f6b70c419e089f82074d177aca9da7aa /lua/conform/init.lua
parentc100b8548fd7262a1275bdb867186d0cd94e8b45 (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.lua37
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