refactor: escape paths and titles, introduce pinfo

This commit is contained in:
Rene Schallner
2021-12-19 06:09:52 +01:00
parent 6d57d418b7
commit a66cc06739

View File

@@ -103,6 +103,11 @@ local function file_exists(fname)
end end
end end
--- escapes a string for use as exact pattern within gsub
local function escape(s)
return string.gsub(s, "[%%%]%^%-$().[*+?]", "%%%1")
end
-- ---------------------------------------------------------------------------- -- ----------------------------------------------------------------------------
-- image stuff -- image stuff
local function imgFromClipboard() local function imgFromClipboard()
@@ -340,47 +345,74 @@ local function create_note_from_template(
ofile:close() ofile:close()
end end
local function num_path_elems(p) --- Pinfo
return #vim.split(p, "/") --- - fexists : true if file exists
--- - title : the title (filename including subdirs without extension)
--- - if opts.subdirs_in_links is false, no subdirs will be included
--- - filename : filename component only
--- - filepath : full path, identical to p
--- - root_dir : the root dir (home, dailies, ...)
--- - sub_dir : subdir if present, relative to root_dir
--- - is_daily_or_weekly : bool
local Pinfo = {
fexists = false,
title = "",
filename = "",
filepath = "",
root_dir = "",
sub_dir = "",
is_daily_or_weekly = false,
}
function Pinfo:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end end
local function path_to_linkname(p, opts) --- resolve_path(p, opts)
local ln --- inspects the path and returns a Pinfo table
local function resolve_path(p, opts)
-- print("resolving path ", p)
opts = opts or {} opts = opts or {}
opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links
local special_dir = false local pinfo = Pinfo:new()
if pinfo.fexists = file_exists(p)
M.Cfg.dailies pinfo.filepath = p
and num_path_elems(p:gsub(M.Cfg.dailies .. "/", "")) == 1 pinfo.root_dir = M.Cfg.home
then pinfo.is_daily_or_weekly = false
ln = p:gsub(M.Cfg.dailies .. "/", "")
special_dir = true -- strip all dirs to get filename
local pp = Path:new(p)
local p_splits = pp:_split()
pinfo.filename = p_splits[#p_splits]
pinfo.title = pinfo.filename:gsub(M.Cfg.extension, "")
if vim.startswith(p, M.Cfg.home) then
pinfo.root_dir = M.Cfg.home
end
if vim.startswith(p, M.Cfg.dailies) then
pinfo.root_dir = M.Cfg.dailies
pinfo.is_daily_or_weekly = true
end
if vim.startswith(p, M.Cfg.weeklies) then
pinfo.root_dir = M.Cfg.weeklies
pinfo.is_daily_or_weekly = true
end end
if -- now work out subdir relative to root
M.Cfg.weeklies pinfo.sub_dir = p
and num_path_elems(p:gsub(M.Cfg.weeklies .. "/", "")) == 1 :gsub(escape(pinfo.root_dir .. "/"), "")
then :gsub(escape(pinfo.filename), "")
ln = p:gsub(M.Cfg.weeklies .. "/", "") :gsub("/$", "")
special_dir = true :gsub("^/", "")
if opts.subdirs_in_links and #pinfo.sub_dir > 0 then
pinfo.title = pinfo.sub_dir .. "/" .. pinfo.title
end end
if special_dir == false then return pinfo
ln = p:gsub(M.Cfg.home .. "/", "")
end
if not opts.subdirs_in_links then
-- strip all subdirs
local pp = Path:new(ln)
local splits = pp:_split()
ln = splits[#splits]
end
local title = vim.split(ln, M.Cfg.extension)
title = title[1]
return title
end end
local function order_numeric(a, b) local function order_numeric(a, b)
@@ -474,7 +506,7 @@ local function find_files_sorted(opts)
local function iconic_display(display_entry) local function iconic_display(display_entry)
local display_opts = { local display_opts = {
path_display = function(_, e) path_display = function(_, e)
return e:gsub(opts.cwd .. "/", "") return e:gsub(escape(opts.cwd .. "/"), "")
end, end,
} }
@@ -664,8 +696,8 @@ function picker_actions.paste_link(opts)
return function(prompt_bufnr) return function(prompt_bufnr)
actions.close(prompt_bufnr) actions.close(prompt_bufnr)
local selection = action_state.get_selected_entry() local selection = action_state.get_selected_entry()
local fn = path_to_linkname(selection.value, opts) local pinfo = resolve_path(selection.filename or selection.value, opts)
local title = "[[" .. fn .. "]]" local title = "[[" .. pinfo.title .. "]]"
vim.api.nvim_put({ title }, "", true, true) vim.api.nvim_put({ title }, "", true, true)
if opts.insert_after_inserting or opts.i then if opts.insert_after_inserting or opts.i then
vim.api.nvim_feedkeys("A", "m", false) vim.api.nvim_feedkeys("A", "m", false)
@@ -681,8 +713,8 @@ function picker_actions.yank_link(opts)
actions.close(prompt_bufnr) actions.close(prompt_bufnr)
end end
local selection = action_state.get_selected_entry() local selection = action_state.get_selected_entry()
local fn = path_to_linkname(selection.value, opts) local pinfo = resolve_path(selection.filename or selection.value, opts)
local title = "[[" .. fn .. "]]" local title = "[[" .. pinfo.title .. "]]"
vim.fn.setreg('"', title) vim.fn.setreg('"', title)
print("yanked " .. title) print("yanked " .. title)
end end
@@ -693,7 +725,7 @@ function picker_actions.paste_img_link(opts)
actions.close(prompt_bufnr) actions.close(prompt_bufnr)
local selection = action_state.get_selected_entry() local selection = action_state.get_selected_entry()
local fn = selection.value local fn = selection.value
fn = fn:gsub(M.Cfg.home .. "/", "") fn = fn:gsub(escape(M.Cfg.home .. "/"), "")
local imglink = "![](" .. fn .. ")" local imglink = "![](" .. fn .. ")"
vim.api.nvim_put({ imglink }, "", true, true) vim.api.nvim_put({ imglink }, "", true, true)
if opts.insert_after_inserting or opts.i then if opts.insert_after_inserting or opts.i then
@@ -710,7 +742,7 @@ function picker_actions.yank_img_link(opts)
end end
local selection = action_state.get_selected_entry() local selection = action_state.get_selected_entry()
local fn = selection.value local fn = selection.value
fn = fn:gsub(M.Cfg.home .. "/", "") fn = fn:gsub(escape(M.Cfg.home .. "/"), "")
local imglink = "![](" .. fn .. ")" local imglink = "![](" .. fn .. ")"
vim.fn.setreg('"', imglink) vim.fn.setreg('"', imglink)
print("yanked " .. imglink) print("yanked " .. imglink)
@@ -828,8 +860,13 @@ local function InsertLink(opts)
actions.select_default:replace(function() actions.select_default:replace(function()
actions.close(prompt_bufnr) actions.close(prompt_bufnr)
local selection = action_state.get_selected_entry() local selection = action_state.get_selected_entry()
local fn = path_to_linkname(selection.value, opts) local pinfo = resolve_path(selection.value, opts)
vim.api.nvim_put({ "[[" .. fn .. "]]" }, "", true, true) vim.api.nvim_put(
{ "[[" .. pinfo.title .. "]]" },
"",
true,
true
)
if opts.i then if opts.i then
vim.api.nvim_feedkeys("A", "m", false) vim.api.nvim_feedkeys("A", "m", false)
end end
@@ -847,47 +884,62 @@ local function InsertLink(opts)
end end
local function resolve_link(title) local function resolve_link(title)
local fexists = false local pinfo = Pinfo:new()
local filename = title .. M.Cfg.extension pinfo.fexists = false
filename = filename:gsub("^%./", "") -- strip potential leading ./ pinfo.filename = title .. M.Cfg.extension
local best_root = M.Cfg.home pinfo.filename = pinfo.filename:gsub("^%./", "") -- strip potential leading ./
pinfo.root_dir = M.Cfg.home
pinfo.is_daily_or_weekly = false
if M.Cfg.weeklies and file_exists(M.Cfg.weeklies .. "/" .. filename) then if
filename = M.Cfg.weeklies .. "/" .. filename M.Cfg.weeklies and file_exists(M.Cfg.weeklies .. "/" .. pinfo.filename)
fexists = true then
best_root = M.Cfg.weeklies pinfo.filepath = M.Cfg.weeklies .. "/" .. pinfo.filename
pinfo.fexists = true
pinfo.root_dir = M.Cfg.weeklies
pinfo.is_daily_or_weekly = true
end end
if M.Cfg.dailies and file_exists(M.Cfg.dailies .. "/" .. filename) then if
filename = M.Cfg.dailies .. "/" .. filename M.Cfg.dailies and file_exists(M.Cfg.dailies .. "/" .. pinfo.filename)
fexists = true then
best_root = M.Cfg.dailies pinfo.filepath = M.Cfg.dailies .. "/" .. pinfo.filename
pinfo.fexists = true
pinfo.root_dir = M.Cfg.dailies
pinfo.is_daily_or_weekly = true
end end
if file_exists(M.Cfg.home .. "/" .. filename) then if file_exists(M.Cfg.home .. "/" .. pinfo.filename) then
filename = M.Cfg.home .. "/" .. filename pinfo.filepath = M.Cfg.home .. "/" .. pinfo.filename
fexists = true pinfo.fexists = true
end end
if fexists == false then if pinfo.fexists == false then
-- now search for it in all subdirs -- now search for it in all subdirs
local subdirs = scan.scan_dir(M.Cfg.home, { only_dirs = true }) local subdirs = scan.scan_dir(M.Cfg.home, { only_dirs = true })
local tempfn local tempfn
for _, folder in pairs(subdirs) do for _, folder in pairs(subdirs) do
tempfn = folder .. "/" .. filename tempfn = folder .. "/" .. pinfo.filename
-- [[testnote]] -- [[testnote]]
if file_exists(tempfn) then if file_exists(tempfn) then
filename = tempfn pinfo.filepath = tempfn
fexists = true pinfo.fexists = true
-- print("Found: " .. filename) -- print("Found: " ..pinfo.filename)
break break
end end
end end
end end
if fexists == false then if pinfo.fexists == false then
-- default fn for creation -- default fn for creation
filename = M.Cfg.home .. "/" .. filename pinfo.filepath = M.Cfg.home .. "/" .. pinfo.filename
end end
return fexists, filename, best_root
-- now work out subdir relative to root
pinfo.sub_dir = pinfo.filepath
:gsub(escape(pinfo.root_dir .. "/"), "")
:gsub(escape(pinfo.filename), "")
:gsub("/$", "")
:gsub("^/", "")
return pinfo
end end
-- local function check_for_link_or_tag() -- local function check_for_link_or_tag()
@@ -935,11 +987,9 @@ local function FollowLink(opts)
local search_mode = "files" local search_mode = "files"
local title local title
local filename = "" local filename = ""
local fexists
-- first: check if we're in a tag or a link -- first: check if we're in a tag or a link
local kind, atcol, tag local kind, atcol, tag
local best_root
if opts.follow_tag ~= nil then if opts.follow_tag ~= nil then
kind = "tag" kind = "tag"
@@ -984,9 +1034,12 @@ local function FollowLink(opts)
title = parts[2] title = parts[2]
end end
end end
-- if we cannot find the file, revert to global heading search by
-- setting filename to empty string
if #filename > 0 then if #filename > 0 then
fexists, filename, _ = resolve_link(filename) local pinfo = resolve_link(filename)
if fexists == false then if pinfo.fexists == false then
-- print("error") -- print("error")
filename = "" filename = ""
end end
@@ -995,9 +1048,9 @@ local function FollowLink(opts)
if search_mode == "files" then if search_mode == "files" then
-- check if fname exists anywhere -- check if fname exists anywhere
fexists, filename, best_root = resolve_link(title) local pinfo = resolve_link(filename)
if if
(fexists ~= true) (pinfo.fexists ~= true)
and ( and (
(opts.follow_creates_nonexisting == true) (opts.follow_creates_nonexisting == true)
or M.Cfg.follow_creates_nonexisting == true or M.Cfg.follow_creates_nonexisting == true
@@ -1005,16 +1058,16 @@ local function FollowLink(opts)
then then
create_note_from_template( create_note_from_template(
title, title,
filename, pinfo.filepath,
M.note_type_templates.normal M.note_type_templates.normal
) )
opts.erase = true opts.erase = true
opts.erase_file = filename opts.erase_file = pinfo.filepath
end end
find_files_sorted({ find_files_sorted({
prompt_title = "Follow link to note...", prompt_title = "Follow link to note...",
cwd = best_root, cwd = pinfo.best_root,
default_text = title, default_text = title,
find_command = M.Cfg.find_command, find_command = M.Cfg.find_command,
attach_mappings = function(_, map) attach_mappings = function(_, map)
@@ -1044,7 +1097,7 @@ local function FollowLink(opts)
local function iconic_display(display_entry) local function iconic_display(display_entry)
local display_opts = { local display_opts = {
path_display = function(_, e) path_display = function(_, e)
return e:gsub(opts.cwd .. "/", "") return e:gsub(escape(opts.cwd .. "/"), "")
end, end,
} }
@@ -1475,7 +1528,9 @@ end
-- Create and yank a [[link]] from the current note. -- Create and yank a [[link]] from the current note.
-- --
local function YankLink() local function YankLink()
local title = "[[" .. path_to_linkname(vim.fn.expand("%:p"), M.Cfg) .. "]]" local title = "[["
.. resolve_path(vim.fn.expand("%:p"), M.Cfg).title
.. "]]"
vim.fn.setreg('"', title) vim.fn.setreg('"', title)
print("yanked " .. title) print("yanked " .. title)
end end
@@ -1613,7 +1668,7 @@ local function InsertImgLink(opts)
actions.close(prompt_bufnr) actions.close(prompt_bufnr)
local selection = action_state.get_selected_entry() local selection = action_state.get_selected_entry()
local fn = selection.value local fn = selection.value
fn = fn:gsub(M.Cfg.home .. "/", "") fn = fn:gsub(escape(M.Cfg.home .. "/"), "")
vim.api.nvim_put({ "![](" .. fn .. ")" }, "", true, true) vim.api.nvim_put({ "![](" .. fn .. ")" }, "", true, true)
if opts.i then if opts.i then
vim.api.nvim_feedkeys("A", "m", false) vim.api.nvim_feedkeys("A", "m", false)
@@ -1675,7 +1730,7 @@ local function ShowBacklinks(opts)
opts.close_after_yanking = opts.close_after_yanking opts.close_after_yanking = opts.close_after_yanking
or M.Cfg.close_after_yanking or M.Cfg.close_after_yanking
local title = path_to_linkname(vim.fn.expand("%:p"), M.Cfg) local title = resolve_path(vim.fn.expand("%:p"), M.Cfg).title
-- or vim.api.nvim_buf_get_name(0) -- or vim.api.nvim_buf_get_name(0)
builtin.live_grep({ builtin.live_grep({
results_title = "Backlinks to " .. title, results_title = "Backlinks to " .. title,
@@ -1951,13 +2006,13 @@ local function ToggleTodo(opts)
if if
vim.startswith(stripped, "- ") and not vim.startswith(stripped, "- [") vim.startswith(stripped, "- ") and not vim.startswith(stripped, "- [")
then then
repline = curline:gsub("- ", "- [ ] ", 1) repline = curline:gsub("%- ", "- [ ] ", 1)
else else
if vim.startswith(stripped, "- [ ]") then if vim.startswith(stripped, "- [ ]") then
repline = curline:gsub("- %[ %]", "- [x]", 1) repline = curline:gsub("%- %[ %]", "- [x]", 1)
else else
if vim.startswith(stripped, "- [x]") then if vim.startswith(stripped, "- [x]") then
repline = curline:gsub("- %[x%]", "-", 1) repline = curline:gsub("%- %[x%]", "-", 1)
else else
repline = curline:gsub("(%S)", "- [ ] %1", 1) repline = curline:gsub("(%S)", "- [ ] %1", 1)
end end