better extmark inclusivity
All checks were successful
NeoVim tests / code-quality (push) Successful in 1m21s
All checks were successful
NeoVim tests / code-quality (push) Successful in 1m21s
This commit is contained in:
parent
28714fb51b
commit
103270a241
@ -10,9 +10,6 @@ local Renderer = require('u.renderer').Renderer
|
|||||||
local h = require('u.renderer').h
|
local h = require('u.renderer').h
|
||||||
local tracker = require 'u.tracker'
|
local tracker = require 'u.tracker'
|
||||||
|
|
||||||
-- Utility to trim brackets from strings:
|
|
||||||
local function trimb(s) return (s:gsub('^%[(.*)%]$', '%1')) end
|
|
||||||
|
|
||||||
-- Create a new, temporary, buffer to the side:
|
-- Create a new, temporary, buffer to the side:
|
||||||
vim.cmd.vnew()
|
vim.cmd.vnew()
|
||||||
vim.bo.buftype = 'nofile'
|
vim.bo.buftype = 'nofile'
|
||||||
@ -21,13 +18,13 @@ vim.bo.buflisted = false
|
|||||||
local renderer = Renderer.new()
|
local renderer = Renderer.new()
|
||||||
|
|
||||||
-- Create two signals:
|
-- Create two signals:
|
||||||
local s_name = tracker.create_signal '[whoever-you-are]'
|
local s_name = tracker.create_signal 'whoever-you-are'
|
||||||
local s_age = tracker.create_signal '[ideally-a-number]'
|
local s_age = tracker.create_signal 'ideally-a-number'
|
||||||
|
|
||||||
-- We can create derived information from the signals above. Say we want to do
|
-- We can create derived information from the signals above. Say we want to do
|
||||||
-- some validation on the input for `age`: we can do that with a memo:
|
-- some validation on the input for `age`: we can do that with a memo:
|
||||||
local s_age_info = tracker.create_memo(function()
|
local s_age_info = tracker.create_memo(function()
|
||||||
local age_raw = trimb(s_age:get())
|
local age_raw = s_age:get()
|
||||||
local age_digits = age_raw:match '^%s*(%d+)%s*$'
|
local age_digits = age_raw:match '^%s*(%d+)%s*$'
|
||||||
local age_n = age_digits and tonumber(age_digits) or nil
|
local age_n = age_digits and tonumber(age_digits) or nil
|
||||||
return {
|
return {
|
||||||
@ -59,9 +56,8 @@ tracker.create_effect(function()
|
|||||||
on_change = function(text) s_name:set(text) end,
|
on_change = function(text) s_name:set(text) end,
|
||||||
}, name),
|
}, name),
|
||||||
},
|
},
|
||||||
'\n',
|
|
||||||
{
|
{
|
||||||
'Age: ',
|
'\nAge: ',
|
||||||
h.Structure({
|
h.Structure({
|
||||||
on_change = function(text) s_age:set(text) end,
|
on_change = function(text) s_age:set(text) end,
|
||||||
}, age),
|
}, age),
|
||||||
@ -72,7 +68,7 @@ tracker.create_effect(function()
|
|||||||
-- Show the values of the signals here, too, so that we can see the
|
-- Show the values of the signals here, too, so that we can see the
|
||||||
-- reactivity in action. If you change the values in the tags above, you
|
-- reactivity in action. If you change the values in the tags above, you
|
||||||
-- can see the changes reflected here immediately.
|
-- can see the changes reflected here immediately.
|
||||||
{ 'Hello, "', trimb(name), '"!' },
|
{ 'Hello, "', name, '"!' },
|
||||||
|
|
||||||
--
|
--
|
||||||
-- A more complex example: we can do much more complex rendering, based on
|
-- A more complex example: we can do much more complex rendering, based on
|
||||||
|
@ -382,6 +382,8 @@ function Range:save_to_extmark()
|
|||||||
end_col = 0
|
end_col = 0
|
||||||
end
|
end
|
||||||
local id = vim.api.nvim_buf_set_extmark(r.start.bufnr, NS, r.start.lnum - 1, r.start.col - 1, {
|
local id = vim.api.nvim_buf_set_extmark(r.start.bufnr, NS, r.start.lnum - 1, r.start.col - 1, {
|
||||||
|
right_gravity = false,
|
||||||
|
end_right_gravity = true,
|
||||||
end_row = end_row,
|
end_row = end_row,
|
||||||
end_col = end_col,
|
end_col = end_col,
|
||||||
})
|
})
|
||||||
|
@ -28,7 +28,6 @@ M.h = setmetatable({}, {
|
|||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
__index = function(_, name)
|
__index = function(_, name)
|
||||||
-- vim.print('dynamic hl ' .. name)
|
|
||||||
return function(attributes, children)
|
return function(attributes, children)
|
||||||
return M.h('text', vim.tbl_deep_extend('force', { hl = name }, attributes), children)
|
return M.h('text', vim.tbl_deep_extend('force', { hl = name }, attributes), children)
|
||||||
end
|
end
|
||||||
@ -324,6 +323,13 @@ function Renderer:_reconcile() -- {{{
|
|||||||
id = extmark.id,
|
id = extmark.id,
|
||||||
end_row = extmark.stop[1],
|
end_row = extmark.stop[1],
|
||||||
end_col = extmark.stop[2],
|
end_col = extmark.stop[2],
|
||||||
|
-- If we change the text starting from the beginning (where the extmark
|
||||||
|
-- is), we don't want the extmark to move to the right.
|
||||||
|
right_gravity = false,
|
||||||
|
-- If we change the text starting from the end (where the end extmark
|
||||||
|
-- is), we don't want the extmark to move to stay stationary: we want
|
||||||
|
-- it to move to the right.
|
||||||
|
end_right_gravity = true,
|
||||||
}, extmark.opts)
|
}, extmark.opts)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
@ -385,7 +391,7 @@ function Renderer:_on_text_changed()
|
|||||||
local end_row0, end_col0 = details.end_row, details.end_col
|
local end_row0, end_col0 = details.end_row, details.end_col
|
||||||
|
|
||||||
if start_row0 == end_row0 and start_col0 == end_col0 then
|
if start_row0 == end_row0 and start_col0 == end_col0 then
|
||||||
-- Invalid extmark
|
on_change ''
|
||||||
else
|
else
|
||||||
local lines = vim.fn.getregion(
|
local lines = vim.fn.getregion(
|
||||||
{ self.bufnr, start_row0 + 1, start_col0 + 1 },
|
{ self.bufnr, start_row0 + 1, start_col0 + 1 },
|
||||||
@ -405,9 +411,11 @@ end
|
|||||||
---
|
---
|
||||||
--- @private (private for now)
|
--- @private (private for now)
|
||||||
--- @param pos0 [number; number]
|
--- @param pos0 [number; number]
|
||||||
|
--- @param mode string?
|
||||||
--- @return { extmark: u.renderer.RendererExtmark; tag: u.renderer.Tag; }[]
|
--- @return { extmark: u.renderer.RendererExtmark; tag: u.renderer.Tag; }[]
|
||||||
function Renderer:get_pos_infos(pos0) -- {{{
|
function Renderer:get_pos_infos(pos0, mode) -- {{{
|
||||||
local cursor_line0, cursor_col0 = pos0[1], pos0[2]
|
local cursor_line0, cursor_col0 = pos0[1], pos0[2]
|
||||||
|
if not mode then mode = vim.api.nvim_get_mode().mode end
|
||||||
|
|
||||||
-- The cursor (block) occupies **two** extmark spaces: one for it's left
|
-- The cursor (block) occupies **two** extmark spaces: one for it's left
|
||||||
-- edge, and one for it's right. We need to do our own intersection test,
|
-- edge, and one for it's right. We need to do our own intersection test,
|
||||||
@ -437,10 +445,23 @@ function Renderer:get_pos_infos(pos0) -- {{{
|
|||||||
--- @param ext u.renderer.RendererExtmark
|
--- @param ext u.renderer.RendererExtmark
|
||||||
:filter(function(ext)
|
:filter(function(ext)
|
||||||
if ext.stop[1] ~= nil and ext.stop[2] ~= nil then
|
if ext.stop[1] ~= nil and ext.stop[2] ~= nil then
|
||||||
return cursor_line0 >= ext.start[1]
|
-- If we've "ciw" and "collapsed" an extmark onto the cursor,
|
||||||
|
-- the cursor pos will equal the exmark's start AND end. In this
|
||||||
|
-- case, we want to include the extmark.
|
||||||
|
return (
|
||||||
|
cursor_line0 == ext.start[1]
|
||||||
|
and cursor_col0 == ext.start[2]
|
||||||
|
and cursor_line0 == ext.stop[1]
|
||||||
|
and cursor_col0 == ext.stop[2]
|
||||||
|
)
|
||||||
|
or (
|
||||||
|
cursor_line0 >= ext.start[1]
|
||||||
and cursor_col0 >= ext.start[2]
|
and cursor_col0 >= ext.start[2]
|
||||||
and cursor_line0 <= ext.stop[1]
|
and cursor_line0 <= ext.stop[1]
|
||||||
and cursor_col0 < ext.stop[2]
|
-- In insert mode, the cursor is "thin", so <= to compensate:
|
||||||
|
-- In normal mode, the cursor is "wide", so < to compensate:
|
||||||
|
and (mode == 'i' and cursor_col0 <= ext.stop[2] or cursor_col0 < ext.stop[2])
|
||||||
|
)
|
||||||
else
|
else
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
@ -103,11 +103,27 @@ describe('Renderer', function()
|
|||||||
}
|
}
|
||||||
|
|
||||||
local pos_infos = r:get_pos_infos { 0, 2 }
|
local pos_infos = r:get_pos_infos { 0, 2 }
|
||||||
|
|
||||||
assert.are.same(#pos_infos, 1)
|
assert.are.same(#pos_infos, 1)
|
||||||
assert.are.same(pos_infos[1].tag.attributes.hl, 'HighlightGroup1')
|
assert.are.same(pos_infos[1].tag.attributes.hl, 'HighlightGroup1')
|
||||||
assert.are.same(pos_infos[1].extmark.start, { 0, 0 })
|
assert.are.same(pos_infos[1].extmark.start, { 0, 0 })
|
||||||
assert.are.same(pos_infos[1].extmark.stop, { 0, 5 })
|
assert.are.same(pos_infos[1].extmark.stop, { 0, 5 })
|
||||||
|
pos_infos = r:get_pos_infos { 0, 4 }
|
||||||
|
assert.are.same(#pos_infos, 1)
|
||||||
|
assert.are.same(pos_infos[1].tag.attributes.hl, 'HighlightGroup1')
|
||||||
|
assert.are.same(pos_infos[1].extmark.start, { 0, 0 })
|
||||||
|
assert.are.same(pos_infos[1].extmark.stop, { 0, 5 })
|
||||||
|
|
||||||
|
pos_infos = r:get_pos_infos { 0, 5 }
|
||||||
|
assert.are.same(#pos_infos, 1)
|
||||||
|
assert.are.same(pos_infos[1].tag.attributes.hl, 'HighlightGroup2')
|
||||||
|
assert.are.same(pos_infos[1].extmark.start, { 0, 5 })
|
||||||
|
assert.are.same(pos_infos[1].extmark.stop, { 0, 11 })
|
||||||
|
|
||||||
|
-- In insert mode, bounds are eagerly included:
|
||||||
|
pos_infos = r:get_pos_infos({ 0, 5 }, 'i')
|
||||||
|
assert.are.same(#pos_infos, 2)
|
||||||
|
assert.are.same(pos_infos[1].tag.attributes.hl, 'HighlightGroup1')
|
||||||
|
assert.are.same(pos_infos[2].tag.attributes.hl, 'HighlightGroup2')
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user