aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/nvim/change.c8
-rw-r--r--src/nvim/indent.c132
-rw-r--r--test/unit/indent_spec.lua30
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)