From f273245f6433d5d43a5671306b520a3230c35787 Mon Sep 17 00:00:00 2001 From: Bram Moolenaar Date: Sun, 3 Jun 2018 14:47:35 +0200 Subject: patch 8.1.0027: difficult to make a plugin that feeds a line to a job Problem: Difficult to make a plugin that feeds a line to a job. Solution: Add the nitial code for the "prompt" buftype. --- src/Makefile | 1 + src/buffer.c | 20 +++++++++- src/channel.c | 34 +++++++++++++++++ src/diff.c | 7 ++++ src/edit.c | 78 ++++++++++++++++++++++++++++++++++++++ src/evalfunc.c | 64 +++++++++++++++++++++++++++++++ src/normal.c | 65 +++++++++++++++++++++++++++++++ src/ops.c | 77 +++++++++++++++++++++---------------- src/option.c | 2 +- src/proto/buffer.pro | 1 + src/proto/channel.pro | 1 + src/proto/edit.pro | 2 + src/proto/ops.pro | 1 + src/structs.h | 5 +++ src/testdir/Make_all.mak | 1 + src/testdir/screendump.vim | 29 +++++++------- src/testdir/test_prompt_buffer.vim | 55 +++++++++++++++++++++++++++ src/version.c | 2 + 18 files changed, 396 insertions(+), 49 deletions(-) create mode 100644 src/testdir/test_prompt_buffer.vim (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 0e4c555627..ca972547f6 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2252,6 +2252,7 @@ test_arglist \ test_popup \ test_preview \ test_profile \ + test_prompt_buffer \ test_put \ test_python2 \ test_python3 \ diff --git a/src/buffer.c b/src/buffer.c index e3cbdac1e8..1c55acbaf2 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -851,6 +851,10 @@ free_buffer(buf_T *buf) #ifdef FEAT_TERMINAL free_terminal(buf); #endif +#ifdef FEAT_JOB_CHANNEL + vim_free(buf->b_prompt_text); + free_callback(buf->b_prompt_callback, buf->b_prompt_partial); +#endif buf_hashtab_remove(buf); @@ -5633,6 +5637,15 @@ bt_help(buf_T *buf) return buf != NULL && buf->b_help; } +/* + * Return TRUE if "buf" is a prompt buffer. + */ + int +bt_prompt(buf_T *buf) +{ + return buf != NULL && buf->b_p_bt[0] == 'p'; +} + /* * Return TRUE if "buf" is a "nofile", "acwrite" or "terminal" buffer. * This means the buffer name is not a file name. @@ -5642,7 +5655,8 @@ bt_nofile(buf_T *buf) { return buf != NULL && ((buf->b_p_bt[0] == 'n' && buf->b_p_bt[2] == 'f') || buf->b_p_bt[0] == 'a' - || buf->b_p_bt[0] == 't'); + || buf->b_p_bt[0] == 't' + || buf->b_p_bt[0] == 'p'); } /* @@ -5651,7 +5665,9 @@ bt_nofile(buf_T *buf) int bt_dontwrite(buf_T *buf) { - return buf != NULL && (buf->b_p_bt[0] == 'n' || buf->b_p_bt[0] == 't'); + return buf != NULL && (buf->b_p_bt[0] == 'n' + || buf->b_p_bt[0] == 't' + || buf->b_p_bt[0] == 'p'); } int diff --git a/src/channel.c b/src/channel.c index 504d6b6095..40a3e955d8 100644 --- a/src/channel.c +++ b/src/channel.c @@ -5836,4 +5836,38 @@ job_stop(job_T *job, typval_T *argvars, char *type) return 1; } + void +invoke_prompt_callback(void) +{ + typval_T rettv; + int dummy; + typval_T argv[2]; + char_u *text; + char_u *prompt; + linenr_T lnum = curbuf->b_ml.ml_line_count; + + // Add a new line for the prompt before invoking the callback, so that + // text can always be inserted above the last line. + ml_append(lnum, (char_u *)"", 0, FALSE); + curwin->w_cursor.lnum = lnum + 1; + curwin->w_cursor.col = 0; + + if (curbuf->b_prompt_callback == NULL) + return; + text = ml_get(lnum); + prompt = prompt_text(); + if (STRLEN(text) >= STRLEN(prompt)) + text += STRLEN(prompt); + argv[0].v_type = VAR_STRING; + argv[0].vval.v_string = vim_strsave(text); + argv[1].v_type = VAR_UNKNOWN; + + call_func(curbuf->b_prompt_callback, + (int)STRLEN(curbuf->b_prompt_callback), + &rettv, 1, argv, NULL, 0L, 0L, &dummy, TRUE, + curbuf->b_prompt_partial, NULL); + clear_tv(&argv[0]); + clear_tv(&rettv); +} + #endif /* FEAT_JOB_CHANNEL */ diff --git a/src/diff.c b/src/diff.c index cc9e6de8fd..c67654f621 100644 --- a/src/diff.c +++ b/src/diff.c @@ -2141,6 +2141,13 @@ nv_diffgetput(int put, long count) exarg_T ea; char_u buf[30]; +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf)) + { + vim_beep(BO_OPER); + return; + } +#endif if (count == 0) ea.arg = (char_u *)""; else diff --git a/src/edit.c b/src/edit.c index 1ae8e2db44..1b79eccac9 100644 --- a/src/edit.c +++ b/src/edit.c @@ -203,6 +203,9 @@ static unsigned quote_meta(char_u *dest, char_u *str, int len); static void ins_redraw(int ready); static void ins_ctrl_v(void); +#ifdef FEAT_JOB_CHANNEL +static void init_prompt(int cmdchar_todo); +#endif static void undisplay_dollar(void); static void insert_special(int, int, int); static void internal_format(int textwidth, int second_indent, int flags, int format_only, int c); @@ -351,6 +354,9 @@ edit( int inserted_space = FALSE; /* just inserted a space */ int replaceState = REPLACE; int nomove = FALSE; /* don't move cursor on return */ +#ifdef FEAT_JOB_CHANNEL + int cmdchar_todo = cmdchar; +#endif /* Remember whether editing was restarted after CTRL-O. */ did_restart_edit = restart_edit; @@ -707,6 +713,14 @@ edit( foldCheckClose(); #endif +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf)) + { + init_prompt(cmdchar_todo); + cmdchar_todo = NUL; + } +#endif + /* * If we inserted a character at the last position of the last line in * the window, scroll the window one line up. This avoids an extra @@ -1373,6 +1387,18 @@ doESCkey: cmdwin_result = CAR; goto doESCkey; } +#endif +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf)) + { + buf_T *buf = curbuf; + + invoke_prompt_callback(); + if (curbuf != buf) + // buffer changed, get out of Insert mode + goto doESCkey; + break; + } #endif if (ins_eol(c) == FAIL && !p_im) goto doESCkey; /* out of memory */ @@ -1808,6 +1834,58 @@ edit_putchar(int c, int highlight) } } +#if defined(FEAT_JOB_CHANNEL) || defined(PROTO) +/* + * Return the effective prompt for the current buffer. + */ + char_u * +prompt_text(void) +{ + if (curbuf->b_prompt_text == NULL) + return (char_u *)"% "; + return curbuf->b_prompt_text; +} + +/* + * Prepare for prompt mode: Make sure the last line has the prompt text. + * Move the cursor to this line. + */ + static void +init_prompt(int cmdchar_todo) +{ + char_u *prompt = prompt_text(); + char_u *text; + + curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + text = ml_get_curline(); + if (STRNCMP(text, prompt, STRLEN(prompt)) != 0) + { + // prompt is missing, insert it or append a line with it + if (*text == NUL) + ml_replace(curbuf->b_ml.ml_line_count, prompt, TRUE); + else + ml_append(curbuf->b_ml.ml_line_count, prompt, 0, FALSE); + curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count; + coladvance((colnr_T)MAXCOL); + changed_bytes(curbuf->b_ml.ml_line_count, 0); + } + if (cmdchar_todo == 'A') + coladvance((colnr_T)MAXCOL); + if (cmdchar_todo == 'I' || curwin->w_cursor.col <= (int)STRLEN(prompt)) + curwin->w_cursor.col = STRLEN(prompt); +} + +/* + * Return TRUE if the cursor is in the editable position of the prompt line. + */ + int +prompt_curpos_editable() +{ + return curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count + && curwin->w_cursor.col >= (int)STRLEN(prompt_text()); +} +#endif + /* * Undo the previous edit_putchar(). */ diff --git a/src/evalfunc.c b/src/evalfunc.c index 90aa2efd50..9441bd89f4 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -294,6 +294,10 @@ static void f_pow(typval_T *argvars, typval_T *rettv); #endif static void f_prevnonblank(typval_T *argvars, typval_T *rettv); static void f_printf(typval_T *argvars, typval_T *rettv); +#ifdef FEAT_JOB_CHANNEL +static void f_prompt_setcallback(typval_T *argvars, typval_T *rettv); +static void f_prompt_setprompt(typval_T *argvars, typval_T *rettv); +#endif static void f_pumvisible(typval_T *argvars, typval_T *rettv); #ifdef FEAT_PYTHON3 static void f_py3eval(typval_T *argvars, typval_T *rettv); @@ -744,6 +748,10 @@ static struct fst #endif {"prevnonblank", 1, 1, f_prevnonblank}, {"printf", 1, 19, f_printf}, +#ifdef FEAT_JOB_CHANNEL + {"prompt_setcallback", 2, 2, f_prompt_setcallback}, + {"prompt_setprompt", 2, 2, f_prompt_setprompt}, +#endif {"pumvisible", 0, 0, f_pumvisible}, #ifdef FEAT_PYTHON3 {"py3eval", 1, 1, f_py3eval}, @@ -1240,6 +1248,11 @@ f_append(typval_T *argvars, typval_T *rettv) appended_lines_mark(lnum, added); if (curwin->w_cursor.lnum > lnum) curwin->w_cursor.lnum += added; +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf) && (State & INSERT)) + // show the line with the prompt + update_topline(); +#endif } else rettv->vval.v_number = 1; /* Failed */ @@ -8379,6 +8392,57 @@ f_printf(typval_T *argvars, typval_T *rettv) did_emsg |= saved_did_emsg; } +#ifdef FEAT_JOB_CHANNEL +/* + * "prompt_setcallback({buffer}, {callback})" function + */ + static void +f_prompt_setcallback(typval_T *argvars, typval_T *rettv UNUSED) +{ + buf_T *buf; + char_u *callback; + partial_T *partial; + + if (check_secure()) + return; + buf = get_buf_tv(&argvars[0], FALSE); + if (buf == NULL) + return; + + callback = get_callback(&argvars[1], &partial); + if (callback == NULL) + return; + + free_callback(buf->b_prompt_callback, buf->b_prompt_partial); + if (partial == NULL) + buf->b_prompt_callback = vim_strsave(callback); + else + /* pointer into the partial */ + buf->b_prompt_callback = callback; + buf->b_prompt_partial = partial; +} + +/* + * "prompt_setprompt({buffer}, {text})" function + */ + static void +f_prompt_setprompt(typval_T *argvars, typval_T *rettv UNUSED) +{ + buf_T *buf; + char_u *text; + + if (check_secure()) + return; + buf = get_buf_tv(&argvars[0], FALSE); + if (buf == NULL) + return; + + text = get_tv_string(&argvars[1]); + vim_free(buf->b_prompt_text); + buf->b_prompt_text = vim_strsave(text); +} +#endif + /* * "pumvisible()" function */ diff --git a/src/normal.c b/src/normal.c index 58f7a7a965..a01a434867 100644 --- a/src/normal.c +++ b/src/normal.c @@ -4180,6 +4180,11 @@ nv_help(cmdarg_T *cap) static void nv_addsub(cmdarg_T *cap) { +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf) && !prompt_curpos_editable()) + clearopbeep(cap->oap); + else +#endif if (!VIsual_active && cap->oap->op_type == OP_NOP) { prep_redo_cmd(cap); @@ -6213,6 +6218,17 @@ nv_down(cmdarg_T *cap) if (cmdwin_type != 0 && cap->cmdchar == CAR) cmdwin_result = CAR; else +#endif +#ifdef FEAT_JOB_CHANNEL + /* In a prompt buffer a in the last line invokes the callback. */ + if (bt_prompt(curbuf) && cap->cmdchar == CAR + && curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) + { + invoke_prompt_callback(); + if (restart_edit == 0) + restart_edit = 'a'; + } + else #endif { cap->oap->motion_type = MLINE; @@ -6972,6 +6988,13 @@ nv_kundo(cmdarg_T *cap) { if (!checkclearopq(cap->oap)) { +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf)) + { + clearopbeep(cap->oap); + return; + } +#endif u_undo((int)cap->count1); curwin->w_set_curswant = TRUE; } @@ -6989,6 +7012,13 @@ nv_replace(cmdarg_T *cap) if (checkclearop(cap->oap)) return; +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf) && !prompt_curpos_editable()) + { + clearopbeep(cap->oap); + return; + } +#endif /* get another character */ if (cap->nchar == Ctrl_V) @@ -7464,6 +7494,13 @@ nv_subst(cmdarg_T *cap) /* When showing output of term_dumpdiff() swap the top and botom. */ if (term_swap_diff() == OK) return; +#endif +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf) && !prompt_curpos_editable()) + { + clearopbeep(cap->oap); + return; + } #endif if (VIsual_active) /* "vs" and "vS" are the same as "vc" */ { @@ -8570,7 +8607,16 @@ nv_Undo(cmdarg_T *cap) nv_tilde(cmdarg_T *cap) { if (!p_to && !VIsual_active && cap->oap->op_type != OP_TILDE) + { +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf) && !prompt_curpos_editable()) + { + clearopbeep(cap->oap); + return; + } +#endif n_swapchar(cap); + } else nv_operator(cap); } @@ -8585,6 +8631,13 @@ nv_operator(cmdarg_T *cap) int op_type; op_type = get_op_type(cap->cmdchar, cap->nchar); +#ifdef FEAT_JOB_CHANNEL + if (bt_prompt(curbuf) && op_is_change(op_type) && !prompt_curpos_editable()) + { + clearopbeep(cap->oap); + return; + } +#endif if (op_type == cap->oap->op_type) /* double operator works on lines */ nv_lineop(cap); @@ -9426,6 +9479,12 @@ nv_put(cmdarg_T *cap) #endif clearopbeep(cap->oap); } +#ifdef FEAT_JOB_CHANNEL + else if (bt_prompt(curbuf) && !prompt_curpos_editable()) + { + clearopbeep(cap->oap); + } +#endif else { dir = (cap->cmdchar == 'P' @@ -9551,6 +9610,12 @@ nv_open(cmdarg_T *cap) #endif if (VIsual_active) /* switch start and end of visual */ v_swap_corners(cap->cmdchar); +#ifdef FEAT_JOB_CHANNEL + else if (bt_prompt(curbuf)) + { + clearopbeep(cap->oap); + } +#endif else n_opencmd(cap); } diff --git a/src/ops.c b/src/ops.c index 1bc51d8480..65fc75e20b 100644 --- a/src/ops.c +++ b/src/ops.c @@ -126,43 +126,47 @@ static int fmt_check_par(linenr_T, int *, char_u **, int do_comments); static int fmt_check_par(linenr_T); #endif +// Flags for third item in "opchars". +#define OPF_LINES 1 // operator always works on lines +#define OPF_CHANGE 2 // operator changes text + /* * The names of operators. * IMPORTANT: Index must correspond with defines in vim.h!!! - * The third field indicates whether the operator always works on lines. + * The third field holds OPF_ flags. */ static char opchars[][3] = { - {NUL, NUL, FALSE}, /* OP_NOP */ - {'d', NUL, FALSE}, /* OP_DELETE */ - {'y', NUL, FALSE}, /* OP_YANK */ - {'c', NUL, FALSE}, /* OP_CHANGE */ - {'<', NUL, TRUE}, /* OP_LSHIFT */ - {'>', NUL, TRUE}, /* OP_RSHIFT */ - {'!', NUL, TRUE}, /* OP_FILTER */ - {'g', '~', FALSE}, /* OP_TILDE */ - {'=', NUL, TRUE}, /* OP_INDENT */ - {'g', 'q', TRUE}, /* OP_FORMAT */ - {':', NUL, TRUE}, /* OP_COLON */ - {'g', 'U', FALSE}, /* OP_UPPER */ - {'g', 'u', FALSE}, /* OP_LOWER */ - {'J', NUL, TRUE}, /* DO_JOIN */ - {'g', 'J', TRUE}, /* DO_JOIN_NS */ - {'g', '?', FALSE}, /* OP_ROT13 */ - {'r', NUL, FALSE}, /* OP_REPLACE */ - {'I', NUL, FALSE}, /* OP_INSERT */ - {'A', NUL, FALSE}, /* OP_APPEND */ - {'z', 'f', TRUE}, /* OP_FOLD */ - {'z', 'o', TRUE}, /* OP_FOLDOPEN */ - {'z', 'O', TRUE}, /* OP_FOLDOPENREC */ - {'z', 'c', TRUE}, /* OP_FOLDCLOSE */ - {'z', 'C', TRUE}, /* OP_FOLDCLOSEREC */ - {'z', 'd', TRUE}, /* OP_FOLDDEL */ - {'z', 'D', TRUE}, /* OP_FOLDDELREC */ - {'g', 'w', TRUE}, /* OP_FORMAT2 */ - {'g', '@', FALSE}, /* OP_FUNCTION */ - {Ctrl_A, NUL, FALSE}, /* OP_NR_ADD */ - {Ctrl_X, NUL, FALSE}, /* OP_NR_SUB */ + {NUL, NUL, 0}, // OP_NOP + {'d', NUL, OPF_CHANGE}, // OP_DELETE + {'y', NUL, 0}, // OP_YANK + {'c', NUL, OPF_CHANGE}, // OP_CHANGE + {'<', NUL, OPF_LINES | OPF_CHANGE}, // OP_LSHIFT + {'>', NUL, OPF_LINES | OPF_CHANGE}, // OP_RSHIFT + {'!', NUL, OPF_LINES | OPF_CHANGE}, // OP_FILTER + {'g', '~', OPF_CHANGE}, // OP_TILDE + {'=', NUL, OPF_LINES | OPF_CHANGE}, // OP_INDENT + {'g', 'q', OPF_LINES | OPF_CHANGE}, // OP_FORMAT + {':', NUL, OPF_LINES}, // OP_COLON + {'g', 'U', OPF_CHANGE}, // OP_UPPER + {'g', 'u', OPF_CHANGE}, // OP_LOWER + {'J', NUL, OPF_LINES | OPF_CHANGE}, // DO_JOIN + {'g', 'J', OPF_LINES | OPF_CHANGE}, // DO_JOIN_NS + {'g', '?', OPF_CHANGE}, // OP_ROT13 + {'r', NUL, OPF_CHANGE}, // OP_REPLACE + {'I', NUL, OPF_CHANGE}, // OP_INSERT + {'A', NUL, OPF_CHANGE}, // OP_APPEND + {'z', 'f', OPF_LINES}, // OP_FOLD + {'z', 'o', OPF_LINES}, // OP_FOLDOPEN + {'z', 'O', OPF_LINES}, // OP_FOLDOPENREC + {'z', 'c', OPF_LINES}, // OP_FOLDCLOSE + {'z', 'C', OPF_LINES}, // OP_FOLDCLOSEREC + {'z', 'd', OPF_LINES}, // OP_FOLDDEL + {'z', 'D', OPF_LINES}, // OP_FOLDDELREC + {'g', 'w', OPF_LINES | OPF_CHANGE}, // OP_FORMAT2 + {'g', '@', OPF_CHANGE}, // OP_FUNCTION + {Ctrl_A, NUL, OPF_CHANGE}, // OP_NR_ADD + {Ctrl_X, NUL, OPF_CHANGE}, // OP_NR_SUB }; /* @@ -201,7 +205,16 @@ get_op_type(int char1, int char2) int op_on_lines(int op) { - return opchars[op][2]; + return opchars[op][2] & OPF_LINES; +} + +/* + * Return TRUE if operator "op" changes text. + */ + int +op_is_change(int op) +{ + return opchars[op][2] & OPF_CHANGE; } /* diff --git a/src/option.c b/src/option.c index 7fd1dda99c..16d05d8b50 100644 --- a/src/option.c +++ b/src/option.c @@ -3229,7 +3229,7 @@ static char *(p_bsdir_values[]) = {"current", "last", "buffer", NULL}; static char *(p_scbopt_values[]) = {"ver", "hor", "jump", NULL}; static char *(p_debug_values[]) = {"msg", "throw", "beep", NULL}; static char *(p_ead_values[]) = {"both", "ver", "hor", NULL}; -static char *(p_buftype_values[]) = {"nofile", "nowrite", "quickfix", "help", "terminal", "acwrite", NULL}; +static char *(p_buftype_values[]) = {"nofile", "nowrite", "quickfix", "help", "terminal", "acwrite", "prompt", NULL}; static char *(p_bufhidden_values[]) = {"hide", "unload", "delete", "wipe", NULL}; static char *(p_bs_values[]) = {"indent", "eol", "start", NULL}; #ifdef FEAT_FOLDING diff --git a/src/proto/buffer.pro b/src/proto/buffer.pro index 59bb2c2012..9e63fb6055 100644 --- a/src/proto/buffer.pro +++ b/src/proto/buffer.pro @@ -59,6 +59,7 @@ void write_viminfo_bufferlist(FILE *fp); int bt_quickfix(buf_T *buf); int bt_terminal(buf_T *buf); int bt_help(buf_T *buf); +int bt_prompt(buf_T *buf); int bt_nofile(buf_T *buf); int bt_dontwrite(buf_T *buf); int bt_dontwrite_msg(buf_T *buf); diff --git a/src/proto/channel.pro b/src/proto/channel.pro index 8d26158a57..e6c95089b8 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -71,4 +71,5 @@ char *job_status(job_T *job); void job_info(job_T *job, dict_T *dict); void job_info_all(list_T *l); int job_stop(job_T *job, typval_T *argvars, char *type); +void invoke_prompt_callback(void); /* vim: set ft=c : */ diff --git a/src/proto/edit.pro b/src/proto/edit.pro index 1f9e5b75e2..9ba71645b5 100644 --- a/src/proto/edit.pro +++ b/src/proto/edit.pro @@ -1,6 +1,8 @@ /* edit.c */ int edit(int cmdchar, int startln, long count); void edit_putchar(int c, int highlight); +char_u *prompt_text(void); +int prompt_curpos_editable(void); void edit_unputchar(void); void display_dollar(colnr_T col); void change_indent(int type, int amount, int round, int replaced, int call_changed_bytes); diff --git a/src/proto/ops.pro b/src/proto/ops.pro index 13e063e27c..01df56f2f7 100644 --- a/src/proto/ops.pro +++ b/src/proto/ops.pro @@ -1,6 +1,7 @@ /* ops.c */ int get_op_type(int char1, int char2); int op_on_lines(int op); +int op_is_change(int op); int get_op_char(int optype); int get_extra_op_char(int optype); void op_shift(oparg_T *oap, int curs_top, int amount); diff --git a/src/structs.h b/src/structs.h index b70b00f96c..cbebd7f964 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2356,6 +2356,11 @@ struct file_buffer int b_shortname; /* this file has an 8.3 file name */ +#ifdef FEAT_JOB_CHANNEL + char_u *b_prompt_text; // set by prompt_setprompt() + char_u *b_prompt_callback; // set by prompt_setcallback() + partial_T *b_prompt_partial; // set by prompt_setcallback() +#endif #ifdef FEAT_MZSCHEME void *b_mzscheme_ref; /* The MzScheme reference to this buffer */ #endif diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index e36089af87..6a0126b850 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -147,6 +147,7 @@ NEW_TESTS = test_arabic.res \ test_perl.res \ test_plus_arg_edit.res \ test_preview.res \ + test_prompt_buffer.res \ test_profile.res \ test_python2.res \ test_python3.res \ diff --git a/src/testdir/screendump.vim b/src/testdir/screendump.vim index 3c424a093c..80d51c336d 100644 --- a/src/testdir/screendump.vim +++ b/src/testdir/screendump.vim @@ -5,19 +5,18 @@ if exists('*CanRunVimInTerminal') finish endif -" Need to be able to run terminal Vim with 256 colors. On MS-Windows the -" console only has 16 colors and the GUI can't run in a terminal. -if !has('terminal') || has('win32') - func CanRunVimInTerminal() - return 0 - endfunc - finish -endif - +" For most tests we need to be able to run terminal Vim with 256 colors. On +" MS-Windows the console only has 16 colors and the GUI can't run in a +" terminal. func CanRunVimInTerminal() - return 1 + return has('terminal') && !has('win32') endfunc +" Skip the rest if there is no terminal feature at all. +if !has('terminal') + finish +endif + source shared.vim " Run Vim with "arguments" in a new terminal window. @@ -54,6 +53,7 @@ func RunVimInTerminal(arguments, options) let cols = get(a:options, 'cols', 75) let cmd = GetVimCommandClean() + " Add -v to have gvim run in the terminal (if possible) let cmd .= ' -v ' . a:arguments let buf = term_start(cmd, {'curwin': 1, 'term_rows': rows, 'term_cols': cols}) @@ -64,11 +64,12 @@ func RunVimInTerminal(arguments, options) let cols = term_getsize(buf)[1] endif - " Wait for "All" or "Top" of the ruler in the status line to be shown. This - " can be quite slow (e.g. when using valgrind). + " Wait for "All" or "Top" of the ruler to be shown in the last line or in + " the status line of the last window. This can be quite slow (e.g. when + " using valgrind). " If it fails then show the terminal contents for debugging. try - call WaitFor({-> len(term_getline(buf, rows)) >= cols - 1}) + call WaitFor({-> len(term_getline(buf, rows)) >= cols - 1 || len(term_getline(buf, rows - 1)) >= cols - 1}) catch /timed out after/ let lines = map(range(1, rows), {key, val -> term_getline(buf, val)}) call assert_report('RunVimInTerminal() failed, screen contents: ' . join(lines, "")) @@ -80,7 +81,7 @@ endfunc " Stop a Vim running in terminal buffer "buf". func StopVimInTerminal(buf) call assert_equal("running", term_getstatus(a:buf)) - call term_sendkeys(a:buf, "\\:qa!\") + call term_sendkeys(a:buf, "\:qa!\") call WaitForAssert({-> assert_equal("finished", term_getstatus(a:buf))}) only! endfunc diff --git a/src/testdir/test_prompt_buffer.vim b/src/testdir/test_prompt_buffer.vim new file mode 100644 index 0000000000..a21acc751e --- /dev/null +++ b/src/testdir/test_prompt_buffer.vim @@ -0,0 +1,55 @@ +" Tests for setting 'buftype' to "prompt" + +if !has('channel') + finish +endif + +source shared.vim +source screendump.vim + +func Test_prompt_basic() + " We need to use a terminal window to be able to feed keys without leaving + " Insert mode. + if !has('terminal') + call assert_report('no terminal') + return + endif + call writefile([ + \ 'func TextEntered(text)', + \ ' if a:text == "exit"', + \ ' stopinsert', + \ ' close', + \ ' else', + \ ' " Add the output above the current prompt.', + \ ' call append(line("$") - 1, "Command: \"" . a:text . "\"")', + \ ' " Reset &modified to allow the buffer to be closed.', + \ ' set nomodified', + \ ' call timer_start(20, {id -> TimerFunc(a:text)})', + \ ' endif', + \ 'endfunc', + \ '', + \ 'func TimerFunc(text)', + \ ' " Add the output above the current prompt.', + \ ' call append(line("$") - 1, "Result: \"" . a:text . "\"")', + \ 'endfunc', + \ '', + \ 'call setline(1, "other buffer")', + \ 'new', + \ 'set buftype=prompt', + \ 'call prompt_setcallback(bufnr(""), function("TextEntered"))', + \ 'startinsert', + \ ], 'Xpromptscript') + let buf = RunVimInTerminal('-S Xpromptscript', {}) + call WaitForAssert({-> assert_equal('%', term_getline(buf, 1))}) + + call term_sendkeys(buf, "hello\") + call WaitForAssert({-> assert_equal('% hello', term_getline(buf, 1))}) + call WaitForAssert({-> assert_equal('Command: "hello"', term_getline(buf, 2))}) + call WaitForAssert({-> assert_equal('Result: "hello"', term_getline(buf, 3))}) + + call term_sendkeys(buf, "exit\") + call WaitForAssert({-> assert_equal('other buffer', term_getline(buf, 1))}) + + call StopVimInTerminal(buf) + call delete('Xpromptscript') +endfunc diff --git a/src/version.c b/src/version.c index 72960f3ea4..985e659bf7 100644 --- a/src/version.c +++ b/src/version.c @@ -761,6 +761,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 27, /**/ 26, /**/ -- cgit v1.2.3