Skip to content

Commit

Permalink
feat: initial commit of rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
folke committed Oct 23, 2023
1 parent f1168fe commit e3b0114
Show file tree
Hide file tree
Showing 38 changed files with 2,855 additions and 1,452 deletions.
16 changes: 16 additions & 0 deletions .neoconf.json
@@ -0,0 +1,16 @@
{
"neodev": {
"library": {
"plugins": [
"plenary.nvim",
"nvim-web-devicons"
]
}
},
"lspconfig": {
"lua_ls": {
"Lua.runtime.version": "LuaJIT",
"Lua.workspace.checkThirdParty": false
}
}
}
134 changes: 134 additions & 0 deletions lua/trouble/async.lua
@@ -0,0 +1,134 @@
local M = {}

M.budget = 1
local Scheduler = {}
Scheduler._queue = {}
Scheduler._executor = assert(vim.loop.new_check())

function Scheduler.step()
local budget = M.budget * 1e6
local start = vim.loop.hrtime()
while #Scheduler._queue > 0 and vim.loop.hrtime() - start < budget do
local a = table.remove(Scheduler._queue, 1)
a:_step()
if a.running then
table.insert(Scheduler._queue, a)
end
end
if #Scheduler._queue == 0 then
return Scheduler._executor:stop()
end
end

---@param a Async
function Scheduler.add(a)
table.insert(Scheduler._queue, a)
if not Scheduler._executor:is_active() then
Scheduler._executor:start(vim.schedule_wrap(Scheduler.step))
end
end

--- @alias AsyncCallback fun(result?:any, error?:string)

--- @class Async
--- @field running boolean
--- @field result? any
--- @field error? string
--- @field callbacks AsyncCallback[]
--- @field thread thread
local Async = {}
Async.__index = Async

function Async.new(fn)
local self = setmetatable({}, Async)
self.callbacks = {}
self.running = true
self.thread = coroutine.create(fn)
Scheduler.add(self)
return self
end

---@param result? any
---@param error? string
function Async:_done(result, error)
if self.running then
self.running = false
self.result = result
self.error = error
end
for _, callback in ipairs(self.callbacks) do
callback(result, error)
end
-- only run each callback once.
-- _done can possibly be called multiple times.
-- so we need to clear callbacks after executing them.
self.callbacks = {}
end

function Async:_step()
local ok, res = coroutine.resume(self.thread)
if not ok then
return self:_done(nil, res)
elseif res == "abort" then
return self:_done(nil, "abort")
elseif coroutine.status(self.thread) == "dead" then
return self:_done(res)
end
end

function Async:cancel()
self:_done(nil, "abort")
end

---@param cb AsyncCallback
function Async:await(cb)
if not cb then
error("callback is required")
end
if self.running then
table.insert(self.callbacks, cb)
else
cb(self.result, self.error)
end
end

function Async:sync()
while self.running do
vim.wait(10)
end
return self.error and error(self.error) or self.result
end

--- @return boolean
function M.is_async(obj)
return obj and type(obj) == "table" and getmetatable(obj) == Async
end

---@generic F
---@param fn F
---@return F|fun(...): Async
function M.wrap(fn)
return function(...)
local args = { ... }
return Async.new(function()
return fn(unpack(args))
end)
end
end

-- This will yield when called from a coroutine
---@async
function M.yield(...)
if coroutine.running() == nil then
error("Trying to yield from a non-yieldable context")
return ...
end
return coroutine.yield(...)
end

---@async
function M.abort()
return M.yield("abort")
end

return M
53 changes: 53 additions & 0 deletions lua/trouble/cache.lua
@@ -0,0 +1,53 @@
---@class trouble.CacheM: {[string]: trouble.Cache}
local M = {}

---@type table<string, {name:string, hit: number, miss: number, ratio?:number}>
M.stats = {}

---@class trouble.Cache: {[string]: any}
---@field data table<string, any>
---@field name string
local C = {}

function C:__index(key)
local ret = C[key]
if ret then
return ret
end
ret = self.data[key]
M.stats[self.name] = M.stats[self.name] or { name = self.name, hit = 0, miss = 0 }
local stats = M.stats[self.name]
if ret ~= nil then
stats.hit = stats.hit + 1
else
stats.miss = stats.miss + 1
end
return ret
end

function C:__newindex(key, value)
self.data[key] = value
end

function C:clear()
self.data = {}
end

function M.new(name)
return setmetatable({ data = {}, name = name }, C)
end

function M.report()
for _, v in pairs(M.stats) do
v.ratio = math.ceil(v.hit / (v.hit + v.miss) * 100)
end
return M.stats
end

function M.__index(_, k)
M[k] = M.new(k)
return M[k]
end

local ret = setmetatable(M, M)
return ret
34 changes: 0 additions & 34 deletions lua/trouble/colors.lua

This file was deleted.

76 changes: 76 additions & 0 deletions lua/trouble/command.lua
@@ -0,0 +1,76 @@
local Config = require("trouble.config")
local Util = require("trouble.util")

local M = {}

---@param input string
function M.parse(input)
local source, args = input:match("%s*(%S+)%s*(.*)$")
if not source then
Util.error("Invalid arguments: " .. input)
return
end
local parts = vim.split(args, "%s+")
---@type trouble.Config
local opts = {}

for _, part in ipairs(parts) do
local key, value = part:match("([^=]+)=(.*)")
if not key then
key = part
value = true
elseif value == "true" then
value = true
elseif value == "false" then
value = false
elseif tonumber(value) then
value = tonumber(value)
end
-- remove quotes
if type(value) == "string" then
---@diagnostic disable-next-line: no-unknown
value = value:gsub("^['\"]", ""):gsub("['\"]$", "")
end
---@diagnostic disable-next-line: no-unknown
opts[key] = value
end
return source, opts
end

---@param line string
---@param col number
function M.complete(_, line, col)
line = line:sub(1, col)
local candidates = {} ---@type string[]
local prefix = ""
local source = line:match("Trouble%s+(%S*)$")
if source then
prefix = source
candidates = Config.modes()
else
local args = line:match("Trouble%s+%S+%s*.*%s+(%S*)$")
if args then
prefix = args
candidates = vim.tbl_keys(Config.get())
candidates = vim.tbl_map(function(x)
return x .. "="
end, candidates)
end
end

candidates = vim.tbl_filter(function(x)
return tostring(x):find(prefix, 1, true) ~= nil
end, candidates)
table.sort(candidates)
return candidates
end

function M.execute(input)
local mode, opts = M.parse(input.args)
if mode and opts then
opts.mode = mode
require("trouble").open(opts)
end
end

return M

0 comments on commit e3b0114

Please sign in to comment.