Skip to content

Commit b533a72

Browse files
committed
Add core completion file to share common functionalities
1 parent 76d9cda commit b533a72

File tree

5 files changed

+225
-492
lines changed

5 files changed

+225
-492
lines changed

lua/parrot/completion/blink.lua

Lines changed: 33 additions & 224 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
local async = require("blink.cmp.lib.async")
22
local logger = require("parrot.logger")
3-
local utils = require("parrot.utils")
3+
local core = require("parrot.completion.core")
44
local comp_utils = require("parrot.completion.utils")
55

66
local source = {}
@@ -22,80 +22,61 @@ function source:is_available(context)
2222
return comp_utils.is_completion_available(context.bufnr)
2323
end
2424

25-
local function extract_cmd(context)
26-
if not context or not context.line then
27-
return nil
28-
end
29-
local line_before_cursor = context.line:sub(1, context.cursor[2])
30-
return line_before_cursor:match("^%s*(@%S*)")
31-
end
32-
3325
function source:get_completions(context, callback)
3426
callback = vim.schedule_wrap(callback)
35-
local cmd = extract_cmd(context)
27+
local input = context.line:sub(1, context.cursor[2])
28+
local cmd = core.extract_cmd(input)
3629
if not cmd then
3730
callback({ is_incomplete_forward = false, is_incomplete_backward = false, items = {} })
3831
return
3932
end
40-
4133
if cmd == "@" then
42-
local CompletionItemKind = require("blink.cmp.types").CompletionItemKind
43-
local items = {
44-
{
45-
label = "file",
46-
kind = CompletionItemKind.Keyword,
47-
documentation = {
48-
kind = "markdown",
49-
value = comp_utils.get_command_documentation("file"),
50-
},
51-
},
52-
{
53-
label = "buffer",
54-
kind = CompletionItemKind.Keyword,
55-
documentation = {
56-
kind = "markdown",
57-
value = comp_utils.get_command_documentation("buffer"),
58-
},
59-
},
60-
{
61-
label = "directory",
62-
kind = CompletionItemKind.Keyword,
63-
documentation = {
64-
kind = "markdown",
65-
value = comp_utils.get_command_documentation("directory"),
66-
},
67-
},
68-
}
69-
callback({ is_incomplete_forward = false, is_incomplete_backward = false, items = items })
34+
local result = core.get_base_completion_items()
35+
callback({ is_incomplete_forward = result.is_incomplete, is_incomplete_backward = false, items = result.items })
7036
elseif cmd:match("^@file:") then
7137
local path = cmd:sub(7)
72-
self
73-
:get_file_completions(context, path)
74-
:map(function(completion_response)
75-
callback(completion_response)
38+
async.task
39+
.new(function(resolve)
40+
local result = core.get_file_completions_sync(path, false, self.opts.max_items)
41+
resolve({
42+
is_incomplete_forward = result.is_incomplete,
43+
is_incomplete_backward = false,
44+
items = result.items,
45+
})
7646
end)
47+
:map(callback)
7748
:catch(function(err)
7849
logger.error("File completion error: " .. tostring(err))
7950
callback({ is_incomplete_forward = false, is_incomplete_backward = false, items = {} })
8051
end)
8152
elseif cmd:match("^@buffer:") then
8253
local query = cmd:sub(9):lower()
83-
self
84-
:get_buffer_completions(context, query)
85-
:map(function(completion_response)
86-
callback(completion_response)
54+
async.task
55+
.new(function(resolve)
56+
local result = core.get_buffer_completions_sync(query, self.opts.max_items)
57+
resolve({
58+
is_incomplete_forward = result.is_incomplete,
59+
is_incomplete_backward = false,
60+
items = result.items,
61+
})
8762
end)
63+
:map(callback)
8864
:catch(function(err)
8965
logger.error("Buffer completion error: " .. tostring(err))
9066
callback({ is_incomplete_forward = false, is_incomplete_backward = false, items = {} })
9167
end)
9268
elseif cmd:match("^@directory:") then
9369
local path = cmd:sub(12)
94-
self
95-
:get_directory_completions(context, path)
96-
:map(function(completion_response)
97-
callback(completion_response)
70+
async.task
71+
.new(function(resolve)
72+
local result = core.get_file_completions_sync(path, true, self.opts.max_items)
73+
resolve({
74+
is_incomplete_forward = result.is_incomplete,
75+
is_incomplete_backward = false,
76+
items = result.items,
77+
})
9878
end)
79+
:map(callback)
9980
:catch(function(err)
10081
logger.error("Directory completion error: " .. tostring(err))
10182
callback({ is_incomplete_forward = false, is_incomplete_backward = false, items = {} })
@@ -105,179 +86,7 @@ function source:get_completions(context, callback)
10586
end
10687
end
10788

108-
function source:get_file_completions(context, path)
109-
local cwd = vim.fn.getcwd()
110-
local target_dir = comp_utils.resolve_path(path, cwd)
111-
if not target_dir then
112-
return async.task.new(function(resolve)
113-
resolve({ is_incomplete_forward = false, is_incomplete_backward = false, items = {} })
114-
end)
115-
end
116-
return comp_utils
117-
.scan_dir_async(target_dir, async)
118-
:map(function(entries)
119-
return comp_utils.fs_stat_all_async(target_dir, entries, async)
120-
end)
121-
:map(function(entries)
122-
local CompletionItemKind = require("blink.cmp.types").CompletionItemKind
123-
local items = {}
124-
for _, entry in ipairs(entries) do
125-
if #items >= self.opts.max_items then
126-
break
127-
end
128-
if self.opts.show_hidden_files or entry.name:sub(1, 1) ~= "." then
129-
local full_path = utils.path_join(target_dir, entry.name)
130-
local is_dir = entry.type == "directory"
131-
local documentation_value
132-
if is_dir then
133-
documentation_value = string.format("**Directory:** %s", full_path)
134-
else
135-
local size = entry.stat.size or 0
136-
local mtime = os.date("%Y-%m-%d %H:%M", (entry.stat.mtime or {}).sec or os.time())
137-
documentation_value =
138-
string.format("**File:** %s\n**Size:** %d bytes\n**Modified:** %s", full_path, size, mtime)
139-
end
140-
table.insert(items, {
141-
label = is_dir and entry.name .. "/" or entry.name,
142-
kind = is_dir and CompletionItemKind.Folder or CompletionItemKind.File,
143-
filterText = entry.name:lower(),
144-
documentation = {
145-
kind = "markdown",
146-
value = documentation_value,
147-
},
148-
data = {
149-
path = entry.name,
150-
full_path = full_path,
151-
type = entry.type,
152-
},
153-
})
154-
end
155-
end
156-
table.sort(items, function(a, b)
157-
local a_is_dir = a.kind == CompletionItemKind.Folder
158-
local b_is_dir = b.kind == CompletionItemKind.Folder
159-
if a_is_dir and not b_is_dir then
160-
return true
161-
elseif not a_is_dir and b_is_dir then
162-
return false
163-
else
164-
return a.label:lower() < b.label:lower()
165-
end
166-
end)
167-
return {
168-
is_incomplete_forward = (#items >= self.opts.max_items),
169-
is_incomplete_backward = false,
170-
items = items,
171-
}
172-
end)
173-
end
174-
175-
function source:get_buffer_completions(context, query)
176-
return async.task.new(function(resolve)
177-
local CompletionItemKind = require("blink.cmp.types").CompletionItemKind
178-
local items = {}
179-
local buffers = vim.api.nvim_list_bufs()
180-
local current_buf = vim.api.nvim_get_current_buf()
181-
for _, buf in ipairs(buffers) do
182-
if #items >= self.opts.max_items then
183-
break
184-
end
185-
if vim.api.nvim_buf_is_loaded(buf) then
186-
local bufhidden = vim.api.nvim_buf_get_option(buf, "bufhidden")
187-
local name = vim.api.nvim_buf_get_name(buf)
188-
local filetype = vim.api.nvim_buf_get_option(buf, "filetype")
189-
local modified = vim.api.nvim_buf_get_option(buf, "modified")
190-
if name and name ~= "" and not (bufhidden and bufhidden:match("^wipe")) then
191-
local filename = vim.fn.fnamemodify(name, ":t")
192-
local rel_path = vim.fn.fnamemodify(name, ":~:.")
193-
if
194-
not query
195-
or query == ""
196-
or filename:lower():find(query, 1, true)
197-
or rel_path:lower():find(query, 1, true)
198-
then
199-
local item = {
200-
label = filename,
201-
kind = CompletionItemKind.Buffer,
202-
detail = string.format("Buffer No.: [%d]\nRelative path: %s\n", buf, rel_path),
203-
filterText = filename:lower(),
204-
documentation = {
205-
kind = "markdown",
206-
value = string.format(
207-
"Absolute path: %s\nType: %s\n%s",
208-
name,
209-
filetype ~= "" and filetype or "unknown",
210-
modified and "*(modified)*" or ""
211-
),
212-
},
213-
data = { buffer_id = buf, path = name },
214-
}
215-
if buf == current_buf then
216-
table.insert(items, 1, item)
217-
else
218-
table.insert(items, item)
219-
end
220-
end
221-
end
222-
end
223-
end
224-
resolve({
225-
is_incomplete_forward = false,
226-
is_incomplete_backward = false,
227-
items = items,
228-
})
229-
end)
230-
end
231-
232-
function source:get_directory_completions(context, path)
233-
local cwd = vim.fn.getcwd()
234-
local target_dir = comp_utils.resolve_path(path, cwd)
235-
if not target_dir then
236-
return async.task.new(function(resolve)
237-
resolve({ is_incomplete_forward = false, is_incomplete_backward = false, items = {} })
238-
end)
239-
end
240-
return comp_utils
241-
.scan_dir_async(target_dir, async)
242-
:map(function(entries)
243-
return comp_utils.fs_stat_all_async(target_dir, entries, async)
244-
end)
245-
:map(function(entries)
246-
local CompletionItemKind = require("blink.cmp.types").CompletionItemKind
247-
local items = {}
248-
for _, entry in ipairs(entries) do
249-
if #items >= self.opts.max_items then
250-
break
251-
end
252-
if entry.type == "directory" and (self.opts.show_hidden_files or entry.name:sub(1, 1) ~= ".") then
253-
local full_path = utils.path_join(target_dir, entry.name)
254-
table.insert(items, {
255-
label = entry.name .. "/",
256-
kind = CompletionItemKind.Folder,
257-
filterText = entry.name:lower(),
258-
documentation = {
259-
kind = "markdown",
260-
value = string.format("**Directory:** %s", full_path),
261-
},
262-
data = {
263-
path = entry.name,
264-
full_path = full_path,
265-
type = entry.type,
266-
},
267-
})
268-
end
269-
end
270-
table.sort(items, function(a, b)
271-
return a.label:lower() < b.label:lower()
272-
end)
273-
return {
274-
is_incomplete_forward = (#items >= self.opts.max_items),
275-
is_incomplete_backward = false,
276-
items = items,
277-
}
278-
end)
279-
end
280-
89+
-- Keep the resolve function as is, since it’s specific to blink.cmp
28190
function source:resolve(item, callback)
28291
callback = vim.schedule_wrap(callback)
28392
if not item or not item.data or item.data.type ~= "file" or not item.data.full_path then

0 commit comments

Comments
 (0)