Move tags-related functions to extra file

also rename some functions from vimwiki#tags#bla() to s:bla()
This commit is contained in:
EinfachToll 2015-02-23 11:29:02 +01:00
parent 8055ef8608
commit 7e87e92954
3 changed files with 281 additions and 281 deletions

View File

@ -1934,282 +1934,8 @@ endfunction "}}}
" }}}
" TAGS functions {{{
let s:TAGS_METADATA_FILE_NAME = '.tags'
" Tags metadata in-memory format:
" metadata := [ entry, ... ]
" entry := { 'tagname':..., 'pagename':..., 'lineno':..., 'link':... }
" Tags metadata in-file format:
"
" Is based on CTags format (see |tags-file-format|).
"
" {tagaddress} is set to lineno. We'll let vim search by exact line number; we
" can afford that, we assume metadata file is always updated before use.
"
" Pagename and link are not saved in standard ctags fields, so we'll add
" an optional field, "vimwiki:". In this field, we encode tab-separated values
" of missing parameters -- "pagename" and "link".
" vimwiki#base#update_tags
" Update tags metadata.
" a:full_rebuild == 1: re-scan entire wiki
" a:full_rebuild == 0: only re-scan current page
function! vimwiki#base#update_tags(full_rebuild) "{{{
if !a:full_rebuild
" Updating for one page (current)
let page_name = expand('%:t:r')
" Collect tags in current file
let tags = vimwiki#base#scan_tags(getline(1, '$'), page_name)
" Load metadata file
let metadata = vimwiki#base#load_tags_metadata()
" Drop old tags
let metadata = vimwiki#base#remove_page_from_tags(metadata, page_name)
" Merge in the new ones
let metadata = vimwiki#base#merge_tags(metadata, tags)
" Save
call vimwiki#base#write_tags_metadata(metadata)
else " full rebuild
let files = s:find_files(g:vimwiki_current_idx, 0)
let metadata = []
for file in files
let page_name = fnamemodify(file, ':t:r')
let tags = vimwiki#base#scan_tags(readfile(file), page_name)
let metadata = vimwiki#base#merge_tags(metadata, tags)
endfor
call vimwiki#base#write_tags_metadata(metadata)
endif
endfunction " }}}
" vimwiki#base#scan_tags
" Scans the list of text lines (argument) and produces tags metadata.
function! vimwiki#base#scan_tags(lines, page_name) "{{{
let metadata = []
let page_name = a:page_name
" Code wireframe to scan for headers -- borrowed from
" vimwiki#base#get_anchors(), with minor modifications.
let rxheader = g:vimwiki_{VimwikiGet('syntax')}_header_search
let rxtag = g:vimwiki_{VimwikiGet('syntax')}_tag_search
let anchor_level = ['', '', '', '', '', '', '']
let current_complete_anchor = ''
let PROXIMITY_LINES_NR = 5
let header_line_nr = - (2 * PROXIMITY_LINES_NR)
for line_nr in range(1, len(a:lines))
let line = a:lines[line_nr - 1]
" process headers
let h_match = matchlist(line, rxheader)
if !empty(h_match) " got a header
let header_line_nr = line_nr
let header = vimwiki#u#trim(h_match[2])
let level = len(h_match[1])
let anchor_level[level-1] = header
for l in range(level, 6)
let anchor_level[l] = ''
endfor
if level == 1
let current_complete_anchor = header
else
let current_complete_anchor = ''
for l in range(level-1)
if anchor_level[l] != ''
let current_complete_anchor .= anchor_level[l].'#'
endif
endfor
let current_complete_anchor .= header
endif
continue " tags are not allowed in headers
endif
" TODO ignore verbatim blocks
" Scan line for tags. There can be many of them.
let str = line
while 1
let tag_group = matchstr(str, rxtag)
if tag_group == ''
break
endif
let tagend = matchend(str, rxtag)
let str = str[(tagend):]
for tag in split(tag_group, ':')
" Create metadata entry
let entry = {}
let entry.tagname = tag
let entry.pagename = page_name
let entry.lineno = line_nr
if line_nr <= (header_line_nr + PROXIMITY_LINES_NR)
let entry.link = page_name . '#' . current_complete_anchor
elseif header_line_nr < 0
" Tag appeared before the first header
let entry.link = page_name
else
let entry.link = page_name . '#' . tag
endif
call add(metadata, entry)
endfor
endwhile
endfor " loop over lines
return metadata
endfunction " }}}
" vimwiki#base#metadata_file_path
" Returns tags metadata file path
function! vimwiki#base#metadata_file_path() abort "{{{
return fnamemodify(VimwikiGet('path') . '/' . s:TAGS_METADATA_FILE_NAME, ':p')
endfunction " }}}
" vimwiki#base#load_tags_metadata
" Loads tags metadata from file, returns a dictionary
function! vimwiki#base#load_tags_metadata() abort "{{{
let metadata_path = vimwiki#base#metadata_file_path()
if !filereadable(metadata_path)
return []
endif
let metadata = []
for line in readfile(metadata_path)
if line =~ '^!_TAG_FILE_'
continue
endif
let parts = matchlist(line, '^\(.\{-}\);"\(.*\)$')
if parts[0] == '' || parts[1] == '' || parts[2] == ''
throw 'VimwikiTags1: Metadata file corrupted'
endif
let std_fields = split(parts[1], '\t')
if len(std_fields) != 3
throw 'VimwikiTags2: Metadata file corrupted'
endif
let vw_part = parts[2]
if vw_part[0] != "\t"
throw 'VimwikiTags3: Metadata file corrupted'
endif
let vw_fields = split(vw_part[1:], "\t")
if len(vw_fields) != 1 || vw_fields[0] !~ '^vimwiki:'
throw 'VimwikiTags4: Metadata file corrupted'
endif
let vw_data = substitute(vw_fields[0], '^vimwiki:', '', '')
let vw_data = substitute(vw_data, '\\n', "\n", 'g')
let vw_data = substitute(vw_data, '\\r', "\r", 'g')
let vw_data = substitute(vw_data, '\\t', "\t", 'g')
let vw_data = substitute(vw_data, '\\\\', "\\", 'g')
let vw_fields = split(vw_data, "\t")
if len(vw_fields) != 2
throw 'VimwikiTags5: Metadata file corrupted'
endif
let entry = {}
let entry.tagname = std_fields[0]
let entry.pagename = vw_fields[0]
let entry.lineno = std_fields[2]
let entry.link = vw_fields[1]
call add(metadata, entry)
endfor
return metadata
endfunction " }}}
" vimwiki#base#remove_page_from_tags
" Removes all entries for given page from metadata in-place. Returns updated
" metadata (just in case).
function! vimwiki#base#remove_page_from_tags(metadata, page_name) "{{{
let metadata = filter(a:metadata,
\ "v:val.pagename != '" . substitute(a:page_name, "'", "''", '') . "'")
return metadata
endfunction " }}}
" vimwiki#base#merge_tags
" Merges two tags metadata objects into (new) one.
function! vimwiki#base#merge_tags(metadata1, metadata2) "{{{
return a:metadata1 + a:metadata2
endfunction " }}}
" vimwiki#base#write_tags_metadata
" Saves metadata object into a file. Throws exceptions in case of problems.
function! vimwiki#base#write_tags_metadata(metadata) "{{{
let metadata_path = vimwiki#base#metadata_file_path()
let entries = []
for entry in a:metadata
let entry_data = entry.pagename . "\t" . entry.link
let entry_data = substitute(entry_data, "\\", '\\\\', 'g')
let entry_data = substitute(entry_data, "\t", '\\t', 'g')
let entry_data = substitute(entry_data, "\r", '\\r', 'g')
let entry_data = substitute(entry_data, "\n", '\\n', 'g')
call add(entries,
\ entry.tagname . "\t"
\ . entry.pagename . VimwikiGet('ext') . "\t"
\ . entry.lineno
\ . ';"'
\ . "\t" . "vimwiki:" . entry_data
\)
endfor
call sort(entries)
call insert(entries, "!_TAG_FILE_SORTED\t1\t{anything}")
call writefile(entries, metadata_path)
endfunction " }}}
" vimwiki#base#get_tags
" Returns list of unique tags found in metadata
function! vimwiki#base#get_tags(metadata) "{{{
let tags = {}
for entry in a:metadata
let tags[entry.tagname] = 1
endfor
return keys(tags)
endfunction " }}}
" vimwiki#base#generate_tags
" Similar to vimwiki#base#generate_links. In the current buffer, appends
" tags and references to all their instances. If no arguments (tags) are
" specified, outputs all tags.
function! vimwiki#base#generate_tags(...) abort "{{{
let need_all_tags = (a:0 == 0)
let specific_tags = a:000
let metadata = vimwiki#base#load_tags_metadata()
call append(line('$'), [
\ '',
\ substitute(g:vimwiki_rxH1_Template, '__Header__', 'Generated Tags', '') ])
call sort(metadata)
let bullet = repeat(' ', vimwiki#lst#get_list_margin()).
\ vimwiki#lst#default_symbol().' '
let current_tag = ''
for entry in metadata
if need_all_tags || index(specific_tags, entry.tagname) != -1
if entry.tagname != current_tag
let current_tag = entry.tagname
call append(line('$'), [
\ '',
\ substitute(g:vimwiki_rxH2_Template, '__Header__', entry.tagname, ''),
\ '' ])
endif
call append(line('$'), bullet . '[[' . entry.link . ']]')
endif
endfor
endfunction " }}}
" }}}
" Command completion functions {{{
" vimwiki#base#complete_tags
function! vimwiki#base#complete_tags(ArgLead, CmdLine, CursorPos) abort " {{{
" We can safely ignore args if we use -custom=complete option, Vim engine
" will do the job of filtering.
let metadata = vimwiki#base#load_tags_metadata()
let taglist = vimwiki#base#get_tags(metadata)
return join(taglist, "\n")
endfunction " }}}
" vimwiki#base#complete_links_escaped
function! vimwiki#base#complete_links_escaped(ArgLead, CmdLine, CursorPos) abort " {{{
" We can safely ignore args if we use -custom=complete option, Vim engine

274
autoload/vimwiki/tags.vim Normal file
View File

@ -0,0 +1,274 @@
" vim:tabstop=2:shiftwidth=2:expandtab:foldmethod=marker:textwidth=79
" Vimwiki autoload plugin file
let s:TAGS_METADATA_FILE_NAME = '.tags'
" Tags metadata in-memory format:
" metadata := [ entry, ... ]
" entry := { 'tagname':..., 'pagename':..., 'lineno':..., 'link':... }
" Tags metadata in-file format:
"
" Is based on CTags format (see |tags-file-format|).
"
" {tagaddress} is set to lineno. We'll let vim search by exact line number; we
" can afford that, we assume metadata file is always updated before use.
"
" Pagename and link are not saved in standard ctags fields, so we'll add
" an optional field, "vimwiki:". In this field, we encode tab-separated values
" of missing parameters -- "pagename" and "link".
" vimwiki#tags#update_tags
" Update tags metadata.
" a:full_rebuild == 1: re-scan entire wiki
" a:full_rebuild == 0: only re-scan current page
function! vimwiki#tags#update_tags(full_rebuild) "{{{
if !a:full_rebuild
" Updating for one page (current)
let page_name = expand('%:t:r')
" Collect tags in current file
let tags = s:scan_tags(getline(1, '$'), page_name)
" Load metadata file
let metadata = vimwiki#tags#load_tags_metadata()
" Drop old tags
let metadata = s:remove_page_from_tags(metadata, page_name)
" Merge in the new ones
let metadata = s:merge_tags(metadata, tags)
" Save
call s:write_tags_metadata(metadata)
else " full rebuild
let files = s:find_files(g:vimwiki_current_idx, 0)
let metadata = []
for file in files
let page_name = fnamemodify(file, ':t:r')
let tags = s:scan_tags(readfile(file), page_name)
let metadata = s:merge_tags(metadata, tags)
endfor
call s:write_tags_metadata(metadata)
endif
endfunction " }}}
" s:scan_tags
" Scans the list of text lines (argument) and produces tags metadata.
function! s:scan_tags(lines, page_name) "{{{
let metadata = []
let page_name = a:page_name
" Code wireframe to scan for headers -- borrowed from
" vimwiki#base#get_anchors(), with minor modifications.
let rxheader = g:vimwiki_{VimwikiGet('syntax')}_header_search
let rxtag = g:vimwiki_{VimwikiGet('syntax')}_tag_search
let anchor_level = ['', '', '', '', '', '', '']
let current_complete_anchor = ''
let PROXIMITY_LINES_NR = 5
let header_line_nr = - (2 * PROXIMITY_LINES_NR)
for line_nr in range(1, len(a:lines))
let line = a:lines[line_nr - 1]
" process headers
let h_match = matchlist(line, rxheader)
if !empty(h_match) " got a header
let header_line_nr = line_nr
let header = vimwiki#u#trim(h_match[2])
let level = len(h_match[1])
let anchor_level[level-1] = header
for l in range(level, 6)
let anchor_level[l] = ''
endfor
if level == 1
let current_complete_anchor = header
else
let current_complete_anchor = ''
for l in range(level-1)
if anchor_level[l] != ''
let current_complete_anchor .= anchor_level[l].'#'
endif
endfor
let current_complete_anchor .= header
endif
continue " tags are not allowed in headers
endif
" TODO ignore verbatim blocks
" Scan line for tags. There can be many of them.
let str = line
while 1
let tag_group = matchstr(str, rxtag)
if tag_group == ''
break
endif
let tagend = matchend(str, rxtag)
let str = str[(tagend):]
for tag in split(tag_group, ':')
" Create metadata entry
let entry = {}
let entry.tagname = tag
let entry.pagename = page_name
let entry.lineno = line_nr
if line_nr <= (header_line_nr + PROXIMITY_LINES_NR)
let entry.link = page_name . '#' . current_complete_anchor
elseif header_line_nr < 0
" Tag appeared before the first header
let entry.link = page_name
else
let entry.link = page_name . '#' . tag
endif
call add(metadata, entry)
endfor
endwhile
endfor " loop over lines
return metadata
endfunction " }}}
" s:metadata_file_path
" Returns tags metadata file path
function! s:metadata_file_path() abort "{{{
return fnamemodify(VimwikiGet('path') . '/' . s:TAGS_METADATA_FILE_NAME, ':p')
endfunction " }}}
" vimwiki#tags#load_tags_metadata
" Loads tags metadata from file, returns a dictionary
function! vimwiki#tags#load_tags_metadata() abort "{{{
let metadata_path = s:metadata_file_path()
if !filereadable(metadata_path)
return []
endif
let metadata = []
for line in readfile(metadata_path)
if line =~ '^!_TAG_FILE_'
continue
endif
let parts = matchlist(line, '^\(.\{-}\);"\(.*\)$')
if parts[0] == '' || parts[1] == '' || parts[2] == ''
throw 'VimwikiTags1: Metadata file corrupted'
endif
let std_fields = split(parts[1], '\t')
if len(std_fields) != 3
throw 'VimwikiTags2: Metadata file corrupted'
endif
let vw_part = parts[2]
if vw_part[0] != "\t"
throw 'VimwikiTags3: Metadata file corrupted'
endif
let vw_fields = split(vw_part[1:], "\t")
if len(vw_fields) != 1 || vw_fields[0] !~ '^vimwiki:'
throw 'VimwikiTags4: Metadata file corrupted'
endif
let vw_data = substitute(vw_fields[0], '^vimwiki:', '', '')
let vw_data = substitute(vw_data, '\\n', "\n", 'g')
let vw_data = substitute(vw_data, '\\r', "\r", 'g')
let vw_data = substitute(vw_data, '\\t', "\t", 'g')
let vw_data = substitute(vw_data, '\\\\', "\\", 'g')
let vw_fields = split(vw_data, "\t")
if len(vw_fields) != 2
throw 'VimwikiTags5: Metadata file corrupted'
endif
let entry = {}
let entry.tagname = std_fields[0]
let entry.pagename = vw_fields[0]
let entry.lineno = std_fields[2]
let entry.link = vw_fields[1]
call add(metadata, entry)
endfor
return metadata
endfunction " }}}
" s:remove_page_from_tags
" Removes all entries for given page from metadata in-place. Returns updated
" metadata (just in case).
function! s:remove_page_from_tags(metadata, page_name) "{{{
let metadata = filter(a:metadata,
\ "v:val.pagename != '" . substitute(a:page_name, "'", "''", '') . "'")
return metadata
endfunction " }}}
" s:merge_tags
" Merges two tags metadata objects into (new) one.
function! s:merge_tags(metadata1, metadata2) "{{{
return a:metadata1 + a:metadata2
endfunction " }}}
" s:write_tags_metadata
" Saves metadata object into a file. Throws exceptions in case of problems.
function! s:write_tags_metadata(metadata) "{{{
let metadata_path = s:metadata_file_path()
let entries = []
for entry in a:metadata
let entry_data = entry.pagename . "\t" . entry.link
let entry_data = substitute(entry_data, "\\", '\\\\', 'g')
let entry_data = substitute(entry_data, "\t", '\\t', 'g')
let entry_data = substitute(entry_data, "\r", '\\r', 'g')
let entry_data = substitute(entry_data, "\n", '\\n', 'g')
call add(entries,
\ entry.tagname . "\t"
\ . entry.pagename . VimwikiGet('ext') . "\t"
\ . entry.lineno
\ . ';"'
\ . "\t" . "vimwiki:" . entry_data
\)
endfor
call sort(entries)
call insert(entries, "!_TAG_FILE_SORTED\t1\t{anything}")
call writefile(entries, metadata_path)
endfunction " }}}
" vimwiki#tags#get_tags
" Returns list of unique tags found in metadata
function! vimwiki#tags#get_tags(metadata) "{{{
let tags = {}
for entry in a:metadata
let tags[entry.tagname] = 1
endfor
return keys(tags)
endfunction " }}}
" vimwiki#tags#generate_tags
" Similar to vimwiki#base#generate_links. In the current buffer, appends
" tags and references to all their instances. If no arguments (tags) are
" specified, outputs all tags.
function! vimwiki#tags#generate_tags(...) abort "{{{
let need_all_tags = (a:0 == 0)
let specific_tags = a:000
let metadata = vimwiki#tags#load_tags_metadata()
call append(line('$'), [
\ '',
\ substitute(g:vimwiki_rxH1_Template, '__Header__', 'Generated Tags', '') ])
call sort(metadata)
let bullet = repeat(' ', vimwiki#lst#get_list_margin()).
\ vimwiki#lst#default_symbol().' '
let current_tag = ''
for entry in metadata
if need_all_tags || index(specific_tags, entry.tagname) != -1
if entry.tagname != current_tag
let current_tag = entry.tagname
call append(line('$'), [
\ '',
\ substitute(g:vimwiki_rxH2_Template, '__Header__', entry.tagname, ''),
\ '' ])
endif
call append(line('$'), bullet . '[[' . entry.link . ']]')
endif
endfor
endfunction " }}}
" vimwiki#tags#complete_tags
function! vimwiki#tags#complete_tags(ArgLead, CmdLine, CursorPos) abort " {{{
" We can safely ignore args if we use -custom=complete option, Vim engine
" will do the job of filtering.
let metadata = vimwiki#tags#load_tags_metadata()
let taglist = vimwiki#tags#get_tags(metadata)
return join(taglist, "\n")
endfunction " }}}

View File

@ -68,8 +68,8 @@ function! Complete_wikifiles(findstart, base)
return []
elseif s:line_context == ':'
" Tags completion
let metadata = vimwiki#base#load_tags_metadata()
let tags = vimwiki#base#get_tags(metadata)
let metadata = vimwiki#tags#load_tags_metadata()
let tags = vimwiki#tags#get_tags(metadata)
if a:base != ''
call filter(tags,
\ "v:val[:" . (len(a:base)-1) . "] == '" . substitute(a:base, "'", "''", '') . "'" )
@ -311,11 +311,11 @@ command! -buffer VimwikiDiaryNextDay call vimwiki#diary#goto_next_day()
command! -buffer VimwikiDiaryPrevDay call vimwiki#diary#goto_prev_day()
" tags commands
command! -buffer VimwikiRebuildTags call vimwiki#base#update_tags(1)
command! -buffer -nargs=* -complete=custom,vimwiki#base#complete_tags
command! -buffer VimwikiRebuildTags call vimwiki#tags#update_tags(1)
command! -buffer -nargs=* -complete=custom,vimwiki#tags#complete_tags
\ VimwikiSearchTags VimwikiSearch /:<args>:/
command! -buffer -nargs=* -complete=custom,vimwiki#base#complete_tags
\ VimwikiGenerateTags call vimwiki#base#generate_tags(<f-args>)
command! -buffer -nargs=* -complete=custom,vimwiki#tags#complete_tags
\ VimwikiGenerateTags call vimwiki#tags#generate_tags(<f-args>)
" COMMANDS }}}
@ -653,7 +653,7 @@ endif
if VimwikiGet('auto_tags')
" Automatically update tags metadata on page write.
augroup vimwiki
au BufWritePost <buffer> call vimwiki#base#update_tags(0)
au BufWritePost <buffer> call vimwiki#tags#update_tags(0)
augroup END
endif
" AUTOCOMMANDS }}}