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:
L3MON4D3
2022-06-29 18:53:49 +02:00
committed by GitHub
parent 1eb9624666
commit 6f6286e4f9
2 changed files with 154 additions and 37 deletions

View File

@@ -156,10 +156,10 @@ P.seq = function(...)
return function(input, pos)
local values = {}
local new_pos = pos
for _, parser in ipairs(parsers) do
for i, parser in ipairs(parsers) do
local result = parser(input, new_pos)
if result.parsed then
table.insert(values, result.value)
values[i] = result.value
new_pos = result.pos
else
return P.unmatch(pos)
@@ -272,22 +272,48 @@ S.format = P.any(
S.open,
S.int,
S.colon,
P.any(
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({ '}' }, { '\\' }))
),
P.seq(S.question, P.opt(P.take_until({ ':' }, { '\\' })), S.colon, P.opt(P.take_until({ '}' }, { '\\' }))),
S.close
),
function(values)
return setmetatable({
type = Node.Type.FORMAT,
capture_index = values[3],
if_text = values[5][2].esc,
else_text = (values[5][4] or {}).esc,
if_text = values[5][2] and values[5][2].esc or '',
else_text = values[5][4] and values[5][4].esc or '',
}, Node)
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(
@@ -333,12 +359,19 @@ S.tabstop = P.any(
S.placeholder = P.any(
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)
return setmetatable({
type = Node.Type.PLACEHOLDER,
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)
end
)

View File

@@ -19,9 +19,9 @@ describe('vim.lsp._snippet', function()
{
type = snippet.NodeType.TEXT,
raw = 'TE\\$\\}XT',
esc = 'TE$}XT'
}
}
esc = 'TE$}XT',
},
},
}, parse('TE\\$\\}XT'))
end)
@@ -36,8 +36,8 @@ describe('vim.lsp._snippet', function()
{
type = snippet.NodeType.TABSTOP,
tabstop = 2,
}
}
},
},
}, parse('$1${2}'))
end)
@@ -56,7 +56,7 @@ describe('vim.lsp._snippet', function()
{
type = snippet.NodeType.TEXT,
raw = 'TE\\$\\}XT',
esc = 'TE$}XT'
esc = 'TE$}XT',
},
{
type = snippet.NodeType.TABSTOP,
@@ -73,21 +73,21 @@ describe('vim.lsp._snippet', function()
{
type = snippet.NodeType.FORMAT,
capture_index = 1,
modifier = 'upcase'
}
}
modifier = 'upcase',
},
},
},
},
{
type = snippet.NodeType.TEXT,
raw = 'TE\\$\\}XT',
esc = 'TE$}XT'
esc = 'TE$}XT',
},
},
},
},
},
}
}
}
},
}
}, parse('${1:${2:TE\\$\\}XT$3${1/regex/${1:/upcase}/i}TE\\$\\}XT}}'))
end)
@@ -110,8 +110,8 @@ describe('vim.lsp._snippet', function()
{
type = snippet.NodeType.TABSTOP,
tabstop = 1,
}
}
},
},
},
{
type = snippet.NodeType.VARIABLE,
@@ -124,11 +124,11 @@ describe('vim.lsp._snippet', function()
type = snippet.NodeType.FORMAT,
capture_index = 1,
modifier = 'upcase',
}
}
}
},
}
},
},
},
},
}, parse('$VAR${VAR}${VAR:$1}${VAR/regex/${1:/upcase}/}'))
end)
@@ -141,12 +141,96 @@ describe('vim.lsp._snippet', function()
tabstop = 1,
items = {
',',
'|'
}
}
}
'|',
},
},
},
}, parse('${1|\\,,\\||}'))
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)
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)