diff options
37 files changed, 1345 insertions, 675 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d8c1132906..93bc7f868d 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1313,8 +1313,10 @@ One local buffer variable is predefined: *b:changedtick* *changetick* b:changedtick The total number of changes to the current buffer. It is incremented for each change. An undo command is also a change - in this case. This can be used to perform an action only when - the buffer has changed. Example: > + in this case. Resetting 'modified' when writing the buffer is + also counted. + This can be used to perform an action only when the buffer has + changed. Example: > :if my_changedtick != b:changedtick : let my_changedtick = b:changedtick : call My_Update() @@ -2297,6 +2299,7 @@ rpcrequest({channel}, {method}[, {args}...]) screenattr({row}, {col}) Number attribute at screen position screenchar({row}, {col}) Number character at screen position screencol() Number current cursor column +screenpos({winid}, {lnum}, {col}) Dict screen row and col of a text character screenrow() Number current cursor row search({pattern} [, {flags} [, {stopline} [, {timeout}]]]) Number search for {pattern} @@ -6798,7 +6801,25 @@ screencol() *screencol()* the following mappings: > nnoremap <expr> GG ":echom ".screencol()."\n" nnoremap <silent> GG :echom screencol()<CR> + noremap GG <Cmd>echom screencol()<Cr> < +screenpos({winid}, {lnum}, {col}) *screenpos()* + The result is a Dict with the screen position of the text + character in window {winid} at buffer line {lnum} and column + {col}. {col} is a one-based byte index. + The Dict has these members: + row screen row + col first screen column + endcol last screen column + curscol cursor screen column + If the specified position is not visible, all values are zero. + The "endcol" value differs from "col" when the character + occupies more than one screen cell. E.g. for a Tab "col" can + be 1 and "endcol" can be 8. + The "curscol" value is where the cursor would be placed. For + a Tab it would be the same as "endcol", while for a double + width character it would be the same as "col". + screenrow() *screenrow()* The result is a Number, which is the current screen row of the cursor. The top line has number one. diff --git a/runtime/plugin/netrwPlugin.vim b/runtime/plugin/netrwPlugin.vim index e39bde88a7..2d67f6a4e4 100644 --- a/runtime/plugin/netrwPlugin.vim +++ b/runtime/plugin/netrwPlugin.vim @@ -47,9 +47,9 @@ augroup Network au FileReadCmd ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau FileReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(1,expand("<amatch>"))|exe "sil doau FileReadPost ".fnameescape(expand("<amatch>")) au BufWriteCmd ftp://*,rcp://*,scp://*,http://*,file://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau BufWritePre ".fnameescape(expand("<amatch>"))|exe 'Nwrite '.fnameescape(expand("<amatch>"))|exe "sil doau BufWritePost ".fnameescape(expand("<amatch>")) au FileWriteCmd ftp://*,rcp://*,scp://*,http://*,file://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau FileWritePre ".fnameescape(expand("<amatch>"))|exe "'[,']".'Nwrite '.fnameescape(expand("<amatch>"))|exe "sil doau FileWritePost ".fnameescape(expand("<amatch>")) - try + try au SourceCmd ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>")) - catch /^Vim\%((\a\+)\)\=:E216/ + catch /^Vim\%((\a\+)\)\=:E216/ au SourcePre ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>")) endtry augroup END @@ -81,7 +81,7 @@ if !exists("g:netrw_nogx") if !hasmapto('<Plug>NetrwBrowseX') nmap <unique> gx <Plug>NetrwBrowseX endif - nno <silent> <Plug>NetrwBrowseX :call netrw#BrowseX(netrw#GX(),netrw#CheckIfRemote(netrw#GX()))<cr> + nno <silent> <Plug>NetrwBrowseX :call netrw#BrowseX(expand((exists("g:netrw_gx")? g:netrw_gx : '<cfile>')),netrw#CheckIfRemote())<cr> endif if maparg('gx','v') == "" if !hasmapto('<Plug>NetrwBrowseXVis') @@ -103,7 +103,7 @@ fun! s:LocalBrowse(dirname) " Unfortunate interaction -- only DechoMsg debugging calls can be safely used here. " Otherwise, the BufEnter event gets triggered when attempts to write to " the DBG buffer are made. - + if !exists("s:vimentered") " If s:vimentered doesn't exist, then the VimEnter event hasn't fired. It will, " and so s:VimEnter() will then be calling this routine, but this time with s:vimentered defined. diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 2034fea770..27344fc093 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1059,6 +1059,12 @@ fail: /// - "SE" south-east /// - `height`: window height (in character cells). Minimum of 1. /// - `width`: window width (in character cells). Minimum of 1. +/// - 'bufpos': position float relative text inside the window `win` (only +/// when relative="win"). Takes a tuple of [line, column] where +/// both are zero-index. Note: `row` and `col` if present, still +/// applies relative this positio. By default `row=1` and `col=0` +/// is used (with default NW anchor), to make the float +/// behave like a tooltip under the buffer text. /// - `row`: row position. Screen cell height are used as unit. Can be /// floating point. /// - `col`: column position. Screen cell width is used as unit. Can be diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index e279edebde..02670c0513 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -503,25 +503,33 @@ Dictionary nvim_win_get_config(Window window, Error *err) return rv; } - PUT(rv, "focusable", BOOLEAN_OBJ(wp->w_float_config.focusable)); - PUT(rv, "external", BOOLEAN_OBJ(wp->w_float_config.external)); + FloatConfig *config = &wp->w_float_config; + + PUT(rv, "focusable", BOOLEAN_OBJ(config->focusable)); + PUT(rv, "external", BOOLEAN_OBJ(config->external)); if (wp->w_floating) { - PUT(rv, "width", INTEGER_OBJ(wp->w_float_config.width)); - PUT(rv, "height", INTEGER_OBJ(wp->w_float_config.height)); - if (!wp->w_float_config.external) { - if (wp->w_float_config.relative == kFloatRelativeWindow) { - PUT(rv, "win", INTEGER_OBJ(wp->w_float_config.window)); + PUT(rv, "width", INTEGER_OBJ(config->width)); + PUT(rv, "height", INTEGER_OBJ(config->height)); + if (!config->external) { + if (config->relative == kFloatRelativeWindow) { + PUT(rv, "win", INTEGER_OBJ(config->window)); + if (config->bufpos.lnum >= 0) { + Array pos = ARRAY_DICT_INIT; + ADD(pos, INTEGER_OBJ(config->bufpos.lnum)); + ADD(pos, INTEGER_OBJ(config->bufpos.col)); + PUT(rv, "bufpos", ARRAY_OBJ(pos)); + } } PUT(rv, "anchor", STRING_OBJ(cstr_to_string( - float_anchor_str[wp->w_float_config.anchor]))); - PUT(rv, "row", FLOAT_OBJ(wp->w_float_config.row)); - PUT(rv, "col", FLOAT_OBJ(wp->w_float_config.col)); + float_anchor_str[config->anchor]))); + PUT(rv, "row", FLOAT_OBJ(config->row)); + PUT(rv, "col", FLOAT_OBJ(config->col)); } } - const char *rel = (wp->w_floating && !wp->w_float_config.external - ? float_relative_str[wp->w_float_config.relative] : ""); + const char *rel = (wp->w_floating && !config->external + ? float_relative_str[config->relative] : ""); PUT(rv, "relative", STRING_OBJ(cstr_to_string(rel))); return rv; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index f8e07a471f..8fd4360aed 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -144,7 +144,7 @@ read_buffer( if (!readonlymode && !BUFEMPTY()) { changed(); } else if (retval != FAIL) { - unchanged(curbuf, false); + unchanged(curbuf, false, true); } apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false, @@ -299,7 +299,7 @@ int open_buffer( || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) { changed(); } else if (retval != FAIL && !read_stdin && !read_fifo) { - unchanged(curbuf, false); + unchanged(curbuf, false, true); } save_file_ff(curbuf); // keep this fileformat @@ -641,13 +641,11 @@ void close_buffer(win_T *win, buf_T *buf, int action, int abort_if_last) } } -/* - * Make buffer not contain a file. - */ +/// Make buffer not contain a file. void buf_clear_file(buf_T *buf) { buf->b_ml.ml_line_count = 1; - unchanged(buf, true); + unchanged(buf, true, true); buf->b_p_eol = true; buf->b_start_eol = true; buf->b_p_bomb = false; diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 117e3f42fe..16c7804be0 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -1006,7 +1006,7 @@ typedef enum { kFloatRelativeCursor = 2, } FloatRelative; -EXTERN const char *const float_relative_str[] INIT(= { "editor", "window", +EXTERN const char *const float_relative_str[] INIT(= { "editor", "win", "cursor" }); typedef enum { @@ -1016,6 +1016,7 @@ typedef enum { typedef struct { Window window; + lpos_T bufpos; int height, width; double row, col; FloatAnchor anchor; @@ -1026,6 +1027,7 @@ typedef struct { } FloatConfig; #define FLOAT_CONFIG_INIT ((FloatConfig){ .height = 0, .width = 0, \ + .bufpos = { -1, 0 }, \ .row = 0, .col = 0, .anchor = 0, \ .relative = 0, .external = false, \ .focusable = true, \ diff --git a/src/nvim/change.c b/src/nvim/change.c index f8a4cf4282..3401f8a0a8 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -478,9 +478,11 @@ changed_lines( } } -/// Called when the changed flag must be reset for buffer "buf". -/// When "ff" is true also reset 'fileformat'. -void unchanged(buf_T *buf, int ff) +/// Called when the changed flag must be reset for buffer `buf`. +/// When `ff` is true also reset 'fileformat'. +/// When `always_inc_changedtick` is true b:changedtick is incremented even +/// when the changed flag was off. +void unchanged(buf_T *buf, int ff, bool always_inc_changedtick) { if (buf->b_changed || (ff && file_ff_differs(buf, false))) { buf->b_changed = false; @@ -491,8 +493,10 @@ void unchanged(buf_T *buf, int ff) check_status(buf); redraw_tabline = true; need_maketitle = true; // set window title later + buf_inc_changedtick(buf); + } else if (always_inc_changedtick) { + buf_inc_changedtick(buf); } - buf_inc_changedtick(buf); } /// Insert string "p" at the cursor position. Stops at a NUL byte. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 98448ff1f9..8e848483a7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -14781,6 +14781,32 @@ static void f_screencol(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = ui_current_col() + 1; } +/// "screenpos({winid}, {lnum}, {col})" function +static void f_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + pos_T pos; + int row = 0; + int scol = 0, ccol = 0, ecol = 0; + + tv_dict_alloc_ret(rettv); + dict_T *dict = rettv->vval.v_dict; + + win_T *wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { + return; + } + + pos.lnum = tv_get_number(&argvars[1]); + pos.col = tv_get_number(&argvars[2]) - 1; + pos.coladd = 0; + textpos2screenpos(wp, &pos, &row, &scol, &ccol, &ecol, false); + + tv_dict_add_nr(dict, S_LEN("row"), row); + tv_dict_add_nr(dict, S_LEN("col"), scol); + tv_dict_add_nr(dict, S_LEN("curscol"), ccol); + tv_dict_add_nr(dict, S_LEN("endcol"), ecol); +} + /* * "screenrow()" function */ @@ -20895,7 +20921,7 @@ void ex_echo(exarg_T *eap) char *tofree = encode_tv2echo(&rettv, NULL); if (*tofree != NUL) { msg_ext_set_kind("echo"); - msg_multiline_attr(tofree, echo_attr); + msg_multiline_attr(tofree, echo_attr, true); } xfree(tofree); } diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 0ad9ef5dac..ab5ff57c2f 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -266,6 +266,7 @@ return { screenattr={args=2}, screenchar={args=2}, screencol={}, + screenpos={args=3}, screenrow={}, search={args={1, 4}}, searchdecl={args={1, 3}}, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 964b884460..affdda0386 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1323,7 +1323,7 @@ void dialog_changed(buf_T *buf, bool checkall) (void)buf_write_all(buf, false); } } else if (ret == VIM_NO) { - unchanged(buf, true); + unchanged(buf, true, false); } else if (ret == VIM_ALL) { // Write all modified files that can be written. // Skip readonly buffers, these need to be confirmed @@ -1348,7 +1348,7 @@ void dialog_changed(buf_T *buf, bool checkall) } else if (ret == VIM_DISCARDALL) { // mark all buffers as unchanged FOR_ALL_BUFFERS(buf2) { - unchanged(buf2, true); + unchanged(buf2, true, false); } } } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 34717b631e..b97c886094 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1527,7 +1527,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, // // where 'addr' is: // - // % (entire file) + // % (entire file) // $ [+-NUM] // 'x [+-NUM] (where x denotes a currently defined mark) // . [+-NUM] diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 517325a2cd..4cf42b41e9 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -3485,10 +3485,10 @@ restore_backup: if (reset_changed && whole && !append && !write_info.bw_conv_error && (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL)) { - unchanged(buf, true); + unchanged(buf, true, false); const varnumber_T changedtick = buf_get_changedtick(buf); if (buf->b_last_changedtick + 1 == changedtick) { - // changedtick is always incremented in unchanged() but that + // b:changedtick may be incremented in unchanged() but that // should not trigger a TextChanged event. buf->b_last_changedtick = changedtick; } @@ -5107,14 +5107,14 @@ void buf_reload(buf_T *buf, int orig_mode) } (void)move_lines(savebuf, buf); } - } else if (buf == curbuf) { /* "buf" still valid */ - /* Mark the buffer as unmodified and free undo info. */ - unchanged(buf, TRUE); + } else if (buf == curbuf) { // "buf" still valid. + // Mark the buffer as unmodified and free undo info. + unchanged(buf, true, true); if ((flags & READ_KEEP_UNDO) == 0) { u_blockfree(buf); u_clearall(buf); } else { - /* Mark all undo states as changed. */ + // Mark all undo states as changed. u_unchanged(curbuf); } } diff --git a/src/nvim/memline.c b/src/nvim/memline.c index b69812a389..15dd2767a2 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1003,7 +1003,7 @@ void ml_recover(void) set_option_value("fenc", 0L, (char *)b0_fenc, OPT_LOCAL); xfree(b0_fenc); } - unchanged(curbuf, TRUE); + unchanged(curbuf, true, true); bnum = 1; /* start with block 1 */ page_count = 1; /* which is 1 page */ diff --git a/src/nvim/message.c b/src/nvim/message.c index ac731210d7..b5a5f656a0 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -222,12 +222,12 @@ int msg_attr(const char *s, const int attr) } /// similar to msg_outtrans_attr, but support newlines and tabs. -void msg_multiline_attr(const char *s, int attr) +void msg_multiline_attr(const char *s, int attr, bool check_int) FUNC_ATTR_NONNULL_ALL { const char *next_spec = s; - while (next_spec != NULL) { + while (next_spec != NULL && (!check_int || !got_int)) { next_spec = strpbrk(s, "\t\n\r"); if (next_spec != NULL) { @@ -306,7 +306,7 @@ bool msg_attr_keep(char_u *s, int attr, bool keep, bool multiline) s = buf; if (multiline) { - msg_multiline_attr((char *)s, attr); + msg_multiline_attr((char *)s, attr, false); } else { msg_outtrans_attr(s, attr); } diff --git a/src/nvim/move.c b/src/nvim/move.c index 4a87f82eb7..e6fee9999f 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -95,6 +95,8 @@ static void comp_botline(win_T *wp) wp->w_valid |= VALID_BOTLINE|VALID_BOTLINE_AP; set_empty_rows(wp, done); + + win_check_anchored_floats(wp); } void reset_cursorline(void) @@ -310,6 +312,7 @@ void update_topline(void) } } curwin->w_valid |= VALID_TOPLINE; + win_check_anchored_floats(curwin); /* * Need to redraw when topline changed. @@ -827,7 +830,8 @@ void curs_columns( new_leftcol = 0; if (new_leftcol != (int)curwin->w_leftcol) { curwin->w_leftcol = new_leftcol; - /* screen has to be redrawn with new curwin->w_leftcol */ + win_check_anchored_floats(curwin); + // screen has to be redrawn with new curwin->w_leftcol redraw_later(NOT_VALID); } } @@ -943,6 +947,74 @@ void curs_columns( curwin->w_valid |= VALID_WCOL|VALID_WROW|VALID_VIRTCOL; } +/// Compute the screen position of text character at "pos" in window "wp" +/// The resulting values are one-based, zero when character is not visible. +/// +/// @param[out] rowp screen row +/// @param[out] scolp start screen column +/// @param[out] ccolp cursor screen column +/// @param[out] ecolp end screen column +void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, + int *ccolp, int *ecolp, bool local) +{ + colnr_T scol = 0, ccol = 0, ecol = 0; + int row = 0; + int rowoff = 0; + colnr_T coloff = 0; + bool visible_row = false; + + if (pos->lnum >= wp->w_topline && pos->lnum < wp->w_botline) { + row = plines_m_win(wp, wp->w_topline, pos->lnum - 1) + 1; + visible_row = true; + } else if (pos->lnum < wp->w_topline) { + row = 0; + } else { + row = wp->w_height_inner; + } + + bool existing_row = (pos->lnum > 0 + && pos->lnum <= wp->w_buffer->b_ml.ml_line_count); + + if ((local && existing_row) || visible_row) { + colnr_T off; + colnr_T col; + int width; + + getvcol(wp, pos, &scol, &ccol, &ecol); + + // similar to what is done in validate_cursor_col() + col = scol; + off = win_col_off(wp); + col += off; + width = wp->w_width - off + win_col_off2(wp); + + // long line wrapping, adjust row + if (wp->w_p_wrap && col >= (colnr_T)wp->w_width && width > 0) { + // use same formula as what is used in curs_columns() + rowoff = visible_row ? ((col - wp->w_width) / width + 1) : 0; + col -= rowoff * width; + } + + col -= wp->w_leftcol; + + if (col >= 0 && col < width) { + coloff = col - scol + (local ? 0 : wp->w_wincol) + 1; + } else { + scol = ccol = ecol = 0; + // character is left or right of the window + if (local) { + coloff = col < 0 ? -1 : wp->w_width_inner + 1; + } else { + row = 0; + } + } + } + *rowp = (local ? 0 : wp->w_winrow) + row + rowoff; + *scolp = scol + coloff; + *ccolp = ccol + coloff; + *ecolp = ecol + coloff; +} + /* * Scroll the current window down by "line_count" logical lines. "CTRL-Y" */ @@ -1099,6 +1171,7 @@ check_topfill ( } } } + win_check_anchored_floats(curwin); } /* diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 3aa2f47907..d846bd0ae0 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -21,13 +21,14 @@ #define SREQ_WIN 1 // Request window-local option value #define SREQ_BUF 2 // Request buffer-local option value -/* - * Default values for 'errorformat'. - * The "%f|%l| %m" one is used for when the contents of the quickfix window is - * written to a file. - */ -#define DFLT_EFM \ - "%*[^\"]\"%f\"%*\\D%l: %m,\"%f\"%*\\D%l: %m,%-G%f:%l: (Each undeclared identifier is reported only once,%-G%f:%l: for each function it appears in.),%-GIn file included from %f:%l:%c:,%-GIn file included from %f:%l:%c\\,,%-GIn file included from %f:%l:%c,%-GIn file included from %f:%l,%-G%*[ ]from %f:%l:%c,%-G%*[ ]from %f:%l:,%-G%*[ ]from %f:%l\\,,%-G%*[ ]from %f:%l,%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,\"%f\"\\, line %l%*\\D%c%*[^ ] %m,%D%*\\a[%*\\d]: Entering directory %*[`']%f',%X%*\\a[%*\\d]: Leaving directory %*[`']%f',%D%*\\a: Entering directory %*[`']%f',%X%*\\a: Leaving directory %*[`']%f',%DMaking %*\\a in %f,%f|%l| %m" +// Default values for 'errorformat'. +// The "%f|%l| %m" one is used for when the contents of the quickfix window is +// written to a file. +#ifdef WIN32 +# define DFLT_EFM "%f(%l) \\=: %t%*\\D%n: %m,%*[^\"]\"%f\"%*\\D%l: %m,%f(%l) \\=: %m,%*[^ ] %f %l: %m,%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,%f|%l| %m" +#else +# define DFLT_EFM "%*[^\"]\"%f\"%*\\D%l: %m,\"%f\"%*\\D%l: %m,%-G%f:%l: (Each undeclared identifier is reported only once,%-G%f:%l: for each function it appears in.),%-GIn file included from %f:%l:%c:,%-GIn file included from %f:%l:%c\\,,%-GIn file included from %f:%l:%c,%-GIn file included from %f:%l,%-G%*[ ]from %f:%l:%c,%-G%*[ ]from %f:%l:,%-G%*[ ]from %f:%l\\,,%-G%*[ ]from %f:%l,%f:%l:%c:%m,%f(%l):%m,%f:%l:%m,\"%f\"\\, line %l%*\\D%c%*[^ ] %m,%D%*\\a[%*\\d]: Entering directory %*[`']%f',%X%*\\a[%*\\d]: Leaving directory %*[`']%f',%D%*\\a: Entering directory %*[`']%f',%X%*\\a: Leaving directory %*[`']%f',%DMaking %*\\a in %f,%f|%l| %m" +#endif #define DFLT_GREPFORMAT "%f:%l:%m,%f:%l%m,%f %l%m" diff --git a/src/nvim/os/signal.c b/src/nvim/os/signal.c index 20f68233e7..ba6226ef9d 100644 --- a/src/nvim/os/signal.c +++ b/src/nvim/os/signal.c @@ -145,6 +145,8 @@ static void deadly_signal(int signum) // Set the v:dying variable. set_vim_var_nr(VV_DYING, 1); + WLOG("got signal %d (%s)", signum, signal_name(signum)); + snprintf((char *)IObuff, sizeof(IObuff), "Vim: Caught deadly signal '%s'\n", signal_name(signum)); diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 80477c6947..26687f14f0 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -1016,7 +1016,8 @@ qf_init_end: /// Set the title of the specified quickfix list. Frees the previous title. /// Prepends ':' to the title. -static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title) +static void qf_store_title(qf_info_T *qi, int qf_idx, const char_u *title) + FUNC_ATTR_NONNULL_ARG(1) { XFREE_CLEAR(qi->qf_lists[qf_idx].qf_title); @@ -1025,7 +1026,7 @@ static void qf_store_title(qf_info_T *qi, int qf_idx, char_u *title) char_u *p = xmallocz(len); qi->qf_lists[qf_idx].qf_title = p; - xstrlcpy((char *)p, (char *)title, len + 1); + xstrlcpy((char *)p, (const char *)title, len + 1); } } @@ -4693,7 +4694,7 @@ static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what) if (di->di_tv.vval.v_number != 0) { qf_idx = (int)di->di_tv.vval.v_number - 1; if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { - qf_idx = -1; + qf_idx = INVALID_QFIDX; } } } else if (di->di_tv.v_type == VAR_STRING @@ -4701,7 +4702,7 @@ static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what) // Get the last quickfix list number qf_idx = qi->qf_listcount - 1; } else { - qf_idx = -1; + qf_idx = INVALID_QFIDX; } } @@ -4713,7 +4714,7 @@ static int qf_getprop_qfidx(qf_info_T *qi, dict_T *what) qf_idx = qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number); } } else { - qf_idx = -1; + qf_idx = INVALID_QFIDX; } } @@ -4829,7 +4830,7 @@ int qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict) } // List is not present or is empty - if (qi == NULL || qi->qf_listcount == 0 || qf_idx == -1) { + if (qi == NULL || qi->qf_listcount == 0 || qf_idx == INVALID_QFIDX) { return qf_getprop_defaults(qi, flags, retdict); } @@ -4978,18 +4979,17 @@ static int qf_add_entries(qf_info_T *qi, int qf_idx, list_T *list, return retval; } -static int qf_set_properties(qf_info_T *qi, dict_T *what, int action, - char_u *title) +// Get the quickfix list index from 'nr' or 'id' +static int qf_setprop_get_qfidx( + const qf_info_T *qi, + const dict_T *what, + int action, + bool *newlist) + FUNC_ATTR_NONNULL_ALL { dictitem_T *di; - int retval = FAIL; - int newlist = false; - char_u *errorformat = p_efm; - - if (action == ' ' || qi->qf_curlist == qi->qf_listcount) { - newlist = true; - } int qf_idx = qi->qf_curlist; // default is the current list + if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { @@ -5002,37 +5002,140 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action, // When creating a new list, accept qf_idx pointing to the next // non-available list and add the new list at the end of the // stack. - newlist = true; - qf_idx = qi->qf_listcount - 1; + *newlist = true; + qf_idx = qi->qf_listcount > 0 ? qi->qf_listcount - 1 : 0; } else if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { - return FAIL; + return INVALID_QFIDX; } else if (action != ' ') { - newlist = false; // use the specified list + *newlist = false; // use the specified list } } else if (di->di_tv.v_type == VAR_STRING && strequal((const char *)di->di_tv.vval.v_string, "$")) { if (qi->qf_listcount > 0) { qf_idx = qi->qf_listcount - 1; - } else if (newlist) { + } else if (*newlist) { qf_idx = 0; } else { - return FAIL; + return INVALID_QFIDX; } } else { - return FAIL; + return INVALID_QFIDX; } } - if (!newlist && (di = tv_dict_find(what, S_LEN("id"))) != NULL) { + if (!*newlist && (di = tv_dict_find(what, S_LEN("id"))) != NULL) { // Use the quickfix/location list with the specified id - if (di->di_tv.v_type == VAR_NUMBER) { - qf_idx = qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number); - if (qf_idx == -1) { - return FAIL; // List not found - } - } else { + if (di->di_tv.v_type != VAR_NUMBER) { + return INVALID_QFIDX; + } + return qf_id2nr(qi, (unsigned)di->di_tv.vval.v_number); + } + + return qf_idx; +} + +// Set the quickfix list title. +static int qf_setprop_title(qf_info_T *qi, int qf_idx, const dict_T *what, + const dictitem_T *di) + FUNC_ATTR_NONNULL_ALL +{ + if (di->di_tv.v_type != VAR_STRING) { + return FAIL; + } + + xfree(qi->qf_lists[qf_idx].qf_title); + qi->qf_lists[qf_idx].qf_title = + (char_u *)tv_dict_get_string(what, "title", true); + if (qf_idx == qi->qf_curlist) { + qf_update_win_titlevar(qi); + } + + return OK; +} + +// Set quickfix list items/entries. +static int qf_setprop_items(qf_info_T *qi, int qf_idx, dictitem_T *di, + int action) + FUNC_ATTR_NONNULL_ALL +{ + if (di->di_tv.v_type != VAR_LIST) { + return FAIL; + } + + char_u *title_save = vim_strsave(qi->qf_lists[qf_idx].qf_title); + const int retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list, + title_save, + action == ' ' ? 'a' : action); + xfree(title_save); + + return retval; +} + +// Set quickfix list items/entries from a list of lines. +static int qf_setprop_items_from_lines( + qf_info_T *qi, + int qf_idx, + const dict_T *what, + dictitem_T *di, + int action) + FUNC_ATTR_NONNULL_ALL +{ + char_u *errorformat = p_efm; + dictitem_T *efm_di; + int retval = FAIL; + + // Use the user supplied errorformat settings (if present) + if ((efm_di = tv_dict_find(what, S_LEN("efm"))) != NULL) { + if (efm_di->di_tv.v_type != VAR_STRING + || efm_di->di_tv.vval.v_string == NULL) { return FAIL; } + errorformat = efm_di->di_tv.vval.v_string; + } + + // Only a List value is supported + if (di->di_tv.v_type != VAR_LIST || di->di_tv.vval.v_list == NULL) { + return FAIL; + } + + if (action == 'r') { + qf_free_items(qi, qf_idx); + } + if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, errorformat, + false, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) { + retval = OK; + } + + return retval; +} + +// Set quickfix list context. +static int qf_setprop_context(qf_info_T *qi, int qf_idx, dictitem_T *di) + FUNC_ATTR_NONNULL_ALL +{ + tv_free(qi->qf_lists[qf_idx].qf_ctx); + typval_T *ctx = xcalloc(1, sizeof(typval_T)); + if (ctx != NULL) { + tv_copy(&di->di_tv, ctx); + } + qi->qf_lists[qf_idx].qf_ctx = ctx; + + return OK; +} + +// Set quickfix/location list properties (title, items, context). +// Also used to add items from parsing a list of lines. +// Used by the setqflist() and setloclist() VimL functions. +static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, + char_u *title) + FUNC_ATTR_NONNULL_ALL +{ + dictitem_T *di; + int retval = FAIL; + bool newlist = action == ' ' || qi->qf_curlist == qi->qf_listcount; + int qf_idx = qf_setprop_get_qfidx(qi, what, action, &newlist); + if (qf_idx == INVALID_QFIDX) { // List not found + return FAIL; } if (newlist) { @@ -5042,63 +5145,16 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action, } if ((di = tv_dict_find(what, S_LEN("title"))) != NULL) { - if (di->di_tv.v_type == VAR_STRING) { - xfree(qi->qf_lists[qf_idx].qf_title); - qi->qf_lists[qf_idx].qf_title = (char_u *)tv_dict_get_string( - what, "title", true); - if (qf_idx == qi->qf_curlist) { - qf_update_win_titlevar(qi); - } - retval = OK; - } + retval = qf_setprop_title(qi, qf_idx, what, di); } if ((di = tv_dict_find(what, S_LEN("items"))) != NULL) { - if (di->di_tv.v_type == VAR_LIST) { - assert(qi->qf_lists[qf_idx].qf_title != NULL); - char_u *title_save = vim_strsave(qi->qf_lists[qf_idx].qf_title); - - retval = qf_add_entries(qi, qf_idx, di->di_tv.vval.v_list, - title_save, action == ' ' ? 'a' : action); - if (action == 'r') { - // When replacing the quickfix list entries using - // qf_add_entries(), the title is set with a ':' prefix. - // Restore the title with the saved title. - xfree(qi->qf_lists[qf_idx].qf_title); - qi->qf_lists[qf_idx].qf_title = vim_strsave(title_save); - } - xfree(title_save); - } + retval = qf_setprop_items(qi, qf_idx, di, action); } - - if ((di = tv_dict_find(what, S_LEN("efm"))) != NULL) { - if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL) { - return FAIL; - } - errorformat = di->di_tv.vval.v_string; - } - if ((di = tv_dict_find(what, S_LEN("lines"))) != NULL) { - // Only a List value is supported - if (di->di_tv.v_type == VAR_LIST && di->di_tv.vval.v_list != NULL) { - if (action == 'r') { - qf_free_items(qi, qf_idx); - } - if (qf_init_ext(qi, qf_idx, NULL, NULL, &di->di_tv, errorformat, - false, (linenr_T)0, (linenr_T)0, NULL, NULL) > 0) { - retval = OK; - } - } else { - return FAIL; - } + retval = qf_setprop_items_from_lines(qi, qf_idx, what, di, action); } - if ((di = tv_dict_find(what, S_LEN("context"))) != NULL) { - tv_free(qi->qf_lists[qf_idx].qf_ctx); - - typval_T *ctx = xcalloc(1, sizeof(typval_T)); - tv_copy(&di->di_tv, ctx); - qi->qf_lists[qf_idx].qf_ctx = ctx; - retval = OK; + retval = qf_setprop_context(qi, qf_idx, di); } if (retval == OK) { diff --git a/src/nvim/testdir/test_changedtick.vim b/src/nvim/testdir/test_changedtick.vim index 3a91bb54aa..c789cdc1bc 100644 --- a/src/nvim/testdir/test_changedtick.vim +++ b/src/nvim/testdir/test_changedtick.vim @@ -55,3 +55,41 @@ func Test_changedtick_fixed() call assert_fails('unlet d["changedtick"]', 'E46:') endfunc + +func Test_changedtick_not_incremented_with_write() + new + let fname = "XChangeTick" + exe 'w ' .. fname + + " :write when the buffer is not changed does not increment changedtick + let expected = b:changedtick + w + call assert_equal(expected, b:changedtick) + + " :write when the buffer IS changed DOES increment changedtick + let expected = b:changedtick + 1 + setlocal modified + w + call assert_equal(expected, b:changedtick) + + " Two ticks: change + write + let expected = b:changedtick + 2 + call setline(1, 'hello') + w + call assert_equal(expected, b:changedtick) + + " Two ticks: start insert + write + let expected = b:changedtick + 2 + normal! o + w + call assert_equal(expected, b:changedtick) + + " Three ticks: start insert + change + write + let expected = b:changedtick + 3 + normal! ochanged + w + call assert_equal(expected, b:changedtick) + + bwipe + call delete(fname) +endfunc diff --git a/src/nvim/testdir/test_cursor_func.vim b/src/nvim/testdir/test_cursor_func.vim index 6bc9535aaf..037918fa31 100644 --- a/src/nvim/testdir/test_cursor_func.vim +++ b/src/nvim/testdir/test_cursor_func.vim @@ -64,3 +64,31 @@ func Test_curswant_with_cursorline() call assert_equal(6, winsaveview().curswant) quit! endfunc + +func Test_screenpos() + rightbelow new + rightbelow 20vsplit + call setline(1, ["\tsome text", "long wrapping line here", "next line"]) + redraw + let winid = win_getid() + let [winrow, wincol] = win_screenpos(winid) + call assert_equal({'row': winrow, + \ 'col': wincol + 0, + \ 'curscol': wincol + 7, + \ 'endcol': wincol + 7}, screenpos(winid, 1, 1)) + call assert_equal({'row': winrow, + \ 'col': wincol + 13, + \ 'curscol': wincol + 13, + \ 'endcol': wincol + 13}, screenpos(winid, 1, 7)) + call assert_equal({'row': winrow + 2, + \ 'col': wincol + 1, + \ 'curscol': wincol + 1, + \ 'endcol': wincol + 1}, screenpos(winid, 2, 22)) + setlocal number + call assert_equal({'row': winrow + 3, + \ 'col': wincol + 9, + \ 'curscol': wincol + 9, + \ 'endcol': wincol + 9}, screenpos(winid, 2, 22)) + close + bwipe! +endfunc diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 90abb30da1..440bf47f4b 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -1167,6 +1167,13 @@ func Test_efm2() call assert_equal(1, len(l), string(l)) call assert_equal('|| msg2', l[0].text) + " When matching error lines, case should be ignored. Test for this. + set noignorecase + let l=getqflist({'lines' : ['Xtest:FOO10:Line 20'], 'efm':'%f:foo%l:%m'}) + call assert_equal(10, l.items[0].lnum) + call assert_equal('Line 20', l.items[0].text) + set ignorecase& + new | only let &efm = save_efm endfunc @@ -1802,6 +1809,9 @@ func Xproperty_tests(cchar) call assert_equal(0, s) let d = g:Xgetlist({"title":1}) call assert_equal('Sample', d.title) + " Try setting title to a non-string value + call assert_equal(-1, g:Xsetlist([], 'a', {'title' : ['Test']})) + call assert_equal('Sample', g:Xgetlist({"title":1}).title) Xopen call assert_equal('Sample', w:quickfix_title) @@ -1950,6 +1960,9 @@ func Xproperty_tests(cchar) call g:Xsetlist([], 'a', {'items' : [{'filename':'F1', 'lnum':10}]}) call assert_equal(10, g:Xgetlist({'items':1}).items[0].lnum) + " Try setting the items using a string + call assert_equal(-1, g:Xsetlist([], ' ', {'items' : 'Test'})) + " Save and restore the quickfix stack call g:Xsetlist([], 'f') call assert_equal(0, g:Xgetlist({'nr':'$'}).nr) @@ -3481,14 +3494,17 @@ func Xautocmd_changelist(cchar) call assert_equal(4, line('.')) autocmd! QuickFixCmdPost - " Test for grep/lgrep - call g:Xsetlist([], 'f') - Xexpr 'Xtestfile1:2:Line2' - autocmd QuickFixCmdPost * Xolder - silent Xgrep Line5 Xtestfile2 - call assert_equal('Xtestfile2', bufname('')) - call assert_equal(5, line('.')) - autocmd! QuickFixCmdPost + " The grepprg may not be set on non-Unix systems + if has('unix') + " Test for grep/lgrep + call g:Xsetlist([], 'f') + Xexpr 'Xtestfile1:2:Line2' + autocmd QuickFixCmdPost * Xolder + silent Xgrep Line5 Xtestfile2 + call assert_equal('Xtestfile2', bufname('')) + call assert_equal(5, line('.')) + autocmd! QuickFixCmdPost + endif " Test for vimgrep/lvimgrep call g:Xsetlist([], 'f') diff --git a/src/nvim/undo.c b/src/nvim/undo.c index c8343941d2..8ec36a8fe3 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -2294,7 +2294,7 @@ static void u_undoredo(int undo, bool do_buf_event) if (old_flags & UH_CHANGED) { changed(); } else { - unchanged(curbuf, FALSE); + unchanged(curbuf, false, true); } // because the calls to changed()/unchanged() above will bump changedtick diff --git a/src/nvim/version.c b/src/nvim/version.c index 74a4852def..0eb005d5df 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -69,17 +69,17 @@ NULL // clang-format off static const int included_patches[] = { 1849, - // 1848, + 1848, 1847, - // 1846, + 1846, 1845, // 1844, 1843, // 1842, - // 1841, + 1841, 1840, 1839, - // 1838, + 1838, 1837, 1836, // 1835, @@ -87,23 +87,23 @@ static const int included_patches[] = { 1833, 1832, 1831, - // 1830, + 1830, 1829, 1828, 1827, 1826, 1825, 1824, - // 1823, + 1823, 1822, - // 1821, - // 1820, + 1821, + 1820, 1819, - // 1818, + 1818, // 1817, 1816, // 1815, - // 1814, + 1814, 1813, // 1812, 1811, @@ -111,18 +111,18 @@ static const int included_patches[] = { 1809, 1808, 1807, - // 1806, + 1806, 1805, // 1804, 1803, - // 1802, - // 1801, + 1802, + 1801, 1800, 1799, // 1798, // 1797, - // 1796, - // 1795, + 1796, + 1795, // 1794, // 1793, 1792, @@ -137,26 +137,26 @@ static const int included_patches[] = { // 1783, 1782, 1781, - // 1780, + 1780, 1779, 1778, 1777, // 1776, - // 1775, + 1775, // 1774, 1773, // 1772, // 1771, // 1770, // 1769, - // 1768, + 1768, // 1767, 1766, 1765, 1764, 1763, - // 1762, - // 1761, + 1762, + 1761, 1760, // 1759, 1758, @@ -164,22 +164,22 @@ static const int included_patches[] = { 1756, 1755, // 1754, - // 1753, + 1753, // 1752, 1751, 1750, 1749, 1748, // 1747, - // 1746, + 1746, // 1745, // 1744, // 1743, // 1742, 1741, - // 1740, + 1740, 1739, - // 1738, + 1738, 1737, 1736, 1735, @@ -188,113 +188,113 @@ static const int included_patches[] = { // 1732, 1731, 1730, - // 1729, - // 1728, + 1729, + 1728, 1727, 1726, // 1725, - // 1724, + 1724, 1723, // 1722, - // 1721, - // 1720, + 1721, + 1720, 1719, - // 1718, + 1718, 1717, - // 1716, - // 1715, - // 1714, + 1716, + 1715, + 1714, // 1713, // 1712, // 1711, 1710, - // 1709, + 1709, 1708, 1707, // 1706, 1705, 1704, - // 1703, + 1703, 1702, 1701, 1700, 1699, 1698, - // 1697, + 1697, 1696, - // 1695, - // 1694, + 1695, + 1694, 1693, 1692, - // 1691, - // 1690, + 1691, + 1690, 1689, - // 1688, + 1688, 1687, 1686, // 1685, - // 1684, + 1684, 1683, 1682, - // 1681, - // 1680, + 1681, + 1680, 1679, 1678, 1677, 1676, 1675, 1674, - // 1673, + 1673, 1672, // 1671, - // 1670, + 1670, 1669, // 1668, - // 1667, + 1667, // 1666, // 1665, - // 1664, + 1664, 1663, - // 1662, + 1662, 1661, // 1660, 1659, 1658, - // 1657, - // 1656, + 1657, + 1656, // 1655, 1654, - // 1653, - // 1652, + 1653, + 1652, // 1651, 1650, 1649, - // 1648, + 1648, // 1647, 1646, - // 1645, - // 1644, - // 1643, - // 1642, + 1645, + 1644, + 1643, + 1642, // 1641, - // 1640, + 1640, 1639, - // 1638, - // 1637, - // 1636, + 1638, + 1637, + 1636, 1635, 1634, 1633, - // 1632, - // 1631, + 1632, + 1631, 1630, - // 1629, - // 1628, + 1629, + 1628, 1627, 1626, 1625, // 1624, - // 1623, + 1623, 1622, // 1621, 1620, @@ -302,28 +302,28 @@ static const int included_patches[] = { 1618, // 1617, // 1616, - // 1615, + 1615, 1614, 1613, // 1612, - // 1611, + 1611, 1610, // 1609, 1608, 1607, 1606, - // 1605, + 1605, // 1604, 1603, 1602, 1601, 1600, // 1599, - // 1598, + 1598, 1597, // 1596, 1595, - // 1594, + 1594, // 1593, // 1592, // 1591, @@ -341,7 +341,7 @@ static const int included_patches[] = { 1579, // 1578, 1577, - // 1576, + 1576, 1575, // 1574, 1573, @@ -365,44 +365,44 @@ static const int included_patches[] = { 1555, // 1554, 1553, - // 1552, + 1552, // 1551, // 1550, 1549, 1548, 1547, - // 1546, + 1546, 1545, // 1544, // 1543, - // 1542, + 1542, 1541, // 1540, // 1539, // 1538, - // 1537, + 1537, 1536, - // 1535, - // 1534, - // 1533, + 1535, + 1534, + 1533, 1532, // 1531, 1530, // 1529, 1528, // 1527, - // 1526, + 1526, // 1525, 1524, // 1523, // 1522, - // 1521, + 1521, // 1520, 1519, 1518, 1517, 1516, - // 1515, + 1515, 1514, 1513, 1512, @@ -433,15 +433,15 @@ static const int included_patches[] = { 1487, 1486, 1485, - // 1484, + 1484, 1483, - // 1482, + 1482, 1481, - // 1480, - // 1479, + 1480, + 1479, 1478, - // 1477, - // 1476, + 1477, + 1476, 1475, 1474, 1473, @@ -455,7 +455,7 @@ static const int included_patches[] = { 1465, 1464, // 1463, - // 1462, + 1462, // 1461, // 1460, // 1459, @@ -487,8 +487,8 @@ static const int included_patches[] = { 1433, 1432, 1431, - // 1430, - // 1429, + 1430, + 1429, 1428, 1427, 1426, @@ -531,17 +531,17 @@ static const int included_patches[] = { 1389, // 1388, 1387, - // 1386, + 1386, 1385, 1384, 1383, // 1382, - // 1381, + 1381, 1380, 1379, 1378, 1377, - // 1376, + 1376, // 1375, 1374, 1373, @@ -549,16 +549,16 @@ static const int included_patches[] = { // 1371, 1370, 1369, - // 1368, + 1368, // 1367, // 1366, 1365, 1364, 1363, - // 1362, + 1362, 1361, - // 1360, - // 1359, + 1360, + 1359, // 1358, 1357, // 1356, @@ -570,14 +570,14 @@ static const int included_patches[] = { 1350, // 1349, 1348, - // 1347, + 1347, // 1346, // 1345, // 1344, 1343, - // 1342, + 1342, // 1341, - // 1340, + 1340, // 1339, 1338, 1337, @@ -589,20 +589,20 @@ static const int included_patches[] = { 1331, // 1330, 1329, - // 1328, + 1328, 1327, 1326, 1325, 1324, - // 1323, + 1323, 1322, // 1321, // 1320, // 1319, // 1318, - // 1317, - // 1316, - // 1315, + 1317, + 1316, + 1315, 1314, 1313, // 1312, @@ -621,7 +621,7 @@ static const int included_patches[] = { 1299, 1298, // 1297, - // 1296, + 1296, // 1295, 1294, // 1293, @@ -631,7 +631,7 @@ static const int included_patches[] = { 1289, 1288, // 1287, - // 1286, + 1286, 1285, 1284, 1283, @@ -640,10 +640,10 @@ static const int included_patches[] = { 1280, 1279, 1278, - // 1277, + 1277, // 1276, 1275, - // 1274, + 1274, 1273, 1272, 1271, @@ -656,9 +656,9 @@ static const int included_patches[] = { // 1264, 1263, 1262, - // 1261, - // 1260, - // 1259, + 1261, + 1260, + 1259, 1258, 1257, 1256, @@ -671,17 +671,17 @@ static const int included_patches[] = { 1249, 1248, 1247, - // 1246, + 1246, 1245, // 1244, 1243, 1242, - // 1241, + 1241, // 1240, - // 1239, + 1239, 1238, 1237, - // 1236, + 1236, 1235, 1234, 1233, @@ -700,11 +700,11 @@ static const int included_patches[] = { 1220, 1219, 1218, - // 1217, + 1217, 1216, 1215, 1214, - // 1213, + 1213, 1212, 1211, 1210, @@ -715,7 +715,7 @@ static const int included_patches[] = { 1205, 1204, // 1203, - // 1202, + 1202, 1201, 1200, 1199, @@ -724,7 +724,7 @@ static const int included_patches[] = { 1196, 1195, // 1194, - // 1193, + 1193, 1192, 1191, 1190, @@ -738,24 +738,24 @@ static const int included_patches[] = { 1182, 1181, 1180, - // 1179, + 1179, 1178, - // 1177, + 1177, // 1176, 1175, // 1174, - // 1173, + 1173, 1172, 1171, // 1170, 1169, 1168, - // 1167, + 1167, 1166, 1165, - // 1164, + 1164, 1163, - // 1162, + 1162, 1161, 1160, 1159, @@ -768,10 +768,10 @@ static const int included_patches[] = { 1152, 1151, 1150, - // 1149, + 1149, 1148, 1147, - // 1146, + 1146, 1145, 1144, 1143, @@ -786,22 +786,22 @@ static const int included_patches[] = { // 1134, 1133, 1132, - // 1131, + 1131, 1130, // 1129, 1128, // 1127, - // 1126, + 1126, // 1125, 1124, // 1123, 1122, 1121, - // 1120, + 1120, // 1119, 1118, - // 1117, - // 1116, + 1117, + 1116, 1115, 1114, // 1113, @@ -811,18 +811,18 @@ static const int included_patches[] = { // 1109, 1108, 1107, - // 1106, + 1106, 1105, 1104, - // 1103, + 1103, // 1102, - // 1101, - // 1100, + 1101, + 1100, 1099, 1098, // 1097, // 1096, - // 1095, + 1095, 1094, 1093, 1092, @@ -830,24 +830,24 @@ static const int included_patches[] = { 1090, 1089, 1088, - // 1087, + 1087, 1086, 1085, 1084, - // 1083, + 1083, 1082, - // 1081, - // 1080, + 1081, + 1080, 1079, 1078, 1077, - // 1076, - // 1075, + 1076, + 1075, // 1074, 1073, 1072, 1071, - // 1070, + 1070, 1069, 1068, 1067, @@ -859,16 +859,16 @@ static const int included_patches[] = { 1061, // 1060, 1059, - // 1058, - // 1057, + 1058, + 1057, 1056, - // 1055, - // 1054, - // 1053, - // 1052, + 1055, + 1054, + 1053, + 1052, // 1051, 1050, - // 1049, + 1049, 1048, 1047, 1046, @@ -878,12 +878,12 @@ static const int included_patches[] = { 1042, 1041, 1040, - // 1039, + 1039, // 1038, 1037, - // 1036, + 1036, // 1035, - // 1034, + 1034, 1033, 1032, 1031, @@ -902,13 +902,13 @@ static const int included_patches[] = { 1018, 1017, 1016, - // 1015, + 1015, 1014, - // 1013, + 1013, 1012, - // 1011, + 1011, 1010, - // 1009, + 1009, 1008, 1007, 1006, @@ -922,45 +922,45 @@ static const int included_patches[] = { 998, 997, 996, - // 995, - // 994, + 995, + 994, 993, - // 992, - // 991, - // 990, + 992, + 991, + 990, 989, 988, - // 987, + 987, 986, - // 985, - // 984, + 985, + 984, 983, - // 982, - // 981, + 982, + 981, 980, - // 979, - // 978, - // 977, + 979, + 978, + 977, // 976, 975, 974, - // 973, + 973, 972, 971, // 970, 969, - // 968, + 968, 967, 966, - // 965, - // 964, - // 963, + 965, + 964, + 963, 962, 961, // 960, - // 959, - // 958, - // 957, + 959, + 958, + 957, // 956, 955, 954, @@ -970,34 +970,34 @@ static const int included_patches[] = { 950, 949, 948, - // 947, + 947, 946, 945, 944, - // 943, - // 942, + 943, + 942, // 941, - // 940, - // 939, - // 938, + 940, + 939, + 938, // 937, - // 936, - // 935, + 936, + 935, // 934, 933, // 932, 931, - // 930, - // 929, + 930, + 929, 928, - // 927, - // 926, + 927, + 926, 925, - // 924, - // 923, + 924, + 923, 922, 921, - // 920, + 920, 919, // 918, // 917, @@ -1008,8 +1008,8 @@ static const int included_patches[] = { // 912, 911, // 910, - // 909, - // 908, + 909, + 908, 907, 906, 905, @@ -1614,14 +1614,14 @@ static const int included_patches[] = { 306, 305, 304, - // 303, + 303, 302, 301, 300, 299, 298, 297, - // 296, + 296, 295, 294, 293, @@ -1679,14 +1679,14 @@ static const int included_patches[] = { 241, 240, 239, - // 238, + 238, 237, 236, 235, 234, // 233, 232, - // 231, + 231, 230, 229, 228, diff --git a/src/nvim/window.c b/src/nvim/window.c index 6861e19ca7..315b5ef759 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -652,6 +652,17 @@ void win_config_float(win_T *wp, FloatConfig fconfig) } } +void win_check_anchored_floats(win_T *win) +{ + for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { + // float might be anchored to moved window + if (wp->w_float_config.relative == kFloatRelativeWindow + && wp->w_float_config.window == win->handle) { + wp->w_pos_changed = true; + } + } +} + static void ui_ext_win_position(win_T *wp) { if (!wp->w_floating) { @@ -673,6 +684,13 @@ static void ui_ext_win_position(win_T *wp) screen_adjust_grid(&grid, &row_off, &col_off); row += row_off; col += col_off; + if (c.bufpos.lnum >= 0) { + pos_T pos = { c.bufpos.lnum+1, c.bufpos.col, 0 }; + int trow, tcol, tcolc, tcole; + textpos2screenpos(win, &pos, &trow, &tcol, &tcolc, &tcole, true); + row += trow-1; + col += tcol-1; + } } api_clear_error(&dummy); } @@ -745,6 +763,18 @@ static bool parse_float_relative(String relative, FloatRelative *out) return true; } +static bool parse_float_bufpos(Array bufpos, lpos_T *out) +{ + if (bufpos.size != 2 + || bufpos.items[0].type != kObjectTypeInteger + || bufpos.items[1].type != kObjectTypeInteger) { + return false; + } + out->lnum = bufpos.items[0].data.integer; + out->col = bufpos.items[1].data.integer; + return true; +} + bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, Error *err) { @@ -753,6 +783,7 @@ bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, bool has_row = false, has_col = false, has_relative = false; bool has_external = false, has_window = false; bool has_width = false, has_height = false; + bool has_bufpos = false; for (size_t i = 0; i < config.size; i++) { char *key = config.items[i].key.data; @@ -832,6 +863,18 @@ bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, return false; } fconfig->window = val.data.integer; + } else if (!strcmp(key, "bufpos")) { + if (val.type != kObjectTypeArray) { + api_set_error(err, kErrorTypeValidation, + "'bufpos' key must be Array"); + return false; + } + if (!parse_float_bufpos(val.data.array, &fconfig->bufpos)) { + api_set_error(err, kErrorTypeValidation, + "Invalid value of 'bufpos' key"); + return false; + } + has_bufpos = true; } else if (!strcmp(key, "external")) { if (val.type == kObjectTypeInteger) { fconfig->external = val.data.integer; @@ -886,6 +929,21 @@ bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, fconfig->window = curwin->handle; } + if (has_window && !has_bufpos) { + fconfig->bufpos.lnum = -1; + } + + if (has_bufpos) { + if (!has_row) { + fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1; + has_row = true; + } + if (!has_col) { + fconfig->col = 0; + has_col = true; + } + } + if (has_relative && has_external) { api_set_error(err, kErrorTypeValidation, "Only one of 'relative' and 'external' must be used"); @@ -4732,7 +4790,8 @@ void shell_new_rows(void) if (!frame_check_height(topframe, h)) frame_new_height(topframe, h, FALSE, FALSE); - (void)win_comp_pos(); /* recompute w_winrow and w_wincol */ + (void)win_comp_pos(); // recompute w_winrow and w_wincol + win_reconfig_floats(); // The size of floats might change compute_cmdrow(); curtab->tp_ch_used = p_ch; @@ -4753,7 +4812,8 @@ void shell_new_columns(void) frame_new_width(topframe, Columns, false, false); } - (void)win_comp_pos(); /* recompute w_winrow and w_wincol */ + (void)win_comp_pos(); // recompute w_winrow and w_wincol + win_reconfig_floats(); // The size of floats might change } /* @@ -4809,15 +4869,23 @@ int win_comp_pos(void) frame_comp_pos(topframe, &row, &col); - // Too often, but when we support anchoring floats to split windows, - // this will be needed for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { - win_config_float(wp, wp->w_float_config); + // float might be anchored to moved window + if (wp->w_float_config.relative == kFloatRelativeWindow) { + wp->w_pos_changed = true; + } } return row; } +void win_reconfig_floats(void) +{ + for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { + win_config_float(wp, wp->w_float_config); + } +} + /* * Update the position of the windows in frame "topfrp", using the width and * height of the frames. diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index ddd044a10f..a20667de40 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -180,7 +180,7 @@ describe('server -> client', function() end) describe('recursive (child) nvim client', function() - if helpers.isCI('travis') and helpers.os_name() == 'osx' then + if helpers.isCI('travis') and helpers.is_os('mac') then -- XXX: Hangs Travis macOS since e9061117a5b8f195c3f26a5cb94e18ddd7752d86. pending("[Hangs on Travis macOS. #5002]", function() end) return @@ -339,7 +339,7 @@ describe('server -> client', function() describe('connecting to its own pipe address', function() it('does not deadlock', function() - if not helpers.isCI('travis') and helpers.os_name() == 'osx' then + if not helpers.isCI('travis') and helpers.is_os('mac') then -- It does, in fact, deadlock on QuickBuild. #6851 pending("deadlocks on QuickBuild", function() end) return diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 07c0c5c8f3..b80b1c87af 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -11,7 +11,7 @@ local iswin = helpers.iswin local meth_pcall = helpers.meth_pcall local meths = helpers.meths local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed -local os_name = helpers.os_name +local is_os = helpers.is_os local parse_context = helpers.parse_context local request = helpers.request local source = helpers.source @@ -83,7 +83,7 @@ describe('API', function() nvim('command', 'w') local f = io.open(fname) ok(f ~= nil) - if os_name() == 'windows' then + if is_os('win') then eq('testing\r\napi\r\n', f:read('*a')) else eq('testing\napi\n', f:read('*a')) diff --git a/test/functional/core/channels_spec.lua b/test/functional/core/channels_spec.lua index 852d9808f5..1ef34c7318 100644 --- a/test/functional/core/channels_spec.lua +++ b/test/functional/core/channels_spec.lua @@ -7,7 +7,7 @@ local sleep = helpers.sleep local spawn, nvim_argv = helpers.spawn, helpers.nvim_argv local set_session = helpers.set_session local nvim_prog = helpers.nvim_prog -local os_name = helpers.os_name +local is_os = helpers.is_os local retry = helpers.retry local expect_twostreams = helpers.expect_twostreams @@ -140,7 +140,7 @@ describe('channels', function() command("call chansend(id, 'incomplet\004')") local is_bsd = not not string.find(uname(), 'bsd') - local bsdlike = is_bsd or (os_name() == "osx") + local bsdlike = is_bsd or is_os('mac') local extra = bsdlike and "^D\008\008" or "" expect_twoline(id, "stdout", "incomplet"..extra, "[1, ['incomplet'], 'stdin']", true) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 88951e5b51..94b34ef05b 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -206,7 +206,7 @@ describe('jobs', function() it("will not buffer data if it doesn't end in newlines", function() if helpers.isCI('travis') and os.getenv('CC') == 'gcc-4.9' - and helpers.os_name() == "osx" then + and helpers.is_os('mac') then -- XXX: Hangs Travis macOS since e9061117a5b8f195c3f26a5cb94e18ddd7752d86. pending("[Hangs on Travis macOS. #5002]", function() end) return diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index c34849b439..cf9f5f9858 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -15,6 +15,7 @@ local check_logs = global_helpers.check_logs local dedent = global_helpers.dedent local eq = global_helpers.eq local filter = global_helpers.filter +local is_os = global_helpers.is_os local map = global_helpers.map local ok = global_helpers.ok local sleep = global_helpers.sleep @@ -271,22 +272,6 @@ function module.eval(expr) return module.request('nvim_eval', expr) end -module.os_name = (function() - local name = nil - return (function() - if not name then - if module.eval('has("win32")') == 1 then - name = 'windows' - elseif module.eval('has("macunix")') == 1 then - name = 'osx' - else - name = 'unix' - end - end - return name - end) -end)() - -- Executes a VimL function. -- Fails on VimL error, but does not update v:errmsg. function module.call(name, ...) @@ -609,7 +594,7 @@ local function do_rmdir(path) -- Try Nvim delete(): it handles `readonly` attribute on Windows, -- and avoids Lua cross-version/platform incompatibilities. if -1 == module.call('delete', abspath) then - local hint = (module.os_name() == 'windows' + local hint = (is_os('win') and ' (hint: try :%bwipeout! before rmdir())' or '') error('delete() failed'..hint..': '..abspath) end @@ -626,7 +611,7 @@ end function module.rmdir(path) local ret, _ = pcall(do_rmdir, path) - if not ret and module.os_name() == "windows" then + if not ret and is_os('win') then -- Maybe "Permission denied"; try again after changing the nvim -- process to the top-level directory. module.command([[exe 'cd '.fnameescape(']]..start_dir.."')") diff --git a/test/functional/legacy/file_perm_spec.lua b/test/functional/legacy/file_perm_spec.lua index d61fdc9b83..8fdee95e91 100644 --- a/test/functional/legacy/file_perm_spec.lua +++ b/test/functional/legacy/file_perm_spec.lua @@ -21,7 +21,7 @@ describe('Test getting and setting file permissions', function() eq(9, call('len', call('getfperm', tempfile))) eq(1, call('setfperm', tempfile, 'rwx------')) - if helpers.os_name() == 'windows' then + if helpers.is_os('win') then eq('rw-rw-rw-', call('getfperm', tempfile)) else eq('rwx------', call('getfperm', tempfile)) diff --git a/test/functional/plugin/shada_spec.lua b/test/functional/plugin/shada_spec.lua index d96b479a62..5663f248bf 100644 --- a/test/functional/plugin/shada_spec.lua +++ b/test/functional/plugin/shada_spec.lua @@ -12,9 +12,7 @@ local shada_helpers = require('test.functional.shada.helpers') local get_shada_rw = shada_helpers.get_shada_rw local function reset(shada_file) - clear{ args={'-u', 'NORC', - '-i', shada_file or 'NONE', - }} + clear{ args={'-u', 'NORC', '-i', shada_file or 'NONE', }} end local mpack_eq = function(expected, mpack_result) @@ -2143,7 +2141,7 @@ describe('plugin/shada.vim', function() local epoch = os.date('%Y-%m-%dT%H:%M:%S', 0) local eol = helpers.iswin() and '\r\n' or '\n' before_each(function() - reset() + -- Note: reset() is called explicitly in each test. os.remove(fname) os.remove(fname .. '.tst') os.remove(fname_tmp) @@ -2159,272 +2157,263 @@ describe('plugin/shada.vim', function() mpack_eq(expected, mpack_result) end - describe('event BufReadCmd', function() - it('works', function() - wshada('\004\000\009\147\000\196\002ab\196\001a') - wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') - nvim_command('edit ' .. fname) - eq({ - 'History entry with timestamp ' .. epoch .. ':', - ' @ Description_ Value', - ' - history type CMD', - ' - contents "ab"', - ' - "a"', - }, nvim_eval('getline(1, "$")')) - eq(false, curbuf('get_option', 'modified')) - eq('shada', curbuf('get_option', 'filetype')) - nvim_command('edit ' .. fname_tmp) - eq({ - 'History entry with timestamp ' .. epoch .. ':', - ' @ Description_ Value', - ' - history type CMD', - ' - contents "ab"', - ' - "b"', - }, nvim_eval('getline(1, "$")')) - eq(false, curbuf('get_option', 'modified')) - eq('shada', curbuf('get_option', 'filetype')) - eq('++opt not supported', exc_exec('edit ++enc=latin1 ' .. fname)) - neq({ - 'History entry with timestamp ' .. epoch .. ':', - ' @ Description_ Value', - ' - history type CMD', - ' - contents "ab"', - ' - "a"', - }, nvim_eval('getline(1, "$")')) - neq(true, curbuf('get_option', 'modified')) - end) + it('event BufReadCmd', function() + reset() + wshada('\004\000\009\147\000\196\002ab\196\001a') + wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') + nvim_command('edit ' .. fname) + eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + }, nvim_eval('getline(1, "$")')) + eq(false, curbuf('get_option', 'modified')) + eq('shada', curbuf('get_option', 'filetype')) + nvim_command('edit ' .. fname_tmp) + eq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "b"', + }, nvim_eval('getline(1, "$")')) + eq(false, curbuf('get_option', 'modified')) + eq('shada', curbuf('get_option', 'filetype')) + eq('++opt not supported', exc_exec('edit ++enc=latin1 ' .. fname)) + neq({ + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + }, nvim_eval('getline(1, "$")')) + neq(true, curbuf('get_option', 'modified')) end) - describe('event FileReadCmd', function() - it('works', function() - wshada('\004\000\009\147\000\196\002ab\196\001a') - wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') - nvim_command('$read ' .. fname) - eq({ - '', - 'History entry with timestamp ' .. epoch .. ':', - ' @ Description_ Value', - ' - history type CMD', - ' - contents "ab"', - ' - "a"', - }, nvim_eval('getline(1, "$")')) - eq(true, curbuf('get_option', 'modified')) - neq('shada', curbuf('get_option', 'filetype')) - nvim_command('1,$read ' .. fname_tmp) - eq({ - '', - 'History entry with timestamp ' .. epoch .. ':', - ' @ Description_ Value', - ' - history type CMD', - ' - contents "ab"', - ' - "a"', - 'History entry with timestamp ' .. epoch .. ':', - ' @ Description_ Value', - ' - history type CMD', - ' - contents "ab"', - ' - "b"', - }, nvim_eval('getline(1, "$")')) - eq(true, curbuf('get_option', 'modified')) - neq('shada', curbuf('get_option', 'filetype')) - curbuf('set_option', 'modified', false) - eq('++opt not supported', exc_exec('$read ++enc=latin1 ' .. fname)) - eq({ - '', - 'History entry with timestamp ' .. epoch .. ':', - ' @ Description_ Value', - ' - history type CMD', - ' - contents "ab"', - ' - "a"', - 'History entry with timestamp ' .. epoch .. ':', - ' @ Description_ Value', - ' - history type CMD', - ' - contents "ab"', - ' - "b"', - }, nvim_eval('getline(1, "$")')) - neq(true, curbuf('get_option', 'modified')) - end) + it('event FileReadCmd', function() + reset() + wshada('\004\000\009\147\000\196\002ab\196\001a') + wshada_tmp('\004\000\009\147\000\196\002ab\196\001b') + nvim_command('$read ' .. fname) + eq({ + '', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + }, nvim_eval('getline(1, "$")')) + eq(true, curbuf('get_option', 'modified')) + neq('shada', curbuf('get_option', 'filetype')) + nvim_command('1,$read ' .. fname_tmp) + eq({ + '', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "b"', + }, nvim_eval('getline(1, "$")')) + eq(true, curbuf('get_option', 'modified')) + neq('shada', curbuf('get_option', 'filetype')) + curbuf('set_option', 'modified', false) + eq('++opt not supported', exc_exec('$read ++enc=latin1 ' .. fname)) + eq({ + '', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "a"', + 'History entry with timestamp ' .. epoch .. ':', + ' @ Description_ Value', + ' - history type CMD', + ' - contents "ab"', + ' - "b"', + }, nvim_eval('getline(1, "$")')) + neq(true, curbuf('get_option', 'modified')) end) - describe('event BufWriteCmd', function() - it('works', function() - nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_lines', 0, 1, true, { - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - ' + f file name ["foo"]', - ' + l line number 2', - ' + c column -200', - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - ' + f file name ["foo"]', - ' + l line number 2', - ' + c column -200', - }) - nvim_command('w ' .. fname .. '.tst') - nvim_command('w ' .. fname) - nvim_command('w ' .. fname_tmp) - eq('++opt not supported', exc_exec('w! ++enc=latin1 ' .. fname)) - eq(table.concat({ - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - ' + f file name ["foo"]', - ' + l line number 2', - ' + c column -200', - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - ' + f file name ["foo"]', - ' + l line number 2', - ' + c column -200', - }, eol) .. eol, read_file(fname .. '.tst')) - shada_eq({{ - timestamp=0, - type=8, - value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, - }, { - timestamp=0, - type=8, - value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, - }}, fname) - shada_eq({{ - timestamp=0, - type=8, - value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, - }, { - timestamp=0, - type=8, - value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, - }}, fname_tmp) - end) + it('event BufWriteCmd', function() + reset() + nvim('set_var', 'shada#add_own_header', 0) + curbuf('set_lines', 0, 1, true, { + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }) + nvim_command('w ' .. fname .. '.tst') + nvim_command('w ' .. fname) + nvim_command('w ' .. fname_tmp) + eq('++opt not supported', exc_exec('w! ++enc=latin1 ' .. fname)) + eq(table.concat({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }, eol) .. eol, read_file(fname .. '.tst')) + shada_eq({{ + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname) + shada_eq({{ + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname_tmp) end) - describe('event FileWriteCmd', function() - it('works', function() - nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_lines', 0, 1, true, { - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - ' + f file name ["foo"]', - ' + l line number 2', - ' + c column -200', - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - ' + f file name ["foo"]', - ' + l line number 2', - ' + c column -200', - }) - nvim_command('1,3w ' .. fname .. '.tst') - nvim_command('1,3w ' .. fname) - nvim_command('1,3w ' .. fname_tmp) - eq('++opt not supported', exc_exec('1,3w! ++enc=latin1 ' .. fname)) - eq(table.concat({ - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - }, eol) .. eol, read_file(fname .. '.tst')) - shada_eq({{ - timestamp=0, - type=8, - value={n=('A'):byte()}, - }}, fname) - shada_eq({{ - timestamp=0, - type=8, - value={n=('A'):byte()}, - }}, fname_tmp) - end) + it('event FileWriteCmd', function() + reset() + nvim('set_var', 'shada#add_own_header', 0) + curbuf('set_lines', 0, 1, true, { + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }) + nvim_command('1,3w ' .. fname .. '.tst') + nvim_command('1,3w ' .. fname) + nvim_command('1,3w ' .. fname_tmp) + eq('++opt not supported', exc_exec('1,3w! ++enc=latin1 ' .. fname)) + eq(table.concat({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + }, eol) .. eol, read_file(fname .. '.tst')) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }}, fname) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }}, fname_tmp) end) - describe('event FileAppendCmd', function() - it('works', function() - nvim('set_var', 'shada#add_own_header', 0) - curbuf('set_lines', 0, 1, true, { - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - ' + f file name ["foo"]', - ' + l line number 2', - ' + c column -200', - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - ' + f file name ["foo"]', - ' + l line number 2', - ' + c column -200', - }) - funcs.writefile({''}, fname .. '.tst', 'b') - funcs.writefile({''}, fname, 'b') - funcs.writefile({''}, fname_tmp, 'b') - nvim_command('1,3w >> ' .. fname .. '.tst') - nvim_command('1,3w >> ' .. fname) - nvim_command('1,3w >> ' .. fname_tmp) - nvim_command('w >> ' .. fname .. '.tst') - nvim_command('w >> ' .. fname) - nvim_command('w >> ' .. fname_tmp) - eq('++opt not supported', exc_exec('1,3w! ++enc=latin1 >> ' .. fname)) - eq(table.concat({ - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - ' + f file name ["foo"]', - ' + l line number 2', - ' + c column -200', - 'Jump with timestamp ' .. epoch .. ':', - ' % Key________ Description Value', - ' + n name \'A\'', - ' + f file name ["foo"]', - ' + l line number 2', - ' + c column -200', - }, eol) .. eol, read_file(fname .. '.tst')) - shada_eq({{ - timestamp=0, - type=8, - value={n=('A'):byte()}, - }, { - timestamp=0, - type=8, - value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, - }, { - timestamp=0, - type=8, - value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, - }}, fname) - shada_eq({{ - timestamp=0, - type=8, - value={n=('A'):byte()}, - }, { - timestamp=0, - type=8, - value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, - }, { - timestamp=0, - type=8, - value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, - }}, fname_tmp) - end) + it('event FileAppendCmd', function() + reset() + nvim('set_var', 'shada#add_own_header', 0) + curbuf('set_lines', 0, 1, true, { + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }) + funcs.writefile({''}, fname .. '.tst', 'b') + funcs.writefile({''}, fname, 'b') + funcs.writefile({''}, fname_tmp, 'b') + nvim_command('1,3w >> ' .. fname .. '.tst') + nvim_command('1,3w >> ' .. fname) + nvim_command('1,3w >> ' .. fname_tmp) + nvim_command('w >> ' .. fname .. '.tst') + nvim_command('w >> ' .. fname) + nvim_command('w >> ' .. fname_tmp) + eq('++opt not supported', exc_exec('1,3w! ++enc=latin1 >> ' .. fname)) + eq(table.concat({ + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + 'Jump with timestamp ' .. epoch .. ':', + ' % Key________ Description Value', + ' + n name \'A\'', + ' + f file name ["foo"]', + ' + l line number 2', + ' + c column -200', + }, eol) .. eol, read_file(fname .. '.tst')) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname) + shada_eq({{ + timestamp=0, + type=8, + value={n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }, { + timestamp=0, + type=8, + value={c=-200, f={'foo'}, l=2, n=('A'):byte()}, + }}, fname_tmp) end) - describe('event SourceCmd', function() - before_each(function() - reset(fname) - end) - it('works', function() - wshada('\004\000\006\146\000\196\002ab') - wshada_tmp('\004\001\006\146\000\196\002bc') - eq(0, exc_exec('source ' .. fname)) - eq(0, exc_exec('source ' .. fname_tmp)) - eq('bc', funcs.histget(':', -1)) - eq('ab', funcs.histget(':', -2)) - end) + it('event SourceCmd', function() + reset(fname) + wshada('\004\000\006\146\000\196\002ab') + wshada_tmp('\004\001\006\146\000\196\002bc') + eq(0, exc_exec('source ' .. fname)) + eq(0, exc_exec('source ' .. fname_tmp)) + eq('bc', funcs.histget(':', -1)) + eq('ab', funcs.histget(':', -2)) end) end) diff --git a/test/functional/terminal/scrollback_spec.lua b/test/functional/terminal/scrollback_spec.lua index 065cb98e69..f4441a5396 100644 --- a/test/functional/terminal/scrollback_spec.lua +++ b/test/functional/terminal/scrollback_spec.lua @@ -403,12 +403,8 @@ describe("'scrollback' option", function() end curbufmeths.set_option('scrollback', 0) - if iswin() then - feed_data('for /L %I in (1,1,30) do @(echo line%I)\r') - else - feed_data('awk "BEGIN{for(n=1;n<=30;n++) print \\\"line\\\" n}"\n') - end - screen:expect{any='line30 '} + feed_data(nvim_dir..'/shell-test REP 31 line'..(iswin() and '\r' or '\n')) + screen:expect{any='30: line '} retry(nil, nil, function() expect_lines(7) end) screen:detach() @@ -428,13 +424,8 @@ describe("'scrollback' option", function() -- Wait for prompt. screen:expect{any='%$'} - if iswin() then - feed_data('for /L %I in (1,1,30) do @(echo line%I)\r') - else - feed_data('awk "BEGIN{for(n=1;n<=30;n++) print \\\"line\\\" n}"\n') - end - - screen:expect{any='line30 '} + feed_data(nvim_dir.."/shell-test REP 31 line"..(iswin() and '\r' or '\n')) + screen:expect{any='30: line '} retry(nil, nil, function() expect_lines(33, 2) end) curbufmeths.set_option('scrollback', 10) @@ -445,18 +436,14 @@ describe("'scrollback' option", function() -- Terminal job data is received asynchronously, may happen before the -- 'scrollback' option is synchronized with the internal sb_buffer. command('sleep 100m') - if iswin() then - feed_data('for /L %I in (1,1,40) do @(echo line%I)\r') - else - feed_data('awk "BEGIN{for(n=1;n<=40;n++) print \\\"line\\\" n}"\n') - end - screen:expect{any='line40 '} + feed_data(nvim_dir.."/shell-test REP 41 line"..(iswin() and '\r' or '\n')) + screen:expect{any='40: line '} retry(nil, nil, function() expect_lines(58) end) -- Verify off-screen state - eq((iswin() and 'line36' or 'line35'), eval("getline(line('w0') - 1)")) - eq((iswin() and 'line27' or 'line26'), eval("getline(line('w0') - 10)")) + eq((iswin() and '36: line' or '35: line'), eval("getline(line('w0') - 1)")) + eq((iswin() and '27: line' or '26: line'), eval("getline(line('w0') - 10)")) screen:detach() end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index 958c56334a..c8cf9d830e 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -963,6 +963,335 @@ describe('floating windows', function() end end) + it('can be placed relative text in a window', function() + screen:try_resize(30,5) + local firstwin = meths.get_current_win().id + meths.buf_set_lines(0, 0, -1, true, {'just some', 'example text that is wider than the window', '', '', 'more text'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:------------------------------]| + [2:------------------------------]| + [2:------------------------------]| + [2:------------------------------]| + [3:------------------------------]| + ## grid 2 + ^just some | + example text that is wider tha| + n the window | + | + ## grid 3 + | + ]]} + else + screen:expect{grid=[[ + ^just some | + example text that is wider tha| + n the window | + | + | + ]]} + end + + local buf = meths.create_buf(false,false) + meths.buf_set_lines(buf, 0, -1, true, {'some info!'}) + + local win = meths.open_win(buf, false, {relative='win', width=12, height=1, bufpos={1,32}}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:------------------------------]| + [2:------------------------------]| + [2:------------------------------]| + [2:------------------------------]| + [3:------------------------------]| + ## grid 2 + ^just some | + example text that is wider tha| + n the window | + | + ## grid 3 + | + ## grid 5 + {1:some info! }| + ]], float_pos={ + [5] = { { + id = 1002 + }, "NW", 2, 3, 2, true } + }} + else + screen:expect{grid=[[ + ^just some | + example text that is wider tha| + n the window | + {1:some info! } | + | + ]]} + end + eq({relative='win', width=12, height=1, bufpos={1,32}, anchor='NW', + external=false, col=0, row=1, win=firstwin, focusable=true}, meths.win_get_config(win)) + + feed('<c-e>') + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:------------------------------]| + [2:------------------------------]| + [2:------------------------------]| + [2:------------------------------]| + [3:------------------------------]| + ## grid 2 + ^example text that is wider tha| + n the window | + | + | + ## grid 3 + | + ## grid 5 + {1:some info! }| + ]], float_pos={ + [5] = { { + id = 1002 + }, "NW", 2, 2, 2, true } + }} + else + screen:expect{grid=[[ + ^example text that is wider tha| + n the window | + {1:some info! } | + | + | + ]]} + end + + + screen:try_resize(45,5) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [2:---------------------------------------------]| + [3:---------------------------------------------]| + ## grid 2 + ^example text that is wider than the window | + | + | + more text | + ## grid 3 + | + ## grid 5 + {1:some info! }| + ]], float_pos={ + [5] = { { + id = 1002 + }, "NW", 2, 1, 32, true } + }} + else + -- note: appears misalinged due to cursor + screen:expect{grid=[[ + ^example text that is wider than the window | + {1:some info! } | + | + more text | + | + ]]} + end + + screen:try_resize(25,10) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [3:-------------------------]| + ## grid 2 + ^example text that is wide| + r than the window | + | + | + more text | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {1:some info! }| + ]], float_pos={ + [5] = { { + id = 1002 + }, "NW", 2, 2, 7, true } + }} + else + screen:expect{grid=[[ + ^example text that is wide| + r than the window | + {1:some info! } | + | + more text | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + end + + meths.win_set_config(win, {relative='win', bufpos={1,32}, anchor='SW'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [3:-------------------------]| + ## grid 2 + ^example text that is wide| + r than the window | + | + | + more text | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {1:some info! }| + ]], float_pos={ + [5] = { { + id = 1002 + }, "SW", 2, 1, 7, true } + }} + else + screen:expect{grid=[[ + ^example{1:some info! }s wide| + r than the window | + | + | + more text | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + end + + meths.win_set_config(win, {relative='win', bufpos={1,32}, anchor='NW', col=-2}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [3:-------------------------]| + ## grid 2 + ^example text that is wide| + r than the window | + | + | + more text | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {1:some info! }| + ]], float_pos={ + [5] = { { + id = 1002 + }, "NW", 2, 2, 5, true } + }} + else + screen:expect{grid=[[ + ^example text that is wide| + r than the window | + {1:some info! } | + | + more text | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + end + + meths.win_set_config(win, {relative='win', bufpos={1,32}, row=2}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [2:-------------------------]| + [3:-------------------------]| + ## grid 2 + ^example text that is wide| + r than the window | + | + | + more text | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 5 + {1:some info! }| + ]], float_pos={ + [5] = { { + id = 1002 + }, "NW", 2, 3, 7, true } + }} + else + screen:expect{grid=[[ + ^example text that is wide| + r than the window | + | + {1:some info! } | + more text | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + | + ]]} + end + end) + it('validates cursor even when window is not entered', function() screen:try_resize(30,5) command("set nowrap") diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index ed65c4526f..a6b9ef9387 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -1052,3 +1052,37 @@ describe('ui/msg_puts_printf', function() os.execute('cmake -E remove_directory '..test_build_dir..'/share') end) end) + +describe('pager', function() + local screen + + before_each(function() + clear() + screen = Screen.new(25, 5) + screen:attach() + screen:set_default_attr_ids({ + [1] = {bold = true, foreground = Screen.colors.Blue1}, + [4] = {bold = true, foreground = Screen.colors.SeaGreen4}, + }) + end) + + it('can be quit', function() + command("set more") + feed(':echon join(map(range(0, &lines*2), "v:val"), "\\n")<cr>') + screen:expect{grid=[[ + 0 | + 1 | + 2 | + 3 | + {4:-- More --}^ | + ]]} + feed('q') + screen:expect{grid=[[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) +end) diff --git a/test/functional/ui/mode_spec.lua b/test/functional/ui/mode_spec.lua index a09df075aa..200f6eecdb 100644 --- a/test/functional/ui/mode_spec.lua +++ b/test/functional/ui/mode_spec.lua @@ -62,7 +62,7 @@ describe('ui mode_change event', function() ]], mode="normal"} command("set showmatch") - command("set matchtime=1") -- tenths of seconds + command("set matchtime=2") -- tenths of seconds feed('a(stuff') screen:expect{grid=[[ word(stuff^ | diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua index 2215c0c7d9..f3fa711fb1 100644 --- a/test/functional/ui/wildmode_spec.lua +++ b/test/functional/ui/wildmode_spec.lua @@ -1,6 +1,5 @@ local helpers = require('test.functional.helpers')(after_each) local Screen = require('test.functional.ui.screen') -local shallowcopy = helpers.shallowcopy local clear, feed, command = helpers.clear, helpers.feed, helpers.command local iswin = helpers.iswin local funcs = helpers.funcs @@ -17,14 +16,6 @@ describe("'wildmenu'", function() screen:attach() end) - -- expect the screen stayed unchanged some time after first seen success - local function expect_stay_unchanged(args) - screen:expect(args) - args = shallowcopy(args) - args.unchanged = true - screen:expect(args) - end - it(':sign <tab> shows wildmenu completions', function() command('set wildmenu wildmode=full') feed(':sign <tab>') @@ -89,24 +80,20 @@ describe("'wildmenu'", function() feed([[:sign <Tab>]]) -- Invoke wildmenu. -- NB: in earlier versions terminal output was redrawn during cmdline mode. -- For now just assert that the screen remains unchanged. - expect_stay_unchanged{grid=[[ - | - | - | - define jump list > | - :sign define^ | - ]]} + screen:expect{any='define jump list > |\n:sign define^ |'} + screen:expect_unchanged() -- cmdline CTRL-D display should also be preserved. feed([[<C-U>]]) feed([[sign <C-D>]]) -- Invoke cmdline CTRL-D. - expect_stay_unchanged{grid=[[ + screen:expect{grid=[[ :sign | define place | jump undefine | list unplace | :sign ^ | ]]} + screen:expect_unchanged() -- Exiting cmdline should show the buffer. feed([[<C-\><C-N>]]) @@ -118,13 +105,14 @@ describe("'wildmenu'", function() command([[call timer_start(10, {->execute('redrawstatus')}, {'repeat':-1})]]) feed([[<C-\><C-N>]]) feed([[:sign <Tab>]]) -- Invoke wildmenu. - expect_stay_unchanged{grid=[[ + screen:expect{grid=[[ | ~ | ~ | define jump list > | :sign define^ | ]]} + screen:expect_unchanged() end) it('with laststatus=0, :vsplit, :term #2255', function() @@ -152,7 +140,8 @@ describe("'wildmenu'", function() feed([[:<Tab>]]) -- Invoke wildmenu. -- Check only the last 2 lines, because the shell output is -- system-dependent. - expect_stay_unchanged{any='! # & < = > @ > |\n:!^'} + screen:expect{any='! # & < = > @ > |\n:!^'} + screen:expect_unchanged() end) it('wildmode=list,full and display+=msgsep interaction #10092', function() diff --git a/test/helpers.lua b/test/helpers.lua index 779d33e3b1..c2a708197f 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -190,6 +190,15 @@ module.uname = (function() end) end)() +function module.is_os(s) + if not (s == 'win' or s == 'mac' or s == 'unix') then + error('unknown platform: '..tostring(s)) + end + return ((s == 'win' and module.iswin()) + or (s == 'mac' and module.uname() == 'darwin') + or (s == 'unix')) +end + local function tmpdir_get() return os.getenv('TMPDIR') and os.getenv('TMPDIR') or os.getenv('TEMP') end |