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.
This commit is contained in:
Bram Moolenaar
2018-06-03 14:47:35 +02:00
parent 33c5e9fa7a
commit f273245f64
22 changed files with 532 additions and 58 deletions

View File

@ -22,6 +22,7 @@ The Netbeans interface also uses a channel. |netbeans|
9. Starting a job without a channel |job-start-nochannel|
10. Job options |job-options|
11. Controlling a job |job-control|
12. Using a prompt buffer |prompt-buffer|
{Vi does not have any of these features}
{only when compiled with the |+channel| feature for channel stuff}
@ -770,5 +771,43 @@ signals. E.g. to force a job to stop, "kill it": >
For more options see |job_stop()|.
==============================================================================
12. Using a prompt buffer *prompt-buffer*
If you want to type input for the job in a Vim window you have a few options:
- Use a normal buffer and handle all possible commands yourself.
This will be complicated, since there are so many possible commands.
- Use a terminal window. This works well if what you type goes directly to
the job and the job output is directly displayed in the window.
See |terminal-window|.
- Use a prompt window. This works well when entering a line for the job in Vim
while displaying (possibly filtered) output from the job.
A prompt buffer is created by setting 'buftype' to "prompt". You would
normally only do that in a newly created buffer.
The user can edit and enter one line of text at the very last line of the
buffer. When pressing Enter in the prompt line the callback set with
|prompt_setcallback()| is invoked. It would normally send the line to a job.
Another callback would receive the output from the job and display it in the
buffer, below the prompt (and above the next prompt).
Only the text in the last line, after the prompt, is editable. The rest of the
buffer is not modifiable with Normal mode commands. It can be modified by
calling functions, such as |append()|. Using other commands may mess up the
buffer.
After setting 'buftype' to "prompt" Vim does not automatically start Insert
mode, use `:startinsert` if you want to enter Insert mode, so that the user
can start typing a line.
The text of the prompt can be set with the |prompt_setprompt()| function.
The user can go to Normal mode and navigate through the buffer. This can be
useful see older output or copy text.
Any command that starts Insert mode, such as "a", "i", "A" and "I", will move
the cursor to the last line, after the prompt.
vim:tw=78:ts=8:ft=help:norl:

View File

@ -2294,6 +2294,9 @@ perleval({expr}) any evaluate |Perl| expression
pow({x}, {y}) Float {x} to the power of {y}
prevnonblank({lnum}) Number line nr of non-blank line <= {lnum}
printf({fmt}, {expr1}...) String format text
prompt_addtext({buf}, {expr}) none add text to a prompt buffer
prompt_setprompt({buf}, {text}) none set prompt text
prompt_setcallback({buf}, {expr}) none set prompt callback function
pumvisible() Number whether popup menu is visible
pyeval({expr}) any evaluate |Python| expression
py3eval({expr}) any evaluate |python3| expression
@ -2302,7 +2305,7 @@ range({expr} [, {max} [, {stride}]])
List items from {expr} to {max}
readfile({fname} [, {binary} [, {max}]])
List get list of lines from file {fname}
reg_executing() Number get the executing register name
reg_executing() String get the executing register name
reg_recording() String get the recording register name
reltime([{start} [, {end}]]) List get time value
reltimefloat({time}) Float turn the time value into a Float
@ -4650,7 +4653,7 @@ getline({lnum} [, {end}])
from the current buffer. Example: >
getline(1)
< When {lnum} is a String that doesn't start with a
digit, line() is called to translate the String into a Number.
digit, |line()| is called to translate the String into a Number.
To get the line under the cursor: >
getline(".")
< When {lnum} is smaller than 1 or bigger than the number of
@ -6475,6 +6478,42 @@ printf({fmt}, {expr1} ...) *printf()*
arguments an error is given. Up to 18 arguments can be used.
prompt_setprompt({buf}, {text}) *prompt_setprompt()*
Set prompt for buffer {buf} to {text}. You most likely want
{text} to end in a space.
The result is only visible if {buf} has 'buftype' set to
"prompt". Example: >
call prompt_setprompt(bufnr(''), 'command: ')
prompt_setcallback({buf}, {expr}) *prompt_setcallback()*
Set prompt callback for buffer {buf} to {expr}. This has only
effect if {buf} has 'buftype' set to "prompt".
The callback is invoked when pressing Enter. The current
buffer will always be the prompt buffer. A new line for a
prompt is added before invoking the callback, thus the prompt
for which the callback was invoked will be in the last but one
line.
If the callback wants to add text to the buffer, it must
insert it above the last line, since that is where the current
prompt is. This can also be done asynchronously.
The callback is invoked with one argument, which is the text
that was entered at the prompt. This can be an empty string
if the user only typed Enter.
Example: >
call prompt_setcallback(bufnr(''), function('s:TextEntered'))
func s:TextEntered(text)
if a:text == 'exit' || a:text == 'quit'
stopinsert
close
else
call append(line('$') - 1, 'Entered: "' . a:text . '"')
" Reset 'modified' to allow the buffer to be closed.
set nomodified
endif
endfunc
pumvisible() *pumvisible()*
Returns non-zero when the popup menu is visible, zero
otherwise. See |ins-completion-menu|.

