packages feed

zwirn-0.2.2.0: src/zwirn-lang/Zwirn/Language/LSP/InlayHints.hs

module Zwirn.Language.LSP.InlayHints where

import Control.Concurrent (readMVar)
import Control.Monad.RWS (gets, liftIO)
import qualified Data.Map as Map
import Data.Maybe (catMaybes, mapMaybe)
import Data.Text as T (Text, filter, unpack)
import Zwirn.Language (Syntax (..), Term (..), catchMany, parseBlock)
import Zwirn.Language.Compiler (CI, CompilerOutput (..), Environment (..), filterErrors, noSolo, runBlocks, runCommand)
import Zwirn.Language.Location (Located (..), Position (..), RealSrcLoc (..), SrcLoc (..))
import Zwirn.Stream.Target (Targeted (..))
import Zwirn.Stream.Types (Identifier (..), PlayMap, PlayState (..), Stream (..))

data Hint
  = Hint
  { hPos :: Position,
    hContent :: Text,
    hDescription :: Text
  }
  deriving (Show)

getHints :: Text -> CI [Hint]
getHints doc = do
  blocks <- runBlocks 0 doc
  ss <- concat <$> catchMany (map parseBlock blocks)
  sh <- getStreamHints ss
  ch <- getCommandHints ss
  return $ sh ++ ch

-- | inlay hints above stream actions of the form `id &: exp`, for displaying the play state of the according expression
getStreamHints :: [Syntax] -> CI [Hint]
getStreamHints ss = do
  pm <- gets (sPlayMap . tStream) >>= liftIO . readMVar
  let chans = map findStreamPattern ss
  return $ mapMaybe (resolvePlayState pm) chans

-- | returns the channel key and a positon one line above it
findStreamPattern :: Syntax -> Maybe (Identifier, Position)
findStreamPattern (Exec (Located (SrcLoc (RealSrcLoc _ ln ch _ _)) (TInfix (Located _ (TNum chan)) (Located _ "($:)") _))) = if ln <= 1 then Nothing else Just (NumID (read $ unpack chan), Position (ln - 1) ch)
findStreamPattern (Exec (Located (SrcLoc (RealSrcLoc _ ln ch _ _)) (TInfix (Located _ (TText chan)) (Located _ "($:)") _))) = if ln <= 1 then Nothing else Just (TextID $ stripText chan, Position (ln - 1) ch)
  where
    stripText = T.filter (/= '\"')
findStreamPattern _ = Nothing

lookupPlayState :: Identifier -> PlayMap -> Maybe PlayState
lookupPlayState key pm = (\(Targeted _ (x, _, _)) -> x) <$> Map.lookup key pm

resolvePlayState :: PlayMap -> Maybe (Identifier, Position) -> Maybe Hint
resolvePlayState pm mx
  | noSolo pm = do
      (key, pos) <- mx
      ps <- lookupPlayState key pm
      case ps of
        Solo -> return $ Hint pos "(solo)" "stream hint"
        Mute -> return $ Hint pos "(muted)" "stream hint"
        Normal -> return $ Hint pos "(playing)" "stream hint"
  | otherwise = do
      (key, pos) <- mx
      ps <- lookupPlayState key pm
      case ps of
        Solo -> return $ Hint pos "(solo)" "stream hint"
        Mute -> return $ Hint pos "(muted)" "stream hint"
        Normal -> return $ Hint pos "(not soloed)" "stream hint"

-- | inlay hints underneath commands to display their output
getCommandHints :: [Syntax] -> CI [Hint]
getCommandHints ss = catMaybes <$> filterErrors (map commandHint ss)

commandHint :: Syntax -> CI (Maybe Hint)
commandHint (Command (Located (SrcLoc (RealSrcLoc _ _ _ ln ch)) x)) = do
  out <- runCommand x
  case out of
    OutMessage ms -> return $ Just $ Hint (Position ln (ch + 1)) ("\n" <> ms) "command hint"
    _ -> return Nothing
commandHint _ = return Nothing