Any piece of knowledge I acquire today has a value at that moment proportional to my skill to deal with it. Tomorrow, when I am more skilled, that same knowledge will have higher value.
Showing posts with label Vim. Show all posts
Showing posts with label Vim. Show all posts
Saturday, November 06, 2010
Friday, June 12, 2009
Handling multiple encodings in Vim
Most people edit, load and save files in a single character encoding (i.e. en_US.UTF-8) but for many this is not the case. For me I need to write documents and emails in japanese UTF-8 (ja_JP.UTF-8), Latex in EUC-JP (ja_JP.EUC-JP) and source code comments in Shift-JIS (ja_JP.SJIS).
To handle a certain character encoding there are three things you must consider:
In the prehistoric era editors and terminal could support a single character encoding so you needed a different pair for each encoding you needed. Believe me when I tell you this was not fun at all.
These days most editors and terminals support a large array of character encodings and a lot of free fonts are available that support all the character sets I need. Still it is necessary to reconfigure each part (editor/terminal) or create different profiles for each character encoding you needed to edit (i.e. link) for the editor and the console.
Today I took the time to understand how vim character encoding support works and I found that it has everything I need and a lot more. By carefully manipulating the the fenc, fencs, enc and tenc configuration parameters I can edit any file in any character encoding with little effort. Here are my .vimrc configuration parameters:
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"" Character encoding settings
"" By manipulating this variables it is possible to edit all files in one
"" encoding while using the terminal in a different encoding and writing/reading
"" the file in another encoding. Here we set all three variables to UTF-8.
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Default file encoding for new files
setglobal fenc=utf-8
" Auto detect file encoding when opening a file. To check what file encoding was
" selected run ":set fenc" and if you know the auto detection failed and want to
" force another one run ":edit ++enc=<your_enc>".
set fencs=utf-8,euc-jp,sjis
" Internal encoding used by vim buffers, help and commands
set enc=utf-8
" Terminal encoding used for input and terminal display
" Make sure your terminal is configured with the same encoding.
set tenc=utf-8
In my example above I set everything to UTF-8 that is recommended because converting from other encodings may cause loss of information. The only parameter I change is fenc when I need edit/save a file in a different encoding.
For example if I want to create a new file in euc-jp encoding:
To edit an existing file simply open it and let vim auto detect the encoding using the options available in fencs. To check what encoding was set by vim you can use the command ":set fenc" and it will display the auto detected encoding. If it is not the correct one you can force the encoding by reloading the file using the command ":edit ++enc=euc-jp" replacing "euc-jp" with the encoding you desire.
To handle a certain character encoding there are three things you must consider:
- Your editor character encoding
- Your terminal character encoding
- Your font character encoding support
In the prehistoric era editors and terminal could support a single character encoding so you needed a different pair for each encoding you needed. Believe me when I tell you this was not fun at all.
These days most editors and terminals support a large array of character encodings and a lot of free fonts are available that support all the character sets I need. Still it is necessary to reconfigure each part (editor/terminal) or create different profiles for each character encoding you needed to edit (i.e. link) for the editor and the console.
Today I took the time to understand how vim character encoding support works and I found that it has everything I need and a lot more. By carefully manipulating the the fenc, fencs, enc and tenc configuration parameters I can edit any file in any character encoding with little effort. Here are my .vimrc configuration parameters:
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"" Character encoding settings
"" By manipulating this variables it is possible to edit all files in one
"" encoding while using the terminal in a different encoding and writing/reading
"" the file in another encoding. Here we set all three variables to UTF-8.
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Default file encoding for new files
setglobal fenc=utf-8
" Auto detect file encoding when opening a file. To check what file encoding was
" selected run ":set fenc" and if you know the auto detection failed and want to
" force another one run ":edit ++enc=<your_enc>".
set fencs=utf-8,euc-jp,sjis
" Internal encoding used by vim buffers, help and commands
set enc=utf-8
" Terminal encoding used for input and terminal display
" Make sure your terminal is configured with the same encoding.
set tenc=utf-8
tenc:
This is the character encoding used to display and input text to the terminal. I configure my terminal (Konsole) always in UTF-8 and as far as I know my input method for Japanese (scim/anthy) is also UTF-8 so to avoid visual/input problems I leave this in UTF-8.enc:
The encoding used internally by vim buffers, help and commands. This does not need to be the same as tenc as vim will convert from one encoding to the other if they differ. This way you can use your native language encoding in your terminal and input method and let vim handle everything internally using UTF-8.fenc:
Is the character encoding used for reading/writing files. Again this can differ from enc and tenc because vim will convert between them if they differ. This way you can have your terminal configured with your native language (i.e. Japanese, Russian, Chinese...), let vim work internally in UTF-8 and finally save your files in any coding you want by setting fenc.fencs:
This is used by vim to try to auto detect the character encoding when opening an already existing file. The order in which you put the options is important so read the help ":h fencs" to learn how to set this correctly. For example if I put euc-jp first in the list all my English documents will be detected as euc-jp instead of utf-8 because all English characters are a subset of euc-jp, the same goes for latin1 and cp1250 encodings so make sure to put these at the end of the list. If the auto detection fails and your document is not displayed correctly you can always reload it forcing an encoding using ":edit ++enc=euc-jp" of course replace euc-jp with your desired encoding.
In my example above I set everything to UTF-8 that is recommended because converting from other encodings may cause loss of information. The only parameter I change is fenc when I need edit/save a file in a different encoding.
For example if I want to create a new file in euc-jp encoding:
- Open new file as normal using vim<
- Set file encoding using :set fenc=euc-jp
- Edit/Save as much as you like and rest assured that your file is euc-jp.
To edit an existing file simply open it and let vim auto detect the encoding using the options available in fencs. To check what encoding was set by vim you can use the command ":set fenc" and it will display the auto detected encoding. If it is not the correct one you can force the encoding by reloading the file using the command ":edit ++enc=euc-jp" replacing "euc-jp" with the encoding you desire.
Thursday, October 09, 2008
Use vimdiff as Subversion diff command

