Favourite places in vim - vim

Is there a command in vim that can bookmark a place (path to the file, line number in that file), so that I can go to that place easily later?
It would be similar as NERDTree :Bookmark command. You can open your file with NERDTreeFromBookmark. I'm looking for the same functionality with the difference that bookmark is not only a file but file + line number.
Thank you

Yes you can do so with the 'mark' command. There are two types of bookmarks you can create, local and global. You are referring to a global bookmark.
You can type 'mP' to create a bookmark called P. Notice the case, uppercase indicates it is a global bookmark. To go to that bookmark, type `P.
Hope this helps
Source

The viminfo setting can contain the option !, which makes it store any global variables with uppercase letters in the viminfo file. Using this, you can define a variable called g:BOOKMARKS and store your bookmarks in there.
Here's some vimscript you could use to do that:
set viminfo+=!
if !exists('g:BOOKMARKS')
let g:BOOKMARKS = {}
endif
" Add the current [filename, cursor position] in g:BOOKMARKS under the given
" name
command! -nargs=1 Bookmark call s:Bookmark(<f-args>)
function! s:Bookmark(name)
let file = expand('%:p')
let cursor = getpos('.')
if file != ''
let g:BOOKMARKS[a:name] = [file, cursor]
else
echom "No file"
endif
wviminfo
endfunction
" Delete the user-chosen bookmark
command! -nargs=1 -complete=custom,s:BookmarkNames DelBookmark call s:DelBookmark(<f-args>)
function! s:DelBookmark(name)
if !has_key(g:BOOKMARKS, a:name)
return
endif
call remove(g:BOOKMARKS, a:name)
wviminfo
endfunction
" Go to the user-chosen bookmark
command! -nargs=1 -complete=custom,s:BookmarkNames GotoBookmark call s:GotoBookmark(<f-args>)
function! s:GotoBookmark(name)
if !has_key(g:BOOKMARKS, a:name)
return
endif
let [filename, cursor] = g:BOOKMARKS[a:name]
exe 'edit '.filename
call setpos('.', cursor)
endfunction
" Completion function for choosing bookmarks
function! s:BookmarkNames(A, L, P)
return join(sort(keys(g:BOOKMARKS)), "\n")
endfunction
I'm not sure how readable the code is, but basically, the Bookmark command accepts a single parameter to use as a name. It will store the current filename and cursor position to the g:BOOKMARKS dictionary. You can use the GotoBookmark command with a mark name to go to it. DelBookmark works in the same way, but deletes the given mark. Both functions are tab-completed.
Another way to jump through them is by using this command:
" Open all bookmarks in the quickfix window
command! CopenBookmarks call s:CopenBookmarks()
function! s:CopenBookmarks()
let choices = []
for [name, place] in items(g:BOOKMARKS)
let [filename, cursor] = place
call add(choices, {
\ 'text': name,
\ 'filename': filename,
\ 'lnum': cursor[1],
\ 'col': cursor[2]
\ })
endfor
call setqflist(choices)
copen
endfunction
CopenBookmarks will load the bookmarks in the quickfix window, which seems like a nice interface to me.
This solution is similar to Eric's -- it uses the .viminfo file, so if something goes wrong with it, you'll probably lose your marks. And if you save your marks in one vim instance, they won't be immediately available in another.
I don't know how comfortable your are with vimscript, so just in case -- to use this, you can put the code in a file under your plugin vimfiles directory, for example plugin/bookmarks.vim. Should be completely enough. Here's the entire code in a gist as well: https://gist.github.com/1371174
EDIT: Changed the interface for the solution a bit. Original version can be found in the gist history.

I have used this script (number marks). There might be better ones though. Wait for other answers!

This doesn't solve your problem as stated, but you may find it helps.
MRU.vim - Most Recently Used files plugin
Type :MRU and you get a nice searchable list of your most recently used files. Pressing enter on one brings you to it.

" When editing a file, always jump to the last known cursor position.
" And open enough folds to make the cursor is not folded
" Don't do it when the position is invalid or when inside an event handler
" (happens when dropping a file on gvim).
autocmd BufWinEnter *
\ if line("'\"") <= line("$") |
\ exe "normal! g`\"" | exe "normal! zv" |
\ endif

