"######################################################################### "# ftplugin/votl.vim: VimOutliner functions, commands and settings "# version 0.4.0 "# Copyright (C) 2001,2003 by Steve Litt (slitt@troubleshooters.com) "# Copyright (C) 2004,2014 by Noel Henson (noelwhenson@gmail.com) "# "# This program is free software; you can redistribute it and/or modify "# it under the terms of the GNU General Public License as published by "# the Free Software Foundation; either version 2 of the License, or "# (at your option) any later version. "# "# This program is distributed in the hope that it will be useful, "# but WITHOUT ANY WARRANTY; without even the implied warranty of "# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "# GNU General Public License for more details. "# "# You should have received a copy of the GNU General Public License "# along with this program; if not, see . "# "# Steve Litt, slitt@troubleshooters.com, http://www.troubleshooters.com "######################################################################### " Load the plugin {{{1 " Prevent the plugin from being loaded twice "if exists("b:did_ftplugin") " finish "endif "let b:did_ftplugin = 1 let b:current_syntax = "outliner" " Default Preferences {{{1 let use_space_colon=0 " End User Preferences " VimOutliner Standard Settings {{{1 setlocal autoindent "setlocal backspace=2 setlocal wrapmargin=5 setlocal wrap setlocal tw=78 setlocal noexpandtab setlocal tabstop=4 " tabstop and shiftwidth must match setlocal shiftwidth=4 " values from 2 to 8 work well "setlocal nosmarttab "setlocal softtabstop=0 setlocal foldlevel=20 setlocal foldcolumn=1 " turns on "+" at the beginning of close folds setlocal foldmethod=expr setlocal foldexpr=MyFoldLevel(v:lnum) setlocal indentexpr= setlocal nocindent setlocal iskeyword=@,39,45,48-57,_,129-255 " Vim Outliner Functions {{{1 if !exists("loaded_vimoutliner_functions") let loaded_vimoutliner_functions=1 " Sorting {{{2 " IsParent(line) {{{3 " Return 1 if this line is a parent function! IsParent(line) return (Ind(a:line)+1) == Ind(a:line+1) endfunction "}}}3 " FindParent(line) {{{3 " Return line if parent, parent line if not function! FindParent(line) if IsParent(a:line) return a:line else let l:parentindent = Ind(a:line)-1 let l:searchline = a:line while (Ind(l:searchline) != l:parentindent) && (l:searchline > 0) let l:searchline = l:searchline-1 endwhile return l:searchline endif endfunction "}}}3 " FindLastChild(line) {{{3 " Return the line number of the last decendent of parent line function! FindLastChild(line) let l:parentindent = Ind(a:line) let l:searchline = a:line+1 while Ind(l:searchline) > l:parentindent let l:searchline = l:searchline+1 endwhile return l:searchline-1 endfunction "}}}3 " MoveDown() {{{3 " Move a heading down by one " Used for sorts and reordering of headings function! MoveDown() call cursor(line("."),0) del x put x endfunction "}}}3 " DelHead() {{{3 " Delete a heading " Used for sorts and reordering of headings function! DelHead(line) let l:fstart = foldclosed(a:line) if l:fstart == -1 let l:execstr = a:line . "del x" else let l:fend = foldclosedend(a:line) let l:execstr = l:fstart . "," . l:fend . "del x" endif exec l:execstr endfunction " PutHead() {{{3 " Put a heading " Used for sorts and reordering of headings function! PutHead(line) let l:fstart = foldclosed(a:line) if l:fstart == -1 let l:execstr = a:line . "put x" exec l:execstr else let l:fend = foldclosedend(a:line) let l:execstr = l:fend . "put x" exec l:execstr endif endfunction "}}}3 " NextHead(line) {{{3 " Return line of next heading " Used for sorts and reordering of headings function! NextHead(line) let l:fend = foldclosedend(a:line) if l:fend == -1 return a:line+1 else return l:fend+1 endif endfunction "}}}3 " CompHead(line) {{{3 " Compare this heading and the next " Return 1: next is greater, 0 next is same, -1 next is less function! CompHead(line) let nexthead = NextHead(a:line) let l:thisline=getline(a:line) let l:nextline=getline(nexthead) if indent(a:line) != indent(nexthead) return 0 elseif l:thisline <# l:nextline return 1 elseif l:thisline ># l:nextline return -1 else return 0 endif endfunction "}}}3 " Sort1Line(line) {{{3 " Compare this heading and the next and swap if out of order " Dir is 0 for forward, 1 for reverse " Return a 1 if a change was made function! Sort1Line(line,dir) if (CompHead(a:line) == -1) && (a:dir == 0) call DelHead(a:line) call PutHead(a:line) return 1 elseif (CompHead(a:line) == 1) && (a:dir == 1) call DelHead(a:line) call PutHead(a:line) return 1 else return 0 endif endfunction "}}}3 " Sort1Pass(start,end,dir) {{{3 " Compare this heading and the next and swap if out of order " Dir is 0 for forward, 1 for reverse " Return a 0 if no change was made, other wise return the change count function! Sort1Pass(fstart,fend,dir) let l:i = a:fstart let l:changed = 0 while l:i < a:fend let l:changed = l:changed + Sort1Line(l:i,a:dir) let l:i = NextHead(l:i) endwhile return l:changed endfunction "}}}3 " Sort(start,end,dir) {{{3 " Sort this range of headings " dir: 0 = ascending, 1 = decending function! SortRange(fstart,fend,dir) let l:changed = 1 while l:changed != 0 let l:changed = Sort1Pass(a:fstart,a:fend,a:dir) endwhile endfunction "}}}3 " SortChildren(dir) {{{3 " Sort the children of a parent " dir: 0 = ascending, 1 = descending function! SortChildren(dir) let l:oldcursor = line(".") let l:fstart = FindParent(line(".")) let l:fend = FindLastChild(l:fstart) let l:fstart = l:fstart if l:fend <= l:fstart + 1 return endif call append(line("$"),"Temporary last line for sorting") mkview let l:execstr = "set foldlevel=" . foldlevel(l:fstart) exec l:execstr call SortRange(l:fstart + 1,l:fend,a:dir) call cursor(line("$"),0) del x loadview call cursor(l:oldcursor,0) endfunction "}}}3 "}}}2 " MakeChars() {{{2 " Make a string of characters " Used for strings of repeated characters function MakeChars(count,char) let i = 0 let l:chars="" while i < a:count let l:chars = l:chars . a:char let i = i + 1 endwhile return l:chars endfunction "}}}2 " MakeSpaces() {{{2 " Make a string of spaces function MakeSpaces(count) return MakeChars(a:count," ") endfunction "}}}2 " MakeDashes() {{{2 " Make a string of dashes function MakeDashes(count) return MakeChars(a:count,"-") endfunction "}}}2 " MyFoldText() {{{2 " Create string used for folded text blocks function MyFoldText() if exists('g:vo_fold_length') && g:vo_fold_length == "max" let l:foldlength = winwidth(0) - 1 - &numberwidth - &foldcolumn elseif exists('g:vo_fold_length') let l:foldlength = g:vo_fold_length else let l:foldlength = 58 endif " I have this as an option, if the user wants to set "…" as the padding " string, or some other string, like "(more)" if exists('g:vo_trim_string') let l:trimstr = g:vo_trim_string else let l:trimstr = "..." endif let l:MySpaces = MakeSpaces(&sw) let l:line = getline(v:foldstart) let l:bodyTextFlag=0 if l:line =~ "^\t* \\S" || l:line =~ "^\t*\:" let l:bodyTextFlag=1 let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1)) let l:line = l:MySpaces."[TEXT]" elseif l:line =~ "^\t*\;" let l:bodyTextFlag=1 let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1)) let l:line = l:MySpaces."[TEXT BLOCK]" elseif l:line =~ "^\t*\> " let l:bodyTextFlag=1 let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1)) let l:line = l:MySpaces."[USER]" elseif l:line =~ "^\t*\>" let l:ls = stridx(l:line,">") let l:le = stridx(l:line," ") if l:le == -1 let l:l = strpart(l:line, l:ls+1) else let l:l = strpart(l:line, l:ls+1, l:le-l:ls-1) endif let l:bodyTextFlag=1 let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1)) let l:line = l:MySpaces."[USER ".l:l."]" elseif l:line =~ "^\t*\< " let l:bodyTextFlag=1 let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1)) let l:line = l:MySpaces."[USER BLOCK]" elseif l:line =~ "^\t*\<" let l:ls = stridx(l:line,"<") let l:le = stridx(l:line," ") if l:le == -1 let l:l = strpart(l:line, l:ls+1) else let l:l = strpart(l:line, l:ls+1, l:le-l:ls-1) endif let l:bodyTextFlag=1 let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1)) let l:line = l:MySpaces."[USER BLOCK ".l:l."]" elseif l:line =~ "^\t*\|" let l:bodyTextFlag=1 let l:MySpaces = MakeSpaces(&sw * (v:foldlevel-1)) let l:line = l:MySpaces."[TABLE]" endif let l:sub = substitute(l:line,'\t',l:MySpaces,'g') let l:sublen = strdisplaywidth(l:sub) let l:end = " (" . ((v:foldend + l:bodyTextFlag)- v:foldstart) if ((v:foldend + l:bodyTextFlag)- v:foldstart) == 1 let l:end = l:end . " line)" else let l:end = l:end . " lines)" endif let l:endlen = strdisplaywidth(l:end) " Multiple cases: " (1) Full padding with ellipse (...) or user defined string, " (2) No point in padding, pad would just obscure the end of text, " (3) Don't pad and use dashes to fill up the space. if l:endlen + l:sublen > l:foldlength let l:sub = strpart(l:sub, 0, l:foldlength - l:endlen - strdisplaywidth(l:trimstr)) let l:sub = l:sub . l:trimstr let l:sublen = strdisplaywidth(l:sub) let l:sub = l:sub . l:end elseif l:endlen + l:sublen == l:foldlength let l:sub = l:sub . l:end else let l:sub = l:sub . " " . MakeDashes(l:foldlength - l:endlen - l:sublen - 1) . l:end endif return l:sub.repeat(' ', winwidth(0)-strdisplaywidth(l:sub)) endfunction "}}}2 " InsertDate() {{{2 " Insert today's date. function InsertDate(ba) let @x = strftime("%Y-%m-%d") if a:ba == "0" normal! "xp else normal! "xP endif endfunction "}}}2 " InsertSpaceDate() {{{2 " Insert a space, then today's date. function InsertSpaceDate() let @x = " " let @x = @x . strftime("%Y-%m-%d") normal! "xp endfunction "}}}2 " InsertTime() {{{2 " Insert the time. function InsertTime(ba) let @x = strftime("%H:%M:%S") if a:ba == "0" normal! "xp else normal! "xP endif endfunction "}}}2 " InsertSpaceTime() {{{2 " Insert a space, then the time. function InsertSpaceTime() let @x = " " let @x = @x . strftime("%H:%M:%S") normal! "xp endfunction "}}}2 " Ind(line) {{{2 " Determine the indent level of a line. " Courtesy of Gabriel Horner function! Ind(line) return indent(a:line)/&tabstop endfunction "}}}2 " BodyText(line) {{{2 " Determine the indent level of a line. function! BodyText(line) return (match(getline(a:line),"^\t*:") == 0) endfunction "}}}2 " PreformattedBodyText(line) {{{2 " Determine the indent level of a line. function! PreformattedBodyText(line) return (match(getline(a:line),"^\t*;") == 0) endfunction "}}}2 " PreformattedUserText(line) {{{2 " Determine the indent level of a line. function! PreformattedUserText(line) return (match(getline(a:line),"^\t*<") == 0) endfunction "}}}2 " PreformattedUserTextLabeled(line) {{{2 " Determine the indent level of a line. function! PreformattedUserTextLabeled(line) return (match(getline(a:line),"^\t*<\S") == 0) endfunction "}}}2 " PreformattedUserTextSpace(line) {{{2 " Determine the indent level of a line. function! PreformattedUserTextSpace(line) return (match(getline(a:line),"^\t*< ") == 0) endfunction "}}}2 " UserText(line) {{{2 " Determine the indent level of a line. function! UserText(line) return (match(getline(a:line),"^\t*>") == 0) endfunction "}}}2 " UserTextSpace(line) {{{2 " Determine the indent level of a line. function! UserTextSpace(line) return (match(getline(a:line),"^\t*> ") == 0) endfunction "}}}2 " UserTextLabeled(line) {{{2 " Determine the indent level of a line. function! UserTextLabeled(line) return (match(getline(a:line),"^\t*>\S") == 0) endfunction "}}}2 " PreformattedTable(line) {{{2 " Determine the indent level of a line. function! PreformattedTable(line) return (match(getline(a:line),"^\t*|") == 0) endfunction "}}}2 " MyFoldLevel(Line) {{{2 " Determine the fold level of a line. function MyFoldLevel(line) let l:myindent = Ind(a:line) let l:nextindent = Ind(a:line+1) if BodyText(a:line) if (BodyText(a:line-1) == 0) return '>'.(l:myindent+1) endif if (BodyText(a:line+1) == 0) return '<'.(l:myindent+1) endif return (l:myindent+1) elseif PreformattedBodyText(a:line) if (PreformattedBodyText(a:line-1) == 0) return '>'.(l:myindent+1) endif if (PreformattedBodyText(a:line+1) == 0) return '<'.(l:myindent+1) endif return (l:myindent+1) elseif PreformattedTable(a:line) if (PreformattedTable(a:line-1) == 0) return '>'.(l:myindent+1) endif if (PreformattedTable(a:line+1) == 0) return '<'.(l:myindent+1) endif return (l:myindent+1) elseif PreformattedUserText(a:line) if (PreformattedUserText(a:line-1) == 0) return '>'.(l:myindent+1) endif if (PreformattedUserTextSpace(a:line+1) == 0) return '<'.(l:myindent+1) endif return (l:myindent+1) elseif PreformattedUserTextLabeled(a:line) if (PreformattedUserTextLabeled(a:line-1) == 0) return '>'.(l:myindent+1) endif if (PreformattedUserText(a:line+1) == 0) return '<'.(l:myindent+1) endif return (l:myindent+1) elseif UserText(a:line) if (UserText(a:line-1) == 0) return '>'.(l:myindent+1) endif if (UserTextSpace(a:line+1) == 0) return '<'.(l:myindent+1) endif return (l:myindent+1) elseif UserTextLabeled(a:line) if (UserTextLabeled(a:line-1) == 0) return '>'.(l:myindent+1) endif if (UserText(a:line+1) == 0) return '<'.(l:myindent+1) endif return (l:myindent+1) else if l:myindent < l:nextindent return '>'.(l:myindent+1) endif if l:myindent > l:nextindent "return '<'.(l:nextindent+1) return (l:myindent) "return '<'.(l:nextindent-1) endif return l:myindent endif endfunction "}}}2 " Spawn(line) {{{2 " Execute an executable line " Courtesy of Steve Litt if !exists("loaded_steveoutliner_functions") let loaded_steveoutliner_functions=1 function Spawn() let theline=getline(line(".")) let idx=matchend(theline, "_exe_\\s*") if idx == -1 echo "Not an executable line" else let command=strpart(theline, idx) let command="!".command exec command endif endfunction endif "}}}2 " This should be a setlocal but that doesn't work when switching to a new .otl file " within the same buffer. Using :e has demonstrated this. set foldtext=MyFoldText() "setlocal fillchars=|, endif " if !exists("loaded_vimoutliner_functions") " End Vim Outliner Functions " Menu Entries {{{1 " VO menu amenu &VO.Expand\ Level\ &1 :set foldlevel=0 amenu &VO.Expand\ Level\ &2 :set foldlevel=1 amenu &VO.Expand\ Level\ &3 :set foldlevel=2 amenu &VO.Expand\ Level\ &4 :set foldlevel=3 amenu &VO.Expand\ Level\ &5 :set foldlevel=4 amenu &VO.Expand\ Level\ &6 :set foldlevel=5 amenu &VO.Expand\ Level\ &7 :set foldlevel=6 amenu &VO.Expand\ Level\ &8 :set foldlevel=7 amenu &VO.Expand\ Level\ &9 :set foldlevel=8 amenu &VO.Expand\ Level\ &All :set foldlevel=99999 amenu &VO.-Sep1- : "Tools sub-menu let s:path2scripts = expand(':p:h:h').'/vimoutliner/scripts' " otl2html exec 'amenu &VO.&Tools.otl2&html\.py\ (otl2html\.py\ thisfile\ -S\ html2otl_nnnnnn\.css\ >\ thisfile\.html) :!'.s:path2scripts.'/otl2html.py -S html2otl_nnnnnn.css % > %.html' " otl2docbook exec 'amenu &VO.&Tools.otl2&docbook\.pl\ (otl2docbook\.pl\ thisfile\ >\ thisfile\.dbk) :!'.s:path2scripts.'/otl2docbook.pl % > %.dbk' " otl2table exec 'amenu &VO.&Tools.otl2&table\.py\ (otl2table\.py\ thisfile\ >\ thisfile\.txt) :!'.s:path2scripts.'/otl2table.py % > %.txt' " otl2tags => FreeMind exec 'amenu &VO.&Tools.otl2tags\.py\ =>\ &FreeMind\ (otl2tags\.py\ \-c\ otl2tags_freemind\.conf\ thisfile\ >\ thisfile\.mm) :!'.s:path2scripts.'/otl2tags.py -c '.s:path2scripts.'/otl2tags_freemind.conf % > %.mm' " otl2tags => Graphviz exec 'amenu &VO.&Tools.otl2tags\.py\ =>\ &Graphviz\ (otl2tags\.py\ \-c\ otl2tags_graphviz\.conf\ thisfile\ >\ thisfile\.gv) :!'.s:path2scripts.'/otl2tags.py -c '.s:path2scripts.'/otl2tags_graphviz.conf % > %.gv' amenu &VO.&Tools.&myotl2thml\.sh\ (myotl2html\.sh\ thisfile) :!myotl2html.sh % amenu &VO.-Sep2- : amenu &VO.&Color\ Scheme :popup Edit.Color\ Scheme amenu &VO.-Sep3- : amenu &VO.&Help.&Index :he vo amenu &VO.&Help.&,,\ Commands :he votl-command amenu &VO.&Help.&Checkboxes :he votl-checkbox amenu &VO.&Help.&Hoisting :he votl-hoisting amenu &Help.-Sep1- : " Help menu additions amenu &Help.&Vim\ Outliner.&Index :he votl amenu &Help.&Vim\ Outliner.&,,\ Commands :he votl-command amenu &Help.&Vim\ Outliner.&Checkboxes :he votl-checkbox amenu &Help.&Vim\ Outliner.&Hoisting :he votl-hoisting "}}}1 " Auto-commands {{{1 if !exists("autocommand_vo_loaded") let autocommand_vo_loaded = 1 au BufNewFile,BufRead *.otl setf votl " au CursorHold *.otl syn sync fromstart "set updatetime=500 endif "}}}1 " this command needs to be run every time so Vim doesn't forget where to look setlocal tags^=$HOME/.vim/vimoutliner/vo_tags.tag " Added an indication of current syntax as per Dillon Jones' request let b:current_syntax = "outliner" " Directory where VO is located now let vo_dir = expand(":p:h:h") " Load rc file, only the first found. let rcs = split(globpath('$HOME,$HOME/.vimoutliner','.vimoutlinerrc'), "\n") + \ split(globpath('$HOME,$HOME/.vimoutliner,$HOME/.vim', 'vimoutlinerrc'), "\n") + \ split(globpath(vo_dir, 'vimoutlinerrc'), "\n") if len(rcs) > 0 exec 'source '.rcs[0] else runtime vimoutliner/vimoutlinerrc endif " Load modules if exists('g:vo_modules_load') for vo_module in split(g:vo_modules_load, '\s*:\s*') exec "runtime! vimoutliner/plugin/votl_" . vo_module . ".vim" endfor unlet! vo_module endif " Vim Outliner Key Mappings {{{1 " insert the date nmap d $:call InsertSpaceDate() imap d ~x:call InsertDate(0)a nmap D ^:call InsertDate(1)a " insert the time nmap t $:call InsertSpaceTime() imap t ~x:call InsertTime(0)a nmap T ^:call InsertTime(1)a " sort a list naturally map s :silent call SortChildren(0) " sort a list, but you supply the options map S :silent call SortChildren(1) " invoke the file explorer map f :e . imap f :e . " Insert a fence for segmented lists. " this divider is used by otl2html.py to create '
' map - o----------------------------------------0 imap - ---------------------------------------- " switch document between the two types of bodytext styles if use_space_colon == 1 " First, convert document to the marker style map b :%s/\(^\t*\) :/\1/e:%s/\(^\t*\) /\1 : /e:let @/="" " Now, convert document to the space style map B :%s/\(^\t*\) :/\1/e:let @/="" else " First, convert document to the marker style map b :%s/\(^\t*\):/\1/e:%s/\(^\t*\) /\1: /e:let @/="" " Now, convert document to the space style map B :%s/\(^\t*\):/\1/e:let @/="" endif " Steve's additional mappings start here map map map 0 :set foldlevel=99999 map 9 :set foldlevel=8 map 8 :set foldlevel=7 map 7 :set foldlevel=6 map 6 :set foldlevel=5 map 5 :set foldlevel=4 map 4 :set foldlevel=3 map 3 :set foldlevel=2 map 2 :set foldlevel=1 map 1 :set foldlevel=0 "next line commented out due to hard-coded nature and ancient, nonexistent file "map ,, :runtime vimoutliner/vimoutlinerrc map! w :wa nmap e :call Spawn() " Steve's additional mappings end here " End of Vim Outliner Key Mappings }}}1 " The End " vim600: set foldmethod=marker foldlevel=0: