sponsor Vim development Vim logo Vim Book Ad

Mark : Highlight several words in different colors simultaneously. (#1238 continued)

 script karma  Rating 390/122, Downloaded by 7158    Comments, bugs, improvements  Vim wiki

created by
Ingo Karkat
 
script type
utility
 
description
DESCRIPTION
This plugin adds mappings and a :Mark command to highlight several words in
different colors simultaneously, similar to the built-in 'hlsearch'
highlighting of search results and the * command. For example, when you
are browsing a big program file, you could highlight multiple identifiers in
parallel. This will make it easier to trace the source code.

This is a continuation of vimscript #1238 by Yuheng Xie, who doesn't maintain
his original version anymore and recommends switching to this fork. This
plugin offers the following advantages over the original:
- Much faster, all colored words can now be highlighted, no more clashes with
  syntax highlighting (due to use of matchadd()).
- Many bug fixes.
- Jumps behave like the built-in search, including wrap and error messages.
- Like the built-in commands, jumps take an optional [count] to quickly skip
  over some marks.
- Marks can be persisted, and patterns can be added / subtracted from
  mark highlight groups.

RELATED WORKS
- MultipleSearch (vimscript #479) can highlight in a single window and in all
  buffers, but still relies on the :syntax highlighting method, which is
  slower and less reliable.
- http://vim.wikia.com/wiki/Highlight_multiple_words offers control over the
  color used by mapping the 1-9 keys on the numeric keypad, persistence, and
  highlights only a single window.
- highlight.vim (vimscript #1599) highlights lines or patterns of interest in
  different colors, using mappings that start with CTRL-H and work on cword.
- quickhl.vim (vimscript #3692) can also list the matches with colors and in
  addition offers on-the-fly highlighting of the current word (like many IDEs
  do).
- Highlight (http://www.drchip.org/astronaut/vim/index.html#HIGHLIGHT) has
  commands and mappings for highlighting and searching, uses matchadd(), but
  limits the scope of highlightings to the current window.
- TempKeyword (vimscript #4636) is a simple plugin that can matchadd() the
  word under the cursor with \0 - \9 mappings. (And clear with \c0 etc.)
- simple_highlighting (vimscript #4688) has commands and mappings to highlight
  8 different slots in all buffers.

HIGHLIGHTING
<Leader>m               Mark the word under the cursor, similar to the star
                        command. The next free highlight group is used.
                        If already on a mark: Clear the mark, like
                        <Leader>n.
{Visual}<Leader>m       Mark or unmark the visual selection.
[N]<Leader>m            With [N], mark the word under the cursor with the
                        named highlight group [N]. When that group is not
                        empty, the word is added as an alternative match, so
                        you can highlight multiple words with the same color.
                        When the word is already contained in the list of
                        alternatives, it is removed.

                        When [N] is greater than the number of defined mark
                        groups, a summary of marks is printed. Active mark
                        groups are prefixed with "*" (or "M*" when there are
                        M pattern alternatives), the default next group with
                        ">", the last used search with "/" (like :Marks
                        does). Input the mark group, accept the default with
                        <CR>, or abort with <Esc> or any other key.
                        This way, when unsure about which number represents
                        which color, just use 99<Leader>n and pick the color
                        interactively!

{Visual}[N]<Leader>m    Ditto, based on the visual selection.

[N]<Leader>r            Manually input a regular expression to mark.
{Visual}[N]<Leader>r    Ditto, based on the visual selection.

<Leader>n               Clear the mark under the cursor.
                        If not on a mark: Disable all marks, similar to
                        :nohlsearch.
[N]<Leader>n            Clear the marks represented by highlight group [N].

:[N]Mark                Clear the marks represented by highlight group [N].
:[N]Mark[!] {pattern}   Mark or unmark {pattern}. Unless [N] is given, the
                        next free highlight group is used for marking.
                        With [N], mark {pattern} with the named highlight
                        group [N]. When that group is not empty, the word is
                        added as an alternative match, so you can highlight
                        multiple words with the same color, unless [!] is
                        given; then, {pattern} overrides the existing mark.
                        When the word is already contained in the list of
                        alternatives, it is removed.
                        For implementation reasons, {pattern} cannot use the
                        'smartcase' setting, only 'ignorecase'.
:Mark                   Disable all marks, similar to :nohlsearch. Marks
                        will automatically re-enable when a mark is added or
                        removed, or a search for marks is performed.

:MarkClear              Clear all marks. In contrast to disabling marks, the
                        actual mark information is cleared, the next mark will
                        use the first highlight group. This cannot be undone.

SEARCHING
[count]*         [count]#
[count]<Leader>* [count]<Leader>#
[count]<Leader>/ [count]<Leader>?
                        Use these six keys to jump to the [count]'th next /
                        previous occurrence of a mark.
                        You could also use Vim's / and ? to search, since the
                        mark patterns are (optionally, see configuration)
                        added to the search history, too.

            Cursor over mark                    Cursor not over mark
---------------------------------------------------------------------------
  <Leader>* Jump to the next occurrence of      Jump to the next occurrence of
            current mark, and remember it       "last mark".
            as "last mark".

  <Leader>/ Jump to the next occurrence of      Same as left.
            ANY mark.

   *        If <Leader>* is the most recently   Do Vim's original * command.
            used, do a <Leader>*; otherwise
            (<Leader>/ is the most recently
            used), do a <Leader>/.

If you work with multiple highlight groups and assign special meaning to them
(e.g. group 1 for notable functions, 2 for variables, 3 for includes), you can
use the 1-9 keys on the numerical keypad to jump to occurrences of a
particular highlight group. With the general * and # commands above, you'd
first need to locate a nearby occurrence of the desired highlight group if
it's not the last mark used.

<k1> .. <k9>            Jump to the [count]'th next occurrence of the mark
                        belonging to highlight group 1..9.
<C-k1> .. <C-k9>        Jump to the [count]'th previous occurrence of the mark
                        belonging to highlight group 1..9.
                        Note that these commands only work when the "Num Lock"
                        indicator of your keyboard is ON; otherwise, the
                        keypad is used for cursor movement.

MARK PERSISTENCE
The marks can be kept and restored across Vim sessions, using the viminfo
file. For this to work, the "!" flag must be part of the 'viminfo' setting:
    set viminfo+=!  " Save and restore global variables.

:MarkLoad               Restore the marks from the previous Vim session. All
                        current marks are discarded.
:MarkLoad {slot}        Restore the marks stored in the named {slot}. All
                        current marks are discarded.

:MarkSave               Save the currently defined marks (or clear the
                        persisted marks if no marks are currently defined) for
                        use in a future Vim session.
:MarkSave {slot}        Save the currently defined marks in the named {slot}.
                        If {slot} is all UPPERCASE, the marks are persisted
                        and can be |:MarkLoad|ed in a future Vim session;
                        otherwise, you can just recall within the current
                        session. When no marks are currently defined, the
                        {slot} is cleared.

MARK INFORMATION
Both mark-highlighting and mark-searching commands print information about
the mark and search pattern, e.g.
        mark-1/\<pattern\>
This is especially useful when you want to add or subtract patterns to a mark
highlight group via [N].

:Marks                  List all mark highlight groups and the search patterns
                        defined for them.
                        The group that will be used for the next :Mark or
                        <Leader>m command (with [N]) is shown with a ">".
                        The last mark used for a search (via <Leader>*) is
                        shown with a "/".

MARK HIGHLIGHTING PALETTES
The plugin comes with three predefined palettes: original, extended, and
maximum. You can dynamically toggle between them, e.g. when you need more
marks or a different set of colors.

:MarkPalette {palette}  Highlight existing and future marks with the colors
                        defined in {palette}. If the new palette contains less
                        mark groups than the current one, the additional marks
                        are lost.
                        You can use :command-completion for {palette}.
 
install details
INSTALLATION
This script is packaged as a vimball. If you have the "gunzip" decompressor
in your PATH, simply edit the *.vmb.gz package in Vim; otherwise, decompress
the archive first, e.g. using WinZip. Inside Vim, install by sourcing the
vimball or via the :UseVimball command.
    vim mark*.vmb.gz
    :so %
To uninstall, use the :RmVimball command.

DEPENDENCIES
- Requires Vim 7.1 with matchadd(), or Vim 7.2 or higher.

CONFIGURATION
For a permanent configuration, put the following commands into your vimrc.

This plugin defines 6 mark groups:
    1: Cyan  2:Green  3:Yellow  4:Red  5:Magenta  6:Blue
Higher numbers always take precedence and are displayed above lower ones.

Especially if you use GVIM, you can switch to a richer palette of up to 18
colors:
    let g:mwDefaultHighlightingPalette = 'extended'
Or, if you have both good eyes and display, you can try a palette that defines
27, 58, or even 77 colors, depending on the number of available colors:
    let g:mwDefaultHighlightingPalette = 'maximum'

If you like the additional colors, but don't need that many of them, restrict
their number via:
        let g:mwDefaultHighlightingNum = 9

If none of the default highlightings suits you, define your own colors in your
vimrc file (or anywhere before this plugin is sourced), in the following form
(where N = 1..):
    highlight MarkWordN ctermbg=Cyan ctermfg=Black guibg=#8CCBEA guifg=Black
You can also use this form to redefine only some of the default highlightings.
If you want to avoid losing the highlightings on :colorscheme commands, you
need to re-apply your highlights on the ColorScheme event, similar to how
this plugin does. Or you define the palette not via :highlight commands, but
use the plugin's infrastructure:
    let g:mwDefaultHighlightingPalette = [
    \   { 'ctermbg':'Cyan', 'ctermfg':'Black', 'guibg':'#8CCBEA', 'guifg':'Black' },
    \   ...
    \]

If you want to switch multiple palettes during runtime, you need to define
them as proper palettes:
    let g:mwPalettes['mypalette'] = [
    \   { 'ctermbg':'Cyan', 'ctermfg':'Black', 'guibg':'#8CCBEA', 'guifg':'Black' },
    \   ...
    \]
    let g:mwPalettes['other'] = [ ... ]
    let g:mwDefaultHighlightingPalette = 'mypalette'
To add your palette to the existing ones, do this after the default palette
has been defined, e.g. in .vim/after/plugin/mark.vim). Alternatively, you can
also completely redefine all available palettes in .vimrc.

The search type highlighting (in the search message) can be changed via:
    highlight link SearchSpecialSearchType MoreMsg

By default, any marked words are also added to the search (/) and input (@)
history; if you don't want that, remove the corresponding symbols from:
    let g:mwHistAdd = '/@'

To enable the automatic restore of marks from a previous Vim session:
    let g:mwAutoLoadMarks = 1

To turn off the automatic persistence of marks across Vim sessions:
    let g:mwAutoSaveMarks = 0
You can still explicitly save marks via :MarkSave.

If you have set 'ignorecase', but want marks to be case-insensitive, you can
override the default behavior of using 'ignorecase' by setting:
        let g:mwIgnoreCase = 0

You can use different mappings by mapping to the <Plug>Mark... mappings (use
":map <Plug>Mark" to list them all) before this plugin is sourced.

There are no default mappings for toggling all marks and for the :MarkClear
command, but you can define some yourself:
    nmap <Leader>M <Plug>MarkToggle
    nmap <Leader>N <Plug>MarkAllClear

To remove the default overriding of * and #, use:
    nmap <Plug>IgnoreMarkSearchNext <Plug>MarkSearchNext
    nmap <Plug>IgnoreMarkSearchPrev <Plug>MarkSearchPrev

If you don't want the * and # mappings remember the last search type and
instead always search for the next occurrence of the current mark, with a
fallback to Vim's original * command, use:
    nmap * <Plug>MarkSearchOrCurNext
    nmap # <Plug>MarkSearchOrCurPrev

The search mappings (*, #, etc.) interpret [count] as the number of
occurrences to jump over. If you don't want to use the separate
mark-keypad-searching mappings, and rather want [count] select the highlight
group to target (and you can live with jumps restricted to the very next
match), (re-)define to these mapping targets:
    nmap * <Plug>MarkSearchGroupNext
    nmap # <Plug>MarkSearchGroupPrev

You can remap the direct group searches (by default via the keypad 1-9 keys):
    nmap <Leader>1  <Plug>MarkSearchGroup1Next
    nmap <Leader>!  <Plug>MarkSearchGroup1Prev

If you need more / less groups, this can be configured via:
    let g:mwDirectGroupJumpMappingNum = 20
Set to 0 to completely turn off the keypad mappings. This is easier than
remapping all <Plug>-mappings.

Some people like to create a mark based on the visual selection, like
v_<Leader>m, but have whitespace in the selection match any whitespace when
searching (searching for "hello world" will also find "hello<Tab>world" as
well as "hello" at the end of a line, with "world" at the start of the next
line). The Vim Tips Wiki describes such a setup for the built-in search at
    http://vim.wikia.com/wiki/Search_for_visually_selected_text
You can achieve the same with the Mark plugin through the following scriptlet:
    function! s:GetVisualSelectionAsLiteralWhitespaceIndifferentPattern()
        return substitute(escape(mark#GetVisualSelection(), '' . '^$.*[~'), '\_s\+', '\\_s\\+', 'g')
    endfunction
    vnoremap <silent> <Plug>MarkWhitespaceIndifferent :<C-u>if !mark#DoMark(v:count, <SID>GetVisualSelectionAsLiteralWhitespaceIndifferentPattern())<Bar>execute "normal! \<lt>C-\>\<lt>C-n>\<lt>Esc>"<Bar>endif<CR>
Using this, you can assign a new visual mode mapping <Leader>*
    vmap <Leader>* <Plug>MarkWhitespaceIndifferent
or override the default v_<Leader>m mapping, in case you always want this
behavior:
    vmap <Plug>IgnoreMarkSet <Plug>MarkSet
    vmap <Leader>m <Plug>MarkWhitespaceIndifferent

INTEGRATION
The following functions offer (read-only) access to the number of marks and
individual patterns:
- mark#GetNum()
- mark#GetPattern([{index}])
 

rate this script Life Changing Helpful Unfulfilling 
script versions (upload new version)

Click on the package to download.

package script version date Vim version user release notes
mark-2.8.2.vmb.gz 2.8.2 2013-12-16 7.2 Ingo Karkat - BUG: :Mark cannot highlight patterns starting with a number. Use -range=0 instead of -count. Thanks to Vladimir Marek for reporting this.
mark-2.8.1.vmb.gz 2.8.1 2013-11-22 7.2 Ingo Karkat - Allow to override the adding to existing marks via :[N]Mark! {pattern}.
- ENH: Implement command completion for :[N]Mark that offers existing mark patterns (from group [N] / all groups), both as one regular expression and individual alternatives. The leading \< can be omitted.
mark-2.8.0.vmb.gz 2.8.0 2013-06-01 7.2 Ingo Karkat - Also allow a [count] for <Leader>r to select (or query for) a mark group, as with <Leader>m.
- CHG: Also set the current mark to the used mark group when a mark was set via <Leader>r and :Mark so that it is easier to determine whether the entered pattern actually matches anywhere. Thanks to Xiaopan Zhang for notifying me about this problem.
- Add <Plug>MarkSearchGroupNext / <Plug>MarkSearchGroupPrev to enable searching for particular mark groups. Thanks to Xiaopan Zhang for the suggestion.
- Define default mappings for keys 1-9 on the numerical keypad to jump to a particular group (backwards with <C-kN>). Their definition is controlled by the new g:mwDirectGroupJumpMappingNum variable.
- ENH: Allow to store an arbitrary number of marks via named slots that can optionally be passed to :MarkLoad / :MarkSave. If the slot is all-uppercase, the marks will also be persisted across Vim invocations.
mark-2.7.2.vmb.gz 2.7.2 2012-10-15 7.2 Ingo Karkat - Issue an error message "No marks defined" instead of moving the cursor by one character when there are no marks (e.g. initially or after :MarkClear).
- Enable custom integrations via new mark#GetNum() and mark#GetPattern() functions.
mark-2.7.1.vmb.gz 2.7.1 2012-09-14 7.2 Ingo Karkat - Enable alternative * / # mappings that do not remember the last search type through new <Plug>MarkSearchOrCurNext, <Plug>MarkSearchOrCurPrev, <Plug>MarkSearchOrAnyNext, <Plug>MarkSearchOrAnyPrev mappings. Based on an inquiry from Kevin Huanpeng Du.
mark.vba.gz 2.7.0 2012-07-04 7.2 Ingo Karkat - ENH: Implement :MarkPalette command to switch mark highlighting on-the-fly during runtime.
- Add "maximum" palette contributed by rockybalboa4.
mark.vba.gz 2.6.5 2012-06-24 7.2 Ingo Karkat Don't define the default <Leader>m and <Leader>r mappings in select mode, just visual mode. Thanks to rockybalboa4 for pointing this out.
mark.vba.gz 2.6.4 2012-04-23 7.2 Ingo Karkat - Allow to override 'ignorecase' setting via g:mwIgnoreCase. Thanks to fanhe for the idea and sending a patch.
mark.vba.gz 2.6.3 2012-03-27 7.2 Ingo Karkat - ENH: Allow choosing of palette and limiting of default mark highlight groups via g:mwDefaultHighlightingPalette and g:mwDefaultHighlightingNum.
- ENH: Offer an extended color palette in addition to the original 6-color one. Enable this via :let g:mwDefaultHighlightingPalette = "extended" in your vimrc.
mark.vba.gz 2.6.2 2012-03-26 7.2 Ingo Karkat - ENH: When a [count] exceeding the number of available mark groups is given, a summary of marks is given and the user is asked to select a mark group. This allows to interactively choose a color via 99<Leader>m.
- ENH: Include count of alternative patterns in :Marks list.
- CHG: Use ">" for next mark and "/" for last search in :Marks.
mark.vba.gz 2.6.1 2012-03-23 7.2 Ingo Karkat - ENH: Add :Marks command that prints all mark highlight groups and their search patterns, plus information about the current search mark, next mark group, and whether marks are disabled.
- ENH: Show which mark group a pattern was set / added / removed / cleared.
- FIX: When the cursor is positioned on the current mark, [N]<Leader>n / <Plug>MarkClear with [N] appended the pattern for the current mark (again and again) instead of clearing it. Must not pass current mark pattern when [N] is given.
- CHG: Show mark group number in same-mark search and rename search types from "any-mark", "same-mark", and "new-mark" to the shorter "mark-*", "mark-N", and "mark-N!", respectively.
mark.vba.gz 2.6.0 2012-03-22 7.2 Ingo Karkat - ENH: Allow [count] for <Leader>m and :Mark to add / subtract match to / from highlight group [count], and use [count]<Leader>n to clear only highlight group [count]. This was also requested by Philipp Marek.
- FIX: :Mark and <Leader>n actually toggled marks back on when they were already off. Now, they stay off on multiple invocations. Use :call mark#Toggle() / <Plug>MarkToggle if you want toggling.
mark.vba.gz 2.5.3 2012-03-02 7.2 Ingo Karkat - BUG: Version check mistakenly excluded Vim 7.1 versions that do have the matchadd() function. Thanks to Philipp Marek for sending a patch.
mark.vba.gz 2.5.2 2011-11-09 7.2 Ingo Karkat Fixed various problems with wrap-around warnings:
- BUG: With a single match and 'wrapscan' set, a search error was issued.
- FIX: Backwards search with single match leads to wrong error message instead.
- FIX: Wrong logic for determining l:isWrapped lets wrap-around go undetected.
mark.vba.gz 2.5.1 2011-05-17 7.2 Ingo Karkat - FIX: == comparison in s:DoMark() leads to wrong regexp (\A vs. \a) being cleared when 'ignorecase' is set. Use case-sensitive comparison ==# instead.
- Refine :MarkLoad messages
- Add whitespace-indifferent visual mark configuration example. Thanks to Greg Klein for the suggestion.
mark.vba.gz 2.5.0 2011-05-07 7.2 Ingo Karkat - ENH: Add explicit mark persistence via :MarkLoad and :MarkSave commands and automatic persistence via the g:mwAutoLoadMarks and g:mwAutoSaveMarks configuration flags. (Request from Mun Johl, 16-Apr-2010)
- Expose toggling of mark display (keeping the mark patterns) via new <Plug>MarkToggle mapping. Offer :MarkClear command as a replacement for the old argumentless :Mark command, which now just disables, but not clears all marks.
mark.vba.gz 2.4.4 2011-04-19 7.0 Ingo Karkat - BUG: Include trailing newline character in check for current mark, so that a mark that matches the entire line (e.g. created by V<Leader>m) can be cleared via <Leader>n. Thanks to ping for reporting this.
- FIX: On overlapping marks, mark#CurrentMark() returned the lowest, not the highest visible mark. So on overlapping marks, the one that was not visible at the cursor position was removed; very confusing! Use reverse iteration order.  
- FIX: To avoid an arbitrary ordering of highlightings when the highlighting group names roll over, and to avoid order inconsistencies across different windows and tabs, we assign a different priority based on the highlighting group.
mark.vba.gz 2.4.3 2011-04-16 7.0 Ingo Karkat - Avoid losing the mark highlightings on :syn on or :colorscheme commands. Thanks to Zhou YiChao for alerting me to this issue and suggesting a fix.
- Made the script more robust when somehow no highlightings have been defined or when the window-local reckoning of match IDs got lost. I had very occasionally encountered such script errors in the past.
- Made global housekeeping variables script-local, only g:mwHistAdd is used for configuration.
- FIX: Capturing the visual selection could still clobber the blockwise yank mode of the unnamed register.
mark.vba.gz 2.4.1 2011-01-13 7.2 Ingo Karkat - FIX: Using a named register for capturing the visual selection on {Visual}<Leader>m and {Visual}<Leader>r clobbered the unnamed register. Now using the unnamed register.
mark.vba.gz 2.4.0 2010-07-29 7.0 Ingo Karkat ENH: The MarkSearch mappings (<Leader>[*#/?]) add the original cursor position to the jump list, like the built-in [/?*#nN] commands. This allows to use the regular jump commands for mark matches, like with regular search matches.
mark.vba.gz 2.3.3 2010-02-19 7.0 Ingo Karkat - BUG: Clearing of an accidental zero-width match (e.g. via :Mark \zs) results in endless loop. Thanks to Andy Wokula for the patch.
mark.vba.gz 2.3.2 2009-11-17 7.0 Ingo Karkat BUG: Creation of literal pattern via '\V' in {Visual}<Leader>m mapping collided with individual escaping done in <Leader>m mapping so that an escaped '\*' would be interpreted as a multi item when both modes are used for marking. Thanks to Andy Wokula for the patch.
mark.vba.gz 2.3.1 2009-08-03 7.0 Ingo Karkat - Now working correctly when 'smartcase' is set. All mappings and the :Mark command use 'ignorecase', but not 'smartcase'.
mark.vba.gz 2.3.0 2009-07-06 7.0 Ingo Karkat - All jump commands now take an optional [count], so you can quickly skip over some marks, as with the built-in */# and n/N commands. For this, the entire core search algorithm has been rewritten. The script's logic has been simplified through the use of Vim 7 features like Lists.
- Now also printing a Vim-alike search error message when 'nowrapscan' is set.
mark.vba.gz 2.2.0 2009-07-05 7.0 Ingo Karkat - Split off functions into autoload script.
- Initialization of global variables and autocommands is now done lazily on
  the first use, not during loading of the plugin. This reduces Vim startup
  time and footprint as long as the functionality isn't yet used.
- Split off documentation into separate help file. Now packaging as VimBall.
mark.vim 2.1.0 2009-07-02 7.0 Ingo Karkat THIS VERSION REQUIRES VIM 7.2 / 7.1 with patches, BUT IT'S WORTH UPDATING!
- Replaced highlighting via :syntax with matchadd() / matchdelete(). This
  requires Vim 7.2 / 7.1 with patches. This method is faster, there are no
  more clashes with syntax highlighting (:match always has preference), and
  the background highlighting does not disappear under 'cursorline'.
- Using winrestcmd() to fix effects of :windo: By entering a window, its
  height is potentially increased from 0 to 1.
- Handling multiple tabs by calling s:UpdateScope() on the TabEnter event.
mark.vim 2.0.0 2009-06-08 7.0 Ingo Karkat THIS VERSION REQUIRES VIM 7.0!
- Now using Vim List for g:mwWord and thus requiring Vim 7. g:mwCycle is now zero-based, but the syntax groups "MarkWordx" are still one-based.
- Factored :syntax operations out of s:DoMark() and s:UpdateMark() so that they can all be done in a single :windo.
- Normal mode <Plug>MarkSet now has the same semantics as its visual mode cousin: If the cursor is on an existing mark, the mark is removed. Beforehand, one could only remove a visually selected mark via again selecting it. Now, one simply can invoke the mapping when on such a mark.
mark.vim 1.6.1 2009-06-07 6.0 Ingo Karkat Initial upload
ip used for rating: 54.196.57.4

If you have questions or remarks about this site, visit the vimonline development pages. Please use this site responsibly.
Questions about Vim should go to the maillist. Help Bram help Uganda.
   
SourceForge.net Logo