vim-patch:8.2.0674: some source files are too big (#19959)

Problem:    Some source files are too big.
Solution:   Move text formatting functions to a new file. (Yegappan
            Lakshmanan, closes vim/vim#6021)
11abd09521

Cherry-pick set_can_cindent() from patch 8.1.2062.
Cherry-pick global old_indent from patch 8.2.2127.
This commit is contained in:
zeertzjq
2022-08-26 22:37:20 +08:00
committed by GitHub
parent 2ecb4076df
commit 0b72e23bf1
11 changed files with 1176 additions and 1159 deletions

View File

@@ -51,6 +51,7 @@
#include "nvim/state.h"
#include "nvim/strings.h"
#include "nvim/terminal.h"
#include "nvim/textformat.h"
#include "nvim/ui.h"
#include "nvim/undo.h"
#include "nvim/vim.h"
@@ -1311,34 +1312,6 @@ int insert_reg(int regname, bool literally_arg)
return retval;
}
/// Stuff a string into the typeahead buffer, such that edit() will insert it
/// literally ("literally" true) or interpret is as typed characters.
static void stuffescaped(const char *arg, bool literally)
{
while (*arg != NUL) {
// Stuff a sequence of normal ASCII characters, that's fast. Also
// stuff K_SPECIAL to get the effect of a special key when "literally"
// is true.
const char *const start = arg;
while ((*arg >= ' ' && *arg < DEL) || ((uint8_t)(*arg) == K_SPECIAL
&& !literally)) {
arg++;
}
if (arg > start) {
stuffReadbuffLen(start, (arg - start));
}
// stuff a single special character
if (*arg != NUL) {
const int c = mb_cptr2char_adv((const char_u **)&arg);
if (literally && ((c < ' ' && c != TAB) || c == DEL)) {
stuffcharReadbuff(Ctrl_V);
}
stuffcharReadbuff(c);
}
}
}
/// If "regname" is a special register, return true and store a pointer to its
/// value in "argp".
///
@@ -4218,527 +4191,6 @@ theend:
return ret;
}
/// @return true if the two comment leaders given are the same.
///
/// @param lnum The first line. White-space is ignored.
///
/// @note the whole of 'leader1' must match 'leader2_len' characters from 'leader2'.
static int same_leader(linenr_T lnum, int leader1_len, char_u *leader1_flags, int leader2_len,
char_u *leader2_flags)
{
int idx1 = 0, idx2 = 0;
char_u *p;
char_u *line1;
char_u *line2;
if (leader1_len == 0) {
return leader2_len == 0;
}
/*
* If first leader has 'f' flag, the lines can be joined only if the
* second line does not have a leader.
* If first leader has 'e' flag, the lines can never be joined.
* If first leader has 's' flag, the lines can only be joined if there is
* some text after it and the second line has the 'm' flag.
*/
if (leader1_flags != NULL) {
for (p = leader1_flags; *p && *p != ':'; p++) {
if (*p == COM_FIRST) {
return leader2_len == 0;
}
if (*p == COM_END) {
return false;
}
if (*p == COM_START) {
if (*(ml_get(lnum) + leader1_len) == NUL) {
return false;
}
if (leader2_flags == NULL || leader2_len == 0) {
return false;
}
for (p = leader2_flags; *p && *p != ':'; p++) {
if (*p == COM_MIDDLE) {
return true;
}
}
return false;
}
}
}
/*
* Get current line and next line, compare the leaders.
* The first line has to be saved, only one line can be locked at a time.
*/
line1 = vim_strsave(ml_get(lnum));
for (idx1 = 0; ascii_iswhite(line1[idx1]); idx1++) {}
line2 = ml_get(lnum + 1);
for (idx2 = 0; idx2 < leader2_len; idx2++) {
if (!ascii_iswhite(line2[idx2])) {
if (line1[idx1++] != line2[idx2]) {
break;
}
} else {
while (ascii_iswhite(line1[idx1])) {
idx1++;
}
}
}
xfree(line1);
return idx2 == leader2_len && idx1 == leader1_len;
}
/// Implementation of the format operator 'gq'.
///
/// @param keep_cursor keep cursor on same text char
static void op_format(oparg_T *oap, int keep_cursor)
{
linenr_T old_line_count = curbuf->b_ml.ml_line_count;
// Place the cursor where the "gq" or "gw" command was given, so that "u"
// can put it back there.
curwin->w_cursor = oap->cursor_start;
if (u_save((linenr_T)(oap->start.lnum - 1),
(linenr_T)(oap->end.lnum + 1)) == FAIL) {
return;
}
curwin->w_cursor = oap->start;
if (oap->is_VIsual) {
// When there is no change: need to remove the Visual selection
redraw_curbuf_later(UPD_INVERTED);
}
if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
// Set '[ mark at the start of the formatted area
curbuf->b_op_start = oap->start;
}
// For "gw" remember the cursor position and put it back below (adjusted
// for joined and split lines).
if (keep_cursor) {
saved_cursor = oap->cursor_start;
}
format_lines((linenr_T)oap->line_count, keep_cursor);
/*
* Leave the cursor at the first non-blank of the last formatted line.
* If the cursor was moved one line back (e.g. with "Q}") go to the next
* line, so "." will do the next lines.
*/
if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
curwin->w_cursor.lnum++;
}
beginline(BL_WHITE | BL_FIX);
old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
msgmore(old_line_count);
if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
// put '] mark on the end of the formatted area
curbuf->b_op_end = curwin->w_cursor;
}
if (keep_cursor) {
curwin->w_cursor = saved_cursor;
saved_cursor.lnum = 0;
// formatting may have made the cursor position invalid
check_cursor();
}
if (oap->is_VIsual) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_old_cursor_lnum != 0) {
// When lines have been inserted or deleted, adjust the end of
// the Visual area to be redrawn.
if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum) {
wp->w_old_cursor_lnum += old_line_count;
} else {
wp->w_old_visual_lnum += old_line_count;
}
}
}
}
}
/// Implementation of the format operator 'gq' for when using 'formatexpr'.
static void op_formatexpr(oparg_T *oap)
{
if (oap->is_VIsual) {
// When there is no change: need to remove the Visual selection
redraw_curbuf_later(UPD_INVERTED);
}
if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0) {
// As documented: when 'formatexpr' returns non-zero fall back to
// internal formatting.
op_format(oap, false);
}
}
/// @param c character to be inserted
int fex_format(linenr_T lnum, long count, int c)
{
int use_sandbox = was_set_insecurely(curwin, "formatexpr", OPT_LOCAL);
int r;
// Set v:lnum to the first line number and v:count to the number of lines.
// Set v:char to the character to be inserted (can be NUL).
set_vim_var_nr(VV_LNUM, (varnumber_T)lnum);
set_vim_var_nr(VV_COUNT, (varnumber_T)count);
set_vim_var_char(c);
// Make a copy, the option could be changed while calling it.
char *fex = xstrdup(curbuf->b_p_fex);
// Evaluate the function.
if (use_sandbox) {
sandbox++;
}
r = (int)eval_to_number(fex);
if (use_sandbox) {
sandbox--;
}
set_vim_var_string(VV_CHAR, NULL, -1);
xfree(fex);
return r;
}
/// @param line_count number of lines to format, starting at the cursor position.
/// when negative, format until the end of the paragraph.
///
/// Lines after the cursor line are saved for undo, caller must have saved the
/// first line.
///
/// @param avoid_fex don't use 'formatexpr'
void format_lines(linenr_T line_count, int avoid_fex)
{
bool is_not_par; // current line not part of parag.
bool next_is_not_par; // next line not part of paragraph
bool is_end_par; // at end of paragraph
bool prev_is_end_par = false; // prev. line not part of parag.
bool next_is_start_par = false;
int leader_len = 0; // leader len of current line
int next_leader_len; // leader len of next line
char_u *leader_flags = NULL; // flags for leader of current line
char_u *next_leader_flags = NULL; // flags for leader of next line
bool advance = true;
int second_indent = -1; // indent for second line (comment aware)
bool first_par_line = true;
int smd_save;
long count;
bool need_set_indent = true; // set indent of next paragraph
linenr_T first_line = curwin->w_cursor.lnum;
bool force_format = false;
const int old_State = State;
// length of a line to force formatting: 3 * 'tw'
const int max_len = comp_textwidth(true) * 3;
// check for 'q', '2' and '1' in 'formatoptions'
const bool do_comments = has_format_option(FO_Q_COMS); // format comments
int do_comments_list = 0; // format comments with 'n' or '2'
const bool do_second_indent = has_format_option(FO_Q_SECOND);
const bool do_number_indent = has_format_option(FO_Q_NUMBER);
const bool do_trail_white = has_format_option(FO_WHITE_PAR);
// Get info about the previous and current line.
if (curwin->w_cursor.lnum > 1) {
is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1,
&leader_len, &leader_flags, do_comments);
} else {
is_not_par = true;
}
next_is_not_par = fmt_check_par(curwin->w_cursor.lnum,
&next_leader_len, &next_leader_flags, do_comments
);
is_end_par = (is_not_par || next_is_not_par);
if (!is_end_par && do_trail_white) {
is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
}
curwin->w_cursor.lnum--;
for (count = line_count; count != 0 && !got_int; count--) {
// Advance to next paragraph.
if (advance) {
curwin->w_cursor.lnum++;
prev_is_end_par = is_end_par;
is_not_par = next_is_not_par;
leader_len = next_leader_len;
leader_flags = next_leader_flags;
}
/*
* The last line to be formatted.
*/
if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
next_is_not_par = true;
next_leader_len = 0;
next_leader_flags = NULL;
} else {
next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1,
&next_leader_len, &next_leader_flags, do_comments
);
if (do_number_indent) {
next_is_start_par =
(get_number_indent(curwin->w_cursor.lnum + 1) > 0);
}
}
advance = true;
is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
if (!is_end_par && do_trail_white) {
is_end_par = !ends_in_white(curwin->w_cursor.lnum);
}
/*
* Skip lines that are not in a paragraph.
*/
if (is_not_par) {
if (line_count < 0) {
break;
}
} else {
/*
* For the first line of a paragraph, check indent of second line.
* Don't do this for comments and empty lines.
*/
if (first_par_line
&& (do_second_indent || do_number_indent)
&& prev_is_end_par
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) {
if (leader_len == 0 && next_leader_len == 0) {
// no comment found
second_indent =
get_indent_lnum(curwin->w_cursor.lnum + 1);
} else {
second_indent = next_leader_len;
do_comments_list = 1;
}
} else if (do_number_indent) {
if (leader_len == 0 && next_leader_len == 0) {
// no comment found
second_indent =
get_number_indent(curwin->w_cursor.lnum);
} else {
// get_number_indent() is now "comment aware"...
second_indent =
get_number_indent(curwin->w_cursor.lnum);
do_comments_list = 1;
}
}
}
/*
* When the comment leader changes, it's the end of the paragraph.
*/
if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
|| !same_leader(curwin->w_cursor.lnum,
leader_len, leader_flags,
next_leader_len,
next_leader_flags)) {
// Special case: If the next line starts with a line comment
// and this line has a line comment after some text, the
// paragraph doesn't really end.
if (next_leader_flags == NULL
|| STRNCMP(next_leader_flags, "://", 3) != 0
|| check_linecomment(get_cursor_line_ptr()) == MAXCOL) {
is_end_par = true;
}
}
/*
* If we have got to the end of a paragraph, or the line is
* getting long, format it.
*/
if (is_end_par || force_format) {
if (need_set_indent) {
int indent = 0; // amount of indent needed
// Replace indent in first line of a paragraph with minimal
// number of tabs and spaces, according to current options.
// For the very first formatted line keep the current
// indent.
if (curwin->w_cursor.lnum == first_line) {
indent = get_indent();
} else if (curbuf->b_p_lisp) {
indent = get_lisp_indent();
} else {
if (cindent_on()) {
indent = *curbuf->b_p_inde != NUL ? get_expr_indent() : get_c_indent();
} else {
indent = get_indent();
}
}
(void)set_indent(indent, SIN_CHANGED);
}
// put cursor on last non-space
State = MODE_NORMAL; // don't go past end-of-line
coladvance(MAXCOL);
while (curwin->w_cursor.col && ascii_isspace(gchar_cursor())) {
dec_cursor();
}
// do the formatting, without 'showmode'
State = MODE_INSERT; // for open_line()
smd_save = p_smd;
p_smd = false;
insertchar(NUL, INSCHAR_FORMAT
+ (do_comments ? INSCHAR_DO_COM : 0)
+ (do_comments && do_comments_list
? INSCHAR_COM_LIST : 0)
+ (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
State = old_State;
p_smd = smd_save;
second_indent = -1;
// at end of par.: need to set indent of next par.
need_set_indent = is_end_par;
if (is_end_par) {
// When called with a negative line count, break at the
// end of the paragraph.
if (line_count < 0) {
break;
}
first_par_line = true;
}
force_format = false;
}
/*
* When still in same paragraph, join the lines together. But
* first delete the leader from the second line.
*/
if (!is_end_par) {
advance = false;
curwin->w_cursor.lnum++;
curwin->w_cursor.col = 0;
if (line_count < 0 && u_save_cursor() == FAIL) {
break;
}
if (next_leader_len > 0) {
(void)del_bytes(next_leader_len, false, false);
mark_col_adjust(curwin->w_cursor.lnum, (colnr_T)0, 0L,
(long)-next_leader_len, 0);
} else if (second_indent > 0) { // the "leader" for FO_Q_SECOND
int indent = (int)getwhitecols_curline();
if (indent > 0) {
(void)del_bytes(indent, false, false);
mark_col_adjust(curwin->w_cursor.lnum,
(colnr_T)0, 0L, (long)-indent, 0);
}
}
curwin->w_cursor.lnum--;
if (do_join(2, true, false, false, false) == FAIL) {
beep_flush();
break;
}
first_par_line = false;
// If the line is getting long, format it next time
if (STRLEN(get_cursor_line_ptr()) > (size_t)max_len) {
force_format = true;
} else {
force_format = false;
}
}
}
line_breakcheck();
}
}
/// @return true if line "lnum" ends in a white character.
static int ends_in_white(linenr_T lnum)
{
char_u *s = ml_get(lnum);
size_t l;
if (*s == NUL) {
return false;
}
l = STRLEN(s) - 1;
return ascii_iswhite(s[l]);
}
/// Blank lines, and lines containing only the comment leader, are left
/// untouched by the formatting. The function returns true in this
/// case. It also returns true when a line starts with the end of a comment
/// ('e' in comment flags), so that this line is skipped, and not joined to the
/// previous line. A new paragraph starts after a blank line, or when the
/// comment leader changes.
static int fmt_check_par(linenr_T lnum, int *leader_len, char_u **leader_flags, int do_comments)
{
char_u *flags = NULL; // init for GCC
char_u *ptr;
ptr = ml_get(lnum);
if (do_comments) {
*leader_len = get_leader_len((char *)ptr, (char **)leader_flags, false, true);
} else {
*leader_len = 0;
}
if (*leader_len > 0) {
/*
* Search for 'e' flag in comment leader flags.
*/
flags = *leader_flags;
while (*flags && *flags != ':' && *flags != COM_END) {
flags++;
}
}
return *skipwhite((char *)ptr + *leader_len) == NUL
|| (*leader_len > 0 && *flags == COM_END)
|| startPS(lnum, NUL, false);
}
/// Used for auto-formatting.
///
/// @return true when a paragraph starts in line "lnum".
/// false when the previous line is in the same paragraph.
int paragraph_start(linenr_T lnum)
{
char_u *p;
int leader_len = 0; // leader len of current line
char_u *leader_flags = NULL; // flags for leader of current line
int next_leader_len = 0; // leader len of next line
char_u *next_leader_flags = NULL; // flags for leader of next line
if (lnum <= 1) {
return true; // start of the file
}
p = ml_get(lnum - 1);
if (*p == NUL) {
return true; // after empty line
}
const bool do_comments = has_format_option(FO_Q_COMS); // format comments
if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments)) {
return true; // after non-paragraph line
}
if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments)) {
return true; // "lnum" is not a paragraph line
}
if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1)) {
return true; // missing trailing space in previous line.
}
if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0)) {
return true; // numbered item starts in "lnum".
}
if (!same_leader(lnum - 1, leader_len, leader_flags,
next_leader_len, next_leader_flags)) {
return true; // change of comment leader.
}
return false;
}
/// prepare a few things for block mode yank/delete/tilde
///
/// for delete: