Skip to content

Instantly share code, notes, and snippets.

@gfixler
Created July 24, 2012 01:14
Show Gist options
  • Save gfixler/3167301 to your computer and use it in GitHub Desktop.
Save gfixler/3167301 to your computer and use it in GitHub Desktop.
A Vim take on Emacs' AceJump mode, itself based on Vim's PreciseJump and EasyMotion plugins
" ACEJUMP
" Based on emacs' AceJump feature (http://www.emacswiki.org/emacs/AceJump).
" AceJump based on these Vim plugins:
" EasyMotion (http://www.vim.org/scripts/script.php?script_id=3526)
" PreciseJump (http://www.vim.org/scripts/script.php?script_id=3437)
" Type AJ mapping, followed by a lower or uppercase letter.
" All words on the screen starting with that letter will have
" their first letters replaced with a sequential character.
" Type this character to jump to that word.
highlight AceJumpGrey ctermfg=darkgrey guifg=lightgrey
highlight AceJumpRed ctermfg=darkred guibg=NONE guifg=black gui=NONE
function! AceJump ()
" store some current values for restoring later
let origPos = getpos('.')
let origSearch = @/
" prompt for and capture user's search character
echo "AceJump to words starting with letter: "
let letter = nr2char(getchar())
" return if invalid key, mouse press, etc.
if len(matchstr(letter, '\k')) != 1
echo ""
redraw
return
endif
" redraws here and there to get past 'more' prompts
redraw
" row/col positions of words beginning with user's chosen letter
let pos = []
" monotone all text in visible part of window (dark grey by default)
call matchadd('AceJumpGrey', '\%'.line('w0').'l\_.*\%'.line('w$').'l', 50)
" loop over every line on the screen (just the visible lines)
for row in range(line('w0'), line('w$'))
" find all columns on this line where a word begins with our letter
let col = 0
let matchCol = match(' '.getline(row), '.\<'.letter, col)
while matchCol != -1
" store any matching row/col positions
call add(pos, [row, matchCol])
let col = matchCol + 1
let matchCol = match(' '.getline(row), '.\<'.letter, col)
endwhile
endfor
if len(pos) > 1
" jump characters used to mark found words (user-editable)
let chars = 'abcdefghijlkmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,.;"[]<>{}|\\'
if len(pos) > len(chars)
" TODO add groupings here if more pos matches than jump characters
endif
" trim found positions list; cannot be longer than jump markers list
let pos = pos[:len(chars)]
" jumps list to pair jump characters with found word positions
let jumps = {}
" change each found word's first letter to a jump character
for [r,c] in pos
" stop marking words if there are no more jump characters
if len(chars) == 0
break
endif
" 'pop' the next jump character from the list
let char = chars[0]
let chars = chars[1:]
" move cursor to the next found word
call setpos('.', [0,r,c+1,0])
" create jump character key to hold associated found word position
let jumps[char] = [0,r,c+1,0]
" replace first character in word with current jump character
exe 'norm r'.char
" change syntax on the jump character to make it highly visible
call matchadd('AceJumpRed', '\%'.r.'l\%'.(c+1).'c', 50)
endfor
call setpos('.', origPos)
" this redraw is critical to syntax highlighting
redraw
" prompt user again for the jump character to jump to
echo 'AceJump to words starting with "'.letter.'" '
let jumpChar = nr2char(getchar())
" get rid of our syntax search highlighting
call clearmatches()
" clear out the status line
echo ""
redraw
" restore previous search register value
let @/ = origSearch
" undo all the jump character letter replacement
norm u
" if the user input a proper jump character, jump to it
if has_key(jumps, jumpChar)
call setpos('.', jumps[jumpChar])
else
" if it didn't work out, restore original cursor position
call setpos('.', origPos)
endif
elseif len(pos) == 1
" if we only found one match, just jump to it without prompting
" set position to the one match
let [r,c] = pos[0]
call setpos('.', [0,r,c+1,0])
elseif len(pos) == 0
" no matches; set position back to start
call setpos('.', origPos)
endif
" turn off all search highlighting
call clearmatches()
" clean up the status line and return
echo ""
redraw
return
endfunction
nnoremap <Leader>aj :call AceJump()<CR>
@joakin
Copy link

joakin commented Apr 8, 2013

This is great, I find it more useful than EasyMotion since it restricts the amount of matches really well.

I'm having some issues with the shadowing, does this have a repo where we could report issues and fork?

Cheers

@gfixler
Copy link
Author

gfixler commented Apr 12, 2013

Well thanks, but this was just me testing myself, seeing if I could implement the idea in Vimscript. I'm not really maintaining it, nor recommending it. Feel free to copy it and update it as you wish, if it works well for you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment