1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-04-13 22:59:15 +08:00

Add doc for undotree (#3889)

This commit is contained in:
Wang Shidong 2020-10-31 15:52:34 +08:00 committed by GitHub
parent 939d8a617f
commit 15a511c32f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
82 changed files with 12852 additions and 8 deletions

View File

@ -12,6 +12,7 @@ let s:PASSWORD = SpaceVim#api#import('password')
let s:NUMBER = SpaceVim#api#import('data#number')
let s:LIST = SpaceVim#api#import('data#list')
let s:VIM = SpaceVim#api#import('vim')
let s:CMP = SpaceVim#api#import('vim#compatible')
function! SpaceVim#layers#edit#plugins() abort
let plugins = [
@ -38,6 +39,11 @@ function! SpaceVim#layers#edit#plugins() abort
if g:spacevim_enable_bepo_layout
call add(plugins,[g:_spacevim_root_dir . 'bundle/vim-bepo', { 'merged' : 0}])
endif
if s:CMP.has('python') || s:CMP.has('python3')
call add(plugins,[g:_spacevim_root_dir . 'bundle/vim-mundo', { 'on_cmd' : 'MundoToggle'}])
else
call add(plugins,[g:_spacevim_root_dir . 'bundle/undotree', { 'on_cmd' : 'UndotreeToggle'}])
endif
return plugins
endfunction
@ -66,6 +72,12 @@ function! SpaceVim#layers#edit#config() abort
vmap <silent> J <Plug>(jplus)
" }}}
if s:CMP.has('python') || s:CMP.has('python3')
nnoremap <silent> <F7> :MundoToggle<CR>
else
nnoremap <silent> <F7> :UndotreeToggle<CR>
endif
let g:_spacevim_mappings_space.x = {'name' : '+Text'}
let g:_spacevim_mappings_space.x.a = {'name' : '+align'}
let g:_spacevim_mappings_space.x.d = {'name' : '+delete'}
@ -307,7 +319,7 @@ endfunction
function! s:lowerCamelCase() abort
" fooFzz
if matchstr(getline('.'), '\%' . col('.') . 'c.') =~ '\s'
if matchstr(getline('.'), '\%' . col('.') . 'c.') =~# '\s'
return
endif
let cword = s:parse_symbol(expand('<cword>'))
@ -327,7 +339,7 @@ endfunction
function! s:UpperCamelCase() abort
" FooFzz
if strcharpart(getline('.')[col('.') - 1:], 0, 1) =~ '\s'
if strcharpart(getline('.')[col('.') - 1:], 0, 1) =~# '\s'
return
endif
let cword = s:parse_symbol(expand('<cword>'))
@ -344,7 +356,7 @@ endfunction
function! s:kebab_case() abort
" foo-fzz
if matchstr(getline('.'), '\%' . col('.') . 'c.') =~ '\s'
if matchstr(getline('.'), '\%' . col('.') . 'c.') =~# '\s'
return
endif
let cword = s:parse_symbol(expand('<cword>'))
@ -373,7 +385,7 @@ endfunction
function! s:up_case() abort
" FOO_FZZ
if matchstr(getline('.'), '\%' . col('.') . 'c.') =~ '\s'
if matchstr(getline('.'), '\%' . col('.') . 'c.') =~# '\s'
return
endif
let cword =map(s:parse_symbol(expand('<cword>')), 'toupper(v:val)')

View File

@ -6,6 +6,8 @@
" License: GPLv3
"=============================================================================
let s:CMP = SpaceVim#api#import('vim#compatible')
function! SpaceVim#layers#tools#plugins() abort
let plugins = []
call add(plugins, ['tpope/vim-scriptease', { 'merged' : 0}])
@ -13,7 +15,6 @@ function! SpaceVim#layers#tools#plugins() abort
call add(plugins, ['lymslive/vnote', { 'depends' : 'vimloo', 'on_cmd' : ['NoteBook','NoteNew','NoteEdit', 'NoteList', 'NoteConfig', 'NoteIndex', 'NoteImport']}])
call add(plugins, ['junegunn/rainbow_parentheses.vim', { 'merged' : 0}])
call add(plugins, ['mbbill/fencview', { 'on_cmd' : 'FencAutoDetect'}])
call add(plugins, ['simnalamburt/vim-mundo', { 'on_cmd' : 'MundoToggle'}])
call add(plugins, ['wsdjeg/vim-cheat', { 'on_cmd' : 'Cheat'}])
call add(plugins, ['wsdjeg/Mysql.vim', { 'on_cmd' : 'SQLGetConnection'}])
call add(plugins, ['wsdjeg/SourceCounter.vim', { 'on_cmd' : 'SourceCounter'}])
@ -29,7 +30,6 @@ function! SpaceVim#layers#tools#plugins() abort
\ 'BookmarkPrev',
\ ],
\ 'loadconf_before' : 1}])
let s:CMP = SpaceVim#api#import('vim#compatible')
if s:CMP.has('python')
call add(plugins, ['gregsexton/VimCalc', {'on_cmd' : 'Calc'}])
elseif s:CMP.has('python3')
@ -56,7 +56,6 @@ function! SpaceVim#layers#tools#config() abort
nnoremap <silent> ma :<C-u>BookmarkShowAll<Cr>
nnoremap <silent> mn :<C-u>BookmarkNext<Cr>
nnoremap <silent> mp :<C-u>BookmarkPrev<Cr>
nnoremap <silent> <F7> :MundoToggle<CR>
augroup rainbow_lisp
autocmd!
autocmd FileType lisp,clojure,scheme,racket,java RainbowParentheses

1
bundle/undotree/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
doc/tags

66
bundle/undotree/README.md Normal file
View File

@ -0,0 +1,66 @@
### [Project on Vim.org](http://www.vim.org/scripts/script.php?script_id=4177)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mbbill/undotree?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
### Screenshot
![](https://sites.google.com/site/mbbill/undotree_new.png)
### Description
The plug-in visualizes undo history and makes it easier to browse and switch between different undo branches. You might wonder what is undo "branches"? It's vim feature that allows you to go back to a state when it is overwritten by a latest edit. For most editors, if you make a change A, then B, then go back to A and make change C, normally you won't be able to go back to B because undo history is linear. That's not the case for Vim because it internally keeps all the edit history like a tree structure, and this plug-in exposes the tree to you so that you not only can switch back and forth but also can switch between branches.
Some people have questions about file contents being changed when switching between undo history states. Don't worry, *undotree* will **NEVER** save your data or write to disk. All it does is to change the current buffer little bit, just like those auto-completion plug-ins do. It just adds or removes something in the buffer temporarily, and if you don't like you can always go back to the last state easily. Let's say, you made some change but didn't save, then you use *undotree* and go back to an arbitrary version, your unsaved change doesn't get lost - it stores in the latest undo history node. Clicking that node on *undotree* will bring you back instantly. Play with undo/redo on other editors is always dangerous because when you step back and accidentally typed something, boom! You lose your edits. But don't worry, that won't happen in Vim. Then you might ask what if I make some changes without saving and switch back to an old version and then **exit**? Well, imaging what would happen if you don't have *undotree*? You lose your latest edits and the file on disk is your last saved version. This behaviour **remains the same** with *undotree*. So, if you saved, you won't lose anything.
We all know that usually undo/redo is only for the current edit session. It's stored in memory and once the process exits, the undo history is lost. Although *undotree* makes switching between history states easier, it doesn't do more than that. Sometimes it would be much safer or more convenient to keep the undo history across edit sessions. In this case you might need to enable a Vim feature called *persistent undo*. Let me explain how persistent undo works: instead of keeping undo history in *RAM*, persistent undo keeps undo history in file. Let's say you make a change A, then B, then go back to A and make change C, then you *save* the file. Now Vim save the file with content state C, and in the mean time it saves **the entire** undo history to a file including state A, B and C. Next time when you open the file, Vim will also restore undo history. So you can still go back to B. The history file is incremental, and every change will be recorded permanently, kind of like Git. You might think that's too much, well, *undotree* does provide a way to clean them up. If you need to enable *persistent undo*, type ```:h persistent-undo``` or follow the instructions below.
Undotree is written in **pure Vim script** and doesn't rely on any third party tools. It's lightweight, simple and fast. It only does what it supposed to do, and it only runs when you need it.
### Download and Install
Use whatever plug-in manager to pull the master branch.
### Usage
1. Use `:UndotreeToggle` to toggle the undo-tree panel. You may want to map this command to whatever hotkey by adding the following line to your vimrc, take F5 for example.
nnoremap &lt;F5&gt; :UndotreeToggle&lt;cr&gt;
1. Markers
* Every change has a sequence number and it is displayed before timestamps.
* The current state is marked as `> number <`.
* The next state which will be restored by `:redo` or `<ctrl-r>` is marked as `{ number }`.
* The `[ number ]` marks the most recent change.
* The undo history is sorted by timestamps.
* Saved changes are marked as `s` and the big `S` indicates the most recent saved change.
1. Press `?` in undotree window for quick help.
1. Persistent undo
* Usually I would like to store the undo files in a seperate place like below.
```
if has("persistent_undo")
set undodir=$HOME."/.undodir"
set undofile
endif
```
### Configuration
[Here](https://github.com/mbbill/undotree/blob/master/plugin/undotree.vim#L15) is a list of options.
### Debug
1. Create a file under $HOME with the name `undotree_debug.log`
* `$touch ~/undotree_debug.log`
1. Run vim, and the log will automatically be appended to the file, and you may watch it using `tail`:
* `$tail -F ~/undotree_debug.log`
1. If you want to disable debug, just delete that file.
### License
**BSD**
### Author
Ming Bai &lt;mbbill AT gmail DOT COM&gt;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,457 @@
*undotree.txt* The undo history visualizer for VIM
Author: Ming Bai <mbbill AT gmail DOT COM>
Licence: BSD
Homepage: https://github.com/mbbill/undotree/
==============================================================================
CONTENTS *undotree-contents*
1. Intro ................................ |undotree-intro|
2. Usage ................................ |undotree-usage|
3. Configuration ........................ |undotree-config|
3.1 undotree_WindowLayout .......... |undotree_WindowLayout|
3.2 undotree_CustomUndotreeCmd...... |undotree_CustomUndotreeCmd|
undotree_CustomDiffpanelCmd..... |undotree_CustomDiffpanelCmd|
3.3 undotree_SplitWidth ............ |undotree_SplitWidth|
3.4 undotree_DiffpanelHeight ....... |undotree_DiffpanelHeight|
3.5 undotree_DiffAutoOpen .......... |undotree_DiffAutoOpen|
3.6 undotree_SetFocusWhenToggle .... |undotree_SetFocusWhenToggle|
3.7 undotree_TreeNodeShape ......... |undotree_TreeNodeShape|
3.8 undotree_DiffCommand ........... |undotree_DiffCommand|
3.9 undotree_RelativeTimestamp ..... |undotree_RelativeTimestamp|
3.10 undotree_ShortIndicators ....... |undotree_ShortIndicators|
3.11 undotree_HighlightChangedText .. |undotree_HighlightChangedText|
3.12 undotree_HighlightSyntaxAdd .... |undotree_HighlightSyntaxAdd|
undotree_HighlightSyntaxChange . |undotree_HighlightSyntaxChange|
3.13 Undotree_CustomMap ............. |Undotree_CustomMap|
3.14 undotree_HelpLine .............. |undotree_HelpLine|
4. Bugs ................................. |undotree-bugs|
5. Changelog ............................ |undotree-changelog|
6. License .............................. |undotree-license|
==============================================================================
1. Intro *undotree-intro*
The plug-in visualizes undo history and makes it easier to browse and switch
between different undo branches. You might wonder what is undo "branches"?
It's vim feature that allows you to go back to a state when it is overwritten
by a latest edit. For most editors, if you make a change A, then B, then go
back to A and make change C, normally you won't be able to go back to B
because undo history is linear. That's not the case for Vim because it
internally keeps all the edit history like a tree structure, and this plug-in
exposes the tree to you so that you not only can switch back and forth but
also can switch between branches.
Some people have questions about file contents being changed when switching
between undo history states. Don't worry, undotree will NEVER save your data
or write to disk. All it does is to change the current buffer little bit, just
like those auto-completion plug-ins do. It just adds or removes something in
the buffer temporarily, and if you don't like you can always go back to the
last state easily. Let's say, you made some change but didn't save, then you
use undotree and go back to an arbitrary version, your unsaved change doesn't
get lost - it stores in the latest undo history node. Clicking that node on
undotree will bring you back instantly. Play with undo/redo on other editors
is always dangerous because when you step back and accidentally typed
something, boom! You lose your edits. But don't worry, that won't happen in
Vim. Then you might ask what if I make some changes without saving and switch
back to an old version and then exit? Well, imaging what would happen if you
don't have undotree? You lose your latest edits and the file on disk is your
last saved version. This behaviour remains the same with undotree. So, if you
saved, you won't lose anything.
We all know that usually undo/redo is only for the current edit session. It's
stored in memory and once the process exits, the undo history is lost.
Although undotree makes switching between history states easier, it doesn't do
more than that. Sometimes it would be much safer or more convenient to keep
the undo history across edit sessions. In this case you might need to enable a
Vim feature called persistent undo. Let me explain how persistent undo works:
instead of keeping undo history in RAM, persistent undo keeps undo history in
file. Let's say you make a change A, then B, then go back to A and make change
C, then you save the file. Now Vim save the file with content state C, and in
the mean time it saves the entire undo history to a file including state A, B
and C. Next time when you open the file, Vim will also restore undo history.
So you can still go back to B. The history file is incremental, and every
change will be recorded permanently, kind of like Git. You might think that's
too much, well, undotree does provide a way to clean them up. If you need to
enable persistent undo, type :h persistent-undo or follow the instructions
below.
Undotree is written in pure Vim script and doesn't rely on any third party
tools. It's lightweight, simple and fast. It only does what it supposed to do,
and it only runs when you need it.
==============================================================================
2. Usage *undotree-usage*
Use :UndotreeToggle to toggle the undo-tree panel. You may want to map this
command to whatever hotkey by adding the following line to your vimrc, take F5
for example.
>
nnoremap <F5> :UndotreeToggle<cr>
<
Markers
* Every change has a sequence number and it is displayed before timestamps.
* The current state is marked as > number <.
* The next state which will be restored by :redo or <ctrl-r> is marked as
{ number }.
* The [ number ] marks the most recent change.
* The undo history is sorted by timestamps.
* Saved changes are marked as s and the big S indicates the most recent
saved change.
* Press ? in undotree window for quick help.
Persistent undo
Usually I would like to store the undo files in a seperate place like below.
>
if has("persistent_undo")
set undodir=$HOME."/.undodir"
set undofile
endif
<
==============================================================================
3. Configuration *undotree-config*
------------------------------------------------------------------------------
3.1 g:undotree_WindowLayout *undotree_WindowLayout*
Set the undotree window layout.
Style 1
>
+----------+------------------------+
| | |
| | |
| undotree | |
| | |
| | |
+----------+ |
| | |
| diff | |
| | |
+----------+------------------------+
<
Style 2
>
+----------+------------------------+
| | |
| | |
| undotree | |
| | |
| | |
+----------+------------------------+
| |
| diff |
| |
+-----------------------------------+
<
Style 3
>
+------------------------+----------+
| | |
| | |
| | undotree |
| | |
| | |
| +----------+
| | |
| | diff |
| | |
+------------------------+----------+
<
Style 4
>
+------------------------+----------+
| | |
| | |
| | undotree |
| | |
| | |
+------------------------+----------+
| |
| diff |
| |
+-----------------------------------+
<
Default: 1
------------------------------------------------------------------------------
3.2 g:undotree_CustomUndotreeCmd *undotree_CustomUndotreeCmd*
g:undotree_CustomDiffpanelCmd *undotree_CustomDiffpanelCmd*
Set up custom window layout.
Setting |undotree_CustomUndotreeCmd| will ignore |undotree_SplitWidth|, and
setting |undotree_CustomDiffpanelCmd| will ignore |undotree_DiffpanelHeight|.
An |undotree_CustomUndotreeCmd| will always open the undotree window relative
to the tracked window and |undotree_CustomDiffpanelCmd| will always open the
diffpanel relative to the undotree window.
Useful when
* absolute positioning commands (|topleft|, |botright|) don't play well
with other plugins
* you have a preferred split window layout and would like to use
UndoTree relative to one specific window only
Examples:
* To recreate Style 1:
>
let g:undotree_CustomUndotreeCmd = 'topleft vertical 30 new'
let g:undotree_CustomDiffpanelCmd = 'belowright 10 new'
<
* To recreate Style 2:
>
let g:undotree_CustomUndotreeCmd = 'topleft vertical 30 new'
let g:undotree_CustomDiffpanelCmd = 'botright 10 new'
<
* A custom layout example:
>
+------------------------+----------+
| | |
| | w |
| | i |
| | n |
| window_1 | d |
| | o |
| | w |
| | | |
| | 2 |
| | |
+------------------------+----------+
| |
| window_3 |
| |
+-----------------------------------+
<
Using the following setup wouldn't mess up the current layout as it
does not use absolute positioning:
>
let g:undotree_CustomUndotreeCmd = 'vertical 32 new'
let g:undotree_CustomDiffpanelCmd= 'belowright 12 new'
<
Issuing :UndotreeToggle now in window_1 would result in:
>
+--------+---------------+----------+
| | | |
| u | | w |
| n | | i |
| d | | n |
| o | window_1 | d |
| | | o |
+--------+ | w |
| | | | |
| diff | | 2 |
| | | |
+--------+---------------+----------+
| |
| window_3 |
| |
+-----------------------------------+
<
Executing :UndotreeToggle again would turn off UndoTree (independently
of which window was active at the time). Moving between window1, window_2
and window_3 would result in showing the respective window's changelog
in the undotree panel.
CAVEAT: To avoid the Vim's default behaviour of equalizing window sizes
when closing a window, set the 'noequalalways' option.
------------------------------------------------------------------------------
3.3 g:undotree_SplitWidth *undotree_SplitWidth*
Set the undotree window width.
Default: 30
------------------------------------------------------------------------------
3.4 g:undotree_DiffpanelHeight *undotree_DiffpanelHeight*
Set the diff window height.
Default: 10
------------------------------------------------------------------------------
3.5 g:undotree_DiffAutoOpen *undotree_DiffAutoOpen*
Set this to 1 to auto open the diff window.
Default: 1
------------------------------------------------------------------------------
3.6 g:undotree_SetFocusWhenToggle *undotree_SetFocusWhenToggle*
If set to 1, the undotree window will get focus after being opened, otherwise
focus will stay in current window.
Default: 0
------------------------------------------------------------------------------
3.7 g:undotree_TreeNodeShape *undotree_TreeNodeShape*
Set the tree node shape.
Default: '*'
------------------------------------------------------------------------------
3.8 g:undotree_DiffCommand *undotree_DiffCommand*
Set the command used to get the diff output.
Default: "diff"
------------------------------------------------------------------------------
3.9 g:undotree_RelativeTimestamp *undotree_RelativeTimestamp*
Set to 1 to use relative timestamp.
Default: 1
------------------------------------------------------------------------------
3.10 g:undotree_ShortIndicators *undotree_ShortIndicators*
Set to 1 to get short timestamps when |undotree_RelativeTimestamp| is also
enabled:
>
Before | After
===========================
(5 seconds ago) | (5 s)
----------------|----------
(1 minute ago) | (1 m)
----------------|----------
(2 minutes ago) | (2 m)
----------------|----------
(1 hour ago) | (1 h)
----------------|----------
(Original) | (Orig)
<
Default: 0
------------------------------------------------------------------------------
3.11 g:undotree_HighlightChangedText *undotree_HighlightChangedText*
Set to 1 to highlight the changed text.
Default: 1
------------------------------------------------------------------------------
3.12 g:undotree_HighlightSyntaxAdd *undotree_HighlightSyntaxAdd*
g:undotree_HighlightSyntaxChange *undotree_HighlightSyntaxChange*
Set the highlight linked syntax type.
You may chose your favorite through ":hi" command.
Default: "DiffAdd" and "DiffChange"
------------------------------------------------------------------------------
3.13 g:Undotree_CustomMap *Undotree_CustomMap*
There are two ways of changing the default key mappings:
The first way is to define global mappings as the following example:
>
nmap <buffer> J <plug>UndotreeNextState
nmap <buffer> K <plug>UndotreePreviousState
<
A better approach is to define the callback function g:Undotree_CustomMap().
The function will be called after the undotree windows is initialized, so the
key mappings only works on the undotree windows.
>
function g:Undotree_CustomMap()
nmap <buffer> J <plug>UndotreeNextState
nmap <buffer> K <plug>UndotreePreviousState
endfunc
<
List of the commands available for redefinition.
>
<plug>UndotreeHelp
<plug>UndotreeClose
<plug>UndotreeFocusTarget
<plug>UndotreeClearHistory
<plug>UndotreeTimestampToggle
<plug>UndotreeDiffToggle
<plug>UndotreeNextState
<plug>UndotreePreviousState
<plug>UndotreeNextSavedState
<plug>UndotreePreviousSavedState
<plug>UndotreeRedo
<plug>UndotreeUndo
<plug>UndotreeEnter
<
------------------------------------------------------------------------------
3.14 g:undotree_HelpLine *undotree_HelpLine*
Set to 0 to hide "Press ? for help".
Default: 1
==============================================================================
4. Bugs *undotree-bugs*
Post any issue and feature request here:
https://github.com/mbbill/undotree/issues
==============================================================================
5. Changelog *undotree-changelog*
Further changes will not be recorded. Please go to github page for more
information.
4.4 (2017-10-15)
- Autoload plugin functions
4.3 (2013-02-18)
- Several fixes and enhancements.
4.2 (2012-11-24)
- Fixed some small issue.
4.1 (2012-09-05)
- Enhanced tree style.
- Multi-window switching support.
4.0 (2012-08-30)
- Live updated highlight for changed text.
- Customizable key mappings.
- Fixed some minor bugs.
3.1 (2012-08-25)
- Add saved status.
- Add relative timestamp.
- Add ability of clear undo history.
3.0 (2012-08-24)
- Add diff panel.
- Performance improvement.
2.2 (2012-08-21)
- Stable version.
2.1 (2012-08-20)
- Fixed some annoying issues.
2.0 (2012-08-19)
- Hotkey support.
- Handle undo levels.
- Auto refresh.
- And so on.
1.0 (2012-08-18)
- Initial upload
==============================================================================
6. License *undotree-license*
BSD
vim:tw=78:ts=8:ft=help:norl:

View File

@ -0,0 +1,166 @@
"=================================================
" File: plugin/undotree.vim
" Description: Manage your undo history in a graph.
" Author: Ming Bai <mbbill@gmail.com>
" License: BSD
" Avoid installing twice.
if exists('g:loaded_undotree')
finish
endif
let g:loaded_undotree = 0
" At least version 7.3 with 005 patch is needed for undo branches.
" Refer to https://github.com/mbbill/undotree/issues/4 for details.
" Thanks kien
if v:version < 703
command! -n=0 -bar UndotreeToggle :echoerr "undotree.vim needs Vim version >= 7.3"
finish
endif
if (v:version == 703 && !has("patch005"))
command! -n=0 -bar UndotreeToggle :echoerr "undotree.vim needs vim7.3 with patch005 applied."
finish
endif
let g:loaded_undotree = 1 " Signal plugin availability with a value of 1.
"=================================================
"Options:
" Window layout
" style 1
" +----------+------------------------+
" | | |
" | | |
" | undotree | |
" | | |
" | | |
" +----------+ |
" | | |
" | diff | |
" | | |
" +----------+------------------------+
" Style 2
" +----------+------------------------+
" | | |
" | | |
" | undotree | |
" | | |
" | | |
" +----------+------------------------+
" | |
" | diff |
" | |
" +-----------------------------------+
" Style 3
" +------------------------+----------+
" | | |
" | | |
" | | undotree |
" | | |
" | | |
" | +----------+
" | | |
" | | diff |
" | | |
" +------------------------+----------+
" Style 4
" +-----------------------++----------+
" | | |
" | | |
" | | undotree |
" | | |
" | | |
" +------------------------+----------+
" | |
" | diff |
" | |
" +-----------------------------------+
if !exists('g:undotree_WindowLayout')
let g:undotree_WindowLayout = 1
endif
" e.g. using 'd' instead of 'days' to save some space.
if !exists('g:undotree_ShortIndicators')
let g:undotree_ShortIndicators = 0
endif
" undotree window width
if !exists('g:undotree_SplitWidth')
if g:undotree_ShortIndicators == 1
let g:undotree_SplitWidth = 24
else
let g:undotree_SplitWidth = 30
endif
endif
" diff window height
if !exists('g:undotree_DiffpanelHeight')
let g:undotree_DiffpanelHeight = 10
endif
" auto open diff window
if !exists('g:undotree_DiffAutoOpen')
let g:undotree_DiffAutoOpen = 1
endif
" if set, let undotree window get focus after being opened, otherwise
" focus will stay in current window.
if !exists('g:undotree_SetFocusWhenToggle')
let g:undotree_SetFocusWhenToggle = 0
endif
" tree node shape.
if !exists('g:undotree_TreeNodeShape')
let g:undotree_TreeNodeShape = '*'
endif
if !exists('g:undotree_DiffCommand')
let g:undotree_DiffCommand = "diff"
endif
" relative timestamp
if !exists('g:undotree_RelativeTimestamp')
let g:undotree_RelativeTimestamp = 1
endif
" Highlight changed text
if !exists('g:undotree_HighlightChangedText')
let g:undotree_HighlightChangedText = 1
endif
" Highlight changed text using signs in the gutter
if !exists('g:undotree_HighlightChangedWithSign')
let g:undotree_HighlightChangedWithSign = 1
endif
" Highlight linked syntax type.
" You may chose your favorite through ":hi" command
if !exists('g:undotree_HighlightSyntaxAdd')
let g:undotree_HighlightSyntaxAdd = "DiffAdd"
endif
if !exists('g:undotree_HighlightSyntaxChange')
let g:undotree_HighlightSyntaxChange = "DiffChange"
endif
if !exists('g:undotree_HighlightSyntaxDel')
let g:undotree_HighlightSyntaxDel = "DiffDelete"
endif
" Deprecates the old style configuration.
if exists('g:undotree_SplitLocation')
echo "g:undotree_SplitLocation is deprecated,
\ please use g:undotree_WindowLayout instead."
endif
" Show help line
if !exists('g:undotree_HelpLine')
let g:undotree_HelpLine = 1
endif
"=================================================
" User commands.
command! -n=0 -bar UndotreeToggle :call undotree#UndotreeToggle()
command! -n=0 -bar UndotreeHide :call undotree#UndotreeHide()
command! -n=0 -bar UndotreeShow :call undotree#UndotreeShow()
command! -n=0 -bar UndotreeFocus :call undotree#UndotreeFocus()
" vim: set et fdm=marker sts=4 sw=4:

View File

@ -0,0 +1,37 @@
"=================================================
" File: undotree.vim
" Description: undotree syntax
" Author: Ming Bai <mbbill@gmail.com>
" License: BSD
syn match UndotreeNode ' \zs\*\ze '
syn match UndotreeNodeCurrent '\zs\*\ze.*>\d\+<'
syn match UndotreeTimeStamp '(.*)$'
syn match UndotreeFirstNode 'Original'
syn match UndotreeBranch '[|/\\]'
syn match UndotreeSeq ' \zs\d\+\ze '
syn match UndotreeCurrent '>\d\+<'
syn match UndotreeNext '{\d\+}'
syn match UndotreeHead '\[\d\+]'
syn match UndotreeHelp '^".*$' contains=UndotreeHelpKey,UndotreeHelpTitle
syn match UndotreeHelpKey '^" \zs.\{-}\ze:' contained
syn match UndotreeHelpTitle '===.*===' contained
syn match UndotreeSavedSmall ' \zss\ze '
syn match UndotreeSavedBig ' \zsS\ze '
hi def link UndotreeNode Question
hi def link UndotreeNodeCurrent Statement
hi def link UndotreeTimeStamp Function
hi def link UndotreeFirstNode Function
hi def link UndotreeBranch Constant
hi def link UndotreeSeq Comment
hi def link UndotreeCurrent Statement
hi def link UndotreeNext Type
hi def link UndotreeHead Identifier
hi def link UndotreeHelp Comment
hi def link UndotreeHelpKey Function
hi def link UndotreeHelpTitle Type
hi def link UndotreeSavedSmall WarningMsg
hi def link UndotreeSavedBig MatchParen
" vim: set et fdm=marker sts=4 sw=4:

2
bundle/vim-mundo/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
**/doc/**/tags
*.pyc

View File

@ -0,0 +1,32 @@
Short version for non-laywers:
The vim-mundo project is licensed under GPLv2 terms or any later version.
Longer version:
vim-mundo
Copyright (C) 2013-2017 Dane Summers
Copyright (C) 2014-2017 Hyeon Kim
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>.
The vim-mundo project includes packages written by third parties. The
following third party packages are included, and carry their own copyright
notices and license terms:
* This project is forked from gundo.vim <https://bitbucket.org/sjl/gundo.vim>
which is licensed under GPLv2 or any later version.
* The initial version of graphing codes was all taken from
Mercurial <https://www.mercurial-scm.org/> which is licensed under GPLv2 or
any later version.

339
bundle/vim-mundo/LICENSE Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

113
bundle/vim-mundo/README.md Normal file
View File

@ -0,0 +1,113 @@
vim-mundo
=========
A Vim plugin to visualizes the Vim [undo tree].
<img src="https://simnalamburt.github.io/vim-mundo/screenshot.png">
* [Official webpage]
* [Introductory Video]
<br>
### How is this different than other plugins?
Mundo is a fork of [Gundo], and it has bunch of improvements.
* Several new features:
* Ability to search undo history using <kbd>/</kbd>.
* An 'in line' diff mode.
* Navigation keys <kbd>J</kbd> and <kbd>K</kbd> to move thru written undos.
* Merged upstream [pull requests]:
* [Fix paths with spaces][pr-29]
* [Display timestamps in ISO format][pr-28]
* [Real time updates][i-40]
* [Show changes saved to disk][i-34]
* [Python NoneType errors][i-38]
* [open vimdiff of current buffer][i-28]
* [Add global_disable option][i-33]
* [Reduce verbosity][i-31]
* [Neovim] support
#### What's your further plan?
* Make faster
* Automated test
<br>
### Requirements
* Vim ≥ *7.3* &nbsp; *or* &nbsp; [Neovim]
* `+python3` or `+python` compile option
* Python ≥ *2.4*
Recommended vim settings:
```vim
" Enable persistent undo so that undo history persists across vim sessions
set undofile
set undodir=~/.vim/undo
```
<br>
### Installation
It is recommended to use one of the popular plugin managers for Vim. There are
many and you probably already have a preferred one, but a few examples for your
copy-and-paste convenience:
#### Pathogen
$ git clone https://github.com/simnalamburt/vim-mundo ~/.vim/bundle/mundo.vim
#### Vundle
```vim
Plugin 'simnalamburt/vim-mundo'
```
#### NeoBundle
```vim
NeoBundle 'simnalamburt/vim-mundo'
```
#### Vim-Plug
```vim
Plug 'simnalamburt/vim-mundo'
```
#### Manual (not recommended)
[Download](releases) the plugin and extract it in `~/.vim/` (or
`%PROGRAMFILES%/Vim/vimfiles` on Windows).
#### Unit tests
Tests unit tests can be run with [nose]:
```shell
cd autoload
nosetests
```
<br>
--------
*vim-mundo* is primarily distributed under the terms of the [GNU General Public
License, version 2] or any later version. See [COPYRIGHT] for details.
[pull requests]: https://github.com/sjl/gundo.vim/pulls
[undo tree]: https://neovim.io/doc/user/undo.html#undo-tree
[Gundo]: https://github.com/sjl/gundo.vim
[Official webpage]: https://simnalamburt.github.io/vim-mundo
[Introductory Video]: https://simnalamburt.github.io/vim-mundo/screencast.mp4
[Neovim]: https://neovim.io
[pr-29]: https://github.com/sjl/gundo.vim/pull/29
[pr-28]: https://github.com/sjl/gundo.vim/pull/28
[i-34]: https://bitbucket.org/sjl/gundo.vim/issue/34/show-changes-that-were-saved-onto-disk
[i-38]: https://bitbucket.org/sjl/gundo.vim/issue/38/python-errors-nonetype-not-iterable-with
[i-40]: https://bitbucket.org/sjl/gundo.vim/issue/40/feature-request-live-reload
[i-28]: https://bitbucket.org/sjl/gundo.vim/issue/28/feature-request-open-vimdiff-of-current#comment-3129981
[i-33]: https://bitbucket.org/sjl/gundo.vim/issue/33/let-g-gundo_disable-0-is-not-available
[i-31]: https://bitbucket.org/sjl/gundo.vim/issue/31/reduce-verbosity-of-the-list
[nose]: https://nose.readthedocs.org/en/latest/
[GNU General Public License, version 2]: LICENSE
[COPYRIGHT]: COPYRIGHT

View File

@ -0,0 +1,35 @@
scriptencoding utf-8
function! airline#extensions#mundo#inactive_statusline(...)
let builder = a:1
if getwinvar(a:2.winnr, '&filetype') == 'Mundo'
return -1
endif
if getwinvar(a:2.winnr, '&filetype') == 'MundoDiff'
return 1
endif
endfunction
function! airline#extensions#mundo#statusline(...)
let builder = a:1
if &filetype == 'Mundo'
call builder.add_section('airline_a',
\ get(g:, 'mundo_tree_statusline', 'Mundo'))
call builder.split()
call builder.add_section('airline_z', '%p%%')
return 1
endif
if &filetype == 'MundoDiff'
call builder.add_section('airline_a',
\ get(g:, 'mundo_preview_statusline', 'Mundo Diff'))
call builder.split()
return 1
endif
endfunction
function! airline#extensions#mundo#init(...)
call airline#add_statusline_func('airline#extensions#mundo#statusline')
call airline#add_inactive_statusline_func(
\ 'airline#extensions#mundo#inactive_statusline')
endfunction

View File

@ -0,0 +1,16 @@
import nose
from nose.tools import *
from mock import patch
import mundo.graphlog as graphlog
from mundo.node import Nodes
@patch('mundo.util.vim')
def test_generate(mock_vim):
eq_(graphlog.generate(
False,
0,
1,
2,
False,
Nodes()
), [['o ', '[0] Original ']])

View File

@ -0,0 +1,547 @@
# vim: set fdm=marker ts=4 sw=4 et:
# ============================================================================
# File: mundo.py
# Description: vim global plugin to visualize your undo tree
# Maintainer: Hyeon Kim <simnalamburt@gmail.com>
# License: GPLv2+ -- look it up.
# Notes: Much of this code was thieved from Mercurial, and the rest was
# heavily inspired by scratch.vim and histwin.vim.
#
# ============================================================================
import re
import tempfile
import vim
from mundo.node import Nodes
import mundo.util as util
import mundo.graphlog as graphlog
# Python Vim utility functions --------------------------------------------#{{{
MISSING_BUFFER = "Cannot find Mundo's target buffer (%s)"
MISSING_WINDOW = "Cannot find window (%s) for Mundo's target buffer (%s)"
def _check_sanity():
""" Check to make sure we're not crazy.
Ensures that:
* The target buffer exists, is loaded and is present in the tab.
* That neovim is not in terminal mode.
"""
global nodesData
if not nodesData:
nodesData = Nodes()
# Check that the target buffer exists, is loaded and is present in the tab
b = int(vim.eval('g:mundo_target_n'))
if not vim.eval('bufloaded(%d)' % int(b)):
vim.command('echo "%s"' % (MISSING_BUFFER % b))
return False
w = int(vim.eval('bufwinnr(%d)' % int(b)))
if w == -1:
vim.command('echo "%s"' % (MISSING_WINDOW % (w, b)))
return False
# Check if we are in terminal mode.
mode = vim.eval('mode()')
if mode == 't':
return False
return True
INLINE_HELP = '''\
" Mundo (%d) - Press ? for Help:
" j/k - Next/Prev undo state.
" J/K - Next/Prev write state.
" i - Toggle 'inline diff' mode.
" / - Find changes that match string.
" n/N - Next/Prev undo that matches search.
" P - Play current state to selected undo.
" d - Vert diff of undo with current state.
" p - Diff of selected undo and current state.
" r - Diff of selected undo and prior undo.
" q - Quit!
" <cr> - Revert to selected state.
'''
# }}}
nodesData = Nodes()
# from profilehooks import profile
# @profile(immediate=True)
def MundoRenderGraph(force=False):# {{{
""" Renders the undo graph if necessary, updating it to reflect changes in
the target buffer's undo tree.
Arguments
---------
force : bool
If True, the graph will always be rendered. If False, then the
graph may not be rendered - when is already current for example.
"""
if not _check_sanity():
return
util._goto_window_for_buffer('__Mundo__')
first_visible_line = int(vim.eval("line('w0')"))
last_visible_line = int(vim.eval("line('w$')"))
verbose = vim.eval('g:mundo_verbose_graph') == "1"
target = int(vim.eval('g:mundo_target_n'))
header = []
if int(vim.eval('g:mundo_header')):
if int(vim.eval('g:mundo_help')):
header = (INLINE_HELP % target).splitlines()
else:
header = [(INLINE_HELP % target).splitlines()[0], '\n']
show_inline_undo = int(vim.eval("g:mundo_inline_undo")) == 1
mundo_last_visible_line = int(vim.eval("g:mundo_last_visible_line"))
mundo_first_visible_line = int(vim.eval("g:mundo_first_visible_line"))
if not force and not nodesData.is_outdated() and (
not show_inline_undo or
(
mundo_first_visible_line == first_visible_line and
mundo_last_visible_line == last_visible_line
)
):
return
result = graphlog.generate(
verbose,
len(header)+1,
first_visible_line,
last_visible_line,
show_inline_undo,
nodesData
)
vim.command("let g:mundo_last_visible_line=%s"%last_visible_line)
vim.command("let g:mundo_first_visible_line=%s"%first_visible_line)
output = []
# right align the dag and flip over the y axis:
flip_dag = int(vim.eval("g:mundo_mirror_graph")) == 1
dag_width = 1
for line in result:
if len(line[0]) > dag_width:
dag_width = len(line[0])
for line in result:
if flip_dag:
dag_line = (line[0][::-1]).replace("/","\\")
output.append("%*s %s"% (dag_width,dag_line,line[1]))
else:
output.append("%-*s %s"% (dag_width,line[0],line[1]))
vim.command('call s:MundoOpenGraph()')
vim.command('setlocal modifiable')
lines = (header + output)
lines = [line.rstrip('\n') for line in lines]
vim.current.buffer[:] = lines
vim.command('setlocal nomodifiable')
i = 1
for line in output:
try:
line.split('[')[0].index('@')
i += 1
break
except ValueError:
pass
i += 1
vim.command('%d' % (i+len(header)-1))
# }}}
def MundoRenderPreview():# {{{
""" Opens the preview window if necessary and renders a preview diff. """
if not _check_sanity():
return
target_state = MundoGetTargetState()
target_n = int(vim.eval('g:mundo_target_n'))
# If there's no target state or the buffer has changed, update the cached
# undo tree data, redraw the graph and abort preview rendering
if target_state is None or nodesData.target_n != target_n:
nodesData.make_nodes()
MundoRenderGraph(True)
return
util._goto_window_for_buffer(target_n)
nodes, nmap = nodesData.make_nodes()
node_after = nmap[target_state]
node_before = node_after.parent
vim.command('call s:MundoOpenPreview()')
util._output_preview_text(nodesData.preview_diff(node_before, node_after))
# Mark the preview as up-to-date
vim.command('call mundo#MundoPreviewOutdated(0)')
# }}}
def MundoGetTargetState():# {{{
""" Get the current undo number that mundo is at. """
util._goto_window_for_buffer('__Mundo__')
target_line = vim.eval("getline('.')")
matches = re.match('^[^\[]* \[([0-9]+)\] .*$', target_line)
if matches:
return int(matches.group(1))
return 0
# }}}
def GetNextLine(direction,move_count,write,start="line('.')"):# {{{
""" Recursively finds the line number resulting from undo graph traversal
according to the given parameters.
"""
start_line_no = int(vim.eval(start))
start_line = vim.eval("getline(%d)" % start_line_no)
mundo_verbose_graph = vim.eval('g:mundo_verbose_graph')
if mundo_verbose_graph != "0":
distance = 2
# If we're in between two nodes we move by one less to get back on track.
if start_line.find('[') == -1:
distance = distance - 1
else:
distance = 1
nextline = vim.eval("getline(%d)" % (start_line_no+direction))
idx1 = nextline.find('@')
idx2 = nextline.find('o')
idx3 = nextline.find('w')
# if the next line is not a revision - then go down one more.
if (idx1+idx2+idx3) == -3:
distance = distance + 1
next_line = start_line_no + distance*direction
if move_count > 1:
return GetNextLine(direction,move_count-1,write,str(next_line))
elif write:
newline = vim.eval("getline(%d)" % (next_line))
if newline.find('w ') == -1:
# make sure that if we can't go up/down anymore that we quit out.
if direction < 0 and next_line == 1:
return next_line
if direction > 0 and next_line >= len(vim.current.window.buffer):
return next_line
return GetNextLine(direction,1,write,str(next_line))
return next_line
# }}}
def MundoMove(direction,move_count=1,relative=True,write=False):# {{{
"""
Move within the undo graph in the direction specified (or to the specific
undo node specified).
Parameters:
direction - -1/1 (up/down). when 'relative' if False, the undo node to
move to.
move_count - how many times to perform the operation (irrelevent for
relative == False).
relative - whether to move up/down, or to jump to a specific undo node.
write - If True, move to the next written undo.
"""
if relative:
target_n = GetNextLine(direction,move_count,write)
else:
updown = 1
if MundoGetTargetState() < direction:
updown = -1
target_n = GetNextLine(updown,abs(MundoGetTargetState()-direction),write)
# Bound the movement to the graph.
help_lines = 0
if int(vim.eval('g:mundo_header')):
help_lines = 3
elif int(vim.eval('g:mundo_help')):
help_lines = len(INLINE_HELP.split('\n'))
if target_n <= help_lines:
vim.command("call cursor(%d, 0)" % help_lines)
else:
vim.command("call cursor(%d, 0)" % target_n)
line = vim.eval("getline('.')")
# Move to the node, whether it's an @, o, or w
idx1 = line.find('@ ')
idx2 = line.find('o ')
idx3 = line.find('w ')
idxs = []
if idx1 != -1:
idxs.append(idx1)
if idx2 != -1:
idxs.append(idx2)
if idx3 != -1:
idxs.append(idx3)
if len(idxs)==0:
minidx=0
else:
minidx=min(idxs)
if idx1 == minidx:
vim.command("call cursor(0, %d + 1)" % idx1)
elif idx2 == minidx:
vim.command("call cursor(0, %d + 1)" % idx2)
else:
vim.command("call cursor(0, %d + 1)" % idx3)
# Mark the preview as outdated
vim.command('call mundo#MundoPreviewOutdated(1)')
# }}}
def MundoSearch():# {{{
try:
search = vim.eval("input('/')")
except:
return
if len(search) == 0:
return
vim.command('let @/="%s"' % search.replace("\\", "\\\\").replace('"', '\\"'))
MundoNextMatch()
# }}}
def MundoPrevMatch():# {{{
MundoMatch(-1)
# }}}
def MundoNextMatch():# {{{
MundoMatch(1)
# }}}
def MundoMatch(down):# {{{
""" Jump to the next node that matches the current pattern. If there is a
next node, search from the next node to the end of the list of changes.
Stop on a match. """
if not _check_sanity():
return
# save the current window number (should be the navigation window)
# then generate the undo nodes, and then go back to the current window.
util._goto_window_for_buffer(int(vim.eval('g:mundo_target_n')))
nodes, nmap = nodesData.make_nodes()
total = len(nodes) - 1
util._goto_window_for_buffer('__Mundo__')
mundo_node = MundoGetTargetState()
found_version = -1
if total > 0:
therange = range(mundo_node-1,-1,-1)
if down < 0:
therange = range(mundo_node+1,total+1)
for version in therange:
util._goto_window_for_buffer('__Mundo__')
undochanges = nodesData.preview_diff(nmap[version].parent, nmap[version])
# Look thru all of the changes, ignore the first two b/c those are the
# diff timestamp fields (not relevent):
for change in undochanges[3:]:
match_index = vim.eval('match("%s",@/)'% change.replace("\\","\\\\").replace('"','\\"'))
# only consider the matches that are actual additions or
# subtractions
if int(match_index) >= 0 and (change.startswith('-') or change.startswith('+')):
found_version = version
break
# found something, lets get out of here:
if found_version != -1:
break
util._goto_window_for_buffer('__Mundo__')
if found_version >= 0:
MundoMove(found_version,1,False)
# }}}
def MundoRenderPatchdiff():# {{{
""" Call MundoRenderChangePreview and display a vert diffpatch with the
current file. """
if MundoRenderChangePreview():
# if there are no lines, do nothing (show a warning).
util._goto_window_for_buffer('__Mundo_Preview__')
if vim.current.buffer[:] == ['']:
# restore the cursor position before exiting.
util._goto_window_for_buffer('__Mundo__')
vim.command('unsilent echo "No difference between current file and undo number!"')
return False
# quit out of mundo main screen
util._goto_window_for_buffer('__Mundo__')
vim.command('quit')
# save the __Mundo_Preview__ buffer to a temp file.
util._goto_window_for_buffer('__Mundo_Preview__')
(handle,filename) = tempfile.mkstemp()
vim.command('silent! w %s' % (filename))
# exit the __Mundo_Preview__ window
vim.command('bdelete')
# diff the temp file
vim.command('silent! keepalt vert diffpatch %s' % (filename))
vim.command('set buftype=nofile bufhidden=delete')
return True
return False
# }}}
def MundoGetChangesForLine():# {{{
if not _check_sanity():
return False
target_state = MundoGetTargetState()
# Check that there's an undo state. There may not be if we're talking about
# a buffer with no changes yet.
if target_state == None:
util._goto_window_for_buffer('__Mundo__')
return False
else:
target_state = int(target_state)
util._goto_window_for_buffer(int(vim.eval('g:mundo_target_n')))
nodes, nmap = nodesData.make_nodes()
node_after = nmap[target_state]
node_before = nmap[nodesData.current()]
return nodesData.change_preview_diff(node_before, node_after)
# }}}
def MundoRenderChangePreview():# {{{
""" Render a diff of the target buffer and the selected undo
tree node. Returns True on success, False otherwise.
"""
if not _check_sanity():
return
vim.command('call s:MundoOpenPreview()')
util._output_preview_text(MundoGetChangesForLine())
util._goto_window_for_buffer('__Mundo__')
# Mark the preview as up-to-date
vim.command('call mundo#MundoPreviewOutdated(0)')
return True
# }}}
def MundoRenderToggleInlineDiff():# {{{
""" Toggles g:mundo_inline_undo and redraws the graph window. """
show_inline = int(vim.eval('g:mundo_inline_undo'))
if show_inline == 0:
vim.command("let g:mundo_inline_undo=1")
else:
vim.command("let g:mundo_inline_undo=0")
line = int(vim.eval("line('.')"))
nodesData.clear_oneline_diffs()
MundoRenderGraph(True)
vim.command("call cursor(%d,0)" % line)
# }}}
def MundoToggleHelp():# {{{
""" Toggles g:mundo_help and redraws the graph window. """
show_help = int(vim.eval('g:mundo_help'))
if show_help == 0:
vim.command("let g:mundo_help=1")
else:
vim.command("let g:mundo_help=0")
line = int(vim.eval("line('.')"))
column = int(vim.eval("col('.')"))
old_line_count = int(vim.eval("line('$')"))
MundoRenderGraph(True)
new_line_count = int(vim.eval("line('$')"))
vim.command(
"call cursor(%d, %d)" % (line + new_line_count - old_line_count,
column)
)
# Mundo undo/redo}}}
def MundoRevert():# {{{
""" Reverts the target buffer to the state associated with a selected node
in the undo graph.
"""
if not _check_sanity():
return
target_n = MundoGetTargetState()
back = int(vim.eval('g:mundo_target_n'))
util._goto_window_for_buffer(back)
util._undo_to(target_n)
MundoRenderGraph()
if int(vim.eval('g:mundo_return_on_revert')):
util._goto_window_for_buffer(back)
if int(vim.eval('g:mundo_close_on_revert')):
vim.command('MundoToggle')
# }}}
def MundoPlayTo():# {{{
""" Replays changes between the current state and a selected state in
real-time.
"""
if not _check_sanity():
return
target_n = MundoGetTargetState()
back = int(vim.eval('g:mundo_target_n'))
delay = int(vim.eval('g:mundo_playback_delay'))
util._goto_window_for_buffer(back)
util.normal('zn')
nodes, nmap = nodesData.make_nodes()
start = nmap[nodesData.current()]
end = nmap[target_n]
def _walk_branch(origin, dest):# {{{
rev = origin.n < dest.n
nodes = []
if origin.n > dest.n:
current, final = origin, dest
else:
current, final = dest, origin
while current.n > final.n:
nodes.append(current)
current = current.parent
if current.n != final.n:
return None
nodes.append(current)
if rev:
return reversed(nodes)
else:
return nodes
# }}}
branch = _walk_branch(start, end)
if not branch:
vim.command('unsilent echo "No path to that node from here!"')
util.normal('zN')
return
for node in branch:
util._undo_to(node.n)
MundoRenderGraph()
util.normal('zz')
util._goto_window_for_buffer(back)
vim.command('redraw | sleep %dm' % delay)
util.normal('zN')
# }}}
# vim: set ts=4 sw=4 tw=79 fdm=marker et :

View File

@ -0,0 +1,561 @@
" ============================================================================
" File: mundo.vim
" Description: vim global plugin to visualize your undo tree
" Maintainer: Hyeon Kim <simnalamburt@gmail.com>
" License: GPLv2+ -- look it up.
" Notes: Much of this code was thiefed from Mercurial, and the rest was
" heavily inspired by scratch.vim and histwin.vim.
"
" ============================================================================
let s:save_cpo = &cpoptions
set cpoptions&vim
"{{{ Init
" Initialise global vars
let s:auto_preview_timer = -1"{{{
let s:preview_outdated = 1
let s:has_supported_python = 0
let s:has_timers = 0
let s:init_error = 'Initialisation failed due to an unknown error. '
\ . 'Please submit a bug report :)'
" This has to be outside of a function, otherwise it just picks up the CWD
let s:plugin_path = escape(expand('<sfile>:p:h'), '\')"}}}
" Default to placeholder functions for exposed methods
function! mundo#MundoToggle() abort "{{{
call mundo#util#Echo('WarningMsg',
\ 'Mundo init error: ' . s:init_error)
endfunction
function! mundo#MundoShow() abort
call mundo#util#Echo('WarningMsg',
\ 'Mundo init error: ' . s:init_error)
endfunction
function! mundo#MundoHide() abort
call mundo#util#Echo('WarningMsg',
\ 'Mundo init error: ' . s:init_error)
endfunction
"}}}
" Check vim version
if v:version <? '703'"{{{
let s:init_error = 'Vim version 7.03+ or later is required.'
let &cpoptions = s:save_cpo
finish
elseif v:version >=? '800' && has('timers')
let s:has_timers = 1
endif"}}}
" Check python version
if g:mundo_prefer_python3 && has('python3')"{{{
let s:has_supported_python = 2
elseif has('python')"
let s:has_supported_python = 1
elseif has('python3')"
let s:has_supported_python = 2
endif
if !s:has_supported_python
let s:init_error = 'A supported python version was not found.'
let &cpoptions = s:save_cpo
finish
endif"}}}
" Python init methods
function! s:InitPythonModule(python)"{{{
exe a:python .' import sys'
exe a:python .' if sys.version_info[:2] < (2, 4): '.
\ 'vim.command("let s:has_supported_python = 0")'
endfunction"}}}
function! s:MundoSetupPythonPath()"{{{
if g:mundo_python_path_setup == 0
let g:mundo_python_path_setup = 1
call s:MundoPython('sys.path.insert(1, "'. s:plugin_path .'")')
call s:MundoPython('sys.path.insert(1, "'. s:plugin_path .'/mundo")')
end
endfunction"}}}
"}}}
"{{{ Mundo buffer settings
function! s:MundoMakeMapping(mapping, action)
exec 'nnoremap <script> <silent> <buffer> ' . a:mapping .' '. a:action
endfunction
function! s:MundoMapGraph()"{{{
for key in keys(g:mundo_mappings)
let l:value = g:mundo_mappings[key]
if l:value == "move_older"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPython('MundoMove(1,'. v:count .')')<CR>")
elseif l:value == "move_newer"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPython('MundoMove(-1,'. v:count .')')<CR>")
elseif l:value == "preview"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoRenderPreview(1)<CR>:<C-u> call <sid>MundoPythonRestoreView('MundoRevert()')<CR>")
elseif l:value == "move_older_write"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPython('MundoMove(1,'.v:count.',True,True)')<CR>")
elseif l:value == "move_newer_write"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPython('MundoMove(-1,'.v:count.',True,True)')<CR>")
elseif l:value == "move_top"
call s:MundoMakeMapping(key, "gg:<C-u>call <sid>MundoPython('MundoMove(1,'.v:count.')')<CR>")
elseif l:value == "move_bottom"
call s:MundoMakeMapping(key, "G:<C-u>call <sid>MundoPython('MundoMove(0,0)')<CR>:<C-u>call <sid>MundoRefresh()<CR>")
elseif l:value == "play_to"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPythonRestoreView('MundoPlayTo()')<CR>zz")
elseif l:value == "diff"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPythonRestoreView('MundoRenderPatchdiff()')<CR>")
elseif l:value == "toggle_inline"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPythonRestoreView('MundoRenderToggleInlineDiff()')<CR>")
elseif l:value == "search"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPython('MundoSearch()')<CR>")
elseif l:value == "next_match"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPython('MundoNextMatch()')<CR>")
elseif l:value == "previous_match"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPython('MundoPrevMatch()')<CR>")
elseif l:value == "diff_current_buffer"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPythonRestoreView('MundoRenderChangePreview()')<CR>")
elseif l:value == "diff"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoRenderPreview(1)<CR>")
elseif l:value == "toggle_help"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoPython('MundoToggleHelp()')<CR>")
elseif l:value == "quit"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoClose()<CR>")
elseif l:value == "mouse_click"
call s:MundoMakeMapping(key, ":<C-u>call <sid>MundoMouseDoubleClick()<CR>")
endif
endfor
cabbrev <script> <silent> <buffer> q call <sid>MundoClose()
cabbrev <script> <silent> <buffer> quit call <sid>MundoClose()
endfunction"}}}
function! s:MundoMapPreview()"{{{
nnoremap <script> <silent> <buffer> q :<C-u>call <sid>MundoClose()<CR>
cabbrev <script> <silent> <buffer> q call <sid>MundoClose()
cabbrev <script> <silent> <buffer> quit call <sid>MundoClose()
endfunction"}}}
function! s:MundoSettingsGraph()"{{{
setlocal buftype=nofile
setlocal bufhidden=hide
setlocal noswapfile
setlocal nobuflisted
setlocal nomodifiable
setlocal filetype=Mundo
setlocal nolist
setlocal nonumber
setlocal norelativenumber
setlocal nowrap
call s:MundoSyntaxGraph()
call s:MundoMapGraph()
endfunction"}}}
function! s:MundoSettingsPreview()"{{{
setlocal buftype=nofile
setlocal bufhidden=hide
setlocal noswapfile
setlocal nobuflisted
setlocal nomodifiable
setlocal filetype=MundoDiff
setlocal syntax=diff
setlocal nonumber
setlocal norelativenumber
setlocal nowrap
setlocal foldlevel=20
setlocal foldmethod=diff
call s:MundoMapPreview()
endfunction"}}}
function! s:MundoSyntaxGraph()"{{{
let b:current_syntax = 'mundo'
syn match MundoCurrentLocation '@'
syn match MundoHelp '\v^".*$'
syn match MundoNumberField '\v\[[0-9]+\]'
syn match MundoNumber '\v[0-9]+' contained containedin=MundoNumberField
syn region MundoDiff start=/\v<ago> / end=/$/
syn match MundoDiffAdd '\v\+[^+-]+\+' contained containedin=MundoDiff
syn match MundoDiffDelete '\v-[^+-]+-' contained containedin=MundoDiff
hi def link MundoCurrentLocation Keyword
hi def link MundoHelp Comment
hi def link MundoNumberField Comment
hi def link MundoNumber Identifier
hi def link MundoDiffAdd DiffAdd
hi def link MundoDiffDelete DiffDelete
endfunction"}}}
"}}}
"{{{ Mundo buffer/window management
function! s:MundoResizeBuffers(backto)"{{{
call mundo#util#GoToBuffer('__Mundo__')
exe "vertical resize " . g:mundo_width
call mundo#util#GoToBuffer('__Mundo_Preview__')
exe "resize " . g:mundo_preview_height
exe a:backto . "wincmd w"
endfunction"}}}
" Open the graph window. Assumes that the preview window is open.
function! s:MundoOpenGraph()"{{{
if !mundo#util#GoToBuffer("__Mundo__")
call assert_true(mundo#util#GoToBuffer('__Mundo_Preview__'))
let existing_mundo_buffer = bufnr("__Mundo__")
if existing_mundo_buffer == -1
" Create buffer
silent new __Mundo__
if g:mundo_preview_bottom
execute 'wincmd ' . (g:mundo_right ? 'L' : 'H')
endif
else
" Open a window for existing buffer
if g:mundo_preview_bottom
let pos = (g:mundo_right ? 'botright' : 'topleft')
silent execute pos.' vsplit +buffer' . existing_mundo_buffer
else
silent execute 'split +buffer' . existing_mundo_buffer
endif
endif
call s:MundoResizeBuffers(winnr())
endif
if exists("g:mundo_tree_statusline")
let &l:statusline = g:mundo_tree_statusline
endif
endfunction"}}}
function! s:MundoOpenPreview()"{{{
if !mundo#util#GoToBuffer("__Mundo_Preview__")
let existing_preview_buffer = bufnr("__Mundo_Preview__")
if existing_preview_buffer == -1
" Create buffer
if g:mundo_preview_bottom
silent botright keepalt new __Mundo_Preview__
else
let pos = (g:mundo_right ? 'botright' : 'topleft')
silent execute pos.' keepalt vnew __Mundo_Preview__'
endif
else
" Open a window for existing buffer
if g:mundo_preview_bottom
silent execute 'botright keepalt split +buffer' .
\ existing_preview_buffer
else
let pos = (g:mundo_right ? 'botright' : 'topleft')
silent execute pos.' keepalt vsplit +buffer' .
\ existing_preview_buffer
endif
endif
endif
if exists("g:mundo_preview_statusline")
let &l:statusline = g:mundo_preview_statusline
endif
endfunction"}}}
" Quits *all* open Mundo graph and preview windows.
function! s:MundoClose() abort
let [l:tabid, l:winid] = win_id2tabwin(win_getid())
" Close all graph and preview windows
while mundo#util#GoToBufferGlobal('__Mundo__') ||
\ mundo#util#GoToBufferGlobal('__Mundo_Preview__')
quit
endwhile
" Attempt to return to previous window / tab or target buffer
if win_gotoid(l:winid)
return
elseif l:tabid != 0 && l:tabid <= tabpagenr('$')
execute 'normal! ' . l:tabid . 'gt'
endif
call mundo#util#GoToBuffer(get(g:, 'mundo_target_n', -1))
endfunction
" Returns 1 if the current buffer is a valid target buffer for Mundo, or a
" (falsy) string indicating the reason if otherwise.
function! s:MundoValidateBuffer()"{{{
if !&modifiable
let reason = 'is not modifiable'
elseif &previewwindow
let reason = 'is a preview window'
elseif &buftype == 'help' || &buftype == 'quickfix' || &buftype == 'terminal'
let reason = 'is a '.&buftype.' window'
else
return 1
endif
call mundo#util#Echo('None', 'Current buffer ('.bufnr('').') is not a '
\ .'valid target for Mundo (Reason: '.reason.')')
return 0
endfunction "}}}
" Returns True if the graph or preview windows are open in the current tab.
function! s:MundoIsVisible()"{{{
return bufwinnr(bufnr("__Mundo__")) != -1 ||
\ bufwinnr(bufnr("__Mundo_Preview__")) != -1
endfunction"}}}
" Open/reopen Mundo for the current buffer, initialising the python module if
" necessary.
function! s:MundoOpen() abort "{{{
" Validate current buffer
if !s:MundoValidateBuffer()
return
endif
let g:mundo_target_n = bufnr('')
call s:MundoClose()
" Initialise python module if necessary
if !exists('g:mundo_py_loaded')
call s:MundoSetupPythonPath()
if s:has_supported_python == 2
exe 'py3file ' . escape(s:plugin_path, ' ') . '/mundo.py'
call s:InitPythonModule('python3')
else
exe 'pyfile ' . escape(s:plugin_path, ' ') . '/mundo.py'
call s:InitPythonModule('python')
endif
let g:mundo_py_loaded = 1
endif
" Save and reset `splitbelow` to avoid window positioning problems
let saved_splitbelow = &splitbelow
let &splitbelow = 0
" Temporarily disable automatic previews until Mundo is opened
let saved_auto_preview = g:mundo_auto_preview
let g:mundo_auto_preview = 0
" Create / open graph and preview windows
call s:MundoOpenPreview()
call mundo#util#GoToBuffer(g:mundo_target_n)
call s:MundoOpenGraph()
" Render the graph and preview, ensure the cursor is on a graph node
call s:MundoPythonRestoreView('MundoRenderGraph(True)')
call s:MundoRenderPreview()
call s:MundoPython('MundoMove(0,0)')
" Restore `splitbelow` and automatic preview option
let &splitbelow = saved_splitbelow
let g:mundo_auto_preview = saved_auto_preview
endfunction"}}}
function! s:MundoToggle()"{{{
if s:MundoIsVisible()
call s:MundoClose()
else
call s:MundoOpen()
endif
endfunction"}}}
function! s:MundoShow()"{{{
if !s:MundoIsVisible()
call s:MundoOpen()
endif
endfunction"}}}
function! s:MundoHide()"{{{
call s:MundoSetupPythonPath()
if s:MundoIsVisible()
call s:MundoClose()
endif
endfunction"}}}
"}}}
"{{{ Mundo mouse handling
function! s:MundoMouseDoubleClick()"{{{
let start_line = getline('.')
if stridx(start_line, '[') == -1
return
else
call <sid>MundoPythonRestoreView('MundoRevert()')
endif
endfunction"}}}
"}}}
"{{{ Mundo rendering
function! s:MundoPython(fn)"{{{
exec "python".(s:has_supported_python == 2 ? '3' : '')." ". a:fn
endfunction"}}}
" Wrapper for MundoPython() that restores the window state and prevents other
" Mundo autocommands (with the exception of BufNewFile) from triggering.
function! s:MundoPythonRestoreView(fn)"{{{
" Store view data, mode, window and 'evntignore' value
let currentmode = mode()
let currentWin = winnr()
let winView = winsaveview()
let eventignoreBack = &eventignore
set eventignore=BufLeave,BufEnter,CursorHold,CursorMoved,TextChanged
\,InsertLeave
" Call python function
call s:MundoPython(a:fn)
" Restore view data
execute currentWin .'wincmd w'
call winrestview(winView)
exec 'set eventignore='.eventignoreBack
" Re-select visual selection
if currentmode == 'v' || currentmode == 'V' || currentmode == ''
execute 'normal! gv'
endif
endfunction"}}}
" Accepts an optional integer that forces rendering if nonzero.
function! s:MundoRenderPreview(...)"{{{
if !s:preview_outdated && (a:0 < 1 || !a:1)
return
endif
call s:MundoPythonRestoreView('MundoRenderPreview()')
endfunction"}}}
"}}}
"{{{ Misc
" automatically reload Mundo buffer if open
function! s:MundoRefresh()"{{{
" abort if Mundo is closed or cursor is in the preview window
let mundoWin = bufwinnr('__Mundo__')
let mundoPreWin = bufwinnr('__Mundo_Preview__')
let currentWin = bufwinnr('%')
if mundoWin == -1 || mundoPreWin == -1 || mundoPreWin == currentWin
return
endif
" Disable the automatic preview delay if vim lacks support for timers
if g:mundo_auto_preview_delay > 0 && !s:has_timers
let g:mundo_auto_preview_delay = 0
call mundo#util#Echo('WarningMsg',
\ 'The "g:mundo_auto_preview_delay" option requires'
\ .' support for timers. Please upgrade to either vim 8.0+'
\ .' (with +timers) or neovim to use this feature. Press '
\ .'any key to continue.')
" Prevent the warning being cleared
call getchar()
endif
" Handle normal refresh
if g:mundo_auto_preview_delay <= 0
call s:MundoPythonRestoreView('MundoRenderGraph()')
if g:mundo_auto_preview && currentWin == mundoWin && mode() == 'n'
call s:MundoRenderPreview()
endif
return
endif
" Handle delayed refresh
call s:MundoRestartRefreshTimer()
endfunction"}}}
function! s:MundoRestartRefreshTimer()"{{{
call s:MundoStopRefreshTimer()
let s:auto_preview_timer = timer_start(
\ get(g:, 'mundo_auto_preview_delay', 0),
\ function('s:MundoRefreshDelayed')
\ )
endfunction"}}}
function! s:MundoStopRefreshTimer()"{{{
if s:auto_preview_timer != -1
call timer_stop(s:auto_preview_timer)
let s:auto_preview_timer = -1
endif
endfunction"}}}
function! s:MundoRefreshDelayed(...)"{{{
" abort if Mundo is closed or cursor is in the preview window
let mundoWin = bufwinnr('__Mundo__')
let mundoPreWin = bufwinnr('__Mundo_Preview__')
let currentWin = bufwinnr('%')
if mundoWin == -1 || mundoPreWin == -1 || mundoPreWin == currentWin
return
endif
" Update graph
call s:MundoPythonRestoreView('MundoRenderGraph()')
" Update preview
if currentWin != mundoWin || !g:mundo_auto_preview
return
endif
if mode() != 'n'
call s:MundoRestartRefreshTimer()
return
endif
call s:MundoRenderPreview()
endfunction"}}}
" Mark the preview as being up-to-date (0) or outdated (1)
function! mundo#MundoPreviewOutdated(outdated)"{{{
if s:preview_outdated && !a:outdated
call s:MundoStopRefreshTimer()
endif
let s:preview_outdated = a:outdated
endfunction"}}}
augroup MundoAug
autocmd!
autocmd BufEnter __Mundo__ call mundo#MundoPreviewOutdated(1)
autocmd BufLeave __Mundo__
\ if g:mundo_auto_preview |
\ call s:MundoRenderPreview() |
\ call s:MundoStopRefreshTimer() |
\ endif |
autocmd BufEnter __Mundo__ call s:MundoSettingsGraph()
autocmd BufEnter __Mundo_Preview__ call s:MundoSettingsPreview()
autocmd CursorHold,CursorMoved,TextChanged,InsertLeave *
\ call s:MundoRefresh()
augroup END
"}}}
" Exposed functions{{{
function! mundo#MundoToggle()"{{{
call s:MundoToggle()
endfunction"}}}
function! mundo#MundoShow()"{{{
call s:MundoShow()
endfunction"}}}
function! mundo#MundoHide()"{{{
call s:MundoHide()
endfunction"}}}
"}}}
let &cpoptions = s:save_cpo
unlet s:save_cpo

