{-# LANGUAGE OverloadedStrings #-} module Agda.Misc where import Agda (runTCMPrettyErrors) import Agda.Interaction.Base (CommandM, CommandQueue (..), CommandState (optionsOnReload), Rewrite (AsIs), initCommandState) import Agda.Interaction.BasicOps (atTopLevel, typeInCurrent) import qualified Agda.Interaction.Imports as Imp import Agda.Interaction.InteractionTop (cmd_load', localStateCommandM) import Agda.Interaction.Options (CommandLineOptions (optAbsoluteIncludePaths)) import qualified Agda.Parser as Parser import Agda.Position (makeOffsetTable, toPositionWithoutFile) import Agda.Syntax.Abstract.Pretty (prettyATop) import Agda.Syntax.Parser (exprParser, parse) import Agda.Syntax.Translation.ConcreteToAbstract (concreteToAbstract_) import Agda.TypeChecking.Monad (HasOptions (commandLineOptions), setInteractionOutputCallback) import Agda.TypeChecking.Warnings (runPM) import Agda.Utils.Pretty (render) import Control.Concurrent.STM import Control.Monad.Reader import Control.Monad.State import Data.Text (Text, pack, unpack) import Language.LSP.Server (LspM) import qualified Language.LSP.Server as LSP import qualified Language.LSP.Types as LSP import qualified Language.LSP.VFS as VFS import Monad (ServerM) initialiseCommandQueue :: IO CommandQueue initialiseCommandQueue = CommandQueue <$> newTChanIO <*> newTVarIO Nothing runCommandM :: CommandM a -> IO (Either String a) runCommandM program = runTCMPrettyErrors $ do -- we need to set InteractionOutputCallback else it would panic setInteractionOutputCallback $ \response -> return () -- setup the command state commandQueue <- liftIO initialiseCommandQueue opts <- commandLineOptions let commandState = (initCommandState commandQueue) {optionsOnReload = opts {optAbsoluteIncludePaths = []}} evalStateT program commandState inferTypeOfText :: FilePath -> Text -> ServerM (LspM ()) (Either String String) inferTypeOfText filepath text = liftIO $ runCommandM $ do -- load first cmd_load' filepath [] True Imp.TypeCheck $ \_ -> return () -- infer later let norm = AsIs -- localStateCommandM: restore TC state afterwards, do we need this here? typ <- localStateCommandM $ do e <- lift $ runPM $ parse exprParser (unpack text) lift $ atTopLevel $ do concreteToAbstract_ e >>= typeInCurrent norm render <$> prettyATop typ onHover :: LSP.Uri -> LSP.Position -> ServerM (LspM ()) (Maybe LSP.Hover) onHover uri pos = do result <- LSP.getVirtualFile (LSP.toNormalizedUri uri) case result of Nothing -> return Nothing Just file -> do let source = VFS.virtualFileText file let offsetTable = makeOffsetTable source let agdaPos = toPositionWithoutFile offsetTable pos lookupResult <- Parser.tokenAt uri source agdaPos case lookupResult of Nothing -> return Nothing Just (token, text) -> do case LSP.uriToFilePath uri of Nothing -> return Nothing Just filepath -> do let range = LSP.Range pos pos inferResult <- inferTypeOfText filepath text case inferResult of Left error -> do let content = LSP.HoverContents $ LSP.markedUpContent "agda-language-server" ("Error: " <> pack error) return $ Just $ LSP.Hover content (Just range) Right typeString -> do let content = LSP.HoverContents $ LSP.markedUpContent "agda-language-server" (pack typeString) return $ Just $ LSP.Hover content (Just range)