Vim is referred to as a “modal” editor because it operates in different modes: the normal mode, insert mode, replace mode, command line mode and visual mode. Understanding how the editor works under these different modes is usually the biggest hurdle that beginners have when learning Vim.
This is the default mode. In this mode all the keystrokes are interpreted as commands instead of characters appended to the file you are editing. Most navigation (movement) and manipulation commands are executed while at this mode.
This is the mode that you use when you want to insert new characters in a file (or erase as well with Backspace
).
While on insert mode, keystrokes are interpreted directly as characters to be added or removed to the file.
You enter insert mode when you are in comand mode and press one of the following keys (there are many more, but these are the most common):
i
to insert text before the current cursor positionI
to insert text at the beginning of the current linea
to append text after the current cursor positionA
to append text at the end of the current lineo
to open a new line before the current line to add textO
to open a new line after the current line to add text.C
to change to the end of line.To exit out of insert mode and go back into command mode type Esc
.
In this mode you can overwrite text. You enter this mode by pressing R
.
This mode allows you to carry out commands based on selected text.
Once you are in one of the visual modes, select text using the arrow keys or hjkl
and then type a command. The Command will be applied to the highlighted text.
Examples of commands that can be applied on visual mode are deleting text, indenting lines, changing case.
You exit any of the visual modes by typing Esc
.
The first variation of this mode (known simply as Visual mode) allows you to create a contiguous selection of characters by using the arrow keys. You enter this mode by typing v
There is also Visual line mode, which lets you select line by line using arrow keys.
You enter this mode by using V
(that is capital V).
Finally, there is the Visual Block mode, which lets you select rectangular blocks of text.
You enter this mode by typing Ctrl+v
(Note: this won’t work while on a terminal session running on Windows system since the Operating System has that key combination mapped to the global paste command).
While on command line mode, keystrokes are buffered as characters of a command that will be executed after you press Enter
.
To enter this mode press :
(the bottom line changes to a colon prompt, where you can type commands).
An example of a command that can be executed while at this mode is the quit command, which exits Vim.
To issue this command, enter :q
and then press Enter
.
In most Vim reference materials, any comand that is supposed to run using command line mode is specified by prepending the colon.
If you have have made changes to a file that have not been saved, Vim will kindly let you know with this message:
~
~
E37: No write since last change (add ! to override)
Press ENTER or type command to continue
To exit without saving, then use the :q!
command. If you want to write and exit instead, use :wq
If you want to just save the current file you use :w
. If you want to save the file with a different name, then you use :w filename
.
You can also enter command line mode in search mode, by pressing /
while in normal mode (or ?
to search backwards).
In this mode, you can perform searches by entering a word and then pressing Enter
.
You can then cycle through the instances of the word by pressing n
to search forward or N
to search backward.
Vim provides lots of options to move the cursor within a file, with the drawback that these are available while on command mode. If you are on insert mode you can still use the arrow keys on your keyboard, and the PageUp
, PageDown
, Home
and End
keys.
The following are the most common commands available to move the cursor:
h
moves the cursor one character to the left.j
moves the cursor down one line.k
moves the cursor up one line.l
moves the cursor one character to the right.0
moves the cursor to the beginning of the line.^
moves the cursor to the first non-blank character of the line.$
moves the cursor to the end of the line.w
move forward one word.b
move backward one word.ta
move to the character before the next instance of the character “a”. Works with other characters as well.fa
move to the next instance of the character “a”. Works with other characters as well.G
move to the end of the file.123G
move to line 123gg
move to the beginning of the file.Ctrl f
move one page down (forward)Ctrl b
move one page up (backward)`.
move to the last edit.One very useful feature while editing source code, is to have line numbers displayed.
To turn these one, use the :set number
(or the short version set nu
) command.
If you have them on and want to turn them off, you need :set nonumber
.
Once you know which line number you want to move the cursor, you can move to any line by entering it in command line mode.
For example, suppose you want to move the cursor to line 123, then you use :123
(which is equivalent to 123G
)
As mentioned before, you add text by entering insert mode. Here are some of the most basic commands to edit:
x
delete character under cursordw
delete from cursor to the end of the worddb
delete from the cursor to the beginning of the word (delete backward)dd
delete lined$
delete from the cursor to the end of the lined0
delete from the cursor to the beginning of t he lined^
delete from the cursor to the first non-blank character of the line.di'
delete all text in between quotesdi"
delete all text in between double quotesdi}
delete all text in between bracesdi]
delete all text in between bracketsdit
delete all text in between tags (useful for HTML/XML)>
indent the current line<
de-indent the current liney
yank (copy) the current selection (visual mode)d
cut selection (visual mode)Y
yank a whole linep
paste after the cursorP
paste before the cursorYou can also perform copying and cutting in normal mode:
yy
yank the whole line, equivalent to Y
dd
cut the whole line.y$
yank from the cursor to the end of the line.d$
cut from the cursor to the end of the line.yw
yank the current worddw
cut the current wordu
undoCtrl+r
redo.
repeat the previous command=
indentWe saw before that you can perform searches while on command line mode by pressing /
.
While on normal mode, you can search for the word that is under the cursor with the combination g*
.
As before, you can cycle through the instances of the word with n
or N
(backwards)
To replace text on the current line, you use the syntax :[range]s/searchstring/replacestring/[mod]
.
For example, to replace the word “apple” by “orange” on the current line you would type this combination :s/apple/orange
The range defaults to the current line as you just saw.
You can specify a global search and replace by using %
as range.
For example :%s/apple/orange
.
You can also specify a range of lines for example :8,20s/apple/orange
will apply from line 8 to line 10.
By default, the replacement is only done to the first match in a given line.
You can replace all the matches in a line by using the g
modifier.
For example :s/apple/orange/g
.
If you want to ask for conformation, intead use gc
instead.
You can toggle case sensitivity by entering the :set ignorecase
or :set noignorecase
commands.
The ~/.vimrc
file allows you to set preferences for vim.
The following example show some basic preferences (yes, the author prefers spaces over tabs) (comment lines are preceded with "
)
" Enable determine the file type and indentation
filetype plugin on
filetype indent on
" Enable syntax on
syntax on
" make stabs show as 4 characters
set tabstop=4
" indent as 4 characters
set shiftwidth=4
" use spaces instead of tabs
set expandtab
There are literally hundreds of plugins that can be used to enhance Vim’s functionality. Plugins for code completion, syntax checking, code navigation, refactoring are available for most programming languages.
gdb
, the GNU Debugger, is an application that lets you run a program while having control of the execution of the program instructions and the ability to perform inspection of the memory used by the program.
More specifically, a debugger lets you:
In this Lab we are going to perform a debugging session with C++ simple program that (rather unsuccessufully) implements the insertion sort algorithm.
Insertion sort is the most basic algorithm used to sort a list of elements. The following is a description of how the algorithm works.
Imagine a list of n numbers arranged horizontally, and we want it to sort it in ascending order, from left to right.
A compiled executable and functional version of the program is available at ~jmora/lab/insort
. You can try it to see how the program is supposed to work:
[user@blue lab05]$ ~jmora/lab05/insort 13 17 2 0 3 1 8 23 2 7 0 1 2 2 3 7 8 13 17 23
In order to be able to debug a program, it needs to be compiled with debugging information. If this prerequisite is not fulfilled, it is just not possible to use gdb
. In gcc
and g++
you enable this by tuning the -g
flag. Using this option saves the symbol table (the list of memory addresses that correspond to the variables and lines of code of your program) into the compiled executable.
To do this, run this command
[user@blue lab05]$ g++ -std=c++11 -g insertion_sort.cpp -o insort
The following table summarizes the most common gdb commands
Command | Funciton |
---|---|
run |
Starts the execution of a program. |
print |
Prints the value of a variable |
break |
Sets a breakpoint |
clear |
Clears a breakpoint |
watch |
Watches for changes in a variable |
condition |
Adds a condition to a breakpoint |
next |
Executes the next statement |
step |
Executes the next statement, but stepping into a function call |
In this procedure we are going to debug a buggy insertion sort program that you will be given. The source code contains three problems, and we are going to follow an iterative approach to find them.
The buggy source code is presented next for convenience, you will be provided with a link to download.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | #include <iostream>
#include <stdlib.h>
#include <string>
using namespace std;
/**
Shifts the elements on an array to the left
@param values : the array
@param begin : the index of the first item to shift left
@param end : the index of the last item to shift left
Note that the element at position end+1 will be overwritten
The element at position begin-1 will retain its value
*/
void shift(int values[], int begin, int end)
{
for ( int i = end; i > begin; i--)
{
values[i] = values[i-1];
}
}
/**
Sorts an array in place in ascending order
@param values : the array to sort
@param length : the length of the array to be sorted.
*/
void sort(int values[], int length)
{
for (int j = 0; j < length; j++)
{
if ( j = 0 ) continue; // skip the first item
for (int k = 0; k < j; k++)
{
if (values[k] <= values[j])
{
int val = values[j];
shift(values, j, k);
values[k] = val;
}
}
}
}
int main( int argc, char *argv[])
{
if (argc < 3)
{
cerr << "You must provide at least two arguments" << endl;
exit(1);
}
int values[argc-1];
// parse arguments
for (int i = 1; i < argc; i++)
{
int val = stoi(argv[i]);
values[i-1]=val;
}
// call sort
sort(values, argc-1);
// print result
for ( int j = 0 ; j < argc-1; j++ )
{
cout << values[j] << " ";
}
cout << endl;
return 0;
}
|
The buggy C++ source file that is supposed to implement this algorithm is available at insertion_sort.cpp
. Create a directory called lab05
within your home directory, and download (remember wget
?) the aforementioned file into it.
Compile the program:
[user@blue lab05]$ g++ -std=c++17 -g insertion_sort.cpp -o insort
The previous command will generate an executable called insort
. Run the executable:
[user@blue lab05]$ ./insort 3 4
It should be obvious that there is a problem in the source file, since the program does not complete and does not print any output. We are going to use gdb
to fix the buggy program. Type CTRL+c
to terminate the buggy program.
Start gdb
by typing the following command, you should see the following output:
[user@blue lab05]$ gdb insort GNU gdb (GDB) Fedora 8.0.1-36.fc27 Copyright (C) 2017 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-redhat-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from insort...done. (gdb)
At this point the program has not been executed, to start the program with the arguments “6 2 5” enter:
(gdb) run 6 2 5 Starting program: /home/student/user/lab05/insort 6 2 5
The program execution will start after the previous command, and again, it will not terminate or produce any output. The difference is that now we can suspend the program execution. To do this, press the CTRL+c
combination, and the program will suspend and will present you again with the (gdb)
prompt. Your screen should look something like the following listing where gdb
is letting us know that the program’s execution stopped at line number 31 of insertion_sort.cpp
(the exact instruction where the program suspended the execution might be different):
Starting program: /home/student/user/lab05/insort 6 2 5 ^C Program received signal SIGINT, Interrupt. 0x0000000000400e46 in sort (values=0x7fffffffe290, length=3) at insertion_sort.cpp:31 31 for (int j = 0; j < length; j++) (gdb)
Experiment with the next
command of gdb. Run it several times, and notice how the line numbers change every time that you run the next command.
Inspect the value of the variable j
. Does the value of this variable change? What values do you see being assigned to this variable?
At this point you should be able to determine the bug that is causing the program to get stuck in an endless loop. Exit gdb
by typing the quit
command.
Using vim
, edit the source code file to fix the bug (for consistency, do not add new line numbers to your program, or you won’t be able to validate your answers in Moodle).
Recompile your program and run it again with different inputs. For example:
[user@blue lab05]$ ./insort 6 5 2 3 7 4 7 7 7 7 7 4 [user@blue lab05]$ ./insort 6 5 9 3 7 4 9 9 9 7 7 4
This time the program terminates and produces output, albeit an incorrect one. This time we are going to try placing a breakpoint. Execute the gdb command, but this time before running the program place a breakpoint in line 34:
(gdb) break 34 Breakpoint 1 at 0x400da3: file insertion_sort.cpp, line 34. (gdb) run 6 5 2 3 7 4 Starting program: /home/student/user/lab05/insort 6 5 2 3 7 4 Breakpoint 1, sort (values=0x7fffffffe270, length=6) at insertion_sort.cpp:34 34 for (int k = 0; k < j; k++) (gdb)
Inspect the values stored in the array called values
, for example, you can do something like this:
(gdb) print values[0] $2 = 6 (gdb) print values[j] $3 = 5 (gdb) print j $4 = 1
Trigger the execution of the next line, and inspect the values of the array at indices k
and j
.
The next line to be executed should be line 36, which corresponds to if (values[k] <= values[j])
. Based on the details of the insertion sort algorithm, and the values that you observed in the previous step, does it make sense to go inside the block that starts on line 37?
At this point you should have been able to find the second bug on your program. Exit gdb
, make a modification to the source code (hint, it needs to be on line 36) and recompile.
Test your program again, you should see output like this:
[user@blue lab05]$ ./insort 6 5 9 3 7 4 3 3 3 3 4 4 [user@blue lab05]$ ./insort 6 5 2 3 7 4 2 2 2 3 4 4
Again, the output seems to be wrong. Using a similar procedure we used, find the last bug.