View File

@ -1394,6 +1394,9 @@ A jump table for the options with a short description can be found at |Q_op|.
manually)
terminal buffer for a |terminal| (you are not supposed to set
this manually)
prompt buffer where only the last line can be edited, meant
to be used by a plugin, see |prompt-buffer|
{only when compiled with the |+channel| feature}
This option is used together with 'bufhidden' and 'swapfile' to
specify special kinds of buffers. See |special-buffers|.
@ -4264,7 +4267,8 @@ A jump table for the options with a short description can be found at |Q_op|.
'imactivatefunc' 'imaf' string (default "")
global
{not in Vi}
{only available when compiled with |+mbyte|}
{only available when compiled with the |+multi_byte|
feature}
This option specifies a function that will be called to
activate or deactivate the Input Method.
It is not used in the GUI.
@ -4316,7 +4320,8 @@ A jump table for the options with a short description can be found at |Q_op|.
'imcmdline' 'imc' boolean (default off)
global
{not in Vi}
{only available when compiled with |+mbyte|}
{only available when compiled with the |+multi_byte|
feature}
When set the Input Method is always on when starting to edit a command
line, unless entering a search pattern (see 'imsearch' for that).
Setting this option is useful when your input method allows entering
@ -4327,7 +4332,8 @@ A jump table for the options with a short description can be found at |Q_op|.
'imdisable' 'imd' boolean (default off, on for some systems (SGI))
global
{not in Vi}
{only available when compiled with |+mbyte|}
{only available when compiled with the |+multi_byte|
feature}
When set the Input Method is never used. This is useful to disable
the IM when it doesn't work properly.
Currently this option is on by default for SGI/IRIX machines. This
@ -4380,7 +4386,8 @@ A jump table for the options with a short description can be found at |Q_op|.
'imstatusfunc' 'imsf' string (default "")
global
{not in Vi}
{only available when compiled with |+mbyte|}
{only available when compiled with the |+multi_byte|
feature}
This option specifies a function that is called to obtain the status
of Input Method. It must return a positive number when IME is active.
It is not used in the GUI.

View File