First create a script svbdiff inside the ~/.vim/scripts/ directory that contains the following
#!/bin/bash
shift 5;/usr/bin/vimdiff -f "$@"
and make it executable:
chmod +x ~/.vim/scripts/svndiff
Then create an alias in your ~/.bashrc file like:
alias svndiff="svn diff --diff-cmd ~/.vim/scripts/svndiff"
Now you can go to any Subversion repository and display file differences with
svndiff h264.c
The screenshot above shows the diff output of the h264.c of libavcodec. Make sure you have a color scheme that highlights the file differences. The screenshot above uses the inkpot color scheme.
Open new files as tabs in current Vim Console
When programming I like to have a single Vim editor open with all my files as tabs. Until now I was using the ":tabnew" command to open files in the current Vim window as I knew no other way.
Some research led me to the --remote-tab switch that allows to open files as tabs in currently open Vim processes but it seemed to work only with the graphical interface (gvim) and not with the console (vim). But as I made some tests I found this can work with the vim in console mode and more over can be nicely integrated with my favorite file managers (Konqueror/Dolphin).
All I needed to do was configure my bash so it will always start vim in server mode if it is not already and to always use the --remote-tab switch when opening files. To do this I simply added these functions to my ~/.bashrc file:
function v {
if [ "$#" -eq "0" ]; then
/usr/bin/vim --servername VIMLOCAL
else
if echo "$*" | grep -q -- "--servername" ; then
# echo "Command already has --servername use it"
/usr/bin/vim $*
else
#echo "Use default VIM server VIMLOCAL"
/usr/bin/vim --servername VIMLOCAL --remote-tab-silent $*
fi
fi
}
function gv {
if [ "$#" -eq "0" ]; then
/usr/bin/gvim --servername GVIMLOCAL
else
if echo "$*" | grep -q -- "--servername" ; then
# echo "Command already has --servername use it"
/usr/bin/gvim $*
else
#echo "Use default VIM server VIMLOCAL"
/usr/bin/gvim --servername GVIMLOCAL --remote-tab-silent $*
fi
fi
}
alias vi=v
alias vim=v
alias gvim=gv
With these functions loaded (i.e. source ~/.bashrc) the first time you execute vim/gvim it will start a new process in server mode.
# this command will open vim in the current console
# and display first_file.txt for editing.
vim first_file.txt
The second and all subsequent times you execute vim/gvim the files you set as parameters will be opened as tabs in the already opened vim/gvim instance.
# This command in other console will open the files
# second_file.txt and third_file.txt as tabs in the
# other console with first_file.txt open
vim second_file.txt third_file.txt
The above examples work also with gvim instead of vim. The only drawback is that now it is difficult to open files in a separate console. The best solution I found is to call vim with a different server name:
# This command will create a new instance of vim in the current console window
# and display the other_file.txt file for editing.
vim --servername OTHERNAME other_file.txt
If you prefer not to use tabs and like files to be opened as buffers inside a current running Vim instance then simply replace "--remote-tab-silent" with "--remote-silent" in the above scripts.
Some research led me to the --remote-tab switch that allows to open files as tabs in currently open Vim processes but it seemed to work only with the graphical interface (gvim) and not with the console (vim). But as I made some tests I found this can work with the vim in console mode and more over can be nicely integrated with my favorite file managers (Konqueror/Dolphin).
All I needed to do was configure my bash so it will always start vim in server mode if it is not already and to always use the --remote-tab switch when opening files. To do this I simply added these functions to my ~/.bashrc file:
function v {
if [ "$#" -eq "0" ]; then
/usr/bin/vim --servername VIMLOCAL
else
if echo "$*" | grep -q -- "--servername" ; then
# echo "Command already has --servername use it"
/usr/bin/vim $*
else
#echo "Use default VIM server VIMLOCAL"
/usr/bin/vim --servername VIMLOCAL --remote-tab-silent $*
fi
fi
}
function gv {
if [ "$#" -eq "0" ]; then
/usr/bin/gvim --servername GVIMLOCAL
else
if echo "$*" | grep -q -- "--servername" ; then
# echo "Command already has --servername use it"
/usr/bin/gvim $*
else
#echo "Use default VIM server VIMLOCAL"
/usr/bin/gvim --servername GVIMLOCAL --remote-tab-silent $*
fi
fi
}
alias vi=v
alias vim=v
alias gvim=gv
With these functions loaded (i.e. source ~/.bashrc) the first time you execute vim/gvim it will start a new process in server mode.
# this command will open vim in the current console
# and display first_file.txt for editing.
vim first_file.txt
The second and all subsequent times you execute vim/gvim the files you set as parameters will be opened as tabs in the already opened vim/gvim instance.
# This command in other console will open the files
# second_file.txt and third_file.txt as tabs in the
# other console with first_file.txt open
vim second_file.txt third_file.txt
The above examples work also with gvim instead of vim. The only drawback is that now it is difficult to open files in a separate console. The best solution I found is to call vim with a different server name:
# This command will create a new instance of vim in the current console window
# and display the other_file.txt file for editing.
vim --servername OTHERNAME other_file.txt
If you prefer not to use tabs and like files to be opened as buffers inside a current running Vim instance then simply replace "--remote-tab-silent" with "--remote-silent" in the above scripts.
Make Vim and Ctag play nice together

The classic tutorial of Ctags + Vim will go something like:
- - Install exuberant ctags
- - Generate tags for your project
- - Also install the tag list plugin
- - Use ^] and ^T to jump around the code
In this post I will try to go a little further and show how I manage tags in vim for real and large C projects.
Note that I mostly program in C (not C++), Ruby and Bash so I cannot guarantee my method works for all languages. In special Ctags has problems supporting C++. On the other side I have worked in small projects in Java and Python and I had no problems navigating them with my method.
Pre requisites
The following instructions were tested on a Linux machine installed using Kubuntu 8.10, 9.04 and 9.10. The first step is of course to make sure you have installed Vim and exuberant-ctags in you machine:
sudo aptitude install vim-ruby vim-scripts vim-common exuberant-ctags
If you are new to Vim then you should take some time to set your minimal configuration. You may follow mine if you want link.
Generate system wide ctags
All large projects will use at some point external libraries. My C projects usually use the Linux/FreeBSD system calls, SDL, OpenGL, libavcodec and libavformat among others.
To be able to jump to these libraries we must generate tags for each of them. I usually generate them inside my vim directory but you may create them in a system wide folder.
This is extremely useful to easily find out the members of libavcodec large structs or to quickly find out what are the arguments of SDL functions or system calls.
Ctags for Linux System Calls
mkdir -p ~/.vim/tags
sudo aptitude install linux-headers-`uname -r`
ctags -R -f ~/.vim/tags/kerneltags /usr/src/linux-headers-`uname -r`
Ctags for Ruby core and gem libraries
mkdir -p ~/.vim/tags
ctags -R -f ~/.vim/tags/rbtags /usr/lib/ruby
Ctags Java libraries
# For the java tags to work you need to install java source package.
# In Kubuntu/Ubuntu follow these instructions:
mkdir -p ~/.vim/tags
sudo aptitude install sun-java6-source
sudo mkdir -p /usr/lib/jvm/java-6-sun/src
sudo unzip -d /usr/lib/jvm/java-6-sun/ /usr/lib/jvm/java-6-sun/src.zip
ctags -R -f ~/.vim/tags/javatags /usr/lib/jvm/java-6-sun/src
At this you should be able to create tags for all the libraries you use in your projects. Simply get the source code or development packages of the library and run ctags on the folder that contains the source code and save the tags in a file you can access.
TODO: I really need to research what are the best ctags flags for each language in order to generate the most information possible per tag.
Updating your system wide ctags
Here we have three options:
- Update your system/library tags manually each time you upgrade them
- Use a cron job to update them every certain period of time (e.g. once a week).
- Use incrontab to monitor changes in the system/library folders and automatically update the tags every time these folders change.
Personally I use the cron job method and for some libraries that I update often (e.g. libavcodec from subversion) I manually generate the tags soon after I update the library.
I found about incrontab just recently and I think will be the best way to update system wide tags. It behaves mostly like crontab but instead of triggering actions based on time periods it triggers by file system events like file change, move, save, etc. It should be easy to monitor the folders containing the libraries of interest and regenerate the tags for each library based on change events in the folder contents. The tricky part would be to determine what file system event we must listen to decide if we should regenerate or not the tags.
Generating ctags for your own libraries/projects
Now we need to generate tags for projects/libraries we are developing at the moment. These projects are constantly changing so we need to update them as soon as possible.
This is a common problem that lot's of people solve in different ways. See for example these links:
- Vim auto-generate ctags
- Vim+ctags tips and tricks
- TagsParser : Automatic tagfile updating and tag viewer
- vim with ctags for multi-level directory hierarchy
- http://vim.wikia.com/wiki/Tags_generation_for_current_file
- Auto tags by project.vim
Most methods I found do the same more or less: they set some auto commands to autogenerate tag files when a file is edited and set the search path in a way it can find the generated tags.
I used to have some autocommands in vim to generate tags every time I modified a file in a project. Unfortunately I never figured out how to generate a single tags file per project/library. I always ended with a global tag file for all projects or a single tag file per folder.
Another methods worked by adding tags generation commands to the build system so the tags get generated when we recompile the project (e.g. Makefile, Ant, CMake) and others simply created scripts per project and executed them manually from the project root directory or within vim using a key map.
Since I use the Project.vim plugin it is natural for me to use it to generate the tags files as it is done here. My method is a lot different in that I do not use a Makefile to generate the tags and I only re-generate them when a file has been edited, not every time we open a file.
Auto-generating Ctags Using the Project.vim plugin
Anyone that has used the Project.vim plugin is familiar with the snippet shown below:
myproj=<src-path> in=in.vim out=out.vim CD=<src-path> {
CMakeLists.txt
in.vim
out.vim
include-----------------
src---------------------
test--------------------
tools-------------------
}
This corresponds to a project entry called "myproj" with root directory set to "src-path". Here the important parts are the "in", "out" and "CD" parameters.
The "CD" parameter makes sure that you are at the project root directory when editing a file from that project. This is useful to have a single tags file per project.
The "in" and "out" parameters are simple vim scripts. The "in" script is executed when opening a file from the project and the "out" script when leaving the file. In these scripts is where I set/unset the autocommands that take care of generating the tags files per project.
Every time I create a new project using the Project.vim plugin I also create these in.vim and out.vim scripts:
in.vim
" let ctags_cmd='/usr/local/bin/exctags' " Use this one in FreeBSD
let ctags_cmd='/usr/bin/ctags' " Use this one in Linux
let proj_path = escape(getcwd(), ' ')
let _ctagargs_ = " --fields=+iaS --extra=+q -R "
let _ctag_ = ctags_cmd . _ctagargs_ . " -f " . proj_path . "/.tags " . proj_path
au BufWritePost <buffer> call system(_ctag_)
out.vim
au! * <buffer>
Now every time you open a file from a project an autocommand will be created for that file that will regenerate tags for the whole project every time the file is saved. The generated tags file is always in the root directory of the project. You may prefer to use the "--append" switch of ctags to generate tags for the current file only but I personally prefer regenerating the tags of the whole project every time.
When you leave the file the autocommand is removed in the out.vim script. If we do not remove the autocommand, it will be created every time we enter the file that results in a lot of autocommands around. I am not really sure if this can cause problems but is better go on the safe side.
Now the last piece of the puzzle is how to tell Vim where to search for the tags.
Tell Vim where to search for tags
To find the per project tags we can simply add this to our vimrc file:
set tags=./.tags;${HOME}
This simple command tells vim to search tag files from the current directory backwards up to our $HOME directory. Since we are sure that we are always at the projects root directory where the tags file is automatically generated we can be sure that vim will always find the tag we are looking for.
There are some system wide tags that we want to always load depending on the language we are developing. For example when I develop in C I would like to have the kernel system calls tag file (that we generated before) or when developing in Ruby/Java I expect to have these languages core tags loaded too.
To do this I load all system wide libraries based on filetype using these commands in my vimrc file:
au BufRead,BufNewFile *.rb setlocal tags+=~/.vim/tags/rbtags
au BufRead,BufNewFile *.cpp,*.h,*.c setlocal tags+=~/.vim/tags/kerneltags
au BufRead,BufNewFile *.rl,*.def setlocal tags+=~/.vim/tags/kerneltags
au BufRead,BufNewFile *.py setlocal tags+=~/.vim/tags/pytags
au BufRead,BufNewFile *.java setlocal tags+=~/.vim/tags/javatags
What these commands do is to load the system wide tag files we generated before depending on the file type. For example when editing a ruby file it loads the Ruby tags file.
We must note that the commands only apply to the current file being edited (e.g. setlocal), not all the files in the project and that we are adding the tags file not replacing the current one (e.g. += ).
Finally how can we add other tag files to the project that are not from the project itself and not system wide?. For example my project requires external libraries like libavcodec and SDL so it would be nice to query the function and struct definitions of these libraries.
To do this I use the "in.vim" script of the Project.vim plugin like:
" let ctags_cmd='/usr/local/bin/exctags' " Use this one in FreeBSD
let ctags_cmd='/usr/bin/ctags' " Use this one in Linux
let proj_path = escape(getcwd(), ' ')
let _ctagargs_ = " --c++-kinds=+p --fields=+iaS --extra=+q -R "
let _ctag_ = ctags_cmd . _ctagargs_ . " -f " . proj_path . "/.tags " . proj_path
au BufWritePost <buffer> call system(_ctag_)
" Add external library tag files
setlocal tags+=~/.vim/tags/sdltags
setlocal tags+=~/.vim/tags/ffmpegtags
This is exactly the same "in.vim" script as above with two added lines that include the libavcodec (FFMpeg) and SDL library tags files to the search path of vim for all files in the project. These files were of course generated before the same way we created the system wide tag files and updated via a cron job.
Once you setup this once you don't need to worry about this anymore until you reinstall your computer. The system wide libraries will be always updated if you configured the corresponding cron jobs and once a project is created it will always have the tags files updated and ready to query.
Setting up the TagList Plugin
No blog post about Vim+Ctags is complete without the mighty TagList plugin. To install this plugin go to http://vim-taglist.sourceforge.net/ and download the taglist_45.zip file. Then add it to your vim installation using the command:
unzip -d ~/.vim taglist_45.zip
Then add these commands to your vimrc file:
" let Tlist_Ctags_Cmd='/usr/local/bin/exctags' " Use this one in FreeBSD
let Tlist_Ctags_Cmd='/usr/bin/ctags' " Use this one in Linux
let Tlist_Auto_Open=0
let Tlist_Auto_Update=1
let Tlist_Use_Horiz_Window = 0
"let Tlist_Inc_Winwidth=0
"let Tlist_Show_One_File=1
let Tlist_Exist_OnlyWindow=1
let Tlist_Use_Right_Window = 1
let Tlist_Sort_Type="name"
let Tlist_Display_Prototype=0
let Tlist_Compact_Format=1 " Compact?
let Tlist_GainFocus_On_ToggleOpen=1
let Tlist_Display_Tag_Scope=1
let Tlist_Close_On_Select=1
let Tlist_Enable_Fold_Column=1
"let TList_WinWidth=25
" Map a F8 to the TlistToggle command for easy tags list access
nnoremap <silent> <F8> <ESC>:TlistToggle<CR>
Of course I recommend you to read the help file and set the configuration as it fits you better. With my configuration you can press
Subscribe to:
Posts (Atom)