Skip to content

Commit

Permalink
Added movement by block segments
Browse files Browse the repository at this point in the history
- Reloading tests is easier
- Added documentation for motions
- Set global segment key for tests
  • Loading branch information
tweekmonster committed Feb 22, 2016
1 parent c9babf7 commit e3ba65e
Show file tree
Hide file tree
Showing 10 changed files with 388 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Expand Up @@ -7,4 +7,4 @@ before_script: |
docker pull tweekmonster/ubuntu-vims
make test-dirs
script: make test
script: make test-print
7 changes: 5 additions & 2 deletions Makefile
@@ -1,8 +1,10 @@
DOCKER = docker run -it --rm -v $(PWD):/testplugin -v $(PWD)/test/vim:/home tweekmonster/ubuntu-vims
TEST_ARGS = '+Vader! test/*.vader'

test: test-dirs
$(DOCKER) vim-trusty $(TEST_ARGS)
$(DOCKER) vim-trusty '+Vader test/test.vader'

test-print: test-dirs
$(DOCKER) vim-trusty '+Vader! test/test.vader'

run-precise:
$(DOCKER) vim-precise -u /home/vimrc_full
Expand All @@ -17,6 +19,7 @@ test/vim/plugins:
cd $@ && git clone https://github.com/junegunn/vader.vim
cd $@ && git clone https://github.com/Lokaltog/vim-easymotion
cd $@ && git clone https://github.com/Raimondi/delimitMate
cd $@ && git clone https://github.com/tpope/vim-scriptease

clean:
rm -rf test/vim/plugins
Expand Down
12 changes: 12 additions & 0 deletions autoload/braceless.vim
Expand Up @@ -486,6 +486,18 @@ function! braceless#get_block_lines(line, ...)
endfunction


function! braceless#get_parent_block_lines(line, ...)
let saved = winsaveview()
let block = braceless#get_block_lines(a:line)
let [indent_char, indent_len] = braceless#indent#space(block[2], -1)
call cursor(block[2], 0)
let sub = search('^'.indent_char.'{-,'.indent_len.'}\S', 'nbW')
let parent = braceless#get_block_lines(sub)
call winrestview(saved)
return [parent, block]
endfunction


" Kinda like black ops, but more exciting.
function! braceless#block_op(motion, keymode, vmode, op, count)
let pattern = braceless#get_pattern()
Expand Down
138 changes: 138 additions & 0 deletions autoload/braceless/movement.vim
Expand Up @@ -31,3 +31,141 @@ function! braceless#movement#block(direction, vmode, by_indent, count)
let i -= 1
endwhile
endfunction



" Note: I think everything below needs some refactoring.

" Positions the cursor after movement in function below
function! s:position_inner_block(pat, top)
if a:top
let top = braceless#scan_head(a:pat, 'nceb')[0]
if top == 0
let top = 1
else
let block = braceless#get_block_lines(top + 1)
if block[1] < line('.')
let top = nextnonblank(block[1] + 1)
else
let top = nextnonblank(top + 1)
endif
endif
let [_, indent_len] = braceless#indent#space(top, 0)
call cursor(top, indent_len + 1)
else
let bottom = braceless#scan_head(a:pat, 'nc')[0]
if bottom == 0
let bottom = prevnonblank(line('.'))
else
let block = braceless#get_block_lines(line('.'))
if block[1] < bottom
let bottom = prevnonblank(block[1])
else
let bottom = prevnonblank(bottom - 1)
endif
endif
call cursor(bottom, col([bottom, '$']) - 1)
endif
endfunction


" Returns the line of the next block boundary depending on direction. A block
" boundary is considered anything that's between block heads and block ends,
" and vice versa.
function! s:skip_boundary(pat, direction, start)
let flags = 'W'
if a:direction == -1
let flags .= 'b'
else
let flags .= 'e'
endif
let pos = getpos('.')[1:2]
let found = braceless#scan_head(a:pat, flags)[0]

if found == 0
if a:direction == -1
let l = prevnonblank(pos[0] - 1)
if l == 0
let l = 1
endif
else
let l = nextnonblank(pos[0] + 1)
if l == 0
let l = line('$')
endif
endif

call cursor(pos)
return l
endif

if a:direction == -1
let block = braceless#get_block_lines(found)
if block[0] != 0 && block[1] < pos[0]
let found = prevnonblank(block[1])
else
let prev_found = braceless#scan_head(a:pat, flags.'n')[0]
if abs(found - prev_found) <= 1
let found = s:skip_boundary(a:pat, a:direction, a:start)
else
let found = prevnonblank(found - 1)
endif
endif
else
let block = braceless#get_block_lines(pos[0])
let n = nextnonblank(block[1] + 1)
if n != 0 && n < found
" Too far beyond the current block
let found = nextnonblank(n - 1)
else
let next_found = braceless#scan_head(a:pat, flags.'n')[0]
if abs(next_found - found) <= 1
" Too close to another block head
let found = s:skip_boundary(a:pat, a:direction, a:start)
else
let found = nextnonblank(found + 1)
endif
endif
endif

