Tuesday, May 29, 2007

Moving around in VIM : buggy terminal edition

Buggy terminals are one of the most frustrating things one can encounter when trying to edit a file on remote site.

Big boys like Emacs are out of question immediately, since they depend on <Esc> sequences too much. And the sequences are what is used to represent functional, arrow and motion keys in Unix world. Often even Backspace and Delete fall victims of buggy terminals.

Though unpractical, it is often required to edit at least single file on remote site - to tell it proper terminal configuration - before anything else might be done.

VIM is for rescue. VIM (ans as its ancestor VI) were designed in times when there were no such keys available at all. It might be painful to work that way, yet quick edit is quite OK.

All the command are to be used in normal (non-insert) mode. Some of them as noted change vim's mode. To go back to normal mode, press Ctrl-[ or Esc.

u Most important command here: undo.
/ Search forward - the most effective way to move around in vim.
? Search backward.
:NNN<Enter> Go to line number NNN.
Pressing 'x' would delete character under cursor. With repetition counter, can delete more than single character in a go: e.g. '20x' would delete 20 characters starting with current one (and analogous to pressing 'x' twenty times).

Backspace key (delete backward) is 'hx'. Delete key (delete forward) is 'x'.

Pressing 'r' would replace character under cursor with character pressed after 'r'. e.g., 'ra' would replace character under cursor with character 'a'.
'f' stands for Find and moves cursor forward to next occurrence of given character.

e.g., 'fa' would move cursor to next character 'a' in current line.

Variation: 'F' (capital f) searches backwards.

Move cursor to the end of current line.
Move cursor to the beginning of current line.
h, j, k, l Move cursor to left, down, up and right correspondently. Very useful with repetition counter: e.g. '10h' (typed 10 followed by 'h') would move cursor ten characters left.
w, e Move cursor over words forward. 'w' stops at word beginnings. 'e' stops at word ends. 'W' and 'E' are the same, but they move backwards.
d Most dangerous command here. Or rather 'd' isn't command, but a modifier. Pressing 'd' before any motion command would delete characters/lines traveled.
e.g. 'df<Space>' would delete all characters from one under cursor till next Space (Space itself included).
e.g. 'dl' would delete current character and one next to it. Analogous to 'xx' or '2x'.
e.g. 'dj' would delete current line and one after it. Analogous to 'dddd' or '2dd'.
e.g. 'd$' would delete characters starting from current one to the end of line. Analogous to 'D'.
e.g. 'd/xxx' would delete characters starting with current one up to next entry of 'xxx' string in text (not including - search stops before - string itself).
dd Delete current line.
c Change operator. Analogous to 'd', it deletes according to given motion, but after deletion switches to insert mode.
cc Clears current line and switches to insert mode.
C Clears line starting from character under cursor and switches to insert mode.
i Pressing 'i' would put vim into edit (insert) mode.
R Pressing 'R' (Shift-r) would put vim into replace mode (or in PC editors called "overwrite" mode). Unlike insert mode, where typing inserts characters, in replace mode any typed character would replace character under cursor.
A Pressing 'A' (Shift-a) in command mode would make vi to go into insert mode and move cursor to end of line after last symbol.

That's about it. Move around with searches ('/' and 'f'), delete with 'x' or 'd'. Insert mode - 'i'. Replace mode - 'R'. Ctrl-[ - to go back to normal mode. Something went wrong - undo with 'u'. And of course ':w' to write changes and ':q' to exit.

Edit1. He-he. As soon as I have written that I ended up working under the ever-sucking Solaris with its original vi whos author needs to be shot dead 666 times to get him out of genetic pool - to make sure that no such thing would ever appear.

Another important point. Adding something at the end of something. This is next to impossible in original vi. With little bit of tinkering I came to conclusion that only way to do that is through substitution.

Add at the end of the line with :s/$/ missing word./<Enter> (See also 'A')

Add new line with :s/$/<^V><^M>/<Enter> (<^V><^M> == Ctrl-V Ctrl-M == ^V says to terminal to insert next typed symbol (^M) verbatim. And ^M is "new line" symbol, which understood by vi as your intention to add a new line. (Enter under Unices is "carriage return")). Doing that on last line in file would add a new line after last.

