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: a lot of edge cases #4

Merged
merged 17 commits into from
May 5, 2024
Merged
Show file tree
Hide file tree
Changes from 6 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
66 changes: 40 additions & 26 deletions lua/precognition/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ local default = {
["$"] = { text = "$", prio = 1 },
["w"] = { text = "w", prio = 10 },
-- ["W"] = "W",
["b"] = { text = "b", prio = 10 },
["e"] = { text = "e", prio = 10 },
["b"] = { text = "b", prio = 9 },
["e"] = { text = "e", prio = 8 },
-- ["ge"] = "ge", -- should we support multi-char / multi-byte hints?
},
gutterHints = {
Expand Down Expand Up @@ -81,27 +81,27 @@ end
---@param start integer
---@return integer | nil
local function next_word_boundary(str, start)
local offset = start - 1
local offset = start
local len = vim.fn.strcharlen(str)
local char = vim.fn.strcharpart(str, offset, 1)
local char = vim.fn.strcharpart(str, offset - 1, 1)
local c_class = char_class(char)

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

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

return offset + 1
return offset
end

---@param str string
Expand All @@ -112,16 +112,28 @@ local function end_of_word(str, start)
if start >= len then
return nil
end
local offset = start - 1
local char = vim.fn.strcharpart(str, offset, 1)
local offset = start
local char = vim.fn.strcharpart(str, offset - 1, 1)
local c_class = char_class(char)
local next_char_class = char_class(vim.fn.strcharpart(str, offset + 1, 1))
local next_char_class =
char_class(vim.fn.strcharpart(str, (offset - 1) + 1, 1))
local rev_offset

if
(c_class == 1 and next_char_class ~= 1)
or (next_char_class == 1 and c_class ~= 1)
then
offset = offset + 1
char = vim.fn.strcharpart(str, offset - 1, 1)
c_class = char_class(char)
next_char_class =
char_class(vim.fn.strcharpart(str, (offset - 1) + 1, 1))
end

if c_class ~= 0 and next_char_class ~= 0 then
while char_class(char) == c_class and offset <= len do
offset = offset + 1
char = vim.fn.strcharpart(str, offset, 1)
char = vim.fn.strcharpart(str, offset - 1, 1)
end
end

Expand All @@ -139,41 +151,40 @@ local function end_of_word(str, start)
if rev_offset ~= nil then
return rev_offset
end
return offset
return offset - 1
end

---@param str string
---@param start integer
---@return integer | nil
local function prev_word_boundary(str, start)
local len = vim.fn.strcharlen(str)
local offset = len - start + 1
str = string.reverse(str)
local offset = start - 1
local char = vim.fn.strcharpart(str, offset - 1, 1)
local c_class = char_class(char)

if c_class == 0 then
while char_class(char) == 0 and offset <= len do
offset = offset + 1
char = vim.fn.strcharpart(str, offset, 1)
while char_class(char) == 0 and offset >= 0 do
offset = offset - 1
char = vim.fn.strcharpart(str, offset - 1, 1)
end
c_class = char_class(char)
end

c_class = char_class(char)
while char_class(char) == c_class and offset <= len do
offset = offset + 1
char = vim.fn.strcharpart(str, offset, 1)
while char_class(char) == c_class and offset >= 0 do
offset = offset - 1
char = vim.fn.strcharpart(str, offset - 1, 1)
--if remaining string is whitespace, return nil_wrap
local remaining = string.sub(str, offset)
if remaining:match("^%s*$") and #remaining > 0 then
return nil
end
end

if offset == nil or (len - offset + 1) > len or (len - offset + 1) <= 0 then
if offset == nil or offset > len or offset < 0 then
return nil
end
return len - offset + 1
return offset + 1
end

---@param marks Precognition.VirtLine
Expand Down Expand Up @@ -254,9 +265,12 @@ local function apply_gutter_hints(gutter_hints, buf)
if ok then
gutter_signs_cache[hint] = { line = loc, id = res }
end
if not ok then
if not ok and loc ~= 0 then
vim.notify_once(
"Failed to place sign: " .. config.gutterHints[hint].text,
"Failed to place sign: "
.. config.gutterHints[hint].text
.. " at line "
.. loc,
vim.log.levels.WARN
)
end
Expand Down
67 changes: 58 additions & 9 deletions tests/precognition/char_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ describe("boundaries", function()
)
eq(
5,
precognition.prev_word_boundary(
" myFunction(example, stuff)",
15
)
)
eq(
15,
precognition.prev_word_boundary(
" myFunction(example, stuff)",
16
Expand Down Expand Up @@ -226,7 +233,7 @@ describe("boundaries", function()
)
)
eq(
24,
23,
precognition.prev_word_boundary(
" myFunction(example, stuff)",
25
Expand Down Expand Up @@ -260,9 +267,13 @@ describe("boundaries", function()
29
)
)
--TODO: This isnt right, it should ne 25, but i dont know the rules
--there is something odd if there is only one class 2 under the cursor
-- eq(25, precognition.prev_word_boundary(" myFunction(example, stuff)", 30))
eq(
25,
precognition.prev_word_boundary(
" myFunction(example, stuff)",
30
)
)
end)

it("can walk string with b", function()
Expand Down Expand Up @@ -307,10 +318,14 @@ describe("boundaries", function()
14,
precognition.end_of_word(" myFunction(example, stuff)", 5)
)
--TODO: These next two dont work either for the same reason as the previous
--something to do with the bracket being under the cursor
-- eq(15, precognition.end_of_word(" myFunction(example, stuff)", 14))
-- eq(22, precognition.end_of_word(" myFunction(example, stuff)", 15))
eq(
15,
precognition.end_of_word(" myFunction(example, stuff)", 14)
)
eq(
22,
precognition.end_of_word(" myFunction(example, stuff)", 15)
)
eq(
22,
precognition.end_of_word(" myFunction(example, stuff)", 16)
Expand All @@ -328,7 +343,7 @@ describe("boundaries", function()
precognition.end_of_word(" myFunction(example, stuff)", 25)
)
eq(
29,
30,
precognition.end_of_word(" myFunction(example, stuff)", 29)
)
eq(
Expand All @@ -337,4 +352,38 @@ describe("boundaries", function()
)
end)
end)

describe("edge case", function()
it("can handle empty strings", function()
eq(nil, precognition.next_word_boundary("", 1))
eq(nil, precognition.prev_word_boundary("", 1))
eq(nil, precognition.end_of_word("", 1))
end)

it("can handle strings with only whitespace", function()
eq(nil, precognition.next_word_boundary(" ", 1))
eq(nil, precognition.prev_word_boundary(" ", 1))
eq(nil, precognition.end_of_word(" ", 1))
end)

it(
"can handle strings with special characters in the middle",
function()
local str = "vim.keymap.set('n', '<leader>t;', ':Test<CR>')"
eq(5, precognition.next_word_boundary(str, 4))
eq(1, precognition.prev_word_boundary(str, 4))
eq(10, precognition.end_of_word(str, 4))
end
)

it(
"can handle strings with multiple consecutive special characters",
function()
local str = "this || that"
eq(9, precognition.next_word_boundary(str, 6))
eq(1, precognition.prev_word_boundary(str, 6))
eq(7, precognition.end_of_word(str, 6))
end
)
end)
end)
Loading