View File

@ -0,0 +1,105 @@
import difflib
import itertools
# one line diff functions.
def one_line_diff_str(before,after,mx=15,pre=2):
"""
Return a summary of the differences between two strings, concatenated.
Parameters:
before - string before.
after - after string.
mx - the max number of strings.
pre - number of characters to show before diff (context)
Returns a string no longer than 'mx'.
"""
old = one_line_diff(before,after)
result = ''
firstEl = True
# TODO instead of using +addition+ and -subtraction- it'd be nice to be able
# to highlight the change w/o requiring the +/- chars.
for v in old:
# if the first element doesn't have a change, then don't include it.
v = escape_returns(v)
if firstEl:
firstEl = False
# add in pre character context:
if not (v.startswith('+') or v.startswith('-')) and result == '':
v = v[-pre:]
# when we're going to be bigger than our max limit, lets ensure that the
# trailing +/- appears in the text:
if len(result) + len(v) > mx:
if v.startswith('+') or v.startswith('-'):
result += v[:mx - len(result) - 1]
result += v[0]
break
result += v
return result
def escape_returns(result):
return result.replace('\n','\\n').replace('\r','\\r').replace('\t','\\t')
def one_line_diff(before, after):
"""
Return a summary of the differences between two arbitrary strings.
Returns a list of strings, summarizing all the changes.
"""
a, b, result = [], [], []
for line in itertools.chain(itertools.islice(
difflib.unified_diff(before.splitlines(),
after.splitlines()), 2, None), ['@@']):
if line.startswith('@@'):
result.extend(one_line_diff_raw('\n'.join(a), '\n'.join(b)))
a, b = [], []
continue
if not line.startswith('+'):
a.append(line[1:])
if not line.startswith('-'):
b.append(line[1:])
if after.endswith('\n') and not before.endswith('\n'):
if result:
result[-1] = result[-1][:-1] + '\n+'
else:
result = ['+\n+']
return result
def one_line_diff_raw(before,after):
s = difflib.SequenceMatcher(None,before,after)
results = []
for tag, i1, i2, j1, j2 in s.get_opcodes():
#print ("%7s a[%d:%d] (%s) b[%d:%d] (%s)" % (tag, i1, i2, before[i1:i2], j1, j2, after[j1:j2]))
if tag == 'equal':
_append_result(results,{
'equal': after[j1:j2]
})
if tag == 'insert':
_append_result(results,{
'plus': after[j1:j2]
})
elif tag == 'delete':
_append_result(results,{
'minus': before[i1:i2]
})
elif tag == 'replace':
_append_result(results,{
'minus': before[j1:j2],
'plus': after[j1:j2]
})
final_results = []
# finally, create a human readable string of information.
for v in results:
if 'minus' in v and 'plus' in v and len(v['minus']) > 0 and len(v['plus']) > 0:
final_results.append("-%s-+%s+"% (v['minus'],v['plus']))
elif 'minus' in v and len(v['minus']) > 0:
final_results.append("-%s-"% (v['minus']))
elif 'plus' in v and len(v['plus']) > 0:
final_results.append("+%s+"% (v['plus']))
elif 'equal' in v:
final_results.append("%s"% (v['equal']))
return final_results
def _append_result(results,val):
results.append(val)

View File

