Skip to content

Archive

Category: Vim

I saw this on Twitter:

From: @jyurek
Sent: 27 Aug 2009 10:35

Is there a vim command to go to the end of the current search
highlight? Like e for words, but for search matches.
#vim #lazytweet

I found it quite interesting and didn’t have an “out of the box” Vim answer to this. So I crafted the following answer:

:nmap <silent> <c-e> /<c-r>//e<cr>:let @/='<c-r>/'<cr>

And that seems to work. Essentially you hit CTRL-e and that starts another search exactly like your previous search but puts the cursor at the end of it, then puts the original search back in the search register so that ‘n’ and ‘N’ work as expected.

I just found this cool cheat sheet for Vim that some folks may find helpful: The Vim Cheat Sheet.

We’re going to start covering a very important topic in the next series: Working with Many Files (Screencast 1). I plan on covering a full range of complexity on this topic but the first video is going to cover the dead basics:

  • How to list the buffers that are currently known and visible.
  • How to switch between buffers.
  • How to delete buffers.

That’s it :) We are starting with the basics here folks. Trust me, it will get more interesting.

I’ve been playing with FuzzyFinder recently. I’ve had it on my list of “things to understand better” for a while now, but I’ve also had a couple of people asking smart questions like this:

“Now that I am only using One Vim, how do you expect me to manage this jungle of files and buffers I’ve wound up with?”

Well, an app like FuzzyFinder is great for helping you tame that particular beast. I’ll be putting up a video tutorial about it once I’ve got a better handle on the ins and outs of it, but for now, I’ll give you a little teaser on how it can make life a bit better.

Let’s say you’ve got a code layout like this (which is a convention I’ve luckily managed to see happen where I work):

/root/of/code/tree/component_name
/root/of/code/tree/component_name/include/{directory per namespace}
/root/of/code/tree/component_name/src
/root/of/code/tree/component_name/test
/root/of/code/tree/component_name/test/include
/root/of/code/tree/component_name/test/src

You can probably imagine that I bounce around the component_name/include/{whatever}, and the component_name/src and the component_name/test/src areas a lot (I always write tests as pure implementation files – no headers). The FSwitch plugin helps a lot for switching between the header and the cpp file but that’s not all you want to do… For the rest, FuzzyFinder is what’s useful.

However, even FuzzyFinder is a pain right out of the box because it’s really not much better than just using the command-line. Yeah it’s a bit smarter but the initial navigation is still a pain. But it would be really snazzy if you could just give it a hint to get it started, and I’ve written a few functions to help out with that:

"
" SanitizeDirForFuzzyFinder()
"
" This is really just a convenience function to clean up
" any stray '/' characters in the path, should they be there.
"
function! SanitizeDirForFuzzyFinder(dir)
    let dir = expand(a:dir)
    let dir = substitute(dir, '/\+$', '', '')
    let dir = substitute(dir, '/\+', '/', '')

    return dir
endfunction

"
" GetDirForFF()
"
" The important function... Given a directory to start 'from', 
" walk up the hierarchy, looking for a path that matches the
" 'addon' you want to see.
"
" If nothing can be found, then we just return the 'from' so 
" we don't really get the advantage of a hint, but just let
" the user start from wherever he was starting from anyway.
"
function! GetDirForFF(from, addon)
    let from = SanitizeDirForFuzzyFinder(a:from)
    let addon = expand(a:addon)
    let addon = substitute(addon, '^/\+', '', '')
    let found = ''
    " If the addon is right here, then we win
    if isdirectory(from . '/' . addon)
        let found = from . '/' . addon
    else
        let dirs = split(from, '/')
        let dirs[0] = '/' . dirs[0]
        " Walk up the tree and see if it's anywhere there
        for n in range(len(dirs) - 1, 0, -1)
            let path = join(dirs[0:n], '/')
            if isdirectory(path . '/' . addon)
                let found = path . '/' . addon
                break
            endif
        endfor
    endif
    " If we found it, then let's see if we can go deeper
    "
    " For example, we may have found component_name/include
    " but what if that directory only has a single directory
    " in it, and that subdirectory only has a single directory
    " in it, etc... ?  This can happen when you're segmenting
    " by namespace like this:
    "
    "    component_name/include/org/vim/CoolClass.h
    "
    " You may find yourself always typing '' from the
    " 'include' directory just to go into 'org/vim' so let's
    " just eliminate the need to hit the ''.
    if found != ''
        let tempfrom = found
        let globbed = globpath(tempfrom, '*')
        while len(split(globbed, "\n")) == 1
            let tempfrom = globbed
            let globbed = globpath(tempfrom, '*')
        endwhile
        let found = SanitizeDirForFuzzyFinder(tempfrom) . '/'
    else
        let found = from
    endif

    return found
endfunction

"
" GetTestDirForFF()
"
" Now overload GetDirFF() specifically for
" the test directory (I'm really only interested in going
" down into test/src 90% of the time, so let's hit that
" 90% and leave the other 10% to couple of extra keystrokes)
"
function! GetTestDirForFF(from)
    return GetDirForFF(a:from, 'test/src/')
endfunction

