├── LICENSE ├── README.md ├── autoload └── zettel │ ├── fzf.vim │ └── vimwiki.vim ├── doc └── zettel.txt ├── ftplugin └── vimwiki │ └── zettel.vim ├── plugin └── zettel.vim └── tests ├── README.md ├── resources └── templ.tpl ├── test_vimrc └── zettel_creation.vader /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Michal Hoftich 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The `vim-zettel` package 2 | 3 | This is a Vim plugin that implements ideas of the 4 | [Zettelkasten](https://zettelkasten.de/) method using Vimwiki. 5 | It supports both Vimwiki and Markdown syntaxes. 6 | 7 | Main features: 8 | 9 | - customizable filenames (date and time, title, consecutive numbering) 10 | - links always show titles, regardless of the actual filename 11 | - fulltext support using FZF for searching and hyperlinking 12 | - search your Zettelkasten from LaTeX or Markdown documents and insert selected notes to the document 13 | - template support 14 | - automatically updated tag index 15 | - backlinks 16 | 17 | You can read the full documentation using 18 | 19 | :help vim-zettel 20 | 21 | command in Vim after package installation. 22 | 23 | # Install 24 | 25 | ## Using Vundle: 26 | 27 | 28 | Plugin 'vimwiki/vimwiki' 29 | Plugin 'junegunn/fzf' 30 | Plugin 'junegunn/fzf.vim' 31 | Plugin 'michal-h21/vim-zettel' 32 | 33 | 34 | ## Using [vim-plug](https://github.com/junegunn/vim-plug) 35 | 36 | call plug#begin() 37 | Plug 'vimwiki/vimwiki' 38 | Plug 'junegunn/fzf' 39 | Plug 'junegunn/fzf.vim' 40 | Plug 'michal-h21/vim-zettel' 41 | call plug#end() 42 | 43 | [Silver Searcher](https://github.com/ggreer/the_silver_searcher) is used for searching in the notes by default. 44 | The used command can be changed by setting the `g:zettel_fzf_command` variable. 45 | 46 | # Usage 47 | 48 | `Vim-zettel` adds some commands and mappings on top of 49 | [Vimwiki](http://vimwiki.github.io/). See Vimwiki documentation on how to set up a 50 | basic wiki and navigate it. 51 | 52 | # Known issues 53 | 54 | - [ZettelOpen and ZettelSearch fail because of an "Error running fzf..."](https://github.com/michal-h21/vim-zettel/issues/58). 55 | This issue is probably caused by too old version of 56 | [FZF](https://github.com/junegunn/fzf) on your machine. Try to update both 57 | `fzf` binary and `fzf.vim`. 58 | 59 | # Related packages 60 | 61 | The following packages may be useful in conjunction with Vimwiki and Vim-zettel: 62 | 63 | - [Notational FZF](https://github.com/alok/notational-fzf-vim) - fast searching 64 | notes with preview window. Similar functionality is now built in in 65 | `Vim-Zettel` using `:ZettelOpen` command. 66 | 67 | To search in the Zettelkasten, set the following variable with path to the Zettelkasten directory in `.vimrc`: 68 | 69 | let g:nv_search_paths = ['/path/to/zettelkasten/dir'] 70 | 71 | - [Vimwiki-sync](https://github.com/michal-h21/vimwiki-sync) - automatically commit changes in wiki and synchronize them with external Git repository. 72 | -------------------------------------------------------------------------------- /autoload/zettel/fzf.vim: -------------------------------------------------------------------------------- 1 | " initialize default wiki 2 | call zettel#vimwiki#initialize_wiki_number() 3 | " get active VimWiki directory 4 | 5 | " FZF command used in the ZettelSearch command 6 | if !exists('g:zettel_fzf_command') 7 | let g:zettel_fzf_command = "ag" 8 | endif 9 | 10 | if !exists('g:zettel_fzf_options') 11 | let g:zettel_fzf_options = ['--exact', '--tiebreak=end'] 12 | endif 13 | 14 | " vimwiki files can have titles in the form of %title title content 15 | function! s:get_zettel_title(filename) 16 | return zettel#vimwiki#get_title(a:filename) 17 | endfunction 18 | 19 | " fzf returns selected filename and matched line from the file, we need to 20 | " strip the unnecessary text to get just the filename 21 | function! s:get_fzf_filename(line) 22 | " line is in the following format: 23 | " filename:linenumber:number:matched_text 24 | " remove spurious text from the line to get just the filename 25 | let filename = substitute(a:line, ":[0-9]\*:[0-9]\*:.\*$", "", "") 26 | return filename 27 | endfunction 28 | 29 | " get clean wiki name from a filename 30 | function! s:get_wiki_file(filename) 31 | return substitute(a:filename, vimwiki#vars#get_wikilocal('ext', vimwiki#vars#get_bufferlocal('wiki_nr')) . '$' , '', '') 32 | 33 | endfunction 34 | 35 | function! s:get_line_number(line) 36 | " line is in the following format: 37 | " filename:linenumber:number:matched_text 38 | let linenumber = matchstr(a:line, ":\\zs[0-9]\*\\ze:[0-9]\*") 39 | return linenumber 40 | endfunction 41 | 42 | " execute fzf function 43 | function! zettel#fzf#execute_fzf(a, b, options) 44 | " search only files in the current wiki syntax 45 | let l:fullscreen = 0 46 | " initialize directory for search if it is missing in options 47 | if get(a:options, "dir") == 0 48 | let wiki_number = getbufvar("%","vimwiki_wiki_nr") 49 | if wiki_number == -1 50 | call zettel#vimwiki#initialize_wiki_number() 51 | endif 52 | call extend(a:options, {"dir":zettel#vimwiki#path()}) 53 | endif 54 | if g:zettel_fzf_command == "ag" 55 | " filetype pattern for ag: -G 'ext$' 56 | let search_ext = "-G '" . substitute(vimwiki#vars#get_wikilocal('ext'), '\.', '', '') . "$'" 57 | let query = empty(a:a) ? '^(?=.)' : a:a 58 | let l:fzf_command = g:zettel_fzf_command . ' --color --smart-case --nogroup --column ' . shellescape(query) " --ignore-case --smart-case 59 | else 60 | " use grep method for other commands 61 | let search_ext = "*" . vimwiki#vars#get_wikilocal('ext') 62 | let l:fzf_command = g:zettel_fzf_command . " " . shellescape(a:a) 63 | endif 64 | 65 | " it seems that when we limit search only to current extension, FZF fails on 66 | " Windows. I couldn't find a better solution than to remove the seach 67 | " extension completely 68 | " return fzf#vim#grep(l:fzf_command . ' ' . search_ext, 1, fzf#vim#with_preview(a:options), l:fullscreen) 69 | return fzf#vim#grep(l:fzf_command, fzf#vim#with_preview(a:options), l:fullscreen) 70 | endfunction 71 | 72 | 73 | " insert link for the searched zettel in the current note 74 | function! zettel#fzf#wiki_search(lines,...) 75 | let links = [] 76 | " create links for all selected notes 77 | for line in zettel#fzf#get_files(a:lines) 78 | let filename = s:get_fzf_filename(line) 79 | let title = s:get_zettel_title(filename) 80 | " insert the filename and title into the current buffer 81 | let wikiname = s:get_wiki_file(filename) 82 | " if the title is empty, the link will be hidden by vimwiki, use the filename 83 | " instead 84 | if empty(title) 85 | let title = wikiname 86 | end 87 | let link = zettel#vimwiki#format_search_link(wikiname, title) 88 | call add(links, link) 89 | endfor 90 | " join all selected links with a comma 91 | let link = join(links, ", ") 92 | " replace the [[ with selected link and title 93 | let line = getline('.') 94 | let caret = col('.') 95 | call setline('.', strpart(line, 0, caret - 2) . link . strpart(line, caret)) 96 | call cursor(line('.'), caret + len(link) - 2) 97 | call feedkeys("a", "n") 98 | endfunction 99 | 100 | 101 | " search for a note and the open it in Vimwiki 102 | function! zettel#fzf#search_open(line,...) 103 | let filename = s:get_fzf_filename(a:line) 104 | let wikiname = s:get_wiki_file(filename) 105 | let linenumber = s:get_line_number(a:line) 106 | if !empty(wikiname) 107 | " open the selected note using this Vimwiki function 108 | " it will keep the history of opened pages, so you can go to the previous 109 | " page using backspace 110 | call vimwiki#base#open_link(':e ', wikiname, zettel#vimwiki#path()) 111 | " scroll to the selected line 112 | if linenumber =~# '^\d\+$' 113 | call cursor(linenumber, 1) 114 | endif 115 | endif 116 | endfunction 117 | 118 | " search for a file, and use visually selected text as a title 119 | function! zettel#fzf#title_selected(line, ...) 120 | let filename = s:get_fzf_filename(a:line) 121 | let wikiname = s:get_wiki_file(filename) 122 | " this code is reused from zettel#vimwiki#zettel_new_selected() 123 | " \\\\0 contains the selected text 124 | execute "normal! :'<,'>s/\\%V.*\\%V./" . zettel#vimwiki#format_link( wikiname, "\\\\0") ."\\" 125 | endfunction 126 | 127 | " get options for fzf#vim#with_preview function 128 | " pass empty dictionary {} if you don't want additinal_options 129 | function! zettel#fzf#preview_options(sink_function, additional_options) 130 | let options = {'sink':function(a:sink_function), 131 | \'down': '~40%', 132 | \'dir':zettel#vimwiki#path(), 133 | \'options':g:zettel_fzf_options} 134 | " make it possible to pass additional options that overwrite the default 135 | " ones 136 | let options = extend(options, a:additional_options) 137 | return options 138 | endfunction 139 | 140 | " helper function to open FZF preview window and pass one selected file to a 141 | " sink function. useful for opening found files 142 | function! zettel#fzf#sink_onefile(params, sink_function,...) 143 | " get optional argument that should contain additional options for the fzf 144 | " preview window 145 | let additional_options = get(a:, 1, {}) 146 | call zettel#fzf#execute_fzf(a:params, 147 | \'--skip-vcs-ignores', fzf#vim#with_preview(zettel#fzf#preview_options(a:sink_function, additional_options))) 148 | endfunction 149 | 150 | " call FZF with a function that expects multiple selected values 151 | function! zettel#fzf#sink_multifile(params, sink_function,...) 152 | " get optional argument that should contain additional options for the fzf 153 | " preview window 154 | let additional_options = get(a:, 1, {}) 155 | " we must move the sink function to sink*, which supports multiple selected 156 | " values 157 | let options = zettel#fzf#preview_options(a:sink_function, additional_options) 158 | unlet options.sink 159 | let options["sink*"] = function(a:sink_function) 160 | call zettel#fzf#execute_fzf(a:params, 161 | \'--skip-vcs-ignores', fzf#vim#with_preview(options)) 162 | endfunction 163 | 164 | " open wiki page using FZF search 165 | function! zettel#fzf#execute_open(params) 166 | call zettel#fzf#sink_onefile(a:params, 'zettel#fzf#search_open') 167 | endfunction 168 | 169 | " return list of unique wiki pages selected in FZF 170 | function! zettel#fzf#get_files(lines) 171 | " remove duplicate lines 172 | let new_list = [] 173 | for line in a:lines 174 | if line !="" 175 | let new_list = add(new_list, s:get_fzf_filename(line)) 176 | endif 177 | endfor 178 | return uniq(new_list) 179 | endfunction 180 | 181 | " map between Vim filetypes and Pandoc output formats 182 | let s:supported_formats = { 183 | \"tex":"latex", 184 | \"latex":"latex", 185 | \"markdown":"markdown", 186 | \"wiki":"vimwiki", 187 | \"md":"markdown", 188 | \"org":"org", 189 | \"html":"html", 190 | \"default":"markdown", 191 | \} 192 | 193 | " this global variable can hold additional mappings between Vim and Pandoc 194 | if exists('g:export_formats') 195 | let s:supported_formats = extend(s:supported_formats, g:export_formats) 196 | endif 197 | 198 | " return section title depending on the syntax 199 | function! s:make_section(title, ft) 200 | if a:ft ==? "md" 201 | return "# " . a:title 202 | else 203 | return "= " . a:title . " =" 204 | endif 205 | endfunction 206 | 207 | " this function is just a test for retrieving multiple results from FZF. see 208 | " plugin/zettel.vim for call example 209 | function! zettel#fzf#insert_note(lines) 210 | " get Pandoc output format for the current file filetype 211 | let output_format = get(s:supported_formats,&filetype, "markdown") 212 | let lines_to_convert = [] 213 | let input_format = "vimwiki" 214 | for line in zettel#fzf#get_files(a:lines) 215 | " convert all files to the destination format 216 | let filename = zettel#vimwiki#path() . line 217 | let ext = fnamemodify(filename, ":e") 218 | " update the input format 219 | let input_format = get(s:supported_formats, ext, "vimwiki") 220 | " convert note title to section 221 | let sect_title = s:make_section( zettel#vimwiki#get_title(filename), ext) 222 | " find start of the content 223 | let header_end = zettel#vimwiki#find_header_end(filename) 224 | let lines_to_convert = add(lines_to_convert, sect_title) 225 | let i = 0 226 | " read note contents without metadata header 227 | for fline in readfile(filename) 228 | if i >= header_end 229 | let lines_to_convert = add(lines_to_convert, fline) 230 | endif 231 | let i = i + 1 232 | endfor 233 | endfor 234 | let command_to_execute = "pandoc -f " . input_format . " -t " . output_format 235 | echom("Executing :" .command_to_execute) 236 | let result = systemlist(command_to_execute, lines_to_convert) 237 | call append(line("."), result) 238 | " Todo: move this to execute_open 239 | call setqflist(map(zettel#fzf#get_files(a:lines), '{ "filename": v:val }')) 240 | endfunction 241 | 242 | if !exists('g:zettel_bufflist_format') 243 | let g:zettel_bufflist_format = " - %title" 244 | endif 245 | 246 | " buffer handling functions 247 | " list zettel titles in buffers 248 | function! zettel#fzf#buflist() 249 | " redirect :ls to a variable 250 | redir => ls 251 | silent ls 252 | redir END 253 | let lines = split(ls, '\n') 254 | let newlines = [] 255 | " run over buffers 256 | for line in lines 257 | let filename = matchstr(line, '\v"\zs([^"]+)') 258 | " we need to expand the matched filename to a full path 259 | let fullname = fnamemodify(filename, ":p") 260 | " use vim-zettel command to read title 261 | let title = zettel#vimwiki#get_title(fullname) 262 | " we don't need the filename in listings 263 | let line = substitute(line, '\".*', '', '') 264 | " if we cannot find title, use filename instead 265 | if title ==? "" 266 | let title = filename 267 | let filename = "" 268 | endif 269 | let template = substitute(g:zettel_bufflist_format, "%title", title, "g") 270 | let template = substitute(template, "%filename", filename, "g") 271 | " add title to the result of :ls 272 | call add(newlines, line . ' ' . template) 273 | endfor 274 | return newlines 275 | endfunction 276 | 277 | " switch to a buffer 278 | function! zettel#fzf#bufopen(e) 279 | execute 'buffer' matchstr(a:e, '^[ 0-9]*') 280 | endfunction 281 | -------------------------------------------------------------------------------- /autoload/zettel/vimwiki.vim: -------------------------------------------------------------------------------- 1 | " source: https://stackoverflow.com/a/6271254/2467963 2 | " get text of the visual selection 3 | function! s:get_visual_selection() 4 | " Why is this not a built-in Vim script function?! 5 | let [line_start, column_start] = getpos("'<")[1:2] 6 | let [line_end, column_end] = getpos("'>")[1:2] 7 | let lines = getline(line_start, line_end) 8 | if len(lines) == 0 9 | return '' 10 | endif 11 | let lines[-1] = lines[-1][: column_end - (&selection == 'inclusive' ? 1 : 2)] 12 | let lines[0] = lines[0][column_start - 1:] 13 | return join(lines, "\n") 14 | endfunction 15 | 16 | function! zettel#vimwiki#get_visual_selection() 17 | return get_visual_selection() 18 | endfunction 19 | 20 | " this function is useful for comands in plugin/zettel.vim 21 | " set number of the active wiki 22 | function! zettel#vimwiki#set_active_wiki(number) 23 | " this buffer value is used by vimwiki#vars#get_wikilocal to retrieve 24 | " the current wiki number 25 | call setbufvar("%","vimwiki_wiki_nr", a:number) 26 | endfunction 27 | 28 | " get the first non emtpy wiki idx from the options 29 | function! zettel#vimwiki#get_wiki_nr_from_options() 30 | let i = 0 31 | let sel_index = 0 32 | while i < len(g:zettel_options) 33 | if len(keys(g:zettel_options[i])) > 0 34 | let sel_index = i 35 | break 36 | endif 37 | let i += 1 38 | endwhile 39 | return sel_index 40 | endfunction 41 | 42 | " set default wiki number. it is set to -1 when no wiki is initialized 43 | " we will set it to first wiki in wiki list, with number 0 44 | function! zettel#vimwiki#initialize_wiki_number() 45 | if getbufvar("%", "vimwiki_wiki_nr") == -1 46 | if !exists('g:zettel_options') && !exists('g:zettel_default_wiki_nr') 47 | call zettel#vimwiki#set_active_wiki(0) 48 | elseif exists('g:zettel_default_wiki_nr') 49 | call zettel#vimwiki#set_active_wiki(g:zettel_default_wiki_nr) 50 | else 51 | let idx = zettel#vimwiki#get_wiki_nr_from_options() 52 | echom("setting wiki number: " . idx) 53 | if idx > -1 54 | call zettel#vimwiki#set_active_wiki(idx) 55 | else 56 | call zettel#vimwiki#set_active_wiki(0) 57 | endif 58 | endif 59 | endif 60 | endfunction 61 | call zettel#vimwiki#initialize_wiki_number() 62 | 63 | " get user option for the current wiki 64 | " it seems that it is not possible to set custom options in g:vimwiki_list 65 | " so we need to use our own options 66 | function! zettel#vimwiki#get_option(name, ...) 67 | if !exists('g:zettel_options') 68 | return "" 69 | endif 70 | " the options for particular wikis must be in the same order as wiki 71 | " definitions in g:vimwiki_list 72 | let idx = a:0 ? a:1 : vimwiki#vars#get_bufferlocal('wiki_nr') 73 | let option_number = "g:zettel_options[" . idx . "]" 74 | if exists(option_number) 75 | if exists(option_number . "." . a:name) 76 | return g:zettel_options[idx][a:name] 77 | endif 78 | endif 79 | return "" 80 | endfunction 81 | 82 | " markdown test for front matter end 83 | function! s:test_header_end_md(line, i) 84 | if a:i > 0 85 | let pos = matchstrpos(a:line, "^\s*---") 86 | return pos[1] 87 | endif 88 | return -1 89 | endfunction 90 | 91 | " vimwiki test fot front matter end 92 | function! s:test_header_end_wiki(line, i) 93 | " return false for all lines that start with % character 94 | let pos = matchstrpos(a:line,"^\s*%") 95 | if pos[1] > -1 96 | return -1 97 | endif 98 | " first line which is not tag should be selected 99 | return 0 100 | endfunction 101 | 102 | let s:test_header_end = function(vimwiki#vars#get_wikilocal('syntax', vimwiki#vars#get_bufferlocal('wiki_nr')) ==? 'markdown' ? 'test_header_end_md' : 'test_header_end_wiki') 103 | 104 | " variables that depend on the wiki syntax 105 | if vimwiki#vars#get_wikilocal('syntax', vimwiki#vars#get_bufferlocal('wiki_nr')) ==? 'markdown' 106 | " add file extension when g:vimwiki_markdown_link_ext is set 107 | if exists("g:vimwiki_markdown_link_ext") && g:vimwiki_markdown_link_ext == 1 108 | let s:link_format = "[%title](%link.md)" 109 | " TODO: s:link_stub used to be different than s:link_format, but it is no longer 110 | " the case. maybe we can get rid of it? 111 | let s:link_stub = "[%title](%link.md)" 112 | else 113 | let s:link_format = "[%title](%link)" 114 | let s:link_stub = "[%title](%link)" 115 | endif 116 | let s:header_format = "%s: %s" 117 | let s:header_delimiter = "---" 118 | let s:insert_mode_title_format = "``l" 119 | let s:grep_link_pattern = '\(.*%s\.?m?d?\)' " match filename in parens. including optional .md extension 120 | let s:section_pattern = "# %s" 121 | else 122 | let s:link_format = "[[%link|%title]]" 123 | let s:link_stub = "[[%link|%title]]" 124 | let s:header_format = "%%%s %s" 125 | let s:header_delimiter = "" 126 | let s:insert_mode_title_format = "h" 127 | " match the wiki id, terminated by |, # or ], to prevent false matches 128 | let s:grep_link_pattern = '"\[\[.*%s[|#\]]"' 129 | " let s:grep_link_pattern = '"\[\[.*%s.*\]\]"' 130 | let s:section_pattern = "= %s =" 131 | endif 132 | 133 | " enable overriding of 134 | if exists("g:zettel_link_format") 135 | let s:link_format = g:zettel_link_format 136 | let s:link_stub = g:zettel_link_format 137 | endif 138 | 139 | let s:tag_pattern = '^!_TAG' 140 | function! zettel#vimwiki#update_listing(lines, title, links_rx, level) 141 | let generator = { 'data': a:lines } 142 | function generator.f() dict 143 | return self.data 144 | endfunction 145 | call vimwiki#base#update_listing_in_buffer(generator, a:title, a:links_rx, line('$')+1, a:level, 1) 146 | endfunction 147 | 148 | " user configurable fields that should be inserted to a front matter of a new 149 | " Zettel 150 | if !exists('g:zettel_front_matter') 151 | let g:zettel_front_matter = {} 152 | endif 153 | 154 | " front matter can be disabled using disable_front_matter local wiki option 155 | let g:zettel_disable_front_matter = zettel#vimwiki#get_option("disable_front_matter") 156 | if empty(g:zettel_disable_front_matter) 157 | let g:zettel_disable_front_matter=0 158 | endif 159 | 160 | if !exists('g:zettel_generated_index_title') 161 | let g:zettel_generated_index_title = "Generated Index" 162 | endif 163 | if !exists('g:zettel_generated_index_title_level') 164 | let g:zettel_generated_index_title_level = 1 165 | endif 166 | 167 | if !exists('g:zettel_backlinks_title') 168 | let g:zettel_backlinks_title = "Backlinks" 169 | endif 170 | if !exists('g:zettel_backlinks_title_level') 171 | let g:zettel_backlinks_title_level = 1 172 | endif 173 | 174 | if !exists('g:zettel_unlinked_notes_title') 175 | let g:zettel_unlinked_notes_title = "Unlinked Notes" 176 | endif 177 | if !exists('g:zettel_unlinked_notes_title_level') 178 | let g:zettel_unlinked_notes_title_level = 1 179 | endif 180 | 181 | if !exists('g:zettel_generated_tags_title') 182 | let g:zettel_generated_tags_title = "Generated Tags" 183 | endif 184 | if !exists('g:zettel_generated_tags_title_level') 185 | let g:zettel_generated_tags_title_level = 1 186 | endif 187 | 188 | " format of a new zettel filename 189 | if !exists('g:zettel_format') 190 | let g:zettel_format = "%y%m%d-%H%M" 191 | endif 192 | 193 | 194 | " default title used for %title placeholder in g:zettel_format if the title is 195 | " empty 196 | if !exists('g:zettel_default_title') 197 | let g:zettel_default_title="untitled" 198 | endif 199 | 200 | " Fixes for Neovim 201 | if has('nvim') 202 | 203 | " make string filled with random characters 204 | function! zettel#vimwiki#make_random_chars() 205 | call luaeval("math.randomseed( os.time() )") 206 | let char_no = range(g:zettel_random_chars) 207 | let str_list = [] 208 | for x in char_no 209 | call add(str_list, nr2char(luaeval("math.random(97,122)"))) 210 | endfor 211 | return join(str_list, "") 212 | endfunction 213 | 214 | elseif v:version < 802 215 | function! zettel#vimwiki#make_random_chars() 216 | let char_no = range(g:zettel_random_chars) 217 | let str_list = [] 218 | for x in char_no 219 | call add(str_list, nr2char(matchstr(reltimestr(reltime()), '\v\.@<=\d+')%26+97)) 220 | endfor 221 | return join(str_list, "") 222 | endfunction 223 | 224 | else 225 | 226 | " make string filled with random characters 227 | function! zettel#vimwiki#make_random_chars() 228 | let seed = srand() 229 | return range(g:zettel_random_chars)->map({-> (97+rand(seed) % 26)->nr2char()})->join('') 230 | endfunction 231 | endif 232 | 233 | " number of random characters used in %random placehoder in new zettel name 234 | if !exists('g:zettel_random_chars') 235 | let g:zettel_random_chars=8 236 | endif 237 | let s:randomchars = zettel#vimwiki#make_random_chars() 238 | 239 | " default date format used in front matter for new zettel 240 | if !exists('g:zettel_date_format') 241 | let g:zettel_date_format = "%Y-%m-%d %H:%M" 242 | endif 243 | 244 | " initialize new zettel date. it should be overwritten in zettel#vimwiki#create() 245 | let s:zettel_date = strftime(g:zettel_date_format) 246 | 247 | 248 | " find end of the front matter variables 249 | function! zettel#vimwiki#find_header_end(filename) 250 | let lines = readfile(a:filename) 251 | " Markdown and Vimwiki use different formats for metadata header, select the 252 | " right one according to the file type 253 | let ext = fnamemodify(a:filename, ":e") 254 | let Header_test = function(ext ==? 'md' ? 'test_header_end_md' : 'test_header_end_wiki') 255 | let i = 0 256 | for line in lines 257 | " let res = s:test_header_end(line, i) 258 | let res = Header_test(line, i) 259 | if res > -1 260 | return i 261 | endif 262 | let i = i + 1 263 | endfor 264 | return 0 265 | endfunction 266 | 267 | " helper function to insert a text line to a new zettel 268 | function! s:add_line(text) 269 | " don't append anything if the argument is empty string 270 | if len(a:text) > 0 271 | call append(line("1"), a:text) 272 | endif 273 | endfunction 274 | 275 | " enable functions to be passed as front_matter values 276 | " this can be useful to dynamic value setting 277 | function! s:expand_front_matter_value(value) 278 | " enable execution of functions that expands to the correct value 279 | if type(a:value) == v:t_func 280 | return a:value() 281 | else 282 | return a:value 283 | endif 284 | endfunction 285 | 286 | function! s:make_header_item(key, value) 287 | let val = expand_front_matter_value(a:value) 288 | return printf(s:header_format, a:key, val) 289 | endfunction 290 | 291 | " add a variable to the zettel header 292 | function! s:add_to_header(key, value) 293 | call add_line(s:make_header_item(a:key, a:value)) 294 | endfunction 295 | 296 | let s:letters = "abcdefghijklmnopqrstuvwxyz" 297 | 298 | " convert number to str (1 -> a, 27 -> aa) 299 | function! s:numtoletter(num) 300 | let numletter = strlen(s:letters) 301 | let charindex = a:num % numletter 302 | let quotient = a:num / numletter 303 | if (charindex-1 == -1) 304 | let charindex = numletter 305 | let quotient = quotient - 1 306 | endif 307 | 308 | let result = strpart(s:letters, charindex - 1, 1) 309 | if (quotient>=1) 310 | return numtoletter(float2nr(quotient)) . result 311 | endif 312 | return result 313 | endfunction 314 | 315 | " title and date to a new zettel note 316 | function! zettel#vimwiki#template(title, date) 317 | if g:zettel_disable_front_matter == 0 318 | call add_line(s:header_delimiter) 319 | call add_to_header("date", a:date) 320 | call add_to_header("title", a:title) 321 | call add_line(s:header_delimiter) 322 | endif 323 | endfunction 324 | 325 | 326 | " sanitize title for filename 327 | function! zettel#vimwiki#escape_filename(name) 328 | let name = substitute(a:name, "[%.%,%?%!%:]", "", "g") " remove unwanted characters 329 | let schar = vimwiki#vars#get_wikilocal('links_space_char') " ' ' by default 330 | let name = substitute(name, " ", schar, "g") " change spaces to link_space_char 331 | 332 | let name = tolower(name) 333 | return fnameescape(name) 334 | endfunction 335 | 336 | " count files that match pattern in the current wiki or if additional indenx 337 | " provided in the wiki indentified by the index 338 | function! zettel#vimwiki#count_files(pattern, ...) 339 | let cwd = a:0 ? zettel#vimwiki#path(a:1) : zettel#vimwiki#path() 340 | let filelist = split(globpath(cwd, a:pattern), '\n') 341 | return len(filelist) 342 | endfunction 343 | 344 | function! zettel#vimwiki#next_counted_file(...) 345 | " count notes in the current / reference wiki directory and return 346 | let ext = a:0 ? vimwiki#vars#get_wikilocal('ext', a:1) : vimwiki#vars#get_wikilocal('ext') 347 | let next_file = a:0 ? zettel#vimwiki#count_files("*" . ext, a:1) + 1 : vimwiki#vars#get_wikilocal('ext') 348 | return next_file 349 | endfunction 350 | 351 | function! zettel#vimwiki#new_zettel_name(...) 352 | let newformat = g:zettel_format 353 | let l:idx = vimwiki#vars#get_bufferlocal('wiki_nr') 354 | if a:0 > 0 && a:1 != "" 355 | " title contains safe version of the original title 356 | " raw_title is exact title 357 | let title = zettel#vimwiki#escape_filename(a:1) 358 | let raw_title = a:1 359 | else 360 | let title = zettel#vimwiki#escape_filename(g:zettel_default_title) 361 | let raw_title = g:zettel_default_title 362 | endif 363 | " expand title in the zettel_format 364 | let newformat = substitute(g:zettel_format, "%title", title, "") 365 | let newformat = substitute(newformat, "%raw_title", raw_title, "") 366 | if matchstr(newformat, "%file_no") != "" 367 | let next_file = zettel#vimwiki#next_counted_file(l:idx) 368 | let newformat = substitute(newformat,"%file_no", next_file, "") 369 | endif 370 | if matchstr(newformat, "%file_alpha") != "" 371 | " same as file_no, but convert numbers to letters 372 | let next_file = s:numtoletter(zettel#vimwiki#next_counted_file(l:idx)) 373 | let newformat = substitute(newformat,"%file_alpha", next_file, "") 374 | endif 375 | if matchstr(newformat, "%random") != "" 376 | " generate random characters, their number is set by g:zettel_random_chars 377 | " random characters are set using zettel#vimwiki#make_random_chars() 378 | " this function is set at the startup and then each time 379 | " zettel#vimwiki#create() is called. we don't call it here because we 380 | " would get wrong links in zettel_new_selected(). It calls new_zettel_name 381 | " twice. 382 | let newformat = substitute(newformat, "%random", s:randomchars, "") 383 | endif 384 | let final_format = strftime(newformat) 385 | if !s:wiki_file_not_exists(final_format) 386 | " if the current file name is used, increase counter and add it as a 387 | " letter to the file name. this ensures that we don't reuse the filename 388 | let file_count = zettel#vimwiki#count_files(final_format . "*", l:idx) 389 | let final_format = final_format . s:numtoletter(file_count) 390 | endif 391 | let g:zettel_current_id = final_format 392 | return final_format 393 | endfunction 394 | 395 | " the optional argument is the wiki number 396 | function! zettel#vimwiki#save_wiki_page(format, ...) 397 | let l:zettel_path = a:0 ? zettel#vimwiki#path(a:1) : zettel#vimwiki#path() 398 | let idx = a:0 ? a:1 : vimwiki#vars#get_bufferlocal('wiki_nr') 399 | let newfile = l:zettel_path . a:format . vimwiki#vars#get_wikilocal('ext',idx ) 400 | " copy the captured file to a new zettel 401 | execute "w! " . newfile 402 | return newfile 403 | endfunction 404 | 405 | " find title in the zettel file and return correct link to it 406 | function! zettel#vimwiki#get_link(filename) 407 | let title =zettel#vimwiki#get_title(a:filename) 408 | let idx = a:0 ? a:1 : vimwiki#vars#get_bufferlocal('wiki_nr') 409 | let wiki_path = zettel#vimwiki#path(idx) 410 | " we will use absolute path from the wiki root for backlinks. This should 411 | " fix issues with relative paths from subdirectories or diary 412 | let wikiname = "/" . vimwiki#base#subdir(wiki_path, fnamemodify(a:filename, ":p")). 413 | \ substitute(fnamemodify(a:filename, ':t'), vimwiki#vars#get_wikilocal('ext', vimwiki#vars#get_bufferlocal('wiki_nr')) . '$' , '', '') 414 | if title == "" 415 | " use the Zettel filename as title if it is empty 416 | let title = wikiname 417 | endif 418 | let link= zettel#vimwiki#format_link(wikiname, title) 419 | return link 420 | endfunction 421 | 422 | " copy of function from Vimwiki 423 | " Params: full path to a wiki file and its wiki number 424 | " Returns: a list of all links inside the wiki file 425 | " Every list item has the form 426 | " [target file, anchor, line number of the link in source file, column number] 427 | function! s:get_links(wikifile, idx) 428 | if !filereadable(a:wikifile) 429 | return [] 430 | endif 431 | 432 | let syntax = vimwiki#vars#get_wikilocal('syntax', a:idx) 433 | let rx_link = vimwiki#vars#get_syntaxlocal('wikilink', syntax) 434 | let links = [] 435 | let lnum = 0 436 | 437 | for line in readfile(a:wikifile) 438 | let lnum += 1 439 | 440 | let link_count = 1 441 | while 1 442 | let col = match(line, rx_link, 0, link_count)+1 443 | let link_text = matchstr(line, rx_link, 0, link_count) 444 | echomsg("link text " . line . " - " . link_text) 445 | if link_text == '' 446 | break 447 | endif 448 | let link_count += 1 449 | let target = vimwiki#base#resolve_link(link_text, a:wikifile) 450 | if target.filename != '' && target.scheme =~# '\mwiki\d\+\|diary\|file\|local' 451 | call add(links, [target.filename, target.anchor, lnum, col]) 452 | endif 453 | endwhile 454 | endfor 455 | 456 | return links 457 | endfunction 458 | 459 | 460 | function! zettel#vimwiki#format_wikigrep(format, pattern, path, ext) 461 | " format string for use in the zettel#vimwiki#wikigrep function 462 | " format is a string with %pattern, %path and %ext placeholders 463 | " the resulting command should accept regular expression in the pattern 464 | " and only list matched files 465 | let command = substitute(a:format, "%pattern", escape(a:pattern, '\'), "") 466 | let command = substitute(command, "%path", escape(a:path, '\'), "") 467 | let command = substitute(command, "%ext", a:ext, "") 468 | return command 469 | endfunction 470 | 471 | " return list of files that match a pattern 472 | function! zettel#vimwiki#wikigrep(pattern) 473 | let paths = [] 474 | let idx = vimwiki#vars#get_bufferlocal('wiki_nr') 475 | let path = fnameescape(zettel#vimwiki#path(idx)) 476 | let ext = vimwiki#vars#get_wikilocal('ext', idx) 477 | if match(&grepprg, '^ag') >= 0 478 | " ag does not support --glob, so we need to include the extension pattern 479 | " in the -r option 480 | let l:command = 'ag -l %pattern -r %path*%ext' 481 | elseif match(&grepprg, '^rg') >= 0 482 | " rg supports --glob, so we can use it 483 | let l:command = 'rg -l %pattern %path --glob=*%ext' 484 | else 485 | " use grep by default 486 | let l:command = 'grep -l -P %pattern -r %path*%ext' 487 | endif 488 | if exists('g:zettel_wikigrep_command') 489 | " use user defined command 490 | let l:command = g:zettel_wikigrep_command 491 | endif 492 | let l:command = zettel#vimwiki#format_wikigrep(l:command, a:pattern, path, ext) 493 | echom("grep command: " . l:command) 494 | " Needs trimming on windows, see `:h systemlist` 495 | let paths = systemlist(l:command)->map('trim(v:val)') 496 | for path in paths 497 | let path = fnamemodify(path, ':t') 498 | " check if file exists, because systemlist returns also files that don't 499 | " if !filereadable(zettel#vimwiki#path(idx) . path) 500 | " call remove(paths, path) 501 | " endif 502 | endfor 503 | return paths 504 | endfunction 505 | 506 | function! zettel#vimwiki#format_file_title(format, file, title) 507 | let link = substitute(a:format, "%title", a:title, "") 508 | let link = substitute(link, "%link", a:file, "") 509 | return link 510 | endfunction 511 | 512 | " use different link style for wiki and markdown syntaxes 513 | function! zettel#vimwiki#format_link(file, title) 514 | return zettel#vimwiki#format_file_title(s:link_format, a:file, a:title) 515 | endfunction 516 | 517 | function zettel#vimwiki#make_path_relative_to_curfile(targetfile, curfile) 518 | " the following line should support Windows paths with backslashes, but I 519 | " don't think Windows require backslashes in paths anymore: 520 | " let s:path_sep = execute('version') =~# 'Windows' ? '\' : '/' 521 | let s:path_sep = '/' 522 | let path_target = split(a:targetfile, s:path_sep) 523 | let path_current = split(a:curfile, s:path_sep) 524 | 525 | " drop common prefix 526 | while(path_target[0] == path_current[0]) 527 | let path_target = path_target[1:] 528 | let path_current = path_current[1:] 529 | endwhile 530 | 531 | " add as many '..' elements to the target path as we have elements in our current path 532 | while(len(path_current) > 1) 533 | let path_current = path_current[1:] 534 | let path_target = extend(path_target, ['..'], 0) 535 | endwhile 536 | 537 | return join(path_target, s:path_sep) 538 | endfunction 539 | 540 | function! zettel#vimwiki#format_search_link(file, title) 541 | " in case the target we're linking to is not where we are, we need to figure out the 542 | " relativ path to where it is, so that the links will work 543 | let new_rel_file_path = zettel#vimwiki#make_path_relative_to_curfile(fnamemodify(a:file, ':p'), fnamemodify(@%, ':p')) 544 | 545 | return zettel#vimwiki#format_file_title(s:link_stub, l:new_rel_file_path, a:title) 546 | endfunction 547 | 548 | " This function is executed when the page referenced by the inserted link 549 | " doesn't contain title. The cursor is placed at the position where title 550 | " should start, and insert mode is started 551 | function! zettel#vimwiki#insert_mode_in_title() 552 | execute "normal! " .s:insert_mode_title_format | :startinsert 553 | endfunction 554 | 555 | function! zettel#vimwiki#get_title(filename) 556 | let filename = a:filename 557 | let title = "" 558 | let lsource = readfile(filename) 559 | let is_markdown = is_markdown() 560 | " this code comes from vimwiki's html export plugin 561 | " Try to load the title from the front matter entry which is present 562 | " at the head of a file. If the front matter is not present use the first 563 | " headline as title either in vimwiki or markup style. 564 | for line in lsource 565 | " Check if front matter title is present 566 | if line =~# '^\s*%\=title' 567 | let title = matchstr(line, '^\s*%\=title:\=\s\zs.*') 568 | return title 569 | endif 570 | if is_markdown 571 | " Check if first headline is present in markdown style 572 | " \zs marks the start of the match part 573 | " \ze marks the end of the match part 574 | if line =~# '^\s*#\s*.*' 575 | let title = matchstr(line, '^\s*#\s*\zs.*\ze') 576 | return title 577 | endif 578 | else 579 | " Check if first headline is present in vimwiki style 580 | " \zs marks the start of the match part 581 | " \ze marks the end of the match part 582 | if line =~# '^\s*=\s\+.*\S\s\+=\s*' 583 | let title = matchstr(line, '^\s*=\s\+\zs.*\S\ze\s\+=\s*') 584 | return title 585 | endif 586 | endif 587 | endfor 588 | return "" 589 | endfunction 590 | 591 | 592 | " check if the file with the current filename exits in wiki 593 | function! s:wiki_file_not_exists(filename, ...) 594 | let l:zettel_path = a:0 ? zettel#vimwiki#path(a:1) : zettel#vimwiki#path() 595 | let link_info = vimwiki#base#resolve_link(a:filename, zettel_path) 596 | return empty(glob(link_info.filename)) 597 | endfunction 598 | 599 | function! zettel#vimwiki#path(...) abort 600 | " Return: diary directory path 601 | " calling with wnum = 0 means we let vimwiki figure it out 602 | let idx = a:0 ? a:1 : vimwiki#vars#get_bufferlocal('wiki_nr') 603 | return vimwiki#vars#get_wikilocal('path', idx).zettel#vimwiki#get_option('rel_path', idx) 604 | endfunction 605 | 606 | " create new zettel note 607 | " there is one optional argument, the zettel title 608 | function! zettel#vimwiki#create(wiki_nr,...) 609 | " name of the new note 610 | let format = zettel#vimwiki#new_zettel_name(a:1) 611 | let date_format = g:zettel_date_format 612 | let date = strftime(date_format) 613 | echomsg("new zettel: ". format) 614 | " update random chars used in %random name format 615 | let s:randomchars = zettel#vimwiki#make_random_chars() 616 | let s:zettel_date = date " save zettel date 617 | " detect if the wiki file exists 618 | let wiki_not_exists = s:wiki_file_not_exists(format, a:wiki_nr) 619 | 620 | if match(vimwiki#path#current_wiki_file(), vimwiki#vars#get_wikilocal('path',a:wiki_nr)) == 0 621 | " let vimwiki to open the wiki file. this is necessary 622 | " to support the vimwiki navigation commands. 623 | let vimwikiabspath = "/". zettel#vimwiki#get_option('rel_path', a:wiki_nr) . format 624 | call vimwiki#base#open_link(':e ', vimwikiabspath) 625 | else 626 | " this happens when we run :ZettelNew outside of wiki. we need to pass 627 | " the full path, so the file will be saved in the correct directory. 628 | " Vimwiki navigation commands will not work in the new note. 629 | let zettelpath = zettel#vimwiki#path(a:wiki_nr) 630 | let ext = vimwiki#vars#get_wikilocal('ext', a:wiki_nr) 631 | execute(':e ' . zettelpath . format . ext) 632 | endif 633 | " add basic template to the new file 634 | if wiki_not_exists 635 | call zettel#vimwiki#template(a:1, date) 636 | return format 637 | endif 638 | return -1 639 | endfunction 640 | 641 | " front_matter can be either list or dict. if it is a dict, then convert it to 642 | " list 643 | function! s:front_matter_list(front_matter) 644 | if type(a:front_matter) ==? v:t_list 645 | return a:front_matter 646 | endif 647 | " it is prefered to use a list for front_matter, as it keeps the order of 648 | " keys. but it is possible to use dict, to keep the backwards compatibility 649 | let newlist = [] 650 | for key in keys(a:front_matter) 651 | call add(newlist, [key, a:front_matter[key]]) 652 | endfor 653 | return newlist 654 | endfunction 655 | 656 | function! zettel#vimwiki#zettel_new(...) 657 | " TODO: the user should be able to pass a wiki number (1 indexed) 658 | let wiki_nr = vimwiki#vars#get_bufferlocal('wiki_nr') 659 | if wiki_nr < 0 " this happens when e.g. VimwikiMakeDiaryNote was called outside a wiki buffer 660 | let wiki_nr = 0 661 | endif 662 | 663 | 664 | if wiki_nr >= vimwiki#vars#number_of_wikis() 665 | call vimwiki#u#error('Wiki '.wiki_nr.' is not registered in g:vimwiki_list!') 666 | return 667 | endif 668 | call vimwiki#path#mkdir(vimwiki#vars#get_wikilocal('path', wiki_nr). 669 | \ zettel#vimwiki#get_option('rel_path', wiki_nr)) 670 | 671 | let filename = zettel#vimwiki#create(wiki_nr, a:1) 672 | " the wiki file already exists 673 | if filename ==? -1 674 | return 0 675 | endif 676 | let front_matter = zettel#vimwiki#get_option("front_matter") 677 | if g:zettel_disable_front_matter == 0 678 | if !empty(front_matter) 679 | let newfile = zettel#vimwiki#save_wiki_page(filename) 680 | let last_header_line = zettel#vimwiki#find_header_end(newfile) 681 | " ensure that front_matter is a list 682 | let front_list = s:front_matter_list(front_matter) 683 | " we must reverse the list, because each line is inserted before the 684 | " ones inserted earlier 685 | for values in reverse(copy(front_list)) 686 | call append(last_header_line, make_header_item(values[0], values[1])) 687 | endfor 688 | endif 689 | endif 690 | 691 | " insert the template text from a template file if it is configured in 692 | " g:zettel_options for the current wiki 693 | let template = zettel#vimwiki#get_option("template") 694 | if !empty(template) 695 | let variables = get(a:, 2, 0) 696 | if empty(variables) 697 | " save file, in order to prevent errors in variable reading 698 | execute "w" 699 | let variables = zettel#vimwiki#prepare_template_variables(expand("%"), a:1) 700 | " backlink contains link to the new note itself, so we will just disable 701 | " it. backlinks are available only when the new note is created using 702 | " ZettelNewSelectedMap (`z` letter in visual mode by default). 703 | let variables.backlink = "" 704 | endif 705 | " we may reuse varaibles from the parent zettel. date would be wrong in this case, 706 | " so we will overwrite it with the current zettel date 707 | let variables.date = s:zettel_date 708 | " enable also Zettel ID 709 | let variables.id = filename 710 | call zettel#vimwiki#expand_template(template, variables) 711 | endif 712 | " save the new wiki file 713 | execute "w" 714 | 715 | endfunction 716 | 717 | " crate zettel link from a selected text 718 | function! zettel#vimwiki#zettel_new_selected() 719 | let title = get_visual_selection() 720 | let name = zettel#vimwiki#new_zettel_name(title) 721 | " prepare_template_variables needs the file saved on disk 722 | execute "w" 723 | " make variables that will be available in the new page template 724 | let variables = zettel#vimwiki#prepare_template_variables(expand("%"), title) 725 | let linktitle = "\\\\0" 726 | if match(vimwiki#path#current_wiki_file(), zettel#vimwiki#path()) != 0 727 | " if we are NOT in the zettelkasten, we should use absolute (vimwiki 728 | " root-relative absolute) paths for the name 729 | let idx = a:0 ? a:1 : vimwiki#vars#get_bufferlocal('wiki_nr') 730 | let prefix = zettel#vimwiki#get_option('rel_path', idx) 731 | let name = "/" . prefix . name 732 | execute "normal! :'<,'>s:\\%V.*\\%V.:" . zettel#vimwiki#format_link( name, linktitle) ."\\" 733 | else 734 | " if we are in zettelkasten, we should get absolute path anyway, but we 735 | " use format_search_link to get a relative path to the current file 736 | " https://github.com/michal-h21/vim-zettel/issues/155 737 | let idx = a:0 ? a:1 : vimwiki#vars#get_bufferlocal('wiki_nr') 738 | let prefix = zettel#vimwiki#get_option('rel_path', idx) 739 | let name = zettel#vimwiki#path(idx) . prefix . name 740 | execute "normal! :'<,'>s:\\%V.*\\%V.:" . zettel#vimwiki#format_search_link( name, linktitle) ."\\" 741 | endif 742 | " replace the visually selected text with a link to the new zettel 743 | " \\%V.*\\%V. should select the whole visual selection 744 | call zettel#vimwiki#zettel_new(title, variables) 745 | endfunction 746 | 747 | function! zettel#vimwiki#zettel_capture_selected(title) 748 | let selection = zettel#vimwiki#get_visual_selection() 749 | let title = a:title 750 | let name = zettel#vimwiki#new_zettel_name(title) 751 | " prepare_template_variables needs the file saved on disk 752 | execute "write" 753 | " make variables that will be available in the new page template 754 | let variables = zettel#vimwiki#prepare_template_variables(expand("%"), title) 755 | let linktitle = "\\\\0" 756 | if match(vimwiki#path#current_wiki_file(), zettel#vimwiki#path()) != 0 757 | " if we are NOT in the zettelkasten, we should use absolute (vimwiki 758 | " root-relative absolute) paths for the name 759 | let idx = a:0 ? a:1 : vimwiki#vars#get_bufferlocal('wiki_nr') 760 | let prefix = zettel#vimwiki#get_option('rel_path', idx) 761 | let name = "/" . prefix . name 762 | let linktitle = prefix . linktitle 763 | endif 764 | " replace the visually selected text with a link to the new zettel 765 | " \\%V.*\\%V. should select the whole visual selection 766 | execute "normal! :'<,'>s:\\%V.*\\%V.:" . zettel#vimwiki#format_link( name, linktitle) ."\\" 767 | call zettel#vimwiki#zettel_new(title, variables) 768 | " paste selection at end of file 769 | execute "normal! Go" . selection 770 | endfunction 771 | 772 | " prepare variables that will be available to expand in the new note template 773 | function! zettel#vimwiki#prepare_template_variables(filename, title) 774 | let variables = {} 775 | let variables.title = a:title 776 | let variables.date = s:zettel_date 777 | " add variables from front_matter, to make them available in the template 778 | let front_matter = zettel#vimwiki#get_option("front_matter") 779 | if !empty(front_matter) 780 | let front_list = s:front_matter_list(front_matter) 781 | for entry in copy(front_list) 782 | let variables[entry[0]] = expand_front_matter_value(entry[1]) 783 | endfor 784 | endif 785 | let variables.backlink = zettel#vimwiki#get_link(a:filename) 786 | " we want to save footer of the parent note. It can contain stuff that can 787 | " be useful in the child note, like citations, etc. Footer is everything 788 | " below last horizontal rule (----) 789 | let variables.footer = s:read_footer(a:filename) 790 | return variables 791 | endfunction 792 | 793 | " find and return footer in the file 794 | " footer is content below last horizontal rule (----) 795 | function! s:read_footer(filename) 796 | let lines = readfile(a:filename) 797 | let footer_lines = [] 798 | let found_footer = -1 799 | " return empty footer if we couldn't find the footer 800 | let footer = "" 801 | " process lines from the last one and try to find the rule 802 | for line in reverse(lines) 803 | if match(line, "^ \*----") == 0 804 | let found_footer = 0 805 | break 806 | endif 807 | call add(footer_lines, line) 808 | endfor 809 | if found_footer == 0 810 | let footer = join(reverse(footer_lines), "\n") 811 | endif 812 | return footer 813 | endfunction 814 | 815 | " populate new note using template 816 | function! zettel#vimwiki#expand_template(template, variables) 817 | " readfile returns list, we need to convert it to string 818 | " in order to do global replace 819 | let template_file = expand(a:template) 820 | if !filereadable(template_file) 821 | return 822 | endif 823 | let content = readfile(template_file) 824 | let text = join(content, "\n") 825 | for key in keys(a:variables) 826 | let text = substitute(text, "%" . key, a:variables[key], "g") 827 | endfor 828 | " when front_matter is disabled, there is an empty line before 829 | " start of the inserted template. we need to ignore it. 830 | let correction = 0 831 | if line('$') == 1 832 | let correction = 1 833 | endif 834 | " add template at the end 835 | " we must split it, 836 | for xline in split(text, "\n") 837 | call append(line('$') - correction, xline) 838 | endfor 839 | endfunction 840 | 841 | " make new zettel from a file. the file contents will be copied to a new 842 | " zettel, the original file contents will be replaced with the zettel filename 843 | " use temporary file if you want to keep the original file 844 | function! zettel#vimwiki#zettel_capture(wnum,...) 845 | let origfile = expand("%") 846 | execute "set ft=vimwiki" 847 | " This probably doesn't work with current vimwiki code 848 | if a:wnum > vimwiki#vars#number_of_wikis() 849 | echomsg 'Vimwiki Error: Wiki '.a:wnum.' is not registered in g:vimwiki_list!' 850 | return 851 | endif 852 | if a:wnum > 0 853 | let idx = a:wnum 854 | else 855 | let idx = 0 856 | endif 857 | let title = zettel#vimwiki#get_title(origfile) 858 | let format = zettel#vimwiki#new_zettel_name(title) 859 | " let link_info = vimwiki#base#resolve_link(format) 860 | let newfile = zettel#vimwiki#save_wiki_page(format, idx) 861 | " delete contents of the captured file 862 | execute "normal! ggdG" 863 | " replace it with a address of the zettel file 864 | execute "normal! i" . newfile 865 | execute "w" 866 | " open the new zettel 867 | execute "e " . newfile 868 | endfunction 869 | 870 | 871 | function! zettel#vimwiki#find_files(wiki_nr, directories_only, ...) abort 872 | " adapted from vimwiki 873 | " Returns: a list containing all files of the given wiki as absolute file path. 874 | " If the given wiki number is negative, the current wiki is used 875 | " If the second argument is not zero, only directories are found 876 | " If third argument: pattern to search for 877 | let wiki_nr = a:wiki_nr 878 | if wiki_nr >= 0 879 | let root_directory = zettel#vimwiki#path(wiki_nr) 880 | else 881 | let root_directory = zettel#vimwiki#path() 882 | let wiki_nr = vimwiki#vars#get_bufferlocal('wiki_nr') 883 | endif 884 | if a:directories_only 885 | let ext = '/' 886 | else 887 | let ext = vimwiki#vars#get_wikilocal('ext', wiki_nr) 888 | endif 889 | " If pattern is given, use it 890 | " if current wiki is temporary -- was added by an arbitrary wiki file then do 891 | " not search wiki files in subdirectories. Or it would hang the system if 892 | " wiki file was created in $HOME or C:/ dirs. 893 | if a:0 && a:1 !=# '' 894 | let pattern = a:1 895 | elseif vimwiki#vars#get_wikilocal('is_temporary_wiki', wiki_nr) 896 | let pattern = '*'.ext 897 | else 898 | let pattern = '**/*'.ext 899 | endif 900 | let files = split(globpath(root_directory, pattern), '\n') 901 | 902 | " filter excluded files before returning 903 | for pattern in vimwiki#vars#get_wikilocal('exclude_files') 904 | let efiles = split(globpath(root_directory, pattern), '\n') 905 | let files = filter(files, 'index(efiles, v:val) == -1') 906 | endfor 907 | 908 | return files 909 | endfunction 910 | 911 | " based on vimwikis "get wiki links", not stripping file extension 912 | function! zettel#vimwiki#get_wikilinks(wiki_nr, also_absolute_links) 913 | let files = zettel#vimwiki#find_files(a:wiki_nr, 0) 914 | if a:wiki_nr == vimwiki#vars#get_bufferlocal('wiki_nr') 915 | let cwd = vimwiki#path#wikify_path(expand('%:p:h')) 916 | elseif a:wiki_nr < 0 917 | let cwd = zettel#vimwiki#path() 918 | else 919 | let cwd = vimwiki#vars#get_wikilocal('path', a:wiki_nr) 920 | endif 921 | 922 | let result = [] 923 | for wikifile in files 924 | let wikifile = vimwiki#path#relpath(cwd, wikifile) 925 | call add(result, wikifile) 926 | endfor 927 | if a:also_absolute_links 928 | for wikifile in files 929 | let wikifile = '/'.vimwiki#path#relpath(cwd, wikifile) 930 | call add(result, wikifile) 931 | endfor 932 | endif 933 | return result 934 | endfunction 935 | 936 | " add link with title of the file referenced in the second argument to the 937 | " array in the first argument 938 | function! s:add_bulleted_link(lines, abs_filepath) 939 | let bullet = repeat(' ', vimwiki#lst#get_list_margin()) . vimwiki#lst#default_symbol().' ' 940 | call add(a:lines, bullet. 941 | \ zettel#vimwiki#get_link(a:abs_filepath)) 942 | return a:lines 943 | endfunction 944 | 945 | 946 | 947 | " insert list of links to the current page 948 | function! s:insert_link_array(title, lines, level) 949 | let links_rx = '\m^\s*'.vimwiki#u#escape(vimwiki#lst#default_symbol()).' ' 950 | call zettel#vimwiki#update_listing(a:lines, a:title, links_rx, a:level) 951 | endfunction 952 | 953 | 954 | " based on vimwikis "generate links", adding the %title to the link 955 | function! zettel#vimwiki#generate_links() 956 | let lines = [] 957 | 958 | let links = zettel#vimwiki#get_wikilinks(vimwiki#vars#get_bufferlocal('wiki_nr'), 0) 959 | call reverse(sort(links)) 960 | 961 | let bullet = repeat(' ', vimwiki#lst#get_list_margin()) . vimwiki#lst#default_symbol().' ' 962 | for link in links 963 | let abs_filepath = vimwiki#path#abs_path_of_link(link) 964 | "let abs_filepath = link 965 | "if !s:is_diary_file(abs_filepath) 966 | call add(lines, bullet. 967 | \ zettel#vimwiki#get_link(abs_filepath)) 968 | "endif 969 | endfor 970 | call s:insert_link_array(g:zettel_generated_index_title, lines, g:zettel_generated_index_title_level) 971 | endfunction 972 | 973 | function! s:is_markdown() 974 | return vimwiki#vars#get_wikilocal('syntax', vimwiki#vars#get_bufferlocal('wiki_nr')) ==? 'markdown' 975 | endfunction 976 | 977 | " detect if we are running in the development version of Vimwiki 978 | function! s:is_vimwiki_devel() 979 | return exists("*vimwiki#base#complete_file") 980 | endfunction 981 | 982 | " test if link in the Backlinks section 983 | function! s:is_in_backlinks(file, filenamepattern) 984 | let f = readfile(a:file) 985 | let content = join(f, "\n") 986 | " search for backlinks section 987 | let backlinks_pattern = printf(s:section_pattern, g:zettel_backlinks_title) 988 | let backlinks_pos = matchstrpos(content, backlinks_pattern) 989 | " if we cannot find backlinks in the page return false 990 | if backlinks_pos[1] == -1 991 | return -1 992 | endif 993 | let file_pos = matchstrpos(content, a:filenamepattern) 994 | " link is in backlinks when it is placed after the Backlinks section title 995 | return backlinks_pos[1] < file_pos[1] 996 | endfunction 997 | 998 | 999 | " based on vimwikis "backlinks" 1000 | " insert backlinks of the current page in a section 1001 | function! zettel#vimwiki#backlinks() 1002 | let current_filename = expand("%:t:r") 1003 | " find [filename| or [filename] to support both wiki and md syntax 1004 | let filenamepattern = printf(s:grep_link_pattern, current_filename) 1005 | let locations = [] 1006 | let backfiles = zettel#vimwiki#wikigrep(filenamepattern) 1007 | for file in backfiles 1008 | " only add backlink if it is not already backlink 1009 | let is_backlink = s:is_in_backlinks(file, current_filename) 1010 | if is_backlink < 1 1011 | " Make sure we don't add ourselves 1012 | if !(file ==# expand("%:p")) 1013 | call s:add_bulleted_link(locations, file) 1014 | endif 1015 | endif 1016 | endfor 1017 | 1018 | if empty(locations) 1019 | echomsg 'Vimzettel: No other file links to this file' 1020 | else 1021 | call uniq(locations) 1022 | " Insert back links section 1023 | call s:insert_link_array(g:zettel_backlinks_title, locations, g:zettel_backlinks_title_level) 1024 | endif 1025 | endfunction 1026 | 1027 | function! zettel#vimwiki#inbox() 1028 | " detect development version of Vimwiki, where check_links can take arguments 1029 | if is_vimwiki_devel() 1030 | " 0 means that it will search only the current wiki 1031 | call vimwiki#base#check_links(0,0,0) 1032 | else 1033 | call vimwiki#base#check_links() 1034 | endif 1035 | let linklist = getqflist() 1036 | cclose 1037 | let paths = [] 1038 | " normalize the current wiki path 1039 | let cwd = fnamemodify(zettel#vimwiki#path(), ":p:h") 1040 | let bullet = repeat(' ', vimwiki#lst#get_list_margin()) . vimwiki#lst#default_symbol().' ' 1041 | for d in linklist 1042 | " detect files that are not reachable from the wiki index 1043 | let filenamematch = matchstr(d.text,'\zs.*\ze is not reachable') 1044 | if filenamematch != "" && filereadable(filenamematch) 1045 | " use only files from the current wiki, we get files from all registered 1046 | " wikis here 1047 | let filepath = fnamemodify(filenamematch, ":p:h") 1048 | if filepath ==# cwd 1049 | " TODO: make the links a valid path from the current file 1050 | call add(paths, bullet. 1051 | \ zettel#vimwiki#get_link(filenamematch)) 1052 | endif 1053 | endif 1054 | endfor 1055 | if empty(paths) 1056 | else 1057 | " remove duplicates and insert inbox section 1058 | call uniq(paths) 1059 | call s:insert_link_array(g:zettel_unlinked_notes_title, paths, g:zettel_unlinked_notes_title_level) 1060 | endif 1061 | 1062 | endfunction 1063 | 1064 | " copy from vimtiki#tags.vim 1065 | function! s:load_tags_metadata() abort 1066 | " Load tags metadata from file, returns a dictionary 1067 | let metadata_path = vimwiki#tags#metadata_file_path() 1068 | if !filereadable(metadata_path) 1069 | return {} 1070 | endif 1071 | let metadata = {} 1072 | for line in readfile(metadata_path) 1073 | if line =~# '^!_TAG_.*$' 1074 | continue 1075 | endif 1076 | let parts = matchlist(line, '^\(.\{-}\);"\(.*\)$') 1077 | if parts[0] ==? '' || parts[1] ==? '' || parts[2] ==? '' 1078 | throw 'VimwikiTags1: Metadata file corrupted' 1079 | endif 1080 | let std_fields = split(parts[1], '\t') 1081 | if len(std_fields) != 3 1082 | throw 'VimwikiTags2: Metadata file corrupted' 1083 | endif 1084 | let vw_part = parts[2] 1085 | if vw_part[0] !=? "\t" 1086 | throw 'VimwikiTags3: Metadata file corrupted' 1087 | endif 1088 | let vw_fields = split(vw_part[1:], "\t") 1089 | if len(vw_fields) != 1 || vw_fields[0] !~# '^vimwiki:' 1090 | throw 'VimwikiTags4: Metadata file corrupted' 1091 | endif 1092 | let vw_data = substitute(vw_fields[0], '^vimwiki:', '', '') 1093 | let vw_data = substitute(vw_data, '\\n', "\n", 'g') 1094 | let vw_data = substitute(vw_data, '\\r', "\r", 'g') 1095 | let vw_data = substitute(vw_data, '\\t', "\t", 'g') 1096 | let vw_data = substitute(vw_data, '\\\\', "\\", 'g') 1097 | let vw_fields = split(vw_data, "\t") 1098 | if len(vw_fields) != 3 1099 | throw 'VimwikiTags5: Metadata file corrupted' 1100 | endif 1101 | let pagename = vw_fields[0] 1102 | let entry = {} 1103 | let entry.tagname = std_fields[0] 1104 | let entry.lineno = std_fields[2] 1105 | let entry.filename = std_fields[1] 1106 | let entry.link = vw_fields[1] 1107 | let entry.description = vw_fields[2] 1108 | if has_key(metadata, pagename) 1109 | call add(metadata[pagename], entry) 1110 | else 1111 | let metadata[pagename] = [entry] 1112 | endif 1113 | endfor 1114 | return metadata 1115 | endfunction 1116 | 1117 | 1118 | " based on vimwiki 1119 | function! zettel#vimwiki#generate_tags(...) abort 1120 | let need_all_tags = (a:0 == 0) 1121 | let specific_tags = a:000 1122 | 1123 | let metadata = s:load_tags_metadata() 1124 | 1125 | " make a dictionary { tag_name: [tag_links, ...] } 1126 | let tags_entries = {} 1127 | for entries in values(metadata) 1128 | for entry in entries 1129 | if has_key(tags_entries, entry.tagname) 1130 | call add(tags_entries[entry.tagname], entry.filename) 1131 | else 1132 | let tags_entries[entry.tagname] = [entry.filename] 1133 | endif 1134 | endfor 1135 | endfor 1136 | 1137 | let rxH_TemplateName = 'rxH'.(g:zettel_generated_index_title_level + 1).'_Template' 1138 | let lines = [] 1139 | let bullet = repeat(' ', vimwiki#lst#get_list_margin()).vimwiki#lst#default_symbol().' ' 1140 | for tagname in sort(keys(tags_entries)) 1141 | if need_all_tags || index(specific_tags, tagname) != -1 1142 | call extend(lines, [ 1143 | \ '', 1144 | \ substitute(vimwiki#vars#get_syntaxlocal(rxH_TemplateName), '__Header__', tagname, ''), 1145 | \ '' ]) 1146 | for taglink in reverse(sort(tags_entries[tagname])) 1147 | let filepath = vimwiki#path#abs_path_of_link(taglink) 1148 | if filereadable(filepath) 1149 | call add(lines, bullet . zettel#vimwiki#get_link(filepath)) 1150 | endif 1151 | endfor 1152 | endif 1153 | endfor 1154 | 1155 | let links_rx = '\m\%(^\s*$\)\|\%('.vimwiki#vars#get_syntaxlocal('rxH2').'\)\|\%(^\s*' 1156 | \ .vimwiki#u#escape(vimwiki#lst#default_symbol()).' ' 1157 | \ .vimwiki#vars#get_syntaxlocal('rxWikiLink').'$\)' 1158 | 1159 | call zettel#vimwiki#update_listing(lines, g:zettel_generated_tags_title, links_rx, g:zettel_generated_tags_title_level) 1160 | endfunction 1161 | -------------------------------------------------------------------------------- /doc/zettel.txt: -------------------------------------------------------------------------------- 1 | *vim-zettel.txt* A Zettelkasten VimWiki Addon Last change: 2020 Jul 21 2 | 3 | _____ _ _ _ _ _ ~ 4 | |__ /___| |_| |_ ___| | | _____ ___| |_ ___ _ __ ~ 5 | / // _ \ __| __/ _ \ | |/ / _ \/ __| __/ _ \ '_ \ ~ 6 | / /| __/ |_| || __/ | < (_| \__ \ || __/ | | | ~ 7 | /____\___|\__|\__\___|_|_|\_\__,_|___/\__\___|_| |_| ~ 8 | 9 | 10 | ============================================================================== 11 | CONTENTS *vim-zettel* 12 | 13 | 0. Intro |Vim-Zettel-Intro| 14 | 1. Installation |Vim-Zettel-Install| 15 | 2. Configuration |Vim-Zettel-Configure| 16 | 3. Commands |Vim-Zettel-Commands| 17 | `ZettelNew` |Vim-Zettel_ZettelNew| 18 | `ZettelOpen` |Vim-Zettel_ZettelOpen| 19 | `ZettelInsertNote` |Vim-Zettel_ZettelInsertNote| 20 | `ZettelCapture` |Vim-Zettel_ZettelCapture| 21 | `ZettelNewSelectedMap` |Vim-Zettel_ZettelNewSelectedMap| 22 | `ZettelSetActiveWiki` |Vim-Zettel_ZettelSetActiveWiki| 23 | `ZettelBackLinks` |Vim-Zettel_ZettelBackLinks| 24 | `ZettelInbox` |Vim-Zettel_ZettelInbox| 25 | `ZettelGenerateLinks` |Vim-Zettel_ZettelGenerateLinks| 26 | `ZettelGenerateTags` |Vim-Zettel_ZettelGenerateTags| 27 | `ZettelSearch` |Vim-Zettel_ZettelSearch| 28 | `ZettelYankName` |Vim-Zettel_ZettelYankName| 29 | 4. Mappings |Vim-Zettel-Mappings| 30 | `z` |Vim-Zettel_z| 31 | `[[` |Vim-Zettel_[[| 32 | `T` |Vim-Zettel_T| 33 | `gZ` |Vim-Zettel_gZ| 34 | 5. Variables |Vim-Zettel-Variables| 35 | 6. Templates |Vim-Zettel-Templates| 36 | 7. Related Packages |Vim-Zettel-Related| 37 | 8. ChangeLog |Vim-Zettel-ChangeLog| 38 | 39 | ============================================================================== 40 | 0. Intro *Vim-Zettel-Intro* 41 | 42 | This is a Vim plugin that implements ideas of the zettelkasten method of 43 | note taking as described at https://zettelkasten.de/. It is an add-on to 44 | the Vimwiki extension for Vim and supports both Vimwiki and Markdown syntaxes. 45 | 46 | Main features: 47 | 48 | - customizable filenames (date and time, title, consecutive numbering, random 49 | characters) 50 | - links always show titles, regardless of the actual filename 51 | - fulltext support using FZF for searching and hyperlinking 52 | - search your Zettelkasten from LaTeX or Markdown documents and insert 53 | selected notes to the document 54 | - template support 55 | - automatically updated tag index 56 | - backlinks 57 | 58 | ============================================================================== 59 | 1. Installation *Vim-Zettel-Install* 60 | 61 | This extension requires an external search utility. It uses The Silver 62 | Searcher by default. 63 | (Available at https://github.com/ggreer/the_silver_searcher/ or in your OS 64 | repositories). 65 | 66 | Using Vundle: > 67 | 68 | Plugin 'vimwiki/vimwiki' 69 | Plugin 'junegunn/fzf' 70 | Plugin 'junegunn/fzf.vim' 71 | Plugin 'michal-h21/vim-zettel' 72 | < 73 | ============================================================================== 74 | 2. Configuration *Vim-Zettel-Configure* 75 | 76 | First of all, it is necessary to configure Vimwiki, as Vim-Zettel builds 77 | on top of it. Vim-Zettel can be used out of the box without further 78 | configuration if you have only one wiki. However, you will probably want to 79 | customize your Vim-Zettel configuration and will have to if you have more than 80 | one wiki. 81 | > 82 | " Settings for Vimwiki 83 | let g:vimwiki_list = \ 84 | [{'path':'~/scratchbox/vimwiki/markdown/','ext':'.md',\ 85 | 'syntax':'markdown'}, {"path":"~/scratchbox/vimwiki/wiki/"}] 86 | < 87 | You may want to set some of the following options in your .vimrc file to make 88 | Vim-Zettel work to your liking: 89 | 90 | - |g:zettel_options| 91 | - |g:zettel_format| 92 | - |g:zettel_default_mappings| 93 | - |g:zettel_fzf_command| 94 | - |g:zettel_fzf_options| 95 | - |g:zettel_backlinks_title| 96 | - |g:zettel_backlinks_title_level| 97 | - |g:zettel_unlinked_notes_title| 98 | - |g:zettel_unlinked_notes_title_level| 99 | - |g:zettel_generated_tags_title| 100 | - |g:zettel_generated_tags_title_level| 101 | - |g:zettel_wikigrep_command| 102 | 103 | You can also supply a custom template for creating new zettels. See 104 | |Vim-Zettel-Templates|. 105 | 106 | ============================================================================== 107 | 3. Commands *Vim-Zettel-Commands* 108 | 109 | Once you have Vim-Zettel installed, use the `:VimwikiIndex` command to open 110 | the main file of your wiki. You can link to your notes from this page. 111 | 112 | Vim-Zettel implements the following commands on top of Vimwiki. 113 | 114 | *Vim-Zettel_ZettelNew* 115 | *ZettelNew* 116 | - `:ZettelNew` command – it will create a new wiki file named as 117 | %y%m%d-%H%M.wiki (it is possible to change the file name format using 118 | |g:zettel_format| variable). The file uses basic template in the form 119 | 120 | > 121 | %title Note title 122 | %date current date 123 | < 124 | where title is the first parameter to `:ZettelNew`. 125 | 126 | If you use the default mappings provided by `Vim-Zettel`, it is possible to 127 | call this command by pressing the `z` character in visual mode. The selected 128 | text will be used as title of the new note. 129 | 130 | The newly created wiki-file will be saved in: 131 | 132 | (1) the current directory if it is in the `g:vimwiki_list` paths 133 | (2) the directory-path of the first `g:vimwiki_list` with non-empty `g:zettel_options` 134 | (3) the directory-path given by the first `g:vimwiki_list` entry 135 | (4) vimwiki's default directory 136 | 137 | with the given order. 138 | 139 | *Vim-Zettel_ZettelOpen* 140 | *ZettelOpen* 141 | - `:ZettelOpen` command - perform fulltext search using FZF. It keeps the 142 | history of opened pages. 143 | 144 | *Vim-Zettel_ZettelInsertNote* 145 | *ZettelInsertNote* 146 | - `:ZettelInsertNote` - select notes using FZF and insert them in the current 147 | document. Multiple notes can be selected using the `` key. They are 148 | automatically converted to the document syntax format using Pandoc. 149 | 150 | *Vim-Zettel_ZettelCapture* 151 | *ZettelCapture* 152 | - `:ZettelCapture` command – turn the content of the current file into a 153 | zettel. This is a global command available throughout Vim. WARNING: 154 | this command is destructive. Use only on temporary files. You can run 155 | this from within vim while viewing a file you want to turn into a zettel 156 | or from the command line: > 157 | 158 | vim -c ZettelCapture filename 159 | < 160 | you can specify wiki number (starting from 0) if you have multiple 161 | wikis. It opens first declared wiki by default: 162 | 163 | > 164 | vim -c "ZettelCapture 1" filename 165 | < 166 | 167 | 168 | *Vim-Zettel_ZettelNewSelectedMap* 169 | *ZettelNewSelectedMap* 170 | - `:ZettelNewSelectedMap` creates a new note with the selected title while 171 | converting the selection as a link to the newly created zettel. If you want 172 | the selection to be the note's content instead, see ZettelCaptureSelected. 173 | 174 | *Vim-Zettel_ZettelCaptureSelected* 175 | *ZettelCaptureSelected* 176 | - `:ZettelCaptureSelected(title)` creates a new note with the selected content 177 | and the title passed as a parameter. The selection is converted to a link 178 | to the newly created zettel. If you want the selected text to be the title 179 | instead, see ZettelNewSelectedMap. 180 | 181 | *Vim-Zettel_ZettelSetActiveWiki* 182 | *ZettelSetActiveWiki* 183 | 184 | - `:ZettelSetActiveWiki` command - select default wiki for commands that can 185 | operate when Vimwiki is not active, like `:ZettelOpen` or 186 | `:ZettelInsertNote`. 187 | 188 | *Vim-Zettel_ZettelBackLinks* 189 | *ZettelBackLinks* 190 | - `:ZettelBackLinks` command – insert list of notes that link to the current 191 | note. It uses a grep like command to collect files that link to the current 192 | file. By default, it checks the `grepprg` option to detect the command. It 193 | supports `ag`, `rg` and `grep`. You can change the used command using the 194 | |g:zettel_wikigrep_command| variable: 195 | 196 | > 197 | let g:zettel_wikigrep_command = 'ag -l %pattern -r %path*%ext' 198 | 199 | The template supports following variables: 200 | > 201 | %pattern regular expression containing name of the current note 202 | %path root directory of the wiki 203 | %ext file extension of notes used in the current wiki 204 | < 205 | *Vim-Zettel_ZettelInbox* 206 | *ZettelInbox* 207 | - `:ZettelInbox` command – insert list of notes that are not reachable from 208 | the index file (you can open the index file using the `:VimwikiIndex` 209 | command). 210 | 211 | *Vim-Zettel_ZettelGenerateLinks* 212 | *ZettelGenerateLinks* 213 | - `:ZettelGenerateLinks` command – insert list of all wiki pages in the 214 | current page. It needs updated tags database. The tags database can be 215 | updated using the `:VimwikiRebuildTags!` command. 216 | 217 | *Vim-Zettel_ZettelGenerateTags* 218 | *ZettelGenerateTags* 219 | - `:ZettelGenerateTags` command – insert list of tags and pages that used 220 | these tags in the current page. It needs updated tags database. The tags 221 | database can be updated using the `:VimwikiRebuildTags!` command. It only 222 | supports the Vimwiki style tags in the form :tag1:tag2. These work 223 | even in the Markdown mode. 224 | 225 | *Vim-Zettel_ZettelSearch* 226 | *ZettelSearch* 227 | - `:ZettelSearch` command – search the content of your zettelkasten and 228 | insert links to the selected zettels in your current note. Use 229 | to select multiple notes. Mapped to `[[` in insert mode. 230 | 231 | *Vim-Zettel_ZettelSelectBuffer* 232 | *ZettelSelectBuffer* 233 | - `:ZettelSelectBuffer` - select from a list of recently opened notes. 234 | 235 | *Vim-Zettel_ZettelTitleSelected* 236 | *ZettelTitleSelected* 237 | - `:ZettelTitleSelected` - select note using FZF and use actually selected 238 | text as a title. 239 | 240 | *Vim-Zettel_ZettelYankName* 241 | *ZettelYankName* 242 | - `:ZettelYankName` – copy formatted link to the current zettel to a register. 243 | Mapped to `T` in normal mode. 244 | 245 | Useful Vimwiki commands ~ 246 | 247 | - |:VimwikiBacklinks| - display files that link to the current page 248 | - |:VimwikiCheckLinks|- display files that no other file links to 249 | 250 | ============================================================================== 251 | 4. Mappings *Vim-Zettel-Mappings* 252 | 253 | `Vim-zettel` sets some mappings by default. You can disable the default 254 | mappings and define your own. See |g:zettel_default_mappings| for more 255 | details. 256 | 257 | *Vim-Zettel_z* 258 | - `z` command in visual mode – create a new wiki file using selected text for 259 | the note title 260 | 261 | *Vim-Zettel_[[* 262 | - [[ command in insert mode – create a link to a note. It uses FZF for the 263 | note searching. 264 | 265 | - g[ command for visual selection - create a link to a note, with the selected 266 | text as a title. 267 | 268 | *Vim-Zettel_T* 269 | - T command in normal mode – yank the current note filename and title as a 270 | Vimwiki link 271 | 272 | *Vim-Zettel_gZ* 273 | - gZ command in normal mode – replace file path under cursor with Wiki link 274 | 275 | It may be convenient to map `ZettelNew` to prompt for a note title also: 276 | > 277 | nnoremap zn :ZettelNew 278 | < 279 | ============================================================================== 280 | 5. Variables *Vim-Zettel-Variables* 281 | 282 | *g:zettel_options* 283 | The g:zettel_options variable corresponds to the g:vimwiki_list variable. If 284 | you have more than one vimwiki and the second wiki listed in your 285 | g:vimwiki_list variable is your zettelkasten, then you must represent the 286 | first wiki in your g:zettel_options list as a set of empty braces: 287 | > 288 | first wiki zettelkasten wiki 289 | ↓ ↓ 290 | let g:zettel_options = [{}, {"front_matter" : [["tags", ""], ["type","note"]], 291 | \ "template" : "~/mytemplate.tpl", 292 | \ "rel_path" : "zettel/"}] 293 | < 294 | `front_matter` contains additional fields to be inserted in the header of a 295 | new note. It should be list of lists, where the first item contains key and 296 | the second contains value. 297 | 298 | The second item can contain funcref. The function will be executed each time 299 | when this `front_matter` option is used. This can be used to insert the 300 | current Zettel ID, for example: > 301 | function! s:insert_id() 302 | if exists("g:zettel_current_id") 303 | return g:zettel_current_id 304 | else 305 | return "unnamed" 306 | endif 307 | endfunction 308 | 309 | let g:zettel_options = [{"front_matter" : 310 | [["tags" , ":hello:"], 311 | [ "id" , function("s:insert_id")]] 312 | }] 313 | < 314 | 315 | You can disable front matter completely by setting `disable_front_matter` 316 | option to other value than 0. 317 | 318 | The `template` field is discussed in |Vim-Zettel-Templates|. 319 | 320 | The `rel_path` field determines where new notes will be created, relative to 321 | the current vimwiki path. The trailing slash is required, e.g. if your vimwiki 322 | path is `~/vimwiki`, then `"rel_path": "zettel/"` will place zettels in 323 | `~/vimwiki/zettel/`. 324 | 325 | *g:zettel_format* 326 | By default, Vim-Zettel creates filenames in the form YYMMDD-HHMM. This 327 | format can be changed using the g:zettel_format variable. Any date and time 328 | formats supported by the `strftime()` function. 329 | 330 | It is also possible to use other formatting strings: 331 | 332 | - %title -- insert sanitized title 333 | - %raw_title -- insert raw title 334 | - %file_no -- sequentially number files in wiki 335 | - %file_alpha -- sequentially number files in wiki, but use characters 336 | instead of numbers 337 | - %random -- use random characters. Their number can be configured using 338 | `g:zettel_random_chars` option, eight is used by default. 339 | 340 | To use filename based on current time and note title, you can use the 341 | following format: 342 | > 343 | let g:zettel_format = "%y%m%d-%H%M-%title" 344 | < 345 | For sequentialy named files use: 346 | > 347 | let g:zettel_format = "%file_no" 348 | < 349 | If the generated file name exists (this may happen when you use the default 350 | format - `%y%m%d-%H-M`, letter suffix is added to the filename. You will then 351 | get `200622-1114` and `200622-1114a` when you create two notes in one minute. 352 | 353 | Vim-Zettel respects the `link_space_char` setting defined by vimwiki 354 | (see `:h links_space_char`), which lets you specify the separates used in place 355 | of spaces for filenames. 356 | 357 | You can use Vimwiki variable `g:vimwiki_markdown_link_ext` to require 358 | inclusion of the `.md` extension in links created by Vim-zettel (eq. using `z` 359 | or `T` keys). 360 | > 361 | let g:vimwiki_markdown_link_ext = 1 362 | < 363 | *g:zettel_default_title* 364 | Text used for `%title` formatting string in new Zettel filename if title was 365 | not provided. 366 | 367 | *g:zettel_date_format* 368 | Date format used for date metadata in front matter for a new zettel. It will 369 | need to be supported by the `strftime()` function. 370 | 371 | For example: 372 | > 373 | let g:zettel_date_format = "%y/%m/%d" 374 | < 375 | *g:zettel_default_mappings* 376 | The default mappings used by Vim-Zettel can be changed by setting the 377 | g:zettel_default_mappings variable to 0 and then prividing your own keymaps. 378 | The code below can serve as a template to start from. 379 | > 380 | let g:zettel_default_mappings = 0 381 | " This is basically the same as the default configuration 382 | augroup filetype_vimwiki 383 | autocmd! 384 | autocmd FileType vimwiki imap [[ [[ZettelSearchMap 385 | autocmd FileType vimwiki nmap T ZettelYankNameMap 386 | autocmd FileType vimwiki xmap z ZettelNewSelectedMap 387 | autocmd FileType vimwiki nmap gZ ZettelReplaceFileWithLink 388 | augroup END 389 | < 390 | *g:zettel_fzf_command* 391 | Vim-Zettel uses The Silver Searcher (ag) by default when searching through 392 | your files. The g:zettel_fzf_command can be used to override the default 393 | setting. > 394 | 395 | " command used for VimwikiSearch 396 | " default value is "ag". To use other command, like ripgrep, pass the 397 | " command line and options: 398 | let g:zettel_fzf_command = "rg --column --line-number --ignore-case \ 399 | --no-heading --color=always " 400 | 401 | < 402 | Note that if you want to use Ripgrep, you need to pass at least the `--column` 403 | option for the correct handling. 404 | 405 | *g:zettel_fzf_options* 406 | Options used for the `fzf` command. 407 | 408 | > 409 | let g:zettel_fzf_options = ['--exact', '--tiebreak=end'] 410 | 411 | < 412 | *g:zettel_generated_index_title* 413 | Text used as generated index section. 414 | 415 | > 416 | let g:zettel_generated_index_title = "Generated Index" 417 | < 418 | 419 | *g:zettel_generated_index_title_level* 420 | Header level to be used when generating the generated index section. 421 | 422 | > 423 | let g:zettel_generated_index_title_level = 1 424 | < 425 | 426 | *g:zettel_backlinks_title* 427 | Text used as back links section. 428 | 429 | > 430 | let g:zettel_backlinks_title = "Backlinks" 431 | < 432 | 433 | *g:zettel_backlinks_title_level* 434 | Header level to be used when generating the back links section. 435 | 436 | > 437 | let g:zettel_backlinks_title_level = 1 438 | < 439 | 440 | *g:zettel_unlinked_notes_title* 441 | Text used as unlinked notes section. 442 | 443 | > 444 | let g:zettel_unlinked_notes_title = "Unlinked Notes" 445 | < 446 | 447 | *g:zettel_unlinked_notes_title_level* 448 | Header level to be used when generating the unlinked notes section. 449 | 450 | > 451 | let g:zettel_unlinked_notes_title_level = 1 452 | < 453 | 454 | *g:zettel_generated_tags_title* 455 | Text used as generated tags section. 456 | 457 | > 458 | let g:zettel_generated_tags_title = "Generated Tags" 459 | < 460 | 461 | *g:zettel_generated_tags_title_level* 462 | Header level to be used when generating the generated tags section. 463 | 464 | > 465 | let g:zettel_generated_tags_title_level = 1 466 | < 467 | 468 | *g:zettel_link_format* 469 | Format used for generated links. You can change this variable if you are not 470 | satisfied with the default format. 471 | > 472 | let g:zettel_link_format="[%title](%link)" 473 | < 474 | 475 | *g:zettel_random_chars* 476 | 477 | Number of characters used in `%random` zettel name format. 478 | 479 | 480 | *g:zettel_bufflist_format* 481 | 482 | Format used in `ZettelSelectBuffer`. Available variables are `%filename` and 483 | `%title`. Note that for notes where title cannot be found, filename is used 484 | as title and filename is empty. 485 | > 486 | let g:zettel_bufflist_format = "%filename - %title 487 | < 488 | 489 | 490 | ============================================================================== 491 | 6. Templates *Vim-Zettel-Templates* 492 | 493 | It is possible to populate new notes with basic structure using templates. 494 | Template can be declared using the g:zettel_options variable: > 495 | 496 | let g:zettel_options = [{"template" : "~/path/to/mytemplate.tpl"}] 497 | < 498 | Sample template: > 499 | 500 | = %title = 501 | 502 | Backlink: %backlink 503 | ---- 504 | %footer 505 | < 506 | Variables that start with the % will be expanded. Supported variables: 507 | 508 | - %title - title of the new note 509 | - %id - filename of the new note 510 | - %backlink - back link to the parent note 511 | - %date - date and time of the new note. Format is based on 512 | `g:zettel_date_format` variable. 513 | - %footer - text from the parent note footer. Footer is separated from the 514 | main text by horizontal rule (----). It can contain some information 515 | shared by notes. For example notes about publication can share citation of 516 | that publication. 517 | 518 | All variables set in the `front_matter` option are available as well. 519 | 520 | ============================================================================== 521 | 7. Related packages *Vim-Zettel-Related* 522 | 523 | The following packages may be useful in conjunction with Vimwiki and 524 | Vim-Zettel: 525 | 526 | - [Notational FZF](https://github.com/alok/notational-fzf-vim) - fast 527 | searching notes with preview window. 528 | 529 | To search in the Zettelkasten, set the following variable with path to the 530 | Zettelkasten direcory in .vimrc: > 531 | 532 | let g:nv_search_paths = ['/path/to/zettelkasten/dir'] 533 | < 534 | - [Vimwiki-sync](https://github.com/michal-h21/vimwiki-sync) - automatically 535 | commit changes in wiki and synchronize them with external Git repository. 536 | 537 | 538 | ============================================================================== 539 | 8. Changelog *Vim-Zettel-ChangeLog* 540 | 541 | 2025/05/29 Michal Hoftich 542 | 543 | * autoload/zettel/vimwiki.vim: fixed automatic detection of grep-like command 544 | in `zettel#vimwiki#wikigrep`, it can be also configured using 545 | `g:zettel_wikigrep_command`. 546 | 547 | 2025/02/18 Matthieu Talbot 548 | 549 | * autoload/zettel/vimwiki.vim, 550 | * ftplugin/vimwiki/zettel.vim: added `ZettelCaptureSelected` function. 551 | 552 | 2025/02/18 Cameron Johnson 553 | 554 | * autoload/zettel/vimwiki.vim: fixed titles for external links. 555 | https://github.com/michal-h21/vim-zettel/pull/160 556 | 557 | 2025/02/05 Matthieu Talbot 558 | 559 | * doc/zettel.txt: added documentation for `ZettelNewSelectedMap` function. 560 | https://github.com/michal-h21/vim-zettel/pull/159 561 | 562 | 2024/11/28 Michal Hoftich 563 | 564 | * autoload/zettel/vimwiki.vim: use relative paths in links inserted by 565 | `zettel_new_selected()`. 566 | https://github.com/michal-h21/vim-zettel/issues/155 567 | 568 | * autoload/zettel/vimwiki.vim: fixed issues with backlinks to and from 569 | subdirectories. 570 | 571 | 2024-10-29 Matthieu Talbot 572 | 573 | * autoload/zettel/vimwiki.vim: fixed support for backlinks in subfolders. 574 | https://github.com/michal-h21/vim-zettel/pull/153 575 | 576 | 2024-07-30 Michal Hoftich 577 | 578 | * autoload/zettel/fzf.vim, 579 | * ftplugin/vimwiki/zettel.vim: enable selection of multiple selected notes 580 | https://github.com/michal-h21/vim-zettel/issues/151 581 | 582 | 2024-04-29 Matthieu Talbot 583 | 584 | * autoload/zettel/fzf.vim: Directly use the wiki extension setting instead of removing last dot. 585 | https://github.com/michal-h21/vim-zettel/pull/150 586 | 587 | 2024-04-25 Matthieu Talbot 588 | 589 | * autoload/zettel/vimwiki.vim: Fix link generation when wiki extension has multiple dots 590 | https://github.com/michal-h21/vim-zettel/pull/149 591 | 592 | 2024-01-31 l1laxS 593 | 594 | * tests/*: added testing infrastructure. 595 | https://github.com/michal-h21/vim-zettel/pull/146 596 | 597 | 598 | 2024-01-11 rfhlmn 599 | 600 | * autoload/zettel/vimwiki.vim: make link paths relative to current file. 601 | https://github.com/michal-h21/vim-zettel/pull/142 602 | 603 | 604 | 2024-01-09 Michal Hoftich 605 | 606 | * autoload/zettel/vimwiki.vim: fixed loading of Vimwiki's tags file. 607 | https://github.com/michal-h21/vim-zettel/issues/144 608 | 609 | 2023-12-17 Ulrich Wolf 610 | 611 | * autoload/zettel/vimwiki.vim: add extension to new notes created using 612 | `:ZettelNew` outside of the Zettel dir. 613 | 614 | 2023-10-31 Bence Ferdinandy 615 | 616 | * autoload/zettel/vimwiki.vim: added basic support for notes in subdirectories 617 | of the main wiki 618 | 619 | 2022-12-22 Michal Hoftich 620 | 621 | * autoload/zettel/vimwiki.vim: pass full path when creating new zettel, it is 622 | necessary for the correct capture. 623 | https://github.com/michal-h21/vim-zettel/issues/132 624 | * autoload/zettel/vimwiki.vim: fixed wiki `idx` retrieval when `:ZettelNew` is 625 | used outside of wiki. 626 | * autoload/zettel/vimwiki.vim, 627 | * ftplugin/vimwiki/zettel.vim: moved `g:zettel_format` declaration, to prevent 628 | errors in `zettel_new` executed outside of wiki. 629 | 630 | 2022-12-21 Michal Hoftich 631 | 632 | * autoload/zettel/fzf.vim: don't limit `execute_fzf` function to the wiki file 633 | extension. It caused search error on Windows. 634 | https://github.com/michal-h21/vim-zettel/issues/133 635 | 636 | 2022-07-19 Michal Hoftich 637 | 638 | * autoload/zettel/vimwiki.vim: fixed passed parameters for 639 | `vimwiki#base#check_links` in Vimwiki development mode, to prevent runtime 640 | errors in `:ZettelInbox`. 641 | https://github.com/michal-h21/vim-zettel/issues/127 642 | 643 | 2022-06-27 Michal Hoftich 644 | 645 | * ftplugin/vimwiki/zettel.vim: use `g[` mapping instead of `[[` in the visual 646 | mode for execution of the `:ZettelTitleSelected`. The former mapping didn't 647 | work for some users. 648 | 649 | 2022-06-22 Michal Hoftich 650 | 651 | * ftplugin/vimwiki/zettel.vim: added a new command, `:ZettelTitleSelected`. It 652 | uses visually selected text as a title for a link to a selected note. 653 | 654 | 2022-06-20 Leandro 655 | 656 | * ftplugin/vimwiki/zettel.vim: use noremap for mapings. 657 | 658 | 2022-06-13 Michal Hoftich 659 | 660 | * autoload/zettel/vimwiki.vim: added `s:is_wiki_devel` function for detection 661 | of the development version of Vimwiki. 662 | 663 | * autoload/zettel/vimwiki.vim: use the new parameter for `:ZettelInbox` only 664 | with development version of Vimwiki. 665 | 666 | 2022-06-13 Edward Joshua Evans 667 | 668 | * autoload/zettel/vimwiki.vim: select current Wiki for the `:ZettelInbox`. 669 | 670 | 2022-06-07 Michal Hoftich 671 | 672 | * autoload/zettel/fzf.vim: removed debugging messages. 673 | https://github.com/michal-h21/vim-zettel/issues/101 674 | * doc/zettel.txt: info about necessary command line options for Ripgrep 675 | https://github.com/michal-h21/vim-zettel/issues/71 676 | 677 | 2022-05-31 Michal Hoftich 678 | 679 | * autoload/zettel/vimwiki.vim: added %id variable to the new zettel templates. 680 | 681 | 2022-05-30 Michal Hoftich 682 | 683 | * autoload/zettel/fzf.vim: added `g:zettel_bufflist_format` template for 684 | formatting of the bufferlist listing. 685 | 686 | 2022-05-27 Michal Hoftich 687 | 688 | * autoload/zettel/fzf.vim: scroll to the line selected in :ZettelOpen. 689 | https://github.com/michal-h21/vim-zettel/issues/120 690 | * ftplugin/vimwiki/zettel.vim: added `:ZettelSelectBuffer` command. 691 | https://github.com/michal-h21/vim-zettel/issues/119 692 | 693 | 2022-05-26 Michal Hoftich 694 | 695 | * plugin/zettel.vim: don't initialize zettelkasten directory in 696 | ZettelInsertNote. 697 | * autoload/zettel/fzf.vim: initialize default wiki for FZF search. 698 | * autoload/zettel/fzf.vim: fixed ag search. 699 | 700 | 2021-12-08 Marc Mezzarobba 701 | 702 | * ftplugin/vimwiki/zettel.vim: support arbitrary registers in wiki_yank_name() 703 | https://github.com/michal-h21/vim-zettel/pull/116 704 | 705 | 2021-10-11 Phorv 706 | 707 | * autoload/zettel/fzf.vim: Use current wiki for ZettelInsertNote. 708 | https://github.com/michal-h21/vim-zettel/pull/114 709 | 710 | 2021-07-26 Sebastián Zaffarano 711 | 712 | * autoload/zettel/vimwiki.vim: make index, backlinks and unlinked section 713 | titles configurable. 714 | https://github.com/michal-h21/vim-zettel/pull/110 715 | 716 | 2021-02-20 Dennis Jung 717 | 718 | * autoload/zettel/vimwiki.vim: extract note title from first heading when 719 | title header is missing. 720 | https://github.com/michal-h21/vim-zettel/pull/105 721 | 722 | 2021-02-18 Guillermo Blanco 723 | 724 | * autoload/zettel/vimwiki.vim: correctly set the default wiki upon startup. 725 | https://github.com/michal-h21/vim-zettel/pull/104 726 | 727 | 2021-02-11 Ricky Anderson 728 | 729 | * ftplugin/vimwiki/zettel.vim: change default mappings to buffer local mappings. 730 | https://github.com/michal-h21/vim-zettel/pull/100 731 | 732 | 2021-01-20 Kraxli 733 | 734 | * autoload/zettel/fzf.vim: using fzf#vim#grep for ag in 735 | zettel#fzf#execute_fzf. 736 | https://github.com/michal-h21/vim-zettel/pull/97 737 | 738 | 2021-01-17 Kraxli 739 | 740 | * autoload/zettel/vimwiki.vim: run on non-vimwiki-buffer and directory to save 741 | new Zettel to. 742 | https://github.com/michal-h21/vim-zettel/pull/96 743 | 744 | 2021-01-13 Roman Garanin 745 | 746 | * doc/zettel.txt: fixed typo. 747 | https://github.com/michal-h21/vim-zettel/pull/92 748 | 749 | 2020-11-15 Benedikt Wildenhain 750 | 751 | * autoload/zettel/vimwiki.vim: make #vimwiki#make_random_chars compatible to 752 | Vim 8.1 753 | https://github.com/michal-h21/vim-zettel/pull/84 754 | 755 | 2020-11-02 Shaine Hatch 756 | 757 | * autoload/zettel/vimwiki.vim: Prevent duplicate file creation. 758 | https://github.com/michal-h21/vim-zettel/pull/78 759 | 760 | 2020-10-22 Doug Ghormley 761 | 762 | * autoload/zettel/vimwiki.vim: detect backlinks without titles in links. 763 | 764 | 2020-10-11 LeducH 765 | 766 | * README.md: `vim-plug` install details. 767 | 768 | 2020-09-24 Michal Hoftich 769 | 770 | * autoload/zettel/fzf.vim: use `'down': '~40%'` option for FZF. 771 | 772 | 2020-09-23 Michal Hoftich 773 | 774 | * autoload/zettel/vimwiki.vim: two new functions, 775 | `zettel#vimwiki#set_active_wiki` and 776 | `zettel#vimwiki#initialize_wiki_number`. Set active wiki number to 0 by 777 | default. 778 | * autoload/zettel/fzf.vim: initialize default wiki number to 0. 779 | * plugin/zettel.vim: added `:ZettelSetActiveWiki` command. 780 | * ftplugin/vimwiki/zettel.vim: moved `:ZettelOpen` command 781 | to `plugin/zettel.vim`, in order to be available outside Vimwiki 782 | https://github.com/michal-h21/vim-zettel/issues/69 783 | 784 | 2020-09-18 Michal Hoftich 785 | 786 | * autoload/zettel/vimwiki.vim: enable `%date` variable in templates. 787 | https://github.com/michal-h21/vim-zettel/issues/68 788 | 789 | * autoload/zettel/vimwiki.vim: added `disable_front_matter` option. It 790 | disables front matter in new zettels. 791 | https://github.com/michal-h21/vim-zettel/issues/67 792 | 793 | 2020-09-08 Michal Hoftich 794 | 795 | * autoload/zettel/fzf.vim: search only in files with the current wiki syntax. 796 | https://github.com/michal-h21/vim-zettel/issues/65 797 | 798 | 2020-09-07 Michal Hoftich 799 | 800 | * autoload/zettel/vimwiki.vim: enable functions to be passed as front matter 801 | values 802 | * autoload/zettel/vimwiki.vim: save the current zettel ID as a global variable 803 | https://github.com/michal-h21/vim-zettel/issues/66 804 | 805 | 2020-09-04 Michal Hoftich 806 | 807 | * autoload/zettel/vimwiki.vim: use `front_matter` variables in template 808 | expansion. 809 | 810 | 2020-09-03 Michal Hoftich 811 | 812 | * ftplugin/vimwiki.vim: renamed this file as `ftplugin/vimwiki/zettel.vim`, in 813 | order to prevent filename conflicts in some pluging managers. 814 | https://github.com/michal-h21/vim-zettel/issues/61 815 | 816 | 2020-09-02 Michal Hoftich 817 | 818 | * autoload/zettel/vimwiki.vim: added support for `%random` zettel name format. 819 | https://github.com/michal-h21/vim-zettel/issues/63 820 | * autoload/zettel/vimwiki.vim: `front_matter` variable should be list now, 821 | because it needs to keep a correct order. 822 | https://github.com/michal-h21/vim-zettel/issues/62 823 | 824 | 2020-08-28 Michal Hoftich 825 | 826 | * autoload/zettel/vimwiki.vim: fixed template application when new zettel 827 | filename starts with letter. 828 | https://github.com/michal-h21/vim-zettel/issues/59 829 | 830 | 2020-08-19 Michal Hoftich 831 | 832 | * autoload/zettel/vimwiki.vim: removed the older version of `update_listing` 833 | function, the code it depended on Vimwiki was removed. 834 | 835 | 2020-08-17 Michal Hoftich 836 | 837 | * doc/zettel.txt: don't create tags for `g:vimwiki_markdown_link_ext` 838 | variable, it can cause clashes with Vimwiki. 839 | https://github.com/michal-h21/vim-zettel/issues/57 840 | 841 | * doc/zettel.txt: link to mapping configuration from the mapping section. 842 | 843 | * doc/zettel.txt, 844 | * README.md: added main features of `vim-zettel`. 845 | 846 | 2020-08-13 Michal Hoftich 847 | 848 | * autoload/zettel/vimwiki.vim: support for anchored links in `:ZettelBackLinks` 849 | https://github.com/michal-h21/vim-zettel/issues/54 850 | 851 | * autoload/zettel/vimwiki.vim: fixed FZF support for notes with colons in 852 | filenames. 853 | https://github.com/michal-h21/vim-zettel/issues/55 854 | 855 | 2020-07-26 Michal Hoftich 856 | 857 | * autoload/zettel/vimwiki.vim: removed `:` character from all `execute` 858 | commands. 859 | 860 | 2020-07-21 Michal Hoftich 861 | 862 | * autoload/zettel/vimwiki.vim: added `g:zettel_link_format` variable. 863 | 864 | 2020-07-17 Michal Hoftich 865 | 866 | * autoload/zettel/vimwiki.vim: added support for `g:vimwiki_markdown_link_ext` 867 | setting. 868 | 869 | 2020-07-15 Dan Pilone 870 | 871 | * autoload/zettel/vimwiki.vim: don't add self-link when building backlinks. 872 | 873 | 2020-07-15 Dan Pilone 874 | 875 | * autoload/zettel/vimwiki.vim: don't add self-link when building backlinks. 876 | 877 | 2020-07-06 Irfan Sharif 878 | 879 | * autoload/zettel/vimwiki.vim: respect `links_space_char` setting from 880 | Vimwiki. 881 | 882 | 2020-07-04 Irfan Sharif 883 | 884 | * autoload/zettel/vimwiki.vim: listify results of :ZettelInbox. 885 | 886 | 2020-06-29 Michal Hoftich 887 | 888 | * autoload/zettel/vimwiki.vim: remove some punctuation from filenames in 889 | `zettel#vimwiki#escape_filename`. 890 | * autoload/zettel/vimwiki.vim: save file before it is read by 891 | `prepare_template_variables` 892 | https://github.com/michal-h21/vim-zettel/issues/43 893 | 894 | 2020-06-27 Irfan Sharif 895 | 896 | * autoload/zettel/vimwiki.vim: added `g:zettel_date_format` variable. 897 | 898 | 2020-06-26 Michal Hoftich 899 | 900 | * ftplugin/vimwiki.vim: detect correct register for `:ZettelYankName`. 901 | * autoload/zettel/vimwiki.vim: detect imported file title in 902 | `capture()`. 903 | * autoload/zettel/vimwiki.vim: use default title in `new_zettel_name()` 904 | 905 | 2020-06-25 Michal Hoftich 906 | 907 | * autoload/zettel/vimwiki.vim: add letter to filename if `:ZettelNew` is used 908 | multiple times in one minute. 909 | 910 | 2020-06-24 Michal Hoftich 911 | 912 | * autoload/zettel/vimwiki.vim: the back links title is configurable 913 | * autoload/zettel/vimwiki.vim: template is now available also in the 914 | `:ZettelNew` command. 915 | * ftplugin/vimwiki.vim: removed unused variable `g:zettel_filename_title`. 916 | * doc/zettel.txt: added ChangeLog and documented few variables. 917 | 918 | 919 | vim:tw=78:ts=8:ft=help 920 | -------------------------------------------------------------------------------- /ftplugin/vimwiki/zettel.vim: -------------------------------------------------------------------------------- 1 | 2 | function! s:wiki_yank_name() 3 | let filename = expand("%") 4 | let link = zettel#vimwiki#get_link(filename) 5 | call setreg(v:register, link) 6 | return link 7 | endfunction 8 | 9 | " replace file name under cursor which corresponds to a wiki file with a 10 | " corresponding Wiki link 11 | function! s:replace_file_with_link() 12 | let filename = expand("") 13 | let link = zettel#vimwiki#get_link(filename) 14 | execute "normal BvExa" . link 15 | endfunction 16 | 17 | 18 | 19 | 20 | 21 | " use visually selected text as a text for a link to note returned by FZF 22 | 23 | command! -bang -nargs=* ZettelSearch call zettel#fzf#sink_multifile(, 'zettel#fzf#wiki_search') 24 | 25 | command! -bang -nargs=* ZettelTitleSelected call zettel#fzf#sink_onefile(, 'zettel#fzf#title_selected') 26 | 27 | command! -bang -nargs=* ZettelYankName call wiki_yank_name() 28 | 29 | command! -buffer ZettelGenerateLinks call zettel#vimwiki#generate_links() 30 | command! -buffer -nargs=* -complete=custom,vimwiki#tags#complete_tags 31 | \ ZettelGenerateTags call zettel#vimwiki#generate_tags() 32 | 33 | command! -buffer ZettelBackLinks call zettel#vimwiki#backlinks() 34 | command! -buffer ZettelInbox call zettel#vimwiki#inbox() 35 | 36 | command! -bang -nargs=* ZettelSelectBuffer call fzf#run({ 37 | \ 'source': reverse(zettel#fzf#buflist()), 38 | \ 'sink': function('zettel#fzf#bufopen'), 39 | \ 'options': '+m', 40 | \ 'down': len(zettel#fzf#buflist()) + 2 41 | \ }) 42 | 43 | if !exists('g:zettel_default_mappings') 44 | let g:zettel_default_mappings=1 45 | endif 46 | 47 | 48 | nnoremap ZettelSearchMap :ZettelSearch 49 | nnoremap ZettelYankNameMap :ZettelYankName 50 | nnoremap ZettelReplaceFileWithLink :call replace_file_with_link() 51 | xnoremap ZettelNewSelectedMap :call zettel#vimwiki#zettel_new_selected() 52 | xnoremap ZettelCaptureSelected :call zettel#vimwiki#zettel_capture_selected() 53 | 54 | " make fulltext search in all VimWiki files using FZF and insert link to the 55 | " found file 56 | " is needed to prevent the "E481: No range alllowed" error 57 | xnoremap ZettelTitleSelectedMap :ZettelTitleSelected 58 | 59 | 60 | if g:zettel_default_mappings==1 61 | " inoremap [[ [[:ZettelSearch 62 | imap [[ [[ZettelSearchMap 63 | nmap T ZettelYankNameMap 64 | " xnoremap z :call zettel#vimwiki#zettel_new_selected() 65 | xmap z ZettelNewSelectedMap 66 | xmap g[ ZettelTitleSelectedMap 67 | nmap gZ ZettelReplaceFileWithLink 68 | endif 69 | -------------------------------------------------------------------------------- /plugin/zettel.vim: -------------------------------------------------------------------------------- 1 | if exists("g:loaded_zettel") || &cp 2 | finish 3 | endif 4 | 5 | let g:loaded_zettel = 1 6 | 7 | " gloabal commands 8 | command! -nargs=? -bang ZettelCapture 9 | \ call zettel#vimwiki#zettel_capture() 10 | 11 | " make fulltext search in all VimWiki files using FZF 12 | " command! -bang -nargs=* ZettelSearch call fzf#vim#ag(, 13 | command! -bang -nargs=* ZettelInsertNote call zettel#fzf#execute_fzf(, 14 | \'--skip-vcs-ignores', fzf#vim#with_preview({ 15 | \'down': '~40%', 16 | \'sink*':function('zettel#fzf#insert_note'), 17 | \'options':['--exact']})) 18 | 19 | " set number of the active wiki 20 | command! -nargs=1 -bang ZettelSetActiveWiki call zettel#vimwiki#set_active_wiki() 21 | 22 | " make fulltext search in all VimWiki files using FZF and open the found file 23 | command! -bang -nargs=* ZettelOpen call zettel#fzf#sink_onefile(, 'zettel#fzf#search_open') 24 | 25 | " crate new zettel using command 26 | command! -bang -nargs=* ZettelNew call zettel#vimwiki#zettel_new() 27 | 28 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | This folder contains the tests for the vim-zettel plugin. 2 | 3 | # How to run the tests 4 | The tests run with a dedicated vimrc (`./test_vimrc`), which specifies minimal settings and configures vimwiki and vim-zettel to use the wikis under `./resources/`. 5 | 6 | ## Prerequisites 7 | - Install `vader`, `vimwiki`, `vim-zettel` 8 | - Create a file named `rtp.vim` under `tests/` 9 | - In `tests/rtp.vim`, add the plugins installation path to the `rtp`: (adjust the paths as needed) 10 | ```vim 11 | set rtp+=~/.vim/pack/test-tools/start/vader.vim 12 | set rtp+=~/.vim/pack/zettel-plugins/start/vimwiki 13 | set rtp+=~/.vim/pack/zettel-plugins/start/vim-zettel 14 | ``` 15 | 16 | ## Run the tests 17 | To run all tests, change your working directory to `tests/` and issue the command: 18 | ```bash 19 | vim -u test_vimrc -i NONE -c "Vader *" 20 | ``` 21 | This starts vim: 22 | - with `test_vimrc` as `.vimrc` (`-u test_vimrc`) 23 | - with no `.viminfo` file (`-i NONE`) 24 | - and issues the Ex command `:Vader *` (`-c "Vader *"`), which will execute all `*.vader` files 25 | 26 | ### CAUTION :warning: 27 | If you want to run the tests from vim with `:Vader *`, make sure to start vim with the `./test_vimrc` (`vim -u ./test_vimrc`). 28 | Otherwise the tests (potentially destructive) will run on your wiki list. 29 | 30 | -------------------------------------------------------------------------------- /tests/resources/templ.tpl: -------------------------------------------------------------------------------- 1 | go back: %backlink 2 | 3 | # %title 4 | 5 | type: %type 6 | -------------------------------------------------------------------------------- /tests/test_vimrc: -------------------------------------------------------------------------------- 1 | " Determine absolute path of the `tests/` folder 2 | let TEST_DIR_PATH = expand(':p:h') 3 | 4 | " Discover the rtp configuration file 5 | let rtp_file = TEST_DIR_PATH."/"."rtp.vim" 6 | if filereadable(rtp_file) 7 | execute 'source ' . rtp_file 8 | else 9 | echohl WarningMsg 10 | echo 'Define a file named `rtp.vim`, setting the rtp for vader, vimwiki and vim-zettel!' 11 | echohl None 12 | finish 13 | endif 14 | 15 | " cleanup files generated during previous test run 16 | call system('rm -f '.TEST_DIR_PATH.'/resources/tmp-wiki/*') 17 | 18 | " vimrc configuration 19 | filetype plugin indent on 20 | syntax enable 21 | set nocompatible 22 | 23 | let vimwiki_markdown = {} 24 | let vimwiki_markdown.path = TEST_DIR_PATH .'/resources/tmp-wiki' 25 | let vimwiki_markdown.syntax = 'markdown' 26 | let vimwiki_markdown.ext = '.md' 27 | 28 | let g:vimwiki_list = [ vimwiki_markdown ] 29 | -------------------------------------------------------------------------------- /tests/zettel_creation.vader: -------------------------------------------------------------------------------- 1 | Before (Open wiki index and go to beginning of the file): 2 | VimwikiIndex 3 | execute "normal! gg" 4 | 5 | -------------------------------------------------------------------------------- 6 | Do (create new zettel with z): 7 | cc 8 | new zettel with default settings\ 9 | 0wwv3ez\ 10 | 11 | Then (new zettel with default frontmatter has been created): 12 | :let date=strftime('%Y-%m-%d %H:%M') 13 | :AssertEqual '---', getline(1) 14 | :AssertEqual 'title: with default settings', getline(2) 15 | :AssertEqual 'date: '.date, getline(3) 16 | :AssertEqual '---', getline(4) 17 | 18 | -------------------------------------------------------------------------------- 19 | Do (check index.md): 20 | k 21 | 22 | Then (link has been created in place of highlighted text): 23 | :AssertEqual 'new zettel [with default settings]('.strftime('%y%m%d-%H%M').')', getline(1) 24 | 25 | -------------------------------------------------------------------------------- 26 | Execute (define front_matter): 27 | let zo_markdown = {} 28 | let zo_markdown.front_matter = [ ["tags", ""], ["type", "note"] ] 29 | let g:zettel_options = [ zo_markdown ] 30 | Log 'zettel_options: ' . string(g:zettel_options) 31 | 32 | Do (create new zettel with z): 33 | Go 34 | zettel with user defined frontmatter\ 35 | 0wwv3ez\ 36 | 37 | Then (new zettel with user defined frontmatter has been created): 38 | :let date=strftime('%Y-%m-%d %H:%M') 39 | :AssertEqual '---', getline(1) 40 | :AssertEqual 'title: user defined frontmatter', getline(2) 41 | :AssertEqual 'date: '.date, getline(3) 42 | :AssertEqual 'tags: ', getline(4) 43 | :AssertEqual 'type: note', getline(5) 44 | :AssertEqual '---', getline(6) 45 | 46 | -------------------------------------------------------------------------------- 47 | Execute (define template): 48 | let zo_markdown = {} 49 | let zo_markdown.template = "./resources/templ.tpl" 50 | let g:zettel_options = [ zo_markdown ] 51 | Log 'zettel_options: ' . string(g:zettel_options) 52 | 53 | Do (create new zettel with z): 54 | Go 55 | zettel with user defined template\ 56 | 0wwv3ez\ 57 | 58 | Then (new zettel with default frontmatter and template has been created): 59 | :let date=strftime('%Y-%m-%d %H:%M') 60 | :AssertEqual '---', getline(1) 61 | :AssertEqual 'title: user defined template', getline(2) 62 | :AssertEqual 'date: '.date, getline(3) 63 | :AssertEqual '---', getline(4) 64 | :AssertEqual '', getline(5) 65 | :AssertEqual 'go back: [index](index)', getline(6) 66 | :AssertEqual '', getline(7) 67 | :AssertEqual '# user defined template', getline(8) 68 | :AssertEqual '', getline(9) 69 | :AssertEqual 'type: %type', getline(10) 70 | 71 | -------------------------------------------------------------------------------- 72 | Execute (define front_matter and template): 73 | let zo_markdown = {} 74 | let zo_markdown.front_matter = [ ["tags", ""], ["type", "note"] ] 75 | let zo_markdown.template = "./resources/templ.tpl" 76 | let g:zettel_options = [ zo_markdown ] 77 | Log 'zettel_options: ' . string(g:zettel_options) 78 | 79 | Do (create new zettel with z): 80 | Go 81 | zettel with frontmatter and template\ 82 | 0wwv3ez\ 83 | 84 | Then (new zettel with user defined frontmatter and template has been created): 85 | :let date=strftime('%Y-%m-%d %H:%M') 86 | :AssertEqual '---', getline(1) 87 | :AssertEqual 'title: frontmatter and template', getline(2) 88 | :AssertEqual 'date: '.date, getline(3) 89 | :AssertEqual 'tags: ', getline(4) 90 | :AssertEqual 'type: note', getline(5) 91 | :AssertEqual '---', getline(6) 92 | :AssertEqual '', getline(7) 93 | :AssertEqual 'go back: [index](index)', getline(8) 94 | :AssertEqual '', getline(9) 95 | :AssertEqual '# frontmatter and template', getline(10) 96 | :AssertEqual '', getline(11) 97 | :AssertEqual 'type: note', getline(12) 98 | 99 | --------------------------------------------------------------------------------