1
mirror of https://github.com/neovim/neovim.git synced 2024-12-28 14:31:13 -07:00
neovim/runtime/lua/coxpcall.lua
bfredl 25e51d393a build(lua): vendor coxpcall
Do not require luarocks on PUC lua CI just because of this single lua file
2023-09-10 11:43:37 +02:00

109 lines
3.5 KiB
Lua

-------------------------------------------------------------------------------
-- Coroutine safe xpcall and pcall versions
--
-- Encapsulates the protected calls with a coroutine based loop, so errors can
-- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines
-- yielding inside the call to pcall or xpcall.
--
-- Authors: Roberto Ierusalimschy and Andre Carregal
-- Contributors: Thomas Harning Jr., Ignacio Burgueño, Fabio Mascarenhas
--
-- Copyright 2005 - Kepler Project
--
-- $Id: coxpcall.lua,v 1.13 2008/05/19 19:20:02 mascarenhas Exp $
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Checks if (x)pcall function is coroutine safe
-------------------------------------------------------------------------------
local function isCoroutineSafe(func)
local co = coroutine.create(function()
return func(coroutine.yield, function() end)
end)
coroutine.resume(co)
return coroutine.resume(co)
end
-- No need to do anything if pcall and xpcall are already safe.
if isCoroutineSafe(pcall) and isCoroutineSafe(xpcall) then
copcall = pcall
coxpcall = xpcall
return { pcall = pcall, xpcall = xpcall, running = coroutine.running }
end
-------------------------------------------------------------------------------
-- Implements xpcall with coroutines
-------------------------------------------------------------------------------
local performResume, handleReturnValue
local oldpcall, oldxpcall = pcall, xpcall
local pack = table.pack or function(...) return {n = select("#", ...), ...} end
local unpack = table.unpack or unpack
local running = coroutine.running
local coromap = setmetatable({}, { __mode = "k" })
function handleReturnValue(err, co, status, ...)
if not status then
return false, err(debug.traceback(co, (...)), ...)
end
if coroutine.status(co) == 'suspended' then
return performResume(err, co, coroutine.yield(...))
else
return true, ...
end
end
function performResume(err, co, ...)
return handleReturnValue(err, co, coroutine.resume(co, ...))
end
local function id(trace, ...)
return trace
end
function coxpcall(f, err, ...)
local current = running()
if not current then
if err == id then
return oldpcall(f, ...)
else
if select("#", ...) > 0 then
local oldf, params = f, pack(...)
f = function() return oldf(unpack(params, 1, params.n)) end
end
return oldxpcall(f, err)
end
else
local res, co = oldpcall(coroutine.create, f)
if not res then
local newf = function(...) return f(...) end
co = coroutine.create(newf)
end
coromap[co] = current
return performResume(err, co, ...)
end
end
local function corunning(coro)
if coro ~= nil then
assert(type(coro)=="thread", "Bad argument; expected thread, got: "..type(coro))
else
coro = running()
end
while coromap[coro] do
coro = coromap[coro]
end
if coro == "mainthread" then return nil end
return coro
end
-------------------------------------------------------------------------------
-- Implements pcall with coroutines
-------------------------------------------------------------------------------
function copcall(f, ...)
return coxpcall(f, id, ...)
end
return { pcall = copcall, xpcall = coxpcall, running = corunning }