diff --git a/lib/gears/init.lua b/lib/gears/init.lua index 6f5198d4..4f5e83f6 100644 --- a/lib/gears/init.lua +++ b/lib/gears/init.lua @@ -17,6 +17,7 @@ return cache = require("gears.cache"); matrix = require("gears.matrix"); shape = require("gears.shape"); + protected_call = require("gears.protected_call"); } -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/lib/gears/protected_call.lua b/lib/gears/protected_call.lua new file mode 100644 index 00000000..41f9dfa7 --- /dev/null +++ b/lib/gears/protected_call.lua @@ -0,0 +1,58 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2016 Uli Schlachter +-- @release @AWESOME_VERSION@ +-- @module gears.protected_call +--------------------------------------------------------------------------- + +local gdebug = require("gears.debug") +local tostring = tostring +local traceback = debug.traceback +local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility with Lua 5.1) +local xpcall = xpcall + +local protected_call = {} + +local function error_handler(err) + gdebug.print_error(traceback("Error during a protected call: " .. tostring(err))) +end + +local function handle_result(success, ...) + if success then + return ... + end +end + +local do_pcall +if _VERSION <= "Lua 5.1" then + -- Lua 5.1 doesn't support arguments in xpcall :-( + do_pcall = function(func, ...) + local args = { ... } + return handle_result(xpcall(function() + return func(unpack(args)) + end, error_handler)) + end +else + do_pcall = function(func, ...) + return handle_result(xpcall(func, error_handler, ...)) + end +end + +--- Call a function in protected mode and handle error-reporting. +-- If the function call succeeds, all results of the function are returned. +-- Otherwise, an error message is printed and nothing is returned. +-- @tparam function func The function to call +-- @param ... Arguments to the function +-- @return The result of the given function, or nothing if an error occurred. +function protected_call.call(func, ...) + return do_pcall(func, ...) +end + +local pcall_mt = {} +function pcall_mt:__call(...) + return do_pcall(...) +end + +return setmetatable(protected_call, pcall_mt) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/spec/gears/protected_call_spec.lua b/spec/gears/protected_call_spec.lua new file mode 100644 index 00000000..602f9bd9 --- /dev/null +++ b/spec/gears/protected_call_spec.lua @@ -0,0 +1,43 @@ +--------------------------------------------------------------------------- +-- @author Uli Schlachter +-- @copyright 2016 Uli Schlachter +--------------------------------------------------------------------------- + +local gdebug = require("gears.debug") +local protected_call = require("gears.protected_call") + +describe("gears.protected_call", function() + -- Stop the error reporting during tests + local orig_print_error = gdebug.print_error + local errors + before_each(function() + errors = {} + gdebug.print_error = function(err) + table.insert(errors, err) + end + end) + after_each(function() + gdebug.print_error = orig_print_error + end) + + it("Call with arguments and result", function() + local called = false + local function f(...) + called = true + assert.is_same({ ... }, { 1, "second" }) + return "first", 2 + end + local results = { protected_call(f, 1, "second") } + assert.is_true(called) + assert.is_same({ "first", 2 }, results) + assert.is_same(errors, {}) + end) + + it("Call with error", function() + assert.is_same({}, { protected_call(error, "I was called") }) + assert.is_same(#errors, 1) + assert.is_truthy(string.find(errors[1], "I was called")) + end) +end) + +-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80