neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

textformat.c (36271B)


      1 // textformat.c: text formatting functions
      2 
      3 #include <stdbool.h>
      4 #include <stdint.h>
      5 #include <string.h>
      6 
      7 #include "nvim/ascii_defs.h"
      8 #include "nvim/buffer_defs.h"
      9 #include "nvim/change.h"
     10 #include "nvim/charset.h"
     11 #include "nvim/cursor.h"
     12 #include "nvim/drawscreen.h"
     13 #include "nvim/edit.h"
     14 #include "nvim/eval.h"
     15 #include "nvim/eval/typval_defs.h"
     16 #include "nvim/eval/vars.h"
     17 #include "nvim/ex_cmds_defs.h"
     18 #include "nvim/getchar.h"
     19 #include "nvim/globals.h"
     20 #include "nvim/indent.h"
     21 #include "nvim/indent_c.h"
     22 #include "nvim/macros_defs.h"
     23 #include "nvim/mark.h"
     24 #include "nvim/mbyte.h"
     25 #include "nvim/memline.h"
     26 #include "nvim/memory.h"
     27 #include "nvim/message.h"
     28 #include "nvim/move.h"
     29 #include "nvim/ops.h"
     30 #include "nvim/option.h"
     31 #include "nvim/option_defs.h"
     32 #include "nvim/option_vars.h"
     33 #include "nvim/os/input.h"
     34 #include "nvim/pos_defs.h"
     35 #include "nvim/search.h"
     36 #include "nvim/state_defs.h"
     37 #include "nvim/strings.h"
     38 #include "nvim/textformat.h"
     39 #include "nvim/textobject.h"
     40 #include "nvim/ui.h"
     41 #include "nvim/undo.h"
     42 #include "nvim/vim_defs.h"
     43 #include "nvim/window.h"
     44 
     45 #include "textformat.c.generated.h"
     46 
     47 static bool did_add_space = false;  ///< auto_format() added an extra space
     48                                    ///< under the cursor
     49 
     50 #define WHITECHAR(cc) (ascii_iswhite(cc) \
     51                       && !utf_iscomposing_first(utf_ptr2char((char *)get_cursor_pos_ptr() + 1)))
     52 
     53 /// Return true if format option 'x' is in effect.
     54 /// Take care of no formatting when 'paste' is set.
     55 bool has_format_option(int x)
     56  FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
     57 {
     58  if (p_paste) {
     59    return false;
     60  }
     61  return vim_strchr(curbuf->b_p_fo, x) != NULL;
     62 }
     63 
     64 /// Format text at the current insert position.
     65 ///
     66 /// If the INSCHAR_COM_LIST flag is present, then the value of second_indent
     67 /// will be the comment leader length sent to open_line().
     68 ///
     69 /// @param c  character to be inserted (can be NUL)
     70 void internal_format(int textwidth, int second_indent, int flags, bool format_only, int c)
     71 {
     72  int cc;
     73  char save_char = NUL;
     74  bool haveto_redraw = false;
     75  const bool fo_ins_blank = has_format_option(FO_INS_BLANK);
     76  const bool fo_multibyte = has_format_option(FO_MBYTE_BREAK);
     77  const bool fo_rigor_tw = has_format_option(FO_RIGOROUS_TW);
     78  const bool fo_white_par = has_format_option(FO_WHITE_PAR);
     79  bool first_line = true;
     80  colnr_T leader_len;
     81  bool no_leader = false;
     82  bool do_comments = (flags & INSCHAR_DO_COM);
     83  int has_lbr = curwin->w_p_lbr;
     84 
     85  // make sure win_charsize() counts correctly
     86  curwin->w_p_lbr = false;
     87 
     88  // When 'ai' is off we don't want a space under the cursor to be
     89  // deleted.  Replace it with an 'x' temporarily.
     90  if (!curbuf->b_p_ai && !(State & VREPLACE_FLAG)) {
     91    cc = gchar_cursor();
     92    if (ascii_iswhite(cc)) {
     93      save_char = (char)cc;
     94      pchar_cursor('x');
     95    }
     96  }
     97 
     98  // Repeat breaking lines, until the current line is not too long.
     99  while (!got_int) {
    100    int startcol;                       // Cursor column at entry
    101    int wantcol;                        // column at textwidth border
    102    int foundcol;                       // column for start of spaces
    103    int end_foundcol = 0;               // column for start of word
    104    int orig_col = 0;
    105    char *saved_text = NULL;
    106    colnr_T col;
    107    bool did_do_comment = false;
    108 
    109    colnr_T virtcol = get_nolist_virtcol() + char2cells(c != NUL ? c : gchar_cursor());
    110    if (virtcol <= (colnr_T)textwidth) {
    111      break;
    112    }
    113 
    114    if (no_leader) {
    115      do_comments = false;
    116    } else if (!(flags & INSCHAR_FORMAT)
    117               && has_format_option(FO_WRAP_COMS)) {
    118      do_comments = true;
    119    }
    120 
    121    // Don't break until after the comment leader
    122    if (do_comments) {
    123      char *line = get_cursor_line_ptr();
    124      leader_len = get_leader_len(line, NULL, false, true);
    125      if (leader_len == 0 && curbuf->b_p_cin) {
    126        // Check for a line comment after code.
    127        int comment_start = check_linecomment(line);
    128        if (comment_start != MAXCOL) {
    129          leader_len = get_leader_len(line + comment_start, NULL, false, true);
    130          if (leader_len != 0) {
    131            leader_len += comment_start;
    132          }
    133        }
    134      }
    135    } else {
    136      leader_len = 0;
    137    }
    138 
    139    // If the line doesn't start with a comment leader, then don't
    140    // start one in a following broken line.  Avoids that a %word
    141    // moved to the start of the next line causes all following lines
    142    // to start with %.
    143    if (leader_len == 0) {
    144      no_leader = true;
    145    }
    146    if (!(flags & INSCHAR_FORMAT)
    147        && leader_len == 0
    148        && !has_format_option(FO_WRAP)) {
    149      break;
    150    }
    151    if ((startcol = curwin->w_cursor.col) == 0) {
    152      break;
    153    }
    154 
    155    // find column of textwidth border
    156    coladvance(curwin, (colnr_T)textwidth);
    157    wantcol = curwin->w_cursor.col;
    158 
    159    curwin->w_cursor.col = startcol;
    160    foundcol = 0;
    161    int skip_pos = 0;
    162 
    163    // Find position to break at.
    164    // Stop at first entered white when 'formatoptions' has 'v'
    165    while ((!fo_ins_blank && !has_format_option(FO_INS_VI))
    166           || (flags & INSCHAR_FORMAT)
    167           || curwin->w_cursor.lnum != Insstart.lnum
    168           || curwin->w_cursor.col >= Insstart.col) {
    169      if (curwin->w_cursor.col == startcol && c != NUL) {
    170        cc = c;
    171      } else {
    172        cc = gchar_cursor();
    173      }
    174      if (WHITECHAR(cc)) {
    175        // remember position of blank just before text
    176        colnr_T end_col = curwin->w_cursor.col;
    177 
    178        // find start of sequence of blanks
    179        int wcc = 0;  // counter for whitespace chars
    180        while (curwin->w_cursor.col > 0 && WHITECHAR(cc)) {
    181          dec_cursor();
    182          cc = gchar_cursor();
    183 
    184          // Increment count of how many whitespace chars in this
    185          // group; we only need to know if it's more than one.
    186          if (wcc < 2) {
    187            wcc++;
    188          }
    189        }
    190        if (curwin->w_cursor.col == 0 && WHITECHAR(cc)) {
    191          break;                        // only spaces in front of text
    192        }
    193 
    194        // Don't break after a period when 'formatoptions' has 'p' and
    195        // there are less than two spaces.
    196        if (has_format_option(FO_PERIOD_ABBR) && cc == '.' && wcc < 2) {
    197          continue;
    198        }
    199 
    200        // Don't break until after the comment leader
    201        if (curwin->w_cursor.col < leader_len) {
    202          break;
    203        }
    204 
    205        if (has_format_option(FO_ONE_LETTER)) {
    206          // do not break after one-letter words
    207          if (curwin->w_cursor.col == 0) {
    208            break;              // one-letter word at begin
    209          }
    210          // do not break "#a b" when 'tw' is 2
    211          if (curwin->w_cursor.col <= leader_len) {
    212            break;
    213          }
    214          col = curwin->w_cursor.col;
    215          dec_cursor();
    216          cc = gchar_cursor();
    217 
    218          if (WHITECHAR(cc)) {
    219            continue;                   // one-letter, continue
    220          }
    221          curwin->w_cursor.col = col;
    222        }
    223 
    224        inc_cursor();
    225 
    226        end_foundcol = end_col + 1;
    227        foundcol = curwin->w_cursor.col;
    228        if (curwin->w_cursor.col <= (colnr_T)wantcol) {
    229          break;
    230        }
    231      } else if ((cc >= 0x100 || !utf_allow_break_before(cc)) && fo_multibyte) {
    232        int ncc;
    233        bool allow_break;
    234 
    235        // Break after or before a multi-byte character.
    236        if (curwin->w_cursor.col != startcol) {
    237          // Don't break until after the comment leader
    238          if (curwin->w_cursor.col < leader_len) {
    239            break;
    240          }
    241          col = curwin->w_cursor.col;
    242          inc_cursor();
    243          ncc = gchar_cursor();
    244          allow_break = utf_allow_break(cc, ncc);
    245 
    246          // If we have already checked this position, skip!
    247          if (curwin->w_cursor.col != skip_pos && allow_break) {
    248            foundcol = curwin->w_cursor.col;
    249            end_foundcol = foundcol;
    250            if (curwin->w_cursor.col <= (colnr_T)wantcol) {
    251              break;
    252            }
    253          }
    254          curwin->w_cursor.col = col;
    255        }
    256 
    257        if (curwin->w_cursor.col == 0) {
    258          break;
    259        }
    260 
    261        ncc = cc;
    262        col = curwin->w_cursor.col;
    263 
    264        dec_cursor();
    265        cc = gchar_cursor();
    266 
    267        if (WHITECHAR(cc)) {
    268          continue;                     // break with space
    269        }
    270        // Don't break until after the comment leader.
    271        if (curwin->w_cursor.col < leader_len) {
    272          break;
    273        }
    274 
    275        curwin->w_cursor.col = col;
    276        skip_pos = curwin->w_cursor.col;
    277 
    278        allow_break = utf_allow_break(cc, ncc);
    279 
    280        // Must handle this to respect line break prohibition.
    281        if (allow_break) {
    282          foundcol = curwin->w_cursor.col;
    283          end_foundcol = foundcol;
    284        }
    285        if (curwin->w_cursor.col <= (colnr_T)wantcol) {
    286          const bool ncc_allow_break = utf_allow_break_before(ncc);
    287 
    288          if (allow_break) {
    289            break;
    290          }
    291          if (!ncc_allow_break && !fo_rigor_tw) {
    292            // Enable at most 1 punct hang outside of textwidth.
    293            if (curwin->w_cursor.col == startcol) {
    294              // We are inserting a non-breakable char, postpone
    295              // line break check to next insert.
    296              end_foundcol = foundcol = 0;
    297              break;
    298            }
    299 
    300            // Neither cc nor ncc is NUL if we are here, so
    301            // it's safe to inc_cursor.
    302            col = curwin->w_cursor.col;
    303 
    304            inc_cursor();
    305            cc = ncc;
    306            ncc = gchar_cursor();
    307            // handle insert
    308            ncc = (ncc != NUL) ? ncc : c;
    309 
    310            allow_break = utf_allow_break(cc, ncc);
    311 
    312            if (allow_break) {
    313              // Break only when we are not at end of line.
    314              end_foundcol = foundcol = ncc == NUL ? 0 : curwin->w_cursor.col;
    315              break;
    316            }
    317            curwin->w_cursor.col = col;
    318          }
    319        }
    320      }
    321      if (curwin->w_cursor.col == 0) {
    322        break;
    323      }
    324      dec_cursor();
    325    }
    326 
    327    if (foundcol == 0) {                // no spaces, cannot break line
    328      curwin->w_cursor.col = startcol;
    329      break;
    330    }
    331 
    332    // Going to break the line, remove any "$" now.
    333    undisplay_dollar();
    334 
    335    // Offset between cursor position and line break is used by replace
    336    // stack functions.  MODE_VREPLACE does not use this, and backspaces
    337    // over the text instead.
    338    if (State & VREPLACE_FLAG) {
    339      orig_col = startcol;              // Will start backspacing from here
    340    } else {
    341      replace_offset = startcol - end_foundcol;
    342    }
    343 
    344    // adjust startcol for spaces that will be deleted and
    345    // characters that will remain on top line
    346    curwin->w_cursor.col = foundcol;
    347    while ((cc = gchar_cursor(), WHITECHAR(cc))
    348           && (!fo_white_par || curwin->w_cursor.col < startcol)) {
    349      inc_cursor();
    350    }
    351    startcol -= curwin->w_cursor.col;
    352    startcol = MAX(startcol, 0);
    353 
    354    if (State & VREPLACE_FLAG) {
    355      // In MODE_VREPLACE state, we will backspace over the text to be
    356      // wrapped, so save a copy now to put on the next line.
    357      saved_text = xstrnsave(get_cursor_pos_ptr(), (size_t)get_cursor_pos_len());
    358      curwin->w_cursor.col = orig_col;
    359      saved_text[startcol] = NUL;
    360 
    361      // Backspace over characters that will move to the next line
    362      if (!fo_white_par) {
    363        backspace_until_column(foundcol);
    364      }
    365    } else {
    366      // put cursor after pos. to break line
    367      if (!fo_white_par) {
    368        curwin->w_cursor.col = foundcol;
    369      }
    370    }
    371 
    372    // Split the line just before the margin.
    373    // Only insert/delete lines, but don't really redraw the window.
    374    open_line(FORWARD, OPENLINE_DELSPACES + OPENLINE_MARKFIX
    375              + (fo_white_par ? OPENLINE_KEEPTRAIL : 0)
    376              + (do_comments ? OPENLINE_DO_COM : 0)
    377              + OPENLINE_FORMAT
    378              + ((flags & INSCHAR_COM_LIST) ? OPENLINE_COM_LIST : 0),
    379              ((flags & INSCHAR_COM_LIST) ? second_indent : old_indent),
    380              &did_do_comment);
    381    if (!(flags & INSCHAR_COM_LIST)) {
    382      old_indent = 0;
    383    }
    384 
    385    // If a comment leader was inserted, may also do this on a following
    386    // line.
    387    if (did_do_comment) {
    388      no_leader = false;
    389    }
    390 
    391    replace_offset = 0;
    392    if (first_line) {
    393      if (!(flags & INSCHAR_COM_LIST)) {
    394        // This section is for auto-wrap of numeric lists.  When not
    395        // in insert mode (i.e. format_lines()), the INSCHAR_COM_LIST
    396        // flag will be set and open_line() will handle it (as seen
    397        // above).  The code here (and in get_number_indent()) will
    398        // recognize comments if needed...
    399        if (second_indent < 0 && has_format_option(FO_Q_NUMBER)) {
    400          second_indent = get_number_indent(curwin->w_cursor.lnum - 1);
    401        }
    402        if (second_indent >= 0) {
    403          if (State & VREPLACE_FLAG) {
    404            change_indent(INDENT_SET, second_indent, false, true);
    405          } else if (leader_len > 0 && second_indent - leader_len > 0) {
    406            int padding = second_indent - leader_len;
    407 
    408            // We started at the first_line of a numbered list
    409            // that has a comment.  the open_line() function has
    410            // inserted the proper comment leader and positioned
    411            // the cursor at the end of the split line.  Now we
    412            // add the additional whitespace needed after the
    413            // comment leader for the numbered list.
    414            for (int i = 0; i < padding; i++) {
    415              ins_str(S_LEN(" "));
    416            }
    417          } else {
    418            set_indent(second_indent, SIN_CHANGED);
    419          }
    420        }
    421      }
    422      first_line = false;
    423    }
    424 
    425    if (State & VREPLACE_FLAG) {
    426      // In MODE_VREPLACE state we have backspaced over the text to be
    427      // moved, now we re-insert it into the new line.
    428      ins_bytes(saved_text);
    429      xfree(saved_text);
    430    } else {
    431      // Check if cursor is not past the NUL off the line, cindent
    432      // may have added or removed indent.
    433      curwin->w_cursor.col += startcol;
    434      colnr_T len = get_cursor_line_len();
    435      curwin->w_cursor.col = MIN(curwin->w_cursor.col, len);
    436    }
    437 
    438    haveto_redraw = true;
    439    set_can_cindent(true);
    440    // moved the cursor, don't autoindent or cindent now
    441    did_ai = false;
    442    did_si = false;
    443    can_si = false;
    444    can_si_back = false;
    445    line_breakcheck();
    446  }
    447 
    448  if (save_char != NUL) {               // put back space after cursor
    449    pchar_cursor(save_char);
    450  }
    451 
    452  curwin->w_p_lbr = has_lbr;
    453 
    454  if (!format_only && haveto_redraw) {
    455    update_topline(curwin);
    456    redraw_curbuf_later(UPD_VALID);
    457  }
    458 }
    459 
    460 /// Blank lines, and lines containing only the comment leader, are left
    461 /// untouched by the formatting.  The function returns true in this
    462 /// case.  It also returns true when a line starts with the end of a comment
    463 /// ('e' in comment flags), so that this line is skipped, and not joined to the
    464 /// previous line.  A new paragraph starts after a blank line, or when the
    465 /// comment leader changes.
    466 static int fmt_check_par(linenr_T lnum, int *leader_len, char **leader_flags, bool do_comments)
    467 {
    468  char *flags = NULL;        // init for GCC
    469  char *ptr = ml_get(lnum);
    470  if (do_comments) {
    471    *leader_len = get_leader_len(ptr, leader_flags, false, true);
    472  } else {
    473    *leader_len = 0;
    474  }
    475 
    476  if (*leader_len > 0) {
    477    // Search for 'e' flag in comment leader flags.
    478    flags = *leader_flags;
    479    while (*flags && *flags != ':' && *flags != COM_END) {
    480      flags++;
    481    }
    482  }
    483 
    484  return *skipwhite(ptr + *leader_len) == NUL
    485         || (*leader_len > 0 && *flags == COM_END)
    486         || startPS(lnum, NUL, false);
    487 }
    488 
    489 /// @return  true if line "lnum" ends in a white character.
    490 static bool ends_in_white(linenr_T lnum)
    491 {
    492  char *s = ml_get(lnum);
    493 
    494  if (*s == NUL) {
    495    return false;
    496  }
    497  colnr_T l = ml_get_len(lnum) - 1;
    498  return ascii_iswhite((uint8_t)s[l]);
    499 }
    500 
    501 /// @return  true if the two comment leaders given are the same.
    502 ///
    503 /// @param lnum  The first line.  White-space is ignored.
    504 ///
    505 /// @note the whole of 'leader1' must match 'leader2_len' characters from 'leader2'.
    506 static bool same_leader(linenr_T lnum, int leader1_len, char *leader1_flags, int leader2_len,
    507                        char *leader2_flags)
    508 {
    509  int idx1 = 0;
    510  int idx2 = 0;
    511 
    512  if (leader1_len == 0) {
    513    return leader2_len == 0;
    514  }
    515 
    516  // If first leader has 'f' flag, the lines can be joined only if the
    517  // second line does not have a leader.
    518  // If first leader has 'e' flag, the lines can never be joined.
    519  // If first leader has 's' flag, the lines can only be joined if there is
    520  // some text after it and the second line has the 'm' flag.
    521  if (leader1_flags != NULL) {
    522    for (char *p = leader1_flags; *p && *p != ':'; p++) {
    523      if (*p == COM_FIRST) {
    524        return leader2_len == 0;
    525      }
    526      if (*p == COM_END) {
    527        return false;
    528      }
    529      if (*p == COM_START) {
    530        int line_len = ml_get_len(lnum);
    531        if (line_len <= leader1_len) {
    532          return false;
    533        }
    534        if (leader2_flags == NULL || leader2_len == 0) {
    535          return false;
    536        }
    537        for (p = leader2_flags; *p && *p != ':'; p++) {
    538          if (*p == COM_MIDDLE) {
    539            return true;
    540          }
    541        }
    542        return false;
    543      }
    544    }
    545  }
    546 
    547  // Get current line and next line, compare the leaders.
    548  // The first line has to be saved, only one line can be locked at a time.
    549  char *line1 = xstrnsave(ml_get(lnum), (size_t)ml_get_len(lnum));
    550  for (idx1 = 0; ascii_iswhite(line1[idx1]); idx1++) {}
    551  char *line2 = ml_get(lnum + 1);
    552  for (idx2 = 0; idx2 < leader2_len; idx2++) {
    553    if (!ascii_iswhite(line2[idx2])) {
    554      if (line1[idx1++] != line2[idx2]) {
    555        break;
    556      }
    557    } else {
    558      while (ascii_iswhite(line1[idx1])) {
    559        idx1++;
    560      }
    561    }
    562  }
    563  xfree(line1);
    564 
    565  return idx2 == leader2_len && idx1 == leader1_len;
    566 }
    567 
    568 /// Used for auto-formatting.
    569 ///
    570 /// @return  true when a paragraph starts in line "lnum".
    571 ///          false when the previous line is in the same paragraph.
    572 static bool paragraph_start(linenr_T lnum)
    573 {
    574  int leader_len = 0;                // leader len of current line
    575  char *leader_flags = NULL;         // flags for leader of current line
    576  int next_leader_len = 0;           // leader len of next line
    577  char *next_leader_flags = NULL;    // flags for leader of next line
    578 
    579  if (lnum <= 1) {
    580    return true;                // start of the file
    581  }
    582  char *p = ml_get(lnum - 1);
    583  if (*p == NUL) {
    584    return true;                // after empty line
    585  }
    586  const bool do_comments = has_format_option(FO_Q_COMS);  // format comments
    587  if (fmt_check_par(lnum - 1, &leader_len, &leader_flags, do_comments)) {
    588    return true;  // after non-paragraph line
    589  }
    590 
    591  if (fmt_check_par(lnum, &next_leader_len, &next_leader_flags, do_comments)) {
    592    return true;  // "lnum" is not a paragraph line
    593  }
    594 
    595  if (has_format_option(FO_WHITE_PAR) && !ends_in_white(lnum - 1)) {
    596    return true;                // missing trailing space in previous line.
    597  }
    598  if (has_format_option(FO_Q_NUMBER) && (get_number_indent(lnum) > 0)) {
    599    return true;                // numbered item starts in "lnum".
    600  }
    601  if (!same_leader(lnum - 1, leader_len, leader_flags,
    602                   next_leader_len, next_leader_flags)) {
    603    return true;                // change of comment leader.
    604  }
    605  return false;
    606 }
    607 
    608 /// Called after inserting or deleting text: When 'formatoptions' includes the
    609 /// 'a' flag format from the current line until the end of the paragraph.
    610 /// Keep the cursor at the same position relative to the text.
    611 /// The caller must have saved the cursor line for undo, following ones will be
    612 /// saved here.
    613 ///
    614 /// @param trailblank  when true also format with trailing blank
    615 /// @param prev_line   may start in previous line
    616 void auto_format(bool trailblank, bool prev_line)
    617 {
    618  if (!has_format_option(FO_AUTO)) {
    619    return;
    620  }
    621 
    622  pos_T pos = curwin->w_cursor;
    623  char *old = get_cursor_line_ptr();
    624 
    625  // may remove added space
    626  check_auto_format(false);
    627 
    628  // Don't format in Insert mode when the cursor is on a trailing blank, the
    629  // user might insert normal text next.  Also skip formatting when "1" is
    630  // in 'formatoptions' and there is a single character before the cursor.
    631  // Otherwise the line would be broken and when typing another non-white
    632  // next they are not joined back together.
    633  bool wasatend = (pos.col == get_cursor_line_len());
    634  if (*old != NUL && !trailblank && wasatend) {
    635    dec_cursor();
    636    int cc = gchar_cursor();
    637    if (!WHITECHAR(cc) && curwin->w_cursor.col > 0
    638        && has_format_option(FO_ONE_LETTER)) {
    639      dec_cursor();
    640    }
    641    cc = gchar_cursor();
    642    if (WHITECHAR(cc)) {
    643      curwin->w_cursor = pos;
    644      return;
    645    }
    646    curwin->w_cursor = pos;
    647  }
    648 
    649  // With the 'c' flag in 'formatoptions' and 't' missing: only format
    650  // comments.
    651  if (has_format_option(FO_WRAP_COMS) && !has_format_option(FO_WRAP)
    652      && get_leader_len(old, NULL, false, true) == 0) {
    653    return;
    654  }
    655 
    656  // May start formatting in a previous line, so that after "x" a word is
    657  // moved to the previous line if it fits there now.  Only when this is not
    658  // the start of a paragraph.
    659  if (prev_line && !paragraph_start(curwin->w_cursor.lnum)) {
    660    curwin->w_cursor.lnum--;
    661    if (u_save_cursor() == FAIL) {
    662      return;
    663    }
    664  }
    665 
    666  // Do the formatting and restore the cursor position.  "saved_cursor" will
    667  // be adjusted for the text formatting.
    668  saved_cursor = pos;
    669  format_lines(-1, false);
    670  curwin->w_cursor = saved_cursor;
    671  saved_cursor.lnum = 0;
    672 
    673  if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count) {
    674    // "cannot happen"
    675    curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
    676    coladvance(curwin, MAXCOL);
    677  } else {
    678    check_cursor_col(curwin);
    679  }
    680 
    681  // Insert mode: If the cursor is now after the end of the line while it
    682  // previously wasn't, the line was broken.  Because of the rule above we
    683  // need to add a space when 'w' is in 'formatoptions' to keep a paragraph
    684  // formatted.
    685  if (!wasatend && has_format_option(FO_WHITE_PAR)) {
    686    char *linep = get_cursor_line_ptr();
    687    colnr_T len = get_cursor_line_len();
    688    if (curwin->w_cursor.col == len) {
    689      char *plinep = xstrnsave(linep, (size_t)len + 2);
    690      plinep[len] = ' ';
    691      plinep[len + 1] = NUL;
    692      ml_replace(curwin->w_cursor.lnum, plinep, false);
    693      // remove the space later
    694      did_add_space = true;
    695    } else {
    696      // may remove added space
    697      check_auto_format(false);
    698    }
    699  }
    700 
    701  check_cursor(curwin);
    702 }
    703 
    704 /// When an extra space was added to continue a paragraph for auto-formatting,
    705 /// delete it now.  The space must be under the cursor, just after the insert
    706 /// position.
    707 ///
    708 /// @param end_insert  true when ending Insert mode
    709 void check_auto_format(bool end_insert)
    710 {
    711  if (!did_add_space) {
    712    return;
    713  }
    714 
    715  int cc = gchar_cursor();
    716  if (!WHITECHAR(cc)) {
    717    // Somehow the space was removed already.
    718    did_add_space = false;
    719  } else {
    720    int c = ' ';
    721    if (!end_insert) {
    722      inc_cursor();
    723      c = gchar_cursor();
    724      dec_cursor();
    725    }
    726    if (c != NUL) {
    727      // The space is no longer at the end of the line, delete it.
    728      del_char(false);
    729      did_add_space = false;
    730    }
    731  }
    732 }
    733 
    734 /// Find out textwidth to be used for formatting:
    735 ///      if 'textwidth' option is set, use it
    736 ///      else if 'wrapmargin' option is set, use curwin->w_view_width-'wrapmargin'
    737 ///      if invalid value, use 0.
    738 ///      Set default to window width (maximum 79) for "gq" operator.
    739 ///
    740 /// @param ff  force formatting (for "gq" command)
    741 int comp_textwidth(bool ff)
    742 {
    743  int textwidth = (int)curbuf->b_p_tw;
    744  if (textwidth == 0 && curbuf->b_p_wm) {
    745    // The width is the window width minus 'wrapmargin' minus all the
    746    // things that add to the margin.
    747    textwidth = curwin->w_view_width - (int)curbuf->b_p_wm;
    748    if (curbuf == cmdwin_buf) {
    749      textwidth -= 1;
    750    }
    751    textwidth -= win_fdccol_count(curwin);
    752    textwidth -= curwin->w_scwidth;
    753 
    754    if (curwin->w_p_nu || curwin->w_p_rnu) {
    755      textwidth -= 8;
    756    }
    757  }
    758  textwidth = MAX(textwidth, 0);
    759  if (ff && textwidth == 0) {
    760    textwidth = MIN(curwin->w_view_width - 1, 79);
    761  }
    762  return textwidth;
    763 }
    764 
    765 /// Implementation of the format operator 'gq'.
    766 ///
    767 /// @param keep_cursor  keep cursor on same text char
    768 void op_format(oparg_T *oap, bool keep_cursor)
    769 {
    770  linenr_T old_line_count = curbuf->b_ml.ml_line_count;
    771 
    772  // Place the cursor where the "gq" or "gw" command was given, so that "u"
    773  // can put it back there.
    774  curwin->w_cursor = oap->cursor_start;
    775 
    776  if (u_save((linenr_T)(oap->start.lnum - 1),
    777             (linenr_T)(oap->end.lnum + 1)) == FAIL) {
    778    return;
    779  }
    780  curwin->w_cursor = oap->start;
    781 
    782  if (oap->is_VIsual) {
    783    // When there is no change: need to remove the Visual selection
    784    redraw_curbuf_later(UPD_INVERTED);
    785  }
    786 
    787  if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
    788    // Set '[ mark at the start of the formatted area
    789    curbuf->b_op_start = oap->start;
    790  }
    791 
    792  // For "gw" remember the cursor position and put it back below (adjusted
    793  // for joined and split lines).
    794  if (keep_cursor) {
    795    saved_cursor = oap->cursor_start;
    796  }
    797 
    798  format_lines(oap->line_count, keep_cursor);
    799 
    800  // Leave the cursor at the first non-blank of the last formatted line.
    801  // If the cursor was moved one line back (e.g. with "Q}") go to the next
    802  // line, so "." will do the next lines.
    803  if (oap->end_adjusted && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
    804    curwin->w_cursor.lnum++;
    805  }
    806  beginline(BL_WHITE | BL_FIX);
    807  old_line_count = curbuf->b_ml.ml_line_count - old_line_count;
    808  msgmore(old_line_count);
    809 
    810  if ((cmdmod.cmod_flags & CMOD_LOCKMARKS) == 0) {
    811    // put '] mark on the end of the formatted area
    812    curbuf->b_op_end = curwin->w_cursor;
    813  }
    814 
    815  if (keep_cursor) {
    816    curwin->w_cursor = saved_cursor;
    817    saved_cursor.lnum = 0;
    818 
    819    // formatting may have made the cursor position invalid
    820    check_cursor(curwin);
    821  }
    822 
    823  if (oap->is_VIsual) {
    824    FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
    825      if (wp->w_old_cursor_lnum != 0) {
    826        // When lines have been inserted or deleted, adjust the end of
    827        // the Visual area to be redrawn.
    828        if (wp->w_old_cursor_lnum > wp->w_old_visual_lnum) {
    829          wp->w_old_cursor_lnum += old_line_count;
    830        } else {
    831          wp->w_old_visual_lnum += old_line_count;
    832        }
    833      }
    834    }
    835  }
    836 }
    837 
    838 /// Implementation of the format operator 'gq' for when using 'formatexpr'.
    839 void op_formatexpr(oparg_T *oap)
    840 {
    841  if (oap->is_VIsual) {
    842    // When there is no change: need to remove the Visual selection
    843    redraw_curbuf_later(UPD_INVERTED);
    844  }
    845 
    846  if (fex_format(oap->start.lnum, oap->line_count, NUL) != 0) {
    847    // As documented: when 'formatexpr' returns non-zero fall back to
    848    // internal formatting.
    849    op_format(oap, false);
    850  }
    851 }
    852 
    853 /// @param c  character to be inserted
    854 int fex_format(linenr_T lnum, long count, int c)
    855 {
    856  bool use_sandbox = was_set_insecurely(curwin, kOptFormatexpr, OPT_LOCAL);
    857  const sctx_T save_sctx = current_sctx;
    858 
    859  // Set v:lnum to the first line number and v:count to the number of lines.
    860  // Set v:char to the character to be inserted (can be NUL).
    861  set_vim_var_nr(VV_LNUM, (varnumber_T)lnum);
    862  set_vim_var_nr(VV_COUNT, (varnumber_T)count);
    863  set_vim_var_char(c);
    864 
    865  // Make a copy, the option could be changed while calling it.
    866  char *fex = xstrdup(curbuf->b_p_fex);
    867  current_sctx = curbuf->b_p_script_ctx[kBufOptFormatexpr];
    868 
    869  // Evaluate the function.
    870  if (use_sandbox) {
    871    sandbox++;
    872  }
    873  int r = (int)eval_to_number(fex, true);
    874  if (use_sandbox) {
    875    sandbox--;
    876  }
    877 
    878  set_vim_var_string(VV_CHAR, NULL, -1);
    879  xfree(fex);
    880  current_sctx = save_sctx;
    881 
    882  return r;
    883 }
    884 
    885 /// @param line_count  number of lines to format, starting at the cursor position.
    886 ///                    when negative, format until the end of the paragraph.
    887 ///
    888 /// Lines after the cursor line are saved for undo, caller must have saved the
    889 /// first line.
    890 ///
    891 /// @param avoid_fex  don't use 'formatexpr'
    892 void format_lines(linenr_T line_count, bool avoid_fex)
    893 {
    894  bool is_not_par;                  // current line not part of parag.
    895  bool next_is_not_par;             // next line not part of paragraph
    896  bool is_end_par;                  // at end of paragraph
    897  bool prev_is_end_par = false;     // prev. line not part of parag.
    898  bool next_is_start_par = false;
    899  int leader_len = 0;               // leader len of current line
    900  int next_leader_len;              // leader len of next line
    901  char *leader_flags = NULL;        // flags for leader of current line
    902  char *next_leader_flags = NULL;   // flags for leader of next line
    903  bool advance = true;
    904  int second_indent = -1;           // indent for second line (comment aware)
    905  bool first_par_line = true;
    906  int smd_save;
    907  long count;
    908  bool need_set_indent = true;      // set indent of next paragraph
    909  linenr_T first_line = curwin->w_cursor.lnum;
    910  bool force_format = false;
    911  const int old_State = State;
    912 
    913  // length of a line to force formatting: 3 * 'tw'
    914  const int max_len = comp_textwidth(true) * 3;
    915 
    916  // check for 'q', '2', 'n' and 'w' in 'formatoptions'
    917  const bool do_comments = has_format_option(FO_Q_COMS);  // format comments
    918  int do_comments_list = 0;  // format comments with 'n' or '2'
    919  const bool do_second_indent = has_format_option(FO_Q_SECOND);
    920  const bool do_number_indent = has_format_option(FO_Q_NUMBER);
    921  const bool do_trail_white = has_format_option(FO_WHITE_PAR);
    922 
    923  // Get info about the previous and current line.
    924  if (curwin->w_cursor.lnum > 1) {
    925    is_not_par = fmt_check_par(curwin->w_cursor.lnum - 1,
    926                               &leader_len, &leader_flags, do_comments);
    927  } else {
    928    is_not_par = true;
    929  }
    930  next_is_not_par = fmt_check_par(curwin->w_cursor.lnum,
    931                                  &next_leader_len, &next_leader_flags, do_comments);
    932  is_end_par = (is_not_par || next_is_not_par);
    933  if (!is_end_par && do_trail_white) {
    934    is_end_par = !ends_in_white(curwin->w_cursor.lnum - 1);
    935  }
    936 
    937  curwin->w_cursor.lnum--;
    938  for (count = line_count; count != 0 && !got_int; count--) {
    939    // Advance to next paragraph.
    940    if (advance) {
    941      curwin->w_cursor.lnum++;
    942      prev_is_end_par = is_end_par;
    943      is_not_par = next_is_not_par;
    944      leader_len = next_leader_len;
    945      leader_flags = next_leader_flags;
    946    }
    947 
    948    // The last line to be formatted.
    949    if (count == 1 || curwin->w_cursor.lnum == curbuf->b_ml.ml_line_count) {
    950      next_is_not_par = true;
    951      next_leader_len = 0;
    952      next_leader_flags = NULL;
    953    } else {
    954      next_is_not_par = fmt_check_par(curwin->w_cursor.lnum + 1,
    955                                      &next_leader_len, &next_leader_flags, do_comments);
    956      if (do_number_indent) {
    957        next_is_start_par =
    958          (get_number_indent(curwin->w_cursor.lnum + 1) > 0);
    959      }
    960    }
    961    advance = true;
    962    is_end_par = (is_not_par || next_is_not_par || next_is_start_par);
    963    if (!is_end_par && do_trail_white) {
    964      is_end_par = !ends_in_white(curwin->w_cursor.lnum);
    965    }
    966 
    967    // Skip lines that are not in a paragraph.
    968    if (is_not_par) {
    969      if (line_count < 0) {
    970        break;
    971      }
    972    } else {
    973      // For the first line of a paragraph, check indent of second line.
    974      // Don't do this for comments and empty lines.
    975      if (first_par_line
    976          && (do_second_indent || do_number_indent)
    977          && prev_is_end_par
    978          && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
    979        if (do_second_indent && !LINEEMPTY(curwin->w_cursor.lnum + 1)) {
    980          if (leader_len == 0 && next_leader_len == 0) {
    981            // no comment found
    982            second_indent =
    983              get_indent_lnum(curwin->w_cursor.lnum + 1);
    984          } else {
    985            second_indent = next_leader_len;
    986            do_comments_list = 1;
    987          }
    988        } else if (do_number_indent) {
    989          if (leader_len == 0 && next_leader_len == 0) {
    990            // no comment found
    991            second_indent =
    992              get_number_indent(curwin->w_cursor.lnum);
    993          } else {
    994            // get_number_indent() is now "comment aware"...
    995            second_indent =
    996              get_number_indent(curwin->w_cursor.lnum);
    997            do_comments_list = 1;
    998          }
    999        }
   1000      }
   1001 
   1002      // When the comment leader changes, it's the end of the paragraph.
   1003      if (curwin->w_cursor.lnum >= curbuf->b_ml.ml_line_count
   1004          || !same_leader(curwin->w_cursor.lnum,
   1005                          leader_len, leader_flags,
   1006                          next_leader_len,
   1007                          next_leader_flags)) {
   1008        // Special case: If the next line starts with a line comment
   1009        // and this line has a line comment after some text, the
   1010        // paragraph doesn't really end.
   1011        if (next_leader_flags == NULL
   1012            || strncmp(next_leader_flags, "://", 3) != 0
   1013            || check_linecomment(get_cursor_line_ptr()) == MAXCOL) {
   1014          is_end_par = true;
   1015        }
   1016      }
   1017 
   1018      // If we have got to the end of a paragraph, or the line is
   1019      // getting long, format it.
   1020      if (is_end_par || force_format) {
   1021        if (need_set_indent) {
   1022          int indent = 0;  // amount of indent needed
   1023 
   1024          // Replace indent in first line of a paragraph with minimal
   1025          // number of tabs and spaces, according to current options.
   1026          // For the very first formatted line keep the current
   1027          // indent.
   1028          if (curwin->w_cursor.lnum == first_line) {
   1029            indent = get_indent();
   1030          } else if (curbuf->b_p_lisp) {
   1031            indent = get_lisp_indent();
   1032          } else {
   1033            if (cindent_on()) {
   1034              indent = *curbuf->b_p_inde != NUL ? get_expr_indent() : get_c_indent();
   1035            } else {
   1036              indent = get_indent();
   1037            }
   1038          }
   1039          set_indent(indent, SIN_CHANGED);
   1040        }
   1041 
   1042        // put cursor on last non-space
   1043        State = MODE_NORMAL;  // don't go past end-of-line
   1044        coladvance(curwin, MAXCOL);
   1045        while (curwin->w_cursor.col && ascii_isspace(gchar_cursor())) {
   1046          dec_cursor();
   1047        }
   1048 
   1049        // do the formatting, without 'showmode'
   1050        State = MODE_INSERT;         // for open_line()
   1051        smd_save = p_smd;
   1052        p_smd = false;
   1053 
   1054        insertchar(NUL, INSCHAR_FORMAT
   1055                   + (do_comments ? INSCHAR_DO_COM : 0)
   1056                   + (do_comments && do_comments_list ? INSCHAR_COM_LIST : 0)
   1057                   + (avoid_fex ? INSCHAR_NO_FEX : 0), second_indent);
   1058 
   1059        State = old_State;
   1060        p_smd = smd_save;
   1061        // Cursor shape may have been updated (e.g. by :normal) in insertchar(),
   1062        // so it needs to be updated here.
   1063        ui_cursor_shape();
   1064 
   1065        second_indent = -1;
   1066        // at end of par.: need to set indent of next par.
   1067        need_set_indent = is_end_par;
   1068        if (is_end_par) {
   1069          // When called with a negative line count, break at the
   1070          // end of the paragraph.
   1071          if (line_count < 0) {
   1072            break;
   1073          }
   1074          first_par_line = true;
   1075        }
   1076        force_format = false;
   1077      }
   1078 
   1079      // When still in same paragraph, join the lines together.  But
   1080      // first delete the leader from the second line.
   1081      if (!is_end_par) {
   1082        advance = false;
   1083        curwin->w_cursor.lnum++;
   1084        curwin->w_cursor.col = 0;
   1085        if (line_count < 0 && u_save_cursor() == FAIL) {
   1086          break;
   1087        }
   1088        if (next_leader_len > 0) {
   1089          del_bytes(next_leader_len, false, false);
   1090          mark_col_adjust(curwin->w_cursor.lnum, 0, 0, -next_leader_len, 0);
   1091        } else if (second_indent > 0) {   // the "leader" for FO_Q_SECOND
   1092          int indent = (int)getwhitecols_curline();
   1093 
   1094          if (indent > 0) {
   1095            del_bytes(indent, false, false);
   1096            mark_col_adjust(curwin->w_cursor.lnum, 0, 0, -indent, 0);
   1097          }
   1098        }
   1099        curwin->w_cursor.lnum--;
   1100        if (do_join(2, true, false, false, false) == FAIL) {
   1101          beep_flush();
   1102          break;
   1103        }
   1104        first_par_line = false;
   1105        // If the line is getting long, format it next time
   1106        force_format = get_cursor_line_len() > max_len;
   1107      }
   1108    }
   1109    line_breakcheck();
   1110  }
   1111 }