hls-eval-plugin-2.4.0.0: src/Ide/Plugin/Eval/Util.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-orphans -Wno-unused-imports #-}
-- |Debug utilities
module Ide.Plugin.Eval.Util (
timed,
isLiterate,
response',
gStrictTry,
logWith,
) where
import Control.Exception (SomeException, evaluate,
fromException)
import Control.Monad.Error.Class (MonadError (throwError))
import Control.Monad.IO.Class (MonadIO (liftIO))
import Control.Monad.Trans.Class (MonadTrans (lift))
import Control.Monad.Trans.Except (ExceptT (..),
runExceptT)
import Data.Aeson (Value)
import Data.Bifunctor (second)
import Data.String (IsString (fromString))
import qualified Data.Text as T
import Development.IDE (IdeState, Priority (..),
ideLogger, logPriority)
import qualified Development.IDE.Core.PluginUtils as PluginUtils
import Development.IDE.GHC.Compat.Outputable
import Development.IDE.GHC.Compat.Util (MonadCatch, bagToList,
catch)
import GHC.Exts (toList)
import GHC.Stack (HasCallStack, callStack,
srcLocFile,
srcLocStartCol,
srcLocStartLine)
import Ide.Plugin.Error
import Language.LSP.Protocol.Message
import Language.LSP.Protocol.Types
import Language.LSP.Server
import System.FilePath (takeExtension)
import System.Time.Extra (duration, showDuration)
import UnliftIO.Exception (catchAny)
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 case stk of
[] -> ""
(x:_) -> pr $ snd x
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"]
response' :: ExceptT PluginError (LspM c) WorkspaceEdit -> ExceptT PluginError (LspM c) (Value |? Null)
response' act = do
res <- ExceptT (runExceptT act
`catchAny` \e -> do
res <- showErr e
pure . Left . PluginInternalError $ fromString res)
_ <- lift $ sendRequest SMethod_WorkspaceApplyEdit (ApplyWorkspaceEditParams Nothing res) (\_ -> pure ())
pure $ InR Null
gStrictTry :: (MonadIO m, MonadCatch m) => m b -> m (Either String b)
gStrictTry op =
catch
(op >>= fmap Right . gevaluate)
(fmap Left . showErr)
gevaluate :: MonadIO m => a -> m a
gevaluate = liftIO . evaluate
showErr :: Monad m => SomeException -> m String
showErr e =
#if MIN_VERSION_ghc(9,3,0)
case fromException e of
-- On GHC 9.4+, the show instance adds the error message span
-- We don't want this for the plugin
-- So render without the span.
Just (SourceError msgs) -> return $ renderWithContext defaultSDocContext
$ vcat
$ bagToList
$ fmap (vcat . unDecorated
. diagnosticMessage
#if MIN_VERSION_ghc(9,5,0)
(defaultDiagnosticOpts @GhcMessage)
#endif
. errMsgDiagnostic)
$ getMessages msgs
_ ->
#endif
return . show $ e