packages feed

elm-make-0.1: src/Display.hs

module Display where

import qualified Control.Concurrent.Chan as Chan
import Control.Monad (when)
import System.Exit (exitFailure)
import System.IO (hFlush, hPutStr, hPutStrLn, stderr, stdout)
import GHC.IO.Handle (hIsTerminalDevice)
import qualified Elm.Compiler.Module as Module
import qualified Elm.Package.Name as Pkg
import qualified Elm.Package.Paths as Path
import qualified Elm.Package.Version as V
import qualified Path
import TheMasterPlan (Location, ModuleID(ModuleID), PackageID)


data Update
    = Completion ModuleID
    | Success
    | Error ModuleID Location String


display :: Chan.Chan Update -> PackageID -> Int -> Int -> IO ()
display updatesChan rootPkg completeTasks totalTasks =
  do  isTerminal <- hIsTerminalDevice stdout
      loop isTerminal updatesChan rootPkg completeTasks totalTasks


loop :: Bool -> Chan.Chan Update -> PackageID -> Int -> Int -> IO ()
loop isTerminal updatesChan rootPkg completeTasks totalTasks =
  do  when isTerminal $
          do  hPutStr stdout (renderProgressBar completeTasks totalTasks)
              hFlush stdout

      update <- Chan.readChan updatesChan
      
      when isTerminal $
          hPutStr stdout clearProgressBar

      case update of
        Completion _moduleID ->
            loop isTerminal updatesChan rootPkg (completeTasks + 1) totalTasks

        Success ->
            case completeTasks of
              0 -> return ()
              1 -> putStrLn $ "Compiled 1 file"
              _ -> putStrLn $ "Compiled " ++ show completeTasks ++ " files"

        Error (ModuleID name pkg) location msg ->
            do  putStrLn ""
                hPutStrLn stderr (errorMessage rootPkg pkg name location msg)
                exitFailure


-- ERROR MESSAGE

errorMessage :: PackageID -> PackageID -> Module.Name -> Location -> String -> String
errorMessage rootPkg errorPkg@(pkgName, version) name location msg =
    "Error" ++ context ++ ":\n\n" ++ msg ++ report
  where
    isLocalError = errorPkg == rootPkg

    context
      | isLocalError = " in " ++ Path.toSource location
      | otherwise =
          " in package " ++ Pkg.toString pkgName ++ " " ++ V.toString version
          ++ " in module " ++ Module.nameToString name

    report
      | isLocalError = ""
      | otherwise =
          "\n\nThis error is probably due to bad version bounds. You should definitely\n"
          ++ "inform the maintainer of " ++ Pkg.toString pkgName ++ " to get this fixed.\n\n"
          ++ "In the meantime, you can attempt to get rid of the problematic dependency by\n"
          ++ "modifying " ++ Path.solvedDependencies ++ ", though that is not a long term\n"
          ++ "solution."


-- PROGRESS BAR

barLength :: Float
barLength = 50.0


renderProgressBar :: Int -> Int -> String
renderProgressBar complete total =
    "[" ++ replicate numDone '=' ++ replicate numLeft ' ' ++ "] - " ++ show complete ++ " / " ++ show total
  where
    fraction = fromIntegral complete / fromIntegral total
    numDone = truncate (fraction * barLength)
    numLeft = truncate barLength - numDone


clearProgressBar :: String
clearProgressBar =
    '\r' : replicate (length (renderProgressBar 99999 99999)) ' ' ++ "\r"