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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
local Agents = require("99.extensions.agents")
local Files = require("99.extensions.files")
local Completions = require("99.extensions.completions")
local DEBOUNCE_MS = 100
--- @param items CompletionItem[]
--- @return table[]
local function to_native_items(items)
local out = {}
for _, item in ipairs(items) do
local info = ""
if item.documentation then
if type(item.documentation) == "string" then
info = item.documentation
elseif item.documentation.value then
info = item.documentation.value
end
end
table.insert(out, {
word = item.insertText or item.label,
abbr = item.label,
info = info,
icase = 1,
dup = 0,
})
end
return out
end
--- @param _99 _99.State
local function register_providers(_99)
Completions.register(Agents.completion_provider(_99))
Completions.register(Files.completion_provider())
end
--- @param buf number
local function setup_completion_autocmd(buf)
local timer = vim.uv.new_timer()
local group = vim.api.nvim_create_augroup(
"99_native_completion_" .. buf,
{ clear = true }
)
vim.api.nvim_create_autocmd("TextChangedI", {
group = group,
buffer = buf,
callback = function()
timer:stop()
timer:start(
DEBOUNCE_MS,
0,
vim.schedule_wrap(function()
if vim.fn.mode() ~= "i" then
return
end
if not vim.api.nvim_buf_is_valid(buf) then
timer:stop()
return
end
local line = vim.api.nvim_get_current_line()
local col = vim.fn.col(".")
local before = line:sub(1, col - 1)
local trigger = nil
local start_col = nil
for _, char in ipairs(Completions.get_trigger_characters()) do
local escaped = Completions.escape_pattern(char)
local pattern = escaped .. "%S*$"
local match_start = before:find(pattern)
if match_start then
trigger = char
start_col = match_start
break
end
end
if not trigger or not start_col then
return
end
local items = Completions.get_completions(trigger)
if #items == 0 then
return
end
local native_items = to_native_items(items)
vim.fn.complete(start_col, native_items)
end)
)
end,
})
vim.api.nvim_create_autocmd("BufWipeout", {
group = group,
buffer = buf,
callback = function()
timer:stop()
pcall(vim.api.nvim_del_augroup_by_id, group)
end,
})
end
--- @param buf number
local function setup_keymaps(buf)
vim.keymap.set("i", "<Tab>", function()
if vim.fn.pumvisible() == 1 then
return "<C-n>"
end
return "<Tab>"
end, { buffer = buf, expr = true, noremap = true })
vim.keymap.set("i", "<S-Tab>", function()
if vim.fn.pumvisible() == 1 then
return "<C-p>"
end
return "<S-Tab>"
end, { buffer = buf, expr = true, noremap = true })
end
--- @param _ _99.State
local function init_for_buffer(_)
local buf = vim.api.nvim_get_current_buf()
vim.bo[buf].filetype = "99prompt"
vim.opt_local.completeopt = "menuone,noinsert,noselect,popup,fuzzy"
setup_completion_autocmd(buf)
setup_keymaps(buf)
end
--- @param _99 _99.State
local function init(_99)
local rule_dirs = {}
if _99.completion and _99.completion.custom_rules then
for _, dir in ipairs(_99.completion.custom_rules) do
table.insert(rule_dirs, dir)
end
end
if _99.completion and _99.completion.files then
Files.setup(_99.completion.files, rule_dirs)
else
Files.setup({ enabled = true }, rule_dirs)
end
register_providers(_99)
end
--- @param _99 _99.State
local function refresh_state(_99)
register_providers(_99)
end
--- @type _99.Extensions.Source
return {
init_for_buffer = init_for_buffer,
init = init,
refresh_state = refresh_state,
}
|