1
0
mirror of https://github.com/amix/vimrc synced 2025-07-03 14:14:59 +08:00

Cleaning deps.

This commit is contained in:
Maksim Pecherskiy
2014-08-07 19:42:41 -04:00
parent 4541dd93ef
commit 2deb035254
266 changed files with 26588 additions and 31 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,84 @@
" Vim auto-load script
" Author: Peter Odding <peter@peterodding.com>
" Last Change: June 23, 2013
" URL: http://peterodding.com/code/vim/notes/
if !exists('g:notes_markdown_program')
let g:notes_markdown_program = 'markdown'
endif
function! xolox#notes#html#view() " {{{1
" Convert the current note to a web page and show the web page in a browser.
" Requires [Markdown] [markdown] to be installed; you'll get a warning if it
" isn't.
"
" [markdown]: http://en.wikipedia.org/wiki/Markdown
try
" Convert the note's text to HTML using Markdown.
let starttime = xolox#misc#timer#start()
let note_title = xolox#notes#current_title()
let filename = xolox#notes#title_to_fname(note_title)
let note_text = join(getline(1, '$'), "\n")
let raw_html = xolox#notes#html#convert_note(note_text)
let styled_html = xolox#notes#html#apply_template({
\ 'encoding': &encoding,
\ 'title': note_title,
\ 'content': raw_html,
\ 'version': g:xolox#notes#version,
\ 'date': strftime('%A %B %d, %Y at %H:%M'),
\ 'filename': fnamemodify(filename, ':~'),
\ })
let filename = s:create_temporary_file(note_title)
if writefile(split(styled_html, "\n"), filename) != 0
throw printf("Failed to write HTML file! (%s)", filename)
endif
" Open the generated HTML in a web browser.
call xolox#misc#open#url('file://' . filename)
call xolox#misc#timer#stop("notes.vim %s: Rendered HTML preview in %s.", g:xolox#notes#version, starttime)
catch
call xolox#misc#msg#warn("notes.vim %s: %s at %s", g:xolox#notes#version, v:exception, v:throwpoint)
endtry
endfunction
function! xolox#notes#html#convert_note(note_text) " {{{1
" Convert a note's text to a web page (HTML) using the [Markdown text
" format] [markdown] as an intermediate format. This function takes the text
" of a note (the first argument) and converts it to HTML, returning a
" string.
if !executable(g:notes_markdown_program)
throw "HTML conversion requires the `markdown' program! On Debian/Ubuntu you can install it by executing `sudo apt-get install markdown'."
endif
let markdown = xolox#notes#markdown#convert_note(a:note_text)
let result = xolox#misc#os#exec({'command': g:notes_markdown_program, 'stdin': markdown})
let html = join(result['stdout'], "\n")
return html
endfunction
function! xolox#notes#html#apply_template(variables) " {{{1
" The vim-notes plug-in contains a web page template that's used to provide
" a bit of styling when a note is converted to a web page and presented to
" the user. This function takes the original HTML produced by [Markdown]
" [markdown] (the first argument) and wraps it in the configured template,
" returning the final HTML as a string.
let filename = expand(g:notes_html_template)
call xolox#misc#msg#debug("notes.vim %s: Reading web page template from %s ..", g:xolox#notes#version, filename)
let template = join(readfile(filename), "\n")
let output = substitute(template, '{{\(.\{-}\)}}', '\= s:template_callback(a:variables)', 'g')
return output
endfunction
function! s:template_callback(variables) " {{{1
" Callback for xolox#notes#html#apply_template().
let key = xolox#misc#str#trim(submatch(1))
return get(a:variables, key, '')
endfunction
function! s:create_temporary_file(note_title) " {{{1
" Create a temporary filename for a note converted to an HTML document,
" based on the title of the note.
if !exists('s:temporary_directory')
let s:temporary_directory = xolox#misc#path#tempdir()
endif
let filename = xolox#misc#str#slug(a:note_title) . '.html'
return xolox#misc#path#merge(s:temporary_directory, filename)
endfunction

View File

