1

Regenerate nvim config

This commit is contained in:
2024-06-02 03:29:20 +02:00
parent 75eea0c030
commit ef2e28883d
5576 changed files with 604886 additions and 503 deletions

View File

@ -0,0 +1,98 @@
local promise = require('promise')
local utils = require('promise-async.utils')
local compat = require('promise-async.compat')
local asyncId = {'promise-async'}
---@class Async
local Async = setmetatable({_id = asyncId}, {
__call = function(self, executor)
return self.sync(executor)
end
})
local packedId = {}
local Packed = {_id = packedId}
Packed.__index = Packed
local function wrapPacked(packed)
return setmetatable(packed, Packed)
end
local function hasPacked(o)
return type(o) == 'table' and o._id == packedId
end
local function injectENV(fn)
compat.setfenv(fn, setmetatable({
await = Async.wait,
pcall = compat.pcall,
xpcall = compat.xpcall
}, {
__index = compat.getfenv(fn)
}))
end
function Async.sync(executor)
local typ = type(executor)
local isCallable, fn = utils.getCallable(executor, typ)
assert(isCallable, 'a callable table or function expected, got ' .. typ)
injectENV(fn)
return promise:new(function(resolve, reject)
local co = coroutine.create(typ == 'function' and executor or function()
return executor()
end)
local function afterResume(status, ...)
if not status then
local reason = select(1, ...)
reject(debug.traceback(co, reason))
return
elseif coroutine.status(co) == 'dead' then
local value
local n = select('#', ...)
if n == 1 then
value = select(1, ...)
elseif n > 1 then
value = wrapPacked({...})
end
resolve(value)
return
end
local p = select(1, ...)
return p
end
local function next(err, res)
local p = afterResume(coroutine.resume(co, err, res))
if p then
p:thenCall(function(value)
next(false, value)
end, function(reason)
next(true, reason)
end)
end
end
next()
end)
end
---Export wait function to someone needs, wait function actually have injected as `await` into
---the executor of async function
---@param p Promise|any
---@return ...
function Async.wait(p)
p = promise.resolve(p)
local err, res = coroutine.yield(p)
if err then
error(res, 0)
elseif hasPacked(res) then
return compat.unpack(res)
else
return res
end
end
return Async

View File

