-
Notifications
You must be signed in to change notification settings - Fork 43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom beacons #65
Comments
I think what you're looking for is |
Thank you for the response. I was using that option, but I don't think it does quite what I was looking for. I pasted in the entry from the faq on lightspeed style highlighting, but the only time matches are highlighted is if they are unique and don't have a red label next to them. What I was looking for was for all of them to be highlighted, regardless of whether it has a label or not. |
As an update, I managed to reproduce the documentation highlighting, it just didn't work how I thought it did. What I really wanted was the ability to clear the backdrop highlight from chars pressed, and highlight the next possible next chars in the sequence with a different color than the label or the highlight applied to the unlabeled matches. Without the backdrop, it is too difficult to see immediately what the jump options are, but with the backdrop it is too difficult to read the next character in the sequence. I realize this isn't an issue if you know the two chars ahead of time, but I would prefer to have progressive feedback as sometimes I lose my train of thought. I tried implementing this with a action function, but it doesn't execute until after I have selected two chars and a label so it is impossible. I have followed your development of lightspeed and leap for a while, and I recognize this might not be consistent with your goals for the project, as some would argue it encourages a more 'step-by-step' approach. I believe that this what leap was written to avoid, so feel free to close this if the feature is something you would rather not have as part of the project. |
Nevermind, I managed to implement it. Here's a demo and the source. demo.mp4The code is kinda messy since I hacked it together, but for anyone who wants something similar, here it is: local keymap = vim.keymap
local leap = require('leap')
local ns = vim.api.nvim_create_namespace('leap_custom')
local prio = 65535
leap.setup({
highlight_unlabeled_phase_one_targets = false,
})
local api = vim.api
local hex_to_rgb = function(hex_str)
local hex = "[abcdef0-9][abcdef0-9]"
local pat = "^#(" .. hex .. ")(" .. hex .. ")(" .. hex .. ")$"
hex_str = string.lower(hex_str)
assert(string.find(hex_str, pat) ~= nil, "hex_to_rgb: invalid hex_str: " .. tostring(hex_str))
local red, green, blue = string.match(hex_str, pat)
return { tonumber(red, 16), tonumber(green, 16), tonumber(blue, 16) }
end
local blend = function(fg, bg, alpha)
fg = hex_to_rgb(fg)
bg = hex_to_rgb(bg)
local blendChannel = function(i)
local ret = (alpha * fg[i] + ((1 - alpha) * bg[i]))
return math.floor(math.min(math.max(0, ret), 255) + 0.5)
end
return string.format("#%02X%02X%02X", blendChannel(1), blendChannel(2), blendChannel(3))
end
local hl_char_one = '#FFFFFF'
local hl_char_two = blend(hl_char_one, '#565c64', .5)
vim.api.nvim_set_hl(0, 'LeapHighlightChar1', {fg = '#FFFFFF', bold = true})
vim.api.nvim_set_hl(0, 'LeapHighlightChar2', {fg = hl_char_two, bold = true})
local extmarks = {}
local state = { prev_input = nil }
local function custom_motion(kwargs)
require('leap').opts.safe_labels = {}
local function get_input()
vim.cmd('echo ""')
local hl = require('leap.highlight')
if vim.v.count == 0 and not (kwargs.unlabeled and vim.fn.mode(1):match('o')) then
hl['apply-backdrop'](hl, kwargs.cc.backward)
end
hl['highlight-cursor'](hl)
vim.cmd('redraw')
local ch = require('leap.util')['get-input-by-keymap']({str = ">"})
hl['cleanup'](hl, { vim.fn.getwininfo(vim.fn.win_getid())[1] })
if not ch then
return
end
-- Repeat with the previous input?
local repeat_key = require('leap.opts').special_keys.repeat_search
if ch == api.nvim_replace_termcodes(repeat_key, true, true, true) then
if state.prev_input then
ch = state.prev_input
else
require('leap.util').echo('no previous search')
return
end
else
state.prev_input = ch
end
return ch
end
local function get_pattern(input, max)
local chars = require('leap.opts').eq_class_of[input]
if chars then
chars = vim.tbl_map(function (ch)
if ch == '\n' then
return '\\n'
elseif ch == '\\' then
return '\\\\'
else return ch end
end, chars or {})
input = '\\(' .. table.concat(chars, '\\|') .. '\\)' -- "\(a\|b\|c\)"
end
return '\\V' .. (kwargs.multiline == false and '\\%.l' or '') .. input
end
local function get_targets(pattern, max)
local search = require('leap.search')
local bounds = search['get-horizontal-bounds']()
local get_char_at = require('leap.util')['get-char-at']
local targets = {}
for pos in search['get-match-positions'](
pattern, bounds, { ['backward?'] = kwargs.cc.backward }
) do
local char1 = get_char_at(pos, {})
local char2 = get_char_at({pos[1], pos[2]+1}, {})
table.insert(targets, { pos = {pos[1], pos[2]+1 }, chars={ char1, char2 } })
end
return targets
end
-- local get_targets = require('leap.search')['get-targets']
local input = get_input()
local pattern = get_pattern(input)
local targets = get_targets(pattern, {})
for _, target in ipairs(targets) do
local extmark_pos = tostring(target.pos[1]) .. ':' .. tostring(target.pos[2])
extmarks[extmark_pos] = {
vim.api.nvim_buf_set_extmark(0, ns, target.pos[1]-1, target.pos[2]-2, {
hl_group = 'LeapHighlightChar1',
end_col = target.pos[2]-1,
strict = false,
priority = prio,
}),
vim.api.nvim_buf_set_extmark(0, ns, target.pos[1]-1, target.pos[2]-1, {
hl_group = 'LeapHighlightChar2',
end_col = target.pos[2],
strict = false,
priority = prio,
})
}
end
local input2 = get_input()
if input2 then
input = input .. input2
else
return {}
end
pattern = get_pattern(input)
local new_targets = get_targets(pattern, {})
for i, target in ipairs(new_targets) do
extmarks[tostring(target.pos[1]) .. ':' .. tostring(target.pos[2]-1)] = nil
new_targets[i].pos = { target.pos[1], target.pos[2]-1 }
end
for _, id in pairs(extmarks) do
vim.api.nvim_buf_del_extmark(0, ns, id[1])
vim.api.nvim_buf_del_extmark(0, ns, id[2])
end
return new_targets
end
local create_mappings = function ()
local modes = { 'n', 'x', 'o' }
local opts = { noremap = true, silent = true }
local mappings = {
['s'] = { backward = false },
['S'] = { backward = true },
}
for mapping, opt in pairs(mappings) do
keymap.set(modes, mapping, function ()
require('leap').leap({
targets = custom_motion({ cc = opt }),
offset = 0
})
end, opts)
end
end
create_mappings()
vim.api.nvim_set_hl(0, 'LeapBackdrop', { link = 'Comment' })
vim.api.nvim_set_hl(0, 'LeapMatch', {
fg = 'white',
bold = true,
nocombine = true,
})
vim.api.nvim_create_autocmd('User', {
pattern = 'LeapLeave',
callback = function ()
vim.api.nvim_buf_clear_namespace(0, ns, 0, -1)
end
}) There's a small bug when the character is at the end of the line with nothing after it, but it shouldn't be too hard to fix. I'd like to have the label show after the first keypress as leap usually does, but my instinct is that that's not going to be easy to implement. I'll take a look at fixing the end of line issue and label issue some time later, as it is quite late now. |
Yes, exactly. The very idea is that you simply look at the target, type the two characters you see, and then type the label, if appears, it's not some step-by-step "okay... I've entered the first one, let's see, what shall I do next..."-kind of process. The label is the only relevant information, everything else (except in some rare cases) is redundant noise.
Wow, this is impressive though :) I've been thinking about exposing a |
I'd love to see this! Don't worry about it being phase agnostic. Currently, Clearing the highlights can be done by overriding the If the user doesn't care about the efficiency of looping over the extmarks an additional time, this is as simple as:
If the user doesn't mind their code being a bit longer, for very minor efficiency gains, they could do this:
All the user has to do is maintain a |
I think it's even simpler, users can add their extmarks to the |
That sounds pretty clean actually... the callback might return a boolean, so one might override the whole thing, or just decorate the original function if they want:
|
I've been running into issues lately since I currently have to override a lot of the behavior in ways that that break frequently when code logic or function signatures change. Do you have an idea of an implementation spec for this so I can get the ball rolling on a PR? |
This is an unofficial feature allowing DIY hacks, for people who know what they are doing. Might be removed anytime. Do not rely on it for anything serious (like a publicly released extension plugin). Related: #65
I just added that line as it is, as an undocumented "escape hatch", so that you folks can experiment with it right away. Feedback is welcome. The advantage of using an Example - this is how one could implement function highlight_unlabeled_phase_one_targets(targets, first_idx, last_idx)
local hl = require('leap.highlight')
for i = first_idx or 1, last_idx or #targets do
local target = targets[i]
if not target.label and target.chars then
local bufnr = target.wininfo.bufnr
local id = vim.api.nvim_buf_set_extmark(
bufnr, hl.ns, target.pos[1] - 1, target.pos[2] - 1,
{
virt_text = { { table.concat(target.chars), "LeapMatch" } },
virt_text_pos = "overlay",
hl_mode = "combine",
priority = hl.priority.label,
}
)
-- This way Leap automatically cleans up your stuff together with its own.
table.insert(hl.extmarks, { bufnr, id })
end
end
-- Continue with Leap's native function body.
return true
end
require('leap').opts.on_beacons = highlight_unlabeled_phase_one_targets To always highlight all matches, just remove
|
I have read through the readme and the help file, and tried every highlight group present, but I can't replicate the photo in the readme which shows a search where typed characters in a search have their own highlight group. Was this feature removed for some reason?
For eye strain and focus reasons, I'd like to have a grey backdrop, where if I press
s
followed bya
, all 'a' characters will be highlighted according to some highlight group. Ideally, a separate highlight group would also be available for all characters following the first one, so that I can see more clearly what the different options are.Right now leap feels a bit 'dead' compared to lightspeed, and the lack of visual feedback is making it really hard to swap over.
The text was updated successfully, but these errors were encountered: