UNIX / Linux keyboard.

Display the Word Count in the Vim Status Line

Counting Words in Vim

I have recently found myself frequently needing to count the number of words in a simple ASCII text file as I create it.

Unfortunately, the Vi editor does not provide this by default. Why not? Continuously maintaining a count of words can require a surprising amount of work as a file is rapidly modified through the sweeping methods possible within Vi.

That's fine, it's easy to add the capability.

Let's see what we want to accomplish by using the creation of this file as an example. First, notice that the buffer content has been color-coded for syntax specific to the automatically detected language content.

The vim editor can display the current word and line count and position within a multicolored statusline.

Second, notice the detailed statusline at the bottom. It starts with the full path to the file being edited. The appended flag ,+ indicates that the file has been modified since it was last written. Press :w to write out the buffer and that flag disappears. The following [xhtml] indicates that vim has automatically determined that the file contains XHTML data. You can see that this is not assumed from the file name, as it ends with .html and not .xhtml.

Then we're told that the buffer currently contains 1202 words, and the cursor is on line 43 out of 286, 15% of the way through the file. This looks useful!

That's a lot of detail on the line, and there is very little free space remaining. That's fine, this has been set up so that the long full path to the file will be truncated at its left end as needed to otherwise allow everything to fit. I show how to do that below, it's simple.

To get started, use vim, not vi. With Linux and OS X, you get vim by default, and typing vi really gives you vim. But with OpenBSD, Solaris and other versions of Unix, you may have to add a package and explicitly run the vim command to get the modernized version.

% uname -sr
OpenBSD 5.9
% which vi
/usr/bin/vi
% which vim
/usr/local/bin/vim
% pkg_info | grep 'vim'
vim-7.4.900-gtk2  vi clone, many additional features

% ssh solarisbox 'uname -sr ; which vi ; which vim'
SunOS 5.10
/usr/ucb/vi
/usr/local/bin/vim 

The critical file is ~/.vimrc. Modify that file to configure your editor. If you break your editor by putting errors into this file, rescue yourself by editing the file without using it:
% vim -U NONE ~/.vimrc

The easiest way to explain this is to simply show my .vimrc file with plenty of comments. It starts with the basic settings, then it defines a function for efficient word counting and builds a complex statusline. Then there is some standard boilerplate provided with the distribution for handling compressed files.

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Basic settings
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

" Turn on syntax highlighting, or really color coding.
" This really helps you spot broken syntax as you type.
syntax on

" Turn off multi-level undo.  Now pressing 'u' will toggle
" the last change off and on, as opposed to rolling back
" undoing all previous changes in reverse order.
" Multi-level undo is NOT helpful for most people!
set undolevels=0

" Maintain existing indentation. 
set autoindent

" Show matches for () [] {} 
set showmatch

" Default tab spacing = 8 columns
set ts=8

" Default backspace like normal
set bs=2

" Some option activate by default (remove the no to allow these).
set nobackup
set nohlsearch
set noincsearch

" Some environments set textwidth to force linewrap.  Disable this. 
set textwidth=0

" Display a status-bar.
set laststatus=2

""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Here begins my automated wordcount addition.
" This combines several ideas from:
" http://stackoverflow.com/questions/114431/fast-word-count-function-in-vim
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
let g:word_count="<unknown>"
function WordCount()
	return g:word_count
endfunction
function UpdateWordCount()
	let lnum = 1
	let n = 0
	while lnum <= line('$')
		let n = n + len(split(getline(lnum)))
		let lnum = lnum + 1
	endwhile
	let g:word_count = n
endfunction
" Update the count when cursor is idle in command or insert mode.
" Update when idle for 1000 msec (default is 4000 msec).
set updatetime=1000
augroup WordCounter
	au! CursorHold,CursorHoldI * call UpdateWordCount()
augroup END
" Set statusline, shown here a piece at a time
highlight User1 ctermbg=green guibg=green ctermfg=black guifg=black
set statusline=%1*			" Switch to User1 color highlight
set statusline+=%<%F			" file name, cut if needed at start
set statusline+=%M			" modified flag
set statusline+=%y			" file type
set statusline+=%=			" separator from left to right justified
set statusline+=\ %{WordCount()}\ words,
set statusline+=\ %l/%L\ lines,\ %P	" percentage through the file


""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Below here is just  standard content from Mageia installation environment.
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""

