nvim-hs
nvim-hs copied to clipboard
Improve feedback when plugin code throws exception
@isovector wrote in #95 :
One side effect of this patch is that unhandled exceptions no longer end up in the log --- plugins just mysteriously stop working.
Is this actually true? I've written and incredibly useful plugin below and I receive log messages and I see error messages in neovim.
When calling the command :ThrowExceptionCommand:
[Socket Reader : DEBUG] Received: ObjectArray [ObjectInt 0,ObjectInt 2,ObjectString "ThrowExceptionCommand:command",ObjectArray [ObjectArray [],ObjectInt 0]]
[Socket Reader : DEBUG] Executing stateful function with ID: Just 2
[EventHandler : DEBUG] Sending: Response 2 (Left (ObjectBinary "ErrorResult throwExceptionFunction (ObjectArray [ObjectInt 1,ObjectBinary \"Simulate neovim exception\"])"))

When calling the function with :call ThrowExceptionFunction():
[Socket Reader : DEBUG] Received: ObjectArray [ObjectInt 2,ObjectString "ThrowExceptionFunction:function",ObjectArray [ObjectArray []]]
[Socket Reader : DEBUG] Executing stateful function with ID: Nothing
[EventHandler : DEBUG] Sending: Request (Request {reqMethod = F "nvim_err_writeln", reqId = 9, reqArgs = [ObjectBinary "ErrorResult throwExceptionFunction (ObjectArray [ObjectInt 1,ObjectBinary \"Simulate neovim exception\"])"]})
[Socket Reader : DEBUG] Received: ObjectArray [ObjectInt 1,ObjectInt 9,ObjectNil,ObjectNil]

When an autocmd is executed:
[Socket Reader : DEBUG] Received: ObjectArray [ObjectInt 2,ObjectString "ThrowExceptionAutoCommand",ObjectArray []]
[Socket Reader : DEBUG] Received: ObjectArray [ObjectInt 2,ObjectString "ThrowExceptionAutoCommand",ObjectArray []]
[Socket Reader : DEBUG] Executing stateful function with ID: Nothing
[Socket Reader : DEBUG] Executing stateful function with ID: Nothing
[EventHandler : DEBUG] Sending: Request (Request {reqMethod = F "nvim_err_writeln", reqId = 8, reqArgs = [ObjectBinary "ErrorResult throwExceptionFunction (ObjectArray [ObjectInt 1,ObjectBinary \"Simulate neovim exception\"])"]})
[EventHandler : DEBUG] Sending: Request (Request {reqMethod = F "nvim_err_writeln", reqId = 8, reqArgs = [ObjectBinary "ErrorResult throwExceptionFunction (ObjectArray [ObjectInt 1,ObjectBinary \"Simulate neovim exception\"])"]})

{-# LANGUAGE OverloadedStrings #-}
module Neovim.Example.Plugin.Throws where
import Neovim
import UnliftIO (throwIO)
throwExceptionFunction :: Neovim env ()
throwExceptionFunction = throwIO $ ErrorResult
"throwExceptionFunction"
(toObject (1 :: Int32, "Simulate neovim exception" :: String))
throwExceptionCommand :: CommandArguments -> Neovim env ()
throwExceptionCommand _ = throwExceptionFunction
throwExceptionAutoCommand :: Neovim env ()
throwExceptionAutoCommand = throwExceptionFunction
{-# LANGUAGE TemplateHaskell, OverloadedStrings #-}
module Neovim.Example.Plugin ( plugin ) where
import Neovim.Example.Plugin.Throws (throwExceptionAutoCommand, throwExceptionCommand, throwExceptionFunction)
plugin :: Neovim () NeovimPlugin
plugin = do
wrapPlugin Plugin
{ environment = ()
, exports =
[ $(command' 'throwExceptionCommand) [CmdBang]
, $(function' 'throwExceptionFunction) Async
, $(autocmd 'throwExceptionAutoCommand) "FileType" Async def{acmdPattern = "vim"}
]
}
I experienced this issue when calling vim from an async thread! Maybe I am confused that it never used to report exceptions in async threads?
I can put together an mvp later today
The following code swallows exceptions and if the second to last line is not commented, and exception is visible.
neovimAsync :: (MonadUnliftIO m) => m a -> m (Async a)
neovimAsync m =
withRunInIO $ \lower ->
liftIO $ async $ lower m
throwExceptionFunctionAsync :: Neovim env Bool
throwExceptionFunctionAsync = do
spawned <- neovimAsync $ throwExceptionFunction
-- liftIO $ waitAnyCancel [spawned]
pure True
I barely ever program in Haskell, but to me it seems that this is kind of expected behavior from the async library?
Yeah, might be. Perhaps it's worth adding an async helper to the library that deals with this --- rather than doing the whole withRunInIO dance. Would you be interested in a PR?
Sure!
Or maybe just use: https://hackage.haskell.org/package/unliftio-0.2.20.1/docs/UnliftIO-Async.html It's already a dependency and used internally.