Adding new line before first apparently can be done with :s/^/<^V><^M>/<Enter> which is analoguos to previous trick - but instead of '$' (end of line) we substitute '^' (beginning of line).

Last words: Solaris muzz-die. It looks to me like my beloved Belarus which is often called a "reservation of communism." Same way, Solaris can be called a "reservation of Unix" - of all those mistakes made 30 years ago, fixed everywhere else, with only Sun keeping them in place. For backward compatibility I gather.

Edit2. Added '^' - moving to beginning of line.

Edit3. Added 'A' - inserting text at end of line.

VIM Crash Course. Study #2.

OK. Recently, we have replaced a word in file. Now let's replace particular line in file.

Example presumes you have open file you need to replace a line in.

Line number is known and is 1234. (Or you can use '/' search operator from previous post to find the line.)

1. :1234<Return>
2. C
3. <new text for given line>
4. <Esc>
5. :wq


1. Colon ':' allows us not only to input commands, but also to jump to particular line given by its number. Press colon, type line number and hit Enter. Cursor will be put on the first character in line number 1234. (Or last line, if number is too big.)

2. 'C' would 'C'lear (delete all characters) starting from current one and put vim into insert mode. If cursor stands on first character in line - as it should after step 1 - it would clear whole line.

3. Now type whatever you need to type in the line. After step 2 we are in insert mode - to insert text. Any text you would type would appear verbatim.

4. <Esc> will put vim back into normal mode.

5. ':wq' will write file and end editing session.


VIM Crash Course. Study #1.

VIM (or equally VI) crash course #1: Correcting misspelled word AAA to BBB in a file "file-name".

Most frustrating part in starting with vim (or vi) is that one often is forced to work with vim while there is no other editor on the system. That happens alot when working on some embedded system or antique unique boxes, which do have slimest and featureless editor installed. E.g. busybox or original vi from Bill Joy. To be frank, I'm also frustrated with the both things. But still they are cheap and thus are widespread. Making some use of them is only natural.

Here we go.

Launching: vi file-name

Commands needs to be typed as they a written here (without line numbers of course).

1. /AAA<Return>

2. n

3. :s/AAA/BBB/<Return>

4. :wq<Return>


1. '/' is search operator. Whatever you would type after it is search string. Search string is terminated (and search started) by pressing Enter. That will move cursor to place of first match. Or complain that string isn't found.

2. Optional step, if found match isn't what you want to replace. 'n' repeats last search and moves cursor to next match in file. Press 'n' repeatedly, until you have found what you want to replace. With 'n' you can go over all matches in file and after hitting end of file, vim would restart search from the beginning of file.

3. ':' colon is start of many commands in vim. 's' is short for "substitute" command. Its syntax is simple. 's' is followed by '/'. After first slash goes string you want to be replaced (or substituted). It is terminated by second '/'. After second slash goes replacement string. It is also optionally terminated by '/'. Pressing Enter would execute the substitution operation on current line, replacing first entry of AAA with BBB.

4. ':wq' is vim's command to "write and quit". That would write modified file and leave vim.

Notice, that the editing was done without switching into vi's infamous insert mode. The example doesn't use any kind of complex keys and guaranteed to work on all terminals.

Friday, May 25, 2007

[link] Making Vim easy

Tips: Making Vim easy by Joe 'Zonker' Brockmeier published on Linux.com

Though I can not recommend using VIM in 'easy' mode (:help easy). You would not learn VIM, nor would make it VIM really easier to you - most commands still require some level of knowledge of real vim modes.

P.S. Somehow, most important shortcut Ctrl-L was left out of this introduction. Pressing Ctrl-L allows you to input a normal or ex command to vim. I guess that idea of easy vim is to not bother user with the modes and commands. Yet main power of vim - and the whole point of using it - is in the commands. Without Ctrl-L, vim in easy mode is pretty lame imitation of M$Windows infamous Notepad.

Wednesday, May 23, 2007

[off topic] Using GNU GDB for assembler

GNU GDB refuses to be good debugger for assembler. But still with some pain it is possible to get out of it something.