@ -0,0 +1,248 @@
import time
import util
# Mercurial's graphlog code -------------------------------------------------------
def asciiedges(seen, rev, parents):
"""adds edge info to changelog DAG walk suitable for ascii()"""
if rev not in seen:
seen.append(rev)
nodeidx = seen.index(rev)
knownparents = []
newparents = []
for parent in parents:
if parent in seen:
knownparents.append(parent)
else:
newparents.append(parent)
ncols = len(seen)
seen[nodeidx:nodeidx + 1] = newparents
edges = [(nodeidx, seen.index(p)) for p in knownparents]
if len(newparents) > 0:
edges.append((nodeidx, nodeidx))
if len(newparents) > 1:
edges.append((nodeidx, nodeidx + 1))
nmorecols = len(seen) - ncols
return nodeidx, edges, ncols, nmorecols
def get_nodeline_edges_tail(
node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
# Still going in the same non-vertical direction.
if n_columns_diff == -1:
start = max(node_index + 1, p_node_index)
tail = ["|", " "] * (start - node_index - 1)
tail.extend(["/", " "] * (n_columns - start))
return tail
else:
return ["\\", " "] * (n_columns - node_index - 1)
else:
return ["|", " "] * (n_columns - node_index - 1)
def draw_edges(edges, nodeline, interline):
for (start, end) in edges:
if start == end + 1:
interline[2 * end + 1] = "/"
elif start == end - 1:
interline[2 * start + 1] = "\\"
elif start == end:
interline[2 * start] = "|"
else:
nodeline[2 * end] = "+"
if start > end:
(start, end) = (end, start)
for i in range(2 * start + 1, 2 * end):
if nodeline[i] != "+":
nodeline[i] = "-"
def fix_long_right_edges(edges):
for (i, (start, end)) in enumerate(edges):
if end > start:
edges[i] = (start, end + 1)
def ascii(state, type, char, text, coldata, verbose):
"""prints an ASCII graph of the DAG
takes the following arguments (one call per node in the graph):
- Somewhere to keep the needed state in (init to asciistate())
- Column of the current node in the set of ongoing edges.
- Type indicator of node data == ASCIIDATA.
- Payload: (char, lines):
- Character to use as node's symbol.
- List of lines to display as the node's text.
- Edges; a list of (col, next_col) indicating the edges between
the current node and its parents.
- Number of columns (ongoing edges) in the current revision.
- The difference between the number of columns (ongoing edges)
in the next revision and the number of columns (ongoing edges)
in the current revision. That is: -1 means one column removed;
0 means no columns added or removed; 1 means one column added.
- Verbosity: if enabled then the graph prints an extra '|'
between each line of information.
Returns a string representing the output.
"""
idx, edges, ncols, coldiff = coldata
assert -2 < coldiff < 2
if coldiff == -1:
# Transform
#
# | | | | | |
# o | | into o---+
# |X / |/ /
# | | | |
fix_long_right_edges(edges)
# fix_nodeline_tail says whether to rewrite
#
# | | o | | | | o | |
# | | |/ / | | |/ /
# | o | | into | o / / # <--- fixed nodeline tail
# | |/ / | |/ /
# o | | o | |
fix_nodeline_tail = len(text) <= 2
# nodeline is the line containing the node character (typically o)
nodeline = ["|", " "] * idx
nodeline.extend([char, " "])
nodeline.extend(
get_nodeline_edges_tail(idx, state[1], ncols, coldiff,
state[0], fix_nodeline_tail))
# shift_interline is the line containing the non-vertical
# edges between this entry and the next
shift_interline = ["|", " "] * idx
if coldiff == -1:
n_spaces = 1
edge_ch = "/"
elif coldiff == 0:
n_spaces = 2
edge_ch = "|"
else:
n_spaces = 3
edge_ch = "\\"
shift_interline.extend(n_spaces * [" "])
shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
# draw edges from the current node to its parents
draw_edges(edges, nodeline, shift_interline)
# lines is the list of all graph lines to print
lines = [nodeline]
lines.append(shift_interline)
# make sure that there are as many graph lines as there are
# log strings
if any("/" in s for s in lines) or verbose:
while len(text) < len(lines):
text.append('')
if len(lines) < len(text):
extra_interline = ["|", " "] * (ncols + coldiff)
while len(lines) < len(text):
lines.append(extra_interline)
indentation_level = max(ncols, ncols + coldiff)
result = []
for (line, logstr) in zip(lines, text):
graph = "%-*s" % (2 * indentation_level, "".join(line))
if not graph.isspace():
result.append([graph, logstr])
# ... and start over
state[0] = coldiff
state[1] = idx
return result
def generate(verbose, num_header_lines, first_visible_line, last_visible_line, inline_graph, nodesData):
"""
Generate an array of the graph, and text describing the node of the graph.
"""
seen, state = [], [0, 0]
result = []
current = nodesData.current()
nodes, nmap = nodesData.make_nodes()
for node in nodes:
node.children = [n for n in nodes if n.parent == node]
def walk_nodes(nodes):
for node in nodes:
if node.parent:
yield (node, [node.parent])
else:
yield (node, [])
dag = sorted(nodes, key=lambda n: int(n.n), reverse=True)
dag = walk_nodes(dag)
line_number = num_header_lines
for idx, part in list(enumerate(dag)):
node, parents = part
if node.time:
age_label = age(int(node.time))
else:
age_label = 'Original'
line = '[%s] %s' % (node.n, age_label)
if node.n == current:
char = '@'
elif node.saved:
char = 'w'
else:
char = 'o'
show_inine_diff = inline_graph and line_number >= first_visible_line and line_number <= last_visible_line
preview_diff = nodesData.preview_diff(node.parent, node, False, show_inine_diff)
line = '[%s] %-10s %s' % (node.n, age_label, preview_diff)
new_lines = ascii(state, 'C', char, [line], asciiedges(seen, node, parents), verbose)
line_number += len(new_lines)
result.extend(new_lines)
util._undo_to(current)
return result
# Mercurial age function -----------------------------------------------------------
agescales = [("yr", 3600 * 24 * 365),
("mon", 3600 * 24 * 30),
("wk", 3600 * 24 * 7),
("dy", 3600 * 24),
("hr", 3600),
("min", 60)]
def age(ts):
'''turn a timestamp into an age string.'''
def plural(t, c):
if c == 1:
return t
return t + "s"
def fmt(t, c):
return "%d %s" % (int(c), plural(t, c))
now = time.time()
then = ts
if then > now:
return 'in the future'
delta = max(1, int(now - then))
if delta > agescales[0][1] * 2:
return time.strftime('%Y-%m-%d', time.gmtime(float(ts)))
for t, s in agescales:
n = delta // s
if n >= 2 or s == 1:
return '%s ago' % fmt(t, n)
return "<1 min ago"

View File

@ -0,0 +1,219 @@
import diff
import difflib
import itertools
import time
import util
# Python undo tree data structures and functions ----------------------------------
class Node(object):
def __init__(self, n, parent, time, curhead, saved):
self.n = int(n)
self.parent = parent
self.children = []
self.curhead = curhead
self.saved = saved
self.time = time
def __repr__(self):
return "[n=%s,parent=%s,time=%s,curhead=%s,saved=%s]" % \
(self.n,self.parent,self.time,self.curhead,self.saved)
class Nodes(object):
def __init__(self):
self.clear_cache()
def clear_cache(self):
self.changedtick = None
self.lines = {}
self.nodes_made = None
self.target_n = None
self.seq_last = None
self.clear_oneline_diffs()
def clear_oneline_diffs(self):
self.diffs = {}
self.diff_has_oneline = {}
def _validate_cache(self):
""" Checks if the targeted buffer number has changed, and if so
clears all cached data and stores the new target buffer number.
"""
target_n = int(util.vim().eval('g:mundo_target_n'))
if self.target_n != target_n:
self.clear_cache()
self.clear_oneline_diffs()
self.target_n = target_n
def _make_nodes(self, alts, nodes, parent=None):
p = parent
for alt in alts:
if not alt:
continue
curhead = 'curhead' in alt
saved = 'save' in alt
node = Node(n=alt['seq'], parent=p, time=alt['time'],
curhead=curhead, saved=saved)
nodes.append(node)
if alt.get('alt'):
self._make_nodes(alt['alt'], nodes, p)
p = node
def is_outdated(self):
""" Checks if the target buffer undo tree has changed since the last
update. Note that this moves to the target buffer.
"""
util._goto_window_for_buffer(int(util.vim().eval('g:mundo_target_n')))
return self.changedtick != util.vim().eval('b:changedtick')
def make_nodes(self):
# Clear cache if it is invalid
self._validate_cache()
# If the current changedtick is unchanged, we don't need to do
# anything:
if not self.is_outdated():
return self.nodes_made
ut = util.vim().eval('undotree()')
# TODO only compute new values (not all values)
nodes = []
root = Node(0, None, False, 0, 0)
self._make_nodes(ut['entries'], nodes, root)
nodes.append(root)
nmap = dict((node.n, node) for node in nodes)
# cache values for later use
self.seq_last = ut['seq_last']
self.nodes_made = (nodes, nmap)
self.changedtick = util.vim().eval('b:changedtick')
return self.nodes_made
def current(self):
""" Return the number of the current change. """
self._validate_cache()
nodes, nmap = self.make_nodes()
_curhead_l = list(itertools.dropwhile(lambda n: not n.curhead, nodes))
if _curhead_l:
current = _curhead_l[0].parent.n
else:
current = int(util.vim().eval('changenr()'))
return current
def _fmt_time(self,t):
return time.strftime('%Y-%m-%d %I:%M:%S %p', time.localtime(float(t)))
def _get_lines(self,node):
n = 0
if node:
n = node.n
if n not in self.lines:
util._undo_to(n)
self.lines[n] = util.vim().current.buffer[:]
return self.lines[n]
def change_preview_diff(self, before, after):
self._validate_cache()
key = "%s-%s-cpd"%(before.n,after.n)
if key in self.diffs:
return self.diffs[key]
util._goto_window_for_buffer(int(util.vim().eval('g:mundo_target_n')))
before_lines = self._get_lines(before)
after_lines = self._get_lines(after)
before_name = str(before.n or 'Original')
before_time = before.time and self._fmt_time(before.time) or ''
after_name = str(after.n or 'Original')
after_time = after.time and self._fmt_time(after.time) or ''
util._undo_to(self.current())
self.diffs[key] = list(difflib.unified_diff(before_lines, after_lines,
before_name, after_name,
before_time, after_time))
return self.diffs[key]
def preview_diff(self, before, after, unified=True, inline=False):
"""
Generate a diff comparing two versions of a file.
Parameters:
current - ?
before
after
unified - If True, generate a unified diff
inline - Generate a one line summary line.
"""
self._validate_cache()
bn = 0
an = 0
if not after.n: # we're at the original file
pass
elif not before.n: # we're at a pseudo-root state
an = after.n
else:
bn = before.n
an = after.n
key = "%s-%s-pd-%s"%(bn,an,unified)
needs_oneline = inline and key not in self.diff_has_oneline
if key in self.diffs and not needs_oneline:
return self.diffs[key]
if not after.n: # we're at the original file
before_lines = []
after_lines = self._get_lines(None)
before_name = 'n/a'
before_time = ''
after_name = 'Original'
after_time = ''
elif not before.n: # we're at a pseudo-root state
before_lines = self._get_lines(None)
after_lines = self._get_lines(after)
before_name = 'Original'
before_time = ''
after_name = str(after.n)
after_time = self._fmt_time(after.time)
else:
before_lines = self._get_lines(before)
after_lines = self._get_lines(after)
before_name = str(before.n)
before_time = self._fmt_time(before.time)
after_name = str(after.n)
after_time = self._fmt_time(after.time)
if unified:
self.diffs[key] = list(
difflib.unified_diff(before_lines, after_lines, before_name,
after_name, before_time, after_time)
)
elif inline:
maxwidth = int(util.vim().eval("winwidth(0)"))
self.diffs[key] = diff.one_line_diff_str(
'\n'.join(before_lines),'\n'.join(after_lines), maxwidth
)
self.diff_has_oneline[key] = True
else:
self.diffs[key] = ""
return self.diffs[key]

View File

@ -0,0 +1,50 @@
# import vim
normal = lambda s: vim().command('normal %s' % s)
normal_silent = lambda s: vim().command('silent! normal %s' % s)
def vim():
""" call Vim.
This is wrapped so that it can easily be mocked.
"""
import vim
return vim
def _goto_window_for_buffer(expr):
""" Moves the cursor to the first window associated with buffer b in the
current tab page (only).
Arguments
---------
expr : int or str
The target buffer - either a buffer number (int) or a file-pattern
(str). See :h bufwinnr for a more detailed description.
"""
if not isinstance(expr, int) and not isinstance(expr, str):
raise TypeError('b has invalid type, str or int expected.')
if isinstance(expr, str):
expr = "'{0}'".format(expr)
winnr = int(vim().eval('bufwinnr({0})'.format(expr)))
assert winnr != -1
vim().command('%dwincmd w' % int(winnr))
# Rendering utility functions
def _output_preview_text(lines):
""" Output a list of lines to the mundo preview window. """
_goto_window_for_buffer('__Mundo_Preview__')
vim().command('setlocal modifiable')
vim().current.buffer[:] = [line.rstrip() for line in lines]
vim().command('setlocal nomodifiable')
def _undo_to(n):
n = int(n)
if n == 0:
vim().command('silent earlier %s' % (int(vim().eval('&undolevels')) + 1))
else:
vim().command('silent undo %d' % int(n))

View File

@ -0,0 +1,116 @@
" ============================================================================
" File: util.vim
" Description: Defines utility functions and default option values for Mundo.
" Maintainer: Hyeon Kim <simnalamburt@gmail.com>
" License: GPLv2+
" ============================================================================
let s:save_cpo = &cpo
set cpo&vim
if exists('g:Mundo_PluginLoaded')
let &cpo = s:save_cpo
finish
endif
" Utility functions{{{
" Moves to the first window in the current tab corresponding to expr. Accepts
" an integer buffer number or a string file-pattern; for a detailed description
" see :h bufname. Returns 1 if successful, 0 otherwise.
function! mundo#util#GoToBuffer(expr)"{{{
let l:winnr = bufwinnr(bufnr(a:expr))
if l:winnr == -1
return 0
elseif l:winnr != winnr()
exe l:winnr . "wincmd w"
endif
return 1
endfunction"}}}
" Similar to MundoGoToBuffer, but considers windows in all tabs.
" Prioritises matches in the current tab.
function! mundo#util#GoToBufferGlobal(expr)"{{{
if mundo#util#GoToBuffer(a:expr)
return 1
endif
let l:bufWinIDs = win_findbuf(bufnr(a:expr))
if len(l:bufWinIDs) <= 0
return 0
endif
call win_gotoid(l:bufWinIDs[0])
return 1
endfunction"}}}
" Prints a highlighted string.
function! mundo#util#Echo(higroup, text)"{{{
execute 'echohl ' . a:higroup
execute 'unsilent echomsg ' . '"' . escape(a:text, '"') . '"'
echohl None
endfunction"}}}
" Set var to val only if var has not been set by the user. Optionally takes a
" deprecated option name and shows a warning if a variable with this name exists.
function! mundo#util#set_default(var, val, ...)"{{{
if !exists(a:var)
let {a:var} = a:val
return 1
endif
let old_var = get(a:000, 0, '')
if exists(old_var)
call mundo#util#Echo(
\ 'WarningMsg',
\ "{".old_var."}is deprecated! "
\ ."Please change your setting to {"
\ .split(old_var,':')[0]
\ .':'
\ .substitute(split(old_var,':')[1],'gundo_','mundo_','g')
\ .'}'
)
endif
return 0
endfunction
"}}}
"}}}
" Placeholder functions for deprecated Gundo commands{{{
function! mundo#util#Toggle()
return mundo#util#Echo('WarningMsg', 'GundoToggle commands are '
\ . 'deprecated. Please change to their corresponding '
\ . 'MundoToggle command.')
endf
function! mundo#util#Show()
return mundo#util#Echo('WarningMsg', 'GundoToggle commands are '
\ . 'deprecated. Please change to their corresponding '
\ . 'MundoShow command.')
endf
function! mundo#util#Hide()
return mundo#util#Echo('WarningMsg', 'GundoToggle commands are '
\ . 'deprecated. Please change to their corresponding '
\ . 'MundoHide command.')
endf
function! mundo#util#RenderGraph()
return mundo#util#Echo('WarningMsg', 'GundoToggle commands are '
\ . 'deprecated. Please change to their corresponding '
\ . 'MundoRenderGraph command.')
endf
"}}}
let g:Mundo_PluginLoaded = 1
let &cpo = s:save_cpo
unlet s:save_cpo

View File

@ -0,0 +1,23 @@
import nose
from nose.tools import *
import mundo.diff as difflib
def test_one_line_diff():
eq_(difflib.one_line_diff('', ''), [])
eq_(difflib.one_line_diff('', 'a'), ['+a+'])
eq_(difflib.one_line_diff('a', 'b'), ['-a-+b+'])
eq_(difflib.one_line_diff('', 'a\nb\nc\n'), ['+a\nb\nc\n+'])
eq_(difflib.one_line_diff('one\ntwo', 'two\nthree'), ['-one\n-', 'two', '+\nthree+'])
eq_(difflib.one_line_diff('b\nd\ne\nf', 'm\nn\no\np\n'), ['-b-+m+', '\n', '-d-+n+', '\n', '-e-+o+', '\n', '-f-+p\n+'])
eq_(difflib.one_line_diff('m\nd\ne\nf', 'moon\nn\no\np\n'), ['m', '+oon+', '\n', '-\n-+n+', '\n', '+o+', '\n', '+p\n+'])
eq_(difflib.one_line_diff('m\nbagman', 'm\nbadger'), ['m\nba', '+d+', 'g', '-an-+er+'])
eq_(difflib.one_line_diff('', '1234567890abcdefghij'), ['+1234567890abcdefghij+'])
def test_one_line_diff_str():
eq_(difflib.one_line_diff_str('', ''), '')
eq_(difflib.one_line_diff_str('one\ntwo', 'two\nthree'), '-one\\n-two+\\nt+')
eq_(difflib.one_line_diff_str('m\nd\ne\nf', 'moon\nn\no\np\n'), 'm+oon+\\n-\\n-+n+')
eq_(difflib.one_line_diff_str('m\nbagman', 'm\nbadger'), 'ba+d+g-an-+er+')
# when the '+' is over the cuttoff, it should be appended:
eq_(difflib.one_line_diff_str('', '1234567890abcdefghij'), '+1234567890abc+')
eq_(difflib.one_line_diff_str('one\n\ntwo', 'one\n\ntwo\n\nthree\n\nfour'), 'wo+\\n\\nthree\\n+')

View File

@ -0,0 +1,419 @@
*mundo.txt* Graph your undo tree so you can actually USE it.
Making Vim's undo tree usable by humans.
==============================================================================
CONTENTS *Mundo-contents*
1. Intro ........................... |MundoIntro|
2. Usage ........................... |MundoUsage|
3. Configuration ................... |MundoConfig|
3.1 mundo_width ............... |mundo_width|
3.2 mundo_preview_height ...... |mundo_preview_height|
3.3 mundo_preview_bottom ...... |mundo_preview_bottom|
3.4 mundo_right ............... |mundo_right|
3.5 mundo_help ................ |mundo_help|
3.6 mundo_disable ............. |mundo_disable|
3.7 mundo_mappings ............ |mundo_mappings|
3.8 mundo_close_on_revert ..... |mundo_close_on_revert|
3.9 mundo_preview_statusline .. |mundo_preview_statusline|
mundo_tree_statusline ..... |mundo_tree_statusline|
3.10 mundo_auto_preview ........ |mundo_auto_preview|
3.11 mundo_auto_preview_delay .. |mundo_auto_preview_delay|
3.12 mundo_verbose_graph ....... |mundo_verbose_graph|
3.13 mundo_playback_delay ...... |mundo_playback_delay|
3.14 mundo_mirror_graph ........ |mundo_mirror_graph|
3.15 mundo_inline_undo ......... |mundo_inline_undo|
3.16 mundo_return_on_revert .... |mundo_return_on_revert|
3.17 mundo_header .............. |mundo_header|
4. License ......................... |MundoLicense|
5. Bugs ............................ |MundoBugs|
6. Contributing .................... |MundoContributing|
7. Changelog ....................... |MundoChangelog|
8. Credits ......................... |MundoCredits|
==============================================================================
1. Intro *MundoIntro*
You know that Vim lets you undo changes like any text editor. What you might
not know is that it doesn't just keep a list of your changes -- it keeps
a goddamed |:undo-tree| of them.
Say you make a change (call it X), undo that change, and then make another
change (call it Y). With most editors, change X is now gone forever. With Vim
you can get it back.
The problem is that trying to do this in the real world is painful. Vim gives
you an |:undolist| command that shows you the leaves of the tree. Good luck
finding the change you want in that list.
Mundo is a plugin to make browsing this ridiculously powerful undo tree less
painful.
==============================================================================
2. Usage *MundoUsage*
We'll get to the technical details later, but if you're a human the first
thing you need to do is add a mapping to your |:vimrc| to toggle the undo
graph: >
nnoremap <F5> :MundoToggle<CR>
Change the mapped key to suit your taste. We'll stick with F5 because that's
what the author uses.
Now you can press F5 to toggle the undo graph and preview pane, which will
look something like this: >
Undo graph File
+-----------------------------------+------------------------------------+
| " Mundo for something.txt [1] |one |
| " j/k - move between undo states |two |
| " <cr> - revert to that state |three |
| |five |
| @ [5] 3 hours ago | |
| | | |
| | o [4] 4 hours ago | |
| | | | |
| o | [3] 4 hours ago | |
| | | | |
| w | [2] 4 hours ago | |
| |/ | |
| o [1] 4 hours ago | |
| | | |
| o [0] Original | |
+-----------------------------------+ |
| --- 3 2010-10-12 06:27:35 PM | |
| +++ 5 2010-10-12 07:38:37 PM | |
| @@ -1,3 +1,4 | |
| one | |
| two | |
| three | |
| +five | |
+-----------------------------------+------------------------------------+
Preview pane
Your current position in the undo tree is marked with an '@' character. Undo
positions that were saved to disk are marked with a 'w'. Other nodes are marked
with an 'o' character.
When you toggle open the graph Mundo will put your cursor on your current
position in the tree. You can move up and down the graph with the j and
k keys.
You can move to the top of the graph (the newest state) with gg and to the
bottom of the graph (the oldest state) with G.
As you move between undo states the preview pane will show you a unified diff
of the change that state made.
Pressing enter on a state (or double clicking on it) will revert the contents
of the file to match that state.
You can use p on a state to make the preview window show the diff between
your current state and the selected state, instead of a preview of what the
selected state changed.
Pressing P while on a state will initiate "play to" mode targeted at that
state. This will replay all the changes between your current state and the
target, with a slight pause after each change. It's mostly useless, but can be
fun to watch and see where your editing lags -- that might be a good place to
define a new mapping to speed up your editing.
Pressing q while in the undo graph will close it. You can also just press your
toggle mapping key.
==============================================================================
3. Configuration *MundoConfig*
You can tweak the behavior of Mundo by setting a few variables in your :vimrc
file. For example: >
let g:mundo_width = 60
let g:mundo_preview_height = 40
let g:mundo_right = 1
------------------------------------------------------------------------------
3.1 g:mundo_width *mundo_width*
Set the horizontal width of the Mundo graph (and preview).
Default: 45
------------------------------------------------------------------------------
3.2 g:mundo_preview_height *mundo_preview_height*
Set the vertical height of the Mundo preview.
Default: 15
------------------------------------------------------------------------------
3.3 g:mundo_preview_bottom *mundo_preview_bottom*
Force the preview window below current windows instead of below the graph.
This gives the preview window more space to show the unified diff.
Example:
+--------+ +--------+
!g! ! ! !g!
!g! ! or ! !g!
!g!______! !______!g!
!g!pppppp! !pppppp!g!
+--------+ +--------+
Default: 0
------------------------------------------------------------------------------
3.4 g:mundo_right *mundo_right*
Set this to 1 to make the Mundo graph (and preview) open on the right side
instead of the left.
Default: 0 (off, open on the left side)
------------------------------------------------------------------------------
3.5 g:mundo_help *mundo_help*
Set this to 1 to make help text expanded by default.
Default: 0 (non-expanded help)
------------------------------------------------------------------------------
3.6 g:mundo_disable *mundo_disable*
Set this to 1 to disable Mundo entirely.
Useful if you use the same ~/.vim folder on multiple machines, and some of
them may not have Python support.
Default: 0 (Mundo is enabled as usual)
------------------------------------------------------------------------------
3.7 g:mundo_mappings *mundo_mappings*
*mundo_map_move_older*
*mundo_map_move_newer*
*mundo_map_up_down*
To override any mappings within mundo set the value of the normal mode keys you
would like to use instead (copy the defaults below and adjust as preferred)
Note: mundo_map_move_older, mundo_map_move_newer, mundo_map_up_down options have
been deprecated in favor of settings here. Although they continue to work, they
will eventually be removed!
Defaults:
let g:mundo_mappings = {
\ '<CR>': 'preview',
\ 'o': 'preview',
\ 'j': 'move_older',
\ 'k': 'move_newer',
\ '<down>': 'move_older',
\ '<up>': 'move_newer',
\ 'J': 'move_older_write',
\ 'K': 'move_newer_write',
\ 'gg': 'move_top',
\ 'G': 'move_bottom',
\ 'P': 'play_to',
\ 'd': 'diff',
\ 'i': 'toggle_inline',
\ '/': 'search',
\ 'n': 'next_match',
\ 'N': 'previous_match',
\ 'p': 'diff_current_buffer',
\ 'r': 'diff',
\ '?': 'toggle_help',
\ 'q': 'quit',
\ '<2-LeftMouse>': 'mouse_click' }
------------------------------------------------------------------------------
3.8 g:mundo_close_on_revert *mundo_close_on_revert*
Set this to 1 to automatically close the Mundo windows when reverting.
Default: 0 (windows do not automatically close)
------------------------------------------------------------------------------
3.9 g:mundo_preview_statusline *mundo_preview_statusline*
g:mundo_tree_statusline *mundo_tree_statusline*
Set these to a string to display it as the status line for each Mundo window.
Default: unset (windows use the default statusline)
------------------------------------------------------------------------------
3.10 g:mundo_auto_preview *mundo_auto_preview*
Set this to 0 to disable automatically rendering preview diffs as you move
through the undo tree (you can still render a specific diff with r). This can
be useful on large files and undo trees to speed up Mundo.
Default: 1 (automatically preview diffs)
------------------------------------------------------------------------------
3.11 g:mundo_auto_preview_delay *mundo_auto_preview_delay*
This is the delay in milliseconds before a preview diff is automatically
rendered. The delay is reset whenever the cursor moves. Use this to speed up
undo tree traversal when automatic previews are enabled. Set this to 0 to
disable the feature.
Default: 250
------------------------------------------------------------------------------
3.12 g:mundo_verbose_graph *mundo_verbose_graph*
Set this to 0 to create shorter graphs: the 'o' characters will only be used
when multiple branches exist, and extra lines of '|' are suppressed making for
a graph half as long.
Default: 1 (verbose graphs)
------------------------------------------------------------------------------
3.13 g:mundo_playback_delay *mundo_playback_delay*
This is the delay in milliseconds between each change when running 'play to'
mode. Set this to a higher number for a slower playback or to a lower number
for a faster playback.
Default: 60
------------------------------------------------------------------------------
3.14 g:mundo_mirror_graph *mundo_mirror_graph*
Set this to 0 to align the graph to the left; set to 1 to align to the right.
Default: 1 (mirror graph)
------------------------------------------------------------------------------
3.15 g:mundo_inline_undo *mundo_inline_undo*
When enabled, a small one line diff is displayed to the right of the graph
undo. Although not as detailed as a full diff provided in the preview window,
it provides a quick summary of the diff w/o having to navigate.
Default: 0 (no inline graph)
------------------------------------------------------------------------------
3.16 g:mundo_return_on_revert *mundo_return_on_revert*
Set this to 0 to keep focus in the Mundo window after a revert.
Default: 1
------------------------------------------------------------------------------
3.17 g:mundo_header *mundo_header*
Set this to 0 to hide the mundo header.
Default: 1 (display mundo header)
==============================================================================
4. License *MundoLicense*
GPLv2+. Look it up.
==============================================================================
5. Bugs *MundoBugs*
If you find a bug please post it on the issue tracker:
https://github.com/simnalamburt/vim-mundo/issues
==============================================================================
6. Contributing *MundoContributing*
Think you can make this plugin better? Awesome. Fork it on BitBucket or GitHub
and send a pull request.
Version numbers should roughly follow Semantic Versioning.
GitHub: https://github.com/simnalamburt/vim-mundo
==============================================================================
7. Changelog *MundoChangelog*
v3.2.0
* Adds g:mundo_mappings setting.
* Deprecates g:mundo_map_move_older, g:mundo_map_move_newer, g:mundo_map_up_down
v3.1.0
* Adds g:mundo_map_up_down setting.
* Adds g:mundo_auto_preview_delay setting.
* Adds validation for target buffers.
* Adds 'G' mapping to graph.
* Fix a runtime error when entering embedded terminal windows in neovim.
* Fix multiple issues associated with Mundo windows in different tabs.
* Fix default option values overriding user-set values in certain cases.
* Fix cursor initially being positioned above the graph.
* Fix some mappings not working correctly when preceded with a count.
* Fix some graph mappings moving the cursor to the target buffer.
* Fix airline plugin ignoring custom status lines.
* Fix Escape and Ctrl+c not being handled correctly in the search prompt.
* Fix MundoGetTargetState returning invalid results with some inline diffs.
* Reduced delay associated with animated playback for neovim.
* Code cleanup and refactoring.
v3.0.1
* Made inline diff mode much faster(e.g. 300x)
* Fix inline diffs not updating after scroll.
* Fix help toggle mode.
v3.0.0
* Rename plugin
* Add one line diff in __Mundo__ window.
* Adds g:mundo_mirror_graph setting.
* Adds g:mundo_return_on_revert setting.
* Adds ability to toggle help on/off in __Mundo__ window.
* Adds J and K commands (navigate between written undos)
* Adds /, n and N commands (search undos)
* Adds g:mundo_verbose_graph setting.
* Adds g:mundo_mirror_graph setting.
* Show written undos in the Mundo graph.
* Adds live reload of Mundo as you edit.
v2.5.0
* Fix the help window to take custom mappings into account.
* Add g:mundo_playback_delay option.
v2.4.0
* Add auto preview option.
* Add 'r' mapping to preview current state.
* Add public mundo#MundoShow() and mundo#MundoHide() functions.
v2.3.0
* Add statusline configuration.
v2.2.2
* More performance improvements.
v2.2.1
* Refactoring and performance improvements.
v2.2.0
* Add the g:mundo_close_on_revert setting.
* Fix a bug with the splitbelow setting.
v2.1.1
* Fix a bug with the movement key mappings.
* Warnings about having an incompatible Vim and/or Python installation
v2.1.0
are now deferred until the first time you try to use Mundo, instead
of being displayed on launch.
* The <j> and <k> mappings are now configurable with
g:mundo_map_move_older and g:mundo_map_move_newer.
* The o, <Up> and <Down> keys are now mapped in the Mundo pane.
* Improve and add several unit tests for Mundo.
v2.0.0
* Make MundoToggle close the Mundo windows if they're visible but not the
current window, instead of moving to them.
* Add the g:mundo_help setting.
* Add the g:mundo_disable setting.
* Add the 'p' mapping to preview the result of reverting to the selected
state.
* Fix movement commands with counts in the graph.
v1.0.0
* Initial stable release.
==============================================================================
8. Credits *MundoCredits*
The graphing code was all taken from Mercurial, hence the GPLv2+ license.
The plugin was heavily inspired by histwin.vim, and the code for scratch.vim
helped the author get started.
==============================================================================

View File

@ -0,0 +1,140 @@
" ============================================================================
" File: mundo.vim
" Description: vim global plugin to visualize your undo tree
" Maintainer: Hyeon Kim <simnalamburt@gmail.com>
" License: GPLv2+ -- look it up.
" Notes: Much of this code was thiefed from Mercurial, and the rest was
" heavily inspired by scratch.vim and histwin.vim.
"
" ============================================================================
if !exists('g:mundo_debug') && (exists('g:mundo_disable') &&
\ g:mundo_disable == 1 || exists('loaded_mundo') || &cp)"{{{
finish
endif
let loaded_mundo = 1"}}}
" Default option values{{{
call mundo#util#set_default(
\ 'g:mundo_auto_preview', 1,
\ 'g:gundo_auto_preview')
call mundo#util#set_default('g:mundo_auto_preview_delay', 250)
call mundo#util#set_default(
\ 'g:mundo_close_on_revert', 0,
\ 'g:gundo_close_on_revert')
call mundo#util#set_default(
\ 'g:mundo_first_visible_line', 0,
\ 'g:gundo_first_visible_line')
call mundo#util#set_default('g:mundo_header', 1)
call mundo#util#set_default(
\ 'g:mundo_help', 0,
\ 'g:gundo_help')
call mundo#util#set_default(
\ 'g:mundo_inline_undo', 0,
\ 'g:gundo_inline_undo')
call mundo#util#set_default(
\ 'g:mundo_last_visible_line', 0,
\ 'g:gundo_last_visible_line')
call mundo#util#set_default(
\ 'g:mundo_map_move_newer', 'k',
\ 'g:gundo_map_move_newer')
call mundo#util#set_default(
\ 'g:mundo_map_move_older', 'j',
\ 'g:gundo_map_move_older')
call mundo#util#set_default(
\ 'g:mundo_map_up_down', 1,
\ 'g:gundo_map_up_down')
call mundo#util#set_default(
\ 'g:mundo_mirror_graph', 0,
\ 'g:gundo_mirror_graph')
call mundo#util#set_default(
\ 'g:mundo_playback_delay', 60,
\ 'g:gundo_playback_delay')
call mundo#util#set_default(
\ 'g:mundo_prefer_python3', 0,
\ 'g:gundo_prefer_python3')
call mundo#util#set_default(
\ 'g:mundo_preview_bottom', 0,
\ 'g:gundo_preview_bottom')
call mundo#util#set_default(
\ 'g:mundo_preview_height', 15,
\ 'g:gundo_preview_height')
call mundo#util#set_default(
\ 'g:mundo_python_path_setup', 0,
\ 'g:gundo_python_path_setup')
call mundo#util#set_default(
\ 'g:mundo_return_on_revert', 1,
\ 'g:gundo_return_on_revert')
call mundo#util#set_default(
\ 'g:mundo_right', 0,
\ 'g:gundo_right')
call mundo#util#set_default(
\ 'g:mundo_verbose_graph', 1,
\ 'g:gundo_verbose_graph')
call mundo#util#set_default(
\ 'g:mundo_width', 45,
\ 'g:gundo_width')
" Set up the default mappings, unless a g:mundo_mappings has already been
" provided
if mundo#util#set_default('g:mundo_mappings', {})
let g:mundo_mappings = {
\ '<CR>': 'preview',
\ 'o': 'preview',
\ 'J': 'move_older_write',
\ 'K': 'move_newer_write',
\ 'gg': 'move_top',
\ 'G': 'move_bottom',
\ 'P': 'play_to',
\ 'd': 'diff',
\ 'i': 'toggle_inline',
\ '/': 'search',
\ 'n': 'next_match',
\ 'N': 'previous_match',
\ 'p': 'diff_current_buffer',
\ 'r': 'diff',
\ '?': 'toggle_help',
\ 'q': 'quit',
\ '<2-LeftMouse>': 'mouse_click' }
let g:mundo_mappings[g:mundo_map_move_older] = 'move_older'
let g:mundo_mappings[g:mundo_map_move_newer] = 'move_newer'
if g:mundo_map_up_down
let g:mundo_mappings['<down>'] = 'move_older'
let g:mundo_mappings['<up>'] = 'move_newer'
endif
endif
"}}}
"{{{ Create commands
command! -nargs=0 MundoToggle call mundo#MundoToggle()
command! -nargs=0 MundoShow call mundo#MundoShow()
command! -nargs=0 MundoHide call mundo#MundoHide()
command! -nargs=0 GundoToggle call mundo#util#MundoToggle()
command! -nargs=0 GundoShow call mundo#util#MundoShow()
command! -nargs=0 GundoHide call mundo#util#MundoHide()
command! -nargs=0 GundoRenderGraph call mundo#util#MundoRenderGraph()
"}}}

View File

@ -0,0 +1,2 @@
nose
mock

View File

@ -0,0 +1,24 @@
Mundo Tests
========
This is Mundo's test suite. In case you didn't know, unit testing vim plugins
is a horrible experience.
Mundo bundles Luc Hermitte's [UT] framework to make the process less painful.
[UT]: https://github.com/LucHermitte/vim-UT
To run a test, `cd` into the `tests` directory and use:
```sh
./run-tests.sh [[some-test.vim] ...]
```
The script will run the console vim command with its own vimrc and .vim
directory, so none of your other plugins should interfere. The result of the
tests will be shown in the quickfix window of this vim instance.
The `q` key will be remapped to perform `:qa!` so you can close everything
quickly if it all looks good.
The `run-tests.sh` script is still a work in progress. I need to figure out
a good way of collecting results. Suggestions and/or patches are very welcome.

View File

@ -0,0 +1 @@
../../../../bundled/ut/autoload/lh/UT.vim

View File

