diff options
Diffstat (limited to 'src')
57 files changed, 1260 insertions, 292 deletions
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 2d803792c8..65c3c6bbb9 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -457,6 +457,8 @@ if(WIN32) COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/Qt5Widgets.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/winpty.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ + COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/libiconv-2.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/ + COMMAND ${CMAKE_COMMAND} -E copy "${DEPS_PREFIX}/bin/platforms/qwindows.dll" ${PROJECT_BINARY_DIR}/windows_runtime_deps/platforms/ ) add_dependencies(nvim_runtime_deps external_blobs) @@ -484,7 +486,9 @@ set_property( APPEND_STRING PROPERTY COMPILE_FLAGS " -DMAKE_LIB " ) -if(LUAJIT_FOUND) +if(NOT LUAJIT_FOUND) + message(STATUS "luajit not found, skipping nvim-test (unit tests) target") +else() set(NVIM_TEST_LINK_LIBRARIES ${NVIM_LINK_LIBRARIES} ${LUAJIT_LIBRARIES}) add_library( nvim-test diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index f8eebcdb10..5207a57b88 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -29,6 +29,8 @@ static void msgpack_rpc_add_method_handler(String method, map_put(String, MsgpackRpcRequestHandler)(methods, method, handler); } +/// @param name API method name +/// @param name_len name size (includes terminating NUL) MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t name_len) { diff --git a/src/nvim/api/ui.c b/src/nvim/api/ui.c index 4cd2657561..b6e0b9a566 100644 --- a/src/nvim/api/ui.c +++ b/src/nvim/api/ui.c @@ -97,6 +97,7 @@ void nvim_ui_attach(uint64_t channel_id, Integer width, Integer height, ui->set_icon = remote_ui_set_icon; ui->option_set = remote_ui_option_set; ui->event = remote_ui_event; + ui->inspect = remote_ui_inspect; memset(ui->ui_ext, 0, sizeof(ui->ui_ext)); @@ -275,3 +276,9 @@ static void remote_ui_event(UI *ui, char *name, Array args, bool *args_consumed) } push_call(ui, name, my_args); } + +static void remote_ui_inspect(UI *ui, Dictionary *info) +{ + UIData *data = ui->data; + PUT(*info, "chan", INTEGER_OBJ((Integer)data->channel_id)); +} diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h index 96d494460b..3ef16a7ac3 100644 --- a/src/nvim/api/ui_events.in.h +++ b/src/nvim/api/ui_events.in.h @@ -62,7 +62,7 @@ void set_title(String title) void set_icon(String icon) FUNC_API_SINCE(3); void option_set(String name, Object value) - FUNC_API_SINCE(4); + FUNC_API_SINCE(4) FUNC_API_BRIDGE_IMPL; void popupmenu_show(Array items, Integer selected, Integer row, Integer col) FUNC_API_SINCE(3) FUNC_API_REMOTE_ONLY; diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index f587948cf0..b3ae52602b 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -16,6 +16,7 @@ #include "nvim/api/private/dispatch.h" #include "nvim/api/buffer.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/helpers.h" #include "nvim/lua/executor.h" #include "nvim/vim.h" #include "nvim/buffer.h" @@ -1163,6 +1164,11 @@ Array nvim_call_atomic(uint64_t channel_id, Array calls, Error *err) MsgpackRpcRequestHandler handler = msgpack_rpc_get_handler_for(name.data, name.size); + if (handler.fn == msgpack_rpc_handle_missing_method) { + api_set_error(&nested_error, kErrorTypeException, "Invalid method: %s", + name.size > 0 ? name.data : "<empty>"); + break; + } Object result = handler.fn(channel_id, args, &nested_error); if (ERROR_SET(&nested_error)) { // error handled after loop @@ -1747,6 +1753,14 @@ Dictionary nvim__stats(void) /// Gets a list of dictionaries representing attached UIs. /// /// @return Array of UI dictionaries +/// +/// Each dictionary has the following keys: +/// - "height" requested height of the UI +/// - "width" requested width of the UI +/// - "rgb" whether the UI uses rgb colors (false implies cterm colors) +/// - "ext_..." Requested UI extensions, see |ui-options| +/// - "chan" Channel id of remote UI (not present for TUI) +/// Array nvim_list_uis(void) FUNC_API_SINCE(4) { diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index af77a9891b..838f267dcd 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -575,7 +575,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) /* Change directories when the 'acd' option is set. */ do_autochdir(); - // disable live updates for the current buffer + // disable buffer updates for the current buffer buf_updates_unregister_all(buf); /* diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c index 4774b969c4..157f80e55a 100644 --- a/src/nvim/buffer_updates.c +++ b/src/nvim/buffer_updates.c @@ -1,3 +1,6 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check +// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + #include "nvim/buffer_updates.h" #include "nvim/memline.h" #include "nvim/api/private/helpers.h" @@ -199,7 +202,7 @@ void buf_updates_send_changes(buf_T *buf, // change notifications are so frequent that many dead channels will be // cleared up quickly. if (badchannelid != 0) { - ELOG("Disabling live updates for dead channel %llu", badchannelid); + ELOG("Disabling buffer updates for dead channel %llu", badchannelid); buf_updates_unregister(buf, badchannelid); } } diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 64d743891b..6ad64bbb85 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -602,6 +602,7 @@ static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, // process_channel_event will modify the read buffer(convert NULs into NLs) if (chan->term) { terminal_receive(chan->term, ptr, count); + terminal_flush_output(chan->term); } rbuffer_consumed(buf, count); diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 7d5f80c531..4f70bcc41a 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1018,7 +1018,7 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he // needs a break here if (wp->w_p_lbr && vim_isbreak(c) - && !vim_isbreak(s[1]) + && !vim_isbreak((int)s[1]) && wp->w_p_wrap && (wp->w_width != 0)) { // Count all characters from first non-blank after a blank up to next @@ -1042,7 +1042,7 @@ int win_lbr_chartabsize(win_T *wp, char_u *line, char_u *s, colnr_T col, int *he c = *s; if (!(c != NUL - && (vim_isbreak(c) || col2 == col || !vim_isbreak(*ps)))) { + && (vim_isbreak(c) || col2 == col || !vim_isbreak((int)(*ps))))) { break; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ffea88aa83..9c29f18c0c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6742,36 +6742,39 @@ static void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *tofree; if (opt_msg_tv->v_type != VAR_UNKNOWN) { - tofree = (char_u *) encode_tv2string(opt_msg_tv, NULL); + tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL); ga_concat(gap, tofree); xfree(tofree); + ga_concat(gap, (char_u *)": "); + } + + if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) { + ga_concat(gap, (char_u *)"Pattern "); + } else if (atype == ASSERT_NOTEQUAL) { + ga_concat(gap, (char_u *)"Expected not equal to "); } else { - if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) { - ga_concat(gap, (char_u *)"Pattern "); - } else if (atype == ASSERT_NOTEQUAL) { - ga_concat(gap, (char_u *)"Expected not equal to "); - } else { - ga_concat(gap, (char_u *)"Expected "); - } - if (exp_str == NULL) { - tofree = (char_u *)encode_tv2string(exp_tv, NULL); - ga_concat_esc(gap, tofree); - xfree(tofree); + ga_concat(gap, (char_u *)"Expected "); + } + + if (exp_str == NULL) { + tofree = (char_u *)encode_tv2string(exp_tv, NULL); + ga_concat_esc(gap, tofree); + xfree(tofree); + } else { + ga_concat_esc(gap, exp_str); + } + + if (atype != ASSERT_NOTEQUAL) { + if (atype == ASSERT_MATCH) { + ga_concat(gap, (char_u *)" does not match "); + } else if (atype == ASSERT_NOTMATCH) { + ga_concat(gap, (char_u *)" does match "); } else { - ga_concat_esc(gap, exp_str); - } - if (atype != ASSERT_NOTEQUAL) { - if (atype == ASSERT_MATCH) { - ga_concat(gap, (char_u *)" does not match "); - } else if (atype == ASSERT_NOTMATCH) { - ga_concat(gap, (char_u *)" does match "); - } else { - ga_concat(gap, (char_u *)" but got "); - } - tofree = (char_u *)encode_tv2string(got_tv, NULL); - ga_concat_esc(gap, tofree); - xfree(tofree); + ga_concat(gap, (char_u *)" but got "); } + tofree = (char_u *)encode_tv2string(got_tv, NULL); + ga_concat_esc(gap, tofree); + xfree(tofree); } } @@ -14806,18 +14809,14 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) pos.col = 0; } if (name[0] == '.' && name[1] == NUL) { - // set cursor - if (fnum == curbuf->b_fnum) { - curwin->w_cursor = pos; - if (curswant >= 0) { - curwin->w_curswant = curswant - 1; - curwin->w_set_curswant = false; - } - check_cursor(); - rettv->vval.v_number = 0; - } else { - EMSG(_(e_invarg)); + // set cursor; "fnum" is ignored + curwin->w_cursor = pos; + if (curswant >= 0) { + curwin->w_curswant = curswant - 1; + curwin->w_set_curswant = false; } + check_cursor(); + rettv->vval.v_number = 0; } else if (name[0] == '\'' && name[1] != NUL && name[2] == NUL) { // set mark if (setmark_pos((uint8_t)name[1], &pos, fnum) == OK) { diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index ce02808ad3..c87e3d4c66 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -698,7 +698,7 @@ return { }, { command='delfunction', - flags=bit.bor(NEEDARG, WORD1, CMDWIN), + flags=bit.bor(BANG, NEEDARG, WORD1, CMDWIN), addr_type=ADDR_LINES, func='ex_delfunction', }, @@ -3082,7 +3082,7 @@ return { }, { command='windo', - flags=bit.bor(BANG, NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), + flags=bit.bor(NEEDARG, EXTRA, NOTRLCOM, RANGE, NOTADR, DFLALL), addr_type=ADDR_WINDOWS, func='ex_listdo', }, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 52b810085c..e1efd5710d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -8104,6 +8104,7 @@ static void ex_normal(exarg_T *eap) if (eap->addr_count != 0) { curwin->w_cursor.lnum = eap->line1++; curwin->w_cursor.col = 0; + check_cursor_moved(curwin); } exec_normal_cmd( diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 96388a2a9d..2c828be083 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -295,10 +295,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) redir_off = true; // don't redirect the typed command if (!cmd_silent) { - s->i = msg_scrolled; - msg_scrolled = 0; // avoid wait_return message gotocmdline(true); - msg_scrolled += s->i; redrawcmdprompt(); // draw prompt or indent set_cmdspos(); } @@ -349,7 +346,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) // redraw the statusline for statuslines that display the current mode // using the mode() function. - if (KeyTyped) { + if (KeyTyped && msg_scrolled == 0) { curwin->w_redr_status = true; redraw_statuslines(); } @@ -629,6 +626,7 @@ static int command_line_execute(VimState *state, int key) // Entered command line, move it up cmdline_row--; redrawcmd(); + wild_menu_showing = 0; } else if (save_p_ls != -1) { // restore 'laststatus' and 'winminheight' p_ls = save_p_ls; @@ -639,12 +637,13 @@ static int command_line_execute(VimState *state, int key) restore_cmdline(&s->save_ccline); redrawcmd(); save_p_ls = -1; + wild_menu_showing = 0; } else { win_redraw_last_status(topframe); + wild_menu_showing = 0; // must be before redraw_statuslines #8385 redraw_statuslines(); } KeyTyped = skt; - wild_menu_showing = 0; if (ccline.input_fn) { RedrawingDisabled = old_RedrawingDisabled; } diff --git a/src/nvim/fold.c b/src/nvim/fold.c index b8ace511e8..52ed2fe3dc 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2464,27 +2464,27 @@ static linenr_T foldUpdateIEMSRecurse(garray_T *gap, int level, flp->lnum - 1 - fp->fd_top); if (lvl < level) { - /* End of fold found, update the length when it got shorter. */ + // End of fold found, update the length when it got shorter. if (fp->fd_len != flp->lnum - fp->fd_top) { - if (fp->fd_top + fp->fd_len > bot + 1) { - /* fold continued below bot */ + if (fp->fd_top + fp->fd_len - 1 > bot) { + // fold continued below bot if (getlevel == foldlevelMarker || getlevel == foldlevelExpr || getlevel == foldlevelSyntax) { - /* marker method: truncate the fold and make sure the - * previously included lines are processed again */ + // marker method: truncate the fold and make sure the + // previously included lines are processed again bot = fp->fd_top + fp->fd_len - 1; fp->fd_len = flp->lnum - fp->fd_top; } else { - /* indent or expr method: split fold to create a new one - * below bot */ + // indent or expr method: split fold to create a new one + // below bot i = (int)(fp - (fold_T *)gap->ga_data); foldSplit(gap, i, flp->lnum, bot); fp = (fold_T *)gap->ga_data + i; } } else fp->fd_len = flp->lnum - fp->fd_top; - fold_changed = TRUE; + fold_changed = true; } } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 4aa0ef7def..2860817f79 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -745,11 +745,9 @@ EXTERN int State INIT(= NORMAL); /* This is the current state of the EXTERN bool finish_op INIT(= false); // true while an operator is pending EXTERN long opcount INIT(= 0); // count for pending operator -/* - * ex mode (Q) state - */ -EXTERN int exmode_active INIT(= 0); /* zero, EXMODE_NORMAL or EXMODE_VIM */ -EXTERN int ex_no_reprint INIT(= FALSE); /* no need to print after z or p */ +// Ex Mode (Q) state +EXTERN int exmode_active INIT(= 0); // zero, EXMODE_NORMAL or EXMODE_VIM +EXTERN int ex_no_reprint INIT(= false); // no need to print after z or p EXTERN int Recording INIT(= FALSE); /* TRUE when recording into a reg. */ EXTERN int Exec_reg INIT(= FALSE); /* TRUE when executing a register */ diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 3cd26a5bf7..bcd9cf2090 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -106,39 +106,41 @@ int setmark_pos(int c, pos_T *pos, int fnum) return OK; } + // Can't set a mark in a non-existant buffer. + buf_T *buf = buflist_findnr(fnum); + if (buf == NULL) { + return FAIL; + } + if (c == '"') { - RESET_FMARK(&curbuf->b_last_cursor, *pos, curbuf->b_fnum); + RESET_FMARK(&buf->b_last_cursor, *pos, buf->b_fnum); return OK; } /* Allow setting '[ and '] for an autocommand that simulates reading a * file. */ if (c == '[') { - curbuf->b_op_start = *pos; + buf->b_op_start = *pos; return OK; } if (c == ']') { - curbuf->b_op_end = *pos; + buf->b_op_end = *pos; return OK; } if (c == '<' || c == '>') { - if (c == '<') - curbuf->b_visual.vi_start = *pos; - else - curbuf->b_visual.vi_end = *pos; - if (curbuf->b_visual.vi_mode == NUL) - /* Visual_mode has not yet been set, use a sane default. */ - curbuf->b_visual.vi_mode = 'v'; + if (c == '<') { + buf->b_visual.vi_start = *pos; + } else { + buf->b_visual.vi_end = *pos; + } + if (buf->b_visual.vi_mode == NUL) { + // Visual_mode has not yet been set, use a sane default. + buf->b_visual.vi_mode = 'v'; + } return OK; } - buf_T *buf = buflist_findnr(fnum); - // Can't set a mark in a non-existant buffer. - if (buf == NULL) { - return FAIL; - } - if (ASCII_ISLOWER(c)) { i = c - 'a'; RESET_FMARK(buf->b_namedm + i, *pos, fnum); @@ -358,13 +360,14 @@ pos_T *getmark_buf_fnum(buf_T *buf, int c, int changefile, int *fnum) } else if (c == '<' || c == '>') { /* start/end of visual area */ startp = &buf->b_visual.vi_start; endp = &buf->b_visual.vi_end; - if ((c == '<') == lt(*startp, *endp)) + if (((c == '<') == lt(*startp, *endp) || endp->lnum == 0) + && startp->lnum != 0) { posp = startp; - else + } else { posp = endp; - /* - * For Visual line mode, set mark at begin or end of line - */ + } + + // For Visual line mode, set mark at begin or end of line if (buf->b_visual.vi_mode == 'V') { pos_copy = *posp; posp = &pos_copy; @@ -647,8 +650,8 @@ void do_marks(exarg_T *eap) show_one_mark(-1, arg, NULL, NULL, false); } -static void -show_one_mark ( +static void +show_one_mark( int c, char_u *arg, pos_T *p, diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index a52ab9f5d3..05e326104b 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -37,6 +37,8 @@ #ifdef HAVE_LOCALE_H # include <locale.h> #endif +#include "nvim/eval.h" +#include "nvim/path.h" #include "nvim/iconv.h" #include "nvim/mbyte.h" #include "nvim/charset.h" @@ -72,6 +74,9 @@ struct interval { # include "unicode_tables.generated.h" #endif +char_u e_loadlib[] = "E370: Could not load library %s"; +char_u e_loadfunc[] = "E448: Could not load library function %s"; + // To speed up BYTELEN(); keep a lookup table to quickly get the length in // bytes of a UTF-8 character from the first byte of a UTF-8 string. Bytes // which are illegal when used as the first byte have a 1. The NUL byte has @@ -2038,9 +2043,10 @@ void * my_iconv_open(char_u *to, char_u *from) return (void *)-1; /* detected a broken iconv() previously */ #ifdef DYNAMIC_ICONV - /* Check if the iconv.dll can be found. */ - if (!iconv_enabled(true)) + // Check if the iconv.dll can be found. + if (!iconv_enabled(true)) { return (void *)-1; + } #endif fd = iconv_open((char *)enc_skip(to), (char *)enc_skip(from)); @@ -2162,7 +2168,7 @@ static HINSTANCE hMsvcrtDLL = 0; # ifndef DYNAMIC_ICONV_DLL # define DYNAMIC_ICONV_DLL "iconv.dll" -# define DYNAMIC_ICONV_DLL_ALT "libiconv.dll" +# define DYNAMIC_ICONV_DLL_ALT "libiconv-2.dll" # endif # ifndef DYNAMIC_MSVCRT_DLL # define DYNAMIC_MSVCRT_DLL "msvcrt.dll" @@ -2208,6 +2214,35 @@ static void * get_iconv_import_func(HINSTANCE hInst, return NULL; } +// Load library "name". +HINSTANCE vimLoadLib(char *name) +{ + HINSTANCE dll = NULL; + + // NOTE: Do not use mch_dirname() and mch_chdir() here, they may call + // vimLoadLib() recursively, which causes a stack overflow. + WCHAR old_dirw[MAXPATHL]; + + // Path to exe dir. + char *buf = xstrdup((char *)get_vim_var_str(VV_PROGPATH)); + // ptrdiff_t len = ; + // assert(len > 0); + buf[path_tail_with_sep(buf) - buf] = '\0'; + + if (GetCurrentDirectoryW(MAXPATHL, old_dirw) != 0) { + // Change directory to where the executable is, both to make + // sure we find a .dll there and to avoid looking for a .dll + // in the current directory. + SetCurrentDirectory((LPCSTR)buf); + // TODO(justinmk): use uv_dlopen instead. see os_libcall + dll = LoadLibrary(name); + SetCurrentDirectoryW(old_dirw); + } + + return dll; +} + + /* * Try opening the iconv.dll and return TRUE if iconv() can be used. */ @@ -2255,10 +2290,13 @@ bool iconv_enabled(bool verbose) void iconv_end(void) { - if (hIconvDLL != 0) + if (hIconvDLL != 0) { + // TODO(justinmk): use uv_dlclose instead. FreeLibrary(hIconvDLL); - if (hMsvcrtDLL != 0) + } + if (hMsvcrtDLL != 0) { FreeLibrary(hMsvcrtDLL); + } hIconvDLL = 0; hMsvcrtDLL = 0; } diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 6c5c47b91f..70733e5564 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -107,7 +107,8 @@ open_line ( char_u *p; char_u saved_char = NUL; // init for GCC pos_T *pos; - bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin); + bool do_si = (!p_paste && curbuf->b_p_si && !curbuf->b_p_cin + && *curbuf->b_p_inde == NUL); bool no_si = false; // reset did_si afterwards int first_char = NUL; // init for GCC int vreplace_mode; diff --git a/src/nvim/move.c b/src/nvim/move.c index cb13a9e207..41859a489f 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -132,11 +132,9 @@ void update_topline(void) bool check_botline = false; long save_so = p_so; - if (!screen_valid(true)) - return; - - // If the window height is zero, just use the cursor line. - if (curwin->w_height == 0) { + // If there is no valid screen and when the window height is zero just use + // the cursor line. + if (!screen_valid(true) || curwin->w_height == 0) { curwin->w_topline = curwin->w_cursor.lnum; curwin->w_botline = curwin->w_topline; curwin->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; @@ -1979,6 +1977,7 @@ void halfpage(bool flag, linenr_T Prenum) int n = curwin->w_p_scr <= curwin->w_height ? (int)curwin->w_p_scr : curwin->w_height; + update_topline(); validate_botline(); int room = curwin->w_empty_rows + curwin->w_filler_rows; if (flag) { diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 26b84b7cc7..6d0c270a51 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -312,24 +312,30 @@ static void handle_request(Channel *channel, msgpack_object *request) api_clear_error(&error); return; } + // Retrieve the request handler MsgpackRpcRequestHandler handler; + Array args = ARRAY_DICT_INIT; msgpack_object *method = msgpack_rpc_method(request); if (method) { handler = msgpack_rpc_get_handler_for(method->via.bin.ptr, method->via.bin.size); + if (handler.fn == msgpack_rpc_handle_missing_method) { + String m = method->via.bin.size > 0 + ? cbuf_to_string(method->via.bin.ptr, method->via.bin.size) + : cstr_to_string("<empty>"); + ADD(args, STRING_OBJ(m)); + handler.async = true; + } else if (!msgpack_rpc_to_array(msgpack_rpc_args(request), &args)) { + handler.fn = msgpack_rpc_handle_invalid_arguments; + handler.async = true; + } } else { handler.fn = msgpack_rpc_handle_missing_method; handler.async = true; } - Array args = ARRAY_DICT_INIT; - if (!msgpack_rpc_to_array(msgpack_rpc_args(request), &args)) { - handler.fn = msgpack_rpc_handle_invalid_arguments; - handler.async = true; - } - RequestEvent *evdata = xmalloc(sizeof(RequestEvent)); evdata->channel = channel; evdata->handler = handler; diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index fecae11d45..e18c4472b5 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -493,7 +493,8 @@ Object msgpack_rpc_handle_missing_method(uint64_t channel_id, Array args, Error *error) { - api_set_error(error, kErrorTypeException, "Invalid method name"); + api_set_error(error, kErrorTypeException, "Invalid method: %s", + args.size > 0 ? args.items[0].data.string.data : "?"); return NIL; } diff --git a/src/nvim/option.c b/src/nvim/option.c index 88c458b597..26fc164c6c 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -939,11 +939,8 @@ void set_init_2(bool headless) { int idx; - /* - * 'scroll' defaults to half the window height. Note that this default is - * wrong when the window height changes. - */ - set_number_default("scroll", Rows / 2); + // 'scroll' defaults to half the window height. The stored default is zero, + // which results in the actual value computed from the window height. idx = findoption("scroll"); if (idx >= 0 && !(options[idx].flags & P_WAS_SET)) { set_option_default(idx, OPT_LOCAL, p_cp); diff --git a/src/nvim/options.lua b/src/nvim/options.lua index f1f559fff0..47c9f5aa78 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -1922,7 +1922,7 @@ return { no_mkrc=true, vi_def=true, pv_name='p_scroll', - defaults={if_true={vi=12}} + defaults={if_true={vi=0}} }, { full_name='scrollback', abbreviation='scbk', diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index 82bb918f70..7c48ac6e5e 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -76,7 +76,7 @@ char *os_get_user_directory(const char *name) { #if defined(HAVE_GETPWNAM) && defined(HAVE_PWD_H) struct passwd *pw; - if (name == NULL) { + if (name == NULL || *name == NUL) { return NULL; } pw = getpwnam(name); // NOLINT(runtime/threadsafe_fn) diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index db93f016bf..356094baa1 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -59,7 +59,6 @@ #define BACKSLASH_IN_FILENAME #ifdef _MSC_VER -typedef SSIZE_T ssize_t; typedef int mode_t; #endif diff --git a/src/nvim/path.c b/src/nvim/path.c index 4f3f7c0661..61cfaea84a 100644 --- a/src/nvim/path.c +++ b/src/nvim/path.c @@ -1097,17 +1097,18 @@ static bool has_env_var(char_u *p) } #ifdef SPECIAL_WILDCHAR -/* - * Return TRUE if "p" contains a special wildcard character. - * Allowing for escaping. - */ + +// Return TRUE if "p" contains a special wildcard character, one that Vim +// cannot expand, requires using a shell. static bool has_special_wildchar(char_u *p) { for (; *p; mb_ptr_adv(p)) { - if (*p == '\\' && p[1] != NUL) - ++p; - else if (vim_strchr((char_u *)SPECIAL_WILDCHAR, *p) != NULL) + // Allow for escaping + if (*p == '\\' && p[1] != NUL) { + p++; + } else if (vim_strchr((char_u *)SPECIAL_WILDCHAR, *p) != NULL) { return true; + } } return false; } @@ -2033,7 +2034,7 @@ int expand_wildcards(int num_pat, char_u **pat, int *num_files, char_u ***files, break; } if (match_file_list(p_wig, (*files)[i], ffname)) { - // remove this matching files from the list + // remove this matching file from the list xfree((*files)[i]); for (j = i; j + 1 < *num_files; j++) { (*files)[j] = (*files)[j + 1]; diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index 94cc63baea..a7b910f0eb 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -1,4 +1,4 @@ -find_package(Gettext) +find_package(Gettext REQUIRED) find_program(XGETTEXT_PRG xgettext) find_program(ICONV_PRG iconv) diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index e4de43b49e..c4af7d9e4a 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -479,6 +479,8 @@ static char_u *regprop(char_u *); #endif static char_u e_missingbracket[] = N_("E769: Missing ] after %s["); +static char_u e_reverse_range[] = N_("E944: Reverse range in character class"); +static char_u e_large_class[] = N_("E945: Range too large in character class"); static char_u e_unmatchedpp[] = N_("E53: Unmatched %s%%("); static char_u e_unmatchedp[] = N_("E54: Unmatched %s("); static char_u e_unmatchedpar[] = N_("E55: Unmatched %s)"); @@ -2232,15 +2234,18 @@ collection: if (endc == '\\' && !reg_cpo_lit) endc = coll_get_char(); - if (startc > endc) - EMSG_RET_NULL(_(e_invrange)); + if (startc > endc) { + EMSG_RET_NULL(_(e_reverse_range)); + } if (has_mbyte && ((*mb_char2len)(startc) > 1 || (*mb_char2len)(endc) > 1)) { - /* Limit to a range of 256 chars */ - if (endc > startc + 256) - EMSG_RET_NULL(_(e_invrange)); - while (++startc <= endc) + // Limit to a range of 256 chars + if (endc > startc + 256) { + EMSG_RET_NULL(_(e_large_class)); + } + while (++startc <= endc) { regmbc(startc); + } } else { while (++startc <= endc) regc(startc); @@ -2328,21 +2333,21 @@ collection: regc('\t'); break; case CLASS_CNTRL: - for (cu = 1; cu <= 255; cu++) { + for (cu = 1; cu <= 127; cu++) { if (iscntrl(cu)) { regmbc(cu); } } break; case CLASS_DIGIT: - for (cu = 1; cu <= 255; cu++) { + for (cu = 1; cu <= 127; cu++) { if (ascii_isdigit(cu)) { regmbc(cu); } } break; case CLASS_GRAPH: - for (cu = 1; cu <= 255; cu++) { + for (cu = 1; cu <= 127; cu++) { if (isgraph(cu)) { regmbc(cu); } diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 98fae858f6..334539b228 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1711,8 +1711,9 @@ collection: if (emit_range) { endc = startc; startc = oldstartc; - if (startc > endc) - EMSG_RET_FAIL(_(e_invrange)); + if (startc > endc) { + EMSG_RET_FAIL(_(e_reverse_range)); + } if (endc > startc + 2) { /* Emit a range instead of the sequence of @@ -4358,16 +4359,18 @@ static int check_char_class(int class, int c) return OK; break; case NFA_CLASS_CNTRL: - if (c >= 1 && c <= 255 && iscntrl(c)) + if (c >= 1 && c <= 127 && iscntrl(c)) { return OK; + } break; case NFA_CLASS_DIGIT: if (ascii_isdigit(c)) return OK; break; case NFA_CLASS_GRAPH: - if (c >= 1 && c <= 255 && isgraph(c)) + if (c >= 1 && c <= 127 && isgraph(c)) { return OK; + } break; case NFA_CLASS_LOWER: if (mb_islower(c) && c != 170 && c != 186) { diff --git a/src/nvim/screen.c b/src/nvim/screen.c index f15afa619f..b0bf3a7d5f 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -3454,7 +3454,8 @@ win_line ( } // Found last space before word: check for line break. - if (wp->w_p_lbr && c0 == c && vim_isbreak(c) && !vim_isbreak(*ptr)) { + if (wp->w_p_lbr && c0 == c && vim_isbreak(c) + && !vim_isbreak((int)(*ptr))) { int mb_off = has_mbyte ? (*mb_head_off)(line, ptr - 1) : 0; char_u *p = ptr - (mb_off + 1); // TODO: is passing p for start of the line OK? @@ -4060,7 +4061,8 @@ win_line ( * Also highlight the 'colorcolumn' if it is different than * 'cursorcolumn' */ vcol_save_attr = -1; - if (draw_state == WL_LINE && !lnum_in_visual_area) { + if (draw_state == WL_LINE && !lnum_in_visual_area + && search_attr == 0 && area_attr == 0) { if (wp->w_p_cuc && VCOL_HLC == (long)wp->w_virtcol && lnum != wp->w_cursor.lnum) { vcol_save_attr = char_attr; diff --git a/src/nvim/search.c b/src/nvim/search.c index 84782497a0..14a7d41300 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -130,8 +130,8 @@ typedef struct SearchedFile { * * returns FAIL if failed, OK otherwise. */ -int -search_regcomp ( +int +search_regcomp( char_u *pat, int pat_save, int pat_use, @@ -2121,9 +2121,9 @@ static int check_linecomment(char_u *line) * Show the match only if it is visible on the screen. * If there isn't a match, then beep. */ -void -showmatch ( - int c /* char to show match for */ +void +showmatch( + int c // char to show match for ) { pos_T *lpos, save_cursor; @@ -2377,8 +2377,14 @@ findpar ( ++curr; curwin->w_cursor.lnum = curr; if (curr == curbuf->b_ml.ml_line_count && what != '}') { - if ((curwin->w_cursor.col = (colnr_T)STRLEN(ml_get(curr))) != 0) { - --curwin->w_cursor.col; + char_u *line = ml_get(curr); + + // Put the cursor on the last character in the last line and make the + // motion inclusive. + if ((curwin->w_cursor.col = (colnr_T)STRLEN(line)) != 0) { + curwin->w_cursor.col--; + curwin->w_cursor.col -= + (*mb_head_off)(line, line + curwin->w_cursor.col); *pincl = true; } } else @@ -2483,8 +2489,8 @@ static int cls(void) * Returns FAIL if the cursor was already at the end of the file. * If eol is TRUE, last word stops at end of line (for operators). */ -int -fwd_word ( +int +fwd_word( long count, int bigword, /* "W", "E" or "B" */ int eol @@ -2666,8 +2672,8 @@ finished: * * Returns FAIL if start of the file was reached. */ -int -bckend_word ( +int +bckend_word( long count, int bigword, /* TRUE for "B" */ int eol /* TRUE: stop at end of line. */ @@ -2756,8 +2762,8 @@ static void find_first_blank(pos_T *posp) /* * Skip count/2 sentences and count/2 separating white spaces. */ -static void -findsent_forward ( +static void +findsent_forward( long count, int at_start_sent /* cursor is at start of sentence */ ) @@ -2776,8 +2782,8 @@ findsent_forward ( * Find word under cursor, cursor at end. * Used while an operator is pending, and in Visual mode. */ -int -current_word ( +int +current_word( oparg_T *oap, long count, int include, /* TRUE: include word and white space */ @@ -3084,8 +3090,8 @@ extend: * Find block under the cursor, cursor at end. * "what" and "other" are two matching parenthesis/brace/etc. */ -int -current_block ( +int +current_block( oparg_T *oap, long count, int include, /* TRUE == include white space */ @@ -3282,8 +3288,8 @@ static int in_html_tag(int end_tag) /* * Find tag block under the cursor, cursor at end. */ -int -current_tagblock ( +int +current_tagblock( oparg_T *oap, long count_arg, int include /* TRUE == include white space */ @@ -3465,8 +3471,8 @@ theend: return retval; } -int -current_par ( +int +current_par( oparg_T *oap, long count, int include, /* TRUE == include white space */ @@ -3632,8 +3638,8 @@ extend: * as a quote. * Returns column number of "quotechar" or -1 when not found. */ -static int -find_next_quote ( +static int +find_next_quote( char_u *line, int col, int quotechar, @@ -3664,8 +3670,8 @@ find_next_quote ( * as a quote. * Return the found column or zero. */ -static int -find_prev_quote ( +static int +find_prev_quote( char_u *line, int col_start, int quotechar, @@ -3694,8 +3700,8 @@ find_prev_quote ( * Find quote under the cursor, cursor at end. * Returns TRUE if found, else FALSE. */ -int -current_quote ( +int +current_quote( oparg_T *oap, long count, int include, /* TRUE == include quote char */ @@ -3920,8 +3926,8 @@ current_quote ( * Find next search match under cursor, cursor at end. * Used while an operator is pending, and in Visual mode. */ -int -current_search ( +int +current_search( long count, int forward /* move forward or backwards */ ) @@ -4116,19 +4122,19 @@ int linewhite(linenr_T lnum) * Find identifiers or defines in included files. * If p_ic && (compl_cont_status & CONT_SOL) then ptr must be in lowercase. */ -void -find_pattern_in_path ( - char_u *ptr, /* pointer to search pattern */ - int dir, /* direction of expansion */ - size_t len, /* length of search pattern */ - int whole, /* match whole words only */ - int skip_comments, /* don't match inside comments */ - int type, /* Type of search; are we looking for a type? - a macro? */ +void +find_pattern_in_path( + char_u *ptr, // pointer to search pattern + int dir, // direction of expansion + size_t len, // length of search pattern + int whole, // match whole words only + int skip_comments, // don't match inside comments + int type, // Type of search; are we looking for a type? + // a macro? long count, - int action, /* What to do when we find it */ - linenr_T start_lnum, /* first line to start searching */ - linenr_T end_lnum /* last line for searching */ + int action, // What to do when we find it + linenr_T start_lnum, // first line to start searching + linenr_T end_lnum // last line for searching ) { SearchedFile *files; /* Stack of included files */ diff --git a/src/nvim/tag.c b/src/nvim/tag.c index ba2727f0d7..d7bdf97c48 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2404,11 +2404,14 @@ jumpto_tag ( } } - /* If it was a CTRL-W CTRL-] command split window now. For ":tab tag" - * open a new tab page. */ + // If it was a CTRL-W CTRL-] command split window now. For ":tab tag" + // open a new tab page. if (postponed_split || cmdmod.tab != 0) { - (void)win_split(postponed_split > 0 ? postponed_split : 0, - postponed_split_flags); + if (win_split(postponed_split > 0 ? postponed_split : 0, + postponed_split_flags) == FAIL) { + RedrawingDisabled--; + goto erret; + } RESET_BINDING(curwin); } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index c29f0b3927..c2370de0f8 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -528,6 +528,13 @@ void terminal_send(Terminal *term, char *data, size_t size) term->opts.write_cb(data, size, term->opts.data); } +void terminal_flush_output(Terminal *term) +{ + size_t len = vterm_output_read(term->vt, term->textbuf, + sizeof(term->textbuf)); + terminal_send(term, term->textbuf, len); +} + void terminal_send_key(Terminal *term, int c) { VTermModifier mod = VTERM_MOD_NONE; @@ -545,9 +552,7 @@ void terminal_send_key(Terminal *term, int c) vterm_keyboard_unichar(term->vt, (uint32_t)c, mod); } - size_t len = vterm_output_read(term->vt, term->textbuf, - sizeof(term->textbuf)); - terminal_send(term, term->textbuf, (size_t)len); + terminal_flush_output(term); } void terminal_receive(Terminal *term, char *data, size_t len) @@ -982,7 +987,7 @@ static bool send_mouse_event(Terminal *term, int c) mouse_action(term, button, row, col, drag, 0); size_t len = vterm_output_read(term->vt, term->textbuf, - sizeof(term->textbuf)); + sizeof(term->textbuf)); terminal_send(term, term->textbuf, (size_t)len); return false; } @@ -1234,8 +1239,9 @@ static void refresh_screen(Terminal *term, buf_T *buf) int change_start = row_to_linenr(term, term->invalid_start); int change_end = change_start + changed; - // Note: don't send nvim_buf_lines_event event for a :terminal buffer - changed_lines(change_start, 0, change_end, added, false); + changed_lines(change_start, 0, change_end, added, + // Don't send nvim_buf_lines_event for :terminal buffer. + false); term->invalid_start = INT_MAX; term->invalid_end = -1; } diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index c1ede08c31..a161f14bc8 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -88,6 +88,8 @@ NEW_TESTS ?= \ test_options.res \ test_profile.res \ test_put.res \ + test_python2.res \ + test_python3.res \ test_quickfix.res \ test_quotestar.res \ test_recover.res \ diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index b4baf7a8d7..c4b4a43ad4 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -27,6 +27,7 @@ source test_popup.vim source test_put.vim source test_recover.vim source test_regexp_utf8.vim +source test_scroll_opt.vim source test_source_utf8.vim source test_sha256.vim source test_statusline.vim diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index be68e9ff9d..8139f00f0e 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -64,6 +64,10 @@ func Test_highlight_completion() hi Aardig ctermfg=green call feedkeys(":hi \<Tab>\<Home>\"\<CR>", 'xt') call assert_equal('"hi Aardig', getreg(':')) + call feedkeys(":hi default \<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"hi default Aardig', getreg(':')) + call feedkeys(":hi clear Aa\<Tab>\<Home>\"\<CR>", 'xt') + call assert_equal('"hi clear Aardig', getreg(':')) call feedkeys(":hi li\<S-Tab>\<Home>\"\<CR>", 'xt') call assert_equal('"hi link', getreg(':')) call feedkeys(":hi d\<S-Tab>\<Home>\"\<CR>", 'xt') diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index 7c6d38d7ec..0c2ec08af3 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -9,8 +9,8 @@ func! Test_address_fold() call setline(1, ['int FuncName() {/*{{{*/', 1, 2, 3, 4, 5, '}/*}}}*/', \ 'after fold 1', 'after fold 2', 'after fold 3']) setl fen fdm=marker - " The next ccommands should all copy the same part of the buffer, - " regardless of the adressing type, since the part to be copied + " The next commands should all copy the same part of the buffer, + " regardless of the addressing type, since the part to be copied " is folded away :1y call assert_equal(['int FuncName() {/*{{{*/', '1', '2', '3', '4', '5', '}/*}}}*/'], getreg(0,1,1)) @@ -381,3 +381,56 @@ func Test_foldopen_exception() endtry call assert_match('E492:', a) endfunc + +func Test_folddoopen_folddoclosed() + new + call setline(1, range(1, 9)) + set foldmethod=manual + 1,3 fold + 6,8 fold + + " Test without range. + folddoopen s/$/o/ + folddoclosed s/$/c/ + call assert_equal(['1c', '2c', '3c', + \ '4o', '5o', + \ '6c', '7c', '8c', + \ '9o'], getline(1, '$')) + + " Test with range. + call setline(1, range(1, 9)) + 1,8 folddoopen s/$/o/ + 4,$ folddoclosed s/$/c/ + call assert_equal(['1', '2', '3', + \ '4o', '5o', + \ '6c', '7c', '8c', + \ '9'], getline(1, '$')) + + set foldmethod& + bw! +endfunc + +func Test_fold_error() + new + call setline(1, [1, 2]) + + for fm in ['indent', 'expr', 'syntax', 'diff'] + exe 'set foldmethod=' . fm + call assert_fails('norm zf', 'E350:') + call assert_fails('norm zd', 'E351:') + call assert_fails('norm zE', 'E352:') + endfor + + set foldmethod=manual + call assert_fails('norm zd', 'E490:') + call assert_fails('norm zo', 'E490:') + call assert_fails('3fold', 'E16:') + + set foldmethod=marker + set nomodifiable + call assert_fails('1,2fold', 'E21:') + + set modifiable& + set foldmethod& + bw! +endfunc diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 398e9ab331..8a82493ab6 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1,5 +1,101 @@ " Tests for various functions. +" Must be done first, since the alternate buffer must be unset. +func Test_00_bufexists() + call assert_equal(0, bufexists('does_not_exist')) + call assert_equal(1, bufexists(bufnr('%'))) + call assert_equal(0, bufexists(0)) + new Xfoo + let bn = bufnr('%') + call assert_equal(1, bufexists(bn)) + call assert_equal(1, bufexists('Xfoo')) + call assert_equal(1, bufexists(getcwd() . '/Xfoo')) + call assert_equal(1, bufexists(0)) + bw + call assert_equal(0, bufexists(bn)) + call assert_equal(0, bufexists('Xfoo')) +endfunc + +func Test_empty() + call assert_equal(1, empty('')) + call assert_equal(0, empty('a')) + + call assert_equal(1, empty(0)) + call assert_equal(1, empty(-0)) + call assert_equal(0, empty(1)) + call assert_equal(0, empty(-1)) + + call assert_equal(1, empty(0.0)) + call assert_equal(1, empty(-0.0)) + call assert_equal(0, empty(1.0)) + call assert_equal(0, empty(-1.0)) + call assert_equal(0, empty(1.0/0.0)) + call assert_equal(0, empty(0.0/0.0)) + + call assert_equal(1, empty([])) + call assert_equal(0, empty(['a'])) + + call assert_equal(1, empty({})) + call assert_equal(0, empty({'a':1})) + + call assert_equal(1, empty(v:null)) + " call assert_equal(1, empty(v:none)) + call assert_equal(1, empty(v:false)) + call assert_equal(0, empty(v:true)) + + if has('channel') + call assert_equal(1, empty(test_null_channel())) + endif + if has('job') + call assert_equal(1, empty(test_null_job())) + endif + + call assert_equal(0, empty(function('Test_empty'))) +endfunc + +func Test_len() + call assert_equal(1, len(0)) + call assert_equal(2, len(12)) + + call assert_equal(0, len('')) + call assert_equal(2, len('ab')) + + call assert_equal(0, len([])) + call assert_equal(2, len([2, 1])) + + call assert_equal(0, len({})) + call assert_equal(2, len({'a': 1, 'b': 2})) + + " call assert_fails('call len(v:none)', 'E701:') + call assert_fails('call len({-> 0})', 'E701:') +endfunc + +func Test_max() + call assert_equal(0, max([])) + call assert_equal(2, max([2])) + call assert_equal(2, max([1, 2])) + call assert_equal(2, max([1, 2, v:null])) + + call assert_equal(0, max({})) + call assert_equal(2, max({'a':1, 'b':2})) + + call assert_fails('call max(1)', 'E712:') + " call assert_fails('call max(v:none)', 'E712:') +endfunc + +func Test_min() + call assert_equal(0, min([])) + call assert_equal(2, min([2])) + call assert_equal(1, min([1, 2])) + call assert_equal(0, min([1, 2, v:null])) + + call assert_equal(0, min({})) + call assert_equal(1, min({'a':1, 'b':2})) + + call assert_fails('call min(1)', 'E712:') + " call assert_fails('call min(v:none)', 'E712:') +endfunc + func Test_str2nr() call assert_equal(0, str2nr('')) call assert_equal(1, str2nr('1')) @@ -15,6 +111,77 @@ func Test_str2nr() call assert_equal(123456789, str2nr('123456789')) call assert_equal(-123456789, str2nr('-123456789')) + + call assert_equal(5, str2nr('101', 2)) + call assert_equal(5, str2nr('0b101', 2)) + call assert_equal(5, str2nr('0B101', 2)) + call assert_equal(-5, str2nr('-101', 2)) + call assert_equal(-5, str2nr('-0b101', 2)) + call assert_equal(-5, str2nr('-0B101', 2)) + + call assert_equal(65, str2nr('101', 8)) + call assert_equal(65, str2nr('0101', 8)) + call assert_equal(-65, str2nr('-101', 8)) + call assert_equal(-65, str2nr('-0101', 8)) + + call assert_equal(11259375, str2nr('abcdef', 16)) + call assert_equal(11259375, str2nr('ABCDEF', 16)) + call assert_equal(-11259375, str2nr('-ABCDEF', 16)) + call assert_equal(11259375, str2nr('0xabcdef', 16)) + call assert_equal(11259375, str2nr('0Xabcdef', 16)) + call assert_equal(11259375, str2nr('0XABCDEF', 16)) + call assert_equal(-11259375, str2nr('-0xABCDEF', 16)) + + call assert_equal(0, str2nr('0x10')) + call assert_equal(0, str2nr('0b10')) + call assert_equal(1, str2nr('12', 2)) + call assert_equal(1, str2nr('18', 8)) + call assert_equal(1, str2nr('1g', 16)) + + call assert_equal(0, str2nr(v:null)) + " call assert_equal(0, str2nr(v:none)) + + call assert_fails('call str2nr([])', 'E730:') + call assert_fails('call str2nr({->2})', 'E729:') + call assert_fails('call str2nr(1.2)', 'E806:') + call assert_fails('call str2nr(10, [])', 'E474:') +endfunc + +func Test_strftime() + if !exists('*strftime') + return + endif + " Format of strftime() depends on system. We assume + " that basic formats tested here are available and + " identical on all systems which support strftime(). + " + " The 2nd parameter of strftime() is a local time, so the output day + " of strftime() can be 17 or 18, depending on timezone. + call assert_match('^2017-01-1[78]$', strftime('%Y-%m-%d', 1484695512)) + " + call assert_match('^\d\d\d\d-\(0\d\|1[012]\)-\([012]\d\|3[01]\) \([01]\d\|2[0-3]\):[0-5]\d:\([0-5]\d\|60\)$', strftime('%Y-%m-%d %H:%M:%S')) + + call assert_fails('call strftime([])', 'E730:') + call assert_fails('call strftime("%Y", [])', 'E745:') +endfunc + +func Test_simplify() + call assert_equal('', simplify('')) + call assert_equal('/', simplify('/')) + call assert_equal('/', simplify('/.')) + call assert_equal('/', simplify('/..')) + call assert_equal('/...', simplify('/...')) + call assert_equal('./dir/file', simplify('./dir/file')) + call assert_equal('./dir/file', simplify('.///dir//file')) + call assert_equal('./dir/file', simplify('./dir/./file')) + call assert_equal('./file', simplify('./dir/../file')) + call assert_equal('../dir/file', simplify('dir/../../dir/file')) + call assert_equal('./file', simplify('dir/.././file')) + + call assert_fails('call simplify({->0})', 'E729:') + call assert_fails('call simplify([])', 'E730:') + call assert_fails('call simplify({})', 'E731:') + call assert_fails('call simplify(1.2)', 'E806:') endfunc func Test_setbufvar_options() @@ -48,6 +215,19 @@ func Test_setbufvar_options() bwipe! endfunc +func Test_strpart() + call assert_equal('de', strpart('abcdefg', 3, 2)) + call assert_equal('ab', strpart('abcdefg', -2, 4)) + call assert_equal('abcdefg', strpart('abcdefg', -2)) + call assert_equal('fg', strpart('abcdefg', 5, 4)) + call assert_equal('defg', strpart('abcdefg', 3)) + + if has('multi_byte') + call assert_equal('lép', strpart('éléphant', 2, 4)) + call assert_equal('léphant', strpart('éléphant', 2)) + endif +endfunc + func Test_tolower() call assert_equal("", tolower("")) @@ -188,7 +368,7 @@ func Test_toupper() call assert_equal("YÝŶŸẎỲỶỸ", toupper("YÝŶŸẎỲỶỸ")) call assert_equal("ZŹŻŽƵẐẔ", toupper("ZŹŻŽƵẐẔ")) - call assert_equal("ⱥ ⱦ", tolower("Ⱥ Ⱦ")) + call assert_equal("Ⱥ Ⱦ", toupper("ⱥ ⱦ")) endfunc " Tests for the mode() function @@ -353,3 +533,236 @@ func Test_getbufvar() set fileformats& endfunc + +func Test_last_buffer_nr() + call assert_equal(bufnr('$'), last_buffer_nr()) +endfunc + +func Test_stridx() + call assert_equal(-1, stridx('', 'l')) + call assert_equal(0, stridx('', '')) + call assert_equal(0, stridx('hello', '')) + call assert_equal(-1, stridx('hello', 'L')) + call assert_equal(2, stridx('hello', 'l', -1)) + call assert_equal(2, stridx('hello', 'l', 0)) + call assert_equal(2, stridx('hello', 'l', 1)) + call assert_equal(3, stridx('hello', 'l', 3)) + call assert_equal(-1, stridx('hello', 'l', 4)) + call assert_equal(-1, stridx('hello', 'l', 10)) + call assert_equal(2, stridx('hello', 'll')) + call assert_equal(-1, stridx('hello', 'hello world')) +endfunc + +func Test_strridx() + call assert_equal(-1, strridx('', 'l')) + call assert_equal(0, strridx('', '')) + call assert_equal(5, strridx('hello', '')) + call assert_equal(-1, strridx('hello', 'L')) + call assert_equal(3, strridx('hello', 'l')) + call assert_equal(3, strridx('hello', 'l', 10)) + call assert_equal(3, strridx('hello', 'l', 3)) + call assert_equal(2, strridx('hello', 'l', 2)) + call assert_equal(-1, strridx('hello', 'l', 1)) + call assert_equal(-1, strridx('hello', 'l', 0)) + call assert_equal(-1, strridx('hello', 'l', -1)) + call assert_equal(2, strridx('hello', 'll')) + call assert_equal(-1, strridx('hello', 'hello world')) +endfunc + +func Test_matchend() + call assert_equal(7, matchend('testing', 'ing')) + call assert_equal(7, matchend('testing', 'ing', 2)) + call assert_equal(-1, matchend('testing', 'ing', 5)) +endfunc + +func Test_nextnonblank_prevnonblank() + new +insert +This + + +is + +a +Test +. + call assert_equal(0, nextnonblank(-1)) + call assert_equal(0, nextnonblank(0)) + call assert_equal(1, nextnonblank(1)) + call assert_equal(4, nextnonblank(2)) + call assert_equal(4, nextnonblank(3)) + call assert_equal(4, nextnonblank(4)) + call assert_equal(6, nextnonblank(5)) + call assert_equal(6, nextnonblank(6)) + call assert_equal(7, nextnonblank(7)) + call assert_equal(0, nextnonblank(8)) + + call assert_equal(0, prevnonblank(-1)) + call assert_equal(0, prevnonblank(0)) + call assert_equal(1, prevnonblank(1)) + call assert_equal(1, prevnonblank(2)) + call assert_equal(1, prevnonblank(3)) + call assert_equal(4, prevnonblank(4)) + call assert_equal(4, prevnonblank(5)) + call assert_equal(6, prevnonblank(6)) + call assert_equal(7, prevnonblank(7)) + call assert_equal(0, prevnonblank(8)) + bw! +endfunc + +func Test_byte2line_line2byte() + new + call setline(1, ['a', 'bc', 'd']) + + set fileformat=unix + call assert_equal([-1, -1, 1, 1, 2, 2, 2, 3, 3, -1], + \ map(range(-1, 8), 'byte2line(v:val)')) + call assert_equal([-1, -1, 1, 3, 6, 8, -1], + \ map(range(-1, 5), 'line2byte(v:val)')) + + set fileformat=mac + call assert_equal([-1, -1, 1, 1, 2, 2, 2, 3, 3, -1], + \ map(range(-1, 8), 'byte2line(v:val)')) + call assert_equal([-1, -1, 1, 3, 6, 8, -1], + \ map(range(-1, 5), 'line2byte(v:val)')) + + set fileformat=dos + call assert_equal([-1, -1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, -1], + \ map(range(-1, 11), 'byte2line(v:val)')) + call assert_equal([-1, -1, 1, 4, 8, 11, -1], + \ map(range(-1, 5), 'line2byte(v:val)')) + + set fileformat& + bw! +endfunc + +func Test_count() + let l = ['a', 'a', 'A', 'b'] + call assert_equal(2, count(l, 'a')) + call assert_equal(1, count(l, 'A')) + call assert_equal(1, count(l, 'b')) + call assert_equal(0, count(l, 'B')) + + call assert_equal(2, count(l, 'a', 0)) + call assert_equal(1, count(l, 'A', 0)) + call assert_equal(1, count(l, 'b', 0)) + call assert_equal(0, count(l, 'B', 0)) + + call assert_equal(3, count(l, 'a', 1)) + call assert_equal(3, count(l, 'A', 1)) + call assert_equal(1, count(l, 'b', 1)) + call assert_equal(1, count(l, 'B', 1)) + call assert_equal(0, count(l, 'c', 1)) + + call assert_equal(1, count(l, 'a', 0, 1)) + call assert_equal(2, count(l, 'a', 1, 1)) + call assert_fails('call count(l, "a", 0, 10)', 'E684:') + + let d = {1: 'a', 2: 'a', 3: 'A', 4: 'b'} + call assert_equal(2, count(d, 'a')) + call assert_equal(1, count(d, 'A')) + call assert_equal(1, count(d, 'b')) + call assert_equal(0, count(d, 'B')) + + call assert_equal(2, count(d, 'a', 0)) + call assert_equal(1, count(d, 'A', 0)) + call assert_equal(1, count(d, 'b', 0)) + call assert_equal(0, count(d, 'B', 0)) + + call assert_equal(3, count(d, 'a', 1)) + call assert_equal(3, count(d, 'A', 1)) + call assert_equal(1, count(d, 'b', 1)) + call assert_equal(1, count(d, 'B', 1)) + call assert_equal(0, count(d, 'c', 1)) + + call assert_fails('call count(d, "a", 0, 1)', 'E474:') + call assert_fails('call count("a", "a")', 'E712:') +endfunc + +func Test_changenr() + new Xchangenr + call assert_equal(0, changenr()) + norm ifoo + call assert_equal(1, changenr()) + set undolevels=10 + norm Sbar + call assert_equal(2, changenr()) + undo + call assert_equal(1, changenr()) + redo + call assert_equal(2, changenr()) + bw! + set undolevels& +endfunc + +func Test_filewritable() + new Xfilewritable + write! + call assert_equal(1, filewritable('Xfilewritable')) + + call assert_notequal(0, setfperm('Xfilewritable', 'r--r-----')) + call assert_equal(0, filewritable('Xfilewritable')) + + call assert_notequal(0, setfperm('Xfilewritable', 'rw-r-----')) + call assert_equal(1, filewritable('Xfilewritable')) + + call assert_equal(0, filewritable('doesnotexist')) + + call delete('Xfilewritable') + bw! +endfunc + +func Test_hostname() + let hostname_vim = hostname() + if has('unix') + let hostname_system = systemlist('uname -n')[0] + call assert_equal(hostname_vim, hostname_system) + endif +endfunc + +func Test_getpid() + " getpid() always returns the same value within a vim instance. + call assert_equal(getpid(), getpid()) + if has('unix') + call assert_equal(systemlist('echo $PPID')[0], string(getpid())) + endif +endfunc + +func Test_hlexists() + call assert_equal(0, hlexists('does_not_exist')) + " call assert_equal(0, hlexists('Number')) + call assert_equal(0, highlight_exists('does_not_exist')) + " call assert_equal(0, highlight_exists('Number')) + syntax on + call assert_equal(0, hlexists('does_not_exist')) + " call assert_equal(1, hlexists('Number')) + call assert_equal(0, highlight_exists('does_not_exist')) + " call assert_equal(1, highlight_exists('Number')) + syntax off +endfunc + +func Test_col() + new + call setline(1, 'abcdef') + norm gg4|mx6|mY2| + call assert_equal(2, col('.')) + call assert_equal(7, col('$')) + call assert_equal(4, col("'x")) + call assert_equal(6, col("'Y")) + call assert_equal(2, col([1, 2])) + call assert_equal(7, col([1, '$'])) + + call assert_equal(0, col('')) + call assert_equal(0, col('x')) + call assert_equal(0, col([2, '$'])) + call assert_equal(0, col([1, 100])) + call assert_equal(0, col([1])) + bw! +endfunc + +func Test_balloon_show() + if has('balloon_eval') + " This won't do anything but must not crash either. + call balloon_show('hi!') + endif +endfunc diff --git a/src/nvim/testdir/test_listlbr_utf8.vim b/src/nvim/testdir/test_listlbr_utf8.vim index 56a4cc9b31..b648a3361b 100644 --- a/src/nvim/testdir/test_listlbr_utf8.vim +++ b/src/nvim/testdir/test_listlbr_utf8.vim @@ -194,6 +194,21 @@ func Test_multibyte_sign_and_colorcolumn() call s:close_windows() endfunc +func Test_colorcolumn_priority() + call s:test_windows('setl cc=4 cuc hls') + call setline(1, ["xxyy", ""]) + norm! gg + exe "normal! /xxyy\<CR>" + norm! G + redraw! + let line_attr = s:screen_attr(1, [1, &cc]) + " Search wins over CursorColumn + call assert_equal(line_attr[1], line_attr[0]) + " Search wins over Colorcolumn + call assert_equal(line_attr[2], line_attr[3]) + call s:close_windows('setl hls&vim') +endfunc + func Test_illegal_byte_and_breakat() call s:test_windows("setl sbr= brk+=<") vert resize 18 diff --git a/src/nvim/testdir/test_mapping.vim b/src/nvim/testdir/test_mapping.vim index f5e4c4b90c..0a198763bb 100644 --- a/src/nvim/testdir/test_mapping.vim +++ b/src/nvim/testdir/test_mapping.vim @@ -160,3 +160,30 @@ func Test_map_meta_quotes() set nomodified iunmap <M-"> endfunc + +func Test_map_timeout() + nnoremap aaaa :let got_aaaa = 1<CR> + nnoremap bb :let got_bb = 1<CR> + nmap b aaa + new + func ExitInsert(timer) + let g:line = getline(1) + call feedkeys("\<Esc>", "t") + endfunc + set timeout timeoutlen=200 + call timer_start(300, 'ExitInsert') + " After the 'b' Vim waits for another character to see if it matches 'bb'. + " When it times out it is expanded to "aaa", but there is no wait for + " "aaaa". Can't check that reliably though. + call feedkeys("b", "xt!") + call assert_equal("aa", g:line) + call assert_false(exists('got_aaa')) + call assert_false(exists('got_bb')) + + bwipe! + nunmap aaaa + nunmap bb + nunmap b + set timeoutlen& + delfunc ExitInsert +endfunc diff --git a/src/nvim/testdir/test_marks.vim b/src/nvim/testdir/test_marks.vim index d00b1ddc88..18a0c71aab 100644 --- a/src/nvim/testdir/test_marks.vim +++ b/src/nvim/testdir/test_marks.vim @@ -24,3 +24,47 @@ function! Test_Incr_Marks() call assert_equal("XXX 123 123", getline(3)) enew! endfunction + +func Test_setpos() + new one + let onebuf = bufnr('%') + let onewin = win_getid() + call setline(1, ['aaa', 'bbb', 'ccc']) + new two + let twobuf = bufnr('%') + let twowin = win_getid() + call setline(1, ['aaa', 'bbb', 'ccc']) + + " for the cursor the buffer number is ignored + call setpos(".", [0, 2, 1, 0]) + call assert_equal([0, 2, 1, 0], getpos(".")) + call setpos(".", [onebuf, 3, 3, 0]) + call assert_equal([0, 3, 3, 0], getpos(".")) + + call setpos("''", [0, 1, 3, 0]) + call assert_equal([0, 1, 3, 0], getpos("''")) + call setpos("''", [onebuf, 2, 2, 0]) + call assert_equal([0, 2, 2, 0], getpos("''")) + + " buffer-local marks + for mark in ["'a", "'\"", "'[", "']", "'<", "'>"] + call win_gotoid(twowin) + call setpos(mark, [0, 2, 1, 0]) + call assert_equal([0, 2, 1, 0], getpos(mark), "for mark " . mark) + call setpos(mark, [onebuf, 1, 3, 0]) + call win_gotoid(onewin) + call assert_equal([0, 1, 3, 0], getpos(mark), "for mark " . mark) + endfor + + " global marks + call win_gotoid(twowin) + call setpos("'N", [0, 2, 1, 0]) + call assert_equal([twobuf, 2, 1, 0], getpos("'N")) + call setpos("'N", [onebuf, 1, 3, 0]) + call assert_equal([onebuf, 1, 3, 0], getpos("'N")) + + call win_gotoid(onewin) + bwipe! + call win_gotoid(twowin) + bwipe! +endfunc diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index 1d15c7f83d..27ac084ef0 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -2314,28 +2314,36 @@ func! Test_normal53_digraph() bw! endfunc -func! Test_normal54_Ctrl_bsl() - new - call setline(1, 'abcdefghijklmn') - exe "norm! df\<c-\>\<c-n>" - call assert_equal(['abcdefghijklmn'], getline(1,'$')) - exe "norm! df\<c-\>\<c-g>" - call assert_equal(['abcdefghijklmn'], getline(1,'$')) - exe "norm! df\<c-\>m" - call assert_equal(['abcdefghijklmn'], getline(1,'$')) +func Test_normal54_Ctrl_bsl() + new + call setline(1, 'abcdefghijklmn') + exe "norm! df\<c-\>\<c-n>" + call assert_equal(['abcdefghijklmn'], getline(1,'$')) + exe "norm! df\<c-\>\<c-g>" + call assert_equal(['abcdefghijklmn'], getline(1,'$')) + exe "norm! df\<c-\>m" + call assert_equal(['abcdefghijklmn'], getline(1,'$')) if !has("multi_byte") return endif - call setline(2, 'abcdefghijklmnāf') - norm! 2gg0 - exe "norm! df\<Char-0x101>" - call assert_equal(['abcdefghijklmn', 'f'], getline(1,'$')) - norm! 1gg0 - exe "norm! df\<esc>" - call assert_equal(['abcdefghijklmn', 'f'], getline(1,'$')) + call setline(2, 'abcdefghijklmnāf') + norm! 2gg0 + exe "norm! df\<Char-0x101>" + call assert_equal(['abcdefghijklmn', 'f'], getline(1,'$')) + norm! 1gg0 + exe "norm! df\<esc>" + call assert_equal(['abcdefghijklmn', 'f'], getline(1,'$')) - " clean up - bw! + " clean up + bw! +endfunc + +func Test_normal_large_count() + " This may fail with 32bit long, how do we detect that? + new + normal o + normal 6666666666dL + bwipe! endfunc " Test for the gr (virtual replace) command @@ -2379,3 +2387,15 @@ func Test_changelist() %bwipe! let &ul = save_ul endfunc + +func Test_delete_until_paragraph() + if !has('multi_byte') + return + endif + new + normal grádv} + call assert_equal('á', getline(1)) + normal grád} + call assert_equal('', getline(1)) + bwipe! +endfunc diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index eb42e35bd3..5ae8528ee9 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -22,6 +22,13 @@ function! Test_whichwrap() set whichwrap& endfunction +function! Test_isfname() + " This used to cause Vim to access uninitialized memory. + set isfname= + call assert_equal("~X", expand("~X")) + set isfname& +endfunction + function! Test_options() let caught = 'ok' try diff --git a/src/nvim/testdir/test_python2.vim b/src/nvim/testdir/test_python2.vim new file mode 100644 index 0000000000..fb98c1eda7 --- /dev/null +++ b/src/nvim/testdir/test_python2.vim @@ -0,0 +1,24 @@ +" Test for python 2 commands. +" TODO: move tests from test87.in here. + +if !has('python') + finish +endif + +func Test_pydo() + " Check deleting lines does not trigger ml_get error. + py import vim + new + call setline(1, ['one', 'two', 'three']) + pydo vim.command("%d_") + bwipe! + + " Check switching to another buffer does not trigger ml_get error. + new + let wincount = winnr('$') + call setline(1, ['one', 'two', 'three']) + pydo vim.command("new") + call assert_equal(wincount + 1, winnr('$')) + bwipe! + bwipe! +endfunc diff --git a/src/nvim/testdir/test_python3.vim b/src/nvim/testdir/test_python3.vim new file mode 100644 index 0000000000..bb241dacb1 --- /dev/null +++ b/src/nvim/testdir/test_python3.vim @@ -0,0 +1,24 @@ +" Test for python 2 commands. +" TODO: move tests from test88.in here. + +if !has('python3') + finish +endif + +func Test_py3do() + " Check deleting lines does not trigger an ml_get error. + py3 import vim + new + call setline(1, ['one', 'two', 'three']) + py3do vim.command("%d_") + bwipe! + + " Check switching to another buffer does not trigger an ml_get error. + new + let wincount = winnr('$') + call setline(1, ['one', 'two', 'three']) + py3do vim.command("new") + call assert_equal(wincount + 1, winnr('$')) + bwipe! + bwipe! +endfunc diff --git a/src/nvim/testdir/test_quotestar.vim b/src/nvim/testdir/test_quotestar.vim index d0dd04f91d..37e3a10ed1 100644 --- a/src/nvim/testdir/test_quotestar.vim +++ b/src/nvim/testdir/test_quotestar.vim @@ -39,6 +39,15 @@ func Do_test_quotestar_for_x11() if cmd == '' return 'GetVimCommand() failed' endif + try + call remote_send('xxx', '') + catch + if v:exception =~ 'E240:' + " No connection to the X server, give up. + return + endif + " ignore other errors + endtry let name = 'XVIMCLIPBOARD' let cmd .= ' --servername ' . name @@ -109,8 +118,12 @@ func Test_quotestar() if has('macunix') let skipped = Do_test_quotestar_for_macunix() - elseif !empty("$DISPLAY") - let skipped = Do_test_quotestar_for_x11() + elseif has('x11') + if empty($DISPLAY) + let skipped = "Test can only run when $DISPLAY is set." + else + let skipped = Do_test_quotestar_for_x11() + endif else let skipped = "Test is not implemented yet for this platform." endif diff --git a/src/nvim/testdir/test_regexp_utf8.vim b/src/nvim/testdir/test_regexp_utf8.vim index a2f4286d4f..97638e9aac 100644 --- a/src/nvim/testdir/test_regexp_utf8.vim +++ b/src/nvim/testdir/test_regexp_utf8.vim @@ -35,12 +35,21 @@ func s:classes_test() set isprint=@,161-255 call assert_equal('Motörhead', matchstr('Motörhead', '[[:print:]]\+')) + let alnumchars = '' let alphachars = '' + let backspacechar = '' + let blankchars = '' + let cntrlchars = '' + let digitchars = '' + let escapechar = '' + let graphchars = '' let lowerchars = '' - let upperchars = '' - let alnumchars = '' let printchars = '' let punctchars = '' + let returnchar = '' + let spacechars = '' + let tabchar = '' + let upperchars = '' let xdigitchars = '' let i = 1 while i <= 255 @@ -48,21 +57,48 @@ func s:classes_test() if c =~ '[[:alpha:]]' let alphachars .= c endif - if c =~ '[[:lower:]]' - let lowerchars .= c - endif - if c =~ '[[:upper:]]' - let upperchars .= c - endif if c =~ '[[:alnum:]]' let alnumchars .= c endif + if c =~ '[[:backspace:]]' + let backspacechar .= c + endif + if c =~ '[[:blank:]]' + let blankchars .= c + endif + if c =~ '[[:cntrl:]]' + let cntrlchars .= c + endif + if c =~ '[[:digit:]]' + let digitchars .= c + endif + if c =~ '[[:escape:]]' + let escapechar .= c + endif + if c =~ '[[:graph:]]' + let graphchars .= c + endif + if c =~ '[[:lower:]]' + let lowerchars .= c + endif if c =~ '[[:print:]]' let printchars .= c endif if c =~ '[[:punct:]]' let punctchars .= c endif + if c =~ '[[:return:]]' + let returnchar .= c + endif + if c =~ '[[:space:]]' + let spacechars .= c + endif + if c =~ '[[:tab:]]' + let tabchar .= c + endif + if c =~ '[[:upper:]]' + let upperchars .= c + endif if c =~ '[[:xdigit:]]' let xdigitchars .= c endif @@ -70,11 +106,20 @@ func s:classes_test() endwhile call assert_equal('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', alphachars) - call assert_equal('abcdefghijklmnopqrstuvwxyzµßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ', lowerchars) - call assert_equal('ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ', upperchars) call assert_equal('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', alnumchars) + call assert_equal("\b", backspacechar) + call assert_equal("\t ", blankchars) + call assert_equal("\x01\x02\x03\x04\x05\x06\x07\b\t\n\x0b\f\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\e\x1c\x1d\x1e\x1f\x7f", cntrlchars) + call assert_equal("0123456789", digitchars) + call assert_equal("\<Esc>", escapechar) + call assert_equal('!"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~', graphchars) + call assert_equal('abcdefghijklmnopqrstuvwxyzµßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ', lowerchars) call assert_equal(' !"#$%&''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ¡¢£¤¥¦§¨©ª«¬®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ', printchars) call assert_equal('!"#$%&''()*+,-./:;<=>?@[\]^_`{|}~', punctchars) + call assert_equal('ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ', upperchars) + call assert_equal("\r", returnchar) + call assert_equal("\t\n\x0b\f\r ", spacechars) + call assert_equal("\t", tabchar) call assert_equal('0123456789ABCDEFabcdef', xdigitchars) endfunc @@ -121,3 +166,20 @@ func Test_eow_with_optional() call assert_equal(expected, actual) endfor endfunc + +func Test_reversed_range() + for re in range(0, 2) + exe 'set re=' . re + call assert_fails('call match("abc def", "[c-a]")', 'E944:') + endfor + set re=0 +endfunc + +func Test_large_class() + set re=1 + call assert_fails('call match("abc def", "[\u3000-\u4000]")', 'E945:') + set re=2 + call assert_equal(0, 'abc def' =~# '[\u3000-\u4000]') + call assert_equal(1, "\u3042" =~# '[\u3000-\u4000]') + set re=0 +endfunc diff --git a/src/nvim/testdir/test_scroll_opt.vim b/src/nvim/testdir/test_scroll_opt.vim new file mode 100644 index 0000000000..77920eb8b0 --- /dev/null +++ b/src/nvim/testdir/test_scroll_opt.vim @@ -0,0 +1,36 @@ +" Test for reset 'scroll' +" + +func Test_reset_scroll() + let scr = &l:scroll + + setlocal scroll=1 + setlocal scroll& + call assert_equal(scr, &l:scroll) + + setlocal scroll=1 + setlocal scroll=0 + call assert_equal(scr, &l:scroll) + + try + execute 'setlocal scroll=' . (winheight(0) + 1) + " not reached + call assert_false(1) + catch + call assert_exception('E49:') + endtry + + split + + let scr = &l:scroll + + setlocal scroll=1 + setlocal scroll& + call assert_equal(scr, &l:scroll) + + setlocal scroll=1 + setlocal scroll=0 + call assert_equal(scr, &l:scroll) + + quit! +endfunc diff --git a/src/nvim/testdir/test_smartindent.vim b/src/nvim/testdir/test_smartindent.vim index d00eac9798..9e93a55eb0 100644 --- a/src/nvim/testdir/test_smartindent.vim +++ b/src/nvim/testdir/test_smartindent.vim @@ -1,3 +1,4 @@ +" Tests for smartindent " Tests for not doing smart indenting when it isn't set. function! Test_nosmartindent() @@ -12,3 +13,29 @@ function! Test_nosmartindent() call assert_equal(" #test", getline(1)) enew! | close endfunction + +function MyIndent() +endfunction + +" When 'indentexpr' is set, setting 'si' has no effect. +function Test_smartindent_has_no_effect() + new + exe "normal! i\<Tab>one\<Esc>" + set noautoindent + set smartindent + set indentexpr= + exe "normal! Gotwo\<Esc>" + call assert_equal("\ttwo", getline("$")) + + set indentexpr=MyIndent + exe "normal! Gothree\<Esc>" + call assert_equal("three", getline("$")) + + delfunction! MyIndent + set autoindent& + set smartindent& + set indentexpr& + bwipe! +endfunction + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index 11e26d03aa..495c561991 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -240,3 +240,14 @@ func Test_progpath() " Only expect "vim" to appear in v:progname. call assert_match('vim\c', v:progname) endfunc + +func Test_silent_ex_mode() + if !has('unix') || has('gui_running') + " can't get output of Vim. + return + endif + + " This caused an ml_get error. + let out = system(GetVimCommand() . '-u NONE -es -c''set verbose=1|h|exe "%norm\<c-y>\<c-d>"'' -c cq') + call assert_notmatch('E315:', out) +endfunc diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 8465fe7d45..ebdfc250aa 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -301,11 +301,19 @@ func Test_syntax_arg_skipped() syn clear endfunc - -func Test_invalid_arg() + +func Test_syntax_invalid_arg() call assert_fails('syntax case asdf', 'E390:') - call assert_fails('syntax conceal asdf', 'E390:') + if has('conceal') + call assert_fails('syntax conceal asdf', 'E390:') + endif call assert_fails('syntax spell asdf', 'E390:') + call assert_fails('syntax clear @ABCD', 'E391:') + call assert_fails('syntax include @Xxx', 'E397:') + call assert_fails('syntax region X start="{"', 'E399:') + call assert_fails('syntax sync x', 'E404:') + call assert_fails('syntax keyword Abc a[', 'E789:') + call assert_fails('syntax keyword Abc a[bc]d', 'E890:') endfunc func Test_syn_sync() @@ -346,6 +354,50 @@ func Test_invalid_name() hi clear @Wrong endfunc +func Test_ownsyntax() + new Xfoo + call setline(1, '#define FOO') + syntax on + set filetype=c + ownsyntax perl + call assert_equal('perlComment', synIDattr(synID(line('.'), col('.'), 1), 'name')) + call assert_equal('c', b:current_syntax) + call assert_equal('perl', w:current_syntax) + + " A new split window should have the original syntax. + split + call assert_equal('cDefine', synIDattr(synID(line('.'), col('.'), 1), 'name')) + call assert_equal('c', b:current_syntax) + call assert_equal(0, exists('w:current_syntax')) + + wincmd x + call assert_equal('perlComment', synIDattr(synID(line("."), col("."), 1), "name")) + + syntax off + set filetype& + %bw! +endfunc + +func Test_ownsyntax_completion() + call feedkeys(":ownsyntax java\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"ownsyntax java javacc javascript', @:) +endfunc + +func Test_highlight_invalid_arg() + if has('gui_running') + call assert_fails('hi XXX guifg=xxx', 'E254:') + endif + call assert_fails('hi DoesNotExist', 'E411:') + call assert_fails('hi link', 'E412:') + call assert_fails('hi link a', 'E412:') + call assert_fails('hi link a b c', 'E413:') + call assert_fails('hi XXX =', 'E415:') + call assert_fails('hi XXX cterm', 'E416:') + call assert_fails('hi XXX cterm=', 'E417:') + call assert_fails('hi XXX cterm=DoesNotExist', 'E418:') + call assert_fails('hi XXX ctermfg=DoesNotExist', 'E421:') + call assert_fails('hi XXX xxx=White', 'E423:') +endfunc func Test_conceal() if !has('conceal') @@ -382,3 +434,4 @@ func Test_conceal() set conceallevel& bw! endfunc + diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 65957626cb..25c39587f4 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -405,6 +405,10 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) { got_winch = true; UI *ui = data; + if (tui_is_stopped(ui)) { + return; + } + update_size(ui); ui_schedule_refresh(); } @@ -845,7 +849,7 @@ CursorShape tui_cursor_decode_shape(const char *shape_str) static cursorentry_T decode_cursor_entry(Dictionary args) { - cursorentry_T r; + cursorentry_T r = shape_table[0]; for (size_t i = 0; i < args.size; i++) { char *key = args.items[i].key.data; diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 42366fdb76..21dd6ec0dd 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -535,14 +535,18 @@ Array ui_array(void) { Array all_uis = ARRAY_DICT_INIT; for (size_t i = 0; i < ui_count; i++) { - Dictionary dic = ARRAY_DICT_INIT; - PUT(dic, "width", INTEGER_OBJ(uis[i]->width)); - PUT(dic, "height", INTEGER_OBJ(uis[i]->height)); - PUT(dic, "rgb", BOOLEAN_OBJ(uis[i]->rgb)); + UI *ui = uis[i]; + Dictionary info = ARRAY_DICT_INIT; + PUT(info, "width", INTEGER_OBJ(ui->width)); + PUT(info, "height", INTEGER_OBJ(ui->height)); + PUT(info, "rgb", BOOLEAN_OBJ(ui->rgb)); for (UIExtension j = 0; j < kUIExtCount; j++) { - PUT(dic, ui_ext_names[j], BOOLEAN_OBJ(uis[i]->ui_ext[j])); + PUT(info, ui_ext_names[j], BOOLEAN_OBJ(ui->ui_ext[j])); + } + if (ui->inspect) { + ui->inspect(ui, &info); } - ADD(all_uis, DICTIONARY_OBJ(dic)); + ADD(all_uis, DICTIONARY_OBJ(info)); } return all_uis; } diff --git a/src/nvim/ui.h b/src/nvim/ui.h index 48896a6a3f..6b04e9c67a 100644 --- a/src/nvim/ui.h +++ b/src/nvim/ui.h @@ -36,6 +36,7 @@ struct ui_t { #endif void (*event)(UI *ui, char *name, Array args, bool *args_consumed); void (*stop)(UI *ui); + void (*inspect)(UI *ui, Dictionary *info); }; #ifdef INCLUDE_GENERATED_DECLARATIONS diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index a8bbeea035..56db124a46 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -163,3 +163,28 @@ static void ui_bridge_suspend_event(void **argv) UI *ui = UI(argv[0]); ui->suspend(ui); } + +static void ui_bridge_option_set(UI *ui, String name, Object value) +{ + String copy_name = copy_string(name); + Object *copy_value = xmalloc(sizeof(Object)); + *copy_value = copy_object(value); + UI_BRIDGE_CALL(ui, option_set, 4, ui, copy_name.data, + INT2PTR(copy_name.size), copy_value); + // TODO(bfredl): when/if TUI/bridge teardown is refactored to use events, the + // commit that introduced this special case can be reverted. + // For now this is needed for nvim_list_uis(). + if (strequal(name.data, "termguicolors")) { + ui->rgb = value.data.boolean; + } +} +static void ui_bridge_option_set_event(void **argv) +{ + UI *ui = UI(argv[0]); + String name = (String){ .data = argv[1], .size = (size_t)argv[2] }; + Object value = *(Object *)argv[3]; + ui->option_set(ui, name, value); + api_free_string(name); + api_free_object(value); + xfree(argv[3]); +} diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 44bcefb332..8b290edd1f 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1698,9 +1698,9 @@ bool u_undo_and_forget(int count) count = 1; } undo_undoes = true; - // don't send a nvim_buf_lines_event for this undo is part of 'inccommand' - // playing with buffer contents - u_doit(count, true, false); + u_doit(count, true, + // Don't send nvim_buf_lines_event for u_undo_and_forget(). + false); if (curbuf->b_u_curhead == NULL) { // nothing was undone. diff --git a/src/nvim/version.c b/src/nvim/version.c index 17f89a25d2..be160e678e 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -145,7 +145,7 @@ static const int included_patches[] = { // 1497, // 1496, // 1495, - // 1494, + 1494, 1493, // 1492, // 1491, @@ -167,7 +167,7 @@ static const int included_patches[] = { 1475, // 1474, // 1473, - // 1472, + 1472, // 1471, // 1470, // 1469, @@ -328,7 +328,7 @@ static const int included_patches[] = { // 1314, // 1313, // 1312, - // 1311, + 1311, // 1310, // 1309, // 1308, @@ -405,9 +405,9 @@ static const int included_patches[] = { // 1237, // 1236, // 1235, - // 1234, + 1234, // 1233, - // 1232, + 1232, // 1231, 1230, 1229, @@ -442,13 +442,13 @@ static const int included_patches[] = { 1200, // 1199, // 1198, - // 1197, + 1197, // 1196, // 1195, // 1194, // 1193, // 1192, - // 1191, + 1191, // 1190, 1189, 1188, @@ -458,8 +458,8 @@ static const int included_patches[] = { // 1184, // 1183, // 1182, - // 1181, - // 1180, + 1181, + 1180, // 1179, // 1178, // 1177, @@ -489,7 +489,7 @@ static const int included_patches[] = { // 1153, // 1152, // 1151, - // 1150, + 1150, // 1149, // 1148, // 1147, @@ -498,7 +498,7 @@ static const int included_patches[] = { // 1144, // 1143, // 1142, - // 1141, + 1141, // 1140, // 1139, // 1138, @@ -518,7 +518,7 @@ static const int included_patches[] = { // 1124, // 1123, // 1122, - // 1121, + 1121, // 1120, // 1119, // 1118, @@ -714,7 +714,7 @@ static const int included_patches[] = { // 928, // 927, // 926, - // 925, + 925, // 924, // 923, // 922, @@ -758,7 +758,7 @@ static const int included_patches[] = { // 884, // 883, // 882, - // 881, + 881, // 880, // 879, // 878, @@ -1077,11 +1077,11 @@ static const int included_patches[] = { 565, 564, // 563, - // 562, + 562, 561, // 560, - // 559, - // 558, + 559, + 558, // 557, // 556, 555, @@ -1120,30 +1120,30 @@ static const int included_patches[] = { // 522, // 521, // 520, - // 519, + 519, 518, 517, - // 516, - // 515, + 516, + 515, // 514, 513, // 512, - // 511, + 511, // 510, // 509, // 508, - // 507, + 507, // 506, - // 505, + 505, // 504, - // 503, + 503, 502, // 501, 500, 499, 498, - // 497, - // 496, + 497, + 496, 495, // 494, // 493, @@ -1175,36 +1175,36 @@ static const int included_patches[] = { // 467, // 466, 465, - // 464, - // 463, + 464, + 463, // 462, 461, - // 460, + 460, 459, 458, 457, - // 456, + 456, // 455, - // 454, + 454, 453, // 452, // 451, - // 450, + 450, // 449, 448, - // 447, + 447, 446, // 445, 444, 443, 442, - // 441, + 441, 440, 439, - // 438, + 438, 437, // 436, - // 435, + 435, 434, 433, // 432, @@ -1220,23 +1220,23 @@ static const int included_patches[] = { // 422, 421, 420, - // 419, + 419, // 418, 417, - // 416, - // 415, + 416, + 415, // 414, // 413, // 412, // 411, // 410, - // 409, + 409, 408, 407, 406, 405, 404, - // 403, + 403, 402, // 401, 400, @@ -1255,20 +1255,20 @@ static const int included_patches[] = { 387, // 386, 385, - // 384, - // 383, - // 382, + 384, + 383, + 382, 381, 380, 379, 378, 377, 376, - // 375, + 375, 374, // 373, // 372, - // 371, + 371, // 370, // 369, // 368, @@ -1277,27 +1277,27 @@ static const int included_patches[] = { // 365, 364, // 363, - // 362, + 362, // 361, 360, 359, 358, 357, - // 356, + 356, // 355, - // 354, + 354, 353, 352, 351, // 350, // 349, - // 348, + 348, 347, // 346, 345, 344, 343, - // 342, + 342, 341, // 340, 339, @@ -1307,10 +1307,10 @@ static const int included_patches[] = { 335, 334, 333, - // 332, + 332, 331, 330, - // 329, + 329, 328, 327, 326, @@ -1318,16 +1318,16 @@ static const int included_patches[] = { 324, 323, 322, - // 321, + 321, 320, 319, - // 318, - // 317, + 318, + 317, 316, 315, 314, - // 313, - // 312, + 313, + 312, 311, 310, 309, @@ -1335,7 +1335,7 @@ static const int included_patches[] = { 307, 306, 305, - // 304, + 304, // 303, 302, 301, @@ -1378,7 +1378,7 @@ static const int included_patches[] = { // 264, 263, 262, - // 261, + 261, 260, 259, 258, |