Patch 8.1.1612

11 views
Skip to first unread message

Bram Moolenaar

unread,
Jun 30, 2019, 4:16:35 PM6/30/19
to vim...@googlegroups.com

Patch 8.1.1612
Problem: Cannot show an existing buffer in a popup window.
Solution: Support buffer number argument in popup_create().
Files: src/buffer.c, src/proto/buffer.pro, src/evalfunc.c,
src/popupwin.c, src/vim.h, src/normal.c, src/screen.c, src/ui.c,
src/window.c, src/testdir/test_popupwin.vim, runtime/doc/popup.txt


*** ../vim-8.1.1611/src/buffer.c 2019-06-20 03:45:31.167536959 +0200
--- src/buffer.c 2019-06-30 20:52:17.048551781 +0200
***************
*** 122,127 ****
--- 122,144 ----
}

/*
+ * Ensure buffer "buf" is loaded. Does not trigger the swap-exists action.
+ */
+ void
+ buffer_ensure_loaded(buf_T *buf)
+ {
+ if (buf->b_ml.ml_mfp == NULL)
+ {
+ aco_save_T aco;
+
+ aucmd_prepbuf(&aco, buf);
+ swap_exists_action = SEA_NONE;
+ open_buffer(FALSE, NULL, 0);
+ aucmd_restbuf(&aco);
+ }
+ }
+
+ /*
* Open current buffer, that is: open the memfile and read the file into
* memory.
* Return FAIL for failure, OK otherwise.
*** ../vim-8.1.1611/src/proto/buffer.pro 2019-06-20 03:45:31.171536943 +0200
--- src/proto/buffer.pro 2019-06-30 21:11:44.776703059 +0200
***************
*** 1,4 ****
--- 1,5 ----
/* buffer.c */
+ void buffer_ensure_loaded(buf_T *buf);
int open_buffer(int read_stdin, exarg_T *eap, int flags);
void set_bufref(bufref_T *bufref, buf_T *buf);
int bufref_valid(bufref_T *bufref);
*** ../vim-8.1.1611/src/evalfunc.c 2019-06-30 20:32:58.424000713 +0200
--- src/evalfunc.c 2019-06-30 21:06:14.166871080 +0200
***************
*** 1963,1977 ****
{
buf_T *buf = get_buf_arg(&argvars[0]);

! if (buf != NULL && buf->b_ml.ml_mfp == NULL)
! {
! aco_save_T aco;
!
! aucmd_prepbuf(&aco, buf);
! swap_exists_action = SEA_NONE;
! open_buffer(FALSE, NULL, 0);
! aucmd_restbuf(&aco);
! }
}

