{-# LANGUAGE NoMonomorphismRestriction #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -Wno-orphans #-} -- |Debug utilities module Ide.Plugin.Eval.Util ( asS, timed, isLiterate, handleMaybe, handleMaybeM, response, response', gStrictTry, logWith, ) where import Control.Monad.Extra (maybeM) import Control.Monad.IO.Class (MonadIO (liftIO)) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Except (ExceptT (..), runExceptT, throwE) import Data.Aeson (Value (Null)) import Data.Bifunctor (first) import Data.String (IsString (fromString)) import qualified Data.Text as T import Development.IDE (IdeState, Priority (..), ideLogger, logPriority) import Development.IDE.GHC.Compat (gcatch) import Exception (ExceptionMonad, SomeException (..), evaluate) import GHC.Exts (toList) import GHC.Stack (HasCallStack, callStack, srcLocFile, srcLocStartCol, srcLocStartLine) import Language.LSP.Server import Language.LSP.Types import Outputable (Outputable (ppr), ppr, showSDocUnsafe) import System.FilePath (takeExtension) import System.Time.Extra (duration, showDuration) import UnliftIO.Exception (catchAny) asS :: Outputable a => a -> String asS = showSDocUnsafe . ppr timed :: MonadIO m => (t -> String -> m a) -> t -> m b -> m b timed out name op = do (secs, r) <- duration op _ <- out name (showDuration secs) return r -- |Log using hie logger, reports source position of logging statement logWith :: (HasCallStack, MonadIO m, Show a1, Show a2) => IdeState -> a1 -> a2 -> m () logWith state key val = liftIO . logPriority (ideLogger state) logLevel $ T.unwords [T.pack logWithPos, asT key, asT val] where logWithPos = let stk = toList callStack pr pos = concat [srcLocFile pos, ":", show . srcLocStartLine $ pos, ":", show . srcLocStartCol $ pos] in if null stk then "" else pr . snd . head $ stk asT :: Show a => a -> T.Text asT = T.pack . show -- | Set to Info to see extensive debug info in hie log, set to Debug in production logLevel :: Priority logLevel = Debug -- Info isLiterate :: FilePath -> Bool isLiterate x = takeExtension x `elem` [".lhs", ".lhs-boot"] handleMaybe :: Monad m => e -> Maybe b -> ExceptT e m b handleMaybe msg = maybe (throwE msg) return handleMaybeM :: Monad m => e -> m (Maybe b) -> ExceptT e m b handleMaybeM msg act = maybeM (throwE msg) return $ lift act response :: Functor f => ExceptT String f c -> f (Either ResponseError c) response = fmap (first (\msg -> ResponseError InternalError (fromString msg) Nothing)) . runExceptT response' :: ExceptT String (LspM c) WorkspaceEdit -> LspM c (Either ResponseError Value) response' act = do res <- runExceptT act `catchAny` showErr case res of Left e -> return $ Left (ResponseError InternalError (fromString e) Nothing) Right a -> do _ <- sendRequest SWorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing a) (\_ -> pure ()) return $ Right Null gStrictTry :: ExceptionMonad m => m b -> m (Either String b) gStrictTry op = gcatch (op >>= fmap Right . gevaluate) showErr gevaluate :: MonadIO m => a -> m a gevaluate = liftIO . evaluate showErr :: Monad m => SomeException -> m (Either String b) showErr = return . Left . show