From dff977f50d0b6687636631fc61806f322bc8aa21 Mon Sep 17 00:00:00 2001 From: Gregory Leeman Date: Tue, 22 Mar 2022 12:44:03 +0000 Subject: [PATCH] init --- README.md | 5 + autoload/notecrate.vim | 363 +++++++++++++++++++++++++++++++++++++++++ ftplugin/notecrate.vim | 34 ++++ plugin/notecrate.vim | 9 + syntax/notecrate.vim | 37 +++++ 5 files changed, 448 insertions(+) create mode 100644 README.md create mode 100644 autoload/notecrate.vim create mode 100644 ftplugin/notecrate.vim create mode 100644 plugin/notecrate.vim create mode 100644 syntax/notecrate.vim diff --git a/README.md b/README.md new file mode 100644 index 0000000..117163b --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# notecrate + +## Requirements + + diff --git a/autoload/notecrate.vim b/autoload/notecrate.vim new file mode 100644 index 0000000..3d94035 --- /dev/null +++ b/autoload/notecrate.vim @@ -0,0 +1,363 @@ +function! notecrate#get_visual_selection() " {{{ + let [line_start, column_start] = getpos("'<")[1:2] + let [line_end, column_end] = getpos("'>")[1:2] + let lines = getline(line_start, line_end) + if len(lines) == 0 + return '' + endif + let lines[-1] = lines[-1][: column_end - (&selection == 'inclusive' ? 1 : 2)] + let lines[0] = lines[0][column_start - 1:] + return join(lines, "\n") +endfunction + +" }}} +function! notecrate#get_word_at_cursor() " {{{ + let l:word = expand('') + return l:word +endfunction +" }}} +function! notecrate#get_link_at_cursor() " {{{ + let l:regex = '\[\([^\]]*\)\](\([^)]*\))' + let l:line = getline('.') + let l:cursor = col('.') - 1 + let l:match = matchstrpos(l:line, l:regex, 0) + while 1 + if (l:match[1] == -1) + return '' + elseif l:match[1] <= l:cursor && l:cursor < l:match[2] + break + endif + let l:match = matchstrpos(l:line, l:regex, l:match[2] + 1) + endwhile + let l:matches = matchlist(l:match[0], l:regex) + return {'text': l:matches[1], 'dest': l:matches[2]} +endfunction + +" }}} +function! notecrate#generate_filename() " {{{ + return strftime("%Y%m%d%H%M%S") . ".md" +endfunction + +" }}} +function! notecrate#apply_template(title) " {{{ + let l:template = "# " . a:title . "\n\n\n---\n\n" + call setreg("l", l:template) + silent execute 'normal "lP4j' + call notecrate#update_backlinks() +endfunction + +" }}} +function! notecrate#convert() " {{{ + execute "normal! :w!\:!pushd " . b:notecrate_dir . "; bash convert.sh;\" +endfunction + +" }}} +function! notecrate#save() " {{{ + execute "normal! :!cd " . b:notecrate_dir . "; git add -A; git commit -m \"autocommit\"; git push;\" +endfunction + +" }}} +function! notecrate#todo_filter_view() " {{{ + execute "normal! :!cd " . b:notecrate_dir . "; git add -A; git commit -m \"autocommit\"; git push;\" +endfunction + +" }}} +function! notecrate#follow_link() " {{{ + let l:link = notecrate#get_link_at_cursor()" + if type(l:link) == 4 + if l:link['dest'] =~ '^.*\.md$' + call notecrate#open_note(l:link['dest']) + else + silent execute "open " . l:link['dest'] + endif + endif +endfunction + +" }}} +function! notecrate#new_note(title, filename) " {{{ + let l:filename = expand('%:t') + while !isdirectory(expand(b:notecrate_dir)) + let choice = confirm('', b:notecrate_dir . " does not exist. Create? &Yes\n&No\n") + if choice == 1 + silent execute "!mkdir " . b:notecrate_dir + else + return 0 + endif + endwhile + let l:path = b:notecrate_dir . "/" . a:filename + call add(b:notecrate_history, l:filename) + silent execute "normal! :w\:e " . l:path . "\" + call notecrate#apply_template(a:title) + call notecrate#update_backlinks() +endfunction + +" }}} +function! notecrate#new_note_from_prompt() " {{{ + let l:title = input("Name of new note? ") + let l:filename = notecrate#generate_filename() + call notecrate#new_note(l:title, l:filename) +endfunction + +" }}} +function! notecrate#new_note_from_selection() " {{{ + let l:title = notecrate#get_visual_selection() + let l:filename = notecrate#generate_filename() + silent execute "normal! :'<,'>s/\\%V.*\\%V/[" . l:title . "](" . l:filename . ")/e\" + call notecrate#new_note(l:title, l:filename) +endfunction + +" }}} +function! notecrate#open_note(filename) " {{{ + let l:filename = expand('%:t') + let l:path = b:notecrate_dir . "/" . a:filename + if !filereadable(expand(l:path)) + echo "Note does't exist!" + return + endif + call add(b:notecrate_history, l:filename) + silent execute "e " . l:path + call notecrate#update_backlinks() + silent execute "normal! /^#\" +endfunction + +" }}} +function! notecrate#open_index() " {{{ + while !isdirectory(expand(b:notecrate_dir)) + let choice = confirm('', b:notecrate_dir . " does not exist. Create? &Yes\n&No\n") + if choice == 1 + silent execute "!mkdir " . b:notecrate_dir + else + return 0 + endif + endwhile + let l:filename = "index.md" + let l:path = b:notecrate_dir . "/" . l:filename + if !filereadable(expand(l:path)) + echo "Note does't exist!" + return + endif + silent execute "e " . l:path + call notecrate#update_backlinks() + silent execute "normal! /^#\" +endfunction + +" }}} +function! notecrate#open_previous() " {{{ + if len(b:notecrate_history) == 0 + return + endif + let l:filename = remove(b:notecrate_history, -1) + call notecrate#open_note(l:filename) + call remove(b:notecrate_history, -1) +endfunction + +" }}} +function! notecrate#get_title(filename) " {{{ + let l:regex = '^# \(\S.*\)$' + let l:path = b:notecrate_dir . "/" . a:filename + let l:lines = readfile(expand(l:path)) + let l:line = matchstr(l:lines, l:regex) + if l:line != "" + return matchlist(l:line, l:regex)[1] + endif + return '' +endfunction + +" }}} +function! notecrate#get_id(filename) " {{{ + return = matchstr(a:filename, '^.*\(\.md$\)\@=') +endfunction + +" }}} +function! notecrate#fzf_sink(sink_function) " {{{ + " let l:additional_options = get(a:, 1, {}) + let l:preview_options = { + \ 'sink' : function(a:sink_function), + \ 'down' : '~40%', + \ 'dir' : b:notecrate_dir, + \ 'options' : ['--exact', '--tiebreak=end'] + \ } + " call fzf#vim#ag("^(?=.)", fzf#vim#with_preview(l:preview_options)) + call fzf#vim#ag("^# ", fzf#vim#with_preview(l:preview_options)) +endfunction + +" }}} +function! notecrate#search_open() " {{{ + call notecrate#fzf_sink('notecrate#open_from_fzf') +endfunction +function! notecrate#open_from_fzf(line) + let filename = substitute(a:line, ":[0-9]\*:[0-9]\*:.\*$", "", "") + call notecrate#open_note(filename) +endfunction + +" }}} +function! notecrate#search_insert_link() " {{{ + call notecrate#fzf_sink('notecrate#insert_link_from_fzf') +endfunction +function! notecrate#insert_link_from_fzf(line) + let l:filename = substitute(a:line, ":[0-9]\*:[0-9]\*:.\*$", "", "") + let l:title = notecrate#get_title(filename) + execute "normal! a[" . l:title . "](" . l:filename . ")\" +endfunction + +" }}} +function! notecrate#search_insert_link_selection() " {{{ + call notecrate#fzf_sink('notecrate#insert_link_from_fzf_selection') +endfunction +function! notecrate#insert_link_from_fzf_selection(line) + let l:filename = substitute(a:line, ":[0-9]\*:[0-9]\*:.\*$", "", "") + let l:title = notecrate#get_visual_selection() + silent execute "normal! :'<,'>s/\\%V.*\\%V/[" . l:title . "](" . l:filename . ")/e\" +endfunction + +" }}} +function! notecrate#grep(pattern) " {{{ + let l:files = [] + try + silent execute 'vimgrep /' . a:pattern . '/j ' . b:notecrate_dir . '/*.md' + catch /^Vim\%((\a\+)\)\=:E480/ " No Match + endtry + for d in getqflist() + let l:filename = fnamemodify(bufname(d.bufnr), ":t") + call add(l:files, l:filename) + endfor + call uniq(l:files) + return l:files +endfunction + +" }}} +function! notecrate#grep_links(pattern) " {{{ + let l:files = notecrate#grep(a:pattern) + let l:links = [] + for filename in l:files + if filename != "index.md" + let l:title = notecrate#get_title(filename) + call add(l:links, "* [" . l:title . "](" . filename . ")") + endif + endfor + return sort(l:links) +endfunction + +" }}} +function! notecrate#update_backlinks() " {{{ + let l:filename = expand('%:t') + if l:filename == "index.md" + return + endif + let l:pattern = '\[[^\]]*\](' . l:filename . ')\(\(.*\n\)*---\)\@=' + let l:links = notecrate#grep_links(l:pattern) + let l:backlinks = "---\n\n" + if len(l:links) == 0 + let l:backlinks = l:backlinks . "* [Index](index.md)" + else + let l:backlinks = l:backlinks . join(uniq(l:links), "\n") . "\n\n* [Index](index.md)" + endif + let l:oldreg = getreg("0") + call setreg("l", l:backlinks) + silent execute "normal! /^---\vG$\"lp" + call setreg("0", l:oldreg) + call setreg("", l:oldreg) +endfunction + +" }}} +function! notecrate#delete_links(filename) " {{{ + execute "!" . g:gsed_command . " -i 's/\\[\\([^]]*\\)\\](" . a:filename . ")/\\1/g' " . b:notecrate_dir . "/*md" +endfunction + +" }}} +function! notecrate#delete_note() " {{{ + let l:filename = expand('%:t') + let l:path = b:notecrate_dir . "/" . l:filename + let choice = confirm('', "Delete " . l:filename . "? &Yes\n&No\n") + if choice == 1 + call delete(l:path) + call notecrate#delete_links(l:filename) + silent execute "!rm " l:path + silent execute "normal :bp!\" + endif +endfunction + +" }}} +function! notecrate#indent_level(lnum) " {{{ + return indent(a:lnum) / &shiftwidth +endfunction + +" }}} +function! notecrate#heading_depth(lnum) " {{{ + let l:depth=0 + let l:thisLine = getline(a:lnum) + if l:thisLine =~ '^#\+\s\+' + let l:hashCount = len(matchstr(thisLine, '^#\{1,6}')) + if l:hashCount > 0 + let l:depth = hashCount - 1 + endif + elseif l:thisLine != '' + let l:nextLine = getline(a:lnum + 1) + if l:nextLine =~ '^=\+\s*$' + let l:depth = 1 + elseif l:nextLine =~ '^-\+\s*$' + let l:depth = 2 + endif + endif + return l:depth +endfunction + +" }}} +function! notecrate#nested_markdown_folds(lnum) " {{{ + let l:thisLine = getline(a:lnum) + let l:thisDepth = notecrate#heading_depth(a:lnum) + let l:thisIndent = notecrate#indent_level(a:lnum) + let l:prevLine = getline(a:lnum - 1) + let l:prevIndent = notecrate#indent_level(a:lnum - 1) + let l:nextLine = getline(a:lnum + 1) + let l:nextIndent = notecrate#indent_level(a:lnum + 1) + let l:nextDepth = notecrate#heading_depth(a:lnum + 1) + + if l:thisLine =~ '^\s*<' && l:prevLine =~ '^\s*$' + return 1 + endif + + if l:thisLine =~ '^\s*$' && l:prevLine =~ '^\s*<' + return 0 + endif + + if l:nextLine =~ '^---$' || l:thisLine =~ '^---$' + return 0 + endif + + if l:thisLine =~ '^\s*$' && l:nextDepth > 0 + return -1 + endif + + if l:thisDepth > 0 + return ">".l:thisDepth + endif + + if l:nextIndent == l:thisIndent + return "=" + endif + + if l:nextIndent > l:thisIndent + let l:dif = l:nextIndent - l:thisIndent + return "a".l:dif + endif + + if l:nextIndent < l:thisIndent + let l:dif = l:thisIndent - l:nextIndent + return "s".l:dif + endif + +endfunction + +" }}} +function! notecrate#fold_text() " {{{ + if getline(v:foldstart) =~ "^\s*<" + return "<>" . repeat(" ", winwidth(0)) + endif + let l:ret = repeat(" ", indent(v:foldstart)) . trim(getline(v:foldstart))[0:-1] . " +" . repeat(" ", winwidth(0)) + let l:ret = substitute(l:ret, ' \S\+:\S\+', '', 'g') + let l:ret = substitute(l:ret, '\*\*', '', 'g') + return l:ret +endfunction + +" }}} diff --git a/ftplugin/notecrate.vim b/ftplugin/notecrate.vim new file mode 100644 index 0000000..adf5227 --- /dev/null +++ b/ftplugin/notecrate.vim @@ -0,0 +1,34 @@ +setlocal textwidth=79 +setlocal autowriteall +setlocal comments+=b:* +setlocal foldlevel=3 +setlocal formatoptions=cro + +command! SearchOpen :call notecrate#base#search_open() +command! SearchInsertLink :call notecrate#base#search_insert_link() +command! UpdateBacklinks :call notecrate#base#update_backlinks() +command! NewNote :call notecrate#base#new_note_from_prompt() +command! DeleteNote :call notecrate#base#delete_note() +command! Convert :call notecrate#base#convert() +command! Save :call notecrate#base#save() + +" inoremap mmgqis`m +nnoremap c :Convert +nnoremap o :SearchOpen +nnoremap i :SearchInsertLink +nnoremap b :UpdateBacklinks +nnoremap n :NewNote +nnoremap d :DeleteNote +nnoremap s :Convert:Save +nnoremap :call notecrate#base#follow_link() +vnoremap :call notecrate#base#new_note_from_selection() +nnoremap :call notecrate#base#open_previous() +nnoremap /\[[^\]]*\]([^)]*):noh +nnoremap /\[[^\]]*\]([^)]*):noh +nnoremap /\[[^\]]*\]([^)]*):noh +nnoremap /\[[^\]]*\]([^)]*)NN:noh +nnoremap /\[[^\]]*\]([^)]*)NN:noh +nnoremap /\[[^\]]*\]([^)]*)NN:noh +vnoremap i :call notecrate#base#search_insert_link_selection() +inoremap +inoremap diff --git a/plugin/notecrate.vim b/plugin/notecrate.vim new file mode 100644 index 0000000..e73dd07 --- /dev/null +++ b/plugin/notecrate.vim @@ -0,0 +1,9 @@ +if !exists("g:notecrate_dirs") + let g:notecrate_dirs = { + \ "notes": { "prefix": "n", "dir": "~/notecrate"} + \ } +endif +for [key, value] in items(g:notecrate_dirs) + execute "autocmd BufRead,BufNewFile " . value["dir"] . "/*.md set filetype=notecrate syntax=notecrate" + execute "normal! :nnoremap w" . value["prefix"] . " :e " . value["dir"] . "/index.md:let b:notecrate_dir = \"" . value["dir"] . "\":let b:notecrate_history = []\" +endfor diff --git a/syntax/notecrate.vim b/syntax/notecrate.vim new file mode 100644 index 0000000..b5b6bd7 --- /dev/null +++ b/syntax/notecrate.vim @@ -0,0 +1,37 @@ +setlocal foldmethod=expr +setlocal foldexpr=notecrate#base#nested_markdown_folds(v:lnum) +setlocal foldtext=notecrate#base#fold_text() +setlocal conceallevel=2 +set comments=b:* +setlocal nowrap + +syn match NotecrateLinkInternal /\(\](\)\@<=[^()]*\()\)\@=/ +syn match NotecrateLinkConceal /!*\[\+\([^\]]*](\)\@=/ conceal +syn match NotecrateLinkConceal /\]\+\((\)\@=/ conceal +syn match NotecrateLinkConceal /\(\[[^\]]*\]\)\@<=([^)]*)/ conceal contains=NotecrateLinkInternal +syn match NotecrateLink /\(\[\)\@<=[^\[\]]*\(\](\)\@=/ +syn match NotecrateLinkImage /\(!\[\+\)\@<=[^\[\]]*\(\]\+\)\@=/ +syn match NotecrateHeader1 /\(^# \)\@<=.*/ +syn match NotecrateHeader2 /\(^##\+ \)\@<=.*/ +syn match NotecrateRule /^---\+/ +syn region NotecrateFrontCustomMatter start=/\%^---/ end=/^---/ +syn region NotecrateCode start=/^```/ end=/^```/ +" syn match NotecrateBoldConceal /\*\*/ conceal containedin=ALL +syn match NotecrateBoldConceal /\*\*/ containedin=ALL +syn region NotecrateBold start=/\*\*/ end=/\*\*/ contains=NotecrateBoldConceal keepend +syn match NotecrateQuote /^>.*$/ +syn match NotecrateTag /#[^# ]\S*/ + +hi def link NotecrateBold base2 +hi def link NotecrateTag cyanu +hi def link NotecrateCode red +hi def link NotecrateFrontCustomMatter base01 +hi def link NotecrateHeader1 base3u +hi def link NotecrateHeader2 base1u +hi def link NotecrateLink blueu +hi def link NotecrateLinkConceal blue +hi def link NotecrateLinkImage redu +hi def link NotecrateLinkInternal orangeu +hi def link NotecrateList base2 +hi def link NotecrateQuote cyan +hi def link NotecrateRule base01