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)

14c01f8348

Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
Jan Edmund Lazo
2025-07-10 23:16:33 -04:00
parent 441d222c0d
commit fadbc0e717
9 changed files with 518 additions and 513 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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)
{

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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)
{

View File

@@ -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