SkillAgentSearch skills...

Sj.nvim

Search based navigation combined with quick jump features.

Install / Use

/learn @woosaaahh/Sj.nvim
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<!-- vim: expandtab tabstop=2 -->

SJ - Search and Jump

Search based navigation combined with quick jump features.

<p align="center"> Demo<br> <img src="https://user-images.githubusercontent.com/111681540/197946515-e2818592-bf3d-439a-99f3-8c9eabd2fbce.gif"> </p> <p align="center"> Screenshots<br> <img src="https://user-images.githubusercontent.com/111681540/197934569-999dba0d-bbd2-4a9b-8be5-997207ac0cc0.png"> <img src="https://user-images.githubusercontent.com/111681540/197934582-b860c767-64f4-4b44-b38b-007afb4e8cc1.png"> </p>

Requirements

Only Neovim 0.9+ is required, nothing more.

Usage

The main goal of this plugin is to quickly jump to any characters using a search pattern.

By default, the search is made forward and only in visible lines of the current buffer.

To start using SJ, you can add the lines below in your configuration for Neovim.

local sj = require("sj")
sj.setup()

vim.keymap.set("n", "s", sj.run)
vim.keymap.set("n", "<A-,>", sj.prev_match)
vim.keymap.set("n", "<A-;>", sj.next_match)
vim.keymap.set("n", "<localleader>s", sj.redo)

As soon as you use the keymap assigned to sj.run() and start typing the pattern :

  • the highlights in the buffer will change ;
  • all matches will be highlighted and will have a label assigned to them ;
  • the pattern is displayed in the command line.

While searching, you can use the keymaps below :

| Keymap | Description | |---------------------|------------------------------------------------------------------| | <Escape> | cancel the search | | <Enter> | jump to the focused match | | :a, :b, :c | jump to the the match with the label a, b or c | | <A-,>, <A-;> | focus the previous or next match | | <C-p>, <C-n> | select the previous or next pattern | | <BS> | delete the previous character | | <C-w> | delete the previous word | | <C-u> | delete the whole pattern | | <A-BS> | restore the pattern to the last version having matches | | <A-q> | send the search results to the quickfix list |

After the search, you can call sj.prev_match() and sj.next_match() to jump on the previous/next match or sj.redo() to redo a search using the last pattern.

Notes :

  • When there are no matches, the pattern in the cmdline will have a different color ;
  • When you use max_pattern_length and you reach that length limit, the labels color will change to indicate that the next key should be for a label and not for the pattern ; (When reaching this limit, no need to type : before the label)
  • You can use an empty separator separator = "" which will avoid the need to type an extra character but will reduce the number of available labels.

Configuration

Here is the default configuration :

local config = {
  auto_jump = false, -- if true, automatically jump on the sole match
  forward_search = true, -- if true, the search will be done from top to bottom
  highlights_timeout = 0, -- if > 0, wait for 'updatetime' + N ms to clear hightlights (sj.prev_match/sj.next_match)
  inclusive = false, -- if true, the jump target will be included with 'operator-pending' keymaps
  max_pattern_length = 0, -- if > 0, wait for a label after N characters
  pattern = "", -- predefined pattern to use at the start of a search
  pattern_type = "vim", -- how to interpret the pattern (lua_plain, lua, vim, vim_very_magic)
  preserve_highlights = true, -- if true, create an autocmd to preserve highlights when switching colorscheme
  prompt_prefix = "", -- if set, the string will be used as a prefix in the command line
  relative_labels = false, -- if true, labels are ordered from the cursor position, not from the top of the buffer
  search_scope = "visible_lines", -- (current_line, visible_lines_above, visible_lines_below, visible_lines, buffer)
  select_window = false, -- if true, ask for a window to jump to before starting the search
  separator = ":", -- character used to split the user input in <pattern> and <label> (can be empty)
  stop_on_fail = true, -- if true, the search will stop when a search fails (no matches)
  update_search_register = false, -- if true, update the search register with the last used pattern
  use_last_pattern = false, -- if true, reuse the last pattern for next calls
  use_overlay = true, -- if true, apply an overlay to better identify labels and matches
  wrap_jumps = vim.o.wrapscan, -- if true, wrap the jumps when focusing previous or next label

  --- keymaps used during the search
  keymaps = { 
    cancel = "<Esc>", -- cancel the search
    validate = "<CR>", -- jump to the focused match
    prev_match = "<A-,>", -- focus the previous match
    next_match = "<A-;>", -- focus the next match
    prev_pattern = "<C-p>", -- select the previous pattern while searching
    next_pattern = "<C-n>", -- select the next pattern while searching
    ---
    delete_prev_char = "<BS>", -- delete the previous character
    delete_prev_word = "<C-w>", -- delete the previous word
    delete_pattern = "<C-u>", -- delete the whole pattern
    restore_pattern = "<A-BS>", -- restore the pattern to the last version having matches
    ---
    send_to_qflist = "<A-q>", --- send the search results to the quickfix list
  },

  --- labels used for each matches. (one-character strings only)
  -- stylua: ignore
  labels = {
    "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
    "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
    "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
    "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ",", ";", "!",
  },
}

