From bffca6e26bf383bf6360da5827ef1f55f1ac7a67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Novakovi=C4=87?= <100767853+NStefan002@users.noreply.github.com> Date: Tue, 10 Mar 2026 00:20:25 +0100 Subject: [PATCH] feat(extmark): support end_col=-1 if strict=false #28169 Problem: There is an inconsistency between extmarks/highlights regarding the `end_col` param. Solution: Allow end_col=-1 to mean "end of line" (if strict=false). Co-authored-by: Justin M. Keyes --- runtime/doc/api.txt | 3 ++- runtime/lua/vim/_meta/api.lua | 2 +- src/nvim/api/extmark.c | 8 ++++++-- test/functional/api/extmark_spec.lua | 29 +++++++++++++++++++++++----- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index dd8dfa5f19..d704a3a071 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -3192,7 +3192,8 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) • {opts} (`vim.api.keyset.set_extmark`) Optional parameters. • id : id of the extmark to edit. • end_row : ending line of the mark, 0-based inclusive. - • end_col : ending col of the mark, 0-based exclusive. + • end_col : ending col of the mark, 0-based exclusive, or -1 + to extend the range to end of line. • hl_group : highlight group used for the text range. This and below highlight groups can be supplied either as a string or as an integer, the latter of which can be diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index e3f99dc539..0ff74f4a2b 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -568,7 +568,7 @@ function vim.api.nvim_buf_line_count(buffer) end --- @param opts vim.api.keyset.set_extmark Optional parameters. --- - id : id of the extmark to edit. --- - end_row : ending line of the mark, 0-based inclusive. ---- - end_col : ending col of the mark, 0-based exclusive. +--- - end_col : ending col of the mark, 0-based exclusive, or -1 to extend the range to end of line. --- - hl_group : highlight group used for the text range. This and below --- highlight groups can be supplied either as a string or as an integer, --- the latter of which can be obtained using `nvim_get_hl_id_by_name()`. diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index a14cf426f4..c3bb23fbb2 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -402,7 +402,7 @@ ArrayOf(DictAs(get_extmark_item)) nvim_buf_get_extmarks(Buffer buffer, Integer n /// @param opts Optional parameters. /// - id : id of the extmark to edit. /// - end_row : ending line of the mark, 0-based inclusive. -/// - end_col : ending col of the mark, 0-based exclusive. +/// - end_col : ending col of the mark, 0-based exclusive, or -1 to extend the range to end of line. /// - hl_group : highlight group used for the text range. This and below /// highlight groups can be supplied either as a string or as an integer, /// the latter of which can be obtained using |nvim_get_hl_id_by_name()|. @@ -586,9 +586,13 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer colnr_T col2 = -1; if (HAS_KEY(opts, set_extmark, end_col)) { Integer val = opts->end_col; - VALIDATE_RANGE((val >= 0 && val <= MAXCOL), "end_col", { + VALIDATE_RANGE((val >= -1 && val <= MAXCOL), "end_col", { goto error; }); + if (val == -1) { + val = MAXCOL; + } + col2 = (int)val; } diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua index 9492e498dd..0dc3cc7bec 100644 --- a/test/functional/api/extmark_spec.lua +++ b/test/functional/api/extmark_spec.lua @@ -153,7 +153,7 @@ describe('API/extmarks', function() ) end) - it('can end extranges past final newline using end_col = 0', function() + it('can end extranges past final newline using end_col=0', function() set_extmark(ns, marks[1], 0, 0, { end_col = 0, end_row = 1, @@ -164,20 +164,39 @@ describe('API/extmarks', function() ) end) - it('can end extranges past final newline when strict mode is false', function() - set_extmark(ns, marks[1], 0, 0, { + it('can end extranges past final newline when strict=false', function() + local id = set_extmark(ns, marks[1], 0, 0, { end_col = 1, end_row = 1, strict = false, }) + ok(id > 0, 'id > 0', id) end) - it('can end extranges past final column when strict mode is false', function() - set_extmark(ns, marks[1], 0, 0, { + it('can end extranges past final column when strict=false', function() + local id = set_extmark(ns, marks[1], 0, 0, { end_col = 6, end_row = 0, strict = false, }) + ok(id > 0, 'id > 0', id) + end) + + it('end_col=-1 means "end of line" when strict=false', function() + local function _test(strict) + return set_extmark(ns, marks[1], 0, 0, { + end_col = -1, + end_row = 0, + strict = strict, + }) + end + + -- strict=false + local id = _test(false) + ok(id > 0, 'id > 0', id) + + -- strict=true + eq("Invalid 'end_col': out of range", pcall_err(_test, true)) end) it('adds, updates and deletes marks', function()