Patch 8.2.2280
Problem:    Fuzzy matching doesn't give access to the scores.
Solution:   Return the scores with a third list. (Yegappan Lakshmanan,
            closes #7596)
Files:      runtime/doc/eval.txt, src/search.c,
            src/testdir/test_matchfuzzy.vim


*** ../vim-8.2.2279/runtime/doc/eval.txt        2020-12-28 12:56:54.179617297 
+0100
--- runtime/doc/eval.txt        2021-01-02 18:26:56.028148817 +0100
***************
*** 7344,7350 ****
  
  
  matchfuzzy({list}, {str} [, {dict}])                  *matchfuzzy()*
!               If {list} is a list of strings, then returns a list with all
                the strings in {list} that fuzzy match {str}. The strings in
                the returned list are sorted based on the matching score.
  
--- 7414,7420 ----
  
  
  matchfuzzy({list}, {str} [, {dict}])                  *matchfuzzy()*
!               If {list} is a list of strings, then returns a |List| with all
                the strings in {list} that fuzzy match {str}. The strings in
                the returned list are sorted based on the matching score.
  
***************
*** 7401,7422 ****
  
  matchfuzzypos({list}, {str} [, {dict}])                       
*matchfuzzypos()*
                Same as |matchfuzzy()|, but returns the list of matched
!               strings and the list of character positions where characters
!               in {str} matches.
  
                If {str} matches multiple times in a string, then only the
                positions for the best match is returned.
  
                If there are no matching strings or there is an error, then a
!               list with two empty list items is returned.
  
                Example: >
                        :echo matchfuzzypos(['testing'], 'tsg')
! <             results in [['testing'], [[0, 2, 6]]] >
                        :echo matchfuzzypos(['clay', 'lacy'], 'la')
! <             results in [['lacy', 'clay'], [[0, 1], [1, 2]]] >
                        :echo [{'text': 'hello', 'id' : 
10}]->matchfuzzypos('ll', {'key' : 'text'})
! <             results in [{'id': 10, 'text': 'hello'}] [[2, 3]]
  
  matchlist({expr}, {pat} [, {start} [, {count}]])              *matchlist()*
                Same as |match()|, but return a |List|.  The first item in the
--- 7471,7494 ----
  
  matchfuzzypos({list}, {str} [, {dict}])                       
*matchfuzzypos()*
                Same as |matchfuzzy()|, but returns the list of matched
!               strings, the list of character positions where characters
!               in {str} matches and a list of matching scores.  You can
!               use |byteidx()|to convert a character position to a byte
!               position.
  
                If {str} matches multiple times in a string, then only the
                positions for the best match is returned.
  
                If there are no matching strings or there is an error, then a
!               list with three empty list items is returned.
  
                Example: >
                        :echo matchfuzzypos(['testing'], 'tsg')
! <             results in [['testing'], [[0, 2, 6]], [99]] >
                        :echo matchfuzzypos(['clay', 'lacy'], 'la')
! <             results in [['lacy', 'clay'], [[0, 1], [1, 2]], [153, 133]] >
                        :echo [{'text': 'hello', 'id' : 
10}]->matchfuzzypos('ll', {'key' : 'text'})
! <             results in [[{'id': 10, 'text': 'hello'}], [[2, 3]], [127]]
  
  matchlist({expr}, {pat} [, {start} [, {count}]])              *matchlist()*
                Same as |match()|, but return a |List|.  The first item in the
*** ../vim-8.2.2279/src/search.c        2020-12-21 19:59:04.569197722 +0100
--- src/search.c        2021-01-02 18:26:56.028148817 +0100
***************
*** 4723,4732 ****
  
        // For matchfuzzy(), return a list of matched strings.
        //          ['str1', 'str2', 'str3']
!       // For matchfuzzypos(), return a list with two items.
        // The first item is a list of matched strings. The second item
        // is a list of lists where each list item is a list of matched
!       // character positions.
        //      [['str1', 'str2', 'str3'], [[1, 3], [1, 3], [1, 3]]]
        if (retmatchpos)
        {
--- 4723,4732 ----
  
        // For matchfuzzy(), return a list of matched strings.
        //          ['str1', 'str2', 'str3']
!       // For matchfuzzypos(), return a list with three items.
        // The first item is a list of matched strings. The second item
        // is a list of lists where each list item is a list of matched
!       // character positions. The third item is a list of matching scores.
        //      [['str1', 'str2', 'str3'], [[1, 3], [1, 3], [1, 3]]]
        if (retmatchpos)
        {
***************
*** 4749,4755 ****
        // next copy the list of matching positions
        if (retmatchpos)
        {
!           li = list_find(fmatchlist, -1);
            if (li == NULL || li->li_tv.vval.v_list == NULL)
                goto done;
            l = li->li_tv.vval.v_list;
--- 4749,4755 ----
        // next copy the list of matching positions
        if (retmatchpos)
        {
!           li = list_find(fmatchlist, -2);
            if (li == NULL || li->li_tv.vval.v_list == NULL)
                goto done;
            l = li->li_tv.vval.v_list;
***************
*** 4762,4767 ****
--- 4762,4780 ----
                        list_append_list(l, ptrs[i].lmatchpos) == FAIL)
                    goto done;
            }
+ 
+           // copy the matching scores
+           li = list_find(fmatchlist, -1);
+           if (li == NULL || li->li_tv.vval.v_list == NULL)
+               goto done;
+           l = li->li_tv.vval.v_list;
+           for (i = 0; i < len; i++)
+           {
+               if (ptrs[i].score == SCORE_NONE)
+                   break;
+               if (list_append_number(l, ptrs[i].score) == FAIL)
+                   goto done;
+           }
        }
      }
  
***************
*** 4842,4850 ****
      {
        list_T  *l;
  
!       // For matchfuzzypos(), a list with two items are returned. First item
!       // is a list of matching strings and the second item is a list of
!       // lists with matching positions within each string.
        l = list_alloc();
        if (l == NULL)
            goto done;
--- 4855,4869 ----
      {
        list_T  *l;
  
!       // For matchfuzzypos(), a list with three items are returned. First
!       // item is a list of matching strings, the second item is a list of
!       // lists with matching positions within each string and the third item
!       // is the list of scores of the matches.
!       l = list_alloc();
!       if (l == NULL)
!           goto done;
!       if (list_append_list(rettv->vval.v_list, l) == FAIL)
!           goto done;
        l = list_alloc();
        if (l == NULL)
            goto done;
*** ../vim-8.2.2279/src/testdir/test_matchfuzzy.vim     2020-10-29 
18:57:57.791880416 +0100
--- src/testdir/test_matchfuzzy.vim     2021-01-02 18:26:56.028148817 +0100
***************
*** 93,147 ****
  
  " Test for the matchfuzzypos() function
  func Test_matchfuzzypos()
!   call assert_equal([['curl', 'world'], [[2,3], [2,3]]], 
matchfuzzypos(['world', 'curl'], 'rl'))
!   call assert_equal([['curl', 'world'], [[2,3], [2,3]]], 
matchfuzzypos(['world', 'one', 'curl'], 'rl'))
    call assert_equal([['hello', 'hello world hello world'],
!         \ [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]]],
          \ matchfuzzypos(['hello world hello world', 'hello', 'world'], 
'hello'))
!   call assert_equal([['aaaaaaa'], [[0, 1, 2]]], matchfuzzypos(['aaaaaaa'], 
'aaa'))
!   call assert_equal([['a  b'], [[0, 3]]], matchfuzzypos(['a  b'], 'a  b'))
!   call assert_equal([['a  b'], [[0, 3]]], matchfuzzypos(['a  b'], 'a    b'))
!   call assert_equal([['a  b'], [[0]]], matchfuzzypos(['a  b'], '  a  '))
!   call assert_equal([[], []], matchfuzzypos(['a  b'], '  '))
!   call assert_equal([[], []], matchfuzzypos(['world', 'curl'], 'ab'))
    let x = matchfuzzypos([repeat('a', 256)], repeat('a', 256))
    call assert_equal(range(256), x[1][0])
!   call assert_equal([[], []], matchfuzzypos([repeat('a', 300)], repeat('a', 
257)))
!   call assert_equal([[], []], matchfuzzypos([], 'abc'))
  
    " match in a long string
!   call assert_equal([[repeat('x', 300) .. 'abc'], [[300, 301, 302]]],
          \ matchfuzzypos([repeat('x', 300) .. 'abc'], 'abc'))
  
    " preference for camel case match
!   call assert_equal([['xabcxxaBc'], [[6, 7, 8]]], 
matchfuzzypos(['xabcxxaBc'], 'abc'))
    " preference for match after a separator (_ or space)
!   call assert_equal([['xabx_ab'], [[5, 6]]], matchfuzzypos(['xabx_ab'], 'ab'))
    " preference for leading letter match
!   call assert_equal([['abcxabc'], [[0, 1]]], matchfuzzypos(['abcxabc'], 'ab'))
    " preference for sequential match
!   call assert_equal([['aobncedone'], [[7, 8, 9]]], 
matchfuzzypos(['aobncedone'], 'one'))
    " best recursive match
!   call assert_equal([['xoone'], [[2, 3, 4]]], matchfuzzypos(['xoone'], 'one'))
  
    " match multiple words (separated by space)
!   call assert_equal([['foo bar baz'], [[8, 9, 10, 0, 1, 2]]], ['foo bar baz', 
'foo', 'foo bar', 'baz bar']->matchfuzzypos('baz foo'))
!   call assert_equal([[], []], ['foo bar baz', 'foo', 'foo bar', 'baz 
bar']->matchfuzzypos('one two'))
!   call assert_equal([[], []], ['foo bar']->matchfuzzypos(" \t "))
!   call assert_equal([['grace'], [[1, 2, 3, 4, 2, 3, 4, 0, 1, 2, 3, 4]]], 
['grace']->matchfuzzypos('race ace grace'))
  
    let l = [{'id' : 5, 'val' : 'crayon'}, {'id' : 6, 'val' : 'camera'}]
!   call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]]],
          \ matchfuzzypos(l, 'cam', {'text_cb' : {v -> v.val}}))
