To: vim_dev@googlegroups.com Subject: Patch 8.1.2302 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.2302 Problem: :lockmarks does not work for '[ and ']. Solution: save and restore '[ and '] marks. (James McCoy, closes #5222) Files: runtime/doc/motion.txt, src/bufwrite.c, src/diff.c, src/ex_cmds.c, src/fileio.c, src/indent.c, src/ops.c, src/register.c, src/testdir/test_autocmd.vim, src/testdir/test_diffmode.vim *** ../vim-8.1.2301/runtime/doc/motion.txt 2019-10-28 02:12:32.811459384 +0100 --- runtime/doc/motion.txt 2019-11-16 13:23:28.833347545 +0100 *************** *** 952,958 **** [` [count] times to lowercase mark before the cursor. ! :loc[kmarks] {command} *:loc* *:lockmarks* Execute {command} without adjusting marks. This is useful when changing text in a way that the line count will be the same when the change has completed. --- 950,956 ---- [` [count] times to lowercase mark before the cursor. ! :loc[kmarks] {command} *:loc* *:lock* *:lockmarks* Execute {command} without adjusting marks. This is useful when changing text in a way that the line count will be the same when the change has completed. *************** *** 966,971 **** --- 964,970 ---- - numbered marks '0 - '9 - last insert position '^ - last change position '. + - last affected text area '[ and '] - the Visual area '< and '> - line numbers in placed signs - line numbers in quickfix positions *************** *** 978,984 **** - folds - diffs ! :kee[pmarks] {command} *:kee* *:keepmarks* Currently only has effect for the filter command |:range!|: - When the number of lines after filtering is equal to --- 977,983 ---- - folds - diffs ! :kee[pmarks] {command} *:kee* *:keep* *:keepmarks* Currently only has effect for the filter command |:range!|: - When the number of lines after filtering is equal to *** ../vim-8.1.2301/src/bufwrite.c 2019-09-28 16:29:44.171649790 +0200 --- src/bufwrite.c 2019-11-16 13:21:05.006107783 +0100 *************** *** 683,688 **** --- 683,690 ---- context_sha256_T sha_ctx; #endif unsigned int bkc = get_bkc_value(buf); + pos_T orig_start = buf->b_op_start; + pos_T orig_end = buf->b_op_end; if (fname == NULL || *fname == NUL) // safety check return FAIL; *************** *** 875,880 **** --- 877,889 ---- #endif ) { + if (buf != NULL && cmdmod.lockmarks) + { + // restore the original '[ and '] positions + buf->b_op_start = orig_start; + buf->b_op_end = orig_end; + } + --no_wait_return; msg_scroll = msg_save; if (nofile_err) *************** *** 952,957 **** --- 961,973 ---- fname = buf->b_sfname; } + if (cmdmod.lockmarks) + { + // restore the original '[ and '] positions + buf->b_op_start = orig_start; + buf->b_op_end = orig_end; + } + #ifdef FEAT_NETBEANS_INTG if (netbeans_active() && isNetbeansBuffer(buf)) { *** ../vim-8.1.2301/src/diff.c 2019-11-10 21:00:21.418665688 +0100 --- src/diff.c 2019-11-16 13:38:26.849359577 +0100 *************** *** 772,777 **** --- 772,778 ---- { int r; char_u *save_ff; + int save_lockmarks; if (din->din_fname == NULL) return diff_write_buffer(buf, din); *************** *** 779,787 **** --- 780,793 ---- // Always use 'fileformat' set to "unix". save_ff = buf->b_p_ff; buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); + save_lockmarks = cmdmod.lockmarks; + // Writing the buffer is an implementation detail of performing the diff, + // so it shouldn't update the '[ and '] marks. + cmdmod.lockmarks = TRUE; r = buf_write(buf, din->din_fname, NULL, (linenr_T)1, buf->b_ml.ml_line_count, NULL, FALSE, FALSE, FALSE, TRUE); + cmdmod.lockmarks = save_lockmarks; free_string_option(buf->b_p_ff); buf->b_p_ff = save_ff; return r; *** ../vim-8.1.2301/src/ex_cmds.c 2019-11-09 20:00:32.325221562 +0100 --- src/ex_cmds.c 2019-11-16 13:21:05.010107764 +0100 *************** *** 749,756 **** foldMoveRange(&win->w_folds, line1, line2, dest); } #endif ! curbuf->b_op_start.lnum = dest - num_lines + 1; ! curbuf->b_op_end.lnum = dest; } else { --- 749,759 ---- foldMoveRange(&win->w_folds, line1, line2, dest); } #endif ! if (!cmdmod.lockmarks) ! { ! curbuf->b_op_start.lnum = dest - num_lines + 1; ! curbuf->b_op_end.lnum = dest; ! } } else { *************** *** 761,770 **** foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2); } #endif ! curbuf->b_op_start.lnum = dest + 1; ! curbuf->b_op_end.lnum = dest + num_lines; } ! curbuf->b_op_start.col = curbuf->b_op_end.col = 0; mark_adjust_nofold(last_line - num_lines + 1, last_line, -(last_line - dest - extra), 0L); --- 764,777 ---- foldMoveRange(&win->w_folds, dest + 1, line1 - 1, line2); } #endif ! if (!cmdmod.lockmarks) ! { ! curbuf->b_op_start.lnum = dest + 1; ! curbuf->b_op_end.lnum = dest + num_lines; ! } } ! if (!cmdmod.lockmarks) ! curbuf->b_op_start.col = curbuf->b_op_end.col = 0; mark_adjust_nofold(last_line - num_lines + 1, last_line, -(last_line - dest - extra), 0L); *************** *** 813,821 **** char_u *p; count = line2 - line1 + 1; ! curbuf->b_op_start.lnum = n + 1; ! curbuf->b_op_end.lnum = n + count; ! curbuf->b_op_start.col = curbuf->b_op_end.col = 0; /* * there are three situations: --- 820,831 ---- char_u *p; count = line2 - line1 + 1; ! if (!cmdmod.lockmarks) ! { ! curbuf->b_op_start.lnum = n + 1; ! curbuf->b_op_end.lnum = n + count; ! curbuf->b_op_start.col = curbuf->b_op_end.col = 0; ! } /* * there are three situations: *************** *** 1055,1064 **** --- 1065,1081 ---- char_u *cmd_buf; buf_T *old_curbuf = curbuf; int shell_flags = 0; + pos_T orig_start = curbuf->b_op_start; + pos_T orig_end = curbuf->b_op_end; + int save_lockmarks = cmdmod.lockmarks; if (*cmd == NUL) /* no filter command */ return; + // Temporarily disable lockmarks since that's needed to propagate changed + // regions of the buffer for foldUpdate(), linecount, etc. + cmdmod.lockmarks = 0; + cursor_save = curwin->w_cursor; linecount = line2 - line1 + 1; curwin->w_cursor.lnum = line1; *************** *** 1287,1297 **** --- 1304,1321 ---- filterend: + cmdmod.lockmarks = save_lockmarks; if (curbuf != old_curbuf) { --no_wait_return; emsg(_("E135: *Filter* Autocommands must not change current buffer")); } + else if (cmdmod.lockmarks) + { + curbuf->b_op_start = orig_start; + curbuf->b_op_end = orig_end; + } + if (itmp != NULL) mch_remove(itmp); if (otmp != NULL) *************** *** 3276,3288 **** * eap->line2 pointed to the end of the buffer and nothing was appended) * "end" is set to lnum when something has been appended, otherwise * it is the same than "start" -- Acevedo */ ! curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ? ! eap->line2 + 1 : curbuf->b_ml.ml_line_count; ! if (eap->cmdidx != CMD_append) ! --curbuf->b_op_start.lnum; ! curbuf->b_op_end.lnum = (eap->line2 < lnum) ! ? lnum : curbuf->b_op_start.lnum; ! curbuf->b_op_start.col = curbuf->b_op_end.col = 0; curwin->w_cursor.lnum = lnum; check_cursor_lnum(); beginline(BL_SOL | BL_FIX); --- 3300,3315 ---- * eap->line2 pointed to the end of the buffer and nothing was appended) * "end" is set to lnum when something has been appended, otherwise * it is the same than "start" -- Acevedo */ ! if (!cmdmod.lockmarks) ! { ! curbuf->b_op_start.lnum = (eap->line2 < curbuf->b_ml.ml_line_count) ? ! eap->line2 + 1 : curbuf->b_ml.ml_line_count; ! if (eap->cmdidx != CMD_append) ! --curbuf->b_op_start.lnum; ! curbuf->b_op_end.lnum = (eap->line2 < lnum) ! ? lnum : curbuf->b_op_start.lnum; ! curbuf->b_op_start.col = curbuf->b_op_end.col = 0; ! } curwin->w_cursor.lnum = lnum; check_cursor_lnum(); beginline(BL_SOL | BL_FIX); *************** *** 4592,4601 **** if (sub_nsubs > start_nsubs) { ! /* Set the '[ and '] marks. */ ! curbuf->b_op_start.lnum = eap->line1; ! curbuf->b_op_end.lnum = line2; ! curbuf->b_op_start.col = curbuf->b_op_end.col = 0; if (!global_busy) { --- 4619,4631 ---- if (sub_nsubs > start_nsubs) { ! if (!cmdmod.lockmarks) ! { ! // Set the '[ and '] marks. ! curbuf->b_op_start.lnum = eap->line1; ! curbuf->b_op_end.lnum = line2; ! curbuf->b_op_start.col = curbuf->b_op_end.col = 0; ! } if (!global_busy) { *** ../vim-8.1.2301/src/fileio.c 2019-09-28 16:29:44.171649790 +0200 --- src/fileio.c 2019-11-16 13:21:05.010107764 +0100 *************** *** 188,193 **** --- 188,194 ---- wasn't possible */ char_u conv_rest[CONV_RESTLEN]; int conv_restlen = 0; /* nr of bytes in conv_rest[] */ + pos_T orig_start; buf_T *old_curbuf; char_u *old_b_ffname; char_u *old_b_fname; *************** *** 250,258 **** */ if (!filtering && !read_stdin && !read_buffer) { ! pos_T pos; ! ! pos = curbuf->b_op_start; /* Set '[ mark to the line above where the lines go (line 1 if zero). */ curbuf->b_op_start.lnum = ((from == 0) ? 1 : from); --- 251,257 ---- */ if (!filtering && !read_stdin && !read_buffer) { ! orig_start = curbuf->b_op_start; /* Set '[ mark to the line above where the lines go (line 1 if zero). */ curbuf->b_op_start.lnum = ((from == 0) ? 1 : from); *************** *** 276,282 **** return OK; #endif ! curbuf->b_op_start = pos; } if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0) --- 275,281 ---- return OK; #endif ! curbuf->b_op_start = orig_start; } if ((shortmess(SHM_OVER) || curbuf->b_help) && p_verbose == 0) *************** *** 617,622 **** --- 616,622 ---- /* * Set '[ mark to the line above where the lines go (line 1 if zero). */ + orig_start = curbuf->b_op_start; curbuf->b_op_start.lnum = ((from == 0) ? 1 : from); curbuf->b_op_start.col = 0; *************** *** 658,663 **** --- 658,664 ---- try_mac = (vim_strchr(p_ffs, 'm') != NULL); try_dos = (vim_strchr(p_ffs, 'd') != NULL); try_unix = (vim_strchr(p_ffs, 'x') != NULL); + curbuf->b_op_start = orig_start; if (msg_scrolled == n) msg_scroll = m; *************** *** 2471,2483 **** check_cursor_lnum(); beginline(BL_WHITE | BL_FIX); /* on first non-blank */ ! /* ! * Set '[ and '] marks to the newly read lines. ! */ ! curbuf->b_op_start.lnum = from + 1; ! curbuf->b_op_start.col = 0; ! curbuf->b_op_end.lnum = from + linecnt; ! curbuf->b_op_end.col = 0; #ifdef MSWIN /* --- 2472,2485 ---- check_cursor_lnum(); beginline(BL_WHITE | BL_FIX); /* on first non-blank */ ! if (!cmdmod.lockmarks) ! { ! // Set '[ and '] marks to the newly read lines. ! curbuf->b_op_start.lnum = from + 1; ! curbuf->b_op_start.col = 0; ! curbuf->b_op_end.lnum = from + linecnt; ! curbuf->b_op_end.col = 0; ! } #ifdef MSWIN /* *** ../vim-8.1.2301/src/indent.c 2019-11-10 00:19:08.729688740 +0100 --- src/indent.c 2019-11-16 13:21:05.010107764 +0100 *************** *** 1001,1009 **** smsg(NGETTEXT("%ld line indented ", "%ld lines indented ", i), i); } ! // set '[ and '] marks ! curbuf->b_op_start = oap->start; ! curbuf->b_op_end = oap->end; } #endif // defined(FEAT_LISP) || defined(FEAT_CINDENT) --- 1001,1012 ---- smsg(NGETTEXT("%ld line indented ", "%ld lines indented ", i), i); } ! if (!cmdmod.lockmarks) ! { ! // set '[ and '] marks ! curbuf->b_op_start = oap->start; ! curbuf->b_op_end = oap->end; ! } } #endif // defined(FEAT_LISP) || defined(FEAT_CINDENT) *** ../vim-8.1.2301/src/ops.c 2019-11-12 20:31:16.568776952 +0100 --- src/ops.c 2019-11-16 13:21:05.014107742 +0100 *************** *** 206,219 **** msg((char *)IObuff); } ! /* ! * Set "'[" and "']" marks. ! */ ! curbuf->b_op_start = oap->start; ! curbuf->b_op_end.lnum = oap->end.lnum; ! curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); ! if (curbuf->b_op_end.col > 0) ! --curbuf->b_op_end.col; } /* --- 206,220 ---- msg((char *)IObuff); } ! if (!cmdmod.lockmarks) ! { ! // Set "'[" and "']" marks. ! curbuf->b_op_start = oap->start; ! curbuf->b_op_end.lnum = oap->end.lnum; ! curbuf->b_op_end.col = (colnr_T)STRLEN(ml_get(oap->end.lnum)); ! if (curbuf->b_op_end.col > 0) ! --curbuf->b_op_end.col; ! } } /* *************** *** 981,994 **** msgmore(curbuf->b_ml.ml_line_count - old_lcount); setmarks: ! if (oap->block_mode) { ! curbuf->b_op_end.lnum = oap->end.lnum; ! curbuf->b_op_end.col = oap->start.col; } - else - curbuf->b_op_end = oap->start; - curbuf->b_op_start = oap->start; return OK; } --- 982,998 ---- msgmore(curbuf->b_ml.ml_line_count - old_lcount); setmarks: ! if (!cmdmod.lockmarks) { ! if (oap->block_mode) ! { ! curbuf->b_op_end.lnum = oap->end.lnum; ! curbuf->b_op_end.col = oap->start.col; ! } ! else ! curbuf->b_op_end = oap->start; ! curbuf->b_op_start = oap->start; } return OK; } *************** *** 1252,1260 **** check_cursor(); changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L); ! /* Set "'[" and "']" marks. */ ! curbuf->b_op_start = oap->start; ! curbuf->b_op_end = oap->end; return OK; } --- 1256,1267 ---- check_cursor(); changed_lines(oap->start.lnum, oap->start.col, oap->end.lnum + 1, 0L); ! if (!cmdmod.lockmarks) ! { ! /* Set "'[" and "']" marks. */ ! curbuf->b_op_start = oap->start; ! curbuf->b_op_end = oap->end; ! } return OK; } *************** *** 1362,1372 **** /* No change: need to remove the Visual selection */ redraw_curbuf_later(INVERTED); ! /* ! * Set '[ and '] marks. ! */ ! curbuf->b_op_start = oap->start; ! curbuf->b_op_end = oap->end; if (oap->line_count > p_report) smsg(NGETTEXT("%ld line changed", "%ld lines changed", --- 1369,1380 ---- /* No change: need to remove the Visual selection */ redraw_curbuf_later(INVERTED); ! if (!cmdmod.lockmarks) ! { ! // Set '[ and '] marks. ! curbuf->b_op_start = oap->start; ! curbuf->b_op_end = oap->end; ! } if (oap->line_count > p_report) smsg(NGETTEXT("%ld line changed", "%ld lines changed", *************** *** 1973,1979 **** for (t = 0; t < count; ++t) { curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t)); ! if (t == 0 && setmark) { /* Set the '[ mark. */ curwin->w_buffer->b_op_start.lnum = curwin->w_cursor.lnum; --- 1981,1987 ---- for (t = 0; t < count; ++t) { curr = curr_start = ml_get((linenr_T)(curwin->w_cursor.lnum + t)); ! if (t == 0 && setmark && !cmdmod.lockmarks) { /* Set the '[ mark. */ curwin->w_buffer->b_op_start.lnum = curwin->w_cursor.lnum; *************** *** 2129,2135 **** #endif ml_replace(curwin->w_cursor.lnum, newp, FALSE); ! if (setmark) { /* Set the '] mark. */ curwin->w_buffer->b_op_end.lnum = curwin->w_cursor.lnum; --- 2137,2143 ---- #endif ml_replace(curwin->w_cursor.lnum, newp, FALSE); ! if (setmark && !cmdmod.lockmarks) { /* Set the '] mark. */ curwin->w_buffer->b_op_end.lnum = curwin->w_cursor.lnum; *************** *** 2268,2275 **** /* When there is no change: need to remove the Visual selection */ redraw_curbuf_later(INVERTED); ! /* Set '[ mark at the start of the formatted area */ ! curbuf->b_op_start = oap->start; /* For "gw" remember the cursor position and put it back below (adjusted * for joined and split lines). */ --- 2276,2284 ---- /* When there is no change: need to remove the Visual selection */ redraw_curbuf_later(INVERTED); ! if (!cmdmod.lockmarks) ! /* Set '[ mark at the start of the formatted area */ ! curbuf->b_op_start = oap->start; /* For "gw" remember the cursor position and put it back below (adjusted * for joined and split lines). */ *************** *** 2289,2296 **** old_line_count = curbuf->b_ml.ml_line_count - old_line_count; msgmore(old_line_count); ! /* put '] mark on the end of the formatted area */ ! curbuf->b_op_end = curwin->w_cursor; if (keep_cursor) { --- 2298,2306 ---- old_line_count = curbuf->b_ml.ml_line_count - old_line_count; msgmore(old_line_count); ! if (!cmdmod.lockmarks) ! /* put '] mark on the end of the formatted area */ ! curbuf->b_op_end = curwin->w_cursor; if (keep_cursor) { *************** *** 2984,2990 **** /* Set '[ mark if something changed. Keep the last end * position from do_addsub(). */ ! if (change_cnt > 0) curbuf->b_op_start = startpos; if (change_cnt > p_report) --- 2994,3000 ---- /* Set '[ mark if something changed. Keep the last end * position from do_addsub(). */ ! if (change_cnt > 0 && !cmdmod.lockmarks) curbuf->b_op_start = startpos; if (change_cnt > p_report) *************** *** 3384,3390 **** --curwin->w_cursor.col; } ! if (did_change) { /* set the '[ and '] marks */ curbuf->b_op_start = startpos; --- 3394,3400 ---- --curwin->w_cursor.col; } ! if (did_change && !cmdmod.lockmarks) { /* set the '[ and '] marks */ curbuf->b_op_start = startpos; *************** *** 3905,3910 **** --- 3915,3922 ---- #ifdef FEAT_EVAL typval_T argv[2]; int save_virtual_op = virtual_op; + pos_T orig_start = curbuf->b_op_start; + pos_T orig_end = curbuf->b_op_end; if (*p_opfunc == NUL) emsg(_("E774: 'operatorfunc' is empty")); *************** *** 3933,3938 **** --- 3945,3955 ---- (void)call_func_retnr(p_opfunc, 1, argv); virtual_op = save_virtual_op; + if (cmdmod.lockmarks) + { + curbuf->b_op_start = orig_start; + curbuf->b_op_end = orig_end; + } } #else emsg(_("E775: Eval feature not available")); *** ../vim-8.1.2301/src/register.c 2019-11-02 22:54:37.409188799 +0100 --- src/register.c 2019-11-16 13:21:05.014107742 +0100 *************** *** 1316,1328 **** } } ! // Set "'[" and "']" marks. ! curbuf->b_op_start = oap->start; ! curbuf->b_op_end = oap->end; ! if (yanktype == MLINE && !oap->block_mode) { ! curbuf->b_op_start.col = 0; ! curbuf->b_op_end.col = MAXCOL; } #ifdef FEAT_CLIPBOARD --- 1316,1331 ---- } } ! if (!cmdmod.lockmarks) { ! // Set "'[" and "']" marks. ! curbuf->b_op_start = oap->start; ! curbuf->b_op_end = oap->end; ! if (yanktype == MLINE && !oap->block_mode) ! { ! curbuf->b_op_start.col = 0; ! curbuf->b_op_end.col = MAXCOL; ! } } #ifdef FEAT_CLIPBOARD *************** *** 1474,1479 **** --- 1477,1484 ---- char_u *insert_string = NULL; int allocated = FALSE; long cnt; + pos_T orig_start = curbuf->b_op_start; + pos_T orig_end = curbuf->b_op_end; #ifdef FEAT_CLIPBOARD // Adjust register name for "unnamed" in 'clipboard'. *************** *** 2100,2105 **** --- 2105,2115 ---- curwin->w_set_curswant = TRUE; end: + if (cmdmod.lockmarks) + { + curbuf->b_op_start = orig_start; + curbuf->b_op_end = orig_end; + } if (allocated) vim_free(insert_string); if (regname == '=') *** ../vim-8.1.2301/src/testdir/test_autocmd.vim 2019-11-10 21:25:41.137362272 +0100 --- src/testdir/test_autocmd.vim 2019-11-16 13:21:05.014107742 +0100 *************** *** 2297,2299 **** --- 2297,2333 ---- split au! WinEnter endfunc + + func Test_BufWrite_lockmarks() + edit! Xtest + call setline(1, ['a', 'b', 'c', 'd']) + + " :lockmarks preserves the marks + call SetChangeMarks(2, 3) + lockmarks write + call assert_equal([2, 3], [line("'["), line("']")]) + + " *WritePre autocmds get the correct line range, but lockmarks preserves the + " original values for the user + augroup lockmarks + au! + au BufWritePre,FilterWritePre * call assert_equal([1, 4], [line("'["), line("']")]) + au FileWritePre * call assert_equal([3, 4], [line("'["), line("']")]) + augroup END + + lockmarks write + call assert_equal([2, 3], [line("'["), line("']")]) + + if executable('cat') + lockmarks %!cat + call assert_equal([2, 3], [line("'["), line("']")]) + endif + + lockmarks 3,4write Xtest2 + call assert_equal([2, 3], [line("'["), line("']")]) + + au! lockmarks + augroup! lockmarks + call delete('Xtest') + call delete('Xtest2') + endfunc *** ../vim-8.1.2301/src/testdir/test_diffmode.vim 2019-11-10 21:00:21.422665678 +0100 --- src/testdir/test_diffmode.vim 2019-11-16 13:21:05.014107742 +0100 *************** *** 989,991 **** --- 989,1010 ---- diffoff! enew! endfunc + + func Test_diff_maintains_change_mark() + enew! + call setline(1, ['a', 'b', 'c', 'd']) + diffthis + new + call setline(1, ['a', 'b', 'c', 'e']) + " Set '[ and '] marks + 2,3yank + call assert_equal([2, 3], [line("'["), line("']")]) + " Verify they aren't affected by the implicit diff + diffthis + call assert_equal([2, 3], [line("'["), line("']")]) + " Verify they aren't affected by an explicit diff + diffupdate + call assert_equal([2, 3], [line("'["), line("']")]) + bwipe! + bwipe! + endfunc *** ../vim-8.1.2301/src/version.c 2019-11-15 22:41:10.915860955 +0100 --- src/version.c 2019-11-16 13:24:02.561161708 +0100 *************** *** 743,744 **** --- 743,746 ---- { /* Add new patch number below this line */ + /**/ + 2302, /**/ -- From "know your smileys": :-)-O Smiling doctor with stethoscope /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///