# HG changeset patch # User Bram Moolenaar # Date 1601066706 -7200 # Node ID 770fe121ca641cdcb2ca786046908e52a0399f1f # Parent 66f9cafbb9b8627b22911df883bba88c409cdf40 patch 8.2.1741: pathshorten() only supports using one character Commit: https://github.com/vim/vim/commit/6a33ef0deb5c75c003a9f3bd1c57f3ca5e77327e Author: Bram Moolenaar Date: Fri Sep 25 22:42:48 2020 +0200 patch 8.2.1741: pathshorten() only supports using one character Problem: pathshorten() only supports using one character. Solution: Add an argument to control the length. (closes https://github.com/vim/vim/issues/7006) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2661,7 +2661,7 @@ mzeval({expr}) any evaluate |MzScheme| nextnonblank({lnum}) Number line nr of non-blank line >= {lnum} nr2char({expr} [, {utf8}]) String single char with ASCII/UTF8 value {expr} or({expr}, {expr}) Number bitwise OR -pathshorten({expr}) String shorten directory names in a path +pathshorten({expr} [, {len}]) String shorten directory names in a path perleval({expr}) any evaluate |Perl| expression popup_atcursor({what}, {options}) Number create popup window near the cursor popup_beval({what}, {options}) Number create popup window for 'ballooneval' @@ -7656,13 +7656,17 @@ or({expr}, {expr}) *or()* :let bits = bits->or(0x80) -pathshorten({expr}) *pathshorten()* +pathshorten({expr} [, {len}]) *pathshorten()* Shorten directory names in the path {expr} and return the result. The tail, the file name, is kept as-is. The other - components in the path are reduced to single letters. Leading - '~' and '.' characters are kept. Example: > + components in the path are reduced to {len} letters in length. + If {len} is omitted or smaller than 1 then 1 is used (single + letters). Leading '~' and '.' characters are kept. Examples: > :echo pathshorten('~/.vim/autoload/myfile.vim') < ~/.v/a/myfile.vim ~ +> + :echo pathshorten('~/.vim/autoload/myfile.vim', 2) +< ~/.vi/au/myfile.vim ~ It doesn't matter if the path exists or not. Can also be used as a |method|: > diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -779,7 +779,7 @@ static funcentry_T global_functions[] = {"nextnonblank", 1, 1, FEARG_1, ret_number, f_nextnonblank}, {"nr2char", 1, 2, FEARG_1, ret_string, f_nr2char}, {"or", 2, 2, FEARG_1, ret_number, f_or}, - {"pathshorten", 1, 1, FEARG_1, ret_string, f_pathshorten}, + {"pathshorten", 1, 2, FEARG_1, ret_string, f_pathshorten}, {"perleval", 1, 1, FEARG_1, ret_any, #ifdef FEAT_PERL f_perleval diff --git a/src/filepath.c b/src/filepath.c --- a/src/filepath.c +++ b/src/filepath.c @@ -1352,15 +1352,87 @@ f_mkdir(typval_T *argvars, typval_T *ret } /* + * Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname" + * "trim_len" specifies how many characters to keep for each directory. + * Must be 1 or more. + * It's done in-place. + */ + static void +shorten_dir_len(char_u *str, int trim_len) +{ + char_u *tail, *s, *d; + int skip = FALSE; + int dirchunk_len = 0; + + tail = gettail(str); + d = str; + for (s = str; ; ++s) + { + if (s >= tail) // copy the whole tail + { + *d++ = *s; + if (*s == NUL) + break; + } + else if (vim_ispathsep(*s)) // copy '/' and next char + { + *d++ = *s; + skip = FALSE; + dirchunk_len = 0; + } + else if (!skip) + { + *d++ = *s; // copy next char + if (*s != '~' && *s != '.') // and leading "~" and "." + { + ++dirchunk_len; // only count word chars for the size + + // keep copying chars until we have our preferred length (or + // until the above if/else branches move us along) + if (dirchunk_len >= trim_len) + skip = TRUE; + } + + if (has_mbyte) + { + int l = mb_ptr2len(s); + + while (--l > 0) + *d++ = *++s; + } + } + } +} + +/* + * Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname" + * It's done in-place. + */ + void +shorten_dir(char_u *str) +{ + shorten_dir_len(str, 1); +} + +/* * "pathshorten()" function */ void f_pathshorten(typval_T *argvars, typval_T *rettv) { char_u *p; + int trim_len = 1; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + trim_len = (int)tv_get_number(&argvars[1]); + if (trim_len < 1) + trim_len = 1; + } rettv->v_type = VAR_STRING; p = tv_get_string_chk(&argvars[0]); + if (p == NULL) rettv->vval.v_string = NULL; else @@ -1368,7 +1440,7 @@ f_pathshorten(typval_T *argvars, typval_ p = vim_strsave(p); rettv->vval.v_string = p; if (p != NULL) - shorten_dir(p); + shorten_dir_len(p, trim_len); } } @@ -2707,47 +2779,6 @@ vim_ispathsep_nocolon(int c) } /* - * Shorten the path of a file from "~/foo/../.bar/fname" to "~/f/../.b/fname" - * It's done in-place. - */ - void -shorten_dir(char_u *str) -{ - char_u *tail, *s, *d; - int skip = FALSE; - - tail = gettail(str); - d = str; - for (s = str; ; ++s) - { - if (s >= tail) // copy the whole tail - { - *d++ = *s; - if (*s == NUL) - break; - } - else if (vim_ispathsep(*s)) // copy '/' and next char - { - *d++ = *s; - skip = FALSE; - } - else if (!skip) - { - *d++ = *s; // copy next char - if (*s != '~' && *s != '.') // and leading "~" and "." - skip = TRUE; - if (has_mbyte) - { - int l = mb_ptr2len(s); - - while (--l > 0) - *d++ = *++s; - } - } - } -} - -/* * Return TRUE if the directory of "fname" exists, FALSE otherwise. * Also returns TRUE if there is no directory name. * "fname" must be writable!. diff --git a/src/proto/filepath.pro b/src/proto/filepath.pro --- a/src/proto/filepath.pro +++ b/src/proto/filepath.pro @@ -21,6 +21,7 @@ void f_glob2regpat(typval_T *argvars, ty void f_globpath(typval_T *argvars, typval_T *rettv); void f_isdirectory(typval_T *argvars, typval_T *rettv); void f_mkdir(typval_T *argvars, typval_T *rettv); +void shorten_dir(char_u *str); void f_pathshorten(typval_T *argvars, typval_T *rettv); void f_readdir(typval_T *argvars, typval_T *rettv); void f_readdirex(typval_T *argvars, typval_T *rettv); @@ -40,7 +41,6 @@ char_u *getnextcomp(char_u *fname); char_u *get_past_head(char_u *path); int vim_ispathsep(int c); int vim_ispathsep_nocolon(int c); -void shorten_dir(char_u *str); int dir_of_file_exists(char_u *fname); int vim_fnamecmp(char_u *x, char_u *y); int vim_fnamencmp(char_u *x, char_u *y, size_t len); diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -500,6 +500,24 @@ func Test_pathshorten() call assert_equal('.~f/bar', pathshorten('.~foo/bar')) call assert_equal('~/f/bar', pathshorten('~/foo/bar')) call assert_fails('call pathshorten([])', 'E730:') + + " test pathshorten with optional variable to set preferred size of shortening + call assert_equal('', pathshorten('', 2)) + call assert_equal('foo', pathshorten('foo', 2)) + call assert_equal('/foo', pathshorten('/foo', 2)) + call assert_equal('fo/', pathshorten('foo/', 2)) + call assert_equal('fo/bar', pathshorten('foo/bar', 2)) + call assert_equal('fo/ba/foobar', pathshorten('foo/bar/foobar', 2)) + call assert_equal('/fo/ba/foobar', pathshorten('/foo/bar/foobar', 2)) + call assert_equal('.fo/bar', pathshorten('.foo/bar', 2)) + call assert_equal('~fo/bar', pathshorten('~foo/bar', 2)) + call assert_equal('~.fo/bar', pathshorten('~.foo/bar', 2)) + call assert_equal('.~fo/bar', pathshorten('.~foo/bar', 2)) + call assert_equal('~/fo/bar', pathshorten('~/foo/bar', 2)) + call assert_fails('call pathshorten([],2)', 'E730:') + call assert_notequal('~/fo/bar', pathshorten('~/foo/bar', 3)) + call assert_equal('~/foo/bar', pathshorten('~/foo/bar', 3)) + call assert_equal('~/f/bar', pathshorten('~/foo/bar', 0)) endfunc func Test_strpart() diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1741, +/**/ 1740, /**/ 1739,