@ -0,0 +1,131 @@
---Functions are compatible with LuaJIT's.
---@class PromiseAsyncCompat
local M = {}
---@return boolean
function M.is51()
return _G._VERSION:sub(-3) == '5.1' and not jit
end
if table.pack then
M.pack = table.pack
else
M.pack = function(...)
return {n = select('#', ...), ...}
end
end
if table.unpack then
M.unpack = table.unpack
else
M.unpack = unpack
end
---@diagnostic enable: deprecated
if M.is51() then
local _pcall, _xpcall = pcall, xpcall
local utils = require('promise-async.utils')
local function yieldInCoroutine(thread, co, success, ...)
if coroutine.status(co) == 'suspended' then
return yieldInCoroutine(thread, co, coroutine.resume(co, coroutine.yield(...)))
end
return success, ...
end
local function doPcall(thread, f, ...)
local typ = type(f)
local ok, fn = utils.getCallable(f, typ)
if not ok then
return false, ('attempt to call a %s value'):format(typ)
end
local co = coroutine.create(function(...)
return fn(...)
end)
return yieldInCoroutine(thread, co, coroutine.resume(co, ...))
end
M.pcall = function(f, ...)
local thread = coroutine.running()
if not thread then
return _pcall(f, ...)
end
return doPcall(thread, f, ...)
end
local function xpcallCatch(msgh, success, ...)
if success then
return true, ...
end
local ok, result = _pcall(msgh, ...)
return false, ok and result or 'error in error handling'
end
M.xpcall = function(f, msgh, ...)
local thread = coroutine.running()
if not thread then
return _xpcall(f, msgh, ...)
end
return xpcallCatch(msgh, doPcall(thread, f, ...))
end
else
M.pcall = pcall
M.xpcall = xpcall
end
if setfenv then
M.setfenv = setfenv
M.getfenv = getfenv
else
local function findENV(f)
local name = ''
local value
local up = 1
while name do
name, value = debug.getupvalue(f, up)
if name == '_ENV' then
return up, value
end
up = up + 1;
end
return 0
end
local function envHelper(f, name)
if type(f) == 'number' then
if f < 0 then
error(([[bad argument #1 to '%s' (level must be non-negative)]]):format(name), 3)
end
local ok, dInfo = pcall(debug.getinfo, f + 2, 'f')
if not ok or not dInfo then
error(([[bad argument #1 to '%s' (invalid level)]]):format(name), 3)
end
f = dInfo.func
elseif type(f) ~= 'function' then
error(([[bad argument #1 to '%s' (number expected, got %s)]]):format(name, type(f)), 3)
end
return f
end
function M.setfenv(f, table)
f = envHelper(f, 'setfenv')
local up = findENV(f)
if up > 0 then
debug.upvaluejoin(f, up, function()
return table
end, 1)
end
return f
end
function M.getfenv(f)
if f == 0 or f == nil then
return _G
end
f = envHelper(f, 'getfenv')
local up, value = findENV(f)
return up > 0 and value or _G
end
end
return M

View File

@ -0,0 +1,115 @@
local errorId = {}
---@class PromiseAsyncError
---@field err any
---@field queue string[]
---@field index number
local Error = {_id = errorId}
Error.__index = Error
local function dump(o, limit)
local s
if type(o) ~= 'table' then
s = tostring(o)
else
local meta = getmetatable(o)
if meta and meta.__tostring then
s = tostring(o)
else
if limit > 0 then
local fmt = '%s [%s] = %s,'
s = '{'
for k, v in pairs(o) do
if type(k) ~= 'number' then
k = '"' .. k .. '"'
end
s = fmt:format(s, k, dump(v, limit - 1))
end
s = s:sub(1, #s - 1) .. ' }'
else
s = '{...}'
end
end
end
return s
end
function Error.isInstance(o)
return type(o) == 'table' and o._id == errorId
end
---@param thread? thread
---@param level number
---@param skipShortSrc? string
---@return string?
function Error.format(thread, level, skipShortSrc)
local res
local dInfo = thread and debug.getinfo(thread, level, 'nSl') or debug.getinfo(level, 'nSl')
if dInfo then
local name, shortSrc, currentline = dInfo.name, dInfo.short_src, dInfo.currentline
if skipShortSrc == shortSrc then
return
end
local detail
if not name or name == '' then
detail = ('in function <Anonymous:%d>'):format(dInfo.linedefined)
else
detail = ([[in function '%s']]):format(name)
end
res = (' %s:%d: %s'):format(shortSrc, currentline, detail)
end
return res
end
---@param err any
---@return PromiseAsyncError
function Error.new(err)
local o = setmetatable({}, Error)
o.err = err
o.queue = {}
o.index = 0
return o
end
function Error:__tostring()
local errMsg = dump(self.err, 1)
if #self.queue == 0 then
return errMsg
end
local t = {}
for i = 1, self.index do
table.insert(t, self.queue[i])
end
table.insert(t, errMsg)
if self.index < #self.queue then
table.insert(t, 'stack traceback:')
end
for i = self.index + 1, #self.queue do
table.insert(t, self.queue[i])
end
return table.concat(t, '\n')
end
---@param value string
function Error:unshift(value)
if value then
self.index = self.index + 1
table.insert(self.queue, 1, value)
end
return #self.queue
end
---@param value? string
function Error:push(value)
if value then
table.insert(self.queue, value)
end
return #self.queue
end
---@return any
function Error:peek()
return self.err
end
return Error

View File

@ -0,0 +1,85 @@
local uv = require('luv')
---@class PromiseAsyncLoop
---@field tick userdata
---@field tickCallbacks function[]
---@field tickStarted boolean
---@field idle userdata
---@field idleCallbacks function[]
---@field idleStarted boolean
local EventLoop = {
tick = uv.new_timer(),
tickCallbacks = {},
tickStarted = false,
idle = uv.new_idle(),
idleCallbacks = {},
idleStarted = false
}
function EventLoop.setTimeout(callback, ms)
local timer = uv.new_timer()
timer:start(ms, 0, function()
timer:close()
EventLoop.callWrapper(callback)
end)
return timer
end
local function runTick()
EventLoop.tickStarted = true
local callbacks = EventLoop.tickCallbacks
EventLoop.tickCallbacks = {}
for _, cb in ipairs(callbacks) do
EventLoop.callWrapper(cb)
end
if #EventLoop.tickCallbacks > 0 then
EventLoop.tick:start(0, 0, runTick)
else
EventLoop.tickStarted = false
end
end
function EventLoop.nextTick(callback)
table.insert(EventLoop.tickCallbacks, callback)
if not EventLoop.tickStarted then
EventLoop.tick:start(0, 0, runTick)
end
end
local function runIdle()
EventLoop.idleStarted = true
local callbacks = EventLoop.idleCallbacks
EventLoop.idleCallbacks = {}
for _, cb in ipairs(callbacks) do
EventLoop.callWrapper(cb)
end
if #EventLoop.idleCallbacks > 0 then
EventLoop.idle:start(runIdle)
else
EventLoop.idle:stop()
EventLoop.idleStarted = false
end
end
function EventLoop.nextIdle(callback)
EventLoop.nextTick(function()
table.insert(EventLoop.idleCallbacks, callback)
if not EventLoop.idleStarted then
EventLoop.idle:start(runIdle)
end
end)
end
if vim and type(vim.schedule) == 'function' then
EventLoop.callWrapper = vim.schedule
else
function EventLoop.callWrapper(fn)
local ok, res = pcall(fn)
if not ok then
-- luv can't handle object with __tostring filed
error(tostring(res))
end
end
end
return EventLoop

View File

@ -0,0 +1,33 @@
---@class PromiseAsyncUtils
local M = {}
---@param o any
---@param expectedType string
function M.assertType(o, expectedType)
local gotType = type(o)
local fmt = '%s expected, got %s'
return assert(gotType == expectedType, fmt:format(expectedType, gotType))
end
---@param o any
---@param typ? string
---@return boolean, function|table|any
function M.getCallable(o, typ)
local ok
local f
local t = typ or type(o)
if t == 'function' then
ok, f = true, o
elseif t ~= 'table' then
ok, f = false, o
else
local meta = getmetatable(o)
ok = meta and type(meta.__call) == 'function'
if ok then
f = meta.__call
end
end
return ok, f
end
return M

View File

@ -0,0 +1,410 @@
local utils = require('promise-async.utils')
local promiseId = {'promise-async'}
local errFactory = require('promise-async.error')
local shortSrc = debug.getinfo(1, 'S').short_src
---@alias PromiseState
---| 1 #PENDING
---| 2 #FULFILLED
---| 3 #REJECTED
local PENDING = 1
local FULFILLED = 2
local REJECTED = 3
--
---@class Promise
---@field state PromiseState
---@field result any
---@field queue table
---@field needHandleRejection? boolean
---@field err? PromiseAsyncError
local Promise = setmetatable({_id = promiseId}, {
__call = function(self, executor)
return self:new(executor)
end
})
Promise.__index = Promise
local function loadEventLoop()
local success, res = pcall(require, 'promise-async.loop')
assert(success, 'Promise need an EventLoop, ' ..
'luv module or a customized EventLoop module is expected.')
return res
end
if vim then
-- `require` in Neovim is hacked by its get_runtime API, may throw an error while calling
-- `require` in libuv, require at once as a workaround.
Promise.loop = require('promise-async.loop')
else
Promise.loop = setmetatable({}, {
__index = function(_, key)
local loop = loadEventLoop()
rawset(Promise, 'loop', loop)
return loop[key]
end,
__newindex = function(_, key, value)
local loop = loadEventLoop()
rawset(Promise, 'loop', loop)
Promise.loop[key] = value
end
})
end
function Promise:__tostring()
local state = self.state
if state == PENDING then
return 'Promise { <pending> }'
elseif state == REJECTED then
return ('Promise { <rejected> %s }'):format(tostring(self.result))
else
return ('Promise { <fulfilled> %s }'):format(tostring(self.result))
end
end
local function noop() end
---@param o any
---@param typ? string
---@return boolean
function Promise.isInstance(o, typ)
return (typ or type(o)) == 'table' and o._id == promiseId
end
---must get `thenCall` field from `o` at one time, can't call repeatedly.
---@param o any
---@param typ? type
---@return function?
local function getThenable(o, typ)
local thenCall
if (typ or type(o)) == 'table' then
thenCall = o.thenCall
if type(thenCall) ~= 'function' then
thenCall = nil
end
end
return thenCall
end
---@param err any
---@return PromiseAsyncError
local function buildError(err)
local o = errFactory.new(err)
local level = 4
local value
local thread = coroutine.running()
repeat
value = errFactory.format(thread, level, shortSrc)
level = level + 1
o:push(value)
until not value
table.remove(o.queue)
return o
end
local resolvePromise, rejectPromise
---@param promise Promise
local function handleQueue(promise)
local queue = promise.queue
if #queue == 0 then
return
end
if promise.needHandleRejection and #queue > 0 then
promise.needHandleRejection = nil
end
promise.queue = {}
Promise.loop.nextTick(function()
local state, result = promise.state, promise.result
for _, q in ipairs(queue) do
local newPromise, onFulfilled, onRejected = q[1], q[2], q[3]
local func
if state == FULFILLED then
if utils.getCallable(onFulfilled) then
func = onFulfilled
else
resolvePromise(newPromise, result)
end
elseif state == REJECTED then
if utils.getCallable(onRejected) then
func = onRejected
else
rejectPromise(newPromise, result)
end
end
if func then
local ok, res = xpcall(function()
return func(result)
end, function(errmsg)
if type(errmsg) == 'string' then
newPromise.err = buildError(errmsg)
end
return errmsg
end)
if ok then
resolvePromise(newPromise, res)
else
rejectPromise(newPromise, res)
end
end
end
end)
end
---@param promise Promise
---@param result any
---@param state PromiseState
local function transition(promise, result, state)
if promise.state ~= PENDING then
return
end
promise.result = result
promise.state = state
handleQueue(promise)
end
---@param promise Promise
---@param executor PromiseExecutor
---@param self? table
local function wrapExecutor(promise, executor, self)
local called = false
local resolve = function(value)
if called then
return
end
resolvePromise(promise, value)
called = true
end
local reject = function(reason)
if called then
return
end
rejectPromise(promise, reason)
called = true
end
local ok, res = xpcall(function()
if self then
---@diagnostic disable-next-line: redundant-parameter, param-type-mismatch
return executor(self, resolve, reject)
else
return executor(resolve, reject)
end
end, function(errmsg)
if type(errmsg) == 'string' then
promise.err = buildError(errmsg)
end
return errmsg
end)
if not ok and not called then
reject(res)
end
end
---@param promise Promise
local function handleRejection(promise)
promise.needHandleRejection = true
Promise.loop.nextIdle(function()
if promise.needHandleRejection then
promise.needHandleRejection = nil
local err = promise.err
if not err then
err = errFactory.new(promise.result)
end
err:unshift('UnhandledPromiseRejection with the reason:')
error(err)
end
end)
end
---@param promise Promise
---@param reason any
rejectPromise = function(promise, reason)
handleRejection(promise)
transition(promise, reason, REJECTED)
end
---@param promise Promise
---@param value any
resolvePromise = function(promise, value)
if promise == value then
local reason = debug.traceback('TypeError: Chaining cycle detected for promise')
rejectPromise(promise, reason)
return
end
local valueType = type(value)
if Promise.isInstance(value, valueType) then
value:thenCall(function(val)
resolvePromise(promise, val)
end, function(reason)
rejectPromise(promise, reason)
end)
else
local thenCall = getThenable(value, valueType)
if thenCall then
wrapExecutor(promise, thenCall, value)
else
transition(promise, value, FULFILLED)
end
end
end
function Promise:new(executor)
utils.assertType(executor, 'function')
local o = self == Promise and setmetatable({}, self) or self
o.state = PENDING
o.result = nil
o.queue = {}
o.needHandleRejection = nil
if executor ~= noop then
wrapExecutor(o, executor)
end
return o
end
function Promise:thenCall(onFulfilled, onRejected)
local o = self.new(Promise, noop)
table.insert(self.queue, {o, onFulfilled, onRejected})
if self.state ~= PENDING then
handleQueue(self)
end
return o
end
function Promise:catch(onRejected)
return self:thenCall(nil, onRejected)
end
function Promise:finally(onFinally)
local function wrapFinally()
if utils.getCallable(onFinally) then
---@diagnostic disable-next-line: need-check-nil
onFinally()
end
end
return self:thenCall(function(value)
wrapFinally()
return value
end, function(reason)
wrapFinally()
return Promise.reject(reason)
end)
end
function Promise.resolve(value)
local typ = type(value)
if Promise.isInstance(value, typ) then
return value
else
local o = Promise:new(noop)
local thenCall = getThenable(value, typ)
if thenCall then
wrapExecutor(o, thenCall, value)
else
o.state = FULFILLED
o.result = value
end
return o
end
end
function Promise.reject(reason)
local o = Promise:new(noop)
o.state = REJECTED
o.result = reason
handleRejection(o)
return o
end
function Promise.all(values)
utils.assertType(values, 'table')
return Promise:new(function(resolve, reject)
local res = {}
local cnt = 0
for k, v in pairs(values) do
cnt = cnt + 1
Promise.resolve(v):thenCall(function(value)
res[k] = value
cnt = cnt - 1
if cnt == 0 then
resolve(res)
end
end, function(reason)
reject(reason)
end)
end
if cnt == 0 then
resolve(res)
end
end)
end
function Promise.allSettled(values)
utils.assertType(values, 'table')
return Promise:new(function(resolve, reject)
local res = {}
local cnt = 0
local _ = reject
for k, v in pairs(values) do
cnt = cnt + 1
Promise.resolve(v):thenCall(function(value)
res[k] = {status = 'fulfilled', value = value}
end, function(reason)
res[k] = {status = 'rejected', reason = reason}
end):finally(function()
cnt = cnt - 1
if cnt == 0 then
resolve(res)
end
end)
end
if cnt == 0 then
resolve(res)
end
end)
end
function Promise.any(values)
utils.assertType(values, 'table')
return Promise:new(function(resolve, reject)
local cnt = 0
local function rejectAggregateError()
if cnt == 0 then
reject('AggregateError: All promises were rejected')
end
end
for _, p in pairs(values) do
cnt = cnt + 1
Promise.resolve(p):thenCall(function(value)
resolve(value)
end, function()
end):finally(function()
cnt = cnt - 1
rejectAggregateError()
end)
end
rejectAggregateError()
end)
end
function Promise.race(values)
utils.assertType(values, 'table')
return Promise:new(function(resolve, reject)
for _, p in pairs(values) do
Promise.resolve(p):thenCall(function(value)
resolve(value)
end, function(reason)
reject(reason)
end)
end
end)
end
return Promise