aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/options.txt7
-rw-r--r--runtime/doc/sign.txt4
-rw-r--r--runtime/doc/vim_diff.txt1
-rw-r--r--src/nvim/api/buffer.c9
-rw-r--r--src/nvim/buffer.c19
-rw-r--r--src/nvim/eval/typval.h11
-rw-r--r--src/nvim/ex_cmds.c66
-rw-r--r--src/nvim/fileio.c563
-rw-r--r--src/nvim/screen.c21
-rw-r--r--src/nvim/sign_defs.h21
-rw-r--r--src/nvim/testdir/test_signs.vim6
-rw-r--r--src/nvim/testdir/test_writefile.vim18
-rw-r--r--test/functional/ex_cmds/sign_spec.lua2
-rw-r--r--test/functional/ui/bufhl_spec.lua11
-rw-r--r--test/functional/ui/sign_spec.lua33
15 files changed, 469 insertions, 323 deletions
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index ab866da320..bfdc09662e 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -4256,12 +4256,11 @@ A jump table for the options with a short description can be found at |Q_op|.
Print the line number in front of each line. When the 'n' option is
excluded from 'cpoptions' a wrapped line will not use the column of
line numbers.
- The 'numberwidth' option can be used to set the room used for the line
- number.
+ Use the 'numberwidth' option to adjust the room for the line number.
When a long, wrapped line doesn't start with the first character, '-'
characters are put before the number.
- See |hl-LineNr| and |hl-CursorLineNr| for the highlighting used for
- the number.
+ For highlighting see |hl-LineNr|, |hl-CursorLineNr|, and the
+ |:sign-define| "numhl" argument.
*number_relativenumber*
The 'relativenumber' option changes the displayed number to be
relative to the cursor. Together with 'number' there are these
diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt
index 977d73b7b2..7cdb460943 100644
--- a/runtime/doc/sign.txt
+++ b/runtime/doc/sign.txt
@@ -85,6 +85,10 @@ DEFINING A SIGN. *:sign-define* *E255* *E160* *E612*
Highlighting group used for the whole line the sign is placed
in. Most useful is defining a background color.
+ numhl={group}
+ Highlighting group used for 'number' column at the associated
+ line. Overrides |hl-LineNr|, |hl-CursorLineNr|.
+
text={text} *E239*
Define the text that is displayed when there is no icon or the
GUI is not being used. Only printable characters are allowed
diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt
index 2615d8a108..4fbfb0d7a0 100644
--- a/runtime/doc/vim_diff.txt
+++ b/runtime/doc/vim_diff.txt
@@ -136,6 +136,7 @@ Commands:
|:cquit| can use [count] to set the exit code
|:drop| is always available
|:Man| is available by default, with many improvements such as completion
+ |:sign-define| accepts a `numhl` argument, to highlight the line number
|:tchdir| tab-local |current-directory|
Events:
diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c
index 16196ec910..1491901877 100644
--- a/src/nvim/api/buffer.c
+++ b/src/nvim/api/buffer.c
@@ -13,6 +13,7 @@
#include "nvim/api/private/defs.h"
#include "nvim/vim.h"
#include "nvim/buffer.h"
+#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/memline.h"
#include "nvim/memory.h"
@@ -933,7 +934,7 @@ Integer nvim_buf_add_highlight(Buffer buffer,
return src_id;
}
-/// Clears highlights from a given source group and a range of lines
+/// Clears highlights and virtual text from a given source id and range of lines
///
/// To clear a source group in the entire buffer, pass in 0 and -1 to
/// line_start and line_end respectively.
@@ -976,6 +977,10 @@ void nvim_buf_clear_highlight(Buffer buffer,
/// begin after one cell to the right of the ordinary text, this will contain
/// the |lcs-eol| char if set, otherwise just be a space.
///
+/// The same src_id can be used for both virtual text and highlights added by
+/// nvim_buf_add_highlight. Virtual text is cleared using
+/// nvim_buf_clear_highlight.
+///
/// @param buffer Buffer handle
/// @param src_id Source group to use or 0 to use a new group,
/// or -1 for a ungrouped annotation
@@ -1025,7 +1030,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer,
}
String str = chunk.items[0].data.string;
- char *text = xstrdup(str.size > 0 ? str.data : "");
+ char *text = transstr(str.size > 0 ? str.data : ""); // allocates
int hl_id = 0;
if (chunk.size == 2) {
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index 68f6ff303b..ce6aa69239 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -5084,13 +5084,16 @@ linenr_T buf_change_sign_type(
return (linenr_T)0;
}
-int buf_getsigntype(
- buf_T *buf,
- linenr_T lnum,
- int type /* SIGN_ICON, SIGN_TEXT, SIGN_ANY, SIGN_LINEHL */
- )
+/// Gets a sign from a given line.
+/// In case of multiple signs, returns the most recently placed one.
+///
+/// @param buf Buffer in which to search
+/// @param lnum Line in which to search
+/// @param type Type of sign to look for
+/// @return Identifier of the first matching sign, or 0
+int buf_getsigntype(buf_T *buf, linenr_T lnum, SignType type)
{
- signlist_T *sign; /* a sign in a b_signlist */
+ signlist_T *sign; // a sign in a b_signlist
for (sign = buf->b_signlist; sign != NULL; sign = sign->next) {
if (sign->lnum == lnum
@@ -5098,7 +5101,9 @@ int buf_getsigntype(
|| (type == SIGN_TEXT
&& sign_get_text(sign->typenr) != NULL)
|| (type == SIGN_LINEHL
- && sign_get_attr(sign->typenr, TRUE) != 0))) {
+ && sign_get_attr(sign->typenr, SIGN_LINEHL) != 0)
+ || (type == SIGN_NUMHL
+ && sign_get_attr(sign->typenr, SIGN_NUMHL) != 0))) {
return sign->typenr;
}
}
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 664bf7332c..e99289c430 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -162,19 +162,20 @@ struct listwatch_S {
};
/// Structure to hold info about a list
+/// Order of members is optimized to reduce padding.
struct listvar_S {
listitem_T *lv_first; ///< First item, NULL if none.
listitem_T *lv_last; ///< Last item, NULL if none.
- int lv_refcount; ///< Reference count.
- int lv_len; ///< Number of items.
listwatch_T *lv_watch; ///< First watcher, NULL if none.
- int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx].
listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx".
- int lv_copyID; ///< ID used by deepcopy().
list_T *lv_copylist; ///< Copied list used by deepcopy().
- VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED.
list_T *lv_used_next; ///< next list in used lists list.
list_T *lv_used_prev; ///< Previous list in used lists list.
+ int lv_refcount; ///< Reference count.
+ int lv_len; ///< Number of items.
+ int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx].
+ int lv_copyID; ///< ID used by deepcopy().
+ VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED.
};
// Static list with 10 items. Use tv_list_init_static10() to initialize.
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c
index 4710ae669b..bce0c35f67 100644
--- a/src/nvim/ex_cmds.c
+++ b/src/nvim/ex_cmds.c
@@ -5471,13 +5471,14 @@ void ex_helptags(exarg_T *eap)
struct sign
{
- sign_T *sn_next; /* next sign in list */
- int sn_typenr; /* type number of sign */
- char_u *sn_name; /* name of sign */
- char_u *sn_icon; /* name of pixmap */
- char_u *sn_text; /* text used instead of pixmap */
- int sn_line_hl; /* highlight ID for line */
- int sn_text_hl; /* highlight ID for text */
+ sign_T *sn_next; // next sign in list
+ int sn_typenr; // type number of sign
+ char_u *sn_name; // name of sign
+ char_u *sn_icon; // name of pixmap
+ char_u *sn_text; // text used instead of pixmap
+ int sn_line_hl; // highlight ID for line
+ int sn_text_hl; // highlight ID for text
+ int sn_num_hl; // highlight ID for line number
};
static sign_T *first_sign = NULL;
@@ -5675,6 +5676,9 @@ void ex_sign(exarg_T *eap)
} else if (STRNCMP(arg, "texthl=", 7) == 0) {
arg += 7;
sp->sn_text_hl = syn_check_group(arg, (int)(p - arg));
+ } else if (STRNCMP(arg, "numhl=", 6) == 0) {
+ arg += 6;
+ sp->sn_num_hl = syn_check_group(arg, (int)(p - arg));
} else {
EMSG2(_(e_invarg2), arg);
return;
@@ -5901,6 +5905,16 @@ static void sign_list_defined(sign_T *sp)
msg_puts(p);
}
}
+ if (sp->sn_num_hl > 0) {
+ msg_puts(" numhl=");
+ const char *const p = get_highlight_name_ext(NULL,
+ sp->sn_num_hl - 1, false);
+ if (p == NULL) {
+ msg_puts("NONE");
+ } else {
+ msg_puts(p);
+ }
+ }
}
/*
@@ -5918,25 +5932,33 @@ static void sign_undefine(sign_T *sp, sign_T *sp_prev)
xfree(sp);
}
-/*
- * Get highlighting attribute for sign "typenr".
- * If "line" is TRUE: line highl, if FALSE: text highl.
- */
-int sign_get_attr(int typenr, int line)
+/// Gets highlighting attribute for sign "typenr" corresponding to "type".
+int sign_get_attr(int typenr, SignType type)
{
sign_T *sp;
+ int sign_hl = 0;
- for (sp = first_sign; sp != NULL; sp = sp->sn_next)
+ for (sp = first_sign; sp != NULL; sp = sp->sn_next) {
if (sp->sn_typenr == typenr) {
- if (line) {
- if (sp->sn_line_hl > 0)
- return syn_id2attr(sp->sn_line_hl);
- } else {
- if (sp->sn_text_hl > 0)
- return syn_id2attr(sp->sn_text_hl);
+ switch (type) {
+ case SIGN_TEXT:
+ sign_hl = sp->sn_text_hl;
+ break;
+ case SIGN_LINEHL:
+ sign_hl = sp->sn_line_hl;
+ break;
+ case SIGN_NUMHL:
+ sign_hl = sp->sn_num_hl;
+ break;
+ default:
+ abort();
+ }
+ if (sign_hl > 0) {
+ return syn_id2attr(sign_hl);
}
break;
}
+ }
return 0;
}
@@ -5997,7 +6019,8 @@ char_u * get_sign_name(expand_T *xp, int idx)
case EXP_SUBCMD:
return (char_u *)cmds[idx];
case EXP_DEFINE: {
- char *define_arg[] = { "icon=", "linehl=", "text=", "texthl=", NULL };
+ char *define_arg[] = { "icon=", "linehl=", "text=", "texthl=", "numhl=",
+ NULL };
return (char_u *)define_arg[idx];
}
case EXP_PLACE: {
@@ -6120,7 +6143,8 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg)
{
case SIGNCMD_DEFINE:
if (STRNCMP(last, "texthl", p - last) == 0
- || STRNCMP(last, "linehl", p - last) == 0) {
+ || STRNCMP(last, "linehl", p - last) == 0
+ || STRNCMP(last, "numhl", p - last) == 0) {
xp->xp_context = EXPAND_HIGHLIGHT;
} else if (STRNCMP(last, "icon", p - last) == 0) {
xp->xp_context = EXPAND_FILES;
diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c
index a5ff13552b..d0e30ddbd3 100644
--- a/src/nvim/fileio.c
+++ b/src/nvim/fileio.c
@@ -2267,15 +2267,16 @@ buf_write (
char_u smallbuf[SMBUFSIZE];
char_u *backup_ext;
int bufsize;
- long perm; /* file permissions */
+ long perm; // file permissions
int retval = OK;
- int newfile = FALSE; /* TRUE if file doesn't exist yet */
+ int newfile = false; // TRUE if file doesn't exist yet
int msg_save = msg_scroll;
- int overwriting; /* TRUE if writing over original */
- int no_eol = FALSE; /* no end-of-line written */
- int device = FALSE; /* writing to a device */
+ int overwriting; // TRUE if writing over original
+ int no_eol = false; // no end-of-line written
+ int device = false; // writing to a device
int prev_got_int = got_int;
- bool file_readonly = false; /* overwritten file is read-only */
+ int checking_conversion;
+ bool file_readonly = false; // overwritten file is read-only
static char *err_readonly =
"is read-only (cannot override: \"W\" in 'cpoptions')";
#if defined(UNIX)
@@ -3156,298 +3157,328 @@ nobackup:
notconverted = TRUE;
}
- /*
- * Open the file "wfname" for writing.
- * We may try to open the file twice: If we can't write to the
- * file and forceit is TRUE we delete the existing file and try to create
- * a new one. If this still fails we may have lost the original file!
- * (this may happen when the user reached his quotum for number of files).
- * Appending will fail if the file does not exist and forceit is FALSE.
- */
- while ((fd = os_open((char *)wfname, O_WRONLY | (append
- ? (forceit ? (
- O_APPEND |
- O_CREAT) :
- O_APPEND)
- : (O_CREAT |
- O_TRUNC))
- , perm < 0 ? 0666 : (perm & 0777))) < 0) {
- /*
- * A forced write will try to create a new file if the old one is
- * still readonly. This may also happen when the directory is
- * read-only. In that case the os_remove() will fail.
- */
- if (errmsg == NULL) {
+ // If conversion is taking place, we may first pretend to write and check
+ // for conversion errors. Then loop again to write for real.
+ // When not doing conversion this writes for real right away.
+ for (checking_conversion = true; ; checking_conversion = false) {
+ // There is no need to check conversion when:
+ // - there is no conversion
+ // - we make a backup file, that can be restored in case of conversion
+ // failure.
+ if (!converted || dobackup) {
+ checking_conversion = false;
+ }
+
+ if (checking_conversion) {
+ // Make sure we don't write anything.
+ fd = -1;
+ write_info.bw_fd = fd;
+ } else {
+ // Open the file "wfname" for writing.
+ // We may try to open the file twice: If we can't write to the file
+ // and forceit is TRUE we delete the existing file and try to
+ // create a new one. If this still fails we may have lost the
+ // original file! (this may happen when the user reached his
+ // quotum for number of files).
+ // Appending will fail if the file does not exist and forceit is
+ // FALSE.
+ while ((fd = os_open((char *)wfname,
+ O_WRONLY |
+ (append ?
+ (forceit ? (O_APPEND | O_CREAT) : O_APPEND)
+ : (O_CREAT | O_TRUNC))
+ , perm < 0 ? 0666 : (perm & 0777))) < 0) {
+ // A forced write will try to create a new file if the old one
+ // is still readonly. This may also happen when the directory
+ // is read-only. In that case the mch_remove() will fail.
+ if (errmsg == NULL) {
#ifdef UNIX
- FileInfo file_info;
+ FileInfo file_info;
- // Don't delete the file when it's a hard or symbolic link.
- if ((!newfile && os_fileinfo_hardlinks(&file_info_old) > 1)
- || (os_fileinfo_link((char *)fname, &file_info)
- && !os_fileinfo_id_equal(&file_info, &file_info_old))) {
- SET_ERRMSG(_("E166: Can't open linked file for writing"));
- } else {
+ // Don't delete the file when it's a hard or symbolic link.
+ if ((!newfile && os_fileinfo_hardlinks(&file_info_old) > 1)
+ || (os_fileinfo_link((char *)fname, &file_info)
+ && !os_fileinfo_id_equal(&file_info, &file_info_old))) {
+ SET_ERRMSG(_("E166: Can't open linked file for writing"));
+ } else {
#endif
- SET_ERRMSG_ARG(_("E212: Can't open file for writing: %s"), fd);
- if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL
- && perm >= 0) {
+ SET_ERRMSG_ARG(_("E212: Can't open file for writing: %s"), fd);
+ if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL
+ && perm >= 0) {
#ifdef UNIX
- /* we write to the file, thus it should be marked
- writable after all */
- if (!(perm & 0200))
- made_writable = TRUE;
- perm |= 0200;
- if (file_info_old.stat.st_uid != getuid()
- || file_info_old.stat.st_gid != getgid()) {
- perm &= 0777;
- }
+ // we write to the file, thus it should be marked
+ // writable after all
+ if (!(perm & 0200)) {
+ made_writable = true;
+ }
+ perm |= 0200;
+ if (file_info_old.stat.st_uid != getuid()
+ || file_info_old.stat.st_gid != getgid()) {
+ perm &= 0777;
+ }
#endif
- if (!append) /* don't remove when appending */
- os_remove((char *)wfname);
- continue;
- }
+ if (!append) { // don't remove when appending
+ os_remove((char *)wfname);
+ }
+ continue;
+ }
#ifdef UNIX
- }
+ }
#endif
- }
+ }
restore_backup:
- {
- /*
- * If we failed to open the file, we don't need a backup. Throw it
- * away. If we moved or removed the original file try to put the
- * backup in its place.
- */
- if (backup != NULL && wfname == fname) {
- if (backup_copy) {
- /*
- * There is a small chance that we removed the original,
- * try to move the copy in its place.
- * This may not work if the vim_rename() fails.
- * In that case we leave the copy around.
- */
- // If file does not exist, put the copy in its place
- if (!os_path_exists(fname)) {
- vim_rename(backup, fname);
+ {
+ // If we failed to open the file, we don't need a backup. Throw it
+ // away. If we moved or removed the original file try to put the
+ // backup in its place.
+ if (backup != NULL && wfname == fname) {
+ if (backup_copy) {
+ // There is a small chance that we removed the original,
+ // try to move the copy in its place.
+ // This may not work if the vim_rename() fails.
+ // In that case we leave the copy around.
+ // If file does not exist, put the copy in its place
+ if (!os_path_exists(fname)) {
+ vim_rename(backup, fname);
+ }
+ // if original file does exist throw away the copy
+ if (os_path_exists(fname)) {
+ os_remove((char *)backup);
+ }
+ } else {
+ // try to put the original file back
+ vim_rename(backup, fname);
+ }
}
- // if original file does exist throw away the copy
- if (os_path_exists(fname)) {
- os_remove((char *)backup);
+
+ // if original file no longer exists give an extra warning
+ if (!newfile && !os_path_exists(fname)) {
+ end = 0;
}
- } else {
- /* try to put the original file back */
- vim_rename(backup, fname);
}
- }
- // if original file no longer exists give an extra warning
- if (!newfile && !os_path_exists(fname)) {
- end = 0;
+ if (wfname != fname) {
+ xfree(wfname);
+ }
+ goto fail;
}
+ write_info.bw_fd = fd;
}
+ SET_ERRMSG(NULL);
- if (wfname != fname)
- xfree(wfname);
- goto fail;
- }
- SET_ERRMSG(NULL);
+ write_info.bw_buf = buffer;
+ nchars = 0;
-
- write_info.bw_fd = fd;
- write_info.bw_buf = buffer;
- nchars = 0;
-
- /* use "++bin", "++nobin" or 'binary' */
- if (eap != NULL && eap->force_bin != 0)
- write_bin = (eap->force_bin == FORCE_BIN);
- else
- write_bin = buf->b_p_bin;
-
- /*
- * Skip the BOM when appending and the file already existed, the BOM
- * only makes sense at the start of the file.
- */
- if (buf->b_p_bomb && !write_bin && (!append || perm < 0)) {
- write_info.bw_len = make_bom(buffer, fenc);
- if (write_info.bw_len > 0) {
- /* don't convert */
- write_info.bw_flags = FIO_NOCONVERT | wb_flags;
- if (buf_write_bytes(&write_info) == FAIL)
- end = 0;
- else
- nchars += write_info.bw_len;
+ // use "++bin", "++nobin" or 'binary'
+ if (eap != NULL && eap->force_bin != 0) {
+ write_bin = (eap->force_bin == FORCE_BIN);
+ } else {
+ write_bin = buf->b_p_bin;
+ }
+
+ // Skip the BOM when appending and the file already existed, the BOM
+ // only makes sense at the start of the file.
+ if (buf->b_p_bomb && !write_bin && (!append || perm < 0)) {
+ write_info.bw_len = make_bom(buffer, fenc);
+ if (write_info.bw_len > 0) {
+ // don't convert
+ write_info.bw_flags = FIO_NOCONVERT | wb_flags;
+ if (buf_write_bytes(&write_info) == FAIL) {
+ end = 0;
+ } else {
+ nchars += write_info.bw_len;
+ }
+ }
}
- }
- write_info.bw_start_lnum = start;
+ write_info.bw_start_lnum = start;
- write_undo_file = (buf->b_p_udf && overwriting && !append
- && !filtering && reset_changed);
- if (write_undo_file)
- /* Prepare for computing the hash value of the text. */
- sha256_start(&sha_ctx);
+ write_undo_file = (buf->b_p_udf && overwriting && !append
+ && !filtering && reset_changed && !checking_conversion);
+ if (write_undo_file) {
+ // Prepare for computing the hash value of the text.
+ sha256_start(&sha_ctx);
+ }
- write_info.bw_len = bufsize;
+ write_info.bw_len = bufsize;
#ifdef HAS_BW_FLAGS
- write_info.bw_flags = wb_flags;
+ write_info.bw_flags = wb_flags;
#endif
- fileformat = get_fileformat_force(buf, eap);
- s = buffer;
- len = 0;
- for (lnum = start; lnum <= end; ++lnum) {
- /*
- * The next while loop is done once for each character written.
- * Keep it fast!
- */
- ptr = ml_get_buf(buf, lnum, FALSE) - 1;
- if (write_undo_file)
- sha256_update(&sha_ctx, ptr + 1, (uint32_t)(STRLEN(ptr + 1) + 1));
- while ((c = *++ptr) != NUL) {
- if (c == NL)
- *s = NUL; /* replace newlines with NULs */
- else if (c == CAR && fileformat == EOL_MAC)
- *s = NL; /* Mac: replace CRs with NLs */
- else
- *s = c;
- ++s;
- if (++len != bufsize)
- continue;
- if (buf_write_bytes(&write_info) == FAIL) {
- end = 0; /* write error: break loop */
+ fileformat = get_fileformat_force(buf, eap);
+ s = buffer;
+ len = 0;
+ for (lnum = start; lnum <= end; lnum++) {
+ // The next while loop is done once for each character written.
+ // Keep it fast!
+ ptr = ml_get_buf(buf, lnum, false) - 1;
+ if (write_undo_file) {
+ sha256_update(&sha_ctx, ptr + 1, (uint32_t)(STRLEN(ptr + 1) + 1));
+ }
+ while ((c = *++ptr) != NUL) {
+ if (c == NL) {
+ *s = NUL; // replace newlines with NULs
+ } else if (c == CAR && fileformat == EOL_MAC) {
+ *s = NL; // Mac: replace CRs with NLs
+ } else {
+ *s = c;
+ }
+ s++;
+ if (++len != bufsize) {
+ continue;
+ }
+ if (buf_write_bytes(&write_info) == FAIL) {
+ end = 0; // write error: break loop
+ break;
+ }
+ nchars += bufsize;
+ s = buffer;
+ len = 0;
+ write_info.bw_start_lnum = lnum;
+ }
+ // write failed or last line has no EOL: stop here
+ if (end == 0
+ || (lnum == end
+ && (write_bin || !buf->b_p_fixeol)
+ && (lnum == buf->b_no_eol_lnum
+ || (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol)))) {
+ lnum++; // written the line, count it
+ no_eol = true;
break;
}
- nchars += bufsize;
- s = buffer;
- len = 0;
- write_info.bw_start_lnum = lnum;
- }
- /* write failed or last line has no EOL: stop here */
- if (end == 0
- || (lnum == end
- && (write_bin || !buf->b_p_fixeol)
- && (lnum == buf->b_no_eol_lnum
- || (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol)))) {
- ++lnum; /* written the line, count it */
- no_eol = TRUE;
- break;
- }
- if (fileformat == EOL_UNIX)
- *s++ = NL;
- else {
- *s++ = CAR; /* EOL_MAC or EOL_DOS: write CR */
- if (fileformat == EOL_DOS) { /* write CR-NL */
- if (++len == bufsize) {
- if (buf_write_bytes(&write_info) == FAIL) {
- end = 0; /* write error: break loop */
- break;
+ if (fileformat == EOL_UNIX) {
+ *s++ = NL;
+ } else {
+ *s++ = CAR; // EOL_MAC or EOL_DOS: write CR
+ if (fileformat == EOL_DOS) { // write CR-NL
+ if (++len == bufsize) {
+ if (buf_write_bytes(&write_info) == FAIL) {
+ end = 0; // write error: break loop
+ break;
+ }
+ nchars += bufsize;
+ s = buffer;
+ len = 0;
}
- nchars += bufsize;
- s = buffer;
- len = 0;
+ *s++ = NL;
+ }
+ }
+ if (++len == bufsize) {
+ if (buf_write_bytes(&write_info) == FAIL) {
+ end = 0; // Write error: break loop.
+ break;
+ }
+ nchars += bufsize;
+ s = buffer;
+ len = 0;
+
+ os_breakcheck();
+ if (got_int) {
+ end = 0; // Interrupted, break loop.
+ break;
}
- *s++ = NL;
}
}
- if (++len == bufsize) {
+ if (len > 0 && end > 0) {
+ write_info.bw_len = len;
if (buf_write_bytes(&write_info) == FAIL) {
- end = 0; // Write error: break loop.
- break;
+ end = 0; // write error
}
- nchars += bufsize;
- s = buffer;
- len = 0;
+ nchars += len;
+ }
- os_breakcheck();
- if (got_int) {
- end = 0; // Interrupted, break loop.
+ // Stop when writing done or an error was encountered.
+ if (!checking_conversion || end == 0) {
break;
- }
}
- }
- if (len > 0 && end > 0) {
- write_info.bw_len = len;
- if (buf_write_bytes(&write_info) == FAIL)
- end = 0; /* write error */
- nchars += len;
- }
- // On many journalling file systems there is a bug that causes both the
- // original and the backup file to be lost when halting the system right
- // after writing the file. That's because only the meta-data is
- // journalled. Syncing the file slows down the system, but assures it has
- // been written to disk and we don't lose it.
- // For a device do try the fsync() but don't complain if it does not work
- // (could be a pipe).
- // If the 'fsync' option is FALSE, don't fsync(). Useful for laptops.
- int error;
- if (p_fs && (error = os_fsync(fd)) != 0 && !device) {
- SET_ERRMSG_ARG(_("E667: Fsync failed: %s"), error);
- end = 0;
+ // If no error happened until now, writing should be ok, so loop to
+ // really write the buffer.
}
+ // If we started writing, finish writing. Also when an error was
+ // encountered.
+ if (!checking_conversion) {
+ // On many journalling file systems there is a bug that causes both the
+ // original and the backup file to be lost when halting the system right
+ // after writing the file. That's because only the meta-data is
+ // journalled. Syncing the file slows down the system, but assures it has
+ // been written to disk and we don't lose it.
+ // For a device do try the fsync() but don't complain if it does not work
+ // (could be a pipe).
+ // If the 'fsync' option is FALSE, don't fsync(). Useful for laptops.
+ int error;
+ if (p_fs && (error = os_fsync(fd)) != 0 && !device) {
+ SET_ERRMSG_ARG(_("E667: Fsync failed: %s"), error);
+ end = 0;
+ }
+
#ifdef HAVE_SELINUX
- /* Probably need to set the security context. */
- if (!backup_copy)
- mch_copy_sec(backup, wfname);
+ // Probably need to set the security context.
+ if (!backup_copy) {
+ mch_copy_sec(backup, wfname);
+ }
#endif
#ifdef UNIX
- /* When creating a new file, set its owner/group to that of the original
- * file. Get the new device and inode number. */
- if (backup != NULL && !backup_copy) {
- /* don't change the owner when it's already OK, some systems remove
- * permission or ACL stuff */
- FileInfo file_info;
- if (!os_fileinfo((char *)wfname, &file_info)
- || file_info.stat.st_uid != file_info_old.stat.st_uid
- || file_info.stat.st_gid != file_info_old.stat.st_gid) {
- os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid);
- if (perm >= 0) { // Set permission again, may have changed.
- (void)os_setperm((const char *)wfname, perm);
+ // When creating a new file, set its owner/group to that of the original
+ // file. Get the new device and inode number.
+ if (backup != NULL && !backup_copy) {
+ // don't change the owner when it's already OK, some systems remove
+ // permission or ACL stuff
+ FileInfo file_info;
+ if (!os_fileinfo((char *)wfname, &file_info)
+ || file_info.stat.st_uid != file_info_old.stat.st_uid
+ || file_info.stat.st_gid != file_info_old.stat.st_gid) {
+ os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid);
+ if (perm >= 0) { // Set permission again, may have changed.
+ (void)os_setperm((const char *)wfname, perm);
+ }
}
+ buf_set_file_id(buf);
+ } else if (!buf->file_id_valid) {
+ // Set the file_id when creating a new file.
+ buf_set_file_id(buf);
}
- buf_set_file_id(buf);
- } else if (!buf->file_id_valid) {
- // Set the file_id when creating a new file.
- buf_set_file_id(buf);
- }
#endif
- if ((error = os_close(fd)) != 0) {
- SET_ERRMSG_ARG(_("E512: Close failed: %s"), error);
- end = 0;
- }
+ if ((error = os_close(fd)) != 0) {
+ SET_ERRMSG_ARG(_("E512: Close failed: %s"), error);
+ end = 0;
+ }
#ifdef UNIX
- if (made_writable)
- perm &= ~0200; /* reset 'w' bit for security reasons */
+ if (made_writable) {
+ perm &= ~0200; // reset 'w' bit for security reasons
+ }
#endif
- if (perm >= 0) { // Set perm. of new file same as old file.
- (void)os_setperm((const char *)wfname, perm);
- }
+ if (perm >= 0) { // Set perm. of new file same as old file.
+ (void)os_setperm((const char *)wfname, perm);
+ }
#ifdef HAVE_ACL
- /* Probably need to set the ACL before changing the user (can't set the
- * ACL on a file the user doesn't own). */
- if (!backup_copy)
- mch_set_acl(wfname, acl);
+ // Probably need to set the ACL before changing the user (can't set the
+ // ACL on a file the user doesn't own).
+ if (!backup_copy) {
+ mch_set_acl(wfname, acl);
+ }
#endif
- if (wfname != fname) {
- /*
- * The file was written to a temp file, now it needs to be converted
- * with 'charconvert' to (overwrite) the output file.
- */
- if (end != 0) {
- if (eval_charconvert(enc_utf8 ? "utf-8" : (char *) p_enc, (char *) fenc,
- (char *) wfname, (char *) fname) == FAIL) {
- write_info.bw_conv_error = true;
- end = 0;
+ if (wfname != fname) {
+ // The file was written to a temp file, now it needs to be converted
+ // with 'charconvert' to (overwrite) the output file.
+ if (end != 0) {
+ if (eval_charconvert(enc_utf8 ? "utf-8" : (char *)p_enc, (char *)fenc,
+ (char *)wfname, (char *)fname) == FAIL) {
+ write_info.bw_conv_error = true;
+ end = 0;
+ }
}
+ os_remove((char *)wfname);
+ xfree(wfname);
}
- os_remove((char *)wfname);
- xfree(wfname);
}
if (end == 0) {
+ // Error encountered.
if (errmsg == NULL) {
if (write_info.bw_conv_error) {
if (write_info.bw_conv_error_lnum == 0) {
@@ -3470,46 +3501,48 @@ restore_backup:
}
}
- /*
- * If we have a backup file, try to put it in place of the new file,
- * because the new file is probably corrupt. This avoids losing the
- * original file when trying to make a backup when writing the file a
- * second time.
- * When "backup_copy" is set we need to copy the backup over the new
- * file. Otherwise rename the backup file.
- * If this is OK, don't give the extra warning message.
- */
+ // If we have a backup file, try to put it in place of the new file,
+ // because the new file is probably corrupt. This avoids losing the
+ // original file when trying to make a backup when writing the file a
+ // second time.
+ // When "backup_copy" is set we need to copy the backup over the new
+ // file. Otherwise rename the backup file.
+ // If this is OK, don't give the extra warning message.
if (backup != NULL) {
if (backup_copy) {
- /* This may take a while, if we were interrupted let the user
- * know we got the message. */
+ // This may take a while, if we were interrupted let the user
+ // know we got the message.
if (got_int) {
MSG(_(e_interr));
ui_flush();
}
if ((fd = os_open((char *)backup, O_RDONLY, 0)) >= 0) {
if ((write_info.bw_fd = os_open((char *)fname,
- O_WRONLY | O_CREAT | O_TRUNC,
- perm & 0777)) >= 0) {
- /* copy the file. */
+ O_WRONLY | O_CREAT | O_TRUNC,
+ perm & 0777)) >= 0) {
+ // copy the file.
write_info.bw_buf = smallbuf;
#ifdef HAS_BW_FLAGS
write_info.bw_flags = FIO_NOCONVERT;
#endif
while ((write_info.bw_len = read_eintr(fd, smallbuf,
- SMBUFSIZE)) > 0)
- if (buf_write_bytes(&write_info) == FAIL)
+ SMBUFSIZE)) > 0) {
+ if (buf_write_bytes(&write_info) == FAIL) {
break;
+ }
+ }
if (close(write_info.bw_fd) >= 0
- && write_info.bw_len == 0)
- end = 1; /* success */
+ && write_info.bw_len == 0) {
+ end = 1; // success
+ }
}
- close(fd); /* ignore errors for closing read file */
+ close(fd); // ignore errors for closing read file
}
} else {
- if (vim_rename(backup, fname) == 0)
+ if (vim_rename(backup, fname) == 0) {
end = 1;
+ }
}
}
goto fail;
@@ -4099,6 +4132,10 @@ static int buf_write_bytes(struct bw_info *ip)
# endif
}
+ if (ip->bw_fd < 0) {
+ // Only checking conversion, which is OK if we get here.
+ return OK;
+ }
wlen = write_eintr(ip->bw_fd, buf, len);
return (wlen < len) ? FAIL : OK;
}
diff --git a/src/nvim/screen.c b/src/nvim/screen.c
index ec4b31c40d..a5b07bb49f 100644
--- a/src/nvim/screen.c
+++ b/src/nvim/screen.c
@@ -2486,7 +2486,7 @@ win_line (
// If this line has a sign with line highlighting set line_attr.
v = buf_getsigntype(wp->w_buffer, lnum, SIGN_LINEHL);
if (v != 0) {
- line_attr = sign_get_attr((int)v, true);
+ line_attr = sign_get_attr((int)v, SIGN_LINEHL);
}
// Highlight the current line in the quickfix window.
@@ -2794,7 +2794,7 @@ win_line (
p_extra = extra;
p_extra[n_extra] = NUL;
}
- char_attr = sign_get_attr(text_sign, FALSE);
+ char_attr = sign_get_attr(text_sign, SIGN_TEXT);
}
}
}
@@ -2841,12 +2841,17 @@ win_line (
c_extra = ' ';
n_extra = number_width(wp) + 1;
char_attr = win_hl_attr(wp, HLF_N);
- // When 'cursorline' is set highlight the line number of
- // the current line differently.
- // TODO(vim): Can we use CursorLine instead of CursorLineNr
- // when CursorLineNr isn't set?
- if ((wp->w_p_cul || wp->w_p_rnu)
- && lnum == wp->w_cursor.lnum) {
+
+ int num_sign = buf_getsigntype(wp->w_buffer, lnum, SIGN_NUMHL);
+ if (num_sign != 0) {
+ // :sign defined with "numhl" highlight.
+ char_attr = sign_get_attr(num_sign, SIGN_NUMHL);
+ } else if ((wp->w_p_cul || wp->w_p_rnu)
+ && lnum == wp->w_cursor.lnum) {
+ // When 'cursorline' is set highlight the line number of
+ // the current line differently.
+ // TODO(vim): Can we use CursorLine instead of CursorLineNr
+ // when CursorLineNr isn't set?
char_attr = win_hl_attr(wp, HLF_CLN);
}
}
diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h
index 3778f4287e..4443fd8a2e 100644
--- a/src/nvim/sign_defs.h
+++ b/src/nvim/sign_defs.h
@@ -9,17 +9,20 @@ typedef struct signlist signlist_T;
struct signlist
{
- int id; /* unique identifier for each placed sign */
- linenr_T lnum; /* line number which has this sign */
- int typenr; /* typenr of sign */
- signlist_T *next; /* next signlist entry */
+ int id; // unique identifier for each placed sign
+ linenr_T lnum; // line number which has this sign
+ int typenr; // typenr of sign
+ signlist_T *next; // next signlist entry
};
-/* type argument for buf_getsigntype() */
-#define SIGN_ANY 0
-#define SIGN_LINEHL 1
-#define SIGN_ICON 2
-#define SIGN_TEXT 3
+// type argument for buf_getsigntype() and sign_get_attr()
+typedef enum {
+ SIGN_ANY,
+ SIGN_LINEHL,
+ SIGN_ICON,
+ SIGN_TEXT,
+ SIGN_NUMHL,
+} SignType;
diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim
index a967435346..d3c6d05f4f 100644
--- a/src/nvim/testdir/test_signs.vim
+++ b/src/nvim/testdir/test_signs.vim
@@ -14,7 +14,7 @@ func Test_sign()
" the icon name when listing signs.
sign define Sign1 text=x
try
- sign define Sign2 text=xy texthl=Title linehl=Error icon=../../pixmaps/stock_vim_find_help.png
+ sign define Sign2 text=xy texthl=Title linehl=Error numhl=Number icon=../../pixmaps/stock_vim_find_help.png
catch /E255:/
" ignore error: E255: Couldn't read in sign data!
" This error can happen when running in gui.
@@ -23,7 +23,7 @@ func Test_sign()
" Test listing signs.
let a=execute('sign list')
- call assert_match("^\nsign Sign1 text=x \nsign Sign2 icon=../../pixmaps/stock_vim_find_help.png .*text=xy linehl=Error texthl=Title$", a)
+ call assert_match("^\nsign Sign1 text=x \nsign Sign2 icon=../../pixmaps/stock_vim_find_help.png .*text=xy linehl=Error texthl=Title numhl=Number$", a)
let a=execute('sign list Sign1')
call assert_equal("\nsign Sign1 text=x ", a)
@@ -140,7 +140,7 @@ func Test_sign_completion()
call assert_equal('"sign define jump list place undefine unplace', @:)
call feedkeys(":sign define Sign \<C-A>\<C-B>\"\<CR>", 'tx')
- call assert_equal('"sign define Sign icon= linehl= text= texthl=', @:)
+ call assert_equal('"sign define Sign icon= linehl= numhl= text= texthl=', @:)
call feedkeys(":sign define Sign linehl=Spell\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"sign define Sign linehl=SpellBad SpellCap SpellLocal SpellRare', @:)
diff --git a/src/nvim/testdir/test_writefile.vim b/src/nvim/testdir/test_writefile.vim
index 06f9d03554..1cd5fb306a 100644
--- a/src/nvim/testdir/test_writefile.vim
+++ b/src/nvim/testdir/test_writefile.vim
@@ -32,6 +32,24 @@ func Test_writefile_fails_gently()
call assert_fails('call writefile([], [])', 'E730:')
endfunc
+func Test_writefile_fails_conversion()
+ if !has('multi_byte') || !has('iconv')
+ return
+ endif
+ set nobackup nowritebackup
+ new
+ let contents = ["line one", "line two"]
+ call writefile(contents, 'Xfile')
+ edit Xfile
+ call setline(1, ["first line", "cannot convert \u010b", "third line"])
+ call assert_fails('write ++enc=cp932')
+ call assert_equal(contents, readfile('Xfile'))
+
+ call delete('Xfile')
+ bwipe!
+ set backup& writebackup&
+endfunc
+
func SetFlag(timer)
let g:flag = 1
endfunc
diff --git a/test/functional/ex_cmds/sign_spec.lua b/test/functional/ex_cmds/sign_spec.lua
index df0f5db860..9d08a66625 100644
--- a/test/functional/ex_cmds/sign_spec.lua
+++ b/test/functional/ex_cmds/sign_spec.lua
@@ -7,7 +7,7 @@ describe('sign', function()
describe('without specifying buffer', function()
it('deletes the sign from all buffers', function()
-- place a sign with id 34 to first buffer
- nvim('command', 'sign define Foo text=+ texthl=Delimiter linehl=Comment')
+ nvim('command', 'sign define Foo text=+ texthl=Delimiter linehl=Comment numhl=Number')
local buf1 = nvim('eval', 'bufnr("%")')
nvim('command', 'sign place 34 line=3 name=Foo buffer='..buf1)
-- create a second buffer and place the sign on it as well
diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua
index ba3e44b677..29173ed7ee 100644
--- a/test/functional/ui/bufhl_spec.lua
+++ b/test/functional/ui/bufhl_spec.lua
@@ -413,5 +413,16 @@ describe('Buffer highlighting', function()
|
]])
+ set_virtual_text(0, 0, {{"x\tx\ny\ry", "Statement"}, {"aa\000bb", "Number"}}, {})
+ screen:expect([[
+ 1 + 2 {3:x^Ix^@y^My}{2:aa} |
+ ^5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5,{1:-} |
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
end)
end)
diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua
index 4fbb46ac34..6abeb0b2f4 100644
--- a/test/functional/ui/sign_spec.lua
+++ b/test/functional/ui/sign_spec.lua
@@ -17,6 +17,9 @@ describe('Signs', function()
[3] = {background = Screen.colors.Gray90},
[4] = {bold = true, reverse = true},
[5] = {reverse = true},
+ [6] = {foreground = Screen.colors.Brown},
+ [7] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey},
+ [8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
} )
end)
@@ -78,5 +81,35 @@ describe('Signs', function()
|
]])
end)
+
+ it('can combine text, linehl and numhl', function()
+ feed('ia<cr>b<cr>c<cr><esc>')
+ command('set number')
+ command('sign define piet text=>> texthl=Search')
+ command('sign define pietx linehl=ErrorMsg')
+ command('sign define pietxx numhl=Folded')
+ command('sign place 1 line=1 name=piet buffer=1')
+ command('sign place 2 line=2 name=pietx buffer=1')
+ command('sign place 3 line=3 name=pietxx buffer=1')
+ command('sign place 4 line=4 name=piet buffer=1')
+ command('sign place 5 line=4 name=pietx buffer=1')
+ command('sign place 6 line=4 name=pietxx buffer=1')
+ screen:expect([[
+ {1:>>}{6: 1 }a |
+ {2: }{6: 2 }{8:b }|
+ {2: }{7: 3 }c |
+ {1:>>}{7: 4 }{8:^ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ |
+ ]])
+ end)
end)
end)