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