and here is a configuration sample :

DISCLAIMER : This plugin is not intended to replace the native functions of Neovim. <br>I do not recommend adding keymaps that replaces /, ?, f/F, t/T....

local sj = require("sj")
local sj_cache = require("sj.cache")

--- Configuration ------------------------------------------------------------------------

sj.setup({
  prompt_prefix = "/",

  -- stylua: ignore
  highlights = {
    SjFocusedLabel = { bold = false, italic = false, fg = "#FFFFFF", bg = "#C000C0", },
    SjLabel =        { bold = true , italic = false, fg = "#000000", bg = "#5AA5DE", },
    SjLimitReached = { bold = true , italic = false, fg = "#000000", bg = "#DE945A", },
    SjMatches =      { bold = false, italic = false, fg = "#DDDDDD", bg = "#005080", },
    SjNoMatches =    { bold = false, italic = false, fg = "#DE945A",                 },
    SjOverlay =      { bold = false, italic = false, fg = "#345576",                 },
  },

  keymaps = {
    send_to_qflist = "<C-q>", --- send search result to the quickfix list
  },
})

--- Keymaps ------------------------------------------------------------------------------

vim.keymap.set("n", "!", function()
	sj.run({ select_window = true })
end)

vim.keymap.set("n", "<A-!>", function()
	sj.select_window()
end)

--- visible lines -------------------------------------

vim.keymap.set({ "n", "o", "x" }, "S", function()
  vim.fn.setpos("''", vim.fn.getpos("."))
  sj.run({
    forward_search = false,
  })
end)

vim.keymap.set({ "n", "o", "x" }, "s", function()
  vim.fn.setpos("''", vim.fn.getpos("."))
  sj.run()
end)

vim.keymap.set("n", "<localleader>c", function()
  sj.run({
    max_pattern_length = 1,
    pattern_type = "lua_plain",
  })
end)

--- buffer --------------------------------------------

vim.keymap.set("n", "gS", function()
  vim.fn.setpos("''", vim.fn.getpos("."))
  sj.run({
    forward_search = false,
    search_scope = "buffer",
    update_search_register = true,
  })
end)

vim.keymap.set("n", "gs", function()
  vim.fn.setpos("''", vim.fn.getpos("."))
  sj.run({
    search_scope = "buffer",
    update_search_register = true,
  })
end)

--- current line --------------------------------------

vim.keymap.set({ "n", "o", "x" }, "<localleader>l", function()
  sj.run({
    auto_jump = true,
    max_pattern_length = 1,
    pattern_type = "lua_plain",
    search_scope = "current_line",
    use_overlay = false,
  })
end)

vim.keymap.set("o", "f", function()
	sj.run({
		auto_jump = true,
		forward_search = true,
		inclusive = true,
		max_pattern_length = 1,
		pattern_type = "lua_plain",
		search_scope = "current_line", -- works with other scopes
		use_overlay = false,
	})
end)

vim.keymap.set("o", "F", function()
	sj.run({
		auto_jump = true,
		forward_search = false,
		inclusive = true,
		max_pattern_length = 1,
		pattern_type = "lua_plain",
		search_scope = "current_line", -- works with other scopes
		use_overlay = false,
	})
end)

vim.keymap.set("o", "t", function()
	sj.run({
		auto_jump = true,
		forward_search = true,
		inclusive = false,
		max_pattern_length = 1,
		pattern_type = "lua_plain",
		search_scope = "current_line", -- works with other scopes
		use_overlay = false,
	})
end)

vim.keymap.set("o", "T", function()
	sj.run({
		auto_jump = true,
		forward_search = false,
		inclusive = false,
		max_pattern_length = 1,
		pattern_type = "lua_plain",
		search_scope = "current_line", -- works with other scopes
		use_overlay = false,
	})
end)

--- prev/next match -----------------------------------

vim.keymap.set("n", "<A-,>", function()
  sj.prev_match()
  if sj_cache.options.search_scope:match("^buffer") then
    vim.cmd("normal! zzzv")
  end
end)

vi

Related Skills

View on GitHub
GitHub Stars130
CategoryDevelopment
Updated25d ago
Forks3

Languages

Lua

Security Score

95/100

Audited on Mar 9, 2026

No findings