aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/eval.txt38
-rw-r--r--runtime/doc/usr_11.txt7
-rw-r--r--runtime/syntax/tutor.vim36
-rw-r--r--src/nvim/eval.c53
-rw-r--r--src/nvim/eval.lua3
-rw-r--r--src/nvim/eval/typval.c28
-rw-r--r--src/nvim/ex_cmds.c34
-rw-r--r--src/nvim/ex_docmd.c7
-rw-r--r--src/nvim/ex_getln.c6
-rw-r--r--src/nvim/file_search.c9
-rw-r--r--src/nvim/fileio.c123
-rw-r--r--src/nvim/main.c7
-rw-r--r--src/nvim/memline.c268
-rw-r--r--src/nvim/message.c10
-rw-r--r--src/nvim/misc1.c8
-rw-r--r--src/nvim/ops.c12
-rw-r--r--src/nvim/os/process.c6
-rw-r--r--src/nvim/popupmnu.c6
-rw-r--r--src/nvim/spell.c15
-rw-r--r--src/nvim/spellfile.c8
-rw-r--r--src/nvim/testdir/test_swap.vim163
-rw-r--r--src/nvim/testdir/test_undo.vim1
-rw-r--r--src/nvim/tui/tui.c8
-rw-r--r--src/nvim/undo.c18
-rw-r--r--test/functional/clipboard/clipboard_provider_spec.lua18
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)