From 706bcab75eaad2c370d61bf828531054439d3a3e Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Tue, 7 Mar 2023 15:17:52 +0900 Subject: docs(lsp): change type annotations from number → integer (#22510) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- runtime/lua/vim/lsp/_snippet.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'runtime/lua/vim/lsp/_snippet.lua') diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua index 3488639fb4..797d8960d5 100644 --- a/runtime/lua/vim/lsp/_snippet.lua +++ b/runtime/lua/vim/lsp/_snippet.lua @@ -483,7 +483,7 @@ end) local M = {} ---The snippet node type enum ----@types table +---@types table M.NodeType = Node.Type ---Parse snippet string and returns the AST -- cgit From 4d04feb6629cb049cb2a13ba35f0c8d3c6b67ff4 Mon Sep 17 00:00:00 2001 From: Christian Clason Date: Fri, 14 Apr 2023 10:39:57 +0200 Subject: feat(lua): vim.tbl_contains supports general tables and predicates (#23040) * feat(lua): vim.tbl_contains supports general tables and predicates Problem: `vim.tbl_contains` only works for list-like tables (integer keys without gaps) and primitive values (in particular, not for nested tables). Solution: Rename `vim.tbl_contains` to `vim.list_contains` and add new `vim.tbl_contains` that works for general tables and optionally allows `value` to be a predicate function that is checked for every key. --- runtime/lua/vim/lsp/_snippet.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'runtime/lua/vim/lsp/_snippet.lua') diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua index 797d8960d5..e7ada5415f 100644 --- a/runtime/lua/vim/lsp/_snippet.lua +++ b/runtime/lua/vim/lsp/_snippet.lua @@ -17,14 +17,14 @@ P.take_until = function(targets, specials) table.insert(raw, '\\') new_pos = new_pos + 1 c = string.sub(input, new_pos, new_pos) - if not vim.tbl_contains(targets, c) and not vim.tbl_contains(specials, c) then + if not vim.list_contains(targets, c) and not vim.list_contains(specials, c) then table.insert(esc, '\\') end table.insert(raw, c) table.insert(esc, c) new_pos = new_pos + 1 else - if vim.tbl_contains(targets, c) then + if vim.list_contains(targets, c) then break end table.insert(raw, c) -- cgit From eb1f0e8fcca756a00d287e23bf87554e0e7f6dfd Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Sun, 1 Oct 2023 09:54:04 -0700 Subject: feat(lsp)!: replace snippet parser by lpeg grammar --- runtime/lua/vim/lsp/_snippet.lua | 500 --------------------------------------- 1 file changed, 500 deletions(-) delete mode 100644 runtime/lua/vim/lsp/_snippet.lua (limited to 'runtime/lua/vim/lsp/_snippet.lua') diff --git a/runtime/lua/vim/lsp/_snippet.lua b/runtime/lua/vim/lsp/_snippet.lua deleted file mode 100644 index e7ada5415f..0000000000 --- a/runtime/lua/vim/lsp/_snippet.lua +++ /dev/null @@ -1,500 +0,0 @@ -local P = {} - ----Take characters until the target characters (The escape sequence is '\' + char) ----@param targets string[] The character list for stop consuming text. ----@param specials string[] If the character isn't contained in targets/specials, '\' will be left. -P.take_until = function(targets, specials) - targets = targets or {} - specials = specials or {} - - return function(input, pos) - local new_pos = pos - local raw = {} - local esc = {} - while new_pos <= #input do - local c = string.sub(input, new_pos, new_pos) - if c == '\\' then - table.insert(raw, '\\') - new_pos = new_pos + 1 - c = string.sub(input, new_pos, new_pos) - if not vim.list_contains(targets, c) and not vim.list_contains(specials, c) then - table.insert(esc, '\\') - end - table.insert(raw, c) - table.insert(esc, c) - new_pos = new_pos + 1 - else - if vim.list_contains(targets, c) then - break - end - table.insert(raw, c) - table.insert(esc, c) - new_pos = new_pos + 1 - end - end - - if new_pos == pos then - return P.unmatch(pos) - end - - return { - parsed = true, - value = { - raw = table.concat(raw, ''), - esc = table.concat(esc, ''), - }, - pos = new_pos, - } - end -end - -P.unmatch = function(pos) - return { - parsed = false, - value = nil, - pos = pos, - } -end - -P.map = function(parser, map) - return function(input, pos) - local result = parser(input, pos) - if result.parsed then - return { - parsed = true, - value = map(result.value), - pos = result.pos, - } - end - return P.unmatch(pos) - end -end - -P.lazy = function(factory) - return function(input, pos) - return factory()(input, pos) - end -end - -P.token = function(token) - return function(input, pos) - local maybe_token = string.sub(input, pos, pos + #token - 1) - if token == maybe_token then - return { - parsed = true, - value = maybe_token, - pos = pos + #token, - } - end - return P.unmatch(pos) - end -end - -P.pattern = function(p) - return function(input, pos) - local maybe_match = string.match(string.sub(input, pos), '^' .. p) - if maybe_match then - return { - parsed = true, - value = maybe_match, - pos = pos + #maybe_match, - } - end - return P.unmatch(pos) - end -end - -P.many = function(parser) - return function(input, pos) - local values = {} - local new_pos = pos - while new_pos <= #input do - local result = parser(input, new_pos) - if not result.parsed then - break - end - table.insert(values, result.value) - new_pos = result.pos - end - if #values > 0 then - return { - parsed = true, - value = values, - pos = new_pos, - } - end - return P.unmatch(pos) - end -end - -P.any = function(...) - local parsers = { ... } - return function(input, pos) - for _, parser in ipairs(parsers) do - local result = parser(input, pos) - if result.parsed then - return result - end - end - return P.unmatch(pos) - end -end - -P.opt = function(parser) - return function(input, pos) - local result = parser(input, pos) - return { - parsed = true, - value = result.value, - pos = result.pos, - } - end -end - -P.seq = function(...) - local parsers = { ... } - return function(input, pos) - local values = {} - local new_pos = pos - for i, parser in ipairs(parsers) do - local result = parser(input, new_pos) - if result.parsed then - values[i] = result.value - new_pos = result.pos - else - return P.unmatch(pos) - end - end - return { - parsed = true, - value = values, - pos = new_pos, - } - end -end - -local Node = {} - -Node.Type = { - SNIPPET = 0, - TABSTOP = 1, - PLACEHOLDER = 2, - VARIABLE = 3, - CHOICE = 4, - TRANSFORM = 5, - FORMAT = 6, - TEXT = 7, -} - -function Node:__tostring() - local insert_text = {} - if self.type == Node.Type.SNIPPET then - for _, c in ipairs(self.children) do - table.insert(insert_text, tostring(c)) - end - elseif self.type == Node.Type.CHOICE then - table.insert(insert_text, self.items[1]) - elseif self.type == Node.Type.PLACEHOLDER then - for _, c in ipairs(self.children or {}) do - table.insert(insert_text, tostring(c)) - end - elseif self.type == Node.Type.TEXT then - table.insert(insert_text, self.esc) - end - return table.concat(insert_text, '') -end - ---@see https://code.visualstudio.com/docs/editor/userdefinedsnippets#_grammar - -local S = {} -S.dollar = P.token('$') -S.open = P.token('{') -S.close = P.token('}') -S.colon = P.token(':') -S.slash = P.token('/') -S.comma = P.token(',') -S.pipe = P.token('|') -S.plus = P.token('+') -S.minus = P.token('-') -S.question = P.token('?') -S.int = P.map(P.pattern('[0-9]+'), function(value) - return tonumber(value, 10) -end) -S.var = P.pattern('[%a_][%w_]+') -S.text = function(targets, specials) - return P.map(P.take_until(targets, specials), function(value) - return setmetatable({ - type = Node.Type.TEXT, - raw = value.raw, - esc = value.esc, - }, Node) - end) -end - -S.toplevel = P.lazy(function() - return P.any(S.placeholder, S.tabstop, S.variable, S.choice) -end) - -S.format = P.any( - P.map(P.seq(S.dollar, S.int), function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[2], - }, Node) - end), - P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - }, Node) - end), - P.map( - P.seq( - S.dollar, - S.open, - S.int, - S.colon, - S.slash, - P.any( - P.token('upcase'), - P.token('downcase'), - P.token('capitalize'), - P.token('camelcase'), - P.token('pascalcase') - ), - S.close - ), - function(values) - return setmetatable({ - type = Node.Type.FORMAT, - capture_index = values[3], - modifier = values[6], - }, Node) - end - ), - P.map( - P.seq( - S.dollar, - S.open, - S.int, - S.colon, - 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] 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( - P.seq( - S.slash, - P.take_until({ '/' }, { '\\' }), - S.slash, - P.many(P.any(S.format, S.text({ '$', '/' }, { '\\' }))), - S.slash, - P.opt(P.pattern('[ig]+')) - ), - function(values) - return setmetatable({ - type = Node.Type.TRANSFORM, - pattern = values[2].raw, - format = values[4], - option = values[6], - }, Node) - end -) - -S.tabstop = P.any( - P.map(P.seq(S.dollar, S.int), function(values) - return setmetatable({ - type = Node.Type.TABSTOP, - tabstop = values[2], - }, Node) - end), - P.map(P.seq(S.dollar, S.open, S.int, S.close), function(values) - return setmetatable({ - type = Node.Type.TABSTOP, - tabstop = values[3], - }, Node) - end), - P.map(P.seq(S.dollar, S.open, S.int, S.transform, S.close), function(values) - return setmetatable({ - type = Node.Type.TABSTOP, - tabstop = values[3], - transform = values[4], - }, Node) - end) -) - -S.placeholder = P.any( - P.map( - 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], - -- insert empty text if opt did not match. - children = values[5] or { - setmetatable({ - type = Node.Type.TEXT, - raw = '', - esc = '', - }, Node), - }, - }, Node) - end - ) -) - -S.choice = P.map( - P.seq( - S.dollar, - S.open, - S.int, - S.pipe, - P.many(P.map(P.seq(S.text({ ',', '|' }), P.opt(S.comma)), function(values) - return values[1].esc - end)), - S.pipe, - S.close - ), - function(values) - return setmetatable({ - type = Node.Type.CHOICE, - tabstop = values[3], - items = values[5], - }, Node) - end -) - -S.variable = P.any( - P.map(P.seq(S.dollar, S.var), function(values) - return setmetatable({ - type = Node.Type.VARIABLE, - name = values[2], - }, Node) - end), - P.map(P.seq(S.dollar, S.open, S.var, S.close), function(values) - return setmetatable({ - type = Node.Type.VARIABLE, - name = values[3], - }, Node) - end), - P.map(P.seq(S.dollar, S.open, S.var, S.transform, S.close), function(values) - return setmetatable({ - type = Node.Type.VARIABLE, - name = values[3], - transform = values[4], - }, Node) - end), - P.map( - P.seq( - S.dollar, - S.open, - S.var, - S.colon, - P.many(P.any(S.toplevel, S.text({ '$', '}' }, { '\\' }))), - S.close - ), - function(values) - return setmetatable({ - type = Node.Type.VARIABLE, - name = values[3], - children = values[5], - }, Node) - end - ) -) - -S.snippet = P.map(P.many(P.any(S.toplevel, S.text({ '$' }, { '}', '\\' }))), function(values) - return setmetatable({ - type = Node.Type.SNIPPET, - children = values, - }, Node) -end) - -local M = {} - ----The snippet node type enum ----@types table -M.NodeType = Node.Type - ----Parse snippet string and returns the AST ----@param input string ----@return table -function M.parse(input) - local result = S.snippet(input, 1) - if not result.parsed then - error('snippet parsing failed.') - end - return result.value -end - -return M -- cgit