" To display color on a vanilla xterm (Use Rxvt luke).
if &term=="xterm"
	set t_Co=8
	set t_AB=<ESC>[%?%p1%{8}%<%t%p1%{40}%+%e%p1%{92}%+%;%dm
	set t_AF=<ESC>[%?%p1%{8}%<%t%p1%{30}%+%e%p1%{82}%+%;%dm
endif

" Some macros to manage the buffer of vim
map <F5> :bp<C-M>
map <F6> :bn<C-M>
map <F7> :bd<C-M>

"" Gzip and Bzip2 files support
" Take from the Debian package and the exemple on $VIM/vim_exemples
if has("autocmd")

" Set some sensible defaults for editing C-files
augroup cprog
" Remove all cprog autocommands
  au!

  " When starting to edit a file:
  "   For *.c and *.h files set formatting of comments and set C-indenting on.
  "   For other files switch it off.
  "   Don't change the order, it's important that the line with * comes first.
  autocmd BufRead *       set formatoptions=tcql nocindent comments&
  autocmd BufRead *.c,*.h set formatoptions=croql cindent comments=sr:/*,mb:*,el:*/,://
augroup END

" Also, support editing of gzip-compressed files. DO NOT REMOVE THIS!
" This is also used when loading the compressed helpfiles.
augroup gzip
" Remove all gzip autocommands
  au!

  " Enable editing of gzipped files
  "	  read:	set binary mode before reading the file
  "		uncompress text in buffer after reading
  "	 write:	compress file after writing
  "	append:	uncompress file, append, compress file
  autocmd BufReadPre,FileReadPre	*.gz set bin
  autocmd BufReadPre,FileReadPre	*.gz let ch_save = &ch|set ch=2
  autocmd BufReadPost,FileReadPost	*.gz '[,']!gunzip
  autocmd BufReadPost,FileReadPost	*.gz set nobin
  autocmd BufReadPost,FileReadPost	*.gz let &ch = ch_save|unlet ch_save
  autocmd BufReadPost,FileReadPost	*.gz execute ":doautocmd BufReadPost " . %:r

  autocmd BufWritePost,FileWritePost	*.gz !mv <afile> <afile>:r
  autocmd BufWritePost,FileWritePost	*.gz !gzip <afile>:r

  autocmd FileAppendPre			*.gz !gunzip <afile>
  autocmd FileAppendPre			*.gz !mv <afile>:r <afile>
  autocmd FileAppendPost		*.gz !mv <afile> <afile>:r
  autocmd FileAppendPost		*.gz !gzip <afile>:r
augroup END

augroup bzip2
" Remove all bzip2 autocommands
  au!

  " Enable editing of bzipped files
  "       read: set binary mode before reading the file
  "             uncompress text in buffer after reading
  "      write: compress file after writing
  "     append: uncompress file, append, compress file
  autocmd BufReadPre,FileReadPre        *.bz2 set bin
  autocmd BufReadPre,FileReadPre        *.bz2 let ch_save = &ch|set ch=2
  autocmd BufReadPost,FileReadPost      *.bz2 set cmdheight=2|'[,']!bunzip2
  autocmd BufReadPost,FileReadPost      *.bz2 set cmdheight=1 nobin|execute ":doautocmd BufReadPost " . %:r
  autocmd BufReadPost,FileReadPost      *.bz2 let &ch = ch_save|unlet ch_save

  autocmd BufWritePost,FileWritePost    *.bz2 !mv <afile> <afile>:r
  autocmd BufWritePost,FileWritePost    *.bz2 !bzip2 <afile>:r

  autocmd FileAppendPre                 *.bz2 !bunzip2 <afile>
  autocmd FileAppendPre                 *.bz2 !mv <afile>:r <afile>
  autocmd FileAppendPost                *.bz2 !mv <afile> <afile>:r
  autocmd FileAppendPost                *.bz2 !bzip2 -9 --repetitive-best <afile>:r
augroup END

endif " has ("autocmd") 

As for color, to be safe you should stick with the ANSI colors going back to MS-DOS command prompts. Quoting from :help color within vim we see:

							*cterm-colors*
	    NR-16   NR-8    COLOR NAME ~
	    0	    0	    Black
	    1	    4	    DarkBlue
	    2	    2	    DarkGreen
	    3	    6	    DarkCyan
	    4	    1	    DarkRed
	    5	    5	    DarkMagenta
	    6	    3	    Brown, DarkYellow
	    7	    7	    LightGray, LightGrey, Gray, Grey
	    8	    0*	    DarkGray, DarkGrey
	    9	    4*	    Blue, LightBlue
	    10	    2*	    Green, LightGreen
	    11	    6*	    Cyan, LightCyan
	    12	    1*	    Red, LightRed
	    13	    5*	    Magenta, LightMagenta
	    14	    3*	    Yellow, LightYellow
	    15	    7*	    White

	The number under "NR-16" is used for 16-color terminals ('t_Co'
	greater than or equal to 16).  The number under "NR-8" is used for
	8-color terminals ('t_Co' less than 16).  The '*' indicates that the
	bold attribute is set for ctermfg.  In many 8-color terminals (e.g.,
	"linux"), this causes the bright colors to appear.  This doesn't work
	for background colors!	Without the '*' the bold attribute is removed.
	If you want to set the bold attribute in a different way, put a
	"cterm=" argument AFTER the "ctermfg=" or "ctermbg=" argument.	Or use
	a number instead of a color name.

	The case of the color names is ignored.
	Note that for 16 color ansi style terminals (including xterms), the
	numbers in the NR-8 column is used.  Here '*' means 'add 8' so that Blue
	is 12, DarkGray is 8 etc.

	Note that for some color terminals these names may result in the wrong
	colors!