@ -0,0 +1,94 @@
" Vim auto-load script
" Author: Peter Odding <peter@peterodding.com>
" Last Change: June 23, 2013
" URL: http://peterodding.com/code/vim/notes/
function! xolox#notes#markdown#view() " {{{1
" Convert the current note to a Markdown document and show the converted text.
let note_text = join(getline(1, '$'), "\n")
let markdown_text = xolox#notes#markdown#convert_note(note_text)
vnew
call setline(1, split(markdown_text, "\n"))
setlocal filetype=markdown
endfunction
function! xolox#notes#markdown#convert_note(note_text) " {{{1
" Convert a note's text to the [Markdown text format] [markdown]. The syntax
" used by vim-notes has a lot of similarities with Markdown, but there are
" some notable differences like the note title and the way code blocks are
" represented. This function takes the text of a note (the first argument)
" and converts it to the Markdown format, returning a string.
"
" [markdown]: http://en.wikipedia.org/wiki/Markdown
let starttime = xolox#misc#timer#start()
let blocks = xolox#notes#parser#parse_note(a:note_text)
call map(blocks, 'xolox#notes#markdown#convert_block(v:val)')
let markdown = join(blocks, "\n\n")
call xolox#misc#timer#stop("notes.vim %s: Converted note to Markdown in %s.", g:xolox#notes#version, starttime)
return markdown
endfunction
function! xolox#notes#markdown#convert_block(block) " {{{1
" Convert a single block produced by `xolox#misc#notes#parser#parse_note()`
" (the first argument, expected to be a dictionary) to the [Markdown text
" format] [markdown]. Returns a string.
if a:block.type == 'title'
let text = s:make_urls_explicit(a:block.text)
return printf("# %s", text)
elseif a:block.type == 'heading'
let marker = repeat('#', 1 + a:block.level)
let text = s:make_urls_explicit(a:block.text)
return printf("%s %s", marker, text)
elseif a:block.type == 'code'
let comment = "<!-- An innocent comment to force Markdown out of list parsing mode. See also http://meta.stackoverflow.com/a/99637 -->"
let text = xolox#misc#str#indent(xolox#misc#str#dedent(a:block.text), 4)
return join([comment, text], "\n\n")
elseif a:block.type == 'divider'
return '* * *'
elseif a:block.type == 'list'
let items = []
if a:block.ordered
let counter = 1
for item in a:block.items
let indent = repeat(' ', item.indent * 4)
let text = s:make_urls_explicit(item.text)
call add(items, printf("%s%d. %s", indent, counter, text))
let counter += 1
endfor
else
for item in a:block.items
let indent = repeat(' ', item.indent * 4)
let text = s:make_urls_explicit(item.text)
call add(items, printf("%s- %s", indent, text))
endfor
endif
return join(items, "\n\n")
elseif a:block.type == 'block-quote'
let lines = []
for line in a:block.lines
let prefix = repeat('>', line.level)
call add(lines, printf('%s %s', prefix, line.text))
endfor
return join(lines, "\n")
elseif a:block.type == 'paragraph'
let text = s:make_urls_explicit(a:block.text)
if len(text) <= 50 && text =~ ':$'
let text = printf('**%s**', text)
endif
return text
else
let msg = "Encountered unsupported block: %s!"
throw printf(msg, string(a:block))
endif
endfunction
function! s:make_urls_explicit(text) " {{{1
" In the vim-notes syntax, URLs are implicitly hyperlinks.
" In Markdown syntax they have to be wrapped in <markers>.
return substitute(a:text, g:xolox#notes#url_pattern, '\= s:url_callback(submatch(0))', 'g')
endfunction
function! s:url_callback(url)
let label = substitute(a:url, '^\w\+:\(//\)\?', '', '')
return printf('[%s](%s)', label, a:url)
endfunction

View File

@ -0,0 +1,333 @@
" Vim auto-load script
" Author: Peter Odding <peter@peterodding.com>
" Last Change: July 18, 2013
" URL: http://peterodding.com/code/vim/notes/
function! xolox#notes#parser#parse_note(text) " {{{1
" Parser for the note taking syntax used by vim-notes.
let starttime = xolox#misc#timer#start()
let context = s:create_parse_context(a:text)
let note_title = context.next_line()
let blocks = [{'type': 'title', 'text': note_title}]
while context.has_more()
let chr = context.peek(1)
if chr == "\n"
" Ignore empty lines.
call context.next(1)
continue
elseif chr == '#'
let block = s:parse_heading(context)
elseif chr == '>'
let block = s:parse_block_quote(context)
elseif chr == '{' && context.peek(3) == "\{\{\{"
let block = s:parse_code_block(context)
else
let lookahead = s:match_bullet_or_divider(context, 0)
if !empty(lookahead)
if lookahead.type =~ 'list'
let block = s:parse_list(context)
elseif lookahead.type == 'divider'
let block = s:parse_divider(context)
else
let msg = "Programming error! Unsupported lookahead: %s."
throw printf(msg, string(lookahead))
endif
else
let block = s:parse_paragraph(context)
endif
endif
" Don't include empty blocks in the output.
if !empty(block)
call add(blocks, block)
endif
endwhile
call xolox#misc#timer#stop("notes.vim %s: Parsed note into %i blocks in %s.", g:xolox#notes#version, len(blocks), starttime)
return blocks
endfunction
function! xolox#notes#parser#view_parse_nodes() " {{{1
" Parse the current note and show the parse nodes in a temporary buffer.
let note_text = join(getline(1, '$'), "\n")
let parse_nodes = xolox#notes#parser#parse_note(note_text)
vnew
call setline(1, map(parse_nodes, 'string(v:val)'))
setlocal filetype=vim nomodified nowrap
endfunction
function! s:create_parse_context(text) " {{{1
" Create an object to encapsulate the lowest level of parser state.
let context = {'text': a:text, 'index': 0}
" The has_more() method returns 1 (true) when more input is available, 0
" (false) otherwise.
function context.has_more()
return self.index < len(self.text)
endfunction
" The peek() method returns the next character without consuming it.
function context.peek(n)
if self.has_more()
return self.text[self.index : self.index + (a:n - 1)]
endif
return ''
endfunction
" The next() method returns the next character and consumes it.
function context.next(n)
let result = self.peek(a:n)
let self.index += a:n
return result
endfunction
" The next_line() method returns the current line and consumes it.
function context.next_line()
let line = ''
while self.has_more()
let chr = self.next(1)
if chr == "\n" || chr == ""
" We hit the end of line or input.
return line
else
" The line continues.
let line .= chr
endif
endwhile
return line
endfunction
return context
endfunction
function! s:match_bullet_or_divider(context, consume_lookahead) " {{{1
" Check whether the current line starts with a list bullet.
let result = {}
let context = copy(a:context)
let line = context.next_line()
let bullet = matchstr(line, s:bullet_pattern)
if !empty(bullet)
" Disambiguate list bullets from horizontal dividers.
if line =~ '^\s\+\*\s\*\s\*$'
let result.type = 'divider'
else
" We matched a bullet! Now we still need to distinguish ordered from
" unordered list items.
if bullet =~ '\d'
let result.type = 'ordered-list'
else
let result.type = 'unordered-list'
endif
let indent = matchstr(bullet, '^\s*')
let result.indent = len(indent)
" Since we already skipped the whitespace and matched the bullet, it's
" very little work to mark our position for the benefit of the caller.
if a:consume_lookahead
let a:context.index += len(bullet)
endif
endif
endif
return result
endfunction
function! s:match_line(context) " {{{1
" Get the text of the current line, stopping at end of the line or just
" before the start of a code block marker, whichever comes first.
let line = ''
while a:context.has_more()
let chr = a:context.peek(1)
if chr == '{' && a:context.peek(3) == "\{\{\{"
" XXX The start of a code block implies the end of whatever came before.
" The marker above contains back slashes so that Vim doesn't apply
" folding because of the marker :-).
return line
elseif chr == "\n"
call a:context.next(1)
return line . "\n"
else
let line .= a:context.next(1)
endif
endwhile
" We hit the end of the input.
return line
endfunction
function! s:parse_heading(context) " {{{1
" Parse the upcoming heading in the input stream.
let level = 0
while a:context.peek(1) == '#'
let level += 1
call a:context.next(1)
endwhile
let text = xolox#misc#str#trim(s:match_line(a:context))
return {'type': 'heading', 'level': level, 'text': text}
endfunction
function! s:parse_block_quote(context) " {{{1
" Parse the upcoming block quote in the input stream.
let lines = []
while a:context.has_more()
if a:context.peek(1) != '>'
break
endif
let line = s:match_line(a:context)
let level = len(matchstr(line, '^>\+'))
let text = matchstr(line, '^>\+\s*\zs.\{-}\ze\_s*$')
call add(lines, {'level': level, 'text': text})
endwhile
return {'type': 'block-quote', 'lines': lines}
endfunction
function! s:parse_code_block(context) " {{{1
" Parse the upcoming code block in the input stream.
let language = ''
let text = ''
" Skip the start marker.
call a:context.next(3)
" Get the optional language name.
while a:context.peek(1) =~ '\w'
let language .= a:context.next(1)
endwhile
" Skip the whitespace separating the start marker and/or language name from
" the text.
while a:context.peek(1) =~ '[ \t]'
call a:context.next(1)
endwhile
" Get the text inside the code block.
while a:context.has_more()
let chr = a:context.next(1)
if chr == '}' && a:context.peek(2) == '}}'
call a:context.next(2)
break
endif
let text .= chr
endwhile
" Strip trailing whitespace.
let text = substitute(text, '\_s\+$', '', '')
return {'type': 'code', 'language': language, 'text': text}
endfunction
function! s:parse_divider(context) " {{{1
" Parse the upcoming horizontal divider in the input stream.
call a:context.next_line()
return {'type': 'divider'}
endfunction
function! s:parse_list(context) " {{{1
" Parse the upcoming sequence of list items in the input stream.
let list_type = 'unknown'
let items = []
let lines = []
let indent = 0
" Outer loop to consume one or more list items.
while a:context.has_more()
let lookahead = s:match_bullet_or_divider(a:context, 1)
if !empty(lookahead)
" Save the previous list item with the old indent level.
call s:save_item(items, lines, indent)
let lines = []
" Set the new indent level (three spaces -> one level).
let indent = lookahead.indent / 3
" The current line starts with a list bullet.
if list_type == 'unknown'
" The first bullet determines the type of list.
let list_type = lookahead.type
endif
endif
let line = s:match_line(a:context)
call add(lines, line)
if line[-1:] != "\n"
" XXX When match_line() returns a line that doesn't end in a newline
" character, it means either we hit the end of the input or the current
" line continues in a code block (which is not ours to parse :-).
break
elseif line =~ '^\_s*$'
" For now an empty line terminates the list item.
" TODO Add support for list items with multiple paragraphs of text.
break
endif
endwhile
call s:save_item(items, lines, indent)
return {'type': 'list', 'ordered': (list_type == 'ordered-list'), 'items': items}
endfunction
function! s:save_item(items, lines, indent)
let text = join(a:lines, "\n")
if text =~ '\S'
let text = xolox#misc#str#compact(text)
call add(a:items, {'text': text, 'indent': a:indent})
endif
endfunction
function! s:parse_paragraph(context) " {{{1
" Parse the upcoming paragraph in the input stream.
let lines = []
while a:context.has_more()
if !empty(s:match_bullet_or_divider(a:context, 0))
" If the next line starts with a list bullet it shouldn't
" be included in the paragraph we're currently parsing.
break
else
let line = s:match_line(a:context)
call add(lines, line)
if line =~ '^\_s*$'
" An empty line marks the end of the paragraph.
break
elseif line[-1:] != "\n"
" XXX When match_line() returns a line that doesn't end in a newline
" character, it means either we hit the end of the input or the current
" line continues in a code block (which is not ours to parse :-).
break
endif
endif
endwhile
" Don't include empty paragraphs in the output.
let text = join(lines, "\n")
if text =~ '\S'
return {'type': 'paragraph', 'text': xolox#misc#str#compact(text)}
else
return {}
endif
endfunction
function! s:generate_list_item_bullet_pattern() " {{{1
" Generate a regular expression that matches any kind of list bullet.
let choices = copy(g:notes_unicode_bullets)
for bullet in g:notes_ascii_bullets
call add(choices, xolox#misc#escape#pattern(bullet))
endfor
call add(choices, '\d\+[[:punct:]]\?')
return join(choices, '\|')
endfunction
let s:bullet_pattern = '^\s*\(' . s:generate_list_item_bullet_pattern() . '\)\s\+'
function! xolox#notes#parser#run_tests() " {{{1
" Tests for the note taking syntax parser.
call xolox#misc#test#reset()
call xolox#misc#test#wrap('xolox#notes#parser#test_parsing_of_note_titles')
call xolox#misc#test#wrap('xolox#notes#parser#test_parsing_of_headings')
call xolox#misc#test#wrap('xolox#notes#parser#test_parsing_of_paragraphs')
call xolox#misc#test#wrap('xolox#notes#parser#test_parsing_of_code_blocks')
call xolox#misc#test#wrap('xolox#notes#parser#test_parsing_of_list_items')
call xolox#misc#test#wrap('xolox#notes#parser#test_parsing_of_block_quotes')
call xolox#misc#test#summarize()
endfunction
function! xolox#notes#parser#test_parsing_of_note_titles()
call xolox#misc#test#assert_equals([{'type': 'title', 'text': 'Just the title'}], xolox#notes#parser#parse_note('Just the title'))
endfunction
function! xolox#notes#parser#test_parsing_of_headings()
call xolox#misc#test#assert_equals([{'type': 'title', 'text': 'Just the title'}, {'type': 'heading', 'level': 1, 'text': 'This is a heading'}], xolox#notes#parser#parse_note("Just the title\n\n# This is a heading"))
endfunction
function! xolox#notes#parser#test_parsing_of_paragraphs()
call xolox#misc#test#assert_equals([{'type': 'title', 'text': 'Just the title'}, {'type': 'paragraph', 'text': 'This is a paragraph'}], xolox#notes#parser#parse_note("Just the title\n\nThis is a paragraph"))
call xolox#misc#test#assert_equals([{'type': 'title', 'text': 'Just the title'}, {'type': 'paragraph', 'text': 'This is a paragraph'}, {'type': 'paragraph', 'text': "And here's another paragraph!"}], xolox#notes#parser#parse_note("Just the title\n\nThis is a paragraph\n\n\n\nAnd here's another paragraph!"))
endfunction
function! xolox#notes#parser#test_parsing_of_code_blocks()
call xolox#misc#test#assert_equals([{'type': 'title', 'text': 'Just the title'}, {'type': 'code', 'language': '', 'text': "This is a code block\nwith two lines"}], xolox#notes#parser#parse_note("Just the title\n\n{{{ This is a code block\nwith two lines }}}"))
endfunction
function! xolox#notes#parser#test_parsing_of_list_items()
call xolox#misc#test#assert_equals([{'type': 'title', 'text': 'Just the title'}, {'type': 'list', 'ordered': 1, 'items': [{'indent': 0, 'text': 'item one'}, {'indent': 0, 'text': 'item two'}, {'indent': 0, 'text': 'item three'}]}], xolox#notes#parser#parse_note("Just the title\n\n1. item one\n2. item two\n3. item three"))
endfunction
function! xolox#notes#parser#test_parsing_of_block_quotes()
call xolox#misc#test#assert_equals([{'type': 'title', 'text': 'Just the title'}, {'type': 'block-quote', 'lines': [{'level': 1, 'text': 'block'}, {'level': 2, 'text': 'quoted'}, {'level': 1, 'text': 'text'}]}], xolox#notes#parser#parse_note("Just the title\n\n> block\n>> quoted\n> text"))
endfunction

View File

@ -0,0 +1,100 @@
" Vim auto-load script
" Author: Peter Odding <peter@peterodding.com>
" Last Change: May 16, 2013
" URL: http://peterodding.com/code/vim/notes/
function! xolox#notes#recent#show(bang, title_filter) " {{{1
call xolox#misc#msg#info("notes.vim %s: Generating overview of recent notes ..", g:xolox#notes#version)
" Show generated note listing all notes by last modified time.
let starttime = xolox#misc#timer#start()
let bufname = '[Recent Notes]'
" Prepare a buffer to hold the list of recent notes.
call xolox#misc#buffer#prepare({
\ 'name': bufname,
\ 'path': xolox#misc#path#merge($HOME, bufname)})
" Filter notes by pattern (argument)?
let notes = []
let title_filter = '\v' . a:title_filter
for [fname, title] in items(xolox#notes#get_fnames_and_titles(0))
if title =~? title_filter
call add(notes, [getftime(fname), title])
endif
endfor
" Start note with "You have N note(s) [matching filter]".
let readme = "You have "
if empty(notes)
let readme .= "no notes"
elseif len(notes) == 1
let readme .= "one note"
else
let readme .= len(notes) . " notes"
endif
if a:title_filter != ''
let quote_format = xolox#notes#unicode_enabled() ? '%s' : "`%s'"
let readme .= " matching " . printf(quote_format, a:title_filter)
endif
" Explain the sorting of the notes.
if empty(notes)
let readme .= "."
elseif len(notes) == 1
let readme .= ", it's listed below."
else
let readme .= ". They're listed below grouped by the day they were edited, starting with your most recently edited note."
endif
" Add the generated text to the buffer.
call setline(1, ["Recent notes", "", readme])
" Reformat the text in the buffer to auto-wrap.
normal Ggqq
" Sort, group and format the list of (matching) notes.
let last_date = ''
let list_item_format = xolox#notes#unicode_enabled() ? ' • %s' : ' * %s'
call sort(notes)
call reverse(notes)
let lines = []
for [ftime, title] in notes
let date = xolox#notes#friendly_date(ftime)
if date != last_date
call add(lines, '')
call add(lines, substitute(date, '^\w', '\u\0', '') . ':')
let last_date = date
endif
call add(lines, printf(list_item_format, title))
endfor
" Add the formatted list of notes to the buffer.
call setline(line('$') + 1, lines)
" Load the notes file type.
call xolox#notes#set_filetype()
let &l:statusline = bufname
" Change the status line
" Lock the buffer contents.
call xolox#misc#buffer#lock()
" And we're done!
call xolox#misc#timer#stop("notes.vim %s: Generated %s in %s.", g:xolox#notes#version, bufname, starttime)
endfunction
function! xolox#notes#recent#track() " {{{1
let fname = expand('%:p')
let indexfile = expand(g:notes_recentindex)
call xolox#misc#msg#debug("notes.vim %s: Recording '%s' as most recent note in %s ..", g:xolox#notes#version, fname, indexfile)
if writefile([fname], indexfile) == -1
call xolox#misc#msg#warn("notes.vim %s: Failed to record most recent note in %s!", g:xolox#notes#version, indexfile)
endif
endfunction
function! xolox#notes#recent#edit(bang) " {{{1
" Edit the most recently edited (not necessarily changed) note.
let indexfile = expand(g:notes_recentindex)
call xolox#misc#msg#debug("notes.vim %s: Recalling most recent note from %s ..", g:xolox#notes#version, indexfile)
try
let fname = readfile(indexfile)[0]
if empty(fname)
throw "The index of recent notes is empty?!"
endif
catch
call xolox#misc#msg#warn("notes.vim %s: Failed to recall most recent note from %s: %s", g:xolox#notes#version, indexfile, v:exception)
return
endtry
call xolox#misc#msg#info("notes.vim %s: Editing most recent note '%s' ..", g:xolox#notes#version, fname)
execute 'edit' . a:bang fnameescape(fname)
call xolox#notes#set_filetype()
endfunction

View File

@ -0,0 +1,201 @@
" Vim auto-load script
" Author: Peter Odding <peter@peterodding.com>
" Last Change: May 5, 2013
" URL: http://peterodding.com/code/vim/notes/
if !exists('s:currently_tagged_notes')
let s:currently_tagged_notes = {} " The in-memory representation of tags and the notes in which they're used.
let s:previously_tagged_notes = {} " Copy of index as it is / should be now on disk (to detect changes).
let s:last_disk_sync = 0 " Whether the on-disk representation of the tags has been read.
let s:buffer_name = 'Tagged Notes' " The buffer name for the list of tagged notes.
let s:loading_index = 0
endif
function! xolox#notes#tags#load_index() " {{{1
if s:loading_index
" Guard against recursive calls.
return s:currently_tagged_notes
endif
let starttime = xolox#misc#timer#start()
let indexfile = expand(g:notes_tagsindex)
let lastmodified = getftime(indexfile)
if lastmodified == -1
let s:loading_index = 1
call xolox#notes#tags#create_index()
let s:loading_index = 0
elseif lastmodified > s:last_disk_sync
let s:currently_tagged_notes = {}
for line in readfile(indexfile)
let filenames = split(line, "\t")
if len(filenames) > 1
let tagname = remove(filenames, 0)
let s:currently_tagged_notes[tagname] = filenames
endif
endfor
let s:previously_tagged_notes = deepcopy(s:currently_tagged_notes)
let s:last_disk_sync = lastmodified
call xolox#misc#timer#stop("notes.vim %s: Loaded tags index in %s.", g:xolox#notes#version, starttime)
endif
return s:currently_tagged_notes
endfunction
function! xolox#notes#tags#create_index() " {{{1
let exists = filereadable(expand(g:notes_tagsindex))
let starttime = xolox#misc#timer#start()
let filenames = xolox#notes#get_fnames(0)
let s:currently_tagged_notes = {}
for idx in range(len(filenames))
if filereadable(filenames[idx])
let title = xolox#notes#fname_to_title(filenames[idx])
call xolox#misc#msg#info("notes.vim %s: Scanning note %i/%i: %s", g:xolox#notes#version, idx + 1, len(filenames), title)
call xolox#notes#tags#scan_note(title, join(readfile(filenames[idx]), "\n"))
endif
endfor
if xolox#notes#tags#save_index()
let s:previously_tagged_notes = deepcopy(s:currently_tagged_notes)
call xolox#misc#timer#stop('notes.vim %s: %s tags index in %s.', g:xolox#notes#version, exists ? "Updated" : "Created", starttime)
else
call xolox#misc#msg#warn("notes.vim %s: Failed to save tags index as %s!", g:xolox#notes#version, g:notes_tagsindex)
endif
endfunction
function! xolox#notes#tags#save_index() " {{{1
let indexfile = expand(g:notes_tagsindex)
let existingfile = filereadable(indexfile)
let nothingchanged = (s:currently_tagged_notes == s:previously_tagged_notes)
if existingfile && nothingchanged
call xolox#misc#msg#debug("notes.vim %s: Index not dirty so not saved.", g:xolox#notes#version)
return 1 " Nothing to be done
else
let lines = []
for [tagname, filenames] in items(s:currently_tagged_notes)
call add(lines, join([tagname] + filenames, "\t"))
endfor
let status = writefile(lines, indexfile) == 0
if status
call xolox#misc#msg#debug("notes.vim %s: Index saved to %s.", g:xolox#notes#version, g:notes_tagsindex)
let s:last_disk_sync = getftime(indexfile)
else
call xolox#misc#msg#debug("notes.vim %s: Failed to save index to %s.", g:xolox#notes#version, g:notes_tagsindex)
endif
return status
endif
endfunction
function! xolox#notes#tags#scan_note(title, text) " {{{1
" Add a note to the tags index.
call xolox#notes#tags#load_index()
" Don't scan tags inside code blocks.
let text = substitute(a:text, '{{{\w\+\_.\{-}}}}', '', 'g')
" Split everything on whitespace.
for token in split(text)
" Match words that start with @ and don't contain { (BibTeX entries).
if token =~ '^@\w' && token !~ '{'
" Strip any trailing punctuation.
let token = substitute(token[1:], '[[:punct:]]*$', '', '')
if token != ''
if !has_key(s:currently_tagged_notes, token)
let s:currently_tagged_notes[token] = [a:title]
elseif index(s:currently_tagged_notes[token], a:title) == -1
" Keep the tags sorted.
call xolox#misc#list#binsert(s:currently_tagged_notes[token], a:title, 1)
endif
endif
endif
endfor
endfunction
function! xolox#notes#tags#forget_note(title) " {{{1
" Remove a note from the tags index.
call xolox#notes#tags#load_index()
for tagname in keys(s:currently_tagged_notes)
call filter(s:currently_tagged_notes[tagname], "v:val != a:title")
if empty(s:currently_tagged_notes[tagname])
unlet s:currently_tagged_notes[tagname]
endif
endfor
endfunction
function! xolox#notes#tags#show_tags(minsize) " {{{1
" TODO Mappings to "zoom" in/out (show only big tags).
let starttime = xolox#misc#timer#start()
call xolox#notes#tags#load_index()
let lines = [s:buffer_name, '']
if empty(s:currently_tagged_notes)
call add(lines, "You haven't used any tags yet!")
else
" Create a dictionary with note titles as keys.
let unmatched = {}
for title in xolox#notes#get_titles(0)
let unmatched[title] = 1
endfor
let totalnotes = len(unmatched)
" Group matching notes and remove them from the dictionary.
let grouped_notes = []
let numtags = 0
for tagname in sort(keys(s:currently_tagged_notes), 1)
let numnotes = len(s:currently_tagged_notes[tagname])
if numnotes >= a:minsize
let matched_notes = s:currently_tagged_notes[tagname]
for title in matched_notes
if has_key(unmatched, title)
unlet unmatched[title]
endif
endfor
call add(grouped_notes, {'name': tagname, 'notes': matched_notes})
let numtags += 1
endif
endfor
" Add a "fake tag" with all unmatched notes.
if !empty(unmatched)
call add(grouped_notes, {'name': "Unmatched notes", 'notes': keys(unmatched)})
endif
" Format the results as a note.
let bullet = xolox#notes#get_bullet('*')
for group in grouped_notes
let tagname = group['name']
let friendly_name = xolox#notes#tags#friendly_name(tagname)
let numnotes = len(group['notes'])
if numnotes >= a:minsize
call extend(lines, ['', printf('# %s (%i note%s)', friendly_name, numnotes, numnotes == 1 ? '' : 's'), ''])
for title in group['notes']
let lastmodified = xolox#notes#friendly_date(getftime(xolox#notes#title_to_fname(title)))
call add(lines, ' ' . bullet . ' ' . title . ' (last edited ' . lastmodified . ')')
endfor
endif
endfor
if a:minsize <= 1
let message = printf("You've used %i %s in %i %s",
\ numtags, numtags == 1 ? "tag" : "tags",
\ totalnotes, totalnotes == 1 ? "note" : "notes")
else
let message = printf("There %s %i %s that %s been used at least %s times",
\ numtags == 1 ? "is" : "are", numtags,
\ numtags == 1 ? "tag" : "tags",
\ numtags == 1 ? "has" : "have", a:minsize)
endif
let message .= ", " . (numtags == 1 ? "it's" : "they're")
let message .= " listed below. Tags and notes are sorted alphabetically and after each note is the date when it was last modified."
if !empty(unmatched)
if a:minsize <= 1
let message .= " At the bottom is a list of untagged notes."
else
let message .= " At the bottom is a list of unmatched notes."
endif
endif
if numtags > 1 && !(&foldmethod == 'expr' && &foldenable)
let message .= " You can enable text folding to get an overview of just the tag names and how many times they've been used."
endif
call insert(lines, message, 2)
endif
call xolox#misc#buffer#prepare(s:buffer_name)
call setline(1, lines)
call xolox#misc#buffer#lock()
call xolox#notes#set_filetype()
setlocal nospell wrap
call xolox#misc#timer#stop('notes.vim %s: Generated [%s] in %s.', g:xolox#notes#version, s:buffer_name, starttime)
endfunction
function! xolox#notes#tags#friendly_name(tagname) " {{{1
return substitute(a:tagname, '\(\U\)\(\u\)', '\1 \2', 'g')
endfunction