!   call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]]],
          \ matchfuzzypos(l, 'cam', {'key' : 'val'}))
!   call assert_equal([[], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> 
v.val}}))
!   call assert_equal([[], []], matchfuzzypos(l, 'day', {'key' : 'val'}))
    call assert_fails("let x = matchfuzzypos(l, 'cam', 'random')", 'E715:')
!   call assert_equal([[], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> 
[]}}))
!   call assert_equal([[], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> 1}}))
    call assert_fails("let x = matchfuzzypos(l, 'day', {'text_cb' : {a, b -> 
1}})", 'E119:')
!   call assert_equal([[], []], matchfuzzypos(l, 'cam'))
    call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 
'E921:')
    call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E730:')
    call assert_fails("let x = matchfuzzypos(l, 'cam', test_null_dict())", 
'E715:')
--- 93,147 ----
  
  " Test for the matchfuzzypos() function
  func Test_matchfuzzypos()
!   call assert_equal([['curl', 'world'], [[2,3], [2,3]], [128, 127]], 
matchfuzzypos(['world', 'curl'], 'rl'))
!   call assert_equal([['curl', 'world'], [[2,3], [2,3]], [128, 127]], 
matchfuzzypos(['world', 'one', 'curl'], 'rl'))
    call assert_equal([['hello', 'hello world hello world'],
!         \ [[0, 1, 2, 3, 4], [0, 1, 2, 3, 4]], [275, 257]],
          \ matchfuzzypos(['hello world hello world', 'hello', 'world'], 
'hello'))
!   call assert_equal([['aaaaaaa'], [[0, 1, 2]], [191]], 
matchfuzzypos(['aaaaaaa'], 'aaa'))
!   call assert_equal([['a  b'], [[0, 3]], [219]], matchfuzzypos(['a  b'], 'a  
b'))
!   call assert_equal([['a  b'], [[0, 3]], [219]], matchfuzzypos(['a  b'], 'a   
 b'))
!   call assert_equal([['a  b'], [[0]], [112]], matchfuzzypos(['a  b'], '  a  
'))
!   call assert_equal([[], [], []], matchfuzzypos(['a  b'], '  '))
!   call assert_equal([[], [], []], matchfuzzypos(['world', 'curl'], 'ab'))
    let x = matchfuzzypos([repeat('a', 256)], repeat('a', 256))
    call assert_equal(range(256), x[1][0])
!   call assert_equal([[], [], []], matchfuzzypos([repeat('a', 300)], 
repeat('a', 257)))
!   call assert_equal([[], [], []], matchfuzzypos([], 'abc'))
  
    " match in a long string
!   call assert_equal([[repeat('x', 300) .. 'abc'], [[300, 301, 302]], [-135]],
          \ matchfuzzypos([repeat('x', 300) .. 'abc'], 'abc'))
  
    " preference for camel case match
!   call assert_equal([['xabcxxaBc'], [[6, 7, 8]], [189]], 
matchfuzzypos(['xabcxxaBc'], 'abc'))
    " preference for match after a separator (_ or space)
!   call assert_equal([['xabx_ab'], [[5, 6]], [145]], 
matchfuzzypos(['xabx_ab'], 'ab'))
    " preference for leading letter match
!   call assert_equal([['abcxabc'], [[0, 1]], [150]], 
matchfuzzypos(['abcxabc'], 'ab'))
    " preference for sequential match
!   call assert_equal([['aobncedone'], [[7, 8, 9]], [158]], 
matchfuzzypos(['aobncedone'], 'one'))
    " best recursive match
!   call assert_equal([['xoone'], [[2, 3, 4]], [168]], matchfuzzypos(['xoone'], 
'one'))
  
    " match multiple words (separated by space)
!   call assert_equal([['foo bar baz'], [[8, 9, 10, 0, 1, 2]], [369]], ['foo 
bar baz', 'foo', 'foo bar', 'baz bar']->matchfuzzypos('baz foo'))
!   call assert_equal([[], [], []], ['foo bar baz', 'foo', 'foo bar', 'baz 
bar']->matchfuzzypos('one two'))
!   call assert_equal([[], [], []], ['foo bar']->matchfuzzypos(" \t "))
!   call assert_equal([['grace'], [[1, 2, 3, 4, 2, 3, 4, 0, 1, 2, 3, 4]], 
[657]], ['grace']->matchfuzzypos('race ace grace'))
  
    let l = [{'id' : 5, 'val' : 'crayon'}, {'id' : 6, 'val' : 'camera'}]
!   call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]], [192]],
          \ matchfuzzypos(l, 'cam', {'text_cb' : {v -> v.val}}))
!   call assert_equal([[{'id' : 6, 'val' : 'camera'}], [[0, 1, 2]], [192]],
          \ matchfuzzypos(l, 'cam', {'key' : 'val'}))
!   call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> 
v.val}}))
!   call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'key' : 'val'}))
    call assert_fails("let x = matchfuzzypos(l, 'cam', 'random')", 'E715:')
!   call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> 
[]}}))
!   call assert_equal([[], [], []], matchfuzzypos(l, 'day', {'text_cb' : {v -> 
1}}))
    call assert_fails("let x = matchfuzzypos(l, 'day', {'text_cb' : {a, b -> 
1}})", 'E119:')
!   call assert_equal([[], [], []], matchfuzzypos(l, 'cam'))
    call assert_fails("let x = matchfuzzypos(l, 'cam', {'text_cb' : []})", 
'E921:')
    call assert_fails("let x = matchfuzzypos(l, 'foo', {'key' : []})", 'E730:')
    call assert_fails("let x = matchfuzzypos(l, 'cam', test_null_dict())", 
'E715:')
***************
*** 193,233 ****
  " Test for matchfuzzypos() with multibyte characters
  func Test_matchfuzzypos_mbyte()
    CheckFeature multi_lang
!   call assert_equal([['こんにちは世界'], [[0, 1, 2, 3, 4]]],
          \ matchfuzzypos(['こんにちは世界'], 'こんにちは'))
!   call assert_equal([['ンヹㄇヺヴ'], [[1, 3]]], matchfuzzypos(['ンヹㄇヺヴ'], 'ヹヺ'))
    " reverse the order of characters
!   call assert_equal([[], []], matchfuzzypos(['ンヹㄇヺヴ'], 'ヺヹ'))
!   call assert_equal([['αβΩxxx', 'xαxβxΩx'], [[0, 1, 2], [1, 3, 5]]],
          \ matchfuzzypos(['αβΩxxx', 'xαxβxΩx'], 'αβΩ'))
    call assert_equal([['ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ', 'πbπ'],
!         \ [[0, 1], [0, 1], [0, 1], [0, 2]]],
          \ matchfuzzypos(['πbπ', 'ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ'], 'ππ'))