"
" GetIncludeDirForFF()
"
" Now specialize for the 'include'.  Note that we rip off any
" '/test/' in the current 'from'.  Why?  The /test/ directory
" contains an 'include' directory, which would match if we 
" were anywhere in the 'test' directory, and we don't want that.
"
function! GetIncludeDirForFF(from)
    let from = substitute(SanitizeDirForFuzzyFinder(a:from),
                        \  '/test/.*$', '', '')
    return GetDirForFF(from, 'include/')
endfunction

"
" GetSrcDirForFF()
"
" Much like the GetIncludeDirForFF() but for the 'src' directory.
"
function! GetSrcDirForFF(from)
    let from = substitute(SanitizeDirForFuzzyFinder(a:from),
                        \  '/test/.*$', '', '')
    return GetDirForFF(from, 'src/')
endfunction

Now, you can use these by making some mappings. I have the following three:

nnoremap ,ff :FuzzyFinderFile<cr>
nnoremap ,ft :FuzzyFinderFile <c-r>=GetTestDirForFF('%:p:h')<cr><cr>
nnoremap ,fi :FuzzyFinderFile <c-r>=GetIncludeDirForFF('%:p:h')<cr><cr>
nnoremap ,fs :FuzzyFinderFile <c-r>=GetSrcDirForFF('%:p:h')<cr><cr>

Clearly this requires that you’re already working on a file somewhere inside the /root/of/code/tree/component_name directory but that’s my convention most of the time when working on a library so this works great for me.

Cheers!

I took a shot at the C++ no-namespace indenting for Vim and thought I had it, but of course this sort of thing is always a pain. I think it’s more solid now but I make no guarantees :). You can find the indent code as part of the General C++ Settings file that should go in $VIM/after/ftplugin/cpp.vim.

I made an enhancement to Protodef to allow for the ability to eliminate the namespace when pulling in the prototypes. So now you can make them look like this:

[cpp]
one::two::SomeClass::functionA()
{
}

one::two::SomeClass::functionB()
{
}
[/cpp]

… or this:

[cpp]
namespace one { namespace two {

SomeClass::functionA()
{
}

SomeClass::functionB()
{
}

} }
[/cpp]

(Protodef doesn’t put the namespace wrappers in for you – you would have done that by yourself. What it does do is allow you to skip putting in the one::two::.)

(NOTE: There’s a more up to date version of this in the General C++ Settings section.)

Finally! Vim’s decision to add a ‘shiftwidth‘ to everything I type when I’m inside a namespace is thoroughly annoying and there appears to be no “standard” way to fix this in Vim aside from writing your own function for use in the ‘indentexpr‘ option.

Well I finally got around to writing this up and, while extremely crude, it appears to work alright. You’ll find the function definition below as well as in the General C++ Settings section.

" Fix up indent issues - I can't stand wasting an indent because
" I'm in a namespace.  If you don't like this then just comment
" this line out.
setlocal indentexpr=GetCppIndentNoNamespace(v:lnum)

"
" GetCppIndentNoNamespace()
"
" This little function calculates the indent level for C++ and
" treats the namespace differently than usual - we ignore it.  The
" indent level is the for a given line is the same as it would
" be were the namespace not event there.
"
" This function is rather crude but it works.
"
function! GetCppIndentNoNamespace(lnum)
    let nsLineNum = search('^\s*\\s\+\S\+', 'bnW')
    if nsLineNum == 0
        return cindent(a:lnum)
    else
        let incomment = 0
        for n in range(nsLineNum + 1, a:lnum - 1)
            let cline = getline(n)
            if cline =~ '^\s*/\*'
                let incomment = 1
            elseif cline =~ '^.*\*/'
                let incomment = 0
            elseif incomment == 0
                if cline =~ '^\s*\S\+'
                    return cindent(a:lnum)
                endif
            endif
        endfor
        return cindent(nsLineNum)
    endif
endfunction

Ah, now this is complete bliss… I’m writing to you straight from within MacVim using the blogit.vim script. Up until now, the best way I could use Vim to write up on this site was to use MacVim‘s great little feature of hacking Cocoa to allow you to edit any NSTextField natively within the MacVim session. But now, I can just write straight within MacVim and then publish directly from here.

I owe a big thanks to Romain Bignon for this one. Excellent plugin!

Snapshot 49 is out! Thanks a lot, Björn and supporters for this great version of Vim for the Mac. Well done.

I use Vim mostly at work on Windows (don’t even get me started on that lovely O/S), working on two different machines – my desktop and my laptop – neither of which are backed up. That presents a couple of problems:

  • How do I keep the two machines up to date with each other?
  • How do I avoid the death crash from hell?

I solve both of those problems with one move – keep the configuration in Perforce. Now, I’m not a huge fan of Perforce but it’s all I’ve got at work, and it certainly does the job when I want to solve these problems. You don’t need much to handle this issue so any version control system should do (so long as the repository is remote and backed up for you, of course :D).

Once you’ve set up your Vim files into your version control system, you just need to tell Vim where they are.  You do this with the $VIM environment variable (or %VIM% if you’re on Windows and you just want to be really pedantic about the details :D).  You don’t need to do this, of course, if your version controlled Vim files map to the same place as Vim would normally look, but in my case they map to somewhere really weird so I need to help Vim out by telling it where the files are.

Then it’s just like versioning any other piece of code.  Check config files out, modify them, check them in, sync them to other machines.  Normally I work on my desktop box, so the first thing I do when I power up my laptop is p4 sync my Vim configuration so I can get the exact same experience as I had on my desktop, right away.

Switch to our mobile site