@ -0,0 +1,147 @@
"=============================================================================
" $Id: askvim.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/askvim.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 17th Apr 2007
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $ (17th Apr 2007)
"------------------------------------------------------------------------
" Description:
" Defines functions that asks vim what it is relinquish to tell us
" - menu
"
"------------------------------------------------------------------------
" Installation:
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History:
" v2.0.0:
" TODO: «missing features»
" }}}1
"=============================================================================
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Functions {{{1
" # Debug {{{2
function! lh#askvim#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#askvim#debug(expr)
return eval(a:expr)
endfunction
"------------------------------------------------------------------------
" # Public {{{2
" Function: lh#askvim#exe(command) {{{3
function! lh#askvim#Exe(command)
echomsg 'lh#askvim#Exe() is deprecated, use lh#askvim#exe()'
return lh#askvim#exe(a:command)
endfunction
function! lh#askvim#exe(command)
let save_a = @a
try
silent! redir @a
silent! exe a:command
redir END
finally
" Always restore everything
let res = @a
let @a = save_a
return res
endtry
endfunction
" Function: lh#askvim#menu(menuid) {{{3
function! s:AskOneMenu(menuact, res)
let sKnown_menus = lh#askvim#exe(a:menuact)
let lKnown_menus = split(sKnown_menus, '\n')
" echo string(lKnown_menus)
" 1- search for the menuid
" todo: fix the next line to correctly interpret "stuff\.stuff" and
" "stuff\\.stuff".
let menuid_parts = split(a:menuact, '\.')
let simplifiedKnown_menus = deepcopy(lKnown_menus)
call map(simplifiedKnown_menus, 'substitute(v:val, "&", "", "g")')
" let idx = lh#list#match(simplifiedKnown_menus, '^\d\+\s\+'.menuid_parts[-1])
let idx = match(simplifiedKnown_menus, '^\d\+\s\+'.menuid_parts[-1])
if idx == -1
" echo "not found"
return
endif
" echo "l[".idx."]=".lKnown_menus[idx]
if empty(a:res)
let a:res.priority = matchstr(lKnown_menus[idx], '\d\+\ze\s\+.*')
let a:res.name = matchstr(lKnown_menus[idx], '\d\+\s\+\zs.*')
let a:res.actions = {}
" else
" what if the priority isn't the same?
endif
" 2- search for the menu definition
let idx += 1
while idx != len(lKnown_menus)
echo "l[".idx."]=".lKnown_menus[idx]
" should not happen
if lKnown_menus[idx] =~ '^\d\+' | break | endif
" :h showing-menus
" -> The format of the result of the call to Exe() seems to be:
" ^ssssMns-sACTION$
" s == 1 whitespace
" M == mode (inrvcs)
" n == noremap(*)/script(&)
" - == disable(-)/of not
let act = {}
let menu_def = matchlist(lKnown_menus[idx],
\ '^\s*\([invocs]\)\([&* ]\) \([- ]\) \(.*\)$')
if len(menu_def) > 4
let act.mode = menu_def[1]
let act.nore_script = menu_def[2]
let act.disabled = menu_def[3]
let act.action = menu_def[4]
else
echomsg string(menu_def)
echoerr "lh#askvim#menu(): Cannot decode ``".lKnown_menus[idx]."''"
endif
let a:res.actions["mode_" . act.mode] = act
let idx += 1
endwhile
" n- Return the result
return a:res
endfunction
function! lh#askvim#menu(menuid, modes)
let res = {}
let i = 0
while i != strlen(a:modes)
call s:AskOneMenu(a:modes[i].'menu '.a:menuid, res)
let i += 1
endwhile
return res
endfunction
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,97 @@
"=============================================================================
" $Id: buffer.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/buffer.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 23rd Jan 2007
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description:
" Defines functions that help finding windows and handling buffers.
"
"------------------------------------------------------------------------
" Installation:
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History:
" v 1.0.0 First Version
" (*) Functions moved from searchInRuntimeTime
" v 2.2.0
" (*) new function: lh#buffer#list()
" TODO:
" }}}1
"=============================================================================
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
" ## Functions {{{1
"------------------------------------------------------------------------
" # Debug {{{2
function! lh#buffer#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#buffer#debug(expr)
return eval(a:expr)
endfunction
"------------------------------------------------------------------------
" # Public {{{2
" Function: lh#buffer#find({filename}) {{{3
" If {filename} is opened in a window, jump to this window, otherwise return -1
" Moved from searchInRuntimeTime.vim
function! lh#buffer#find(filename)
let b = bufwinnr(a:filename)
if b == -1 | return b | endif
exe b.'wincmd w'
return b
endfunction
function! lh#buffer#Find(filename)
return lh#buffer#find(a:filename)
endfunction
" Function: lh#buffer#jump({filename},{cmd}) {{{3
function! lh#buffer#jump(filename, cmd)
if lh#buffer#find(a:filename) != -1 | return | endif
exe a:cmd . ' ' . a:filename
endfunction
function! lh#buffer#Jump(filename, cmd)
return lh#buffer#jump(a:filename, a:cmd)
endfunction
" Function: lh#buffer#scratch({bname},{where}) {{{3
function! lh#buffer#scratch(bname, where)
try
silent exe a:where.' sp '.a:bname
catch /.*/
throw "Can't open a buffer named '".a:bname."'!"
endtry
setlocal bt=nofile bh=wipe nobl noswf ro
endfunction
function! lh#buffer#Scratch(bname, where)
return lh#buffer#scratch(a:bname, a:where)
endfunction
" Function: lh#buffer#list() {{{3
function! lh#buffer#list()
let all = range(0, bufnr('$'))
" let res = lh#list#transform_if(all, [], 'v:1_', 'buflisted')
let res = lh#list#copy_if(all, [], 'buflisted')
return res
endfunction
" Ex: echo lh#list#transform(lh#buffer#list(), [], "bufname")
"=============================================================================
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,268 @@
"=============================================================================
" $Id: dialog.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/buffer/dialog.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 21st Sep 2007
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description: «description»
"
"------------------------------------------------------------------------
" Installation:
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History:
" v 1.0.0 First Version
" (*) Functions imported from Mail_mutt_alias.vim
" TODO:
" (*) --abort-- line
" (*) custom messages
" (*) do not mess with search history
" (*) support any &magic
" (*) syntax
" (*) add number/letters
" (*) tag with '[x] ' instead of '* '
" }}}1
"=============================================================================
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"=============================================================================
" ## Globals {{{1
let s:LHdialog = {}
"=============================================================================
" ## Functions {{{1
" # Debug {{{2
function! lh#buffer#dialog#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#buffer#dialog#debug(expr)
return eval(a:expr)
endfunction
"=============================================================================
" # Dialog functions {{{2
"------------------------------------------------------------------------
function! s:Mappings(abuffer)
" map <enter> to edit a file, also dbl-click
exe "nnoremap <silent> <buffer> <esc> :silent call ".a:abuffer.action."(-1, ".a:abuffer.id.")<cr>"
exe "nnoremap <silent> <buffer> q :call lh#buffer#dialog#select(-1, ".a:abuffer.id.")<cr>"
exe "nnoremap <silent> <buffer> <cr> :call lh#buffer#dialog#select(line('.'), ".a:abuffer.id.")<cr>"
" nnoremap <silent> <buffer> <2-LeftMouse> :silent call <sid>GrepEditFileLine(line("."))<cr>
" nnoremap <silent> <buffer> Q :call <sid>Reformat()<cr>
" nnoremap <silent> <buffer> <Left> :set tabstop-=1<cr>
" nnoremap <silent> <buffer> <Right> :set tabstop+=1<cr>
if a:abuffer.support_tagging
nnoremap <silent> <buffer> t :silent call <sid>ToggleTag(line("."))<cr>
nnoremap <silent> <buffer> <space> :silent call <sid>ToggleTag(line("."))<cr>
endif
nnoremap <silent> <buffer> <tab> :silent call <sid>NextChoice('')<cr>
nnoremap <silent> <buffer> <S-tab> :silent call <sid>NextChoice('b')<cr>
exe "nnoremap <silent> <buffer> h :silent call <sid>ToggleHelp(".a:abuffer.id.")<cr>"
endfunction
"----------------------------------------
" Tag / untag the current choice {{{
function! s:ToggleTag(lineNum)
if a:lineNum > s:Help_NbL()
" If tagged
if (getline(a:lineNum)[0] == '*')
let b:NbTags = b:NbTags - 1
silent exe a:lineNum.'s/^\* / /e'
else
let b:NbTags = b:NbTags + 1
silent exe a:lineNum.'s/^ /* /e'
endif
" Move after the tag ; there is something with the two previous :s. They
" don't leave the cursor at the same position.
silent! normal! 3|
call s:NextChoice('') " move to the next choice
endif
endfunction
" }}}
function! s:Help_NbL()
" return 1 + nb lines of BuildHelp
return 2 + len(b:dialog['help_'.b:dialog.help_type])
endfunction
"----------------------------------------
" Go to the Next (/previous) possible choice. {{{
function! s:NextChoice(direction)
" echomsg "next!"
call search('^[ *]\s*\zs\S\+', a:direction)
endfunction
" }}}
"------------------------------------------------------------------------
function! s:RedisplayHelp(dialog)
silent! 2,$g/^@/d_
normal! gg
for help in a:dialog['help_'.a:dialog.help_type]
silent put=help
endfor
endfunction
function! lh#buffer#dialog#update(dialog)
set noro
exe (s:Help_NbL()+1).',$d_'
for choice in a:dialog.choices
silent $put=' '.choice
endfor
set ro
endfunction
function! s:Display(dialog, atitle)
set noro
0 put = a:atitle
call s:RedisplayHelp(a:dialog)
for choice in a:dialog.choices
silent $put=' '.choice
endfor
set ro
exe s:Help_NbL()+1
endfunction
function! s:ToggleHelp(bufferId)
call lh#buffer#find(a:bufferId)
call b:dialog.toggle_help()
endfunction
function! lh#buffer#dialog#toggle_help() dict
let self.help_type
\ = (self.help_type == 'short')
\ ? 'long'
\ : 'short'
call s:RedisplayHelp(self)
endfunction
function! lh#buffer#dialog#new(bname, title, where, support_tagging, action, choices)
" The ID will be the buffer id
let res = {}
let where_it_started = getpos('.')
let where_it_started[0] = bufnr('%')
let res.where_it_started = where_it_started
try
call lh#buffer#scratch(a:bname, a:where)
catch /.*/
echoerr v:exception
return res
endtry
let res.id = bufnr('%')
let b:NbTags = 0
let b:dialog = res
let s:LHdialog[res.id] = res
let res.help_long = []
let res.help_short = []
let res.help_type = 'short'
let res.support_tagging = a:support_tagging
let res.action = a:action
let res.choices = a:choices
" Long help
call lh#buffer#dialog#add_help(res, '@| <cr>, <double-click> : select this', 'long')
call lh#buffer#dialog#add_help(res, '@| <esc>, q : Abort', 'long')
if a:support_tagging
call lh#buffer#dialog#add_help(res, '@| <t>, <space> : Tag/Untag the current item', 'long')
endif
call lh#buffer#dialog#add_help(res, '@| <up>/<down>, <tab>, +/- : Move between entries', 'long')
call lh#buffer#dialog#add_help(res, '@|', 'long')
" call lh#buffer#dialog#add_help(res, '@| h : Toggle help', 'long')
call lh#buffer#dialog#add_help(res, '@+'.repeat('-', winwidth(bufwinnr(res.id))-3), 'long')
" Short Help
" call lh#buffer#dialog#add_help(res, '@| h : Toggle help', 'short')
call lh#buffer#dialog#add_help(res, '@+'.repeat('-', winwidth(bufwinnr(res.id))-3), 'short')
let res.toggle_help = function("lh#buffer#dialog#toggle_help")
let title = '@ ' . a:title
let helpstr = '| Toggle (h)elp'
let title = title
\ . repeat(' ', winwidth(bufwinnr(res.id))-strlen(title)-strlen(helpstr)-1)
\ . helpstr
call s:Display(res, title)
call s:Mappings(res)
return res
endfunction
function! lh#buffer#dialog#add_help(abuffer, text, help_type)
call add(a:abuffer['help_'.a:help_type],a:text)
endfunction
"=============================================================================
function! lh#buffer#dialog#quit()
let bufferId = b:dialog.where_it_started[0]
echohl WarningMsg
echo "Abort"
echohl None
quit
call lh#buffer#find(bufferId)
endfunction
" Function: lh#buffer#dialog#select(line, bufferId [,overriden-action])
function! lh#buffer#dialog#select(line, bufferId, ...)
if a:line == -1
call lh#buffer#dialog#quit()
return
" elseif a:line <= s:Help_NbL() + 1
elseif a:line <= s:Help_NbL()
echoerr "Unselectable item"
return
else
let dialog = s:LHdialog[a:bufferId]
let results = { 'dialog' : dialog, 'selection' : [] }
if b:NbTags == 0
" -1 because first index is 0
" let results = [ dialog.choices[a:line - s:Help_NbL() - 1] ]
let results.selection = [ a:line - s:Help_NbL() - 1 ]
else
silent g/^* /call add(results.selection, line('.')-s:Help_NbL()-1)
endif
endif
if a:0 > 0 " action overriden
exe 'call '.dialog.action.'(results, a:000)'
else
exe 'call '.dialog.action.'(results)'
endif
endfunction
function! lh#buffer#dialog#Select(line, bufferId, ...)
echomsg "lh#buffer#dialog#Select() is deprecated, use lh#buffer#dialog#select() instead"
if a:0 > 0 " action overriden
exe 'call lh#buffer#dialog#select(a:line, a:bufferId, a:1)'
else
exe 'call lh#buffer#dialog#select(a:line, a:bufferId)'
endif
endfunction
function! Action(results)
let dialog = a:results.dialog
let choices = dialog.choices
for r in a:results.selection
echomsg '-> '.choices[r]
endfor
endfunction
"=============================================================================
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,224 @@
"=============================================================================
" $Id: command.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/command.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 08th Jan 2007
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $ (08th Jan 2007)
"------------------------------------------------------------------------
" Description:
" Helpers to define commands that:
" - support subcommands
" - support autocompletion
"
"------------------------------------------------------------------------
" Installation:
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History:
" v2.0.0:
" Code move from other plugins
" TODO: «missing features»
" }}}1
"=============================================================================
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
" ## Debug {{{1
function! lh#command#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#command#debug(expr)
return eval(a:expr)
endfunction
"------------------------------------------------------------------------
" ## Functions {{{1
" Tool functions {{{2
" Function: lh#command#Fargs2String(aList) {{{3
" @param[in,out] aList list of params from <f-args>
" @see tests/lh/test-Fargs2String.vim
function! lh#command#Fargs2String(aList)
if empty(a:aList) | return '' | endif
let quote_char = a:aList[0][0]
let res = a:aList[0]
call remove(a:aList, 0)
if quote_char !~ '["'."']"
return res
endif
" else
let end_string = '[^\\]\%(\\\\\)*'.quote_char.'$'
while !empty(a:aList) && res !~ end_string
let res .= ' ' . a:aList[0]
call remove(a:aList, 0)
endwhile
return res
endfunction
"------------------------------------------------------------------------
" ## Experimental Functions {{{1
" Internal functions {{{2
" Function: s:SaveData({Data}) {{{3
" @param Data Command definition
" Saves {Data} as s:Data{s:data_id++}. The definition will be used by
" automatically generated commands.
" @return s:data_id
let s:data_id = 0
function! s:SaveData(Data)
if has_key(a:Data, "command_id")
" Avoid data duplication
return a:Data.command_id
else
let s:Data{s:data_id} = a:Data
let id = s:data_id
let s:data_id += 1
let a:Data.command_id = id
return id
endif
endfunction
" BTWComplete(ArgLead, CmdLine, CursorPos): Auto-complete {{{3
function! lh#command#complete(ArgLead, CmdLine, CursorPos)
let tmp = substitute(a:CmdLine, '\s*\S*', 'Z', 'g')
let pos = strlen(tmp)
if 0
call confirm( "AL = ". a:ArgLead."\nCL = ". a:CmdLine."\nCP = ".a:CursorPos
\ . "\ntmp = ".tmp."\npos = ".pos
\, '&Ok', 1)
endif
if 2 == pos
" First argument: a command
return s:commands
elseif 3 == pos
" Second argument: first arg of the command
if -1 != match(a:CmdLine, '^BTW\s\+echo')
return s:functions . "\n" . s:variables
elseif -1 != match(a:CmdLine, '^BTW\s\+\%(help\|?\)')
elseif -1 != match(a:CmdLine, '^BTW\s\+\%(set\|add\)\%(local\)\=')
" Adds a filter
" let files = globpath(&rtp, 'compiler/BT-*')
" let files = files . globpath(&rtp, 'compiler/BT_*')
" let files = files . globpath(&rtp, 'compiler/BT/*')
let files = s:FindFilter('*')
let files = substitute(files,
\ '\(^\|\n\).\{-}compiler[\\/]BTW[-_\\/]\(.\{-}\)\.vim\>\ze\%(\n\|$\)',
\ '\1\2', 'g')
return files
elseif -1 != match(a:CmdLine, '^BTW\s\+remove\%(local\)\=')
" Removes a filter
return substitute(s:FiltersList(), ',', '\n', 'g')
endif
endif
" finally: unknown
echoerr 'BTW: unespected parameter ``'. a:ArgLead ."''"
return ''
endfunction
function! s:BTW(command, ...)
" todo: check a:0 > 1
if 'set' == a:command | let g:BTW_build_tool = a:1
if exists('b:BTW_build_tool')
let b:BTW_build_tool = a:1
endif
elseif 'setlocal' == a:command | let b:BTW_build_tool = a:1
elseif 'add' == a:command | call s:AddFilter('g', a:1)
elseif 'addlocal' == a:command | call s:AddFilter('b', a:1)
" if exists('b:BTW_filters_list') " ?????
" call s:AddFilter('b', a:1)
" endif
elseif 'remove' == a:command | call s:RemoveFilter('g', a:1)
elseif 'removelocal' == a:command | call s:RemoveFilter('b', a:1)
elseif 'rebuild' == a:command " wait for s:ReconstructToolsChain()
elseif 'echo' == a:command | exe "echo s:".a:1
" echo s:{a:f1} ## don't support «echo s:f('foo')»
elseif 'reloadPlugin' == a:command
let g:force_reload_BuildToolsWrapper = 1
let g:BTW_BTW_in_use = 1
exe 'so '.s:sfile
unlet g:force_reload_BuildToolsWrapper
unlet g:BTW_BTW_in_use
return
elseif a:command =~ '\%(help\|?\)'
call s:Usage()
return
endif
call s:ReconstructToolsChain()
endfunction
" ##############################################################
" Public functions {{{2
function! s:FindSubcommand(definition, subcommand)
for arg in a:definition.arguments
if arg.name == a:subcommand
return arg
endif
endfor
throw "NF"
endfunction
function! s:execute_function(definition, params)
if len(a:params) < 1
throw "(lh#command) Not enough arguments"
endif
let l:Fn = a:definition.action
echo "calling ".string(l:Fn)
echo "with ".string(a:params)
" call remove(a:params, 0)
call l:Fn(a:params)
endfunction
function! s:execute_sub_commands(definition, params)
try
if len(a:params) < 1
throw "(lh#command) Not enough arguments"
endif
let subcommand = s:FindSubcommand(a:definition, a:params[0])
call remove(a:params, 0)
call s:int_execute(subcommand, a:params)
catch /NF.*/
throw "(lh#command) Unexpected subcommand `".a:params[0]."'."
endtry
endfunction
function! s:int_execute(definition, params)
echo "params=".string(a:params)
call s:execute_{a:definition.arg_type}(a:definition, a:params)
endfunction
function! s:execute(definition, ...)
try
let params = copy(a:000)
call s:int_execute(a:definition, params)
catch /(lh#command).*/
echoerr v:exception . " in `".a:definition.name.' '.join(a:000, ' ')."'"
endtry
endfunction
function! lh#command#new(definition)
let cmd_name = a:definition.name
" Save the definition as an internal script variable
let id = s:SaveData(a:definition)
exe "command! -nargs=* ".cmd_name." :call s:execute(s:Data".id.", <f-args>)"
endfunction
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,93 @@
"=============================================================================
" $Id: common.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/common.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 07th Oct 2006
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $ (08th Feb 2008)
"------------------------------------------------------------------------
" Description:
" Some common functions for:
" - displaying error messages
" - checking dependencies
"
"------------------------------------------------------------------------
" Installation:
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History:
" v2.1.1
" - New function: lh#common#echomsg_multilines()
" - lh#common#warning_msg() supports multilines messages
"
" v2.0.0:
" - Code moved from other plugins
" }}}1
"=============================================================================
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" Functions {{{1
" Function: lh#common#echomsg_multilines {{{2
function! lh#common#echomsg_multilines(text)
let lines = split(a:text, "[\n\r]")
for line in lines
echomsg line
endfor
endfunction
function! lh#common#echomsgMultilines(text)
return lh#common#echomsg_multilines(a:text)
endfunction
" Function: lh#common#error_msg {{{2
function! lh#common#error_msg(text)
if has('gui_running')
call confirm(a:text, '&Ok', '1', 'Error')
else
" echohl ErrorMsg
echoerr a:text
" echohl None
endif
endfunction
function! lh#common#ErrorMsg(text)
return lh#common#error_msg(a:text)
endfunction
" Function: lh#common#warning_msg {{{2
function! lh#common#warning_msg(text)
echohl WarningMsg
" echomsg a:text
call lh#common#echomsg_multilines(a:text)
echohl None
endfunction
function! lh#common#WarningMsg(text)
return lh#common#warning_msg(a:text)
endfunction
" Dependencies {{{2
function! lh#common#check_deps(Symbol, File, path, plugin) " {{{3
if !exists(a:Symbol)
exe "runtime ".a:path.a:File
if !exists(a:Symbol)
call lh#common#error_msg( a:plugin.': Requires <'.a:File.'>')
return 0
endif
endif
return 1
endfunction
function! lh#common#CheckDeps(Symbol, File, path, plugin) " {{{3
echomsg "lh#common#CheckDeps() is deprecated, use lh#common#check_deps() instead."
return lh#common#check_deps(a:Symbol, a:File, a:path, a:plugin)
endfunction
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,49 @@
"=============================================================================
" $Id: encoding.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/encoding.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 21st Feb 2008
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description:
" Defines functions that help managing various encodings
"
"------------------------------------------------------------------------
" Installation:
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History:
" v2.0.7:
" (*) lh#encoding#Iconv() copied from map-tools
" TODO: «missing features»
" }}}1
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" Function: lh#encoding#iconv(expr, from, to) " {{{3
" Unlike |iconv()|, this wrapper returns {expr} when we know no convertion can
" be acheived.
function! lh#encoding#iconv(expr, from, to)
" call Dfunc("s:ICONV(".a:expr.','.a:from.','.a:to.')')
if has('multi_byte') &&
\ ( has('iconv') || has('iconv/dyn') ||
\ ((a:from=~'latin1\|utf-8') && (a:to=~'latin1\|utf-8')))
" call confirm('encoding: '.&enc."\nto:".a:to, "&Ok", 1)
" call Dret("s:ICONV convert=".iconv(a:expr, a:from, a:to))
return iconv(a:expr,a:from,a:to)
else
" Cannot convert
" call Dret("s:ICONV no convert=".a:expr)
return a:expr
endif
endfunction
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,75 @@
"=============================================================================
" $Id: env.vim 244 2010-09-19 22:38:24Z luc.hermitte $
" File: autoload/lh/env.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 19th Jul 2010
" Last Update: $Date: 2010-09-19 18:38:24 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description:
" Functions related to environment (variables)
"
"------------------------------------------------------------------------
" Installation:
" Drop this file into {rtp}/autoload/lh
" Requires Vim7+
" History:
" v2.2.1 First Version
" TODO: «missing features»
" }}}1
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Misc Functions {{{1
" # Version {{{2
let s:k_version = 221
function! lh#env#version()
return s:k_version
endfunction
" # Debug {{{2
let s:verbose = 0
function! lh#env#verbose(...)
if a:0 > 0 | let s:verbose = a:1 | endif
return s:verbose
endfunction
function! s:Verbose(expr)
if s:verbose
echomsg a:expr
endif
endfunction
function! lh#env#debug(expr)
return eval(a:expr)
endfunction
"------------------------------------------------------------------------
" ## Exported functions {{{1
function! lh#env#expand_all(string)
let res = ''
let tail = a:string
while !empty(tail)
let [ all, head, var, tail; dummy ] = matchlist(tail, '\(.\{-}\)\%(${\(.\{-}\)}\)\=\(.*\)')
if empty(var)
let res .= tail
break
else
let res .= head
let val = eval('$'.var)
let res .= val
endif
endwhile
return res
endfunction
"------------------------------------------------------------------------
" ## Internal functions {{{1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,68 @@
"=============================================================================
" $Id: event.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/event.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 15th Feb 2008
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description:
" Function to help manage vim |autocommand-events|
"
"------------------------------------------------------------------------
" Installation:
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History:
" v2.0.6:
" Creation
" TODO:
" }}}1
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Functions {{{1
" # Debug {{{2
function! lh#event#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#event#debug(expr)
return eval(a:expr)
endfunction
"------------------------------------------------------------------------
" # Event Registration {{{2
function! s:RegisteredOnce(cmd, group)
" We can't delete the current augroup autocommand => increment a counter
if !exists('s:'.a:group) || s:{a:group} == 0
let s:{a:group} = 1
exe a:cmd
endif
endfunction
function! lh#event#register_for_one_execution_at(event, cmd, group)
let group = a:group.'_once'
let s:{group} = 0
exe 'augroup '.group
au!
exe 'au '.a:event.' '.expand('%:p').' call s:RegisteredOnce('.string(a:cmd).','.string(group).')'
augroup END
endfunction
function! lh#event#RegisterForOneExecutionAt(event, cmd, group)
return lh#event#register_for_one_execution_at(a:event, a:cmd, a:group)
endfunction
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,214 @@
"=============================================================================
" $Id: function.vim 161 2010-05-07 01:04:44Z luc.hermitte $
" File: autoload/lh/function.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.0
" Created: 03rd Nov 2008
" Last Update: $Date: 2010-05-06 21:04:44 -0400 (Thu, 06 May 2010) $
"------------------------------------------------------------------------
" Description:
" Implements:
" - lh#function#bind()
" - lh#function#execute()
" - lh#function#prepare()
" - a binded function type
"
"------------------------------------------------------------------------
" Installation:
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History:
" v2.2.0: first implementation
" TODO: «missing features»
" }}}1
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Functions {{{1
" # Debug {{{2
function! lh#function#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#function#debug(expr)
return eval(a:expr)
endfunction
" # Function: s:Join(arguments...) {{{2
function! s:Join(args)
let res = ''
if len(a:args) > 0
let res = string(a:args[0])
let i = 1
while i != len(a:args)
let res.=','.string(a:args[i])
let i += 1
endwhile
endif
return res
endfunction
" # Function: s:DoBindList(arguments...) {{{2
function! s:DoBindList(formal, real)
let args = []
for arg in a:formal
if type(arg)==type('string') && arg =~ '^v:\d\+_$'
let new = a:real[matchstr(arg, 'v:\zs\d\+\ze_')-1]
elseif type(arg)==type('string')
let new = eval(s:DoBindEvaluatedString(arg, a:real))
else
let new = arg
endif
call add(args, new)
unlet new
unlet arg
endfor
return args
endfunction
" # Function: s:DoBindString(arguments...) {{{2
function! s:DoBindString(expr, real)
let expr = substitute(a:expr, '\<v:\(\d\+\)_\>', a:real.'[\1-1]', 'g')
return expr
endfunction
function! s:ToString(expr)
return type(a:expr) != type('')
\ ? string(a:expr)
\ : (a:expr)
endfunction
function! s:DoBindEvaluatedString(expr, real)
let expr = a:expr
let p = 0
while 1
let p = match(expr, '\<v:\d\+_\>', p)
if -1 == p | break | endif
let e = matchend(expr, '\<v:\d\+_\>', p)
let n = eval(expr[p+2 : e-2])
" let new = (type(a:real[n-1])==type('') && a:real[n-1]=~ '\<v:\d\+_\>')
" \ ? a:real[n-1]
" \ : string(a:real[n-1])
let new = s:ToString(a:real[n-1])
" let new = string(a:real[n-1]) " -> bind_counpound vars
let expr = ((p>0) ? (expr[0:p-1]) : '') . new . expr[e : -1]
" echo expr
let p += len(new)
" silent! unlet new
endwhile
return expr
endfunction
" # Function: s:Execute(arguments...) {{{2
function! s:Execute(args) dict
if type(self.function) == type(function('exists'))
let args = s:DoBindList(self.args, a:args)
" echomsg '##'.string(self.function).'('.join(args, ',').')'
let res = eval(string(self.function).'('.s:Join(args).')')
elseif type(self.function) == type('string')
let expr = s:DoBindString(self.function, 'a:args')
let res = eval(expr)
elseif type(self.function) == type({})
return self.function.execute(a:args)
else
throw "lh#functor#execute: unpected function type: ".type(self.function)
endif
return res
endfunction
" # Function: lh#function#prepare(function, arguments_list) {{{2
function! lh#function#prepare(Fn, arguments_list)
if type(a:Fn) == type(function('exists'))
let expr = string(a:Fn).'('.s:Join(a:arguments_list).')'
return expr
elseif type(a:Fn) == type('string')
if a:Fn =~ '^[a-zA-Z0-9_#]\+$'
let expr = string(function(a:Fn)).'('.s:Join(a:arguments_list).')'
return expr
else
let expr = s:DoBindString(a:Fn, 'a:000')
return expr
endif
else
throw "lh#function#prepare(): {Fn} argument of type ".type(a:Fn). " is unsupported"
endif
endfunction
" # Function: lh#function#execute(function, arguments...) {{{2
function! lh#function#execute(Fn, ...)
if type(a:Fn) == type({}) && has_key(a:Fn, 'execute')
return a:Fn.execute(a:000)
else
let expr = lh#function#prepare(a:Fn, a:000)
return eval(expr)
endif
endfunction
" # Function: lh#function#bind(function, arguments...) {{{2
function! lh#function#bind(Fn, ...)
let args = copy(a:000)
if type(a:Fn) == type('string') && a:Fn =~ '^[a-zA-Z0-9_#]\+$'
\ && exists('*'.a:Fn)
let Fn = function(a:Fn)
elseif type(a:Fn) == type({})
" echo string(a:Fn).'('.string(a:000).')'
" Rebinding another binded function
" TASSERT has_key(a:Fn, 'function')
" TASSERT has_key(a:Fn, 'execute')
" TASSERT has_key(a:Fn, 'args')
let Fn = a:Fn.function
let N = len(a:Fn.args)
if N != 0 " args to rebind
let i = 0
let t_args = [] " necessary to avoid type changes
while i != N
silent! unlet arg
let arg = a:Fn.args[i]
if arg =~ 'v:\d\+_$'
let arg2 = eval(s:DoBindString(arg, string(args)))
" echo arg."-(".string(args).")->".string(arg2)
unlet arg
let arg = arg2
unlet arg2
endif
call add(t_args, arg)
let i += 1
endwhile
unlet a:Fn.args
let a:Fn.args = t_args
else " expression to fix
" echo Fn
" echo s:DoBindString(Fn, string(args))
" echo eval(string(s:DoBindString(Fn, string(args))))
let Fn = (s:DoBindEvaluatedString(Fn, args))
endif
let args = a:Fn.args
else
let Fn = a:Fn
endif
let binded_fn = {
\ 'function': Fn,
\ 'args': args,
\ 'execute': function('s:Execute')
\}
return binded_fn
endfunction
" }}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:
" Vim: let g:UTfiles='tests/lh/function.vim'

View File

@ -0,0 +1,177 @@
"=============================================================================
" $Id: tsort.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/tsort.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 21st Apr 2008
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description: Library functions for Topological Sort
"
"------------------------------------------------------------------------
" Drop the file into {rtp}/autoload/lh/graph
" History: «history»
" TODO: «missing features»
" }}}1
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Debug {{{1
function! lh#graph#tsort#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#graph#tsort#debug(expr)
return eval(a:expr)
endfunction
"------------------------------------------------------------------------
"## Helper functions {{{1
"# s:Successors_fully_defined(node) {{{2
function! s:Successors_fully_defined(node) dict
if has_key(self.table, a:node)
return self.table[a:node]
else
return []
endif
endfunction
"# s:Successors_lazy(node) {{{2
function! s:Successors_lazy(node) dict
if !has_key(self.table, a:node)
let nodes = self.fetch(a:node)
let self.table[a:node] = nodes
" if len(nodes) > 0
" let self.nb += 1
" endif
return nodes
else
return self.table[a:node]
endif
endfunction
"# s:PrepareDAG(dag) {{{2
function! s:PrepareDAG(dag)
if type(a:dag) == type(function('has_key'))
let dag = {
\ 'successors': function('s:Successors_lazy'),
\ 'fetch' : a:dag,
\ 'table' : {}
\}
else
let dag = {
\ 'successors': function('s:Successors_fully_defined'),
\ 'table' : deepcopy(a:dag)
\}
endif
return dag
endfunction
"## Depth-first search (recursive) {{{1
" Do not detect cyclic graphs
"# lh#graph#tsort#depth(dag, start_nodes) {{{2
function! lh#graph#tsort#depth(dag, start_nodes)
let dag = s:PrepareDAG(a:dag)
let results = []
let visited_nodes = { 'Visited':function('s:Visited')}
call s:RecursiveDTSort(dag, a:start_nodes, results, visited_nodes)
call reverse(results)
return results
endfunction
"# The real, recursive, T-Sort {{{2
"see boost.graph for a non recursive implementation
function! s:RecursiveDTSort(dag, start_nodes, results, visited_nodes)
for node in a:start_nodes
let visited = a:visited_nodes.Visited(node)
if visited == 1 | continue " done
elseif visited == 2 | throw "Tsort: cyclic graph detected: ".node
endif
let a:visited_nodes[node] = 2 " visiting
let succs = a:dag.successors(node)
try
call s:RecursiveDTSort(a:dag, succs, a:results, a:visited_nodes)
catch /Tsort:/
throw v:exception.'>'.node
endtry
let a:visited_nodes[node] = 1 " visited
call add(a:results, node)
endfor
endfunction
function! s:Visited(node) dict
return has_key(self, a:node) ? self[a:node] : 0
endfunction
"## Breadth-first search (non recursive) {{{1
"# lh#graph#tsort#breadth(dag, start_nodes) {{{2
" warning: This implementation does not work with lazy dag, but only with fully
" defined ones
function! lh#graph#tsort#breadth(dag, start_nodes)
let result = []
let dag = s:PrepareDAG(a:dag)
let queue = deepcopy(a:start_nodes)
while len(queue) > 0
let node = remove(queue, 0)
" echomsg "result <- ".node
call add(result, node)
let successors = dag.successors(node)
while len(successors) > 0
let m = s:RemoveEdgeFrom(dag, node)
" echomsg "graph loose ".node."->".m
if !s:HasIncomingEgde(dag, m)
" echomsg "queue <- ".m
call add(queue, m)
endif
endwhile
endwhile
if !s:Empty(dag)
throw "Tsort: cyclic graph detected: "
endif
return result
endfunction
function! s:HasIncomingEgde(dag, node)
for node in keys(a:dag.table)
if type(a:dag.table[node]) != type([])
continue
endif
if index(a:dag.table[node], a:node) != -1
return 1
endif
endfor
return 0
endfunction
function! s:RemoveEdgeFrom(dag, node)
let successors = a:dag.successors(a:node)
if len(successors) > 0
let successor = remove(successors, 0)
if len(successors) == 0
" echomsg "finished with ->".a:node
call remove(a:dag.table, a:node)
endif
return successor
endif
throw "No more edges from ".a:node
endfunction
function! s:Empty(dag)
" echomsg "len="len(a:dag.table)
return len(a:dag.table) == 0
endfunction
" }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker

View File

@ -0,0 +1,338 @@
"=============================================================================
" $Id: list.vim 236 2010-06-01 00:43:34Z luc.hermitte $
" File: autoload/lh/list.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 17th Apr 2007
" Last Update: $Date: 2010-05-31 20:43:34 -0400 (Mon, 31 May 2010) $ (17th Apr 2007)
"------------------------------------------------------------------------
" Description:
" Defines functions related to |Lists|
"
"------------------------------------------------------------------------
" Installation:
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History:
" v2.2.1:
" (*) use :unlet in :for loop to support heterogeneous lists
" (*) binary search algorithms (upper_bound, lower_bound, equal_range)
" v2.2.0:
" (*) new functions: lh#list#accumulate, lh#list#transform,
" lh#list#transform_if, lh#list#find_if, lh#list#copy_if,
" lh#list#subset, lh#list#intersect
" (*) the functions are compatible with lh#function functors
" v2.1.1:
" (*) unique_sort
" v2.0.7:
" (*) Bug fix: lh#list#Match()
" v2.0.6:
" (*) lh#list#Find_if() supports search predicate, and start index
" (*) lh#list#Match() supports start index
" v2.0.0:
" TODO: «missing features»
" }}}1
"=============================================================================
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Functions {{{1
" # Debug {{{2
function! lh#list#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#list#debug(expr)
return eval(a:expr)
endfunction
"------------------------------------------------------------------------
" # Public {{{2
" Function: lh#list#Transform(input, output, action) {{{3
" deprecated version
function! lh#list#Transform(input, output, action)
let new = map(copy(a:input), a:action)
let res = extend(a:output,new)
return res
for element in a:input
let action = substitute(a:action, 'v:val','element', 'g')
let res = eval(action)
call add(a:output, res)
unlet element " for heterogeneous lists
endfor
return a:output
endfunction
function! lh#list#transform(input, output, action)
for element in a:input
let res = lh#function#execute(a:action, element)
call add(a:output, res)
unlet element " for heterogeneous lists
endfor
return a:output
endfunction
function! lh#list#transform_if(input, output, action, predicate)
for element in a:input
if lh#function#execute(a:predicate, element)
let res = lh#function#execute(a:action, element)
call add(a:output, res)
endif
unlet element " for heterogeneous lists
endfor
return a:output
endfunction
function! lh#list#copy_if(input, output, predicate)
for element in a:input
if lh#function#execute(a:predicate, element)
call add(a:output, element)
endif
unlet element " for heterogeneous lists
endfor
return a:output
endfunction
function! lh#list#accumulate(input, transformation, accumulator)
let transformed = lh#list#transform(a:input, [], a:transformation)
let res = lh#function#execute(a:accumulator, transformed)
return res
endfunction
" Function: lh#list#match(list, to_be_matched [, idx]) {{{3
function! lh#list#match(list, to_be_matched, ...)
let idx = (a:0>0) ? a:1 : 0
while idx < len(a:list)
if match(a:list[idx], a:to_be_matched) != -1
return idx
endif
let idx += 1
endwhile
return -1
endfunction
function! lh#list#Match(list, to_be_matched, ...)
let idx = (a:0>0) ? a:1 : 0
return lh#list#match(a:list, a:to_be_matched, idx)
endfunction
" Function: lh#list#Find_if(list, predicate [, predicate-arguments] [, start-pos]) {{{3
function! lh#list#Find_if(list, predicate, ...)
" Parameters
let idx = 0
let args = []
if a:0 == 2
let idx = a:2
let args = a:1
elseif a:0 == 1
if type(a:1) == type([])
let args = a:1
elseif type(a:1) == type(42)
let idx = a:1
else
throw "lh#list#Find_if: unexpected argument type"
endif
elseif a:0 != 0
throw "lh#list#Find_if: unexpected number of arguments: lh#list#Find_if(list, predicate [, predicate-arguments] [, start-pos])"
endif
" The search loop
while idx != len(a:list)
let predicate = substitute(a:predicate, 'v:val', 'a:list['.idx.']', 'g')
let predicate = substitute(predicate, 'v:\(\d\+\)_', 'args[\1-1]', 'g')
let res = eval(predicate)
if res | return idx | endif
let idx += 1
endwhile
return -1
endfunction
" Function: lh#list#find_if(list, predicate [, predicate-arguments] [, start-pos]) {{{3
function! lh#list#find_if(list, predicate, ...)
" Parameters
let idx = 0
let args = []
if a:0 == 1
let idx = a:1
elseif a:0 != 0
throw "lh#list#find_if: unexpected number of arguments: lh#list#find_if(list, predicate [, start-pos])"
endif
" The search loop
while idx != len(a:list)
" let predicate = substitute(a:predicate, 'v:val', 'a:list['.idx.']', 'g')
let res = lh#function#execute(a:predicate, a:list[idx])
if res | return idx | endif
let idx += 1
endwhile
return -1
endfunction
" Function: lh#list#lower_bound(sorted_list, value [, first[, last]]) {{{3
function! lh#list#lower_bound(list, val, ...)
let first = 0
let last = len(a:list)
if a:0 >= 1 | let first = a:1
elseif a:0 >= 2 | let last = a:2
elseif a:0 > 2
throw "lh#list#equal_range: unexpected number of arguments: lh#list#equal_range(sorted_list, value [, first[, last]])"
endif
let len = last - first
while len > 0
let half = len / 2
let middle = first + half
if a:list[middle] < a:val
let first = middle + 1
let len -= half + 1
else
let len = half
endif
endwhile
return first
endfunction
" Function: lh#list#upper_bound(sorted_list, value [, first[, last]]) {{{3
function! lh#list#upper_bound(list, val, ...)
let first = 0
let last = len(a:list)
if a:0 >= 1 | let first = a:1
elseif a:0 >= 2 | let last = a:2
elseif a:0 > 2
throw "lh#list#equal_range: unexpected number of arguments: lh#list#equal_range(sorted_list, value [, first[, last]])"
endif
let len = last - first
while len > 0
let half = len / 2
let middle = first + half
if a:val < a:list[middle]
let len = half
else
let first = middle + 1
let len -= half + 1
endif
endwhile
return first
endfunction
" Function: lh#list#equal_range(sorted_list, value [, first[, last]]) {{{3
" @return [f, l], where
" f : First position where {value} could be inserted
" l : Last position where {value} could be inserted
function! lh#list#equal_range(list, val, ...)
let first = 0
let last = len(a:list)
" Parameters
if a:0 >= 1 | let first = a:1
elseif a:0 >= 2 | let last = a:2
elseif a:0 > 2
throw "lh#list#equal_range: unexpected number of arguments: lh#list#equal_range(sorted_list, value [, first[, last]])"
endif
" The search loop ( == STLPort's equal_range)
let len = last - first
while len > 0
let half = len / 2
let middle = first + half
if a:list[middle] < a:val
let first = middle + 1
let len -= half + 1
elseif a:val < a:list[middle]
let len = half
else
let left = lh#list#lower_bound(a:list, a:val, first, middle)
let right = lh#list#upper_bound(a:list, a:val, middle+1, first+len)
return [left, right]
endif
" let predicate = substitute(a:predicate, 'v:val', 'a:list['.idx.']', 'g')
" let res = lh#function#execute(a:predicate, a:list[idx])
endwhile
return [first, first]
endfunction
" Function: lh#list#unique_sort(list [, func]) {{{3
" See also http://vim.wikia.com/wiki/Unique_sorting
"
" Works like sort(), optionally taking in a comparator (just like the
" original), except that duplicate entries will be removed.
" todo: support another argument that act as an equality predicate
function! lh#list#unique_sort(list, ...)
let dictionary = {}
for i in a:list
let dictionary[string(i)] = i
endfor
let result = []
" echo join(values(dictionary),"\n")
if ( exists( 'a:1' ) )
let result = sort( values( dictionary ), a:1 )
else
let result = sort( values( dictionary ) )
endif
return result
endfunction
function! lh#list#unique_sort2(list, ...)
let list = copy(a:list)
if ( exists( 'a:1' ) )
call sort(list, a:1 )
else
call sort(list)
endif
if len(list) <= 1 | return list | endif
let result = [ list[0] ]
let last = list[0]
let i = 1
while i < len(list)
if last != list[i]
let last = list[i]
call add(result, last)
endif
let i += 1
endwhile
return result
endfunction
" Function: lh#list#subset(list, indices) {{{3
function! lh#list#subset(list, indices)
let result=[]
for e in a:indices
call add(result, a:list[e])
endfor
return result
endfunction
" Function: lh#list#intersect(list1, list2) {{{3
function! lh#list#intersect(list1, list2)
let result = copy(a:list1)
call filter(result, 'index(a:list2, v:val) >= 0')
return result
for e in a:list1
if index(a:list2, e) > 0
call result(result, e)
endif
endfor
endfunction
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,384 @@
"=============================================================================
" $Id: menu.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/menu.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 13th Oct 2006
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $ (28th Aug 2007)
"------------------------------------------------------------------------
" Description:
" Defines the global function lh#menu#def_menu
" Aimed at (ft)plugin writers.
"
"------------------------------------------------------------------------
" Installation:
" Drop this file into {rtp}/autoload/lh/
" Requires Vim 7+
" History:
" v2.0.0: Moving to vim7
" v2.0.1: :ToggleXxx echoes the new value
" v2.2.0: Support environment variables
" Only one :Toggle command is defined.
" TODO:
" Should the argument to :Toggle be simplified to use the variable
" name instead ?
" }}}1
"=============================================================================
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Internal Variables {{{1
let s:k_Toggle_cmd = 'Toggle'
if !exists('s:toggle_commands')
let s:toggle_commands = {}
endif
"------------------------------------------------------------------------
" ## Functions {{{1
" # Debug {{{2
function! lh#menu#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#menu#debug(expr)
return eval(a:expr)
endfunction
"------------------------------------------------------------------------
" # Common stuff {{{2
" Function: lh#menu#text({text}) {{{3
" @return a text to be used in menus where "\" and spaces have been escaped.
function! lh#menu#text(text)
return escape(a:text, '\ ')
endfunction
" # Toggling menu item {{{2
" Function: s:Fetch({Data},{key}) {{{3
" @param[in] Data Menu-item definition
" @param[in] key Table table from which the result will be fetched
" @return the current value, or text, whose index is Data.idx_crt_value.
function! s:Fetch(Data, key)
let len = len(a:Data[a:key])
if a:Data.idx_crt_value >= len | let a:Data.idx_crt_value = 0 | endif
let value = a:Data[a:key][a:Data.idx_crt_value]
return value
endfunction
" Function: s:Search({Data},{value}) {{{3
" Searches for the index of {value} in {Data.values} list. Return 0 if not
" found.
function! s:Search(Data, value)
let idx = 0
while idx != len(a:Data.values)
if a:value == a:Data.values[idx]
" echo a:Data.variable . "[".idx."] == " . a:value
return idx
endif
let idx = idx + 1
endwhile
" echo a:Data.variable . "[".-1."] == " . a:value
return 0 " default is first element
endfunction
" Function: s:Set({Data}) {{{3
" @param[in,out] Data Menu item definition
"
" Sets the global variable associated to the menu item according to the item's
" current value.
function! s:Set(Data)
let value = a:Data.values[a:Data.idx_crt_value]
let variable = a:Data.variable
if variable[0] == '$' " environment variabmes
exe "let ".variable." = ".string(value)
else
let g:{variable} = value
endif
if has_key(a:Data, "actions")
let l:Action = a:Data.actions[a:Data.idx_crt_value]
if type(l:Action) == type(function('tr'))
call l:Action()
else
exe l:Action
endif
endif
return value
endfunction
" Function: s:MenuKey({Data}) {{{3
" @return the table name from which the current value name (to dsplay in the
" menu) must be fetched.
" Priority is given to the optional "texts" table over the madatory "values" table.
function! s:MenuKey(Data)
if has_key(a:Data, "texts")
let menu_id = "texts"
else
let menu_id = "values"
endif
return menu_id
endfunction
" Function: s:NextValue({Data}) {{{3
" Change the value of the variable to the next in the list of value.
" The menu, and the variable are updated in consequence.
function! s:NextValue(Data)
" Where the texts for values must be fetched
let labels_key = s:MenuKey(a:Data)
" Fetch the old current value
let old = s:Fetch(a:Data, labels_key)
" Remove the entry from the menu
call s:ClearMenu(a:Data.menu, old)
" Cycle/increment the current value
let a:Data.idx_crt_value += 1
" Fetch it
let new = s:Fetch(a:Data,labels_key)
" Add the updated entry in the menu
call s:UpdateMenu(a:Data.menu, new, a:Data.command)
" Update the binded global variable
let value = s:Set(a:Data)
echo a:Data.variable.'='.value
endfunction
" Function: s:ClearMenu({Menu}, {text}) {{{3
" Removes a menu item
"
" @param[in] Menu.priority Priority of the new menu-item
" @param[in] Menu.name Name of the new menu-item
" @param[in] text Text of the previous value of the variable binded
function! s:ClearMenu(Menu, text)
if has('gui_running')
let name = substitute(a:Menu.name, '&', '', 'g')
let cmd = 'unmenu '.lh#menu#text(name.'<tab>('.a:text.')')
silent! exe cmd
endif
endfunction
" Function: s:UpdateMenu({Menu}, {text}, {command}) {{{3
" Adds a new menu item, with the text associated to the current value in
" braces.
"
" @param[in] Menu.priority Priority of the new menu-item
" @param[in] Menu.name Name of the new menu-item
" @param[in] text Text of the current value of the variable binded to
" the menu-item
" @param[in] command Toggle command to execute when the menu-item is selected
function! s:UpdateMenu(Menu, text, command)
if has('gui_running')
let cmd = 'nnoremenu <silent> '.a:Menu.priority.' '.
\ lh#menu#text(a:Menu.name.'<tab>('.a:text.')').
\ ' :silent '.s:k_Toggle_cmd.' '.a:command."\<cr>"
silent! exe cmd
endif
endfunction
" Function: s:SaveData({Data}) {{{3
" @param Data Menu-item definition
" Saves {Data} as s:Data{s:data_id++}. The definition will be used by
" automatically generated commands.
" @return s:data_id
let s:data_id = 0
function! s:SaveData(Data)
let s:Data{s:data_id} = a:Data
let id = s:data_id
let s:data_id += 1
return id
endfunction
" Function: lh#menu#def_toggle_item({Data}) {{{3
" @param Data.idx_crt_value
" @param Data.definitions == [ {value:, menutext: } ]
" @param Data.menu == { name:, position: }
"
" Sets a toggle-able menu-item defined by {Data}.
"
function! lh#menu#def_toggle_item(Data)
" Save the menu data as an internal script variable
let id = s:SaveData(a:Data)
" If the index of the current value hasn't been set, fetch it from the
" associated variable
if !has_key(a:Data, "idx_crt_value")
" Fetch the value of the associated variable
let value = lh#option#get(a:Data.variable, 0, 'g')
" echo a:Data.variable . " <- " . value
" Update the index of the current value
let a:Data.idx_crt_value = s:Search(a:Data, value)
endif
" Name of the auto-matically generated toggle command
let cmdName = substitute(a:Data.menu.name, '[^a-zA-Z_]', '', 'g')
" Lazy definition of the command
if 2 != exists(':'.s:k_Toggle_cmd)
exe 'command! -nargs=1 -complete=custom,lh#menu#_toggle_complete '
\ . s:k_Toggle_cmd . ' :call s:Toggle(<f-args>)'
endif
" silent exe 'command! -nargs=0 '.cmdName.' :call s:NextValue(s:Data'.id.')'
let s:toggle_commands[cmdName] = eval('s:Data'.id)
let a:Data["command"] = cmdName
" Add the menu entry according to the current value
call s:UpdateMenu(a:Data.menu, s:Fetch(a:Data, s:MenuKey(a:Data)), cmdName)
" Update the associated global variable
call s:Set(a:Data)
endfunction
"------------------------------------------------------------------------
function! s:Toggle(cmdName)
if !has_key(s:toggle_commands, a:cmdName)
throw "toggle-menu: unknown toggable variable ".a:cmdName
endif
let data = s:toggle_commands[a:cmdName]
call s:NextValue(data)
endfunction
function! lh#menu#_toggle_complete(ArgLead, CmdLine, CursorPos)
return join(keys(s:toggle_commands),"\n")
endfunction
"------------------------------------------------------------------------
" # IVN Menus {{{2
" Function: s:CTRL_O({cmd}) {{{3
" Build the command (sequence of ':ex commands') to be executed from
" INSERT-mode.
function! s:CTRL_O(cmd)
return substitute(a:cmd, '\(^\|<CR>\):', '\1\<C-O>:', 'g')
endfunction
" Function: lh#menu#is_in_visual_mode() {{{3
function! lh#menu#is_in_visual_mode()
return exists('s:is_in_visual_mode') && s:is_in_visual_mode
endfunction
" Function: lh#menu#_CMD_and_clear_v({cmd}) {{{3
" Internal function that executes the command and then clears the @v buffer
" todo: save and restore @v,
function! lh#menu#_CMD_and_clear_v(cmd)
try
let s:is_in_visual_mode = 1
exe a:cmd
finally
let @v=''
silent! unlet s:is_in_visual_mode
endtry
endfunction
" Function: s:Build_CMD({prefix},{cmd}) {{{3
" build the exact command to execute regarding the mode it is dedicated
function! s:Build_CMD(prefix, cmd)
if a:cmd[0] != ':' | return ' ' . a:cmd
endif
if a:prefix[0] == "i" | return ' ' . <SID>CTRL_O(a:cmd)
elseif a:prefix[0] == "n" | return ' ' . a:cmd
elseif a:prefix[0] == "v"
if match(a:cmd, ":VCall") == 0
return substitute(a:cmd, ':VCall', ' :call', ''). "\<cr>gV"
" gV exit select-mode if we where in it!
else
return
\ " \"vy\<C-C>:call lh#menu#_CMD_and_clear_v('" .
\ substitute(a:cmd, "<CR>$", '', '') ."')\<cr>"
endif
elseif a:prefix[0] == "c" | return " \<C-C>" . a:cmd
else | return ' ' . a:cmd
endif
endfunction
" Function: lh#menu#map_all({map_type}, [{map args}...) {{{3
" map the command to all the modes required
function! lh#menu#map_all(map_type,...)
let nore = (match(a:map_type, '[aincv]*noremap') != -1) ? "nore" : ""
let prefix = matchstr(substitute(a:map_type, nore, '', ''), '[aincv]*')
if a:1 == "<buffer>" | let i = 3 | let binding = a:1 . ' ' . a:2
else | let i = 2 | let binding = a:1
endif
let binding = '<silent> ' . binding
let cmd = a:{i}
let i += 1
while i <= a:0
let cmd .= ' ' . a:{i}
let i += 1
endwhile
let build_cmd = nore . 'map ' . binding
while strlen(prefix)
if prefix[0] == "a" | let prefix = "incv"
else
execute prefix[0] . build_cmd . <SID>Build_CMD(prefix[0],cmd)
let prefix = strpart(prefix, 1)
endif
endwhile
endfunction
" Function: lh#menu#make({prefix},{code},{text},{binding},...) {{{3
" Build the menu and map its associated binding to all the modes required
function! lh#menu#make(prefix, code, text, binding, ...)
let nore = (match(a:prefix, '[aincv]*nore') != -1) ? "nore" : ""
let prefix = matchstr(substitute(a:prefix, nore, '', ''), '[aincv]*')
let b = (a:1 == "<buffer>") ? 1 : 0
let i = b + 1
let cmd = a:{i}
let i += 1
while i <= a:0
let cmd .= ' ' . a:{i}
let i += 1
endwhile
let build_cmd = nore . "menu <silent> " . a:code . ' ' . lh#menu#text(a:text)
if strlen(a:binding) != 0
let build_cmd .= '<tab>' .
\ substitute(lh#menu#text(a:binding), '&', '\0\0', 'g')
if b != 0
call lh#menu#map_all(prefix.nore."map", ' <buffer> '.a:binding, cmd)
else
call lh#menu#map_all(prefix.nore."map", a:binding, cmd)
endif
endif
if has("gui_running")
while strlen(prefix)
execute <SID>BMenu(b).prefix[0].build_cmd.<SID>Build_CMD(prefix[0],cmd)
let prefix = strpart(prefix, 1)
endwhile
endif
endfunction
" Function: s:BMenu({b}) {{{3
" If <buffermenu.vim> is installed and the menu should be local, then the
" apropriate string is returned.
function! s:BMenu(b)
let res = (a:b && exists(':Bmenu')
\ && (1 == lh#option#get("want_buffermenu_or_global_disable", 1, "bg"))
\) ? 'B' : ''
" call confirm("BMenu(".a:b.")=".res, '&Ok', 1)
return res
endfunction
" Function: lh#menu#IVN_make(...) {{{3
function! lh#menu#IVN_make(code, text, binding, i_cmd, v_cmd, n_cmd, ...)
" nore options
let nore_i = (a:0 > 0) ? ((a:1 != 0) ? 'nore' : '') : ''
let nore_v = (a:0 > 1) ? ((a:2 != 0) ? 'nore' : '') : ''
let nore_n = (a:0 > 2) ? ((a:3 != 0) ? 'nore' : '') : ''
"
call lh#menu#make('i'.nore_i,a:code,a:text, a:binding, '<buffer>', a:i_cmd)
call lh#menu#make('v'.nore_v,a:code,a:text, a:binding, '<buffer>', a:v_cmd)
if strlen(a:n_cmd) != 0
call lh#menu#make('n'.nore_n,a:code,a:text, a:binding, '<buffer>', a:n_cmd)
endif
endfunction
"
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,107 @@
"=============================================================================
" $Id: option.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/option.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 24th Jul 2004
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $ (07th Oct 2006)
"------------------------------------------------------------------------
" Description:
" Defines the global function lh#option#get().
" Aimed at (ft)plugin writers.
"
"------------------------------------------------------------------------
" Installation:
" Drop this file into {rtp}/autoload/lh/
" Requires Vim 7+
" History:
" v2.0.5
" (*) lh#option#get_non_empty() manages Lists and Dictionaries
" (*) lh#option#get() doesn't test emptyness anymore
" v2.0.0
" Code moved from {rtp}/macros/
" }}}1
"=============================================================================
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Functions {{{1
" # Debug {{{2
function! lh#option#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#option#debug(expr)
return eval(a:expr)
endfunction
" # Public {{{2
" Function: lh#option#get(name, default [, scope]) {{{3
" @return b:{name} if it exists, or g:{name} if it exists, or {default}
" otherwise
" The order of the variables checked can be specified through the optional
" argument {scope}
function! lh#option#get(name,default,...)
let scope = (a:0 == 1) ? a:1 : 'bg'
let name = a:name
let i = 0
while i != strlen(scope)
if exists(scope[i].':'.name)
" \ && (0 != strlen({scope[i]}:{name}))
return {scope[i]}:{name}
endif
let i += 1
endwhile
return a:default
endfunction
function! lh#option#Get(name,default,...)
let scope = (a:0 == 1) ? a:1 : 'bg'
return lh#option#get(a:name, a:default, scope)
endfunction
function! s:IsEmpty(variable)
if type(a:variable) == type('string') | return 0 == strlen(a:variable)
elseif type(a:variable) == type(42) | return 0 == a:variable
elseif type(a:variable) == type([]) | return 0 == len(a:variable)
elseif type(a:variable) == type({}) | return 0 == len(a:variable)
else | return false
endif
endfunction
" Function: lh#option#get_non_empty(name, default [, scope]) {{{3
" @return of b:{name}, g:{name}, or {default} the first which exists and is not empty
" The order of the variables checked can be specified through the optional
" argument {scope}
function! lh#option#get_non_empty(name,default,...)
let scope = (a:0 == 1) ? a:1 : 'bg'
let name = a:name
let i = 0
while i != strlen(scope)
if exists(scope[i].':'.name) && !s:IsEmpty({scope[i]}:{name})
return {scope[i]}:{name}
endif
let i += 1
endwhile
return a:default
endfunction
function! lh#option#GetNonEmpty(name,default,...)
let scope = (a:0 == 1) ? a:1 : 'bg'
return lh#option#get_non_empty(a:name, a:default, scope)
endfunction
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,308 @@
"=============================================================================
" $Id: path.vim 237 2010-06-01 00:44:35Z luc.hermitte $
" File: autoload/lh/path.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 23rd Jan 2007
" Last Update: 11th Feb 2008
"------------------------------------------------------------------------
" Description:
" Functions related to the handling of pathnames
"
"------------------------------------------------------------------------
" Installation:
" Drop this file into {rtp}/autoload/lh
" Requires Vim7+
" History:
" v 1.0.0 First Version
" (*) Functions moved from searchInRuntimeTime
" v 2.0.1
" (*) lh#path#Simplify() becomes like |simplify()| except for trailing
" v 2.0.2
" (*) lh#path#SelectOne()
" (*) lh#path#ToRelative()
" v 2.0.3
" (*) lh#path#GlobAsList()
" v 2.0.4
" (*) lh#path#StripStart()
" v 2.0.5
" (*) lh#path#StripStart() interprets '.' as getcwd()
" v 2.2.0
" (*) new functions: lh#path#common(), lh#path#to_dirname(),
" lh#path#depth(), lh#path#relative_to(), lh#path#to_regex(),
" lh#path#find()
" (*) lh#path#simplify() fixed
" (*) lh#path#to_relative() use simplify()
" TODO:
" (*) Decide what #depth('../../bar') shall return
" (*) Fix #simplify('../../bar')
" }}}1
"=============================================================================
"=============================================================================
" Avoid global reinclusion {{{1
let s:cpo_save=&cpo
set cpo&vim
"=============================================================================
" ## Functions {{{1
" # Debug {{{2
let s:verbose = 0
function! lh#path#verbose(...)
if a:0 > 0 | let s:verbose = a:1 | endif
return s:verbose
endfunction
function! s:Verbose(expr)
if s:verbose
echomsg a:expr
endif
endfunction
function! lh#path#debug(expr)
return eval(a:expr)
endfunction
"=============================================================================
" # Public {{{2
" Function: lh#path#simplify({pathname}) {{{3
" Like |simplify()|, but also strip the leading './'
" It seems unable to simplify '..\' when compiled without +shellslash
function! lh#path#simplify(pathname)
let pathname = simplify(a:pathname)
let pathname = substitute(pathname, '^\%(\.[/\\]\)\+', '', '')
let pathname = substitute(pathname, '\([/\\]\)\%(\.[/\\]\)\+', '\1', 'g')
let pwd = getcwd().'/'
let pathname = substitute(pathname, '^'.lh#path#to_regex(pwd), '', 'g')
return pathname
endfunction
function! lh#path#Simplify(pathname)
return lh#path#simplify(a:pathname)
endfunction
" Function: lh#path#common({pathnames}) {{{3
" Find the common leading path between all pathnames
function! lh#path#common(pathnames)
" assert(len(pathnames)) > 1
let common = a:pathnames[0]
let i = 1
while i < len(a:pathnames)
let fcrt = a:pathnames[i]
" pathnames should not contain @
let common = matchstr(common.'@@'.fcrt, '^\zs\(.*[/\\]\)\ze.\{-}@@\1.*$')
if strlen(common) == 0
" No need to further checks
break
endif
let i += 1
endwhile
return common
endfunction
" Function: lh#path#strip_common({pathnames}) {{{3
" Find the common leading path between all pathnames, and strip it
function! lh#path#strip_common(pathnames)
" assert(len(pathnames)) > 1
let common = lh#path#common(a:pathnames)
let l = strlen(common)
if l == 0
return a:pathnames
else
let pathnames = a:pathnames
call map(pathnames, 'strpart(v:val, '.l.')' )
return pathnames
endif
endfunction
function! lh#path#StripCommon(pathnames)
return lh#path#strip_common(a:pathnames)
endfunction
" Function: lh#path#is_absolute_path({path}) {{{3
function! lh#path#is_absolute_path(path)
return a:path =~ '^/'
\ . '\|^[a-zA-Z]:[/\\]'
\ . '\|^[/\\]\{2}'
" Unix absolute path
" or Windows absolute path
" or UNC path
endfunction
function! lh#path#IsAbsolutePath(path)
return lh#path#is_absolute_path(a:path)
endfunction
" Function: lh#path#is_url({path}) {{{3
function! lh#path#is_url(path)
" todo: support UNC paths and other urls
return a:path =~ '^\%(https\=\|s\=ftp\|dav\|fetch\|file\|rcp\|rsynch\|scp\)://'
endfunction
function! lh#path#IsURL(path)
return lh#path#is_url(a:path)
endfunction
" Function: lh#path#select_one({pathnames},{prompt}) {{{3
function! lh#path#select_one(pathnames, prompt)
if len(a:pathnames) > 1
let simpl_pathnames = deepcopy(a:pathnames)
let simpl_pathnames = lh#path#strip_common(simpl_pathnames)
let simpl_pathnames = [ '&Cancel' ] + simpl_pathnames
" Consider guioptions+=c is case of difficulties with the gui
let selection = confirm(a:prompt, join(simpl_pathnames,"\n"), 1, 'Question')
let file = (selection == 1) ? '' : a:pathnames[selection-2]
return file
elseif len(a:pathnames) == 0
return ''
else
return a:pathnames[0]
endif
endfunction
function! lh#path#SelectOne(pathnames, prompt)
return lh#path#select_one(a:pathnames, a:prompt)
endfunction
" Function: lh#path#to_relative({pathname}) {{{3
function! lh#path#to_relative(pathname)
let newpath = fnamemodify(a:pathname, ':p:.')
let newpath = simplify(newpath)
return newpath
endfunction
function! lh#path#ToRelative(pathname)
return lh#path#to_relative(a:pathname)
endfunction
" Function: lh#path#to_dirname({dirname}) {{{3
" todo: use &shellslash
function! lh#path#to_dirname(dirname)
let dirname = a:dirname . (a:dirname[-1:] =~ '[/\\]' ? '' : '/')
return dirname
endfunction
" Function: lh#path#depth({dirname}) {{{3
" todo: make a choice about "negative" paths like "../../foo"
function! lh#path#depth(dirname)
if empty(a:dirname) | return 0 | endif
let dirname = lh#path#to_dirname(a:dirname)
let dirname = lh#path#simplify(dirname)
if lh#path#is_absolute_path(dirname)
let dirname = matchstr(dirname, '.\{-}[/\\]\zs.*')
endif
let depth = len(substitute(dirname, '[^/\\]\+[/\\]', '§', 'g'))
return depth
endfunction
" Function: lh#path#relative_to({from}, {to}) {{{3
" @param two directories
" @return a directories delta that ends with a '/' (may depends on
" &shellslash)
function! lh#path#relative_to(from, to)
" let from = fnamemodify(a:from, ':p')
" let to = fnamemodify(a:to , ':p')
let from = lh#path#to_dirname(a:from)
let to = lh#path#to_dirname(a:to )
let [from, to] = lh#path#strip_common([from, to])
let nb_up = lh#path#depth(from)
return repeat('../', nb_up).to
" cannot rely on :cd (as it alters things, and doesn't work with
" non-existant paths)
let pwd = getcwd()
exe 'cd '.a:to
let res = lh#path#to_relative(a:from)
exe 'cd '.pwd
return res
endfunction
" Function: lh#path#glob_as_list({pathslist}, {expr}) {{{3
function! s:GlobAsList(pathslist, expr)
let sResult = globpath(a:pathslist, a:expr)
let lResult = split(sResult, '\n')
" workaround a non feature of wildignore: it does not ignore directories
for ignored_pattern in split(&wildignore,',')
if stridx(ignored_pattern,'/') != -1
call filter(lResult, 'v:val !~ '.string(ignored_pattern))
endif
endfor
return lResult
endfunction
function! lh#path#glob_as_list(pathslist, expr)
if type(a:expr) == type('string')
return s:GlobAsList(a:pathslist, a:expr)
elseif type(a:expr) == type([])
let res = []
for expr in a:expr
call extend(res, s:GlobAsList(a:pathslist, expr))
endfor
return res
else
throw "Unexpected type for a:expression"
endif
endfunction
function! lh#path#GlobAsList(pathslist, expr)
return lh#path#glob_as_list(a:pathslist, a:expr)
endfunction
" Function: lh#path#strip_start({pathname}, {pathslist}) {{{3
" Strip occurrence of paths from {pathslist} in {pathname}
" @param[in] {pathname} name to simplify
" @param[in] {pathslist} list of pathname (can be a |string| of pathnames
" separated by ",", of a |List|).
function! lh#path#strip_start(pathname, pathslist)
if type(a:pathslist) == type('string')
" let strip_re = escape(a:pathslist, '\\.')
" let strip_re = '^' . substitute(strip_re, ',', '\\|^', 'g')
let pathslist = split(a:pathslist, ',')
elseif type(a:pathslist) == type([])
let pathslist = deepcopy(a:pathslist)
else
throw "Unexpected type for a:pathname"
endif
" apply a realpath like operation
let nb_paths = len(pathslist) " set before the loop
let i = 0
while i != nb_paths
if pathslist[i] =~ '^\.\%(/\|$\)'
let path2 = getcwd().pathslist[i][1:]
call add(pathslist, path2)
endif
let i += 1
endwhile
" replace path separators by a regex that can match them
call map(pathslist, 'substitute(v:val, "[\\\\/]", "[\\\\/]", "g")')
" echomsg string(pathslist)
" escape .
call map(pathslist, '"^".escape(v:val, ".")')
" build the strip regex
let strip_re = join(pathslist, '\|')
" echomsg strip_re
let res = substitute(a:pathname, '\%('.strip_re.'\)[/\\]\=', '', '')
return res
endfunction
function! lh#path#StripStart(pathname, pathslist)
return lh#path#strip_start(a:pathname, a:pathslist)
endfunction
" Function: lh#path#to_regex({pathname}) {{{3
function! lh#path#to_regex(path)
let regex = substitute(a:path, '[/\\]', '[/\\\\]', 'g')
return regex
endfunction
" Function: lh#path#find({pathname}, {regex}) {{{3
function! lh#path#find(paths, regex)
let paths = (type(a:paths) == type([]))
\ ? (a:paths)
\ : split(a:paths,',')
for path in paths
if match(path ,a:regex) != -1
return path
endif
endfor
return ''
endfunction
"=============================================================================
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,93 @@
"=============================================================================
" $Id: position.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/position.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 05th Sep 2007
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $ (05th Sep 2007)
"------------------------------------------------------------------------
" Description: «description»
"
"------------------------------------------------------------------------
" Installation:
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History: «history»
" v1.0.0:
" Creation
" TODO:
" }}}1
"=============================================================================
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Functions {{{1
" # Debug {{{2
function! lh#position#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#position#debug(expr)
return eval(a:expr)
endfunction
"------------------------------------------------------------------------
" # Public {{{2
" Function: lh#position#is_before {{{3
" @param[in] positions as those returned from |getpos()|
" @return whether lhs_pos is before rhs_pos
function! lh#position#is_before(lhs_pos, rhs_pos)
if a:lhs_pos[0] != a:rhs_pos[0]
throw "Positions from incompatible buffers can't be ordered"
endif
"1 test lines
"2 test cols
let before
\ = (a:lhs_pos[1] == a:rhs_pos[1])
\ ? (a:lhs_pos[2] < a:rhs_pos[2])
\ : (a:lhs_pos[1] < a:rhs_pos[1])
return before
endfunction
function! lh#position#IsBefore(lhs_pos, rhs_pos)
return lh#position#is_before(a:lhs_pos, a:rhs_pos)
endfunction
" Function: lh#position#char_at_mark {{{3
" @return the character at a given mark (|mark|)
function! lh#position#char_at_mark(mark)
let c = getline(a:mark)[col(a:mark)-1]
return c
endfunction
function! lh#position#CharAtMark(mark)
return lh#position#char_at_mark(a:mark)
endfunction
" Function: lh#position#char_at_pos {{{3
" @return the character at a given position (|getpos()|)
function! lh#position#char_at_pos(pos)
let c = getline(a:pos[1])[col(a:pos[2])-1]
return c
endfunction
function! lh#position#CharAtPos(pos)
return lh#position#char_at_pos(a:pos)
endfunction
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,138 @@
"=============================================================================
" $Id: syntax.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: autoload/lh/syntax.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 05th Sep 2007
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $ (05th Sep 2007)
"------------------------------------------------------------------------
" Description: «description»
"
"------------------------------------------------------------------------
" Installation:
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History: «history»
" v1.0.0:
" Creation ;
" Functions moved from lhVimSpell
" TODO:
" function, to inject "contained", see lhVimSpell approach
" }}}1
"=============================================================================
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Functions {{{1
" # Debug {{{2
function! lh#syntax#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! lh#syntax#debug(expr)
return eval(a:expr)
endfunction
" # Public {{{2
" Functions: Show name of the syntax kind of a character {{{3
function! lh#syntax#name_at(l,c, ...)
let what = a:0 > 0 ? a:1 : 0
return synIDattr(synID(a:l, a:c, what),'name')
endfunction
function! lh#syntax#NameAt(l,c, ...)
let what = a:0 > 0 ? a:1 : 0
return lh#syntax#name_at(a:l, a:c, what)
endfunction
function! lh#syntax#name_at_mark(mark, ...)
let what = a:0 > 0 ? a:1 : 0
return lh#syntax#name_at(line(a:mark), col(a:mark), what)
endfunction
function! lh#syntax#NameAtMark(mark, ...)
let what = a:0 > 0 ? a:1 : 0
return lh#syntax#name_at_mark(a:mark, what)
endfunction
" Functions: skip string, comment, character, doxygen {{{3
func! lh#syntax#skip_at(l,c)
return lh#syntax#name_at(a:l,a:c) =~? 'string\|comment\|character\|doxygen'
endfun
func! lh#syntax#SkipAt(l,c)
return lh#syntax#skip_at(a:l,a:c)
endfun
func! lh#syntax#skip()
return lh#syntax#skip_at(line('.'), col('.'))
endfun
func! lh#syntax#Skip()
return lh#syntax#skip()
endfun
func! lh#syntax#skip_at_mark(mark)
return lh#syntax#skip_at(line(a:mark), col(a:mark))
endfun
func! lh#syntax#SkipAtMark(mark)
return lh#syntax#skip_at_mark(a:mark)
endfun
" Function: Show current syntax kind {{{3
command! SynShow echo 'hi<'.lh#syntax#name_at_mark('.',1).'> trans<'
\ lh#syntax#name_at_mark('.',0).'> lo<'.
\ synIDattr(synIDtrans(synID(line('.'), col('.'), 1)), 'name').'>'
" Function: lh#syntax#list_raw(name) : string {{{3
function! lh#syntax#list_raw(name)
let a_save = @a
try
redir @a
exe 'silent! syn list '.a:name
redir END
let res = @a
finally
let @a = a_save
endtry
return res
endfunction
" Function: lh#syntax#list(name) : List {{{3
function! lh#syntax#list(name)
let raw = lh#syntax#list_raw(a:name)
let res = []
let lines = split(raw, '\n')
let started = 0
for l in lines
if started
let li = (l =~ 'links to') ? '' : l
elseif l =~ 'xxx'
let li = matchstr(l, 'xxx\s*\zs.*')
let started = 1
else
let li = ''
endif
if strlen(li) != 0
let li = substitute(li, 'contained\S*\|transparent\|nextgroup\|skipwhite\|skipnl\|skipempty', '', 'g')
let kinds = split(li, '\s\+')
call extend(res, kinds)
endif
endfor
return res
endfunction
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,42 @@
"=============================================================================
" $Id$
" File: autoload/lh/visual.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 08th Sep 2008
" Last Update: $Date$
"------------------------------------------------------------------------
" Helpers functions releated to the visual mode
"
"------------------------------------------------------------------------
" Drop it into {rtp}/autoload/lh/
" Vim 7+ required.
" History:
" v2.0.6: First appearance
" TODO: «missing features»
" }}}1
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" Functions {{{1
" Function: lh#visual#selection() {{{3
" @return the text currently selected
function! lh#visual#selection()
try
let a_save = @a
normal! gv"ay
return @a
finally
let @a = a_save
endtry
endfunction
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,17 @@
------------------
lh-vim-lib 2.2.1
------------------
lh-vim-lib is a library plugin for vim v7+. It is just a collection of
functions that are meant to be used by script writers.
Audience : Vim script writers
Requirements : Vim 7.1
Required by : Just a few other plugins for the moment
Author : Luc Hermitte
License : GPL2
More Help At : http://code.google.com/p/lh-vim/wiki/lhVimLib
Vim script#214: http://www.vim.org/scripts/script.php?script_id=214
Repository :
svn checkout http://lh-vim.googlecode.com/svn/vim-lib/trunk lh-vim-lib

View File

@ -0,0 +1,83 @@
"===========================================================================
" $Id: menu-map.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: macros/menu-map.vim
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
"
" Purpose: Define functions to build mappings and menus at the same time
"
" Version: 2.2.1
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $ (02nd Dec 2006)
"
" Last Changes: {{{
" Version 2.0.0:
" Moved to vim7,
" Functions moved to {rtp}/autoload/
" Version 1.6.2:
" (*) Silent mappings and menus
" Version 1.6. :
" (*) Uses has('gui_running') instead of has('gui') to check if
" we can generate the menu.
" Version 1.5. :
" (*) visual mappings launched from select-mode don't end with
" text still selected -- applied to :VCalls
" Version 1.4. :
" (*) address obfuscated for spammers
" (*) support the local option
" b:want_buffermenu_or_global_disable if we don't want
" buffermenu to be used systematically.
" 0 -> buffer menu not used
" 1 -> buffer menu used
" 2 -> the VimL developper will use a global disable.
" cf.: tex-maps.vim:: s:SimpleMenu()
" and texmenus.vim
" Version 1.3. :
" (*) add continuation lines support ; cf 'cpoptions'
" Version 1.2. :
" (*) Code folded.
" (*) Take advantage of buffermenu.vim if present for local
" menus.
" (*) If non gui is available, the menus won't be defined
" Version 1.1. :
" (*) Bug corrected :
" vnore(map\|menu) does not imply v+n(map\|menu) any more
" }}}
"
" Inspired By: A function from Benji Fisher
"
" TODO: (*) no menu if no gui.
"
"===========================================================================
if exists("g:loaded_menu_map") | finish | endif
let g:loaded_menu_map = 1
"" line continuation used here ??
let s:cpo_save = &cpo
set cpo&vim
"=========================================================================
" Commands {{{
command! -nargs=+ -bang MAP map<bang> <args>
command! -nargs=+ IMAP imap <args>
command! -nargs=+ NMAP nmap <args>
command! -nargs=+ CMAP cmap <args>
command! -nargs=+ VMAP vmap <args>
command! -nargs=+ AMAP
\ call lh#menu#map_all('amap', <f-args>)
command! -nargs=+ -bang NOREMAP noremap<bang> <args>
command! -nargs=+ INOREMAP inoremap <args>
command! -nargs=+ NNOREMAP nnoremap <args>
command! -nargs=+ CNOREMAP cnoremap <args>
command! -nargs=+ VNOREMAP vnoremap <args>
command! -nargs=+ ANOREMAP
\ call lh#menu#map_all('anoremap', <f-args>)
" }}}
" End !
let &cpo = s:cpo_save
finish
"=========================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,53 @@
"=============================================================================
" $Id: mk-lh-vim-lib.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: mk-lh-lib.vim
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
let s:version = '2.2.1'
" Created: 06th Nov 2007
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
cd <sfile>:p:h
try
let save_rtp = &rtp
let &rtp = expand('<sfile>:p:h:h').','.&rtp
exe '22,$MkVimball! lh-vim-lib-'.s:version
set modifiable
set buftype=
finally
let &rtp = save_rtp
endtry
finish
autoload/lh/askvim.vim
autoload/lh/buffer.vim
autoload/lh/buffer/dialog.vim
autoload/lh/command.vim
autoload/lh/common.vim
autoload/lh/encoding.vim
autoload/lh/env.vim
autoload/lh/event.vim
autoload/lh/graph/tsort.vim
autoload/lh/list.vim
autoload/lh/menu.vim
autoload/lh/option.vim
autoload/lh/path.vim
autoload/lh/position.vim
autoload/lh/syntax.vim
autoload/lh/visual.vim
doc/lh-vim-lib.txt
macros/menu-map.vim
mkVba/mk-lh-vim-lib.vim
plugin/let.vim
plugin/lhvl.vim
plugin/ui-functions.vim
plugin/words_tools.vim
tests/lh/function.vim
tests/lh/list.vim
tests/lh/path.vim
tests/lh/test-Fargs2String.vim
tests/lh/test-askmenu.vim
tests/lh/test-command.vim
tests/lh/test-menu-map.vim
tests/lh/test-toggle-menu.vim
tests/lh/topological-sort.vim

View File

@ -0,0 +1,54 @@
"=============================================================================
" $Id: let.vim 239 2010-06-01 00:48:43Z luc.hermitte $
" File: plugin/let.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 31st May 2010
" Last Update: $Date: 2010-05-31 20:48:43 -0400 (Mon, 31 May 2010) $
"------------------------------------------------------------------------
" Description:
" Defines a command :LetIfUndef that sets a variable if undefined
"
"------------------------------------------------------------------------
" Installation:
" Drop this file into {rtp}/plugin
" Requires Vim7+
" History:
" v2.2.1: first version of this command into lh-vim-lib
" TODO:
" }}}1
"=============================================================================
" Avoid global reinclusion {{{1
let s:k_version = 221
if &cp || (exists("g:loaded_let")
\ && (g:loaded_let >= s:k_version)
\ && !exists('g:force_reload_let'))
finish
endif
let g:loaded_let = s:k_version
let s:cpo_save=&cpo
set cpo&vim
" Avoid global reinclusion }}}1
"------------------------------------------------------------------------
" Commands and Mappings {{{1
command! -nargs=+ LetIfUndef call s:LetIfUndef(<f-args>)
" Commands and Mappings }}}1
"------------------------------------------------------------------------
" Functions {{{1
" Note: most functions are best placed into
" autoload/«your-initials»/«let».vim
" Keep here only the functions are are required when the plugin is loaded,
" like functions that help building a vim-menu for this plugin.
function! s:LetIfUndef(var, value)
if !exists(a:var)
let {a:var} = eval(a:value)
endif
endfunction
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,45 @@
"=============================================================================
" $Id: lhvl.vim 245 2010-09-19 22:40:10Z luc.hermitte $
" File: plugin/lhvl.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 27th Apr 2010
" Last Update: $Date: 2010-09-19 18:40:10 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description:
" Non-function resources from lh-vim-lib
"
"------------------------------------------------------------------------
" Installation:
" Drop the file into {rtp}/plugin
" History:
" v2.2.1 first version
" TODO: «missing features»
" }}}1
"=============================================================================
" Avoid global reinclusion {{{1
let s:k_version = 221
if &cp || (exists("g:loaded_lhvl")
\ && (g:loaded_lhvl >= s:k_version)
\ && !exists('g:force_reload_lhvl'))
finish
endif
let g:loaded_lhvl = s:k_version
let s:cpo_save=&cpo
set cpo&vim
" Avoid global reinclusion }}}1
"------------------------------------------------------------------------
" Commands and Mappings {{{1
" Moved from lh-cpp
command! PopSearch :call histdel('search', -1)| let @/=histget('search',-1)
" Commands and Mappings }}}1
"------------------------------------------------------------------------
" Functions {{{1
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,480 @@
"=============================================================================
" File: plugin/ui-functions.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" URL: http://hermitte.free.fr/vim/ressources/vimfiles/plugin/ui-functions.vim
"
" Version: 2.2.1
" Created: 18th nov 2002
" Last Update: 28th Nov 2007
"------------------------------------------------------------------------
" Description: Functions for the interaction with a User Interface.
" The UI can be graphical or textual.
" At first, this was designed to ease the syntax of
" mu-template's templates.
"
" Option: {{{2
" {[bg]:ui_type}
" = "g\%[ui]",
" = "t\%[ext]" ; the call must not be |:silent|
" = "f\%[te]"
" }}}2
"------------------------------------------------------------------------
" Installation: Drop this into one of your {rtp}/plugin/ directories.
" History: {{{2
" v0.01 Initial Version
" v0.02
" (*) Code "factorisations"
" (*) Help on <F1> enhanced.
" (*) Small changes regarding the parameter accepted
" (*) Function SWITCH
" v0.03
" (*) Small bug fix with INPUT()
" v0.04
" (*) New function: WHICH()
" v0.05
" (*) In vim7e, inputdialog() returns a trailing '\n'. INPUT() strips the
" NL character.
" v0.06
" (*) :s/echoerr/throw/ => vim7 only
" v2.2.0
" (*) menu to switch the ui_type
"
" TODO: {{{2
" (*) Save the hl-User1..9 before using them
" (*) Possibility other than &statusline:
" echohl User1 |echon "bla"|echohl User2|echon "bli"|echohl None
" (*) Wraps too long choices-line (length > term-width)
" (*) Add to the documentation: "don't use CTRL-C to abort !!"
" (*) Look if I need to support 'wildmode'
" (*) 3rd mode: return string for FTE
" (*) 4th mode: interaction in a scratch buffer
"
" }}}1
"=============================================================================
" Avoid reinclusion {{{1
"
if exists("g:loaded_ui_functions") && !exists('g:force_reload_ui_functions')
finish
endif
let g:loaded_ui_functions = 1
let s:cpo_save=&cpo
set cpo&vim
" }}}1
"------------------------------------------------------------------------
" External functions {{{1
" Function: IF(var, then, else) {{{2
function! IF(var,then, else)
let o = s:Opt_type() " {{{3
if o =~ 'g\%[ui]\|t\%[ext]' " {{{4
return a:var ? a:then : a:else
elseif o =~ 'f\%[te]' " {{{4
return s:if_fte(a:var, a:then, a:else)
else " {{{4
throw "UI-Fns::IF(): Unkonwn user-interface style (".o.")"
endif
" }}}3
endfunction
" Function: SWITCH(var, case, action [, case, action] [default_action]) {{{2
function! SWITCH(var, ...)
let o = s:Opt_type() " {{{3
if o =~ 'g\%[ui]\|t\%[ext]' " {{{4
let explicit_def = ((a:0 % 2) == 1)
let default = explicit_def ? a:{a:0} : ''
let i = a:0 - 1 - explicit_def
while i > 0
if a:var == a:{i}
return a:{i+1}
endif
let i = i - 2
endwhile
return default
elseif o =~ 'f\%[te]' " {{{4
return s:if_fte(a:var, a:then, a:else)
else " {{{4
throw "UI-Fns::SWITCH(): Unkonwn user-interface style (".o.")"
endif
" }}}3
endfunction
" Function: CONFIRM(text [, choices [, default [, type]]]) {{{2
function! CONFIRM(text, ...)
" 1- Check parameters {{{3
if a:0 > 4 " {{{4
throw "UI-Fns::CONFIRM(): too many parameters"
return 0
endif
" build the parameters string {{{4
let i = 1
while i <= a:0
if i == 1 | let params = 'a:{1}'
else | let params = params. ',a:{'.i.'}'
endif
let i = i + 1
endwhile
" 2- Choose the correct way to execute according to the option {{{3
let o = s:Opt_type()
if o =~ 'g\%[ui]' " {{{4
exe 'return confirm(a:text,'.params.')'
elseif o =~ 't\%[ext]' " {{{4
if !has('gui_running') && has('dialog_con')
exe 'return confirm(a:text,'.params.')'
else
exe 'return s:confirm_text("none", a:text,'.params.')'
endif
elseif o =~ 'f\%[te]' " {{{4
exe 'return s:confirm_fte(a:text,'.params.')'
else " {{{4
throw "UI-Fns::CONFIRM(): Unkonwn user-interface style (".o.")"
endif
" }}}3
endfunction
" Function: INPUT(prompt [, default ]) {{{2
function! INPUT(prompt, ...)
" 1- Check parameters {{{3
if a:0 > 4 " {{{4
throw "UI-Fns::INPUT(): too many parameters"
return 0
endif
" build the parameters string {{{4
let i = 1 | let params = ''
while i <= a:0
if i == 1 | let params = 'a:{1}'
else | let params = params. ',a:{'.i.'}'
endif
let i = i + 1
endwhile
" 2- Choose the correct way to execute according to the option {{{3
let o = s:Opt_type()
if o =~ 'g\%[ui]' " {{{4
exe 'return matchstr(inputdialog(a:prompt,'.params.'), ".\\{-}\\ze\\n\\=$")'
elseif o =~ 't\%[ext]' " {{{4
exe 'return input(a:prompt,'.params.')'
elseif o =~ 'f\%[te]' " {{{4
exe 'return s:input_fte(a:prompt,'.params.')'
else " {{{4
throw "UI-Fns::INPUT(): Unkonwn user-interface style (".o.")"
endif
" }}}3
endfunction
" Function: COMBO(prompt, choice [, ... ]) {{{2
function! COMBO(prompt, ...)
" 1- Check parameters {{{3
if a:0 > 4 " {{{4
throw "UI-Fns::COMBO(): too many parameters"
return 0
endif
" build the parameters string {{{4
let i = 1
while i <= a:0
if i == 1 | let params = 'a:{1}'
else | let params = params. ',a:{'.i.'}'
endif
let i = i + 1
endwhile
" 2- Choose the correct way to execute according to the option {{{3
let o = s:Opt_type()
if o =~ 'g\%[ui]' " {{{4
exe 'return confirm(a:prompt,'.params.')'
elseif o =~ 't\%[ext]' " {{{4
exe 'return s:confirm_text("combo", a:prompt,'.params.')'
elseif o =~ 'f\%[te]' " {{{4
exe 'return s:combo_fte(a:prompt,'.params.')'
else " {{{4
throw "UI-Fns::COMBO(): Unkonwn user-interface style (".o.")"
endif
" }}}3
endfunction
" Function: WHICH(function, prompt, choice [, ... ]) {{{2
function! WHICH(fn, prompt, ...)
" 1- Check parameters {{{3
" build the parameters string {{{4
let i = 1
while i <= a:0
if i == 1 | let params = 'a:{1}'
else | let params = params. ',a:{'.i.'}'
endif
let i = i + 1
endwhile
" 2- Execute the function {{{3
exe 'let which = '.a:fn.'(a:prompt,'.params.')'
if 0 >= which | return ''
elseif 1 == which
return substitute(matchstr(a:{1}, '^.\{-}\ze\%(\n\|$\)'), '&', '', 'g')
else
return substitute(
\ matchstr(a:{1}, '^\%(.\{-}\n\)\{'.(which-1).'}\zs.\{-}\ze\%(\n\|$\)')
\ , '&', '', 'g')
endif
" }}}3
endfunction
" Function: CHECK(prompt, choice [, ... ]) {{{2
function! CHECK(prompt, ...)
" 1- Check parameters {{{3
if a:0 > 4 " {{{4
throw "UI-Fns::CHECK(): too many parameters"
return 0
endif
" build the parameters string {{{4
let i = 1
while i <= a:0
if i == 1 | let params = 'a:{1}'
else | let params = params. ',a:{'.i.'}'
endif
let i = i + 1
endwhile
" 2- Choose the correct way to execute according to the option {{{3
let o = s:Opt_type()
if o =~ 'g\%[ui]' " {{{4
exe 'return s:confirm_text("check", a:prompt,'.params.')'
elseif o =~ 't\%[ext]' " {{{4
exe 'return s:confirm_text("check", a:prompt,'.params.')'
elseif o =~ 'f\%[te]' " {{{4
exe 'return s:check_fte(a:prompt,'.params.')'
else " {{{4
throw "UI-Fns::CHECK(): Unkonwn user-interface style (".o.")"
endif
" }}}3
endfunction
" }}}1
"------------------------------------------------------------------------
" Options setting {{{1
let s:OptionData = {
\ "variable": "ui_type",
\ "idx_crt_value": 1,
\ "values": ['gui', 'text', 'fte'],
\ "menu": { "priority": '500.2700', "name": '&Plugin.&LH.&UI type'}
\}
call lh#menu#def_toggle_item(s:OptionData)
" }}}1
"------------------------------------------------------------------------
" Internal functions {{{1
function! s:Option(name, default) " {{{2
if exists('b:ui_'.a:name) | return b:ui_{a:name}
elseif exists('g:ui_'.a:name) | return g:ui_{a:name}
else | return a:default
endif
endfunction
function! s:Opt_type() " {{{2
return s:Option('type', 'gui')
endfunction
"
" Function: s:status_line(current, hl [, choices] ) {{{2
" a:current: current item
" a:hl : Generic, Warning, Error
function! s:status_line(current, hl, ...)
" Highlightning {{{3
if a:hl == "Generic" | let hl = '%1*'
elseif a:hl == "Warning" | let hl = '%2*'
elseif a:hl == "Error" | let hl = '%3*'
elseif a:hl == "Info" | let hl = '%4*'
elseif a:hl == "Question" | let hl = '%5*'
else | let hl = '%1*'
endif
" Build the string {{{3
let sl_choices = '' | let i = 1
while i <= a:0
if i == a:current
let sl_choices = sl_choices . ' '. hl .
\ substitute(a:{i}, '&\(.\)', '%6*\1'.hl, '') . '%* '
else
let sl_choices = sl_choices . ' ' .
\ substitute(a:{i}, '&\(.\)', '%6*\1%*', '') . ' '
endif
let i = i + 1
endwhile
" }}}3
return sl_choices
endfunction
" Function: s:confirm_text(box, text [, choices [, default [, type]]]) {{{2
function! s:confirm_text(box, text, ...)
let help = "/<esc>/<s-tab>/<tab>/<left>/<right>/<cr>/<F1>"
" 1- Retrieve the parameters {{{3
let choices = ((a:0>=1) ? a:1 : '&Ok')
let default = ((a:0>=2) ? a:2 : (('check' == a:box) ? 0 : 1))
let type = ((a:0>=3) ? a:3 : 'Generic')
if 'none' == a:box | let prefix = ''
elseif 'combo' == a:box | let prefix = '( )_'
elseif 'check' == a:box | let prefix = '[ ]_'
let help = '/ '.help
else | let prefix = ''
endif
" 2- Retrieve the proposed choices {{{3
" Prepare the hot keys
let i = 0
while i != 26
let hotkey_{nr2char(i+65)} = 0
let i += 1
endwhile
let hotkeys = '' | let help_k = '/'
" Parse the choices
let i = 0
while choices != ""
let i = i + 1
let item = matchstr(choices, "^.\\{-}\\ze\\(\n\\|$\\)")
let choices = matchstr(choices, "\n\\zs.*$")
" exe 'anoremenu ]'.a:text.'.'.item.' :let s:choice ='.i.'<cr>'
if ('check' == a:box) && (strlen(default)>=i) && (1 == default[i-1])
" let choice_{i} = '[X]' . substitute(item, '&', '', '')
let choice_{i} = '[X]_' . item
else
" let choice_{i} = prefix . substitute(item, '&', '', '')
let choice_{i} = prefix . item
endif
if i == 1
let list_choices = 'choice_{1}'
else
let list_choices = list_choices . ',choice_{'.i.'}'
endif
" Update the hotkey.
let key = toupper(matchstr(choice_{i}, '&\zs.\ze'))
let hotkey_{key} = i
let hotkeys = hotkeys . tolower(key) . toupper(key)
let help_k = help_k . tolower(key)
endwhile
let nb_choices = i
if default > nb_choices | let default = nb_choices | endif
" 3- Run an interactive text menu {{{3
" Note: emenu can not be used through ":exe" {{{4
" let wcm = &wcm
" set wcm=<tab>
" exe ':emenu ]'.a:text.'.'."<tab>"
" let &wcm = wcm
" 3.1- Preparations for the statusline {{{4
" save the statusline
let sl = &l:statusline
" Color schemes for selected item {{{5
:hi User1 term=inverse,bold cterm=inverse,bold ctermfg=Yellow
\ guifg=Black guibg=Yellow
:hi User2 term=inverse,bold cterm=inverse,bold ctermfg=LightRed
\ guifg=Black guibg=LightRed
:hi User3 term=inverse,bold cterm=inverse,bold ctermfg=Red
\ guifg=Black guibg=Red
:hi User4 term=inverse,bold cterm=inverse,bold ctermfg=Cyan
\ guifg=Black guibg=Cyan
:hi User5 term=inverse,bold cterm=inverse,bold ctermfg=LightYellow
\ guifg=Black guibg=LightYellow
:hi User6 term=inverse,bold cterm=inverse,bold ctermfg=LightGray
\ guifg=DarkRed guibg=LightGray
" }}}5
" 3.2- Interactive loop {{{4
let help = "\r-- Keys available (".help_k.help.")"
" item selected at the start
let i = ('check' != a:box) ? default : 1
let direction = 0 | let toggle = 0
while 1
if 'combo' == a:box
let choice_{i} = substitute(choice_{i}, '^( )', '(*)', '')
endif
" Colored statusline
" Note: unfortunately the 'statusline' is a global option, {{{
" not a local one. I the hope that may change, as it does not provokes any
" error, I use '&l:statusline'. }}}
exe 'let &l:statusline=s:status_line(i, type,'. list_choices .')'
if has(':redrawstatus')
redrawstatus!
else
redraw!
endif
" Echo the current selection
echo "\r". a:text.' '.substitute(choice_{i}, '&', '', '')
" Wait the user to hit a key
let key=getchar()
let complType=nr2char(key)
" If the key hit matched awaited keys ...
if -1 != stridx(" \<tab>\<esc>\<enter>".hotkeys,complType) ||
\ (key =~ "\<F1>\\|\<right>\\|\<left>\\|\<s-tab>")
if key == "\<F1>" " Help {{{5
redraw!
echohl StatusLineNC
echo help
echohl None
let key=getchar()
let complType=nr2char(key)
endif
" TODO: support CTRL-D
if complType == "\<enter>" " Validate {{{5
break
elseif complType == " " " check box {{{5
let toggle = 1
elseif complType == "\<esc>" " Abort {{{5
let i = -1 | break
elseif complType == "\<tab>" || key == "\<right>" " Next {{{5
let direction = 1
elseif key =~ "\<left>\\|\<s-tab>" " Previous {{{5
let direction = -1
elseif -1 != stridx(hotkeys, complType ) " Hotkeys {{{5
if '' == complType | continue | endif
let direction = hotkey_{toupper(complType)} - i
let toggle = 1
" else
endif
" }}}5
endif
if direction != 0 " {{{5
if 'combo' == a:box
let choice_{i} = substitute(choice_{i}, '^(\*)', '( )', '')
endif
let i = i + direction
if i > nb_choices | let i = 1
elseif i == 0 | let i = nb_choices
endif
let direction = 0
endif
if toggle == 1 " {{{5
if 'check' == a:box
let choice_{i} = ((choice_{i}[1] == ' ')? '[X]' : '[ ]')
\ . strpart(choice_{i}, 3)
endif
let toggle = 0
endif
endwhile " }}}4
" 4- Terminate {{{3
" Clear screen
redraw!
" Restore statusline
let &l:statusline=sl
" Return
if (i == -1) || ('check' != a:box)
return i
else
let r = '' | let i = 1
while i <= nb_choices
let r = r . ((choice_{i}[1] == 'X') ? '1' : '0')
let i = i + 1
endwhile
return r
endif
endfunction
" }}}1
"------------------------------------------------------------------------
" Functions that insert fte statements {{{1
" Function: s:if_fte(var, then, else) {{{2
" Function: s:confirm_fte(text, [, choices [, default [, type]]]) {{{2
" Function: s:input_fte(prompt [, default]) {{{2
" Function: s:combo_fte(prompt, choice [, ...]) {{{2
" Function: s:check_fte(prompt, choice [, ...]) {{{2
" }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,104 @@
" File: plugin/words_tools.vim
" Author: Luc Hermitte <hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" URL: http://hermitte.free.fr/vim/ressources/vim_dollar/plugin/words_tools.vim
"
" Last Update: 14th nov 2002
" Purpose: Define functions better than expand("<cword>")
"
" Note: They are expected to be used in insert mode (thanks to <c-r>
" or <c-o>)
"
"===========================================================================
" Return the current keyword, uses spaces to delimitate {{{1
function! GetNearestKeyword()
let c = col ('.')-1
let ll = getline('.')
let ll1 = strpart(ll,0,c)
let ll1 = matchstr(ll1,'\k*$')
let ll2 = strpart(ll,c,strlen(ll)-c+1)
let ll2 = matchstr(ll2,'^\k*')
" let ll2 = strpart(ll2,0,match(ll2,'$\|\s'))
return ll1.ll2
endfunction
" Return the current word, uses spaces to delimitate {{{1
function! GetNearestWord()
let c = col ('.')-1
let l = line('.')
let ll = getline(l)
let ll1 = strpart(ll,0,c)
let ll1 = matchstr(ll1,'\S*$')
let ll2 = strpart(ll,c,strlen(ll)-c+1)
let ll2 = strpart(ll2,0,match(ll2,'$\|\s'))
""echo ll1.ll2
return ll1.ll2
endfunction
" Return the word before the cursor, uses spaces to delimitate {{{1
" Rem : <cword> is the word under or after the cursor
function! GetCurrentWord()
let c = col ('.')-1
let l = line('.')
let ll = getline(l)
let ll1 = strpart(ll,0,c)
let ll1 = matchstr(ll1,'\S*$')
if strlen(ll1) == 0
return ll1
else
let ll2 = strpart(ll,c,strlen(ll)-c+1)
let ll2 = strpart(ll2,0,match(ll2,'$\|\s'))
return ll1.ll2
endif
endfunction
" Return the keyword before the cursor, uses \k to delimitate {{{1
" Rem : <cword> is the word under or after the cursor
function! GetCurrentKeyword()
let c = col ('.')-1
let l = line('.')
let ll = getline(l)
let ll1 = strpart(ll,0,c)
let ll1 = matchstr(ll1,'\k*$')
if strlen(ll1) == 0
return ll1
else
let ll2 = strpart(ll,c,strlen(ll)-c+1)
let ll2 = matchstr(ll2,'^\k*')
" let ll2 = strpart(ll2,0,match(ll2,'$\|\s'))
return ll1.ll2
endif
endfunction
" Extract the word before the cursor, {{{1
" use keyword definitions, skip latter spaces (see "bla word_accepted ")
function! GetPreviousWord()
let lig = getline(line('.'))
let lig = strpart(lig,0,col('.')-1)
return matchstr(lig, '\<\k*\>\s*$')
endfunction
" GetLikeCTRL_W() retrieves the characters that i_CTRL-W deletes. {{{1
" Initial need by Hari Krishna Dara <hari_vim@yahoo.com>
" Last ver:
" Pb: "if strlen(w) == " --> ") == " instead of just "== ".
" There still exists a bug regarding the last char of a line. VIM bug ?
function! GetLikeCTRL_W()
let lig = getline(line('.'))
let lig = strpart(lig,0,col('.')-1)
" treat ending spaces apart.
let s = matchstr(lig, '\s*$')
let lig = strpart(lig, 0, strlen(lig)-strlen(s))
" First case : last characters belong to a "word"
let w = matchstr(lig, '\<\k\+\>$')
if strlen(w) == 0
" otherwise, they belong to a "non word" (without any space)
let w = substitute(lig, '.*\(\k\|\s\)', '', 'g')
endif
return w . s
endfunction
" }}}1
"========================================================================
" vim60: set fdm=marker:

View File

@ -0,0 +1,284 @@
"=============================================================================
" $Id: function.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: tests/lh/function.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 03rd Nov 2008
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description:
" Tests for autoload/lh/function.vim
"
"------------------------------------------------------------------------
" Installation: «install details»
" History: «history»
" TODO: «missing features»
" }}}1
"=============================================================================
UTSuite [lh-vim-lib] Testing lh#function plugin
runtime autoload/lh/function.vim
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
function! Test(...)
let nb = len(a:000)
" echo "test(".nb.':' .join(a:000, ' -- ')')'
let i =0
while i!= len(a:000)
echo "Test: type(".i.")=".type(a:000[i]).' --> '. string(a:000[i])
let i += 1
endwhile
endfunction
function! Print(...)
let res = lh#list#accumulate([1,2,'foo'], 'string', 'join(v:1_, " ## ")')
return res
endfunction
function! Id(...)
return copy(a:000)
endfunction
function! s:TestId()
let r = Id(1, 'string', [0], [[1]], {'ffo':42}, function('exists'), 1.2)
Assert! len(r) == 7
Assert! should#be#number (r[0])
Assert! should#be#string (r[1])
Assert! should#be#list (r[2])
Assert! should#be#list (r[3])
Assert! should#be#dict (r[4])
Assert! should#be#funcref(r[5])
Assert! should#be#float (r[6])
Assert r[0] == 1
Assert r[1] == 'string'
Assert r[2] == [0]
Assert r[3] == [[1]]
Assert r[4].ffo == 42
Assert r[5] == function('exists')
Assert r[6] == 1.2
endfunction
function! s:Test_bind()
" lh#function#bind + lh#function#execute
let rev4 = lh#function#bind(function('Id'), 'v:4_', 42, 'v:3_', 'v:2_', 'v:1_')
let r = lh#function#execute(rev4, 1,'two','three', [4,5])
Assert! len(r) == 5
Assert! should#be#list (r[0])
Assert! should#be#number (r[1])
Assert! should#be#string (r[2])
Assert! should#be#string (r[3])
Assert! should#be#number (r[4])
Assert r[0] == [4,5]
Assert r[1] == 42
Assert r[2] == 'three'
Assert r[3] == 'two'
Assert r[4] == 1
endfunction
function! s:Test_bind_compound_vars()
" lh#function#bind + lh#function#execute
let rev4 = lh#function#bind(function('Id'), 'v:4_', 'v:1_ . v:2_', 'v:3_', 'v:2_', 'v:1_')
let r = lh#function#execute(rev4, 1,'two','three', [4,5])
Assert! len(r) == 5
Assert! should#be#list (r[0])
Assert! should#be#string (r[1])
Assert! should#be#string (r[2])
Assert! should#be#string (r[3])
Assert! should#be#number (r[4])
Assert r[0] == [4,5]
Assert r[1] == '1two'
Assert r[2] == 'three'
Assert r[3] == 'two'
Assert r[4] == 1
endfunction
function! s:Test_execute_func_string_name()
" function name as string
let r = lh#function#execute('Id', 1,'two',3)
Assert! len(r) == 3
Assert! should#be#number (r[0])
Assert! should#be#string (r[1])
Assert! should#be#number (r[2])
Assert r[0] == 1
Assert r[1] == 'two'
Assert r[2] == 3
endfunction
function! s:Test_execute_string_expr()
" exp as binded-string
let r = lh#function#execute('Id(12,len(v:2_).v:2_, 42, v:3_, v:1_)', 1,'two',3)
Assert! len(r) == 5
Assert! should#be#number (r[0])
Assert! should#be#string (r[1])
Assert! should#be#number (r[2])
Assert! should#be#number (r[3])
Assert! should#be#number (r[4])
Assert r[0] == 12
Assert r[1] == len('two').'two'
Assert r[2] == 42
Assert r[3] == 3
Assert r[4] == 1
endfunction
function! s:Test_execute_func()
" calling a function() + bind
let r = lh#function#execute(function('Id'), 1,'two','v:1_',['a',42])
Assert! len(r) == 4
Assert! should#be#number (r[0])
Assert! should#be#string (r[1])
Assert! should#be#string (r[2])
Assert! should#be#list (r[3])
Assert r[0] == 1
Assert r[1] == 'two'
Assert r[2] == 'v:1_'
Assert r[3] == ['a', 42]
endfunction
"------------------------------------------------------------------------
function! s:Test_bind_func_string_name_AND_execute()
" function name as string
let rev3 = lh#function#bind('Id', 'v:3_', 12, 'v:2_', 'v:1_')
let r = lh#function#execute(rev3, 1,'two',3)
Assert! len(r) == 4
Assert! should#be#number (r[0])
Assert! should#be#number (r[1])
Assert! should#be#string (r[2])
Assert! should#be#number (r[3])
Assert r[0] == 3
Assert r[1] == 12
Assert r[2] == 'two'
Assert r[3] == 1
endfunction
function! s:Test_bind_string_expr_AND_execute()
" expressions as string
let rev3 = lh#function#bind('Id(12,len(v:2_).v:2_, 42, v:3_, v:1_)')
let r = lh#function#execute(rev3, 1,'two',3)
Assert! len(r) == 5
Assert! should#be#number (r[0])
Assert! should#be#string (r[1])
Assert! should#be#number (r[2])
Assert! should#be#number (r[3])
Assert! should#be#number (r[4])
Assert r[0] == 12
Assert r[1] == len('two').'two'
Assert r[2] == 42
Assert r[3] == 3
Assert r[4] == 1
endfunction
function! s:Test_double_bind_func_name()
let f1 = lh#function#bind('Id', 1, 2, 'v:1_', 4, 'v:2_')
" Comment "f1=".string(f1)
let r = lh#function#execute(f1, 3, 5)
Assert! len(r) == 5
let i = 0
while i != len(r)
Assert! should#be#number (r[i])
Assert r[i] == i+1
let i += 1
endwhile
" f2
let f2 = lh#function#bind(f1, 'v:1_', 5)
" Comment "f2=f1(v:1_, 5)=".string(f2)
let r = lh#function#execute(f2, 3)
Assert! len(r) == 5
let i = 0
while i != len(r)
Assert! should#be#number (r[i])
" echo "?? ".(r[i])."==".(i+1)
Assert r[i] == i+1
let i += 1
endwhile
endfunction
function! s:Test_double_bind_func()
let f1 = lh#function#bind(function('Id'), 1, 2, 'v:1_', 4, 'v:2_')
" Comment "f1=".string(f1)
let r = lh#function#execute(f1, 3, 5)
Assert! len(r) == 5
let i = 0
while i != len(r)
Assert! should#be#number (r[i])
Assert r[i] == i+1
let i += 1
endwhile
" f2
let f2 = lh#function#bind(f1, 'v:1_', 5)
" Comment "f2=f1(v:1_, 5)=".string(f2)
let r = lh#function#execute(f2, 3)
Assert! len(r) == 5
let i = 0
while i != len(r)
Assert! should#be#number (r[i])
Assert r[i] == i+1
let i += 1
endwhile
endfunction
function! s:Test_double_bind_func_cplx()
let s:bar = "bar"
let f1 = lh#function#bind(function('Id'), 1, 2, 'v:1_', 4, 'v:2_', 'v:3_', 'v:4_', 'v:5_', 'v:6_', 'v:7_')
" Comment "f1=".string(f1)
let f2 = lh#function#bind(f1, 'v:1_', 5, 'foo', s:bar, 'len(s:bar.v:1_)+v:1_', [1,2], '[v:1_, v:2_]')
" Comment "f2=f1(v:1_, 5)=".string(f2)
let r = lh#function#execute(f2, 42, "foo")
Assert! 0 && "not ready"
Comment "2bcpl# ".string(r)
endfunction
function! s:Test_double_bind_expr()
let f1 = lh#function#bind('Id(1, 2, v:1_, v:3_, v:2_)')
Comment "2be# f1=".string(f1)
let r = lh#function#execute(f1, 3, 5, 4)
Comment "2be# ".string(r)
Assert! len(r) == 5
let i = 0
while i != len(r)
Assert! should#be#number (r[i])
Assert r[i] == i+1
let i += 1
endwhile
" f2
let f2 = lh#function#bind(f1, 'v:1_', '"foo"', [])
Comment "2be# f2=f1(v:1_, 5)=".string(f2)
let r = lh#function#execute(f2, 3)
Comment "2be# ".string(r)
Assert! len(r) == 5
let i = 0
while i != len(r)-2
Assert! should#be#number (r[i])
Assert r[i] == i+1
let i += 1
endwhile
Assert! should#be#list (r[-2])
Assert r[-2] == []
Assert! should#be#string (r[-1])
Assert r[-1] == 'foo'
endfunction
"todo: write double-binded tests for all kind of binded parameters:
" 'len(g:bar)'
" 42
" []
" v:1_ + len(v:2_.v:3_)
" '"foo"'
" v:1_
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,165 @@
"=============================================================================
" $Id: list.vim 238 2010-06-01 00:47:16Z luc.hermitte $
" File: tests/lh/list.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 19th Nov 2008
" Last Update: $Date: 2010-05-31 20:47:16 -0400 (Mon, 31 May 2010) $
"------------------------------------------------------------------------
" Description:
" Tests for autoload/lh/list.vim
"
"------------------------------------------------------------------------
" Installation: «install details»
" History: «history»
" TODO: «missing features»
" }}}1
"=============================================================================
UTSuite [lh-vim-lib] Testing lh#list functions
runtime autoload/lh/function.vim
runtime autoload/lh/list.vim
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" Find_if
function! s:Test_Find_If_string_predicate()
:let b = { 'min': 12, 'max': 42 }
:let l = [ 1, 5, 48, 25, 5, 28, 6]
:let i = lh#list#Find_if(l, 'v:val>v:1_.min && v:val<v:1_.max && v:val%v:2_==0', [b, 2] )
" echo i . '/' . len(l)
Assert i == 5
Assert l[i] == 28
" :echo l[i]
endfunction
function! s:Test_Find_If_functor_predicate()
:let l = [ 1, 5, 48, 25, 5, 28, 6]
:let i = lh#list#find_if(l, 'v:1_>12 && v:1_<42 && v:1_%2==0')
" echo i . '/' . len(l)
Assert i == 5
Assert l[i] == 28
" :echo l[i]
endfunction
function! s:Test_find_if_double_bind()
:let b = { 'min': 12, 'max': 42 }
:let l = [ 1, 5, 48, 25, 5, 28, 6]
:let f = lh#function#bind( 'v:3_>v:1_.min && v:3_<v:1_.max && v:3_%v:2_==0')
:let p = lh#function#bind(f, b,2,'v:1_')
:let i = lh#list#find_if(l, p)
:echo l[i]
endfunction
" double bind is not yet operational
UTIgnore Test_find_if_double_bind
"------------------------------------------------------------------------
" Unique Sorting
function! CmpNumbers(lhs, rhs)
if a:lhs < a:rhs | return -1
elseif a:lhs == a:rhs | return 0
else | return +1
endif
endfunction
function! s:Test_sort()
:let l = [ 1, 5, 48, 25, 5, 28, 6]
:let expected = [ 1, 5, 6, 25, 28, 48]
:let s = lh#list#unique_sort(l, "CmpNumbers")
" Comment string(s)
Assert s == expected
endfunction
function! s:Test_sort2()
:let l = [ 1, 5, 48, 25, 5, 28, 6]
:let expected = [ 1, 5, 6, 25, 28, 48]
:let s = lh#list#unique_sort2(l, "CmpNumbers")
" Comment string(s)
Assert s == expected
endfunction
"------------------------------------------------------------------------
" Searchs
function! s:TestBinarySearches()
let v1 = [ -3, -2, -1, -1, 0, 0, 1, 2, 3, 4, 6 ]
let i = lh#list#lower_bound(v1, 3)
Assert v1[i] == 3
let i = lh#list#upper_bound(v1, 3)
Assert v1[i] == 4
let r = lh#list#equal_range(v1, 3)
Assert v1[r[0]:r[1]-1] == [3]
let i = lh#list#lower_bound(v1, -1)
Assert v1[i] == -1
let i = lh#list#upper_bound(v1, -1)
Assert v1[i] == 0
let r = lh#list#equal_range(v1, -1)
Assert v1[r[0]:r[1]-1] == [-1, -1]
let i = lh#list#lower_bound(v1, 5)
Assert v1[i] == 6
let i = lh#list#upper_bound(v1, 5)
Assert v1[i] == 6
let r = lh#list#equal_range(v1, 5)
Assert v1[r[0]:r[1]-1] == []
Assert len(v1) == lh#list#lower_bound(v1, 10)
Assert len(v1) == lh#list#upper_bound(v1, 10)
Assert [len(v1), len(v1)] == lh#list#equal_range(v1, 10)
endfunction
"------------------------------------------------------------------------
" accumulate
function! s:Test_accumulate_len_strings()
let strings = [ 'foo', 'bar', 'toto' ]
let len = eval(lh#list#accumulate(strings, 'strlen', 'join(v:1_, "+")'))
Assert len == 3+3+4
endfunction
function! s:Test_accumulate_join()
let ll = [ 1, 2, 'foo', ['bar'] ]
let res = lh#list#accumulate(ll, 'string', 'join(v:1_, " ## ")')
Assert res == "1 ## 2 ## 'foo' ## ['bar']"
" This test will fail because it seems :for each loop cannot iterate on
" heterogeneous containers
endfunction
"------------------------------------------------------------------------
" Copy_if
function! s:Test_copy_if()
:let l = [ 1, 25, 5, 48, 25, 5, 28, 6]
:let expected = [ 25, 48, 25, 28, 6]
:let s = lh#list#copy_if(l, [], "v:1_ > 5")
" Comment string(s)
Assert s == expected
endfunction
"------------------------------------------------------------------------
" subset
function! s:Test_subset()
:let l = [ 1, 25, 5, 48, 25, 5, 28, 6]
:let indices = [ 0, 5, 7, 3 ]
:let expected = [ 1, 5, 6, 48 ]
:let s = lh#list#subset(l, indices)
" Comment string(s)
Assert s == expected
endfunction
"------------------------------------------------------------------------
" intersect
function! s:Test_intersect()
:let l1 = [ 1, 25, 7, 48, 26, 5, 28, 6]
:let l2 = [ 3, 8, 7, 25, 6 ]
:let expected = [ 25, 7, 6 ]
:let s = lh#list#intersect(l1, l2)
" Comment string(s)
Assert s == expected
endfunction
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,173 @@
"=============================================================================
" $Id: path.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: tests/lh/path.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 28th May 2009
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description:
" Tests for autoload/lh/path.vim
"
"------------------------------------------------------------------------
" Installation: «install details»
" History: «history»
" TODO: «missing features»
" }}}1
"=============================================================================
UTSuite [lh-vim-lib] Testing lh#path functions
runtime autoload/lh/path.vim
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
function! s:Test_simplify()
Assert lh#path#simplify('a/b/c') == 'a/b/c'
Assert lh#path#simplify('a/b/./c') == 'a/b/c'
Assert lh#path#simplify('./a/b/./c') == 'a/b/c'
Assert lh#path#simplify('./a/../b/./c') == 'b/c'
Assert lh#path#simplify('../a/../b/./c') == '../b/c'
Assert lh#path#simplify('a\b\c') == 'a\b\c'
Assert lh#path#simplify('a\b\.\c') == 'a\b\c'
Assert lh#path#simplify('.\a\b\.\c') == 'a\b\c'
if exists('+shellslash')
Assert lh#path#simplify('.\a\..\b\.\c') == 'b\c'
Assert lh#path#simplify('..\a\..\b\.\c') == '..\b\c'
endif
endfunction
function! s:Test_strip_common()
let paths = ['foo/bar/file', 'foo/file', 'foo/foo/file']
let expected = [ 'bar/file', 'file', 'foo/file']
Assert lh#path#strip_common(paths) == expected
endfunction
function! s:Test_common()
Assert 'foo/' == lh#path#common(['foo/bar/dir', 'foo'])
Assert 'foo/bar/' == lh#path#common(['foo/bar/dir', 'foo/bar'])
Assert 'foo/' == lh#path#common(['foo/bar/dir', 'foo/bar2'])
endfunction
function! s:Test_strip_start()
let expected = 'template/bar.template'
Assert lh#path#strip_start($HOME.'/.vim/template/bar.template',
\ [ $HOME.'/.vim', $HOME.'/vimfiles', '/usr/local/share/vim' ])
\ == expected
Assert lh#path#strip_start($HOME.'/vimfiles/template/bar.template',
\ [ $HOME.'/.vim', $HOME.'/vimfiles', '/usr/local/share/vim' ])
\ == expected
Assert lh#path#strip_start('/usr/local/share/vim/template/bar.template',
\ [ $HOME.'/.vim', $HOME.'/vimfiles', '/usr/local/share/vim' ])
\ == expected
endfunction
function! s:Test_IsAbsolutePath()
" nix paths
Assert lh#path#is_absolute_path('/usr/local')
Assert lh#path#is_absolute_path($HOME)
Assert ! lh#path#is_absolute_path('./usr/local')
Assert ! lh#path#is_absolute_path('.usr/local')
" windows paths
Assert lh#path#is_absolute_path('e:\usr\local')
Assert ! lh#path#is_absolute_path('.\usr\local')
Assert ! lh#path#is_absolute_path('.usr\local')
" UNC paths
Assert lh#path#is_absolute_path('\\usr\local')
Assert lh#path#is_absolute_path('//usr/local')
endfunction
function! s:Test_IsURL()
" nix paths
Assert ! lh#path#is_url('/usr/local')
Assert ! lh#path#is_url($HOME)
Assert ! lh#path#is_url('./usr/local')
Assert ! lh#path#is_url('.usr/local')
" windows paths
Assert ! lh#path#is_url('e:\usr\local')
Assert ! lh#path#is_url('.\usr\local')
Assert ! lh#path#is_url('.usr\local')
" UNC paths
Assert ! lh#path#is_url('\\usr\local')
Assert ! lh#path#is_url('//usr/local')
" URLs
Assert lh#path#is_url('http://www.usr/local')
Assert lh#path#is_url('https://www.usr/local')
Assert lh#path#is_url('ftp://www.usr/local')
Assert lh#path#is_url('sftp://www.usr/local')
Assert lh#path#is_url('dav://www.usr/local')
Assert lh#path#is_url('fetch://www.usr/local')
Assert lh#path#is_url('file://www.usr/local')
Assert lh#path#is_url('rcp://www.usr/local')
Assert lh#path#is_url('rsynch://www.usr/local')
Assert lh#path#is_url('scp://www.usr/local')
endfunction
function! s:Test_ToRelative()
let pwd = getcwd()
Assert lh#path#to_relative(pwd.'/foo/bar') == 'foo/bar'
Assert lh#path#to_relative(pwd.'/./foo') == 'foo'
Assert lh#path#to_relative(pwd.'/foo/../bar') == 'bar'
" Does not work yet as it returns an absolute path it that case
Assert lh#path#to_relative(pwd.'/../bar') == '../bar'
endfunction
function! s:Test_relative_path()
Assert lh#path#relative_to('foo/bar/dir', 'foo') == '../../'
Assert lh#path#relative_to('foo', 'foo/bar/dir') == 'bar/dir/'
Assert lh#path#relative_to('foo/bar', 'foo/bar2/dir') == '../bar2/dir/'
let pwd = getcwd()
Assert lh#path#relative_to(pwd ,pwd.'/../bar') == '../bar/'
endfunction
function! s:Test_search_vimfiles()
let expected_win = $HOME . '/vimfiles'
let expected_nix = $HOME . '/.vim'
let what = lh#path#to_regex($HOME.'/').'\(vimfiles\|.vim\)'
" Comment what
let z = lh#path#find(&rtp,what)
if has('win16')||has('win32')||has('win64')
Assert z == expected_win
else
Assert z == expected_nix
endif
endfunction
function! s:Test_path_depth()
Assert 0 == lh#path#depth('.')
Assert 0 == lh#path#depth('./')
Assert 0 == lh#path#depth('.\')
Assert 1 == lh#path#depth('toto')
Assert 1 == lh#path#depth('toto/')
Assert 1 == lh#path#depth('toto\')
Assert 1 == lh#path#depth('toto/.')
Assert 1 == lh#path#depth('toto\.')
Assert 1 == lh#path#depth('toto/./.')
Assert 1 == lh#path#depth('toto\.\.')
Assert 0 == lh#path#depth('toto/..')
if exists('+shellslash')
Assert 0 == lh#path#depth('toto\..')
endif
Assert 2 == lh#path#depth('toto/titi/')
Assert 2 == lh#path#depth('toto\titi\')
Assert 2 == lh#path#depth('/toto/titi/')
Assert 2 == lh#path#depth('c:/toto/titi/')
Assert 2 == lh#path#depth('c:\toto/titi/')
" todo: make a choice about "negative" paths like "../../foo"
Assert -1 == lh#path#depth('../../foo')
endfunction
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,83 @@
"=============================================================================
" $Id: test-Fargs2String.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: tests/lh/test-Fargs2String.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 16th Apr 2007
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description: Tests for lh-vim-lib . lh#command#Fargs2String
"
"------------------------------------------------------------------------
" Installation:
" Relies on the version «patched by myself|1?» of vim_units
" History: «history»
" TODO: «missing features»
" }}}1
"=============================================================================
function! s:TestEmpty()
let empty = []
let res = lh#command#Fargs2String(empty)
call VUAssertEquals(len(empty), 0, 'Expected empty', 22)
call VUAssertEquals(res, '', 'Expected empty result', 23)
endfunction
function! s:TestSimpleText1()
let expected = 'text'
let one = [ expected ]
let res = lh#command#Fargs2String(one)
call VUAssertEquals(len(one), 0, 'Expected empty', 27)
call VUAssertEquals(res, expected, 'Expected a simple result', 28)
endfunction
function! s:TestSimpleTextN()
let expected = 'text'
let list = [ expected , 'stuff1', 'stuff2']
let res = lh#command#Fargs2String(list)
call VUAssertEquals(len(list), 2, 'Expected not empty', 38)
call VUAssertEquals(res, expected, 'Expected a simple result', 39)
endfunction
function! s:TestComposedN()
let expected = '"a several tokens string"'
let list = [ '"a', 'several', 'tokens', 'string"', 'stuff1', 'stuff2']
let res = lh#command#Fargs2String(list)
call VUAssertEquals(len(list), 2, 'Expected not empty', 46)
call VUAssertEquals(res, expected, 'Expected a composed string', 47)
call VUAssertEquals(list, ['stuff1', 'stuff2'], 'Expected a list', 48)
call VUAssertNotSame(list, ['stuff1', 'stuff2'], 'Expected different lists', 49)
endfunction
function! s:TestComposed1()
let expected = '"string"'
let list = [ '"string"', 'stuff1', 'stuff2']
let res = lh#command#Fargs2String(list)
call VUAssertEquals(len(list), 2, 'Expected not empty', 56)
call VUAssertEquals(res, expected, 'Expected a string', 57)
call VUAssertEquals(list, ['stuff1', 'stuff2'], 'Expected a list', 58)
call VUAssertNotSame(list, ['stuff1', 'stuff2'], 'Expected different lists', 59)
endfunction
function! s:TestInvalidString()
let expected = '"a string'
let list = [ '"a', 'string']
let res = lh#command#Fargs2String(list)
call VUAssertEquals(len(list), 0, 'Expected empty', 66)
call VUAssertEquals(res, expected, 'Expected an invalid string', 67)
endfunction
function! AllTests()
call s:TestEmpty()
call s:TestSimpleText1()
call s:TestSimpleTextN()
call s:TestComposed1()
call s:TestComposedN()
endfunction
" call VURunnerRunTest('AllTests')
VURun % AllTests
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,65 @@
"=============================================================================
" $Id: test-askmenu.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: test-buffer-menu.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 18th Apr 2007
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description:
" Test units for buffermenu.vim
"
"------------------------------------------------------------------------
" Installation: Requires:
" (*) Vim 7.0+
" (*) vim_units.vim v0.2/1.0?
" Vimscript # «???»
" (*) lh-vim-lib (lh#ask#menu)
"
" User Manual:
" Source this file.
"
" History:
" (*) 17th Apr 2007: First version
" TODO: «missing features»
" }}}1
"=============================================================================
"=============================================================================
let s:cpo_save=&cpo
"------------------------------------------------------------------------
" Functions {{{1
function! TestAskMenu()
imenu 42.40.10 &LH-Tests.&Menu.&ask.i iask
inoremenu 42.40.10 &LH-Tests.&Menu.&ask.inore inoreask
nmenu 42.40.10 &LH-Tests.&Menu.&ask.n nask
nnoremenu 42.40.10 &LH-Tests.&Menu.&ask.nnore nnoreask
nmenu <script> 42.40.10 &LH-Tests.&Menu.&ask.nscript nscriptask
nnoremenu <script> 42.40.10 &LH-Tests.&Menu.&ask.nnnscript nnscriptask
vmenu 42.40.10 &LH-Tests.&Menu.&ask.v vask
vnoremenu 42.40.10 &LH-Tests.&Menu.&ask.vnore vnoreask
call s:CheckInMode('i', 'i')
endfunction
function! s:CheckInMode(mode, name)
let g:menu = lh#askvim#menu('LH-Tests.Menu.ask.'.a:name, a:mode)
let g:name = a:name
" VUAssert 55 Equals g:menu.name g:name "Name mismatch"
" VUAssert 56 Equals g:menu.priority '42.40.10' "Priority mismatch"
" VUAssert 57 Fail "parce qu'il le faut bien"
echomsg "name= ".g:menu.name
echomsg "prio= ".g:menu.priority
endfunction
" Functions }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,69 @@
" $Id: test-command.vim 156 2010-05-07 00:54:36Z luc.hermitte $
" Tests for lh-vim-lib . lh#command
" FindFilter(filter): Helper {{{3
function! s:FindFilter(filter)
let filter = a:filter . '.vim'
let result =globpath(&rtp, "compiler/BTW-".filter) . "\n" .
\ globpath(&rtp, "compiler/BTW_".filter). "\n" .
\ globpath(&rtp, "compiler/BTW/".filter)
let result = substitute(result, '\n\n', '\n', 'g')
let result = substitute(result, '^\n', '', 'g')
return result
endfunction
function! s:ComplFilter(filter)
let files = s:FindFilter('*')
let files = substitute(files,
\ '\(^\|\n\).\{-}compiler[\\/]BTW[-_\\/]\(.\{-}\)\.vim\>\ze\%(\n\|$\)',
\ '\1\2', 'g')
return files
endfunction
function! s:Add()
endfunction
let s:v1 = 'v1'
let s:v2 = 2
function! s:Foo(i)
return a:i*a:i
endfunction
function! s:echo(params)
echo s:{join(a:params, '')}
endfunction
function! Echo(params)
" echo "Echo(".string(a:params).')'
let expr = 's:'.join(a:params, '')
" echo expr
exe 'echo '.expr
endfunction
let TBTWcommand = {
\ "name" : "TBT",
\ "arg_type" : "sub_commands",
\ "arguments" :
\ [
\ { "name" : "echo",
\ "arg_type" : "function",
\ "arguments" : "v1,v2",
\ "action": function("\<sid>echo") },
\ { "name" : "Echo",
\ "arg_type" : "function",
\ "arguments" : "v1,v2",
\ "action": function("Echo") },
\ { "name" : "help" },
\ { "name" : "add",
\ "arguments": function("s:ComplFilter"),
\ "action" : function("s:Add") }
\ ]
\ }
call lh#command#new(TBTWcommand)
nnoremap µ :call lh#command#new(TBTWcommand)<cr>
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,54 @@
"=============================================================================
" $Id: test-menu-map.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: tests/lh/test-menu-map.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 05th Dec 2006
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description: Tests for lh-vim-lib . lh#menu#
"
"------------------------------------------------------------------------
" Installation: «install details»
" History: «history»
" TODO: «missing features»
" }}}1
"=============================================================================
" let g:want_buffermenu_or_global_disable = 1
" let b:want_buffermenu_or_global_disable = 1
" echo lh#option#get("want_buffermenu_or_global_disable", 1, "bg")
" Call a command (':Command')
call lh#menu#make("nic", '42.50.340',
\ '&LH-Tests.&Menu-Make.Build Ta&gs', "<C-L>g",
\ '<buffer>',
\ ":echo 'TeXtags'<CR>")
" With '{' expanding to '{}××', or '{}' regarding the mode
call lh#menu#IVN_make('42.50.360.200',
\ '&LH-Tests.&Menu-Make.&Insert.\toto{}', ']toto',
\ '\\toto{',
\ '{%i\\toto<ESC>%l',
\ "viw]toto")
" Noremap for the visual maps
call lh#menu#IVN_make('42.50.360.200',
\ '&LH-Tests.&Menu-Make.&Insert.\titi{}', ']titi',
\ '\\titi{',
\ '<ESC>`>a}<ESC>`<i\\titi{<ESC>%l',
\ "viw]titi",
\ 0, 1, 0)
" Noremap for the insert and visual maps
call lh#menu#IVN_make('42.50.360.200',
\ '&LH-Tests.&Menu-Make.&Insert.<tata></tata>', ']tata',
\ '<tata></tata><esc>?<<CR>i',
\ '<ESC>`>a</tata><ESC>`<i<tata><ESC>/<\\/tata>/e1<CR>',
\ "viw]tata",
\ 1, 1, 0)
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,84 @@
"=============================================================================
" $Id: test-toggle-menu.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: tests/lh/topological-sort.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 17th Apr 2007
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description:
" Tests for lh-vim-lib . lh#menu#def_toggle_item()
"
"------------------------------------------------------------------------
" Installation: «install details»
" History: «history»
" TODO: «missing features»
" }}}1
"=============================================================================
source autoload/lh/menu.vim
let Data = {
\ "variable": "bar",
\ "idx_crt_value": 1,
\ "values": [ 'a', 'b', 'c', 'd' ],
\ "menu": { "priority": '42.50.10', "name": '&LH-Tests.&TogMenu.&bar'}
\}
call lh#menu#def_toggle_item(Data)
let Data2 = {
\ "variable": "foo",
\ "idx_crt_value": 3,
\ "texts": [ 'un', 'deux', 'trois', 'quatre' ],
\ "values": [ 1, 2, 3, 4 ],
\ "menu": { "priority": '42.50.11', "name": '&LH-Tests.&TogMenu.&foo'}
\}
call lh#menu#def_toggle_item(Data2)
" No default
let Data3 = {
\ "variable": "nodef",
\ "texts": [ 'one', 'two', 'three', 'four' ],
\ "values": [ 1, 2, 3, 4 ],
\ "menu": { "priority": '42.50.12', "name": '&LH-Tests.&TogMenu.&nodef'}
\}
call lh#menu#def_toggle_item(Data3)
" No default
let g:def = 2
let Data4 = {
\ "variable": "def",
\ "values": [ 1, 2, 3, 4 ],
\ "menu": { "priority": '42.50.13', "name": '&LH-Tests.&TogMenu.&def'}
\}
call lh#menu#def_toggle_item(Data4)
" What follows does not work because we can't build an exportable FuncRef on top
" of a script local function
" finish
function! s:getSNR()
if !exists("s:SNR")
let s:SNR=matchstr(expand("<sfile>"), "<SNR>\\d\\+_\\zegetSNR$")
endif
return s:SNR
endfunction
function! s:Yes()
echomsg "Yes"
endfunction
function! s:No()
echomsg "No"
endfunction
let Data4 = {
\ "variable": "yesno",
\ "values": [ 1, 2 ],
\ "text": [ "No", "Yes" ],
\ "actions": [ function(s:getSNR()."No"), function(s:getSNR()."Yes") ],
\ "menu": { "priority": '42.50.20', "name": '&LH-Tests.&TogMenu.&yesno'}
\}
call lh#menu#def_toggle_item(Data4)

View File

@ -0,0 +1,120 @@
"=============================================================================
" $Id: topological-sort.vim 246 2010-09-19 22:40:58Z luc.hermitte $
" File: tests/lh/topological-sort.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 2.2.1
" Created: 17th Apr 2008
" Last Update: $Date: 2010-09-19 18:40:58 -0400 (Sun, 19 Sep 2010) $
"------------------------------------------------------------------------
" Description: «description»
"
"------------------------------------------------------------------------
" Installation: «install details»
" History: «history»
" TODO: «missing features»
" }}}1
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
UTSuite [lh-vim-lib] topological sort
" Fully defineds DAGs {{{1
" A Direct Acyclic Graph {{{2
let s:dag1 = {}
let s:dag1[7] = [11, 8]
let s:dag1[5] = [11]
let s:dag1[3] = [8, 10]
let s:dag1[11] = [2, 9, 10]
let s:dag1[8] = [9]
" A Direct Cyclic Graph {{{2
let s:dcg1 = deepcopy(s:dag1)
let s:dcg1[9] = [11]
" Check routine: are the elements correctly sorted? {{{2
function! s:DoTestOrder(elements)
Assert! len(a:elements) == 8
Assert index(a:elements, 7) < index(a:elements, 11)
Assert index(a:elements, 7) < index(a:elements, 8)
Assert index(a:elements, 5) < index(a:elements, 11)
Assert index(a:elements, 3) < index(a:elements, 8)
Assert index(a:elements, 3) < index(a:elements, 10)
Assert index(a:elements, 11) < index(a:elements, 2)
Assert index(a:elements, 11) < index(a:elements, 9)
Assert index(a:elements, 11) < index(a:elements, 10)
Assert index(a:elements, 8) < index(a:elements, 9)
endfunction
" Test DAG1 {{{2
function! s:TestDAG_depth()
let res = lh#graph#tsort#depth(s:dag1, [3, 5,7])
call s:DoTestOrder(res)
echo "D(s:dag1)=".string(res)
endfunction
function! s:TestDAG_breadth()
let res = lh#graph#tsort#breadth(s:dag1, [3, 5,7])
call s:DoTestOrder(res)
echo "B(s:dag1)=".string(res)
endfunction
" Test DCG1 {{{2
function! s:TestDCG_depth()
let expr = 'lh#graph#tsort#depth('.string(s:dcg1).', [3, 5,7])'
Assert should#throw(expr, 'Tsort: cyclic graph detected')
endfunction
function! s:TestDCG_breadth()
let expr = 'lh#graph#tsort#breadth('.string(s:dcg1).', [3, 5,7])'
Assert should#throw(expr, 'Tsort: cyclic graph detected')
endfunction
" Lazzy Evaluated DAGs {{{1
" Emulated lazzyness {{{2
" The time-consumings evaluation function
let s:called = 0
function! Fetch(node)
let s:called += 1
return has_key(s:dag1, a:node) ? (s:dag1[a:node]) : []
endfunction
" Test Fetch on a DAG {{{2
function! s:TestDAG_fetch()
let s:called = 0
let res = lh#graph#tsort#depth(function('Fetch'), [3,5,7])
call s:DoTestOrder(res)
echo "D(fetch)=".string(res)
echo "Fetch has been evaluated ".s:called." times / ".len(res)
Assert s:called == len(res)
endfunction
" Setup/Teardown functions {{{1
" display the test name before each assertion
function! s:Setup()
if exists('g:UT_print_test')
let s:old_print_test = g:UT_print_test
endif
let g:UT_print_test = 1
endfunction
function! s:Teardown()
if exists('s:old_print_test')
let g:UT_print_test = s:old_print_test
unlet s:old_print_test
else
unlet g:UT_print_test
endif
endfunction
" }}}1
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,16 @@
--------------------
UT v0.0.3
--------------------
UT is a Unit Testing Framework for Vim
Audience : Vim script maintainers
Requirements : Vim 7, lh-vim-lib v2.2.0 ;
takes advantage of BuildtoolsWrappers if installed.
Required by : Only lh-vim
Maintainer : Luc Hermitte
License : GPL 2 (see http://www.gnu.org/licenses/gpl.txt)
More Help At : http://code.google.com/p/lh-vim/wiki/UT
Vim script#???: http://www.vim.org/scripts/script.php?script_id=???
Repository :
svn checkout http://lh-vim.googlecode.com/svn/UT/trunk UT

View File

@ -0,0 +1,442 @@
"=============================================================================
" $Id: UT.vim 193 2010-05-17 23:10:03Z luc.hermitte $
" File: autoload/lh/UT.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
" Version: 0.0.3
" Created: 11th Feb 2009
" Last Update: $Date: 2010-05-17 19:10:03 -0400 (Mon, 17 May 2010) $
"------------------------------------------------------------------------
" Description: Yet Another Unit Testing Framework for Vim
"
"------------------------------------------------------------------------
" Installation:
" Drop this file into {rtp}/autoload/lh/
" History:
" Strongly inspired by Tom Link's tAssert plugin: all its functions are
" compatible with this framework.
"
" Features:
" - Assertion failures are reported in the quickfix window
" - Assertion syntax is simple, check Tom Link's suite, it's the same
" - Supports banged :Assert! to stop processing a given test on failed
" assertions
" - All the s:Test* functions of a suite are executed (almost) independently
" (i.e., a critical :Assert! failure will stop the Test of the function, and
" lh#UT will proceed to the next s:Test function
" - Lightweight and simple to use: there is only one command defined, all the
" other definitions are kept in an autoload plugin.
" - A suite == a file
" - Several s:TestXxx() per suite
" - +optional s:Setup(), s:Teardown()
" - Supports :Comment's ; :Comment takes an expression to evaluate
" - s:LocalFunctions(), s:variables, and l:variables are supported
" - Takes advantage of BuildToolsWrapper's :Copen command if installed
" - Count successful tests (and not successful assertions)
" - Short-cuts to run the Unit Tests associated to a given vim script
" Relies on: Let-Modeline/local_vimrc/Project to set g:UTfiles (space
" separated list of glob-able paths), and on lh-vim-lib#path
" - Command to exclude, or specify the tests to play => UTPlay, UTIgnore
" - Option g:UT_print_test to display, on assertion failure, the current test
" name with the assertion failed.
"
" TODO:
" - Always execute s:Teardown() -- move its call to a :finally bloc
" - Test in UTF-8 (because of <SNR>_ injection)
" - test under windows (where paths have spaces, etc)
" - What about s:/SNR pollution ? The tmpfile is reused, and there is no
" guaranty a script will clean its own place
" - add &efm for viml errors like the one produced by :Assert 0 + [0]
" and take into account the offset introduced by lines injected at the top of
" the file
" - simplify s:errors functions
" - merge with Tom Link tAssert plugin? (the UI is quite different)
" - :AssertEquals that shows the name of both expressions and their values as
" well -- a correct distinction of both parameters will be tricky with
" regexes ; using functions will loose either the name, or the value in case
" of local/script variables use ; we need macros /à la C/...
" - Support Embedded comments like for instance:
" Assert 1 == 1 " 1 must value 1
" - Ways to test buffers produced
" }}}1
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Functions {{{1
"------------------------------------------------------------------------
" # Debug {{{2
function! lh#UT#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr, ...)
let lvl = a:0>0 ? a:1 : 1
if exists('s:verbose') && s:verbose >= lvl
echomsg a:expr
endif
endfunction
function! lh#UT#debug(expr)
return eval(a:expr)
endfunction
"------------------------------------------------------------------------
" # Internal functions {{{2
"------------------------------------------------------------------------
" Sourcing a script doesn't imply a new entry with its name in :scriptnames
" As a consequence, the easiest thing to do is to reuse the same file over and
" over in a given vim session.
" This approach should be fine as long as there are less than 26 VimL testing vim
" sessions opened simultaneously.
let s:tempfile = tempname()
"------------------------------------------------------------------------
" s:errors
let s:errors = {
\ 'qf' : [],
\ 'crt_suite' : {},
\ 'nb_asserts' : 0,
\ 'nb_successful_asserts' : 0,
\ 'nb_success' : 0,
\ 'suites' : []
\ }
function! s:errors.clear() dict
let self.qf = []
let self.nb_asserts = 0
let self.nb_successful_asserts = 0
let self.nb_success = 0
let self.nb_tests = 0
let self.suites = []
let self.crt_suite = {}
endfunction
function! s:errors.display() dict
let g:errors = self.qf
cexpr self.qf
" Open the quickfix window
if exists(':Copen')
" Defined in lh-BTW, make the windows as big as the number of errors, not
" opened if there is no error
Copen
else
copen
endif
endfunction
function! s:errors.set_current_SNR(SNR)
let self.crt_suite.snr = a:SNR
endfunction
function! s:errors.get_current_SNR()
return self.crt_suite.snr
endfunction
function! s:errors.add(FILE, LINE, message) dict
let msg = a:FILE.':'.a:LINE.':'
if lh#option#get('UT_print_test', 0, 'g') && has_key(s:errors, 'crt_test')
let msg .= '['. s:errors.crt_test.name .'] '
endif
let msg.= a:message
call add(self.qf, msg)
endfunction
function! s:errors.add_test(test_name) dict
call self.add_test(a:test_name)
endfunction
function! s:errors.set_test_failed() dict
if has_key(self, 'crt_test')
let self.crt_test.failed = 1
endif
endfunction
"------------------------------------------------------------------------
" Tests wrapper functions
function! s:RunOneTest(file) dict
try
let s:errors.crt_test = self
if has_key(s:errors.crt_suite, 'setup')
let F = function(s:errors.get_current_SNR().'Setup')
call F()
endif
let F = function(s:errors.get_current_SNR(). self.name)
call F()
if has_key(s:errors.crt_suite, 'teardown')
let F = function(s:errors.get_current_SNR().'Teardown')
call F()
endif
catch /Assert: abort/
call s:errors.add(a:file,
\ matchstr(v:exception, '.*(\zs\d\+\ze)'),
\ 'Test <'. self.name .'> execution aborted on critical assertion failure')
catch /.*/
let throwpoint = substitute(v:throwpoint, escape(s:tempfile, '.\'), a:file, 'g')
let msg = throwpoint . ': '.v:exception
call s:errors.add(a:file, 0, msg)
finally
unlet s:errors.crt_test
endtry
endfunction
function! s:AddTest(test_name) dict
let test = {
\ 'name' : a:test_name,
\ 'run' : function('s:RunOneTest'),
\ 'failed' : 0
\ }
call add(self.tests, test)
endfunction
"------------------------------------------------------------------------
" Suites wrapper functions
function! s:ConcludeSuite() dict
call s:errors.add(self.file,0, 'SUITE<'. self.name.'> '. s:errors.nb_success .'/'. s:errors.nb_tests . ' tests successfully executed.')
" call add(s:errors.qf, 'SUITE<'. self.name.'> '. s:rrors.nb_success .'/'. s:errors.nb_tests . ' tests successfully executed.')
endfunction
function! s:PlayTests(...) dict
call s:Verbose('Execute tests: '.join(a:000, ', '))
call filter(self.tests, 'index(a:000, v:val.name) >= 0')
call s:Verbose('Keeping tests: '.join(self.tests, ', '))
endfunction
function! s:IgnoreTests(...) dict
call s:Verbose('Ignoring tests: '.join(a:000, ', '))
call filter(self.tests, 'index(a:000, v:val.name) < 0')
call s:Verbose('Keeping tests: '.join(self.tests, ', '))
endfunction
function! s:errors.new_suite(file) dict
let suite = {
\ 'scriptname' : s:tempfile,
\ 'file' : a:file,
\ 'tests' : [],
\ 'snr' : '',
\ 'add_test' : function('s:AddTest'),
\ 'conclude' : function('s:ConcludeSuite'),
\ 'play' : function('s:PlayTests'),
\ 'ignore' : function('s:IgnoreTests'),
\ 'nb_tests_failed' : 0
\ }
call add(self.suites, suite)
let self.crt_suite = suite
return suite
endfunction
function! s:errors.set_suite(suite_name) dict
let a = s:Decode(a:suite_name)
call s:Verbose('SUITE <- '. a.expr, 1)
call s:Verbose('SUITE NAME: '. a:suite_name, 2)
" call self.add(a.file, a.line, 'SUITE <'. a.expr .'>')
call self.add(a.file,0, 'SUITE <'. a.expr .'>')
let self.crt_suite.name = a.expr
" let self.crt_suite.file = a.file
endfunction
"------------------------------------------------------------------------
function! s:Decode(expression)
let filename = s:errors.crt_suite.file
let expr = a:expression
let line = matchstr(expr, '^\d\+')
" echo filename.':'.line
let expr = strpart(expr, strlen(line)+1)
let res = { 'file':filename, 'line':line, 'expr':expr}
call s:Verbose('decode:'. (res.file) .':'. (res.line) .':'. (res.expr), 2)
return res
endfunction
function! lh#UT#callback_decode(expression)
return s:Decode(a:expression)
endfunction
"------------------------------------------------------------------------
let s:k_commands = '\%(Assert\|UTSuite\|Comment\)'
let s:k_local_evaluate = [
\ 'command! -bang -nargs=1 Assert '.
\ 'let s:a = lh#UT#callback_decode(<q-args>) |'.
\ 'let s:ok = !empty(eval(s:a.expr)) |'.
\ 'exe "UTAssert<bang> ".s:ok." ".(<f-args>)|'
\]
let s:k_getSNR = [
\ 'function! s:getSNR()',
\ ' if !exists("s:SNR")',
\ ' let s:SNR=matchstr(expand("<sfile>"), "<SNR>\\d\\+_\\zegetSNR$")',
\ ' endif',
\ ' return s:SNR',
\ 'endfunction',
\ 'call lh#UT#callback_set_SNR(s:getSNR())',
\ ''
\ ]
function! s:PrepareFile(file)
if !filereadable(a:file)
call s:errors.add('-', 0, a:file . " can not be read")
return
endif
let file = escape(a:file, ' \')
let lines = readfile(a:file)
let need_to_know_SNR = 0
let suite = s:errors.new_suite(a:file)
let no = 0
let last_line = len(lines)
while no < last_line
if lines[no] =~ '^\s*'.s:k_commands.'\>'
let lines[no] = substitute(lines[no], '^\s*'.s:k_commands.'!\= \zs', (no+1).' ', '')
elseif lines[no] =~ '^\s*function!\=\s\+s:Test'
let test_name = matchstr(lines[no], '^\s*function!\=\s\+s:\zsTest\S\{-}\ze(')
call suite.add_test(test_name)
elseif lines[no] =~ '^\s*function!\=\s\+s:Teardown'
let suite.teardown = 1
elseif lines[no] =~ '^\s*function!\=\s\+s:Setup'
let suite.setup = 1
endif
if lines[no] =~ '^\s*function!\=\s\+s:'
let need_to_know_SNR = 1
endif
let no += 1
endwhile
" Inject s:getSNR() in the script if there is a s:Function in the Test script
if need_to_know_SNR
call extend(lines, s:k_getSNR, 0)
let last_line += len(s:k_getSNR)
endif
" Inject local evualation of expressions in the script
" => takes care of s:variables, s:Functions(), and l:variables
call extend(lines, s:k_local_evaluate, 0)
call writefile(lines, suite.scriptname)
let g:lines=lines
endfunction
function! s:RunOneFile(file)
try
call s:PrepareFile(a:file)
exe 'source '.s:tempfile
let s:errors.nb_tests = len(s:errors.crt_suite.tests)
if !empty(s:errors.crt_suite.tests)
call s:Verbose('Executing tests: '.join(s:errors.crt_suite.tests, ', '))
for test in s:errors.crt_suite.tests
call test.run(a:file)
let s:errors.nb_success += 1 - test.failed
endfor
endif
catch /Assert: abort/
call s:errors.add(a:file,
\ matchstr(v:exception, '.*(\zs\d\+\ze)'),
\ 'Suite <'. s:errors.crt_suite .'> execution aborted on critical assertion failure')
catch /.*/
let throwpoint = substitute(v:throwpoint, escape(s:tempfile, '.\'), a:file, 'g')
let msg = throwpoint . ': '.v:exception
call s:errors.add(a:file, 0, msg)
finally
call s:errors.crt_suite.conclude()
" Never! the name must not be used by other Vim sessions
" call delete(s:tempfile)
endtry
endfunction
"------------------------------------------------------------------------
function! s:StripResultAndDecode(expr)
" Function needed because of an odd degenerescence of vim: commands
" eventually loose their '\'
return s:Decode(matchstr(a:expr, '^\d\+\s\+\zs.*'))
endfunction
function! s:GetResult(expr)
" Function needed because of an odd degenerescence of vim: commands
" eventually loose their '\'
return matchstr(a:expr, '^\d\+\ze\s\+.*')
endfunction
function! s:DefineCommands()
" NB: variables are already interpreted, make it a function
" command! -nargs=1 Assert call s:Assert(<q-args>)
command! -bang -nargs=1 UTAssert
\ let s:a = s:StripResultAndDecode(<q-args>) |
\ let s:ok = s:GetResult(<q-args>) |
\ let s:errors.nb_asserts += 1 |
\ if ! s:ok |
\ call s:errors.set_test_failed() |
\ call s:errors.add(s:a.file, s:a.line, 'assertion failed: '.s:a.expr) |
\ if '<bang>' == '!' |
\ throw "Assert: abort (".s:a.line.")" |
\ endif |
\ else |
\ let s:errors.nb_successful_asserts += 1 |
\ endif
command! -nargs=1 Comment
\ let s:a = s:Decode(<q-args>) |
\ call s:errors.add(s:a.file, s:a.line, eval(s:a.expr))
command! -nargs=1 UTSuite call s:errors.set_suite(<q-args>)
command! -nargs=+ UTPlay call s:errors.crt_suite.play(<f-args>)
command! -nargs=+ UTIgnore call s:errors.crt_suite.ignore(<f-args>)
endfunction
function! s:UnDefineCommands()
silent! delcommand Assert
silent! delcommand UTAssert
silent! command! -nargs=* UTSuite :echoerr "Use :UTRun and not :source on this script"<bar>finish
silent! delcommand UTPlay
silent! delcommand UTIgnore
endfunction
"------------------------------------------------------------------------
" # callbacks {{{2
function! lh#UT#callback_set_SNR(SNR)
call s:errors.set_current_SNR(a:SNR)
endfunction
" # Main function {{{2
function! lh#UT#run(bang,...)
" 1- clear the errors table
let must_keep = a:bang == "!"
if ! must_keep
call s:errors.clear()
endif
try
" 2- define commands
call s:DefineCommands()
" 3- run every test
let rtp = '.,'.&rtp
let files = []
for file in a:000
let lFile = lh#path#glob_as_list(rtp, file)
if len(lFile) > 0
call add(files, lFile[0])
endif
endfor
for file in files
call s:RunOneFile(file)
endfor
finally
call s:UnDefineCommands()
call s:errors.display()
endtry
" 3- Open the quickfix
endfunction
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:
" VIM: let g:UTfiles='tests/lh/UT*.vim'

View File

@ -0,0 +1,68 @@
"=============================================================================
" $Id: be.vim 193 2010-05-17 23:10:03Z luc.hermitte $
" File: autoload/should/be.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://hermitte.free.fr/vim/>
" Version: 0.0.3
" Created: 23rd Feb 2009
" Last Update: $Date: 2010-05-17 19:10:03 -0400 (Mon, 17 May 2010) $
"------------------------------------------------------------------------
" Description:
" UT & tAssert API
"
"------------------------------------------------------------------------
" Installation:
" Drop this file into {rtp}/autoload/should
" History:
"
" TODO: «missing features»
" }}}1
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
" ## Functions {{{1
" # Debug {{{2
function! should#be#verbose(level)
let s:verbose = a:level
endfunction
function! s:Verbose(expr)
if exists('s:verbose') && s:verbose
echomsg a:expr
endif
endfunction
function! should#be#debug(expr)
return eval(a:expr)
endfunction
" # Convinience functions for tAssert/UT {{{2
function! should#be#list(var)
return type(a:var) == type([])
endfunction
function! should#be#number(var)
return type(a:var) == type(42)
endfunction
function! should#be#string(var)
return type(a:var) == type('')
endfunction
function! should#be#dict(var)
return type(a:var) == type({})
endfunction
function! should#be#float(var)
return type(a:var) == type(0.1)
endfunction
function! should#be#funcref(var)
return type(a:var) == type(function('exists'))
endfunction
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,165 @@
*UT.txt* Unit Testing Framework for Vim (v0.0.3)
For Vim version 7+. Last change: $Date: 2010-05-17 19:10:03 -0400 (Mon, 17 May 2010) $
By Luc Hermitte
hermitte {at} free {dot} fr
------------------------------------------------------------------------------
CONTENTS *UT-contents* {{{1
|UT-presentation| Presentation
|UT-usage| Usage
|UT-API| UT API
|UT-examples| Examples
|UT-todo| Bugs to fix and futur enhancements to come
|UT-design| Design choices
|UT-others| Other tests related plugins for vim
|add-local-help| Instructions on installing this file
------------------------------------------------------------------------------
PRESENTATION *UT-presentation* {{{1
UT is another Test Unit Framework for Vim, which main particularity is to fill
the |quickfix| window with the assertion failures.
Features~
- Assertion failures are reported in the |quickfix| window
- Assertion syntax is simple, check Tom Link's suite, it's the same
- Supports banged ":Assert!" to stop processing a given test on failed
assertions
- All the |s:Test()| functions of a suite are executed (almost)
independently (i.e., a critical ":Assert!" failure will stop the Test of
the function, and |lh#UT| will proceed to the next |s:Test()| function
- Lightweight and simple to use: there is only one command defined, all the
other definitions are kept in an autoload plugin.
- A suite == a file
- Several |s:Test()| functions per suite
- +optional |s:Setup()|, |s:Teardown()|
- Supports |:Comments|
- |local-function|s, |script-variable|s, and |local-variable|s are supported
- Takes advantage of |BuildToolsWrapper|'s |:Copen| command if installed
- Counts successful tests and not successful assertions
- Short-cuts to run the Unit Tests associated to a given vim script; Relies
on: |Let-Modeline|, |local_vimrc|/|project.vim| to set |g:UTfiles| (space
separated list of glob-able paths), and on |lhvl#path|.
- Command to exclude, or specify the tests to play => |:UTPlay|, |UTIgnore|
Requirements~
This suite requires Vim 7.1 and |lh-vim-lib| v2.2.0+.
------------------------------------------------------------------------------
USAGE *UT-usage* {{{1
First, create a new vim script, it will be a Unit Testing Suite.
*:UTSuite*
One of the first lines must contain >
UTSuite Some intelligible name for the suite
<
*:Assert*
Then you are free to directly assert anything you wish as long as it is a
valid vim |expression|, e.g. >
Assert 1 > 2
Assert 1 > 0
Assert s:foo > s:Bar(g:var + 28) / strlen("foobar")
or to define as many independent tests as you wish.
*:Comment*
Comments may be added to the |quickfix| report thanks to the |:Comment|
fake command.
*s:Test()*
A test is a function with a name starting with |s:Test|. Even if a test
critically fails, the next test will be executed, e.g. >
function s:Test1()
let var = SomeFucntion()
Assert! type(var) == type(0)
Assert var < 42
Assert! var > 0
" Some other code that won't be executed if the previous assertion failed
let i = var / 42.0
Comment This comment may never be displayed if {var} is negative or not a number
endfunction
function s:Test2()
Assert s:what != Ever()
endfunction
<
*s:Setup()* *s:Teardown()*
If you wish to see a set-up function executed before each test, define the
|s:Setup()| function.
If you wish to see a clean-up function executed after each test, define the
|s:Teardown()| function.
*:UTRun*
Finally run |:UTRun| on your test script (filename), and ... debug your failed
assertions from the |quickfix| window.
------------------------------------------------------------------------------
UT API *UT-API* {{{1
*should#be#dict()* returns whether the parameter is a |Dictionary|
*should#be#float()* returns whether the parameter is a |float|
*should#be#funcref()* returns whether the parameter is a |Funcref|
*should#be#list()* returns whether the parameter is a |List|
*should#be#number()* returns whether the parameter is a |expr-number|
*should#be#string()* returns whether the parameter is a |expr-string|
------------------------------------------------------------------------------
EXAMPLES *UT-examples* {{{1
See:
- {rtp}/tests/lh/UT.vim tests/lh/UT.vim for a classical test,
- {rtp}/tests/lh/UT-fixtures.vim tests/lh/UT-fixtures.vim for a test with
fixtures.
------------------------------------------------------------------------------
TO DO *UT-todo* {{{1
- Add |'efm'| for VimL errors like the one produced by >
:Assert 0 + [0]
- Check UT works fine under windows (where paths have spaces, etc), and on
UTF-8 files
- Simplify "s:errors" functions
- Merge with Tom Link's tAssert plugin? (the UI is quite different)
- |:AssertEquals| that shows the name of both expressions and their values as
well -- a correct distinction of both parameters will be tricky with regexes
; using functions will loose either the name, or the value in case of
local/script variables use ; we need macros /à la C/...
- Support Embedded comments like for instance: >
Assert 1 == 1 " 1 must value 1
- Ways to test buffers produced
- Always execute |s:Teardown()| -- move its call to a |:finally| bloc
- Find a way to prevent the potential script scope pollution
------------------------------------------------------------------------------
DESIGN CHOICES *UT-design* {{{1
The assertions supported by this plugin are expected to be made in a Unit
Testing file, they are not to be used in regular VimL scripts as a /Design by
Contract/ tool. Check Thomas Link's plugin, it is much more suited for that
kind of assertions.
In order to be able to produce the |quickfix| entries, the plugin first parses
the Unit Test file to complete all |:Assert| occurrences with extra
information about the line number where the assertion is made.
------------------------------------------------------------------------------
OTHER TESTS RELATED PLUGINS FOR VIM *UT-others* {{{1
You may also want to have a look at:
- Tom Link's |tAssert| plugin
http://www.vim.org/scripts/script.php?script_id=1730
- Staale Flock's |vimUnit| plugin
http://www.vim.org/scripts/script.php?script_id=1125
- Meikel Brandmeyer's |vimTAP| plugin
http://www.vim.org/scripts/script.php?script_id=2213
------------------------------------------------------------------------------
© Luc Hermitte, 2010, http://code.google.com/p/lh-vim/
$Id: UT.txt 193 2010-05-17 23:10:03Z luc.hermitte $
VIM: let b:VS_language = 'american'
vim:ts=8:sw=4:tw=80:fo=tcq2:isk=!-~,^*,^\|,^\":ft=help:fdm=marker:

View File

@ -0,0 +1,67 @@
"=============================================================================
" $Id: vim_UT.vim 193 2010-05-17 23:10:03Z luc.hermitte $
" File: ftplugin/vim/vim_UT.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
let s:k_version = 003
" Version: 0.0.3
" Created: 20th Feb 2009
" Last Update: $Date: 2010-05-17 19:10:03 -0400 (Mon, 17 May 2010) $
"------------------------------------------------------------------------
" Description: Yet Another Unit Testing Framework for Vim
" - Defines <F7> as :UTRun {targets=g:UTfiles}
"
"------------------------------------------------------------------------
" Installation: «install details»
" History: «history»
" TODO: «missing features»
" }}}1
"=============================================================================
" Buffer-local Definitions {{{1
" Avoid local reinclusion {{{2
if &cp || (exists("b:loaded_ftplug_vim_UT") && !exists('g:force_reload_ftplug_vim_UT'))
finish
endif
let b:loaded_ftplug_vim_UT = s:k_version
let s:cpo_save=&cpo
set cpo&vim
" Avoid local reinclusion }}}2
"------------------------------------------------------------------------
" Local mappings {{{2
nnoremap <buffer> <silent> <Plug>UTMake :call <sid>UTMake()<cr>
let s:key = lh#option#get('UTMake_key', '<F7>')
exe 'imap <buffer> '.s:key.' <c-\><c-n><Plug>UTMake'
exe 'vmap <buffer> '.s:key.' <c-\><c-n><Plug>UTMake'
exe 'nmap <buffer> '.s:key.' <Plug>UTMake'
"=============================================================================
" Global Definitions {{{1
" Avoid global reinclusion {{{2
if &cp || (exists("g:loaded_ftplug_vim_UT") && !exists('g:force_reload_ftplug_vim_UT'))
let &cpo=s:cpo_save
finish
endif
let g:loaded_ftplug_vim_UT = s:k_version
" Avoid global reinclusion }}}2
"------------------------------------------------------------------------
" Functions {{{2
function! s:UTMake()
let files = lh#option#get('UTfiles', '%')
echo 'update|source '.expand('%').'|UTRun '.files
update
so%
exe 'UTRun '.files
endfunction
" Functions }}}2
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:
" VIM: let g:UTfiles='tests/lh/UT*.vim'

View File

@ -0,0 +1,32 @@
"=============================================================================
" $Id: mk-UT.vim 194 2010-05-17 23:26:14Z luc.hermitte $
" File: mk-UT.vim
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://hermitte.free.fr/vim/>
" Version: 0.0.3
let s:version = '0.0.3'
" Created: 19th Feb 2009
" Last Update: $Date: 2010-05-17 19:26:14 -0400 (Mon, 17 May 2010) $
"------------------------------------------------------------------------
cd <sfile>:p:h
try
let save_rtp = &rtp
let &rtp = expand('<sfile>:p:h:h').','.&rtp
exe '22,$MkVimball! UT-'.s:version
set modifiable
set buftype=
finally
let &rtp = save_rtp
endtry
finish
UT.README
autoload/lh/UT.vim
autoload/should.vim
autoload/should/be.vim
doc/UT.txt
ftplugin/vim/vim_UT.vim
mkVba/mk-UT.vim
plugin/UT.vim
tests/lh/UT-fixtures.vim
tests/lh/UT.vim
tests/lh/assert.vim

View File

@ -0,0 +1,48 @@
"=============================================================================
" $Id: UT.vim 193 2010-05-17 23:10:03Z luc.hermitte $
" File: plugin/UT.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://code.google.com/p/lh-vim/>
let s:k_version = 003
" Version: 0.0.3
" Created: 11th Feb 2009
" Last Update: $Date: 2010-05-17 19:10:03 -0400 (Mon, 17 May 2010) $
"------------------------------------------------------------------------
" Description: Yet Another Unit Testing Framework for Vim
"
"------------------------------------------------------------------------
" Installation:
" Drop the file into {rtp}/plugin/lh/
" History:
" Strongly inspired by Tom Link's tAssert
" TODO: «missing features»
" }}}1
"=============================================================================
" Avoid global reinclusion {{{1
if &cp || (exists("g:loaded_UT") && !exists('g:force_reload_UT'))
finish
endif
let g:loaded_UT = s:k_version
let s:cpo_save=&cpo
set cpo&vim
" Avoid global reinclusion }}}1
"------------------------------------------------------------------------
" Commands and Mappings {{{1
" Real commands (used to call UT files)
"command! UTRun {filenames}
command! -bang -nargs=+ -complete=file UTRun :call lh#UT#run("<bang>",<f-args>)
" Fake commands (used in UT files)
"command UTSuite {expression} [#{comments}]
command! -nargs=* UTSuite :echoerr "Use :UTRun and not :source on this script"<bar>finish
"command Assert {expression} [#{comments}]
" Commands and Mappings }}}1
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:
" VIM: let g:UTfiles='tests/lh/UT*.vim'

View File

@ -0,0 +1,58 @@
"=============================================================================
" $Id: UT-fixtures.vim 193 2010-05-17 23:10:03Z luc.hermitte $
" File: tests/lh/UT-fixtures.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://hermitte.free.fr/vim/>
" Version: 0.0.1
" Created: 11th Feb 2009
" Last Update: $Date: 2010-05-17 19:10:03 -0400 (Mon, 17 May 2010) $
"------------------------------------------------------------------------
" Description: UnitTests for the UT plugin.
" - Test fixtures
"
"------------------------------------------------------------------------
" Installation: «install details»
" History: «history»
" TODO: «missing features»
" }}}1
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
UTSuite [lh#UT] Testing fixtures
let s:v1 = 0
let s:v2 = 0
function! s:Setup()
Assert! exists('s:v1')
Assert! exists('s:v2')
let s:v1 += 1
let s:v2 += 1
endfunction
function! s:Teardown()
let s:v1 = 0
endfunction
function! s:TestSetup()
Comment "First test weither s:v1 and g:v2 are set to 1"
" Assert0 s:v1 == 1
Assert s:v1 == 1
Assert s:v2 == 1
endfunction
function! s:TestTeardown()
Comment "Second test weither only s:v1 is incremented, while g:v2 is set to 1"
Assert s:v1 == 1
Assert s:v2 == 2
endfunction
" UTPlay TestTeardown
UTIgnore TestTeardown
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,73 @@
"=============================================================================
" $Id: UT.vim 193 2010-05-17 23:10:03Z luc.hermitte $
" File: tests/lh/UT.vim {{{1
" Author: Luc Hermitte <EMAIL:hermitte {at} free {dot} fr>
" <URL:http://hermitte.free.fr/vim/>
" Version: 0.0.1
" Created: 11th Feb 2009
" Last Update: $Date: 2010-05-17 19:10:03 -0400 (Mon, 17 May 2010) $
"------------------------------------------------------------------------
" Description: UnitTests for the UT plugin.
" - Tests global assertions
" - Tests assertions definied in tests (functions s:Test)
"
"------------------------------------------------------------------------
" Installation: «install details»
" History: «history»
" TODO: «missing features»
" }}}1
"=============================================================================
let s:cpo_save=&cpo
set cpo&vim
"------------------------------------------------------------------------
UTSuite [lh#UT] Testing global and local assertions
Assert 1 == 1
Assert 1 != 42
Assert 1 < 20
Assert 1 > 20
let st = "string"
Assert st =~ 'str'
Assert st !~ 'str'
Assert st == 'str'
Assert st != 'str'
Assert st == 0
" Assert 0 + [0]
function! s:One()
return 1
endfunction
Assert s:One() == 1
"------------------------------------------------------------------------
function! s:TestOK()
Comment "TestOK"
Assert! 1 == 1
Assert 1 == 1
Assert repeat('1', 5) == '11111'
Assert! repeat('1', 5) == '11111'
endfunction
"------------------------------------------------------------------------
function! s:TestCriticalNOK()
Comment "TestCriticalNOK"
Assert! 1 == 0
Assert repeat('1', 5) == '1111'
endfunction
"------------------------------------------------------------------------
function! s:TestNOK()
Comment "TestNOK"
Assert 0 == 1
Assert repeat('1', 5) == '1111'
endfunction
function! s:Foo()
endfunction
"------------------------------------------------------------------------
"------------------------------------------------------------------------
let &cpo=s:cpo_save
"=============================================================================
" vim600: set fdm=marker:

View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
set -e
if [[ $# -eq 0 ]]
then
TESTS="`ls *.vim | tr "\n" ' '`"
else
IFS=' '
TESTS="$*"
fi
vim -u vimrc_test -c ":UTRun $TESTS"

View File

@ -0,0 +1,64 @@
UTSuite [Mundo] Testing Toggling
function! s:Setup()"{{{
exec 'edit test'
call g:Goto('test')
setlocal buftype=nofile
endfunction"}}}
function! s:Teardown()"{{{
if bufwinnr(bufnr('__Mundo__')) != -1
exec bufwinnr(bufnr('__Mundo__')) . 'wincmd w'
quit
endif
if bufwinnr(bufnr('__Mundo_Preview__')) != -1
exec bufwinnr(bufnr('__Mundo_Preview__')) . 'wincmd w'
quit
endif
if bufnr('__Mundo__') != -1
exec 'bwipeout ' . bufnr('__Mundo__')
endif
if bufnr('__Mundo_Preview__') != -1
exec 'bwipeout ' . bufnr('__Mundo_Preview__')
endif
if bufnr('test') != -1
exec 'bwipeout ' . bufnr('test')
endif
if bufnr('test2') != -1
exec 'bwipeout ' . bufnr('test2')
endif
endfunction"}}}
function! s:TestToggleBasic()"{{{
call g:TypeLine("ONE")
call g:TypeLineDone("TWO")
MundoToggle
Assert g:Contains("o [0]")
Assert g:Contains("o [1]")
Assert g:Contains("@ [2]")
Assert !g:Contains("[3]")
endfunction"}}}
function! s:TestToggleBranches()"{{{
call g:TypeLineDone("ONE")
silent! undo
call g:TypeLineDone("TWO")
MundoToggle
" Make sure there is a branch next to state 2
call g:GotoLineContaining("[1]")
Assert g:CurrentLineContains("|")
" Make sure there is no branch next to states 0 and 2
call g:GotoLineContaining("[0]")
Assert !g:CurrentLineContains("|")
call g:GotoLineContaining("[2]")
Assert !g:CurrentLineContains("|")
" Make sure the branch point is directly above state 0
call g:GotoLineContaining("[0]")
call g:MoveUp()
Assert g:CurrentLineContains("|/")
endfunction"}}}

View File

@ -0,0 +1,150 @@
UTSuite [Mundo] Testing Movement
function! s:Setup()"{{{
exec 'edit test'
call g:Goto('test')
endfunction"}}}
function! s:Teardown()"{{{
if bufwinnr(bufnr('__Mundo__')) != -1
exec bufwinnr(bufnr('__Mundo__')) . 'wincmd w'
quit
endif
if bufwinnr(bufnr('__Mundo_Preview__')) != -1
exec bufwinnr(bufnr('__Mundo_Preview__')) . 'wincmd w'
quit
endif
if bufnr('__Mundo__') != -1
exec 'bwipeout! ' . bufnr('__Mundo__')
endif
if bufnr('__Mundo_Preview__') != -1
exec 'bwipeout! ' . bufnr('__Mundo_Preview__')
endif
if bufnr('test') != -1
exec 'bwipeout! ' . bufnr('test')
endif
if bufnr('test2') != -1
exec 'bwipeout! ' . bufnr('test2')
endif
endfunction"}}}
function! s:TestMovementBasic()"{{{
" Make three linear changes
call g:TypeLineDone("ONE")
call g:TypeLineDone("TWO")
call g:TypeLineDone("THREE")
" Open Mundo
MundoToggle
" Make sure we're on the newest/current state
Assert g:CurrentLineContains("[3]")
" Move down
normal j
Assert g:CurrentLineContains("[2]")
" Move down
normal j
Assert g:CurrentLineContains("[1]")
" Move down
normal j
Assert g:CurrentLineContains("[0]")
" Move up
normal k
Assert g:CurrentLineContains("[1]")
" Move up
normal k
Assert g:CurrentLineContains("[2]")
" Move up
normal k
Assert g:CurrentLineContains("[3]")
" Test arrow mappings
" Move down
exec "normal \<down>"
Assert g:CurrentLineContains("[2]")
" Move down
exec "normal \<down>"
Assert g:CurrentLineContains("[1]")
" Move down
exec "normal \<down>"
Assert g:CurrentLineContains("[0]")
" Move up
exec "normal \<up>"
Assert g:CurrentLineContains("[1]")
" Move up
exec "normal \<up>"
Assert g:CurrentLineContains("[2]")
" Move up
exec "normal \<up>"
Assert g:CurrentLineContains("[3]")
endfunction"}}}
function! s:TestMovementLinear()"{{{
" Make four changes:
"
" o [4]
" |
" | o [3]
" | |
" o | [2]
" |/
" o [1]
" |
" o [0] (original)
call g:TypeLineDone("ONE")
call g:TypeLineDone("TWO")
silent! undo
call g:TypeLineDone("THREE")
silent! undo 2
call g:TypeLineDone("FOUR")
" Open Mundo
MundoToggle
" Make sure we're on the newest/current state
Assert g:CurrentLineContains("[4]")
" Move down
normal j
Assert g:CurrentLineContains("[3]")
" Move down
normal j
Assert g:CurrentLineContains("[2]")
" Move down
normal j
Assert g:CurrentLineContains("[1]")
" Move down
normal j
Assert g:CurrentLineContains("[0]")
" Move up
normal k
Assert g:CurrentLineContains("[1]")
" Move up
normal k
Assert g:CurrentLineContains("[2]")
" Move up
normal k
Assert g:CurrentLineContains("[3]")
" Move up
normal k
Assert g:CurrentLineContains("[4]")
endfunction"}}}

View File

@ -0,0 +1,131 @@
UTSuite [Mundo] Testing Preview Pane
function! s:Setup()"{{{
exec 'edit test'
call g:Goto('test')
endfunction"}}}
function! s:Teardown()"{{{
if bufwinnr(bufnr('__Mundo__')) != -1
exec bufwinnr(bufnr('__Mundo__')) . 'wincmd w'
quit
endif
if bufwinnr(bufnr('__Mundo_Preview__')) != -1
exec bufwinnr(bufnr('__Mundo_Preview__')) . 'wincmd w'
quit
endif
if bufnr('__Mundo__') != -1
exec 'bwipeout! ' . bufnr('__Mundo__')
endif
if bufnr('__Mundo_Preview__') != -1
exec 'bwipeout! ' . bufnr('__Mundo_Preview__')
endif
if bufnr('test') != -1
exec 'bwipeout! ' . bufnr('test')
endif
if bufnr('test2') != -1
exec 'bwipeout! ' . bufnr('test2')
endif
endfunction"}}}
function! s:TestPreviewBasic()"{{{
" Make three linear changes, then a change that deletes the last line
call g:TypeLineDone("ONE")
call g:TypeLineDone("TWO")
call g:TypeLineDone("THREE")
normal k
normal dd
" Open Mundo
MundoToggle
call g:Goto("__Mundo_Preview__")
call g:GotoLineContaining("THREE")
Assert g:CurrentLineContains("-THREE")
call g:Goto("__Mundo__")
normal j
call g:Goto("__Mundo_Preview__")
call g:GotoLineContaining("THREE")
Assert g:CurrentLineContains("+THREE")
call g:Goto("__Mundo__")
normal j
call g:Goto("__Mundo_Preview__")
call g:GotoLineContaining("TWO")
Assert g:CurrentLineContains("+TWO")
call g:Goto("__Mundo__")
normal j
call g:Goto("__Mundo_Preview__")
call g:GotoLineContaining("ONE")
Assert g:CurrentLineContains("+ONE")
call g:Goto("__Mundo__")
normal k
call g:Goto("__Mundo_Preview__")
call g:GotoLineContaining("TWO")
Assert g:CurrentLineContains("+TWO")
call g:Goto("__Mundo__")
normal k
call g:Goto("__Mundo_Preview__")
call g:GotoLineContaining("THREE")
Assert g:CurrentLineContains("+THREE")
call g:Goto("__Mundo__")
normal k
call g:Goto("__Mundo_Preview__")
call g:GotoLineContaining("THREE")
Assert g:CurrentLineContains("-THREE")
endfunction"}}}
function! s:TestPreviewLinear()"{{{
" Make four non-linear changes
"
" o [4]
" |
" | o [3]
" | |
" o | [2]
" |/
" o [1]
call g:TypeLineDone("ONE")
call g:TypeLineDone("TWO")
silent! undo
call g:TypeLineDone("THREE")
silent! undo 2
call g:TypeLineDone("FOUR")
" Open Mundo
MundoToggle
" Check state 4
call g:Goto("__Mundo_Preview__")
call g:GotoLineContaining("FOUR")
Assert g:CurrentLineContains("+FOUR")
call g:GotoLineContaining("THREE")
Assert !g:CurrentLineContains("THREE")
" Check state 3
call g:Goto("__Mundo__")
normal j
call g:Goto("__Mundo_Preview__")
call g:GotoLineContaining("THREE")
Assert g:CurrentLineContains("+THREE")
call g:GotoLineContaining("FOUR")
Assert !g:CurrentLineContains("FOUR")
call g:GotoLineContaining("TWO")
Assert !g:CurrentLineContains("TWO")
call g:GotoLineContaining("ONE")
Assert g:CurrentLineContains("ONE")
" Check state 2
call g:Goto("__Mundo__")
normal j
call g:Goto("__Mundo_Preview__")
call g:GotoLineContaining("TWO")
Assert g:CurrentLineContains("+TWO")
call g:GotoLineContaining("ONE")
Assert g:CurrentLineContains("ONE")
endfunction"}}}

View File

@ -0,0 +1,123 @@
UTSuite [Mundo] Testing Toggling
function! s:Setup()"{{{
exec 'edit test'
call g:Goto('test')
endfunction"}}}
function! s:Teardown()"{{{
if bufwinnr(bufnr('__Mundo__')) != -1
exec bufwinnr(bufnr('__Mundo__')) . 'wincmd w'
quit
endif
if bufwinnr(bufnr('__Mundo_Preview__')) != -1
exec bufwinnr(bufnr('__Mundo_Preview__')) . 'wincmd w'
quit
endif
if bufnr('__Mundo__') != -1
exec 'bwipeout ' . bufnr('__Mundo__')
endif
if bufnr('__Mundo_Preview__') != -1
exec 'bwipeout ' . bufnr('__Mundo_Preview__')
endif
if bufnr('test') != -1
exec 'bwipeout ' . bufnr('test')
endif
if bufnr('test2') != -1
exec 'bwipeout ' . bufnr('test2')
endif
endfunction"}}}
function! s:TestToggleBasic()"{{{
" Make sure we're starting from scratch.
Assert bufnr('__Mundo__') == -1
Assert bufnr('__Mundo_Preview__') == -1
Assert bufwinnr(bufnr('__Mundo__')) == -1
Assert bufwinnr(bufnr('__Mundo_Preview__')) == -1
" Open Mundo
MundoToggle
" Buffers and windows should exist.
Assert bufnr('__Mundo__') != -1
Assert bufnr('__Mundo_Preview__') != -1
Assert bufwinnr(bufnr('__Mundo__')) != -1
Assert bufwinnr(bufnr('__Mundo_Preview__')) != -1
" We should be in the Mundo pane.
Assert expand('%') == '__Mundo__'
" Close Mundo
MundoToggle
" Windows should have been closed, but buffers should remain.
Assert bufnr('__Mundo__') != -1
Assert bufnr('__Mundo_Preview__') != -1
Assert bufwinnr(bufnr('__Mundo__')) == -1
Assert bufwinnr(bufnr('__Mundo_Preview__')) == -1
endfunction"}}}
function! s:TestToggleWhenMoved()"{{{
" Make sure we're starting from scratch.
Assert bufnr('__Mundo__') == -1
Assert bufnr('__Mundo_Preview__') == -1
Assert bufwinnr(bufnr('__Mundo__')) == -1
Assert bufwinnr(bufnr('__Mundo_Preview__')) == -1
" Open Mundo
MundoToggle
call g:Goto('test')
Assert expand('%') == 'test'
" Close Mundo
MundoToggle
" Windows should have been closed, but buffers should remain.
Assert bufnr('__Mundo__') != -1
Assert bufnr('__Mundo_Preview__') != -1
Assert bufwinnr(bufnr('__Mundo__')) == -1
Assert bufwinnr(bufnr('__Mundo_Preview__')) == -1
" Open Mundo
MundoToggle
call g:Goto('__Mundo_Preview__')
Assert expand('%') == '__Mundo_Preview__'
" Close Mundo
MundoToggle
" Windows should have been closed, but buffers should remain.
Assert bufnr('__Mundo__') != -1
Assert bufnr('__Mundo_Preview__') != -1
Assert bufwinnr(bufnr('__Mundo__')) == -1
Assert bufwinnr(bufnr('__Mundo_Preview__')) == -1
endfunction"}}}
function! s:TestToggleReturnToTarget()"{{{
" Make sure we're starting from scratch.
Assert bufnr('__Mundo__') == -1
Assert bufnr('__Mundo_Preview__') == -1
Assert bufwinnr(bufnr('__Mundo__')) == -1
Assert bufwinnr(bufnr('__Mundo_Preview__')) == -1
exec 'new test2'
call g:Goto('test')
" Toggle Mundo
MundoToggle
MundoToggle
" We should be returned to test
Assert expand('%') == 'test'
" Move to test2
call g:Goto('test2')
" Toggle Mundo
MundoToggle
MundoToggle
" We should be returned to test2
Assert expand('%') == 'test2'
endfunction"}}}

View File

@ -0,0 +1,108 @@
" Vim color file
" Maintainer: Hans Fugal <hans@fugal.net>
" Last Change: $Date: 2004/06/13 19:30:30 $
" Last Change: $Date: 2004/06/13 19:30:30 $
" URL: http://hans.fugal.net/vim/colors/desert.vim
" Version: $Id: desert.vim,v 1.1 2004/06/13 19:30:30 vimboss Exp $
" cool help screens
" :he group-name
" :he highlight-groups
" :he cterm-colors
set background=dark
if version > 580
" no guarantees for version 5.8 and below, but this makes it stop
" complaining
hi clear
if exists("syntax_on")
syntax reset
endif
endif
let g:colors_name="desert"
hi Normal guifg=White guibg=grey20
" highlight groups
hi Cursor guibg=khaki guifg=slategrey
"hi CursorIM
"hi Directory
"hi DiffAdd
"hi DiffChange
"hi DiffDelete
"hi DiffText
"hi ErrorMsg
hi VertSplit guibg=#c2bfa5 guifg=grey50 gui=none
hi Folded guibg=grey30 guifg=gold
hi FoldColumn guibg=grey30 guifg=tan
hi IncSearch guifg=slategrey guibg=khaki
"hi LineNr
hi ModeMsg guifg=goldenrod
hi MoreMsg guifg=SeaGreen
hi NonText guifg=LightBlue guibg=grey30
hi Question guifg=springgreen
hi Search guibg=peru guifg=wheat
hi SpecialKey guifg=yellowgreen
hi StatusLine guibg=#c2bfa5 guifg=black gui=none
hi StatusLineNC guibg=#c2bfa5 guifg=grey50 gui=none
hi Title guifg=indianred
hi Visual gui=none guifg=khaki guibg=olivedrab
"hi VisualNOS
hi WarningMsg guifg=salmon
"hi WildMenu
"hi Menu
"hi Scrollbar
"hi Tooltip
" syntax highlighting groups
hi Comment guifg=SkyBlue
hi Constant guifg=#ffa0a0
hi Identifier guifg=palegreen
hi Statement guifg=khaki
hi PreProc guifg=indianred
hi Type guifg=darkkhaki
hi Special guifg=navajowhite
"hi Underlined
hi Ignore guifg=grey40
"hi Error
hi Todo guifg=orangered guibg=yellow2
" color terminal definitions
hi SpecialKey ctermfg=darkgreen
hi NonText cterm=bold ctermfg=darkblue
hi Directory ctermfg=darkcyan
hi ErrorMsg cterm=bold ctermfg=7 ctermbg=1
hi IncSearch cterm=NONE ctermfg=yellow ctermbg=green
hi Search cterm=NONE ctermfg=grey ctermbg=blue
hi MoreMsg ctermfg=darkgreen
hi ModeMsg cterm=NONE ctermfg=brown
hi LineNr ctermfg=3
hi Question ctermfg=green
hi StatusLine cterm=bold,reverse
hi StatusLineNC cterm=reverse
hi VertSplit cterm=reverse
hi Title ctermfg=5
hi Visual cterm=reverse
hi VisualNOS cterm=bold,underline
hi WarningMsg ctermfg=1
hi WildMenu ctermfg=0 ctermbg=3
hi Folded ctermfg=darkgrey ctermbg=NONE
hi FoldColumn ctermfg=darkgrey ctermbg=NONE
hi DiffAdd ctermbg=4
hi DiffChange ctermbg=5
hi DiffDelete cterm=bold ctermfg=4 ctermbg=6
hi DiffText cterm=bold ctermbg=1
hi Comment ctermfg=darkcyan
hi Constant ctermfg=brown
hi Special ctermfg=5
hi Identifier ctermfg=6
hi Statement ctermfg=3
hi PreProc ctermfg=5
hi Type ctermfg=2
hi Underlined cterm=underline ctermfg=5
hi Ignore cterm=bold ctermfg=7
hi Ignore ctermfg=darkgrey
hi Error cterm=bold ctermfg=7 ctermbg=1
"vim: sw=4

View File

@ -0,0 +1,53 @@
let s:undolevels_save = &undolevels
function! g:Goto(buffername)"{{{
let l:winnr = bufwinnr(a:buffername)
if l:winnr == -1
return
endif
execute l:winnr . 'wincmd w'
endfunction"}}}
function! g:GotoLineContaining(text)"{{{
let index = match(getline(1, '$'), '\V' . escape(a:text, '\'))
if index == -1
return
endif
call cursor(index + 1, 0)
endfunction"}}}
function! g:CurrentLineContains(text)"{{{
return stridx(getline('.'), a:text) != -1
endfunction"}}}
function! g:Contains(text)"{{{
return match(getline(1, '$'), '\V' . escape(a:text, '\')) != -1
endfunction"}}}
function! g:TypeLine(text)"{{{
execute "normal i" . a:text . "\<C-g>u\n\e"
endfunction"}}}
function! g:TypeLineDone(text)"{{{
execute "normal i" . a:text . "\n\e"
" Break the undo chain
let &undolevels = s:undolevels_save
endfunction"}}}
function! g:PrintTheFuckingBuffer()"{{{
echo join(getline(1, '$'), "\n")
echo "SOMETIMES I HATE YOU VIM"
endfunction"}}}
function! g:MoveUp()"{{{
call cursor(line('.') - 1, 0)
endfunction"}}}
function! g:MoveDown()"{{{
call cursor(line('.') + 1, 0)
endfunction"}}}

View File

@ -0,0 +1,10 @@
set nocompatible
let mundo_root = simplify(expand("<sfile>:h:h"))
let mundo_test_bundle = mundo_root .'/tests/bundled'
let &runtimepath = mundo_root
let &runtimepath .= ','. mundo_root .'/tests/vim_test'
let &runtimepath .= ','. mundo_test_bundle .'/lh-vim-lib'
let &runtimepath .= ','. mundo_test_bundle .'/ut'
filetype plugin on
nnoremap q :qa!<cr>
color desert

View File

@ -44,6 +44,7 @@ lang: zh
- [增加或减小数字](#增加或减小数字)
- [复制粘贴](#复制粘贴)
- [增删注释](#增删注释)
- [编辑历史](#编辑历史)
- [文本编码格式](#文本编码格式)
- [窗口管理](#窗口管理)
- [常用编辑器窗口](#常用编辑器窗口)
@ -1075,6 +1076,38 @@ echo "selected text" | curl -s -F "content=<-" http://dpaste.com/api/v2/
`SPC ;` 可以启动一个注释操作符模式,在该模式下,可以使用移动命令确认注释的范围,
比如 `SPC ; 4 j`,这个组合键会注释当前行以及下方的 4 行。这个数字即为相对行号,可在左侧看到。
#### 编辑历史
当前文件的编辑历史,可以使用快捷键 `F7` 查看,默认会在左侧打开一个编辑历史可视化窗口。
若当前编辑器支持 `+python` 或者 `+python3`,则会使用 mundo 作为默认插件,否则则使用
undotree。
在编辑历史窗口内的快捷键如下:
| 快捷键 | 功能描述 |
| --------------- | ------------------- |
| `G` | move_bottom |
| `J` | move_older_write |
| `K` | move_newer_write |
| `N` | previous_match |
| `P` | play_to |
| `<2-LeftMouse>` | mouse_click |
| `/` | search |
| `<CR>` | preview |
| `d` | diff |
| `<down>` | move_older |
| `<up>` | move_newer |
| `i` | toggle_inline |
| `j` | move_older |
| `k` | move_newer |
| `n` | next_match |
| `o` | preview |
| `p` | diff_current_buffer |
| `q` | quit |
| `r` | diff |
| `gg` | move_top |
| `?` | toggle_help |
#### 文本编码格式
SpaceVim 默认使用 `utf-8` 码进行编码。下面是 `utf-8` 编码的四个设置:

View File

@ -42,6 +42,7 @@ description: "General documentation about how to using SpaceVim, including the q
- [Increase/Decrease numbers](#increasedecrease-numbers)
- [Copy and paste](#copy-and-paste)
- [Commenting](#commenting)
- [Undo tree](#undo-tree)
- [Multi-Encodings](#multi-encodings)
- [Window manager](#window-manager)
- [General Editor windows](#general-editor-windows)
@ -1129,6 +1130,38 @@ Comments are handled by [nerdcommenter](https://github.com/scrooloose/nerdcommen
**Tips:** `SPC ;` will start operator mode, in this mode, you can use motion command to comment lines.
For example, `SPC ; 4 j` will comment current line and the following 4 lines.
#### Undo tree
Undo tree visualizes undo history and makes it easier to browse and switch between different undo branches.
The default key binding is `F7`. If `+python` or `+python3` is enabled, mundo will be loaded,
otherwise undotree will be loaded.
Key bindings within undo tree windows:
| key bindings | description |
| --------------- | ------------------- |
| `G` | move_bottom |
| `J` | move_older_write |
| `K` | move_newer_write |
| `N` | previous_match |
| `P` | play_to |
| `<2-LeftMouse>` | mouse_click |
| `/` | search |
| `<CR>` | preview |
| `d` | diff |
| `<down>` | move_older |
| `<up>` | move_newer |
| `i` | toggle_inline |
| `j` | move_older |
| `k` | move_newer |
| `n` | next_match |
| `o` | preview |
| `p` | diff_current_buffer |
| `q` | quit |
| `r` | diff |
| `gg` | move_top |
| `?` | toggle_help |
#### Multi-Encodings
SpaceVim uses utf-8 as default encoding. There are four options for these case:

View File

@ -16,7 +16,7 @@ description: "This layer provides some tools for vim"
## Description
This layer provides some extra vim tools for SpaceVim. All tools can be
called via command. no key bindings will be definded in this layer.
called via command.
## Install