But generally, I would advise learning to debug your programs right in your brains while you typing them. Think that's too complicated? Then read on. By end of the post you might be not that sure about that.

0. Starting program. 'break *&_start' to set breakpoint at program entry point. 'run' as usual to actually load program.

1. 'list' - forget about it. It will never ever show assembler. 'set language asm'? - HA-HA!! Naively thought you can outsmart GDB?? How naive of you. 'list' will not work. Period.

2. But how to see the code then?? Answer: 'disass $pc $pc+36'. $pc is content of register 'pc', what under PowerPC is 'program counter' - pointer to next instruction. (Probably pc is portable, have no clue.) Second parameter - $pc+36 - tells to disassembler to stop at address +36 (thus disassembling next 8 instructions, stopping at 9th).

3. 'step' and 'next' will not work. I would have paid a premium to disable them altogether when I do use gdb for assembler, because they are screwing execution context in most unexpected way. You fingers slipped and typed 'n<Return>'? Restart everything again - that way it's faster.

4. 'stepi' and 'nexti' are used to walk instruction-wise.

5. Breakpoints: 'break *0xdeadbeef', where 0xXXXXXXXX is address where you want to set a break point.

Overall, you would hit snag at every corner. Yet, if you are courageous enough, my experience laid out here might serve as starting point.

P.S. Interesting article aboug GDB from IBM's developerWorks.

Hello World in VIM

OK, I feel myself empowered enough to actually write a "Hello World!" in VIM.

function Hello(wrld)
let hw = "Hello ".a:wrld."!"
echo hw

call Hello("World")

Save that to hw.vim and run this:

$ vim -u hw.vim -c ":q"

It should print "Hello World!" and wait for you to press Enter before quiting. (-u hw.vim tells vim to load hw.vim as substitute of ~/.vimrc and -c ":q" tells vim after loading ~/.vimrc (which is substituted with hw.vim) to execute ":q" to terminate.)

Dissection. :function & :endfunction are start and end of function definition. :let creates new variable hw which contains string "Hello " concatenated with a:wrld (function argument) and "!". (:help :let describes operator '.' which is string concatenation operator.) :echo is used to print the value contained in variable hw to screen. If we pass as parameter "World" to the function, hw would contain "Hello World!" string.

N.B. Colon ':' before command names, when is inside of script file - e.g. ~/.vimrc - is optional and used by me here merely to highlight that commands are all from normal mode. Also when you say to VIM :help :let, colon before let would tell vim that you are interested precisely in let command of normal mode - not something else. (Just like in :help 'ts', where single quotes tells vim to look for ts only amongst settable options.)

P.S. Adding that function to you ~/.vimrc with call Hello($LOGNAME) would greet you with silly "Hello <Your-Login-Name-Here>!" message every time you would launch vim.

Simple string function in VIM

Recently I have posted a script for automatically picking up a ctags' tags file, if one found in obvious locations.

New thing for me in the script is definition and calling of a user function. User functions in VIM are documented under :help user-functions topic. (Standard functions are documented under :help functions. Pressing ^] on function name would bring you to detailed description of corresponding function.)

So, from POV of vimrc, function definition is simply set of commands, open with 'function Name([params])' line and closed with 'endfunction' line. Name must start with capital letter. Parameters are given as comma separated list - types are automagically deduced by VIM itself. Normally, most parameters would be strings and numbers - and VIM apparently can convert them silently one into another w/o bothering user about that (a-la Perl or JavaScript).

Inside of function one puts normal VIM commands (:help :normal). To access parameters, their names must be prefixed with 'a:' (:help internal-variables has nice list of all prefixes.)

To generate a value, function must use inside a return command (:help :return). Function will stop and callee would receive value given to return.

Function found in .vimrc become global and available everywhere. Additionally one can put function definitions in external file and then call :source to make them available (:help :source).

To call a function, one can use :call command:

:call FindCtagsHere('/home/ifilipau/trunk/src/','/')

or simple :echo (which also prints function return value):

:echo FindCtagsHere('/home/ifilipau/trunk/src/','/')

or to save the value in variable one can use :let:

