Mercurial > vim
diff src/option.c @ 14175:2ad722003b36 v8.1.0105
patch 8.1.0105: all tab stops are the same
commit https://github.com/vim/vim/commit/04958cbaf25eea27eceedaa987adfb354ad5f7fd
Author: Bram Moolenaar <Bram@vim.org>
Date: Sat Jun 23 19:23:02 2018 +0200
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 #2711)
author | Christian Brabandt <cb@256bit.org> |
---|---|
date | Sat, 23 Jun 2018 19:30:07 +0200 |
parents | 1157f16150b3 |
children | ab64a4fb5edd |
line wrap: on
line diff
--- a/src/option.c +++ b/src/option.c @@ -182,6 +182,10 @@ # define PV_UDF OPT_BUF(BV_UDF) #endif #define PV_WM OPT_BUF(BV_WM) +#ifdef FEAT_VARTABS +# define PV_VSTS OPT_BUF(BV_VSTS) +# define PV_VTS OPT_BUF(BV_VTS) +#endif /* * Definition of the PV_ values for window-local options. @@ -371,6 +375,10 @@ static int p_tx; static int p_udf; #endif static long p_wm; +#ifdef FEAT_VARTABS +static char_u *p_vsts; +static char_u *p_vts; +#endif #ifdef FEAT_KEYMAP static char_u *p_keymap; #endif @@ -390,6 +398,9 @@ static int p_et_nopaste; static long p_sts_nopaste; static long p_tw_nopaste; static long p_wm_nopaste; +#ifdef FEAT_VARTABS +static char_u *p_vsts_nopaste; +#endif struct vimoption { @@ -2925,6 +2936,24 @@ static struct vimoption options[] = {"updatetime", "ut", P_NUM|P_VI_DEF, (char_u *)&p_ut, PV_NONE, {(char_u *)4000L, (char_u *)0L} SCRIPTID_INIT}, + {"varsofttabstop", "vsts", P_STRING|P_VI_DEF|P_VIM|P_COMMA, +#ifdef FEAT_VARTABS + (char_u *)&p_vsts, PV_VSTS, + {(char_u *)"", (char_u *)0L} +#else + (char_u *)NULL, PV_NONE, + {(char_u *)"", (char_u *)NULL} +#endif + SCRIPTID_INIT}, + {"vartabstop", "vts", P_STRING|P_VI_DEF|P_VIM|P_RBUF|P_COMMA, +#ifdef FEAT_VARTABS + (char_u *)&p_vts, PV_VTS, + {(char_u *)"", (char_u *)0L} +#else + (char_u *)NULL, PV_NONE, + {(char_u *)"", (char_u *)NULL} +#endif + SCRIPTID_INIT}, {"verbose", "vbs", P_NUM|P_VI_DEF, (char_u *)&p_verbose, PV_NONE, {(char_u *)0L, (char_u *)0L} SCRIPTID_INIT}, @@ -5608,6 +5637,10 @@ didset_options2(void) /* Parse default for 'clipboard' */ (void)check_clipboard_option(); #endif +#ifdef FEAT_VARTABS + tabstop_set(curbuf->b_p_vsts, &curbuf->b_p_vsts_array); + tabstop_set(curbuf->b_p_vts, &curbuf->b_p_vts_array); +#endif } /* @@ -5725,6 +5758,10 @@ check_buf_options(buf_T *buf) #ifdef FEAT_MBYTE check_string_option(&buf->b_p_menc); #endif +#ifdef FEAT_VARTABS + check_string_option(&buf->b_p_vsts); + check_string_option(&buf->b_p_vts); +#endif } /* @@ -7472,6 +7509,88 @@ did_set_string_option( } #endif +#ifdef FEAT_VARTABS + /* 'varsofttabstop' */ + else if (varp == &(curbuf->b_p_vsts)) + { + char_u *cp; + + if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) + { + if (curbuf->b_p_vsts_array) + { + vim_free(curbuf->b_p_vsts_array); + curbuf->b_p_vsts_array = 0; + } + } + else + { + for (cp = *varp; *cp; ++cp) + { + if (vim_isdigit(*cp)) + continue; + if (*cp == ',' && cp > *varp && *(cp-1) != ',') + continue; + errmsg = e_invarg; + break; + } + if (errmsg == NULL) + { + int *oldarray = curbuf->b_p_vsts_array; + if (tabstop_set(*varp, &(curbuf->b_p_vsts_array))) + { + if (oldarray) + vim_free(oldarray); + } + else + errmsg = e_invarg; + } + } + } + + /* 'vartabstop' */ + else if (varp == &(curbuf->b_p_vts)) + { + char_u *cp; + + if (!(*varp)[0] || ((*varp)[0] == '0' && !(*varp)[1])) + { + if (curbuf->b_p_vts_array) + { + vim_free(curbuf->b_p_vts_array); + curbuf->b_p_vts_array = NULL; + } + } + else + { + for (cp = *varp; *cp; ++cp) + { + if (vim_isdigit(*cp)) + continue; + if (*cp == ',' && cp > *varp && *(cp-1) != ',') + continue; + errmsg = e_invarg; + break; + } + if (errmsg == NULL) + { + int *oldarray = curbuf->b_p_vts_array; + if (tabstop_set(*varp, &(curbuf->b_p_vts_array))) + { + if (oldarray) + vim_free(oldarray); +#ifdef FEAT_FOLDING + if (foldmethodIsIndent(curwin)) + foldUpdateAll(curwin); +#endif /* FEAT_FOLDING */ + } + else + errmsg = e_invarg; + } + } + } +#endif + /* Options that are a list of flags. */ else { @@ -8780,7 +8899,14 @@ set_num_option( if (curbuf->b_p_sw < 0) { errmsg = e_positive; +#ifdef FEAT_VARTABS + // Use the first 'vartabstop' value, or 'tabstop' if vts isn't in use. + curbuf->b_p_sw = tabstop_count(curbuf->b_p_vts_array) > 0 + ? tabstop_first(curbuf->b_p_vts_array) + : curbuf->b_p_ts; +#else curbuf->b_p_sw = curbuf->b_p_ts; +#endif } /* @@ -10814,6 +10940,10 @@ get_varp(struct vimoption *p) #ifdef FEAT_SIGNS case PV_SCL: return (char_u *)&(curwin->w_p_scl); #endif +#ifdef FEAT_VARTABS + case PV_VSTS: return (char_u *)&(curbuf->b_p_vsts); + case PV_VTS: return (char_u *)&(curbuf->b_p_vts); +#endif default: IEMSG(_("E356: get_varp ERROR")); } /* always return a valid pointer to avoid a crash! */ @@ -11138,6 +11268,15 @@ buf_copy_options(buf_T *buf, int flags) #endif buf->b_p_sts = p_sts; buf->b_p_sts_nopaste = p_sts_nopaste; +#ifdef FEAT_VARTABS + 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; +#endif buf->b_p_sn = p_sn; #ifdef FEAT_COMMENTS buf->b_p_com = vim_strsave(p_com); @@ -11259,12 +11398,27 @@ buf_copy_options(buf_T *buf, int flags) * or to a help buffer. */ if (dont_do_help) + { buf->b_p_isk = save_p_isk; +#ifdef FEAT_VARTABS + 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; +#endif + } else { buf->b_p_isk = vim_strsave(p_isk); did_isk = TRUE; buf->b_p_ts = p_ts; +#ifdef FEAT_VARTABS + 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; +#endif buf->b_help = FALSE; if (buf->b_p_bt[0] == 'h') clear_string_option(&buf->b_p_bt); @@ -12084,6 +12238,12 @@ 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; +#ifdef FEAT_VARTABS + if (buf->b_p_vsts_nopaste) + vim_free(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; +#endif } /* save global options */ @@ -12102,6 +12262,11 @@ paste_option_changed(void) p_sts_nopaste = p_sts; p_tw_nopaste = p_tw; p_wm_nopaste = p_wm; +#ifdef FEAT_VARTABS + if (p_vsts_nopaste) + vim_free(p_vsts_nopaste); + p_vsts_nopaste = p_vsts && p_vsts != empty_option ? vim_strsave(p_vsts) : NULL; +#endif } /* @@ -12116,6 +12281,14 @@ 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 */ +#ifdef FEAT_VARTABS + if (buf->b_p_vsts) + free_string_option(buf->b_p_vsts); + buf->b_p_vsts = empty_option; + if (buf->b_p_vsts_array) + vim_free(buf->b_p_vsts_array); + buf->b_p_vsts_array = 0; +#endif } /* set global options */ @@ -12135,6 +12308,11 @@ paste_option_changed(void) p_wm = 0; p_sts = 0; p_ai = 0; +#ifdef FEAT_VARTABS + if (p_vsts) + free_string_option(p_vsts); + p_vsts = empty_option; +#endif } /* @@ -12150,6 +12328,18 @@ 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; +#ifdef FEAT_VARTABS + 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) + vim_free(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; +#endif } /* restore global options */ @@ -12170,6 +12360,11 @@ paste_option_changed(void) p_sts = p_sts_nopaste; p_tw = p_tw_nopaste; p_wm = p_wm_nopaste; +#ifdef FEAT_VARTABS + if (p_vsts) + free_string_option(p_vsts); + p_vsts = p_vsts_nopaste ? vim_strsave(p_vsts_nopaste) : empty_option; +#endif } old_p_paste = p_paste; @@ -12510,6 +12705,295 @@ check_ff_value(char_u *p) return check_opt_strings(p, p_ff_values, FALSE); } +#ifdef FEAT_VARTABS + +/* + * Set the integer values corresponding to the string setting of 'vartabstop'. + */ + int +tabstop_set(char_u *var, int **array) +{ + int 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 (VIM_ISDIGIT(*cp)) + continue; + if (*cp == ',' && cp > var && *(cp - 1) != ',') + { + ++valcount; + continue; + } + EMSG(_(e_invarg)); + return FALSE; + } + + *array = (int *) alloc((unsigned) ((valcount + 1) * sizeof(int))); + (*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, int ts_arg, int *vts) +{ + int ts = ts_arg == 0 ? 8 : ts_arg; + int tabcount; + colnr_T tabcol = 0; + int t; + int padding = 0; + + if (vts == NULL || vts[0] == 0) + return ts - (col % 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 padding; +} + +/* + * Find the size of the tab that covers a particular column. + */ + int +tabstop_at(colnr_T col, int ts, int *vts) +{ + int tabcount; + colnr_T tabcol = 0; + int t; + int tab_size = 0; + + if (vts == 0 || vts[0] == 0) + return ts; + + tabcount = vts[0]; + for (t = 1; t <= tabcount; ++t) + { + tabcol += vts[t]; + if (tabcol > col) + { + tab_size = vts[t]; + break; + } + } + if (t > tabcount) + tab_size = vts[tabcount]; + + return tab_size; +} + +/* + * Find the column on which a tab starts. + */ + colnr_T +tabstop_start(colnr_T col, int ts, int *vts) +{ + int tabcount; + colnr_T tabcol = 0; + int t; + int excess; + + if (vts == 0 || vts[0] == 0) + return (col / ts) * ts; + + tabcount = vts[0]; + for (t = 1; t <= tabcount; ++t) + { + tabcol += vts[t]; + if (tabcol > col) + return tabcol - vts[t]; + } + + excess = tabcol % vts[tabcount]; + return excess + ((col - excess) / vts[tabcount]) * 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, + int ts, + int *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 = ts - (start_col % 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 = 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 = vts[tabcount] - (int)((start_col - tabcol) % 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 = vts[t]; + if (spaces < padding) + { + *nspcs = spaces; + return; + } + ++*ntabs; + spaces -= padding; + } + + *ntabs += spaces / vts[tabcount]; + *nspcs = spaces % vts[tabcount]; +} + +/* + * See if two tabstop arrays contain the same values. + */ + int +tabstop_eq(int *ts1, int *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(int *oldts) +{ + int *newts; + int t; + + if (oldts == 0) + return 0; + + newts = (int *) alloc((unsigned) ((oldts[0] + 1) * sizeof(int))); + for (t = 0; t <= oldts[0]; ++t) + newts[t] = oldts[t]; + + return newts; +} + +/* + * Return a count of the number of tabstops. + */ + int +tabstop_count(int *ts) +{ + return ts != NULL ? ts[0] : 0; +} + +/* + * Return the first tabstop, or 8 if there are no tabstops defined. + */ + int +tabstop_first(int *ts) +{ + return ts != NULL ? ts[1] : 8; +} + +#endif + /* * Return the effective shiftwidth value for current buffer, using the * 'tabstop' value when 'shiftwidth' is zero.