call cursor(pos)
return found
endfunction


function! braceless#movement#inner_block(direction, vmode, inclusive, top)
if a:vmode == 'v'
normal! gv
endif

let pattern = braceless#get_pattern()
let pat = '^\s*'
if pattern.jump !~ '\\zs'
let pat .= '\zs'
endif
let pat .= pattern.jump

let c = a:inclusive ? v:count : v:count1
let pos = getpos('.')[1:2]
let flag = ''
let alt_flag = ''
if a:direction == -1
let flag = 'b'
let next_flag = 'nb'
else
let flag = 'e'
let next_flag = 'ne'
endif

let c_line = line('.')
while c > 0
let c_line = s:skip_boundary(pat, a:direction, c_line)
if c_line == 0
break
endif
call cursor(c_line, 0)
let c -= 1
endwhile

call s:position_inner_block(pat, a:top)
endfunction
41 changes: 41 additions & 0 deletions doc/braceless.txt
Expand Up @@ -18,6 +18,7 @@ CONTENTS *braceless-contents*
Rationale |braceless-rationale|
Quick Start |braceless-quickstart|
Text Object Taxonomy |braceless-taxonomy|
Motions |braceless-motions|
Configuration |braceless-config|
Features
Indentation |braceless-indent|
Expand Down Expand Up @@ -136,6 +137,37 @@ While the primary focus of this plugin is for Python, it could be useful for
other languages that rely heavily on indentation.


MOTIONS *braceless-motions*
==============================================================================

Movement by blocks is done using the |[[| and |]]| motions. These will
position you at the end of the previous or next block heads.

The object selection key is P, as in `iP` and `aP`. These are always |linewise|
operations.

In addition to movement by blocks, you can move by block segments. These
movements are similar to Vim's fold movements (e.g. |[z| and |zj|). The
default key for these movements is <Tab>. I'm sorry in advanced if you are
using <Tab> in normal mode for other things. Keyboard real estate is tough in
Vim. You can change this key with |g:braceless_segment_key|.


*[<Tab>* Position the cursor at the beginning of the current block
segment.

*]<Tab>* Position the cursor at the end of the current block segment.

*<Tab>k* Position the cursor at the end of the block segment above the
current one.

*<Tab>j* Position the cursor at the beginning of the block segment
below the current one.


All of the above key accept a |count| and can be used with an |operator|.


CONFIGURATION *braceless-config*
==============================================================================

Expand All @@ -144,6 +176,15 @@ them to your |vimrc| unless you want to change them. If you want to disable a
mapping, assign it an empty string.


*g:braceless_segment_key*

This sets the block segment movement key. By default it uses <Tab>.

Default:
>
let g:braceless_segment_key = '<tab>'
<

*g:braceless_block_key*

This sets the text object key. This gives you commands like `vaP`, `ciP`,
Expand Down
24 changes: 24 additions & 0 deletions plugin/braceless.vim
Expand Up @@ -10,6 +10,7 @@ let s:nomodline_avail = v:version > 703 || (v:version == 703 && has('patch438'))
let g:loaded_braceless = 1


let g:braceless#key#segment = get(g:, 'braceless_segment_key', '<tab>')
let g:braceless#key#block = get(g:, 'braceless_block_key', 'P')
let g:braceless#key#jump_prev = get(g:, 'braceless_jump_prev_key', '[')
let g:braceless#key#jump_next = get(g:, 'braceless_jump_next_key', ']')
Expand All @@ -29,6 +30,18 @@ function! s:enable(...)

let b:braceless_enabled = 1

