From c6bb6d91b92301a11ca0b87dc7eee0b2e7c2e176 Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Sun, 11 Oct 2020 15:30:41 +0200 Subject: [PATCH] Initial commit --- L9/autoload/l9.vim | 570 +++ L9/autoload/l9/async.py | 92 + L9/autoload/l9/async.vim | 67 + L9/autoload/l9/quickfix.vim | 107 + L9/autoload/l9/tempbuffer.vim | 112 + L9/autoload/l9/tempvariables.vim | 60 + L9/doc/l9.jax | 55 + L9/doc/l9.txt | 73 + L9/doc/tags | 9 + L9/doc/tags-ja | 9 + L9/plugin/l9.vim | 108 + ScrollColor/plugin/ScrollColor.vim | 457 +++ bufonly/plugin/BufOnly.vim | 69 + .../autoload/colorschemedegradelib.vim | 764 ++++ .../plugin/ColorschemeDegrade.vim | 416 +++ exheres-syntax-20160115/.mailmap | 1 + exheres-syntax-20160115/README | 40 + .../doc/exheres-syntax.txt | 13 + exheres-syntax-20160115/doc/tags | 1 + exheres-syntax-20160115/ftdetect/exheres.vim | 14 + .../ftplugin/exheres-0.vim | 19 + exheres-syntax-20160115/ftplugin/exlib.vim | 19 + exheres-syntax-20160115/indent/exheres-0.vim | 18 + exheres-syntax-20160115/indent/exlib.vim | 18 + .../plugin/new-common-metadata.vim | 89 + .../plugin/new-exheres-0.vim | 80 + exheres-syntax-20160115/syntax/exheres-0.vim | 18 + .../syntax/exheres-common.vim | 142 + exheres-syntax-20160115/syntax/exlib.vim | 18 + fontzoom/doc/fontzoom.txt | 105 + fontzoom/doc/tags | 15 + fontzoom/plugin/fontzoom.vim | 75 + fuzzyfinder/autoload/fuf.vim | 1052 ++++++ fuzzyfinder/autoload/fuf/bookmarkdir.vim | 163 + fuzzyfinder/autoload/fuf/bookmarkfile.vim | 199 ++ fuzzyfinder/autoload/fuf/buffer.vim | 189 + fuzzyfinder/autoload/fuf/buffertag.vim | 300 ++ fuzzyfinder/autoload/fuf/callbackfile.vim | 137 + fuzzyfinder/autoload/fuf/callbackitem.vim | 139 + fuzzyfinder/autoload/fuf/changelist.vim | 172 + fuzzyfinder/autoload/fuf/coveragefile.vim | 199 ++ fuzzyfinder/autoload/fuf/dir.vim | 132 + fuzzyfinder/autoload/fuf/file.vim | 139 + fuzzyfinder/autoload/fuf/givencmd.vim | 123 + fuzzyfinder/autoload/fuf/givendir.vim | 123 + fuzzyfinder/autoload/fuf/givenfile.vim | 121 + fuzzyfinder/autoload/fuf/help.vim | 198 ++ fuzzyfinder/autoload/fuf/jumplist.vim | 182 + fuzzyfinder/autoload/fuf/line.vim | 135 + fuzzyfinder/autoload/fuf/mrucmd.vim | 134 + fuzzyfinder/autoload/fuf/mrufile.vim | 234 ++ fuzzyfinder/autoload/fuf/quickfix.vim | 154 + fuzzyfinder/autoload/fuf/tag.vim | 178 + fuzzyfinder/autoload/fuf/taggedfile.vim | 159 + fuzzyfinder/doc/fuf.jax | 1405 ++++++++ fuzzyfinder/doc/fuf.txt | 1883 ++++++++++ fuzzyfinder/doc/tags | 176 + fuzzyfinder/doc/tags-ja | 174 + ...e8d68b6774324b58c02692b896a6a36a0397.patch | 36 + fuzzyfinder/plugin/fuf.vim | 158 + log/syntax/log.vim | 73 + paredit/doc/paredit.txt | 461 +++ paredit/plugin/paredit.vim | 1863 ++++++++++ tslime/tslime.vim | 58 + txtfmt/doc/tags | 248 ++ txtfmt/doc/txtfmt.txt | 2908 +++++++++++++++ txtfmt/ftplugin/txtfmt.vim | 2672 ++++++++++++++ txtfmt/plugin/txtfmt.vim | 3104 +++++++++++++++++ txtfmt/syntax/txtfmt.vim | 2262 ++++++++++++ 69 files changed, 25696 insertions(+) create mode 100644 L9/autoload/l9.vim create mode 100644 L9/autoload/l9/async.py create mode 100644 L9/autoload/l9/async.vim create mode 100644 L9/autoload/l9/quickfix.vim create mode 100644 L9/autoload/l9/tempbuffer.vim create mode 100644 L9/autoload/l9/tempvariables.vim create mode 100644 L9/doc/l9.jax create mode 100644 L9/doc/l9.txt create mode 100644 L9/doc/tags create mode 100644 L9/doc/tags-ja create mode 100644 L9/plugin/l9.vim create mode 100755 ScrollColor/plugin/ScrollColor.vim create mode 100644 bufonly/plugin/BufOnly.vim create mode 100755 colorschemedegrade/autoload/colorschemedegradelib.vim create mode 100755 colorschemedegrade/plugin/ColorschemeDegrade.vim create mode 100644 exheres-syntax-20160115/.mailmap create mode 100644 exheres-syntax-20160115/README create mode 100644 exheres-syntax-20160115/doc/exheres-syntax.txt create mode 100644 exheres-syntax-20160115/doc/tags create mode 100644 exheres-syntax-20160115/ftdetect/exheres.vim create mode 100644 exheres-syntax-20160115/ftplugin/exheres-0.vim create mode 100644 exheres-syntax-20160115/ftplugin/exlib.vim create mode 100644 exheres-syntax-20160115/indent/exheres-0.vim create mode 100644 exheres-syntax-20160115/indent/exlib.vim create mode 100644 exheres-syntax-20160115/plugin/new-common-metadata.vim create mode 100644 exheres-syntax-20160115/plugin/new-exheres-0.vim create mode 100644 exheres-syntax-20160115/syntax/exheres-0.vim create mode 100644 exheres-syntax-20160115/syntax/exheres-common.vim create mode 100644 exheres-syntax-20160115/syntax/exlib.vim create mode 100755 fontzoom/doc/fontzoom.txt create mode 100644 fontzoom/doc/tags create mode 100755 fontzoom/plugin/fontzoom.vim create mode 100644 fuzzyfinder/autoload/fuf.vim create mode 100644 fuzzyfinder/autoload/fuf/bookmarkdir.vim create mode 100644 fuzzyfinder/autoload/fuf/bookmarkfile.vim create mode 100644 fuzzyfinder/autoload/fuf/buffer.vim create mode 100644 fuzzyfinder/autoload/fuf/buffertag.vim create mode 100644 fuzzyfinder/autoload/fuf/callbackfile.vim create mode 100644 fuzzyfinder/autoload/fuf/callbackitem.vim create mode 100644 fuzzyfinder/autoload/fuf/changelist.vim create mode 100644 fuzzyfinder/autoload/fuf/coveragefile.vim create mode 100644 fuzzyfinder/autoload/fuf/dir.vim create mode 100644 fuzzyfinder/autoload/fuf/file.vim create mode 100644 fuzzyfinder/autoload/fuf/givencmd.vim create mode 100644 fuzzyfinder/autoload/fuf/givendir.vim create mode 100644 fuzzyfinder/autoload/fuf/givenfile.vim create mode 100644 fuzzyfinder/autoload/fuf/help.vim create mode 100644 fuzzyfinder/autoload/fuf/jumplist.vim create mode 100644 fuzzyfinder/autoload/fuf/line.vim create mode 100644 fuzzyfinder/autoload/fuf/mrucmd.vim create mode 100644 fuzzyfinder/autoload/fuf/mrufile.vim create mode 100644 fuzzyfinder/autoload/fuf/quickfix.vim create mode 100644 fuzzyfinder/autoload/fuf/tag.vim create mode 100644 fuzzyfinder/autoload/fuf/taggedfile.vim create mode 100644 fuzzyfinder/doc/fuf.jax create mode 100644 fuzzyfinder/doc/fuf.txt create mode 100644 fuzzyfinder/doc/tags create mode 100644 fuzzyfinder/doc/tags-ja create mode 100644 fuzzyfinder/fd41e8d68b6774324b58c02692b896a6a36a0397.patch create mode 100644 fuzzyfinder/plugin/fuf.vim create mode 100755 log/syntax/log.vim create mode 100644 paredit/doc/paredit.txt create mode 100644 paredit/plugin/paredit.vim create mode 100644 tslime/tslime.vim create mode 100644 txtfmt/doc/tags create mode 100755 txtfmt/doc/txtfmt.txt create mode 100755 txtfmt/ftplugin/txtfmt.vim create mode 100755 txtfmt/plugin/txtfmt.vim create mode 100755 txtfmt/syntax/txtfmt.vim diff --git a/L9/autoload/l9.vim b/L9/autoload/l9.vim new file mode 100644 index 0000000..b6a0ae7 --- /dev/null +++ b/L9/autoload/l9.vim @@ -0,0 +1,570 @@ +"============================================================================= +" Copyright (c) 2009-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if exists('g:loaded_autoload_l9') + finish +endif +let g:loaded_autoload_l9 = 1 + +" }}}1 +"============================================================================= +" COMPATIBILITY TEST {{{1 + +" +let s:L9_VERSION_CURRENT = 101 +let s:L9_VERSION_PASSABLE = 101 + +" returns true if given version is compatible. +function l9#isCompatible(ver) + return +endfunction + +let s:VERSION_FACTOR = str2float('0.01') + +" returns false if the caller script should finish. +" a:vimVersion: if 0, don't check vim version +" a:l9Version: same rule as v:version +function l9#guardScriptLoading(path, vimVersion, l9Version, exprs) + let loadedVarName = 'g:loaded_' . substitute(a:path, '\W', '_', 'g') + if exists(loadedVarName) + return 0 + elseif a:vimVersion > 0 && a:vimVersion > v:version + echoerr a:path . ' requires Vim version ' . string(a:vimVersion * s:VERSION_FACTOR) + return 0 + elseif a:l9Version > 0 && (a:l9Version > s:L9_VERSION_CURRENT || + \ a:l9Version < s:L9_VERSION_PASSABLE) + echoerr a:path . ' requires L9 library version ' . string(a:l9Version * s:VERSION_FACTOR) + return 0 + endif + for expr in a:exprs + if !eval(expr) + echoerr a:path . ' requires: ' . expr + return 0 + endif + endfor + let {loadedVarName} = 1 + return 1 +endfunction + +" +function l9#getVersion() + return s:L9_VERSION_CURRENT +endfunction + +" }}}1 +"============================================================================= +" LIST {{{1 + +" Removes duplicates (unstable) +" This function doesn't change the list of argument. +function l9#unique(items) + let sorted = sort(a:items) + if len(sorted) < 2 + return sorted + endif + let last = remove(sorted, 0) + let result = [last] + for item in sorted + if item != last + call add(result, item) + let last = item + endif + endfor + return result +endfunction + +" Removes duplicates (stable) +" This function doesn't change the list of argument. +function l9#uniqueStably(items) + let result = [] + for item in a:items + if count(result, item, &ignorecase) == 0 + call add(result, item) + endif + endfor + return result +endfunction + +" [ [0], [1,2], [3] ] -> [ 0, 1, 2, 3 ] +" This function doesn't change the list of argument. +function l9#concat(items) + let result = [] + for l in a:items + let result += l + endfor + return result +endfunction + +" [ [0,1,2], [3,4], [5,6,7,8] ] -> [ [0,3,5],[1,4,6] ] +" This function doesn't change the list of argument. +function l9#zip(items) + let result = [] + for i in range(min(map(copy(a:items), 'len(v:val)'))) + call add(result, map(copy(a:items), 'v:val[i]')) + endfor + return result +endfunction + +" filter() with the maximum number of items +" This function doesn't change the list of argument. +function l9#filterWithLimit(items, expr, limit) + if a:limit <= 0 + return filter(copy(a:items), a:expr) + endif + let result = [] + let stride = a:limit * 3 / 2 " x1.5 + for i in range(0, len(a:items) - 1, stride) + let result += filter(a:items[i : i + stride - 1], a:expr) + if len(result) >= a:limit + return remove(result, 0, a:limit - 1) + endif + endfor + return result +endfunction + +" Removes if a:expr is evaluated as non-zero and returns removed items. +" This function change the list of argument. +function l9#removeIf(items, expr) + let removed = filter(copy(a:items), a:expr) + call filter(a:items, '!( ' . a:expr . ')') + return removed +endfunction + +" }}}1 +"============================================================================= +" NUMERIC {{{1 + +" }}}1 +"============================================================================= +" STRING {{{1 + +" Snips a:str and add a:mask if the length of a:str is more than a:len +function l9#snipHead(str, len, mask) + if a:len >= len(a:str) + return a:str + elseif a:len <= len(a:mask) + return a:mask + endif + return a:mask . a:str[-a:len + len(a:mask):] +endfunction + +" Snips a:str and add a:mask if the length of a:str is more than a:len +function l9#snipTail(str, len, mask) + if a:len >= len(a:str) + return a:str + elseif a:len <= len(a:mask) + return a:mask + endif + return a:str[:a:len - 1 - len(a:mask)] . a:mask +endfunction + +" Snips a:str and add a:mask if the length of a:str is more than a:len +function l9#snipMid(str, len, mask) + if a:len >= len(a:str) + return a:str + elseif a:len <= len(a:mask) + return a:mask + endif + let len_head = (a:len - len(a:mask)) / 2 + let len_tail = a:len - len(a:mask) - len_head + return (len_head > 0 ? a:str[: len_head - 1] : '') . a:mask . + \ (len_tail > 0 ? a:str[-len_tail :] : '') +endfunction + +" +function l9#hash224(str) + let a = 0x00000800 " shift 11 bit (if unsigned) + let b = 0x001fffff " extract 11 bit (if unsigned) + let nHash = 7 + let hashes = repeat([0], nHash) + for i in range(len(a:str)) + let iHash = i % nHash + let hashes[iHash] = hashes[iHash] * a + hashes[iHash] / b + let hashes[iHash] += char2nr(a:str[i]) + endfor + return join(map(hashes, 'printf("%08x", v:val)'), '') +endfunction + +" wildcard -> regexp +function l9#convertWildcardToRegexp(expr) + let re = escape(a:expr, '\') + for [pat, sub] in [ [ '*', '\\.\\*' ], [ '?', '\\.' ], [ '[', '\\[' ], ] + let re = substitute(re, pat, sub, 'g') + endfor + return '\V' . re +endfunction + +" }}}1 +"============================================================================= +" LINES {{{1 + +" Removes from the line matching with a:begin first to the line matching with +" a:end next and returns removed lines. +" If matching range is not found, returns [] +function l9#removeLinesBetween(lines, begin, end) + for i in range(len(a:lines) - 1) + if a:lines[i] =~ a:begin + break + endif + endfor + for j in range(i + 1, len(a:lines) - 1) + if a:lines[j] =~ a:end + let g:l0 += [a:lines[i : j]] + return remove(a:lines, i, j) + endif + endfor + return [] +endfunction + +" }}}1 +"============================================================================= +" PATH {{{1 + +" returns the path separator charactor. +function l9#getPathSeparator() + return (!&shellslash && (has('win32') || has('win64')) ? '\' : '/') +endfunction + +" [ 'a', 'b/', '/c' ] -> 'a/b/c' +function l9#concatPaths(paths) + let result = '' + for p in a:paths + if empty(p) + continue + elseif empty(result) + let result = p + else + let result = substitute(result, '[/\\]$', '', '') . l9#getPathSeparator() + \ . substitute(p, '^[/\\]', '', '') + endif + endfor + return result +endfunction + +" path: '/a/b/c/d', dir: '/a/b' => 'c/d' +function l9#modifyPathRelativeToDir(path, dir) + let pathFull = fnamemodify(a:path, ':p') + let dirFull = fnamemodify(a:dir, ':p') + if len(pathFull) < len(dirFull) || pathFull[:len(dirFull) - 1] !=# dirFull + return pathFull + endif + return pathFull[len(dirFull):] +endfunction + +" }}}1 +"============================================================================= +" FILE {{{1 + +" Almost same as readfile(). +function l9#readFile(...) + let args = copy(a:000) + let args[0] = expand(args[0]) + try + return call('readfile', args) + catch + endtry + return [] +endfunction + +" Almost same as writefile(). +function l9#writeFile(...) + let args = copy(a:000) + let args[1] = expand(args[1]) + let dir = fnamemodify(args[1], ':h') + try + if !isdirectory(dir) + call mkdir(dir, 'p') + endif + return call('writefile', args) + catch + endtry + return -1 " -1 is error code. +endfunction + +" }}}1 +"============================================================================= +" BUFFER {{{1 + +" :wall/:wall! wrapper. Useful for writing readonly buffers. +function l9#writeAll() + try + silent update " NOTE: avoiding a problem with a buftype=acwrite buffer. + silent wall + catch /^Vim/ " E45, E505 + if l9#inputHl('Question', v:exception . "\nWrite readonly files? (Y/N) : ", 'Y') ==? 'y' + redraw + :wall! + endif + endtry +endfunction + +" Loads given files with :edit command +function l9#loadFilesToBuffers(files) + for file in filter(copy(a:files), '!bufloaded(v:val)') + execute 'edit ' . fnameescape(file) + if !exists('bufNrFirst') + let bufNrFirst = bufnr('%') + endif + endfor + if exists('bufNrFirst') + execute bufNrFirst . 'buffer' + endif +endfunction + +" Deletes all buffers except given files with :bdelete command +function l9#deleteAllBuffersExcept(files) + let bufNrExcepts = map(copy(a:files), 'bufnr("^" . v:val . "$")') + for bufNr in filter(range(1, bufnr('$')), 'bufloaded(v:val)') + if count(bufNrExcepts, bufNr) == 0 + execute bufNr . 'bdelete' + endif + endfor +endfunction + +" }}}1 +"============================================================================= +" WINDOW {{{1 + +" move current window to next tabpage. +function l9#shiftWinNextTabpage() + if tabpagenr('$') < 2 + return + endif + let bufnr = bufnr('%') + tabnext + execute bufnr . 'sbuffer' + tabprevious + if winnr('$') > 1 + close + tabnext + else + close " if tabpage is closed, next tabpage will become current + endif +endfunction + +" move current window to previous tabpage. +function l9#shiftWinPrevTabpage() + if tabpagenr('$') < 2 + return + endif + let bufnr = bufnr('%') + tabprevious + execute bufnr . 'sbuffer' + tabnext + close + tabprevious +endfunction + +" move to a window containing specified buffer. +" returns 0 if the buffer is not found. +function l9#moveToBufferWindowInCurrentTabpage(bufNr) + if bufnr('%') == a:bufNr + return 1 + elseif count(tabpagebuflist(), a:bufNr) == 0 + return 0 + endif + execute bufwinnr(a:bufNr) . 'wincmd w' + return 1 +endfunction + +" returns 0 if the buffer is not found. +function s:moveToOtherTabpageOpeningBuffer(bufNr) + for tabNr in range(1, tabpagenr('$')) + if tabNr != tabpagenr() && count(tabpagebuflist(tabNr), a:bufNr) > 0 + execute 'tabnext ' . tabNr + return 1 + endif + endfor + return 0 +endfunction + +" move to a window containing specified buffer. +" returns 0 if the buffer is not found. +function l9#moveToBufferWindowInOtherTabpage(bufNr) + if !s:moveToOtherTabpageOpeningBuffer(a:bufNr) + return 0 + endif + return l9#moveToBufferWindowInCurrentTabpage(a:bufNr) +endfunction + +" }}}1 +"============================================================================= +" COMMAND LINE {{{1 + +" echo/echomsg with highlighting. +function l9#echoHl(hl, msg, prefix, addingHistory) + let echoCmd = (a:addingHistory ? 'echomsg' : 'echo') + execute "echohl " . a:hl + try + for l in (type(a:msg) == type([]) ? a:msg : split(a:msg, "\n")) + execute echoCmd . ' a:prefix . l' + endfor + finally + echohl None + endtry +endfunction + +" input() with highlighting. +" This function can take list as {completion} argument. +function l9#inputHl(hl, ...) + execute "echohl " . a:hl + try + let args = copy(a:000) + if len(args) > 2 && type(args[2]) == type([]) + let s:candidatesForInputHl = args[2] + let args[2] = 'custom,l9#completeForInputHl' + endif + let s = call('input', args) + unlet! s:candidatesForInputHl + finally + echohl None + endtry + redraw " needed to show following echo to next line. + return s +endfunction + +" only called by l9#inputHl() for completion. +function l9#completeForInputHl(lead, line, pos) + return join(s:candidatesForInputHl, "\n") +endfunction + +" }}}1 +"============================================================================= +" VISUAL MODE {{{1 + +" returns last selected text in Visual mode. +function l9#getSelectedText() + let reg_ = [@", getregtype('"')] + let regA = [@a, getregtype('a')] + if mode() =~# "[vV\]" + silent normal! "aygv + else + let pos = getpos('.') + silent normal! gv"ay + call setpos('.', pos) + endif + let text = @a + call setreg('"', reg_[0], reg_[1]) + call setreg('a', regA[0], regA[1]) + return text +endfunction + + +" }}}1 +"============================================================================= +" EVAL {{{1 + +" loads given text as Vim script with :source command +function l9#loadScript(text) + let lines = (type(a:text) == type([]) ? a:text : split(a:text, "\n")) + let fname = tempname() + call writefile(lines, fname) + source `=fname` + call delete(fname) +endfunction + + +" }}}1 +"============================================================================= +" VARIABLES {{{1 + +" +function l9#defineVariableDefault(name, default) + if !exists(a:name) + let {a:name} = a:default + endif +endfunction + +" }}}1 +"============================================================================= +" GREP {{{1 + +" Execute :vimgrep and opens the quickfix window if matches are found. +" +" a:pattern: search pattern. If ommitted, last search pattern (@/) is used. +" a:files: List of files +function l9#grepFiles(pattern, files) + let target = join(map(a:files, 'escape(v:val, " ")'), ' ') + let pattern = (a:pattern[0] ==# '/' ? a:pattern[1:] : a:pattern) + let pattern = (empty(pattern) ? @/ : pattern) + try + execute printf('vimgrep/%s/j %s', pattern, target) + catch /^Vim/ + call setqflist([]) + endtry + call l9#quickfix#sort() + call l9#quickfix#openIfNotEmpty(1, 0) +endfunction + +" Execute :vimgrep for buffers using l9#grepFiles() +" See also: :L9GrepBuffer :L9GrepBufferAll +function l9#grepBuffers(pattern, bufNrs) + let files = map(filter(a:bufNrs, 'bufloaded(v:val)'), 'bufname(v:val)') + call l9#grepFiles(a:pattern, files) +endfunction + +" }}}1 +"============================================================================= +" SIGN {{{1 + +" Highlights lines using :sign define and :sign place. +" +" a:linehl, a:text, a:texthl: See |signs|. Ignored if empty string. +" a:locations: List of [{buffer number}, {line number}] for highlighting +function l9#placeSign(linehl, text, texthl, locations) + let argLinehl = (empty(a:linehl) ? '' : 'linehl=' . a:linehl) + let argText = (empty(a:text) ? '' : 'text=' . a:text) + let argTexthl = (empty(a:texthl) ? '' : 'texthl=' . a:texthl) + let name = 'l9--' . a:linehl . '--' . a:text . '--' . a:texthl + execute printf('sign define %s linehl=%s text=%s texthl=%s', + \ name, a:linehl, a:text, a:texthl) + for [bufNr, lnum] in a:locations + execute printf('sign place 1 line=%d name=%s buffer=%d', lnum, name, bufNr) + endfor +endfunction + +" }}}1 +"============================================================================= +" NOTIFY EXTERNALLY {{{1 + +" Notify a message using an external program. +" Currently supports Balloonly, Screen, and Tmux. +function l9#notifyExternally(msg) + return l9#notifyBalloonly(a:msg) + \ || l9#notifyScreen(a:msg) + \ || l9#notifyTmux(a:msg) +endfunction + +" +function l9#notifyBalloonly(msg) + if !(has('win32') || has('win64')) || !executable(g:l9_balloonly) + return 0 + endif + execute 'silent !start ' . shellescape(g:l9_balloonly) . ' 4000 "l9" ' . shellescape(a:msg) + return 1 +endfunction + +" +function l9#notifyScreen(msg) + if !has('unix') || has('gui_running') || $WINDOW !~ '\d' || !executable('screen') + return 0 + endif + call system('screen -X wall ' . shellescape('l9: ' . a:msg)) + return 1 +endfunction + +" +function l9#notifyTmux(msg) + if !has('unix') || has('gui_running') || empty($TMUX) || !executable('tmux') + return 0 + endif + call system('tmux display-message ' . shellescape('l9: ' . a:msg)) + return 1 +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/L9/autoload/l9/async.py b/L9/autoload/l9/async.py new file mode 100644 index 0000000..eeb0cc3 --- /dev/null +++ b/L9/autoload/l9/async.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python + +from __future__ import with_statement +import vim +import os +import subprocess +import threading +import Queue + + +class Asyncer: + + def __init__(self): + self._workers = {} + + def execute(self, var_key, var_command, var_cwd, var_input, var_appends): + key = vim.eval(var_key) + command = vim.eval(var_command) + cwd = vim.eval(var_cwd) + input = vim.eval(var_input) + appends = vim.eval(var_appends) + if key not in self._workers: + self._workers[key] = Worker() + self._workers[key].start() + self._workers[key].put(Executor(command, cwd, input, appends)) + + def print_output(self, var_key): + key = vim.eval(var_key) + if key not in self._workers: + return + for l in self._workers[key].copy_outputs(): + print l, + + def print_worker_keys(self): + for k in self._workers.keys(): + print k + + def print_active_worker_keys(self): + for k in self._workers.keys(): + print k + + +class Worker(threading.Thread): + + def __init__(self): + threading.Thread.__init__(self) + self._queue = Queue.Queue() + self._lines = [] + self._lock = threading.Lock() + + def run(self): + while True: + self._queue.get().execute(self) + self._queue.task_done() + + def put(self, executor): + self._queue.put(executor) + + def clear_outputs(self): + with self._lock: + self._lines = [] + + def record_output(self, line): + with self._lock: + self._lines.append(line) + + def copy_outputs(self): + with self._lock: + return self._lines[:] + + +class Executor: + + def __init__(self, command, cwd, input, appends): + self._command = command + self._cwd = cwd + self._input = input + self._appends = appends + + def execute(self, worker): + if not self._appends: + worker.clear_outputs() + os.chdir(self._cwd) + p = subprocess.Popen(self._command, shell=True, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + p.stdin.write(self._input) + line = p.stdout.readline() + while line: + worker.record_output(line) + line = p.stdout.readline() + + diff --git a/L9/autoload/l9/async.vim b/L9/autoload/l9/async.vim new file mode 100644 index 0000000..fa66e9f --- /dev/null +++ b/L9/autoload/l9/async.vim @@ -0,0 +1,67 @@ +"============================================================================= +" Copyright (C) 2009-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, ['has("python")']) + finish +endif + +" }}}1 +"============================================================================= +" ASYNC EXECUTE {{{1 + +" +function s:checkKey(key) + if a:key =~ '\n' || a:key !~ '\S' + throw "Asyncer: Invalid key: " . a:key + endif +endfunction + +" +function l9#async#execute(key, cmd, cwd, input, appends) + call s:checkKey(a:key) + python asyncer.execute('a:key', 'a:cmd', 'a:cwd', 'a:input', 'a:appends') +endfunction + +" +function l9#async#read(key) + call s:checkKey(a:key) + redir => result + silent python asyncer.print_output('a:key') + redir END + " NOTE: "\n" is somehow inserted by redir. + return (result[0] ==# "\n" ? result[1:] : result) +endfunction + +" +function l9#async#listWorkers() + redir => result + silent python asyncer.print_worker_keys() + redir END + return split(result, "\n") +endfunction + +" +function l9#async#listActiveWorkers() + redir => result + silent python asyncer.print_active_worker_keys() + redir END + return split(result, "\n") +endfunction + +" }}}1 +"============================================================================= +" INITIALIZATION {{{1 + +let s:ASYNC_PY_PATH = fnamemodify(expand(':p:h'), ':p') . 'async.py' + +pyfile `=s:ASYNC_PY_PATH` +python asyncer = Asyncer() + +" }}}1 +"============================================================================= +" vim: set fdm=marker: + + diff --git a/L9/autoload/l9/quickfix.vim b/L9/autoload/l9/quickfix.vim new file mode 100644 index 0000000..1758b39 --- /dev/null +++ b/L9/autoload/l9/quickfix.vim @@ -0,0 +1,107 @@ +"============================================================================= +" Copyright (C) 2009-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" QUICKFIX {{{1 + +" Returns non-zero if quickfix window is opened. +function l9#quickfix#isWindowOpened() + return count(map(range(1, winnr('$')), 'getwinvar(v:val, "&buftype")'), 'quickfix') > 0 +endfunction + +" Opens quickfix window if quickfix is not empty, and echo the number of errors. +" +" a:onlyRecognized: if non-zero, opens only if quickfix has recognized errors. +" a:holdCursor: if non-zero, the cursor won't move to quickfix window. +function l9#quickfix#openIfNotEmpty(onlyRecognized, holdCursor) + let numErrors = len(filter(getqflist(), 'v:val.valid')) + let numOthers = len(getqflist()) - numErrors + if numErrors > 0 || (!a:onlyRecognized && numOthers > 0) + copen + if a:holdCursor + wincmd p + endif + else + cclose + endif + redraw + if numOthers > 0 + echo printf('Quickfix: %d(+%d)', numErrors, numOthers) + else + echo printf('Quickfix: %d', numErrors) + endif +endfunction + +" Toggles Quickfix window +function l9#quickfix#toggleWindow() + if l9#quickfix#isWindowOpened() + cclose + else + call l9#quickfix#openIfNotEmpty(0, 0) + endif +endfunction + +" Creates quickfix list form given lines and opens the quickfix window if +" errors exists. +" +" a:lines: +" a:jump: if non-zero, jump to the first error. +function l9#quickfix#setMakeResult(lines) + cexpr a:lines + call l9#quickfix#openIfNotEmpty(0, 1) +endfunction + +" Compares quickfix entries for sorting. +function l9#quickfix#compareEntries(e0, e1) + if a:e0.bufnr != a:e1.bufnr + let i0 = bufname(a:e0.bufnr) + let i1 = bufname(a:e1.bufnr) + elseif a:e0.lnum != a:e1.lnum + let i0 = a:e0.lnum + let i1 = a:e1.lnum + elseif a:e0.col != a:e1.col + let i0 = a:e0.col + let i1 = a:e1.col + else + return 0 + endif + return (i0 > i1 ? +1 : -1) +endfunction + +" Sorts quickfix +function l9#quickfix#sort() + call setqflist(sort(getqflist(), 'l9#quickfix#compareEntries'), 'r') +endfunction + +" Highlights Quickfix lines by :sign. +" Inspired by errormarker plugin. +" +" You can customize the highlighting via L9ErrorLine and L9WarningLine +" highlight groups. +function l9#quickfix#placeSign() + let warnings = [] + let errors = [] + for e in filter(getqflist(), 'v:val.valid') + let warning = (e.type ==? 'w' || e.text =~? '^\s*warning:') + call add((warning ? warnings : errors), [e.bufnr, e.lnum]) + endfor + sign unplace * + call l9#placeSign('L9WarningLine', '>>', '', warnings) + call l9#placeSign('L9ErrorLine', '>>', '', errors) +endfunction + +highlight default L9ErrorLine ctermfg=white ctermbg=52 guibg=#5F0000 +highlight default L9WarningLine ctermfg=white ctermbg=17 guibg=#00005F + +" }}}1 +"============================================================================= +" vim: set fdm=marker: + diff --git a/L9/autoload/l9/tempbuffer.vim b/L9/autoload/l9/tempbuffer.vim new file mode 100644 index 0000000..6f11a78 --- /dev/null +++ b/L9/autoload/l9/tempbuffer.vim @@ -0,0 +1,112 @@ +"============================================================================= +" Copyright (C) 2009-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" TEMPORARY BUFFER {{{1 + +" each key is a buffer name. +let s:dataMap = {} + +" +function s:onBufDelete(bufname) + if exists('s:dataMap[a:bufname].listener.onClose') + call s:dataMap[a:bufname].listener.onClose(s:dataMap[a:bufname].written) + endif + if bufnr('%') == s:dataMap[a:bufname].bufNr && winnr('#') != 0 + " if winnr('#') returns 0, "wincmd p" causes ringing the bell. + wincmd p + endif +endfunction + +" +function s:onBufWriteCmd(bufname) + if !exists('s:dataMap[a:bufname].listener.onWrite') || + \ s:dataMap[a:bufname].listener.onWrite(getline(1, '$')) + setlocal nomodified + let s:dataMap[a:bufname].written = 1 + call l9#tempbuffer#close(a:bufname) + else + endif +endfunction + +" a:bufname: +" a:height: Window height. If 0, default height is used. +" If less than 0, the window becomes full-screen. +" a:listener: +" a:listener.onClose(written) +function l9#tempbuffer#openScratch(bufname, filetype, lines, topleft, vertical, height, listener) + let openCmdPrefix = (a:topleft ? 'topleft ' : '') + \ . (a:vertical ? 'vertical ' : '') + \ . (a:height > 0 ? a:height : '') + if !exists('s:dataMap[a:bufname]') || !bufexists(s:dataMap[a:bufname].bufNr) + execute openCmdPrefix . 'new' + else + call l9#tempbuffer#close(a:bufname) + execute openCmdPrefix . 'split' + execute 'silent ' . s:dataMap[a:bufname].bufNr . 'buffer' + endif + if a:height < 0 + only + endif + setlocal buflisted noswapfile bufhidden=delete modifiable noreadonly buftype=nofile + let &l:filetype = a:filetype + silent file `=a:bufname` + call setline(1, a:lines) + setlocal nomodified + augroup L9TempBuffer + autocmd! * + execute printf('autocmd BufDelete call s:onBufDelete (%s)', string(a:bufname)) + execute printf('autocmd BufWriteCmd nested call s:onBufWriteCmd(%s)', string(a:bufname)) + augroup END + let s:dataMap[a:bufname] = { + \ 'bufNr': bufnr('%'), + \ 'written': 0, + \ 'listener': a:listener, + \ } +endfunction + +" +function l9#tempbuffer#openReadOnly(bufname, filetype, lines, topleft, vertical, height, listener) + call l9#tempbuffer#openScratch(a:bufname, a:filetype, a:lines, a:topleft, a:vertical, a:height, a:listener) + setlocal nomodifiable readonly +endfunction + +" a:listener: +" a:listener.onClose(written) +" a:listener.onWrite(lines) +function l9#tempbuffer#openWritable(bufname, filetype, lines, topleft, vertical, height, listener) + call l9#tempbuffer#openScratch(a:bufname, a:filetype, a:lines, a:topleft, a:vertical, a:height, a:listener) + setlocal buftype=acwrite +endfunction + +" makes specified temp buffer current. +function l9#tempbuffer#moveTo(bufname) + return l9#moveToBufferWindowInCurrentTabpage(s:dataMap[a:bufname].bufNr) || + \ l9#moveToBufferWindowInOtherTabpage(s:dataMap[a:bufname].bufNr) +endfunction + +" +function l9#tempbuffer#close(bufname) + if !l9#tempbuffer#isOpen(a:bufname) + return + endif + execute printf('%dbdelete!', s:dataMap[a:bufname].bufNr) +endfunction + +" +function l9#tempbuffer#isOpen(bufname) + return exists('s:dataMap[a:bufname]') && bufloaded(s:dataMap[a:bufname].bufNr) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: + diff --git a/L9/autoload/l9/tempvariables.vim b/L9/autoload/l9/tempvariables.vim new file mode 100644 index 0000000..ee847ee --- /dev/null +++ b/L9/autoload/l9/tempvariables.vim @@ -0,0 +1,60 @@ +"============================================================================= +" Copyright (C) 2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" TEMPORARY VARIABLES {{{1 + +" +let s:origMap = {} + +" set temporary variables +function l9#tempvariables#set(group, name, value) + if !exists('s:origMap[a:group]') + let s:origMap[a:group] = {} + endif + if !exists('s:origMap[a:group][a:name]') + let s:origMap[a:group][a:name] = eval(a:name) + endif + execute 'let ' . a:name . ' = a:value' +endfunction + +" set temporary variables +function l9#tempvariables#setList(group, variables) + for [name, value] in a:variables + call l9#tempvariables#set(a:group, name, value) + unlet value " to avoid E706 + endfor +endfunction + +" get temporary variables +function l9#tempvariables#getList(group) + if !exists('s:origMap[a:group]') + return [] + endif + return map(keys(s:origMap[a:group]), '[v:val, eval(v:val)]') +endfunction + +" restore original variables and clean up. +function l9#tempvariables#end(group) + if !exists('s:origMap[a:group]') + return + endif + for [name, value] in items(s:origMap[a:group]) + execute 'let ' . name . ' = value' + unlet value " to avoid E706 + endfor + unlet s:origMap[a:group] +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: + diff --git a/L9/doc/l9.jax b/L9/doc/l9.jax new file mode 100644 index 0000000..c84d45f --- /dev/null +++ b/L9/doc/l9.jax @@ -0,0 +1,55 @@ +*l9.txt* Vimスクリプトライブラリ + + Copyright (c) 2009-2010 Takeshi NISHIDA + +l9 *l9* + +概要 |l9-introduction| +インストール |l9-installation| +使い方 |l9-usage| +CHANGELOG |l9-changelog| +あばうと |l9-about| + +============================================================================== +概要 *l9-introduction* + +l9はVimスクリプトの関数やコマンドを提供するライブラリです。 + + +============================================================================== + インストール *l9-installation* + +ZIPファイルをランタイムディレクトリに展開します。 + +以下のようにファイルが配置されるはずです。 +> + /plugin/l9.vim + /doc/l9.txt + ... +< +もしランタイムディレクトリが多数のプラグインでごちゃごちゃになるのが嫌なら、各 +プラグインを個別のディレクトリに配置し、そのディレクトリのパスを 'runtimepath' +に追加してください。アンインストールも楽になります。 + +その後、ヘルプを有効にするためにタグファイルを更新してください。詳しくは +|add-local-help|を参照してください。 + +============================================================================== +使い方 *l9-usage* + +ソースコードを参照してください。 + +============================================================================== +あばうと *l9-about* *l9-contact* *l9-author* + +作者: Takeshi NISHIDA +ライセンス: MIT Licence +URL: http://www.vim.org/scripts/script.php?script_id=3252 + http://bitbucket.org/ns9tks/vim-l9/ + +バグや要望など ~ + +こちらへどうぞ: http://bitbucket.org/ns9tks/vim-l9/issues/ + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: diff --git a/L9/doc/l9.txt b/L9/doc/l9.txt new file mode 100644 index 0000000..8664ad4 --- /dev/null +++ b/L9/doc/l9.txt @@ -0,0 +1,73 @@ +*l9.txt* Vim-script library + + Copyright (c) 2009-2010 Takeshi NISHIDA + +l9 *l9* + +INTRODUCTION |l9-introduction| +INSTALLATION |l9-installation| +USAGE |l9-usage| +CHANGELOG |l9-changelog| +ABOUT |l9-about| + +============================================================================== +INTRODUCTION *l9-introduction* + +l9 is a Vim-script library, which provides some utility functions and commands +for programming in Vim. + +============================================================================== + INSTALLATION *l9-installation* + +Put all files into your runtime directory. If you have the zip file, extract +it to your runtime directory. + +You should place the files as follows: +> + /plugin/l9.vim + /doc/l9.txt + ... +< +If you are disgusted to make your runtime directory confused with a lot of +plugins, put each of the plugins into a directory individually and just add +the directory path to 'runtimepath'. It's easy to uninstall the plugin. + +Then update your help tags files to enable fuzzyfinder help. See +|add-local-help| for details. + +============================================================================== +USAGE *l9-usage* + +See source code. + +============================================================================== +CHANGELOG *l9-changelog* + +1.1: + - Added l9#zip() + - Added l9#tempvariables#getList() + - Changed l9#guardScriptLoading() + - Removed l9#tempvariables#swap() + +1.0.1: + - Fixed a bug that floating point numbers weren't evaluated correctly and + caused errors on some non-English locales. + +1.0: + - First release. + + +============================================================================== +ABOUT *l9-about* *l9-contact* *l9-author* + +Author: Takeshi NISHIDA +Licence: MIT Licence +URL: http://www.vim.org/scripts/script.php?script_id=3252 + http://bitbucket.org/ns9tks/vim-l9/ + +Bugs/Issues/Suggestions/Improvements ~ + +Please submit to http://bitbucket.org/ns9tks/vim-l9/issues/ . + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: diff --git a/L9/doc/tags b/L9/doc/tags new file mode 100644 index 0000000..f26b5fa --- /dev/null +++ b/L9/doc/tags @@ -0,0 +1,9 @@ +l9 l9.txt /*l9* +l9-about l9.txt /*l9-about* +l9-author l9.txt /*l9-author* +l9-changelog l9.txt /*l9-changelog* +l9-contact l9.txt /*l9-contact* +l9-installation l9.txt /*l9-installation* +l9-introduction l9.txt /*l9-introduction* +l9-usage l9.txt /*l9-usage* +l9.txt l9.txt /*l9.txt* diff --git a/L9/doc/tags-ja b/L9/doc/tags-ja new file mode 100644 index 0000000..dbb4de6 --- /dev/null +++ b/L9/doc/tags-ja @@ -0,0 +1,9 @@ +!_TAG_FILE_ENCODING utf-8 // +l9 l9.jax /*l9* +l9-about l9.jax /*l9-about* +l9-author l9.jax /*l9-author* +l9-contact l9.jax /*l9-contact* +l9-installation l9.jax /*l9-installation* +l9-introduction l9.jax /*l9-introduction* +l9-usage l9.jax /*l9-usage* +l9.txt l9.jax /*l9.txt* diff --git a/L9/plugin/l9.vim b/L9/plugin/l9.vim new file mode 100644 index 0000000..03613e4 --- /dev/null +++ b/L9/plugin/l9.vim @@ -0,0 +1,108 @@ +"============================================================================= +" Copyright (C) 2009-2010 Takeshi NISHIDA +" +" GetLatestVimScripts: 3252 1 :AutoInstall: L9 +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 702, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" OPTIONS: {{{1 + +call l9#defineVariableDefault('g:l9_balloonly', 'balloonly.exe') + +" }}}1 +"============================================================================= +" ASSERTION: {{{1 + +" This command has effect only if $L9_DEBUG is non-zero. +" Used as follows: +" L9Assert a:i > 0 +" This command can't interpret script-local variables directly. +" NG: L9Assert s:a == 1 +" OK: execute 'L9Assert ' . s:a . ' == 1' +" +if $L9_DEBUG + command -nargs=* L9Assert call eval(() ? 0 : s:handleFailedAssersion()) + + function s:handleFailedAssersion(expr) + echoerr '[L9Assert] Assersion failure: ' . a:expr + if input('[L9Assert] Continue? (Y/N) ', 'Y') !=? 'Y' + throw 'L9Assert ' . a:expr + endif + endfunction + +else + command -nargs=* L9Assert : +endif + +" }}}1 +"============================================================================= +" TIMER: {{{1 + +" These commands have effect only if $L9_TIMER is non-zero. +" Used as follows: +" L9Timer foo +" ... (1) +" L9Timer bar +" ... (2) +" L9TimerStop +" ... +" L9TimerDump <- shows each elapsed time of (1) and (2) +" +if $L9_TIMER + command -nargs=1 L9Timer call s:timerBegin() + command -nargs=0 L9TimerStop call s:timerStop() + command -nargs=0 L9TimerDump call s:timerDump() + + let s:timerData = [] + let s:timerTagMaxLen = 0 + + function s:timerBegin(tag) + L9TimerStop + let s:timerCurrent = {'tag': strftime('%c ') . a:tag . ' ', 'time': reltime()} + let s:timerTagMaxLen = max([len(s:timerCurrent.tag), s:timerTagMaxLen]) + endfunction + + function s:timerStop() + if !exists('s:timerCurrent') + return + endif + let s:timerCurrent.time = reltimestr(reltime(s:timerCurrent.time)) + call add(s:timerData, s:timerCurrent) + unlet s:timerCurrent + endfunction + + function s:timerDump() + L9TimerStop + let lines = map(s:timerData, 'v:val.tag . repeat(" ", s:timerTagMaxLen - len(v:val.tag)) . v:val.time') + call l9#tempbuffer#openReadOnly('[l9-timer]', '', lines, 0, 0, 0, {}) + let s:timerData = [] + let s:timerTagMaxLen = 0 + endfunction + +else + command -nargs=1 L9Timer : + command -nargs=0 L9TimerStop : + command -nargs=0 L9TimerDump : +endif + +" }}}1 +"============================================================================= +" GREP BUFFER: {{{1 + +" Grep for current buffer by l9#grepBuffers() +" Used as :L9GrepBuffer/pattern +command -nargs=? L9GrepBuffer call l9#grepBuffers(, [bufnr('%')]) + +" Grep for all buffers by l9#grepBuffers() +" Used as :L9GrepBufferAll/pattern +command -nargs=? L9GrepBufferAll call l9#grepBuffers(, range(1, bufnr('$'))) + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/ScrollColor/plugin/ScrollColor.vim b/ScrollColor/plugin/ScrollColor.vim new file mode 100755 index 0000000..7c4960c --- /dev/null +++ b/ScrollColor/plugin/ScrollColor.vim @@ -0,0 +1,457 @@ +" ScrollColors.vim - Colorsheme Scroller, Chooser, and Browser +" +" Author and maintainer: Yakov Lerner +" Last Change: 2006-07-18 +" +" SYNOPSIS: +" This is colorscheme Scroller/Chooser/Browser. +" With this plugin, you walk through installed +" colorschemes using arrow keys. +" +" SHORT USAGE DESCRIPTION: +" Drop ScrollColors.vim into your plugin directory. +" Type :SCROLL +" Use arrow keys to walk through colorschemes, ? for help, Esc to exit. +" +" DETAILED DESCRIPTION: +" 1. source ScrollColors.vim " or drop ScrollColors.vim into +" " your ~/.vim/plugins directory +" 2. Type :SCROLL +" 3. Use arrows to scroll thgough colorschemes. +" 4. When done, press Esc to exit. You will be prompted +" wether to +" +" You can download 140 colorschemes pack from: +" http://www.vim.org/scripts/script.php?script_id=625 +" Having 140 installed colorschemes is in no way prerequisite for +" ScrollColors. But with ScrollColors you can preview 140 colorschemes +" in couple of minutes. +" +" CUSTOM KEY MAPPINGS: +" You can map two keys of your choice to NextColor and PrevColor actions. +" Choose pair of shortcut keys (for example and , or \n and \p) +" and map them as follows: +" map :NEXTCOLOR +" map :PREVCOLOR + + +if exists("g:scroll_colors") | finish | endif +let g:scroll_colors = 1 + +command! COLORSCROLL :call s:ColorScroller() +command! SCROLLCOLOR :call s:ColorScroller() +command! NEXTCOLOR :call s:NextColorscheme() +command! PREVCOLOR :call s:PrevColorscheme() + +" Example of convenience mappings: +"map :NEXTCOLOR +"map :PREVCOLOR +"map :SCROLLCOLOR + +function! s:ScrollerHelp() + echo " " + echohl Title + echo "Color Scroller Help:" + echo "--------------------" + echohl NONE + echo "Arrows - change colorscheme" + echo "Esc,q,Enter - exit" + echo "h,j,k,l - change colorscheme" + echo "0,g - go to first colorscheme" + echo "$,G - go to last colorscheme" + echo "L - list colorschemes" + echo "PgUp,PgDown - jump by 10 colorschemes" + echo "# - go to colorscheme by index (1-N)" + echo "R - refresh colorscheme list" + echo "? - this help text" + echohl MoreMsg + echo "Press any key to continue" + echohl NONE + call getchar() +endfu + +function! s:Align(s, width) + if strlen(a:s) >= a:width + return a:s." " + else + let pad=" " + let res=a:s + while strlen(res) < a:width + let chunk = (a:width - strlen(res) > strlen(pad) ? strlen(pad) : a:width - strlen(res)) + let res = res . strpart(pad,0,chunk) + endw + return res + endif +endfu + +function! s:ListColors() + echo " " + let list=s:GetColorschemesList() + let width=18 + let pos=0 + while list != '' + let str=substitute(list,"\n.*","","") + let list=substitute(list,"[^\n]*\n", "", "") + let aligned = s:Align(str, width) + if( pos+strlen(aligned)+1 >= &columns) + echo " " + let pos=0 + endif + echon aligned + let pos = pos + strlen(aligned) + endw + echo "Press any key to continue" + call getchar() +endfu + +function! s:CurrentColor() + return exists("g:colors_name") ? g:colors_name : "" +endfu + +function! s:SetColor(name) + exe "color ".a:name + " if we do not assign a:colors_name, then + " bad things happen if file colors/name.vim conmtains wrong assignment inside. + " Wrong assignment inside happens when file was copied but + " assignment inside not fixed. + " Such wrong assignment cause up erratic switches unless + " we do our own assignment to g:colors_name + let g:colors_name=a:name +endfu + +function! s:JumpByIndex(list,total) + let ans = input("Enter colorscheme number (1-".a:total.") : ") + let index = (ans<=0? 1 : 1+(ans-1)%a:total ) + let name = s:EntryByIndex(a:list, index ) + call s:SetColor(name) +endfu + +function! s:JumpByIndex2(list,total, index) + let mod = (a:index <= 0? 1 : 1+(a:index-1)%a:total ) + let name = s:EntryByIndex(a:list, mod ) + call s:SetColor(name) +endfu + +function! s:ExitDialog(old, action) + let ans = 0 + + if a:old == s:CurrentColor() + let ans=1 + elseif a:action == '' + let ans = confirm("Keep this colorscheme ?", "&Yes\n&No\n&Cancel") + elseif action == 'keep' + ans = 1 + elseif action == 'revert' + ans = 2 + endif + + if ans == 1 || ans==0 + " exit, keep colorscheme + let msg = (a:old == s:CurrentColor() ? '' : "(original: '".a:old."')") + call s:FinalEcho( msg ) + elseif ans == 2 + " exit, revert colorscheme + call s:SetColor(a:old) + call s:FinalEcho('original color restored') + elseif ans == 3 + " do not exit, continue browsing + return -1 + endif +endfu + +function! s:ColorScroller() + let old = s:CurrentColor() + let list = s:GetColorschemesList() + let total = s:CountEntries(list) + let loop=0 + + if line("$") == 1 && getline(1) == "" && bufnr('$')==1 + " if buffer is empty, open something + echo "We will open sample text with syntax highlighting." + echo "Watch for the guiding prompt in the bottom line." + echo "When the text will open, use Arrow keys to switch colorschemes, ? for help." + echo " " + echo "Press any key to continue" + call getchar() + :e $VIMRUNTIME/syntax/abc.vim + :setlocal ro + syntax on + redraw + endif + + if !exists("g:syntax_on") + syntax on + redraw + endif + + while 1 + redraw + let index = s:FindIndex(list, s:CurrentColor()) + echo "[" + echohl Search + echon s:CurrentColor() + echohl NONE + if loop == 0 + echon "] ColorScroller: " + echohl MoreMsg | echon "Arrows" | echohl NONE | echon "-next/prev; " + echohl MoreMsg | echon "Esc" | echohl NONE | echon "-exit; " + echohl MoreMsg | echon "?" | echohl NONE | echon "-help > " + else + echon "] " + echon " " . index . "/" . total . " " + echon s:Align("", 12-strlen(s:CurrentColor())) + echon "> ColorScroll > " + echon "Arrows,Esc,? > " + endif + let key = getchar() + let c = nr2char(key) + + if key == "\" || key == "\" || c ==# 'h' || c ==# 'j' + call s:PrevSilent() + elseif key == "\" || key == "\" || c ==# 'l' || c==# 'k' || c==# ' ' + call s:NextSilent() + elseif c==# 'g' || c=='0' || c=='1' + call s:SetColor( s:GetFirstColors() ) + elseif c=='$' || c==# 'G' + call s:SetColor( s:GetLastColors() ) + elseif c ==# 'L' + " command 'L' list colors + call s:ListColors() + elseif c=='Z' || c=='z' || key == 13 || c=='q' || c=='Q' || c==':' || key == 27 + if s:ExitDialog(old, '') != -1 + break + endif + elseif key == 12 " c=="\" + redraw + elseif c == '#' + call s:JumpByIndex(list,total) + elseif key == "\" + call s:JumpByIndex2(list,total, (index-10>=1 ? index-10 : index-10+total)) + elseif key == "\" + call s:JumpByIndex2(list,total, index+10) + elseif c == '?' + call s:ScrollerHelp() + elseif c == 'R' + call s:RefreshColorschemesList() + echo "Colorscheme list refreshed. Press any key to continue." + call getchar() + else + call s:ScrollerHelp() + endif + let loop = loop + 1 + endw +endfu + +" Get 1-based index of 'entry' in \n-separated 'list' +function! s:FindIndex(list,entry) + " we assume entry has no special chars or we could escape() it + let str = substitute("\n" . a:list . "\n", "\n" . a:entry . "\n.*$", "", "") + return 1 + s:CountEntries(str) +endfu + +" Get list element by 1-based index +function! s:EntryByIndex(list,index) + let k=1 + let tail=a:list + while tail != '' && k < a:index + let tail=substitute(tail, "^[^\n]*\n", "", "") + let k = k + 1 + endw + let tail = substitute(tail, "\n.*$", "", "") + return tail +endfu + +function! s:MakeWellFormedList(list) + + " make sure last \n is present + let str=a:list."\n" + " make sure leading \n are not present + let str=substitute(str, "^\n*", "", "") + " make sure entries are separated by exactly one \n + let str=substitute(str, "\n\\+", "\n", "g") + + return str +endfu + +function! s:CountEntries(list) + let str = s:MakeWellFormedList(a:list) + + let str=substitute(str, "[^\n]\\+\n", ".", "g") + + return strlen(str) +endfu + +function! s:RemoveDuplicates(list) + let sep = "\n" + let res = s:MakeWellFormedList(a:list . "\n") + let beg = 0 + while beg < strlen(res) + let end = matchend(res, sep, beg) + let str1 = strpart( res, beg, end - beg) + let res = strpart(res,0,end) . substitute("\n".strpart(res,end), "\n".str1,"\n","g") + let res = substitute(res, "\n\\+", "\n", "g") + let beg = end + endw + return res +endfu + +if v:version >= 700 + +" s:SortVar(): sort components of string @var separated +" by delimiter @sep, and returns the sorted string. +" For example, s:SortVar("c\nb\na", "\n") returns "a\nb\nc\n" +function! s:SortVar(list, sep) + let list = split( a:list, a:sep ) + let sorted = sort(list) + let result = join( sorted, "\n" ) + return result . "\n" +endfun + +endif + +if v:version < 700 +" s:SortVar(): sort components of string @var separated +" by delimiter @sep, and returns the sorted string. +" For example, s:SortVar("c\nb\na", "\n") returns "a\nb\nc\n" +function! s:SortVar(list, sep) + + let res=s:MakeWellFormedList(a:list . "\n") + while 1 + let disorder=0 + let index1=0 + + let len=strlen(res) + while 1 + let index2=matchend(res, a:sep, index1) + if index2 == -1 || index2>=len + break + endif + let index3=matchend(res, a:sep, index2) + if index3 == -1 + let index3=len + endif + let str1=strpart(res, index1, index2-index1) + let str2=strpart(res, index2, index3-index2) + if str1 > str2 + let disorder=1 + " swap str1 and str2 in res + let res=strpart(res,0,index1).str2.str1.strpart(res,index3) + let index1=index1 + strlen(str2) + else + let index1=index1 + strlen(str1) + endif + endw + + if !disorder + break + endif + endw + return res +endfu +endif " v:version < 700 + +let s:list = "" + +function! s:GetColorschemesList() + if s:list == "" + let s:list = s:RefreshColorschemesList() + endif + return s:list +endfunction + + +function! s:RefreshColorschemesList() + let x=globpath(&rtp, "colors/*.vim") + let y=substitute(x."\n","\\(^\\|\n\\)[^\n]*[/\\\\]", "\n", "g") + let z=substitute(y,"\\.vim\n", "\n", "g") + let sorted = s:SortVar(z, "\n") + let s:list = s:RemoveDuplicates(sorted) + return s:list +endfun + +function! s:GetFirstColors() + let list=s:GetColorschemesList() + let trim=substitute(list, "^\n\\+", "", "") + return substitute(trim, "\n.*", "", "") +endfu + +function! s:GetLastColors() + let list=s:GetColorschemesList() + let trim=substitute(list, "\n\\+$", "", "") + return substitute(trim, "^.*\n", "", "") +endfu + +function! s:FinalEcho(suffix) + let list = s:GetColorschemesList() + let total = s:CountEntries(list) + let index = s:FindIndex(list, s:CurrentColor()) + + redraw + echon "[" + echohl Search + echon s:CurrentColor() + echohl NONE + echon "] colorscheme #".index ." of " . total.". " + echon a:suffix +endfu + +function! s:GetNextColor(color) + let list=s:GetColorschemesList() + if ("\n".list) =~ ("\n".s:CurrentColor()."\n") + let next=substitute("\n".list."\n", ".*\n".a:color."\n", "", "") + let next = substitute(next, "\n.*", "", "") + return next=='' ? s:GetFirstColors() : next + else + return s:GetFirstColors() + endif +endfu + +function! s:GetPrevColor(color) + let list=s:GetColorschemesList() + if ("\n".list) =~ ("\n".a:color."\n") + let prev=substitute("\n".list."\n", "\n".a:color."\n.*", "", "") + let prev=substitute(prev, "^.*\n", "", "") + return prev=='' ? s:GetLastColors() : prev + else + return s:GetLastColors() + endif +endfu + +function! s:NextSilent() + let old = s:CurrentColor() + let next = s:GetNextColor(s:CurrentColor()) + call s:SetColor( next ) +endfu + +function! s:PrevSilent() + let old = s:CurrentColor() + let prev = s:GetPrevColor(s:CurrentColor()) + call s:SetColor( prev ) +endfu + +function! s:NextColorscheme() + let old = s:CurrentColor() + let next = s:GetNextColor(s:CurrentColor()) + call s:SetColor( next ) + redraw + call s:FinalEcho('previous: '.old) +endfun + +function! s:PrevColorscheme() + let old = s:CurrentColor() + let prev = s:GetPrevColor(s:CurrentColor()) + call s:SetColor( prev ) + redraw + call s:FinalEcho('previous: '.old) +endfun + +command! CN :call s:NextColorscheme() +command! CP :call s:PrevColorscheme() +map \n :CN +map \p :CP +map \c :echo g:colors_name + +" 2006-07-18 fixed bug with Align() -> s:Align() (affected L command) +" 2006-07-18 added colorlist cache (s:list) +" 2006-07-18 added R key to refresh colorlist +" 2006-07-19 for vim7, sort using builtin sort() (bubblesort is slow) diff --git a/bufonly/plugin/BufOnly.vim b/bufonly/plugin/BufOnly.vim new file mode 100644 index 0000000..5aa0359 --- /dev/null +++ b/bufonly/plugin/BufOnly.vim @@ -0,0 +1,69 @@ +" BufOnly.vim - Delete all the buffers except the current/named buffer. +" +" Copyright November 2003 by Christian J. Robinson +" +" Distributed under the terms of the Vim license. See ":help license". +" +" Usage: +" +" :Bonly / :BOnly / :Bufonly / :BufOnly [buffer] +" +" Without any arguments the current buffer is kept. With an argument the +" buffer name/number supplied is kept. + +command! -nargs=? -complete=buffer -bang Bonly + \ :call BufOnly('', '') +command! -nargs=? -complete=buffer -bang BOnly + \ :call BufOnly('', '') +command! -nargs=? -complete=buffer -bang Bufonly + \ :call BufOnly('', '') +command! -nargs=? -complete=buffer -bang BufOnly + \ :call BufOnly('', '') + +function! BufOnly(buffer, bang) + if a:buffer == '' + " No buffer provided, use the current buffer. + let buffer = bufnr('%') + elseif (a:buffer + 0) > 0 + " A buffer number was provided. + let buffer = bufnr(a:buffer + 0) + else + " A buffer name was provided. + let buffer = bufnr(a:buffer) + endif + + if buffer == -1 + echohl ErrorMsg + echomsg "No matching buffer for" a:buffer + echohl None + return + endif + + let last_buffer = bufnr('$') + + let delete_count = 0 + let n = 1 + while n <= last_buffer + if n != buffer && buflisted(n) + if a:bang == '' && getbufvar(n, '&modified') + echohl ErrorMsg + echomsg 'No write since last change for buffer' + \ n '(add ! to override)' + echohl None + else + silent exe 'bdel' . a:bang . ' ' . n + if ! buflisted(n) + let delete_count = delete_count+1 + endif + endif + endif + let n = n+1 + endwhile + + if delete_count == 1 + echomsg delete_count "buffer deleted" + elseif delete_count > 1 + echomsg delete_count "buffers deleted" + endif + +endfunction diff --git a/colorschemedegrade/autoload/colorschemedegradelib.vim b/colorschemedegrade/autoload/colorschemedegradelib.vim new file mode 100755 index 0000000..99b6770 --- /dev/null +++ b/colorschemedegrade/autoload/colorschemedegradelib.vim @@ -0,0 +1,764 @@ +function! colorschemedegradelib#RGB() + let rv = {} + + let rv["snow"] = "#FFFAFA" + let rv["ghost_white"] = "#F8F8FF" + let rv["ghostwhite"] = "#F8F8FF" + let rv["white_smoke"] = "#F5F5F5" + let rv["whitesmoke"] = "#F5F5F5" + let rv["gainsboro"] = "#DCDCDC" + let rv["floral_white"] = "#FFFAF0" + let rv["floralwhite"] = "#FFFAF0" + let rv["old_lace"] = "#FDF5E6" + let rv["oldlace"] = "#FDF5E6" + let rv["linen"] = "#FAF0E6" + let rv["antique_white"] = "#FAEBD7" + let rv["antiquewhite"] = "#FAEBD7" + let rv["papaya_whip"] = "#FFEFD5" + let rv["papayawhip"] = "#FFEFD5" + let rv["blanched_almond"] = "#FFEBCD" + let rv["blanchedalmond"] = "#FFEBCD" + let rv["bisque"] = "#FFE4C4" + let rv["peach_puff"] = "#FFDAB9" + let rv["peachpuff"] = "#FFDAB9" + let rv["navajo_white"] = "#FFDEAD" + let rv["navajowhite"] = "#FFDEAD" + let rv["moccasin"] = "#FFE4B5" + let rv["cornsilk"] = "#FFF8DC" + let rv["ivory"] = "#FFFFF0" + let rv["lemon_chiffon"] = "#FFFACD" + let rv["lemonchiffon"] = "#FFFACD" + let rv["seashell"] = "#FFF5EE" + let rv["honeydew"] = "#F0FFF0" + let rv["mint_cream"] = "#F5FFFA" + let rv["mintcream"] = "#F5FFFA" + let rv["azure"] = "#F0FFFF" + let rv["alice_blue"] = "#F0F8FF" + let rv["aliceblue"] = "#F0F8FF" + let rv["lavender"] = "#E6E6FA" + let rv["lavender_blush"] = "#FFF0F5" + let rv["lavenderblush"] = "#FFF0F5" + let rv["misty_rose"] = "#FFE4E1" + let rv["mistyrose"] = "#FFE4E1" + let rv["white"] = "#FFFFFF" + let rv["black"] = "#000000" + let rv["dark_slate_gray"] = "#2F4F4F" + let rv["darkslategray"] = "#2F4F4F" + let rv["dark_slate_grey"] = "#2F4F4F" + let rv["darkslategrey"] = "#2F4F4F" + let rv["dim_gray"] = "#696969" + let rv["dimgray"] = "#696969" + let rv["dim_grey"] = "#696969" + let rv["dimgrey"] = "#696969" + let rv["slate_gray"] = "#708090" + let rv["slategray"] = "#708090" + let rv["slate_grey"] = "#708090" + let rv["slategrey"] = "#708090" + let rv["light_slate_gray"] = "#778899" + let rv["lightslategray"] = "#778899" + let rv["light_slate_grey"] = "#778899" + let rv["lightslategrey"] = "#778899" + let rv["gray"] = "#BEBEBE" + let rv["grey"] = "#BEBEBE" + let rv["light_grey"] = "#D3D3D3" + let rv["lightgrey"] = "#D3D3D3" + let rv["light_gray"] = "#D3D3D3" + let rv["lightgray"] = "#D3D3D3" + let rv["midnight_blue"] = "#191970" + let rv["midnightblue"] = "#191970" + let rv["navy"] = "#000080" + let rv["navy_blue"] = "#000080" + let rv["navyblue"] = "#000080" + let rv["cornflower_blue"] = "#6495ED" + let rv["cornflowerblue"] = "#6495ED" + let rv["dark_slate_blue"] = "#483D8B" + let rv["darkslateblue"] = "#483D8B" + let rv["slate_blue"] = "#6A5ACD" + let rv["slateblue"] = "#6A5ACD" + let rv["medium_slate_blue"] = "#7B68EE" + let rv["mediumslateblue"] = "#7B68EE" + let rv["light_slate_blue"] = "#8470FF" + let rv["lightslateblue"] = "#8470FF" + let rv["medium_blue"] = "#0000CD" + let rv["mediumblue"] = "#0000CD" + let rv["royal_blue"] = "#4169E1" + let rv["royalblue"] = "#4169E1" + let rv["blue"] = "#0000FF" + let rv["dodger_blue"] = "#1E90FF" + let rv["dodgerblue"] = "#1E90FF" + let rv["deep_sky_blue"] = "#00BFFF" + let rv["deepskyblue"] = "#00BFFF" + let rv["sky_blue"] = "#87CEEB" + let rv["skyblue"] = "#87CEEB" + let rv["light_sky_blue"] = "#87CEFA" + let rv["lightskyblue"] = "#87CEFA" + let rv["steel_blue"] = "#4682B4" + let rv["steelblue"] = "#4682B4" + let rv["light_steel_blue"] = "#B0C4DE" + let rv["lightsteelblue"] = "#B0C4DE" + let rv["light_blue"] = "#ADD8E6" + let rv["lightblue"] = "#ADD8E6" + let rv["powder_blue"] = "#B0E0E6" + let rv["powderblue"] = "#B0E0E6" + let rv["pale_turquoise"] = "#AFEEEE" + let rv["paleturquoise"] = "#AFEEEE" + let rv["dark_turquoise"] = "#00CED1" + let rv["darkturquoise"] = "#00CED1" + let rv["medium_turquoise"] = "#48D1CC" + let rv["mediumturquoise"] = "#48D1CC" + let rv["turquoise"] = "#40E0D0" + let rv["cyan"] = "#00FFFF" + let rv["light_cyan"] = "#E0FFFF" + let rv["lightcyan"] = "#E0FFFF" + let rv["cadet_blue"] = "#5F9EA0" + let rv["cadetblue"] = "#5F9EA0" + let rv["medium_aquamarine"] = "#66CDAA" + let rv["mediumaquamarine"] = "#66CDAA" + let rv["aquamarine"] = "#7FFFD4" + let rv["dark_green"] = "#006400" + let rv["darkgreen"] = "#006400" + let rv["dark_olive_green"] = "#556B2F" + let rv["darkolivegreen"] = "#556B2F" + let rv["dark_sea_green"] = "#8FBC8F" + let rv["darkseagreen"] = "#8FBC8F" + let rv["sea_green"] = "#2E8B57" + let rv["seagreen"] = "#2E8B57" + let rv["medium_sea_green"] = "#3CB371" + let rv["mediumseagreen"] = "#3CB371" + let rv["light_sea_green"] = "#20B2AA" + let rv["lightseagreen"] = "#20B2AA" + let rv["pale_green"] = "#98FB98" + let rv["palegreen"] = "#98FB98" + let rv["spring_green"] = "#00FF7F" + let rv["springgreen"] = "#00FF7F" + let rv["lawn_green"] = "#7CFC00" + let rv["lawngreen"] = "#7CFC00" + let rv["green"] = "#00FF00" + let rv["chartreuse"] = "#7FFF00" + let rv["medium_spring_green"] = "#00FA9A" + let rv["mediumspringgreen"] = "#00FA9A" + let rv["green_yellow"] = "#ADFF2F" + let rv["greenyellow"] = "#ADFF2F" + let rv["lime_green"] = "#32CD32" + let rv["limegreen"] = "#32CD32" + let rv["yellow_green"] = "#9ACD32" + let rv["yellowgreen"] = "#9ACD32" + let rv["forest_green"] = "#228B22" + let rv["forestgreen"] = "#228B22" + let rv["olive_drab"] = "#6B8E23" + let rv["olivedrab"] = "#6B8E23" + let rv["dark_khaki"] = "#BDB76B" + let rv["darkkhaki"] = "#BDB76B" + let rv["khaki"] = "#F0E68C" + let rv["pale_goldenrod"] = "#EEE8AA" + let rv["palegoldenrod"] = "#EEE8AA" + let rv["light_goldenrod_yellow"] = "#FAFAD2" + let rv["lightgoldenrodyellow"] = "#FAFAD2" + let rv["light_yellow"] = "#FFFFE0" + let rv["lightyellow"] = "#FFFFE0" + let rv["yellow"] = "#FFFF00" + let rv["gold"] = "#FFD700" + let rv["light_goldenrod"] = "#EEDD82" + let rv["lightgoldenrod"] = "#EEDD82" + let rv["goldenrod"] = "#DAA520" + let rv["dark_goldenrod"] = "#B8860B" + let rv["darkgoldenrod"] = "#B8860B" + let rv["rosy_brown"] = "#BC8F8F" + let rv["rosybrown"] = "#BC8F8F" + let rv["indian_red"] = "#CD5C5C" + let rv["indianred"] = "#CD5C5C" + let rv["saddle_brown"] = "#8B4513" + let rv["saddlebrown"] = "#8B4513" + let rv["sienna"] = "#A0522D" + let rv["peru"] = "#CD853F" + let rv["burlywood"] = "#DEB887" + let rv["beige"] = "#F5F5DC" + let rv["wheat"] = "#F5DEB3" + let rv["sandy_brown"] = "#F4A460" + let rv["sandybrown"] = "#F4A460" + let rv["tan"] = "#D2B48C" + let rv["chocolate"] = "#D2691E" + let rv["firebrick"] = "#B22222" + let rv["brown"] = "#A52A2A" + let rv["dark_salmon"] = "#E9967A" + let rv["darksalmon"] = "#E9967A" + let rv["salmon"] = "#FA8072" + let rv["light_salmon"] = "#FFA07A" + let rv["lightsalmon"] = "#FFA07A" + let rv["orange"] = "#FFA500" + let rv["dark_orange"] = "#FF8C00" + let rv["darkorange"] = "#FF8C00" + let rv["coral"] = "#FF7F50" + let rv["light_coral"] = "#F08080" + let rv["lightcoral"] = "#F08080" + let rv["tomato"] = "#FF6347" + let rv["orange_red"] = "#FF4500" + let rv["orangered"] = "#FF4500" + let rv["red"] = "#FF0000" + let rv["hot_pink"] = "#FF69B4" + let rv["hotpink"] = "#FF69B4" + let rv["deep_pink"] = "#FF1493" + let rv["deeppink"] = "#FF1493" + let rv["pink"] = "#FFC0CB" + let rv["light_pink"] = "#FFB6C1" + let rv["lightpink"] = "#FFB6C1" + let rv["pale_violet_red"] = "#DB7093" + let rv["palevioletred"] = "#DB7093" + let rv["maroon"] = "#B03060" + let rv["medium_violet_red"] = "#C71585" + let rv["mediumvioletred"] = "#C71585" + let rv["violet_red"] = "#D02090" + let rv["violetred"] = "#D02090" + let rv["magenta"] = "#FF00FF" + let rv["violet"] = "#EE82EE" + let rv["plum"] = "#DDA0DD" + let rv["orchid"] = "#DA70D6" + let rv["medium_orchid"] = "#BA55D3" + let rv["mediumorchid"] = "#BA55D3" + let rv["dark_orchid"] = "#9932CC" + let rv["darkorchid"] = "#9932CC" + let rv["dark_violet"] = "#9400D3" + let rv["darkviolet"] = "#9400D3" + let rv["blue_violet"] = "#8A2BE2" + let rv["blueviolet"] = "#8A2BE2" + let rv["purple"] = "#A020F0" + let rv["medium_purple"] = "#9370DB" + let rv["mediumpurple"] = "#9370DB" + let rv["thistle"] = "#D8BFD8" + let rv["snow1"] = "#FFFAFA" + let rv["snow2"] = "#EEE9E9" + let rv["snow3"] = "#CDC9C9" + let rv["snow4"] = "#8B8989" + let rv["seashell1"] = "#FFF5EE" + let rv["seashell2"] = "#EEE5DE" + let rv["seashell3"] = "#CDC5BF" + let rv["seashell4"] = "#8B8682" + let rv["antiquewhite1"] = "#FFEFDB" + let rv["antiquewhite2"] = "#EEDFCC" + let rv["antiquewhite3"] = "#CDC0B0" + let rv["antiquewhite4"] = "#8B8378" + let rv["bisque1"] = "#FFE4C4" + let rv["bisque2"] = "#EED5B7" + let rv["bisque3"] = "#CDB79E" + let rv["bisque4"] = "#8B7D6B" + let rv["peachpuff1"] = "#FFDAB9" + let rv["peachpuff2"] = "#EECBAD" + let rv["peachpuff3"] = "#CDAF95" + let rv["peachpuff4"] = "#8B7765" + let rv["navajowhite1"] = "#FFDEAD" + let rv["navajowhite2"] = "#EECFA1" + let rv["navajowhite3"] = "#CDB38B" + let rv["navajowhite4"] = "#8B795E" + let rv["lemonchiffon1"] = "#FFFACD" + let rv["lemonchiffon2"] = "#EEE9BF" + let rv["lemonchiffon3"] = "#CDC9A5" + let rv["lemonchiffon4"] = "#8B8970" + let rv["cornsilk1"] = "#FFF8DC" + let rv["cornsilk2"] = "#EEE8CD" + let rv["cornsilk3"] = "#CDC8B1" + let rv["cornsilk4"] = "#8B8878" + let rv["ivory1"] = "#FFFFF0" + let rv["ivory2"] = "#EEEEE0" + let rv["ivory3"] = "#CDCDC1" + let rv["ivory4"] = "#8B8B83" + let rv["honeydew1"] = "#F0FFF0" + let rv["honeydew2"] = "#E0EEE0" + let rv["honeydew3"] = "#C1CDC1" + let rv["honeydew4"] = "#838B83" + let rv["lavenderblush1"] = "#FFF0F5" + let rv["lavenderblush2"] = "#EEE0E5" + let rv["lavenderblush3"] = "#CDC1C5" + let rv["lavenderblush4"] = "#8B8386" + let rv["mistyrose1"] = "#FFE4E1" + let rv["mistyrose2"] = "#EED5D2" + let rv["mistyrose3"] = "#CDB7B5" + let rv["mistyrose4"] = "#8B7D7B" + let rv["azure1"] = "#F0FFFF" + let rv["azure2"] = "#E0EEEE" + let rv["azure3"] = "#C1CDCD" + let rv["azure4"] = "#838B8B" + let rv["slateblue1"] = "#836FFF" + let rv["slateblue2"] = "#7A67EE" + let rv["slateblue3"] = "#6959CD" + let rv["slateblue4"] = "#473C8B" + let rv["royalblue1"] = "#4876FF" + let rv["royalblue2"] = "#436EEE" + let rv["royalblue3"] = "#3A5FCD" + let rv["royalblue4"] = "#27408B" + let rv["blue1"] = "#0000FF" + let rv["blue2"] = "#0000EE" + let rv["blue3"] = "#0000CD" + let rv["blue4"] = "#00008B" + let rv["dodgerblue1"] = "#1E90FF" + let rv["dodgerblue2"] = "#1C86EE" + let rv["dodgerblue3"] = "#1874CD" + let rv["dodgerblue4"] = "#104E8B" + let rv["steelblue1"] = "#63B8FF" + let rv["steelblue2"] = "#5CACEE" + let rv["steelblue3"] = "#4F94CD" + let rv["steelblue4"] = "#36648B" + let rv["deepskyblue1"] = "#00BFFF" + let rv["deepskyblue2"] = "#00B2EE" + let rv["deepskyblue3"] = "#009ACD" + let rv["deepskyblue4"] = "#00688B" + let rv["skyblue1"] = "#87CEFF" + let rv["skyblue2"] = "#7EC0EE" + let rv["skyblue3"] = "#6CA6CD" + let rv["skyblue4"] = "#4A708B" + let rv["lightskyblue1"] = "#B0E2FF" + let rv["lightskyblue2"] = "#A4D3EE" + let rv["lightskyblue3"] = "#8DB6CD" + let rv["lightskyblue4"] = "#607B8B" + let rv["slategray1"] = "#C6E2FF" + let rv["slategray2"] = "#B9D3EE" + let rv["slategray3"] = "#9FB6CD" + let rv["slategray4"] = "#6C7B8B" + let rv["lightsteelblue1"] = "#CAE1FF" + let rv["lightsteelblue2"] = "#BCD2EE" + let rv["lightsteelblue3"] = "#A2B5CD" + let rv["lightsteelblue4"] = "#6E7B8B" + let rv["lightblue1"] = "#BFEFFF" + let rv["lightblue2"] = "#B2DFEE" + let rv["lightblue3"] = "#9AC0CD" + let rv["lightblue4"] = "#68838B" + let rv["lightcyan1"] = "#E0FFFF" + let rv["lightcyan2"] = "#D1EEEE" + let rv["lightcyan3"] = "#B4CDCD" + let rv["lightcyan4"] = "#7A8B8B" + let rv["paleturquoise1"] = "#BBFFFF" + let rv["paleturquoise2"] = "#AEEEEE" + let rv["paleturquoise3"] = "#96CDCD" + let rv["paleturquoise4"] = "#668B8B" + let rv["cadetblue1"] = "#98F5FF" + let rv["cadetblue2"] = "#8EE5EE" + let rv["cadetblue3"] = "#7AC5CD" + let rv["cadetblue4"] = "#53868B" + let rv["turquoise1"] = "#00F5FF" + let rv["turquoise2"] = "#00E5EE" + let rv["turquoise3"] = "#00C5CD" + let rv["turquoise4"] = "#00868B" + let rv["cyan1"] = "#00FFFF" + let rv["cyan2"] = "#00EEEE" + let rv["cyan3"] = "#00CDCD" + let rv["cyan4"] = "#008B8B" + let rv["darkslategray1"] = "#97FFFF" + let rv["darkslategray2"] = "#8DEEEE" + let rv["darkslategray3"] = "#79CDCD" + let rv["darkslategray4"] = "#528B8B" + let rv["aquamarine1"] = "#7FFFD4" + let rv["aquamarine2"] = "#76EEC6" + let rv["aquamarine3"] = "#66CDAA" + let rv["aquamarine4"] = "#458B74" + let rv["darkseagreen1"] = "#C1FFC1" + let rv["darkseagreen2"] = "#B4EEB4" + let rv["darkseagreen3"] = "#9BCD9B" + let rv["darkseagreen4"] = "#698B69" + let rv["seagreen1"] = "#54FF9F" + let rv["seagreen2"] = "#4EEE94" + let rv["seagreen3"] = "#43CD80" + let rv["seagreen4"] = "#2E8B57" + let rv["palegreen1"] = "#9AFF9A" + let rv["palegreen2"] = "#90EE90" + let rv["palegreen3"] = "#7CCD7C" + let rv["palegreen4"] = "#548B54" + let rv["springgreen1"] = "#00FF7F" + let rv["springgreen2"] = "#00EE76" + let rv["springgreen3"] = "#00CD66" + let rv["springgreen4"] = "#008B45" + let rv["green1"] = "#00FF00" + let rv["green2"] = "#00EE00" + let rv["green3"] = "#00CD00" + let rv["green4"] = "#008B00" + let rv["chartreuse1"] = "#7FFF00" + let rv["chartreuse2"] = "#76EE00" + let rv["chartreuse3"] = "#66CD00" + let rv["chartreuse4"] = "#458B00" + let rv["olivedrab1"] = "#C0FF3E" + let rv["olivedrab2"] = "#B3EE3A" + let rv["olivedrab3"] = "#9ACD32" + let rv["olivedrab4"] = "#698B22" + let rv["darkolivegreen1"] = "#CAFF70" + let rv["darkolivegreen2"] = "#BCEE68" + let rv["darkolivegreen3"] = "#A2CD5A" + let rv["darkolivegreen4"] = "#6E8B3D" + let rv["khaki1"] = "#FFF68F" + let rv["khaki2"] = "#EEE685" + let rv["khaki3"] = "#CDC673" + let rv["khaki4"] = "#8B864E" + let rv["lightgoldenrod1"] = "#FFEC8B" + let rv["lightgoldenrod2"] = "#EEDC82" + let rv["lightgoldenrod3"] = "#CDBE70" + let rv["lightgoldenrod4"] = "#8B814C" + let rv["lightyellow1"] = "#FFFFE0" + let rv["lightyellow2"] = "#EEEED1" + let rv["lightyellow3"] = "#CDCDB4" + let rv["lightyellow4"] = "#8B8B7A" + let rv["yellow1"] = "#FFFF00" + let rv["yellow2"] = "#EEEE00" + let rv["yellow3"] = "#CDCD00" + let rv["yellow4"] = "#8B8B00" + let rv["gold1"] = "#FFD700" + let rv["gold2"] = "#EEC900" + let rv["gold3"] = "#CDAD00" + let rv["gold4"] = "#8B7500" + let rv["goldenrod1"] = "#FFC125" + let rv["goldenrod2"] = "#EEB422" + let rv["goldenrod3"] = "#CD9B1D" + let rv["goldenrod4"] = "#8B6914" + let rv["darkgoldenrod1"] = "#FFB90F" + let rv["darkgoldenrod2"] = "#EEAD0E" + let rv["darkgoldenrod3"] = "#CD950C" + let rv["darkgoldenrod4"] = "#8B6508" + let rv["rosybrown1"] = "#FFC1C1" + let rv["rosybrown2"] = "#EEB4B4" + let rv["rosybrown3"] = "#CD9B9B" + let rv["rosybrown4"] = "#8B6969" + let rv["indianred1"] = "#FF6A6A" + let rv["indianred2"] = "#EE6363" + let rv["indianred3"] = "#CD5555" + let rv["indianred4"] = "#8B3A3A" + let rv["sienna1"] = "#FF8247" + let rv["sienna2"] = "#EE7942" + let rv["sienna3"] = "#CD6839" + let rv["sienna4"] = "#8B4726" + let rv["burlywood1"] = "#FFD39B" + let rv["burlywood2"] = "#EEC591" + let rv["burlywood3"] = "#CDAA7D" + let rv["burlywood4"] = "#8B7355" + let rv["wheat1"] = "#FFE7BA" + let rv["wheat2"] = "#EED8AE" + let rv["wheat3"] = "#CDBA96" + let rv["wheat4"] = "#8B7E66" + let rv["tan1"] = "#FFA54F" + let rv["tan2"] = "#EE9A49" + let rv["tan3"] = "#CD853F" + let rv["tan4"] = "#8B5A2B" + let rv["chocolate1"] = "#FF7F24" + let rv["chocolate2"] = "#EE7621" + let rv["chocolate3"] = "#CD661D" + let rv["chocolate4"] = "#8B4513" + let rv["firebrick1"] = "#FF3030" + let rv["firebrick2"] = "#EE2C2C" + let rv["firebrick3"] = "#CD2626" + let rv["firebrick4"] = "#8B1A1A" + let rv["brown1"] = "#FF4040" + let rv["brown2"] = "#EE3B3B" + let rv["brown3"] = "#CD3333" + let rv["brown4"] = "#8B2323" + let rv["salmon1"] = "#FF8C69" + let rv["salmon2"] = "#EE8262" + let rv["salmon3"] = "#CD7054" + let rv["salmon4"] = "#8B4C39" + let rv["lightsalmon1"] = "#FFA07A" + let rv["lightsalmon2"] = "#EE9572" + let rv["lightsalmon3"] = "#CD8162" + let rv["lightsalmon4"] = "#8B5742" + let rv["orange1"] = "#FFA500" + let rv["orange2"] = "#EE9A00" + let rv["orange3"] = "#CD8500" + let rv["orange4"] = "#8B5A00" + let rv["darkorange1"] = "#FF7F00" + let rv["darkorange2"] = "#EE7600" + let rv["darkorange3"] = "#CD6600" + let rv["darkorange4"] = "#8B4500" + let rv["coral1"] = "#FF7256" + let rv["coral2"] = "#EE6A50" + let rv["coral3"] = "#CD5B45" + let rv["coral4"] = "#8B3E2F" + let rv["tomato1"] = "#FF6347" + let rv["tomato2"] = "#EE5C42" + let rv["tomato3"] = "#CD4F39" + let rv["tomato4"] = "#8B3626" + let rv["orangered1"] = "#FF4500" + let rv["orangered2"] = "#EE4000" + let rv["orangered3"] = "#CD3700" + let rv["orangered4"] = "#8B2500" + let rv["red1"] = "#FF0000" + let rv["red2"] = "#EE0000" + let rv["red3"] = "#CD0000" + let rv["red4"] = "#8B0000" + let rv["deeppink1"] = "#FF1493" + let rv["deeppink2"] = "#EE1289" + let rv["deeppink3"] = "#CD1076" + let rv["deeppink4"] = "#8B0A50" + let rv["hotpink1"] = "#FF6EB4" + let rv["hotpink2"] = "#EE6AA7" + let rv["hotpink3"] = "#CD6090" + let rv["hotpink4"] = "#8B3A62" + let rv["pink1"] = "#FFB5C5" + let rv["pink2"] = "#EEA9B8" + let rv["pink3"] = "#CD919E" + let rv["pink4"] = "#8B636C" + let rv["lightpink1"] = "#FFAEB9" + let rv["lightpink2"] = "#EEA2AD" + let rv["lightpink3"] = "#CD8C95" + let rv["lightpink4"] = "#8B5F65" + let rv["palevioletred1"] = "#FF82AB" + let rv["palevioletred2"] = "#EE799F" + let rv["palevioletred3"] = "#CD6889" + let rv["palevioletred4"] = "#8B475D" + let rv["maroon1"] = "#FF34B3" + let rv["maroon2"] = "#EE30A7" + let rv["maroon3"] = "#CD2990" + let rv["maroon4"] = "#8B1C62" + let rv["violetred1"] = "#FF3E96" + let rv["violetred2"] = "#EE3A8C" + let rv["violetred3"] = "#CD3278" + let rv["violetred4"] = "#8B2252" + let rv["magenta1"] = "#FF00FF" + let rv["magenta2"] = "#EE00EE" + let rv["magenta3"] = "#CD00CD" + let rv["magenta4"] = "#8B008B" + let rv["orchid1"] = "#FF83FA" + let rv["orchid2"] = "#EE7AE9" + let rv["orchid3"] = "#CD69C9" + let rv["orchid4"] = "#8B4789" + let rv["plum1"] = "#FFBBFF" + let rv["plum2"] = "#EEAEEE" + let rv["plum3"] = "#CD96CD" + let rv["plum4"] = "#8B668B" + let rv["mediumorchid1"] = "#E066FF" + let rv["mediumorchid2"] = "#D15FEE" + let rv["mediumorchid3"] = "#B452CD" + let rv["mediumorchid4"] = "#7A378B" + let rv["darkorchid1"] = "#BF3EFF" + let rv["darkorchid2"] = "#B23AEE" + let rv["darkorchid3"] = "#9A32CD" + let rv["darkorchid4"] = "#68228B" + let rv["purple1"] = "#9B30FF" + let rv["purple2"] = "#912CEE" + let rv["purple3"] = "#7D26CD" + let rv["purple4"] = "#551A8B" + let rv["mediumpurple1"] = "#AB82FF" + let rv["mediumpurple2"] = "#9F79EE" + let rv["mediumpurple3"] = "#8968CD" + let rv["mediumpurple4"] = "#5D478B" + let rv["thistle1"] = "#FFE1FF" + let rv["thistle2"] = "#EED2EE" + let rv["thistle3"] = "#CDB5CD" + let rv["thistle4"] = "#8B7B8B" + let rv["gray0"] = "#000000" + let rv["grey0"] = "#000000" + let rv["gray1"] = "#030303" + let rv["grey1"] = "#030303" + let rv["gray2"] = "#050505" + let rv["grey2"] = "#050505" + let rv["gray3"] = "#080808" + let rv["grey3"] = "#080808" + let rv["gray4"] = "#0A0A0A" + let rv["grey4"] = "#0A0A0A" + let rv["gray5"] = "#0D0D0D" + let rv["grey5"] = "#0D0D0D" + let rv["gray6"] = "#0F0F0F" + let rv["grey6"] = "#0F0F0F" + let rv["gray7"] = "#121212" + let rv["grey7"] = "#121212" + let rv["gray8"] = "#141414" + let rv["grey8"] = "#141414" + let rv["gray9"] = "#171717" + let rv["grey9"] = "#171717" + let rv["gray10"] = "#1A1A1A" + let rv["grey10"] = "#1A1A1A" + let rv["gray11"] = "#1C1C1C" + let rv["grey11"] = "#1C1C1C" + let rv["gray12"] = "#1F1F1F" + let rv["grey12"] = "#1F1F1F" + let rv["gray13"] = "#212121" + let rv["grey13"] = "#212121" + let rv["gray14"] = "#242424" + let rv["grey14"] = "#242424" + let rv["gray15"] = "#262626" + let rv["grey15"] = "#262626" + let rv["gray16"] = "#292929" + let rv["grey16"] = "#292929" + let rv["gray17"] = "#2B2B2B" + let rv["grey17"] = "#2B2B2B" + let rv["gray18"] = "#2E2E2E" + let rv["grey18"] = "#2E2E2E" + let rv["gray19"] = "#303030" + let rv["grey19"] = "#303030" + let rv["gray20"] = "#333333" + let rv["grey20"] = "#333333" + let rv["gray21"] = "#363636" + let rv["grey21"] = "#363636" + let rv["gray22"] = "#383838" + let rv["grey22"] = "#383838" + let rv["gray23"] = "#3B3B3B" + let rv["grey23"] = "#3B3B3B" + let rv["gray24"] = "#3D3D3D" + let rv["grey24"] = "#3D3D3D" + let rv["gray25"] = "#404040" + let rv["grey25"] = "#404040" + let rv["gray26"] = "#424242" + let rv["grey26"] = "#424242" + let rv["gray27"] = "#454545" + let rv["grey27"] = "#454545" + let rv["gray28"] = "#474747" + let rv["grey28"] = "#474747" + let rv["gray29"] = "#4A4A4A" + let rv["grey29"] = "#4A4A4A" + let rv["gray30"] = "#4D4D4D" + let rv["grey30"] = "#4D4D4D" + let rv["gray31"] = "#4F4F4F" + let rv["grey31"] = "#4F4F4F" + let rv["gray32"] = "#525252" + let rv["grey32"] = "#525252" + let rv["gray33"] = "#545454" + let rv["grey33"] = "#545454" + let rv["gray34"] = "#575757" + let rv["grey34"] = "#575757" + let rv["gray35"] = "#595959" + let rv["grey35"] = "#595959" + let rv["gray36"] = "#5C5C5C" + let rv["grey36"] = "#5C5C5C" + let rv["gray37"] = "#5E5E5E" + let rv["grey37"] = "#5E5E5E" + let rv["gray38"] = "#616161" + let rv["grey38"] = "#616161" + let rv["gray39"] = "#636363" + let rv["grey39"] = "#636363" + let rv["gray40"] = "#666666" + let rv["grey40"] = "#666666" + let rv["gray41"] = "#696969" + let rv["grey41"] = "#696969" + let rv["gray42"] = "#6B6B6B" + let rv["grey42"] = "#6B6B6B" + let rv["gray43"] = "#6E6E6E" + let rv["grey43"] = "#6E6E6E" + let rv["gray44"] = "#707070" + let rv["grey44"] = "#707070" + let rv["gray45"] = "#737373" + let rv["grey45"] = "#737373" + let rv["gray46"] = "#757575" + let rv["grey46"] = "#757575" + let rv["gray47"] = "#787878" + let rv["grey47"] = "#787878" + let rv["gray48"] = "#7A7A7A" + let rv["grey48"] = "#7A7A7A" + let rv["gray49"] = "#7D7D7D" + let rv["grey49"] = "#7D7D7D" + let rv["gray50"] = "#7F7F7F" + let rv["grey50"] = "#7F7F7F" + let rv["gray51"] = "#828282" + let rv["grey51"] = "#828282" + let rv["gray52"] = "#858585" + let rv["grey52"] = "#858585" + let rv["gray53"] = "#878787" + let rv["grey53"] = "#878787" + let rv["gray54"] = "#8A8A8A" + let rv["grey54"] = "#8A8A8A" + let rv["gray55"] = "#8C8C8C" + let rv["grey55"] = "#8C8C8C" + let rv["gray56"] = "#8F8F8F" + let rv["grey56"] = "#8F8F8F" + let rv["gray57"] = "#919191" + let rv["grey57"] = "#919191" + let rv["gray58"] = "#949494" + let rv["grey58"] = "#949494" + let rv["gray59"] = "#969696" + let rv["grey59"] = "#969696" + let rv["gray60"] = "#999999" + let rv["grey60"] = "#999999" + let rv["gray61"] = "#9C9C9C" + let rv["grey61"] = "#9C9C9C" + let rv["gray62"] = "#9E9E9E" + let rv["grey62"] = "#9E9E9E" + let rv["gray63"] = "#A1A1A1" + let rv["grey63"] = "#A1A1A1" + let rv["gray64"] = "#A3A3A3" + let rv["grey64"] = "#A3A3A3" + let rv["gray65"] = "#A6A6A6" + let rv["grey65"] = "#A6A6A6" + let rv["gray66"] = "#A8A8A8" + let rv["grey66"] = "#A8A8A8" + let rv["gray67"] = "#ABABAB" + let rv["grey67"] = "#ABABAB" + let rv["gray68"] = "#ADADAD" + let rv["grey68"] = "#ADADAD" + let rv["gray69"] = "#B0B0B0" + let rv["grey69"] = "#B0B0B0" + let rv["gray70"] = "#B3B3B3" + let rv["grey70"] = "#B3B3B3" + let rv["gray71"] = "#B5B5B5" + let rv["grey71"] = "#B5B5B5" + let rv["gray72"] = "#B8B8B8" + let rv["grey72"] = "#B8B8B8" + let rv["gray73"] = "#BABABA" + let rv["grey73"] = "#BABABA" + let rv["gray74"] = "#BDBDBD" + let rv["grey74"] = "#BDBDBD" + let rv["gray75"] = "#BFBFBF" + let rv["grey75"] = "#BFBFBF" + let rv["gray76"] = "#C2C2C2" + let rv["grey76"] = "#C2C2C2" + let rv["gray77"] = "#C4C4C4" + let rv["grey77"] = "#C4C4C4" + let rv["gray78"] = "#C7C7C7" + let rv["grey78"] = "#C7C7C7" + let rv["gray79"] = "#C9C9C9" + let rv["grey79"] = "#C9C9C9" + let rv["gray80"] = "#CCCCCC" + let rv["grey80"] = "#CCCCCC" + let rv["gray81"] = "#CFCFCF" + let rv["grey81"] = "#CFCFCF" + let rv["gray82"] = "#D1D1D1" + let rv["grey82"] = "#D1D1D1" + let rv["gray83"] = "#D4D4D4" + let rv["grey83"] = "#D4D4D4" + let rv["gray84"] = "#D6D6D6" + let rv["grey84"] = "#D6D6D6" + let rv["gray85"] = "#D9D9D9" + let rv["grey85"] = "#D9D9D9" + let rv["gray86"] = "#DBDBDB" + let rv["grey86"] = "#DBDBDB" + let rv["gray87"] = "#DEDEDE" + let rv["grey87"] = "#DEDEDE" + let rv["gray88"] = "#E0E0E0" + let rv["grey88"] = "#E0E0E0" + let rv["gray89"] = "#E3E3E3" + let rv["grey89"] = "#E3E3E3" + let rv["gray90"] = "#E5E5E5" + let rv["grey90"] = "#E5E5E5" + let rv["gray91"] = "#E8E8E8" + let rv["grey91"] = "#E8E8E8" + let rv["gray92"] = "#EBEBEB" + let rv["grey92"] = "#EBEBEB" + let rv["gray93"] = "#EDEDED" + let rv["grey93"] = "#EDEDED" + let rv["gray94"] = "#F0F0F0" + let rv["grey94"] = "#F0F0F0" + let rv["gray95"] = "#F2F2F2" + let rv["grey95"] = "#F2F2F2" + let rv["gray96"] = "#F5F5F5" + let rv["grey96"] = "#F5F5F5" + let rv["gray97"] = "#F7F7F7" + let rv["grey97"] = "#F7F7F7" + let rv["gray98"] = "#FAFAFA" + let rv["grey98"] = "#FAFAFA" + let rv["gray99"] = "#FCFCFC" + let rv["grey99"] = "#FCFCFC" + let rv["gray100"] = "#FFFFFF" + let rv["grey100"] = "#FFFFFF" + let rv["dark_grey"] = "#A9A9A9" + let rv["darkgrey"] = "#A9A9A9" + let rv["dark_gray"] = "#A9A9A9" + let rv["darkgray"] = "#A9A9A9" + let rv["dark_blue"] = "#00008B" + let rv["darkblue"] = "#00008B" + let rv["dark_cyan"] = "#008B8B" + let rv["darkcyan"] = "#008B8B" + let rv["dark_magenta"] = "#8B008B" + let rv["darkmagenta"] = "#8B008B" + let rv["dark_red"] = "#8B0000" + let rv["darkred"] = "#8B0000" + let rv["light_green"] = "#90EE90" + let rv["lightgreen"] = "#90EE90" + let rv["darkyellow"] = "#BBBB00" + let rv["dark_yellow"] = "#BBBB00" + let rv["lightred"] = "#FFA0A0" + let rv["light_red"] = "#FFA0A0" + let rv["lightmagenta"] = "#F0A0F0" + let rv["light_magenta"] = "#F0A0F0" + + return rv +endfunction diff --git a/colorschemedegrade/plugin/ColorschemeDegrade.vim b/colorschemedegrade/plugin/ColorschemeDegrade.vim new file mode 100755 index 0000000..d033ea3 --- /dev/null +++ b/colorschemedegrade/plugin/ColorschemeDegrade.vim @@ -0,0 +1,416 @@ +" ColorschemeDegrade: Degrade gvim colorschemes to be suitable for a terminal +" Maintainer: Matthew Wozniski (mjw@drexel.edu) +" Date: Sun, 21 Oct 2007 21:04:33 -0400 +" Version: 0.2 +" History: TODO(History Link) +" Installation: Drop this script into ~/.vim/plugin. + +" Whenever you change colorschemes using the :colorscheme command, this script +" will be executed. If you're running in 256 color terminal or an 88 color +" terminal, as reported by the command ":echo &t_Co" it will take the colors +" that the scheme specified for use in the gui and use an approximation +" algorithm to try to gracefully degrade them to the closest color available. +" If you are running in a gui or if t_Co is reported as less than 88 colors, +" no changes are made. + +" Abort if running in vi-compatible mode or the user doesn't want or need us. +if &cp || has("gui_running") || ! has("gui") || exists('g:colorschemedegrade_loaded') + if &cp && &verbose + echomsg "Not loading ColorschemeDegrade in compatible mode." + endif + if has('gui_running') && &verbose + echomsg "Not loading ColorschemeDegrade in gui mode." + endif + if ! has('gui') && &verbose + echomsg "Unfortunately, ColorschemeDegrade needs gui support. Not loading." + endif + finish +endif + +" A local copy of rgb.txt must be included, since I can't count on it being in +" a standard location. But, we won't load it unless we need it. +let s:rgb = {} + +let g:colorschemedegrade_loaded = 1 + +let s:savecpo = &cpo +set cpo&vim + +" Script-local variables {{{1 + +" Script-local variables defining the rgb vals on a 256-color cube {{{2 + +" Every possible 256-color cube color is made up of 3 rgb values, all out of +" this table. +let s:vals_greys_256 = [ 0, 8, 18, 28, 38, + \ 48, 58, 68, 78, 88, + \ 95, 98, 108, 118, 128, + \ 135, 138, 148, 158, 168, + \ 175, 178, 188, 198, 208, + \ 215, 218, 228, 238, 255 ] + +" Many of those colors can only be used for a grey (r == g == b). This subset +" can be mix-and-matched. +let s:vals_color_256 = [ 0, 95, 135, 175, 215, 255 ] + +" This table holds the midpoints between each of the possible grey values, as +" well as one extra element higher than all, to be used in the approximation +" algorithm. +let s:mids_greys_256 = [ 4, 13, 23, 33, 43, + \ 53, 63, 73, 83, 91, + \ 96, 103, 113, 123, 131, + \ 136, 143, 153, 163, 171, + \ 176, 183, 193, 203, 211, + \ 216, 223, 233, 246, 256 ] + +" This table is the same, only for the non-grey midpoints. +let s:mids_color_256 = [ 48, 115, 155, 195, 235, 256 ] + +" Script-local variables defining the rgb vals on a 88-color cube {{{2 + +" Every possible 88-color cube color is made up of 3 rgb values, all out of +" this table. +let s:vals_greys_88 = [ 0, 46, 92, 115, 139, 162, + \ 185, 205, 208, 231, 255 ] + +" Many of those colors can only be used for a grey (r == g == b). This subset +" can be mix-and-matched. +let s:vals_color_88 = [ 0, 139, 205, 255 ] + +" This table holds the midpoints between each of the possible grey values, as +" well as one extra element higher than all, to be used in the approximation +" algorithm. +let s:mids_greys_88 = [ 23, 69, 103, 127, 150, 173, + \ 195, 206, 219, 243, 256 ] + +" This table is the same, only for the non-grey midpoints. +let s:mids_color_88 = [ 69, 172, 230, 256 ] + +" Function definitions {{{1 + +" Given 3 hex strings rr, gg, bb, return the closest color cube number. +function! s:FindClosestCode(h1,h2,h3) + let d1 = str2nr(a:h1, 16) + let d2 = str2nr(a:h2, 16) + let d3 = str2nr(a:h3, 16) + + let r = s:FindClosest(d1, s:vals_greys_{&t_Co}, s:mids_greys_{&t_Co}) + let g = s:FindClosest(d2, s:vals_greys_{&t_Co}, s:mids_greys_{&t_Co}) + let b = s:FindClosest(d3, s:vals_greys_{&t_Co}, s:mids_greys_{&t_Co}) + + if(r == g && g == b) + return s:GreyComponentTo{&t_Co}Cube(r) + else + let r = s:FindClosest(d1, s:vals_color_{&t_Co}, s:mids_color_{&t_Co}) + let g = s:FindClosest(d2, s:vals_color_{&t_Co}, s:mids_color_{&t_Co}) + let b = s:FindClosest(d3, s:vals_color_{&t_Co}, s:mids_color_{&t_Co}) + return s:RGBComponentsTo{&t_Co}Cube(r, g, b) + endif +endfunction + +" Given a number, an array of elements, and an array of midpts, find the index +" of the least midpt that the number is strictly less than, and return the +" corresponding element. +function! s:FindClosest(num, elems, midpts) + for i in range(len(a:elems)) + if ( a:num < a:midpts[i] ) + return a:elems[i] + endif + endfor +endfunction + +" Expects a decimal value 'x' between 0 and 255, inclusive +" Returns a 256-color colorcube number for the color at RGB=x,x,x +function! s:GreyComponentTo256Cube(num) + if(a:num % 10 == 8) + return 232 + (a:num - 8) / 10 + else + " Not in the greyscale ramp, so we can use our normal processing + return s:RGBComponentsTo256Cube(a:num, a:num, a:num) + endif +endfunction + +" Expects a decimal value 'x' between 0 and 255, inclusive +" Returns an 88-color colorcube number for the color at RGB=x,x,x +function! s:GreyComponentTo88Cube(num) + if a:num == 46 + return 80 + elseif a:num == 92 + return 81 + elseif a:num == 115 + return 82 + elseif a:num == 139 + return 83 + elseif a:num == 162 + return 84 + elseif a:num == 185 + return 85 + elseif a:num == 208 + return 86 + elseif a:num == 231 + return 87 + else + " Not in the greyscale ramp, so we can use our normal processing + return s:RGBComponentsTo88Cube(a:num, a:num, a:num) + endif +endfunction + +" Expects 3 decimal values 'r', 'g', and 'b', each between 0 and 255 inclusive. +" Returns a 256-color colorcube number for the color at RGB=r,g,b. +" Will not use the greyscale ramp. +function! s:RGBComponentsTo256Cube(r,g,b) + let rc = index(s:vals_color_256, a:r) + let gc = index(s:vals_color_256, a:g) + let bc = index(s:vals_color_256, a:b) + + return (rc * 36 + gc * 6 + bc + 16) +endfunction + +" Expects 3 decimal values 'r', 'g', and 'b', each between 0 and 255 inclusive. +" Returns a 88-color colorcube number for the color at RGB=r,g,b. +" Will not use the greyscale ramp. +function! s:RGBComponentsTo88Cube(r,g,b) + let rc = index(s:vals_color_88, a:r) + let gc = index(s:vals_color_88, a:g) + let bc = index(s:vals_color_88, a:b) + + return (rc * 16 + gc * 4 + bc + 16) +endfunction + +" Check if the provided value is found in "g:colorschemedegrade_ignore", which +" may either be a list or a comma or space separated string. If the variable +" "g:colorschemedegrade_ignore" is not present, the default is "bold italic" +function! s:ignoring(attr) + if !exists("g:colorschemedegrade_ignore") + let ignore = [ 'bold', 'italic' ] + elseif type(g:colorschemedegrade_ignore) == type("") + let ignore = split(g:colorschemedegrade_ignore, '[ ,]') + elseif type(g:colorschemedegrade_ignore) == type([]) + let ignore = g:colorschemedegrade_ignore + else + return 0 + endif + + return index(ignore, a:attr) != -1 +endfunction + +" Sets some settings, calls s:ColorschemeDegradeImpl to handle actually +" degrading the colorscheme, then restores the settings. This wrapper +" should make sure that we don't accidentally recurse, and that settings are +" restored properly even if something throws. +function! s:ColorschemeDegrade() + if g:colors_name =~ ".*-rgb" + return + endif + let saveei = &ei + set ei+=ColorScheme + + if exists("g:colors_name") + let colors_name = g:colors_name + unlet g:colors_name + endif + + let savelz = &lz + set lz + + let rv = -1 + + try + let rv = s:ColorschemeDegradeImpl() + catch + let ex = v:exception + endtry + + let &lz = savelz + + if exists("colors_name") + let g:colors_name = colors_name + endif + + let &ei = saveei + + if exists("ex") + echoerr 'ColorschemeDegrade failed: ' . substitute(ex, '.\{-}:', '', '') + endif + + return rv +endfunction + +" For every highlight group, sets the cterm values to the best approximation +" of the gui values possible given the value of &t_Co. +function! s:ColorschemeDegradeImpl() + if has('gui_running') || (&t_Co != 256 && &t_Co != 88) + return + endif + + let g:highlights = "" + redir => g:highlights + " Normal must be set 1st for ctermfg=bg, etc, and resetting it doesn't hurt + silent highlight Normal + silent highlight + redir END + + let hilines = split(g:highlights, '\n') + + " hilines[0] is Normal. If that doesn't use gui colors, we should probably + " just give up. That way we don't muck up an already 256/88 color scheme. + if hilines[0] !~ 'gui[fb]g' + if &verbose + echomsg "Not degrading colorscheme; doesn't set Normal group gui colors" + endif + return + endif + + call filter(hilines, 'v:val !~ "links to" && v:val !~ "cleared"') + + let i = 0 + let end = len(hilines) + + while i < end + let line = hilines[i] + let i += 1 + while i < end && hilines[i] !~ '\' + let line .= hilines[i] + let i += 1 + endwhile + let line = substitute(line, '\', '', '') + let line = substitute(line, '\8[35];' + \ && exists('g:colorschemedegrade_changecube') + \ && g:colorschemedegrade_changecube + if !exists("s:lastcubepos") + let s:lastcubepos="15" + let s:colors = {} + endif + + let tisave = &t_ti + let tesave = &t_te + set t_ti= t_te= + + if has_key(s:colors, tolower(val)) + let nr = s:colors[tolower(val)] + else + let s:lastcubepos = s:lastcubepos + 1 + + if s:lastcubepos >= &t_Co + let s:lastcubepos = 16 + endif + + let nr = s:lastcubepos + let s:colors[tolower(val)] = nr + + if $STY == "" + exe 'sil !echo -n -e "\\033]4;' . nr . ';\' . val . '\\a"' + else + exe 'sil !echo -n -e "\\033P\\033]4;' . nr . ';\' . val . '\\a\\033\\\\"' + endif + endif + + let &t_ti = tisave + let &t_te = tesave + exe 'hi' higrp var.'='.nr + else + let r = val[1] . val[2] + let g = val[3] . val[4] + let b = val[5] . val[6] + exe 'hi ' . higrp . ' ' . var . '=' . s:FindClosestCode(r, g, b) + endif + endif + endfor + endwhile + if exists("s:lastcubepos") + unlet s:lastcubepos + endif +endfunction + +augroup ColorSchemeDegrade + au! + au ColorScheme * call s:ColorschemeDegrade() +augroup END + +autocmd TermResponse * if exists("g:colors_name") + \ | exe "colorscheme" g:colors_name + \ | call s:ColorschemeDegrade() + \ | endif + +if exists("g:colors_name") + " Don't do anything unless :colorscheme has already been called + call s:ColorschemeDegrade() +endif + +let &cpo = s:savecpo +unlet s:savecpo + +" vim:set sw=2 sts=2 fdm=marker: diff --git a/exheres-syntax-20160115/.mailmap b/exheres-syntax-20160115/.mailmap new file mode 100644 index 0000000..e824bff --- /dev/null +++ b/exheres-syntax-20160115/.mailmap @@ -0,0 +1 @@ +Saleem Abdulrasool compnerd diff --git a/exheres-syntax-20160115/README b/exheres-syntax-20160115/README new file mode 100644 index 0000000..745a767 --- /dev/null +++ b/exheres-syntax-20160115/README @@ -0,0 +1,40 @@ +COPYRIGHT +========= + +Copyright (c) 2008 Alexander Færøy +You may redistribute this package under the same terms as Vim itself. + +Based in part upon gentoo-syntax, which is: +Copyright (c) 2004-2005 Ciaran McCreesh, Aaron Walker + +INSTALL +======= + +If you are on an Exherbo-based system: + + cave resolve app-vim/exheres-syntax + +If you are on a Gentoo-based system: + + cave resolve app-vim/exheres-syntax + + or + + emerge app-vim/exheres-syntax + +Other: + + Just extract the tarball to your ~/.vim/ directory. + +BUGS +==== + +If you discover any bugs in exheres-syntax, please file a bug under the +"Hosted Projects" -> "Exheres-syntax" component on https://bugs.exherbo.org/ + +AUTHORS +======= + + * Alexander Færøy + * Ingmar Vanhassel + * Saleem Abdulrasool diff --git a/exheres-syntax-20160115/doc/exheres-syntax.txt b/exheres-syntax-20160115/doc/exheres-syntax.txt new file mode 100644 index 0000000..8368c36 --- /dev/null +++ b/exheres-syntax-20160115/doc/exheres-syntax.txt @@ -0,0 +1,13 @@ +*exheres-syntax.txt* Exheres Syntax Plug-in + +Author: Alexander Færøy + +Copyright: (c) 2007 by Alexander Færøy + +============================================================================== + +Please read http://www.exherbo.org/docs/exheres-for-smarties.html for +information about the exheres package format. + +============================================================================== +vim:tw=78:ts=4:ft=help:et diff --git a/exheres-syntax-20160115/doc/tags b/exheres-syntax-20160115/doc/tags new file mode 100644 index 0000000..7fcfb2a --- /dev/null +++ b/exheres-syntax-20160115/doc/tags @@ -0,0 +1 @@ +exheres-syntax.txt exheres-syntax.txt /*exheres-syntax.txt* diff --git a/exheres-syntax-20160115/ftdetect/exheres.vim b/exheres-syntax-20160115/ftdetect/exheres.vim new file mode 100644 index 0000000..2c12855 --- /dev/null +++ b/exheres-syntax-20160115/ftdetect/exheres.vim @@ -0,0 +1,14 @@ +" Vim filetype detection file +" Language: Exheres +" Author: Alexander Færøy +" Copyright: Copyright (c) 2008 Alexander Færøy +" License: You may redistribute this under the same terms as Vim itself + +if &compatible || v:version < 603 + finish +endif + +au BufNewFile,BufRead *.exlib set filetype=exlib +au BufNewFile,BufRead *.exheres-0 set filetype=exheres-0 + +" vim: set et ts=4 : diff --git a/exheres-syntax-20160115/ftplugin/exheres-0.vim b/exheres-syntax-20160115/ftplugin/exheres-0.vim new file mode 100644 index 0000000..c595e2c --- /dev/null +++ b/exheres-syntax-20160115/ftplugin/exheres-0.vim @@ -0,0 +1,19 @@ +" Vim filetype plugin file +" Language: Exheres-0 +" Author: Alexander Færøy +" Copyright: Copyright (c) 2008, 2009 Alexander Færøy +" License: You may redistribute this under the same terms as Vim itself + +if &compatible || v:version < 603 + finish +endif + +runtime! ftplugin/sh.vim + +setlocal tabstop=4 +setlocal shiftwidth=4 +setlocal expandtab +setlocal fileencoding=utf-8 +setlocal textwidth=100 + +" vim: set et ts=4 : diff --git a/exheres-syntax-20160115/ftplugin/exlib.vim b/exheres-syntax-20160115/ftplugin/exlib.vim new file mode 100644 index 0000000..7a47383 --- /dev/null +++ b/exheres-syntax-20160115/ftplugin/exlib.vim @@ -0,0 +1,19 @@ +" Vim filetype plugin file +" Language: Exlib +" Author: Alexander Færøy +" Copyright: Copyright (c) 2008, 2009 Alexander Færøy +" License: You may redistribute this under the same terms as Vim itself + +if &compatible || v:version < 603 + finish +endif + +runtime! ftplugin/sh.vim + +setlocal tabstop=4 +setlocal shiftwidth=4 +setlocal expandtab +setlocal fileencoding=utf-8 +setlocal textwidth=100 + +" vim: set et ts=4 : diff --git a/exheres-syntax-20160115/indent/exheres-0.vim b/exheres-syntax-20160115/indent/exheres-0.vim new file mode 100644 index 0000000..c4a5f3d --- /dev/null +++ b/exheres-syntax-20160115/indent/exheres-0.vim @@ -0,0 +1,18 @@ +" Vim indent file +" Language: Exheres-0 +" Author: Alexander Færøy +" Copyright: Copyright (c) 2008 Alexander Færøy +" License: You may redistribute this under the same terms as Vim itself + +if &compatible || v:version < 603 + finish +endif + +if exists("b:did_indent") + finish +endif + +runtime! indent/sh.vim +let b:did_indent = 1 + +" vim: set et ts=4 : diff --git a/exheres-syntax-20160115/indent/exlib.vim b/exheres-syntax-20160115/indent/exlib.vim new file mode 100644 index 0000000..a4f0851 --- /dev/null +++ b/exheres-syntax-20160115/indent/exlib.vim @@ -0,0 +1,18 @@ +" Vim indent file +" Language: Exlib +" Author: Alexander Færøy +" Copyright: Copyright (c) 2008 Alexander Færøy +" License: You may redistribute this under the same terms as Vim itself + +if &compatible || v:version < 603 + finish +endif + +if exists("b:did_indent") + finish +endif + +runtime! indent/sh.vim +let b:did_indent = 1 + +" vim: set et ts=4 : diff --git a/exheres-syntax-20160115/plugin/new-common-metadata.vim b/exheres-syntax-20160115/plugin/new-common-metadata.vim new file mode 100644 index 0000000..8da69f4 --- /dev/null +++ b/exheres-syntax-20160115/plugin/new-common-metadata.vim @@ -0,0 +1,89 @@ +" Vim plugin +" Language: Create new common-metadata.exlib +" Author: Saleem Abdulrasool +" Copyright: Copyright (c) 2008-2012 Saleem Abdulrasool +" License: You may redistribute this under the same terms as Vim itself + +if &compatible || v:version < 603 + finish +endif + +fun! GenerateCommonMetadataExlib() + let l:pastebackup = &paste + set nopaste + + if exists("*strftime") + let l:year = strftime("%Y") + else + let l:year = "" + endif + + put = '# Copyright ' . l:year . ' ' . g:exheres_author_name + put = '# Distributed under the terms of the GNU General Public License v2' + put = '' + put = 'SUMMARY=\"\"' + put = 'DESCRIPTION=\"' + put = 'If DESCRIPTION is set it must not be an empty string.' + put = '\"' + put = 'HOMEPAGE=\"\"' + put = 'DOWNLOADS=\"\"' + put = '' + put = 'LICENCES=\"\"' + put = 'SLOT=\"0\"' + put = 'MYOPTIONS=\"\"' + put = '' + put = 'DEPENDENCIES=\"' + put = ' build:' + put = ' build+run:' + put = '\"' + put = '' + put = 'BUGS_TO=\"\"' + put = '' + put = 'REMOTE_IDS=\"\"' + put = '' + put = 'UPSTREAM_CHANGELOG=\"\"' + put = 'UPSTREAM_DOCUMENTATION=\"\"' + put = 'UPSTREAM_RELEASE_NOTES=\"\"' + put = '' + + 0 + /^SUMMARY=/ + exec "normal 2f\"" + nohls + + if pastebackup != 0 + set paste + endif +endfun + +com! -nargs=0 NewCommonMetadataExlib call GenerateCommonMetadataExlib + +if !exists("g:common_metadata_create_on_empty") + let g:common_metadata_create_on_empty = 1 +endif + +if v:progname =~ "vimdiff" + let g:common_metadata_create_on_empty = 0 +endif + +if !exists("g:exheres_author_name") + let g:exheres_author_name = "" +endif + +augroup NewCommonMetadataExlib + au! + " common-metadata.exlib + autocmd BufNewFile common-metadata.exlib + \ if g:common_metadata_create_on_empty | + \ call GenerateCommonMetadataExlib() | + \ endif + " ${PN}.exlib + autocmd BufNewFile *.exlib + \ if expand('%:p:t') == expand('%:p:h:t') . '.exlib' | + \ if g:common_metadata_create_on_empty | + \ call GenerateCommonMetadataExlib() | + \ endif | + \ endif +augroup END + +" vim: set et ts=4 sw=4 : diff --git a/exheres-syntax-20160115/plugin/new-exheres-0.vim b/exheres-syntax-20160115/plugin/new-exheres-0.vim new file mode 100644 index 0000000..8ecf567 --- /dev/null +++ b/exheres-syntax-20160115/plugin/new-exheres-0.vim @@ -0,0 +1,80 @@ +" Vim plugin +" Language: Create new exheres-0 package. +" Author: Alexander Færøy +" Copyright: Copyright (c) 2007 Alexander Færøy +" License: You may redistribute this under the same terms as Vim itself + +if &compatible || v:version < 603 + finish +endif + +fun! GenerateExheresZeroPackage() + let l:pastebackup = &paste + set nopaste + + if exists("*strftime") + let l:year = strftime("%Y") + else + let l:year = "" + endif + + put! ='# Copyright ' . l:year . ' ' . g:exheres_author_name + put ='# Distributed under the terms of the GNU General Public License v2' + put ='' + put ='SUMMARY=\"\"' + put ='DESCRIPTION=\"' + put ='If DESCRIPTION is set it must not be an empty string.' + put ='\"' + put ='HOMEPAGE=\"\"' + put ='DOWNLOADS=\"\"' + put ='' + put ='LICENCES=\"\"' + put ='SLOT=\"0\"' + put ='PLATFORMS=\"\"' + put ='MYOPTIONS=\"\"' + put ='' + put ='DEPENDENCIES=\"' + put =' build:' + put =' build+run:' + put ='\"' + put ='' + put ='BUGS_TO=\"\"' + put ='' + put ='DEFAULT_SRC_CONFIGURE_PARAMS=( )' + put ='DEFAULT_SRC_CONFIGURE_OPTION_WITHS=( )' + put ='DEFAULT_SRC_CONFIGURE_OPTION_ENABLES=( )' + put ='' + + 0 + /^SUMMARY=/ + exec "normal 2f\"" + nohls + + if pastebackup == 0 + set nopaste + endif +endfun + +com! -nargs=0 NewExheresZeroPackage call GenerateExheresZeroPackage() + +if !exists("g:package_create_on_empty") + let g:package_create_on_empty = 1 +endif + +if v:progname =~ "vimdiff" + let g:package_create_on_empty = 0 +endif + +if !exists("g:exheres_author_name") + let g:exheres_author_name = "" +endif + +augroup NewExheresZeroPackage + au! + autocmd BufNewFile *.exheres-0 + \ if g:package_create_on_empty | + \ call GenerateExheresZeroPackage() | + \ endif +augroup END + +" vim: set et ts=4 : diff --git a/exheres-syntax-20160115/syntax/exheres-0.vim b/exheres-syntax-20160115/syntax/exheres-0.vim new file mode 100644 index 0000000..7e85a9a --- /dev/null +++ b/exheres-syntax-20160115/syntax/exheres-0.vim @@ -0,0 +1,18 @@ +" Vim syntaxfile +" Language: Exheres-0 +" Author: Alexander Færøy +" Copyright: Copyright (c) 2008 Alexander Færøy +" License: You may redistribute this under the same terms as Vim itself + +if &compatible || v:version < 603 + finish +endif + +syn keyword ExheresZeroError export_exlib_phases +syn keyword ExheresZeroError myexparam exparam + +runtime syntax/exheres-common.vim + +let b:current_syntax = "exheres-0" + +" vim: set et ts=4 : diff --git a/exheres-syntax-20160115/syntax/exheres-common.vim b/exheres-syntax-20160115/syntax/exheres-common.vim new file mode 100644 index 0000000..9e0a546 --- /dev/null +++ b/exheres-syntax-20160115/syntax/exheres-common.vim @@ -0,0 +1,142 @@ +" Vim syntaxfile +" Language: Common code for exheres syntax +" Author: Alexander Færøy +" Copyright: Copyright (c) 2008 Alexander Færøy +" License: You may redistribute this under the same terms as Vim itself + +if &compatible || v:version < 603 + finish +endif + +let is_bash = 1 +runtime! syntax/sh.vim +unlet b:current_syntax + +syn region ExheresHeader contained start=/^#/ end=/$/ contains=ExheresCopyrightHeader +syn region ExheresHeaderBlock start=/\%^\(#\)\@=/ end=/^$/ contains=ExheresHeader + +" Unfilled copyright notice +syn region ExheresCopyrightHeader contained start=/^#\s*Copyright/ end=/$/ contains=ExheresCopyrightError +syn match ExheresCopyrightError contained /<\(name\|year\)>/ + +" Phases +syn keyword ExheresZeroFunctions pkg_pretend pkg_setup pkg_preinst pkg_postinst pkg_prerm pkg_postrm pkg_nofetch pkg_config pkg_info +syn keyword ExheresZeroFunctions src_fetch_extra src_unpack src_prepare src_configure src_compile src_test src_test_expensive src_install + +" Default phases +syn keyword ExheresZeroFunctions default +syn keyword ExheresZeroFunctions default_pkg_pretend default_pkg_setup default_pkg_preinst default_pkg_postinst default_pkg_prerm default_pkg_postrm default_pkg_nofetch default_pkg_config default_pkg_info +syn keyword ExheresZeroFunctions default_src_fetch_extra default_src_unpack default_src_prepare default_src_configure default_src_compile default_src_test default_src_test_expensive default_src_install + +" Multibuild phases +syn keyword ExheresZeroFunctions compile_one_multibuild compile_prepare_one_multibuild configure_one_multibuild configure_prepare_one_multibuild install_one_multibuild install_prepare_one_multibuild prepare_one_multibuild prepare_prepare_one_multibuild test_expensive_one_multibuild test_expensive_prepare_one_multibuild test_one_multibuild test_prepare_one_multibuild unpack_one_multibuild unpack_prepare_one_multibuild + +" die_functions.bash +syn keyword ExheresZeroCoreKeyword die assert nonfatal + +" echo_functions.bash +syn keyword ExheresZeroCoreKeyword einfo elog ewarn eerror ebegin eend +syn keyword ExheresZeroCoreKeyword einfon ewend + +" install_functions.bash +syn keyword ExheresZeroCoreKeyword keepdir into insinto exeinto docinto insopts diropts exeopts libopts + +" kernel_functions.bash +syn keyword ExheresZeroCoreKeyword KV_major KV_minor KV_micro KV_to_int get_KV + +" sydbox.bash +syn keyword ExheresZeroCoreKeyword esandbox +syn match ExheresZeroError "sydboxcheck" +syn match ExheresZeroError "sydboxcmd" +syn match ExheresZeroError "addread" +syn match ExheresZeroError "adddeny" +syn match ExheresZeroError "addpredict" + +" exheres-0/build_functions.bash +syn keyword ExheresZeroCoreKeyword expatch econf emagicdocs edo exhost + +" exheres-0/conditional_functions.bash +syn keyword ExheresZeroCoreKeyword option_with option_enable +syn keyword ExheresZeroError use_with use_enable + +" exheres-0/exlib_functions.bash +syn keyword ExheresZeroRequire require + +" exheres-0/list_functions.bash +syn keyword ExheresZeroError einstall use usev useq +syn keyword ExheresZeroCoreKeyword optionfmt option optionv optionq has hasv hasq +syn keyword ExheresZeroCoreKeyword expecting_tests + +" exheres-0/portage_stubs.bash +syn keyword ExheresZeroCoreKeyword has_version best_version +syn keyword ExheresZeroError portageq vdb_path check_KV debug-print debug-print-function debug-print-section + +" utils/ +syn keyword ExheresZeroCoreKeyword dobin doconfd dodir doenvd doexe doinfo +syn keyword ExheresZeroCoreKeyword doinitd doins dolib dolib.a dolib.so doman domo dosym +syn keyword ExheresZeroCoreKeyword newbin newconfd newdoc newenvd newexe newinitd newins newlib.a newlib.so +syn keyword ExheresZeroCoreKeyword newman unpack +syn keyword ExheresZeroCoreKeyword herebin hereconfd hereenvd hereinitd hereins +syn keyword ExheresZeroError dosbin fperms fowners newsbin heresbin + +" utils/exheres-0/ +syn keyword ExheresZeroCoreKeyword emake dodoc +syn keyword ExheresZeroError dohard donewins dosed doset dohtml +syn keyword ExheresZeroError prepall prepalldocs prepallinfo prepallman prepallstrip prepdocs prepinfo prepman prepstrip +syn match ExheresZeroError /ecompress\w*/ + +" autotools.exlib +syn keyword ExheresZeroCoreKeyword eautoreconf eaclocal eautoconf eautoheader eautomake + +" Legacy ebuild stuff +syn match ExheresZeroError /^SOURCES/ +syn match ExheresZeroError /^DISTDIR/ +syn match ExheresZeroError /^FILESDIR/ +syn match ExheresZeroError /^PORTDIR/ +syn match ExheresZeroError /^WORKDIR/ +syn match ExheresZeroError /^KEYWORDS/ +syn match ExheresZeroError /^PROVIDE/ +syn match ExheresZeroError /^IUSE/ +syn match ExheresZeroError /^LICENSE/ +syn match ExheresZeroError /^LICENCE[^S]/ +syn match ExheresZeroError /^SRC_URI/ +syn match ExheresZeroError /^EAPI/ +syn match ExheresZeroError /AA/ +syn match ExheresZeroError /ARCH/ +syn match ExheresZeroError /KV/ +syn match ExheresZeroError /^\(A\|D\|S\|T\)=/ +syn match ExheresZeroErrorC /\${\(P\|PF\|A\|D\|S\|T\)}/ +syn match ExheresZeroErrorC /\${\(DISTDIR\|FILESDIR\|PORTDIR\|SOURCES\|WORKDIR\)}/ + +" Read-only variables +syn match ExheresZeroError /^\(PNV\|PN\|PV\|PR\|PVR\|PNVR\|ARCHIVES\)=/ + +" Bad variable assignments +syn match ExheresZeroError /^SLOT\s*=\s*\(""\|''\|$\)/ +syn match ExheresZeroError ~^WORK="\?\${\?WORKBASE}\?/\${\?PNV}\?"\?\s*$~ +syn match ExheresZeroErrorC /\${PN}-\${PV}/ + +" Highlight tabs and trailing whitespace as errors +syn match ExheresZeroError " " +syn match ExheresZeroError "\s\+$" + +" Highlight last line if it's not empty +syn match ExheresZeroError /^.\+\%$/ + +" Highlight it +syn cluster ExheresZeroContents contains=ExheresZeroCoreKeyword,ExheresZeroFunctions,ExheresZeroRequire +syn cluster ExheresZeroContents add=ExheresZeroError,ExheresZeroErrorC + +syn cluster shCommandSubList add=@ExheresZeroContents +syn cluster shDblQuoteList add=ExheresZeroErrorC + +hi def link ExheresZeroCoreKeyword Keyword +hi def link ExheresZeroFunctions Special +hi def link ExheresZeroRequire Include +hi def link ExheresZeroError Error +hi def link ExheresZeroErrorC Error +hi def link ExheresHeader Comment +hi def link ExheresCopyrightHeader Comment +hi def link ExheresCopyrightError Error + +" vim: set et ts=4 : diff --git a/exheres-syntax-20160115/syntax/exlib.vim b/exheres-syntax-20160115/syntax/exlib.vim new file mode 100644 index 0000000..e4f0de6 --- /dev/null +++ b/exheres-syntax-20160115/syntax/exlib.vim @@ -0,0 +1,18 @@ +" Vim syntaxfile +" Language: Exheres-0 +" Author: Alexander Færøy +" Copyright: Copyright (c) 2008 Alexander Færøy +" License: You may redistribute this under the same terms as Vim itself + +if &compatible || v:version < 603 + finish +endif + +syn keyword ExheresZeroRequire export_exlib_phases +syn keyword ExheresZeroRequire myexparam exparam + +runtime syntax/exheres-common.vim + +let b:current_syntax = "exlib" + +" vim: set et ts=4 : diff --git a/fontzoom/doc/fontzoom.txt b/fontzoom/doc/fontzoom.txt new file mode 100755 index 0000000..f3d1749 --- /dev/null +++ b/fontzoom/doc/fontzoom.txt @@ -0,0 +1,105 @@ +*fontzoom.txt* The fontsize controller in gVim. + +Version: 0.1.1 +Modified: thinca +License: Creative Commons Attribution 2.1 Japan License + + +============================================================================== +CONTENTS *fontzoom-contents* + +INTRODUCTION |fontzoom-introduction| +INTERFACE |fontzoom-interface| + KEY MAPPINGS |fontzoom-key-mappings| + COMMANDS |fontzoom-commands| +SETTINGS |fontzoom-settings| +LIMITATION |fontzoom-limitation| +CHANGELOG |fontzoom-changelog| + + +============================================================================== +INTRODUCTION *fontzoom-introduction* + +*fontzoom* is a Vim plugin to control gui fontsize. You can change fontsize +with + and - keys(when default setting). You can also use the [count]. +This plugin remember 'guifont', 'lines', and 'columns' when you change +fontsize first. And tries to keep the size of the window as much as possible. + + + +============================================================================== +INTERFACE *fontzoom-interface* + +------------------------------------------------------------------------------ +KEY MAPPINGS *fontzoom-key-mappings* + +(fontzoom-learger) *(fontzoom-learger)* + Fontsize is made large [count] point. + +(fontzoom-smaller) *(fontzoom-smaller)* + Fontsize is made small [count] point. + + + *g:fontzoom_no_default_key_mappings* +The following key mappings will be also available unless +g:fontzoom_no_default_key_mappings is defined: + +{lhs} {rhs} +-------- ----------------------------- ++ (fontzoom-larger) +- (fontzoom-smaller) + (fontzoom-larger) + (fontzoom-smaller) + +------------------------------------------------------------------------------ +COMMANDS *fontzoom-commands* + +:Fontzoom *:Fontzoom* + Show fontsize in current. + +:Fontzoom +[N] + Fontsize is made large [N] point. + +:Fontzoom -[N] + Fontsize is made small [N] point. + +:Fontzoom [N] + Fontsize is changed into [N] points. + +:Fontzoom! + Fontsize is changed into initial size. All arguments + are ignored. + + + +============================================================================== +SETTINGS *fontzoom-settings* + +g:fontzoom_pattern *g:fontzoom_pattern* + Pick out the Fontsize from font name. The pattern + must match to the Fontsize. |/\zs| and |/\ze| are + convenient. + + + +============================================================================== +LIMITATION *fontzoom-limitation* + +- 'guifont' should contain fontsize that matches to |g:fontzoom_pattern|. + + + +============================================================================== +CHANGELOG *fontzoom-changelog* + +0.1.1 2011-02-24 + - Added default key mappings. + - and . + +0.1.0 2009-12-25 + - Initial version. + + + +============================================================================== +vim:tw=78:fo=tcq2mM:ts=8:ft=help:norl diff --git a/fontzoom/doc/tags b/fontzoom/doc/tags new file mode 100644 index 0000000..af1694c --- /dev/null +++ b/fontzoom/doc/tags @@ -0,0 +1,15 @@ +:Fontzoom fontzoom.txt /*:Fontzoom* +(fontzoom-learger) fontzoom.txt /*(fontzoom-learger)* +(fontzoom-smaller) fontzoom.txt /*(fontzoom-smaller)* +fontzoom fontzoom.txt /*fontzoom* +fontzoom-changelog fontzoom.txt /*fontzoom-changelog* +fontzoom-commands fontzoom.txt /*fontzoom-commands* +fontzoom-contents fontzoom.txt /*fontzoom-contents* +fontzoom-interface fontzoom.txt /*fontzoom-interface* +fontzoom-introduction fontzoom.txt /*fontzoom-introduction* +fontzoom-key-mappings fontzoom.txt /*fontzoom-key-mappings* +fontzoom-limitation fontzoom.txt /*fontzoom-limitation* +fontzoom-settings fontzoom.txt /*fontzoom-settings* +fontzoom.txt fontzoom.txt /*fontzoom.txt* +g:fontzoom_no_default_key_mappings fontzoom.txt /*g:fontzoom_no_default_key_mappings* +g:fontzoom_pattern fontzoom.txt /*g:fontzoom_pattern* diff --git a/fontzoom/plugin/fontzoom.vim b/fontzoom/plugin/fontzoom.vim new file mode 100755 index 0000000..9f2bf38 --- /dev/null +++ b/fontzoom/plugin/fontzoom.vim @@ -0,0 +1,75 @@ +" The fontsize controller in gVim. +" Version: 0.1.1 +" Author : thinca +" License: Creative Commons Attribution 2.1 Japan License +" + +if exists('g:loaded_fontzoom') || !has('gui_running') + finish +endif +let g:loaded_fontzoom = 1 + +let s:save_cpo = &cpo +set cpo&vim + + +function! s:fontzoom(size, reset) + if a:reset + if exists('s:keep') " Reset font size. + let [&guifont, &lines, &columns] = s:keep + unlet! s:keep + endif + elseif a:size == '' + echo matchstr(&guifont, g:fontzoom_pattern) + else + let size = (a:size =~ '^[+-]' ? 'submatch(0)' : '') . a:size + if !exists('s:keep') + let s:keep = [&guifont, &lines, &columns] + endif + let &guifont = join(map(split(&guifont, '\\\@, 0) + +" Key mappings. +nnoremap (fontzoom-larger) +\ :Fontzoom +=v:count1 +nnoremap (fontzoom-smaller) +\ :Fontzoom -=v:count1 +inoremap (fontzoom-larger) +\ :Fontzoom +=v:count1 +inoremap (fontzoom-smaller) +\ :Fontzoom -=v:count1 + +if !exists('g:fontzoom_no_default_key_mappings') +\ || !g:fontzoom_no_default_key_mappings + silent! nmap + (fontzoom-larger) + silent! nmap - (fontzoom-smaller) + silent! nmap (fontzoom-larger) + silent! nmap (fontzoom-smaller) + silent! imap (fontzoom-larger) + silent! imap (fontzoom-smaller) +endif + +let &cpo = s:save_cpo +unlet s:save_cpo diff --git a/fuzzyfinder/autoload/fuf.vim b/fuzzyfinder/autoload/fuf.vim new file mode 100644 index 0000000..78be490 --- /dev/null +++ b/fuzzyfinder/autoload/fuf.vim @@ -0,0 +1,1052 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + + +" returns list of paths. +" An argument for glob() is normalized in order to avoid a bug on Windows. +function fuf#glob(expr) + " Substitutes "\", because on Windows, "**\" doesn't include ".\", + " but "**/" include "./". I don't know why. + return split(glob(substitute(a:expr, '\', '/', 'g')), "\n") +endfunction + +" +function fuf#countModifiedFiles(files, time) + return len(filter(copy(a:files), 'getftime(expand(v:val)) > a:time')) +endfunction + +" +function fuf#getCurrentTagFiles() + return sort(filter(map(tagfiles(), 'fnamemodify(v:val, '':p'')'), 'filereadable(v:val)')) +endfunction + +" +function fuf#mapToSetSerialIndex(in, offset) + for i in range(len(a:in)) + let a:in[i].index = i + a:offset + endfor + return a:in +endfunction + +" +function fuf#updateMruList(mrulist, newItem, maxItem, exclude) + let result = copy(a:mrulist) + let result = filter(result,'v:val.word !=# a:newItem.word') + let result = insert(result, a:newItem) + if len(a:exclude) + let result = filter(result, 'v:val.word !~ a:exclude') + endif + return result[0 : a:maxItem - 1] +endfunction + +" takes suffix number. if no digits, returns -1 +function fuf#suffixNumber(str) + let s = matchstr(a:str, '\d\+$') + return (len(s) ? str2nr(s) : -1) +endfunction + +" "foo/bar/buz/hoge" -> { head: "foo/bar/buz/", tail: "hoge" } +function fuf#splitPath(path) + let head = matchstr(a:path, '^.*[/\\]') + return { + \ 'head' : head, + \ 'tail' : a:path[strlen(head):] + \ } +endfunction + +" "foo/.../bar/...hoge" -> "foo/.../bar/../../hoge" +function fuf#expandTailDotSequenceToParentDir(pattern) + return substitute(a:pattern, '^\(.*[/\\]\)\?\zs\.\(\.\+\)\ze[^/\\]*$', + \ '\=repeat(".." . l9#getPathSeparator(), len(submatch(2)))', '') +endfunction + +" +function fuf#formatPrompt(prompt, partialMatching, otherString) + let indicator = escape((a:partialMatching ? '!' : '') . a:otherString, '\') + return substitute(a:prompt, '[]', indicator, 'g') +endfunction + +" +function fuf#getFileLines(file) + let bufnr = (type(a:file) ==# type(0) ? a:file : bufnr('^' . a:file . '$')) + let lines = getbufline(bufnr, 1, '$') + if !empty(lines) + return lines + endif + return l9#readFile(a:file) +endfunction + +" +function fuf#makePreviewLinesAround(lines, indices, page, maxHeight) + let index = ((empty(a:indices) ? 0 : a:indices[0]) + \ + a:page * a:maxHeight) % len(a:lines) + if empty(a:lines) || a:maxHeight <= 0 + return [] + endif + let beg = max([0, index - a:maxHeight / 2]) + let end = min([beg + a:maxHeight, len(a:lines)]) + let beg = max([0, end - a:maxHeight]) + let lines = [] + for i in range(beg, end - 1) + let mark = (count(a:indices, i) ? '>' : ' ') + call add(lines, printf('%s%4d ', mark, i + 1) . a:lines[i]) + endfor + return lines +endfunction + +" a:file: a path string or a buffer number +function fuf#makePreviewLinesForFile(file, count, maxHeight) + let lines = fuf#getFileLines(a:file) + if empty(lines) + return [] + endif + let bufnr = (type(a:file) ==# type(0) ? a:file : bufnr('^' . a:file . '$')) + if exists('s:bufferCursorPosMap[bufnr]') + let indices = [s:bufferCursorPosMap[bufnr][1] - 1] + else + let indices = [] + endif + return fuf#makePreviewLinesAround( + \ lines, indices, a:count, a:maxHeight) +endfunction + +" +function fuf#echoWarning(msg) + call l9#echoHl('WarningMsg', a:msg, '[fuf] ', 1) +endfunction + +" +function fuf#echoError(msg) + call l9#echoHl('ErrorMsg', a:msg, '[fuf] ', 1) +endfunction + +" +function fuf#openBuffer(bufNr, mode, reuse) + if a:reuse && ((a:mode ==# s:OPEN_TYPE_SPLIT && + \ l9#moveToBufferWindowInCurrentTabpage(a:bufNr)) || + \ (a:mode ==# s:OPEN_TYPE_VSPLIT && + \ l9#moveToBufferWindowInCurrentTabpage(a:bufNr)) || + \ (a:mode ==# s:OPEN_TYPE_TAB && + \ l9#moveToBufferWindowInOtherTabpage(a:bufNr))) + return + endif + execute printf({ + \ s:OPEN_TYPE_CURRENT : '%sbuffer' , + \ s:OPEN_TYPE_SPLIT : '%ssbuffer' , + \ s:OPEN_TYPE_VSPLIT : 'vertical %ssbuffer', + \ s:OPEN_TYPE_TAB : 'tab %ssbuffer' , + \ }[a:mode], a:bufNr) +endfunction + +" +function fuf#openFile(path, mode, reuse) + let bufNr = bufnr('^' . a:path . '$') + if bufNr > -1 + call fuf#openBuffer(bufNr, a:mode, a:reuse) + else + execute { + \ s:OPEN_TYPE_CURRENT : 'edit ' , + \ s:OPEN_TYPE_SPLIT : 'split ' , + \ s:OPEN_TYPE_VSPLIT : 'vsplit ' , + \ s:OPEN_TYPE_TAB : 'tabedit ', + \ }[a:mode] . fnameescape(fnamemodify(a:path, ':~:.')) + endif +endfunction + +" +function fuf#openTag(tag, mode) + execute { + \ s:OPEN_TYPE_CURRENT : 'tjump ' , + \ s:OPEN_TYPE_SPLIT : 'stjump ' , + \ s:OPEN_TYPE_VSPLIT : 'vertical stjump ', + \ s:OPEN_TYPE_TAB : 'tab stjump ' , + \ }[a:mode] . a:tag +endfunction + +" +function fuf#openHelp(tag, mode) + execute { + \ s:OPEN_TYPE_CURRENT : 'help ' , + \ s:OPEN_TYPE_SPLIT : 'help ' , + \ s:OPEN_TYPE_VSPLIT : 'vertical help ', + \ s:OPEN_TYPE_TAB : 'tab help ' , + \ }[a:mode] . a:tag +endfunction + +" +function fuf#prejump(mode) + execute { + \ s:OPEN_TYPE_CURRENT : '' , + \ s:OPEN_TYPE_SPLIT : 'split' , + \ s:OPEN_TYPE_VSPLIT : 'vsplit' , + \ s:OPEN_TYPE_TAB : 'tab split', + \ }[a:mode] +endfunction + +" +function fuf#compareRanks(i1, i2) + if exists('a:i1.ranks') && exists('a:i2.ranks') + for i in range(min([len(a:i1.ranks), len(a:i2.ranks)])) + if a:i1.ranks[i] > a:i2.ranks[i] + return +1 + elseif a:i1.ranks[i] < a:i2.ranks[i] + return -1 + endif + endfor + endif + return 0 +endfunction + +" +function fuf#makePathItem(fname, menu, appendsDirSuffix) + let pathPair = fuf#splitPath(a:fname) + let dirSuffix = (a:appendsDirSuffix && isdirectory(expand(a:fname)) + \ ? l9#getPathSeparator() + \ : '') + return { + \ 'word' : a:fname . dirSuffix, + \ 'wordForPrimaryHead': s:toLowerForIgnoringCase(pathPair.head), + \ 'wordForPrimaryTail': s:toLowerForIgnoringCase(pathPair.tail), + \ 'wordForBoundary' : s:toLowerForIgnoringCase(s:getWordBoundaries(pathPair.tail)), + \ 'wordForRefining' : s:toLowerForIgnoringCase(a:fname . dirSuffix), + \ 'wordForRank' : s:toLowerForIgnoringCase(pathPair.tail), + \ 'menu' : a:menu, + \ } +endfunction + +" +function fuf#makeNonPathItem(word, menu) + let wordL = s:toLowerForIgnoringCase(a:word) + return { + \ 'word' : a:word, + \ 'wordForPrimary' : wordL, + \ 'wordForBoundary': s:toLowerForIgnoringCase(s:getWordBoundaries(a:word)), + \ 'wordForRefining': wordL, + \ 'wordForRank' : wordL, + \ 'menu' : a:menu, + \ } +endfunction + +" +function fuf#makePatternSet(patternBase, interpreter, partialMatching) + let MakeMatchingExpr = function(a:partialMatching + \ ? 's:makePartialMatchingExpr' + \ : 's:makeFuzzyMatchingExpr') + let [primary; refinings] = split(a:patternBase, g:fuf_patternSeparator, 1) + let elements = call(a:interpreter, [primary]) + let primaryExprs = map(elements.matchingPairs, 'MakeMatchingExpr(v:val[0], v:val[1])') + let refiningExprs = map(refinings, 's:makeRefiningExpr(v:val)') + return { + \ 'primary' : elements.primary, + \ 'primaryForRank': elements.primaryForRank, + \ 'filteringExpr' : join(primaryExprs + refiningExprs, ' && '), + \ } +endfunction + +" +function fuf#enumExpandedDirsEntries(dir, exclude) + let entries = fuf#glob(a:dir . '*') + fuf#glob(a:dir . '.*') + " removes "*/." and "*/.." + call filter(entries, 'v:val !~ ''\v(^|[/\\])\.\.?$''') + call map(entries, 'fuf#makePathItem(v:val, "", 1)') + if len(a:exclude) + call filter(entries, 'v:val.word !~ a:exclude') + endif + return entries +endfunction + +" +function fuf#mapToSetAbbrWithSnippedWordAsPath(items) + let maxLenStats = {} + call map(a:items, 's:makeFileAbbrInfo(v:val, maxLenStats)') + let snippedHeads = + \ map(maxLenStats, 's:getSnippedHead(v:key[: -2], v:val)') + return map(a:items, 's:setAbbrWithFileAbbrData(v:val, snippedHeads)') +endfunction + +" +function fuf#setAbbrWithFormattedWord(item, abbrIndex) + let lenMenu = (exists('a:item.menu') ? len(a:item.menu) + 2 : 0) + let abbrPrefix = (exists('a:item.abbrPrefix') ? a:item.abbrPrefix : '') + let a:item.abbr = abbrPrefix . a:item.word + if a:abbrIndex + let a:item.abbr = printf('%4d: ', a:item.index) . a:item.abbr + endif + let a:item.abbr = l9#snipTail(a:item.abbr, g:fuf_maxMenuWidth - lenMenu, s:ABBR_SNIP_MASK) + return a:item +endfunction + +" +function s:onCommandPre() + for m in filter(copy(fuf#getModeNames()), 'fuf#{v:val}#requiresOnCommandPre()') + call fuf#{m}#onCommandPre(getcmdtype() . getcmdline()) + endfor + " lets last entry become the newest in the history + call histadd(getcmdtype(), getcmdline()) + " this is not mapped again (:help recursive_mapping) + return "\" +endfunction + +" +let s:modeNames = [] + +" +function fuf#addMode(modeName) + if count(g:fuf_modesDisable, a:modeName) > 0 + return + endif + call add(s:modeNames, a:modeName) + call fuf#{a:modeName}#renewCache() + call fuf#{a:modeName}#onInit() + if fuf#{a:modeName}#requiresOnCommandPre() + " cnoremap has a problem, which doesn't expand cabbrev. + cmap onCommandPre() + endif +endfunction + +" +function fuf#getModeNames() + return s:modeNames +endfunction + +" +function fuf#defineLaunchCommand(CmdName, modeName, prefixInitialPattern, tempVars) + if empty(a:tempVars) + let preCmd = '' + else + let preCmd = printf('call l9#tempvariables#setList(%s, %s) | ', + \ string(s:TEMP_VARIABLES_GROUP), string(a:tempVars)) + endif + execute printf('command! -range -bang -narg=? %s %s call fuf#launch(%s, %s . , len())', + \ a:CmdName, preCmd, string(a:modeName), a:prefixInitialPattern) +endfunction + +" +function fuf#defineKeyMappingInHandler(key, func) + " hacks to be able to use feedkeys(). + execute printf( + \ 'inoremap %s =fuf#getRunningHandler().%s ? "" : ""', + \ a:key, a:func) +endfunction + +" +let s:oneTimeVariables = [] + +" +function fuf#setOneTimeVariables(...) + let s:oneTimeVariables += a:000 +endfunction + +" +function fuf#launch(modeName, initialPattern, partialMatching) + if exists('s:runningHandler') + call fuf#echoWarning('FuzzyFinder is running.') + endif + if count(fuf#getModeNames(), a:modeName) == 0 + echoerr 'This mode is not available: ' . a:modeName + return + endif + let s:runningHandler = fuf#{a:modeName}#createHandler(copy(s:handlerBase)) + let s:runningHandler.stats = fuf#loadDataFile(s:runningHandler.getModeName(), 'stats') + let s:runningHandler.partialMatching = a:partialMatching + let s:runningHandler.bufNrPrev = bufnr('%') + let s:runningHandler.lastCol = -1 + let s:runningHandler.windowRestoringCommand = winrestcmd() + call s:runningHandler.onModeEnterPre() + " NOTE: updatetime is set, because in Buffer-Tag mode on Vim 7.3 on Windows, + " Vim keeps from triggering CursorMovedI for updatetime after system() is + " called. I don't know why. + call fuf#setOneTimeVariables( + \ ['&completeopt', 'menuone'], + \ ['&ignorecase', 0], + \ ['&updatetime', 10], + \ ) + if s:runningHandler.getPreviewHeight() > 0 + call fuf#setOneTimeVariables( + \ ['&cmdheight', s:runningHandler.getPreviewHeight() + 1]) + endif + call l9#tempvariables#setList(s:TEMP_VARIABLES_GROUP, s:oneTimeVariables) + let s:oneTimeVariables = [] + call s:activateFufBuffer() + augroup FufLocal + autocmd! + autocmd CursorMovedI call s:runningHandler.onCursorMovedI() + autocmd InsertLeave nested call s:runningHandler.onInsertLeave() + augroup END + for [key, func] in [ + \ [ g:fuf_keyOpen , 'onCr(' . s:OPEN_TYPE_CURRENT . ')' ], + \ [ g:fuf_keyOpenSplit , 'onCr(' . s:OPEN_TYPE_SPLIT . ')' ], + \ [ g:fuf_keyOpenVsplit , 'onCr(' . s:OPEN_TYPE_VSPLIT . ')' ], + \ [ g:fuf_keyOpenTabpage , 'onCr(' . s:OPEN_TYPE_TAB . ')' ], + \ [ '' , 'onBs()' ], + \ [ '' , 'onBs()' ], + \ [ '' , 'onDeleteWord()' ], + \ [ g:fuf_keyPreview , 'onPreviewBase(1)' ], + \ [ g:fuf_keyNextMode , 'onSwitchMode(+1)' ], + \ [ g:fuf_keyPrevMode , 'onSwitchMode(-1)' ], + \ [ g:fuf_keySwitchMatching, 'onSwitchMatching()' ], + \ [ g:fuf_keyPrevPattern , 'onRecallPattern(+1)' ], + \ [ g:fuf_keyNextPattern , 'onRecallPattern(-1)' ], + \ ] + call fuf#defineKeyMappingInHandler(key, func) + endfor + " Starts Insert mode and makes CursorMovedI event now. Command prompt is + " needed to forces a completion menu to update every typing. + call setline(1, s:runningHandler.getPrompt() . a:initialPattern) + call s:runningHandler.onModeEnterPost() + call feedkeys("A", 'n') " startinsert! does not work in InsertLeave event handler + redraw +endfunction + +" +function fuf#loadDataFile(modeName, dataName) + if !s:dataFileAvailable + return [] + endif + let lines = l9#readFile(l9#concatPaths([g:fuf_dataDir, a:modeName, a:dataName])) + return map(lines, 'eval(v:val)') +endfunction + +" +function fuf#saveDataFile(modeName, dataName, items) + if !s:dataFileAvailable + return -1 + endif + let lines = map(copy(a:items), 'string(v:val)') + return l9#writeFile(lines, l9#concatPaths([g:fuf_dataDir, a:modeName, a:dataName])) +endfunction + +" +function fuf#getDataFileTime(modeName, dataName) + if !s:dataFileAvailable + return -1 + endif + return getftime(expand(l9#concatPaths([g:fuf_dataDir, a:modeName, a:dataName]))) +endfunction + +" +function s:createDataBufferListener(dataFile) + let listener = { 'dataFile': a:dataFile } + + function listener.onWrite(lines) + let [modeName, dataName] = split(self.dataFile, l9#getPathSeparator()) + let items = map(filter(a:lines, '!empty(v:val)'), 'eval(v:val)') + call fuf#saveDataFile(modeName, dataName, items) + echo "Data files updated" + return 1 + endfunction + + return listener +endfunction + +" +function s:createEditDataListener() + let listener = {} + + function listener.onComplete(dataFile, method) + let bufName = '[fuf-info]' + let lines = l9#readFile(l9#concatPaths([g:fuf_dataDir, a:dataFile])) + call l9#tempbuffer#openWritable(bufName, 'vim', lines, 0, 0, 0, + \ s:createDataBufferListener(a:dataFile)) + endfunction + + return listener +endfunction + +" +function s:getEditableDataFiles(modeName) + let dataFiles = fuf#{a:modeName}#getEditableDataNames() + call filter(dataFiles, 'fuf#getDataFileTime(a:modeName, v:val) != -1') + return map(dataFiles, 'l9#concatPaths([a:modeName, v:val])') +endfunction + +" +function fuf#editDataFile() + let dataFiles = map(copy(fuf#getModeNames()), 's:getEditableDataFiles(v:val)') + let dataFiles = l9#concat(dataFiles) + call fuf#callbackitem#launch('', 0, '>Mode>', s:createEditDataListener(), dataFiles, 0) +endfunction + +" +function fuf#getRunningHandler() + return s:runningHandler +endfunction + +" +function fuf#onComplete(findstart, base) + return s:runningHandler.onComplete(a:findstart, a:base) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:TEMP_VARIABLES_GROUP = expand(':p') +let s:ABBR_SNIP_MASK = '...' +let s:OPEN_TYPE_CURRENT = 1 +let s:OPEN_TYPE_SPLIT = 2 +let s:OPEN_TYPE_VSPLIT = 3 +let s:OPEN_TYPE_TAB = 4 + +" a:pattern: 'str' -> '\V\.\*s\.\*t\.\*r\.\*' +function s:makeFuzzyMatchingExpr(target, pattern) + let wi = '' + for c in split(a:pattern, '\zs') + if wi =~# '[^*?]$' && c !~ '[*?]' + let wi .= '*' + endif + let wi .= c + endfor + return s:makePartialMatchingExpr(a:target, wi) +endfunction + +" a:pattern: 'str' -> '\Vstr' +" 'st*r' -> '\Vst\.\*r' +function s:makePartialMatchingExpr(target, pattern) + let patternMigemo = s:makeAdditionalMigemoPattern(a:pattern) + if a:pattern !~ '[*?]' && empty(patternMigemo) + " NOTE: stridx is faster than regexp matching + return 'stridx(' . a:target . ', ' . string(a:pattern) . ') >= 0' + endif + return a:target . ' =~# ' . + \ string(l9#convertWildcardToRegexp(a:pattern)) . patternMigemo +endfunction + +" +function s:makeRefiningExpr(pattern) + if g:fuf_fuzzyRefining + let expr = s:makeFuzzyMatchingExpr('v:val.wordForRefining', a:pattern) + else + let expr = s:makePartialMatchingExpr('v:val.wordForRefining', a:pattern) + endif + if a:pattern =~# '\D' + return expr + else + return '(' . expr . ' || v:val.index == ' . string(a:pattern) . ')' + endif +endfunction + +" +function s:makeAdditionalMigemoPattern(pattern) + if !g:fuf_useMigemo || a:pattern =~# '[^\x01-\x7e]' + return '' + endif + return '\|\m' . substitute(migemo(a:pattern), '\\_s\*', '.*', 'g') +endfunction + +" +function s:interpretPrimaryPatternForPathTail(pattern) + let pattern = fuf#expandTailDotSequenceToParentDir(a:pattern) + let pairL = fuf#splitPath(s:toLowerForIgnoringCase(pattern)) + return { + \ 'primary' : pattern, + \ 'primaryForRank': pairL.tail, + \ 'matchingPairs' : [['v:val.wordForPrimaryTail', pairL.tail],], + \ } +endfunction + +" +function s:interpretPrimaryPatternForPath(pattern) + let pattern = fuf#expandTailDotSequenceToParentDir(a:pattern) + let patternL = s:toLowerForIgnoringCase(pattern) + let pairL = fuf#splitPath(patternL) + if g:fuf_splitPathMatching + let matches = [ + \ ['v:val.wordForPrimaryHead', pairL.head], + \ ['v:val.wordForPrimaryTail', pairL.tail], + \ ] + else + let matches = [ + \ ['v:val.wordForPrimaryHead . v:val.wordForPrimaryTail', patternL], + \ ] + endif + return { + \ 'primary' : pattern, + \ 'primaryForRank': pairL.tail, + \ 'matchingPairs' : matches, + \ } +endfunction + +" +function s:interpretPrimaryPatternForNonPath(pattern) + let patternL = s:toLowerForIgnoringCase(a:pattern) + return { + \ 'primary' : a:pattern, + \ 'primaryForRank': patternL, + \ 'matchingPairs' : [['v:val.wordForPrimary', patternL],], + \ } +endfunction + +" +function s:getWordBoundaries(word) + return substitute(a:word, '\a\zs\l\+\|\zs\A', '', 'g') +endfunction + +" +function s:toLowerForIgnoringCase(str) + return (g:fuf_ignoreCase ? tolower(a:str) : a:str) +endfunction + +" +function s:setRanks(item, pattern, exprBoundary, stats) + "let word2 = substitute(a:eval_word, '\a\zs\l\+\|\zs\A', '', 'g') + let a:item.ranks = [ + \ s:evaluateLearningRank(a:item.word, a:stats), + \ -s:scoreSequentialMatching(a:item.wordForRank, a:pattern), + \ -s:scoreBoundaryMatching(a:item.wordForBoundary, + \ a:pattern, a:exprBoundary), + \ a:item.index, + \ ] + return a:item +endfunction + +" +function s:evaluateLearningRank(word, stats) + for i in range(len(a:stats)) + if a:stats[i].word ==# a:word + return i + endif + endfor + return len(a:stats) +endfunction + +" range of return value is [0.0, 1.0] +function s:scoreSequentialMatching(word, pattern) + if empty(a:pattern) + return str2float('0.0') + endif + let pos = stridx(a:word, a:pattern) + if pos < 0 + return str2float('0.0') + endif + let lenRest = len(a:word) - len(a:pattern) - pos + return str2float(pos == 0 ? '0.5' : '0.0') + str2float('0.5') / (lenRest + 1) +endfunction + +" range of return value is [0.0, 1.0] +function s:scoreBoundaryMatching(wordForBoundary, pattern, exprBoundary) + if empty(a:pattern) + return str2float('0.0') + endif + if !eval(a:exprBoundary) + return 0 + endif + return (s:scoreSequentialMatching(a:wordForBoundary, a:pattern) + 1) / 2 +endfunction + +" +function s:highlightPrompt(prompt) + syntax clear + execute printf('syntax match %s /^\V%s/', g:fuf_promptHighlight, escape(a:prompt, '\/')) +endfunction + +" +function s:highlightError() + syntax clear + syntax match Error /^.*$/ +endfunction + +" +function s:expandAbbrevMap(pattern, abbrevMap) + let result = [a:pattern] + for [pattern, subs] in items(a:abbrevMap) + let exprs = result + let result = [] + for expr in exprs + let result += map(copy(subs), 'substitute(expr, pattern, escape(v:val, ''\''), "g")') + endfor + endfor + return l9#unique(result) +endfunction + +" +function s:makeFileAbbrInfo(item, maxLenStats) + let head = matchstr(a:item.word, '^.*[/\\]\ze.') + let a:item.abbr = { 'head' : head, + \ 'tail' : a:item.word[strlen(head):], + \ 'key' : head . '.', + \ 'prefix' : printf('%4d: ', a:item.index), } + if exists('a:item.abbrPrefix') + let a:item.abbr.prefix .= a:item.abbrPrefix + endif + let len = len(a:item.abbr.prefix) + len(a:item.word) + + \ (exists('a:item.menu') ? len(a:item.menu) + 2 : 0) + if !exists('a:maxLenStats[a:item.abbr.key]') || len > a:maxLenStats[a:item.abbr.key] + let a:maxLenStats[a:item.abbr.key] = len + endif + return a:item +endfunction + +" +function s:getSnippedHead(head, baseLen) + return l9#snipMid(a:head, len(a:head) + g:fuf_maxMenuWidth - a:baseLen, s:ABBR_SNIP_MASK) +endfunction + +" +function s:setAbbrWithFileAbbrData(item, snippedHeads) + let lenMenu = (exists('a:item.menu') ? len(a:item.menu) + 2 : 0) + let abbr = a:item.abbr.prefix . a:snippedHeads[a:item.abbr.key] . a:item.abbr.tail + let a:item.abbr = l9#snipTail(abbr, g:fuf_maxMenuWidth - lenMenu, s:ABBR_SNIP_MASK) + return a:item +endfunction + +" +let s:FUF_BUF_NAME = '[fuf]' + +" +function s:activateFufBuffer() + " Save the last window number so we can switch back to it later (otherwise, + " at least with more recent versions of Vim, we end up with the top left + " window focused) + let s:fuf_buffer_last_winnr = winnr() + + " lcd . : To avoid the strange behavior that unnamed buffer changes its cwd + " if 'autochdir' was set on. + lcd . + let cwd = getcwd() + call l9#tempbuffer#openScratch(s:FUF_BUF_NAME, 'fuf', [], 1, 0, 1, {}) + resize 1 " for issue #21 + " lcd ... : countermeasure against auto-cd script + lcd `=cwd` + setlocal nocursorline " for highlighting + setlocal nocursorcolumn " for highlighting + setlocal omnifunc=fuf#onComplete + redraw " for 'lazyredraw' + if exists(':AcpLock') + AcpLock + elseif exists(':AutoComplPopLock') + AutoComplPopLock + endif +endfunction + +" +function s:deactivateFufBuffer() + if exists(':AcpUnlock') + AcpUnlock + elseif exists(':AutoComplPopUnlock') + AutoComplPopUnlock + endif + call l9#tempbuffer#close(s:FUF_BUF_NAME) + exec s:fuf_buffer_last_winnr . "wincmd w" +endfunction + +" }}}1 +"============================================================================= +" s:handlerBase {{{1 + +let s:handlerBase = {} + +"----------------------------------------------------------------------------- +" PURE VIRTUAL FUNCTIONS {{{2 +" +" " +" s:handler.getModeName() +" +" " +" s:handler.getPrompt() +" +" " +" s:handler.getCompleteItems(patternSet) +" +" " +" s:handler.onOpen(word, mode) +" +" " Before entering FuzzyFinder buffer. This function should return in a short time. +" s:handler.onModeEnterPre() +" +" " After entering FuzzyFinder buffer. +" s:handler.onModeEnterPost() +" +" " After leaving FuzzyFinder buffer. +" s:handler.onModeLeavePost(opened) +" +" }}}2 +"----------------------------------------------------------------------------- + +" +function s:handlerBase.concretize(deriv) + call extend(self, a:deriv, 'error') + return self +endfunction + +" +function s:handlerBase.addStat(pattern, word) + let stat = { 'pattern' : a:pattern, 'word' : a:word } + call filter(self.stats, 'v:val !=# stat') + call insert(self.stats, stat) + let self.stats = self.stats[0 : g:fuf_learningLimit - 1] +endfunction + +" +function s:handlerBase.getMatchingCompleteItems(patternBase) + let MakeMatchingExpr = function(self.partialMatching + \ ? 's:makePartialMatchingExpr' + \ : 's:makeFuzzyMatchingExpr') + let patternSet = self.makePatternSet(a:patternBase) + let exprBoundary = s:makeFuzzyMatchingExpr('a:wordForBoundary', patternSet.primaryForRank) + let stats = filter( + \ copy(self.stats), 'v:val.pattern ==# patternSet.primaryForRank') + let items = self.getCompleteItems(patternSet.primary) + " NOTE: In order to know an excess, plus 1 to limit number + let items = l9#filterWithLimit( + \ items, patternSet.filteringExpr, g:fuf_enumeratingLimit + 1) + return map(items, + \ 's:setRanks(v:val, patternSet.primaryForRank, exprBoundary, stats)') +endfunction + +" +function s:handlerBase.onComplete(findstart, base) + if a:findstart + return 0 + elseif !self.existsPrompt(a:base) + return [] + endif + call s:highlightPrompt(self.getPrompt()) + let items = [] + for patternBase in s:expandAbbrevMap(self.removePrompt(a:base), g:fuf_abbrevMap) + let items += self.getMatchingCompleteItems(patternBase) + if len(items) > g:fuf_enumeratingLimit + let items = items[ : g:fuf_enumeratingLimit - 1] + call s:highlightError() + break + endif + endfor + if empty(items) + call s:highlightError() + else + call sort(items, 'fuf#compareRanks') + if g:fuf_autoPreview + call feedkeys("\\\=fuf#getRunningHandler().onPreviewBase(0) ? '' : ''\", 'n') + else + call feedkeys("\\", 'n') + endif + let self.lastFirstWord = items[0].word + endif + return items +endfunction + +" +function s:handlerBase.existsPrompt(line) + return strlen(a:line) >= strlen(self.getPrompt()) && + \ a:line[:strlen(self.getPrompt()) -1] ==# self.getPrompt() +endfunction + +" +function s:handlerBase.removePrompt(line) + return a:line[(self.existsPrompt(a:line) ? strlen(self.getPrompt()) : 0):] +endfunction + +" +function s:handlerBase.restorePrompt(line) + let i = 0 + while i < len(self.getPrompt()) && i < len(a:line) && self.getPrompt()[i] ==# a:line[i] + let i += 1 + endwhile + return self.getPrompt() . a:line[i : ] +endfunction + +" +function s:handlerBase.onCursorMovedI() + if !self.existsPrompt(getline('.')) + call setline('.', self.restorePrompt(getline('.'))) + call feedkeys("\", 'n') + elseif col('.') <= len(self.getPrompt()) + " if the cursor is moved before command prompt + call feedkeys(repeat("\", len(self.getPrompt()) - col('.') + 1), 'n') + elseif col('.') > strlen(getline('.')) && col('.') != self.lastCol + " if the cursor is placed on the end of the line and has been actually moved. + let self.lastCol = col('.') + let self.lastPattern = self.removePrompt(getline('.')) + call feedkeys("\\", 'n') + endif +endfunction + +" +function s:handlerBase.onInsertLeave() + unlet s:runningHandler + let tempVars = l9#tempvariables#getList(s:TEMP_VARIABLES_GROUP) + call l9#tempvariables#end(s:TEMP_VARIABLES_GROUP) + call s:deactivateFufBuffer() + call fuf#saveDataFile(self.getModeName(), 'stats', self.stats) + execute self.windowRestoringCommand + let fOpen = exists('s:reservedCommand') + if fOpen + call self.onOpen(s:reservedCommand[0], s:reservedCommand[1]) + unlet s:reservedCommand + endif + call self.onModeLeavePost(fOpen) + if exists('self.reservedMode') + call l9#tempvariables#setList(s:TEMP_VARIABLES_GROUP, tempVars) + call fuf#launch(self.reservedMode, self.lastPattern, self.partialMatching) + endif +endfunction + +" +function s:handlerBase.onCr(openType) + if pumvisible() + call feedkeys(printf("\\=fuf#getRunningHandler().onCr(%d) ? '' : ''\", + \ a:openType), 'n') + return + endif + if !empty(self.lastPattern) + call self.addStat(self.lastPattern, self.removePrompt(getline('.'))) + endif + if !self.isOpenable(getline('.')) + " To clear i_ expression (fuf#getRunningHandler().onCr...) + echo '' + return + endif + let s:reservedCommand = [self.removePrompt(getline('.')), a:openType] + call feedkeys("\", 'n') " stopinsert behavior is strange... +endfunction + +" +function s:handlerBase.onBs() + call feedkeys((pumvisible() ? "\\" : "\"), 'n') +endfunction + +" +function s:getLastBlockLength(pattern, patternIsPath) + let separatorPos = strridx(a:pattern, g:fuf_patternSeparator) + if separatorPos >= 0 + return len(a:pattern) - separatorPos + endif + if a:patternIsPath && a:pattern =~# '[/\\].' + return len(matchstr(a:pattern, '[^/\\]*.$')) + endif + return len(a:pattern) +endfunction + +" +function s:handlerBase.onDeleteWord() + let pattern = self.removePrompt(getline('.')[ : col('.') - 2]) + let numBs = s:getLastBlockLength(pattern, 1) + call feedkeys((pumvisible() ? "\" : "") . repeat("\", numBs), 'n') +endfunction + +" +function s:handlerBase.onPreviewBase(repeatable) + if self.getPreviewHeight() <= 0 + return + elseif !pumvisible() + return + elseif !self.existsPrompt(getline('.')) + let word = self.removePrompt(getline('.')) + elseif !exists('self.lastFirstWord') + return + else + let word = self.lastFirstWord + endif + redraw + if a:repeatable && exists('self.lastPreviewInfo') && self.lastPreviewInfo.word ==# word + let self.lastPreviewInfo.count += 1 + else + let self.lastPreviewInfo = {'word': word, 'count': 0} + endif + let lines = self.makePreviewLines(word, self.lastPreviewInfo.count) + let lines = lines[: self.getPreviewHeight() - 1] + call map(lines, 'substitute(v:val, "\t", repeat(" ", &tabstop), "g")') + call map(lines, 'strtrans(v:val)') + call map(lines, 'l9#snipTail(v:val, &columns - 1, s:ABBR_SNIP_MASK)') + echo join(lines, "\n") +endfunction + +" +function s:handlerBase.onSwitchMode(shift) + let modes = copy(fuf#getModeNames()) + call map(modes, '{ "ranks": [ fuf#{v:val}#getSwitchOrder(), v:val ] }') + call filter(modes, 'v:val.ranks[0] >= 0') + call sort(modes, 'fuf#compareRanks') + let self.reservedMode = self.getModeName() + for i in range(len(modes)) + if modes[i].ranks[1] ==# self.getModeName() + let self.reservedMode = modes[(i + a:shift) % len(modes)].ranks[1] + break + endif + endfor + call feedkeys("\", 'n') " stopinsert doesn't work. +endfunction + +" +function s:handlerBase.onSwitchMatching() + let self.partialMatching = !self.partialMatching + let self.lastCol = -1 + call setline('.', self.restorePrompt(self.lastPattern)) + call feedkeys("\", 'n') + "call self.onCursorMovedI() +endfunction + +" +function s:handlerBase.onRecallPattern(shift) + let patterns = map(copy(self.stats), 'v:val.pattern') + if !exists('self.indexRecall') + let self.indexRecall = -1 + endif + let self.indexRecall += a:shift + if self.indexRecall < 0 + let self.indexRecall = -1 + elseif self.indexRecall >= len(patterns) + let self.indexRecall = len(patterns) - 1 + else + call setline('.', self.getPrompt() . patterns[self.indexRecall]) + call feedkeys("\", 'n') + endif +endfunction + +" }}}1 +"============================================================================= +" INITIALIZATION {{{1 + +augroup FufGlobal + autocmd! + autocmd BufLeave * let s:bufferCursorPosMap[bufnr('')] = getpos('.') +augroup END + +let s:bufferCursorPosMap = {} + +" +let s:DATA_FILE_VERSION = 400 + +" +function s:checkDataFileCompatibility() + if empty(g:fuf_dataDir) + let s:dataFileAvailable = 0 + return + endif + let versionPath = l9#concatPaths([g:fuf_dataDir, 'VERSION']) + let lines = l9#readFile(versionPath) + if empty(lines) + call l9#writeFile([s:DATA_FILE_VERSION], versionPath) + let s:dataFileAvailable = 1 + elseif str2nr(lines[0]) == s:DATA_FILE_VERSION + let s:dataFileAvailable = 1 + else + call fuf#echoWarning(printf( + \ "=======================================================\n" . + \ " Existing data files for FuzzyFinder is no longer \n" . + \ " compatible with this version of FuzzyFinder. Remove \n" . + \ " %-53s\n" . + \ "=======================================================\n" , + \ string(g:fuf_dataDir))) + call l9#inputHl('Question', 'Press Enter') + let s:dataFileAvailable = 0 + endif +endfunction + +call s:checkDataFileCompatibility() + +" }}}1 +"============================================================================= +" vim: set fdm=marker: + diff --git a/fuzzyfinder/autoload/fuf/bookmarkdir.vim b/fuzzyfinder/autoload/fuf/bookmarkdir.vim new file mode 100644 index 0000000..01585ff --- /dev/null +++ b/fuzzyfinder/autoload/fuf/bookmarkdir.vim @@ -0,0 +1,163 @@ +"============================================================================= +" Copyright (c) 2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#bookmarkdir#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#bookmarkdir#getSwitchOrder() + return g:fuf_bookmarkdir_switchOrder +endfunction + +" +function fuf#bookmarkdir#getEditableDataNames() + return ['items'] +endfunction + +" +function fuf#bookmarkdir#renewCache() +endfunction + +" +function fuf#bookmarkdir#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#bookmarkdir#onInit() + call fuf#defineLaunchCommand('FufBookmarkDir', s:MODE_NAME, '""', []) + command! -bang -narg=? FufBookmarkDirAdd call s:bookmark() +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') +let s:OPEN_TYPE_DELETE = -1 + +" +function s:bookmark(word) + let item = { + \ 'time' : localtime(), + \ } + + let item.path = l9#inputHl('Question', '[fuf] Directory to bookmark:', + \ fnamemodify(getcwd(), ':p:~'), 'dir') + if item.path !~ '\S' + call fuf#echoWarning('Canceled') + return + endif + let item.word = l9#inputHl('Question', '[fuf] Bookmark as:', + \ fnamemodify(getcwd(), ':p:~')) + if item.word !~ '\S' + call fuf#echoWarning('Canceled') + return + endif + let items = fuf#loadDataFile(s:MODE_NAME, 'items') + call insert(items, item) + call fuf#saveDataFile(s:MODE_NAME, 'items', items) +endfunction + +" +function s:findItem(items, word) + for item in a:items + if item.word ==# a:word + return item + endif + endfor + return {} +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_bookmarkdir_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return 0 +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForNonPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return [] +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + if a:mode ==# s:OPEN_TYPE_DELETE + let items = fuf#loadDataFile(s:MODE_NAME, 'items') + call filter(items, 'v:val.word !=# a:word') + call fuf#saveDataFile(s:MODE_NAME, 'items', items) + let self.reservedMode = self.getModeName() + return + else + let item = s:findItem(fuf#loadDataFile(s:MODE_NAME, 'items'), a:word) + if !empty(item) + execute ':cd ' . fnameescape(item.path) + endif + endif +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() + call fuf#defineKeyMappingInHandler(g:fuf_bookmarkdir_keyDelete, + \ 'onCr(' . s:OPEN_TYPE_DELETE . ')') + let self.items = fuf#loadDataFile(s:MODE_NAME, 'items') + call map(self.items, 'fuf#makeNonPathItem(v:val.word, strftime(g:fuf_timeFormat, v:val.time))') + call fuf#mapToSetSerialIndex(self.items, 1) + call map(self.items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/bookmarkfile.vim b/fuzzyfinder/autoload/fuf/bookmarkfile.vim new file mode 100644 index 0000000..12ac80f --- /dev/null +++ b/fuzzyfinder/autoload/fuf/bookmarkfile.vim @@ -0,0 +1,199 @@ +"============================================================================= +" Copyright (c) 2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#bookmarkfile#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#bookmarkfile#getSwitchOrder() + return g:fuf_bookmarkfile_switchOrder +endfunction + +" +function fuf#bookmarkfile#getEditableDataNames() + return ['items'] +endfunction + +" +function fuf#bookmarkfile#renewCache() +endfunction + +" +function fuf#bookmarkfile#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#bookmarkfile#onInit() + call fuf#defineLaunchCommand('FufBookmarkFile', s:MODE_NAME, '""', []) + command! -bang -narg=? FufBookmarkFileAdd call s:bookmarkHere() + command! -bang -narg=0 -range FufBookmarkFileAddAsSelectedText call s:bookmarkHere(l9#getSelectedText()) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') +let s:OPEN_TYPE_DELETE = -1 + +" opens a:path and jumps to the line matching to a:pattern from a:lnum within +" a:range. if not found, jumps to a:lnum. +function s:jumpToBookmark(path, mode, pattern, lnum) + call fuf#openFile(a:path, a:mode, g:fuf_reuseWindow) + call cursor(s:getMatchingLineNumber(getline(1, '$'), a:pattern, a:lnum), 0) + normal! zvzz +endfunction + +" +function s:getMatchingLineNumber(lines, pattern, lnumBegin) + let l = min([a:lnumBegin, len(a:lines)]) + for [l0, l1] in map(range(0, g:fuf_bookmarkfile_searchRange), + \ '[l + v:val, l - v:val]') + if l0 <= len(a:lines) && a:lines[l0 - 1] =~# a:pattern + return l0 + elseif l1 >= 0 && a:lines[l1 - 1] =~# a:pattern + return l1 + endif + endfor + return l +endfunction + +" +function s:getLinePattern(lnum) + return '\C\V\^' . escape(getline(a:lnum), '\') . '\$' +endfunction + +" +function s:bookmarkHere(word) + if !empty(&buftype) || expand('%') !~ '\S' + call fuf#echoWarning('Can''t bookmark this buffer.') + return + endif + let item = { + \ 'word' : (a:word =~# '\S' ? substitute(a:word, '\n', ' ', 'g') + \ : pathshorten(expand('%:p:~')) . '|' . line('.') . '| ' . getline('.')), + \ 'path' : expand('%:p'), + \ 'lnum' : line('.'), + \ 'pattern' : s:getLinePattern(line('.')), + \ 'time' : localtime(), + \ } + let item.word = l9#inputHl('Question', '[fuf] Bookmark as:', item.word) + if item.word !~ '\S' + call fuf#echoWarning('Canceled') + return + endif + let items = fuf#loadDataFile(s:MODE_NAME, 'items') + call insert(items, item) + call fuf#saveDataFile(s:MODE_NAME, 'items', items) +endfunction + +" +function s:findItem(items, word) + for item in a:items + if item.word ==# a:word + return item + endif + endfor + return {} +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_bookmarkfile_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForNonPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + let item = s:findItem(fuf#loadDataFile(s:MODE_NAME, 'items'), a:word) + let lines = fuf#getFileLines(item.path) + if empty(lines) + return [] + endif + let index = s:getMatchingLineNumber(lines, item.pattern, item.lnum) - 1 + return fuf#makePreviewLinesAround( + \ lines, [index], a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + if a:mode ==# s:OPEN_TYPE_DELETE + let items = fuf#loadDataFile(s:MODE_NAME, 'items') + call filter(items, 'v:val.word !=# a:word') + call fuf#saveDataFile(s:MODE_NAME, 'items', items) + let self.reservedMode = self.getModeName() + return + else + let item = s:findItem(fuf#loadDataFile(s:MODE_NAME, 'items'), a:word) + if !empty(item) + call s:jumpToBookmark(item.path, a:mode, item.pattern, item.lnum) + endif + endif +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() + call fuf#defineKeyMappingInHandler(g:fuf_bookmarkfile_keyDelete, + \ 'onCr(' . s:OPEN_TYPE_DELETE . ')') + let self.items = fuf#loadDataFile(s:MODE_NAME, 'items') + call map(self.items, 'fuf#makeNonPathItem(v:val.word, strftime(g:fuf_timeFormat, v:val.time))') + call fuf#mapToSetSerialIndex(self.items, 1) + call map(self.items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/buffer.vim b/fuzzyfinder/autoload/fuf/buffer.vim new file mode 100644 index 0000000..08b954a --- /dev/null +++ b/fuzzyfinder/autoload/fuf/buffer.vim @@ -0,0 +1,189 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#buffer#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#buffer#getSwitchOrder() + return g:fuf_buffer_switchOrder +endfunction + +" +function fuf#buffer#getEditableDataNames() + return [] +endfunction + +" +function fuf#buffer#renewCache() +endfunction + +" +function fuf#buffer#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#buffer#onInit() + call fuf#defineLaunchCommand('FufBuffer', s:MODE_NAME, '""', []) + augroup fuf#buffer + autocmd! + autocmd BufEnter * call s:updateBufTimes() + autocmd BufWritePost * call s:updateBufTimes() + augroup END +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') +let s:OPEN_TYPE_DELETE = -1 + +let s:bufTimes = {} + +" +function s:updateBufTimes() + let s:bufTimes[bufnr('%')] = localtime() +endfunction + +" +function s:makeItem(nr) + let fname = (empty(bufname(a:nr)) + \ ? '[No Name]' + \ : fnamemodify(bufname(a:nr), ':p:~:.')) + let time = (exists('s:bufTimes[a:nr]') ? s:bufTimes[a:nr] : 0) + let item = fuf#makePathItem(fname, strftime(g:fuf_timeFormat, time), 0) + let item.index = a:nr + let item.bufNr = a:nr + let item.time = time + let item.abbrPrefix = s:getBufIndicator(a:nr) . ' ' + return item +endfunction + +" +function s:getBufIndicator(bufNr) + if !getbufvar(a:bufNr, '&modifiable') + return '[-]' + elseif getbufvar(a:bufNr, '&modified') + return '[+]' + elseif getbufvar(a:bufNr, '&readonly') + return '[R]' + else + return ' ' + endif +endfunction + +" +function s:compareTimeDescending(i1, i2) + return a:i1.time == a:i2.time ? 0 : a:i1.time > a:i2.time ? -1 : +1 +endfunction + +" +function s:findItem(items, word) + for item in a:items + if item.word ==# a:word + return item + endif + endfor + return {} +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_buffer_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + let item = s:findItem(self.items, a:word) + if empty(item) + return [] + endif + return fuf#makePreviewLinesForFile(item.bufNr, a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + " not use bufnr(a:word) in order to handle unnamed buffer + let item = s:findItem(self.items, a:word) + if empty(item) + " do nothing + elseif a:mode ==# s:OPEN_TYPE_DELETE + execute item.bufNr . 'bdelete' + let self.reservedMode = self.getModeName() + else + call fuf#openBuffer(item.bufNr, a:mode, g:fuf_reuseWindow) + endif +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() + call fuf#defineKeyMappingInHandler(g:fuf_buffer_keyDelete, + \ 'onCr(' . s:OPEN_TYPE_DELETE . ')') + let self.items = range(1, bufnr('$')) + call filter(self.items, 'buflisted(v:val) && v:val != self.bufNrPrev && v:val != bufnr("%")') + call map(self.items, 's:makeItem(v:val)') + if g:fuf_buffer_mruOrder + call sort(self.items, 's:compareTimeDescending') + call fuf#mapToSetSerialIndex(self.items, 1) + endif + let self.items = fuf#mapToSetAbbrWithSnippedWordAsPath(self.items) +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/buffertag.vim b/fuzzyfinder/autoload/fuf/buffertag.vim new file mode 100644 index 0000000..392b996 --- /dev/null +++ b/fuzzyfinder/autoload/fuf/buffertag.vim @@ -0,0 +1,300 @@ +"============================================================================= +" Copyright (c) 2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#buffertag#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#buffertag#getSwitchOrder() + return g:fuf_buffertag_switchOrder +endfunction + +" +function fuf#buffertag#getEditableDataNames() + return [] +endfunction + +" +function fuf#buffertag#renewCache() + let s:tagItemsCache = {} + let s:tagDataCache = {} +endfunction + +" +function fuf#buffertag#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#buffertag#onInit() + call fuf#defineLaunchCommand('FufBufferTag', s:MODE_NAME, '""', + \ [['g:fuf_buffertag_forAll', 0]]) + call fuf#defineLaunchCommand('FufBufferTagAll', s:MODE_NAME, '""', + \ [['g:fuf_buffertag_forAll', 1]]) + call fuf#defineLaunchCommand('FufBufferTagWithCursorWord', s:MODE_NAME, + \ 'expand('''')', [['g:fuf_buffertag_forAll', 0]]) + call fuf#defineLaunchCommand('FufBufferTagAllWithCursorWord', s:MODE_NAME, + \ 'expand('''')', [['g:fuf_buffertag_forAll', 1]]) + call fuf#defineLaunchCommand('FufBufferTagWithSelectedText', s:MODE_NAME, + \ 'l9#getSelectedText()', [['g:fuf_buffertag_forAll', 0]]) + call fuf#defineLaunchCommand('FufBufferTagAllWithSelectedText', s:MODE_NAME, + \ 'l9#getSelectedText()', [['g:fuf_buffertag_forAll', 1]]) + call l9#defineVariableDefault('g:fuf_buffertag_forAll', 0) " private option + " the following settings originate from taglist.vim + call l9#defineVariableDefault('g:fuf_buffertag__asm' , '--language-force=asm --asm-types=dlmt') + call l9#defineVariableDefault('g:fuf_buffertag__aspperl' , '--language-force=asp --asp-types=fsv') + call l9#defineVariableDefault('g:fuf_buffertag__aspvbs' , '--language-force=asp --asp-types=fsv') + call l9#defineVariableDefault('g:fuf_buffertag__awk' , '--language-force=awk --awk-types=f') + call l9#defineVariableDefault('g:fuf_buffertag__beta' , '--language-force=beta --beta-types=fsv') + call l9#defineVariableDefault('g:fuf_buffertag__c' , '--language-force=c --c-types=dgsutvf') + call l9#defineVariableDefault('g:fuf_buffertag__cpp' , '--language-force=c++ --c++-types=nvdtcgsuf') + call l9#defineVariableDefault('g:fuf_buffertag__cs' , '--language-force=c# --c#-types=dtncEgsipm') + call l9#defineVariableDefault('g:fuf_buffertag__cobol' , '--language-force=cobol --cobol-types=dfgpPs') + call l9#defineVariableDefault('g:fuf_buffertag__eiffel' , '--language-force=eiffel --eiffel-types=cf') + call l9#defineVariableDefault('g:fuf_buffertag__erlang' , '--language-force=erlang --erlang-types=drmf') + call l9#defineVariableDefault('g:fuf_buffertag__expect' , '--language-force=tcl --tcl-types=cfp') + call l9#defineVariableDefault('g:fuf_buffertag__fortran' , '--language-force=fortran --fortran-types=pbceiklmntvfs') + call l9#defineVariableDefault('g:fuf_buffertag__html' , '--language-force=html --html-types=af') + call l9#defineVariableDefault('g:fuf_buffertag__java' , '--language-force=java --java-types=pcifm') + call l9#defineVariableDefault('g:fuf_buffertag__javascript', '--language-force=javascript --javascript-types=f') + call l9#defineVariableDefault('g:fuf_buffertag__lisp' , '--language-force=lisp --lisp-types=f') + call l9#defineVariableDefault('g:fuf_buffertag__lua' , '--language-force=lua --lua-types=f') + call l9#defineVariableDefault('g:fuf_buffertag__make' , '--language-force=make --make-types=m') + call l9#defineVariableDefault('g:fuf_buffertag__pascal' , '--language-force=pascal --pascal-types=fp') + call l9#defineVariableDefault('g:fuf_buffertag__perl' , '--language-force=perl --perl-types=clps') + call l9#defineVariableDefault('g:fuf_buffertag__php' , '--language-force=php --php-types=cdvf') + call l9#defineVariableDefault('g:fuf_buffertag__python' , '--language-force=python --python-types=cmf') + call l9#defineVariableDefault('g:fuf_buffertag__rexx' , '--language-force=rexx --rexx-types=s') + call l9#defineVariableDefault('g:fuf_buffertag__ruby' , '--language-force=ruby --ruby-types=cfFm') + call l9#defineVariableDefault('g:fuf_buffertag__scheme' , '--language-force=scheme --scheme-types=sf') + call l9#defineVariableDefault('g:fuf_buffertag__sh' , '--language-force=sh --sh-types=f') + call l9#defineVariableDefault('g:fuf_buffertag__csh' , '--language-force=sh --sh-types=f') + call l9#defineVariableDefault('g:fuf_buffertag__zsh' , '--language-force=sh --sh-types=f') + call l9#defineVariableDefault('g:fuf_buffertag__slang' , '--language-force=slang --slang-types=nf') + call l9#defineVariableDefault('g:fuf_buffertag__sml' , '--language-force=sml --sml-types=ecsrtvf') + call l9#defineVariableDefault('g:fuf_buffertag__sql' , '--language-force=sql --sql-types=cFPrstTvfp') + call l9#defineVariableDefault('g:fuf_buffertag__tcl' , '--language-force=tcl --tcl-types=cfmp') + call l9#defineVariableDefault('g:fuf_buffertag__vera' , '--language-force=vera --vera-types=cdefgmpPtTvx') + call l9#defineVariableDefault('g:fuf_buffertag__verilog' , '--language-force=verilog --verilog-types=mcPertwpvf') + call l9#defineVariableDefault('g:fuf_buffertag__vim' , '--language-force=vim --vim-types=avf') + call l9#defineVariableDefault('g:fuf_buffertag__yacc' , '--language-force=yacc --yacc-types=l') +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:parseTagLine(line) + " tag W:\Win32\SRC7\NCSIM\NCVW32\CUBEFACE.H /^#define CUBEFACE_H$/;" macro line:4 + let fields = matchlist(a:line, '\v^([^\t]+)\t(.+)\t\/\^(.+)\$\/\;\"\t(.+)\tline\:(\d+)') + if empty(fields) + return {} + endif + return { + \ 'tag' : fields[1], + \ 'fname' : fields[2], + \ 'pattern': fields[3], + \ 'kind' : fields[4], + \ 'lnum' : str2nr(fields[5]), + \ } +endfunction + +" +let s:TEMP_VARIABLES_GROUP = expand(':p') + +" +function s:getFileType(bufNr) + let ft = getbufvar(a:bufNr, '&filetype') + if !empty(ft) || bufloaded(a:bufNr) + return ft + endif + let ft = getbufvar(a:bufNr, 'fuf_buffertag_filetype') + if !empty(ft) + return ft + endif + call l9#tempvariables#set(s:TEMP_VARIABLES_GROUP, '&eventignore', 'FileType') + call l9#tempvariables#set(s:TEMP_VARIABLES_GROUP, '&filetype', &filetype) + " from taglist.vim + execute 'doautocmd filetypedetect BufRead ' . bufname(a:bufNr) + let ft = &filetype + call l9#tempvariables#end(s:TEMP_VARIABLES_GROUP) + call setbufvar(a:bufNr, 'fuf_buffertag_filetype', ft) + return ft +endfunction + +" +function s:makeCtagsCmd(bufNr) + let ft = s:getFileType(a:bufNr) + if !exists('g:fuf_buffertag__{ft}') + return '' + endif + " + let cmd = join([g:fuf_buffertag_ctagsPath, + \ '-f - --sort=no --excmd=pattern --fields=nKs', + \ g:fuf_buffertag__{ft}, + \ shellescape(fnamemodify(bufname(a:bufNr), ':p'))]) + return cmd +endfunction + +" +function s:getTagItems(bufNr) + let cmd = s:makeCtagsCmd(a:bufNr) + if empty(cmd) + return [] + elseif !exists('s:tagItemsCache[cmd]') || + \ s:tagItemsCache[cmd].time < getftime(expand(bufname(a:bufNr))) + let items = split(system(cmd), "\n") + if v:shell_error + call fuf#echoError([cmd] + items) + throw "Command error" + endif + call map(items, 's:parseTagLine(v:val)') + call filter(items, '!empty(v:val)') + let s:tagItemsCache[cmd] = { + \ 'time' : localtime(), + \ 'items' : items, + \ } + endif + return s:tagItemsCache[cmd].items +endfunction + +" +function s:makeItem(tag, itemMap) + let menu = fnamemodify(a:itemMap[a:tag][0].fname, ':t') + \ . ' [' . a:itemMap[a:tag][0].kind . ']' + if len(a:itemMap[a:tag]) > 1 + let menu .= ' (' . len(a:itemMap[a:tag]) . ')' + endif + let item = fuf#makeNonPathItem(a:tag, menu) + return item +endfunction + +" +function s:getTagData(bufNrs) + let key = join([0] + sort(copy(a:bufNrs)), "\n") + let bufNames = map(copy(a:bufNrs), 'bufname(v:val)') + if !exists('s:tagDataCache[key]') || + \ fuf#countModifiedFiles(bufNames, s:tagDataCache[key].time) > 0 + let itemMap = {} + for item in l9#concat(map(copy(a:bufNrs), 's:getTagItems(v:val)')) + if !exists('itemMap[item.tag]') + let itemMap[item.tag] = [] + endif + call add(itemMap[item.tag], item) + endfor + let items = sort(keys(itemMap)) + call map(items, 's:makeItem(v:val, itemMap)') + call fuf#mapToSetSerialIndex(items, 1) + call map(items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') + let s:tagDataCache[key] = { + \ 'time' : localtime(), + \ 'itemMap': itemMap, + \ 'items' : items, + \ } + endif + return [s:tagDataCache[key].items, s:tagDataCache[key].itemMap] +endfunction + +" +function s:jumpToTag(item, mode) + call fuf#openFile(a:item.fname, a:mode, g:fuf_reuseWindow) + call cursor(a:item.lnum, 1) + normal! zvzz +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_buffertag_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return 0 +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForNonPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return [] +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + if !exists('self.itemMap[a:word][0]') + call fuf#echoError('Definition not found:' . a:word) + return + elseif len(self.itemMap[a:word]) == 1 + let i = 0 + else + let list = map(fuf#mapToSetSerialIndex(copy(self.itemMap[a:word]), 1), + \ 'printf(" %2d: %s|%d| [%s] %s",v:val.index, fnamemodify(v:val.fname, ":~:."), v:val.lnum, v:val.kind, v:val.pattern)') + let i = inputlist(['Select a definition of "' . a:word . '":'] + list) - 1 + endif + if 0 <= i && i < len(self.itemMap[a:word]) + call s:jumpToTag(self.itemMap[a:word][i], a:mode) + endif +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() + if g:fuf_buffertag_forAll + let bufNrs = filter(range(1, bufnr('$')), 'buflisted(v:val)') + else + let bufNrs = [self.bufNrPrev] + endif + let [self.items, self.itemMap] = s:getTagData(bufNrs) +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/callbackfile.vim b/fuzzyfinder/autoload/fuf/callbackfile.vim new file mode 100644 index 0000000..fedf0cf --- /dev/null +++ b/fuzzyfinder/autoload/fuf/callbackfile.vim @@ -0,0 +1,137 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#callbackfile#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#callbackfile#getSwitchOrder() + return -1 +endfunction + +" +function fuf#callbackfile#getEditableDataNames() + return [] +endfunction + +" +function fuf#callbackfile#renewCache() + let s:cache = {} +endfunction + +" +function fuf#callbackfile#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#callbackfile#onInit() +endfunction + +" +function fuf#callbackfile#launch(initialPattern, partialMatching, prompt, exclude, listener) + let s:prompt = (empty(a:prompt) ? '>' : a:prompt) + let s:exclude = a:exclude + let s:listener = a:listener + call fuf#launch(s:MODE_NAME, a:initialPattern, a:partialMatching) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:enumItems(dir) + let key = getcwd() . g:fuf_ignoreCase . s:exclude . "\n" . a:dir + if !exists('s:cache[key]') + let s:cache[key] = fuf#enumExpandedDirsEntries(a:dir, s:exclude) + if isdirectory(a:dir) + call insert(s:cache[key], fuf#makePathItem(a:dir . '.', '', 0)) + endif + call fuf#mapToSetSerialIndex(s:cache[key], 1) + call fuf#mapToSetAbbrWithSnippedWordAsPath(s:cache[key]) + endif + return s:cache[key] +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(s:prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return a:enteredPattern =~# '[^/\\]$' +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForPathTail', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return fuf#makePreviewLinesForFile(a:word, a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + let items = copy(s:enumItems(fuf#splitPath(a:patternPrimary).head)) + return filter(items, 'bufnr("^" . v:val.word . "$") != self.bufNrPrev') +endfunction + +" +function s:handler.onOpen(word, mode) + call s:listener.onComplete(a:word, a:mode) +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() +endfunction + +" +function s:handler.onModeLeavePost(opened) + if !a:opened && exists('s:listener.onAbort()') + call s:listener.onAbort() + endif +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/callbackitem.vim b/fuzzyfinder/autoload/fuf/callbackitem.vim new file mode 100644 index 0000000..118ee08 --- /dev/null +++ b/fuzzyfinder/autoload/fuf/callbackitem.vim @@ -0,0 +1,139 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#callbackitem#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#callbackitem#getSwitchOrder() + return -1 +endfunction + +" +function fuf#callbackitem#getEditableDataNames() + return [] +endfunction + +" +function fuf#callbackitem#renewCache() +endfunction + +" +function fuf#callbackitem#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#callbackitem#onInit() +endfunction + +" +function fuf#callbackitem#launch(initialPattern, partialMatching, prompt, listener, items, forPath) + let s:prompt = (empty(a:prompt) ? '>' : a:prompt) + let s:listener = a:listener + let s:forPath = a:forPath + let s:items = copy(a:items) + if s:forPath + call map(s:items, 'fuf#makePathItem(v:val, "", 1)') + call fuf#mapToSetSerialIndex(s:items, 1) + call fuf#mapToSetAbbrWithSnippedWordAsPath(s:items) + else + call map(s:items, 'fuf#makeNonPathItem(v:val, "")') + call fuf#mapToSetSerialIndex(s:items, 1) + call map(s:items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') + endif + call fuf#launch(s:MODE_NAME, a:initialPattern, a:partialMatching) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(s:prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + if s:forPath + return g:fuf_previewHeight + endif + return 0 +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + let parser = (s:forPath + \ ? 's:interpretPrimaryPatternForPath' + \ : 's:interpretPrimaryPatternForNonPath') + return fuf#makePatternSet(a:patternBase, parser, self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + if s:forPath + return fuf#makePreviewLinesForFile(a:word, a:count, self.getPreviewHeight()) + endif + return [] +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return s:items +endfunction + +" +function s:handler.onOpen(word, mode) + call s:listener.onComplete(a:word, a:mode) +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() +endfunction + +" +function s:handler.onModeLeavePost(opened) + if !a:opened && exists('s:listener.onAbort()') + call s:listener.onAbort() + endif +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/changelist.vim b/fuzzyfinder/autoload/fuf/changelist.vim new file mode 100644 index 0000000..545f6ca --- /dev/null +++ b/fuzzyfinder/autoload/fuf/changelist.vim @@ -0,0 +1,172 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#changelist#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#changelist#getSwitchOrder() + return g:fuf_changelist_switchOrder +endfunction + +" +function fuf#changelist#getEditableDataNames() + return [] +endfunction + +" +function fuf#changelist#renewCache() +endfunction + +" +function fuf#changelist#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#changelist#onInit() + call fuf#defineLaunchCommand('FufChangeList', s:MODE_NAME, '""', []) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:getChangesLines() + redir => result + :silent changes + redir END + return split(result, "\n") +endfunction + +" +function s:parseChangesLine(line) + " return matchlist(a:line, '^\(.\)\s\+\(\d\+\)\s\(.*\)$') + let elements = matchlist(a:line, '\v^(.)\s*(\d+)\s+(\d+)\s+(\d+)\s*(.*)$') + if empty(elements) + return {} + endif + return { + \ 'prefix': elements[1], + \ 'count' : elements[2], + \ 'lnum' : elements[3], + \ 'text' : printf('|%d:%d|%s', elements[3], elements[4], elements[5]), + \ } +endfunction + +" +function s:makeItem(line) + let parsed = s:parseChangesLine(a:line) + if empty(parsed) + return {} + endif + let item = fuf#makeNonPathItem(parsed.text, '') + let item.abbrPrefix = parsed.prefix + let item.lnum = parsed.lnum + return item +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_changelist_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForNonPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + let items = filter(copy(self.items), 'v:val.word ==# a:word') + if empty(items) + return [] + endif + let lines = fuf#getFileLines(self.bufNrPrev) + return fuf#makePreviewLinesAround( + \ lines, [items[0].lnum - 1], a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + call fuf#prejump(a:mode) + let older = 0 + for line in reverse(s:getChangesLines()) + if stridx(line, '>') == 0 + let older = 1 + endif + let parsed = s:parseChangesLine(line) + if !empty(parsed) && parsed.text ==# a:word + if parsed.count != 0 + execute 'normal! ' . parsed.count . (older ? 'g;' : 'g,') . 'zvzz' + endif + break + endif + endfor +endfunction + +" +function s:handler.onModeEnterPre() + let self.items = s:getChangesLines() +endfunction + +" +function s:handler.onModeEnterPost() + call map(self.items, 's:makeItem(v:val)') + call filter(self.items, '!empty(v:val)') + call reverse(self.items) + call fuf#mapToSetSerialIndex(self.items, 1) + call map(self.items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: + diff --git a/fuzzyfinder/autoload/fuf/coveragefile.vim b/fuzzyfinder/autoload/fuf/coveragefile.vim new file mode 100644 index 0000000..1471ef8 --- /dev/null +++ b/fuzzyfinder/autoload/fuf/coveragefile.vim @@ -0,0 +1,199 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#coveragefile#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#coveragefile#getSwitchOrder() + return g:fuf_coveragefile_switchOrder +endfunction + +" +function fuf#coveragefile#getEditableDataNames() + return ['coverages'] +endfunction + +" +function fuf#coveragefile#renewCache() + let s:cache = {} +endfunction + +" +function fuf#coveragefile#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#coveragefile#onInit() + call fuf#defineLaunchCommand('FufCoverageFile', s:MODE_NAME, '""', []) + call l9#defineVariableDefault('g:fuf_coveragefile_name', '') " private option + command! -bang -narg=0 FufCoverageFileRegister call s:registerCoverage() + command! -bang -narg=? FufCoverageFileChange call s:changeCoverage() +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:enumItems() + let key = join([getcwd(), g:fuf_ignoreCase, g:fuf_coveragefile_exclude, + \ g:fuf_coveragefile_globPatterns], "\n") + if !exists('s:cache[key]') + let s:cache[key] = l9#concat(map(copy(g:fuf_coveragefile_globPatterns), + \ 'fuf#glob(v:val)')) + call filter(s:cache[key], 'filereadable(v:val)') " filter out directories + call map(s:cache[key], 'fuf#makePathItem(fnamemodify(v:val, ":~:."), "", 0)') + if len(g:fuf_coveragefile_exclude) + call filter(s:cache[key], 'v:val.word !~ g:fuf_coveragefile_exclude') + endif + call fuf#mapToSetSerialIndex(s:cache[key], 1) + call fuf#mapToSetAbbrWithSnippedWordAsPath(s:cache[key]) + endif + return s:cache[key] +endfunction + +" +function s:registerCoverage() + let patterns = [] + while 1 + let pattern = l9#inputHl('Question', '[fuf] Glob pattern for coverage ( and end):', + \ '', 'file') + if pattern !~ '\S' + break + endif + call add(patterns, pattern) + endwhile + if empty(patterns) + call fuf#echoWarning('Canceled') + return + endif + echo '[fuf] patterns: ' . string(patterns) + let name = l9#inputHl('Question', '[fuf] Coverage name:') + if name !~ '\S' + call fuf#echoWarning('Canceled') + return + endif + let coverages = fuf#loadDataFile(s:MODE_NAME, 'coverages') + call insert(coverages, {'name': name, 'patterns': patterns}) + call fuf#saveDataFile(s:MODE_NAME, 'coverages', coverages) +endfunction + +" +function s:createChangeCoverageListener() + let listener = {} + + function listener.onComplete(name, method) + call s:changeCoverage(a:name) + endfunction + + return listener +endfunction + +" +function s:changeCoverage(name) + let coverages = fuf#loadDataFile(s:MODE_NAME, 'coverages') + if a:name !~ '\S' + let names = map(copy(coverages), 'v:val.name') + call fuf#callbackitem#launch('', 0, '>Coverage>', s:createChangeCoverageListener(), names, 0) + return + else + let name = a:name + endif + call filter(coverages, 'v:val.name ==# name') + if empty(coverages) + call fuf#echoError('Coverage not found: ' . name) + return + endif + call fuf#setOneTimeVariables( + \ ['g:fuf_coveragefile_globPatterns', coverages[0].patterns], + \ ['g:fuf_coveragefile_name' , a:name] + \ ) + FufCoverageFile +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + let nameString = (empty(g:fuf_coveragefile_name) ? '' + \ : '[' . g:fuf_coveragefile_name . ']') + return fuf#formatPrompt(g:fuf_coveragefile_prompt, self.partialMatching, + \ nameString) +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return fuf#makePreviewLinesForFile(a:word, a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + call fuf#openFile(a:word, a:mode, g:fuf_reuseWindow) +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() + " NOTE: Comparing filenames is faster than bufnr('^' . fname . '$') + let bufNamePrev = fnamemodify(bufname(self.bufNrPrev), ':~:.') + let self.items = copy(s:enumItems()) + call filter(self.items, 'v:val.word !=# bufNamePrev') +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/dir.vim b/fuzzyfinder/autoload/fuf/dir.vim new file mode 100644 index 0000000..5316093 --- /dev/null +++ b/fuzzyfinder/autoload/fuf/dir.vim @@ -0,0 +1,132 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#dir#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#dir#getSwitchOrder() + return g:fuf_dir_switchOrder +endfunction + +" +function fuf#dir#getEditableDataNames() + return [] +endfunction + +" +function fuf#dir#renewCache() + let s:cache = {} +endfunction + +" +function fuf#dir#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#dir#onInit() + call fuf#defineLaunchCommand('FufDir' , s:MODE_NAME, '""', []) + call fuf#defineLaunchCommand('FufDirWithFullCwd' , s:MODE_NAME, 'fnamemodify(getcwd(), '':p'')', []) + call fuf#defineLaunchCommand('FufDirWithCurrentBufferDir', s:MODE_NAME, 'expand(''%:~:.'')[:-1-len(expand(''%:~:.:t''))]', []) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:enumItems(dir) + let key = getcwd() . g:fuf_ignoreCase . g:fuf_dir_exclude . "\n" . a:dir + if !exists('s:cache[key]') + let s:cache[key] = fuf#enumExpandedDirsEntries(a:dir, g:fuf_dir_exclude) + call filter(s:cache[key], 'v:val.word =~# ''[/\\]$''') + if isdirectory(a:dir) + call insert(s:cache[key], fuf#makePathItem(a:dir . '.', '', 0)) + endif + call fuf#mapToSetSerialIndex(s:cache[key], 1) + call fuf#mapToSetAbbrWithSnippedWordAsPath(s:cache[key]) + endif + return s:cache[key] +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_dir_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return a:enteredPattern =~# '[^/\\]$' +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForPathTail', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return fuf#makePreviewLinesAround( + \ fuf#glob(fnamemodify(a:word, ':p') . '*'), + \ [], a:count, self.getPreviewHeight()) + return +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return s:enumItems(fuf#splitPath(a:patternPrimary).head) +endfunction + +" +function s:handler.onOpen(word, mode) + execute ':cd ' . fnameescape(a:word) +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/file.vim b/fuzzyfinder/autoload/fuf/file.vim new file mode 100644 index 0000000..1569192 --- /dev/null +++ b/fuzzyfinder/autoload/fuf/file.vim @@ -0,0 +1,139 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#file#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#file#getSwitchOrder() + return g:fuf_file_switchOrder +endfunction + +" +function fuf#file#getEditableDataNames() + return [] +endfunction + +" +function fuf#file#renewCache() + let s:cache = {} +endfunction + +" +function fuf#file#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#file#onInit() + call fuf#defineLaunchCommand('FufFile' , s:MODE_NAME, '""', []) + call fuf#defineLaunchCommand('FufFileWithFullCwd' , s:MODE_NAME, 'fnamemodify(getcwd(), '':p'')', []) + call fuf#defineLaunchCommand('FufFileWithCurrentBufferDir', s:MODE_NAME, 'expand(''%:~:.'')[:-1-len(expand(''%:~:.:t''))]', []) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:enumItems(dir) + let key = join([getcwd(), g:fuf_ignoreCase, g:fuf_file_exclude, a:dir], "\n") + if !exists('s:cache[key]') + let s:cache[key] = fuf#enumExpandedDirsEntries(a:dir, g:fuf_file_exclude) + call fuf#mapToSetSerialIndex(s:cache[key], 1) + call fuf#mapToSetAbbrWithSnippedWordAsPath(s:cache[key]) + endif + return s:cache[key] +endfunction + +" +function s:enumNonCurrentItems(dir, bufNrPrev, cache) + let key = a:dir . 'AVOIDING EMPTY KEY' + if !exists('a:cache[key]') + " NOTE: Comparing filenames is faster than bufnr('^' . fname . '$') + let bufNamePrev = bufname(a:bufNrPrev) + let a:cache[key] = + \ filter(copy(s:enumItems(a:dir)), 'v:val.word !=# bufNamePrev') + endif + return a:cache[key] +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_file_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return a:enteredPattern =~# '[^/\\]$' +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForPathTail', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return fuf#makePreviewLinesForFile(a:word, a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return s:enumNonCurrentItems( + \ fuf#splitPath(a:patternPrimary).head, self.bufNrPrev, self.cache) +endfunction + +" +function s:handler.onOpen(word, mode) + call fuf#openFile(a:word, a:mode, g:fuf_reuseWindow) +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() + let self.cache = {} +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/givencmd.vim b/fuzzyfinder/autoload/fuf/givencmd.vim new file mode 100644 index 0000000..d59178c --- /dev/null +++ b/fuzzyfinder/autoload/fuf/givencmd.vim @@ -0,0 +1,123 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#givencmd#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#givencmd#getSwitchOrder() + return -1 +endfunction + +" +function fuf#givencmd#getEditableDataNames() + return [] +endfunction + +" +function fuf#givencmd#renewCache() +endfunction + +" +function fuf#givencmd#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#givencmd#onInit() +endfunction + +" +function fuf#givencmd#launch(initialPattern, partialMatching, prompt, items) + let s:prompt = (empty(a:prompt) ? '>' : a:prompt) + let s:items = copy(a:items) + call map(s:items, 'fuf#makeNonPathItem(v:val, "")') + call fuf#mapToSetSerialIndex(s:items, 1) + call map(s:items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') + call fuf#launch(s:MODE_NAME, a:initialPattern, a:partialMatching) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(s:prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return 0 +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForNonPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return [] +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return s:items +endfunction + +" +function s:handler.onOpen(word, mode) + if a:word[0] =~# '[:/?]' + call histadd(a:word[0], a:word[1:]) + endif + call feedkeys(a:word . "\", 'n') +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/givendir.vim b/fuzzyfinder/autoload/fuf/givendir.vim new file mode 100644 index 0000000..e654d85 --- /dev/null +++ b/fuzzyfinder/autoload/fuf/givendir.vim @@ -0,0 +1,123 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#givendir#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#givendir#getSwitchOrder() + return -1 +endfunction + +" +function fuf#givendir#getEditableDataNames() + return [] +endfunction + +" +function fuf#givendir#renewCache() +endfunction + +" +function fuf#givendir#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#givendir#onInit() +endfunction + +" +function fuf#givendir#launch(initialPattern, partialMatching, prompt, items) + let s:prompt = (empty(a:prompt) ? '>' : a:prompt) + let s:items = map(copy(a:items), 'substitute(v:val, ''[/\\]\?$'', "", "")') + let s:items = map(s:items, 'fuf#makePathItem(v:val, "", 0)') + call fuf#mapToSetSerialIndex(s:items, 1) + call fuf#mapToSetAbbrWithSnippedWordAsPath(s:items) + call fuf#launch(s:MODE_NAME, a:initialPattern, a:partialMatching) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(s:prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return fuf#makePreviewLinesAround( + \ fuf#glob(fnamemodify(a:word, ':p') . '*'), + \ [], a:count, self.getPreviewHeight()) + return +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return s:items +endfunction + +" +function s:handler.onOpen(word, mode) + execute ':cd ' . fnameescape(a:word) +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/givenfile.vim b/fuzzyfinder/autoload/fuf/givenfile.vim new file mode 100644 index 0000000..5419ff8 --- /dev/null +++ b/fuzzyfinder/autoload/fuf/givenfile.vim @@ -0,0 +1,121 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#givenfile#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#givenfile#getSwitchOrder() + return -1 +endfunction + +" +function fuf#givenfile#getEditableDataNames() + return [] +endfunction + +" +function fuf#givenfile#renewCache() +endfunction + +" +function fuf#givenfile#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#givenfile#onInit() +endfunction + +" +function fuf#givenfile#launch(initialPattern, partialMatching, prompt, items) + let s:prompt = (empty(a:prompt) ? '>' : a:prompt) + let s:items = map(copy(a:items), 'fuf#makePathItem(v:val, "", 0)') + call fuf#mapToSetSerialIndex(s:items, 1) + call map(s:items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') + call fuf#launch(s:MODE_NAME, a:initialPattern, a:partialMatching) +endfunction + + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(s:prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return fuf#makePreviewLinesForFile(a:word, a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return s:items +endfunction + +" +function s:handler.onOpen(word, mode) + call fuf#openFile(a:word, a:mode, g:fuf_reuseWindow) +endfunction + + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/help.vim b/fuzzyfinder/autoload/fuf/help.vim new file mode 100644 index 0000000..8f03e36 --- /dev/null +++ b/fuzzyfinder/autoload/fuf/help.vim @@ -0,0 +1,198 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#help#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#help#getSwitchOrder() + return g:fuf_help_switchOrder +endfunction + +" +function fuf#help#getEditableDataNames() + return [] +endfunction + +" +function fuf#help#renewCache() + let s:cache = {} +endfunction + +" +function fuf#help#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#help#onInit() + call fuf#defineLaunchCommand('FufHelp' , s:MODE_NAME, '""', []) + call fuf#defineLaunchCommand('FufHelpWithCursorWord', s:MODE_NAME, 'expand('''')', []) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:getCurrentHelpTagFiles() + let prefix = 'doc' . l9#getPathSeparator() + let tagFiles = split(globpath(&runtimepath, prefix . 'tags' ), "\n") + \ + split(globpath(&runtimepath, prefix . 'tags-??'), "\n") + return sort(map(tagFiles, 'fnamemodify(v:val, ":p")')) +endfunction + +" +function s:parseHelpTagEntry(line, tagFile) + let elements = split(a:line, "\t") + if len(elements) != 3 || elements[0][0] ==# '!' + return {} + endif + let suffix = matchstr(a:tagFile, '-\zs..$') + if empty(suffix) + let suffix = '@en' + else + let suffix = '@' . suffix + endif + let dir = fnamemodify(a:tagFile, ':h') . l9#getPathSeparator() + return { + \ 'word' : elements[0] . suffix, + \ 'path' : dir . elements[1], + \ 'pattern': elements[2][1:], + \ } +endfunction + +" +function s:getHelpTagEntries(tagFile) + let names = map(l9#readFile(a:tagFile), 's:parseHelpTagEntry(v:val, a:tagFile)') + return filter(names, '!empty(v:val)') +endfunction + +" +function s:parseHelpTagFiles(tagFiles, key) + let cacheName = 'cache-' . l9#hash224(a:key) + let cacheTime = fuf#getDataFileTime(s:MODE_NAME, cacheName) + if cacheTime != -1 && fuf#countModifiedFiles(a:tagFiles, cacheTime) == 0 + return fuf#loadDataFile(s:MODE_NAME, cacheName) + endif + let items = l9#unique(l9#concat(map(copy(a:tagFiles), 's:getHelpTagEntries(v:val)'))) + let items = map(items, 'extend(v:val, fuf#makeNonPathItem(v:val.word, ""))') + call fuf#mapToSetSerialIndex(items, 1) + let items = map(items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') + call fuf#saveDataFile(s:MODE_NAME, cacheName, items) + return items +endfunction + +" +function s:enumHelpTags(tagFiles) + if !len(a:tagFiles) + return [] + endif + let key = join([g:fuf_ignoreCase] + a:tagFiles, "\n") + if !exists('s:cache[key]') || fuf#countModifiedFiles(a:tagFiles, s:cache[key].time) + let s:cache[key] = { + \ 'time' : localtime(), + \ 'items' : s:parseHelpTagFiles(a:tagFiles, key) + \ } + endif + return s:cache[key].items +endfunction + +" +function s:getMatchingIndex(lines, pattern) + if empty(a:pattern) + return -1 + endif + for i in range(len(a:lines)) + if stridx(a:lines[i], a:pattern) >= 0 + return i + endif + endfor + return -1 +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_help_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForNonPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + let items = filter(copy(s:enumHelpTags(self.tagFiles)), 'v:val.word ==# a:word') + if empty(items) + return [] + endif + let lines = fuf#getFileLines(items[0].path) + let index = s:getMatchingIndex(lines, items[0].pattern) + return [items[0].path . ':'] + fuf#makePreviewLinesAround( + \ lines, (index < 0 ? [] : [index]), a:count, self.getPreviewHeight() - 1) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return s:enumHelpTags(self.tagFiles) +endfunction + +" +function s:handler.onOpen(word, mode) + call fuf#openHelp(a:word, a:mode) +endfunction + +" +function s:handler.onModeEnterPre() + let self.tagFiles = s:getCurrentHelpTagFiles() +endfunction + +" +function s:handler.onModeEnterPost() +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/jumplist.vim b/fuzzyfinder/autoload/fuf/jumplist.vim new file mode 100644 index 0000000..ddbb1ab --- /dev/null +++ b/fuzzyfinder/autoload/fuf/jumplist.vim @@ -0,0 +1,182 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#jumplist#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#jumplist#getSwitchOrder() + return g:fuf_jumplist_switchOrder +endfunction + +" +function fuf#jumplist#getEditableDataNames() + return [] +endfunction + +" +function fuf#jumplist#renewCache() +endfunction + +" +function fuf#jumplist#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#jumplist#onInit() + call fuf#defineLaunchCommand('FufJumpList', s:MODE_NAME, '""', []) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:getJumpsLines() + redir => result + :silent jumps + redir END + return split(result, "\n") +endfunction + +" +function s:parseJumpsLine(line, bufnrPrev) + "return matchlist(a:line, '^\(.\)\s\+\(\d\+\)\s\(.*\)$') + let elements = matchlist(a:line, '\v^(.)\s*(\d+)\s+(\d+)\s+(\d+)\s*(.*)$') + if empty(elements) + return {} + endif + let linePrevBuffer = join(getbufline(a:bufnrPrev, elements[3])) + if stridx(linePrevBuffer, elements[5]) >= 0 + let fname = bufname(a:bufnrPrev) + let text = elements[5] + else + let fname = elements[5] + let text = join(getbufline('^' . elements[5] . '$', elements[3])) + endif + return { + \ 'prefix': elements[1], + \ 'count' : elements[2], + \ 'lnum' : elements[3], + \ 'fname' : fname, + \ 'text' : printf('%s|%d:%d|%s', fname, elements[3], elements[4], text), + \ } +endfunction + +" +function s:makeItem(line, bufnrPrev) + let parsed = s:parseJumpsLine(a:line, a:bufnrPrev) + if empty(parsed) + return {} + endif + let item = fuf#makeNonPathItem(parsed.text, '') + let item.abbrPrefix = parsed.prefix + let item.lnum = parsed.lnum + let item.fname = parsed.fname + return item +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_jumplist_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForNonPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + let items = filter(copy(self.items), 'v:val.word ==# a:word') + if empty(items) + return [] + endif + let lines = fuf#getFileLines(items[0].fname) + return fuf#makePreviewLinesAround( + \ lines, [items[0].lnum - 1], a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + call fuf#prejump(a:mode) + let older = 0 + for line in reverse(s:getJumpsLines()) + if stridx(line, '>') == 0 + let older = 1 + endif + let parsed = s:parseJumpsLine(line, self.bufNrPrev) + if !empty(parsed) && parsed.text ==# a:word + if parsed.count != 0 + execute 'normal! ' . parsed.count . (older ? "\" : "\") . 'zvzz' + endif + break + endif + endfor +endfunction + +" +function s:handler.onModeEnterPre() + let self.items = s:getJumpsLines() +endfunction + +" +function s:handler.onModeEnterPost() + call map(self.items, 's:makeItem(v:val, self.bufNrPrev)') + call filter(self.items, '!empty(v:val)') + call reverse(self.items) + call fuf#mapToSetSerialIndex(self.items, 1) + call map(self.items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: + diff --git a/fuzzyfinder/autoload/fuf/line.vim b/fuzzyfinder/autoload/fuf/line.vim new file mode 100644 index 0000000..60447b5 --- /dev/null +++ b/fuzzyfinder/autoload/fuf/line.vim @@ -0,0 +1,135 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#line#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#line#getSwitchOrder() + return g:fuf_line_switchOrder +endfunction + +" +function fuf#line#getEditableDataNames() + return [] +endfunction + +" +function fuf#line#renewCache() +endfunction + +" +function fuf#line#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#line#onInit() + call fuf#defineLaunchCommand('FufLine', s:MODE_NAME, '""', []) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') +let s:OPEN_TYPE_DELETE = -1 + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_line_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForNonPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + let items = filter(copy(self.items), 'v:val.word ==# a:word') + if empty(items) + return [] + endif + let lines = fuf#getFileLines(self.bufNrPrev) + return fuf#makePreviewLinesAround( + \ lines, [items[0].index - 1], a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + call fuf#prejump(a:mode) + call filter(self.items, 'v:val.word ==# a:word') + if empty(self.items) + return + execute 'cc ' . self.items[0].index + endif + call cursor(self.items[0].index, 0) + normal! zvzz +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() + let tab = repeat(' ', getbufvar(self.bufNrPrev, '&tabstop')) + let self.items = getbufline(self.bufNrPrev, 1, '$') + let lnumFormat = '%' . len(string(len(self.items) + 1)) . 'd|' + for i in range(len(self.items)) + let self.items[i] = printf(lnumFormat, i + 1) + \ . substitute(self.items[i], "\t", tab, 'g') + endfor + call map(self.items, 'fuf#makeNonPathItem(v:val, "")') + call fuf#mapToSetSerialIndex(self.items, 1) + call map(self.items, 'fuf#setAbbrWithFormattedWord(v:val, 0)') +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/mrucmd.vim b/fuzzyfinder/autoload/fuf/mrucmd.vim new file mode 100644 index 0000000..58632ce --- /dev/null +++ b/fuzzyfinder/autoload/fuf/mrucmd.vim @@ -0,0 +1,134 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#mrucmd#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#mrucmd#getSwitchOrder() + return g:fuf_mrucmd_switchOrder +endfunction + +" +function fuf#mrucmd#getEditableDataNames() + return ['items'] +endfunction + +" +function fuf#mrucmd#renewCache() +endfunction + +" +function fuf#mrucmd#requiresOnCommandPre() + return 1 +endfunction + +" +function fuf#mrucmd#onInit() + call fuf#defineLaunchCommand('FufMruCmd', s:MODE_NAME, '""', []) +endfunction + +" +function fuf#mrucmd#onCommandPre(cmd) + if getcmdtype() =~# '^[:/?]' + call s:updateInfo(a:cmd) + endif +endfunction + + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:updateInfo(cmd) + let items = fuf#loadDataFile(s:MODE_NAME, 'items') + let items = fuf#updateMruList( + \ items, { 'word' : a:cmd, 'time' : localtime() }, + \ g:fuf_mrucmd_maxItem, g:fuf_mrucmd_exclude) + call fuf#saveDataFile(s:MODE_NAME, 'items', items) +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_mrucmd_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return 0 +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForNonPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return [] +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + call s:updateInfo(a:word) + call histadd(a:word[0], a:word[1:]) + call feedkeys(a:word . "\", 'n') +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() + let self.items = fuf#loadDataFile(s:MODE_NAME, 'items') + call map(self.items, 'fuf#makeNonPathItem(v:val.word, strftime(g:fuf_timeFormat, v:val.time))') + call fuf#mapToSetSerialIndex(self.items, 1) + call map(self.items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/mrufile.vim b/fuzzyfinder/autoload/fuf/mrufile.vim new file mode 100644 index 0000000..f90b9e3 --- /dev/null +++ b/fuzzyfinder/autoload/fuf/mrufile.vim @@ -0,0 +1,234 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#mrufile#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#mrufile#getSwitchOrder() + return g:fuf_mrufile_switchOrder +endfunction + +" +function fuf#mrufile#getEditableDataNames() + return ['items', 'itemdirs'] +endfunction + +" +function fuf#mrufile#renewCache() + let s:cache = {} + let s:aroundCache = {} +endfunction + +" +function fuf#mrufile#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#mrufile#onInit() + call fuf#defineLaunchCommand('FufMruFile', s:MODE_NAME, '""', []) + call fuf#defineLaunchCommand('FufMruFileInCwd', s:MODE_NAME, + \ '""', [['g:fuf_mrufile_underCwd', 1]]) + call l9#defineVariableDefault('g:fuf_mrufile_underCwd', 0) " private option + call l9#defineVariableDefault('g:fuf_mrufile_searchAroundLevel', -1) " private option + augroup fuf#mrufile + autocmd! + autocmd BufEnter * call s:updateData() + autocmd BufWritePost * call s:updateData() + augroup END +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') +let s:OPEN_TYPE_EXPAND = -1 + +" +function s:updateData() + if !empty(&buftype) || !filereadable(expand('%')) + return + endif + let items = fuf#loadDataFile(s:MODE_NAME, 'items') + let items = fuf#updateMruList( + \ items, { 'word' : expand('%:p'), 'time' : localtime() }, + \ g:fuf_mrufile_maxItem, g:fuf_mrufile_exclude) + call fuf#saveDataFile(s:MODE_NAME, 'items', items) + call s:removeItemFromCache(expand('%:p')) + let itemDirs = fuf#loadDataFile(s:MODE_NAME, 'itemdirs') + let itemDirs = fuf#updateMruList( + \ itemDirs, { 'word' : expand('%:p:h') }, + \ g:fuf_mrufile_maxItemDir, g:fuf_mrufile_exclude) + call fuf#saveDataFile(s:MODE_NAME, 'itemdirs', itemDirs) +endfunction + +" +function s:removeItemFromCache(word) + for items in values(s:cache) + if exists('items[a:word]') + unlet items[a:word] + endif + endfor +endfunction + +" returns empty value if invalid item +function s:formatItemUsingCache(item) + if a:item.word !~ '\S' + return {} + endif + if !exists('s:cache[a:item.word]') + if filereadable(a:item.word) + let s:cache[a:item.word] = fuf#makePathItem( + \ fnamemodify(a:item.word, ':p:~'), strftime(g:fuf_timeFormat, a:item.time), 0) + else + let s:cache[a:item.word] = {} + endif + endif + return s:cache[a:item.word] +endfunction + +" +function s:expandSearchDir(dir, level) + let dirs = [a:dir] + let dirPrev = a:dir + for i in range(a:level) + let dirPrev = l9#concatPaths([dirPrev, '*']) + call add(dirs, dirPrev) + endfor + let dirPrev = a:dir + for i in range(a:level) + let dirPrevPrev = dirPrev + let dirPrev = fnamemodify(dirPrev, ':h') + if dirPrevPrev ==# dirPrev + break + endif + call add(dirs, dirPrev) + endfor + return dirs +endfunction + +" +function s:listAroundFiles(dir) + if !exists('s:aroundCache[a:dir]') + let s:aroundCache[a:dir] = [a:dir] + + \ fuf#glob(l9#concatPaths([a:dir, '*' ])) + + \ fuf#glob(l9#concatPaths([a:dir, '.*'])) + call filter(s:aroundCache[a:dir], 'filereadable(v:val)') + call map(s:aroundCache[a:dir], 'fuf#makePathItem(fnamemodify(v:val, ":~"), "", 0)') + if len(g:fuf_mrufile_exclude) + call filter(s:aroundCache[a:dir], 'v:val.word !~ g:fuf_mrufile_exclude') + endif + endif + return s:aroundCache[a:dir] +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + let cwdString = (g:fuf_mrufile_underCwd ? '[CWD]' : '') + let levelString = (g:fuf_mrufile_searchAroundLevel < 0 ? '' + \ : '[Around:' . g:fuf_mrufile_searchAroundLevel . ']') + return fuf#formatPrompt(g:fuf_mrufile_prompt, self.partialMatching, cwdString . levelString) +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return fuf#makePreviewLinesForFile(a:word, a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + if a:mode ==# s:OPEN_TYPE_EXPAND + let nextLevel = (self.searchAroundLevel < 0 ? 0 : self.searchAroundLevel + 1) + call fuf#setOneTimeVariables(['g:fuf_mrufile_searchAroundLevel', nextLevel]) + let self.reservedMode = self.getModeName() + return + else + call fuf#openFile(a:word, a:mode, g:fuf_reuseWindow) + endif +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() + let self.searchAroundLevel = g:fuf_mrufile_searchAroundLevel + call fuf#defineKeyMappingInHandler(g:fuf_mrufile_keyExpand, + \ 'onCr(' . s:OPEN_TYPE_EXPAND . ')') + if self.searchAroundLevel < 0 + let self.items = fuf#loadDataFile(s:MODE_NAME, 'items') + call map(self.items, 's:formatItemUsingCache(v:val)') + else + let self.items = fuf#loadDataFile(s:MODE_NAME, 'itemdirs') + call map(self.items, 's:expandSearchDir(v:val.word, g:fuf_mrufile_searchAroundLevel)') + let self.items = l9#concat(self.items) + let self.items = l9#unique(self.items) + call map(self.items, 's:listAroundFiles(v:val)') + let self.items = l9#concat(self.items) + endif + " NOTE: Comparing filenames is faster than bufnr('^' . fname . '$') + let bufNamePrev = fnamemodify(bufname(self.bufNrPrev), ':p:~') + call filter(self.items, '!empty(v:val) && v:val.word !=# bufNamePrev') + if g:fuf_mrufile_underCwd + let cwd = fnamemodify(getcwd(), ':p:~') + call filter(self.items, 'stridx(v:val.word, cwd) == 0') + endif + call fuf#mapToSetSerialIndex(self.items, 1) + call fuf#mapToSetAbbrWithSnippedWordAsPath(self.items) +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/quickfix.vim b/fuzzyfinder/autoload/fuf/quickfix.vim new file mode 100644 index 0000000..dd5d67c --- /dev/null +++ b/fuzzyfinder/autoload/fuf/quickfix.vim @@ -0,0 +1,154 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#quickfix#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#quickfix#getSwitchOrder() + return g:fuf_quickfix_switchOrder +endfunction + +" +function fuf#quickfix#getEditableDataNames() + return [] +endfunction + +" +function fuf#quickfix#renewCache() +endfunction + +" +function fuf#quickfix#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#quickfix#onInit() + call fuf#defineLaunchCommand('FufQuickfix', s:MODE_NAME, '""', []) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:getJumpsLines() + redir => result + :silent jumps + redir END + return split(result, "\n") +endfunction + +" +function s:parseJumpsLine(line) + return matchlist(a:line, '^\(.\)\s\+\(\d\+\)\s\(.*\)$') +endfunction + +" +function s:makeItem(qfItem) + if !a:qfItem.valid + return {} + endif + let item = fuf#makeNonPathItem( + \ printf('%s|%d:%d|%s', bufname(a:qfItem.bufnr), a:qfItem.lnum, + \ a:qfItem.col, matchstr(a:qfItem.text, '\s*\zs.*\S')) + \ , '') + let item.bufnr = a:qfItem.bufnr + let item.lnum = a:qfItem.lnum + return item +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_quickfix_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForNonPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + let items = filter(copy(self.items), 'v:val.word ==# a:word') + if empty(items) + return [] + endif + let lines = fuf#getFileLines(items[0].bufnr) + return fuf#makePreviewLinesAround( + \ lines, [items[0].lnum - 1], a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + call fuf#prejump(a:mode) + call filter(self.items, 'v:val.word ==# a:word') + if !empty(self.items) + execute 'cc ' . self.items[0].index + endif +endfunction + +" +function s:handler.onModeEnterPre() +endfunction + +" +function s:handler.onModeEnterPost() + let self.items = getqflist() + call map(self.items, 's:makeItem(v:val)') + call fuf#mapToSetSerialIndex(self.items, 1) + call filter(self.items, 'exists("v:val.word")') + call map(self.items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: + diff --git a/fuzzyfinder/autoload/fuf/tag.vim b/fuzzyfinder/autoload/fuf/tag.vim new file mode 100644 index 0000000..362cabf --- /dev/null +++ b/fuzzyfinder/autoload/fuf/tag.vim @@ -0,0 +1,178 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#tag#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#tag#getSwitchOrder() + return g:fuf_tag_switchOrder +endfunction + +" +function fuf#tag#getEditableDataNames() + return [] +endfunction + +" +function fuf#tag#renewCache() + let s:cache = {} +endfunction + +" +function fuf#tag#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#tag#onInit() + call fuf#defineLaunchCommand('FufTag' , s:MODE_NAME, '""', []) + call fuf#defineLaunchCommand('FufTagWithCursorWord', s:MODE_NAME, 'expand('''')', []) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:getTagNames(tagFile) + let names = map(l9#readFile(a:tagFile), 'matchstr(v:val, ''^[^!\t][^\t]*'')') + return filter(names, 'v:val =~# ''\S''') +endfunction + +" +function s:parseTagFiles(tagFiles, key) + let cacheName = 'cache-' . l9#hash224(a:key) + let cacheTime = fuf#getDataFileTime(s:MODE_NAME, cacheName) + if cacheTime != -1 && fuf#countModifiedFiles(a:tagFiles, cacheTime) == 0 + return fuf#loadDataFile(s:MODE_NAME, cacheName) + endif + let items = l9#unique(l9#concat(map(copy(a:tagFiles), 's:getTagNames(v:val)'))) + let items = map(items, 'fuf#makeNonPathItem(v:val, "")') + call fuf#mapToSetSerialIndex(items, 1) + let items = map(items, 'fuf#setAbbrWithFormattedWord(v:val, 1)') + call fuf#saveDataFile(s:MODE_NAME, cacheName, items) + return items +endfunction + +" +function s:enumTags(tagFiles) + if !len(a:tagFiles) + return [] + endif + let key = join([g:fuf_ignoreCase] + a:tagFiles, "\n") + if !exists('s:cache[key]') || fuf#countModifiedFiles(a:tagFiles, s:cache[key].time) + let s:cache[key] = { + \ 'time' : localtime(), + \ 'items' : s:parseTagFiles(a:tagFiles, key) + \ } + endif + return s:cache[key].items +endfunction + +" +function s:getMatchingIndex(lines, cmd) + if a:cmd !~# '\D' + return str2nr(a:cmd) + endif + let pattern = matchstr(a:cmd, '^\/\^\zs.*\ze\$\/$') + if empty(pattern) + return -1 + endif + for i in range(len(a:lines)) + if a:lines[i] ==# pattern + return i + endif + endfor + return -1 +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_tag_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForNonPath', + \ self.partialMatching) +endfunction + +" 'cmd' is '/^hoge hoge$/' or line number +function s:handler.makePreviewLines(word, count) + let tags = taglist('^' . a:word . '$') + if empty(tags) + return [] + endif + let i = a:count % len(tags) + let title = printf('(%d/%d) %s', i + 1, len(tags), tags[i].filename) + let lines = fuf#getFileLines(tags[i].filename) + let index = s:getMatchingIndex(lines, tags[i].cmd) + return [title] + fuf#makePreviewLinesAround( + \ lines, (index < 0 ? [] : [index]), 0, self.getPreviewHeight() - 1) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return s:enumTags(self.tagFiles) +endfunction + +" +function s:handler.onOpen(word, mode) + call fuf#openTag(a:word, a:mode) +endfunction + +" +function s:handler.onModeEnterPre() + let self.tagFiles = fuf#getCurrentTagFiles() +endfunction + +" +function s:handler.onModeEnterPost() + let &l:tags = join(self.tagFiles, ',') +endfunction + +" +function s:handler.onModeLeavePost(opened) + let &l:tags = '' +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/autoload/fuf/taggedfile.vim b/fuzzyfinder/autoload/fuf/taggedfile.vim new file mode 100644 index 0000000..74652fc --- /dev/null +++ b/fuzzyfinder/autoload/fuf/taggedfile.vim @@ -0,0 +1,159 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +"============================================================================= +" LOAD GUARD {{{1 + +if !l9#guardScriptLoading(expand(':p'), 0, 0, []) + finish +endif + +" }}}1 +"============================================================================= +" GLOBAL FUNCTIONS {{{1 + +" +function fuf#taggedfile#createHandler(base) + return a:base.concretize(copy(s:handler)) +endfunction + +" +function fuf#taggedfile#getSwitchOrder() + return g:fuf_taggedfile_switchOrder +endfunction + +" +function fuf#taggedfile#getEditableDataNames() + return [] +endfunction + +" +function fuf#taggedfile#renewCache() + let s:cache = {} +endfunction + +" +function fuf#taggedfile#requiresOnCommandPre() + return 0 +endfunction + +" +function fuf#taggedfile#onInit() + call fuf#defineLaunchCommand('FufTaggedFile', s:MODE_NAME, '""', []) +endfunction + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS/VARIABLES {{{1 + +let s:MODE_NAME = expand(':t:r') + +" +function s:getTaggedFileList(tagfile) + execute 'cd ' . fnamemodify(a:tagfile, ':h') + let result = map(l9#readFile(a:tagfile), 'matchstr(v:val, ''^[^!\t][^\t]*\t\zs[^\t]\+'')') + call map(l9#readFile(a:tagfile), 'fnamemodify(v:val, ":p")') + cd - + call map(l9#readFile(a:tagfile), 'fnamemodify(v:val, ":~:.")') + return filter(result, 'v:val =~# ''[^/\\ ]$''') +endfunction + +" +function s:parseTagFiles(tagFiles, key) + let cacheName = 'cache-' . l9#hash224(a:key) + let cacheTime = fuf#getDataFileTime(s:MODE_NAME, cacheName) + if cacheTime != -1 && fuf#countModifiedFiles(a:tagFiles, cacheTime) == 0 + return fuf#loadDataFile(s:MODE_NAME, cacheName) + endif + let items = l9#unique(l9#concat(map(copy(a:tagFiles), 's:getTaggedFileList(v:val)'))) + call map(items, 'fuf#makePathItem(v:val, "", 0)') + call fuf#mapToSetSerialIndex(items, 1) + call fuf#mapToSetAbbrWithSnippedWordAsPath(items) + call fuf#saveDataFile(s:MODE_NAME, cacheName, items) + return items +endfunction + +" +function s:enumTaggedFiles(tagFiles) + if !len(a:tagFiles) + return [] + endif + let key = join([getcwd(), g:fuf_ignoreCase] + a:tagFiles, "\n") + if !exists('s:cache[key]') || fuf#countModifiedFiles(a:tagFiles, s:cache[key].time) + let s:cache[key] = { + \ 'time' : localtime(), + \ 'items' : s:parseTagFiles(a:tagFiles, key) + \ } + endif + return s:cache[key].items +endfunction + +" }}}1 +"============================================================================= +" s:handler {{{1 + +let s:handler = {} + +" +function s:handler.getModeName() + return s:MODE_NAME +endfunction + +" +function s:handler.getPrompt() + return fuf#formatPrompt(g:fuf_taggedfile_prompt, self.partialMatching, '') +endfunction + +" +function s:handler.getPreviewHeight() + return g:fuf_previewHeight +endfunction + +" +function s:handler.isOpenable(enteredPattern) + return 1 +endfunction + +" +function s:handler.makePatternSet(patternBase) + return fuf#makePatternSet(a:patternBase, 's:interpretPrimaryPatternForPath', + \ self.partialMatching) +endfunction + +" +function s:handler.makePreviewLines(word, count) + return fuf#makePreviewLinesForFile(a:word, a:count, self.getPreviewHeight()) +endfunction + +" +function s:handler.getCompleteItems(patternPrimary) + return self.items +endfunction + +" +function s:handler.onOpen(word, mode) + call fuf#openFile(a:word, a:mode, g:fuf_reuseWindow) +endfunction + +" +function s:handler.onModeEnterPre() + let self.tagFiles = fuf#getCurrentTagFiles() +endfunction + +" +function s:handler.onModeEnterPost() + " NOTE: Comparing filenames is faster than bufnr('^' . fname . '$') + let bufNamePrev = fnamemodify(bufname(self.bufNrPrev), ':p:~:.') + " NOTE: Don't do this in onModeEnterPre() + " because that should return in a short time. + let self.items = copy(s:enumTaggedFiles(self.tagFiles)) + call filter(self.items, 'v:val.word !=# bufNamePrev') +endfunction + +" +function s:handler.onModeLeavePost(opened) +endfunction + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/fuzzyfinder/doc/fuf.jax b/fuzzyfinder/doc/fuf.jax new file mode 100644 index 0000000..403872f --- /dev/null +++ b/fuzzyfinder/doc/fuf.jax @@ -0,0 +1,1405 @@ +*fuf.jax* バッファ/ファイル/その他を、あいまい検索 + + Copyright (c) 2007-2010 Takeshi NISHIDA + +FuzzyFinder *fuzzyfinder* *fuf* + +概要 |fuf-introduction| +インストール |fuf-installation| +使い方 |fuf-usage| +モード |fuf-modes| +詳細なトピック |fuf-detailed-topics| +コマンド |fuf-commands| +オプション |fuf-options| +VIMRC の例 |fuf-vimrc-example| +SPECIAL THANKS |fuf-thanks| +CHANGELOG |fuf-changelog| +あばうと |fuf-about| + +============================================================================== +概要 *fuf-introduction* + +FuzzyFinder はバッファ/ファイル/コマンド/ブックマーク/タグに素早くアクセスする +ための手段を提供します。入力されたパターンから変換されたあいまいパターンまたは +部分一致パターンで検索を行います。 + + 入力パターン あいまいパターン 部分一致パターン ~ +> + abc *a*b*c* *abc* + dir/file dir/*f*i*l*e* dir/*file* + d*r/file d*r/*f*i*l*e* d*r/*file* + ../**/s ../**/*s* ../**/*s* + (** : 再帰検索) +< +次のような場面で有用です: + + "./AhLongLongLongLongLongFile.txt" + "./AhLongLongLongLongLongName.txt" + "./OhLongLongLongLongLongFile.txt" + "./OhLongLongLongLongLongName.txt" <- 欲しいファイル :-O + +"ON" と入力すれば "OhLongLongLongLongLongName.txt" が選択できます. :-D + +FuzzyFinder が検索できる対象は次の通りです: + - バッファ + - ファイル + - ディレクトリ + - 最近使ったファイル + - 最近使ったファイルの近くのファイル + - 最近使ったコマンドライン + - ブックマークされたファイル + - ブックマークされたディレクトリ + - タグ + - タグファイルに含まれるファイル + - ジャンプリスト + - チェンジリスト + - バッファの行 + - quickfix + - ヘルプ + +FuzzyFinder は ファイルを検索したりアイテムを選択するシステムを利用するための +API も提供します。 + +FuzzyFinder はマルチバイト文字をサポートしています。 + + +============================================================================== +インストール *fuf-installation* + +ZIPファイルをランタイムディレクトリに展開します。 + +以下のようにファイルが配置されるはずです。 +> + <ランタイムディレクトリ>/plugin/fuf.vim + <ランタイムディレクトリ>/doc/fuf.txt + ... +< +もしランタイムディレクトリが多数のプラグインでごちゃごちゃになるのが嫌なら、各 +プラグインを個別のディレクトリに配置し、そのディレクトリのパスを 'runtimepath' +に追加してください。アンインストールも楽になります。 + +その後、ヘルプを有効にするためにタグファイルを更新してください。詳しくは +|add-local-help|を参照してください。 + +必要なもの: ~ + +- L9 library (vimscript #3252) + + +============================================================================== +使い方 *fuf-usage* + +次のコマンドで FuzzyFinder を起動します: + + コマンド モード ~ + |:FufBuffer| - Buffer モード (|fuf-buffer-mode|) + |:FufFile| - File モード (|fuf-file-mode|) + |:FufCoverageFile| - Coverage-File モード (|fuf-coveragefile-mode|) + |:FufDir| - Directory モード (|fuf-dir-mode|) + |:FufMruFile| - MRU-File モード (|fuf-mrufile-mode|) + |:FufMruCmd| - MRU-Command モード (|fuf-mrucmd-mode|) + |:FufBookmarkFile| - Bookmark-File モード (|fuf-bookmarkfile-mode|) + |:FufBookmarkDir| - Bookmark-Dir モード (|fuf-bookmarkdir-mode|) + |:FufTag| - Tag モード (|fuf-tag-mode|) + |:FufBufferTag| - Buffer-Tag モード (|fuf-buffertag-mode|) + |:FufTaggedFile| - Tagged-File モード (|fuf-taggedfile-mode|) + |:FufJumpList| - Jump-List モード (|fuf-jumplist-mode|) + |:FufChangeList| - Change-List モード (|fuf-changelist-mode|) + |:FufQuickfix| - Quickfix モード (|fuf-quickfix-mode|) + |:FufLine| - Line モード (|fuf-line-mode|) + |:FufHelp| - Help モード (|fuf-help-mode|) + +これらのコマンドを押しやすいキーにマッピングすることを推奨します。 + +これらのコマンドを実行するとパターンを入力するための1行のバッファを開き、イン +サートモードを開始します。 + +FuzzyFinder は入力されたパターンにマッチするアイテムを検索し、それを補完メニュ +ーに表示します。パターンマッチングの詳細は|fuf-search-patterns|を参照してくだ +さい。 + +多くのアイテムがマッチングする場合、FuzzyFinder はレスポンスを向上させるために +列挙するアイテムの数(|g:fuf_enumeratingLimit|)を制限し、その際、入力されたパタ +ーンを"Error" グループでハイライトします。 + +補完メニューの最初のアイテムは自動的に選択状態になります。 + + で入力パターンからカーソル前の、ディレクトリ名などのひとかたまりを削除す +ることができます。 + + (|g:fuf_keyPrevPattern|) と (|g:fuf_keyNextPattern|) で、履歴から +過去に入力したパターンを呼び出すことができます。 + +いろいろな方法で、選択されたアイテムを開くことができます: + + (|g:fuf_keyOpen|) - 直前のウィンドウで開きます。 + (|g:fuf_keyOpenSplit|) - ウィンドウを分割して開きます。 + (|g:fuf_keyOpenVsplit|) - ウィンドウを垂直分割して開きます。 + (|g:fuf_keyOpenTabpage|) - 別のタブページで開きます。 + +キャンセルして直前のウィンドウに戻るには、インサートモードを抜けてください。 + + (|g:fuf_keySwitchMatching|) で、検索方法をあいまいマッチングまたは +部分一致マッチングに交互に切り替えることができます。 + + (|g:fuf_keyNextMode|) と (|g:fuf_keyPrevMode|) で、インサートモー +ドを抜けることなくカレントモードを切り替えることが出来ます。 + +いくつかのモードでは、選択されたアイテムを (|g:fuf_keyPreview|) でプレビ +ューすることができます。同じアイテムでキーを繰り返すことで別の情報を表示させる +ことができます。プレビューをサポートするモードを起動すると、コマンドラインの高 +さが|g:fuf_previewHeight|になります。この機能は|g:fuf_previewHeight|が0ではな +い場合、有効になります。 + + +============================================================================== +モード *fuf-modes* + + *fuf-buffer-mode* +Buffer モード ~ + +このモードはバッファを選択して開くインターフェースを提供します。 + +Buffer モード中に (|g:fuf_buffer_keyDelete|) を押すと選択したバッファを +削除することができます。 + + *fuf-file-mode* +File モード ~ + +このモードはファイルツリーからファイルを検索して開くインターフェースを提供しま +す。 + + *fuf-coveragefile-mode* +Coverage-File モード ~ + +このモードはあらかじめ設定した検索対象の全ファイルからファイルを選択して開くイ +ンターフェースを提供します。 + +デフォルトではカレントディレクトリ以下の全ファイルを列挙します。 +(|g:fuf_coveragefile_globPatterns|) + +他の検索対象を検索したい場合、|FufCoverageFileRegister|コマンドで新しい検索対 +象を登録し、|FufCoverageFileChange|コマンドで検索対象を選択して Coverage-File +モードを起動します。 + +加えて、|fuf#setOneTimeVariables()|関数を使う方法もあります。 + + +例: .hと.cファイルだけ検索する: +> + call fuf#setOneTimeVariables(['g:fuf_coveragefile_globPatterns', ['**/*.h', '**/*.c']]) + \ | FufCoverageFile +< +例: デフォルトの検索対象に加えてホームディレクトリも検索する: +> + call fuf#setOneTimeVariables(['g:fuf_coveragefile_globPatterns', g:fuf_coveragefile_globPatterns + ['~/**/.*', '~/**/*']]) + \ | FufCoverageFile +< + + *fuf-dir-mode* +Directory モード ~ + +このモードはファイルツリーからディレクトリを検索してカレントディレクトリを変更 +するインターフェースを提供します。 + + *fuf-mrufile-mode* +MRU-File モード ~ + +このモードは最近使ったファイルを選択して開くインターフェースを提供します。 + +このモード中に (|g:fuf_mrufile_keyExpand|) を押すと、最近使ったファイル +の付近にあるファイルを検索します。このキーを押す毎に、検索範囲をディレクトリツ +リーの上下に1階層ずつ広げます。 + +|BufEnter| と |BufWritePost| で行う処理がパフォーマンス上の問題を起こしうるの +で、デフォルトでは無効化するモードに指定されています。(|g:fuf_modesDisable|) + +See also: |FufMruFileInCwd| + + *fuf-mrucmd-mode* +MRU-Command モード ~ + +このモードは最近使ったコマンドラインを選択して開くインターフェースを提供します +。 +このモードに必要な、コマンドラインモードの のマッピングに副作用があるので、 +、デフォルトでは無効化するモードに指定されています。(|g:fuf_modesDisable|) + + *fuf-bookmarkfile-mode* +Bookmark-File モード ~ + +このモードは事前に追加したブックマークを選択してその行へジャンプするインターフ +ェースを提供します。 + +|:FufBookmarkFileAdd|コマンドでカーソルのある行をブックマークに追加できます。 +このコマンドを実行すると、ブックマーク名の入力を求められます。 + +FuzzyFinder はジャンプする行番号を調整します。ブックマークされた行がブックマー +クされたときのパターンとマッチしない場合、FuzzyFinder はブックマークされた位置 +の周辺でマッチする行を探します。なのでブックマークした行が多少移動していたとし +ても、そこでジャンプすることができます。ブックマークした行番号へ調整せずにジャ +ンプしたい場合、|g:fuf_bookmarkfile_searchRange|を 0 に設定してください。 + +このモード中に (|g:fuf_bookmarkfile_keyDelete|) を押すと選択したブックマ +ークを削除することができます。 + + *fuf-bookmarkdir-mode* +Bookmark-Dir モード ~ + +このモードは事前に追加したブックマークを選択してカレントディレクトリを変更する +するインターフェースを提供します。 + +|:FufBookmarkDirAdd|コマンドでディレクトリをブックマークに追加できます。このコ +マンドを実行すると、ディレクトリのパスとブックマーク名の入力を求められます。 + +このモード中に (|g:fuf_bookmarkdir_keyDelete|) を押すと選択したブックマ +ークを削除することができます。 + + *fuf-tag-mode* +Tag モード ~ + +このモードはタグを選択してその定義へジャンプするインターフェースを提供します。 + +以下は を置き換えるマッピングです。 +> + noremap :FufTagWithCursorWord! +< + + *fuf-buffertag-mode* +Buffer-Tag モード ~ + +このモードはカレントバッファまたは全バッファのタグを選択してその定義へジャンプ +するインターフェースを提供します。 + +タグのリストはFuzzyFinderの起動時にその場で作成されるので、前もってtagsファイ +ルを作成する必要はありません。 + +|FufBufferTag|はカレントバッファを対象にし、|FufBufferTagAll|は全バッファを対 +象にします。 + +以下は を置き換えるマッピングです: +> + nnoremap :FufBufferTagWithCursorWord! + vnoremap :FufBufferTagAllWithSelectedText! +< +または +> + nnoremap :FufBufferTagAllWithCursorWord! + vnoremap :FufBufferTagAllWithSelectedText! +< +このモードは taglist.vim (vimscript #273) にインスパイアされました。コードも参 +考にしています。 + + *fuf-taggedfile-mode* +Tagged-File モード ~ + +このモードはタグファイルに含まれるファイルを選択して開くインターフェースを提供 +します。 + + *fuf-jumplist-mode* +Jump-List モード ~ + +このモードはカレントウィンドウの|jumplist|から選択した位置へジャンプするインタ +ーフェースを提供します。 + + *fuf-changelist-mode* +Change-List モード ~ + +このモードはカレントバッファの|changelist|から選択した位置へジャンプするインタ +ーフェースを提供します。 + + *fuf-quickfix-mode* +Quickfix モード ~ + +このモードは|quickfix|リストから選択した位置へジャンプするインターフェースを提 +供します。 + + *fuf-line-mode* +Line モード ~ + +このモードはカレントバッファの行を選択してジャンプするインターフェースを提供し +ます。 + + *fuf-help-mode* +Help モード ~ + +このモードはヘルプタグを選択してそのヘルプページへジャンプするインターフェース +を提供します。 + + *fuf-givenfile-mode* +Given-File モード ~ + +このモードは与えられたリストから選択されたファイルを開く API を提供します。 + +API 関数: +> + function fuf#givenfile#launch( + \ initialPattern, partialMatching, prompt, items) +< + initialPattern - FuzzyFinder 起動直後に挿入される文字列 + partialMatching - あいまい検索ではなく部分一致検索を行うか + prompt - プロンプト文字列 + items - アイテムのリスト + +利用例: +> + " ドットファイルを開く + call fuf#givenfile#launch('', 0, '>', split(glob('~/.*'), "\n")) +< + + *fuf-givendir-mode* +Given-Directory モード ~ + +このモードは与えられたリストから選択されたディレクトリにカレントディレクトリを +変更する API を提供します。 + +API 関数: +> + function fuf#givendir#launch( + \ initialPattern, partialMatching, prompt, items) +< + initialPattern - FuzzyFinder 起動直後に挿入される文字列 + partialMatching - あいまい検索ではなく部分一致検索を行うか + prompt - プロンプト文字列 + items - アイテムのリスト + + +利用例: +> + " ランタイムディレクトリのどれかをカレントディレクトリにする + call fuf#givendir#launch('', 0, '>', split(&runtimepath, ',')) +< + + *fuf-givencmd-mode* +Given-Command モード ~ + +このモードは与えられたリストから選択されたコマンドを実行する API を提供します。 + +選択されたコマンドは feedkeys() によって実行されるので、ノーマルモードでの一連 +のキー入力をエミュレートさせることも可能です。 + +API 関数: +> + function fuf#givencmd#launch( + \ initialPattern, partialMatching, prompt, items) +< + initialPattern - FuzzyFinder 起動直後に挿入される文字列 + partialMatching - あいまい検索ではなく部分一致検索を行うか + prompt - プロンプト文字列 + items - アイテムのリスト + + +利用例: +> + function GetAllCommands() + redir => commands + silent command + redir END + return map((split(commands, "\n")[3:]), + \ '":" . matchstr(v:val, ''^....\zs\S*'')') + endfunction + + " ユーザー定義コマンドを選択して実行 + call fuf#givencmd#launch('', 0, '>', GetAllCommands()) + +< + + *fuf-callbackfile-mode* +Callback-File モード ~ + +このモードはファイルを検索して選択されたファイルパスを得る API を提供します。 + +API 関数: +> + function fuf#callbackfile#launch( + \ initialPattern, partialMatching, prompt, exclude, listener) +< + initialPattern - FuzzyFinder 起動直後に挿入される文字列 + partialMatching - あいまい検索ではなく部分一致検索を行うか + prompt - プロンプト文字列 + exclude - 補完リストから除外したいアイテムの正規表現パターン + listener - 'onComplete' と 'onAbort' を持つ|Dictionary|。これ + らは FuzzyFinder 終了時に呼ばれます。 + listener.onComplete(item, method) は選択が完了したと + き、選択されたアイテム名とオープン方式番号の2引数と + 共に呼ばれます。listener.onAbort() は選択を中止した + ときに呼ばれます。 + +利用例: +> + let listener = {} + + function listener.onComplete(item, method) + echo "Item: " . a:item . "\nMethod: " . a:method + endfunction + + function listener.onAbort() + echo "Abort" + endfunction + + " カレントディレクトリからファイルを選択 + call fuf#callbackfile#launch('', 0, '>', '', listener) + + " ホームディレクトリからファイルを選択 + call fuf#callbackfile#launch('~/', 0, '>', '', listener) +< + + *fuf-callbackitem-mode* +Callback-Item モード ~ + +このモードは与えられたリストから選択されたアイテムを得るための API を提供しま +す。 + +API 関数: +> + function fuf#callbackitem#launch( + \ initialPattern, partialMatching, prompt, listener, items, forPath) +< + initialPattern - FuzzyFinder 起動直後に挿入される文字列 + partialMatching - あいまい検索ではなく部分一致検索を行うか + prompt - プロンプト文字列 + listener - 'onComplete' と 'onAbort' を持つ|Dictionary|。これ + らは FuzzyFinder 終了時に呼ばれます。 + listener.onComplete(item, method) は選択が完了したと + き、選択されたアイテム名とオープン方式番号の2引数と + 共に呼ばれます。listener.onAbort() は選択を中止した + ときに呼ばれます。 + items - アイテムのリスト + forPath - ファイル選択に特化したマッチングを利用するか + +利用例: +> + let listener = {} + + function listener.onComplete(item, method) + echo "Item: " . a:item . "\nMethod: " . a:method + endfunction + + function listener.onAbort() + echo "Abort" + endfunction + + " 与えられたリストからアイテムを選択 + call fuf#callbackitem#launch('', 0, '>', listener, ['ed', 'vi', 'vim'], 0) + + " 与えられたリストからファイルを選択 + call fuf#callbackitem#launch('', 0, '>', listener, ['../foo/bar', 'baz'], 1) +< + +============================================================================== +詳細なトピック *fuf-detailed-topics* + + *fuf-setting-one-time-option* *fuf#setOneTimeVariables()* +一回限りのオプションの設定 ~ + +次回の FuzzyFinder 用に一回限りのオプションを設定したいとき、 +|fuf#setOneTimeVariables()|関数が役に立ちます。この関数は次のようにして使いま +す: +> + call fuf#setOneTimeVariables(['g:fuf_ignoreCase', 0], ['&lines', 50]) +< +この関数は 0 個以上の引数を取り、各々は変数名と値のペアです。指定されたオプシ +ョンは次回 FuzzyFinder が起動したときに実際に設定され、終了するときに復元され +ます。 + + *fuf-search-patterns* +検索パターン ~ + +検索パターンとして、一つのプライマリパターンと0個以上の絞り込みパターンを入力 +することができます。入力パターンは ";" (|g:fuf_patternSeparator|) で区切られ、 +最初のパターンがプライマリパターンになり、残りのパターンが絞り込みパターンにな +ります。 +> + プライマリ 絞り込み 絞り込み + |----------| |-------| |----| + >MruFile>bookmark.vim;autoload/;/home/ +< +プライマリパターンにマッチしたアイテムのリストを別のパターンで絞り込むために、 +絞り込みパターンを利用します。 + +プライマリパターンでは、あいまいマッチングと部分一致マッチングのうち、選択され +た方を行います。絞り込みパターンでは、デフォルトで部分一致マッチングを行います +。(|g:fuf_fuzzyRefining|) + +絞り込みパターンとして数値を入力した場合、アイテムのインデックスに対しても +マッチングします。 + +ファイルパスの静的な集合を対象とするモード (Buffer, MRU-File モードなど。File, +Directory モードなどではない) で|g:fuf_splitPathMatching|が真の場合、プライマ +リパターンのマッチングは head 部とtail 部に分けて行われます。 +> + head tail + |------||-----| + foo/bar/baz.vim + + あいまいマッチング例: + +----------------+---------+---------+---------+ + | item \ pattern | foo/bar | foo/ | bar | + +----------------+---------+---------+---------+ + | foo/bar | match | match | match | + | foo/abc | unmatch | match | unmatch | + | abc/bar | unmatch | unmatch | match | + | foobar | unmatch | unmatch | match | + | foooo/barrrr | match | match | match | + | foooo/fooooo | unmatch | match | unmatch | + +----------------+---------+---------+---------+ +< +上記のケースで、絞り込みパターンはパス全体に対してマッチングできます。 + + *fuf-sorting-of-completion-items* +補完アイテムのソート ~ + +FuzzyFinder は幾つかのルールに従って補完アイテムをソートします。 + +パターン全体が一部分にぴったりマッチするアイテムは優先されます。例えば、パター +ン "bc" ではアイテム "abc" は "bac" より優先されます。 + +このケースで、マッチする部分が先頭であるアイテムはそうでないアイテムより優先さ +れます。例えばパターン "foo" ではアイテム "foobar" は"barfoo" より優先されます +。 + +マッチング位置より後の文字数が少ないほど優先されます。例えばパターン "bar" で +はアイテム"foobar" は"foobarbaz"より優先されます。 + +単語の境界文字にだけマッチングするアイテムは優先されます。 例えば、パターン +"fb" ではアイテム"fooBarBaz" や "foo_bar_baz" などが優先されます。 + +加えて、FuzzyFinder には学習システムがあります。現在のパターンで、過去に補完さ +れたことのあるアイテムを優先します。 + + *fuf-reusing-window* +目的のバッファ/ファイルが開かれているウィンドウの再利用 ~ + +ウィンドウを分割してバッファ/ファイルを開くときに、現在のタブページでそれが開 +かれているウィンドウが見つかった場合、そこへ移動します。別のタブページでバッフ +ァ/ファイルを開くときに、他のタブページでそれが開かれているウィンドウが見つか +った場合、そこへ移動します。 + +常にバッファ/ファイルを新ウィンドウで開きたい場合、'reuse_window'オプションで +この機能を無効にすることができます。 + + *fuf-hiding-menu* +補完メニューの一時非表示 ~ + + で補完メニューを閉じることができます。また、で再度開くことがで +きます。 + + *fuf-abbreviation* *fuf-multiple-search* +短縮入力及び複合検索 ~ + +|g:fuf_abbrevMap|を設定することで、全モードで短縮入力と複合検索が利用できます。 + +例えば次のように設定したとします: +> + let g:fuf_abbrevMap = { + \ "^doc:" : [ + \ "~/project/**/doc/", + \ ".vim/doc/", + \ ], + \ } +< +そして File モードで "doc:txt" と入力すると、次の2つのパターンの検索結果を複合 +します: + + "~/project/**/doc/*t*x*t*" + ".vim/doc/*t*x*t*" + + *fuf-data-file* +データファイル ~ + +FuzzyFinder は補完統計、MRUデータ、ブックマークなどを|g:fuf_dataDir|以下のファ +イルに書き込みます。 + +|:FufEditDataFile|コマンドはデータファイルの編集を補助します。このコマンドを実 +行すると、データファイルを無名バッファに読み込みます。:write などで書き込みを +行うと、データファイルを更新します。 + + *fuf-cache* +キャッシュ ~ + +一旦キャッシュが生成されると、レスポンスを向上させるため自動的には更新されませ +ん。これを更新するには|:FufRenewCache|コマンドを実行してください。 + + *fuf-dot-sequence* +ドット列で親ディレクトリへ移動 ~ + +ドット列を入力することで親ディレクトリを上がっていくことができます。パス区切り +文字直後のドット列は "../" の列に展開されます。 + + ドット列 展開パターン ~ + /.. /../ + /... /../../ + /.... /../../../ + + *fuf-how-to-add-mode* +モードの追加方法 ~ + +"mymode" モードを追加するには、ソースファイルを autoload/fuf/mymode.vim に置き +、 fuf#addMode('mymode') を呼びます。 + + *fuf-migemo* +Migemo とは ~ + +以下のページを参照してください。 + - http://0xcc.net/migemo/ + - http://www.kaoriya.net/#CMIGEMO + + +============================================================================== +コマンド *fuf-commands* + +See also: |fuf-vimrc-example| + + *:FufBuffer* +:FufBuffer [{pattern}] + Buffer モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufFile* +:FufFile [{pattern}] + File モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufFileWithFullCwd* +:FufFileWithFullCwd [{pattern}] + カレントディレクトリのフルパスを初期パターンとする以外は|:FufFile|と同 + じです。 + + *:FufFileWithCurrentBufferDir* +:FufFileWithCurrentBufferDir [{pattern}] + カレントバッファのディレクトリを初期パターンとする以外は|:FufFile|と同 + じです。 + + *:FufDir* +:FufDir [{pattern}] + Directory モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufDirWithFullCwd* +:FufDirWithFullCwd [{pattern}] + カレントディレクトリのフルパスを初期パターンとする以外は|:FufDir|と同 + じです。 + + *:FufDirWithCurrentBufferDir* +:FufDirWithCurrentBufferDir [{pattern}] + カレントバッファのディレクトリを初期パターンとする以外は|:FufDir|と同 + じです。 + + *:FufMruFile* +:FufMruFile [{pattern}] + MRU-File モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufMruFileInCwd* +:FufMruFileInCwd [{pattern}] + カレントディレクトリ内のファイルのみを対象とする以外は + |:FufMruFile|と同じです。 + + *:FufMruCmd* +:FufMruCmd [{pattern}] + MRU-Command モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufBookmarkFile* +:FufBookmarkFile [{pattern}] + Bookmark-File モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufBookmarkDir* +:FufBookmarkDir [{pattern}] + Bookmark-Dir モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufTag* +:FufTag [{pattern}] + Tag モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufTagWithCursorWord* +:FufTagWithCursorWord [{pattern}] + カーソル下の単語を初期パターンとする以外は|:FufTag|と同じです。 + + *:FufBufferTag* +:FufBufferTag[!] [{pattern}] + Buffer-Tag モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufBufferTagAll* +:FufBufferTagAll[!] [{pattern}] + カレントバッファだけでなく他の全てのバッファからもタグを集める以外は + |:FufBufferTag|と同じです。 + + *:FufBufferTagWithCursorWord* +:FufBufferTagWithCursorWord[!] [{pattern}] + カーソル下の単語を初期パターンとする以外は|:FufBufferTag|と同じです。 + + *:FufBufferTagAllWithCursorWord* +:FufBufferTagAllWithCursorWord[!] [{pattern}] + カーソル下の単語を初期パターンとする以外は|:FufBufferTagAll|と同じです + 。 + + *:FufBufferTagWithSelectedText* +:FufBufferTagWithSelectedText[!] [{pattern}] + 最後に選択したテキストを初期パターンとする以外は|:FufBufferTag|と同じ + です。 + + *:FufBufferTagAllWithSelectedText* +:FufBufferTagAllWithSelectedText[!] [{pattern}] + 最後に選択したテキストを初期パターンとする以外は|:FufBufferTagAll|と同 + じです。 + + *:FufTaggedFile* +:FufTaggedFile [{pattern}] + Tagged-File モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufJumpList* +:FufJumpList [{pattern}] + Jump-List モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufChangeList* +:FufChangeList [{pattern}] + Change-List モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufQuickfix* +:FufQuickfix [{pattern}] + Quickfix モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufLine* +:FufLine [{pattern}] + Line モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufHelp* +:FufHelp[!] [{pattern}] + Help モードを起動します。 + + ! 修飾子を付けて実行した場合、あいまい検索ではなく部分一致検索を行うよ + うになります。 + + FuzzyFinder 起動後に {pattern} が挿入されます。 + + *:FufEditDataFile* +:FufEditDataFile + データファイルを編集するためのバッファを開きます。詳しくは + |fuf-data-file|を参照してください。 + + *:FufCoverageFileRegister* +:FufCoverageFileRegister + Coverage-File モードで検索される、新しい検索対象を登録します。最初に + ~/* のような glob パターンを入力します。 を入力するまでパターン + を追加することができます。次に対象名を入力します。 + + See also: |glob()|, |fuf-coveragefile-mode| + + *:FufCoverageFileChange* +:FufCoverageFileChange [{name}] + |FufCoverageFileRegister|コマンドで登録されている中から選択された検索 + 対象でCoverage-File モードを起動します。 + + 対象名が与えられた場合、選択プロセスは飛ばされます。 + + See also: |fuf-coveragefile-mode| + + *:FufBookmarkFileAdd* +:FufBookmarkFileAdd [{name}] + カーソル行をブックマークに追加します。 + + See also: |fuf-bookmarkfile-mode| + + *:FufBookmarkFileAddAsSelectedText* +:FufBookmarkFileAddAsSelectedText + 最後に選択されたテキストをブックマーク名とする以外は + |:FufBookmarkFileAdd|と同じです。 + + *:FufBookmarkDirAdd* +:FufBookmarkDirAdd [{name}] + ディレクトリをブックマークに追加します。 + + See also: |fuf-bookmarkdir-mode| + + *:FufRenewCache* +:FufRenewCache + 補完アイテムを作り直すためにキャッシュを削除します。詳しくは + |fuf-cache|を参照してください。 + + +============================================================================== +オプション *fuf-options* + + *fuf-options-for-all-modes* +全モード用 ~ + + *g:fuf_modesDisable* > + let g:fuf_modesDisable = [ 'mrufile', 'mrucmd', ] +< + 無効にするモード名のリスト。これに含まれるモードは初期化されず、イベン + トの処理も行われません。 + + *g:fuf_keyOpen* > + let g:fuf_keyOpen = '' +< + 補完を確定し、バッファ/ファイルを直前のウィンドウで開くキー。 + + *g:fuf_keyOpenSplit* > + let g:fuf_keyOpenSplit = '' +< + 補完を確定し、バッファ/ファイルを直前のウィンドウを分割して開くキー。 + + *g:fuf_keyOpenVsplit* > + let g:fuf_keyOpenVsplit = '' +< + 補完を確定し、バッファ/ファイルを直前のウィンドウを垂直分割して開くキ + ー。 + + *g:fuf_keyOpenTabpage* > + let g:fuf_keyOpenTabpage = '' +< + 補完を確定し、バッファ/ファイルを別タブページ開くキー。 + + *g:fuf_keyPreview* > + let g:fuf_keyPreview = '' +< + 選択されている補完アイテムの情報をコマンドライン領域に表示するキー。プ + レビューをサポートするモードでのみ作用します。 + + *g:fuf_keyNextMode* > + let g:fuf_keyNextMode = '' +< + 次のモードに切り替えるキー。 + + *g:fuf_keyPrevMode* > + let g:fuf_keyPrevMode = '' +< + 前のモードに切り替えるキー。 + + *g:fuf_keyPrevPattern* > + let g:fuf_keyPrevPattern = '' +< + 履歴から前の入力パターンを呼び出すキー。 + + *g:fuf_keyNextPattern* > + let g:fuf_keyNextPattern = '' +< + 履歴から次の入力パターンを呼び出すキー。 + + *g:fuf_keySwitchMatching* > + let g:fuf_keySwitchMatching = '' +< + あいまいマッチングと部分一致マッチングを切り替えるキー。 + + *g:fuf_dataDir* > + let g:fuf_dataDir = '~/.vim-fuf-data' +< + データファイルを置くディレクトリのパス。空文字列を設定するとファイルへ + の書き込みは行われなくなります。 + + *g:fuf_abbrevMap* > + let g:fuf_abbrevMap = {} +< + |Dictionary|型でそれぞれの値は|List|型です。入力されたテキストの、キー + にマッチする部分が対応する値に展開されます。 + + *g:fuf_patternSeparator* > + let g:fuf_patternSeparator = ';' +< + 入力パターンをプライマリパターンと絞り込みパターン列に区切る文字列。 + + *g:fuf_promptHighlight* > + let g:fuf_promptHighlight = 'Question' +< + プロンプトをハイライトするグループ名。 + + *g:fuf_ignoreCase* > + let g:fuf_ignoreCase = 1 +< + 真なら、大文字小文字を無視します。 + + *g:fuf_splitPathMatching* > + let g:fuf_splitPathMatching = 1 +< + 真なら、プライマリパターンのマッチングは head 部とtail 部に分けて行わ + れます。 + + See also: |fuf-search-patterns| + + *g:fuf_fuzzyRefining* > + let g:fuf_fuzzyRefining = 0 +< + 真なら、絞り込みパターンについて部分一致マッチングの代わりにあいまいマ + ッチングが行われます。 + + See also: |fuf-search-patterns| + + *g:fuf_reuseWindow* > + let g:fuf_reuseWindow = 1 +< + 真なら、すでに開かれているバッファを開くとき、目的のバッファを含むウィ + ンドウを再利用します。 + + *g:fuf_timeFormat* > + let g:fuf_timeFormat = '(%Y-%m-%d %H:%M:%S)' +< + アイテムが登録された日時の書式を設定します。書式の詳細は|strftime()|を + 参照してください。 + + *g:fuf_learningLimit* > + let g:fuf_learningLimit = 100 +< + 保持する補完統計データのモード毎の上限値です。 + + *g:fuf_enumeratingLimit* > + let g:fuf_enumeratingLimit = 50 +< + レスポンスを向上させるため、補完アイテムの列挙をこの数に達した時点で打 + ち切ります。 + + *g:fuf_maxMenuWidth* > + let g:fuf_maxMenuWidth = 78 +< + 長い補完アイテムは、この長さに収まるよう省略して表示します。 + + *g:fuf_previewHeight* > + let g:fuf_previewHeight = 0 +< + プレビューをサポートするモードを起動したとき、'cmdheight'がこの値に設 + 定されます。選択されている補完アイテムの情報がコマンドライン領域に表示 + されます。0 ならプレビュー機能は無効になります。 + + *g:fuf_autoPreview* > + let g:fuf_autoPreview = 0 +< + 真ならプレビューを自動的に表示します。 + + *g:fuf_useMigemo* > + let g:fuf_useMigemo = 0 +< + 真なら migemo を利用します。 + + *fuf-options-for-buffer-mode* +Buffer モード用 ~ + + *g:fuf_buffer_prompt* > + let g:fuf_buffer_prompt = '>Buffer[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_buffer_switchOrder* > + let g:fuf_buffer_switchOrder = 10 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *g:fuf_buffer_mruOrder* > + let g:fuf_buffer_mruOrder = 1 +< + 真なら、最後に使った時間順に補完アイテムをソートします。 + + *g:fuf_buffer_keyDelete* > + let g:fuf_buffer_keyDelete = '' +< + 選択したバッファを削除するキー。 + + *fuf-options-for-file-mode* +File モード用 ~ + + *g:fuf_file_prompt* > + let g:fuf_file_prompt = '>File[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_file_switchOrder* > + let g:fuf_file_switchOrder = 20 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *g:fuf_file_exclude* > + let g:fuf_file_exclude = '\v\~$|\.(o|exe|bak|orig|swp)$|(^|[/\\])\.(hg|git|bzr)($|[/\\])' +< + 補完リストから除外したいアイテムの正規表現パターン。 + + *fuf-options-for-coveragefile-mode* +Coverage-File モード用 ~ + + *g:fuf_coveragefile_prompt* > + let g:fuf_coveragefile_prompt = '>CoverageFile[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_coveragefile_switchOrder* > + let g:fuf_coveragefile_switchOrder = 30 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *g:fuf_coveragefile_exclude* > + let g:fuf_coveragefile_exclude = '\v\~$|\.(o|exe|dll|bak|orig|swp)$|(^|[/\\])\.(hg|git|bzr)($|[/\\])' +< + 補完リストから除外したいアイテムの正規表現パターン。 + + *g:fuf_coveragefile_globPatterns* > + let g:fuf_coveragefile_globPatterns = ['**/.*', '**/*'] +< + 検索されるファイルパスを得るためのglobパターンのリスト。 + + *fuf-options-for-dir-mode* +Directory モード用 ~ + + *g:fuf_dir_prompt* > + let g:fuf_dir_prompt = '>Dir[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_dir_switchOrder* > + let g:fuf_dir_switchOrder = 40 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *g:fuf_dir_exclude* > + let g:fuf_dir_exclude = '\v(^|[/\\])\.(hg|git|bzr)($|[/\\])' +< + 補完リストから除外したいアイテムの正規表現パターン。 + + *fuf-options-for-mrufile-mode* +MRU-File モード用 ~ + + *g:fuf_mrufile_prompt* > + let g:fuf_mrufile_prompt = '>MRU-File[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_mrufile_switchOrder* > + let g:fuf_mrufile_switchOrder = 50 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *g:fuf_mrufile_exclude* > + let g:fuf_mrufile_exclude = '\v\~$|\.(o|exe|dll|bak|orig|sw[po])$|^(\/\/|\\\\|\/mnt\/|\/media\/)' +< + 補完リストから除外したいアイテムの正規表現パターン。 + + *g:fuf_mrufile_maxItem* > + let g:fuf_mrufile_maxItem = 200 +< + 保持するMRUアイテムの上限値。 + + *g:fuf_mrufile_maxItemDir* > + let g:fuf_mrufile_maxItemDir = 50 +< + 保持するMRUアイテムの親ディレクトリ(周辺検索で使われる)の上限値。 + + *g:fuf_mrufile_keyExpand* > + let g:fuf_mrufile_keyExpand = '' +< + 検索する範囲を広げるキー。 + + *fuf-options-for-mrucmd-mode* +MRU-Cmd モード用 ~ + + *g:fuf_mrucmd_prompt* > + let g:fuf_mrucmd_prompt = '>MRU-Cmd[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_mrucmd_switchOrder* > + let g:fuf_mrucmd_switchOrder = 60 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *g:fuf_mrucmd_exclude* > + let g:fuf_mrucmd_exclude = '^$' +< + 補完リストから除外したいアイテムの正規表現パターン。 + + *g:fuf_mrucmd_maxItem* > + let g:fuf_mrucmd_maxItem = 200 +< + 保持するMRUアイテムの上限値。 + + *fuf-options-for-bookmarkfile-mode* +Bookmark-File モード用 ~ + + *g:fuf_bookmarkfile_prompt* > + let g:fuf_bookmarkfile_prompt = '>BookmarkFile[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_bookmarkfile_switchOrder* > + let g:fuf_bookmarkfile_switchOrder = 70 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *g:fuf_bookmarkfile_searchRange* > + let g:fuf_bookmarkfile_searchRange = 400 +< + ジャンプするとき、ブックマークした位置からこの行数の範囲内でブックマー + クしたときのパターンとマッチする行を探します。 + + *g:fuf_bookmarkfile_keyDelete* > + let g:fuf_bookmarkfile_keyDelete = '' +< + 選択したブックマークを削除するキー。 + + *fuf-options-for-bookmarkdir-mode* +Bookmark-Dir モード用 ~ + + *g:fuf_bookmarkdir_prompt* > + let g:fuf_bookmarkdir_prompt = '>BookmarkDir[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_bookmarkdir_switchOrder* > + let g:fuf_bookmarkdir_switchOrder = 80 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *g:fuf_bookmarkdir_keyDelete* > + let g:fuf_bookmarkdir_keyDelete = '' +< + 選択したブックマークを削除するキー。 + + *fuf-options-for-tag-mode* +Tag モード用 ~ + + *g:fuf_tag_prompt* > + let g:fuf_tag_prompt = '>Tag[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_tag_switchOrder* > + let g:fuf_tag_switchOrder = 90 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *fuf-options-for-buffertag-mode* +For Buffer-Tag モード用 ~ + + *g:fuf_buffertag_prompt* > + let g:fuf_buffertag_prompt = '>Buffer-Tag[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_buffertag_switchOrder* > + let g:fuf_buffertag_switchOrder = 100 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *g:fuf_buffertag_ctagsPath* > + let g:fuf_buffertag_ctagsPath = 'ctags' +< + Ctagsの実行ファイルのパス + + *fuf-options-for-taggedfile-mode* +Tagged-File モード用 ~ + + *g:fuf_taggedfile_prompt* > + let g:fuf_taggedfile_prompt = '>Tagged-File[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_taggedfile_switchOrder* > + let g:fuf_taggedfile_switchOrder = 110 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *fuf-options-for-jumplist-mode* +Jump-List モード用 ~ + + *g:fuf_jumplist_prompt* > + let g:fuf_jumplist_prompt = '>Jump-List[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_jumplist_switchOrder* > + let g:fuf_jumplist_switchOrder = 120 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *fuf-options-for-changelist-mode* +Change-List モード用 ~ + + *g:fuf_changelist_prompt* > + let g:fuf_changelist_prompt = '>Change-List[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_changelist_switchOrder* > + let g:fuf_changelist_switchOrder = 130 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *fuf-options-for-quickfix-mode* +Quickfix モード用 ~ + + *g:fuf_quickfix_prompt* > + let g:fuf_quickfix_prompt = '>Quickfix[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_quickfix_switchOrder* > + let g:fuf_quickfix_switchOrder = 140 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *fuf-options-for-line-mode* +Line モード用 ~ + + *g:fuf_line_prompt* > + let g:fuf_line_prompt = '>Line[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_line_switchOrder* > + let g:fuf_line_switchOrder = 150 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + *fuf-options-for-help-mode* +Help モード用 ~ + + *g:fuf_help_prompt* > + let g:fuf_help_prompt = '>Help[]>' +< + プロンプト文字列。"[]" はインジケータに置換されます。 + + *g:fuf_help_switchOrder* > + let g:fuf_help_switchOrder = 160 +< + 次/前のモードに切り替えるときの、モードの順位です。負数ならこのモード + には切り替えません。 + + +============================================================================== +vimrc の例 *fuf-vimrc-example* + +> + let g:fuf_modesDisable = [] + let g:fuf_mrufile_maxItem = 400 + let g:fuf_mrucmd_maxItem = 400 + nnoremap sj :FufBuffer + nnoremap sk :FufFileWithCurrentBufferDir + nnoremap sK :FufFileWithFullCwd + nnoremap s :FufFile + nnoremap sl :FufCoverageFileChange + nnoremap sL :FufCoverageFileChange + nnoremap s :FufCoverageFileRegister + nnoremap sd :FufDirWithCurrentBufferDir + nnoremap sD :FufDirWithFullCwd + nnoremap s :FufDir + nnoremap sn :FufMruFile + nnoremap sN :FufMruFileInCwd + nnoremap sm :FufMruCmd + nnoremap su :FufBookmarkFile + nnoremap s :FufBookmarkFileAdd + vnoremap s :FufBookmarkFileAddAsSelectedText + nnoremap si :FufBookmarkDir + nnoremap s :FufBookmarkDirAdd + nnoremap st :FufTag + nnoremap sT :FufTag! + nnoremap s :FufTagWithCursorWord! + nnoremap s, :FufBufferTag + nnoremap s< :FufBufferTag! + vnoremap s, :FufBufferTagWithSelectedText! + vnoremap s< :FufBufferTagWithSelectedText + nnoremap s} :FufBufferTagWithCursorWord! + nnoremap s. :FufBufferTagAll + nnoremap s> :FufBufferTagAll! + vnoremap s. :FufBufferTagAllWithSelectedText! + vnoremap s> :FufBufferTagAllWithSelectedText + nnoremap s] :FufBufferTagAllWithCursorWord! + nnoremap sg :FufTaggedFile + nnoremap sG :FufTaggedFile! + nnoremap so :FufJumpList + nnoremap sp :FufChangeList + nnoremap sq :FufQuickfix + nnoremap sy :FufLine + nnoremap sh :FufHelp + nnoremap se :FufEditDataFile + nnoremap sr :FufRenewCache +< + +============================================================================== +あばうと *fuf-about* *fuf-contact* *fuf-author* + +作者: Takeshi NISHIDA +ライセンス: MIT Licence +URL: http://www.vim.org/scripts/script.php?script_id=1984 + http://bitbucket.org/ns9tks/vim-fuzzyfinder/ + +バグや要望など ~ + +こちらへどうぞ: http://bitbucket.org/ns9tks/vim-fuzzyfinder/issues/ + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: diff --git a/fuzzyfinder/doc/fuf.txt b/fuzzyfinder/doc/fuf.txt new file mode 100644 index 0000000..2e36831 --- /dev/null +++ b/fuzzyfinder/doc/fuf.txt @@ -0,0 +1,1883 @@ +*fuf.txt* buffer/file/command/tag/etc explorer with fuzzy matching. + + Copyright (c) 2007-2010 Takeshi NISHIDA + +FuzzyFinder *fuzzyfinder* *fuf* + +INTRODUCTION |fuf-introduction| +INSTALLATION |fuf-installation| +USAGE |fuf-usage| +MODES |fuf-modes| +DETAILED TOPICS |fuf-detailed-topics| +COMMANDS |fuf-commands| +OPTIONS |fuf-options| +VIMRC EXAMPLE |fuf-vimrc-example| +SPECIAL THANKS |fuf-thanks| +CHANGELOG |fuf-changelog| +ABOUT |fuf-about| + +============================================================================== +INTRODUCTION *fuf-introduction* + +FuzzyFinder provides convenient ways to quickly reach the +buffer/file/command/bookmark/tag you want. FuzzyFinder searches with the +fuzzy/partial pattern to which it converted an entered pattern. + + Entered pattern Fuzzy pattern Partial pattern ~ +> + abc *a*b*c* *abc* + dir/file dir/*f*i*l*e* dir/*file* + d*r/file d*r/*f*i*l*e* d*r/*file* + ../**/s ../**/*s* ../**/*s* + (** allows searching a directory tree.) +< +You will be happy when: + + "./AhLongLongLongLongLongFile.txt" + "./AhLongLongLongLongLongName.txt" + "./OhLongLongLongLongLongFile.txt" + "./OhLongLongLongLongLongName.txt" <- you want :O + +Type "ON" and "OhLongLongLongLongLongName.txt" will be selected. :D + +FuzzyFinder can search: + + - buffers + - files + - directories + - most recently used files + - files around most recently used files + - most recently used command-lines + - bookmarked files + - bookmarked directories + - tags + - files which are included in current tagfiles + - jump list + - change list + - buffer lines + - quickfix + - help + +FuzzyFinder also provides APIs to use its system of searching files or +selecting items. + +FuzzyFinder supports multibyte characters. + + +============================================================================== +INSTALLATION *fuf-installation* + +Put all files into your runtime directory. If you have the zip file, extract +it to your runtime directory. + +You should place the files as follows: +> + /plugin/fuf.vim + /doc/fuf.txt + ... +< +If you are disgusted to make your runtime directory confused with a lot of +plugins, put each of the plugins into a directory individually and just add +the directory path to 'runtimepath'. It's easy to uninstall plugins. + +Then update your help tags files to enable help for this plugin. See +|add-local-help| for details. + +Requirements: ~ + +- L9 library (vimscript #3252) + + +============================================================================== +USAGE *fuf-usage* + +You can launch FuzzyFinder by the following commands: + + Command Mode ~ + |:FufBuffer| - Buffer mode (|fuf-buffer-mode|) + |:FufFile| - File mode (|fuf-file-mode|) + |:FufCoverageFile| - Coverage-File mode (|fuf-coveragefile-mode|) + |:FufDir| - Directory mode (|fuf-dir-mode|) + |:FufMruFile| - MRU-File mode (|fuf-mrufile-mode|) + |:FufMruCmd| - MRU-Command mode (|fuf-mrucmd-mode|) + |:FufBookmarkFile| - Bookmark-File mode (|fuf-bookmarkfile-mode|) + |:FufBookmarkDir| - Bookmark-Dir mode (|fuf-bookmarkdir-mode|) + |:FufTag| - Tag mode (|fuf-tag-mode|) + |:FufBufferTag| - Buffer-Tag mode (|fuf-buffertag-mode|) + |:FufTaggedFile| - Tagged-File mode (|fuf-taggedfile-mode|) + |:FufJumpList| - Jump-List mode (|fuf-jumplist-mode|) + |:FufChangeList| - Change-List mode (|fuf-changelist-mode|) + |:FufQuickfix| - Quickfix mode (|fuf-quickfix-mode|) + |:FufLine| - Line mode (|fuf-line-mode|) + |:FufHelp| - Help mode (|fuf-help-mode|) + +It is recommended to map these commands. + +These commands open 1-line buffer to enter search pattern and start insert +mode. + +FuzzyFinder searchs for matching items with an entered pattern and shows them +in a completion menu. For more details on pattern matching, see +|fuf-search-patterns|. + +If there are a lot of matching items, FuzzyFinder limits the number of +enumerating items (|g:fuf_enumeratingLimit|) to speed up a response time, and +highlights the pattern with "Error" group. + +The first item in the completion menu will be selected automatically. + +Typing deletes one block of an entered pattern before the cursor, like a +directory name. + +with (|g:fuf_keyPrevPattern|) and (|g:fuf_keyNextPattern|), You +can recall patterns which have been entered before from history. + +You can open a selected item in various ways: + + (|g:fuf_keyOpen|) - opens in a previous window. + (|g:fuf_keyOpenSplit|) - opens in a split window. + (|g:fuf_keyOpenVsplit|) - opens in a vertical-split window. + (|g:fuf_keyOpenTabpage|) - opens in a new tab page. + +To cancel and return to previous window, just leave Insert mode. + +With (|g:fuf_keySwitchMatching|), You can switch search method +between fuzzy matching and partial matching. + +With (|g:fuf_keyNextMode|) and (|g:fuf_keyPrevMode|), You can +switch current mode without leaving Insert mode . + +You can preview selected item with (|g:fuf_keyPreview|) in some modes. +Repeating the key on the same item shows another information. The height +of command-line area is changed to |g:fuf_previewHeight| when you launch a +mode supporting preview. This feature is available when |g:fuf_previewHeight| +is not 0. + + +============================================================================== +MODES *fuf-modes* + + *fuf-buffer-mode* +Buffer mode ~ + +This mode provides an interface to select a buffer from a list of existing +buffers and open it. + +Press (|g:fuf_buffer_keyDelete|) in this mode and selected buffer will +be deleted. + + *fuf-file-mode* +File mode ~ + +This mode provides an interface to search a file tree for a file and open it. + + *fuf-coveragefile-mode* +Coverage-File mode ~ + +This mode provides an interface to select a file from all files of a preset +coverage and open it. + +By default, This mode lists all files under the current working directory +recursively. (|g:fuf_coveragefile_globPatterns|) + +If you want to search other coverage, execute |FufCoverageFileRegister| +command to register new search coverage and |FufCoverageFileChange| command to +choose a search coverage and launch Coverage-File mode. + +In addition, there is another way to change a search coverage with +|fuf#setOneTimeVariables()| function. + +Example: search only .h and .c files: +> + call fuf#setOneTimeVariables(['g:fuf_coveragefile_globPatterns', ['**/*.h', '**/*.c']]) + \ | FufCoverageFile +< +Example: search your home directory in addition to the default coverage: +> + call fuf#setOneTimeVariables(['g:fuf_coveragefile_globPatterns', g:fuf_coveragefile_globPatterns + ['~/**/.*', '~/**/*']]) + \ | FufCoverageFile +< + + *fuf-dir-mode* +Directory mode ~ + +This mode provides an interface to search a file tree for a directory and +change the current directory. + + *fuf-mrufile-mode* +MRU-File mode ~ + +This mode provides an interface to select a file from the most recently used +files and open it. + +Press (|g:fuf_mrufile_keyExpand|) in this mode and files around the most +recently used files are listed. Each time the key is pressed, the search range +are expanded one level along the directory tree upwardly/downwardly. + +This mode is set to disable by default (|g:fuf_modesDisable|) because +processes for this mode in |BufEnter| and |BufWritePost| could cause +Performance issue. + +See also: |FufMruFileInCwd| + + *fuf-mrucmd-mode* +MRU-Command mode ~ + +This mode provides an interface to select a command from the most recently +used commands and execute it. + +This mode is set to disable by default (|g:fuf_modesDisable|) because mapping + of Command-line mode required by this mode has side effects. + + *fuf-bookmarkfile-mode* +Bookmark-File mode ~ + +This mode provides an interface to select one of the bookmarks you have added +beforehand and jump there. + +You can add a cursor line to bookmarks by |:FufBookmarkFileAdd| command. +Execute that command and you will be prompted to enter a bookmark name. + +FuzzyFinder adjusts a line number for jump. If a line of bookmarked position +does not match to a pattern when the bookmark was added, FuzzyFinder searches +a matching line around bookmarked position. So you can jump to a bookmarked +line even if the line is out of bookmarked position. If you want to jump to +bookmarked line number without the adjustment, set +|g:fuf_bookmarkfile_searchRange| option to 0. + +Press (|g:fuf_bookmarkfile_keyDelete|) in this mode and selected +bookmark will be deleted. + + *fuf-bookmarkdir-mode* +Bookmark-Dir mode ~ + +This mode provides an interface to select one of the bookmarks you have added +beforehand and change the current directory. + +You can add a directory to bookmarks by |:FufBookmarkDirAdd| command. Execute +that command and you will be prompted to enter a directory path and a +bookmark name. + +Press (|g:fuf_bookmarkdir_keyDelete|) in this mode and selected bookmark +will be deleted. + + *fuf-tag-mode* +Tag mode ~ + +This mode provides an interface to select a tag and jump to the definition of +it. + +Following mapping is a replacement for : +> + noremap :FufTagWithCursorWord! +< + + *fuf-buffertag-mode* +Buffer-Tag mode ~ + +This mode provides an interface to select a tag of current buffer or all +buffers and jump to the definition of it. + +Tag list is instantly created when FuzzyFinder is launched, so there is no +need to make tags file in advance. + +|FufBufferTag| covers current buffer and |FufBufferTagAll| covers all buffers. + +Following mapping is a replacement for : +> + nnoremap :FufBufferTagWithCursorWord! + vnoremap :FufBufferTagAllWithSelectedText! +< +or +> + nnoremap :FufBufferTagAllWithCursorWord! + vnoremap :FufBufferTagAllWithSelectedText! +< +This mode is inspired by taglist.vim (vimscript #273) and refered its codes. + + *fuf-taggedfile-mode* +Tagged-File mode ~ + +This mode provides an interface to select one of the files which are included +in current tagfiles and open it. + + *fuf-jumplist-mode* +Jump-List mode ~ + +This mode provides an interface to select one from the |jumplist| of the +current window and jump there. + + *fuf-changelist-mode* +Change-List mode ~ + +This mode provides an interface to select one from the |changelist| of the +current buffer and jump there. + + *fuf-quickfix-mode* +Quickfix mode ~ + +This mode provides an interface to select one from the |quickfix| list and +jump there. + + *fuf-line-mode* +Line mode ~ + +This mode provides an interface to select a line from current buffer and jump +there. + + *fuf-help-mode* +Help mode ~ + +This mode provides an interface to select a help tag and jump to the help +page. + + *fuf-givenfile-mode* +Given-File mode ~ + +This mode provides an API to open a selected file from a given list. + +API function: +> + function fuf#givenfile#launch( + \ initialPattern, partialMatching, prompt, items) +< + initialPattern - String which is inserted after launching + FuzzyFinder. + partialMatching - If non-zero, enable partial matching instead of + fuzzy matching. + prompt - Prompt string + items - List of items. + +Example of use: +> + " Open one of your dotfiles. + call fuf#givenfile#launch('', 0, '>', split(glob('~/.*'), "\n")) +< + + *fuf-givendir-mode* +Given-Directory mode ~ + +This mode provides an API to change current working directory to a selected +one from a given list. + +API function: +> + function fuf#givendir#launch( + \ initialPattern, partialMatching, prompt, items) +< + initialPattern - String which is inserted after launching + FuzzyFinder. + partialMatching - If non-zero, enable partial matching instead of + fuzzy matching. + prompt - Prompt string + items - List of items. + + +Example of use: +> + " Change current working directory to one of your runtime directory. + call fuf#givendir#launch('', 0, '>', split(&runtimepath, ',')) +< + + *fuf-givencmd-mode* +Given-Command mode ~ + +This mode provides an API to execute a selected command from a given list. + +A selected command is executed by |feedkeys()|, so it is able to emulate a +series of key input in Normal mode. + +API function: +> + function fuf#givencmd#launch( + \ initialPattern, partialMatching, prompt, items) +< + initialPattern - String which is inserted after launching + FuzzyFinder. + partialMatching - If non-zero, enable partial matching instead of + fuzzy matching. + prompt - Prompt string + items - List of items. + + +Example of use: +> + function GetAllCommands() + redir => commands + silent command + redir END + return map((split(commands, "\n")[3:]), + \ '":" . matchstr(v:val, ''^....\zs\S*'')') + endfunction + + " execute one of the user-defined commands + call fuf#givencmd#launch('', 0, '>', GetAllCommands()) + +< + + *fuf-callbackfile-mode* +Callback-File mode ~ + +This mode provides an API to find and get a file path which is selected by an +user. + +API function: +> + function fuf#callbackfile#launch( + \ initialPattern, partialMatching, prompt, exclude, listener) +< + initialPattern - String which is inserted after launching + FuzzyFinder. + partialMatching - If non-zero, enable partial matching instead of + fuzzy matching. + prompt - Prompt string. + exclude - Regexp pattern for items which you want to exclude + from completion list. + listener - |Dictionary| which has 'onComplete' and 'onAbort'. + They are called at the end of FuzzyFinder. + listener.onComplete(item, method) is called with 2 + arguments which are a name of selected item and a + number of open method when completed. + listener.onAbort() is called when aborted. + +Example of use: +> + let listener = {} + + function listener.onComplete(item, method) + echo "Item: " . a:item . "\nMethod: " . a:method + endfunction + + function listener.onAbort() + echo "Abort" + endfunction + + " Find a file from current working directory. + call fuf#callbackfile#launch('', 0, '>', '', listener) + + " Find a file from home directory. + call fuf#callbackfile#launch('~/', 0, '>', '', listener) +< + + *fuf-callbackitem-mode* +Callback-Item mode ~ + +This mode provides an API to get an item which is selected from a given list +by an user. + +API function: +> + function fuf#callbackitem#launch( + \ initialPattern, partialMatching, prompt, listener, items, forPath) +< + initialPattern - String which is inserted after launching + FuzzyFinder. + partialMatching - If non-zero, enable partial matching instead of + fuzzy matching. + prompt - Prompt string + listener - |Dictionary| which has 'onComplete' and 'onAbort'. + They are called at the end of FuzzyFinder. + listener.onComplete(item, method) is called with 2 + arguments which are a name of selected item and a + number of open method when completed. + listener.onAbort() is called when aborted. + items - List of items. + forPath - If non-zero, use a matching method for files. + +Example of use: +> + let listener = {} + + function listener.onComplete(item, method) + echo "Item: " . a:item . "\nMethod: " . a:method + endfunction + + function listener.onAbort() + echo "Abort" + endfunction + + " Select an item from a given list. + call fuf#callbackitem#launch('', 0, '>', listener, ['ed', 'vi', 'vim'], 0) + + " Select a file from a given list. + call fuf#callbackitem#launch('', 0, '>', listener, ['../foo/bar', 'baz'], 1) +< + +============================================================================== +DETAILED TOPICS *fuf-detailed-topics* + + *fuf-setting-one-time-option* *fuf#setOneTimeVariables()* +Setting One-Time Options ~ + +If you want to set one-time options only for the next FuzzyFinder, +|fuf#setOneTimeVariables()| function will be of help. This function is used as +follows: +> + call fuf#setOneTimeVariables(['g:fuf_ignoreCase', 0], ['&lines', 50]) +< +This function takes 0 or more arguments and each of them is a pair of a +variable name and its value. Specified options will be set practically next +time FuzzyFinder is launched, and restored when FuzzyFinder is closed. + + *fuf-search-patterns* +Search Patterns ~ + +You can enter one primary pattern and zero or more refining patterns as search +patterns. An entered pattern is separated by ";" (|g:fuf_patternSeparator|), +and the first pattern is a primary pattern and the rest of patterns is a +refining pattern. +> + primary refining refining + |----------| |-------| |----| + >MruFile>bookmark.vim;autoload/;/home/ +< +A refining pattern is used to narrow down the list of matching items by +another pattern. + +With a primary pattern, FuzzyFinder does fuzzy matching or partial matching, +which you specified. With a refining pattern, FuzzyFinder does partial +matching by default. (|g:fuf_fuzzyRefining|) + +When you enter a number as refining pattern, it also can match the index of +each item. + +In a mode which targets a static set of file paths (such as Buffer or MRU-File +mode, not File or Directory) and |g:fuf_splitPathMatching| is non-zero, +matching with a primary pattern is divided into head part and tail part and +done individually. +> + head tail + |------||-----| + foo/bar/baz.vim + + fuzzy matching example: + +----------------+---------+---------+---------+ + | item \ pattern | foo/bar | foo/ | bar | + +----------------+---------+---------+---------+ + | foo/bar | match | match | match | + | foo/abc | unmatch | match | unmatch | + | abc/bar | unmatch | unmatch | match | + | foobar | unmatch | unmatch | match | + | foooo/barrrr | match | match | match | + | foooo/fooooo | unmatch | match | unmatch | + +----------------+---------+---------+---------+ +< +refining pattern can match anywhere on each path in the above case. + + *fuf-sorting-of-completion-items* +Sorting Of Completion Items ~ + +FuzzyFinder sorts completion items with some rules. + +An item, one part of which is matched with a whole pattern, is placed upper. +E.g., with the pattern "bc", the item "abc" is placed upper than "bac". + +In the above case, items, each having matching part at the head of itself, are +placed upper than others. E.g., with the pattern "foo", the item "foobar" is +placed upper than "foobarbaz". + +And the shorter the length of the item after matching position puts it higher. +E.g., with the pattern "bar", the item "foobar" is placed upper than +"foobarbaz". + +If a pattern matches an item at only word boundaries of it, the item is placed +upper. E.g., with a pattern "fb", items such as "fooBarBaz" and "foo_bar_baz" +is placed upper. + +Plus, FuzzyFinder has a learning system. An item which has been completed in +the past with current pattern is placed upper. + + *fuf-reusing-window* +Reusing Of A Window Containing Target Buffer/File ~ + +If a window containing target buffer is found in current tab page when +FuzzyFinder is going to open the buffer in a split new window, move to it. If +a window containing target buffer is found in other tab page when FuzzyFinder +is going to open the buffer in a new tab page, move to it. + +You can disable that feature via 'reuse_window' options if always want to open +a buffer in a new window. + + *fuf-hiding-menu* +To Hide The Completion Menu Temporarily In FuzzyFinder ~ + +You can close it by and reopen it by . + + *fuf-abbreviation* *fuf-multiple-search* +Abbreviations And Multiple Search ~ + +You can use abbreviations and multiple search in all modes by setting +|g:fuf_abbrevMap| option. + +For example, set as below: +> + let g:fuf_abbrevMap = { + \ "^doc:" : [ + \ "~/project/**/doc/", + \ ".vim/doc/", + \ ], + \ } +< +and enter "doc:txt" in File mode, then FuzzyFinder searches by the following +patterns: + + "~/project/**/doc/*t*x*t*" + ".vim/doc/*t*x*t*" + +and show concatenated search results. + + *fuf-data-file* +Data File ~ + +FuzzyFinder writes completion statistics, MRU data, bookmark, etc to files +under |g:fuf_dataDir|. + +|:FufEditDataFile| command is helpful in editing your data files. This command +reads the data file in new unnamed buffer. Write the buffer and the data file +will be updated. + + *fuf-cache* +Cache ~ + +Once a cache was created, It is not automatically updated to speed up the +response time by default. To update it, use |:FufRenewCache| command. + + *fuf-dot-sequence* +Going Up Parent Directories With Dot Sequence ~ + +You can go up parent directories with entering dot sequence. Dot sequence +after a path separator is expanded to "../" sequence. + + Dot sequence Expanded pattern ~ + /.. /../ + /... /../../ + /.... /../../../ + + *fuf-how-to-add-mode* +How To Add Mode ~ + +To add "mymode" mode, put the source file at autoload/fuf/mymode.vim and call +fuf#addMode("mymode") . + + *fuf-migemo* +What Is Migemo ~ + +Migemo is a search method for Japanese language. + + +============================================================================== +COMMANDS *fuf-commands* + +See also: |fuf-vimrc-example| + + *:FufBuffer* +:FufBuffer[!] [{pattern}] + Launchs Buffer mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufFile* +:FufFile[!] [{pattern}] + Launchs File mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufFileWithFullCwd* +:FufFileWithFullCwd[!] [{pattern}] + Is mostly the same as |:FufFile|, except that initial pattern is a + full path of current working directory. + + *:FufFileWithCurrentBufferDir* +:FufFileWithCurrentBufferDir[!] [{pattern}] + Is mostly the same as |:FufFile|, except that initial pattern is a + path of directory current buffer is in. + + *:FufCoverageFile* +:FufCoverageFile[!] [{pattern}] + Launchs Coverage-File mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufDir* +:FufDir[!] [{pattern}] + Launchs Directory mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufDirWithFullCwd* +:FufDirWithFullCwd[!] [{pattern}] + Is mostly the same as |:FufDir|, except that initial pattern is a full + path of current working directory. + + *:FufDirWithCurrentBufferDir* +:FufDirWithCurrentBufferDir[!] [{pattern}] + Is mostly the same as |:FufDir|, except that initial pattern is a path + of directory current buffer is in. + + *:FufMruFile* +:FufMruFile[!] [{pattern}] + Launchs MRU-File mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufMruFileInCwd* +:FufMruFileInCwd[!] [{pattern}] + Is mostly the same as |:FufMruFile|, except that files + only in current working directory are listed. + + *:FufMruCmd* +:FufMruCmd[!] [{pattern}] + Launchs MRU-Command mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufBookmarkFile* +:FufBookmarkFile[!] [{pattern}] + Launchs Bookmark-File mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufBookmarkDir* +:FufBookmarkDir[!] [{pattern}] + Launchs Bookmark-Dir mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufTag* +:FufTag[!] [{pattern}] + Launchs Tag mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufTagWithCursorWord* +:FufTagWithCursorWord[!] [{pattern}] + Is mostly the same as |:FufTag|, except that initial pattern is the + word under the cursor. + + *:FufBufferTag* +:FufBufferTag[!] [{pattern}] + Launchs Buffer-Tag mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufBufferTagAll* +:FufBufferTagAll[!] [{pattern}] + Is mostly the same as |:FufBufferTag|, except that tags are gathered + from all other buffers in addition to the current one. + + *:FufBufferTagWithCursorWord* +:FufBufferTagWithCursorWord[!] [{pattern}] + Is mostly the same as |:FufBufferTag|, except that initial pattern is + the word under the cursor. + + *:FufBufferTagAllWithCursorWord* +:FufBufferTagAllWithCursorWord[!] [{pattern}] + Is mostly the same as |:FufBufferTagAll|, except that initial pattern + is the word under the cursor. + + *:FufBufferTagWithSelectedText* +:FufBufferTagWithSelectedText[!] [{pattern}] + Is mostly the same as |:FufBufferTag|, except that initial pattern is + the last selected text. + + *:FufBufferTagAllWithSelectedText* +:FufBufferTagAllWithSelectedText[!] [{pattern}] + Is mostly the same as |:FufBufferTagAll|, except that initial pattern + is the last selected text. + + *:FufTaggedFile* +:FufTaggedFile[!] [{pattern}] + Launchs Tagged-File mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufJumpList* +:FufJumpList[!] [{pattern}] + Launchs Jump-List mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufChangeList* +:FufChangeList[!] [{pattern}] + Launchs Change-List mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufQuickfix* +:FufQuickfix[!] [{pattern}] + Launchs Quickfix mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufLine* +:FufLine[!] [{pattern}] + Launchs Line mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufHelp* +:FufHelp[!] [{pattern}] + Launchs Help mode. + + If a command was executed with a ! modifier, it does partial matching + instead of fuzzy matching. + + {pattern} will be inserted after launching FuzzyFinder. + + *:FufEditDataFile* +:FufEditDataFile + Opens a buffer for editing your data files. See |fuf-data-file| for + details. + + *:FufCoverageFileRegister* +:FufCoverageFileRegister + Registers new search coverage to be searched in Coverage-File mode. + First, input glob patterns, like ~/* . You can add patterns unless + typing . Next, input coverage name. + + See also: |glob()|, |fuf-coveragefile-mode| + + *:FufCoverageFileChange* +:FufCoverageFileChange [{name}] + Launchs Coverage-File mode with a chosen coverage, registered with + |FufCoverageFileRegister| command. + + If location name is given, the choise process will be skipped. + + See also: |fuf-coveragefile-mode| + + *:FufBookmarkFileAdd* +:FufBookmarkFileAdd [{name}] + Adds a cursor line to bookmarks. + + See also: |fuf-bookmarkfile-mode| + + *:FufBookmarkFileAddAsSelectedText* +:FufBookmarkFileAddAsSelectedText + Is mostly the same as |:FufBookmarkFileAdd|, except that initial + pattern is the last selected one. + + *:FufBookmarkDirAdd* +:FufBookmarkDirAdd [{name}] + Adds a directory to bookmarks. + + See also: |fuf-bookmarkdir-mode| + + *:FufRenewCache* +:FufRenewCache + Removes caches to renew completion items. See |fuf-cache| for details. + + +============================================================================== +OPTIONS *fuf-options* + + *fuf-options-for-all-modes* +For All Modes ~ + + *g:fuf_modesDisable* > + let g:fuf_modesDisable = [ 'mrufile', 'mrucmd', ] +< + List of mode names to disable. + + Modes which are listed will never be initialized and never handle any + event. + + *g:fuf_keyOpen* > + let g:fuf_keyOpen = '' +< + Key mapped to select completion item or finish input and open a + buffer/file in previous window. + + *g:fuf_keyOpenSplit* > + let g:fuf_keyOpenSplit = '' +< + Key mapped to select completion item or finish input and open a + buffer/file in split new window + + *g:fuf_keyOpenVsplit* > + let g:fuf_keyOpenVsplit = '' +< + Key mapped to select completion item or finish input and open a + buffer/file in vertical-split new window. + + *g:fuf_keyOpenTabpage* > + let g:fuf_keyOpenTabpage = '' +< + + Key mapped to select completion item or finish input and open a + buffer/file in a new tab page. + + *g:fuf_keyPreview* > + let g:fuf_keyPreview = '' +< + + Key mapped to show information of selected completion item on + command-line area. This key makes sense only in modes supporting + preview. + + *g:fuf_keyNextMode* > + let g:fuf_keyNextMode = '' +< + Key mapped to switch to next mode. + + *g:fuf_keyPrevMode* > + let g:fuf_keyPrevMode = '' +< + Key mapped to switch to previous mode. + + *g:fuf_keyPrevPattern* > + let g:fuf_keyPrevPattern = '' +< + Key mapped to recall previous entered patten from history. + + *g:fuf_keyNextPattern* > + let g:fuf_keyNextPattern = '' +< + Key mapped to recall next entered patten from history. + + *g:fuf_keySwitchMatching* > + let g:fuf_keySwitchMatching = '' +< + Key mapped to switch between fuzzy matching and partial matching. + + *g:fuf_dataDir* > + let g:fuf_dataDir = '~/.vim-fuf-data' +< + Directory path to which data files is put. If empty string, + FuzzyFinder does not write data files. + + *g:fuf_abbrevMap* > + let g:fuf_abbrevMap = {} +< + |Dictionary|. Each value must be a |List|. All matchs of a + key in entered text is expanded with the value. + + *g:fuf_patternSeparator* > + let g:fuf_patternSeparator = ';' +< + String which sparates a input pattern into a primary pattern and + refining patterns. + + *g:fuf_promptHighlight* > + let g:fuf_promptHighlight = 'Question' +< + a highlight group name for a prompt string. + + *g:fuf_ignoreCase* > + let g:fuf_ignoreCase = 1 +< + If non-zero, FuzzyFinder ignores case in search patterns. + + *g:fuf_splitPathMatching* > + let g:fuf_splitPathMatching = 1 +< + If non-zero, matching with a primary pattern is divided into head part + and tail part and done individually. + + See also: |fuf-search-patterns| + + *g:fuf_fuzzyRefining* > + let g:fuf_fuzzyRefining = 0 +< + If non-zero, fuzzy matching is done with refining pattern instead of + partial matching. + + See also: |fuf-search-patterns| + + *g:fuf_reuseWindow* > + let g:fuf_reuseWindow = 1 +< + If non-zero and when FuzzyFinder opens a buffer which has already been + opened, it reuses a window containing the target buffer. + + *g:fuf_timeFormat* > + let g:fuf_timeFormat = '(%Y-%m-%d %H:%M:%S)' +< + String to format time string. See |strftime()| for details. + + *g:fuf_learningLimit* > + let g:fuf_learningLimit = 100 +< + Ceiling for the number of completion statistics to be stored. + + *g:fuf_enumeratingLimit* > + let g:fuf_enumeratingLimit = 50 +< + To speed up the response time, FuzzyFinder ends enumerating completion + items when found over this. + + *g:fuf_maxMenuWidth* > + let g:fuf_maxMenuWidth = 78 +< + If a length of a completion item is more than this, it is snipped in + completion menu. + + *g:fuf_previewHeight* > + let g:fuf_previewHeight = 0 +< + 'cmdheight' is set to this when a mode supporting preview is launched. + Information of selected completion item will be shown on command-line + area. If zero, preview feature is disabled. + + *g:fuf_autoPreview* > + let g:fuf_autoPreview = 0 +< + If non-zero, previews will be shown automatically. + + *g:fuf_useMigemo* > + let g:fuf_useMigemo = 0 +< + If non-zero, FuzzyFinder uses Migemo. + + *fuf-options-for-buffer-mode* +For Buffer Mode ~ + + *g:fuf_buffer_prompt* > + let g:fuf_buffer_prompt = '>Buffer[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_buffer_switchOrder* > + let g:fuf_buffer_switchOrder = 10 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *g:fuf_buffer_mruOrder* > + let g:fuf_buffer_mruOrder = 1 +< + If non-zero, completion items is sorted in order of recently used. + + *g:fuf_buffer_keyDelete* > + let g:fuf_buffer_keyDelete = '' +< + Key mapped to delete selected buffer. + + *fuf-options-for-file-mode* +For File Mode ~ + + *g:fuf_file_prompt* > + let g:fuf_file_prompt = '>File[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_file_switchOrder* > + let g:fuf_file_switchOrder = 20 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *g:fuf_file_exclude* > + let g:fuf_file_exclude = '\v\~$|\.(o|exe|dll|bak|orig|swp)$|(^|[/\\])\.(hg|git|bzr)($|[/\\])' +< + Regexp pattern for items which you want to exclude from completion + list. + + *fuf-options-for-coveragefile-mode* +For Coverage-File Mode ~ + + *g:fuf_coveragefile_prompt* > + let g:fuf_coveragefile_prompt = '>CoverageFile[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_coveragefile_switchOrder* > + let g:fuf_coveragefile_switchOrder = 30 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *g:fuf_coveragefile_exclude* > + let g:fuf_coveragefile_exclude = '\v\~$|\.(o|exe|dll|bak|orig|swp)$|(^|[/\\])\.(hg|git|bzr)($|[/\\])' +< + Regexp pattern for items which you want to exclude from completion + list. + + *g:fuf_coveragefile_globPatterns* > + let g:fuf_coveragefile_globPatterns = ['**/.*', '**/*'] +< + List of glob patterns to get file paths to be searched. + + See also: |glob()| + + *fuf-options-for-dir-mode* +For Directory Mode ~ + + *g:fuf_dir_prompt* > + let g:fuf_dir_prompt = '>Dir[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_dir_switchOrder* > + let g:fuf_dir_switchOrder = 40 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *g:fuf_dir_exclude* > + let g:fuf_dir_exclude = '\v(^|[/\\])\.(hg|git|bzr)($|[/\\])' +< + Regexp pattern for items which you want to exclude from completion + list. + + *fuf-options-for-mrufile-mode* +For MRU-File Mode ~ + + *g:fuf_mrufile_prompt* > + let g:fuf_mrufile_prompt = '>MRU-File[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_mrufile_switchOrder* > + let g:fuf_mrufile_switchOrder = 50 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *g:fuf_mrufile_exclude* > + let g:fuf_mrufile_exclude = '\v\~$|\.(o|exe|dll|bak|orig|sw[po])$|^(\/\/|\\\\|\/mnt\/|\/media\/)' +< + Regexp pattern for items which you want to exclude from completion + list. + + *g:fuf_mrufile_maxItem* > + let g:fuf_mrufile_maxItem = 200 +< + Ceiling for the number of MRU items to be stored. + + *g:fuf_mrufile_maxItemDir* > + let g:fuf_mrufile_maxItemDir = 50 +< + Ceiling for the number of parent directories of MRU items to be + stored, which are used for around search. + + *g:fuf_mrufile_keyExpand* > + let g:fuf_mrufile_keyExpand = '' +< + Key mapped to expand search range. + + *fuf-options-for-mrucmd-mode* +For MRU-Cmd Mode ~ + + *g:fuf_mrucmd_prompt* > + let g:fuf_mrucmd_prompt = '>MRU-Cmd[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_mrucmd_switchOrder* > + let g:fuf_mrucmd_switchOrder = 60 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *g:fuf_mrucmd_exclude* > + let g:fuf_mrucmd_exclude = '^$' +< + Regexp pattern for items which you want to exclude from completion + list. + + *g:fuf_mrucmd_maxItem* > + let g:fuf_mrucmd_maxItem = 200 +< + This is the ceiling for the number of MRU items to be stored. + + *fuf-options-for-bookmarkfile-mode* +For Bookmark-File Mode ~ + + *g:fuf_bookmarkfile_prompt* > + let g:fuf_bookmarkfile_prompt = '>BookmarkFile[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_bookmarkfile_switchOrder* > + let g:fuf_bookmarkfile_switchOrder = 70 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *g:fuf_bookmarkfile_searchRange* > + let g:fuf_bookmarkfile_searchRange = 400 +< + Number of lines which FuzzyFinder searches a matching line from + bookmarked position within. + + *g:fuf_bookmarkfile_keyDelete* > + let g:fuf_bookmarkfile_keyDelete = '' +< + Key mapped to delete selected bookmark. + + *fuf-options-for-bookmarkdir-mode* +For Bookmark-Dir Mode ~ + + *g:fuf_bookmarkdir_prompt* > + let g:fuf_bookmarkdir_prompt = '>BookmarkDir[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_bookmarkdir_switchOrder* > + let g:fuf_bookmarkdir_switchOrder = 80 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *g:fuf_bookmarkdir_keyDelete* > + let g:fuf_bookmarkdir_keyDelete = '' +< + Key mapped to delete selected bookmark. + + *fuf-options-for-tag-mode* +For Tag Mode ~ + + *g:fuf_tag_prompt* > + let g:fuf_tag_prompt = '>Tag[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_tag_switchOrder* > + let g:fuf_tag_switchOrder = 90 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *fuf-options-for-buffertag-mode* +For Buffer-Tag Mode ~ + + *g:fuf_buffertag_prompt* > + let g:fuf_buffertag_prompt = '>Buffer-Tag[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_buffertag_switchOrder* > + let g:fuf_buffertag_switchOrder = 100 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *g:fuf_buffertag_ctagsPath* > + let g:fuf_buffertag_ctagsPath = 'ctags' +< + Executable file path of Ctags. + + *fuf-options-for-taggedfile-mode* +For Tagged-File Mode ~ + + *g:fuf_taggedfile_prompt* > + let g:fuf_taggedfile_prompt = '>Tagged-File[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_taggedfile_switchOrder* > + let g:fuf_taggedfile_switchOrder = 110 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *fuf-options-for-jumplist-mode* +For Jump-List Mode ~ + + *g:fuf_jumplist_prompt* > + let g:fuf_jumplist_prompt = '>Jump-List[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_jumplist_switchOrder* > + let g:fuf_jumplist_switchOrder = 120 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *fuf-options-for-changelist-mode* +For Change-List Mode ~ + + *g:fuf_changelist_prompt* > + let g:fuf_changelist_prompt = '>Change-List[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_changelist_switchOrder* > + let g:fuf_changelist_switchOrder = 130 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *fuf-options-for-quickfix-mode* +For Quickfix Mode ~ + + *g:fuf_quickfix_prompt* > + let g:fuf_quickfix_prompt = '>Quickfix[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_quickfix_switchOrder* > + let g:fuf_quickfix_switchOrder = 140 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *fuf-options-for-line-mode* +For Line Mode ~ + + *g:fuf_line_prompt* > + let g:fuf_line_prompt = '>Line[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_line_switchOrder* > + let g:fuf_line_switchOrder = 150 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + *fuf-options-for-help-mode* +For Help Mode ~ + + *g:fuf_help_prompt* > + let g:fuf_help_prompt = '>Help[]>' +< + Prompt string. "[]" will be substituted with indicators. + + *g:fuf_help_switchOrder* > + let g:fuf_help_switchOrder = 160 +< + Number of order for switching to the next/previous mode. If negative + number, Fuzzyfinder never switches to this mode. + + +============================================================================== +VIMRC EXAMPLE *fuf-vimrc-example* + +> + let g:fuf_modesDisable = [] + let g:fuf_mrufile_maxItem = 400 + let g:fuf_mrucmd_maxItem = 400 + nnoremap sj :FufBuffer + nnoremap sk :FufFileWithCurrentBufferDir + nnoremap sK :FufFileWithFullCwd + nnoremap s :FufFile + nnoremap sl :FufCoverageFileChange + nnoremap sL :FufCoverageFileChange + nnoremap s :FufCoverageFileRegister + nnoremap sd :FufDirWithCurrentBufferDir + nnoremap sD :FufDirWithFullCwd + nnoremap s :FufDir + nnoremap sn :FufMruFile + nnoremap sN :FufMruFileInCwd + nnoremap sm :FufMruCmd + nnoremap su :FufBookmarkFile + nnoremap s :FufBookmarkFileAdd + vnoremap s :FufBookmarkFileAddAsSelectedText + nnoremap si :FufBookmarkDir + nnoremap s :FufBookmarkDirAdd + nnoremap st :FufTag + nnoremap sT :FufTag! + nnoremap s :FufTagWithCursorWord! + nnoremap s, :FufBufferTag + nnoremap s< :FufBufferTag! + vnoremap s, :FufBufferTagWithSelectedText! + vnoremap s< :FufBufferTagWithSelectedText + nnoremap s} :FufBufferTagWithCursorWord! + nnoremap s. :FufBufferTagAll + nnoremap s> :FufBufferTagAll! + vnoremap s. :FufBufferTagAllWithSelectedText! + vnoremap s> :FufBufferTagAllWithSelectedText + nnoremap s] :FufBufferTagAllWithCursorWord! + nnoremap sg :FufTaggedFile + nnoremap sG :FufTaggedFile! + nnoremap so :FufJumpList + nnoremap sp :FufChangeList + nnoremap sq :FufQuickfix + nnoremap sy :FufLine + nnoremap sh :FufHelp + nnoremap se :FufEditDataFile + nnoremap sr :FufRenewCache +< + +============================================================================== +SPECIAL THANKS *fuf-thanks* + +- Vincent Wang +- Ingo Karkat +- Nikolay Golubev +- Brian Doyle +- id:secondlife +- Nathan Neff + + +============================================================================== +CHANGELOG *fuf-changelog* + +4.2.2: + - Fixed a bug that unloaded buffers weren't covered by FufBufferTagAll. + +4.2.1: + - Improved response of Buffer-Tag mode. + - Fixed a bug that buffers which had been opened weren't listed in + Coverage-File mode + - Fixed a bug that tag entries including tab characters weren't parsed + correctly in Coverage-File mode + +4.2: + - L9 library (vimscript #3252) version 1.1 is required. + - Added Buffer-Tag mode, inspired by taglist.vim (vimscript #273). + - Added :FufMruFileInCwd command. + +4.1.1: + - Fixed a bug causing a error in MRU-File mode. + +4.1: + - Added Bookmark-Dir mode. + - Added Bookmark-File mode and removed Bookmark mode. + - Changed the filename to store data of Coverage-File mode, from + '~/.vim-fuf-data/coveragefile/items' to + '~/.vim-fuf-data/coveragefile/coverages' . + - Fixed a bug that floating point numbers weren't evaluated correctly and + caused errors on some non-English locales. + - Removed Around-MRU-File mode and integrated its feature to MRU-File mode. + +4.0: + - From this version, L9 library (vimscript #3252) is required. + - Added Coverage-File mode for users wanting something like TextMate's + command-t. (But I've never used it.) + - Added Around-MRU-File mode. (Too slow. There is room for improvement.) + - Added new feature which deletes selected buffer with FuzzyFinder and + g:fuf_buffer_keyDelete option. + - Added new feature which allows to set one-time options/variables with + fuf#setOneTimeVariables() function. + - Added g:fuf_dataDir option and removed g:fuf_infoFile, + g:g:fuf_tag_cache_dir, g:fuf_taggedfile_cache_dir, and + g:fuf_help_cache_dir options. + - Added :FufEditDataFile command and removed :FufEditInfo command. + - Added g:fuf_fuzzyRefining option. + - Added new feature which is auto-preview and g:fuf_autoPreview option. + - Changed the default value of g:fuf_previewHeight to 0 in order to disable + preview feature. There is an unfixable problem which is caused by a Vim's + bug. + - Changed the default value of g:fuf_modesDisable option. + - Changed the default value of g:fuf_*_switchOrder options. + - Improved speed of changing buffers. + - Improved the way to add user-defined mode. + - Fixed a bug that FuzzyFinder caused reseting window layout. + - Removed g:fuf_smartBs option. Use instead. + +3.5: + - Added Line mode. + - Added Help mode. + - Added key mapping to switch between fuzzy matching and partial matching. + - Changed the default values of g:fuf_file_exclude for ignoring "*.dll". + - Changed Tag mode and Tagged-File mode to cache parsed data to files in + "~/.vim-fuf-cache/". + - Fixed a bug that repeating preview key produced no effect. + - Fixed a bug that File mode and Directory mode didn't list items in a + directory whose name includes uppercase characters. (Thanks, ryo7000) + +3.4: + - Added new feature which makes it possible to preview selected completion + item. + - Changed matching rules and added g:fuf_splitPathMatching. + - Changed sorting rules. + - Changed the default values of g:fuf_file_exclude and g:fuf_dir_exclude in + order to ignore ".hg", ".git", and ".bzr" directories. + - Changed the default value of g:fuf_mrufile_exclude in order to ignore + network files (\\*) on Windows and ignore /mnt/* and /media/* on Unix like + systems. + - Fixed a bug that an exclude pattern of File, Dir, and Callback-File mode + can't be changed. + +3.3: + - Added Jump-List mode, Change-List mode, and Quickfix mode which enable + jumps with jump list, change list, and quickfix list. + - Added new feature which deletes selected bookmark with FuzzyFinder and + g:fuf_bookmark_keyDelete option. + - Changed default values of g:fuf_keyPrevPattern. + - Changed to show error message when incompatible with a installed vim. + +3.2: + - Added g:fuf_promptHighlight option to integrate such options for each + mode. + - Changed APIs of Given-File, Given-Directory, Given-Command, Callback-File, + and Callback-Item modes to be able to set a prompt string. + - Changed default values of g:fuf_keyPrevPattern and g:fuf_keyNextPattern. + - Fixed a bug that MRU-File data was not updated When a file was opened with + FuzzyFinder. + - Fixed a bug with scoring matchings for sorting. Thanks to Vincent. + - Brought back the removed feature which is switching to an other mode in + FuzzyFinder. + +3.1: + - Added new feature to recall patterns which have been entered before from + history. + +3.0: + - Redesigned the whole plugin for improvements of maintainability and + performance. "fuzzyfinder" is abbreviated to "fuf" in the sorce code and + filenames. All commands and options are renamed. + - Added new feature which is refining pattern. + - Improved the rules for sorting completion items. Thanks to the suggestion + by Nathan, the rule for boundary matching was implemented. + - Changed to open one line buffer of FuzzyFinder with :topleft command + instead of :leftabove. The window will alway appear at the top and occupy + the full with of the vim window. Thanks to Jan Christoph. + - Changed default filename of information file. + - Changed MRU-File mode and MRU-Command mode to be disabled by default + due to performance and side effect issues. + - Removed the feature which is switching to an other mode in FuzzyFinder. + - Removed the feature which is temporarily switching 'ignorecase' in + FuzzyFinder. + +2.22.3: + - Fixed a bug that Fuzzyfinder could not open files with '$' in the name on + Windows. + +2.22.2: + - Changed to consider a length of a date/time string when abbreviates long + completion items. + - Fixed a bug that '**/' pattern did not search for files directly under the + current working directory in File mode. Thanks to Martin for reporting. + +2.22.1: + - Fixed a bug that Fuzzyfinder could not expand abbreviations to patterns + including '\' correctly. + - Fixed to show item number in Given-File, Given-Directory, and + Given-Command mode. + +2.22.0: + - More improved the abbreviation method for long completion items. + - Added Given-File mode for third-party script to select a file from a given + list and open. + - Added Given-Directory mode for third-party script to select a directory + from a given list and change current working directory to it. + - Added Given-Command mode for third-party script to select a command from a + given list and execute. + - Changed ways to launch Callback-File mode and Callback-item mode. + +2.21.0: + - Improved a method of trimming long completion items. Thanks to Andy, + pyrhockz, and Nathan. + - Changed not to map command-line for MRU-Command mode if + g:FuzzyFinderOptions.MruCmd.mode_available is set 0 before loading + fuzzyfinder.vim. + - Added Callback-File mode and Callback-Item mode for third-party script to + find a file/directory or an item from a given list using Fuzzyfinder. + - Changed not to append ".." to a completion menu in File/Directory mode. + Use dot sequence feature. + - Changed default value of g:FuzzyFinderOptions.File.excluded_path option. + - Changed default value of g:FuzzyFinderOptions.Dir.excluded_path option. + - Fixed a bug that couldn't jump to a tag. Thanks to Thinca. + +2.20: + - Added help files which are doc/fuzzyfinder.txt and doc/fuzzyfinder.jax. + - Fixed a bug that an error occurs if current directory included spaces. + Thanks id:cho45 and id:secondlife. + - Implemented a feature to reuse a window containing target buffer. + - Added g:FuzzyFinderOptions.Buffer.reuse_window option. + - Added g:FuzzyFinderOptions.File.reuse_window option. + - Added g:FuzzyFinderOptions.MruFile.reuse_window option. + - Added g:FuzzyFinderOptions.Bookmark.reuse_window option. + - Added g:FuzzyFinderOptions.TaggedFile.reuse_window option. + - Changed to use 'omnifunc' instead of 'completefunc'. Now you can use + to delete all entered characters. + - Changed default value of g:FuzzyFinderOptions.Base.key_open_tab option. + - Changed default value of g:FuzzyFinderOptions.Base.key_next_mode option. + - Changed default value of g:FuzzyFinderOptions.Base.key_prev_mode option. + - Changed default value of g:FuzzyFinderOptions.Base.key_ignore_case option. + - Changed to truncate long completion items from the head instead of tail. + - Added g:FuzzyFinderOptions.Base.max_menu_width option instead of + g:FuzzyFinderOptions.Base.trim_length option. + - Added :FuzzyFinderFileWithFullCwd command. + - Added :FuzzyFinderFileWithCurrentBufferDir command. + - Added :FuzzyFinderDirWithFullCwd command. + - Added :FuzzyFinderDirWithCurrentBufferDir command. + - Added :FuzzyFinderTagWithCursorWord command. + - Renamed :FuzzyFinderRemoveCache command to :FuzzyFinderRenewCache. + +2.19: + - Changed MRU-File mode that always formats completion items to be relative + to the home directory. + - Fixed a bug that a file was opened in an unintended window with Tag List + plugin. Thanks Alexey. + - Fixed a bug that garbage characters were entered when switched current + mode. Thanks id:lugecy. + +2.18: + - Improved rules for the sorting of completion items. + - Changed not to learn a completion if an entered pattern is empty. + - Fixed a bug that Buffer mode did not work. Thanks ryo7000. + +2.17: + - Introduced a learning system for the sorting of completion items. + - Added g:FuzzyFinderOptions.Base.learning_limit option. + - Changed the specification of the information file. Please remove your + information file for Fuzzyfinder. + +2.16: + - Improved response time by caching in MRU-File mode. + - Fixed a bug in Bookmark mode that Fuzzyfinder did not jump to the + Bookmarked line number when Bookmarked pattern was not found. + +2.15: + - Added Bookmark mode. + - Removed Favorite-file mode. Use Bookmark mode instead. + - Fixed not to record a entry of input() in MRU-Command mode. + +2.14: + - Changed to show buffer status in Buffer mode. + - Fixed a bug that an error occurs when nonexistent buffer-name was entered + in Buffer mode. Thanks Maxim Kim. + - Added 'enumerating_limit' option. Thanks id:secondlife. + - Removed 'matching_limit' option. Use 'enumerating_limit' instead. + +2.13: + - Fixed a bug that a directory disappeared when a file in that directory was + being opened in File/Mru-File mode. + +2.12: + - Changed to be able to show completion items in the order of recently used + in Buffer mode. + - Added g:FuzzyFinderOptions.Buffer.mru_order option. + +2.11: + - Changed that a dot sequence of entered pattern is expanded to parent + directories in File/Dir mode. + E.g.: "foo/...bar" -> "foo/../../bar" + - Fixed a bug that a prompt string was excessively inserted. + +2.10: + - Changed not to show a current buffer in a completion menu. + - Fixed a bug that a filename to open was not been escaped. + - Added 'prompt' option. + - Added 'prompt_highlight' option. + - Removed g:FuzzyFinderOptions.MruFile.no_special_buffer option. + +2.9: + - Enhanced behavior in Fuzzyfinder and added 'smart_bs' option. + - Fixed a bug that entered pattern was not been escaped. + - Fixed not to insert "zv" with "c/pattern" command in Normal mode. + - Avoid the slow down problem caused by filereadable() check for the MRU + information in BufEnter/BufWritePost. + +2.8.1: + - Fixed a bug caused by the non-escaped buffer name "[Fuzzyfinder]". + - Fixed a command to open in a new tab page in Buffer mode. +2.8: + - Added 'trim_length' option. + - Added 'switch_order' option. + - Fixed a bug that entered command did not become the newest in the history. + - Fixed a bug that folds could not open with in a command-line when + searching. + - Removed 'excluded_indicator' option. Now a completion list in Buffer mode + is the same as a result of :buffers. + +2.7: + - Changed to find an item whose index is matched with the number suffixed + with entered pattern. + - Fixed the cache bug after changing current directory in File mode. + +2.6.2: + - Fixed not to miss changes in options when updates the MRU information. + +2.6.1: + - Fixed a bug related to floating-point support. + - Added support for GetLatestVimScripts. + +2.6: + - Revived MRU-command mode. The problem with a command-line abbreviation was + solved. + - Changed the specification of the information file. + - Added :FuzzyFinderEditInfo command. + +2.5.1: + - Fixed to be able to match "foo/./bar" by "foo/**/bar" in File mode. + - Fixed to be able to open a space-containing file in File mode. + - Fixed to honor the current working directory properly in File mode. + +2.5: + - Fixed the bug that a wrong initial text is entered after switching to a + next mode. + - Fixed the bug that it does not return to previous window after leaving + Fuzzyfinder one. + +2.4: + - Fixed the bug that Fuzzyfinder fails to open a file caused by auto-cd + plugin/script. + +2.3: + - Added a key mapping to open items in a new tab page and + g:FuzzyFinderOptions.Base.key_open_tab option. + - Changed to show Fuzzyfinder window above last window even if 'splitbelow' + was set. + - Changed to set nocursorline and nocursorcolumn in Fuzzyfinder. + - Fixed not to push up a buffer number unlimitedly. + +2.2: + - Added new feature, which is the partial matching. + - Fixed the bug that an error occurs when "'" was entered. + +2.1: + - Restructured the option system AGAIN. Sorry :p + - Changed to inherit a typed text when switching a mode without leaving + Insert mode. + - Changed commands which launch explorers to be able to take a argument for + initial text. + - Changed to complete file names by relative path and not full path in the + buffer/mru-file/tagged-file mode. + - Changed to highlight a typed text when the completion item was not found + or the completion process was aborted. + - Changed to create caches for each tag file and not working directory in + the tag/tagged-file mode. + - Fixed the bug that the buffer mode couldn't open a unnamed buffer. + - Added 'matching_limit' option. + - Removed 'max_match' option. Use 'matching_limit' option instead. + - Removed 'initial_text' option. Use command argument instead. + - Removed the MRU-command mode. + +2.0: + - Added the tag mode. + - Added the tagged-file mode. + - Added :FuzzyFinderRemoveCache command. + - Restructured the option system. many options are changed names or default + values of some options. + - Changed to hold and reuse caches of completion lists by default. + - Changed to set filetype 'fuzzyfinder'. + - Disabled the MRU-command mode by default because there are problems. + - Removed FuzzyFinderAddMode command. + +1.5: + - Added the directory mode. + - Fixed the bug that it caused an error when switch a mode in Insert mode. + - Changed g:FuzzyFinder_KeySwitchMode type to a list. + +1.4: + - Changed the specification of the information file. + - Added the MRU-commands mode. + - Renamed :FuzzyFinderAddFavorite command to :FuzzyFinderAddFavFile. + - Renamed g:FuzzyFinder_MruModeVars option to g:FuzzyFinder_MruFileModeVars. + - Renamed g:FuzzyFinder_FavoriteModeVars option to + g:FuzzyFinder_FavFileModeVars. + - Changed to show registered time of each item in MRU/favorite mode. + - Added 'timeFormat' option for MRU/favorite modes. + +1.3: + - Fixed a handling of multi-byte characters. + +1.2: + - Added support for Migemo. (Migemo is Japanese search method.) + +1.1: + - Added the favorite mode. + - Added new features, which are abbreviations and multiple search. + - Added 'abbrevMap' option for each mode. + - Added g:FuzzyFinder_MruModeVars['ignoreSpecialBuffers'] option. + - Fixed the bug that it did not work correctly when a user have mapped + or . + +1.0: + - Added the MRU mode. + - Added commands to add and use original mode. + - Improved the sorting algorithm for completion items. + - Added 'initialInput' option to automatically insert a text at the + beginning of a mode. + - Changed that 'excludedPath' option works for the entire path. + - Renamed some options. + - Changed default values of some options. + - Packed the mode-specific options to dictionaries. + - Removed some options. + +0.6: + - Fixed some bugs. + +0.5: + - Improved response by aborting processing too many items. + - Changed to be able to open a buffer/file not only in previous window but + also in new window. + - Fixed a bug that recursive searching with '**' does not work. + - Added g:FuzzyFinder_CompletionItemLimit option. + - Added g:FuzzyFinder_KeyOpen option. + +0.4: + - Improved response of the input. + - Improved the sorting algorithm for completion items. It is based on the + matching level. 1st is perfect matching, 2nd is prefix matching, and 3rd + is fuzzy matching. + - Added g:FuzzyFinder_ExcludePattern option. + - Removed g:FuzzyFinder_WildIgnore option. + - Removed g:FuzzyFinder_EchoPattern option. + - Removed g:FuzzyFinder_PathSeparator option. + - Changed the default value of g:FuzzyFinder_MinLengthFile from 1 to 0. + +0.3: + - Added g:FuzzyFinder_IgnoreCase option. + - Added g:FuzzyFinder_KeyToggleIgnoreCase option. + - Added g:FuzzyFinder_EchoPattern option. + - Changed the open command in a buffer mode from ":edit" to ":buffer" to + avoid being reset cursor position. + - Changed the default value of g:FuzzyFinder_KeyToggleMode from to + because does not work on some CUI environments. + - Changed to avoid being loaded by Vim before 7.0. + - Fixed a bug with making a fuzzy pattern which has '\'. + +0.2: + - A bug it does not work on Linux is fixed. + +0.1: + - First release. + + +============================================================================== +ABOUT *fuf-about* *fuf-contact* *fuf-author* + +Author: Takeshi NISHIDA +Licence: MIT Licence +URL: http://www.vim.org/scripts/script.php?script_id=1984 + http://bitbucket.org/ns9tks/vim-fuzzyfinder/ + +Bugs/Issues/Suggestions/Improvements ~ + +Please submit to http://bitbucket.org/ns9tks/vim-fuzzyfinder/issues/ . + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: diff --git a/fuzzyfinder/doc/tags b/fuzzyfinder/doc/tags new file mode 100644 index 0000000..a7b5103 --- /dev/null +++ b/fuzzyfinder/doc/tags @@ -0,0 +1,176 @@ +:FufBookmarkDir fuf.txt /*:FufBookmarkDir* +:FufBookmarkDirAdd fuf.txt /*:FufBookmarkDirAdd* +:FufBookmarkFile fuf.txt /*:FufBookmarkFile* +:FufBookmarkFileAdd fuf.txt /*:FufBookmarkFileAdd* +:FufBookmarkFileAddAsSelectedText fuf.txt /*:FufBookmarkFileAddAsSelectedText* +:FufBuffer fuf.txt /*:FufBuffer* +:FufBufferTag fuf.txt /*:FufBufferTag* +:FufBufferTagAll fuf.txt /*:FufBufferTagAll* +:FufBufferTagAllWithCursorWord fuf.txt /*:FufBufferTagAllWithCursorWord* +:FufBufferTagAllWithSelectedText fuf.txt /*:FufBufferTagAllWithSelectedText* +:FufBufferTagWithCursorWord fuf.txt /*:FufBufferTagWithCursorWord* +:FufBufferTagWithSelectedText fuf.txt /*:FufBufferTagWithSelectedText* +:FufChangeList fuf.txt /*:FufChangeList* +:FufCoverageFile fuf.txt /*:FufCoverageFile* +:FufCoverageFileChange fuf.txt /*:FufCoverageFileChange* +:FufCoverageFileRegister fuf.txt /*:FufCoverageFileRegister* +:FufDir fuf.txt /*:FufDir* +:FufDirWithCurrentBufferDir fuf.txt /*:FufDirWithCurrentBufferDir* +:FufDirWithFullCwd fuf.txt /*:FufDirWithFullCwd* +:FufEditDataFile fuf.txt /*:FufEditDataFile* +:FufFile fuf.txt /*:FufFile* +:FufFileWithCurrentBufferDir fuf.txt /*:FufFileWithCurrentBufferDir* +:FufFileWithFullCwd fuf.txt /*:FufFileWithFullCwd* +:FufHelp fuf.txt /*:FufHelp* +:FufJumpList fuf.txt /*:FufJumpList* +:FufLine fuf.txt /*:FufLine* +:FufMruCmd fuf.txt /*:FufMruCmd* +:FufMruFile fuf.txt /*:FufMruFile* +:FufMruFileInCwd fuf.txt /*:FufMruFileInCwd* +:FufQuickfix fuf.txt /*:FufQuickfix* +:FufRenewCache fuf.txt /*:FufRenewCache* +:FufTag fuf.txt /*:FufTag* +:FufTagWithCursorWord fuf.txt /*:FufTagWithCursorWord* +:FufTaggedFile fuf.txt /*:FufTaggedFile* +abc fuf.txt /*abc* +fuf fuf.txt /*fuf* +fuf#setOneTimeVariables() fuf.txt /*fuf#setOneTimeVariables()* +fuf-abbreviation fuf.txt /*fuf-abbreviation* +fuf-about fuf.txt /*fuf-about* +fuf-author fuf.txt /*fuf-author* +fuf-bookmarkdir-mode fuf.txt /*fuf-bookmarkdir-mode* +fuf-bookmarkfile-mode fuf.txt /*fuf-bookmarkfile-mode* +fuf-buffer-mode fuf.txt /*fuf-buffer-mode* +fuf-buffertag-mode fuf.txt /*fuf-buffertag-mode* +fuf-cache fuf.txt /*fuf-cache* +fuf-callbackfile-mode fuf.txt /*fuf-callbackfile-mode* +fuf-callbackitem-mode fuf.txt /*fuf-callbackitem-mode* +fuf-changelist-mode fuf.txt /*fuf-changelist-mode* +fuf-changelog fuf.txt /*fuf-changelog* +fuf-commands fuf.txt /*fuf-commands* +fuf-contact fuf.txt /*fuf-contact* +fuf-coveragefile-mode fuf.txt /*fuf-coveragefile-mode* +fuf-data-file fuf.txt /*fuf-data-file* +fuf-detailed-topics fuf.txt /*fuf-detailed-topics* +fuf-dir-mode fuf.txt /*fuf-dir-mode* +fuf-dot-sequence fuf.txt /*fuf-dot-sequence* +fuf-file-mode fuf.txt /*fuf-file-mode* +fuf-givencmd-mode fuf.txt /*fuf-givencmd-mode* +fuf-givendir-mode fuf.txt /*fuf-givendir-mode* +fuf-givenfile-mode fuf.txt /*fuf-givenfile-mode* +fuf-help-mode fuf.txt /*fuf-help-mode* +fuf-hiding-menu fuf.txt /*fuf-hiding-menu* +fuf-how-to-add-mode fuf.txt /*fuf-how-to-add-mode* +fuf-installation fuf.txt /*fuf-installation* +fuf-introduction fuf.txt /*fuf-introduction* +fuf-jumplist-mode fuf.txt /*fuf-jumplist-mode* +fuf-line-mode fuf.txt /*fuf-line-mode* +fuf-migemo fuf.txt /*fuf-migemo* +fuf-modes fuf.txt /*fuf-modes* +fuf-mrucmd-mode fuf.txt /*fuf-mrucmd-mode* +fuf-mrufile-mode fuf.txt /*fuf-mrufile-mode* +fuf-multiple-search fuf.txt /*fuf-multiple-search* +fuf-options fuf.txt /*fuf-options* +fuf-options-for-all-modes fuf.txt /*fuf-options-for-all-modes* +fuf-options-for-bookmarkdir-mode fuf.txt /*fuf-options-for-bookmarkdir-mode* +fuf-options-for-bookmarkfile-mode fuf.txt /*fuf-options-for-bookmarkfile-mode* +fuf-options-for-buffer-mode fuf.txt /*fuf-options-for-buffer-mode* +fuf-options-for-buffertag-mode fuf.txt /*fuf-options-for-buffertag-mode* +fuf-options-for-changelist-mode fuf.txt /*fuf-options-for-changelist-mode* +fuf-options-for-coveragefile-mode fuf.txt /*fuf-options-for-coveragefile-mode* +fuf-options-for-dir-mode fuf.txt /*fuf-options-for-dir-mode* +fuf-options-for-file-mode fuf.txt /*fuf-options-for-file-mode* +fuf-options-for-help-mode fuf.txt /*fuf-options-for-help-mode* +fuf-options-for-jumplist-mode fuf.txt /*fuf-options-for-jumplist-mode* +fuf-options-for-line-mode fuf.txt /*fuf-options-for-line-mode* +fuf-options-for-mrucmd-mode fuf.txt /*fuf-options-for-mrucmd-mode* +fuf-options-for-mrufile-mode fuf.txt /*fuf-options-for-mrufile-mode* +fuf-options-for-quickfix-mode fuf.txt /*fuf-options-for-quickfix-mode* +fuf-options-for-tag-mode fuf.txt /*fuf-options-for-tag-mode* +fuf-options-for-taggedfile-mode fuf.txt /*fuf-options-for-taggedfile-mode* +fuf-quickfix-mode fuf.txt /*fuf-quickfix-mode* +fuf-reusing-window fuf.txt /*fuf-reusing-window* +fuf-search-patterns fuf.txt /*fuf-search-patterns* +fuf-setting-one-time-option fuf.txt /*fuf-setting-one-time-option* +fuf-sorting-of-completion-items fuf.txt /*fuf-sorting-of-completion-items* +fuf-tag-mode fuf.txt /*fuf-tag-mode* +fuf-taggedfile-mode fuf.txt /*fuf-taggedfile-mode* +fuf-thanks fuf.txt /*fuf-thanks* +fuf-usage fuf.txt /*fuf-usage* +fuf-vimrc-example fuf.txt /*fuf-vimrc-example* +fuf.txt fuf.txt /*fuf.txt* +fuzzyfinder fuf.txt /*fuzzyfinder* +g:fuf_abbrevMap fuf.txt /*g:fuf_abbrevMap* +g:fuf_autoPreview fuf.txt /*g:fuf_autoPreview* +g:fuf_bookmarkdir_keyDelete fuf.txt /*g:fuf_bookmarkdir_keyDelete* +g:fuf_bookmarkdir_prompt fuf.txt /*g:fuf_bookmarkdir_prompt* +g:fuf_bookmarkdir_switchOrder fuf.txt /*g:fuf_bookmarkdir_switchOrder* +g:fuf_bookmarkfile_keyDelete fuf.txt /*g:fuf_bookmarkfile_keyDelete* +g:fuf_bookmarkfile_prompt fuf.txt /*g:fuf_bookmarkfile_prompt* +g:fuf_bookmarkfile_searchRange fuf.txt /*g:fuf_bookmarkfile_searchRange* +g:fuf_bookmarkfile_switchOrder fuf.txt /*g:fuf_bookmarkfile_switchOrder* +g:fuf_buffer_keyDelete fuf.txt /*g:fuf_buffer_keyDelete* +g:fuf_buffer_mruOrder fuf.txt /*g:fuf_buffer_mruOrder* +g:fuf_buffer_prompt fuf.txt /*g:fuf_buffer_prompt* +g:fuf_buffer_switchOrder fuf.txt /*g:fuf_buffer_switchOrder* +g:fuf_buffertag_ctagsPath fuf.txt /*g:fuf_buffertag_ctagsPath* +g:fuf_buffertag_prompt fuf.txt /*g:fuf_buffertag_prompt* +g:fuf_buffertag_switchOrder fuf.txt /*g:fuf_buffertag_switchOrder* +g:fuf_changelist_prompt fuf.txt /*g:fuf_changelist_prompt* +g:fuf_changelist_switchOrder fuf.txt /*g:fuf_changelist_switchOrder* +g:fuf_coveragefile_exclude fuf.txt /*g:fuf_coveragefile_exclude* +g:fuf_coveragefile_globPatterns fuf.txt /*g:fuf_coveragefile_globPatterns* +g:fuf_coveragefile_prompt fuf.txt /*g:fuf_coveragefile_prompt* +g:fuf_coveragefile_switchOrder fuf.txt /*g:fuf_coveragefile_switchOrder* +g:fuf_dataDir fuf.txt /*g:fuf_dataDir* +g:fuf_dir_exclude fuf.txt /*g:fuf_dir_exclude* +g:fuf_dir_prompt fuf.txt /*g:fuf_dir_prompt* +g:fuf_dir_switchOrder fuf.txt /*g:fuf_dir_switchOrder* +g:fuf_enumeratingLimit fuf.txt /*g:fuf_enumeratingLimit* +g:fuf_file_exclude fuf.txt /*g:fuf_file_exclude* +g:fuf_file_prompt fuf.txt /*g:fuf_file_prompt* +g:fuf_file_switchOrder fuf.txt /*g:fuf_file_switchOrder* +g:fuf_fuzzyRefining fuf.txt /*g:fuf_fuzzyRefining* +g:fuf_help_prompt fuf.txt /*g:fuf_help_prompt* +g:fuf_help_switchOrder fuf.txt /*g:fuf_help_switchOrder* +g:fuf_ignoreCase fuf.txt /*g:fuf_ignoreCase* +g:fuf_jumplist_prompt fuf.txt /*g:fuf_jumplist_prompt* +g:fuf_jumplist_switchOrder fuf.txt /*g:fuf_jumplist_switchOrder* +g:fuf_keyNextMode fuf.txt /*g:fuf_keyNextMode* +g:fuf_keyNextPattern fuf.txt /*g:fuf_keyNextPattern* +g:fuf_keyOpen fuf.txt /*g:fuf_keyOpen* +g:fuf_keyOpenSplit fuf.txt /*g:fuf_keyOpenSplit* +g:fuf_keyOpenTabpage fuf.txt /*g:fuf_keyOpenTabpage* +g:fuf_keyOpenVsplit fuf.txt /*g:fuf_keyOpenVsplit* +g:fuf_keyPrevMode fuf.txt /*g:fuf_keyPrevMode* +g:fuf_keyPrevPattern fuf.txt /*g:fuf_keyPrevPattern* +g:fuf_keyPreview fuf.txt /*g:fuf_keyPreview* +g:fuf_keySwitchMatching fuf.txt /*g:fuf_keySwitchMatching* +g:fuf_learningLimit fuf.txt /*g:fuf_learningLimit* +g:fuf_line_prompt fuf.txt /*g:fuf_line_prompt* +g:fuf_line_switchOrder fuf.txt /*g:fuf_line_switchOrder* +g:fuf_maxMenuWidth fuf.txt /*g:fuf_maxMenuWidth* +g:fuf_modesDisable fuf.txt /*g:fuf_modesDisable* +g:fuf_mrucmd_exclude fuf.txt /*g:fuf_mrucmd_exclude* +g:fuf_mrucmd_maxItem fuf.txt /*g:fuf_mrucmd_maxItem* +g:fuf_mrucmd_prompt fuf.txt /*g:fuf_mrucmd_prompt* +g:fuf_mrucmd_switchOrder fuf.txt /*g:fuf_mrucmd_switchOrder* +g:fuf_mrufile_exclude fuf.txt /*g:fuf_mrufile_exclude* +g:fuf_mrufile_keyExpand fuf.txt /*g:fuf_mrufile_keyExpand* +g:fuf_mrufile_maxItem fuf.txt /*g:fuf_mrufile_maxItem* +g:fuf_mrufile_maxItemDir fuf.txt /*g:fuf_mrufile_maxItemDir* +g:fuf_mrufile_prompt fuf.txt /*g:fuf_mrufile_prompt* +g:fuf_mrufile_switchOrder fuf.txt /*g:fuf_mrufile_switchOrder* +g:fuf_patternSeparator fuf.txt /*g:fuf_patternSeparator* +g:fuf_previewHeight fuf.txt /*g:fuf_previewHeight* +g:fuf_promptHighlight fuf.txt /*g:fuf_promptHighlight* +g:fuf_quickfix_prompt fuf.txt /*g:fuf_quickfix_prompt* +g:fuf_quickfix_switchOrder fuf.txt /*g:fuf_quickfix_switchOrder* +g:fuf_reuseWindow fuf.txt /*g:fuf_reuseWindow* +g:fuf_splitPathMatching fuf.txt /*g:fuf_splitPathMatching* +g:fuf_tag_prompt fuf.txt /*g:fuf_tag_prompt* +g:fuf_tag_switchOrder fuf.txt /*g:fuf_tag_switchOrder* +g:fuf_taggedfile_prompt fuf.txt /*g:fuf_taggedfile_prompt* +g:fuf_taggedfile_switchOrder fuf.txt /*g:fuf_taggedfile_switchOrder* +g:fuf_timeFormat fuf.txt /*g:fuf_timeFormat* +g:fuf_useMigemo fuf.txt /*g:fuf_useMigemo* diff --git a/fuzzyfinder/doc/tags-ja b/fuzzyfinder/doc/tags-ja new file mode 100644 index 0000000..f031b24 --- /dev/null +++ b/fuzzyfinder/doc/tags-ja @@ -0,0 +1,174 @@ +!_TAG_FILE_ENCODING utf-8 // +:FufBookmarkDir fuf.jax /*:FufBookmarkDir* +:FufBookmarkDirAdd fuf.jax /*:FufBookmarkDirAdd* +:FufBookmarkFile fuf.jax /*:FufBookmarkFile* +:FufBookmarkFileAdd fuf.jax /*:FufBookmarkFileAdd* +:FufBookmarkFileAddAsSelectedText fuf.jax /*:FufBookmarkFileAddAsSelectedText* +:FufBuffer fuf.jax /*:FufBuffer* +:FufBufferTag fuf.jax /*:FufBufferTag* +:FufBufferTagAll fuf.jax /*:FufBufferTagAll* +:FufBufferTagAllWithCursorWord fuf.jax /*:FufBufferTagAllWithCursorWord* +:FufBufferTagAllWithSelectedText fuf.jax /*:FufBufferTagAllWithSelectedText* +:FufBufferTagWithCursorWord fuf.jax /*:FufBufferTagWithCursorWord* +:FufBufferTagWithSelectedText fuf.jax /*:FufBufferTagWithSelectedText* +:FufChangeList fuf.jax /*:FufChangeList* +:FufCoverageFileChange fuf.jax /*:FufCoverageFileChange* +:FufCoverageFileRegister fuf.jax /*:FufCoverageFileRegister* +:FufDir fuf.jax /*:FufDir* +:FufDirWithCurrentBufferDir fuf.jax /*:FufDirWithCurrentBufferDir* +:FufDirWithFullCwd fuf.jax /*:FufDirWithFullCwd* +:FufEditDataFile fuf.jax /*:FufEditDataFile* +:FufFile fuf.jax /*:FufFile* +:FufFileWithCurrentBufferDir fuf.jax /*:FufFileWithCurrentBufferDir* +:FufFileWithFullCwd fuf.jax /*:FufFileWithFullCwd* +:FufHelp fuf.jax /*:FufHelp* +:FufJumpList fuf.jax /*:FufJumpList* +:FufLine fuf.jax /*:FufLine* +:FufMruCmd fuf.jax /*:FufMruCmd* +:FufMruFile fuf.jax /*:FufMruFile* +:FufMruFileInCwd fuf.jax /*:FufMruFileInCwd* +:FufQuickfix fuf.jax /*:FufQuickfix* +:FufRenewCache fuf.jax /*:FufRenewCache* +:FufTag fuf.jax /*:FufTag* +:FufTagWithCursorWord fuf.jax /*:FufTagWithCursorWord* +:FufTaggedFile fuf.jax /*:FufTaggedFile* +abc fuf.jax /*abc* +fuf fuf.jax /*fuf* +fuf#setOneTimeVariables() fuf.jax /*fuf#setOneTimeVariables()* +fuf-abbreviation fuf.jax /*fuf-abbreviation* +fuf-about fuf.jax /*fuf-about* +fuf-author fuf.jax /*fuf-author* +fuf-bookmarkdir-mode fuf.jax /*fuf-bookmarkdir-mode* +fuf-bookmarkfile-mode fuf.jax /*fuf-bookmarkfile-mode* +fuf-buffer-mode fuf.jax /*fuf-buffer-mode* +fuf-buffertag-mode fuf.jax /*fuf-buffertag-mode* +fuf-cache fuf.jax /*fuf-cache* +fuf-callbackfile-mode fuf.jax /*fuf-callbackfile-mode* +fuf-callbackitem-mode fuf.jax /*fuf-callbackitem-mode* +fuf-changelist-mode fuf.jax /*fuf-changelist-mode* +fuf-commands fuf.jax /*fuf-commands* +fuf-contact fuf.jax /*fuf-contact* +fuf-coveragefile-mode fuf.jax /*fuf-coveragefile-mode* +fuf-data-file fuf.jax /*fuf-data-file* +fuf-detailed-topics fuf.jax /*fuf-detailed-topics* +fuf-dir-mode fuf.jax /*fuf-dir-mode* +fuf-dot-sequence fuf.jax /*fuf-dot-sequence* +fuf-file-mode fuf.jax /*fuf-file-mode* +fuf-givencmd-mode fuf.jax /*fuf-givencmd-mode* +fuf-givendir-mode fuf.jax /*fuf-givendir-mode* +fuf-givenfile-mode fuf.jax /*fuf-givenfile-mode* +fuf-help-mode fuf.jax /*fuf-help-mode* +fuf-hiding-menu fuf.jax /*fuf-hiding-menu* +fuf-how-to-add-mode fuf.jax /*fuf-how-to-add-mode* +fuf-installation fuf.jax /*fuf-installation* +fuf-introduction fuf.jax /*fuf-introduction* +fuf-jumplist-mode fuf.jax /*fuf-jumplist-mode* +fuf-line-mode fuf.jax /*fuf-line-mode* +fuf-migemo fuf.jax /*fuf-migemo* +fuf-modes fuf.jax /*fuf-modes* +fuf-mrucmd-mode fuf.jax /*fuf-mrucmd-mode* +fuf-mrufile-mode fuf.jax /*fuf-mrufile-mode* +fuf-multiple-search fuf.jax /*fuf-multiple-search* +fuf-options fuf.jax /*fuf-options* +fuf-options-for-all-modes fuf.jax /*fuf-options-for-all-modes* +fuf-options-for-bookmarkdir-mode fuf.jax /*fuf-options-for-bookmarkdir-mode* +fuf-options-for-bookmarkfile-mode fuf.jax /*fuf-options-for-bookmarkfile-mode* +fuf-options-for-buffer-mode fuf.jax /*fuf-options-for-buffer-mode* +fuf-options-for-buffertag-mode fuf.jax /*fuf-options-for-buffertag-mode* +fuf-options-for-changelist-mode fuf.jax /*fuf-options-for-changelist-mode* +fuf-options-for-coveragefile-mode fuf.jax /*fuf-options-for-coveragefile-mode* +fuf-options-for-dir-mode fuf.jax /*fuf-options-for-dir-mode* +fuf-options-for-file-mode fuf.jax /*fuf-options-for-file-mode* +fuf-options-for-help-mode fuf.jax /*fuf-options-for-help-mode* +fuf-options-for-jumplist-mode fuf.jax /*fuf-options-for-jumplist-mode* +fuf-options-for-line-mode fuf.jax /*fuf-options-for-line-mode* +fuf-options-for-mrucmd-mode fuf.jax /*fuf-options-for-mrucmd-mode* +fuf-options-for-mrufile-mode fuf.jax /*fuf-options-for-mrufile-mode* +fuf-options-for-quickfix-mode fuf.jax /*fuf-options-for-quickfix-mode* +fuf-options-for-tag-mode fuf.jax /*fuf-options-for-tag-mode* +fuf-options-for-taggedfile-mode fuf.jax /*fuf-options-for-taggedfile-mode* +fuf-quickfix-mode fuf.jax /*fuf-quickfix-mode* +fuf-reusing-window fuf.jax /*fuf-reusing-window* +fuf-search-patterns fuf.jax /*fuf-search-patterns* +fuf-setting-one-time-option fuf.jax /*fuf-setting-one-time-option* +fuf-sorting-of-completion-items fuf.jax /*fuf-sorting-of-completion-items* +fuf-tag-mode fuf.jax /*fuf-tag-mode* +fuf-taggedfile-mode fuf.jax /*fuf-taggedfile-mode* +fuf-usage fuf.jax /*fuf-usage* +fuf-vimrc-example fuf.jax /*fuf-vimrc-example* +fuf.jax fuf.jax /*fuf.jax* +fuzzyfinder fuf.jax /*fuzzyfinder* +g:fuf_abbrevMap fuf.jax /*g:fuf_abbrevMap* +g:fuf_autoPreview fuf.jax /*g:fuf_autoPreview* +g:fuf_bookmarkdir_keyDelete fuf.jax /*g:fuf_bookmarkdir_keyDelete* +g:fuf_bookmarkdir_prompt fuf.jax /*g:fuf_bookmarkdir_prompt* +g:fuf_bookmarkdir_switchOrder fuf.jax /*g:fuf_bookmarkdir_switchOrder* +g:fuf_bookmarkfile_keyDelete fuf.jax /*g:fuf_bookmarkfile_keyDelete* +g:fuf_bookmarkfile_prompt fuf.jax /*g:fuf_bookmarkfile_prompt* +g:fuf_bookmarkfile_searchRange fuf.jax /*g:fuf_bookmarkfile_searchRange* +g:fuf_bookmarkfile_switchOrder fuf.jax /*g:fuf_bookmarkfile_switchOrder* +g:fuf_buffer_keyDelete fuf.jax /*g:fuf_buffer_keyDelete* +g:fuf_buffer_mruOrder fuf.jax /*g:fuf_buffer_mruOrder* +g:fuf_buffer_prompt fuf.jax /*g:fuf_buffer_prompt* +g:fuf_buffer_switchOrder fuf.jax /*g:fuf_buffer_switchOrder* +g:fuf_buffertag_ctagsPath fuf.jax /*g:fuf_buffertag_ctagsPath* +g:fuf_buffertag_prompt fuf.jax /*g:fuf_buffertag_prompt* +g:fuf_buffertag_switchOrder fuf.jax /*g:fuf_buffertag_switchOrder* +g:fuf_changelist_prompt fuf.jax /*g:fuf_changelist_prompt* +g:fuf_changelist_switchOrder fuf.jax /*g:fuf_changelist_switchOrder* +g:fuf_coveragefile_exclude fuf.jax /*g:fuf_coveragefile_exclude* +g:fuf_coveragefile_globPatterns fuf.jax /*g:fuf_coveragefile_globPatterns* +g:fuf_coveragefile_prompt fuf.jax /*g:fuf_coveragefile_prompt* +g:fuf_coveragefile_switchOrder fuf.jax /*g:fuf_coveragefile_switchOrder* +g:fuf_dataDir fuf.jax /*g:fuf_dataDir* +g:fuf_dir_exclude fuf.jax /*g:fuf_dir_exclude* +g:fuf_dir_prompt fuf.jax /*g:fuf_dir_prompt* +g:fuf_dir_switchOrder fuf.jax /*g:fuf_dir_switchOrder* +g:fuf_enumeratingLimit fuf.jax /*g:fuf_enumeratingLimit* +g:fuf_file_exclude fuf.jax /*g:fuf_file_exclude* +g:fuf_file_prompt fuf.jax /*g:fuf_file_prompt* +g:fuf_file_switchOrder fuf.jax /*g:fuf_file_switchOrder* +g:fuf_fuzzyRefining fuf.jax /*g:fuf_fuzzyRefining* +g:fuf_help_prompt fuf.jax /*g:fuf_help_prompt* +g:fuf_help_switchOrder fuf.jax /*g:fuf_help_switchOrder* +g:fuf_ignoreCase fuf.jax /*g:fuf_ignoreCase* +g:fuf_jumplist_prompt fuf.jax /*g:fuf_jumplist_prompt* +g:fuf_jumplist_switchOrder fuf.jax /*g:fuf_jumplist_switchOrder* +g:fuf_keyNextMode fuf.jax /*g:fuf_keyNextMode* +g:fuf_keyNextPattern fuf.jax /*g:fuf_keyNextPattern* +g:fuf_keyOpen fuf.jax /*g:fuf_keyOpen* +g:fuf_keyOpenSplit fuf.jax /*g:fuf_keyOpenSplit* +g:fuf_keyOpenTabpage fuf.jax /*g:fuf_keyOpenTabpage* +g:fuf_keyOpenVsplit fuf.jax /*g:fuf_keyOpenVsplit* +g:fuf_keyPrevMode fuf.jax /*g:fuf_keyPrevMode* +g:fuf_keyPrevPattern fuf.jax /*g:fuf_keyPrevPattern* +g:fuf_keyPreview fuf.jax /*g:fuf_keyPreview* +g:fuf_keySwitchMatching fuf.jax /*g:fuf_keySwitchMatching* +g:fuf_learningLimit fuf.jax /*g:fuf_learningLimit* +g:fuf_line_prompt fuf.jax /*g:fuf_line_prompt* +g:fuf_line_switchOrder fuf.jax /*g:fuf_line_switchOrder* +g:fuf_maxMenuWidth fuf.jax /*g:fuf_maxMenuWidth* +g:fuf_modesDisable fuf.jax /*g:fuf_modesDisable* +g:fuf_mrucmd_exclude fuf.jax /*g:fuf_mrucmd_exclude* +g:fuf_mrucmd_maxItem fuf.jax /*g:fuf_mrucmd_maxItem* +g:fuf_mrucmd_prompt fuf.jax /*g:fuf_mrucmd_prompt* +g:fuf_mrucmd_switchOrder fuf.jax /*g:fuf_mrucmd_switchOrder* +g:fuf_mrufile_exclude fuf.jax /*g:fuf_mrufile_exclude* +g:fuf_mrufile_keyExpand fuf.jax /*g:fuf_mrufile_keyExpand* +g:fuf_mrufile_maxItem fuf.jax /*g:fuf_mrufile_maxItem* +g:fuf_mrufile_maxItemDir fuf.jax /*g:fuf_mrufile_maxItemDir* +g:fuf_mrufile_prompt fuf.jax /*g:fuf_mrufile_prompt* +g:fuf_mrufile_switchOrder fuf.jax /*g:fuf_mrufile_switchOrder* +g:fuf_patternSeparator fuf.jax /*g:fuf_patternSeparator* +g:fuf_previewHeight fuf.jax /*g:fuf_previewHeight* +g:fuf_promptHighlight fuf.jax /*g:fuf_promptHighlight* +g:fuf_quickfix_prompt fuf.jax /*g:fuf_quickfix_prompt* +g:fuf_quickfix_switchOrder fuf.jax /*g:fuf_quickfix_switchOrder* +g:fuf_reuseWindow fuf.jax /*g:fuf_reuseWindow* +g:fuf_splitPathMatching fuf.jax /*g:fuf_splitPathMatching* +g:fuf_tag_prompt fuf.jax /*g:fuf_tag_prompt* +g:fuf_tag_switchOrder fuf.jax /*g:fuf_tag_switchOrder* +g:fuf_taggedfile_prompt fuf.jax /*g:fuf_taggedfile_prompt* +g:fuf_taggedfile_switchOrder fuf.jax /*g:fuf_taggedfile_switchOrder* +g:fuf_timeFormat fuf.jax /*g:fuf_timeFormat* +g:fuf_useMigemo fuf.jax /*g:fuf_useMigemo* diff --git a/fuzzyfinder/fd41e8d68b6774324b58c02692b896a6a36a0397.patch b/fuzzyfinder/fd41e8d68b6774324b58c02692b896a6a36a0397.patch new file mode 100644 index 0000000..7e85408 --- /dev/null +++ b/fuzzyfinder/fd41e8d68b6774324b58c02692b896a6a36a0397.patch @@ -0,0 +1,36 @@ +From fd41e8d68b6774324b58c02692b896a6a36a0397 Mon Sep 17 00:00:00 2001 +From: David Wolever +Date: Thu, 1 Jun 2017 10:11:43 -0400 +Subject: [PATCH] Fix focus top left window bug + +With newer versions of Vim, the top left window will be focused after +opening the fuf menu. This patch restores the window focus after the +menu is closed. +--- + autoload/fuf.vim | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/autoload/fuf.vim b/autoload/fuf.vim +index fe9e6eb..78be490 100644 +--- a/autoload/fuf.vim ++++ b/autoload/fuf.vim +@@ -706,6 +706,11 @@ let s:FUF_BUF_NAME = '[fuf]' + + " + function s:activateFufBuffer() ++ " Save the last window number so we can switch back to it later (otherwise, ++ " at least with more recent versions of Vim, we end up with the top left ++ " window focused) ++ let s:fuf_buffer_last_winnr = winnr() ++ + " lcd . : To avoid the strange behavior that unnamed buffer changes its cwd + " if 'autochdir' was set on. + lcd . +@@ -733,6 +738,7 @@ function s:deactivateFufBuffer() + AutoComplPopUnlock + endif + call l9#tempbuffer#close(s:FUF_BUF_NAME) ++ exec s:fuf_buffer_last_winnr . "wincmd w" + endfunction + + " }}}1 diff --git a/fuzzyfinder/plugin/fuf.vim b/fuzzyfinder/plugin/fuf.vim new file mode 100644 index 0000000..9826dab --- /dev/null +++ b/fuzzyfinder/plugin/fuf.vim @@ -0,0 +1,158 @@ +"============================================================================= +" Copyright (c) 2007-2010 Takeshi NISHIDA +" +" GetLatestVimScripts: 1984 1 :AutoInstall: FuzzyFinder +"============================================================================= +" LOAD GUARD {{{1 + +try + if !l9#guardScriptLoading(expand(':p'), 702, 101, []) + finish + endif +catch /E117/ + echoerr '***** L9 library must be installed! *****' + finish +endtry + +" }}}1 +"============================================================================= +" LOCAL FUNCTIONS {{{1 + +" +function s:initialize() + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_modesDisable' , [ 'mrufile', 'mrucmd', ]) + call l9#defineVariableDefault('g:fuf_keyOpen' , '') + call l9#defineVariableDefault('g:fuf_keyOpenSplit' , '') + call l9#defineVariableDefault('g:fuf_keyOpenVsplit' , '') + call l9#defineVariableDefault('g:fuf_keyOpenTabpage' , '') + call l9#defineVariableDefault('g:fuf_keyPreview' , '') + call l9#defineVariableDefault('g:fuf_keyNextMode' , '') + call l9#defineVariableDefault('g:fuf_keyPrevMode' , '') + call l9#defineVariableDefault('g:fuf_keyPrevPattern' , '') + call l9#defineVariableDefault('g:fuf_keyNextPattern' , '') + call l9#defineVariableDefault('g:fuf_keySwitchMatching', '') + call l9#defineVariableDefault('g:fuf_dataDir' , '~/.vim-fuf-data') + call l9#defineVariableDefault('g:fuf_abbrevMap' , {}) + call l9#defineVariableDefault('g:fuf_patternSeparator' , ';') + call l9#defineVariableDefault('g:fuf_promptHighlight' , 'Question') + call l9#defineVariableDefault('g:fuf_ignoreCase' , 1) + call l9#defineVariableDefault('g:fuf_splitPathMatching', 1) + call l9#defineVariableDefault('g:fuf_fuzzyRefining' , 0) + call l9#defineVariableDefault('g:fuf_smartBs' , 1) + call l9#defineVariableDefault('g:fuf_reuseWindow' , 1) + call l9#defineVariableDefault('g:fuf_timeFormat' , '(%Y-%m-%d %H:%M:%S)') + call l9#defineVariableDefault('g:fuf_learningLimit' , 100) + call l9#defineVariableDefault('g:fuf_enumeratingLimit' , 50) + call l9#defineVariableDefault('g:fuf_maxMenuWidth' , 78) + call l9#defineVariableDefault('g:fuf_previewHeight' , 0) + call l9#defineVariableDefault('g:fuf_autoPreview' , 0) + call l9#defineVariableDefault('g:fuf_useMigemo' , 0) + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_buffer_prompt' , '>Buffer[]>') + call l9#defineVariableDefault('g:fuf_buffer_switchOrder', 10) + call l9#defineVariableDefault('g:fuf_buffer_mruOrder' , 1) + call l9#defineVariableDefault('g:fuf_buffer_keyDelete' , '') + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_file_prompt' , '>File[]>') + call l9#defineVariableDefault('g:fuf_file_switchOrder', 20) + call l9#defineVariableDefault('g:fuf_file_exclude' , '\v\~$|\.(o|exe|dll|bak|orig|sw[po])$|(^|[/\\])\.(hg|git|bzr)($|[/\\])') + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_coveragefile_prompt' , '>CoverageFile[]>') + call l9#defineVariableDefault('g:fuf_coveragefile_switchOrder', 30) + call l9#defineVariableDefault('g:fuf_coveragefile_exclude' , '\v\~$|\.(o|exe|dll|bak|orig|sw[po])$|(^|[/\\])\.(hg|git|bzr)($|[/\\])') + call l9#defineVariableDefault('g:fuf_coveragefile_globPatterns', ['**/.*', '**/*']) + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_dir_prompt' , '>Dir[]>') + call l9#defineVariableDefault('g:fuf_dir_switchOrder', 40) + call l9#defineVariableDefault('g:fuf_dir_exclude' , '\v(^|[/\\])\.(hg|git|bzr)($|[/\\])') + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_mrufile_prompt' , '>MRU-File[]>') + call l9#defineVariableDefault('g:fuf_mrufile_switchOrder', 50) + call l9#defineVariableDefault('g:fuf_mrufile_exclude' , '\v\~$|\.(o|exe|dll|bak|orig|sw[po])$|^(\/\/|\\\\|\/mnt\/|\/media\/)') + call l9#defineVariableDefault('g:fuf_mrufile_maxItem' , 200) + call l9#defineVariableDefault('g:fuf_mrufile_maxItemDir' , 50) + call l9#defineVariableDefault('g:fuf_mrufile_keyExpand' , '') + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_mrucmd_prompt' , '>MRU-Cmd[]>') + call l9#defineVariableDefault('g:fuf_mrucmd_switchOrder', 60) + call l9#defineVariableDefault('g:fuf_mrucmd_exclude' , '^$') + call l9#defineVariableDefault('g:fuf_mrucmd_maxItem' , 200) + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_bookmarkfile_prompt' , '>Bookmark-File[]>') + call l9#defineVariableDefault('g:fuf_bookmarkfile_switchOrder', 70) + call l9#defineVariableDefault('g:fuf_bookmarkfile_searchRange', 400) + call l9#defineVariableDefault('g:fuf_bookmarkfile_keyDelete' , '') + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_bookmarkdir_prompt' , '>Bookmark-Dir[]>') + call l9#defineVariableDefault('g:fuf_bookmarkdir_switchOrder', 80) + call l9#defineVariableDefault('g:fuf_bookmarkdir_keyDelete' , '') + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_tag_prompt' , '>Tag[]>') + call l9#defineVariableDefault('g:fuf_tag_switchOrder', 90) + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_buffertag_prompt' , '>Buffer-Tag[]>') + call l9#defineVariableDefault('g:fuf_buffertag_switchOrder', 100) + call l9#defineVariableDefault('g:fuf_buffertag_ctagsPath' , 'ctags') + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_taggedfile_prompt' , '>Tagged-File[]>') + call l9#defineVariableDefault('g:fuf_taggedfile_switchOrder', 110) + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_jumplist_prompt' , '>Jump-List[]>') + call l9#defineVariableDefault('g:fuf_jumplist_switchOrder', 120) + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_changelist_prompt' , '>Change-List[]>') + call l9#defineVariableDefault('g:fuf_changelist_switchOrder', 130) + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_quickfix_prompt' , '>Quickfix[]>') + call l9#defineVariableDefault('g:fuf_quickfix_switchOrder', 140) + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_line_prompt' , '>Line[]>') + call l9#defineVariableDefault('g:fuf_line_switchOrder', 150) + "--------------------------------------------------------------------------- + call l9#defineVariableDefault('g:fuf_help_prompt' , '>Help[]>') + call l9#defineVariableDefault('g:fuf_help_switchOrder', 160) + "--------------------------------------------------------------------------- + command! -bang -narg=0 FufEditDataFile call fuf#editDataFile() + command! -bang -narg=0 FufRenewCache call s:renewCachesOfAllModes() + "--------------------------------------------------------------------------- + call fuf#addMode('buffer') + call fuf#addMode('file') + call fuf#addMode('coveragefile') + call fuf#addMode('dir') + call fuf#addMode('mrufile') + call fuf#addMode('mrucmd') + call fuf#addMode('bookmarkfile') + call fuf#addMode('bookmarkdir') + call fuf#addMode('tag') + call fuf#addMode('buffertag') + call fuf#addMode('taggedfile') + call fuf#addMode('jumplist') + call fuf#addMode('changelist') + call fuf#addMode('quickfix') + call fuf#addMode('line') + call fuf#addMode('help') + call fuf#addMode('givenfile') + call fuf#addMode('givendir') + call fuf#addMode('givencmd') + call fuf#addMode('callbackfile') + call fuf#addMode('callbackitem') + "--------------------------------------------------------------------------- +endfunction + +" +function s:renewCachesOfAllModes() + for m in fuf#getModeNames() + call fuf#{m}#renewCache() + endfor +endfunction + +" }}}1 +"============================================================================= +" INITIALIZATION {{{1 + +call s:initialize() + +" }}}1 +"============================================================================= +" vim: set fdm=marker: diff --git a/log/syntax/log.vim b/log/syntax/log.vim new file mode 100755 index 0000000..d2ae3e8 --- /dev/null +++ b/log/syntax/log.vim @@ -0,0 +1,73 @@ +" Vim syntax file +" Language: build logs +" Maintainer: Julian Ospald +" Latest Revision: 30 May 2012 + +if exists("b:current_syntax") + finish +endif + +" flags +syn match CFLAGS " -g" +syn match CFLAGS " -[a-zA-Z][a-zA-Z0-9_\-\,\=\.\/]\+" +syn match CPPFLAGS " -D[a-zA-Z0-9_\-\,\=\.\/\"]\+" +syn match LINK " -l[a-zA-Z0-9_\-\,\=\.\/]\+" +syn match LDFLAGS " -L[a-zA-Z0-9_\-\,\=\.\/]\+" +syn match LDFLAGS " -Wl,[a-zA-Z0-9_\-\,\=\.\/]\+" +syn match LDFLAGS " -shared" +syn match LDFLAGS " -static" +syn match LDFLAGS " -static[a-zA-Z0-9_\-\,\=\.\/]\+" +syn match LDFLAGS " -rdynamic" +syn match INCS " -I[a-zA-Z0-9_\-\,\=\.\/]\+" + +" files +syn match SOURCE " [a-zA-Z0-9_\-\,\=\.\/]\+\.c" +syn match SOURCE " [a-zA-Z0-9_\-\,\=\.\/]\+\.cc" +syn match SOURCE " [a-zA-Z0-9_\-\,\=\.\/]\+\.cxx" +syn match SOURCE " [a-zA-Z0-9_\-\,\=\.\/]\+\.cpp" +syn match SOURCE " [a-zA-Z0-9_\-\,\=\.\/]\+\.h" +syn match SOURCE " [a-zA-Z0-9_\-\,\=\.\/]\+\.hpp" +syn match OBJECTS " [a-zA-Z0-9_\-\,\=\.\/]\+\.o" +syn match LIBS " [a-zA-Z0-9_\,\=\.\/]\+\.a" +syn match LIBS " [a-zA-Z0-9_\,\=\.\/]\+\.so" +syn match LIBS " [a-zA-Z0-9_\,\=\.\/]\+\.so[\.0-9]\+" +syn match TARGETS " -o [a-zA-Z0-9_\-\,\=\.\/]\+" + +" messages +syn match cMLogMissing "[\./a-zA-Z0-9_]\+\.[a-zA-Z_]\+: No such .*$" +syn match cMLogMissing "[\./a-zA-Z0-9_]\+\.[a-zA-Z_]\+: Datei oder Verzeichnis nicht gefunden" +syn match cMLogMissing "undefined reference to .*$" +syn match cMLogMissing "Keine Regel vorhanden.*$" +syn match cMLogCurDir "Entering directory .*$" +" syn match cMLogCurDir "cd [a-zA-Z0-9_\-\,\=\.\/]\+" +syn match cMLogWarn "\<[wW]arn[iu]ng.*$" +syn match cMLogErr "[Ee]rror.*$" +syn match cMLogErr "No such .*$" + +" disrespected toolchain +syn match toolchain "\V\C\<\(-\)\@" +syn match toolchain "\V\C\<\(-\)\@" +syn match toolchain "\V\C\<\(-\)\@" +syn match toolchain "\V\C\<\(-\)\@" +syn match toolchain "\V\C\<\(-\)\@\+++" +syn match toolchain "\V\C\<\(-\)\@\+++" + +hi cMLogWarn guifg=Red +hi cMLogErr guifg=Red term=underline cterm=underline gui=underline +hi cMLogCurDir guifg=Blue +hi cMLogMissing guifg=Red + +hi toolchain guifg=Red + +hi CFLAGS guifg=Green +hi CPPFLAGS guifg=DarkGreen +hi LINK guifg=Yellow +hi LDFLAGS guifg=Orange +hi INCS guifg=DarkViolet +hi TARGETS guifg=Brown +hi LIBS guifg=Brown +hi OBJECTS guifg=Black +hi SOURCE guifg=Grey + +let b:current_syntax = "log" + diff --git a/paredit/doc/paredit.txt b/paredit/doc/paredit.txt new file mode 100644 index 0000000..b740a11 --- /dev/null +++ b/paredit/doc/paredit.txt @@ -0,0 +1,461 @@ +*paredit.txt* Paredit Last Change: 13 Dec 2016 + +Paredit Mode for Vim *paredit* *slimv-paredit* + Version 0.9.13 + +The paredit.vim plugin performs structured editing of s-expressions used in +the Lisp, Clojure, Scheme programming languages. It may come as part of Slimv +but it is also distributed separately as a standalone plugin. + +|paredit-mode| Paredit mode +|paredit-keys| Paredit keybindings +|paredit-options| Paredit options + +=============================================================================== +PAREDIT MODE *paredit-mode* + +Paredit mode is a special editing mode that keeps all matched characters +(parentheses, square and curly braces, double quotes) balanced, i.e. all opening +characters have a matching closing character. Most text entering and erasing +commands try to maintain the balanced state, so no single matched character is +added or deleted, they are entered or removed in pairs. +The function takes care of strings and comments, so no parenthesis and square +bracket balancing is performed inside a string or comment. +Please note that [] and {} pairs are not balanced for Lisp filetypes, only +for Clojure and Scheme. + +The idea is taken from the paredit mode of Emacs, but not all paredit.el +editing functions are implemented or behave exactly the same way as they do +in Emacs. + +When you enter a '(' then a matching ')' is automatically inserted. +If needed, spaces before and/or after the '()' pair are added. + +When you press ')' in insert mode then there's no need to insert a closing +parenthesis mark (it is already there), so the cursor is simply advanced past +the next closing parenthesis (then the next outer closing parenthesis, etc.). +The result of this is however that when entering text with paredit mode +you can use the same keystrokes as without paredit mode and you get the same +result. Of course you can choose to not enter the closing parenthesis (as +required without paredit mode), because it is already there. + +When you are trying to delete a ')' alone then it is not possible, the cursor +is simply moved inside the list, where all regular characters can be deleted. +When the list is finally empty: '()', then the deletion of the opening '(' +makes both parentheses erased at once, so the balanced state is maintained. + +All the above holds for [...] and "..." character pairs. + +When you are deleting multiple characters at once, e.g. deleting a whole line, +or deleting till the end of the line, etc, then the deletion logic of a single +character is iterated. This means that the whole line or the characters till +the end of the line, etc are not necessarily deleted all. Depending on the +number of open/close parentheses, square or curly braces, double quotes some +of them might be kept in order to maintain the balanced state. +For example if you press D in Normal mode to delete till the end of line +between the a and b parameters of the following Clojure function definition: + +(defn myfunc [a b c] (+ a b c)) + ^--- press D here + +then the closing ] as well as the last closing ) will not be deleted, because +in the list you have an ( and a [ to be matched, so the result will be: + +(defn myfunc [a]) + +If you are deleting multiple lines, then the above process is performed for +all lines involved. If a line was not completely cleared, then it is joined +with the next line and the process continues. + + +Of course not all Vim commands are compatible with the paredit mode (e.g. +you can yank and paste unbalanced code snippet, or comment out an asymmetrical +part of the code), and there is also the possibility to edit the source code +with paredit mode switched off or with another editor to make it unbalanced. +When paredit mode detects that the underlying code is not balanced, then the +paredit functionality is suspended until the top level form balance is fixed. +As soon as all parens are matched, the paredit mode is automatically resumed. +Paredit needs "syntax on" to identify the syntax elements of the underlying +code, so if syntax is switched off, then paredit will not be suspended inside +comments or strings. + + +Slurpage and Barfage known from Emacs is also possible but in a different +fashion: you don't move the symbols but move the opening or closing parenthesis +over the symbol or a sub-list. This way you can move any symbol or sub-list +into or out of the current list. It is not possible to move the parenthesis +over its pair, so for example if you move the opening parenthesis to the right, +then it will stop at the matched closing parenthesis. + + +Paredit mode is set by default for .lisp, .cl, .clj, cljs, .scm and .rkt files, +but it is possible to switch it off by putting the following statement in the +.vimrc file: + + let g:paredit_mode = 0 + +You can enable paredit mode for other file types as well. Here is how to set +it for Arc files in your .vimrc (assuming you have a filetype 'arc' defined): + + au FileType arc call PareditInitBuffer() + +Paredit is part of Slimv, but it is also distributed separately as a standalone +plugin. If you indend to use the SWANK client and/or Slimv's indentation and +syntax functions, then please install the Slimv plugin. Otherwise you may want +to install the Paredit plugin thus omitting other unnecessary files. + + +=============================================================================== +PAREDIT KEYBINDINGS *paredit-keys* + +Here follows a list of paredit keybindings: + + +Insert Mode: + + ( Inserts '()' and moves the cursor inside. Also adds leading + or trailing spaces when needed. + Inserts '(' when inside comment or string. + + ) Moves the cursor to the next closing parenthesis mark of + the current list. When pressed again then moves to the next + outer closing parenthesis, etc, until the closing of the + top level form is reached. + Inserts ')' when inside comment or string. + If |g:paredit_electric_return| is on then it also re-gathers + electric returns when appropriate. + + [ Inserts '[]' and moves the cursor inside. Also adds leading + or trailing spaces when needed. + Inserts '[' when inside comment or string. + + ] Moves the cursor to the next closing square bracket of the + current list. When pressed again then moves to the next + outer closing square bracket, etc, until the closing of the + top level form is reached. + Inserts ']' when inside comment or string. + If |g:paredit_electric_return| is on then it also re-gathers + electric returns when appropriate. + + { Inserts '{}' and moves the cursor inside. Also adds leading + or trailing spaces when needed. + Inserts '{' when inside comment or string. + + } Moves the cursor to the next closing curly brace of the + current list. When pressed again then moves to the next + outer closing curly brace, etc, until the closing of the + top level form is reached. + Inserts '}' when inside comment or string. + If |g:paredit_electric_return| is on then it also re-gathers + electric returns when appropriate. + + " When outside of string, inserts '""' and moves the cursor + inside. When inside string then moves to the closing '"'. + Inserts '"' when inside comment. Also insert '"' when inside + string and preceded by a '\'. + + When about to delete a (, ), [, ], or " and there are other + characters inside, then just skip it to the left. When + about to delete the opening part of the matched character + with nothing inside, then the whole empty list is removed. + + When about to delete a (, ), [, ], or " and there are other + characters inside, then just skip it to the right. When + about to delete the closing part of the matched character + with nothing inside, then the whole empty list is removed. + + If |g:paredit_electric_return| is on then insert an + "electric return", i.e. create an empty line by inserting + two newline characters. + + +Normal Mode: + + ( Finds opening '(' of the current list. Can be pressed + repeatedly until the opening of the top level form reached. + + ) Finds closing ')' of the current list. Can be pressed + repeatedly until the closing of the top level form reached. + + [[ Go to the start of current/previous defun. + + ]] Go to the start of next defun. + + < If standing on a delimiter (parenthesis or square bracket) + then moves it to the left by slurping or barfing the + s-expression to the left, depending on the direction of the + delimiter: + Pressing '<' when standing on a ')' makes the s-expression + to the left of the ')' going out of the current list. + Pressing '<' when standing on a '(' makes the s-expression + to the left of the '(' coming into the current list. + For example pressing < at position marked with |: + (aaa bbb|) ---> (aaa|) bbb + aaa |(bbb) ---> |(aaa bbb) + + > If standing on a delimiter (parenthesis or square bracket) + then moves it to the right by slurping or barfing the + s-expression to the right, depending on the direction of the + delimiter: + Pressing '>' when standing on a '(' makes the s-expression + to the right of the '(' going out of the current list. + Pressing '>' when standing on a ')' makes the s-expression + to the right of the ')' coming into the current list. + For example pressing < at position marked with |: + (aaa|) bbb ---> (aaa bbb|) + |(aaa bbb) ---> aaa |(bbb) + + J Join two subsequent lists or strings. The first one must end + before the cursor, the second one must start after the + cursor position. + For example pressing J at position marked with |: + (aaa)| (bbb) ---> (aaa |bbb) + "aaa"| "bbb" ---> "aaa |bbb" + + O Split ("Open") current list or string at the cursor position. + Opposite of Join. Key O is selected because for the original + Vim mapping J and O are also kind of opposites. + For example pressing O at position marked with |: + (aaa |bbb) ---> (aaa) |(bbb) + "aaa|bbb" ---> "aaa" |"bbb" + + W Wrap the current symbol in a pair of parentheses. The cursor + w( is then positioned on the opening parenthesis, as wrapping + is usually done because one wants to call a function with + the symbol as parameter, so by pressing "a" one can enter + the function name right after the newly inserted "(". + For example pressing W at position marked with |: + (aaa b|bb ccc) ---> (aaa |(bbb) ccc) + + w[ Wrap the current symbol in a pair of square brackets, + similarly to W. + For example pressing w[ at position marked with |: + (aaa b|bb ccc) ---> (aaa |[bbb] ccc) + + w{ Wrap the current symbol in a pair of curly braces, + similarly to W. + For example pressing w{ at position marked with |: + (aaa b|bb ccc) ---> (aaa |{bbb} ccc) + + w" Wrap the current symbol in a pair of double quotes, + similarly to W. + For example pressing w" at position marked with |: + (aaa b|bb ccc) ---> (aaa "bbb|" ccc) + + S Splice the current list into the containing list, i.e. + remove the opening and closing parens. Opposite of wrap. + For example pressing S at position marked with |: + (aaa (b|bb ccc) ddd) ---> (aaa |bbb ccc ddd) + + Splice the current list into the containing list by deleting + everything backward from the cursor position up to the + opening paren. + For example pressing at position marked with |: + (aaa (bbb |ccc) ddd) ---> (aaa |ccc ddd) + + Splice the current list into the containing list by deleting + everything forward from the cursor position up to the + closing paren. + For example pressing at position marked with |: + (aaa (bbb| ccc) ddd) ---> (aaa |bbb ddd) + + I Raise the current symbol, i.e. replace the current list with + the current symbol by deleting everything else (except the + symbol) in the list, including the enclosing pair of parens. + For example pressing I at position marked with |: + (aaa (b|bb ccc) ddd) ---> (aaa |bbb ddd) + + x or When about to delete a (, ), [, ], or " and there are other + characters inside, then just skip it to the right. When + about to delete the closing part of the matched character + with nothing inside, then the whole empty list is removed. + When preceded by a value then delete this many + characters. + + X When about to delete a (, ), [, ], or " and there are other + characters inside, then just skip it to the left. When + about to delete the opening part of the matched character + with nothing inside, then the whole empty list is removed. + + D Keep deleting characters towards the end of line, + maintaining the balanced state, i.e. keep the number of + opening and closing parens the same. + + C Same as 'D' but go to insert mode at the end. + + s Same as 'x' but go to insert mode at the end. + + dd Delete whole line by keeping the balanced state, i.e. + keep the number of opening and closing parens the same. + When preceded by a value then delete this many + lines. + + cc Same as 'dd' but go to insert mode at the end. + + d{motion} Delete text till {motion}. Keeps text balanced, so if the + surrounded text contains unpaired matched characters then + they are not removed. + + c{motion} Delete text till {motion} and start insert mode. Keeps text + balanced just like d{motion}. + + p Put the text after the cursor with all unbalanced matched + characters removed. + + P Put the text before the cursor with all unbalanced matched + characters removed. + + +Visual Mode: + + ( Finds opening '(' of the current list and selects the whole + list. Can be pressed repeatedly until the top level form + selected. + + ) Finds closing ')' of the current list and selects the whole + list. Can be pressed repeatedly until the top level form + selected. + + d Delete the current visual selection. Keeps text balanced, + x so the the selection contains unpaired matched characters + then they are not removed. + + c Delete the current visual selection and start insert mode. + Keeps text balanced just like the 'd' command. + + W Wrap the current visual selection in a pair of parentheses. + w( The visual selection is kept. + + w[ Wrap the current visual selection in a pair of square + brackets. The visual selection is kept. + + w{ Wrap the current visual selection in a pair of curly braces. + The visual selection is kept. + + w" Wrap the current visual selection in a pair of double + quotes. The visual selection is kept. + + +Please note that if variable |g:paredit_shortmaps| is nonzero then the +following normal mode mappings don't get a prefix, they are mapped +to existing (but infrequently used) Vim functions and instead the original Vim +functions are mapped with the prefix: + + <, >, J, O, W, S + + +Vim has many built-in mappings for manipulating s-expressions. Here follows a +list of useful commands, these are not defined by paredit.vim, they are +available even when paredit mode is switched off. + + % Find the matching pair of the parenthesis the cursor is + standing on. + + d% Delete till the matching parenthesis. Normally it is used + when the cursor is standing on a parenthesis (works with + square or curly braces as well). If not standing on a + parenthesis then deletes left till the first opening paren, + so this command may also be used to delete an s-expression + that is right before the cursor. + + daw Delete a word. Can be used to delete a list element, the + cursor may be placed anywhere in the element. + + da( Delete the innermost s-expression. The cursor may be placed + anywhere inside the s-expression. + + di( Same as da( but does not delete the enclosing parens. + + +Davide Taviani made a cheetsheet for Paredit, which can be accessed here: +https://github.com/StudyFlow/paredit.vim-cheatsheet + +=============================================================================== +PAREDIT OPTIONS *paredit-options* + +|g:paredit_disable_clojure| If defined, paredit is disabled for clojure files. + +|g:paredit_disable_hy| If defined, paredit is disabled for hy files. + +|g:paredit_disable_lisp| If defined, paredit is disabled for lisp files. + +|g:paredit_disable_scheme| If defined, paredit is disabled for scheme files. + +|g:paredit_disable_shen| If defined, paredit is disabled for shen files. + +|g:paredit_electric_return| If nonzero, electric return feature is enabled. + +|g:paredit_smartjump| If nonzero, '(' and ')' also target square brackets + and curly braces when editing Clojure or Scheme. + +|g:paredit_leader| Custom setting for Paredit. + +|g:paredit_matchlines| Number of lines to look backward and forward + when checking if the current form is balanced. + +|g:paredit_mode| If nonzero, paredit mode is switched on. + +|g:paredit_shortmaps| If nonzero, paredit is remapping some one-letter + Vim commands that are not frequently used. + + + *g:paredit_disable_clojure* + *g:paredit_disable_lisp* + *g:paredit_disable_scheme* + *g:paredit_disable_shen* +If defined then paredit is disabled for the given file type. Useful to use +a different plugin for a specific file type, but keep using paredit for the +others. + + *g:paredit_electric_return* +If nonzero then "electric return" feature is enabled. This means that when an + is pressed before a closing paren in insert mode, paredit will actually +insert two newlines creating an empty line. The extra newline is consumed at +pressing the next closing paren. This feature allows linewise editing of the +subform entered in the next (empty) line. +In other words "opens" parenthetical expressions while editing, ')' +"closes" them. +Please note that electric return is disabled for the REPL buffer if Slimv +option |g:slimv_repl_simple_eval| is nonzero. In this case is used +to send the command line to the swank server for evaluation. + +Please find a video demonstration of the electric return feature here: +http://img8.imageshack.us/img8/9479/openparen.gif + + *g:paredit_smartjump* +If nonzero, this option changes the behavior of '(' and ')' in normal and visual +modes when editing Clojure or Scheme. Rather than jumping to nearest open or close +parenthesis, instead the cursor will jump to the nearest '(', '[', or '{' if +you press '(', and it will jump to the nearest ')', ']', or '}' if you press +')'. This option makes it much easier to navigate nested Clojure data +structures. It does nothing if the filetype is not clojure or Scheme. + + *g:paredit_leader* +This option allows a custom setting for the Paredit keybindings. +By default it has the same value as |mapleader|. If neither g:paredit_leader +nor mapleader are defined then the default is "," in Paredit. +Example: + let g:paredit_leader = '\' +If this is set in the .vimrc then Wrap will be mapped to \W instead of ,W. + +There is a separate |g:slimv_leader| option for the general Slimv keybindings. + + *g:paredit_matchlines* +Number of lines to look backward and forward when checking if the current +top level form is balanced in paredit mode. Default is 100. + + *g:paredit_mode* +If nonzero then paredit mode is switched on, i.e. the plugin tries to keep the +balanced state of parens. This is the default behaviour. + + *g:paredit_shortmaps* +If nonzero, paredit is remapping some one-letter normal mode Vim commands that +are not frequently used. These are <, >, J, O, W, S. The original function of +these maps then can be reached via (which is the "," character +by default in Paredit). +Otherwise these paredit functions can be reached via maintaining the +original functions of these keys. + + +=============================================================================== +vim:tw=80:et:wrap:ft=help:norl: diff --git a/paredit/plugin/paredit.vim b/paredit/plugin/paredit.vim new file mode 100644 index 0000000..befc118 --- /dev/null +++ b/paredit/plugin/paredit.vim @@ -0,0 +1,1863 @@ +" paredit.vim: +" Paredit mode for Slimv +" Version: 0.9.13 +" Last Change: 15 Jan 2017 +" Maintainer: Tamas Kovacs +" License: This file is placed in the public domain. +" No warranty, express or implied. +" *** *** Use At-Your-Own-Risk! *** *** +" +" ===================================================================== +" +" Load Once: +if &cp || exists( 'g:paredit_loaded' ) + finish +endif + +let g:paredit_loaded = 1 + +" Needed to load filetype and indent plugins +filetype plugin on +filetype indent on + +" ===================================================================== +" Global variable definitions +" ===================================================================== + +" Paredit mode selector +if !exists( 'g:paredit_mode' ) + let g:paredit_mode = 1 +endif + +" Match delimiter this number of lines before and after cursor position +if !exists( 'g:paredit_matchlines' ) + let g:paredit_matchlines = 100 +endif + +" Use short keymaps, i.e. J instead of J +if !exists( 'g:paredit_shortmaps' ) + let g:paredit_shortmaps = 0 +endif + +" Use smart jumping to the nearest paren, curly brace, or square bracket in +" clojure +if !exists( 'g:paredit_smartjump' ) + let g:paredit_smartjump = 0 +endif + +" Custom for the Paredit plugin +if !exists( 'g:paredit_leader' ) + if exists( 'mapleader' ) + let g:paredit_leader = '' + else + let g:paredit_leader = ',' + endif +endif + +" Use 'Electric Return', i.e. add double newlines if enter pressed before a closing paren +if !exists( 'g:paredit_electric_return' ) + let g:paredit_electric_return = 1 +endif + +" ===================================================================== +" Other variable definitions +" ===================================================================== + +" Valid macro prefix characters +let s:any_macro_prefix = "'" . '\|`\|#\|@\|\~\|,\|\^' + +" Repeat count for some remapped edit functions (like 'd') +let s:repeat = 0 + +let s:yank_pos = [] + +" Filetypes with [] and {} pairs balanced as well +let s:fts_balancing_all_brackets = '.*\(clojure\|hy\|scheme\|racket\|shen\).*' + +" ===================================================================== +" General utility functions +" ===================================================================== +" Buffer specific initialization +function! PareditInitBuffer() + let b:paredit_init = 1 + " in case they are accidentally removed + " Also define regular expressions to identify special characters used by paredit + if &ft =~ s:fts_balancing_all_brackets + let b:any_matched_char = '(\|)\|\[\|\]\|{\|}\|\"' + let b:any_matched_pair = '()\|\[\]\|{}\|\"\"' + let b:any_opening_char = '(\|\[\|{' + let b:any_closing_char = ')\|\]\|}' + let b:any_openclose_char = '(\|)\|\[\|\]\|{\|}' + let b:any_wsopen_char = '\s\|(\|\[\|{' + let b:any_wsclose_char = '\s\|)\|\]\|}' + else + let b:any_matched_char = '(\|)\|\"' + let b:any_matched_pair = '()\|\"\"' + let b:any_opening_char = '(' + let b:any_closing_char = ')' + let b:any_openclose_char = '(\|)' + let b:any_wsopen_char = '\s\|(' + let b:any_wsclose_char = '\s\|)' + endif + + if g:paredit_mode + " Paredit mode is on: add buffer specific keybindings + inoremap ( PareditInsertOpening('(',')') + inoremap ) =PareditInsertClosing('(',')') + inoremap " PareditInsertQuotes() + inoremap PareditBackspace(0) + inoremap PareditBackspace(0) + inoremap PareditDel() + if &ft =~ s:fts_balancing_all_brackets && g:paredit_smartjump + noremap ( :call PareditSmartJumpOpening(0) + noremap ) :call PareditSmartJumpClosing(0) + vnoremap ( :call PareditSmartJumpOpening(1) + vnoremap ) :call PareditSmartJumpClosing(1) + else + noremap ( :call PareditFindOpening('(',')',0) + noremap ) :call PareditFindClosing('(',')',0) + vnoremap ( :call PareditFindOpening('(',')',1) + vnoremap ) :call PareditFindClosing('(',')',1) + endif + noremap [[ :call PareditFindDefunBck() + noremap ]] :call PareditFindDefunFwd() + + call RepeatableNNoRemap('x', ':call PareditEraseFwd()') + nnoremap :call PareditEraseFwd() + call RepeatableNNoRemap('X', ':call PareditEraseBck()') + nnoremap s :call PareditEraseFwd()i + call RepeatableNNoRemap('D', 'v$:call PareditDelete(visualmode(),1)') + nnoremap C v$:call PareditChange(visualmode(),1) + nnoremap d :call PareditSetDelete(v:count)g@ + vnoremap d :call PareditDelete(visualmode(),1) + vnoremap x :call PareditDelete(visualmode(),1) + vnoremap :call PareditDelete(visualmode(),1) + nnoremap c :set opfunc=PareditChangeg@ + vnoremap c :call PareditChange(visualmode(),1) + call RepeatableNNoRemap('dd', ':call PareditDeleteLines()') + nnoremap cc :call PareditChangeLines() + nnoremap cw :call PareditChangeSpec('cw',1) + nnoremap cW :set opfunc=PareditChangeg@E + nnoremap cb :call PareditChangeSpec('cb',0) + nnoremap ciw :call PareditChangeSpec('ciw',1) + nnoremap caw :call PareditChangeSpec('caw',1) + call RepeatableNNoRemap('p', ':call PareditPut("p")') + call RepeatableNNoRemap('P', ':call PareditPut("P")') + call RepeatableNNoRemap(g:paredit_leader . 'w(', ':call PareditWrap("(",")")') + execute 'vnoremap ' . g:paredit_leader.'w( :call PareditWrapSelection("(",")")' + call RepeatableNNoRemap(g:paredit_leader . 'w"', ':call PareditWrap('."'".'"'."','".'"'."')") + execute 'vnoremap ' . g:paredit_leader.'w" :call PareditWrapSelection('."'".'"'."','".'"'."')" + " Spliec s-expression killing backward/forward + execute 'nmap ' . g:paredit_leader.' d[(:call PareditSplice()' + execute 'nmap ' . g:paredit_leader.' d])%:call PareditSplice()' + call RepeatableNNoRemap(g:paredit_leader . 'I', ':call PareditRaise()') + if &ft =~ s:fts_balancing_all_brackets + inoremap [ PareditInsertOpening('[',']') + inoremap ] =PareditInsertClosing('[',']') + inoremap { PareditInsertOpening('{','}') + inoremap } =PareditInsertClosing('{','}') + call RepeatableNNoRemap(g:paredit_leader . 'w[', ':call PareditWrap("[","]")') + execute 'vnoremap ' . g:paredit_leader.'w[ :call PareditWrapSelection("[","]")' + call RepeatableNNoRemap(g:paredit_leader . 'w{', ':call PareditWrap("{","}")') + execute 'vnoremap ' . g:paredit_leader.'w{ :call PareditWrapSelection("{","}")' + endif + + if g:paredit_shortmaps + " Shorter keymaps: old functionality of KEY is remapped to KEY + call RepeatableNNoRemap('<', ':call PareditMoveLeft()') + call RepeatableNNoRemap('>', ':call PareditMoveRight()') + call RepeatableNNoRemap('O', ':call PareditSplit()') + call RepeatableNNoRemap('J', ':call PareditJoin()') + call RepeatableNNoRemap('W', ':call PareditWrap("(",")")') + vnoremap W :call PareditWrapSelection('(',')') + call RepeatableNNoRemap('S', ':call PareditSplice()') + execute 'nnoremap ' . g:paredit_leader.'< :normal! <' + execute 'nnoremap ' . g:paredit_leader.'> :normal! >' + execute 'nnoremap ' . g:paredit_leader.'O :normal! O' + execute 'nnoremap ' . g:paredit_leader.'J :normal! J' + execute 'nnoremap ' . g:paredit_leader.'W :normal! W' + execute 'vnoremap ' . g:paredit_leader.'W :normal! W' + execute 'nnoremap ' . g:paredit_leader.'S :normal! S' + else + " Longer keymaps with prefix + nnoremap S V:call PareditChange(visualmode(),1) + call RepeatableNNoRemap(g:paredit_leader . '<', ':call PareditMoveLeft()') + call RepeatableNNoRemap(g:paredit_leader . '>', ':call PareditMoveRight()') + call RepeatableNNoRemap(g:paredit_leader . 'O', ':call PareditSplit()') + call RepeatableNNoRemap(g:paredit_leader . 'J', ':call PareditJoin()') + call RepeatableNNoRemap(g:paredit_leader . 'W', ':call PareditWrap("(",")")') + execute 'vnoremap ' . g:paredit_leader.'W :call PareditWrapSelection("(",")")' + call RepeatableNNoRemap(g:paredit_leader . 'S', ':call PareditSplice()') + endif + + if g:paredit_electric_return && mapcheck( "", "i" ) == "" + " Do not override any possible mapping for + inoremap PareditEnter() + endif + else + " Paredit mode is off: remove keybindings + silent! iunmap ( + silent! iunmap ) + silent! iunmap " + silent! iunmap + silent! iunmap + silent! iunmap + silent! unmap ( + silent! unmap ) + silent! unmap [[ + silent! unmap ]] + silent! unmap x + silent! unmap + silent! unmap X + silent! unmap s + silent! unmap D + silent! unmap C + silent! unmap d + silent! unmap c + silent! unmap dd + silent! unmap cc + silent! unmap cw + silent! unmap cW + silent! unmap cb + silent! unmap ciw + silent! unmap caw + if &ft =~ s:fts_balancing_all_brackets + silent! iunmap [ + silent! iunmap ] + silent! iunmap { + silent! iunmap } + endif + if mapcheck( "", "i" ) == "PareditEnter()" + " Remove only if we have added this mapping + silent! iunmap + endif + endif +endfunction + +" Run the command normally but append a call to repeat#set afterwards +function! RepeatableMap(map_type, keys, command) + let escaped_keys = substitute(a:keys, '["<]', '\\\0', "g") + execute a:map_type . ' ' . + \ a:keys . ' ' . a:command . + \ '\|silent! call repeat#set("' . escaped_keys . '")' +endfunction + +function! RepeatableNMap(keys, command) + call RepeatableMap('nmap', a:keys, a:command) +endfunction + +function! RepeatableNNoRemap(keys, command) + call RepeatableMap('nnoremap', a:keys, a:command) +endfunction + +" Include all prefix and special characters in 'iskeyword' +function! s:SetKeyword() + let old_value = &iskeyword + if &ft =~ s:fts_balancing_all_brackets + setlocal iskeyword+=+,-,*,/,%,<,=,>,:,$,?,!,@-@,94,~,#,\|,& + else + setlocal iskeyword+=+,-,*,/,%,<,=,>,:,$,?,!,@-@,94,~,#,\|,&,.,{,},[,] + endif + return old_value +endfunction + +" General Paredit operator function +function! PareditOpfunc( func, type, visualmode ) + let sel_save = &selection + let ve_save = &virtualedit + set virtualedit=all + let regname = v:register + let save_0 = getreg( '0' ) + + if a:visualmode " Invoked from Visual mode, use '< and '> marks. + silent exe "normal! `<" . a:type . "`>" + elseif a:type == 'line' + let &selection = "inclusive" + silent exe "normal! '[V']" + elseif a:type == 'block' + let &selection = "inclusive" + silent exe "normal! `[\`]" + else + let &selection = "inclusive" + silent exe "normal! `[v`]" + endif + + if !g:paredit_mode || (a:visualmode && (a:type == 'block' || a:type == "\")) + " Block mode is too difficult to handle at the moment + silent exe "normal! d" + let putreg = getreg( '"' ) + else + silent exe "normal! y" + let putreg = getreg( '"' ) + if a:func == 'd' + " Register "0 is corrupted by the above 'y' command + call setreg( '0', save_0 ) + elseif a:visualmode && &selection == "inclusive" && len(getline("'>")) < col("'>") && len(putreg) > 0 + " Remove extra space added at the end of line when selection=inclusive, all, or onemore + let putreg = putreg[:-2] + endif + + " Find and keep unbalanced matched characters in the region + let instring = s:InsideString( line("'<"), col("'<") ) + if col("'>") > 1 && !s:InsideString( line("'<"), col("'<") - 1 ) + " We are at the beginning of the string + let instring = 0 + endif + let matched = s:GetMatchedChars( putreg, instring, s:InsideComment( line("'<"), col("'<") ) ) + let matched = s:Unbalanced( matched ) + let matched = substitute( matched, '\s', '', 'g' ) + + if matched == '' + if a:func == 'c' && (a:type == 'v' || a:type == 'V' || a:type == 'char') + silent exe "normal! gvc" + else + silent exe "normal! gvd" + endif + else + silent exe "normal! gvc" . matched + silent exe "normal! l" + let offs = len(matched) + if matched[0] =~ b:any_closing_char + let offs = offs + 1 + endif + if a:func == 'd' + let offs = offs - 1 + elseif instring && matched == '"' + " Keep cursor inside the double quotes + let offs = offs + 1 + endif + if offs > 0 + silent exe "normal! " . string(offs) . "h" + endif + endif + endif + + let &selection = sel_save + let &virtualedit = ve_save + if a:func == 'd' && regname == '"' + " Do not currupt the '"' register and hence the "0 register + call setreg( '1', putreg ) + else + call setreg( regname, putreg ) + endif +endfunction + +" Set delete mode also saving repeat count +function! PareditSetDelete( count ) + let s:repeat = a:count + set opfunc=PareditDelete +endfunction + +" General delete operator handling +function! PareditDelete( type, ... ) + call PareditOpfunc( 'd', a:type, a:0 ) + if s:repeat > 1 + call feedkeys( (s:repeat-1) . "." ) + endif + let s:repeat = 0 +endfunction + +" General change operator handling +function! PareditChange( type, ... ) + let ve_save = &virtualedit + set virtualedit=all + call PareditOpfunc( 'c', a:type, a:0 ) + if len(getline('.')) == 0 + let v:lnum = line('.') + let expr = &indentexpr + if expr == '' + " No special 'indentexpr', call default lisp indent + let expr = 'lispindent(v:lnum)' + endif + execute "call setline( v:lnum, repeat( ' ', " . expr . " ) )" + call cursor(v:lnum, len(getline(v:lnum))+1) + else + normal! l + endif + startinsert + let &virtualedit = ve_save +endfunction + +" Delete v:count number of lines +function! PareditDeleteLines() + if v:count > 1 + silent exe "normal! V" . (v:count-1) . "j\" + else + silent exe "normal! V\" + endif + call PareditDelete(visualmode(),1) +endfunction + +" Change v:count number of lines +function! PareditChangeLines() + if v:count > 1 + silent exe "normal! V" . (v:count-1) . "j\" + else + silent exe "normal! V\" + endif + call PareditChange(visualmode(),1) +endfunction + +" Handle special change command, e.g. cw +" Check if we may revert to its original Vim function +" This way '.' can be used to repeat the command +function! PareditChangeSpec( cmd, dir ) + let line = getline( '.' ) + if a:dir == 0 + " Changing backwards + let c = col( '.' ) - 2 + while c >= 0 && line[c] =~ b:any_matched_char + " Shouldn't delete a matched character, just move left + call feedkeys( 'h', 'n') + let c = c - 1 + endwhile + if c < 0 && line[0] =~ b:any_matched_char + " Can't help, still on matched character, insert instead + call feedkeys( 'i', 'n') + return + endif + else + " Changing forward + let c = col( '.' ) - 1 + while c < len(line) && line[c] =~ b:any_matched_char + " Shouldn't delete a matched character, just move right + call feedkeys( 'l', 'n') + let c = c + 1 + endwhile + if c == len(line) + " Can't help, still on matched character, append instead + call feedkeys( 'a', 'n') + return + endif + endif + + " Safe to use Vim's built-in change function + call feedkeys( a:cmd, 'n') +endfunction + +" Paste text from put register in a balanced way +function! PareditPut( cmd ) + let regname = v:register + let reg_save = getreg( regname ) + let putreg = reg_save + + " Find unpaired matched characters by eliminating paired ones + let matched = s:GetMatchedChars( putreg, s:InsideString(), s:InsideComment() ) + let matched = s:Unbalanced( matched ) + + if matched !~ '\S\+' + " Register contents is balanced, perform default put function + silent exe "normal! " . (v:count>1 ? v:count : '') . (regname=='"' ? '' : '"'.regname) . a:cmd + return + endif + + " Replace all unpaired matched characters with a space in order to keep balance + let i = 0 + while i < len( putreg ) + if matched[i] !~ '\s' + let putreg = strpart( putreg, 0, i ) . ' ' . strpart( putreg, i+1 ) + endif + let i = i + 1 + endwhile + + " Store balanced text in put register and call the appropriate put command + call setreg( regname, putreg ) + silent exe "normal! " . (v:count>1 ? v:count : '') . (regname=='"' ? '' : '"'.regname) . a:cmd + call setreg( regname, reg_save ) +endfunction + +" Toggle paredit mode +function! PareditToggle() + " Don't disable paredit if it was not initialized yet for the current buffer + if exists( 'b:paredit_init') || g:paredit_mode == 0 + let g:paredit_mode = 1 - g:paredit_mode + endif + echo g:paredit_mode ? 'Paredit mode on' : 'Paredit mode off' + call PareditInitBuffer() +endfunction + +" Does the current syntax item match the given regular expression? +function! s:SynIDMatch( regexp, line, col, match_eol ) + let col = a:col + if a:match_eol && col > len( getline( a:line ) ) + let col = col - 1 + endif + return synIDattr( synID( a:line, col, 0), 'name' ) =~ a:regexp +endfunction + +" Expression used to check whether we should skip a match with searchpair() +function! s:SkipExpr() + let l = line('.') + let c = col('.') + if synIDattr(synID(l, c, 0), "name") =~ "[Ss]tring\\|[Cc]omment\\|[Ss]pecial\\|clojureRegexp\\|clojurePattern" + " Skip parens inside strings, comments, special elements + return 1 + endif + if getline(l)[c-2] == "\\" && getline(l)[c-3] != "\\" + " Skip parens escaped by '\' + return 1 + endif + return 0 +endfunction + +" Is the current cursor position inside a comment? +function! s:InsideComment( ... ) + let l = a:0 ? a:1 : line('.') + let c = a:0 ? a:2 : col('.') + if &syntax == '' + " No help from syntax engine, + " remove strings and search for ';' up to the cursor position + let line = strpart( getline(l), 0, c - 1 ) + let line = substitute( line, '\\"', '', 'g' ) + let line = substitute( line, '"[^"]*"', '', 'g' ) + return match( line, ';' ) >= 0 + endif + if s:SynIDMatch( 'clojureComment', l, c, 1 ) + if strpart( getline(l), c-1, 2 ) == '#_' || strpart( getline(l), c-2, 2 ) == '#_' + " This is a commented out clojure form of type #_(...), treat it as regular form + return 0 + endif + endif + return s:SynIDMatch( '[Cc]omment', l, c, 1 ) +endfunction + +" Is the current cursor position inside a string? +function! s:InsideString( ... ) + let l = a:0 ? a:1 : line('.') + let c = a:0 ? a:2 : col('.') + if &syntax == '' + " No help from syntax engine, + " count quote characters up to the cursor position + let line = strpart( getline(l), 0, c - 1 ) + let line = substitute( line, '\\"', '', 'g' ) + let quotes = substitute( line, '[^"]', '', 'g' ) + return len(quotes) % 2 + endif + " VimClojure and vim-clojure-static define special syntax for regexps + return s:SynIDMatch( '[Ss]tring\|clojureRegexp\|clojurePattern', l, c, 0 ) +endfunction + +" Is this a Slimv or VimClojure REPL buffer? +function! s:IsReplBuffer() + if exists( 'b:slimv_repl_buffer' ) || exists( 'b:vimclojure_repl' ) + return 1 + else + return 0 + endif +endfunction + +" Get Slimv or VimClojure REPL buffer last command prompt position +" Return [0, 0] if this is not the REPL buffer +function! s:GetReplPromptPos() + if !s:IsReplBuffer() + return [0, 0] + endif + if exists( 'b:vimclojure_repl') + let cur_pos = getpos( '.' ) + call cursor( line( '$' ), 1) + call cursor( line( '.' ), col( '$') ) + call search( b:vimclojure_namespace . '=>', 'bcW' ) + let target_pos = getpos( '.' )[1:2] + call setpos( '.', cur_pos ) + return target_pos + else + return [ b:repl_prompt_line, b:repl_prompt_col ] + endif +endfunction + +" Is the current top level form balanced, i.e all opening delimiters +" have a matching closing delimiter +function! s:IsBalanced() + let l = line( '.' ) + let c = col( '.' ) + let line = getline( '.' ) + if c > len(line) + let c = len(line) + endif + let matchb = max( [l-g:paredit_matchlines, 1] ) + let matchf = min( [l+g:paredit_matchlines, line('$')] ) + let [prompt, cp] = s:GetReplPromptPos() + if s:IsReplBuffer() && l >= prompt && matchb < prompt + " Do not go before the last command prompt in the REPL buffer + let matchb = prompt + endif + if line[c-1] == '(' + let p1 = searchpair( '(', '', ')', 'brnmWc', 's:SkipExpr()', matchb ) + let p2 = searchpair( '(', '', ')', 'rnmW' , 's:SkipExpr()', matchf ) + elseif line[c-1] == ')' + let p1 = searchpair( '(', '', ')', 'brnmW' , 's:SkipExpr()', matchb ) + let p2 = searchpair( '(', '', ')', 'rnmWc', 's:SkipExpr()', matchf ) + else + let p1 = searchpair( '(', '', ')', 'brnmW' , 's:SkipExpr()', matchb ) + let p2 = searchpair( '(', '', ')', 'rnmW' , 's:SkipExpr()', matchf ) + endif + if p1 != p2 + " Number of opening and closing parens differ + return 0 + endif + + if &ft =~ s:fts_balancing_all_brackets + if line[c-1] == '[' + let b1 = searchpair( '\[', '', '\]', 'brnmWc', 's:SkipExpr()', matchb ) + let b2 = searchpair( '\[', '', '\]', 'rnmW' , 's:SkipExpr()', matchf ) + elseif line[c-1] == ']' + let b1 = searchpair( '\[', '', '\]', 'brnmW' , 's:SkipExpr()', matchb ) + let b2 = searchpair( '\[', '', '\]', 'rnmWc', 's:SkipExpr()', matchf ) + else + let b1 = searchpair( '\[', '', '\]', 'brnmW' , 's:SkipExpr()', matchb ) + let b2 = searchpair( '\[', '', '\]', 'rnmW' , 's:SkipExpr()', matchf ) + endif + if b1 != b2 + " Number of opening and closing brackets differ + return 0 + endif + if line[c-1] == '{' + let b1 = searchpair( '{', '', '}', 'brnmWc', 's:SkipExpr()', matchb ) + let b2 = searchpair( '{', '', '}', 'rnmW' , 's:SkipExpr()', matchf ) + elseif line[c-1] == '}' + let b1 = searchpair( '{', '', '}', 'brnmW' , 's:SkipExpr()', matchb ) + let b2 = searchpair( '{', '', '}', 'rnmWc', 's:SkipExpr()', matchf ) + else + let b1 = searchpair( '{', '', '}', 'brnmW' , 's:SkipExpr()', matchb ) + let b2 = searchpair( '{', '', '}', 'rnmW' , 's:SkipExpr()', matchf ) + endif + if b1 != b2 + " Number of opening and closing curly braces differ + return 0 + endif + endif + return 1 +endfunction + +" Filter out all non-matched characters from the region +function! s:GetMatchedChars( lines, start_in_string, start_in_comment ) + let inside_string = a:start_in_string + let inside_comment = a:start_in_comment + let matched = repeat( ' ', len( a:lines ) ) + let i = 0 + while i < len( a:lines ) + if inside_string + " We are inside a string, skip parens, wait for closing '"' + " but skip escaped \" characters + if a:lines[i] == '"' && a:lines[i-1] != '\' + let matched = strpart( matched, 0, i ) . a:lines[i] . strpart( matched, i+1 ) + let inside_string = 0 + endif + elseif inside_comment + " We are inside a comment, skip parens, wait for end of line + if a:lines[i] == "\n" + let inside_comment = 0 + endif + elseif i > 0 && a:lines[i-1] == '\' && (i < 2 || a:lines[i-2] != '\') + " This is an escaped character, ignore it + else + " We are outside of strings and comments, now we shall count parens + if a:lines[i] == '"' + let matched = strpart( matched, 0, i ) . a:lines[i] . strpart( matched, i+1 ) + let inside_string = 1 + endif + if a:lines[i] == ';' + let inside_comment = 1 + endif + if a:lines[i] =~ b:any_openclose_char + let matched = strpart( matched, 0, i ) . a:lines[i] . strpart( matched, i+1 ) + endif + endif + let i = i + 1 + endwhile + return matched +endfunction + +" Find unpaired matched characters by eliminating paired ones +function! s:Unbalanced( matched ) + let matched = a:matched + let tmp = matched + while 1 + let matched = tmp + let tmp = substitute( tmp, '(\(\s*\))', ' \1 ', 'g') + if &ft =~ s:fts_balancing_all_brackets + let tmp = substitute( tmp, '\[\(\s*\)\]', ' \1 ', 'g') + let tmp = substitute( tmp, '{\(\s*\)}', ' \1 ', 'g') + endif + let tmp = substitute( tmp, '"\(\s*\)"', ' \1 ', 'g') + if tmp == matched + " All paired chars eliminated + let tmp = substitute( tmp, ')\(\s*\)(', ' \1 ', 'g') + if &ft =~ s:fts_balancing_all_brackets + let tmp = substitute( tmp, '\]\(\s*\)\[', ' \1 ', 'g') + let tmp = substitute( tmp, '}\(\s*\){', ' \1 ', 'g') + endif + if tmp == matched + " Also no more inverse pairs can be eliminated + break + endif + endif + endwhile + return matched +endfunction + +" Find opening matched character +function! PareditFindOpening( open, close, select ) + let open = escape( a:open , '[]' ) + let close = escape( a:close, '[]' ) + call searchpair( open, '', close, 'bW', 's:SkipExpr()' ) + if a:select + call searchpair( open, '', close, 'W', 's:SkipExpr()' ) + let save_ve = &ve + set ve=all + normal! lvh + let &ve = save_ve + call searchpair( open, '', close, 'bW', 's:SkipExpr()' ) + if &selection == 'inclusive' + " Trim last character from the selection, it will be included anyway + normal! oho + endif + endif +endfunction + +" Find closing matched character +function! PareditFindClosing( open, close, select ) + let open = escape( a:open , '[]' ) + let close = escape( a:close, '[]' ) + if a:select + let line = getline( '.' ) + if line[col('.')-1] != a:open + normal! h + endif + call searchpair( open, '', close, 'W', 's:SkipExpr()' ) + call searchpair( open, '', close, 'bW', 's:SkipExpr()' ) + normal! v + call searchpair( open, '', close, 'W', 's:SkipExpr()' ) + if &selection != 'inclusive' + normal! l + endif + else + call searchpair( open, '', close, 'W', 's:SkipExpr()' ) + endif +endfunction + +" Returns the nearest opening character to the cursor +" Used for smart jumping in Clojure +function! PareditSmartJumpOpening( select ) + let [paren_line, paren_col] = searchpairpos('(', '', ')', 'bWn', 's:SkipExpr()') + let [bracket_line, bracket_col] = searchpairpos('\[', '', '\]', 'bWn', 's:SkipExpr()') + let [brace_line, brace_col] = searchpairpos('{', '', '}', 'bWn', 's:SkipExpr()') + let paren_score = paren_line * 10000 + paren_col + let bracket_score = bracket_line * 10000 + bracket_col + let brace_score = brace_line * 10000 + brace_col + if (brace_score > paren_score || paren_score == 0) && (brace_score > bracket_score || bracket_score == 0) && brace_score != 0 + call PareditFindOpening('{','}', a:select) + elseif (bracket_score > paren_score || paren_score == 0) && bracket_score != 0 + call PareditFindOpening('[',']', a:select) + else + call PareditFindOpening('(',')', a:select) + endif +endfunction + +" Returns the nearest opening character to the cursor +" Used for smart jumping in Clojure +function! PareditSmartJumpClosing( select ) + let [paren_line, paren_col] = searchpairpos('(', '', ')', 'Wn', 's:SkipExpr()') + let [bracket_line, bracket_col] = searchpairpos('\[', '', '\]', 'Wn', 's:SkipExpr()') + let [brace_line, brace_col] = searchpairpos('{', '', '}', 'Wn', 's:SkipExpr()') + let paren_score = paren_line * 10000 + paren_col + let bracket_score = bracket_line * 10000 + bracket_col + let brace_score = brace_line * 10000 + brace_col + if (brace_score < paren_score || paren_score == 0) && (brace_score < bracket_score || bracket_score == 0) && brace_score != 0 + call PareditFindClosing('{','}', a:select) + elseif (bracket_score < paren_score || paren_score == 0) && bracket_score != 0 + call PareditFindClosing('[',']', a:select) + else + call PareditFindClosing('(',')', a:select) + endif +endfunction + +" Find defun start backwards +function! PareditFindDefunBck() + let l = line( '.' ) + let matchb = max( [l-g:paredit_matchlines, 1] ) + let oldpos = getpos( '.' ) + let newpos = searchpairpos( '(', '', ')', 'brW', 's:SkipExpr()', matchb ) + if newpos[0] == 0 + " Already standing on a defun, find the end of the previous one + let newpos = searchpos( ')', 'bW' ) + while newpos[0] != 0 && (s:InsideComment() || s:InsideString()) + let newpos = searchpos( ')', 'W' ) + endwhile + if newpos[0] == 0 + " No ')' found, don't move cursor + call setpos( '.', oldpos ) + else + " Find opening paren + let pairpos = searchpairpos( '(', '', ')', 'brW', 's:SkipExpr()', matchb ) + if pairpos[0] == 0 + " ')' has no matching pair + call setpos( '.', oldpos ) + endif + endif + endif +endfunction + +" Find defun start forward +function! PareditFindDefunFwd() + let l = line( '.' ) + let matchf = min( [l+g:paredit_matchlines, line('$')] ) + let oldpos = getpos( '.' ) + call searchpair( '(', '', ')', 'brW', 's:SkipExpr()', matchf ) + normal! % + let newpos = searchpos( '(', 'W' ) + while newpos[0] != 0 && (s:InsideComment() || s:InsideString()) + let newpos = searchpos( '(', 'W' ) + endwhile + if newpos[0] == 0 + " No '(' found, don't move cursor + call setpos( '.', oldpos ) + endif +endfunction + +" Insert opening type of a paired character, like ( or [. +function! PareditInsertOpening( open, close ) + if !g:paredit_mode || s:InsideComment() || s:InsideString() || !s:IsBalanced() + return a:open + endif + let line = getline( '.' ) + let pos = col( '.' ) - 1 + if pos > 0 && line[pos-1] == '\' && (pos < 2 || line[pos-2] != '\') + " About to enter a \( or \[ + return a:open + elseif line[pos] !~ b:any_wsclose_char && pos < len( line ) + " Add a space after if needed + let retval = a:open . a:close . " \\" + else + let retval = a:open . a:close . "\" + endif + if pos > 0 && line[pos-1] !~ b:any_wsopen_char && line[pos-1] !~ s:any_macro_prefix + " Add a space before if needed + let retval = " " . retval + endif + return retval +endfunction + +" Re-gather electric returns up +function! s:ReGatherUp() + if g:paredit_electric_return && getline('.') =~ '^\s*)' + " Re-gather electric returns in the current line for ')' + normal! k + while getline( line('.') ) =~ '^\s*$' + " Delete all empty lines + normal! ddk + endwhile + normal! Jl + elseif g:paredit_electric_return && getline('.') =~ '^\s*\(\]\|}\)' && &ft =~ s:fts_balancing_all_brackets + " Re-gather electric returns in the current line for ']' and '}' + normal! k + while getline( line('.') ) =~ '^\s*$' + " Delete all empty lines + normal! ddk + endwhile + call setline( line('.'), substitute( line, '\s*$', '', 'g' ) ) + normal! Jxl + endif + " Already have the desired character, move right + normal! l +endfunction + +" Insert closing type of a paired character, like ) or ]. +function! PareditInsertClosing( open, close ) + let retval = "" + if pumvisible() + let retval = "\" + endif + let save_ve = &ve + set ve=all + let line = getline( '.' ) + let pos = col( '.' ) - 1 + if !g:paredit_mode || s:InsideComment() || s:InsideString() || !s:IsBalanced() + call setline( line('.'), line[0 : pos-1] . a:close . line[pos : -1] ) + normal! l + let &ve = save_ve + return retval + endif + if pos > 0 && line[pos-1] == '\' && (pos < 2 || line[pos-2] != '\') + " About to enter a \) or \] + call setline( line('.'), line[0 : pos-1] . a:close . line[pos : -1] ) + normal! l + let &ve = save_ve + return retval + elseif line[pos] == a:close + call s:ReGatherUp() + let &ve = save_ve + return retval + endif + let open = escape( a:open , '[]' ) + let close = escape( a:close, '[]' ) + let newpos = searchpairpos( open, '', close, 'nW', 's:SkipExpr()' ) + if g:paredit_electric_return && newpos[0] > line('.') + " Closing paren is in a line below, check if there are electric returns to re-gather + while getline('.') =~ '^\s*$' + " Delete all empty lines above the cursor + normal! ddk + endwhile + let oldpos = getpos( '.' ) + normal! j + while getline('.') =~ '^\s*$' + " Delete all empty lines below the cursor + normal! dd + endwhile + let nextline = substitute( getline('.'), '\s', '', 'g' ) + call setpos( '.', oldpos ) + if len(nextline) > 0 && nextline[0] == ')' + " Re-gather electric returns in the line of the closing ')' + call setline( line('.'), substitute( getline('.'), '\s*$', '', 'g' ) ) + normal! Jl + let &ve = save_ve + return retval + endif + if len(nextline) > 0 && nextline[0] =~ '\]\|}' && &ft =~ s:fts_balancing_all_brackets + " Re-gather electric returns in the line of the closing ']' or '}' + call setline( line('.'), substitute( line, '\s*$', '', 'g' ) ) + normal! Jxl + let &ve = save_ve + return retval + endif + elseif g:paredit_electric_return && line =~ '^\s*)' + " Re-gather electric returns in the current line + call s:ReGatherUp() + let &ve = save_ve + return retval + endif + if searchpair( open, '', close, 'W', 's:SkipExpr()' ) > 0 + normal! l + endif + "TODO: indent after going to closing character + let &ve = save_ve + return retval +endfunction + +" Insert an (opening or closing) double quote +function! PareditInsertQuotes() + if !g:paredit_mode || s:InsideComment() + return '"' + endif + let line = getline( '.' ) + let pos = col( '.' ) - 1 + if pos > 0 && line[pos-1] == '\' && (pos < 2 || line[pos-2] != '\') + " About to enter a \" + return '"' + elseif s:InsideString() + "TODO: skip comments in search(...) + if line[pos] == '"' + " Standing on a ", just move to the right + return "\" + elseif search('[^\\]"\|^"', 'nW') == 0 + " We don't have any closing ", insert one + return '"' + else + " Move to the closing " + return "\:call search('" . '[^\\]"\|^"' . "','eW')\\" + endif + else + " Outside of string: insert a pair of "" + return '""' . "\" + endif +endfunction + +" Handle keypress, insert electric return if applicable +function! PareditEnter() + if pumvisible() + " Pressing in a pop up selects entry. + return "\" + else + let line = getline( '.' ) + let pos = col( '.' ) - 1 + if g:paredit_electric_return && pos > 0 && line[pos] =~ b:any_closing_char && !s:InsideString() && s:IsBalanced() + " Electric Return + return "\\\" + else + " Regular Return + return "\" + endif + endif +endfunction + +" Handle keypress +function! PareditBackspace( repl_mode ) + let [lp, cp] = s:GetReplPromptPos() + if a:repl_mode && line( "." ) == lp && col( "." ) <= cp + " No BS allowed before the previous EOF mark in the REPL + " i.e. don't delete Lisp prompt + return "" + endif + + if !g:paredit_mode || s:InsideComment() + return "\" + endif + + let line = getline( '.' ) + let pos = col( '.' ) - 1 + + if pos == 0 + " We are at the beginning of the line + return "\" + elseif s:InsideString() && line[pos-1] =~ b:any_openclose_char + " Deleting a paren inside a string + return "\" + elseif pos > 1 && line[pos-1] =~ b:any_matched_char && line[pos-2] == '\' && (pos < 3 || line[pos-3] != '\') + " Deleting an escaped matched character + return "\\" + elseif line[pos-1] !~ b:any_matched_char + " Deleting a non-special character + return "\" + elseif line[pos-1] != '"' && !s:IsBalanced() + " Current top-form is unbalanced, can't retain paredit mode + return "\" + endif + + if line[pos-1:pos] =~ b:any_matched_pair + " Deleting an empty character-pair + return "\\\" + else + " Character-pair is not empty, don't delete just move inside + return "\" + endif +endfunction + +" Handle keypress +function! PareditDel() + if !g:paredit_mode || s:InsideComment() + return "\" + endif + + let line = getline( '.' ) + let pos = col( '.' ) - 1 + + if pos == len(line) + " We are at the end of the line + return "\" + elseif line[pos] == '\' && line[pos+1] =~ b:any_matched_char && (pos < 1 || line[pos-1] != '\') + " Deleting an escaped matched character + return "\\" + elseif line[pos] !~ b:any_matched_char + " Erasing a non-special character + return "\" + elseif line[pos] != '"' && !s:IsBalanced() + " Current top-form is unbalanced, can't retain paredit mode + return "\" + elseif pos == 0 + return "\" + endif + + if line[pos-1:pos] =~ b:any_matched_pair + " Erasing an empty character-pair + return "\\\" + else + " Character-pair is not empty, don't erase just move inside + return "\" + endif +endfunction + +" Initialize yank position list +function! s:InitYankPos() + call setreg( &clipboard == 'unnamed' ? '*' : '"', '' ) + let s:yank_pos = [] +endfunction + +" Add position to the yank list +function! s:AddYankPos( pos ) + let s:yank_pos = [a:pos] + s:yank_pos +endfunction + +" Remove the head of yank position list and return it +function! s:RemoveYankPos() + if len(s:yank_pos) > 0 + let pos = s:yank_pos[0] + let s:yank_pos = s:yank_pos[1:] + return pos + else + return 0 + endif +endfunction + +" Forward erasing a character in normal mode, do not check if current form balanced +function! s:EraseFwd( count, startcol ) + let line = getline( '.' ) + let pos = col( '.' ) - 1 + let reg = '' + let ve_save = &virtualedit + set virtualedit=all + let c = a:count + while c > 0 + if line[pos] == '\' && line[pos+1] =~ b:any_matched_char && (pos < 1 || line[pos-1] != '\') + " Erasing an escaped matched character + let reg = reg . line[pos : pos+1] + let line = strpart( line, 0, pos ) . strpart( line, pos+2 ) + elseif s:InsideComment() && line[pos] == ';' && a:startcol >= 0 + " Erasing the whole comment, only when erasing a block of characters + let reg = reg . strpart( line, pos ) + let line = strpart( line, 0, pos ) + elseif s:InsideComment() || ( s:InsideString() && line[pos] != '"' ) + " Erasing any character inside string or comment + let reg = reg . line[pos] + let line = strpart( line, 0, pos ) . strpart( line, pos+1 ) + elseif pos > 0 && line[pos-1:pos] =~ b:any_matched_pair + if pos > a:startcol + " Erasing an empty character-pair + let p2 = s:RemoveYankPos() + let reg = strpart( reg, 0, p2 ) . line[pos-1] . strpart( reg, p2 ) + let reg = reg . line[pos] + let line = strpart( line, 0, pos-1 ) . strpart( line, pos+1 ) + let pos = pos - 1 + normal! h + else + " Can't erase character-pair: it would move the cursor before startcol + let pos = pos + 1 + normal! l + endif + elseif line[pos] =~ b:any_matched_char + " Character-pair is not empty, don't erase just move inside + call s:AddYankPos( len(reg) ) + let pos = pos + 1 + normal! l + elseif pos < len(line) && pos >= a:startcol + " Erasing a non-special character + let chars = split(strpart(line, pos), '\zs') + if len(chars) > 0 + " Identify the character to be erased and it's length + " The length may be >1 if this is a multi-byte character + let ch = chars[0] + let reg = reg . ch + let line = strpart( line, 0, pos ) . strpart( line, pos+len(ch) ) + endif + endif + let c = c - 1 + endwhile + let &virtualedit = ve_save + call setline( '.', line ) + call setreg( &clipboard == 'unnamed' ? '*' : '"', reg ) +endfunction + +" Backward erasing a character in normal mode, do not check if current form balanced +function! s:EraseBck( count ) + let line = getline( '.' ) + let pos = col( '.' ) - 1 + let reg = '' + let c = a:count + while c > 0 && pos > 0 + if pos > 1 && line[pos-2] == '\' && line[pos-1] =~ b:any_matched_char && (pos < 3 || line[pos-3] != '\') + " Erasing an escaped matched character + let reg = reg . line[pos-2 : pos-1] + let line = strpart( line, 0, pos-2 ) . strpart( line, pos ) + normal! h + let pos = pos - 1 + elseif s:InsideComment() || ( s:InsideString() && line[pos-1] != '"' ) + let reg = reg . line[pos-1] + let line = strpart( line, 0, pos-1 ) . strpart( line, pos ) + elseif line[pos-1:pos] =~ b:any_matched_pair + " Erasing an empty character-pair + let p2 = s:RemoveYankPos() + let reg = strpart( reg, 0, p2 ) . line[pos-1] . strpart( reg, p2 ) + let reg = reg . line[pos] + let line = strpart( line, 0, pos-1 ) . strpart( line, pos+1 ) + elseif line[pos-1] =~ b:any_matched_char + " Character-pair is not empty, don't erase + call s:AddYankPos( len(reg) ) + else + " Erasing a non-special character + let chars = split(strpart(line, 0, pos), '\zs') + if len(chars) > 0 + " Identify the character to be erased and it's length + " The length may be >1 if this is a multi-byte character + let ch = chars[-1] + let reg = reg . ch + let line = strpart( line, 0, pos-len(ch) ) . strpart( line, pos ) + let pos = pos - len(ch) + 1 + endif + endif + normal! h + let pos = pos - 1 + let c = c - 1 + endwhile + call setline( '.', line ) + call setreg( &clipboard == 'unnamed' ? '*' : '"', reg ) +endfunction + +" Forward erasing a character in normal mode +function! PareditEraseFwd() + if !g:paredit_mode || !s:IsBalanced() + if v:count > 0 + silent execute 'normal! ' . v:count . 'x' + else + normal! x + endif + return + endif + + call s:InitYankPos() + call s:EraseFwd( v:count1, -1 ) +endfunction + +" Backward erasing a character in normal mode +function! PareditEraseBck() + if !g:paredit_mode || !s:IsBalanced() + if v:count > 0 + silent execute 'normal! ' . v:count . 'X' + else + normal! X + endif + return + endif + + call s:InitYankPos() + call s:EraseBck( v:count1 ) +endfunction + +" Find beginning of previous element (atom or sub-expression) in a form +" skip_whitespc: skip whitespaces before the previous element +function! s:PrevElement( skip_whitespc ) + let [l0, c0] = [line( '.' ), col( '.' )] + let symbol_pos = [0, 0] + let symbol_end = [0, 0] + + " Move to the beginning of the prefix if any + let line = getline( '.' ) + let c = col('.') - 1 + if c > 0 && line[c-1] =~ s:any_macro_prefix + normal! h + endif + + let moved = 0 + while 1 + " Go to previous character + if !moved + let [l1, c1] = [line( '.' ), col( '.' )] + let save_ww = &whichwrap + set whichwrap= + normal! h + let &whichwrap = save_ww + endif + let moved = 0 + let [l, c] = [line( '.' ), col( '.' )] + + if [l, c] == [l1, c1] + " Beginning of line reached + if symbol_pos != [0, 0] + let symbol_end = [l, c] + if !a:skip_whitespc && !s:InsideString() + " Newline before previous symbol + call setpos( '.', [0, l0, c0, 0] ) + return [l, c] + endif + endif + normal! k$ + let [l, c] = [line( '.' ), col( '.' )] + if [l, c] == [l1, c1] + " Beginning of file reached: stop + call setpos( '.', [0, l0, c0, 0] ) + return [0, 0] + endif + let moved = 1 + elseif s:InsideComment() + " Skip comments + else + let line = getline( '.' ) + if s:InsideString() && !(a:skip_whitespc && line[c] =~ '\s' && symbol_end != [0, 0]) + let symbol_pos = [l, c] + elseif symbol_pos == [0, 0] + if line[c-1] =~ b:any_closing_char + " Skip to the beginning of this sub-expression + let symbol_pos = [l, c] + normal! % + let line2 = getline( '.' ) + let c2 = col('.') - 1 + if c2 > 0 && line2[c2-1] =~ s:any_macro_prefix + normal! h + endif + elseif line[c-1] =~ b:any_opening_char + " Opening delimiter found: stop + call setpos( '.', [0, l0, c0, 0] ) + return [0, 0] + elseif line[c-1] =~ '\S' + " Previous symbol starting + let symbol_pos = [l, c] + endif + else + if line[c-1] =~ b:any_opening_char || (a:skip_whitespc && line[c-1] =~ '\S' && symbol_end != [0, 0]) + " Previous symbol beginning reached, opening delimiter or second previous symbol starting + call setpos( '.', [0, l0, c0, 0] ) + return [l, c+1] + elseif line[c-1] =~ '\s' || symbol_pos[0] != l + " Whitespace before previous symbol + let symbol_end = [l, c] + if !a:skip_whitespc + call setpos( '.', [0, l0, c0, 0] ) + return [l, c+1] + endif + endif + endif + endif + endwhile +endfunction + +" Find end of next element (atom or sub-expression) in a form +" skip_whitespc: skip whitespaces after the next element +function! s:NextElement( skip_whitespc ) + let [l0, c0] = [line( '.' ), col( '.' )] + let symbol_pos = [0, 0] + let symbol_end = [0, 0] + + while 1 + " Go to next character + let [l1, c1] = [line( '.' ), col( '.' )] + let save_ww = &whichwrap + set whichwrap= + normal! l + let &whichwrap = save_ww + let [l, c] = [line( '.' ), col( '.' )] + + " Skip comments + while [l, c] == [l1, c1] || s:InsideComment() + if symbol_pos != [0, 0] + let symbol_end = [l, c] + if !a:skip_whitespc && !s:InsideString() + " Next symbol ended with comment + call setpos( '.', [0, l0, c0, 0] ) + return [l, c + ([l, c] == [l1, c1])] + endif + endif + normal! 0j0 + let [l, c] = [line( '.' ), col( '.' )] + if [l, c] == [l1, c1] + " End of file reached: stop + call setpos( '.', [0, l0, c0, 0] ) + return [0, 0] + endif + endwhile + + let line = getline( '.' ) + if s:InsideString() && !(a:skip_whitespc && line[c-2] =~ '\s' && symbol_end != [0, 0]) + let symbol_pos = [l, c] + elseif symbol_pos == [0, 0] + if line[c-1] =~ s:any_macro_prefix && line[c] =~ b:any_opening_char + " Skip to the end of this prefixed sub-expression + let symbol_pos = [l, c] + normal! l% + elseif line[c-1] =~ b:any_opening_char + " Skip to the end of this sub-expression + let symbol_pos = [l, c] + normal! % + elseif line[c-1] =~ b:any_closing_char + " Closing delimiter found: stop + call setpos( '.', [0, l0, c0, 0] ) + return [0, 0] + elseif line[c-1] =~ '\S' + " Next symbol starting + let symbol_pos = [l, c] + endif + else + if line[c-1] =~ b:any_closing_char || (a:skip_whitespc && line[c-1] =~ '\S' && symbol_end != [0, 0]) + " Next symbol ended, closing delimiter or second next symbol starting + call setpos( '.', [0, l0, c0, 0] ) + return [l, c] + elseif line[c-1] =~ '\s' || symbol_pos[0] != l + " Next symbol ending with whitespace + let symbol_end = [l, c] + if !a:skip_whitespc + call setpos( '.', [0, l0, c0, 0] ) + return [l, c] + endif + endif + endif + endwhile +endfunction + +" Move character from [l0, c0] to [l1, c1] +" Set position to [l1, c1] +function! s:MoveChar( l0, c0, l1, c1 ) + let line = getline( a:l0 ) + let c = line[a:c0-1] + if a:l1 == a:l0 + " Move character inside line + if a:c1 > a:c0 + let line = strpart( line, 0, a:c0-1 ) . strpart( line, a:c0, a:c1-a:c0-1 ) . c . strpart( line, a:c1-1 ) + call setline( a:l0, line ) + call setpos( '.', [0, a:l1, a:c1-1, 0] ) + else + let line = strpart( line, 0, a:c1-1 ) . c . strpart( line, a:c1-1, a:c0-a:c1 ) . strpart( line, a:c0 ) + call setline( a:l0, line ) + call setpos( '.', [0, a:l1, a:c1, 0] ) + endif + else + " Move character to another line + let line = strpart( line, 0, a:c0-1 ) . strpart( line, a:c0 ) + call setline( a:l0, line ) + let line1 = getline( a:l1 ) + if a:c1 > 1 + let line1 = strpart( line1, 0, a:c1-1 ) . c . strpart( line1, a:c1-1 ) + call setline( a:l1, line1 ) + call setpos( '.', [0, a:l1, a:c1, 0] ) + else + let line1 = c . line1 + call setline( a:l1, line1 ) + call setpos( '.', [0, a:l1, 1, 0] ) + endif + endif +endfunction + +" Find a paren nearby to move +function! s:FindParenNearby() + let line = getline( '.' ) + let c0 = col( '.' ) + if line[c0-1] !~ b:any_openclose_char + " OK, we are not standing on a paren to move, but check if there is one nearby + if (c0 < 2 || line[c0-2] !~ b:any_openclose_char) && line[c0] =~ b:any_openclose_char + normal! l + elseif c0 > 1 && line[c0-2] =~ b:any_openclose_char && line[c0] !~ b:any_openclose_char + normal! h + endif + endif + + " Skip macro prefix character + let c0 = col( '.' ) + if line[c0-1] =~ s:any_macro_prefix && line[c0] =~ b:any_opening_char + normal! l + endif + + " If still not standing on a paren then find the next closing one + if line[c0-1] !~ b:any_openclose_char + call search(b:any_closing_char, 'W') + endif +endfunction + +" Reindent current form +function! PareditReindentForm() + let l = line('.') + let c = col('.') + let old_indent = len(matchstr(getline(l), '^\s*')) + normal! =ib + let new_indent = len(matchstr(getline(l), '^\s*')) + call cursor( l, c + new_indent - old_indent ) +endfunction + +" Move delimiter one atom or s-expression to the left +function! PareditMoveLeft() + call s:FindParenNearby() + + let line = getline( '.' ) + let l0 = line( '.' ) + let c0 = col( '.' ) + + if line[c0-1] =~ b:any_opening_char + let closing = 0 + elseif line[c0-1] =~ b:any_closing_char + let closing = 1 + else + " Can move only delimiters + return + endif + + let [lp, cp] = s:GetReplPromptPos() + let [l1, c1] = s:PrevElement( closing ) + if [l1, c1] == [0, 0] + " No previous element found + return + elseif [lp, cp] != [0, 0] && l0 >= lp && (l1 < lp || (l1 == lp && c1 < cp)) + " Do not go before the last command prompt in the REPL buffer + return + endif + if !closing && c0 > 0 && line[c0-2] =~ s:any_macro_prefix + call s:MoveChar( l0, c0-1, l1, c1 ) + call s:MoveChar( l0, c0 - (l0 != l1), l1, c1+1 ) + let len = 2 + else + call s:MoveChar( l0, c0, l1, c1 ) + let len = 1 + endif + let line = getline( '.' ) + let c = col( '.' ) - 1 + if closing && c+1 < len(line) && line[c+1] !~ b:any_wsclose_char + " Insert a space after if needed + execute "normal! a " + normal! h + endif + let line = getline( '.' ) + let c = col( '.' ) - 1 + if !closing && c > 0 && line[c-len] !~ b:any_wsopen_char + " Insert a space before if needed + if len > 1 + execute "normal! hi " + normal! ll + else + execute "normal! i " + normal! l + endif + endif + call PareditReindentForm() +endfunction + +" Move delimiter one atom or s-expression to the right +function! PareditMoveRight() + call s:FindParenNearby() + + "TODO: move ')' in '() xxx' leaves space + let line = getline( '.' ) + let l0 = line( '.' ) + let c0 = col( '.' ) + + if line[c0-1] =~ b:any_opening_char + let opening = 1 + elseif line[c0-1] =~ b:any_closing_char + let opening = 0 + else + " Can move only delimiters + return + endif + + let [lp, cp] = s:GetReplPromptPos() + let [l1, c1] = s:NextElement( opening ) + if [l1, c1] == [0, 0] + " No next element found + return + elseif [lp, cp] != [0, 0] && l0 < lp && l1 >= lp + " Do not go after the last command prompt in the REPL buffer + return + endif + if opening && c0 > 1 && line[c0-2] =~ s:any_macro_prefix + call s:MoveChar( l0, c0-1, l1, c1 ) + call s:MoveChar( l0, c0-1, l1, c1 + (l0 != l1) ) + let len = 2 + else + call s:MoveChar( l0, c0, l1, c1 ) + let len = 1 + endif + let line = getline( '.' ) + let c = col( '.' ) - 1 + if opening && c > 0 && line[c-len] !~ b:any_wsopen_char + " Insert a space before if needed + if len > 1 + execute "normal! hi " + normal! ll + else + execute "normal! i " + normal! l + endif + endif + let line = getline( '.' ) + let c = col( '.' ) - 1 + if !opening && c+1 < len(line) && line[c+1] !~ b:any_wsclose_char + " Insert a space after if needed + execute "normal! a " + normal! h + endif + call PareditReindentForm() +endfunction + +" Find closing of the innermost structure: (...) or [...] or {...} +" Return a list where first element is the closing character, +" second and third is its position (line, column) +function! s:FindClosing() + let l = line( '.' ) + let c = col( '.' ) + let paren = '' + let l2 = 0 + let c2 = 0 + + call PareditFindClosing( '(', ')', 0 ) + let lp = line( '.' ) + let cp = col( '.' ) + if [lp, cp] != [l, c] + " Do we have a closing ')'? + let paren = ')' + let l2 = lp + let c2 = cp + endif + call setpos( '.', [0, l, c, 0] ) + + if &ft =~ s:fts_balancing_all_brackets + call PareditFindClosing( '[', ']', 0 ) + let lp = line( '.' ) + let cp = col( '.' ) + if [lp, cp] != [l, c] && (lp < l2 || (lp == l2 && cp < c2)) + " Do we have a ']' closer? + let paren = ']' + let l2 = lp + let c2 = cp + endif + call setpos( '.', [0, l, c, 0] ) + + call PareditFindClosing( '{', '}', 0 ) + let lp = line( '.' ) + let cp = col( '.' ) + if [lp, cp] != [l, c] && (lp < l2 || (lp == l2 && cp < c2)) + " Do we have a '}' even closer? + let paren = '}' + let l2 = lp + let c2 = cp + endif + call setpos( '.', [0, l, c, 0] ) + endif + + return [paren, l2, c2] +endfunction + +" Split list or string at the cursor position +" Current symbol will be split into the second part +function! PareditSplit() + if !g:paredit_mode || s:InsideComment() + return + endif + + if s:InsideString() + normal! i" " + else + " Go back to the beginning of the current symbol + let c = col('.') - 1 + if getline('.')[c] =~ '\S' + if c == 0 || (c > 0 && getline('.')[c-1] =~ b:any_wsopen_char) + " OK, we are standing on the first character of the symbol + else + normal! b + endif + endif + + " First find which kind of paren is the innermost + let [p, l, c] = s:FindClosing() + if p !~ b:any_closing_char + " Not found any kind of parens + return + endif + + " Delete all whitespaces around cursor position + while getline('.')[col('.')-1] =~ '\s' + normal! x + endwhile + while col('.') > 1 && getline('.')[col('.')-2] =~ '\s' + normal! X + endwhile + + if p == ')' + normal! i) ( + elseif p == '}' + normal! i} { + else + normal! i] [ + endif + endif +endfunction + +" Join two neighboring lists or strings +function! PareditJoin() + if !g:paredit_mode || s:InsideComment() || s:InsideString() + return + endif + + "TODO: skip parens in comments + let [l0, c0] = searchpos(b:any_matched_char, 'nbW') + let [l1, c1] = searchpos(b:any_matched_char, 'ncW') + if [l0, c0] == [0, 0] || [l1, c1] == [0, 0] + return + endif + let line0 = getline( l0 ) + let line1 = getline( l1 ) + let p0 = line0[c0-1] + let p1 = line1[c1-1] + if (p0 == ')' && p1 == '(') || (p0 == ']' && p1 == '[') || (p0 == '}' && p1 == '{') || (p0 == '"' && p1 == '"') + if l0 == l1 + " First list ends on the same line where the second list begins + let line0 = strpart( line0, 0, c0-1 ) . ' ' . strpart( line0, c1 ) + call setline( l0, line0 ) + else + " First list ends on a line different from where the second list begins + let line0 = strpart( line0, 0, c0-1 ) + let line1 = strpart( line1, 0, c1-1 ) . strpart( line1, c1 ) + call setline( l0, line0 ) + call setline( l1, line1 ) + endif + endif +endfunction + +" Wrap current visual block in parens of the given kind +function! s:WrapSelection( open, close ) + let l0 = line( "'<" ) + let l1 = line( "'>" ) + let c0 = col( "'<" ) + let c1 = col( "'>" ) + if &selection == 'inclusive' + let c1 = c1 + strlen(matchstr(getline(l1)[c1-1 :], '.')) + endif + if [l0, c0] == [0, 0] || [l1, c1] == [0, 0] + " No selection + return + endif + if l0 > l1 || (l0 == l1 && c0 > c1) + " Swap both ends of selection to make [l0, c0] < [l1, c1] + let [ltmp, ctmp] = [l0, c0] + let [l0, c0] = [l1, c1] + let [l1, c1] = [ltmp, ctmp] + endif + let save_ve = &ve + set ve=all + call setpos( '.', [0, l0, c0, 0] ) + execute "normal! i" . a:open + call setpos( '.', [0, l1, c1 + (l0 == l1), 0] ) + execute "normal! i" . a:close + let &ve = save_ve +endfunction + +" Wrap current visual block in parens of the given kind +" Keep visual mode +function! PareditWrapSelection( open, close ) + call s:WrapSelection( a:open, a:close ) + " Always leave the cursor to the opening char's pos after + " wrapping selection. + if getline('.')[col('.')-1] =~ b:any_closing_char + normal! % + endif +endfunction + +" Wrap current symbol in parens of the given kind +" If standing on a paren then wrap the whole s-expression +" Stand on the opening paren (if not wrapping in "") +function! PareditWrap( open, close ) + let isk_save = s:SetKeyword() + let sel_save = &selection + let line = line('.') + let column = col('.') + let line_content = getline(line) + let current_char = line_content[column - 1] + + if a:open != '"' && current_char =~ b:any_openclose_char + execute "normal! " . "v%\" + else + let inside_comment = s:InsideComment(line, column) + + if current_char == '"' && !inside_comment + let escaped_quote = line_content[column - 2] == "\\" + if escaped_quote + execute "normal! " . "vh\" + else + let is_starting_quote = 1 + if column == 1 && line > 1 + let endOfPreviousLine = col([line - 1, '$']) + if s:InsideString(line - 1, endOfPreviousLine - 1) + let previous_line_content = getline(line - 1) + if previous_line_content[endOfPreviousLine - 2] != '"' + let is_starting_quote = 0 + elseif previous_line_content[endOfPreviousLine - 3] == "\\" + let is_starting_quote = 0 + endif + endif + elseif s:InsideString(line, column - 1) + if line_content[column - 2] != '"' + let is_starting_quote = 0 + elseif line_content[column - 3] == "\\" + let is_starting_quote = 0 + endif + endif + let &selection="inclusive" + normal! v + if is_starting_quote + call search( '\\\@" + endif + else + execute "normal! " . "viw\" + endif + endif + call s:WrapSelection( a:open, a:close ) + if a:open != '"' + normal! % + else + call cursor(line, column + 1) + endif + let &selection = sel_save + let &iskeyword = isk_save +endfunction + +" Splice current list into the containing list +function! PareditSplice() + if !g:paredit_mode + return + endif + + " First find which kind of paren is the innermost + let [p, l, c] = s:FindClosing() + if p !~ b:any_closing_char + " Not found any kind of parens + return + endif + + call setpos( '.', [0, l, c, 0] ) + normal! % + let l = line( '.' ) + let c = col( '.' ) + normal! %x + call setpos( '.', [0, l, c, 0] ) + normal! x + if c > 1 && getline('.')[c-2] =~ s:any_macro_prefix + normal! X + endif +endfunction + +" Raise: replace containing form with the current symbol or sub-form +function! PareditRaise() + let isk_save = s:SetKeyword() + let ch = getline('.')[col('.')-1] + if ch =~ b:any_openclose_char + " Jump to the closing char in order to find the outer + " closing char. + if ch =~ b:any_opening_char + normal! % + endif + + let [p, l, c] = s:FindClosing() + if p =~ b:any_closing_char + " Raise sub-form and re-indent + exe "normal! y%d%da" . p + if getline('.')[col('.')-1] == ' ' + normal! "0p=% + else + normal! "0P=% + endif + elseif ch =~ b:any_opening_char + " Restore position if there is no appropriate + " closing char. + normal! % + endif + else + let [p, l, c] = s:FindClosing() + if p =~ b:any_closing_char + " Raise symbol + exe "normal! yiwda" . p + normal! "0Pb + endif + endif + let &iskeyword = isk_save +endfunction + +" ===================================================================== +" Autocommands +" ===================================================================== + +if !exists("g:paredit_disable_lisp") + au FileType lisp call PareditInitBuffer() +endif + +if !exists("g:paredit_disable_clojure") + au FileType *clojure* call PareditInitBuffer() +endif + +if !exists("g:paredit_disable_hy") + au FileType hy call PareditInitBuffer() +endif + +if !exists("g:paredit_disable_scheme") + au FileType scheme call PareditInitBuffer() + au FileType racket call PareditInitBuffer() +endif + +if !exists("g:paredit_disable_shen") + au FileType shen call PareditInitBuffer() +endif diff --git a/tslime/tslime.vim b/tslime/tslime.vim new file mode 100644 index 0000000..77bbeee --- /dev/null +++ b/tslime/tslime.vim @@ -0,0 +1,58 @@ +function! Send_to_Tmux(text) + if !exists("b:tmux_sessionname") || !exists("b:tmux_windowname") + if exists("g:tmux_sessionname") && exists("g:tmux_windowname") + let b:tmux_sessionname = g:tmux_sessionname + let b:tmux_windowname = g:tmux_windowname + if exists("g:tmux_panenumber") + let b:tmux_panenumber = g:tmux_panenumber + end + else + call Tmux_Vars() + end + end + + let target = b:tmux_sessionname . ":" . b:tmux_windowname + + if exists("b:tmux_panenumber") + let target .= "." . b:tmux_panenumber + end + + call system("tmux set-buffer -t " . b:tmux_sessionname . " '" . substitute(a:text, "'", "'\\\\''", 'g') . "'" ) + call system("tmux paste-buffer -t " . target) +endfunction + +function! Tmux_Session_Names(A,L,P) + return system("tmux list-sessions | sed -e 's/:.*$//'") +endfunction + +function! Tmux_Window_Names(A,L,P) + return system("tmux list-windows -t" . b:tmux_sessionname . ' | grep -e "^\w:" | sed -e "s/ \[[0-9x]*\]$//"') +endfunction + +function! Tmux_Pane_Numbers(A,L,P) + return system("tmux list-panes -t " . b:tmux_sessionname . ":" . b:tmux_windowname . " | sed -e 's/:.*$//'") +endfunction + +function! Tmux_Vars() + let b:tmux_sessionname = input("session name: ", "", "custom,Tmux_Session_Names") + let b:tmux_windowname = substitute(input("window name: ", "", "custom,Tmux_Window_Names"), ":.*$" , '', 'g') + + if system("tmux list-panes -t " . b:tmux_sessionname . ":" . b:tmux_windowname . " | wc -l") > 1 + let b:tmux_panenumber = input("pane number: ", "", "custom,Tmux_Pane_Numbers") + end + + if !exists("g:tmux_sessionname") || !exists("g:tmux_windowname") + let g:tmux_sessionname = b:tmux_sessionname + let g:tmux_windowname = b:tmux_windowname + if exists("b:tmux_panenumber") + let g:tmux_panenumber = b:tmux_panenumber + end + end +endfunction + +"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +vmap "ry :call Send_to_Tmux(@r) +nmap vip + +nmap v :call Tmux_Vars() diff --git a/txtfmt/doc/tags b/txtfmt/doc/tags new file mode 100644 index 0000000..b2493ac --- /dev/null +++ b/txtfmt/doc/tags @@ -0,0 +1,248 @@ +'short'/'long'-formats txtfmt.txt /*'short'\/'long'-formats* + txtfmt.txt /** +[a txtfmt.txt /*[a* +[ba txtfmt.txt /*[ba* +[bc txtfmt.txt /*[bc* +[bf txtfmt.txt /*[bf* +[bk txtfmt.txt /*[bk* +[c txtfmt.txt /*[c* +[ea txtfmt.txt /*[ea* +[ec txtfmt.txt /*[ec* +[ef txtfmt.txt /*[ef* +[ek txtfmt.txt /*[ek* +[f txtfmt.txt /*[f* +[k txtfmt.txt /*[k* +[ta txtfmt.txt /*[ta* +[tba txtfmt.txt /*[tba* +[tbc txtfmt.txt /*[tbc* +[tbf txtfmt.txt /*[tbf* +[tbk txtfmt.txt /*[tbk* +[tc txtfmt.txt /*[tc* +[tea txtfmt.txt /*[tea* +[tec txtfmt.txt /*[tec* +[tef txtfmt.txt /*[tef* +[tek txtfmt.txt /*[tek* +[tf txtfmt.txt /*[tf* +[tk txtfmt.txt /*[tk* +\A txtfmt.txt /*\\A* +\I txtfmt.txt /*\\I* +\O txtfmt.txt /*\\O* +\a txtfmt.txt /*\\a* +\ga txtfmt.txt /*\\ga* +\i txtfmt.txt /*\\i* +\o txtfmt.txt /*\\o* +\s txtfmt.txt /*\\s* +\vA txtfmt.txt /*\\vA* +\vI txtfmt.txt /*\\vI* +\vO txtfmt.txt /*\\vO* +\va txtfmt.txt /*\\va* +\vi txtfmt.txt /*\\vi* +\vo txtfmt.txt /*\\vo* +\vs txtfmt.txt /*\\vs* +]a txtfmt.txt /*]a* +]ba txtfmt.txt /*]ba* +]bc txtfmt.txt /*]bc* +]bf txtfmt.txt /*]bf* +]bk txtfmt.txt /*]bk* +]c txtfmt.txt /*]c* +]ea txtfmt.txt /*]ea* +]ec txtfmt.txt /*]ec* +]ef txtfmt.txt /*]ef* +]ek txtfmt.txt /*]ek* +]f txtfmt.txt /*]f* +]k txtfmt.txt /*]k* +]ta txtfmt.txt /*]ta* +]tba txtfmt.txt /*]tba* +]tbc txtfmt.txt /*]tbc* +]tbf txtfmt.txt /*]tbf* +]tbk txtfmt.txt /*]tbk* +]tc txtfmt.txt /*]tc* +]tea txtfmt.txt /*]tea* +]tec txtfmt.txt /*]tec* +]tef txtfmt.txt /*]tef* +]tek txtfmt.txt /*]tek* +]tf txtfmt.txt /*]tf* +]tk txtfmt.txt /*]tk* +indent_patch.txt txtfmt.txt /*indent_patch.txt* +txtfmt txtfmt.txt /*txtfmt* +txtfmt-'bcm' txtfmt.txt /*txtfmt-'bcm'* +txtfmt-'bgcolormask' txtfmt.txt /*txtfmt-'bgcolormask'* +txtfmt-'cncl' txtfmt.txt /*txtfmt-'cncl'* +txtfmt-'cocu' txtfmt.txt /*txtfmt-'cocu'* +txtfmt-'conceal' txtfmt.txt /*txtfmt-'conceal'* +txtfmt-'concealcursor' txtfmt.txt /*txtfmt-'concealcursor'* +txtfmt-'esc' txtfmt.txt /*txtfmt-'esc'* +txtfmt-'escape' txtfmt.txt /*txtfmt-'escape'* +txtfmt-'fcm' txtfmt.txt /*txtfmt-'fcm'* +txtfmt-'fgcolormask' txtfmt.txt /*txtfmt-'fgcolormask'* +txtfmt-'nested' txtfmt.txt /*txtfmt-'nested'* +txtfmt-'nst' txtfmt.txt /*txtfmt-'nst'* +txtfmt-'pack' txtfmt.txt /*txtfmt-'pack'* +txtfmt-'pck' txtfmt.txt /*txtfmt-'pck'* +txtfmt-'rng' txtfmt.txt /*txtfmt-'rng'* +txtfmt-'sync' txtfmt.txt /*txtfmt-'sync'* +txtfmt-'tokrange' txtfmt.txt /*txtfmt-'tokrange'* +txtfmt-'tokrange'-expr txtfmt.txt /*txtfmt-'tokrange'-expr* +txtfmt-'uc' txtfmt.txt /*txtfmt-'uc'* +txtfmt-'undercurl' txtfmt.txt /*txtfmt-'undercurl'* +txtfmt-:GetTokInfo txtfmt.txt /*txtfmt-:GetTokInfo* +txtfmt-:MakeTestPage txtfmt.txt /*txtfmt-:MakeTestPage* +txtfmt-:MoveStartTok txtfmt.txt /*txtfmt-:MoveStartTok* +txtfmt-:Refresh txtfmt.txt /*txtfmt-:Refresh* +txtfmt-:ShowTokenMap txtfmt.txt /*txtfmt-:ShowTokenMap* +txtfmt-GetTokInfo() txtfmt.txt /*txtfmt-GetTokInfo()* +txtfmt-GetTokStr() txtfmt.txt /*txtfmt-GetTokStr()* +txtfmt-bck-till-any-beg-tok txtfmt.txt /*txtfmt-bck-till-any-beg-tok* +txtfmt-bck-till-any-end-tok txtfmt.txt /*txtfmt-bck-till-any-end-tok* +txtfmt-bck-till-any-tok txtfmt.txt /*txtfmt-bck-till-any-tok* +txtfmt-bck-till-bgc-beg-tok txtfmt.txt /*txtfmt-bck-till-bgc-beg-tok* +txtfmt-bck-till-bgc-end-tok txtfmt.txt /*txtfmt-bck-till-bgc-end-tok* +txtfmt-bck-till-bgc-tok txtfmt.txt /*txtfmt-bck-till-bgc-tok* +txtfmt-bck-till-clr-beg-tok txtfmt.txt /*txtfmt-bck-till-clr-beg-tok* +txtfmt-bck-till-clr-end-tok txtfmt.txt /*txtfmt-bck-till-clr-end-tok* +txtfmt-bck-till-clr-tok txtfmt.txt /*txtfmt-bck-till-clr-tok* +txtfmt-bck-till-fmt-beg-tok txtfmt.txt /*txtfmt-bck-till-fmt-beg-tok* +txtfmt-bck-till-fmt-end-tok txtfmt.txt /*txtfmt-bck-till-fmt-end-tok* +txtfmt-bck-till-fmt-tok txtfmt.txt /*txtfmt-bck-till-fmt-tok* +txtfmt-bck-to-any-beg-tok txtfmt.txt /*txtfmt-bck-to-any-beg-tok* +txtfmt-bck-to-any-end-tok txtfmt.txt /*txtfmt-bck-to-any-end-tok* +txtfmt-bck-to-any-tok txtfmt.txt /*txtfmt-bck-to-any-tok* +txtfmt-bck-to-bgc-beg-tok txtfmt.txt /*txtfmt-bck-to-bgc-beg-tok* +txtfmt-bck-to-bgc-end-tok txtfmt.txt /*txtfmt-bck-to-bgc-end-tok* +txtfmt-bck-to-bgc-tok txtfmt.txt /*txtfmt-bck-to-bgc-tok* +txtfmt-bck-to-clr-beg-tok txtfmt.txt /*txtfmt-bck-to-clr-beg-tok* +txtfmt-bck-to-clr-end-tok txtfmt.txt /*txtfmt-bck-to-clr-end-tok* +txtfmt-bck-to-clr-tok txtfmt.txt /*txtfmt-bck-to-clr-tok* +txtfmt-bck-to-fmt-beg-tok txtfmt.txt /*txtfmt-bck-to-fmt-beg-tok* +txtfmt-bck-to-fmt-end-tok txtfmt.txt /*txtfmt-bck-to-fmt-end-tok* +txtfmt-bck-to-fmt-tok txtfmt.txt /*txtfmt-bck-to-fmt-tok* +txtfmt-buflocal-user-map txtfmt.txt /*txtfmt-buflocal-user-map* +txtfmt-choosing-token-range txtfmt.txt /*txtfmt-choosing-token-range* +txtfmt-clr-spec txtfmt.txt /*txtfmt-clr-spec* +txtfmt-color-config txtfmt.txt /*txtfmt-color-config* +txtfmt-color-defaults txtfmt.txt /*txtfmt-color-defaults* +txtfmt-colorschemes txtfmt.txt /*txtfmt-colorschemes* +txtfmt-combining txtfmt.txt /*txtfmt-combining* +txtfmt-conceal-backwards-compatibility txtfmt.txt /*txtfmt-conceal-backwards-compatibility* +txtfmt-conceal-history txtfmt.txt /*txtfmt-conceal-history* +txtfmt-conceal-notes txtfmt.txt /*txtfmt-conceal-notes* +txtfmt-configuration txtfmt.txt /*txtfmt-configuration* +txtfmt-contents txtfmt.txt /*txtfmt-contents* +txtfmt-create-tok-str txtfmt.txt /*txtfmt-create-tok-str* +txtfmt-cterm-default-background txtfmt.txt /*txtfmt-cterm-default-background* +txtfmt-cterm-ignore-issue txtfmt.txt /*txtfmt-cterm-ignore-issue* +txtfmt-cterm-ignore-workaround txtfmt.txt /*txtfmt-cterm-ignore-workaround* +txtfmt-cterm-pitfalls txtfmt.txt /*txtfmt-cterm-pitfalls* +txtfmt-cterm-term-issue txtfmt.txt /*txtfmt-cterm-term-issue* +txtfmt-default-tokrange txtfmt.txt /*txtfmt-default-tokrange* +txtfmt-dist-files txtfmt.txt /*txtfmt-dist-files* +txtfmt-encoding txtfmt.txt /*txtfmt-encoding* +txtfmt-filetype txtfmt.txt /*txtfmt-filetype* +txtfmt-fmt-clr-spec-list txtfmt.txt /*txtfmt-fmt-clr-spec-list* +txtfmt-fmt-spec txtfmt.txt /*txtfmt-fmt-spec* +txtfmt-formats txtfmt.txt /*txtfmt-formats* +txtfmt-fwd-till-any-beg-tok txtfmt.txt /*txtfmt-fwd-till-any-beg-tok* +txtfmt-fwd-till-any-end-tok txtfmt.txt /*txtfmt-fwd-till-any-end-tok* +txtfmt-fwd-till-any-tok txtfmt.txt /*txtfmt-fwd-till-any-tok* +txtfmt-fwd-till-bgc-beg-tok txtfmt.txt /*txtfmt-fwd-till-bgc-beg-tok* +txtfmt-fwd-till-bgc-end-tok txtfmt.txt /*txtfmt-fwd-till-bgc-end-tok* +txtfmt-fwd-till-bgc-tok txtfmt.txt /*txtfmt-fwd-till-bgc-tok* +txtfmt-fwd-till-clr-beg-tok txtfmt.txt /*txtfmt-fwd-till-clr-beg-tok* +txtfmt-fwd-till-clr-end-tok txtfmt.txt /*txtfmt-fwd-till-clr-end-tok* +txtfmt-fwd-till-clr-tok txtfmt.txt /*txtfmt-fwd-till-clr-tok* +txtfmt-fwd-till-fmt-beg-tok txtfmt.txt /*txtfmt-fwd-till-fmt-beg-tok* +txtfmt-fwd-till-fmt-end-tok txtfmt.txt /*txtfmt-fwd-till-fmt-end-tok* +txtfmt-fwd-till-fmt-tok txtfmt.txt /*txtfmt-fwd-till-fmt-tok* +txtfmt-fwd-to-any-beg-tok txtfmt.txt /*txtfmt-fwd-to-any-beg-tok* +txtfmt-fwd-to-any-end-tok txtfmt.txt /*txtfmt-fwd-to-any-end-tok* +txtfmt-fwd-to-any-tok txtfmt.txt /*txtfmt-fwd-to-any-tok* +txtfmt-fwd-to-bgc-beg-tok txtfmt.txt /*txtfmt-fwd-to-bgc-beg-tok* +txtfmt-fwd-to-bgc-end-tok txtfmt.txt /*txtfmt-fwd-to-bgc-end-tok* +txtfmt-fwd-to-bgc-tok txtfmt.txt /*txtfmt-fwd-to-bgc-tok* +txtfmt-fwd-to-clr-beg-tok txtfmt.txt /*txtfmt-fwd-to-clr-beg-tok* +txtfmt-fwd-to-clr-end-tok txtfmt.txt /*txtfmt-fwd-to-clr-end-tok* +txtfmt-fwd-to-clr-tok txtfmt.txt /*txtfmt-fwd-to-clr-tok* +txtfmt-fwd-to-fmt-beg-tok txtfmt.txt /*txtfmt-fwd-to-fmt-beg-tok* +txtfmt-fwd-to-fmt-end-tok txtfmt.txt /*txtfmt-fwd-to-fmt-end-tok* +txtfmt-fwd-to-fmt-tok txtfmt.txt /*txtfmt-fwd-to-fmt-tok* +txtfmt-get-tok-info txtfmt.txt /*txtfmt-get-tok-info* +txtfmt-gnome-terminal-issue txtfmt.txt /*txtfmt-gnome-terminal-issue* +txtfmt-gnome-terminal-issue-workaround txtfmt.txt /*txtfmt-gnome-terminal-issue-workaround* +txtfmt-hl-color-names txtfmt.txt /*txtfmt-hl-color-names* +txtfmt-ins-tok txtfmt.txt /*txtfmt-ins-tok* +txtfmt-ins-tok-A txtfmt.txt /*txtfmt-ins-tok-A* +txtfmt-ins-tok-CTRL-\_CTRL-\ txtfmt.txt /*txtfmt-ins-tok-CTRL-\\_CTRL-\\* +txtfmt-ins-tok-I txtfmt.txt /*txtfmt-ins-tok-I* +txtfmt-ins-tok-O txtfmt.txt /*txtfmt-ins-tok-O* +txtfmt-ins-tok-a txtfmt.txt /*txtfmt-ins-tok-a* +txtfmt-ins-tok-i txtfmt.txt /*txtfmt-ins-tok-i* +txtfmt-ins-tok-map-list txtfmt.txt /*txtfmt-ins-tok-map-list* +txtfmt-ins-tok-maps txtfmt.txt /*txtfmt-ins-tok-maps* +txtfmt-ins-tok-o txtfmt.txt /*txtfmt-ins-tok-o* +txtfmt-ins-tok-s txtfmt.txt /*txtfmt-ins-tok-s* +txtfmt-ins-tok-vA txtfmt.txt /*txtfmt-ins-tok-vA* +txtfmt-ins-tok-vI txtfmt.txt /*txtfmt-ins-tok-vI* +txtfmt-ins-tok-vO txtfmt.txt /*txtfmt-ins-tok-vO* +txtfmt-ins-tok-va txtfmt.txt /*txtfmt-ins-tok-va* +txtfmt-ins-tok-vi txtfmt.txt /*txtfmt-ins-tok-vi* +txtfmt-ins-tok-vo txtfmt.txt /*txtfmt-ins-tok-vo* +txtfmt-ins-tok-vs txtfmt.txt /*txtfmt-ins-tok-vs* +txtfmt-installation txtfmt.txt /*txtfmt-installation* +txtfmt-jump-to-tok txtfmt.txt /*txtfmt-jump-to-tok* +txtfmt-jump-to-tok-maps txtfmt.txt /*txtfmt-jump-to-tok-maps* +txtfmt-loading txtfmt.txt /*txtfmt-loading* +txtfmt-map-config txtfmt.txt /*txtfmt-map-config* +txtfmt-map-conflict txtfmt.txt /*txtfmt-map-conflict* +txtfmt-modeline txtfmt.txt /*txtfmt-modeline* +txtfmt-modeline-fmt txtfmt.txt /*txtfmt-modeline-fmt* +txtfmt-motivation txtfmt.txt /*txtfmt-motivation* +txtfmt-move-tok-range txtfmt.txt /*txtfmt-move-tok-range* +txtfmt-multibyte-syntax-bug txtfmt.txt /*txtfmt-multibyte-syntax-bug* +txtfmt-nesting txtfmt.txt /*txtfmt-nesting* +txtfmt-nesting-c-example txtfmt.txt /*txtfmt-nesting-c-example* +txtfmt-nesting-notes-example txtfmt.txt /*txtfmt-nesting-notes-example* +txtfmt-nesting-tvo-example txtfmt.txt /*txtfmt-nesting-tvo-example* +txtfmt-opt-list txtfmt.txt /*txtfmt-opt-list* +txtfmt-opt-names txtfmt.txt /*txtfmt-opt-names* +txtfmt-opt-num-fmt txtfmt.txt /*txtfmt-opt-num-fmt* +txtfmt-opt-refresh txtfmt.txt /*txtfmt-opt-refresh* +txtfmt-opt-types txtfmt.txt /*txtfmt-opt-types* +txtfmt-options txtfmt.txt /*txtfmt-options* +txtfmt-overview txtfmt.txt /*txtfmt-overview* +txtfmt-performance-considerations txtfmt.txt /*txtfmt-performance-considerations* +txtfmt-private-use-area txtfmt.txt /*txtfmt-private-use-area* +txtfmt-problematic-ranges txtfmt.txt /*txtfmt-problematic-ranges* +txtfmt-query-tok-type txtfmt.txt /*txtfmt-query-tok-type* +txtfmt-start-token txtfmt.txt /*txtfmt-start-token* +txtfmt-starttok txtfmt.txt /*txtfmt-starttok* +txtfmt-syn-rgn-count-table txtfmt.txt /*txtfmt-syn-rgn-count-table* +txtfmt-test-cur-settings txtfmt.txt /*txtfmt-test-cur-settings* +txtfmt-tok-descriptor txtfmt.txt /*txtfmt-tok-descriptor* +txtfmt-upgrading-to-extended-long-formats txtfmt.txt /*txtfmt-upgrading-to-extended-long-formats* +txtfmt-upgrading-tokrange txtfmt.txt /*txtfmt-upgrading-tokrange* +txtfmt-user-interface txtfmt.txt /*txtfmt-user-interface* +txtfmt-user-map-examples txtfmt.txt /*txtfmt-user-map-examples* +txtfmt-user-map-expansion txtfmt.txt /*txtfmt-user-map-expansion* +txtfmt-user-map-fmt txtfmt.txt /*txtfmt-user-map-fmt* +txtfmt-user-maps txtfmt.txt /*txtfmt-user-maps* +txtfmt-view-tok-map txtfmt.txt /*txtfmt-view-tok-map* +txtfmt-viewing-old-files-without-conceal txtfmt.txt /*txtfmt-viewing-old-files-without-conceal* +txtfmt.txt txtfmt.txt /*txtfmt.txt* +txtfmtAllowxl txtfmt.txt /*txtfmtAllowxl* +txtfmtBgcolormask txtfmt.txt /*txtfmtBgcolormask* +txtfmtBgcolor{} txtfmt.txt /*txtfmtBgcolor{}* +txtfmtColor{} txtfmt.txt /*txtfmtColor{}* +txtfmtConceal txtfmt.txt /*txtfmtConceal* +txtfmtConcealcursor txtfmt.txt /*txtfmtConcealcursor* +txtfmtEscape txtfmt.txt /*txtfmtEscape* +txtfmtFgcolormask txtfmt.txt /*txtfmtFgcolormask* +txtfmtJumptoinactive txtfmt.txt /*txtfmtJumptoinactive* +txtfmtMapwarn txtfmt.txt /*txtfmtMapwarn* +txtfmtModelines txtfmt.txt /*txtfmtModelines* +txtfmtNested txtfmt.txt /*txtfmtNested* +txtfmtPack txtfmt.txt /*txtfmtPack* +txtfmtSync txtfmt.txt /*txtfmtSync* +txtfmtTokrange txtfmt.txt /*txtfmtTokrange* +txtfmtUndercurl txtfmt.txt /*txtfmtUndercurl* +txtfmtUsermaplimit txtfmt.txt /*txtfmtUsermaplimit* +txtfmtUsermap{} txtfmt.txt /*txtfmtUsermap{}* diff --git a/txtfmt/doc/txtfmt.txt b/txtfmt/doc/txtfmt.txt new file mode 100755 index 0000000..8519b6d --- /dev/null +++ b/txtfmt/doc/txtfmt.txt @@ -0,0 +1,2908 @@ +*txtfmt.txt* Syntax/Filetype plugin for formatting plain text + +Author: Brett Pershing Stahlman +Version: 2.4 + +============================================================================== +1. Contents *txtfmt* *txtfmt-contents* + + 1. Contents....................................: |txtfmt-contents| + 2. Motivation..................................: |txtfmt-motivation| + 3. Overview....................................: |txtfmt-overview| + 4. Installation................................: |txtfmt-installation| + Distributed files...........................: |txtfmt-dist-files| + Detecting/setting Txtfmt filetype...........: |txtfmt-filetype| + Combining with other filetypes..............: |txtfmt-combining| + Nesting Txtfmt regions....................: |txtfmt-nesting| + Using Txtfmt within C comments..........: |txtfmt-nesting-c-example| + Using Txtfmt within .otl (TVO) files....: |txtfmt-nesting-tvo-example| + Using Txtfmt within 'Notes' plugin......: |txtfmt-nesting-notes-example| + Loading.....................................: |txtfmt-loading| + 5. Configuration...............................: |txtfmt-configuration| + Options.....................................: |txtfmt-options| + Option types..............................: |txtfmt-opt-types| + Option naming convention..................: |txtfmt-opt-names| + Number format.............................: |txtfmt-opt-num-fmt| + Txtfmt modelines..........................: |txtfmt-modeline| + Txtfmt modeline format..................: |txtfmt-modeline-fmt| + Refreshing display after option changes...: |txtfmt-opt-refresh| + :Refresh command........................: |txtfmt-:Refresh| + Option list...............................: |txtfmt-opt-list| + Txtfmt "conceal" feature considerations.....: |txtfmt-conceal-notes| + Choosing a token range......................: |txtfmt-choosing-token-range| + Colors......................................: |txtfmt-color-config| + Color defaults............................: |txtfmt-color-defaults| + Colorscheme considerations................: |txtfmt-colorschemes| + Achieving "colorscheme-independence"....: |txtfmt-hl-color-names| + Color terminal pitfalls.....................: |txtfmt-cterm-pitfalls| + Customizing the default maps................: |txtfmt-map-config| + Handling conflicts and ambiguities........: |txtfmt-map-conflict| + 6. User interface..............................: |txtfmt-user-interface| + Inserting tokens............................: |txtfmt-ins-tok| + Fmt spec..................................: |txtfmt-fmt-spec| + Clr spec..................................: |txtfmt-clr-spec| + Fmt/clr spec list.........................: |txtfmt-fmt-clr-spec-list| + Mapping overview..........................: |txtfmt-ins-tok-maps| + Mappings..................................: |txtfmt-ins-tok-map-list| + Jumping to tokens...........................: |txtfmt-jump-to-tok| + Mappings..................................: |txtfmt-jump-to-tok-maps| + Building complex mappings from primitives...: |txtfmt-user-maps| + txtfmtUsermap{} element format............: |txtfmt-user-map-fmt| + User-map expansion macros.................: |txtfmt-user-map-expansion| + User-map examples.........................: |txtfmt-user-map-examples| + Using buf-local sets......................: |txtfmt-buflocal-user-map| + Creating token strings......................: |txtfmt-create-tok-str|| + Txtfmt_GetTokStr()........................: |txtfmt-GetTokStr()| + Querying token type.........................: |txtfmt-query-tok-type| + Format of token description string........: |txtfmt-tok-descriptor| + \ga.......................................: |txtfmt-get-tok-info| + :GetTokInfo...............................: |txtfmt-:GetTokInfo| + Txtfmt_GetTokInfo()....!!!DEPRECATED!!!...: |txtfmt-GetTokInfo()| + Viewing token map...........................: |txtfmt-view-tok-map| + :ShowTokenMap.............................: |txtfmt-:ShowTokenMap| + Testing current settings....................: |txtfmt-test-cur-settings| + :MakeTestPage.............................: |txtfmt-:MakeTestPage| + Moving the current token range..............: |txtfmt-move-tok-range| + :MoveStartTok.............................: |txtfmt-:MoveStartTok| + 7. File structure..............................: |txtfmt-files| + 8. Detailed theory of operation................: |txtfmt-detailed-theory| + +============================================================================== +2. Motivation *txtfmt-motivation* + +In addition to using Vim to edit C code, I sometimes use it to write notes of +various kinds. Sometimes these notes are temporary in nature, and sometimes +they are meant to be more permanent; for example, a document describing +various subsystems in a large programming project. For a task of this nature, +Vim offers many advantages over a typical word processor. Features like word +completion, multiple text registers, tags, cscope, etc. are very useful when +documenting source code. However, there was always one advantage that a word +processor had when it came to creating non source code documents: formatted +text. The ability to set certain regions of text to an arbitrary foreground or +background color, or to make them stand out with attributes such as bold, +italic and underline can make a document much more readable, and hence, more +useful. + +I realize that there are a number of very powerful formatting languages and +utilities, which may be used to create such documents. What I wanted, however, +was something very lightweight, something that depended only upon Vim. In +other words, I didn't want to create a file, which could be operated upon by a +tool, to create a file for viewing. I wanted only 1 file, the one I edited +with Vim, which could be viewed "on-the-fly," as each editing change was made. +The Txtfmt plugin is my solution for creating formatted documents with Vim, +to be viewed within Vim. + +============================================================================== +3. Overview *txtfmt-overview* + +Txtfmt's formatting is accomplished through the use of single-character +"tokens" (chosen from ranges not normally used in text files), which are used +to define highlight attributes for subsequent text. The tokens themselves are +concealed by Txtfmt's syntax file, so that each appears as a single space (or +not at all if your version of Vim was compiled with "conceal" support - see +|txtfmt-'conceal'|) +Note: See section |txtfmt-cterm-ignore-issue| if you notice that tokens are +not hidden on your system. + +Txtfmt requires 9 of the aforementioned tokens for foreground color regions, +and an additional 9 tokens if background colors are enabled (see +txtfmt-'tokrange' ). The |txtfmt-'tokrange'| option setting also determines +the number of tokens required for format regions. If the tokrange suffix is +'S' (short formats) or 'X' (extended), then the only formatting attributes in +use are bold, underline and italic. All possible permutations of these +attributes may be represented with only 8 tokens. If, on the other hand, the +tokrange suffix is 'L' (long formats), the set of available format attributes +expands to include standout, reverse and (for Vim versions >= 7.0) undercurl. +In this case, Txtfmt will require either 32 or 64 format tokens, depending +upon whether the version of Vim supports undercurl. + +There are 3 types of Txtfmt marker token: format, foreground color and +background color. The 3 types of regions begun by these 3 types of tokens are +"orthogonal"; i.e., the presence of one has no effect upon any others that +happen to be active at that point in the buffer. For example, suppose you have +begun a green color region by inserting the appropriate token (with one of +Txtfmt's mappings, to be discussed later). You now wish to emphasize a word or +phrase by formatting it in italics, but you do not wish to change the color. +In this case, you would simply insert the format token denoting "italic", and +subsequently typed text would be green italic. If you later wished to switch +back to un-italicized (normal) green text, you would simply insert the format +token corresponding to "no format". Note that format and color tokens may be +freely interspersed with one another in any order. + +The tokens described above are simply characters belonging to the character +set in use (e.g. latin1, utf-8, etc...), so it would be theoretically possible +to accomplish the highlighting with a syntax file alone, by simply inserting +the marker characters with CTRL-V or something similar. However, this would +require you to memorize the character codes corresponding to 8 colors and all +combinations of underline, bold, italic, etc... Thus, in addition to a syntax +file, Txtfmt provides a filetype plugin (ftplugin), which provides mappings +and functions to facilitate working with the format/color tokens. Inserting +tokens, finding (jumping to) tokens, and querying token type, are a few of the +operations that can be performed with mappings and/or functions defined in the +ftplugin. + +============================================================================== +4. Installation *txtfmt-installation* + +DISTRIBUTED FILES *txtfmt-dist-files* + +The Txtfmt distribution comprises the following 4 files: + +doc/txtfmt.txt Txtfmt help file (this file) +ftplugin/txtfmt.vim Txtfmt filetype plugin (contains mappings for working + with Txtfmt tokens) +syntax/txtfmt.vim Txtfmt syntax file (defines the Txtfmt syntax + highlighting) +plugin/txtfmt.vim Txtfmt common configuration code (contains + configuration script needed by both the filetype and + syntax plugins) +indent_patch.txt Bram Moolenaar's patch to fix indent.vim's handling of + dot-separated lists in 'filetype' option values + (required only if your version of Vim is older than + 7.2 and you plan to "nest" Txtfmt within other + filetypes) + For details, see |txtfmt-combining|. + +All of the txtfmt.vim files should be placed in their respective parent +directories, somewhere in your 'runtimepath'. If you need the patch files, you +may place them wherever you like. + +IMPORTANT NOTE: To make Txtfmt help topics visible to Vim's :help command, you +must run :helptags, passing the path to the doc folder where you unpacked +txtfmt.txt as an argument. + +DETECTING OR SETTING TXTFMT FILETYPE *txtfmt-filetype* + +Once you have copied the Txtfmt files to the appropriate directories in +'runtimepath', you can always use the Txtfmt syntax and mappings by manually +setting the 'filetype' option. (I am assuming, of course, that you have +enabled syntax at some point, most likely in your .vimrc. If you are not sure +about this, see |syntax-enable|.) The 'filetype' option may be set on the +command line: e.g. > + :set ft=txtfmt + +A better approach for a file designed to be viewed with Txtfmt highlighting +would be to set the filetype in a Vim modeline: e.g. > + vim:ft=txtfmt + +While the manual approach may be appropriate for some uses, if you find +yourself always using Txtfmt with certain types of files, it probably makes +sense to set up automatic detection via one of the methods discussed in the +section |new-filetype| in the Vim help. The simplest approach is to create +your own filetype.vim (assuming you haven't done so already) in a directory +that comes before $VIMRUNTIME in 'runtimepath'. Within this script, you should +define an autocommand that will :setfiletype to txtfmt for any file with an +appropriate extension. + +Example: > + " Set up detection for Txtfmt files + au! filetypedetect BufRead,BufNewFile *.txt setfiletype txtfmt + +COMBINING TXTFMT WITH OTHER FILETYPES *txtfmt-combining* + +It is possible to use the Txtfmt plugins with the plugins of other filetypes. +This feature is useful when you wish to take advantage of Txtfmt's arbitrary +formatting capabilities in a file that would otherwise support only static +formatting. The simplest way to accomplish this is to set 'filetype' to a +dot-separated list of filetype names: e.g. "c.txtfmt". When you do this, Vim +will load the corresponding filetype and syntax files in the order in which +they appear in the list. + +WARNING: Txtfmt's filetype plugin is designed to be used in this way; some of +the standard plugins, however, will skip loading if a plugin that sets +b:did_ftplugin has already been sourced. Thus, it is best to put Txtfmt last +in the list of filetypes. + +The method just described is useful when you wish to use Txtfmt in conjunction +with an existing filetype. It is possible, however, that you simply wish to +extend Txtfmt by adding a few predefined formatting regions. As an example, +suppose you find yourself highlighting things like "TODO" and "IMPORTANT NOTE" +quite often in your Txtfmt files. You can always highlight them manually by +using Txtfmt markers; it would be more efficient, however, to define syntax +highlighting that handles these special cases automatically. You could do this +in a special Txtfmt syntax file, which you create in an "after" directory in +your 'runtimepath'. (See |after-directory|.) + +Nesting Txtfmt regions *txtfmt-nesting* + +Whether you are combining Txtfmt with an existing filetype, or simply +extending Txtfmt's highlighting with predefined regions of your own, it is +possible that you will want to use Txtfmt's highlighting within non-txtfmt +syntax regions. If, for example, you are using Txtfmt in combination with C +syntax, you may wish to create Txtfmt regions within C comments. This is +possible, but only if you have set the |txtfmt-'nested'| option. If you are +nesting Txtfmt regions within non-Txtfmt regions, you should generally +terminate the Txtfmt regions explicitly, rather than relying upon the +containing region to terminate it. A containing region without a "keepend" +argument will not be able to terminate the Txtfmt region. A containing region +with "keepend" will terminate the Txtfmt region, but the containing region's +end marker may not be formatted correctly unless a "matchgroup" was used to +define it. + +A few practical examples follow... + +C source file nesting example: *txtfmt-nesting-c-example* + +Here is how you could use Txtfmt to format your C-language comments... + +Put the following within your user "filetype.vim" +(e.g. $VIM/vimfiles/filetype.vim) > + " Allow Txtfmt regions to be nested within C comments + augroup filetypedetect + au! BufRead,BufNewFile *.c let b:txtfmtNested = 1 + au BufRead,BufNewFile *.c setf c.txtfmt + augroup END + +Now, you may use Txtfmt mappings to add color and formatting regions to your C +comments! (Technically, of course, you can format anything in the file, but +I'm assuming you want the source file to compile.) + + *indent_patch.txt* +IMPORTANT NOTE: Prior to Vim 7.2, the distributed version of "indent.vim" was +not aware of the dot-separated filetype name convention. Thus, if you're using +an older version of Vim, C-indenting will not work properly if you set +filetype to "c.txtfmt". Bram's patch for this issue is packaged along with the +Txtfmt files. If you have not yet upgraded to the latest version of Vim, and +you do not wish to apply the patch, you could work around the issue with an +alternative approach such as this: > + " Allow Txtfmt regions to be nested within C comments without losing + " cindent capability in pre-7.2 Vim. + augroup filetypedetect + " Set filetype to c + au! BufRead,BufNewFile *.c setf c + " Make sure Txtfmt regions can nest within comments + au BufRead,BufNewFile *.c let b:txtfmtNested = 1 + " Manually source the Txtfmt filetype and syntax plugin files... + au BufRead,BufNewFile *.c runtime syntax/txtfmt.vim + au BufRead,BufNewFile *.c runtime ftplugin/txtfmt.vim + augroup END + +Of course, it's probably easier just to upgrade Vim;-) + +TVO nesting example: *txtfmt-nesting-tvo-example* + +Here is how you could use Txtfmt to format the ordinary text within .otl files +created with TVO (The Vim Outliner plugin written by Ned Konz)... + +Put the following within the ftdetect/otl.vim file distributed with the TVO +plugin: > + " This sets up *.otl files to be outlined + au BufRead,BufNewFile *.otl setf otl.txtfmt + +Important Note: Because the TVO plugin was not designed to work with the +"dot-separated" filetype syntax, it is important that your .otl files DO NOT +set 'filetype' within a Vim modeline; i.e., the set of 'filetype' to +otl.txtfmt should occur exclusively within the autocommand as shown above. + +'Notes' nesting example: *txtfmt-nesting-notes-example* + +Here is how you could use Txtfmt to format the ordinary text within files +created with 'Notes' (the lightweight notes management plugin written by Hari +Krishna Dara)... + +In your .vimrc, set the g:notesFileType option as follows: > + let g:notesFileType = 'txtfmt' + +This instructs the 'Notes' plugin to set 'filetype' to txtfmt for all notes +files it creates. + +LOADING *txtfmt-loading* + +Note: This section is provided for informational purposes only; the average +user can safely skip. + +The Txtfmt plugin is actually several plugins working in concert: a syntax +plugin implementing token based highlighting, and a filetype plugin +facilitating working with the tokens. There are a significant number of +options permitting customization of various aspects of both plugins. Due to +the tightly integrated nature of the syntax and filetype plugins, most of the +options used to customize Txtfmt's behavior have implications for both +filetype and syntax plugins. Problems can result when the filetype and syntax +plugins see different values for the shared options. Since most Txtfmt options +are used only when the plugin is sourced, option values can get out of sync if +the filetype and syntax plugins are sourced at different times with +intervening option changes. The best protection against this is to have the +plugins loaded in the standard manner, via :setfiletype or assignment to +'filetype' option, as previously described. + +Just as many of the options are common to both filetype and syntax plugins, so +is much of the option processing and attendant configuration code. It would be +highly inefficient (and dangerous from a maintainability standpoint) to +maintain large blocks of identical Vim script in two different script files. +Accordingly, I have segregated the option/configuration processing, which is +common to both filetype and syntax, and located it in a separate file, which +is intended to be installed in the user's plugin directory: +($VIM/plugin/txtfmt.vim). Very little of the script in this file is actually +executed at Vim startup. Most of it is executed only upon demand by either the +filetype or syntax plugin (whichever happens to be sourced first when Txtfmt +is loaded). When the common configuration script is executed, it processes the +various options (|txtfmt-modeline|, buffer-local and global), and saves the +resulting configuration to buffer-local variables, where it may be accessed by +both the filetype and syntax plugins. + +Although the details will vary according to the user's setup, it generally +works something like this... + +-FileType set to txtfmt +--Txtfmt syntax file is sourced +---Txtfmt syntax file invokes common configuration code in + $VIM/plugin/txtfmt.vim +--Txtfmt filetype plugin is sourced + +============================================================================== +5. Configuration *txtfmt-configuration* + +OPTIONS *txtfmt-options* + +There are 3 ways for the Txtfmt user to set most Txtfmt options. The 3 methods +are listed below in order of highest to lowest priority. + + 1. in a Txtfmt modeline + 2. with a buffer-local option variable + 3. with a global option variable + +Note: Some options do not support all 3 mechanisms. The help for a specific +option indicates which mechanisms may be used to set it. + +Txtfmt will attempt to determine the value of an option from the highest +priority mechanism employed by the user. +Note: If the option value obtained in this manner is invalid, Txtfmt will not +consider lower-priority mechanisms. (In other words, the decision about which +user setting to use is made before the option value is validated.) + +Although different strategies may be conceived by the Txtfmt user, the +intended use of the 3 mechanisms is as follows: + +Txtfmt modelines + Used to associate options with a particular file +buffer-local + Used to set options automatically for a particular buffer or filetype. + Hint: Set buffer-local options in your local filetype.vim just before + calling :setfiletype +global + Used to set defaults for options not overridden by one of the + preceding 2 methods + +Buffer-local and global option variables are set just like any other such +variables in Vim. + +Examples: > + :let b:txtfmtEscape = 'bslash' + :let g:txtfmtNested = 1 + +For details on setting options via Txtfmt modelines, see |txtfmt-modeline|. + +Option naming convention *txtfmt-opt-names* + +Since the buffer-local and global Txtfmt options share a namespace with other +Vim global variables, the "txtfmt" prefix is prepended to these option names. +The option name itself is capitalized. Since a |txtfmt-modeline| is processed +only by Txtfmt, the "txtfmt" prefix is omitted from a |txtfmt-modeline| option +name. The modeline form of the option name is all lowercase, and just as with +Vim options, a short form of the option name is provided. In the option list +below, both the buflocal/global and modeline forms of each option are shown. + + +Option types *txtfmt-opt-types* + +Txtfmt options fall into one of three categories: + +1. String options +2. Number options +3. Boolean options + +In most cases, the rhs of the assignment used to set the option's value is +identical for all set mechanisms (txtfmt modelines, buffer-local and global), +except of course that string options are not quoted in a Txtfmt modeline. +Boolean options are a special case, however. Setting and clearing boolean +options in a Txtfmt modeline is accomplished in the same manner as it is for a +Vim modeline: by including the bare option name in the modeline to turn the +option on, or by prepending "no" to the bare option name to turn it off. + +Examples: > + " Txtfmt modeline that turns nesting ON + txtfmt:nested + " Txtfmt modeline that turns nesting OFF + txtfmt:nonested + +Number format *txtfmt-opt-num-fmt* + +Unless otherwise specified, a Txtfmt numeric option (represented with {number} +in this document) may be specified in either decimal or hexadecimal format: +e.g. + 0, 1000, 0x8f, 0x1F1F +are all valid values for numeric options. + +Txtfmt modelines *txtfmt-modeline* + +Since a Txtfmt file should generally be viewed with the same set of options +that were in effect when it was created, Txtfmt provides a way to associate +certain option values with the file itself. Vim users should be familiar with +the concept of a modeline. Txtfmt extends this concept to a "Txtfmt-modeline", +which may be used to set various Txtfmt-specific options, described later. As +a general rule, an option that may be set from a Txtfmt-modeline may also be +set buffer-locally and/or globally, using the corresponding buffer-local or +global option variable. +Note: Txtfmt will search the first and last |txtfmtModelines| lines of a file +looking for a Txtfmt-modeline. All valid Txtfmt-modelines found will be used; +however, the search for valid modelines stops as soon as an invalid modeline +is encountered. (This is consistent with Vim's modeline processing.) + +Format of a Txtfmt-modeline *txtfmt-modeline-fmt* + Txtfmt modelines are based upon the 1st Vim modeline format + [text]{white}txtfmt:[white]{options} + + where {options} is a comma or whitespace separated list of Txtfmt + option settings. For a description of the various {options}, see + |txtfmt-opt-list|. + + Note: Since Vim will not choke on a trailing ':' (probably because a + trailing ':' may be viewed as introducing an empty option field), + neither will I. + + Examples: > + txtfmt:tokrange=180X:escape=bslash + txtfmt:rng=0xE000L esc=self nested + +Refreshing display after option changes *txtfmt-opt-refresh* + +As mentioned in the section on "Loading" (|txtfmt-loading|), option values are +generally taken into account only at the instant the plugin using them is +sourced. Thus, changes to one of the Txtfmt options discussed below will have +no immediate effect upon a Txtfmt buffer that has already been loaded. If you +have changed option settings and wish to have those changes applied to a +loaded Txtfmt buffer, you have a choice of three methods: + + 1) :e[dit] + This is probably the simplest method. It relies upon a buffer-local + BufReadPre autocmd, whose job is to ensure that all options will be + re-processed when the filetype/syntax plugins are reloaded in response to + the BufRead event. + + *txtfmt-:Refresh* + 2) :Refresh + :Refresh is a buffer-local command, which may be invoked from any loaded + Txtfmt buffer. For most practical purposes, this method yields the same + results as ":e[dit]", although the implementation is significantly + different (and more complex). In particular, :Refresh does not actually + re-edit the file, so the various BufRead autocommands will not be + triggered. It works via assignments to 'filetype' and/or 'syntax', as + appropriate. + + 3) :bd[elete] (followed by re-edit of the file) + This is arguably the safest method, since a :bdelete unlets all + buffer-local variables, thus wiping out all "memory" of any previous + Txtfmt plugin load for the current buffer. Of course, you shouldn't use + this method if you don't want to lose other (non-Txtfmt) buffer-local + variables. + + +Option List *txtfmt-opt-list* + +The following is a comprehensive list of Txtfmt options. In the list below, +the option name as it would appear in a |txtfmt-modeline| is given within +single quotes (''). The variable name used for global or buffer-local +assignments is listed without the leading "g:" or "b:", but is accompanied by +a string such as "global" or "global or buffer-local" to indicate how it may +be set. + + *txtfmtColor{}* + *txtfmtBgcolor{}* +txtfmtColor{1..8} +txtfmtBgcolor{1..8} old-style array of strings + (default: See |txtfmt-color-defaults|) + global or buffer-local + These old-style Vim "arrays" allow you to override the default colors + corresponding to the 8 foreground and 8 background color tokens. It is + possible to override none, some, or all of the colors. The + txtfmtBgcolor{} array may be used to configure background colors + differently from foreground colors. The txtfmtColor{} array applies + not only to foreground colors, but also to background colors for which + the corresponding entry in txtfmtBgcolor{} has not been defined. + Txtfmt considers each color index independently of all others; thus, + it is possible to set only those elements whose default color you wish + to override. Note that for a foreground color, "default" in this + context refers to the default color definition provided by the Txtfmt + plugin; for a background color, it could also refer to the default + provided by the txtfmtColor{} array. In other words, you can provide + overrides for both foreground and background colors in txtfmtColor{}, + then use txtfmtBgcolor{} to override only the subset of background + colors you wish to differ from their corresponding foreground colors. + For details on the format of the strings, see |txtfmt-color-config|. + + *txtfmt-'conceal'* *txtfmt-'cncl'* *txtfmtConceal* +'conceal' 'cncl' +txtfmtConceal boolean (default on when has('conceal') == 1) + global or buffer-local + *** New in version 2.0 *** + This option determines whether Txtfmt will attempt to use the + concealment capability provided either by the "conceal" feature (Vim + versions >= 7.3) or by Vince Negri's "conceal / ownsyntax" patch + (available for older versions of Vim). Note that this option is + ignored when has("conceal") returns 0. + Note: You can check to see whether your version of Vim supports the + "conceal" feature either by executing has('conceal'), or by searching + the output of :version for the applicable feature string: + +conceal (enabled) + -conceal (disabled) + + The ideal Txtfmt token is zero-width, affecting the highlighting of + subsequent text without taking up any visible space in the Txtfmt + buffer. Such "zero-width" Txtfmt tokens are the default in versions of + Vim >= 7.3. In older versions of Vim, zero-width tokens were possible + only for users who built a custom version of Vim with Vince Negri's + "conceal / ownsyntax" patch. When the "conceal" feature is enabled, + Txtfmt will attempt to conceal completely all tokens not in the cursor + line. (Whether or not tokens in the cursor line are concealed depends + upon the |txtfmt-'concealcursor'| option setting.) + + When the "conceal" feature is disabled (either by this option or by + lack of support in Vim), Txtfmt attempts to conceal the special + formatting tokens by making each appear as a single space character, + each of which consumes a single column of screen width. Although this + is typically not an issue when the tokens can be placed at the end of + a line or on a line by themselves, it can be a source of annoyance in + certain scenarios. Consider, for example, the case in which you want + to highlight a single word in red bold. You would need to insert both + a red fg color and a bold format token prior to the word, and both the + 'no format' and 'no fg color' tokens immediately after the word. Since + each token appears as a single space, the highlighted word will appear + to be surrounded by more whitespace than usual. This becomes even more + of an issue in the unlikely event you wish to highlight only part of a + particular word. For this reason, Txtfmt will always use the "conceal" + feature whenever it is available. + + *txtfmt-conceal-notes* *txtfmt-conceal-history* + *txtfmt-conceal-backwards-compatibility* + Prior to Vim 7.3, the default setting of the |txtfmt-'conceal'| option + was "off", even when has("conceal") indicated that the feature was + enabled in Vim (i.e., because the user had applied Vince Negri's + "conceal" patch). This design choice was made, in spite of the fact + that use of the "conceal" feature is optimal, primarily for reasons of + backwards-compatibility. In particular, as annoying as non-zero-width + tokens might be, many users were sure to have created files that + relied upon the tokens' width to achieve proper indenting. A 4 space + leading indent, for instance, might have been realized with a bold + format token followed by 3 spaces. Suddenly concealing the bold format + token would break the alignment. + + Although this rationale is still valid, it is no longer sufficient (in + my judgment) to justify disabling the "conceal" feature by default, + now that the feature has been incorporated into Vim itself. First-time + Txtfmt users who begin using the plugin with Vim 7.3 will naturally + expect the plugin to take advantage of the "conceal" feature without + any explicit action on their part. Such users might not even be aware + of the existence of this option. Presenting Txtfmt in a sub-optimal + light (i.e., without "conceal" functionality) would increase the + probability that such users would abandon use of the plugin before + becoming sufficiently familiar with its documentation to learn that + they had the power to enable the "conceal" feature with a simple + option setting. + + *txtfmt-viewing-old-files-without-conceal* + If you have legacy files that are best viewed without the "conceal" + feature, but you're using a version of Vim with compiled-in support + for "conceal", you may wish to view the old files with "noconceal", + and view all newly-created Txtfmt files with "conceal". This is easily + accomplished as follows... + + 1. Set txtfmtConceal true globally, either by leaving the option at + its default, or setting it explicitly in your vimrc. E.g., + let g:txtfmtConceal = 1 + + 2. Use Txtfmt modelines in your old files to disable the "conceal" + feature for those files only. E.g., + txtfmt:noconceal + + Note: If you have many old files, you might wish to automate the + addition of the modelines with a simple sed or awk script: E.g., + + find . -iname '*.txtfmt' | xargs sed -i -e '$a\' -e 'txtfmt:noconceal' + + Explanation: Adds a Txtfmt-modeline disabling "conceal" on the last + line of all .txtfmt files recursively under the current directory. + Note: Txtfmt will process up to |txtfmtModelines| modelines at the + beginning or end of a file; thus, the command above simply adds a new + modeline without bothering to check for an existing one. + + Of course, the strategy outlined above is merely a suggestion. You can + use a combination of Txtfmt modeline and buffer-local / global option + settings to achieve whatever behavior you desire. + + *txtfmt-'concealcursor'* *txtfmt-'cocu'* *txtfmtConcealcursor* +'concealcursor' 'cocu' +txtfmtConcealcursor string (default: "nvic") + global or buffer-local + *** New in version 2.4 *** + Important Note: The default value of this option may change in a + future version of Txtfmt. If you care about its value, you should set + it explicitly. + + Note: This option is ignored when the "conceal" feature is disabled + (via the |txtfmt-'conceal'| option) or is not supported by the running + Vim instance. + + This option determines whether Txtfmt tokens in the line containing + the cursor are concealed. Its value is a string of single character + flags indicating the modes for which tokens in the cursor line should + be concealed. The flags are defined as follows in the Vim help: + + n Normal mode + v Visual mode + i Insert mode + c Command line editing, for 'incsearch' + + For detailed descriptions, see Vim help on 'concealcursor'... + + Note: Some new users might be disconcerted by the fact that with the + default setting of this option, the tokens disappear as soon as they + are entered. (After all, he might wish to delete them at some point.) + This will not be a concern at all once visual maps (targeted for next + release) have been added. These maps will automate addition/removal of + tokens completely, such that the user needn't even be aware of the + tokens' existence. In the meantime, however, the best way to locate + existing tokens does not require you to see them: it involves use of + the |txtfmt-jump-to-tok| maps, which facilitate locating Txtfmt tokens + by their types; additionally, the |\ga| map (|txtfmt-get-tok-info|) + may be used to learn the nature of the token under the cursor, even + when the token is completely concealed (i.e., occupying zero width). + + *txtfmt-'escape'* *txtfmt-'esc'* *txtfmtEscape* +'escape' 'esc' +txtfmtEscape string (default: "none") + global or buffer-local + This option determines how (if at all) the special format/color tokens + may be escaped in a Txtfmt buffer. Escaping would be necessary only in + the unlikely event that you needed to use the special tokens in a file + for a non-Txtfmt purpose. Hopefully, you have chosen a value for + |txtfmt-'tokrange'| that renders this unnecessary. If so, it is safe + to leave this option at its default value: "none". The following + values are possible: + bslash Escape a format/color token with backslash ('\') + self Escape a format/color token with itself (i.e., by + doubling the special chars) + none No escaping of format/color tokens + Note: Escaping characters themselves may be escaped, but only when + they appear in an unbroken sequence immediately preceding a Txtfmt + token. Examples for both the "bslash" and "self" cases are shown + below... + + --bslash-- + \ Escaped bold format token + \\ Backslash followed by bold format token + \\\ Backslash followed by escaped bold format token + --self-- + Escaped bold format token + Escaped bold format token followed by bold format token + Escaped bold format token followed by escaped bold + format token + Note: When a Txtfmt token is escaped, the escaping character will be + hidden, and the escaped character should be highlighted just like the + surrounding text. + + *txtfmt-'fgcolormask'* *txtfmt-'fcm'* *txtfmtFgcolormask* + *txtfmt-'bgcolormask'* *txtfmt-'bcm'* *txtfmtBgcolormask* +'fgcolormask' 'fcm' +txtfmtFgcolormask string (default: "11111111") + global or buffer-local + *** New in version 2.0 *** +'bgcolormask' 'bcm' +txtfmtBgcolormask string (default: "01101000") + global or buffer-local + *** New in version 2.0 *** + + These options permit you to deactivate colors you do not wish to view + in your Txtfmt buffer. Typically, colors are deactivated for + performance reasons. (See |txtfmt-performance-considerations|.) Each + option is a string containing exactly 8 characters. Each of the 8 + characters corresponds to one of the 8 possible colors in the + corresponding color definition array. (For information on color + definition arrays, see help on |txtfmtColor{}|.) The first character + in the string corresponds to the first element in the color array. The + meaning of the mask characters is as follows: + "1" activates the color + "0" deactivates the color + + Consider the bgcolormask default as an example: 01101000 + This mask enables the colors corresponding to indices 2, 3 and 5 in + the background color definition array. In the default color definition + arrays, these indices correspond to blue, green and red, respectively. + For performance reasons, I enable only these 3 most commonly used + background colors by default. Feel free to enable an entirely + different set of colors if you like. For backward-compatibility + reasons, the default fgcolormask enables all foreground colors. + However, if you notice there are foreground colors that you never use, + you can deactivate them to speed up the syntax redrawing. + + Note: Deactivating a color does not alter a Txtfmt buffer in any way. + The character code corresponding to the deactivated color continues to + be reserved within the Txtfmt token range; it simply has no effect on + highlighting. + + *txtfmtJumptoinactive* +txtfmtJumptoinactive boolean (default off) + global or buffer-local + This option determines whether the |txtfmt-jump-to-tok| maps will + treat an inactive (masked) fg or bg color token as the target of a + jump. By default, only tokens that affect the highlighting of the + Txtfmt buffer will be considered as potential jump targets. + Note: Currently, this option may be set at any time; i.e., a change + will take effect even after the plugin has loaded. This behavior is + subject to change. + + *txtfmtMapwarn* +txtfmtMapwarn string (default: "mMoOcC") + global + This option determines what the Txtfmt filetype plugin does when one + of its default 1st level maps is either ambiguous or conflicting with + an existing map. For an explanation of 1st and 2nd level maps, see + |txtfmt-map-config|. For a description of the character flags + comprising the option value, see |txtfmt-map-conflict|. + + *txtfmtModelines* +txtfmtModelines number (default: 5) + global or buffer-local + This option is analogous to Vim's 'modelines' option. It determines + how many (if any) lines at the beginning and end of a file will be + searched for a |txtfmt-modeline|. For (hopefully) obvious reasons, it + cannot be set from a txtfmt-modeline. + + *txtfmt-'nested'* *txtfmt-'nst'* *txtfmtNested* +'nested' 'nst' +txtfmtNested boolean (default on) + global or buffer-local + This option determines whether Txtfmt regions can be nested within + non-txtfmt regions. If you are not sure what this means, see + |txtfmt-nesting|. + + *txtfmt-'pack'* *txtfmt-'pck'* *txtfmtPack* +'pack' 'pck' +txtfmtPack boolean (default on) + global or buffer-local + *** New in version 2.0 *** + This option determines the offset within the Txtfmt token range where + the background color tokens are located. By default, the background + color tokens are "packed" up against the end of the format token + range. In other words, space within the range is not reserved for the + inactive "long" format tokens. Currently, there is little reason to + reserve space for inactive "long" format tokens: you cannot have both + "long" formats and background colors in the same buffer. Moreover, + there are backward-compatibility concerns that dictate the packing of + background color tokens. There are, however, several reasons you might + wish to leave space for "long" format tokens, even if you're not + currently using them. It is recommended that you read the following + section for details: |txtfmt-upgrading-to-extended-long-formats| + + Note: This option is meaningful only if the tokrange suffix is 'X' + (extended). + + *txtfmt-'sync'* *txtfmtSync* +'sync' +txtfmtSync string or number (default: 250) + global or buffer-local + This option determines the method used to perform syntax + synchronization on a Txtfmt buffer. (For an overview of syntax + synchronization, see |:syn-sync|.) + + Possible values: + fromstart Syncing always starts at the beginning of the Txtfmt + buffer. This is the most accurate, but least efficient + method. + none Syncing is not performed at all. This method is + extremely fast, but may produce incorrect highlighting + for Txtfmt regions that begin prior to the first + visible screen line. + {number} Works as though a "minlines={number}" argument has + been supplied to Vim's |:syn-sync| command. + (See |:syn-sync-minlines|for more information.) + + Set examples: > + let g:txtfmtSync = "fromstart" + let g:txtfmtSync = 500 + + " In a modeline... + txtfmt:sync=none + txtfmt:sync=100 +< + + *txtfmt-'tokrange'* *txtfmt-'rng'* *txtfmtTokrange* + *txtfmt-starttok* *txtfmt-formats* +'tokrange' 'rng' +txtfmtTokrange string or Vim expression evaluating to string + (default: + 1 byte encoding: "180X" + 2 byte encoding: "180X" + unicode: "0xE000X") + format: {number}[{formats}] + global or buffer-local + This option allows the user to specify both the location of the range + of characters dedicated to Txtfmt and the types of highlighting + regions that may be created: {number} specifies the character code of + the first character in the range; {formats}, if specified, is a single + letter indicating the types of regions supported. The following values + are supported: + 'S' ("short" formats) + 'L' ("long" formats) + 'X' ("extended") + All 3 modes suppport up to 8 foreground colors. Short format mode + supports only the bold, underline and italic format attributes, + whereas long formats adds to this basic set the standout and reverse + (inverse) attributes, and if the version of Vim in use supports it, + the undercurl attribute. Extended mode is equivalent to short formats + with the addition of up to 8 background colors. Note that for + performance reasons, you are currently not permitted to enable both + long formats and background colors. If you wish to know why (and + whether this restriction will ever be lifted), see + |txtfmt-upgrading-to-extended-long-formats|. + + The table below provides a convenient illustration of the effect each + {formats} value has on the set of supported highlight attributes. + + {--{formats}--} + +-------------------------------+ + | S L X | + +-------------------------------+ + |underline Y Y Y | + |bold Y Y Y | + |italic Y Y Y | + |standout - Y - | + |reverse* - Y - | + |undercurl - Y* - | + +-------------------------------+ + |fg_clrs <=8 <=8 <=8 | + |bg_clrs - - <=8* | + +-------------------------------+ + + *Note: "Reverse" is another name for "inverse". + *Note: Versions of Vim prior to 7.0 did not support the undercurl + attribute; thus, Txtfmt will automatically disable undercurl if it + detects a Vim version < 7.0. + *Note: By default, only 3 background colors are enabled. You may + enable more with the |txtfmt-'bgcolormask'| option. + + If you omit the optional {formats} suffix, Txtfmt assumes the default + of 'X'. The suffixes are all case-insensitive. + + *txtfmt-'tokrange'-expr* + Note: If the option string is not a valid 'tokrange' string, Txtfmt + will attempt to evaluate the option string as a Vim expression. If + this evaluation produces a valid 'tokrange' value, Txtfmt will use it; + otherwise, Txtfmt will display error and use a default setting. This + feature permits you to defer the choice of 'tokrange' until a + particular file is being loaded. (Alternatively, you could defer + choosing 'tokrange' by performing buf-local sets within autocommands + or by using a |txtfmt-modeline|.) > + Example: + let g:txtfmtTokrange = 'MyTokrangeChooser()' + let g:txtfmtTokrange = '&enc == "utf-8" ? "0xE000X" : "180X"' +< + + If you do not specify this option, either in a |txtfmt-modeline| or + via the txtfmtTokrange option variable, Txtfmt will attempt to choose + a suitable default. However, you should probably not let Txtfmt choose + a default for an option as important as this one. It is recommended + that you read |txtfmt-choosing-token-range| before setting this + option. Moreover, it is recommended that you use the :MakeTestPage + command to test whatever setting you ultimately choose. (See + |txtfmt-:MakeTestPage|.) + + *txtfmt-'undercurl'* *txtfmt-'uc'* *txtfmtUndercurl* +'undercurl' 'uc' +txtfmtUndercurl boolean (default on if running Vim supports undercurl) + global or buffer-local + *** New in version 2.0 *** + Txtfmt disables use of the undercurl attribute automatically for + versions of Vim that don't support it (i.e., Vim versions older than + 7.0). Turning this option off prevents the creation of undercurl + regions, even when the running version of Vim does support it. You + might wish to do this for performance reasons if you use "long" + formats but rarely if ever use undercurl. Disabling undercurl cuts the + number of format tokens in the Txtfmt token range in half. Of course, + the token reduction is not as significant as the associated runtime + performance improvement, especially if you're using Unicode, in which + character codes are plentiful. For a more detailed look at performance + considerations, read |txtfmt-performance-considerations|. + + As is the case with color masks, setting or clearing this option does + not alter a Txtfmt buffer in any way; it merely alters its syntax + highlighting. Thus, there is nothing to prevent you from disabling + undercurl until you find you have need of it. Suppose, for example, + you have recently upgraded to the current version of Vim from a + pre-7.0 version (i.e., one that doesn't support undercurl). Suppose + further that you've been managing without undercurl, and you're not + sure you really need it. You could put a line such as... > + let g:txtfmtUndercurl = 0 +< + ...in your vimrc to disable undercurl globally until you decide you're + ready to use it. Alternatively, you may decide you want to use + undercurl right away, but you have a number of legacy files, created + under the old version of Vim, which do not have undercurl regions and + probably never will. To avoid paying a performance penalty when + viewing these legacy files, you could add the following Txtfmt + modeline to the legacy files: > + txtfmt:nouc +< + Caveat: For backward-compatibility reasons, disabled undercurl tokens + are not considered to be part of the Txtfmt token range in the same + way that disabled color tokens are. This fact has implications for the + |txtfmt-:MoveStartTok| command, which will not attempt to translate + characters corresponding to disabled undercurl tokens. Another + implication of this fact is that you will not be warned if you choose + a |txtfmt-'tokrange'| setting that does not leave room for undercurl + tokens at the end of the range. Of course, this is not really an + issue, since if you did eventually decide to enable undercurl in such + a case, you could simply run the :MoveStartTok command to translate + the entire token range leftward, thereby making room for the undercurl + tokens. + + *txtfmtUsermap{}* +txtfmtUsermap{} old-style array of strings + (default: none) + global or buffer-local + This old-style Vim "array" allows you to build your own complex + mappings from Txtfmt primitives, by using a powerful mechanism known + as "Txtfmt user-maps". The user-map mechanism is described in detail + in section |txtfmt-user-maps|. + Note: First element of the array is at index 1. + + *txtfmtUsermaplimit* +txtfmtUsermaplimit number (default: 25) + global or buffer-local + Defines the number of elements of txtfmtUsermap{} (described above) + that will be tested for valid user-map definitions. + +CHOOSING A TOKEN RANGE *txtfmt-choosing-token-range* + + *txtfmt-encoding* +Before choosing a value for the |txtfmt-'tokrange'| option, you should +consider what character encoding you will be using. Because of the sheer +number of available character codes, utf-8 is perhaps the ideal encoding for +use with Txtfmt. + + *'short'/'long'-formats* +The number of character codes that must be dedicated to Txtfmt usage depends +upon the setting of txtfmt-'tokrange'. The number of tokens required for +format attributes is given by 2^N, where "N" is the number of format +attributes (e.g., bold, underline, etc...). As mentioned in the help for the +|txtfmt-'tokrange'| option, N will be 3 if "short" or "extended" formats are +selected and either 5 or 6 if "long" formats are used. All tokrange settings +require exactly 9 tokens to be reserved for foreground colors. If background +colors have been enabled ("extended" formats), an additional 9 tokens are +reserved. The following table illustrates the character range requirements +graphically. +Note: The undercurl column indicates whether the undercurl attribute is +active. It will be "on" if the version of the running Vim is >= 7.0 AND +undercurl has not been explicitly disabled via the |txtfmt-'undercurl'| +option. + + +--------+-----------+------------+ + | suffix | undercurl | num tokens | + +--------+-----------+------------+ + | S | NA | 17 | + | X | NA | 26 | + | L | off | 41 | + | L | on | 73 | + +--------+-----------+------------+ + + +When I wrote the initial version of the Txtfmt plugin, only the "short" format +attributes (bold, underline and italic) were supported. Accordingly, only 17 +tokens needed to be dedicated to Txtfmt usage. At that time, I assumed that +only ASCII characters would be used for text. (The initial version of Txtfmt +was actually called "ascfmt"!) Since that time, my mindset has grown a bit +more global, and I now envision Txtfmt usage with a wide variety of character +encodings. If Txtfmt is configured for use with all possible format attributes +(bold, underline, italic, standout, reverse and undercurl), the Txtfmt token +requirement jumps to 73. Although it is possible for many English speakers +using a "latin1" encoding to forego the use of 73 character codes in the range +128-255, Europeans may find this restriction unacceptable, given the presence +of a number of accented characters in that range. If you're using a multibyte +character set such as Unicode, you should have no difficulty finding an unused +sequence of 73 characters to dedicate to Txtfmt. (Hint: see +|txtfmt-private-use-area| below.) + + *txtfmt-performance-considerations* +The number of character codes dedicated to Txtfmt usage is not the only factor +you should consider when choosing a |txtfmt-'tokrange'| setting. The tokrange +setting also determines the number of syntax regions Txtfmt must define in +order to accomplish the desired highlighting. The number of syntax regions, in +turn, directly affects the speed with which Vim can load and redraw a Txtfmt +buffer. Consider, as an example, the choice between "short" and "long" +formats. Although it supports only 3 additional format attributes, the "long" +formats configuration requires roughly 10 times as many syntax regions as +"short" formats! This increase in the number of syntax regions engenders a +significant increase in CPU overhead consumed by syntax highlighting, even +when the additional format attributes are not used, since Vim must spend time +checking the unused region definitions. On some systems, the performance +penalty is large enough to render display of "long" formats noticeably +sluggish. + +With the addition of "extended" formats (i.e., background colors) in Txtfmt +version 2.0, performance considerations have become even more significant. The +addition of 8 background colors to the "short" formats baseline results in a +thirty-fold increase in the number of syntax regions! (Hint: The dramatic +increase is due to the "orthogonality" of the various types of regions.) +Fortunately, Txtfmt allows you to reduce the number of syntax regions +considerably (thereby increasing load/redraw speed) by "de-activating" unused +colors. Consider that the typical user may never use more than 3 or 4 colors +in his Txtfmt buffers. If Txtfmt knows in advance which colors are used, it +can avoid defining syntax regions for the unused ones. Note that the tokens +corresponding to these inactive colors will still be reserved by Txtfmt, since +if the token range were "collapsed" on top of unused color tokens, it would be +impossible (or at least difficult) to re-activate the colors later when, for +example, the user got a faster computer and decided he might occasionally use +the extra colors. + +Txtfmt provides the following 2 "color mask" options to permit you to +enable foreground and background colors independently: + |txtfmt-'fgcolormask'| + |txtfmt-'bgcolormask'| +If you don't define these options, Txtfmt will enable all 8 foreground colors, +and the 3 background colors corresponding to red, green and blue in the +default color configuration. + + *txtfmt-syn-rgn-count-table* +The following table is intended to aid you in choosing your +|txtfmt-'tokrange'| suffix and fg/bg color masks, by showing the number of +syntax regions required for various configurations and numbers of active fg/bg +colors. The list is intended to be representative, not exhaustive. +Note: To facilitate comparison, the table rows are presented in order of +increasing syntax region counts. + + +--------+-----------+-----------+------------+ + | suffix | # fg clrs | # bg clrs | # syn rgns | + +--------+-----------+-----------+------------+ + | S | 8 | NA | 142 | + | L* | 8 | NA | 574 | * no undercurl + | L | 4 | NA | 638 | + | X | 5 | 2 | 684 | + | X | 4 | 4 | 990 | + | L | 8 | NA | 1150 | + | X | 8 | 3 | 1448 | + | X | 8 | 4 | 1846 | + | X | 6 | 6 | 2030 | + | X | 8 | 8 | 3438 | + +--------+-----------+-----------+------------+ + + *txtfmt-upgrading-tokrange* +If you don't currently need background colors or "long" formats and thus are +considering the use of "short" formats because of the reduced token +requirement, but suspect that you may eventually want to use either "long" +format attributes or background colors, it may be reassuring to know that the +implementation has been designed to permit a seamless "upgrade" from "short" +to "long" or "extended" formats. + +As an example, consider a Txtfmt file with the following |txtfmt-modeline|: > + txtfmt:tokrange=180S + +This file will use tokens in the range 180-196 to support 8 colors as well as +all combinations of bold, underline and italic attributes. If you decide at +some point that you would like to begin using standout, reverse and undercurl +attributes in this file, simply change the modeline to the following: > + txtfmt:tokrange=180L + +The original (bold, underline and italic) regions will be unaffected, but you +will now be able to create regions with all 63 combinations of format +attributes. The cost is an extra 56 character codes that will be unavailable +for normal text usage. The expanded range of characters used by Txtfmt is now +180-252. + +Alternatively, if you were content with bold, underline and italic format +attributes, but wished to add background colors to your buffer, you could have +upgraded from "short" to "extended" with the following modeline: > + txtfmt:tokrange=180X +< + + *txtfmt-upgrading-to-extended-long-formats* + *txtfmtAllowxl* +It should be pointed out that the default location for background color tokens +is just past the end of the "short" format tokens. Note that this is precisely +where the "long" format tokens would be located. Ideally, since the format +token range is the only variable-length range, the background color tokens +would have been located before it. Such a strategy would have permitted the +user to switch freely back and forth between "extended" and "long" formats +without the need to move or delete any existing background color tokens. +Unfortunately, for reasons of backward-compatibility, the background color +tokens had to go after all other tokens. The reason is that earlier versions +of Txtfmt, which didn't support background colors at all, used a default for +tokrange that puts the first token at the very beginning of Unicode's Private +Use Area. Locating background color tokens at the head of the token range, +therefore, would require users to translate the tokens at least 9 characters +rightward in their existing files if they wished to use background colors. +This was out of the question, as one of my design goals for version 2.0 was to +ensure that a user who wished to add background colors to a buffer created +with an older version of Txtfmt would be able to do so simply, i.e., without +the need for special upgrade steps. + +The fact that background color and "long" format tokens occupy overlapping +ranges means that, by default, you can't have both background colors and long +formats active in the same buffer. Of course, it would have been possible to +locate the background color tokens after the long token range, leaving the +long tokens reserved but unused when only "short" formats are active. Although +this strategy would support simultaneously active long formats and background +colors, it would have broken backward compatibility. The reason is that the +default tokrange for the latin1 encoding is such that there is not enough room +after the final long format token for the range of background color tokens. +Thus, users with existing files that use the default tokrange would be +required to translate their entire token range at least 9 characters leftward +prior to enabling background colors. In light of the previously stated design +goals, this was unacceptable. Thus, by default, the background color tokens +are "packed" in just after the short format tokens. It is possible, however, +to override this default behavior by clearing the |txtfmt-'pack'| option. When +this option is turned off, the background color tokens will be located after +the range reserved for long format tokens, even when the configuration is such +that long formats are disabled. + +Why would you ever wish to clear the |txtfmt-'pack'| option? Well, there are +two possible motivations: +1. You're not initially sure whether you would prefer to have background + colors or long formats. + Example scenario: You're currently using "extended" formats, but you've + done a lot of editing and have inserted only a handful of background + colors. Moreover, you've found yourself wishing more than once for the + undercurl attribute. At this point, you could simply change the tokrange + suffix from 'X' to 'L' and begin using undercurl. If |txtfmt-'pack'| had + been set when you added the background color tokens, those tokens would now + be interpreted as long format tokens, thereby introducing unwanted format + regions. Since |txtfmt-'pack'| was clear, however, they are located + harmlessly beyond the end of the Txtfmt token range, and will therefore + have no effect. +2. You know that one day Txtfmt will support the 'XL' (extended long) tokrange + suffix, and you want to create files that will be able to transition + seamlessly from 'X' or 'L' to 'XL'. + Note: Unofficially, Txtfmt already does support the 'XL' suffix, but it is + currently disabled for performance reasons. If you're wondering why, you + may be interested to know that the 'XL' suffix requires 28633 syntax + regions!!! On my machine, Vim crashes if I attempt to use 'XL' formats with + all foreground and background colors enabled! I can get it to load without + crashing by de-activating several colors, but even then, syntax definitions + are sometimes silently corrupted, apparently because Vim has run out of + memory for storing them. For this reason, I do not advertise support for + 'XL', and in fact it is disallowed by default. However, if you set the + global |txtfmtAllowxl| option to a nonzero value before attempting to load + Txtfmt, you will be able to set suffix to 'XL'. (If you do, you may wish to + go make a pot of coffee while you wait for the file to load, and if Vim + crashes while you're gone, don't say I didn't warn you...;-) + + *txtfmt-start-token* + *txtfmt-private-use-area* +If you're using Unicode, the "Private Use Area" (a block of character codes +beginning at 0xE000) is probably the ideal location for the Txtfmt token +range. This block is reserved for "application-specific" use, and is +guaranteed never to be used for actual characters. + + *txtfmt-multibyte-syntax-bug* +Note: Several beta-release versions of Txtfmt did not work correctly with +multibyte encodings because of a Vim bug involving the treatment of offsets +used in syntax highlight matches. A patch for the Vim bug was created by +Yukihiro Nakadaira, and was included in the Txtfmt distribution. Only users +who applied the patch were able to use Txtfmt with a multibyte encoding such +as utf-8. As of Txtfmt version 1.1, however, the patch was no longer +necessary, as workaround logic had been added to Txtfmt itself. Moreover, +Yukihiro's patch has been incorporated into Vim 7.2, so Txtfmt's internal +workaround is required only for older versions of Vim. There is, however, one +_extremely_ rare scenario in which someone running a version of Vim older than +7.2 might wish to apply the patch: namely, when different characters within +tokrange are encoded with different numbers of bytes. Since the number of +bytes per character is usually constant across a large block of Unicode +characters, only users wishing to set tokrange to a very non-standard value +would be affected by this limitation. All of the default tokrange settings +work properly with no need for the patch. Thus, as of Txtfmt version 1.5, I'm +removing Yukihiro's patch from the distribution (to avoid frightening away new +users;-). If you determine that you need it, you can obtain it from a Txtfmt +version <= 1.4. Note, however, that you should never need this patch if you're +running Vim 7.2 or later. + + *txtfmt-problematic-ranges* +Not all character codes are suitable for use as Txtfmt tokens. In particular, +only tokens that are susceptible to Vim's syntax highlighting should be used. +For a color terminal, characters used in terminal control sequences are +sometimes immune to syntax highlighting. The specific character code ranges +affected will vary from terminal to terminal. For latin1 and utf-8 encodings, +characters in the range 0x80-0xA0 should generally be avoided, as they tend to +cause problems in both GUI and terminal versions of Vim. (Exception: The +Windows GUI appears to be able to handle this range.) For utf-8 encodings, +only 16-bit character codes should be used, as Vim does not display characters +above 0xFFFF correctly. Note that not all 16-bit values correspond to a valid +Unicode character. The ones that do not will be displayed as (where +xxxx is the hexadecimal representation of the character code). These +"characters" cannot be highlighted and should not be used. + +As a general rule, modern unix-style terminals tend to work best when Unicode +is used. I have found by experimentation that some ranges of tokens work +perfectly when 'encoding' is set to utf-8, but don't work at all when it is +set to latin1. The bottom-line is, if you're using a color terminal designed +to work with Unicode, you should probably keep 'encoding' at utf-8. If for +some reason you must use a different encoding, you should experiment with the +|txtfmt-:MakeTestPage| command to find a setting for |txtfmt-'tokrange'| that +works. + + *txtfmt-default-tokrange* +If you do not explicitly set the |txtfmt-'tokrange'| option, either in a +|txtfmt-modeline| or via the |txtfmtTokrange| option variable, Txtfmt will +attempt to choose a default setting that is suitable for the current character +encoding. Success in this endeavor is not guaranteed, however, since it +involves choosing a range of characters that will not be used for any other +purpose in the file being edited. For Unicode, the task of choosing a suitable +range is simple, due to the existence of the "Private Use Area". + +Backward-compatibility Note: Because versions of Vim prior to 7.2 handled +syntax highlighting match offsets incorrectly for multibyte characters, +versions of Txtfmt prior to 1.1 would not choose a tokrange default within the +"Private Use Area", even for Unicode encodings such as utf-8. Txtfmt version +1.1, however, contained a workaround that permitted multibyte tokens to be +used even in versions of Vim suffering from the aforementioned bug. Thus, in +Txtfmt version 1.1, the default start token for Unicode changed from 180 to +0xE000 (the start of the "Private Use Area"). + +Important Note: If you are a multibyte encoding user who created Txtfmt files +with the old start token value of 180, you should consider the following +(non-exhaustive) list of options... + 1. Continue using 180 for all Txtfmt files. + How: Override the new default by setting the |txtfmtTokrange| + option explicitly. + 2. Continue using 180 for existing files, but use 0xE000 for all + newly-created Txtfmt files. + How: Put a |txtfmt-modeline| containing (e.g.) "tokrange=180X" in + your existing Txtfmt files to override the new default. + 3. Use 0xE000 for all Txtfmt files, old and new. + How: Use the :MoveStartTok command to relocate the tokens in your + existing file(s) to a new range within the "Private Use Area". + e.g. > + :MoveStartTok 0xE000 +< + For more information, see |txtfmt-:MoveStartTok| + +For encodings other than Unicode, there is nothing analogous to the "Private +Use Area"; hence, it is not possible to choose a default tokrange that will +work for everyone. Currently, Txtfmt's default algorithm chooses '180X' for +all 1 and 2 byte encodings. This value should be suitable for most +non-European users of a single byte encoding such as latin1. European users +who use accented letters located at or above character code 180 should +probably be using a multibyte encoding such as utf-8. + +Although the tokrange defaults should be suitable for the vast majority of +users, they may not be suitable for everyone, and each Txtfmt user is +encouraged to put some thought into the tokrange he will use and then test it +thoroughly with :MakeTestPage. For usage details, see |txtfmt-:MakeTestPage|. + + +COLORS *txtfmt-color-config* + +IMPORTANT NOTE: This section makes no attempt to distinguish between +foreground and background colors. The description of the |txtfmtColor{}| array +applies equally to the|txtfmtBgcolor{}| array, which permits background colors +to be configured independently of their foreground color counterparts. + +As mentioned previously, there are 9 tokens used to begin Txtfmt color +regions: 8 corresponding to colors, and 1 indicating "default" or "no color". +By default, Txtfmt picks these colors according to RGB values: #000000 +(black), #0000FF (blue), #00FF00 (green), etc... However, this choice may not +be desirable for all users; hence, Txtfmt provides a rather flexible mechanism +by which the user can configure the colors used. In fact, the user can +configure different sets of colors for different color terminals, GUI's, or +colorschemes. The configuration is accomplished via the old-style Vim array +txtfmtColor{1..8}, which may be set either buf-locally or globally. If a given +element has both a buf-local and a global setting, the buf-local setting is +given priority. This array has a static counterpart, initialized to default +values by Txtfmt, so the user can override only those elements he wishes to +change. + +Each element of the txtfmtColor{} array corresponds to 1 of the 8 possible +colors. Its value is a string consisting of at least 2 comma-separated parts. +The first part is a Vim regular expression that determines how the color may +be specified in a "color spec". (See |txtfmt-clr-spec| for more information.) +Each of the subsequent parts contains a prefix indicating whether the +definition applies to cterm's or GUI's, an optional list of patterns +specifying the applicable values of 'term', and the actual color string to be +used as the rhs of a "ctermfg=" or "guifg=" in a :highlight command. + +Here is the formal definition of a single string in the txtfmtColor{} array: +{namepat},{clrdef1}[,{clrdef2},...,{clrdefN}] + + {clrdef} := + {c|g}[{termpatlist}]:{clrstr} + + {termpatlist} := + :{termpat1}:{termpat2}:...:{termpatN} + +Here are the meanings of the fields: +{namepat} Vim regex used to recognize the name used to specify a certain + color; e.g., when prompted by one of the insert token maps. May not + contain spaces or commas. (Example: 'k\\|bla\\%[ck]') +{c|g} Determines whether {clrstr} will be the rhs of a "ctermfg=" ('c'), + or "guifg=" ('g') +{termpat} Pattern used to match against &term option. It is a regex pattern + which will be applied as ^{termpat}$. + Note: The {termpatlist} is optional. If it is omitted, the color + definition will apply to all cterm's or gui's, as determined by + {c|g}. +{clrstr} rhs of a "ctermfg=" or "guifg=" assignment. For possible values, + see |gui-colors| and |cterm-colors| in the Vim help. + +Note: When multiple {clrdef}'s could apply, the rightmost one is used. +Note: {namepat}, {termpat} and {clrstr} fields will have one level of +backslashes removed during processing. Commas, colons and backslashes +appearing within those fields should be preceded with a single backslash. (See +examples below...) + +Example txtfmtColor{} elements: > + 'red,c:xterm:dosterm:DarkRed,g:#FF0000,g:builtin_gui:other_gui:#FF1010' + '^b\\%[lue]$,c:DarkBlue,g:#0000FF' +< + +Color defaults *txtfmt-color-defaults* + +The default values for the 8 colors are given below: > + txtfmtColor{1} = '^\\%(k\\|bla\\%[ck]\\)$,c:Black,g:#000000' + txtfmtColor{2} = '^b\\%[lue]$,c:DarkBlue,g:#0000FF' + txtfmtColor{3} = '^g\\%[reen]$,c:DarkGreen,g:#00FF00' + txtfmtColor{4} = '^t\\%[urquoise]$,c:DarkCyan,g:#00FFFF' + txtfmtColor{5} = '^r\\%[ed]$,c:DarkRed,g:#FF0000' + txtfmtColor{6} = '^v\\%[iolet]$,c:DarkMagenta,g:#FF00FF' + txtfmtColor{7} = '^y\\%[ellow]$,c:DarkYellow,g:#FFFF00' + txtfmtColor{8} = '^w\\%[hite]$,c:White,g:#FFFFFF' + +You may override none, some or all of the defaults. It is even permissible to +override only part of an element; you could, for example, override the +definition of "red" for a "dosterm" color terminal, but use the default red +definition for other cterm's and GUI's. + +Colorscheme considerations *txtfmt-colorschemes* + +Although the simplest way to override the default colors is to set the global +version of txtfmtColor{} once and for all in your vimrc, there may be times +when this simple solution is inadequate. Consider the following scenario... +You use Txtfmt to create some blue text in a buffer with a light background. +You later decide you wish to view the Txtfmt buffer with the "blue" +colorscheme. The blue text you created is no longer visible! Of course, you +can make it visible again by redefining the "blue" element in the global +txtfmtColor{} array and re-editing or :Refresh'ing the Txtfmt buffer (see +|txtfmt-:Refresh|), but changing g:txtfmtColor{} everytime you change +colorschemes could become tedious. A better solution is to use autocommands to +change the color definitions automatically whenever the colorscheme changes. +Although you could set the global version of txtfmtColor{} in the autocommand, +setting the buf-local version instead lets you reserve the global version for +defaults that will be used with filetypes for which an autocommand has not +been defined. + +For the sake of example, let us assume that you use Txtfmt to edit *.txt +files, and that you wish to vary the color definitions according to current +colorscheme for all such files. To keep the example simple, I will consider +only the following 3 cases: + + 1. "default" colorscheme + Txtfmt defaults are acceptable + 2. "blue" and "darkblue" colorscheme + Change black to orange + Change blue to brown + 3. all other colorschemes + Change black to orange + Leave blue at the Txtfmt default + +The following sample implementation shows one possible strategy for +configuring the Txtfmt colors as described above... +> + " --INSIDE YOUR USER FILETYPE.VIM-- + augroup filetypedetect + au! ColorScheme *.txt call Define_colors(1) + au! BufRead,BufNewFile *.txt call Define_colors(0) + au BufRead,BufNewFile *.txt setfiletype txtfmt + augroup END +< +------------------------------- +Explanation of the autocommands +------------------------------- +All assignments to the buf-local txtfmtColor{} array occur within the +Define_colors function (shown below). A call to Define_colors has been placed +in a BufRead,BufNewFile autocommand to ensure that colors can be set +appropriately even when :colorscheme has never been called. The call from +within the ColorScheme autocommand ensures that the colors are recomputed +whenever the colorscheme is changed. > +Note: It is not necessary that the ColorScheme autocommand be placed within +the filetypedetect group. +> + " IN A FILE SOURCED AT STARTUP (E.G., VIMRC) + " Configure colors in response to colorscheme change or Txtfmt buffer load + " Note: 'refresh' input will be true if and only if the function is + " called in response to colorscheme change. + fu! Define_colors(refresh) + if !exists('g:colors_name') || g:colors_name == 'default' + " Use defaults + let b:txtfmtColor{1} = '^\\%(k\\|bla\\%[ck]\\)$,c:Black,g:#000000' + let b:txtfmtColor{2} = '^b\\%[lue]$,c:DarkBlue,g:#0000FF' + elseif g:colors_name == 'blue' || g:colors_name == 'darkblue' + " Replace black with orange + let b:txtfmtColor{1} = '^o\\%[range]$,c:Yellow,g:#FF8040' + " Replace blue with brown + let b:txtfmtColor{2} = '^b\\%[rown]$,c:Brown,g:#804000' + else + " Replace black with orange, but leave blue at Txtfmt + " default + let b:txtfmtColor{1} = '^o\\%[range]$,c:Yellow,g:#FF8040' + let b:txtfmtColor{2} = '^b\\%[lue]$,c:DarkBlue,g:#0000FF' + endif + " We shouldn't get here if this isn't a Txtfmt buffer, but check to be + " safe since :Refresh is defined only for Txtfmt buffers + if a:refresh && b:current_syntax =~ '\' + " Important: We must reload the Txtfmt plugin because + " :colorscheme clears the old highlight groups + :Refresh + endif + endfu + +------------------------------ +Explanation of Define_colors() +------------------------------ +In the example, I use g:colors_name (which, if it exists, holds the name of +the currently active colorscheme) to decide which elements of the buf-local +txtfmtColor{} array need to be overridden, and what color values should be +used. It would also be possible to take 'background' into account, but be +aware that not all colorschemes set this option. You can make the color +configuration logic as simple or as complex as you like. +Note: Because Txtfmt processes each element of the txtfmtColor{} array as a +separate entity, it is necessary to set only the elements you wish to change +from the Txtfmt defaults. Of course, if one of the if/else blocks sets a +particular element, the others should set it as well, even if they do not need +to override the default. Alternatively, a block that wishes to use the default +could simply unlet! the corresponding element to ensure that the Txtfmt +default is used. + +It is important to note that simply changing the buf-local version of +txtfmtColor{} has no effect on an existing Txtfmt buffer; the Txtfmt plugin +must be reloaded (i.e., "refreshed") before the color changes can take effect. +When Define_colors() is called in response to a BufRead or BufNewFile event, +the required reload happens automatically because of the subsequent +"setfiletype txtfmt". In the case of the ColorScheme event, however, there is +no such automatic reload. To ensure that the user will not need to do anything +special to see the results of changing the colorscheme, I have added the +conditional :Refresh to Define_colors(). The test of the boolean "refresh" +argument ensures that the |txtfmt-:Refresh| command will be invoked only when +the colorscheme is changing and we are within a Txtfmt buffer. + +Achieving colorscheme independence *txtfmt-hl-color-names* + +Although the default txtfmtColor{} array uses hard-coded color names, this is +by no means required. An intriguing possibility, which I have implemented on +my own system, is to use color name patterns corresponding to the default Vim +highlighting groups. Under this strategy, instead of naming a particular color +"red", you might instead name it "error". Within the Define_colors() routine +outlined above, you would define the error color according to the +colorscheme-specific guibg/ctermbg definitions used for the Error highlight +group. The advantage of this strategy is that it guarantees that your +Txtfmt files will look good in any colorscheme (assuming they looked good in +the original colorscheme ;-). To facilitate creation of the required color +definitions, I created a Vim script that parses the colorscheme files in +$VIMRUNTIME/colors and extracts the desired definitions for each colorscheme +automatically. I can send a copy of this script, along with instructions for +using it, to anyone who is interested... + +COLOR TERMINAL PITFALLS *txtfmt-cterm-pitfalls* + +Cterm default background *txtfmt-cterm-default-background* + +When you define a highlighting region for a cterm without specifying the +background color with ctermbg, Vim assumes the "default" background color is +desired. The "default" background for a cterm is the default background color +of the terminal itself, unless ctermbg has been overridden in a highlight +statement for the "Normal" group: e.g., > + :hi Normal ctermbg=15 +< +In such cases, the Normal background color replaces the cterm's default +background for all subsequent highlighting. Certain colorschemes (e.g., blue, +darkblue and evening) explicitly set the Normal background color in a cterm. +Others leave it at the default for the color terminal. Ordinarily, Txtfmt can +handle either case, but the following paragraph describes an issue I have +encountered with one particular terminal, seen only when 'term' is set +incorrectly... + +Incorrect 'term' setting for gnome-terminal *txtfmt-cterm-term-issue* + *txtfmt-gnome-terminal-issue* +The default version of gnome-terminal leaves the TERM environment variable +set to xterm, although the proper setting appears to be gnome-256color. +Unfortunately, the package containing the gnome-256color terminfo description +is not installed by default on some gnome systems. Strangely, even when the +gnome-256color terminfo description is installed, gnome-terminal won't set +TERM to use it. When Vim is started in a gnome-terminal for which TERM=xterm, +the 't_Co' termcap entry is incorrectly set to 8 instead of 256. One of the +effects of this is that outputting certain foreground colors to the cterm will +cause the default background color to revert from the value defined for the +Normal group to the terminal's default. The problem appears to be limited to +the "dark" foreground colors (0-7), which are precisely the colors used in +Txtfmt's default color map. Thus, this issue has the potential to cause +serious display problems for Txtfmt. + + *txtfmt-gnome-terminal-issue-workaround* +There are several ways to resolve this issue. The simplest is to set t_Co=256: +e.g., > + :set t_Co=256 +< +The set of t_Co could go in your vimrc, or anyplace that causes it to be +executed before Txtfmt is loaded. A better solution, in my opinion, is to +ensure that Vim's 'term' option is set to its proper value (i.e., +gnome-256color), but this is possible only if the corresponding terminfo +description is installed on your system. If the following command... > + :set term=gnome-256color +< +...generates an error in Vim, you must first install the terminfo description. +On Fedora Linux, the package is named "ncurses-term". (It may have a different +name in other distributions.) Once you have installed the terminfo package, +you should ensure that Vim's 'term' option is set properly. There are at least +2 possible approaches: + +1. Set TERM environment variable + Put the following in one of your shell startup scripts (e.g., .bashrc): > + export TERM=gnome-256color +< +2. Set 'term' option in your vimrc + e.g., > + set term=gnome-256color +< + +When "hidden" tokens are not hidden *txtfmt-cterm-ignore-issue* + +Originally, Txtfmt relied exclusively upon the "Ignore" preferred highlighting +group to hide the special formatting tokens. The Ignore group was added in Vim +5.2 to permit markup characters such as the `*' and `|' ("stars and bars") +surrounding tags in a Vim help file to be hidden. It has since come to my +attention, however, that roughly half of the colorschemes distributed with Vim +do not hide text in the Ignore group. In some cases, the contrast between +foreground and background color is minimized; in other cases, there appears to +have been no attempt at all to obscure the text. +Note: It is easy to see how your particular colorscheme treats text in the +Ignore group: simply execute... > + :hi Ignore +If the Ignore group is defined properly, the "xxx" test pattern should be +invisible. + +In some cases, the Txtfmt plugin can take the Ignore group completely out of +the equation, by defining a Txtfmt-specific concealment region that sets the +foreground color equal to the background color. This strategy always works for +the GUI. It works in a cterm if and only if the terminal background color is +known to Vim; i.e., it works when the Normal group's background color has been +set (as described in section |txtfmt-cterm-default-background|). If the Normal +group background is not set, any attempt to use "bg" as a color name in a +highlight command will generate an error: e.g., > + :hi Tf_conceal ctermfg=bg " ERROR if Normal ctermbg not set +< +If you are seeing the formatting tokens in your Txtfmt buffer, the plugin was +unable to set ctermfg=bg. In that case, you have several options, as described +below... + + *txtfmt-cterm-ignore-workaround* +1. Redefine the Ignore group: + First of all, determine the foreground color that most closely matches the + default background color of your terminal. You can do this by + experimentation, or by looking at the Ignore group definition of a + colorscheme that does a better job of hiding Ignore'd text. (The default + colorscheme would be a good one to check first.) Once you have determined + the proper color, put it into a :highlight command as follows: > + :hi Ignore ctermfg= +< + Of course, you need to ensure that the :highlight command is executed after + the colorscheme has been sourced. There are several ways this can be + accomplished... If your colorscheme is sourced in your vimrc, and you never + change it after that, you could put the :highlight command in your vimrc, + just after the :colorscheme command. If, however, you sometimes change + colorschemes after starting Vim, you should avoid this method. Instead, you + might try one of the following: + + 1. Put the :highlight command into an autocommand defined for the + ColorScheme event: e.g., > + :au ColorScheme * hi Ignore ctermfg= +< + The autocommand itself could be defined in your vimrc. Note that + the ColorScheme event is triggered only after the colorscheme has + been loaded. (See |ColorScheme| in the Vim help for more + information.) + + 2. Create an override of your colorscheme file containing the + following two lines: > + :source + :hi Ignore ctermfg= +< + The file you create should be placed in a folder that appears + earlier in your 'runtimepath' than the folder containing the + colorscheme you are overriding. + +2. Define a background color for the Normal group: + This method ensures that Vim knows the terminal background color, which + enables Txtfmt to define its own highlight group with ctermfg=bg. You can + set the default background color like so... > + :hi Normal ctermbg= +< + Note: Be sure to put the :highlight command in a place where it will be + executed before Txtfmt is loaded. Any of the methods described above for + redefining the Ignore group could be used here as well. + Note: The Normal background color is used for all buffers, not just the + Txtfmt ones, so you will probably want to choose a color that is close to + your default background color. + Note: If you are using a gnome-terminal, read section + |txtfmt-gnome-terminal-issue| before using this method. + +3. Choose a colorscheme that does a better job of hiding the text in the + Ignore group. + Note: Because it is not always possible to specify a foreground text color + that _exactly_ matches a color terminal's default background, only + colorschemes that explicitly set the Normal background color can guarantee + that text in the Ignore group will be hidden. However, even when the + Normal group's background color is not defined, it should usually be + possible to find a foreground color that comes reasonably close to matching + the default background. + + +CUSTOMIZING MAPPINGS *txtfmt-map-config* + +The Txtfmt filetype plugin defines a number of mappings for various modes. By +default, Txtfmt picks consistent and intuitive key sequences for the lhs of +these mappings. It may be, however, that you already use one or more of those +key sequences for an existing map in the same mode. Even if there is no direct +conflict, it is possible that Txtfmt's default mapping would create an +ambiguity with respect to an existing mapping. (For details, see +|map-ambiguous| in the Vim help.) Finally, it may be that you simply do not +like the key sequences chosen by the plugin author. Whatever the case, Txtfmt +provides several ways to customize the map lhs. You can even specify that a +conflicting or ambiguous map should not be defined at all. All of these +customization mechanisms are discussed below... + +All but one of the maps defined by the Txtfmt filetype plugin begin with +; hence, you may achieve a small amount of customization by +setting the special Vim global variable |maplocalleader| to a non-default +value. + +Example: > + let g:maplocalleader = '_' + +Now, when a Txtfmt map such as I is defined, the actual {lhs} +will be _I instead of the default \I. Of course, if the conflicting map was +also defined with , then this approach will not resolve the +conflict. + +A more flexible mechanism for customizing maps involves the construct. +If you are unfamiliar with this construct, you may wish to see |using-| +in the Vim documentation. All of the mappings provided by the Txtfmt filetype +plugin are defined in a 2 stage process, which permits the default {lhs} to be +completely redefined. To the 2 "stages" in the map definition process +correspond 2 map "levels", henceforth referred to as the 1st and 2nd levels. + +The 2nd level map is the one whose {rhs} actually implements the Txtfmt +functionality. The {lhs} of the 2nd level map is a key sequence beginning with +, which can never be produced by the keyboard alone. Thus, if the Txtfmt +map functionality is to be usable, there must be a 1st level mapping capable +of producing this untypable key sequence. If Txtfmt sees that the user has +already defined such a map, it will define only the 2nd level map, effectively +allowing the user-defined 1st level map to override the default map sequence. +If, on the other hand, the user has not defined a 1st level map for a +particular map functionality, Txtfmt will define a 1st level map with the +default key sequence listed in this help document. + +Some examples using one of the "Jump to token" mappings will be used to +illustrate the concepts presented above... + +From the section defining the insert token maps (|txtfmt-ins-tok-map-list|), +we see the following map definition: + + i TxtfmtInsertTok_i + +The i represents the default 1st level map {lhs}, while +TxtfmtInsertTok_i is the 2nd level map {lhs}. The following 4 examples +show the 1st level map that results for 4 distinct customization scenarios. + + Scenario: Neither maplocalleader nor 1st level map redefined + Result: \i ==> insert token functionality + + Scenario: maplocalleader redefined as follows: > + let maplocalleader = '_' +< Result: _i ==> insert token functionality + + Scenario: 1st level map redefined as follows in user's vimrc (or + something sourced prior to Txtfmt load): > + nmap _it TxtfmtInsertTok_i +< Result: _it ==> insert token functionality + (\i map not created) + + Scenario: Both maplocalleader and 1st level map redefined: > + let maplocalleader = '_' + nmap it TxtfmtInsertTok_i +< Result: _it ==> insert token functionality + (\i map not created) + + +Handling map conflicts and ambiguities *txtfmt-map-conflict* + +The mechanisms described above permit the Txtfmt user to eliminate map +conflicts and ambiguities once they become known. Of course, when you first +begin using the Txtfmt plugin, you will probably not be aware of the +conflicts. By default, Txtfmt will overwrite any existing mappings with its +own 1st level maps; moreover, to ensure that you have the opportunity to +resolve the conflict, Txtfmt gives one warning per Vim session for each +conflict or ambiguity it detects. The default behavior just described may be +tailored with global option |txtfmtMapwarn|. With this option, you can specify +any or all of the following: + + -whether echomsg or echoerr is used for warnings + -whether the same map conflict/ambiguity will produce a warning every + time the plugin is sourced, or only once per Vim session + -whether a Txtfmt map will be created when a conflicting/ambiguous map + exists + +The |txtfmtMapwarn| option may be set to a string of the following character +flags: "mMeEoOcC". The flags may appear in any combination and in any order. +Lowercase flags affect the handling of map ambiguities, while uppercase flags +pertain to map conflicts. The meaning of the flags is as follows: + + m Use :echomsg to warn about map ambiguities + M Use :echomsg to warn about map conflicts + e Use :echoerr to warn about map ambiguities + E Use :echoerr to warn about map conflicts + o Warn of a particular map ambiguity only once per Vim session + O Warn of a particular map conflict only once per Vim session + c Create the Txtfmt map in case of map ambiguity + C Create the Txtfmt map in case of map conflict + +The "once-only" flag: The intent of the o and O flags is to prevent the user +from being bombarded with the same conflict warning every time he loads a +Txtfmt buffer. To determine whether a map conflict or ambiguity is identical +to one for which the user has already been warned, Txtfmt employs the concept +of a "conflict instance". A conflict instance is defined by a unique +combination of the following: + + -map mode + -{lhs} of the Txtfmt map + -{rhs} of the existing map + +Note: If mutually exclusive flags are used, Txtfmt will give precedence to the +flag appearing later in the string. If a flag is used without another flag +required to give it meaning, the useless flag is simply ignored. Txtfmt will +not generate an error unless invalid flags are used. + +============================================================================== +6. User interface *txtfmt-user-interface* + +INSERTING TOKENS *txtfmt-ins-tok* + +Whenever you wish to alter the formatting or coloring of text in a Txtfmt +buffer, you must insert the appropriate Txtfmt token or tokens. A single +format token is sufficient to determine any combination of format attributes, +just as a single color token is sufficient to determine any of the 8 possible +foreground or background colors. Thus, as a general rule, a Txtfmt buffer will +not contain sequences of more than 3 consecutive Txtfmt tokens. There are, +however, times when it makes sense to insert more than 3 Txtfmt tokens at +once; when you know, for example, that the region you are creating will be +terminated by an explicit "no format" and/or "no color" token, you may wish to +insert both the region opening and closing tokens with a single insert +mapping, which leaves the cursor positioned just after the opening tokens +where the region text will go. Details on the various mappings are provided +below in the section on |txtfmt-ins-tok-map-list|. Before you attempt to use +those mappings, however, you should be familiar with the concept of +format/color specifications. Each of the token insertion mappings prompts you +to enter a list of such specifications, which it then translates into a +sequence of tokens to be inserted into the buffer. The following sections +describe these specifications and the lists comprising them in detail. + +Fmt spec *txtfmt-fmt-spec* + +A format specification specifies a unique combination of valid format +attributes. The set of "valid" format attributes is determined by the +|txtfmt-'tokrange'| option (in particular, by the tokrange suffix) as well as +by the version of Vim you are using. + +The format of a single format specification is given as follows: + + f{fmts} + +where {fmts} is a string consisting of either a single dash ('-') indicating +"no format attributes", or a sequence of letters from the set "ubisrc", in +which each letter represents a format attribute that should be applied to the +region. The letter/attribute correspondences are shown in the table below. +Also shown are the |txtfmt-'tokrange'| suffix and version of Vim required to +enable the format attribute. + + ==================================================== + | ltr | attribute | tokrange | v:version | + | | | suffix | | + ==================================================== + | u | underline | S or X | any | + | b | bold | S or X | any | + | i | italic | S or X | any | + | s | standout | L | any | + | r | reverse (inverse) | L | any | + | c | undercurl | L | >= 700 | + ==================================================== + + Note: {fmts} may contain any combination of valid format specification + letters in any order. + +Clr spec *txtfmt-clr-spec* + +A color specification specifies a single color from among up to 8 +possibilities in the active color definition. + +The format of a single color specification is given as follows: + + {typ}{clr} + +{typ} is either + 'c' => foreground color + 'k' => background color +{clr} is either a single dash ('-') indicating "no color" (i.e., default +color), or a string matching the {namepat} corresponding to one of the 8 +possible colors. As discussed in the section |txtfmt-color-config|, {namepat} +is simply a Vim regular expression used to recognize a (possibly abbreviated) +color name. The default color definitions use Vim's \%[] contruct with +intuitive color names (e.g. "red", "green", "blue") so that you can usually +specify a color by the first letter of its name. + +Example: > + {namepat} valid {clr} values + =================================== + b\%[lue]$ "b" "bl" "blu" "blue" + r\%[ed]$ "r" "re" "red" + +Of course, if you override the default color definition array, you are free to +define whatever colors and corresponding {namepat}'s you like. See +|txtfmt-color-config| for details. + + +Fmt/clr spec list *txtfmt-fmt-clr-spec-list* + +Since most mappings and functions for working with fmt/clr tokens permit +multiple tokens to be inserted simultaneously, Txtfmt permits the format/color +specifications described in the preceding two sections to be concatenated into +comma or dot-separated lists. The resulting "format/color specification lists" +are the topic of this section. + +Format/color specification lists may contain any number of fmt-spec and +clr-spec "atoms", freely intermixed and in any order. Internally, Txtfmt will +translate the list of fmt/clr atoms into a sequence of character codes +representing the corresponding fmt/clr tokens. By default, Txtfmt's token +insertion mappings leave the cursor after the final token inserted; you can +alter this behavior, however, by replacing one of the commas separating the +individual fmt/clr specs with a dot ('.') to indicate the desired cursor +position. +Note: Although commas are used only to separate items in the list, a dot may +be placed at the beginning of the list to indicate that the cursor should end +up before any of the inserted tokens. + +Here are a few example fmt/clr spec lists: + Note: The following examples assume the default color definitions + listed in |txtfmt-color-defaults|. + + kb + Result: Blue background region is begun (but not terminated). Cursor + is positioned after the inserted token. + + fu,cr + Result: Red underline region is begun (but not terminated). Cursor is + positioned after the inserted tokens. + + fbi,cg,kr.k-,c-,f- + Result: Green bold,italic on red background region is begun (and + simultaneously terminated). Cursor is left just inside the region. + + .f-,c- + Result: Inserts the "no format" and "no color" tokens, but leaves + cursor positioned within the combined fmt/clr region being terminated. + + fubisrc + Result: Underline,bold,italic,standout,reverse,undercurl region is + begun. Cursor is positioned after the inserted token. + +Mapping overview *txtfmt-ins-tok-maps* + +The Txtfmt filetype plugin provides a number of mappings that may be used to +insert the special tokens used to delineate the Txtfmt format/color regions. +All of the token insertion mappings prompt the user to enter a special comma +or dot-separated list, known as a fmt/clr spec list, which completely +describes the sequence of tokens that should be inserted. Details on the +format of this list may be found in section |txtfmt-fmt-clr-spec-list|. + +There are actually 15 separate mappings that may be used to insert Txtfmt +tokens: 1 may be used only in insert mode; the other 14 are used only in +normal mode. The main reason there are so many normal mode mappings is that +Vim provides many ways to go from normal mode to insert mode, each of which +may be more suitable than the others in a given context. Suppose, for example, +that you are in normal mode and you wish to insert the "no format" token at +the end of the current line. You might consider jumping to the end of the line +with $, then invoking a Txtfmt normal mode mapping to insert the token. If, +for the sake of example, Txtfmt provided only one normal mode mapping, which +inserted tokens before the cursor position, you would have a problem. The +token would be inserted prior to the last character on the line, when in fact, +it needed to go after the last character on the line. In this case, you could +circumvent the difficulty by hitting A in normal mode (instead of $) and using +the Txtfmt insert mode mapping to insert the desired token, but this would +require separate operations for entering and exiting insert mode. What would +be preferable here is a Txtfmt normal mode mapping that functions in a manner +analogous to Vim's normal mode 'A' command for entering insert mode. + +Of course, we could construct other examples, which call for mappings that +insert tokens at the beginning of a line (analogous to Vim's normal mode 'I' +command), or mappings that replace the N characters under the cursor with the +tokens inserted (analogous to Vim's normal mode 's' command), and so on... By +the same token, there may be times when you wish to remain in insert mode +after the tokens have been inserted, and times when you wish simply to insert +the tokens and return to normal mode. The Txtfmt insert-token mappings listed +below provide you with all of those capabilities and more. + +Although the number of normal mode mappings may appear large at first glance, +their default key sequences are intuitive, once you understand that they all +are constructed according to the following pattern: + + [{end-in-normal}]{vim-enterinsert-char} + +where {vim-enterinsert-char} is one of Vim's single-letter normal mode +commands for entering insert mode: > + i, I, a, A, o, O, s +and the optional {end-in-normal} is the letter 'v', which is simply a flag +indicating that you wish to return to normal mode after the insertion. + +There is only one insert mode mapping. Its default map sequence was selected +because it is (in the author's opinion, at least) fairly easy to type, and not +something you are otherwise likely to type in insert mode. + +All 15 of the insert-token mappings are listed below, with similar ones +grouped together to avoid repetition. For each mapping, 2 forms are shown: + 1. The default map sequence + 2. The form, used only for customization +For details on customizing maps, refer to section |txtfmt-map-config|. + +Mapping list *txtfmt-ins-tok-map-list* + +i TxtfmtInsertTok_i *\i* *txtfmt-ins-tok-i* +I TxtfmtInsertTok_I *\I* *txtfmt-ins-tok-I* +a TxtfmtInsertTok_a *\a* *txtfmt-ins-tok-a* +A TxtfmtInsertTok_A *\A* *txtfmt-ins-tok-A* +o TxtfmtInsertTok_o *\o* *txtfmt-ins-tok-o* +O TxtfmtInsertTok_O *\O* *txtfmt-ins-tok-O* +s TxtfmtInsertTok_s *\s* *txtfmt-ins-tok-s* + +Available in normal mode only. Insert a sequence of Txtfmt tokens in the +current buffer and remain in insert mode. The various mappings differ only in +how they enter insert mode. Conceptually, they all do the following: + + -Prompt user to enter a |txtfmt-fmt-clr-spec-list| at the command line + -Translate the entered text into a Txtfmt token sequence + -Enter insert mode via the {vim-enterinsert-char} specified in the + default map sequence. + -Insert the token sequence (with =) + -Perform any cursor adjustment requested by the presence of a dot + ('.') in the |txtfmt-fmt-clr-spec-list|. + -Remain in insert mode. + +vi TxtfmtInsertTok_vi *\vi* *txtfmt-ins-tok-vi* +vI TxtfmtInsertTok_vI *\vI* *txtfmt-ins-tok-vI* +va TxtfmtInsertTok_va *\va* *txtfmt-ins-tok-va* +vA TxtfmtInsertTok_vA *\vA* *txtfmt-ins-tok-vA* +vo TxtfmtInsertTok_vo *\vo* *txtfmt-ins-tok-vo* +vO TxtfmtInsertTok_vO *\vO* *txtfmt-ins-tok-vO* +vs TxtfmtInsertTok_vs *\vs* *txtfmt-ins-tok-vs* + +Available in normal mode only. Insert a sequence of Txtfmt tokens in the +current buffer and return to normal mode. The various mappings differ only in +how they enter insert mode. Conceptually, they all do the following: + + -Prompt user to enter a |txtfmt-fmt-clr-spec-list| at the command line + -Translate the entered text into a Txtfmt token sequence + -Enter insert mode via the {vim-enterinsert-char} specified in the + default map sequence. + -Insert the token sequence (with =) + -Perform any cursor adjustment requested by the presence of a dot + ('.') in the |txtfmt-fmt-clr-spec-list|. + -Exit insert mode. + + ** + TxtfmtInsertTok_i *txtfmt-ins-tok-CTRL-\_CTRL-\* + +Available in insert mode only. Insert a sequence of Txtfmt tokens in the +current buffer and remain in insert mode. The mapping works like this: + + -Prompt user to enter a |txtfmt-fmt-clr-spec-list| at the command line + -Translate the entered text into a Txtfmt token sequence + -Insert the token sequence (with =) + -Perform any cursor adjustment requested by the presence of a dot + ('.') in the |txtfmt-fmt-clr-spec-list|. + -Remain in insert mode. + +IMPORTANT NOTE: Txtfmt will generate an error if |txtfmt-fmt-clr-spec-list| +specifies any colors that are inactive under the effective color masks. See +|txtfmtFgcolormask| or |txtfmtBgcolormask| for details on configuring color +masks. + +JUMPING TO TOKENS *txtfmt-jump-to-tok* + +The Txtfmt syntax plugin conceals the special tokens delineating the +format/color regions. Normally, you will see such tokens only in 3 cases: + 1. when a region containing them is visually selected + 2. when the token is serving no purpose in the buffer + Rationale: When a default ("no format" or "no color") token, + normally used to terminate a region, appears where there is no + region to terminate, Txtfmt intentionally leaves the token visible + so that you might notice and remove it. + 3. when a fg or bg color token is disabled by |txtfmtFgcolormask| or + |txtfmtBgcolormask| + +Although this behavior is generally what you want, it can make it difficult to +locate tokens after you have inserted them. There are many reasons why you +might wish to locate a concealed token: to remove it, to change it to a +different token, to insert another token immediately before or after it, +etc... To simplify the task of locating such tokens, Txtfmt provides various +mappings for jumping to them. These "jump to token" commands are analogous to +various Vim motion commands: e.g., [{, ]}, f, F, t, T, etc... + +There are exactly 48 such mappings, but their default map sequences are +constructed according to a pattern, which renders memorization of the +individual mappings unnecessary. (The desired mapping can be easily deduced +according to the pattern.) + + {direction}[{till-flag}][{target-modifier}]{target} + +The definitions of the fields in the pattern given above are as follows: + {direction} + [ (search backwards) + ] (search forwards) + {till-flag} + Optional flag consisting of the letter 't'. If present, + indicates that the sought position is 1 character position + closer to the starting point of the jump than it would have + been without the {till-flag}. Behavior is analogous to that of + Vim's normal mode T operator; i.e., jump is "till" the target + location. + {target-modifier} + b (begin region tokens) + e (end region tokens) + Note: While there are a number of tokens that can begin a + format or color region, each of the 3 orthogonal region types + has only one "end region" token: the one specified by a dash + (`-') in the |txtfmt-fmt-spec| or |txtfmt-clr-spec|. + {target} + f (format tokens) + c (fg color tokens) + k (bg color tokens) + a (any format or fg/bg color token) + + Examples: > + ]f Jump forward to next token that begins or ends a + format region + ]ef Jump forward to next "end format region" token + [tbc Jump backward "till" previous "begin fg color region" + token + + +All of the jump-to-token mappings are defined for normal, visual, and +operator-pending modes, and all can take a count, which causes them to jump to +the [count] token of the specified type in the specified direction. Whether +the search for tokens wraps around the start or end of the buffer is +determined by the 'wrapscan' option. + +SPECIAL NOTE ON VISUAL MODE + The visual-mode maps correctly extend the visual selection for linewise, + characterwise and blockwise selections. + +SPECIAL NOTE ON OPERATOR-PENDING MODE + By default, when used with an operator such as 'd', 'c' or 'y', a + jump-to-token map behaves in an "exclusive" manner; i.e., the character + landed upon is not deleted, changed or yanked. If you wish to make these + motions work more like the builtin 'f', 't', 'F' and 'T' commands, simply + hit 'v' between the operator and the jump-to-token map. (For details, see + |o_v| in the Vim help.) + +All 48 of the jump-to-token mappings are listed below. For each mapping, 2 +forms are shown: + 1. The default map sequence + 2. The form, used only for customization +For details on customizing maps, refer to section |txtfmt-map-config|. + +Mappings *txtfmt-jump-to-tok-maps* + +[f TxtfmtBckToFmtTok *[f* *txtfmt-bck-to-fmt-tok* + Jump to [count] previous format begin/end region token +]f TxtfmtFwdToFmtTok *]f* *txtfmt-fwd-to-fmt-tok* + Jump to [count] next format begin/end region token +[c TxtfmtBckToClrTok *[c* *txtfmt-bck-to-clr-tok* + Jump to [count] previous fg color begin/end region token +]c TxtfmtFwdToClrTok *]c* *txtfmt-fwd-to-clr-tok* + Jump to [count] next fg color begin/end region token +[k TxtfmtBckToBgcTok *[k* *txtfmt-bck-to-bgc-tok* + Jump to [count] previous bg color begin/end region token +]k TxtfmtFwdToBgcTok *]k* *txtfmt-fwd-to-bgc-tok* + Jump to [count] next bg color begin/end region token +[a TxtfmtBckToAnyTok *[a* *txtfmt-bck-to-any-tok* + Jump to [count] previous format/color begin/end region token +]a TxtfmtFwdToAnyTok *]a* *txtfmt-fwd-to-any-tok* + Jump to [count] next format/color begin/end region token + +[bf TxtfmtBckToFmtBegTok *[bf* *txtfmt-bck-to-fmt-beg-tok* + Jump to [count] previous format begin region token +]bf TxtfmtFwdToFmtBegTok *]bf* *txtfmt-fwd-to-fmt-beg-tok* + Jump to [count] next format begin region token +[bc TxtfmtBckToClrBegTok *[bc* *txtfmt-bck-to-clr-beg-tok* + Jump to [count] previous fg color begin region token +]bc TxtfmtFwdToClrBegTok *]bc* *txtfmt-fwd-to-clr-beg-tok* + Jump to [count] next fg color begin region token +[bk TxtfmtBckToBgcBegTok *[bk* *txtfmt-bck-to-bgc-beg-tok* + Jump to [count] previous bg color begin region token +]bk TxtfmtFwdToBgcBegTok *]bk* *txtfmt-fwd-to-bgc-beg-tok* + Jump to [count] next bg color begin region token +[ba TxtfmtBckToAnyBegTok *[ba* *txtfmt-bck-to-any-beg-tok* + Jump to [count] previous format/color begin region token +]ba TxtfmtFwdToAnyBegTok *]ba* *txtfmt-fwd-to-any-beg-tok* + Jump to [count] next format/color begin region token + +[ef TxtfmtBckToFmtEndTok *[ef* *txtfmt-bck-to-fmt-end-tok* + Jump to [count] previous format end region token +]ef TxtfmtFwdToFmtEndTok *]ef* *txtfmt-fwd-to-fmt-end-tok* + Jump to [count] next format end region token +[ec TxtfmtBckToClrEndTok *[ec* *txtfmt-bck-to-clr-end-tok* + Jump to [count] previous fg color end region token +]ec TxtfmtFwdToClrEndTok *]ec* *txtfmt-fwd-to-clr-end-tok* + Jump to [count] next fg color end region token +[ek TxtfmtBckToBgcEndTok *[ek* *txtfmt-bck-to-bgc-end-tok* + Jump to [count] previous bg color end region token +]ek TxtfmtFwdToBgcEndTok *]ek* *txtfmt-fwd-to-bgc-end-tok* + Jump to [count] next bg color end region token +[ea TxtfmtBckToAnyEndTok *[ea* *txtfmt-bck-to-any-end-tok* + Jump to [count] previous format/color end region token +]ea TxtfmtFwdToAnyEndTok *]ea* *txtfmt-fwd-to-any-end-tok* + Jump to [count] next format/color end region token + +[tf TxtfmtBckTillFmtTok *[tf* *txtfmt-bck-till-fmt-tok* + Jump "till" [count] previous format begin/end region token +]tf TxtfmtFwdTillFmtTok *]tf* *txtfmt-fwd-till-fmt-tok* + Jump "till" [count] next format begin/end region token +[tc TxtfmtBckTillClrTok *[tc* *txtfmt-bck-till-clr-tok* + Jump "till" [count] previous fg color begin/end region token +]tc TxtfmtFwdTillClrTok *]tc* *txtfmt-fwd-till-clr-tok* + Jump "till" [count] next fg color begin/end region token +[tk TxtfmtBckTillBgcTok *[tk* *txtfmt-bck-till-bgc-tok* + Jump "till" [count] previous bg color begin/end region token +]tk TxtfmtFwdTillBgcTok *]tk* *txtfmt-fwd-till-bgc-tok* + Jump "till" [count] next bg color begin/end region token +[ta TxtfmtBckTillAnyTok *[ta* *txtfmt-bck-till-any-tok* + Jump "till" [count] previous format/color begin/end region token +]ta TxtfmtFwdTillAnyTok *]ta* *txtfmt-fwd-till-any-tok* + Jump "till" [count] next format/color begin/end region token + +[tbf TxtfmtBckTillFmtBegTok *[tbf* *txtfmt-bck-till-fmt-beg-tok* + Jump "till" [count] previous format begin region token +]tbf TxtfmtFwdTillFmtBegTok *]tbf* *txtfmt-fwd-till-fmt-beg-tok* + Jump "till" [count] next format begin region token +[tbc TxtfmtBckTillClrBegTok *[tbc* *txtfmt-bck-till-clr-beg-tok* + Jump "till" [count] previous fg color begin region token +]tbc TxtfmtFwdTillClrBegTok *]tbc* *txtfmt-fwd-till-clr-beg-tok* + Jump "till" [count] next fg color begin region token +[tbk TxtfmtBckTillBgcBegTok *[tbk* *txtfmt-bck-till-bgc-beg-tok* + Jump "till" [count] previous bg color begin region token +]tbk TxtfmtFwdTillBgcBegTok *]tbk* *txtfmt-fwd-till-bgc-beg-tok* + Jump "till" [count] next bg color begin region token +[tba TxtfmtBckTillAnyBegTok *[tba* *txtfmt-bck-till-any-beg-tok* + Jump "till" [count] previous format/color begin region token +]tba TxtfmtFwdTillAnyBegTok *]tba* *txtfmt-fwd-till-any-beg-tok* + Jump "till" [count] next format/color begin region token + +[tef TxtfmtBckTillFmtEndTok *[tef* *txtfmt-bck-till-fmt-end-tok* + Jump "till" [count] previous format end region token +]tef TxtfmtFwdTillFmtEndTok *]tef* *txtfmt-fwd-till-fmt-end-tok* + Jump "till" [count] next format end region token +[tec TxtfmtBckTillClrEndTok *[tec* *txtfmt-bck-till-clr-end-tok* + Jump "till" [count] previous fg color end region token +]tec TxtfmtFwdTillClrEndTok *]tec* *txtfmt-fwd-till-clr-end-tok* + Jump "till" [count] next fg color end region token +[tek TxtfmtBckTillBgcEndTok *[tek* *txtfmt-bck-till-bgc-end-tok* + Jump "till" [count] previous bg color end region token +]tek TxtfmtFwdTillBgcEndTok *]tek* *txtfmt-fwd-till-bgc-end-tok* + Jump "till" [count] next bg color end region token +[tea TxtfmtBckTillAnyEndTok *[tea* *txtfmt-bck-till-any-end-tok* + Jump "till" [count] previous format/color end region token +]tea TxtfmtFwdTillAnyEndTok *]tea* *txtfmt-fwd-till-any-end-tok* + Jump "till" [count] next format/color end region token + + +BUILDING COMPLEX MAPPINGS FROM PRIMITIVES *txtfmt-user-maps* + +The insert-token maps discussed in a preceding section permit the insertion of +a single sequence of fmt/clr tokens at a fixed location relative to the cursor +position. A common scenario, however, is one in which you wish to perform +multiple insertions, possibly interspersed with cursor movements and other Vim +commands. Suppose, for example, that you often find yourself highlighting an +entire line in bold,italic, and then using :ce to center the text on the line. +You could accomplish this using only Vim commands and insert-token maps as +follows: > + + Hit \vI and enter fmt spec "fbi" at the prompt + Hit \vA and enter fmt spec "f-" at the prompt + Execute :ce[nter] at the command-line + +While this approach works, it could become tedious if you do it often. A +better approach would be to create a single mapping that executes the entire +sequence of steps shown above. To facilitate creation of such maps, Txtfmt +provides a mechanism known as "user-maps". User-map creation is requested +through the "old-style" Vim array |txtfmtUsermap{}|. The first element of the +array is txtfmtUsermap{1} and the last element is txtfmtUsermap{N}, where N is +determined from option variable |txtfmtUsermaplimit|. It is not necessary to +set every element between indices 1 and N. You may define as many or as few +maps within that range as you like. Only the elements that have been set will +cause a map to be created. Moreover, both |txtfmtUsermap{}| and +|txtfmtUsermaplimit| may be set either globally or buffer-locally. As a +general rule, the global elements define mappings that should be available in +any type of file, whereas buffer-local elements would define more specialized +maps: e.g. maps that are used only when using Txtfmt within C comments in +buffers whose filetype is set to "c.txtfmt". For a more detailed look at +possible usage scenarios involving buf-local assignment to |txtfmtUsermap{}| +elements, see |txtfmt-buflocal-user-map|. + +txtfmtUsermap{} element format *txtfmt-user-map-fmt* + +Each element of the |txtfmtUsermap{}| array is a string, which specifies a +single normal or insert mode map to be created in the Txtfmt buffer. The +format of the string is as follows: + + "{map-cmd} {map-lhs} {map-rhs}" + +The descriptions of the fields in braces are as follows: + + {map-cmd} + The map creation command used by Txtfmt to create the mapping. + Currently, the following commands are supported: + imap, inoremap, nmap, nnoremap + {map-lhs} + Everything following {map-cmd} up to the first whitespace character + that is not preceded by a literal CTRL-V represents the {lhs} of the + mapping to be created. + {map-rhs} + The remainder of the string represents the {rhs} of the mapping to be + created. May contain special user-map expansion macros within <<...>> + requesting Txtfmt "insert-token" or "jump-to-token" functionality. + (See section |txtfmt-user-map-expansion| below.) + +MAP-LHS DETAILS +Note that {map-lhs} should appear exactly as it would if the mapping were +being created without the aid of Txtfmt; i.e., just as it would if you were +creating the map with an :exe[cute] on the |txtfmtUsermap{}| string. This +means, for example, that whitespace may be included in the {lhs} if it is +escaped by a literal CTRL-V, and that the special key notation sequences (e.g. +, , , etc...) may be used in place of the corresponding key +codes. For more details, read the following sections in the Vim documentation: + |:map-special-keys| + |:map-special-chars| + +MAP-RHS DETAILS +Vim's normal map processing rules apply to {map-rhs} as well; however, Txtfmt +performs a special pre-processing step on {map-rhs} before it is used to +define the map; in particular, it scans for and processes the special Txtfmt +expansion macros (enclosed in <<...>>), which are the subject of the following +section. To include a literal `<' in {map-rhs}, use the special notation +discussed in the Vim help for commands and mappings and documented in the +|key-notation| table. + +Example: > + EOF + produces a mapping that looks like + < to get a literal `>' +into {map-rhs}. The symbol is special only within a Txtfmt user-map +expansion macro. (Although it is unlikely you would ever need to include a +literal `>>' within an expansion macro, use of non-alphanumeric characters as +part of a color name is currently not prohibited.) + +Example: > + <colorname>> + inserts token for fg color whose user-defined pattern matches + "strange>>color>>name" + +User-map expansion macros *txtfmt-user-map-expansion* + +When Txtfmt encounters `<<' in {map-rhs}, it attempts to process a user-map +expansion macro. To facilitate the addition of user-map macros in future +versions of txtfmt, the following template has been devised: + + <<{mode}{cmd}[{cmd-modifiers}][:{cmd-parameters}]>> + +The descriptions of the fields in braces are as follows: + + {mode} Single character representing the expected + mode at the instant the `<<' is encountered. + i = insert + n = normal + o = operator-pending + v = visual + Note: The mode flag may specify a mode + different from the mode indicated by {map-cmd} + (discussed in the previous section). Consider + that a single normal or insert mode mapping + may cause multiple modes to be entered and + exited as the rhs of the mapping is traversed. + This flag specifies the "instantaneous" mode. + [{count}] Optional count to be applied to {cmd}. + Note: Not all expansion macros accept a count. + See command-specific documentation below to + determine which ones do. + {cmd} A character or characters indicating the + Txtfmt functionality that is required. + Currently, all {cmd}'s are indicated by a + special character, which, in most cases, + corresponds to the first character of the + associated default map: + \ Insert-token + `[' or `]' Jump-to-token + [{cmd-modifiers}] Optional data modifying the behavior of the + {cmd} in some way. (See command-specific + documentation below.) + [{cmd-parameters}] Permits data that would normally be entered at + a prompt at the time of map execution to be + specified in advance (i.e. at the time of map + creation). + +The specific forms of user-map macro are shown below, grouped according to the +Txtfmt functionality involved. + +INSERT TOKEN MACROS + <> + <> + +Both forms of "insert-token" macro contain a |fmt-clr-spec-list|, describing +the sequence of fmt/clr tokens to be inserted and optionally specifying a +cursor offset with a single dot somewhere in the otherwise comma-separated +list. The first form is used when the instantaneous mode within {map-rhs} is +"insert", whereas the more complicated second form is needed when the +instantaneous mode is "normal". (See earlier description of {mode} flag.) + +There are 3 additional parameters that apply only to the normal mode form of +the insert-token expansion sequence: + + {count} is an optional value, specifying the count to be applied to the + enter-insert command. Specifying a count in this manner is analogous to + typing a count prior to executing the corresponding insert-token map in + normal mode. + + {vim-enterinsert-char} is one of Vim's single-letter normal mode commands + for entering insert mode: > + i, I, a, A, o, O, s +< Proper choice of enter-insert command can simplify the mapping by + minimizing the number of movement commands required to position the cursor + prior to token insertion. + + {end-in-normal} is the letter 'v', which, when present, serves as a flag + indicating that you wish to return to normal mode after the insertion. The + flag is provided as a convenience to minimize the number of "" + characters that need to be embedded in {map-rhs}. + +Examples: > + <> Starting in insert mode, insert "no fg color" token at + cursor location. + <> Starting in normal mode, insert "bold,italic" token at + beginning of line. Ends in insert mode. + <> Starting in normal mode, 'substitute' the next 5 + characters with a single "underline" token. Ends in + normal mode. + +JUMP TO TOKEN MACROS + + <<{mode}[{count}]{default-jump-to-tok-map}>> + +A jump-to-token macro is created simply by prepending the {mode} flag and the +optional {count} to the default mapping representing the desired jump, and +enclosing the whole string within `<<' and `>>'. For a detailed discussion of +the various forms of jump-to-token mappings and a listing of the default +mappings, see |txtfmt-jump-to-tok| and |txtfmt-jump-to-tok-maps|. + +Examples: > + <> Jump forward to next bg color region token + <> Jump backward to previous "end fg color region" token + <> Jump forward "till" 3rd "begin format region" token + +User-map examples *txtfmt-user-map-examples* + +The following concrete examples should help to clarify the use of the various +forms of user-map expansion sequence. You should be able to copy any that +appear useful to your .vimrc. + + + " Map CTRL-B in insert mode to start and terminate a 'bold' region, + " leaving the cursor positioned within the region, ready to type bold + " text. + " Hint: Similar maps might be created for underline and italic > + let g:txtfmtUsermap1 = 'inoremap <>' +< + " Map CTRL-\f in insert mode to end current format region. > + let g:txtfmtUsermap2 = 'inoremap f <>' +< + " Map CTRL-\k in insert mode to end current bg color region. > + let g:txtfmtUsermap3 = 'inoremap k <>' +< + " Map \t in normal mode to embolden, underline and center (i.e. + " 'title-ize') the current line > + let g:txtfmtUsermap4 = + \'nnoremap t <><>:ce' +< + " Map \cf in normal mode to change all text within the current format + " region (without deleting the tokens that begin and end the region). + " Note: Since the default jump-to-token mappings are used in the rhs + " (rather than the special expansion macros), nmap must be used (rather + " than nnoremap). + " Note: The reason the ]f does not cause the format 'end region' token to + " be deleted is that the operator-pending jump-to-token maps work + " 'exclusive-ly' when there is no 'v' between operator and motion. + " :help exclusive > + let g:txtfmtUsermap5 = 'nmap cf [tbfc]f' +< + " Same as preceding map but for current fg color region. + " Note: This one demonstrates the use of the 'jump-to-token' expansion + " macros. > + let g:txtfmtUsermap6 = 'nnoremap cc <>c<>' +< + " Map bw in normal mode to embolden the word under the + " cursor. (The extra complexity is needed to ensure that you can invoke + " with cursor anywhere on the word.) > + let g:txtfmtUsermap7 = + \'nnoremap bw :if col(".")!=1 && ' + \.'getline(".")[col(".")-2]=~"\\w"exe "norm! b"' + \.'endif<>e<>b' +< + " Map \vf in normal mode to select all of the current format region + " visually. + " Note: Unlike the earlier one for changing the text in the current + " format region, this one doesn't constrain the backwards jump to a + " 'begin region' token; hence, it will also highlight the text between + " regions if the cursor lies between regions at the time the map is + " invoked. > + let g:txtfmtUsermap8 = 'nnoremap vf <>v<>' +< + " Map vf in insert mode to do the same in insert mode > + let g:txtfmtUsermap9 = 'inoremap vf <>lv<>' +< + " Map in normal mode to jump forward to the 3rd + " 'begin format region' token. (Not overly practical, but demonstrates the + " use of whitespace in the lhs, as well as the use of the optional count + " with the jump-to-token expansion macros.) > + let g:txtfmtUsermap10 = 'nnoremap <>' +< + " Map _ in normal mode to substitute the next 4 characters + " with a bold format token followed by a 'no format' token, leaving the + " cursor positioned between the two. + " (This map is not intended to be useful, but merely to demonstrate the + " specification of a count with an insert-token expansion macro.) > + let g:txtfmtUsermap11 = 'nnoremap _ <>' +< + " Map rb in normal mode to make the current line bold with a + " red background. > + let g:txtfmtUsermap12 = + \'nnoremap rb <><>' +< + +Using buf-local sets *txtfmt-buflocal-user-map* + +As mentioned in an earlier section, both txtfmtUsermap{} and +txtfmtUsermaplimit may be set either globally or buf-locally. Here's how it +works... When Txtfmt loads, it determines the effective value of +txtfmtUsermaplimit according to the following logic: > + if exists('b:txtfmtUsermaplimit') + let txtfmtUsermaplimit = b:txtfmtUsermaplimit + elseif exists('g:txtfmtUsermaplimit') + let txtfmtUsermaplimit = g:txtfmtUsermaplimit + else + let txtfmtUsermaplimit = 25 "use default + endif + +Txtfmt then executes the following (pseudo-code) loop: > + for i = 1, txtfmtUsermaplimit + if exists('b:txtfmtUsermap{i}') + Define user-map b:txtfmtUsermap{i} + elseif exists('g:txtfmtUsermap{i}') + Define user-map g:txtfmtUsermap{i} + endif + endloop + +Several points to note: + -Leaving elements of txtfmtUsermap{} undefined is harmless. + -Setting b:txtfmtUsermaplimit effectively overrides any existing setting + of g:txtfmtUsermaplimit. + -Setting an element of b:txtfmtUsermap{} effectively hides the + corresponding element of g:txtfmtUsermap{}. + +You may be wondering why you would ever need to use the buffer-local versions +of txtfmtUsermap{} and txtfmtUsermaplimit. If you edit only one type of file +with Txtfmt, then the simplest approach would be to use only the global +versions. It is possible, however, that while you have some user-maps that are +generally useful, you also have some special-purpose user-maps, which are +useful only in a certain type of file. Defining such user-maps globally would +render the corresponding {map-lhs} unusable in files in which you would never +need the special-purpose user-map. I will now describe a strategy that uses +the buf-local user-map option variables to circumvent the problem just +described... + +STEP #1 +======= +In your .vimrc, define user-maps 1..N using g:txtfmtUsermap{} and set the +global version of txtfmtUsermaplimit to N. +Note: All of the maps defined in this manner should be general purpose maps, +which you might need in any Txtfmt buffer. + +Example: > + let g:txtfmtUsermap1 = '' + let g:txtfmtUsermap2 = '' + . + . + let g:txtfmtUsermapN = '' + let g:txtfmtUsermaplimit = N + +STEP #2 +======= +Create global functions that define special-purpose user maps N+1..M using the +buffer-local versions of txtfmtUsermap{} and set the buffer-local version of +txtfmtUsermaplimit to N+M. +Note: Since these functions do not overwrite previously set elements of the +g:txtfmtUsermap{}, the general-purpose user-maps will be left intact. + +Example: > + " This function defines special-purpose Txtfmt user-maps, useful only + " for *.abc files. + fu! Define_txtfmt_abc_usermaps() + let N = g:txtfmtUsermaplimit + let b:txtfmtUsermap{N+1} = '' + let b:txtfmtUsermap{N+2} = '' + . + . + let b:txtfmtUsermap{N+M} = '' + " Ensure that both the general-purpose and special-purpose + " user-maps are defined. + let b:txtfmtUsermaplimit = N+M + endfu + +STEP #3 +======= +Ensure that each special-purpose map function is called from an autocommand +specific to the filetype that will need the user-maps it defines. + +Note: It is necessary to define these autocommands prior to the ones that set +filetype to txtfmt, so that the changes to txtfmtUsermap{} occur before Txtfmt +is loaded. An easy way to ensure the proper sequence is to place the +autocommands that call the map definition functions within the same +autocommand group as the setfiletype autocommand (see example below). + +Example: > + " Inside ~/.vim/filetype.vim + augroup filetypedetect + au! BufRead,BufNewFile *.abc call Define_txtfmt_abc_usermaps() + au! BufRead,BufNewFile *.xyz call Define_txtfmt_xyz_usermaps() + . + . + " Set up detection for Txtfmt files + au! BufRead,BufNewFile *.abc setfiletype txtfmt + au! BufRead,BufNewFile *.xyz setfiletype txtfmt + au! BufRead,BufNewFile *.txt setfiletype txtfmt + augroup END + +It should be noted that the strategy outlined above is simply one of many +possibilities. A slight variation would be to replace the general-purpose +user-maps altogether with special-purpose ones for one or more filetypes. +Suppose, for example, that we knew we would use only special-purpose maps when +editing a '*.abc' file. In that case, we would simply omit all occurrences of +"N+" in Define_txtfmt_abc_usermaps() to ensure that the general-purpose maps +were overridden. + +CREATING TOKEN STRINGS *txtfmt-create-tok-str* +Txtfmt_GetTokStr({fmt-clr-spec-list}) *txtfmt-GetTokStr()* + This function accepts a |txtfmt-fmt-clr-spec-list| as input and + returns the corresponding sequence of Txtfmt tokens as a string. + Note: May be called only from a Txtfmt buffer. + +QUERYING TOKEN TYPE *txtfmt-query-tok-type* + +In most cases, the role played by a Txtfmt token will be evident from the +highlighting of the text that follows it. There are at least two scenarios, +however, in which the role played by a particular format or color token may +not be evident: 1) there is no text following the token; 2) adjacent format +and color tokens make it difficult to tell which is which, since the +formatting of the subsequent text does not depend upon the order of the +tokens. Although you can always look up the character code (obtained from +Vim's ga command) in the output of the |txtfmt-:ShowTokenMap| command, this +section presents some more convenient methods of determining a token's type... + +Token-descriptors *txtfmt-tok-descriptor* + +A {token-descriptor} is returned or displayed by all of the mappings, commands +and functions discussed in this section. A {token-descriptor} is simply a +string providing information about a particular character or token in a Txtfmt +buffer. Its format depends upon how the character is used in the buffer. The +possibilities are described below... + + format token + A |txtfmt-fmt-spec| in fiducial form (e.g. "fubi") + color token + A string of the form {type}{colornum}[ (inactive)], where {type} + indicates the type of color token ('c' => fg, 'k' => bg), {colornum} + is a value between 1 and 8 indicating the color's index, and the + optional " (inactive)" label, if present, indicates that the color is + disabled by the current color mask. (For details on color masks, see + |txtfmtFgcolormask| or |txtfmtBgcolormask|.) + non-Txtfmt char + A character code value in decimal format (e.g. 'a' => 97) + invalid char position + "NUL" (just like Vim's ga command) + + *\ga* *txtfmt-get-tok-info* +ga TxtfmtGetTokInfo + This mapping echoes the {token-descriptor} (discussed above) of the + character under the cursor. Analogous to Vim's builtin ga command, it + provides the most convenient way to obtain information about the token + under the cursor. The primary difference between it and the builtin + version is that the Txtfmt version is aware of the special role played + by Txtfmt formatting tokens. + + Note: The form of the mapping may be used for customization. + For details on customizing maps, refer to section |txtfmt-map-config|. + + *txtfmt-:GetTokInfo* +:GetTokInfo [ {line} {col} ] + This buffer-local command echoes the {token-descriptor} corresponding + to the character/token at the requested {line} and {col} in the + current buffer. If {line} and {col} are not supplied, the cursor + location is assumed; i.e., calling :GetTokInfo without args is + equivalent to executing the \ga mapping. + Note: If either {line} or {col} is specified, both must be specified. + Note: {col} is a 1-based byte index, such as would be returned by + Vim's col() function. Note that this index may differ from both the + virtual column index and the character index. + + *txtfmt-GetTokInfo()* +Txtfmt_GetTokInfo([{line}, {char}]) + !!!!!!!!!!!!!!!!!! + <<< DEPRECATED >>> + !!!!!!!!!!!!!!!!!! + + IMPORTANT NOTE: I have marked this function as deprecated because both + the mapping and command (discussed above) provide similar + functionality and are easier to use; however, on the off-chance that + someone created a mapping to this function in one of the initial + versions of Txtfmt, I have not yet removed it. Note that the 2nd + argument to this function is a character index, which is fundamentally + different from the byte index provided to the :GetTokInfo command. As + a general rule, supplying byte indices, (which may be obtained with + Vim's col() function), is easier for the plugin user. + + This function returns a string describing the token specified by + {line} and {char}. Note that both {line} and {char} are optional. + Either both or neither should be specified. If neither is specified, + the returned information will apply to the character under the cursor. + Otherwise, {char} is the 1-based index of a token on the line + specified by {line}. + Important Note: Because {char} is a character index, its value may + differ from the values returned by virtcol() (screen column index) and + col() (byte index). + The format of the return string depends upon the type of token + being queried, and is described above under |txtfmt-tok-descriptor|. + + +VIEWING TOKEN MAP *txtfmt-view-tok-map* + *txtfmt-:ShowTokenMap* +:ShowTokenMap + This buffer-local command echoes a table displaying the current + format/color token map in an intuitive and informative manner. + Note: Since a Txtfmt token map is specific to a buffer, the command + may be used only from a Txtfmt buffer. The table columns are slightly + different for formats and colors. Here is some sample output text, + which corresponds to the default |txtfmt-'tokrange'| setting... + + === FG COLORS === + char-nr description clr-pattern clr-def + 0xE000 no color - N.A. + 0xE001 Color1 ^\%(k\|bla\%[ck]\)$ #000000 + 0xE002 Color2 ^b\%[lue]$ #0000FF + . + . + 0xE008 Color8 ^w\%[hite]$ #FFFFFF + === FORMAT === + char-nr description fmt-spec + 0xE009 no format - + 0xE00A underline u + . + . + 0xE010 underline,bold,italic ibu + === BG COLORS === + char-nr description clr-pattern clr-def + 0xE011 no color - N.A. + *0xE012 Color1 (inactive) ^\%(k\|bla\%[ck]\)$ #000000 + 0xE013 Color2 ^b\%[lue]$ #0000FF + . + . + *0xE019 Color8 (inactive) ^w\%[hite]$ #FFFFFF + + Several points to note: + + The fmt-spec column gives the set of characters (in fiducial form) + that you would put after the 'f' in a |txtfmt-fmt-spec| to insert the + corresponding format token. + Note: The order of the characters in the set is not significant; i.e., + "ubi" is equivalent to "ibu" and "bui". + + The clr-pattern column represents the Vim regular expression that is + used to recognize a color name in a |txtfmt-clr-spec|. + + The clr-def column under the "COLORS" section represents the actual + color definitions supplied to Vim via the :highlight command: e.g. + hi guifg={clr-def} + hi ctermfg={clr-def} + For GUI'S, the {clr-def} is either a symbolic color name (e.g., + DarkBlue) or a literal RGB value (e.g., #FF0000). For cterm's, it + could be a color number or a symbolic color name. + Note: All of the values in the final 2 columns of the "FG/BG COLORS" + sections may be overridden by the user. For details, see + |txtfmt-color-config|. + + The presence of an asterisk before the char-nr and the string + "(inactive)" following the color description indicates that a color + has been rendered inactive by the fg or bg colormask currently in + effect. For details, see |txtfmtFgcolormask|. + + The char-nr column displays the character code value in the number + format used to set |txtfmt-'tokrange'|. + + +TESTING CURRENT SETTINGS *txtfmt-test-cur-settings* + *txtfmt-:MakeTestPage* +:MakeTestPage [{modeline}] + This buffer-local command creates a scratch buffer containing a + Txtfmt "test page". The Txtfmt options used to create the test page + take into account any global option settings, as well as any + |txtfmt-modeline| settings specified on the command line as arguments + to the command. Example: > + + :MakeTestPage tokrange=180S escape=bslash nested +< + This creates a test page with the |txtfmt-'tokrange'| option set to + "180S", the |txtfmt-'escape'| option set to "bslash" and the + |txtfmt-'nested'| option turned on. All other Txtfmt options will have + the values set globally, or default values if the global option has + not been set. + Note: Since the test page is created in a new scratch buffer, + buffer-local option settings are not considered. + + This command provides a quick and easy way to visualize the effects of + a particular combination of option values. Suppose, for example, that + the default 'tokrange' option setting yields Txtfmt tokens that cannot + be concealed by Vim, due to their use as control sequences by a + particular terminal. In this case, you might wish to run :MakeTestPage + iteratively, each time supplying a different value for 'tokrange', + until you find one that appears to work. + + In addition to displaying all available formats and colors (both + foreground and background if applicable), the test page attempts to + show how things like token escaping and nesting of Txtfmt regions + within non-Txtfmt regions will (or won't) work, given the applicable + option settings. The text in the test page buffer is rather verbose, + and is intended as an aid to the new user. Although the primary + purpose of the test page is to provide information, the buffer created + is a fully valid Txtfmt buffer; hence, you are free to make changes in + it, using it as a scratchpad for testing the various Txtfmt mappings. + + Note: If the text in the buffer does not appear to be highlighted at + all, it may be that you have not installed Txtfmt properly. (See + section |txtfmt-installation|.) Currently, the :MakeTestPage command + loads the Txtfmt plugin files by executing the following command in + the scratch buffer: > + + :set filetype=txtfmt + +< On a typical system (one on which filetype and syntax are both + enabled), this results in a :runtime sourcing of both + ftplugin/txtfmt.vim and syntax/txtfmt.vim. Of course, if you have + syntax and/or filetype plugins disabled, you will not be able to view + the test page as it was meant to be viewed... + +TRANSLATING TOKEN RANGE *txtfmt-move-tok-range* + *txtfmt-:MoveStartTok* +:MoveStartTok {new-starttok} [{old-vim-ver}] + NOTE: This command changes the current buffer! + + This buffer-local command relocates the Txtfmt token range in use in + the current buffer from its current location to {new-starttok}. Its + purpose is to provide an easy way to change the |txtfmt-'tokrange'| + option value in a file that already contains a large number of Txtfmt + tokens. + + The {new-starttok} argument specifies the character code to be used as + the start of the new token range. It should be a decimal or + hexadecimal value, which is valid for the 'encoding' in effect. + Note: You should NOT append the {formats} specifier to the numeric + starttok value; the current value will be used. (See + |txtfmt-'tokrange'| for a description of the {formats} specifier.) + + The optional {old-vim-ver} argument allows you to specify the version + of Vim that was used to create the file being changed. This could be + important if the following conditions are met: + -"Long" formats are in effect. + -The version of Vim used to create the file differs from the + current version. + -Only one of these versions supports the undercurl attribute. + + Txtfmt will not attempt to move characters in the undercurl range if + the version of Vim used to create the file does not support undercurl. + Txtfmt will, on the other hand, move such tokens if the version used + to create the file supports undercurl, and undercurl has not been + explicitly disabled with the |txtfmt-'undercurl'| option. Thus, it is + possible to translate undercurl tokens even with a version of Vim that + doesn't support undercurl. In other words, Txtfmt uses the + {old-vim-ver} argument to determine the "shape" of the Txtfmt token + range that needs to be translated. + Note: If the optional {old-vim-ver} argument is omitted, Txtfmt + assumes the buffer was created with the current version of Vim. + + Since Txtfmt was first released after Vim began supporting undercurl + (in version 7.0), the {old-vim-ver} argument can generally be omitted, + and is included only for the sake of completeness. Note: The format of + {old-vim-ver} is identical to the format of v:version; i.e., Vim major + version number times 100 plus Vim minor version number. e.g., + Vim 7.1 ==> "701" + + If the |txtfmt-'escape'| option is set to something other than "none", + :MoveStartTok takes the current escape method into account, and + attempts to do whatever makes sense with regard to escaped and + escaping characters. Consider that escaping is used to prevent special + interpretation of characters. When the Txtfmt token range moves, some + of the characters that would have been interpreted specially under the + old token range are not special at all under the new range, and hence, + no longer require escaping. Similarly, characters that were not + special at all under the old token range lie squarely within the new + token range, and hence require escaping to prevent their being + interpreted specially. + + Here's how Txtfmt handles token translation when escaping is in + effect... Characters that are not currently special, but will be after + the move, are escaped. Tokens that are currently escaped are not + translated. Characters that are currently escaped to prevent special + interpretation, but will not need to be escaped after the translation, + have their escaping characters removed. + + Note: In the context of the preceding paragraph, a character is + considered to be "special" if it could be interpreted as either a + Txtfmt token or an escaping character. For details on what constitutes + an escaping character, see |txtfmt-'escape'|. + + If there is a |txtfmt-modeline| containing a tokrange option setting + in the current buffer, it will be altered to reflect the new token + range. After the changes have been made to the buffer, the filetype + and syntax plugins are automatically reloaded to cause the new + tokrange setting to take effect. As a result, there should generally + be no visible change to the buffer as a result of executing the + :MoveStartTok command. + + Note: As previously mentioned, :MoveStartTok alters the current + buffer. Txtfmt will save the buffer after performing the token + translation unless there were unsaved changes when the command was + invoked. In that case, Txtfmt assumes that the user was not yet ready + to save the existing changes and leaves the buffer modified. Of + course, if you don't like the changes made by the :MoveStartTok + command, you can always undo them, regardless of whether or not the + changes have been saved. + + Examples of use: + Move current tokrange to start of "Private Use Area" > + :MoveStartTok 0xE000 +< + Move current tokrange to 190 (in file created with Vim 6.3) > + :MoveStartTok 190 603 +< + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: diff --git a/txtfmt/ftplugin/txtfmt.vim b/txtfmt/ftplugin/txtfmt.vim new file mode 100755 index 0000000..9cc1aa6 --- /dev/null +++ b/txtfmt/ftplugin/txtfmt.vim @@ -0,0 +1,2672 @@ +" Txtfmt: Set of Vim plugins (syntax, ftplugin, plugin) for creating and +" displaying formatted text with Vim. +" File: This is the txtfmt ftplugin file, which contains mappings and +" functions for working with the txtfmt color/formatting tokens. +" Creation: 2004 Nov 06 +" Last Change: 2008 May 10 +" Maintainer: Brett Pershing Stahlman +" License: This file is placed in the public domain. + +"echoerr "Sourcing ftplugin..." + +" Let the common code know whether this is syntax file or ftplugin +let s:script_name = 'ftplugin' +" Function: s:Add_undo() <<< +" Purpose: Add the input string to the b:undo_ftplugin variable. (e.g., unmap +" commands, etc...) +" Return: none +" NOTE: This cannot be in common config, since it is called even when common +" config is being skipped for this script. +fu! s:Add_undo(s) + if !exists('b:undo_ftplugin') + let b:undo_ftplugin = '' + let add_sep = 0 + elseif b:undo_ftplugin !~ '^\s*$' + let add_sep = 1 + else + let add_sep = 0 + endif + let b:undo_ftplugin = b:undo_ftplugin.(add_sep!=0?'|':'').a:s +endfu +" >>> +" plugin load considerations <<< +" Important Note: I intentionally skip the standard check for +" exists("b:did_ftplugin") because I want to make it easy for txtfmt user to +" load txtfmt ftplugin after another ftplugin. The standard check would +" make this more difficult. +" NOTE: Vim bug? in ftplugin.vim will cause error if you set b:undo_ftplugin +" but not b:did_ftplugin. +" Yes. It is a bug. It's been fixed, but don't count on it, since we want it +" to work for version 6.3... +let b:did_ftplugin = 1 +" Don't source the ftplugin again for same buffer, as it will cause errors due +" to . Note that this is important now that I've removed the check on +" b:did_ftplugin. +if exists("b:loaded_txtfmt") + finish +endif +let b:loaded_txtfmt = 1 +" Ensure that b:loaded_txtfmt is unlet whenever an ftplugin is about to be +" loaded. +" Note: Originally, I initialized b:undo_ftplugin to the empty string. The +" problem with doing so, however, is that it clobbers undo actions defined by +" a previously sourced ftplugin. Recall that txtfmt is designed to be used in +" combination with other filetypes (e.g., by using the dot-separated filetype +" name mechanism). +" Note: Don't unlet b:did_ftplugin, as standard ftplugin.vim does this +call s:Add_undo('unlet! b:loaded_txtfmt') +" >>> +" Set compatibility options <<< +" (set to Vim defaults to avoid errors with line continuation) +let s:save_cpo = &cpo +set cpo&vim +" >>> +" Common Configuration <<< +" Note: No point in having the modeline and/or global options processed by +" both the syntax and ftplugin files. +" IMPORTANT: Everything inside the "Common Configuration" fold should be +" identical between the syntax and ftplugin files. Keep in sync as changes are +" made... +if !exists('b:txtfmt_did_common_config') + " Note: An unlet of b:txtfmt_did_common_config is intentionally NOT added + " to b:undo_ftplugin. If it were unlet by b:undo_ftplugin, we would + " generally do common configuration twice, since the standard setup will + " source b:undo_ftplugin between the loading of syntax and filetype + " plugins. In order for the mechanism to work properly, + " b:txtfmt_did_common_config needs to be unlet before either syntax or + " filetype plugin is loaded. There are currently several ways to get this + " to happen: :e[dit], :b[delete], :Refresh + let b:txtfmt_did_common_config = 1 + " Ensure that the code within plugin/txtfmt.vim will be executed when the + " file is sourced. + let b:txtfmt_do_common_config = 1 + " TODO - Should we ensure that warning occurs for missing file? + runtime plugin/txtfmt.vim + " Make sure the common config doesn't run again + unlet b:txtfmt_do_common_config + +endif +" >>> +" Note on writing to b:undo_ftplugin <<< +" Regardless of who did the common config, it is ESSENTIAL that only the +" ftplugin write to b:undo_ftplugin, as ftplugin.vim function LoadFTPlugin() +" can run between sourcing of syntax file and sourcing of ftplugin file, and +" when it does, if b:undo_ftplugin exists, it will attempt to unlet both it +" and did_ftplugin. Problem is two-fold. Whatever syntax file has written to +" undo_ftplugin is lost, and worse, did_ftplugin may not exist at that point. +" If it does not, the "unlet" without the ! will generate error! Note: Bram +" said he would fix this in distributed version, but everyone will still have +" the old for awhile... +" >>> +" Script-local functions <<< +" Function: s:SID() <<< +" Purpose: Returns the SID number of this script. (help ) +" Note: This function is taken directly from the Vim help. +fu! s:SID() + return matchstr(expand(''), '\zs\d\+\ze_SID$') +endfu +" >>> +" Function: s:Prep_for_single_quotes() <<< +" Description: Double all single-quotes in the input string to prepare it for +" insertion within single quotes in a string that will be exec'ed. (:help +" literal-string) +fu! s:Prep_for_single_quotes(s) + return substitute(a:s, "'", "''", 'g') +endfu +" >>> +" Function: s:Move_cursor() <<< +" Purpose: Move the cursor right or left (within the current line) by the +" specified number of characters positions. Note that character positions are +" not always the same as screen columns, and are certainly not always the same +" as byte positions. +" IMPORTANT NOTE: This function was designed to effect the cursor position +" offset specified by the '.' in a fmt/clr spec. Because of the way the +" offsets are calculated in the caller (Insert_tokstr), the raw offset may +" need to be adjusted by one character due to (for example) the impossibility +" of positioning cursor before BOL. The logic for doing this should be +" multi-byte aware. Although such logic could be implemented in this +" function's caller, there is no reason to have it in both functions. The +" logic in Insert_tokstr may be simplified if this function is made simply to +" move as far as possible on the current line in the specified direction, in +" cases in which the offset as specified is too large. +" Note: As a general rule, character positions correspond to screen columns; +" in some cases, however, a single character may occupy multiple screen +" columns (e.g. a tab). Also, there may be cases in which multiple characters +" occupy a single screen column (e.g. a base character followed by combining +" characters). +" Method: Begin moving in the specified direction a single byte at a time. +" Each time a byte position change results in a screen position change of *at +" least* one column, consider that we have moved by exactly one character. +" This strategy ensures that a tab will be considered a single character +" (unless 'virtualedit' is set), as will the combination of a base character +" and its associated combining characters. (Note, however, that such special +" cases should not generally be an issue because this function was designed to +" handle offsets in sequences of txtfmt tokens, which should not include tabs +" or combining characters.) +" Inputs: +" off - Absolute value specifies number of character positions to +" move. Sign specifies the direction: +" pos. ==> right, neg. ==> left +" Return: The difference between the number of characters positions we were +" requested to move and the number of character positions we actually moved; +" i.e., zero indicates no problem executing the requested move. +" Error: Not possible. Since this function is used only internally, +" questionable inputs are silently converted to safe ones. +fu! s:Move_cursor(off) + " Convert the signed offset that was input to a direction and magnitude. + if a:off < 0 + let off = -a:off + let inc = -1 + else + let off = a:off + let inc = 1 + endif + " Are we moving rightward? + if inc == 1 + " Determine the current mode, which determines the right-most valid + " position on the line. + let mode = mode() + " Determine the last byte on which cursor may be positioned. + " (In insert mode, cursor may be positioned just after the last byte.) + let last_col = mode =~ 'i' ? virtcol('$') : virtcol('$') - 1 + "echomsg 'mode='.mode.' last_col='.last_col + endif + " Determine starting byte and screen column location. + let col = col('.') + let vcol_prev = virtcol('.') + " Keep track of how many character positions we've moved. + let moved = 0 + " Loop until we reach desired location + while moved < off + " Determine next byte position. (Next byte may or may not belong to a + " different character.) + let col = col + inc + " Make sure we're not about to move too far. + if inc == 1 + " Moving rightward + if col > last_col + " Can't move any further right! + break + endif + else + " Moving leftward + if col < 1 + " Can't move any further left! + break + endif + endif + " Adjust cursor pos to new byte position + call cursor(0, col) + " Determine new virtual col + let vcol = virtcol('.') + " Has screen position changed since last time through? + if vcol_prev != vcol + let moved = moved + 1 + endif + " Save the virtual column for next time + let vcol_prev = vcol + endwhile + " Return the number of character positions *not* moved (usually 0) + return off - moved +endfu +" >>> +" Function: s:Insert_tokstr() <<< +" Called_from: 'insert' or 'normal' mode mapping +" Purpose: Insert the fmt/clr token sequence specified by tokstr at the cursor +" location. Tokstr should be one of the following: +" -a literal (already translated) token sequence with offset prepended (just +" as it would have been returned by s:Translate_fmt_clr_spec) +" -an untranslated fmt/clr spec. +" -empty string (in which case, user will be prompted to enter a fmt/clr spec) +" After any required translations, this function ensures that the token +" sequence is inserted into the buffer (at a location determined by 'cmd' +" argument). It accomplishes the token insertion in one of two ways, depending +" upon current mode: +" 'insert': Returns the token sequence to be inserted (since the assumption is +" that this function has been invoked from an expression register +" (=)). +" 'normal': Uses the specified 'cmd' in conjunction with normal!. +" Originally, this function was also responsible for moving the cursor +" to the inter-token position indicated by the (optional) dot replacing one of +" the commas in the fmt/clr spec. This could be done only because all +" insertions (including ones from 'insert' mode) were accomplished within this +" function using normal! That implementation, however, (specifically, the +" use of normal! from within an insert-mode mapping) caused problems with +" respect to an active count applied to the preceding enter-insert command +" (the one that started the insert from which mapping was invoked). Thus, the +" cursor adjustment functionality has been moved to a separate function +" (s:Adjust_cursor), invoked from the mapping after this function has +" returned. Since the offset governing cursor adjustment is determined within +" this function, we use static variables s:Adjust_cursor_inv_off and +" s:Adjust_cursor_modestr to communicate the information to s:Adjust_cursor. +" Inputs: +" tokstr - raw (un-translated) fmt/clr spec to insert. If empty, user +" will be prompted for fmt/clr spec list. +" cmd - [iIaAoOs] - Vim command used (conceptually, at least) to +" insert the token string. Note that in some cases, the current +" mode will determine how the tokens are actually inserted. +" literal - nonzero if the input tokstr comprises an offset followed by +" the literal fmt/clr tokens; i.e., if it needs no translation. +" (This might be the case if this routine is called from a +" user-defined mapping.) +" Note: When this flag is set, the input tokstr should be in the +" form returned by s:Translate_fmt_clr_spec. +" end_in_norm - nonzero if it is desired that we end up in normal mode after +" map completion. (Most 'insert-token' commands have an +" alternate form that causes this flag to be set) +" v:count1 If greater than 1 and mode is normal, indicates the count to +" be used with the normal mode command used to insert the +" tokens. +" Note: Used only when a:1 is not supplied. +" a:1 If supplied, represents the count to be used with the normal +" mode command that inserts the tokens. (Needed when this +" function is called from a user-map) +" Return: Depends upon mode from which we are called: +" 'insert': Returns the inserted string, since we have been called from an +" expression register (=). +" 'normal': Returns an empty string. +" Error: Use echoerr with meaningful error message, as this function is +" generally invoked directly from mapping. +" Assumptions: Cursor position and mode unchanged from when map was invoked +" Vim trivia: col('.') and col('$') return 1 for empty line +fu! s:Insert_tokstr(tokstr, cmd, literal, end_in_norm, ...) + " Declare modifiable version of input parameter + let tokstr = a:tokstr + " Ensure that if we return without inserting any tokens (i.e. because user + " canceled the operation), s:Adjust_cursor will not attempt to do + " anything. + unlet! s:Adjust_cursor_inv_off + unlet! s:Adjust_cursor_modestr + " Assumption: If a:literal is true, input tokstr has already been + " translated and validated by s:Translate_fmt_clr_spec, and in fact, is + " exactly the string returned by that function; hence, validation will not + " be performed here. + if !a:literal + if tokstr == '' + " Prompt user for special fmt/clr spec string + let tokstr = s:Prompt_fmt_clr_spec() + " Strip surrounding whitespace, which is ignored. + " Note: Only surrounding whitespace is ignored! Whitespace not + " permitted within fmt/clr spec list. + let tokstr = substitute(tokstr, '^\s*\(.\{-}\)\s*$', '\1', 'g') + " Check for Cancel request + if tokstr == '' + " Note that nothing about position or mode has changed at this point + return '' + endif + endif + " Translate and validate fmt/clr spec + let tokstr = s:Translate_fmt_clr_list(tokstr) + if tokstr == '' + " Invalid fmt/clr sequence + echoerr "Insert_tokstr(): Invalid fmt/clr sequence entered: ".s:err_str + " Note that nothing about position or mode has changed at this point + return '' + endif + endif + " At this point, we have a translated fmt/clr spec comprising an offset + " followed by the actual fmt/clr token sequence. Extract the pieces from + " the string (internally generated - no need for validation) + let offset = substitute(tokstr, '\(\-\?[[:digit:]]\+\),\(.*\)', '\1', '') + let tokstr = substitute(tokstr, '\(\-\?[[:digit:]]\+\),\(.*\)', '\2', '') + " If user didn't specify offset, default is past inserted chars + if offset < 0 + " Get length of tokstr, noting that it may contain multi-byte + " tokens. + let offset = strlen(substitute(tokstr, '.', 'x', 'g')) + endif + + " Validate the command + " Get cmd string in standard form (strip surrounding whitespace) + " TODO - Perhaps this isn't necessary, since cmd is generated internally. + let cmd = substitute(a:cmd, '^\s*\(.\{-}\)\s*$', '\1', '') + if cmd !~ '^[iIaAoOs]$' + echoerr 'Insert_tokstr(): Invalid insert token cmd string: '.cmd + return '' + endif + " Validate current mode + let modestr = mode() + if modestr !~ '^[niR]$' + echoerr "Insert_tokstr(): May be called only from 'normal' and 'insert' modes." + return '' + endif + " Validation Complete! + " Build start/end mode string for convenience in testing + let modestr = modestr.(a:end_in_norm ? 'n' : 'i') + " Calculate offset from end of tokstr (inverse offset) + " a b c d e f + "0 1 2 3 4 5 6 : offset + "6 5 4 3 2 1 0 : inv_off + " We'll need to know number of *characters* (not bytes) in tokstr + let tokstrlen = strlen(substitute(tokstr, '.', 'x', 'g')) + let inv_off = tokstrlen - offset + " Make modestr and inv_off available for s:Adjust_cursor, which should run + " immediately after this function returns. + let s:Adjust_cursor_inv_off = inv_off + let s:Adjust_cursor_modestr = modestr + " Note on cursor positioning: <<< + " -The normal! insert commands will result in cursor being positioned 'ON' + " the last char inserted, regardless of which mode we start in. (For + " commands ending in insert mode, this means insert position just before + " the last char inserted.) + " TODO - Explain why - help on normal explains how the incomplete + " command (in this case enter-insert) will be terminated with . + " -The 'stopinsert' used when starting in insert and ending in normal mode + " will cause cursor to move back one, if we were in insert mode when + " mapping was invoked. + " -undo undoes everything in a 'normal' command as a unit, so we put the + " entire command for changing text into a single normal!. Note that since + " cursor movement commands are not part of undo mechanism, any necessary + " cursor adjustments may be made after the text change. + " -When starting in normal and ending in insert mode, there are 2 cases: + " 1) Last char inserted is end of line, so we must use startinsert! to + " start insert and end up with cursor after last inserted char. (Can't + " position cursor beyond it in normal mode.) + " 2) Last char inserted is not at end of line. Could move right one, then + " use 'startinsert'. + " >>> + if modestr[0] == 'n' + " Insert the tokens with the appropriate enter-insert command and + " count. Originally, I thought that doing it this way, rather than + " using explicit cursor() and setline() calls, made the insertions + " repeatable with repeat command (.); unfortunately, however, the + " repeat command works only for normal mode commands entered ON + " COMMAND LINE! While I could go back to using setline() and cursor(), + " the logic for simulating a particular type of enter insert command + " ([iIaAoOs]) with an optional count argument is a bit simpler this + " way. + " Determine the effective count (either from optional input or + " v:count1). + if a:0 > 0 + " Note: Counts are generated internally; hence, validation has + " been performed already. + let l:count = a:1 + else + let l:count = v:count1 + endif + " IMPORTANT NOTE: = works differently in actual normal mode + " from the way it works when used in a normal command. Due to what I + " would call a bug, but one which Bram has no intention of fixing due + " to the complexity of the various segments of code that process the + " strings, you cannot embed character nr2char(128) in a string literal + " used in an expression register. This character is used in the gui to + " introduce a special sequence, which results in termcap expansion of + " some sort. The point is, since character 128 could be used as txtfmt + " token, need another way to insert a string that could contain it. + " Solution: use the actual string variable, rather than a string + " literal. This is actually more intuitive anyways - I just wasn't + " sure what the scope requirements were for variables used in + " expression register - it works. + exe 'normal! '.l:count.a:cmd."\\=l:tokstr\" + "IMPORTANT: Return empty string so that default return of 0 is not + "inserted by the "=" used to invoke this function from insert + "mode ! + return '' + else + " Return the token string to be inserted via expression register + " (=) in insert-mode mapping. + return l:tokstr + endif +endfu +" >>> +" Function: s:Adjust_cursor() <<< +" Purpose: +" Method: +" Inputs: (indirect, via script-local vars) +" s:Adjust_cursor_inv_off +" s:Adjust_cursor_modestr +" Return: Empty string (to permit the function to be called from an expression +" register) +" Error: Not possible. Since this function is used only internally, +" questionable inputs are silently converted to safe ones. +" Note: There is at least one scenario under which we must return empty string +" immediately (i.e., without performing any adjustment): it is the case in +" which user has canceled a token insertion. In this case, neither of the +" script-local input vars will exist. +fu! s:Adjust_cursor() + if !exists('s:Adjust_cursor_modestr') || !exists('s:Adjust_cursor_inv_off') + " It appears no adjustment is required + return '' + endif + if s:Adjust_cursor_modestr == 'nn' + " Cursor is on last inserted char now. Special care must be taken + " to ensure that we don't attempt to set cursor before beginning of + " line (inv_off == tokstrlen and first char inserted is first char on + " line). Note that this insert type works as though the chars had been + " inserted, offset implemented, then insert mode exited. + " Extreme points: + " inv_off==0 --> Position on last char inserted + " inv_off==tokstrlen --> Position before first char or on first + " char if beginning of line. + " Adjust cursor in multi-byte safe manner + " Note: I am intentionally letting Move_cursor handle the special + " case of cursor positioned at beginning of line. Move_cursor is + " multi-byte safe and will not attempt to position the cursor before + " the beginning of the line, even when inv_off requests it. + call s:Move_cursor(-s:Adjust_cursor_inv_off) + elseif s:Adjust_cursor_modestr == 'ni' + " Cursor is on last inserted char now, but with an inv_off of 0, + " needs to end up 1 col position right of last inserted char after + " entering insert mode. There are 2 cases to consider... + " *** Case 1 *** + " New cursor position is past the end of the line. + " In this case, we cannot use Move_cursor to accomplish the shift + " because we are currently in normal mode, which precludes the setting + " of cursor position past EOL. (Note: Calling startinsert prior to + " Move_cursor doesn't work since, according to the Vim docs, "when + " using this command in a function or script, the insertion only + " starts after the function or script is finished.") Fortunately, we + " can call startinsert! to enter insert mode and position the cursor + " past the end of the line. + " *** Case 2 *** + " New cursor position is *NOT* past the end of the line. + " Accomplish the required right shift simply by adjusting the value of + " inv_off passed to Move_cursor. There is no way for Move_cursor + " to fail to reach requested position in this case, since even in the + " extreme case (inv_off == tokstrlen and tokens inserted at beginning + " of line), the offset of 1 ensures we won't request a position before + " BOL. + if s:Move_cursor(-s:Adjust_cursor_inv_off + 1) + " Move_cursor was unable to move past EOL. + startinsert! + else + startinsert + endif + elseif s:Adjust_cursor_modestr == 'in' + " Cursor is at col number of last inserted char + 1, which is where it + " needs to be for inv_off==0. Stopinsert will shift it 1 char left. + " Note that if inv_off==tokstrlen, cursor will end up to left of + " inserted chars unless this would put it prior to beginning of line. + call s:Move_cursor(-s:Adjust_cursor_inv_off) + exe 'stopinsert' + elseif s:Adjust_cursor_modestr == 'ii' + " Cursor is at col number of last inserted char + 1, which is where it + " needs to be for inv_off==0. + " one beyond it (for inv_off==0). Note that since we're staying in + " insert mode, positions before and after inserted chars are legal, + " even when inserted char(s) are at beginning or end of line. + call s:Move_cursor(-s:Adjust_cursor_inv_off) + endif + return '' +endfu +" >>> +" Function: s:Prompt_fmt_clr_spec() <<< +" Purpose: Prompt user for type of formatting region desired, and return +" the string entered +" How: The user will be prompted to enter a fmt/clr[/bgc] list, consisting of +" fmt/clr[/bgc] atoms separated by commas and/or dots. The format of a +" fmt/clr[/bgc] atom is described in header of Translate_fmt_clr_spec()., +" Return: The entered string +fu! s:Prompt_fmt_clr_spec() + " Prompt the user for fmt/clr spec string + " TODO: Decide whether prompt needs to distinguish between bgc and clr + call inputsave() + let str = input('Enter a fmt / clr string. (Enter to cancel): ') + call inputrestore() + return str +endfu +" >>> +" Function: s:Lookup_clr_namepat() <<< +" Purpose: Convert the input color name pattern to a color index in range +" 1..8, using the buffer-specific color definition array +" b:txtfmt_{clr|bgc}_namepat. +" Use b:txtfmt_cfg_{fg|bg}color{} arrays to determine whether the specified +" color is active. +" Return: +" Requested color valid and active: Color index between 1 and 8 +" Requested color invalid: 0 +" Requested color valid but inactive: -1 * {color_index} +fu! s:Lookup_clr_namepat(typ, namepat) + if a:typ == 'c' + let fg_or_bg = 'fg' + let clr_or_bgc = 'clr' + elseif a:typ == 'k' + let fg_or_bg = 'bg' + let clr_or_bgc = 'bgc' + else + echoerr "Internal error - Unknown color type `".a:typ."' passed to Lookup_clr_namepat()" + return 0 + endif + " Loop over all color definitions (active and inactive), looking for one + " whose regex matches input color name + let i = 1 + while i < b:txtfmt_num_colors + if a:namepat =~ b:txtfmt_{clr_or_bgc}_namepat{i} + " We found a match! + if b:txtfmt_cfg_{fg_or_bg}colormask[i - 1] != '1' + " Inactive color + return -1 * i + else + " Active color + return i + endif + endif + let i = i + 1 + endwhile + " Didn't find it! + return 0 +endfu +" >>> +" Function: s:Translate_fmt_clr_spec() <<< +" Purpose: Convert the input fmt/clr spec string to the corresponding fmt/clr +" token. +" How: The input fmt/clr spec string will be in one of the following formats: +" "f-" +" "c-" +" "k-" if background colors are active +" "f[u][b][i][[s][r][[c]]]" Note that s, r and c values must be disallowed for +" certain permutations of b:txtfmt_cfg_longformats +" and b:txtfmt_cfg_undercurl +" "c" +" "k" if background colors are active +" Note: must match one of the color definitions specified by user +" (or default if user hasn't overriden). +" Note: Specification of an inactive color is considered to be an error. +" Return: One of the following: +" 1) A single fmt token +" 2) A single clr token +" 3) A single bgc token +" 4) '' - empty string if erroneous user entry +" Error: If error, function will set the script-local s:err_str +" Note: The function logic takes advantage of the fact that both strpart() and +" string offset bracket notation (s[i]) allow indices past end of string, in +" which case, they return empty strings. +fu! s:Translate_fmt_clr_spec(s) + " Declare modifiable version of input parameter + let s = a:s + " Check for empty string (all whitespace considered empty string, as it + " should have been detected as 'Cancel' request by caller). + if s =~ '^\s*$' + " Caller should validate this, but just in case + let s:err_str = "Empty fmt/clr spec" + return '' + endif + let len = strlen(s) + let ret_str = '' + if s[0] ==? 'f' + " fmt string + if s[1] == '-' + if strlen(s) == 2 + " default format + let ret_str = ret_str.nr2char(b:txtfmt_fmt_first_tok) + else + " Shouldn't be anything after f- + let s:err_str = 'Unexpected chars after "f-"' + return '' + endif + else + " Not a default fmt request - remainder of string should match + " [ubi[sr[c]]] + let s = strpart(s, 1) + if s =~ '[^'.b:ubisrc_fmt{b:txtfmt_num_formats-1}.']' + " s contains illegal (but not necessarily invalid) char + if s !~ '[^ubisrc]' + " Illegal (but not invalid) char + " User has mistakenly used s, r or c with one of the + " 'short' formats or c with a version of Vim that doesn't + " support undercurl. Give an appropriate warning. + if !b:txtfmt_cfg_longformats + let s:err_str = "Only 'u', 'b' and 'i' attributes are permitted when one of the 'short' formats is in effect" + else + " Long formats are in use; hence, we can get here only + " if user attempted to use undercurl in version of Vim + " that doesn't support it. + let s:err_str = "Undercurl attribute supported only in Vim 7 or later" + endif + else + let s:err_str = 'Invalid chars in fmt spec after "f"' + endif + return '' + else + " Convert the entered chars to a binary val used to get token + " Note: Validation has already been performed; hence, we know + " that s represents both a valid and active token. + let bin_val = 0 + if s=~'u' | let bin_val = bin_val + 1 | endif + if s=~'b' | let bin_val = bin_val + 2 | endif + if s=~'i' | let bin_val = bin_val + 4 | endif + if s=~'s' | let bin_val = bin_val + 8 | endif + if s=~'r' | let bin_val = bin_val + 16 | endif + if s=~'c' | let bin_val = bin_val + 32 | endif + let ret_str = ret_str.nr2char(b:txtfmt_fmt_first_tok + bin_val) + endif + endif + elseif s[0] ==? 'c' || s[0] ==? 'k' + if s[0] ==? 'k' && !b:txtfmt_cfg_bgcolor + " Oops! Background colors aren't active. + let s:err_str = "The current 'tokrange' setting does not support background colors." + \." (:help txtfmt-formats)" + return '' + endif + " clr or bgc string + if s[1] == '-' + if strlen(s) == 2 + " default format + let ret_str = ret_str.nr2char( + \ s[0] ==? 'c' + \ ? b:txtfmt_clr_first_tok + \ : b:txtfmt_bgc_first_tok + \) + else + " Shouldn't be anything after c- or k- + let s:err_str = 'Unexpected chars after "'.s[0].'-"' + return '' + endif + else + " Not a default clr/bgc request - remainder of string denotes a + " color + let typ = s[0] + let s = strpart(s, 1) + " Determine which color index corresponds to color pattern + let clr_ind = s:Lookup_clr_namepat(typ, s) + if clr_ind == 0 + let s:err_str = "Invalid color name pattern: '".s."'" + return '' + elseif clr_ind < 0 + " TODO_BG: Make sure the help note below is still valid after + " help has been updated. + let s:err_str = "Color ".(-1 * clr_ind)." is not an active " + \.(typ ==? 'c' ? "foreground" : "background") + \." color. (:help " + \.(typ ==? 'c' ? "txtfmtFgcolormask" : "txtfmtBgcolormask").")" + return '' + endif + " IMPORTANT NOTE: clr_ind is 1-based index (1 corresponds to first + " non-default color) + let ret_str = ret_str.nr2char( + \(typ ==? 'c' + \ ? b:txtfmt_clr_first_tok + \ : b:txtfmt_bgc_first_tok) + \ + clr_ind) + endif + else + let s:err_str = 'Invalid fmt/clr spec. Must begin with ' + \.(b:txtfmt_cfg_bgcolor ? '"f", "c" or "k"' : '"f" or "c"') + return '' + endif + " Return the token as a string + return ret_str +endfu +" >>> +" Function: s:Translate_fmt_clr_list() <<< +" Purpose: Translate the input comma/dot-separated list of fmt/clr/bgc spec +" atoms into a string of tokens suitable for insertion into the buffer. +" Validation is performed. Also, cursor offset into translated token string is +" determined based upon the presence of a dot (replaces comma when it appears +" between fmt/clr/bgc atoms - may also appear as first or last character in +" fmt/clr/bgc spec list). +" Input: Comma/Dot-separated list of fmt/clr/bgc spec atoms. +" Return: String of the following format: +" , +" Error: Return empty string and set s:err_str +" Warning: Set s:wrn_str +fu! s:Translate_fmt_clr_list(s) + " For convenience + let s = a:s + let len = strlen(s) + " Initializations <<< + let offset = -1 " -1 means not explicitly set by user + let offset_fixed = 0 " binary flag + let i = 0 + let sep = '' "[,.] or '' for end-of string + let num_fld = 0 " # of atoms encountered + let tokstr = '' " built up in loop + " >>> + " Process the fmt/clr/bgc spec atom(s) in a loop + while i < len + " Find end of spec ([,.] or end of string) + " (Commas and dots not allowed except as field sep) + " NOTE: Match with '$' returns strlen (even for empty string) + let ie = match(s, '[,.]\|$', i) + " Extract field sep and text + let sep = iei + let fld = strpart(s, i, ie-i) + let num_fld = num_fld+1 + " Translate field if non-empty + let tok = s:Translate_fmt_clr_spec(fld) + if tok == '' + " Must have been error + let s:err_str = "Invalid fmt/clr spec: '".fld."': ".s:err_str + return '' + endif + let tokstr = tokstr.tok + " Validate the field in various ways + elseif i==ie " check null fields + let fld = '' + if ie==0 " at beginning of list ('.' permitted) + if ie==len-1 + let s:err_str = "Separator with nothing to separate" + return '' + elseif len==0 + " Note: This should probably be checked before now, but + " just to be complete... + let s:err_str = "Invalid empty fmt/clr spec list" + return '' + elseif sep=='.' + let offset = 0 + else + let s:err_str = "Invalid leading ',' in fmt/clr spec list" + return '' + endif + else " not at beginning of list + let s:err_str = "Empty field encountered at '".strpart(s, i) + return '' + endif + endif + if ie==len-1 " validate last char in string + if num_fld==0 + " NOTE: Can't actually get here... + let s:err_str = "fmt/clr spec list contains no fields" + return '' + elseif sep!='.' + let s:err_str = "Trailing comma not allowed in fmt/clr spec list" + return '' + endif + endif + " If here, field is OK unless atom is bad... + " Do offset logic + if offset==-1 && sep=='.' + let offset = num_fld + endif + " Update for next iteration + let i = ie+1 + if i > len + break + endif + " OLD (implicit) logic for determining cursor offset <<< + " TODO_BG: Get rid of this... + "if tok=~b:re_fmt_any_stok || tok=~b:re_fmt_etok + " if fmt_begun + " " Fix cursor location (if not already fixed) + " if offset==0 + " let offset = num_fld-1 + " endif + " endif + " " If fmt start tok, set flag + " if tok!~b:re_fmt_etok + " let fmt_begun = 1 + " endif + "elseif tok=~b:re_clr_any_stok || tok=~b:re_clr_etok + " if clr_begun + " " Fix cursor location (if not already fixed) + " if offset==0 + " let offset = num_fld-1 + " endif + " endif + " " If clr start tok, set flag + " if tok!~b:re_clr_etok + " let clr_begun = 1 + " endif + "endif + " >>> + endwhile + " Return the special format string + return offset.','.tokstr +endfu +" >>> +" Function: s:Jump_to_tok() <<< +" Purpose: Jumps forward or backwards (as determined by a:dir), to the +" v:count1'th nearest token of type given by a:type ('c'=clr 'k'=bgc 'f'=fmt +" 'a'=any (clr, bgc or fmt)). If 'till' argument is nonzero, jump will +" position cursor one char position closer to starting location than the +" sought token. (This behavior is analogous to t and T normal mode commands.) +" Note: If the map that invokes this function is a visual-mode mapping, +" special logic is required to restore the visual selection prior to +" performing any cursor movement. This is because Vim's default vmap behavior +" is to remove the visual highlighting and position the cursor at the start of +" the visual area as soon as the map is invoked. For the motion mappings that +" call this function, the default behavior is not acceptable. +" Inputs: +" type 1 or 2 chars indicating the type of token sought. Format is as +" follows: +" [{target-modifier}]{target-type} +" {target-modifier} := +" b 'begin region' tokens only +" e 'end region' tokens only +" {target-type} +" c = fg color, k = bg color, f = format, +" a = fg color, bg color, or format +" dir single char indicating direction for search (f=forward, b=back). +" Wrap behavior is determined by the 'wrapscan' option. +" till If nonzero, jump lands cursor not on the token, but just 'before' +" it (where before indicates the side closest to starting point). +" 'Till' is used because of analogy with Vim's t and T commands in +" normal mode. +" v:count1 If greater than 1, indicates the number of jumps to be performed. +" Allows a count to be used with the invoking mapping when jumping +" to the N'th token of a particular type and in a particular +" direction is desired. +" a:1 If supplied, represents the count to be used. (Needed when this +" function is called from a user-map) +" Return: Always return empty string, in case function is called from an +" expression register in insert mode. +" IMPORTANT NOTE: On the use of \%# atom -- When used in search() (i.e., +" non-interactively), Vim appears to use lookahead to optimize when using +" \%#\@!; however, a '\%#' by itself, or followed by \@=, is NOT optimized. +" (Vim searches the entire file with wraparound before finding the cursor +" position!) +" NOTE: Ideally, if the 'till' flag is set for a backwards search, I would use +" the /e modifier with a ? search begun from normal mode to find the token and +" position the cursor on the character after it. (If token is last char on +" line, cursor would be positioned in first column of following line.) +" However, this can cause problems when tok range includes char code 128. This +" problem can be avoided if search() is used. Unfortunately, search() does not +" permit the /e modifier to be used (and \zs and/or \ze appear to be a bit +" buggy when used just after a newline - e.g., try /\n\zs/ and see what +" happens!). Thus, my strategy for finding the target location when the 'till' +" flag is set is to use search() to find the sought token, employing patterns +" that will match only if the 'till' destination location actually exists. If +" search() finds a valid destination, I then accomplish the 'till' move with a +" subsequent positioning command. +fu! s:Jump_to_tok(mode, type, dir, till, ...) + " Determine whether we jump only to active tokens + " By default, we don't. + let jtin = exists('b:txtfmtJumptoinactive') + \ ? b:txtfmtJumptoinactive + \ : exists('g:txtfmtJumptoinactive') + \ ? g:txtfmtJumptoinactive + \ : 0 + " Get the search pattern + " Design Decision Needed: Decide whether to permit inactive color tokens + " to serve as target of jump. If this is desired, perhaps create special + " b:txtfmt_re_CLR_<...> and b:txtfmt_re_BGC_<...> regexes. Alternatively, + " use the b:re_no_self_esc and b:re_no_bslash_esc patterns on the + " <...>_atom regexes. + " Note: Let jumptoinactive option determine whether inactive tokens can + " serve as jump targets. + if a:type == 'c' + let re = b:txtfmt_re_{jtin ? 'CLR' : 'clr'}_tok + elseif a:type == 'bc' + let re = b:txtfmt_re_{jtin ? 'CLR' : 'clr'}_stok + elseif a:type == 'ec' + let re = b:txtfmt_re_{jtin ? 'CLR' : 'clr'}_etok + elseif a:type == 'k' + let re = b:txtfmt_re_{jtin ? 'BGC' : 'bgc'}_tok + elseif a:type == 'bk' + let re = b:txtfmt_re_{jtin ? 'BGC' : 'bgc'}_stok + elseif a:type == 'ek' + let re = b:txtfmt_re_{jtin ? 'BGC' : 'bgc'}_etok + elseif a:type == 'f' + let re = b:txtfmt_re_fmt_tok + elseif a:type == 'bf' + let re = b:txtfmt_re_fmt_stok + elseif a:type == 'ef' + let re = b:txtfmt_re_fmt_etok + elseif a:type == 'a' + let re = b:txtfmt_re_{jtin ? 'ANY' : 'any'}_tok + elseif a:type == 'ba' + let re = b:txtfmt_re_{jtin ? 'ANY' : 'any'}_stok + elseif a:type == 'ea' + let re = b:txtfmt_re_{jtin ? 'ANY' : 'any'}_etok + else + " Error - shouldn't ever get here - just return + return '' + endif + " Important Note: If mode is visual, Vim has already removed the visual + " highlighting and positioned the cursor at the start of the visual + " region. Since this is a motion mapping, we need to undo this; i.e., + " restore the visual highlighting and put the cursor at the correct + " end/corner of the visual region, allowing for the fact that any number + " of "o" and or "O" commands may have been executed to bounce the cursor + " between ends/corners... Normal mode gv fits the bill. + " Important Note: When a visual mode mapping invokes this function, Vim + " has already changed mode to normal before we get here. Thus, we must use + " the mode string passed from the mapping to determine whether we need to + " restore the visual selection. Since we're using gv, it doesn't matter + " which visual sub-mode was in effect. + if a:mode == 'v' + normal! gv + endif + " Get the search options + if a:dir == 'b' + " Leave wrap option alone so that 'wrapscan' will be honored + let opt = 'b' + if a:till + " NOTE: The \n\_. handles the following 2 cases: + " 1) Sought token is at end of line followed by non-empty line + " 2) Sought token is at end of line followed by empty line + " NOTE: The \%#\@! ensures that if we're sitting on a character + " after the the target token type, we don't match the token just + " before it. (Otherwise we'd get stuck when trying to do multiple + " successive backwards jumps.) + let re = re.'\%(\n\%#\@!\_.\|\%#\@!.\)\@=' + endif + elseif a:dir == 'f' + " Leave wrap option alone so that 'wrapscan' will be honored + let opt = '' + if a:till + " The following pattern will position us on the buffer position + " one char prior to the sought token, even in case where the token + " is at beginning of a line preceded by blank line. + " NOTE: landing on a \n puts cursor at end of line ended by the + " newline. + " NOTE: \@= is necessary when cpo-c is set to avoid skipping every + " other token when there are multiple consecutive tokens of same + " type. + let re = '\_.'.re.'\@=' + endif + else + " Error - Should never get here - just return + return '' + endif + " Get the count, which is either supplied explicitly as optional extra + " arg, or is obtained from v:count1 + if a:0 > 0 + " Note: Counts are generated internally; hence, validation has + " been performed already. + let l:count = a:1 + else + let l:count = v:count1 + endif + " In a loop count, perform the search() + let i = 0 + while i < l:count + " Note: If search fails, cursor will not be moved. + " IMPORTANT NOTE: Simplest thing would be to use normal search command + " here, but that gives problems if tok range includes 128! + let l2 = search(re, opt) + " Did we find the sought token? + if l2 > 0 + " We're on found tok + if a:till + " NOTE: 2 cases: + " 1) Backward search - we're on token, but we need to be at + " position just past it (and search() assures us the position + " exists. + " 2) Forward search - search() got us to correct position (for + " both 'till' and non-'till' cases. + if a:dir == 'b' + " Backward search + " IMPORTANT NOTE: Original implementation used col() and + " cursor(), which are *NOT* multi-byte safe! + " Use virtcol and special search instead. + " Note: Vim documentation implies that the following holds + " true when cursor is positioned on the last character of + " the line: virtcol('.') == virtcol('$') - 1 + let c2 = virtcol('.') + if c2 != virtcol('$') - 1 + " Not last char on line + call search('\%'.(c2 + 1).'v', '') + else + " Last char on line - move to start of next line + " Note: cursor() can handle col of 1 even for empty + " line. Also, it's mult-byte safe. + call cursor(l2 + 1, 1) + endif + endif + endif + else + " No reason to keep looping... + break + endif + let i = i + 1 + endwhile + return '' +endfu +" >>> +" Function: s:Mapwarn_check() <<< +" Purpose: Determine whether the user has already been warned about the +" mapping ambiguity/conflict indicated by input arguments, and return nonzero +" if so. Additionally, if the 'add' parameter is true, record the input +" conflict/ambiguity in the data structures maintaining such information so +" that the function will return true for it next time. +" Inputs: +" lhs - lhs of the new mapping +" rhs - rhs of the old (existing) mapping +" mode - single character indicating the mode of the mapping (e.g. n=normal, +" v=visual, i=insert, o=operator-pending, etc...) +" add - flag indicating whether the conflict indicated by lhs, rhs and mode +" should be added to the data structures searched by this function +fu! s:Mapwarn_check(lhs, rhs, mode, add) + let found = 0 + let i = 0 + if exists('g:txtfmt_mapwarn_cnt') + while i < g:txtfmt_mapwarn_cnt + if a:lhs == g:txtfmt_mapwarn_lhs{i} && + \ a:rhs == g:txtfmt_mapwarn_rhs{i} && + \ a:mode == g:txtfmt_mapwarn_mode{i} + let found = 1 + break + endif + let i = i + 1 + endwhile + endif + if !found && a:add + " Make sure g:txtfmt_mapwarn_cnt is self-starting + if !exists('g:txtfmt_mapwarn_cnt') + let g:txtfmt_mapwarn_cnt = 0 + endif + " Add a new conflict/ambiguity to the arrays + let g:txtfmt_mapwarn_lhs{g:txtfmt_mapwarn_cnt} = a:lhs + let g:txtfmt_mapwarn_rhs{g:txtfmt_mapwarn_cnt} = a:rhs + let g:txtfmt_mapwarn_mode{g:txtfmt_mapwarn_cnt} = a:mode + let g:txtfmt_mapwarn_cnt = g:txtfmt_mapwarn_cnt + 1 + endif + " Indicate whether input conflict/ambiguity was found + return found +endfu +" >>> +" Function: s:Undef_map() <<< +" Purpose: Creates an undo action for the map whose lhs, rhs and unmap_cmd are +" input, and adds the undo action to b:undo_ftplugin. +" Inputs: +" mode - single char, used as input to maparg, mapcheck, etc... +" lhs - string representing the lhs of the map to be undone +" rhs - string representing the rhs of the map to be undone. +" Assumptions: +" -All maps to be undone are buffer-local. +" -All occurrences of '[_a-zA-Z0-9]' in the rhs of a mapping defined by +" this plugin represent a call to a script-local function. +" Note: rhs is required so that we can be sure to delete *only* maps created +" by this plugin. (Consider that user could either intentionally or +" inadvertently override one of the txtfmt maps with a completely unrelated +" map after this plugin is loaded. For this reason, we cannot (or should not) +" blindly delete lhs.) +fu! s:Undef_map(lhs, rhs, mode) + " Determine the unmap command to be used. + if a:mode=='n' + let unmap_cmd = 'nunmap' + elseif a:mode=='i' + let unmap_cmd = 'iunmap' + elseif a:mode=='o' + let unmap_cmd = 'ounmap' + elseif a:mode=='v' + let unmap_cmd = 'vunmap' + else + echoerr 'Internal error - unsupported mapmode passed to Undef_map()' + return 1 + endif + " Create the undo action, taking special care to avoid deleting a map with + " the same lhs, created by user after the sourcing of this plugin. + " Note: Prep_for_single_quotes ensures that single quotes contained in lhs + " or rhs are properly escaped before being wrapped in the single-quoted + " string that will be parsed when b:undo_ftplugin is exec'ed. + " Note: Be sure not to add whitespace between the lhs of the map being + " unmapped and the subsequent '|' as this will result in nonexistent + " mapping error. + " Note: When the maparg() is executed, it will return function names of + " the form '{number}_func' rather than 'func'. Thus, to ensure + " that the delayed comparison works properly, I need to convert a:rhs to + " the {number}_ form now. + let rhs = substitute(a:rhs, '\ze[_a-zA-Z0-9]', + \'\= "" . s:SID() . "_"', 'g') + call s:Add_undo("if maparg('".s:Prep_for_single_quotes(a:lhs) + \."', '".a:mode."') == '".s:Prep_for_single_quotes(rhs)."' | " + \.unmap_cmd." ".a:lhs."| endif") +endfu +" >>> +" Function: s:Def_map() <<< +" Purpose: Define both the level 1 and level 2 map as appropriate. +" Inputs: +" mode - single char, used as input to maparg, mapcheck, etc... +" lhs1 - lhs of first-level map +" lhs2 - rhs of first-level map, lhs of second-level map +" rhs2 - rhs of second-level map +" How: Consider whether user already has a map to level 2 (which should take +" precedence over maplevel 1). Also, make sure the map from level 2, if it +" exists, is not incorrect, etc... +" Note: Cause b:undo_ftplugin to be updated so that whatever mappings are made +" by us will be unmapped when ftplugin is unloaded. +" Return: +" 0 - success +" nonzero - error +" NOTE: Function will echoerr to user +fu! s:Def_map(mode, lhs1, lhs2, rhs2) + " TODO - Perhaps eventually support operator mode if needed + if a:mode=='n' + let cmd1 = 'nmap' + let cmd2 = 'nnoremap' + elseif a:mode=='i' + let cmd1 = 'imap' + let cmd2 = 'inoremap' + elseif a:mode=='o' + let cmd1 = 'omap' + let cmd2 = 'onoremap' + elseif a:mode=='v' + let cmd1 = 'vmap' + let cmd2 = 'vnoremap' + else + echoerr 'Internal error - unsupported mapmode passed to Def_map()' + return 1 + endif + " Do first map level <<< + if !hasmapto(a:lhs2, a:mode) + " User hasn't overridden the default level 1 mapping + " Make sure there's no conflict or ambiguity between an existing map + " and the default one we plan to add... + let oldarg = maparg(a:lhs1, a:mode) + let oldchk = mapcheck(a:lhs1, a:mode) + " Check for conflicts and ambiguities, decoding applicable portions of + " mapwarn option character flag string into more immediately useful + " variables, to avoid messy ternaries in the subsequent logic. + " Note: Create only the variables that will be used. + if oldarg != '' + " Map conflict + let l:problem = 'c' + if b:txtfmt_cfg_mapwarn =~ 'M' + let l:msg_or_err = 'm' + elseif b:txtfmt_cfg_mapwarn =~ 'E' + let l:msg_or_err = 'e' + endif + if exists('l:msg_or_err') + let l:once_only = b:txtfmt_cfg_mapwarn =~ 'O' + endif + let l:create = b:txtfmt_cfg_mapwarn =~ 'C' + let l:old_rhs = oldarg + elseif oldchk != '' + " Map ambiguity + let l:problem = 'a' + if b:txtfmt_cfg_mapwarn =~ 'm' + let l:msg_or_err = 'm' + elseif b:txtfmt_cfg_mapwarn =~ 'e' + let l:msg_or_err = 'e' + endif + if exists('l:msg_or_err') + let l:once_only = b:txtfmt_cfg_mapwarn =~ 'o' + endif + let l:create = b:txtfmt_cfg_mapwarn =~ 'c' + let l:old_rhs = oldchk + endif + if exists('l:problem') + " There's an ambiguity or conflict + if exists('l:msg_or_err') + " We need to warn unless warning is precluded by 'once-only' + " mechanism + if !l:once_only || !s:Mapwarn_check(a:lhs1, l:old_rhs, a:mode, l:once_only) + let l:warnstr = 'Level 1 map ' + \.(l:problem == 'a' ? 'ambiguity:' : 'conflict: ') + \.a:lhs1.' already mapped to '.l:old_rhs + if l:msg_or_err == 'm' + echomsg l:warnstr + else + echoerr l:warnstr + endif + endif + endif + endif + " Do the map for buffer unless map creation is precluded by conflict + " or ambiguity in absence of the 'create' flag. + " Note: Do not use attribute, since that would cause Vim to + " display error, due to the original mapping. + if !exists('l:problem') || l:create + exe cmd1.' '.a:lhs1.' '.a:lhs2 + " Create undo action for the map just created + call s:Undef_map(a:lhs1, a:lhs2, a:mode) + endif + else + "echomsg "Skipping 1st level" + endif + " >>> + " Do second map level <<< + " Assumption: Second-level mappings have long <...> names, + " preceded by . It is safe to assume user hasn't mapped one to + " something else... + exe cmd2.' '.a:lhs2.' '.a:rhs2 + " Create undo action for the map just created + call s:Undef_map(a:lhs2, a:rhs2, a:mode) + " >>> + " Success + return 0 +endfu +" >>> +" Function: s:MakeString() <<< +" Purpose: Build and return a string by concatenating a base string some +" number of times to itself. +" Inputs: +" str -base string, which will be concatenated to itself +" len -# of occurrences of 'str' to put in the return string +" Return: The generated string +fu! s:MakeString(str, len) + let s = '' + let i = 0 + while i < a:len + let s = s.a:str + let i = i + 1 + endwhile + return s +endfu +" >>> +" Function: s:ShowTokenMap() <<< +" Purpose: Echo to user a table showing the current use of all tokens in the +" range of fmt/clr tokens. +" How: Use echo, as this is intended as a temporary showing for informational +" purposes only. Highlighting of column headers is accomplished via echohl +" Format: Should be something like the sample table shown below... +" Note: char-nr should use the number format indicated by +" b:txtfmt_cfg_starttok_display. +" Note: For inactive colors, an asterisk will be prepended to char-nr, and +" '(inactive)' will be appended to the description. In order to keep the +" numbers aligned properly, active colors will have a space prepended to the +" char-nr. +" TODO: Decide whether it's necessary to wrap inactive char-nr's in parens. If +" not, get rid of it. +"=== [FG] COLORS === +"char-nr description clr-pattern clr-def +"180 no color - +"181 Color0 ^\\%(k\\|bla\\%[ck]\\)$,c:Black,g:#000000 #000000 +"182 Color1 ^blu\\%[e]$,c:DarkBlue,g:#0000FF #0000FF +"183 Color2 ^g\\%[reen]$,c:DarkGreen,g:#00FF00 #00FF00 +". +". +"=== FORMAT === +"char-nr description spec +"189 no format - +"190 italic i +"191 bold b +"192 bold,italic bi +". +". +". +". +" Important Note: The subsequent lines will be output if and only if +" background colors are enabled. +"=== BG COLORS === +"char-nr description clr-pattern clr-def +" 197 no color - +"*198 Color0 (inactive) ^\\%(k\\|bla\\%[ck]\\)$,c:Black,g:#000000 #000000 +" 199 Color1 ^blu\\%[e]$,c:DarkBlue,g:#0000FF #0000FF +" 200 Color2 ^g\\%[reen]$,c:DarkGreen,g:#00FF00 #00FF00 +" . +" . +fu! s:ShowTokenMap() + " Loop 2 times - first time is just to calculate column widths + let cw1 = 0 | let cw2 = 0 | let cw3 = 0 | let cw4 = 0 + " Define an array, indexed by fgbg_idx, which may be used to build fg/bg + " specific var names. + let clr_or_bgc{0} = 'clr' + let clr_or_bgc{1} = 'bgc' + " Initialize the vars that will accumulate table text + let fmt_header = '' | let fmt_lines = '' + let clr_header = '' | let clr_lines = '' + let bgc_header = '' | let bgc_lines = '' + " Determine number format to use for char-nr column + let use_hex = strpart(b:txtfmt_cfg_starttok_display, 0, 2) == '0x' + let i = 0 + while i < 2 + " Loop over all format lines (1 hdr and b:txtfmt_num_formats-1 fmt) + let iFmt = -1 " start with header line + while iFmt < b:txtfmt_num_formats + let line = '' " Initialize text for current line + " Column 1 + if iFmt == -1 + let col1_text = ' char-nr' + else + let col1_text = b:txtfmt_fmt_first_tok + iFmt + if use_hex + " Convert to hex + let col1_text = TxtfmtUtil_num_to_hex_str(col1_text) + endif + " Prepend space for alignment + let col1_text = ' ' . col1_text + endif + if i == 0 + " Calculate col width + if strlen(col1_text) > cw1 + let cw1 = strlen(col1_text) + endif + else + " Output line + let line = line.(col1_text.s:MakeString(' ', cw1 + 2 - strlen(col1_text))) + endif + " Column 2 + if iFmt == -1 + let col2_text = 'description' + elseif iFmt == 0 + let col2_text = 'no format' + else + let col2_text = b:txtfmt_fmt{iFmt} + endif + if i == 0 + " Calculate col width + if strlen(col2_text) > cw2 + let cw2 = strlen(col2_text) + endif + else + " Output line + let line = line.(col2_text.s:MakeString(' ', cw2 + 2 - strlen(col2_text))) + endif + " Column 3 + if iFmt == -1 + let col3_text = 'fmt-spec' + elseif iFmt == 0 + let col3_text = '-' + else + let col3_text = b:ubisrc_fmt{iFmt} + endif + if i == 0 + " Calculate col width + if strlen(col3_text) > cw3 + let cw3 = strlen(col3_text) + endif + else + " Output line + let line = line.(col3_text.s:MakeString(' ', cw3 + 2 - strlen(col3_text))) + endif + " Accumulate line just built into the list of lines + if i == 1 + if iFmt == -1 + " Store header line separately so that echohl can be used + let fmt_header = line + else + " Regular row in table (non-header) + let fmt_lines = fmt_lines.(iFmt==0?'':"\").line + endif + endif + let iFmt = iFmt + 1 + endwhile + " Loop over fg colors and (if necessary) bg colors + let fgbg_idx = 0 + while fgbg_idx < (b:txtfmt_cfg_bgcolor ? 2 : 1) + if fgbg_idx == 0 + let first_tok = b:txtfmt_clr_first_tok + let colormask = b:txtfmt_cfg_fgcolormask + else + let first_tok = b:txtfmt_bgc_first_tok + let colormask = b:txtfmt_cfg_bgcolormask + endif + " Loop over all color tokens (even inactive ones) + " Index note: In this loop, index 0 refers to 'no color', while index + " 1 refers to txtfmtColor{1} (default rgb=0x000000). + let iClr = -1 + while iClr < b:txtfmt_num_colors + let line = '' " Initialize text for current line + " Column 1 + if iClr == -1 + let col1_text = ' char-nr' + else + if iClr >= 0 + let col1_text = (first_tok + iClr) + if use_hex + " Convert to hex + let col1_text = TxtfmtUtil_num_to_hex_str(col1_text) + endif + " If color is inactive, prepend char-nr with asterisk + if iClr > 0 && strpart(colormask, iClr - 1, 1) != '1' + " This color is inactive + let col1_text = '*' . col1_text + else + " Prepend space for alignment + let col1_text = ' ' . col1_text + endif + endif + endif + if i == 0 + " Calculate col width + if strlen(col1_text) > cw1 + let cw1 = strlen(col1_text) + endif + else + " Output line + let line = line.(col1_text.s:MakeString(' ', cw1 + 2 - strlen(col1_text))) + endif + " Column 2 + if iClr == -1 + let col2_text = 'description' + elseif iClr == 0 + let col2_text = 'no color' + else + let col2_text = 'Color'.iClr + if strpart(colormask, iClr - 1, 1) != '1' + let col2_text = col2_text . ' (inactive)' + endif + endif + if i == 0 + " Calculate col width + if strlen(col2_text) > cw2 + let cw2 = strlen(col2_text) + endif + else + " Output line + let line = line.(col2_text.s:MakeString(' ', cw2 + 2 - strlen(col2_text))) + endif + " Column 3 + if iClr == -1 + let col3_text = 'clr-pattern' + elseif iClr == 0 + let col3_text = '-' + else + let col3_text = b:txtfmt_{clr_or_bgc{fgbg_idx}}_namepat{iClr} + endif + if i == 0 + " Calculate col width + if strlen(col3_text) > cw3 + let cw3 = strlen(col3_text) + endif + else + " Output line + let line = line.(col3_text.s:MakeString(' ', cw3 + 2 - strlen(col3_text))) + endif + " Column 4 + if iClr == -1 + let col4_text = 'clr-def' + elseif iClr == 0 + let col4_text = 'N.A.' + else + let col4_text = b:txtfmt_{clr_or_bgc{fgbg_idx}}{iClr} + endif + if i == 0 + " Calculate col width + if strlen(col4_text) > cw4 + let cw4 = strlen(col4_text) + endif + else + " Output line + let line = line.(col4_text.s:MakeString(' ', cw4 + 2 - strlen(col4_text))) + endif + " Accumulate line just built into the list of lines + if i == 1 + if iClr == -1 + " Store header line separately so that echohl can be used + if fgbg_idx == 0 + let clr_header = line + else + let bgc_header = line + endif + else + " Regular row in table (non-header) + if fgbg_idx == 0 + let clr_lines = clr_lines.(iClr==0?'':"\").line + else + let bgc_lines = bgc_lines.(iClr==0?'':"\").line + endif + endif + endif + let iClr = iClr + 1 + endwhile + let fgbg_idx = fgbg_idx + 1 + endwhile + let i = i + 1 + endwhile + echohl Title + echo b:txtfmt_cfg_bgcolor ? ' === FG COLORS ===' : ' === COLORS ===' + echo clr_header + echohl None + echo clr_lines + echohl Title + echo ' === FORMAT ===' + echo fmt_header + echohl None + echo fmt_lines + " If bg colors are not active, we're done + if b:txtfmt_cfg_bgcolor + echohl Title + echo ' === BG COLORS ===' + echo bgc_header + echohl None + echo bgc_lines + endif +endfu +" >>> +" Function: s:MoveStartTok() <<< +" IMPORTANT NOTE: Special care must be taken when defining this function, as +" it invokes :Refresh command, which causes the script to be re-sourced. This +" leads to E127 'Cannot redefine function' when fu[!] is encountered, since +" the function is in the process of executing. +if !exists('*s:MoveStartTok') +fu! s:MoveStartTok(moveto, ...) + if a:0 + " Validate and process optional version value + if a:0 != 1 + echoerr 'Incorrect # of arguments supplied to :MoveStartTok (1 or 2 expected)' + return + elseif (0 + a:1) =~ '^[1-9][0-9]\{2}$' + " Use version supplied by user + let old_ver = a:1 + else + echoerr a:1.' is not a valid Vim version number. Should be same format as v:version' + return + endif + else + " Assume current version + let old_ver = v:version + endif + " Validate the new starttok + if a:moveto !~ '^\s*'.b:txtfmt_re_number_atom.'\s*$' + echoerr "Invalid 'starttok' value supplied: `".a:moveto."'" + return + endif + " Get current settings from buffer-local vars + " Assumption: This function can be invoked only from an active txtfmt + " buffer + let old_starttok = b:txtfmt_cfg_starttok + " Determine new settings + let new_starttok = a:moveto + " Determine amount of shift (signed value) + let l:offset = new_starttok - old_starttok + + " Before proceeding, cache 'effective' values for bgcolor, longformats and + " undercurl. Note that 'effective' values are those that would be in + " effect if current Vim version were old_ver. Note that effective + " undercurl may differ from b:txtfmt_cfg_undercurl. + let bgcolor = b:txtfmt_cfg_bgcolor + let longformats = b:txtfmt_cfg_longformats + if old_ver != v:version + " Effective undercurl could differ from b:txtfmt_cfg_undercurl + if b:txtfmt_cfg_undercurlpref && old_ver >= b:txtfmt_const_vimver_undercurl + " Undercurl desired and supported + let undercurl = 1 + else + " Undercurl either not desired or not supported + let undercurl = 0 + endif + else + let undercurl = b:txtfmt_cfg_undercurl + endif + " Set a flag that indicates whether we will be reserving space for long + " formats before the start of bgc range. Note that this value can be true + " even if longformats is false. Also note that its value is N/A if + " bgcolors are disabled. + let lf_reserved = bgcolor && (longformats || !b:txtfmt_cfg_pack) + " Determine size of the entire range + let rangelen = + \ b:txtfmt_const_tokrange_size_{bgcolor}{lf_reserved}{lf_reserved} + " Perform upper-bound check on new range + if !(new_starttok + rangelen - 1 <= + \ b:txtfmt_const_tokrange_limit_{b:txtfmt_cfg_enc_class}) + " Invalid destination for move! + echoerr "Starttok value of `".new_starttok."' causes upper bound for encoding `" + \.b:txtfmt_cfg_enc."' to be exceeded" + return + endif + " If here, move is legal. + " Record whether buffer is modified before we start modifying it. This + " information is used by modeline processing to determine whether save is + " required. + let b:txtfmt_ml_save_modified = &modified + + " Build 2 character class interiors (i.e., without the [ ]): + " 1) all chars that are tokens under old range + " 2) all chars that are tokens under new range + " Begin the first range, which begins with fg color and ends either with + " formats (no bg colors or discontinuity between formats and bg colors) or + " bg colors. + " Note: The end of the first range is determined independently of + " lf_reserved, as the range includes only tokens actually used. + let re_old_tokrange = nr2char(old_starttok).'-' + let re_new_tokrange = nr2char(new_starttok).'-' + if !bgcolor || !(longformats && undercurl) + " End first range after format tokens + " Calculate length of range + let end_offset = b:txtfmt_const_tokrange_size_{0}{longformats}{undercurl} - 1 + " Close the range + let re_old_tokrange = re_old_tokrange.nr2char(old_starttok + end_offset) + let re_new_tokrange = re_new_tokrange.nr2char(new_starttok + end_offset) + " If bgcolor is enabled, start a new range so that logic after this if + " block needn't know or care whether it was entered + if bgcolor + " Determine offset to start of bgc range + let start_offset = b:txtfmt_const_tokrange_size_{0}{lf_reserved}{lf_reserved} + let re_old_tokrange = re_old_tokrange.nr2char(old_starttok + start_offset).'-' + let re_new_tokrange = re_new_tokrange.nr2char(new_starttok + start_offset).'-' + endif + endif + " If bgcolor is enabled, need to close the first or second range. (If no + " bgcolor, first and only range has already been closed.) + if bgcolor + let end_offset = b:txtfmt_const_tokrange_size_{1}{lf_reserved}{lf_reserved} - 1 + let re_old_tokrange = re_old_tokrange.nr2char(old_starttok + end_offset) + let re_new_tokrange = re_new_tokrange.nr2char(new_starttok + end_offset) + endif + + " STEP 1: (If and only if escaping is permitted) + " Before translating any tokens, need to escape characters that are not + " currently tokens, but will be after the move. Escapes, if applicable, + " must be taken into account. + " Note: Also, need to escape any escape chars that would be considered escaping + " or escaped chars after the move. E.g. (if esc=bslash) + " should become to ensure that + " the effective sequence `' is preserved. + " The algorithm for `esc=bslash' may be expressed as follows: Escape each + " char in a sequence consisting of any number of backslashes terminated + " with a token. Note that it doesn't matter whether number of backslashes + " is even or odd, since the assumption is that prior to the move, neither + " the backslashes nor the token chars have any special meaning. + if b:txtfmt_cfg_escape != 'none' + " Note: This concat order is *much* more efficient than the + " alternative (since tokens are less common than non-token chars) + let re_need_esc = + \'\%(['.re_new_tokrange.']' + \.'\&[^'.re_old_tokrange.']\)' + if b:txtfmt_cfg_escape == 'bslash' + silent! exe '%s/\%(\\\%(\\*'.re_need_esc.'\)\@=\|'.re_need_esc.'\)/\\\0/g' + elseif b:txtfmt_cfg_escape == 'self' + " TODO_BG: Decide whether to use escape() on re_need_esc or + " whether to hardcode the extra escapes... + silent! exe '%s/'.substitute(b:re_no_self_esc, 'placeholder', + \ escape(re_need_esc, '&\'), '').'/\0\0/g' + endif + endif + + " STEP 2: Translate token range + let re_move = '['.re_old_tokrange.']' + if b:txtfmt_cfg_escape != 'none' + if b:txtfmt_cfg_escape == 'bslash' + let re_move = b:re_no_bslash_esc.re_move + elseif b:txtfmt_cfg_escape == 'self' + let re_move = substitute(b:re_no_self_esc, 'placeholder', re_move, '') + endif + endif + silent! exe '%s/'.re_move.'/\=' + \.'nr2char(char2nr(submatch(0)) + l:offset)' + \.'/g' + + " STEP 3: (If and only if escaping is permitted) + " Remove escape chars for characters that are txtfmt tokens under old + " tokrange setting, but not under new. Also, since this post-unescaping + " step is the complement of the pre-escaping performed above, we must + " unescape backslashes that occur in sequences leading up to the escaped + " token. E.g., + " would become , since neither + " the nor the subsequent is significant after the move. + " Note that there's no need to check for even/odd number of backslashes + " preceding tokens. The number will always be odd. For proof, see the + " Rationale below. + " Note: Any character that is in the old tokrange but not the new is an + " escaped token that no longer needs escaping. + " Rationale: All unescaped tokens of the old range have been translated, + " and hence will be tokens in the new range as well. Thus, any token that + " is within the old range but not within the new must, by definition, have + " been escaped (else it would have been translated to the new range). + " Design Decision: An escape is an escape if it's escaping any txtfmt + " token, even a useless 'no-format' or 'no-color' token appearing outside + " a region. (Recall that I don't highlight these to facilitate removal by + " user...) + " Rationale: The goal of this function is not to clean up user's file, but + " simply to translate tokrange + if b:txtfmt_cfg_escape != 'none' + " Note: This concat order is *much* more efficient than the + " alternative (since tokens are less common than non-token chars) + let re_noneed_esc = + \'\%(['.re_old_tokrange.']' + \.'\&[^'.re_new_tokrange.']\)' + " Perform substitution + if b:txtfmt_cfg_escape == 'bslash' + " Note: The nature of global substitutions is such that the first + " char matched will always be an escaping (not an escaped) char. + silent! exe '%s/\\\(\\\%(\\*'.re_noneed_esc.'\)\@=\|'.re_noneed_esc.'\)/\1/g' + else " self-escape + silent! exe '%s/\('.re_noneed_esc.'\)\(\1\)/\1/g' + endif + endif + " Cause buffer to be refreshed with the new settings + " Note: The following events are consumed by modeline processing logic, + " which may need to alter the starttok value in a modeline + " Note: ensures that new_starttok is a string. This is important + " because it permits the modeline processing function to respect user's + " choice of hex or dec when altering the modeline. + let b:txtfmt_ml_new_starttok = new_starttok + :Refresh +endfu +endif " if !exists('*s:MoveStartTok') +" >>> +" Function: s:GetTokInfo() <<< +" Purpose: Return a string, which gives information about a token at a +" specific line/col. If optional line/col pair is not supplied, cursor +" location will be assumed. +" Inputs: +" [line] Optional arg #1. Line number of char for which info is desired. If +" present, 2nd optional arg (col) must also be supplied. +" [col] Optional arg #2. Column number of char for which info is desired. +" Note: This number is actually a byte index, such as would be +" returned by Vim's col() function. +" Return: Variable format string as follows: +" *** fg color token *** +" c [(inactive)] +" Note: is 1 based. +" Also Note: `(inactive)' is appended if the token corresponds to a color that +" is not in the active color mask +" *** bg color token *** +" k [(inactive)] +" Note: is 1 based. +" Also Note: `(inactive)' is appended if the token corresponds to a color that +" is not in the active color mask +" *** format token *** +" f<[u][b][i]> +" i.e., the format descriptor in fiducial form +" *** non-token *** +" +" *** invalid char location *** +" 'NUL' (just like Vim's ga builtin) +" *** invalid inputs *** +" (and echoerr a warning) +" Note: Will show warning to user if inputs were invalid in a syntactical +" sense. (No error msg for nonexistent char position.) +" Interface note: This function is meant to be used both from a mapping (which +" assumes cursor position) and from a command (which permits user to specify +" position). +" IMPORTANT NOTE: This function is multibyte-safe. +fu! s:GetTokInfo(...) + " The output of the if/else will be line/col of character of interest, + " assuming the inputs are valid. + if a:0 == 0 + " Character of interest is at cursor position + let line = line('.') + let col = col('.') + elseif a:0 == 1 + " Makes no sense to supply line but not column! + echoerr 'GetTokInfo(): Attempt to specify line without column' + return '' + elseif a:0 == 2 + " Check for nonnegative line number + if a:1 =~ '^[1-9][0-9]*$' + let line = a:1 + else + echoerr 'GetTokInfo(): '.a:1.' is not a valid line #' + return '' + endif + " Check for nonnegative col number + if a:2 =~ '^[1-9][0-9]*$' + let col = a:2 + else + echoerr 'GetTokInfo(): '.a:2.' is not a valid col #' + return '' + endif + else + echoerr 'GetTokInfo(): Wrong # of args - should be 0 or 2' + return '' + endif + " If here, inputs are syntactically valid and line/col represents the + " position of character about which information is desired. Obtain a + " string whose first character is the character of interest. + " Note: char2nr considers only first character in a string, so we don't + " need to strip subsequent characters yet (and we can't do so with + " byte-aware strpart anyway). + let ch = strpart(getline(line), col - 1) + " Note: If input position was invalid, ch will contain empty string. + if ch == '' + " Char pos doesn't exist - not an error + return 'NUL' + endif + " If here, we have a character! Get its character code. + let char_nr = char2nr(ch) + " Get *single* char corresponding to the char code. + " Note: strpart() and expr-[] deal with bytes not chars! + let ch = nr2char(char_nr) + " Determine the range within which token lies + if char_nr >= b:txtfmt_fmt_first_tok && char_nr <= b:txtfmt_fmt_last_tok + " fmt token + return 'f'.b:ubisrc_fmt{char_nr - b:txtfmt_fmt_first_tok} + elseif char_nr >= b:txtfmt_clr_first_tok && char_nr <= b:txtfmt_clr_last_tok + " clr token + " offset 0 = 'no color', represented by 'c-' + " offset i = txtfmtColor{i} + " Note: User-visible array is 1-based, and b:txtfmt_clr_first_tok + " corresponds to the default fg color token + let offset = char_nr - b:txtfmt_clr_first_tok + let ret_str = 'c'.(offset == 0 ? '-' : ''.offset.'') + " Distinguish between active/inactive start color tokens + if char_nr > b:txtfmt_clr_first_tok && ch !~ '['.b:txtfmt_re_clr_stok_atom.']' + let ret_str = ret_str.' (inactive)' + endif + return ret_str + elseif char_nr >= b:txtfmt_bgc_first_tok && char_nr <= b:txtfmt_bgc_last_tok + " bgc token + " offset 0 = 'no color', represented by 'k-' + " offset i = txtfmtColor{i} + " Note: User-visible array is 1-based, and b:txtfmt_bgc_first_tok + " corresponds to the default bg color token + let offset = char_nr - b:txtfmt_bgc_first_tok + let ret_str = 'k'.(offset == 0 ? '-' : ''.offset.'') + " Distinguish between active/inactive start color tokens + if char_nr > b:txtfmt_bgc_first_tok && ch !~ '['.b:txtfmt_re_bgc_stok_atom.']' + let ret_str = ret_str.' (inactive)' + endif + return ret_str + else + " Not a txtfmt token - just return ascii value + return ''.char_nr.'' + endif +endfu +" >>> +" >>> +" Configuration <<< +" Needed only for ftplugin +" Note: Performed after the Common Configuration, which sets the 'starttok' +" option, needed when processing user maps + +" Function: s:Expand_user_map_macro() <<< +" Purpose: Expand the input string, which is assumed to be the `...' in one of +" the user-map expansion sequences of the form <...>. +" Return: If the macro is valid, return the expanded text, just as it would +" appear in the rhs of the map; otherwise, an empty string. +fu! s:Expand_user_map_macro(s) + let re_ins_tok_i = '^i\\:\(.\+\)$' + let re_ins_tok_n = '^n\([1-9]\d*\)\?\\\(v\?\)\([iIaAoOs]\):\(.\+\)$' + let re_jump_to_tok = '^\([nvio]\)\([1-9]\d*\)\?\([][]\)\(t\?\)\([be]\?[fkca]\)' + " Determine which macro type we have + if a:s =~ re_ins_tok_n . '\|' . re_ins_tok_i + " Insert-token macro + if a:s[0] == 'n' + " Insert-token macro (normal) + let l:count = substitute(a:s, re_ins_tok_n, '\1', '') + let end_in_norm = substitute(a:s, re_ins_tok_n, '\2', '') == 'v' + let enter_ins_cmd = substitute(a:s, re_ins_tok_n, '\3', '') + let fmtclr_list = substitute(a:s, re_ins_tok_n, '\4', '') + else + " Insert-token macro (insert) + let fmtclr_list = substitute(a:s, re_ins_tok_i, '\1', '') + endif + " Validate / Translate the fmt/clr list + let tokstr = s:Translate_fmt_clr_list(fmtclr_list) + if tokstr=='' + " Invalid fmt/clr list + " TODO: Perhaps fix up the error string. + let s:err_str = "Invalid fmt/clr list in user map rhs: ".s:err_str + return '' + endif + " Create the mode-specific expansion text + if a:s[0] == 'n' + " normal mode + let seq = ":call Insert_tokstr('" + \.tokstr."', '".enter_ins_cmd."', 1, ".end_in_norm + \.(strlen(l:count) ? (", ".l:count) : "") + \.")" + \.":call Adjust_cursor()" + else + " insert mode + let seq = "=Insert_tokstr('".tokstr."', 'i', 1, 0)" + \."=Adjust_cursor()" + endif + elseif a:s =~ re_jump_to_tok + " Jump to token macro + let l:mode = substitute(a:s, re_jump_to_tok, '\1', '') + let l:count = substitute(a:s, re_jump_to_tok, '\2', '') + let l:dir = substitute(a:s, re_jump_to_tok, '\3', '') == '[' ? 'b' : 'f' + let l:till = substitute(a:s, re_jump_to_tok, '\4', '') == 't' ? 1 : 0 + let l:target = substitute(a:s, re_jump_to_tok, '\5', '') + if l:mode =~ '[nvo]' + let l:seq = ":call Jump_to_tok('" + \.l:mode."', '".l:target."', '".l:dir."', ".l:till + \.(strlen(l:count) ? (", ".l:count) : "") + \.")" + else + " TODO - Permit insert-mode? + let l:seq = "=Jump_to_tok('" + \.l:mode."', '".l:target."', '".l:dir."', ".l:till + \.(strlen(l:count) ? (", ".l:count) : "") + \.")" + endif + else + let s:err_str = "Invalid user-map expansion sequence: `<".a:s.">'" + return '' + endif + " If here, expansion was successul. Return the expanded text. + return seq +endfu +" >>> +" Function: s:Translate_user_map_rhs() <<< +" Purpose: Convert the rhs specified in a user map definition string to the +" rhs that will be used in the actual map command. Special <<...>> sequences +" are expanded. +" Input: rhs string as it would appear in a user-map string +" Return: The rhs as it would appear in a map command (with user-map macros +" expanded) +" Error: Set s:err_str and return empty string +fu! s:Translate_user_map_rhs(rhs) + let s = a:rhs + " Catch empty (or all ws) strings - shouldn't be input + if s =~ '^[[:space:]]*$' + let s:err_str = "f:User map rhs must contain at least 1 non-whitespace char" + return '' + endif + " Loop until special sequences are all expanded + let ret_str = '' " build up in loop + let len = strlen(s) + let i1 = 0 + let i2 = 0 + while i2 < len + " Find start of <<...>> sequence - this is safe even if i2 is index of + " next '<' + "let i1 = matchend(s, '\%(\\\_.\|[^<]\)*', i2) + let i1 = matchend(s, '<<', i2) + if i1 < 0 + " String is exhausted - accumulate up to end + let ret_str = ret_str.strpart(s, i2) + break + else + " Accumulate, prior to processing <<...>> + let ret_str = ret_str.strpart(s, i2, i1-i2-2) + endif + " Now find closing `>>' (it's not optional at this point) + let i2 = match(s, '>>', i1) + if i2 < 0 + let s:err_str = "Unmatched `<<' in user map rhs" + return '' + endif + " Extract stuff inside <<...>> + " i1 points to 1st char beyond `<<' + " i2 points to first `>' + " i1 == i2 implies empty ... + if i2 > i1 + let seq = strpart(s, i1, i2-i1) + else + let s:err_str = "Empty fmt/clr map sequence" + return '' + endif + " We have a non-empty sequence. Convert txtfmt-specific to `>' + " before passing to Expand_user_map_macro for expansion. + "let seq = substitute(seq, '\\\(.\)', '\1', 'g') + let seq = substitute(seq, '', '>', 'g') + " Expand the macro + let expseq = s:Expand_user_map_macro(seq) + " Was it valid? + if expseq == '' + let s:err_str = "Invalid usermap rhs: " . seq + return '' + endif + " Append the expanded text to the return string + let ret_str = ret_str.expseq + " Make i2 point just past `>>' (it's on the 1st `>') + let i2 = i2+2 + endwhile + " Return the now completely expanded string + return ret_str +endfu +" >>> +" Function: s:Do_user_maps() <<< +" Purpose: Process any special global variables set by user, for the purpose +" of allowing him to build his own map sequences from primitives. +" How: +fu! s:Do_user_maps() + " In the following regex, \1=map command, \2=lhs, \3=rhs + " RULES: + " map_cmd must be imap, inoremap, nmap or nnoremap + " map_lhs is terminated by first unescaped whitespace + " -whitespace may appear in lhs if preceded by + " map_rhs is everything else in the string + " -must have extra level of escaping for \ and < + " -may contain special <[in]:...> sequences + " TODO - Fix up the regex... + let re_usermap = '^\s*\([in]\%(nore\)\?map\)\s\+' + \.'\(\%('."\.".'\|\S\)\+\)\s\+\(.\+\)' + " Allow up to configurable number of user maps + " Note: txtfmtUsermaplimit option may be set globally or buflocally, with + " precedence given to buflocal set. + let bset = exists('b:txtfmtUsermaplimit') + let gset = exists('g:txtfmtUsermaplimit') + if bset || gset + let user_map_limit = bset ? b:txtfmtUsermaplimit : g:txtfmtUsermaplimit + " Validate the limit set by user + if user_map_limit !~ '^\s*\([1-9]\d*\|0x\x\+\)\s*$' + " Invalid format - Warn and abort user-map processing + echoerr "Aborting user-defined map processing: " + \.(bset ? 'b:' : 'g:').'txtfmtUsermaplimit set to invalid value: ' + \."`".user_map_limit."'" + return + endif + else + " Set default + let user_map_limit = 25 + endif + " Loop over all possible maps + let i = 1 + while i <= user_map_limit + " Determine whether buflocal or global setting exists for this element + let bset = exists('b:txtfmtUsermap'.i) + let gset = exists('g:txtfmtUsermap'.i) + if bset || gset + " Obtain the buflocal or global element + let s = bset ? b:txtfmtUsermap{i} : g:txtfmtUsermap{i} + " Validate and process the user map string + if s !~ re_usermap + echoerr 'Ignoring malformed user-defined map specified by ' + \.(bset ? 'b:' : 'g:').'txtfmtUsermap{'.i.'}: ' + \.'help txtfmt-user-map-fmt' + else + " Extract the map command and the map lhs/rhs + let map_cmd = substitute(s, re_usermap, '\1', '') + let map_lhs = substitute(s, re_usermap, '\2', '') + let map_rhs = substitute(s, re_usermap, '\3', '') + " Process non-empty rhs for special sequences + " NOTE: rhs has extra level of \ and < escaping because of the + " special embedded <...> sequences + let map_rhs = s:Translate_user_map_rhs(map_rhs) + if map_rhs=='' + echoerr "User-defined map #".i." ignored due to error: ".s:err_str + else + " Attempt to define the map + exe map_cmd.' '.map_lhs.' '.map_rhs + " Add corresponding undo action (n or i unmap) + " TODO - Figure out how to use s:Undef_map and avoid "no + " such mapping error. + call s:Add_undo(map_cmd[0].'unmap '.map_lhs) + endif + endif + endif + " Progress to next possible user map + let i = i + 1 + endwhile +endfu +" >>> +" Function: s:Set_mapwarn() <<< +" Purpose: Set txtfmt_cfg_mapwarn option either from user-supplied +" g:txtfmtMapwarn or to default value. Global var txtfmtMapwarn is a character +" flag option, which may contain the following flags: mMeEcCoO. Although the +" flags may appear in any combination and in any order, there are certain +" combinations that make no sense and should (arguably) result in a warning: +" -m and e should not be used together +" -M and E should not be used together +" Note: If either of the above 2 rules are violated, the last supplied flag +" takes precedence. +" -o should not be used without either e or m +" -O should not be used without either E or M +fu! s:Set_mapwarn() + " The following buffer-local config option is the output of this function, + " and must be set before return. + unlet! b:txtfmt_cfg_mapwarn + if exists('g:txtfmtMapwarn') + " Process value supplied by user, storing to buffer-local config + " variable a valid and normalized set of character flags. + " Design Decision: Preserve the order of flags being retained rather + " than arranging them in fiducial order. + " Note: Existence of l:mapwarn after the loop implies that no error + " was found with user-supplied option value. (Note that empty string + " is a valid setting.) + let mapwarn = '' + let i = strlen(g:txtfmtMapwarn) - 1 + while i >= 0 + let ch = g:txtfmtMapwarn[i] + if ch !~ '[mMeEcCoO]' + " Invalid flag! + unlet mapwarn + break + endif + " Make sure flags already in mapwarn don't preclude addition of + " this one. + if ( + \-1 == stridx(mapwarn, ch) && + \(ch != 'm' || -1 == stridx(mapwarn, 'e')) && + \(ch != 'e' || -1 == stridx(mapwarn, 'm')) && + \(ch != 'M' || -1 == stridx(mapwarn, 'E')) && + \(ch != 'E' || -1 == stridx(mapwarn, 'M')) + \) + " Prepend the flag to preserve order. (Recall that loop is in + " reverse order.) + let mapwarn = ch . mapwarn + endif + " Retreat to preceding character flag + let i = i - 1 + endwhile + if exists('l:mapwarn') + " No errors were encountered in the set of mapwarn. + let b:txtfmt_cfg_mapwarn = mapwarn + else + " Warn user that his setting was not valid + echomsg "Ignoring invalid setting of txtfmtMapwarn: `".g:txtfmtMapwarn + \."' (:he txtfmtMapwarn)" + endif + endif + " If option was not set by user to a valid value, set to default + if !exists('b:txtfmt_cfg_mapwarn') + " Use default + let b:txtfmt_cfg_mapwarn = 'mMoOcC' + endif +endfu +" >>> +" Function: s:Define_user_map_defaults() <<< +" Purpose: Set up some default user maps for testing... +fu! s:Define_user_map_defaults() + " User map definition examples for test <<< + + " Map CTRL-B in insert mode to start and terminate a 'bold' region, + " leaving the cursor positioned in the region interior, ready to type bold + " text. + " Hint: Similar maps might be created for underline and italic + let g:txtfmtUsermap1 = 'inoremap <>' + + " Map CTRL-\f in insert mode to end current format region. + let g:txtfmtUsermap2 = 'inoremap f <>' + + " Map CTRL-\k in insert mode to end current bg color region. + let g:txtfmtUsermap3 = 'inoremap k <>' + + " Map \t in normal mode to embolden, underline and center (i.e. + " 'title-ize') the current line + let g:txtfmtUsermap4 = + \'nnoremap t <><>:ce' + + " Map \cf in normal mode to change all text within the current format + " region (without deleting the tokens that begin and end the region). + " Note: Since the default jump-to-token mappings are used in the rhs + " (rather than the special expansion macros), nmap must be used (rather + " than nnoremap). + " Note: The reason the ]f does not cause the format 'end region' token to + " be deleted is that the operator-pending jump-to-token maps work + " 'exclusively' when there is no 'v' between operator and motion. + let g:txtfmtUsermap5 = + \'nmap cf [tbfc]f' + + " Same as preceding map but for current color region. + " Note: This one demonstrates the use of the 'jump-to-token' expansion + " macros. + let g:txtfmtUsermap6 = + \'nnoremap cc <>c<>' + + " Map bw in normal mode to embolden the word under the + " cursor. (The extra complexity is needed to ensure that you can invoke + " with cursor anywhere on the word.) + let g:txtfmtUsermap7 = + \'nnoremap bw :if col(".")!=1 && ' + \.'getline(".")[col(".")-2]=~"\\w"exe "norm! b"' + \.'endif<>e<>b' + + " Map \vf in normal mode to select all of the current format region + " visually. + " Note: Unlike the earlier one for changing the current format region, + " this one doesn't constrain the backwards jump to a 'begin' region token; + " hence, it will also highlight the text between regions. + let g:txtfmtUsermap8 = + \'nnoremap vf <>v<>' + + " Map vf in insert mode to do the same in insert mode + let g:txtfmtUsermap9 = + \'inoremap vf <>lv<>' + + " Map in normal mode to jump forward to the 3rd + " 'begin format region' token. (Not overly practical, but demonstrates the + " use of whitespace in the lhs, as well as the use of the optional count + " with the jump-to-token expansion macros.) + let g:txtfmtUsermap10 = + \'nnoremap <>' + + " Map _ in normal mode to substitute the next 4 characters + " with a 'bold' format token followed by a 'no format' token, leaving the + " cursor positioned between the two. + " (This map is not intended to be useful, but merely to demonstrate the + " specification of a count with an insert-token expansion macro.) + let g:txtfmtUsermap11 = + \'nnoremap _ <>' + + " Map rb in normal mode to make the current line bold with a + " red background. + let g:txtfmtUsermap12 = + \'nnoremap rb <><>' + " >>> +endfu +" >>> +" Function: s:Do_config() <<< +" Purpose: Set script local variables, taking into account whether user has +" overriden via txtfmt globals. +fu! s:Do_config() + " set vim 'iskeyword' option <<< + " Exclude the special tokens from iskeyword option, so that word movement + " normal commands will work intuitively. (Recall that the delimiters will + " appear as space characters.) + " IMPORTANT NOTE: Ideally, we would be able to have the tokens treated + " just like whitespace, from the standpoint of word and WORD motions; + " unfortunately, we can't instruct Vim to do this - the best we can do is + " make them non-keyword, which means they'll be treated like punctation; + " i.e., word motions will stop on them and on the beginning of subsequent + " word. + " IMPORTANT TODO: Vim doesn't allow multi-byte characters above 255 to be + " excluded! + " Decide whether there's a workaround. For now, don't do this if we're + " dealing with tokens above 255. + " Note: I'm intentionally including inactive color tokens in the ranges. + " Rationale: I don't feel that the complexity that would be added by the + " logic to exclude them is justified by any advantage doing so would + " provide. + if (b:txtfmt_last_tok <= 255) + let val = '^'.b:txtfmt_clr_first_tok.'-'.b:txtfmt_last_tok + exe 'setlocal iskeyword+='.val + call s:Add_undo('setlocal iskeyword-='.val) + endif + " >>> + " Process txtfmtMapwarn option <<< + call s:Set_mapwarn() + " >>> + " txtfmtUsermaplimit: Max # of user maps that will be checked <<< + " Allow nonnegative dec, hex, or oct + " Cannot set from modeline + if exists('g:txtfmtUsermaplimit') && g:txtfmtUsermaplimit =~ '^\%(0[xX]\)\?[0-9]\+$' + let s:txtfmtUsermaplimit = g:txtfmtUsermaplimit + else + " Set to reasonable default + let s:txtfmtUsermaplimit = 25 + endif + " >>> + " TEST ONLY: Define some default user-maps for testing <<< + "call s:Define_user_map_defaults() + " >>> + " Process any user-defined maps <<< + call s:Do_user_maps() + " >>> +endfu +" >>> +call s:Do_config() +" >>> +" Public-interface functions <<< +" Function: g:Txtfmt_GetTokInfo() <<< +" !!!!!!!!!!!!!!!!!!!!!! +" !!!!! DEPRECATED !!!!! +" !!!!!!!!!!!!!!!!!!!!!! +" Purpose: Return a string, which gives information about a token at a +" specific line/col. If optional line/col pair is not supplied, cursor +" location will be assumed. +" Important Note: This function is conceptually a wrapper for script-local +" s:GetTokInfo. For backwards-compatibility reasons, however, the meaning of +" the 'col' parameter is slightly different. For this function, col represents +" a 1-based char index; for s:GetTokInfo it is a 1-based byte index. +" Note: See s:GetTokInfo for additional description +" Interface note: This function is meant to be used by plugin user; e.g., from +" mappings. +" IMPORTANT NOTE: This function now works for multibyte encodings. +fu! Txtfmt_GetTokInfo(...) + " Call s:GetTokInfo with the appropriate arguments + if a:0 == 0 + return s:GetTokInfo() + elseif a:0 == 1 + " Makes no sense to supply line but not column! + echoerr 'Txtfmt_GetTokInfo(): Attempt to specify line without column' + return '' + elseif a:0 == 2 + " Check for nonnegative line number + if a:1 =~ '^[1-9][0-9]*$' + let line = a:1 + else + echoerr 'Txtfmt_GetTokInfo(): '.a:1.' is not a valid line #' + return '' + endif + " Check for nonnegative col number + if a:2 =~ '^[1-9][0-9]*$' + " Note: Input col is 1-based character index. Use byteidx to convert + " to 1-based byte index for strpart. + let col = byteidx(getline(line), a:2 - 1) + 1 + if col == 0 + " Invalid (too large) col position - not error... + return 'NUL' + else + return s:GetTokInfo(line, col) + endif + else + echoerr 'Txtfmt_GetTokInfo(): '.a:2.' is not a valid col #' + return '' + endif + else + echoerr 'Txtfmt_GetTokInfo(): Wrong # of args - should be 0 or 2' + return '' + endif +endfu +" >>> +" Function: g:OldTxtfmt_GetTokInfo() <<< +" Purpose: Return a string, which gives information about a token at a +" specific line/col. If optional line/col pair is not supplied, cursor +" location will be assumed. +" Inputs: +" [line] Optional arg #1. Line number of char for which info is desired. If +" present, 2nd optional arg (col) must also be supplied. +" [col] Optional arg #2. Column number of char for which info is desired. +" NOTE: Currently, even when a multi-byte encoding is used, [col] is used as a +" byte offset rather than a character offset. +" TODO: Decide whether I should stop obtaining the character via +" getline()[pos] in favor of a multi-byte safe way. +" Return: Variable format string as follows: +" *** color token *** +" c +" Note: is 1 based. +" *** format token *** +" f<[u][b][i]> +" i.e., the format descriptor in fiducial form +" *** non-token *** +" +" *** invalid char location or wrong # of inputs *** +" +" Note: Will show warning to user if inputs were invalid in a syntactical +" sense. (No error msg for nonexistent char position.) +" Interface note: This function is meant to be used by plugin user; e.g., from +" mappings. +" IMPORTANT NOTE: This function now works for multibyte encodings. +" TODO_BG: Delete this "old" version of the function if I haven't rolled back +" prior to the release of 2.0... +fu! OldTxtfmt_GetTokInfo(...) + " The output of the if/else will be a variable (ch) whose first character + " is the token about which information is requested + if a:0 == 0 + let ch = strpart(getline('.'), col('.') - 1) + elseif a:0 == 1 + " Makes no sense to supply line but not column! + echoerr 'Txtfmt_GetTokInfo(): Attempt to specify line without column' + return '' + elseif a:0 == 2 + " Check for nonnegative line number + if a:1 =~ '^[1-9][0-9]*$' + let line = a:1 + else + echoerr 'Txtfmt_GetTokInfo(): '.a:1.' is not a valid line #' + return '' + endif + " Check for nonnegative col number + if a:2 =~ '^[1-9][0-9]*$' + " Note: Input col is 1-based character index. Use byteidx to convert + " to byte index for strpart. + let col0 = byteidx(getline(line), a:2 - 1) + if col0 == -1 + " Invalid (too large) col position - not error... + let ch = '' + else + let ch = strpart(getline(line), col0) + endif + else + echoerr 'Txtfmt_GetTokInfo(): '.a:2.' is not a valid col #' + return '' + endif + else + echoerr 'Txtfmt_GetTokInfo(): Wrong # of args - should be 0 or 2' + return '' + endif + " If here, inputs are syntactically valid and ch holds a string whose + " first character is the one about which info is requested, or empty + " string if the requested position is invalid. + if ch == '' + " Char pos doesn't exist - not an error + return '' + endif + let char_nr = char2nr(ch) + " Determine the range within which token lies + if char_nr >= b:txtfmt_fmt_first_tok && char_nr <= b:txtfmt_fmt_last_tok + " fmt token + return 'f'.b:ubisrc_fmt{char_nr - b:txtfmt_fmt_first_tok} + elseif char_nr >= b:txtfmt_clr_first_tok && char_nr <= b:txtfmt_clr_last_tok + " clr token + " offset 0 = 'no color', represented by 'c-' + " offset i = color{i-1} + let offset = char_nr - b:txtfmt_clr_first_tok + return 'c'.(offset == 0 ? '-' : ''.(offset-1).'') + else + " Not a txtfmt token - just return ascii value + return ''.char_nr.'' + endif +endfu +" >>> +" Function: g:Txtfmt_GetTokStr() <<< +" Purpose: Translate the input fmt/clr spec list and return the resulting +" token string. +" Inputs: +" s fmt/clr spec list to be translated +" Return: If input spec list is valid, the corresponding literal token +" sequence is returned as a string; otherwise, empty string is returned and +" error msg is output. +fu! Txtfmt_GetTokStr(s) + " Make sure this is a txtfmt buffer + if !exists('b:loaded_txtfmt') + echoerr "Function Txtfmt_GetTokStr can be used only within a 'txtfmt' buffer" + return '' + endif + " Call script-local function to perform the translation + let tokstr = s:Translate_fmt_clr_list(a:s) + if (tokstr == '') + echoerr "`".a:s."' is not a valid fmt/clr spec list" + return '' + else + " We have a translated fmt/clr spec comprising an offset followed by + " the actual fmt/clr token sequence. Extract the literal token string + " and throw the offset away. + " TODO - Embed this in a special accessor function that may be used + " elsewhere... + let tokstr = substitute(tokstr, '\(\-\?[[:digit:]]\+\),\(.*\)', '\2', '') + return tokstr + endif +endfu +" >>> +" >>> +" Public-interface commands <<< +com! -buffer ShowTokenMap call ShowTokenMap() +com! -buffer -nargs=? MoveStartTok call MoveStartTok() +com! -buffer -nargs=* GetTokInfo echo GetTokInfo() +" >>> +" MAPS: LEVEL 1 & 2 (reconfig): normal/insert mode --> ... mappings <<< +" Note: used (rather than ) to prevent side-effect when insert-mode +" mapping invoked past end of line (cursor pos off by 1) +" normal mode jump 'to' token mappings <<< +" Align sequence <<< +" AlignCtrl default +" AlignCtrl w=p0P1 , +" AlignCtrl g ^call +" '<,'>Align +" >>> +call s:Def_map('n', '[bf', 'TxtfmtBckToFmtBegTok', ":call Jump_to_tok('n', 'bf', 'b', 0)") +call s:Def_map('n', ']bf', 'TxtfmtFwdToFmtBegTok', ":call Jump_to_tok('n', 'bf', 'f', 0)") +call s:Def_map('n', '[bc', 'TxtfmtBckToClrBegTok', ":call Jump_to_tok('n', 'bc', 'b', 0)") +call s:Def_map('n', ']bc', 'TxtfmtFwdToClrBegTok', ":call Jump_to_tok('n', 'bc', 'f', 0)") +call s:Def_map('n', '[bk', 'TxtfmtBckToBgcBegTok', ":call Jump_to_tok('n', 'bk', 'b', 0)") +call s:Def_map('n', ']bk', 'TxtfmtFwdToBgcBegTok', ":call Jump_to_tok('n', 'bk', 'f', 0)") +call s:Def_map('n', '[ba', 'TxtfmtBckToAnyBegTok', ":call Jump_to_tok('n', 'ba', 'b', 0)") +call s:Def_map('n', ']ba', 'TxtfmtFwdToAnyBegTok', ":call Jump_to_tok('n', 'ba', 'f', 0)") +call s:Def_map('n', '[f' , 'TxtfmtBckToFmtTok' , ":call Jump_to_tok('n', 'f' , 'b', 0)") +call s:Def_map('n', ']f' , 'TxtfmtFwdToFmtTok' , ":call Jump_to_tok('n', 'f' , 'f', 0)") +call s:Def_map('n', '[c' , 'TxtfmtBckToClrTok' , ":call Jump_to_tok('n', 'c' , 'b', 0)") +call s:Def_map('n', ']c' , 'TxtfmtFwdToClrTok' , ":call Jump_to_tok('n', 'c' , 'f', 0)") +call s:Def_map('n', '[k' , 'TxtfmtBckToBgcTok' , ":call Jump_to_tok('n', 'k' , 'b', 0)") +call s:Def_map('n', ']k' , 'TxtfmtFwdToBgcTok' , ":call Jump_to_tok('n', 'k' , 'f', 0)") +call s:Def_map('n', '[a' , 'TxtfmtBckToAnyTok' , ":call Jump_to_tok('n', 'a' , 'b', 0)") +call s:Def_map('n', ']a' , 'TxtfmtFwdToAnyTok' , ":call Jump_to_tok('n', 'a' , 'f', 0)") +call s:Def_map('n', '[ef', 'TxtfmtBckToFmtEndTok', ":call Jump_to_tok('n', 'ef', 'b', 0)") +call s:Def_map('n', ']ef', 'TxtfmtFwdToFmtEndTok', ":call Jump_to_tok('n', 'ef', 'f', 0)") +call s:Def_map('n', '[ec', 'TxtfmtBckToClrEndTok', ":call Jump_to_tok('n', 'ec', 'b', 0)") +call s:Def_map('n', ']ec', 'TxtfmtFwdToClrEndTok', ":call Jump_to_tok('n', 'ec', 'f', 0)") +call s:Def_map('n', '[ek', 'TxtfmtBckToBgcEndTok', ":call Jump_to_tok('n', 'ek', 'b', 0)") +call s:Def_map('n', ']ek', 'TxtfmtFwdToBgcEndTok', ":call Jump_to_tok('n', 'ek', 'f', 0)") +call s:Def_map('n', '[ea', 'TxtfmtBckToAnyEndTok', ":call Jump_to_tok('n', 'ea', 'b', 0)") +call s:Def_map('n', ']ea', 'TxtfmtFwdToAnyEndTok', ":call Jump_to_tok('n', 'ea', 'f', 0)") +" >>> +" visual mode jump 'to' token mappings <<< +call s:Def_map('v', '[bf', 'TxtfmtBckToFmtBegTok', ":call Jump_to_tok('v', 'bf', 'b', 0)") +call s:Def_map('v', ']bf', 'TxtfmtFwdToFmtBegTok', ":call Jump_to_tok('v', 'bf', 'f', 0)") +call s:Def_map('v', '[bc', 'TxtfmtBckToClrBegTok', ":call Jump_to_tok('v', 'bc', 'b', 0)") +call s:Def_map('v', ']bc', 'TxtfmtFwdToClrBegTok', ":call Jump_to_tok('v', 'bc', 'f', 0)") +call s:Def_map('v', '[bk', 'TxtfmtBckToBgcBegTok', ":call Jump_to_tok('v', 'bk', 'b', 0)") +call s:Def_map('v', ']bk', 'TxtfmtFwdToBgcBegTok', ":call Jump_to_tok('v', 'bk', 'f', 0)") +call s:Def_map('v', '[ba', 'TxtfmtBckToAnyBegTok', ":call Jump_to_tok('v', 'ba', 'b', 0)") +call s:Def_map('v', ']ba', 'TxtfmtFwdToAnyBegTok', ":call Jump_to_tok('v', 'ba', 'f', 0)") +call s:Def_map('v', '[f' , 'TxtfmtBckToFmtTok' , ":call Jump_to_tok('v', 'f' , 'b', 0)") +call s:Def_map('v', ']f' , 'TxtfmtFwdToFmtTok' , ":call Jump_to_tok('v', 'f' , 'f', 0)") +call s:Def_map('v', '[c' , 'TxtfmtBckToClrTok' , ":call Jump_to_tok('v', 'c' , 'b', 0)") +call s:Def_map('v', ']c' , 'TxtfmtFwdToClrTok' , ":call Jump_to_tok('v', 'c' , 'f', 0)") +call s:Def_map('v', '[k' , 'TxtfmtBckToBgcTok' , ":call Jump_to_tok('v', 'k' , 'b', 0)") +call s:Def_map('v', ']k' , 'TxtfmtFwdToBgcTok' , ":call Jump_to_tok('v', 'k' , 'f', 0)") +call s:Def_map('v', '[a' , 'TxtfmtBckToAnyTok' , ":call Jump_to_tok('v', 'a' , 'b', 0)") +call s:Def_map('v', ']a' , 'TxtfmtFwdToAnyTok' , ":call Jump_to_tok('v', 'a' , 'f', 0)") +call s:Def_map('v', '[ef', 'TxtfmtBckToFmtEndTok', ":call Jump_to_tok('v', 'ef', 'b', 0)") +call s:Def_map('v', ']ef', 'TxtfmtFwdToFmtEndTok', ":call Jump_to_tok('v', 'ef', 'f', 0)") +call s:Def_map('v', '[ec', 'TxtfmtBckToClrEndTok', ":call Jump_to_tok('v', 'ec', 'b', 0)") +call s:Def_map('v', ']ec', 'TxtfmtFwdToClrEndTok', ":call Jump_to_tok('v', 'ec', 'f', 0)") +call s:Def_map('v', '[ek', 'TxtfmtBckToBgcEndTok', ":call Jump_to_tok('v', 'ek', 'b', 0)") +call s:Def_map('v', ']ek', 'TxtfmtFwdToBgcEndTok', ":call Jump_to_tok('v', 'ek', 'f', 0)") +call s:Def_map('v', '[ea', 'TxtfmtBckToAnyEndTok', ":call Jump_to_tok('v', 'ea', 'b', 0)") +call s:Def_map('v', ']ea', 'TxtfmtFwdToAnyEndTok', ":call Jump_to_tok('v', 'ea', 'f', 0)") +" >>> +" operator-pending mode jump 'to' token mappings <<< +" Note: 'v' can be used with these to toggle inclusive/exclusive +call s:Def_map('o', '[bf', 'TxtfmtBckToFmtBegTok', ":call Jump_to_tok('o', 'bf', 'b', 0)") +call s:Def_map('o', ']bf', 'TxtfmtFwdToFmtBegTok', ":call Jump_to_tok('o', 'bf', 'f', 0)") +call s:Def_map('o', '[bc', 'TxtfmtBckToClrBegTok', ":call Jump_to_tok('o', 'bc', 'b', 0)") +call s:Def_map('o', ']bc', 'TxtfmtFwdToClrBegTok', ":call Jump_to_tok('o', 'bc', 'f', 0)") +call s:Def_map('o', '[bk', 'TxtfmtBckToBgcBegTok', ":call Jump_to_tok('o', 'bk', 'b', 0)") +call s:Def_map('o', ']bk', 'TxtfmtFwdToBgcBegTok', ":call Jump_to_tok('o', 'bk', 'f', 0)") +call s:Def_map('o', '[ba', 'TxtfmtBckToAnyBegTok', ":call Jump_to_tok('o', 'ba', 'b', 0)") +call s:Def_map('o', ']ba', 'TxtfmtFwdToAnyBegTok', ":call Jump_to_tok('o', 'ba', 'f', 0)") +call s:Def_map('o', '[f' , 'TxtfmtBckToFmtTok' , ":call Jump_to_tok('o', 'f' , 'b', 0)") +call s:Def_map('o', ']f' , 'TxtfmtFwdToFmtTok' , ":call Jump_to_tok('o', 'f' , 'f', 0)") +call s:Def_map('o', '[c' , 'TxtfmtBckToClrTok' , ":call Jump_to_tok('o', 'c' , 'b', 0)") +call s:Def_map('o', ']c' , 'TxtfmtFwdToClrTok' , ":call Jump_to_tok('o', 'c' , 'f', 0)") +call s:Def_map('o', '[k' , 'TxtfmtBckToBgcTok' , ":call Jump_to_tok('o', 'k' , 'b', 0)") +call s:Def_map('o', ']k' , 'TxtfmtFwdToBgcTok' , ":call Jump_to_tok('o', 'k' , 'f', 0)") +call s:Def_map('o', '[a' , 'TxtfmtBckToAnyTok' , ":call Jump_to_tok('o', 'a' , 'b', 0)") +call s:Def_map('o', ']a' , 'TxtfmtFwdToAnyTok' , ":call Jump_to_tok('o', 'a' , 'f', 0)") +call s:Def_map('o', '[ef', 'TxtfmtBckToFmtEndTok', ":call Jump_to_tok('o', 'ef', 'b', 0)") +call s:Def_map('o', ']ef', 'TxtfmtFwdToFmtEndTok', ":call Jump_to_tok('o', 'ef', 'f', 0)") +call s:Def_map('o', '[ec', 'TxtfmtBckToClrEndTok', ":call Jump_to_tok('o', 'ec', 'b', 0)") +call s:Def_map('o', ']ec', 'TxtfmtFwdToClrEndTok', ":call Jump_to_tok('o', 'ec', 'f', 0)") +call s:Def_map('o', '[ek', 'TxtfmtBckToBgcEndTok', ":call Jump_to_tok('o', 'ek', 'b', 0)") +call s:Def_map('o', ']ek', 'TxtfmtFwdToBgcEndTok', ":call Jump_to_tok('o', 'ek', 'f', 0)") +call s:Def_map('o', '[ea', 'TxtfmtBckToAnyEndTok', ":call Jump_to_tok('o', 'ea', 'b', 0)") +call s:Def_map('o', ']ea', 'TxtfmtFwdToAnyEndTok', ":call Jump_to_tok('o', 'ea', 'f', 0)") +" >>> +" normal mode jump 'till' token mappings <<< +call s:Def_map('n', '[tbf', 'TxtfmtBckTillFmtBegTok', ":call Jump_to_tok('n', 'bf', 'b', 1)") +call s:Def_map('n', ']tbf', 'TxtfmtFwdTillFmtBegTok', ":call Jump_to_tok('n', 'bf', 'f', 1)") +call s:Def_map('n', '[tbc', 'TxtfmtBckTillClrBegTok', ":call Jump_to_tok('n', 'bc', 'b', 1)") +call s:Def_map('n', ']tbc', 'TxtfmtFwdTillClrBegTok', ":call Jump_to_tok('n', 'bc', 'f', 1)") +call s:Def_map('n', '[tbk', 'TxtfmtBckTillBgcBegTok', ":call Jump_to_tok('n', 'bk', 'b', 1)") +call s:Def_map('n', ']tbk', 'TxtfmtFwdTillBgcBegTok', ":call Jump_to_tok('n', 'bk', 'f', 1)") +call s:Def_map('n', '[tba', 'TxtfmtBckTillAnyBegTok', ":call Jump_to_tok('n', 'ba', 'b', 1)") +call s:Def_map('n', ']tba', 'TxtfmtFwdTillAnyBegTok', ":call Jump_to_tok('n', 'ba', 'f', 1)") +call s:Def_map('n', '[tf' , 'TxtfmtBckTillFmtTok' , ":call Jump_to_tok('n', 'f' , 'b', 1)") +call s:Def_map('n', ']tf' , 'TxtfmtFwdTillFmtTok' , ":call Jump_to_tok('n', 'f' , 'f', 1)") +call s:Def_map('n', '[tc' , 'TxtfmtBckTillClrTok' , ":call Jump_to_tok('n', 'c' , 'b', 1)") +call s:Def_map('n', ']tc' , 'TxtfmtFwdTillClrTok' , ":call Jump_to_tok('n', 'c' , 'f', 1)") +call s:Def_map('n', '[tk' , 'TxtfmtBckTillBgcTok' , ":call Jump_to_tok('n', 'k' , 'b', 1)") +call s:Def_map('n', ']tk' , 'TxtfmtFwdTillBgcTok' , ":call Jump_to_tok('n', 'k' , 'f', 1)") +call s:Def_map('n', '[ta' , 'TxtfmtBckTillAnyTok' , ":call Jump_to_tok('n', 'a' , 'b', 1)") +call s:Def_map('n', ']ta' , 'TxtfmtFwdTillAnyTok' , ":call Jump_to_tok('n', 'a' , 'f', 1)") +call s:Def_map('n', '[tef', 'TxtfmtBckTillFmtEndTok', ":call Jump_to_tok('n', 'ef', 'b', 1)") +call s:Def_map('n', ']tef', 'TxtfmtFwdTillFmtEndTok', ":call Jump_to_tok('n', 'ef', 'f', 1)") +call s:Def_map('n', '[tec', 'TxtfmtBckTillClrEndTok', ":call Jump_to_tok('n', 'ec', 'b', 1)") +call s:Def_map('n', ']tec', 'TxtfmtFwdTillClrEndTok', ":call Jump_to_tok('n', 'ec', 'f', 1)") +call s:Def_map('n', '[tek', 'TxtfmtBckTillBgcEndTok', ":call Jump_to_tok('n', 'ek', 'b', 1)") +call s:Def_map('n', ']tek', 'TxtfmtFwdTillBgcEndTok', ":call Jump_to_tok('n', 'ek', 'f', 1)") +call s:Def_map('n', '[tea', 'TxtfmtBckTillAnyEndTok', ":call Jump_to_tok('n', 'ea', 'b', 1)") +call s:Def_map('n', ']tea', 'TxtfmtFwdTillAnyEndTok', ":call Jump_to_tok('n', 'ea', 'f', 1)") +" >>> +" visual mode jump 'till' token mappings <<< +call s:Def_map('v', '[tbf', 'TxtfmtBckTillFmtBegTok', ":call Jump_to_tok('v', 'bf', 'b', 1)") +call s:Def_map('v', ']tbf', 'TxtfmtFwdTillFmtBegTok', ":call Jump_to_tok('v', 'bf', 'f', 1)") +call s:Def_map('v', '[tbc', 'TxtfmtBckTillClrBegTok', ":call Jump_to_tok('v', 'bc', 'b', 1)") +call s:Def_map('v', ']tbc', 'TxtfmtFwdTillClrBegTok', ":call Jump_to_tok('v', 'bc', 'f', 1)") +call s:Def_map('v', '[tbk', 'TxtfmtBckTillBgcBegTok', ":call Jump_to_tok('v', 'bk', 'b', 1)") +call s:Def_map('v', ']tbk', 'TxtfmtFwdTillBgcBegTok', ":call Jump_to_tok('v', 'bk', 'f', 1)") +call s:Def_map('v', '[tba', 'TxtfmtBckTillAnyBegTok', ":call Jump_to_tok('v', 'ba', 'b', 1)") +call s:Def_map('v', ']tba', 'TxtfmtFwdTillAnyBegTok', ":call Jump_to_tok('v', 'ba', 'f', 1)") +call s:Def_map('v', '[tf' , 'TxtfmtBckTillFmtTok' , ":call Jump_to_tok('v', 'f' , 'b', 1)") +call s:Def_map('v', ']tf' , 'TxtfmtFwdTillFmtTok' , ":call Jump_to_tok('v', 'f' , 'f', 1)") +call s:Def_map('v', '[tc' , 'TxtfmtBckTillClrTok' , ":call Jump_to_tok('v', 'c' , 'b', 1)") +call s:Def_map('v', ']tc' , 'TxtfmtFwdTillClrTok' , ":call Jump_to_tok('v', 'c' , 'f', 1)") +call s:Def_map('v', '[tk' , 'TxtfmtBckTillBgcTok' , ":call Jump_to_tok('v', 'k' , 'b', 1)") +call s:Def_map('v', ']tk' , 'TxtfmtFwdTillBgcTok' , ":call Jump_to_tok('v', 'k' , 'f', 1)") +call s:Def_map('v', '[ta' , 'TxtfmtBckTillAnyTok' , ":call Jump_to_tok('v', 'a' , 'b', 1)") +call s:Def_map('v', ']ta' , 'TxtfmtFwdTillAnyTok' , ":call Jump_to_tok('v', 'a' , 'f', 1)") +call s:Def_map('v', '[tef', 'TxtfmtBckTillFmtEndTok', ":call Jump_to_tok('v', 'ef', 'b', 1)") +call s:Def_map('v', ']tef', 'TxtfmtFwdTillFmtEndTok', ":call Jump_to_tok('v', 'ef', 'f', 1)") +call s:Def_map('v', '[tec', 'TxtfmtBckTillClrEndTok', ":call Jump_to_tok('v', 'ec', 'b', 1)") +call s:Def_map('v', ']tec', 'TxtfmtFwdTillClrEndTok', ":call Jump_to_tok('v', 'ec', 'f', 1)") +call s:Def_map('v', '[tek', 'TxtfmtBckTillBgcEndTok', ":call Jump_to_tok('v', 'ek', 'b', 1)") +call s:Def_map('v', ']tek', 'TxtfmtFwdTillBgcEndTok', ":call Jump_to_tok('v', 'ek', 'f', 1)") +call s:Def_map('v', '[tea', 'TxtfmtBckTillAnyEndTok', ":call Jump_to_tok('v', 'ea', 'b', 1)") +call s:Def_map('v', ']tea', 'TxtfmtFwdTillAnyEndTok', ":call Jump_to_tok('v', 'ea', 'f', 1)") +" >>> +" operator-pending mode jump 'till' token mappings <<< +" Note: 'v' can be used with these to toggle inclusive/exclusive +call s:Def_map('o', '[tbf', 'TxtfmtBckTillFmtBegTok', ":call Jump_to_tok('o', 'bf', 'b', 1)") +call s:Def_map('o', ']tbf', 'TxtfmtFwdTillFmtBegTok', ":call Jump_to_tok('o', 'bf', 'f', 1)") +call s:Def_map('o', '[tbc', 'TxtfmtBckTillClrBegTok', ":call Jump_to_tok('o', 'bc', 'b', 1)") +call s:Def_map('o', ']tbc', 'TxtfmtFwdTillClrBegTok', ":call Jump_to_tok('o', 'bc', 'f', 1)") +call s:Def_map('o', '[tbk', 'TxtfmtBckTillBgcBegTok', ":call Jump_to_tok('o', 'bk', 'b', 1)") +call s:Def_map('o', ']tbk', 'TxtfmtFwdTillBgcBegTok', ":call Jump_to_tok('o', 'bk', 'f', 1)") +call s:Def_map('o', '[tba', 'TxtfmtBckTillAnyBegTok', ":call Jump_to_tok('o', 'ba', 'b', 1)") +call s:Def_map('o', ']tba', 'TxtfmtFwdTillAnyBegTok', ":call Jump_to_tok('o', 'ba', 'f', 1)") +call s:Def_map('o', '[tf' , 'TxtfmtBckTillFmtTok' , ":call Jump_to_tok('o', 'f' , 'b', 1)") +call s:Def_map('o', ']tf' , 'TxtfmtFwdTillFmtTok' , ":call Jump_to_tok('o', 'f' , 'f', 1)") +call s:Def_map('o', '[tc' , 'TxtfmtBckTillClrTok' , ":call Jump_to_tok('o', 'c' , 'b', 1)") +call s:Def_map('o', ']tc' , 'TxtfmtFwdTillClrTok' , ":call Jump_to_tok('o', 'c' , 'f', 1)") +call s:Def_map('o', '[tk' , 'TxtfmtBckTillBgcTok' , ":call Jump_to_tok('o', 'k' , 'b', 1)") +call s:Def_map('o', ']tk' , 'TxtfmtFwdTillBgcTok' , ":call Jump_to_tok('o', 'k' , 'f', 1)") +call s:Def_map('o', '[ta' , 'TxtfmtBckTillAnyTok' , ":call Jump_to_tok('o', 'a' , 'b', 1)") +call s:Def_map('o', ']ta' , 'TxtfmtFwdTillAnyTok' , ":call Jump_to_tok('o', 'a' , 'f', 1)") +call s:Def_map('o', '[tef', 'TxtfmtBckTillFmtEndTok', ":call Jump_to_tok('o', 'ef', 'b', 1)") +call s:Def_map('o', ']tef', 'TxtfmtFwdTillFmtEndTok', ":call Jump_to_tok('o', 'ef', 'f', 1)") +call s:Def_map('o', '[tec', 'TxtfmtBckTillClrEndTok', ":call Jump_to_tok('o', 'ec', 'b', 1)") +call s:Def_map('o', ']tec', 'TxtfmtFwdTillClrEndTok', ":call Jump_to_tok('o', 'ec', 'f', 1)") +call s:Def_map('o', '[tek', 'TxtfmtBckTillBgcEndTok', ":call Jump_to_tok('o', 'ek', 'b', 1)") +call s:Def_map('o', ']tek', 'TxtfmtFwdTillBgcEndTok', ":call Jump_to_tok('o', 'ek', 'f', 1)") +call s:Def_map('o', '[tea', 'TxtfmtBckTillAnyEndTok', ":call Jump_to_tok('o', 'ea', 'b', 1)") +call s:Def_map('o', ']tea', 'TxtfmtFwdTillAnyEndTok', ":call Jump_to_tok('o', 'ea', 'f', 1)") +" >>> +" normal mode insert token mappings <<< +" These mappings may be used from normal mode to insert special tokens. +" Note: The first set leaves cursor in insert mode, and is probably the most +" useful. The second set enters insert mode to do the insert and puts cursor +" at correct offset prior to returning to normal mode. Works just like +" inserting the token, then hitting . +" TODO - This one is redundant to the \vi one - use the latter instead for +" notational consistency? +call s:Def_map('n', '', 'TxtfmtInsertTok_n', + \":call Insert_tokstr('', 'i', 0, 0)" + \.":call Adjust_cursor()") +" Start in normal / End in insert +call s:Def_map('n', 'i', 'TxtfmtInsertTok_i', + \":call Insert_tokstr('', 'i', 0, 0)" + \.":call Adjust_cursor()") +call s:Def_map('n', 'I', 'TxtfmtInsertTok_I', + \":call Insert_tokstr('', 'I', 0, 0)" + \.":call Adjust_cursor()") +call s:Def_map('n', 'a', 'TxtfmtInsertTok_a', + \":call Insert_tokstr('', 'a', 0, 0)" + \.":call Adjust_cursor()") +call s:Def_map('n', 'A', 'TxtfmtInsertTok_A', + \":call Insert_tokstr('', 'A', 0, 0)" + \.":call Adjust_cursor()") +call s:Def_map('n', 'o', 'TxtfmtInsertTok_o', + \":call Insert_tokstr('', 'o', 0, 0)" + \.":call Adjust_cursor()") +call s:Def_map('n', 'O', 'TxtfmtInsertTok_O', + \":call Insert_tokstr('', 'O', 0, 0)" + \.":call Adjust_cursor()") +call s:Def_map('n', 's', 'TxtfmtInsertTok_s', + \":call Insert_tokstr('', 's', 0, 0)" + \.":call Adjust_cursor()") +" Start in normal / End in normal +call s:Def_map('n', 'vi', 'TxtfmtInsertTok_vi', + \":call Insert_tokstr('', 'i', 0, 1)" + \.":call Adjust_cursor()") +call s:Def_map('n', 'vI', 'TxtfmtInsertTok_vI', + \":call Insert_tokstr('', 'I', 0, 1)" + \.":call Adjust_cursor()") +call s:Def_map('n', 'va', 'TxtfmtInsertTok_va', + \":call Insert_tokstr('', 'a', 0, 1)" + \.":call Adjust_cursor()") +call s:Def_map('n', 'vA', 'TxtfmtInsertTok_vA', + \":call Insert_tokstr('', 'A', 0, 1)" + \.":call Adjust_cursor()") +call s:Def_map('n', 'vo', 'TxtfmtInsertTok_vo', + \":call Insert_tokstr('', 'o', 0, 1)" + \.":call Adjust_cursor()") +call s:Def_map('n', 'vO', 'TxtfmtInsertTok_vO', + \":call Insert_tokstr('', 'O', 0, 1)" + \.":call Adjust_cursor()") +call s:Def_map('n', 'vs', 'TxtfmtInsertTok_vs', + \":call Insert_tokstr('', 's', 0, 1)" + \.":call Adjust_cursor()") +" >>> +" insert mode insert token mappings <<< +" NOTE: Default is to use something that wouldn't be typed as text for the +" insert mode map. User may wish to remap this one to a Function key or +" something else entirely. I find very easy to type... +call s:Def_map('i', '', 'TxtfmtInsertTok_i', + \"=Insert_tokstr('', 'i', 0, 0)" + \."=Adjust_cursor()") +" >>> +" normal mode get token info mapping <<< +call s:Def_map('n', 'ga', 'TxtfmtGetTokInfo', + \":echo GetTokInfo()") +" >>> +" NOTES <<< +" -enterinsert default is 'i' +" -mode default is 'ni' +" - can't be used in insert-mode mapping for some reason... +" >>> +" TODO <<< +" -Convert ASCII only pattern character classes to ones that will work with +" multi-byte chars +" -Add commands/functions for detecting and altering the range of character +" codes used for txtfmt tokens. +" -Use syntax clusters instead of the double definition trickery I used when I +" didn't know about syntax clusters. +" >>> +" >>> +" Restore compatibility options <<< +" Restore compatibility options to what they were +let &cpo = s:save_cpo +" >>> + " vim: sw=4 ts=4 foldmethod=marker foldmarker=<<<,>>> : diff --git a/txtfmt/plugin/txtfmt.vim b/txtfmt/plugin/txtfmt.vim new file mode 100755 index 0000000..332ad9a --- /dev/null +++ b/txtfmt/plugin/txtfmt.vim @@ -0,0 +1,3104 @@ +" Txtfmt: Set of Vim plugins (syntax, ftplugin, plugin) for creating and +" displaying formatted text with Vim. +" File: This is the global plugin file, which contains configuration code +" needed by both the ftplugin and the syntax files. +" Creation: 2004 Nov 06 +" Last Change: 2010 Sep 18 +" Maintainer: Brett Pershing Stahlman +" License: This file is placed in the public domain. + +" Note: The following line is required by a packaging script +let g:Txtfmt_Version = "2.4" + +" Autocommands needed by refresh mechanism <<< +au FileType * call s:Txtfmt_save_filetype() +au Syntax * call s:Txtfmt_save_syntax() +fu! s:Txtfmt_save_filetype() + let l:filetype = expand("") + if l:filetype =~ '\%(^\|\.\)txtfmt\%(\.\|$\)' + let b:txtfmt_filetype = l:filetype + endif +endfu +fu! s:Txtfmt_save_syntax() + let l:syntax = expand("") + if l:syntax =~ '\%(^\|\.\)txtfmt\%(\.\|$\)' + let b:txtfmt_syntax = l:syntax + endif +endfu +" >>> +" Constants needed regardless of whether common config is being performed <<< +" The following is passed to s:New_window() to construct the test page +let s:TESTPAGE_WINHEIGHT = 20 +" >>> +" Functions needed regardless of whether common config is being performed <<< +" Function: s:Is_txtfmt_modeline() <<< +" Purpose: Return nonzero if and only if the line whose text is input "looks +" like" a txtfmt modeline. +" Note: Option names and values are NOT validated. That function is performed +" by s:Process_txtfmt_modeline. +" Inputs: +" linestr -A line of text that may or may not represent a txtfmt modeline. +" Note: Format of the modeline is as follows: +" (Based upon 1st Vim modeline format) +" [text]{white}txtfmt:[white]{options} +" {options} can be the following: +" {tokrange|rng}=|[sSlL] +" {escape|esc}={self|bslash|none} +" {sync}={||fromstart|none} +" {bgcolor|bg}= +" {nested|nst} +" {concealcursor|cocu}=[n][v][i][c] +" Must be no whitespace surrounding the '=' +" Also: Since Vim will not choke on a trailing ':' (though it's not +" technically part of the 1st modeline format), neither will I. +" Define regexes used to process modeline +" Also: ' txtfmt:' with no options is not an error, but will not be recognized +" as a modeline. Rationale: If user doesn't define any options in a modeline, +" assume it's just text that looks like the start of a modeline. +let s:re_ml_start = '\%(.\{-}\%(\s\+txtfmt:\s*\)\)' +let s:re_ml_name = '\%(\I\i*\)' +let s:re_ml_val = '\%(\%(\\.\|[^:[:space:]]\)*\)' +let s:re_ml_el = '\%('.s:re_ml_name.'\%(='.s:re_ml_val.'\)\?\)' +let s:re_ml_elsep = '\%(\s\+\|\s*:\s*\)' +let s:re_ml_end = '\%(\s*:\?\s*$\)' +" Construct pattern matching entire line, which will capture all leading text +" in \1, all options in \2, and all trailing text in \3 +let s:re_modeline = '\('.s:re_ml_start.'\)\(' + \.s:re_ml_el.'\%('.s:re_ml_elsep.s:re_ml_el.'\)*\)' + \.'\('.s:re_ml_end.'\)' +fu! s:Is_txtfmt_modeline(linestr) + return a:linestr =~ s:re_modeline +endfu +" >>> +" Function: s:New_window <<< +" Description: Attempt to create display window of at least the +" specified size for things like test page and tutorial. If possible, +" don't change the height of existing windows (other than the current +" one). Note that this is easy to accomplish, even when 'ea' is set, +" because the `:N new' form effectively overrides 'ea'. If the current +" window is small, we may not be able to avoid shrinking the height of +" existing windows; fortunately, when the `:N new' form is used, Vim +" shrinks as few windows as possible, starting with the current one. +" Note On Size Calculation: Making a *precise" determination as to +" whether we could make the new window higher than requested height by +" halfing the current window would require special logic involving the +" 'laststatus' option. Fortunately, it is not necessary to make a +" precise determination. We can simply assume worst-case: i.e., the +" original winheight() will be reduced by 2 statuslines when the new +" window is created. This corresponds to the case in which 'laststatus' +" == 1, and there's only 1 window before the new one is created. +" Rationale: Using this simplification may result in our sometimes +" creating a window with exactly the requested height, when we might +" have made it a line or so higher, but this is not a problem, and the +" extra logic required to 'fix' it is not justified. +" Important Note: We need to check for error generated by the :new +" command. In particular, E36 will be generated if there's not enough +" space to create the new window. Note that the rules Vim uses to +" determine whether space is sufficient depend upon current setting of +" 'ea'). +" Return: Nonzero on success, zero on failure +" Error: Set s:err_str and return zero +fu! s:New_window(height) + let avail_height = winheight(0) - 2 + " Determine size of window to create + if avail_height > 2 * a:height + " Make test page half current window height (which should be >= + " requested height) + let create_height = avail_height / 2 + else + " Make test page exactly the requested height + let create_height = a:height + endif + " Create the window + let v:errmsg = '' + exe 'silent! ' . create_height . 'new' + " Check for E36 + if v:errmsg != '' + " Make error string available to caller and return false + let s:err_str = v:errmsg + return 0 + else + " return true for Success + return 1 + endif +endfu +" >>> +" >>> +" IMPORTANT NOTE: The common configuration code in this file is intended to be +" executed only upon request by either the txtfmt ftplugin or syntax file. +" Since this file resides in the Vim plugin directory, it will be sourced by +" Vim automatically whenever Vim is started; the common configuration code, +" however, will not be executed at Vim startup because the special txtfmt +" variable b:txtfmt_do_common_config will not be set at that time. When either +" the ftplugin or syntax file wishes to execute the code in this script, it +" sets b:txtfmt_do_common_config and uses :runtime to source this file. When +" the common configuration code within this file executes, it makes its output +" available to both ftplugin and syntax files via buffer-local variables. + +if exists('b:txtfmt_do_common_config') +" Needed for both ftplugin and syntax +" Command: Refresh <<< +com! -buffer Refresh call s:Txtfmt_refresh() +" >>> +" Autocommands <<< +" Ensure that common configuration will be redone whenever the txtfmt buffer +" is re-read (e.g. after a txtfmt-modeline has been changed). +" Note: This autocmd allows user simply to do ":e" at the Vim command line to +" resource everything, including the option processing in common config. +augroup TxtfmtCommonConfig + au BufReadPre :unlet! b:txtfmt_did_common_config +augroup END +" >>> +" Common constant definitions <<< + +" Define first Vim version that supported undercurl +let b:txtfmt_const_vimver_undercurl = 700 +" Define several sets of constants needed by the functions used to process +" 'tokrange' option. +" Define tokrange defaults (decomposed into 'starttok' and 'formats' values) +" as a function of encoding class. +" Note: starttok encoded as string to preserve dec or hex display preference +let b:txtfmt_const_starttok_def_{'1'} = '180' +let b:txtfmt_const_formats_def_{'1'} = 'X' +let b:txtfmt_const_starttok_def_{'2'} = '180' +let b:txtfmt_const_formats_def_{'2'} = 'X' +let b:txtfmt_const_starttok_def_{'u'} = '0xE000' +let b:txtfmt_const_formats_def_{'u'} = 'X' +" Define the number of tokens in the txtfmt token range as a function of +" the format variant flags (which determine num_attributes in the equations +" below): +" --no background colors-- +" N = 2 ^ {num_attributes} + 9 +" --background colors-- +" N = 2 ^ {num_attributes} + 9 + 9 +" Important Note: The numbers are not dependent upon the number of active fg +" and bg colors, since we always reserve 8 colors. + +" The 3 parameters in the following constants represent the following 3 format +" variant flags: +" txtfmt_cfg_bgcolor +" txtfmt_cfg_longformats +" txtfmt_cfg_undercurl +" Note: Value supplied for longformats and undercurl indices must take 'pack' +" option into account. +" TODO: Fix the final 3 elements or get rid of this array altogether. Note +" that it doesn't take 'pack' option into account +let b:txtfmt_const_tokrange_size_{0}{0}{0} = 17 +let b:txtfmt_const_tokrange_size_{0}{1}{0} = 41 +let b:txtfmt_const_tokrange_size_{0}{1}{1} = 73 +let b:txtfmt_const_tokrange_size_{1}{0}{0} = 26 +let b:txtfmt_const_tokrange_size_{1}{1}{0} = 82 +let b:txtfmt_const_tokrange_size_{1}{1}{1} = 82 + +" Make sure we can easily deduce the suffix from the format variant flags +let b:txtfmt_const_tokrange_suffix_{0}{0}{0} = 'S' +let b:txtfmt_const_tokrange_suffix_{0}{1}{0} = 'L' +let b:txtfmt_const_tokrange_suffix_{0}{1}{1} = 'L' +let b:txtfmt_const_tokrange_suffix_{1}{0}{0} = 'X' +let b:txtfmt_const_tokrange_suffix_{1}{1}{0} = 'XL' +let b:txtfmt_const_tokrange_suffix_{1}{1}{1} = 'XL' + +" Define the maximum character code that may be used as a txtfmt token as a +" function of encoding class. (Encoding class is specified as '1' for single +" byte, '2' for 16-bit and 'u' for unicode.) +" Note: Vim doesn't support unicode chars larger than 16-bit. +let b:txtfmt_const_tokrange_limit_{'1'} = 0xFF +let b:txtfmt_const_tokrange_limit_{'2'} = 0xFFFF +let b:txtfmt_const_tokrange_limit_{'u'} = 0xFFFF +" The following regex describes a {number} that may be used to set a numeric +" option. The txtfmt help specifies that only decimal or hexadecimal formats +" are permitted. +let b:txtfmt_re_number_atom = '\([1-9]\d*\|0x\x\+\)' + +" >>> +" General utility functions <<< +" Function: s:Repeat() <<< +" Purpose: Generate a string consisting of the input string repeated the +" requested number of times. +" Note: Vim7 added a repeat() function, but I'm intentionally not using +" anything added post-Vim6. +" Input: +" str - string to repeat +" cnt - number of times to repeat it +" Return: The generated string +fu! s:Repeat(str, cnt) + let ret_str = '' + let i = 0 + while i < a:cnt + let ret_str = ret_str . a:str + let i = i + 1 + endwhile + return ret_str +endfu +" >>> +" >>> +" Parse_<...> functions <<< +" Function: s:Parse_init() +" Purpose: +" Input: +" text - string to be parsed +" re_tok - regex matching a token +" Return: ID that must be passed to the other parse functions (-1 indicates +" error) +" Simulated struct: +" text +" len +" re +" pos +fu! s:Parse_init(text, re_tok) + let i = 0 + let MAX_PARSE_INSTANCES = 1000 + while i < MAX_PARSE_INSTANCES + if !exists('s:parsedata_'.i.'_text') + " Found a free one + let s:parsedata_{i}_text = a:text + let s:parsedata_{i}_len = strlen(a:text) " for speed + let s:parsedata_{i}_re = a:re_tok + let s:parsedata_{i}_pos = 0 + return i + endif + let i = i + 1 + endwhile + if i >= MAX_PARSE_INSTANCES + echoerr "Internal Parse_init error - contact developer" + return -1 + endif +endfu +" Function: s:Parse_nexttok() +" Purpose: +" Input: +" Return: The next token as a string (or empty string if no more tokens) +" Error: Not possible when used correctly, and since this is an internally +" used function, we will not check for error. +fu! s:Parse_nexttok(parse_id) + " Note: Structures used and generated internally in controlled manner, so + " assume parse_id points to valid struct + let text = s:parsedata_{a:parse_id}_text + let re = s:parsedata_{a:parse_id}_re + let pos = s:parsedata_{a:parse_id}_pos + let len = s:parsedata_{a:parse_id}_len + let parse_complete = 0 " set if text exhausted + " Did last call exhaust text? + if pos >= len + let parse_complete = 1 + let ret_str = '' + else + " text not exhausted yet - get past any whitespace + " ^\s* will return pos if no whitespace at pos (cannot return -1) + let pos = matchend(text, '^\s*', pos) + " Did we move past trailing whitespace? + if pos >= len + let parse_complete = 1 + let ret_str = '' + else + " We're sitting on first char to be returned. + " re determines how many more will be part of return str + " Note: Force re to match at current pos if at all + let pos2 = matchend(text, '^'.re, pos) + if pos2 < 0 + " curr char is not part of a token, so just return it by itself + let ret_str = text[pos] + let pos = pos + 1 + else + " Return the token whose end was located + let ret_str = strpart(text, pos, pos2-pos) + let pos = pos2 + endif + endif + endif + " Only way out of this function + if parse_complete + call s:Parse_free(a:parse_id) + else + " Update pos in structure for next call... + let s:parsedata_{a:parse_id}_pos = pos + endif + return ret_str +endfu +" Function: s:Parse_free() +" Purpose: Free the data structures for a particular parse instance (denoted +" by input id) +" Input: parse_id - parse instance whose data is to be freed +" Return: none +" Error: not possible +fu! s:Parse_free(parse_id) + " Using unlet! ensures that error not possible + unlet! s:parsedata_{a:parse_id}_text + unlet! s:parsedata_{a:parse_id}_re + unlet! s:parsedata_{a:parse_id}_pos + unlet! s:parsedata_{a:parse_id}_len + +endfu +" >>> +" Configuration utility functions (common) <<< +" >>> +" encoding utility functions (common) <<< +let s:re_encs_1 = '^\%(' + \.'latin1\|iso-8859-n\|koi8-r\|koi8-u' + \.'\|macroman\|cp437\|cp737\|cp775' + \.'\|cp850\|cp852\|cp855\|cp857' + \.'\|cp860\|cp861\|cp862\|cp863' + \.'\|cp865\|cp866\|cp869\|cp874' + \.'\|cp1250\|cp1251\|cp1253\|cp1254' + \.'\|cp1255\|cp1256\|cp1257\|cp1258' + \.'\)$' +let s:re_encs_2 = '^\%(' + \.'cp932\|euc-jp\|sjis\|cp949' + \.'\|euc-kr\|cp936\|euc-cn\|cp950' + \.'\|big5\|euc-tw' + \.'\)' +let s:re_encs_u = '^\%(' + \.'utf-8\|ucs-2\|ucs-2le\|utf-16\|utf-16le\|ucs-4\|ucs-4le' + \.'\)' +" Function: TxtfmtCommon_Encoding_get_class() <<< +" Purpose: Return single character indicating whether the input encoding name +" represents a 1-byte encoding ('1'), a 2-byte encoding ('2'), or a unicode +" encoding ('u'). If input encoding name is unrecognized, return empty string. +fu! TxtfmtCommon_Encoding_get_class(enc) + if a:enc =~ s:re_encs_1 + return '1' + elseif a:enc =~ s:re_encs_2 + return '2' + elseif a:enc =~ s:re_encs_u + return 'u' + else + return '' + endif +endfu +" >>> +" >>> +" 'tokrange' utility functions <<< +" Construct pattern that will capture char code in \1 and optional size +" specification (sSlLxX) in \2. +if exists('g:txtfmtAllowxl') && g:txtfmtAllowxl + " Note: By default, XL suffix is illegal, but user has overridden the + " default + let s:re_tokrange_spec = '^\([1-9]\d*\|0x\x\+\)\(\%([sSlL]\|[xX][lL]\?\)\?\)$' +else + let s:re_tokrange_spec = '^\([1-9]\d*\|0x\x\+\)\([sSlLxX]\?\)$' +endif + +" Function: s:Tokrange_is_valid() <<< +" Purpose: Indicate whether input string is a valid tokrange spec. +fu! s:Tokrange_is_valid(spec) + return a:spec =~ s:re_tokrange_spec +endfu +" >>> +" Function: s:Tokrange_get_formats() <<< +" Purpose: Return 'formats' component of the input tokrange spec. +" Note: If optional 'formats' component is omitted, empty string will be +" returned. +" Note: formats spec will be returned in canonical form (uppercase). +" Assumption: Input string has already been validated by s:Tokrange_is_valid. +fu! s:Tokrange_get_formats(spec) + return substitute(a:spec, s:re_tokrange_spec, '\U\2', '') +endfu +" >>> +" Function: s:Tokrange_translate_tokrange() <<< +" Description: Decompose the input tokrange into its constituent parts, +" setting all applicable option variables: +" b:txtfmt_cfg_starttok +" b:txtfmt_cfg_bgcolor +" b:txtfmt_cfg_longformats +" b:txtfmt_cfg_undercurl +" b:txtfmt_cfg_starttok_display +" b:txtfmt_cfg_formats_display +fu! s:Tokrange_translate_tokrange(tokrange) + " Extract starttok and formats from input tokrange + let starttok_str = substitute(a:tokrange, s:re_tokrange_spec, '\1', '') + let formats_str = substitute(a:tokrange, s:re_tokrange_spec, '\2', '') + + " Decompose starttok into a numeric and a display portion + let b:txtfmt_cfg_starttok = 0 + starttok_str + let b:txtfmt_cfg_starttok_display = starttok_str + + " Decompose formats into constituent flags and a display portion + " Note: Formats is a bit special. For one thing, it can be omitted from a + " tokrange spec, in which case we'll choose a default. Also, long formats + " ('L') can mean either 'all' or 'all_but_undercurl', depending upon Vim + " version and b:txtfmt_cfg_undercurlpref. (Undercurl was not supported + " until Vim version 7.0, and we allow user to disable it with the + " 'undercurl' option even when it is supported.) + " Note: b:txtfmt_cfg_undercurl is always set explicitly to 0 when it + " doesn't apply, so that it can be used in parameterized variable names. + if strlen(formats_str) == 0 + " Format suffix was omitted. Default to 'extended' formats (background + " colors with short formats) + let b:txtfmt_cfg_bgcolor = 1 + let b:txtfmt_cfg_longformats = 0 + let b:txtfmt_cfg_undercurl = 0 + let b:txtfmt_cfg_formats_display = 'X' + elseif formats_str ==? 'L' + " Long formats with no background colors + let b:txtfmt_cfg_bgcolor = 0 + let b:txtfmt_cfg_longformats = 1 + if v:version >= b:txtfmt_const_vimver_undercurl && b:txtfmt_cfg_undercurlpref + let b:txtfmt_cfg_undercurl = 1 + else + let b:txtfmt_cfg_undercurl = 0 + endif + let b:txtfmt_cfg_formats_display = 'L' + elseif formats_str ==? 'X' + " Background colors with short formats + let b:txtfmt_cfg_bgcolor = 1 + let b:txtfmt_cfg_longformats = 0 + let b:txtfmt_cfg_undercurl = 0 + let b:txtfmt_cfg_formats_display = 'X' + elseif formats_str ==? 'XL' + " Background colors with long formats + let b:txtfmt_cfg_bgcolor = 1 + let b:txtfmt_cfg_longformats = 1 + if v:version >= b:txtfmt_const_vimver_undercurl && b:txtfmt_cfg_undercurlpref + let b:txtfmt_cfg_undercurl = 1 + else + let b:txtfmt_cfg_undercurl = 0 + endif + " Note: This is no longer legal!!!! + let b:txtfmt_cfg_formats_display = 'XL' + else + " Short formats + let b:txtfmt_cfg_bgcolor = 0 + let b:txtfmt_cfg_longformats = 0 + let b:txtfmt_cfg_undercurl = 0 + let b:txtfmt_cfg_formats_display = 'S' + endif +endfu +" >>> +" Function: s:Tokrange_size() <<< +" Note: Now that I'm reserving 8 colors even when numfgcolors and numbgcolors +" are less than 8, this function can probably be removed, or at least renamed +" (e.g., Tokrange_used_size). +fu! s:Tokrange_size(formats) + return b:txtfmt_const_tokrange_size_{a:formats} +endfu +" >>> +" >>> +" 'sync' utility functions <<< +" Construct pattern that will validate the option value. +let s:re_sync_number_spec = '^\([1-9]\d*\|0x\x\+\)$' +let s:re_sync_name_spec = '^fromstart\|none$' + +" Function: s:Sync_is_valid() <<< +" Purpose: Indicate whether input string is a valid sync option value +fu! s:Sync_is_valid(spec) + return a:spec =~ s:re_sync_number_spec || a:spec =~ s:re_sync_name_spec +endfu +" >>> +" Function: s:Sync_get_method() <<< +" Purpose: Determine the syncmethod represented by the input sync value and +" return its name. Possible values: 'minlines', 'fromstart', 'none' +" Assumption: Input string has already been validated by s:Sync_is_valid. +fu! s:Sync_get_method(spec) + if a:spec =~ s:re_sync_name_spec + return a:spec + else + return 'minlines' + endif +endfu +" >>> +" >>> +" 'escape' utility functions <<< +" Construct pattern that will validate the option value. +let s:re_escape_optval = '^\%(none\|bslash\|self\)$' +" Function: s:Escape_is_valid() <<< +" Purpose: Indicate whether input string is a valid escape option value +fu! s:Escape_is_valid(optval) + return a:optval =~ s:re_escape_optval +endfu +" >>> +" >>> +" 'concealcursor' utility functions <<< +" Construct pattern that will validate the option value. +let s:re_concealcursor_optval = '^[nvic]*$' +" Function: s:Concealcursor_is_valid() <<< +" Purpose: Indicate whether input string is a valid concealcursor option value +fu! s:Concealcursor_is_valid(optval) + return a:optval =~ s:re_concealcursor_optval +endfu +" >>> +" >>> +" Number validation utility functions <<< +" Function: s:Number_is_valid(s) +" Purpose: Indicate whether the input string represents a valid number +fu! s:Number_is_valid(s) + return a:s =~ '^\s*'.b:txtfmt_re_number_atom.'\s*$' +endfu +" >>> +" Num fg/bg colors validation utility function <<< +let s:re_num_clrs = '^\%(0x\)\?[0-8]$' +fu! s:Numclrs_is_valid(s) + return a:s =~ s:re_num_clrs +endfu +" >>> +" fg/bg color mask validation utility function <<< +let s:re_clr_mask = '^[01]\{8}$' +fu! s:Clrmask_is_valid(s) + return a:s =~ s:re_clr_mask +endfu +" >>> +" Function: s:Set_tokrange() <<< +" Purpose: Set b:txtfmt_cfg_starttok, b:txtfmt_cfg_bgcolor, +" b:txtfmt_cfg_longformats and b:txtfmt_cfg_undercurl options, taking into +" account any user-setting of 'tokrange' option, and if necessary, the txtfmt +" defaults, which may take 'encoding' into consideration. +" Note: If set via modeline, tokrange option value must be a literal tokrange +" specification; however, buf-local and global option variables may be set to +" either a valid tokrange spec, or a Vim expression that evaluates to one. +" Permitting arbitrary Vim expressions facilitates the use of complex tokrange +" selection logic, implemented by a user-defined expression or function. +" Examples: +" 'g:My_tokrange_calculator()' +" '&enc == "utf-8" ? "1400l" : "130s"' +fu! s:Set_tokrange() + " Undef variables that are outputs of this function. Note that these + " variables are the decomposition of b:txtfmt_cfg_tokrange, which may or + " may not be set at this point. + unlet! b:txtfmt_cfg_starttok + \ b:txtfmt_cfg_bgcolor b:txtfmt_cfg_longformats b:txtfmt_cfg_undercurl + \ b:txtfmt_cfg_starttok_display b:txtfmt_cfg_formats_display + " Cache the 'encoding' in effect + let enc = &encoding + " Determine the corresponding encoding class + let enc_class = TxtfmtCommon_Encoding_get_class(enc) + if !exists('b:txtfmt_cfg_tokrange') || strlen(b:txtfmt_cfg_tokrange) == 0 + " Either option wasn't set within modeline, or it was set to invalid + " value. + if exists('b:txtfmt_cfg_tokrange') && strlen(b:txtfmt_cfg_tokrange) == 0 + " Bad modeline set + let l:warnmsg = + \"Warning: Ignoring invalid modeline value for txtfmt `tokrange' option" + elseif exists('b:txtfmtTokrange') + " User overrode buf-local option. Save the option for validation + " below... + let b:txtfmt_cfg_tokrange = b:txtfmtTokrange + let l:set_by = 'b' + elseif exists('g:txtfmtTokrange') + " User overrode global option. Save the option for validation + " below... + let b:txtfmt_cfg_tokrange = g:txtfmtTokrange + let l:set_by = 'g' + endif + endif + if exists('l:set_by') && (l:set_by == 'b' || l:set_by == 'g') + " Perform special validation for buf-local/global settings, which + " permits either a tokrange spec or a Vim expression that evaluates to + " one. + if !s:Tokrange_is_valid(b:txtfmt_cfg_tokrange) + " Not a valid tokrange literal. Let's see whether it evaluates to + " one. + let v:errmsg = '' + " Evaluate expression, using silent! to prevent problems in the + " event that rhs is invalid. + silent! exe 'let l:tokrange = '.b:txtfmt_cfg_tokrange + if v:errmsg != '' + " Bad expression + let l:warnmsg = + \"Warning: Ignoring invalid ".(l:set_by == 'b' ? 'buf-local' : 'global') + \." value for txtfmt 'tokrange' option: ".b:txtfmt_cfg_tokrange + " Discard the invalid setting + let b:txtfmt_cfg_tokrange = '' + else + " It was a valid Vim expression. Did it produce a valid + " tokrange spec? + if !s:Tokrange_is_valid(l:tokrange) + let l:warnmsg = + \"Ignoring ".(l:set_by == 'b' ? 'buf-local' : 'global') + \." set of txtfmt `tokrange' option: `".b:txtfmt_cfg_tokrange + \."' produced invalid option value: ".l:tokrange + " Discard the invalid setting + let b:txtfmt_cfg_tokrange = '' + else + " Save the valid setting + let b:txtfmt_cfg_tokrange = l:tokrange + endif + endif + endif + endif + " Warn user if invalid user-setting is about to be overridden + if exists('l:warnmsg') + echoerr l:warnmsg + endif + " Decompose any valid user setting stored in b:txtfmt_cfg_tokrange. + + " Note: The output from the preceding stage is b:txtfmt_cfg_tokrange, + " which we must now decompose into b:txtfmt_cfg_starttok, + " b:txtfmt_cfg_bgcolor, b:txtfmt_cfg_longformats, b:txtfmt_cfg_undercurl, + " b:txtfmt_cfg_starttok_display and b:txtfmt_cfg_formats_display. If + " b:txtfmt_cfg_tokrange is nonexistent or null, there is no valid user + " setting, in which case, we'll supply default. + if exists('b:txtfmt_cfg_tokrange') && strlen(b:txtfmt_cfg_tokrange) + " Decompose valid tokrange setting via s:Tokrange_translate_tokrange, + " which sets all constituent variables. + call s:Tokrange_translate_tokrange(b:txtfmt_cfg_tokrange) + " Perform upper-bound validation + if b:txtfmt_cfg_starttok + + \ b:txtfmt_const_tokrange_size_{b:txtfmt_cfg_bgcolor}{b:txtfmt_cfg_longformats}{b:txtfmt_cfg_undercurl} + \ - 1 + \ > b:txtfmt_const_tokrange_limit_{enc_class} + " Warn user and use default + echoerr + \ "Warning: Tokrange value '".b:txtfmt_cfg_tokrange."' causes upper" + \." bound to be exceeded for encoding ".&enc + " Make sure we set to default below + " Note: It suffices to unlet b:txtfmt_cfg_starttok, since its + " nonexistence will ensure that all constituent vars are set below + unlet! b:txtfmt_cfg_starttok + endif + endif + " If b:txtfmt_cfg_starttok is still undefined, see whether there's an + " encoding-specific default. + " Note: b:txtfmt_cfg_starttok was unlet at the top of this function, so it + " will be undefined unless it's been set successfully. + if !exists('b:txtfmt_cfg_starttok') + " TODO - Put any logic that depends upon specific encoding here... + " . + " . + + endif + " If b:txtfmt_cfg_starttok is still undefined, see whether there's an + " encoding-class-specific default. + if !exists('b:txtfmt_cfg_starttok') + " If encoding class is unrecognized, default to '1' + if enc_class == '' + let use_enc_class = '1' + else + let use_enc_class = enc_class + endif + " Pass default tokrange to function that will decompose it and set all + " constituent variables. + call s:Tokrange_translate_tokrange( + \ b:txtfmt_const_starttok_def_{use_enc_class} + \ . b:txtfmt_const_formats_def_{use_enc_class} + \) + endif + " Note: If b:txtfmt_cfg_tokrange exists, we are done with it now that it + " has been completely decomposed + unlet! b:txtfmt_cfg_tokrange + " Save the encoding class for later use (will be needed by Define_syntax + " logic used to determine whether syn match offsets are byte or + " char-based) + let b:txtfmt_cfg_enc_class = enc_class + " Also save encoding itself. If we're using a cached copy of encoding + " class, we should be able to verify that the encoding upon which it is + " based is the currently active one. + let b:txtfmt_cfg_enc = enc +endfu +" >>> +" Function: s:Set_syncing() <<< +" Purpose: Set b:txtfmt_cfg_syncmethod and (if applicable) b:txtfmt_cfg_synclines +" options, according to the following logic: +" 1) If user set sync option via modeline, buffer-local option, or global +" option, attempt to use the setting with the highest priority. +" 2) If step 1 fails to set the option, either because of error or because the +" user made no attempt to set, default to minlines=250 +" Note: From a user perspective, there is only the 'sync' option. For +" convenience within the plugin, we break this single option into two options: +" 'syncmethod' and 'synclines'. Currently, 'synclines' is used only when +" syncmethod=minlines. +fu! s:Set_syncing() + if !exists('b:txtfmt_cfg_sync') || strlen(b:txtfmt_cfg_sync) == 0 + " Either option wasn't set within modeline, or it was set to invalid + " value. + if exists('b:txtfmt_cfg_sync') && strlen(b:txtfmt_cfg_sync) == 0 + " Bad modeline set + let l:bad_set_by = 'm' + elseif exists('b:txtfmtSync') + " User overrode buf-local option + if s:Sync_is_valid(b:txtfmtSync) + let b:txtfmt_cfg_sync = b:txtfmtSync + else + let l:bad_set_by = 'b' + endif + elseif exists('g:txtfmtSync') + " User overrode global option + if s:Sync_is_valid(g:txtfmtSync) + let b:txtfmt_cfg_sync = g:txtfmtSync + else + let l:bad_set_by = 'g' + endif + endif + endif + " Warn user if invalid user-setting is about to be overridden + if exists('l:bad_set_by') + " Note: Display the offending option value for buf-local or global + " option, but not for modeline, since modeline processing has already + " reported the error. + echoerr "Warning: Ignoring invalid ".( + \ l:bad_set_by == 'm' ? "modeline" : + \ l:bad_set_by == 'b' ? "buf-local" : + \ "global") . " value for txtfmt `sync' option" . ( + \ l:bad_set_by == 'm' ? '' : + \ l:bad_set_by == 'b' ? (': ' . b:txtfmtSync) : + \ (': ' . g:txtfmtSync)) + endif + if !exists('b:txtfmt_cfg_sync') || strlen(b:txtfmt_cfg_sync) == 0 + " Set to default + let b:txtfmt_cfg_syncmethod = 'minlines' + let b:txtfmt_cfg_synclines = 250 + else + " Decompose validated 'sync' option into 'syncmethod' and (if + " applicable) 'synclines' + let b:txtfmt_cfg_syncmethod = s:Sync_get_method(b:txtfmt_cfg_sync) + if b:txtfmt_cfg_syncmethod == 'minlines' + " Save the number of lines + let b:txtfmt_cfg_synclines = (0 + b:txtfmt_cfg_sync) + endif + endif + " We're done with b:txtfmt_cfg_sync now that it has been completely + " decomposed. + unlet! b:txtfmt_cfg_sync +endfu +" >>> +" Function: s:Translate_color_optstr() <<< +" Purpose: Process the string representing a single element from the array +" txtfmtColor{1..8}, and return the extracted information. +" Return: A comma-separated string containing the extracted information as +" follows: +" ,, +" Note that ctermfg_rhs and/or guifg_rhs may be blank, in the event that user +" specified term patterns and none of them matched. (If no term patterns are +" specified, there is an implied match with current &term value.) +" Note that return string will have commas and backslashes escaped. +" Note that the last color def that matches for each of guifg and ctermfg is +" the one that is returned to caller. +" Error: Set s:err_str and return empty string +" Details: +" Here is the format of a single string in the txtfmtColor{} array: +" ,[,,...,] +" := +" []: +" := +" :::...: +" *** Parse table *** +" st next_st can_end? tok +" 0 1 n +" 1 2 n , +" 2 3 n +" 3 4 n : +" 4 5 n str +" 5 4 y : +" 5 2 y , +" Example color optstr: +" red,c:xterm:dosterm:DarkRed,g:builtin_gui:other_gui:#FF0000 +" +" Here are the meanings of the fields: +" -regex used to recognize the token used to specify a +" certain color; e.g., when prompted by one of the insert token maps. May +" not contain spaces or commas. +" Example: k\|b\%[lack] +" -specifies whether clrstr will be the rhs of a +" "ctermfg=" ('c'), or "guifg=" ('g') +" -pattern used to match against &term option. It is a regex pattern +" which will be applied as ^$ +" -rhs of a "ctermfg=" or "guifg=" assignment. +" :help gui-colors | help cterm-colors +" Additional Note: +" Commas, colons, and backslashes appearing in fields must be +" backslash-escaped. +" Note: Due to the extra backslash escaping, it is recommended to use a +" string-literal, rather than double-quoted string. +fu! s:Translate_color_optstr(optstr) + " optstr is the string to be parsed + " Initialize the state machine + let pst = 0 + " Initialize the parse engine to consider tokens to be broken only at + " unescaped commas and colons. + let pid = s:Parse_init(a:optstr, '\%(\\.\|[^,:]\)\+') + if pid < 0 + let s:err_str = 'Internal error within s:Translate_color_optstr(). Contact developer.' + echomsg 'Internal error' + return '' + endif + " Extract and handle tokens in a loop + let parse_complete = 0 + " The following 2 will be set in loop (hopefully at least 1 anyways) + let ctermfg_rhs = '' + let guifg_rhs = '' + while !parse_complete + let tok = s:Parse_nexttok(pid) + " Note: Could handle end of string here for states in which end of + " string is illegal - but that would make it more difficult to + " formulate meaningful error messages. + "if tok == '' && pst != 5 + "endif + " Switch on the current state + if pst == 0 " Extract non empty namepat + let namepat = substitute(tok, '\\\(.\)', '\1', 'g') + if namepat =~ '^[[:space:]]*$' + let s:err_str = "Color def string must contain at least 1 non-whitespace char" + return '' + endif + let pst = 1 + elseif pst == 1 + if tok == ',' + let pst = 2 + elseif tok == '' + let s:err_str = "Expected comma, encountered end of color def string" + return '' + else + let s:err_str = "Expected comma, got '".tok."'" + return '' + endif + elseif pst == 2 + if tok == 'c' || tok == 'g' + let pst = 3 + let c_or_g = tok + " Do some initializations for this cterm/gui + let tp_or_cs_cnt = 0 + let got_term_match = 0 + elseif tok == '' + let s:err_str = "Expected 'c' or 'g', encountered end of color def string" + return '' + else + let s:err_str = "Expected 'c' or 'g', got '".tok."'" + return '' + endif + elseif pst == 3 + if tok == ':' + let pst = 4 + elseif + let s:err_str = "Expected ':', encountered end of color def string" + return '' + else + let s:err_str = "Expected ':', got '".tok."'" + return '' + endif + elseif pst == 4 + let pst = 5 + " Do some processing with this and possibly previous termpat or + " clrstr token. Note that if previous one exists, it is termpat; + " we can't yet know what current one is. + let termpat_or_clrstr = substitute(tok, '\\\(.\)', '\1', 'g') + if termpat_or_clrstr =~ '^[[:space:]]*$' + let s:err_str = "Term patterns and color strings must contain at least one non-whitespace char" + return '' + endif + " If here, update the count. Note that we won't know whether this + " is termpat or clrstr until next time here. + let tp_or_cs_cnt = tp_or_cs_cnt + 1 + if !got_term_match && tp_or_cs_cnt > 1 + " Process saved string as termpat + " Pattern has implied ^ and $. Also, termpat may contain \c + if &term =~ '^'.tp_or_cs_str.'$' + " Found a match! + let got_term_match = 1 + endif + endif + " Save current token for processing next time + let tp_or_cs_str = termpat_or_clrstr + elseif pst == 5 + if tok == ':' " another termpat or clrstr + let pst = 4 + elseif tok == ',' " another cterm/gui + let pst = 2 + elseif tok == '' " end of string - legal in state 5 + let parse_complete = 1 + else " illegal token + let s:err_str = "Unexpected input in color def string: ".tok + return '' + endif + if tok == ',' || tok == '' + " Need to process saved data from pst 4, which we now know to + " be a clrstr. + if tp_or_cs_cnt == 1 || got_term_match + " Either no termpats were specified (implied match with + " &term) or one of the termpats matched. + " Note that prior ctermfg/guifg rhs strings may be + " overwritten here, if more than one cterm/gui def exists + " and has a match. + if c_or_g == 'c' + let ctermfg_rhs = tp_or_cs_str + else + let guifg_rhs = tp_or_cs_str + endif + endif + endif + endif + endwhile + " Construct the return string: + " ,, + let ret_str = escape(namepat, ',\').',' + \.escape(ctermfg_rhs, ',\').','.escape(guifg_rhs, ',\') + return ret_str +endfu +" >>> +" Function: s:Get_color_uniq_idx() <<< +" Purpose: Convert the rhs of b:txtfmt_clr and b:txtfmt_bgc (if applicable) to +" a single string. Look the string up in global array +" g:txtfmt_color_configs{}. If the color config already exists, return its +" index; otherwise, append the color config to a new element at the end of +" g:txtfmt_color_configs{}, and return the corresponding index. +" Note: The index returned will be used to ensure that the highlight groups +" used for this buffer are specific to the color configuration. +" Input: none +" Return: Uniqueness index corresponding to the color configuration stored in +" b:txtfmt_clr and b:txtfmt_bgc (if applicable). In the unlikely event that no +" colors are active in the current configuration (i.e., numfgcolors and +" numbgcolors both equal 0), we return an empty string. +fu! s:Get_color_uniq_idx() + " Build the string to be looked up + let s = '' + let fgbg_idx = 0 + let clr_or_bgc{0} = 'clr' + let clr_or_bgc{1} = 'bgc' + let fg_or_bg{0} = 'fg' + let fg_or_bg{1} = 'bg' + while fgbg_idx < (b:txtfmt_cfg_bgcolor ? 2 : 1) + " Loop over all used colors + " TODO_BG: Does it make sense to include unused ones? It shouldn't be + " necessary... + " Note: Index 1 corresponds to first non-default color + let i = 1 + while i <= b:txtfmt_cfg_num{fg_or_bg{fgbg_idx}}colors + let s = s.b:txtfmt_{clr_or_bgc{fgbg_idx}}{i}."\" + let i = i + 1 + endwhile + let fgbg_idx = fgbg_idx + 1 + endwhile + " In the unlikely event that string is still empty, config is such that no + " colors are in use, in which case, we return an empty string, since no + " uniqueness index applies. + if strlen(s) == 0 + return '' + endif + " Look up the newly-generated string in g:txtfmt_color_configs + if !exists('g:txtfmt_color_configs_len') + let g:txtfmt_color_configs_len = 0 + endif + let i = 0 + while i < g:txtfmt_color_configs_len + if g:txtfmt_color_configs{i} == s + " Color config exists already - return its index + return i + endif + let i = i + 1 + endwhile + " If here, color config doesn't exist yet, so append it + let g:txtfmt_color_configs{i} = s + let g:txtfmt_color_configs_len = g:txtfmt_color_configs_len + 1 + " Return index of appended element + return i +endfu +" >>> +" Function: s:Process_color_options() <<< +" Purpose: Process the special color definition arrays to determine the color +" arrays in effect for the current buffer. There are global and buffer-local +" versions of both txtfmtColor and txtfmtBgcolor, as well as a default in +" s:txtfmt_clr. From these, we construct the 8 element buf-local arrays, +" described under 'Return:' below. +" Algorithm: Each color index is considered independently of all others, with +" a buf-local definition taking precedence over a global one. A txtfmtBgcolor +" element is always preferred for background color, but txtfmtColor will be +" used for a background color when no background-color-specific element +" exists. If no other element can be found, defaults from s:txtfmt_clr will be +" used. +" Cterm and gui may be overridden separately, and color definition strings may +" even take into account the value of &term. Note that for each possible +" element in the array, there is a default element (in s:txtfmt_clr{1..8}), +" which will be used if user has not overriden. +" Return: indirect only +" Builds the following buffer-scope arrays: (indexed by 1-based color index) +" b:txtfmt_clr_namepat{}, b:txtfmt_clr{} +" b:txtfmt_bgc_namepat{}, b:txtfmt_bgc{} +" Note: bgc arrays will not be built if background colors are disabled. +" Details: The format of the color array is as follows: +" The array has 8 elements (1..8), each of which represents a color region +" begun with one of the 8 successive color tokens in the token range. Each +" element is a string whose format is described in header of +" s:Translate_color_optstr() +" Rules: In outer loop over fg and bg cases, and inner loop over all color +" indices in range i = 1..8, check all applicable variants of the txtfmtColor +" array (as defined in arrname{}) for an element corresponding to the current +" color index. Set b:txtfmt_clr{} and move to next color index as soon as a +" suitable definition is found. Suitability is determined by checking +" has('gui_running') against the 'g:' or 'c:' in the color definition string, +" and if necessary, by matching the current value of 'term' against a 'term' +" pattern in the color definition string. +" Note: This function was rewritten on 10May2008, and then again on 31Jan2009. +" A corresponding partial rewrite of s:Translate_color_optstr is probably in +" order, but is not necessary, so I've left the latter function completely +" intact for now. (We don't really need both ctermfg and guifg values any +" more, but s:Translate_color_optstr still returns both...) +fu! s:Process_color_options() + let arrname{0} = 'b:txtfmtBgcolor' + let arrname{1} = 'g:txtfmtBgcolor' + let arrname{2} = 'b:txtfmtColor' + let arrname{3} = 'g:txtfmtColor' + let arrname{4} = 's:txtfmt_clr' + " TODO_BG: Decide whether s:txtfmt_bgc should be removed - if so, may get + " rid of arr_end{} + " Define strings used to build appropriate var names for both fg and bg + let clr_or_bgc{0} = 'clr' + let clr_or_bgc{1} = 'bgc' + " Frame the array for fg and bg + " Note: Could use a common value for end. + let arr_beg{0} = 2 + let arr_end{0} = 4 + let arr_beg{1} = 0 + let arr_end{1} = 4 + " Determine arrays that don't apply to fg color + let skip{0}_{0} = 1 + let skip{0}_{1} = 1 + let skip{0}_{2} = 0 + let skip{0}_{3} = 0 + let skip{0}_{4} = 0 + if b:txtfmt_cfg_bgcolor + " Determine arrays that don't apply to bg color + let skip{1}_{0} = 0 + let skip{1}_{1} = 0 + let skip{1}_{2} = 0 + let skip{1}_{3} = 0 + let skip{1}_{4} = 0 + endif + " Loop over fg and bg color (if applicable) + let fgbg_idx = 0 + while fgbg_idx < (b:txtfmt_cfg_bgcolor ? 2 : 1) + let i = 1 + " Loop over all colors (1st non-default color at index 1) + while i < b:txtfmt_num_colors + " Init strings to value signifying not specified or error + let namepat = '' | let clr_rhs = '' + " Loop over the possible color arrays + let j = arr_beg{fgbg_idx} + while j <= arr_end{fgbg_idx} + " Skip inapplicable arrays + if skip{fgbg_idx}_{j} + let j = j + 1 + continue + endif + " Skip nonexistent color definitions + if exists(arrname{j}.'{'.i.'}') + exe 'let l:el = '.arrname{j}.'{'.i.'}' + " If here, color definition exists. Let's see whether it contains + " a match... + let s = s:Translate_color_optstr(el) + if s != '' + " Extract fields from the escaped return string (which is + " internally generated, and hence, does not require + " validation) + " TODO - Perhaps standardize this in one spot (function or + " static return variables) + let re_fld = '\%(\%(\\.\|[^,]\)*\)' + let re_sfld = '\(\%(\\.\|[^,]\)*\)' + let namepat = substitute(s, re_sfld.'.*', '\1', '') + if has('gui_running') + let clr_rhs = substitute(s, re_fld.','.re_fld.','.re_sfld, '\1', '') + else + let clr_rhs = substitute(s, re_fld.','.re_sfld.'.*', '\1', '') + endif + " Note: clr_rhs may be null at this point; if so, there + " was no applicable color definition, though the color def + " element was valid + if strlen(clr_rhs) + " Remove extra level of backslashes + let namepat = substitute(namepat, '\\\(.\)', '\1', 'g') + let clr_rhs = substitute(clr_rhs, '\\\(.\)', '\1', 'g') + endif + elseif arrname{j}[0] == 'b' || arrname{j}[0] == 'g' + echomsg "Ignoring invalid user-specified color def ".arrname{j}.i." due to error: " + \.s:err_str + else + " Shouldn't get here! Problem with defaults... + echomsg "Internal error within Process_color_options - bad default for txtfmtColors" + \.i." - contact developer." + endif + " Are we done yet? + if strlen(clr_rhs) + break + endif + endif + let j = j + 1 + endwhile + " Assumption: Lack of color rhs at this point implies internal error. + " Build the buffer-specific array used in syntax file... + " Note: In the following 2 arrays, an index of 1 corresponds to the + " first non-default color. + let b:txtfmt_{clr_or_bgc{fgbg_idx}}_namepat{i} = namepat + let b:txtfmt_{clr_or_bgc{fgbg_idx}}{i} = clr_rhs + " Advance to next color + let i = i + 1 + endwhile + let fgbg_idx = fgbg_idx + 1 + endwhile + " Now that the color configuration is completely determined (and loaded + " into b:txtfmt_clr{} (and b:txtfmt_bgc{} if applicable)), determine the + " 'uniqueness index' for this color configuration. The uniqueness index is + " used to ensure that each color configuration has its own set of syntax + " groups. + " TODO_BG: Is the _cfg_ infix appropriate for such variables? For now, I'm + " using it only for option vars (with one exception that needs looking at) + let b:txtfmt_color_uniq_idx = s:Get_color_uniq_idx() +endfu +" >>> +" Function: s:Process_txtfmt_modeline() <<< +" Purpose: Determine whether input line is a valid txtfmt modeline. Process +" options if so. If required by s:txtfmt_ml_new_<...> variables, change +" options in the modeline itself. (Example: change 'tokrange' as requested by +" preceding call to :MoveStartTok command.) +" Note: Input line may be either a valid line number or a string representing +" a valid modeline (which is constructed by the caller) +" Return: +" 0 - no txtfmt modeline found +" -1 - txtfmt modeline containing error (bad option) +" 1 - valid txtfmt modeline found and processed +" Note: Modeline format is described under function s:Is_txtfmt_modeline. +fu! s:Process_txtfmt_modeline(line) + if a:line =~ '^[1-9][0-9]*$' + " Obtain the line to be processed + let l:line = a:line + let linestr = getline(a:line) + else + " The line to be processed is not in the buffer + let l:line = 0 + let linestr = a:line + endif + " Is the line a modeline? + if !s:Is_txtfmt_modeline(linestr) + " Note: This is not considered error - the line is simply not a + " modeline + return 0 + endif + " If here, overall format is correct. Are all options valid? + " Assume valid modeline until we find out otherwise... + let ret_val = 1 + " Save the leading text, in case we need to create a version of the line + " with changed options (e.g., starttok change) + let leading = substitute(linestr, s:re_modeline, '\1', '') + " The middle (options) part will be built up as we go + let middle = '' + " Save the trailing stuff + let trailing = substitute(linestr, s:re_modeline, '\3', '') + " Extract the {options} portion (leading/trailing stuff removed) for + " processing + let optstr = substitute(linestr, s:re_modeline, '\2', '') + " Extract pieces from head of optstr as long as unprocessed options exist + " Note: The following pattern may be used to extract element separators + " into \1, opt name into \2, equal sign (if it exists) into \3, opt value + " (if it exists) into \4, and to strip all three from the head of the + " string. (The remainder of the modeline will be in \5.) + " Note: Element separator is optional in the following re, since it won't + " exist for first option, and we've already verified that it exists + " between all other options. + let re_opt = '\('.s:re_ml_elsep.'\)\?\(' + \.s:re_ml_name.'\)\%(\(=\)\('.s:re_ml_val.'\)\)\?\(.*\)' + " If this is a real buffer line, do some special processing required only + " when modeline options are being changed or added + if l:line > 0 + " Set this flag if we make a requested change to an option, which needs to + " be reflected in the actual modeline text in the buffer (e.g., starttok + " change) + unlet! line_changed + " If we haven't done so already, save location at which new options can be + " added. + if !exists('s:txtfmt_ml_addline') + let s:txtfmt_ml_addline = l:line + " Note: The +1 changes 0-based byte index to 1-based col index + " Note: Saved value represents the col number of the first char to be + " added + let s:txtfmt_ml_addcol = matchend(linestr, s:re_ml_start) + 1 + endif + endif + " Loop over all options, exiting loop early if error occurs + while strlen(optstr) > 0 && ret_val != -1 + " Accumulate the option separator text + let middle = middle.substitute(optstr, re_opt, '\1', '') + " Extract option name and value + let optn = substitute(optstr, re_opt, '\2', '') + let has_eq = '=' == substitute(optstr, re_opt, '\3', '') + let optv = substitute(optstr, re_opt, '\4', '') + " Remove the option about to be processed from head of opt str + let optstr = substitute(optstr, re_opt, '\5', '') + " IMPORTANT TODO: The following if/else needs major refactoring to + " avoid duplication. There are ways of doing this that require far + " less brute-force. I'm saving it for a subsequent release to mitigate + " testing. + " Validate the option(s) + if optn == 'tokrange' || optn == 'rng' + "format: tokrange=[sSlLxX] + "Examples: '130s' '1500l' '130' '0x2000L' + if !has_eq + let s:err_str = "Value required for non-boolean txtfmt option 'tokrange'" + let b:txtfmt_cfg_tokrange = '' + let ret_val = -1 + elseif !s:Tokrange_is_valid(optv) + " 2 cases when option is invalid: + " 1) We can fix the invalid tokrange by changing to the value + " specified by user in call to :MoveStartTok + " 2) We must display error and use default + if exists('s:txtfmt_ml_new_starttok') + " Design Decision: Since option value is not valid, don't + " attempt to preserve any existing 'formats' specifier + " Assumption: b:txtfmt_cfg_bgcolor, + " b:txtfmt_cfg_longformats, and b:txtfmt_cfg_undercurl are + " unlet *only* at the top of Set_tokrange; thus, we can + " assume they will be valid here. + let optv = s:txtfmt_ml_new_starttok + \.b:txtfmt_const_tokrange_suffix_{b:txtfmt_cfg_bgcolor}{b:txtfmt_cfg_longformats}{b:txtfmt_cfg_undercurl} + " Record fact that change was made + unlet s:txtfmt_ml_new_starttok + let line_changed = 1 + else + let s:err_str = "Invalid 'tokrange' value - must be hex or dec" + \." char code value optionally followed by one of [sSlLxX]" + " Note: 'XL' suffix is currently illegal, but give a special + " warning if user attempts to use it. + if optv =~? 'xl$' + let s:err_str = s:err_str + \." (The XL suffix is currently illegal, due to a memory resource limitation" + \." affecting current versions of Vim; this suffix may, however, be supported in" + \." a future release.)" + endif + " Record the attempt to set b:txtfmt_cfg_tokrange from modeline + let b:txtfmt_cfg_tokrange = '' + let ret_val = -1 + endif + else + if exists('s:txtfmt_ml_new_starttok') + " Change the starttok value, preserving both the starttok + " number format (note that s:txtfmt_ml_new_starttok is + " actually a string) and any existing 'formats' + " specification + " Note: Since modeline setting trumps all others, an + " existing setting should agree with current setting + " anyway. + let optv = substitute(optv, b:txtfmt_re_number_atom, s:txtfmt_ml_new_starttok, '') + " Record fact that change was made + unlet s:txtfmt_ml_new_starttok + let line_changed = 1 + endif + " Save the option value, deferring processing till later... + let b:txtfmt_cfg_tokrange = optv + endif + elseif optn == 'fgcolormask' || optn == 'fcm' + if !has_eq + let s:err_str = "Value required for non-boolean txtfmt option 'fgcolormask'" + let b:txtfmt_cfg_fgcolormask = '' + let ret_val = -1 + elseif !s:Clrmask_is_valid(optv) + " Invalid number of colors + let s:err_str = "Invalid foreground color mask - must be string of 8 ones and zeroes" + let b:txtfmt_cfg_fgcolormask = '' + let ret_val = -1 + else + let b:txtfmt_cfg_fgcolormask = optv + endif + elseif optn == 'bgcolormask' || optn == 'bcm' + if !has_eq + let s:err_str = "Value required for non-boolean txtfmt option 'bgcolormask'" + let b:txtfmt_cfg_bgcolormask = '' + let ret_val = -1 + elseif !s:Clrmask_is_valid(optv) + " Invalid number of colors + let s:err_str = "Invalid background color mask - must be string of 8 ones and zeroes" + let b:txtfmt_cfg_bgcolormask = '' + let ret_val = -1 + else + let b:txtfmt_cfg_bgcolormask = optv + endif + elseif optn == 'sync' + "format: sync={||fromstart|none} + "Examples: 'sync=300' 'sync=0x1000' 'sync=fromstart' 'sync=none' + if !has_eq + let s:err_str = "Value required for non-boolean txtfmt option 'sync'" + let b:txtfmt_cfg_sync = '' + let ret_val = -1 + elseif !s:Sync_is_valid(optv) + let s:err_str = "Invalid 'sync' value - must be one of the" + \." following: , 'fromstart'" + \.", 'none'" + " Record the attempt to set b:txtfmt_cfg_sync from modeline + let b:txtfmt_cfg_sync = '' + let ret_val = -1 + else + " Defer processing of sync till later + let b:txtfmt_cfg_sync = optv + endif + elseif optn =~ '^\(no\)\?\(pack\|pck\)$' + " Make sure no option value was supplied to binary option + if has_eq + let s:err_str = "Cannot assign value to boolean txtfmt option 'pack'" + let b:txtfmt_cfg_pack = '' + let ret_val = -1 + else + " Option has been explicitly turned on or off + let b:txtfmt_cfg_pack = optn =~ '^no' ? 0 : 1 + endif + elseif optn =~ '^\(no\)\?\(undercurl\|uc\)$' + " Make sure no option value was supplied to binary option + if has_eq + let s:err_str = "Cannot assign value to boolean txtfmt option 'undercurl'" + let b:txtfmt_cfg_undercurlpref = '' + let ret_val = -1 + else + " Option has been explicitly turned on or off + let b:txtfmt_cfg_undercurlpref = optn =~ '^no' ? 0 : 1 + endif + elseif optn =~ '^\(no\)\?\(nested\|nst\)$' + " Make sure no option value was supplied to binary option + if has_eq + let s:err_str = "Cannot assign value to boolean txtfmt option 'nested'" + let b:txtfmt_cfg_nested = '' + let ret_val = -1 + else + " Option has been explicitly turned on or off + let b:txtfmt_cfg_nested = optn =~ '^no' ? 0 : 1 + endif + elseif optn =~ '^\(no\)\?\(conceal\|cncl\)$' + " Make sure no option value was supplied to binary option + if has_eq + let s:err_str = "Cannot assign value to boolean txtfmt option 'conceal'" + let b:txtfmt_cfg_conceal = '' + let ret_val = -1 + else + " Option has been explicitly turned on or off + let b:txtfmt_cfg_conceal = optn =~ '^no' || !has('conceal') ? 0 : 1 + endif + elseif optn == 'concealcursor' || optn == 'cocu' + " format: cocu=[n][v][i][c] + if !has_eq + let s:err_str = "Value required for non-boolean txtfmt option 'concealcursor'" + let b:txtfmt_cfg_concealcursor_invalid = 1 + let ret_val = -1 + elseif !s:Concealcursor_is_valid(optv) + let s:err_str = "Invalid 'concealcursor' option: `" . optv + \ . "' - only the following flags are permitted: nvic" + " Note: This one is different from the others: empty string is + " valid option value, so we can't use null string to indicate + " error + let b:txtfmt_cfg_concealcursor_invalid = 1 + let ret_val = -1 + else + " Option has been explicitly set + " Vim Issue: With 'concealcursor', duplicate flags aren't + " removed as they are for other such options (and as + " documentation indicates they should be). + " Note: Intentially skipping check for duplicates since + " they're harmless. + let b:txtfmt_cfg_concealcursor = optv + endif + elseif optn == 'escape' || optn == 'esc' + "format: escape=[bslash|self|none] + "TODO: Perhaps use s:Escape_is_valid() for validation + if !has_eq + let s:err_str = "Value required for non-boolean txtfmt option 'escape'" + let b:txtfmt_cfg_escape = '' + let ret_val = -1 + elseif optv == 'bslash' + let b:txtfmt_cfg_escape = 'bslash' + elseif optv == 'self' + let b:txtfmt_cfg_escape = 'self' + elseif optv == 'none' + let b:txtfmt_cfg_escape = 'none' + else + let s:err_str = "Invalid 'escape' value - must be 'bslash', 'self', or 'none'" + let b:txtfmt_cfg_escape = '' + let ret_val = -1 + endif + else + let s:err_str = "Unknown txtfmt modeline option: ".optn + let ret_val = -1 + endif + " Append optn[=optv] to middle + let middle = middle . optn . (has_eq ? '=' . optv : '') + endwhile + " Processed txtfmt modeline without error + if l:line > 0 && exists('line_changed') + " Alter the line to reflect any option changes + " Note: If error occurred above, optstr may be non-empty, in which + " case, we need to append it to the already processed options in + " middle. + call setline(l:line, leading.middle.optstr.trailing) + endif + return ret_val +endfu +" >>> +" Function: s:Do_txtfmt_modeline() <<< +" Purpose: Look for txtfmt "modelines" of the following form: +" .\{-}txtfmt:: +" which appear within the first or last 'modelines' lines in the buffer. +" Note: Function will search a number of lines (at start and end of buffer), +" as determined from 'modelines', provided that this value is nonzero. If +" 'modelines' is 0, default of 5 lines at beginning and end will be searched. +" Return: +" 0 - no txtfmt modeline found +" N - N valid txtfmt modelines found and processed +" -N - txtfmt modeline processing error occurred on the Nth modeline +" processed +" Error_handling: If error is encountered in a modeline, the remainder of +" the offending modeline is discarded, and modeline processing is aborted; +" i.e., no more lines are searched. This is consistent with the way Vim's +" modeline processing works. +" Modeline_modifications: The following buf-local variables are considered to +" be inputs that request changes to existing modelines: +" b:txtfmt_ml_new_starttok +" ... +" If the requested change can't be made, we will attempt to add the requested +" setting to an existing modeline. If there are no existing modelines, we will +" add a new one (warning user if he has modeline processing turned off). If +" modifications are made to the buffer, we will use b:txtfmt_ml_save_modified +" to determine whether the buffer was in a modified state prior to our +" changes, and will save our changes if and only if doing so doesn't commit +" any unsaved user changes. +" Assumption: Another Txtfmt function sets b:txtfmt_ml_save_modified +" appropriately before the Txtfmt-initiated changes begin. +fu! s:Do_txtfmt_modeline() + " Check for request to change starttok + if exists('b:txtfmt_ml_new_starttok') + " Communicate the request to modeline processing function. + let s:txtfmt_ml_new_starttok = b:txtfmt_ml_new_starttok + " Unlet the original to ensure that if we abort with error, we don't + " do this next time + unlet! b:txtfmt_ml_new_starttok + else + " Clean up after any previous failed attempts + unlet! s:txtfmt_ml_new_starttok + endif + " Did someone anticipate that we might be modifying the buffer? + if exists('b:txtfmt_ml_save_modified') + let l:save_modified = b:txtfmt_ml_save_modified + unlet b:txtfmt_ml_save_modified + endif + " The following apply to any option change request. They're set within + " Process_txtfmt_modeline the first time a suitable add location is found + unlet! s:txtfmt_ml_addline + unlet! s:txtfmt_ml_addcol + + " Keep up with # of modelines actually encountered + let l:ml_seen = 0 + " How many lines should we search? + " Priority is given to txtfmtModelines user option if it exists + if exists('b:txtfmtModelines') || exists('g:txtfmtModelines') + " User can disable by setting to 0 + let mls_use = exists('b:txtfmtModelines') ? b:txtfmtModelines : g:txtfmtModelines + else + " Use 'modelines' option+1 unless it's not a valid nonzero value, in + " which case, we use default of 5 + " NOTE: 1 is added to 'modelines' option so that if modeline is at + " highest or lowest possible line, putting a txtfmt modeline above or + " below it, respectively, will work. + let mls_use = &modelines > 0 ? &modelines+1 : 5 + endif + let nlines = line('$') + " Check first modelines lines + " TODO - Combine the 2 loops into one, which can alter the loop counter in + " a stepwise manner. + let i = 1 + while i <= mls_use && i <= nlines + let status = s:Process_txtfmt_modeline(i) + if status == 1 + " Successfully extracted options + let l:ml_seen = l:ml_seen + 1 + elseif status == -1 + " Error processing the modeline + echoerr "Ignoring txtfmt modeline on line ".i.": ".s:err_str + return -(l:ml_seen + 1) + endif + " Keep looking... + let i = i + 1 + endwhile + " Check last modelines lines + let i = nlines - mls_use + 1 + " Adjust if necessary to keep from checking already checked lines + if i <= mls_use + let i = mls_use + 1 + endif + while i <= nlines + let status = s:Process_txtfmt_modeline(i) + if status == 1 + " Successfully extracted options + let l:ml_seen = l:ml_seen + 1 + elseif status == -1 + " Error processing the modeline + echoerr "Ignoring txtfmt modeline on line ".i.": ".s:err_str + return -(l:ml_seen + 1) + endif + " Keep looking... + let i = i + 1 + endwhile + " Deal with any unprocessed requests for modeline option changes + " Note: Process_txtfmt_modeline unlets s:txtfmt_ml_new_<...> vars that + " have been completely handled. + let l:ml_new_opts = '' + if exists('s:txtfmt_ml_new_starttok') + " TODO: Decide whether it matters that this strategy will put a + " useless space at end of modeline in the unlikely event that the + " original modeline contained no options + " Assumption: b:txtfmt_cfg_bgcolor, b:txtfmt_cfg_longformats, and + " b:txtfmt_cfg_undercurl are unlet *only* at the top of Set_tokrange; + " thus, we can assume they will be valid here. + let l:ml_new_opts = l:ml_new_opts + \.'tokrange=' + \.s:txtfmt_ml_new_starttok + \.b:txtfmt_const_tokrange_suffix_{b:txtfmt_cfg_bgcolor}{b:txtfmt_cfg_longformats}{b:txtfmt_cfg_undercurl} + \.' ' + endif + " Any additional requests would go here + " ... + " Do we have any options to add? + if strlen(l:ml_new_opts) + " Create the modeline that will be passed to Process_txtfmt_modeline + let l:ml_process = "\txtfmt:".l:ml_new_opts + " Add what needs to be added to the buffer + if exists('s:txtfmt_ml_addline') + " Add new options to existing modeline + let linestr = getline(s:txtfmt_ml_addline) + " Recreate the line + call setline(s:txtfmt_ml_addline, + \ strpart(linestr, 0, s:txtfmt_ml_addcol - 1) + \ . l:ml_new_opts + \ . strpart(linestr, s:txtfmt_ml_addcol - 1)) + else + " Create a new txtfmt modeline on first line. Note that it's the + " same as what will be passed to Process_txtfmt_modeline for + " processing. + call append(0, l:ml_process) + if mls_use == 0 + " Warn user that he should change his modelines setting + " TODO_BG: Figure out how to prevent the warning from + " disappearing after a second or two. + echomsg "Warning: Txtfmt has added option settings to a modeline at the beginning" + \." of the buffer, but your modeline setting is such that this modeline" + \." will be ignored next time the buffer is opened." + \." (:help txtfmtModelines)" + endif + " Record fact that we've processed another modeline + let l:ml_seen = l:ml_seen + 1 + endif + " Process only the options just added + let status = s:Process_txtfmt_modeline(l:ml_process) + " Note: Error should be impossible here, but just in case... + if status < 0 + echoerr "Internal error: Unexpected error while processing txtfmt-generated modeline: ".s:err_str.". Contact the developer" + return -(l:ml_seen) + elseif status == 0 + echoerr "Internal error: Failed to extract option(s) while processing txtfmt-generated modeline. Contact the developer" + return -(l:ml_seen) + endif + endif + " If modeline processing made an unmodified buffer modified, save our + " changes now. (Rationale: Leave the buffer in the state it was in prior + " to modeline processing. This avoids making user's unsaved changes + " permanent.) + if exists('l:save_modified') && !l:save_modified && &modified + write + endif + " If here, we encountered no error. Return the number of modelines + " processed (could be zero) + return l:ml_seen +endfu +" >>> +" Function: s:Process_clr_masks() <<< +" Inputs: +" b:txtfmt_cfg_fgcolormask +" b:txtfmt_cfg_bgcolormask +" Description: Each mask is a string of 8 chars, each of which must be either +" '0' or '1' +" Outputs: +" b:txtfmt_cfg_fgcolor{} b:txtfmt_cfg_numfgcolors +" b:txtfmt_cfg_bgcolor{} b:txtfmt_cfg_numbgcolors +" Description: The color arrays are 1-based indirection arrays, which +" contain a single element for each of the active colors. The elements of +" these arrays are the 1-based indices of the corresponding color in the +" actual color definition array (which always contains 8 elements). +" Note: numcolors will be 0 and corresponding array will be empty if +" there are no 1's in the colormask +" Note: If 'tokrange' setting precludes background colors, the bg colormask +" option will be all 0's at this point, regardless of how user has configured +" the option. +fu! s:Process_clr_masks() + " Loop over fg and bg + let fgbg_idx = 0 + let fg_or_bg{0} = 'fg' + let fg_or_bg{1} = 'bg' + while fgbg_idx < 2 + " Cache the mask to be processed + let mask = b:txtfmt_cfg_{fg_or_bg{fgbg_idx}}colormask + " Note: To be on the safe side, I'm going to zero the bg color mask + " whenever bg colors are disabled, just in case caller forgot. + if fg_or_bg{fgbg_idx} == 'bg' && !b:txtfmt_cfg_bgcolor && mask =~ '1' + let mask = '00000000' + endif + " Loop over all 8 'bits' in the mask + " Assumption: The mask length has already been validated + " Note: All color arrays processed are 1-based (since index 0, if it + " exists, corresponds to 'no color'), but mask bit 'array' is + " inherently 0-based (because it's a string) + let i = 0 + let iadd = 0 + while i < 8 + if mask[i] == '1' + " Append this color's (1-based) index to active color array + " (which is also 1-based) + let iadd = iadd + 1 + let b:txtfmt_cfg_{fg_or_bg{fgbg_idx}}color{iadd} = i + 1 + endif + let i = i + 1 + endwhile + " Store number of active colors + let b:txtfmt_cfg_num{fg_or_bg{fgbg_idx}}colors = iadd + " Prepare for next iteration + let fgbg_idx = fgbg_idx + 1 + endwhile +endfu +" >>> +" Function: s:Define_fmtclr_vars() <<< +fu! s:Define_fmtclr_vars() + " Format definition array <<< + " NOTE: This array is used for rhs of syntax definitions, but also for display + " by ShowTokenMap. + let b:txtfmt_fmt{0} = 'NONE' + let b:txtfmt_fmt{1} = 'underline' + let b:txtfmt_fmt{2} = 'bold' + let b:txtfmt_fmt{3} = 'underline,bold' + let b:txtfmt_fmt{4} = 'italic' + let b:txtfmt_fmt{5} = 'underline,italic' + let b:txtfmt_fmt{6} = 'bold,italic' + let b:txtfmt_fmt{7} = 'underline,bold,italic' + if !b:txtfmt_cfg_longformats + " short formats + let b:txtfmt_num_formats = 8 + else + " long formats + let b:txtfmt_fmt{8} = 'standout' + let b:txtfmt_fmt{9} = 'underline,standout' + let b:txtfmt_fmt{10} = 'bold,standout' + let b:txtfmt_fmt{11} = 'underline,bold,standout' + let b:txtfmt_fmt{12} = 'italic,standout' + let b:txtfmt_fmt{13} = 'underline,italic,standout' + let b:txtfmt_fmt{14} = 'bold,italic,standout' + let b:txtfmt_fmt{15} = 'underline,bold,italic,standout' + let b:txtfmt_fmt{16} = 'reverse' + let b:txtfmt_fmt{17} = 'underline,reverse' + let b:txtfmt_fmt{18} = 'bold,reverse' + let b:txtfmt_fmt{19} = 'underline,bold,reverse' + let b:txtfmt_fmt{20} = 'italic,reverse' + let b:txtfmt_fmt{21} = 'underline,italic,reverse' + let b:txtfmt_fmt{22} = 'bold,italic,reverse' + let b:txtfmt_fmt{23} = 'underline,bold,italic,reverse' + let b:txtfmt_fmt{24} = 'standout,reverse' + let b:txtfmt_fmt{25} = 'underline,standout,reverse' + let b:txtfmt_fmt{26} = 'bold,standout,reverse' + let b:txtfmt_fmt{27} = 'underline,bold,standout,reverse' + let b:txtfmt_fmt{28} = 'italic,standout,reverse' + let b:txtfmt_fmt{29} = 'underline,italic,standout,reverse' + let b:txtfmt_fmt{30} = 'bold,italic,standout,reverse' + let b:txtfmt_fmt{31} = 'underline,bold,italic,standout,reverse' + " If using undercurl (introduced in Vim 7.0), there will be twice as + " many formats. + if !b:txtfmt_cfg_undercurl + let b:txtfmt_num_formats = 32 + else + let b:txtfmt_fmt{32} = 'undercurl' + let b:txtfmt_fmt{33} = 'underline,undercurl' + let b:txtfmt_fmt{34} = 'bold,undercurl' + let b:txtfmt_fmt{35} = 'underline,bold,undercurl' + let b:txtfmt_fmt{36} = 'italic,undercurl' + let b:txtfmt_fmt{37} = 'underline,italic,undercurl' + let b:txtfmt_fmt{38} = 'bold,italic,undercurl' + let b:txtfmt_fmt{39} = 'underline,bold,italic,undercurl' + let b:txtfmt_fmt{40} = 'standout,undercurl' + let b:txtfmt_fmt{41} = 'underline,standout,undercurl' + let b:txtfmt_fmt{42} = 'bold,standout,undercurl' + let b:txtfmt_fmt{43} = 'underline,bold,standout,undercurl' + let b:txtfmt_fmt{44} = 'italic,standout,undercurl' + let b:txtfmt_fmt{45} = 'underline,italic,standout,undercurl' + let b:txtfmt_fmt{46} = 'bold,italic,standout,undercurl' + let b:txtfmt_fmt{47} = 'underline,bold,italic,standout,undercurl' + let b:txtfmt_fmt{48} = 'reverse,undercurl' + let b:txtfmt_fmt{49} = 'underline,reverse,undercurl' + let b:txtfmt_fmt{50} = 'bold,reverse,undercurl' + let b:txtfmt_fmt{51} = 'underline,bold,reverse,undercurl' + let b:txtfmt_fmt{52} = 'italic,reverse,undercurl' + let b:txtfmt_fmt{53} = 'underline,italic,reverse,undercurl' + let b:txtfmt_fmt{54} = 'bold,italic,reverse,undercurl' + let b:txtfmt_fmt{55} = 'underline,bold,italic,reverse,undercurl' + let b:txtfmt_fmt{56} = 'standout,reverse,undercurl' + let b:txtfmt_fmt{57} = 'underline,standout,reverse,undercurl' + let b:txtfmt_fmt{58} = 'bold,standout,reverse,undercurl' + let b:txtfmt_fmt{59} = 'underline,bold,standout,reverse,undercurl' + let b:txtfmt_fmt{60} = 'italic,standout,reverse,undercurl' + let b:txtfmt_fmt{61} = 'underline,italic,standout,reverse,undercurl' + let b:txtfmt_fmt{62} = 'bold,italic,standout,reverse,undercurl' + let b:txtfmt_fmt{63} = 'underline,bold,italic,standout,reverse,undercurl' + let b:txtfmt_num_formats = 64 + endif + endif + " >>> + " <<< Default color definition array + " These are the original defaults + let s:txtfmt_clr{1} = '^\\%(k\\|bla\\%[ck]\\)$,c:Black,g:#000000' + let s:txtfmt_clr{2} = '^b\\%[lue]$,c:DarkBlue,g:#0000FF' + let s:txtfmt_clr{3} = '^g\\%[reen]$,c:DarkGreen,g:#00FF00' + let s:txtfmt_clr{4} = '^t\\%[urquoise]$,c:DarkCyan,g:#00FFFF' + let s:txtfmt_clr{5} = '^r\\%[ed]$,c:DarkRed,g:#FF0000' + let s:txtfmt_clr{6} = '^v\\%[iolet]$,c:DarkMagenta,g:#FF00FF' + let s:txtfmt_clr{7} = '^y\\%[ellow]$,c:DarkYellow,g:#FFFF00' + let s:txtfmt_clr{8} = '^w\\%[hite]$,c:White,g:#FFFFFF' + " Note: The following variable indicates the total number of colors + " possible, including 'no color', which is not in the txtfmt_clr array. + " TODO: See how this is used to see how to use numfgcolors and numbgcolors... + let b:txtfmt_num_colors = 9 + " >>> + " Set fmt/clr specific values for convenience + " txtfmt__first_tok: 1st (default) token + " txtfmt__last_tok: last (reserved) token + " txtfmt_last_tok: last token that could be used (if + " txtfmt_cfg_numbgcolors is applicable, + " assumes it to be 8) + " TODO NOTE - If desired, could allow the fmt/clr ranges to be split, in + " which case, the following 2 would be user-configurable. For now, just + " base them on txtfmtStarttok. + " TODO: Decide whether to keep this here or move outside this function + call s:Process_clr_masks() + " Save some useful char codes + let b:txtfmt_clr_first_tok = b:txtfmt_cfg_starttok + let b:txtfmt_clr_last_tok = b:txtfmt_cfg_starttok + b:txtfmt_num_colors - 1 + let b:txtfmt_fmt_first_tok = b:txtfmt_clr_last_tok + 1 + let b:txtfmt_fmt_last_tok = b:txtfmt_fmt_first_tok + b:txtfmt_num_formats - 1 + if b:txtfmt_cfg_bgcolor + " Location of bg color range depends upon 'pack' setting as well + " as well as type of formats in effect + " Note: Intentionally hardcoding bgcolor index to 0 and undercurl + " index to 1 (when formats are long) to get desired length + " TODO: Replace ternaries with normal if block + let b:txtfmt_bgc_first_tok = b:txtfmt_cfg_starttok + + \ b:txtfmt_const_tokrange_size_{0}{ + \(b:txtfmt_cfg_longformats || !b:txtfmt_cfg_pack ? 1 : 0)}{ + \(b:txtfmt_cfg_longformats || !b:txtfmt_cfg_pack ? 1 : 0)} + let b:txtfmt_bgc_last_tok = b:txtfmt_bgc_first_tok + b:txtfmt_num_colors - 1 + let b:txtfmt_last_tok = b:txtfmt_bgc_last_tok + else + " nothing after the fmt range + let b:txtfmt_bgc_first_tok = -1 + let b:txtfmt_bgc_last_tok = -1 + let b:txtfmt_last_tok = b:txtfmt_fmt_last_tok + endif +endfu +" >>> +" Function: s:Define_fmtclr_regexes() <<< +" Purpose: Define regexes involving the special fmt/clr tokens. +" Assumption: The following variable(s) has been defined for the buffer: +" b:txtfmt_cfg_starttok +" Note: The start tok is user-configurable. Thus, this function should be +" called only after processing options. +fu! s:Define_fmtclr_regexes() + " Cache bgc enabled flag for subsequent tests + let bgc = b:txtfmt_cfg_bgcolor && b:txtfmt_cfg_numbgcolors > 0 + let clr = b:txtfmt_cfg_numfgcolors > 0 + " 0 1 3 4 5 7 8 + " 1 3 4 5 7 8 + let fgbg_idx = 0 + let clr_or_bgc{0} = 'clr' | let fg_or_bg{0} = 'fg' + let clr_or_bgc{1} = 'bgc' | let fg_or_bg{1} = 'bg' + while fgbg_idx < 2 + if b:txtfmt_cfg_num{fg_or_bg{fgbg_idx}}colors + " Note: Handle first active color here, outside the loop. To handle it + " inside the loop, I would need to initialize tok_cnt to 0 and tok_i + " to -1 and handle tok_i == -1 specially within the loop. (tok_i == -1 + " indicates that we don't have the potential beginning of a range) + let tok_i = b:txtfmt_cfg_{fg_or_bg{fgbg_idx}}color{1} + let tok_cnt = 1 + let i = 2 " first active color already accounted for + " Initialize regex string to be built within loop + let b:txtfmt_re_{clr_or_bgc{fgbg_idx}}_stok_atom = '' + " Loop over active colors (and one fictitious element past end of + " array). Note that first active color was handled in initializations. + while i <= b:txtfmt_cfg_num{fg_or_bg{fgbg_idx}}colors + 1 + " We know something is ending (atom or range) if any of the + " following conditions is true: + " -We're on the non-existent element one past end of active color + " array + " -The current active color index is not 1 greater than the last + " TODO_BG: Can't compare with tok_i + 1 since tok_i isn't + " updated through range. + if i >= b:txtfmt_cfg_num{fg_or_bg{fgbg_idx}}colors + 1 || (b:txtfmt_cfg_{fg_or_bg{fgbg_idx}}color{i} != tok_i + tok_cnt) + " Something is ending + if tok_cnt > 1 + " Append range if more than 2 chars; otherwise, make + " it a double atom. + let b:txtfmt_re_{clr_or_bgc{fgbg_idx}}_stok_atom = b:txtfmt_re_{clr_or_bgc{fgbg_idx}}_stok_atom + \.nr2char(b:txtfmt_{clr_or_bgc{fgbg_idx}}_first_tok + tok_i) + \.(tok_cnt > 2 ? '-' : '') + \.nr2char(b:txtfmt_{clr_or_bgc{fgbg_idx}}_first_tok + tok_i + tok_cnt - 1) + else + " Append atom + let b:txtfmt_re_{clr_or_bgc{fgbg_idx}}_stok_atom = b:txtfmt_re_{clr_or_bgc{fgbg_idx}}_stok_atom + \.nr2char(b:txtfmt_{clr_or_bgc{fgbg_idx}}_first_tok + tok_i) + endif + " Start something new unless at end + if i <= b:txtfmt_cfg_num{fg_or_bg{fgbg_idx}}colors + let tok_cnt = 1 + let tok_i = b:txtfmt_cfg_{fg_or_bg{fgbg_idx}}color{i} + endif + else + " Nothing is ending - record continuation + let tok_cnt = tok_cnt + 1 + endif + let i = i + 1 + endwhile + " Create the _tok_ version from the _stok_ version by prepending + " default (end) token + " Decision Needed: Do I want to create tok this way, or would it be + " better to gain a slight bit of highlighting efficiency in some cases + " by putting the default tok into a range if possible. + " Note: I'm leaning against this optimization. Consider that it would + " be possible only for color configurations in which the first color + " is active; hence, if there were any speed difference in the + " highlighting (and a significant one is doubtful in my opinion), it + " would depend upon the specific color masks, which seems inconsistent + " and therefore inappropriate. + let b:txtfmt_re_{clr_or_bgc{fgbg_idx}}_tok_atom = + \ nr2char(b:txtfmt_{clr_or_bgc{fgbg_idx}}_first_tok) + \ . b:txtfmt_re_{clr_or_bgc{fgbg_idx}}_stok_atom + let b:txtfmt_re_{clr_or_bgc{fgbg_idx}}_etok_atom = + \ nr2char(b:txtfmt_{clr_or_bgc{fgbg_idx}}_first_tok) + endif + let fgbg_idx = fgbg_idx + 1 + endwhile + " Format region tokens + let b:txtfmt_re_fmt_tok_atom = nr2char(b:txtfmt_fmt_first_tok).'-'.nr2char(b:txtfmt_fmt_last_tok) + let b:txtfmt_re_fmt_stok_atom = nr2char(b:txtfmt_fmt_first_tok + 1).'-'.nr2char(b:txtfmt_fmt_last_tok) + let b:txtfmt_re_fmt_etok_atom = nr2char(b:txtfmt_fmt_first_tok) + " Color regions that include inactive colors + if clr + let b:txtfmt_re_CLR_tok_atom = nr2char(b:txtfmt_clr_first_tok).'-'.nr2char(b:txtfmt_clr_last_tok) + let b:txtfmt_re_CLR_stok_atom = nr2char(b:txtfmt_clr_first_tok + 1).'-'.nr2char(b:txtfmt_clr_last_tok) + let b:txtfmt_re_CLR_etok_atom = nr2char(b:txtfmt_clr_first_tok) + endif + if bgc + let b:txtfmt_re_BGC_tok_atom = nr2char(b:txtfmt_bgc_first_tok).'-'.nr2char(b:txtfmt_bgc_last_tok) + let b:txtfmt_re_BGC_stok_atom = nr2char(b:txtfmt_bgc_first_tok + 1).'-'.nr2char(b:txtfmt_bgc_last_tok) + let b:txtfmt_re_BGC_etok_atom = nr2char(b:txtfmt_bgc_first_tok) + endif + " Combined regions + let b:txtfmt_re_any_tok_atom = + \(clr ? b:txtfmt_re_clr_tok_atom : '') + \.nr2char(b:txtfmt_fmt_first_tok).'-'.nr2char(b:txtfmt_fmt_last_tok) + \.(bgc ? b:txtfmt_re_bgc_tok_atom : '') + " TODO: Perhaps get rid of dependence upon b:txtfmt_clr_last_tok? + " TODO: Refactor to use newly-created CLR and BGC atoms + let b:txtfmt_re_ANY_tok_atom = + \(clr ? nr2char(b:txtfmt_clr_first_tok).'-'.nr2char(b:txtfmt_clr_last_tok) : '') + \.nr2char(b:txtfmt_fmt_first_tok).'-'.nr2char(b:txtfmt_fmt_last_tok) + \.(bgc ? nr2char(b:txtfmt_bgc_first_tok).'-'.nr2char(b:txtfmt_bgc_last_tok) : '') + let b:txtfmt_re_any_stok_atom = + \(clr ? b:txtfmt_re_clr_stok_atom : '') + \.nr2char(b:txtfmt_fmt_first_tok + 1).'-'.nr2char(b:txtfmt_fmt_last_tok) + \.(bgc ? b:txtfmt_re_bgc_stok_atom : '') + let b:txtfmt_re_any_etok_atom = + \(clr ? b:txtfmt_re_clr_etok_atom : '') + \.b:txtfmt_re_fmt_etok_atom + \.(bgc ? b:txtfmt_re_bgc_etok_atom : '') + + if b:txtfmt_cfg_escape == 'bslash' + " The following pattern is a zero-width look-behind assertion, which + " matches only at a non-backslash-escaped position. + let noesc = '\%(\%(^\|[^\\]\)\%(\\\\\)*\\\)\@>> +" Function: s:Do_config_common() <<< +" Purpose: Set script local variables, taking into account whether user has +" overriden via txtfmt globals. +fu! s:Do_config_common() + " unlet any buffer-specific options that may be set by txtfmt modeline <<< + unlet! b:txtfmt_cfg_tokrange + \ b:txtfmt_cfg_sync b:txtfmt_cfg_escape + \ b:txtfmt_cfg_pack b:txtfmt_cfg_nested + \ b:txtfmt_cfg_numfgcolors b:txtfmt_cfg_numbgcolors + \ b:txtfmt_cfg_fgcolormask b:txtfmt_cfg_bgcolormask + \ b:txtfmt_cfg_undercurlpref b:txtfmt_cfg_conceal + \ b:txtfmt_cfg_concealcursor b:txtfmt_cfg_concealcursor_invalid + " >>> + " Attempt to process modeline <<< + let ml_status = s:Do_txtfmt_modeline() + " >>> + " 'escape' option <<< + if !exists('b:txtfmt_cfg_escape') || strlen(b:txtfmt_cfg_escape) == 0 + " Either option wasn't set within modeline, or it was set to invalid + " value + unlet! l:bad_set_by + if exists('b:txtfmt_cfg_escape') && strlen(b:txtfmt_cfg_escape) == 0 + " Bad modeline set + let l:bad_set_by = 'm' + elseif exists('b:txtfmtEscape') + " User overrode buf-local option + if s:Escape_is_valid(b:txtfmtEscape) + let b:txtfmt_cfg_escape = b:txtfmtEscape + else + let l:bad_set_by = 'b' + endif + elseif exists('g:txtfmtEscape') + " User overrode global option + if s:Escape_is_valid(g:txtfmtEscape) + let b:txtfmt_cfg_escape = g:txtfmtEscape + else + let l:bad_set_by = 'g' + endif + endif + " Warn user if invalid user-setting is about to be overridden + if exists('l:bad_set_by') + " Note: Display the offending option value for buf-local or global + " option, but not for modeline, since modeline processing has + " already reported the error. + echoerr "Warning: Ignoring invalid ".( + \ l:bad_set_by == 'm' ? "modeline" : + \ l:bad_set_by == 'b' ? "buf-local" : + \ "global") . " value for txtfmt `escape' option" . ( + \ l:bad_set_by == 'm' ? '' : + \ l:bad_set_by == 'b' ? (': ' . b:txtfmtEscape) : + \ (': ' . g:txtfmtEscape)) + endif + if !exists('b:txtfmt_cfg_escape') || strlen(b:txtfmt_cfg_escape) == 0 + " Set to default + let b:txtfmt_cfg_escape = 'none' + endif + endif + " >>> + " 'pack' option <<< + if !exists('b:txtfmt_cfg_pack') || strlen(b:txtfmt_cfg_pack) == 0 + " Either option wasn't set within modeline, or it was set to invalid + " value + if exists('b:txtfmt_cfg_pack') && strlen(b:txtfmt_cfg_pack) == 0 + " Bad modeline set. Warn user that we're about to override. Note + " that modeline processing has already reported the specific + " error. + echoerr "Warning: Ignoring invalid modeline value for txtfmt `pack' option" + elseif exists('b:txtfmtPack') + " User overrode buf-local option + " Note: Invalid setting impossible for boolean + let b:txtfmt_cfg_pack = b:txtfmtPack + elseif exists('g:txtfmtPack') + " User overrode global option + " Note: Invalid setting impossible for boolean + let b:txtfmt_cfg_pack = g:txtfmtPack + endif + if !exists('b:txtfmt_cfg_pack') || strlen(b:txtfmt_cfg_pack) == 0 + " Set to default (on) + let b:txtfmt_cfg_pack = 1 + endif + endif + " >>> + " 'undercurl' option <<< + if !exists('b:txtfmt_cfg_undercurlpref') || strlen(b:txtfmt_cfg_undercurlpref) == 0 + " Either option wasn't set within modeline, or it was set to invalid + " value + if exists('b:txtfmt_cfg_undercurlpref') && strlen(b:txtfmt_cfg_undercurlpref) == 0 + " Bad modeline set. Warn user that we're about to override. Note + " that modeline processing has already reported the specific + " error. + echoerr "Warning: Ignoring invalid modeline value for txtfmt `undercurl' option" + elseif exists('b:txtfmtUndercurl') + " User overrode buf-local option + " Note: Invalid setting impossible for boolean + let b:txtfmt_cfg_undercurlpref = b:txtfmtUndercurl + elseif exists('g:txtfmtUndercurl') + " User overrode global option + " Note: Invalid setting impossible for boolean + let b:txtfmt_cfg_undercurlpref = g:txtfmtUndercurl + endif + if !exists('b:txtfmt_cfg_undercurlpref') || strlen(b:txtfmt_cfg_undercurlpref) == 0 + " Set to default (on) + " Note: This is 'preference' only; if Vim version doesn't support + " undercurl, we won't attempt to enable. + let b:txtfmt_cfg_undercurlpref = 1 + endif + endif + " >>> + " 'nested' option <<< + if !exists('b:txtfmt_cfg_nested') || strlen(b:txtfmt_cfg_nested) == 0 + " Either option wasn't set within modeline, or it was set to invalid + " value + if exists('b:txtfmt_cfg_nested') && strlen(b:txtfmt_cfg_nested) == 0 + " Bad modeline set. Warn user that we're about to override. Note + " that modeline processing has already reported the specific + " error. + echoerr "Warning: Ignoring invalid modeline value for txtfmt `nested' option" + elseif exists('b:txtfmtNested') + " User overrode buf-local option + " Note: Invalid setting impossible for boolean + let b:txtfmt_cfg_nested = b:txtfmtNested + elseif exists('g:txtfmtNested') + " User overrode global option + " Note: Invalid setting impossible for boolean + let b:txtfmt_cfg_nested = g:txtfmtNested + endif + if !exists('b:txtfmt_cfg_nested') || strlen(b:txtfmt_cfg_nested) == 0 + " Set to default (on) + let b:txtfmt_cfg_nested = 1 + endif + endif + " >>> + " 'conceal' option <<< + if !exists('b:txtfmt_cfg_conceal') || strlen(b:txtfmt_cfg_conceal) == 0 + " Either option wasn't set within modeline, or it was set to invalid + " value + if exists('b:txtfmt_cfg_conceal') + if strlen(b:txtfmt_cfg_conceal) == 0 + " Bad modeline set. Warn user that we're about to override. Note + " that modeline processing has already reported the specific + " error. + echoerr "Warning: Ignoring invalid modeline value for txtfmt `conceal' option" + elseif b:txtfmt_cfg_conceal && !has('conceal') + " Modeline enables 'conceal' but the feature isn't supported + " by Vim. Silently override the request. + let b:txtfmt_cfg_conceal = 0 + endif + elseif exists('b:txtfmtConceal') + " User overrode buf-local option + " Note: Invalid setting impossible for boolean + let b:txtfmt_cfg_conceal = has('conceal') && b:txtfmtConceal + elseif exists('g:txtfmtConceal') + " User overrode global option + " Note: Invalid setting impossible for boolean + let b:txtfmt_cfg_conceal = has('conceal') && g:txtfmtConceal + endif + if !exists('b:txtfmt_cfg_conceal') || strlen(b:txtfmt_cfg_conceal) == 0 + " Enable if support for 'conceal' feature is compiled into Vim. + " Note: This is a slightly non-backwards-compatible change with + " respect to Txtfmt version 2.3. See help for explanation. + let b:txtfmt_cfg_conceal = has('conceal') + endif + endif + " >>> + " 'concealcursor' option (dependent upon 'conceal') <<< + " Note: The value of this option will be ignored if 'conceal' is off (in + " which case, it might be safest to unset it). + if b:txtfmt_cfg_conceal + if !exists('b:txtfmt_cfg_concealcursor') || exists('b:txtfmt_cfg_concealcursor_invalid') + " Either option wasn't set within modeline, or it was set to invalid + " value + unlet! l:bad_set_by + if exists('b:txtfmt_cfg_concealcursor_invalid') + " Bad modeline set + let l:bad_set_by = 'm' + elseif exists('b:txtfmtConcealcursor') + " User overrode buf-local option + if s:Concealcursor_is_valid(b:txtfmtConcealcursor) + let b:txtfmt_cfg_concealcursor = b:txtfmtConcealcursor + else + let l:bad_set_by = 'b' + endif + elseif exists('g:txtfmtConcealcursor') + " User overrode global option + if s:Concealcursor_is_valid(g:txtfmtConcealcursor) + let b:txtfmt_cfg_concealcursor = g:txtfmtConcealcursor + else + let l:bad_set_by = 'g' + endif + endif + " Warn user if invalid user-setting is about to be overridden + if exists('l:bad_set_by') + " Note: Display the offending option value for buf-local or global + " option, but not for modeline, since modeline processing has + " already reported the error. + echoerr "Warning: Ignoring invalid ".( + \ l:bad_set_by == 'm' ? "modeline" : + \ l:bad_set_by == 'b' ? "buf-local" : + \ "global") . " value for txtfmt `concealcursor' option" . ( + \ l:bad_set_by == 'm' ? '' : + \ l:bad_set_by == 'b' ? (': ' . b:txtfmtConcealcursor) : + \ (': ' . g:txtfmtConcealcursor)) + endif + if !exists('b:txtfmt_cfg_concealcursor') || exists('b:txtfmt_cfg_concealcursor_invalid') + " By default, conceal tokens always, even in cursor line + let b:txtfmt_cfg_concealcursor = 'nvic' + endif + " At this point, b:txtfmt_cfg_concealcursor_invalid has served its + " purpose, and although it's unlet at the head of this function, + " I'd rather not leave an 'invalid' var in existence. + unlet! b:txtfmt_cfg_concealcursor_invalid + endif + else + " This option is N/A when 'conceal' is off + unlet! b:txtfmt_cfg_concealcursor + endif + " >>> + " 'tokrange' option <<< + " Note: 'starttok' and 'formats' are distinct options internally, but may + " be set only as a unit by the plugin user. Even if tokrange was set + " within modeline, there is work yet to be done. + call s:Set_tokrange() + " >>> + " 'fgcolormask' option <<< + if !exists('b:txtfmt_cfg_fgcolormask') || strlen(b:txtfmt_cfg_fgcolormask) == 0 + " Either option wasn't set within modeline, or it was set to invalid + " value + unlet! l:bad_set_by + if exists('b:txtfmt_cfg_fgcolormask') && strlen(b:txtfmt_cfg_fgcolormask) == 0 + " Bad modeline set + let l:bad_set_by = 'm' + elseif exists('b:txtfmtFgcolormask') + " User overrode buf-local option + if s:Clrmask_is_valid(b:txtfmtFgcolormask) + let b:txtfmt_cfg_fgcolormask = b:txtfmtFgcolormask + else + let l:bad_set_by = 'b' + endif + elseif exists('g:txtfmtFgcolormask') + " User overrode global option + if s:Clrmask_is_valid(g:txtfmtFgcolormask) + let b:txtfmt_cfg_fgcolormask = g:txtfmtFgcolormask + else + let l:bad_set_by = 'g' + endif + endif + " Warn user if invalid user-setting is about to be overridden + if exists('l:bad_set_by') + " Note: Display the offending option value for buf-local or global + " option, but not for modeline, since modeline processing has + " already reported the error. + echoerr "Warning: Ignoring invalid ".( + \ l:bad_set_by == 'm' ? "modeline" : + \ l:bad_set_by == 'b' ? "buf-local" : + \ "global") . " value for txtfmt `fgcolormask' option" . ( + \ l:bad_set_by == 'm' ? '' : + \ l:bad_set_by == 'b' ? (': ' . b:txtfmtFgcolormask) : + \ (': ' . g:txtfmtFgcolormask)) + endif + if !exists('b:txtfmt_cfg_fgcolormask') || strlen(b:txtfmt_cfg_fgcolormask) == 0 + " Set to default - all foreground colors active (for backward + " compatibility) + " TODO: Don't hardcode + let b:txtfmt_cfg_fgcolormask = '11111111' + endif + endif + " >>> + " 'bgcolormask' option <<< + if !exists('b:txtfmt_cfg_bgcolormask') || strlen(b:txtfmt_cfg_bgcolormask) == 0 + " Either option wasn't set within modeline, or it was set to invalid + " value + unlet! l:bad_set_by + if exists('b:txtfmt_cfg_bgcolormask') && strlen(b:txtfmt_cfg_bgcolormask) == 0 + " Bad modeline set + let l:bad_set_by = 'm' + elseif exists('b:txtfmtBgcolormask') + " User overrode buf-local option + if s:Clrmask_is_valid(b:txtfmtBgcolormask) + let b:txtfmt_cfg_bgcolormask = b:txtfmtBgcolormask + else + let l:bad_set_by = 'b' + endif + elseif exists('g:txtfmtBgcolormask') + " User overrode global option + if s:Clrmask_is_valid(g:txtfmtBgcolormask) + let b:txtfmt_cfg_bgcolormask = g:txtfmtBgcolormask + else + let l:bad_set_by = 'g' + endif + endif + " Warn user if invalid user-setting is about to be overridden + if exists('l:bad_set_by') + " Note: Display the offending option value for buf-local or global + " option, but not for modeline, since modeline processing has + " already reported the error. + echoerr "Warning: Ignoring invalid ".( + \ l:bad_set_by == 'm' ? "modeline" : + \ l:bad_set_by == 'b' ? "buf-local" : + \ "global") . " value for txtfmt `bgcolormask' option" . ( + \ l:bad_set_by == 'm' ? '' : + \ l:bad_set_by == 'b' ? (': ' . b:txtfmtBgcolormask) : + \ (': ' . g:txtfmtBgcolormask)) + endif + if !exists('b:txtfmt_cfg_bgcolormask') || strlen(b:txtfmt_cfg_bgcolormask) == 0 + " Set to default of red, green and blue if background colors are + " active; otherwise, disable all colors. + " TODO_BG: Decide whether it makes sense to unlet the variable + " completely. + " TODO_BG: b:txtfmt_cfg_bgcolor is probably not set yet!!!! This + " needs to be moved till after Set_tokrange + if b:txtfmt_cfg_bgcolor + " TODO: Don't hardcode + let b:txtfmt_cfg_bgcolormask = '01101000' + else + " No background color supported + let b:txtfmt_cfg_bgcolormask = '00000000' + endif + endif + endif + " Force mask to all zeroes if background colors are disabled. + " Assumption: Set_tokrange has already run; thus, b:txtfmt_cfg_bgcolor has + " been set. + if !b:txtfmt_cfg_bgcolor + let b:txtfmt_cfg_bgcolormask = '00000000' + endif + " >>> + " 'sync' option <<< + " Note: 'syncmethod' and 'synclines' are distinct options internally, but + " may be set only as a unit by the plugin user. Even if sync was set + " within modeline, there is work yet to be done. + call s:Set_syncing() + " >>> + " Define various buffer-specific variables now that fmt/clr ranges are fixed. + " TODO: Perhaps combine the following 2 functions in some way... + call s:Define_fmtclr_vars() + " Define fmt/clr regexes - used in both syntax and ftplugin <<< + call s:Define_fmtclr_regexes() + " >>> + " Process color options <<< + call s:Process_color_options() + " >>> +endfu +" >>> +call s:Do_config_common() +" Define buffer-local constants <<< +" For convenience, associate format indices with their respective +" '[u][b][i][s][r][c]' string, in fiducial form. Note that fiducial form may +" be used for display, but is also a valid (but not the only) fmt spec. +let b:ubisrc_fmt0 = '-' +let b:ubisrc_fmt1 = 'u' +let b:ubisrc_fmt2 = 'b' +let b:ubisrc_fmt3 = 'bu' +let b:ubisrc_fmt4 = 'i' +let b:ubisrc_fmt5 = 'iu' +let b:ubisrc_fmt6 = 'ib' +let b:ubisrc_fmt7 = 'ibu' +let b:ubisrc_fmt8 = 's' +let b:ubisrc_fmt9 = 'su' +let b:ubisrc_fmt10 = 'sb' +let b:ubisrc_fmt11 = 'sbu' +let b:ubisrc_fmt12 = 'si' +let b:ubisrc_fmt13 = 'siu' +let b:ubisrc_fmt14 = 'sib' +let b:ubisrc_fmt15 = 'sibu' +let b:ubisrc_fmt16 = 'r' +let b:ubisrc_fmt17 = 'ru' +let b:ubisrc_fmt18 = 'rb' +let b:ubisrc_fmt19 = 'rbu' +let b:ubisrc_fmt20 = 'ri' +let b:ubisrc_fmt21 = 'riu' +let b:ubisrc_fmt22 = 'rib' +let b:ubisrc_fmt23 = 'ribu' +let b:ubisrc_fmt24 = 'rs' +let b:ubisrc_fmt25 = 'rsu' +let b:ubisrc_fmt26 = 'rsb' +let b:ubisrc_fmt27 = 'rsbu' +let b:ubisrc_fmt28 = 'rsi' +let b:ubisrc_fmt29 = 'rsiu' +let b:ubisrc_fmt30 = 'rsib' +let b:ubisrc_fmt31 = 'rsibu' +let b:ubisrc_fmt32 = 'c' +let b:ubisrc_fmt33 = 'cu' +let b:ubisrc_fmt34 = 'cb' +let b:ubisrc_fmt35 = 'cbu' +let b:ubisrc_fmt36 = 'ci' +let b:ubisrc_fmt37 = 'ciu' +let b:ubisrc_fmt38 = 'cib' +let b:ubisrc_fmt39 = 'cibu' +let b:ubisrc_fmt40 = 'cs' +let b:ubisrc_fmt41 = 'csu' +let b:ubisrc_fmt42 = 'csb' +let b:ubisrc_fmt43 = 'csbu' +let b:ubisrc_fmt44 = 'csi' +let b:ubisrc_fmt45 = 'csiu' +let b:ubisrc_fmt46 = 'csib' +let b:ubisrc_fmt47 = 'csibu' +let b:ubisrc_fmt48 = 'cr' +let b:ubisrc_fmt49 = 'cru' +let b:ubisrc_fmt50 = 'crb' +let b:ubisrc_fmt51 = 'crbu' +let b:ubisrc_fmt52 = 'cri' +let b:ubisrc_fmt53 = 'criu' +let b:ubisrc_fmt54 = 'crib' +let b:ubisrc_fmt55 = 'cribu' +let b:ubisrc_fmt56 = 'crs' +let b:ubisrc_fmt57 = 'crsu' +let b:ubisrc_fmt58 = 'crsb' +let b:ubisrc_fmt59 = 'crsbu' +let b:ubisrc_fmt60 = 'crsi' +let b:ubisrc_fmt61 = 'crsiu' +let b:ubisrc_fmt62 = 'crsib' +let b:ubisrc_fmt63 = 'crsibu' +" >>> +else " if exists('b:txtfmt_do_common_config') +" Function: s:Txtfmt_refresh() <<< +" Purpose: Invoked by buffer-local command Refresh when user wishes to +" reload txtfmt plugins safely for the current buffer; e.g., after changing +" option settings. +" Important Note: This function must be within the else of an if +" exists('b:txtfmt_do_common_config'); otherwise, we will get an error when this +" function causes the plugins to be re-sourced, since the re-sourcing of this +" file will result in an attempt to redefine the function while it is running! +fu! s:Txtfmt_refresh() + " Ensure that common configuration code will not be skipped next time + unlet! b:txtfmt_did_common_config + " Determine whether txtfmt ftplugin is loaded + if exists('b:loaded_txtfmt') + " b:loaded_txtfmt is set only within ftplugin/txtfmt.vim and unlet by + " b:undo_ftplugin; hence, its existence indicates that txtfmt ftplugin + " is currently loaded. Cache the filetype that was cached at load + " time. + let l:current_filetype = b:txtfmt_filetype + endif + " Determine whether txtfmt syntax plugin is loaded + let v:errmsg = '' + silent! syn sync match Tf_existence_test grouphere Tf_fmt_1 /\%^/ + if v:errmsg == '' + " No error means txtfmt syntax plugin is loaded. Cache the syntax name + " that was cached at load time. + let l:current_syntax = b:txtfmt_syntax + endif + " Is there anything to refresh? + if !exists('l:current_filetype') && !exists('l:current_syntax') + echomsg "Warning: Useless call to Refresh: " + \."no txtfmt plugins appear to be loaded." + return + endif + " If here, there was a reason for the Txtfmt_refresh call. Cause ftplugin + " and/or syntax plugin to be reloaded via FileType and Syntax sets, as + " appropriate. + if exists('l:current_syntax') + " We're going to attempt to reload syntax plugin. Unload it now + " (causing b:current_syntax to be unlet). If we set filetype below, + " and b:current_syntax exists afterwards, we'll know syntax was loaded + " via syntaxset autocmd linked to FileType event. Alternatively, + " could simply unlet b:current_syntax here... + set syntax=OFF + endif + if exists('l:current_filetype') + " Set filetype to whatever it was before + exe 'set filetype=' . l:current_filetype + endif + if exists('l:current_syntax') + " Syntax may have been loaded already, but if not, we'll need to do it + " manually... + if exists('b:current_syntax') + " Syntax was loaded as a result of the filetype set. Make sure it + " appears to be the correct one. + if b:current_syntax != l:current_syntax + echomsg "Warning: Txtfmt attempted to restore syntax to `" + \.l:current_syntax."'. Result was `".b:current_syntax."'" + echomsg "I'm guessing you have loaded the txtfmt plugins " + \."in a non-standard manner. See txtfmt help for more information." + endif + else + " Syntax wasn't linked to filetype. Load the desired syntax manually. + exe 'set syntax=' . l:current_syntax + endif + endif +endfu +" >>> +endif " if exists('b:txtfmt_do_common_config') +" General-purpose utilities <<< +" Note: These utilities are defined globally in the plugin file so that they +" might be used by any of the Txtfmt script files. +" Naming Convention: All of these utilities should begin with 'TxtfmtUtil_' +" and should separate internal 'words' with underscore. Internal words should +" not be capitalized. +" Function: TxtfmtUtil_num_to_hex_str <<< +" Description: Take the input value and convert it to a hex string of the form +" 0xXXXX. +" Format Note: Output string will have '0x' prepended, but will omit leading +" zeroes. +fu! TxtfmtUtil_num_to_hex_str(num) + " Get writable copy + let num = a:num + " Loop until the value has been completely processed + let str = '' + let abcdef = "ABCDEF" + while num > 0 + let dig = num % 16 + " Convert the digit value to a hex digit and prepend to hex str + if dig <= 9 + let str = dig . str + else + let str = strpart(abcdef, dig - 10, 1) . str + endif + let num = num / 16 + endwhile + " Prepend '0x' to hex string built in loop + " Note: If string is empty, it should be '0' + return '0x' . (strlen(str) == 0 ? '0' : str) +endfu +" >>> +" >>> +" Function: s:MakeTestPage() <<< +" Purpose: Build a "test-page" in a scratch buffer, to show user how color +" and format regions will look with current definitions and on current +" terminal. (Useful to prevent user from having to insert all the color and +" format regions manually with text such as "Here's a little test...") +" How: Create a scratch buffer whose filetype is set to txtfmt. Add some +" explanation lines at the top, followed by one line for each active color, as +" follows: +" color none i b bi u ui ub ubi ... +" Repeat for each active background color... +" Note: The function is script-local, as it is designed to be invoked from a +" command. +" IMPORTANT NOTE: Special care must be taken when defining this function, as +" it creates a buffer with 'ft' set to txtfmt, which causes the script to be +" re-sourced. This leads to E127 'Cannot redefine function' when fu[!] is +" encountered, since the function is in the process of executing. +if !exists('*s:MakeTestPage') +fu! s:MakeTestPage(...) + if a:0 == 1 + " User provided optional modeline arguments. Before opening scratch + " buffer, make sure the modeline constructed from the arguments has at + " least the overall appearance of being valid. (Option name/value + " validation is performed only after opening the test page buffer.) + if !s:Is_txtfmt_modeline("\txtfmt:".a:1) + " Warn of invalid modeline and return without creating the test + " buffer + echoerr "Invalid arguments passed to :MakeTestPage command: `".a:1."'" + return + endif + endif + " Attempt to open the buffer (success not guaranteed) + if !s:New_window(s:TESTPAGE_WINHEIGHT) + " If E36, give helpful message about increasing size of current + " window; otherwise, given generic message followed by raw Vim + " error message. + if s:err_str =~ '^E36:' + echoerr "MakeTestPage(): Can't open test page. Increase size of current window and try again." + else + echoerr "MakeTestPage(): Error opening test page: " . s:err_str + endif + " Abort test page + return + endif + set buftype=nofile + set bufhidden=hide + set noswapfile + " The following setlocal is necessary to prevent E21 in the event that + " 'nomodifiable' is set globally. + setlocal modifiable + " If user provided modeline, add it to top of file before setting filetype + " to txtfmt... + if a:0 == 1 + let modeline = a:1 + if modeline =~ '\S' + call setline(1, "\txtfmt:".modeline) + endif + elseif a:0 > 1 + " This should never happen, since this function is called from a + " mapping. + echoerr "Too many arguments passed to MakeTestPage." + \." (Note that this function should not be called directly.)" + endif + set filetype=txtfmt + " Set page formatting options + " TODO - Decide whether the following are necessary anymore. (I'm + " formatting things explicitly now...) + set noai ts=4 sw=4 tw=78 + set nowrap + " Cache some special tokens that will be used on this page + let tok_fb = Txtfmt_GetTokStr('fb') + let tok_fui = Txtfmt_GetTokStr('fui') + let tok_fu = Txtfmt_GetTokStr('fu') + let tok_fmt_end = nr2char(b:txtfmt_fmt_first_tok) + let tok_clr_end = nr2char(b:txtfmt_clr_first_tok) + " Important Note: Most of the following logic assumes that each token that + " is hidden by a txtfmt concealment group will appear as a single + " whitespace. If the 'conceal' patch is in effect, however, such tokens + " will not appear at all. The problem is that the token width is sometimes + " used to achieve the desired alignment. To facilitate keeping the + " alignment constant, I declare a variable that resolves to a single + " whitespace if and only if the conceal patch is in effect. This variable + " will be appended to tokens that would affect alignment in the absence of + " the conceal patch. + " Note: The space could go before or after the token, but after is best in + " the case of bg colors. + let cncl_ws = b:txtfmt_cfg_conceal ? ' ' : '' + call append(line('$'), tok_fb) + call append(line('$'), + \"************************") + $center + call append(line('$'), + \"*** TXTFMT TEST PAGE ***") + $center + call append(line('$'), + \"************************") + $center + call append(line('$'), + \"=============================================================================") + call append(line('$'), + \"*** Overview ***") + $center + call append(line('$'), tok_fmt_end) + call append(line('$'), "") + call append(line('$'), + \"The purpose of this page is to present an overview of the txtfmt highlighting") + call append(line('$'), + \"that results from the global txtfmt options and any txtfmt modeline settings") + call append(line('$'), + \"passed to the MakeTestPage command.") + call append(line('$'), "") + call append(line('$'), + \" :help txtfmt-MakeTestPage") + call append(line('$'), "") + call append(line('$'), + \"The text on the page has been chosen to display all possible combinations of") + call append(line('$'), + \"color and format regions, and if applicable, to illustrate the escaping of") + call append(line('$'), + \"tokens and the nesting of txtfmt regions.") + call append(line('$'), tok_fb) + call append(line('$'), + \"=============================================================================") + call append(line('$'), + \"*** Colors and Formats ***") + $center + call append(line('$'), tok_fui) + " Possible TODO: Use b:txtfmt_cfg_tokrange so that number format specified + " by user is respected. + call append(line('$'), + \'Configuration:'.tok_fb.cncl_ws + \."tokrange =".tok_fmt_end.cncl_ws + \.b:txtfmt_cfg_starttok_display.b:txtfmt_cfg_formats_display + \) + call append(line('$'), "") + call append(line('$'), + \"\start token: ".b:txtfmt_cfg_starttok_display) + call append(line('$'), + \"\background colors: ".(b:txtfmt_cfg_bgcolor + \? "enabled (".b:txtfmt_cfg_numbgcolors." active)" + \: "disabled")) + call append(line('$'), + \"\".(b:txtfmt_cfg_longformats ? "'long'" : "'short'")." formats " + \.(b:txtfmt_cfg_longformats + \ ? + \ (b:txtfmt_cfg_undercurl + \ ? "with" + \ : "without") + \ ." undercurl" + \ : + \ "" + \ )) + call append(line('$'), '') + " TODO_BG: Decide whether to attempt to be more discriminating: e.g., what + " if bgcolor is enabled, but none are active? Same question for fgcolor? + " Decision: I'm thinking there's no reason to do it. Typically, user won't + " de-activate all colors, but if he does, perhaps we want him to scratch + " his head a bit. + if b:txtfmt_cfg_bgcolor + call append(line('$'), + \"Each line in the table below corresponds to a single permutation of foreground") + call append(line('$'), + \"and background colors. You may use the special global arrays g:txtfmtColor{}") + call append(line('$'), + \"and g:txtfmtBgcolor{} to change these colors.") + else + call append(line('$'), + \"Each line in the table below corresponds to a single foreground color. You may") + call append(line('$'), + \"use the special global array g:txtfmtColor{} to change these colors.") + endif + call append(line('$'), '') + call append(line('$'), + \' :help txtfmt-defining-colors') + call append(line('$'), '') + call append(line('$'), + \"The ".b:txtfmt_num_formats." permutations of the format attributes ") + call append(line('$'), + \'(u=underline, b=bold, i=italic' + \.(b:txtfmt_cfg_longformats + \ ? ', s=standout, r=reverse' + \ .b:txtfmt_cfg_undercurl + \ ? ', c=undercurl' + \ : '' + \ : '' + \ ).')') + call append(line('$'), "are shown on each color line for completeness.") + call append(line('$'), tok_fb) + call append(line('$'), + \"IMPORTANT NOTE:".tok_fmt_end."Txtfmt chooses a default range for clr/fmt tokens, which works") + call append(line('$'), + \"well on most terminals; however, this range may not be suitable for all") + call append(line('$'), + \"terminals. In particular, Vim cannot highlight those characters displayed by") + call append(line('$'), + \"the terminal as special 2-character sequences (e.g., ^M, ^B, etc...). Although") + call append(line('$'), + \"coloring and formatting of text will work when these characters are used as") + call append(line('$'), + \"tokens, their use is discouraged, because txtfmt is unable to conceal them. If") + call append(line('$'), + \"any such control sequences are visible in the sample text below, you may wish") + call append(line('$'), + \"to try a different range, either by setting global txtfmt option") + call append(line('$'), + \"g:txtfmtTokrange to a different value, or by including a different definition") + call append(line('$'), + \"in a txtfmt modeline string passed to the MakeTestPage command. Either way, you") + call append(line('$'), + \"will need to invoke MakeTestPage again to see whether the changed settings are") + call append(line('$'), + \"better.") + call append(line('$'), "") + call append(line('$'), + \" :help txtfmt-choosing-token-range") + call append(line('$'), '') + call append(line('$'), tok_fb) + call append(line('$'), + \'--color/format table--') + call append(line('$'), tok_fmt_end) + + " Determine line on which to start the fmt/clr table + let iLine = line('.') + " Before looping over bgc, fgc and fmt, determine the length of the list + " of format specs (i.e., the number of characters, including start fmt + " specs, from the hyphen to the end of the line). + " Assumption: Each format token will take up a single character width. (If + " conceal patch is in effect, it will be a literal space.) + " Note: We don't include either the 'no format' token at the end of the + " line or the following space (used for table framing) in the count, as + " these characters are beyond the edge of the table proper, and we want + " them to extend beyond the end of the underscores. + let post_hyphen_width = 1 " hyphen representing 'no fmt' + let iFmt = 1 " start just after 'no format' token + while iFmt < b:txtfmt_num_formats + " Accumulate width of space and fmt spec + let post_hyphen_width = post_hyphen_width + 1 + strlen(b:ubisrc_fmt{iFmt}) + let iFmt = iFmt + 1 + endwhile + " Define width of lines up to the hyphen, *NOT* including potentially + " multibyte token chars that appear at the beginning of the line. A fixed + " number of columns will be reserved for such tokens. + " Note: This width is chosen with the string 'no bg color' in mind + let pre_hyphen_width = 16 + " Generate a string of underscores that spans the table (but not the + " framing whitespace/tokens at left and right edges) + let underscores = s:Repeat('_', pre_hyphen_width + post_hyphen_width) + " Put the text into the buffer + " Outer loop is over background colors + " Note: piBgc in the loop below is a 1-based index into + " b:txtfmt_cfg_bgcolor{}, the array of active color indices. This array + " stores the actual color index corresponding to the piBgc'th active + " color; i.e., it stores the indices that are used for the b:txtfmt_bgc{} + " array. Both arrays are 1-based. Index 0 represents the default (no) + " color token in b:txtfmt_bgc{}. + " Note: Even if bgc is disabled, we'll iterate once for default background + let piBgc = 0 + while piBgc <= (b:txtfmt_cfg_bgcolor ? b:txtfmt_cfg_numbgcolors : 0) + " Get the actual color index via one level of indirection + let iBgc = piBgc == 0 ? 0 : b:txtfmt_cfg_bgcolor{piBgc} + " Don't output the bg color title if bgcolor is disabled + if b:txtfmt_cfg_bgcolor + " Put a line consisting entirely of underscores before the bg + " color title line + " Note: If this is not the default bg color, a bg token and + " possibly a default fg token will need to be prepended. + if iBgc == 0 + " Default bg color + let s = ' ' + else + " Non-default bg color + let s = nr2char(b:txtfmt_bgc_first_tok + iBgc).cncl_ws + if b:txtfmt_cfg_numfgcolors + " We're currently inside a fg clr region, but bg color + " title line should be default fg color, so end the fg + " color + let s = s.nr2char(b:txtfmt_clr_first_tok).cncl_ws + else + " No fg clr region to end, but need space for alignment + let s = s.' ' + endif + endif + " Now append the underscores and a 2-space end of line pad + let s = s . underscores . ' ' + call append(line('$'), s) + " Insert a description of the current bg color + if iBgc == 0 + let s = " no bg color" + else + let s = " Bg color ".iBgc + endif + " Append spaces such that background coloration is as wide as it is on + " subsequent lines. + " Note: The hardcoded 4 is for the beginning and end of line framing spaces + " Note: s cannot contain multibyte chars at this point, so the + " strlen() is safe. + let s = s . s:Repeat(' ', pre_hyphen_width + post_hyphen_width + 4 - strlen(s)) + call append(line('$'), s) + " Put a line consisting entirely of underscores after the bg color + " title line + call append(line('$'), ' ' . underscores . ' ') + endif + " Note: See notes on piBgc and iBgc above for description of piClr and + " iClr. + let piClr = 0 + while piClr <= b:txtfmt_cfg_numfgcolors + " Get the actual color index via one level of indirection + let iClr = piClr == 0 ? 0 : b:txtfmt_cfg_fgcolor{piClr} + " Build the string for this line, taking care to ensure there is a + " margin of 2 space widths + " Note: Need to keep beginning of line spaces/tokens separate + " until after the strlen(), since strlen counts characters rather + " than bytes. + if iClr == 0 + let ldr = ' ' + let s = "no fg color" + else + " Insert the non-default fg clr token, preceded by a space in + " the column dedicated to bg clr tokens + let ldr = ' '.nr2char(b:txtfmt_clr_first_tok + iClr).cncl_ws + let s = 'Fg color '.iClr + endif + " Pad with spaces till hyphen + let s = ldr . s . s:Repeat(' ', pre_hyphen_width - strlen(s)) + " Loop over format attributes + let iFmt = 0 + while iFmt < b:txtfmt_num_formats + if iFmt == 0 + let s = s.'-' + else + " Conceal patch entails special handling to prevent the + " space between the specifiers from being underlined or + " undercurled. + " Case 1: 'conceal' + " + " Case 2: 'noconceal' + " + " Note: For the 'noconceal' case *only*, a single + " goes outside loop. + let s = s . cncl_ws + \. nr2char(b:txtfmt_fmt_first_tok + iFmt) + \. b:ubisrc_fmt{iFmt} + \. (b:txtfmt_cfg_conceal ? nr2char(b:txtfmt_fmt_first_tok) : '') + endif + let iFmt = iFmt + 1 + endwhile + " If necessary, add default fmt token to prevent formatting from + " spilling onto next line, and add space(s) for margin + " Case 1: 'conceal' + " + " Case 2: 'noconceal' + " + let s = s . (b:txtfmt_cfg_conceal ? ' ' : nr2char(b:txtfmt_fmt_first_tok)) . ' ' + call append(line('$'), s) + let piClr = piClr + 1 + endwhile + let piBgc = piBgc + 1 + endwhile + " Return to default background and foreground colors (as applicable) + " TODO: If 'conceal', then this has been done already. + call append(line('$'), + \(b:txtfmt_cfg_bgcolor && b:txtfmt_cfg_numbgcolors > 0 ? nr2char(b:txtfmt_bgc_first_tok) : '') + \.(b:txtfmt_cfg_numfgcolors > 0 ? nr2char(b:txtfmt_clr_first_tok) : '')) + + call append(line('$'), tok_fb) + call append(line('$'), + \"=============================================================================") + call append(line('$'), + \"*** Escaping txtfmt tokens ***") + $center + call append(line('$'), tok_fui) + call append(line('$'), + \'Configuration:'.tok_fb.cncl_ws."escape".tok_fmt_end.cncl_ws."= ".b:txtfmt_cfg_escape) + call append(line('$'), "") + call append(line('$'), + \" :help txtfmt-escape") + " Now display text specific to the option setting + if b:txtfmt_cfg_escape != 'none' + call append(line('$'), tok_fb) + call append(line('$'), + \'--Escaping tokens outside a region--'.tok_fmt_end) + call append(line('$'), + \"The following shows that all tokens (other than the \"no fmt\" / \"no clr\" tokens)") + call append(line('$'), + \"may be escaped to prevent them from beginning a region:") + " Escaped fg color tokens + call append(line('$'), '') + call append(line('$'), tok_fb.cncl_ws + \.'*'.(b:txtfmt_cfg_bgcolor ? 'fg ' : '').'color tokens*'.tok_fmt_end) + " Loop over all clr tokens, inserting an escaped version of each. + let s = '' + let piClr = 1 + while piClr <= b:txtfmt_cfg_numfgcolors + let iClr = b:txtfmt_cfg_fgcolor{piClr} + let tok = nr2char(b:txtfmt_clr_first_tok + iClr) + let s = s.cncl_ws.(b:txtfmt_cfg_escape == 'self' ? tok : '\').tok + let piClr = piClr + 1 + endwhile + if s == '' + " Indicate that no fg colors are active + let s = ' --N/A--' + endif + call append(line('$'), s) + " Escaped bg color tokens + if b:txtfmt_cfg_bgcolor + call append(line('$'), tok_fb.cncl_ws + \.'*bg color tokens*'.tok_fmt_end) + " Loop over all bgc tokens, inserting an escaped version of each. + let s = '' + let piBgc = 1 + while piBgc <= b:txtfmt_cfg_numbgcolors + let iBgc = b:txtfmt_cfg_bgcolor{piBgc} + let tok = nr2char(b:txtfmt_bgc_first_tok + iBgc) + let s = s.cncl_ws.(b:txtfmt_cfg_escape == 'self' ? tok : '\').tok + let piBgc = piBgc + 1 + endwhile + if s == '' + " Indicate that no bg colors are active + let s = ' --N/A--' + endif + call append(line('$'), s) + endif + " Escaped format tokens + call append(line('$'), tok_fb.cncl_ws + \.'*format tokens*'.tok_fmt_end) + " Loop over all fmt tokens, inserting an escaped version of each. + let s = '' + let iFmt = 1 + while iFmt < b:txtfmt_num_formats + let tok = nr2char(b:txtfmt_fmt_first_tok + iFmt) + let s = s.cncl_ws.(b:txtfmt_cfg_escape == 'self' ? tok : '\').tok + let iFmt = iFmt + 1 + endwhile + call append(line('$'), s) + call append(line('$'), tok_fb) + call append(line('$'), + \'--Escaping tokens inside a region--'.tok_fui) + call append(line('$'), '') + call append(line('$'), + \"Here's a little swatch of \"underline, italic\" text. On the line below are some") + call append(line('$'), + \"escaped tokens, which, in their unescaped form, would alter the region's") + call append(line('$'), + \"formatting:") + call append(line('$'), + \(b:txtfmt_cfg_escape == 'self' ? tok_fb : '\').tok_fb + \.' (escaped bold token), ' + \.(b:txtfmt_cfg_escape == 'self' ? tok_fmt_end : '\').tok_fmt_end + \.' (escaped "no fmt" token)') + call append(line('$'), + \"As you can see, the escaping characters are concealed, and the formatting is") + call append(line('$'), + \"unaffected by the escaped tokens.") + call append(line('$'), + \"Note: After you have viewed the rest of this page, you may wish to experiment") + call append(line('$'), + \"by removing the escape tokens to see how the formatting is affected.") + else + " Inform user that escaping is not configured + call append(line('$'), '') + call append(line('$'), + \"Escaping of txtfmt tokens is currently disabled.") + endif + + call append(line('$'), tok_fb) + call append(line('$'), + \"=============================================================================") + call append(line('$'), + \"*** Nesting txtfmt regions ***") + $center + call append(line('$'), tok_fui) + call append(line('$'), + \'Configuration:'.tok_fb.cncl_ws.(b:txtfmt_cfg_nested ? "nested" : "nonested").tok_fmt_end) + call append(line('$'), "") + call append(line('$'), + \" :help txtfmt-nesting") + " Now display text specific to the option setting + if b:txtfmt_cfg_nested + call append(line('$'), '') + call append(line('$'), + \"/* Here's a sample comment (italicized), intended to illustrate the nesting of") + call append(line('$'), + \" * txtfmt regions within non-txtfmt regions.") + call append(line('$'), + \" *") + call append(line('$'), + \" * The following txtfmt token -->".tok_fu."<-- begins a nested \"underline\" region, which") + call append(line('$'), + \" * ends with the following \"no fmt\" token. -->".tok_fmt_end."<--") + call append(line('$'), + \" * As you can see, the comment resumes automatically after the nested region") + call append(line('$'), + \" * ends. */") + call append(line('$'), "") + call append(line('$'), + \"Non-txtfmt regions may be divided into two categories: those with the") + call append(line('$'), + \"'keepend' attribute, and those without it. To demonstrate the effect of the") + call append(line('$'), + \"'keepend' attribute on nested txtfmt regions, I have defined two additional") + call append(line('$'), + \"regions, enclosed by curly braces and square brackets respectively. The curly") + call append(line('$'), + \"brace region does not have the 'keepend' attribute; the square bracket region") + call append(line('$'), + \"does. Both regions are highlighted in bold.") + call append(line('$'), + \"{ Once again, here's a".tok_fu.cncl_ws."nested \"underline\" txtfmt region, followed by a curly") + call append(line('$'), + \"brace. }") + call append(line('$'), + \"As you can see, the nested txtfmt region was *not* terminated by the") + call append(line('$'), + \"closing curly brace. In fact, the curly brace region was extended by the") + call append(line('$'), + \"txtfmt region. Notice how the following txtfmt \"no fmt\" token -->".tok_fmt_end."<--") + call append(line('$'), + \"permits the resumption of the curly brace region}, which is finally ended by") + call append(line('$'), + \"the unobscured closing curly brace.") + call append(line('$'), + \"[ Notice, by contrast, how both the".tok_fu.cncl_ws."nested \"underline\" txtfmt region and the") + call append(line('$'), + \"square bracket region itself are terminated by the following square bracket ]") + call append(line('$'), + \"because the square bracket region was defined with the 'keepend' attribute.") + + + " Define comment, curly brace, and square brace regions... + syn region Tf_example_comment start=+/\*+ end=+\*/+ keepend + hi Tf_example_comment cterm=italic gui=italic + syn region Tf_example_curly start=+{+ end=+}+ + hi Tf_example_curly cterm=bold gui=bold + syn region Tf_example_square start=+\[+ end=+\]+ keepend + hi Tf_example_square cterm=bold gui=bold + else + " Inform user that nesting is not configured + call append(line('$'), "") + call append(line('$'), + \"Nesting of txtfmt regions is currently disabled.") + endif + +endfu +endif " if !exists('*s:MakeTestPage') +" >>> +" Public-interface commands <<< +" TODO - Add this command to undo list - Should it redefine (com!)? +com! -nargs=? MakeTestPage call s:MakeTestPage() +" >>> + " vim: sw=4 ts=4 foldmethod=marker foldmarker=<<<,>>> : diff --git a/txtfmt/syntax/txtfmt.vim b/txtfmt/syntax/txtfmt.vim new file mode 100755 index 0000000..e78cb11 --- /dev/null +++ b/txtfmt/syntax/txtfmt.vim @@ -0,0 +1,2262 @@ +" Txtfmt: Set of Vim plugins (syntax, ftplugin, plugin) for creating and +" displaying formatted text with Vim. +" File: This is the txtfmt syntax file +" Creation: 2004 Nov 06 +" Last Change: 2010 Sep 04 +" Maintainer: Brett Pershing Stahlman +" License: This file is placed in the public domain. +" Let the common code know whether this is syntax file or ftplugin + +let s:script_name = 'syntax' +" Constants <<< +" >>> +" Common Configuration <<< +" Note: No point in having the modeline and/or global options processed by +" both the syntax and ftplugin files. +" IMPORTANT: Everything inside the "Common Configuration" fold should be +" identical between the syntax and ftplugin files. Keep in sync as changes are +" made... +if !exists('b:txtfmt_did_common_config') + let b:txtfmt_did_common_config = 1 + " Ensure that the code within plugin/txtfmt.vim will be executed when the + " file is sourced. + let b:txtfmt_do_common_config = 1 + " TODO - Should we ensure that warning occurs for missing file? + runtime plugin/txtfmt.vim + " Make sure the common config doesn't run again + unlet b:txtfmt_do_common_config + +endif +" >>> +" Config <<< +" Needed only for syntax file +" Note: Performed after the Common Configuration, which sets the 'escape' +" option, needed when defining syntax +" Function: s:Do_config() <<< +" Purpose: Do configuration required only for syntax file. +" Assumption: Common config has already been performed, so that needed options +" have been set. +fu! s:Do_config() + " Nothing to do here now that skip def is defined in Define_syntax + " TODO... +endfu +" >>> +call s:Do_config() +" >>> +" Function: s:Winrestcmd() <<< +" Purpose: Works just like Vim's winrestcmd, which was added in Vim 6.3, +" (and therefore, is not something I want to rely upon). Returns a +" string that can be :execute'd to restore the current window layout, +" assuming exactly the same set of windows exist when the string is +" exe'd. +" Note: winrestcmd() format looks like this: +" 1resize |vert 1resize | ... Nresize |vert Nresize | +" Note how the final element is terminated by a vertical bar. +" Testing: Verified for non-trivial window layout that winrestcmd() and +" this function return the same string. +fu! s:Winrestcmd() + let i = 1 + let N = winnr('$') + let cmd = '' + while i <= N + let cmd = cmd . i . 'resize ' . winheight(i) . '|vert ' . i . 'resize ' . winwidth(i) . '|' + let i = i + 1 + endwhile + return cmd +endfu +" >>> +" Function: s:Create_scratch_buffer() <<< +" Purpose: Create an empty scratch buffer in the current window, hiding +" the current buffer. +" Assumption: The hidden buffer will be restored by a call to +" s:Cleanup_scratch_buffer() later. +" Vim Idiosyncrasy: Special care must be taken when the current window +" contains an empty, [No Name] buffer; in that case, :hide enew will, by +" default, reuse that buffer for the newly created one (discarding any +" existing buf-local variables). This is problematic, since we need to +" return to the original buffer when we're finished with the scratch +" buffer. Note that in some cases, Txtfmt options may already have been +" stored to buf-local variables in the empty, [No Name] buffer before +" this function is called. +" Solution: Bram suggested adding a blank line to the original buffer to +" ensure it isn't reused. The blank line can be removed in the +" s:Cleanup_scratch_buffer() function. +" Note: Before this function was added, the scratch buffer was created +" in a new window. The problem with that approach was that it fails when +" there's no room to create another window. +fu! s:Create_scratch_buffer() + " See whether the current buffer is an empty, unnamed buffer that + " will be discarded by the :hide enew below. + if line('$') == 1 && col('$') == 1 && bufname('%') == '' + " Current buffer is an empty, unnamed buffer + " To prevent its being discarded by :hide enew, add a blank + " line, which we'll remove in the associated cleanup function + " Make sure the buffer is modifiable, taking care to save and restore + " current setting. + let modifiable_save = &l:modifiable + setlocal modifiable + call append(1, '') + let &l:modifiable = modifiable_save + let s:Added_blank_line_to_empty_buffer = 1 + endif + " Create the scratch buffer + hide enew + set buftype=nofile + set bufhidden=wipe + set noswapfile + " The following setlocal is necessary to prevent E21 in the event that + " 'nomodifiable' is set globally. + setlocal modifiable +endfu +" >>> +" Function: s:Cleanup_scratch_buffer() <<< +" Purpose: Wipe out the scratch buffer in the current window, restoring +" the buffer it supplanted. +" Assumption: s:Create_scratch_buffer() was called to create the scratch +" buffer in the current window. +fu! s:Cleanup_scratch_buffer() + " Return to the buffer that was current when the associated create + " function was called + " Note: The scratch buffer 'bufhidden' option will ensure that it's + " bwipe'd + buffer # + if exists('s:Added_blank_line_to_empty_buffer') + unlet s:Added_blank_line_to_empty_buffer + " Get rid of the blank line we added in the associated create + " function. + " Note: Make sure the buffer is modifiable, taking care to save and + " restore current setting. + let modifiable_save = &l:modifiable + setlocal modifiable + undo + let &l:modifiable = modifiable_save + endif +endfu +" >>> +" Function: s:Is_match_offset_char_based() <<< +" Purpose: Return nonzero if and only if the this version of Vim treats match +" offsets as character offsets. +" Assumption: Current encoding is multi-byte +fu! s:Is_match_offset_char_based() + let s = "AB" . nr2char(0x100) . 'C' + " Set lazyredraw to ensure user never sees the buffer we create + let lazyredraw_save = &lazyredraw + set lazyredraw + " Create a scratch buffer in the current window + call s:Create_scratch_buffer() + " Put the test string at the head of the new scratch buffer + call setline(1, s) + " Create syntax region that will include the last character on the line if + " and only if this Vim treats match offsets as char offsets + syn match Tf_Test /AB/me=e+2,he=e+2 + " Is the last char in the Tf_Test syntax group? + if synIDattr(synID(line("."), col("$") - 1, 1), "name") == 'Tf_Test' + let off_is_char = 1 + else + let off_is_char = 0 + endif + " Clean up the scratch buffer, returning to the previous buffer + call s:Cleanup_scratch_buffer() + " Restore old lazyredraw setting + if !lazyredraw_save + set nolazyredraw + endif + " Return true if and only if offsets are char-based + return off_is_char +endfu +" >>> +" Function: s:Get_bytes_per_token() <<< +" Purpose: Return the number of bytes used to encode the first Txtfmt token, +" warning user if this number is different from the number of bytes used to +" encode the last. +" Assumption: # of bytes per token will never decrease as character codes +" increase +fu! s:Get_bytes_per_token() + let num_bytes = strlen(nr2char(b:txtfmt_clr_first_tok)) + " Make sure first and last token comprise the same number of bytes, + " and warn user if not... + if num_bytes != strlen(nr2char(b:txtfmt_fmt_last_tok)) + " Note: Txtfmt highlighting will probably be incorrect, but there's + " not much we can do about it other than warn user... + echohl WarningMsg + echomsg "Warning! The 'tokrange' setting you have chosen may not work correctly" + echomsg "because not all tokens within the range are encoded with the same number" + echomsg "of bytes. If fmt/clr regions do not display correctly, you should either" + echomsg "choose a different 'tokrange', or apply the multi-byte patch included with" + echomsg "the plugin." + echohl Comment + echomsg " :help txtfmt-choosing-token-range" + echohl MoreMsg + echomsg "Hit any key to continue..." + echohl None + call getchar() + endif + " Return # of bytes used by first token + return num_bytes +endfu +" >>> +" Function: s:Define_syntax() <<< +fu! s:Define_syntax() + " Define a convenience flag that indicates whether background colors are + " in effect + let bgc_enabled = b:txtfmt_cfg_bgcolor && b:txtfmt_cfg_numbgcolors > 0 + let clr_enabled = b:txtfmt_cfg_numfgcolors > 0 + + " cui (color uniqueness index) will contain a different index for each + " color configuration (and will be empty string in the unlikely event that + " both numfgcolors and numbgcolors are 0 - i.e., no colors used) + " Note: This strategy is necessary because Vim's highlight groups are not + " buffer-specific, but there is a buffer-specific version of txtfmtColor{} + let cui = b:txtfmt_color_uniq_idx + + " Concealment group <<< + " Create a concealment highlight group, to which others can link + " The Ignore group is a preferred group, defined in distributed + " syncolor.vim + " IMPORTANT NOTE: Some of the distributed colorschemes DO NOT hide text in + " the Ignore group. I disagree with this practice, and have posted to the + " Vim list on the subject, but the situation is unlikely to change... + " Fortunately, there is a workaround that always works for the GUI,and + " sometimes works for a cterm. + " Workaround: *Attempt* to define fg=bg. This will always work for the + " GUI, and will work for a cterm if the colorscheme has defined ctermbg + " for the Normal group. If the attempt fails, simply link to Ignore group, + " which may or may not hide text. + if has('gui_running') + hi Tf_conceal guifg=bg + else + let v:errmsg = "" + silent! hi Tf_conceal ctermfg=bg + if v:errmsg != "" + " Link to Ignore and put suggestions in help file for users of + " colorschemes that don't hide Ignore'd text. + hi link Tf_conceal Ignore + endif + endif + " Check for existence of 'conceal' patch (and desire on part of user to + " use it) + if b:txtfmt_cfg_conceal + " Note: 'conceallevel' and 'concealcursor' are window-local + setl conceallevel=3 + let &l:concealcursor = b:txtfmt_cfg_concealcursor + let concealends = ' concealends' + let conceal = ' conceal' + else + let concealends = '' + let conceal = '' + endif + " >>> + " Define match offset that corresponds to a single txtfmt token <<< + " Note: This is required because of a Vim bug: as of Vim 7.1, syntax match + " offsets are always treated as byte offsets, though the documentation + " suggests offsets are char-based. There is a patch floating around, + " however, which fixes this; also, Vim 7.2 *may* fix it; thus, it's not + " safe to assume anything about whether the running Vim uses byte or char + " offsets. If necessary, Is_match_offset_char_based will find out. + " Note: Eventually, the if and first elseif can be combined, but for now, + " I want to set b:txtfmt_dbg_syn_off as a debug var, in case any users + " experience problems... + if b:txtfmt_cfg_enc_class == '1' + let tok_off = 1 + " Nonexistence of b:txtfmt_dbg_syn_off indicates + " Is_match_offset_char_based wasn't run + unlet! b:txtfmt_dbg_syn_off + silent elseif s:Is_match_offset_char_based() + let tok_off = 1 + let b:txtfmt_dbg_syn_off = 'char' + else + " Offsets are measured in bytes; hence, we need to determine how many + " bytes per token + let tok_off = s:Get_bytes_per_token() + let b:txtfmt_dbg_syn_off = 'byte' + endif + " >>> + " 'containedin' list (option dependent) <<< + if b:txtfmt_cfg_nested + " Ensure that txtfmt top-level item can be contained by a non-txtfmt + " syntax group (e.g. C-language comment). + " TODO - Perhaps put inner_esc groups into a cluster. + if b:txtfmt_cfg_escape != 'none' + let containedin_def = ' containedin=ALLBUT,@Tf'.cui.'_all,Tf_def_tok' + \.',Tf_outer_esc,Tf_any_stok_inner_esc' + \.(clr_enabled ? ',Tf_clr_etok_inner_esc' : '') + \.(bgc_enabled ? ',Tf_bgc_etok_inner_esc' : '').',Tf_fmt_etok_inner_esc' + else + let containedin_def = ' containedin=ALLBUT,@Tf'.cui.'_all,Tf_def_tok' + endif + else + let containedin_def = '' + endif + " >>> + " Define special default token group <<< + " This group ensures that 'no clr', 'no bgc', and 'no fmt' tokens at top + " level (including those that end a clr only, bgc only, or fmt only group) + " will be concealed. + " Order note: This region is defined here to ensure that it has a lower + " priority than any of the other txtfmt regions that can be begun by a + " default token. + exe 'syn match Tf_def_tok /['.b:txtfmt_re_any_etok_atom.']/ contained'.conceal + hi link Tf_def_tok Tf_conceal + " >>> + " Choose cterm or gui versions of color and format assignments + if has('gui_running') + let eq_clr = ' guifg=' + let eq_bgc = ' guibg=' + let eq_fmt = ' gui=' + else + let eq_clr = ' ctermfg=' + let eq_bgc = ' ctermbg=' + let eq_fmt = ' cterm=' + endif + " NOTES TO PRESERVE + " TODO - Decide whether the inner_esc regions obviate the need for the + " skip-def. (I don't think so...) + " UNDER CONSTRUCTION + + " Create background-color specific concealment groups + " Note: Use cui to ensure that different buffers could have different sets + " of background colors in effect + " Loop over active colors only (with the aid of index indirection array) + let pi = 1 + while pi <= (b:txtfmt_cfg_bgcolor ? b:txtfmt_cfg_numbgcolors : 0) + let i = b:txtfmt_cfg_bgcolor{pi} + exe 'hi Tf'.cui.'_conceal_'.i.' '.eq_bgc.b:txtfmt_bgc{i}.eq_clr.b:txtfmt_bgc{i} + let pi = pi + 1 + endwhile + " Create vars to facilitate switching between normal (toplevel) + " concealment group and background-color-specific groups + let matchgroup_top_def = ' matchgroup=Tf_conceal' + let matchgroup_def = matchgroup_top_def + + " Important Note: The following line may be executed on the Vim command + " line to regenerate the code within the BEGIN...<<< / END...>>> markers. + " The script generating the code is a perl script appended to the end of + " this file: specifically, between the `#!perl' and the __END__ marker. + " :0/BEGIN AUTOGENERATED CODE BLOCK <\{3}/;/END AUTOGENERATED CODE BLOCK >\{3}/d|exe '.-1r !perl -x %'|exe "norm '[" + + " BEGIN AUTOGENERATED CODE BLOCK <<< + " Last update: Sun Sep 27 16:16:52 2009 + + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + "=== + "*** clr + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_clr= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_clr_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_clr = ' skip=/\\./' + else + let skip_clr = ' skip=/\(.\)\1/' + endif + else + let contains_clr = '' + let skip_clr = '' + endif + let end_clr = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_clr_etok_atom + \.']/me=e-'.tok_off + let r1_clr = + \skip_clr + \.contains_clr + \.end_clr + \.containedin_def + let start_clr = + \' start=/[' + \.(bgc_enabled ? b:txtfmt_re_bgc_etok_atom : '').b:txtfmt_re_fmt_etok_atom + \.']/' + let r2_clr = + \skip_clr + \.contains_clr + \.start_clr + \.end_clr + \.' contained' + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + "=== + "*** clr-bgc + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_clrbgc= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_clr_etok_inner_esc,' + \.'Tf_bgc_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_clrbgc = ' skip=/\\./' + else + let skip_clrbgc = ' skip=/\(.\)\1/' + endif + else + let contains_clrbgc = '' + let skip_clrbgc = '' + endif + let end_clrbgc = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_clr_etok_atom + \.b:txtfmt_re_bgc_etok_atom + \.']/me=e-'.tok_off + let r1_clrbgc = + \skip_clrbgc + \.contains_clrbgc + \.end_clrbgc + \.' contained' + let start_clrbgc = + \' start=/[' + \.b:txtfmt_re_fmt_etok_atom + \.']/' + let r2_clrbgc = + \skip_clrbgc + \.contains_clrbgc + \.start_clrbgc + \.end_clrbgc + \.' contained' + "=== + "*** clr-bgc-fmt + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_clrbgcfmt= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_clr_etok_inner_esc,' + \.'Tf_bgc_etok_inner_esc,' + \.'Tf_fmt_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_clrbgcfmt = ' skip=/\\./' + else + let skip_clrbgcfmt = ' skip=/\(.\)\1/' + endif + else + let contains_clrbgcfmt = '' + let skip_clrbgcfmt = '' + endif + let end_clrbgcfmt = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_clr_etok_atom + \.b:txtfmt_re_bgc_etok_atom + \.b:txtfmt_re_fmt_etok_atom + \.']/me=e-'.tok_off + let r1_clrbgcfmt = + \skip_clrbgcfmt + \.contains_clrbgcfmt + \.end_clrbgcfmt + \.' contained' + " Revert to toplevel (no background color) matchgroup + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + "=== + "*** clr-fmt + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_clrfmt= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_clr_etok_inner_esc,' + \.'Tf_fmt_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_clrfmt = ' skip=/\\./' + else + let skip_clrfmt = ' skip=/\(.\)\1/' + endif + else + let contains_clrfmt = '' + let skip_clrfmt = '' + endif + let end_clrfmt = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_clr_etok_atom + \.b:txtfmt_re_fmt_etok_atom + \.']/me=e-'.tok_off + let r1_clrfmt = + \skip_clrfmt + \.contains_clrfmt + \.end_clrfmt + \.' contained' + " Define the r2 region vars if and only if at least one of the + " region types whose end token could begin this region is active + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + let start_clrfmt = + \' start=/[' + \.b:txtfmt_re_bgc_etok_atom + \.']/' + let r2_clrfmt = + \skip_clrfmt + \.contains_clrfmt + \.start_clrfmt + \.end_clrfmt + \.' contained' + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + "=== + "*** clr-fmt-bgc + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_clrfmtbgc= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_clr_etok_inner_esc,' + \.'Tf_fmt_etok_inner_esc,' + \.'Tf_bgc_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_clrfmtbgc = ' skip=/\\./' + else + let skip_clrfmtbgc = ' skip=/\(.\)\1/' + endif + else + let contains_clrfmtbgc = '' + let skip_clrfmtbgc = '' + endif + let end_clrfmtbgc = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_clr_etok_atom + \.b:txtfmt_re_fmt_etok_atom + \.b:txtfmt_re_bgc_etok_atom + \.']/me=e-'.tok_off + let r1_clrfmtbgc = + \skip_clrfmtbgc + \.contains_clrfmtbgc + \.end_clrfmtbgc + \.' contained' + " Revert to toplevel (no background color) matchgroup + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + "=== + "*** bgc + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_bgc= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_bgc_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_bgc = ' skip=/\\./' + else + let skip_bgc = ' skip=/\(.\)\1/' + endif + else + let contains_bgc = '' + let skip_bgc = '' + endif + let end_bgc = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_bgc_etok_atom + \.']/me=e-'.tok_off + let r1_bgc = + \skip_bgc + \.contains_bgc + \.end_bgc + \.containedin_def + let start_bgc = + \' start=/[' + \.(clr_enabled ? b:txtfmt_re_clr_etok_atom : '').b:txtfmt_re_fmt_etok_atom + \.']/' + let r2_bgc = + \skip_bgc + \.contains_bgc + \.start_bgc + \.end_bgc + \.' contained' + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + "=== + "*** bgc-clr + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_bgcclr= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_bgc_etok_inner_esc,' + \.'Tf_clr_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_bgcclr = ' skip=/\\./' + else + let skip_bgcclr = ' skip=/\(.\)\1/' + endif + else + let contains_bgcclr = '' + let skip_bgcclr = '' + endif + let end_bgcclr = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_bgc_etok_atom + \.b:txtfmt_re_clr_etok_atom + \.']/me=e-'.tok_off + let r1_bgcclr = + \skip_bgcclr + \.contains_bgcclr + \.end_bgcclr + \.' contained' + let start_bgcclr = + \' start=/[' + \.b:txtfmt_re_fmt_etok_atom + \.']/' + let r2_bgcclr = + \skip_bgcclr + \.contains_bgcclr + \.start_bgcclr + \.end_bgcclr + \.' contained' + "=== + "*** bgc-clr-fmt + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_bgcclrfmt= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_bgc_etok_inner_esc,' + \.'Tf_clr_etok_inner_esc,' + \.'Tf_fmt_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_bgcclrfmt = ' skip=/\\./' + else + let skip_bgcclrfmt = ' skip=/\(.\)\1/' + endif + else + let contains_bgcclrfmt = '' + let skip_bgcclrfmt = '' + endif + let end_bgcclrfmt = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_bgc_etok_atom + \.b:txtfmt_re_clr_etok_atom + \.b:txtfmt_re_fmt_etok_atom + \.']/me=e-'.tok_off + let r1_bgcclrfmt = + \skip_bgcclrfmt + \.contains_bgcclrfmt + \.end_bgcclrfmt + \.' contained' + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + "=== + "*** bgc-fmt + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_bgcfmt= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_bgc_etok_inner_esc,' + \.'Tf_fmt_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_bgcfmt = ' skip=/\\./' + else + let skip_bgcfmt = ' skip=/\(.\)\1/' + endif + else + let contains_bgcfmt = '' + let skip_bgcfmt = '' + endif + let end_bgcfmt = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_bgc_etok_atom + \.b:txtfmt_re_fmt_etok_atom + \.']/me=e-'.tok_off + let r1_bgcfmt = + \skip_bgcfmt + \.contains_bgcfmt + \.end_bgcfmt + \.' contained' + " Define the r2 region vars if and only if at least one of the + " region types whose end token could begin this region is active + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + let start_bgcfmt = + \' start=/[' + \.b:txtfmt_re_clr_etok_atom + \.']/' + let r2_bgcfmt = + \skip_bgcfmt + \.contains_bgcfmt + \.start_bgcfmt + \.end_bgcfmt + \.' contained' + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + "=== + "*** bgc-fmt-clr + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_bgcfmtclr= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_bgc_etok_inner_esc,' + \.'Tf_fmt_etok_inner_esc,' + \.'Tf_clr_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_bgcfmtclr = ' skip=/\\./' + else + let skip_bgcfmtclr = ' skip=/\(.\)\1/' + endif + else + let contains_bgcfmtclr = '' + let skip_bgcfmtclr = '' + endif + let end_bgcfmtclr = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_bgc_etok_atom + \.b:txtfmt_re_fmt_etok_atom + \.b:txtfmt_re_clr_etok_atom + \.']/me=e-'.tok_off + let r1_bgcfmtclr = + \skip_bgcfmtclr + \.contains_bgcfmtclr + \.end_bgcfmtclr + \.' contained' + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + " Revert to toplevel (no background color) matchgroup + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + "=== + "*** fmt + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_fmt= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_fmt_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_fmt = ' skip=/\\./' + else + let skip_fmt = ' skip=/\(.\)\1/' + endif + else + let contains_fmt = '' + let skip_fmt = '' + endif + let end_fmt = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_fmt_etok_atom + \.']/me=e-'.tok_off + let r1_fmt = + \skip_fmt + \.contains_fmt + \.end_fmt + \.containedin_def + " Define the r2 region vars if and only if at least one of the + " region types whose end token could begin this region is active + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled || bgc_enabled + let start_fmt = + \' start=/[' + \.(clr_enabled ? b:txtfmt_re_clr_etok_atom : '').(bgc_enabled ? b:txtfmt_re_bgc_etok_atom : '') + \.']/' + let r2_fmt = + \skip_fmt + \.contains_fmt + \.start_fmt + \.end_fmt + \.' contained' + endif " clr_enabled || bgc_enabled + "============= END NON-INDENTING BLOCK ============= + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + "=== + "*** fmt-clr + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_fmtclr= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_fmt_etok_inner_esc,' + \.'Tf_clr_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_fmtclr = ' skip=/\\./' + else + let skip_fmtclr = ' skip=/\(.\)\1/' + endif + else + let contains_fmtclr = '' + let skip_fmtclr = '' + endif + let end_fmtclr = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_fmt_etok_atom + \.b:txtfmt_re_clr_etok_atom + \.']/me=e-'.tok_off + let r1_fmtclr = + \skip_fmtclr + \.contains_fmtclr + \.end_fmtclr + \.' contained' + " Define the r2 region vars if and only if at least one of the + " region types whose end token could begin this region is active + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + let start_fmtclr = + \' start=/[' + \.b:txtfmt_re_bgc_etok_atom + \.']/' + let r2_fmtclr = + \skip_fmtclr + \.contains_fmtclr + \.start_fmtclr + \.end_fmtclr + \.' contained' + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + "=== + "*** fmt-clr-bgc + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_fmtclrbgc= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_fmt_etok_inner_esc,' + \.'Tf_clr_etok_inner_esc,' + \.'Tf_bgc_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_fmtclrbgc = ' skip=/\\./' + else + let skip_fmtclrbgc = ' skip=/\(.\)\1/' + endif + else + let contains_fmtclrbgc = '' + let skip_fmtclrbgc = '' + endif + let end_fmtclrbgc = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_fmt_etok_atom + \.b:txtfmt_re_clr_etok_atom + \.b:txtfmt_re_bgc_etok_atom + \.']/me=e-'.tok_off + let r1_fmtclrbgc = + \skip_fmtclrbgc + \.contains_fmtclrbgc + \.end_fmtclrbgc + \.' contained' + " Revert to toplevel (no background color) matchgroup + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + "=== + "*** fmt-bgc + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_fmtbgc= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_fmt_etok_inner_esc,' + \.'Tf_bgc_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_fmtbgc = ' skip=/\\./' + else + let skip_fmtbgc = ' skip=/\(.\)\1/' + endif + else + let contains_fmtbgc = '' + let skip_fmtbgc = '' + endif + let end_fmtbgc = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_fmt_etok_atom + \.b:txtfmt_re_bgc_etok_atom + \.']/me=e-'.tok_off + let r1_fmtbgc = + \skip_fmtbgc + \.contains_fmtbgc + \.end_fmtbgc + \.' contained' + " Define the r2 region vars if and only if at least one of the + " region types whose end token could begin this region is active + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + let start_fmtbgc = + \' start=/[' + \.b:txtfmt_re_clr_etok_atom + \.']/' + let r2_fmtbgc = + \skip_fmtbgc + \.contains_fmtbgc + \.start_fmtbgc + \.end_fmtbgc + \.' contained' + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + "=== + "*** fmt-bgc-clr + "=== + if b:txtfmt_cfg_escape != 'none' + let contains_fmtbgcclr= + \' contains=' + \.'Tf_any_stok_inner_esc,' + \.'Tf_fmt_etok_inner_esc,' + \.'Tf_bgc_etok_inner_esc,' + \.'Tf_clr_etok_inner_esc' + if b:txtfmt_cfg_escape == 'bslash' + let skip_fmtbgcclr = ' skip=/\\./' + else + let skip_fmtbgcclr = ' skip=/\(.\)\1/' + endif + else + let contains_fmtbgcclr = '' + let skip_fmtbgcclr = '' + endif + let end_fmtbgcclr = + \' end=/[' + \.b:txtfmt_re_any_stok_atom + \.b:txtfmt_re_fmt_etok_atom + \.b:txtfmt_re_bgc_etok_atom + \.b:txtfmt_re_clr_etok_atom + \.']/me=e-'.tok_off + let r1_fmtbgcclr = + \skip_fmtbgcclr + \.contains_fmtbgcclr + \.end_fmtbgcclr + \.' contained' + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + " Revert to toplevel (no background color) matchgroup + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + "=== + "*** Loop over clr levels + "=== + let ip = 1 + while ip <= b:txtfmt_cfg_numfgcolors + let i = b:txtfmt_cfg_fgcolor{ip} + let chi = nr2char(b:txtfmt_clr_first_tok + i) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_clr_'.i + exe 'syn cluster Tf'.cui.'_clr_all add=Tf'.cui.'_clr_'.i + " Cache the nextgroup clause + let ng = ' nextgroup=@Tf'.cui.'_clr_all'.(bgc_enabled ? ',@Tf'.cui.'_clrbgc_'.i.'_all' : '').',@Tf'.cui.'_clrfmt_'.i.'_all,Tf_def_tok' + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_clr_'.i.matchgroup_def + \.' start=/'.chi.'/'.r1_clr.ng.concealends + " Define region that is begun by an end token + " (when permitted by a nextgroup) + exe 'syn region Tf'.cui.'_clr_'.i.'_rtd'.matchgroup_def + \.r2_clr.ng.concealends + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_clr_'.i + \.eq_clr.b:txtfmt_clr{i} + " Link rtd to non-rtd group + exe 'hi link Tf'.cui.'_clr_'.i.'_rtd Tf'.cui.'_clr_'.i + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + "=== + "*** Loop over clr-bgc levels + "=== + let jp = 1 + while jp <= b:txtfmt_cfg_numbgcolors + let j = b:txtfmt_cfg_bgcolor{jp} + let chj = nr2char(b:txtfmt_bgc_first_tok + j) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_clrbgc_'.i.'_'.j + exe 'syn cluster Tf'.cui.'_clrbgc_'.i.'_all add=Tf'.cui.'_clrbgc_'.i.'_'.j + " Ensure that this and higher order regions use bgc-specific concealment group + let matchgroup_def = ' matchgroup=Tf'.cui.'_conceal_'.j + " Cache the nextgroup clause + let ng = ' nextgroup=Tf'.cui.'_bgc_'.j.'_rtd,Tf'.cui.'_clr_'.i.'_rtd,@Tf'.cui.'_bgcclr_'.j.'_all,@Tf'.cui.'_clrbgc_'.i.'_all,@Tf'.cui.'_clrbgcfmt_'.i.'_'.j.'_all' + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_clrbgc_'.i.'_'.j.matchgroup_def + \.' start=/'.chj.'/'.r1_clrbgc.ng.concealends + " Define region that is begun by an end token + " (when permitted by a nextgroup) + exe 'syn region Tf'.cui.'_clrbgc_'.i.'_'.j.'_rtd'.matchgroup_def + \.r2_clrbgc.ng.concealends + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_clrbgc_'.i.'_'.j + \.eq_clr.b:txtfmt_clr{i}.eq_bgc.b:txtfmt_bgc{j} + " Link rtd to non-rtd group + exe 'hi link Tf'.cui.'_clrbgc_'.i.'_'.j.'_rtd Tf'.cui.'_clrbgc_'.i.'_'.j + "=== + "*** Loop over clr-bgc-fmt levels + "=== + let k = 1 + while k <= b:txtfmt_num_formats - 1 + let chk = nr2char(b:txtfmt_fmt_first_tok + k) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_clrbgcfmt_'.i.'_'.j.'_'.k + exe 'syn cluster Tf'.cui.'_clrbgcfmt_'.i.'_'.j.'_all add=Tf'.cui.'_clrbgcfmt_'.i.'_'.j.'_'.k + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_clrbgcfmt_'.i.'_'.j.'_'.k.matchgroup_def + \.' start=/'.chk.'/'.r1_clrbgcfmt.' nextgroup=Tf'.cui.'_bgcfmt_'.j.'_'.k.'_rtd,Tf'.cui.'_clrfmt_'.i.'_'.k.'_rtd,Tf'.cui.'_clrbgc_'.i.'_'.j.'_rtd,@Tf'.cui.'_bgcfmtclr_'.j.'_'.k.'_all,@Tf'.cui.'_clrfmtbgc_'.i.'_'.k.'_all,@Tf'.cui.'_clrbgcfmt_'.i.'_'.j.'_all'.concealends + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_clrbgcfmt_'.i.'_'.j.'_'.k + \.eq_clr.b:txtfmt_clr{i}.eq_bgc.b:txtfmt_bgc{j}.eq_fmt.b:txtfmt_fmt{k} + let k = k + 1 + endwhile + let jp = jp + 1 + endwhile + " Revert to toplevel (no background color) matchgroup + let matchgroup_def = matchgroup_top_def + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + "=== + "*** Loop over clr-fmt levels + "=== + let j = 1 + while j <= b:txtfmt_num_formats - 1 + let chj = nr2char(b:txtfmt_fmt_first_tok + j) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_clrfmt_'.i.'_'.j + exe 'syn cluster Tf'.cui.'_clrfmt_'.i.'_all add=Tf'.cui.'_clrfmt_'.i.'_'.j + " Cache the nextgroup clause + let ng = ' nextgroup=Tf'.cui.'_fmt_'.j.'_rtd,Tf'.cui.'_clr_'.i.'_rtd,@Tf'.cui.'_fmtclr_'.j.'_all,@Tf'.cui.'_clrfmt_'.i.'_all'.(bgc_enabled ? ',@Tf'.cui.'_clrfmtbgc_'.i.'_'.j.'_all' : '') + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_clrfmt_'.i.'_'.j.matchgroup_def + \.' start=/'.chj.'/'.r1_clrfmt.ng.concealends + " Define the following region if and only if at least one of the + " region types whose end token could begin this region is active + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + " Define region that is begun by an end token + " (when permitted by a nextgroup) + exe 'syn region Tf'.cui.'_clrfmt_'.i.'_'.j.'_rtd'.matchgroup_def + \.r2_clrfmt.ng.concealends + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_clrfmt_'.i.'_'.j + \.eq_clr.b:txtfmt_clr{i}.eq_fmt.b:txtfmt_fmt{j} + " Link rtd to non-rtd group + exe 'hi link Tf'.cui.'_clrfmt_'.i.'_'.j.'_rtd Tf'.cui.'_clrfmt_'.i.'_'.j + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + "=== + "*** Loop over clr-fmt-bgc levels + "=== + let kp = 1 + while kp <= b:txtfmt_cfg_numbgcolors + let k = b:txtfmt_cfg_bgcolor{kp} + let chk = nr2char(b:txtfmt_bgc_first_tok + k) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_clrfmtbgc_'.i.'_'.j.'_'.k + exe 'syn cluster Tf'.cui.'_clrfmtbgc_'.i.'_'.j.'_all add=Tf'.cui.'_clrfmtbgc_'.i.'_'.j.'_'.k + " Ensure that this and higher order regions use bgc-specific concealment group + let matchgroup_def = ' matchgroup=Tf'.cui.'_conceal_'.k + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_clrfmtbgc_'.i.'_'.j.'_'.k.matchgroup_def + \.' start=/'.chk.'/'.r1_clrfmtbgc.' nextgroup=Tf'.cui.'_fmtbgc_'.j.'_'.k.'_rtd,Tf'.cui.'_clrbgc_'.i.'_'.k.'_rtd,Tf'.cui.'_clrfmt_'.i.'_'.j.'_rtd,@Tf'.cui.'_fmtbgcclr_'.j.'_'.k.'_all,@Tf'.cui.'_clrbgcfmt_'.i.'_'.k.'_all,@Tf'.cui.'_clrfmtbgc_'.i.'_'.j.'_all'.concealends + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_clrfmtbgc_'.i.'_'.j.'_'.k + \.eq_clr.b:txtfmt_clr{i}.eq_bgc.b:txtfmt_bgc{k}.eq_fmt.b:txtfmt_fmt{j} + let kp = kp + 1 + endwhile + " Revert to toplevel (no background color) matchgroup + let matchgroup_def = matchgroup_top_def + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + let j = j + 1 + endwhile + let ip = ip + 1 + endwhile + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + "=== + "*** Loop over bgc levels + "=== + let ip = 1 + while ip <= b:txtfmt_cfg_numbgcolors + let i = b:txtfmt_cfg_bgcolor{ip} + let chi = nr2char(b:txtfmt_bgc_first_tok + i) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_bgc_'.i + exe 'syn cluster Tf'.cui.'_bgc_all add=Tf'.cui.'_bgc_'.i + " Ensure that this and higher order regions use bgc-specific concealment group + let matchgroup_def = ' matchgroup=Tf'.cui.'_conceal_'.i + " Cache the nextgroup clause + let ng = ' nextgroup=@Tf'.cui.'_bgc_all'.(clr_enabled ? ',@Tf'.cui.'_bgcclr_'.i.'_all' : '').',@Tf'.cui.'_bgcfmt_'.i.'_all,Tf_def_tok' + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_bgc_'.i.matchgroup_def + \.' start=/'.chi.'/'.r1_bgc.ng.concealends + " Define region that is begun by an end token + " (when permitted by a nextgroup) + exe 'syn region Tf'.cui.'_bgc_'.i.'_rtd'.matchgroup_def + \.r2_bgc.ng.concealends + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_bgc_'.i + \.eq_bgc.b:txtfmt_bgc{i} + " Link rtd to non-rtd group + exe 'hi link Tf'.cui.'_bgc_'.i.'_rtd Tf'.cui.'_bgc_'.i + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + "=== + "*** Loop over bgc-clr levels + "=== + let jp = 1 + while jp <= b:txtfmt_cfg_numfgcolors + let j = b:txtfmt_cfg_fgcolor{jp} + let chj = nr2char(b:txtfmt_clr_first_tok + j) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_bgcclr_'.i.'_'.j + exe 'syn cluster Tf'.cui.'_bgcclr_'.i.'_all add=Tf'.cui.'_bgcclr_'.i.'_'.j + " Cache the nextgroup clause + let ng = ' nextgroup=Tf'.cui.'_clr_'.j.'_rtd,Tf'.cui.'_bgc_'.i.'_rtd,@Tf'.cui.'_clrbgc_'.j.'_all,@Tf'.cui.'_bgcclr_'.i.'_all,@Tf'.cui.'_bgcclrfmt_'.i.'_'.j.'_all' + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_bgcclr_'.i.'_'.j.matchgroup_def + \.' start=/'.chj.'/'.r1_bgcclr.ng.concealends + " Define region that is begun by an end token + " (when permitted by a nextgroup) + exe 'syn region Tf'.cui.'_bgcclr_'.i.'_'.j.'_rtd'.matchgroup_def + \.r2_bgcclr.ng.concealends + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_bgcclr_'.i.'_'.j + \.eq_clr.b:txtfmt_clr{j}.eq_bgc.b:txtfmt_bgc{i} + " Link rtd to non-rtd group + exe 'hi link Tf'.cui.'_bgcclr_'.i.'_'.j.'_rtd Tf'.cui.'_bgcclr_'.i.'_'.j + "=== + "*** Loop over bgc-clr-fmt levels + "=== + let k = 1 + while k <= b:txtfmt_num_formats - 1 + let chk = nr2char(b:txtfmt_fmt_first_tok + k) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_bgcclrfmt_'.i.'_'.j.'_'.k + exe 'syn cluster Tf'.cui.'_bgcclrfmt_'.i.'_'.j.'_all add=Tf'.cui.'_bgcclrfmt_'.i.'_'.j.'_'.k + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_bgcclrfmt_'.i.'_'.j.'_'.k.matchgroup_def + \.' start=/'.chk.'/'.r1_bgcclrfmt.' nextgroup=Tf'.cui.'_clrfmt_'.j.'_'.k.'_rtd,Tf'.cui.'_bgcfmt_'.i.'_'.k.'_rtd,Tf'.cui.'_bgcclr_'.i.'_'.j.'_rtd,@Tf'.cui.'_clrfmtbgc_'.j.'_'.k.'_all,@Tf'.cui.'_bgcfmtclr_'.i.'_'.k.'_all,@Tf'.cui.'_bgcclrfmt_'.i.'_'.j.'_all'.concealends + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_bgcclrfmt_'.i.'_'.j.'_'.k + \.eq_clr.b:txtfmt_clr{j}.eq_bgc.b:txtfmt_bgc{i}.eq_fmt.b:txtfmt_fmt{k} + let k = k + 1 + endwhile + let jp = jp + 1 + endwhile + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + "=== + "*** Loop over bgc-fmt levels + "=== + let j = 1 + while j <= b:txtfmt_num_formats - 1 + let chj = nr2char(b:txtfmt_fmt_first_tok + j) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_bgcfmt_'.i.'_'.j + exe 'syn cluster Tf'.cui.'_bgcfmt_'.i.'_all add=Tf'.cui.'_bgcfmt_'.i.'_'.j + " Cache the nextgroup clause + let ng = ' nextgroup=Tf'.cui.'_fmt_'.j.'_rtd,Tf'.cui.'_bgc_'.i.'_rtd,@Tf'.cui.'_fmtbgc_'.j.'_all,@Tf'.cui.'_bgcfmt_'.i.'_all'.(clr_enabled ? ',@Tf'.cui.'_bgcfmtclr_'.i.'_'.j.'_all' : '') + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_bgcfmt_'.i.'_'.j.matchgroup_def + \.' start=/'.chj.'/'.r1_bgcfmt.ng.concealends + " Define the following region if and only if at least one of the + " region types whose end token could begin this region is active + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + " Define region that is begun by an end token + " (when permitted by a nextgroup) + exe 'syn region Tf'.cui.'_bgcfmt_'.i.'_'.j.'_rtd'.matchgroup_def + \.r2_bgcfmt.ng.concealends + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_bgcfmt_'.i.'_'.j + \.eq_bgc.b:txtfmt_bgc{i}.eq_fmt.b:txtfmt_fmt{j} + " Link rtd to non-rtd group + exe 'hi link Tf'.cui.'_bgcfmt_'.i.'_'.j.'_rtd Tf'.cui.'_bgcfmt_'.i.'_'.j + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + "=== + "*** Loop over bgc-fmt-clr levels + "=== + let kp = 1 + while kp <= b:txtfmt_cfg_numfgcolors + let k = b:txtfmt_cfg_fgcolor{kp} + let chk = nr2char(b:txtfmt_clr_first_tok + k) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_bgcfmtclr_'.i.'_'.j.'_'.k + exe 'syn cluster Tf'.cui.'_bgcfmtclr_'.i.'_'.j.'_all add=Tf'.cui.'_bgcfmtclr_'.i.'_'.j.'_'.k + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_bgcfmtclr_'.i.'_'.j.'_'.k.matchgroup_def + \.' start=/'.chk.'/'.r1_bgcfmtclr.' nextgroup=Tf'.cui.'_fmtclr_'.j.'_'.k.'_rtd,Tf'.cui.'_bgcclr_'.i.'_'.k.'_rtd,Tf'.cui.'_bgcfmt_'.i.'_'.j.'_rtd,@Tf'.cui.'_fmtclrbgc_'.j.'_'.k.'_all,@Tf'.cui.'_bgcclrfmt_'.i.'_'.k.'_all,@Tf'.cui.'_bgcfmtclr_'.i.'_'.j.'_all'.concealends + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_bgcfmtclr_'.i.'_'.j.'_'.k + \.eq_clr.b:txtfmt_clr{k}.eq_bgc.b:txtfmt_bgc{i}.eq_fmt.b:txtfmt_fmt{j} + let kp = kp + 1 + endwhile + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + let j = j + 1 + endwhile + let ip = ip + 1 + endwhile + " Revert to toplevel (no background color) matchgroup + let matchgroup_def = matchgroup_top_def + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + "=== + "*** Loop over fmt levels + "=== + let i = 1 + while i <= b:txtfmt_num_formats - 1 + let chi = nr2char(b:txtfmt_fmt_first_tok + i) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_fmt_'.i + exe 'syn cluster Tf'.cui.'_fmt_all add=Tf'.cui.'_fmt_'.i + " Cache the nextgroup clause + let ng = ' nextgroup=@Tf'.cui.'_fmt_all'.(clr_enabled ? ',@Tf'.cui.'_fmtclr_'.i.'_all' : '').(bgc_enabled ? ',@Tf'.cui.'_fmtbgc_'.i.'_all' : '').',Tf_def_tok' + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_fmt_'.i.matchgroup_def + \.' start=/'.chi.'/'.r1_fmt.ng.concealends + " Define the following region if and only if at least one of the + " region types whose end token could begin this region is active + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled || bgc_enabled + " Define region that is begun by an end token + " (when permitted by a nextgroup) + exe 'syn region Tf'.cui.'_fmt_'.i.'_rtd'.matchgroup_def + \.r2_fmt.ng.concealends + endif " clr_enabled || bgc_enabled + "============= END NON-INDENTING BLOCK ============= + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_fmt_'.i + \.eq_fmt.b:txtfmt_fmt{i} + " Link rtd to non-rtd group + exe 'hi link Tf'.cui.'_fmt_'.i.'_rtd Tf'.cui.'_fmt_'.i + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + "=== + "*** Loop over fmt-clr levels + "=== + let jp = 1 + while jp <= b:txtfmt_cfg_numfgcolors + let j = b:txtfmt_cfg_fgcolor{jp} + let chj = nr2char(b:txtfmt_clr_first_tok + j) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_fmtclr_'.i.'_'.j + exe 'syn cluster Tf'.cui.'_fmtclr_'.i.'_all add=Tf'.cui.'_fmtclr_'.i.'_'.j + " Cache the nextgroup clause + let ng = ' nextgroup=Tf'.cui.'_clr_'.j.'_rtd,Tf'.cui.'_fmt_'.i.'_rtd,@Tf'.cui.'_clrfmt_'.j.'_all,@Tf'.cui.'_fmtclr_'.i.'_all'.(bgc_enabled ? ',@Tf'.cui.'_fmtclrbgc_'.i.'_'.j.'_all' : '') + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_fmtclr_'.i.'_'.j.matchgroup_def + \.' start=/'.chj.'/'.r1_fmtclr.ng.concealends + " Define the following region if and only if at least one of the + " region types whose end token could begin this region is active + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + " Define region that is begun by an end token + " (when permitted by a nextgroup) + exe 'syn region Tf'.cui.'_fmtclr_'.i.'_'.j.'_rtd'.matchgroup_def + \.r2_fmtclr.ng.concealends + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_fmtclr_'.i.'_'.j + \.eq_clr.b:txtfmt_clr{j}.eq_fmt.b:txtfmt_fmt{i} + " Link rtd to non-rtd group + exe 'hi link Tf'.cui.'_fmtclr_'.i.'_'.j.'_rtd Tf'.cui.'_fmtclr_'.i.'_'.j + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + "=== + "*** Loop over fmt-clr-bgc levels + "=== + let kp = 1 + while kp <= b:txtfmt_cfg_numbgcolors + let k = b:txtfmt_cfg_bgcolor{kp} + let chk = nr2char(b:txtfmt_bgc_first_tok + k) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_fmtclrbgc_'.i.'_'.j.'_'.k + exe 'syn cluster Tf'.cui.'_fmtclrbgc_'.i.'_'.j.'_all add=Tf'.cui.'_fmtclrbgc_'.i.'_'.j.'_'.k + " Ensure that this and higher order regions use bgc-specific concealment group + let matchgroup_def = ' matchgroup=Tf'.cui.'_conceal_'.k + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_fmtclrbgc_'.i.'_'.j.'_'.k.matchgroup_def + \.' start=/'.chk.'/'.r1_fmtclrbgc.' nextgroup=Tf'.cui.'_clrbgc_'.j.'_'.k.'_rtd,Tf'.cui.'_fmtbgc_'.i.'_'.k.'_rtd,Tf'.cui.'_fmtclr_'.i.'_'.j.'_rtd,@Tf'.cui.'_clrbgcfmt_'.j.'_'.k.'_all,@Tf'.cui.'_fmtbgcclr_'.i.'_'.k.'_all,@Tf'.cui.'_fmtclrbgc_'.i.'_'.j.'_all'.concealends + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_fmtclrbgc_'.i.'_'.j.'_'.k + \.eq_clr.b:txtfmt_clr{j}.eq_bgc.b:txtfmt_bgc{k}.eq_fmt.b:txtfmt_fmt{i} + let kp = kp + 1 + endwhile + " Revert to toplevel (no background color) matchgroup + let matchgroup_def = matchgroup_top_def + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + let jp = jp + 1 + endwhile + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + "============= BEGIN NON-INDENTING BLOCK ============= + if bgc_enabled + "=== + "*** Loop over fmt-bgc levels + "=== + let jp = 1 + while jp <= b:txtfmt_cfg_numbgcolors + let j = b:txtfmt_cfg_bgcolor{jp} + let chj = nr2char(b:txtfmt_bgc_first_tok + j) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_fmtbgc_'.i.'_'.j + exe 'syn cluster Tf'.cui.'_fmtbgc_'.i.'_all add=Tf'.cui.'_fmtbgc_'.i.'_'.j + " Ensure that this and higher order regions use bgc-specific concealment group + let matchgroup_def = ' matchgroup=Tf'.cui.'_conceal_'.j + " Cache the nextgroup clause + let ng = ' nextgroup=Tf'.cui.'_bgc_'.j.'_rtd,Tf'.cui.'_fmt_'.i.'_rtd,@Tf'.cui.'_bgcfmt_'.j.'_all,@Tf'.cui.'_fmtbgc_'.i.'_all'.(clr_enabled ? ',@Tf'.cui.'_fmtbgcclr_'.i.'_'.j.'_all' : '') + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_fmtbgc_'.i.'_'.j.matchgroup_def + \.' start=/'.chj.'/'.r1_fmtbgc.ng.concealends + " Define the following region if and only if at least one of the + " region types whose end token could begin this region is active + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + " Define region that is begun by an end token + " (when permitted by a nextgroup) + exe 'syn region Tf'.cui.'_fmtbgc_'.i.'_'.j.'_rtd'.matchgroup_def + \.r2_fmtbgc.ng.concealends + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_fmtbgc_'.i.'_'.j + \.eq_bgc.b:txtfmt_bgc{j}.eq_fmt.b:txtfmt_fmt{i} + " Link rtd to non-rtd group + exe 'hi link Tf'.cui.'_fmtbgc_'.i.'_'.j.'_rtd Tf'.cui.'_fmtbgc_'.i.'_'.j + "============= BEGIN NON-INDENTING BLOCK ============= + if clr_enabled + "=== + "*** Loop over fmt-bgc-clr levels + "=== + let kp = 1 + while kp <= b:txtfmt_cfg_numfgcolors + let k = b:txtfmt_cfg_fgcolor{kp} + let chk = nr2char(b:txtfmt_clr_first_tok + k) + " Add to appropriate clusters + exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_fmtbgcclr_'.i.'_'.j.'_'.k + exe 'syn cluster Tf'.cui.'_fmtbgcclr_'.i.'_'.j.'_all add=Tf'.cui.'_fmtbgcclr_'.i.'_'.j.'_'.k + " Define region that is begun by a start token + exe 'syn region Tf'.cui.'_fmtbgcclr_'.i.'_'.j.'_'.k.matchgroup_def + \.' start=/'.chk.'/'.r1_fmtbgcclr.' nextgroup=Tf'.cui.'_bgcclr_'.j.'_'.k.'_rtd,Tf'.cui.'_fmtclr_'.i.'_'.k.'_rtd,Tf'.cui.'_fmtbgc_'.i.'_'.j.'_rtd,@Tf'.cui.'_bgcclrfmt_'.j.'_'.k.'_all,@Tf'.cui.'_fmtclrbgc_'.i.'_'.k.'_all,@Tf'.cui.'_fmtbgcclr_'.i.'_'.j.'_all'.concealends + " Define highlighting for this region + " Note: cterm= MUST come after ctermfg= to ensure that bold attribute is + " handled correctly in a cterm. + " :help cterm-colors + exe 'hi Tf'.cui.'_fmtbgcclr_'.i.'_'.j.'_'.k + \.eq_clr.b:txtfmt_clr{k}.eq_bgc.b:txtfmt_bgc{j}.eq_fmt.b:txtfmt_fmt{i} + let kp = kp + 1 + endwhile + endif " clr_enabled + "============= END NON-INDENTING BLOCK ============= + let jp = jp + 1 + endwhile + " Revert to toplevel (no background color) matchgroup + let matchgroup_def = matchgroup_top_def + endif " bgc_enabled + "============= END NON-INDENTING BLOCK ============= + let i = i + 1 + endwhile + " END AUTOGENERATED CODE BLOCK >>> + " Handle escape/escapee token pairs both inside and outside of fmt/clr + " regions. + " Important Note: These groups must be defined after the fmt/clr regions, + " since they must take precedence over them. + " Objectives: + " -Conceal the escape token + " -Prevent escaped or escaping tokens from beginning a region + " -Prevent escaped or escaping tokens from ending a region + " Note: Must take into account the txtfmt 'escape' option + " Also note: Must take into account the size in bytes of the escape char + " if this Vim treats offsets as byte offsets + " TODO_BG: Guard against reliance upon _bgc_ vars when they wouldn't have + " been defined. + if b:txtfmt_cfg_escape == 'bslash' || b:txtfmt_cfg_escape == 'self' + if b:txtfmt_cfg_escape == 'self' + let re_outer_esc_pair = '\(['.b:txtfmt_re_any_stok_atom.']\)\1' + let re_any_stok_esc_pair = '\(['.b:txtfmt_re_any_stok_atom.']\)\1' + let re_fmt_etok_esc_pair = '\(['.b:txtfmt_re_fmt_etok_atom.']\)\1' + if bgc_enabled + let re_bgc_etok_esc_pair = '\(['.b:txtfmt_re_bgc_etok_atom.']\)\1' + endif + if clr_enabled + let re_clr_etok_esc_pair = '\(['.b:txtfmt_re_clr_etok_atom.']\)\1' + endif + " Escape char is same number of bytes as a token + let esc_off = tok_off + elseif b:txtfmt_cfg_escape == 'bslash' + let re_outer_esc_pair = '\\\%(\\\%(\\*['.b:txtfmt_re_any_stok_atom.']\)\@=\|['.b:txtfmt_re_any_stok_atom.']\)' + let re_any_stok_esc_pair = '\\\%(\\\%(\\*['.b:txtfmt_re_any_stok_atom.']\)\@=\|['.b:txtfmt_re_any_stok_atom.']\)' + let re_fmt_etok_esc_pair = '\\\%(\\\%(\\*['.b:txtfmt_re_fmt_etok_atom.']\)\@=\|['.b:txtfmt_re_fmt_etok_atom.']\)' + if bgc_enabled + let re_bgc_etok_esc_pair = '\\\%(\\\%(\\*['.b:txtfmt_re_bgc_etok_atom.']\)\@=\|['.b:txtfmt_re_bgc_etok_atom.']\)' + endif + if clr_enabled + let re_clr_etok_esc_pair = '\\\%(\\\%(\\*['.b:txtfmt_re_clr_etok_atom.']\)\@=\|['.b:txtfmt_re_clr_etok_atom.']\)' + endif + " Escape char is single byte + let esc_off = 1 + endif + " The following group prevents escaping or escaped token from starting + " a region, and causes the escaping token to be hidden + exe 'syn match Tf_outer_esc /'.re_outer_esc_pair.'/he=s+'.esc_off.containedin_def.conceal + " The following group allows escaping tokens to be hidden within a fmt/clr + " region. + exe 'syn match Tf_any_stok_inner_esc /'.re_any_stok_esc_pair.'/he=s+'.esc_off.' contains=NONE' + \.' contained'.conceal + exe 'syn match Tf_fmt_etok_inner_esc /'.re_fmt_etok_esc_pair.'/he=s+'.esc_off.' contains=NONE' + \.' contained'.conceal + if bgc_enabled + exe 'syn match Tf_bgc_etok_inner_esc /'.re_bgc_etok_esc_pair.'/he=s+'.esc_off.' contains=NONE' + \.' contained'.conceal + endif + if clr_enabled + exe 'syn match Tf_clr_etok_inner_esc /'.re_clr_etok_esc_pair.'/he=s+'.esc_off.' contains=NONE' + \.' contained'.conceal + endif + " Define highlighting for the outer and inner escape tokens + hi link Tf_outer_esc Tf_conceal + hi link Tf_any_stok_inner_esc Tf_conceal + hi link Tf_fmt_etok_inner_esc Tf_conceal + if bgc_enabled + hi link Tf_bgc_etok_inner_esc Tf_conceal + endif + if clr_enabled + hi link Tf_clr_etok_inner_esc Tf_conceal + endif + endif +endfu " >>> +" Function: s:Define_syntax_syncing() <<< +fu! s:Define_syntax_syncing() + " Configure syncing based upon syncmethod and (if applicable) synclines + " options. (Note that 'sync' is the only option visible to user. It is + " decoded into the two more convenient options by Do_config_common -> + " Set_syncing.) + if b:txtfmt_cfg_syncmethod == 'fromstart' + syn sync fromstart + elseif b:txtfmt_cfg_syncmethod == 'minlines' + exe 'syn sync minlines='.b:txtfmt_cfg_synclines + endif + " TODO - Get rid of the function code after the following return prior to + " release. + return + " SYNTAX SYNCHRONIZATION using regions correctly but with a flaw <<< + " NOTE: This is the most promising method other than 'minlines'. The only + " problem is, there's no good way to handle the case of txtfmt regions + " intermixed with other types of regions. Note that a problem exists both + " for the nested and nonested cases: + " 'nested' problem scenario: We synchronize to a nested txtfmt region. + " When the nested txtfmt region ends, the containing region is not + " resumed. (If we had used 'minlines', the presumption is we would have + " gone back far enough to parse the containing region.) + " 'nonested' problem scenario: We incorrectly synchronize to a nested + " txtfmt region because, not knowing anything about the non-txtfmt region + " definitions, we have no way of knowing when we are within one. + " TODO - If I decide to dust this off and use it, I need to look at the + " handling of and regions. + if 0 + " IMPORTANT NOTE: This should probably just be removed, but if it's ever + " used, [] need to be wrapped around the tok atoms. + " 'skip' pattern (option dependent) <<< + if b:txtfmt_cfg_escape != 'none' + " Make sure tokens that are part of an escape sequence cannot end a + " region. + if b:txtfmt_cfg_escape == 'bslash' + " TODO - Decide on allowing newlines as tokens (\_. or . ?) + let skip_def = ' skip=/\\./' + elseif b:txtfmt_cfg_escape == 'self' + let skip_def = ' skip=/\('.b:txtfmt_re_any_tok_atom.'\)\1/' + else + let skip_def = '' + endif + else + let skip_def = '' + endif + " >>> + " 'contains' list (option dependent) <<< + " Note: I'm intentionally keeping this out of the 'if' for skip_def to + " keep the logic compartmentalized. + if b:txtfmt_cfg_escape != 'none' + " Permit txtfmt regions to contain specially highlighted pairs of + " escape-escapee tokens. + let contains_def = ' contains=Tf_inner_esc' + else + let contains_def = '' + endif + " >>> + " 'containedin' list (option dependent) <<< + if b:txtfmt_cfg_nested + " Ensure that txtfmt top-level item can be contained by a non-txtfmt + " syntax group (e.g. C-language comment). + if b:txtfmt_cfg_escape != 'none' + let containedin_def = ' containedin=ALLBUT,@Tf_all,Tf_outer_esc,Tf_inner_esc' + else + let containedin_def = ' containedin=ALLBUT,@Tf_all' + endif + else + let containedin_def = '' + endif + " >>> + " Loop over the clr only regions <<< + let i = 1 + let ch = nr2char(b:txtfmt_clr_first_tok + 1) + while i < b:txtfmt_num_colors + " clr{i} region (top level) <<< + " Add to appropriate clusters + exe 'syn cluster Tf_all add=Tf_clr_'.i + exe 'syn cluster Tf_clr_all add=Tf_clr_'.i + " Define nextgroup, which is common to all definitions of this group + let nextgroup_def = ' nextgroup=@Tf_clrfmt_'.i.'_all,@Tf_clr_all' + " Define a clr region that is not contained and is introduced by a clr + " token. This one can match at top level. If txtfmt 'nested' option is + " set, it can also match within non-txtfmt regions. + " Order note: This definition *must* occur prior to the combined + " fmtclr regions that begin with this fmt token. + exe 'syn region Tf_clr_'.i.' matchgroup=Ignore start=/'.ch.'/' + \.skip_def + \.' end=/'.b:txtfmt_re_any_tok_atom.'/me=e-1 ' + \.nextgroup_def + \.contains_def + \.containedin_def + " Define a 'contained' clr region that is introduced by the 'no fmt' + " token when permitted by a 'nextgroup' + exe 'syn region Tf_clr_'.i.' matchgroup=Ignore' + \.' start=/'.nr2char(b:txtfmt_fmt_first_tok).'/' + \.skip_def + \.' end=/'.b:txtfmt_re_any_tok_atom.'/me=e-1 ' + \.nextgroup_def + \.contains_def + \.' contains=Tfsm_clr_'.i + \.' contained' + exe 'syn sync match Tfsm_clr_'.i.' contained grouphere Tf_clr_'.i.' /./' + " >>> + " Update for next iteration + let i = i + 1 + let ch = nr2char(b:txtfmt_clr_first_tok + i) + endwhile + " >>> + " Loop over the fmt only regions <<< + let i = 1 + let ch = nr2char(b:txtfmt_fmt_first_tok + 1) + while i < b:txtfmt_num_formats + " fmt{i} region (top level) <<< + " Add to appropriate clusters + exe 'syn cluster Tf_all add=Tf_fmt_'.i + exe 'syn cluster Tf_fmt_all add=Tf_fmt_'.i + " Define nextgroup, which is common to all definitions of this group + let nextgroup_def = ' nextgroup=@Tf_fmtclr_'.i.'_all,@Tf_fmt_all' + " Define a fmt region that is not contained and is introduced by a fmt + " token. This one can match at top level. If txtfmt 'nested' option is + " set, it can also match within non-txtfmt regions. + " Order note: This definition *must* occur prior to the combined + " clrfmt regions that begin with this fmt token. + exe 'syn region Tf_fmt_'.i.' matchgroup=Ignore start=/'.ch.'/' + \.skip_def + \.' end=/'.b:txtfmt_re_any_tok_atom.'/me=e-1 ' + \.nextgroup_def + \.contains_def + \.containedin_def + " Define a 'contained' fmt region that is introduced by the 'no clr' + " token when permitted by a 'nextgroup' + exe 'syn region Tf_fmt_'.i.' matchgroup=Ignore' + \.' start=/'.nr2char(b:txtfmt_clr_first_tok).'/' + \.skip_def + \.' end=/'.b:txtfmt_re_any_tok_atom.'/me=e-1' + \.nextgroup_def + \.contains_def + \.' contained' + \.' contains=Tfsm_fmt_'.i + exe 'syn sync match Tfsm_fmt_'.i.' contained grouphere Tf_fmt_'.i.' /./' + " >>> + " Update for next iteration + let i = i + 1 + let ch = nr2char(b:txtfmt_fmt_first_tok + i) + endwhile + " >>> + " Loop over the clrfmt regions <<< + let i = 1 + while i < b:txtfmt_num_colors + " clr{i} -- fmt{j} <<< + let j = 1 + let ch = nr2char(b:txtfmt_fmt_first_tok + 1) + while j < b:txtfmt_num_formats + " Add to appropriate clusters + exe 'syn cluster Tf_all add=Tf_clrfmt_'.i.'_'.j + exe 'syn cluster Tf_clrfmt_'.i.'_all add=Tf_clrfmt_'.i.'_'.j + let nextgroup_def = + \' nextgroup=@Tf_clr_'.i.',@Tf_fmt_'.j.',@Tf_clrfmt_'.i.'_all,@Tf_fmtclr_'.j.'_all' + exe 'syn region Tf_clrfmt_'.i.'_'.j.' matchgroup=Ignore' + \.' start=/'.ch.'/'.skip_def + \.' end=/'.b:txtfmt_re_any_tok_atom.'/me=e-1' + \.nextgroup_def + \.contains_def + \.' contained' + \.' contains='.'Tfsm_clrfmt_'.i.'_'.j + exe 'syn sync match Tfsm_clrfmt_'.i.'_'.j.' contained grouphere Tf_clrfmt_'.i.'_'.j.' /./' + " Update for next iteration + let j = j + 1 + let ch = nr2char(b:txtfmt_fmt_first_tok + j) + endwhile + " >>> + " Update for next iteration <<< + let i = i + 1 + " >>> + endwhile + " >>> + " Loop over the fmtclr regions <<< + let i = 1 + while i < b:txtfmt_num_formats + " fmt{i} -- clr{j} <<< + let j = 1 + let ch = nr2char(b:txtfmt_clr_first_tok + 1) + while j < b:txtfmt_num_colors + " Add to appropriate clusters + exe 'syn cluster Tf_all add=Tf_fmtclr_'.i.'_'.j + exe 'syn cluster Tf_fmtclr_'.i.'_all add=Tf_fmtclr_'.i.'_'.j + let nextgroup_def = + \' nextgroup=@Tf_fmt_'.i.',@Tf_clr_'.j.',@Tf_fmtclr_'.i.'_all,@Tf_clrfmt_'.j.'_all' + exe 'syn region Tf_fmtclr_'.i.'_'.j.' matchgroup=Ignore' + \.' start=/'.ch.'/'.skip_def + \.' end=/'.b:txtfmt_re_any_tok_atom.'/me=e-1' + \.nextgroup_def + \.contains_def + \.' contained' + \.' contains='.'Tfsm_fmtclr_'.i.'_'.j + exe 'syn sync match Tfsm_fmtclr_'.i.'_'.j.' contained grouphere Tf_fmtclr_'.i.'_'.j.' /./' + " Update for next iteration + let j = j + 1 + let ch = nr2char(b:txtfmt_clr_first_tok + j) + endwhile + " >>> + " Update for next iteration <<< + let i = i + 1 + " >>> + endwhile + " >>> + endif + " >>> +endfu " >>> +" Function: s:Set_current_syntax() <<< +" Purpose: Set b:current_syntax to something sensible. If txtfmt is loaded +" in conjunction with one or more other plugins, we should set +" b:current_syntax to a dot-separated syntax name list that reflects all +" syntaxes loaded up to and including ours. Note that the b:txtfmt_syntax +" variable should permit us to do this even when other syntax plugins in the +" load chain have not respected assignments to b:current_syntax made by their +" predecessors in the load chain. +fu! s:Set_current_syntax() + if exists('b:txtfmt_syntax') && b:txtfmt_syntax =~ '\%(^\|\.\)txtfmt\%(\.\|$\)' + " Set b:current_syntax to the portion of b:txtfmt_syntax up to and + " including the first (and hopefully the only) occurrence of 'txtfmt' + let b:current_syntax = + \ substitute(b:txtfmt_syntax, + \ '\(.\{-}\%(^\|\.\)txtfmt\%(\.\|$\)\).*', '\1', '') + else + " This shouldn't happen unless user is manually sourcing the txtfmt + " plugin files (which also shouldn't happen). Still, if it does, + " 'txtfmt' is the most sensible choice. + let b:current_syntax = 'txtfmt' + endif +endfu +" >>> +" Call functions to define syntax and syntax syncing <<< +call s:Define_syntax() +call s:Define_syntax_syncing() +" >>> +" Call function to set b:current_syntax variable <<< +call s:Set_current_syntax() +" >>> +" LESSONS LEARNED <<< +" -By default, an item is contained only at top level. +" -containedin=TOP causes an item to be contained not just in top level, but +" in an item which does not have contained set. +" -When an inner (contained) region is truncated because of a keepend in a +" containing region, the inner regions highlighting is used up until the +" point where truncation occurs!!!!! This is not obvious from help. However, +" it's simple to demonstrate: copy the nested parens example from Vim help +" as-is, but add the keepend argument to par1 region. Source the file and +" view text with 3 levels of parens. Colors will be as expected until the +" first close paren is encountered. It will be colored according to the +" highlighting of the innermost nested paren region, not the outer as I would +" have expected. +" -You can use patterns for contains=, containedin=, etc..., but only groups +" defined at the time the command is executed will be matched! (In original +" implementation, this is why I ran the Define_syntax() function twice. Now I +" use clusters.) +" -With keepend, when doing matches for contained groups, the match is +" performed prior to checking for end of containing group. If containing +" group ends inside the contained group, the contained group will be +" truncated, but for purposes of ms=, me=, hs=, he=, the end of the contained +" group is not altered to reflect the point of truncation!!!!! +" -There is an apparent bug with the way contains= and containedin= work with +" matchgroup. I have submitted to Vim list, and am awaiting Bram's return +" from Uganda, at which time he has suggested he will investigate. +" -NOTE: A transparent group inherits the contains= arguments of its +" containing group! (Can lead to unexpected behavior.) +" -Apparent bug with transparent groups inheriting syntax of contained group, +" even when the current location in containing group has syntax disabled due +" to a he=<...>. Example: An empty format region has its open delimiter +" highlighted as error. The remainder of the region is not highlighted +" specially. However, when a transparent escape-escapee pair appears inside +" the empty region, it takes on Error syntax, even though it is past the +" portion of the empty region highlighted as error. +" -IT IS POSSIBLE to have multiple containedin= attributes in the same group, +" even where they would appear to conflict. +" Example: group A may be contained by any group whose name matches +" MyGroup.*Special; additionally, it may be contained in any group whose name +" does not begin with MyGroup. +" containedin=ALLBUT,MyGroup.* containedin=MyGroup.*Special +" Note that this works because when Vim encounters a containedin, it simply +" adds the appropriate contains= attributes to the specified containing +" groups; i.e., a containedin= cannot "rule out" future containment due to a +" subsequent containedin=. +" E56 - (* operand could be empty) workaround for the following pattern: +" \(ab\)\1* +" which will generate E56 in Vim, even though \1 cannot be empty +" Workaround: \(ab\)\%(\1\@=..\)* +" >>> + +" IMPORTANT NOTE: The Vim script ends here. The perl script used to generate +" portions of this file follows... +finish + +#!perl + +# This script generates the core of the Define_syntax function of Brett +# Stahlman's Txtfmt Vim plugin +# It is designed to be invoked from a Vim external program read filter, as +# follows: +# :r !gen_txtfmt_def_syn.pl +# There are 2 passes: +# Pass 1) Generates definitions, which are specific to a token type combination +# (e.g., "fmt", "fmtclr", etc...) +# Pass 2) Generates the nested loops used to define syntax regions + +@rgn = qw(clr bgc fmt); +# TODO: If I keep support for separate bgc and clr definitions, I can get rid +# of the %rhs hash (after making corresponding code modifications) +%rhs = qw(clr clr bgc bgc fmt fmt); +%ord = qw(clr 0 bgc 1 fmt 2); +# Note which types of regions can be disabled through Vim options +@can_dsbl = qw(clr bgc); +# Subtract 1 from b:txtfmt_num_formats (which currently includes the default +# format token) +%loopinfo = ( + clr => { + cnt =>'b:txtfmt_cfg_numfgcolors', + indarr => 'b:txtfmt_cfg_fgcolor', + }, + bgc => { + cnt =>'b:txtfmt_cfg_numbgcolors', + indarr => 'b:txtfmt_cfg_bgcolor', + }, + fmt => { + cnt =>'b:txtfmt_num_formats - 1', + indarr => undef, + }, +); +# TODO: Get rid of %cnt and %idxind if I elect to keep %loopinfo +%cnt = ( + clr => 'b:txtfmt_cfg_numfgcolors', + bgc => 'b:txtfmt_cfg_numbgcolors', + fmt => 'b:txtfmt_num_formats - 1' +); +# Define a hash supporting index indirection +%idxind = ( + clr => 'b:txtfmt_cfg_fgcolor', + bgc => 'b:txtfmt_cfg_bgcolor', + fmt => undef + # Could omit fmt or leave it undef +); + +# Define base indentlevel of the entire block +$init_il = 1; + +# Define the various output passes +use constant { + DEFS => 0, + LOOPS => 1, +}; + +# This semaphore helps determine when an "if _enabled" construct in the +# Vim code would be redundant with a containing one. +# TODO: Currently unused - remove... +my $bgc_guard_cnt = 0; + +sub do_lvl($$$) +{ + # Description of arrays + # @a1 - $_[1]: lhs (fixed) array of token type names. Upon function entry, + # this array represents the most recent region combination to have + # been processed. + # @a1n - lhs (fixed) array of token type names that will be passed in next + # recursive call to this function. Created by appending a single + # token type name from @a2 + # @a2 - $_[2]: rhs (unfixed) array of token type names. Upon function + # entry, this array contains the token type names not included in the + # currently processed region combination. (e.g, if we're currently + # processing "fmt-clr" regions, then @a2 contains "bgc") + # @a2n - rhs (unfixed) array of token type names that will be passed in + # next recursive call to this function. Created by removing a single + # token type name from @a2 + + # Basic algorithm + # Loop over the token type names in @a2, appending each, in turn, to @a1 + # (and calling the result @a1n). For each iteration, process the + # corresponding region combination, which will involve all token type names + # contained in @a1n, then call self recursively (passing @a1n and @a2n) to + # process the next level. + my $section = shift; + my @a1 = @{shift()}; # Fixed portion + my @a2 = @{shift()}; # Unfixed portion + # Determine the level of this recursive call according to number of + # elements in @a1 + my $lvl = @a1; + # Loop over unfixed portion + for my $a2 (@a2) { + my @a1n = (@a1, $a2); + # Create a hash mapping region names to loop var names + my %idx = (); + my $var = 'i'; + for my $a1n (@a1n) { + $idx{$a1n} = $var++; + } + + my @a2n = grep { $_ ne $a2 } @a2; + # Description of @lor, @sor, @hor + # These 3 arrays are a convenience. They are 2D arrays containing + # specific useful combinations of token type names corresponding to the + # preceding level, the current level, and the next level, respectively. + + # Lower order + # Remove each element in turn from @a1n + my @lor = (); + if (@a1n > 1) { + for my $r (@a1n) { + push @lor, [ grep { $_ ne $r } @a1n ]; + } + } + # Same order + # Move each element within @a1n to the end + my @sor = (); + for (my $i = 0; $i < @a1n; $i++) { + my @r = @a1n; + my $r = splice @r, $i, 1; + push @sor, [ @r, $r ]; + } + # Higher order + # Add region types from @a2n to the end + my @hor = (); + for my $r (@a2n) { + push @hor, [ @a1n, $r ]; + } + # Determine initial indent level + my $il; + if ($section == DEFS) { + $il = "\t"; + } else { # if $section == LOOPS + $il = "\t" x ($init_il + $lvl); + } + + # Set convenience variable $need_r2_guard if and only if all the + # region types yet to be pulled in (i.e., the ones whose end tokens + # could begin the current region) are regions that can be disabled. + my $need_r2_guard = join('', sort @can_dsbl) =~ join('', sort @a2n); + + # Begin outputting + # Insert an "if _enabled" if and only if the rightmost region + # type in the fixed array is (i.e., the region being begun + # should not exist if _enabled is false). (If the fixed portion + # contains prior to the last element, we're already inside an + # "if _enabled", in which case, another would be redundant.) + for my $typ (grep { $_ eq $a1n[-1] } @can_dsbl) { + print "\n$il", '"============= BEGIN NON-INDENTING BLOCK ============='; + print "\n$il", "if ${typ}_enabled"; + } + + # PRE RECURSION + if ($section == DEFS) { + # Determine current level's indent + $il = "\t"; + print "\n$il\"==="; + print "\n$il\"*** ", join("-", @a1n); + print "\n$il\"==="; + + print "\n$il", "if b:txtfmt_cfg_escape != 'none'"; + print "\n$il\t", "let contains_", join("", @a1n), "=", + "\n$il\t\t\\' contains='", + "\n$il\t\t\\.'Tf_any_stok_inner_esc,'", + "\n$il\t\t\\.'", + join(",'\n$il\t\t\\.'", + map { "Tf_${_}_etok_inner_esc" } @a1n + ), + "'" + ; + print "\n$il\t", "if b:txtfmt_cfg_escape == 'bslash'"; + print "\n$il\t\t", "let skip_", join("", @a1n), " = ' skip=/\\\\./'"; + print "\n$il\t", "else"; + # TODO: Decide whether to keep this or go back to the more complex pattern + print "\n$il\t\t", "let skip_", join("", @a1n), " = ' skip=/\\(.\\)\\1/'"; + print "\n$il\t", "endif"; + print "\n$il", "else"; + print "\n$il\t", "let contains_", join("", @a1n), " = ''"; + print "\n$il\t", "let skip_", join("", @a1n), " = ''"; + print "\n$il", "endif"; + print "\n$il", "let end_", join("", @a1n), " ="; + print "\n$il\t", "\\' end=/['"; + print "\n$il\t", "\\.b:txtfmt_re_any_stok_atom"; + print "\n$il\t", "\\.", + join("\n$il\t\\.", + map { "b:txtfmt_re_${_}_etok_atom" } @a1n + ), + "\n$il\t", "\\.']/me=e-'.tok_off" + ; + # Define r1_ var + print "\n$il", "let r1_", join("", @a1n), " ="; + print "\n$il\t", "\\skip_", join("", @a1n); + print "\n$il\t", "\\.contains_", join("", @a1n); + print "\n$il\t", "\\.end_", join("", @a1n); + if (@a1n == 1) { + print "\n$il\t", "\\.containedin_def"; + } else { + print "\n$il\t", "\\.' contained'"; + } + # Don't define r2_ var if there's not at least 1 higher order + # region + if (@a2n) { + # Ensure that we don't define the r2 region variables if all + # of the 's whose end token could begin the region are + # inactive. If at least one of these 's is active, the + # vars will be defined, and _enabled ternaries will be + # used as necessary to ensure that we don't consider end + # tokens for inactive region types. + if ($need_r2_guard) { + print "\n$il", '" Define the r2 region vars if and only if at least one of the'; + print "\n$il", '" region types whose end token could begin this region is active'; + print "\n$il", '"============= BEGIN NON-INDENTING BLOCK ============='; + print "\n$il", "if ", + join ' || ', map { "${_}_enabled" } @a2n + ; + } + print "\n$il", "let start_", join("", @a1n), " ="; + print "\n$il\t", "\\' start=/['"; + print "\n$il\t", "\\.", + join(".", + map { + # Wrap the end token for in ternary guard + # unless a containing `if _enabled' renders + # it redundant. + # Note: The ternary is never redundant when + # multiple types are logically or'ed in the + # containing if: e.g., + # if _enabled || _enabled + # Note: If $need_r2_guard is true, @a2n is + # precisely the number of conditions in the if + my $typ = $_; + my $need_ternary = + (grep { $_ eq $typ } @can_dsbl + and !$need_r2_guard || @a2n > 1); + ( + $need_ternary + ? "(${typ}_enabled ? " + : "" + ) . + "b:txtfmt_re_${typ}_etok_atom" . + ( + $need_ternary + ? " : '')" + : "" + ) + } @a2n + ) + ; + print "\n$il\t", "\\.']/'"; + + print "\n$il", "let r2_", join("", @a1n), " ="; + print "\n$il\t", "\\skip_", join("", @a1n); + print "\n$il\t", "\\.contains_", join("", @a1n); + print "\n$il\t", "\\.start_", join("", @a1n); + print "\n$il\t", "\\.end_", join("", @a1n); + print "\n$il\t", "\\.' contained'"; + if ($need_r2_guard) { + print "\n$il", "endif \" ", + join ' || ', map { "${_}_enabled" } @a2n; + print "\n$il", '"============= END NON-INDENTING BLOCK ============='; + } + } + } else { # $section == LOOPS + # Determine current level's indent + $il = "\t" x ($init_il + $lvl); + print "\n$il\"==="; + print "\n$il\"*** Loop over ", join("-", @a1n), " levels"; + print "\n$il\"==="; + # TODO: Think about cleaning this up a bit and adding comments for + # the index indirection... + print "\n$il", "let $idx{$a1n[-1]}", ($loopinfo{$a1n[-1]}{indarr} ? 'p' : ''), " = 1"; + print "\n$il", "while $idx{$a1n[-1]}", ($loopinfo{$a1n[-1]}{indarr} ? 'p' : ''), " <= $loopinfo{$a1n[-1]}{cnt}"; + $il .= "\t"; + print "\n$il", "let $idx{$a1n[-1]} = $loopinfo{$a1n[-1]}{indarr}\{$idx{$a1n[-1]}p\}" + if $loopinfo{$a1n[-1]}{indarr}; + print "\n$il", "let ch$idx{$a1n[-1]} = nr2char(b:txtfmt_$a1n[-1]_first_tok + $idx{$a1n[-1]})"; + + print "\n$il\" Add to appropriate clusters"; + print "\n$il", "exe 'syn cluster Tf'.cui.'_all add=Tf'.cui.'_", + join("", @a1n), "_'.", + join(".'_'.", @idx{@a1n}) + ; + print "\n$il", "exe 'syn cluster Tf'.cui.'_", + join("", @a1n), + (@a1n > 1 + ? "_'." . join(".'_'.", @idx{@a1n[0 .. $#a1n - 1]}) . ".'" + : "" + ), + "_all add=Tf'.cui.'_", + join("", @a1n), + "_'.", + join(".'_'.", @idx{@a1n}) + ; + # Define matchgroup if background color could be changing (i.e., if + # last element of @a1n is 'bgc') + # Note: matchgroup will retain setting given until we move back to + # a lower order region + # Also note: bgc_enabled check would be superfluous here, since + # code won't be executed if bgc_enabled is false + if ($a1n[-1] eq 'bgc') { + print "\n$il", '" Ensure that this and higher order regions use bgc-specific concealment group'; + print "\n$il", "let matchgroup_def = ' matchgroup=Tf'.cui.'_conceal_'.$idx{$a1n[-1]}"; + } + + # Define nextgroup + my $ng = ""; + for my $lor (@lor) { + # Transitions to lower-order groups will always be made to one + # of the special "return to default" (rtd) groups. + $ng .= ",Tf'.cui.'_" . join("", @$lor) . "_'." . join(".'_'.", @idx{@$lor}) . ".'_rtd"; + } + for my $sor (@sor) { + $ng .= ",\@Tf'.cui.'_" . join("", @$sor) . + (@$sor > 1 + ? "_'." . join(".'_'.", @idx{@{$sor}[0 .. $#$sor - 1]}) . ".'" + : "" + ) . + "_all"; + } + # Note: We didn't need to worry about checking _enabled for + # the lor and sor case (since this code will be inside an "if + # _enabled" if is in either of those arrays); however, + # the hor case pulls in a new region type, so we will need to + # check it. + my $unquoted = 0; + for my $hor (@hor) { + my $typ; + if (($typ) = grep { $_ eq $hor->[-1] } @can_dsbl) { + $ng .= ($unquoted ? '' : "'") . ".(${typ}_enabled ? '"; + } + elsif ($unquoted) { + $ng .= ".'"; + } + $ng .= ",\@Tf'.cui.'_" . join("", @$hor) . "_'." . + join(".'_'.", @idx{@a1n}) . + ".'_all"; + if ($typ) { + $ng .= "' : '')"; + $unquoted = 1; + } + else { + $unquoted = 0; + } + } + if (@a1n == 1) { + $ng .= ($unquoted ? ".'" : '') . ",Tf_def_tok'"; + } elsif (!$unquoted) { + $ng .= "'"; + } + # Use substr to strip off the leading comma at the head of $ng + $ng = "' nextgroup=" . substr($ng, 1); + + # If nextgroup is about to be used in 2 region definitions (r1 and + # r2), assign it to a variable for efficiency. (Many + # concatenations are required to build the entire nextgroup + # clause.) + # Note: If there are no more regions to pull in, the nextgroup + # clause will be used only once, so it's more efficient to build + # it within the region definition itself. + if (@a2n) { + print "\n$il\" Cache the nextgroup clause"; + print "\n$il", "let ng = ", $ng; + # Obviate the need for subsequent logic within this script to + # know whether we're caching nextgroup clause or not + $ng = 'ng'; + } + + # Define the rgn that is begun with an stok + print "\n$il\" Define region that is begun by a start token"; + # Save the beginning of the `syn region' statement, which is + # common to both the region begun by start tok and the region + # begun by end tok. (Note that they diverge at the `_rtd' in the + # latter's region name.) + my $pre_start = "\n$il" . + "exe 'syn region " . + "Tf'.cui.'_" . join("", @a1n) . "_'." . join(".'_'.", @idx{@a1n}); + print "$pre_start.matchgroup_def", + "\n$il\t\\.' start=/'.ch$idx{$a1n[-1]}.'/'", + ".r1_", join("", @a1n), ".$ng.concealends"; + + ; + # Define the region introduced by 'no rgn' token (if it exists) + if (@a2n) { + # Ensure that we don't define the region if all of the 's + # whose end token could begin the region are inactive. If at + # least one of these 's is active, the region will be + # defined, and _enabled ternaries will be used as + # necessary to ensure that we don't consider end tokens for + # inactive region types. + if ($need_r2_guard) { + print "\n$il", '" Define the following region if and only if at least one of the'; + print "\n$il", '" region types whose end token could begin this region is active'; + print "\n$il", '"============= BEGIN NON-INDENTING BLOCK ============='; + print "\n$il", "if ", + join ' || ', map { "${_}_enabled" } @a2n + ; + } + print "\n$il\" Define region that is begun by an end token"; + print "\n$il\" (when permitted by a nextgroup)"; + print "$pre_start.'_rtd'.matchgroup_def\n$il\t\\.", + "r2_", join("", @a1n), ".$ng.concealends"; + ; + if ($need_r2_guard) { + print "\n$il", "endif \" ", + join ' || ', map { "${_}_enabled" } @a2n; + print "\n$il", '"============= END NON-INDENTING BLOCK ============='; + } + } + # Define the highlighting region + print "\n$il\" Define highlighting for this region"; + print "\n$il\" Note: cterm= MUST come after ctermfg= to ensure that bold attribute is"; + print "\n$il\" handled correctly in a cterm."; + print "\n$il\"\t:help cterm-colors"; + print "\n$il", "exe 'hi Tf'.cui.'_", join("", @a1n), "_'.", + join(".'_'.", @idx{@a1n}), + "\n$il\t\\.", + join(".", map { + "eq_$_.b:txtfmt_$rhs{$_}\{$idx{$_}\}" + } sort { $ord{$a} <=> $ord{$b} } @a1n + ), + ; + # Define the highlighting region for the rtd group (if it exists) + if (@a2n) { + # Link rtd to non-rtd group, since highlighting is identical + print "\n$il\" Link rtd to non-rtd group"; + print "\n$il", "exe 'hi link Tf'.cui.'_", join("", @a1n), "_'.", + join(".'_'.", @idx{@a1n}), ".'_rtd", + " Tf'.cui.'_", join("", @a1n), "_'.", + join(".'_'.", @idx{@a1n}) + ; + + } + } + + # RECURSE + # Call ourself recursively to handle the next level + do_lvl($section, \@a1n, \@a2n); + + # POST RECURSION + if ($section == DEFS) { + } else { # if $section == LOOPS + # Update for next iteration + my $idx = $idx{$a1n[-1]}; + if ($a1n[-1] eq 'fmt') { + print "\n$il", "let $idx = $idx + 1"; + } else { + print "\n$il", "let ${idx}p = ${idx}p + 1"; + } + # Strip a level of indent + chop $il; + print "\n$il", "endwhile"; + } + # Handle departure from blocks corresponding to 's that can be + # disabled + if (my ($typ) = grep { $_ eq $a1n[-1] } @can_dsbl) { + if ($typ eq 'bgc') { + # Revert to toplevel (no bgc) matchgroup + # Note: Code emitted won't be reached if bgc_enabled is false + print "\n$il", '" Revert to toplevel (no background color) matchgroup'; + print "\n$il", "let matchgroup_def = matchgroup_top_def" if $section eq LOOPS; + } + print "\n$il", "endif \" ${typ}_enabled"; + print "\n$il", '"============= END NON-INDENTING BLOCK ============='; + } + } +} + +# Top level recursion for both sections +# When the following call returns, the entire DEFS section will have been +# output +print "\t\" BEGIN AUTOGENERATED CODE BLOCK ", "<<<"; +print "\n\t\" Last update: ", scalar(localtime), "\n"; +do_lvl(DEFS, [], \@rgn); +# When the following call returns, the entire LOOPS section will have been +# output +do_lvl(LOOPS, [], \@rgn); +print "\n\t\" END AUTOGENERATED CODE BLOCK ", ">>>"; + +__END__ + " vim: sw=4 ts=4 foldmethod=marker foldmarker=<<<,>>> :