From 54df9391de669d3f313bb775d9e6c234befc6617 Mon Sep 17 00:00:00 2001 From: Petur Subev Date: Sat, 27 Dec 2025 01:49:19 +0200 Subject: [PATCH 1/4] feat: add virtual text counter display at end of line - Add new counter.lua module to display [X/Y] reference count - Show counter as virtual text at end of line when jumping - Counter clears automatically when highlights are cleared - Configurable via counter.enable and counter.hl_group options - Default styling: bold orange text for high visibility --- lua/refjump/counter.lua | 58 +++++++++++++++++++++++++++++++++++++++ lua/refjump/highlight.lua | 1 + lua/refjump/init.lua | 13 +++++++++ lua/refjump/jump.lua | 22 +++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 lua/refjump/counter.lua diff --git a/lua/refjump/counter.lua b/lua/refjump/counter.lua new file mode 100644 index 0000000..c8cab2d --- /dev/null +++ b/lua/refjump/counter.lua @@ -0,0 +1,58 @@ +local M = {} + +---Namespace for counter virtual text extmarks +local counter_namespace = vim.api.nvim_create_namespace('RefjumpCounter') + +---Name of the highlight group for the counter +local counter_hl_name = 'RefjumpCounter' + +---Create fallback highlight group if it doesn't exist +function M.create_fallback_hl_group(fallback_hl) + local hl = vim.api.nvim_get_hl(0, { name = counter_hl_name }) + + if vim.tbl_isempty(hl) then + -- Create a distinctive highlight: yellow/orange text on dark background + -- with bold for extra visibility + vim.api.nvim_set_hl(0, counter_hl_name, { + fg = '#FFA500', -- Orange/yellow color + bold = true, + default = true, + }) + end +end + +---Show virtual text counter at the end of the current line +---@param current_index integer Current reference index (1-based) +---@param total_count integer Total number of references +---@param bufnr integer Buffer number +function M.show(current_index, total_count, bufnr) + if not require('refjump').get_options().counter.enable then + return + end + + -- Get current cursor position + local cursor = vim.api.nvim_win_get_cursor(0) + local line = cursor[1] - 1 -- Convert to 0-indexed + + -- Clear any existing counter in this buffer + M.clear(bufnr) + + -- Format the counter text + local text = string.format(' [%d/%d]', current_index, total_count) + + -- Add virtual text at end of line + vim.api.nvim_buf_set_extmark(bufnr, counter_namespace, line, 0, { + virt_text = { { text, counter_hl_name } }, + virt_text_pos = 'eol', + priority = 100, + }) +end + +---Clear counter virtual text from buffer +---@param bufnr integer Buffer number (0 for current buffer) +function M.clear(bufnr) + bufnr = bufnr or 0 + vim.api.nvim_buf_clear_namespace(bufnr, counter_namespace, 0, -1) +end + +return M diff --git a/lua/refjump/highlight.lua b/lua/refjump/highlight.lua index d302dca..70667fe 100644 --- a/lua/refjump/highlight.lua +++ b/lua/refjump/highlight.lua @@ -51,6 +51,7 @@ end function M.disable() if not highlight_references then vim.api.nvim_buf_clear_namespace(0, highlight_namespace, 0, -1) + require('refjump.counter').clear(0) else highlight_references = false end diff --git a/lua/refjump/init.lua b/lua/refjump/init.lua index cffc23a..2e7404c 100644 --- a/lua/refjump/init.lua +++ b/lua/refjump/init.lua @@ -9,12 +9,17 @@ local M = {} ---@field enable? boolean Highlight the LSP references on jump ---@field auto_clear boolean Automatically clear highlights when cursor moves +---@class RefjumpCounterOptions +---@field enable? boolean Show virtual text counter at end of line +---@field hl_group? string Highlight group for counter text + ---@class RefjumpIntegrationOptions ---@field demicolon? { enable?: boolean } Make `]r`/`[r` repeatable with `;`/`,` using demicolon.nvim ---@class RefjumpOptions ---@field keymaps? RefjumpKeymapOptions ---@field highlights? RefjumpHighlightOptions +---@field counter? RefjumpCounterOptions ---@field integrations? RefjumpIntegrationOptions ---@field verbose? boolean Print message if no reference is found local options = { @@ -27,6 +32,10 @@ local options = { enable = true, auto_clear = true, }, + counter = { + enable = true, + hl_group = 'WarningMsg', + }, integrations = { demicolon = { enable = true, @@ -55,6 +64,10 @@ function M.setup(opts) require('refjump.highlight').auto_clear_reference_highlights() end end + + if options.counter.enable then + require('refjump.counter').create_fallback_hl_group(options.counter.hl_group) + end end M.reference_jump = require('refjump.jump').reference_jump diff --git a/lua/refjump/jump.lua b/lua/refjump/jump.lua index 66c4eb6..183f84b 100644 --- a/lua/refjump/jump.lua +++ b/lua/refjump/jump.lua @@ -63,6 +63,20 @@ local function jump_to(next_reference) vim.cmd('normal! zv') end +---Find the index of a reference in the references list +---@param reference RefjumpReference +---@param references RefjumpReference[] +---@return integer|nil +local function find_reference_index(reference, references) + for i, ref in ipairs(references) do + if ref.range.start.line == reference.range.start.line + and ref.range.start.character == reference.range.start.character then + return i + end + end + return nil +end + ---@param next_reference RefjumpReference ---@param forward boolean ---@param references RefjumpReference[] @@ -74,6 +88,14 @@ local function jump_to_next_reference(next_reference, forward, references) if next_reference then jump_to(next_reference) + + -- Display current index and total count + local current_index = find_reference_index(next_reference, references) + if current_index then + local total_count = #references + local bufnr = vim.api.nvim_get_current_buf() + require('refjump.counter').show(current_index, total_count, bufnr) + end else vim.notify('refjump.nvim: Could not find the next reference', vim.log.levels.WARN) end From 9a2c688b8495570474a289ccbd6c13bc62655c8d Mon Sep 17 00:00:00 2001 From: Petur Subev Date: Sat, 3 Jan 2026 22:02:29 +0200 Subject: [PATCH 2/4] refactor: add state module and address PR review feedback - Add lua/refjump/state.lua for per-buffer state storage - Expose get_reference_info() for statusline integration - Remove counter.hl_group option, link RefjumpCounter to WarningMsg - Refactor find_reference_index() to use vim.iter - Move counter.enable check to jump.lua with show_counter() helper - Add test infrastructure with plenary.nvim (12 tests) - Update CI workflow to run tests on push/PR --- .github/workflows/refjump.yml | 27 +++++- lua/refjump/counter.lua | 16 +--- lua/refjump/highlight.lua | 11 +-- lua/refjump/init.lua | 9 +- lua/refjump/jump.lua | 30 ++++-- lua/refjump/state.lua | 75 +++++++++++++++ tests/minimal_init.lua | 17 ++++ tests/state_spec.lua | 169 ++++++++++++++++++++++++++++++++++ 8 files changed, 317 insertions(+), 37 deletions(-) create mode 100644 lua/refjump/state.lua create mode 100644 tests/minimal_init.lua create mode 100644 tests/state_spec.lua diff --git a/.github/workflows/refjump.yml b/.github/workflows/refjump.yml index e5e5034..00e7e08 100644 --- a/.github/workflows/refjump.yml +++ b/.github/workflows/refjump.yml @@ -2,17 +2,36 @@ name: Refjump Workflow on: push: branches: [main] - paths: - - .github/workflows/refjump.yml - - README.md + pull_request: + branches: [main] permissions: contents: write jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Neovim + run: | + sudo apt-get update + sudo apt-get install -y software-properties-common + sudo add-apt-repository -y ppa:neovim-ppa/stable + sudo apt-get update + sudo apt-get install -y neovim + - name: Install Plenary + run: | + git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/lazy/plenary.nvim + - name: Run Tests + run: | + nvim --headless -u tests/minimal_init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/minimal_init.lua'}" docs: name: Pandoc to Vimdoc runs-on: ubuntu-latest + needs: test + if: github.event_name == 'push' && github.ref == 'refs/heads/main' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: panvimdoc uses: kdheepak/panvimdoc@main with: diff --git a/lua/refjump/counter.lua b/lua/refjump/counter.lua index c8cab2d..ecd6af3 100644 --- a/lua/refjump/counter.lua +++ b/lua/refjump/counter.lua @@ -6,18 +6,12 @@ local counter_namespace = vim.api.nvim_create_namespace('RefjumpCounter') ---Name of the highlight group for the counter local counter_hl_name = 'RefjumpCounter' ----Create fallback highlight group if it doesn't exist -function M.create_fallback_hl_group(fallback_hl) +---Create highlight group linked to WarningMsg if it doesn't exist +function M.create_hl_group() local hl = vim.api.nvim_get_hl(0, { name = counter_hl_name }) if vim.tbl_isempty(hl) then - -- Create a distinctive highlight: yellow/orange text on dark background - -- with bold for extra visibility - vim.api.nvim_set_hl(0, counter_hl_name, { - fg = '#FFA500', -- Orange/yellow color - bold = true, - default = true, - }) + vim.api.nvim_set_hl(0, counter_hl_name, { link = 'WarningMsg' }) end end @@ -26,10 +20,6 @@ end ---@param total_count integer Total number of references ---@param bufnr integer Buffer number function M.show(current_index, total_count, bufnr) - if not require('refjump').get_options().counter.enable then - return - end - -- Get current cursor position local cursor = vim.api.nvim_win_get_cursor(0) local line = cursor[1] - 1 -- Convert to 0-indexed diff --git a/lua/refjump/highlight.lua b/lua/refjump/highlight.lua index 70667fe..bd2ef46 100644 --- a/lua/refjump/highlight.lua +++ b/lua/refjump/highlight.lua @@ -1,8 +1,5 @@ local M = {} ----Used to keep track of if LSP reference highlights should be enabled -local highlight_references = false - local highlight_namespace = vim.api.nvim_create_namespace('RefjumpReferenceHighlights') local reference_hl_name = 'RefjumpReference' @@ -37,8 +34,6 @@ function M.enable(references, bufnr) { inclusive = false } ) end - - highlight_references = true end ---@deprecated Use `disable()` instead @@ -49,11 +44,13 @@ function M.disable_reference_highlights() end function M.disable() - if not highlight_references then + local state = require('refjump.state') + if not state.is_active() then vim.api.nvim_buf_clear_namespace(0, highlight_namespace, 0, -1) require('refjump.counter').clear(0) + state.clear() else - highlight_references = false + state.clear() end end diff --git a/lua/refjump/init.lua b/lua/refjump/init.lua index 2e7404c..583905b 100644 --- a/lua/refjump/init.lua +++ b/lua/refjump/init.lua @@ -11,7 +11,6 @@ local M = {} ---@class RefjumpCounterOptions ---@field enable? boolean Show virtual text counter at end of line ----@field hl_group? string Highlight group for counter text ---@class RefjumpIntegrationOptions ---@field demicolon? { enable?: boolean } Make `]r`/`[r` repeatable with `;`/`,` using demicolon.nvim @@ -34,7 +33,6 @@ local options = { }, counter = { enable = true, - hl_group = 'WarningMsg', }, integrations = { demicolon = { @@ -66,10 +64,15 @@ function M.setup(opts) end if options.counter.enable then - require('refjump.counter').create_fallback_hl_group(options.counter.hl_group) + require('refjump.counter').create_hl_group() end end M.reference_jump = require('refjump.jump').reference_jump +---Get info about current reference position (for statusline use) +---@param bufnr? integer Buffer number (defaults to current buffer) +---@return { index: integer|nil, total: integer } +M.get_reference_info = require('refjump.state').get_reference_info + return M diff --git a/lua/refjump/jump.lua b/lua/refjump/jump.lua index 183f84b..b966700 100644 --- a/lua/refjump/jump.lua +++ b/lua/refjump/jump.lua @@ -68,13 +68,22 @@ end ---@param references RefjumpReference[] ---@return integer|nil local function find_reference_index(reference, references) - for i, ref in ipairs(references) do - if ref.range.start.line == reference.range.start.line - and ref.range.start.character == reference.range.start.character then - return i - end + local idx, _ = vim.iter(references):enumerate():find(function(_, ref) + return ref.range.start.line == reference.range.start.line + and ref.range.start.character == reference.range.start.character + end) + return idx +end + +---Display the reference counter if enabled +---@param current_index integer +---@param total_count integer +---@param bufnr integer +local function show_counter(current_index, total_count, bufnr) + if not require('refjump').get_options().counter.enable then + return end - return nil + require('refjump.counter').show(current_index, total_count, bufnr) end ---@param next_reference RefjumpReference @@ -89,12 +98,13 @@ local function jump_to_next_reference(next_reference, forward, references) if next_reference then jump_to(next_reference) - -- Display current index and total count + -- Find current index and update state local current_index = find_reference_index(next_reference, references) + local bufnr = vim.api.nvim_get_current_buf() + if current_index then - local total_count = #references - local bufnr = vim.api.nvim_get_current_buf() - require('refjump.counter').show(current_index, total_count, bufnr) + require('refjump.state').set(references, current_index, bufnr) + show_counter(current_index, #references, bufnr) end else vim.notify('refjump.nvim: Could not find the next reference', vim.log.levels.WARN) diff --git a/lua/refjump/state.lua b/lua/refjump/state.lua new file mode 100644 index 0000000..3781dcb --- /dev/null +++ b/lua/refjump/state.lua @@ -0,0 +1,75 @@ +local M = {} + +---@class RefjumpBufferState +---@field references RefjumpReference[] +---@field current_index integer|nil + +---Per-buffer state storage +---@type table +local buffer_states = {} + +---Get or create state for a buffer +---@param bufnr integer +---@return RefjumpBufferState +local function get_buffer_state(bufnr) + if not buffer_states[bufnr] then + buffer_states[bufnr] = { + references = {}, + current_index = nil, + } + end + return buffer_states[bufnr] +end + +---Get info about current reference position (for statusline use) +---@param bufnr? integer Buffer number (defaults to current buffer) +---@return { index: integer|nil, total: integer } +function M.get_reference_info(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + local state = get_buffer_state(bufnr) + return { + index = state.current_index, + total = #state.references, + } +end + +---Check if reference navigation is currently active for a buffer +---@param bufnr? integer Buffer number (defaults to current buffer) +---@return boolean +function M.is_active(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + local state = get_buffer_state(bufnr) + return state.current_index ~= nil and #state.references > 0 +end + +---Update state after jumping to a reference (internal use) +---@param references RefjumpReference[] +---@param current_index integer|nil +---@param bufnr? integer Buffer number (defaults to current buffer) +function M.set(references, current_index, bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + local state = get_buffer_state(bufnr) + state.references = references or {} + state.current_index = current_index +end + +---Clear state for a buffer (internal use) +---@param bufnr? integer Buffer number (defaults to current buffer) +function M.clear(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + buffer_states[bufnr] = nil +end + +---Clean up state when buffer is deleted +local function setup_buffer_cleanup() + vim.api.nvim_create_autocmd('BufDelete', { + group = vim.api.nvim_create_augroup('refjump_state_cleanup', { clear = true }), + callback = function(event) + buffer_states[event.buf] = nil + end, + }) +end + +setup_buffer_cleanup() + +return M diff --git a/tests/minimal_init.lua b/tests/minimal_init.lua new file mode 100644 index 0000000..1abf295 --- /dev/null +++ b/tests/minimal_init.lua @@ -0,0 +1,17 @@ +-- Minimal init for running tests +local plenary_path = vim.fn.stdpath('data') .. '/lazy/plenary.nvim' + +if vim.fn.isdirectory(plenary_path) == 0 then + vim.fn.system({ + 'git', + 'clone', + '--depth=1', + 'https://github.com/nvim-lua/plenary.nvim', + plenary_path, + }) +end + +vim.opt.rtp:prepend(plenary_path) +vim.opt.rtp:prepend(vim.fn.getcwd()) + +vim.cmd('runtime plugin/plenary.vim') diff --git a/tests/state_spec.lua b/tests/state_spec.lua new file mode 100644 index 0000000..ec9f8a6 --- /dev/null +++ b/tests/state_spec.lua @@ -0,0 +1,169 @@ +local state = require('refjump.state') + +describe('refjump.state', function() + local bufnr + + before_each(function() + -- Create a fresh buffer for each test + bufnr = vim.api.nvim_create_buf(false, true) + vim.api.nvim_set_current_buf(bufnr) + -- Clear any existing state + state.clear(bufnr) + end) + + after_each(function() + -- Clean up buffer + if vim.api.nvim_buf_is_valid(bufnr) then + vim.api.nvim_buf_delete(bufnr, { force = true }) + end + end) + + describe('get_reference_info', function() + it('returns empty state initially', function() + local info = state.get_reference_info(bufnr) + assert.is_nil(info.index) + assert.equals(0, info.total) + end) + + it('returns correct info after set()', function() + local refs = { + { range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 5 } } }, + { range = { start = { line = 5, character = 0 }, ['end'] = { line = 5, character = 5 } } }, + { range = { start = { line = 10, character = 0 }, ['end'] = { line = 10, character = 5 } } }, + } + state.set(refs, 2, bufnr) + + local info = state.get_reference_info(bufnr) + assert.equals(2, info.index) + assert.equals(3, info.total) + end) + + it('uses current buffer when bufnr not provided', function() + local refs = { + { range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 5 } } }, + } + state.set(refs, 1, bufnr) + + -- Should work without explicit bufnr since we set current buf in before_each + local info = state.get_reference_info() + assert.equals(1, info.index) + assert.equals(1, info.total) + end) + end) + + describe('is_active', function() + it('returns false initially', function() + assert.is_false(state.is_active(bufnr)) + end) + + it('returns true after set() with valid data', function() + local refs = { + { range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 5 } } }, + } + state.set(refs, 1, bufnr) + + assert.is_true(state.is_active(bufnr)) + end) + + it('returns false when index is nil', function() + local refs = { + { range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 5 } } }, + } + state.set(refs, nil, bufnr) + + assert.is_false(state.is_active(bufnr)) + end) + + it('returns false when references is empty', function() + state.set({}, 1, bufnr) + + assert.is_false(state.is_active(bufnr)) + end) + end) + + describe('set', function() + it('stores references and index', function() + local refs = { + { range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 5 } } }, + { range = { start = { line = 5, character = 0 }, ['end'] = { line = 5, character = 5 } } }, + } + state.set(refs, 1, bufnr) + + local info = state.get_reference_info(bufnr) + assert.equals(1, info.index) + assert.equals(2, info.total) + end) + + it('handles nil references gracefully', function() + state.set(nil, 1, bufnr) + + local info = state.get_reference_info(bufnr) + assert.equals(1, info.index) + assert.equals(0, info.total) + end) + end) + + describe('clear', function() + it('clears state for buffer', function() + local refs = { + { range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 5 } } }, + } + state.set(refs, 1, bufnr) + + state.clear(bufnr) + + local info = state.get_reference_info(bufnr) + assert.is_nil(info.index) + assert.equals(0, info.total) + assert.is_false(state.is_active(bufnr)) + end) + end) + + describe('per-buffer isolation', function() + it('maintains separate state for different buffers', function() + local bufnr2 = vim.api.nvim_create_buf(false, true) + + local refs1 = { + { range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 5 } } }, + } + local refs2 = { + { range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 5 } } }, + { range = { start = { line = 5, character = 0 }, ['end'] = { line = 5, character = 5 } } }, + { range = { start = { line = 10, character = 0 }, ['end'] = { line = 10, character = 5 } } }, + } + + state.set(refs1, 1, bufnr) + state.set(refs2, 3, bufnr2) + + local info1 = state.get_reference_info(bufnr) + local info2 = state.get_reference_info(bufnr2) + + assert.equals(1, info1.index) + assert.equals(1, info1.total) + assert.equals(3, info2.index) + assert.equals(3, info2.total) + + -- Cleanup + vim.api.nvim_buf_delete(bufnr2, { force = true }) + end) + + it('clearing one buffer does not affect others', function() + local bufnr2 = vim.api.nvim_create_buf(false, true) + + local refs = { + { range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 5 } } }, + } + + state.set(refs, 1, bufnr) + state.set(refs, 1, bufnr2) + + state.clear(bufnr) + + assert.is_false(state.is_active(bufnr)) + assert.is_true(state.is_active(bufnr2)) + + -- Cleanup + vim.api.nvim_buf_delete(bufnr2, { force = true }) + end) + end) +end) From a83a906c4319ab3639ac8f3fcef15262c8635b7c Mon Sep 17 00:00:00 2001 From: Petur Subev Date: Sat, 3 Jan 2026 23:02:12 +0200 Subject: [PATCH 3/4] fix: restore highlight_references flag to fix state persistence The state was being cleared immediately on CursorMoved after a jump, preventing lualine/statusline from reading reference info. - Restore highlight_references toggle flag in highlight.lua - Add highlight.is_active() to expose the flag - Update state.is_active() to delegate to highlight.is_active() - Fix disable() to use toggle pattern: first call flips flag, second clears - Update tests to reflect new is_active() behavior --- lua/refjump/highlight.lua | 18 +++++++++++++---- lua/refjump/state.lua | 9 +++------ tests/state_spec.lua | 42 ++++++++++++++++++++++++--------------- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/lua/refjump/highlight.lua b/lua/refjump/highlight.lua index bd2ef46..f2b209d 100644 --- a/lua/refjump/highlight.lua +++ b/lua/refjump/highlight.lua @@ -1,5 +1,8 @@ local M = {} +---Used to keep track of if LSP reference highlights should be enabled +local highlight_references = false + local highlight_namespace = vim.api.nvim_create_namespace('RefjumpReferenceHighlights') local reference_hl_name = 'RefjumpReference' @@ -34,6 +37,14 @@ function M.enable(references, bufnr) { inclusive = false } ) end + + highlight_references = true +end + +---Check if reference highlights are currently active +---@return boolean +function M.is_active() + return highlight_references end ---@deprecated Use `disable()` instead @@ -44,13 +55,12 @@ function M.disable_reference_highlights() end function M.disable() - local state = require('refjump.state') - if not state.is_active() then + if not highlight_references then vim.api.nvim_buf_clear_namespace(0, highlight_namespace, 0, -1) require('refjump.counter').clear(0) - state.clear() + require('refjump.state').clear() else - state.clear() + highlight_references = false end end diff --git a/lua/refjump/state.lua b/lua/refjump/state.lua index 3781dcb..9c7f0cd 100644 --- a/lua/refjump/state.lua +++ b/lua/refjump/state.lua @@ -33,13 +33,10 @@ function M.get_reference_info(bufnr) } end ----Check if reference navigation is currently active for a buffer ----@param bufnr? integer Buffer number (defaults to current buffer) +---Check if reference navigation is currently active ---@return boolean -function M.is_active(bufnr) - bufnr = bufnr or vim.api.nvim_get_current_buf() - local state = get_buffer_state(bufnr) - return state.current_index ~= nil and #state.references > 0 +function M.is_active() + return require('refjump.highlight').is_active() end ---Update state after jumping to a reference (internal use) diff --git a/tests/state_spec.lua b/tests/state_spec.lua index ec9f8a6..f7c03a4 100644 --- a/tests/state_spec.lua +++ b/tests/state_spec.lua @@ -1,4 +1,5 @@ local state = require('refjump.state') +local highlight = require('refjump.highlight') describe('refjump.state', function() local bufnr @@ -53,31 +54,34 @@ describe('refjump.state', function() describe('is_active', function() it('returns false initially', function() - assert.is_false(state.is_active(bufnr)) + assert.is_false(state.is_active()) end) - it('returns true after set() with valid data', function() + it('returns true when highlights are enabled', function() local refs = { { range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 5 } } }, } - state.set(refs, 1, bufnr) + -- Enable highlights to make is_active() return true + highlight.enable(refs, bufnr) + + assert.is_true(state.is_active()) - assert.is_true(state.is_active(bufnr)) + -- Cleanup: disable highlights (need to call twice due to toggle behavior) + highlight.disable() + highlight.disable() end) - it('returns false when index is nil', function() + it('returns false after highlights are disabled', function() local refs = { { range = { start = { line = 0, character = 0 }, ['end'] = { line = 0, character = 5 } } }, } - state.set(refs, nil, bufnr) - - assert.is_false(state.is_active(bufnr)) - end) + highlight.enable(refs, bufnr) + -- First disable flips flag to false + highlight.disable() + -- Second disable clears highlights + highlight.disable() - it('returns false when references is empty', function() - state.set({}, 1, bufnr) - - assert.is_false(state.is_active(bufnr)) + assert.is_false(state.is_active()) end) end) @@ -115,7 +119,6 @@ describe('refjump.state', function() local info = state.get_reference_info(bufnr) assert.is_nil(info.index) assert.equals(0, info.total) - assert.is_false(state.is_active(bufnr)) end) end) @@ -159,8 +162,15 @@ describe('refjump.state', function() state.clear(bufnr) - assert.is_false(state.is_active(bufnr)) - assert.is_true(state.is_active(bufnr2)) + -- State data for bufnr should be cleared + local info1 = state.get_reference_info(bufnr) + assert.is_nil(info1.index) + assert.equals(0, info1.total) + + -- State data for bufnr2 should still exist + local info2 = state.get_reference_info(bufnr2) + assert.equals(1, info2.index) + assert.equals(1, info2.total) -- Cleanup vim.api.nvim_buf_delete(bufnr2, { force = true }) From 58e1c96d1a2dbcd49a2436dd92af5bd80b1470c9 Mon Sep 17 00:00:00 2001 From: Petur Subev Date: Sat, 3 Jan 2026 23:23:12 +0200 Subject: [PATCH 4/4] docs: add counter option and statusline integration example - Document counter.enable option in configuration - Document RefjumpCounter highlight group - Add statusline integration section with lualine example --- README.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 739a335..10e9bb9 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,9 @@ opts = { enable = true, -- Highlight the LSP references on jump auto_clear = true, -- Automatically clear highlights when cursor moves }, + counter = { + enable = true, -- Show [X/Y] virtual text counter at end of line + }, integrations = { demicolon = { enable = true, -- Make `]r`/`[r` repeatable with `;`/`,` using demicolon.nvim @@ -53,10 +56,36 @@ opts = { ### Highlights -Refjump highlights the references by default. It uses the highlight group `RefjumpReference`. To change the highlight, see `:help nvim_set_hl()`. +Refjump uses the following highlight groups: + +- `RefjumpReference` - for highlighting the references (links to `LspReferenceText` by default) +- `RefjumpCounter` - for the virtual text counter (links to `WarningMsg` by default) + +To change the highlights, see `:help nvim_set_hl()`. ## Integrations +### Statusline + +Refjump exposes `get_reference_info()` for statusline integration. This returns `{ index = number|nil, total = number }` with the current reference position. + +Example for [lualine.nvim](https://github.com/nvim-lualine/lualine.nvim): + +```lua +lualine_x = { + { + function() + local refjump = require("refjump") + local info = refjump.get_reference_info() + if info.index then + return string.format("[%d/%d]", info.index, info.total) + end + return "" + end, + }, +}, +``` + ### Demicolon This plugin integrates with [demicolon.nvim](https://github.com/mawkler/demicolon.nvim). Demicolon lets you repeat `]r`/`[r` jumps with `;`/`,` (you can also still repeat `t`/`f`/`T`/`F` like you would expect). Refjump will cache the list of LSP references which gives you super responsive jump repetitions.