Sunday, December 16, 2012

Edit/open files quick


As developers, we edit lot of source files, and each has a specific extension
and mostly we either type them or let the shell do the auto-completion.
Most of the source files that I work on, follow this long_underscore_separated_names convention, and I hate to even press TAB TAB TAB...
When I know a file name, or am seeing it on console (with the previous ls), I want to be able to open a file (in the current directory) with as few keystrokes as possible.

I came up with a small Perl script, which, when combined with shell aliases
can be a real time saver (and save you from RSI as well! :) )
The same effect can be achieved with sed/awk or any other regex-well-supported
languages, but Perl is my favorite.

Approach

Given a file like: my_lisp_parser.c,  I want to be able to call it as
m l p or just mlp (first letters of the words, assuming file names separated with
underscore, or hyphen)
The extensions, lets not worry for now...
So, how do we go about?
The regex way...
build a regex from these characters and then do a quick look-up in the current dir:
like, m_l_p.
How to build this regex:
simple, split the input if its one word, or concat if its multiple words, but
before joining, add [^_-]*, that's the equivalent of our something above.

What to do in case of ambiguity, i.e, if we have a file like
my_lex_print.c (some hypothetical name, duh!)
then, using mlp will have a problem, so we should say
m le p
where le is the disambiguator.

So, now we can edit some files, with some extensions, what about those with
no extension ? usually I edit a lot of Makefile, that, I can handle as a
special case.

The extension is given as an argument to the script, but while using this
script, we do not invoke it directly, but with shell aliases, therefore
typing the extension will not be necessary. I use bash BTW, so here are my bash aliases (for most commonly used file extensions)

alias vc='~/short_open.pl c'
alias vh='~/short_open.pl h'
alias vp='~/short_open.pl p[ly]'
alias vy='~/short_open.pl yml'
alias vd='~/short_open.pl diff'
alias vs='~/short_open.pl sh'
alias vl='~/short_open.pl l[ou][ga]'
alias vm='~/short_open.pl mk'
 

I can open a .c file like vc mlp, or a .h file like vh mlp

The full script is here:

$editor = shift;
$ext = shift;

if ( scalar(@ARGV) == 1 ) {
    $in = shift;
    if ((length($in) == 1) and ($in eq 'm')) {
        system("$editor [Mm]akefile");
        exit 0;
    }
    if ((length($in) == 1) and ($in eq 'R')) {
        system("$editor README");
        exit 0;
    }
    if ((length($in) == 1) and ($in eq 'w')) {
        system("$editor wscript");
        exit 0;
    }
    @p = split( //, $in ); # 1 arg - pick and split each char
}
else {
    @p = (@ARGV); # multi arg - use them as is
}
$res = '^';
$res .= join( '[^_-]*[-_]', @p );
$res .= '[^_-]*[.]';
$res .= $ext . '$'
$re = qr/$res/;
foreach $a ( glob("*.$ext") ) {
    if ( $a =~ m/$re/ ) {
        print " [$a] \n";
        system ("$editor $a");
        exit 0;
    }
}

# If it were not really a long name with underscore, then try
# a plain short name (assume 1 arg)
if ( scalar(@ARGV) > 1 ) {
    exit 1;
}
$res = $in;  
$res .= '.*[.]'# deliberate unbounded match so that any part
$res .= $ext;     # of the file name can be input
$re = qr/$res/;
foreach $a ( glob("*.$ext") ) {
    if ( $a =~ m/$re/ ) {
        print " [$a] \n";
        system ("$editor $a");
        exit 0;
    }
}


A safe `rm' for bash


I have many times typed
rm -rf *  !!
in a hurry, unknowingly
This can be really really upsetting, depending on which directory you
are in ! :)
While talking to my teammates on how to make rm a little safe, I
tried the following:
alias rm to a function, which will check the args and then force
interactive mode. The option check is simple, the only problem is
your function will not receive '*', as the shell would have already
expanded it!
The solution is to club alias and function, with disable globbing
and then re-enable it!

Thanks to Simon's article which helped me with this.

Full bash function here:

alias rm='set -f;rm_in'
rm_in() {
    local YN HIT LAST N
    N=$#
    LAST=${!N}
    if [[ $1 =~ -[fr]+ ]] && [[ $2 == '*' ]] ; then
        HIT=1
        echo -n "Are you insane ? (y/n): "
        read YN
    fi
    if [[ -z $HIT ]] ; then
        if [[ $LAST == '*' ]]; then
            HIT=1
            echo -n "This is li'll scary, are you sure ? (y/n): "
            read YN
        fi
    fi
    set +f
    if [[ ! -z $YN ]]; then
        if [[ $YN =~ [yY] ]] ; then
            echo "May it be! .."
            eval command rm "$@"
        else
            echo "Thank God!"
        fi
    fi
    if [[ -z $HIT ]] ; then
        eval command rm "$@"
    fi
}