commit 441d222c0df7574c00c5d71dfd81e7c6cc75e21f
parent c3991b8ef4f086b479ae3bc0f65aecaa07271a0a
Author: Jan Edmund Lazo <jan.lazo@mail.utoronto.ca>
Date: Thu, 10 Jul 2025 20:50:49 -0400
vim-patch:8.1.0857: indent functionality is not separated
Problem: Ignore functionality is not separated.
Solution: Move indent functionality into a new file. (Yegappan Lakshmanan,
closes vim/vim#3886)
https://github.com/vim/vim/commit/4b47162ccede0b6d9cbb9473ad870220a24fbf54
----
Partial port of v8.1.2127 by porting directly to indent_c.c,
not indent.c.
----
Co-authored-by: Bram Moolenaar <Bram@vim.org>
Diffstat:
| M | src/nvim/edit.c | | | 227 | ------------------------------------------------------------------------------- |
| M | src/nvim/indent_c.c | | | 228 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 228 insertions(+), 227 deletions(-)
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
@@ -2978,233 +2978,6 @@ static void replace_do_bs(int limit_col)
}
}
-/// Check that C-indenting is on.
-bool cindent_on(void)
- FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
-{
- return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL);
-}
-
-/// Check that "cinkeys" contains the key "keytyped",
-/// when == '*': Only if key is preceded with '*' (indent before insert)
-/// when == '!': Only if key is preceded with '!' (don't insert)
-/// when == ' ': Only if key is not preceded with '*' or '!' (indent afterwards)
-///
-/// "keytyped" can have a few special values:
-/// KEY_OPEN_FORW :
-/// KEY_OPEN_BACK :
-/// KEY_COMPLETE : Just finished completion.
-///
-/// @param keytyped key that was typed
-/// @param when condition on when to perform the check
-/// @param line_is_empty when true, accept keys with '0' before them.
-bool in_cinkeys(int keytyped, int when, bool line_is_empty)
-{
- char *look;
- bool try_match;
- bool try_match_word;
- char *p;
- bool icase;
-
- if (keytyped == NUL) {
- // Can happen with CTRL-Y and CTRL-E on a short line.
- return false;
- }
-
- if (*curbuf->b_p_inde != NUL) {
- look = curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys'
- } else {
- look = curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys'
- }
- while (*look) {
- // Find out if we want to try a match with this key, depending on
- // 'when' and a '*' or '!' before the key.
- switch (when) {
- case '*':
- try_match = (*look == '*'); break;
- case '!':
- try_match = (*look == '!'); break;
- default:
- try_match = (*look != '*') && (*look != '!'); break;
- }
- if (*look == '*' || *look == '!') {
- look++;
- }
-
- // If there is a '0', only accept a match if the line is empty.
- // But may still match when typing last char of a word.
- if (*look == '0') {
- try_match_word = try_match;
- if (!line_is_empty) {
- try_match = false;
- }
- look++;
- } else {
- try_match_word = false;
- }
-
- // Does it look like a control character?
- if (*look == '^' && look[1] >= '?' && look[1] <= '_') {
- if (try_match && keytyped == CTRL_CHR(look[1])) {
- return true;
- }
- look += 2;
-
- // 'o' means "o" command, open forward.
- // 'O' means "O" command, open backward.
- } else if (*look == 'o') {
- if (try_match && keytyped == KEY_OPEN_FORW) {
- return true;
- }
- look++;
- } else if (*look == 'O') {
- if (try_match && keytyped == KEY_OPEN_BACK) {
- return true;
- }
- look++;
-
- // 'e' means to check for "else" at start of line and just before the
- // cursor.
- } else if (*look == 'e') {
- if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) {
- p = get_cursor_line_ptr();
- if (skipwhite(p) == p + curwin->w_cursor.col - 4
- && strncmp(p + curwin->w_cursor.col - 4, "else", 4) == 0) {
- return true;
- }
- }
- look++;
-
- // ':' only causes an indent if it is at the end of a label or case
- // statement, or when it was before typing the ':' (to fix
- // class::method for C++).
- } else if (*look == ':') {
- if (try_match && keytyped == ':') {
- p = get_cursor_line_ptr();
- if (cin_iscase(p, false) || cin_isscopedecl(p) || cin_islabel()) {
- return true;
- }
- // Need to get the line again after cin_islabel().
- p = get_cursor_line_ptr();
- if (curwin->w_cursor.col > 2
- && p[curwin->w_cursor.col - 1] == ':'
- && p[curwin->w_cursor.col - 2] == ':') {
- p[curwin->w_cursor.col - 1] = ' ';
- const bool i = cin_iscase(p, false)
- || cin_isscopedecl(p)
- || cin_islabel();
- p = get_cursor_line_ptr();
- p[curwin->w_cursor.col - 1] = ':';
- if (i) {
- return true;
- }
- }
- }
- look++;
-
- // Is it a key in <>, maybe?
- } else if (*look == '<') {
- if (try_match) {
- // make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>,
- // <:> and <!> so that people can re-indent on o, O, e, 0, <,
- // >, *, : and ! keys if they really really want to.
- if (vim_strchr("<>!*oOe0:", (uint8_t)look[1]) != NULL
- && keytyped == look[1]) {
- return true;
- }
-
- if (keytyped == get_special_key_code(look + 1)) {
- return true;
- }
- }
- while (*look && *look != '>') {
- look++;
- }
- while (*look == '>') {
- look++;
- }
- // Is it a word: "=word"?
- } else if (*look == '=' && look[1] != ',' && look[1] != NUL) {
- look++;
- if (*look == '~') {
- icase = true;
- look++;
- } else {
- icase = false;
- }
- p = vim_strchr(look, ',');
- if (p == NULL) {
- p = look + strlen(look);
- }
- if ((try_match || try_match_word)
- && curwin->w_cursor.col >= (colnr_T)(p - look)) {
- bool match = false;
-
- if (keytyped == KEY_COMPLETE) {
- char *n, *s;
-
- // Just completed a word, check if it starts with "look".
- // search back for the start of a word.
- char *line = get_cursor_line_ptr();
- for (s = line + curwin->w_cursor.col; s > line; s = n) {
- n = mb_prevptr(line, s);
- if (!vim_iswordp(n)) {
- break;
- }
- }
- assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX);
- if (s + (p - look) <= line + curwin->w_cursor.col
- && (icase
- ? mb_strnicmp(s, look, (size_t)(p - look))
- : strncmp(s, look, (size_t)(p - look))) == 0) {
- match = true;
- }
- } else {
- // TODO(@brammool): multi-byte
- if (keytyped == (int)(uint8_t)p[-1]
- || (icase && keytyped < 256 && keytyped >= 0
- && TOLOWER_LOC(keytyped) == TOLOWER_LOC((uint8_t)p[-1]))) {
- char *line = get_cursor_pos_ptr();
- assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX);
- if ((curwin->w_cursor.col == (colnr_T)(p - look)
- || !vim_iswordc((uint8_t)line[-(p - look) - 1]))
- && (icase
- ? mb_strnicmp(line - (p - look), look, (size_t)(p - look))
- : strncmp(line - (p - look), look, (size_t)(p - look))) == 0) {
- match = true;
- }
- }
- }
- if (match && try_match_word && !try_match) {
- // "0=word": Check if there are only blanks before the
- // word.
- if (getwhitecols_curline() !=
- (int)(curwin->w_cursor.col - (p - look))) {
- match = false;
- }
- }
- if (match) {
- return true;
- }
- }
- look = p;
-
- // Ok, it's a boring generic character.
- } else {
- if (try_match && (uint8_t)(*look) == keytyped) {
- return true;
- }
- if (*look != NUL) {
- look++;
- }
- }
-
- // Skip over ", ".
- look = skip_to_option_part(look);
- }
- return false;
-}
-
static void ins_reg(void)
{
bool need_redraw = false;
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
@@ -12,6 +12,7 @@
#include "nvim/globals.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
+#include "nvim/keycodes.h"
#include "nvim/macros_defs.h"
#include "nvim/mark_defs.h"
#include "nvim/math.h"
@@ -232,6 +233,13 @@ bool cin_is_cinword(const char *line)
return retval;
}
+/// Check that C-indenting is on.
+bool cindent_on(void)
+ FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
+{
+ return !p_paste && (curbuf->b_p_cin || *curbuf->b_p_inde != NUL);
+}
+
// Skip over white space and C comments within the line.
// Also skip over Perl/shell comments if desired.
static const char *cin_skipcomment(const char *s)
@@ -3695,6 +3703,226 @@ static int find_match(int lookfor, linenr_T ourscope)
return FAIL;
}
+/// Check that "cinkeys" contains the key "keytyped",
+/// when == '*': Only if key is preceded with '*' (indent before insert)
+/// when == '!': Only if key is preceded with '!' (don't insert)
+/// when == ' ': Only if key is not preceded with '*' or '!' (indent afterwards)
+///
+/// "keytyped" can have a few special values:
+/// KEY_OPEN_FORW :
+/// KEY_OPEN_BACK :
+/// KEY_COMPLETE : Just finished completion.
+///
+/// @param keytyped key that was typed
+/// @param when condition on when to perform the check
+/// @param line_is_empty when true, accept keys with '0' before them.
+bool in_cinkeys(int keytyped, int when, bool line_is_empty)
+{
+ char *look;
+ bool try_match;
+ bool try_match_word;
+ char *p;
+ bool icase;
+
+ if (keytyped == NUL) {
+ // Can happen with CTRL-Y and CTRL-E on a short line.
+ return false;
+ }
+
+ if (*curbuf->b_p_inde != NUL) {
+ look = curbuf->b_p_indk; // 'indentexpr' set: use 'indentkeys'
+ } else {
+ look = curbuf->b_p_cink; // 'indentexpr' empty: use 'cinkeys'
+ }
+ while (*look) {
+ // Find out if we want to try a match with this key, depending on
+ // 'when' and a '*' or '!' before the key.
+ switch (when) {
+ case '*':
+ try_match = (*look == '*'); break;
+ case '!':
+ try_match = (*look == '!'); break;
+ default:
+ try_match = (*look != '*') && (*look != '!'); break;
+ }
+ if (*look == '*' || *look == '!') {
+ look++;
+ }
+
+ // If there is a '0', only accept a match if the line is empty.
+ // But may still match when typing last char of a word.
+ if (*look == '0') {
+ try_match_word = try_match;
+ if (!line_is_empty) {
+ try_match = false;
+ }
+ look++;
+ } else {
+ try_match_word = false;
+ }
+
+ // Does it look like a control character?
+ if (*look == '^' && look[1] >= '?' && look[1] <= '_') {
+ if (try_match && keytyped == CTRL_CHR(look[1])) {
+ return true;
+ }
+ look += 2;
+
+ // 'o' means "o" command, open forward.
+ // 'O' means "O" command, open backward.
+ } else if (*look == 'o') {
+ if (try_match && keytyped == KEY_OPEN_FORW) {
+ return true;
+ }
+ look++;
+ } else if (*look == 'O') {
+ if (try_match && keytyped == KEY_OPEN_BACK) {
+ return true;
+ }
+ look++;
+
+ // 'e' means to check for "else" at start of line and just before the
+ // cursor.
+ } else if (*look == 'e') {
+ if (try_match && keytyped == 'e' && curwin->w_cursor.col >= 4) {
+ p = get_cursor_line_ptr();
+ if (skipwhite(p) == p + curwin->w_cursor.col - 4
+ && strncmp(p + curwin->w_cursor.col - 4, "else", 4) == 0) {
+ return true;
+ }
+ }
+ look++;
+
+ // ':' only causes an indent if it is at the end of a label or case
+ // statement, or when it was before typing the ':' (to fix
+ // class::method for C++).
+ } else if (*look == ':') {
+ if (try_match && keytyped == ':') {
+ p = get_cursor_line_ptr();
+ if (cin_iscase(p, false) || cin_isscopedecl(p) || cin_islabel()) {
+ return true;
+ }
+ // Need to get the line again after cin_islabel().
+ p = get_cursor_line_ptr();
+ if (curwin->w_cursor.col > 2
+ && p[curwin->w_cursor.col - 1] == ':'
+ && p[curwin->w_cursor.col - 2] == ':') {
+ p[curwin->w_cursor.col - 1] = ' ';
+ const bool i = cin_iscase(p, false)
+ || cin_isscopedecl(p)
+ || cin_islabel();
+ p = get_cursor_line_ptr();
+ p[curwin->w_cursor.col - 1] = ':';
+ if (i) {
+ return true;
+ }
+ }
+ }
+ look++;
+
+ // Is it a key in <>, maybe?
+ } else if (*look == '<') {
+ if (try_match) {
+ // make up some named keys <o>, <O>, <e>, <0>, <>>, <<>, <*>,
+ // <:> and <!> so that people can re-indent on o, O, e, 0, <,
+ // >, *, : and ! keys if they really really want to.
+ if (vim_strchr("<>!*oOe0:", (uint8_t)look[1]) != NULL
+ && keytyped == look[1]) {
+ return true;
+ }
+
+ if (keytyped == get_special_key_code(look + 1)) {
+ return true;
+ }
+ }
+ while (*look && *look != '>') {
+ look++;
+ }
+ while (*look == '>') {
+ look++;
+ }
+ // Is it a word: "=word"?
+ } else if (*look == '=' && look[1] != ',' && look[1] != NUL) {
+ look++;
+ if (*look == '~') {
+ icase = true;
+ look++;
+ } else {
+ icase = false;
+ }
+ p = vim_strchr(look, ',');
+ if (p == NULL) {
+ p = look + strlen(look);
+ }
+ if ((try_match || try_match_word)
+ && curwin->w_cursor.col >= (colnr_T)(p - look)) {
+ bool match = false;
+
+ if (keytyped == KEY_COMPLETE) {
+ char *n, *s;
+
+ // Just completed a word, check if it starts with "look".
+ // search back for the start of a word.
+ char *line = get_cursor_line_ptr();
+ for (s = line + curwin->w_cursor.col; s > line; s = n) {
+ n = mb_prevptr(line, s);
+ if (!vim_iswordp(n)) {
+ break;
+ }
+ }
+ assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX);
+ if (s + (p - look) <= line + curwin->w_cursor.col
+ && (icase
+ ? mb_strnicmp(s, look, (size_t)(p - look))
+ : strncmp(s, look, (size_t)(p - look))) == 0) {
+ match = true;
+ }
+ } else {
+ // TODO(@brammool): multi-byte
+ if (keytyped == (int)(uint8_t)p[-1]
+ || (icase && keytyped < 256 && keytyped >= 0
+ && TOLOWER_LOC(keytyped) == TOLOWER_LOC((uint8_t)p[-1]))) {
+ char *line = get_cursor_pos_ptr();
+ assert(p >= look && (uintmax_t)(p - look) <= SIZE_MAX);
+ if ((curwin->w_cursor.col == (colnr_T)(p - look)
+ || !vim_iswordc((uint8_t)line[-(p - look) - 1]))
+ && (icase
+ ? mb_strnicmp(line - (p - look), look, (size_t)(p - look))
+ : strncmp(line - (p - look), look, (size_t)(p - look))) == 0) {
+ match = true;
+ }
+ }
+ }
+ if (match && try_match_word && !try_match) {
+ // "0=word": Check if there are only blanks before the
+ // word.
+ if (getwhitecols_curline() !=
+ (int)(curwin->w_cursor.col - (p - look))) {
+ match = false;
+ }
+ }
+ if (match) {
+ return true;
+ }
+ }
+ look = p;
+
+ // Ok, it's a boring generic character.
+ } else {
+ if (try_match && (uint8_t)(*look) == keytyped) {
+ return true;
+ }
+ if (*look != NUL) {
+ look++;
+ }
+ }
+
+ // Skip over ", ".
+ look = skip_to_option_part(look);
+ }
+ return false;
+}
+
// Do C or expression indenting on the current line.
void do_c_expr_indent(void)
{