Files
hop.nvim/lua/hop/jump_regex.lua
Maksym Medvied a7961ac34d fix: make case_insensitive work even if smartcase is not set (#101)
https://github.com/smoka7/hop.nvim/pull/76 changed the behavior of
case_insensitive in the following way: now if case_insensitive is set it
only works if ``smartcase`` is set and ``starts_with_uppercase(pat)`` is
true.

This patch makes ``case_insensitive`` work even if ``smartcase`` is not
set (``smartcase`` [is not set by default](
https://neovim.io/doc/user/options.html#'smartcase')).
Otherwise ``case_insensitive = true`` doesn't work in neovim with
default settings and requires setting ``smartcase = true`` just to make
``case_insensitive = true`` work.
2025-02-26 21:24:03 +03:30

177 lines
4.2 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- Match jump target and return jump range within line
---@class Regex
---@field oneshot boolean
---@field match fun(s:string, jctx:JumpContext, opts:Options):ColumnRange Get column range within the line string
---@class JumpRegexModule
local M = {}
local hint = require('hop.hint')
local window = require('hop.window')
local mappings = require('hop.mappings')
-- JumpRegex modes for the buffer-driven generator.
---@param s string
---@return boolean
local function starts_with_uppercase(s)
if #s == 0 then
return false
end
local f = s:sub(1, vim.fn.byteidx(s, 1))
-- if its a space, we assume its not uppercase, even though Lua doesnt agree with us; I mean, Lua is horrible, who
-- would like to argue with that creature, right?
if f == ' ' then
return false
end
return f:upper() == f
end
-- JumpRegex by searching a pattern.
---@param pat string
---@param plain_search boolean|nil
---@return Regex
local function regex_by_searching(pat, plain_search)
if plain_search then
pat = vim.fn.escape(pat, '\\/.$^~[]')
end
local regex = vim.regex(pat)
return {
oneshot = false,
match = function(s)
return regex:match_str(s)
end,
}
end
-- Wrapper over M.regex_by_searching to add support for case sensitivity.
---@param pat string
---@param plain_search boolean
---@param opts Options
---@return Regex
function M.regex_by_case_searching(pat, plain_search, opts)
local pat_case = ''
if opts.case_insensitive and not (vim.o.smartcase and starts_with_uppercase(pat)) then
pat_case = '\\c'
end
local pat_mappings = mappings.checkout(pat, opts)
if plain_search then
pat = vim.fn.escape(pat, '\\/.$^~[]')
end
if pat_mappings ~= '' then
pat = string.format([[\(%s\)\|\(%s\)]], pat, pat_mappings)
end
pat = pat .. pat_case
local regex = vim.regex(pat)
return {
oneshot = false,
match = function(s)
return regex:match_str(s)
end,
}
end
-- Word regex.
---@return Regex
function M.regex_by_word_start()
return regex_by_searching('\\k\\+')
end
-- Camel case regex.
---@return Regex
function M.regex_by_camel_case()
local camel = '\\u\\l\\+'
local acronyms = '\\u\\+\\ze\\u\\l'
local upper = '\\u\\+'
local lower = '\\l\\+'
local rgb = '#\\x\\+\\>'
local ox = '\\<0[xX]\\x\\+\\>'
local oo = '\\<0[oO][0-7]\\+\\>'
local ob = '\\<0[bB][01]\\+\\>'
local num = '\\d\\+'
local tab = { camel, acronyms, upper, lower, rgb, ox, oo, ob, num, '\\~', '!', '@', '#', '$' }
-- regex that matches camel or acronyms or upper ... or num ...
local patStr = '\\%(\\%(' .. table.concat(tab, '\\)\\|\\%(') .. '\\)\\)'
local pat = vim.regex(patStr)
return {
oneshot = false,
match = function(s)
return pat:match_str(s)
end,
}
end
-- Line regex.
---@return Regex
function M.by_line_start()
return {
oneshot = true,
---@param jctx JumpContext
match = function(_, jctx)
if window.is_active_line(jctx.win_ctx, jctx.line_ctx) then
return
end
return 0, 1
end,
}
end
-- Line regex at cursor position.
---@return Regex
function M.regex_by_vertical()
return {
oneshot = true,
---@param jctx JumpContext
match = function(s, jctx, opts)
if window.is_cursor_line(jctx.win_ctx, jctx.line_ctx) then
if window.is_active_window(jctx.win_ctx) then
return
end
if opts.direction == hint.HintDirection.AFTER_CURSOR then
return 0, 1
end
end
local idx = window.cell2char(s, jctx.win_ctx.col_first)
local col = vim.fn.byteidx(s, idx)
if -1 < col and col < #s then
return col, col + 1
else
return #s - 1, #s
end
end,
}
end
-- Line regex skipping finding the first non-whitespace character on each line.
---@return Regex
function M.regex_by_line_start_skip_whitespace()
local regex = vim.regex('\\S')
return {
oneshot = true,
---@param jctx JumpContext
match = function(s, jctx)
if window.is_active_line(jctx.win_ctx, jctx.line_ctx) then
return
end
return regex:match_str(s)
end,
}
end
-- Anywhere regex.
---@return Regex
function M.regex_by_anywhere()
return regex_by_searching('\\v(<.|^$)|(.>|^$)|(\\l)\\zs(\\u)|(_\\zs.)|(#\\zs.)')
end
return M