/*
--- 1963,1970 ----
{
buf_T *buf = get_buf_arg(&argvars[0]);

! if (buf != NULL)
! buffer_ensure_loaded(buf);
}

/*
***************
*** 4905,4911 ****
return;
(void)mouse_comp_pos(win, &row, &col, &lnum);
# ifdef FEAT_TEXT_PROP
! if (bt_popup(win->w_buffer))
winnr = 0;
else
# endif
--- 4898,4904 ----
return;
(void)mouse_comp_pos(win, &row, &col, &lnum);
# ifdef FEAT_TEXT_PROP
! if (WIN_IS_POPUP(win))
winnr = 0;
else
# endif
*** ../vim-8.1.1611/src/popupwin.c 2019-06-30 18:04:53.793559360 +0200
--- src/popupwin.c 2019-06-30 21:46:23.763132010 +0200
***************
*** 997,1010 ****
win_T *wp;
tabpage_T *tp = NULL;
int tabnr;
! buf_T *buf;
dict_T *d;
int nr;
int i;

// Check arguments look OK.
! if (!(argvars[0].v_type == VAR_STRING && argvars[0].vval.v_string != NULL)
! && !(argvars[0].v_type == VAR_LIST && argvars[0].vval.v_list != NULL))
{
emsg(_(e_listreq));
return NULL;
--- 997,1022 ----
win_T *wp;
tabpage_T *tp = NULL;
int tabnr;
! int new_buffer;
! buf_T *buf = NULL;
dict_T *d;
int nr;
int i;

// Check arguments look OK.
! if (argvars[0].v_type == VAR_NUMBER)
! {
! buf = buflist_findnr( argvars[0].vval.v_number);
! if (buf == NULL)
! {
! semsg(_(e_nobufnr), argvars[0].vval.v_number);
! return NULL;
! }
! }
! else if (!(argvars[0].v_type == VAR_STRING
! && argvars[0].vval.v_string != NULL)
! && !(argvars[0].v_type == VAR_LIST
! && argvars[0].vval.v_list != NULL))
{
emsg(_(e_listreq));
return NULL;
***************
*** 1038,1064 ****
return NULL;
rettv->vval.v_number = wp->w_id;
wp->w_popup_pos = POPPOS_TOPLEFT;

! buf = buflist_new(NULL, NULL, (linenr_T)0, BLN_NEW|BLN_LISTED|BLN_DUMMY);
! if (buf == NULL)
! return NULL;
! ml_open(buf);

! win_init_popup_win(wp, buf);

! set_local_options_default(wp);
! set_string_option_direct_in_buf(buf, (char_u *)"buftype", -1,
(char_u *)"popup", OPT_FREE|OPT_LOCAL, 0);
! set_string_option_direct_in_buf(buf, (char_u *)"bufhidden", -1,
! (char_u *)"hide", OPT_FREE|OPT_LOCAL, 0);
! buf->b_p_ul = -1; // no undo
! buf->b_p_swf = FALSE; // no swap file
! buf->b_p_bl = FALSE; // unlisted buffer
! buf->b_locked = TRUE;
! wp->w_p_wrap = TRUE; // 'wrap' is default on

! // Avoid that 'buftype' is reset when this buffer is entered.
! buf->b_p_initialized = TRUE;

if (tp != NULL)
{
--- 1050,1091 ----
return NULL;
rettv->vval.v_number = wp->w_id;
wp->w_popup_pos = POPPOS_TOPLEFT;
+ wp->w_popup_flags = POPF_IS_POPUP;

! if (buf != NULL)
! {
! // use existing buffer
! new_buffer = FALSE;
! wp->w_buffer = buf;
! ++buf->b_nwindows;
! buffer_ensure_loaded(buf);
! }
! else
! {
! // create a new buffer associated with the popup
! new_buffer = TRUE;
! buf = buflist_new(NULL, NULL, (linenr_T)0,
! BLN_NEW|BLN_LISTED|BLN_DUMMY);
! if (buf == NULL)
! return NULL;
! ml_open(buf);

! win_init_popup_win(wp, buf);

! set_local_options_default(wp);
! set_string_option_direct_in_buf(buf, (char_u *)"buftype", -1,
(char_u *)"popup", OPT_FREE|OPT_LOCAL, 0);
! set_string_option_direct_in_buf(buf, (char_u *)"bufhidden", -1,
! (char_u *)"hide", OPT_FREE|OPT_LOCAL, 0);
! buf->b_p_ul = -1; // no undo
! buf->b_p_swf = FALSE; // no swap file
! buf->b_p_bl = FALSE; // unlisted buffer
! buf->b_locked = TRUE;
! wp->w_p_wrap = TRUE; // 'wrap' is default on

! // Avoid that 'buftype' is reset when this buffer is entered.
! buf->b_p_initialized = TRUE;
! }

if (tp != NULL)
{
***************
*** 1088,1094 ****
}
}

! popup_set_buffer_text(buf, argvars[0]);

if (type == TYPE_ATCURSOR)
{
--- 1115,1122 ----
}
}

! if (new_buffer)
! popup_set_buffer_text(buf, argvars[0]);

if (type == TYPE_ATCURSOR)
{
***************
*** 1456,1462 ****
{
win_T *wp = win_id2wp(id);

! if (wp != NULL && !bt_popup(wp->w_buffer))
{
semsg(_("E993: window %d is not a popup window"), id);
return NULL;
--- 1484,1490 ----
{
win_T *wp = win_id2wp(id);

! if (wp != NULL && !WIN_IS_POPUP(wp))
{
semsg(_("E993: window %d is not a popup window"), id);
return NULL;
***************
*** 1524,1531 ****

if (wp != NULL)
{
! popup_set_buffer_text(wp->w_buffer, argvars[1]);
! popup_adjust_position(wp);
}
}

--- 1552,1564 ----

if (wp != NULL)
{
! if (argvars[1].v_type != VAR_STRING && argvars[1].v_type != VAR_LIST)
! semsg(_(e_invarg2), tv_get_string(&argvars[1]));
! else
! {
! popup_set_buffer_text(wp->w_buffer, argvars[1]);
! popup_adjust_position(wp);
! }
}
}

***************
*** 1880,1886 ****
int
error_if_popup_window()
{
! if (bt_popup(curwin->w_buffer))
{
emsg(_("E994: Not allowed in a popup window"));
return TRUE;
--- 1913,1919 ----
int
error_if_popup_window()
{
! if (WIN_IS_POPUP(curwin))
{
emsg(_("E994: Not allowed in a popup window"));
return TRUE;
*** ../vim-8.1.1611/src/vim.h 2019-06-23 00:15:02.581534910 +0200
--- src/vim.h 2019-06-30 21:05:19.151234091 +0200
***************
*** 614,621 ****
#define VALID_TOPLINE 0x80 // w_topline is valid (for cursor position)

// Values for w_popup_flags.
! #define POPF_HIDDEN 1 // popup is not displayed
! #define POPF_HANDLED 2 // popup was just redrawn or filtered

/*
* Terminal highlighting attribute bits.
--- 614,627 ----
#define VALID_TOPLINE 0x80 // w_topline is valid (for cursor position)

// Values for w_popup_flags.
! #define POPF_IS_POPUP 1 // this is a popup window
! #define POPF_HIDDEN 2 // popup is not displayed
! #define POPF_HANDLED 4 // popup was just redrawn or filtered
! #ifdef FEAT_TEXT_PROP
! # define WIN_IS_POPUP(wp) ((wp)->w_popup_flags != 0)
! #else
! # define WIN_IS_POPUP(wp) 0
! #endif

/*
* Terminal highlighting attribute bits.
*** ../vim-8.1.1611/src/normal.c 2019-06-29 07:36:05.348264881 +0200
--- src/normal.c 2019-06-30 21:07:40.414303484 +0200
***************
*** 4525,4531 ****
if (wp == NULL)
return;
#ifdef FEAT_TEXT_PROP
! if (bt_popup(wp->w_buffer) && !wp->w_has_scrollbar)
return;
#endif
curwin = wp;
--- 4525,4531 ----
if (wp == NULL)
return;
#ifdef FEAT_TEXT_PROP
! if (WIN_IS_POPUP(wp) && !wp->w_has_scrollbar)
return;
#endif
curwin = wp;
***************
*** 4560,4566 ****
nv_scroll_line(cap);
}
#ifdef FEAT_TEXT_PROP
! if (bt_popup(curwin->w_buffer))
popup_set_firstline(curwin);
#endif
}
--- 4560,4566 ----
nv_scroll_line(cap);
}
#ifdef FEAT_TEXT_PROP
! if (WIN_IS_POPUP(curwin))
popup_set_firstline(curwin);
#endif
}
*** ../vim-8.1.1611/src/screen.c 2019-06-28 21:37:56.758355954 +0200
--- src/screen.c 2019-06-30 21:10:16.537279829 +0200
***************
*** 1005,1011 ****
if (*wp->w_p_wcr != NUL)
wcr_attr = syn_name2attr(wp->w_p_wcr);
#ifdef FEAT_TEXT_PROP
! if (bt_popup(wp->w_buffer) && wcr_attr == 0)
wcr_attr = HL_ATTR(HLF_PNI);
#endif
return wcr_attr;
--- 1005,1011 ----
if (*wp->w_p_wcr != NUL)
wcr_attr = syn_name2attr(wp->w_p_wcr);
#ifdef FEAT_TEXT_PROP
! if (WIN_IS_POPUP(wp) && wcr_attr == 0)
wcr_attr = HL_ATTR(HLF_PNI);
#endif
return wcr_attr;
***************
*** 1555,1565 ****
if (mid_start == 0)
{
mid_end = wp->w_height;
! if (ONE_WINDOW
! #ifdef FEAT_TEXT_PROP
! && !bt_popup(wp->w_buffer)
! #endif
! )
{
/* Clear the screen when it was not done by win_del_lines() or
* win_ins_lines() above, "screen_cleared" is FALSE or MAYBE
--- 1555,1561 ----
if (mid_start == 0)
{
mid_end = wp->w_height;
! if (ONE_WINDOW && !WIN_IS_POPUP(wp))
{
/* Clear the screen when it was not done by win_del_lines() or
* win_ins_lines() above, "screen_cleared" is FALSE or MAYBE
***************
*** 2085,2093 ****
&& wp->w_lines[idx].wl_lnum == lnum
&& lnum > wp->w_topline
&& !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
! #ifdef FEAT_TEXT_PROP
! && !bt_popup(wp->w_buffer)
! #endif
&& srow + wp->w_lines[idx].wl_size > wp->w_height
#ifdef FEAT_DIFF
&& diff_check_fill(wp, lnum) == 0
--- 2081,2087 ----
&& wp->w_lines[idx].wl_lnum == lnum
&& lnum > wp->w_topline
&& !(dy_flags & (DY_LASTLINE | DY_TRUNCATE))
! && !WIN_IS_POPUP(wp)
&& srow + wp->w_lines[idx].wl_size > wp->w_height
#ifdef FEAT_DIFF
&& diff_check_fill(wp, lnum) == 0
***************
*** 2244,2250 ****
}
#endif
#ifdef FEAT_TEXT_PROP
! else if (bt_popup(wp->w_buffer))
{
// popup line that doesn't fit is left as-is
wp->w_botline = lnum;
--- 2238,2244 ----
}
#endif
#ifdef FEAT_TEXT_PROP
! else if (WIN_IS_POPUP(wp))
{
// popup line that doesn't fit is left as-is
wp->w_botline = lnum;
***************
*** 2310,2320 ****

// Make sure the rest of the screen is blank
// put '~'s on rows that aren't part of the file.
! win_draw_end(wp,
! #ifdef FEAT_TEXT_PROP
! bt_popup(wp->w_buffer) ? ' ' :
! #endif
! '~', ' ', FALSE, row, wp->w_height, HLF_EOB);
}

#ifdef SYN_TIME_LIMIT
--- 2304,2311 ----

// Make sure the rest of the screen is blank
// put '~'s on rows that aren't part of the file.
! win_draw_end(wp, WIN_IS_POPUP(wp) ? ' ' : '~',
! ' ', FALSE, row, wp->w_height, HLF_EOB);
}

#ifdef SYN_TIME_LIMIT
***************
*** 3673,3679 ****
area_highlighting = TRUE;
}
#ifdef FEAT_TEXT_PROP
! if (bt_popup(wp->w_buffer))
screen_line_flags |= SLF_POPUP;
#endif

--- 3664,3670 ----
area_highlighting = TRUE;
}
#ifdef FEAT_TEXT_PROP
! if (WIN_IS_POPUP(wp))
screen_line_flags |= SLF_POPUP;
#endif

*** ../vim-8.1.1611/src/ui.c 2019-06-30 18:04:53.793559360 +0200
--- src/ui.c 2019-06-30 21:10:52.817042529 +0200
***************
*** 1078,1084 ****
int col_cp = col;

wp = mouse_find_win(&row_cp, &col_cp, FIND_POPUP);
! if (wp != NULL && bt_popup(wp->w_buffer))
{
// Click in a popup window restricts selection to that window,
// excluding the border.
--- 1078,1084 ----
int col_cp = col;

wp = mouse_find_win(&row_cp, &col_cp, FIND_POPUP);
! if (wp != NULL && WIN_IS_POPUP(wp))
{
// Click in a popup window restricts selection to that window,
// excluding the border.
***************
*** 3052,3058 ****
#ifdef FEAT_TEXT_PROP
// Click in a popup window may start dragging or modeless selection,
// but not much else.
! if (bt_popup(wp->w_buffer))
{
on_sep_line = 0;
in_popup_win = TRUE;
--- 3052,3058 ----
#ifdef FEAT_TEXT_PROP
// Click in a popup window may start dragging or modeless selection,
// but not much else.
! if (WIN_IS_POPUP(wp))
{
on_sep_line = 0;
in_popup_win = TRUE;
*** ../vim-8.1.1611/src/window.c 2019-06-26 05:13:51.799753098 +0200
--- src/window.c 2019-06-30 21:38:18.710290147 +0200
***************
*** 4887,4893 ****
int
win_unlisted(win_T *wp)
{
! return wp == aucmd_win || bt_popup(wp->w_buffer);
}

#if defined(FEAT_TEXT_PROP) || defined(PROTO)
--- 4887,4893 ----
int
win_unlisted(win_T *wp)
{
! return wp == aucmd_win || WIN_IS_POPUP(wp);
}

#if defined(FEAT_TEXT_PROP) || defined(PROTO)
***************
*** 4898,4904 ****
void
win_free_popup(win_T *win)
{
! win_close_buffer(win, DOBUF_WIPE, FALSE);
# if defined(FEAT_TIMERS)
if (win->w_popup_timer != NULL)
stop_timer(win->w_popup_timer);
--- 4898,4907 ----
void
win_free_popup(win_T *win)
{
! if (bt_popup(win->w_buffer))
! win_close_buffer(win, DOBUF_WIPE, FALSE);
! else
! close_buffer(win, win->w_buffer, 0, FALSE);
# if defined(FEAT_TIMERS)
if (win->w_popup_timer != NULL)
stop_timer(win->w_popup_timer);
***************
*** 6605,6611 ****
curbuf = curwin->w_buffer;
}
#ifdef FEAT_TEXT_PROP
! else if (bt_popup(curwin->w_buffer))
// original window was closed and now we're in a popup window: Go
// to the first valid window.
win_goto(firstwin);
--- 6608,6614 ----
curbuf = curwin->w_buffer;
}
#ifdef FEAT_TEXT_PROP
! else if (WIN_IS_POPUP(curwin))
// original window was closed and now we're in a popup window: Go
// to the first valid window.
win_goto(firstwin);
*** ../vim-8.1.1611/src/testdir/test_popupwin.vim 2019-06-30 18:04:53.793559360 +0200
--- src/testdir/test_popupwin.vim 2019-06-30 22:13:41.956739605 +0200
***************
*** 559,565 ****
endfunc

func Test_popup_invalid_arguments()
! call assert_fails('call popup_create(666, {})', 'E714:')
call popup_clear()
call assert_fails('call popup_create("text", "none")', 'E715:')
call popup_clear()
--- 559,565 ----
endfunc

func Test_popup_invalid_arguments()
! call assert_fails('call popup_create(666, {})', 'E86:')
call popup_clear()
call assert_fails('call popup_create("text", "none")', 'E715:')
call popup_clear()
***************
*** 1654,1656 ****
--- 1654,1671 ----
call popup_close(winid)
delfunc MyPopupFilter
endfunc
+
+ func Test_popupwin_with_buffer()
+ call writefile(['some text', 'in a buffer'], 'XsomeFile')
+ let buf = bufadd('XsomeFile')
+ call assert_equal(0, bufloaded(buf))
+ let winid = popup_create(buf, {})
+ call assert_notequal(0, winid)
+ let pos = popup_getpos(winid)
+ call assert_equal(2, pos.height)
+ call assert_equal(1, bufloaded(buf))
+ call popup_close(winid)
+ call assert_equal({}, popup_getpos(winid))
+ call assert_equal(1, bufloaded(buf))
+ exe 'bwipe! ' .. buf
+ endfunc
*** ../vim-8.1.1611/runtime/doc/popup.txt 2019-06-30 18:04:53.793559360 +0200
--- runtime/doc/popup.txt 2019-06-30 22:13:18.380888563 +0200
***************
*** 98,106 ****


TODO:
- - Currently 'buftype' is set to "popup", but all the specifics are on the
- window. Can we use a "normal" buffer and put the type on the window? (#4595)
- What if it's modified and the window closes?
- Add test for when popup with mask is off the left and off the right of the
screen.
- check padding/border when popup is off the left and right of the screen.
--- 98,103 ----
***************
*** 164,173 ****

[functions help to be moved to eval.txt later]

! popup_atcursor({text}, {options}) *popup_atcursor()*
! Show the {text} above the cursor, and close it when the cursor
moves. This works like: >
! call popup_create({text}, {
\ 'pos': 'botleft',
\ 'line': 'cursor-1',
\ 'col': 'cursor',
--- 161,170 ----

[functions help to be moved to eval.txt later]

! popup_atcursor({what}, {options}) *popup_atcursor()*
! Show the {what} above the cursor, and close it when the cursor
moves. This works like: >
! call popup_create({what}, {
\ 'pos': 'botleft',
\ 'line': 'cursor-1',
\ 'col': 'cursor',
***************
*** 191,201 ****
Otherwise zero is passed to the callback.


! popup_create({text}, {options}) *popup_create()*
! Open a popup window showing {text}, which is either:
- a string
- a list of strings
- a list of text lines with text properties

{options} is a dictionary with many possible entries.
See |popup_create-usage| for details.
--- 188,202 ----
Otherwise zero is passed to the callback.


! popup_create({what}, {options}) *popup_create()*
! Open a popup window showing {what}, which is either:
! - a buffer number
- a string
- a list of strings
- a list of text lines with text properties
+ When {what} is not a buffer number, a buffer is created with
+ 'buftype' set to "popup". That buffer will be wiped out once
+ the popup closes.

{options} is a dictionary with many possible entries.
See |popup_create-usage| for details.
***************
*** 209,217 ****
< In case of failure zero is returned.


! popup_dialog({text}, {options}) *popup_dialog()*
Just like |popup_create()| but with these default options: >
! call popup_create({text}, {
\ 'pos': 'center',
\ 'zindex': 200,
\ 'drag': 1,
--- 210,218 ----
< In case of failure zero is returned.


! popup_dialog({what}, {options}) *popup_dialog()*
Just like |popup_create()| but with these default options: >
! call popup_create({what}, {
\ 'pos': 'center',
\ 'zindex': 200,
\ 'drag': 1,
***************
*** 312,323 ****
exists but is not a popup window an error is given. *E993*


! popup_menu({text}, {options}) *popup_menu()*
! Show the {text} near the cursor, handle selecting one of the
items with cursorkeys, and close it an item is selected with
! Space or Enter. {text} should have multiple lines to make this
useful. This works like: >
! call popup_create({text}, {
\ 'pos': 'center',
\ 'zindex': 200,
\ 'drag': 1,
--- 313,324 ----
exists but is not a popup window an error is given. *E993*


! popup_menu({what}, {options}) *popup_menu()*
! Show the {what} near the cursor, handle selecting one of the
items with cursorkeys, and close it an item is selected with
! Space or Enter. {what} should have multiple lines to make this
useful. This works like: >
! call popup_create({what}, {
\ 'pos': 'center',
\ 'zindex': 200,
\ 'drag': 1,
***************
*** 349,358 ****
For other options see |popup_setoptions()|.


! popup_notification({text}, {options}) *popup_notification()*
! Show the {text} for 3 seconds at the top of the Vim window.
This works like: >
! call popup_create({text}, {
\ 'line': 1,
\ 'col': 10,
\ 'minwidth': 20,
--- 350,359 ----
For other options see |popup_setoptions()|.


! popup_notification({what}, {options}) *popup_notification()*
! Show the {what} for 3 seconds at the top of the Vim window.
This works like: >
! call popup_create({what}, {
\ 'line': 1,
\ 'col': 10,
\ 'minwidth': 20,
***************
*** 410,416 ****

popup_settext({id}, {text}) *popup_settext()*
Set the text of the buffer in poup win {id}. {text} is the
! same as supplied to |popup_create()|.
Does not change the window size or position, other than caused
by the different text.

--- 411,418 ----

popup_settext({id}, {text}) *popup_settext()*
Set the text of the buffer in poup win {id}. {text} is the
! same as supplied to |popup_create()|, except that a buffer
! number is not allowed.
Does not change the window size or position, other than caused
by the different text.

***************
*** 450,456 ****

The first argument of |popup_create()| (and the second argument to
|popup_settext()|) specifies the text to be displayed, and optionally text
! properties. It is in one of three forms:
- a string
- a list of strings
- a list of dictionaries, where each dictionary has these entries:
--- 452,459 ----

The first argument of |popup_create()| (and the second argument to
|popup_settext()|) specifies the text to be displayed, and optionally text
! properties. It is in one of four forms:
! - a buffer number
- a string
- a list of strings
- a list of dictionaries, where each dictionary has these entries:
*** ../vim-8.1.1611/src/version.c 2019-06-30 20:32:58.424000713 +0200
--- src/version.c 2019-06-30 22:14:40.936366556 +0200
***************
*** 779,780 ****
--- 779,782 ----
{ /* Add new patch number below this line */
+ /**/
+ 1612,
/**/

--
A successful man is one who makes more money than his wife can spend.
A successful woman is one who can find such a man.

/// Bram Moolenaar -- Br...@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 ///
Reply all
Reply to author
Forward
0 new messages