packages feed

ghcup-0.2.1.0: lib/GHCup/Compat/Pager.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}

module GHCup.Compat.Pager where

import GHCup.Compat.Terminal
import GHCup.System.Directory

import Control.Exception  ( IOException, try )
import Control.Monad      ( forM_, (<=<) )
import Data.Foldable      ( asum )
import Data.Text          ( Text )
import System.Environment
import System.Exit
import System.IO
import System.Process

import qualified Data.Text    as T
import qualified Data.Text.IO as T


getPager :: IO (Maybe FilePath)
getPager = do
  lookupEnv "GHCUP_PAGER" >>= \case
    Just r  -> pure $ Just r
    Nothing -> lookupEnv "PAGER" >>= \case
      Just r' -> pure $ Just r'
      Nothing ->
        let pagers = ["most", "more", "less"]
        in fmap (either (const Nothing) Just)
           . try @IOException
           . asum
           . fmap (maybe (fail "could not find") pure <=< findExecutable)
           $ pagers

-- 'more' reads from STDERR, and requires std_err to be 'Inherit'
sendToPager :: FilePath -> [Text] -> IO (Either IOException ())
sendToPager pager text = try @IOException
    $ withCreateProcess (shell pager) { std_in = CreatePipe
                                      , std_err = Inherit
                                      , std_out = Inherit
                                      , delegate_ctlc = True
                                      }
    $ \mStdinH _ _ ph -> case mStdinH of
          Just stdinH -> do
            forM_ text $ T.hPutStrLn stdinH
            hClose stdinH
            exitCode <- waitForProcess ph
            case exitCode of
              ExitFailure i -> fail ("Pager exited with exit code " <> show i)
              _             -> pure ()
          Nothing -> fail "well, I got nothing!"


sendToPager' :: Maybe FilePath -> [Text] -> IO ()
sendToPager' (Just pager) text = do
  fits <- fitsInTerminal text
  case fits of
    Just True -> do
      T.putStr $ T.unlines text
    _ -> sendToPager pager text >>= \case
      Right _ -> pure ()
      Left _ -> do
        T.putStrLn $ T.unlines text
sendToPager' _ text =
  forM_ text T.putStrLn