mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	vim-patch:9.1.0009: Cannot easily get the list of matches (#27028)
Problem:  Cannot easily get the list of matches
Solution: Add the matchstrlist() and matchbufline() Vim script
          functions (Yegappan Lakshmanan)
closes: vim/vim#13766
Omit CHECK_LIST_MATERIALIZE(): it populates a List with numbers only,
and there is a check for strings below.
f93b1c881a
vim-patch:eb3475df0d92
runtime(doc): Replace non-breaking space with normal space (vim/vim#13868)
eb3475df0d
Co-authored-by: Yegappan Lakshmanan <4298407+yegappan@users.noreply.github.com>
			
			
This commit is contained in:
		
							
								
								
									
										73
									
								
								runtime/doc/builtin.txt
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										73
									
								
								runtime/doc/builtin.txt
									
									
									
										generated
									
									
									
								
							@@ -4480,6 +4480,47 @@ matcharg({nr})                                                      *matcharg()*
 | 
			
		||||
		Highlighting matches using the |:match| commands are limited
 | 
			
		||||
		to three matches. |matchadd()| does not have this limitation.
 | 
			
		||||
 | 
			
		||||
matchbufline({buf}, {pat}, {lnum}, {end}, [, {dict}])           *matchbufline()*
 | 
			
		||||
		Returns the |List| of matches in lines from {lnum} to {end} in
 | 
			
		||||
		buffer {buf} where {pat} matches.
 | 
			
		||||
 | 
			
		||||
		{lnum} and {end} can either be a line number or the string "$"
 | 
			
		||||
		to refer to the last line in {buf}.
 | 
			
		||||
 | 
			
		||||
		The {dict} argument supports following items:
 | 
			
		||||
		    submatches	include submatch information (|/\(|)
 | 
			
		||||
 | 
			
		||||
		For each match, a |Dict| with the following items is returned:
 | 
			
		||||
		    byteidx	starting byte index of the match
 | 
			
		||||
		    lnum	line number where there is a match
 | 
			
		||||
		    text	matched string
 | 
			
		||||
		Note that there can be multiple matches in a single line.
 | 
			
		||||
 | 
			
		||||
		This function works only for loaded buffers. First call
 | 
			
		||||
		|bufload()| if needed.
 | 
			
		||||
 | 
			
		||||
		When {buf} is not a valid buffer, the buffer is not loaded or
 | 
			
		||||
		{lnum} or {end} is not valid then an error is given and an
 | 
			
		||||
		empty |List| is returned.
 | 
			
		||||
 | 
			
		||||
		Examples: >vim
 | 
			
		||||
		    " Assuming line 3 in buffer 5 contains "a"
 | 
			
		||||
		    :echo matchbufline(5, '\<\k\+\>', 3, 3)
 | 
			
		||||
		    [{'lnum': 3, 'byteidx': 0, 'text': 'a'}]
 | 
			
		||||
		    " Assuming line 4 in buffer 10 contains "tik tok"
 | 
			
		||||
		    :echo matchbufline(10, '\<\k\+\>', 1, 4)
 | 
			
		||||
		    [{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}]
 | 
			
		||||
<
 | 
			
		||||
		If {submatch} is present and is v:true, then submatches like
 | 
			
		||||
		"\1", "\2", etc. are also returned.  Example: >vim
 | 
			
		||||
		    " Assuming line 2 in buffer 2 contains "acd"
 | 
			
		||||
		    :echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2
 | 
			
		||||
						\ {'submatches': v:true})
 | 
			
		||||
		    [{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]
 | 
			
		||||
<		The "submatches" List always contains 9 items.  If a submatch
 | 
			
		||||
		is not found, then an empty string is returned for that
 | 
			
		||||
		submatch.
 | 
			
		||||
 | 
			
		||||
matchdelete({id} [, {win}])                            *matchdelete()* *E802* *E803*
 | 
			
		||||
		Deletes a match with ID {id} previously defined by |matchadd()|
 | 
			
		||||
		or one of the |:match| commands.  Returns 0 if successful,
 | 
			
		||||
@@ -4617,6 +4658,36 @@ matchstr({expr}, {pat} [, {start} [, {count}]])                     *matchstr()*
 | 
			
		||||
		When {expr} is a |List| then the matching item is returned.
 | 
			
		||||
		The type isn't changed, it's not necessarily a String.
 | 
			
		||||
 | 
			
		||||
matchstrlist({list}, {pat} [, {dict}])                          *matchstrlist()*
 | 
			
		||||
		Returns the |List| of matches in {list} where {pat} matches.
 | 
			
		||||
		{list} is a |List| of strings.  {pat} is matched against each
 | 
			
		||||
		string in {list}.
 | 
			
		||||
 | 
			
		||||
		The {dict} argument supports following items:
 | 
			
		||||
		    submatches	include submatch information (|/\(|)
 | 
			
		||||
 | 
			
		||||
		For each match, a |Dict| with the following items is returned:
 | 
			
		||||
		    byteidx	starting byte index of the match.
 | 
			
		||||
		    idx		index in {list} of the match.
 | 
			
		||||
		    text	matched string
 | 
			
		||||
		    submatches	a List of submatches.  Present only if
 | 
			
		||||
				"submatches" is set to v:true in {dict}.
 | 
			
		||||
 | 
			
		||||
		Example: >vim
 | 
			
		||||
		    :echo matchstrlist(['tik tok'], '\<\k\+\>')
 | 
			
		||||
		    [{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}]
 | 
			
		||||
		    :echo matchstrlist(['a', 'b'], '\<\k\+\>')
 | 
			
		||||
		    [{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}]
 | 
			
		||||
<
 | 
			
		||||
		If "submatches" is present and is v:true, then submatches like
 | 
			
		||||
		"\1", "\2", etc. are also returned.  Example: >vim
 | 
			
		||||
		    :echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)',
 | 
			
		||||
						\ #{submatches: v:true})
 | 
			
		||||
		    [{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]
 | 
			
		||||
<		The "submatches" List always contains 9 items.  If a submatch
 | 
			
		||||
		is not found, then an empty string is returned for that
 | 
			
		||||
		submatch.
 | 
			
		||||
 | 
			
		||||
matchstrpos({expr}, {pat} [, {start} [, {count}]])               *matchstrpos()*
 | 
			
		||||
		Same as |matchstr()|, but return the matched string, the start
 | 
			
		||||
		position and the end position of the match.  Example: >vim
 | 
			
		||||
@@ -4643,7 +4714,7 @@ max({expr})                                                              *max()*
 | 
			
		||||
		it returns the maximum of all values in the Dictionary.
 | 
			
		||||
		If {expr} is neither a List nor a Dictionary, or one of the
 | 
			
		||||
		items in {expr} cannot be used as a Number this results in
 | 
			
		||||
		                an error.  An empty |List| or |Dictionary| results in zero.
 | 
			
		||||
		an error.  An empty |List| or |Dictionary| results in zero.
 | 
			
		||||
 | 
			
		||||
menu_get({path} [, {modes}])                                        *menu_get()*
 | 
			
		||||
		Returns a |List| of |Dictionaries| describing |menus| (defined
 | 
			
		||||
 
 | 
			
		||||
@@ -610,10 +610,13 @@ String manipulation:					*string-functions*
 | 
			
		||||
	toupper()		turn a string to uppercase
 | 
			
		||||
	charclass()		class of a character
 | 
			
		||||
	match()			position where a pattern matches in a string
 | 
			
		||||
	matchbufline()		all the matches of a pattern in a buffer
 | 
			
		||||
	matchend()		position where a pattern match ends in a string
 | 
			
		||||
	matchfuzzy()		fuzzy matches a string in a list of strings
 | 
			
		||||
	matchfuzzypos()		fuzzy matches a string in a list of strings
 | 
			
		||||
	matchstr()		match of a pattern in a string
 | 
			
		||||
	matchstrlist()		all the matches of a pattern in a List of
 | 
			
		||||
				strings
 | 
			
		||||
	matchstrpos()		match and positions of a pattern in a string
 | 
			
		||||
	matchlist()		like matchstr() and also return submatches
 | 
			
		||||
	stridx()		first index of a short string in a long string
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										85
									
								
								runtime/lua/vim/_meta/vimfn.lua
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										85
									
								
								runtime/lua/vim/_meta/vimfn.lua
									
									
									
										generated
									
									
									
								
							@@ -5412,6 +5412,54 @@ function vim.fn.matchaddpos(group, pos, priority, id, dict) end
 | 
			
		||||
--- @return any
 | 
			
		||||
function vim.fn.matcharg(nr) end
 | 
			
		||||
 | 
			
		||||
--- Returns the |List| of matches in lines from {lnum} to {end} in
 | 
			
		||||
--- buffer {buf} where {pat} matches.
 | 
			
		||||
---
 | 
			
		||||
--- {lnum} and {end} can either be a line number or the string "$"
 | 
			
		||||
--- to refer to the last line in {buf}.
 | 
			
		||||
---
 | 
			
		||||
--- The {dict} argument supports following items:
 | 
			
		||||
---     submatches  include submatch information (|/\(|)
 | 
			
		||||
---
 | 
			
		||||
--- For each match, a |Dict| with the following items is returned:
 | 
			
		||||
---     byteidx  starting byte index of the match
 | 
			
		||||
---     lnum  line number where there is a match
 | 
			
		||||
---     text  matched string
 | 
			
		||||
--- Note that there can be multiple matches in a single line.
 | 
			
		||||
---
 | 
			
		||||
--- This function works only for loaded buffers. First call
 | 
			
		||||
--- |bufload()| if needed.
 | 
			
		||||
---
 | 
			
		||||
--- When {buf} is not a valid buffer, the buffer is not loaded or
 | 
			
		||||
--- {lnum} or {end} is not valid then an error is given and an
 | 
			
		||||
--- empty |List| is returned.
 | 
			
		||||
---
 | 
			
		||||
--- Examples: >vim
 | 
			
		||||
---     " Assuming line 3 in buffer 5 contains "a"
 | 
			
		||||
---     :echo matchbufline(5, '\<\k\+\>', 3, 3)
 | 
			
		||||
---     [{'lnum': 3, 'byteidx': 0, 'text': 'a'}]
 | 
			
		||||
---     " Assuming line 4 in buffer 10 contains "tik tok"
 | 
			
		||||
---     :echo matchbufline(10, '\<\k\+\>', 1, 4)
 | 
			
		||||
---     [{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}]
 | 
			
		||||
--- <
 | 
			
		||||
--- If {submatch} is present and is v:true, then submatches like
 | 
			
		||||
--- "\1", "\2", etc. are also returned.  Example: >vim
 | 
			
		||||
---     " Assuming line 2 in buffer 2 contains "acd"
 | 
			
		||||
---     :echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2
 | 
			
		||||
---         \ {'submatches': v:true})
 | 
			
		||||
---     [{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]
 | 
			
		||||
--- <The "submatches" List always contains 9 items.  If a submatch
 | 
			
		||||
--- is not found, then an empty string is returned for that
 | 
			
		||||
--- submatch.
 | 
			
		||||
---
 | 
			
		||||
--- @param buf string|integer
 | 
			
		||||
--- @param pat string
 | 
			
		||||
--- @param lnum string|integer
 | 
			
		||||
--- @param end_ string|integer
 | 
			
		||||
--- @param dict? table
 | 
			
		||||
--- @return any
 | 
			
		||||
function vim.fn.matchbufline(buf, pat, lnum, end_, dict) end
 | 
			
		||||
 | 
			
		||||
--- Deletes a match with ID {id} previously defined by |matchadd()|
 | 
			
		||||
--- or one of the |:match| commands.  Returns 0 if successful,
 | 
			
		||||
--- otherwise -1.  See example for |matchadd()|.  All matches can
 | 
			
		||||
@@ -5581,6 +5629,41 @@ function vim.fn.matchlist(expr, pat, start, count) end
 | 
			
		||||
--- @return any
 | 
			
		||||
function vim.fn.matchstr(expr, pat, start, count) end
 | 
			
		||||
 | 
			
		||||
--- Returns the |List| of matches in {list} where {pat} matches.
 | 
			
		||||
--- {list} is a |List| of strings.  {pat} is matched against each
 | 
			
		||||
--- string in {list}.
 | 
			
		||||
---
 | 
			
		||||
--- The {dict} argument supports following items:
 | 
			
		||||
---     submatches  include submatch information (|/\(|)
 | 
			
		||||
---
 | 
			
		||||
--- For each match, a |Dict| with the following items is returned:
 | 
			
		||||
---     byteidx  starting byte index of the match.
 | 
			
		||||
---     idx    index in {list} of the match.
 | 
			
		||||
---     text  matched string
 | 
			
		||||
---     submatches  a List of submatches.  Present only if
 | 
			
		||||
---     "submatches" is set to v:true in {dict}.
 | 
			
		||||
---
 | 
			
		||||
--- Example: >vim
 | 
			
		||||
---     :echo matchstrlist(['tik tok'], '\<\k\+\>')
 | 
			
		||||
---     [{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}]
 | 
			
		||||
---     :echo matchstrlist(['a', 'b'], '\<\k\+\>')
 | 
			
		||||
---     [{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}]
 | 
			
		||||
--- <
 | 
			
		||||
--- If "submatches" is present and is v:true, then submatches like
 | 
			
		||||
--- "\1", "\2", etc. are also returned.  Example: >vim
 | 
			
		||||
---     :echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)',
 | 
			
		||||
---         \ #{submatches: v:true})
 | 
			
		||||
---     [{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]
 | 
			
		||||
--- <The "submatches" List always contains 9 items.  If a submatch
 | 
			
		||||
--- is not found, then an empty string is returned for that
 | 
			
		||||
--- submatch.
 | 
			
		||||
---
 | 
			
		||||
--- @param list string[]
 | 
			
		||||
--- @param pat string
 | 
			
		||||
--- @param dict? table
 | 
			
		||||
--- @return any
 | 
			
		||||
function vim.fn.matchstrlist(list, pat, dict) end
 | 
			
		||||
 | 
			
		||||
--- Same as |matchstr()|, but return the matched string, the start
 | 
			
		||||
--- position and the end position of the match.  Example: >vim
 | 
			
		||||
---   echo matchstrpos("testing", "ing")
 | 
			
		||||
@@ -5612,7 +5695,7 @@ function vim.fn.matchstrpos(expr, pat, start, count) end
 | 
			
		||||
--- it returns the maximum of all values in the Dictionary.
 | 
			
		||||
--- If {expr} is neither a List nor a Dictionary, or one of the
 | 
			
		||||
--- items in {expr} cannot be used as a Number this results in
 | 
			
		||||
---                 an error.  An empty |List| or |Dictionary| results in zero.
 | 
			
		||||
--- an error.  An empty |List| or |Dictionary| results in zero.
 | 
			
		||||
---
 | 
			
		||||
--- @param expr any
 | 
			
		||||
--- @return any
 | 
			
		||||
 
 | 
			
		||||
@@ -6615,6 +6615,60 @@ M.funcs = {
 | 
			
		||||
    params = { { 'nr', 'integer' } },
 | 
			
		||||
    signature = 'matcharg({nr})',
 | 
			
		||||
  },
 | 
			
		||||
  matchbufline = {
 | 
			
		||||
    args = { 4, 5 },
 | 
			
		||||
    base = 1,
 | 
			
		||||
    desc = [=[
 | 
			
		||||
      Returns the |List| of matches in lines from {lnum} to {end} in
 | 
			
		||||
      buffer {buf} where {pat} matches.
 | 
			
		||||
 | 
			
		||||
      {lnum} and {end} can either be a line number or the string "$"
 | 
			
		||||
      to refer to the last line in {buf}.
 | 
			
		||||
 | 
			
		||||
      The {dict} argument supports following items:
 | 
			
		||||
          submatches	include submatch information (|/\(|)
 | 
			
		||||
 | 
			
		||||
      For each match, a |Dict| with the following items is returned:
 | 
			
		||||
          byteidx	starting byte index of the match
 | 
			
		||||
          lnum	line number where there is a match
 | 
			
		||||
          text	matched string
 | 
			
		||||
      Note that there can be multiple matches in a single line.
 | 
			
		||||
 | 
			
		||||
      This function works only for loaded buffers. First call
 | 
			
		||||
      |bufload()| if needed.
 | 
			
		||||
 | 
			
		||||
      When {buf} is not a valid buffer, the buffer is not loaded or
 | 
			
		||||
      {lnum} or {end} is not valid then an error is given and an
 | 
			
		||||
      empty |List| is returned.
 | 
			
		||||
 | 
			
		||||
      Examples: >vim
 | 
			
		||||
          " Assuming line 3 in buffer 5 contains "a"
 | 
			
		||||
          :echo matchbufline(5, '\<\k\+\>', 3, 3)
 | 
			
		||||
          [{'lnum': 3, 'byteidx': 0, 'text': 'a'}]
 | 
			
		||||
          " Assuming line 4 in buffer 10 contains "tik tok"
 | 
			
		||||
          :echo matchbufline(10, '\<\k\+\>', 1, 4)
 | 
			
		||||
          [{'lnum': 4, 'byteidx': 0, 'text': 'tik'}, {'lnum': 4, 'byteidx': 4, 'text': 'tok'}]
 | 
			
		||||
      <
 | 
			
		||||
      If {submatch} is present and is v:true, then submatches like
 | 
			
		||||
      "\1", "\2", etc. are also returned.  Example: >vim
 | 
			
		||||
          " Assuming line 2 in buffer 2 contains "acd"
 | 
			
		||||
          :echo matchbufline(2, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 2, 2
 | 
			
		||||
      				\ {'submatches': v:true})
 | 
			
		||||
          [{'lnum': 2, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]
 | 
			
		||||
      <The "submatches" List always contains 9 items.  If a submatch
 | 
			
		||||
      is not found, then an empty string is returned for that
 | 
			
		||||
      submatch.
 | 
			
		||||
    ]=],
 | 
			
		||||
    name = 'matchbufline',
 | 
			
		||||
    params = {
 | 
			
		||||
      { 'buf', 'string|integer' },
 | 
			
		||||
      { 'pat', 'string' },
 | 
			
		||||
      { 'lnum', 'string|integer' },
 | 
			
		||||
      { 'end', 'string|integer' },
 | 
			
		||||
      { 'dict', 'table' },
 | 
			
		||||
    },
 | 
			
		||||
    signature = 'matchbufline({buf}, {pat}, {lnum}, {end}, [, {dict}])',
 | 
			
		||||
  },
 | 
			
		||||
  matchdelete = {
 | 
			
		||||
    args = { 1, 2 },
 | 
			
		||||
    base = 1,
 | 
			
		||||
@@ -6799,6 +6853,43 @@ M.funcs = {
 | 
			
		||||
    params = { { 'expr', 'any' }, { 'pat', 'any' }, { 'start', 'any' }, { 'count', 'any' } },
 | 
			
		||||
    signature = 'matchstr({expr}, {pat} [, {start} [, {count}]])',
 | 
			
		||||
  },
 | 
			
		||||
  matchstrlist = {
 | 
			
		||||
    args = { 2, 3 },
 | 
			
		||||
    base = 1,
 | 
			
		||||
    desc = [=[
 | 
			
		||||
      Returns the |List| of matches in {list} where {pat} matches.
 | 
			
		||||
      {list} is a |List| of strings.  {pat} is matched against each
 | 
			
		||||
      string in {list}.
 | 
			
		||||
 | 
			
		||||
      The {dict} argument supports following items:
 | 
			
		||||
          submatches	include submatch information (|/\(|)
 | 
			
		||||
 | 
			
		||||
      For each match, a |Dict| with the following items is returned:
 | 
			
		||||
          byteidx	starting byte index of the match.
 | 
			
		||||
          idx		index in {list} of the match.
 | 
			
		||||
          text	matched string
 | 
			
		||||
          submatches	a List of submatches.  Present only if
 | 
			
		||||
      		"submatches" is set to v:true in {dict}.
 | 
			
		||||
 | 
			
		||||
      Example: >vim
 | 
			
		||||
          :echo matchstrlist(['tik tok'], '\<\k\+\>')
 | 
			
		||||
          [{'idx': 0, 'byteidx': 0, 'text': 'tik'}, {'idx': 0, 'byteidx': 4, 'text': 'tok'}]
 | 
			
		||||
          :echo matchstrlist(['a', 'b'], '\<\k\+\>')
 | 
			
		||||
          [{'idx': 0, 'byteidx': 0, 'text': 'a'}, {'idx': 1, 'byteidx': 0, 'text': 'b'}]
 | 
			
		||||
      <
 | 
			
		||||
      If "submatches" is present and is v:true, then submatches like
 | 
			
		||||
      "\1", "\2", etc. are also returned.  Example: >vim
 | 
			
		||||
          :echo matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)',
 | 
			
		||||
      				\ #{submatches: v:true})
 | 
			
		||||
          [{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}]
 | 
			
		||||
      <The "submatches" List always contains 9 items.  If a submatch
 | 
			
		||||
      is not found, then an empty string is returned for that
 | 
			
		||||
      submatch.
 | 
			
		||||
    ]=],
 | 
			
		||||
    name = 'matchstrlist',
 | 
			
		||||
    params = { { 'list', 'string[]' }, { 'pat', 'string' }, { 'dict', 'table' } },
 | 
			
		||||
    signature = 'matchstrlist({list}, {pat} [, {dict}])',
 | 
			
		||||
  },
 | 
			
		||||
  matchstrpos = {
 | 
			
		||||
    args = { 2, 4 },
 | 
			
		||||
    base = 1,
 | 
			
		||||
@@ -6836,7 +6927,7 @@ M.funcs = {
 | 
			
		||||
      it returns the maximum of all values in the Dictionary.
 | 
			
		||||
      If {expr} is neither a List nor a Dictionary, or one of the
 | 
			
		||||
      items in {expr} cannot be used as a Number this results in
 | 
			
		||||
                      an error.  An empty |List| or |Dictionary| results in zero.
 | 
			
		||||
      an error.  An empty |List| or |Dictionary| results in zero.
 | 
			
		||||
 | 
			
		||||
    ]=],
 | 
			
		||||
    name = 'max',
 | 
			
		||||
 
 | 
			
		||||
@@ -4731,6 +4731,151 @@ theend:
 | 
			
		||||
  p_cpo = save_cpo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Return all the matches in string "str" for pattern "rmp".
 | 
			
		||||
/// The matches are returned in the List "mlist".
 | 
			
		||||
/// If "submatches" is true, then submatch information is also returned.
 | 
			
		||||
/// "matchbuf" is true when called for matchbufline().
 | 
			
		||||
static void get_matches_in_str(const char *str, regmatch_T *rmp, list_T *mlist, int idx,
 | 
			
		||||
                               bool submatches, bool matchbuf)
 | 
			
		||||
{
 | 
			
		||||
  size_t len = strlen(str);
 | 
			
		||||
  int match = 0;
 | 
			
		||||
  colnr_T startidx = 0;
 | 
			
		||||
 | 
			
		||||
  while (true) {
 | 
			
		||||
    match = vim_regexec_nl(rmp, str, startidx);
 | 
			
		||||
    if (!match) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dict_T *d = tv_dict_alloc();
 | 
			
		||||
    tv_list_append_dict(mlist, d);
 | 
			
		||||
 | 
			
		||||
    if (matchbuf) {
 | 
			
		||||
      tv_dict_add_nr(d, S_LEN("lnum"), idx);
 | 
			
		||||
    } else {
 | 
			
		||||
      tv_dict_add_nr(d, S_LEN("idx"), idx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    tv_dict_add_nr(d, S_LEN("byteidx"),
 | 
			
		||||
                   (colnr_T)(rmp->startp[0] - str));
 | 
			
		||||
 | 
			
		||||
    tv_dict_add_str_len(d, S_LEN("text"), rmp->startp[0],
 | 
			
		||||
                        (int)(rmp->endp[0] - rmp->startp[0]));
 | 
			
		||||
 | 
			
		||||
    if (submatches) {
 | 
			
		||||
      list_T *sml = tv_list_alloc(NSUBEXP - 1);
 | 
			
		||||
 | 
			
		||||
      tv_dict_add_list(d, S_LEN("submatches"), sml);
 | 
			
		||||
 | 
			
		||||
      // return a list with the submatches
 | 
			
		||||
      for (int i = 1; i < NSUBEXP; i++) {
 | 
			
		||||
        if (rmp->endp[i] == NULL) {
 | 
			
		||||
          tv_list_append_string(sml, "", 0);
 | 
			
		||||
        } else {
 | 
			
		||||
          tv_list_append_string(sml, rmp->startp[i], rmp->endp[i] - rmp->startp[i]);
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    startidx = (colnr_T)(rmp->endp[0] - str);
 | 
			
		||||
    if (startidx >= (colnr_T)len || str + startidx <= rmp->startp[0]) {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// "matchbufline()" function
 | 
			
		||||
static void f_matchbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 | 
			
		||||
{
 | 
			
		||||
  rettv->vval.v_number = -1;
 | 
			
		||||
  tv_list_alloc_ret(rettv, kListLenUnknown);
 | 
			
		||||
  list_T *retlist = rettv->vval.v_list;
 | 
			
		||||
 | 
			
		||||
  if (tv_check_for_buffer_arg(argvars, 0) == FAIL
 | 
			
		||||
      || tv_check_for_string_arg(argvars, 1) == FAIL
 | 
			
		||||
      || tv_check_for_lnum_arg(argvars, 2) == FAIL
 | 
			
		||||
      || tv_check_for_lnum_arg(argvars, 3) == FAIL
 | 
			
		||||
      || tv_check_for_opt_dict_arg(argvars, 4) == FAIL) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const int prev_did_emsg = did_emsg;
 | 
			
		||||
  buf_T *buf = tv_get_buf(&argvars[0], false);
 | 
			
		||||
  if (buf == NULL) {
 | 
			
		||||
    if (did_emsg == prev_did_emsg) {
 | 
			
		||||
      semsg(_(e_invalid_buffer_name_str), tv_get_string(&argvars[0]));
 | 
			
		||||
    }
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (buf->b_ml.ml_mfp == NULL) {
 | 
			
		||||
    emsg(_(e_buffer_is_not_loaded));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  char patbuf[NUMBUFLEN];
 | 
			
		||||
  const char *pat = tv_get_string_buf(&argvars[1], patbuf);
 | 
			
		||||
 | 
			
		||||
  const int did_emsg_before = did_emsg;
 | 
			
		||||
  linenr_T slnum = tv_get_lnum_buf(&argvars[2], buf);
 | 
			
		||||
  if (did_emsg > did_emsg_before) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (slnum < 1) {
 | 
			
		||||
    semsg(_(e_invargval), "lnum");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  linenr_T elnum = tv_get_lnum_buf(&argvars[3], buf);
 | 
			
		||||
  if (did_emsg > did_emsg_before) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (elnum < 1 || elnum < slnum) {
 | 
			
		||||
    semsg(_(e_invargval), "end_lnum");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (elnum > buf->b_ml.ml_line_count) {
 | 
			
		||||
    elnum = buf->b_ml.ml_line_count;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool submatches = false;
 | 
			
		||||
  if (argvars[4].v_type != VAR_UNKNOWN) {
 | 
			
		||||
    dict_T *d = argvars[4].vval.v_dict;
 | 
			
		||||
    if (d != NULL) {
 | 
			
		||||
      dictitem_T *di = tv_dict_find(d, S_LEN("submatches"));
 | 
			
		||||
      if (di != NULL) {
 | 
			
		||||
        if (di->di_tv.v_type != VAR_BOOL) {
 | 
			
		||||
          semsg(_(e_invargval), "submatches");
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        submatches = tv_get_bool(&di->di_tv);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Make 'cpoptions' empty, the 'l' flag should not be used here.
 | 
			
		||||
  char *const save_cpo = p_cpo;
 | 
			
		||||
  p_cpo = empty_string_option;
 | 
			
		||||
 | 
			
		||||
  regmatch_T regmatch;
 | 
			
		||||
  regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
 | 
			
		||||
  if (regmatch.regprog == NULL) {
 | 
			
		||||
    goto theend;
 | 
			
		||||
  }
 | 
			
		||||
  regmatch.rm_ic = p_ic;
 | 
			
		||||
 | 
			
		||||
  while (slnum <= elnum) {
 | 
			
		||||
    const char *str = ml_get_buf(buf, slnum);
 | 
			
		||||
    get_matches_in_str(str, ®match, retlist, slnum, submatches, true);
 | 
			
		||||
    slnum++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  vim_regfree(regmatch.regprog);
 | 
			
		||||
 | 
			
		||||
theend:
 | 
			
		||||
  p_cpo = save_cpo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// "match()" function
 | 
			
		||||
static void f_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 | 
			
		||||
{
 | 
			
		||||
@@ -4755,6 +4900,73 @@ static void f_matchstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 | 
			
		||||
  find_some_match(argvars, rettv, kSomeMatchStr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// "matchstrlist()" function
 | 
			
		||||
static void f_matchstrlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 | 
			
		||||
{
 | 
			
		||||
  rettv->vval.v_number = -1;
 | 
			
		||||
  tv_list_alloc_ret(rettv, kListLenUnknown);
 | 
			
		||||
  list_T *retlist = rettv->vval.v_list;
 | 
			
		||||
 | 
			
		||||
  if (tv_check_for_list_arg(argvars, 0) == FAIL
 | 
			
		||||
      || tv_check_for_string_arg(argvars, 1) == FAIL
 | 
			
		||||
      || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  list_T *l = NULL;
 | 
			
		||||
  if ((l = argvars[0].vval.v_list) == NULL) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  char patbuf[NUMBUFLEN];
 | 
			
		||||
  const char *pat = tv_get_string_buf_chk(&argvars[1], patbuf);
 | 
			
		||||
  if (pat == NULL) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Make 'cpoptions' empty, the 'l' flag should not be used here.
 | 
			
		||||
  char *const save_cpo = p_cpo;
 | 
			
		||||
  p_cpo = empty_string_option;
 | 
			
		||||
 | 
			
		||||
  regmatch_T regmatch;
 | 
			
		||||
  regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
 | 
			
		||||
  if (regmatch.regprog == NULL) {
 | 
			
		||||
    goto theend;
 | 
			
		||||
  }
 | 
			
		||||
  regmatch.rm_ic = p_ic;
 | 
			
		||||
 | 
			
		||||
  bool submatches = false;
 | 
			
		||||
  if (argvars[2].v_type != VAR_UNKNOWN) {
 | 
			
		||||
    dict_T *d = argvars[2].vval.v_dict;
 | 
			
		||||
    if (d != NULL) {
 | 
			
		||||
      dictitem_T *di = tv_dict_find(d, S_LEN("submatches"));
 | 
			
		||||
      if (di != NULL) {
 | 
			
		||||
        if (di->di_tv.v_type != VAR_BOOL) {
 | 
			
		||||
          semsg(_(e_invargval), "submatches");
 | 
			
		||||
          goto cleanup;
 | 
			
		||||
        }
 | 
			
		||||
        submatches = tv_get_bool(&di->di_tv);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int idx = 0;
 | 
			
		||||
  TV_LIST_ITER_CONST(l, li, {
 | 
			
		||||
    const typval_T *const li_tv = TV_LIST_ITEM_TV(li);
 | 
			
		||||
    if (li_tv->v_type == VAR_STRING && li_tv->vval.v_string != NULL) {
 | 
			
		||||
      const char *str = li_tv->vval.v_string;
 | 
			
		||||
      get_matches_in_str(str, ®match, retlist, idx, submatches, false);
 | 
			
		||||
    }
 | 
			
		||||
    idx++;
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
cleanup:
 | 
			
		||||
  vim_regfree(regmatch.regprog);
 | 
			
		||||
 | 
			
		||||
theend:
 | 
			
		||||
  p_cpo = save_cpo;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// "matchstrpos()" function
 | 
			
		||||
static void f_matchstrpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
 | 
			
		||||
{
 | 
			
		||||
 
 | 
			
		||||
@@ -4346,6 +4346,22 @@ int tv_check_for_string_or_number_arg(const typval_T *const args, const int idx)
 | 
			
		||||
  return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Give an error and return FAIL unless "args[idx]" is a buffer number.
 | 
			
		||||
/// Buffer number can be a number or a string.
 | 
			
		||||
int tv_check_for_buffer_arg(const typval_T *const args, const int idx)
 | 
			
		||||
  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
 | 
			
		||||
{
 | 
			
		||||
  return tv_check_for_string_or_number_arg(args, idx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Give an error and return FAIL unless "args[idx]" is a line number.
 | 
			
		||||
/// Line number can be a number or a string.
 | 
			
		||||
int tv_check_for_lnum_arg(const typval_T *const args, const int idx)
 | 
			
		||||
  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
 | 
			
		||||
{
 | 
			
		||||
  return tv_check_for_string_or_number_arg(args, idx);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Give an error and return FAIL unless "args[idx]" is a string or a list.
 | 
			
		||||
int tv_check_for_string_or_list_arg(const typval_T *const args, const int idx)
 | 
			
		||||
  FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
 | 
			
		||||
 
 | 
			
		||||
@@ -804,7 +804,9 @@ EXTERN const char e_argreq[] INIT(= N_("E471: Argument required"));
 | 
			
		||||
EXTERN const char e_backslash[] INIT(= N_("E10: \\ should be followed by /, ? or &"));
 | 
			
		||||
EXTERN const char e_cmdwin[] INIT(= N_("E11: Invalid in command-line window; <CR> executes, CTRL-C quits"));
 | 
			
		||||
EXTERN const char e_curdir[] INIT(= N_("E12: Command not allowed in secure mode in current dir or tag search"));
 | 
			
		||||
EXTERN const char e_invalid_buffer_name_str[] INIT(= N_("E158: Invalid buffer name: %s"));
 | 
			
		||||
EXTERN const char e_command_too_recursive[] INIT(= N_("E169: Command too recursive"));
 | 
			
		||||
EXTERN const char e_buffer_is_not_loaded[] INIT(= N_("E681: Buffer is not loaded"));
 | 
			
		||||
EXTERN const char e_endif[] INIT(= N_("E171: Missing :endif"));
 | 
			
		||||
EXTERN const char e_endtry[] INIT(= N_("E600: Missing :endtry"));
 | 
			
		||||
EXTERN const char e_endwhile[] INIT(= N_("E170: Missing :endwhile"));
 | 
			
		||||
 
 | 
			
		||||
@@ -6916,7 +6916,7 @@ static int cbuffer_process_args(exarg_T *eap, buf_T **bufp, linenr_T *line1, lin
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (buf->b_ml.ml_mfp == NULL) {
 | 
			
		||||
    emsg(_("E681: Buffer is not loaded"));
 | 
			
		||||
    emsg(_(e_buffer_is_not_loaded));
 | 
			
		||||
    return FAIL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -807,7 +807,7 @@ static int parse_sign_cmd_args(int cmd, char *arg, char **name, int *id, char **
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (filename != NULL && *buf == NULL) {
 | 
			
		||||
    semsg(_("E158: Invalid buffer name: %s"), filename);
 | 
			
		||||
    semsg(_(e_invalid_buffer_name_str), filename);
 | 
			
		||||
    return FAIL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1044,6 +1044,192 @@ func Test_matchstrpos()
 | 
			
		||||
  call assert_equal(['', -1, -1], matchstrpos(v:_null_list, '\a'))
 | 
			
		||||
endfunc
 | 
			
		||||
 | 
			
		||||
" Test for matchstrlist()
 | 
			
		||||
func Test_matchstrlist()
 | 
			
		||||
  let lines =<< trim END
 | 
			
		||||
    #" Basic match
 | 
			
		||||
    call assert_equal([{'idx': 0, 'byteidx': 1, 'text': 'bout'},
 | 
			
		||||
          \ {'idx': 1, 'byteidx': 1, 'text': 'bove'}],
 | 
			
		||||
          \ matchstrlist(['about', 'above'], 'bo.*'))
 | 
			
		||||
    #" no match
 | 
			
		||||
    call assert_equal([], matchstrlist(['about', 'above'], 'xy.*'))
 | 
			
		||||
    #" empty string
 | 
			
		||||
    call assert_equal([], matchstrlist([''], '.'))
 | 
			
		||||
    #" empty pattern
 | 
			
		||||
    call assert_equal([{'idx': 0, 'byteidx': 0, 'text': ''}], matchstrlist(['abc'], ''))
 | 
			
		||||
    #" method call
 | 
			
		||||
    call assert_equal([{'idx': 0, 'byteidx': 2, 'text': 'it'}], ['editor']->matchstrlist('ed\zsit\zeor'))
 | 
			
		||||
    #" single character matches
 | 
			
		||||
    call assert_equal([{'idx': 0, 'byteidx': 5, 'text': 'r'}],
 | 
			
		||||
          \ ['editor']->matchstrlist('r'))
 | 
			
		||||
    call assert_equal([{'idx': 0, 'byteidx': 0, 'text': 'a'}], ['a']->matchstrlist('a'))
 | 
			
		||||
    call assert_equal([{'idx': 0, 'byteidx': 0, 'text': ''}],
 | 
			
		||||
          \ matchstrlist(['foobar'], '\zs'))
 | 
			
		||||
    #" string with tabs
 | 
			
		||||
    call assert_equal([{'idx': 0, 'byteidx': 1, 'text': 'foo'}],
 | 
			
		||||
          \ matchstrlist(["\tfoobar"], 'foo'))
 | 
			
		||||
    #" string with multibyte characters
 | 
			
		||||
    call assert_equal([{'idx': 0, 'byteidx': 2, 'text': '😊😊'}],
 | 
			
		||||
          \ matchstrlist(["\t\t😊😊"], '\k\+'))
 | 
			
		||||
 | 
			
		||||
    #" null string
 | 
			
		||||
    call assert_equal([], matchstrlist(v:_null_list, 'abc'))
 | 
			
		||||
    call assert_equal([], matchstrlist([v:_null_string], 'abc'))
 | 
			
		||||
    call assert_equal([{'idx': 0, 'byteidx': 0, 'text': ''}],
 | 
			
		||||
          \ matchstrlist(['abc'], v:_null_string))
 | 
			
		||||
 | 
			
		||||
    #" sub matches
 | 
			
		||||
    call assert_equal([{'idx': 0, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}], matchstrlist(['acd'], '\(a\)\?\(b\)\?\(c\)\?\(.*\)', {'submatches': v:true}))
 | 
			
		||||
 | 
			
		||||
    #" null dict argument
 | 
			
		||||
    call assert_equal([{'idx': 0, 'byteidx': 0, 'text': 'vim'}],
 | 
			
		||||
          \ matchstrlist(['vim'], '\w\+', v:_null_dict))
 | 
			
		||||
 | 
			
		||||
    #" Error cases
 | 
			
		||||
    call assert_fails("echo matchstrlist('abc', 'a')", 'E1211: List required for argument 1')
 | 
			
		||||
    call assert_fails("echo matchstrlist(['abc'], {})", 'E1174: String required for argument 2')
 | 
			
		||||
    call assert_fails("echo matchstrlist(['abc'], '.', [])", 'E1206: Dictionary required for argument 3')
 | 
			
		||||
    call assert_fails("echo matchstrlist(['abc'], 'a', {'submatches': []})", 'E475: Invalid value for argument submatches')
 | 
			
		||||
    call assert_fails("echo matchstrlist(['abc'], '\\@=')", 'E866: (NFA regexp) Misplaced @')
 | 
			
		||||
  END
 | 
			
		||||
  call CheckLegacyAndVim9Success(lines)
 | 
			
		||||
 | 
			
		||||
  let lines =<< trim END
 | 
			
		||||
    vim9script
 | 
			
		||||
    # non string items
 | 
			
		||||
    matchstrlist([0z10, {'a': 'x'}], 'x')
 | 
			
		||||
  END
 | 
			
		||||
  call CheckSourceSuccess(lines)
 | 
			
		||||
 | 
			
		||||
  let lines =<< trim END
 | 
			
		||||
    vim9script
 | 
			
		||||
    def Foo()
 | 
			
		||||
      # non string items
 | 
			
		||||
      assert_equal([], matchstrlist([0z10, {'a': 'x'}], 'x'))
 | 
			
		||||
    enddef
 | 
			
		||||
    Foo()
 | 
			
		||||
  END
 | 
			
		||||
  call CheckSourceFailure(lines, 'E1013: Argument 1: type mismatch, expected list<string> but got list<any>', 2)
 | 
			
		||||
endfunc
 | 
			
		||||
 | 
			
		||||
" Test for matchbufline()
 | 
			
		||||
func Test_matchbufline()
 | 
			
		||||
  let lines =<< trim END
 | 
			
		||||
    #" Basic match
 | 
			
		||||
    new
 | 
			
		||||
    call setline(1, ['about', 'above', 'below'])
 | 
			
		||||
    VAR bnr = bufnr()
 | 
			
		||||
    wincmd w
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 1, 'text': 'bout'},
 | 
			
		||||
          \ {'lnum': 2, 'byteidx': 1, 'text': 'bove'}],
 | 
			
		||||
          \ matchbufline(bnr, 'bo.*', 1, '$'))
 | 
			
		||||
    #" multiple matches in a line
 | 
			
		||||
    call setbufline(bnr, 1, ['about about', 'above above', 'below'])
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 1, 'text': 'bout'},
 | 
			
		||||
          \ {'lnum': 1, 'byteidx': 7, 'text': 'bout'},
 | 
			
		||||
          \ {'lnum': 2, 'byteidx': 1, 'text': 'bove'},
 | 
			
		||||
          \ {'lnum': 2, 'byteidx': 7, 'text': 'bove'}],
 | 
			
		||||
          \ matchbufline(bnr, 'bo\k\+', 1, '$'))
 | 
			
		||||
    #" no match
 | 
			
		||||
    call assert_equal([], matchbufline(bnr, 'xy.*', 1, '$'))
 | 
			
		||||
    #" match on a particular line
 | 
			
		||||
    call assert_equal([{'lnum': 2, 'byteidx': 7, 'text': 'bove'}],
 | 
			
		||||
          \ matchbufline(bnr, 'bo\k\+$', 2, 2))
 | 
			
		||||
    #" match on a particular line
 | 
			
		||||
    call assert_equal([], matchbufline(bnr, 'bo.*', 3, 3))
 | 
			
		||||
    #" empty string
 | 
			
		||||
    call deletebufline(bnr, 1, '$')
 | 
			
		||||
    call assert_equal([], matchbufline(bnr, '.', 1, '$'))
 | 
			
		||||
    #" empty pattern
 | 
			
		||||
    call setbufline(bnr, 1, 'abc')
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 0, 'text': ''}],
 | 
			
		||||
          \ matchbufline(bnr, '', 1, '$'))
 | 
			
		||||
    #" method call
 | 
			
		||||
    call setbufline(bnr, 1, 'editor')
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 2, 'text': 'it'}],
 | 
			
		||||
          \ bnr->matchbufline('ed\zsit\zeor', 1, 1))
 | 
			
		||||
    #" single character matches
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 5, 'text': 'r'}],
 | 
			
		||||
          \ matchbufline(bnr, 'r', 1, '$'))
 | 
			
		||||
    call setbufline(bnr, 1, 'a')
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 0, 'text': 'a'}],
 | 
			
		||||
          \ matchbufline(bnr, 'a', 1, '$'))
 | 
			
		||||
    #" zero-width match
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 0, 'text': ''}],
 | 
			
		||||
          \ matchbufline(bnr, '\zs', 1, '$'))
 | 
			
		||||
    #" string with tabs
 | 
			
		||||
    call setbufline(bnr, 1, "\tfoobar")
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 1, 'text': 'foo'}],
 | 
			
		||||
          \ matchbufline(bnr, 'foo', 1, '$'))
 | 
			
		||||
    #" string with multibyte characters
 | 
			
		||||
    call setbufline(bnr, 1, "\t\t😊😊")
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 2, 'text': '😊😊'}],
 | 
			
		||||
          \ matchbufline(bnr, '\k\+', 1, '$'))
 | 
			
		||||
    #" empty buffer
 | 
			
		||||
    call deletebufline(bnr, 1, '$')
 | 
			
		||||
    call assert_equal([], matchbufline(bnr, 'abc', 1, '$'))
 | 
			
		||||
 | 
			
		||||
    #" Non existing buffer
 | 
			
		||||
    call setbufline(bnr, 1, 'abc')
 | 
			
		||||
    call assert_fails("echo matchbufline(5000, 'abc', 1, 1)", 'E158: Invalid buffer name: 5000')
 | 
			
		||||
    #" null string
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 0, 'text': ''}],
 | 
			
		||||
          \ matchbufline(bnr, v:_null_string, 1, 1))
 | 
			
		||||
    #" invalid starting line number
 | 
			
		||||
    call assert_equal([], matchbufline(bnr, 'abc', 100, 100))
 | 
			
		||||
    #" ending line number greater than the last line
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 0, 'text': 'abc'}],
 | 
			
		||||
          \ matchbufline(bnr, 'abc', 1, 100))
 | 
			
		||||
    #" ending line number greater than the starting line number
 | 
			
		||||
    call setbufline(bnr, 1, ['one', 'two'])
 | 
			
		||||
    call assert_fails($"echo matchbufline({bnr}, 'abc', 2, 1)", 'E475: Invalid value for argument end_lnum')
 | 
			
		||||
 | 
			
		||||
    #" sub matches
 | 
			
		||||
    call deletebufline(bnr, 1, '$')
 | 
			
		||||
    call setbufline(bnr, 1, 'acd')
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 0, 'text': 'acd', 'submatches': ['a', '', 'c', 'd', '', '', '', '', '']}],
 | 
			
		||||
          \ matchbufline(bnr, '\(a\)\?\(b\)\?\(c\)\?\(.*\)', 1, '$', {'submatches': v:true}))
 | 
			
		||||
 | 
			
		||||
    #" null dict argument
 | 
			
		||||
    call assert_equal([{'lnum': 1, 'byteidx': 0, 'text': 'acd'}],
 | 
			
		||||
          \ matchbufline(bnr, '\w\+', '$', '$', v:_null_dict))
 | 
			
		||||
 | 
			
		||||
    #" Error cases
 | 
			
		||||
    call assert_fails("echo matchbufline([1], 'abc', 1, 1)", 'E1220: String or Number required for argument 1')
 | 
			
		||||
    call assert_fails("echo matchbufline(1, {}, 1, 1)", 'E1174: String required for argument 2')
 | 
			
		||||
    call assert_fails("echo matchbufline(1, 'abc', {}, 1)", 'E1220: String or Number required for argument 3')
 | 
			
		||||
    call assert_fails("echo matchbufline(1, 'abc', 1, {})", 'E1220: String or Number required for argument 4')
 | 
			
		||||
    call assert_fails($"echo matchbufline({bnr}, 'abc', -1, '$')", 'E475: Invalid value for argument lnum')
 | 
			
		||||
    call assert_fails($"echo matchbufline({bnr}, 'abc', 1, -1)", 'E475: Invalid value for argument end_lnum')
 | 
			
		||||
    call assert_fails($"echo matchbufline({bnr}, '\\@=', 1, 1)", 'E866: (NFA regexp) Misplaced @')
 | 
			
		||||
    call assert_fails($"echo matchbufline({bnr}, 'abc', 1, 1, {{'submatches': []}})", 'E475: Invalid value for argument submatches')
 | 
			
		||||
    :%bdelete!
 | 
			
		||||
    call assert_fails($"echo matchbufline({bnr}, 'abc', 1, '$'))", 'E681: Buffer is not loaded')
 | 
			
		||||
  END
 | 
			
		||||
  call CheckLegacyAndVim9Success(lines)
 | 
			
		||||
 | 
			
		||||
  call assert_fails($"echo matchbufline('', 'abc', 'abc', 1)", 'E475: Invalid value for argument lnum')
 | 
			
		||||
  call assert_fails($"echo matchbufline('', 'abc', 1, 'abc')", 'E475: Invalid value for argument end_lnum')
 | 
			
		||||
 | 
			
		||||
  let lines =<< trim END
 | 
			
		||||
    vim9script
 | 
			
		||||
    def Foo()
 | 
			
		||||
      echo matchbufline('', 'abc', 'abc', 1)
 | 
			
		||||
    enddef
 | 
			
		||||
    Foo()
 | 
			
		||||
  END
 | 
			
		||||
  call CheckSourceFailure(lines, 'E1030: Using a String as a Number: "abc"', 1)
 | 
			
		||||
 | 
			
		||||
  let lines =<< trim END
 | 
			
		||||
    vim9script
 | 
			
		||||
    def Foo()
 | 
			
		||||
      echo matchbufline('', 'abc', 1, 'abc')
 | 
			
		||||
    enddef
 | 
			
		||||
    Foo()
 | 
			
		||||
  END
 | 
			
		||||
  call CheckSourceFailure(lines, 'E1030: Using a String as a Number: "abc"', 1)
 | 
			
		||||
endfunc
 | 
			
		||||
 | 
			
		||||
func Test_nextnonblank_prevnonblank()
 | 
			
		||||
  new
 | 
			
		||||
insert
 | 
			
		||||
@@ -2104,7 +2290,7 @@ func Test_trim()
 | 
			
		||||
  call assert_equal(" \tabcd\t     xxxx   tail", trim(" \tabcd\t     xxxx   tail", "abx"))
 | 
			
		||||
  call assert_equal("RESERVE", trim("你RESERVE好", "你好"))
 | 
			
		||||
  call assert_equal("您R E SER V E早", trim("你好您R E SER V E早好你你", "你好"))
 | 
			
		||||
  call assert_equal("你好您R E SER V E早好你你", trim(" \n\r\r   你好您R E SER V E早好你你    \t  \x0B"))
 | 
			
		||||
  call assert_equal("你好您R E SER V E早好你你", trim(" \n\r\r   你好您R E SER V E早好你你    \t  \x0B", ))
 | 
			
		||||
  call assert_equal("您R E SER V E早好你你    \t  \x0B", trim("    你好您R E SER V E早好你你    \t  \x0B", " 你好"))
 | 
			
		||||
  call assert_equal("您R E SER V E早好你你    \t  \x0B", trim("    tteesstttt你好您R E SER V E早好你你    \t  \x0B ttestt", " 你好tes"))
 | 
			
		||||
  call assert_equal("您R E SER V E早好你你    \t  \x0B", trim("    tteesstttt你好您R E SER V E早好你你    \t  \x0B ttestt", "   你你你好好好tttsses"))
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,49 @@ func CheckScriptSuccess(lines)
 | 
			
		||||
  endtry
 | 
			
		||||
endfunc
 | 
			
		||||
 | 
			
		||||
" :source a list of "lines" and check whether it fails with "error"
 | 
			
		||||
func CheckSourceFailure(lines, error, lnum = -3)
 | 
			
		||||
  if get(a:lines, 0, '') ==# 'vim9script'
 | 
			
		||||
    return
 | 
			
		||||
  endif
 | 
			
		||||
  new
 | 
			
		||||
  call setline(1, a:lines)
 | 
			
		||||
  try
 | 
			
		||||
    call assert_fails('source', a:error, a:lines, a:lnum)
 | 
			
		||||
  finally
 | 
			
		||||
    bw!
 | 
			
		||||
  endtry
 | 
			
		||||
endfunc
 | 
			
		||||
 | 
			
		||||
" :source a list of "lines" and check whether it fails with the list of
 | 
			
		||||
" "errors"
 | 
			
		||||
func CheckSourceFailureList(lines, errors, lnum = -3)
 | 
			
		||||
  if get(a:lines, 0, '') ==# 'vim9script'
 | 
			
		||||
    return
 | 
			
		||||
  endif
 | 
			
		||||
  new
 | 
			
		||||
  call setline(1, a:lines)
 | 
			
		||||
  try
 | 
			
		||||
    call assert_fails('source', a:errors, a:lines, a:lnum)
 | 
			
		||||
  finally
 | 
			
		||||
    bw!
 | 
			
		||||
  endtry
 | 
			
		||||
endfunc
 | 
			
		||||
 | 
			
		||||
" :source a list of "lines" and check whether it succeeds
 | 
			
		||||
func CheckSourceSuccess(lines)
 | 
			
		||||
  if get(a:lines, 0, '') ==# 'vim9script'
 | 
			
		||||
    return
 | 
			
		||||
  endif
 | 
			
		||||
  new
 | 
			
		||||
  call setline(1, a:lines)
 | 
			
		||||
  try
 | 
			
		||||
    :source
 | 
			
		||||
  finally
 | 
			
		||||
    bw!
 | 
			
		||||
  endtry
 | 
			
		||||
endfunc
 | 
			
		||||
 | 
			
		||||
func CheckDefAndScriptSuccess(lines)
 | 
			
		||||
  return
 | 
			
		||||
endfunc
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user