if !empty(g:braceless#key#segment)
execute 'map <buffer> ['.g:braceless#key#segment.' <Plug>(braceless-jump-inner-prev)'
execute 'map <buffer> ]'.g:braceless#key#segment.' <Plug>(braceless-jump-inner-next)'
execute 'map <buffer> '.g:braceless#key#segment.'k <Plug>(braceless-jump-inner-prev-ex)'
execute 'map <buffer> '.g:braceless#key#segment.'j <Plug>(braceless-jump-inner-next-ex)'

execute 'vmap <buffer> ['.g:braceless#key#segment.' <Plug>(braceless-jump-inner-prev-v)'
execute 'vmap <buffer> ]'.g:braceless#key#segment.' <Plug>(braceless-jump-inner-next-v)'
execute 'vmap <buffer> '.g:braceless#key#segment.'k <Plug>(braceless-jump-inner-prev-v-ex)'
execute 'vmap <buffer> '.g:braceless#key#segment.'j <Plug>(braceless-jump-inner-next-v-ex)'
endif

if !empty(g:braceless#key#block)
execute 'vmap <buffer> i'.g:braceless#key#block.' <Plug>(braceless-i-v)'
execute 'vmap <buffer> a'.g:braceless#key#block.' <Plug>(braceless-a-v)'
Expand Down Expand Up @@ -124,11 +137,22 @@ endfunction


function! s:init()

vnoremap <silent> <Plug>(braceless-i-v) :<C-u>call braceless#block_op('i', 'v', visualmode(), '', v:count1)<cr>
vnoremap <silent> <Plug>(braceless-a-v) :<C-u>call braceless#block_op('a', 'v', visualmode(), '', v:count1)<cr>
onoremap <silent> <Plug>(braceless-i-n) :<C-u>call braceless#block_op('i', 'n', visualmode(), v:operator, v:count1)<cr>
onoremap <silent> <Plug>(braceless-a-n) :<C-u>call braceless#block_op('a', 'n', visualmode(), v:operator, v:count1)<cr>
vnoremap <silent> <Plug>(braceless-jump-inner-prev-v) :<C-u>call braceless#movement#inner_block(-1, 'v', 1, 1)<cr>
vnoremap <silent> <Plug>(braceless-jump-inner-next-v) :<C-u>call braceless#movement#inner_block(1, 'v', 1, 0)<cr>
vnoremap <silent> <Plug>(braceless-jump-inner-prev-v-ex) :<C-u>call braceless#movement#inner_block(-1, 'v', 0, 0)<cr>
vnoremap <silent> <Plug>(braceless-jump-inner-next-v-ex) :<C-u>call braceless#movement#inner_block(1, 'v', 0, 1)<cr>
noremap <silent> <Plug>(braceless-jump-inner-prev) :<C-u>call braceless#movement#inner_block(-1, 'n', 1, 1)<cr>
noremap <silent> <Plug>(braceless-jump-inner-next) :<C-u>call braceless#movement#inner_block(1, 'n', 1, 0)<cr>
noremap <silent> <Plug>(braceless-jump-inner-prev-ex) :<C-u>call braceless#movement#inner_block(-1, 'n', 0, 0)<cr>
noremap <silent> <Plug>(braceless-jump-inner-next-ex) :<C-u>call braceless#movement#inner_block(1, 'n', 0, 1)<cr>
vnoremap <silent> <Plug>(braceless-jump-prev-v) :<C-u>call braceless#movement#block(-1, visualmode(), 0, v:count1)<cr>
vnoremap <silent> <Plug>(braceless-jump-next-v) :<C-u>call braceless#movement#block(1, visualmode(), 0, v:count1)<cr>
vnoremap <silent> <Plug>(braceless-jump-prev-v-indent) :<C-u>call braceless#movement#block(-1, visualmode(), 1, v:count1)<cr>
Expand Down
35 changes: 23 additions & 12 deletions test/autocmd.vader
@@ -1,4 +1,4 @@
Before (setup autocmd):
Execute (setup autocmd):
function! s:init()
let g:did_init = 1
endfunction
Expand All @@ -11,25 +11,36 @@ Before (setup autocmd):
let g:did_enable_yaml = 1
endfunction

autocmd User BracelessInit call <SID>init()
autocmd User BracelessEnabled_python call <SID>enabled_python()
autocmd User BracelessEnabled_yaml call <SID>enabled_yaml()
autocmd FileType python,yaml BracelessEnable
augroup braceless_test
autocmd User BracelessInit call <SID>init()
autocmd User BracelessEnabled_python call <SID>enabled_python()
autocmd User BracelessEnabled_yaml call <SID>enabled_yaml()
autocmd FileType python,yaml BracelessEnable
augroup END

After (autocmd):
After (reset global vars):
silent! unlet g:did_enable_python
silent! unlet g:did_enable_yaml

Execute (autocmd no init without enable):
AssertEqual 0, get(g:, 'did_init', 0), 'init'
AssertEqual 0, get(g:, 'did_init', 0), 'braceless init before enable'

Execute (enable python):
set filetype=python
AssertEqual 1, get(g:, 'did_init', 0), 'init'
AssertEqual 1, get(g:, 'did_enable_python', 0), 'enable python'
AssertEqual 0, get(g:, 'did_enable_yaml', 0), 'enable yaml'
AssertEqual 1, get(g:, 'did_init', 0), 'braceless did not init'
AssertEqual 1, get(g:, 'did_enable_python', 0), 'python was not enabled'
AssertEqual 0, get(g:, 'did_enable_yaml', 0), 'yaml should not be enabled'

Execute (enable yaml):
set filetype=yaml
AssertEqual 0, get(g:, 'did_enable_python', 0), 'enable python'
AssertEqual 1, get(g:, 'did_enable_yaml', 0), 'enable yaml'
AssertEqual 0, get(g:, 'did_enable_python', 0), 'python should not be enabled'
AssertEqual 1, get(g:, 'did_enable_yaml', 0), 'yaml was not enabled'

Execute (cleanup autocmd):
silent! delf s:init
silent! delf s:enabled_python
silent! delf s:enabled_yaml
silent! unlet g:did_init
silent! unlet g:did_enable_python
silent! unlet g:did_enable_yaml
autocmd! braceless_test

0 comments on commit e3ba65e

Please sign in to comment.