So, mika started a little pet project called zsh-pony, which aims at telling zsh-newcomers about how to facilitate many of zsh's advanced features in their own setups (which by the way, I'm considering to be a way better approach of getting starters into zsh than telling them "Here, clone this github-repo and be happy" - which only helps until the zsh-padawan hits the first issue in that repo - at which point, he/she is screwed).

One of the things mika lists is `auto_cd' as "switching directories for lazy people". I don't agree. Never liked that option. It makes stuff at the command position even more ambiguous than it already is. Sure, it may sound nice to be able to type "foo<RET>" to go a directory "foo" in the current directory. But what if the directory is called "git"? "git<RET>", huh? Yeah, well. That'll execute `git' the version control's main command. Of course, you'll scream at me, that you'll just "./git<RET>". Because that's so much simpler than "cd git<RET>". Especially on - say - german keyboards, where `/' is "Shift-7".

Also, you don't get the special completion for the `cd' builtin. It's nice to have it not complete stuff like files or "lost+found/" or "CVS/".

The one and only thing in `auto_cd', that seems worthwhile to me, is "..<RET>" to go up a directory level. But then, what to do to go up two levels? "../..<RET>"? I think not.

A friend of mine (hey Fabian!) asked me a while back if I could come up with a feature that would let him go up levels like he did in his amiga shell. Apparently, you could enter a forward-slash to go up a level. That is useful. So, what my setup does is if upon hitting "<RET>" and the command buffer is made up of just dots, it will convert that into "cd ../..". One ".."-level for each dot in the buffer.

So, ".<RET>" gets me up one directory. "..<RET>" two levels. And so on. There is no arbitrary limit, so if I input a hundert dots my shell will try to go up a hundert levels.

How to do that, well here's how:

function ft_accept_line_cd_up() {
    setopt local_options extended_glob
    local -a cmdline
    cmdline=( ${(z)BUFFER} )
    if (( ${#cmdline} == 1 )) && [[ ${cmdline[1]} == .# ]] ; then
        BUFFER='cd '
        for (( i = 1; i <= ${#${cmdline[1]}}; i++ )); do
            BUFFER="${BUFFER}../"
        done
        BUFFER=${BUFFER%/}
    fi
    zle ".$WIDGET"
}
zle -N accept-line ft_accept_line_cd_up

After that the following

% ...<RET>

will turn into

% cd ../../..<RET>

But what about "../../<TAB>"? I hear you ask. Well, my setup does that too. I can just do this:

% ..<TAB>

...and that will behave as if I did this:

% cd ../../<TAB>

Here's a simple way to get that going:

function ft_complete_with_dots() {
    setopt local_options extended_glob
    local -a cmdline
    cmdline=( ${(z)BUFFER} )
    if (( ${#cmdline} == 1 )) && [[ ${cmdline[1]} == .# ]] ; then
        BUFFER='cd '
        for (( i = 1; i <= ${#${cmdline[1]}}; i++ )); do
            BUFFER="${BUFFER}../"
        done
        CURSOR=${#BUFFER}
    fi
    zle ".$WIDGET"
}
zle -N complete-word ft_complete_with_dots
# if you don't do this already...
bindkey '^I' complete-word

You could factor the turn-dots-into-cd code into its own function to avoid the code duplication with the previous snippet, but that's basically it.

Take that for laziness.

(Oh yeah. If you're a big big fan of `auto_cd', use it by all means. The above snippets also work if that option is enabled, so you can have both.)

Posted Sat 30 Jul 2011 11:57:14 CEST Tags: