]> granicus.if.org Git - vim/commitdiff
patch 8.1.0027: difficult to make a plugin that feeds a line to a job v8.1.0027
authorBram Moolenaar <Bram@vim.org>
Sun, 3 Jun 2018 12:47:35 +0000 (14:47 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 3 Jun 2018 12:47:35 +0000 (14:47 +0200)
Problem:    Difficult to make a plugin that feeds a line to a job.
Solution:   Add the nitial code for the "prompt" buftype.

22 files changed:
runtime/doc/channel.txt
runtime/doc/eval.txt
runtime/doc/options.txt
runtime/doc/todo.txt
src/Makefile
src/buffer.c
src/channel.c
src/diff.c
src/edit.c
src/evalfunc.c
src/normal.c
src/ops.c
src/option.c
src/proto/buffer.pro
src/proto/channel.pro
src/proto/edit.pro
src/proto/ops.pro
src/structs.h
src/testdir/Make_all.mak
src/testdir/screendump.vim
src/testdir/test_prompt_buffer.vim [new file with mode: 0644]
src/version.c

index 9cc2be37ade4bf62bd9ee71269365d6eb648d27e..bffb3e2833509c8ac5a01e7a2b64843b2fbd3f55 100644 (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:
index e243ba3788333bf3096269fa5202e5900ba5dc60..7d214c11aab30a277fdb1b5bca42aad5bcf97e75 100644 (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|.
index 422f1002852e35215df093b0663aab2b91c48dee..d37fa641a49c91589705fe699be1cf45b6e153ec 100644 (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.
index 2029c35ee142bb66acb9280c79bbb914acd51f7b..dbd293526b0d0cff18f827764cd69f1bc11a14fc 100644 (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)
index 0e4c55562751dc6e7631531e5db702dad18dff25..ca972547f679f0c743db86f83f2bbe1ca3d25dbd 100644 (file)
@@ -2252,6 +2252,7 @@ test_arglist \
        test_popup \
        test_preview \
        test_profile \
+       test_prompt_buffer \
        test_put \
        test_python2 \
        test_python3 \
index e3cbdac1e8581f448198f85ff32469ba3b9b1d53..1c55acbaf2b143e1c1fb54cb782bf6b7f25b1688 100644 (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
index 504d6b6095407e92d7da0713e0d57162f0adaa69..40a3e955d8988df72a5933f05549f4e9ae8d7939 100644 (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 */
index cc9e6de8fd0993e9f31b418c0588ef9a232418bf..c67654f621e296dbc5bee2e84b728b2f4bfe2e40 100644 (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
index 1ae8e2db44395878112631b4540b2e6250be88fd..1b79eccac9f7eff8a4c828958edf5747a41abf4d 100644 (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().
  */
index 90aa2efd50e1b08683d8ea62b27a2e4e0dd001a8..9441bd89f4f92c027c79c27cdae24eff7df21642 100644 (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
  */
index 58f7a7a9657065931c7e54833b0a54af56e0cb77..a01a434867965181b25f4229dae5a7c6ea2b1a88 100644 (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);
 }
index 1bc51d84809ab17b8e40dcfbd54f3578c95e9305..65fc75e20b722bd081444542502bd7b259d4f75d 100644 (file)
--- 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;
 }
 
 /*
index 7fd1dda99ce1195a67ab761b06f77f3325e5d11f..16d05d8b5095aa956de8a15b0e2bb82191faf7b5 100644 (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
index 59bb2c201222aff717bd83fefbde723875b4a63a..9e63fb60552e9aa7ef7a511c93bf349ddf1bc19f 100644 (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);
index 8d26158a577f2a132c3f520f49c9d41ab55ee411..e6c95089b85eafd4dccc4cfb591b4e94a811eb25 100644 (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 : */
index 1f9e5b75e2b4e05882cf043a951cb55345ae3698..9ba71645b5b6e40ad5f21bbb26eb9cd7fc38b588 100644 (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);
index 13e063e27cec4bb899713e7d6b1f282049d4d4b6..01df56f2f7a60d8567746f32827639e2cae43a52 100644 (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);
index b70b00f96c9a2ef191be3a546f95b6335661e076..cbebd7f9644ddb4808ea2c613ac31cb003a95c6c 100644 (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
index e36089af87b8342602ae2df4ef5c9d61530939e8..6a0126b8509e5370ad2555159fb57be2db404a27 100644 (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 \
index 3c424a093cba7e3b85c56425d7f7bf8ff0d50730..80d51c336d857e005409d757082511e6a4b679fa 100644 (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
-  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, "<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
diff --git a/src/testdir/test_prompt_buffer.vim b/src/testdir/test_prompt_buffer.vim
new file mode 100644 (file)
index 0000000..a21acc7
--- /dev/null
@@ -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
index 72960f3ea43b90bbb37d02f42cdd9ed82102ede0..985e659bf7375cdc2fd268e524f06bcbfd7abb2c 100644 (file)
@@ -761,6 +761,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    27,
 /**/
     26,
 /**/