diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/nvim/buffer_defs.h | 4 | ||||
| -rw-r--r-- | src/nvim/indent.c | 80 | ||||
| -rw-r--r-- | src/nvim/option.c | 11 | ||||
| -rw-r--r-- | src/nvim/optionstr.c | 10 | ||||
| -rw-r--r-- | src/nvim/plines.c | 4 | ||||
| -rw-r--r-- | src/nvim/regexp_nfa.c | 45 | ||||
| -rw-r--r-- | src/nvim/testdir/test_breakindent.vim | 156 | 
7 files changed, 260 insertions, 50 deletions
diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 3629199f9a..4e46a1aef2 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -963,7 +963,8 @@ struct frame_S {                              // for first    // fr_child and fr_win are mutually exclusive    frame_T *fr_child;        // first contained frame -  win_T *fr_win;          // window that fills this frame +  win_T *fr_win;        // window that fills this frame; for a snapshot +                        // set to the current window  };  #define FR_LEAF 0       // frame is a leaf @@ -1340,6 +1341,7 @@ struct window_S {    int w_briopt_shift;               // additional shift for breakindent    bool w_briopt_sbr;                // sbr in 'briopt'    int w_briopt_list;                // additional indent for lists +  int w_briopt_vcol;                // indent for specific column    // transform a pointer to a "onebuf" option into a "allbuf" option  #define GLOBAL_WO(p)    ((char *)(p) + sizeof(winopt_T)) diff --git a/src/nvim/indent.c b/src/nvim/indent.c index cb5819e946..d372e41459 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -743,6 +743,7 @@ bool briopt_check(win_T *wp)    int bri_min = 20;    bool bri_sbr = false;    int bri_list = 0; +  int bri_vcol = 0;    char *p = wp->w_p_briopt;    while (*p != NUL) { @@ -759,6 +760,9 @@ bool briopt_check(win_T *wp)      } else if (STRNCMP(p, "list:", 5) == 0) {        p += 5;        bri_list = (int)getdigits(&p, false, 0); +    } else if (STRNCMP(p, "column:", 7) == 0) { +      p += 7; +      bri_vcol = (int)getdigits(&p, false, 0);      }      if (*p != ',' && *p != NUL) {        return false; @@ -771,7 +775,8 @@ bool briopt_check(win_T *wp)    wp->w_briopt_shift = bri_shift;    wp->w_briopt_min = bri_min;    wp->w_briopt_sbr = bri_sbr; -  wp->w_briopt_list  = bri_list; +  wp->w_briopt_list = bri_list; +  wp->w_briopt_vcol = bri_vcol;    return true;  } @@ -783,51 +788,78 @@ int get_breakindent_win(win_T *wp, char_u *line)    FUNC_ATTR_NONNULL_ALL  {    static int prev_indent = 0;  // Cached indent value. -  static long prev_ts = 0;  // Cached tabstop value. +  static long prev_ts = 0L;  // Cached tabstop value.    static const char_u *prev_line = NULL;  // cached pointer to line.    static varnumber_T prev_tick = 0;  // Changedtick of cached value. -  static long *prev_vts = NULL;    // Cached vartabs values. +  static long *prev_vts = NULL;  // Cached vartabs values. +  static int prev_list = 0;  // cached list value +  static int prev_listopt = 0;  // cached w_p_briopt_list value +  static char *prev_flp = NULL;  // cached formatlistpat value    int bri = 0;    // window width minus window margin space, i.e. what rests for text    const int eff_wwidth = wp->w_width_inner -                           ((wp->w_p_nu || wp->w_p_rnu)                            && (vim_strchr(p_cpo, CPO_NUMCOL) == NULL) ? number_width(wp) + 1 : 0); -  // used cached indent, unless pointer or 'tabstop' changed +  // used cached indent, unless +  // - line pointer changed +  // - 'tabstop' changed +  // - 'briopt_list changed' changed or +  // - 'formatlistpattern' changed    if (prev_line != line || prev_ts != wp->w_buffer->b_p_ts        || prev_tick != buf_get_changedtick(wp->w_buffer) +      || prev_listopt != wp->w_briopt_list +      || (prev_flp == NULL || (strcmp(prev_flp, get_flp_value(wp->w_buffer)) != 0))        || prev_vts != wp->w_buffer->b_p_vts_array) {      prev_line = line;      prev_ts = wp->w_buffer->b_p_ts;      prev_tick = buf_get_changedtick(wp->w_buffer);      prev_vts = wp->w_buffer->b_p_vts_array; -    prev_indent = get_indent_str_vtab((char *)line, -                                      wp->w_buffer->b_p_ts, -                                      wp->w_buffer->b_p_vts_array, -                                      wp->w_p_list); +    if (wp->w_briopt_vcol == 0) { +      prev_indent = get_indent_str_vtab((char *)line, +                                        wp->w_buffer->b_p_ts, +                                        wp->w_buffer->b_p_vts_array, +                                        wp->w_p_list); +    } +    prev_listopt = wp->w_briopt_list; +    prev_list = 0; +    xfree(prev_flp); +    prev_flp = xstrdup(get_flp_value(wp->w_buffer)); +    // add additional indent for numbered lists +    if (wp->w_briopt_list != 0 && wp->w_briopt_vcol == 0) { +      regmatch_T regmatch = { +        .regprog = vim_regcomp(prev_flp, RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT), +      }; +      if (regmatch.regprog != NULL) { +        regmatch.rm_ic = false; +        if (vim_regexec(®match, (char *)line, 0)) { +          if (wp->w_briopt_list > 0) { +            prev_list += wp->w_briopt_list; +          } else { +            prev_list = (int)(*regmatch.endp - *regmatch.startp); +          } +        } +        vim_regfree(regmatch.regprog); +      } +    } +  } +  if (wp->w_briopt_vcol != 0) { +    // column value has priority +    bri = wp->w_briopt_vcol; +    prev_list = 0; +  } else { +    bri = prev_indent + wp->w_briopt_shift;    } -  bri = prev_indent + wp->w_briopt_shift;    // Add offset for number column, if 'n' is in 'cpoptions'    bri += win_col_off2(wp);    // add additional indent for numbered lists    if (wp->w_briopt_list != 0) { -    regmatch_T regmatch = { -      .regprog = vim_regcomp(curbuf->b_p_flp, -                             RE_MAGIC + RE_STRING + RE_AUTO + RE_STRICT), -    }; - -    if (regmatch.regprog != NULL) { -      regmatch.rm_ic = false; -      if (vim_regexec(®match, (char *)line, 0)) { -        if (wp->w_briopt_list > 0) { -          bri += wp->w_briopt_list; -        } else { -          bri = (int)(*regmatch.endp - *regmatch.startp); -        } -      } -      vim_regfree(regmatch.regprog); +    if (wp->w_briopt_list > 0) { +      bri += prev_list; +    } else { +      bri = prev_list;      }    } diff --git a/src/nvim/option.c b/src/nvim/option.c index 8de86ce76e..8142be4eb1 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5220,6 +5220,17 @@ unsigned int get_bkc_value(buf_T *buf)    return buf->b_bkc_flags ? buf->b_bkc_flags : bkc_flags;  } +/// Get the local or global value of 'formatlistpat'. +/// +/// @param buf The buffer. +char *get_flp_value(buf_T *buf) +{ +  if (buf->b_p_flp == NULL || *buf->b_p_flp == NUL) { +    return p_flp; +  } +  return buf->b_p_flp; +} +  /// Get the local or global value of the 'virtualedit' flags.  unsigned int get_ve_flags(void)  { diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 43628d2842..65bc9f60df 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -697,6 +697,10 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf      if (briopt_check(curwin) == FAIL) {        errmsg = e_invarg;      } +    // list setting requires a redraw +    if (curwin->w_briopt_list) { +      redraw_all_later(UPD_NOT_VALID); +    }    } else if (varp == &p_isi               || varp == &(curbuf->b_p_isk)               || varp == &p_isp @@ -1601,6 +1605,12 @@ char *did_set_string_option(int opt_idx, char **varp, char *oldval, char *errbuf      setmouse();  // in case 'mouse' changed    } +  // Changing Formatlistpattern when briopt includes the list setting: +  // redraw +  if ((varp == &p_flp || varp == &(curbuf->b_p_flp)) && curwin->w_briopt_list) { +    redraw_all_later(UPD_NOT_VALID); +  } +    if (curwin->w_curswant != MAXCOL        && (opt->flags & (P_CURSWANT | P_RALL)) != 0) {      curwin->w_set_curswant = true; diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 42218ac847..bed15f9e36 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -444,9 +444,9 @@ int win_lbr_chartabsize(chartabsize_T *cts, int *headp)    // May have to add something for 'breakindent' and/or 'showbreak'    // string at start of line.    // Set *headp to the size of what we add. +  // Do not use 'showbreak' at the NUL after the text.    added = 0; - -  char *const sbr = (char *)get_showbreak_value(wp); +  char *const sbr = c == NUL ? empty_option : (char *)get_showbreak_value(wp);    if ((*sbr != NUL || wp->w_p_bri) && wp->w_p_wrap && vcol != 0) {      colnr_T sbrlen = 0;      int numberwidth = win_col_off(wp); diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index 2f4b1b98c1..d4d2ed28cc 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -3370,8 +3370,8 @@ static void nfa_print_state2(FILE *debugf, nfa_state_T *state, garray_T *indent)      int last = indent->ga_len - 3;      char_u save[2]; -    STRNCPY(save, &p[last], 2); -    STRNCPY(&p[last], "+-", 2); +    STRNCPY(save, &p[last], 2);  // NOLINT(runtime/printf) +    memcpy(&p[last], "+-", 2);      fprintf(debugf, " %s", p);      STRNCPY(&p[last], save, 2);  // NOLINT(runtime/printf)    } else { @@ -4635,6 +4635,20 @@ static bool sub_equal(regsub_T *sub1, regsub_T *sub2)  }  #ifdef REGEXP_DEBUG +static void open_debug_log(TriState result) +{ +  log_fd = fopen(NFA_REGEXP_RUN_LOG, "a"); +  if (log_fd == NULL) { +    emsg(_(e_log_open_failed)); +    log_fd = stderr; +  } + +  fprintf(log_fd, "****************************\n"); +  fprintf(log_fd, "FINISHED RUNNING nfa_regmatch() recursively\n"); +  fprintf(log_fd, "MATCH = %s\n", result == kTrue ? "OK" : result == kNone ? "MAYBE" : "FALSE"); +  fprintf(log_fd, "****************************\n"); +} +  static void report_state(char *action, regsub_T *sub, nfa_state_T *state, int lid, nfa_pim_T *pim)  {    int col; @@ -4647,6 +4661,9 @@ static void report_state(char *action, regsub_T *sub, nfa_state_T *state, int li      col = (int)(sub->list.line[0].start - rex.line);    }    nfa_set_code(state->c); +  if (log_fd == NULL) { +    open_debug_log(kNone); +  }    fprintf(log_fd, "> %s state %d to list %d. char %d: %s (start col %d)%s\n",            action, abs(state->id), lid, state->c, code, col,            pim_info(pim)); @@ -5668,16 +5685,7 @@ static int recursive_regmatch(nfa_state_T *state, nfa_pim_T *pim, nfa_regprog_T    nfa_endp = save_nfa_endp;  #ifdef REGEXP_DEBUG -  log_fd = fopen(NFA_REGEXP_RUN_LOG, "a"); -  if (log_fd != NULL) { -    fprintf(log_fd, "****************************\n"); -    fprintf(log_fd, "FINISHED RUNNING nfa_regmatch() recursively\n"); -    fprintf(log_fd, "MATCH = %s\n", !result ? "false" : "OK"); -    fprintf(log_fd, "****************************\n"); -  } else { -    emsg(_(e_log_open_failed)); -    log_fd = stderr; -  } +  open_debug_log(result);  #endif    return result; @@ -5983,16 +5991,15 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, regsubs_T *subm  #ifdef REGEXP_DEBUG    log_fd = fopen(NFA_REGEXP_RUN_LOG, "a"); -  if (log_fd != NULL) { -    fprintf(log_fd, "**********************************\n"); -    nfa_set_code(start->c); -    fprintf(log_fd, " RUNNING nfa_regmatch() starting with state %d, code %s\n", -            abs(start->id), code); -    fprintf(log_fd, "**********************************\n"); -  } else { +  if (log_fd == NULL) {      emsg(_(e_log_open_failed));      log_fd = stderr;    } +  fprintf(log_fd, "**********************************\n"); +  nfa_set_code(start->c); +  fprintf(log_fd, " RUNNING nfa_regmatch() starting with state %d, code %s\n", +          abs(start->id), code); +  fprintf(log_fd, "**********************************\n");  #endif    thislist = &list[0]; diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index 69c98f1f05..995683c68c 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -8,6 +8,7 @@ source check.vim  CheckOption breakindent  source view_util.vim +source screendump.vim  let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" @@ -876,17 +877,164 @@ endfunc  func Test_window_resize_with_linebreak()    new    53vnew -  set linebreak -  set showbreak=>> -  set breakindent -  set breakindentopt=shift:4 +  setl linebreak +  setl showbreak=>> +  setl breakindent +  setl breakindentopt=shift:4    call setline(1, "\naaaaaaaaa\n\na\naaaaa\n¯aaaaaaaaaa\naaaaaaaaaaaa\naaa\n\"a:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - aaaaaaaa\"\naaaaaaaa\n\"a")    redraw!    call assert_equal(["    >>aa^@\"a: "], ScreenLines(2, 14))    vertical resize 52    redraw!    call assert_equal(["    >>aaa^@\"a:"], ScreenLines(2, 14)) +  set linebreak& showbreak& breakindent& breakindentopt&    %bw!  endfunc +func Test_cursor_position_with_showbreak() +  CheckScreendump + +  let lines =<< trim END +      vim9script +      &signcolumn = 'yes' +      &showbreak = '+ ' +      var leftcol: number = win_getid()->getwininfo()->get(0, {})->get('textoff') +      repeat('x', &columns - leftcol - 1)->setline(1) +      'second line'->setline(2) +  END +  call writefile(lines, 'XscriptShowbreak') +  let buf = RunVimInTerminal('-S XscriptShowbreak', #{rows: 6}) + +  call term_sendkeys(buf, "AX") +  call VerifyScreenDump(buf, 'Test_cursor_position_with_showbreak', {}) + +  call StopVimInTerminal(buf) +  call delete('XscriptShowbreak') +endfunc + +func Test_no_spurious_match() +  let s:input = printf('- y %s y %s', repeat('x', 50), repeat('x', 50)) +  call s:test_windows('setl breakindent breakindentopt=list:-1 formatlistpat=^- hls') +  let @/ = '\%>3v[y]' +  redraw! +  call searchcount().total->assert_equal(1) +  " cleanup +  set hls&vim +  let s:input = "\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" +  bwipeout! +endfunc + +func Test_no_extra_indent() +  call s:test_windows('setl breakindent breakindentopt=list:-1,min:10') +  %d +  let &l:formatlistpat='^\s*\d\+\.\s\+' +  let text = 'word ' +  let len = text->strcharlen() +  let line1 = text->repeat((winwidth(0) / len) * 2) +  let line2 = repeat(' ', 2) .. '1. ' .. line1 +  call setline(1, [line2]) +  redraw! +  " 1) matches formatlist pattern, so indent +  let expect = [ +  \ "  1. word word word ", +  \ "     word word word ", +  \ "     word word      ", +  \ "~                   ", +  \ ] +  let lines = s:screen_lines2(1, 4, 20) +  call s:compare_lines(expect, lines) +  " 2) change formatlist pattern +  " -> indent adjusted +  let &l:formatlistpat='^\s*\d\+\.' +  let expect = [ +  \ "  1. word word word ", +  \ "    word word word  ", +  \ "    word word       ", +  \ "~                   ", +  \ ] +  let lines = s:screen_lines2(1, 4, 20) +  " 3) no local formatlist pattern, +  " so use global one -> indent +  let g_flp = &g:flp +  let &g:formatlistpat='^\s*\d\+\.\s\+' +  let &l:formatlistpat='' +  let expect = [ +  \ "  1. word word word ", +  \ "     word word word ", +  \ "     word word      ", +  \ "~                   ", +  \ ] +  let lines = s:screen_lines2(1, 4, 20) +  call s:compare_lines(expect, lines) +  let &g:flp = g_flp +  let &l:formatlistpat='^\s*\d\+\.' +  " 4) add something in front, no additional indent +  norm! gg0 +  exe ":norm! 5iword \<esc>" +  redraw! +  let expect = [ +  \ "word word word word ", +  \ "word   1. word word ", +  \ "word word word word ", +  \ "word word           ", +  \ "~                   ", +  \ ] +  let lines = s:screen_lines2(1, 5, 20) +  call s:compare_lines(expect, lines) +  bwipeout! +endfunc + +func Test_breakindent_column() +  " restore original +  let s:input ="\tabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOP" +  call s:test_windows('setl breakindent breakindentopt=column:10') +  redraw! +  " 1) default: does not indent, too wide :( +  let expect = [ +  \ "                    ", +  \ "    abcdefghijklmnop", +  \ "qrstuvwxyzABCDEFGHIJ", +  \ "KLMNOP              " +  \ ] +  let lines = s:screen_lines2(1, 4, 20) +  call s:compare_lines(expect, lines) +  " 2) lower min value, so that breakindent works +  setl breakindentopt+=min:5 +  redraw! +  let expect = [ +  \ "                    ", +  \ "    abcdefghijklmnop", +  \ "          qrstuvwxyz", +  \ "          ABCDEFGHIJ", +  \ "          KLMNOP    " +  \ ] +  let lines = s:screen_lines2(1, 5, 20) +  " 3) set shift option -> no influence +  setl breakindentopt+=shift:5 +  redraw! +  let expect = [ +  \ "                    ", +  \ "    abcdefghijklmnop", +  \ "          qrstuvwxyz", +  \ "          ABCDEFGHIJ", +  \ "          KLMNOP    " +  \ ] +  let lines = s:screen_lines2(1, 5, 20) +  call s:compare_lines(expect, lines) +  " 4) add showbreak value +  setl showbreak=++ +  redraw! +  let expect = [ +  \ "                    ", +  \ "    abcdefghijklmnop", +  \ "          ++qrstuvwx", +  \ "          ++yzABCDEF", +  \ "          ++GHIJKLMN", +  \ "          ++OP      " +  \ ] +  let lines = s:screen_lines2(1, 6, 20) +  call s:compare_lines(expect, lines) +  bwipeout! +endfunc +  " vim: shiftwidth=2 sts=2 expandtab  | 
