mirror of
https://github.com/neovim/neovim.git
synced 2025-09-27 05:28:33 +00:00
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:
550
src/nvim/ops.c
550
src/nvim/ops.c
@@ -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:
|
||||
|
Reference in New Issue
Block a user