From 1bd06e5d3642970f7cde3c2edb01631603c0b67e Mon Sep 17 00:00:00 2001 From: Rene Schallner Date: Wed, 1 Dec 2021 05:35:12 +0100 Subject: [PATCH] we can now follow links to headings and paragraphs both globally and within specific notes! --- BACKLOG.md | 54 ++++++++-------------- README.md | 70 ++++++++++++++++++++++------ doc/telekasten.txt | 110 ++++++++++++++++++++++++++++++------------- lua/telekasten.lua | 113 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 248 insertions(+), 99 deletions(-) diff --git a/BACKLOG.md b/BACKLOG.md index b98dfa5..c8f69c7 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -46,44 +46,28 @@ ## Special links -- like this - - [[2021-11-27#Make it]] - - [[2021-11-27# it]] - - [[2021-11-27#^4ba88c]] - - block references only need to be local to the file - - for grepping, it makes sense for them to rather not collide - - we could use hex format of current time when creating them - - or some hash of the block - plus some time info +Everything behind a # is a search! -- also provide highlighting -- yank link to this heading -- yank link to this paragraph - - warning: appends ^xxxxxx to the paragraph, if not present +- [[#some heading]] -- will search for the heading 'some heading' -- can we jump to a specific line (col) from telescope? -- how can we jump to the heading / para in the preview? -- can we use live_grep or file_finder? - - con for live_grep: will find any file with the same heading - - --> pressing enter might jump to a wrong default - - con for file finder: - - maybe not possible to move preview to correct line - - better previewer? -- or shall everything be custom? - - only 1 file or a list of files containing the same heading, with the one under the link as default - - and a pre-viewer at correct line (cat new or so) +- [[some note#some heading]] -- will search for 'some note' in filns and within it the heading 'some heading' -`require('telescope.previewers').vim_buffer_vimgrep()`: -A previewer that is used to display a file and jump to the provided line. -It uses the `buffer_previewer` interface. To integrate this one into your -own picker make sure that the field `path` or `filename` and `lnum` is set -in each entry. If the latter is not present, it will default to the first -line. The preferred way of using this previewer is like this -`require('telescope.config').values.grep_previewer` This will respect user -configuration and will use `termopen_previewer` in case it's configured -that way. +- [[#^some-para-id]] -- will search for the ^para-id +- [[some note#^some-para-id]] -- will search for the ^para-id in the note whose filn matches some note -Also interesting: termopen_previewer! -`vim.fn.search` to jump to a specific line +If note is specified and cannot be found, it will be ignored and a global search triggered instead -> Start typing a link like you would normally. When the note you want is highlighted, press # instead of Enter and you'll see a list of headings in that file. Continue typing or navigate with arrow keys as before, press # again at each subheading you want to add, and Enter to complete the link. +## Supportive features + +- Yank link to heading +- Yank link to paragraph + +### Insert links...? +Maybe as an extra action: link with heading -> telescope popup for headings +Maybe as an extra action: link with paragraph -> telescope popup for paragraphs + +Global: link to heading: search through all headings +Global: link to paragraphs: search all paragraphs: maybe overkill +Global: link to already postfixed paragraphs +Global: Place paragraph id diff --git a/README.md b/README.md index 7f59f74..c45af38 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ mixing it with a journal, based on [telescope.nvim](https://github.com/nvim-tele - following a link to a non-existing note can also create the missing note (optional) - find notes **that link back to your notes** - find other notes that link to the same note as the link under the cursor +- support for **links to headings or specific paragraphs** within specific notes or globally (see [link + notation](#20-link-notation)) - calendar support - paste images from clipboard - toggle [ ] todo status of line @@ -78,11 +80,12 @@ of being able to edit it. * [0.3 Configure your own colors](#03-configure-your-own-colors) * [1. Get Help](#1-get-help) * [2. Use it](#2-use-it) - * [3. Note templates](#3-note-templates) - * [3.1 Template files](#31-template-files) - * [3.2 Using the calendar](#32-using-the-calendar) -* [4. Bind it](#4-bind-it) -* [5. The hardcoded stuff](#5-the-hardcoded-stuff) + * [2.0 Link notation](#20-link-notation) + * [2.1 Note templates](#21-note-templates) + * [2.1.1 Template files](#211-template-files) + * [2.2 Using the calendar](#22-using-the-calendar) +* [3. Bind it](#3-bind-it) +* [4. The hardcoded stuff](#4-the-hardcoded-stuff) @@ -296,7 +299,7 @@ or .. **just use telescope**: `:Telescope help_tags` and search for `telekasten` ## 2. Use it -The plugin defines the following functions. +The plugin defines the following functions: - `new_note()` : prompts for title and creates new note by template, then shows it in Telescope - `new_templated_note()` : prompts for title and then uses telescope for choosing a template. When a template is @@ -314,10 +317,12 @@ The plugin defines the following functions. - `insert_link()` : select a note by name, via Telescope, and place a `[[link]]` at the current cursor position - **note**: - this function accepts a parameter `{i}`. If `true`, it will enter input mode by pressing the 'A' key. This is - useful when being used in a simple `inoremap` key mapping like shown in [Bind it](#4-bind-it). + useful when being used in a simple `inoremap` key mapping like shown in [Bind it](#3-bind-it). - example: `insert_link({ i=true })` - `follow_link()`: take text between brackets (linked note) and open a Telescope file finder with it: selects note to open (incl. preview) - with optional note creation for non-existing notes, honoring the configured template + - **note**: + - notes linked to with headings or paragraph IDs **will not be created automatically**. See below for link notation. - `yank_notelink()` : yank a link to the current note, ready to paste - `show_calendar()` : opens up the calendar in a properly-sized vertical split at the very right - `paste_img_and_link()` : pastes an image from the clipboard into a file under `image_subdir` and inserts a link to it @@ -325,7 +330,7 @@ The plugin defines the following functions. - `toggle_todo()` : turn a line into a `- [ ] ` line, or toggle between `- [ ]`, `- [x]`, and `- `. - **note**: - this function accepts a parameter `{i}`. If `true`, it will enter input mode by pressing the 'A' key. This is - useful when being used in a simple `inoremap` key mapping like shown in [Bind it](#4-bind-it). + useful when being used in a simple `inoremap` key mapping like shown in [Bind it](#3-bind-it). - example: `toggle_todo({ i=true })` - `show_backlinks()` : opens a telescope search for notes that `[[link]]` back to the current note. - `find_friends()` : opens a telescope search for notes that also `[[link]]` to the link under the cursor. @@ -335,7 +340,7 @@ The plugin defines the following functions. - if the `telescope-media-files.nvim` plugin is installed, **a preview of images / media files will be given** during the search. - this function accepts a parameter `{i}`. If `true`, it will enter input mode by pressing the 'A' key. This is - useful for being able to continue to type after link insertion. See also: [Bind it](#4-bind-it). + useful for being able to continue to type after link insertion. See also: [Bind it](#3-bind-it). - example: `insert_link({ i=true })` - `preview_img()` : uses the `telescope-media-files.nvim` extension to preview the image / media file under the cursor of a markdown image link: `![](path/to/img.png)`. The cursor must be between `(the two parenthesis)`. @@ -348,7 +353,44 @@ To use one of the functions above, just run them with the `:lua ...` command. :lua require("telekasten").find_daily_notes() ``` -### 3. Note templates +### 2.0 Link notation + +The following links are supported: + +```markdown +# Note links + +- [[A cool title]] ................. links to the note named 'A cool title' +- [[A cool title#Heading 27]] ...... links to the heading 'Heading 27' within the note + named 'A cool title' +- [[A cool title#^xxxxxxxx]] ....... links to the paragraph with id ^xxxxxxxx within the note + named 'A cool title' +- [[#Heading 27]] .................. links to the heading 'Heading 27' within all notes +- [[#^xxxxxxxx]] ................... links to the paragraph with id ^xxxxxxxx within all notes + +## Optionally, notes can live in specific sub-directories + +- [[some/subdirectory/A cool title]] ................. links to note named 'A cool title' + in some/subdirectory +- [[some/subdirectory/A cool title#Heading 27]] ...... links to the heading 'Heading 27' within + the note named 'A cool title' + in some/subdirectory +- [[some/subdirectory/A cool title#^xxxxxxxx]] ....... links to the paragraph with + id ^xxxxxxxx within the note named + 'A cool title' in some/subdirectory + +# Media links + +Use these for images, PDF files, videos. If telescope-media-files is installed, these can +be previewed. + +- ![optional title](path/to/file) ... links to the file `path/to/file` +``` + +Note that notes linked to with headings or paragraph IDs **will not be created automatically**. + + +### 2.1 Note templates The functions `goto_today`, `goto_thisweek`, `find_daily_notes`, `find_weekly_notes`, and `follow_link` can create non-existing notes. This allows you to 'go to today' without having to create today's note beforehand. When you just @@ -369,7 +411,7 @@ The following table shows which command relies on what config option: If the associated option is `true`, non-existing notes will be created. -#### 3.1 Template files +#### 2.1.1 Template files The options `template_new_note`, `template_new_daily`, and `template_new_weekly` are used to specify the paths to template text files that are used for creating new notes. @@ -426,7 +468,7 @@ date: {{hdate}} ## Sunday link ``` -### 3.2 Using the calendar +### 2.2 Using the calendar When invoking `show_calendar()`, a calendar showing the previous, current, and next month is shown at the right side of vim. @@ -443,7 +485,7 @@ command in vim: :CalendarT ``` -## 4. Bind it +## 3. Bind it Usually, you would set up some key bindings, though: ```vim @@ -482,7 +524,7 @@ hi tkBrackets ctermfg=gray hi tkHighlight ctermbg=yellow ctermfg=darkred cterm=bold ``` -## 5. The hardcoded stuff +## 4. The hardcoded stuff Currently, the following things are hardcoded: - the file naming format for daily note files: `YYYY-MM-DD.ext` (e.g. `2021-11-21.md`) diff --git a/doc/telekasten.txt b/doc/telekasten.txt index 903b15e..4940ded 100644 --- a/doc/telekasten.txt +++ b/doc/telekasten.txt @@ -28,8 +28,9 @@ CONTENTS 1. Setup ......................... |telekasten.setup| 2. Colors ........................ |telekasten.colors| 3. Usage ......................... |telekasten.usage| - 3.1 Templates ................ |telekasten.templates| - 3.2 Calendar ................. |telekasten.calendar| + 3.1 Link Notation ............ |telekasten.link_notation| + 3.2 Templates ................ |telekasten.templates| + 3.3 Calendar ................. |telekasten.calendar| 4. Suggested mappings ............ |telekasten.mappings| 5. Credits ....................... |telekasten.credits| @@ -464,7 +465,50 @@ telekasten.preview_img()~ This requires the `telescope-media-files.nvim` plugin to be installed. -------------------------------------------------------------------------------- -Section 3.1: Templates *telekasten.templates* +Section 3.1: Link notation *telekasten.link_notation* + +The following links are supported: + +Note links~ + +`[[A cool title]]` ................. links to the note named 'A cool title' + +`[[A cool title#Heading 27]]` ...... links to the heading 'Heading 27' within + the note named 'A cool title' + +`[[A cool title#^xxxxxxxx]]` ....... links to the paragraph with id ^xxxxxxxx + within the note named 'A cool title' + +`[[#Heading 27]]` .................. links to the heading 'Heading 27' within + all notes + +`[[#^xxxxxxxx]]` ................... links to the paragraph with id ^xxxxxxxx + within all notes + +Optionally, notes can live in specific sub-directories: + +`[[some/subdirectory/A cool title]]`: links to note named 'A cool title' + in some/subdirectory + +`[[some/subdirectory/A cool title#Heading 27]]`: links to the heading + 'Heading 27' within the note named + 'A cool title' in some/subdirectory + +`[[some/subdirectory/A cool title#^xxxxxxxx]]` : links to the paragraph + with id ^xxxxxxxx within the note named + 'A cool title' in some/subdirectory + +Media links~ +Use these for images, PDF files, videos. If telescope-media-files is installed, +these can be previewed. + +`![optional title](path/to/file)` ... links to the file `path/to/file` + +Note that notes linked to with headings or paragraph IDs **will not be created +automatically**. + +-------------------------------------------------------------------------------- +Section 3.2: Templates *telekasten.templates* Telekasten.nvim can create non-existing notes, providing this is enabled in the settings - which it is by default. Auto-creation of notes is useful when @@ -549,7 +593,7 @@ And finally, what a template for weekly notes could look like: < -------------------------------------------------------------------------------- -Section 3.2: Calendar *telekasten.calendar* +Section 3.3: Calendar *telekasten.calendar* When invoking `show_calendar()`, a calendar showing the previous, current, and next month is shown at the right side of vim. @@ -577,39 +621,39 @@ Telekasten.nvim does not come with pre-defined mappings. However, here are some suggestions: > - nnoremap zf :lua require('telekasten').find_notes() - nnoremap zd :lua require('telekasten').find_daily_notes() - nnoremap zg :lua require('telekasten').search_notes() - nnoremap zz :lua require('telekasten').follow_link() - nnoremap zt :lua require('telekasten').goto_today() - nnoremap zw :lua require('telekasten').find_weekly_notes() - nnoremap zn :lua require('telekasten').new_note() - nnoremap zN :lua require('telekasten').new_templated_note() - nnoremap zy :lua require('telekasten').yank_notelink() - nnoremap zc :lua require('telekasten').show_calendar() - nnoremap zC :CalendarT - nnoremap zi :lua require('telekasten').paste_img_and_link() - nnoremap zt :lua require('telekasten').toggle_todo() - nnoremap zb :lua require('telekasten').show_backlinks() - nnoremap zF :lua require('telekasten').find_friends() - nnoremap zI :lua require('telekasten').insert_img_link({ i=true }) - nnoremap zp :lua require('telekasten').preview_img() + nnoremap zf :lua require('telekasten').find_notes() + nnoremap zd :lua require('telekasten').find_daily_notes() + nnoremap zg :lua require('telekasten').search_notes() + nnoremap zz :lua require('telekasten').follow_link() + nnoremap zt :lua require('telekasten').goto_today() + nnoremap zw :lua require('telekasten').find_weekly_notes() + nnoremap zn :lua require('telekasten').new_note() + nnoremap zN :lua require('telekasten').new_templated_note() + nnoremap zy :lua require('telekasten').yank_notelink() + nnoremap zc :lua require('telekasten').show_calendar() + nnoremap zC :CalendarT + nnoremap zi :lua require('telekasten').paste_img_and_link() + nnoremap zt :lua require('telekasten').toggle_todo() + nnoremap zb :lua require('telekasten').show_backlinks() + nnoremap zF :lua require('telekasten').find_friends() + nnoremap zI :lua require('telekasten').insert_img_link({ i=true }) + nnoremap zp :lua require('telekasten').preview_img() - " we could define [[ in **insert mode** to call insert link - " inoremap [[ :lua require('telekasten').insert_link() - " alternatively: leader [ - inoremap [ :lua require('telekasten').insert_link({ i=true }) - inoremap zt :lua require('telekasten').toggle_todo({ i=true }) + " we could define [[ in **insert mode** to call insert link + " inoremap [[ :lua require('telekasten').insert_link() + " alternatively: leader [ + inoremap [ :lua require('telekasten').insert_link({ i=true }) + inoremap zt :lua require('telekasten').toggle_todo({ i=true }) - " the following are for syntax-coloring [[links]] and ==highlighted text== - " (see the section about coloring in README.md) + " the following are for syntax-coloring [[links]] and ==highlighted text== + " (see the section about coloring in README.md) - " colors suitable for gruvbox color scheme - hi tkLink ctermfg=72 cterm=bold,underline - hi tkBrackets ctermfg=gray + " colors suitable for gruvbox color scheme + hi tkLink ctermfg=72 cterm=bold,underline + hi tkBrackets ctermfg=gray - " highlight ==highlighted== text - hi tkHighlight ctermbg=yellow ctermfg=darkred cterm=bold + " highlight ==highlighted== text + hi tkHighlight ctermbg=yellow ctermfg=darkred cterm=bold < ================================================================================ diff --git a/lua/telekasten.lua b/lua/telekasten.lua index da3e32f..f423f5a 100644 --- a/lua/telekasten.lua +++ b/lua/telekasten.lua @@ -7,6 +7,7 @@ local conf = require("telescope.config").values local scan = require("plenary.scandir") local utils = require("telescope.utils") local previewers = require("telescope.previewers") +local make_entry = require("telescope.make_entry") -- declare locals for the nvim api stuff to avoid more lsp warnings local vim = vim @@ -348,7 +349,7 @@ local function find_files_sorted(opts) return popup_opts.preview end - local function make_entry(entry) + local function entry_maker(entry) local iconic_entry = {} iconic_entry.value = entry iconic_entry.ordinal = entry @@ -364,7 +365,7 @@ local function find_files_sorted(opts) local picker = pickers.new(opts, { finder = finders.new_table({ results = file_list, - entry_maker = make_entry, + entry_maker = entry_maker, }), sorter = conf.generic_sorter(opts), @@ -469,25 +470,103 @@ local function FollowLink(opts) opts = opts or {} vim.cmd("normal yi]") local title = vim.fn.getreg('"0') + local search_mode = "files" - -- check if fname exists anywhere - local fexists = file_exists(M.Cfg.weeklies .. "/" .. title .. M.Cfg.extension) - fexists = fexists or file_exists(M.Cfg.dailies .. "/" .. title .. M.Cfg.extension) - fexists = fexists or file_exists(M.Cfg.home .. "/" .. title .. M.Cfg.extension) + local parts = vim.split(title, "#") + local filename - if - (fexists ~= true) and ((opts.follow_creates_nonexisting == true) or M.Cfg.follow_creates_nonexisting == true) - then - local fname = M.Cfg.home .. "/" .. title .. M.Cfg.extension - create_note_from_template(title, fname, M.note_type_templates.normal) + -- if there is a # + if #parts ~= 1 then + search_mode = "heading" + title = parts[2] + filename = parts[1] + parts = vim.split(title, "%^") + if #parts ~= 1 then + search_mode = "para" + title = parts[2] + end end - find_files_sorted({ - prompt_title = "Follow link to note...", - cwd = M.Cfg.home, - default_text = title, - find_command = M.Cfg.find_command, - }) + if #filename > 0 then + local fexists = false + if file_exists(M.Cfg.weeklies .. "/" .. filename .. M.Cfg.extension) then + filename = M.Cfg.weeklies .. "/" .. filename .. M.Cfg.extension + fexists = true + end + if file_exists(M.Cfg.dailies .. "/" .. filename .. M.Cfg.extension) then + filename = M.Cfg.dailies .. "/" .. filename .. M.Cfg.extension + fexists = true + end + if file_exists(M.Cfg.home .. "/" .. filename .. M.Cfg.extension) then + filename = M.Cfg.home .. "/" .. filename .. M.Cfg.extension + fexists = true + end + + if fexists == false then + -- print("error") + filename = "" + end + end + + if search_mode == "files" then + -- check if fname exists anywhere + local fexists = file_exists(M.Cfg.weeklies .. "/" .. title .. M.Cfg.extension) + fexists = fexists or file_exists(M.Cfg.dailies .. "/" .. title .. M.Cfg.extension) + fexists = fexists or file_exists(M.Cfg.home .. "/" .. title .. M.Cfg.extension) + + if + (fexists ~= true) + and ((opts.follow_creates_nonexisting == true) or M.Cfg.follow_creates_nonexisting == true) + then + local fname = M.Cfg.home .. "/" .. title .. M.Cfg.extension + create_note_from_template(title, fname, M.note_type_templates.normal) + end + + find_files_sorted({ + prompt_title = "Follow link to note...", + cwd = M.Cfg.home, + default_text = title, + find_command = M.Cfg.find_command, + }) + end + + if search_mode ~= "files" then + local search_pattern = title + local cwd = M.Cfg.home + + opts.cwd = cwd + + 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 #filename > 0 then + table.insert(search_command, filename) + else + table.insert(search_command, cwd) + end + + local ret = vim.tbl_flatten({ search_command }) + return ret + end, make_entry.gen_from_vimgrep(opts), opts.max_results, opts.cwd) + + builtin.live_grep({ + cwd = cwd, + prompt_title = "Notes referencing `" .. title .. "`", + default_text = search_pattern, + -- 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, + }) + end end --