Refactor open actions

Open actions now make use of a 'link' data structure containing a text
and ref string, and the respective start and end positions on the line.

Additionally, parsers are now simple objects containing extraction regex
for extracting their text and ref link individually.
This commit is contained in:
Marty Oehme 2020-11-04 21:29:23 +01:00
parent 587fa47268
commit 3a9e04d3ce
Signed by: Marty
GPG key ID: B7538B8F50A1C800
2 changed files with 116 additions and 56 deletions

View file

@ -2,16 +2,19 @@ local A = {}
local o = require 'zettelkasten.options' local o = require 'zettelkasten.options'
local parsers = {markdown = "%[.-%]%((.-)%)", wiki = "%[%[(.+)|?.-%]%]"} local BIGNUMBER = 10000000
-- Extracts a file name from a link and opens the corresponding file local parsers = {
-- in the current buffer. markdown = {ref = "%[.-%]%((.-)%)", text = "%[(.-)%]%(.-%)"},
-- Takes an optional input parameter wiki = {ref = "%[%[(.-)|?.-%]%]", text = "%[%[.-|?(.-)%]%]"}
function A.open(input) }
local fname = A.extract_link(input)
if not fname then return end -- Opens the link passed in in the editor's current buffer.
-- Requires a link object passed in.
function A.open(link)
if not link or not link.ref then return end
-- TODO follow: go to anchor, fall back to filename -- TODO follow: go to anchor, fall back to filename
vim.api.nvim_command(string.format("edit %s", fname)) vim.api.nvim_command(string.format("edit %s", link.ref))
end end
-- Gets the input at the current buffer cursor and opens it -- Gets the input at the current buffer cursor and opens it
@ -27,29 +30,56 @@ function A.open_selected(style)
end end
end end
-- Return only the link reference portion of a markdown/wiki style link. -- Return all links contained in the input given in an array.
-- For example, for a markdown link [my text](my-link.md) -- Returned link tables have the following structure:
-- it would only return my-link.md -- link = { text=, ref=, startpos=27, endpos=65 }
function A.extract_link(input) function A.extract_all_links(input)
if not input then return end if not input then return end
for _, parser in pairs(parsers) do return input:match(parser) end local links = {}
return local curpos = 1
for _, parser in pairs(parsers) do
while input:find(parser.ref, curpos) do
local ref = input:match(parser.ref, curpos)
local text = input:match(parser.text, curpos)
local startpos, endpos = input:find(parser.ref, curpos)
table.insert(links, {
ref = ref,
text = text,
startpos = startpos,
endpos = endpos
})
curpos = endpos
end
end
return links
end end
-- Returns the word currently under cursor, the vim equivalent of yiW. -- Returns the link currently under cursor, roughly the vim equivalent of yiW.
-- Takes an optional boolean flag to set the word being caught -- Works for links containing spaces in their text or reference link.
-- to the vim equivalent of doing yiw, a more exclusive version. function A.get_link_under_cursor()
function A.get_link_under_cursor(small) local curpos = vim.api.nvim_win_get_cursor(0)[2]
local c = "<cWORD>" local links = A.extract_all_links(vim.api.nvim_get_current_line())
if small then c = "<cword>" end for _, link in pairs(links) do
local word = vim.fn.expand(c) if link.startpos <= curpos + 1 and link.endpos > curpos then
return word return link
end
end
return nil
end end
-- Returns the content of the line from the cursor onwards. -- Returns the next link of the line from the cursor onwards.
function A.get_next_link_on_line() function A.get_next_link_on_line()
local line = vim.api.nvim_get_current_line() local curpos = vim.api.nvim_win_get_cursor(0)[2]
return line:sub(vim.api.nvim_win_get_cursor(0)[2]) local links = A.extract_all_links(vim.api.nvim_get_current_line())
local nearestpos = BIGNUMBER
local nearestlink
for k, link in pairs(links) do
if link.endpos > curpos and link.endpos < nearestpos then
nearestpos = link.endpos
nearestlink = link
end
end
return nearestlink
end end
return {open = A.open, open_selected = A.open_selected} return {open = A.open, open_selected = A.open_selected}

View file

