From 05caa03bdd445b16789a0f526e158c2c8bf141a3 Mon Sep 17 00:00:00 2001 From: Thomas Lambert Date: Fri, 28 Apr 2023 23:02:17 +0200 Subject: [PATCH] style: make stylua happy --- lua/telekasten.lua | 5502 ++++++++++++++-------------- lua/telekasten/config.lua | 0 lua/telekasten/functions.lua | 0 lua/telekasten/pickers.lua | 62 +- lua/telekasten/syntax.lua | 0 lua/telekasten/templates.lua | 0 lua/telekasten/utils/dateutils.lua | 168 +- 7 files changed, 2866 insertions(+), 2866 deletions(-) create mode 100644 lua/telekasten/config.lua create mode 100644 lua/telekasten/functions.lua create mode 100644 lua/telekasten/syntax.lua create mode 100644 lua/telekasten/templates.lua diff --git a/lua/telekasten.lua b/lua/telekasten.lua index 07f2643..7728868 100644 --- a/lua/telekasten.lua +++ b/lua/telekasten.lua @@ -31,334 +31,334 @@ local vim = vim local _home = vim.fn.expand("~/zettelkasten") local M = {} local function defaultConfig(home) - if home == nil then - home = _home - end + if home == nil then + home = _home + end - local cfg = { - home = home, - -- if true, telekasten will be enabled when opening a note within the configured home - take_over_my_home = true, - -- auto-set telekasten filetype: if false, the telekasten filetype will not be used - -- and thus the telekasten syntax will not be loaded either - auto_set_filetype = true, - -- auto-set telekasten syntax: if false, the telekasten syntax will not be set - -- this syntax setting is independent from auto-set filetype - auto_set_syntax = true, - -- dir names for special notes (absolute path or subdir name) - dailies = home .. "/" .. "daily", - weeklies = home .. "/" .. "weekly", - templates = home .. "/" .. "templates", - -- image (sub)dir for pasting - -- dir name (absolute path or subdir name) - -- or nil if pasted images shouldn't go into a special subdir - image_subdir = nil, - -- markdown file extension - extension = ".md", - -- Generate note filenames. One of: - -- "title" (default) - Use title if supplied, uuid otherwise - -- "uuid" - Use uuid - -- "uuid-title" - Prefix title by uuid - -- "title-uuid" - Suffix title with uuid - new_note_filename = "title", - --[[ file UUID type + local cfg = { + home = home, + -- if true, telekasten will be enabled when opening a note within the configured home + take_over_my_home = true, + -- auto-set telekasten filetype: if false, the telekasten filetype will not be used + -- and thus the telekasten syntax will not be loaded either + auto_set_filetype = true, + -- auto-set telekasten syntax: if false, the telekasten syntax will not be set + -- this syntax setting is independent from auto-set filetype + auto_set_syntax = true, + -- dir names for special notes (absolute path or subdir name) + dailies = home .. "/" .. "daily", + weeklies = home .. "/" .. "weekly", + templates = home .. "/" .. "templates", + -- image (sub)dir for pasting + -- dir name (absolute path or subdir name) + -- or nil if pasted images shouldn't go into a special subdir + image_subdir = nil, + -- markdown file extension + extension = ".md", + -- Generate note filenames. One of: + -- "title" (default) - Use title if supplied, uuid otherwise + -- "uuid" - Use uuid + -- "uuid-title" - Prefix title by uuid + -- "title-uuid" - Suffix title with uuid + new_note_filename = "title", + --[[ file UUID type - "rand" - string input for os.date() - or custom lua function that returns a string --]] - uuid_type = "%Y%m%d%H%M", - -- UUID separator - uuid_sep = "-", - -- if not nil, replaces any spaces in the title when it is used in filename generation - filename_space_subst = nil, - -- following a link to a non-existing note will create it - follow_creates_nonexisting = true, - dailies_create_nonexisting = true, - weeklies_create_nonexisting = true, - -- skip telescope prompt for goto_today and goto_thisweek - journal_auto_open = false, - -- templates for new notes - -- template_new_note = home .. "/" .. "templates/new_note.md", - -- template_new_daily = home .. "/" .. "templates/daily_tk.md", - -- template_new_weekly = home .. "/" .. "templates/weekly_tk.md", + uuid_type = "%Y%m%d%H%M", + -- UUID separator + uuid_sep = "-", + -- if not nil, replaces any spaces in the title when it is used in filename generation + filename_space_subst = nil, + -- following a link to a non-existing note will create it + follow_creates_nonexisting = true, + dailies_create_nonexisting = true, + weeklies_create_nonexisting = true, + -- skip telescope prompt for goto_today and goto_thisweek + journal_auto_open = false, + -- templates for new notes + -- template_new_note = home .. "/" .. "templates/new_note.md", + -- template_new_daily = home .. "/" .. "templates/daily_tk.md", + -- template_new_weekly = home .. "/" .. "templates/weekly_tk.md", - -- image link style - -- wiki: ![[image name]] - -- markdown: ![](image_subdir/xxxxx.png) - image_link_style = "markdown", - -- default sort option: 'filename', 'modified' - sort = "filename", - -- when linking to a note in subdir/, create a [[subdir/title]] link - -- instead of a [[title only]] link - subdirs_in_links = true, - -- integrate with calendar-vim - plug_into_calendar = true, - calendar_opts = { - -- calendar week display mode: 1 .. 'WK01', 2 .. 'WK 1', 3 .. 'KW01', 4 .. 'KW 1', 5 .. '1' - weeknm = 4, - -- use monday as first day of week: 1 .. true, 0 .. false - calendar_monday = 1, - -- calendar mark: where to put mark for marked days: 'left', 'right', 'left-fit' - calendar_mark = "left-fit", - }, - close_after_yanking = false, - insert_after_inserting = true, - -- tag notation: '#tag', ':tag:', 'yaml-bare' - tag_notation = "#tag", - -- command palette theme: dropdown (window) or ivy (bottom panel) - command_palette_theme = "ivy", - -- tag list theme: - -- get_cursor: small tag list at cursor; ivy and dropdown like above - show_tags_theme = "ivy", - -- template_handling - -- What to do when creating a new note via `new_note()` or `follow_link()` - -- to a non-existing note - -- - prefer_new_note: use `new_note` template - -- - smart: if day or week is detected in title, use daily / weekly templates (default) - -- - always_ask: always ask before creating a note - template_handling = "smart", - -- path handling: - -- this applies to: - -- - new_note() - -- - new_templated_note() - -- - follow_link() to non-existing note - -- - -- it does NOT apply to: - -- - goto_today() - -- - goto_thisweek() - -- - -- Valid options: - -- - smart: put daily-looking notes in daily, weekly-looking ones in weekly, - -- all other ones in home, except for notes/with/subdirs/in/title. - -- (default) - -- - -- - prefer_home: put all notes in home except for goto_today(), goto_thisweek() - -- except for notes/with/subdirs/in/title. - -- - -- - same_as_current: put all new notes in the dir of the current note if - -- present or else in home - -- except for notes/with/subdirs/in/title. - new_note_location = "smart", - -- should all links be updated when a file is renamed - rename_update_links = true, - -- how to preview media files - -- "telescope-media-files" if you have telescope-media-files.nvim installed - -- "catimg-previewer" if you have catimg installed - -- "viu-previewer" if you have viu installed - media_previewer = "telescope-media-files", - -- A customizable fallback handler for urls. - follow_url_fallback = nil, - } - M.Cfg = cfg - M.note_type_templates = { - normal = M.Cfg.template_new_note, - daily = M.Cfg.template_new_daily, - weekly = M.Cfg.template_new_weekly, - } + -- image link style + -- wiki: ![[image name]] + -- markdown: ![](image_subdir/xxxxx.png) + image_link_style = "markdown", + -- default sort option: 'filename', 'modified' + sort = "filename", + -- when linking to a note in subdir/, create a [[subdir/title]] link + -- instead of a [[title only]] link + subdirs_in_links = true, + -- integrate with calendar-vim + plug_into_calendar = true, + calendar_opts = { + -- calendar week display mode: 1 .. 'WK01', 2 .. 'WK 1', 3 .. 'KW01', 4 .. 'KW 1', 5 .. '1' + weeknm = 4, + -- use monday as first day of week: 1 .. true, 0 .. false + calendar_monday = 1, + -- calendar mark: where to put mark for marked days: 'left', 'right', 'left-fit' + calendar_mark = "left-fit", + }, + close_after_yanking = false, + insert_after_inserting = true, + -- tag notation: '#tag', ':tag:', 'yaml-bare' + tag_notation = "#tag", + -- command palette theme: dropdown (window) or ivy (bottom panel) + command_palette_theme = "ivy", + -- tag list theme: + -- get_cursor: small tag list at cursor; ivy and dropdown like above + show_tags_theme = "ivy", + -- template_handling + -- What to do when creating a new note via `new_note()` or `follow_link()` + -- to a non-existing note + -- - prefer_new_note: use `new_note` template + -- - smart: if day or week is detected in title, use daily / weekly templates (default) + -- - always_ask: always ask before creating a note + template_handling = "smart", + -- path handling: + -- this applies to: + -- - new_note() + -- - new_templated_note() + -- - follow_link() to non-existing note + -- + -- it does NOT apply to: + -- - goto_today() + -- - goto_thisweek() + -- + -- Valid options: + -- - smart: put daily-looking notes in daily, weekly-looking ones in weekly, + -- all other ones in home, except for notes/with/subdirs/in/title. + -- (default) + -- + -- - prefer_home: put all notes in home except for goto_today(), goto_thisweek() + -- except for notes/with/subdirs/in/title. + -- + -- - same_as_current: put all new notes in the dir of the current note if + -- present or else in home + -- except for notes/with/subdirs/in/title. + new_note_location = "smart", + -- should all links be updated when a file is renamed + rename_update_links = true, + -- how to preview media files + -- "telescope-media-files" if you have telescope-media-files.nvim installed + -- "catimg-previewer" if you have catimg installed + -- "viu-previewer" if you have viu installed + media_previewer = "telescope-media-files", + -- A customizable fallback handler for urls. + follow_url_fallback = nil, + } + M.Cfg = cfg + M.note_type_templates = { + normal = M.Cfg.template_new_note, + daily = M.Cfg.template_new_daily, + weekly = M.Cfg.template_new_weekly, + } end --- escapes a string for use as exact pattern within gsub local function escape(s) - return string.gsub(s, "[%%%]%^%-$().[*+?]", "%%%1") + return string.gsub(s, "[%%%]%^%-$().[*+?]", "%%%1") end local function remove_alias(link) - local split_index = string.find(link, "%s*|") - if split_index ~= nil and type(split_index) == "number" then - return string.sub(link, 0, split_index - 1) - end - return link + local split_index = string.find(link, "%s*|") + if split_index ~= nil and type(split_index) == "number" then + return string.sub(link, 0, split_index - 1) + end + return link end local function file_exists(fname) - if fname == nil then - return false - end + if fname == nil then + return false + end - local f = io.open(fname, "r") - if f ~= nil then - io.close(f) - return true - else - return false - end + local f = io.open(fname, "r") + if f ~= nil then + io.close(f) + return true + else + return false + end end local function random_variable(length) - math.randomseed(os.clock() ^ 5) - local res = "" - for _ = 1, length do - res = res .. string.char(math.random(97, 122)) - end - return res + math.randomseed(os.clock() ^ 5) + local res = "" + for _ = 1, length do + res = res .. string.char(math.random(97, 122)) + end + return res end local function get_uuid(opts) - opts.uuid_type = opts.uuid_type or M.Cfg.uuid_type + opts.uuid_type = opts.uuid_type or M.Cfg.uuid_type - local uuid - if opts.uuid_type == "rand" then - uuid = random_variable(6) - elseif type(opts.uuid_type) == "function" then - uuid = opts.uuid_type() - else - uuid = os.date(opts.uuid_type) - end - return uuid + local uuid + if opts.uuid_type == "rand" then + uuid = random_variable(6) + elseif type(opts.uuid_type) == "function" then + uuid = opts.uuid_type() + else + uuid = os.date(opts.uuid_type) + end + return uuid end local function generate_note_filename(uuid, title) - if M.Cfg.filename_space_subst ~= nil then - title = title:gsub(" ", M.Cfg.filename_space_subst) - end - - local pp = Path:new(title) - local p_splits = pp:_split() - local filename = p_splits[#p_splits] - local subdir = title:gsub(escape(filename), "") - - local sep = M.Cfg.uuid_sep or "-" - if M.Cfg.new_note_filename ~= "uuid" and #title > 0 then - if M.Cfg.new_note_filename == "uuid-title" then - return subdir .. uuid .. sep .. filename - elseif M.Cfg.new_note_filename == "title-uuid" then - return title .. sep .. uuid - else - return title + if M.Cfg.filename_space_subst ~= nil then + title = title:gsub(" ", M.Cfg.filename_space_subst) + end + + local pp = Path:new(title) + local p_splits = pp:_split() + local filename = p_splits[#p_splits] + local subdir = title:gsub(escape(filename), "") + + local sep = M.Cfg.uuid_sep or "-" + if M.Cfg.new_note_filename ~= "uuid" and #title > 0 then + if M.Cfg.new_note_filename == "uuid-title" then + return subdir .. uuid .. sep .. filename + elseif M.Cfg.new_note_filename == "title-uuid" then + return title .. sep .. uuid + else + return title + end + else + return uuid end - else - return uuid - end end local function print_error(s) - vim.cmd("echohl ErrorMsg") - vim.cmd("echomsg " .. '"' .. s .. '"') - vim.cmd("echohl None") + vim.cmd("echohl ErrorMsg") + vim.cmd("echomsg " .. '"' .. s .. '"') + vim.cmd("echohl None") end local function check_dir_and_ask(dir, purpose) - local ret = false - if dir ~= nil and Path:new(dir):exists() == false then - vim.ui.select({ "No (default)", "Yes" }, { - prompt = "Telekasten.nvim: " - .. purpose - .. " folder " - .. dir - .. " does not exist!" - .. " Shall I create it? ", - }, function(answer) - if answer == "Yes" then - if - Path:new(dir):mkdir({ parents = true, exists_ok = false }) - then - vim.cmd('echomsg " "') - vim.cmd('echomsg "' .. dir .. ' created"') - ret = true - else - -- unreachable: plenary.Path:mkdir() will error out - print_error("Could not create directory " .. dir) - ret = false - end - end - end) - else - ret = true - end - return ret + local ret = false + if dir ~= nil and Path:new(dir):exists() == false then + vim.ui.select({ "No (default)", "Yes" }, { + prompt = "Telekasten.nvim: " + .. purpose + .. " folder " + .. dir + .. " does not exist!" + .. " Shall I create it? ", + }, function(answer) + if answer == "Yes" then + if + Path:new(dir):mkdir({ parents = true, exists_ok = false }) + then + vim.cmd('echomsg " "') + vim.cmd('echomsg "' .. dir .. ' created"') + ret = true + else + -- unreachable: plenary.Path:mkdir() will error out + print_error("Could not create directory " .. dir) + ret = false + end + end + end) + else + ret = true + end + return ret end local function global_dir_check() - local ret - if M.Cfg.home == nil then - print_error("Telekasten.nvim: home is not configured!") - ret = false - else - ret = check_dir_and_ask(M.Cfg.home, "home") - end + local ret + if M.Cfg.home == nil then + print_error("Telekasten.nvim: home is not configured!") + ret = false + else + ret = check_dir_and_ask(M.Cfg.home, "home") + end - ret = ret and check_dir_and_ask(M.Cfg.dailies, "dailies") - ret = ret and check_dir_and_ask(M.Cfg.weeklies, "weeklies") - ret = ret and check_dir_and_ask(M.Cfg.templates, "templates") - ret = ret and check_dir_and_ask(M.Cfg.image_subdir, "images") + ret = ret and check_dir_and_ask(M.Cfg.dailies, "dailies") + ret = ret and check_dir_and_ask(M.Cfg.weeklies, "weeklies") + ret = ret and check_dir_and_ask(M.Cfg.templates, "templates") + ret = ret and check_dir_and_ask(M.Cfg.image_subdir, "images") - return ret + return ret end local function make_config_path_absolute(path) - local ret = path - if not (Path:new(path):is_absolute()) and path ~= nil then - ret = M.Cfg.home .. "/" .. path - end - return ret + local ret = path + if not (Path:new(path):is_absolute()) and path ~= nil then + ret = M.Cfg.home .. "/" .. path + end + return ret end -- sanitize strings local escape_chars = function(string) - return string.gsub(string, "[%(|%)|\\|%[|%]|%-|%{%}|%?|%+|%*|%^|%$|%/]", { - ["\\"] = "\\\\", - ["-"] = "\\-", - ["("] = "\\(", - [")"] = "\\)", - ["["] = "\\[", - ["]"] = "\\]", - ["{"] = "\\{", - ["}"] = "\\}", - ["?"] = "\\?", - ["+"] = "\\+", - ["*"] = "\\*", - ["^"] = "\\^", - ["$"] = "\\$", - }) + return string.gsub(string, "[%(|%)|\\|%[|%]|%-|%{%}|%?|%+|%*|%^|%$|%/]", { + ["\\"] = "\\\\", + ["-"] = "\\-", + ["("] = "\\(", + [")"] = "\\)", + ["["] = "\\[", + ["]"] = "\\]", + ["{"] = "\\{", + ["}"] = "\\}", + ["?"] = "\\?", + ["+"] = "\\+", + ["*"] = "\\*", + ["^"] = "\\^", + ["$"] = "\\$", + }) end local function recursive_substitution(dir, old, new) - if not global_dir_check() then - return - end + if not global_dir_check() then + return + end - if vim.fn.executable("sed") == 0 then - vim.api.nvim_err_write("Sed not installed!\n") - return - end + if vim.fn.executable("sed") == 0 then + vim.api.nvim_err_write("Sed not installed!\n") + return + end - old = escape_chars(old) - new = escape_chars(new) + old = escape_chars(old) + new = escape_chars(new) - local sedcommand = "sed -i" - if vim.fn.has("mac") == 1 then - sedcommand = "sed -i ''" - end + local sedcommand = "sed -i" + if vim.fn.has("mac") == 1 then + sedcommand = "sed -i ''" + end - -- 's|\(\[\[foo\)\([]#|\]\)|\[\[MYTEST\2|g' - local replace_cmd = "rg -0 -l -t markdown '" - .. old - .. "' " - .. dir - .. " | xargs -0 " - .. sedcommand - .. " 's|\\(" - .. old - .. "\\)\\([]#|]\\)|" - .. new - .. "\\2|g' >/dev/null 2>&1" - os.execute(replace_cmd) + -- 's|\(\[\[foo\)\([]#|\]\)|\[\[MYTEST\2|g' + local replace_cmd = "rg -0 -l -t markdown '" + .. old + .. "' " + .. dir + .. " | xargs -0 " + .. sedcommand + .. " 's|\\(" + .. old + .. "\\)\\([]#|]\\)|" + .. new + .. "\\2|g' >/dev/null 2>&1" + os.execute(replace_cmd) end local function save_all_mod_buffers() - for i = 1, vim.fn.bufnr("$") do - if - vim.fn.getbufvar(i, "&mod") == 1 - and ( - ( - M.Cfg.auto_set_filetype == true - and vim.fn.getbufvar(i, "&filetype") == "telekasten" - ) or M.Cfg.auto_set_filetype == false - ) - then - vim.cmd(i .. "bufdo w") + for i = 1, vim.fn.bufnr("$") do + if + vim.fn.getbufvar(i, "&mod") == 1 + and ( + ( + M.Cfg.auto_set_filetype == true + and vim.fn.getbufvar(i, "&filetype") == "telekasten" + ) or M.Cfg.auto_set_filetype == false + ) + then + vim.cmd(i .. "bufdo w") + end end - end end -- ---------------------------------------------------------------------------- @@ -366,314 +366,314 @@ end -- ---------------------------------------------------------------------------- local function make_relative_path(bufferpath, imagepath, sep) - sep = sep or "/" + sep = sep or "/" - -- Split the buffer and image path into their dirs/files - local buffer_dirs = {} - for w in string.gmatch(bufferpath, "([^" .. sep .. "]+)") do - buffer_dirs[#buffer_dirs + 1] = w - end - local image_dirs = {} - for w in string.gmatch(imagepath, "([^" .. sep .. "]+)") do - image_dirs[#image_dirs + 1] = w - end - - -- The parts of the dir list that match won't matter, so skip them - local i = 1 - while i < #image_dirs and i < #buffer_dirs do - if image_dirs[i] ~= buffer_dirs[i] then - break - else - i = i + 1 + -- Split the buffer and image path into their dirs/files + local buffer_dirs = {} + for w in string.gmatch(bufferpath, "([^" .. sep .. "]+)") do + buffer_dirs[#buffer_dirs + 1] = w end - end - - -- Append ../ to walk up from the buffer location and the path downward - -- to the location of the image file in order to create a relative path - local relative_path = "" - while i <= #image_dirs or i <= #buffer_dirs do - if i <= #image_dirs then - if relative_path == "" then - relative_path = image_dirs[i] - else - relative_path = relative_path .. sep .. image_dirs[i] - end + local image_dirs = {} + for w in string.gmatch(imagepath, "([^" .. sep .. "]+)") do + image_dirs[#image_dirs + 1] = w end - if i <= #buffer_dirs - 1 then - relative_path = ".." .. sep .. relative_path - end - i = i + 1 - end - return relative_path + -- The parts of the dir list that match won't matter, so skip them + local i = 1 + while i < #image_dirs and i < #buffer_dirs do + if image_dirs[i] ~= buffer_dirs[i] then + break + else + i = i + 1 + end + end + + -- Append ../ to walk up from the buffer location and the path downward + -- to the location of the image file in order to create a relative path + local relative_path = "" + while i <= #image_dirs or i <= #buffer_dirs do + if i <= #image_dirs then + if relative_path == "" then + relative_path = image_dirs[i] + else + relative_path = relative_path .. sep .. image_dirs[i] + end + end + if i <= #buffer_dirs - 1 then + relative_path = ".." .. sep .. relative_path + end + i = i + 1 + end + + return relative_path end local function imgFromClipboard() - if not global_dir_check() then - return - end - - local get_paste_command - if vim.fn.executable("xclip") == 1 then - get_paste_command = function(dir, filename) - return "xclip -selection clipboard -t image/png -o > " - .. dir - .. "/" - .. filename + if not global_dir_check() then + return end - elseif vim.fn.executable("wl-paste") == 1 then - get_paste_command = function(dir, filename) - return "wl-paste -n -t image/png > " .. dir .. "/" .. filename - end - elseif vim.fn.executable("osascript") == 1 then - get_paste_command = function(dir, filename) - return string.format( - 'osascript -e "tell application \\"System Events\\" to write (the clipboard as «class PNGf») to ' - .. '(make new file at folder \\"%s\\" with properties {name:\\"%s\\"})"', - dir, - filename - ) - end - else - vim.api.nvim_err_write("No xclip installed!\n") - return - end - -- TODO: check `xclip -selection clipboard -t TARGETS -o` for the occurence of `image/png` - - -- using plenary.job::new():sync() with on_stdout(_, data) unfortunately did some weird ASCII translation on the - -- data, so the PNGs were invalid. It seems like 0d 0a and single 0a bytes were stripped by the plenary job: - -- - -- plenary job version: - -- $ hexdump -C /tmp/x.png|head - -- 00000000 89 50 4e 47 1a 00 00 00 49 48 44 52 00 00 03 19 |.PNG....IHDR....| - -- 00000010 00 00 01 c1 08 02 00 00 00 8a 73 e1 c3 00 00 00 |..........s.....| - -- 00000020 09 70 48 59 73 00 00 0e c4 00 00 0e c4 01 95 2b |.pHYs..........+| - -- 00000030 0e 1b 00 00 20 00 49 44 41 54 78 9c ec dd 77 58 |.... .IDATx...wX| - -- 00000040 14 d7 fa 07 f0 33 bb b3 4b af 0b 2c 08 22 1d 04 |.....3..K..,."..| - -- 00000050 05 11 10 1b a2 54 c5 1e bb b1 c6 98 c4 68 72 4d |.....T.......hrM| - -- 00000060 e2 cd 35 37 26 b9 49 6e 6e 7e f7 a6 98 98 a8 29 |..57&.Inn~.....)| - -- 00000070 26 6a 8c 51 63 8b bd 00 8a 58 40 b0 81 08 2a 45 |&j.Qc....X@...*E| - -- 00000080 69 52 17 58 ca ee b2 f5 f7 c7 ea 4a 10 66 d7 01 |iR.X.......J.f..| - -- 00000090 b1 e4 fb 79 7c f2 2c e7 cc 39 e7 3d 67 66 b3 2f |...y|.,..9.=gf./| - -- - -- OK version - -- $ hexdump -C /tmp/x2.png|head - -- 00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR| - -- 00000010 00 00 03 19 00 00 01 c1 08 02 00 00 00 8a 73 e1 |..............s.| - -- 00000020 c3 00 00 00 09 70 48 59 73 00 00 0e c4 00 00 0e |.....pHYs.......| - -- 00000030 c4 01 95 2b 0e 1b 00 00 20 00 49 44 41 54 78 9c |...+.... .IDATx.| - -- 00000040 ec dd 77 58 14 d7 fa 07 f0 33 bb b3 4b af 0b 2c |..wX.....3..K..,| - -- 00000050 08 22 1d 04 05 11 10 1b a2 54 c5 1e bb b1 c6 98 |.".......T......| - -- 00000060 c4 68 72 4d e2 cd 35 37 26 b9 49 6e 6e 7e f7 a6 |.hrM..57&.Inn~..| - -- 00000070 98 98 a8 29 26 6a 8c 51 63 8b bd 00 8a 58 40 b0 |...)&j.Qc....X@.| - -- 00000080 81 08 2a 45 69 52 17 58 ca ee b2 f5 f7 c7 ea 4a |..*EiR.X.......J| - -- 00000090 10 66 d7 01 b1 e4 fb 79 7c f2 2c e7 cc 39 e7 3d |.f.....y|.,..9.=| - - local pngname = "pasted_img_" .. os.date("%Y%m%d%H%M%S") .. ".png" - local pngdir = M.Cfg.image_subdir and M.Cfg.image_subdir or M.Cfg.home - local png = pngdir .. "/" .. pngname - local relpath = make_relative_path(vim.fn.expand("%:p"), png, "/") - - local result = os.execute(get_paste_command(pngdir, pngname)) - if result > 0 then - vim.api.nvim_err_writeln( - string.format( - "Unable to write image %s (exit code: %d). Is there an image on the clipboard? ", - png, - result - ) - ) - return - end - - if file_exists(png) then - if M.Cfg.image_link_style == "markdown" then - vim.api.nvim_put({ "![](" .. relpath .. ")" }, "", true, true) + local get_paste_command + if vim.fn.executable("xclip") == 1 then + get_paste_command = function(dir, filename) + return "xclip -selection clipboard -t image/png -o > " + .. dir + .. "/" + .. filename + end + elseif vim.fn.executable("wl-paste") == 1 then + get_paste_command = function(dir, filename) + return "wl-paste -n -t image/png > " .. dir .. "/" .. filename + end + elseif vim.fn.executable("osascript") == 1 then + get_paste_command = function(dir, filename) + return string.format( + 'osascript -e "tell application \\"System Events\\" to write (the clipboard as «class PNGf») to ' + .. '(make new file at folder \\"%s\\" with properties {name:\\"%s\\"})"', + dir, + filename + ) + end else - vim.api.nvim_put({ "![[" .. pngname .. "]]" }, "", true, true) + vim.api.nvim_err_write("No xclip installed!\n") + return + end + + -- TODO: check `xclip -selection clipboard -t TARGETS -o` for the occurence of `image/png` + + -- using plenary.job::new():sync() with on_stdout(_, data) unfortunately did some weird ASCII translation on the + -- data, so the PNGs were invalid. It seems like 0d 0a and single 0a bytes were stripped by the plenary job: + -- + -- plenary job version: + -- $ hexdump -C /tmp/x.png|head + -- 00000000 89 50 4e 47 1a 00 00 00 49 48 44 52 00 00 03 19 |.PNG....IHDR....| + -- 00000010 00 00 01 c1 08 02 00 00 00 8a 73 e1 c3 00 00 00 |..........s.....| + -- 00000020 09 70 48 59 73 00 00 0e c4 00 00 0e c4 01 95 2b |.pHYs..........+| + -- 00000030 0e 1b 00 00 20 00 49 44 41 54 78 9c ec dd 77 58 |.... .IDATx...wX| + -- 00000040 14 d7 fa 07 f0 33 bb b3 4b af 0b 2c 08 22 1d 04 |.....3..K..,."..| + -- 00000050 05 11 10 1b a2 54 c5 1e bb b1 c6 98 c4 68 72 4d |.....T.......hrM| + -- 00000060 e2 cd 35 37 26 b9 49 6e 6e 7e f7 a6 98 98 a8 29 |..57&.Inn~.....)| + -- 00000070 26 6a 8c 51 63 8b bd 00 8a 58 40 b0 81 08 2a 45 |&j.Qc....X@...*E| + -- 00000080 69 52 17 58 ca ee b2 f5 f7 c7 ea 4a 10 66 d7 01 |iR.X.......J.f..| + -- 00000090 b1 e4 fb 79 7c f2 2c e7 cc 39 e7 3d 67 66 b3 2f |...y|.,..9.=gf./| + -- + -- OK version + -- $ hexdump -C /tmp/x2.png|head + -- 00000000 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 |.PNG........IHDR| + -- 00000010 00 00 03 19 00 00 01 c1 08 02 00 00 00 8a 73 e1 |..............s.| + -- 00000020 c3 00 00 00 09 70 48 59 73 00 00 0e c4 00 00 0e |.....pHYs.......| + -- 00000030 c4 01 95 2b 0e 1b 00 00 20 00 49 44 41 54 78 9c |...+.... .IDATx.| + -- 00000040 ec dd 77 58 14 d7 fa 07 f0 33 bb b3 4b af 0b 2c |..wX.....3..K..,| + -- 00000050 08 22 1d 04 05 11 10 1b a2 54 c5 1e bb b1 c6 98 |.".......T......| + -- 00000060 c4 68 72 4d e2 cd 35 37 26 b9 49 6e 6e 7e f7 a6 |.hrM..57&.Inn~..| + -- 00000070 98 98 a8 29 26 6a 8c 51 63 8b bd 00 8a 58 40 b0 |...)&j.Qc....X@.| + -- 00000080 81 08 2a 45 69 52 17 58 ca ee b2 f5 f7 c7 ea 4a |..*EiR.X.......J| + -- 00000090 10 66 d7 01 b1 e4 fb 79 7c f2 2c e7 cc 39 e7 3d |.f.....y|.,..9.=| + + local pngname = "pasted_img_" .. os.date("%Y%m%d%H%M%S") .. ".png" + local pngdir = M.Cfg.image_subdir and M.Cfg.image_subdir or M.Cfg.home + local png = pngdir .. "/" .. pngname + local relpath = make_relative_path(vim.fn.expand("%:p"), png, "/") + + local result = os.execute(get_paste_command(pngdir, pngname)) + if result > 0 then + vim.api.nvim_err_writeln( + string.format( + "Unable to write image %s (exit code: %d). Is there an image on the clipboard? ", + png, + result + ) + ) + return + end + + if file_exists(png) then + if M.Cfg.image_link_style == "markdown" then + vim.api.nvim_put({ "![](" .. relpath .. ")" }, "", true, true) + else + vim.api.nvim_put({ "![[" .. pngname .. "]]" }, "", true, true) + end + else + vim.api.nvim_err_writeln("Unable to write image " .. png) end - else - vim.api.nvim_err_writeln("Unable to write image " .. png) - end end -- end of image stuff local function daysuffix(day) - day = tostring(day) - if (day == "1") or (day == "21") or (day == "31") then - return "st" - end - if (day == "2") or (day == "22") then - return "nd" - end - if (day == "3") or (day == "23") then - return "rd" - end - return "th" + day = tostring(day) + if (day == "1") or (day == "21") or (day == "31") then + return "st" + end + if (day == "2") or (day == "22") then + return "nd" + end + if (day == "3") or (day == "23") then + return "rd" + end + return "th" end local daymap = { - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", } local monthmap = { - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", } local dateformats = { - date = "%Y-%m-%d", - week = "%V", - isoweek = "%Y-W%V", - time24 = "%H:%M:%S", - time12 = "%I:%M:%S %p", + date = "%Y-%m-%d", + week = "%V", + isoweek = "%Y-W%V", + time24 = "%H:%M:%S", + time12 = "%I:%M:%S %p", } local function calculate_dates(date) - local time = os.time(date) - local dinfo = os.date("*t", time) -- this normalizes the input to a full date table - local oneday = 24 * 60 * 60 -- hours * days * seconds - local oneweek = 7 * oneday - local df = dateformats + local time = os.time(date) + local dinfo = os.date("*t", time) -- this normalizes the input to a full date table + local oneday = 24 * 60 * 60 -- hours * days * seconds + local oneweek = 7 * oneday + local df = dateformats - local dates = {} + local dates = {} - -- this is to compensate for the calendar showing M-Su, but os.date Su is - -- always wday = 1 - local wday = dinfo.wday - 1 - if wday == 0 then - wday = 7 - end + -- this is to compensate for the calendar showing M-Su, but os.date Su is + -- always wday = 1 + local wday = dinfo.wday - 1 + if wday == 0 then + wday = 7 + end - dates.year = dinfo.year - dates.month = dinfo.month - dates.day = dinfo.day - dates.hdate = daymap[wday] - .. ", " - .. monthmap[dinfo.month] - .. " " - .. dinfo.day - .. daysuffix(dinfo.day) - .. ", " - .. dinfo.year + dates.year = dinfo.year + dates.month = dinfo.month + dates.day = dinfo.day + dates.hdate = daymap[wday] + .. ", " + .. monthmap[dinfo.month] + .. " " + .. dinfo.day + .. daysuffix(dinfo.day) + .. ", " + .. dinfo.year - local zonehour = string.sub(os.date("%z"), 1, 3) - local zonemin = string.sub(os.date("%z"), 4, 5) - dates.rfc3339 = os.date(df.date, time) - .. os.date("T%H:%M:%S") - .. "Z" - .. zonehour - .. ":" - .. zonemin + local zonehour = string.sub(os.date("%z"), 1, 3) + local zonemin = string.sub(os.date("%z"), 4, 5) + dates.rfc3339 = os.date(df.date, time) + .. os.date("T%H:%M:%S") + .. "Z" + .. zonehour + .. ":" + .. zonemin - dates.time24 = os.date(df.time24, time) - dates.time12 = os.date(df.time12, time) - dates.date = os.date(df.date, time) - dates.prevday = os.date(df.date, time - oneday) - dates.nextday = os.date(df.date, time + oneday) - dates.week = os.date(df.week, time) - dates.prevweek = os.date(df.week, time - oneweek) - dates.nextweek = os.date(df.week, time + oneweek) - dates.isoweek = os.date(df.isoweek, time) - dates.isoprevweek = os.date(df.isoweek, time - oneweek) - dates.isonextweek = os.date(df.isoweek, time + oneweek) + dates.time24 = os.date(df.time24, time) + dates.time12 = os.date(df.time12, time) + dates.date = os.date(df.date, time) + dates.prevday = os.date(df.date, time - oneday) + dates.nextday = os.date(df.date, time + oneday) + dates.week = os.date(df.week, time) + dates.prevweek = os.date(df.week, time - oneweek) + dates.nextweek = os.date(df.week, time + oneweek) + dates.isoweek = os.date(df.isoweek, time) + dates.isoprevweek = os.date(df.isoweek, time - oneweek) + dates.isonextweek = os.date(df.isoweek, time + oneweek) - -- things get a bit hairy at the year rollover. W01 only starts the first week ofs - -- January if it has more than 3 days. Partial weeks with less than 4 days are - -- considered W52, but os.date still sets the year as the new year, so Jan 1 2022 - -- would appear as being in 2022-W52. That breaks linear linking respective - -- of next/prev week, so we want to put the days of that partial week in - -- January in 2021-W52. This tweak will only change the ISO formatted week string. - if tonumber(dates.week) == 52 and tonumber(dates.month) == 1 then - dates.isoweek = tostring(dates.year - 1) .. "-W52" - end + -- things get a bit hairy at the year rollover. W01 only starts the first week ofs + -- January if it has more than 3 days. Partial weeks with less than 4 days are + -- considered W52, but os.date still sets the year as the new year, so Jan 1 2022 + -- would appear as being in 2022-W52. That breaks linear linking respective + -- of next/prev week, so we want to put the days of that partial week in + -- January in 2021-W52. This tweak will only change the ISO formatted week string. + if tonumber(dates.week) == 52 and tonumber(dates.month) == 1 then + dates.isoweek = tostring(dates.year - 1) .. "-W52" + end - -- Find the Sunday that started this week regardless of the calendar - -- display preference. Then use that as the base to calculate the dates - -- for the days of the current week. - -- Finally, adjust Sunday to suit user calendar preference. - local starting_sunday = time - (wday * oneday) - local sunday_offset = 0 - if M.Cfg.calendar_opts.calendar_monday == 1 then - sunday_offset = 7 - end - dates.monday = os.date(df.date, starting_sunday + (1 * oneday)) - dates.tuesday = os.date(df.date, starting_sunday + (2 * oneday)) - dates.wednesday = os.date(df.date, starting_sunday + (3 * oneday)) - dates.thursday = os.date(df.date, starting_sunday + (4 * oneday)) - dates.friday = os.date(df.date, starting_sunday + (5 * oneday)) - dates.saturday = os.date(df.date, starting_sunday + (6 * oneday)) - dates.sunday = os.date(df.date, starting_sunday + (sunday_offset * oneday)) + -- Find the Sunday that started this week regardless of the calendar + -- display preference. Then use that as the base to calculate the dates + -- for the days of the current week. + -- Finally, adjust Sunday to suit user calendar preference. + local starting_sunday = time - (wday * oneday) + local sunday_offset = 0 + if M.Cfg.calendar_opts.calendar_monday == 1 then + sunday_offset = 7 + end + dates.monday = os.date(df.date, starting_sunday + (1 * oneday)) + dates.tuesday = os.date(df.date, starting_sunday + (2 * oneday)) + dates.wednesday = os.date(df.date, starting_sunday + (3 * oneday)) + dates.thursday = os.date(df.date, starting_sunday + (4 * oneday)) + dates.friday = os.date(df.date, starting_sunday + (5 * oneday)) + dates.saturday = os.date(df.date, starting_sunday + (6 * oneday)) + dates.sunday = os.date(df.date, starting_sunday + (sunday_offset * oneday)) - return dates + return dates end local function linesubst(line, title, dates, uuid) - if dates == nil then - dates = calculate_dates() - end - if uuid == nil then - uuid = "" - end + if dates == nil then + dates = calculate_dates() + end + if uuid == nil then + uuid = "" + end - local shorttitle = string.match(title, "^.+/(.+)$") - if shorttitle == nil then - shorttitle = title - end + local shorttitle = string.match(title, "^.+/(.+)$") + if shorttitle == nil then + shorttitle = title + end - local substs = vim.tbl_extend("error", dates, { - shorttitle = shorttitle:gsub("^%l", string.upper), - uuid = uuid, - title = title:gsub("^%l", string.upper), - }) + local substs = vim.tbl_extend("error", dates, { + shorttitle = shorttitle:gsub("^%l", string.upper), + uuid = uuid, + title = title:gsub("^%l", string.upper), + }) - for k, v in pairs(substs) do - line = line:gsub("{{" .. k .. "}}", v) - end + for k, v in pairs(substs) do + line = line:gsub("{{" .. k .. "}}", v) + end - return line + return line end local function create_note_from_template( - title, - uuid, - filepath, - templatefn, - calendar_info + title, + uuid, + filepath, + templatefn, + calendar_info ) - -- first, read the template file - local lines = {} - if file_exists(templatefn) then - for line in io.lines(templatefn) do - lines[#lines + 1] = line + -- first, read the template file + local lines = {} + if file_exists(templatefn) then + for line in io.lines(templatefn) do + lines[#lines + 1] = line + end end - end - -- now write the output file, substituting vars line by line - local ofile = io.open(filepath, "a") - for _, line in pairs(lines) do - ofile:write(linesubst(line, title, calendar_info, uuid) .. "\n") - end + -- now write the output file, substituting vars line by line + local ofile = io.open(filepath, "a") + for _, line in pairs(lines) do + ofile:write(linesubst(line, title, calendar_info, uuid) .. "\n") + end - ofile:flush() - ofile:close() + ofile:flush() + ofile:close() end --- Pinfo @@ -689,256 +689,256 @@ end --- - is_weekly : bool --- - template : suggested template based on opts local Pinfo = { - fexists = false, - title = "", - filename = "", - filepath = "", - root_dir = "", - sub_dir = "", - is_daily_or_weekly = false, - is_daily = false, - is_weekly = false, - template = "", - calendar_info = nil, + fexists = false, + title = "", + filename = "", + filepath = "", + root_dir = "", + sub_dir = "", + is_daily_or_weekly = false, + is_daily = false, + is_weekly = false, + template = "", + calendar_info = nil, } function Pinfo:new(opts) - opts = opts or {} + opts = opts or {} - local object = {} - setmetatable(object, self) - self.__index = self - if opts.filepath then - return object:resolve_path(opts.filepath, opts) - end - if opts.title ~= nil then - return object:resolve_link(opts.title, opts) - end - return object + local object = {} + setmetatable(object, self) + self.__index = self + if opts.filepath then + return object:resolve_path(opts.filepath, opts) + end + if opts.title ~= nil then + return object:resolve_link(opts.title, opts) + end + return object end --- resolve_path(p, opts) --- inspects the path and returns a Pinfo table function Pinfo:resolve_path(p, opts) - opts = opts or {} - opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links + opts = opts or {} + opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links - self.fexists = file_exists(p) - self.filepath = p - self.root_dir = M.Cfg.home - self.is_daily_or_weekly = false - self.is_daily = false - self.is_weekly = false - - -- strip all dirs to get filename - local pp = Path:new(p) - local p_splits = pp:_split() - self.filename = p_splits[#p_splits] - self.title = self.filename:gsub(M.Cfg.extension, "") - - if vim.startswith(p, M.Cfg.home) then + self.fexists = file_exists(p) + self.filepath = p self.root_dir = M.Cfg.home - end - if vim.startswith(p, M.Cfg.dailies) then - self.root_dir = M.Cfg.dailies - -- TODO: parse "title" into calendarinfo like in resolve_link - -- not really necessary as the file exists anyway and therefore we don't need to instantiate a template - self.is_daily_or_weekly = true - self.is_daily = true - end - if vim.startswith(p, M.Cfg.weeklies) then - -- TODO: parse "title" into calendarinfo like in resolve_link - -- not really necessary as the file exists anyway and therefore we don't need to instantiate a template - self.root_dir = M.Cfg.weeklies - self.is_daily_or_weekly = true - self.is_weekly = true - end + self.is_daily_or_weekly = false + self.is_daily = false + self.is_weekly = false - -- now work out subdir relative to root - self.sub_dir = p:gsub(escape(self.root_dir .. "/"), "") - :gsub(escape(self.filename), "") - :gsub("/$", "") - :gsub("^/", "") + -- strip all dirs to get filename + local pp = Path:new(p) + local p_splits = pp:_split() + self.filename = p_splits[#p_splits] + self.title = self.filename:gsub(M.Cfg.extension, "") - if opts.subdirs_in_links and #self.sub_dir > 0 then - self.title = self.sub_dir .. "/" .. self.title - end + if vim.startswith(p, M.Cfg.home) then + self.root_dir = M.Cfg.home + end + if vim.startswith(p, M.Cfg.dailies) then + self.root_dir = M.Cfg.dailies + -- TODO: parse "title" into calendarinfo like in resolve_link + -- not really necessary as the file exists anyway and therefore we don't need to instantiate a template + self.is_daily_or_weekly = true + self.is_daily = true + end + if vim.startswith(p, M.Cfg.weeklies) then + -- TODO: parse "title" into calendarinfo like in resolve_link + -- not really necessary as the file exists anyway and therefore we don't need to instantiate a template + self.root_dir = M.Cfg.weeklies + self.is_daily_or_weekly = true + self.is_weekly = true + end - return self + -- now work out subdir relative to root + self.sub_dir = p:gsub(escape(self.root_dir .. "/"), "") + :gsub(escape(self.filename), "") + :gsub("/$", "") + :gsub("^/", "") + + if opts.subdirs_in_links and #self.sub_dir > 0 then + self.title = self.sub_dir .. "/" .. self.title + end + + return self end local function check_if_daily_or_weekly(title) - local daily_match = "^(%d%d%d%d)-(%d%d)-(%d%d)$" - local weekly_match = "^(%d%d%d%d)-W(%d%d)$" + local daily_match = "^(%d%d%d%d)-(%d%d)-(%d%d)$" + local weekly_match = "^(%d%d%d%d)-W(%d%d)$" - local is_daily = false - local is_weekly = false - local dateinfo = calculate_dates() -- sane default + local is_daily = false + local is_weekly = false + local dateinfo = calculate_dates() -- sane default - local start, _, year, month, day = title:find(daily_match) - if start ~= nil then - if tonumber(month) < 13 then - if tonumber(day) < 32 then - is_daily = true - dateinfo.year = tonumber(year) - dateinfo.month = tonumber(month) - dateinfo.day = tonumber(day) - dateinfo = calculate_dates(dateinfo) - end + local start, _, year, month, day = title:find(daily_match) + if start ~= nil then + if tonumber(month) < 13 then + if tonumber(day) < 32 then + is_daily = true + dateinfo.year = tonumber(year) + dateinfo.month = tonumber(month) + dateinfo.day = tonumber(day) + dateinfo = calculate_dates(dateinfo) + end + end end - end - local week - start, _, year, week = title:find(weekly_match) - if start ~= nil then - if tonumber(week) < 53 then - is_weekly = true - -- ISO8601 week -> date calculation - dateinfo = dateutils.isoweek_to_date(tonumber(year), tonumber(week)) - dateinfo = calculate_dates(dateinfo) + local week + start, _, year, week = title:find(weekly_match) + if start ~= nil then + if tonumber(week) < 53 then + is_weekly = true + -- ISO8601 week -> date calculation + dateinfo = dateutils.isoweek_to_date(tonumber(year), tonumber(week)) + dateinfo = calculate_dates(dateinfo) + end end - end - return is_daily, is_weekly, dateinfo + return is_daily, is_weekly, dateinfo end function Pinfo:resolve_link(title, opts) - opts = opts or {} - opts.weeklies = opts.weeklies or M.Cfg.weeklies - opts.dailies = opts.dailies or M.Cfg.dailies - opts.home = opts.home or M.Cfg.home - opts.extension = opts.extension or M.Cfg.extension - opts.template_handling = opts.template_handling or M.Cfg.template_handling - opts.new_note_location = opts.new_note_location or M.Cfg.new_note_location + opts = opts or {} + opts.weeklies = opts.weeklies or M.Cfg.weeklies + opts.dailies = opts.dailies or M.Cfg.dailies + opts.home = opts.home or M.Cfg.home + opts.extension = opts.extension or M.Cfg.extension + opts.template_handling = opts.template_handling or M.Cfg.template_handling + opts.new_note_location = opts.new_note_location or M.Cfg.new_note_location - self.fexists = false - self.title = title - self.filename = title .. opts.extension - self.filename = self.filename:gsub("^%./", "") -- strip potential leading ./ - self.root_dir = opts.home - self.is_daily_or_weekly = false - self.is_daily = false - self.is_weekly = false - self.template = nil - self.calendar_info = nil - - if opts.weeklies and file_exists(opts.weeklies .. "/" .. self.filename) then - -- TODO: parse "title" into calendarinfo like below - -- not really necessary as the file exists anyway and therefore we don't need to instantiate a template - -- if we still want calendar_info, just move the code for it out of `if self.fexists == false`. - self.filepath = opts.weeklies .. "/" .. self.filename - self.fexists = true - self.root_dir = opts.weeklies - self.is_daily_or_weekly = true - self.is_weekly = true - end - if opts.dailies and file_exists(opts.dailies .. "/" .. self.filename) then - -- TODO: parse "title" into calendarinfo like below - -- not really necessary as the file exists anyway and therefore we don't need to instantiate a template - -- if we still want calendar_info, just move the code for it out of `if self.fexists == false`. - self.filepath = opts.dailies .. "/" .. self.filename - self.fexists = true - self.root_dir = opts.dailies - self.is_daily_or_weekly = true - self.is_daily = true - end - if file_exists(opts.home .. "/" .. self.filename) then - self.filepath = opts.home .. "/" .. self.filename - self.fexists = true - end - - if self.fexists == false then - -- now search for it in all subdirs - local subdirs = scan.scan_dir(opts.home, { only_dirs = true }) - local tempfn - for _, folder in pairs(subdirs) do - tempfn = folder .. "/" .. self.filename - -- [[testnote]] - if file_exists(tempfn) then - self.filepath = tempfn - self.fexists = true - -- print("Found: " ..self.filename) - break - end - end - end - - -- if we just cannot find the note, check if it's a daily or weekly one - if self.fexists == false then - -- TODO: if we're not smart, we also shouldn't need to try to set the calendar info..? - -- I bet someone will want the info in there, so let's put it in if possible - _, _, self.calendar_info = check_if_daily_or_weekly(self.title) -- will set today as default, so leave in! - - if opts.new_note_location == "smart" then - self.filepath = opts.home .. "/" .. self.filename -- default - self.is_daily, self.is_weekly, self.calendar_info = - check_if_daily_or_weekly(self.title) - if self.is_daily == true then - self.root_dir = opts.dailies - self.filepath = opts.dailies .. "/" .. self.filename - self.is_daily_or_weekly = true - end - if self.is_weekly == true then - self.root_dir = opts.weeklies - self.filepath = opts.weeklies .. "/" .. self.filename - self.is_daily_or_weekly = true - end - elseif opts.new_note_location == "same_as_current" then - local cwd = vim.fn.expand("%:p") - if #cwd > 0 then - self.root_dir = Path:new(cwd):parent():absolute() - if Path:new(self.root_dir):exists() then - -- check if potential subfolders in filename would end up in a non-existing directory - self.filepath = self.root_dir .. "/" .. self.filename - if not Path:new(self.filepath):parent():exists() then - print("Path " .. self.filepath .. " is invalid!") - -- self.filepath = opts.home .. "/" .. self.filename - end - else - print("Path " .. self.root_dir .. " is invalid!") - -- self.filepath = opts.home .. "/" .. self.filename - end - else - self.filepath = opts.home .. "/" .. self.filename - end - else - -- default fn for creation - self.filepath = opts.home .. "/" .. self.filename - end - - -- final round, there still can be a subdir mess-up - if not Path:new(self.filepath):parent():exists() then - print("Path " .. self.filepath .. " is invalid!") - -- local fname_only = Path:new(self.filename):_split() - -- fname_only = fname_only[#fname_only] - -- self.filepath = opts.home .. "/" .. fname_only - self.filepath = "" - end - end - - -- now work out subdir relative to root - self.sub_dir = self.filepath - :gsub(escape(self.root_dir .. "/"), "") - :gsub(escape(self.filename), "") - :gsub("/$", "") - :gsub("^/", "") - - -- now suggest a template based on opts - self.template = M.note_type_templates.normal - if opts.template_handling == "prefer_new_note" then - self.template = M.note_type_templates.normal - elseif opts.template_handling == "always_ask" then + self.fexists = false + self.title = title + self.filename = title .. opts.extension + self.filename = self.filename:gsub("^%./", "") -- strip potential leading ./ + self.root_dir = opts.home + self.is_daily_or_weekly = false + self.is_daily = false + self.is_weekly = false self.template = nil - elseif opts.template_handling == "smart" then - if self.is_daily then - self.template = M.note_type_templates.daily - elseif self.is_weekly then - self.template = M.note_type_templates.weekly - else - self.template = M.note_type_templates.normal - end - end + self.calendar_info = nil - return self + if opts.weeklies and file_exists(opts.weeklies .. "/" .. self.filename) then + -- TODO: parse "title" into calendarinfo like below + -- not really necessary as the file exists anyway and therefore we don't need to instantiate a template + -- if we still want calendar_info, just move the code for it out of `if self.fexists == false`. + self.filepath = opts.weeklies .. "/" .. self.filename + self.fexists = true + self.root_dir = opts.weeklies + self.is_daily_or_weekly = true + self.is_weekly = true + end + if opts.dailies and file_exists(opts.dailies .. "/" .. self.filename) then + -- TODO: parse "title" into calendarinfo like below + -- not really necessary as the file exists anyway and therefore we don't need to instantiate a template + -- if we still want calendar_info, just move the code for it out of `if self.fexists == false`. + self.filepath = opts.dailies .. "/" .. self.filename + self.fexists = true + self.root_dir = opts.dailies + self.is_daily_or_weekly = true + self.is_daily = true + end + if file_exists(opts.home .. "/" .. self.filename) then + self.filepath = opts.home .. "/" .. self.filename + self.fexists = true + end + + if self.fexists == false then + -- now search for it in all subdirs + local subdirs = scan.scan_dir(opts.home, { only_dirs = true }) + local tempfn + for _, folder in pairs(subdirs) do + tempfn = folder .. "/" .. self.filename + -- [[testnote]] + if file_exists(tempfn) then + self.filepath = tempfn + self.fexists = true + -- print("Found: " ..self.filename) + break + end + end + end + + -- if we just cannot find the note, check if it's a daily or weekly one + if self.fexists == false then + -- TODO: if we're not smart, we also shouldn't need to try to set the calendar info..? + -- I bet someone will want the info in there, so let's put it in if possible + _, _, self.calendar_info = check_if_daily_or_weekly(self.title) -- will set today as default, so leave in! + + if opts.new_note_location == "smart" then + self.filepath = opts.home .. "/" .. self.filename -- default + self.is_daily, self.is_weekly, self.calendar_info = + check_if_daily_or_weekly(self.title) + if self.is_daily == true then + self.root_dir = opts.dailies + self.filepath = opts.dailies .. "/" .. self.filename + self.is_daily_or_weekly = true + end + if self.is_weekly == true then + self.root_dir = opts.weeklies + self.filepath = opts.weeklies .. "/" .. self.filename + self.is_daily_or_weekly = true + end + elseif opts.new_note_location == "same_as_current" then + local cwd = vim.fn.expand("%:p") + if #cwd > 0 then + self.root_dir = Path:new(cwd):parent():absolute() + if Path:new(self.root_dir):exists() then + -- check if potential subfolders in filename would end up in a non-existing directory + self.filepath = self.root_dir .. "/" .. self.filename + if not Path:new(self.filepath):parent():exists() then + print("Path " .. self.filepath .. " is invalid!") + -- self.filepath = opts.home .. "/" .. self.filename + end + else + print("Path " .. self.root_dir .. " is invalid!") + -- self.filepath = opts.home .. "/" .. self.filename + end + else + self.filepath = opts.home .. "/" .. self.filename + end + else + -- default fn for creation + self.filepath = opts.home .. "/" .. self.filename + end + + -- final round, there still can be a subdir mess-up + if not Path:new(self.filepath):parent():exists() then + print("Path " .. self.filepath .. " is invalid!") + -- local fname_only = Path:new(self.filename):_split() + -- fname_only = fname_only[#fname_only] + -- self.filepath = opts.home .. "/" .. fname_only + self.filepath = "" + end + end + + -- now work out subdir relative to root + self.sub_dir = self.filepath + :gsub(escape(self.root_dir .. "/"), "") + :gsub(escape(self.filename), "") + :gsub("/$", "") + :gsub("^/", "") + + -- now suggest a template based on opts + self.template = M.note_type_templates.normal + if opts.template_handling == "prefer_new_note" then + self.template = M.note_type_templates.normal + elseif opts.template_handling == "always_ask" then + self.template = nil + elseif opts.template_handling == "smart" then + if self.is_daily then + self.template = M.note_type_templates.daily + elseif self.is_weekly then + self.template = M.note_type_templates.weekly + else + self.template = M.note_type_templates.normal + end + end + + return self end -- local function endswith(s, ending) @@ -946,24 +946,24 @@ end -- end local function file_extension(fname) - return fname:match("^.+(%..+)$") + return fname:match("^.+(%..+)$") end local function filter_filetypes(flist, ftypes) - local new_fl = {} - ftypes = ftypes or { M.Cfg.extension } + local new_fl = {} + ftypes = ftypes or { M.Cfg.extension } - local ftypeok = {} - for _, ft in pairs(ftypes) do - ftypeok[ft] = true - end - - for _, fn in pairs(flist) do - if ftypeok[file_extension(fn)] then - table.insert(new_fl, fn) + local ftypeok = {} + for _, ft in pairs(ftypes) do + ftypeok[ft] = true end - end - return new_fl + + for _, fn in pairs(flist) do + if ftypeok[file_extension(fn)] then + table.insert(new_fl, fn) + end + end + return new_fl end local sourced_file = debug_utils.sourced_filepath() @@ -972,47 +972,47 @@ local media_files_base_directory = M.base_directory .. "/telescope-media-files.nvim" local defaulter = utils.make_default_callable local media_preview = defaulter(function(opts) - local preview_cmd = "" - if M.Cfg.media_previewer == "telescope-media-files" then - preview_cmd = media_files_base_directory .. "/scripts/vimg" - end + local preview_cmd = "" + if M.Cfg.media_previewer == "telescope-media-files" then + preview_cmd = media_files_base_directory .. "/scripts/vimg" + end - if M.Cfg.media_previewer == "catimg-previewer" then - preview_cmd = M.base_directory - .. "/telekasten.nvim/scripts/catimg-previewer" - end + if M.Cfg.media_previewer == "catimg-previewer" then + preview_cmd = M.base_directory + .. "/telekasten.nvim/scripts/catimg-previewer" + end - if vim.startswith(M.Cfg.media_previewer, "viu-previewer") then - preview_cmd = M.base_directory - .. "/telekasten.nvim/scripts/" - .. M.Cfg.media_previewer - end + if vim.startswith(M.Cfg.media_previewer, "viu-previewer") then + preview_cmd = M.base_directory + .. "/telekasten.nvim/scripts/" + .. M.Cfg.media_previewer + end - if vim.fn.executable(preview_cmd) == 0 then - print("Previewer not found: " .. preview_cmd) - return conf.file_previewer(opts) - end - return previewers.new_termopen_previewer({ - get_command = opts.get_command - or function(entry) - local tmp_table = vim.split(entry.value, "\t") - local preview = opts.get_preview_window() - opts.cwd = opts.cwd and vim.fn.expand(opts.cwd) - or vim.loop.cwd() - if vim.tbl_isempty(tmp_table) then - return { "echo", "" } - end - print(tmp_table[1]) - return { - preview_cmd, - tmp_table[1], - preview.col, - preview.line + 1, - preview.width, - preview.height, - } - end, - }) + if vim.fn.executable(preview_cmd) == 0 then + print("Previewer not found: " .. preview_cmd) + return conf.file_previewer(opts) + end + return previewers.new_termopen_previewer({ + get_command = opts.get_command + or function(entry) + local tmp_table = vim.split(entry.value, "\t") + local preview = opts.get_preview_window() + opts.cwd = opts.cwd and vim.fn.expand(opts.cwd) + or vim.loop.cwd() + if vim.tbl_isempty(tmp_table) then + return { "echo", "" } + end + print(tmp_table[1]) + return { + preview_cmd, + tmp_table[1], + preview.col, + preview.line + 1, + preview.width, + preview.height, + } + end, + }) end, {}) -- note picker actions @@ -1030,1308 +1030,52 @@ local picker_actions = {} -- - optionally previews media (pdf, images, mp4, webm) -- - this requires the telescope-media-files.nvim extension local function find_files_sorted(opts) - opts = opts or {} - - local file_list = scan.scan_dir(opts.cwd, {}) - local filter_extensions = opts.filter_extensions or M.Cfg.filter_extensions - file_list = filter_filetypes(file_list, filter_extensions) - local sort_option = opts.sort or "filename" - if sort_option == "modified" then - table.sort(file_list, function(a, b) - return vim.fn.getftime(a) > vim.fn.getftime(b) - end) - else - table.sort(file_list, function(a, b) - return a > b - end) - end - - local counts = nil - if opts.show_link_counts then - counts = linkutils.generate_backlink_map(M.Cfg) - end - - -- display with devicons - local function iconic_display(display_entry) - local display_opts = { - path_display = function(_, e) - return e:gsub(escape(opts.cwd .. "/"), "") - end, - } - - local hl_group - local display = utils.transform_path(display_opts, display_entry.value) - - display, hl_group = - utils.transform_devicons(display_entry.value, display, false) - - if hl_group then - return display, { { { 1, 3 }, hl_group } } - else - return display - end - end - - -- for media_files - local popup_opts = {} - opts.get_preview_window = function() - return popup_opts.preview - end - - -- local width = config.width - -- or config.layout_config.width - -- or config.layout_config[config.layout_strategy].width - -- or vim.o.columns - -- local telescope_win_width - -- if width > 1 then - -- telescope_win_width = width - -- else - -- telescope_win_width = math.floor(vim.o.columns * width) - -- end - local displayer = entry_display.create({ - separator = "", - items = { - { width = 4 }, - { width = 4 }, - { remaining = true }, - }, - }) - - local function make_display(entry) - local fn = entry.value - local nlinks = counts.link_counts[fn] or 0 - local nbacks = counts.backlink_counts[fn] or 0 - - if opts.show_link_counts then - local display, hl = iconic_display(entry) - - return displayer({ - { - "L" .. tostring(nlinks), - function() - return { - { { 0, 1 }, "tkTagSep" }, - { { 1, 3 }, "tkTag" }, - } - end, - }, - { - "B" .. tostring(nbacks), - function() - return { - { { 0, 1 }, "tkTagSep" }, - { { 1, 3 }, "DevIconMd" }, - } - end, - }, - { - display, - function() - return hl - end, - }, - }) - else - return iconic_display(entry) - end - end - - local function entry_maker(entry) - local iconic_entry = {} - iconic_entry.value = entry - iconic_entry.path = entry - iconic_entry.ordinal = entry - if opts.show_link_counts then - iconic_entry.display = make_display - else - iconic_entry.display = iconic_display - end - return iconic_entry - end - - local previewer = conf.file_previewer(opts) - if opts.preview_type == "media" then - previewer = media_preview.new(opts) - end - - opts.attach_mappings = opts.attach_mappings - or function(_, _) - actions.select_default:replace(picker_actions.select_default) - end - - local picker = pickers.new(opts, { - finder = finders.new_table({ - results = file_list, - entry_maker = entry_maker, - }), - sorter = conf.generic_sorter(opts), - previewer = previewer, - }) - - -- local oc = picker.finder.close - -- - -- picker.finder.close = function() - -- print('on close') - -- print(vim.inspect(picker:get_selection())) - -- -- unfortunately, no way to tell if the selection was confirmed or - -- -- canceled out - -- oc() - -- -- alternative: attach default mappings for and - -- -- if anyone quits with q!, it's their fault - -- end - - -- for media_files: - local line_count = vim.o.lines - vim.o.cmdheight - if vim.o.laststatus ~= 0 then - line_count = line_count - 1 - end - - popup_opts = picker:get_window_options(vim.o.columns, line_count) - - picker:find() -end - -picker_actions.post_open = function() - if M.Cfg.auto_set_filetype then - vim.cmd("set ft=telekasten") - end - if M.Cfg.auto_set_syntax then - vim.cmd("set syntax=telekasten") - end -end - -picker_actions.select_default = function(prompt_bufnr) - local ret = action_set.select(prompt_bufnr, "default") - picker_actions.post_open() - return ret -end - -function picker_actions.close(opts) - opts = opts or {} - return function(prompt_bufnr) - actions.close(prompt_bufnr) - if opts.erase then - if file_exists(opts.erase_file) then - vim.fn.delete(opts.erase_file) - end - end - end -end - -function picker_actions.paste_tag(opts) - return function(prompt_bufnr) - actions.close(prompt_bufnr) - local selection = action_state.get_selected_entry() - vim.api.nvim_put({ selection.value.tag }, "", true, true) - if opts.insert_after_inserting or opts.i then - vim.api.nvim_feedkeys("A", "m", false) - end - end -end - -function picker_actions.yank_tag(opts) - return function(prompt_bufnr) opts = opts or {} - if opts.close_after_yanking then - actions.close(prompt_bufnr) - end - local selection = action_state.get_selected_entry() - vim.fn.setreg('"', selection.value.tag) - print("yanked " .. selection.value.tag) - end -end -function picker_actions.paste_link(opts) - opts = opts or {} - opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links - return function(prompt_bufnr) - actions.close(prompt_bufnr) - local selection = action_state.get_selected_entry() - local pinfo = Pinfo:new({ - filepath = selection.filename or selection.value, - opts, - }) - local title = "[[" .. pinfo.title .. "]]" - vim.api.nvim_put({ title }, "", true, true) - if opts.insert_after_inserting or opts.i then - vim.api.nvim_feedkeys("A", "m", false) - end - end -end - -function picker_actions.yank_link(opts) - return function(prompt_bufnr) - opts = opts or {} - opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links - if opts.close_after_yanking then - actions.close(prompt_bufnr) - end - local selection = action_state.get_selected_entry() - local pinfo = Pinfo:new({ - filepath = selection.filename or selection.value, - opts, - }) - local title = "[[" .. pinfo.title .. "]]" - vim.fn.setreg('"', title) - print("yanked " .. title) - end -end - -function picker_actions.paste_img_link(opts) - return function(prompt_bufnr) - actions.close(prompt_bufnr) - local selection = action_state.get_selected_entry() - local fn = selection.value - fn = make_relative_path(vim.fn.expand("%:p"), fn, "/") - local imglink = "![](" .. fn .. ")" - vim.api.nvim_put({ imglink }, "", true, true) - if opts.insert_after_inserting or opts.i then - vim.api.nvim_feedkeys("A", "m", false) - end - end -end - -function picker_actions.yank_img_link(opts) - return function(prompt_bufnr) - opts = opts or {} - if opts.close_after_yanking then - actions.close(prompt_bufnr) - end - local selection = action_state.get_selected_entry() - local fn = selection.value - fn = make_relative_path(vim.fn.expand("%:p"), fn, "/") - local imglink = "![](" .. fn .. ")" - vim.fn.setreg('"', imglink) - print("yanked " .. imglink) - end -end - --- --- FindDailyNotes: --- --------------- --- --- Select from daily notes --- -local function FindDailyNotes(opts) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - - if not global_dir_check() then - return - end - - local today = os.date(dateformats.date) - local fname = M.Cfg.dailies .. "/" .. today .. M.Cfg.extension - local fexists = file_exists(fname) - if - (fexists ~= true) - and ( - (opts.dailies_create_nonexisting == true) - or M.Cfg.dailies_create_nonexisting == true - ) - then - create_note_from_template( - today, - nil, - fname, - M.note_type_templates.daily - ) - opts.erase = true - opts.erase_file = fname - end - - find_files_sorted({ - prompt_title = "Find daily note", - cwd = M.Cfg.dailies, - find_command = M.Cfg.find_command, - attach_mappings = function(_, map) - actions.select_default:replace(picker_actions.select_default) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.close(opts)) - map("n", "", picker_actions.close(opts)) - return true - end, - sort = M.Cfg.sort, - }) -end - --- --- FindWeeklyNotes: --- --------------- --- --- Select from daily notes --- -local function FindWeeklyNotes(opts) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - - if not global_dir_check() then - return - end - - local title = os.date(dateformats.isoweek) - local fname = M.Cfg.weeklies .. "/" .. title .. M.Cfg.extension - local fexists = file_exists(fname) - if - (fexists ~= true) - and ( - (opts.weeklies_create_nonexisting == true) - or M.Cfg.weeklies_create_nonexisting == true - ) - then - create_note_from_template( - title, - nil, - fname, - M.note_type_templates.weekly - ) - opts.erase = true - opts.erase_file = fname - end - - find_files_sorted({ - prompt_title = "Find weekly note", - cwd = M.Cfg.weeklies, - find_command = M.Cfg.find_command, - attach_mappings = function(_, map) - actions.select_default:replace(picker_actions.select_default) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.close(opts)) - map("n", "", picker_actions.close(opts)) - return true - end, - sort = M.Cfg.sort, - }) -end - --- --- InsertLink: --- ----------- --- --- Select from all notes and put a link in the current buffer --- -local function InsertLink(opts) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links - - if not global_dir_check() then - return - end - - local cwd = M.Cfg.home - local find_command = M.Cfg.find_command - local sort = M.Cfg.sort - local attach_mappings = function(prompt_bufnr, map) - actions.select_default:replace(function() - actions.close(prompt_bufnr) - local selection = action_state.get_selected_entry() - local pinfo = Pinfo:new({ - filepath = selection.filename or selection.value, - opts, - }) - vim.api.nvim_put({ "[[" .. pinfo.title .. "]]" }, "", false, true) - if opts.i then - vim.api.nvim_feedkeys("a", "m", false) - end - end) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - return true - end - - if opts.with_live_grep then - builtin.live_grep({ - prompt_title = "Insert link to note with live grep", - cwd = cwd, - attach_mappings = attach_mappings, - find_command = find_command, - sort = sort, - }) - else - find_files_sorted({ - prompt_title = "Insert link to note", - cwd = cwd, - attach_mappings = attach_mappings, - find_command = find_command, - sort = sort, - }) - end -end - --- local function check_for_link_or_tag() -local function check_for_link_or_tag() - local line = vim.api.nvim_get_current_line() - local col = vim.fn.col(".") - return taglinks.is_tag_or_link_at(line, col, M.Cfg) -end - -local function follow_url(url) - if M.Cfg.follow_url_fallback then - local cmd = string.gsub(M.Cfg.follow_url_fallback, "{{url}}", url) - return vim.cmd(cmd) - end - - -- we just leave it to the OS's handler to deal with what kind of URL it is - local function format_command(cmd) - return 'call jobstart(["' - .. cmd - .. '", "' - .. url - .. '"], {"detach": v:true})' - end - - local command - if vim.fn.has("mac") == 1 then - command = format_command("open") - vim.cmd(command) - elseif vim.fn.has("unix") then - command = format_command("xdg-open") - vim.cmd(command) - else - print("Cannot open URLs on your operating system") - end -end - --- --- PreviewImg: --- ----------- --- --- preview media --- -local function PreviewImg(opts) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - - if not global_dir_check() then - return - end - - vim.cmd("normal yi)") - local fname = vim.fn.getreg('"0'):gsub("^img/", "") - - -- check if fname exists anywhere - local imageDir = M.Cfg.image_subdir or M.Cfg.home - local fexists = file_exists(imageDir .. "/" .. fname) - - if fexists == true then - find_files_sorted({ - prompt_title = "Preview image/media", - cwd = imageDir, - default_text = fname, - find_command = M.Cfg.find_command, - filter_extensions = { - ".png", - ".jpg", - ".bmp", - ".gif", - ".pdf", - ".mp4", - ".webm", - }, - preview_type = "media", - attach_mappings = function(prompt_bufnr, map) - actions.select_default:replace(function() - actions.close(prompt_bufnr) + local file_list = scan.scan_dir(opts.cwd, {}) + local filter_extensions = opts.filter_extensions or M.Cfg.filter_extensions + file_list = filter_filetypes(file_list, filter_extensions) + local sort_option = opts.sort or "filename" + if sort_option == "modified" then + table.sort(file_list, function(a, b) + return vim.fn.getftime(a) > vim.fn.getftime(b) end) - map("i", "", picker_actions.yank_img_link(opts)) - map("i", "", picker_actions.paste_img_link(opts)) - map("n", "", picker_actions.yank_img_link(opts)) - map("n", "", picker_actions.paste_img_link(opts)) - map("i", "", picker_actions.paste_img_link(opts)) - map("n", "", picker_actions.paste_img_link(opts)) - return true - end, - sort = M.Cfg.sort, - }) - else - print("File not found: " .. M.Cfg.home .. "/" .. fname) - end -end - --- --- BrowseImg: --- ----------- --- --- preview media --- -local function BrowseImg(opts) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - - if not global_dir_check() then - return - end - - find_files_sorted({ - prompt_title = "Preview image/media", - cwd = M.Cfg.home, - find_command = M.Cfg.find_command, - filter_extensions = { - ".png", - ".jpg", - ".bmp", - ".gif", - ".pdf", - ".mp4", - ".webm", - }, - preview_type = "media", - attach_mappings = function(prompt_bufnr, map) - actions.select_default:replace(function() - actions.close(prompt_bufnr) - end) - map("i", "", picker_actions.yank_img_link(opts)) - map("i", "", picker_actions.paste_img_link(opts)) - map("n", "", picker_actions.yank_img_link(opts)) - map("n", "", picker_actions.paste_img_link(opts)) - map("i", "", picker_actions.paste_img_link(opts)) - map("n", "", picker_actions.paste_img_link(opts)) - return true - end, - sort = M.Cfg.sort, - }) -end - --- --- FindFriends: --- ----------- --- --- Find notes also linking to the link under cursor --- -local function FindFriends(opts) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - - if not global_dir_check() then - return - end - - vim.cmd("normal yi]") - local title = vim.fn.getreg('"0') - title = remove_alias(title) - title = title:gsub("^(%[)(.+)(%])$", "%2") - - builtin.live_grep({ - prompt_title = "Notes referencing `" .. title .. "`", - cwd = M.Cfg.home, - default_text = "\\[\\[" .. title .. "([#|].+)?\\]\\]", - find_command = M.Cfg.find_command, - attach_mappings = function(_, map) - actions.select_default:replace(picker_actions.select_default) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - return true - end, - }) -end - --- --- YankLink: --- ----------- --- --- Create and yank a [[link]] from the current note. --- -local function YankLink() - local title = "[[" - .. Pinfo:new({ filepath = vim.fn.expand("%:p"), M.Cfg }).title - .. "]]" - vim.fn.setreg('"', title) - print("yanked " .. title) -end - --- --- RenameNote: --- ----------- --- --- Prompt for new note title, rename the note and update all links. --- -local function RenameNote() - local oldfile = Pinfo:new({ filepath = vim.fn.expand("%:p"), M.Cfg }) - - tkutils.prompt_title(M.Cfg.extension, oldfile.title, function(newname) - local newpath = newname:match("(.*/)") or "" - newpath = M.Cfg.home .. "/" .. newpath - - -- If no subdir specified, place the new note in the same place as old note - if - M.Cfg.subdirs_in_links == true - and newpath == M.Cfg.home .. "/" - and oldfile.sub_dir ~= "" - then - newname = oldfile.sub_dir .. "/" .. newname - end - - local fname = M.Cfg.home .. "/" .. newname .. M.Cfg.extension - local fexists = file_exists(fname) - if fexists then - print_error("File alreay exists. Renaming abandonned") - return - end - - -- Savas newfile, delete buffer of old one and remove old file - if newname ~= "" and newname ~= oldfile.title then - if not (check_dir_and_ask(newpath, "Renamed file")) then - return - end - - local oldTitle = oldfile.title:gsub(" ", "\\ ") - vim.cmd( - "saveas " .. M.Cfg.home .. "/" .. newname .. M.Cfg.extension - ) - vim.cmd("bdelete " .. oldTitle .. M.Cfg.extension) - os.execute( - "rm " .. M.Cfg.home .. "/" .. oldTitle .. M.Cfg.extension - ) - end - - if M.Cfg.rename_update_links == true then - -- Only look for the first part of the link, so we do not touch to #heading or #^paragraph - -- Should use regex instead to ensure it is a proper link - local oldlink = "[[" .. oldfile.title - local newlink = "[[" .. newname - - -- Save open buffers before looking for links to replace - if #(vim.fn.getbufinfo({ bufmodified = 1 })) > 1 then - vim.ui.select({ "Yes (default)", "No" }, { - prompt = "Telekasten.nvim: " - .. "Save all modified buffers before updating links?", - }, function(answer) - if answer ~= "No" then - save_all_mod_buffers() - end - end) - end - - recursive_substitution(M.Cfg.home, oldlink, newlink) - recursive_substitution(M.Cfg.dailies, oldlink, newlink) - recursive_substitution(M.Cfg.weeklies, oldlink, newlink) - end - end) -end - --- --- GotoDate: --- ---------- --- --- find note for date and create it if necessary. --- -local function GotoDate(opts) - opts.dates = calculate_dates(opts.date_table) - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - opts.journal_auto_open = opts.journal_auto_open or M.Cfg.journal_auto_open - - local word = opts.date or os.date(dateformats.date) - - local fname = M.Cfg.dailies .. "/" .. word .. M.Cfg.extension - local fexists = file_exists(fname) - if - (fexists ~= true) - and ( - (opts.dailies_create_nonexisting == true) - or M.Cfg.dailies_create_nonexisting == true - ) - then - create_note_from_template( - word, - nil, - fname, - M.note_type_templates.daily, - opts.dates - ) - opts.erase = true - opts.erase_file = fname - end - - if opts.journal_auto_open then - vim.cmd("e " .. fname) - else - find_files_sorted({ - prompt_title = "Goto day", - cwd = M.Cfg.dailies, - default_text = word, - find_command = M.Cfg.find_command, - attach_mappings = function(prompt_bufnr, map) - actions.select_default:replace(function() - actions.close(prompt_bufnr) - - -- open the new note - if opts.calendar == true then - vim.cmd("wincmd w") - end - vim.cmd("e " .. fname) - picker_actions.post_open() - end) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.close(opts)) - map("n", "", picker_actions.close(opts)) - return true - end, - }) - end -end - --- --- GotoToday: --- ---------- --- --- find today's daily note and create it if necessary. --- -local function GotoToday(opts) - opts = opts or {} - - if not global_dir_check() then - return - end - - local today = os.date(dateformats.date) - opts.date_table = os.date("*t") - opts.date = today - opts.dailies_create_nonexisting = true -- Always use template for GotoToday - GotoDate(opts) -end - --- --- FindNotes: --- ---------- --- --- Select from notes --- -local function FindNotes(opts) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - - if not global_dir_check() then - return - end - - local cwd = M.Cfg.home - local find_command = M.Cfg.find_command - local sort = M.Cfg.sort - local attach_mappings = function(_, map) - actions.select_default:replace(picker_actions.select_default) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("i", "", picker_actions.create_new(opts)) - map("n", "", picker_actions.create_new(opts)) - return true - end - - if opts.with_live_grep then - builtin.live_grep({ - prompt_title = "Find notes by live grep", - cwd = cwd, - find_command = find_command, - attach_mappings = attach_mappings, - sort = sort, - }) - else - find_files_sorted({ - prompt_title = "Find notes by name", - cwd = cwd, - find_command = find_command, - attach_mappings = attach_mappings, - sort = sort, - }) - end -end - --- --- InsertImgLink: --- -------------- --- --- Insert link to image / media, with optional preview --- -local function InsertImgLink(opts) - opts = opts or {} - - if not global_dir_check() then - return - end - - find_files_sorted({ - prompt_title = "Find image/media", - cwd = M.Cfg.home, - find_command = M.Cfg.find_command, - filter_extensions = { - ".png", - ".jpg", - ".bmp", - ".gif", - ".pdf", - ".mp4", - ".webm", - }, - preview_type = "media", - attach_mappings = function(prompt_bufnr, map) - actions.select_default:replace(function() - actions.close(prompt_bufnr) - local selection = action_state.get_selected_entry() - local fn = selection.value - fn = make_relative_path(vim.fn.expand("%:p"), fn, "/") - vim.api.nvim_put({ "![](" .. fn .. ")" }, "", true, true) - if opts.i then - vim.api.nvim_feedkeys("A", "m", false) - end - end) - map("i", "", picker_actions.yank_img_link(opts)) - map("i", "", picker_actions.paste_img_link(opts)) - map("n", "", picker_actions.yank_img_link(opts)) - map("n", "", picker_actions.paste_img_link(opts)) - map("i", "", picker_actions.paste_img_link(opts)) - map("n", "", picker_actions.paste_img_link(opts)) - return true - end, - sort = M.Cfg.sort, - }) -end - --- --- SearchNotes: --- ------------ --- --- find the file linked to by the word under the cursor --- -local function SearchNotes(opts) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - - if not global_dir_check() then - return - end - - builtin.live_grep({ - prompt_title = "Search in notes", - cwd = M.Cfg.home, - search_dirs = { M.Cfg.home }, - default_text = opts.default_text or vim.fn.expand(""), - find_command = M.Cfg.find_command, - attach_mappings = function(_, map) - actions.select_default:replace(picker_actions.select_default) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - return true - end, - }) -end - --- --- ShowBacklinks: --- ------------ --- --- Find all notes linking to this one --- -local function ShowBacklinks(opts) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - - if not global_dir_check() then - return - end - - local title = Pinfo:new({ filepath = vim.fn.expand("%:p"), M.Cfg }).title - -- or vim.api.nvim_buf_get_name(0) - builtin.live_grep({ - results_title = "Backlinks to " .. title, - prompt_title = "Search", - cwd = M.Cfg.home, - search_dirs = { M.Cfg.home }, - default_text = "\\[\\[" .. title .. "([#|].+)?\\]\\]", - find_command = M.Cfg.find_command, - attach_mappings = function(_, map) - actions.select_default:replace(picker_actions.select_default) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - return true - end, - }) -end - --- --- CreateNoteSelectTemplate() --- -------------------------- --- --- Prompts for title, then pops up telescope for template selection, --- creates the new note by template and opens it - -local function on_create_with_template(opts, title) - if title == nil then - return - end - - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - opts.new_note_location = opts.new_note_location or M.Cfg.new_note_location - opts.template_handling = opts.template_handling or M.Cfg.template_handling - - local uuid = get_uuid(opts) - local pinfo = Pinfo:new({ - title = generate_note_filename(uuid, title), - opts, - }) - local fname = pinfo.filepath - if pinfo.fexists == true then - -- open the new note - vim.cmd("e " .. fname) - picker_actions.post_open() - return - end - - find_files_sorted({ - prompt_title = "Select template...", - cwd = M.Cfg.templates, - find_command = M.Cfg.find_command, - attach_mappings = function(prompt_bufnr, map) - actions.select_default:replace(function() - actions.close(prompt_bufnr) - -- local template = M.Cfg.templates .. "/" .. action_state.get_selected_entry().value - local template = action_state.get_selected_entry().value - -- TODO: pass in the calendar_info returned from the pinfo - create_note_from_template( - title, - uuid, - fname, - template, - pinfo.calendar_info - ) - -- open the new note - vim.cmd("e " .. fname) - picker_actions.post_open() - end) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - return true - end, - }) -end - -local function CreateNoteSelectTemplate(opts) - opts = opts or {} - - if not global_dir_check() then - return - end - - tkutils.prompt_title(M.Cfg.extension, nil, function(title) - on_create_with_template(opts, title) - end) -end - --- --- CreateNote: --- ------------ --- --- Prompts for title and creates note with default template --- -local function on_create(opts, title) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - opts.new_note_location = opts.new_note_location or M.Cfg.new_note_location - opts.template_handling = opts.template_handling or M.Cfg.template_handling - - if title == nil then - return - end - - local uuid = get_uuid(opts) - local pinfo = Pinfo:new({ - title = generate_note_filename(uuid, title), - opts, - }) - local fname = pinfo.filepath - - if pinfo.fexists ~= true then - -- TODO: pass in the calendar_info returned in pinfo - create_note_from_template( - title, - uuid, - fname, - pinfo.template, - pinfo.calendar_info - ) - opts.erase = true - opts.erase_file = fname - end - - find_files_sorted({ - prompt_title = "Created note...", - cwd = pinfo.root_dir, - default_text = generate_note_filename(uuid, title), - find_command = M.Cfg.find_command, - attach_mappings = function(_, map) - actions.select_default:replace(picker_actions.select_default) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.close(opts)) - map("n", "", picker_actions.close(opts)) - return true - end, - }) -end - -local function CreateNote(opts) - opts = opts or {} - - if not global_dir_check() then - return - end - - if M.Cfg.template_handling == "always_ask" then - return CreateNoteSelectTemplate(opts) - end - - tkutils.prompt_title(M.Cfg.extension, nil, function(title) - on_create(opts, title) - end) -end - --- --- FollowLink: --- ----------- --- --- find the file linked to by the word under the cursor --- -local function FollowLink(opts) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - - opts.template_handling = opts.template_handling or M.Cfg.template_handling - opts.new_note_location = opts.new_note_location or M.Cfg.new_note_location - - if not global_dir_check() then - return - end - - local search_mode = "files" - local title - local filename_part = "" - - -- first: check if we're in a tag or a link - local kind, atcol, tag - local globArg = "" - - if opts.follow_tag ~= nil then - kind = "tag" - tag = opts.follow_tag - if opts.templateDir ~= nil then - globArg = "--glob=!" .. "**/" .. opts.templateDir .. "/*.md" - end - else - kind, atcol = check_for_link_or_tag() - end - - if kind == "tag" then - if atcol ~= nil then - tag = taglinks.get_tag_at( - vim.api.nvim_get_current_line(), - atcol, - M.Cfg - ) - end - search_mode = "tag" - title = tag - else - if kind == "link" then - -- we are in a link - vim.cmd("normal yi]") - title = vim.fn.getreg('"0') - title = title:gsub("^(%[)(.+)(%])$", "%2") - title = remove_alias(title) else - -- we are in an external [link] - vim.cmd("normal yi)") - local url = vim.fn.getreg('"0') - return follow_url(url) + table.sort(file_list, function(a, b) + return a > b + end) end - local parts = vim.split(title, "#") - - -- if there is a # - if #parts ~= 1 then - search_mode = "heading" - title = parts[2] - filename_part = parts[1] - parts = vim.split(title, "%^") - if #parts ~= 1 then - search_mode = "para" - title = parts[2] - end - end - - -- this applies to heading and para search_mode - -- if we cannot find the file, revert to global heading search by - -- setting filename to empty string - if #filename_part > 0 then - local pinfo = Pinfo:new({ title = filename_part }) - filename_part = pinfo.filepath - if pinfo.fexists == false then - -- print("error") - filename_part = "" - end - end - end - - if search_mode == "files" then - -- check if subdir exists - local filepath = title:match("(.*/)") or "" - filepath = M.Cfg.home .. "/" .. filepath - check_dir_and_ask(filepath, "") - - -- check if fname exists anywhere - local pinfo = Pinfo:new({ title = title }) - if - (pinfo.fexists ~= true) - and ( - (opts.follow_creates_nonexisting == true) - or M.Cfg.follow_creates_nonexisting == true - ) - then - if opts.template_handling == "always_ask" then - return on_create_with_template(opts, title) - end - - if #pinfo.filepath > 0 then - local uuid = get_uuid(opts) - create_note_from_template( - title, - uuid, - pinfo.filepath, - pinfo.template, - pinfo.calendar_info - ) - opts.erase = true - opts.erase_file = pinfo.filepath - end - end - - find_files_sorted({ - prompt_title = "Follow link to note...", - cwd = pinfo.root_dir, - default_text = title, - find_command = M.Cfg.find_command, - attach_mappings = function(_, map) - actions.select_default:replace(picker_actions.select_default) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.close(opts)) - map("n", "", picker_actions.close(opts)) - return true - end, - sort = M.Cfg.sort, - }) - end - - if search_mode ~= "files" then - local search_pattern = title - local cwd = M.Cfg.home - - opts.cwd = cwd local counts = nil if opts.show_link_counts then - counts = linkutils.generate_backlink_map(M.Cfg) + counts = linkutils.generate_backlink_map(M.Cfg) end -- display with devicons local function iconic_display(display_entry) - local display_opts = { - path_display = function(_, e) - return e:gsub(escape(opts.cwd .. "/"), "") - end, - } + local display_opts = { + path_display = function(_, e) + return e:gsub(escape(opts.cwd .. "/"), "") + end, + } - local hl_group - local display = - utils.transform_path(display_opts, display_entry.value) + local hl_group + local display = utils.transform_path(display_opts, display_entry.value) - display_entry.filn = display_entry.filn or display:gsub(":.*", "") - display, hl_group = - utils.transform_devicons(display_entry.filn, display, false) + display, hl_group = + utils.transform_devicons(display_entry.value, display, false) - if hl_group then - return display, { { { 1, 3 }, hl_group } } - else - return display - end + if hl_group then + return display, { { { 1, 3 }, hl_group } } + else + return display + end end -- for media_files local popup_opts = {} opts.get_preview_window = function() - return popup_opts.preview + return popup_opts.preview end -- local width = config.width @@ -2345,240 +1089,367 @@ local function FollowLink(opts) -- telescope_win_width = math.floor(vim.o.columns * width) -- end local displayer = entry_display.create({ - separator = "", - items = { - { width = 4 }, - { width = 4 }, - { remaining = true }, - }, + separator = "", + items = { + { width = 4 }, + { width = 4 }, + { remaining = true }, + }, }) local function make_display(entry) - local fn = entry.filename - local nlinks = counts.link_counts[fn] or 0 - local nbacks = counts.backlink_counts[fn] or 0 + local fn = entry.value + local nlinks = counts.link_counts[fn] or 0 + local nbacks = counts.backlink_counts[fn] or 0 - if opts.show_link_counts then - local display, hl = iconic_display(entry) + if opts.show_link_counts then + local display, hl = iconic_display(entry) - return displayer({ - { - "L" .. tostring(nlinks), - function() - return { - { { 0, 1 }, "tkTagSep" }, - { { 1, 3 }, "tkTag" }, - } - end, - }, - { - "B" .. tostring(nbacks), - function() - return { - { { 0, 1 }, "tkTagSep" }, - { { 1, 3 }, "DevIconMd" }, - } - end, - }, - { - display, - function() - return hl - end, - }, - }) - else - return iconic_display(entry) - end - end - - local lookup_keys = { - value = 1, - ordinal = 1, - } - - local find = (function() - if Path.path.sep == "\\" then - return function(t) - local start, _, filn, lnum, col, text = - string.find(t, [[([^:]+):(%d+):(%d+):(.*)]]) - - -- Handle Windows drive letter (e.g. "C:") at the beginning (if present) - if start == 3 then - filn = string.sub(t.value, 1, 3) .. filn - end - - return filn, lnum, col, text - end - else - return function(t) - local _, _, filn, lnum, col, text = - string.find(t, [[([^:]+):(%d+):(%d+):(.*)]]) - return filn, lnum, col, text - end - end - end)() - - local parse = function(t) - -- print("t: ", vim.inspect(t)) - local filn, lnum, col, text = find(t.value) - - local ok - ok, lnum = pcall(tonumber, lnum) - if not ok then - lnum = nil - end - - ok, col = pcall(tonumber, col) - if not ok then - col = nil - end - - t.filn = filn - t.lnum = lnum - t.col = col - t.text = text - - return { filn, lnum, col, text } - end - - local function entry_maker(_) - local mt_vimgrep_entry - - opts = opts or {} - - -- local disable_devicons = opts.disable_devicons - -- local disable_coordinates = opts.disable_coordinates or true - local only_sort_text = opts.only_sort_text - - local execute_keys = { - path = function(t) - if Path:new(t.filename):is_absolute() then - return t.filename, false - else - return Path:new({ t.cwd, t.filename }):absolute(), false - end - end, - filename = function(t) - return parse(t)[1], true - end, - lnum = function(t) - return parse(t)[2], true - end, - col = function(t) - return parse(t)[3], true - end, - text = function(t) - return parse(t)[4], true - end, - } - - -- For text search only, the ordinal value is actually the text. - if only_sort_text then - execute_keys.ordinal = function(t) - return t.text - end - end - - mt_vimgrep_entry = { - cwd = vim.fn.expand(opts.cwd or vim.loop.cwd()), - __index = function(t, k) - local raw = rawget(mt_vimgrep_entry, k) - if raw then - return raw - end - - local executor = rawget(execute_keys, k) - if executor then - local val, save = executor(t) - if save then - rawset(t, k, val) - end - return val - end - - return rawget(t, rawget(lookup_keys, k)) - end, - } - - -- - if opts.show_link_counts then - mt_vimgrep_entry.display = make_display - else - mt_vimgrep_entry.display = iconic_display - end - - return function(line) - return setmetatable({ line }, mt_vimgrep_entry) - end - end - - opts.entry_maker = entry_maker(opts) - - local live_grepper = finders.new_job( - function(prompt) - if not prompt or prompt == "" then - return nil - end - - local search_command = { - "rg", - "--vimgrep", - "-e", - "^#+\\s" .. prompt, - "--", - } - if search_mode == "para" then - search_command = { - "rg", - "--vimgrep", - "-e", - "\\^" .. prompt, - "--", - } - end - - if search_mode == "tag" then - search_command = { - "rg", - "--vimgrep", - globArg, - "-e", - prompt, - "--", - } - end - - if #filename_part > 0 then - table.insert(search_command, filename_part) + return displayer({ + { + "L" .. tostring(nlinks), + function() + return { + { { 0, 1 }, "tkTagSep" }, + { { 1, 3 }, "tkTag" }, + } + end, + }, + { + "B" .. tostring(nbacks), + function() + return { + { { 0, 1 }, "tkTagSep" }, + { { 1, 3 }, "DevIconMd" }, + } + end, + }, + { + display, + function() + return hl + end, + }, + }) else - table.insert(search_command, cwd) + return iconic_display(entry) + end + end + + local function entry_maker(entry) + local iconic_entry = {} + iconic_entry.value = entry + iconic_entry.path = entry + iconic_entry.ordinal = entry + if opts.show_link_counts then + iconic_entry.display = make_display + else + iconic_entry.display = iconic_display + end + return iconic_entry + end + + local previewer = conf.file_previewer(opts) + if opts.preview_type == "media" then + previewer = media_preview.new(opts) + end + + opts.attach_mappings = opts.attach_mappings + or function(_, _) + actions.select_default:replace(picker_actions.select_default) end - local ret = vim.tbl_flatten({ search_command }) - return ret - end, - opts.entry_maker or make_entry.gen_from_vimgrep(opts), - opts.max_results, - opts.cwd - ) + local picker = pickers.new(opts, { + finder = finders.new_table({ + results = file_list, + entry_maker = entry_maker, + }), + sorter = conf.generic_sorter(opts), + previewer = previewer, + }) - -- builtin.live_grep({ - local picker = pickers.new({ - cwd = cwd, - prompt_title = "Notes referencing `" .. title .. "`", - default_text = search_pattern, - initial_mode = "insert", - -- link to specific file (a daily file): [[2021-02-22]] - -- link to heading in specific file (a daily file): [[2021-02-22#Touchpoint]] - -- link to heading globally [[#Touchpoint]] - -- link to heading in specific file (a daily file): [[The cool note#^xAcSh-xxr]] - -- link to paragraph globally [[#^xAcSh-xxr]] - finder = live_grepper, - previewer = conf.grep_previewer(opts), - sorter = sorters.highlighter_only(opts), - attach_mappings = function(_, map) - actions.select_default:replace(picker_actions.select_default) + -- local oc = picker.finder.close + -- + -- picker.finder.close = function() + -- print('on close') + -- print(vim.inspect(picker:get_selection())) + -- -- unfortunately, no way to tell if the selection was confirmed or + -- -- canceled out + -- oc() + -- -- alternative: attach default mappings for and + -- -- if anyone quits with q!, it's their fault + -- end + + -- for media_files: + local line_count = vim.o.lines - vim.o.cmdheight + if vim.o.laststatus ~= 0 then + line_count = line_count - 1 + end + + popup_opts = picker:get_window_options(vim.o.columns, line_count) + + picker:find() +end + +picker_actions.post_open = function() + if M.Cfg.auto_set_filetype then + vim.cmd("set ft=telekasten") + end + if M.Cfg.auto_set_syntax then + vim.cmd("set syntax=telekasten") + end +end + +picker_actions.select_default = function(prompt_bufnr) + local ret = action_set.select(prompt_bufnr, "default") + picker_actions.post_open() + return ret +end + +function picker_actions.close(opts) + opts = opts or {} + return function(prompt_bufnr) + actions.close(prompt_bufnr) + if opts.erase then + if file_exists(opts.erase_file) then + vim.fn.delete(opts.erase_file) + end + end + end +end + +function picker_actions.paste_tag(opts) + return function(prompt_bufnr) + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + vim.api.nvim_put({ selection.value.tag }, "", true, true) + if opts.insert_after_inserting or opts.i then + vim.api.nvim_feedkeys("A", "m", false) + end + end +end + +function picker_actions.yank_tag(opts) + return function(prompt_bufnr) + opts = opts or {} + if opts.close_after_yanking then + actions.close(prompt_bufnr) + end + local selection = action_state.get_selected_entry() + vim.fn.setreg('"', selection.value.tag) + print("yanked " .. selection.value.tag) + end +end + +function picker_actions.paste_link(opts) + opts = opts or {} + opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links + return function(prompt_bufnr) + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + local pinfo = Pinfo:new({ + filepath = selection.filename or selection.value, + opts, + }) + local title = "[[" .. pinfo.title .. "]]" + vim.api.nvim_put({ title }, "", true, true) + if opts.insert_after_inserting or opts.i then + vim.api.nvim_feedkeys("A", "m", false) + end + end +end + +function picker_actions.yank_link(opts) + return function(prompt_bufnr) + opts = opts or {} + opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links + if opts.close_after_yanking then + actions.close(prompt_bufnr) + end + local selection = action_state.get_selected_entry() + local pinfo = Pinfo:new({ + filepath = selection.filename or selection.value, + opts, + }) + local title = "[[" .. pinfo.title .. "]]" + vim.fn.setreg('"', title) + print("yanked " .. title) + end +end + +function picker_actions.paste_img_link(opts) + return function(prompt_bufnr) + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + local fn = selection.value + fn = make_relative_path(vim.fn.expand("%:p"), fn, "/") + local imglink = "![](" .. fn .. ")" + vim.api.nvim_put({ imglink }, "", true, true) + if opts.insert_after_inserting or opts.i then + vim.api.nvim_feedkeys("A", "m", false) + end + end +end + +function picker_actions.yank_img_link(opts) + return function(prompt_bufnr) + opts = opts or {} + if opts.close_after_yanking then + actions.close(prompt_bufnr) + end + local selection = action_state.get_selected_entry() + local fn = selection.value + fn = make_relative_path(vim.fn.expand("%:p"), fn, "/") + local imglink = "![](" .. fn .. ")" + vim.fn.setreg('"', imglink) + print("yanked " .. imglink) + end +end + +-- +-- FindDailyNotes: +-- --------------- +-- +-- Select from daily notes +-- +local function FindDailyNotes(opts) + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + + if not global_dir_check() then + return + end + + local today = os.date(dateformats.date) + local fname = M.Cfg.dailies .. "/" .. today .. M.Cfg.extension + local fexists = file_exists(fname) + if + (fexists ~= true) + and ( + (opts.dailies_create_nonexisting == true) + or M.Cfg.dailies_create_nonexisting == true + ) + then + create_note_from_template( + today, + nil, + fname, + M.note_type_templates.daily + ) + opts.erase = true + opts.erase_file = fname + end + + find_files_sorted({ + prompt_title = "Find daily note", + cwd = M.Cfg.dailies, + find_command = M.Cfg.find_command, + attach_mappings = function(_, map) + actions.select_default:replace(picker_actions.select_default) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.close(opts)) + map("n", "", picker_actions.close(opts)) + return true + end, + sort = M.Cfg.sort, + }) +end + +-- +-- FindWeeklyNotes: +-- --------------- +-- +-- Select from daily notes +-- +local function FindWeeklyNotes(opts) + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + + if not global_dir_check() then + return + end + + local title = os.date(dateformats.isoweek) + local fname = M.Cfg.weeklies .. "/" .. title .. M.Cfg.extension + local fexists = file_exists(fname) + if + (fexists ~= true) + and ( + (opts.weeklies_create_nonexisting == true) + or M.Cfg.weeklies_create_nonexisting == true + ) + then + create_note_from_template( + title, + nil, + fname, + M.note_type_templates.weekly + ) + opts.erase = true + opts.erase_file = fname + end + + find_files_sorted({ + prompt_title = "Find weekly note", + cwd = M.Cfg.weeklies, + find_command = M.Cfg.find_command, + attach_mappings = function(_, map) + actions.select_default:replace(picker_actions.select_default) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.close(opts)) + map("n", "", picker_actions.close(opts)) + return true + end, + sort = M.Cfg.sort, + }) +end + +-- +-- InsertLink: +-- ----------- +-- +-- Select from all notes and put a link in the current buffer +-- +local function InsertLink(opts) + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links + + if not global_dir_check() then + return + end + + local cwd = M.Cfg.home + local find_command = M.Cfg.find_command + local sort = M.Cfg.sort + local attach_mappings = function(prompt_bufnr, map) + actions.select_default:replace(function() + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + local pinfo = Pinfo:new({ + filepath = selection.filename or selection.value, + opts, + }) + vim.api.nvim_put({ "[[" .. pinfo.title .. "]]" }, "", false, true) + if opts.i then + vim.api.nvim_feedkeys("a", "m", false) + end + end) map("i", "", picker_actions.yank_link(opts)) map("i", "", picker_actions.paste_link(opts)) map("n", "", picker_actions.yank_link(opts)) @@ -2586,10 +1457,1139 @@ local function FollowLink(opts) map("i", "", picker_actions.paste_link(opts)) map("n", "", picker_actions.paste_link(opts)) return true - end, + end + + if opts.with_live_grep then + builtin.live_grep({ + prompt_title = "Insert link to note with live grep", + cwd = cwd, + attach_mappings = attach_mappings, + find_command = find_command, + sort = sort, + }) + else + find_files_sorted({ + prompt_title = "Insert link to note", + cwd = cwd, + attach_mappings = attach_mappings, + find_command = find_command, + sort = sort, + }) + end +end + +-- local function check_for_link_or_tag() +local function check_for_link_or_tag() + local line = vim.api.nvim_get_current_line() + local col = vim.fn.col(".") + return taglinks.is_tag_or_link_at(line, col, M.Cfg) +end + +local function follow_url(url) + if M.Cfg.follow_url_fallback then + local cmd = string.gsub(M.Cfg.follow_url_fallback, "{{url}}", url) + return vim.cmd(cmd) + end + + -- we just leave it to the OS's handler to deal with what kind of URL it is + local function format_command(cmd) + return 'call jobstart(["' + .. cmd + .. '", "' + .. url + .. '"], {"detach": v:true})' + end + + local command + if vim.fn.has("mac") == 1 then + command = format_command("open") + vim.cmd(command) + elseif vim.fn.has("unix") then + command = format_command("xdg-open") + vim.cmd(command) + else + print("Cannot open URLs on your operating system") + end +end + +-- +-- PreviewImg: +-- ----------- +-- +-- preview media +-- +local function PreviewImg(opts) + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + + if not global_dir_check() then + return + end + + vim.cmd("normal yi)") + local fname = vim.fn.getreg('"0'):gsub("^img/", "") + + -- check if fname exists anywhere + local imageDir = M.Cfg.image_subdir or M.Cfg.home + local fexists = file_exists(imageDir .. "/" .. fname) + + if fexists == true then + find_files_sorted({ + prompt_title = "Preview image/media", + cwd = imageDir, + default_text = fname, + find_command = M.Cfg.find_command, + filter_extensions = { + ".png", + ".jpg", + ".bmp", + ".gif", + ".pdf", + ".mp4", + ".webm", + }, + preview_type = "media", + attach_mappings = function(prompt_bufnr, map) + actions.select_default:replace(function() + actions.close(prompt_bufnr) + end) + map("i", "", picker_actions.yank_img_link(opts)) + map("i", "", picker_actions.paste_img_link(opts)) + map("n", "", picker_actions.yank_img_link(opts)) + map("n", "", picker_actions.paste_img_link(opts)) + map("i", "", picker_actions.paste_img_link(opts)) + map("n", "", picker_actions.paste_img_link(opts)) + return true + end, + sort = M.Cfg.sort, + }) + else + print("File not found: " .. M.Cfg.home .. "/" .. fname) + end +end + +-- +-- BrowseImg: +-- ----------- +-- +-- preview media +-- +local function BrowseImg(opts) + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + + if not global_dir_check() then + return + end + + find_files_sorted({ + prompt_title = "Preview image/media", + cwd = M.Cfg.home, + find_command = M.Cfg.find_command, + filter_extensions = { + ".png", + ".jpg", + ".bmp", + ".gif", + ".pdf", + ".mp4", + ".webm", + }, + preview_type = "media", + attach_mappings = function(prompt_bufnr, map) + actions.select_default:replace(function() + actions.close(prompt_bufnr) + end) + map("i", "", picker_actions.yank_img_link(opts)) + map("i", "", picker_actions.paste_img_link(opts)) + map("n", "", picker_actions.yank_img_link(opts)) + map("n", "", picker_actions.paste_img_link(opts)) + map("i", "", picker_actions.paste_img_link(opts)) + map("n", "", picker_actions.paste_img_link(opts)) + return true + end, + sort = M.Cfg.sort, }) - picker:find() - end +end + +-- +-- FindFriends: +-- ----------- +-- +-- Find notes also linking to the link under cursor +-- +local function FindFriends(opts) + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + + if not global_dir_check() then + return + end + + vim.cmd("normal yi]") + local title = vim.fn.getreg('"0') + title = remove_alias(title) + title = title:gsub("^(%[)(.+)(%])$", "%2") + + builtin.live_grep({ + prompt_title = "Notes referencing `" .. title .. "`", + cwd = M.Cfg.home, + default_text = "\\[\\[" .. title .. "([#|].+)?\\]\\]", + find_command = M.Cfg.find_command, + attach_mappings = function(_, map) + actions.select_default:replace(picker_actions.select_default) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + return true + end, + }) +end + +-- +-- YankLink: +-- ----------- +-- +-- Create and yank a [[link]] from the current note. +-- +local function YankLink() + local title = "[[" + .. Pinfo:new({ filepath = vim.fn.expand("%:p"), M.Cfg }).title + .. "]]" + vim.fn.setreg('"', title) + print("yanked " .. title) +end + +-- +-- RenameNote: +-- ----------- +-- +-- Prompt for new note title, rename the note and update all links. +-- +local function RenameNote() + local oldfile = Pinfo:new({ filepath = vim.fn.expand("%:p"), M.Cfg }) + + tkutils.prompt_title(M.Cfg.extension, oldfile.title, function(newname) + local newpath = newname:match("(.*/)") or "" + newpath = M.Cfg.home .. "/" .. newpath + + -- If no subdir specified, place the new note in the same place as old note + if + M.Cfg.subdirs_in_links == true + and newpath == M.Cfg.home .. "/" + and oldfile.sub_dir ~= "" + then + newname = oldfile.sub_dir .. "/" .. newname + end + + local fname = M.Cfg.home .. "/" .. newname .. M.Cfg.extension + local fexists = file_exists(fname) + if fexists then + print_error("File alreay exists. Renaming abandonned") + return + end + + -- Savas newfile, delete buffer of old one and remove old file + if newname ~= "" and newname ~= oldfile.title then + if not (check_dir_and_ask(newpath, "Renamed file")) then + return + end + + local oldTitle = oldfile.title:gsub(" ", "\\ ") + vim.cmd( + "saveas " .. M.Cfg.home .. "/" .. newname .. M.Cfg.extension + ) + vim.cmd("bdelete " .. oldTitle .. M.Cfg.extension) + os.execute( + "rm " .. M.Cfg.home .. "/" .. oldTitle .. M.Cfg.extension + ) + end + + if M.Cfg.rename_update_links == true then + -- Only look for the first part of the link, so we do not touch to #heading or #^paragraph + -- Should use regex instead to ensure it is a proper link + local oldlink = "[[" .. oldfile.title + local newlink = "[[" .. newname + + -- Save open buffers before looking for links to replace + if #(vim.fn.getbufinfo({ bufmodified = 1 })) > 1 then + vim.ui.select({ "Yes (default)", "No" }, { + prompt = "Telekasten.nvim: " + .. "Save all modified buffers before updating links?", + }, function(answer) + if answer ~= "No" then + save_all_mod_buffers() + end + end) + end + + recursive_substitution(M.Cfg.home, oldlink, newlink) + recursive_substitution(M.Cfg.dailies, oldlink, newlink) + recursive_substitution(M.Cfg.weeklies, oldlink, newlink) + end + end) +end + +-- +-- GotoDate: +-- ---------- +-- +-- find note for date and create it if necessary. +-- +local function GotoDate(opts) + opts.dates = calculate_dates(opts.date_table) + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + opts.journal_auto_open = opts.journal_auto_open or M.Cfg.journal_auto_open + + local word = opts.date or os.date(dateformats.date) + + local fname = M.Cfg.dailies .. "/" .. word .. M.Cfg.extension + local fexists = file_exists(fname) + if + (fexists ~= true) + and ( + (opts.dailies_create_nonexisting == true) + or M.Cfg.dailies_create_nonexisting == true + ) + then + create_note_from_template( + word, + nil, + fname, + M.note_type_templates.daily, + opts.dates + ) + opts.erase = true + opts.erase_file = fname + end + + if opts.journal_auto_open then + vim.cmd("e " .. fname) + else + find_files_sorted({ + prompt_title = "Goto day", + cwd = M.Cfg.dailies, + default_text = word, + find_command = M.Cfg.find_command, + attach_mappings = function(prompt_bufnr, map) + actions.select_default:replace(function() + actions.close(prompt_bufnr) + + -- open the new note + if opts.calendar == true then + vim.cmd("wincmd w") + end + vim.cmd("e " .. fname) + picker_actions.post_open() + end) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.close(opts)) + map("n", "", picker_actions.close(opts)) + return true + end, + }) + end +end + +-- +-- GotoToday: +-- ---------- +-- +-- find today's daily note and create it if necessary. +-- +local function GotoToday(opts) + opts = opts or {} + + if not global_dir_check() then + return + end + + local today = os.date(dateformats.date) + opts.date_table = os.date("*t") + opts.date = today + opts.dailies_create_nonexisting = true -- Always use template for GotoToday + GotoDate(opts) +end + +-- +-- FindNotes: +-- ---------- +-- +-- Select from notes +-- +local function FindNotes(opts) + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + + if not global_dir_check() then + return + end + + local cwd = M.Cfg.home + local find_command = M.Cfg.find_command + local sort = M.Cfg.sort + local attach_mappings = function(_, map) + actions.select_default:replace(picker_actions.select_default) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("i", "", picker_actions.create_new(opts)) + map("n", "", picker_actions.create_new(opts)) + return true + end + + if opts.with_live_grep then + builtin.live_grep({ + prompt_title = "Find notes by live grep", + cwd = cwd, + find_command = find_command, + attach_mappings = attach_mappings, + sort = sort, + }) + else + find_files_sorted({ + prompt_title = "Find notes by name", + cwd = cwd, + find_command = find_command, + attach_mappings = attach_mappings, + sort = sort, + }) + end +end + +-- +-- InsertImgLink: +-- -------------- +-- +-- Insert link to image / media, with optional preview +-- +local function InsertImgLink(opts) + opts = opts or {} + + if not global_dir_check() then + return + end + + find_files_sorted({ + prompt_title = "Find image/media", + cwd = M.Cfg.home, + find_command = M.Cfg.find_command, + filter_extensions = { + ".png", + ".jpg", + ".bmp", + ".gif", + ".pdf", + ".mp4", + ".webm", + }, + preview_type = "media", + attach_mappings = function(prompt_bufnr, map) + actions.select_default:replace(function() + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + local fn = selection.value + fn = make_relative_path(vim.fn.expand("%:p"), fn, "/") + vim.api.nvim_put({ "![](" .. fn .. ")" }, "", true, true) + if opts.i then + vim.api.nvim_feedkeys("A", "m", false) + end + end) + map("i", "", picker_actions.yank_img_link(opts)) + map("i", "", picker_actions.paste_img_link(opts)) + map("n", "", picker_actions.yank_img_link(opts)) + map("n", "", picker_actions.paste_img_link(opts)) + map("i", "", picker_actions.paste_img_link(opts)) + map("n", "", picker_actions.paste_img_link(opts)) + return true + end, + sort = M.Cfg.sort, + }) +end + +-- +-- SearchNotes: +-- ------------ +-- +-- find the file linked to by the word under the cursor +-- +local function SearchNotes(opts) + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + + if not global_dir_check() then + return + end + + builtin.live_grep({ + prompt_title = "Search in notes", + cwd = M.Cfg.home, + search_dirs = { M.Cfg.home }, + default_text = opts.default_text or vim.fn.expand(""), + find_command = M.Cfg.find_command, + attach_mappings = function(_, map) + actions.select_default:replace(picker_actions.select_default) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + return true + end, + }) +end + +-- +-- ShowBacklinks: +-- ------------ +-- +-- Find all notes linking to this one +-- +local function ShowBacklinks(opts) + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + + if not global_dir_check() then + return + end + + local title = Pinfo:new({ filepath = vim.fn.expand("%:p"), M.Cfg }).title + -- or vim.api.nvim_buf_get_name(0) + builtin.live_grep({ + results_title = "Backlinks to " .. title, + prompt_title = "Search", + cwd = M.Cfg.home, + search_dirs = { M.Cfg.home }, + default_text = "\\[\\[" .. title .. "([#|].+)?\\]\\]", + find_command = M.Cfg.find_command, + attach_mappings = function(_, map) + actions.select_default:replace(picker_actions.select_default) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + return true + end, + }) +end + +-- +-- CreateNoteSelectTemplate() +-- -------------------------- +-- +-- Prompts for title, then pops up telescope for template selection, +-- creates the new note by template and opens it + +local function on_create_with_template(opts, title) + if title == nil then + return + end + + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + opts.new_note_location = opts.new_note_location or M.Cfg.new_note_location + opts.template_handling = opts.template_handling or M.Cfg.template_handling + + local uuid = get_uuid(opts) + local pinfo = Pinfo:new({ + title = generate_note_filename(uuid, title), + opts, + }) + local fname = pinfo.filepath + if pinfo.fexists == true then + -- open the new note + vim.cmd("e " .. fname) + picker_actions.post_open() + return + end + + find_files_sorted({ + prompt_title = "Select template...", + cwd = M.Cfg.templates, + find_command = M.Cfg.find_command, + attach_mappings = function(prompt_bufnr, map) + actions.select_default:replace(function() + actions.close(prompt_bufnr) + -- local template = M.Cfg.templates .. "/" .. action_state.get_selected_entry().value + local template = action_state.get_selected_entry().value + -- TODO: pass in the calendar_info returned from the pinfo + create_note_from_template( + title, + uuid, + fname, + template, + pinfo.calendar_info + ) + -- open the new note + vim.cmd("e " .. fname) + picker_actions.post_open() + end) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + return true + end, + }) +end + +local function CreateNoteSelectTemplate(opts) + opts = opts or {} + + if not global_dir_check() then + return + end + + tkutils.prompt_title(M.Cfg.extension, nil, function(title) + on_create_with_template(opts, title) + end) +end + +-- +-- CreateNote: +-- ------------ +-- +-- Prompts for title and creates note with default template +-- +local function on_create(opts, title) + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + opts.new_note_location = opts.new_note_location or M.Cfg.new_note_location + opts.template_handling = opts.template_handling or M.Cfg.template_handling + + if title == nil then + return + end + + local uuid = get_uuid(opts) + local pinfo = Pinfo:new({ + title = generate_note_filename(uuid, title), + opts, + }) + local fname = pinfo.filepath + + if pinfo.fexists ~= true then + -- TODO: pass in the calendar_info returned in pinfo + create_note_from_template( + title, + uuid, + fname, + pinfo.template, + pinfo.calendar_info + ) + opts.erase = true + opts.erase_file = fname + end + + find_files_sorted({ + prompt_title = "Created note...", + cwd = pinfo.root_dir, + default_text = generate_note_filename(uuid, title), + find_command = M.Cfg.find_command, + attach_mappings = function(_, map) + actions.select_default:replace(picker_actions.select_default) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.close(opts)) + map("n", "", picker_actions.close(opts)) + return true + end, + }) +end + +local function CreateNote(opts) + opts = opts or {} + + if not global_dir_check() then + return + end + + if M.Cfg.template_handling == "always_ask" then + return CreateNoteSelectTemplate(opts) + end + + tkutils.prompt_title(M.Cfg.extension, nil, function(title) + on_create(opts, title) + end) +end + +-- +-- FollowLink: +-- ----------- +-- +-- find the file linked to by the word under the cursor +-- +local function FollowLink(opts) + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + + opts.template_handling = opts.template_handling or M.Cfg.template_handling + opts.new_note_location = opts.new_note_location or M.Cfg.new_note_location + + if not global_dir_check() then + return + end + + local search_mode = "files" + local title + local filename_part = "" + + -- first: check if we're in a tag or a link + local kind, atcol, tag + local globArg = "" + + if opts.follow_tag ~= nil then + kind = "tag" + tag = opts.follow_tag + if opts.templateDir ~= nil then + globArg = "--glob=!" .. "**/" .. opts.templateDir .. "/*.md" + end + else + kind, atcol = check_for_link_or_tag() + end + + if kind == "tag" then + if atcol ~= nil then + tag = taglinks.get_tag_at( + vim.api.nvim_get_current_line(), + atcol, + M.Cfg + ) + end + search_mode = "tag" + title = tag + else + if kind == "link" then + -- we are in a link + vim.cmd("normal yi]") + title = vim.fn.getreg('"0') + title = title:gsub("^(%[)(.+)(%])$", "%2") + title = remove_alias(title) + else + -- we are in an external [link] + vim.cmd("normal yi)") + local url = vim.fn.getreg('"0') + return follow_url(url) + end + + local parts = vim.split(title, "#") + + -- if there is a # + if #parts ~= 1 then + search_mode = "heading" + title = parts[2] + filename_part = parts[1] + parts = vim.split(title, "%^") + if #parts ~= 1 then + search_mode = "para" + title = parts[2] + end + end + + -- this applies to heading and para search_mode + -- if we cannot find the file, revert to global heading search by + -- setting filename to empty string + if #filename_part > 0 then + local pinfo = Pinfo:new({ title = filename_part }) + filename_part = pinfo.filepath + if pinfo.fexists == false then + -- print("error") + filename_part = "" + end + end + end + + if search_mode == "files" then + -- check if subdir exists + local filepath = title:match("(.*/)") or "" + filepath = M.Cfg.home .. "/" .. filepath + check_dir_and_ask(filepath, "") + + -- check if fname exists anywhere + local pinfo = Pinfo:new({ title = title }) + if + (pinfo.fexists ~= true) + and ( + (opts.follow_creates_nonexisting == true) + or M.Cfg.follow_creates_nonexisting == true + ) + then + if opts.template_handling == "always_ask" then + return on_create_with_template(opts, title) + end + + if #pinfo.filepath > 0 then + local uuid = get_uuid(opts) + create_note_from_template( + title, + uuid, + pinfo.filepath, + pinfo.template, + pinfo.calendar_info + ) + opts.erase = true + opts.erase_file = pinfo.filepath + end + end + + find_files_sorted({ + prompt_title = "Follow link to note...", + cwd = pinfo.root_dir, + default_text = title, + find_command = M.Cfg.find_command, + attach_mappings = function(_, map) + actions.select_default:replace(picker_actions.select_default) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.close(opts)) + map("n", "", picker_actions.close(opts)) + return true + end, + sort = M.Cfg.sort, + }) + end + + if search_mode ~= "files" then + local search_pattern = title + local cwd = M.Cfg.home + + opts.cwd = cwd + local counts = nil + if opts.show_link_counts then + counts = linkutils.generate_backlink_map(M.Cfg) + end + + -- display with devicons + local function iconic_display(display_entry) + local display_opts = { + path_display = function(_, e) + return e:gsub(escape(opts.cwd .. "/"), "") + end, + } + + local hl_group + local display = + utils.transform_path(display_opts, display_entry.value) + + display_entry.filn = display_entry.filn or display:gsub(":.*", "") + display, hl_group = + utils.transform_devicons(display_entry.filn, display, false) + + if hl_group then + return display, { { { 1, 3 }, hl_group } } + else + return display + end + end + + -- for media_files + local popup_opts = {} + opts.get_preview_window = function() + return popup_opts.preview + end + + -- local width = config.width + -- or config.layout_config.width + -- or config.layout_config[config.layout_strategy].width + -- or vim.o.columns + -- local telescope_win_width + -- if width > 1 then + -- telescope_win_width = width + -- else + -- telescope_win_width = math.floor(vim.o.columns * width) + -- end + local displayer = entry_display.create({ + separator = "", + items = { + { width = 4 }, + { width = 4 }, + { remaining = true }, + }, + }) + + local function make_display(entry) + local fn = entry.filename + local nlinks = counts.link_counts[fn] or 0 + local nbacks = counts.backlink_counts[fn] or 0 + + if opts.show_link_counts then + local display, hl = iconic_display(entry) + + return displayer({ + { + "L" .. tostring(nlinks), + function() + return { + { { 0, 1 }, "tkTagSep" }, + { { 1, 3 }, "tkTag" }, + } + end, + }, + { + "B" .. tostring(nbacks), + function() + return { + { { 0, 1 }, "tkTagSep" }, + { { 1, 3 }, "DevIconMd" }, + } + end, + }, + { + display, + function() + return hl + end, + }, + }) + else + return iconic_display(entry) + end + end + + local lookup_keys = { + value = 1, + ordinal = 1, + } + + local find = (function() + if Path.path.sep == "\\" then + return function(t) + local start, _, filn, lnum, col, text = + string.find(t, [[([^:]+):(%d+):(%d+):(.*)]]) + + -- Handle Windows drive letter (e.g. "C:") at the beginning (if present) + if start == 3 then + filn = string.sub(t.value, 1, 3) .. filn + end + + return filn, lnum, col, text + end + else + return function(t) + local _, _, filn, lnum, col, text = + string.find(t, [[([^:]+):(%d+):(%d+):(.*)]]) + return filn, lnum, col, text + end + end + end)() + + local parse = function(t) + -- print("t: ", vim.inspect(t)) + local filn, lnum, col, text = find(t.value) + + local ok + ok, lnum = pcall(tonumber, lnum) + if not ok then + lnum = nil + end + + ok, col = pcall(tonumber, col) + if not ok then + col = nil + end + + t.filn = filn + t.lnum = lnum + t.col = col + t.text = text + + return { filn, lnum, col, text } + end + + local function entry_maker(_) + local mt_vimgrep_entry + + opts = opts or {} + + -- local disable_devicons = opts.disable_devicons + -- local disable_coordinates = opts.disable_coordinates or true + local only_sort_text = opts.only_sort_text + + local execute_keys = { + path = function(t) + if Path:new(t.filename):is_absolute() then + return t.filename, false + else + return Path:new({ t.cwd, t.filename }):absolute(), false + end + end, + filename = function(t) + return parse(t)[1], true + end, + lnum = function(t) + return parse(t)[2], true + end, + col = function(t) + return parse(t)[3], true + end, + text = function(t) + return parse(t)[4], true + end, + } + + -- For text search only, the ordinal value is actually the text. + if only_sort_text then + execute_keys.ordinal = function(t) + return t.text + end + end + + mt_vimgrep_entry = { + cwd = vim.fn.expand(opts.cwd or vim.loop.cwd()), + __index = function(t, k) + local raw = rawget(mt_vimgrep_entry, k) + if raw then + return raw + end + + local executor = rawget(execute_keys, k) + if executor then + local val, save = executor(t) + if save then + rawset(t, k, val) + end + return val + end + + return rawget(t, rawget(lookup_keys, k)) + end, + } + + -- + if opts.show_link_counts then + mt_vimgrep_entry.display = make_display + else + mt_vimgrep_entry.display = iconic_display + end + + return function(line) + return setmetatable({ line }, mt_vimgrep_entry) + end + end + + opts.entry_maker = entry_maker(opts) + + local live_grepper = finders.new_job( + function(prompt) + if not prompt or prompt == "" then + return nil + end + + local search_command = { + "rg", + "--vimgrep", + "-e", + "^#+\\s" .. prompt, + "--", + } + if search_mode == "para" then + search_command = { + "rg", + "--vimgrep", + "-e", + "\\^" .. prompt, + "--", + } + end + + if search_mode == "tag" then + search_command = { + "rg", + "--vimgrep", + globArg, + "-e", + prompt, + "--", + } + end + + if #filename_part > 0 then + table.insert(search_command, filename_part) + else + table.insert(search_command, cwd) + end + + local ret = vim.tbl_flatten({ search_command }) + return ret + end, + opts.entry_maker or make_entry.gen_from_vimgrep(opts), + opts.max_results, + opts.cwd + ) + + -- builtin.live_grep({ + local picker = pickers.new({ + cwd = cwd, + prompt_title = "Notes referencing `" .. title .. "`", + default_text = search_pattern, + initial_mode = "insert", + -- link to specific file (a daily file): [[2021-02-22]] + -- link to heading in specific file (a daily file): [[2021-02-22#Touchpoint]] + -- link to heading globally [[#Touchpoint]] + -- link to heading in specific file (a daily file): [[The cool note#^xAcSh-xxr]] + -- link to paragraph globally [[#^xAcSh-xxr]] + finder = live_grepper, + previewer = conf.grep_previewer(opts), + sorter = sorters.highlighter_only(opts), + attach_mappings = function(_, map) + actions.select_default:replace(picker_actions.select_default) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + return true + end, + }) + picker:find() + end end -- @@ -2599,58 +2599,58 @@ end -- find this week's weekly note and create it if necessary. -- local function GotoThisWeek(opts) - opts = opts or {} - opts.insert_after_inserting = opts.insert_after_inserting - or M.Cfg.insert_after_inserting - opts.close_after_yanking = opts.close_after_yanking - or M.Cfg.close_after_yanking - opts.journal_auto_open = opts.journal_auto_open or M.Cfg.journal_auto_open + opts = opts or {} + opts.insert_after_inserting = opts.insert_after_inserting + or M.Cfg.insert_after_inserting + opts.close_after_yanking = opts.close_after_yanking + or M.Cfg.close_after_yanking + opts.journal_auto_open = opts.journal_auto_open or M.Cfg.journal_auto_open - if not global_dir_check() then - return - end + if not global_dir_check() then + return + end - local dinfo = calculate_dates() - local title = dinfo.isoweek - local fname = M.Cfg.weeklies .. "/" .. title .. M.Cfg.extension - local fexists = file_exists(fname) - if - (fexists ~= true) - and ( - (opts.weeklies_create_nonexisting == true) - or M.Cfg.weeklies_create_nonexisting == true - ) - then - create_note_from_template( - title, - nil, - fname, - M.note_type_templates.weekly - ) - opts.erase = true - opts.erase_file = fname - end + local dinfo = calculate_dates() + local title = dinfo.isoweek + local fname = M.Cfg.weeklies .. "/" .. title .. M.Cfg.extension + local fexists = file_exists(fname) + if + (fexists ~= true) + and ( + (opts.weeklies_create_nonexisting == true) + or M.Cfg.weeklies_create_nonexisting == true + ) + then + create_note_from_template( + title, + nil, + fname, + M.note_type_templates.weekly + ) + opts.erase = true + opts.erase_file = fname + end - if opts.journal_auto_open then - vim.cmd("e " .. fname) - else - find_files_sorted({ - prompt_title = "Goto this week:", - cwd = M.Cfg.weeklies, - default_text = title, - find_command = M.Cfg.find_command, - attach_mappings = function(_, map) - actions.select_default:replace(picker_actions.select_default) - map("i", "", picker_actions.yank_link(opts)) - map("i", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.yank_link(opts)) - map("n", "", picker_actions.paste_link(opts)) - map("n", "", picker_actions.close(opts)) - map("n", "", picker_actions.close(opts)) - return true - end, - }) - end + if opts.journal_auto_open then + vim.cmd("e " .. fname) + else + find_files_sorted({ + prompt_title = "Goto this week:", + cwd = M.Cfg.weeklies, + default_text = title, + find_command = M.Cfg.find_command, + attach_mappings = function(_, map) + actions.select_default:replace(picker_actions.select_default) + map("i", "", picker_actions.yank_link(opts)) + map("i", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.yank_link(opts)) + map("n", "", picker_actions.paste_link(opts)) + map("n", "", picker_actions.close(opts)) + map("n", "", picker_actions.close(opts)) + return true + end, + }) + end end -- @@ -2659,37 +2659,37 @@ end -- return if a daily 'note exists' indicator (sign) should be displayed for a particular day local function CalendarSignDay(day, month, year) - local fn = M.Cfg.dailies - .. "/" - .. string.format("%04d-%02d-%02d", year, month, day) - .. M.Cfg.extension - if file_exists(fn) then - return 1 - end - return 0 + local fn = M.Cfg.dailies + .. "/" + .. string.format("%04d-%02d-%02d", year, month, day) + .. M.Cfg.extension + if file_exists(fn) then + return 1 + end + return 0 end -- action on enter on a specific day: -- preview in telescope, stay in calendar on cancel, open note in other window on accept local function CalendarAction(day, month, year, _, _) - local opts = {} - opts.date = string.format("%04d-%02d-%02d", year, month, day) - opts.date_table = { year = year, month = month, day = day } - opts.calendar = true - GotoDate(opts) + local opts = {} + opts.date = string.format("%04d-%02d-%02d", year, month, day) + opts.date_table = { year = year, month = month, day = day } + opts.calendar = true + GotoDate(opts) end local function ShowCalendar(opts) - local defaults = {} - defaults.cmd = "CalendarVR" - defaults.vertical_resize = 1 + local defaults = {} + defaults.cmd = "CalendarVR" + defaults.vertical_resize = 1 - opts = opts or defaults - vim.cmd(opts.cmd) - if opts.vertical_resize then - vim.cmd("vertical resize +" .. opts.vertical_resize) - end - vim.cmd([[ + opts = opts or defaults + vim.cmd(opts.cmd) + if opts.vertical_resize then + vim.cmd("vertical resize +" .. opts.vertical_resize) + end + vim.cmd([[ set signcolumn=no set nonumber set norelativenumber @@ -2698,10 +2698,10 @@ end -- set up calendar integration: forward to our lua functions local function SetupCalendar(opts) - local defaults = M.Cfg.calendar_opts - opts = opts or defaults + local defaults = M.Cfg.calendar_opts + opts = opts or defaults - local cmd = [[ + local cmd = [[ function! MyCalSign(day, month, year) return luaeval('require("telekasten").CalendarSignDay(_A[1], _A[2], _A[3])', [a:day, a:month, a:year]) endfunction @@ -2729,166 +2729,166 @@ local function SetupCalendar(opts) let g:calendar_weeknm = {{weeknm}} ]] - for k, v in pairs(opts) do - cmd = cmd:gsub("{{" .. k .. "}}", v) - end - vim.cmd(cmd) - if opts.calendar_monday == 1 then - vim.cmd("let g:calendar_monday = 1") - end + for k, v in pairs(opts) do + cmd = cmd:gsub("{{" .. k .. "}}", v) + end + vim.cmd(cmd) + if opts.calendar_monday == 1 then + vim.cmd("let g:calendar_monday = 1") + end end local function ToggleTodo(opts) - -- replace - -- by - - -- - by - [ ] - -- - [ ] by - [x] - -- - [x] by - - -- enter insert mode if opts.i == true - -- if opts.v = true, then look for marks to toggle - opts = opts or {} - local startline = vim.api.nvim_buf_get_mark(0, "<")[1] - local endline = vim.api.nvim_buf_get_mark(0, ">")[1] - local cursorlinenr = vim.api.nvim_win_get_cursor(0)[1] - -- to avoid the visual range marks not being reset when calling - -- command from normal mode - vim.api.nvim_buf_set_mark(0, "<", 0, 0, {}) - vim.api.nvim_buf_set_mark(0, ">", 0, 0, {}) - if startline <= 0 or endline <= 0 or opts.v ~= true then - startline = cursorlinenr - endline = cursorlinenr - end - for curlinenr = startline, endline do - local curline = - vim.api.nvim_buf_get_lines(0, curlinenr - 1, curlinenr, false)[1] - local stripped = vim.trim(curline) - local repline - if - vim.startswith(stripped, "- ") - and not vim.startswith(stripped, "- [") - then - repline = curline:gsub("%- ", "- [ ] ", 1) - else - if vim.startswith(stripped, "- [ ]") then - repline = curline:gsub("%- %[ %]", "- [x]", 1) - else - if vim.startswith(stripped, "- [x]") then - if opts.onlyTodo then - repline = curline:gsub("%- %[x%]", "- [ ]", 1) - else - repline = curline:gsub("%- %[x%]", "-", 1) - end + -- replace + -- by - + -- - by - [ ] + -- - [ ] by - [x] + -- - [x] by - + -- enter insert mode if opts.i == true + -- if opts.v = true, then look for marks to toggle + opts = opts or {} + local startline = vim.api.nvim_buf_get_mark(0, "<")[1] + local endline = vim.api.nvim_buf_get_mark(0, ">")[1] + local cursorlinenr = vim.api.nvim_win_get_cursor(0)[1] + -- to avoid the visual range marks not being reset when calling + -- command from normal mode + vim.api.nvim_buf_set_mark(0, "<", 0, 0, {}) + vim.api.nvim_buf_set_mark(0, ">", 0, 0, {}) + if startline <= 0 or endline <= 0 or opts.v ~= true then + startline = cursorlinenr + endline = cursorlinenr + end + for curlinenr = startline, endline do + local curline = + vim.api.nvim_buf_get_lines(0, curlinenr - 1, curlinenr, false)[1] + local stripped = vim.trim(curline) + local repline + if + vim.startswith(stripped, "- ") + and not vim.startswith(stripped, "- [") + then + repline = curline:gsub("%- ", "- [ ] ", 1) else - repline = curline:gsub("(%S)", "- [ ] %1", 1) + if vim.startswith(stripped, "- [ ]") then + repline = curline:gsub("%- %[ %]", "- [x]", 1) + else + if vim.startswith(stripped, "- [x]") then + if opts.onlyTodo then + repline = curline:gsub("%- %[x%]", "- [ ]", 1) + else + repline = curline:gsub("%- %[x%]", "-", 1) + end + else + repline = curline:gsub("(%S)", "- [ ] %1", 1) + end + end + end + vim.api.nvim_buf_set_lines( + 0, + curlinenr - 1, + curlinenr, + false, + { repline } + ) + if opts.i then + vim.api.nvim_feedkeys("A", "m", false) end - end end - vim.api.nvim_buf_set_lines( - 0, - curlinenr - 1, - curlinenr, - false, - { repline } - ) - if opts.i then - vim.api.nvim_feedkeys("A", "m", false) - end - end end local function FindAllTags(opts) - opts = opts or {} - local i = opts.i - opts.cwd = M.Cfg.home - opts.tag_notation = M.Cfg.tag_notation - local templateDir = Path:new(M.Cfg.templates):make_relative(M.Cfg.home) - opts.templateDir = templateDir - opts.rg_pcre = M.Cfg.rg_pcre + opts = opts or {} + local i = opts.i + opts.cwd = M.Cfg.home + opts.tag_notation = M.Cfg.tag_notation + local templateDir = Path:new(M.Cfg.templates):make_relative(M.Cfg.home) + opts.templateDir = templateDir + opts.rg_pcre = M.Cfg.rg_pcre - if not global_dir_check() then - return - end - - local tag_map = tagutils.do_find_all_tags(opts) - local taglist = {} - - local max_tag_len = 0 - for k, v in pairs(tag_map) do - taglist[#taglist + 1] = { tag = k, details = v } - if #k > max_tag_len then - max_tag_len = #k + if not global_dir_check() then + return end - end - if M.Cfg.show_tags_theme == "get_cursor" then - opts = themes.get_cursor({ - layout_config = { - height = math.min(math.floor(vim.o.lines * 0.8), #taglist), - }, - }) - elseif M.Cfg.show_tags_theme == "ivy" then - opts = themes.get_ivy({ - layout_config = { - prompt_position = "top", - height = math.min(math.floor(vim.o.lines * 0.8), #taglist), - }, - }) - else - opts = themes.get_dropdown({ - layout_config = { - prompt_position = "top", - height = math.min(math.floor(vim.o.lines * 0.8), #taglist), - }, - }) - end - -- re-apply - opts.cwd = M.Cfg.home - opts.tag_notation = M.Cfg.tag_notation - opts.i = i - pickers - .new(opts, { - prompt_title = "Tags", - finder = finders.new_table({ - results = taglist, - entry_maker = function(entry) - return { - value = entry, - -- display = entry.tag .. ' \t (' .. #entry.details .. ' matches)', - display = string.format( - "%" .. max_tag_len .. "s ... (%3d matches)", - entry.tag, - #entry.details - ), - ordinal = entry.tag, - } - end, - }), - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr, map) - actions.select_default:replace(function() - -- actions for insert tag, default action: search for tag - local selection = - action_state.get_selected_entry().value.tag - local follow_opts = { - follow_tag = selection, - show_link_counts = true, - templateDir = templateDir, - } - actions._close(prompt_bufnr, false) - vim.schedule(function() - FollowLink(follow_opts) - end) - end) - map("i", "", picker_actions.yank_tag(opts)) - map("i", "", picker_actions.paste_tag(opts)) - map("n", "", picker_actions.yank_tag(opts)) - map("n", "", picker_actions.paste_tag(opts)) - map("n", "", picker_actions.close(opts)) - map("n", "", picker_actions.close(opts)) - return true - end, - }) - :find() + local tag_map = tagutils.do_find_all_tags(opts) + local taglist = {} + + local max_tag_len = 0 + for k, v in pairs(tag_map) do + taglist[#taglist + 1] = { tag = k, details = v } + if #k > max_tag_len then + max_tag_len = #k + end + end + + if M.Cfg.show_tags_theme == "get_cursor" then + opts = themes.get_cursor({ + layout_config = { + height = math.min(math.floor(vim.o.lines * 0.8), #taglist), + }, + }) + elseif M.Cfg.show_tags_theme == "ivy" then + opts = themes.get_ivy({ + layout_config = { + prompt_position = "top", + height = math.min(math.floor(vim.o.lines * 0.8), #taglist), + }, + }) + else + opts = themes.get_dropdown({ + layout_config = { + prompt_position = "top", + height = math.min(math.floor(vim.o.lines * 0.8), #taglist), + }, + }) + end + -- re-apply + opts.cwd = M.Cfg.home + opts.tag_notation = M.Cfg.tag_notation + opts.i = i + pickers + .new(opts, { + prompt_title = "Tags", + finder = finders.new_table({ + results = taglist, + entry_maker = function(entry) + return { + value = entry, + -- display = entry.tag .. ' \t (' .. #entry.details .. ' matches)', + display = string.format( + "%" .. max_tag_len .. "s ... (%3d matches)", + entry.tag, + #entry.details + ), + ordinal = entry.tag, + } + end, + }), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr, map) + actions.select_default:replace(function() + -- actions for insert tag, default action: search for tag + local selection = + action_state.get_selected_entry().value.tag + local follow_opts = { + follow_tag = selection, + show_link_counts = true, + templateDir = templateDir, + } + actions._close(prompt_bufnr, false) + vim.schedule(function() + FollowLink(follow_opts) + end) + end) + map("i", "", picker_actions.yank_tag(opts)) + map("i", "", picker_actions.paste_tag(opts)) + map("n", "", picker_actions.yank_tag(opts)) + map("n", "", picker_actions.paste_tag(opts)) + map("n", "", picker_actions.close(opts)) + map("n", "", picker_actions.close(opts)) + return true + end, + }) + :find() end -- Setup(cfg) @@ -2896,134 +2896,134 @@ end -- Overrides config with elements from cfg. See top of file for defaults. -- local function Setup(cfg) - cfg = cfg or {} - defaultConfig(cfg.home) - local debug = cfg.debug - for k, v in pairs(cfg) do - -- merge everything but calendar opts - -- they will be merged later - if k ~= "calendar_opts" then - if k == "home" then - v = v - end - M.Cfg[k] = v - if debug then - print( - "Setup() setting `" - .. k - .. "` -> `" - .. tostring(v) - .. "`" - ) - end + cfg = cfg or {} + defaultConfig(cfg.home) + local debug = cfg.debug + for k, v in pairs(cfg) do + -- merge everything but calendar opts + -- they will be merged later + if k ~= "calendar_opts" then + if k == "home" then + v = v + end + M.Cfg[k] = v + if debug then + print( + "Setup() setting `" + .. k + .. "` -> `" + .. tostring(v) + .. "`" + ) + end + end end - end - -- TODO: this is obsolete: - if vim.fn.executable("rg") == 1 then - M.Cfg.find_command = { "rg", "--files", "--sortr", "created" } - else - M.Cfg.find_command = nil - end - - -- this looks a little messy - if M.Cfg.plug_into_calendar then - cfg.calendar_opts = cfg.calendar_opts or {} - M.Cfg.calendar_opts = M.Cfg.calendar_opts or {} - M.Cfg.calendar_opts.weeknm = cfg.calendar_opts.weeknm - or M.Cfg.calendar_opts.weeknm - or 1 - M.Cfg.calendar_opts.calendar_monday = cfg.calendar_opts.calendar_monday - or M.Cfg.calendar_opts.calendar_monday - or 1 - M.Cfg.calendar_opts.calendar_mark = cfg.calendar_opts.calendar_mark - or M.Cfg.calendar_opts.calendar_mark - or "left-fit" - SetupCalendar(M.Cfg.calendar_opts) - end - - -- setup extensions to filter for - M.Cfg.filter_extensions = cfg.filter_extensions or { M.Cfg.extension } - - -- provide fake filenames for template loading to fail silently if template is configured off - M.Cfg.template_new_note = M.Cfg.template_new_note or "none" - M.Cfg.template_new_daily = M.Cfg.template_new_daily or "none" - M.Cfg.template_new_weekly = M.Cfg.template_new_weekly or "none" - - -- refresh templates - M.note_type_templates = { - normal = M.Cfg.template_new_note, - daily = M.Cfg.template_new_daily, - weekly = M.Cfg.template_new_weekly, - } - - -- for previewers to pick up our syntax, we need to tell plenary to override `.md` with our syntax - if M.Cfg.auto_set_filetype or M.Cfg.auto_set_syntax then - filetype.add_file("telekasten") - end - -- setting the syntax moved into plugin/telekasten.vim - -- and does not work - - if M.Cfg.take_over_my_home == true then - if M.Cfg.auto_set_filetype then - vim.cmd( - "au BufEnter " - .. M.Cfg.home - .. "/*" - .. M.Cfg.extension - .. " set ft=telekasten" - ) + -- TODO: this is obsolete: + if vim.fn.executable("rg") == 1 then + M.Cfg.find_command = { "rg", "--files", "--sortr", "created" } + else + M.Cfg.find_command = nil end - end - if debug then - print("Resulting config:") - print("-----------------") - print(vim.inspect(M.Cfg)) - end + -- this looks a little messy + if M.Cfg.plug_into_calendar then + cfg.calendar_opts = cfg.calendar_opts or {} + M.Cfg.calendar_opts = M.Cfg.calendar_opts or {} + M.Cfg.calendar_opts.weeknm = cfg.calendar_opts.weeknm + or M.Cfg.calendar_opts.weeknm + or 1 + M.Cfg.calendar_opts.calendar_monday = cfg.calendar_opts.calendar_monday + or M.Cfg.calendar_opts.calendar_monday + or 1 + M.Cfg.calendar_opts.calendar_mark = cfg.calendar_opts.calendar_mark + or M.Cfg.calendar_opts.calendar_mark + or "left-fit" + SetupCalendar(M.Cfg.calendar_opts) + end - -- Convert all directories in full path - M.Cfg.image_subdir = make_config_path_absolute(M.Cfg.image_subdir) - M.Cfg.dailies = make_config_path_absolute(M.Cfg.dailies) - M.Cfg.weeklies = make_config_path_absolute(M.Cfg.weeklies) - M.Cfg.templates = make_config_path_absolute(M.Cfg.templates) + -- setup extensions to filter for + M.Cfg.filter_extensions = cfg.filter_extensions or { M.Cfg.extension } - -- Check if ripgrep is compiled with --pcre - -- ! This will need to be fixed when neovim moves to lua >=5.2 by the following: - -- M.Cfg.rg_pcre = os.execute("echo 'hello' | rg --pcr2 hello &> /dev/null") or false + -- provide fake filenames for template loading to fail silently if template is configured off + M.Cfg.template_new_note = M.Cfg.template_new_note or "none" + M.Cfg.template_new_daily = M.Cfg.template_new_daily or "none" + M.Cfg.template_new_weekly = M.Cfg.template_new_weekly or "none" - M.Cfg.rg_pcre = false - local has_pcre = - os.execute("echo 'hello' | rg --pcre2 hello > /dev/null 2>&1") - if has_pcre == 0 then - M.Cfg.rg_pcre = true - end - M.Cfg.media_previewer = M.Cfg.media_previewer + -- refresh templates + M.note_type_templates = { + normal = M.Cfg.template_new_note, + daily = M.Cfg.template_new_daily, + weekly = M.Cfg.template_new_weekly, + } + + -- for previewers to pick up our syntax, we need to tell plenary to override `.md` with our syntax + if M.Cfg.auto_set_filetype or M.Cfg.auto_set_syntax then + filetype.add_file("telekasten") + end + -- setting the syntax moved into plugin/telekasten.vim + -- and does not work + + if M.Cfg.take_over_my_home == true then + if M.Cfg.auto_set_filetype then + vim.cmd( + "au BufEnter " + .. M.Cfg.home + .. "/*" + .. M.Cfg.extension + .. " set ft=telekasten" + ) + end + end + + if debug then + print("Resulting config:") + print("-----------------") + print(vim.inspect(M.Cfg)) + end + + -- Convert all directories in full path + M.Cfg.image_subdir = make_config_path_absolute(M.Cfg.image_subdir) + M.Cfg.dailies = make_config_path_absolute(M.Cfg.dailies) + M.Cfg.weeklies = make_config_path_absolute(M.Cfg.weeklies) + M.Cfg.templates = make_config_path_absolute(M.Cfg.templates) + + -- Check if ripgrep is compiled with --pcre + -- ! This will need to be fixed when neovim moves to lua >=5.2 by the following: + -- M.Cfg.rg_pcre = os.execute("echo 'hello' | rg --pcr2 hello &> /dev/null") or false + + M.Cfg.rg_pcre = false + local has_pcre = + os.execute("echo 'hello' | rg --pcre2 hello > /dev/null 2>&1") + if has_pcre == 0 then + M.Cfg.rg_pcre = true + end + M.Cfg.media_previewer = M.Cfg.media_previewer end local function _setup(cfg) - if cfg.vaults ~= nil and cfg.default_vault ~= nil then - M.vaults = cfg.vaults - cfg.vaults = nil - Setup(cfg.vaults[cfg.default_vault]) - elseif cfg.vaults ~= nil and cfg.vaults["default"] ~= nil then - M.vaults = cfg.vaults - cfg.vaults = nil - Setup(cfg.vaults["default"]) - elseif cfg.home ~= nil then - M.vaults = cfg.vaults or {} - cfg.vaults = nil - M.vaults["default"] = cfg - Setup(cfg) - end + if cfg.vaults ~= nil and cfg.default_vault ~= nil then + M.vaults = cfg.vaults + cfg.vaults = nil + Setup(cfg.vaults[cfg.default_vault]) + elseif cfg.vaults ~= nil and cfg.vaults["default"] ~= nil then + M.vaults = cfg.vaults + cfg.vaults = nil + Setup(cfg.vaults["default"]) + elseif cfg.home ~= nil then + M.vaults = cfg.vaults or {} + cfg.vaults = nil + M.vaults["default"] = cfg + Setup(cfg) + end end local function ChangeVault(opts) - tkpickers.vaults(M, opts) + tkpickers.vaults(M, opts) end local function chdir(cfg) - Setup(cfg) - -- M.Cfg = vim.tbl_deep_extend("force", defaultConfig(new_home), cfg) + Setup(cfg) + -- M.Cfg = vim.tbl_deep_extend("force", defaultConfig(new_home), cfg) end M.find_notes = FindNotes @@ -3056,122 +3056,122 @@ M.chdir = chdir -- Telekasten command, completion local TelekastenCmd = { - commands = function() - return { - { "find notes", "find_notes", M.find_notes }, - { "find daily notes", "find_daily_notes", M.find_daily_notes }, - { "search in notes", "search_notes", M.search_notes }, - { "insert link", "insert_link", M.insert_link }, - { "follow link", "follow_link", M.follow_link }, - { "goto today", "goto_today", M.goto_today }, - { "new note", "new_note", M.new_note }, - { "goto thisweek", "goto_thisweek", M.goto_thisweek }, - { "find weekly notes", "find_weekly_notes", M.find_weekly_notes }, - { "yank link to note", "yank_notelink", M.yank_notelink }, - { "rename note", "rename_note", M.rename_note }, - { - "new templated note", - "new_templated_note", - M.new_templated_note, - }, - { "show calendar", "show_calendar", M.show_calendar }, - { - "paste image from clipboard", - "paste_img_and_link", - M.paste_img_and_link, - }, - { "toggle todo", "toggle_todo", M.toggle_todo }, - { "show backlinks", "show_backlinks", M.show_backlinks }, - { "find friend notes", "find_friends", M.find_friends }, - { - "browse images, insert link", - "insert_img_link", - M.insert_img_link, - }, - { "preview image under cursor", "preview_img", M.preview_img }, - { "browse media", "browse_media", M.browse_media }, - { "panel", "panel", M.panel }, - { "show tags", "show_tags", M.show_tags }, - { "switch vault", "switch_vault", M.switch_vault }, - } - end, + commands = function() + return { + { "find notes", "find_notes", M.find_notes }, + { "find daily notes", "find_daily_notes", M.find_daily_notes }, + { "search in notes", "search_notes", M.search_notes }, + { "insert link", "insert_link", M.insert_link }, + { "follow link", "follow_link", M.follow_link }, + { "goto today", "goto_today", M.goto_today }, + { "new note", "new_note", M.new_note }, + { "goto thisweek", "goto_thisweek", M.goto_thisweek }, + { "find weekly notes", "find_weekly_notes", M.find_weekly_notes }, + { "yank link to note", "yank_notelink", M.yank_notelink }, + { "rename note", "rename_note", M.rename_note }, + { + "new templated note", + "new_templated_note", + M.new_templated_note, + }, + { "show calendar", "show_calendar", M.show_calendar }, + { + "paste image from clipboard", + "paste_img_and_link", + M.paste_img_and_link, + }, + { "toggle todo", "toggle_todo", M.toggle_todo }, + { "show backlinks", "show_backlinks", M.show_backlinks }, + { "find friend notes", "find_friends", M.find_friends }, + { + "browse images, insert link", + "insert_img_link", + M.insert_img_link, + }, + { "preview image under cursor", "preview_img", M.preview_img }, + { "browse media", "browse_media", M.browse_media }, + { "panel", "panel", M.panel }, + { "show tags", "show_tags", M.show_tags }, + { "switch vault", "switch_vault", M.switch_vault }, + } + end, } TelekastenCmd.command = function(subcommand) - local show = function(opts) - opts = opts or {} - pickers - .new(opts, { - prompt_title = "Command palette", - finder = finders.new_table({ - results = TelekastenCmd.commands(), - entry_maker = function(entry) - return { - value = entry, - display = entry[1], - ordinal = entry[2], - } - end, - }), - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr, _) - actions.select_default:replace(function() - -- important: actions.close(bufnr) is not enough - -- it resulted in: preview_img NOT receiving the prompt as default text - -- apparently it has sth to do with keeping insert mode - actions._close(prompt_bufnr, true) + local show = function(opts) + opts = opts or {} + pickers + .new(opts, { + prompt_title = "Command palette", + finder = finders.new_table({ + results = TelekastenCmd.commands(), + entry_maker = function(entry) + return { + value = entry, + display = entry[1], + ordinal = entry[2], + } + end, + }), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr, _) + actions.select_default:replace(function() + -- important: actions.close(bufnr) is not enough + -- it resulted in: preview_img NOT receiving the prompt as default text + -- apparently it has sth to do with keeping insert mode + actions._close(prompt_bufnr, true) - local selection = - action_state.get_selected_entry().value[3] - selection() - end) - return true - end, - }) - :find() - end - if subcommand then - -- print("trying subcommand " .. "`" .. subcommand .. "`") - for _, entry in pairs(TelekastenCmd.commands()) do - if entry[2] == subcommand then - local selection = entry[3] - selection() - return - end + local selection = + action_state.get_selected_entry().value[3] + selection() + end) + return true + end, + }) + :find() end - print("No such subcommand: `" .. subcommand .. "`") - else - local theme - - if M.Cfg.command_palette_theme == "ivy" then - theme = themes.get_ivy() + if subcommand then + -- print("trying subcommand " .. "`" .. subcommand .. "`") + for _, entry in pairs(TelekastenCmd.commands()) do + if entry[2] == subcommand then + local selection = entry[3] + selection() + return + end + end + print("No such subcommand: `" .. subcommand .. "`") else - theme = themes.get_dropdown({ - layout_config = { prompt_position = "top" }, - }) + local theme + + if M.Cfg.command_palette_theme == "ivy" then + theme = themes.get_ivy() + else + theme = themes.get_dropdown({ + layout_config = { prompt_position = "top" }, + }) + end + show(theme) end - show(theme) - end end function picker_actions.create_new(opts) - opts = opts or {} - opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links - return function(prompt_bufnr) - local prompt = - action_state.get_current_picker(prompt_bufnr).sorter._discard_state.prompt - actions.close(prompt_bufnr) - on_create(opts, prompt) - -- local selection = action_state.get_selected_entry() - end + opts = opts or {} + opts.subdirs_in_links = opts.subdirs_in_links or M.Cfg.subdirs_in_links + return function(prompt_bufnr) + local prompt = + action_state.get_current_picker(prompt_bufnr).sorter._discard_state.prompt + actions.close(prompt_bufnr) + on_create(opts, prompt) + -- local selection = action_state.get_selected_entry() + end end -- nvim completion function for completing :Telekasten sub-commands TelekastenCmd.complete = function() - local candidates = {} - for k, v in pairs(TelekastenCmd.commands()) do - candidates[k] = v[2] - end - return candidates + local candidates = {} + for k, v in pairs(TelekastenCmd.commands()) do + candidates[k] = v[2] + end + return candidates end M.panel = TelekastenCmd.command diff --git a/lua/telekasten/config.lua b/lua/telekasten/config.lua new file mode 100644 index 0000000..e69de29 diff --git a/lua/telekasten/functions.lua b/lua/telekasten/functions.lua new file mode 100644 index 0000000..e69de29 diff --git a/lua/telekasten/pickers.lua b/lua/telekasten/pickers.lua index 702b899..f11465d 100644 --- a/lua/telekasten/pickers.lua +++ b/lua/telekasten/pickers.lua @@ -8,37 +8,37 @@ local conf = require("telescope.config").values -- Pick between the various configured vaults function M.vaults(telekasten, opts) - opts = opts or {} - local vaults = telekasten.vaults - local _vaults = {} - for k, v in pairs(vaults) do - table.insert(_vaults, { k, v }) - end - pickers - .new(opts, { - prompt_title = "Vaults", - finder = finders.new_table({ - results = _vaults, - entry_maker = function(entry) - return { - value = entry, - display = entry[1], - ordinal = entry[1], - } - end, - }), - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr, map) - actions.select_default:replace(function() - actions.close(prompt_bufnr) - local selection = action_state.get_selected_entry() - -- print(vim.inspect(selection)) - telekasten.chdir(selection.value[2]) - end) - return true - end, - }) - :find() + opts = opts or {} + local vaults = telekasten.vaults + local _vaults = {} + for k, v in pairs(vaults) do + table.insert(_vaults, { k, v }) + end + pickers + .new(opts, { + prompt_title = "Vaults", + finder = finders.new_table({ + results = _vaults, + entry_maker = function(entry) + return { + value = entry, + display = entry[1], + ordinal = entry[1], + } + end, + }), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr, map) + actions.select_default:replace(function() + actions.close(prompt_bufnr) + local selection = action_state.get_selected_entry() + -- print(vim.inspect(selection)) + telekasten.chdir(selection.value[2]) + end) + return true + end, + }) + :find() end return M diff --git a/lua/telekasten/syntax.lua b/lua/telekasten/syntax.lua new file mode 100644 index 0000000..e69de29 diff --git a/lua/telekasten/templates.lua b/lua/telekasten/templates.lua new file mode 100644 index 0000000..e69de29 diff --git a/lua/telekasten/utils/dateutils.lua b/lua/telekasten/utils/dateutils.lua index 435a403..e52979e 100644 --- a/lua/telekasten/utils/dateutils.lua +++ b/lua/telekasten/utils/dateutils.lua @@ -5,110 +5,110 @@ local date = require("telekasten.utils.luadate") --- see https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm --- see https://en.wikipedia.org/wiki/ISO_week_date M.dow_for_year = function(year) - return ( - year - + math.floor(year / 4) - - math.floor(year / 100) - + math.floor(year / 400) - ) % 7 + return ( + year + + math.floor(year / 4) + - math.floor(year / 100) + + math.floor(year / 400) + ) % 7 end M.weeks_in_year = function(year) - local d = 0 - local dy = M.dow_for_year(year) == 4 -- current year ends Thursday - local dyy = M.dow_for_year(year - 1) == 3 -- previous year ends Wednesday - if dy or dyy then - d = 1 - end - return 52 + d + local d = 0 + local dy = M.dow_for_year(year) == 4 -- current year ends Thursday + local dyy = M.dow_for_year(year - 1) == 3 -- previous year ends Wednesday + if dy or dyy then + d = 1 + end + return 52 + d end M.days_in_year = function(year) - local t = os.time({ year = year, month = 12, day = 31 }) - return os.date("*t", t).yday + local t = os.time({ year = year, month = 12, day = 31 }) + return os.date("*t", t).yday end M.date_from_doy = function(year, doy) - local ret = { - year = year, - month = 1, - day = doy, - } - -- january is clear immediately - if doy < 32 then - return ret - end - - local dmap = { - [1] = 31, - [2] = 28, -- will be fixed further down - [3] = 31, - [4] = 30, - [5] = 31, - [6] = 30, - [7] = 31, - [8] = 31, - [9] = 30, - [10] = 31, - [11] = 30, - [12] = 31, - } - if M.days_in_year(year) == 366 then - dmap[2] = 29 - end - - for month, d in pairs(dmap) do - doy = doy - d - if doy < 0 then - ret.day = doy + d - ret.month = month - return ret + local ret = { + year = year, + month = 1, + day = doy, + } + -- january is clear immediately + if doy < 32 then + return ret end - end - return ret -- unreachable if input values are sane + + local dmap = { + [1] = 31, + [2] = 28, -- will be fixed further down + [3] = 31, + [4] = 30, + [5] = 31, + [6] = 30, + [7] = 31, + [8] = 31, + [9] = 30, + [10] = 31, + [11] = 30, + [12] = 31, + } + if M.days_in_year(year) == 366 then + dmap[2] = 29 + end + + for month, d in pairs(dmap) do + doy = doy - d + if doy < 0 then + ret.day = doy + d + ret.month = month + return ret + end + end + return ret -- unreachable if input values are sane end -- the algo on wikipedia seems wrong, so we opt for full-blown luadate M.isoweek_to_date = function(year, isoweek) - local ret = date(year .. "-W" .. string.format("%02d", isoweek) .. "-1") - return { - year = ret:getyear(), - month = ret:getmonth(), - day = ret:getday(), - } + local ret = date(year .. "-W" .. string.format("%02d", isoweek) .. "-1") + return { + year = ret:getyear(), + month = ret:getmonth(), + day = ret:getday(), + } end local function check_isoweek(year, isoweek, ydate) - print("*********** KW " .. isoweek .. " " .. year .. ": ") - -- local ret = M.weeknumber_to_date(year, isoweek) - local ret = M.isoweek_to_date(year, isoweek) - local result = ret.year == ydate.year - and ret.month == ydate.month - and ret.day == ydate.day - print( - ret.year - .. "-" - .. ret.month - .. "-" - .. ret.day - .. " == " - .. ydate.year - .. "-" - .. ydate.month - .. "-" - .. ydate.day - .. " : " - .. tostring(result) - ) + print("*********** KW " .. isoweek .. " " .. year .. ": ") + -- local ret = M.weeknumber_to_date(year, isoweek) + local ret = M.isoweek_to_date(year, isoweek) + local result = ret.year == ydate.year + and ret.month == ydate.month + and ret.day == ydate.day + print( + ret.year + .. "-" + .. ret.month + .. "-" + .. ret.day + .. " == " + .. ydate.year + .. "-" + .. ydate.month + .. "-" + .. ydate.day + .. " : " + .. tostring(result) + ) end M.run_tests = function() - print(check_isoweek(2020, 1, { year = 2019, month = 12, day = 30 })) -- 30.12.2019 - print(check_isoweek(2020, 52, { year = 2020, month = 12, day = 21 })) -- 21.12.2020 - print(check_isoweek(2020, 53, { year = 2020, month = 12, day = 28 })) -- 28.12.2020 - print(check_isoweek(2021, 1, { year = 2021, month = 1, day = 4 })) -- 4.1.2020 - print(check_isoweek(2021, 52, { year = 2021, month = 12, day = 27 })) -- 27.12.2021 - print(check_isoweek(2022, 1, { year = 2022, month = 1, day = 3 })) -- 3.1.2022 + print(check_isoweek(2020, 1, { year = 2019, month = 12, day = 30 })) -- 30.12.2019 + print(check_isoweek(2020, 52, { year = 2020, month = 12, day = 21 })) -- 21.12.2020 + print(check_isoweek(2020, 53, { year = 2020, month = 12, day = 28 })) -- 28.12.2020 + print(check_isoweek(2021, 1, { year = 2021, month = 1, day = 4 })) -- 4.1.2020 + print(check_isoweek(2021, 52, { year = 2021, month = 12, day = 27 })) -- 27.12.2021 + print(check_isoweek(2022, 1, { year = 2022, month = 1, day = 3 })) -- 3.1.2022 end -- M.run_tests()