diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index cbd9e43106..73bb1329d5 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -1595,9 +1595,11 @@ format({opts}) *vim.lsp.buf.format()* default: current selection in visual mode, `nil` in other modes, formatting the full buffer) Range to format. Table must contain `start` and `end` keys with {row,col} tuples - using (1,0) indexing. Can also be a list of tables that - contain `start` and `end` keys as described above, in which - case `textDocument/rangesFormatting` support is required. + using (1,0) indexing. The end column can be -1 to format + through the end of the line. Can also be a list of tables + that contain `start` and `end` keys as described above, in + which case `textDocument/rangesFormatting` support is + required. • {timeout_ms}? (`integer`, default: `1000`) Time in milliseconds to block for formatting requests. No effect if async=true. diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 06fbc1bb3f..cacc0fa169 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -611,7 +611,7 @@ end --- --- Range to format. --- Table must contain `start` and `end` keys with {row,col} tuples using ---- (1,0) indexing. +--- (1,0) indexing. The end column can be -1 to format through the end of the line. --- Can also be a list of tables that contain `start` and `end` keys as described above, --- in which case `textDocument/rangesFormatting` support is required. --- (Default: current selection in visual mode, `nil` in other modes, @@ -664,6 +664,13 @@ function M.format(opts) local function set_range(client, params) --- @param r {start:[integer,integer],end:[integer, integer]} local function to_lsp_range(r) + if r['end'][2] == -1 then + local end_line = api.nvim_buf_get_lines(bufnr, r['end'][1] - 1, r['end'][1], true)[1] + return { + start = vim.pos(bufnr, r.start[1] - 1, r.start[2]):to_lsp(client.offset_encoding), + ['end'] = vim.pos(bufnr, r['end'][1] - 1, #end_line):to_lsp(client.offset_encoding), + } + end return util.make_given_range_params(r.start, r['end'], bufnr, client.offset_encoding).range end diff --git a/test/functional/plugin/lsp/buf_spec.lua b/test/functional/plugin/lsp/buf_spec.lua index d2fd84f4e3..fe785c35dd 100644 --- a/test/functional/plugin/lsp/buf_spec.lua +++ b/test/functional/plugin/lsp/buf_spec.lua @@ -1343,6 +1343,52 @@ describe('vim.lsp.buf', function() } end) + it('formats range ending with -1 column', function() + exec_lua(create_server_definition) + local result = exec_lua(function() + local server = _G._create_server({ + capabilities = { + documentRangeFormattingProvider = true, + }, + handlers = { + ['textDocument/rangeFormatting'] = function(_, params, callback) + callback(nil, { + { + range = params.range, + newText = 'function fn(abc)\n print("hello")', + }, + }) + end, + }, + }) + local bufnr = vim.api.nvim_get_current_buf() + local client_id = assert(vim.lsp.start({ name = 'dummy', cmd = server.cmd })) + vim.api.nvim_win_set_buf(0, bufnr) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, true, { + 'function fn(abc )', + ' print("hello" )', + 'end', + }) + vim.lsp.buf.format({ + bufnr = bufnr, + range = { + start = { 1, 0 }, + ['end'] = { 2, -1 }, + }, + }) + vim.lsp.get_client_by_id(client_id):stop() + return { + lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true), + range = server.messages[3].params.range, + } + end) + eq({ + start = { line = 0, character = 0 }, + ['end'] = { line = 1, character = 17 }, + }, result.range) + eq({ 'function fn(abc)', ' print("hello")', 'end' }, result.lines) + end) + it('sends textDocument/rangesFormatting request to format multiple ranges', function() local expected_handlers = { { NIL, {}, { method = 'shutdown', client_id = 1 } },