On 04-Aug-15, Olaf Dabrunz wrote:
> There is no simple way to prepare a file name parameter for bufnr() so
> that it reliably finds a specific buffer.
> 
> 
> The file name can contain file-pattern characters that can lead to more
> than one match, or a match attempt in buflist_findpat() can find a match
> on the wrong buffer.
> 
>     :let buf = bufnr('[my]_testfile')
> 
>     can match           does not match
> 
>         m_testfile          [my]_testfile
>         y_testfile

To make this even clearer, this can also match

        yay_testfile                                (1)
        try_testfile                                (2)
        m_testfile_for_a_different_project          (3)
        ...

When all three buffers exist, it will match on (3).  An un-anchored match
matches all three, but a single match is required.  Then the match with an
anchor at the start matches only (3), which is reported.

When only buffers (1) and (2) exist, either both match or none matches, so
no single match, and no match is reported.

When only buffer (1) OR (2) exists, it is matched and reported.

> According to ':he bufname()', to force a single full match, embed the
> filename in '^' and '$'.  Also, the file-pattern characters and special
> characters need to be escaped:
> 
>     :let buf = bufnr('^'. escape(fname, '*?\,{}[]^$%#') .'$')
> 
> But for some character combinations, the escaped characters do not match:
> 
>     :echo '^'. escape('test\{,}name', '*?\,{}[]^$%#') .'$'
>         ^test\\\{\,\}name$
> 
> In the escaped filename, '\\\{\,\}' is treated as the file-pattern
> '\\\{n,m\}', equivalent to '\{n,m}' in a regex (':he file-pattern').
>
> But the '\,' in the middle is syntactically incorrect, and the whole
> pattern is rejected.

Actually, '\\\{\,\}' is not rejected, but it is used as the regex pattern
'\{,}', which seems to work as '\{}', but is not documented (':he \{').

It still does not match '\{,}' in the buffer name, as it is interpreted as
a regex pattern, which is what the escaping tried to prevent.

In a slightly different example the pattern *is* rejected:

    :echo '^'. escape('test\{x}name', '*?\,{}[]^$%#') .'$'
        ^test\\\{x\}name$

A pattern match cannot be made, and a literal match fails.

>                       A literal match also fails.  Unless a buffer named
> '^test\\\{\,\}name$' exists, which would be a false match.
>
> According to ':he file-pattern', the only construct with magic escaped
> characters is '\\\{n,m\}'.
> 
> But file_pat_to_reg_pat() knows more, at least when '\' is allowed as a
> directory separator.  E.g.:
> 
>     '\*'    used as     '\.*'
>     '\?'    used as     '\.'
>             ...
> 
> All functions that use an {expr} as in bufname() have this problem:
> 
>     bufname()
>     bufnr()
>     bufwinnr()
>     getbufline()
>     getbufvar()
>     setbufvar()
> 
> 
> Suggested solution:
> 
> Add a {literal} parameter to bufnr(), which forces a literal match as in
> bufexists().
> 
> When a full path is used in the {expr} parameter, a match is also unique.
> 
> In all other functions, the result of bufnr() can be used.
> 
> ---
>  runtime/doc/eval.txt |    8 ++++++--
>  src/eval.c           |   17 +++++++++++++----
>  2 files changed, 19 insertions(+), 6 deletions(-)
> 
> Index: b/runtime/doc/eval.txt
> ===================================================================
> --- a/runtime/doc/eval.txt    2015-08-04 22:23:36.705032764 +0200
> +++ b/runtime/doc/eval.txt    2015-08-04 22:28:32.096557318 +0200
> @@ -1763,7 +1763,8 @@ bufexists( {expr})              Number  TRUE if buffe
>  buflisted( {expr})           Number  TRUE if buffer {expr} is listed
>  bufloaded( {expr})           Number  TRUE if buffer {expr} is loaded
>  bufname( {expr})             String  Name of the buffer {expr}
> -bufnr( {expr})                       Number  Number of the buffer {expr}
> +bufnr( {expr} [, {create} [, {literal}]])
> +                             Number  Number of the buffer {expr}
>  bufwinnr( {expr})            Number  window number of buffer {expr}
>  byte2line( {byte})           Number  line number at byte count {byte}
>  byteidx( {expr}, {nr})               Number  byte index of {nr}'th char in 
> {expr}
> @@ -2293,10 +2294,13 @@ bufname({expr})                                       
>         *bufname()*
>               Obsolete name: buffer_name().
>  
>                                                       *bufnr()*
> -bufnr({expr} [, {create}])
> +bufnr({expr} [, {create} [, {literal}]])
>               The result is the number of a buffer, as it is displayed by
>               the ":ls" command.  For the use of {expr}, see |bufname()|
>               above.
> +             If the {literal} argument is present and not zero, {expr} is
> +             used as in |bufexists()|.  Use this to prevent expansion of
> +             pattern wildcards in {expr}.
>               If the buffer doesn't exist, -1 is returned.  Or, if the
>               {create} argument is present and not zero, a new, unlisted,
>               buffer is created and its number is returned.
> Index: b/src/eval.c
> ===================================================================
> --- a/src/eval.c      2015-08-04 22:24:36.704935227 +0200
> +++ b/src/eval.c      2015-08-04 22:28:32.100557312 +0200
> @@ -8083,7 +8083,7 @@ static struct fst
>      {"buflisted",    1, 1, f_buflisted},
>      {"bufloaded",    1, 1, f_bufloaded},
>      {"bufname",              1, 1, f_bufname},
> -    {"bufnr",                1, 2, f_bufnr},
> +    {"bufnr",                1, 3, f_bufnr},
>      {"bufwinnr",     1, 1, f_bufwinnr},
>      {"byte2line",    1, 1, f_byte2line},
>      {"byteidx",              2, 2, f_byteidx},
> @@ -9388,9 +9388,18 @@ f_bufnr(argvars, rettv)
>      char_u   *name;
>  
>      (void)get_tv_number(&argvars[0]);            /* issue errmsg if type 
> error */
> -    ++emsg_off;
> -    buf = get_buf_tv(&argvars[0], FALSE);
> -    --emsg_off;
> +
> +    if (argvars[1].v_type != VAR_UNKNOWN
> +         && argvars[2].v_type != VAR_UNKNOWN
> +         && get_tv_number_chk(&argvars[2], &error) != 0
> +         && !error)
> +     buf = find_buffer(&argvars[0]);
> +    else
> +    {
> +     ++emsg_off;
> +     buf = get_buf_tv(&argvars[0], FALSE);
> +     --emsg_off;
> +    }
>  
>      /* If the buffer isn't found and the second argument is not zero create a
>       * new buffer. */
> -- 
> Olaf Dabrunz (oda <at> fctrace.org)
> 
> -- 
> -- 
> You received this message from the "vim_dev" maillist.
> Do not top-post! Type your reply below the text you are replying to.
> For more information, visit http://www.vim.org/maillist.php
> 
> --- 
> You received this message because you are subscribed to the Google Groups 
> "vim_dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to [email protected].
> For more options, visit https://groups.google.com/d/optout.

-- 
Olaf Dabrunz (oda <at> fctrace.org)

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui