feat(picker.matcher): new opts.matcher.file_pos which defaults to true to support patterns like file:line:col or file:line. Closes #517. Closes #496. Closes #651

This commit is contained in:
Folke Lemaitre 2025-01-20 11:40:02 +01:00
parent 01b70b9f01
commit 5e00b0ab27
No known key found for this signature in database
GPG key ID: 41F8B1FBACAE2040
5 changed files with 38 additions and 3 deletions

View file

@ -109,6 +109,7 @@ local defaults = {
ignorecase = true, -- use ignorecase
sort_empty = false, -- sort results when the search string is empty
filename_bonus = true, -- give bonus for matching file names (last part of the path)
file_pos = true, -- support patterns like `file:line:col` and `file:line`
},
sort = {
-- default sort is by score, text length and index

View file

@ -205,6 +205,7 @@ function M:clear()
self.topk:clear()
self.top, self.cursor = 1, 1
self.items = {}
self._current = nil
self.dirty = true
if next(self.items) == nil then
return

View file

@ -10,6 +10,7 @@ local Async = require("snacks.picker.util.async")
---@field live? boolean
---@field score snacks.picker.Score
---@field sorting? boolean
---@field file? {path: string, pos: snacks.picker.Pos}
local M = {}
M.__index = M
M.DEFAULT_SCORE = 1000
@ -114,6 +115,7 @@ end
function M:init(opts)
opts = opts or {}
self.tick = self.tick + 1
self.file = nil
local pattern = vim.trim(opts.pattern or self.pattern)
self.mods = {}
self.pattern = pattern
@ -158,7 +160,29 @@ end
function M:_prepare(pattern)
---@type snacks.picker.matcher.Mods
local mods = { pattern = pattern, entropy = 0, chars = {} }
local field, p = pattern:match("^([%w_]+):(.*)$")
local file_patterns = {
"^(.*[/\\].*):(%d*):(%d*)$",
"^(.*[/\\].*):(%d*)$",
"^(.+%.[a-z_]+):(%d*):(%d*)$",
"^(.+%.[a-z_]+):(%d*)$",
}
for _, p in ipairs(file_patterns) do
local file, line, col = pattern:match(p)
if file then
mods.field = "file"
mods.pattern = file .. "$"
self.file = {
path = file,
pos = { tonumber(line) or 1, tonumber(col) or 0 },
}
break
end
end
-- minimum two chars for field pattern
local field, p = pattern:match("^([%w_][%w_]+):(.*)$")
if field then
mods.field = field
mods.pattern = p
@ -217,6 +241,9 @@ function M:update(item)
if item.match_tick == self.tick then
return false
end
if item.match_pos then
item.pos = nil
end
local score = self:match(item)
if score ~= 0 then
if item.score_add then
@ -225,6 +252,10 @@ function M:update(item)
if item.score_mul then
score = score * item.score_mul
end
if self.file and not item.pos then
item.pos = self.file.pos
item.match_pos = true
end
end
item.match_tick, item.score = self.tick, score
return true

View file

@ -1,5 +1,6 @@
---@class snacks.picker.Preview
---@field item? snacks.picker.Item
---@field pos? snacks.picker.Pos
---@field win snacks.win
---@field preview snacks.picker.preview
---@field state table<string, any>
@ -101,11 +102,12 @@ end
---@param picker snacks.Picker
function M:show(picker)
local item, prev = picker:current({ resolve = false }), self.item
if self.item == item then
if self.item == item and self.pos == (item and item.pos or nil) then
return
end
Snacks.picker.util.resolve(item)
self.item = item
self.pos = item and item.pos or nil
if item then
local buf = self.win.buf
local ok, err = pcall(

View file

@ -182,7 +182,7 @@ function M.parse(str)
end
--- Resolves the item if it has a resolve function
---@param item snacks.picker.Item
---@param item snacks.picker.Item?
function M.resolve(item)
if item and item.resolve then
item.resolve(item)