forked from aniani/vim
patch 8.2.0674: some source files are too big
Problem: Some source files are too big. Solution: Move text formatting functions to a new file. (Yegappan Lakshmanan, closes #6021)
This commit is contained in:
590
src/ops.c
590
src/ops.c
@@ -17,8 +17,6 @@
|
||||
static void shift_block(oparg_T *oap, int amount);
|
||||
static void mb_adjust_opend(oparg_T *oap);
|
||||
static int do_addsub(int op_type, pos_T *pos, int length, linenr_T Prenum1);
|
||||
static int ends_in_white(linenr_T lnum);
|
||||
static int fmt_check_par(linenr_T, int *, char_u **, int do_comments);
|
||||
|
||||
// Flags for third item in "opchars".
|
||||
#define OPF_LINES 1 // operator always works on lines
|
||||
@@ -590,46 +588,6 @@ block_insert(
|
||||
State = oldstate;
|
||||
}
|
||||
|
||||
/*
|
||||
* Stuff a string into the typeahead buffer, such that edit() will insert it
|
||||
* literally ("literally" TRUE) or interpret is as typed characters.
|
||||
*/
|
||||
void
|
||||
stuffescaped(char_u *arg, int literally)
|
||||
{
|
||||
int c;
|
||||
char_u *start;
|
||||
|
||||
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.
|
||||
start = arg;
|
||||
while ((*arg >= ' '
|
||||
#ifndef EBCDIC
|
||||
&& *arg < DEL // EBCDIC: chars above space are normal
|
||||
#endif
|
||||
)
|
||||
|| (*arg == K_SPECIAL && !literally))
|
||||
++arg;
|
||||
if (arg > start)
|
||||
stuffReadbuffLen(start, (long)(arg - start));
|
||||
|
||||
// stuff a single special character
|
||||
if (*arg != NUL)
|
||||
{
|
||||
if (has_mbyte)
|
||||
c = mb_cptr2char_adv(&arg);
|
||||
else
|
||||
c = *arg++;
|
||||
if (literally && ((c < ' ' && c != TAB) || c == DEL))
|
||||
stuffcharReadbuff(Ctrl_V);
|
||||
stuffcharReadbuff(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a delete operation.
|
||||
*
|
||||
@@ -2170,554 +2128,6 @@ theend:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE if the two comment leaders given are the same. "lnum" is
|
||||
* the first line. White-space is ignored. Note that the whole of
|
||||
* 'leader1' must match 'leader2_len' characters from 'leader2' -- webb
|
||||
*/
|
||||
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 fist 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));
|
||||
if (line1 != NULL)
|
||||
{
|
||||
for (idx1 = 0; VIM_ISWHITE(line1[idx1]); ++idx1)
|
||||
;
|
||||
line2 = ml_get(lnum + 1);
|
||||
for (idx2 = 0; idx2 < leader2_len; ++idx2)
|
||||
{
|
||||
if (!VIM_ISWHITE(line2[idx2]))
|
||||
{
|
||||
if (line1[idx1++] != line2[idx2])
|
||||
break;
|
||||
}
|
||||
else
|
||||
while (VIM_ISWHITE(line1[idx1]))
|
||||
++idx1;
|
||||
}
|
||||
vim_free(line1);
|
||||
}
|
||||
return (idx2 == leader2_len && idx1 == leader1_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implementation of the format operator 'gq'.
|
||||
*/
|
||||
static void
|
||||
op_format(
|
||||
oparg_T *oap,
|
||||
int keep_cursor) // keep cursor on same text char
|
||||
{
|
||||
long 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(INVERTED);
|
||||
|
||||
if (!cmdmod.lockmarks)
|
||||
// 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(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.lockmarks)
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (oap->is_VIsual)
|
||||
{
|
||||
win_T *wp;
|
||||
|
||||
FOR_ALL_WINDOWS(wp)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(FEAT_EVAL) || defined(PROTO)
|
||||
/*
|
||||
* 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(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);
|
||||
}
|
||||
|
||||
int
|
||||
fex_format(
|
||||
linenr_T lnum,
|
||||
long count,
|
||||
int c) // character to be inserted
|
||||
{
|
||||
int use_sandbox = was_set_insecurely((char_u *)"formatexpr",
|
||||
OPT_LOCAL);
|
||||
int r;
|
||||
char_u *fex;
|
||||
|
||||
/*
|
||||
* 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, lnum);
|
||||
set_vim_var_nr(VV_COUNT, count);
|
||||
set_vim_var_char(c);
|
||||
|
||||
// Make a copy, the option could be changed while calling it.
|
||||
fex = vim_strsave(curbuf->b_p_fex);
|
||||
if (fex == NULL)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
vim_free(fex);
|
||||
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Format "line_count" lines, starting at the cursor position.
|
||||
* When "line_count" is negative, format until the end of the paragraph.
|
||||
* Lines after the cursor line are saved for undo, caller must have saved the
|
||||
* first line.
|
||||
*/
|
||||
void
|
||||
format_lines(
|
||||
linenr_T line_count,
|
||||
int avoid_fex) // don't use 'formatexpr'
|
||||
{
|
||||
int max_len;
|
||||
int is_not_par; // current line not part of parag.
|
||||
int next_is_not_par; // next line not part of paragraph
|
||||
int is_end_par; // at end of paragraph
|
||||
int prev_is_end_par = FALSE;// prev. line not part of parag.
|
||||
int 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; // flags for leader of next line
|
||||
int do_comments; // format comments
|
||||
int do_comments_list = 0; // format comments with 'n' or '2'
|
||||
int advance = TRUE;
|
||||
int second_indent = -1; // indent for second line (comment
|
||||
// aware)
|
||||
int do_second_indent;
|
||||
int do_number_indent;
|
||||
int do_trail_white;
|
||||
int first_par_line = TRUE;
|
||||
int smd_save;
|
||||
long count;
|
||||
int need_set_indent = TRUE; // set indent of next paragraph
|
||||
int force_format = FALSE;
|
||||
int old_State = State;
|
||||
|
||||
// length of a line to force formatting: 3 * 'tw'
|
||||
max_len = comp_textwidth(TRUE) * 3;
|
||||
|
||||
// check for 'q', '2' and '1' in 'formatoptions'
|
||||
do_comments = has_format_option(FO_Q_COMS);
|
||||
do_second_indent = has_format_option(FO_Q_SECOND);
|
||||
do_number_indent = has_format_option(FO_Q_NUMBER);
|
||||
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))
|
||||
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)
|
||||
// replace indent in first line with minimal number of
|
||||
// tabs and spaces, according to current options
|
||||
(void)set_indent(get_indent(), SIN_CHANGED);
|
||||
|
||||
// put cursor on last non-space
|
||||
State = NORMAL; // don't go past end-of-line
|
||||
coladvance((colnr_T)MAXCOL);
|
||||
while (curwin->w_cursor.col && vim_isspace(gchar_cursor()))
|
||||
dec_cursor();
|
||||
|
||||
// do the formatting, without 'showmode'
|
||||
State = 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((long)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 = 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(ml_get_curline()) > (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;
|
||||
// Don't use STRLEN() inside VIM_ISWHITE(), SAS/C complains: "macro
|
||||
// invocation may call function multiple times".
|
||||
l = STRLEN(s) - 1;
|
||||
return VIM_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 -- webb.
|
||||
*/
|
||||
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(ptr, 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(ptr + *leader_len) == NUL
|
||||
|| (*leader_len > 0 && *flags == COM_END)
|
||||
|| startPS(lnum, NUL, FALSE));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE when a paragraph starts in line "lnum". Return FALSE when the
|
||||
* previous line is in the same paragraph. Used for auto-formatting.
|
||||
*/
|
||||
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; // leader len of next line
|
||||
char_u *next_leader_flags; // flags for leader of next line
|
||||
int do_comments; // format comments
|
||||
|
||||
if (lnum <= 1)
|
||||
return TRUE; // start of the file
|
||||
|
||||
p = ml_get(lnum - 1);
|
||||
if (*p == NUL)
|
||||
return TRUE; // after empty line
|
||||
|
||||
do_comments = has_format_option(FO_Q_COMS);
|
||||
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
|
||||
*
|
||||
|
Reference in New Issue
Block a user