Related

Vim command to comment out a selection of lines?

Say I have a bunch of lines:
#Override
public void draw(Graphics g) {
g.setColor(Color.MAGENTA);
g.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
g.setColor(Color.BLACK);
g.drawRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
}
When I want to comment them out with // (i prefer line comments instead of block comments), what I do is:
Place the cursor infront of the # symbol
Ctrl-V: Switch to enter block-select mode
Select the column down to the } closing parenthesis using multiple hits of j
Shift-I: to enter block-insert
Type //
ESC to excit
Enter to finish the command
--> The lines are now commented out.
Is there an easier way where I don't need to do the block-select? I found I can use a substitution like :'<, '>s/^/\/\///g but this has two problems:
Its very clumsy and error prone to type (multiple forward and backward slashes need to be
escaped)
It places the comment symbols (//) at the beginning of
the line (position 0), not at the position where the first character
of that line was (so indentation is lost).
How can I insert // on the selected lines at the position of the first character of each line using Vi?
You can define a custom mapping or command for your :substitute.
However, there are several commenter plugins that do this very well, and those are generic (and often extensible) so that they work for any filetype:
NERD Commenter plugin
tComment plugin
commentary.vim plugin
I'd highly recommend to use one of those plugins instead of trying to reinvent a poor solution yourself.
I use Commentary as in the other answer, but a few thoughts:
<C-v>jjjjj could be <C-v>} or <C-v>/}<CR>
:substitute doesn’t have to use / as a separator: :'<,'>s-^-//
with a visual selection, you can also do :'<,'>normal! I//
How can I insert // on the selected lines at the position of the first character of each line using Vi?
Although, I'm agree with others and the dedicated plugin is a must have, but, as it is formulated in the OP, that's quite an easy task which can be implemented as one-liner:
vnoremap <silent>gc :call setline(".", printf("%*s" . &cms, indent("."), "", trim(getline("."))))<CR>
Now select some text, press "gc", and, voila, it works. To force // usage instead of the default /**/ set the following option for your buffer: setlocal cms=//\ %s. See :h 'cms'.
" I have a 'toggle comment function' that looks like
" Reference: https://stackoverflow.com/a/24652257/2571881
" these lines are needed for ToggleComment()
" Reference: https://stackoverflow.com/a/24652257/2571881
autocmd FileType c,cpp,java let b:comment_leader = '//'
autocmd FileType arduino let b:comment_leader = '//'
autocmd FileType sh,ruby,python let b:comment_leader = '#'
autocmd FileType zsh let b:comment_leader = '#'
autocmd FileType conf,fstab let b:comment_leader = '#'
autocmd FileType matlab,tex let b:comment_leader = '%'
autocmd FileType vim let b:comment_leader = '"'
function! ToggleComment()
if exists('b:comment_leader')
let l:pos = col('.')
let l:space = ( &ft =~ '\v(c|cpp|java|arduino)' ? '3' : '2' )
if getline('.') =~ '\v(\s*|\t*)' .b:comment_leader
let l:space -= ( getline('.') =~ '\v.*\zs' . b:comment_leader . '(\s+|\t+)#!' ? 1 : 0 )
execute 'silent s,\v^(\s*|\t*)\zs' .b:comment_leader.'[ ]?,,g'
let l:pos -= l:space
else
exec 'normal! 0i' .b:comment_leader .' '
let l:pos += l:space
endif
call cursor(line("."), l:pos)
else
echo 'no comment leader found for filetype'
end
endfunction
nnoremap <Leader>t :call ToggleComment()<CR>
inoremap <Leader>t <C-o>:call ToggleComment()<CR>
xnoremap <Leader>t :'<,'>call ToggleComment()<CR>
" vnoremap <Leader>t :call ToggleComment()<CR>
So, once you have this function on your ~/.vimrc you can do:
vip ...................... visual inner paragraph
<leader>t ................ in order to call the function
Make a macro with q, lets put it into the a buffer, so hit qa on a given line. Then press I// to jump to start of line, and comment it out. hit Esc and q and now your macro is done. This macro will comment out the current line.
The full command is qaI//Escq
Now visually select a bunch of lines with V, and type :norm!#a to run your a macro over those lines. This will comment out a bunch of lines.
Record another macro to do the opposite with qb^xx. This can be invoked by visually selecting the lines you want to uncomment and typing norm!#b
You can save these macros in your .vimrc and map the specific macro to a key combination if you want to "save" these commands.

