Skip to content

Commit d9542ca

Browse files
committed
feat: initial commit of rewrite
1 parent a8264a6 commit d9542ca

38 files changed

+2855
-1452
lines changed

.neoconf.json

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"neodev": {
3+
"library": {
4+
"plugins": [
5+
"plenary.nvim",
6+
"nvim-web-devicons"
7+
]
8+
}
9+
},
10+
"lspconfig": {
11+
"lua_ls": {
12+
"Lua.runtime.version": "LuaJIT",
13+
"Lua.workspace.checkThirdParty": false
14+
}
15+
}
16+
}

lua/trouble/async.lua

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
local M = {}
2+
3+
M.budget = 1
4+
local Scheduler = {}
5+
Scheduler._queue = {}
6+
Scheduler._executor = assert(vim.loop.new_check())
7+
8+
function Scheduler.step()
9+
local budget = M.budget * 1e6
10+
local start = vim.loop.hrtime()
11+
while #Scheduler._queue > 0 and vim.loop.hrtime() - start < budget do
12+
local a = table.remove(Scheduler._queue, 1)
13+
a:_step()
14+
if a.running then
15+
table.insert(Scheduler._queue, a)
16+
end
17+
end
18+
if #Scheduler._queue == 0 then
19+
return Scheduler._executor:stop()
20+
end
21+
end
22+
23+
---@param a Async
24+
function Scheduler.add(a)
25+
table.insert(Scheduler._queue, a)
26+
if not Scheduler._executor:is_active() then
27+
Scheduler._executor:start(vim.schedule_wrap(Scheduler.step))
28+
end
29+
end
30+
31+
--- @alias AsyncCallback fun(result?:any, error?:string)
32+
33+
--- @class Async
34+
--- @field running boolean
35+
--- @field result? any
36+
--- @field error? string
37+
--- @field callbacks AsyncCallback[]
38+
--- @field thread thread
39+
local Async = {}
40+
Async.__index = Async
41+
42+
function Async.new(fn)
43+
local self = setmetatable({}, Async)
44+
self.callbacks = {}
45+
self.running = true
46+
self.thread = coroutine.create(fn)
47+
Scheduler.add(self)
48+
return self
49+
end
50+
51+
---@param result? any
52+
---@param error? string
53+
function Async:_done(result, error)
54+
if self.running then
55+
self.running = false
56+
self.result = result
57+
self.error = error
58+
end
59+
for _, callback in ipairs(self.callbacks) do
60+
callback(result, error)
61+
end
62+
-- only run each callback once.
63+
-- _done can possibly be called multiple times.
64+
-- so we need to clear callbacks after executing them.
65+
self.callbacks = {}
66+
end
67+
68+
function Async:_step()
69+
local ok, res = coroutine.resume(self.thread)
70+
if not ok then
71+
return self:_done(nil, res)
72+
elseif res == "abort" then
73+
return self:_done(nil, "abort")
74+
elseif coroutine.status(self.thread) == "dead" then
75+
return self:_done(res)
76+
end
77+
end
78+
79+
function Async:cancel()
80+
self:_done(nil, "abort")
81+
end
82+
83+
---@param cb AsyncCallback
84+
function Async:await(cb)
85+
if not cb then
86+
error("callback is required")
87+
end
88+
if self.running then
89+
table.insert(self.callbacks, cb)
90+
else
91+
cb(self.result, self.error)
92+
end
93+
end
94+
95+
function Async:sync()
96+
while self.running do
97+
vim.wait(10)
98+
end
99+
return self.error and error(self.error) or self.result
100+
end
101+
102+
--- @return boolean
103+
function M.is_async(obj)
104+
return obj and type(obj) == "table" and getmetatable(obj) == Async
105+
end
106+
107+
---@generic F
108+
---@param fn F
109+
---@return F|fun(...): Async
110+
function M.wrap(fn)
111+
return function(...)
112+
local args = { ... }
113+
return Async.new(function()
114+
return fn(unpack(args))
115+
end)
116+
end
117+
end
118+
119+
-- This will yield when called from a coroutine
120+
---@async
121+
function M.yield(...)
122+
if coroutine.running() == nil then
123+
error("Trying to yield from a non-yieldable context")
124+
return ...
125+
end
126+
return coroutine.yield(...)
127+
end
128+
129+
---@async
130+
function M.abort()
131+
return M.yield("abort")
132+
end
133+
134+
return M

