commit fadbc0e71734c3e2b8f6623258fbb74a9709ed4a
parent 441d222c0df7574c00c5d71dfd81e7c6cc75e21f
Author: Jan Edmund Lazo <jan.lazo@mail.utoronto.ca>
Date: Thu, 10 Jul 2025 23:16:33 -0400
vim-patch:8.1.2127: the indent.c file is a bit big
Problem: The indent.c file is a bit big.
Solution: Move C-indent code a a new cindent.c file. Move other
indent-related code to indent.c. (Yegappan Lakshmanan,
closes vim/vim#5031)
https://github.com/vim/vim/commit/14c01f83487d5c53192297a710eda2b8a4ab17c9
Co-authored-by: Bram Moolenaar <Bram@vim.org>
Diffstat:
9 files changed, 518 insertions(+), 513 deletions(-)
diff --git a/src/nvim/change.c b/src/nvim/change.c
@@ -957,118 +957,6 @@ int del_bytes(colnr_T count, bool fixpos_arg, bool use_delcombine)
return OK;
}
-/// Copy the indent from ptr to the current line (and fill to size).
-/// Leaves the cursor on the first non-blank in the line.
-///
-/// @return true if the line was changed.
-bool copy_indent(int size, char *src)
-{
- char *p = NULL;
- char *line = NULL;
- int ind_len;
- int line_len = 0;
- int tab_pad;
-
- // Round 1: compute the number of characters needed for the indent
- // Round 2: copy the characters.
- for (int round = 1; round <= 2; round++) {
- int todo = size;
- ind_len = 0;
- int ind_done = 0;
- int ind_col = 0;
- char *s = src;
-
- // Count/copy the usable portion of the source line.
- while (todo > 0 && ascii_iswhite(*s)) {
- if (*s == TAB) {
- tab_pad = tabstop_padding(ind_done,
- curbuf->b_p_ts,
- curbuf->b_p_vts_array);
-
- // Stop if this tab will overshoot the target.
- if (todo < tab_pad) {
- break;
- }
- todo -= tab_pad;
- ind_done += tab_pad;
- ind_col += tab_pad;
- } else {
- todo--;
- ind_done++;
- ind_col++;
- }
- ind_len++;
-
- if (p != NULL) {
- *p++ = *s;
- }
- s++;
- }
-
- // Fill to next tabstop with a tab, if possible.
- tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, curbuf->b_p_vts_array);
-
- if ((todo >= tab_pad) && !curbuf->b_p_et) {
- todo -= tab_pad;
- ind_len++;
- ind_col += tab_pad;
-
- if (p != NULL) {
- *p++ = TAB;
- }
- }
-
- // Add tabs required for indent.
- if (!curbuf->b_p_et) {
- while (true) {
- tab_pad = tabstop_padding(ind_col,
- curbuf->b_p_ts,
- curbuf->b_p_vts_array);
- if (todo < tab_pad) {
- break;
- }
- todo -= tab_pad;
- ind_len++;
- ind_col += tab_pad;
- if (p != NULL) {
- *p++ = TAB;
- }
- }
- }
-
- // Count/add spaces required for indent.
- while (todo > 0) {
- todo--;
- ind_len++;
-
- if (p != NULL) {
- *p++ = ' ';
- }
- }
-
- if (p == NULL) {
- // Allocate memory for the result: the copied indent, new indent
- // and the rest of the line.
- line_len = get_cursor_line_len() + 1;
- assert(ind_len + line_len >= 0);
- size_t line_size;
- STRICT_ADD(ind_len, line_len, &line_size, size_t);
- line = xmalloc(line_size);
- p = line;
- }
- }
-
- // Append the original line
- memmove(p, get_cursor_line_ptr(), (size_t)line_len);
-
- // Replace the line
- ml_replace(curwin->w_cursor.lnum, line, false);
-
- // Put the cursor after the indent.
- curwin->w_cursor.col = ind_len;
- return true;
-}
-
/// open_line: Add a new line below or above the current line.
///
/// For MODE_VREPLACE state, we only add a new line when we get to the end of
diff --git a/src/nvim/edit.c b/src/nvim/edit.c
@@ -1636,204 +1636,6 @@ void undisplay_dollar(void)
redrawWinline(curwin, curwin->w_cursor.lnum);
}
-/// Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D).
-/// Keep the cursor on the same character.
-/// type == INDENT_INC increase indent (for CTRL-T or <Tab>)
-/// type == INDENT_DEC decrease indent (for CTRL-D)
-/// type == INDENT_SET set indent to "amount"
-///
-/// @param round if true, round the indent to 'shiftwidth' (only with _INC and _Dec).
-/// @param call_changed_bytes call changed_bytes()
-void change_indent(int type, int amount, int round, bool call_changed_bytes)
-{
- int insstart_less; // reduction for Insstart.col
- colnr_T orig_col = 0; // init for GCC
- char *orig_line = NULL; // init for GCC
-
- // MODE_VREPLACE state needs to know what the line was like before changing
- if (State & VREPLACE_FLAG) {
- orig_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len());
- orig_col = curwin->w_cursor.col;
- }
-
- // for the following tricks we don't want list mode
- int save_p_list = curwin->w_p_list;
- curwin->w_p_list = false;
- colnr_T vc = getvcol_nolist(&curwin->w_cursor);
- int vcol = vc;
-
- // For Replace mode we need to fix the replace stack later, which is only
- // possible when the cursor is in the indent. Remember the number of
- // characters before the cursor if it's possible.
- int start_col = curwin->w_cursor.col;
-
- // determine offset from first non-blank
- int new_cursor_col = curwin->w_cursor.col;
- beginline(BL_WHITE);
- new_cursor_col -= curwin->w_cursor.col;
-
- insstart_less = curwin->w_cursor.col;
-
- // If the cursor is in the indent, compute how many screen columns the
- // cursor is to the left of the first non-blank.
- if (new_cursor_col < 0) {
- vcol = get_indent() - vcol;
- }
-
- if (new_cursor_col > 0) { // can't fix replace stack
- start_col = -1;
- }
-
- // Set the new indent. The cursor will be put on the first non-blank.
- if (type == INDENT_SET) {
- set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0);
- } else {
- int save_State = State;
-
- // Avoid being called recursively.
- if (State & VREPLACE_FLAG) {
- State = MODE_INSERT;
- }
- shift_line(type == INDENT_DEC, round, 1, call_changed_bytes);
- State = save_State;
- }
- insstart_less -= curwin->w_cursor.col;
-
- // Try to put cursor on same character.
- // If the cursor is at or after the first non-blank in the line,
- // compute the cursor column relative to the column of the first
- // non-blank character.
- // If we are not in insert mode, leave the cursor on the first non-blank.
- // If the cursor is before the first non-blank, position it relative
- // to the first non-blank, counted in screen columns.
- if (new_cursor_col >= 0) {
- // When changing the indent while the cursor is touching it, reset
- // Insstart_col to 0.
- if (new_cursor_col == 0) {
- insstart_less = MAXCOL;
- }
- new_cursor_col += curwin->w_cursor.col;
- } else if (!(State & MODE_INSERT)) {
- new_cursor_col = curwin->w_cursor.col;
- } else {
- // Compute the screen column where the cursor should be.
- vcol = get_indent() - vcol;
- int const end_vcol = (colnr_T)((vcol < 0) ? 0 : vcol);
- curwin->w_virtcol = end_vcol;
-
- // Advance the cursor until we reach the right screen column.
- new_cursor_col = 0;
- char *const line = get_cursor_line_ptr();
- vcol = 0;
- if (*line != NUL) {
- CharsizeArg csarg;
- CSType cstype = init_charsize_arg(&csarg, curwin, 0, line);
- StrCharInfo ci = utf_ptr2StrCharInfo(line);
- while (true) {
- int next_vcol = vcol + win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width;
- if (next_vcol > end_vcol) {
- break;
- }
- vcol = next_vcol;
- ci = utfc_next(ci);
- if (*ci.ptr == NUL) {
- break;
- }
- }
- new_cursor_col = (int)(ci.ptr - line);
- }
-
- // May need to insert spaces to be able to position the cursor on
- // the right screen column.
- if (vcol != (int)curwin->w_virtcol) {
- curwin->w_cursor.col = (colnr_T)new_cursor_col;
- const size_t ptrlen = (size_t)(curwin->w_virtcol - vcol);
- char *ptr = xmallocz(ptrlen);
- memset(ptr, ' ', ptrlen);
- new_cursor_col += (int)ptrlen;
- ins_str(ptr, ptrlen);
- xfree(ptr);
- }
-
- // When changing the indent while the cursor is in it, reset
- // Insstart_col to 0.
- insstart_less = MAXCOL;
- }
-
- curwin->w_p_list = save_p_list;
- curwin->w_cursor.col = MAX(0, (colnr_T)new_cursor_col);
- curwin->w_set_curswant = true;
- changed_cline_bef_curs(curwin);
-
- // May have to adjust the start of the insert.
- if (State & MODE_INSERT) {
- if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) {
- if ((int)Insstart.col <= insstart_less) {
- Insstart.col = 0;
- } else {
- Insstart.col -= insstart_less;
- }
- }
- if ((int)ai_col <= insstart_less) {
- ai_col = 0;
- } else {
- ai_col -= insstart_less;
- }
- }
-
- // For MODE_REPLACE state, may have to fix the replace stack, if it's
- // possible. If the number of characters before the cursor decreased, need
- // to pop a few characters from the replace stack.
- // If the number of characters before the cursor increased, need to push a
- // few NULs onto the replace stack.
- if (REPLACE_NORMAL(State) && start_col >= 0) {
- while (start_col > (int)curwin->w_cursor.col) {
- replace_join(0); // remove a NUL from the replace stack
- start_col--;
- }
- while (start_col < (int)curwin->w_cursor.col) {
- replace_push_nul();
- start_col++;
- }
- }
-
- // For MODE_VREPLACE state, we also have to fix the replace stack. In this
- // case it is always possible because we backspace over the whole line and
- // then put it back again the way we wanted it.
- if (State & VREPLACE_FLAG) {
- // Save new line
- char *new_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len());
-
- // We only put back the new line up to the cursor
- new_line[curwin->w_cursor.col] = NUL;
- int new_col = curwin->w_cursor.col;
-
- // Put back original line
- ml_replace(curwin->w_cursor.lnum, orig_line, false);
- curwin->w_cursor.col = orig_col;
-
- curbuf_splice_pending++;
-
- // Backspace from cursor to start of line
- backspace_until_column(0);
-
- // Insert new stuff into line again
- ins_bytes(new_line);
-
- xfree(new_line);
-
- curbuf_splice_pending--;
-
- // TODO(bfredl): test for crazy edge cases, like we stand on a TAB or
- // something? does this even do the right text change then?
- int delta = orig_col - new_col;
- extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, new_col,
- delta < 0 ? -delta : 0,
- delta > 0 ? delta : 0,
- kExtmarkUndo);
- }
-}
-
/// Truncate the space at the end of a line. This is to be used only in an
/// insert mode. It handles fixing the replace stack for MODE_REPLACE and
/// MODE_VREPLACE modes.
@@ -2885,7 +2687,7 @@ static int replace_pop_if_nul(void)
/// encountered.
///
/// @param off offset for which NUL to remove
-static void replace_join(int off)
+void replace_join(int off)
{
for (ssize_t i = (ssize_t)kv_size(replace_stack); --i >= 0;) {
if (kv_A(replace_stack, i) == NUL && off-- <= 0) {
@@ -4403,80 +4205,6 @@ static int ins_ctrl_ey(int tc)
return c;
}
-// Try to do some very smart auto-indenting.
-// Used when inserting a "normal" character.
-static void ins_try_si(int c)
-{
- pos_T *pos;
-
- // do some very smart indenting when entering '{' or '}'
- if (((did_si || can_si_back) && c == '{') || (can_si && c == '}' && inindent(0))) {
- pos_T old_pos;
- char *ptr;
- int i;
- bool temp;
- // for '}' set indent equal to indent of line containing matching '{'
- if (c == '}' && (pos = findmatch(NULL, '{')) != NULL) {
- old_pos = curwin->w_cursor;
- // If the matching '{' has a ')' immediately before it (ignoring
- // white-space), then line up with the start of the line
- // containing the matching '(' if there is one. This handles the
- // case where an "if (..\n..) {" statement continues over multiple
- // lines -- webb
- ptr = ml_get(pos->lnum);
- i = pos->col;
- if (i > 0) { // skip blanks before '{'
- while (--i > 0 && ascii_iswhite(ptr[i])) {}
- }
- curwin->w_cursor.lnum = pos->lnum;
- curwin->w_cursor.col = i;
- if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL) {
- curwin->w_cursor = *pos;
- }
- i = get_indent();
- curwin->w_cursor = old_pos;
- if (State & VREPLACE_FLAG) {
- change_indent(INDENT_SET, i, false, true);
- } else {
- set_indent(i, SIN_CHANGED);
- }
- } else if (curwin->w_cursor.col > 0) {
- // when inserting '{' after "O" reduce indent, but not
- // more than indent of previous line
- temp = true;
- if (c == '{' && can_si_back && curwin->w_cursor.lnum > 1) {
- old_pos = curwin->w_cursor;
- i = get_indent();
- while (curwin->w_cursor.lnum > 1) {
- ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum)));
-
- // ignore empty lines and lines starting with '#'.
- if (*ptr != '#' && *ptr != NUL) {
- break;
- }
- }
- if (get_indent() >= i) {
- temp = false;
- }
- curwin->w_cursor = old_pos;
- }
- if (temp) {
- shift_line(true, false, 1, true);
- }
- }
- }
-
- // set indent of '#' always to 0
- if (curwin->w_cursor.col > 0 && can_si && c == '#' && inindent(0)) {
- // remember current indent for next line
- old_indent = get_indent();
- set_indent(0, SIN_CHANGED);
- }
-
- // Adjust ai_col, the char at this position can be deleted.
- ai_col = MIN(ai_col, curwin->w_cursor.col);
-}
-
// Get the value that w_virtcol would have when 'list' is off.
// Unless 'cpo' contains the 'L' flag.
colnr_T get_nolist_virtcol(void)
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
@@ -777,20 +777,6 @@ static void f_charcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
get_col(argvars, rettv, true);
}
-/// "cindent(lnum)" function
-static void f_cindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- pos_T pos = curwin->w_cursor;
- linenr_T lnum = tv_get_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
- curwin->w_cursor.lnum = lnum;
- rettv->vval.v_number = get_c_indent();
- curwin->w_cursor = pos;
- } else {
- rettv->vval.v_number = -1;
- }
-}
-
win_T *get_optional_window(typval_T *argvars, int idx)
{
if (argvars[idx].v_type == VAR_UNKNOWN) {
@@ -3246,17 +3232,6 @@ static void f_hostname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
rettv->vval.v_string = xstrdup(hostname);
}
-/// "indent()" function
-static void f_indent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- const linenr_T lnum = tv_get_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
- rettv->vval.v_number = get_indent_lnum(lnum);
- } else {
- rettv->vval.v_number = -1;
- }
-}
-
/// "index()" function
static void f_index(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
@@ -4415,20 +4390,6 @@ static void f_line2byte(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
}
-/// "lispindent(lnum)" function
-static void f_lispindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
-{
- const pos_T pos = curwin->w_cursor;
- const linenr_T lnum = tv_get_lnum(argvars);
- if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
- curwin->w_cursor.lnum = lnum;
- rettv->vval.v_number = get_lisp_indent();
- curwin->w_cursor = pos;
- } else {
- rettv->vval.v_number = -1;
- }
-}
-
/// "localtime()" function
static void f_localtime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
{
diff --git a/src/nvim/indent.c b/src/nvim/indent.c
@@ -15,6 +15,7 @@
#include "nvim/edit.h"
#include "nvim/errors.h"
#include "nvim/eval.h"
+#include "nvim/eval/typval.h"
#include "nvim/eval/typval_defs.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
@@ -31,6 +32,7 @@
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/move.h"
+#include "nvim/ops.h"
#include "nvim/option.h"
#include "nvim/option_defs.h"
#include "nvim/option_vars.h"
@@ -276,7 +278,7 @@ void tabstop_fromto(colnr_T start_col, colnr_T end_col, int ts_arg, const colnr_
}
/// See if two tabstop arrays contain the same values.
-bool tabstop_eq(const colnr_T *ts1, const colnr_T *ts2)
+static bool tabstop_eq(const colnr_T *ts1, const colnr_T *ts2)
{
if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) {
return false;
@@ -977,12 +979,482 @@ bool inindent(int extra)
return false;
}
+/// Handle reindenting a block of lines.
+void op_reindent(oparg_T *oap, Indenter how)
+{
+ int i = 0;
+ linenr_T first_changed = 0;
+ linenr_T last_changed = 0;
+ linenr_T start_lnum = curwin->w_cursor.lnum;
+
+ // Don't even try when 'modifiable' is off.
+ if (!MODIFIABLE(curbuf)) {
+ emsg(_(e_modifiable));
+ return;
+ }
+
+ // Save for undo. Do this once for all lines, much faster than doing this
+ // for each line separately, especially when undoing.
+ if (u_savecommon(curbuf, start_lnum - 1, start_lnum + oap->line_count,
+ start_lnum + oap->line_count, false) == OK) {
+ int amount;
+ for (i = oap->line_count - 1; i >= 0 && !got_int; i--) {
+ // it's a slow thing to do, so give feedback so there's no worry
+ // that the computer's just hung.
+
+ if (i > 1
+ && (i % 50 == 0 || i == oap->line_count - 1)
+ && oap->line_count > p_report) {
+ smsg(0, _("%" PRId64 " lines to indent... "), (int64_t)i);
+ }
+
+ // Be vi-compatible: For lisp indenting the first line is not
+ // indented, unless there is only one line.
+ if (i != oap->line_count - 1 || oap->line_count == 1
+ || how != get_lisp_indent) {
+ char *l = skipwhite(get_cursor_line_ptr());
+ if (*l == NUL) { // empty or blank line
+ amount = 0;
+ } else {
+ amount = how(); // get the indent for this line
+ }
+ if (amount >= 0 && set_indent(amount, 0)) {
+ // did change the indent, call changed_lines() later
+ if (first_changed == 0) {
+ first_changed = curwin->w_cursor.lnum;
+ }
+ last_changed = curwin->w_cursor.lnum;
+ }
+ }
+ curwin->w_cursor.lnum++;
+ curwin->w_cursor.col = 0; // make sure it's valid
+ }
+ }
+
+ // put cursor on first non-blank of indented line
+ curwin->w_cursor.lnum = start_lnum;
+ beginline(BL_SOL | BL_FIX);
+
+ // Mark changed lines so that they will be redrawn. When Visual
+ // highlighting was present, need to continue until the last line. When
+ // there is no change still need to remove the Visual highlighting.
+ if (last_changed != 0) {
+ changed_lines(curbuf, first_changed, 0,
+ oap->is_VIsual ? start_lnum + oap->line_count
+ : last_changed + 1, 0, true);
+ } else if (oap->is_VIsual) {
+ redraw_curbuf_later(UPD_INVERTED);
+ }
+
+ if (oap->line_count > p_report) {
+ i = oap->line_count - (i + 1);
+ smsg(0, NGETTEXT("%" PRId64 " line indented ", "%" PRId64 " lines indented ", i), (int64_t)i);
+ }
+ if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
+ // set '[ and '] marks
+ curbuf->b_op_start = oap->start;
+ curbuf->b_op_end = oap->end;
+ }
+}
+
+/// @return true if lines starting with '#' should be left aligned.
+bool preprocs_left(void)
+{
+ return ((curbuf->b_p_si && !curbuf->b_p_cin)
+ || (curbuf->b_p_cin && in_cinkeys('#', ' ', true)
+ && curbuf->b_ind_hash_comment == 0));
+}
+
/// @return true if the conditions are OK for smart indenting.
bool may_do_si(void)
{
return curbuf->b_p_si && !curbuf->b_p_cin && *curbuf->b_p_inde == NUL && !p_paste;
}
+// Try to do some very smart auto-indenting.
+// Used when inserting a "normal" character.
+void ins_try_si(int c)
+{
+ pos_T *pos;
+
+ // do some very smart indenting when entering '{' or '}'
+ if (((did_si || can_si_back) && c == '{') || (can_si && c == '}' && inindent(0))) {
+ pos_T old_pos;
+ char *ptr;
+ int i;
+ bool temp;
+ // for '}' set indent equal to indent of line containing matching '{'
+ if (c == '}' && (pos = findmatch(NULL, '{')) != NULL) {
+ old_pos = curwin->w_cursor;
+ // If the matching '{' has a ')' immediately before it (ignoring
+ // white-space), then line up with the start of the line
+ // containing the matching '(' if there is one. This handles the
+ // case where an "if (..\n..) {" statement continues over multiple
+ // lines -- webb
+ ptr = ml_get(pos->lnum);
+ i = pos->col;
+ if (i > 0) { // skip blanks before '{'
+ while (--i > 0 && ascii_iswhite(ptr[i])) {}
+ }
+ curwin->w_cursor.lnum = pos->lnum;
+ curwin->w_cursor.col = i;
+ if (ptr[i] == ')' && (pos = findmatch(NULL, '(')) != NULL) {
+ curwin->w_cursor = *pos;
+ }
+ i = get_indent();
+ curwin->w_cursor = old_pos;
+ if (State & VREPLACE_FLAG) {
+ change_indent(INDENT_SET, i, false, true);
+ } else {
+ set_indent(i, SIN_CHANGED);
+ }
+ } else if (curwin->w_cursor.col > 0) {
+ // when inserting '{' after "O" reduce indent, but not
+ // more than indent of previous line
+ temp = true;
+ if (c == '{' && can_si_back && curwin->w_cursor.lnum > 1) {
+ old_pos = curwin->w_cursor;
+ i = get_indent();
+ while (curwin->w_cursor.lnum > 1) {
+ ptr = skipwhite(ml_get(--(curwin->w_cursor.lnum)));
+
+ // ignore empty lines and lines starting with '#'.
+ if (*ptr != '#' && *ptr != NUL) {
+ break;
+ }
+ }
+ if (get_indent() >= i) {
+ temp = false;
+ }
+ curwin->w_cursor = old_pos;
+ }
+ if (temp) {
+ shift_line(true, false, 1, true);
+ }
+ }
+ }
+
+ // set indent of '#' always to 0
+ if (curwin->w_cursor.col > 0 && can_si && c == '#' && inindent(0)) {
+ // remember current indent for next line
+ old_indent = get_indent();
+ set_indent(0, SIN_CHANGED);
+ }
+
+ // Adjust ai_col, the char at this position can be deleted.
+ ai_col = MIN(ai_col, curwin->w_cursor.col);
+}
+
+/// Insert an indent (for <Tab> or CTRL-T) or delete an indent (for CTRL-D).
+/// Keep the cursor on the same character.
+/// type == INDENT_INC increase indent (for CTRL-T or <Tab>)
+/// type == INDENT_DEC decrease indent (for CTRL-D)
+/// type == INDENT_SET set indent to "amount"
+///
+/// @param round if true, round the indent to 'shiftwidth' (only with _INC and _Dec).
+/// @param call_changed_bytes call changed_bytes()
+void change_indent(int type, int amount, int round, bool call_changed_bytes)
+{
+ int insstart_less; // reduction for Insstart.col
+ colnr_T orig_col = 0; // init for GCC
+ char *orig_line = NULL; // init for GCC
+
+ // MODE_VREPLACE state needs to know what the line was like before changing
+ if (State & VREPLACE_FLAG) {
+ orig_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len());
+ orig_col = curwin->w_cursor.col;
+ }
+
+ // for the following tricks we don't want list mode
+ int save_p_list = curwin->w_p_list;
+ curwin->w_p_list = false;
+ colnr_T vc = getvcol_nolist(&curwin->w_cursor);
+ int vcol = vc;
+
+ // For Replace mode we need to fix the replace stack later, which is only
+ // possible when the cursor is in the indent. Remember the number of
+ // characters before the cursor if it's possible.
+ int start_col = curwin->w_cursor.col;
+
+ // determine offset from first non-blank
+ int new_cursor_col = curwin->w_cursor.col;
+ beginline(BL_WHITE);
+ new_cursor_col -= curwin->w_cursor.col;
+
+ insstart_less = curwin->w_cursor.col;
+
+ // If the cursor is in the indent, compute how many screen columns the
+ // cursor is to the left of the first non-blank.
+ if (new_cursor_col < 0) {
+ vcol = get_indent() - vcol;
+ }
+
+ if (new_cursor_col > 0) { // can't fix replace stack
+ start_col = -1;
+ }
+
+ // Set the new indent. The cursor will be put on the first non-blank.
+ if (type == INDENT_SET) {
+ set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0);
+ } else {
+ int save_State = State;
+
+ // Avoid being called recursively.
+ if (State & VREPLACE_FLAG) {
+ State = MODE_INSERT;
+ }
+ shift_line(type == INDENT_DEC, round, 1, call_changed_bytes);
+ State = save_State;
+ }
+ insstart_less -= curwin->w_cursor.col;
+
+ // Try to put cursor on same character.
+ // If the cursor is at or after the first non-blank in the line,
+ // compute the cursor column relative to the column of the first
+ // non-blank character.
+ // If we are not in insert mode, leave the cursor on the first non-blank.
+ // If the cursor is before the first non-blank, position it relative
+ // to the first non-blank, counted in screen columns.
+ if (new_cursor_col >= 0) {
+ // When changing the indent while the cursor is touching it, reset
+ // Insstart_col to 0.
+ if (new_cursor_col == 0) {
+ insstart_less = MAXCOL;
+ }
+ new_cursor_col += curwin->w_cursor.col;
+ } else if (!(State & MODE_INSERT)) {
+ new_cursor_col = curwin->w_cursor.col;
+ } else {
+ // Compute the screen column where the cursor should be.
+ vcol = get_indent() - vcol;
+ int const end_vcol = (colnr_T)((vcol < 0) ? 0 : vcol);
+ curwin->w_virtcol = end_vcol;
+
+ // Advance the cursor until we reach the right screen column.
+ new_cursor_col = 0;
+ char *const line = get_cursor_line_ptr();
+ vcol = 0;
+ if (*line != NUL) {
+ CharsizeArg csarg;
+ CSType cstype = init_charsize_arg(&csarg, curwin, 0, line);
+ StrCharInfo ci = utf_ptr2StrCharInfo(line);
+ while (true) {
+ int next_vcol = vcol + win_charsize(cstype, vcol, ci.ptr, ci.chr.value, &csarg).width;
+ if (next_vcol > end_vcol) {
+ break;
+ }
+ vcol = next_vcol;
+ ci = utfc_next(ci);
+ if (*ci.ptr == NUL) {
+ break;
+ }
+ }
+ new_cursor_col = (int)(ci.ptr - line);
+ }
+
+ // May need to insert spaces to be able to position the cursor on
+ // the right screen column.
+ if (vcol != (int)curwin->w_virtcol) {
+ curwin->w_cursor.col = (colnr_T)new_cursor_col;
+ const size_t ptrlen = (size_t)(curwin->w_virtcol - vcol);
+ char *ptr = xmallocz(ptrlen);
+ memset(ptr, ' ', ptrlen);
+ new_cursor_col += (int)ptrlen;
+ ins_str(ptr, ptrlen);
+ xfree(ptr);
+ }
+
+ // When changing the indent while the cursor is in it, reset
+ // Insstart_col to 0.
+ insstart_less = MAXCOL;
+ }
+
+ curwin->w_p_list = save_p_list;
+ curwin->w_cursor.col = MAX(0, (colnr_T)new_cursor_col);
+ curwin->w_set_curswant = true;
+ changed_cline_bef_curs(curwin);
+
+ // May have to adjust the start of the insert.
+ if (State & MODE_INSERT) {
+ if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0) {
+ if ((int)Insstart.col <= insstart_less) {
+ Insstart.col = 0;
+ } else {
+ Insstart.col -= insstart_less;
+ }
+ }
+ if ((int)ai_col <= insstart_less) {
+ ai_col = 0;
+ } else {
+ ai_col -= insstart_less;
+ }
+ }
+
+ // For MODE_REPLACE state, may have to fix the replace stack, if it's
+ // possible. If the number of characters before the cursor decreased, need
+ // to pop a few characters from the replace stack.
+ // If the number of characters before the cursor increased, need to push a
+ // few NULs onto the replace stack.
+ if (REPLACE_NORMAL(State) && start_col >= 0) {
+ while (start_col > (int)curwin->w_cursor.col) {
+ replace_join(0); // remove a NUL from the replace stack
+ start_col--;
+ }
+ while (start_col < (int)curwin->w_cursor.col) {
+ replace_push_nul();
+ start_col++;
+ }
+ }
+
+ // For MODE_VREPLACE state, we also have to fix the replace stack. In this
+ // case it is always possible because we backspace over the whole line and
+ // then put it back again the way we wanted it.
+ if (State & VREPLACE_FLAG) {
+ // Save new line
+ char *new_line = xstrnsave(get_cursor_line_ptr(), (size_t)get_cursor_line_len());
+
+ // We only put back the new line up to the cursor
+ new_line[curwin->w_cursor.col] = NUL;
+ int new_col = curwin->w_cursor.col;
+
+ // Put back original line
+ ml_replace(curwin->w_cursor.lnum, orig_line, false);
+ curwin->w_cursor.col = orig_col;
+
+ curbuf_splice_pending++;
+
+ // Backspace from cursor to start of line
+ backspace_until_column(0);
+
+ // Insert new stuff into line again
+ ins_bytes(new_line);
+
+ xfree(new_line);
+
+ curbuf_splice_pending--;
+
+ // TODO(bfredl): test for crazy edge cases, like we stand on a TAB or
+ // something? does this even do the right text change then?
+ int delta = orig_col - new_col;
+ extmark_splice_cols(curbuf, (int)curwin->w_cursor.lnum - 1, new_col,
+ delta < 0 ? -delta : 0,
+ delta > 0 ? delta : 0,
+ kExtmarkUndo);
+ }
+}
+
+/// Copy the indent from ptr to the current line (and fill to size).
+/// Leaves the cursor on the first non-blank in the line.
+///
+/// @return true if the line was changed.
+bool copy_indent(int size, char *src)
+{
+ char *p = NULL;
+ char *line = NULL;
+ int ind_len;
+ int line_len = 0;
+ int tab_pad;
+
+ // Round 1: compute the number of characters needed for the indent
+ // Round 2: copy the characters.
+ for (int round = 1; round <= 2; round++) {
+ int todo = size;
+ ind_len = 0;
+ int ind_done = 0;
+ int ind_col = 0;
+ char *s = src;
+
+ // Count/copy the usable portion of the source line.
+ while (todo > 0 && ascii_iswhite(*s)) {
+ if (*s == TAB) {
+ tab_pad = tabstop_padding(ind_done,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
+
+ // Stop if this tab will overshoot the target.
+ if (todo < tab_pad) {
+ break;
+ }
+ todo -= tab_pad;
+ ind_done += tab_pad;
+ ind_col += tab_pad;
+ } else {
+ todo--;
+ ind_done++;
+ ind_col++;
+ }
+ ind_len++;
+
+ if (p != NULL) {
+ *p++ = *s;
+ }
+ s++;
+ }
+
+ // Fill to next tabstop with a tab, if possible.
+ tab_pad = tabstop_padding(ind_done, curbuf->b_p_ts, curbuf->b_p_vts_array);
+
+ if ((todo >= tab_pad) && !curbuf->b_p_et) {
+ todo -= tab_pad;
+ ind_len++;
+ ind_col += tab_pad;
+
+ if (p != NULL) {
+ *p++ = TAB;
+ }
+ }
+
+ // Add tabs required for indent.
+ if (!curbuf->b_p_et) {
+ while (true) {
+ tab_pad = tabstop_padding(ind_col,
+ curbuf->b_p_ts,
+ curbuf->b_p_vts_array);
+ if (todo < tab_pad) {
+ break;
+ }
+ todo -= tab_pad;
+ ind_len++;
+ ind_col += tab_pad;
+ if (p != NULL) {
+ *p++ = TAB;
+ }
+ }
+ }
+
+ // Count/add spaces required for indent.
+ while (todo > 0) {
+ todo--;
+ ind_len++;
+
+ if (p != NULL) {
+ *p++ = ' ';
+ }
+ }
+
+ if (p == NULL) {
+ // Allocate memory for the result: the copied indent, new indent
+ // and the rest of the line.
+ line_len = get_cursor_line_len() + 1;
+ assert(ind_len + line_len >= 0);
+ size_t line_size;
+ STRICT_ADD(ind_len, line_len, &line_size, size_t);
+ line = xmalloc(line_size);
+ p = line;
+ }
+ }
+
+ // Append the original line
+ memmove(p, get_cursor_line_ptr(), (size_t)line_len);
+
+ // Replace the line
+ ml_replace(curwin->w_cursor.lnum, line, false);
+
+ // Put the cursor after the indent.
+ curwin->w_cursor.col = ind_len;
+ return true;
+}
+
/// Give a "resulting text too long" error and maybe set got_int.
static void emsg_text_too_long(void)
{
@@ -1474,3 +1946,28 @@ void fix_indent(void)
do_c_expr_indent();
}
}
+
+/// "indent()" function
+void f_indent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const linenr_T lnum = tv_get_lnum(argvars);
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
+ rettv->vval.v_number = get_indent_lnum(lnum);
+ } else {
+ rettv->vval.v_number = -1;
+ }
+}
+
+/// "lispindent(lnum)" function
+void f_lispindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ const pos_T pos = curwin->w_cursor;
+ const linenr_T lnum = tv_get_lnum(argvars);
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
+ curwin->w_cursor.lnum = lnum;
+ rettv->vval.v_number = get_lisp_indent();
+ curwin->w_cursor = pos;
+ } else {
+ rettv->vval.v_number = -1;
+ }
+}
diff --git a/src/nvim/indent.h b/src/nvim/indent.h
@@ -1,6 +1,7 @@
#pragma once
#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
+#include "nvim/normal_defs.h"
#include "nvim/pos_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
@@ -14,6 +15,8 @@ enum {
SIN_NOMARK = 8, ///< don't adjust extmarks
};
+typedef int (*Indenter)(void);
+
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "indent.h.generated.h"
#endif
diff --git a/src/nvim/indent_c.c b/src/nvim/indent_c.c
@@ -9,6 +9,7 @@
#include "nvim/charset.h"
#include "nvim/cursor.h"
#include "nvim/edit.h"
+#include "nvim/eval/typval.h"
#include "nvim/globals.h"
#include "nvim/indent.h"
#include "nvim/indent_c.h"
@@ -3932,3 +3933,17 @@ void do_c_expr_indent(void)
fixthisline(get_c_indent);
}
}
+
+/// "cindent(lnum)" function
+void f_cindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
+{
+ pos_T pos = curwin->w_cursor;
+ linenr_T lnum = tv_get_lnum(argvars);
+ if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count) {
+ curwin->w_cursor.lnum = lnum;
+ rettv->vval.v_number = get_c_indent();
+ curwin->w_cursor = pos;
+ } else {
+ rettv->vval.v_number = -1;
+ }
+}
diff --git a/src/nvim/indent_c.h b/src/nvim/indent_c.h
@@ -1,5 +1,6 @@
#pragma once
+#include "nvim/eval/typval_defs.h"
#include "nvim/pos_defs.h" // IWYU pragma: keep
#include "nvim/types_defs.h" // IWYU pragma: keep
diff --git a/src/nvim/ops.c b/src/nvim/ops.c
@@ -723,84 +723,6 @@ static void block_insert(oparg_T *oap, const char *s, size_t slen, bool b_insert
changed_lines(curbuf, oap->start.lnum + 1, 0, oap->end.lnum + 1, 0, true);
}
-/// Handle reindenting a block of lines.
-void op_reindent(oparg_T *oap, Indenter how)
-{
- int i = 0;
- linenr_T first_changed = 0;
- linenr_T last_changed = 0;
- linenr_T start_lnum = curwin->w_cursor.lnum;
-
- // Don't even try when 'modifiable' is off.
- if (!MODIFIABLE(curbuf)) {
- emsg(_(e_modifiable));
- return;
- }
-
- // Save for undo. Do this once for all lines, much faster than doing this
- // for each line separately, especially when undoing.
- if (u_savecommon(curbuf, start_lnum - 1, start_lnum + oap->line_count,
- start_lnum + oap->line_count, false) == OK) {
- int amount;
- for (i = oap->line_count - 1; i >= 0 && !got_int; i--) {
- // it's a slow thing to do, so give feedback so there's no worry
- // that the computer's just hung.
-
- if (i > 1
- && (i % 50 == 0 || i == oap->line_count - 1)
- && oap->line_count > p_report) {
- smsg(0, _("%" PRId64 " lines to indent... "), (int64_t)i);
- }
-
- // Be vi-compatible: For lisp indenting the first line is not
- // indented, unless there is only one line.
- if (i != oap->line_count - 1 || oap->line_count == 1
- || how != get_lisp_indent) {
- char *l = skipwhite(get_cursor_line_ptr());
- if (*l == NUL) { // empty or blank line
- amount = 0;
- } else {
- amount = how(); // get the indent for this line
- }
- if (amount >= 0 && set_indent(amount, 0)) {
- // did change the indent, call changed_lines() later
- if (first_changed == 0) {
- first_changed = curwin->w_cursor.lnum;
- }
- last_changed = curwin->w_cursor.lnum;
- }
- }
- curwin->w_cursor.lnum++;
- curwin->w_cursor.col = 0; // make sure it's valid
- }
- }
-
- // put cursor on first non-blank of indented line
- curwin->w_cursor.lnum = start_lnum;
- beginline(BL_SOL | BL_FIX);
-
- // Mark changed lines so that they will be redrawn. When Visual
- // highlighting was present, need to continue until the last line. When
- // there is no change still need to remove the Visual highlighting.
- if (last_changed != 0) {
- changed_lines(curbuf, first_changed, 0,
- oap->is_VIsual ? start_lnum + oap->line_count
- : last_changed + 1, 0, true);
- } else if (oap->is_VIsual) {
- redraw_curbuf_later(UPD_INVERTED);
- }
-
- if (oap->line_count > p_report) {
- i = oap->line_count - (i + 1);
- smsg(0, NGETTEXT("%" PRId64 " line indented ", "%" PRId64 " lines indented ", i), (int64_t)i);
- }
- if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
- // set '[ and '] marks
- curbuf->b_op_start = oap->start;
- curbuf->b_op_end = oap->end;
- }
-}
-
// Keep the last expression line here, for repeating.
static char *expr_line = NULL;
@@ -3738,14 +3660,6 @@ void adjust_cursor_eol(void)
}
}
-/// @return true if lines starting with '#' should be left aligned.
-bool preprocs_left(void)
-{
- return ((curbuf->b_p_si && !curbuf->b_p_cin)
- || (curbuf->b_p_cin && in_cinkeys('#', ' ', true)
- && curbuf->b_ind_hash_comment == 0));
-}
-
/// @return the character name of the register with the given number
int get_register_name(int num)
{
diff --git a/src/nvim/ops.h b/src/nvim/ops.h
@@ -33,8 +33,6 @@ struct block_def {
colnr_T start_char_vcols; ///< number of vcols of pre-block char
};
-typedef int (*Indenter)(void);
-
/// flags for do_put()
enum {
PUT_FIXINDENT = 1, ///< make indent look nice