diff options
Diffstat (limited to 'src/nvim/ex_cmds.c')
-rw-r--r-- | src/nvim/ex_cmds.c | 592 |
1 files changed, 327 insertions, 265 deletions
diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 115df815c6..0a9b6ecc57 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -34,6 +34,7 @@ #include "nvim/fileio.h" #include "nvim/fold.h" #include "nvim/getchar.h" +#include "nvim/highlight.h" #include "nvim/indent.h" #include "nvim/buffer_updates.h" #include "nvim/main.h" @@ -113,6 +114,7 @@ typedef struct { /// ":ascii" and "ga" implementation void do_ascii(const exarg_T *const eap) { + char_u *dig; int cc[MAX_MCO]; int c = utfc_ptr2char(get_cursor_pos_ptr(), cc); if (c == NUL) { @@ -140,10 +142,22 @@ void do_ascii(const exarg_T *const eap) } char buf2[20]; buf2[0] = NUL; - iobuff_len += ( - vim_snprintf((char *)IObuff + iobuff_len, sizeof(IObuff) - iobuff_len, - _("<%s>%s%s %d, Hex %02x, Octal %03o"), - transchar(c), buf1, buf2, cval, cval, cval)); + + dig = get_digraph_for_char(cval); + if (dig != NULL) { + iobuff_len += ( + vim_snprintf((char *)IObuff + iobuff_len, + sizeof(IObuff) - iobuff_len, + _("<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s"), + transchar(c), buf1, buf2, cval, cval, cval, dig)); + } else { + iobuff_len += ( + vim_snprintf((char *)IObuff + iobuff_len, + sizeof(IObuff) - iobuff_len, + _("<%s>%s%s %d, Hex %02x, Octal %03o"), + transchar(c), buf1, buf2, cval, cval, cval)); + } + c = cc[ci++]; } @@ -178,11 +192,25 @@ void do_ascii(const exarg_T *const eap) IObuff[iobuff_len++] = ' '; // Draw composing char on top of a space. } iobuff_len += utf_char2bytes(c, IObuff + iobuff_len); - iobuff_len += ( - vim_snprintf((char *)IObuff + iobuff_len, sizeof(IObuff) - iobuff_len, - (c < 0x10000 - ? _("> %d, Hex %04x, Octal %o") - : _("> %d, Hex %08x, Octal %o")), c, c, c)); + + dig = get_digraph_for_char(c); + if (dig != NULL) { + iobuff_len += ( + vim_snprintf((char *)IObuff + iobuff_len, + sizeof(IObuff) - iobuff_len, + (c < 0x10000 + ? _("> %d, Hex %04x, Oct %o, Digr %s") + : _("> %d, Hex %08x, Oct %o, Digr %s")), + c, c, c, dig)); + } else { + iobuff_len += ( + vim_snprintf((char *)IObuff + iobuff_len, + sizeof(IObuff) - iobuff_len, + (c < 0x10000 + ? _("> %d, Hex %04x, Octal %o") + : _("> %d, Hex %08x, Octal %o")), + c, c, c)); + } if (ci == MAX_MCO) { break; } @@ -306,9 +334,12 @@ static int linelen(int *has_tab) ; save = *last; *last = NUL; - len = linetabsize(line); /* get line length */ - if (has_tab != NULL) /* check for embedded TAB */ - *has_tab = (vim_strrchr(first, TAB) != NULL); + // Get line length. + len = linetabsize(line); + // Check for embedded TAB. + if (has_tab != NULL) { + *has_tab = STRRCHR(first, TAB) != NULL; + } *last = save; return len; @@ -420,6 +451,7 @@ void ex_sort(exarg_T *eap) sort_abort = sort_ic = sort_rx = sort_nr = sort_flt = 0; size_t format_found = 0; + bool change_occurred = false; // Buffer contents changed. for (p = eap->arg; *p != NUL; ++p) { if (ascii_iswhite(*p)) { @@ -580,8 +612,16 @@ void ex_sort(exarg_T *eap) // Insert the lines in the sorted order below the last one. lnum = eap->line2; - for (i = 0; i < count; ++i) { - s = ml_get(nrs[eap->forceit ? count - i - 1 : i].lnum); + for (i = 0; i < count; i++) { + const linenr_T get_lnum = nrs[eap->forceit ? count - i - 1 : i].lnum; + + // If the original line number of the line being placed is not the same + // as "lnum" (accounting for offset), we know that the buffer changed. + if (get_lnum + ((linenr_T)count - 1) != lnum) { + change_occurred = true; + } + + s = ml_get(get_lnum); if (!unique || i == 0 || (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0) { // Copy the line into a buffer, it may become invalid in @@ -610,10 +650,13 @@ void ex_sort(exarg_T *eap) if (deleted > 0) { mark_adjust(eap->line2 - deleted, eap->line2, (long)MAXLNUM, -deleted, false); + msgmore(-deleted); } else if (deleted < 0) { mark_adjust(eap->line2, MAXLNUM, -deleted, 0L, false); } - changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true); + if (change_occurred || deleted != 0) { + changed_lines(eap->line1, 0, eap->line2 + 1, -deleted, true); + } curwin->w_cursor.lnum = eap->line1; beginline(BL_WHITE | BL_FIX); @@ -715,11 +758,13 @@ void ex_retab(exarg_T *eap) memmove(new_line + start_col + len, ptr + col, (size_t)(old_len - col + 1)); ptr = new_line + start_col; - for (col = 0; col < len; col++) + for (col = 0; col < len; col++) { ptr[col] = (col < num_tabs) ? '\t' : ' '; - ml_replace(lnum, new_line, FALSE); - if (first_line == 0) + } + ml_replace(lnum, new_line, false); + if (first_line == 0) { first_line = lnum; + } last_line = lnum; ptr = new_line; col = start_col + len; @@ -771,10 +816,23 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) linenr_T last_line; // Last line in file after adding new text if (dest >= line1 && dest < line2) { - EMSG(_("E134: Move lines into themselves")); + EMSG(_("E134: Cannot move a range of lines into itself")); return FAIL; } + // Do nothing if we are not actually moving any lines. This will prevent + // the 'modified' flag from being set without cause. + if (dest == line1 - 1 || dest == line2) { + // Move the cursor as if lines were moved (see below) to be backwards + // compatible. + if (dest >= line1) { + curwin->w_cursor.lnum = dest; + } else { + curwin->w_cursor.lnum = dest + (line2 - line1) + 1; + } + return OK; + } + num_lines = line2 - line1 + 1; /* @@ -1170,16 +1228,6 @@ static void do_filter( cmd_buf = make_filter_cmd(cmd, itmp, otmp); ui_cursor_goto((int)Rows - 1, 0); - /* - * When not redirecting the output the command can write anything to the - * screen. If 'shellredir' is equal to ">", screen may be messed up by - * stderr output of external command. Clear the screen later. - * If do_in is FALSE, this could be something like ":r !cat", which may - * also mess up the screen, clear it later. - */ - if (!do_out || STRCMP(p_srr, ">") == 0 || !do_in) - redraw_later_clear(); - if (do_out) { if (u_save((linenr_T)(line2), (linenr_T)(line2 + 1)) == FAIL) { xfree(cmd_buf); @@ -1189,19 +1237,8 @@ static void do_filter( } read_linecount = curbuf->b_ml.ml_line_count; - // When call_shell() fails wait_return() is called to give the user a chance - // to read the error messages. Otherwise errors are ignored, so you can see - // the error messages from the command that appear on stdout; use 'u' to fix - // the text. // Pass on the kShellOptDoOut flag when the output is being redirected. - if (call_shell( - cmd_buf, - kShellOptFilter | shell_flags, - NULL - )) { - redraw_later_clear(); - wait_return(FALSE); - } + call_shell(cmd_buf, kShellOptFilter | shell_flags, NULL); xfree(cmd_buf); did_check_timestamps = FALSE; @@ -1302,23 +1339,17 @@ filterend: xfree(otmp); } -/* - * Call a shell to execute a command. - * When "cmd" is NULL start an interactive shell. - */ +// Call a shell to execute a command. +// When "cmd" is NULL start an interactive shell. void do_shell( char_u *cmd, int flags // may be SHELL_DOOUT when output is redirected ) { - int save_nwr; - - /* - * Disallow shell commands in restricted mode (-Z) - * Disallow shell commands from .exrc and .vimrc in current directory for - * security reasons. - */ + // Disallow shell commands in restricted mode (-Z) + // Disallow shell commands from .exrc and .vimrc in current directory for + // security reasons. if (check_restricted() || check_secure()) { msg_end(); return; @@ -1356,38 +1387,31 @@ do_shell( msg_row = Rows - 1; msg_col = 0; - if (autocmd_busy) { - if (msg_silent == 0) - redraw_later_clear(); - } else { - /* - * For ":sh" there is no need to call wait_return(), just redraw. - * Also for the Win32 GUI (the output is in a console window). - * Otherwise there is probably text on the screen that the user wants - * to read before redrawing, so call wait_return(). - */ - if (cmd == NULL - ) { - if (msg_silent == 0) - redraw_later_clear(); - need_wait_return = FALSE; - } else { - /* - * If we switch screens when starttermcap() is called, we really - * want to wait for "hit return to continue". - */ - save_nwr = no_wait_return; - wait_return(msg_silent == 0); - no_wait_return = save_nwr; - } - } - - /* display any error messages now */ + // display any error messages now display_errors(); apply_autocmds(EVENT_SHELLCMDPOST, NULL, NULL, FALSE, curbuf); } +#if !defined(UNIX) +static char *find_pipe(const char *cmd) +{ + bool inquote = false; + + for (const char *p = cmd; *p != NUL; p++) { + if (!inquote && *p == '|') { + return p; + } + if (*p == '"') { + inquote = !inquote; + } else if (rem_backslash((const char_u *)p)) { + p++; + } + } + return NULL; +} +#endif + /// Create a shell command from a command string, input redirection file and /// output redirection file. /// @@ -1441,7 +1465,7 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) // Don't do this when 'shellquote' is not empty, otherwise the // redirection would be inside the quotes. if (*p_shq == NUL) { - char *const p = strchr(buf, '|'); + char *const p = find_pipe(buf); if (p != NULL) { *p = NUL; } @@ -1449,7 +1473,7 @@ char_u *make_filter_cmd(char_u *cmd, char_u *itmp, char_u *otmp) xstrlcat(buf, " < ", len); xstrlcat(buf, (const char *)itmp, len); if (*p_shq == NUL) { - const char *const p = strchr((const char *)cmd, '|'); + const char *const p = find_pipe((const char *)cmd); if (p != NULL) { xstrlcat(buf, " ", len - 1); // Insert a space before the '|' for DOS xstrlcat(buf, p, len - 1); @@ -1589,12 +1613,14 @@ void ex_file(exarg_T *eap) } if (*eap->arg != NUL || eap->addr_count == 1) { - if (rename_buffer(eap->arg) == FAIL) + if (rename_buffer(eap->arg) == FAIL) { return; + } + redraw_tabline = true; } - if (!shortmess(SHM_FILEINFO)) { - // print full file name if :cd used + // print file name if no argument or 'F' is not in 'shortmess' + if (*eap->arg == NUL || !shortmess(SHM_FILEINFO)) { fileinfo(false, false, eap->forceit); } } @@ -2538,11 +2564,17 @@ int do_ecmd( } check_arg_idx(curwin); - // If autocommands change the cursor position or topline, we should keep - // it. Also when it moves within a line. + // If autocommands change the cursor position or topline, we should + // keep it. Also when it moves within a line. But not when it moves + // to the first non-blank. if (!equalpos(curwin->w_cursor, orig_pos)) { - newlnum = curwin->w_cursor.lnum; - newcol = curwin->w_cursor.col; + const char_u *text = get_cursor_line_ptr(); + + if (curwin->w_cursor.lnum != orig_pos.lnum + || curwin->w_cursor.col != (int)(skipwhite(text) - text)) { + newlnum = curwin->w_cursor.lnum; + newcol = curwin->w_cursor.col; + } } if (curwin->w_topline == topline) topline = 0; @@ -3307,7 +3339,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, endcolumn = (curwin->w_curswant == MAXCOL); } - if (sub_joining_lines(eap, pat, sub, cmd, !preview)) { + if (sub != NULL && sub_joining_lines(eap, pat, sub, cmd, !preview)) { return NULL; } @@ -3623,7 +3655,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, // before the cursor. len_change = (int)STRLEN(new_line) - (int)STRLEN(orig_line); curwin->w_cursor.col += len_change; - ml_replace(lnum, new_line, FALSE); + ml_replace(lnum, new_line, false); } search_match_lines = regmatch.endpos[0].lnum @@ -3665,17 +3697,14 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, msg_col = 0; gotocmdline(TRUE); - /* restore the line */ - if (orig_line != NULL) - ml_replace(lnum, orig_line, FALSE); + // restore the line + if (orig_line != NULL) { + ml_replace(lnum, orig_line, false); + } } - need_wait_return = FALSE; /* no hit-return prompt */ - if (typed == 'q' || typed == ESC || typed == Ctrl_C -#ifdef UNIX - || typed == intr_char -#endif - ) { + need_wait_return = false; // no hit-return prompt + if (typed == 'q' || typed == ESC || typed == Ctrl_C) { got_quit = true; break; } @@ -3924,9 +3953,10 @@ skip: prev_matchcol = (colnr_T)STRLEN(sub_firstline) - prev_matchcol; - if (u_savesub(lnum) != OK) + if (u_savesub(lnum) != OK) { break; - ml_replace(lnum, new_start, TRUE); + } + ml_replace(lnum, new_start, true); if (nmatch_tl > 0) { /* @@ -4432,7 +4462,7 @@ void ex_help(exarg_T *eap) buf_T *buf; int len; char_u *lang; - int old_KeyTyped = KeyTyped; + const bool old_KeyTyped = KeyTyped; if (eap != NULL) { /* @@ -4504,7 +4534,7 @@ void ex_help(exarg_T *eap) * Re-use an existing help window or open a new one. * Always open a new one for ":tab help". */ - if (!curwin->w_buffer->b_help + if (!bt_help(curwin->w_buffer) || cmdmod.tab != 0 ) { if (cmdmod.tab != 0) { @@ -4512,7 +4542,7 @@ void ex_help(exarg_T *eap) } else { wp = NULL; FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) { - if (wp2->w_buffer != NULL && wp2->w_buffer->b_help) { + if (bt_help(wp2->w_buffer)) { wp = wp2; break; } @@ -4665,49 +4695,66 @@ static int help_compare(const void *s1, const void *s2) return strcmp(p1, p2); } -/* - * Find all help tags matching "arg", sort them and return in matches[], with - * the number of matches in num_matches. - * The matches will be sorted with a "best" match algorithm. - * When "keep_lang" is TRUE try keeping the language of the current buffer. - */ -int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_lang) +// Find all help tags matching "arg", sort them and return in matches[], with +// the number of matches in num_matches. +// The matches will be sorted with a "best" match algorithm. +// When "keep_lang" is true try keeping the language of the current buffer. +int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, + bool keep_lang) { - char_u *s, *d; int i; - static char *(mtable[]) = {"*", "g*", "[*", "]*", - "/*", "/\\*", "\"*", "**", - "/\\(\\)", "/\\%(\\)", - "?", ":?", "?<CR>", "g?", "g?g?", "g??", - "/\\?", "/\\z(\\)", "\\=", ":s\\=", - "[count]", "[quotex]", - "[range]", ":[range]", - "[pattern]", "\\|", "\\%$", - "s/\\~", "s/\\U", "s/\\L", - "s/\\1", "s/\\2", "s/\\3", "s/\\9"}; - static char *(rtable[]) = {"star", "gstar", "[star", "]star", - "/star", "/\\\\star", "quotestar", "starstar", - "/\\\\(\\\\)", "/\\\\%(\\\\)", - "?", ":?", "?<CR>", "g?", "g?g?", "g??", - "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=", - "\\[count]", "\\[quotex]", - "\\[range]", ":\\[range]", - "\\[pattern]", "\\\\bar", "/\\\\%\\$", - "s/\\\\\\~", "s/\\\\U", "s/\\\\L", - "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9"}; - int flags; - - d = IObuff; /* assume IObuff is long enough! */ - - /* - * Recognize a few exceptions to the rule. Some strings that contain '*' - * with "star". Otherwise '*' is recognized as a wildcard. - */ - for (i = (int)ARRAY_SIZE(mtable); --i >= 0; ) - if (STRCMP(arg, mtable[i]) == 0) { - STRCPY(d, rtable[i]); - break; + static const char *(mtable[]) = { + "*", "g*", "[*", "]*", + "/*", "/\\*", "\"*", "**", + "/\\(\\)", "/\\%(\\)", + "?", ":?", "?<CR>", "g?", "g?g?", "g??", + "-?", "q?", "v_g?", + "/\\?", "/\\z(\\)", "\\=", ":s\\=", + "[count]", "[quotex]", + "[range]", ":[range]", + "[pattern]", "\\|", "\\%$", + "s/\\~", "s/\\U", "s/\\L", + "s/\\1", "s/\\2", "s/\\3", "s/\\9" + }; + static const char *(rtable[]) = { + "star", "gstar", "[star", "]star", + "/star", "/\\\\star", "quotestar", "starstar", + "/\\\\(\\\\)", "/\\\\%(\\\\)", + "?", ":?", "?<CR>", "g?", "g?g?", "g??", + "-?", "q?", "v_g?", + "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=", + "\\[count]", "\\[quotex]", + "\\[range]", ":\\[range]", + "\\[pattern]", "\\\\bar", "/\\\\%\\$", + "s/\\\\\\~", "s/\\\\U", "s/\\\\L", + "s/\\\\1", "s/\\\\2", "s/\\\\3", "s/\\\\9" + }; + static const char *(expr_table[]) = { + "!=?", "!~?", "<=?", "<?", "==?", "=~?", + ">=?", ">?", "is?", "isnot?" + }; + char_u *d = IObuff; // assume IObuff is long enough! + + if (STRNICMP(arg, "expr-", 5) == 0) { + // When the string starting with "expr-" and containing '?' and matches + // the table, it is taken literally. Otherwise '?' is recognized as a + // wildcard. + for (i = (int)ARRAY_SIZE(expr_table); --i >= 0; ) { + if (STRCMP(arg + 5, expr_table[i]) == 0) { + STRCPY(d, arg); + break; + } } + } else { + // Recognize a few exceptions to the rule. Some strings that contain + // '*' with "star". Otherwise '*' is recognized as a wildcard. + for (i = (int)ARRAY_SIZE(mtable); --i >= 0; ) { + if (STRCMP(arg, mtable[i]) == 0) { + STRCPY(d, rtable[i]); + break; + } + } + } if (i < 0) { /* no match in table */ /* Replace "\S" with "/\\S", etc. Otherwise every tag is matched. @@ -4739,7 +4786,7 @@ int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_la if (*arg == '(' && arg[1] == '\'') { arg++; } - for (s = arg; *s; s++) { + for (const char_u *s = arg; *s; s++) { // Replace "|" with "bar" and '"' with "quote" to match the name of // the tags for these commands. // Replace "*" with ".*" and "?" with "." to match command line @@ -4848,9 +4895,10 @@ int find_help_tags(char_u *arg, int *num_matches, char_u ***matches, int keep_la *matches = (char_u **)""; *num_matches = 0; - flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE; - if (keep_lang) + int flags = TAG_HELP | TAG_REGEXP | TAG_NAMES | TAG_VERBOSE; + if (keep_lang) { flags |= TAG_KEEP_LANG; + } if (find_tags(IObuff, num_matches, matches, flags, (int)MAXCOL, NULL) == OK && *num_matches > 0) { /* Sort the matches found on the heuristic number that is after the @@ -4912,11 +4960,7 @@ void fix_help_buffer(void) { linenr_T lnum; char_u *line; - int in_example = FALSE; - int len; - char_u *fname; - char_u *p; - char_u *rt; + bool in_example = false; // Set filetype to "help". if (STRCMP(curbuf->b_p_ft, "help") != 0) { @@ -4926,9 +4970,9 @@ void fix_help_buffer(void) } if (!syntax_present(curwin)) { - for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; ++lnum) { - line = ml_get_buf(curbuf, lnum, FALSE); - len = (int)STRLEN(line); + for (lnum = 1; lnum <= curbuf->b_ml.ml_line_count; lnum++) { + line = ml_get_buf(curbuf, lnum, false); + const size_t len = STRLEN(line); if (in_example && len > 0 && !ascii_iswhite(line[0])) { /* End of example: non-white or '<' in first column. */ if (line[0] == '<') { @@ -4936,14 +4980,14 @@ void fix_help_buffer(void) line = ml_get_buf(curbuf, lnum, TRUE); line[0] = ' '; } - in_example = FALSE; + in_example = false; } if (!in_example && len > 0) { if (line[len - 1] == '>' && (len == 1 || line[len - 2] == ' ')) { /* blank-out a '>' in the last column (start of example) */ line = ml_get_buf(curbuf, lnum, TRUE); line[len - 1] = ' '; - in_example = TRUE; + in_example = true; } else if (line[len - 1] == '~') { /* blank-out a '~' at the end of line (header marker) */ line = ml_get_buf(curbuf, lnum, TRUE); @@ -4957,7 +5001,7 @@ void fix_help_buffer(void) * In the "help.txt" and "help.abx" file, add the locally added help * files. This uses the very first line in the help file. */ - fname = path_tail(curbuf->b_fname); + char_u *const fname = path_tail(curbuf->b_fname); if (fnamecmp(fname, "help.txt") == 0 || (fnamencmp(fname, "help.", 5) == 0 && ASCII_ISALPHA(fname[5]) @@ -4972,16 +5016,15 @@ void fix_help_buffer(void) /* Go through all directories in 'runtimepath', skipping * $VIMRUNTIME. */ - p = p_rtp; + char_u *p = p_rtp; while (*p != NUL) { copy_option_part(&p, NameBuff, MAXPATHL, ","); - rt = (char_u *)vim_getenv("VIMRUNTIME"); - if (path_full_compare(rt, NameBuff, FALSE) != kEqualFiles) { + char_u *const rt = (char_u *)vim_getenv("VIMRUNTIME"); + if (rt != NULL + && path_full_compare(rt, NameBuff, false) != kEqualFiles) { int fcount; char_u **fnames; - FILE *fd; char_u *s; - int fi; vimconv_T vc; char_u *cp; @@ -4999,29 +5042,22 @@ void fix_help_buffer(void) if (gen_expand_wildcards(1, buff_list, &fcount, &fnames, EW_FILE|EW_SILENT) == OK && fcount > 0) { - int i1; - int i2; - char_u *f1; - char_u *f2; - char_u *t1; - char_u *e1; - char_u *e2; - - /* If foo.abx is found use it instead of foo.txt in - * the same directory. */ - for (i1 = 0; i1 < fcount; ++i1) { - for (i2 = 0; i2 < fcount; ++i2) { - if (i1 == i2) + // If foo.abx is found use it instead of foo.txt in + // the same directory. + for (int i1 = 0; i1 < fcount; i1++) { + for (int i2 = 0; i2 < fcount; i2++) { + if (i1 == i2) { continue; - if (fnames[i1] == NULL || fnames[i2] == NULL) - continue; - f1 = fnames[i1]; - f2 = fnames[i2]; - t1 = path_tail(f1); - if (fnamencmp(f1, f2, t1 - f1) != 0) + } + if (fnames[i1] == NULL || fnames[i2] == NULL) { continue; - e1 = vim_strrchr(t1, '.'); - e2 = vim_strrchr(path_tail(f2), '.'); + } + const char_u *const f1 = fnames[i1]; + const char_u *const f2 = fnames[i2]; + const char_u *const t1 = path_tail(f1); + const char_u *const t2 = path_tail(f2); + const char_u *const e1 = STRRCHR(t1, '.'); + const char_u *const e2 = STRRCHR(t2, '.'); if (e1 == NULL || e2 == NULL) { continue; } @@ -5032,8 +5068,10 @@ void fix_help_buffer(void) fnames[i1] = NULL; continue; } - if (fnamencmp(f1, f2, e1 - f1) != 0) + if (e1 - f1 != e2 - f2 + || fnamencmp(f1, f2, e1 - f1) != 0) { continue; + } if (fnamecmp(e1, ".txt") == 0 && fnamecmp(e2, fname + 4) == 0) { /* use .abx instead of .txt */ @@ -5042,10 +5080,12 @@ void fix_help_buffer(void) } } } - for (fi = 0; fi < fcount; ++fi) { - if (fnames[fi] == NULL) + for (int fi = 0; fi < fcount; fi++) { + if (fnames[fi] == NULL) { continue; - fd = mch_fopen((char *)fnames[fi], "r"); + } + + FILE *const fd = mch_fopen((char *)fnames[fi], "r"); if (fd == NULL) { continue; } @@ -5053,9 +5093,9 @@ void fix_help_buffer(void) if (IObuff[0] == '*' && (s = vim_strchr(IObuff + 1, '*')) != NULL) { - int this_utf = MAYBE; - /* Change tag definition to a - * reference and remove <CR>/<NL>. */ + TriState this_utf = kNone; + // Change tag definition to a + // reference and remove <CR>/<NL>. IObuff[0] = '|'; *s = '|'; while (*s != NUL) { @@ -5065,13 +5105,12 @@ void fix_help_buffer(void) * above 127 is found and no * illegal byte sequence is found. */ - if (*s >= 0x80 && this_utf != FALSE) { - int l; - - this_utf = TRUE; - l = utf_ptr2len(s); - if (l == 1) - this_utf = FALSE; + if (*s >= 0x80 && this_utf != kFalse) { + this_utf = kTrue; + const int l = utf_ptr2len(s); + if (l == 1) { + this_utf = kFalse; + } s += l - 1; } ++s; @@ -5080,18 +5119,20 @@ void fix_help_buffer(void) * conversion to the current * 'encoding' may be required. */ vc.vc_type = CONV_NONE; - convert_setup(&vc, (char_u *)( - this_utf == TRUE ? "utf-8" - : "latin1"), p_enc); - if (vc.vc_type == CONV_NONE) - /* No conversion needed. */ + convert_setup( + &vc, + (char_u *)(this_utf == kTrue ? "utf-8" : "latin1"), + p_enc); + if (vc.vc_type == CONV_NONE) { + // No conversion needed. cp = IObuff; - else { - /* Do the conversion. If it fails - * use the unconverted text. */ + } else { + // Do the conversion. If it fails + // use the unconverted text. cp = string_convert(&vc, IObuff, NULL); - if (cp == NULL) + if (cp == NULL) { cp = IObuff; + } } convert_setup(&vc, NULL, NULL); @@ -5136,22 +5177,16 @@ void ex_viusage(exarg_T *eap) /// @param tagname Name of the tags file ("tags" for English, "tags-fr" for /// French) /// @param add_help_tags Whether to add the "help-tags" tag -static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, - bool add_help_tags) +static void helptags_one(char_u *const dir, const char_u *const ext, + const char_u *const tagfname, const bool add_help_tags) { - FILE *fd_tags; - FILE *fd; garray_T ga; int filecount; char_u **files; char_u *p1, *p2; - int fi; char_u *s; - char_u *fname; - int utf8 = MAYBE; - int this_utf8; - int firstline; - int mix = FALSE; /* detected mixed encodings */ + TriState utf8 = kNone; + bool mix = false; // detected mixed encodings // Find all *.txt files. size_t dirlen = STRLCPY(NameBuff, dir, sizeof(NameBuff)); @@ -5184,7 +5219,8 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, EMSG(_(e_fnametoolong)); return; } - fd_tags = mch_fopen((char *)NameBuff, "w"); + + FILE *const fd_tags = mch_fopen((char *)NameBuff, "w"); if (fd_tags == NULL) { EMSG2(_("E152: Cannot open %s for writing"), NameBuff); FreeWild(filecount, files); @@ -5196,8 +5232,9 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, * add the "help-tags" tag. */ ga_init(&ga, (int)sizeof(char_u *), 100); - if (add_help_tags || path_full_compare((char_u *)"$VIMRUNTIME/doc", - dir, FALSE) == kEqualFiles) { + if (add_help_tags + || path_full_compare((char_u *)"$VIMRUNTIME/doc", + dir, false) == kEqualFiles) { s = xmalloc(18 + STRLEN(tagfname)); sprintf((char *)s, "help-tags\t%s\t1\n", tagfname); GA_APPEND(char_u *, &ga, s); @@ -5206,44 +5243,44 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, /* * Go over all the files and extract the tags. */ - for (fi = 0; fi < filecount && !got_int; ++fi) { - fd = mch_fopen((char *)files[fi], "r"); + for (int fi = 0; fi < filecount && !got_int; fi++) { + FILE *const fd = mch_fopen((char *)files[fi], "r"); if (fd == NULL) { EMSG2(_("E153: Unable to open %s for reading"), files[fi]); continue; } - fname = files[fi] + dirlen + 1; + const char_u *const fname = files[fi] + dirlen + 1; - firstline = TRUE; + bool firstline = true; while (!vim_fgets(IObuff, IOSIZE, fd) && !got_int) { if (firstline) { - /* Detect utf-8 file by a non-ASCII char in the first line. */ - this_utf8 = MAYBE; - for (s = IObuff; *s != NUL; ++s) + // Detect utf-8 file by a non-ASCII char in the first line. + TriState this_utf8 = kNone; + for (s = IObuff; *s != NUL; s++) { if (*s >= 0x80) { - int l; - - this_utf8 = TRUE; - l = utf_ptr2len(s); + this_utf8 = kTrue; + const int l = utf_ptr2len(s); if (l == 1) { - /* Illegal UTF-8 byte sequence. */ - this_utf8 = FALSE; + // Illegal UTF-8 byte sequence. + this_utf8 = kFalse; break; } s += l - 1; } - if (this_utf8 == MAYBE) /* only ASCII characters found */ - this_utf8 = FALSE; - if (utf8 == MAYBE) /* first file */ + } + if (this_utf8 == kNone) { // only ASCII characters found + this_utf8 = kFalse; + } + if (utf8 == kNone) { // first file utf8 = this_utf8; - else if (utf8 != this_utf8) { + } else if (utf8 != this_utf8) { EMSG2(_( "E670: Mix of help file encodings within a language: %s"), files[fi]); mix = !got_int; got_int = TRUE; } - firstline = FALSE; + firstline = false; } p1 = vim_strchr(IObuff, '*'); /* find first '*' */ while (p1 != NULL) { @@ -5313,8 +5350,9 @@ static void helptags_one(char_u *dir, char_u *ext, char_u *tagfname, } } - if (utf8 == TRUE) + if (utf8 == kTrue) { fprintf(fd_tags, "!_TAG_FILE_ENCODING\tutf-8\t//\n"); + } /* * Write the tags into the file. @@ -5473,13 +5511,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; @@ -5491,8 +5530,8 @@ static int next_sign_typenr = 1; void ex_helpclose(exarg_T *eap) { FOR_ALL_WINDOWS_IN_TAB(win, curtab) { - if (win->w_buffer->b_help) { - win_close(win, FALSE); + if (bt_help(win->w_buffer)) { + win_close(win, false); return; } } @@ -5649,11 +5688,11 @@ void ex_sign(exarg_T *eap) // Count cells and check for non-printable chars cells = 0; - for (s = arg; s < p; s += (*mb_ptr2len)(s)) { - if (!vim_isprintc((*mb_ptr2char)(s))) { + for (s = arg; s < p; s += utfc_ptr2len(s)) { + if (!vim_isprintc(utf_ptr2char(s))) { break; } - cells += (*mb_ptr2cells)(s); + cells += utf_ptr2cells(s); } // Currently must be one or two display cells if (s != p || cells < 1 || cells > 2) { @@ -5677,6 +5716,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; @@ -5903,6 +5945,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); + } + } } /* @@ -5920,25 +5972,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; } @@ -5999,7 +6059,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: { @@ -6122,7 +6183,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; |