mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	fix(lsp): small bugs in snippet-parser #18998
This fixes the following bugs:
`${1:else_text}` -> format with if_text: "else_text"
`${1:-else_text}` -> format with if_text: "else_text"
`${1:}` in `format` (eg. empty else_text) -> error.
`${1:}` (eg. empty placeholder) -> error.
Thanks hrsh7th :)
			
			
This commit is contained in:
		| @@ -156,10 +156,10 @@ P.seq = function(...) | |||||||
|   return function(input, pos) |   return function(input, pos) | ||||||
|     local values = {} |     local values = {} | ||||||
|     local new_pos = pos |     local new_pos = pos | ||||||
|     for _, parser in ipairs(parsers) do |     for i, parser in ipairs(parsers) do | ||||||
|       local result = parser(input, new_pos) |       local result = parser(input, new_pos) | ||||||
|       if result.parsed then |       if result.parsed then | ||||||
|         table.insert(values, result.value) |         values[i] = result.value | ||||||
|         new_pos = result.pos |         new_pos = result.pos | ||||||
|       else |       else | ||||||
|         return P.unmatch(pos) |         return P.unmatch(pos) | ||||||
| @@ -272,22 +272,48 @@ S.format = P.any( | |||||||
|       S.open, |       S.open, | ||||||
|       S.int, |       S.int, | ||||||
|       S.colon, |       S.colon, | ||||||
|       P.any( |       P.seq(S.question, P.opt(P.take_until({ ':' }, { '\\' })), S.colon, P.opt(P.take_until({ '}' }, { '\\' }))), | ||||||
|         P.seq(S.question, P.take_until({ ':' }, { '\\' }), S.colon, P.take_until({ '}' }, { '\\' })), |  | ||||||
|         P.seq(S.plus, P.take_until({ '}' }, { '\\' })), |  | ||||||
|         P.seq(S.minus, P.take_until({ '}' }, { '\\' })) |  | ||||||
|       ), |  | ||||||
|       S.close |       S.close | ||||||
|     ), |     ), | ||||||
|     function(values) |     function(values) | ||||||
|       return setmetatable({ |       return setmetatable({ | ||||||
|         type = Node.Type.FORMAT, |         type = Node.Type.FORMAT, | ||||||
|         capture_index = values[3], |         capture_index = values[3], | ||||||
|         if_text = values[5][2].esc, |         if_text = values[5][2] and values[5][2].esc or '', | ||||||
|         else_text = (values[5][4] or {}).esc, |         else_text = values[5][4] and values[5][4].esc or '', | ||||||
|       }, Node) |       }, Node) | ||||||
|     end |     end | ||||||
|   ) |   ), | ||||||
|  |   P.map( | ||||||
|  |     P.seq(S.dollar, S.open, S.int, S.colon, P.seq(S.plus, P.opt(P.take_until({ '}' }, { '\\' }))), S.close), | ||||||
|  |     function(values) | ||||||
|  |       return setmetatable({ | ||||||
|  |         type = Node.Type.FORMAT, | ||||||
|  |         capture_index = values[3], | ||||||
|  |         if_text = values[5][2] and values[5][2].esc or '', | ||||||
|  |         else_text = '', | ||||||
|  |       }, Node) | ||||||
|  |     end | ||||||
|  |   ), | ||||||
|  |   P.map( | ||||||
|  |     P.seq(S.dollar, S.open, S.int, S.colon, S.minus, P.opt(P.take_until({ '}' }, { '\\' })), S.close), | ||||||
|  |     function(values) | ||||||
|  |       return setmetatable({ | ||||||
|  |         type = Node.Type.FORMAT, | ||||||
|  |         capture_index = values[3], | ||||||
|  |         if_text = '', | ||||||
|  |         else_text = values[6] and values[6].esc or '', | ||||||
|  |       }, Node) | ||||||
|  |     end | ||||||
|  |   ), | ||||||
|  |   P.map(P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.take_until({ '}' }, { '\\' })), S.close), function(values) | ||||||
|  |     return setmetatable({ | ||||||
|  |       type = Node.Type.FORMAT, | ||||||
|  |       capture_index = values[3], | ||||||
|  |       if_text = '', | ||||||
|  |       else_text = values[5] and values[5].esc or '', | ||||||
|  |     }, Node) | ||||||
|  |   end) | ||||||
| ) | ) | ||||||
|  |  | ||||||
| S.transform = P.map( | S.transform = P.map( | ||||||
| @@ -333,12 +359,19 @@ S.tabstop = P.any( | |||||||
|  |  | ||||||
| S.placeholder = P.any( | S.placeholder = P.any( | ||||||
|   P.map( |   P.map( | ||||||
|     P.seq(S.dollar, S.open, S.int, S.colon, P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), S.close), |     P.seq(S.dollar, S.open, S.int, S.colon, P.opt(P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' })))), S.close), | ||||||
|     function(values) |     function(values) | ||||||
|       return setmetatable({ |       return setmetatable({ | ||||||
|         type = Node.Type.PLACEHOLDER, |         type = Node.Type.PLACEHOLDER, | ||||||
|         tabstop = values[3], |         tabstop = values[3], | ||||||
|         children = values[5], |         -- insert empty text if opt did not match. | ||||||
|  |         children = values[5] or { | ||||||
|  |           setmetatable({ | ||||||
|  |             type = Node.Type.TEXT, | ||||||
|  |             raw = '', | ||||||
|  |             esc = '', | ||||||
|  |           }, Node), | ||||||
|  |         }, | ||||||
|       }, Node) |       }, Node) | ||||||
|     end |     end | ||||||
|   ) |   ) | ||||||
|   | |||||||
| @@ -19,9 +19,9 @@ describe('vim.lsp._snippet', function() | |||||||
|         { |         { | ||||||
|           type = snippet.NodeType.TEXT, |           type = snippet.NodeType.TEXT, | ||||||
|           raw = 'TE\\$\\}XT', |           raw = 'TE\\$\\}XT', | ||||||
|           esc = 'TE$}XT' |           esc = 'TE$}XT', | ||||||
|         } |         }, | ||||||
|       } |       }, | ||||||
|     }, parse('TE\\$\\}XT')) |     }, parse('TE\\$\\}XT')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -36,8 +36,8 @@ describe('vim.lsp._snippet', function() | |||||||
|         { |         { | ||||||
|           type = snippet.NodeType.TABSTOP, |           type = snippet.NodeType.TABSTOP, | ||||||
|           tabstop = 2, |           tabstop = 2, | ||||||
|         } |         }, | ||||||
|       } |       }, | ||||||
|     }, parse('$1${2}')) |     }, parse('$1${2}')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -56,7 +56,7 @@ describe('vim.lsp._snippet', function() | |||||||
|                 { |                 { | ||||||
|                   type = snippet.NodeType.TEXT, |                   type = snippet.NodeType.TEXT, | ||||||
|                   raw = 'TE\\$\\}XT', |                   raw = 'TE\\$\\}XT', | ||||||
|                   esc = 'TE$}XT' |                   esc = 'TE$}XT', | ||||||
|                 }, |                 }, | ||||||
|                 { |                 { | ||||||
|                   type = snippet.NodeType.TABSTOP, |                   type = snippet.NodeType.TABSTOP, | ||||||
| @@ -73,21 +73,21 @@ describe('vim.lsp._snippet', function() | |||||||
|                       { |                       { | ||||||
|                         type = snippet.NodeType.FORMAT, |                         type = snippet.NodeType.FORMAT, | ||||||
|                         capture_index = 1, |                         capture_index = 1, | ||||||
|                         modifier = 'upcase' |                         modifier = 'upcase', | ||||||
|                       } |                       }, | ||||||
|                     } |                     }, | ||||||
|                   }, |                   }, | ||||||
|                 }, |                 }, | ||||||
|                 { |                 { | ||||||
|                   type = snippet.NodeType.TEXT, |                   type = snippet.NodeType.TEXT, | ||||||
|                   raw = 'TE\\$\\}XT', |                   raw = 'TE\\$\\}XT', | ||||||
|                   esc = 'TE$}XT' |                   esc = 'TE$}XT', | ||||||
|  |                 }, | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|         }, |         }, | ||||||
|               } |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|       }, |       }, | ||||||
|       } |  | ||||||
|     }, parse('${1:${2:TE\\$\\}XT$3${1/regex/${1:/upcase}/i}TE\\$\\}XT}}')) |     }, parse('${1:${2:TE\\$\\}XT$3${1/regex/${1:/upcase}/i}TE\\$\\}XT}}')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -110,8 +110,8 @@ describe('vim.lsp._snippet', function() | |||||||
|             { |             { | ||||||
|               type = snippet.NodeType.TABSTOP, |               type = snippet.NodeType.TABSTOP, | ||||||
|               tabstop = 1, |               tabstop = 1, | ||||||
|             } |             }, | ||||||
|           } |           }, | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|           type = snippet.NodeType.VARIABLE, |           type = snippet.NodeType.VARIABLE, | ||||||
| @@ -124,11 +124,11 @@ describe('vim.lsp._snippet', function() | |||||||
|                 type = snippet.NodeType.FORMAT, |                 type = snippet.NodeType.FORMAT, | ||||||
|                 capture_index = 1, |                 capture_index = 1, | ||||||
|                 modifier = 'upcase', |                 modifier = 'upcase', | ||||||
|               } |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|               }, |               }, | ||||||
|       } |             }, | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|     }, parse('$VAR${VAR}${VAR:$1}${VAR/regex/${1:/upcase}/}')) |     }, parse('$VAR${VAR}${VAR:$1}${VAR/regex/${1:/upcase}/}')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
| @@ -141,12 +141,96 @@ describe('vim.lsp._snippet', function() | |||||||
|           tabstop = 1, |           tabstop = 1, | ||||||
|           items = { |           items = { | ||||||
|             ',', |             ',', | ||||||
|             '|' |             '|', | ||||||
|           } |           }, | ||||||
|         } |         }, | ||||||
|       } |       }, | ||||||
|     }, parse('${1|\\,,\\||}')) |     }, parse('${1|\\,,\\||}')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|  |   it('should parse format', function() | ||||||
|  |     eq({ | ||||||
|  |       type = snippet.NodeType.SNIPPET, | ||||||
|  |       children = { | ||||||
|  |         { | ||||||
|  |           type = snippet.NodeType.VARIABLE, | ||||||
|  |           name = 'VAR', | ||||||
|  |           transform = { | ||||||
|  |             type = snippet.NodeType.TRANSFORM, | ||||||
|  |             pattern = 'regex', | ||||||
|  |             format = { | ||||||
|  |               { | ||||||
|  |                 type = snippet.NodeType.FORMAT, | ||||||
|  |                 capture_index = 1, | ||||||
|  |                 modifier = 'upcase', | ||||||
|  |               }, | ||||||
|  |               { | ||||||
|  |                 type = snippet.NodeType.FORMAT, | ||||||
|  |                 capture_index = 1, | ||||||
|  |                 if_text = 'if_text', | ||||||
|  |                 else_text = '', | ||||||
|  |               }, | ||||||
|  |               { | ||||||
|  |                 type = snippet.NodeType.FORMAT, | ||||||
|  |                 capture_index = 1, | ||||||
|  |                 if_text = '', | ||||||
|  |                 else_text = 'else_text', | ||||||
|  |               }, | ||||||
|  |               { | ||||||
|  |                 type = snippet.NodeType.FORMAT, | ||||||
|  |                 capture_index = 1, | ||||||
|  |                 else_text = 'else_text', | ||||||
|  |                 if_text = 'if_text', | ||||||
|  |               }, | ||||||
|  |               { | ||||||
|  |                 type = snippet.NodeType.FORMAT, | ||||||
|  |                 capture_index = 1, | ||||||
|  |                 if_text = '', | ||||||
|  |                 else_text = 'else_text', | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |     }, parse('${VAR/regex/${1:/upcase}${1:+if_text}${1:-else_text}${1:?if_text:else_text}${1:else_text}/}')) | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|  |   it('should parse empty strings', function() | ||||||
|  |     eq({ | ||||||
|  |       children = { | ||||||
|  |         { | ||||||
|  |           children = { { | ||||||
|  |             esc = '', | ||||||
|  |             raw = '', | ||||||
|  |             type = 7, | ||||||
|  |           } }, | ||||||
|  |           tabstop = 1, | ||||||
|  |           type = 2, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           esc = ' ', | ||||||
|  |           raw = ' ', | ||||||
|  |           type = 7, | ||||||
|  |         }, | ||||||
|  |         { | ||||||
|  |           name = 'VAR', | ||||||
|  |           transform = { | ||||||
|  |             format = { | ||||||
|  |               { | ||||||
|  |                 capture_index = 1, | ||||||
|  |                 else_text = '', | ||||||
|  |                 if_text = '', | ||||||
|  |                 type = 6, | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |             option = 'g', | ||||||
|  |             pattern = 'erg', | ||||||
|  |             type = 5, | ||||||
|  |           }, | ||||||
|  |           type = 3, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       type = 0, | ||||||
|  |     }, parse('${1:} ${VAR/erg/${1:?:}/g}')) | ||||||
|  |   end) | ||||||
|  | end) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 L3MON4D3
					L3MON4D3