From ba033e2b24c75bab019443d49f6e0dc44f2c2354 Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Mon, 2 Nov 2020 21:51:14 +0100 Subject: [PATCH] Add function to search for next link on line By choosing 'line' in the `zettel_link_following` option, the link to be opened will be searched for on the complete rest of the line, meaning from the cursor to the newline symbol onwards. --- README.md | 12 +++--- lua/zettelkasten/action.lua | 71 +++++++++++--------------------- lua/zettelkasten/action_spec.lua | 40 ++++++++++++++++-- 3 files changed, 66 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index a9ea1c0..8b82644 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ To develop / debug: start neovim with `nvim --cmd "set rtp+=$(pwd)" .` to automatically load the files in project dir as if they were on path next up: -* link following option (under cursor, next on line) -* next link on line function (actions) -* helper function to decide which one to use from A.open +* [x] link following option (under cursor, next on line) +* [x] next link on line function (actions) +* [x] helper function to decide which one to use from A.open ## TODO: needed functionality @@ -19,10 +19,11 @@ next up: * [ ] list anchors * [ ] list filenames * [ ] link following (to existing anchor) + * [ ] fallback to filename if anchor invalid / not found * [ ] link creation (to existing note) * [ ] list existing * [ ] create link (md / wiki) -* [ ] link switching (to another existing note) +* [ ] link switching (point to another existing note) * [ ] note search (title / full-text) * [ ] jump to zettel (open existing anchor) * [ ] select by anchor @@ -32,13 +33,14 @@ next up: * [x] zettel extension * [x] link style (wiki/markdown) * [ ] custom link style? - * [ ] link detection/following (under word, next on line) + * [x] link detection/following (under word, next on line) * [ ] recursive dir lookup for zettel * [ ] zettel anchor regex ## TODO: nice-to-haves * [ ] refactor parsers (md/wiki) to be tables of functions/regex in options, so e.g. valid link detection can call `options.parser.isValidLink(link)` or transformation `options.parser.styleLink(anchor, text)` + * [ ] enable custom parser supply * [ ] completion engine (e.g. for `completion-nvim`, look in completion_buffers/completion-tags for reference) * [ ] zettel caching for big directories * [ ] backlinks (via rg for filename anchor?) diff --git a/lua/zettelkasten/action.lua b/lua/zettelkasten/action.lua index 0f3dbd4..27e998f 100644 --- a/lua/zettelkasten/action.lua +++ b/lua/zettelkasten/action.lua @@ -1,5 +1,9 @@ local A = {} +local o = require 'zettelkasten.options' + +local parsers = {markdown = "%[.-%]%((.-)%)", wiki = "%[%[(.+)|?.-%]%]"} + -- Extracts a file name from a link and opens the corresponding file -- in the current buffer. -- Takes an optional input parameter @@ -12,15 +16,24 @@ end -- Gets the input at the current buffer cursor and opens it -- in the current buffer. -function A.open_selected() - -- TODO decide between currentword/restofline option - A.open(A.get_link_under_cursor()) +-- Takes an optional style of link following to use, +-- superseding the one set in options. +function A.open_selected(style) + local style = style or o.zettel().link_following + if style == 'line' then + A.open(A.get_next_link_on_line()) + elseif style == 'cursor' then + A.open(A.get_link_under_cursor()) + end end --- Return only the link reference portion of a markdown/wiki style link +-- Return only the link reference portion of a markdown/wiki style link. +-- For example, for a markdown link [my text](my-link.md) +-- it would only return my-link.md function A.extract_link(input) if not input then return end - return input:match("%[%[(.+)|?.*%]%]") or input:match("%[.*%]%((.+)%)") + for _, parser in pairs(parsers) do return input:match(parser) end + return end -- Returns the word currently under cursor, the vim equivalent of yiW. @@ -33,48 +46,10 @@ function A.get_link_under_cursor(small) return word end --- -- Returns the content of the line from the cursor onwards. --- function A.get_next_link_on_line() --- local line = vim.api.nvim_get_current_line() --- local linenr = vim.api.nvim_win_get_cursor(0)[1] --- print(line, linenr) --- return "" --- end +-- Returns the content of the line from the cursor onwards. +function A.get_next_link_on_line() + local line = vim.api.nvim_get_current_line() + return line:sub(vim.api.nvim_win_get_cursor(0)[2]) +end return {open = A.open, open_selected = A.open_selected} - --- local function get_selection() --- s_start = vim.fn.line("'<") - 1 --- s_end = vim.fn.line("'>") --- return vim.api.nvim_buf_get_lines(0, s_start, s_end, true) --- end - --- -- UGLY HACKS ABOUND --- function ZK.create_zettel() --- -- get line and its number --- local selection --- local line = vim.api.nvim_get_current_line() --- local linenr = vim.api.nvim_win_get_cursor(0)[1] - --- -- get words under cursor / selected --- local mode = vim.api.nvim_get_mode()['mode'] --- if mode == "n" then --- print(vim.fn.line("'<'") - 1) --- selection = vim.fn.expand("") --- -- NOT WORKING YET --- elseif mode == "v" then --- selection = get_selection() --- else --- return --- end - --- -- get valid link --- local link = l.create(nil, selection) - --- -- create new line with selection replaced in middle --- local st, en = line:find(selection, 0, true) --- local repl_line = line:sub(1, st - 1) .. link .. line:sub(en + 1) - --- -- replace existing line in favor of new one --- vim.api.nvim_buf_set_lines(0, linenr - 1, linenr, true, {repl_line}) --- end diff --git a/lua/zettelkasten/action_spec.lua b/lua/zettelkasten/action_spec.lua index 49fcbae..581ce46 100644 --- a/lua/zettelkasten/action_spec.lua +++ b/lua/zettelkasten/action_spec.lua @@ -11,22 +11,54 @@ describe("open", function() assert.spy(vim.api.nvim_command).was_called_with( "edit 1910271456_link-to-my-file.md") end) - it("should not fail when no input is available", function() + it("should do nothing when no link passed in", function() vim.fn = {expand = function() end} assert.is_not_error(action.open) end) end) + describe("open_selected", function() - it("should open the next link found if no argument passed in", function() - vim.api = {nvim_command = mock(function() end)} + before_each(function() + vim.api = { + nvim_command = mock(function() end), + nvim_get_current_line = function(sure) + return + "Hello, this is a line and [mylink](1910271456_link-to-my-file.md) whereas another [link](2030101158 another-link-now.md)" + end, + nvim_win_get_cursor = function(winnum) return {0, 0} end + } + end) + it("should use the style passed to it, above the one set in options", + function() + vim.g['zettel_link_following'] = 'cursor' + + vim.api.nvim_get_current_line = mock(vim.api.nvim_get_current_line) + action.open_selected("line") + + assert.spy(vim.api.nvim_get_current_line).was_called() + end) + it("should open link under cursor if option set", function() + vim.g['zettel_link_following'] = 'cursor' vim.fn = { expand = function(sure) return "[" .. sure .. "](1910271456_link-to-my-file.md)" end } - action.open_selected() assert.spy(vim.api.nvim_command).was_called_with( "edit 1910271456_link-to-my-file.md") end) + it("should open next link on line if option set", function() + vim.g['zettel_link_following'] = 'line' + action.open_selected() + assert.spy(vim.api.nvim_command).was_called_with( + "edit 1910271456_link-to-my-file.md") + end) + it("should ignore links before cursor position", function() + vim.g['zettel_link_following'] = 'line' + vim.api.nvim_win_get_cursor = function(winnum) return {0, 65} end + action.open_selected() + assert.spy(vim.api.nvim_command).was_called_with( + "edit 2030101158 another-link-now.md") + end) end)