Skip to content

Commit

Permalink
feat: stable labels
Browse files Browse the repository at this point in the history
  • Loading branch information
folke committed Jun 9, 2023
1 parent 43b96c6 commit 3e6b345
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 52 deletions.
56 changes: 5 additions & 51 deletions lua/flash/jump.lua
Original file line number Diff line number Diff line change
@@ -1,58 +1,12 @@
local M = {}

---@param label string?
---@param state Flash.State
function M.labels(state)
local skip = {} ---@type table<string, boolean>
for _, m in ipairs(state.results) do
skip[m.next] = true
end

local labels = {} ---@type string[]
for _, l in ipairs(vim.split(state.config.labels .. state.config.labels:upper(), "")) do
if not skip[l] then
labels[#labels + 1] = l
skip[l] = true
end
end
return labels
end

---@param state Flash.State
function M.update(state)
---@type Flash.Match[]
local matches = {}
vim.list_extend(matches, state.results)
-- sort by current win, other win, then by distance
table.sort(matches, function(a, b)
if a.win ~= b.win then
local aw = a.win == state.win and 0 or a.win
local bw = b.win == state.win and 0 or b.win
return aw < bw
end
if a.from[1] ~= b.from[1] then
if a.win == state.win then
local da = math.abs(a.from[1] - state.pos[1])
local db = math.abs(b.from[1] - state.pos[1])
return da < db
end
return a.from[1] < b.from[1]
end
if a.win == state.win then
local da = math.abs(a.from[2] - state.pos[2])
local db = math.abs(b.from[2] - state.pos[2])
return da < db
end
return a.from[2] < b.from[2]
end)
---@return Flash.Match?
function M.jump(label, state)
---@type Flash.Match
local match

local labels = M.labels(state)
for _, m in ipairs(matches) do
-- only label visible matches
-- and don't label the first match in the current window
if m.visible and not (m.current and m.win == state.win and not state.config.highlight.label_first) then
m.label = table.remove(labels, 1)
if #labels == 0 then
break
end
end
end
Expand Down
108 changes: 108 additions & 0 deletions lua/flash/labeler.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
---@class Flash.Labeler
---@field state Flash.State
---@field used table<string, string>
local M = {}
M.__index = M

function M.new(state)
local self = setmetatable({}, M)
self.state = state
self.used = {}
return self
end

function M:update()
local labeler = self:labeler()
local matches = self:filter()
for _, match in ipairs(matches) do
if not labeler(match, true) then
break
end
end
for _, match in ipairs(matches) do
if not labeler(match) then
break
end
end
end

function M:labeler()
local skip = {} ---@type table<string, boolean>
for _, m in ipairs(self.state.results) do
skip[m.next] = true
end

---@type table<string, boolean>
local available = {}
local labels = {} ---@type string[]
for _, l in ipairs(vim.split(self.state.config.labels .. self.state.config.labels:upper(), "")) do
if not skip[l] then
labels[#labels + 1] = l
available[l] = true
skip[l] = true
end
end

---@param m Flash.Match
---@param used boolean?
return function(m, used)
if m.label then
return true
end
local pos = table.concat(m.from, ":")
local label ---@type string?
if used then
label = available[self.used[pos]] and self.used[pos] or nil
else
label = table.remove(labels, 1)
while label and not available[label] do
label = table.remove(labels, 1)
end
end
if label then
self.used[pos] = label
m.label = label
available[label] = nil
end
return #labels > 0
end
end

function M:filter()
---@type Flash.Match[]
local ret = {}

-- only label visible matches
-- and don't label the first match in the current window
for m, match in ipairs(self.state.results) do
if match.visible and not (self.state.current == m and not self.state.config.highlight.label_first) then
table.insert(ret, match)
end
end

-- sort by current win, other win, then by distance
table.sort(ret, function(a, b)
if a.win ~= b.win then
local aw = a.win == self.state.win and 0 or a.win
local bw = b.win == self.state.win and 0 or b.win
return aw < bw
end
if a.from[1] ~= b.from[1] then
if a.win == self.state.win then
local da = math.abs(a.from[1] - self.state.pos[1])
local db = math.abs(b.from[1] - self.state.pos[1])
return da < db
end
return a.from[1] < b.from[1]
end
if a.win == self.state.win then
local da = math.abs(a.from[2] - self.state.pos[2])
local db = math.abs(b.from[2] - self.state.pos[2])
return da < db
end
return a.from[2] < b.from[2]
end)
return ret
end

return M
9 changes: 8 additions & 1 deletion lua/flash/state.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ local Search = require("flash.search")
---@field results Flash.Match[]
---@field pattern string
---@field config Flash.Config
---@field labels boolean
---@field labeler Flash.Labeler
local M = {}

---@type Flash.State?
Expand Down Expand Up @@ -74,11 +76,12 @@ function M.setup()
})
end

---@param opts? {win:number, op:boolean, config:Flash.Config, wrap:boolean}
---@param opts? {win:number, op:boolean, config:Flash.Config, wrap:boolean, labels:boolean}
function M.new(opts)
opts = opts or {}
local self = setmetatable({}, { __index = M })
self.config = Config.get(opts.config)
self.labels = opts.labels == nil and true or opts.labels
self.op = opts.op or false
self.win = opts.win or vim.api.nvim_get_current_win()
self.buf = vim.api.nvim_win_get_buf(self.win)
Expand All @@ -87,6 +90,7 @@ function M.new(opts)
self.results = {}
self.wins = {}
self.pattern = ""
self.labeler = require("flash.labeler").new(self)
self:update("")
return self
end
Expand Down Expand Up @@ -143,6 +147,9 @@ function M:update(pattern)

local Jump = require("flash.jump")
Jump.update(self)
if self.labels then
self.labeler:update()
end
Highlight.update(self)
vim.cmd.redraw()
end
Expand Down

0 comments on commit 3e6b345

Please sign in to comment.