packages feed

reflex-ghci-0.2.0.0: src/Reflex/Vty/GHCi.hs

{-|
 - Module: Reflex.Vty.GHCi
 - Description: Vty widgets useful when building your own GHCi runner
-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecursiveDo #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
module Reflex.Vty.GHCi where

import Control.Monad
import qualified Data.Map as Map
import Data.Maybe
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Graphics.Vty.Input as V
import Reflex.Network
import Reflex.Process.GHCi
import Reflex.Vty
import qualified System.Process as P
import qualified Reflex.Process.Repl as Repl

-- | The main reflex-ghci widget
run :: String -> Maybe String -> IO ()
run cmd expr = mainWidget $ initManager_ $ do
  tabNavigation
  exit' <- keyCombo (V.KChar 'c', [V.MCtrl])
  exit <- keyCombo (V.KChar 'x', [V.MCtrl])
  rec Repl _ finished started readyToExit <- ghciWatch (P.shell cmd) (Repl.unsafeCommand . T.encodeUtf8 . T.pack <$> expr) reload ((() <$ exit) <> (() <$ exit') <> quit)
      oldCommands <- foldDyn ($) Map.empty $
        (\new old -> Map.fromList $ take 3 $ reverse $ Map.toList $ Map.union old new) <$> finished
      command <- holdDyn Nothing $ fmap Just $ leftmost
        [ fforMaybe (updated started) $ \(ix, x) -> (ix,) <$> x
        , fmapMaybe id $ fmap fst . Map.maxViewWithKey <$> finished
        ]
      let atPrompt = ffor started $ isNothing . snd
          errors = maybe False (hasErrors . snd) <$> command
      (reload, quit) <- col $ do
        r <- tile (fixed 3) $ row $ do
          grout flex $ boxStatic def $ text $ (\x -> if x then "Ready" else "Busy") <$> current atPrompt
          void $ networkView $ ffor errors $ \case
            True -> grout flex $ boxStatic def $ text "Error!"
            False -> grout flex $ boxStatic def $ text "All Good!"
          r <- tile flex $ button def $ text "Reload"
          q <- tile flex $ button def $ text "Quit"
          pure (r, q)
        let cmdbtn ix c = tile (fixed 3) $ button def $ text $ pure $
              T.pack (show ix) <> ": " <> (T.decodeUtf8 $ displayCommand $ _cmd_stdin c)
        oldE <- switchHold never <=< fmap (fmap leftmost) $ networkView $ ffor oldCommands $ \old -> forM (Map.toList old) $ \(ix, c) -> do
          go <- cmdbtn ix c
          pure $ ix <$ go
        currentCommand <- switchHold never <=< networkView $ ffor started $ \case
          (_, Nothing) -> pure never
          (ix, Just c) -> cmdbtn ix c

        let showOutput (Cmd _ out err) = do
              _ <- tile flex $ boxStatic def $ scrollableText never $ pure $ T.decodeUtf8 . unLines $ out
              _ <- tile flex $ boxStatic def $ scrollableText never $ pure $ T.decodeUtf8 . unLines $ err
              pure ()

        void $ networkHold (void $ networkView $ maybe blank (showOutput . snd) <$> command) $ leftmost
          [ ffor (attachWithMaybe (flip Map.lookup) (current oldCommands) oldE) showOutput
          , ffor (attachWithMaybe (\a _ -> fmap snd a) (current command) currentCommand) showOutput
          ]
        pure r
  return $ () <$ readyToExit