!   call assert_equal([['ααααααα'], [[0, 1, 2]]],
          \ matchfuzzypos(['ααααααα'], 'ααα'))
  
!   call assert_equal([[], []], matchfuzzypos(['ンヹㄇ', 'ŗŝţ'], 'fffifl'))
    let x = matchfuzzypos([repeat('Ψ', 256)], repeat('Ψ', 256))
    call assert_equal(range(256), x[1][0])
!   call assert_equal([[], []], matchfuzzypos([repeat('✓', 300)], repeat('✓', 
257)))
  
    " match multiple words (separated by space)
!   call assert_equal([['세 마리의 작은 돼지'], [[9, 10, 2, 3, 4]]], ['세 마리의 작은 돼지', 
'마리의', '마리의 작은', '작은 돼지']->matchfuzzypos('돼지 마리의'))
!   call assert_equal([[], []], ['세 마리의 작은 돼지', '마리의', '마리의 작은', '작은 
돼지']->matchfuzzypos('파란 하늘'))
  
    " match in a long string
!   call assert_equal([[repeat('ぶ', 300) .. 'ẼẼẼ'], [[300, 301, 302]]],
          \ matchfuzzypos([repeat('ぶ', 300) .. 'ẼẼẼ'], 'ẼẼẼ'))
    " preference for camel case match
!   call assert_equal([['xѳѵҁxxѳѴҁ'], [[6, 7, 8]]], 
matchfuzzypos(['xѳѵҁxxѳѴҁ'], 'ѳѵҁ'))
    " preference for match after a separator (_ or space)
!   call assert_equal([['xちだx_ちだ'], [[5, 6]]], matchfuzzypos(['xちだx_ちだ'], 'ちだ'))
    " preference for leading letter match
!   call assert_equal([['ѳѵҁxѳѵҁ'], [[0, 1]]], matchfuzzypos(['ѳѵҁxѳѵҁ'], 'ѳѵ'))
    " preference for sequential match
!   call assert_equal([['aンbヹcㄇdンヹㄇ'], [[7, 8, 9]]], 
matchfuzzypos(['aンbヹcㄇdンヹㄇ'], 'ンヹㄇ'))
    " best recursive match
!   call assert_equal([['xффйд'], [[2, 3, 4]]], matchfuzzypos(['xффйд'], 'фйд'))
  endfunc
  
  " vim: shiftwidth=2 sts=2 expandtab
--- 193,233 ----
  " Test for matchfuzzypos() with multibyte characters
  func Test_matchfuzzypos_mbyte()
    CheckFeature multi_lang
!   call assert_equal([['こんにちは世界'], [[0, 1, 2, 3, 4]], [273]],
          \ matchfuzzypos(['こんにちは世界'], 'こんにちは'))
!   call assert_equal([['ンヹㄇヺヴ'], [[1, 3]], [88]], matchfuzzypos(['ンヹㄇヺヴ'], 
'ヹヺ'))
    " reverse the order of characters
!   call assert_equal([[], [], []], matchfuzzypos(['ンヹㄇヺヴ'], 'ヺヹ'))
!   call assert_equal([['αβΩxxx', 'xαxβxΩx'], [[0, 1, 2], [1, 3, 5]], [222, 
113]],
          \ matchfuzzypos(['αβΩxxx', 'xαxβxΩx'], 'αβΩ'))
    call assert_equal([['ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ', 'πbπ'],
!         \ [[0, 1], [0, 1], [0, 1], [0, 2]], [151, 148, 145, 110]],
          \ matchfuzzypos(['πbπ', 'ππbbππ', 'πππbbbπππ', 'ππππbbbbππππ'], 'ππ'))
!   call assert_equal([['ααααααα'], [[0, 1, 2]], [191]],
          \ matchfuzzypos(['ααααααα'], 'ααα'))
  
!   call assert_equal([[], [], []], matchfuzzypos(['ンヹㄇ', 'ŗŝţ'], 'fffifl'))
    let x = matchfuzzypos([repeat('Ψ', 256)], repeat('Ψ', 256))
    call assert_equal(range(256), x[1][0])
!   call assert_equal([[], [], []], matchfuzzypos([repeat('✓', 300)], 
repeat('✓', 257)))
  
    " match multiple words (separated by space)
!   call assert_equal([['세 마리의 작은 돼지'], [[9, 10, 2, 3, 4]], [328]], ['세 마리의 작은 
돼지', '마리의', '마리의 작은', '작은 돼지']->matchfuzzypos('돼지 마리의'))
!   call assert_equal([[], [], []], ['세 마리의 작은 돼지', '마리의', '마리의 작은', '작은 
돼지']->matchfuzzypos('파란 하늘'))
  
    " match in a long string
!   call assert_equal([[repeat('ぶ', 300) .. 'ẼẼẼ'], [[300, 301, 302]], [-135]],
          \ matchfuzzypos([repeat('ぶ', 300) .. 'ẼẼẼ'], 'ẼẼẼ'))
    " preference for camel case match
!   call assert_equal([['xѳѵҁxxѳѴҁ'], [[6, 7, 8]], [189]], 
matchfuzzypos(['xѳѵҁxxѳѴҁ'], 'ѳѵҁ'))
    " preference for match after a separator (_ or space)
!   call assert_equal([['xちだx_ちだ'], [[5, 6]], [145]], 
matchfuzzypos(['xちだx_ちだ'], 'ちだ'))
    " preference for leading letter match
!   call assert_equal([['ѳѵҁxѳѵҁ'], [[0, 1]], [150]], 
matchfuzzypos(['ѳѵҁxѳѵҁ'], 'ѳѵ'))
    " preference for sequential match
!   call assert_equal([['aンbヹcㄇdンヹㄇ'], [[7, 8, 9]], [158]], 
matchfuzzypos(['aンbヹcㄇdンヹㄇ'], 'ンヹㄇ'))
    " best recursive match
!   call assert_equal([['xффйд'], [[2, 3, 4]], [168]], matchfuzzypos(['xффйд'], 
'фйд'))
  endfunc
  
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.2279/src/version.c       2021-01-02 18:17:22.946191468 +0100
--- src/version.c       2021-01-02 18:28:01.463911156 +0100
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     2280,
  /**/

-- 
You are only young once, but you can stay immature indefinitely.

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.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].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202101021731.102HVxqg1906426%40masaka.moolenaar.net.

Raspunde prin e-mail lui