diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/doc/voom.txt b/vim_plugins_src/VOoM-4.3/VOoM-4.3/doc/voom.txt new file mode 100644 index 00000000..21bf652b --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/doc/voom.txt @@ -0,0 +1,4122 @@ +*voom.txt* VOoM -- Vim two-pane outliner +Last Modified: 2012-05-06 +VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +Version: 4.3 +Website: http://www.vim.org/scripts/script.php?script_id=2657 +Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +License: WTFPL Version 2, see http://sam.zoy.org/wtfpl/COPYING + + + Overview . . . . . . . . . . . . . . . . . . . .|voom_overview| + Requirements . . . . . . . . . . . . . . . . . .|voom_requirements| + Installation . . . . . . . . . . . . . . . . . .|voom_install| + Options . . . . . . . . . . . . . . . . . . . . .|voom_options| + ALL MAPPINGS & COMMANDS . . . . . . . . . . . . .|voom_map| + OUTLINING (:Voom) . . . . . . . . . . . . . . . .|voom_Voom| + EXECUTING NODES (:Voomexec) . . . . . . . . . . .|voom_Voomexec| + __PyLog__ BUFFER (:Voomlog) . . . . . . . . . . .|voom_Voomlog| + Add-ons . . . . . . . . . . . . . . . . . . . . .|voom_addons| + Implementation notes . . . . . . . . . . . . . .|voom_notes| + + +============================================================================== +Overview [[[1~ + *voom_overview* +VOoM (Vim Outliner of Markers) is a plugin for Vim that emulates a two-pane +text outliner. + +Screenshots and an animation: http://vim-voom.github.com/ + +VOoM was originally written to work with start fold markers with level numbers, +such as in this help file. This is the most versatile outline markup -- it is +suitable for organizing all kinds of files, including source code, and it +allows features not possible with other markups (|fold-marker|). +Markers are specified by option 'foldmarker'. End fold markers with levels are +not supported. + +VOoM currently can handle several other markup formats that have headlines and +support an outline structure. (Headlines are also called headings, headers, +section headers, titles.) Available markup modes: + wiki |voom_mode_wiki| + vimwiki |voom_mode_vimwiki| + viki |voom_mode_viki| + org |voom_mode_org| + rest |voom_mode_rest| + markdown |voom_mode_markdown| + hashes |voom_mode_hashes| + txt2tags |voom_mode_txt2tags| + asciidoc |voom_mode_asciidoc| + html |voom_mode_html| + thevimoutliner |voom_mode_thevimoutliner| + vimoutliner |voom_mode_vimoutliner| + python |voom_mode_python| + fmr1, fmr2 |voom_mode_fmr| + various other |voom_mode_various| + + +FEATURES: + - Works with Vim buffers, not with files on disk as ctags-based tools. + - Automatic outline update on entering the Tree buffer. + - Not a 'filetype' plugin. Not tied to a particular outline format. + Has (almost) no side effects on the buffer being outlined. + - Fast and efficient enough to handle MB-sized files with >1000 headlines. + (Some modes can be slower.) + - Many one-character mappings for efficient outline navigation, which can + be combined into complex commands, e.g., "UVD" selects all siblings of + the current node. Outlines can also be navigated with the mouse. + - Outline structure manipulation: move nodes Up/Down, Promote/Demote, + Copy/Cut/Paste, Insert New Headline, Sort in various ways. + - Command to search nodes. Boolean AND/NOT search (in addition to |/bar|). + + +There are four main Ex commands: Voom, Voomhelp, Voomexec, Voomlog. + +:Voom +:Voom {MarkupMode} + Create outline of the current buffer. By default, outline is + constructed from lines with start fold markers with level numbers. + To work with headlines in a different format, an argument + specifying the desired markup mode must be provided, see above and + |voom_markup_modes|. + Outline is displayed in a special buffer in a separate window which + emulates the tree pane of a two-pane outliner. Such buffers are + referred to as Tree buffers. The current buffer becomes a Body + buffer. Each Tree line is associated with a region (node) of the + corresponding source buffer (Body). Nodes can be navigated and + manipulated in the Tree: moved up/down, promoted/demoted, + copied/cut/pasted, marked/unmarked, sorted, etc. + See OUTLINING (|voom_Voom|) for details. + + *voom_Voomhelp* +:Voomhelp Open help file voom.txt as outline in a new tabpage. If voom.txt + is installed via |helptags|, it is opened as a Vim help file + (:tab help voom.txt) so that all tags will be active. + + +The VOoM plugin includes two utilities useful when working with Vim and Python +scripts -- commands :Voomexec and :Voomlog. They can be used independently of +the outlining functionality provided by the command :Voom. These commands +attempt to emulate similar features of Leo outlining editor. A Python file with +code snippets organized via fold markers, plus the command :Voomexec, plus the +PyLog buffer is an alternative to running Python's interactive interpreter. + +:Voomexec Execute text in the current node or fold as Vim script or Python + script. This is useful for testing code snippets and for + organizing scripts by segregating them into folds. This command + does not require an outline to be created and can be used with any + buffer that has folds and has fold method set to marker. + See EXECUTING SCRIPTS (|voom_Voomexec|) for details. + +:Voomlog Create scratch buffer __PyLog__ and redirect Python's stdout and + stderr to it. This is useful when developing Python scripts and + when scripting Vim with Python. This feature is not related to + folding or outlining and is completely independent from the rest of + the plugin. + See __PyLog__ BUFFER (|voom_Voomlog|) for details. + + +============================================================================== +QUICK DEMO (no installation needed) [[[2~ + +Extract VOoM archive to any folder. Open "plugin/voom.vim" in Vim, and do > + :so % + :Voom +This will create Tree buffer for "voom.vim", which will become a Body buffer. + +All VOoM mappings, except Return and Tab, are for Tree buffers only. + +, , , arrow keys move around the Tree and select new +node (Normal mode). + + selects node under the cursor and then cycles between Tree and Body. +So, to select another node, move to it with h, j, etc. and hit Return. + + cycles between Tree and Body windows without selecting node. + + expands/contracts node without selecting it. Standard Vim folding +command (zo, zc, zR, zM, etc.) can be used as well. + +Left mouse click in the Tree selects node. If the click is outside of headline +text, the node's expanded/contracted status is toggled. + +Edit a headline (line with a start fold marker) in "voom.vim" and go back into +the Tree: the outline will be updated. + +, move node or a range of sibling nodes Up/Down. +, move nodes Left/Right (promote/demote). +(If the above CTRL mappings are not recognized by your Vim, you can also move +nodes Up/Down/Left/Right with ^^ __ << >> or u/d/l/r .) + +Execute the command :Voomhelp to see all commands and mappings. + +To create outline for another buffer, execute the command :Voom for it: > + :tab h netrw + :Voom +Folder "voom_samples" contains some outlines to experiment with. +File "calendar_outline.txt" is a rather large outline for stress-testing +purposes: 3.1 MB, 56527 lines, 4160 headlines. + +To outline the most common Wiki format (headlines marked by strings of =) > + :Voom wiki + +To try Python Log Buffer feature: > + :Vooml + :py assert 2==3 + :py print u"\u042D \u042E \u042F" + :py import this + +============================================================================== +Limitations [[[2~ + +============================================================================== +File size [[[3~ + +VOoM outlining is not scalable to large outlines. The bottleneck is the brute +force update of outline data. Such update, which scans Body for headlines and +recreates outline, must be done whenever the user enters a Tree buffer after +modifying corresponding Body--we can't possibly know what the user did with the +Body while he was away from the Tree. + +Sample outline "calendar_outline.txt" seems to be approaching the usable size +limit on my 2002 notebook (1.6GHz Pentium 4 Mobile): > + 3.2 Mb, 56527 lines, 4160 headlines. +When moving to Tree after modifying Body, the pause due to outline update is +noticeable but is still less than a second. + +Browsing an outline is fast regardless of it's size. + +In case of stress test file "calendar_outline.txt", the time-consuming step is +not just scanning for fold markers, but also comparing >4000 headlines between +the old and new outlines, or, if outlines are very different, setting all +lines in the Tree buffer. This means that even larger files can be outlined +comfortably if they have much fewer headlines. + +============================================================================== +Numbered Markers: Pros and Cons [[[3~ + +Start fold markers with levels have many advantages: + - It's a built-in Vim folding method (:set fdm=marker). + - Fast folding suitable for Mb-sized files with >1000 headlines. + - More flexible than indent-based or syntax-based folding. Suitable for + outlining of most file types, including source code. + - Easy to parse and to search for. Area after level number is a natural + place for storing node attributes. + - Fold markers without levels are handy for folding smaller regions. + +One drawback of numbered fold markers is that inserting them is somewhat +awkward and slow. This is not a big deal if outline nodes have a lot of body +text: most of the time is spent writing body text rather than creating +headlines (also known as headings). For outlines that consist mostly of +headlines (e.g., a shopping list) an indent based outlining mode is more +appropriate. See VO (VimOutliner) and TVO (The Vim Outliner) plugins. + +P.S. I wrote a simple plugin that helps insert start fold markers with levels: +http://www.vim.org/scripts/script.php?script_id=2891 + +P.S. Version 4.0 added support for outline markups other than start fold +markers with levels, including common Wiki markups (|voom_markup_modes|). + +============================================================================== +VOoM is not a 'filetype' plugin [[[3~ + +This is a design philosophy rather than a limitation. VOoM is expected to work +with files of any 'filetype': source code, plain text notes, Vim help file, a +large wiki file, a custom GTD format. The command :Voom, which creates outline, +does not configure the current buffer (Body) in any substantial way: it does +not set Body syntax highlighting, indent settings, folding settings, mappings +(with the exception of |voom_shuttle_keys|). + +In other words, VOoM is designed to have (almost) no side effects on the buffer +being outlined (Body). All mappings are bound to the Tree pane (except for +shuttle keys). + +In contrast, other text outliners are usually geared toward taking notes and +managing tasks (VO, TVO, Emacs Org-mode). They use special format and typically +have features such as: custom syntax highlighting and folding, a tagging +system, clickable URLs, intra- and inter-outline linking, mappings to insert +dates and other things. VOoM does not provide such features because they should +be 'filetype'-specific. + +============================================================================== +Other Text Outliners [[[2~ + +Leo outlining editor: + http://webpages.charter.net/edreamleo/front.html + - The __PyLog__ buffer, which is created by the command :Voomlog, is the + equivalent of Leo's log pane. + - The :Voomexec command is like Leo's Execute Script command when executed + in a node which contains the @others directive. + - Mark/Unmark nodes operations are modeled after identical Leo commands. + - Like Leo, VOoM can save which nodes in the Tree are expanded/contracted + and which node is the selected node. The difference from Leo is that this + is done manually via Tree commands and mappings. + +The "Tag List" Vim plugin: + http://vim.sourceforge.net/scripts/script.php?script_id=273 + - Conceptually, VOoM is similar to the "Tag List" plugin and other source + code browsers. "Tag List" uses the "ctags" program to scan files for + tags. VOoM uses Python script to scan Vim buffer for start fold markers + with levels or some other headline markers. + +Other Vim scripts for outlining are listed at + http://vim.wikia.com/wiki/Script:List_of_scripts_for_outlining?useskin=monobook + +Emacs Org-mode: + http://orgmode.org/ +Emacs oultining modes: + http://www.emacswiki.org/emacs/CategoryOutline + +Code Browser: + http://code-browser.sourceforge.net/ + +Listings of outliner programs: + http://en.wikipedia.org/wiki/Outliner + http://www.psychinnovations.com/directory/outliners-mind-maps + http://texteditors.org/cgi-bin/wiki.pl?OutlinerFamily + http://www.marktaw.com/reviews/Outliners.html + http://www.outlinersoftware.com/topics/viewt/807/0/list-of-outliners + +============================================================================== +Requirements [[[1~ + *voom_requirements* +VOoM uses Python and requires Python-enabled Vim 7.x, that is Vim compiled +with the Python interface. Your Vim is Python-enabled if it can do > + :py print 2**0.5 + :py import sys; print sys.version + +Python version should be 2.4 - 2.7. Python 3 is not supported. + +Vim version 7.2 or above is preferred. Version 7.1 should also work. +Version 7.0 might work as well but has not been tested. + +Vim should be compiled using normal or bigger feature set. + +Vim patch 7.2.161 is required in order to be able to work on the same outline +(or any buffer with folds) in separate tabpages. + +============================================================================== +Vim and Python on Windows [[[2~ + +Getting Vim and Python to work together on Windows can be a bit tricky +(|python-dynamic|). + - Obviously, Python must be installed. Use Python version 2.6 or 2.7 + Windows installer from http://www.python.org/ . The installer will put + Python DLL in system search path. + - Vim must be compiled with the Python interface (:echo has("python")). + - Finally, the version of Python DLL against which Vim was compiled must + match the installed Python version. + +There are several Windows Vim installers (version 7.3). +Installer from vim.org, http://www.vim.org/download.php#pc, probably installs +Vim compiled against Python 2.7. +Installer from http://sourceforge.net/projects/cream/files/ (gVim one-click +installer for Windows) has Vim compiled against Python 2.6 according to release +notes. + +It is not hard to compile your own Python-enabled gvim.exe and vim.exe. See +http://vim.wikia.com/wiki/Build_Python-enabled_Vim_on_Windows_with_MinGW?useskin=monobook + +============================================================================== +Installation [[[1~ + *voom_install* +Copy content of the plugin folder (file "voom.vim", directory "voom" with +Python files) to your local Vim plugin folder: > + $HOME/vimfiles/plugin/ (Windows) + $HOME/.vim/plugin/ (*nix) +Copy "doc/voom.txt" to the local doc folder. > + $HOME/vimfiles/doc/ (Windows) + $HOME/.vim/doc/ (*nix) +This will make commands Voom, Voomlog, Voomexec, Voomhelp available in any +buffer. + +Execute the :helptags command to install "voom.txt" as Vim help and to +generate help tags (|add-local-help|): > + :helptags $HOME/vimfiles/doc + :helptags $HOME/.vim/doc + +VOoM can also be run from any directory without installing anything, see + Overview -> QUICK DEMO. + +NOTE: VOoM uses quickload mechanism (|write-plugin-quickload|). The bulk of +the script "voom.vim" is sourced and Python module "voom.py" is imported only +after a Voom command is executed for the first time. + +NOTE: VOoM Python modules are located in folder "voom" which must be in the +directory of "voom.vim". When "voom.vim" is sourced, its Python code adds +"voom" directory to sys.path and then imports "voom.py". This creates file +"voom.pyc" if needed. + +============================================================================== +Options [[[1o~ + *voom_options* +============================================================================== +Vim Options [[[2~ + +When outline of the current buffer is created by the command :Voom, the +following Vim options determine how outline is constructed: +(NOTE: not applicable when a markup mode is specified) + + - 'foldmarker' is used to obtain the start fold marker string. There is + rarely a reason to change this option from default, which is {{{,}}} . + + - 'commentstring' and 'filetype' affect how Tree headline text is + constructed. For details, see node + OUTLINING (:Voom) -> Create Outline -> Tree Headline Text + +'foldmethod' for the buffer for which the command :Voom is executed should be +"marker" (:set fdm=marker). This, however, is not required to create an +outline or to use it. Outline operations do not rely on Vim folds, they use +start fold markers with levels. Other folding options (|fold-options|), such +as 'foldtext', can be set according to personal preferences and are usually +'filetype'-specific. + + is used to start many outline operations while in a Tree buffer. +By default, it's backslash. For example, "\i" inserts new node. To change it +to another character, assign maplocalleader in .vimrc: > + let maplocalleader=',' + +'scrolloff' should be 0 (default) or a small number (1 or 2). This global +option affects how the headline is positioned in Body window after selecting +node in Tree window. For example, after :set scrolloff=1, the headline will be +on the 2nd window line in Body window. A very large value can be confusing +when switching between Tree and Body windows. + +Vim commands for creating and deleting folds are not very useful and are +potentially dangerous when typed accidentally. They can be disabled in .vimrc +as follows: > + " Disable commands for creating and deleting folds. + noremap zf + noremap zF + noremap zd + noremap zD + noremap zE + +Some color schemes (including default) use the same or similar background +colors for selected text (Visual), folded lines (Folded), and current line +(CursorLine) highlight groups. These highlight groups are used in Tree buffers +and it's better if they are easily distinguished from each other. + +============================================================================== +VOoM Options [[[2o~ + +VOoM options are Vim global variables that can be defined by users in their +.vimrc files. Example: > + let g:voom_tree_placement = "top" + let g:voom_tree_height = 14 + +============================================================================== +Window positioning [[[3~ + +g:voom_tree_placement ~ + Where Tree window is created: "left", "right", "top", "bottom" + This is relative to the current window. + Default: "left" + +g:voom_tree_width ~ + Initial Tree window width. + Default: 30 + +g:voom_tree_height ~ + Initial Tree window height. + Default: 12 + +g:voom_log_placement ~ + Where __PyLog__ window is created: "left", "right", "top", "bottom" + This is far left/right/top/bottom. + Default: "bottom" + +g:voom_log_width ~ + Initial __PyLog__ window width. + Default: 30 + +g:voom_log_height ~ + Initial __PyLog__ window height. + Default: 12 + +============================================================================== +Tree/Body shuttle keys [[[3~ + *voom_shuttle_keys* +Since VOoM emulates a two-pane outliner, it's important to have keys that +shuttle between the two panes. By default, such keys are and . +These keys are used in buffer-local mappings in Trees (Normal and Visual +modes) and in Bodies (Normal mode). + +These are the only keys that get mapped in Body buffer when the command :Voom +is executed. + +Note that these keys have default meaning in Vim: + + moves cursor down. This is not very useful since "j" does almost + the same thing. + + By default, /CTRL-I in Normal mode goes to newer position in the jump + list (opposite of CTRL-O, see |CTRL-I|). Thus, although tempting, mapping + is usually a bad idea. It seems that Ctrl-Tab still works like + default /CTRL-I, at least in GUI Vim, when is mapped. + +The following two settings allow to use keys or key combinations other than + and . + +g:voom_return_key ~ + Mapping that selects node under the cursor and, if the node is already + selected, shuttles between Tree and Body windows. + Default: "" + +g:voom_tab_key ~ + Mapping that shuttles between Tree and Body windows without selecting + node. + Default: "" + +Example, use Ctrl-Return and Ctrl-Tab: > + let g:voom_return_key = '' + let g:voom_tab_key = '' + +============================================================================== +g:voom_ft_modes, g:voom_default_mode [[[3~ + *g:voom_ft_modes* *g:voom_default_mode* +By default, the :Voom command without an argument creates outline from lines +with start fold markers with level numbers (the default mode). To outline +another format, an argument specifying the desired markup mode must be +provided. E.g., for a Markdown (MultiMarkdown, Pandoc) file: > + :Voom markdown + +User options "g:voom_ft_modes" and "g:voom_default_mode" change which markup +mode the command :Voom will use when it is invoked without an argument. These +variables do not exist by default, they must be created by the user in .vimrc. + +g:voom_ft_modes ~ +"g:voom_ft_modes" is a Vim dictionary: keys are filetypes (|ft|), values are +corresponding markup modes (|voom_markup_modes|). Example: > + let g:voom_ft_modes = {'markdown': 'markdown', 'pandoc': 'markdown'} + +This option allows automatic selection of markup mode according to filetype of +the source buffer. If "g:voom_ft_modes" is defined as above, and 'filetype' of +the current buffer is "markdown" or "pandoc", then the command > + :Voom +is identical to the command > + :Voom markdown + +g:voom_default_mode ~ +"g:voom_default_mode" is a string with the name of the default markup mode. +Example, if there is this in vimr: > + let g:voom_default_mode = 'asciidoc' +then, the command > + :Voom +is equivalent to > + :Voom asciidoc +unless "g:voom_ft_modes" is defined and has an entry for the current filetype. + +NOTE: To overide these two options, that is to force the original default mode, +specify the "fmr" mode (|voom_mode_fmr|): > + :Voom fmr + +============================================================================== +Various options [[[3~ + +g:voom_verify_oop ~ + Verify outline after every outline operation (doesn't apply to :VoomSort). + Default is 1 (enabled). + Set to 0 to disable (not recommended, especially with markup modes). + + This option turns on outline verification after most outline operations. + It will alert to outline corruption, which is very likely if there is a bug + in outline operation. The downside is that there is a performance hit, + usually noticeable only with large outlines (>1000 headlines). + NOTE: do not disable this option when using "rest" or "python" outlining + modes -- these markups have some intrinsic problems. + +g:voom_rstrip_chars_{filetype} ~ + NOTE: Not applicable when a non-default markup mode is used + (|voom_markup_modes|). + This variable must be created for each 'filetype' of interest. + Value is a string of characters to be stripped from the right side of Tree + headlines (from before fold marker) when the default Tree headline + construction procedure is used and Body has 'filetype' {filetype}. + Usually, the chars to be stripped are comment chars, space and tab. For + details, see node > + OUTLINING (:Voom) -> Create Outline -> Tree Headline Text +< + Defaults exist for filetypes "vim", "text", "help": > + let g:voom_rstrip_chars_vim = "\"# \t" + let g:voom_rstrip_chars_text = " \t" + let g:voom_rstrip_chars_help = " \t" + +g:voom_user_command ~ + This option allows to execute an arbitrary user-defined command when file + voom.vim is sourced. It is a string to be executed via |execute| at the + very end of voom.vim. It does not exist by default. This option is intended + for loading user add-ons. For details, see |voom_addons|. + +g:voom_create_devel_commands ~ + If this variable exists, several commands are created to help during VOoM + development. See "Commands" node in voom.vim for details. + +============================================================================== +Customization tips [[[3~ + +When a Tree buffer is created, its 'filetype' is set to "voomtree" +When __PyLog__ buffer is created, its 'filetype' is set to "voomlog". +This should allow user customization of these buffers (bufhidden, syntax, +wrap/norwap, list/nolist, etc.) via standard Vim configuration files: > + $HOME/.vim/ftplugin/voomtree.vim + $HOME/.vim/syntax/voomtree.vim + $HOME/.vim/after/syntax/log.vim + etc. + +To modify default Tree buffer-local mappings or create new ones: + 1. Create file ftplugin/voomtree.vim . + 2. Copy relevant mappings from voom.vim function Voom_TreeMap(). + 3. Change {lhs} and/or {rhs}. + +Most VOoM commands can be mapped to key shortcuts in .vimrc: > + nnoremap :Voom + nnoremap n :Voomunl + + +To make Body headlines stand out, lines with fold markers can be highlighted. +Since I use .txt files for notes, I have the following line in .vimrc > + au BufWinEnter *.txt if &ft==#'text' | exe 'syn match ModeMsg /\V\.\*' . split(&fmr, ',')[0] . '\.\*/' | endif +This method is better than using syntax/txt.vim because it also works when a +nonstandard foldmarker is specified on file's modeline. + +============================================================================== +# ALL MAPPINGS & COMMANDS # [[[1x= ~ + *voom_map* +------------------------------------------------------------------------------ +MAIN COMMANDS ~ +------------------------------------------------------------------------------ +:Voom Create outline of the current buffer. |voom_Voom| +:Voom {MarkupName} Create outline using markup mode defined in module + "voom_mode_{MarkupName}.py". |voom_markup_modes| +:Voomhelp Open voom.txt as outline in a new tabpage. |voom_Voomhelp| +:Voomexec [vim|py] Execute node or fold as [type] script. |voom_Voomexec| +:Voomlog Create __PyLog__ buffer. |voom_Voomlog| + +------------------------------------------------------------------------------ +SHUTTLE KEYS (BODY AND TREE BUFFERS) ~ +------------------------------------------------------------------------------ +These cycle between Tree and Body windows. Configurable by the user. +Body: Normal mode. Tree: Normal and Visual modes. |voom_shuttle_keys| + + Select node under the cursor. If already selected, move + cursor to Tree or Body window. A Tree or Body window is + created in the current tabpage if there is none. + + Move cursor to Tree or Body window. + + +------------------------------------------------------------------------------ +OUTLINE NAVIGATION (TREE BUFFER) ~ +------------------------------------------------------------------------------ + Mouse left button click. Select node under mouse. + Toggle node's expanded/contracted state if the click is + outside of headline text. (N) +<2-LeftMouse> Mouse left button double-click. Disabled. + + Move cursor Up and select new node. (N) + + Move cursor Down and select new node. (N) + + Move cursor to the first child and select it. (N) + + Move cursor to the parent and select it. (N) + If the current node is expanded, it is contracted first. + +------------------------------------------------------------------------------ +expand/contract nodes +------------------------------------------------------------------------------ +zc, zo, zM, zR, zv, etc. + These are Vim's standard folding commands. + They expand/contract nodes (|fold-commands|). + Note: zf, zF, zd, zD, zE are disabled. + + Expand/contract the current node (node under the cursor). (N) + +O Recursively expand the current node and its siblings. (N) + Recursively expand all nodes in Visual selection. (V) + Similar to |zO|. + +C Recursively contract the current node and its siblings. (N) + Recursively contract all nodes in Visual selection. (V) + Similar to |zC|. + +------------------------------------------------------------------------------ +move cursor to another node (in addition to j, k, H, M, L, etc.) +------------------------------------------------------------------------------ +o Down to the first child of the current node (like |zo|). (N) + +c Up to the parent node and contract it (like |zc|). (N) +P Up to the parent node. (N) + +K Up to the previous sibling. (N,V,count) +J Down to the next sibling. (N,V,count) + +U Up to the uppermost sibling. (N,V) +D Down to the downmost sibling. (N,V) + += Put cursor on the currently selected node. (N) + +------------------------------------------------------------------------------ +go to specially marked node |voom_special_marks| +------------------------------------------------------------------------------ +x Go to next marked node (find headline marked with 'x'). (N) +X Go to previous marked node. (N) + ++ Put cursor on the startup node (node with '=' mark in Body + headline). Warns if there are several such nodes. (N) + +------------------------------------------------------------------------------ +show (echo) information for node under the cursor +------------------------------------------------------------------------------ +s Show Tree headline (text after first '|'). (N) +S Show UNL. Same as :Voomunl (|voom_Voomunl|). (N) + + +------------------------------------------------------------------------------ +OUTLINE OPERATIONS (TREE BUFFER) ~ +------------------------------------------------------------------------------ +i I a A Edit headline of node under the cursor. (N) + +R Switch to Body buffer, select the line range corresponding + to the current node or to nodes in Visual selection. (N,V) + +i Insert new node after the current node. (N) + +I Insert new node as first child of the current node. (N) + + +^^ +u Move node(s) Up. (N,V) + + +__ +d Move node(s) Down. (N,V) + + +<< +l Move node(s) Left (Promote). (N,V) + Nodes must be at the end of their subtree. + +>> +r Move node(s) Right (Demote). (N,V) + +------------------------------------------------------------------------------ +COPY/CUT/PASTE (these use Vim's + register: system clipboard) +------------------------------------------------------------------------------ +yy Copy node(s). (N,V) + +dd Cut node(s). (N,V) + +pp Paste node(s) after the current node. (N) + +------------------------------------------------------------------------------ +MARK/UNMARK |voom_special_marks| +------------------------------------------------------------------------------ +m Mark node(s): add 'x' to Body headlines. (N,V) + +M Unmark node(s): remove 'x' from Body headlines. (N,V) + += Mark node as startup node: add '=' to Body headline and + remove '=' from all other headlines. When cursor is on + Tree line 1, all '=' marks are removed. (N) + +------------------------------------------------------------------------------ +SORTING |voom_sort| +------------------------------------------------------------------------------ +:VoomSort [options] Sort siblings of node under the cursor. + Options are: "deep" (also sort all descendant nodes), + "i" (ignore-case), "u" (Unicode-aware), "r" (reverse-sort), + "flip" (reverse), "shuffle". + +:[range]VoomSort [options] + Sort siblings in the [range], start and end range lines + must be different. + +------------------------------------------------------------------------------ +SAVE/RESTORE TREE BUFFER FOLDING |voom_tree_folding| +------------------------------------------------------------------------------ +:[range]VoomFoldingSave + Save Tree folding (writes 'o' marks in Body headlines). + +:[range]VoomFoldingRestore + Restore Tree folding (reads 'o' marks in Body headlines). + +:[range]VoomFoldingCleanup + Cleanup 'o' marks: remove them from nodes without children. + +fs Save Tree folding for the current node and all descendant + nodes. Same as :VoomFoldingSave. (N) + +fr Restore Tree folding for the current node and all descendant + nodes. Same as :VoomFoldingRestore. (N) + +fas Save Tree folding for entire outline. + Same as :%VoomFoldingSave. (N) + +far Restore Tree folding for entire outline. + Same as :%VoomFoldingRestore. (N) + + +------------------------------------------------------------------------------ +SEARCH NODES (Body and Tree buffers) ~ +------------------------------------------------------------------------------ +:Voomunl Display node's UNL: Uniform Node Locator. |voom_Voomunl| + +:Voomgrep [pattern(s)] + Search current outline for pattern and display results in + the quickfix window as list of UNLs of nodes with matches. + Performs boolean AND and NOT searches if there are several + patterns separated by words "and" or "not". Uses word at + cursor if no patterns are provided. |voom_Voomgrep| + +------------------------------------------------------------------------------ +QUIT (DELETE), TOGGLE OUTLINE ~ +------------------------------------------------------------------------------ + (see |voom_quit|) +q Delete outline. (Tree buffer Normal mode mapping) +:Voomquit Delete outline. (Tree or Body buffer) +:VoomQuitAll Delete all VOoM outlines. (any buffer) +:VoomToggle [MarkupMode] + Create outline if current buffer is a non-VOoM buffer. + Delete outline if current buffer is a Tree or Body buffer. +:Voomtoggle Minimize/Restore Tree window. (Tree or Body) + +------------------------------------------------------------------------------ +VARIOUS ~ +------------------------------------------------------------------------------ + +e Execute node. Same as :Voomexec. Tree buffer only. (N) + +Several additional commands is created if there exists variable +"g:voom_create_devel_commands". These commands are useful only during VOoM +development. See node "Commands" in ../plugin/voom.vim for details. + + +============================================================================== +OUTLINING (:Voom) [[[1o~ + +============================================================================== +Create Outline [[[2o~ + *voom_Voom* +:Voom [MarkupMode] + Scan the current buffer for headlines, construct an indent-based + outline from them, and display it in a specially configured, + non-modifiable buffer called Tree buffer. The current buffer + becomes a Body buffer. + +:Voom By default, headlines are lines with start fold markers with level + numbers: {{{3, {{{1, etc. The level of each headline is set to the + number after the fold marker. Headline text is part of line before + the fold marker (this can be customized). + + NOTE: End fold markers with levels, }}}1, }}}3, etc., are ignored + and should not be used. + + Matching fold markers without level numbers, {{{ and }}}, are + ignored. They are handy for folding small areas inside numbered + folds, e.g. parts of functions. The region between {{{ and }}} + should not contain fold markers with levels. + + For best results, Body 'foldmethod' should be "marker" + (|fold-marker|). If this is the case, Body nodes are also folds. + This is not required. Body buffer folding has no effect on outline + construction or outline operations. + +:Voom {MarkupMode} + The format of headlines is specified by the {MarkupMode} + (|voom_markup_modes|). + +NOTE: A TREE BUFFER IS NOT MODIFIABLE AND SHOULD NEVER BE EDITED DIRECTLY. +It has many buffer-local mappings for navigating the outline and for performing +outline operations. Most of Vim standard Normal and Visual text change commands +are either disabled or remapped. + +Tree buffers are named {bufname}_VOOM{bufnr} where {bufname} and {bufnr} are +the name and number of the corresponding source buffer (Body). The 'filetype' +of Tree buffers is set to "voomtree". + +A Tree buffer is displayed in a separate window. It is configured to behave as +the tree pane of a two-pane outliner. Every line in a Tree buffer is associated +with a node of the corresponding Body. + +Each "node" is a range of Body buffer lines beginning with headline and ending +before the next headline (or end-of-buffer). The first Tree line (outline +title) is treated as a special node number 1: it is associated with the region +from start of Body buffer to its first headline (or end-of-file); it has zero +lines if the first Body line is a headline. + +When a headline is selected in a Tree window (, , , , +), the corresponding node is displayed in Body window. Nodes can be +manipulated from Tree window: deleted, moved, promoted, demoted, marked, etc. +Obviously, Body buffers can be edited directly as any other buffers with fold +markers. + +The outline data and the Tree are updated automatically on entering the Tree +buffer (on |BufEnter|). The actual update happens if the Body has been +modified since the last update (when Body's |b:changedtick-variable| is +different). This update is the bottleneck that limits the size of outlines +that can be edited comfortably. + +A Body buffer is not configured in any substantial way by the command :Voom. +It has only two VOoM-specific mappings: and in Normal mode +(local to buffer). These mappings select node under the cursor and cycle +between Body and Tree windows. These two mappings can be changed by the user +(|voom_shuttle_keys|). The user is responsible for setting all other Body +settings to his liking: folding, indenting, syntax highlighting and so on +(these are usually determined by Body 'filetype'). + +============================================================================== +About Fold Markers [[[3~ + +The command :Voom does not create an outline of folds. It creates an outline +of start fold markers with level numbers. When Body has 'foldmethod' set to +"marker", lines in Tree buffer also represent Body folds. + +The start fold marker string is obtained from window-local option 'foldmarker' +when outline is created by the command :Voom. For example, after > + :set fmr=<<<,>>> + :Voom +the outline will be created from lines with <<<1, <<<2, <<<3, etc. + +'foldmarker' should not be changed while using an outline. If you change it, +make sure to recreate the outline: delete Tree buffer and execute the command +:Voom again. + +VOoM scans only for **start** fold markers with level numbers. End fold markers +with levels and fold markers without levels are ignored. This assumes that the +user follows certain rules of using fold markers. These rules make a lot of +sense and are similar to recommendations given in Vim help (|fold-marker|). + +1) Use start fold markers with levels, <<<1, <<<2, etc. to start new + fold/node. These should correspond to important structures: parts and + chapters in a book, functions and classes in a code. + +2) DO NOT USE END FOLD MARKERS WITH LEVELS: >>>1, >>>2, etc. They are + redundant and are hard to keep track of in a large outline. + +3) Do use pairs of matching fold markers without level, <<< and >>>, to fold + small areas of text (a screenful), such as parts of functions. Make sure + the area doesn't contain any fold markers with levels. + +Files that do have end fold markers with levels are ok for browsing with VOoM, +but outline operations will most definitely produce unintended results. +Consider the following structure: > + node 0 + node 1 <<<1 + node 1.1 <<<2 + >>>1 + ? ? ? ? + ? ? ? ? + node 2 <<<1 + node 3 <<<1 +Lines with ? are not part of any fold. But VOoM considers them part of node +1.1 and will move them accordingly when node 1.1 is moved. When node's level +is changed, only number after the start fold marker is updated. + +============================================================================== +Special Node Marks [[[3~ + *voom_special_marks* +NOTE: Special node marks are available only when outlining start fold markers +with levels. They are not available when using a markup mode +(|voom_markup_modes|) unless it's an "fmr" mode (|voom_mode_fmr|). + +The following characters in Body headline immediately after the start fold +marker level number have special meaning. They are used by VOoM to indicate +node properties: + 'x' - Node is marked. This is like a checked checkbox. If the node is + marked, 'x' is displayed in the second column of Tree buffer. + 'o' - Node is opened (expanded). The corresponding Tree buffer fold + will be opened when outline is created by the command :Voom. + Obviously, this applies only to nodes with children. + '=' - Startup node. This node will be selected when outline is created + by the command :Voom. + +Various VOoM mappings and commands read and write these special marks. + +Each mark is optional, but the order must be xo= . Examples, assuming that +foldmarker is set to <<<,>>> : > + headline <<<1xo= --node is marked, opened, startup node + headline <<<1xo --node is marked, opened + headline <<<1o --node is opened + headline <<<1x= --node is marked, startup node + + headline <<<1=xo --node is startup node, 'x' and 'o' are ignored + headline <<<1 xo= --all marks are ignored + +============================================================================== +~~~===--- Tree Headline Text ---===~~~ [[[3~ + +NOTE: This section does not apply when a markup mode is in use (|voom_markup_modes|). + +Tree headline text is constructed from the corresponding Body buffer headline. +The default procedure is to take part before the matching fold marker and to +strip whitespace and other distracting characters. The exact procedure depends +on Body 'filetype' and can be customized by the user. For most filetypes, the +following happens: + - Part of the Body line before the first start fold marker with level + number is taken. + - Whitespace is stripped from the left side. + - Spaces, tabs, and comment characters are stripped from the right side. + Which chars are comment chars is determined by option 'commentstring', + or by user option "g:voom_rstrip_chars_{filetype}", see below. + - Leading and trailing filler chars -=~ are removed. These chars can be + used as decorators to make headlines stand out. + - Whitespace is stripped again on both ends. + +In step 3, characters that are stripped from the right side of headline (from +before the fold marker) are determined as follows: + - If variable "g:voom_rstrip_chars_{filetype}" exists, it's value is used. + {filetype} here is Body 'filetype'. Value is string of characters to be + stripped from the right side (Space and Tab must be included). + - If "g:voom_rstrip_chars_{filetype}" does not exist, comment characters + are obtained from option 'commentstring'. They, Spaces, and Tabs are + stripped from the right side. + +By default, "g:voom_rstrip_chars_{filetype}" are defined for filetypes "vim", +"text" and "help". For most source code filetypes 'commentstring' is set +correctly by the corresponding ftplugin. If not defined, 'commentstring' +defaults to /*%s*/, which makes no sense for filetypes like text and help. + +So, to change what characters are stripped from the right side of Tree +headlines for particular Body filetypes, you can either set 'commentstring' or +you can define "g:voom_rstrip_chars_{filetype}" in vimrc (or in an add-on). +Example for "autohotkey" filetype, ';' is line comment char: > + let g:voom_rstrip_chars_autohotkey = "; \t" + +The above procedure can be replaced completely by a custom Python function +which returns Tree headline text. The function must be registered in Python +dictionary voom.MAKE_HEAD: key is Body filetype, value is the function to be +used with this filetype. By default, this is done for "html" files (we can't +just strip + ==== headline level 4 ==== + etc. + +First = must be at the start of the line. +Closing = are required. +Trailing whitespace is ok. +Whitespace around the text is not required. + +HTML comment tags are ok if they are after the headline: > + ==== headline level 4 ==== + ===== headline level 5 ===== + + +KNOWN PROBLEMS +-------------- + +1) Headlines are not ignored inside
,  and other special blocks.
+
+2) Only trailing HTML comment tags are stripped.
+The following valid headline is not recognized: >
+    === missed me ===
+
+A comment inside headline is ok, but it will be displayed in Tree buffer: >
+    ==  headline level 2 ==
+
+
+REFERENCES
+----------
+http://www.mediawiki.org/wiki/Help:Formatting
+http://www.mediawiki.org/wiki/Markup_spec
+http://meta.wikimedia.org/wiki/Help:Section
+http://en.wikipedia.org/wiki/Help:Section
+http://en.wikipedia.org/wiki/Wikipedia:Manual_of_Style#Section_headings
+
+==============================================================================
+vimwiki   [[[3~
+                                                 *voom_mode_vimwiki*
+:Voom vimwiki
+MODULE: ../plugin/voom/voom_mode_vimwiki.py
+Headline markup used by vimwiki plugin:
+    http://www.vim.org/scripts/script.php?script_id=2226
+
+Like wiki mode except that:
+    there can be leading whitespace (centered headline?)
+    HTML comment tags are not stripped
+>
+    = headline level 1 =
+    body text
+    == headline level 2 ==
+    body text
+           === headline level 3 ===
+
+==============================================================================
+viki   [[[3~
+                                                 *voom_mode_viki*
+:Voom viki
+MODULE: ../plugin/voom/voom_mode_viki.py
+Mode for outlining Viki/Deplate headings:
+    http://www.vim.org/scripts/script.php?script_id=861
+    http://deplate.sourceforge.net/Markup.html#hd0010004
+>
+    * headline level 1
+    some text
+    ** headline level 2
+    more text
+    *** headline level 3
+    **** headline level 4
+
+The first * must be at the start of the line.
+There must be a whitespace after the last * .
+
+Headlines are ignored inside special regions other than #Region:
+    http://deplate.sourceforge.net/Regions.html
+    http://deplate.sourceforge.net/Regions.html#hd00110013
+Special regions have the following format: >
+    #Type [OPTIONS] <
+    * headline level 1
+    some text
+    ** headline level 2
+    more text
+    *** headline level 3
+    **** headline level 4
+
+The first * must be at the start of the line.
+There must be a whitespace after the last * .
+
+==============================================================================
+rest   [[[3~
+                                                 *voom_mode_rest*
+:Voom rest
+MODULE: ../plugin/voom/voom_mode_rest.py
+Mode for outlining reStructuredText (reST) section titles.
+    http://docutils.sourceforge.net/rst.html
+    http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#sections
+    http://docutils.sourceforge.net/docs/user/rst/quickstart.html#sections
+    http://docs.python.org/documenting/rest.html#sections
+
+For examples of reST files, click "Show Source" or similar link on above pages.
+Vim has reST syntax highlighting, do :set ft=rst to enable.
+
+To customize the order in which adornment styles are used: modify constant
+AD_STYLES near the top of module. It's a string containing preferred adornment
+styles in order of preference.
+
+KNOWN PROBLEMS
+--------------
+
+1) Sort can make headlines disappear if some sections are not terminated with a
+blank line (including last section, that is end of file). The command :VoomSort
+will detect that **after** sort is finished and display an error message.
+Example, sorting nodes C and B kills node C because it is moved down after B. >
+
+    =========
+    A level 1
+    =========
+
+    C level 2
+    ---------
+    some text in node C
+
+    B level 2
+    ---------
+    some text in node B
+    =========
+    E level 1
+    =========
+
+Note that there is no such problem with other outline operations--they will
+insert blank line before headline if needed to prevent headline loss. E.g., it
+is safe to move node C down.
+
+
+2) Outline verification can fail after an outline operation if there are
+inconsistent levels, that is when node's level is incremented by >1. VOoM will
+complain about different Tree lines, different levels, and force outline
+update.
+
+Example 1. Errors when moving node D up. >
+
+    A level 1
+    ===========
+
+    B level 2
+    -----------
+
+    C level 3
+    """""""""""
+
+    D level 1
+    ===========
+
+    E level 3
+    """""""""""
+
+Example 2. Errors when moving node C left. This is because node E is also moved
+left, even though it is in a different branch. >
+
+    A level 1
+    =========
+
+    B level 2
+    ---------
+
+    C level 3
+    +++++++++
+
+    D level 1
+    =========
+
+    E level 4
+    *********
+
+==============================================================================
+markdown   [[[3~
+                                                 *voom_mode_markdown*
+:Voom markdown
+MODULE: ../plugin/voom/voom_mode_markdown.py
+Mode for outlining of Markdown headers.
+    http://daringfireball.net/projects/markdown/
+    http://daringfireball.net/projects/markdown/syntax#header
+    http://daringfireball.net/projects/markdown/dingus  --online demo
+    http://babelmark.bobtfish.net/ --derivatives
+    http://fletcherpenney.net/multimarkdown/ --MultiMarkdown
+    http://johnmacfarlane.net/pandoc/ --Pandoc
+    https://gist.github.com/1035030 --Vim folding for Markdown
+
+There are two types of header styles. Both can be used in the same outline.
+
+Underline-style, levels 1 and 2 only: >
+    header level 1
+    ==============
+
+    header level 2
+    --------------
+
+Hashes-style (also see |voom_mode_hashes|): >
+    # header level 1
+    ## header level 2
+    ### header level 3 ###
+
+A blank line before or after a headline is optional. (NOTE: Pandoc version of
+Markdown does require blank lines before headlines, unlike standard Markdown.)
+One = or - in column 1 is sufficient for underline-style.
+Spaces after opening #'s and before closing #'s are optional.
+Closing #'s are optional. Their number is not important.
+
+Differences from the actual Markdown parser (according to dinguses):
+
+    - Hashes-style overrides underline-style. In Markdown, underline overrides
+      hashes: >
+        #### this is headline level 4, not level 2
+        ------------------------------------------
+<
+    - Line consisting only of hashes is interpreted as a blank headline.
+      For example, line "###" is blank headline at level 3. In contrast,
+      Markdown interprets it as header "#" at level 2, that is 

#

. + + +HOW OUTLINE OPERATIONS CHOOSE HEADLINE FORMAT +--------------------------------------------- + +When an outline operation changes headline level, it has to choose between +several possible headline formats: + - When changing to level 1 or 2, choose underline-style or hashes-style. + - When using hashes-style, choose to add not to add closing hashes. + +Outline operations try to keep the format of headlines consistent throughout +the current outline. The above ambiguities are resolved as follows: + + A) If possible, preserve the current style of headline and closing hashes. + Demoting headline + Headline + ======== + changes it to + Headline + -------- + Demoting "# Headline" changes it to "## Headline". + Demoting "#Headline#" changes it to "##Headline##". And so on. + + B) When a choice must be made between underline-style and hashes-style + (changing level from >2 to 1 or 2): the style of the first headline with + level 1 or 2 is used. The default (in case there are no headlines with + level 1 or 2) is to use underline-style. + + C) When a choice must be made to add or not to add closing hashes (changing + from underline-style to hashes-style): the first headline which is in + hashes-style is checked. If it ends with "#", then closing hashes are + added. The default (in case there are no headlines in hashes-style) is to + add closing hashes. + + The headline format is always chosen as in B) and C) above when: + - Inserting new headline. + - Pasting nodes (because we may paste from another outline with a + different style). + + +OTHER NOTES +------------ +If a headline is not preceded by a blank line, outline operations may add one. + +An underline-style headline should not contain leading or trailing #'s. + +============================================================================== +hashes [[[3~ + *voom_mode_hashes* +:Voom hashes +MODULE: ../plugin/voom/voom_mode_hashes.py +Headlines are marked by #'s. This is a subset of Markdown format (atx-style +headers): > + + # headline level 1 + ## headline level 2 + text + ###headline level 3 + ### headline level 3 + +#'s must be at the start of the line. +A whitespace after #'s is optional. + +This mode is much simpler and more efficient than the full Markdown mode +(|voom_mode_markdown|) because it does not have to deal with underlined +headlines and closing #'s. + +NOTE: This mode can be easily modified: + a) To use any ASCII character as a marker instead of '#'. + b) To require a whitespace after the marker chars (as in org-mode). + c) To strip optional closing #'s. +See comments in the module file. + +============================================================================== +txt2tags [[[3~ + *voom_mode_txt2tags* +:Voom txt2tags +MODULE: ../plugin/voom/voom_mode_txt2tags.py +Mode for outlining txt2tags titles. + http://txt2tags.org/ + http://txt2tags.org/userguide/TitleNumberedTitle.html#6_2 + +Both Titles and Numbered Titles are recognized. Anchors are OK. > + = title level 1 = + == title level 2 ==[anchor-A] + +++ numbered title level 3 +++ + +++ numbered title level 3 +++[anchor-B] + +There can be leading spaces, but not tabs. +The number of = or + chars must be the same on both sides. + +Titles are ignored inside Verbatim, Raw, and Tagged Areas, that is between +pairs of lines of ```, """, '''. These areas cannot be nested. + +In the Tree, the text of Numbered Titles is prepended with "+ " to help +distinguish them from regular titles. The type of the title is preserved during +outline operations. + +============================================================================== +asciidoc [[[3~ + *voom_mode_asciidoc* +:Voom asciidoc +MODULE: ../plugin/voom/voom_mode_asciidoc.py +Mode for outlining of AsciiDoc Document and Section Titles. + http://www.methods.co.nz/asciidoc/ + http://www.methods.co.nz/asciidoc/userguide.html#X17 +The AsciiDoc Userguide source is an example of a large and complex document: + http://www.methods.co.nz/asciidoc/userguide.txt + +Both two-line and one-line title styles are recognized and can be used in the +same outline. Document Titles, that is topmost nodes, are not treated +specially. Note that the topmost level in VOoM is level 1, not level 0 as in +AsciiDoc documentation. + + +Two-line style, levels 1 to 5 only: > + Level 1 + ======= + + Level 2 + ------- + + Level 3 + ~~~~~~~ + + Level 4 + ^^^^^^^ + + Level 5 + +++++++ + +The underline must be of the same size as the title line +/- 2 chars. +Both the underline and the title line must be at least 2 chars long. +Trailing whitespace is always ignored and is not counted. + + +One-line style: > + = Level 1 = + + == Level 2 == + + === Level 3 === + +Closing ='s are optional: > + = Level 1 + + == Level 2 + + === Level 3 + +There must be a whitespace between headline text and ='s. The number of closing +='s must match the number of opening ='s. + +One-line style overrides two-line style: > + ===== Level 5 + ------------- + listing + ------------- + +When a style must be chosen during an outline operation (when level changes or +when a new node is inserted), the style is chosen so that to preserve the +current style of the document. When that's not possible, the default is to use +two-line and to add closing ='s. See |voom_mode_markdown| for details. + +In addition to Titles, the VOoM asciidoc mode is also aware of: + - Standard Delimited Blocks. Titles are ignored inside of them. + - Lines with [[ BlockID ]] and [ AttributeList ] preceding the title line. +See below for details. + +Because AsciiDoc is a complex format, there are various edge cases and gotchas, +see below. See also file voom_samples/asciidoc.asciidoc for examples. + +------------------------------------------------------------------------------ +Delimited Blocks [[[4~ + +Headlines are ignored inside Delimited Blocks: + http://www.methods.co.nz/asciidoc/userguide.html#_delimited_blocks +A delimited block is started and ended by a line consisting of 4 or more of the +following characters: > + //// + ++++ + ---- + .... + **** + ____ + ==== + +Example: > + == headline == + ------------------------------------ + + == listing, not headline == + + ------------------------------------ + == headline == + +Confusing cases when the start of a Delimited Block looks like an underline: > + == headline == + -------------- + == listing, not headline == + --------------------------- + + headline + -------- + -------- + listing, not headline + --------------------- + +------------------------------------------------------------------------------ +BlockID, AttributeList [[[4~ + +Section titles in AsciiDoc are often preceded by lines with attributes. Thus, +in general, it is dangerous to move nodes around--sections can become separated +from their attributes. VOoM accommodates the most common usage pattern, at +least as seen in the "userguide.txt". + +A headline may be preceded by any number of [...] lines, that is lines that +start with "[" and end with "]". This allows for any number of BlockID and +AttributeList elements and in any order. Examples: > + + [[appendix_B]] + == Appendix B == + + [appendix] + == Appendix B == + + [[appendix_B]] + [appendix] + == Appendix B == + + [appendix] + [[appendix_B]] + == Appendix B == + +In such cases, the first line of the node is the topmost [[...]] or [...] line, +not the title line. This means that it is usually safe to move such nodes -- +lines with the BlockId and AttributeList will stay with the section title. + +NOTE: There must be no comment lines or blank lines in between. + +NOTE: No attempt is made to detect any other directives, macros, etc. before +the headline. Stuff like Attribute Entries, that is lines > + :numbered: + :numbered!: +and other thingies as well as comments in front of the headline are part of the +preceding node. + +------------------------------------------------------------------------------ +Blank Lines [[[4~ + +A blank separator line is usually required before a headline. The VOoM behavior +is mostly in conformance with AsciiDoc specification for Section Titles, but it +is not perfect and false negatives can occur, see Gotchas below. + +NOTE: There should be no blank lines or comment lines between preceding [[...]] +and [...] lines and the title line. That is, unless you do not want them to be +treated as part of the headline. + +Wrong: > + == headline == + text + == not headline == + +Correct: > + == headline == + text + + == headline == + +In the following example the second underline starts Delimited Block: > + headline + -------- + text + not headline + ------------ + + not headline + ------------ + +Comment lines are OK, the check for a preceding blank line ignores them: > + == headline 1 == + text + // comment + == not headline == + + // comment + == headline 2 == + text + + // comment + // comment + == headline 3 == + + +A BLANK LINE IS NOT REQUIRED in the following cases (this matches AsciiDoc +behavior): + +1) Between adjacent headlines: > + == headline 1 == + == headline 2 == + // comment + == headline 3 == + headline 4 + ---------- + [blah] + headline 5 + ---------- + +2) If the title line is preceded by [[...]] or [...] lines: > + == headline 1 == + text + [[X1]] + [blah] + == headline 2 == + +3) After the end of a Delimited Block: > + == headline 1 == + ---------------------------- + listing + ---------------------------- + == headline 2 == + +Outline operations other than Sort will insert blank lines before headlines if +needed. They are thus not sensitive to missing blank separator lines. + +Sort does not check for blank lines before headlines and does not insert them. +There will be an error message after :VoomSort if some headlines disappear due +to a missing blank line at the end of some nodes. + +------------------------------------------------------------------------------ +Disallowed Headlines (2-line style) [[[4~ + +Some lines are never treated by VOoM as headlines when underlined because they +resemble certain AsciiDoc elements commonly found in front of Delimited Blocks. +The following are not headlines, the underline starts Delimited Block instead: +("AAA" can be any text or no text at all) +BlockID > + [[AAA]] + ------- +Attribute List > + [AAA] + ----- +Comment line (exactly two // at start) > + //AAA + ----- +Block Title > + .AAA + ---- + +Tab at start of the title line is also not allowed. Leading spaces are OK. + +An underlined headline cannot be just one character. These are not recognized +as headlines (they can be in AsciiDoc): > + A + -- + + B + --- + +An underlined title cannot look like an underline or a Delimited Block line, +that is a line of only =,-,+, etc. There are no headlines here: > + ===== + ----- + ===== + + +++ + +++++ + ^^^^^^^^^^ + ++++++++++ + +------------------------------------------------------------------------------ +Gotchas [[[4~ + +1) Do not insert blank lines or comment lines between [[...]] or [...] and the +following headline. + +2) There must be a blank line between a Macro or an Attribute Entry and the +following headline. The underline in the example below is mistaken for a +Delimited Block, which kills subsequent headlines. > + == headline + + :numbered: + == not headline + + ifdef::something[] + not headline + ------------ + + == not headline + +3) As already mentioned, any comment lines, Macros, Attribute Entries, etc. +before a headline belong to the previous node and can become separated from the +section title when nodes are moved. + +------------------------------------------------------------------------------ +Customizing [[[4~ + +1) If you use non-default characters for title underlines or for delimited +blocks, you will have to edit dictionaries ADS_LEVELS, LEVELS_ADS, BLOCK_CHARS +in module ../plugin/voom/voom_mode_asciidoc.py . + + +2) If you do not want VOoM to check for blank lines before AsciiDoc headlines +and to insert them when cutting/pasting/moving nodes, add the following to +your .vimrc: > + let g:voom_asciidoc_do_blanks = 0 + +NOTE: This is not recommended because after an outline operation, a section +title can cease to be a title due to a missing blank line. Example document: > + Title + ===== + BBBB + ---- + AAAA + ---- + some text, end of file, no blank lines after it +When BBBB is moved after AAAA (via Move Down/Up, or Cut/Paste, or :VoomSort) +it is no longer a section title in accordance with AsciiDoc specifications, but +VOoM will not know that (false-positive node) and will not issue any warnings. + +============================================================================== +html [[[3~ + *voom_mode_html* +:Voom html +MODULE: ../plugin/voom/voom_mode_html.py +HTML heading tags. Single line only. > + +

headline level 1

+ some text +

headline level 2

+ more text +

headline level 3

+ < h4 > headline level 4 + some text

headline 5

+ etc. + +Both tags must be on the same line. +Closing tag must start with =0 Tabs followed by any character except '|'. + +Blank lines are not headlines. + +KNOWN PROBLEMS +-------------- + +If TVO is installed, navigating .otl file with arrows in Tree pane is sluggish, +even with relatively small outlines like README.otl . The culprit seems to be a +time-consuming BufEnter autocommand. Function OtlEnterBuffer() is called on +BufEnter. It sets up among other things window-local folding options, which +apparently triggers recalculation of folds, which is expensive. + +The following trick seems to speed up things: change the following lines in +function OtlEnterBuffer() > + setlocal foldtext=OtlFoldText() + setlocal foldmethod=expr + setlocal foldexpr=OtlFoldLevel(v:lnum) +To > + if &foldtext !=# "OtlFoldText()" + setlocal foldtext=OtlFoldText() + endif + if &foldmethod !=# "expr" + setlocal foldmethod=expr + endif + if &foldexpr !=# "OtlFoldLevel(v:lnum)" + setlocal foldexpr=OtlFoldLevel(v:lnum) + endif + +============================================================================== +vimoutliner [[[3~ + *voom_mode_vimoutliner* +:Voom vimoutliner +MODULE: ../plugin/voom/voom_mode_vimoutliner.py +VimOutliner format: + http://www.vimoutliner.org/ + +Headlines are lines with >=0 Tabs followed by any character except: + : ; | > < +Otherwise this mode is identical to the "thevimoutliner" mode. + +============================================================================== +python [[[3~ + *voom_mode_python* +:Voom python +MODULE: ../plugin/voom/voom_mode_python.py +Mode for outlining Python code. Like a class browser except that regions +between "class" and "def" blocks are also nodes. + +Headlines are + + 1) Lines that start with: + class + def + @ + '### ' (special comment headline) + '#---' (special comment headline) + + 2) First non-blank line after the end of any "class" or "def" code block. + NOTE: Such headlines can be killed or created by an outline operation. + +NOTE: Comment lines are not ignored. Their indent is significant and can +influence the level of next headlines. + +Headline level is determined by indent relative to previous (smaller) indents. +One tab equals one space (not eight). If indent is inconsistent, the headline +is marked with '!!!' to indicate possible indent error. + +The mode uses tokenize.py to identify lines that should be ignored (multi-line +strings and expressions), as well as lines with "class" and "def". + +Outline update is slow with large files (>2000 lines, e.g, Tkinter.py). + +Note that tokenize.py also checks for inconsistent indenting and can raise +exceptions in which case outline update will not be completed. + + +OUTLINE OPERATIONS +------------------ +This mode have several intrinsic problems with outline operations. Do not +disable post-operation outline verification (g:voom_verify_oop). + +Outline operations assume that the Body buffer has tab-related options set +correctly to work with the Python code displayed in the buffer: + 1. If 'et' is off, indenting is done with Tabs, one Tab for each level. + 2. If 'et' is on, indenting is done with Spaces. The number of spaces is + set to the value of 'ts'. +The above settings must match indentation style used by the Python code being +outlined. If they don't, an outline operation will create wrong indents +whenever a level must be changed. Outline verification after outline operation +will detect that, display error messages, and force outline update. + + +Outline operations can cause some headlines to disappear. (It's not clear to me +if they can appear.) This happens because regions between "class" and "def" +blocks are also nodes. This is not a bug but a confusing behavior. In the +following code there are four headlines: > + def func1(): # headline + pass + a = 1 # headline (can disappear) + def func2(): # headline + pass + b = 1 # headline (can disappear) +After "func2" is moved Up, line "b = 1" ceases to be a headline. Outline +verification will detect that and complain about wrong Tree size. To protect +such fragile headline you can insert a special comment headline: > + def func1(): # headline + pass + a = 1 # headline (can disappear) + def func2(): # headline + pass + ### b=1 # headline (persistent) + b = 1 + +Weirdly indented comment lines also can cause various confusing problems during +outline operations. + +In summary: + - Errors "wrong Tree size" after outline operations are expected and can be + ignored. Such errors occur when nodes are moved and blocks of code + between "class" and "def" are merged. + - Errors "wrong levels", "wrong bnodes" indicate serious problems and must + not be ignored. Undo the operation after such an error. Make sure buffer + indent settings are set correctly to handle Python code. + +============================================================================== +'fmr' modes [[[3~ + *voom_mode_fmr* +"fmr" modes are very similar to the default mode, that is the :Voom command +without an argument. They deal with start fold markers with level numbers and +support special node marks (|voom_special_marks|). +"fmr" modes can be used to customize how Tree headline text is constructed and +to change the format of new headlines (Insert New Node). + +:Voom fmr +MODULE: ../plugin/voom/voom_mode_fmr.py +This mode changes absolutely nothing, it is identical to the default mode. +The purpose of this mode is to make possible the original default mode when +|g:voom_ft_modes| or |g:voom_default_mode| have been defined, e.g., for an +AsciiDoc file with fold markers. + +:Voom fmr1 +MODULE: ../plugin/voom/voom_mode_fmr1.py +Headline text is before the fold marker. This mode is identical to the default +mode except that no chars other than leading and trailing whitespace are +stripped from headlines. > + headline level 1 {{{1 + headline level 2 {{{2 + +:Voom fmr2 +MODULE: ../plugin/voom/voom_mode_fmr2.py +Headline text is after the fold marker: > + {{{1 headline level 1 + {{{2 headline level 2 +as seen in some Vim plugins: > + "{{{1 my functions + "{{{2 s:DoSomething + func! s:DoSomething() +NOTE: If {{{'s are in the first column and |matchparen| is enabled, outline +navigation is slow. A workaround: > + :set mps-={:} + +============================================================================== +Miscellaneous Modes [[[3~ + *voom_mode_various* + +:Voom cwiki +MODULE: ../plugin/voom/voom_mode_cwiki.py +For Vim cwiki plugin: http://www.vim.org/scripts/script.php?script_id=2176 + + +============================================================================== +Known Issues [[[2~ + +1) Memory used by Vim can increase significantly when outline operations Move +Up/Down are applied repetitively to a large node or a block of nodes (>1MB). +These commands delete and then insert lines in Body buffer. If the range being +moved is large, this can cause dramatic increase in memory used by the undo +history. Thus, to move a large node over a long distance it's better to use +Cut/Paste rather than keep pressing Ctrl-Up/Down. + +This problem doesn't exist if 'undolevels' is set to -1, 0, 1. +A handy way to clear undo history: +set 'undoreload' to 0, reload the file with :e or :e! . + + +2) Undoing some outline operations can take a longer than usual time if a large +number of Body folds (>1000) is affected. The workaround is to temporarily set +Body 'foldmethod' to manual (:set fdm=manual). + + +3) Outline navigation and outline operations can be sluggish if there are +time-consuming BufEnter, BufLeave, WinEnter, WinLeave autocommands associated +with the Body buffer. This is because most VOoM commands involve entering and +leaving Body buffer window, often temporarily. This is a problem with .otl +files of The Vim Outliner plugin (|voom_mode_thevimoutliner|). + +Heavy syntax highlighting can also make outline navigation slow, especially +when selecting a node in a large outline for the first time. This is a problem +with large reST, Markdown, AsciiDoc files. Disabling cursorline or cursorcolumn +or both helps a bit (:set nocul nocuc). + + +4) Support for Vim Sessions (|:mksession|) is far from perfect. If 'ssop' +contains "blank", the command :mksession will save info about Tree buffers, +that is no-file buffers named {Body_name}_VOOM{Body_bufnr}. When the session is +restored, VOoM tries to recreate the outline for such buffers. + - Markup modes are not remembered. Outline is always created with the + command :Voom. You can use |g:voom_ft_modes| or |g:voom_default_mode| to + select the desired markup mode automatically. + - The Tree and corresponding Body buffer must be in the same tab page. + - If 'ssop' contains "options", the command :mksession saves all Tree + buffer-local mapping (because all voom.vim functions are global). + This is redundant and increases the size of the Session file for no good + reason -- about 120 mappings for each Tree buffer. + - If 'ssop' contains "folds", :mksession doesn't really save Tree folding, + only some folding options which will be restored anyway. + + +5) Some markup modes (rest, asciidoc, markdown) depend on 'encoding'. If it is +changed, outline needs to be recreated for the new value to take effect. + +============================================================================== +EXECUTING NODES (:Voomexec) [[[1~ + *voom_Voomexec* +:Voomexec [type] Execute text from the current node and descendant nodes + (Tree buffers) or from the current fold and subfolds + (Body and non-VOoM buffers) as [type] script. Supported + types are: "vim", "python" or "py". + In Tree buffers Voomexec is mapped to e. + +The following happens when the command :Voomexec is executed: + +1) The type of script is determined. +----------------------------------- + :Voomexec Without an argument, the type of script is set to + buffer 'filetype': "python" if filetype is "python", + "vim" if filetype is "vim", etc. When executed from a + Tree buffer (also with e), filetype of + the corresponding Body is used. + + :Voomexec vim Execute as "vim" script. + + :Voomexec python + :Voomexec py Execute as "python" script. + + :Voomexec whatever Execute as "whatever" script. + + If script type is neither "vim" nor "python", the command aborts. + It should be possible to add support for other script types. + +2) The text of script is obtained. +--------------------------------- + a) If the current buffer is a VOoM Tree buffer, the script's text is set to + that of the current node (including headline) and all descendant nodes, + that is to Body's text in the current VOoM subtree. Body folding does + not matter. + + b) If the current buffer is a VOoM Body or a non-VOoM buffer, the script's + text is set to that of the current fold, including all subfolds. This is + most useful when 'foldmethod' is "marker". If 'foldmethod' is not + "marker", the command aborts and the script is not executed. + +3) The script is executed according to its type. +----------------------------------------------- + a) A "vim" script is executed by copying text into a register and executing + that register (|:@|) in a function inside try/catch/finally/endtry. + If an error occurs, v:exception is echoed. (v:throwpoint is useless.) + + b) A "python" script is executed as a string via the "exec" statement, see + http://docs.python.org/reference/simple_stmts.html#exec . + The following Python names are pre-defined: vim, voom, VOOMS. + + An extra line is prepended to script lines to specify encoding as per + http://www.python.org/dev/peps/pep-0263/ , e.g. + # -*- coding: utf-8 -*- + Encoding is Vim's internal encoding ('utf-8' for all Unicode &enc). + + The script is executed inside try/except block. If __PyLog__ is enabled + and an error occurs, Python traceback is printed to the __PyLog__ buffer + instead of Vim command line. + +NOTE: The "end of script" message shows the first and last line number of the +script's text. + +============================================================================== +sample Vim scripts [[[2~ + +Scripts in the following subnodes can be executed with > + :Voome vim + +------------------------------------------------------------------------------ +"---node 1---[[[3o~ +echo 'in node 1' + +" section [[[ +echo 'inside section in node 1' +" ]]] + +"----------------------------------------------------------------------------~ +"---node 1.1---[[[4o~ +echo 'in node 1.1' + +"----------------------------------------------------------------------------~ +"---node 1.1.1---[[[5~ +echo 'in node 1.1.1' + +"============================================================================~ +sample Python scripts [[[2~ + +Scripts in the following subnodes can be executed with > + :Voome py + +------------------------------------------------------------------------------ +#---node 1---[[[3o~ +print ' in node 1' + +print 'current buffer number:', vim.eval('bufnr("")') +print 'VOoM Body buffer numbers:', voom.VOOMS.keys() +print 'voom.makeOutline() docstring:\n ', voom.makeOutline.__doc__ ,'\n' +import os +print 'current working dir:', os.getcwd() + +# section [[[ +print ' inside section in node 1' +# ]]] + +#----------------------------------------------------------------------------~ +#---node 1.1---[[[4o~ +print ' in node 1.1' + +#----------------------------------------------------------------------------~ +#---node 1.1.1---[[[5~ +print ' in node 1.1.1' + +#============================================================================~ +Alternatives to :Voomexec [[[2~ + +Other Vim commands and scripts can retrieve the content of VOoM nodes as a +range of Body lines and do something with it. + +1) In a Tree buffer, the "R" command selects the corresponding Body line range, +which can then be passed to a range-accepting command. + +2) Function Voom_GetExecRange(lnum) is what :Voomexec uses to obtain the +script's text, that is Body's lines from the current subtree (Tree buffers), or +lines from the current fold (Body buffers, non-VOoM buffers). +The following function shows how to use Voom_GetExecRange(): > + func! Voom_WriteExecRange() + " Write to a file lines that are executed by :Voomexec. + let filePath = '~/voomscript' + let [bufType, body, bln1, bln2] = Voom_GetExecRange(line('.')) + if body<1 | return | endif + let blines = getbufline(body, bln1, bln2) + call writefile(blines, expand(filePath)) + endfunc + +3) Function Voom_GetVoomRange(lnum,withSubnodes) can be used by other scripts +to obtain the content of a VOoM node at line number lnum (withSubnodes==0), or +the content of node and its subnodes (withSubnodes==1). Unlike +Voom_GetExecRange(), it works the same for Tree and Body buffers, and it +doesn't care about folding or non-VOoM buffers. Typical usage: > + let [bufType, body, bln1, bln2] = Voom_GetVoomRange(line('.'), 0) + " Error: Body not loaded, outline update failed, etc. + if body < 0 + echo 'ERROR' + " Current buffer is not a VOoM buffer. Do something with the current line. + elseif bufType==#'None' + echo getline('.') + elseif bufType==#'Tree' + echo 'in Tree' + echo getbufline(body,bln1,bln2) + elseif bufType==#'Body' + echo 'in Body' + echo getbufline(body,bln1,bln2) + endif + +4) Function Voom_GetBuffRange(ln1,ln2) can be used by other scripts to obtain +the content of VOoM nodes in Tree line range ln1,ln2 if the current buffer is a +Tree (same as the "R" command). If the current buffer is not a Tree, it returns +the ln1,ln2 range for the current buffer. Example: > + let [bufType, body, bln1, bln2] = Voom_GetBuffRange(line("'<"),line("'>")) + if body < 0 | return | endif + let blines = getbufline(body,bln1,bln2) + ... do something with blines ... + +============================================================================== +Known Issues [[[2~ + +1) Vim script code executed this way cannot use |line-continuation|. + +2) When :Voomexec executes a Vim script with Python code and a Python error +occurs, Python traceback is not printed. However, Python traceback is printed +to the PyLog buffer if it is enabled. Example in the next fold can be executed +with ":Voome vim". > + + " Vim script with Python error [[[ + echo 'start of vim script' + py print bogus_name + py print 'py after error' + echo 'the end' + " ]]] + +3) As the example above illustrates, Vim script is not terminated when an +error occurs in the Python code. + +============================================================================== +__PyLog__ BUFFER (:Voomlog) [[[1~ + *voom_Voomlog* +:Voomlog This command creates scratch buffer __PyLog__ and redirects + Python's stdout and stderr to that buffer. + +Subsequent Python print statements and error messages are appended to the +__PyLog__ buffer instead of being printed on Vim command line. + +Windows with the __PyLog__ buffer are scrolled automatically in all tabpages +when something is printed to the PyLog buffer. If a tabpage has several PyLog +windows, only the first one is scrolled. If the current tabpage has no PyLog +windows, the command :Voomlog will create one. + +To restore original stdout and stderr (that is Vim command line): unload, +delete, or wipeout the __PyLog__ buffer (:bun, :bd, :bw). + +NOTE: __PyLog__ buffer is configured to be wiped out when unloaded or +deleted. 'bufhidden' is set to "wipe". + +The filetype of the PyLog buffer is set to "voomlog". Some syntax highlighting +is added automatically to highlight Python tracebacks, Vim error, and common +VOoM messages. + +When Python attempts to print a unicode string, e.g. > + :py print u'ascii test' + :py print u'\u042D \u042E \u042F \u2248 \u2260' +the string is encoded using internal Vim encoding at the time of __PyLog__ +buffer creation. Internal encoding is determined from Vim option 'encoding': +"utf-8" if &encoding is a Unicode encoding, &encoding otherwise. + +============================================================================== +Known Issues [[[2~ + +1) All output lines appear in the __PyLog__ buffer simultaneously after the +script is finished, not in real time. Example (executable with :Voome py): + +### demo Python code [[[ +import time, datetime +print datetime.datetime.now() +time.sleep(5) +print datetime.datetime.now() +### ]]] + + +2) Printing many lines one by one can take a long time. Instead of doing > + :py for i in range(1000): print i +It is much faster to do > + :py print '\n'.join([str(i) for i in range(1000)]) +(It's also easier to undo.) + + +3) Visiting other tabpages during automatic scrolling is slow on Linux in GUI +Vim (GTK). It's better to have PyLog window only in the current tabpage. + + +4) __PyLog__ is not usable when in the Ex mode, that is after 'Q' or 'gQ'. +The lines in the __PyLog__ buffer will appear after the Ex mode is exited. + + id_20110213225841 +5) When __PyLog__ is enabled, a Python error in a Vim script does not result in +Vim error. This is probably because Python's sys.stderr is redirected. This +changes Vim error handling when a Python code is executed by Vim inside +try/endtry. Example Vim script, try it with PyLog on: > + try + python assert 1==2 + echo 'AFTER PYTHON ERROR -- should not be here' + finally + echo 'AFTER FINALLY' + endtry + echo 'AFTER TRY -- should not be here' + + +6) In versions before 1.7 there was problem with the output of help(), which +apparently uses Lib/pydoc.py, which does something strange to output trailing +\n. Steps to reproduce: + 1. Open new instance of Vim. + 2. Voomlog + 3. :py help(help) + 4. Wipe out __PyLog__ buffer to restore sys.stdout. + 5. :py help(help) + An error occurs: '\n' is printed to the nonexisting log buffer. +The culprit is in Lib/pydoc.py: + help = Helper(sys.stdin, sys.stdout) +The current workaround is to delete pydoc from sys.modules when changing +stdout and stderr. + +============================================================================== +Add-ons [[[1~ + *voom_addons* +VOoM add-ons are Vim or Python scripts that use "voom.vim" and "voom.py" +functions and data. Add-ons allow to add new functionality or to customize +default features without modifying the core files. + + +LOADING ADD-ONS +--------------- +Some Vim script add-ons can be sourced at any time, which means they can be +placed in $HOME/.vim/plugin/ like any other plugin. + +For finer control, user option "g:voom_user_command" should be used to load +add-ons only when file voom.vim is being sourced. This option defines a string +to be executed via |execute|. This is the last thing done in voom.vim: > + if exists('g:voom_user_command') + execute g:voom_user_command + endif + +There is no default "g:voom_user_command", it must be created by the user. + +METHOD 1: Add-ons are .vim files located in $HOME/.vim/add-ons/voom/ +To load them all via |runtime|, put this in vimrc: > + let g:voom_user_command = "runtime! add-ons/voom/*.vim" + +METHOD 2: Add-ons are in one file, D:/SCRIPTS/VOoM/voom_addons.vim +To source the file, put this in vimrc: > + let g:voom_user_command = "source D:/SCRIPTS/VOoM/voom_addons.vim" + +METHOD 3: Add-ons are in Python module voom_addons.py somewhere in Python +search path (directory ./plugin/voom will do). To import the module, put this +in vimrc: > + let g:voom_user_command = "python import voom_addons" +The voom.py module should be accessed from within voom_addons.py as follows: > + import sys + voom = sys.modules['voom'] + + +WRITING ADD-ONS +--------------- +There is no special API. The following applies: + + - Python-side functions and data are available as attributes of module + "voom.py". Python-side outline data for each Body are attributes of an + instance of class VoomOutline (VO). These class instances are stored in + dictionary voom.VOOMS, keys are Body buffer numbers: VO=voom.VOOMS[body]. + + - All Vim functions in "voom.vim" are global and start with "Voom_". + + - Vim-side data are script-local. Two functions in "voom.vim" allow + external scripts to read "voom.vim" script-local outline data: + Voom_GetBufInfo() + Voom_GetData() + Sample add-on "voom_info.vim" shows how to use them: > + :let [bufType, body, tree] = Voom_GetBufInfo() + :let [voom_bodies, voom_trees] = Voom_GetData() +< + Function Voom_GetVar(var) allows external scripts to read any "voom.vim" + script-local variable if it exists. Examples (these always exist) > + :echo Voom_GetVar('s:voom_logbnr') + :echo Voom_GetVar('s:voom_trees') + :echo Voom_GetVar('s:voom_bodies') +< + Example: move cursor to Log window in the current tab > + :let logwnr = bufwinnr(Voom_GetVar('s:voom_logbnr')) + :if logwnr > 0 | exe logwnr.'wincmd w' | endif + + +USING ADD-ONS TO ADD NEW FUNCTIONALITY +-------------------------------------- +Add-ons can create global commands, menus and mappings. + +A global command that accesses VOoM outline data must first check that the +current buffer is a VOoM buffer (Tree or Body) and refuse to execute if it's +not. It should update outline if current buffer is a Body. Sample add-on +"voom_info.vim" shows how to do that. + +To create Tree-local mappings or commands use ~/.vim/ftplugin/voomtree.vim -- +Tree buffer filetype is set to "voomtree" when Tree buffer is created. + + +USING ADD-ONS TO CUSTOMIZE VOoM +------------------------------- +Add-ons can overwrite and modify core code functions and some data. Add-on +"custom_headlines.vim" is an example of this approach. It shows how to +customize construction of Tree headline text for individual filetypes. + +Such add-ons must be loaded after "voom.vim" has been sourced completely, that +is via option g:voom_user_command as explained above. + + +MARKUP MODES +------------ +Markup modes are special kinds of add-ons. They change how outline is +constructed and how outline operations are performed (|voom_markup_modes|). + +============================================================================== +Implementation notes [[[1~ + *voom_notes* + +============================================================================== +Theory of Operation [[[2~ + +============================================================================== +why Python [[[3~ + +The main reason VOoM uses Python is because some of its critical code is much +faster in Python than in Vim script. + +Scanning a buffer for fold markers is >10 times faster with Python code than +with a similar Vim script code. A demo code is given below. To test: select +lines, copy into a register, and execute that register while in any buffer with +a large number of fold markers, or in any large buffer. + +Results with "calendar_outline.txt": > + 3.2MB, 56527 lines, 4160 headlines + Vim 7.3.145; Python 2.6.5; Win2k; Intel Pentium 4 Mobile, 1.6 GHz + + Vim method 1: 1.53 sec + Vim method 2: 0.70 sec + Vim method 3: 0.14 sec + Python: 0.084 sec + +While Vim method 3 is fast, it is inconvenient because: + a) It requires the cursor to be in Body buffer, but outline update should + be run after entering the Tree buffer. + b) It moves the cursor. + +"--------------GET LINES WITH FOLD MARKERS---------------------------[[[ +" Get list of headlines: lines with start fold marker followed by number. +" This is the bare minimum that must be done to create an outline. + +""""" Vim method 1 +func! Voom_VimTest1() + let headlines = [] + let allLines = getline(1,'$') + for line in allLines + if stridx(line, '{{{')==-1 "}}} + continue + endif + if match(line, '{{{\d\+')!=-1 "}}} + call add(headlines, line) + endif + endfor + return len(headlines) +endfunc + +""""" Vim method 2 +func! Voom_VimTest2() + let lnums = filter(range(1,line('$')), 'getline(v:val)=~''{{{\d\+''') + let headlines = map(lnums, 'getline(v:val)') + return len(headlines) +endfunc + +""""" Vim method 3 +func! Voom_VimTest3() + let headlines = [] + g/{{{\d\+/ call add(headlines, getline('.')) "}}} + return len(headlines) +endfunc + +""""" Python code, similar to Vim method 1 +python << EOF +def Voom_PyTest(): + import vim + import re + re_marker = re.compile(r'{{{\d+') #}}} + headlines = [] + allLines = vim.current.buffer[:] + for line in allLines: + if not '{{{' in line: continue #}}} + if re_marker.search(line): + headlines.append(line) + vim.command('let bnodes=%s' %len(headlines)) +EOF + +""""" timing +let start = reltime() +let nodeCount = Voom_VimTest1() +echo 'Vim method 1: ' . reltimestr(reltime(start)) . 'sec; '. nodeCount . ' nodes' + +let start = reltime() +let nodeCount = Voom_VimTest2() +echo 'Vim method 2: ' . reltimestr(reltime(start)) . 'sec; '. nodeCount . ' nodes' + +let start = reltime() +let nodeCount = Voom_VimTest3() +echo 'Vim method 3: ' . reltimestr(reltime(start)) . 'sec; '. nodeCount . ' nodes' + +let start = reltime() +py Voom_PyTest() +echo 'Python: ' . reltimestr(reltime(start)) . 'sec; '. nodeCount . ' nodes' + +unlet nodeCount +"--------------END OF CODE ------------------------------------------]]] + + +In addition, Python's FOR loop is >30 times faster then Vim's. In the demo +code below the Python function is >60 times faster. + +"------ Vim FOR loop versus Python FOR loop -------------------------[[[ +func! Time_VimForLoop() + let aList = range(1000000) + for i in aList + " pass + endfor +endfunc + +python << EOF +def Time_PyForLoop(): + aList = range(1000000) + for i in aList: + pass +EOF + +""" 9.76 sec """ +let start = reltime() +call Time_VimForLoop() +echo 'Vim: ' . reltimestr(reltime(start)) + +""" 0.15 sec """ +let start = reltime() +py Time_PyForLoop() +echo 'Python: ' . reltimestr(reltime(start)) +"-------END OF CODE--------------------------------------------------]]] + +Thus, Python code should be much faster when handling large lists. + +============================================================================== +separate Trees or single Tree [[[3~ + +A single Tree buffer could be used to display outlines of many files. Tlist +does that. This makes sense when working with several related files. Also, +having a single Tree would be more like Leo. + +VOoM creates new Tree buffer for every new outline. This is simpler. It is +more appropriate for text notes, when outline files are likely to be +unrelated. Searching headlines is easier. + +============================================================================== +checking Bodies for ticks [[[3~ + +Tree buffer and associated outline data are updated on entering Tree via +BufEnter autocommand. To perform update only when the Body has changed since +the last update, Body's b:changedtick is used as shown in the docs. +Unfortunately, b:changedtick cannot be read with getbufvar(), so it's not +accessible from Tree on BufEnter [1]. The workaround is to use Body's BufLeave +autocommand to save Body's b:changedtick. So the entire update scheme is: + - on Body BufLeave save Body's b:changedtick as "tick" + - on Tree BufEnter compare "tick_" to "tick" + - if different, do outline update and set "tick_" to "tick" + +The outline must be up to date when the cursor is in the Tree buffer. If it's +not, the consequences could be unpleasant. Performing outline operations will +cause data corruption. + +Outline update can fail when something goes wrong with autocommands, e.g., +when the user messes with 'eventignore'. Or, the Body file can be modified by +an external application while cursor is in Tree. + +Fortunately, most Voom commands involve a visit from Tree to Body or vice +versa, so we can compare "tick_" directly to Body's "b:changedtick". If they +are different: the command is aborted, outline update is forced, error message +is displayed. Such check is performed during: + - selecting node from Tree or Body + - Voomgrep command initiated from Tree + - during every outline operation, before modifying buffers +The function that does this check is Voom_BodyCheckTicks(). + +These checks can be tested by modifying Body and then moving to Tree with +":noau wincmd w" or after ":set ei=BufLeave", etc. + +Another precaution is that "tick_" is not set to "tick" when an unexpected +error occurs during update. voom.updateTree()) is always called from Vim code +inside try/finally/endtry. It also sets Vim var l:ok to indicate success, see +#id_20110213212708 . + + + [1] 2011-01-21 Fixed by Patch 7.3.105 !!! + see #ID_20111006013436 + +============================================================================== +unloaded buffer + python == trouble [[[3~ + +Bad things happen when attempting to modify an unloaded buffer via Python +vim.buffer object. (This might be considered a Vim bug.) Example: + - Create two buffers: buf1 and buf2. They can be new, no-file buffers. + - With cursor in buf2 + :py buf2=vim.current.buffer + - Buffer 2 can now be modified via Python: + :py buf2[0]="xxxxxxxxx" + - Unload buffer 2 + :bun! + Buffer 1 is the current buffer. + - Try writing to buffer 2, which is not loaded + :py buf2[0]="yyyyy" + - Buffer 1 is modified instead of buffer 2, and the change cannot be undone! + Buffer 2 is no longer unloaded, so subsequent writes to it via buf2 + happen correctly. + +VOoM uses Python vim.buffer methods to modify Tree, Body, and PyLog buffers. +It is essential that these buffers are loaded (bufloaded())before being written +to. Writing to a non-existing (wiped out) buffer is not as dangerous because it +produces an error. + +Tree and PyLog BufUnload autocommands make it unlikely that a Tree or PyLog +buffer is unloaded -- they are wiped out on BufUnload. +These buffers can still become unloaded when they are closed improperly with +"noa bun" or "noa bd" or when something goes wrong with autocommands. + +Body buffers can be unloaded since v3.0. + +There are checks that ensure that the buffer is loaded (bufloaded()) before it +is modified via Python vim.buffer object. + +============================================================================== +wipe out Tree on BufUnload [[[3~ + +A Tree buffer should be wiped out and the corresponding VOoM data deleted +after: + +1) Tree is unloaded. All content is lost, Tree reverts to blank buffer. +2) Tree is deleted. As above, plus buffer-local mappings are lost. +2) Tree is wiped out. VOoM data need to be cleaned up. + +This is accomplished via BufUnload autocmd for Tree, which is also triggered on +BufDelete and BufWipeout. + +Unloaded, deleted, and wiped out Body buffers are obviously also a problem, see +next node. Prior to v3.0 there was Body BufUnload au that wiped out Tree. That +was found to be too risky. + +There are several fail-safe measures that ensure that nothing damaging will +happen if Tree BufUnload autocommand is not triggered, as after "noa bun", "noa +bd", "noa bw". + +Most Voom commands check that: Tree is loaded, Body exists, Body is loaded (see +next). This relies on bufloaded() and bufexists(). + +Functions Voom_ToBody() and Voom_ToTree(), which are called when selecting +nodes and before almost every outline operation, perform all of the above +checks and will do cleanup if checks fail. + + +The PyLog buffer should also be wiped out when unloaded or deleted. There is a +check that ensures that PyLog is loaded before printing to it. + +============================================================================== +unloaded, deleted, wiped out Bodies [[[3~ + +Unloaded Body buffers are a problem: + +It is not possible to outline a buffer if it is unloaded. +Python vim.buffer object is useless for unloaded buffer, it's [""]. + +When unloaded Body is loaded again the following events are hard to detect: + - buffer changes were abandoned after q!, bun!, bd! + - file was modified by external process + +Thus, a global outline update must be done after loading Body. This means we +should abort outline command if Body is found unloaded, even if we can load it +and force outline update. + +PERFECT: Body b:changedtick is incremented by 2 after unloading/loading. +Outline update is guarantied on Tree BufEnter or when updating from Body. + +We deal with unloaded Bodies by disabling Tree buffer commands -- as soon as +Body bufnr is computed, check if it's loaded and abort the command if it's not. +Helper function is Voom_BufLoaded(body). It will also detect if Body does not +exist. This must be done for: + outline update on BufEnter + all outline operations + node selection (always done by Voom_TreeSelect()) + Voomgrep, Voomunl, Body text getters (Voomexec) + any other command that requires up-to-date outline, or reads/writes Body + +The next line of defense is Voom_ToBody(), which is called by almost all Tree +commands. When it detects Body is unloaded it loads it in new window as usual, +runs outline update, returns -1. If Body does not exist it performs clean up. + +The b:changedtick check (see "Checking Bodies for ticks") also should prevent +potential troubles after Body unload/reload. This is because b:changedtick +changes after unloading a buffer and loading it again. + + +When Body buffer is deleted (:bd) it is unloaded. In addition, buffer-local +mappings are lost. The loss of Body-local mappings (shuttle keys) is detected +by Body BufEnter au. It checks for hasmapto('Voom_ToTreeOrBodyWin') and +restores mappings if needed. + +The command :Voom checks hasmapto('Voom_ToTreeOrBodyWin') when executed in Tree +or Body. If not found, it reconfigures Tree/Body. In theory, this can be used +to restore Tree and Body configurations after some perverted unloads/reloads +with ":noa bd", "noa b", etc. + + +Wiped out Bodies are also unloaded. Tree has no reason to exist after Body has +been wiped out. Sadly, wiping out Tree from Body BufWipeout au is too risky, +see v3.0 notes. + +============================================================================== +CHANGELOG [[[2o~ + +v4.3, 2012-05-06 [[[3x~ + +PROBLEM: The Voom command cannot handle buffer names containing %, #, etc. +Session restore is also affected. +Whitespace in names is also a problem on some systems (Jonathan Reeve). +SOLUTION: Do fnameescape() when :edit, :file, :tabnew, etc. + +Added |voom_mode_hashes|. + + +v4.2, 2012-04-04 [[[3~ + +New commands for quitting and toggling outline window (|voom_quit|): +"q", VoomToggle, Voomtoggle, Voomquit, VoomQuitAll. + https://github.com/vim-voom/vim-voom.github.com/issues/2 + +New Tree mappings ^^ (Move Up) and __ (Move Down) for symmetry with << and >>. + +Added support for "fmr" modes (|voom_mode_fmr|). Modes fmr, fmr1, fmr2. + +New options |g:voom_ft_modes|, |g:voom_default_mode| allow automatic selection +of markup mode according to filetype of the source buffer. + +Improved asciidoc mode: there can be any number of preceding [] or [[]] lines +and in any order; blank line is not required before the topmost [] or [[]]. + +:Voomexec now executes Python scripts via "exec" instead of execfile(). Temp +file plugin/voom/_voomScript_.py is no longer created and should be deleted. +Python traceback's lnums match buffer lnums. +The "end of script" message shows script's start/end lnums. + + +Vim code +-------- +Replaced Voom_GetBodyLines1() with more useful functions. + +Voomexec fix: execute Vim scripts in a separate function Voom_ExecVim() to +avoid potential interference with Voom_Exec() local vars. +Python scripts still have access to Voom_Exec() vars. No big deal, unlikely to +cause any problems. Demo: +### :Voomexec py [[[ +vim.command("echo [bufType,body,bln1,bln2]") +print vim.eval("[bufType,body,bln1,bln2]") +### ]]] + + +Python code +----------- +Mode-specific functions, such as makeOutline(), are now VO methods set during +outline init. Thus got rid of incessant getattr(VO.mmode,...) during outline +updates and outline operations. It's now easier to control what modes can do. + +Split makeOutline() into makeOutline() and makeOutlineH() for efficiency sake. +(If needed, the old makeOutline() function can be in a fmr mode.) + +Added check for clipboard size in setClipboard() to guard against failures with +very large clipboards. + +Get &enc during outline init (VO.enc) instead of during markup mode imports. +Otherwise, it's impossible to change &enc without reloading everything. + + +v4.1, 2011-11-27 [[[3~ + +PROBLEM: Tree mappings J/K are supposed to accept a count, but they don't. +This is with Vim 7.3.145. No problem with Vim 7.2. +SOLUTION: Save original v:count1 before + exe 'normal... +in Voom_Tree_KJUD(). + +Better argument completion for the :Voom command. The list of modes is +constructed from file names voom_mode_{whatever}.py in ../plugin/voom . + + +v4.0, 2011-11-06 [[[3~ + +New markup modes: asciidoc, org (same as old viki), cwiki. +Viki mode now ignores special regions. + +New Tree mapping: "R" selects corresponding Body range. + +Tweaked Markdown mode. + +Fix in viki/org mode: level changing outline operations converted any +whitespace after * into space. + +Fixed Tree syntax hi to avoid false hi after "|" inside of headline text. +Example: part after # is not comment > + = . . |<> + +ID_20111006013436 +Improved function Insert Node: use getbufvar(body,"changedtick") if +has("patch105"). This saves one trip to Body and back when checking for ticks. +Code for timing, execute from Tree, the Body is empty: > + let tree = bufnr('') + let start = reltime() + for i in range(1,100) + call Voom_OopInsert('') + call Voom_ToTree(tree) + endfor + echo reltimestr(reltime(start)) + unlet tree start +0.71 sec vs 1.10-1.13 sec with the old code or if there is no patch. +It seems there are no other functions that would benefit from this. +Note: when check for ticks via getbufvar() fails, the next step should be to +move cursor into Body buffer--it may no longer exist, unloaded, etc. + + +v4.0b5, 2011-03-24 [[[3~ + +New markup mode: txt2tags. + +Added support for Vim Sessions (:mksession) via BufFilePost autocmd for VOOM +Tree buffers and __PyLog__ buffers. + +Fixed bug in :Voomexec -- Python script file encoding was set incorrectly. +Source code encoding of the temp script file should be Vim internal encoding, +not &fenc or &enc. + +Fixed command Edit Headline (iIaA): cursor was not positioned on the first word +char in Body headline when there are was no foldmarker (markup modes). + +Dealing with Python errors during outline update. +------------------------------------------------- +Working in the python mode revealed a flaw in safeguards against Python errors +during outline update. Such errors are expected while in python mode -- +tokenize.py raises exception when indentation is wrong or a quote is missing. + + id_20110213212708 , also see #id_20110213225841 +Calling voom.updateTree() via try/python.../finally/endtry in the Vim code +(|try-finally|) does not guard against Python errors when PyLog is on. It looks +like Vim error is not triggered when Python's sys.stderr is redirected. The +result is that changedtick (tick_) is updated despite a failed update. +SOLUTION: always set Vim var l:ok in voom.updateTree() before returning to +indicate a successful update. + +Python mode: catch exceptions raised by tokenize.py, echo the error, set Tree +lines to make it clear that update has failed and outline is invalid. + +Refactoring +----------- +Insert New Headline -- don't need Body column, just search for "NewHeadline". +voom.newHeadline(), hook_newHeadline() no longer return column. + +Voom_LogScroll() -- several optimizations. PyLog is usually only in the current +tabpage. Thus, check tabpagenr() before tabnext--faster than redundant tabnext. + + +v4.0b4, 2011-01-30 [[[3~ +New Tree mappings for navigating outline: + P (go to parent node), + c (go to parent and contract it), + C (contract siblings or everything in Visual selection), + o (go to first child), + O (expand siblings or everything in Visual selection), + K/J/U/D (go to previous/next/uppermost/downmost sibling), + s (show headline text), S (show UNL). + + id_20110121201243 +PROBLEM: Longstanding annoyance with some Tree mappings. Example: hit "d" +(disabled by mapping to ), wait a few seconds, hit "dd" (cut node) -- +there is no response. Can be very confusing. +SOLUTION: disable "d" and similar by mapping them to instead of . +Another option is to map them to 0f| . + +Disabled more text changing keys in Tree: < > etc. + +PROBLEM: User placed VOoM package in ~/.vim/plugin. Everything gets loaded on +Vim startup (|load-plugins|). Add-on custom_headlines.vim causes error because +it must be loaded only after voom.vim has been sourced completely. +SOLUTION: finish loading custom_headlines.vim if !exists('*Voom_Exec'). + +PROBLEM: Command :Voomhelp does not reuse existing voom.txt windows if current +buffer is not voom.txt or its Tree. +SOLUTION: Start by searching all tabs for voom.txt window. + + id_20110120011733 +PROBLEM: Outline has only level 1 headlines and there is a stray 'o' mark. +vim.error in voom.foldingCreate() on startup after :Voom. +E490: No fold found, triggered by initial :foldopen, because Tree has no folds. +SOLUTION: Catch E490 when doing :foldopen in foldingCreate(). +Note1: must execute :foldopen even when cFolds list is empty. +Note2: cFolds (lnums of closed folds) never contains nodes without children. +This means "zc" will not trigger E490 error. Unless Tree folding is messed up +or lost, e.g., because fdm was reset. + + id_20110125210844 +Fixed glitches with initial cursor positioning in Tree when markup mode is +used. snLn can be >1 when markup mode is used or when there is no startup node +(Body cursor on >1 node). +Create jumps marks when outline is created: line 1 and selected node. +Initial gg restores view after jumping around when creating folds. + +Added "keepj" when jumping around Tree and Body via G or gg. +No jump marks are created in Tree or Body during node navigation or manipulation. + +TODO: set jump mark when selecting node from Tree with or other +actions. Outline browsing history. + + +Vim code for outline navigation id_20110116213809 +--------------------------------------------------- +Made Voom_TreeLeft() much faster with large outlines. +The final step (go to parent) used this inefficient code: > + let ind = stridx(getline('.'),'|') + let indp = ind + while indp>=ind + normal! k + let indp = stridx(getline('.'),'|') + endwhile +It is much faster to call search() to find line with required indent of |. +Multibyte chars should not be a problem because there are never any before |. +Timing: 5876 childless siblings, cursor on the last. +Old code: 0.28 sec. New code: 0.01 sec. + +Voom_TreeToSiblings(), etc: also use search() to locate parents/siblings. +virtcol() ensures multibyte chars before | will never be a problem. + +Simplified Voom_TreeSelect(lnum, focus) signature. No need for lnum, it's +always current line. focus is 1 (stay in Tree) or 0. + +Got rid of Voom_TreePlaceCursor(), just do + call cursor(0,stridx(getline('.'),'|')+1) + or + normal! 0f| + + +v4.0b3, 2011-01-04 [[[3~ + +New markup mode: markdown. + +Fixed severe bug in reST mode. Paste and level-changing outline operations were +affected. One manifestation: when pasting into an empty outline all headlines +become level 1. + +The command :VoomSort now accepts a line range in front of it. If a range is +not a single line, siblings in the range are sorted. + +Changed how some outline operations handle the first Tree line (outline title): + - VoomSort now aborts if the first Tree line is selected. This is to be + consistent with other outline operations (Cut, Copy, Move) which also + require a valid range. + - Print error message when an operation is aborted because the first Tree + line is selected (Cut, Copy, Move, Sort). + - Mark Node as Startup is allowed: remove "=" marks from all headlines. + +Mode viki: allow any whitespace after last leading *, not just space. + +Outline operation Copy: do not display error message complaining about Body +buffer being nomodifiable or readonly. + +Code refactoring +---------------- + +s:voom_logbnr now always exists. It is 0 if there is no Log buffer. + +New helper function Voom_GetVar(var) -- allows external scripts to read any +"voom.vim" script-local variable such as s:voom_logbnr. + +:Voomexec -- improved printing of errors. When PyLog is not enabled, +Python traceback is echoed as Vim error message. See + ../plugin/voom/voom.py#id_20101214100357 + +Assign VO.marker and VO.marker_re to MARKER and MARKER_RE instead of 0 when +foldmarker is default. MARKER_RE object is reused, so this is still efficient. +This eliminated the need for silly code + marker = VO.marker or MARKER + marker_re = VO.marker_re or MARKER_RE + +------------------------------------------------------------------------------ +v4.0b2, 2010-10-24 [[[3~ + +New markup modes: rest (reStructuredText), python, thevimoutliner, vimoutliner. + +Changed default for g:voom_verify_oop to 1 (enabled). We need this to detect +inherent problems with "python" and "rest" modes, to debug other modes. Outline +verification is performed by new function Voom_OopVerify(). It forces outline +update if verification fails. + +Option g:voom_rstrip_chars (dictionary) has been removed. Instead, there are +options g:voom_rstrip_chars_{filetype} (strings) for each Body filetype of +interest. REASON: it's easier to define a string for one filetype than to mess +with a dictionary that has settings for a bunch of other filetypes. + +Command VoomSort now checks that the number of headlines is not changed after +sorting (after Tree update on BufEnter). When using modes "rest" or "python" +sorting can make some headlines cease to be headlines. + +Added argument completion for command :Voom. + +Added special syntax hi in Tree when Body's filetype is vim or python. +See Voom_TreeSyntax(). + +Python code refactoring: + - Name VOOMS is no longer defined in Vim module namespace. It is available + only as voom.VOOMS. + - Changed argument "body" in many functions like makeOutline() to "VO". + This makes more sense since "VO" is what we need. Makes it easier to + write markup modes and add-ons -- no need to look up VO in voom.VOOMS. + - Refactored oops Cut, Paste, Up, Down, Left, Right to accomodate modes + like "python" and "rest". The new sequence of actions is: + - modify Body lines (move); + - update VO.bnodes; update VO.levels; + - call hook_doBodyAfterOop() to finish updating Body lines -- change + indentation, headline adornment styles, etc; + - go back to Tree; update Tree lines. + + +'noautocmd' troubles [[[4~ +Testing thevimoutliner mode with TVO plugin installed revealed serious flaw in +node selection functions -- they screw up TVO's BufEnter and BufLeave autocmds +that disable/enable TVO's menu. There are errors about missing menu, etc. + +The culprit is "noautocmd" in Voom_TreeSelect() and Voom_BodySelect(). + +Same problem with VoomSort. + +All outline operations use "noautocmd" when cycling between Body and Tree. This +can also cause problems -- if outline operation fails the cursor stuck in Body. + +SOLUTION: do not use "noautocmd". + +The original reason for "noautocmds" was to increase performance by disabling +autocmds when temporarily visiting Tree or Body window -- a frequent action, +e.g., when selecting nodes. The performance gain is usually minuscule and is +not worth the risk of screwing up autocommands created by other plugins. + +There is also problem with Tree BufUnload au -- it must be "nested" to trigger +BufEnter, etc. after Tree is wiped out, and we cannot use "noautocmd" when +wiping out Tree. But without "noautocmd" we get recursive call. +SOLUTION: first delete Tree autocommands, then wipe out Tree without using +"noautocmd". +This change was made in Voom_TreeBufUnload() and Voom_UnVoom() +Same change was made in Log BufUnload au: made it nested, delete Log au before +wiping out Log. + +"noautocmds" is now used only in Voom_LogScroll() for performance sake. +It's very unlikely that something will go wrong there. + +------------------------------------------------------------------------------ +v4.0b1, 2010-09-21 [[[3~ + +Added support for headline markups other than start fold markers with levels: +|voom_markup_modes|. Available markup modes: wiki, vimwiki, viki, html. + +Changed plugin directory structure: all Python files are now located in folder +plugin/voom. + +Changed how global outline data are stored. +Old scheme: class VOOM has a bunch of dictionaries as attribs. Keys are Body +bufnr. Data for one outline: + bnodes, levels = VOOM.bnodes[body], VOOM.levels[body] +New scheme: there is instance of class VoomOutline for each Body, attribs are +outline properties. These instances are stored in global dict VOOMS, keys are +Body bufnr. + VO = VOOMS[body] + bnodes, levels = VO.bnodes, VO.levels + +PROBLEM introduced since setting Tree's "bufhidden" to "wipe". +Tabpage has two windows, Tree and Body. Load another buffer in Body window and +create outline. What is left is one window with new Tree. +FIX: Voom_ToTreeWin(), when re-using another Tree window: split it if current +tabpage has no other windows with this Tree buffer. This actually makes sense +regardless of Tree "bufhidden". + +------------------------------------------------------------------------------ +v3.0, 2010-08-01 [[[3~ + +New command :VoomSort [options] for sorting outline, |voom_sort|. + +Tree buffer is no longer automatically wiped out when its Body buffer is +unloaded, deleted, or wiped out. Instead, outline is locked until Body is +loaded again. This change was needed to eliminate crashes after :q, :q! and +related problems. This can also make working with outlines easier when buffers +routinely get unloaded, as when 'hidden' and 'bufhidden' are not set. + +Option 'bufhidden' for Tree buffers is set to "wipe" instead of "hide". +This should make it less likely that an orphan Tree is hanging around long +after its Body is gone. + +PyLog buffer has 'bufhidden' set to "wipe" instead of "hide". +PyLog filetype is set to "voomlog" instead of "log". +PyLog syntax: better highlighting of Python tracebacks. + +In several places in voom.py a new list of Body lines was created for no good +reason: VOOM.buffers[body][:]. These were changed to VOOM.buffers[body], which +is Vim buffer object. This substantially reduces memory usage, especially when +working with large buffers. This affects outline update. Timing tests with +calendar_outline.txt: makeOutline() is slower (0.15 vs 0.11 sec). But the +overall time to run update on Tree BufEnter is about the same (0.16 sec if no +outline change), so it's definitely worth it. +Similarly, there is no need to create a new list of current Tree lines in +updateTree() (tlines_ = Tree[:]) since we compare Tree lines one by one. + +PROBLEM: Tree window-local settings can be wrong if new window is created +manually. Example: cursor is in Body, :split, :b[Tree bufnr]. +FIX: On Tree BufEnter check if w:voom_tree exists. If not, call +Voom_TreeConfigWin()--it sets window-local options and creates w:voom_tree. + +Added "vim" filetype to default g:voom_rstrip_chars: # is stripped in addition +to " because it's comment char in Python etc. sections of .vim files. + + Various code changes + +Renamed VOOM.nodes to VOOM.bnodes to make clearer it is list of Body lnums. + +voom.py functions no longer access voom.vim script-local variables directly. +This means all voom.py functions can be called from add-ons. +Some functions used to compute tree from body like this + tree = int(vim.eval('s:voom_bodies[%s].tree' %body)) +These now require both body and tree as arguments. (updateTree, verifyTree, +nodeUNL) +In several places snLn was set: + vim.command('let s:voom_bodies[%s].snLn=%s' %(body, snLn)) +These now call Voom_SetSnLn(body,snLn) instead. + +When converting buffer lines to/from Python Unicode objects encoding is set to +"utf-8" if &encoding is any Unicode encoding. It turns out Vim uses utf-8 +internally in such cases. See voom.getVimEnc() + +Vim code changes due to new scheme of dealing with unloaded and deleted Body +buffers. Body BufUnload au is gone. New Body BufEnter au detects loss of +buffer-local mappings. + +Tree autocmds are now buffer-local. This seems more robust than relying on Tree +name pattern, easier to disable for individual Trees should we need to do so. +s:voom_TreeBufEnter is not needed anymore. + +Got rid of b:voom_tree and b:voom_body. Use hasmapto('Voom_ToTreeOrBodyWin') to +detect loss of buffer-local mappings. + + +crash after :q, :q! [[[4 ~ + +(reported by William Fugy) +Current tabpage has two windows: Body and corresponding Tree. +There are no other windows with Body or Tree. +'hidden' is off, Body 'bufhidden' is "". +With cursor in Body, :q or :q! produce spectacular crash--sometimes gvim.exe +crashes, sometimes stream of E315 errors. +------------------------------------------------- +The culprit is Body BufUnload autocmd: it wipes out Tree buffer and thus can +close windows and tabs. This confuses :q but not :bun :bd :bw. + +Setting hidden or bufhidden doesn't help because :q! always unloads buffer. + +Kludge attempt in Body BufUnload, before Tree wipeout: + if winnr('$')==2 && bufwinnr(body)>0 && bufwinnr(tree)>0 + new + endif +No crashes after :q or q!. New crash after :bd, :bw in Body. +Creating new window on BufUnload is as dangerous as closing one. + +Not wiping out Tree on Body BufUnload is the only solution. +------------------------------------------------- +Got rid of Body BufUnload au. +Try Body BufWipeout au -- wipe out Tree when Body is wiped out. + +Crash after :q still happens if Body 'bufhidden' is "wipe" -- obviously same +situation as with BufUnload. Such setting seems unlikely. :Voom can refuse to +create outline if current buffer has such setting. + +ANOTHER NASTY GLITCH: +gvim.exe test_outline.txt +:Voom +:bw1 +Tree is gone, window still shows test_outline.txt -- this is horribly wrong. +:Voom +Both Body and Tree are empty. + +**CONCLUSION: DON'T DO IT** +The workaround is function Voom_Delete('ex_command') to be used in custom +mappings. +Also, set Tree 'bufhidden' to wipe instead of hide. + +------------------------- +Possible Body BufWipeout au, should be safe: +if Tree is shown in a window: set Tree bufhidden to wipe +if not: wipe out Tree +This is too convoluted. + + +Tree folds are wrong in split windows after outline operation [[[4~ + +gvim.exe test_outline.txt +:Voom +:set fdc=6 +:split +:split +Copy node "5", Paste after "5.2" +Folds are wrong in 2nd and 3rd window. + +Also affects Tree windows in other tabs. +Folds in the current window are fixed after :setl fdm=expr +-------------------------- +Sorting is not afflicted with this bug. +Sorting is different from other Oops--Tree is drawn while in Tree, on BufEnter. +Change + call Voom_OopFromBody(body,tree,l:blnShow,'') +to + if Voom_BodyUpdateTree()==-1 | let &lz=lz_ | return | endif + call Voom_OopFromBody(body,tree,l:blnShow,'noa') +and folds are wrong in split windows +-------------------------- +Thus, the fix is to draw Tree lines while in Tree. + +Current Oop scheme for most Oops, start in Tree, Vim code: + perform checks, get data + go to Body + check ticks + run Python code: + change Body lines; change Tree lines; adjust bnodes and levels + call Voom_OopFromBody() -- adjust Body view and go back to Tree + adjust Tree view + +New Oop scheme, start in Tree, Vim code: + perform checks, get data + go to Body + check ticks + run Python code: + change Body lines; (adjust bnodes and levels) + call Voom_OopFromBody() -- adjust Body view and go back to Tree + change Tree lines; (adjust bnodes and levels) + adjust Tree view + +Changed the following Oops: Paste, Cut, Up, Down, Right, Left. + +These Oops do not change Tree folds: Mark/Unmark, Mark as selected +No change is needed for: Insert new node (done from Tree), Copy. +Save/Restore/Cleanup Folding do not modify Tree, they are done from Tree. +-------------------------- +Folds can also be wrong in split Tree windows after outline update was forced +from Body after :Voomgrep, :Voomunl, etc. This is rare and no big deal. + + +v2.1, 2010-06-01 [[[3~ + +The procedure for constructing Tree headline text was modifed to permit +customization for individual filetypes: + - Comment chars that are stripped from the right side of Tree headlines + are by default obtained from Body's 'commentstring' option. + - User dictionary g:voom_rstrip_chars can be used to control exactly which + characters are stripped from the right side of Tree headlines. This is + done for individual filetypes and will overide 'commentstring' option. + - Finally, an arbitrary headline constructing function can be defined for + individual filetypes in an add-on. Add-on "custom_headlines.vim" shows + how. +For details, see node + OUTLINING (:Voom) -> Create Outline -> Tree Headline Text + +New user option "g:voom_create_devel_commands" controls if development helper +commands are created. They are commented out in previous versions. + +Removed Tree-local mapping (same as :Voomhelp). + +Bug in PyLog buffer creation/destruction: Python original sys.stdout and +sys.stderr can be lost after some actions, e.g. after command :VoomReloadAll. +FIX: changed how original sys.stdout and sys.stderr are saved. + + +v2.0, 2010-04-01 [[[3~ + +The name of this plugin was changed from VOOF (Vim Outliner Of Folds) to VOoM +(Vim Outliner of Markers): + - The new name is more accurate. It deemphasizes the role of folds. Body + buffer folding has no effect on outline construction or on outline + operations. Markers are determined by option "foldmarker", but only + start fold markers with levels are used. + - Voom sounds better than Voof, more energetic -- vroom-zoom-boom. + (Look matey, this parrot wouldn't "voom" if I put four thousand volts + through it.) + +Corresponding changes were made in file names, commands, user options, help +tags, names of functions and variables. All occurrences of VOOF/Voof/voof were +changed to VOOM/Voom/voom: the command "Voof" became "Voom", +"g:voof_tree_placement" became "g:voom_tree_placement", and so on. + +If you are upgrading from previous versions, please delete old "voof" files +(voof.vim, voof.py, voof.pyc, voof.txt), delete file "voofScript.py" if any, +edit user options in .vimrc if you have any, run :helptags. + +Added rudimentary support for add-ons, sample add-on "voom_info.vim". See node + Implementation notes -> Extending VOoM with add-ons +for details. + +Added instructions for Windows users on how to get Python-enabled Vim. + +Renamed some functions. Other minor code style changes. + +There is an elusive bug in mouse left click Tree mapping. It seems it's +possible for to be triggered in a wrong buffer. Cannot +reproduce, has something to do with resizing windows. +FIX: added check that current buffer is Tree in Voom_TreeMouseClick(). + + +v1.92, 2010-03-03 [[[3~ + +PROBLEM: outline operations Mark/Unmark, Move Right/Left can be slow when they +involve a large number of folds. +EXAMPLE: mark/unmark all nodes in calendar_outline.txt takes about 3 seconds. +But set Body foldmethod to "manual" and the time is reduced to 0.85 seconds. +Set Tree foldmethod to "manual" and the time is reduced further to 0.16 sec. +FIX: Set Tree and Body foldmethod to "manual" during Mark/Unmark. Set Body +foldmethod to manual during Move Right/Left. Other operations are not +susceptible. + +Command :VoofFoldingSave is now much faster when applied to huge and deeply +nested branches with lots of closed folds. The problem was recursive function +foldingGet(). Got rid of recursion -- unnecessary and inefficient. +foldingGet() and foldingGetAll() were merged into foldingGet(). + +If Body "foldmethod" is not "marker", Body node could be hidden in fold after +selecting node. +FIX: do "zv" in Body after: selecting node in Tree, outline operations, on +startup. In other words, if foldmethod is marker, do "zMzvzt" to show selected +Body node. Otherwise do "zvzt". + +Fixed stupid code in Voof_ToTreeOrBodyWin(), which is the command -- no +need to visit all windows to find the target. It was causing confusion when +working with split windows. + +Code tweaks to save precious microseconds: +voof.vim + - Use stridx(line,'|') instead of match(line,'|') in various Tree + functions, including foldexpr. + - Compacted and simplified Tree foldexpr function. +voof.py + - xrange() is now used in many places instead of other iteration methods. + - Cleaned up some code, especially for outline operations. + + +v1.91, 2010-02-06 [[[3~ +Command :Voofgrep can now perform boolean AND and NOT searches. + +Increased maximum number of matches when doing Voofgrep to 10000 from 1000. + +Annoyance: when outline is created, there can be unnecessary scrolling down in +the Tree window. +Fix: Voof_TreeCreate() code that puts cursor on startup node. Do "gg" before +jumping to startup node to counteract scrolling caused by fiddling with folds. +Don't do "zz" if the first or the last Tree line is in the window. + +There were some "normal" in voof.vim. Changed all to "normal!". + + +v1.9, 2009-12-19 [[[3~ +It's now possible to save and restore Tree buffer folding. This feature uses +special node marks 'o' in Body headlines. See |voof_VoofFoldingSave|. + +New Tree mapping: + (Shift-=) finds startup node, if any, that is node marked +with '=' in Body headline. Warns if there are several such nodes. + +Command "Voofrun" was renamed "Voofexec". + +Tree mapping for Execute Script was changed to "e" from +"r", which was in conflict with mapping for "Move Right". + +Executing Python code via Voofexec: source code encoding is now specified on +the first line of script file as per http://www.python.org/dev/peps/pep-0263/. +Encoding is obtained from Body's 'fenc' or, if it's empty, from 'enc'. + +Fixed bug in Voofexec: unsupported script type argument was ignored if +buffer's filetype was a supported script type. More informative message if +script type is unsupported. + +Improved how the command Edit Headline (iIaA) positions cursor in Body +headline: "\<" is used instead of "\w" to find the first word char. This works +better with unicode. + +"g:voof_tree_hight" and "g:voof_log_hight" were renamed "g:voof_tree_height" +and "g:voof_log_height" respectively. + + +v1.8, 2009-09-18 [[[3~ +Bug in Normal mode mappings: nasty errors when attempting to use mapping with +a count, which is not supported, e.g., 3. +Fix: made all mappings start with ":" to clear command line before +calling a function. + +Added highlighting of warning and error messages. + +Added fancy highlighting of Voofunl output: different highlights for headlines +and separators. + +Correction in docs: /CTRL-I is Vim default key for going forward in the +jumps list. + +Distribution now follows Vim directory structure: there are /plugin and /doc +folders. Simplified Voofhelp accordingly: if voof.vim is in dir a/b, voof.txt +is assumed to be in a/doc. + +Changed license to WTFPL, version 2. + +v1.7, 2009-08-31 [[[3~ +Checks that previously checked that Body or Tree buffer exists now check if +the buffer is loaded (bufloaded()). This is needed because bad things happen +when writing to an unloaded buffer via Python's vim.buffer. +See "Implementation notes -> unloaded buffer + python == trouble" + +When killing Trees and PyLog do "bwipeout" instead of "bwipeout!" -- it's +sufficient and safer. + +Adjusted how new Tree window is opened: previous window (^wp) is used if it +shows a Tree buffer. + +PyLog: +Added fail-safe check that ensures PyLog buffer is loaded before being written +to. This can be tested by unloading PyLog with "noa bun" or "noa bd" and then +printing to it: py print "something". +Added workaround for a glitch with the output of help(). +Made voof_logbnr variable script-local. + +v1.6, 2009-08-23 [[[3~ +Added checks to prevent data corruption when outline update fails for any +reason. When these checks fail, the Tree buffer is wiped out and outline data +are cleaned up. These checks can be tested as follows: + - Create outline with the Voof command. + - Delete some lines in Body buffer. + - Move to Tree buffer with + :noa wincmd w + - Tree update did not happen and outline data are out of sync with the + Body. In previous versions, performing outline operation at this stage + would cause data corruption. + - Select new node or try outline operation. Voof will issue error message, + wipe out Tree buffer, and perform clean up. +Another way to test these checks is to modify Body file with an external +application while cursor is in the Tree window. +There is more details in "Implementation notes -> Checking Bodies for ticks". + +Added some other foolproofing measures. + +Improved automatic scrolling of PyLog buffer. Both previous (^wp) and current +window numbers are preserved in tabpages where PyLog is scrolled. Previously, +only current window number was preserved. + +Fixed some bugs. Streamlined some code. + +v1.5, 2009-08-15 [[[3~ +New commands: Voofgrep, Voofunl. + +Fixed blunder in "Move Down" outline operation that could cause outline +corruption. To find node after which to move, the cursor must be put on the +last node of the branch. That was done in Visual mode, but not in Normal mode. + + and in Tree buffers now also work in Visual mode. + +Changed behavior of : move cursor to Body window if current window is +Tree and vice versa. Previous behavior (cycle through all Body and Tree +windows) was less useful and inconsistent with behavior. + +Added checks for Body foldmethod. If it's not "marker": + - folds in Body are not collapsed (zMzv) after node selection in Tree and + after outline operations; + - Voofrun will refuse to run when executed while in Body buffer. + +Made Tree buffers and PyLog buffer unlisted. + +If possible, :Voofhelp command will open voof.txt via "tab help voof.txt" +command, so that tags will be active. + +Made help tags start with "voof_". + +Edited "Why VOoF uses Python": it turns out there is a fast, pure Vim method +to scan for headlines, but it's much less convenient than the Python way: > + let headlines=[] + g/{{{\d\+/ call add(headlines, getline('.')) "}}} + +code improvements [[[4~ +The way "eventignore" was used to temporarily disable autocommands was unsafe. +"eventignore" is no longer set anywhere. "noautocmd" is used instead: +|autocmd-disable|. + +Modified voof.voofUpdate() (formally treeUpdate) to work from any buffer as +long as the Tree is "ma". Voof_TreeBufEnter() now calls voof.voofUpdate() +directly. Voof_BodyUpdateTree() updates Tree while in Body without moving to +Tree. This is extremely useful--can now use outline data while in Body. + +Optimization in voof.voofOutline() parser function: > + if not marker in line: continue +This makes sense because search with marker regexp is 3-4 times slower than +the above membership test, and in a typical outline most lines don't have +markers. Timing voof.voofUpdate() in Voof_TreeBufEnter(), +"calendar_outline.txt" update when headlines unchanged: +0.17 sec instead of 0.24 sec. + +Changed Vim data variables voof_bodies, voof_trees, etc. from global to +script-local. Command VoofPrintData prints these for debugging purposes. +Should external scripts need to read these, a function that returns these +could be provided. + +voof.computeSnLn() uses bisect--should be faster than previous naive code. + +Changed in Voofrun to -- simpler. + +PyLog code is, hopefully, near the state of perfection: when something goes +wrong, the exception info is displayed no matter what. + +voof.oopMarkSelected() -- don't remove just one =, strip all consecutive + +Voof_GetLines() uses winsaveview()/winrestview() to prevent scrolling after +zc/zo. + +Use setreg() to restore registers exactly as shown in help. +Doing "let @z=z_old" is not reliable enough--register mode can change. + + +v1.4, 2009-07-12 [[[3~ +New Tree navigation commands (Normal mode): + x Go to next marked node (mnemonic: find headline marked with 'x'). + X Go to previous marked node. + +"Unmark Node" operation now removes all consecutive 'x' chars from Body +headline instead of just one. This eliminates confusion when a bunch of 'x' is +present after start fold marker level number. For the same reason, "Mark Node +as Selected" (=) now strips 'x' chars after removed '=' char. + +Bug: When Body starts with a headline, click on the first line in Tree (path +info line) doesn't select first node. +Fix: in Python code of Voof_TreeSelect() replaced + nodeEnd = VOOF.nodes[body][lnum]-1 + with + nodeEnd = VOOF.nodes[body][lnum]-1 or 1 + +Fixed errors in LogBufferClass write() method, printing messages when log +buffer doesn't exist. + +Bug: Select more than one lines in Tree and press i/I/A/a. An error in +Voof_OopEdit() occurs. +Fix: Mapped i/I/A/a keys only for Normal mode with nnoremap. They were +mistakenly mapped with noremap. + +A message is now printed when an outline operation is aborted because Body +buffer is readonly or nomodifiable. + +Replaced most Python regions in voof.vim with voof.py functions. + +Renamed some Python functions: +voof_WhatEver() means it's Python code for Voof_WhatEver() Vim function. + +Voof_FoldLines() renamed Voof_GetLines(). +Voof_FoldRun() renamed Voof_Run(). + +Various edits and additions in voof.txt. + + +v1.3, 2009-06-06 [[[3~ +New: start fold marker string is obtained from Vim option 'foldmarker' when +the Voof command is run. Each Body buffer can have its own start fold marker. + +Replaced Body's BufDelete autocommand with BufUnload autocommand. Tree buffer +is now wiped out when its Body is unloaded, deleted or wiped out. Corrected +Body and Tree BufUnload au functions: use "nested" and "noautocmd". + +Added * to chars being stripped during headline construction to allow /**/ +around fold markers. Better syntax highlight for commented headlines in Tree. + +Changed how Tree buffer name is constructed: {bufname}_VOOF{bufnr} instead of +VOOF_{bufname}_{bufnr}. + +When checking if current buffer is a Tree, instead of checking buffer name, do +has_key(g:voof_trees, bufnr('')). + +When eventignore is set, save and restore original eventignore instead of +doing "set eventignore=" . + +Annoyance: Moving Tree window to top/bottom (^W K/J) maximizes window height. +Fix: Don't set "winfixheight" when creating Tree window. I don't understand why +this happens. There is no such problem with "winfixwidth". + +Got rid of Voof_ErrorMsg() and Voof_InfoMsg(). + +Expanded help file. + +v1.2, 2009-05-30 [[[3~ +Bug: after outline operation cursor may be on the last line of range instead +of first (if Visual and there is only one root node). +Fix: tweaked Voof_OopShowTree(). + +Re-wrote Voof_TreeToggleFold() to handle: no fold at cursor; cursor hidden in +fold. + +Allow outline operation Copy when Body is noma or ro. + +v1.1, 2009-05-26 [[[3~ +Bug fix involving nomodifiable and readonly buffers. +Outline operations now silently abort if Body is noma or ro. + +v1.0, 2009-05-25 [[[3~ +Initial release. + +============================================================================== +modelines [[[1~ + vim:fdm=marker:fmr=[[[,]]]:ft=help:ai:et:noma:ro: + vim:foldtext=getline(v\:foldstart).'...'.(v\:foldend-v\:foldstart): diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom.vim b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom.vim new file mode 100644 index 00000000..efdb5954 --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom.vim @@ -0,0 +1,2936 @@ +" voom.vim +" Last Modified: 2012-05-05 +" VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +" Version: 4.3 +" Website: http://www.vim.org/scripts/script.php?script_id=2657 +" Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +" License: This program is free software. It comes without any warranty, +" to the extent permitted by applicable law. You can redistribute it +" and/or modify it under the terms of the Do What The Fuck You Want To +" Public License, Version 2, as published by Sam Hocevar. +" See http://sam.zoy.org/wtfpl/COPYING for more details. + + +"---Conventions-------------------------------{{{1 +" Tree --Tree buffer +" Body --Body buffer +" tree --Tree buffer number +" body --Body buffer number +" headline --Body line with a matching fold marker, also a Tree line +" node --Body region between two headlines, usually also a fold. +" A node is identified by Tree lnum (nodes) or Body lnum (bnodes). +" nodes --list of Tree lnums +" bnodes --list of Body lnums, line numbers of Body headlines +" bnr --buffer number +" wnr, tnr --window number, tab number +" lnum, ln, lnr --line number, usually Tree +" blnum, bln, blnr --Body line number +" tline(s) --Tree line(s) +" bline(s) --Body line(s) +" snLn --selected node line number, a Tree line number +" var_ --previous value of var +" l:var --this var is set by Python code (l:blnShow) +" z, Z --list siZe, usually len(bnodes) + + +"---Quickload---------------------------------{{{1 +if !exists('s:voom_did_quickload') + com! -complete=custom,Voom_Complete -nargs=? Voom call Voom_Init() + com! -complete=custom,Voom_Complete -nargs=? VoomToggle call Voom_Init(,1) + com! Voomhelp call Voom_Help() + com! Voomlog call Voom_LogInit() + com! -nargs=? Voomexec call Voom_Exec() + exe "au FuncUndefined Voom_* source " . expand(":p") + " support for Vim sessions (:mksession) + au BufFilePost __PyLog__ call Voom_LogSessionLoad() + au BufFilePost *_VOOM\d\+ call Voom_TreeSessionLoad() + let s:voom_did_quickload = 'v4.3' + finish +endif + + +"---Initialize--------------------------------{{{1 +if !exists('s:voom_did_init') + let s:script_path = substitute(expand(":p"),'\','/','g') + let s:script_dir = substitute(expand(":p:h"),'\','/','g') + let s:voom_dir = s:script_dir.'/voom' + + let s:voom_logbnr = 0 + + " {tree : associated body, ...} + let s:voom_trees = {} + " {body : {'tree' : associated tree, + " 'blnr' : Body cursor lnum, set when selecting node from Body, + " 'snLn' : selected node Tree lnum, + " 'mmode' : 0 (no mode or fmr mode) 1 (markup mode), + " 'tick' : b:changedtick of Body on Body BufLeave, + " 'tick_' : b:changedtick of Body on last Tree update}, {...}, ... } + let s:voom_bodies = {} + +python << EOF +import sys, vim +if not vim.eval("s:voom_dir") in sys.path: + sys.path.append(vim.eval("s:voom_dir")) +import voom +sys.modules['voom'].VOOMS = {} +EOF + au! FuncUndefined Voom_* + let s:voom_did_init = 1 +endif + + +"---User Options------------------------------{{{1 +" These can be defined in .vimrc . + +" Where Tree window is created: 'left', 'right', 'top', 'bottom' +" This is relative to the current window. +if !exists('g:voom_tree_placement') + let g:voom_tree_placement = 'left' +endif +" Initial Tree window width. +if !exists('g:voom_tree_width') + let g:voom_tree_width = 30 +endif +" Initial Tree window height. +if !exists('g:voom_tree_height') + let g:voom_tree_height = 12 +endif + +" Where Log window is created: 'left', 'right', 'top', 'bottom' +" This is far left/right/top/bottom. +if !exists('g:voom_log_placement') + let g:voom_log_placement = 'bottom' +endif +" Initial Log window width. +if !exists('g:voom_log_width') + let g:voom_log_width = 30 +endif +" Initial Log window height. +if !exists('g:voom_log_height') + let g:voom_log_height = 12 +endif + +" Verify outline after outline operations. +if !exists('g:voom_verify_oop') + let g:voom_verify_oop = 1 +endif + +" Which key to map to Select-Node-and-Shuttle-between-Body/Tree +if !exists('g:voom_return_key') + let g:voom_return_key = '' +endif + +" Which key to map to Shuttle-between-Body/Tree +if !exists('g:voom_tab_key') + let g:voom_tab_key = '' +endif + +" g:voom_rstrip_chars_{filetype} -- string with chars to strip from right side +" of Tree headlines for Body 'filetype' {filetype}. +" If defined, these will be used instead of 'commentstring' chars. +if !exists('g:voom_rstrip_chars_vim') + let g:voom_rstrip_chars_vim = "\"# \t" +endif +if !exists('g:voom_rstrip_chars_text') + let g:voom_rstrip_chars_text = " \t" +endif +if !exists('g:voom_rstrip_chars_help') + let g:voom_rstrip_chars_help = " \t" +endif + + +"---Commands----------------------------------{{{1 +" Main commands are defined in Quickload section. +" Naming convention: Voomdoit will not modify Body, VoomDoit can modify Body. + +com! Voomunl call Voom_EchoUNL() +com! -nargs=? Voomgrep call Voom_Grep() +com! -range -nargs=? VoomSort call Voom_OopSort(,, ) + +com! -range VoomFoldingSave call Voom_OopFolding(,, 'save') +com! -range VoomFoldingRestore call Voom_OopFolding(,, 'restore') +com! -range VoomFoldingCleanup call Voom_OopFolding(,, 'cleanup') + +com! Voomtoggle call Voom_ToggleTreeWindow() +com! Voomquit call Voom_DeleteOutline() +com! VoomQuitAll call Voom_DeleteOutlines() + +""" development helpers +if exists('g:voom_create_devel_commands') + " print Vim-side data + com! VoomPrintData call Voom_PrintData() + " reload voom.vim (outlines are preserved) + com! VoomReloadVim exe 'so '.s:script_path + " wipe out Trees, PyLog, delete Python modules, reload voom.vim and voom.py + " Note: simply reloading Python modules is pointless since v4.2 + com! VoomReloadAll call Voom_ReloadAllPre() | exe 'so '.s:script_path +endif + + +"---Voom_Init(), various commands, helpers----{{{1 + +func! Voom_Init(qargs,...) "{{{2 +" Commands :Voom, :VoomToggle. + let bnr = bufnr('') + " Current buffer is Tree. + if has_key(s:voom_trees, bnr) + let body = s:voom_trees[bnr] + if a:0 && a:1 + call Voom_UnVoom(body, bnr) + return + endif + if !hasmapto('Voom_ToTreeOrBodyWin','n') + echoerr "VOoM: Tree lost mappings. Reconfiguring..." + call Voom_TreeConfig(body) + endif + call Voom_ToBody(body) + return + " Current buffer is Body. + elseif has_key(s:voom_bodies, bnr) + let tree = s:voom_bodies[bnr].tree + if a:0 && a:1 + call Voom_UnVoom(bnr, tree) + return + endif + if !hasmapto('Voom_ToTreeOrBodyWin','n') + echoerr "VOoM: Body lost mappings. Reconfiguring..." + call Voom_BodyConfig() + endif + call Voom_ToTree(tree) + return + endif + " Current buffer is not a VOoM buffer. Create Tree for it. Current buffer + " becomes a Body buffer. + let body = bnr + let s:voom_bodies[body] = {} + let s:voom_bodies[body].blnr = line('.') + let [b_name, b_dir] = [expand('%:p:t'), expand('%:p:h')] + if b_name=='' | let b_name='No Name' | endif + let l:firstLine = ' '.b_name.' ['.b_dir.'], b'.body + let [l:mmode, l:qargs] = [-1, a:qargs] + python voom.voom_Init(int(vim.eval('l:body'))) + if l:mmode < 0 | unlet s:voom_bodies[body] | return | endif + let s:voom_bodies[body].mmode = l:mmode + call Voom_BodyConfig() + call Voom_ToTreeWin() + call Voom_TreeCreate(body) + if a:0 && a:1 + call Voom_ToBody(body) + return + endif +endfunc + + +func! Voom_Complete(A,L,P) "{{{2 +" Argument completion for command :Voom. Return string "wiki\nvimwiki\nviki..." +" constructed from file names ../plugin/voom/voom_mode_{whatever}.py . + let thefiles = split(glob(s:voom_dir.'/voom_mode_?*.py'), "\n") + let themodes = [] + for the in thefiles + let themode = substitute(fnamemodify(the,':t'), '\c^voom_mode_\(.*\)\.py$', '\1', '') + call add(themodes, themode) + endfor + return join(themodes, "\n") +endfunc + + +func! Voom_Help() "{{{2 +" Open voom.txt as outline in a new tabpage. + let help_path = fnamemodify(s:script_dir.'/../doc/voom.txt', ":p") + if !filereadable(help_path) + echoerr "VOoM: can't read help file:" help_path + return + endif + + """ voom.txt exists and is shown in some window in some tab -- go there + let help_bufnr = bufnr('^'.help_path.'$') + if help_bufnr > 0 + let alltabs = range(tabpagenr(),tabpagenr('$')) + range(1,tabpagenr()-1) + for tnr in alltabs + if index(tabpagebuflist(tnr), help_bufnr) > -1 + exe 'tabnext '.tnr + exe bufwinnr(help_bufnr).'wincmd w' + " make sure critical settings are correct + if &ft!=#'help' + set ft=help + endif + if &fmr!=#'[[[,]]]' || &fdm!=#'marker' + setl fmr=[[[,]]] fdm=marker + endif + " make sure outline is present + call Voom_Init('') + return + endif + endfor + endif + + """ try 'tab help' command + let help_installed = 1 + let [tnr_, tnrM_] = [tabpagenr(), tabpagenr('$')] + try + silent tab help voom.txt + catch /^Vim\%((\a\+)\)\=:E149/ " no help for voom.txt + let help_installed = 0 + catch /^Vim\%((\a\+)\)\=:E429/ " help file not found--removed after installing + let help_installed = 0 + endtry + if help_installed==1 + if fnamemodify(bufname(""), ":t")!=#'voom.txt' + echoerr "VOoM: internal error" + return + endif + if &fmr!=#'[[[,]]]' || &fdm!=#'marker' + setl fmr=[[[,]]] fdm=marker + endif + call Voom_Init('') + return + " 'tab help' failed, we are on new empty tabpage -- kill it + elseif tabpagenr()!=tnr_ && tabpagenr('$')==tnrM_+1 && bufname('')=='' && winnr('$')==1 + bwipeout + exe 'tabnext '.tnr_ + endif + + """ open voom.txt as regular file + exe 'tabnew '.fnameescape(help_path) + if fnamemodify(bufname(""), ":t")!=#'voom.txt' + echoerr "VOoM: internal error" + return + endif + if &ft!=#'help' + setl ft=help + endif + if &fmr!=#'[[[,]]]' || &fdm!=#'marker' + setl fmr=[[[,]]] fdm=marker + endif + call Voom_Init('') +endfunc + + +func! Voom_DeleteOutline(...) "{{{2 +" Delete current outline, execute Ex command if in Body or non-VOoM buffer. + let bnr = bufnr('') + " current buffer is Tree + if has_key(s:voom_trees, bnr) + call Voom_UnVoom(s:voom_trees[bnr], bnr) + return + " current buffer is Body + elseif has_key(s:voom_bodies, bnr) + call Voom_UnVoom(bnr, s:voom_bodies[bnr].tree) + endif + " current buffer is Body or non-VOoM buffer + if a:0 + execute a:1 + endif +endfunc + + +func! Voom_DeleteOutlines() "{{{2 +" Delete all VOoM outlines. + for bnr in keys(s:voom_trees) + let tree = str2nr(bnr) + call Voom_UnVoom(s:voom_trees[tree], tree) + endfor +endfunc + + +func! Voom_UnVoom(body,tree) "{{{2 +" Remove VOoM data for Body body and its Tree tree. +" Wipeout Tree, delete Body au, etc. +" Can be called from any buffer. +" Note: when called from Tree BufUnload au, tree doesn't exist. + if has_key(s:voom_bodies, a:body) && has_key(s:voom_trees, a:tree) + unlet s:voom_bodies[a:body] + unlet s:voom_trees[a:tree] + else + echoerr 'VOoM: internal error' + return + endif + python voom.voom_UnVoom(int(vim.eval('a:body'))) + exe 'au! VoomBody * ' + if bufexists(a:tree) + "exe 'noautocmd bwipeout '.a:tree + exe 'au! VoomTree * ' + exe 'bwipeout '.a:tree + endif + if bufnr('')==a:body + call Voom_BodyUnMap() + endif +endfunc + + +func! Voom_FoldStatus(lnum) "{{{2 + " there is no fold + if foldlevel(a:lnum)==0 + return 'nofold' + endif + let fc = foldclosed(a:lnum) + " line is hidden in fold, cannot determine it's status + if fc < a:lnum && fc > 0 + return 'hidden' + " line is first line of a closed fold + elseif fc==a:lnum + return 'folded' + " line is in an opened fold + else + return 'notfolded' + endif +" Helper for dealing with folds. Determine if line lnum is: +" not in a fold; +" hidden in a closed fold; +" not hidden and is a closed fold; +" not hidden and is in an open fold. +endfunc + + +func! Voom_WarningMsg(...) "{{{2 + echohl WarningMsg + for line in a:000 + echo line + endfor + echohl None +endfunc + + +func! Voom_ErrorMsg(...) "{{{2 + echohl ErrorMsg + for line in a:000 + echom line + endfor + echohl None +endfunc + + +func! Voom_BufLoaded(body) "{{{2 + if !bufloaded(a:body) + if bufexists(a:body) + let bname = fnamemodify(bufname(a:body),":t") + call Voom_ErrorMsg('VOoM: Body buffer '.a:body.' ('.bname.') is not loaded') + else + call Voom_ErrorMsg('VOoM: Body buffer '.a:body.' does not exist') + endif + return -1 + endif +endfunc + + +func! Voom_BufEditable(body) "{{{2 +" Check if Body is 'noma' or 'ro' before outline operation. +" Also catches if buffer doesn't exist. + if getbufvar(a:body, "&ma")==0 || getbufvar(a:body, "&ro")==1 + let bname = fnamemodify(bufname(a:body),":t") + call Voom_ErrorMsg("VOoM: Body buffer ".a:body." (".bname.") is 'nomodifiable' or 'readonly'") + return -1 + endif +endfunc + + +func! Voom_SetSnLn(body, snLn) "{{{2 +" Set snLn. Used by Python code. + let s:voom_bodies[a:body].snLn= a:snLn +endfunc + + +func! Voom_ToggleTreeWindow() "{{{2 +" Mimimize/restore Tree window. + let bnr = bufnr('') + if has_key(s:voom_bodies, bnr) + let [body, tree, inBody] = [bnr, s:voom_bodies[bnr].tree, 1] + elseif has_key(s:voom_trees, bnr) + let [body, tree, inBody] = [s:voom_trees[bnr], bnr, 0] + else + call Voom_ErrorMsg("VOoM: current buffer is not a VOoM buffer") + return + endif + + if inBody + if Voom_ToTree(tree)!=0 | return | endif + endif + + " current window width (w) and height (h) + let [winw, winh] = [winwidth(0), winheight(0)] + " maximum possible w and h (-2 for statusline and tabline) + let [maxw, maxh] = [&columns, &lines-&cmdheight-2] + " minimize w, h, or both + if winw > 1 && winh > 1 + let w:voom_winsave = winsaveview() + if winw < maxw + let w:voom_w = winw + vertical resize 1 + endif + if winh < maxh + let w:voom_h = winh + resize 1 + endif + " restore w, h, or both + else + if winw <= 1 + let w = exists('w:voom_w') ? w:voom_w : g:voom_tree_width + exe 'vertical resize '.w + endif + if winh <= 1 + let h = exists('w:voom_h') ? w:voom_h : g:voom_tree_height + exe 'resize '.h + endif + if exists('w:voom_winsave') + call winrestview(w:voom_winsave) + endif + endif + + if inBody | call Voom_ToBody(body) | endif +endfunc + + +"--- for external scripts --- {{{2 + +func! Voom_GetVar(var) "{{{2 + return {a:var} +endfunc + + +func! Voom_GetData() "{{{2 + return [s:voom_bodies, s:voom_trees] +endfunc + + +func! Voom_GetBufInfo(...) "{{{2 + let bnr = bufnr('') + if has_key(s:voom_trees, bnr) + let [bufType, body, tree] = ['Tree', s:voom_trees[bnr], bnr] + if Voom_BufLoaded(body) < 0 | return ['Tree',-1,-1] | endif + elseif has_key(s:voom_bodies, bnr) + let [bufType, body, tree] = ['Body', bnr, s:voom_bodies[bnr].tree] + if Voom_BodyUpdateTree() < 0 | return ['Body',-1,-1] | endif + else + if !(a:0 && a:1) + call Voom_ErrorMsg("VOoM: current buffer is not a VOoM buffer") + endif + return ['None',0,0] + endif + return [bufType, body, tree] +" Helper for external scripts and add-ons. +" Return ['Body'/'Tree', body, tree] for the current buffer. +" Return ['None',0,0] if current buffer is neither Body nor Tree and print +" error message. To supress the error message: Voom_GetBufInfo(1) +" Return ['Body'/'Tree',-1,-1] if outline is not available. +" Update outline if current buffer is Body. +endfunc + + +func! Voom_PrintData() "{{{2 +" Print Vim-side VOoM data. + redir => voomData + silent echo repeat('-', 60) + for v in ['s:voom_did_quickload', 's:voom_did_init', 's:voom_logbnr', 's:script_dir', 's:script_path', 's:voom_dir', 'g:voom_verify_oop', 's:voom_trees', 's:voom_bodies'] + silent echo v '--' {v} + endfor + redir END + echo ' ' + python print vim.eval('l:voomData') +endfunc + + +func! Voom_ReloadAllPre() "{{{2 +" Helper for reloading the entire plugin and all modes. +" Wipe out all Tree buffers and PyLog buffer. Delete Python voom modules. + call Voom_DeleteOutlines() + if s:voom_logbnr && bufexists(s:voom_logbnr) + exe 'bwipeout '.s:voom_logbnr + endif +python << EOF +sys.exc_clear() +del sys.modules['voom'] +for k in sys.modules.keys(): + if k.startswith('voom_mode_'): + del sys.modules[k] +del k +EOF + unlet s:voom_did_init +endfunc + + +"---Windows Navigation and Creation-----------{{{1 +" These deal only with the current tab page. + +func! Voom_ToTreeOrBodyWin() "{{{2 +" If in Tree window, move to Body window. +" If in Body window, move to Tree window. +" If possible, use previous window. + let bnr = bufnr('') + " current buffer is Tree + if has_key(s:voom_trees, bnr) + let target = s:voom_trees[bnr] + " current buffer is Body + else + " This happens after Tree is wiped out. + if !has_key(s:voom_bodies, bnr) + call Voom_BodyUnMap() + return + endif + let target = s:voom_bodies[bnr].tree + endif + " Try previous window. It's the most common case. + let wnr = winnr('#') + if winbufnr(wnr)==target + exe wnr.'wincmd w' + return + endif + " Use any other window. + if bufwinnr(target) > 0 + exe bufwinnr(target).'wincmd w' + return + endif +endfunc + + +func! Voom_ToTreeWin() "{{{2 +" Move to window or create a new one where a Tree will be loaded. + " Already in a Tree buffer. + if has_key(s:voom_trees, bufnr('')) | return | endif + + " Use previous window if it shows Tree. + let wnr = winnr('#') + if has_key(s:voom_trees, winbufnr(wnr)) + exe wnr.'wincmd w' + call Voom_SplitIfUnique() + return + endif + + " Use any window with a Tree buffer. + for bnr in tabpagebuflist() + if has_key(s:voom_trees, bnr) + exe bufwinnr(bnr).'wincmd w' + call Voom_SplitIfUnique() + return + endif + endfor + + " Create new window. + if g:voom_tree_placement=='top' + exe 'leftabove '.g:voom_tree_height.'split' + elseif g:voom_tree_placement=='bottom' + exe 'rightbelow '.g:voom_tree_height.'split' + elseif g:voom_tree_placement=='left' + exe 'leftabove '.g:voom_tree_width.'vsplit' + elseif g:voom_tree_placement=='right' + exe 'rightbelow '.g:voom_tree_width.'vsplit' + endif +endfunc + + +func! Voom_SplitIfUnique() "{{{2 +" Split current window if current buffer is not displayed in any other window +" in current tabpage. + let bnr = bufnr('') + let wnr = winnr() + for i in range(1,winnr('$')) + if winbufnr(i)==bnr && i!=wnr + return + endif + endfor + if winheight(0) * 2 >= winwidth(0) + leftabove split + else + leftabove vsplit + endif +endfunc + + +func! Voom_ToTree(tree) abort "{{{2 +" Move cursor to window with Tree buffer tree. +" If there is no such window, load buffer in a new window. + " Already there. + if bufnr('')==a:tree | return | endif + + " Try previous window. + let wnr = winnr('#') + if winbufnr(wnr)==a:tree + exe wnr.'wincmd w' + return + endif + + " There is window with buffer a:tree. + if bufwinnr(a:tree) > 0 + exe bufwinnr(a:tree).'wincmd w' + return + endif + + " Bail out if Tree is unloaded or doesn't exist. + " Because of au, this should never happen. + if !bufloaded(a:tree) + let body = s:voom_trees[a:tree] + call Voom_UnVoom(body,a:tree) + echoerr "VOoM: Tree buffer" a:tree "is not loaded or does not exist. Cleanup has been performed." + return -1 + endif + + " Load Tree in appropriate window. + call Voom_ToTreeWin() + silent exe 'b '.a:tree + " window-local options will be set on BufEnter + return 1 +endfunc + + +func! Voom_ToBodyWin() "{{{2 +" Split current Tree window to create window where Body will be loaded + if g:voom_tree_placement=='top' + exe 'leftabove '.g:voom_tree_height.'split' + wincmd p + elseif g:voom_tree_placement=='bottom' + exe 'rightbelow '.g:voom_tree_height.'split' + wincmd p + elseif g:voom_tree_placement=='left' + exe 'leftabove '.g:voom_tree_width.'vsplit' + wincmd p + elseif g:voom_tree_placement=='right' + exe 'rightbelow '.g:voom_tree_width.'vsplit' + wincmd p + endif +endfunc + + +func! Voom_ToBody(body) abort "{{{2 +" Move to window with Body a:body or load it in a new window. + " Already there. + if bufnr('')==a:body | return | endif + + " Try previous window. + let wnr = winnr('#') + if winbufnr(wnr)==a:body + exe wnr.'wincmd w' + return + endif + + " There is a window with buffer a:body . + if bufwinnr(a:body) > 0 + exe bufwinnr(a:body).'wincmd w' + return + endif + + if !bufloaded(a:body) + " Body is unloaded. Load it and force outline update. + if bufexists(a:body) + call Voom_ToBodyWin() + exe 'b '.a:body + call Voom_BodyUpdateTree() + call Voom_WarningMsg('VOoM: loaded Body buffer and updated outline') + " Body doesn't exist. Bail out. + else + let tree = s:voom_bodies[a:body].tree + if !has_key(s:voom_trees, tree) || s:voom_trees[tree]!=a:body + echoerr "VOoM: internal error" + return -1 + endif + call Voom_UnVoom(a:body,tree) + call Voom_ErrorMsg("VOoM: Body ".a:body." does not exist. Cleanup has been performed.") + endif + return -1 + endif + + " Create new window and load there. + call Voom_ToBodyWin() + exe 'b '.a:body + return 1 +endfunc + + +func! Voom_ToLogWin() "{{{2 +" Create new window where PyLog will be loaded. + if g:voom_log_placement=='top' + exe 'topleft '.g:voom_log_height.'split' + elseif g:voom_log_placement=='bottom' + exe 'botright '.g:voom_log_height.'split' + elseif g:voom_log_placement=='left' + exe 'topleft '.g:voom_log_width.'vsplit' + elseif g:voom_log_placement=='right' + exe 'botright '.g:voom_log_width.'vsplit' + endif +endfunc + + +"---TREE BUFFERS------------------------------{{{1 + +func! Voom_TreeCreate(body) "{{{2 +" Create new Tree buffer for Body body in the current window. + let b_name = fnamemodify(bufname(a:body),":t") + if b_name=='' | let b_name='NoName' | endif + silent exe 'edit '.fnameescape(b_name).'_VOOM'.a:body + let tree = bufnr('') + let blnr = s:voom_bodies[a:body].blnr + + """ Finish initializing VOoM data for this Body. + let s:voom_bodies[a:body].tree = tree + let s:voom_trees[tree] = a:body + let s:voom_bodies[a:body].tick_ = 0 + python voom.VOOMS[int(vim.eval('a:body'))].tree = int(vim.eval('l:tree')) + python voom.VOOMS[int(vim.eval('a:body'))].Tree = vim.current.buffer + + call Voom_TreeConfig(a:body) + + let l:blnShow = -1 + """ Create outline and draw Tree lines. + let lz_ = &lz | set lz + setl ma + let ul_=&ul | setl ul=-1 + try + let l:ok = 0 + keepj python voom.updateTree(int(vim.eval('a:body')), int(vim.eval('l:tree'))) + " Draw = mark. Create folding from o marks. + " This must be done afer creating outline. + " this assigns s:voom_bodies[body].snLn + if l:ok + python voom.voom_TreeCreate() + let snLn = s:voom_bodies[a:body].snLn + " Initial draw puts = on first line. + if snLn > 1 + keepj call setline(snLn, '='.getline(snLn)[1:]) + keepj call setline(1, ' '.getline(1)[1:]) + endif + let s:voom_bodies[a:body].tick_ = s:voom_bodies[a:body].tick + endif + finally + let &ul=ul_ + setl noma + let &lz=lz_ + endtry + + """ Position cursor on snLn line. ../doc/voom.txt#id_20110125210844 + keepj normal! gg + if snLn > 1 + exe "normal! ".snLn."G0f|m'" + call Voom_TreeZV() + if line('w0')!=1 && line('w$')!=line('$') + normal! zz + endif + endif + + "--- the end if markup mode --- + " blnShow is set by voom_TreeCreate() when there is Body headline marked with = + if l:blnShow > 0 + " go to Body + let wnr_ = winnr() + if Voom_ToBody(a:body) < 0 | return | endif + " show fold at l:blnShow + exe 'keepj normal! '.l:blnShow.'G' + if &fdm==#'marker' + normal! zMzvzt + else + normal! zvzt + endif + " go back to Tree + let wnr_ = winnr('#') + if winbufnr(wnr_)==tree + exe wnr_.'wincmd w' + else + exe bufwinnr(tree).'wincmd w' + endif + endif +endfunc + + +func! Voom_TreeConfig(body) "{{{2 +" Configure current buffer as a Tree buffer. + augroup VoomTree + au! * + au BufEnter call Voom_TreeBufEnter() + "au BufUnload call Voom_TreeBufUnload() + au BufUnload nested call Voom_TreeBufUnload() + augroup END + + call Voom_TreeMap() + + " Options local to window. + call Voom_TreeConfigWin() + + " local to buffer, may be changed by the user + setl bufhidden=wipe + + " This should allow customizing via ftplugin. Removes syntax hi. + setl ft=voomtree + + " Options local to buffer. Should not be changed. + setl nobuflisted buftype=nofile noswapfile + setl noro ma ff=unix noma + + call Voom_TreeSyntax(a:body) +endfunc + + +func! Voom_TreeConfigWin() "{{{2 +" Tree window-local options. + setl foldenable + setl foldtext=getline(v:foldstart).'\ \ \ /'.(v:foldend-v:foldstart) + setl foldmethod=expr + setl foldexpr=Voom_TreeFoldexpr(v:lnum) + setl cul nocuc nowrap nolist + "setl winfixheight + setl winfixwidth + + let w:voom_tree = 'VOoM' +endfunc + + +func! Voom_TreeBufEnter() "{{{2 +" Tree BufEnter au. +" Update outline if Body changed since last update. Redraw Tree if needed. + let tree = bufnr('') + let body = s:voom_trees[tree] + + if !exists('w:voom_tree') + call Voom_TreeConfigWin() + endif + + """ update is not needed + if s:voom_bodies[body].tick_==s:voom_bodies[body].tick + return + endif + + """ don't update if Body is not loaded + if Voom_BufLoaded(body) < 0 | return | endif + + """ do update + let snLn_ = s:voom_bodies[body].snLn + setl ma + let ul_=&ul | setl ul=-1 + try + let l:ok = 0 + keepj python voom.updateTree(int(vim.eval('l:body')), int(vim.eval('l:tree'))) + if l:ok + let s:voom_bodies[body].tick_ = s:voom_bodies[body].tick + endif + finally + let &ul=ul_ + setl noma + endtry + + " The = mark is placed by updateTree() + " When nodes are deleted by editing Body, snLn can get > last Tree lnum, + " updateTree() will set snLn to the last line lnum. + if snLn_ != s:voom_bodies[body].snLn + keepj normal! Gzv + endif +endfunc + + +func! Voom_TreeBufUnload() "{{{2 +" Tree BufUnload au. Wipe out Tree and cleanup. + let tree = expand("") + if !exists("s:voom_trees") || !has_key(s:voom_trees, tree) + echoerr "VOoM: internal error" + return + endif + let body = s:voom_trees[tree] + "echom bufexists(tree) --always 0 + "exe 'noautocmd bwipeout '.tree + exe 'au! VoomTree * ' + exe 'bwipeout '.tree + call Voom_UnVoom(body,tree) +endfunc + + +func! Voom_TreeFoldexpr(lnum) "{{{2 + let ind = stridx(getline(a:lnum),'|') / 2 + let indn = stridx(getline(a:lnum+1),'|') / 2 + return indn>ind ? '>'.ind : ind-1 + "return indn>ind ? '>'.ind : indnind ? '>'.ind : '<'.indn +endfunc + + +func! Voom_TreeSyntax(body) "{{{2 +" Tree buffer default syntax highlighting. + " first line + syn match Title /\%1l.*/ + + let FT = getbufvar(a:body, "&ft") + if FT==#'text' + " organizer nodes: /headline/ + syn match Comment '^[^|]\+|\zs[/#].*' contains=Todo + syn keyword Todo TODO XXX FIXME + elseif FT==#'python' + syn match Statement /^[^|]\+|\zs\%(def\s\|class\s\)/ + syn match Define /^[^|]\+|\zs@/ + syn match Comment /^[^|]\+|\zs#.*/ contains=Todo + syn keyword Todo contained TODO XXX FIXME + elseif FT==#'vim' + syn match Statement /^[^|]\+|\zs\%(fu\%[nction]\>\|def\s\|class\s\)/ + syn match Comment /^[^|]\+|\zs\%("\|#\).*/ contains=Todo + syn keyword Todo contained TODO XXX FIXME + elseif FT==#'html' || FT==#'xml' + syn match Comment /^[^|]\+|\zs i +noremap I +noremap a +noremap A +noremap o +noremap O +noremap s +noremap S +noremap r +noremap R +noremap x +noremap X +noremap D +noremap J +noremap c +noremap C +noremap P +noremap . +noremap = +noremap +noremap +noremap +noremap p +noremap d +noremap < +noremap > +noremap ^ +noremap _ + +" disable undo (also case conversion) +noremap u +noremap U +noremap + +" disable creation/deletion of folds +noremap zf +noremap zF +noremap zd +noremap zD +noremap zE + """ }}} + + """ edit headline {{{ +nnoremap i :call Voom_OopEdit() +nnoremap I :call Voom_OopEdit() +nnoremap a :call Voom_OopEdit() +nnoremap A :call Voom_OopEdit() + """ }}} + + """ node navigation and selection {{{ +"--- the following select node ----------- +exe "nnoremap ".g:voom_return_key." :call Voom_TreeSelect(0)" +exe "vnoremap ".g:voom_return_key." :call Voom_TreeSelect(0)" +"exe "vnoremap ".g:voom_return_key." " +exe "nnoremap ".g:voom_tab_key." :call Voom_ToTreeOrBodyWin()" +exe "vnoremap ".g:voom_tab_key." :call Voom_ToTreeOrBodyWin()" +"exe "vnoremap ".g:voom_tab_key." " + +" MOUSE: Left mouse release. Triggered when resizing window with the mouse. +nnoremap :call Voom_TreeMouseClick() +inoremap +" disable Left mouse double click to avoid entering Visual mode +nnoremap <2-LeftMouse> + +nnoremap :call Voom_TreeSelect(1) +nnoremap :call Voom_TreeSelect(1) + +nnoremap :call Voom_TreeLeft() +nnoremap :call Voom_TreeRight() + +nnoremap x :call Voom_TreeToMark(0) +nnoremap X :call Voom_TreeToMark(1) + +"--- the following don't select node ----------- + +nnoremap :call Voom_TreeToggleFold() +"vnoremap :call Voom_TreeToggleFold() + +" put cursor on the selected node +nnoremap = :call Voom_TreeToSelected() +" put cursor on the node marked with '=', if any +nnoremap + :call Voom_TreeToStartupNode() + +" go up to the parent node +nnoremap P :call Voom_Tree_Pco('P','n') +" go up to the parent node and contract it +nnoremap c :call Voom_Tree_Pco('c','n') +" go down to direct child node +nnoremap o :call Voom_Tree_Pco('o','n') + +" contract all siblings of current node +nnoremap C :call Voom_Tree_CO('zC','n') +" contract all nodes in Visual selection +vnoremap C :call Voom_Tree_CO('zC','v') +" expand all siblings of current node +nnoremap O :call Voom_Tree_CO('zO','n') +" expand all nodes in Visual selection +vnoremap O :call Voom_Tree_CO('zO','v') + +" go up to the previous sibling +nnoremap K :call Voom_Tree_KJUD('K','n') +vnoremap K :call Voom_Tree_KJUD('K','v') +" go down to the next sibling +nnoremap J :call Voom_Tree_KJUD('J','n') +vnoremap J :call Voom_Tree_KJUD('J','v') +" go up to the uppermost sibling +nnoremap U :call Voom_Tree_KJUD('U','n') +vnoremap U :call Voom_Tree_KJUD('U','v') +" go down to the downmost sibling +nnoremap D :call Voom_Tree_KJUD('D','n') +vnoremap D :call Voom_Tree_KJUD('D','v') + """ }}} + + """ outline operations {{{ +" insert new node +nnoremap i :call Voom_OopInsert('') +nnoremap I :call Voom_OopInsert('as_child') + +" move +nnoremap u :call Voom_Oop('up', 'n') +nnoremap :call Voom_Oop('up', 'n') +nnoremap ^^ :call Voom_Oop('up', 'n') +vnoremap u :call Voom_Oop('up', 'v') +vnoremap :call Voom_Oop('up', 'v') +vnoremap ^^ :call Voom_Oop('up', 'v') + +nnoremap d :call Voom_Oop('down', 'n') +nnoremap :call Voom_Oop('down', 'n') +nnoremap __ :call Voom_Oop('down', 'n') +vnoremap d :call Voom_Oop('down', 'v') +vnoremap :call Voom_Oop('down', 'v') +vnoremap __ :call Voom_Oop('down', 'v') + +nnoremap l :call Voom_Oop('left', 'n') +nnoremap :call Voom_Oop('left', 'n') +nnoremap << :call Voom_Oop('left', 'n') +vnoremap l :call Voom_Oop('left', 'v') +vnoremap :call Voom_Oop('left', 'v') +vnoremap << :call Voom_Oop('left', 'v') + +nnoremap r :call Voom_Oop('right', 'n') +nnoremap :call Voom_Oop('right', 'n') +nnoremap >> :call Voom_Oop('right', 'n') +vnoremap r :call Voom_Oop('right', 'v') +vnoremap :call Voom_Oop('right', 'v') +vnoremap >> :call Voom_Oop('right', 'v') + +" cut/copy/paste +nnoremap dd :call Voom_Oop('cut', 'n') +vnoremap dd :call Voom_Oop('cut', 'v') + +nnoremap yy :call Voom_Oop('copy', 'n') +vnoremap yy :call Voom_Oop('copy', 'v') + +nnoremap pp :call Voom_OopPaste() + +" mark/unmark +nnoremap m :call Voom_OopMark('mark', 'n') +vnoremap m :call Voom_OopMark('mark', 'v') + +nnoremap M :call Voom_OopMark('unmark', 'n') +vnoremap M :call Voom_OopMark('unmark', 'v') + +" mark node as selected node +nnoremap = :call Voom_OopMarkStartup() + +" select Body region +nnoremap R :call Voom_OopSelectBodyRange('n') +vnoremap R :call Voom_OopSelectBodyRange('v') + """ }}} + + """ save/Restore Tree folding {{{ +nnoremap fs :call Voom_OopFolding(line('.'),line('.'), 'save') +nnoremap fr :call Voom_OopFolding(line('.'),line('.'), 'restore') +nnoremap fas :call Voom_OopFolding(1,line('$'), 'save') +nnoremap far :call Voom_OopFolding(1,line('$'), 'restore') + """ }}} + + """ various commands {{{ + +" echo Tree headline +nnoremap s :echo getline('.')[(stridx(getline('.'),'')+1):] +" echo UNL +nnoremap S :call Voom_EchoUNL() +"nnoremap :call Voom_Help() +nnoremap e :call Voom_Exec('') +" delete outline +nnoremap q :call Voom_DeleteOutline() + + """ }}} + + let &cpo = cpo_ + return + " Use noremap to disable keys. This must be done first. + " Use nnoremap and vnoremap in VOoM mappings, don't use noremap. + " Some keys should be disabled via instead of : + " ../doc/voom.txt#id_20110121201243 + " + " Do not map . Not triggered on first click in the buffer. + " Triggered on first click in another buffer. Vim probably doesn't know + " what buffer it is until after the click. + " + " Can't use Ctrl: is Tab; , are page up/down. + " Use instead of Ctrl. + " + " Still up for grabs: [ ] { } +endfunc + + +func! Voom_TreeSessionLoad() "{{{2 +" Create outline when loading session created with :mksession. + if !exists('g:SessionLoad') || &modified || line('$')>1 || getline(1)!='' + return + endif + call setline(1,[' PLEASE',' KILL',' ME (:bw)']) + setl nomod noma bh=wipe + " don't -- horrible errors if two tabs with a Tree in each + "exe 'au SessionLoadPost bw '.bufnr('') + "au SessionLoadPost call Voom_TreeSessionLoadPost() + let [tree, tname] = [bufnr(''), bufname('')] + if has_key(s:voom_trees,tree) | return | endif + """ try to find Body matching this Tree buffer name + let treeName = fnamemodify(tname,':t') + if treeName !~# '^.\+_VOOM\d\+$' | return | endif + let bodyName = substitute(treeName, '\C_VOOM\d\+$', '', '') + let bodyNameM = substitute(bodyName, '[', '[[]', 'g') . '$' + let [body, bodyWnr] = [bufnr(bodyNameM), bufwinnr(bodyNameM)] + "echo 'DEBUG' treeName tree '|' bodyName body bodyWnr + " Body must exist and be in a window in the current tabpage + if body < 0 || bodyName !=# fnamemodify(bufname(body),':t') + return + elseif bodyWnr < 0 || bodyWnr == winnr() || bodyWnr != bufwinnr(body) + return + " there is already an outline for this Body + elseif has_key(s:voom_bodies, body) + exe 'b'.s:voom_bodies[body].tree + call Voom_TreeConfigWin() + return + endif + " rename Tree (current buffer), if needed, to correct Body bufnr + let tname_new = substitute(tname, '\C_VOOM\d\+$', '_VOOM'.body, '') + if tname !=# tname_new + if bufexists(tname_new) | return | endif + let bnrMax_ = bufnr('$') + exe 'silent file '.fnameescape(tname_new) + " An unlisted buffer is created to hold the old name. Kill it. + let bnrMax = bufnr('$') + if bnrMax > bnrMax_ && bnrMax==bufnr(tname.'$') + exe 'bwipeout '.bnrMax + endif + endif + """ go to Body, create outline, go back, configure Tree + let wnr_ = winnr() + let wnr_p = winnr('#') + try + exe 'noautocmd '.bodyWnr.'wincmd w' + let s:voom_bodies[body] = {} + let s:voom_bodies[body].blnr = line('.') + let b_dir = expand('%:p:h') + let l:firstLine = ' '.bodyName.' ['.b_dir.'], b'.body + let [l:mmode, l:qargs] = [-1, ''] + python voom.voom_Init(int(vim.eval('l:body'))) + if l:mmode < 0 | unlet s:voom_bodies[body] | return | endif + let s:voom_bodies[body].mmode = l:mmode + call Voom_BodyConfig() + finally + if wnr_p | exe 'noautocmd '.wnr_p.'wincmd w' | endif + exe 'noautocmd '.wnr_.'wincmd w' + endtry + if bufnr('')==tree + call Voom_TreeCreate(body) + endif +endfunc + + +"---Outline Navigation---{{{2 +" To select node from Tree, call Voom_TreeSelect(). ALWAYS return immediately +" after calling Voom_TreeSelect() in case Body checks fail. +" +" To position cursor on | in Tree (not needed if Voom_TreeSelect() is called): +" call cursor(0,stridx(getline('.'),'|')+1) +" or +" normal! 0f| + +" Notes: ../doc/voom.txt#id_20110116213809 + +" zt is affected by 'scrolloff' (Voom_TreeSelect) + + +func! Voom_TreeSelect(stayInTree) "{{{3 +" Select node corresponding to the current Tree line. +" Show correspoding Body's node. +" Leave cursor in Body if current line was in the selected node and !stayInTree. + let tree = bufnr('') + let body = s:voom_trees[tree] + if Voom_BufLoaded(body) < 0 | return | endif + let lnum = line('.') + + let snLn = s:voom_bodies[body].snLn + + let lz_ = &lz | set lz + call Voom_TreeZV() + call cursor(0,stridx(getline('.'),'|')+1) + + " compute l:blnum1, l:blnum2 -- start and end of the selected Body node + " set VO.snLn before going to Body in case outline update is forced + python voom.voom_TreeSelect() + + """ Mark new line with =. Remove old = mark. + if lnum != snLn + setl ma | let ul_ = &ul | setl ul=-1 + keepj call setline(lnum, '='.getline(lnum)[1:]) + keepj call setline(snLn, ' '.getline(snLn)[1:]) + setl noma | let &ul = ul_ + let s:voom_bodies[body].snLn = lnum + endif + + """ Go to Body, show selected node, come back or stay in Body. + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + let blnum = line('.') + let gotNewNode = (blnum < l:blnum1) || (blnum > l:blnum2) + if gotNewNode + exe 'keepj normal! '.l:blnum1.'G' + if &fdm ==# 'marker' + normal! zMzvzt + else + normal! zvzt + endif + endif + + """ Go back to Tree after showing new node in Body. + """ Stay in Body if Body's current line was in the selected node. + if gotNewNode || a:stayInTree + let wnr_ = winnr('#') + if winbufnr(wnr_)==tree + exe wnr_.'wincmd w' + else + exe bufwinnr(tree).'wincmd w' + endif + endif + + let &lz=lz_ +endfunc + + +func! Voom_TreeZV() "{{{3 +" Make current line visible. Return -1 if it was hidden. Like zv, but when +" current line starts a fold, do not open that fold. + let lnum = line('.') + let fc = foldclosed(lnum) + if fc < lnum && fc > 0 + normal! zo + let fc = foldclosed(lnum) + while fc < lnum && fc > 0 + normal! zo + let fc = foldclosed(lnum) + endwhile + return -1 + endif +endfunc + + +func! Voom_TreeToLine(lnum) "{{{3 +" Put cursor on line lnum, e.g., snLn. + if (line('w0') < a:lnum) && (a:lnum > 'w$') + let offscreen = 0 + else + let offscreen = 1 + endif + exe 'keepj normal! '.a:lnum.'G' + call Voom_TreeZV() + call cursor(0,stridx(getline('.'),'|')+1) + if offscreen==1 + normal! zz + endif +endfunc + + +func! Voom_TreeToggleFold() "{{{3 +" Toggle fold at cursor: expand/contract node. + let lnum=line('.') + let ln_status = Voom_FoldStatus(lnum) + + if ln_status=='folded' + normal! zo + elseif ln_status=='notfolded' + if stridx(getline(lnum),'|') < stridx(getline(lnum+1),'|') + normal! zc + endif + elseif ln_status=='hidden' + call Voom_TreeZV() + endif +endfunc + + +func! Voom_TreeMouseClick() "{{{3 +" Select node. Toggle fold if click is outside of headline text. + if !has_key(s:voom_trees, bufnr('')) + call Voom_ErrorMsg('VOoM: in wrong buffer') + return + endif + if virtcol('.')+1 >= virtcol('$') || col('.')-1 < stridx(getline('.'),'|') + call Voom_TreeToggleFold() + endif + call Voom_TreeSelect(1) +endfunc + + +func! Voom_TreeLeft() "{{{3 +" Go to parent node, but first contract current node if it's expanded. + if Voom_TreeZV() < 0 + call Voom_TreeSelect(1) + return + endif + let lnum = line('.') + if lnum==1 | return | endif + + let ind = stridx(getline(lnum),'|') + " next line has bigger indent and line is an opened fold -- close fold + if stridx(getline(lnum+1),'|') > ind && foldclosed(lnum) < 0 + normal! zc + " top level -- do not go anywhere + elseif ind < 3 + " go to parent + else + call search('\m^[^|]\{0,'.(ind-2).'}|', 'bWe') + endif + call Voom_TreeSelect(1) +endfunc + + +func! Voom_TreeRight() "{{{3 +" Go to first child of current node. + if Voom_TreeZV() < 0 + call Voom_TreeSelect(1) + return + endif + let lnum = line('.') + if lnum==1 | return | endif + + " line is first line of a closed fold + if foldclosed(lnum)==lnum + normal! zoj + " line is not in a closed fold and next line has bigger indent + elseif stridx(getline(lnum),'|') < stridx(getline(lnum+1),'|') + normal! j + endif + call Voom_TreeSelect(1) +endfunc + + +func! Voom_Tree_KJUD(action, mode) "{{{3 +" Move cursor to a sibling node as specified by action: U D K J. + if Voom_TreeZV() < 0 + call cursor(0,stridx(getline('.'),'|')+1) + return + endif + let lnum = line('.') + if lnum==1 | return | endif + + if a:mode==#'v' + let [ln1,ln2] = [line("'<"), line("'>")] + else + let [ln1,ln2] = [lnum, lnum] + endif + + " make sure we are on the first line of visual selection + " node level is indent of first | + let vcount1 = v:count1 + exe 'keepj normal! '.ln1.'G0f|' + let ind = virtcol('.')-1 + + " go to the uppermost sibling: up to parent, down to sibling + if a:action==#'U' + call search('\m^[^|]\{0,'.(ind-2).'}|', 'bWe') + if line('.') < lnum + call search('\m^[^|]\{'.(ind).'}|', 'We') + else + keepj normal! gg + call search('\m^[^|]\{'.(ind).'}|', 'We') + endif + + " go to the downmost sibling: down to next elder, up to sibling + elseif a:action==#'D' + call search('\m^[^|]\{0,'.(ind-2).'}|', 'We') + if line('.') > lnum + call search('\m^[^|]\{'.(ind).'}|', 'bWe') + else + keepj normal! G0f| + call search('\m^[^|]\{'.(ind).'}|', 'bcWe') + endif + + " go up to the previous sibling, stopline is parent + elseif a:action==#'K' + let stopline = search('\m^[^|]\{0,'.(ind-2).'}|', 'bWn') + for i in range(vcount1) + call search('\m^[^|]\{'.(ind).'}|', 'bWe', stopline) + endfor + + " go down to the next sibling, stopline is next elder node + elseif a:action==#'J' + " must first move to the last sibling in Visual selection + let sibln = lnum + while sibln > 0 + let sibln = search('\m^[^|]\{'.(ind).'}|', 'We', ln2) + endwhile + let stopline = search('\m^[^|]\{0,'.(ind-2).'}|', 'Wn') + for i in range(vcount1) + call search('\m^[^|]\{'.(ind).'}|', 'We', stopline) + endfor + + endif + + " restore and extend Visual selection + if a:mode==#'v' + let lnum = line(".") + if a:action==#'U' || a:action==#'K' + exe 'keepj normal! '.ln2.'GV'.lnum.'G0f|' + elseif a:action==#'D' || a:action==#'J' + exe 'keepj normal! '.ln1.'GV'.lnum.'G0f|' + endif + endif +endfunc + + +func! Voom_Tree_Pco(action, mode) "{{{3 +" action: P c o + if Voom_TreeZV() < 0 + call cursor(0,stridx(getline('.'),'|')+1) + return + endif + let lnum = line('.') + if lnum==1 | return | endif + + """ action 'P' or 'c': go up to parent, contract if 'c' + if a:action==#'c' || a:action==#'P' + keepj normal! 0f| + let ind = virtcol('.')-1 + call search('\m^[^|]\{0,'.(ind-2).'}|', 'bWe') + if a:action==#'c' && line('.') < lnum + normal! zc + endif + return + " action 'o': go to first child node, same as Voom_TreeRight() + elseif a:action==#'o' + " line is first line of a closed fold + if foldclosed(lnum)==lnum + normal! zoj0f| + let fc = foldclosed(lnum) + if fc < lnum && fc > 0 + normal! zo + endif + " line is not in a closed fold and next line has bigger indent + elseif stridx(getline(lnum),'|') < stridx(getline(lnum+1),'|') + normal! j0f| + let fc = foldclosed(lnum) + if fc < lnum && fc > 0 + normal! zo + endif + endif + endif +endfunc + + + +func! Voom_Tree_CO(action, mode) "{{{3 +" action: zC zO + if Voom_TreeZV() < 0 + call cursor(0,stridx(getline('.'),'|')+1) + return + endif + let lnum = line('.') + if lnum==1 | return | endif + + """ do 'zC' or 'zO' for all siblings of current node + if a:mode==#'n' + keepj normal! 0f| + let ind = virtcol('.')-1 + + let winsave_dict = winsaveview() + + " go the uppermost sibling: up to parent, down to sibling + call search('\m^[^|]\{0,'.(ind-2).'}|', 'bWe') + if line('.') < lnum + let lnUp = search('\m^[^|]\{'.(ind).'}|', 'We') + else + keepj normal! gg + let lnUp = search('\m^[^|]\{'.(ind).'}|', 'We') + endif + exe 'keepj normal! '.lnum.'G0f|' + + " go to the last subnode of the downmost sibling: down to elder node, up + call search('\m^[^|]\{0,'.(ind-2).'}|', 'We') + if line('.') > lnum + exe 'keepj normal! '.(line('.')-1).'G0f|' + else + keepj normal! G0f| + endif + + try + "exe 'keepj normal! V'.lnUp.'GzC' + exe 'keepj normal! V'.lnUp.'G'.a:action + catch /^Vim\%((\a\+)\)\=:E490/ + endtry + + call winrestview(winsave_dict) + exe 'keepj normal! '.lnum.'G0f|' + + """ do 'zC' or 'zO' for all nodes in Visual selection + elseif a:mode==#'v' + try + "normal! gvzC + exe 'normal! gv'.a:action + catch /^Vim\%((\a\+)\)\=:E490/ + endtry + endif + + call Voom_TreeZV() +endfunc + + + +func! Voom_TreeToSelected() "{{{3 +" Put cursor on selected node, that is on SnLn line. + let lnum = s:voom_bodies[s:voom_trees[bufnr('')]].snLn + call Voom_TreeToLine(lnum) +endfunc + + +func! Voom_TreeToStartupNode() "{{{3 +" Put cursor on startup node, if any: node marked with '=' in Body headline. +" Warn if there are several such nodes. + let body = s:voom_trees[bufnr('')] + if s:voom_bodies[body].mmode + call Voom_ErrorMsg('VOoM: startup nodes are not available in this markup mode') + return + endif + " this creates l:lnums + python voom.voom_TreeToStartupNode() + if len(l:lnums)==0 + call Voom_WarningMsg("VOoM: no nodes marked with '='") + return + endif + call Voom_TreeToLine(l:lnums[-1]) + if len(l:lnums)>1 + call Voom_WarningMsg("VOoM: multiple nodes marked with '=': ".join(l:lnums, ', ')) + endif +endfunc + + +func! Voom_TreeToMark(back) "{{{3 +" Go to next or previous marked node. + if a:back==1 + normal! 0 + let found = search('\C\v^.x', 'bw') + else + let found = search('\C\v^.x', 'w') + endif + + if found==0 + call Voom_WarningMsg("VOoM: there are no marked nodes") + else + call Voom_TreeSelect(1) + endif +endfunc + + +"---Outline Operations---{{{2 + +func! Voom_OopSelectBodyRange(mode) "{{{3 +" Move to Body and select region corresponding to node(s) in the Tree. + let tree = bufnr('') + let body = s:voom_trees[tree] + if Voom_BufLoaded(body) < 0 | return | endif + if Voom_BufEditable(body) < 0 | return | endif + let ln = line('.') + let ln_status = Voom_FoldStatus(ln) + " current line must not be hidden in a fold + if ln_status=='hidden' + call Voom_ErrorMsg("VOoM: current line is hidden in fold") + return + endif + " normal mode: use current line + if a:mode=='n' + let [ln1, ln2] = [ln, ln] + " visual mode: use range + elseif a:mode=='v' + let [ln1, ln2] = [line("'<"), line("'>")] + endif + + if Voom_ToBody(body) < 0 | return | endif + if Voom_BodyCheckTicks(body) < 0 | return | endif + " compute bln1 and bln2 + python voom.voom_OopSelectBodyRange() + " this happens when ln2==1 and the first headline is top of buffer + if l:bln2==0 | return | endif + exe 'normal! '.bln1.'Gzv'.bln2.'GzvV'.bln1.'G' + if line('w$') < bln2 + normal! zt + endif +endfunc + + +func! Voom_OopEdit() "{{{3 +" Edit headline text: move into Body, put cursor on headline. + let tree = bufnr('') + let body = s:voom_trees[tree] + if Voom_BufLoaded(body) < 0 | return | endif + if Voom_BufEditable(body) < 0 | return | endif + let lnum = line('.') + if lnum==1 | return | endif + + python vim.command("let l:bLnr=%s" %voom.VOOMS[int(vim.eval('l:body'))].bnodes[int(vim.eval('l:lnum'))-1]) + + let lz_ = &lz | set lz + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + + " do zz only when target line is not in the window + if l:bLnr < line('w0') || l:bLnr > line('w$') + let do_zz = 1 + else + let do_zz = 0 + endif + exe 'keepj normal! '.l:bLnr.'Gzv^' + if do_zz + normal! zz + endif + " put cursor on the first word char + call search('\m\<', 'c', line('.')) + let &lz=lz_ +endfunc + + +func! Voom_OopInsert(as_child) "{{{3 +" Insert new node, headline text should be NewHeadline. + let tree = bufnr('') + let body = s:voom_trees[tree] + if Voom_BufLoaded(body) < 0 | return | endif + if Voom_BufEditable(body) < 0 | return | endif + let ln = line('.') + let ln_status = Voom_FoldStatus(ln) + if ln_status=='hidden' + call Voom_ErrorMsg("VOoM: current line is hidden in fold") + return + endif + + let lz_ = &lz | set lz + if v:version > 703 || v:version==703 && has('patch105') + if s:voom_bodies[body].tick_ != getbufvar(body,'changedtick') + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + call Voom_OopFromBody(body,tree,-1,1) + endif + else + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + call Voom_OopFromBody(body,tree,-1,1) + endif + + setl ma + if a:as_child=='as_child' + keepj python voom.voom_OopInsert(as_child=True) + else + keepj python voom.voom_OopInsert(as_child=False) + endif + setl noma + + let snLn = s:voom_bodies[body].snLn + exe "keepj normal! ".snLn."G0f|" + call Voom_TreeZV() + + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + exe "keepj normal! ".l:bLnum."Gzvz\" + call search('\CNewHeadline', 'c', line('.')) + let &lz=lz_ +endfunc + + +func! Voom_OopPaste() "{{{3 +" Paste nodes in the clipboard. + let tree = bufnr('') + let body = s:voom_trees[tree] + if Voom_BufLoaded(body) < 0 | return | endif + if Voom_BufEditable(body) < 0 | return | endif + let ln = line('.') + let ln_status = Voom_FoldStatus(ln) + if ln_status=='hidden' + call Voom_ErrorMsg("VOoM: current line is hidden in fold") + return + endif + + let lz_ = &lz | set lz + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + + " default bnlShow -1 means pasting not possible + let l:blnShow = -1 + + call setbufvar(tree, '&ma', 1) + keepj python voom.voom_OopPaste() + call setbufvar(tree, '&ma', 0) + + " no pasting was done or Python code failed + if l:blnShow < 0 | let &lz=lz_ | return | endif + + let s:voom_bodies[body].snLn = l:ln1 + if l:ln1==l:ln2 + call Voom_OopShowTree(l:ln1, l:ln2, 'n') + else + call Voom_OopShowTree(l:ln1, l:ln2, 'v') + endif + let &lz=lz_ + + call Voom_OopVerify(body, tree, 'paste') +endfunc + + +func! Voom_OopMark(op, mode) "{{{3 +" Mark or unmark current node or all nodes in selection + + " Checks and init vars. {{{ + let tree = bufnr('') + let body = s:voom_trees[tree] + if s:voom_bodies[body].mmode + call Voom_ErrorMsg('VOoM: marked nodes are not available in this markup mode') + return + endif + if Voom_BufLoaded(body) < 0 | return | endif + if Voom_BufEditable(body) < 0 | return | endif + let ln = line('.') + let ln_status = Voom_FoldStatus(ln) + " current line must not be hidden in a fold + if ln_status=='hidden' + call Voom_ErrorMsg("VOoM: current line is hidden in fold") + return + endif + " normal mode: use current line + if a:mode=='n' + let ln1 = ln + let ln2 = ln + " visual mode: use range + elseif a:mode=='v' + let ln1 = line("'<") + let ln2 = line("'>") + endif + " don't touch first line + if ln1==1 && ln2==ln1 + return + elseif ln1==1 && ln2>1 + let ln1=2 + endif + " }}} + + let lz_ = &lz | set lz + let fdm_t = &fdm + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + + let fdm_b=&fdm | setl fdm=manual + call setbufvar(tree, '&fdm', 'manual') + call setbufvar(tree, '&ma', 1) + if a:op=='mark' + keepj python voom.voom_OopMark() + elseif a:op=='unmark' + keepj python voom.voom_OopUnmark() + endif + call setbufvar(tree, '&ma', 0) + let &fdm=fdm_b + + call Voom_OopFromBody(body,tree,0,1) + let &fdm=fdm_t + let &lz=lz_ + + call Voom_OopVerify(body, tree, a:op) +endfunc + + +func! Voom_OopMarkStartup() "{{{3 +" Mark current node as startup node. + let tree = bufnr('') + let body = s:voom_trees[tree] + if s:voom_bodies[body].mmode + call Voom_ErrorMsg('VOoM: startup nodes are not available in this markup mode') + return + endif + if Voom_BufLoaded(body) < 0 | return | endif + if Voom_BufEditable(body) < 0 | return | endif + let ln = line('.') + let ln_status = Voom_FoldStatus(ln) + " current line must not be hidden in a fold + if ln_status=='hidden' + call Voom_ErrorMsg("VOoM: current line is hidden in fold") + return + endif + + let lz_ = &lz | set lz + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + + call setbufvar(tree, '&ma', 1) + keepj python voom.voom_OopMarkStartup() + call setbufvar(tree, '&ma', 0) + + call Voom_OopFromBody(body,tree,0,1) + let &lz=lz_ + + call Voom_OopVerify(body, tree, 'markStartup') +endfunc + + +func! Voom_Oop(op, mode) "{{{3 +" Outline operations that can be perfomed on current node or on nodes in Visual +" selection. All apply to branches, not to single nodes. + + " Checks and init vars. {{{ + let tree = bufnr('') + let body = s:voom_trees[tree] + if Voom_BufLoaded(body) < 0 | return | endif + if a:op!='copy' && Voom_BufEditable(body) < 0 | return | endif + let ln = line('.') + let ln_status = Voom_FoldStatus(ln) + if ln_status=='hidden' + call Voom_ErrorMsg("VOoM: node is hidden in fold") + return + endif + " normal mode: use current line + if a:mode=='n' + let [ln1,ln2] = [ln,ln] + " visual mode: use range + elseif a:mode=='v' + let [ln1,ln2] = [line("'<"),line("'>")] + " before op: move cursor to ln1 or ln2 + endif + if ln1==1 + call Voom_ErrorMsg("VOoM (".a:op."): first Tree line cannot be operated on") + return + endif + " set ln2 to last node in the last sibling branch in selection + " check validity of selection + python vim.command('let ln2=%s' %voom.voom_OopSelEnd()) + if ln2==0 + call Voom_ErrorMsg("VOoM: invalid Tree selection") + return + endif + " }}} + + " default bnlShow -1 means no changes were made + let l:blnShow = -1 + let lz_ = &lz | set lz + + if a:op=='up' " {{{ + if ln1<3 | let &lz=lz_ | return | endif + if a:mode=='v' + " must be on first line of selection + exe "keepj normal! ".ln1."G" + endif + " ln before which to insert, also, new snLn + normal! k + let lnUp1 = line('.') + " top node of a tree after which to insert + normal! k + let lnUp2 = line('.') + + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + + call setbufvar(tree, '&ma', 1) + keepj python voom.voom_OopUp() + call setbufvar(tree, '&ma', 0) + " Python code failed + if l:blnShow < 0 | let &lz=lz_ | return | endif + + let s:voom_bodies[body].snLn = lnUp1 + let lnEnd = lnUp1+ln2-ln1 + call Voom_OopShowTree(lnUp1, lnEnd, a:mode) + " }}} + + elseif a:op=='down' " {{{ + if ln2==line('$') | let &lz=lz_ | return | endif + " must be on the last node of current tree or last tree in selection + exe "keepj normal! ".ln2."G" + " line after which to insert + normal! j + let lnDn1 = line('.') " should be ln2+1 + let lnDn1_status = Voom_FoldStatus(lnDn1) + + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + + call setbufvar(tree, '&ma', 1) + keepj python voom.voom_OopDown() + call setbufvar(tree, '&ma', 0) + " Python code failed + if l:blnShow < 0 | let &lz=lz_ | return | endif + + let s:voom_bodies[body].snLn = l:snLn + let lnEnd = snLn+ln2-ln1 + call Voom_OopShowTree(snLn, lnEnd, a:mode) + " }}} + + elseif a:op=='right' " {{{ + if ln1==2 | let &lz=lz_ | return | endif + + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + + let fdm_b=&fdm | setl fdm=manual + call setbufvar(tree, '&ma', 1) + keepj python voom.voom_OopRight() + call setbufvar(tree, '&ma', 0) + + " can't move right or Python code failed + if l:blnShow < 0 + call setbufvar(body, '&fdm', fdm_b) + let &lz=lz_ + return + endif + + let s:voom_bodies[body].snLn = ln1 + call Voom_OopShowTree(ln1, ln2, a:mode) + " }}} + + elseif a:op=='left' " {{{ + if ln1==2 | let &lz=lz_ | return | endif + + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + + let fdm_b=&fdm | setl fdm=manual + call setbufvar(tree, '&ma', 1) + keepj python voom.voom_OopLeft() + call setbufvar(tree, '&ma', 0) + + " can't move right or Python code failed + if l:blnShow < 0 + call setbufvar(body, '&fdm', fdm_b) + let &lz=lz_ + return + endif + + let s:voom_bodies[body].snLn = ln1 + call Voom_OopShowTree(ln1, ln2, a:mode) + " }}} + + elseif a:op=='copy' " {{{ + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + + keepj python voom.voom_OopCopy() + + call Voom_OopFromBody(body,tree,-1,1) + "}}} + + elseif a:op=='cut' " {{{ + if a:mode=='v' + " must be on first line of selection + exe "keepj normal! ".ln1."G" + endif + " new snLn + normal! k + let lnUp1 = line('.') + + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + + call setbufvar(tree, '&ma', 1) + keepj python voom.voom_OopCut() + call setbufvar(tree, '&ma', 0) + + let s:voom_bodies[body].snLn = lnUp1 + call cursor(0,stridx(getline('.'),'|')+1) + " }}} + endif + + let &lz=lz_ + + call Voom_OopVerify(body, tree, a:op) +endfunc + + +func! Voom_OopFolding(ln1, ln2, action) "{{{3 +" Deal with Tree folding in range ln1-ln2 according to action: +" save, restore, cleanup. Range is ignored if 'cleanup'. +" Since potentially large lists are involved, folds are manipulated in Python. + + " must be in Tree buffer + let tree = bufnr('') + if !has_key(s:voom_trees, tree) + call Voom_ErrorMsg("VOoM: this command must be executed in Tree buffer") + return + endif + let body = s:voom_trees[tree] + if s:voom_bodies[body].mmode + call Voom_ErrorMsg('VOoM: Tree folding operations are not available in this markup mode') + return + endif + if Voom_BufLoaded(body) < 0 | return | endif + if a:action!=#'restore' && Voom_BufEditable(body) < 0 + return + endif + + " can't deal with folds of node hidden in a fold + if a:action!=#'cleanup' && Voom_FoldStatus(a:ln1)=='hidden' + call Voom_ErrorMsg("VOoM: node is hidden in fold") + return + endif + + let lz_ = &lz | set lz + + " go to Body, check ticks, go back + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + call Voom_OopFromBody(body,tree,-1,1) + " make sure we are back + if bufnr('')!=tree + echoerr "VOoM: internal error" | let &lz=lz_ | return + endif + + """ diddle with folds + let winsave_dict = winsaveview() + python voom.voom_OopFolding(vim.eval('a:action')) + call winrestview(winsave_dict) + + if a:action!=#'restore' + " go to Body, set ticks, go back + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + call Voom_OopFromBody(body,tree,0,1) + endif + + let &lz=lz_ +endfunc + +func! Voom_OopSort(ln1,ln2,qargs) "{{{3 +" Sort siblings in Tree range ln1:ln2 according to options qargs. +" Sort siblings of the current node if range is one line (ln1==ln2). +" If one of the options is 'deep' -- also sort siblings in all subnodes. +" Options are dealt with in the Python code. + + " must be in Tree buffer + let tree = bufnr('') + if !has_key(s:voom_trees, tree) + call Voom_ErrorMsg("VOoM (sort): this command must be executed in Tree buffer") + return + endif + let body = s:voom_trees[tree] + if Voom_BufLoaded(body) < 0 | return | endif + if Voom_BufEditable(body) < 0 | return | endif + if a:ln1 < 2 || a:ln2 < 2 + call Voom_ErrorMsg("VOoM (sort): first Tree line cannot be operated on") + return + endif + if Voom_FoldStatus(a:ln1)=='hidden' + call Voom_ErrorMsg("VOoM (sort): line is hidden in fold") + return + endif + + let Z = line('$') + + let lz_ = &lz | set lz + """ go to Body window + if Voom_ToBody(body) < 0 | let &lz=lz_ | return | endif + if Voom_BodyCheckTicks(body) < 0 | let &lz=lz_ | return | endif + + " default l:bnlShow -1 means no changes were made + let l:blnShow = -1 + " Modify Body buffer. Tree buffer and outline data are not adjusted. + keepj python voom.voom_OopSort() + " IMPORTANT: we rely on Tree BufEnter au to update outline + call Voom_OopFromBody(body,tree,l:blnShow,0) + if l:blnShow > 0 + call Voom_OopShowTree(l:lnum1, l:lnum2, a:ln1==a:ln2 ? 'n' : 'v') + endif + + let &lz=lz_ + + " Sorting must not change the number of headlines! + " (This is problem with reST and Python modes.) + if Z != line('$') + call Voom_ErrorMsg("VOoM (sort): ERROR has occurred during sorting!!!", "The number of headlines has changed!!!", "You must undo this sort!!!") + endif +endfunc + + +func! Voom_OopFromBody(body, tree, blnShow, setTick) "{{{3 +" Move from Body to Tree after an outline operation. +" Set ticks if a:setTick to suppress Tree update on BufEnter. +" Show node (or just line) at Body lnum blnShow. +" Go back to Tree. +" Special blnShow values: +" -1 --don't set ticks and don't show node. +" 0 --set ticks, but don't show node. + + if bufnr('')!=a:body + echoerr 'VOoM: internal error' + return + endif + + let body_tick = b:changedtick + + if a:blnShow >= 0 && a:setTick + " set ticks to suppress Tree update + let s:voom_bodies[a:body].tick_ = b:changedtick + let s:voom_bodies[a:body].tick = b:changedtick + endif + + if a:blnShow > 0 + " show fold at blnShow + exe 'keepj normal! '.a:blnShow.'G' + if &fdm==#'marker' + normal! zMzvzt + else + normal! zvzt + endif + endif + + " go back to Tree window, which should be previous window + let wnr_ = winnr('#') + if winbufnr(wnr_)==a:tree + exe wnr_.'wincmd w' + else + exe bufwinnr(a:tree).'wincmd w' + endif + if bufnr('')!=a:tree + throw 'This is not Tree!' + endif + if s:voom_bodies[a:body].tick_ != body_tick + echoerr 'VOoM: wrong ticks! Forcing outline update.' + let s:voom_bodies[a:body].tick = body_tick + call Voom_TreeBufEnter() + endif +endfunc + + +func! Voom_OopShowTree(ln1, ln2, mode) " {{{3 +" Adjust Tree view after an outline operation. +" ln1 and ln2 are first and last line of the range. +" +" After outline operation Tree folds in the affected range are usually +" completely expanded. To be consistent: close all folds in the range +" (select range, zC, show first line). + + " zv ensures ln1 node is expanded before next GV + exe 'keepj normal! '.a:ln1.'Gzv' + " select range and close all folds in range + exe 'keepj normal! '.a:ln2.'GV'.a:ln1.'G' + try + normal! zC + " E490: No fold found + catch /^Vim\%((\a\+)\)\=:E490/ + endtry + + " show first node + call Voom_TreeZV() + call cursor(0,stridx(getline('.'),'|')+1) + + " restore visual mode selection + if a:mode=='v' + normal! gv + endif +endfunc + + +func! Voom_OopVerify(body, tree, op) "{{{3 +" Verify outline after outline operation. Current buffer is Tree. + if !g:voom_verify_oop || a:op=='copy' + return + endif + + python voom.voom_OopVerify() + if exists('l:ok') + return + endif + + echoerr 'VOoM: outline verification failed after "'.a:op.'". Forcing outline update.' + let s:voom_bodies[a:body].tick_ = -1 + if bufnr('')!=a:tree + echoerr 'Current buffer is not Tree! Aborting outline update.' + return + endif + call Voom_TreeBufEnter() +endfunc + + +"---BODY BUFFERS------------------------------{{{1 + +func! Voom_BodyConfig() "{{{2 +" Configure current buffer as a Body buffer. + augroup VoomBody + au! * + au BufLeave call Voom_BodyBufLeave() + au BufEnter call Voom_BodyBufEnter() + augroup END + + " redundant: will be set on BufLeave + let s:voom_bodies[bufnr('')].tick = b:changedtick + + call Voom_BodyMap() +endfunc + + +func! Voom_BodyBufLeave() "{{{2 +" Body BufLeave au. +" getbufvar() doesn't work with b:changedtick, thus the need for this au + let s:voom_bodies[bufnr('')].tick = b:changedtick +endfunc + + +func! Voom_BodyBufEnter() "{{{2 +" Body BufEnter au. Restore buffer-local mappings lost after :bd. + if !hasmapto('Voom_ToTreeOrBodyWin','n') + call Voom_BodyMap() + endif +endfunc + + +func! Voom_BodyMap() "{{{2 +" Body buffer local mappings. + let cpo_ = &cpo | set cpo&vim + exe "nnoremap ".g:voom_return_key." :call Voom_BodySelect()" + exe "nnoremap ".g:voom_tab_key. " :call Voom_ToTreeOrBodyWin()" + let &cpo = cpo_ +endfunc + + +func! Voom_BodyUnMap() "{{{2 +" Remove Body local mappings. Must be called from Body. + let cpo_ = &cpo | set cpo&vim + exe "nunmap ".g:voom_return_key + exe "nunmap ".g:voom_tab_key + let &cpo = cpo_ +endfunc + + +func! Voom_BodySelect() "{{{2 +" Select current Body node. Show corresponding line in the Tree. +" Stay in the Tree if the node is already selected. + let body = bufnr('') + " Tree has been wiped out. + if !has_key(s:voom_bodies, body) + call Voom_BodyUnMap() + return + endif + + let wnr_ = winnr() + let tree = s:voom_bodies[body].tree + let blnr = line('.') + let s:voom_bodies[body].blnr = blnr + + let bchangedtick = b:changedtick + " Go to Tree. Outline will be updated on BufEnter. + if Voom_ToTree(tree) < 0 | return | endif + " Check for ticks. + if s:voom_bodies[body].tick_!=bchangedtick + exe bufwinnr(body).'wincmd w' + call Voom_BodyCheckTicks(body) + return + endif + + " updateTree() sets = mark and may change snLn to a wrong value if outline was modified from Body. + let snLn_ = s:voom_bodies[body].snLn + " Compute new and correct snLn with updated outline. + python voom.computeSnLn(int(vim.eval('l:body')), int(vim.eval('l:blnr'))) + let snLn = s:voom_bodies[body].snLn + + call Voom_TreeToLine(snLn) + " Node has not changed. Stay in Tree. + if snLn==snLn_ + return + endif + + " Node has changed. Draw marks. Go back to Body + setl ma | let ul_ = &ul | setl ul=-1 + keepj call setline(snLn_, ' '.getline(snLn_)[1:]) + keepj call setline(snLn, '='.getline(snLn)[1:]) + setl noma | let &ul = ul_ + + let wnr_ = winnr('#') + if winbufnr(wnr_)==body + exe wnr_.'wincmd w' + else + exe bufwinnr(body).'wincmd w' + endif +endfunc + + +func! Voom_BodyCheckTicks(body) "{{{2 +" Current buffer is Body body. Check ticks assuming that outline is up to date, +" as after going to Body from Tree. +" note: 'abort' argument is not needed and would be counterproductive + if bufnr('')!=a:body + echoerr 'VOoM: wrong buffer' + return -1 + endif + " Wrong ticks, probably after :bun or :bd. Force outline update. + if s:voom_bodies[a:body].tick_!=b:changedtick + let tree = s:voom_bodies[a:body].tree + if !exists("s:voom_trees") || !has_key(s:voom_trees, tree) + echoerr "VOoM: internal error" + return -1 + endif + call Voom_BodyUpdateTree() + call Voom_ErrorMsg('VOoM: wrong ticks for Body buffer '.a:body.'. Updated outline.') + return -1 + endif +endfunc + + +func! Voom_BodyUpdateTree() "{{{2 +" Current buffer is Body. Update outline and Tree. + let body = bufnr('') + if !has_key(s:voom_bodies, body) + call Voom_ErrorMsg('VOoM: current buffer is not Body') + return -1 + endif + + let tree = s:voom_bodies[body].tree + + " paranoia + if !bufloaded(tree) + call Voom_UnVoom(body,tree) + echoerr "VOoM: Tree buffer" tree "is not loaded or does not exist. Cleanup has been performed." + return -1 + endif + + """" update is not needed + if s:voom_bodies[body].tick_==b:changedtick + return + endif + + """" do update + call setbufvar(tree, '&ma', 1) + let ul_=&ul | setl ul=-1 + try + let l:ok = 0 + keepj python voom.updateTree(int(vim.eval('l:body')), int(vim.eval('l:tree'))) + if l:ok + let s:voom_bodies[body].tick_ = b:changedtick + let s:voom_bodies[body].tick = b:changedtick + endif + finally + " Why: &ul is global, but this causes 'undo list corrupt' error + "let &ul=ul_ + call setbufvar(tree, '&ul', ul_) + call setbufvar(tree, '&ma', 0) + endtry +endfunc + + +"---Tree or Body------------------------------{{{1 + +func! Voom_EchoUNL() "{{{2 +" Display UNL (Uniformed Node Locator) of current node. +" Copy UNL to register 'n'. +" Can be called from any buffer. + let bnr = bufnr('') + let lnum = line('.') + if has_key(s:voom_trees, bnr) + let [bufType, body, tree] = ['Tree', s:voom_trees[bnr], bnr] + if Voom_BufLoaded(body) < 0 | return | endif + elseif has_key(s:voom_bodies, bnr) + let [bufType, body, tree] = ['Body', bnr, s:voom_bodies[bnr].tree] + if Voom_BodyUpdateTree() < 0 | return | endif + else + call Voom_ErrorMsg("VOoM: current buffer is not a VOoM buffer") + return + endif + python voom.voom_EchoUNL() +endfunc + + +func! Voom_Grep(input) "{{{2 +" Seach Body for pattern(s). Show list of UNLs of nodes with matches. +" Input can have several patterns separated by boolean 'AND' and 'NOT'. +" Stop each search after 10,000 matches. +" Set search register to the first AND pattern. + + """ Process input first in case we are in Tree and want word under cursor. + if a:input=='' + let input = expand('') + let input = substitute(input, '\s\+$', '', '') + if input=='' | return | endif + let [pattsAND, pattsNOT] = [['\<'.input.'\>'], []] + else + let input = substitute(a:input, '\s\+$', '', '') + if input=='' | return | endif + let [pattsAND, pattsNOT] = Voom_GrepParseInput(input) + endif + + """ Search must be done in Body buffer. Move to Body if in Tree. + let bnr = bufnr('') + if has_key(s:voom_trees, bnr) + let body = s:voom_trees[bnr] + let tree = bnr + if Voom_BufLoaded(body) < 0 | return | endif + if Voom_ToBody(body) < 0 | return | endif + if Voom_BodyCheckTicks(body) < 0 | return | endif + elseif has_key(s:voom_bodies, bnr) + let body = bnr + let tree = s:voom_bodies[bnr].tree + " update outline + if Voom_BodyUpdateTree() < 0 | return | endif + else + call Voom_ErrorMsg("VOoM: current buffer is not a VOoM buffer") + return + endif + + """ Search for each pattern with search(). + let lz_ = &lz | set lz + let winsave_dict = winsaveview() + let [matchesAND, matchesNOT] = [[], []] + for patt in pattsAND + let matches = Voom_GrepSearch(patt) + if matches==[0] + call Voom_WarningMsg('VOoM (Voomgrep): pattern not found: '.patt) + call winrestview(winsave_dict) + call winline() + let &lz=lz_ + return + endif + call add(matchesAND, matches) + endfor + for patt in pattsNOT + call add(matchesNOT, Voom_GrepSearch(patt)) + endfor + call winrestview(winsave_dict) + call winline() + let &lz=lz_ + + """ Highlight first AND pattern. + " Problem: there is no search highlight after :noh + " Consider: use matchadd() if several AND patterns + if len(pattsAND)>0 + let @/ = pattsAND[0] + endif + + """ Set and display quickfix list. + " first line shows patterns and number of matches + let line1 = '' + for i in range(len(pattsAND)) + let L = matchesAND[i] + let line1 = i==0 ? line1.pattsAND[i].' {' : line1.'AND '.pattsAND[i].' {' + let line1 = L[-1]==0 ? line1. (len(L)-1) .' matches} ' : line1.'>10,000 matches} ' + endfor + for i in range(len(pattsNOT)) + let L = matchesNOT[i] + let line1 = line1.'NOT '.pattsNOT[i].' {' + let line1 = L[-1]==0 ? line1. (len(L)-1) .' matches} ' : line1.'>10,000 matches} ' + endfor + let line1 = 'Voomgrep '. substitute(line1,"'","''",'g') + exe "call setqflist([{'text':'".line1."'}])" + + python voom.voom_Grep() + botright copen +endfunc + + +func! Voom_GrepParseInput(input) "{{{2 +" Input string is patterns separated by AND or NOT. +" There can be a leading NOT, but not leading AND. +" Segregate patterns into AND and NOT lists. + let [pattsAND, pattsNOT] = [[], []] + " split at AND + let andParts = split(a:input, '\v\c\s+and\s+') + let i = 1 + for part in andParts + " split at NOT + let notParts = split(part, '\v\c\s+not\s+') + " check for leading NOT + if i==1 + let i+=1 + let parts1 = split(notParts[0], '\v\c^\s*not\s+', 1) + if len(parts1)>1 + call add(pattsNOT, parts1[1]) + else + call add(pattsAND, notParts[0]) + endif + else + call add(pattsAND, notParts[0]) + endif + if len(notParts)>1 + let pattsNOT+=notParts[1:] + endif + endfor + return [pattsAND, pattsNOT] +endfunc + + +func! Voom_GrepSearch(pattern) "{{{2 +" Seach buffer for pattern. Return [lnums of matching lines]. +" Stop search after first 10000 matches. + let matches = [] + " always search from start + keepj normal! gg0 + " special effort needed to detect match at cursor + if searchpos(a:pattern, 'nc')==[1,1] + call add(matches,1) + endif + " do search + let found = 1 + while found>0 && len(matches)<10000 + let found = search(a:pattern, 'W') + call add(matches, found) + endwhile + " search was terminated after 10000 matches were found + if matches[-1]!=0 + call add(matches,-1) + endif + return matches +endfunc + + +"---LOG BUFFER (Voomlog)----------------------{{{1 +" +" Do "normal! G" to position cursor and scroll Log window. +" "call cursor('$',1)" does not scroll Log window. + + +func! Voom_LogInit() "{{{2 +" Create and configure PyLog buffer or show existing one. + let bnr_ = bufnr('') + """ Log buffer exists, show it. + if s:voom_logbnr + if !bufloaded(s:voom_logbnr) + python sys.stdout, sys.stderr = _voom_py_sys_stdout, _voom_py_sys_stderr + python if 'pydoc' in sys.modules: del sys.modules['pydoc'] + if bufexists(s:voom_logbnr) + exe 'au! VoomLog * ' + exe 'bwipeout '.s:voom_logbnr + endif + let bnr = s:voom_logbnr + let s:voom_logbnr = 0 + echoerr "VOoM: PyLog buffer" bnr "was not shut down properly. Cleanup has been performed. Execute the command :Voomlog again." + return + endif + if bufwinnr(s:voom_logbnr) < 0 + call Voom_ToLogWin() + silent exe 'b '.s:voom_logbnr + keepj normal! G + exe bufwinnr(bnr_).'wincmd w' + endif + return + endif + + """ Create and configure PyLog buffer. + if bufexists('__PyLog__') > 0 + call Voom_ErrorMsg('VOoM: there is already a buffer named __PyLog__') + return + endif + call Voom_ToLogWin() + silent edit __PyLog__ + call Voom_LogConfig() + """ Go back. + exe bufwinnr(bnr_).'wincmd w' +endfunc + + +func! Voom_LogConfig() "{{{2 +" Configure current buffer as PyLog. Redirect Python stdout and stderr to it. +" NOTE: the caller must check if PyLog already exists. + let s:voom_logbnr = bufnr('') + augroup VoomLog + au! * + au BufUnload nested call Voom_LogBufUnload() + augroup END + setl cul nocuc list wrap + setl bufhidden=wipe + setl ft=voomlog + setl noro ma ff=unix + setl nobuflisted buftype=nofile noswapfile + call Voom_LogSyntax() +python << EOF +_voom_py_sys_stdout, _voom_py_sys_stderr = sys.stdout, sys.stderr +sys.stdout = sys.stderr = voom.LogBufferClass() +if 'pydoc' in sys.modules: del sys.modules['pydoc'] +EOF +endfunc + + +func! Voom_LogBufUnload() "{{{2 + if !s:voom_logbnr || expand("")!=s:voom_logbnr + echoerr 'VOoM: internal error' + return + endif + python sys.stdout, sys.stderr = _voom_py_sys_stdout, _voom_py_sys_stderr + python if 'pydoc' in sys.modules: del sys.modules['pydoc'] + exe 'au! VoomLog * ' + exe 'bwipeout '.s:voom_logbnr + let s:voom_logbnr = 0 +endfunc + + +func! Voom_LogSyntax() "{{{2 +" Log buffer syntax highlighting. + + " Python tracebacks + syn match Error /^Traceback (most recent call last):/ + syn match Error /^\u\h*Error/ + syn match Error /^vim\.error/ + syn region WarningMsg start="^Traceback (most recent call last):" end="\%(^\u\h*Error.*\)\|\%(^\s*$\)\|\%(^vim\.error\)" contains=Error keepend + + "Vim exceptions + syn match Error /^Vim.*:E\d\+:.*/ + + " VOoM messages + syn match Error /^ERROR: .*/ + syn match Error /^EXCEPTION: .*/ + syn match PreProc /^---end of \w\+ script.*---$/ + + " -> UNL separator + syn match Title / -> / + +endfunc + + +func! Voom_LogScroll() "{{{2 +" Scroll windows with the __PyLog__ buffer. +" All tabs are searched. Only the first found Log window in each tab is scrolled. +" Uses noautocmd when jumping between tabs and windows. +" Note: don't use Python here: an error will result in recursive loop. + + " can't go to other windows when in Ex mode (after 'Q' or 'gQ') + if mode()=='c' | return | endif + " This should never happen. + if !s:voom_logbnr || !bufloaded(s:voom_logbnr) + echoerr "VOoM: internal error" + return + endif + + let lz_=&lz | set lz + let log_found = 0 + let [tnr_, wnr_, bnr_] = [tabpagenr(), winnr(), bufnr('')] + " search among visible buffers in all tabs + for tnr in range(1, tabpagenr('$')) + if index(tabpagebuflist(tnr), s:voom_logbnr) > -1 + let log_found = 1 + if tabpagenr() != tnr + exe 'noautocmd tabnext '.tnr + endif + let [wnr__, wnr__p] = [winnr(), winnr('#')] + exe 'noautocmd '. bufwinnr(s:voom_logbnr).'wincmd w' + keepj normal! G + " restore tab's current and previous window numbers + if wnr__p + exe 'noautocmd '.wnr__p.'wincmd w' + endif + exe 'noautocmd '.wnr__.'wincmd w' + endif + endfor + " At least one Log window was found and scrolled. Return to original tab and window. + if log_found==1 + if tabpagenr() != tnr_ + exe 'noautocmd tabnext '.tnr_ + exe 'noautocmd '.wnr_.'wincmd w' + endif + " Log window was not found. Create it. + else + call Voom_ToLogWin() + exe 'b '.s:voom_logbnr + keepj normal! G + exe 'tabnext '.tnr_ + exe bufwinnr(bnr_).'wincmd w' + endif + let &lz=lz_ +endfunc + + +func! Voom_LogSessionLoad() "{{{2 +" Activate PyLog when loading Vim session created with :mksession. + if !exists('g:SessionLoad') || &modified || line('$')>1 || getline(1)!='' || (exists('s:voom_logbnr') && s:voom_logbnr) + return + endif + call Voom_LogConfig() +endfunc + + +"---EXECUTE SCRIPT (Voomexec)-----------------{{{1 + +func! Voom_GetVoomRange(lnum, withSubnodes) "{{{2 + let bnr = bufnr('') + if has_key(s:voom_trees, bnr) + let [bufType, body, tree] = ['Tree', s:voom_trees[bnr], bnr] + if Voom_BufLoaded(body) < 0 | return ['Tree',-1,-1,-1] | endif + elseif has_key(s:voom_bodies, bnr) + let [bufType, body, tree] = ['Body', bnr, s:voom_bodies[bnr].tree] + if Voom_BodyUpdateTree() < 0 | return ['Body',-1,-1,-1] | endif + else + return ['None',0,0,0] + endif + if a:withSubnodes + python voom.voom_GetVoomRange(withSubnodes=1) + else + python voom.voom_GetVoomRange() + return [bufType, body, l:bln1, l:bln2] +" Return [bufType, body, bln1, bln2] for node at line lnum of the current +" VOoM buffer (Tree or Body). +" bln1, bln2: VOoM node's first and last Body lnums. Current node only if +" a:withSubnodes==0. Include all subnodes if a:withSubnodes==1. +" Return [bufType,-1,-1,-1] in case of an error (unloaded Body, etc.) +" Return ['None',0,0,0] for a non-VOoM buffer. +" This is for use by external scripts: +" let [bufType, body, bln1, bln2] = Voom_GetVoomRange(line('.'),0) +" let bodyLines = getbufline(body,bln1,bln2) +endfunc + + +func! Voom_GetBuffRange(ln1, ln2) "{{{2 + let bnr = bufnr('') + if has_key(s:voom_trees, bnr) + let [bufType, body, tree] = ['Tree', s:voom_trees[bnr], bnr] + if Voom_BufLoaded(body) < 0 | return ['Tree',-1,-1,-1] | endif + python voom.voom_GetBuffRange() + return [bufType, body, l:bln1, l:bln2] + elseif has_key(s:voom_bodies, bnr) + return ['Body',bnr,a:ln1,a:ln2] + else + return ['None',bnr,a:ln1,a:ln2] + endif +" Return [bufType, body, bln1, bln2] for line range lnum1,lnum2. +" If current buffer is a Tree: bln1, bln2 are start and end lnums of the +" corresponding Body line range; 'body' is Body's buffer number. +" Return ['Tree',-1,-1,-1] in case of an error (unloaded Body.) +" If current buffer is not a Tree: bln1, bln2 are lnum1, lnum2; 'body' is the +" current buffer number. +" NOTE: Outline is not updated if the current buffer is Body. +endfunc + + +func! Voom_GetExecRange(lnum) "{{{2 +" Return line range info for Voomexec: [bufType, bufnr, start lnum, end lnum] + let bnr = bufnr('') + let status = Voom_FoldStatus(a:lnum) + if status=='hidden' + call Voom_ErrorMsg('VOoM: line is hidden in fold') + return ['',-1,-1,-1] + endif + " Tree buffer: get start/end of Body node and subnodes. + if has_key(s:voom_trees, bnr) + let [bufType, body, tree] = ['Tree', s:voom_trees[bnr], bnr] + if Voom_BufLoaded(body) < 0 | return ['',-1,-1,-1] | endif + python voom.voom_GetVoomRange(withSubnodes=1) + return [bufType, body, l:bln1, l:bln2] + endif + " Any other buffer: get start/end of the current fold and subfolds. + if &fdm !=# 'marker' + call Voom_ErrorMsg('VOoM: ''foldmethod'' must be "marker"') + return ['',-1,-1,-1] + endif + if status=='nofold' + call Voom_ErrorMsg('VOoM: no fold at cursor') + return ['',-1,-1,-1] + elseif status=='folded' + return ['', bnr, foldclosed(a:lnum), foldclosedend(a:lnum)] + elseif status=='notfolded' + let lz_ = &lz | set lz + let winsave_dict = winsaveview() + normal! zc + let foldStart = foldclosed(a:lnum) + let foldEnd = foldclosedend(a:lnum) + normal! zo + call winrestview(winsave_dict) + let &lz=lz_ + return ['', bnr, foldStart, foldEnd] + endif +endfunc + + +func! Voom_Exec(qargs) "{{{2 +" Execute text from the current node (Tree or Body, include subnodes) or fold +" (non-VOoM buffer, include subfolds) as a script. +" If argument is 'vim' or 'py'/'python': execute as Vim or Python script. +" Otherwise execute according to filetype. + + " If current buffer is a Tree: use Body filetype, encodings, etc. + let bnr = bufnr('') + if has_key(s:voom_trees, bnr) + let bnr = s:voom_trees[bnr] + endif + let FT = getbufvar(bnr, '&ft') + + if a:qargs==#'vim' + let scriptType = 'vim' + elseif a:qargs==#'py' || a:qargs==#'python' + let scriptType = 'python' + elseif a:qargs!='' + call Voom_ErrorMsg('VOoM: unsupported script type: "'.a:qargs.'"') + return + elseif FT==#'vim' + let scriptType = 'vim' + elseif FT==#'python' + let scriptType = 'python' + else + call Voom_ErrorMsg('VOoM: unsupported script type: "'.FT.'"') + return + endif + + " Get script lines. + let [bufType, body, bln1, bln2] = Voom_GetExecRange(line('.')) + if body<1 | return | endif + + " Execute Vim script: Copy list of lines to register and execute it. + " Problem: Python errors do not terminate script and Python tracebacks are + " not printed. They are printed to the PyLog if it's enabled. Probably + " caused by 'catch', but without it foldtext is temporarily messed up in + " all windows after any error. + if scriptType==#'vim' + let lines = getbufline(body, bln1, bln2) + if lines==[] | return | endif + let reg_z = getreg('z') + let reg_z_mode = getregtype('z') + let script = join(lines, "\n") . "\n" + call setreg('z', script, "l") + try + call s:Voom_ExecVim() + catch + call Voom_ErrorMsg(v:exception) + finally + call setreg('z', reg_z, reg_z_mode) + echo '---end of Vim script ('.bln1.'-'.bln2.')---' + endtry + " Execute Python script. + elseif scriptType==#'python' + " do not change, see ./voom/voom.py#id_20101214100357 + if s:voom_logbnr + try + python voom.voom_Exec() + catch + python print vim.eval('v:exception') + endtry + else + python voom.voom_Exec() + endif + endif +endfunc + + +func! s:Voom_ExecVim() "{{{2 + @z +endfunc + + +"---execute user command----------------------{{{1 +if exists('g:voom_user_command') + execute g:voom_user_command +endif + + +" modelines {{{1 +" vim:fdm=marker:fdl=0: +" vim:foldtext=getline(v\:foldstart).'...'.(v\:foldend-v\:foldstart): diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom.py new file mode 100644 index 00000000..909f4925 --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom.py @@ -0,0 +1,1957 @@ +# voom.py +# Last Modified: 2012-04-16 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Version: 4.3 +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +"""This module is meant to be imported by voom.vim .""" + +import vim +import sys, os, re +import traceback +import bisect +# lazy imports +shuffle = None # random.shuffle + +#Vim = sys.modules['__main__'] + +# see voom.vim for conventions +# voom_WhatEver() is Python code for Voom_WhatEver() function in voom.vim + + +#---Constants and Settings--------------------{{{1= + +# VO is instance of VoomOutline class, stored in dict VOOMS +# create VOOMS in voom.vim: less disruption if this module is reloaded +#VOOMS = {} # {body: VO, ...} + +# {filetype: make_head_ function, ...} +MAKE_HEAD = {} + +# default start fold marker string and regexp +MARKER = '{{{' #}}} +MARKER_RE = re.compile(r'{{{(\d+)(x?)') #}}} + +# {'markdown': 'markdown', 'pandoc': 'markdown', ...} +if vim.eval("exists('g:voom_ft_modes')")=='1': + FT_MODES = vim.eval('g:voom_ft_modes') +else: + FT_MODES = {} +# default markup mode +if vim.eval("exists('g:voom_default_mode')")=='1': + MODE = vim.eval('g:voom_default_mode') +else: + MODE = '' + + +#---Outline Construction----------------------{{{1o + + +class VoomOutline: #{{{2 + """Outline data for one Body buffer. + Instantiated from Body by Voom_Init(). + """ + def __init__(self,body): + assert body == int(vim.eval("bufnr('')")) + + +def voom_Init(body): #{{{2 + VO = VoomOutline(body) + VO.bnodes = [] # Body lnums of headlines + VO.levels = [] # headline levels + VO.body = body + VO.Body = vim.current.buffer + VO.tree = None # will set later + VO.Tree = None # will set later + VO.snLn = 1 # will change later if different + # first Tree line is Body buffer name and path + VO.bname = vim.eval('l:firstLine') + # Body &filetype + VO.filetype = vim.eval('&filetype') + VO.enc = get_vim_encoding() + + # start fold marker string and regexp (default and 'fmr' modes) + marker = vim.eval('&foldmarker').split(',')[0] + VO.marker = marker + if marker==MARKER: + VO.marker_re = MARKER_RE + else: + VO.marker_re = re.compile(re.escape(marker) + r'(\d+)(x?)') + + # chars to strip from right side of Tree headlines (default and 'fmr' modes) + if vim.eval("exists('g:voom_rstrip_chars_{&ft}')")=="1": + VO.rstrip_chars = vim.eval("g:voom_rstrip_chars_{&ft}") + else: + VO.rstrip_chars = vim.eval("&commentstring").split('%s')[0].strip() + " \t" + + ### get markup mode, l:qargs is mode's name ### + mModule = 0 + qargs = vim.eval('l:qargs').strip() or FT_MODES.get(VO.filetype, MODE) + if qargs: + mName = 'voom_mode_%s' %qargs + try: + mModule = __import__(mName) + VO.bname += ', %s' %qargs + vim.command("call Voom_WarningMsg('VOoM: mode ''%s'' [%s]')" %(qargs.replace("'","''"), os.path.abspath(mModule.__file__).replace("'","''"))) + except ImportError: + vim.command("call Voom_ErrorMsg('VOoM: cannot import Python module %s')" %mName.replace("'","''")) + return + + VO.mModule = mModule + ### define mode-specific methods ### + # no markup mode, default behavior + if not mModule: + VO.mmode = 0 + if VO.filetype in MAKE_HEAD: + VO.makeOutline = makeOutlineH + else: + VO.makeOutline = makeOutline + VO.newHeadline = newHeadline + VO.changeLevBodyHead = changeLevBodyHead + VO.hook_doBodyAfterOop = 0 + # markup mode for fold markers, similar to the default behavior + elif getattr(mModule,'MODE_FMR',0): + VO.mmode = 0 + f = getattr(mModule,'hook_makeOutline',0) + if f: + VO.makeOutline = f + elif VO.filetype in MAKE_HEAD: + VO.makeOutline = makeOutlineH + else: + VO.makeOutline = makeOutline + VO.newHeadline = getattr(mModule,'hook_newHeadline',0) or newHeadline + VO.changeLevBodyHead = changeLevBodyHead + VO.hook_doBodyAfterOop = 0 + # markup mode not for fold markers + else: + VO.mmode = 1 + VO.makeOutline = getattr(mModule,'hook_makeOutline',0) or makeOutline + VO.newHeadline = getattr(mModule,'hook_newHeadline',0) or newHeadline + # These must be False if not defined by the markup mode. + VO.changeLevBodyHead = getattr(mModule,'hook_changeLevBodyHead',0) + VO.hook_doBodyAfterOop = getattr(mModule,'hook_doBodyAfterOop',0) + + ### the end ### + vim.command('let l:mmode=%s' %VO.mmode) + VOOMS[body] = VO + + +def voom_TreeCreate(): #{{{2 + """This is part of Voom_TreeCreate(), called from Tree.""" + body = int(vim.eval('a:body')) + blnr = int(vim.eval('l:blnr')) # Body cursor lnum + VO = VOOMS[body] + + if VO.mmode: + computeSnLn(body, blnr) + # reST, wiki files often have most headlines at level >1 + vim.command('setl fdl=2') + return + + bnodes = VO.bnodes + Body = VO.Body + z = len(bnodes) + + ### compute snLn, create Tree folding + + # find bnode marked with '=' + # find bnodes marked with 'o' + snLn = 0 + marker_re = VO.marker_re + marker_re_search = marker_re.search + oFolds = [] + for i in xrange(1,z): + bline = Body[bnodes[i]-1] + # part of Body headline after marker+level+'x' + bline2 = bline[marker_re_search(bline).end():] + if not bline2: continue + if bline2[0]=='=': + snLn = i+1 + elif bline2[0]=='o': + oFolds.append(i+1) + if bline2[1:] and bline2[1]=='=': + snLn = i+1 + + # create Tree folding + if oFolds: + cFolds = foldingFlip(VO,2,z,oFolds) + foldingCreate(2,z,cFolds) + + if snLn: + vim.command('call Voom_SetSnLn(%s,%s)' %(body,snLn)) + VO.snLn = snLn + # set blnShow if Body cursor is on or before the first headline + if z > 1 and blnr <= bnodes[1]: + vim.command('let l:blnShow=%s' %bnodes[snLn-1]) + else: + # no Body headline is marked with = + # select current Body node + computeSnLn(body, blnr) + + +def makeOutline(VO, blines): #{{{2 + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + # blines is usually Body. It is list of clipboard lines during Paste. + # This function is slower when blines is Vim buffer object instead of + # Python list. But overall time to do outline update is the same and memory + # usage is less because we don't create new list (see v3.0 notes) + + # Optimized for buffers in which most lines don't have fold markers. + + # NOTE: duplicate code with makeOutlineH(), only head construction is different + marker = VO.marker + marker_re_search = VO.marker_re.search + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + c = VO.rstrip_chars + for i in xrange(Z): + if not marker in blines[i]: continue + bline = blines[i] + m = marker_re_search(bline) + if not m: continue + lev = int(m.group(1)) + head = bline[:m.start()].lstrip().rstrip(c).strip('-=~').strip() + tline = ' %s%s|%s' %(m.group(2) or ' ', '. '*(lev-1), head) + tlines_add(tline) + bnodes_add(i+1) + levels_add(lev) + return (tlines, bnodes, levels) + + +def makeOutlineH(VO, blines): #{{{2 + """Identical to makeOutline(), duplicate code. The only difference is that + a custom function is used to construct Tree headline text. + """ + # NOTE: duplicate code with makeOutline(), only head construction is different + marker = VO.marker + marker_re_search = VO.marker_re.search + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + h = MAKE_HEAD[VO.filetype] + for i in xrange(Z): + if not marker in blines[i]: continue + bline = blines[i] + m = marker_re_search(bline) + if not m: continue + lev = int(m.group(1)) + head = h(bline,m) + tline = ' %s%s|%s' %(m.group(2) or ' ', '. '*(lev-1), head) + tlines_add(tline) + bnodes_add(i+1) + levels_add(lev) + return (tlines, bnodes, levels) + + +#--- make_head functions --- {{{2 + +def make_head_html(bline,match): + s = bline[:match.start()].strip().strip('-=~').strip() + if s.endswith(' Z: + snLn = Z + vim.command('call Voom_SetSnLn(%s,%s)' %(body,snLn)) + VO.snLn = snLn + tlines[snLn-1] = '=%s' %tlines[snLn-1][1:] + + ### Compare Tree lines, draw as needed. + # Draw all Tree lines only when needed. This is optimization for large + # outlines, e.g. >1000 Tree lines. Drawing all lines is slower than + # comparing all lines and then drawing nothing or just one line. + + Tree = VO.Tree + #tlines_ = Tree[:] + if not len(Tree)==len(tlines): + Tree[:] = tlines + vim.command('let l:ok=1') + return + + # If only one line is modified, draw that line only. This ensures that + # editing (and inserting) a single headline in a large outline is fast. + # If more than one line is modified, draw all lines from first changed line + # to the end of buffer. + draw_one = False + for i in xrange(len(tlines)): + if not tlines[i]==Tree[i]: + if draw_one==False: + draw_one = True + diff = i + else: + Tree[diff:] = tlines[diff:] + vim.command('let l:ok=1') + return + if draw_one: + Tree[diff] = tlines[diff] + + vim.command('let l:ok=1') + # why l:ok is needed: ../../doc/voom.txt#id_20110213212708 + + +def computeSnLn(body, blnr): #{{{2 + """Compute Tree lnum for node at line blnr in Body body. + Assign Vim and Python snLn vars. + """ + # snLn should be 1 if blnr is before the first node, top of Body + VO = VOOMS[body] + snLn = bisect.bisect_right(VO.bnodes, blnr) + vim.command('call Voom_SetSnLn(%s,%s)' %(body,snLn)) + VO.snLn = snLn + + +def voom_UnVoom(body): #{{{2 + if body in VOOMS: del VOOMS[body] + + +#---Outline Traversal-------------------------{{{1 +# Functions for getting node's parents, children, ancestors, etc. +# Nodes here are Tree buffer lnums. +# All we do is traverse VO.levels. + + +def nodeHasChildren(VO, lnum): #{{{2 + """Determine if node at Tree line lnum has children.""" + levels = VO.levels + if lnum==1 or lnum==len(levels): return False + elif levels[lnum-1] < levels[lnum]: return True + else: return False + + +def nodeSubnodes(VO, lnum): #{{{2 + """Number of all subnodes for node at Tree line lnum.""" + levels = VO.levels + z = len(levels) + if lnum==1 or lnum==z: return 0 + lev = levels[lnum-1] + for i in xrange(lnum,z): + if levels[i]<=lev: + return i-lnum + return z-lnum + + +def nodeParent(VO, lnum): #{{{2 + """Return lnum of closest parent of node at Tree line lnum.""" + levels = VO.levels + lev = levels[lnum-1] + if lev==1: return None + for i in xrange(lnum-2,0,-1): + if levels[i] < lev: return i+1 + + +def nodeAncestors(VO, lnum): #{{{2 + """Return lnums of ancestors of node at Tree line lnum.""" + levels = VO.levels + lev = levels[lnum-1] + if lev==1: return [] + ancestors = [] + for i in xrange(lnum-2,0,-1): + levi = levels[i] + if levi < lev: + lev = levi + ancestors.append(i+1) + if lev==1: + ancestors.reverse() + return ancestors + # we get here if there are no nodes at level 1 (wiki mode) + ancestors.reverse() + return ancestors + + +def nodeUNL(VO, lnum): #{{{2 + """Compute UNL of node at Tree line lnum. + Return list of headlines. + """ + Tree = VO.Tree + levels = VO.levels + if lnum==1: return ['top-of-buffer'] + parents = nodeAncestors(VO,lnum) + parents.append(lnum) + heads = [Tree[ln-1].split('|',1)[1] for ln in parents] + return heads + + +def nodeSiblings(VO, lnum): #{{{2 + """Return lnums of siblings for node at Tree line lnum. + These are nodes with the same parent and level as lnum node. Sorted in + ascending order. lnum itself is included. First node (line 1) is never + included, that is minimum lnum in results is 2. + """ + levels = VO.levels + lev = levels[lnum-1] + siblings = [] + # scan back + for i in xrange(lnum-1,0,-1): + levi = levels[i] + if levi < lev: + break + elif levi==lev: + siblings[0:0] = [i+1] + # scan forward + for i in xrange(lnum,len(levels)): + levi = levels[i] + if levi < lev: + break + elif levi==lev: + siblings.append(i+1) + return siblings + + +def rangeSiblings(VO, lnum1, lnum2): #{{{2 + """Return lnums of siblings for nodes in Tree range lnum1,lnum2. + These are nodes with the same parent and level as lnum1 node. + First node (first Tree line) is never included, that is minimum lnum in results is 2. + Return None if range is ivalid. + """ + if lnum1==1: lnum1 = 2 + if lnum1 > lnum2: return None + levels = VO.levels + lev = levels[lnum1-1] + siblings = [lnum1] + for i in xrange(lnum1,lnum2): + levi = levels[i] + # invalid range + if levi < lev: + return None + elif levi==lev: + siblings.append(i+1) + return siblings + + +def getSiblingsGroups(VO, siblings): #{{{2 + """Return list of groups of siblings in the region defined by 'siblings' + group, which is list of siblings in ascending order (Tree lnums). + Siblings in each group are nodes with the same parent and level. + Siblings in each group are in ascending order. + List of groups is reverse-sorted by level of siblings and by parent lnum: + from RIGHT TO LEFT and from BOTTOM TO TOP. + """ + if not siblings: return [] + levels = VO.levels + lnum1, lnum2 = siblings[0], siblings[-1] + lnum2 = lnum2 + nodeSubnodes(VO,lnum2) + + # get all parents (nodes with children) in the range + parents = [i for i in xrange(lnum1,lnum2) if levels[i-1] '") + vim.command("echohl None") + vim.command("echon '%s'" %(heads[-1].replace("'", "''"))) + + +def voom_Grep(): #{{{2 + body = int(vim.eval('l:body')) + tree = int(vim.eval('l:tree')) + VO = VOOMS[body] + assert VO.tree == tree + bnodes = VO.bnodes + matchesAND, matchesNOT = vim.eval('l:matchesAND'), vim.eval('l:matchesNOT') + + # convert blnums of mathes into tlnums, that is node numbers + tlnumsAND, tlnumsNOT = [], [] # lists of AND and NOT "tlnums" dicts + counts = {} # {tlnum: count of all AND matches in this node, ...} + blnums = {} # {tlnum: first AND match in this node, ...} + for L in matchesAND: + tlnums = {} # {tlnum of node with a match:0, ...} + L.pop() + for bln in L: + bln = int(bln) + tln = bisect.bisect_right(bnodes, bln) + if not tln in blnums: + blnums[tln] = bln + elif blnums[tln] > bln: + blnums[tln] = bln + if tln in counts: + counts[tln]+=1 + else: + counts[tln] = 1 + tlnums[tln] = 0 + tlnumsAND.append(tlnums) + for L in matchesNOT: + tlnums = {} # {tlnum of node with a match:0, ...} + L.pop() + for bln in L: + bln = int(bln) + tln = bisect.bisect_right(bnodes, bln) + tlnums[tln] = 0 + tlnumsNOT.append(tlnums) + + # if there are only NOT patterns + if not matchesAND: + tlnumsAND = [{}.fromkeys(range(1,len(bnodes)+1))] + + # compute intersection + results = intersectDicts(tlnumsAND, tlnumsNOT) + results = results.keys() + results.sort() + #print results + + # need this to left-align UNLs in the qflist + max_size = 0 + for t in results: + if not matchesAND: + blnums[t] = bnodes[t-1] + counts[t] = 0 + size = len('%s%s%s' %(t, counts[t], blnums[t])) + if size > max_size: + max_size = size + + # list of dictionaries for setloclist() or setqflist() + loclist = [] + for t in results: + size = len('%s%s%s' %(t, counts[t], blnums[t])) + spaces = ' '*(max_size - size) + UNL = ' -> '.join(nodeUNL(VO,t)).replace("'", "''") + text = 'n%s:%s%s|%s' %(t, counts[t], spaces, UNL) + d = "{'text':'%s', 'lnum':%s, 'bufnr':%s}, " %(text, blnums[t], body) + loclist .append(d) + #print '\n'.join(loclist) + + vim.command("call setqflist([%s],'a')" %(''.join(loclist)) ) + + +def intersectDicts(dictsAND, dictsNOT): #{{{2 + """Arguments are two lists of dictionaries. Keys are Tree lnums. + Return dict: intersection of all dicts in dictsAND and non-itersection with + all dicts in dictsNOT. + """ + if not dictsAND: return {} + D1 = dictsAND[0] + if len(dictsAND)==1: + res = D1 + else: + res = {} + # get intersection with all other AND dicts + for D in dictsAND[1:]: + for item in D1: + if item in D: res[item] = 0 + # get non-intersection with NOT dicts + for D in dictsNOT: + keys = res.keys() + for key in keys: + if key in D: del res[key] + return res + + +#---Outline Operations------------------------{{{1o +# voom_Oop... functions are called from Voom_Oop... Vim functions. +# They use local Vim vars set by the caller and can create and change Vim vars. +# Most of them set lines in Tree and Body via vim.buffer objects. +# Default l:blnShow is -1. +# Returning before setting l:blnShow means no changes were made. + + +def changeLevTreeHead(h, levDelta): #{{{2 + """Increase of decrese level of Tree headline by levDelta: + insert or delete levDelta*". " string. + """ + if levDelta > 0: + return '%s%s%s' %(h[:2], '. '*levDelta, h[2:]) + elif levDelta < 0: + return '%s%s' %(h[:2], h[2-2*levDelta:]) + else: + return h + + +def changeLevBodyHead(VO, h, levDelta): #{{{2 + """Increase of decrease level number of Body headline by levDelta. + NOTE: markup modes can replace this function with hook_changeLevBodyHead. + """ + if levDelta==0: return h + m = VO.marker_re.search(h) + level = int(m.group(1)) + return '%s%s%s' %(h[:m.start(1)], level+levDelta, h[m.end(1):]) + + +def newHeadline(VO, level, blnum, ln): #{{{2 + """Return (tree_head, bodyLines). + tree_head is new headline string in Tree buffer (text after |). + bodyLines is list of lines to insert in Body buffer. + """ + tree_head = 'NewHeadline' + bodyLines = ['---%s--- %s%s' %(tree_head, VO.marker, level), ''] + return (tree_head, bodyLines) + + +def setClipboard(s): #{{{2 + """Set Vim + register (system clipboard) to string s.""" + # important: use '' for Vim string + vim.command("let @+='%s'" %s.replace("'", "''")) + + # The above failed once: empty clipboard after copy/delete >5MB outline. Could + # not reproduce after Windows restart. Probably stale system. Thus the + # following check. It adds about 0.09 sec for each 1MB in the clipboard. + # 30-40% increase overall in the time of Copy operation (yy). + if not vim.eval('len(@+)')=='%s' %len(s): + vim.command("echoerr 'VOoM: error setting clipboard'") + + +def voom_OopVerify(): #{{{2 + body, tree = int(vim.eval('a:body')), int(vim.eval('a:tree')) + VO = VOOMS[body] + assert VO.tree == tree + + tlines, bnodes, levels = VO.makeOutline(VO, VO.Body) + if not len(VO.Tree)==len(tlines)+1: + vim.command("echoerr 'VOoM: wrong Tree size'") + return + tlines[0:0], bnodes[0:0], levels[0:0] = [VO.bname], [1], [1] + snLn = VO.snLn + tlines[snLn-1] = '=%s' %tlines[snLn-1][1:] + + ok = True + if not VO.Tree[:] == tlines: + vim.command("echoerr 'VOoM: DIFFERENT Tree lines'") + ok = False + if not VO.bnodes == bnodes: + vim.command("echoerr 'VOoM: DIFFERENT bnodes'") + ok = False + if not VO.levels == levels: + vim.command("echoerr 'VOoM: DIFFERENT levels'") + ok = False + if ok: + vim.command("let l:ok=1") + + +def voom_OopSelEnd(): #{{{2 + """This is part of Voom_Oop() checks. + Selection in Tree starts at line ln1 and ends at line ln2. + Selection can have many sibling nodes: nodes with the same level as ln1 node. + Return lnum of last node in the last sibling node's branch. + Return 0 if selection is invalid. + """ + body = int(vim.eval('l:body')) + ln1, ln2 = int(vim.eval('l:ln1')), int(vim.eval('l:ln2')) + if ln1==1: return 0 + levels = VOOMS[body].levels + z, lev0 = len(levels), levels[ln1-1] + for i in xrange(ln1,z): + lev = levels[i] + # invalid selection: there is node with level smaller than that of ln1 node + if i+1 <= ln2 and lev < lev0: return 0 + # node after the last sibling node's branch + elif i+1 > ln2 and lev <= lev0: return i + return z + + +def voom_OopSelectBodyRange(): # {{{2 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln1, ln2 = int(vim.eval('l:ln1')), int(vim.eval('l:ln2')) + VO = VOOMS[body] + assert VO.tree == tree + bln1, bln2 = nodesBodyRange(VO, ln1, ln2) + vim.command("let [l:bln1,l:bln2]=[%s,%s]" %(bln1,bln2)) + + +def voom_OopInsert(as_child=False): #{{{2 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln, ln_status = int(vim.eval('l:ln')), vim.eval('l:ln_status') + VO = VOOMS[body] + assert VO.tree == tree + Body, Tree, levels, snLn = VO.Body, VO.Tree, VO.levels, VO.snLn + + # Compute where to insert and at what level. + # Insert new headline after node at ln. + # If node is folded, insert after the end of node's tree. + # default level + lev = levels[ln-1] + # after first Tree line + if ln==1: lev=1 + # as_child always inserts as first child of current node, even if it's folded + elif as_child: lev+=1 + # after last Tree line, same level + elif ln==len(levels): pass + # node has children, it can be folded + elif lev < levels[ln]: + # folded: insert after current node's branch, same level + if ln_status=='folded': ln += nodeSubnodes(VO,ln) + # not folded, insert as child + else: lev+=1 + + # remove = mark before modifying Tree + Tree[snLn-1] = ' ' + Tree[snLn-1][1:] + + # insert headline in Tree and Body + # bLnum is Body lnum after which to insert new headline + if ln < len(levels): + bLnum = VO.bnodes[ln]-1 + else: + bLnum = len(Body) + + tree_head, bodyLines = VO.newHeadline(VO,lev,bLnum,ln) + + treeLine = '= %s|%s' %('. '*(lev-1), tree_head) + Tree[ln:ln] = [treeLine] + Body[bLnum:bLnum] = bodyLines + + vim.command('let l:bLnum=%s' %(bLnum+1)) + + # write = mark and set snLn to new headline + Tree[ln] = '=' + Tree[ln][1:] + VO.snLn = ln+1 + vim.command('call Voom_SetSnLn(%s,%s)' %(body, ln+1)) + + +def voom_OopCopy(): #{{{2 + body = int(vim.eval('l:body')) + ln1, ln2 = int(vim.eval('l:ln1')), int(vim.eval('l:ln2')) + VO = VOOMS[body] + Body, bnodes = VO.Body, VO.bnodes + + # body lines to copy + bln1 = bnodes[ln1-1] + if ln2 < len(bnodes): bln2 = bnodes[ln2]-1 + else: bln2 = len(Body) + blines = Body[bln1-1:bln2] + + setClipboard('\n'.join(blines)) + + +def voom_OopCut(): #{{{2 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln1, ln2 = int(vim.eval('l:ln1')), int(vim.eval('l:ln2')) + lnUp1 = int(vim.eval('l:lnUp1')) + VO = VOOMS[body] + assert VO.tree == tree + Body, Tree = VO.Body, VO.Tree + bnodes, levels = VO.bnodes, VO.levels + + # diagram {{{ + # .............. blnUp1-1 + # ============== blnUp1=bnodes[lnUp1-1] + # .............. + # ============== bln1=bnodes[ln1-1] + # range being + # deleted + # .............. bln2=bnodes[ln2]-1, or last Body line + # ============== + # .............. }}} + + ### copy and delete body lines + bln1 = bnodes[ln1-1] + if ln2 < len(bnodes): bln2 = bnodes[ln2]-1 + else: bln2 = len(Body) + blines = Body[bln1-1:bln2] + + setClipboard('\n'.join(blines)) + Body[bln1-1:bln2] = [] + + blnShow = bnodes[lnUp1-1] # does not change + + ### update bnodes + # decrement lnums after deleted range + delta = bln2-bln1+1 + for i in xrange(ln2,len(bnodes)): + bnodes[i]-=delta + # cut + bnodes[ln1-1:ln2] = [] + + ### delete range in levels (same as in Tree) + levels[ln1-1:ln2] = [] + + if VO.hook_doBodyAfterOop: + VO.hook_doBodyAfterOop(VO, 'cut', 0, None, None, None, None, bln1-1, ln1-1) + + ### ---go back to Tree--- + vim.command('let l:blnShow=%s' %blnShow) + vim.command("call Voom_OopFromBody(%s,%s,%s,1)" %(body,tree, blnShow)) + + ### remove = mark before modifying Tree + snLn = VO.snLn + Tree[snLn-1] = ' ' + Tree[snLn-1][1:] + ### delete range in Tree (same as in levels)) + Tree[ln1-1:ln2] = [] + + ### add snLn mark + Tree[lnUp1-1] = '=' + Tree[lnUp1-1][1:] + VO.snLn = lnUp1 + + +def voom_OopPaste(): #{{{2 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln, ln_status = int(vim.eval('l:ln')), vim.eval('l:ln_status') + VO = VOOMS[body] + assert VO.tree == tree + Body, Tree = VO.Body, VO.Tree + levels, bnodes = VO.levels, VO.bnodes + + ### clipboard + pText = vim.eval('@+') + if not pText: + vim.command("call Voom_ErrorMsg('VOoM (paste): clipboard is empty')") + vim.command("call Voom_OopFromBody(%s,%s,-1,1)" %(body,tree)) + return + pBlines = pText.split('\n') # Body lines to paste + pTlines, pBnodes, pLevels = VO.makeOutline(VO, pBlines) + + ### verify that clipboard is a valid outline + if pBnodes==[] or pBnodes[0]!=1: + vim.command("call Voom_ErrorMsg('VOoM (paste): invalid clipboard--first line is not a headline')") + vim.command("call Voom_OopFromBody(%s,%s,-1,1)" %(body,tree)) + return + lev_ = pLevels[0] + for lev in pLevels: + # there is node with level smaller than that of the first node + if lev < pLevels[0]: + vim.command("call Voom_ErrorMsg('VOoM (paste): invalid clipboard--root level error')") + vim.command("call Voom_OopFromBody(%s,%s,-1,1)" %(body,tree)) + return + # level incremented by 2 or more + elif lev-lev_ > 1: + vim.command("call Voom_WarningMsg('VOoM (paste): inconsistent levels in clipboard--level incremented by >1', ' ')") + lev_ = lev + + ### compute where to insert and at what level + # insert nodes after node at ln at level lev + # if node is folded, insert after the end of node's tree + lev = levels[ln-1] # default level + # after first Tree line: use level of next node in case min level is not 1 (wiki mode) + if ln==1: + if len(levels)>1: lev = levels[1] + else: lev=1 + # after last Tree line, same level + elif ln==len(levels): pass + # node has children, it can be folded + elif lev < levels[ln]: + # folded: insert after current node's branch, same level + if ln_status=='folded': ln += nodeSubnodes(VO,ln) + # not folded, insert as child + else: lev+=1 + + ### adjust levels of nodes being inserted + levDelta = lev - pLevels[0] + if levDelta: + pTlines = [changeLevTreeHead(h, levDelta) for h in pTlines] + pLevels = [(lev+levDelta) for lev in pLevels] + f = VO.changeLevBodyHead + if f: + for bl in pBnodes: + pBlines[bl-1] = f(VO, pBlines[bl-1], levDelta) + + ### insert body lines in Body + # bln is Body lnum after which to insert + if ln < len(bnodes): bln = bnodes[ln]-1 + else: bln = len(Body) + Body[bln:bln] = pBlines + blnShow = bln+1 + + ### update bnodes + # increment bnodes being pasted + for i in xrange(0,len(pBnodes)): + pBnodes[i]+=bln + # increment bnodes after pasted region + delta = len(pBlines) + for i in xrange(ln,len(bnodes)): + bnodes[i]+=delta + # insert pBnodes after ln + bnodes[ln:ln] = pBnodes + + ### insert new levels in levels (same as in Tree) + levels[ln:ln] = pLevels + + ### start and end lnums of inserted region + ln1 = ln+1 + ln2 = ln+len(pBnodes) + + if VO.hook_doBodyAfterOop: + VO.hook_doBodyAfterOop(VO, 'paste', levDelta, + blnShow, ln1, + blnShow+len(pBlines)-1, ln2, + None, None) + + ### ---go back to Tree--- + vim.command("call Voom_OopFromBody(%s,%s,%s,1)" %(body,tree, blnShow)) + + # remove = mark before modifying Tree + snLn = VO.snLn + Tree[snLn-1] = ' ' + Tree[snLn-1][1:] + ### insert new headlines in Tree (same as in levels) + Tree[ln:ln] = pTlines + + ### start and end lnums of inserted region + vim.command('let l:ln1=%s' %ln1) + vim.command('let l:ln2=%s' %ln2) + # set snLn to first headline of inserted nodes + Tree[ln1-1] = '=' + Tree[ln1-1][1:] + VO.snLn = ln1 + + # we don't get here if previous code fails + vim.command('let l:blnShow=%s' %blnShow) + + +def voom_OopUp(): #{{{2 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln1, ln2 = int(vim.eval('l:ln1')), int(vim.eval('l:ln2')) + lnUp1, lnUp2 = int(vim.eval('l:lnUp1')), int(vim.eval('l:lnUp2')) + VO = VOOMS[body] + assert VO.tree == tree + Body, Tree = VO.Body, VO.Tree + bnodes, levels = VO.bnodes, VO.levels + + # diagram {{{ + # .............. blnUp1-1 + # ============== blnUp1=bnodes[lnUp1-1] + # range before + # which to move + # .............. + # ============== bln1=bnodes[ln1-1] + # range being + # moved + # .............. bln2=bnodes[ln2]-1, or last Body line + # ============== + # .............. }}} + + ### compute change in level + # current level of root nodes in selection + levOld = levels[ln1-1] + # new level of root nodes in selection + # lnUp1 is fist child of lnUp2, insert also as first child + if levels[lnUp2-1] + 1 == levels[lnUp1-1]: + levNew = levels[lnUp1-1] + # all other cases, includes insertion after folded node + else: + levNew = levels[lnUp2-1] + levDelta = levNew-levOld + + ### body lines to move + bln1 = bnodes[ln1-1] + if ln2 < len(bnodes): bln2 = bnodes[ln2]-1 + else: bln2 = len(Body) + blines = Body[bln1-1:bln2] + if levDelta: + f = VO.changeLevBodyHead + if f: + for bl in bnodes[ln1-1:ln2]: + blines[bl-bln1] = f(VO, blines[bl-bln1], levDelta) + + ### move body lines: cut, then insert + # insert before line blnUp1, it will not change after bnodes update + blnUp1 = bnodes[lnUp1-1] + blnShow = blnUp1 + Body[bln1-1:bln2] = [] + Body[blnUp1-1:blnUp1-1] = blines + + ###update bnodes + # increment lnums in the range before which the move is made + delta = bln2-bln1+1 + for i in xrange(lnUp1-1,ln1-1): + bnodes[i]+=delta + # decrement lnums in the range which is being moved + delta = bln1-blnUp1 + for i in xrange(ln1-1,ln2): + bnodes[i]-=delta + # cut, insert + nLines = bnodes[ln1-1:ln2] + bnodes[ln1-1:ln2] = [] + bnodes[lnUp1-1:lnUp1-1] = nLines + + ### update levels (same as for Tree) + nLevels = levels[ln1-1:ln2] + if levDelta: + nLevels = [(lev+levDelta) for lev in nLevels] + # cut, then insert + levels[ln1-1:ln2] = [] + levels[lnUp1-1:lnUp1-1] = nLevels + + if VO.hook_doBodyAfterOop: + VO.hook_doBodyAfterOop(VO, 'up', levDelta, + blnShow, lnUp1, + blnShow+len(blines)-1, lnUp1+len(nLevels)-1, + bln1-1+len(blines), ln1-1+len(nLevels)) + + ### ---go back to Tree--- + vim.command("call Voom_OopFromBody(%s,%s,%s,1)" %(body,tree, blnShow)) + + ### remove snLn mark before modifying Tree + snLn = VO.snLn + Tree[snLn-1] = ' ' + Tree[snLn-1][1:] + + ### update Tree (same as for levels) + tlines = Tree[ln1-1:ln2] + if levDelta: + tlines = [changeLevTreeHead(h, levDelta) for h in tlines] + # cut, then insert + Tree[ln1-1:ln2] = [] + Tree[lnUp1-1:lnUp1-1] = tlines + + ### add snLn mark + Tree[lnUp1-1] = '=' + Tree[lnUp1-1][1:] + VO.snLn = lnUp1 + + # we don't get here only if previous code fails + vim.command('let l:blnShow=%s' %blnShow) + + +def voom_OopDown(): #{{{2 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln1, ln2 = int(vim.eval('l:ln1')), int(vim.eval('l:ln2')) + lnDn1, lnDn1_status = int(vim.eval('l:lnDn1')), vim.eval('l:lnDn1_status') + # note: lnDn1 == ln2+1 + VO = VOOMS[body] + assert VO.tree == tree + Body, Tree = VO.Body, VO.Tree + bnodes, levels = VO.bnodes, VO.levels + + # diagram {{{ + # .............. + # ============== bln1=bnodes[ln1-1] + # range being + # moved + # .............. bln2=bnodes[ln2]-1 + # ============== blnDn1=bnodes[lnDn1-1] + # range after + # which to move + # .............. blnIns=bnodes[lnIns]-1, or last Body line + # ============== + # .............. }}} + + ### compute change in level, and line after which to insert + # current level + levOld = levels[ln1-1] + # new level is either that of lnDn1 or +1 + levNew = levels[lnDn1-1] + # line afer which to insert + lnIns = lnDn1 + if lnDn1==len(levels): # end of Tree + pass + # lnDn1 has children; insert as child unless it's folded + elif levels[lnDn1-1] < levels[lnDn1]: + if lnDn1_status=='folded': + lnIns += nodeSubnodes(VO,lnDn1) + else: + levNew+=1 + levDelta = levNew-levOld + + ### body lines to move + bln1 = bnodes[ln1-1] + bln2 = bnodes[ln2]-1 + blines = Body[bln1-1:bln2] + if levDelta: + f = VO.changeLevBodyHead + if f: + for bl in bnodes[ln1-1:ln2]: + blines[bl-bln1] = f(VO, blines[bl-bln1], levDelta) + + ### move body lines: insert, then cut + if lnIns < len(bnodes): blnIns = bnodes[lnIns]-1 + else: blnIns = len(Body) + Body[blnIns:blnIns] = blines + Body[bln1-1:bln2] = [] + + ### update bnodes + # increment lnums in the range which is being moved + delta = blnIns-bln2 + for i in xrange(ln1-1,ln2): + bnodes[i]+=delta + # decrement lnums in the range after which the move is made + delta = bln2-bln1+1 + for i in xrange(ln2,lnIns): + bnodes[i]-=delta + # insert, cut + nLines = bnodes[ln1-1:ln2] + bnodes[lnIns:lnIns] = nLines + bnodes[ln1-1:ln2] = [] + + ### compute and set new snLn, blnShow + snLn_ = VO.snLn + snLn = lnIns+1-(ln2-ln1+1) + VO.snLn = snLn + vim.command('let snLn=%s' %snLn) + + blnShow = bnodes[snLn-1] # must compute after bnodes update + + ### update levels (same as for Tree) + nLevels = levels[ln1-1:ln2] + if levDelta: + nLevels = [(lev+levDelta) for lev in nLevels] + # insert, then cut + levels[lnIns:lnIns] = nLevels + levels[ln1-1:ln2] = [] + + if VO.hook_doBodyAfterOop: + VO.hook_doBodyAfterOop(VO, 'down', levDelta, + blnShow, snLn, + blnShow+len(blines)-1, snLn+len(nLevels)-1, + bln1-1, ln1-1) + + ### ---go back to Tree--- + vim.command("call Voom_OopFromBody(%s,%s,%s,1)" %(body,tree, blnShow)) + + ### remove snLn mark before modifying Tree + Tree[snLn_-1] = ' ' + Tree[snLn_-1][1:] + + ### update Tree (same as for levels) + tlines = Tree[ln1-1:ln2] + if levDelta: + tlines = [changeLevTreeHead(h, levDelta) for h in tlines] + # insert, then cut + Tree[lnIns:lnIns] = tlines + Tree[ln1-1:ln2] = [] + + ### add snLn mark + Tree[snLn-1] = '=' + Tree[snLn-1][1:] + + # we don't get here only if previous code fails + vim.command('let l:blnShow=%s' %blnShow) + + +def voom_OopRight(): #{{{2 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln1, ln2 = int(vim.eval('l:ln1')), int(vim.eval('l:ln2')) + VO = VOOMS[body] + assert VO.tree == tree + Body, Tree = VO.Body, VO.Tree + bnodes, levels = VO.bnodes, VO.levels + + ### Move right means increment level by 1 for all nodes in the range. + + # can't move right if ln1 node is child of previous node + if levels[ln1-1] > levels[ln1-2]: + vim.command("call Voom_OopFromBody(%s,%s,-1,1)" %(body,tree)) + return + + ### change levels of Body headlines + f = VO.changeLevBodyHead + if f: + for bln in bnodes[ln1-1:ln2]: + Body[bln-1] = f(VO, Body[bln-1], 1) + + # new snLn will be set to ln1 + blnShow = bnodes[ln1-1] + + ### change levels of VO.levels (same as for Tree) + nLevels = levels[ln1-1:ln2] + nLevels = [(lev+1) for lev in nLevels] + levels[ln1-1:ln2] = nLevels + + if VO.hook_doBodyAfterOop: + if ln2 < len(bnodes): blnum2 = bnodes[ln2]-1 + else: blnum2 = len(Body) + VO.hook_doBodyAfterOop(VO, 'right', 1, blnShow, ln1, blnum2, ln2, None, None) + + ### ---go back to Tree--- + vim.command("let &fdm=fdm_b") + vim.command("call Voom_OopFromBody(%s,%s,%s,1)" %(body,tree, blnShow)) + + ### change levels of Tree lines (same as for VO.levels) + tlines = Tree[ln1-1:ln2] + tlines = [changeLevTreeHead(h, 1) for h in tlines] + Tree[ln1-1:ln2] = tlines + + ### set snLn to ln1 + snLn = VO.snLn + if not snLn==ln1: + Tree[snLn-1] = ' ' + Tree[snLn-1][1:] + snLn = ln1 + Tree[snLn-1] = '=' + Tree[snLn-1][1:] + VO.snLn = snLn + + # we don't get here if previous code fails + vim.command('let l:blnShow=%s' %blnShow) + + +def voom_OopLeft(): #{{{2 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln1, ln2 = int(vim.eval('l:ln1')), int(vim.eval('l:ln2')) + VO = VOOMS[body] + assert VO.tree == tree + Body, Tree = VO.Body, VO.Tree + bnodes, levels = VO.bnodes, VO.levels + + ### Move left means decrement level by 1 for all nodes in the range. + + # can't move left if at top level 1 + if levels[ln1-1]==1: + vim.command("call Voom_OopFromBody(%s,%s,-1,1)" %(body,tree)) + return + # don't move left if the range is not at the end of subtree + if ln2 < len(levels) and levels[ln2]==levels[ln1-1]: + vim.command("call Voom_OopFromBody(%s,%s,-1,1)" %(body,tree)) + return + + ### change levels of Body headlines + f = VO.changeLevBodyHead + if f: + for bln in bnodes[ln1-1:ln2]: + Body[bln-1] = f(VO, Body[bln-1], -1) + + # new snLn will be set to ln1 + blnShow = bnodes[ln1-1] + + ### change levels of VO.levels (same as for Tree) + nLevels = levels[ln1-1:ln2] + nLevels = [(lev-1) for lev in nLevels] + levels[ln1-1:ln2] = nLevels + + if VO.hook_doBodyAfterOop: + if ln2 < len(bnodes): blnum2 = bnodes[ln2]-1 + else: blnum2 = len(Body) + VO.hook_doBodyAfterOop(VO, 'left', -1, blnShow, ln1, blnum2, ln2, None, None) + + ### ---go back to Tree--- + vim.command("let &fdm=fdm_b") + vim.command("call Voom_OopFromBody(%s,%s,%s,1)" %(body,tree, blnShow)) + + ### change levels of Tree lines (same as for VO.levels) + tlines = Tree[ln1-1:ln2] + tlines = [changeLevTreeHead(h, -1) for h in tlines] + Tree[ln1-1:ln2] = tlines + + ### set snLn to ln1 + snLn = VO.snLn + if not snLn==ln1: + Tree[snLn-1] = ' ' + Tree[snLn-1][1:] + snLn = ln1 + Tree[snLn-1] = '=' + Tree[snLn-1][1:] + VO.snLn = snLn + + # we don't get here if previous code fails + vim.command('let l:blnShow=%s' %blnShow) + + +def voom_OopMark(): # {{{2 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln1, ln2 = int(vim.eval('l:ln1')), int(vim.eval('l:ln2')) + VO = VOOMS[body] + assert VO.tree == tree + Body, Tree = VO.Body, VO.Tree + bnodes, levels = VO.bnodes, VO.levels + marker_re = VO.marker_re + + for i in xrange(ln1-1,ln2): + # insert 'x' in Tree line + tline = Tree[i] + if tline[1]!='x': + Tree[i] = '%sx%s' %(tline[0], tline[2:]) + # insert 'x' in Body headline + bln = bnodes[i] + bline = Body[bln-1] + end = marker_re.search(bline).end(1) + Body[bln-1] = '%sx%s' %(bline[:end], bline[end:]) + + +def voom_OopUnmark(): # {{{2 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln1, ln2 = int(vim.eval('l:ln1')), int(vim.eval('l:ln2')) + VO = VOOMS[body] + assert VO.tree == tree + Body, Tree = VO.Body, VO.Tree + bnodes, levels = VO.bnodes, VO.levels + marker_re = VO.marker_re + + for i in xrange(ln1-1,ln2): + # remove 'x' from Tree line + tline = Tree[i] + if tline[1]=='x': + Tree[i] = '%s %s' %(tline[0], tline[2:]) + # remove 'x' from Body headline + bln = bnodes[i] + bline = Body[bln-1] + end = marker_re.search(bline).end(1) + # remove one 'x', not enough + #Body[bln-1] = '%s%s' %(bline[:end], bline[end+1:]) + # remove all consecutive 'x' chars + Body[bln-1] = '%s%s' %(bline[:end], bline[end:].lstrip('x')) + + +def voom_OopMarkStartup(): # {{{2 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln = int(vim.eval('l:ln')) + VO = VOOMS[body] + assert VO.tree == tree + Body, Tree = VO.Body, VO.Tree + bnodes, levels = VO.bnodes, VO.levels + marker_re = VO.marker_re + + if ln==1: + bln_selected = 0 + else: + bln_selected = bnodes[ln-1] + # remove '=' from all other Body headlines + # also, strip 'x' and 'o' after removed '=' + for bln in bnodes[1:]: + if bln==bln_selected: continue + bline = Body[bln-1] + end = marker_re.search(bline).end() + bline2 = bline[end:] + if not bline2: continue + if bline2[0]=='=': + Body[bln-1] = '%s%s' %(bline[:end], bline[end:].lstrip('=xo')) + elif bline2[0]=='o' and bline2[1:] and bline2[1]=='=': + Body[bln-1] = '%s%s' %(bline[:end+1], bline[end+1:].lstrip('=xo')) + + if ln==1: return + + # insert '=' in current Body headline, but only if it's not there already + bline = Body[bln_selected-1] + end = marker_re.search(bline).end() + bline2 = bline[end:] + if not bline2: + Body[bln_selected-1] = '%s=' %bline + return + if bline2[0]=='=': + return + elif bline2[0]=='o' and bline2[1:] and bline2[1]=='=': + return + elif bline2[0]=='o': + end+=1 + Body[bln_selected-1] = '%s=%s' %(bline[:end], bline[end:]) + + +#--- Tree Folding Operations --- {{{2 +# Opened/Closed Tree buffer folds are equivalent to Expanded/Contracted nodes. +# By default, folds are closed. +# Opened folds are marked by 'o' in Body headlines (after 'x', before '='). +# +# To determine which folds are currently closed/opened, we open all closed +# folds one by one, from top to bottom, starting from top level visible folds. +# This produces list of closed folds. +# +# To restore folding according to a list of closed folds: +# open all folds; +# close folds from bottom to top. +# +# Conventions: +# cFolds --lnums of closed folds +# oFolds --lnums of opened folds +# ln, ln1, ln2 --Tree line number +# +# NOTE: Cursor position and window view are not restored here. +# See also: +# ../../doc/voom.txt#id_20110120011733 + + +def voom_OopFolding(action): #{{{3 + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + VO = VOOMS[body] + assert VO.tree == tree + # check and adjust range lnums + # don't worry about invalid range lnums: Vim checks that + if not action=='cleanup': + ln1, ln2 = int(vim.eval('a:ln1')), int(vim.eval('a:ln2')) + if ln2 1: + vim.command("call Voom_ErrorMsg('VOoM (sort): these options cannot be combined: r, flip, shuffle')") + return + + if D['oShuffle']: + global shuffle + if shuffle is None: from random import shuffle + + if D['oUnicode']: + D['oEnc'] = get_vim_encoding() + ###### }}} + + ### get other Vim data, compute 'siblings' {{{ + body, tree = int(vim.eval('l:body')), int(vim.eval('l:tree')) + ln1, ln2 = int(vim.eval('a:ln1')), int(vim.eval('a:ln2')) + if ln21 siblings, order changed after sort) + flag1,flag2 = 0,0 + if not oDeep: + flag1,flag2 = sortSiblings(VO, siblings, **D) + else: + siblings_groups = getSiblingsGroups(VO,siblings) + for group in siblings_groups: + m, n = sortSiblings(VO, group, **D) + flag1+=m; flag2+=n + + if flag1==0: + vim.command("call Voom_WarningMsg('VOoM (sort): nothing to sort')") + return + elif flag2==0: + vim.command("call Voom_WarningMsg('VOoM (sort): already sorted')") + return + + # Show first sibling. Tracking the current node and bnode is too hard. + lnum1 = siblings[0] + lnum2 = siblings[-1] + nodeSubnodes(VO,siblings[-1]) + blnShow = bnodes[lnum1-1] + vim.command('let [l:blnShow,l:lnum1,l:lnum2]=[%s,%s,%s]' %(blnShow,lnum1,lnum2)) + + +def sortSiblings(VO, siblings, oIgnorecase, oUnicode, oEnc, oReverse, oFlip, oShuffle): #{{{3 + """Sort sibling nodes. 'siblings' is list of Tree lnums in ascending order. + This only modifies Body buffer. Outline data are not updated. + Return progress flags (flag1,flag2), see voom_OopSort(). + """ + sibs = siblings + if len(sibs) < 2: + return (0,0) + Body, Tree = VO.Body, VO.Tree + bnodes, levels = VO.bnodes, VO.levels + z, Z = len(sibs), len(bnodes) + + ### decorate siblings for sorting + # [(Tree headline text, index, lnum), ...] + sibs_dec = [] + for i in xrange(z): + sib = sibs[i] + head = Tree[sib-1].split('|',1)[1] + if oUnicode and oEnc: + head = unicode(head, oEnc, 'replace') + if oIgnorecase: + head = head.lower() + sibs_dec.append((head, i, sib)) + + ### sort + if oReverse: + sibs_dec.sort(key=lambda x: x[0], reverse=True) + elif oFlip: + sibs_dec.reverse() + elif oShuffle: + shuffle(sibs_dec) + else: + sibs_dec.sort() + + sibs_sorted = [i[2] for i in sibs_dec] + #print sibs_dec; print sibs_sorted + if sibs==sibs_sorted: + return (1,0) + + ### blnum1, blnum2: first and last Body lnums of the affected region + blnum1 = bnodes[sibs[0]-1] + n = sibs[-1] + nodeSubnodes(VO,sibs[-1]) + if n < Z: + blnum2 = bnodes[n]-1 + else: + blnum2 = len(Body) + + ### construct new Body region + blines = [] + for i in xrange(z): + sib = sibs[i] + j = sibs_dec[i][1] # index into sibs that points to new sib + sib_new = sibs[j] + + # get Body region for sib_new branch + bln1 = bnodes[sib_new-1] + if j+1 < z: + sib_next = sibs[j+1] + bln2 = bnodes[sib_next-1]-1 + else: + node_last = sib_new + nodeSubnodes(VO,sib_new) + if node_last < Z: + bln2 = bnodes[node_last]-1 + else: + bln2 = len(Body) + + blines.extend(Body[bln1-1:bln2]) + + ### replace Body region with the new, sorted region + body_len = len(Body) + Body[blnum1-1:blnum2] = blines + assert body_len == len(Body) + + return (1,1) + + +#---EXECUTE SCRIPT----------------------------{{{1 +# + +def voom_GetVoomRange(withSubnodes=0): #{{{2 + body = int(vim.eval('l:body')) + VO = VOOMS[body] + lnum = int(vim.eval('a:lnum')) + if vim.eval('l:bufType')=='Body': + lnum = bisect.bisect_right(VO.bnodes, lnum) + bln1, bln2 = nodesBodyRange(VO, lnum, lnum, withSubnodes) + vim.command("let [l:bln1,l:bln2]=[%s,%s]" %(bln1,bln2)) + + +def voom_GetBuffRange(): #{{{2 + body = int(vim.eval('l:body')) + ln1, ln2 = int(vim.eval('a:ln1')), int(vim.eval('a:ln2')) + VO = VOOMS[body] + bln1, bln2 = nodesBodyRange(VO, ln1, ln2) + vim.command("let [l:bln1,l:bln2]=[%s,%s]" %(bln1,bln2)) + + +def voom_Exec(): #{{{2 + if vim.eval('l:bufType')=='Tree': + Buf = VOOMS[int(vim.eval('l:body'))].Body + else: + Buf = vim.current.buffer + bln1, bln2 = int(vim.eval('l:bln1')), int(vim.eval('l:bln2')) + blines = Buf[bln1-1:bln2] + # specifiy script encoding (Vim internal encoding) on the first line + enc = '# -*- coding: %s -*-' %get_vim_encoding() + # prepend extra \n's to make traceback lnums match buffer lnums + # TODO: find less silly way to adjust traceback lnums + script = '%s\n%s%s\n' %(enc, '\n'*(bln1-2), '\n'.join(blines)) + d = {'vim':vim, 'VOOMS':VOOMS, 'voom':sys.modules['voom']} + try: + exec script in d + #except Exception: # does not catch vim.error + except: + #traceback.print_exc() # writes to sys.stderr + printTraceback(bln1,bln2) + + print '---end of Python script (%s-%s)---' %(bln1,bln2) + +# id_20101214100357 +# NOTES on printing Python tracebacks and Vim errors. +# +# When there is no PyLog, we want Python traceback echoed as Vim error message. +# Writing to sys.stderr accomplishes that: +# :py sys.stderr.write('oopsy-doopsy') +# Drawback: writing to default sys.stderr (no PyLog) triggers Vim error. +# Thus, without PyLog there are two useless lines on top with Vim error: +# Error detected while processing function Voom_Exec: +# line 63: +# +# Vim code: +# +# 1) PyLog is enabled. Must execute this inside try/catch/entry. +# Otherwise, something weird happens when Vim error occurs, most likely +# Vim error echoing interferes with PyLog scrolling. +# The only downside is that only v:exception is printed, no details +# about Vim error location (v:throwpoint is useless). +# +# 2) PyLog is not enabled. Do not execute this inside try/catch/endtry. +# Python traceback is not printed if we do. +# + + +def printTraceback(bln1,bln2): #{{{2 + """Print traceback from exception caught during Voomexec.""" + out = None + # like traceback.format_exc(), traceback.print_exc() + try: + etype, value, tb = sys.exc_info() + out = traceback.format_exception(etype, value, tb) + #out = traceback.format_exception(etype, value, tb.tb_next) + finally: + etype = value = tb = None + if not out: + sys.stderr.write('ERROR: Voomexec failed to format Python traceback') + return + info = ' ...exception executing script (%s-%s)...\n' %(bln1,bln2) + if bln1==1: + info += ' ...subtract 1 from traceback lnums to get buffer lnums...\n' + out[1:2] = [info] + #out[1:1] = [info] + sys.stderr.write(''.join(out)) + + +#---LOG BUFFER--------------------------------{{{1 +# +class LogBufferClass: #{{{2 + """A file-like object for replacing sys.stdout and sys.stdin with a Vim buffer.""" + def __init__(self): #{{{3 + self.buffer = vim.current.buffer + self.logbnr = vim.eval('bufnr("")') + self.buffer[0] = 'Python Log buffer ...' + #self.encoding = vim.eval('&enc') + self.encoding = get_vim_encoding() + self.join = False + + def write(self,s): #{{{3 + """Append string to buffer, scroll Log windows in all tabs.""" + # Messages are terminated by sending '\n' (null string? ^@). + # Thus "print '\n'" sends '\n' twice. + # The message itself can contain '\n's. + # One line can be sent in many strings which don't always end with \n. + # This is certainly true for Python errors and for 'print a, b, ...' . + + # Can't append unicode strings. This produces an error: + # :py vim.current.buffer.append(u'test') + + # Can't have '\n' in appended list items, so always use splitlines(). + # A trailing \n is lost after splitlines(), but not for '\n\n' etc. + #print self.buffer.name + + if not s: return + # Nasty things happen when printing to unloaded PyLog buffer. + # This also catches printing to noexisting buffer, as in pydoc help() glitch. + if vim.eval("bufloaded(%s)" %self.logbnr)=='0': + vim.command("echoerr 'VOoM (PyLog): PyLog buffer %s is unloaded or doesn''t exist'" %self.logbnr) + vim.command("echoerr 'VOoM (PyLog): unable to write string:'") + vim.command("echom '%s'" %(repr(s).replace("'", "''")) ) + vim.command("echoerr 'VOoM (PyLog): please try executing command Voomlog to fix'") + return + + try: + if type(s) == type(u" "): + s = s.encode(self.encoding) + + # Join with previous message if it had no ending newline. + if self.join==True: + s = self.buffer[-1] + s + del self.buffer[-1] + + if s[-1]=='\n': + self.join = False + else: + self.join = True + + self.buffer.append(s.splitlines()) + except: + # list of all exception lines, no newlines in items + exc_lines = traceback.format_exc().splitlines() + self.buffer.append('') + self.buffer.append('VOoM: exception writing to PyLog buffer:') + self.buffer.append(repr(s)) + self.buffer.append(exc_lines) + self.buffer.append('') + + vim.command('call Voom_LogScroll()') + + +#---misc--------------------------------------{{{1 + +def get_vim_encoding(): #{{{2 + """Return Vim internal encoding.""" + # When &enc is any Unicode Vim allegedly uses utf-8 internally. + # See |encoding|, mbyte.c, values are from |encoding-values| + enc = vim.eval('&enc') + if enc in ('utf-8','ucs-2','ucs-2le','utf-16','utf-16le','ucs-4','ucs-4le'): + return 'utf-8' + return enc + + +# modelines {{{1 +# vim:fdm=marker:fdl=0: +# vim:foldtext=getline(v\:foldstart).'...'.(v\:foldend-v\:foldstart): diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_asciidoc.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_asciidoc.py new file mode 100644 index 00000000..9a7b3e60 --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_asciidoc.py @@ -0,0 +1,436 @@ +# voom_mode_asciidoc.py +# Last Modified: 2012-04-02 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode for AsciiDoc document and section titles. +See |voom_mode_asciidoc|, ../../doc/voom.txt#*voom_mode_asciidoc* +""" + +### NOTES +# +# When outline operation changes level, it has to deal with two ambiguities: +# a) Level 1-5 headline can use 2-style (underline) or 1-style (=). +# b) 1-style can have or not have closing ='s. +# To determine current preferences: check first headline at level <6 and check +# first headline with =. This must be done in hook_makeOutline(). +# (Save in VO, similar to reST mode.) Cannot be done during outline operation, +# that is in hook_doBodyAfterOop(). +# Defaults: use underline, use closing ='s. + +try: + import vim + if vim.eval('exists("g:voom_asciidoc_do_blanks")')=='1' and vim.eval("g:voom_asciidoc_do_blanks")=='0': + DO_BLANKS = False + else: + DO_BLANKS = True +except ImportError: + DO_BLANKS = True + +import re +# regex for 1-style headline, assumes there is no trailing whitespace +HEAD_MATCH = re.compile(r'^(=+)(\s+\S.*?)(\s+\1)?$').match + +# underline chars +ADS_LEVELS = {'=':1, '-':2, '~':3, '^':4, '+':5} +LEVELS_ADS = {1:'=', 2:'-', 3:'~', 4:'^', 5:'+'} + +# DelimitedBlock chars, headines are ignored inside such blocks +BLOCK_CHARS = {'/':0, '+':0, '-':0, '.':0, '*':0, '_':0, '=':0} + +# Combine all signficant chars. Need one of these at start of line for a +# headline or DelimitedBlock to occur. +CHARS = {} +for k in ADS_LEVELS: + CHARS[k] = 0 +for k in BLOCK_CHARS: + CHARS[k] = 0 + + +def hook_makeOutline(VO, blines): + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + ENC = VO.enc + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + + # trailing whitespace is always removed with rstrip() + # if headline is precedeed by [AAA] and/or [[AAA]], bnode is set to their lnum + # + # 1-style, overides 2-style + # [[AAA]] L3, blines[i-2] + # [yyy] L2, blines[i-1] + # == head == L1, blines[i] -- current line, closing = are optional + # + # 2-style (underline) + # [[AAA]] L4, blines[i-3] + # [yyy] L3, blines[i-2] + # head L2, blines[i-1] -- title line, many restrictions on the format + # ---- L1, blines[i] -- current line + + + # Set this the first time a headline with level 1-5 is encountered. + # 0 or 1 -- False, use 2-style (default); 2 -- True, use 1-style + useOne = 0 + # Set this the first time headline in 1-style is encountered. + # 0 or 1 -- True, use closing ='s (default); 2 -- False, do not use closing ='s + useOneClose = 0 + + gotHead = False + inBlock = False # True if inside DelimitedBlock, the value is the char + headI = -2 # idx of the last line that is part of a headline + blockI = -2 # idx of the last line where a DelimitedBlock ended + m = None # match object for 1-style regex + + for i in xrange(Z): + L1 = blines[i].rstrip() + if not L1 or not L1[0] in CHARS: + continue + ch = L1[0] + + if inBlock: + if inBlock==ch and len(L1)>3 and L1.lstrip(ch)=='': + inBlock = False + blockI = i + continue + + # 1-style headline + if ch == '=' and L1.strip('='): + m = HEAD_MATCH(L1) + if m: + gotHead = True + headI_ = headI + headI = i + lev = len(m.group(1)) + head = m.group(2).strip() + bnode = i+1 + + # current line is an underline + # the previous, underlined line (L2) is not a headline if it: + # is not exactly the length of underline +/- 2 + # is already part of in the previous headline + # looks like an underline or a delimited block line + # is [[AAA]] or [AAA] (BlockID or Attribute List) + # starts with . (Block Title, they have no level) + # starts with // (comment line) + # starts with tab (don't know why, spaces are ok) + # is only 1 chars (avoids confusion with --, as in Vim syntax, not as in AsciiDoc) + if not gotHead and ch in ADS_LEVELS and L1.lstrip(ch)=='' and i > 0: + L2 = blines[i-1].rstrip() + z2 = len(L2.decode(ENC,'replace')) + z1 = len(L1) + if (L2 and + (-3 < z2 - z1 < 3) and z1 > 1 and z2 > 1 and + headI != i-1 and + not ((L2[0] in CHARS) and L2.lstrip(L2[0])=='') and + not (L2.startswith('[') and L2.endswith(']')) and + not L2.startswith('.') and + not L2.startswith('\t') and + not (L2.startswith('//') and not L2.startswith('///')) + ): + gotHead = True + headI_ = headI + headI = i + lev = ADS_LEVELS[ch] + head = L2.strip() + bnode = i # lnum of previous line (L2) + + if gotHead and bnode > 1: + # decrement bnode if preceding lines are [[AAA]] or [AAA] lines + # that is set bnode to the topmost [[AAA]] or [AAA] line number + j_ = bnode-2 # idx of line before the title line + L3 = blines[bnode-2].rstrip() + while L3.startswith('[') and L3.endswith(']'): + bnode -= 1 + if bnode > 1: + L3 = blines[bnode-2].rstrip() + else: + break + + # headline must be preceded by a blank line unless: + # it's line 1 (j == -1) + # headline is preceded by [AAA] or [[AAA]] lines (j != j_) + # previous line is a headline (headI_ == j) + # previous line is the end of a DelimitedBlock (blockI == j) + j = bnode-2 + if DO_BLANKS and j==j_ and j > -1: + L3 = blines[j].rstrip() + if L3 and headI_ != j and blockI != j: + # skip over any adjacent comment lines + while L3.startswith('//') and not L3.startswith('///'): + j -= 1 + if j > -1: + L3 = blines[j].rstrip() + else: + L3 = '' + if L3 and headI_ != j and blockI != j: + gotHead = False + headI = headI_ + + # start of DelimitedBlock + if not gotHead and ch in BLOCK_CHARS and len(L1)>3 and L1.lstrip(ch)=='': + inBlock = ch + continue + + if gotHead: + gotHead = False + # save style info for first headline and first 1-style headline + if not useOne and lev < 6: + if m: + useOne = 2 + else: + useOne = 1 + if not useOneClose and m: + if m.group(3): + useOneClose = 1 + else: + useOneClose = 2 + # make outline + tline = ' %s|%s' %('. '*(lev-1), head) + tlines_add(tline) + bnodes_add(bnode) + levels_add(lev) + + # don't clobber these when parsing clipboard during Paste + # which is the only time blines is not Body + if blines is VO.Body: + VO.useOne = useOne == 2 + VO.useOneClose = useOneClose < 2 + + return (tlines, bnodes, levels) + + +def hook_newHeadline(VO, level, blnum, tlnum): + """Return (tree_head, bodyLines). + tree_head is new headline string in Tree buffer (text after |). + bodyLines is list of lines to insert in Body buffer. + """ + tree_head = 'NewHeadline' + if level < 6 and not VO.useOne: + bodyLines = [tree_head, LEVELS_ADS[level]*11, ''] + else: + lev = '='*level + if VO.useOneClose: + bodyLines = ['%s %s %s' %(lev, tree_head, lev), ''] + else: + bodyLines = ['%s %s' %(lev, tree_head), ''] + + # Add blank line when inserting after non-blank Body line. + if VO.Body[blnum-1].strip(): + bodyLines[0:0] = [''] + + return (tree_head, bodyLines) + + +#def hook_changeLevBodyHead(VO, h, levDelta): +# DO NOT CREATE THIS HOOK + + +def hook_doBodyAfterOop(VO, oop, levDelta, blnum1, tlnum1, blnum2, tlnum2, blnumCut, tlnumCut): + # this is instead of hook_changeLevBodyHead() + + # Based on Markdown mode function. + # Inserts blank separator lines if missing. + + #print oop, levDelta, blnum1, tlnum1, blnum2, tlnum2, tlnumCut, blnumCut + Body = VO.Body + Z = len(Body) + bnodes, levels = VO.bnodes, VO.levels + ENC = VO.enc + + # blnum1 blnum2 is first and last lnums of Body region pasted, inserted + # during up/down, or promoted/demoted. + if blnum1: + assert blnum1 == bnodes[tlnum1-1] + if tlnum2 < len(bnodes): + assert blnum2 == bnodes[tlnum2]-1 + else: + assert blnum2 == Z + + # blnumCut is Body lnum after which a region was removed during 'cut', + # 'up', 'down'. Need this to check if there is blank line between nodes + # used to be separated by the cut/moved region. + if blnumCut: + if tlnumCut < len(bnodes): + assert blnumCut == bnodes[tlnumCut]-1 + else: + assert blnumCut == Z + + # Total number of added lines minus number of deleted lines. + b_delta = 0 + + ### After 'cut' or 'up': insert blank line if there is none + # between the nodes used to be separated by the cut/moved region. + if DO_BLANKS and (oop=='cut' or oop=='up') and (0 < blnumCut < Z) and Body[blnumCut-1].strip(): + Body[blnumCut:blnumCut] = [''] + update_bnodes(VO, tlnumCut+1 ,1) + b_delta+=1 + + if oop=='cut': + return + + ### Make sure there is blank line after the last node in the region: + # insert blank line after blnum2 if blnum2 is not blank, that is insert + # blank line before bnode at tlnum2+1. + if DO_BLANKS and blnum2 < Z and Body[blnum2-1].strip(): + Body[blnum2:blnum2] = [''] + update_bnodes(VO, tlnum2+1 ,1) + b_delta+=1 + + ### Change levels and/or formats of headlines in the affected region. + # Always do this after Paste, even if level is unchanged -- format can + # be different when pasting from other outlines. + # Examine each headline, from bottom to top, and change level and/or format. + # To change from 1-style to 2-style: + # strip ='s, strip whitespace; + # insert underline. + # To change from 2-style to 1-style: + # delete underline; + # insert ='s. + # Update bnodes after inserting or deleting a line. + # + # NOTE: bnode can be [[AAA]] or [AAA] line, we check for that and adjust it + # to point to the headline text line + # + # 1-style 2-style + # + # L0 L0 Body[bln-2] + # == head L1 head L1 <--bnode Body[bln-1] (not always the actual bnode) + # L2 ---- L2 Body[bln] + # L3 L3 Body[bln+1] + + if levDelta or oop=='paste': + for i in xrange(tlnum2, tlnum1-1, -1): + # required level (VO.levels has been updated) + lev = levels[i-1] + # current level from which to change to lev + lev_ = lev - levDelta + + # Body headline (bnode) and the next line + bln = bnodes[i-1] + L1 = Body[bln-1].rstrip() + # bnode can point to the tompost [AAA] or [[AAA]] line + # increment bln until the actual headline (title line) is found + while L1.startswith('[') and L1.endswith(']'): + bln += 1 + L1 = Body[bln-1].rstrip() + # the underline line + if bln+1 < len(Body): + L2 = Body[bln].rstrip() + else: + L2 = '' + + # get current headline format + hasOne, hasOneClose = False, VO.useOneClose + theHead = L1 + if L1.startswith('='): + m = HEAD_MATCH(L1) + if m: + hasOne = True + # headline without ='s but with whitespace around it preserved + theHead = m.group(2) + theclose = m.group(3) + if theclose: + hasOneClose = True + theHead += theclose.rstrip('=') + else: + hasOneClose = False + + # get desired headline format + if oop=='paste': + if lev > 5: + useOne = True + else: + useOne = VO.useOne + useOneClose = VO.useOneClose + elif lev < 6 and lev_ < 6: + useOne = hasOne + useOneClose = hasOneClose + elif lev > 5 and lev_ > 5: + useOne = True + useOneClose = hasOneClose + elif lev < 6 and lev_ > 5: + useOne = VO.useOne + useOneClose = VO.useOneClose + elif lev > 5 and lev_ < 6: + useOne = True + useOneClose = hasOneClose + else: + assert False + #print useOne, hasOne, ';', useOneClose, hasOneClose + + ### change headline level and/or format + # 2-style unchanged, only adjust level of underline + if not useOne and not hasOne: + if not levDelta: continue + Body[bln] = LEVELS_ADS[lev]*len(L2) + # 1-style unchanged, adjust level of ='s and add/remove closing ='s + elif useOne and hasOne: + # no format change, there are closing ='s + if useOneClose and hasOneClose: + if not levDelta: continue + Body[bln-1] = '%s%s%s' %('='*lev, theHead, '='*lev) + # no format change, there are no closing ='s + elif not useOneClose and not hasOneClose: + if not levDelta: continue + Body[bln-1] = '%s%s' %('='*lev, theHead) + # add closing ='s + elif useOneClose and not hasOneClose: + Body[bln-1] = '%s%s %s' %('='*lev, theHead.rstrip(), '='*lev) + # remove closing ='s + elif not useOneClose and hasOneClose: + Body[bln-1] = '%s%s' %('='*lev, theHead.rstrip()) + # insert underline, remove ='s + elif not useOne and hasOne: + L1 = theHead.strip() + Body[bln-1] = L1 + # insert underline + Body[bln:bln] = [LEVELS_ADS[lev]*len(L1.decode(ENC,'replace'))] + update_bnodes(VO, i+1, 1) + b_delta+=1 + # remove underline, insert ='s + elif useOne and not hasOne: + if useOneClose: + Body[bln-1] = '%s %s %s' %('='*lev, theHead.strip(), '='*lev) + else: + Body[bln-1] = '%s %s' %('='*lev, theHead.strip()) + # delete underline + Body[bln:bln+1] = [] + update_bnodes(VO, i+1, -1) + b_delta-=1 + + ### Make sure first headline is preceded by a blank line. + blnum1 = bnodes[tlnum1-1] + if DO_BLANKS and blnum1 > 1 and Body[blnum1-2].strip(): + Body[blnum1-1:blnum1-1] = [''] + update_bnodes(VO, tlnum1 ,1) + b_delta+=1 + + ### After 'down' : insert blank line if there is none + # between the nodes used to be separated by the moved region. + if DO_BLANKS and oop=='down' and (0 < blnumCut < Z) and Body[blnumCut-1].strip(): + Body[blnumCut:blnumCut] = [''] + update_bnodes(VO, tlnumCut+1 ,1) + b_delta+=1 + + assert len(Body) == Z + b_delta + + +def update_bnodes(VO, tlnum, delta): + """Update VO.bnodes by adding/substracting delta to each bnode + starting with bnode at tlnum and to the end. + """ + bnodes = VO.bnodes + for i in xrange(tlnum, len(bnodes)+1): + bnodes[i-1] += delta + + diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_cwiki.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_cwiki.py new file mode 100644 index 00000000..cb5a1321 --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_cwiki.py @@ -0,0 +1,72 @@ +# voom_mode_cwiki.py +# Last Modified: 2011-10-30 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode for cwiki Vim plugin. Contributed by Craig B. Allen. +http://www.vim.org/scripts/script.php?script_id=2176 +See |voom_mode_various|, ../../doc/voom.txt#*voom_mode_various* + ++++ headline level 1 +some text +++++ headline level 2 +more text ++++++ headline level 3 +++++++ headline level 4 +etc. + +First + must be at start of line. Whitespace after the last + is optional. +""" + +import re +headline_match = re.compile(r'^\+\+(\++)').match + + +def hook_makeOutline(VO, blines): + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + for i in xrange(Z): + if not blines[i].startswith('+'): + continue + bline = blines[i] + m = headline_match(bline) + if not m: + continue + lev = len(m.group(1)) + head = bline[2+lev:].strip() + tline = ' %s|%s' %('. '*(lev-1), head) + tlines_add(tline) + bnodes_add(i+1) + levels_add(lev) + return (tlines, bnodes, levels) + + +def hook_newHeadline(VO, level, blnum, tlnum): + """Return (tree_head, bodyLines). + tree_head is new headline string in Tree buffer (text after |). + bodyLines is list of lines to insert in Body buffer. + """ + tree_head = 'NewHeadline' + bodyLines = ['++%s %s' %('+'*level, tree_head), ''] + return (tree_head, bodyLines) + + +def hook_changeLevBodyHead(VO, h, levDelta): + """Increase of decrease level number of Body headline by levDelta.""" + if levDelta==0: return h + m = headline_match(h) + level = len(m.group(1)) + return '++%s%s' %('+'*(level+levDelta), h[m.end(1):]) + + diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_fmr.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_fmr.py new file mode 100644 index 00000000..0aee76b3 --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_fmr.py @@ -0,0 +1,18 @@ +# voom_mode_fmr1.py +# Last Modified: 2012-02-25 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +This mode changes absolutely nothing, it is identical to the default mode. +See |voom_mode_fmr|, ../../doc/voom.txt#*voom_mode_fmr* +""" + +# Define this mode as an 'fmr' mode. +MODE_FMR = True diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_fmr1.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_fmr1.py new file mode 100644 index 00000000..c66057df --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_fmr1.py @@ -0,0 +1,63 @@ +# voom_mode_fmr1.py +# Last Modified: 2012-02-25 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode for start fold markers with levels. +Similar to the default mode, that is the :Voom command. +See |voom_mode_fmr|, ../../doc/voom.txt#*voom_mode_fmr* + +headline level 1 {{{1 +some text +headline level 2 {{{2 +more text +""" + +# Define this mode as an 'fmr' mode. +MODE_FMR = True + +# voom.makeoutline() without char stripping +def hook_makeOutline(VO, blines): + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + marker = VO.marker + marker_re_search = VO.marker_re.search + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + #c = VO.rstrip_chars + for i in xrange(Z): + if not marker in blines[i]: continue + bline = blines[i] + m = marker_re_search(bline) + if not m: continue + lev = int(m.group(1)) + #head = bline[:m.start()].lstrip().rstrip(c).strip('-=~').strip() + head = bline[:m.start()].strip() + tline = ' %s%s|%s' %(m.group(2) or ' ', '. '*(lev-1), head) + tlines_add(tline) + bnodes_add(i+1) + levels_add(lev) + return (tlines, bnodes, levels) + + +# same as voom.newHeadline() but without --- +def hook_newHeadline(VO, level, blnum, ln): + """Return (tree_head, bodyLines). + tree_head is new headline string in Tree buffer (text after |). + bodyLines is list of lines to insert in Body buffer. + """ + tree_head = 'NewHeadline' + #bodyLines = ['---%s--- %s%s' %(tree_head, VO.marker, level), ''] + bodyLines = ['%s %s%s' %(tree_head, VO.marker, level), ''] + return (tree_head, bodyLines) + + diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_fmr2.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_fmr2.py new file mode 100644 index 00000000..9b1ccb55 --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_fmr2.py @@ -0,0 +1,63 @@ +# voom_mode_fmr2.py +# Last Modified: 2012-02-04 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode. Headline text is after the start fold marker with level. +See |voom_mode_fmr|, ../../doc/voom.txt#*voom_mode_fmr* + +{{{1 headline level 1 +some text +{{{2 headline level 2 +more text +""" + +# Define this mode as an 'fmr' mode. +MODE_FMR = True + + +def hook_makeOutline(VO, blines): + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + marker = VO.marker + marker_re_search = VO.marker_re.search + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + #c = VO.rstrip_chars + for i in xrange(Z): + if not marker in blines[i]: continue + bline = blines[i] + m = marker_re_search(bline) + if not m: continue + lev = int(m.group(1)) + #head = bline[:m.start()].lstrip().rstrip(c).strip('-=~').strip() + head = bline[m.end():] + # strip special marks o= + if head and head[0]=='o': head = head[1:] + if head and head[0]=='=': head = head[1:] + tline = ' %s%s|%s' %(m.group(2) or ' ', '. '*(lev-1), head.strip()) + tlines_add(tline) + bnodes_add(i+1) + levels_add(lev) + return (tlines, bnodes, levels) + + +def hook_newHeadline(VO, level, blnum, ln): + """Return (tree_head, bodyLines). + tree_head is new headline string in Tree buffer (text after |). + bodyLines is list of lines to insert in Body buffer. + """ + tree_head = 'NewHeadline' + bodyLines = ['%s%s %s' %(VO.marker, level, tree_head), ''] + return (tree_head, bodyLines) + + diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_hashes.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_hashes.py new file mode 100644 index 00000000..557a8c6c --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_hashes.py @@ -0,0 +1,74 @@ +# voom_mode_hashes.py +# Last Modified: 2012-05-06 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode for headlines marked with #'s (atx-headers, a subset of Markdown format). +See |voom_mode_hashes|, ../../doc/voom.txt#*voom_mode_hashes* + +# heading level 1 +##heading level 2 +### heading level 3 +""" + +import re + +# Marker character can be changed to any ASCII character. +CH = '#' + +# Use this if whitespace after marker chars is optional. +headline_match = re.compile(r'^(%s+)' %re.escape(CH)).match +# Use this if a whitespace is required after marker chars (as in org-mode). +#headline_match = re.compile(r'^(%s+)\s' %re.escape(CH)).match + + +def hook_makeOutline(VO, blines): + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + for i in xrange(Z): + if not blines[i].startswith(CH): + continue + bline = blines[i] + m = headline_match(bline) + if not m: + continue + lev = len(m.group(1)) + head = bline[lev:].strip() + # Do this instead if optional closing markers need to be stripped. + #head = bline[lev:].strip().rstrip(CH).rstrip() + tline = ' %s|%s' %('. '*(lev-1), head) + tlines_add(tline) + bnodes_add(i+1) + levels_add(lev) + return (tlines, bnodes, levels) + + +def hook_newHeadline(VO, level, blnum, tlnum): + """Return (tree_head, bodyLines). + tree_head is new headline string in Tree buffer (text after |). + bodyLines is list of lines to insert in Body buffer. + """ + tree_head = 'NewHeadline' + bodyLines = ['%s %s' %(CH * level, tree_head), ''] + return (tree_head, bodyLines) + + +def hook_changeLevBodyHead(VO, h, levDelta): + """Increase of decrease level number of Body headline by levDelta.""" + if levDelta==0: return h + m = headline_match(h) + level = len(m.group(1)) + return '%s%s' %(CH * (level+levDelta), h[m.end(1):]) + + diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_html.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_html.py new file mode 100644 index 00000000..d825bb6f --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_html.py @@ -0,0 +1,73 @@ +# voom_mode_html.py +# Last Modified: 2011-05-01 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode for HTML headings. +See |voom_mode_html|, ../../doc/voom.txt#*voom_mode_html* + +

headline level 1

+some text +

headline level 2

+more text +

headline level 3

+ < h4 > headline level 4 + some text

headline 5

+ etc. +""" + +import re +headline_search = re.compile(r'<\s*h(\d+).*?>(.*?)', re.IGNORECASE).search +html_tag_sub = re.compile('<.*?>').sub + + +def hook_makeOutline(VO, blines): + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + for i in xrange(Z): + bline = blines[i] + if not ('%s' %(level, tree_head, level), ''] + return (tree_head, bodyLines) + + +def hook_changeLevBodyHead(VO, h, levDelta): + """Increase of decrease level number of Body headline by levDelta.""" + if levDelta==0: return h + m = headline_search(h) + level = int(m.group(1)) + lev = level+levDelta + return '%s%s%s%s%s' %(h[:m.start(1)], lev, h[m.end(1):m.start(3)], lev, h[m.end(3):]) + diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_markdown.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_markdown.py new file mode 100644 index 00000000..71d64a18 --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_markdown.py @@ -0,0 +1,318 @@ +# voom_mode_markdown.py +# Last Modified: 2012-04-02 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode for Markdown headers. +See |voom_mode_markdown|, ../../doc/voom.txt#*voom_mode_markdown* +""" + +### NOTES +# When outline operation changes level, it has to deal with two ambiguities: +# a) Level 1 and 2 headline can use underline-style or hashes-style. +# b) Hashes-style can have or not have closing hashes. +# To determine current preferences: check first headline at level <3 and check +# first headline with hashes. This must be done in hook_makeOutline(). +# (Save in VO, similar to reST mode.) Cannot be done during outline operation, +# that is in hook_doBodyAfterOop(). +# Defaults: use underline, use closing hashes. + + +levels_ads = {1:'=', 2:'-'} + + +def hook_makeOutline(VO, blines): + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + + # trailing whitespace is always removed with rstrip() + # + # hashes-style, overides underline-style + # abcde L2, blines[i-1] + # ## head L1, blines[i] -- current line + # + # underline-style + # head L2, blines[i-1] -- title line, not blank, does not start with # + # ------ L1, blines[i] -- current line, any number of = or - only + + L1, L2 = '','' + + # Set this once when headline with level 1 or 2 is encountered. + # 0 or 1 -- False, use underline-style (default); 2 -- True, use hashes-style + useHash = 0 + # Set this once when headline with hashes is encountered. + # 0 or 1 -- True, use closing hashes (default); 2 -- False, do not use closing hashes + useCloseHash = 0 + + gotHead = False + for i in xrange(Z): + L2 = L1 + L1 = blines[i].rstrip() + + if L1.startswith('#'): + gotHead = True + lev = len(L1) - len(L1.lstrip('#')) + bnode = i+1 + head = L1.strip('#').strip() + elif L2 and L1.startswith('=') and L1.lstrip('=')=='': + gotHead = True + lev = 1 + head = L2.strip() + bnode = i + elif L2 and L1.startswith('-') and L1.lstrip('-')=='': + gotHead = True + lev = 2 + head = L2.strip() + bnode = i + else: + continue + + if gotHead: + gotHead = False + if not useHash and lev < 3: + if L1.startswith('#'): + useHash = 2 + else: + useHash = 1 + if not useCloseHash and L1.startswith('#'): + if L1.endswith('#'): + useCloseHash = 1 + else: + useCloseHash = 2 + L1, L2 = '','' + + tline = ' %s|%s' %('. '*(lev-1), head) + tlines_add(tline) + bnodes_add(bnode) + levels_add(lev) + + # don't clobber these when parsing clipboard during Paste + # which is the only time blines is not Body + if blines is VO.Body: + VO.useHash = useHash == 2 + VO.useCloseHash = useCloseHash < 2 + + return (tlines, bnodes, levels) + + +def hook_newHeadline(VO, level, blnum, tlnum): + """Return (tree_head, bodyLines). + tree_head is new headline string in Tree buffer (text after |). + bodyLines is list of lines to insert in Body buffer. + """ + tree_head = 'NewHeadline' + if level < 3 and not VO.useHash: + bodyLines = [tree_head, levels_ads[level]*11, ''] + else: + lev = '#'*level + if VO.useCloseHash: + bodyLines = ['%s %s %s' %(lev, tree_head, lev), ''] + else: + bodyLines = ['%s %s' %(lev, tree_head), ''] + + # Add blank line when inserting after non-blank Body line. + if VO.Body[blnum-1].strip(): + bodyLines[0:0] = [''] + + return (tree_head, bodyLines) + + +#def hook_changeLevBodyHead(VO, h, levDelta): +# DO NOT CREATE THIS HOOK + + +def hook_doBodyAfterOop(VO, oop, levDelta, blnum1, tlnum1, blnum2, tlnum2, blnumCut, tlnumCut): + # this is instead of hook_changeLevBodyHead() + + # Based on reST mode function. Insert blank separator lines if missing, + # even though they are not important for Markdown headlines. + + #print oop, levDelta, blnum1, tlnum1, blnum2, tlnum2, tlnumCut, blnumCut + Body = VO.Body + Z = len(Body) + bnodes, levels = VO.bnodes, VO.levels + ENC = VO.enc + + # blnum1 blnum2 is first and last lnums of Body region pasted, inserted + # during up/down, or promoted/demoted. + if blnum1: + assert blnum1 == bnodes[tlnum1-1] + if tlnum2 < len(bnodes): + assert blnum2 == bnodes[tlnum2]-1 + else: + assert blnum2 == Z + + # blnumCut is Body lnum after which a region was removed during 'cut', + # 'up', 'down'. Need this to check if there is blank line between nodes + # used to be separated by the cut/moved region. + if blnumCut: + if tlnumCut < len(bnodes): + assert blnumCut == bnodes[tlnumCut]-1 + else: + assert blnumCut == Z + + # Total number of added lines minus number of deleted lines. + b_delta = 0 + + ### After 'cut' or 'up': insert blank line if there is none + # between the nodes used to be separated by the cut/moved region. + if (oop=='cut' or oop=='up') and (0 < blnumCut < Z) and Body[blnumCut-1].strip(): + Body[blnumCut:blnumCut] = [''] + update_bnodes(VO, tlnumCut+1 ,1) + b_delta+=1 + + if oop=='cut': + return + + ### Make sure there is blank line after the last node in the region: + # insert blank line after blnum2 if blnum2 is not blank, that is insert + # blank line before bnode at tlnum2+1. + if blnum2 < Z and Body[blnum2-1].strip(): + Body[blnum2:blnum2] = [''] + update_bnodes(VO, tlnum2+1 ,1) + b_delta+=1 + + ### Change levels and/or formats of headlines in the affected region. + # Always do this after Paste, even if level is unchanged -- format can + # be different when pasting from other outlines. + # Examine each headline, from bottom to top, and change level and/or format. + # To change from hashes to underline-style: + # strip hashes, strip whitespace; + # insert underline. + # To change from underline to hashes-style: + # delete underline; + # insert hashes. + # Update bnodes after inserting or deleting a line. + + # hash-style underline-style + # + # L0 L0 Body[bln-2] + # ## head L1 head L1 <--bnode Body[bln-1] + # L2 ---- L2 Body[bln] + # L3 L3 Body[bln+1] + + if levDelta or oop=='paste': + for i in xrange(tlnum2, tlnum1-1, -1): + # required level (VO.levels has been updated) + lev = levels[i-1] + # current level from which to change to lev + lev_ = lev - levDelta + + # Body headline (bnode) and next line + bln = bnodes[i-1] + L1 = Body[bln-1].rstrip() + if bln+1 < len(Body): + L2 = Body[bln].rstrip() + else: + L2 = '' + + # get current headline format + hasHash, hasCloseHash = False, VO.useCloseHash + if L1.startswith('#'): + hasHash = True + if L1.endswith('#'): + hasCloseHash = True + else: + hasCloseHash = False + + # get desired headline format + if oop=='paste': + if lev > 2: + useHash = True + else: + useHash = VO.useHash + useCloseHash = VO.useCloseHash + elif lev < 3 and lev_ < 3: + useHash = hasHash + useCloseHash = hasCloseHash + elif lev > 2 and lev_ > 2: + useHash = True + useCloseHash = hasCloseHash + elif lev < 3 and lev_ > 2: + useHash = VO.useHash + useCloseHash = VO.useCloseHash + elif lev > 2 and lev_ < 3: + useHash = True + useCloseHash = hasCloseHash + else: + assert False + #print useHash, hasHash, ';', useCloseHash, hasCloseHash + + # change headline level and/or format + + # underline-style unchanged, only adjust level of underline + if not useHash and not hasHash: + if not levDelta: continue + Body[bln] = levels_ads[lev]*len(L2) + # hashes-style unchanged, adjust level of hashes and add/remove closing hashes + elif useHash and hasHash: + # no format change, there are closing hashes + if useCloseHash and hasCloseHash: + if not levDelta: continue + Body[bln-1] = '%s%s%s' %('#'*lev, L1.strip('#'), '#'*lev) + # no format change, there are no closing hashes + elif not useCloseHash and not hasCloseHash: + if not levDelta: continue + Body[bln-1] = '%s%s' %('#'*lev, L1.lstrip('#')) + # add closing hashes + elif useCloseHash and not hasCloseHash: + Body[bln-1] = '%s%s %s' %('#'*lev, L1.strip('#').rstrip(), '#'*lev) + # remove closing hashes + elif not useCloseHash and hasCloseHash: + Body[bln-1] = '%s%s' %('#'*lev, L1.strip('#').rstrip()) + # insert underline, remove hashes + elif not useHash and hasHash: + L1 = L1.strip('#').strip() + Body[bln-1] = L1 + # insert underline + Body[bln:bln] = [levels_ads[lev]*len(L1.decode(ENC,'replace'))] + update_bnodes(VO, i+1, 1) + b_delta+=1 + # remove underline, insert hashes + elif useHash and not hasHash: + if useCloseHash: + Body[bln-1] = '%s %s %s' %('#'*lev, L1.strip('#').strip(), '#'*lev) + else: + Body[bln-1] = '%s %s' %('#'*lev, L1.strip('#').strip()) + # delete underline + Body[bln:bln+1] = [] + update_bnodes(VO, i+1, -1) + b_delta-=1 + + ### Make sure first headline is preceded by a blank line. + blnum1 = bnodes[tlnum1-1] + if blnum1 > 1 and Body[blnum1-2].strip(): + Body[blnum1-1:blnum1-1] = [''] + update_bnodes(VO, tlnum1 ,1) + b_delta+=1 + + ### After 'down' : insert blank line if there is none + # between the nodes used to be separated by the moved region. + if oop=='down' and (0 < blnumCut < Z) and Body[blnumCut-1].strip(): + Body[blnumCut:blnumCut] = [''] + update_bnodes(VO, tlnumCut+1 ,1) + b_delta+=1 + + assert len(Body) == Z + b_delta + + +def update_bnodes(VO, tlnum, delta): + """Update VO.bnodes by adding/substracting delta to each bnode + starting with bnode at tlnum and to the end. + """ + bnodes = VO.bnodes + for i in xrange(tlnum, len(bnodes)+1): + bnodes[i-1] += delta + + diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_org.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_org.py new file mode 100644 index 00000000..c1a331bd --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_org.py @@ -0,0 +1,61 @@ +# voom_mode_org.py +# Last Modified: 2011-10-28 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode for Emacs Org-mode headline format. +See |voom_mode_org|, ../../doc/voom.txt#*voom_mode_org* +""" + +import re +headline_match = re.compile(r'^(\*+)\s').match + + +def hook_makeOutline(VO, blines): + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + for i in xrange(Z): + if not blines[i].startswith('*'): + continue + bline = blines[i] + m = headline_match(bline) + if not m: + continue + lev = len(m.group(1)) + head = bline[lev:].strip() + tline = ' %s|%s' %('. '*(lev-1), head) + tlines_add(tline) + bnodes_add(i+1) + levels_add(lev) + return (tlines, bnodes, levels) + + +def hook_newHeadline(VO, level, blnum, tlnum): + """Return (tree_head, bodyLines). + tree_head is new headline string in Tree buffer (text after |). + bodyLines is list of lines to insert in Body buffer. + """ + tree_head = 'NewHeadline' + bodyLines = ['%s %s' %('*'*level, tree_head), ''] + return (tree_head, bodyLines) + + +def hook_changeLevBodyHead(VO, h, levDelta): + """Increase of decrease level number of Body headline by levDelta.""" + if levDelta==0: return h + m = headline_match(h) + level = len(m.group(1)) + return '%s%s' %('*'*(level+levDelta), h[m.end(1):]) + + diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_python.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_python.py new file mode 100644 index 00000000..21cf7523 --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_python.py @@ -0,0 +1,214 @@ +# voom_mode_python.py +# Last Modified: 2011-05-01 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode for Python code. +See |voom_mode_python|, ../../doc/voom.txt#*voom_mode_python* +""" + +import token, tokenize +import traceback +import vim + + +def hook_makeOutline(VO, blines): + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + + #ignore_lnums, func_lnums = get_lnums_from_tokenize(blines) + try: + ignore_lnums, func_lnums = get_lnums_from_tokenize(blines) + except (IndentationError, tokenize.TokenError): + vim.command("call Voom_ErrorMsg('VOoM: EXCEPTION WHILE PARSING PYTHON OUTLINE')") + # DO NOT print to sys.stderr -- triggers Vim error when default stderr (no PyLog) + #traceback.print_exc() --this goes to sys.stderr + #print traceback.format_exc() --ok but no highlighting + lines = traceback.format_exc().replace("'","''").split('\n') + for l in lines: + vim.command("call Voom_ErrorMsg('%s')" %l) + return (['= |!!!ERROR: OUTLINE IS INVALID'], [1], [1]) + + gotHead = False # True if current line is a headline + indents = [0,] # indents of previous levels + funcLevels = [] # levels of previous def or class + indent_error = '' # inconsistent indent + for i in xrange(Z): + if i+1 in ignore_lnums: continue + bline = blines[i] + bline_s = bline.strip() + if not bline_s: continue + bline_ls = bline.lstrip() + + # compute indent and level + indent = len(bline) - len(bline_ls) + if indent > indents[-1]: + indents.append(indent) + elif indent < indents[-1]: + while indents and (indents[-1] > indent): + indents.pop() + if indents[-1]==indent: + indent_error = '' + else: + indent_error = '!!! ' + lev = len(indents) + + # first line after the end of a class or def block + if funcLevels and lev <= funcLevels[-1]: + gotHead = True + while funcLevels and funcLevels[-1] >= lev: + funcLevels.pop() + # first line of a class or def block + if i+1 in func_lnums: + gotHead = True + if not funcLevels or (lev > funcLevels[-1]): + funcLevels.append(lev) + # special comment line (unconditional headline) or line with @decorator + elif bline_s.startswith('@') or bline_s.startswith('### ') or bline_s.startswith('#---'): + gotHead = True + + if gotHead: + gotHead = False + tline = ' %s|%s%s' %('. '*(lev-1), indent_error, bline_s) + tlines_add(tline) + bnodes_add(i+1) + levels_add(lev) + + return (tlines, bnodes, levels) + + +class BLines: + """Wrapper around Vim buffer object or list of Body lines to provide + readline() method for use with tokenize.generate_tokens(). + """ + def __init__(self, blines): + self.blines = blines + self.size = len(blines) + self.idx = -1 + + def readline(self): + self.idx += 1 + if self.idx == self.size: + return '' + return "%s\n" %self.blines[self.idx] + + +### toktypes of tokens +STRING = token.STRING +NAME = token.NAME +NEWLINE = token.NEWLINE + +def get_lnums_from_tokenize(blines): + """Return dicts. Keys are Body lnums. + The main purpose is to get list of lnums to ignore: multi-line strings and + expressions. + """ + # lnums to ignore: multi-line strings and expressions other than the first line + ignore_lnums = {} + # lnums of 'class' and 'def' tokens + func_lnums = {} + + inName = False + + for tok in tokenize.generate_tokens(BLines(blines).readline): + toktype, toktext, (srow, scol), (erow, ecol), line = tok + #print token.tok_name[toktype], tok + if toktype == NAME: + if not inName: + inName = True + srow_name = srow + if toktext in ('def','class'): + func_lnums[srow] = toktext + elif toktype == NEWLINE and inName: + inName = False + if srow_name != erow: + for i in xrange(srow_name+1, erow+1): + ignore_lnums[i] = 0 + elif toktype == STRING: + if srow != erow: + for i in xrange(srow+1, erow+1): + ignore_lnums[i] = 0 + + return (ignore_lnums, func_lnums) + + +def get_body_indent(body): + """Return string used for indenting Body lines.""" + et = int(vim.eval("getbufvar(%s,'&et')" %body)) + if et: + ts = int(vim.eval("getbufvar(%s,'&ts')" %body)) + return ' '*ts + else: + return '\t' + + +def hook_newHeadline(VO, level, blnum, tlnum): + """Return (tree_head, bodyLines). + tree_head is new headline string in Tree buffer (text after |). + bodyLines is list of lines to insert in Body buffer. + """ + tree_head = '### NewHeadline' + indent = get_body_indent(VO.body) + body_head = '%s%s' %(indent*(level-1), tree_head) + return (tree_head, [body_head]) + + +#def hook_changeLevBodyHead(VO, h, levDelta): + #"""Increase of decrease level number of Body headline by levDelta.""" + #if levDelta==0: return h + + +def hook_doBodyAfterOop(VO, oop, levDelta, blnum1, tlnum1, blnum2, tlnum2, blnumCut, tlnumCut): + # this is instead of hook_changeLevBodyHead() + #print oop, levDelta, blnum1, tlnum1, blnum2, tlnum2, tlnumCut, blnumCut + Body = VO.Body + Z = len(Body) + + ind = get_body_indent(VO.body) + # levDelta is wrong when pasting because hook_makeOutline() looks at relative indent + # determine level of pasted region from indent of its first line + if oop=='paste': + bline1 = Body[blnum1-1] + lev = (len(bline1) - len(bline1.lstrip())) / len(ind) + 1 + levDelta = VO.levels[tlnum1-1] - lev + + if not levDelta: return + + indent = abs(levDelta) * ind + #--- copied from voom_mode_thevimoutliner.py ----------------------------- + if blnum1: + assert blnum1 == VO.bnodes[tlnum1-1] + if tlnum2 < len(VO.bnodes): + assert blnum2 == VO.bnodes[tlnum2]-1 + else: + assert blnum2 == Z + + # dedent (if possible) or indent every non-blank line in Body region blnum1,blnum2 + blines = [] + for i in xrange(blnum1-1,blnum2): + line = Body[i] + if not line.strip(): + blines.append(line) + continue + if levDelta > 0: + line = '%s%s' %(indent,line) + elif levDelta < 0 and line.startswith(indent): + line = line[len(indent):] + blines.append(line) + + # replace Body region + Body[blnum1-1:blnum2] = blines + assert len(Body)==Z + + diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_rest.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_rest.py new file mode 100644 index 00000000..31c60678 --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_rest.py @@ -0,0 +1,369 @@ +# voom_mode_rest.py +# Last Modified: 2012-04-02 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode for reStructuredText. +See |voom_mode_rest|, ../../doc/voom.txt#*voom_mode_rest* + +http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#sections + The following are all valid section title adornment characters: + ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ + + Some characters are more suitable than others. The following are recommended: + = - ` : . ' " ~ ^ _ * + # + +http://docs.python.org/documenting/rest.html#sections +Python recommended styles: ## ** = - ^ " +""" + +# All valid section title adornment characters. +AD_CHARS = """ ! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ """ +AD_CHARS = AD_CHARS.split() + +# List of adornment styles, in order of preference. +# Adornment style (ad) is a char or double char: '=', '==', '-', '--', '*', etc. +# Char is adornment char, double if there is overline. +AD_STYLES = """ == -- = - * " ' ` ~ : ^ + # . _ """ +AD_STYLES = AD_STYLES.split() + +# add all other possible styles to AD_STYLES +d = {}.fromkeys(AD_STYLES) +for c in AD_CHARS: + if not c*2 in d: + AD_STYLES.append(c*2) + if not c in d: + AD_STYLES.append(c) +assert len(AD_STYLES)==64 + +# convert AD_CHARS to dict for faster lookups +AD_CHARS = {}.fromkeys(AD_CHARS) + + +def hook_makeOutline(VO, blines): + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + ENC = VO.enc + + # {adornment style: level, ...} + # Level indicates when the first instance of this style was found. + ads_levels = {} + + # diagram of Body lines when a headline is detected + # trailing whitespace always removed with rstrip() + # a b c + # ------ L3, blines[i-2] -- an overline or blank line + # head L2, blines[i-1] -- title line, not blank, <= than underline, can be inset only if overline + # ------ L1, blines[i] -- current line, always underline + # x y z + L1, L2, L3 = '','','' + + gotHead = False + for i in xrange(Z): + L2, L3 = L1, L2 + L1 = blines[i].rstrip() + # current line must be underline and title line cannot be blank + if not (L1 and L2 and (L1[0] in AD_CHARS) and L1.lstrip(L1[0])==''): + continue + # underline must be as long as headline text + if len(L1) < len(L2.decode(ENC,'replace')): + continue + # there is no overline; L3 must be blank line; L2 must be not inset + if not L3 and len(L2)==len(L2.lstrip()): + #if len(L1) < len(L2.decode(ENC,'replace')): continue + gotHead = True + ad = L1[0] + head = L2.strip() + bnode = i + # there is overline -- bnode is lnum of overline! + elif L3==L1: + #if len(L1) < len(L2.decode(ENC,'replace')): continue + gotHead = True + ad = L1[0]*2 + head = L2.strip() + bnode = i-1 + + if gotHead: + if not ad in ads_levels: + ads_levels[ad] = len(ads_levels)+1 + lev = ads_levels[ad] + gotHead = False + L1, L2, L3 = '','','' + + tline = ' %s|%s' %('. '*(lev-1), head) + tlines_add(tline) + bnodes_add(bnode) + levels_add(lev) + + # save ads_levels for outline operations + # don't clobber VO.ads_levels when parsing clipboard during Paste + # which is the only time blines is not Body + if blines is VO.Body: + VO.ads_levels = ads_levels + + return (tlines, bnodes, levels) + + +def hook_newHeadline(VO, level, blnum, tlnum): + """Return (tree_head, bodyLines). + tree_head is new headline string in Tree buffer (text after |). + bodyLines is list of lines to insert in Body buffer. + """ + tree_head = 'NewHeadline' + ads_levels = VO.ads_levels + levels_ads = dict([[v,k] for k,v in ads_levels.items()]) + + if level in levels_ads: + ad = levels_ads[level] + else: + ad = get_new_ad(levels_ads, ads_levels, level) + + if len(ad)==1: + bodyLines = [tree_head, ad*11, ''] + elif len(ad)==2: + ad = ad[0] + bodyLines = [ad*11, tree_head, ad*11, ''] + + # Add blank line when inserting after non-blank Body line. + if VO.Body[blnum-1].strip(): + bodyLines[0:0] = [''] + + return (tree_head, bodyLines) + + +#def hook_changeLevBodyHead(VO, h, levDelta): +# DO NOT CREATE THIS HOOK + + +def hook_doBodyAfterOop(VO, oop, levDelta, blnum1, tlnum1, blnum2, tlnum2, blnumCut, tlnumCut): + # this is instead of hook_changeLevBodyHead() + #print oop, levDelta, blnum1, tlnum1, blnum2, tlnum2, tlnumCut, blnumCut + Body = VO.Body + Z = len(Body) + bnodes, levels = VO.bnodes, VO.levels + ENC = VO.enc + + # blnum1 blnum2 is first and last lnums of Body region pasted, inserted + # during up/down, or promoted/demoted. + if blnum1: + assert blnum1 == bnodes[tlnum1-1] + if tlnum2 < len(bnodes): + assert blnum2 == bnodes[tlnum2]-1 + else: + assert blnum2 == Z + + # blnumCut is Body lnum after which a region was removed during 'cut', + # 'up', 'down'. We need to check if there is blank line between nodes + # used to be separated by the cut/moved region to prevent headline loss. + if blnumCut: + if tlnumCut < len(bnodes): + assert blnumCut == bnodes[tlnumCut]-1 + else: + assert blnumCut == Z + + # Total number of added lines minus number of deleted lines. + b_delta = 0 + + ### After 'cut' or 'up': insert blank line if there is none + # between the nodes used to be separated by the cut/moved region. + if (oop=='cut' or oop=='up') and (0 < blnumCut < Z) and Body[blnumCut-1].strip(): + Body[blnumCut:blnumCut] = [''] + update_bnodes(VO, tlnumCut+1 ,1) + b_delta+=1 + + if oop=='cut': + return + + ### Prevent loss of headline after last node in the region: + # insert blank line after blnum2 if blnum2 is not blank, that is insert + # blank line before bnode at tlnum2+1. + if blnum2 < Z and Body[blnum2-1].strip(): + Body[blnum2:blnum2] = [''] + update_bnodes(VO, tlnum2+1 ,1) + b_delta+=1 + + ### Change levels and/or styles of headlines in the affected region. + # Always do this after Paste, even if level is unchanged -- adornments can + # be different when pasting from other outlines. + # Examine each headline, from bottom to top, and change adornment style. + # To change from underline to overline style: + # insert overline. + # To change from overline to underline style: + # delete overline if there is blank before it; + # otherwise change overline to blank line; + # remove inset from headline text. + # Update bnodes after inserting or deleting a line. + if levDelta or oop=='paste': + ads_levels = VO.ads_levels + levels_ads = dict([[v,k] for k,v in ads_levels.items()]) + # Add adornment styles for new levels. Can't do this in the main loop + # because it goes backwards and thus will add styles in reverse order. + for i in xrange(tlnum1, tlnum2+1): + lev = levels[i-1] + if not lev in levels_ads: + ad = get_new_ad(levels_ads, ads_levels, lev) + levels_ads[lev] = ad + ads_levels[ad] = lev + for i in xrange(tlnum2, tlnum1-1, -1): + # required level (VO.levels has been updated) + lev = levels[i-1] + # required adornment style + ad = levels_ads[lev] + + # deduce current adornment style + bln = bnodes[i-1] + L1 = Body[bln-1].rstrip() + L2 = Body[bln].rstrip() + if bln+1 < len(Body): + L3 = Body[bln+1].rstrip() + else: + L3 = '' + ad_ = deduce_ad_style(L1,L2,L3,ENC) + + # change adornment style + # see deduce_ad_style() for diagram + if ad_==ad: + continue + elif len(ad_)==1 and len(ad)==1: + Body[bln] = ad*len(L2) + elif len(ad_)==2 and len(ad)==2: + Body[bln-1] = ad[0]*len(L1) + Body[bln+1] = ad[0]*len(L3) + elif len(ad_)==1 and len(ad)==2: + # change underline if different + if not ad_ == ad[0]: + Body[bln] = ad[0]*len(L2) + # insert overline; current bnode doesn't change + Body[bln-1:bln-1] = [ad[0]*len(L2)] + update_bnodes(VO, i+1, 1) + b_delta+=1 + elif len(ad_)==2 and len(ad)==1: + # change underline if different + if not ad_[0] == ad: + Body[bln+1] = ad*len(L3) + # remove headline inset if any + if not len(L2) == len(L2.lstrip()): + Body[bln] = L2.lstrip() + # check if line before overline is blank + if bln >1: + L0 = Body[bln-2].rstrip() + else: + L0 = '' + # there is blank before overline + # delete overline; current bnode doesn't change + if not L0: + Body[bln-1:bln] = [] + update_bnodes(VO, i+1, -1) + b_delta-=1 + # there is no blank before overline + # change overline to blank; only current bnode needs updating + else: + Body[bln-1] = '' + bnodes[i-1]+=1 + + ### Prevent loss of first headline: make sure it is preceded by a blank line + blnum1 = bnodes[tlnum1-1] + if blnum1 > 1 and Body[blnum1-2].strip(): + Body[blnum1-1:blnum1-1] = [''] + update_bnodes(VO, tlnum1 ,1) + b_delta+=1 + + ### After 'down' : insert blank line if there is none + # between the nodes used to be separated by the moved region. + if oop=='down' and (0 < blnumCut < Z) and Body[blnumCut-1].strip(): + Body[blnumCut:blnumCut] = [''] + update_bnodes(VO, tlnumCut+1 ,1) + b_delta+=1 + + assert len(Body) == Z + b_delta + + +def update_bnodes(VO, tlnum, delta): + """Update VO.bnodes by adding/substracting delta to each bnode + starting with bnode at tlnum and to the end. + """ + bnodes = VO.bnodes + for i in xrange(tlnum, len(bnodes)+1): + bnodes[i-1] += delta + + +def get_new_ad(levels_ads, ads_levels, level): + """Return adornment style for new level, that is level missing from + levels_ads and ads_levels. + """ + for ad in AD_STYLES: + if not ad in ads_levels: + return ad + # all 64 adornment styles are in use, return style for level 64 + assert len(levels_ads)==64 + return levels_ads[64] + + +def deduce_ad_style(L1,L2,L3,ENC): + """Deduce adornment style given first 3 lines of Body node. + 1st line is bnode line. Lines must be rstripped. + """ + # '--' style '-' style + # + # L0 L0 Body[bln-2] + # ---- L1 head L1 <--bnode Body[bln-1] + # head L2 ---- L2 Body[bln] + # ---- L3 text L3 Body[bln+1] + + # bnode is overline + if L1==L3 and (L1[0] in AD_CHARS) and L1.lstrip(L1[0])=='' and (len(L1) >= len(L2.decode(ENC,'replace'))): + ad = 2*L1[0] + # bnode is headline text + elif (L2[0] in AD_CHARS) and L2.lstrip(L2[0])=='' and (len(L2) >= len(L1.decode(ENC,'replace'))): + ad = L2[0] + else: + print L1 + print L2 + print L3 + print ENC + assert None + + return ad + + # wrong if perverse headline like this (correct ad style is '-') + # + # ^^^^^ + # ----- + # ^^^^^ + # text + + +def deduce_ad_style_test(VO): + """ Test to verify deduce_ad_style(). Execute from Vim + :py voom.VOOMS[1].mModule.deduce_ad_style_test(voom.VOOMS[1]) + """ + bnodes, levels, Body = VO.bnodes, VO.levels, VO.Body + ads_levels = VO.ads_levels + levels_ads = dict([[v,k] for k,v in ads_levels.items()]) + ENC = VO.enc + + for i in xrange(2, len(bnodes)+1): + bln = bnodes[i-1] + L1 = Body[bln-1].rstrip() + L2 = Body[bln].rstrip() + if bln+1 < len(Body): + L3 = Body[bln+1].rstrip() + else: + L3 = '' + ad = deduce_ad_style(L1,L2,L3,ENC) + lev = levels[i-1] + print i, ad, levels_ads[lev] + assert ad == levels_ads[lev] + + diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_thevimoutliner.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_thevimoutliner.py new file mode 100644 index 00000000..8d661843 --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_thevimoutliner.py @@ -0,0 +1,97 @@ +# voom_mode_thevimoutliner.py +# Last Modified: 2011-05-01 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode for The Vim Outliner format. +See |voom_mode_thevimoutliner|, ../../doc/voom.txt#*voom_mode_thevimoutliner* + +Headlines and body lines are indented with Tabs. Number of tabs indicates +level. 0 Tabs means level 1. + +Headlines are lines with >=0 Tabs followed by any character except '|'. + +Blank lines are not headlines. +""" + +# Body lines start with these chars +BODY_CHARS = {'|':0,} + +# ------ the rest is identical to voom_mode_vimoutliner.py ------------------- +def hook_makeOutline(VO, blines): + """Return (tlines, bnodes, levels) for Body lines blines. + blines is either Vim buffer object (Body) or list of buffer lines. + """ + Z = len(blines) + tlines, bnodes, levels = [], [], [] + tlines_add, bnodes_add, levels_add = tlines.append, bnodes.append, levels.append + for i in xrange(Z): + bline = blines[i].rstrip() + if not bline: + continue + head = bline.lstrip('\t') + if head[0] in BODY_CHARS: + continue + lev = len(bline) - len(head) + 1 + + tline = ' %s|%s' %('. '*(lev-1), head) + tlines_add(tline) + bnodes_add(i+1) + levels_add(lev) + return (tlines, bnodes, levels) + + +def hook_newHeadline(VO, level, blnum, tlnum): + """Return (tree_head, bodyLines). + tree_head is new headline string in Tree buffer (text after |). + bodyLines is list of lines to insert in Body buffer. + """ + tree_head = 'NewHeadline' + bodyLines = ['%s%s' %('\t'*(level-1), tree_head),] + return (tree_head, bodyLines) + + +#def hook_changeLevBodyHead(VO, h, levDelta): + #"""Increase of decrease level number of Body headline by levDelta.""" + #if levDelta==0: return h + +def hook_doBodyAfterOop(VO, oop, levDelta, blnum1, tlnum1, blnum2, tlnum2, blnumCut, tlnumCut): + # this is instead of hook_changeLevBodyHead() + if not levDelta: return + + indent = abs(levDelta) * '\t' + + Body = VO.Body + Z = len(Body) + + # ---- identical to voom_mode_python.py code ---------------------------- + if blnum1: + assert blnum1 == VO.bnodes[tlnum1-1] + if tlnum2 < len(VO.bnodes): + assert blnum2 == VO.bnodes[tlnum2]-1 + else: + assert blnum2 == Z + + # dedent (if possible) or indent every non-blank line in Body region blnum1,blnum2 + blines = [] + for i in xrange(blnum1-1,blnum2): + line = Body[i] + if not line.strip(): + blines.append(line) + continue + if levDelta > 0: + line = '%s%s' %(indent,line) + elif levDelta < 0 and line.startswith(indent): + line = line[len(indent):] + blines.append(line) + + # replace Body region + Body[blnum1-1:blnum2] = blines + assert len(Body)==Z diff --git a/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_txt2tags.py b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_txt2tags.py new file mode 100644 index 00000000..42dc4f10 --- /dev/null +++ b/vim_plugins_src/VOoM-4.3/VOoM-4.3/plugin/voom/voom_mode_txt2tags.py @@ -0,0 +1,105 @@ +# voom_mode_txt2tags.py +# Last Modified: 2011-05-01 +# VOoM -- Vim two-pane outliner, plugin for Python-enabled Vim version 7.x +# Website: http://www.vim.org/scripts/script.php?script_id=2657 +# Author: Vlad Irnov (vlad DOT irnov AT gmail DOT com) +# License: This program is free software. It comes without any warranty, +# to the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want To +# Public License, Version 2, as published by Sam Hocevar. +# See http://sam.zoy.org/wtfpl/COPYING for more details. + +""" +VOoM markup mode for txt2tags titles. +See |voom_mode_txt2tags|, ../../doc/voom.txt#*voom_mode_txt2tags* +""" + +import re +# from txt2tags.py +# titskel = r'^ *(?P%s)(?P%s)\1(\[(?P