1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
local utils = {
parse_cache = {},
}
--- Parses the json file and notifies the user if there's an error
---@param path string
---@param json string
function utils.parse_json_with_error_msg(path, json)
local ok, parsed = pcall(vim.json.decode, json)
if not ok then
vim.notify(
'Failed to parse json file "' .. path .. '" for blink.cmp snippets. Error: ' .. parsed,
vim.log.levels.ERROR,
{ title = 'blink.cmp' }
)
return {}
end
return parsed
end
---@type fun(path: string): string|nil
function utils.read_file(path)
local file = io.open(path, 'r')
if not file then return nil end
local content = file:read('*a')
file:close()
return content
end
---@type fun(input: string): vim.snippet.Node<vim.snippet.SnippetData>|nil
function utils.safe_parse(input)
if utils.parse_cache[input] then return utils.parse_cache[input] end
local safe, parsed = pcall(vim.lsp._snippet_grammar.parse, input)
if not safe then return nil end
utils.parse_cache[input] = parsed
return parsed
end
---@type fun(snippet: blink.cmp.Snippet, fallback: string): table
function utils.read_snippet(snippet, fallback)
local snippets = {}
local prefix = snippet.prefix or fallback
local description = snippet.description or fallback
local body = snippet.body
if type(description) == 'table' then description = vim.fn.join(description, '') end
if type(prefix) == 'table' then
for _, p in ipairs(prefix) do
snippets[p] = {
prefix = p,
body = body,
description = description,
}
end
else
snippets[prefix] = {
prefix = prefix,
body = body,
description = description,
}
end
return snippets
end
function utils.get_tab_stops(snippet)
local expanded_snippet = require('blink.cmp.sources.snippets.utils').safe_parse(snippet)
if not expanded_snippet then return end
local tabstops = {}
local grammar = require('vim.lsp._snippet_grammar')
local line = 1
local character = 1
for _, child in ipairs(expanded_snippet.data.children) do
local lines = tostring(child) == '' and {} or vim.split(tostring(child), '\n')
line = line + math.max(#lines - 1, 0)
character = #lines == 0 and character or #lines > 1 and #lines[#lines] or (character + #lines[#lines])
if child.type == grammar.NodeType.Placeholder or child.type == grammar.NodeType.Tabstop then
table.insert(tabstops, { index = child.data.tabstop, line = line, character = character })
end
end
table.sort(tabstops, function(a, b) return a.index < b.index end)
return tabstops
end
return utils
|