mirror of
https://github.com/neovim/neovim.git
synced 2025-09-11 22:08:18 +00:00
refactor(indent): refactor computing of a string's indent size (#27252)
The `get_indent_str_vtab()` function currently calls `tabstop_padding()` every time a tab is encountered (unless tabstops aren't used). `tabstop_padding()` either does a division by 'tabstop' If 'vartabstop' is not set, or iterates through the 'vartabstop' list to find current tab width. Since the virtual column only increases, we can keep track of where the next tabstop would be, and update this information once it was reached. `get_indent_str_vtab()` also depends on 'listchars' "tab" value from the current window, even though it may be called for a line from the same buffer in a different window. In most cases, it is called with tabstops enabled (last argument was `false`), so I split the function into one that uses tabstops and the other that doesn't. I removed `get_indent_str()` since I couldn't find any calls to it.
This commit is contained in:
@@ -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.
|
// indent to use for the new line.
|
||||||
if (curbuf->b_p_ai || do_si) {
|
if (curbuf->b_p_ai || do_si) {
|
||||||
// count white space on current line
|
// count white space on current line
|
||||||
newindent = get_indent_str_vtab(saved_line,
|
newindent = indent_size_ts(saved_line, curbuf->b_p_ts, curbuf->b_p_vts_array);
|
||||||
curbuf->b_p_ts,
|
|
||||||
curbuf->b_p_vts_array, false);
|
|
||||||
if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) {
|
if (newindent == 0 && !(flags & OPENLINE_COM_LIST)) {
|
||||||
newindent = second_line_indent; // for ^^D command in insert mode
|
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.
|
// Recompute the indent, it may have changed.
|
||||||
if (curbuf->b_p_ai || do_si) {
|
if (curbuf->b_p_ai || do_si) {
|
||||||
newindent = get_indent_str_vtab(leader,
|
newindent = indent_size_ts(leader, curbuf->b_p_ts, curbuf->b_p_vts_array);
|
||||||
curbuf->b_p_ts,
|
|
||||||
curbuf->b_p_vts_array, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the indent offset
|
// Add the indent offset
|
||||||
|
@@ -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,
|
/// If "vts" is set then the tab widths are taken from that array,
|
||||||
/// otherwise the value of ts is used.
|
/// otherwise the value of ts is used.
|
||||||
int tabstop_padding(colnr_T col, OptInt ts_arg, const colnr_T *vts)
|
int tabstop_padding(colnr_T col, OptInt ts_arg, const colnr_T *vts)
|
||||||
|
FUNC_ATTR_PURE
|
||||||
{
|
{
|
||||||
OptInt ts = ts_arg == 0 ? 8 : ts_arg;
|
OptInt ts = ts_arg == 0 ? 8 : ts_arg;
|
||||||
colnr_T tabcol = 0;
|
colnr_T tabcol = 0;
|
||||||
@@ -349,83 +350,96 @@ int get_sts_value(void)
|
|||||||
return result;
|
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)
|
int get_indent(void)
|
||||||
{
|
{
|
||||||
return get_indent_str_vtab(get_cursor_line_ptr(),
|
return indent_size_ts(get_cursor_line_ptr(), curbuf->b_p_ts, curbuf->b_p_vts_array);
|
||||||
curbuf->b_p_ts,
|
|
||||||
curbuf->b_p_vts_array,
|
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
int get_indent_lnum(linenr_T lnum)
|
||||||
{
|
{
|
||||||
return get_indent_str_vtab(ml_get(lnum),
|
return indent_size_ts(ml_get(lnum), curbuf->b_p_ts, curbuf->b_p_vts_array);
|
||||||
curbuf->b_p_ts,
|
|
||||||
curbuf->b_p_vts_array,
|
|
||||||
false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count the size (in window cells) of the indent in line "lnum" of buffer
|
/// Count the size (in window cells) of the indent in line "lnum" of buffer "buf".
|
||||||
// "buf".
|
|
||||||
int get_indent_buf(buf_T *buf, linenr_T lnum)
|
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
|
/// Compute the size of the indent (in window cells) in line "ptr",
|
||||||
/// 'tabstop' at "ts".
|
/// without tabstops (count tab as ^I or <09>).
|
||||||
/// If @param list is true, count only screen size for tabs.
|
int indent_size_no_ts(char const *ptr)
|
||||||
int get_indent_str(const char *ptr, int ts, bool list)
|
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE
|
||||||
FUNC_ATTR_NONNULL_ALL
|
|
||||||
{
|
{
|
||||||
int count = 0;
|
int tab_size = byte2cells(TAB);
|
||||||
|
|
||||||
for (; *ptr; ptr++) {
|
int vcol = 0;
|
||||||
// Count a tab for what it is worth.
|
while (true) {
|
||||||
if (*ptr == TAB) {
|
char const c = *ptr++;
|
||||||
if (!list || curwin->w_p_lcs_chars.tab1) {
|
if (c == ' ') {
|
||||||
// count a tab for what it is worth
|
vcol++;
|
||||||
count += ts - (count % ts);
|
} else if (c == TAB) {
|
||||||
|
vcol += tab_size;
|
||||||
} else {
|
} else {
|
||||||
// In list mode, when tab is not set, count screen char width
|
return vcol;
|
||||||
// for Tab, displays: ^I
|
|
||||||
count += ptr2cells(ptr);
|
|
||||||
}
|
|
||||||
} else if (*ptr == ' ') {
|
|
||||||
// Count a space for one.
|
|
||||||
count++;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Count the size (in window cells) of the indent in line "ptr", using
|
/// Compute the size of the indent (in window cells) in line "ptr",
|
||||||
/// variable tabstops.
|
/// using tabstops
|
||||||
/// if "list" is true, count only screen size for tabs.
|
int indent_size_ts(char const *ptr, OptInt ts, colnr_T *vts)
|
||||||
int get_indent_str_vtab(const char *ptr, OptInt ts, colnr_T *vts, bool list)
|
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_PURE
|
||||||
{
|
{
|
||||||
int count = 0;
|
assert(char2cells(' ') == 1);
|
||||||
|
|
||||||
for (; *ptr; ptr++) {
|
int vcol = 0;
|
||||||
if (*ptr == TAB) { // count a tab for what it is worth
|
int tabstop_width, next_tab_vcol;
|
||||||
if (!list || curwin->w_p_lcs_chars.tab1) {
|
|
||||||
count += tabstop_padding(count, ts, vts);
|
if (vts == NULL || vts[0] < 1) { // tab has fixed width
|
||||||
} else {
|
// can ts be 0 ? This is from tabstop_padding().
|
||||||
// In list mode, when tab is not set, count screen char width
|
tabstop_width = (int)(ts == 0 ? 8 : ts);
|
||||||
// for Tab, displays: ^I
|
next_tab_vcol = tabstop_width;
|
||||||
count += ptr2cells(ptr);
|
} else { // tab has variable width
|
||||||
}
|
colnr_T *cur_tabstop = vts + 1;
|
||||||
} else if (*ptr == ' ') {
|
colnr_T *const last_tabstop = vts + vts[0];
|
||||||
count++; // count a space for one
|
|
||||||
} else {
|
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;
|
break;
|
||||||
|
} else {
|
||||||
|
return cur_vcol;
|
||||||
|
}
|
||||||
|
} while (cur_vcol != vcol);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return vcol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the indent of the current line.
|
/// 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_tick = buf_get_changedtick(wp->w_buffer);
|
||||||
prev_vts = wp->w_buffer->b_p_vts_array;
|
prev_vts = wp->w_buffer->b_p_vts_array;
|
||||||
if (wp->w_briopt_vcol == 0) {
|
if (wp->w_briopt_vcol == 0) {
|
||||||
prev_indent = get_indent_str_vtab(line,
|
if (wp->w_p_list && !wp->w_p_lcs_chars.tab1) {
|
||||||
wp->w_buffer->b_p_ts,
|
prev_indent = indent_size_no_ts(line);
|
||||||
wp->w_buffer->b_p_vts_array,
|
} else {
|
||||||
wp->w_p_list);
|
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_listopt = wp->w_briopt_list;
|
||||||
prev_list = 0;
|
prev_list = 0;
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
local helpers = require('test.unit.helpers')(after_each)
|
local helpers = require('test.unit.helpers')(after_each)
|
||||||
local itp = helpers.gen_itp(it)
|
local itp = helpers.gen_itp(it)
|
||||||
|
|
||||||
|
local to_cstr = helpers.to_cstr
|
||||||
|
local ffi = helpers.ffi
|
||||||
local eq = helpers.eq
|
local eq = helpers.eq
|
||||||
|
|
||||||
local indent = helpers.cimport('./src/nvim/indent.h')
|
local indent = helpers.cimport('./src/nvim/indent.h')
|
||||||
@@ -28,3 +30,31 @@ describe('get_sts_value', function()
|
|||||||
eq(tabstop, indent.get_sts_value())
|
eq(tabstop, indent.get_sts_value())
|
||||||
end)
|
end)
|
||||||
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)
|
||||||
|
Reference in New Issue
Block a user