1{-# LANGUAGE BangPatterns, OverloadedStrings #-}
29import Control.Concurrent
30import Control.Exception (SomeException, AsyncException(UserInterrupt), catch, fromException, throw)
31import Control.Monad (when)
32import qualified Data.ByteString.Builder as B
33import qualified Data.NonEmptyList as NE
34import qualified System.Exit as Exit
35import qualified System.Info as Info
36import System.IO (hFlush, hPutStr, stderr, stdout)
38import qualified Elm.ModuleName as ModuleName
39import qualified Elm.Package as Pkg
40import qualified Elm.Version as V
41import qualified Json.Encode as Encode
42import Reporting.Doc ((<+>), (<>))
43import qualified Reporting.Doc as D
44import qualified Reporting.Exit as Exit
45import qualified Reporting.Exit.Help as Help
70 Terminal <$> newMVar ()
77attempt :: (x -> Help.Report) -> IO (Either x a) -> IO a
78attempt toReport work =
79 do result <- work `catch` reportExceptionsNicely
83 do Exit.toStderr (toReport x)
87attemptWithStyle :: Style -> (x -> Help.Report) -> IO (Either x a) -> IO a
88attemptWithStyle style toReport work =
89 do result <- work `catch` reportExceptionsNicely
98 do B.hPutBuilder stderr (Encode.encodeUgly (Exit.toJson (toReport x)))
103 Exit.toStderr (toReport x)
113 D.green $ if isWindows then "+" else "●"
118 D.red $ if isWindows then "X" else "✗"
123 D.dullyellow $ if isWindows then "-" else "→"
135newtype Key msg = Key (msg -> IO ())
138report :: Key msg -> msg -> IO ()
139report (Key send) msg =
145 Key (\_ -> return ())
152ask :: D.Doc -> IO Bool
168 do putStr "Must type 'y' for yes or 'n' for no: "
178trackDetails :: Style -> (DKey -> IO a) -> IO a
179trackDetails style callback =
182 callback (Key (\_ -> return ()))
185 callback (Key (\_ -> return ()))
192 detailsLoop chan (DState 0 0 0 0 0 0 0)
195 answer <- callback (Key (writeChan chan . Just))
196 writeChan chan Nothing
200detailsLoop :: Chan (Maybe DMsg) -> DState -> IO ()
201detailsLoop chan state@(DState total _ _ _ _ built _) =
202 do msg <- readChan chan
205 detailsLoop chan =<< detailsStep dmsg state
208 putStrLn $ clear (toBuildProgress total total) $
210 then "Dependencies ready!"
211 else "Dependency problem!"
230 | DReceived Pkg.Name V.Version
231 | DFailed Pkg.Name V.Version
236detailsStep :: DMsg -> DState -> IO DState
237detailsStep msg (DState total cached rqst rcvd failed built broken) =
239 DStart numDependencies ->
240 return (DState numDependencies 0 0 0 0 0 0)
243 putTransition (DState total (cached + 1) rqst rcvd failed built broken)
246 do when (rqst == 0) (putStrLn "Starting downloads...\n")
247 return (DState total cached (rqst + 1) rcvd failed built broken)
250 do putDownload goodMark pkg vsn
251 putTransition (DState total cached rqst (rcvd + 1) failed built broken)
254 do putDownload badMark pkg vsn
255 putTransition (DState total cached rqst rcvd (failed + 1) built broken)
258 putBuilt (DState total cached rqst rcvd failed (built + 1) broken)
261 putBuilt (DState total cached rqst rcvd failed built (broken + 1))
264putDownload :: D.Doc -> Pkg.Name -> V.Version -> IO ()
265putDownload mark pkg vsn =
266 Help.toStdout $ D.indent 2 $
268 <+> D.fromPackage pkg
269 <+> D.fromVersion vsn
273putTransition :: DState -> IO DState
274putTransition state@(DState total cached _ rcvd failed built broken) =
275 if cached + rcvd + failed < total then
279 do let char = if rcvd + failed == 0 then '\r' else '\n'
280 putStrFlush (char : toBuildProgress (built + broken + failed) total)
284putBuilt :: DState -> IO DState
285putBuilt state@(DState total cached _ rcvd failed built broken) =
286 do when (total == cached + rcvd + failed) $
287 putStrFlush $ '\r' : toBuildProgress (built + broken + failed) total
291toBuildProgress :: Int -> Int -> [Char]
292toBuildProgress built total =
293 "Verifying dependencies (" ++ show built ++ "/" ++ show total ++ ")"
296clear :: [Char] -> [Char] -> [Char]
298 '\r' : replicate (length before) ' ' ++ '\r' : after
307type BResult a = Either Exit.BuildProblem a
310trackBuild :: Style -> (BKey -> IO (BResult a)) -> IO (BResult a)
311trackBuild style callback =
314 callback (Key (\_ -> return ()))
317 callback (Key (\_ -> return ()))
324 putStrFlush "Compiling ..."
328 result <- callback (Key (writeChan chan . Left))
329 writeChan chan (Right result)
337buildLoop :: Chan (Either BMsg (BResult a)) -> Int -> IO ()
339 do msg <- readChan chan
342 do let !done1 = done + 1
343 putStrFlush $ "\rCompiling (" ++ show done1 ++ ")"
348 !message = toFinalMessage done result
349 !width = 12 + length (show done)
352 if length message < width
353 then '\r' : replicate width ' ' ++ '\r' : message
357toFinalMessage :: Int -> BResult a -> [Char]
358toFinalMessage done result =
363 1 -> "Success! Compiled 1 module."
364 n -> "Success! Compiled " ++ show n ++ " modules."
368 Exit.BuildModuleProblems _ [] ->
369 "Detected problems in 1 module."
371 Exit.BuildModuleProblems _ (_:ps) ->
372 "Detected problems in " ++ show (2 + length ps) ++ " modules."
374 Exit.BuildProjectProblem _ ->
375 "Detected a problem."
382reportGenerate :: Style -> NE.List ModuleName.Raw -> FilePath -> IO ()
383reportGenerate style names output =
393 let cnames = fmap ModuleName.toChars names
394 putStrLn ('\n' : toGenDiagram cnames output)
397toGenDiagram :: NE.List [Char] -> FilePath -> [Char]
398toGenDiagram (NE.List name names) output =
400 width = 3 + foldr (max . length) (length name) names
404 toGenLine width name ('>' : ' ' : output ++ "\n")
408 toGenLine width name (vtop : hbar : hbar : '>' : ' ' : output)
409 : reverse (zipWith (toGenLine width) (reverse names) ([vbottom] : repeat [vmiddle]))
412toGenLine :: Int -> [Char] -> [Char] -> [Char]
413toGenLine width name end =
414 " " ++ name ++ ' ' : replicate (width - length name) hbar ++ end
418hbar = if isWindows then '-' else '─'
421vtop = if isWindows then '+' else '┬'
424vmiddle = if isWindows then '+' else '┤'
427vbottom = if isWindows then '+' else '┘'
433putStrFlush :: String -> IO ()
435 hPutStr stdout str >> hFlush stdout
439-- REPORT EXCEPTIONS NICELY
442reportExceptionsNicely :: SomeException -> IO a
443reportExceptionsNicely e =
444 case fromException e of
445 Just UserInterrupt -> throw e
446 _ -> putException e >> throw e
449putException :: SomeException -> IO ()
451 Help.toStderr $ D.stack $
453 "Oh no! I ran into something that bypassed the normal error reporting process!\
454 \ I extracted whatever information I could from the internal error:"
455 , D.vcat $ map (\line -> D.red ">" <> " " <> D.fromChars line) (lines (show e))
457 "These errors are usually pretty confusing, so start by asking around on Slack\
458 \ or Discourse to see if anyone can get you unstuck quickly."
459 , "------------------------------------------------------------"
461 "From there, if you are feeling up to it, please try to get your code down to the\
462 \ smallest version that still triggers this message. Ideally in a single Main.elm\
463 \ and elm.json file."
465 "From there open a NEW issue at https://github.com/elm/compiler/issues with\
466 \ your reduced example pasted in directly. (Not a link to a repo or gist!) Do not\
467 \ worry about if someone else saw something similar. More examples is better!"
469 "This kind of error is usually tied up in larger architectural choices that are\
470 \ hard to change, so even when we have a couple good examples, it can take some\
471 \ time to resolve in a solid way."
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list