diff options
-rw-r--r-- | src/nvim/change.c | 8 | ||||
-rw-r--r-- | src/nvim/indent.c | 132 | ||||
-rw-r--r-- | test/unit/indent_spec.lua | 30 |
3 files changed, 106 insertions, 64 deletions
diff --git a/src/nvim/change.c b/src/nvim/change.c index e0b5822233..e01921a826 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -1149,9 +1149,7 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // indent to use for the new line. if (curbuf->b_p_ai || do_si) { // count white space on current line - newindent = get_indent_str_vtab(saved_line, - curbuf->b_p_ts, - curbuf->b_p_vts_array, false); + newindent = indent_size_ts(saved_line, curbuf->b_p_ts, curbuf->b_p_vts_array); if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) { newindent = second_line_indent; // for ^^D command in insert mode } @@ -1593,9 +1591,7 @@ bool open_line(int dir, int flags, int second_line_indent, bool *did_do_comment) // Recompute the indent, it may have changed. if (curbuf->b_p_ai || do_si) { - newindent = get_indent_str_vtab(leader, - curbuf->b_p_ts, - curbuf->b_p_vts_array, false); + newindent = indent_size_ts(leader, curbuf->b_p_ts, curbuf->b_p_vts_array); } // Add the indent offset diff --git a/src/nvim/indent.c b/src/nvim/indent.c index 54aab72f81..05336b88d6 100644 --- a/src/nvim/indent.c +++ b/src/nvim/indent.c @@ -118,6 +118,7 @@ bool tabstop_set(char *var, colnr_T **array) /// If "vts" is set then the tab widths are taken from that array, /// otherwise the value of ts is used. int tabstop_padding(colnr_T col, OptInt ts_arg, const colnr_T *vts) + FUNC_ATTR_PURE { OptInt ts = ts_arg == 0 ? 8 : ts_arg; colnr_T tabcol = 0; @@ -349,83 +350,96 @@ int get_sts_value(void) return result; } -// Count the size (in window cells) of the indent in the current line. +/// Count the size (in window cells) of the indent in the current line. int get_indent(void) { - return get_indent_str_vtab(get_cursor_line_ptr(), - curbuf->b_p_ts, - curbuf->b_p_vts_array, - false); + return indent_size_ts(get_cursor_line_ptr(), curbuf->b_p_ts, curbuf->b_p_vts_array); } -// Count the size (in window cells) of the indent in line "lnum". +/// Count the size (in window cells) of the indent in line "lnum". int get_indent_lnum(linenr_T lnum) { - return get_indent_str_vtab(ml_get(lnum), - curbuf->b_p_ts, - curbuf->b_p_vts_array, - false); + return indent_size_ts(ml_get(lnum), curbuf->b_p_ts, curbuf->b_p_vts_array); } -// Count the size (in window cells) of the indent in line "lnum" of buffer -// "buf". +/// Count the size (in window cells) of the indent in line "lnum" of buffer "buf". int get_indent_buf(buf_T *buf, linenr_T lnum) { - return get_indent_str_vtab(ml_get_buf(buf, lnum), buf->b_p_ts, buf->b_p_vts_array, false); + return indent_size_ts(ml_get_buf(buf, lnum), buf->b_p_ts, buf->b_p_vts_array); } -/// Count the size (in window cells) of the indent in line "ptr", with -/// 'tabstop' at "ts". -/// If @param list is true, count only screen size for tabs. -int get_indent_str(const char *ptr, int ts, bool list) - FUNC_ATTR_NONNULL_ALL +/// Compute the size of the indent (in window cells) in line "ptr", +/// without tabstops (count tab as ^I or <09>). +int indent_size_no_ts(char const *ptr) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE { - int count = 0; - - for (; *ptr; ptr++) { - // Count a tab for what it is worth. - if (*ptr == TAB) { - if (!list || curwin->w_p_lcs_chars.tab1) { - // count a tab for what it is worth - count += ts - (count % ts); - } else { - // In list mode, when tab is not set, count screen char width - // for Tab, displays: ^I - count += ptr2cells(ptr); - } - } else if (*ptr == ' ') { - // Count a space for one. - count++; + int tab_size = byte2cells(TAB); + + int vcol = 0; + while (true) { + char const c = *ptr++; + if (c == ' ') { + vcol++; + } else if (c == TAB) { + vcol += tab_size; } else { - break; + return vcol; } } - return count; } -/// Count the size (in window cells) of the indent in line "ptr", using -/// variable tabstops. -/// if "list" is true, count only screen size for tabs. -int get_indent_str_vtab(const char *ptr, OptInt ts, colnr_T *vts, bool list) +/// Compute the size of the indent (in window cells) in line "ptr", +/// using tabstops +int indent_size_ts(char const *ptr, OptInt ts, colnr_T *vts) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_PURE { - int count = 0; + assert(char2cells(' ') == 1); + + int vcol = 0; + int tabstop_width, next_tab_vcol; + + if (vts == NULL || vts[0] < 1) { // tab has fixed width + // can ts be 0 ? This is from tabstop_padding(). + tabstop_width = (int)(ts == 0 ? 8 : ts); + next_tab_vcol = tabstop_width; + } else { // tab has variable width + colnr_T *cur_tabstop = vts + 1; + colnr_T *const last_tabstop = vts + vts[0]; + + while (cur_tabstop != last_tabstop) { + int cur_vcol = vcol; + vcol += *cur_tabstop++; + assert(cur_vcol < vcol); + + do { + char const c = *ptr++; + if (c == ' ') { + cur_vcol++; + } else if (c == TAB) { + break; + } else { + return cur_vcol; + } + } while (cur_vcol != vcol); + } - for (; *ptr; ptr++) { - if (*ptr == TAB) { // count a tab for what it is worth - if (!list || curwin->w_p_lcs_chars.tab1) { - count += tabstop_padding(count, ts, vts); - } else { - // In list mode, when tab is not set, count screen char width - // for Tab, displays: ^I - count += ptr2cells(ptr); - } - } else if (*ptr == ' ') { - count++; // count a space for one + tabstop_width = *last_tabstop; + next_tab_vcol = vcol + tabstop_width; + } + + assert(tabstop_width != 0); + while (true) { + char const c = *ptr++; + if (c == ' ') { + vcol++; + next_tab_vcol += (vcol == next_tab_vcol) ? tabstop_width : 0; + } else if (c == TAB) { + vcol = next_tab_vcol; + next_tab_vcol += tabstop_width; } else { - break; + return vcol; } } - return count; } /// Set the indent of the current line. @@ -828,10 +842,12 @@ int get_breakindent_win(win_T *wp, char *line) prev_tick = buf_get_changedtick(wp->w_buffer); prev_vts = wp->w_buffer->b_p_vts_array; if (wp->w_briopt_vcol == 0) { - prev_indent = get_indent_str_vtab(line, - wp->w_buffer->b_p_ts, - wp->w_buffer->b_p_vts_array, - wp->w_p_list); + if (wp->w_p_list && !wp->w_p_lcs_chars.tab1) { + prev_indent = indent_size_no_ts(line); + } else { + prev_indent = indent_size_ts(line, wp->w_buffer->b_p_ts, + wp->w_buffer->b_p_vts_array); + } } prev_listopt = wp->w_briopt_list; prev_list = 0; diff --git a/test/unit/indent_spec.lua b/test/unit/indent_spec.lua index f93f6d7581..7902918c54 100644 --- a/test/unit/indent_spec.lua +++ b/test/unit/indent_spec.lua @@ -1,6 +1,8 @@ local helpers = require('test.unit.helpers')(after_each) local itp = helpers.gen_itp(it) +local to_cstr = helpers.to_cstr +local ffi = helpers.ffi local eq = helpers.eq local indent = helpers.cimport('./src/nvim/indent.h') @@ -28,3 +30,31 @@ describe('get_sts_value', function() eq(tabstop, indent.get_sts_value()) end) end) + +describe('indent_size_ts()', function() + itp('works for spaces', function() + local line = to_cstr((' '):rep(7) .. 'a ') + eq(7, indent.indent_size_ts(line, 100, nil)) + end) + + itp('works for tabs and spaces', function() + local line = to_cstr(' \t \t \t\t a ') + eq(19, indent.indent_size_ts(line, 4, nil)) + end) + + itp('works for tabs and spaces with empty vts', function() + local vts = ffi.new('int[1]') -- zero initialized => first element (size) == 0 + local line = to_cstr(' \t \t \t\t a ') + eq(23, indent.indent_size_ts(line, 4, vts)) + end) + + itp('works for tabs and spaces with vts', function() + local vts = ffi.new('int[3]') + vts[0] = 2 -- zero indexed + vts[1] = 7 + vts[2] = 2 + + local line = to_cstr(' \t \t \t\t a ') + eq(18, indent.indent_size_ts(line, 4, vts)) + end) +end) |