Create a mapping for Vim's command-line that escapes the contents of a register before inserting it

Suppose that I have a document like this, and I want to search for all occurences of the URL:
Vim resources: [http://example.com/search?q=vim][q]
...
[q]: http://example.com/search?q=vim
I don't want to type it out in full, so I'll place my cursor on the first URL, and run "uyi[ to yank it into the 'u' register. Now to search for it, I'd like to just paste the contents of that register into the search field by running:
/\V<c-r>u<CR>
This results in Vim searching for the string 'http:' - because the '/' character terminates the search field.
I can get around the problem by running this instead:
/\V<c-r>=escape(#u, '\/')<CR><CR>
But it's a lot of typing!
How can I create a mapping for Vim's commandline that simplifies this workflow?
My ideal workflow would go something like this:
press /\V to bring up the search prompt, and use very nomagic mode
hit ctrl-x to trigger the custom mapping (ctrl-x is available)
Vim listens for the next key press... (pressing <Esc> would cancel)
pressing 'u' would escape the contents of the 'u' register, and insert on the command line
Try this:
cnoremap <c-x> <c-r>=<SID>PasteEscaped()<cr>
function! s:PasteEscaped()
" show some kind of feedback
echo ":".getcmdline()."..."
" get a character from the user
let char = getchar()
if char == "\<esc>"
return ''
else
let register_content = getreg(nr2char(char))
return escape(register_content, '\/')
endif
endfunction
By the way, something that might be useful to know (if you don't already) is that you can use ? as the delimiter for :s. Which means that you could write a search-and-replace for an url like so:
:s?http://foo.com?http://bar.com?g
I've accepted Andrew Radev's solution, which solved the hard parts. But here's the version that I've added to my vimrc file, which adds a couple of enhancements:
cnoremap <c-x> <c-r>=<SID>PasteEscaped()<cr>
function! s:PasteEscaped()
echo "\\".getcmdline()."\""
let char = getchar()
if char == "\<esc>"
return ''
else
let register_content = getreg(nr2char(char))
let escaped_register = escape(register_content, '\'.getcmdtype())
return substitute(escaped_register, '\n', '\\n', 'g')
endif
endfunction
This should work:
whether you use / or ? (to search forwards, or backwards)
and when the pasted register includes multiple lines
Also, I changed the prompt. While waiting for a register, the prompt switches to \ - which seems like a suitable cue for 'PasteEscaped'. Also, I've appended a ", which mimics Vim's behavior after pressing <c-r> at the command line.
If you've any further suggestions for improvements, please leave a comment.
How about different workflow? For example, creating your own operator to search target text as is:
" https://gist.github.com/1213642
" Requiement: https://github.com/kana/vim-operator-user
map YourFavoriteKeySequence <Plug>(operator-search-target-text)
call operator#user#define('search-target-text', 'OperatorSerachTargetText')
function! OperatorSerachTargetText(motion_wise)
execute 'normal!' '`['.operator#user#visual_command_from_wise_name(a:motion_wise).'`]"xy'
let #/ = '\V' . escape(substitute(#x, '[\r\n]$', '', ''), '\')
normal! n
endfunction
I like #nelstrom's solution and made a small change to support escaping [ and ].
cnoremap <c-x> <c-r>=<SID>PasteEscaped()<cr>
function! s:PasteEscaped()
echo "\\".getcmdline()."\""
let char = getchar()
if char == "\<esc>"
return ''
else
let register_content = getreg(nr2char(char))
let escaped_register = escape(register_content, '\'.getcmdtype())
let escaped_register2 = substitute(escaped_register,'[','\\[','g')
let escaped_register3 = substitute(escaped_register2,']','\\]','g')
return substitute(escaped_register3, '\n', '\\n', 'g')
endif
endfunction

vim: jump to buffer that contains /string/

I normally have quite a few buffers opened, which I navigate using combination of Bufexplorer and FuzzyFinder. Finding the right buffer still involves going through file names. But often, it could be much easier to say something like 'jump to buffer that contains "wip"'. Anyone knows how?
I am using a small function I put inside my .vimrc:
function! s:GrepOpenBuffers(search, jump)
call setqflist([])
let cur = getpos('.')
silent! exe 'bufdo vimgrepadd /' . a:search . '/ %'
let matches = len(getqflist())
if a:jump && matches > 0
sil! cfirst
else
call setpos('.', cur)
endif
echo 'BufGrep:' ((matches) ? matches : 'No') 'matches found'
endfunction
com! -nargs=1 -bang BufGrep call <SID>GrepOpenBuffers('<args>', <bang>0)
You could use something like the above to grep for a search term in all opened buffers.
Check out buffer grep: http://www.vim.org/scripts/script.php?script_id=2545

Vim autocommand trigger on opening "nothing"

I want vim to open up the :Explorer when no file is opened or created. Eg. when I call vim without any options.
calling vim newfile.txt should still behave the normal way though.
How would I go about doing this? I can't seem to find the correct autocmd for it.
If you want to do this for vim invocation only, the best way is to use argc():
autocmd VimEnter * :if argc() is 0 | Explore | endif
argc() function returns a number of filenames specified on command-line when vim was invoked unless something modified arguments list, more information at :h argc().
Found the answer myself:
"open to Explorer when no file is opened
function! TabIsEmpty()
" Remember which window we're in at the moment
let initial_win_num = winnr()
let win_count = 0
" Add the length of the file name on to count:
" this will be 0 if there is no file name
windo let win_count += len(expand('%'))
" Go back to the initial window
exe initial_win_num . "wincmd w"
" Check count
if win_count == 0
" Tab page is empty
return 1
else
return 0
endif
endfunction
" Test it like this:
" echo TabIsEmpty()
function! OpenExplorer()
if (TabIsEmpty())
:Explore
end
endfunction
The greatest part of this code was taken from this question.

how to remove file name from VIM dictionary menu?

I've installed pydiction dictionary in vim so that I'd be able to get a list of python commands when I press tab after partially typed command. Everything is working fine, except every time the menu shows up, there is a file name besides the each command in the list. How do I remove that filename from the menu?
plz, take a look at the picture: http://www.uzbozor.com/uploads/vim.png
(copy and paste the link if clicking doesn't work)
Thanks
I haven't managed to solve this very elegantly, but there's a workaround by writing a custom completion function that simply greps the dictionary file for matches:
function! MyCompleteFunction( findstart, base )
if a:findstart
let line = getline('.')
let start = col('.') - 1
while start > 0 && line[start - 1] =~ '[A-Za-z_]'
let start -= 1
endwhile
return start
else
silent call DictGrep( a:base, 'path\to\dictionary\file' )
let matches = []
for thismatch in getqflist()
call add(matches, thismatch.text)
endfor
return matches
endif
endfunction
Note that I have defined a function DictGrep() that actually performs the vimgrep. This is so I can call it silently and not be troubled by error messages:
function! DictGrep( leader, file )
try
exe "vimgrep /^" . a:leader . ".*/j " . a:file
catch /.*/
echo "no matches"
endtry
endfunction
Then simply define set the completefunc:
setlocal completefunc=MyCompleteFunction()
and then use for insert-mode completion (which could be mapped to replace your current dictionary completion binding).
The vimgrep could be quite a slow operation, but I haven't noticed any problems unless there are hundreds of matches in the dictionary file.
Hope this helps.

Resources