@ -38,6 +38,10 @@ browser use: https://github.com/vim/vim/issues/1234
*known-bugs*
-------------------- Known bugs and current work -----------------------
Prompt buffer:
- Add a command line history.
- delay next prompt until plugin gives OK?
Terminal emulator window:
- Win32: Termdebug doesn't work, because gdb does not support mi2 on a tty.
This plugin: https://github.com/cpiger/NeoDebug runs gdb as a job,
@ -71,9 +75,15 @@ Crash when mixing matchadd and substitute()? (Max Christian Pohle, 2018 May
On Win32 when not in the console and t_Co >= 256, allow using 'tgc'.
(Nobuhiro Takasaki, #2833) Also check t_Co.
balloon_show() does not work properly in the terminal. (Ben Jackson, 2017 Dec
20, #2481)
Also see #2352, want better control over balloon, perhaps set the position.
Patch to fix arguments of :edit. (Dominique Pelle, 2018 May 28 #2966)
Ptch to update html syntax. (Jorge Maldonado Ventura, #2974)
Patch to fix that restoring window doesn't work when 'winheight' is large.
(Darrell Nash, 2018 May 30, #2971) Doesn't work? Issue #2970
Patch to add completion to :unlet for environment vars. (Jason Franklin, 2018
May 30) Last update.
Errors found with random data:
heap-buffer-overflow in alist_add (#2472)
@ -81,6 +91,22 @@ Errors found with random data:
More warnings from static analysis:
https://lgtm.com/projects/g/vim/vim/alerts/?mode=list
Patch to make "is" and "as" work bettter. (Jason Franklin, 2018 May 19)
Patch to add tests for user and language completion. (Dominique Pelle, 2018
Jun 2, #2978)
Using ":file" in quickfix window during an autocommand doesn't work.
(Jason Franklin, 2018 May 23) Allow for using it when there is no argument.
Pull request #2967: Allow white space in sign text. (Ben Jackson)
Patch for xterm and vt320 builtin termcap. (Kouichi Iwamoto, 2018 May 31,
#2973)
Patch to add more testing for :cd command. (Dominique Pelle, 2018 May 30,
#2972)
Script generated by :mksession does not work well if there are windows with
modified buffers
change "silent only" into "silent only!"
@ -88,16 +114,27 @@ modified buffers
skip "badd fname" if "fname" is already in the buffer list
remove remark about unloading buffers from documentation
Patch to make :help work for tags with a ?. (Hirohito Higashi, 2018 May 28)
Compiler warnings (geeknik, 2017 Oct 26):
- signed integer overflow in do_sub() (#2249)
- signed integer overflow in get_address() (#2248)
- signed integer overflow in getdecchrs() (#2254)
- undefined left shift in get_string_tv() (#2250)
Patch for more quickfix refactoring. (Yegappan Lakshmanan, #2950)
Tests failing for "make testgui" with GTK:
- Test_setbufvar_options()
- Test_exit_callback_interval()
Make balloon_show() work outside of 'balloonexpr'? Users expect it to work:
#2948. (related to #1512?)
On Win32 it stops showing, because showState is already ShS_SHOWING.
balloon_show() does not work properly in the terminal. (Ben Jackson, 2017 Dec
20, #2481)
Also see #2352, want better control over balloon, perhaps set the position.
Try out background make plugin:
https://github.com/AndrewVos/vim-make-background
or asyncmake:
@ -112,6 +149,8 @@ used for git temp files.
Cursor in wrong position when line wraps. (#2540)
Patch for Lua support. (Kazunobu Kuriyama, 2018 May 26)
Add an option similar to 'lazyredraw' to skip redrawing while executing a
script or function.
@ -141,6 +180,9 @@ How to test that it works well for all Vim users?
Alternative manpager.vim. (Enno, 2018 Jan 5, #2529)
Patch to use NGETTEXT() in many more places. (Sergey Alyoshin, 2018 May 25)
Updated ptach May 27.
Does setting 'cursorline' cause syntax highlighting to slow down? Perhaps is
mess up the cache? (Mike Lee Williams, 2018 Jan 27, #2539)
Also: 'foldtext' is evaluated too often. (Daniel Hahler, #2773)

View File

@ -2252,6 +2252,7 @@ test_arglist \
test_popup \
test_preview \
test_profile \
test_prompt_buffer \
test_put \
test_python2 \
test_python3 \

View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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().
*/

View File

@ -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
*/

View File

@ -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 <CR> 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);
}

View File

@ -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;
}
/*

View File

@ -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

View File

@ -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);

View File

@ -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 : */

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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 \

View File

@ -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
" 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 has('terminal') && !has('win32')
endfunc
" Skip the rest if there is no terminal feature at all.
if !has('terminal')
finish
endif
func CanRunVimInTerminal()
return 1
endfunc
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, "<NL>"))
@ -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, "\<Esc>\<Esc>:qa!\<cr>")
call term_sendkeys(a:buf, "\<Esc>:qa!\<cr>")
call WaitForAssert({-> assert_equal("finished", term_getstatus(a:buf))})
only!
endfunc

View File

@ -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\<CR>")
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\<CR>")
call WaitForAssert({-> assert_equal('other buffer', term_getline(buf, 1))})
call StopVimInTerminal(buf)
call delete('Xpromptscript')
endfunc

View File

@ -761,6 +761,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
27,
/**/
26,
/**/