:let x=FindCtagsHere('/home/ifilipau/trunk/src/','/')

because (compared to simpler :set used for options, :let (used for generic variable manipulation) evaluates right side of assignment operator).

Tuesday, May 22, 2007

making vim and ctags real friends, rev2

Time have to review my little script from awhile ago to automatically update &tags option with tag file if one is found in directory under current one.

What bothered me that if I was opening file by full path, 'tags' option wasn't updated - since vim would be launched in $HOME, instead of directory were file itself resided. Thus script was failing to do anything.

But today, I decided to waste whooooping 15 minutes to actually read VIM documentation and update my script. Now I use user functions to do the dirty job.

Here we go.

" 1
function FindCtagsHere(dir, dir_sep)
let tag_dir = a:dir
let sep = a:dir_sep
while !filereadable(tag_dir.sep."tags") && tag_dir!=$HOME && stridx(tag_dir, sep)>=0
let tag_dir = substitute(tag_dir, sep.'[^'.sep.']\+$', "", "")
if filereadable(tag_dir.sep."tags")
return tag_dir.sep."tags"
return ''

" 2
au BufReadPost *.cpp let b:current_file_tags=FindCtagsHere(expand('%:p:h'),'/')
au BufReadPost *.h let b:current_file_tags=FindCtagsHere(expand('%:p:h'),'/')

" 3
au BufReadPost * if exists("b:current_file_tags") &&
\ filereadable(b:current_file_tags) &&
\ stridx(&tags, b:current_file_tags)<0 | let &tags.=','.b:current_file_tags | endif

" 4
let x = FindCtagsHere(getcwd(), '/')
if filereadable(x)
let &tags .= ','.x

That's it. (Blogger of course tries to eat '<' & '&' - so watch for syntax errors.)

First I declare function. The function takes two parameters - directory to start search from and directory separator ('\\' for Windows (not tested!) and '/' for sane rest of the world). It returns string containing name of found tag file. Content was more or less copy-pasted from previous post.

Second with 'au BufReadPost *.cpp/*.h' I add triggers to search for tags upon file loading. The tag file name is stored in buffer local variable 'b:current_file_tags'.

Third with 'au BufReadPost *' I check to see if there was tag file found by earlier auto commands and if so I add the tag file into global option 'tags' (which is comma separated list of all tag files for VIM to check.)

Fourth, lastly, I do restore original functionality of the script: if editor was launched even without files open, we search for tags starting from current directory. If tags were found we add them to 'tags' option. As before, launching VIM w/o files would allow to use ':tag' to browse code.

Now we search for tags every time relevant file is open. This is added overhead, but I haven't noticed any visible slowdown from VIM. And my tags file is 300k lines & ~30MB big.

[off topic] Driving Principles of Programming: Ignorance

Well, I have already talked a bit about laziness and egoism, but now time have come to mention - after all - the last (as I count them) third principle: ignorance.

I was pitched to write this after whole week of meetings with two engineers, whose many questions may be rephrased as "what if sky falls down?" After several days I was ready to scream "As heck I care!!!" (and this is really polite form of what I actually wanted to say.)

I have a function to do task A. WTF shall I care here about B, C and D??? Some people just do not get it.

IGNORANCE. The ignorance which is bliss. As programming concerned, ignorance allows programmer to concentrate on particular task, ignoring ever-changing environment his program is ought to work in. Otherwise, if one would try to track all new requirements and developments, his/her attention would be diverted from original task. End result? Task would be always in half finished state.

Egoism and ignorance are very similar. Egoism tells us to ignore other's opinion (a.k.a. critics). Ignorance tells us to ignore other's problems (a.k.a. new requirements).

Like with other two principles, all is good in moderation. Pushing any of the principles to the limit of course goes against the laziness principle. And being too lazy would mean that you are not egoistic. And being too egoistic would mean that you can't ignore yourself. You see now? That's vicious circle of software development. ;)

P.S. How to use ignorance in practice. Ignorance normally results in task-oriented applications. You do not try to do everything - since you ignore everything - but concentrate on precisely the task/job you want to accomplish. Often such applications might look as hairy agglutinations of functions - and they are really functional and do what you want from them. Though lazy guys like I am would find them short on ergonomics ^_^

