jet-0.0.0.0: src/Jet/Render.hs
{-# LANGUAGE OverloadedStrings #-}
module Jet.Render where
import Control.Lens
import Control.Monad.State
import qualified Data.List as List
import Data.Maybe
import qualified Graphics.Vty as Vty
import Prettyprinter
import Prettyprinter.Render.Util.StackMachine (renderSimplyDecoratedA)
data Cursor = Cursor
deriving (Show, Eq)
data Output = LineBreak | ImgChunk Vty.Image
renderScreen :: Int -> SimpleDocStream (Either Cursor Vty.Attr) -> [Vty.Image]
renderScreen winHeight doc = do
let (outputs, (_, _, mcursor)) =
doc
& toLineStream
& flip runState ([], 0, Nothing)
let allLines =
outputs & List.foldr collapse ([], [])
-- clean up anything remaining in the buffer
& ( \(buf, rest) ->
if null buf
then rest
else Vty.horizCat buf : rest
)
let cropped = case mcursor of
Nothing -> allLines
Just cursorPos -> takeSurroundingCursor winHeight cursorPos allLines
cropped
where
collapse out (buf, rest) =
case out of
LineBreak -> ([], Vty.horizCat buf : rest)
ImgChunk img -> (img : buf, rest)
-- Take only enough lines to fill the screen, with the cursor centered don't print any more.
takeSurroundingCursor :: Int -> Int -> [a] -> [a]
takeSurroundingCursor height cursorPos xs
| cursorPos - half > 0 =
xs
& drop (cursorPos - half)
& take height
| otherwise = take height xs
where
half = height `div` 2
toLineStream ::
SimpleDocStream (Either Cursor Vty.Attr) ->
State ([Vty.Attr], Int, Maybe Int) [Output]
toLineStream doc =
renderSimplyDecoratedA
renderText
pushAnn
popAnn
doc
where
popAnn _ = do
_1 %= drop 1
-- Need to clear existing colors or they bleed to the right.
pure [ImgChunk (Vty.text' Vty.defAttr "")]
pushAnn = \case
Left Cursor -> do
cursorLine <- use _2
_3 ?= cursorLine
pure []
Right ann -> do
_1 %= (ann :)
pure mempty
-- prettyprinter always renders lines as a single text fragment
renderText "\n" = do
_2 += 1
pure [LineBreak]
-- prettyprinter never passes text with newlines here
renderText txt = do
attr <- uses _1 (fromMaybe Vty.defAttr . listToMaybe)
pure [ImgChunk (Vty.text' attr txt)]