Skip to content

Commit 37dbee4

Browse files
committed
feat: implement snippets without deps
1 parent 3ee91b5 commit 37dbee4

File tree

7 files changed

+539
-66
lines changed

7 files changed

+539
-66
lines changed

README.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -204,5 +204,8 @@ All of the following are planned, but not yet implemented.
204204

205205
## Special Thanks
206206

207-
@redxtech Help with design, testing and helping me keep my sanity
208-
@aadityasahay Help with rust and design
207+
@garymjr nvim-snippets implementation used for snippets source
208+
209+
@redxtech Help with design and testing
210+
211+
@aadityasahay Help with rust, testing and design

lua/blink/cmp/sources/snippets.lua

-64
This file was deleted.
+188
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
-- credit to https://github.com/L3MON4D3 for these variables
2+
-- see: https://github.com/L3MON4D3/LuaSnip/blob/master/lua/luasnip/util/_builtin_vars.lua
3+
-- and credit to https://github.com/garymjr for his changes
4+
-- see: https://github.com/garymjr/nvim-snippets/blob/main/lua/snippets/utils/builtin.lua
5+
6+
local builtin = {
7+
lazy = {},
8+
}
9+
10+
function builtin.lazy.TM_FILENAME() return vim.fn.expand('%:t') end
11+
12+
function builtin.lazy.TM_FILENAME_BASE() return vim.fn.expand('%:t:s?\\.[^\\.]\\+$??') end
13+
14+
function builtin.lazy.TM_DIRECTORY() return vim.fn.expand('%:p:h') end
15+
16+
function builtin.lazy.TM_FILEPATH() return vim.fn.expand('%:p') end
17+
18+
function builtin.lazy.CLIPBOARD() return vim.fn.getreg(vim.v.register, true) end
19+
20+
local function buf_to_ws_part()
21+
local LSP_WORSKPACE_PARTS = 'LSP_WORSKPACE_PARTS'
22+
local ok, ws_parts = pcall(vim.api.nvim_buf_get_var, 0, LSP_WORSKPACE_PARTS)
23+
if not ok then
24+
local file_path = vim.fn.expand('%:p')
25+
26+
for _, ws in pairs(vim.lsp.buf.list_workspace_folders()) do
27+
if file_path:find(ws, 1, true) == 1 then
28+
ws_parts = { ws, file_path:sub(#ws + 2, -1) }
29+
break
30+
end
31+
end
32+
-- If it can't be extracted from lsp, then we use the file path
33+
if not ok and not ws_parts then ws_parts = { vim.fn.expand('%:p:h'), vim.fn.expand('%:p:t') } end
34+
vim.api.nvim_buf_set_var(0, LSP_WORSKPACE_PARTS, ws_parts)
35+
end
36+
return ws_parts
37+
end
38+
39+
function builtin.lazy.RELATIVE_FILEPATH() -- The relative (to the opened workspace or folder) file path of the current document
40+
return buf_to_ws_part()[2]
41+
end
42+
43+
function builtin.lazy.WORKSPACE_FOLDER() -- The path of the opened workspace or folder
44+
return buf_to_ws_part()[1]
45+
end
46+
47+
function builtin.lazy.WORKSPACE_NAME() -- The name of the opened workspace or folder
48+
local parts = vim.split(buf_to_ws_part()[1] or '', '[\\/]')
49+
return parts[#parts]
50+
end
51+
52+
function builtin.lazy.CURRENT_YEAR() return os.date('%Y') end
53+
54+
function builtin.lazy.CURRENT_YEAR_SHORT() return os.date('%y') end
55+
56+
function builtin.lazy.CURRENT_MONTH() return os.date('%m') end
57+
58+
function builtin.lazy.CURRENT_MONTH_NAME() return os.date('%B') end
59+
60+
function builtin.lazy.CURRENT_MONTH_NAME_SHORT() return os.date('%b') end
61+
62+
function builtin.lazy.CURRENT_DATE() return os.date('%d') end
63+
64+
function builtin.lazy.CURRENT_DAY_NAME() return os.date('%A') end
65+
66+
function builtin.lazy.CURRENT_DAY_NAME_SHORT() return os.date('%a') end
67+
68+
function builtin.lazy.CURRENT_HOUR() return os.date('%H') end
69+
70+
function builtin.lazy.CURRENT_MINUTE() return os.date('%M') end
71+
72+
function builtin.lazy.CURRENT_SECOND() return os.date('%S') end
73+
74+
function builtin.lazy.CURRENT_SECONDS_UNIX() return tostring(os.time()) end
75+
76+
local function get_timezone_offset(ts)
77+
local utcdate = os.date('!*t', ts)
78+
local localdate = os.date('*t', ts)
79+
localdate.isdst = false -- this is the trick
80+
local diff = os.difftime(os.time(localdate), os.time(utcdate))
81+
local h, m = math.modf(diff / 3600)
82+
return string.format('%+.4d', 100 * h + 60 * m)
83+
end
84+
85+
function builtin.lazy.CURRENT_TIMEZONE_OFFSET()
86+
return get_timezone_offset(os.time()):gsub('([+-])(%d%d)(%d%d)$', '%1%2:%3')
87+
end
88+
89+
math.randomseed(os.time())
90+
91+
function builtin.lazy.RANDOM() return string.format('%06d', math.random(999999)) end
92+
93+
function builtin.lazy.RANDOM_HEX()
94+
return string.format('%06x', math.random(16777216)) --16^6
95+
end
96+
97+
function builtin.lazy.UUID()
98+
local random = math.random
99+
local template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
100+
local out
101+
local function subs(c)
102+
local v = (((c == 'x') and random(0, 15)) or random(8, 11))
103+
return string.format('%x', v)
104+
end
105+
106+
out = template:gsub('[xy]', subs)
107+
return out
108+
end
109+
110+
local _comments_cache = {}
111+
local function buffer_comment_chars()
112+
local commentstring = vim.bo.commentstring
113+
if _comments_cache[commentstring] then return _comments_cache[commentstring] end
114+
local comments = { '//', '/*', '*/' }
115+
local placeholder = '%s'
116+
local index_placeholder = commentstring:find(vim.pesc(placeholder))
117+
if index_placeholder then
118+
index_placeholder = index_placeholder - 1
119+
if index_placeholder + #placeholder == #commentstring then
120+
comments[1] = vim.trim(commentstring:sub(1, -#placeholder - 1))
121+
else
122+
comments[2] = vim.trim(commentstring:sub(1, index_placeholder))
123+
comments[3] = vim.trim(commentstring:sub(index_placeholder + #placeholder + 1, -1))
124+
end
125+
end
126+
_comments_cache[commentstring] = comments
127+
return comments
128+
end
129+
130+
function builtin.lazy.LINE_COMMENT() return buffer_comment_chars()[1] end
131+
132+
function builtin.lazy.BLOCK_COMMENT_START() return buffer_comment_chars()[2] end
133+
134+
function builtin.lazy.BLOCK_COMMENT_END() return buffer_comment_chars()[3] end
135+
136+
local function get_cursor()
137+
local c = vim.api.nvim_win_get_cursor(0)
138+
c[1] = c[1] - 1
139+
return c
140+
end
141+
142+
local function get_current_line()
143+
local pos = get_cursor()
144+
return vim.api.nvim_buf_get_lines(0, pos[1], pos[1] + 1, false)[1]
145+
end
146+
147+
local function word_under_cursor(cur, line)
148+
if line == nil then return end
149+
150+
local ind_start = 1
151+
local ind_end = #line
152+
153+
while true do
154+
local tmp = string.find(line, '%W%w', ind_start)
155+
if not tmp then break end
156+
if tmp > cur[2] + 1 then break end
157+
ind_start = tmp + 1
158+
end
159+
160+
local tmp = string.find(line, '%w%W', cur[2] + 1)
161+
if tmp then ind_end = tmp end
162+
163+
return string.sub(line, ind_start, ind_end)
164+
end
165+
166+
local function get_selected_text()
167+
if vim.fn.visualmode() == 'V' then return vim.fn.trim(vim.fn.getreg(vim.v.register, true), '\n', 2) end
168+
return ''
169+
end
170+
171+
vim.api.nvim_create_autocmd('InsertEnter', {
172+
group = vim.api.nvim_create_augroup('BlinkSnippetsEagerEnter', { clear = true }),
173+
callback = function()
174+
builtin.eager = {}
175+
builtin.eager.TM_CURRENT_LINE = get_current_line()
176+
builtin.eager.TM_CURRENT_WORD = word_under_cursor(get_cursor(), builtin.eager.TM_CURRENT_LINE)
177+
builtin.eager.TM_LINE_INDEX = tostring(get_cursor()[1])
178+
builtin.eager.TM_LINE_NUMBER = tostring(get_cursor()[1] + 1)
179+
builtin.eager.TM_SELECTED_TEXT = get_selected_text()
180+
end,
181+
})
182+
183+
vim.api.nvim_create_autocmd('InsertLeave', {
184+
group = vim.api.nvim_create_augroup('BlinkSnippetsEagerLeave', { clear = true }),
185+
callback = function() builtin.eager = nil end,
186+
})
187+
188+
return builtin
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
--- @class blink.cmp.SnippetsOpts
2+
--- @param friendly_snippets boolean
3+
--- @param search_paths string[]
4+
--- @param global_snippets string[]
5+
--- @param extended_filetypes table<string, string[]>
6+
--- @param ignored_filetypes string[]
7+
8+
--- @class blink.cmp.SnippetsSource : blink.cmp.Source
9+
--- @field cache table<string, blink.cmp.CompletionItem[]>
10+
local snippets = {}
11+
12+
--- @param opts blink.cmp.SnippetsOpts
13+
function snippets.new(opts)
14+
local self = setmetatable({}, { __index = snippets })
15+
self.cache = {}
16+
self.registry = require('blink.cmp.sources.snippets.registry').new(opts)
17+
return self
18+
end
19+
20+
function snippets:get_completions(_, callback)
21+
local filetype = vim.bo.filetype
22+
if vim.tbl_contains(self.registry.config.ignored_filetypes, filetype) then
23+
return callback({ is_incomplete_forward = false, is_incomplete_backward = false, items = {} })
24+
end
25+
26+
if not self.cache[filetype] then
27+
local global_snippets = self.registry:get_global_snippets()
28+
local extended_snippets = self.registry:get_extended_snippets(filetype)
29+
local ft_snippets = self.registry:get_snippets_for_ft(filetype)
30+
local snips = vim.tbl_deep_extend('force', {}, global_snippets, extended_snippets, ft_snippets)
31+
32+
self.cache[filetype] = vim.tbl_map(
33+
function(snippet) return self.registry:snippet_to_completion_item(snippet) end,
34+
snips
35+
)
36+
end
37+
38+
callback({
39+
is_incomplete_forward = false,
40+
is_incomplete_backward = false,
41+
items = self.cache[filetype],
42+
})
43+
end
44+
45+
function snippets:resolve(item, callback)
46+
-- TODO: ideally context is passed with the filetype
47+
local documentation = '```'
48+
.. vim.bo.filetype
49+
.. '\n'
50+
.. self.registry:preview(item.insertText)
51+
.. '```'
52+
.. '\n---\n'
53+
.. item.description
54+
55+
local resolved_item = vim.deepcopy(item)
56+
resolved_item.documentation = {
57+
kind = 'markdown',
58+
value = documentation,
59+
}
60+
callback(resolved_item)
61+
end
62+
63+
return snippets

0 commit comments

Comments
 (0)