diff --git a/lua/zettelkasten/action.lua b/lua/zettelkasten/action.lua index 6c1c1bc..45226d5 100644 --- a/lua/zettelkasten/action.lua +++ b/lua/zettelkasten/action.lua @@ -3,6 +3,7 @@ local A = {} local o = require 'zettelkasten.options' local l = require 'zettelkasten.link' local f = require 'zettelkasten.files' +local t = require 'zettelkasten.text' -- Opens the link passed in in the editor's current buffer. -- Requires a link object passed in. @@ -25,145 +26,26 @@ function A.open_selected(style) local ln if st == 'line' then - ln = A.get_next_link_on_line(links, curpos) + ln = t.get_next_link_on_line(links, curpos) elseif st == 'cursor' then - ln = A.get_link_under_cursor(links, curpos) + ln = t.get_link_under_cursor(links, curpos) end A.open(ln) end --- Returns visually selected text and cursor column where selection starts. --- Works with selections over multiple lines, but will only return the --- starting line, as well as the starting line's text. -local function get_current_selection() - local line, start_col, end_col = vim.fn.getpos("'<")[2], - vim.fn.getpos("'<")[3], - vim.fn.getpos("'>")[3] - local selection = vim.fn.getline(line, line)[1]:sub(start_col, end_col) - return selection, start_col -end - --- Returns word currently under cursor and cursor column where --- the word begins. --- If big argument resolves to true, it will get the whitespace --- delimited word, otherwise the vim specified wordboundary word. -local function get_current_word(big) - local pattern = [[\k]] - if not big then pattern = [[\S]] end - - local cur_col = vim.api.nvim_win_get_cursor(0)[2] - local line = vim.api.nvim_get_current_line() - - local word_before_cur = vim.fn.matchstrpos(line:sub(1, cur_col + 1), - pattern .. "*$") - local word_start_col = word_before_cur[2] + 1 - word_before_cur = word_before_cur[1] - - local word_after_cur = vim.fn.matchstr(line:sub(cur_col + 1), - "^" .. pattern .. "*"):sub(2) - - return word_before_cur .. word_after_cur, word_start_col -end - --- Sanitizes the string before replacement, taking care of escaping any --- characters that lua uses to signify patterns. -local function replace(str, patt, repl, n) - patt = string.gsub(patt, "[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1") - repl = string.gsub(repl, "[%%]", "%%%%") - return string.gsub(str, patt, repl, n) -end - --- Replaces the input text on the current line with a zettel link. --- Takes an optional initial column on which the text to be replaced starts, --- which can prevent falsely substituting the wrong text fragment if an --- identical one exists earlier on the line. -local function replace_text_with_link(text, start_col) - local link = l.new(text) - - local line_full = vim.api.nvim_get_current_line() - local line_edited - if start_col then - line_edited = line_full:sub(1, start_col - 1) .. - replace(line_full:sub(start_col), text, link, 1) - else - line_edited = replace(line_full, text, link, 1) - end - - return line_edited -end - -- Replaces the current text context with a link to a new zettel. -- The current context is the visual selection (if called from visual mode) -- or the (big) word under the cursor if called from any other mode. function A.link(visual) local selection, start_col if visual or vim.api.nvim_get_mode()['mode'] == "v" then - selection, start_col = get_current_selection() + selection, start_col = t.get_current_selection() else - selection, start_col = get_current_word() + selection, start_col = t.get_current_word() end - vim.api.nvim_set_current_line(replace_text_with_link(selection, start_col)) -end - --- Returns the link currently under cursor, roughly the vim equivalent of yiW. --- Works for links containing spaces in their text or reference link. -function A.get_link_under_cursor(links, curpos) - for _, link in pairs(links) do - if link.startpos <= curpos + 1 and link.endpos > curpos then - return link - end - end - return nil -end - --- Returns the next link of the current line from the cursor onwards. -function A.get_next_link_on_line(links, curpos) - local nearestpos = math.huge - local nearestlink - for _, ln in pairs(links) do - if ln.endpos > curpos and ln.endpos < nearestpos then - nearestpos = ln.endpos - nearestlink = ln - end - end - return nearestlink + vim.api.nvim_set_current_line(t.replace_text(selection, l.new(selection), + start_col)) end return {open = A.open, open_selected = A.open_selected, link = A.link} - ---- 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/options.lua b/lua/zettelkasten/options.lua index 3b444fe..ad7986f 100644 --- a/lua/zettelkasten/options.lua +++ b/lua/zettelkasten/options.lua @@ -29,6 +29,7 @@ local anchor_defaults = { -- TODO zettel_root = vim.g["zettel_root"] or vim.b["zettel_root"] or "~/documents/notes", -- TODO zettel_anchor_pattern = regex? -> needs custom creation function in `create_anchor` +-- TODO refer to option name in error as well if something is not set correctly local function must_contain(set, value, name) if type(set) ~= "table" then return false end if not set[value] then diff --git a/lua/zettelkasten/text.lua b/lua/zettelkasten/text.lua new file mode 100644 index 0000000..9bb75e8 --- /dev/null +++ b/lua/zettelkasten/text.lua @@ -0,0 +1,86 @@ +local T = {} + +-- Returns visually selected text and cursor column where selection starts. +-- Works with selections over multiple lines, but will only return the +-- starting line, as well as the starting line's text. +function T.get_current_selection() + local line, start_col, end_col = vim.fn.getpos("'<")[2], + vim.fn.getpos("'<")[3], + vim.fn.getpos("'>")[3] + local selection = vim.fn.getline(line, line)[1]:sub(start_col, end_col) + return selection, start_col +end + +-- Returns word currently under cursor and cursor column where +-- the word begins. +-- If big argument resolves to true, it will get the whitespace +-- delimited word, otherwise the vim specified wordboundary word. +function T.get_current_word(big) + local pattern = [[\k]] + if not big then + pattern = [[\S]] + end + + local cur_col = vim.api.nvim_win_get_cursor(0)[2] + local line = vim.api.nvim_get_current_line() + + local word_before_cur = vim.fn.matchstrpos(line:sub(1, cur_col + 1), pattern .. "*$") + local word_start_col = word_before_cur[2] + 1 + word_before_cur = word_before_cur[1] + + local word_after_cur = vim.fn.matchstr(line:sub(cur_col + 1), "^" .. pattern .."*"):sub(2) + + return word_before_cur .. word_after_cur, word_start_col +end + +-- Returns the link currently under cursor, roughly the vim equivalent of yiW. +-- Works for links containing spaces in their text or reference link. +function T.get_link_under_cursor(links, curpos) + for _, link in pairs(links) do + if link.startpos <= curpos + 1 and link.endpos > curpos then + return link + end + end + return nil +end + +-- Returns the next link of the current line from the cursor onwards. +function T.get_next_link_on_line(links, curpos) + local nearestpos = math.huge + local nearestlink + for _, link in pairs(links) do + if link.endpos > curpos and link.endpos < nearestpos then + nearestpos = link.endpos + nearestlink = link + end + end + return nearestlink +end + +-- Sanitizes the string before replacement, taking care of escaping any +-- characters that lua uses to signify patterns. +local function replace(str, patt, repl, n) + patt = string.gsub(patt, "[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1") + repl = string.gsub(repl, "[%%]", "%%%%") + return string.gsub(str, patt, repl, n) +end + +-- Replaces the input text on the current line with new text. +-- Takes an optional initial column on which the text to be replaced starts, +-- which can prevent falsely substituting the wrong text fragment if an +-- identical one exists earlier on the line. (E.g. I want to replace the +-- second 'test' in 'test test 1 2 3'). +function T.replace_text(text, new_text, start_col) + local line_full = vim.api.nvim_get_current_line() + local line_edited + if start_col then + line_edited = line_full:sub(1, start_col - 1) .. + replace(line_full:sub(start_col), text, new_text, 1) + else + line_edited = replace(line_full, text, new_text, 1) + end + + return line_edited +end + +return T