vim-patch:8.1.0105: all tab stops are the same

Problem:    All tab stops are the same.
Solution:   Add the variable tabstop feature. (Christian Brabandt,
            closes vim/vim#2711)
04958cbaf2
This commit is contained in:
VVKot
2021-02-13 19:06:37 +00:00
parent b79596eb5e
commit 6752ac4968
22 changed files with 1458 additions and 249 deletions

View File

@@ -180,6 +180,8 @@ static long p_ts;
static long p_tw;
static int p_udf;
static long p_wm;
static char_u *p_vsts;
static char_u *p_vts;
static char_u *p_keymap;
// Saved values for when 'bin' is set.
@@ -194,6 +196,7 @@ static int p_et_nopaste;
static long p_sts_nopaste;
static long p_tw_nopaste;
static long p_wm_nopaste;
static char_u *p_vsts_nopaste;
typedef struct vimoption {
char *fullname; // full option name
@@ -1998,6 +2001,8 @@ static void didset_options2(void)
// Parse default for 'wildmode'.
check_opt_wim();
tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array);
tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array);
}
/// Check for string options that are NULL (normally only termcap options).
@@ -2064,6 +2069,8 @@ void check_buf_options(buf_T *buf)
check_string_option(&buf->b_p_lw);
check_string_option(&buf->b_p_bkc);
check_string_option(&buf->b_p_menc);
check_string_option(&buf->b_p_vsts);
check_string_option(&buf->b_p_vts);
}
/// Free the string allocated for an option.
@@ -3110,6 +3117,69 @@ ambw_end:
if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) {
errmsg = e_invarg;
}
} else if (varp == &(curbuf->b_p_vsts)) { // 'varsofttabstop'
char_u *cp;
if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
if (curbuf->b_p_vsts_array) {
xfree(curbuf->b_p_vsts_array);
curbuf->b_p_vsts_array = 0;
}
} else {
for (cp = *varp; *cp; cp++) {
if (ascii_isdigit(*cp)) {
continue;
}
if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
continue;
}
errmsg = e_invarg;
break;
}
if (errmsg == NULL) {
long *oldarray = curbuf->b_p_vsts_array;
if (tabstop_set(*varp, &(curbuf->b_p_vsts_array))) {
if (oldarray) {
xfree(oldarray);
}
} else {
errmsg = e_invarg;
}
}
}
} else if (varp == &(curbuf->b_p_vts)) { // 'vartabstop'
char_u *cp;
if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) {
if (curbuf->b_p_vts_array) {
xfree(curbuf->b_p_vts_array);
curbuf->b_p_vts_array = NULL;
}
} else {
for (cp = *varp; *cp; cp++) {
if (ascii_isdigit(*cp)) {
continue;
}
if (*cp == ',' && cp > *varp && *(cp - 1) != ',') {
continue;
}
errmsg = e_invarg;
break;
}
if (errmsg == NULL) {
long *oldarray = curbuf->b_p_vts_array;
if (tabstop_set(*varp, &(curbuf->b_p_vts_array))) {
if (oldarray) {
xfree(oldarray);
}
if (foldmethodIsIndent(curwin)) {
foldUpdateAll(curwin);
}
} else {
errmsg = e_invarg;
}
}
}
} else {
// Options that are a list of flags.
p = NULL;
@@ -5684,6 +5754,8 @@ static char_u *get_varp(vimoption_T *p)
case PV_TW: return (char_u *)&(curbuf->b_p_tw);
case PV_UDF: return (char_u *)&(curbuf->b_p_udf);
case PV_WM: return (char_u *)&(curbuf->b_p_wm);
case PV_VSTS: return (char_u *)&(curbuf->b_p_vsts);
case PV_VTS: return (char_u *)&(curbuf->b_p_vts);
case PV_KMAP: return (char_u *)&(curbuf->b_p_keymap);
case PV_SCL: return (char_u *)&(curwin->w_p_scl);
case PV_WINHL: return (char_u *)&(curwin->w_p_winhl);
@@ -5935,6 +6007,15 @@ void buf_copy_options(buf_T *buf, int flags)
buf->b_p_tfu = vim_strsave(p_tfu);
buf->b_p_sts = p_sts;
buf->b_p_sts_nopaste = p_sts_nopaste;
buf->b_p_vsts = vim_strsave(p_vsts);
if (p_vsts && p_vsts != empty_option) {
tabstop_set(p_vsts, &buf->b_p_vsts_array);
} else {
buf->b_p_vsts_array = 0;
}
buf->b_p_vsts_nopaste = p_vsts_nopaste
? vim_strsave(p_vsts_nopaste)
: NULL;
buf->b_p_com = vim_strsave(p_com);
buf->b_p_cms = vim_strsave(p_cms);
buf->b_p_fo = vim_strsave(p_fo);
@@ -6006,10 +6087,21 @@ void buf_copy_options(buf_T *buf, int flags)
*/
if (dont_do_help) {
buf->b_p_isk = save_p_isk;
if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
buf->b_p_vts_array = NULL;
}
} else {
buf->b_p_isk = vim_strsave(p_isk);
did_isk = true;
buf->b_p_ts = p_ts;
buf->b_p_vts = vim_strsave(p_vts);
if (p_vts && p_vts != empty_option && !buf->b_p_vts_array) {
tabstop_set(p_vts, &buf->b_p_vts_array);
} else {
buf->b_p_vts_array = NULL;
}
buf->b_help = false;
if (buf->b_p_bt[0] == 'h') {
clear_string_option(&buf->b_p_bt);
@@ -6624,6 +6716,12 @@ static void paste_option_changed(void)
buf->b_p_sts_nopaste = buf->b_p_sts;
buf->b_p_ai_nopaste = buf->b_p_ai;
buf->b_p_et_nopaste = buf->b_p_et;
if (buf->b_p_vsts_nopaste) {
xfree(buf->b_p_vsts_nopaste);
}
buf->b_p_vsts_nopaste = buf->b_p_vsts && buf->b_p_vsts != empty_option
? vim_strsave(buf->b_p_vsts)
: NULL;
}
// save global options
@@ -6638,6 +6736,12 @@ static void paste_option_changed(void)
p_sts_nopaste = p_sts;
p_tw_nopaste = p_tw;
p_wm_nopaste = p_wm;
if (p_vsts_nopaste) {
xfree(p_vsts_nopaste);
}
p_vsts_nopaste = p_vsts && p_vsts != empty_option
? vim_strsave(p_vsts)
: NULL;
}
// Always set the option values, also when 'paste' is set when it is
@@ -6649,6 +6753,14 @@ static void paste_option_changed(void)
buf->b_p_sts = 0; // softtabstop is 0
buf->b_p_ai = 0; // no auto-indent
buf->b_p_et = 0; // no expandtab
if (buf->b_p_vsts) {
free_string_option(buf->b_p_vsts);
}
buf->b_p_vsts = empty_option;
if (buf->b_p_vsts_array) {
xfree(buf->b_p_vsts_array);
}
buf->b_p_vsts_array = 0;
}
// set global options
@@ -6665,6 +6777,10 @@ static void paste_option_changed(void)
p_wm = 0;
p_sts = 0;
p_ai = 0;
if (p_vsts) {
free_string_option(p_vsts);
}
p_vsts = empty_option;
} else if (old_p_paste) {
// Paste switched from on to off: Restore saved values.
@@ -6675,6 +6791,20 @@ static void paste_option_changed(void)
buf->b_p_sts = buf->b_p_sts_nopaste;
buf->b_p_ai = buf->b_p_ai_nopaste;
buf->b_p_et = buf->b_p_et_nopaste;
if (buf->b_p_vsts) {
free_string_option(buf->b_p_vsts);
}
buf->b_p_vsts = buf->b_p_vsts_nopaste
? vim_strsave(buf->b_p_vsts_nopaste)
: empty_option;
if (buf->b_p_vsts_array) {
xfree(buf->b_p_vsts_array);
}
if (buf->b_p_vsts && buf->b_p_vsts != empty_option) {
tabstop_set(buf->b_p_vsts, &buf->b_p_vsts_array);
} else {
buf->b_p_vsts_array = 0;
}
}
// restore global options
@@ -6692,6 +6822,10 @@ static void paste_option_changed(void)
p_sts = p_sts_nopaste;
p_tw = p_tw_nopaste;
p_wm = p_wm_nopaste;
if (p_vsts) {
free_string_option(p_vsts);
}
p_vsts = p_vsts_nopaste ? vim_strsave(p_vsts_nopaste) : empty_option;
}
old_p_paste = p_paste;
@@ -6941,6 +7075,265 @@ int check_ff_value(char_u *p)
return check_opt_strings(p, p_ff_values, false);
}
// Set the integer values corresponding to the string setting of 'vartabstop'.
// "array" will be set, caller must free it if needed.
bool tabstop_set(char_u *var, long **array)
{
long valcount = 1;
int t;
char_u *cp;
if ((!var[0] || (var[0] == '0' && !var[1]))) {
*array = NULL;
return true;
}
for (cp = var; *cp; cp++) {
if (cp == var || *(cp - 1) == ',') {
char_u *end;
if (strtol((char *)cp, (char **)&end, 10) <= 0) {
if (cp != end) {
EMSG(_(e_positive));
} else {
EMSG(_(e_invarg));
}
return false;
}
}
if (ascii_isdigit(*cp)) {
continue;
}
if (*cp == ',' && cp > var && *(cp - 1) != ',') {
valcount++;
continue;
}
EMSG(_(e_invarg));
return false;
}
*array = (long *)xmalloc((unsigned)(valcount + 1) * sizeof(long));
(*array)[0] = valcount;
t = 1;
for (cp = var; *cp;) {
(*array)[t++] = atoi((char *)cp);
while (*cp && *cp != ',') {
cp++;
}
if (*cp) {
cp++;
}
}
return true;
}
// Calculate the number of screen spaces a tab will occupy.
// 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, long ts_arg, long *vts)
{
long ts = ts_arg == 0 ? 8 : ts_arg;
long tabcount;
colnr_T tabcol = 0;
int t;
long padding = 0;
if (vts == NULL || vts[0] == 0) {
return (int)ts - (col % (int)ts);
}
tabcount = vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > col) {
padding = (int)(tabcol - col);
break;
}
}
if (t > tabcount) {
padding = vts[tabcount] - (int)((col - tabcol) % vts[tabcount]);
}
return (int)padding;
}
// Find the size of the tab that covers a particular column.
int tabstop_at(colnr_T col, long ts, long *vts)
{
int tabcount;
colnr_T tabcol = 0;
int t;
int tab_size = 0;
if (vts == 0 || vts[0] == 0) {
return (int)ts;
}
tabcount = (int)vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > col) {
tab_size = (int)vts[t];
break;
}
}
if (t > tabcount) {
tab_size = (int)vts[tabcount];
}
return tab_size;
}
// Find the column on which a tab starts.
colnr_T tabstop_start(colnr_T col, long ts, long *vts)
{
int tabcount;
colnr_T tabcol = 0;
int t;
int excess;
if (vts == 0 || vts[0] == 0) {
return (col / (int)ts) * (int)ts;
}
tabcount = (int)vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > col) {
return tabcol - (int)vts[t];
}
}
excess = tabcol % vts[tabcount];
return excess + ((col - excess) / (int)vts[tabcount]) * (int)vts[tabcount];
}
// Find the number of tabs and spaces necessary to get from one column
// to another.
void tabstop_fromto(colnr_T start_col,
colnr_T end_col,
long ts,
long *vts,
int *ntabs,
int *nspcs)
{
int spaces = end_col - start_col;
colnr_T tabcol = 0;
int padding = 0;
int tabcount;
int t;
if (vts == 0 || vts[0] == 0) {
int tabs = 0;
int initspc = (int)ts - (start_col % (int)ts);
if (spaces >= initspc) {
spaces -= initspc;
tabs++;
}
tabs += spaces / ts;
spaces -= (spaces / ts) * ts;
*ntabs = tabs;
*nspcs = spaces;
return;
}
// Find the padding needed to reach the next tabstop.
tabcount = (int)vts[0];
for (t = 1; t <= tabcount; t++) {
tabcol += vts[t];
if (tabcol > start_col) {
padding = (int)(tabcol - start_col);
break;
}
}
if (t > tabcount) {
padding =
(int)vts[tabcount] - (int)((start_col - tabcol) % (int)vts[tabcount]);
}
// If the space needed is less than the padding no tabs can be used.
if (spaces < padding) {
*ntabs = 0;
*nspcs = spaces;
return;
}
*ntabs = 1;
spaces -= padding;
// At least one tab has been used. See if any more will fit.
while (spaces != 0 && ++t <= tabcount) {
padding = (int)vts[t];
if (spaces < padding) {
*nspcs = spaces;
return;
}
*ntabs += 1;
spaces -= padding;
}
*ntabs += spaces / (int)vts[tabcount];
*nspcs = spaces % (int)vts[tabcount];
}
// See if two tabstop arrays contain the same values.
bool tabstop_eq(long *ts1, long *ts2)
{
int t;
if ((ts1 == 0 && ts2) || (ts1 && ts2 == 0)) {
return false;
}
if (ts1 == ts2) {
return true;
}
if (ts1[0] != ts2[0]) {
return false;
}
for (t = 1; t <= ts1[0]; t++) {
if (ts1[t] != ts2[t]) {
return false;
}
}
return true;
}
// Copy a tabstop array, allocating space for the new array.
int *tabstop_copy(long *oldts)
{
long *newts;
int t;
if (oldts == 0) {
return 0;
}
newts = xmalloc((unsigned)(oldts[0] + 1) * sizeof(long));
for (t = 0; t <= oldts[0]; t++) {
newts[t] = oldts[t];
}
return (int *)newts;
}
// Return a count of the number of tabstops.
int tabstop_count(long *ts)
{
return ts != NULL ? (int)ts[0] : 0;
}
// Return the first tabstop, or 8 if there are no tabstops defined.
int tabstop_first(long *ts)
{
return ts != NULL ? (int)ts[1] : 8;
}
/// Return the effective shiftwidth value for current buffer, using the
/// 'tabstop' value when 'shiftwidth' is zero.
int get_sw_value(buf_T *buf)