Sj.nvim
Search based navigation combined with quick jump features.
Install / Use
/learn @woosaaahh/Sj.nvimREADME
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_lengthand 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
node-connect
347.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.8kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.0kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