@ -4,10 +4,10 @@ before_each(function() _G.vim = {g = {}, b = {}} end)
after_each(function() _G.vim = nil end) after_each(function() _G.vim = nil end)
describe("open", function() describe("open", function()
it("should open file in editor if it is a valid link", function() it("should open file in editor if it contains a valid link ref", function()
vim.api = {nvim_command = mock(function() end)} vim.api = {nvim_command = mock(function() end)}
action.open("[some text](1910271456_link-to-my-file.md)") action.open({ref = "1910271456_link-to-my-file.md"})
assert.spy(vim.api.nvim_command).was_called_with( assert.spy(vim.api.nvim_command).was_called_with(
"edit 1910271456_link-to-my-file.md") "edit 1910271456_link-to-my-file.md")
end) end)
@ -28,37 +28,67 @@ describe("open_selected", function()
nvim_win_get_cursor = function(winnum) return {0, 0} end nvim_win_get_cursor = function(winnum) return {0, 0} end
} }
end) end)
it("should use the style passed to it, above the one set in options", describe("when looking under cursor", function()
function() it("should open link", function()
vim.g['zettel_link_following'] = 'cursor' vim.g['zettel_link_following'] = 'cursor'
vim.api.nvim_win_get_cursor =
function(winnum) return {0, 30} end
action.open_selected()
assert.spy(vim.api.nvim_command).was_called_with(
"edit 1910271456_link-to-my-file.md")
end)
it("should detect correct position for link start", function()
vim.g['zettel_link_following'] = 'cursor'
vim.api.nvim_get_current_line = mock(vim.api.nvim_get_current_line) vim.api.nvim_win_get_cursor =
action.open_selected("line") function(winnum) return {0, 25} end
action.open_selected()
assert.spy(vim.api.nvim_command).was_not_called()
assert.spy(vim.api.nvim_get_current_line).was_called() vim.api.nvim_win_get_cursor =
function(winnum) return {0, 26} end
action.open_selected()
assert.spy(vim.api.nvim_command).was_called_with(
"edit 1910271456_link-to-my-file.md")
end)
it("should detect correct position for link end", function()
vim.g['zettel_link_following'] = 'cursor'
vim.api.nvim_win_get_cursor =
function(winnum) return {0, 65} end
action.open_selected()
assert.spy(vim.api.nvim_command).was_not_called()
vim.api.nvim_win_get_cursor =
function(winnum) return {0, 64} end
action.open_selected()
assert.spy(vim.api.nvim_command).was_called_with(
"edit 1910271456_link-to-my-file.md")
end)
end) end)
it("should open link under cursor if option set", function() describe("when looking until end of line", function()
vim.g['zettel_link_following'] = 'cursor' it("should use the style passed to it, above the one set in options",
vim.fn = { function()
expand = function(sure) vim.g['zettel_link_following'] = 'cursor'
return "[" .. sure .. "](1910271456_link-to-my-file.md)"
end vim.api.nvim_get_current_line = mock(vim.api.nvim_get_current_line)
} action.open_selected("line")
action.open_selected()
assert.spy(vim.api.nvim_command).was_called_with( assert.spy(vim.api.nvim_get_current_line).was_called()
"edit 1910271456_link-to-my-file.md") end)
end) it("should open next link on line if option set", function()
it("should open next link on line if option set", function() vim.g['zettel_link_following'] = 'line'
vim.g['zettel_link_following'] = 'line' action.open_selected()
action.open_selected() assert.spy(vim.api.nvim_command).was_called_with(
assert.spy(vim.api.nvim_command).was_called_with( "edit 1910271456_link-to-my-file.md")
"edit 1910271456_link-to-my-file.md") end)
end) it("should ignore links before cursor position", function()
it("should ignore links before cursor position", function() vim.g['zettel_link_following'] = 'line'
vim.g['zettel_link_following'] = 'line' vim.api.nvim_win_get_cursor =
vim.api.nvim_win_get_cursor = function(winnum) return {0, 65} end function(winnum) return {0, 65} end
action.open_selected() action.open_selected()
assert.spy(vim.api.nvim_command).was_called_with( assert.spy(vim.api.nvim_command).was_called_with(
"edit 2030101158 another-link-now.md") "edit 2030101158 another-link-now.md")
end)
end) end)
end) end)