diff options
-rw-r--r-- | runtime/doc/eval.txt | 38 | ||||
-rw-r--r-- | runtime/doc/usr_11.txt | 7 | ||||
-rw-r--r-- | runtime/syntax/tutor.vim | 36 | ||||
-rw-r--r-- | src/nvim/eval.c | 53 | ||||
-rw-r--r-- | src/nvim/eval.lua | 3 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 28 | ||||
-rw-r--r-- | src/nvim/ex_cmds.c | 34 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 7 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 6 | ||||
-rw-r--r-- | src/nvim/file_search.c | 9 | ||||
-rw-r--r-- | src/nvim/fileio.c | 123 | ||||
-rw-r--r-- | src/nvim/main.c | 7 | ||||
-rw-r--r-- | src/nvim/memline.c | 268 | ||||
-rw-r--r-- | src/nvim/message.c | 10 | ||||
-rw-r--r-- | src/nvim/misc1.c | 8 | ||||
-rw-r--r-- | src/nvim/ops.c | 12 | ||||
-rw-r--r-- | src/nvim/os/process.c | 6 | ||||
-rw-r--r-- | src/nvim/popupmnu.c | 6 | ||||
-rw-r--r-- | src/nvim/spell.c | 15 | ||||
-rw-r--r-- | src/nvim/spellfile.c | 8 | ||||
-rw-r--r-- | src/nvim/testdir/test_swap.vim | 163 | ||||
-rw-r--r-- | src/nvim/testdir/test_undo.vim | 1 | ||||
-rw-r--r-- | src/nvim/tui/tui.c | 8 | ||||
-rw-r--r-- | src/nvim/undo.c | 18 | ||||
-rw-r--r-- | test/functional/clipboard/clipboard_provider_spec.lua | 18 |
25 files changed, 629 insertions, 263 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 9600a0d356..b05c806ec3 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2115,8 +2115,8 @@ gettabwinvar({tabnr}, {winnr}, {name} [, {def}]) gettagstack([{nr}]) Dict get the tag stack of window {nr} getwininfo([{winid}]) List list of windows getwinpos([{timeout}]) List X and Y coord in pixels of the Vim window -getwinposx() Number X coord in pixels of GUI Vim window -getwinposy() Number Y coord in pixels of GUI Vim window +getwinposx() Number X coord in pixels of Vim window +getwinposy() Number Y coord in pixels of Vim window getwinvar({nr}, {varname} [, {def}]) any variable {varname} in window {nr} glob({expr} [, {nosuf} [, {list} [, {alllinks}]]]) @@ -2324,6 +2324,8 @@ submatch({nr} [, {list}]) String or List specific match in ":s" or substitute() substitute({expr}, {pat}, {sub}, {flags}) String all {pat} in {expr} replaced with {sub} +swapinfo({fname}) Dict information about swap file {fname} +swapname({expr}) String swap file of buffer {expr} synID({lnum}, {col}, {trans}) Number syntax ID at {lnum} and {col} synIDattr({synID}, {what} [, {mode}]) String attribute {what} of syntax ID {synID} @@ -4581,6 +4583,13 @@ gettagstack([{nr}]) *gettagstack()* See |tagstack| for more information about the tag stack. +getwinpos([{timeout}]) *getwinpos()* + The result is a list with two numbers, the result of + getwinposx() and getwinposy() combined: + [x-pos, y-pos] + {timeout} can be used to specify how long to wait in msec for + a response from the terminal. When omitted 100 msec is used. + *getwinposx()* getwinposx() The result is a Number, which is the X coordinate in pixels of the left hand side of the GUI Vim window. The result will be @@ -7814,6 +7823,31 @@ substitute({expr}, {pat}, {sub}, {flags}) *substitute()* |submatch()| returns. Example: > :echo substitute(s, '\(\x\x\)', {m -> '0x' . m[1]}, 'g') +swapinfo({fname}) swapinfo() + The result is a dictionary, which holds information about the + swapfile {fname}. The available fields are: + version VIM version + user user name + host host name + fname original file name + pid PID of the VIM process that created the swap + file + mtime last modification time in seconds + inode Optional: INODE number of the file + dirty 1 if file was modified, 0 if not + In case of failure an "error" item is added with the reason: + Cannot open file: file not found or in accessible + Cannot read file: cannot read first block + Not a swap file: does not contain correct block ID + Magic number mismatch: Info in first block is invalid + +swapname({expr}) *swapname()* + The result is the swap file path of the buffer {expr}. + For the use of {expr}, see |bufname()| above. + If buffer {expr} is the current buffer, the result is equal to + |:swapname| (unless no swap file). + If buffer {expr} has no swap file, returns an empty string. + synID({lnum}, {col}, {trans}) *synID()* The result is a Number, which is the syntax ID at the position {lnum} and {col} in the current window. diff --git a/runtime/doc/usr_11.txt b/runtime/doc/usr_11.txt index e5591ac1d1..42b564e962 100644 --- a/runtime/doc/usr_11.txt +++ b/runtime/doc/usr_11.txt @@ -205,6 +205,13 @@ something wrong. It may be one of these two situations. NEWER than swap file! ~ +NOTE that in the following situation Vim knows the swap file is not useful and +will automatically delete it: +- The file is a valid swap file (Magic number is correct). +- The flag that the file was modified is not set. +- The process is not running. + + UNREADABLE SWAP FILE Sometimes the line diff --git a/runtime/syntax/tutor.vim b/runtime/syntax/tutor.vim index cb101ee9a7..6305eef734 100644 --- a/runtime/syntax/tutor.vim +++ b/runtime/syntax/tutor.vim @@ -49,29 +49,29 @@ syn match tutorInlineType /{\(normal\|vim\)}/ contained conceal syn match tutorInlineOK /✓/ syn match tutorInlineX /✗/ -hi! tutorLink cterm=underline gui=underline ctermfg=lightblue guifg=#0088ff -hi! link tutorLinkBands Delimiter -hi! link tutorLinkAnchor Underlined -hi! link tutorInternalAnchor Underlined -hi! link tutorURL tutorLink -hi! link tutorEmail tutorLink +hi def tutorLink cterm=underline gui=underline ctermfg=lightblue guifg=#0088ff +hi def link tutorLinkBands Delimiter +hi def link tutorLinkAnchor Underlined +hi def link tutorInternalAnchor Underlined +hi def link tutorURL tutorLink +hi def link tutorEmail tutorLink -hi! link tutorSection Title -hi! link tutorSectionBullet Delimiter +hi def link tutorSection Title +hi def link tutorSectionBullet Delimiter -hi! link tutorTOC Directory +hi def link tutorTOC Directory -hi! tutorMarks cterm=bold gui=bold +hi def tutorMarks cterm=bold gui=bold -hi! tutorEmphasis gui=italic cterm=italic -hi! tutorBold gui=bold cterm=bold +hi def tutorEmphasis gui=italic cterm=italic +hi def tutorBold gui=bold cterm=bold -hi! link tutorExpect Special -hi! tutorOK ctermfg=green guifg=#00ff88 cterm=bold gui=bold -hi! tutorX ctermfg=red guifg=#ff2000 cterm=bold gui=bold -hi! link tutorInlineOK tutorOK -hi! link tutorInlineX tutorX +hi def link tutorExpect Special +hi def tutorOK ctermfg=green guifg=#00ff88 cterm=bold gui=bold +hi def tutorX ctermfg=red guifg=#ff2000 cterm=bold gui=bold +hi def link tutorInlineOK tutorOK +hi def link tutorInlineX tutorX -hi! link tutorShellPrompt Delimiter +hi def link tutorShellPrompt Delimiter let b:current_syntax = "tutor" diff --git a/src/nvim/eval.c b/src/nvim/eval.c index bfe9b2dbdd..b7ae0d4dc1 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5221,7 +5221,7 @@ bool garbage_collect(bool testing) (void)garbage_collect(testing); } } else if (p_verbose > 0) { - verb_msg((char_u *)_( + verb_msg(_( "Not enough memory to set references, garbage collection aborted!")); } #undef ABORTING @@ -10491,6 +10491,14 @@ static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1); } +// "getwinpos({timeout})" function +static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_list_alloc_ret(rettv, 2); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); +} + /* * "getwinposx()" function */ @@ -11483,25 +11491,21 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static garray_T ga_userinput = {0, 0, sizeof(tasave_T), 4, NULL}; -/* - * "inputrestore()" function - */ +/// "inputrestore()" function static void f_inputrestore(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (!GA_EMPTY(&ga_userinput)) { - --ga_userinput.ga_len; + ga_userinput.ga_len--; restore_typeahead((tasave_T *)(ga_userinput.ga_data) - + ga_userinput.ga_len); - /* default return is zero == OK */ + + ga_userinput.ga_len); + // default return is zero == OK } else if (p_verbose > 1) { - verb_msg((char_u *)_("called inputrestore() more often than inputsave()")); - rettv->vval.v_number = 1; /* Failed */ + verb_msg(_("called inputrestore() more often than inputsave()")); + rettv->vval.v_number = 1; // Failed } } -/* - * "inputsave()" function - */ +/// "inputsave()" function static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr) { // Add an entry to the stack of typeahead storage. @@ -11509,9 +11513,7 @@ static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr) save_typeahead(p); } -/* - * "inputsecret()" function - */ +/// "inputsecret()" function static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr) { cmdline_star++; @@ -16486,6 +16488,27 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +/// "swapinfo(swap_filename)" function +static void f_swapinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_alloc_ret(rettv); + get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict); +} + +/// "swapname(expr)" function +static void f_swapname(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->v_type = VAR_STRING; + buf_T *buf = tv_get_buf(&argvars[0], false); + if (buf == NULL + || buf->b_ml.ml_mfp == NULL + || buf->b_ml.ml_mfp->mf_fname == NULL) { + rettv->vval.v_string = NULL; + } else { + rettv->vval.v_string = vim_strsave(buf->b_ml.ml_mfp->mf_fname); + } +} + /// "synID(lnum, col, trans)" function static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 5e0e2c038a..a7f8461fc3 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -144,6 +144,7 @@ return { gettabwinvar={args={3, 4}}, gettagstack={args={0, 1}}, getwininfo={args={0, 1}}, + getwinpos={args={0, 1}}, getwinposx={}, getwinposy={}, getwinvar={args={2, 3}}, @@ -302,6 +303,8 @@ return { strwidth={args=1}, submatch={args={1, 2}}, substitute={args=4}, + swapinfo={args={1}}, + swapname={args={1}}, synID={args=3}, synIDattr={args={2, 3}}, synIDtrans={args=1}, diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 912aecafec..ffb46abfea 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1656,12 +1656,7 @@ int tv_dict_add_special(dict_T *const d, const char *const key, /// Add a string entry to dictionary /// -/// @param[out] d Dictionary to add entry to. -/// @param[in] key Key to add. -/// @param[in] key_len Key length. -/// @param[in] val String to add. -/// -/// @return OK in case of success, FAIL when key already exists. +/// @see tv_dict_add_allocated_str int tv_dict_add_str(dict_T *const d, const char *const key, const size_t key_len, const char *const val) @@ -1672,6 +1667,27 @@ int tv_dict_add_str(dict_T *const d, /// Add a string entry to dictionary /// +/// @param[out] d Dictionary to add entry to. +/// @param[in] key Key to add. +/// @param[in] key_len Key length. +/// @param[in] val String to add. NULL adds empty string. +/// @param[in] len Use this many bytes from `val`, or -1 for whole string. +/// +/// @return OK in case of success, FAIL when key already exists. +int tv_dict_add_str_len(dict_T *const d, + const char *const key, const size_t key_len, + char *const val, int len) + FUNC_ATTR_NONNULL_ARG(1, 2) +{ + char *s = val ? val : ""; + if (val != NULL) { + s = (len < 0) ? xstrdup(val) : xstrndup(val, (size_t)len); + } + return tv_dict_add_allocated_str(d, key, key_len, s); +} + +/// Add a string entry to dictionary +/// /// Unlike tv_dict_add_str() saves val to the new dictionary item in place of /// creating a new copy. /// diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 150e13efd5..d20b6f5f58 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -855,7 +855,7 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) return FAIL; for (extra = 0, l = line1; l <= line2; l++) { str = vim_strsave(ml_get(l + extra)); - ml_append(dest + l - line1, str, (colnr_T)0, FALSE); + ml_append(dest + l - line1, str, (colnr_T)0, false); xfree(str); if (dest < line1) extra++; @@ -914,9 +914,9 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) if (u_save(line1 + extra - 1, line2 + extra + 1) == FAIL) return FAIL; - for (l = line1; l <= line2; l++) - ml_delete(line1 + extra, TRUE); - + for (l = line1; l <= line2; l++) { + ml_delete(line1 + extra, true); + } if (!global_busy && num_lines > p_report) { if (num_lines == 1) MSG(_("1 line moved")); @@ -982,7 +982,7 @@ void ex_copy(linenr_T line1, linenr_T line2, linenr_T n) /* need to use vim_strsave() because the line will be unlocked within * ml_append() */ p = vim_strsave(ml_get(line1)); - ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, FALSE); + ml_append(curwin->w_cursor.lnum, p, (colnr_T)0, false); xfree(p); /* situation 2: skip already copied lines */ @@ -2720,7 +2720,7 @@ static int append_indent = 0; /* autoindent for first line */ void ex_append(exarg_T *eap) { char_u *theline; - int did_undo = FALSE; + bool did_undo = false; linenr_T lnum = eap->line2; int indent = 0; char_u *p; @@ -2808,16 +2808,16 @@ void ex_append(exarg_T *eap) if (p[0] == NUL) theline[0] = NUL; - did_undo = TRUE; - ml_append(lnum, theline, (colnr_T)0, FALSE); + did_undo = true; + ml_append(lnum, theline, (colnr_T)0, false); appended_lines_mark(lnum + (empty ? 1 : 0), 1L); xfree(theline); ++lnum; if (empty) { - ml_delete(2L, FALSE); - empty = FALSE; + ml_delete(2L, false); + empty = 0; } } State = NORMAL; @@ -2862,7 +2862,7 @@ void ex_change(exarg_T *eap) for (lnum = eap->line2; lnum >= eap->line1; --lnum) { if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */ break; - ml_delete(eap->line1, FALSE); + ml_delete(eap->line1, false); } /* make sure the cursor is not beyond the end of the file now */ @@ -3989,8 +3989,9 @@ skip: ++lnum; if (u_savedel(lnum, nmatch_tl) != OK) break; - for (i = 0; i < nmatch_tl; ++i) - ml_delete(lnum, (int)FALSE); + for (i = 0; i < nmatch_tl; i++) { + ml_delete(lnum, false); + } mark_adjust(lnum, lnum + nmatch_tl - 1, (long)MAXLNUM, -nmatch_tl, false); if (subflags.do_ask) { @@ -5154,10 +5155,11 @@ void fix_help_buffer(void) } convert_setup(&vc, NULL, NULL); - ml_append(lnum, cp, (colnr_T)0, FALSE); - if (cp != IObuff) + ml_append(lnum, cp, (colnr_T)0, false); + if (cp != IObuff) { xfree(cp); - ++lnum; + } + lnum++; } fclose(fd); } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 9fc047dde9..a028814f3d 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -7213,10 +7213,11 @@ static void ex_read(exarg_T *eap) else lnum = 1; if (*ml_get(lnum) == NUL && u_savedel(lnum, 1L) == OK) { - ml_delete(lnum, FALSE); + ml_delete(lnum, false); if (curwin->w_cursor.lnum > 1 - && curwin->w_cursor.lnum >= lnum) - --curwin->w_cursor.lnum; + && curwin->w_cursor.lnum >= lnum) { + curwin->w_cursor.lnum--; + } deleted_lines_mark(lnum, 1L); } } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index f3ae3a5e5d..ece2b59bc6 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -6110,9 +6110,9 @@ static int open_cmdwin(void) do { if (++i == hislen) i = 0; - if (history[histtype][i].hisstr != NULL) - ml_append(lnum++, history[histtype][i].hisstr, - (colnr_T)0, FALSE); + if (history[histtype][i].hisstr != NULL) { + ml_append(lnum++, history[histtype][i].hisstr, (colnr_T)0, false); + } } while (i != hisidx[histtype]); } } diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index e358f0218e..05611fb8ba 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -687,20 +687,24 @@ char_u *vim_findfile(void *search_ctx_arg) if (!vim_isAbsName(stackp->ffs_fix_path) && search_ctx->ffsc_start_dir) { if (STRLEN(search_ctx->ffsc_start_dir) + 1 >= MAXPATHL) { + ff_free_stack_element(stackp); goto fail; } STRCPY(file_path, search_ctx->ffsc_start_dir); if (!add_pathsep((char *)file_path)) { + ff_free_stack_element(stackp); goto fail; } } // append the fix part of the search path if (STRLEN(file_path) + STRLEN(stackp->ffs_fix_path) + 1 >= MAXPATHL) { + ff_free_stack_element(stackp); goto fail; } STRCAT(file_path, stackp->ffs_fix_path); if (!add_pathsep((char *)file_path)) { + ff_free_stack_element(stackp); goto fail; } @@ -715,6 +719,7 @@ char_u *vim_findfile(void *search_ctx_arg) if (*p > 0) { (*p)--; if (len + 1 >= MAXPATHL) { + ff_free_stack_element(stackp); goto fail; } file_path[len++] = '*'; @@ -743,6 +748,7 @@ char_u *vim_findfile(void *search_ctx_arg) while (*rest_of_wildcards && !vim_ispathsep(*rest_of_wildcards)) { if (len + 1 >= MAXPATHL) { + ff_free_stack_element(stackp); goto fail; } file_path[len++] = *rest_of_wildcards++; @@ -792,10 +798,12 @@ char_u *vim_findfile(void *search_ctx_arg) // prepare the filename to be checked for existence below if (STRLEN(stackp->ffs_filearray[i]) + 1 + STRLEN(search_ctx->ffsc_file_to_search) >= MAXPATHL) { + ff_free_stack_element(stackp); goto fail; } STRCPY(file_path, stackp->ffs_filearray[i]); if (!add_pathsep((char *)file_path)) { + ff_free_stack_element(stackp); goto fail; } STRCAT(file_path, search_ctx->ffsc_file_to_search); @@ -964,7 +972,6 @@ char_u *vim_findfile(void *search_ctx_arg) } fail: - ff_free_stack_element(stackp); xfree(file_path); return NULL; } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index b2840c9402..77ddbb1eda 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -308,29 +308,30 @@ readfile ( #ifdef UNIX int swap_mode = -1; /* protection bits for swap file */ #endif - int fileformat = 0; /* end-of-line format */ - int keep_fileformat = FALSE; + int fileformat = 0; // end-of-line format + bool keep_fileformat = false; + FileInfo file_info; int file_readonly; linenr_T skip_count = 0; linenr_T read_count = 0; int msg_save = msg_scroll; linenr_T read_no_eol_lnum = 0; // non-zero lnum when last line of // last read was missing the eol - int file_rewind = false; + bool file_rewind = false; int can_retry; - linenr_T conv_error = 0; /* line nr with conversion error */ - linenr_T illegal_byte = 0; /* line nr with illegal byte */ - int keep_dest_enc = FALSE; /* don't retry when char doesn't fit - in destination encoding */ + linenr_T conv_error = 0; // line nr with conversion error + linenr_T illegal_byte = 0; // line nr with illegal byte + bool keep_dest_enc = false; // don't retry when char doesn't fit + // in destination encoding int bad_char_behavior = BAD_REPLACE; /* BAD_KEEP, BAD_DROP or character to * replace with */ char_u *tmpname = NULL; /* name of 'charconvert' output file */ int fio_flags = 0; - char_u *fenc; /* fileencoding to use */ - int fenc_alloced; /* fenc_next is in allocated memory */ - char_u *fenc_next = NULL; /* next item in 'fencs' or NULL */ - int advance_fenc = FALSE; + char_u *fenc; // fileencoding to use + bool fenc_alloced; // fenc_next is in allocated memory + char_u *fenc_next = NULL; // next item in 'fencs' or NULL + bool advance_fenc = false; long real_size = 0; # ifdef USE_ICONV iconv_t iconv_fd = (iconv_t)-1; /* descriptor for iconv() or -1 */ @@ -481,7 +482,6 @@ readfile ( if (newfile && !read_stdin && !read_buffer && !read_fifo) { // Remember time of file. - FileInfo file_info; if (os_fileinfo((char *)fname, &file_info)) { buf_store_file_info(curbuf, &file_info); curbuf->b_mtime_read = curbuf->b_mtime; @@ -627,13 +627,30 @@ readfile ( // Set swap file protection bits after creating it. if (swap_mode > 0 && curbuf->b_ml.ml_mfp != NULL && curbuf->b_ml.ml_mfp->mf_fname != NULL) { - (void)os_setperm((const char *)curbuf->b_ml.ml_mfp->mf_fname, - (long)swap_mode); + const char *swap_fname = (const char *)curbuf->b_ml.ml_mfp->mf_fname; + + // If the group-read bit is set but not the world-read bit, then + // the group must be equal to the group of the original file. If + // we can't make that happen then reset the group-read bit. This + // avoids making the swap file readable to more users when the + // primary group of the user is too permissive. + if ((swap_mode & 044) == 040) { + FileInfo swap_info; + + if (os_fileinfo(swap_fname, &swap_info) + && file_info.stat.st_gid != swap_info.stat.st_gid + && os_fchown(curbuf->b_ml.ml_mfp->mf_fd, -1, file_info.stat.st_gid) + == -1) { + swap_mode &= 0600; + } + } + + (void)os_setperm(swap_fname, swap_mode); } #endif } - /* If "Quit" selected at ATTENTION dialog, don't load the file */ + // If "Quit" selected at ATTENTION dialog, don't load the file. if (swap_exists_action == SEA_QUIT) { if (!read_buffer && !read_stdin) close(fd); @@ -747,11 +764,11 @@ readfile ( */ if (eap != NULL && eap->force_enc != 0) { fenc = enc_canonize(eap->cmd + eap->force_enc); - fenc_alloced = TRUE; - keep_dest_enc = TRUE; + fenc_alloced = true; + keep_dest_enc = true; } else if (curbuf->b_p_bin) { - fenc = (char_u *)""; /* binary: don't convert */ - fenc_alloced = FALSE; + fenc = (char_u *)""; // binary: don't convert + fenc_alloced = false; } else if (curbuf->b_help) { // Help files are either utf-8 or latin1. Try utf-8 first, if this // fails it must be latin1. @@ -762,12 +779,12 @@ readfile ( fenc_alloced = false; } else if (*p_fencs == NUL) { - fenc = curbuf->b_p_fenc; /* use format from buffer */ - fenc_alloced = FALSE; + fenc = curbuf->b_p_fenc; // use format from buffer + fenc_alloced = false; } else { fenc_next = p_fencs; /* try items in 'fileencodings' */ fenc = next_fenc(&fenc_next); - fenc_alloced = TRUE; + fenc_alloced = true; } /* @@ -800,10 +817,11 @@ retry: error = true; goto failed; } - /* Delete the previously read lines. */ - while (lnum > from) - ml_delete(lnum--, FALSE); - file_rewind = FALSE; + // Delete the previously read lines. + while (lnum > from) { + ml_delete(lnum--, false); + } + file_rewind = false; if (set_options) { curbuf->b_p_bomb = FALSE; curbuf->b_start_bomb = FALSE; @@ -815,9 +833,9 @@ retry: * When retrying with another "fenc" and the first time "fileformat" * will be reset. */ - if (keep_fileformat) - keep_fileformat = FALSE; - else { + if (keep_fileformat) { + keep_fileformat = false; + } else { if (eap != NULL && eap->force_ff != 0) { fileformat = get_fileformat_force(curbuf, eap); try_unix = try_dos = try_mac = FALSE; @@ -841,7 +859,7 @@ retry: /* * Try the next entry in 'fileencodings'. */ - advance_fenc = FALSE; + advance_fenc = false; if (eap != NULL && eap->force_enc != 0) { /* Conversion given with "++cc=" wasn't possible, read @@ -851,7 +869,7 @@ retry: if (fenc_alloced) xfree(fenc); fenc = (char_u *)""; - fenc_alloced = FALSE; + fenc_alloced = false; } else { if (fenc_alloced) xfree(fenc); @@ -860,7 +878,7 @@ retry: fenc_alloced = (fenc_next != NULL); } else { fenc = (char_u *)""; - fenc_alloced = FALSE; + fenc_alloced = false; } } if (tmpname != NULL) { @@ -926,8 +944,8 @@ retry: if (tmpname == NULL) { tmpname = readfile_charconvert(fname, fenc, &fd); if (tmpname == NULL) { - /* Conversion failed. Try another one. */ - advance_fenc = TRUE; + // Conversion failed. Try another one. + advance_fenc = true; if (fd < 0) { /* Re-opening the original file failed! */ EMSG(_("E202: Conversion made file unreadable!")); @@ -945,7 +963,7 @@ retry: ) { /* Conversion wanted but we can't. * Try the next conversion in 'fileencodings' */ - advance_fenc = TRUE; + advance_fenc = true; goto retry; } } @@ -1180,14 +1198,14 @@ retry: if (fio_flags == FIO_UCSBOM) { if (ccname == NULL) { - /* No BOM detected: retry with next encoding. */ - advance_fenc = TRUE; + // No BOM detected: retry with next encoding. + advance_fenc = true; } else { /* BOM detected: set "fenc" and jump back */ if (fenc_alloced) xfree(fenc); fenc = ccname; - fenc_alloced = FALSE; + fenc_alloced = false; } /* retry reading without getting new bytes or rewinding */ skip_read = TRUE; @@ -1512,9 +1530,9 @@ rewind_retry: did_iconv = TRUE; else # endif - /* use next item from 'fileencodings' */ - advance_fenc = TRUE; - file_rewind = TRUE; + // use next item from 'fileencodings' + advance_fenc = true; + file_rewind = true; goto retry; } } @@ -1648,8 +1666,8 @@ rewind_retry: fileformat = EOL_UNIX; if (set_options) set_fileformat(EOL_UNIX, OPT_LOCAL); - file_rewind = TRUE; - keep_fileformat = TRUE; + file_rewind = true; + keep_fileformat = true; goto retry; } ff_error = EOL_DOS; @@ -1760,8 +1778,8 @@ failed: if (!recoverymode) { /* need to delete the last line, which comes from the empty buffer */ if (newfile && wasempty && !(curbuf->b_ml.ml_flags & ML_EMPTY)) { - ml_delete(curbuf->b_ml.ml_line_count, FALSE); - --linecnt; + ml_delete(curbuf->b_ml.ml_line_count, false); + linecnt--; } linecnt = curbuf->b_ml.ml_line_count - linecnt; if (filesize == 0) @@ -4894,9 +4912,9 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf) /* Copy the lines in "frombuf" to "tobuf". */ curbuf = tobuf; - for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; ++lnum) { - p = vim_strsave(ml_get_buf(frombuf, lnum, FALSE)); - if (ml_append(lnum - 1, p, 0, FALSE) == FAIL) { + for (lnum = 1; lnum <= frombuf->b_ml.ml_line_count; lnum++) { + p = vim_strsave(ml_get_buf(frombuf, lnum, false)); + if (ml_append(lnum - 1, p, 0, false) == FAIL) { xfree(p); retval = FAIL; break; @@ -4907,13 +4925,14 @@ static int move_lines(buf_T *frombuf, buf_T *tobuf) /* Delete all the lines in "frombuf". */ if (retval != FAIL) { curbuf = frombuf; - for (lnum = curbuf->b_ml.ml_line_count; lnum > 0; --lnum) - if (ml_delete(lnum, FALSE) == FAIL) { - /* Oops! We could try putting back the saved lines, but that - * might fail again... */ + for (lnum = curbuf->b_ml.ml_line_count; lnum > 0; lnum--) { + if (ml_delete(lnum, false) == FAIL) { + // Oops! We could try putting back the saved lines, but that + // might fail again... retval = FAIL; break; } + } } curbuf = tbuf; diff --git a/src/nvim/main.c b/src/nvim/main.c index 27e2abe1ad..4e1c7dff57 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1520,10 +1520,11 @@ static void create_windows(mparm_T *parmp) dorewind = FALSE; curbuf = curwin->w_buffer; if (curbuf->b_ml.ml_mfp == NULL) { - /* Set 'foldlevel' to 'foldlevelstart' if it's not negative. */ - if (p_fdls >= 0) + // Set 'foldlevel' to 'foldlevelstart' if it's not negative.. + if (p_fdls >= 0) { curwin->w_p_fdl = p_fdls; - /* When getting the ATTENTION prompt here, use a dialog */ + } + // When getting the ATTENTION prompt here, use a dialog. swap_exists_action = SEA_DIALOG; set_buflisted(TRUE); diff --git a/src/nvim/memline.c b/src/nvim/memline.c index 662eda3c7c..a071314453 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -71,6 +71,7 @@ #include "nvim/undo.h" #include "nvim/window.h" #include "nvim/os/os.h" +#include "nvim/os/process.h" #include "nvim/os/input.h" #ifndef UNIX /* it's in os/unix_defs.h for Unix */ @@ -348,7 +349,7 @@ int ml_open(buf_T *buf) /* * Allocate first data block and create an empty line 1. */ - hp = ml_new_data(mfp, FALSE, 1); + hp = ml_new_data(mfp, false, 1); if (hp->bh_bnum != 2) { IEMSG(_("E298: Didn't get block nr 2?")); goto error; @@ -763,7 +764,7 @@ void ml_recover(void) long error; int cannot_open; linenr_T line_count; - int has_error; + bool has_error; int idx; int top; int txt_start; @@ -983,8 +984,9 @@ void ml_recover(void) * Now that we are sure that the file is going to be recovered, clear the * contents of the current buffer. */ - while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) - ml_delete((linenr_T)1, FALSE); + while (!(curbuf->b_ml.ml_flags & ML_EMPTY)) { + ml_delete((linenr_T)1, false); + } /* * Try reading the original file to obtain the values of 'fileformat', @@ -1033,8 +1035,8 @@ void ml_recover(void) } ++error; ml_append(lnum++, (char_u *)_("???MANY LINES MISSING"), - (colnr_T)0, TRUE); - } else { /* there is a block */ + (colnr_T)0, true); + } else { // there is a block pp = hp->bh_data; if (pp->pb_id == PTR_ID) { /* it is a pointer block */ /* check line count when using pointer block first time */ @@ -1044,15 +1046,15 @@ void ml_recover(void) if (line_count != 0) { ++error; ml_append(lnum++, (char_u *)_("???LINE COUNT WRONG"), - (colnr_T)0, TRUE); + (colnr_T)0, true); } } if (pp->pb_count == 0) { ml_append(lnum++, (char_u *)_("???EMPTY BLOCK"), - (colnr_T)0, TRUE); - ++error; - } else if (idx < (int)pp->pb_count) { /* go a block deeper */ + (colnr_T)0, true); + error++; + } else if (idx < (int)pp->pb_count) { // go a block deeper if (pp->pb_pointer[idx].pe_bnum < 0) { /* * Data block with negative block number. @@ -1072,7 +1074,7 @@ void ml_recover(void) if (cannot_open) { ++error; ml_append(lnum++, (char_u *)_("???LINES MISSING"), - (colnr_T)0, TRUE); + (colnr_T)0, true); } ++idx; /* get same block again for next index */ continue; @@ -1102,23 +1104,21 @@ void ml_recover(void) } ++error; ml_append(lnum++, (char_u *)_("???BLOCK MISSING"), - (colnr_T)0, TRUE); + (colnr_T)0, true); } else { - /* - * it is a data block - * Append all the lines in this block - */ - has_error = FALSE; - /* - * check length of block - * if wrong, use length in pointer block - */ + // it is a data block + // Append all the lines in this block + has_error = false; + // check length of block + // if wrong, use length in pointer block if (page_count * mfp->mf_page_size != dp->db_txt_end) { - ml_append(lnum++, - (char_u *)_("??? from here until ???END lines may be messed up"), - (colnr_T)0, TRUE); - ++error; - has_error = TRUE; + ml_append( + lnum++, + (char_u *)_("??? from here until ???END lines" + " may be messed up"), + (colnr_T)0, true); + error++; + has_error = true; dp->db_txt_end = page_count * mfp->mf_page_size; } @@ -1130,12 +1130,13 @@ void ml_recover(void) * if wrong, use count in data block */ if (line_count != dp->db_line_count) { - ml_append(lnum++, - (char_u *)_( - "??? from here until ???END lines may have been inserted/deleted"), - (colnr_T)0, TRUE); - ++error; - has_error = TRUE; + ml_append( + lnum++, + (char_u *)_("??? from here until ???END lines" + " may have been inserted/deleted"), + (colnr_T)0, true); + error++; + has_error = true; } for (i = 0; i < dp->db_line_count; ++i) { @@ -1146,11 +1147,11 @@ void ml_recover(void) ++error; } else p = (char_u *)dp + txt_start; - ml_append(lnum++, p, (colnr_T)0, TRUE); + ml_append(lnum++, p, (colnr_T)0, true); + } + if (has_error) { + ml_append(lnum++, (char_u *)_("???END"), (colnr_T)0, true); } - if (has_error) - ml_append(lnum++, (char_u *)_("???END"), - (colnr_T)0, TRUE); } } } @@ -1201,7 +1202,7 @@ void ml_recover(void) */ while (curbuf->b_ml.ml_line_count > lnum && !(curbuf->b_ml.ml_flags & ML_EMPTY)) - ml_delete(curbuf->b_ml.ml_line_count, FALSE); + ml_delete(curbuf->b_ml.ml_line_count, false); curbuf->b_flags |= BF_RECOVERED; recoverymode = FALSE; @@ -1453,14 +1454,47 @@ static char *make_percent_swname(const char *dir, char *name) return d; } -#ifdef UNIX static bool process_still_running; -#endif -/* - * Give information about an existing swap file. - * Returns timestamp (0 when unknown). - */ +/// Return information found in swapfile "fname" in dictionary "d". +/// This is used by the swapinfo() function. +void get_b0_dict(const char *fname, dict_T *d) +{ + int fd; + struct block0 b0; + + if ((fd = os_open(fname, O_RDONLY, 0)) >= 0) { + if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) { + if (ml_check_b0_id(&b0) == FAIL) { + tv_dict_add_str(d, S_LEN("error"), "Not a swap file"); + } else if (b0_magic_wrong(&b0)) { + tv_dict_add_str(d, S_LEN("error"), "Magic number mismatch"); + } else { + // We have swap information. + tv_dict_add_str_len(d, S_LEN("version"), (char *)b0.b0_version, 10); + tv_dict_add_str_len(d, S_LEN("user"), (char *)b0.b0_uname, + B0_UNAME_SIZE); + tv_dict_add_str_len(d, S_LEN("host"), (char *)b0.b0_hname, + B0_HNAME_SIZE); + tv_dict_add_str_len(d, S_LEN("fname"), (char *)b0.b0_fname, + B0_FNAME_SIZE_ORG); + + tv_dict_add_nr(d, S_LEN("pid"), char_to_long(b0.b0_pid)); + tv_dict_add_nr(d, S_LEN("mtime"), char_to_long(b0.b0_mtime)); + tv_dict_add_nr(d, S_LEN("dirty"), b0.b0_dirty ? 1 : 0); + tv_dict_add_nr(d, S_LEN("inode"), char_to_long(b0.b0_ino)); + } + } else { + tv_dict_add_str(d, S_LEN("error"), "Cannot read file"); + } + close(fd); + } else { + tv_dict_add_str(d, S_LEN("error"), "Cannot open file"); + } +} + +/// Give information about an existing swap file. +/// Returns timestamp (0 when unknown). static time_t swapfile_info(char_u *fname) { assert(fname != NULL); @@ -1530,12 +1564,10 @@ static time_t swapfile_info(char_u *fname) if (char_to_long(b0.b0_pid) != 0L) { MSG_PUTS(_("\n process ID: ")); msg_outnum(char_to_long(b0.b0_pid)); -#if defined(UNIX) - if (kill((pid_t)char_to_long(b0.b0_pid), 0) == 0) { + if (os_proc_running((int)char_to_long(b0.b0_pid))) { MSG_PUTS(_(" (STILL RUNNING)")); process_still_running = true; } -#endif } if (b0_magic_wrong(&b0)) { @@ -1552,6 +1584,51 @@ static time_t swapfile_info(char_u *fname) return x; } +/// Returns TRUE if the swap file looks OK and there are no changes, thus it +/// can be safely deleted. +static time_t swapfile_unchanged(char *fname) +{ + struct block0 b0; + int ret = true; + + // Swap file must exist. + if (!os_path_exists((char_u *)fname)) { + return false; + } + + // must be able to read the first block + int fd = os_open(fname, O_RDONLY, 0); + if (fd < 0) { + return false; + } + if (read_eintr(fd, &b0, sizeof(b0)) != sizeof(b0)) { + close(fd); + return false; + } + + // the ID and magic number must be correct + if (ml_check_b0_id(&b0) == FAIL|| b0_magic_wrong(&b0)) { + ret = false; + } + + // must be unchanged + if (b0.b0_dirty) { + ret = false; + } + + // process must be known and not running. + long pid = char_to_long(b0.b0_pid); + if (pid == 0L || os_proc_running((int)pid)) { + ret = false; + } + + // TODO(bram): Should we check if the swap file was created on the current + // system? And the current user? + + close(fd); + return ret; +} + static int recov_file_names(char_u **names, char_u *path, int prepend_dot) FUNC_ATTR_NONNULL_ALL { @@ -1737,7 +1814,7 @@ char_u * ml_get_buf ( buf_T *buf, linenr_T lnum, - int will_change /* line will be changed */ + bool will_change // line will be changed ) { bhdr_T *hp; @@ -1823,12 +1900,11 @@ int ml_line_alloced(void) * * return FAIL for failure, OK otherwise */ -int -ml_append ( - linenr_T lnum, /* append after this line (can be 0) */ - char_u *line, /* text of the new line */ - colnr_T len, /* length of new line, including NUL, or 0 */ - int newfile /* flag, see above */ +int ml_append( + linenr_T lnum, // append after this line (can be 0) + char_u *line, // text of the new line + colnr_T len, // length of new line, including NUL, or 0 + bool newfile // flag, see above ) { /* When starting up, we might still need to create the memfile */ @@ -1844,13 +1920,12 @@ ml_append ( * Like ml_append() but for an arbitrary buffer. The buffer must already have * a memline. */ -int -ml_append_buf ( +int ml_append_buf( buf_T *buf, - linenr_T lnum, /* append after this line (can be 0) */ - char_u *line, /* text of the new line */ - colnr_T len, /* length of new line, including NUL, or 0 */ - int newfile /* flag, see above */ + linenr_T lnum, // append after this line (can be 0) + char_u *line, // text of the new line + colnr_T len, // length of new line, including NUL, or 0 + bool newfile // flag, see above ) { if (buf->b_ml.ml_mfp == NULL) @@ -1861,14 +1936,13 @@ ml_append_buf ( return ml_append_int(buf, lnum, line, len, newfile, FALSE); } -static int -ml_append_int ( +static int ml_append_int( buf_T *buf, - linenr_T lnum, /* append after this line (can be 0) */ - char_u *line, /* text of the new line */ - colnr_T len, /* length of line, including NUL, or 0 */ - int newfile, /* flag, see above */ - int mark /* mark the new line */ + linenr_T lnum, // append after this line (can be 0) + char_u *line, // text of the new line + colnr_T len, // length of line, including NUL, or 0 + bool newfile, // flag, see above + int mark // mark the new line ) { int i; @@ -2351,13 +2425,13 @@ int ml_replace(linenr_T lnum, char_u *line, bool copy) /// /// @param message Show "--No lines in buffer--" message. /// @return FAIL for failure, OK otherwise -int ml_delete(linenr_T lnum, int message) +int ml_delete(linenr_T lnum, bool message) { ml_flush_line(curbuf); return ml_delete_int(curbuf, lnum, message); } -static int ml_delete_int(buf_T *buf, linenr_T lnum, int message) +static int ml_delete_int(buf_T *buf, linenr_T lnum, bool message) { bhdr_T *hp; memfile_T *mfp; @@ -2677,17 +2751,15 @@ static void ml_flush_line(buf_T *buf) /* The else case is already covered by the insert and delete */ ml_updatechunk(buf, lnum, (long)extra, ML_CHNK_UPDLINE); } else { - /* - * Cannot do it in one data block: Delete and append. - * Append first, because ml_delete_int() cannot delete the - * last line in a buffer, which causes trouble for a buffer - * that has only one line. - * Don't forget to copy the mark! - */ - /* How about handling errors??? */ - (void)ml_append_int(buf, lnum, new_line, new_len, FALSE, - (dp->db_index[idx] & DB_MARKED)); - (void)ml_delete_int(buf, lnum, FALSE); + // Cannot do it in one data block: Delete and append. + // Append first, because ml_delete_int() cannot delete the + // last line in a buffer, which causes trouble for a buffer + // that has only one line. + // Don't forget to copy the mark! + // How about handling errors??? + (void)ml_append_int(buf, lnum, new_line, new_len, false, + (dp->db_index[idx] & DB_MARKED)); + (void)ml_delete_int(buf, lnum, false); } } xfree(new_line); @@ -2701,7 +2773,7 @@ static void ml_flush_line(buf_T *buf) /* * create a new, empty, data block */ -static bhdr_T *ml_new_data(memfile_T *mfp, int negative, int page_count) +static bhdr_T *ml_new_data(memfile_T *mfp, bool negative, int page_count) { assert(page_count >= 0); bhdr_T *hp = mf_new(mfp, negative, (unsigned)page_count); @@ -3353,17 +3425,24 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, && vim_strchr(p_shm, SHM_ATTENTION) == NULL) { int choice = 0; -#ifdef UNIX process_still_running = false; -#endif - /* - * If there is a SwapExists autocommand and we can handle - * the response, trigger it. It may return 0 to ask the - * user anyway. - */ - if (swap_exists_action != SEA_NONE - && has_autocmd(EVENT_SWAPEXISTS, (char_u *) buf_fname, buf)) - choice = do_swapexists(buf, (char_u *) fname); + // It's safe to delete the swap file if all these are true: + // - the edited file exists + // - the swap file has no changes and looks OK + if (os_path_exists(buf->b_fname) && swapfile_unchanged(fname)) { + choice = 4; + if (p_verbose > 0) { + verb_msg(_("Found a swap file that is not useful, deleting it")); + } + } + + // If there is a SwapExists autocommand and we can handle the + // response, trigger it. It may return 0 to ask the user anyway. + if (choice == 0 + && swap_exists_action != SEA_NONE + && has_autocmd(EVENT_SWAPEXISTS, (char_u *)buf_fname, buf)) { + choice = do_swapexists(buf, (char_u *)fname); + } if (choice == 0) { // Show info about the existing swap file. @@ -3395,21 +3474,18 @@ static char *findswapname(buf_T *buf, char **dirp, char *old_fname, xstrlcat(name, sw_msg_2, name_len); choice = do_dialog(VIM_WARNING, (char_u *)_("VIM - ATTENTION"), (char_u *)name, -# if defined(UNIX) process_still_running ? (char_u *)_( "&Open Read-Only\n&Edit anyway\n&Recover" "\n&Quit\n&Abort") : -# endif (char_u *)_( "&Open Read-Only\n&Edit anyway\n&Recover" "\n&Delete it\n&Quit\n&Abort"), 1, NULL, false); -# if defined(UNIX) - if (process_still_running && choice >= 4) - choice++; /* Skip missing "Delete it" button */ -# endif + if (process_still_running && choice >= 4) { + choice++; // Skip missing "Delete it" button. + } xfree(name); // pretend screen didn't scroll, need redraw anyway diff --git a/src/nvim/message.c b/src/nvim/message.c index f7c248184e..28855402f4 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -133,15 +133,11 @@ int msg(char_u *s) return msg_attr_keep(s, 0, false, false); } -/* - * Like msg() but keep it silent when 'verbosefile' is set. - */ -int verb_msg(char_u *s) +/// Like msg() but keep it silent when 'verbosefile' is set. +int verb_msg(char *s) { - int n; - verbose_enter(); - n = msg_attr_keep(s, 0, false, false); + int n = msg_attr_keep((char_u *)s, 0, false, false); verbose_leave(); return n; diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 57285fa252..61fe5e74eb 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -745,9 +745,9 @@ open_line ( if (dir == BACKWARD) --curwin->w_cursor.lnum; if (!(State & VREPLACE_FLAG) || old_cursor.lnum >= orig_line_count) { - if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_T)0, FALSE) - == FAIL) + if (ml_append(curwin->w_cursor.lnum, p_extra, (colnr_T)0, false) == FAIL) { goto theend; + } // Postpone calling changed_lines(), because it would mess up folding // with markers. // Skip mark_adjust when adding a line after the last one, there can't @@ -1747,8 +1747,8 @@ del_lines ( if (curbuf->b_ml.ml_flags & ML_EMPTY) /* nothing to delete */ break; - ml_delete(first, TRUE); - ++n; + ml_delete(first, true); + n++; /* If we delete the last line in the file, stop */ if (first > curbuf->b_ml.ml_line_count) diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 79a7271819..216bab4dda 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -3001,9 +3001,10 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) /* add a new line */ if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) { if (ml_append(curbuf->b_ml.ml_line_count, (char_u *)"", - (colnr_T)1, FALSE) == FAIL) + (colnr_T)1, false) == FAIL) { break; - ++nr_lines; + } + nr_lines++; } /* get the old line and advance to the position to insert at */ oldp = get_cursor_line_ptr(); @@ -3190,8 +3191,8 @@ void do_put(int regname, yankreg_T *reg, int dir, long count, int flags) newp = (char_u *) xmalloc((size_t)(STRLEN(ptr) + totlen + 1)); STRCPY(newp, y_array[y_size - 1]); STRCAT(newp, ptr); - /* insert second line */ - ml_append(lnum, newp, (colnr_T)0, FALSE); + // insert second line + ml_append(lnum, newp, (colnr_T)0, false); xfree(newp); oldp = ml_get(lnum); @@ -5616,6 +5617,9 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing) if (explicit_cb_reg) { target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER]; + if (writing && (cb_flags & (*name == '*' ? CB_UNNAMED : CB_UNNAMEDPLUS))) { + clipboard_needs_update = false; + } goto end; } else { // unnamed register: "implicit" clipboard if (writing && clipboard_delay_update) { diff --git a/src/nvim/os/process.c b/src/nvim/os/process.c index a67e7487eb..a1020be215 100644 --- a/src/nvim/os/process.c +++ b/src/nvim/os/process.c @@ -265,3 +265,9 @@ Dictionary os_proc_info(int pid) return pinfo; } #endif + +/// Return true if process `pid` is running. +bool os_proc_running(int pid) +{ + return uv_kill(pid, 0) == 0; +} diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index 8cf09b14d7..58a0008e04 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -672,7 +672,7 @@ static int pum_set_selected(int n, int repeat) && (curbuf->b_p_bh[0] == 'w')) { // Already a "wipeout" buffer, make it empty. while (!BUFEMPTY()) { - ml_delete((linenr_T)1, FALSE); + ml_delete((linenr_T)1, false); } } else { // Don't want to sync undo in the current buffer. @@ -697,11 +697,11 @@ static int pum_set_selected(int n, int repeat) for (p = pum_array[pum_selected].pum_info; *p != NUL;) { e = vim_strchr(p, '\n'); if (e == NULL) { - ml_append(lnum++, p, 0, FALSE); + ml_append(lnum++, p, 0, false); break; } else { *e = NUL; - ml_append(lnum++, p, (int)(e - p + 1), FALSE); + ml_append(lnum++, p, (int)(e - p + 1), false); *e = '\n'; p = e + 1; } diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 331d8da55a..0fc33bec81 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -7100,9 +7100,9 @@ void ex_spelldump(exarg_T *eap) spell_dump_compl(NULL, 0, NULL, eap->forceit ? DUMPFLAG_COUNT : 0); // Delete the empty line that we started with. - if (curbuf->b_ml.ml_line_count > 1) - ml_delete(curbuf->b_ml.ml_line_count, FALSE); - + if (curbuf->b_ml.ml_line_count > 1) { + ml_delete(curbuf->b_ml.ml_line_count, false); + } redraw_later(NOT_VALID); } @@ -7171,7 +7171,7 @@ spell_dump_compl ( if (do_region && region_names != NULL) { if (pat == NULL) { vim_snprintf((char *)IObuff, IOSIZE, "/regions=%s", region_names); - ml_append(lnum++, IObuff, (colnr_T)0, FALSE); + ml_append(lnum++, IObuff, (colnr_T)0, false); } } else do_region = false; @@ -7185,7 +7185,7 @@ spell_dump_compl ( if (pat == NULL) { vim_snprintf((char *)IObuff, IOSIZE, "# file: %s", slang->sl_fname); - ml_append(lnum++, IObuff, (colnr_T)0, FALSE); + ml_append(lnum++, IObuff, (colnr_T)0, false); } // When matching with a pattern and there are no prefixes only use @@ -7347,14 +7347,15 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, int *dir, int d } } - ml_append(lnum, p, (colnr_T)0, FALSE); + ml_append(lnum, p, (colnr_T)0, false); } else if (((dumpflags & DUMPFLAG_ICASE) ? mb_strnicmp(p, pat, STRLEN(pat)) == 0 : STRNCMP(p, pat, STRLEN(pat)) == 0) && ins_compl_add_infercase(p, (int)STRLEN(p), - p_ic, NULL, *dir, 0) == OK) + p_ic, NULL, *dir, 0) == OK) { // if dir was BACKWARD then honor it just once *dir = FORWARD; + } } // For ":spelldump": Find matching prefixes for "word". Prepend each to diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index ccad893d61..117939e7e9 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -955,8 +955,9 @@ someerror: break; } if (ml_append_buf(slang->sl_sugbuf, (linenr_T)wordnr, - ga.ga_data, ga.ga_len, TRUE) == FAIL) + ga.ga_data, ga.ga_len, true) == FAIL) { goto someerror; + } } ga_clear(&ga); @@ -4920,9 +4921,10 @@ sug_filltable ( ((char_u *)gap->ga_data)[gap->ga_len++] = NUL; if (ml_append_buf(spin->si_spellbuf, (linenr_T)wordnr, - gap->ga_data, gap->ga_len, TRUE) == FAIL) + gap->ga_data, gap->ga_len, true) == FAIL) { return -1; - ++wordnr; + } + wordnr++; // Remove extra NUL entries, we no longer need them. We don't // bother freeing the nodes, the won't be reused anyway. diff --git a/src/nvim/testdir/test_swap.vim b/src/nvim/testdir/test_swap.vim index bc7b7c00d3..3db438cf4b 100644 --- a/src/nvim/testdir/test_swap.vim +++ b/src/nvim/testdir/test_swap.vim @@ -1,5 +1,9 @@ " Tests for the swap feature +func s:swapname() + return trim(execute('swapname')) +endfunc + " Tests for 'directory' option. func Test_swap_directory() if !has("unix") @@ -17,7 +21,7 @@ func Test_swap_directory() " Verify that the swap file doesn't exist in the current directory call assert_equal([], glob(".Xtest1*.swp", 1, 1, 1)) edit Xtest1 - let swfname = split(execute("swapname"))[0] + let swfname = s:swapname() call assert_equal([swfname], glob(swfname, 1, 1, 1)) " './dir', swap file in a directory relative to the file @@ -27,7 +31,7 @@ func Test_swap_directory() edit Xtest1 call assert_equal([], glob(swfname, 1, 1, 1)) let swfname = "Xtest2/Xtest1.swp" - call assert_equal(swfname, split(execute("swapname"))[0]) + call assert_equal(swfname, s:swapname()) call assert_equal([swfname], glob("Xtest2/*", 1, 1, 1)) " 'dir', swap file in directory relative to the current dir @@ -38,7 +42,7 @@ func Test_swap_directory() edit Xtest2/Xtest3 call assert_equal(["Xtest2/Xtest3"], glob("Xtest2/*", 1, 1, 1)) let swfname = "Xtest.je/Xtest3.swp" - call assert_equal(swfname, split(execute("swapname"))[0]) + call assert_equal(swfname, s:swapname()) call assert_equal([swfname], glob("Xtest.je/*", 1, 1, 1)) set dir& @@ -47,6 +51,42 @@ func Test_swap_directory() call delete("Xtest.je", "rf") endfunc +func Test_swap_group() + if !has("unix") + return + endif + let groups = split(system('groups')) + if len(groups) <= 1 + throw 'Skipped: need at least two groups, got ' . string(groups) + endif + + try + call delete('Xtest') + split Xtest + call setline(1, 'just some text') + wq + if system('ls -l Xtest') !~ ' ' . groups[0] . ' \d' + throw 'Skipped: test file does not have the first group' + else + silent !chmod 640 Xtest + call system('chgrp ' . groups[1] . ' Xtest') + if system('ls -l Xtest') !~ ' ' . groups[1] . ' \d' + throw 'Skipped: cannot set second group on test file' + else + split Xtest + let swapname = substitute(execute('swapname'), '[[:space:]]', '', 'g') + call assert_match('Xtest', swapname) + " Group of swapfile must now match original file. + call assert_match(' ' . groups[1] . ' \d', system('ls -l ' . swapname)) + + bwipe! + endif + endif + finally + call delete('Xtest') + endtry +endfunc + func Test_missing_dir() call mkdir('Xswapdir') exe 'set directory=' . getcwd() . '/Xswapdir' @@ -61,3 +101,120 @@ func Test_missing_dir() set directory& call delete('Xswapdir', 'rf') endfunc + +func Test_swapinfo() + new Xswapinfo + call setline(1, ['one', 'two', 'three']) + w + let fname = s:swapname() + call assert_match('Xswapinfo', fname) + let info = swapinfo(fname) + + let ver = printf('VIM %d.%d', v:version / 100, v:version % 100) + call assert_equal(ver, info.version) + + call assert_match('\w', info.user) + " host name is truncated to 39 bytes in the swap file + call assert_equal(hostname()[:38], info.host) + call assert_match('Xswapinfo', info.fname) + call assert_match(0, info.dirty) + call assert_equal(getpid(), info.pid) + call assert_match('^\d*$', info.mtime) + if has_key(info, 'inode') + call assert_match('\d', info.inode) + endif + bwipe! + call delete(fname) + call delete('Xswapinfo') + + let info = swapinfo('doesnotexist') + call assert_equal('Cannot open file', info.error) + + call writefile(['burp'], 'Xnotaswapfile') + let info = swapinfo('Xnotaswapfile') + call assert_equal('Cannot read file', info.error) + call delete('Xnotaswapfile') + + call writefile([repeat('x', 10000)], 'Xnotaswapfile') + let info = swapinfo('Xnotaswapfile') + call assert_equal('Not a swap file', info.error) + call delete('Xnotaswapfile') +endfunc + +func Test_swapname() + edit Xtest1 + let expected = s:swapname() + call assert_equal(expected, swapname('%')) + + new Xtest2 + let buf = bufnr('%') + let expected = s:swapname() + wincmd p + call assert_equal(expected, swapname(buf)) + + new Xtest3 + setlocal noswapfile + call assert_equal('', swapname('%')) + + bwipe! + call delete('Xtest1') + call delete('Xtest2') + call delete('Xtest3') +endfunc + +func Test_swapfile_delete() + throw 'skipped: need the "blob" feature for this test' + autocmd! SwapExists + function s:swap_exists() + let v:swapchoice = s:swap_choice + let s:swapname = v:swapname + let s:filename = expand('<afile>') + endfunc + augroup test_swapfile_delete + autocmd! + autocmd SwapExists * call s:swap_exists() + augroup END + + + " Create a valid swapfile by editing a file. + split XswapfileText + call setline(1, ['one', 'two', 'three']) + write " file is written, not modified + " read the swapfile as a Blob + let swapfile_name = swapname('%') + let swapfile_bytes = readfile(swapfile_name, 'B') + + " Close the file and recreate the swap file. + " Now editing the file will run into the process still existing + quit + call writefile(swapfile_bytes, swapfile_name) + let s:swap_choice = 'e' + let s:swapname = '' + split XswapfileText + quit + call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t')) + + " Write the swapfile with a modified PID, now it will be automatically + " deleted. Process one should never be Vim. + let swapfile_bytes[24:27] = 0z01000000 + call writefile(swapfile_bytes, swapfile_name) + let s:swapname = '' + split XswapfileText + quit + call assert_equal('', s:swapname) + + " Now set the modified flag, the swap file will not be deleted + let swapfile_bytes[28 + 80 + 899] = 0x55 + call writefile(swapfile_bytes, swapfile_name) + let s:swapname = '' + split XswapfileText + quit + call assert_equal(fnamemodify(swapfile_name, ':t'), fnamemodify(s:swapname, ':t')) + + call delete('XswapfileText') + call delete(swapfile_name) + augroup test_swapfile_delete + autocmd! + augroup END + augroup! test_swapfile_delete +endfunc diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index 9729ca9f57..beb44f9699 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -242,6 +242,7 @@ func Test_undojoin() endfunc func Test_undo_write() + call delete('Xtest') split Xtest call feedkeys("ione one one\<Esc>", 'xt') w! diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index 0603e504ec..42e5b9b270 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -704,14 +704,6 @@ static void cursor_goto(UI *ui, int row, int col) // even less expensive than using BSes or CUB. unibi_out(ui, unibi_carriage_return); ugrid_goto(grid, grid->row, 0); - } else if (col > grid->col) { - int n = col - grid->col; - if (n <= (row == grid->row ? 4 : 2) - && cheap_to_print(ui, grid->row, grid->col, n)) { - UGRID_FOREACH_CELL(grid, grid->row, grid->col, col, { - print_cell(ui, cell); - }); - } } if (row == grid->row) { if (col < grid->col diff --git a/src/nvim/undo.c b/src/nvim/undo.c index 0a32d9b872..10996d99d5 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -1131,8 +1131,9 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, /* If there is no undo information at all, quit here after deleting any * existing undo file. */ if (buf->b_u_numhead == 0 && buf->b_u_line_ptr == NULL) { - if (p_verbose > 0) - verb_msg((char_u *)_("Skipping undo file write, nothing to undo")); + if (p_verbose > 0) { + verb_msg(_("Skipping undo file write, nothing to undo")); + } goto theend; } @@ -2150,7 +2151,7 @@ static void u_undoredo(int undo, bool do_buf_event) int new_flags; fmark_T namedm[NMARKS]; visualinfo_T visualinfo; - int empty_buffer; /* buffer became empty */ + bool empty_buffer; // buffer became empty u_header_T *curhead = curbuf->b_u_curhead; /* Don't want autocommands using the undo structures here, they are @@ -2217,7 +2218,7 @@ static void u_undoredo(int undo, bool do_buf_event) } } - empty_buffer = FALSE; + empty_buffer = false; /* delete the lines between top and bot and save them in newarray */ if (oldsize > 0) { @@ -2228,9 +2229,10 @@ static void u_undoredo(int undo, bool do_buf_event) newarray[i] = u_save_line(lnum); /* remember we deleted the last line in the buffer, and a * dummy empty line will be inserted */ - if (curbuf->b_ml.ml_line_count == 1) - empty_buffer = TRUE; - ml_delete(lnum, FALSE); + if (curbuf->b_ml.ml_line_count == 1) { + empty_buffer = true; + } + ml_delete(lnum, false); } } else newarray = NULL; @@ -2245,7 +2247,7 @@ static void u_undoredo(int undo, bool do_buf_event) if (empty_buffer && lnum == 0) { ml_replace((linenr_T)1, uep->ue_array[i], true); } else { - ml_append(lnum, uep->ue_array[i], (colnr_T)0, FALSE); + ml_append(lnum, uep->ue_array[i], (colnr_T)0, false); } xfree(uep->ue_array[i]); } diff --git a/test/functional/clipboard/clipboard_provider_spec.lua b/test/functional/clipboard/clipboard_provider_spec.lua index 2bbc678a02..b2d75db745 100644 --- a/test/functional/clipboard/clipboard_provider_spec.lua +++ b/test/functional/clipboard/clipboard_provider_spec.lua @@ -236,7 +236,7 @@ describe('clipboard', function() end) end) -describe('clipboard', function() +describe('clipboard (with fake clipboard.vim)', function() local function reset(...) clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp', ...) end @@ -664,4 +664,20 @@ describe('clipboard', function() the a sourcetarget]]) end) + it('setreg("*") with clipboard=unnamed #5646', function() + source([=[ + function! Paste_without_yank(direction) range + let [reg_save,regtype_save] = [getreg('*'), getregtype('*')] + normal! gvp + call setreg('*', reg_save, regtype_save) + endfunction + xnoremap p :call Paste_without_yank('p')<CR> + set clipboard=unnamed + ]=]) + insert('some words') + feed('gg0yiw') + feed('wviwp') + expect('some some') + eq('some', eval('getreg("*")')) + end) end) |