Pages

Wednesday, August 17, 2011

Tab completes my life

Sorry for the somewhat awkward title. I just couldn't resist the geeky pun, and couldn't come up with a better phrase to express it. Hope you got the short message from the title. Also please forgive the awful (or awesome?) prose below. I've always wanted to write a post like this :)

Having been a happy Linux user for the past year-and-a-half, I respect the art of command line-fu. The most useful, yet simple, trick in the bag of the CLI ninja is, of course, the all-powerful Tab key. In one stroke - literally - it can help him type even the longest of commands with minimal key presses. I just love going ..<Tab>..<Tab>..<Tab><Enter> on my terminal, commands flowing at the speed of thought.

Now I'm so used to it, I expect it to be present everywhere - my browser, my text editor, in git commands, in the python interactive interpreter. Basically, I expect my computer to do its job - that is, helping me out while I try to do mine. That does not happen easily, however. So I set out on a quest - to try to have some form of Tab-completion in as many programs as possible. Below, I chronicle my journey, for the benefit of the traveller seeking the same end :-

The Switch from Bash to Zsh:
All Linuxes come with the same default shell: bash. However, there's another shell out there - zsh - which is little known, but extremely powerful. It's the first step from CLI apprentice to CLI productivity master. There are some excellent plugins available for zsh which add tab completion for many commands like git, pip, gem, and kill along with some bonuses. It'll also help you out with many other small things, like ignoring case and correcting spelling mistakes.

If you're thinking to try out zsh, I'd recommend installing Oh My Zsh! first thing after you install zsh itself. It'll make it much easier to set up zsh to your liking, replete with themes, plugins, and aliases. Also, as it turns out, the $RPS1 environment variable, along with the git support in Oh My Zsh!, pretty much renders my alternative (and very useful) bash prompt obsolete.

The Text Editor:
Since I'm a die-hard Emacs user, this section's a bit biased. For Emacs, I use Auto Complete Mode. It's fairly easy to setup, and blazing fast once you get used to it. Note that this is not code completion, it's simple, cross-buffer word completion, so it'll suggest words based on what's in the rest of the buffer, or in other open buffers. I find this better than code completion for the simple reason that code completion would require me to install separate plugins for each language I use, while on the other hand, word completion is universally effective, if not as accurate. Heck, it even works in comments. Combine that with YASnippet, and you'll find yourself with one finger on the Tab key nearly perpetually, and code flowing from your fingers.

Oh, and let's not forget ido-mode. It's the coolest, dead-simplest Emacs plugin ever.

For Vim users who feel left out, a quick Google search yields this.

The Interactive Interpreters (Python and Ruby):
I hated it when I fired up python the first time and it didn't offer Tab-completion. I later understood that since whitespace is significant in Python, completion is not enabled by default in the interpreter (or the Python REPL, in purist-speak). I figured spaces could be used for indentation just as easily as tabs, so I enabled completion using this .pythonrc. It's not like I miss tabs for indentation now.

Coming to irb (the Ruby interpreter), you can have Tab-completion and syntax highlighting in it using the wirble gem and a .irbrc. Pretty straight forward, supremely effective, no down-sides.

