Skip to content
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

fix: multibyte chars in text lines #48

Merged
merged 2 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions lua/precognition/horizontal_motions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,26 @@ end

---@param str string
---@param cursorcol integer
---@param _linelen integer
---@param linelen integer
---@param big_word boolean
---@return Precognition.PlaceLoc
function M.next_word_boundary(str, cursorcol, _linelen, big_word)
function M.next_word_boundary(str, cursorcol, linelen, big_word)
local offset = cursorcol
local len = vim.fn.strcharlen(str)
local char = vim.fn.strcharpart(str, offset - 1, 1)
local c_class = utils.char_class(char, big_word)

if c_class ~= cc.whitespace then
while utils.char_class(char, big_word) == c_class and offset <= len do
while utils.char_class(char, big_word) == c_class and offset <= linelen do
offset = offset + 1
char = vim.fn.strcharpart(str, offset - 1, 1)
end
end

while utils.char_class(char, big_word) == cc.whitespace and offset <= len do
while utils.char_class(char, big_word) == cc.whitespace and offset <= linelen do
offset = offset + 1
char = vim.fn.strcharpart(str, offset - 1, 1)
end
if offset > len then
if offset > linelen then
return 0
end

Expand Down Expand Up @@ -114,11 +113,10 @@ end

---@param str string
---@param cursorcol integer
---@param _linelen integer
---@param linelen integer
---@param big_word boolean
---@return Precognition.PlaceLoc
function M.prev_word_boundary(str, cursorcol, _linelen, big_word)
local len = vim.fn.strcharlen(str)
function M.prev_word_boundary(str, cursorcol, linelen, big_word)
local offset = cursorcol - 1
local char = vim.fn.strcharpart(str, offset - 1, 1)
local c_class = utils.char_class(char, big_word)
Expand All @@ -141,7 +139,7 @@ function M.prev_word_boundary(str, cursorcol, _linelen, big_word)
end
end

if offset == nil or offset > len or offset < 0 then
if offset == nil or offset > linelen or offset < 0 then
return 0
end
return offset + 1
Expand Down
30 changes: 23 additions & 7 deletions lua/precognition/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ local M = {}
---@field PrevParagraph Precognition.PlaceLoc
---@field NextParagraph Precognition.PlaceLoc

---@class Precognition.ExtraPadding
---@field start integer
---@field length integer

---@type Precognition.HintConfig
local defaultHintConfig = {
Caret = { text = "^", prio = 2 },
Expand Down Expand Up @@ -106,8 +110,9 @@ local gutter_group = "precognition_gutter"

---@param marks Precognition.VirtLine
---@param line_len integer
---@param extra_padding Precognition.ExtraPadding
---@return table
local function build_virt_line(marks, line_len)
local function build_virt_line(marks, line_len, extra_padding)
if not marks then
return {}
end
Expand All @@ -127,20 +132,26 @@ local function build_virt_line(marks, line_len)
if existing == " " and existing ~= hint then
line_table[col] = hint
else -- if the character is not a space, then we need to check the prio
local existingKey
local existing_key
for key, value in pairs(config.hints) do
if value.text == existing then
existingKey = key
existing_key = key
break
end
end
if existing ~= " " and config.hints[mark].prio > config.hints[existingKey].prio then
if existing ~= " " and config.hints[mark].prio > config.hints[existing_key].prio then
line_table[col] = hint
end
end
end
end

if #extra_padding > 0 then
for _, padding in ipairs(extra_padding) do
line_table[padding.start] = line_table[padding.start] .. string.rep(" ", padding.length)
end
end

local line = table.concat(line_table)
if line:match("^%s+$") then
return {}
Expand Down Expand Up @@ -201,15 +212,17 @@ local function display_marks()
if utils.is_blacklisted_buffer(bufnr) then
return
end
local cursorline, cursorcol = unpack(vim.api.nvim_win_get_cursor(0))
cursorcol = cursorcol + 1
local cursorline = vim.fn.line(".")
local cursorcol = vim.fn.charcol(".")
if extmark and not dirty then
return
end

local tab_width = vim.bo.expandtab and vim.bo.shiftwidth or vim.bo.tabstop
local cur_line = vim.api.nvim_get_current_line():gsub("\t", string.rep(" ", tab_width))
local line_len = vim.fn.strcharlen(cur_line)
---@type Precognition.ExtraPadding[]
local extra_padding = {}
-- local after_cursor = vim.fn.strcharpart(cur_line, cursorcol + 1)
-- local before_cursor = vim.fn.strcharpart(cur_line, 0, cursorcol - 1)
-- local before_cursor_rev = string.reverse(before_cursor)
Expand All @@ -232,7 +245,10 @@ local function display_marks()
Zero = 1,
}

local virt_line = build_virt_line(virtual_line_marks, line_len)
--multicharacter padding
utils.add_multibyte_padding(cur_line, extra_padding, line_len)

local virt_line = build_virt_line(virtual_line_marks, line_len, extra_padding)

-- TODO: can we add indent lines to the virt line to match indent-blankline or similar (if installed)?

Expand Down
14 changes: 14 additions & 0 deletions lua/precognition/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,18 @@ function M.create_pad_array(len, str)
return pad_array
end

---Add extra padding for multi byte character characters
---@param cur_line string
---@param extra_padding Precognition.ExtraPadding[]
---@param line_len integer
function M.add_multibyte_padding(cur_line, extra_padding, line_len)
for i = 1, line_len do
local char = vim.fn.strcharpart(cur_line, i - 1, 1)
local width = vim.fn.strdisplaywidth(char)
if width > 1 then
table.insert(extra_padding, { start = i, length = width - 1 })
end
end
end

return M
10 changes: 10 additions & 0 deletions tests/precognition/char_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ describe("big_word classing", function()
eq(utils.char_class("@", true), 1)
eq(utils.char_class(".", true), 1)
end)

it("can class emoji characters", function()
eq(utils.char_class("🐱", false), 1)
eq(utils.char_class("😸", false), 1)
end)

it("can class nerdfont characters", function()
eq(utils.char_class("", false), 1)
eq(utils.char_class("", false), 1)
end)
end)

describe("pad arrays", function()
Expand Down
9 changes: 9 additions & 0 deletions tests/precognition/horizontal_motions_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -304,4 +304,13 @@ describe("edge case", function()
eq(16, hm.end_of_word(str, 15, #str, false))
eq(22, hm.end_of_word(str, 21, #str, false))
end)

it("multibyte characters", function()
local str = "# 💭👀precognition.nvim"
local len = vim.fn.strcharlen(str)
eq(3, hm.next_word_boundary(str, 1, len, false))
eq(17, hm.prev_word_boundary(str, 18, len, false))
eq(3, hm.prev_word_boundary(str, 18, len, true))
eq(3, hm.prev_word_boundary(str, 5, len, false))
end)
end)
Loading
Loading