Sunday, May 20, 2007

[off topic] Perl v. Python: Python Sucks!

Results of my non-scientific research: "Python sucks". Or rather Python performance does suck big time. Or rather lack of performance.

Simple test.

$ cat a.py
print "Hello world!"
$ time python a.py
Hello world!

real 0m0.039s
user 0m0.032s
sys 0m0.008s

Compare to this:
$ cat a.pl
print "Hello World!\n";
$ time perl a.pl
Hello World!

real 0m0.008s
user 0m0.004s
sys 0m0.004s

or this:
$ cat a.c
#include <stdio.h>
int main()
printf("Hello World!\n");
return 0;
$ make a && time ./a
gcc a.c -o a
Hello World!

real 0m0.003s
user 0m0.000s
sys 0m0.000s

I mean, what Python does those 40ms? Compared to Perl's 4ms? Or C's ZILCHms?

Absolute values seem to be small enough to be disregarded. But the same cliche can be seen in bigger applications.

For example Gentoo's tool chain - emerge - many Gentoo users are familiar with. Its DARN slow. Even compared to Debian system - apt-get - which does so much more and often labeled by jealous RedHat folks "amateurish" - works magnitudes faster (and even on junk hardware).

Or SCons. SCons take 1.5 seconds to just start up and print help message. It's Perl's sibling cons/pcons in the time manages to compile several sources.

Wednesday, May 16, 2007

[link] Vi Lovers Home Page

Vi Lovers Home Page - terrific resource for everybody. Piles of links - tutorials and FAQs.

Must read.

[link] Gentoo's VI guide

Learning vi

Pretty good for beginners - just like about any other document I have seen from Gentoo.

Friday, May 04, 2007

[off topic] Divide and Conquer (DaC) vs. Model-View-Controller (MVC)

Thinking about team work organization, I was constantly humming "Divide and Conquer" (DaC) principle. That's pretty obvious since it is guiding principle how big teams are taking on large problems - by splitting them into many smaller ones. (Small enough for single person to solve.)

Turning back to work, my eyes have met again with several of my constructs loosely based on MVC model.

Sudden enlightenment: MVC is another application of more general DaC.

How DaC works. A (simple) problem is split into three (primitive) problems:

  • 2 smaller problems of original bigger problem and (one more piece everybody forgets about)

  • problem of integration the two smaller solutions into solution for bigger problem.

The third part - often forgotten line connecting two blocks - is very important and often play crucial development organization role. Recurrently applying DaC we can slice problems until they reach manageable size.

How MVC works. A problem is split into three problems:

  • stateful part containing all data/state (model),

  • stateless part containing all utilities needed to work with model (view) and

  • controller part which best exemplified by UI to manage the data of model using the utility of view.

MVC generally drawn as three blocks, with model being independent, view depending solely on model and controller depending on both model and view.

Similarities are obvious.

On high level, MVC's controller is the interface connecting the two pieces of problem - model (state) and view (functions). (Bigger problem split into two smaller problems - model and view - with controller playing role of integration problem.)

On lower level, MVC looks like DaC splitting original problem into three smaller problems and plus three more integration problems (view to model, controller to view and to model).

Tuesday, May 01, 2007

Clever tricks from VIM's Bram Moolenaar

The page where I found tip for the annoying PuTTY problem, also had great tip:

:help usr_12.txt
There you will find clever tricks you can do with VIM to accomplish simple yet sometimes needed tasks. And overall page has lots of simple hints. Very good reading.

VIM vs. PuTTY keypad

The problem was annoying me for very long time: under PuTTY, applications (e.g. less, vim) using tek window (alternative terminal screen) had totally screwed keypad. Solution was found here.
Apparently, PuTTY tries to do something smart. And fails.

  1. Change function key/keypad mapping from standard "ESC[n~" to "Linux".
  2. Disable application keypad mode
Apparently only second is required, but first is also recommended.
The options, as usual to all options of Windows applications, change their location in settings dialog periodically so I cannot give their precise locations. Go through PuTTY setting and you will find them.