The Browser:
Not much to say here. Probably the only thing you can Tab-complete in a browser is URLs, which the latest versions of Firefox and Chrome already do, anyway (it's not true, CLI-style tab-completion; just passable). If you want word completion too and you're on a recent version of Firefox, use this addon. Or, if you're using an older Firefox version, you could try this one.


Here ended my quest. Let your chronicles and criticisms be outlined in the comments below.

Friday, July 29, 2011

Ack!

I'm sure you've used grep at some point in your life as a programmer to search for that piece of code in your code base. I was using it myself — until today. It turns out there's a better alternative to all that time spent trying to get the perfect grep alias so it works as it should: searching recursively, ignoring the .git/.svn directory, ignoring backup files (foo~/#foo#), printing out the file names and line numbers, grouping matches by file name. You've got to listen when Jacob Kaplan-Moss, creator of Django, has this to say about it:
"Whoa, this is *so* much better than grep it's not even funny."
It's called ack. Without further ado, you can install it on your Ubuntu system using sudo apt-get install ack-grep (for other distros, check out the ack homepage). Then use it as: ack-grep search_term. It's really that simple (although the default colours are a little irritating). You can see what files/directories it ignores by default using ack-grep --help. Here's a screenshot for the impatient reader:


'nuff said. I'm gonna go explore the man page.

Thursday, July 28, 2011

The alternative (and very useful) bash prompt

Almost all Linux users have used bash at some point, but how many of them know how to customize it to their liking? Here is a screenshot of one custom bash prompt I've created, which I find much better than the default (it took me a few hours to reach this point, owing mainly to my poor skills in bash scripting):

Click the image to view it properly.
So, other than showing useful stuff related to git repositories, it gives me a full line to type my command, and separates each command with a hyphenated separator and a newline, so I can see it clearly as I scroll upwards. The corresponding code snippet is (put it at the end of your ~/.bashrc if you wish to use it):

PS1='\[\033[01;34m\]\$\[\033[00m\] '

parse_git_dirty () {
    [[ $(__git_ps1) != "" ]] && [[ $(git status 2> /dev/null | tail -n1) != "nothing to commit (working directory clean)" ]] && echo "*"
}

print_pre_prompt () {
    # the first part of the prompt, i.e., the user's name
    local PS1U=$USER

    # the second part of the prompt, i.e., the present working directory
    local PS1WD="${PWD}"
    if [[ $PS1WD = "$HOME" ]]; then PS1WD=\~; fi
    if [[ $PS1WD = *"$HOME"/* ]]; then PS1WD=\~${PS1WD#$HOME}; fi

    # the fourth and last part of the prompt, i.e., the git stuff
    local git_dirty=$(parse_git_dirty)
    local git_dirty_color="\033[1;31m"
    local git_clean_color="\033[1;30m"
    if [[ $git_dirty = "*" ]]; then local git_color=$git_dirty_color; else local git_color=$git_clean_color; fi
    local PS1R=`__git_ps1 "%s"`$git_dirty

    # the third part of the prompt, i.e., the separator consisting of hyphens
    local PS1SEP=""
    while [ $(echo ${#PS1U}+${#PS1SEP}+${#PS1WD}+${#PS1R}+3 | bc) -lt $COLUMNS ]; do
        PS1SEP=$PS1SEP-
    done

    # putting it all together
    printf "\n\033[1;32m%s \033[1;34m%s \033[0;36m%s $git_color%s" "$PS1U" "$PS1WD" "$PS1SEP" "$PS1R"
}

PROMPT_COMMAND=print_pre_prompt

Rather than explaining the code (which I'm not very clear about myself), I'll point you to some links which helped me along the way:
1. https://wiki.archlinux.org/index.php/Color_Bash_Prompt
2. http://superuser.com/questions/187455/right-align-part-of-prompt
3. https://gist.github.com/306785/4da3e39fc012475140fdf33ef0f6bc0a6e7c04a5

-----
Liked this article? How about following me or Rajat on Twitter?

Friday, July 15, 2011

5 Cool Things in Git

Thing #1: Pretty coloured output
Command: git config --global color.ui auto
This will make all output colour-coded. So now git diff and git status will show up in red-green awesomeness (not that that's a great help to me, but it will be to you, undoubtedly).

Thing #2: Aliases
Command: git config --global alias.shortcut command
The beloved time-savers. Here are some of my aliases:
git config --global alias.l log
git config --global alias.s status
git config --global alias.co checkout

Thing #3: Awesome-looking git log
Command: git log --pretty=oneline --graph
Combine that with Thing #1, and you've got an ASCII-art style, rainbow-coloured, git log. And, if you have a lot of free time, or if you're just way too interested in git, you can read man git-log. There's ton of options in there.

Thing #4: A bucket-load of ways to refer to commits
Ok, first of all, branches are not branches. Yes, you read that right. A "branch" is actually just a reference to the latest commit in it. A commit's lineage defines its history, and so arises the idea of a "branch".

You can refer to commits in many ways. Let's agree on some common notations before I explain that. Every commit can be referred to by a commit pointer. The commit pointer may be followed by any number of modifiers, which will together constitute another commit pointer. A commit pointer without any modifiers can have any one of the following forms:
    a. the SHA key of the commit (the first 6-7 characters should suffice)
    b. HEAD (refers to the latest commit in the current branch)
    c. branch_name (refers to the latest commit in the branch specifed by branch_name)
    d. tag_name (refers to the commit pointed to by the tag called tag_name)

A modifier, on the other hand, can be either one of the following:
    a. ~n (refers to the nth ancestor of the commit pointed to by the commit pointer preceding it)
    b. ^ (refers to the parent of the commit pointed to by the commit pointer preceding it. If a commit has multiple parents, you can use ^n to refer to the nth one)

We all learn by example, so let's consider a possible scenario. Say we have a git repository with 2 branches, master and testing. Say the master branch has 2 commits with SHA keys 0af4780... (=HEAD) and 326399d..., and say the testing branch has 3 commits. Say the current branch is testing. Here are some examples in the context of that scenario:
    a. 0af4780... = HEAD = self explanatory
    b. HEAD~1 = commit 326399d...
    c. testing^^ = the oldest commit in the testing branch

Thing #5: Alternate usages
In commands like git diff, which make sense for both commits and files, the commit pointers passed to them can be followed by :file_path, if you wish to compare 2 past versions of a file instead of 2 whole commits. For example, say we have a file called blah.c in the lib/ directory in our repository (which happens to be huge), and we want to see what changes were made to it between the second last and fourth last commits. We could do something like:

git diff HEAD~1:lib/blah.c HEAD~3:lib/blah.c

Another interesting thing is commit ranges, which you can pass to commands like git log to see what has changed in that range of commits. They have the form commit_pointer1..commit_pointer2.

If this got you interested, you really should read this amazing PDF on the internals of git.

----
If you liked this article, why not extend your appreciation by following me or Rajat on Twitter?

Thursday, July 14, 2011

Sed - reducing effort since '74

Sed, short for 'stream editor', is a (pretty awesome) utility created by  Lee E. McMahon of Bell Labs. I would like to take a detour here and mention that I am in real awe of Bell labs. They have given us so much, starting from C and C++ to Unix itself. Another useful utility (which I should cover here soon) made by them is cscope.

Now, coming back to 'sed', it saved me quite some effort today while working with Kate. Well, frankly, the time it took me to learn some basic tricks and perfect the command might have taken a little longer than it would have taken me to manually make the change, but I won't add that time. Why ? Because the time invested would save me plenty more in the future, whereas doing the task manually wouldn't save me anything in the future.

So what was this task ? As I have explained here, I am working on the modeline variable editor in Kate. The widget that my mentor and I had created, to be used in place of the 'QLineEdit' , had some help text. I had forgotten to wrap the text in i18n() wrappers.

The initial text was something like this :-
item-> setHelpText(" Help text goes here ") ;

Now had there been just two or three of such occurrences, I wouldn't have thought about using sed; but there were a lot more (I didn't check then, but as it turns out, about 40 changes were made using sed). Anyways, I needed the text to be modified to :-

item->setHelpText(i18n(" Help text goes here"));

With sed, that wasn't too hard.

sed 's:setHelpText(\"\([a-zA-Z0-9.() ]*\)\"):setHelpText(i18n(\"\1\")):g' <old >new

Just one line and all the changes were made. And I had with me another patch to offer to the community ;-).

Combined with Regular Expression (Regex), sed has a lot to offer.

Suggested links :

For more information on Internalization (i18n), check out the Wikipedia page.
For a tutorial on Sed, I would suggest this.
Online manual for Sed is available here.