lua/trouble/cache.lua

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
---@class trouble.CacheM: {[string]: trouble.Cache}
2+
local M = {}
3+
4+
---@type table<string, {name:string, hit: number, miss: number, ratio?:number}>
5+
M.stats = {}
6+
7+
---@class trouble.Cache: {[string]: any}
8+
---@field data table<string, any>
9+
---@field name string
10+
local C = {}
11+
12+
function C:__index(key)
13+
local ret = C[key]
14+
if ret then
15+
return ret
16+
end
17+
ret = self.data[key]
18+
M.stats[self.name] = M.stats[self.name] or { name = self.name, hit = 0, miss = 0 }
19+
local stats = M.stats[self.name]
20+
if ret ~= nil then
21+
stats.hit = stats.hit + 1
22+
else
23+
stats.miss = stats.miss + 1
24+
end
25+
return ret
26+
end
27+
28+
function C:__newindex(key, value)
29+
self.data[key] = value
30+
end
31+
32+
function C:clear()
33+
self.data = {}
34+
end
35+
36+
function M.new(name)
37+
return setmetatable({ data = {}, name = name }, C)
38+
end
39+
40+
function M.report()
41+
for _, v in pairs(M.stats) do
42+
v.ratio = math.ceil(v.hit / (v.hit + v.miss) * 100)
43+
end
44+
return M.stats
45+
end
46+
47+
function M.__index(_, k)
48+
M[k] = M.new(k)
49+
return M[k]
50+
end
51+
52+
local ret = setmetatable(M, M)
53+
return ret

lua/trouble/colors.lua

-34
This file was deleted.

lua/trouble/command.lua

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
local Config = require("trouble.config")
2+
local Util = require("trouble.util")
3+
4+
local M = {}
5+
6+
---@param input string
7+
function M.parse(input)
8+
local source, args = input:match("%s*(%S+)%s*(.*)$")
9+
if not source then
10+
Util.error("Invalid arguments: " .. input)
11+
return
12+
end
13+
local parts = vim.split(args, "%s+")
14+
---@type trouble.Config
15+
local opts = {}
16+
17+
for _, part in ipairs(parts) do
18+
local key, value = part:match("([^=]+)=(.*)")
19+
if not key then
20+
key = part
21+
value = true
22+
elseif value == "true" then
23+
value = true
24+
elseif value == "false" then
25+
value = false
26+
elseif tonumber(value) then
27+
value = tonumber(value)
28+
end
29+
-- remove quotes
30+
if type(value) == "string" then
31+
---@diagnostic disable-next-line: no-unknown
32+
value = value:gsub("^['\"]", ""):gsub("['\"]$", "")
33+
end
34+
---@diagnostic disable-next-line: no-unknown
35+
opts[key] = value
36+
end
37+
return source, opts
38+
end
39+
40+
---@param line string
41+
---@param col number
42+
function M.complete(_, line, col)
43+
line = line:sub(1, col)
44+
local candidates = {} ---@type string[]
45+
local prefix = ""
46+
local source = line:match("Trouble%s+(%S*)$")
47+
if source then
48+
prefix = source
49+
candidates = Config.modes()
50+
else
51+
local args = line:match("Trouble%s+%S+%s*.*%s+(%S*)$")
52+
if args then
53+
prefix = args
54+
candidates = vim.tbl_keys(Config.get())
55+
candidates = vim.tbl_map(function(x)
56+
return x .. "="
57+
end, candidates)
58+
end
59+
end
60+
61+
candidates = vim.tbl_filter(function(x)
62+
return tostring(x):find(prefix, 1, true) ~= nil
63+
end, candidates)
64+
table.sort(candidates)
65+
return candidates
66+
end
67+
68+
function M.execute(input)
69+
local mode, opts = M.parse(input.args)
70+
if mode and opts then
71+
opts.mode = mode
72+
require("trouble").open(opts)
73+
end
74+
end
75+
76+
return M

0 commit comments

Comments
 (0)