diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/api/extmark.c | 26 | ||||
-rw-r--r-- | src/nvim/api/keysets.lua | 1 | ||||
-rw-r--r-- | src/nvim/api/vim.c | 5 | ||||
-rw-r--r-- | src/nvim/autocmd.c | 5 | ||||
-rw-r--r-- | src/nvim/cursor.c | 1 | ||||
-rw-r--r-- | src/nvim/edit.c | 38 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 3 | ||||
-rw-r--r-- | src/nvim/screen.c | 4 | ||||
-rw-r--r-- | src/nvim/terminal.c | 5 | ||||
-rw-r--r-- | src/nvim/testdir/runtest.vim | 5 | ||||
-rw-r--r-- | src/nvim/testdir/test_filetype.vim | 3 | ||||
-rw-r--r-- | src/nvim/testdir/test_prompt_buffer.vim | 80 | ||||
-rw-r--r-- | src/nvim/window.c | 65 |
13 files changed, 212 insertions, 29 deletions
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c index 6e25e627b9..742b953c2a 100644 --- a/src/nvim/api/extmark.c +++ b/src/nvim/api/extmark.c @@ -339,7 +339,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// @param col Column where to place the mark, 0-based. |api-indexing| /// @param opts Optional parameters. /// - id : id of the extmark to edit. -/// - end_line : ending line of the mark, 0-based inclusive. +/// - end_row : ending line of the mark, 0-based inclusive. /// - end_col : ending col of the mark, 0-based exclusive. /// - hl_group : name of the highlight group used to highlight /// this mark. @@ -431,16 +431,26 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer } int line2 = -1; - if (opts->end_line.type == kObjectTypeInteger) { - Integer val = opts->end_line.data.integer; + + // For backward compatibility we support "end_line" as an alias for "end_row" + if (HAS_KEY(opts->end_line)) { + if (HAS_KEY(opts->end_row)) { + api_set_error(err, kErrorTypeValidation, "cannot use both end_row and end_line"); + goto error; + } + opts->end_row = opts->end_line; + } + + if (opts->end_row.type == kObjectTypeInteger) { + Integer val = opts->end_row.data.integer; if (val < 0 || val > buf->b_ml.ml_line_count) { - api_set_error(err, kErrorTypeValidation, "end_line value outside range"); + api_set_error(err, kErrorTypeValidation, "end_row value outside range"); goto error; } else { line2 = (int)val; } - } else if (HAS_KEY(opts->end_line)) { - api_set_error(err, kErrorTypeValidation, "end_line is not an integer"); + } else if (HAS_KEY(opts->end_row)) { + api_set_error(err, kErrorTypeValidation, "end_row is not an integer"); goto error; } @@ -571,10 +581,10 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer OPTION_TO_BOOL(right_gravity, right_gravity, true); // Only error out if they try to set end_right_gravity without - // setting end_col or end_line + // setting end_col or end_row if (line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)) { api_set_error(err, kErrorTypeValidation, - "cannot set end_right_gravity without setting end_line or end_col"); + "cannot set end_right_gravity without setting end_row or end_col"); goto error; } diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua index e956a54dbc..f3e7f2f1dc 100644 --- a/src/nvim/api/keysets.lua +++ b/src/nvim/api/keysets.lua @@ -5,6 +5,7 @@ return { set_extmark = { "id"; "end_line"; + "end_row"; "end_col"; "hl_group"; "virt_text"; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 36179b1fab..4f7c320129 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -842,7 +842,7 @@ void nvim_echo(Array chunks, Boolean history, Dictionary opts, Error *err) for (uint32_t i = 0; i < kv_size(hl_msg); i++) { HlMessageChunk chunk = kv_A(hl_msg, i); msg_multiline_attr((const char *)chunk.text.data, chunk.attr, - false, &need_clear); + true, &need_clear); } if (history) { msg_ext_set_kind("echomsg"); @@ -1842,6 +1842,9 @@ static void write_msg(String message, bool to_err) ++no_wait_return; for (uint32_t i = 0; i < message.size; i++) { + if (got_int) { + break; + } if (to_err) { PUSH_CHAR(i, err_pos, err_line_buf, emsg); } else { diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 490fe5a0ac..9044657358 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -1213,10 +1213,13 @@ win_found: // Hmm, original window disappeared. Just use the first one. curwin = firstwin; } + curbuf = curwin->w_buffer; + // May need to restore insert mode for a prompt buffer. + entering_window(curwin); + prevwin = win_find_by_handle(aco->save_prevwin_handle); vars_clear(&aucmd_win->w_vars->dv_hashtab); // free all w: variables hash_init(&aucmd_win->w_vars->dv_hashtab); // re-use the hashtab - curbuf = curwin->w_buffer; xfree(globaldir); globaldir = aco->globaldir; diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index e334fd166e..4e1d7f9d78 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -108,6 +108,7 @@ static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_a int head = 0; one_more = (State & INSERT) + || (State & TERM_FOCUS) || restart_edit != NUL || (VIsual_active && *p_sel != 'o') || ((ve_flags & VE_ONEMORE) && wcol < MAXCOL); diff --git a/src/nvim/edit.c b/src/nvim/edit.c index fe69da7fcb..9bfb8a9d4a 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -227,6 +227,7 @@ typedef struct insert_state { cmdarg_T *ca; int mincol; int cmdchar; + int cmdchar_todo; // cmdchar to handle once in init_prompt int startln; long count; int c; @@ -290,6 +291,7 @@ static void insert_enter(InsertState *s) s->did_backspace = true; s->old_topfill = -1; s->replaceState = REPLACE; + s->cmdchar_todo = s->cmdchar; // Remember whether editing was restarted after CTRL-O did_restart_edit = restart_edit; // sleep before redrawing, needed for "CTRL-O :" that results in an @@ -585,7 +587,8 @@ static int insert_check(VimState *state) } if (bt_prompt(curbuf)) { - init_prompt(s->cmdchar); + init_prompt(s->cmdchar_todo); + s->cmdchar_todo = NUL; } // If we inserted a character at the last position of the last line in the @@ -655,10 +658,17 @@ static int insert_check(VimState *state) static int insert_execute(VimState *state, int key) { + InsertState *const s = (InsertState *)state; + if (stop_insert_mode) { + // Insert mode ended, possibly from a callback. + s->count = 0; + s->nomove = true; + return 0; + } + if (key == K_IGNORE || key == K_NOP) { return -1; // get another key } - InsertState *s = (InsertState *)state; s->c = key; // Don't want K_EVENT with cursorhold for the second key, e.g., after CTRL-V. @@ -984,6 +994,15 @@ static int insert_handle_key(InsertState *s) break; case Ctrl_W: // delete word before the cursor + if (bt_prompt(curbuf) && (mod_mask & MOD_MASK_SHIFT) == 0) { + // In a prompt window CTRL-W is used for window commands. + // Use Shift-CTRL-W to delete a word. + stuffcharReadbuff(Ctrl_W); + restart_edit = 'A'; + s->nomove = true; + s->count = 0; + return 0; + } s->did_backspace = ins_bs(s->c, BACKSPACE_WORD, &s->inserted_space); auto_format(false, true); break; @@ -1653,10 +1672,21 @@ static void init_prompt(int cmdchar_todo) coladvance(MAXCOL); changed_bytes(curbuf->b_ml.ml_line_count, 0); } + + // Insert always starts after the prompt, allow editing text after it. + if (Insstart_orig.lnum != curwin->w_cursor.lnum || Insstart_orig.col != (colnr_T)STRLEN(prompt)) { + Insstart.lnum = curwin->w_cursor.lnum; + Insstart.col = STRLEN(prompt); + Insstart_orig = Insstart; + Insstart_textlen = Insstart.col; + Insstart_blank_vcol = MAXCOL; + arrow_used = false; + } + if (cmdchar_todo == 'A') { coladvance(MAXCOL); } - if (cmdchar_todo == 'I' || curwin->w_cursor.col <= (int)STRLEN(prompt)) { + if (curwin->w_cursor.col < (colnr_T)STRLEN(prompt)) { curwin->w_cursor.col = STRLEN(prompt); } // Make sure the cursor is in a valid position. @@ -8241,7 +8271,7 @@ static bool ins_bs(int c, int mode, int *inserted_space_p) || (!revins_on && ((curwin->w_cursor.lnum == 1 && curwin->w_cursor.col == 0) || (!can_bs(BS_START) - && (arrow_used + && ((arrow_used && !bt_prompt(curbuf)) || (curwin->w_cursor.lnum == Insstart_orig.lnum && curwin->w_cursor.col <= Insstart_orig.col))) || (!can_bs(BS_INDENT) && !arrow_used && ai_col > 0 diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 6a8b70a158..a899ca63ac 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -521,6 +521,9 @@ static void nlua_print_event(void **argv) const size_t len = (size_t)(intptr_t)argv[1]-1; // exclude final NUL for (size_t i = 0; i < len;) { + if (got_int) { + break; + } const size_t start = i; while (i < len) { switch (str[i]) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 4146ae0fe3..a666b9c8b0 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -6932,7 +6932,7 @@ int showmode(void) do_mode = ((p_smd && msg_silent == 0) && ((State & TERM_FOCUS) || (State & INSERT) - || restart_edit + || restart_edit != NUL || VIsual_active)); if (do_mode || reg_recording != 0) { // Don't show mode right now, when not redrawing or inside a mapping. @@ -7012,7 +7012,7 @@ int showmode(void) } msg_puts_attr(_(" INSERT"), attr); } else if (restart_edit == 'I' || restart_edit == 'i' - || restart_edit == 'a') { + || restart_edit == 'a' || restart_edit == 'A') { msg_puts_attr(_(" (insert)"), attr); } else if (restart_edit == 'R') { msg_puts_attr(_(" (replace)"), attr); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 83ade74db1..afebda4948 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -46,6 +46,7 @@ #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/change.h" +#include "nvim/cursor.h" #include "nvim/edit.h" #include "nvim/event/loop.h" #include "nvim/event/time.h" @@ -464,9 +465,7 @@ static void terminal_check_cursor(void) row_to_linenr(term, term->cursor.row)); // Nudge cursor when returning to normal-mode. int off = is_focused(term) ? 0 : (curwin->w_p_rl ? 1 : -1); - curwin->w_cursor.col = MAX(0, term->cursor.col + win_col_off(curwin) + off); - curwin->w_cursor.coladd = 0; - mb_check_adjust_col(curwin); + coladvance(MAX(0, term->cursor.col + off)); } // Function executed before each iteration of terminal mode. diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 49993c03aa..ab047fd2a8 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -197,7 +197,12 @@ func RunTheTest(test) " Close any extra tab pages and windows and make the current one not modified. while tabpagenr('$') > 1 + let winid = win_getid() quit! + if winid == win_getid() + echoerr 'Could not quit window' + break + endif endwhile while 1 diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index 69edbc227d..31052ce47d 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -502,7 +502,7 @@ let s:filename_checks = { \ 'tex': ['file.latex', 'file.sty', 'file.dtx', 'file.ltx', 'file.bbl'], \ 'texinfo': ['file.texinfo', 'file.texi', 'file.txi'], \ 'texmf': ['texmf.cnf'], - \ 'text': ['file.text', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'], + \ 'text': ['file.text', 'file.txt', 'README', 'LICENSE', 'COPYING', 'AUTHORS', '/usr/share/doc/bash-completion/AUTHORS', '/etc/apt/apt.conf.d/README', '/etc/Muttrc.d/README'], \ 'tf': ['file.tf', '.tfrc', 'tfrc'], \ 'tidy': ['.tidyrc', 'tidyrc', 'tidy.conf'], \ 'tilde': ['file.t.html'], @@ -568,6 +568,7 @@ let s:filename_checks = { \ 'yaml': ['file.yaml', 'file.yml'], \ 'raml': ['file.raml'], \ 'z8a': ['file.z8a'], + \ 'zig': ['file.zig'], \ 'zimbu': ['file.zu'], \ 'zimbutempl': ['file.zut'], \ 'zsh': ['.zprofile', '/etc/zprofile', '.zfbfmarks', 'file.zsh', '.zcompdump', '.zlogin', '.zlogout', '.zshenv', '.zshrc', '.zcompdump-file', '.zlog', '.zlog-file', '.zsh', '.zsh-file', 'any/etc/zprofile', 'zlog', 'zlog-file', 'zsh', 'zsh-file'], diff --git a/src/nvim/testdir/test_prompt_buffer.vim b/src/nvim/testdir/test_prompt_buffer.vim index c59a00afcc..8f94a8572b 100644 --- a/src/nvim/testdir/test_prompt_buffer.vim +++ b/src/nvim/testdir/test_prompt_buffer.vim @@ -41,6 +41,10 @@ func WriteScript(name) \ ' set nomodified', \ 'endfunc', \ '', + \ 'func SwitchWindows()', + \ ' call timer_start(0, {-> execute("wincmd p|wincmd p", "")})', + \ 'endfunc', + \ '', \ 'call setline(1, "other buffer")', \ 'set nomodified', \ 'new', @@ -89,9 +93,12 @@ func Test_prompt_editing() call term_sendkeys(buf, left . left . left . bs . '-') call WaitForAssert({-> assert_equal('cmd: -hel', term_getline(buf, 1))}) + call term_sendkeys(buf, "\<C-O>lz") + call WaitForAssert({-> assert_equal('cmd: -hzel', term_getline(buf, 1))}) + let end = "\<End>" call term_sendkeys(buf, end . "x") - call WaitForAssert({-> assert_equal('cmd: -helx', term_getline(buf, 1))}) + call WaitForAssert({-> assert_equal('cmd: -hzelx', term_getline(buf, 1))}) call term_sendkeys(buf, "\<C-U>exit\<CR>") call WaitForAssert({-> assert_equal('other buffer', term_getline(buf, 1))}) @@ -100,6 +107,28 @@ func Test_prompt_editing() call delete(scriptName) endfunc +func Test_prompt_switch_windows() + throw 'skipped: TODO' + call CanTestPromptBuffer() + let scriptName = 'XpromptSwitchWindows' + call WriteScript(scriptName) + + let buf = RunVimInTerminal('-S ' . scriptName, {'rows': 12}) + call WaitForAssert({-> assert_equal('cmd:', term_getline(buf, 1))}) + call WaitForAssert({-> assert_match('-- INSERT --', term_getline(buf, 12))}) + + call term_sendkeys(buf, "\<C-O>:call SwitchWindows()\<CR>") + call term_wait(buf, 50) + call WaitForAssert({-> assert_match('-- INSERT --', term_getline(buf, 12))}) + + call term_sendkeys(buf, "\<Esc>") + call term_wait(buf, 50) + call WaitForAssert({-> assert_match('^ *$', term_getline(buf, 12))}) + + call StopVimInTerminal(buf) + call delete(scriptName) +endfunc + func Test_prompt_garbage_collect() func MyPromptCallback(x, text) " NOP @@ -126,6 +155,14 @@ func Test_prompt_garbage_collect() bwipe! endfunc +func Test_prompt_backspace() + new + set buftype=prompt + call feedkeys("A123456\<Left>\<BS>\<Esc>", 'xt') + call assert_equal('% 12346', getline(1)) + bwipe! +endfunc + " Test for editing the prompt buffer func Test_prompt_buffer_edit() new @@ -145,10 +182,9 @@ func Test_prompt_buffer_edit() call assert_beeps("normal! \<C-X>") " pressing CTRL-W in the prompt buffer should trigger the window commands call assert_equal(1, winnr()) - " In Nvim, CTRL-W commands aren't usable from insert mode in a prompt buffer - " exe "normal A\<C-W>\<C-W>" - " call assert_equal(2, winnr()) - " wincmd w + exe "normal A\<C-W>\<C-W>" + call assert_equal(2, winnr()) + wincmd w close! call assert_equal(0, prompt_setprompt([], '')) endfunc @@ -187,4 +223,38 @@ func Test_prompt_buffer_getbufinfo() %bwipe! endfunc +function! Test_prompt_while_writing_to_hidden_buffer() + throw 'skipped: TODO' + call CanTestPromptBuffer() + CheckUnix + + " Make a job continuously write to a hidden buffer, check that the prompt + " buffer is not affected. + let scriptName = 'XpromptscriptHiddenBuf' + let script =<< trim END + set buftype=prompt + call prompt_setprompt( bufnr(), 'cmd:' ) + let job = job_start(['/bin/sh', '-c', + \ 'while true; + \ do echo line; + \ sleep 0.1; + \ done'], #{out_io: 'buffer', out_name: ''}) + startinsert + END + eval script->writefile(scriptName) + + let buf = RunVimInTerminal('-S ' .. scriptName, {}) + call WaitForAssert({-> assert_equal('cmd:', term_getline(buf, 1))}) + + call term_sendkeys(buf, 'test') + call WaitForAssert({-> assert_equal('cmd:test', term_getline(buf, 1))}) + call term_sendkeys(buf, 'test') + call WaitForAssert({-> assert_equal('cmd:testtest', term_getline(buf, 1))}) + call term_sendkeys(buf, 'test') + call WaitForAssert({-> assert_equal('cmd:testtesttest', term_getline(buf, 1))}) + + call StopVimInTerminal(buf) + call delete(scriptName) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/window.c b/src/nvim/window.c index 3e6e42dec2..be963d8374 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -2229,6 +2229,54 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } } +static void leaving_window(win_T *const win) + FUNC_ATTR_NONNULL_ALL +{ + // Only matters for a prompt window. + if (!bt_prompt(win->w_buffer)) { + return; + } + + // When leaving a prompt window stop Insert mode and perhaps restart + // it when entering that window again. + win->w_buffer->b_prompt_insert = restart_edit; + if (restart_edit != NUL && mode_displayed) { + clear_cmdline = true; // unshow mode later + } + restart_edit = NUL; + + // When leaving the window (or closing the window) was done from a + // callback we need to break out of the Insert mode loop and restart Insert + // mode when entering the window again. + if (State & INSERT) { + stop_insert_mode = true; + if (win->w_buffer->b_prompt_insert == NUL) { + win->w_buffer->b_prompt_insert = 'A'; + } + } +} + +void entering_window(win_T *const win) + FUNC_ATTR_NONNULL_ALL +{ + // Only matters for a prompt window. + if (!bt_prompt(win->w_buffer)) { + return; + } + + // When switching to a prompt buffer that was in Insert mode, don't stop + // Insert mode, it may have been set in leaving_window(). + if (win->w_buffer->b_prompt_insert != NUL) { + stop_insert_mode = false; + } + + // When entering the prompt window restart Insert mode if we were in Insert + // mode when we left it and not already in Insert mode. + if ((State & INSERT) == 0) { + restart_edit = win->w_buffer->b_prompt_insert; + } +} + /// Closes all windows for buffer `buf`. /// /// @param keep_curwin don't close `curwin` @@ -2367,6 +2415,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev shell_new_rows(); } } + entering_window(curwin); // Since goto_tabpage_tp above did not trigger *Enter autocommands, do // that now. @@ -2434,10 +2483,10 @@ int win_close(win_T *win, bool free_buf) } if (win == curwin) { - /* - * Guess which window is going to be the new current window. - * This may change because of the autocommands (sigh). - */ + leaving_window(curwin); + + // Guess which window is going to be the new current window. + // This may change because of the autocommands (sigh). if (!win->w_floating) { wp = frame2win(win_altframe(win, NULL)); } else { @@ -3801,6 +3850,8 @@ int win_new_tabpage(int after, char_u *filename) lastused_tabpage = old_curtab; + entering_window(curwin); + apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf); @@ -3956,6 +4007,7 @@ static int leave_tabpage(buf_T *new_curbuf, bool trigger_leave_autocmds) { tabpage_T *tp = curtab; + leaving_window(curwin); reset_VIsual_and_resel(); // stop Visual mode if (trigger_leave_autocmds) { if (new_curbuf != curbuf) { @@ -4478,6 +4530,10 @@ static void win_enter_ext(win_T *const wp, const int flags) return; } + if (!curwin_invalid) { + leaving_window(curwin); + } + if (!curwin_invalid && (flags & WEE_TRIGGER_LEAVE_AUTOCMDS)) { // Be careful: If autocommands delete the window, return now. if (wp->w_buffer != curbuf) { @@ -4525,6 +4581,7 @@ static void win_enter_ext(win_T *const wp, const int flags) fix_current_dir(); + entering_window(curwin); // Careful: autocommands may close the window and make "wp" invalid if (flags & WEE_TRIGGER_NEW_AUTOCMDS) { apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); |