3Description : Utilities to display Idris' internals and other informtation to the user.
6Maintainer : The Idris Community.
9{-# LANGUAGE CPP, FlexibleInstances #-}
10{-# OPTIONS_GHC -fwarn-incomplete-patterns #-}
12module Idris.Output (clearHighlights, emitWarning, formatMessage, idemodePutSExp,
13 iPrintError, iPrintFunTypes, iPrintResult, iPrintTermWithType,
14 iputGoal, iputStr, iputStrLn, iRender, iRenderError,
15 iRenderOutput, iRenderResult, iWarn, prettyDocumentedIst,
16 printUndefinedNames, pshow, renderExternal, sendHighlighting,
17 sendParserHighlighting, warnTotality, writeHighlights,
18 OutputDoc(..), Message(..)) where
21import Idris.Colours (hEndColourise, hStartColourise)
22import Idris.Core.Evaluate (normaliseAll)
24import Idris.Delaborate
25import Idris.Docstrings
30import Util.ScreenSize (getScreenWidth)
31import Util.System (isATTY)
33#if (MIN_VERSION_base(4,11,0))
34import Prelude hiding ((<$>), (<>))
36import Prelude hiding ((<$>))
39import Control.Arrow (first)
40import Control.Monad.Trans.Except (ExceptT(ExceptT), runExceptT)
41import Data.List (intersperse, nub)
42import Data.Maybe (fromJust, fromMaybe, isJust, listToMaybe)
43import System.Console.Haskeline.MonadException (MonadException(controlIO),
45import System.FilePath (replaceExtension)
46import System.IO (Handle, hPutStr, hPutStrLn)
47import System.IO.Error (tryIOError)
49instance MonadException m => MonadException (ExceptT Err m) where
50 controlIO f = ExceptT $ controlIO $ \(RunIO run) -> let
51 run' = RunIO (fmap ExceptT . run . runExceptT)
52 in fmap runExceptT $ f run'
54pshow :: IState -> Err -> String
55pshow ist err = displayDecorated (consoleDecorate ist) .
57 fmap (fancifyAnnots ist True) $ pprintErr ist err
59type OutputDoc = Doc OutputAnnotation
62 messageExtent :: a -> FC
63 messageText :: a -> OutputDoc
64 messageSource :: a -> Maybe String
66data Ann = AText String | ATagged OutputAnnotation Ann | ASplit Ann Ann
68data SimpleWarning = SimpleWarning FC OutputDoc
69instance Message SimpleWarning where
70 messageExtent (SimpleWarning extent _) = extent
71 messageText (SimpleWarning _ msg) = msg
72 messageSource _ = Nothing
74formatMessage :: Message w => w -> Idris OutputDoc
77 maybeSource <- case messageSource w of
78 Just src -> pure (Just src)
79 Nothing -> readSource fc
80 let maybeFormattedSource = maybeSource >>= layoutSource fc (idris_highlightedRegions i)
81 return $ layoutMessage (layoutFC fc) maybeFormattedSource (messageText w)
86 layoutFC :: FC -> OutputDoc
87 layoutFC fc@(FC fn _ _) | fn /= "" = text (show $ fc) <> colon
90 readSource :: FC -> Idris (Maybe String)
91 readSource (FC fn _ _) = do
92 result <- runIO $ tryIOError (readFile fn)
94 Left _ -> pure Nothing
95 Right v -> pure (Just v)
96 readSource _ = pure Nothing
98 layoutSource :: FC -> [(FC, OutputAnnotation)] -> String -> Maybe OutputDoc
99 layoutSource (FC fn (si, sj) (ei, ej)) highlights src =
104 sourceLine :: Maybe String
105 sourceLine = listToMaybe . drop (si - 1) . (++ ["<end of file>"]) . lines $ src
108 haveSource = isJust sourceLine
111 line1 = text $ replicate (length (show si)) ' ' ++ " |"
114 lineFC = FC fn (si, 1) (si, length (fromJust sourceLine))
116 intersects :: FC -> FC -> Bool
117 intersects (FC fn1 s1 e1) (FC fn2 s2 e2)
122 intersects _ _ = False
124 intersection :: FC -> FC -> FC
125 intersection fc1@(FC fn1 s1 e1) fc2@(FC _ s2 e2)
126 | intersects fc1 fc2 = FC fn1 (max s1 s2) (min e1 e2)
128 intersection _ _ = NoFC
131 width (AText s) = length s
132 width (ATagged _ n) = width n
133 width (ASplit l r) = width l + width r
135 sourceDoc :: Ann -> OutputDoc
136 sourceDoc (AText s) = text s
137 sourceDoc (ATagged a n) = annotate a (sourceDoc n)
138 sourceDoc (ASplit l r) = sourceDoc l <> sourceDoc r
140 insertAnnotation :: ((Int, Int), OutputAnnotation) -> Ann -> Ann
141 insertAnnotation ((sj, ej), oa) (ATagged tag n) = ATagged tag (insertAnnotation ((sj, ej), oa) n)
142 insertAnnotation ((sj, ej), oa) (ASplit l r)
143 | w <- width l = ASplit (insertAnnotation ((sj, ej), oa) l) (insertAnnotation ((sj - w, ej - w), oa) r)
144 insertAnnotation ((sj, ej), oa) a@(AText t)
145 | sj <= 1, ej >= width a = ATagged oa a
146 | sj > 1, sj <= width a = ASplit (AText $ take (sj - 1) t) (insertAnnotation ((1, ej - sj + 1), oa) (AText $ drop (sj - 1) t))
147 | sj == 1, ej >= 1, ej < width a = ASplit (insertAnnotation ((1, ej), oa) (AText $ take ej t)) (AText $ drop ej t)
150 highlightedSource :: OutputDoc
151 highlightedSource = sourceDoc .
152 foldr insertAnnotation (AText $ fromJust sourceLine) .
153 map (\(FC _ (_, sj) (_, ej), ann) -> ((sj, ej), ann)) .
154 map (first (intersection lineFC)) .
155 filter (intersects lineFC . fst) $
159 line2 = text (show si ++ " | ") <> highlightedSource
161 indicator :: OutputDoc
162 indicator = text (replicate (end - sj + 1) ch) <> ellipsis
164 (end, ch, ellipsis) = case (si == ei, sj == ej) of
165 (True , True ) -> (ej, '^', empty)
166 (True , False) -> (ej, '~', empty)
167 (False, _ ) -> (length (fromJust sourceLine), '~', text " ...")
170 line3 = line1 <> text (replicate sj ' ') <> indicator
173 source = line1 <$$> line2 <$$> line3
174 layoutSource _ _ _ = Nothing
176 layoutMessage :: OutputDoc -> Maybe OutputDoc -> OutputDoc -> OutputDoc
177 layoutMessage loc (Just src) err = loc <$$> src <$$> err <$$> empty
178 layoutMessage loc Nothing err = loc </> err
180iWarn :: FC -> OutputDoc -> Idris ()
181iWarn fc err = emitWarning $ SimpleWarning fc err
183emitWarning :: Message w => w -> Idris ()
186 case idris_outputmode i of
188 do formattedErr <- formatMessage w
189 err' <- iRender . fmap (fancifyAnnots i True) $ formattedErr
192 do err' <- iRender . fmap (fancifyAnnots i True) $ messageText w
193 let fc = messageExtent w
194 let (str, spans) = displaySpans err'
195 runIO . hPutStrLn h $
196 convSExp "warning" (fc_fname fc, fc_start fc, fc_end fc, str, spans) n
198iRender :: Doc a -> Idris (SimpleDoc a)
199iRender d = do w <- getWidth
201 let ideMode = case idris_outputmode ist of
206 InfinitelyWide -> return $ renderPretty 1.0 1000000000 d
207 ColsWide n -> return $
209 then renderPretty 1.0 1000000000 d
210 else renderPretty 0.8 n d
211 AutomaticWidth | ideMode || not tty -> return $ renderPretty 1.0 80 d
212 | otherwise -> do width <- runIO getScreenWidth
213 return $ renderPretty 0.8 width d
215hWriteDoc :: Handle -> IState -> SimpleDoc OutputAnnotation -> Idris ()
216hWriteDoc h ist rendered =
217 do runIO $ displayDecoratedA
219 (maybe (return ()) (hStartColourise h))
220 (maybe (return ()) (hEndColourise h))
221 (fmap (annotationColour ist) rendered)
222 runIO $ hPutStr h "\n" -- newline translation on the output
223 -- stream should take care of this for
226-- | Write a pretty-printed term to the console with semantic coloring
227consoleDisplayAnnotated :: Handle -> Doc OutputAnnotation -> Idris ()
228consoleDisplayAnnotated h output =
230 rendered <- iRender output
231 hWriteDoc h ist rendered
233iPrintTermWithType :: Doc OutputAnnotation -> Doc OutputAnnotation -> Idris ()
234iPrintTermWithType tm ty = iRenderResult (tm <+> colon <+> align ty)
236-- | Pretty-print a collection of overloadings to REPL or IDEMode - corresponds to :t name
237iPrintFunTypes :: [(Name, Bool)] -> Name -> [(Name, Doc OutputAnnotation)] -> Idris ()
238iPrintFunTypes bnd n [] = iPrintError $ "No such variable " ++ show n
239iPrintFunTypes bnd n overloads = do ist <- getIState
240 let ppo = ppOptionIst ist
241 let infixes = idris_infixes ist
242 let output = vsep (map (uncurry (ppOverload ppo infixes)) overloads)
244 where fullName ppo n | length overloads > 1 = prettyName True True bnd n
245 | otherwise = prettyName True (ppopt_impl ppo) bnd n
246 ppOverload ppo infixes n tm =
247 fullName ppo n <+> colon <+> align tm
249iRenderOutput :: Doc OutputAnnotation -> Idris ()
252 case idris_outputmode i of
253 RawOutput h -> do out <- iRender doc
257 do (str, spans) <- fmap displaySpans . iRender . fmap (fancifyAnnots i True) $ doc
258 let out = [toSExp str, toSExp spans]
259 runIO . hPutStrLn h $ convSExp "write-decorated" out n
261iRenderResult :: Doc OutputAnnotation -> Idris ()
262iRenderResult d = do ist <- getIState
263 case idris_outputmode ist of
264 RawOutput h -> consoleDisplayAnnotated h d
265 IdeMode n h -> ideModeReturnAnnotated n h d
267ideModeReturnWithStatus :: String -> Integer -> Handle -> Doc OutputAnnotation -> Idris ()
268ideModeReturnWithStatus status n h out = do
270 (str, spans) <- fmap displaySpans .
272 fmap (fancifyAnnots ist True) $
274 let good = [SymbolAtom status, toSExp str, toSExp spans]
275 runIO . hPutStrLn h $ convSExp "return" good n
278-- | Write pretty-printed output to IDEMode with semantic annotations
279ideModeReturnAnnotated :: Integer -> Handle -> Doc OutputAnnotation -> Idris ()
280ideModeReturnAnnotated = ideModeReturnWithStatus "ok"
282-- | Show an error with semantic highlighting
283iRenderError :: Doc OutputAnnotation -> Idris ()
284iRenderError e = do ist <- getIState
285 case idris_outputmode ist of
286 RawOutput h -> consoleDisplayAnnotated h e
287 IdeMode n h -> ideModeReturnWithStatus "error" n h e
289iPrintWithStatus :: String -> String -> Idris ()
290iPrintWithStatus status s = do
292 case idris_outputmode i of
293 RawOutput h -> case s of
295 s -> runIO $ hPutStrLn h s
297 let good = SexpList [SymbolAtom status, toSExp s] in
298 runIO $ hPutStrLn h $ convSExp "return" good n
301iPrintResult :: String -> Idris ()
302iPrintResult = iPrintWithStatus "ok"
304iPrintError :: String -> Idris ()
305iPrintError = iPrintWithStatus "error"
307iputStrLn :: String -> Idris ()
308iputStrLn s = do i <- getIState
309 case idris_outputmode i of
310 RawOutput h -> runIO $ hPutStrLn h s
311 IdeMode n h -> runIO . hPutStrLn h $ convSExp "write-string" s n
313iputStr :: String -> Idris ()
314iputStr s = do i <- getIState
315 case idris_outputmode i of
316 RawOutput h -> runIO $ hPutStr h s
317 IdeMode n h -> runIO . hPutStr h $ convSExp "write-string" s n
319idemodePutSExp :: SExpable a => String -> a -> Idris ()
320idemodePutSExp cmd info = do i <- getIState
321 case idris_outputmode i of
323 runIO . hPutStrLn h $
327-- TODO: send structured output similar to the metavariable list
328iputGoal :: SimpleDoc OutputAnnotation -> Idris ()
329iputGoal g = do i <- getIState
330 case idris_outputmode i of
331 RawOutput h -> hWriteDoc h i g
333 let (str, spans) = displaySpans . fmap (fancifyAnnots i True) $ g
334 goal = [toSExp str, toSExp spans]
335 in runIO . hPutStrLn h $ convSExp "write-goal" goal n
337-- | Warn about totality problems without failing to compile
338warnTotality :: Idris ()
339warnTotality = do ist <- getIState
340 mapM_ (warn ist) (nub (idris_totcheckfail ist))
341 where warn ist (fc, e) = iWarn fc (pprintErr ist (Msg e))
344printUndefinedNames :: [Name] -> Doc OutputAnnotation
345printUndefinedNames ns = text "Undefined " <> names <> text "."
346 where names = encloseSep empty empty (char ',') $ map ppName ns
347 ppName = prettyName True True []
350prettyDocumentedIst :: IState
351 -> (Name, PTerm, Maybe (Docstring DocTerm))
352 -> Doc OutputAnnotation
353prettyDocumentedIst ist (name, ty, docs) =
354 prettyName True True [] name <+> colon <+> align (prettyIst ist ty) <$>
355 fromMaybe empty (fmap (\d -> renderDocstring (renderDocTerm ppTm norm) d <> line) docs)
356 where ppTm = pprintDelab ist
357 norm = normaliseAll (tt_ctxt ist) []
359sendParserHighlighting :: Idris ()
360sendParserHighlighting =
362 let hs = map unwrap . nub . map wrap $ idris_parserHighlights ist
365 putIState ist {idris_parserHighlights = []}
366 where wrap (fc, a) = (FC' fc, a)
367 unwrap (fc', a) = (unwrapFC fc', a)
369sendHighlighting :: [(FC, OutputAnnotation)] -> Idris ()
370sendHighlighting highlights =
372 case idris_outputmode ist of
373 RawOutput _ -> updateIState $
374 \ist -> ist { idris_highlightedRegions =
375 highlights ++ idris_highlightedRegions ist }
377 let fancier = [ toSExp (fc, fancifyAnnots ist False annot)
378 | (fc, annot) <- highlights, fullFC fc
382 _ -> runIO . hPutStrLn h $
385 (SymbolAtom "highlight-source", fancier)) n
387 where fullFC (FC _ _ _) = True
390-- | Write the highlighting information to a file, for use in external tools
391-- or in editors that don't support the IDE protocol
392writeHighlights :: FilePath -> Idris ()
395 let hs = reverse $ idris_highlightedRegions ist
396 let hfile = replaceExtension f "idh"
397 let annots = toSExp [ (fc, fancifyAnnots ist False annot)
398 | (fc@(FC _ _ _), annot) <- hs
400 runIO $ writeFile hfile $ sExpToString annots
402clearHighlights :: Idris ()
403clearHighlights = updateIState $ \ist -> ist { idris_highlightedRegions = [] }
405renderExternal :: OutputFmt -> Int -> Doc OutputAnnotation -> Idris String
406renderExternal fmt width doc
407 | width < 1 = throwError . Msg $ "There must be at least one column for the pretty-printer."
411 displayDecorated (decorate fmt) .
412 renderPretty 1.0 width .
413 fmap (fancifyAnnots ist True) $
416 decorate _ (AnnFC _) = id
418 decorate HTMLOutput (AnnName _ (Just TypeOutput) d _) =
420 decorate HTMLOutput (AnnName _ (Just FunOutput) d _) =
421 tag "idris-function" d
422 decorate HTMLOutput (AnnName _ (Just DataOutput) d _) =
424 decorate HTMLOutput (AnnName _ (Just MetavarOutput) d _) =
425 tag "idris-metavar" d
426 decorate HTMLOutput (AnnName _ (Just PostulateOutput) d _) =
427 tag "idris-postulate" d
428 decorate HTMLOutput (AnnName _ _ _ _) = id
429 decorate HTMLOutput (AnnBoundName _ True) = tag "idris-bound idris-implicit" Nothing
430 decorate HTMLOutput (AnnBoundName _ False) = tag "idris-bound" Nothing
431 decorate HTMLOutput (AnnConst c) =
432 tag (if constIsType c then "idris-type" else "idris-data")
434 decorate HTMLOutput (AnnData _ _) = tag "idris-data" Nothing
435 decorate HTMLOutput (AnnType _ _) = tag "idris-type" Nothing
436 decorate HTMLOutput AnnKeyword = tag "idris-keyword" Nothing
437 decorate HTMLOutput (AnnTextFmt fmt) =
439 BoldText -> mkTag "strong"
440 ItalicText -> mkTag "em"
441 UnderlineText -> tag "idris-underlined" Nothing
442 where mkTag t x = "<"++t++">"++x++"</"++t++">"
443 decorate HTMLOutput (AnnTerm _ _) = id
444 decorate HTMLOutput (AnnSearchResult _) = id
445 decorate HTMLOutput (AnnErr _) = id
446 decorate HTMLOutput (AnnNamespace _ _) = id
447 decorate HTMLOutput (AnnLink url) =
448 \txt -> "<a href=\"" ++ url ++ "\">" ++ txt ++ "</a>"
449 decorate HTMLOutput AnnQuasiquote = id
450 decorate HTMLOutput AnnAntiquote = id
451 decorate HTMLOutput (AnnSyntax c) = \txt -> c
453 decorate LaTeXOutput (AnnName _ (Just TypeOutput) _ _) =
455 decorate LaTeXOutput (AnnName _ (Just FunOutput) _ _) =
456 latex "IdrisFunction"
457 decorate LaTeXOutput (AnnName _ (Just DataOutput) _ _) =
459 decorate LaTeXOutput (AnnName _ (Just MetavarOutput) _ _) =
461 decorate LaTeXOutput (AnnName _ (Just PostulateOutput) _ _) =
462 latex "IdrisPostulate"
463 decorate LaTeXOutput (AnnName _ _ _ _) = id
464 decorate LaTeXOutput (AnnBoundName _ True) = latex "IdrisImplicit"
465 decorate LaTeXOutput (AnnBoundName _ False) = latex "IdrisBound"
466 decorate LaTeXOutput (AnnConst c) =
467 latex $ if constIsType c then "IdrisType" else "IdrisData"
468 decorate LaTeXOutput (AnnData _ _) = latex "IdrisData"
469 decorate LaTeXOutput (AnnType _ _) = latex "IdrisType"
470 decorate LaTeXOutput AnnKeyword = latex "IdrisKeyword"
471 decorate LaTeXOutput (AnnTextFmt fmt) =
473 BoldText -> latex "textbf"
474 ItalicText -> latex "emph"
475 UnderlineText -> latex "underline"
476 decorate LaTeXOutput (AnnTerm _ _) = id
477 decorate LaTeXOutput (AnnSearchResult _) = id
478 decorate LaTeXOutput (AnnErr _) = id
479 decorate LaTeXOutput (AnnNamespace _ _) = id
480 decorate LaTeXOutput (AnnLink url) = (++ "(\\url{" ++ url ++ "})")
481 decorate LaTeXOutput AnnQuasiquote = id
482 decorate LaTeXOutput AnnAntiquote = id
483 decorate LaTeXOutput (AnnSyntax c) = \txt ->
485 "\\" -> "\\textbackslash"
491 tag cls docs str = "<span class=\""++cls++"\""++title++">" ++ str ++ "</span>"
492 where title = maybe "" (\d->" title=\"" ++ d ++ "\"") docs
494 latex cmd str = "\\"++cmd++"{"++str++"}"
496 wrap HTMLOutput str =
497 "<!doctype html><html><head><style>" ++ css ++ "</style></head>" ++
498 "<body><!-- START CODE --><pre>" ++ str ++ "</pre><!-- END CODE --></body></html>"
499 where css = concat . intersperse "\n" $
500 [".idris-data { color: red; } ",
501 ".idris-type { color: blue; }",
502 ".idris-function {color: green; }",
503 ".idris-metavar {color: turquoise; }",
504 ".idris-keyword { font-weight: bold; }",
505 ".idris-bound { color: purple; }",
506 ".idris-implicit { font-style: italic; }",
507 ".idris-underlined { text-decoration: underline; }"]
508 wrap LaTeXOutput str =
509 concat . intersperse "\n" $
510 ["\\documentclass{article}",
511 "\\usepackage{fancyvrb}",
512 "\\usepackage[usenames]{xcolor}"] ++
513 map (\(cmd, color) ->
514 "\\newcommand{\\"++ cmd ++
515 "}[1]{\\textcolor{"++ color ++"}{#1}}")
516 [("IdrisData", "red"), ("IdrisType", "blue"),
517 ("IdrisBound", "magenta"), ("IdrisFunction", "green"),
518 ("IdrisMetavar", "cyan")] ++
519 ["\\newcommand{\\IdrisKeyword}[1]{{\\underline{#1}}}",
520 "\\newcommand{\\IdrisImplicit}[1]{{\\itshape \\IdrisBound{#1}}}",
524 "\\begin{Verbatim}[commandchars=\\\\\\{\\}]",
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