1{-# LANGUAGE BangPatterns, OverloadedStrings #-}
28import Control.Concurrent
29import Control.Exception (SomeException, AsyncException(UserInterrupt), catch, fromException, throw)
30import Control.Monad (when)
31import qualified Data.ByteString.Builder as B
32import qualified Data.NonEmptyList as NE
33import qualified System.Exit as Exit
34import qualified System.Info as Info
35import System.IO (hFlush, hPutStr, stderr, stdout)
37import qualified Elm.ModuleName as ModuleName
38import qualified Elm.Package as Pkg
39import qualified Elm.Version as V
40import qualified Json.Encode as Encode
41import Reporting.Doc ((<+>), (<>))
42import qualified Reporting.Doc as D
43import qualified Reporting.Exit as Exit
44import qualified Reporting.Exit.Help as Help
63 Terminal <$> newMVar ()
70attempt :: (x -> Help.Report) -> IO (Either x a) -> IO a
71attempt toReport work =
72 do result <- work `catch` reportExceptionsNicely
76 do Exit.toStderr (toReport x)
80attemptWithStyle :: Style -> (x -> Help.Report) -> IO (Either x a) -> IO a
81attemptWithStyle style toReport work =
82 do result <- work `catch` reportExceptionsNicely
88 do B.hPutBuilder stderr (Encode.encodeUgly (Exit.toJson (toReport x)))
93 Exit.toStderr (toReport x)
103 D.green $ if isWindows then "+" else "●"
108 D.red $ if isWindows then "X" else "✗"
113 D.dullyellow $ if isWindows then "-" else "→"
125newtype Key msg = Key (msg -> IO ())
128report :: Key msg -> msg -> IO ()
129report (Key send) msg =
135 Key (\_ -> return ())
142ask :: D.Doc -> IO Bool
158 do putStr "Must type 'y' for yes or 'n' for no: "
168trackDetails :: Style -> (DKey -> IO a) -> IO a
169trackDetails style callback =
172 callback (Key (\_ -> return ()))
179 detailsLoop chan (DState 0 0 0 0 0 0 0)
182 answer <- callback (Key (writeChan chan . Just))
183 writeChan chan Nothing
187detailsLoop :: Chan (Maybe DMsg) -> DState -> IO ()
188detailsLoop chan state@(DState total _ _ _ _ built _) =
189 do msg <- readChan chan
192 detailsLoop chan =<< detailsStep dmsg state
195 putStrLn $ clear (toBuildProgress total total) $
197 then "Dependencies ready!"
198 else "Dependency problem!"
217 | DReceived Pkg.Name V.Version
218 | DFailed Pkg.Name V.Version
223detailsStep :: DMsg -> DState -> IO DState
224detailsStep msg (DState total cached rqst rcvd failed built broken) =
226 DStart numDependencies ->
227 return (DState numDependencies 0 0 0 0 0 0)
230 putTransition (DState total (cached + 1) rqst rcvd failed built broken)
233 do when (rqst == 0) (putStrLn "Starting downloads...\n")
234 return (DState total cached (rqst + 1) rcvd failed built broken)
237 do putDownload goodMark pkg vsn
238 putTransition (DState total cached rqst (rcvd + 1) failed built broken)
241 do putDownload badMark pkg vsn
242 putTransition (DState total cached rqst rcvd (failed + 1) built broken)
245 putBuilt (DState total cached rqst rcvd failed (built + 1) broken)
248 putBuilt (DState total cached rqst rcvd failed built (broken + 1))
251putDownload :: D.Doc -> Pkg.Name -> V.Version -> IO ()
252putDownload mark pkg vsn =
253 Help.toStdout $ D.indent 2 $
255 <+> D.fromUtf8 (Pkg.toString pkg)
256 <+> D.fromUtf8 (V.toString vsn)
260putTransition :: DState -> IO DState
261putTransition state@(DState total cached _ rcvd failed built broken) =
262 if cached + rcvd + failed < total then
266 do let char = if rcvd + failed == 0 then '\r' else '\n'
267 putStrFlush (char : toBuildProgress (built + broken + failed) total)
271putBuilt :: DState -> IO DState
272putBuilt state@(DState total cached _ rcvd failed built broken) =
273 do when (total == cached + rcvd + failed) $
274 putStrFlush $ '\r' : toBuildProgress (built + broken + failed) total
278toBuildProgress :: Int -> Int -> [Char]
279toBuildProgress built total =
280 "Verifying dependencies (" ++ show built ++ "/" ++ show total ++ ")"
283clear :: [Char] -> [Char] -> [Char]
285 '\r' : replicate (length before) ' ' ++ '\r' : after
294type BResult a = Either Exit.BuildProblem a
297trackBuild :: Style -> (BKey -> IO (BResult a)) -> IO (BResult a)
298trackBuild style callback =
301 callback (Key (\_ -> return ()))
308 putStrFlush "Compiling ..."
312 result <- callback (Key (writeChan chan . Left))
313 writeChan chan (Right result)
321buildLoop :: Chan (Either BMsg (BResult a)) -> Int -> IO ()
323 do msg <- readChan chan
326 do let !done1 = done + 1
327 putStrFlush $ "\rCompiling (" ++ show done1 ++ ")"
332 !message = toFinalMessage done result
333 !width = 12 + length (show done)
336 if length message < width
337 then '\r' : replicate width ' ' ++ '\r' : message
341toFinalMessage :: Int -> BResult a -> [Char]
342toFinalMessage done result =
347 1 -> "Success! Compiled 1 module."
348 n -> "Success! Compiled " ++ show n ++ " modules."
352 Exit.BuildModuleProblems _ [] ->
353 "Detected problems in 1 module."
355 Exit.BuildModuleProblems _ (_:ps) ->
356 "Detected problems in " ++ show (2 + length ps) ++ " modules."
358 Exit.BuildProjectProblem _ ->
359 "Detected a problem."
366reportGenerate :: Style -> NE.List ModuleName.Raw -> FilePath -> IO ()
367reportGenerate style names output =
374 let cnames = fmap ModuleName.toChars names
375 putStrLn ('\n' : toGenDiagram cnames output)
378toGenDiagram :: NE.List [Char] -> FilePath -> [Char]
379toGenDiagram (NE.List name names) output =
381 width = 3 + foldr (max . length) (length name) names
385 toGenLine width name ('>' : ' ' : output ++ "\n")
389 toGenLine width name (vtop : hbar : hbar : '>' : ' ' : output)
390 : reverse (zipWith (toGenLine width) (reverse names) ([vbottom] : repeat [vmiddle]))
393toGenLine :: Int -> [Char] -> [Char] -> [Char]
394toGenLine width name end =
395 " " ++ name ++ ' ' : replicate (width - length name) hbar ++ end
399hbar = if isWindows then '-' else '─'
402vtop = if isWindows then '+' else '┬'
405vmiddle = if isWindows then '+' else '┤'
408vbottom = if isWindows then '+' else '┘'
414putStrFlush :: String -> IO ()
416 hPutStr stdout str >> hFlush stdout
420-- REPORT EXCEPTIONS NICELY
423reportExceptionsNicely :: SomeException -> IO a
424reportExceptionsNicely e =
425 case fromException e of
426 Just UserInterrupt -> throw e
427 _ -> putException e >> throw e
430putException :: SomeException -> IO ()
432 Help.toStderr $ D.stack $
434 "Oh no! I ran into something that bypassed the normal error reporting process!\
435 \ I extracted whatever information I could from the internal error:"
436 , D.vcat $ map (\line -> D.red ">" <> " " <> D.fromChars line) (lines (show e))
438 "These errors are usually pretty confusing, so start by asking around on Slack\
439 \ or Discourse to see if anyone can get you unstuck quickly."
440 , "------------------------------------------------------------"
442 "From there, if you are feeling up to it, please try to get your code down to the\
443 \ smallest version that still triggers this message. Ideally in a single Main.elm\
444 \ and elm.json file."
446 "From there open a NEW issue at https://github.com/elm/compiler/issues with\
447 \ your reduced example pasted in directly. (Not a link to a repo or gist!) Do not\
448 \ worry about if someone else saw something similar. More examples is better!"
450 "This kind of error is usually tied up in larger architectural choices that are\
451 \ hard to change, so even when we have a couple good examples, it can take some\
452 \ 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