Commit 51b8ea24 authored by Andrew Gibiansky's avatar Andrew Gibiansky

changing DisplayData -> Display in IHaskell

parent 6b5fb53d
...@@ -10,7 +10,7 @@ import Data.String.Here ...@@ -10,7 +10,7 @@ import Data.String.Here
import IHaskell.Display import IHaskell.Display
instance IHaskellDisplay Value where instance IHaskellDisplay Value where
display renderable = return [plain json, html dom] display renderable = return $ Display [plain json, html dom]
where where
json = unpack $ decodeUtf8 $ encodePretty renderable json = unpack $ decodeUtf8 $ encodePretty renderable
dom = [i|<div class="highlight-code" id="javascript">${json}</div>|] dom = [i|<div class="highlight-code" id="javascript">${json}</div>|]
...@@ -6,7 +6,7 @@ import IHaskell.Display ...@@ -6,7 +6,7 @@ import IHaskell.Display
import Text.Printf import Text.Printf
instance Show a => IHaskellDisplay (Maybe a) where instance Show a => IHaskellDisplay (Maybe a) where
display just = return [stringDisplay, htmlDisplay] display just = return $ Display [stringDisplay, htmlDisplay]
where where
stringDisplay = plain (show just) stringDisplay = plain (show just)
htmlDisplay = html str htmlDisplay = html str
......
...@@ -10,7 +10,7 @@ import Text.Blaze.Internal ...@@ -10,7 +10,7 @@ import Text.Blaze.Internal
import Control.Monad import Control.Monad
instance IHaskellDisplay (MarkupM a) where instance IHaskellDisplay (MarkupM a) where
display val = return [stringDisplay, htmlDisplay] display val = return $ Display [stringDisplay, htmlDisplay]
where where
str = renderMarkup (void val) str = renderMarkup (void val)
stringDisplay = plain str stringDisplay = plain str
......
...@@ -26,7 +26,7 @@ instance IHaskellDisplay (Renderable a) where ...@@ -26,7 +26,7 @@ instance IHaskellDisplay (Renderable a) where
-- but SVGs are not resizable in the IPython notebook. -- but SVGs are not resizable in the IPython notebook.
svgDisp <- chartData renderable SVG svgDisp <- chartData renderable SVG
return [pngDisp, svgDisp] return $ Display [pngDisp, svgDisp]
chartData :: Renderable a -> FileFormat -> IO DisplayData chartData :: Renderable a -> FileFormat -> IO DisplayData
chartData renderable format = do chartData renderable format = do
......
...@@ -16,7 +16,7 @@ instance IHaskellDisplay (Diagram Cairo R2) where ...@@ -16,7 +16,7 @@ instance IHaskellDisplay (Diagram Cairo R2) where
display renderable = do display renderable = do
png <- diagramData renderable PNG png <- diagramData renderable PNG
svg <- diagramData renderable SVG svg <- diagramData renderable SVG
return [png, svg] return $ Display [png, svg]
diagramData :: Diagram Cairo R2 -> OutputType -> IO DisplayData diagramData :: Diagram Cairo R2 -> OutputType -> IO DisplayData
diagramData renderable format = do diagramData renderable format = do
......
...@@ -24,7 +24,7 @@ instance IHaskellDisplay B.ByteString where ...@@ -24,7 +24,7 @@ instance IHaskellDisplay B.ByteString where
m <- magicOpen [] m <- magicOpen []
magicLoadDefault m magicLoadDefault m
f <- B.unsafeUseAsCStringLen x (magicCString m) f <- B.unsafeUseAsCStringLen x (magicCString m)
return [withClass (parseMagic f) x] return $ Display [withClass (parseMagic f) x]
b64 :: B.ByteString -> String b64 :: B.ByteString -> String
b64 = Char.unpack . Base64.encode b64 = Char.unpack . Base64.encode
......
...@@ -101,7 +101,7 @@ instance ToJSON StreamType where ...@@ -101,7 +101,7 @@ instance ToJSON StreamType where
-- | Convert a MIME type and value into a JSON dictionary pair. -- | Convert a MIME type and value into a JSON dictionary pair.
displayDataToJson :: DisplayData -> (Text, Value) displayDataToJson :: DisplayData -> (Text, Value)
displayDataToJson (Display mimeType dataStr) = pack (show mimeType) .= dataStr displayDataToJson (DisplayData mimeType dataStr) = pack (show mimeType) .= dataStr
----- Constants ----- ----- Constants -----
......
...@@ -341,13 +341,13 @@ replyType ShutdownRequestMessage = Just ShutdownReplyMessage ...@@ -341,13 +341,13 @@ replyType ShutdownRequestMessage = Just ShutdownReplyMessage
replyType _ = Nothing replyType _ = Nothing
-- | Data for display: a string with associated MIME type. -- | Data for display: a string with associated MIME type.
data DisplayData = Display MimeType ByteString deriving (Typeable, Generic) data DisplayData = DisplayData MimeType ByteString deriving (Typeable, Generic)
-- We can't print the actual data, otherwise this will be printed every -- We can't print the actual data, otherwise this will be printed every
-- time it gets computed because of the way the evaluator is structured. -- time it gets computed because of the way the evaluator is structured.
-- See how `displayExpr` is computed. -- See how `displayExpr` is computed.
instance Show DisplayData where instance Show DisplayData where
show _ = "Display" show _ = "DisplayData"
-- Allow DisplayData serialization -- Allow DisplayData serialization
instance Serialize DisplayData instance Serialize DisplayData
...@@ -369,9 +369,9 @@ extractPlain :: [DisplayData] -> String ...@@ -369,9 +369,9 @@ extractPlain :: [DisplayData] -> String
extractPlain disps = extractPlain disps =
case find isPlain disps of case find isPlain disps of
Nothing -> "" Nothing -> ""
Just (Display PlainText bytestr) -> Char.unpack bytestr Just (DisplayData PlainText bytestr) -> Char.unpack bytestr
where where
isPlain (Display mime _) = mime == PlainText isPlain (DisplayData mime _) = mime == PlainText
instance Show MimeType where instance Show MimeType where
show PlainText = "text/plain" show PlainText = "text/plain"
......
...@@ -5,12 +5,13 @@ module IHaskell.Display ( ...@@ -5,12 +5,13 @@ module IHaskell.Display (
serializeDisplay, serializeDisplay,
Width, Height, Base64, Width, Height, Base64,
encode64, base64, encode64, base64,
DisplayData Display(..),
DisplayData(..),
) where ) where
import ClassyPrelude import ClassyPrelude
import Data.Serialize as Serialize import Data.Serialize as Serialize
import Data.ByteString import Data.ByteString hiding (map)
import Data.String.Utils (rstrip) import Data.String.Utils (rstrip)
import qualified Data.ByteString.Base64 as Base64 import qualified Data.ByteString.Base64 as Base64
import qualified Data.ByteString.Char8 as Char import qualified Data.ByteString.Char8 as Char
...@@ -27,52 +28,59 @@ type Base64 = ByteString ...@@ -27,52 +28,59 @@ type Base64 = ByteString
-- > instance (Show a) => IHaskellDisplay a -- > instance (Show a) => IHaskellDisplay a
-- > instance Show a where shows _ = id -- > instance Show a where shows _ = id
class IHaskellDisplay a where class IHaskellDisplay a where
display :: a -> IO [DisplayData] display :: a -> IO Display
-- | these instances cause the image, html etc. which look like: -- | these instances cause the image, html etc. which look like:
-- --
-- > DisplayData -- > Display
-- > [DisplayData] -- > [Display]
-- > IO [DisplayData] -- > IO [Display]
-- > IO (IO DisplayData) -- > IO (IO Display)
-- --
-- be run the IO and get rendered (if the frontend allows it) in the pretty -- be run the IO and get rendered (if the frontend allows it) in the pretty
-- form. -- form.
instance IHaskellDisplay a => IHaskellDisplay (IO a) where instance IHaskellDisplay a => IHaskellDisplay (IO a) where
display = (display =<<) display = (display =<<)
instance IHaskellDisplay DisplayData where
display disp = return [disp]
instance IHaskellDisplay [DisplayData] where instance IHaskellDisplay Display where
display = return display = return
instance IHaskellDisplay a => IHaskellDisplay [a] where
display disps = do
displays <- mapM display disps
return $ ManyDisplay displays
-- | Encode many displays into a single one. All will be output.
many :: [Display] -> Display
many = ManyDisplay
-- | Generate a plain text display. -- | Generate a plain text display.
plain :: String -> DisplayData plain :: String -> DisplayData
plain = Display PlainText . Char.pack . rstrip plain = DisplayData PlainText . Char.pack . rstrip
-- | Generate an HTML display. -- | Generate an HTML display.
html :: String -> DisplayData html :: String -> DisplayData
html = Display MimeHtml . Char.pack html = DisplayData MimeHtml . Char.pack
-- | Genreate an SVG display. -- | Genreate an SVG display.
svg :: String -> DisplayData svg :: String -> DisplayData
svg = Display MimeSvg . Char.pack svg = DisplayData MimeSvg . Char.pack
-- | Genreate a LaTeX display. -- | Genreate a LaTeX display.
latex :: String -> DisplayData latex :: String -> DisplayData
latex = Display MimeLatex . Char.pack latex = DisplayData MimeLatex . Char.pack
-- | Generate a PNG display of the given width and height. Data must be -- | Generate a PNG display of the given width and height. Data must be
-- provided in a Base64 encoded manner, suitable for embedding into HTML. -- provided in a Base64 encoded manner, suitable for embedding into HTML.
-- The @base64@ function may be used to encode data into this format. -- The @base64@ function may be used to encode data into this format.
png :: Width -> Height -> Base64 -> DisplayData png :: Width -> Height -> Base64 -> DisplayData
png width height = Display (MimePng width height) png width height = DisplayData (MimePng width height)
-- | Generate a JPG display of the given width and height. Data must be -- | Generate a JPG display of the given width and height. Data must be
-- provided in a Base64 encoded manner, suitable for embedding into HTML. -- provided in a Base64 encoded manner, suitable for embedding into HTML.
-- The @base64@ function may be used to encode data into this format. -- The @base64@ function may be used to encode data into this format.
jpg :: Width -> Height -> Base64 -> DisplayData jpg :: Width -> Height -> Base64 -> DisplayData
jpg width height = Display (MimeJpg width height) jpg width height = DisplayData (MimeJpg width height)
-- | Convert from a string into base 64 encoded data. -- | Convert from a string into base 64 encoded data.
encode64 :: String -> Base64 encode64 :: String -> Base64
...@@ -84,5 +92,5 @@ base64 = Base64.encode ...@@ -84,5 +92,5 @@ base64 = Base64.encode
-- | For internal use within IHaskell. -- | For internal use within IHaskell.
-- Serialize displays to a ByteString. -- Serialize displays to a ByteString.
serializeDisplay :: [DisplayData] -> ByteString serializeDisplay :: Display -> ByteString
serializeDisplay = Serialize.encode serializeDisplay = Serialize.encode
...@@ -216,7 +216,7 @@ type Publisher = (EvaluationResult -> IO ()) ...@@ -216,7 +216,7 @@ type Publisher = (EvaluationResult -> IO ())
-- | Output of a command evaluation. -- | Output of a command evaluation.
data EvalOut = EvalOut { data EvalOut = EvalOut {
evalStatus :: ErrorOccurred, evalStatus :: ErrorOccurred,
evalResult :: [DisplayData], evalResult :: Display,
evalState :: KernelState, evalState :: KernelState,
evalPager :: String evalPager :: String
} }
...@@ -232,7 +232,7 @@ evaluate kernelState code output = do ...@@ -232,7 +232,7 @@ evaluate kernelState code output = do
when (getLintStatus kernelState /= LintOff) $ liftIO $ do when (getLintStatus kernelState /= LintOff) $ liftIO $ do
lintSuggestions <- lint cmds lintSuggestions <- lint cmds
unless (null lintSuggestions) $ unless (noResults lintSuggestions) $
output $ FinalResult lintSuggestions "" output $ FinalResult lintSuggestions ""
updated <- runUntilFailure kernelState (map unloc cmds ++ [storeItCommand execCount]) updated <- runUntilFailure kernelState (map unloc cmds ++ [storeItCommand execCount])
...@@ -240,6 +240,9 @@ evaluate kernelState code output = do ...@@ -240,6 +240,9 @@ evaluate kernelState code output = do
getExecutionCounter = execCount + 1 getExecutionCounter = execCount + 1
} }
where where
noResults (Display res) = null res
noResults (ManyDisplay res) = all noResults res
runUntilFailure :: KernelState -> [CodeBlock] -> Interpreter KernelState runUntilFailure :: KernelState -> [CodeBlock] -> Interpreter KernelState
runUntilFailure state [] = return state runUntilFailure state [] = return state
runUntilFailure state (cmd:rest) = do runUntilFailure state (cmd:rest) = do
...@@ -248,7 +251,7 @@ evaluate kernelState code output = do ...@@ -248,7 +251,7 @@ evaluate kernelState code output = do
-- Output things only if they are non-empty. -- Output things only if they are non-empty.
let result = evalResult evalOut let result = evalResult evalOut
helpStr = evalPager evalOut helpStr = evalPager evalOut
unless (null result && null helpStr) $ unless (noResults result && null helpStr) $
liftIO $ output $ FinalResult result helpStr liftIO $ output $ FinalResult result helpStr
let newState = evalState evalOut let newState = evalState evalOut
...@@ -302,7 +305,7 @@ doc sdoc = do ...@@ -302,7 +305,7 @@ doc sdoc = do
wrapExecution :: KernelState wrapExecution :: KernelState
-> Interpreter [DisplayData] -> Interpreter Display
-> Interpreter EvalOut -> Interpreter EvalOut
wrapExecution state exec = safely state $ exec >>= \res -> wrapExecution state exec = safely state $ exec >>= \res ->
return EvalOut { return EvalOut {
...@@ -328,7 +331,7 @@ evalCommand _ (Import importStr) state = wrapExecution state $ do ...@@ -328,7 +331,7 @@ evalCommand _ (Import importStr) state = wrapExecution state $ do
return $ if "Test.Hspec" `isInfixOf` importStr return $ if "Test.Hspec" `isInfixOf` importStr
then displayError $ "Warning: Hspec is unusable in IHaskell until the resolution of GHC bug #8639." ++ then displayError $ "Warning: Hspec is unusable in IHaskell until the resolution of GHC bug #8639." ++
"\nThe variable `it` is shadowed and cannot be accessed, even in qualified form." "\nThe variable `it` is shadowed and cannot be accessed, even in qualified form."
else [] else Display []
where where
implicitImportOf :: ImportDecl RdrName -> InteractiveImport -> Bool implicitImportOf :: ImportDecl RdrName -> InteractiveImport -> Bool
implicitImportOf _ (IIModule _) = False implicitImportOf _ (IIModule _) = False
...@@ -382,7 +385,7 @@ evalCommand _ (Directive SetExtension exts) state = wrapExecution state $ do ...@@ -382,7 +385,7 @@ evalCommand _ (Directive SetExtension exts) state = wrapExecution state $ do
write $ "Extension: " ++ exts write $ "Extension: " ++ exts
results <- mapM setExtension (words exts) results <- mapM setExtension (words exts)
case catMaybes results of case catMaybes results of
[] -> return [] [] -> return $ Display []
errors -> return $ displayError $ intercalate "\n" errors errors -> return $ displayError $ intercalate "\n" errors
evalCommand _ (Directive GetType expr) state = wrapExecution state $ do evalCommand _ (Directive GetType expr) state = wrapExecution state $ do
...@@ -414,7 +417,7 @@ evalCommand _ (Directive SetOpt option) state = do ...@@ -414,7 +417,7 @@ evalCommand _ (Directive SetOpt option) state = do
newState = setOpt opt state newState = setOpt opt state
out = case newState of out = case newState of
Nothing -> displayError $ "Unknown option: " ++ opt Nothing -> displayError $ "Unknown option: " ++ opt
Just _ -> [] Just _ -> Display []
return EvalOut { return EvalOut {
evalStatus = if isJust newState then Success else Failure, evalStatus = if isJust newState then Success else Failure,
...@@ -462,7 +465,7 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $ ...@@ -462,7 +465,7 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
if exists if exists
then do then do
setCurrentDirectory directory setCurrentDirectory directory
return [] return $ Display []
else else
return $ displayError $ printf "No such directory: '%s'" directory return $ displayError $ printf "No such directory: '%s'" directory
cmd -> do cmd -> do
...@@ -490,7 +493,7 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $ ...@@ -490,7 +493,7 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
-- Maximum size of the output (after which we truncate). -- Maximum size of the output (after which we truncate).
maxSize = 100 * 1000 maxSize = 100 * 1000
incSize = 200 incSize = 200
output str = publish $ IntermediateResult [plain str] output str = publish $ IntermediateResult $ Display [plain str]
loop = do loop = do
-- Wait and then check if the computation is done. -- Wait and then check if the computation is done.
...@@ -516,11 +519,11 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $ ...@@ -516,11 +519,11 @@ evalCommand publish (Directive ShellCmd ('!':cmd)) state = wrapExecution state $
else do else do
out <- readMVar outputAccum out <- readMVar outputAccum
case fromJust exitCode of case fromJust exitCode of
ExitSuccess -> return [plain out] ExitSuccess -> return $ Display [plain out]
ExitFailure code -> do ExitFailure code -> do
let errMsg = "Process exited with error code " ++ show code let errMsg = "Process exited with error code " ++ show code
htmlErr = printf "<span class='err-msg'>%s</span>" errMsg htmlErr = printf "<span class='err-msg'>%s</span>" errMsg
return [plain $ out ++ "\n" ++ errMsg, return $ Display [plain $ out ++ "\n" ++ errMsg,
html $ printf "<span class='mono'>%s</span>" out ++ htmlErr] html $ printf "<span class='mono'>%s</span>" out ++ htmlErr]
loop loop
...@@ -531,7 +534,7 @@ evalCommand _ (Directive GetHelp _) state = do ...@@ -531,7 +534,7 @@ evalCommand _ (Directive GetHelp _) state = do
write "Help via :help or :?." write "Help via :help or :?."
return EvalOut { return EvalOut {
evalStatus = Success, evalStatus = Success,
evalResult = [out], evalResult = Display [out],
evalState = state, evalState = state,
evalPager = "" evalPager = ""
} }
...@@ -595,7 +598,7 @@ evalCommand _ (Directive GetInfo str) state = safely state $ do ...@@ -595,7 +598,7 @@ evalCommand _ (Directive GetInfo str) state = safely state $ do
return EvalOut { return EvalOut {
evalStatus = Success, evalStatus = Success,
evalResult = [], evalResult = Display [],
evalState = state, evalState = state,
evalPager = output evalPager = output
} }
...@@ -610,7 +613,7 @@ evalCommand _ (Directive GetDoc query) state = safely state $ do ...@@ -610,7 +613,7 @@ evalCommand _ (Directive GetDoc query) state = safely state $ do
evalCommand output (Statement stmt) state = wrapExecution state $ do evalCommand output (Statement stmt) state = wrapExecution state $ do
write $ "Statement:\n" ++ stmt write $ "Statement:\n" ++ stmt
let outputter str = output $ IntermediateResult [plain str] let outputter str = output $ IntermediateResult $ Display [plain str]
(printed, result) <- capturedStatement outputter stmt (printed, result) <- capturedStatement outputter stmt
case result of case result of
RunOk names -> do RunOk names -> do
...@@ -628,7 +631,7 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do ...@@ -628,7 +631,7 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do
-- Display the types of all bound names if the option is on. -- Display the types of all bound names if the option is on.
-- This is similar to GHCi :set +t. -- This is similar to GHCi :set +t.
if not $ useShowTypes state if not $ useShowTypes state
then return output then return $ Display output
else do else do
-- Get all the type strings. -- Get all the type strings.
types <- forM nonItNames $ \name -> do types <- forM nonItNames $ \name -> do
...@@ -639,11 +642,11 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do ...@@ -639,11 +642,11 @@ evalCommand output (Statement stmt) state = wrapExecution state $ do
htmled = unlines $ map formatGetType types htmled = unlines $ map formatGetType types
return $ case extractPlain output of return $ case extractPlain output of
"" -> [html htmled] "" -> Display [html htmled]
-- Return plain and html versions. -- Return plain and html versions.
-- Previously there was only a plain version. -- Previously there was only a plain version.
text -> text -> Display
[plain $ joined ++ "\n" ++ text, [plain $ joined ++ "\n" ++ text,
html $ htmled ++ mono text] html $ htmled ++ mono text]
...@@ -654,7 +657,7 @@ evalCommand output (Expression expr) state = do ...@@ -654,7 +657,7 @@ evalCommand output (Expression expr) state = do
write $ "Expression:\n" ++ expr write $ "Expression:\n" ++ expr
-- Try to use `display` to convert our type into the output -- Try to use `display` to convert our type into the output
-- DisplayData. If typechecking fails and there is no appropriate -- Dislay If typechecking fails and there is no appropriate
-- typeclass instance, this will throw an exception and thus `attempt` will -- typeclass instance, this will throw an exception and thus `attempt` will
-- return False, and we just resort to plaintext. -- return False, and we just resort to plaintext.
let displayExpr = printf "(IHaskell.Display.display (%s))" expr :: String let displayExpr = printf "(IHaskell.Display.display (%s))" expr :: String
...@@ -686,24 +689,27 @@ evalCommand output (Expression expr) state = do ...@@ -686,24 +689,27 @@ evalCommand output (Expression expr) state = do
-- Check if the error is due to trying to print something that doesn't -- Check if the error is due to trying to print something that doesn't
-- implement the Show typeclass. -- implement the Show typeclass.
isShowError errs = isShowError (ManyDisplay _) = False
isShowError (Display errs) =
-- Note that we rely on this error message being 'type cleaned', so -- Note that we rely on this error message being 'type cleaned', so
-- that `Show` is not displayed as GHC.Show.Show. -- that `Show` is not displayed as GHC.Show.Show.
startswith "No instance for (Show" msg && startswith "No instance for (Show" msg &&
isInfixOf " arising from a use of `print'" msg isInfixOf " arising from a use of `print'" msg
where msg = extractPlain errs where msg = extractPlain errs
isPlain (Display mime _) = mime == PlainText isSvg (DisplayData mime _) = mime == MimeSvg
isSvg (Display mime _) = mime == MimeSvg
removeSvg (Display disps) = Display $ filter (not . isSvg) disps
removeSvg (ManyDisplay disps) = ManyDisplay $ map removeSvg disps
useDisplay displayExpr = wrapExecution state $ do useDisplay displayExpr = wrapExecution state $ do
-- If there are instance matches, convert the object into -- If there are instance matches, convert the object into
-- a [DisplayData]. We also serialize it into a bytestring. We get -- a Display. We also serialize it into a bytestring. We get
-- the bytestring as a dynamic and then convert back to -- the bytestring as a dynamic and then convert back to
-- a bytestring, which we promptly unserialize. Note that -- a bytestring, which we promptly unserialize. Note that
-- attempting to do this without the serialization to binary and -- attempting to do this without the serialization to binary and
-- back gives very strange errors - all the types match but it -- back gives very strange errors - all the types match but it
-- refuses to decode back into a [DisplayData]. -- refuses to decode back into a Display.
-- Suppress output, so as not to mess up console. -- Suppress output, so as not to mess up console.
out <- capturedStatement (const $ return ()) displayExpr out <- capturedStatement (const $ return ()) displayExpr
...@@ -713,20 +719,19 @@ evalCommand output (Expression expr) state = do ...@@ -713,20 +719,19 @@ evalCommand output (Expression expr) state = do
Just bytestring -> Just bytestring ->
case Serialize.decode bytestring of case Serialize.decode bytestring of
Left err -> error err Left err -> error err
Right displayData -> do Right display -> do
write $ show displayData
return $ return $
if useSvg state if useSvg state
then displayData then display
else filter (not . isSvg) displayData else removeSvg display
postprocessShowError :: EvalOut -> EvalOut postprocessShowError :: EvalOut -> EvalOut
postprocessShowError evalOut = evalOut { evalResult = map postprocess disps } postprocessShowError evalOut = evalOut { evalResult = Display $ map postprocess disps }
where where
disps = evalResult evalOut Display disps = evalResult evalOut
text = extractPlain disps text = extractPlain disps
postprocess (Display MimeHtml _) = html $ printf fmt unshowableType (formatErrorWithClass "err-msg collapse" text) script postprocess (DisplayData MimeHtml _) = html $ printf fmt unshowableType (formatErrorWithClass "err-msg collapse" text) script
where where
fmt = "<div class='collapse-group'><span class='btn' href='#' id='unshowable'>Unshowable:<span class='show-type'>%s</span></span>%s</div><script>%s</script>" fmt = "<div class='collapse-group'><span class='btn' href='#' id='unshowable'>Unshowable:<span class='show-type'>%s</span></span>%s</div><script>%s</script>"
script = unlines [ script = unlines [
...@@ -763,14 +768,14 @@ evalCommand _ (Declaration decl) state = wrapExecution state $ do ...@@ -763,14 +768,14 @@ evalCommand _ (Declaration decl) state = wrapExecution state $ do
-- Display the types of all bound names if the option is on. -- Display the types of all bound names if the option is on.
-- This is similar to GHCi :set +t. -- This is similar to GHCi :set +t.
if not $ useShowTypes state if not $ useShowTypes state
then return [] then return $ Display []
else do else do
-- Get all the type strings. -- Get all the type strings.
types <- forM nonDataNames $ \name -> do types <- forM nonDataNames $ \name -> do
theType <- showSDocUnqual dflags . ppr <$> exprType name theType <- showSDocUnqual dflags . ppr <$> exprType name
return $ name ++ " :: " ++ theType return $ name ++ " :: " ++ theType
return [html $ unlines $ map formatGetType types] return $ Display [html $ unlines $ map formatGetType types]
evalCommand _ (TypeSignature sig) state = wrapExecution state $ evalCommand _ (TypeSignature sig) state = wrapExecution state $
-- We purposefully treat this as a "success" because that way execution -- We purposefully treat this as a "success" because that way execution
...@@ -792,7 +797,7 @@ evalCommand _ (ParseError loc err) state = do ...@@ -792,7 +797,7 @@ evalCommand _ (ParseError loc err) state = do
hoogleResults :: KernelState -> [Hoogle.HoogleResult] -> EvalOut hoogleResults :: KernelState -> [Hoogle.HoogleResult] -> EvalOut
hoogleResults state results = EvalOut { hoogleResults state results = EvalOut {
evalStatus = Success, evalStatus = Success,
evalResult = [], evalResult = Display [],
evalState = state, evalState = state,
evalPager = output evalPager = output
} }
...@@ -826,7 +831,7 @@ readChars handle delims nchars = do ...@@ -826,7 +831,7 @@ readChars handle delims nchars = do
Left _ -> return [] Left _ -> return []
doLoadModule :: String -> String -> Ghc [DisplayData] doLoadModule :: String -> String -> Ghc Display
doLoadModule name modName = flip gcatch unload $ do doLoadModule name modName = flip gcatch unload $ do
-- Compile loaded modules. -- Compile loaded modules.
flags <- getSessionDynFlags flags <- getSessionDynFlags
...@@ -854,10 +859,10 @@ doLoadModule name modName = flip gcatch unload $ do ...@@ -854,10 +859,10 @@ doLoadModule name modName = flip gcatch unload $ do
setSessionDynFlags flags{ hscTarget = HscInterpreted } setSessionDynFlags flags{ hscTarget = HscInterpreted }
case result of case result of
Succeeded -> return [] Succeeded -> return $ Display []
Failed -> return $ displayError $ "Failed to load module " ++ modName Failed -> return $ displayError $ "Failed to load module " ++ modName
where where
unload :: SomeException -> Ghc [DisplayData] unload :: SomeException -> Ghc Display
unload exception = do unload exception = do
-- Explicitly clear targets -- Explicitly clear targets
setTargets [] setTargets []
...@@ -1036,11 +1041,11 @@ formatParseError (Loc line col) = ...@@ -1036,11 +1041,11 @@ formatParseError (Loc line col) =
formatGetType :: String -> String formatGetType :: String -> String
formatGetType = printf "<span class='get-type'>%s</span>" formatGetType = printf "<span class='get-type'>%s</span>"
formatType :: String -> [DisplayData] formatType :: String -> Display
formatType typeStr = [plain typeStr, html $ formatGetType typeStr] formatType typeStr = Display [plain typeStr, html $ formatGetType typeStr]
displayError :: ErrMsg -> [DisplayData] displayError :: ErrMsg -> Display
displayError msg = [plain . fixStdinError . typeCleaner $ msg, html $ formatError msg] displayError msg = Display [plain . fixStdinError . typeCleaner $ msg, html $ formatError msg]
fixStdinError :: ErrMsg -> ErrMsg fixStdinError :: ErrMsg -> ErrMsg
fixStdinError err = fixStdinError err =
......
...@@ -38,7 +38,7 @@ lintIdent = "lintIdentAEjlkQeh" ...@@ -38,7 +38,7 @@ lintIdent = "lintIdentAEjlkQeh"
-- | Given parsed code chunks, perform linting and output a displayable -- | Given parsed code chunks, perform linting and output a displayable
-- report on linting warnings and errors. -- report on linting warnings and errors.
lint :: [Located CodeBlock] -> IO [DisplayData] lint :: [Located CodeBlock] -> IO Display
lint blocks = do lint blocks = do
let validBlocks = map makeValid blocks let validBlocks = map makeValid blocks
fileContents = joinBlocks validBlocks fileContents = joinBlocks validBlocks
...@@ -50,8 +50,8 @@ lint blocks = do ...@@ -50,8 +50,8 @@ lint blocks = do
suggestions <- catMaybes <$> map parseSuggestion <$> hlint [filename, "--quiet"] suggestions <- catMaybes <$> map parseSuggestion <$> hlint [filename, "--quiet"]
return $ return $
if null suggestions if null suggestions
then [] then Display []
else else Display
[plain $ concatMap plainSuggestion suggestions, html $ htmlSuggestions suggestions] [plain $ concatMap plainSuggestion suggestions, html $ htmlSuggestions suggestions]
where where
-- Join together multiple valid file blocks into a single file. -- Join together multiple valid file blocks into a single file.
......
...@@ -20,12 +20,15 @@ module IHaskell.Types ( ...@@ -20,12 +20,15 @@ module IHaskell.Types (
Width, Height, Width, Height,
FrontendType(..), FrontendType(..),
ViewFormat(..), ViewFormat(..),
Display(..),
defaultKernelState, defaultKernelState,
extractPlain extractPlain
) where ) where
import ClassyPrelude import ClassyPrelude
import qualified Data.ByteString.Char8 as Char import qualified Data.ByteString.Char8 as Char
import Data.Serialize
import GHC.Generics
import Text.Read as Read hiding (pfail, String) import Text.Read as Read hiding (pfail, String)
import Text.ParserCombinators.ReadP import Text.ParserCombinators.ReadP
...@@ -60,6 +63,12 @@ instance Read ViewFormat where ...@@ -60,6 +63,12 @@ instance Read ViewFormat where
"md" -> return Markdown "md" -> return Markdown
_ -> pfail _ -> pfail
-- | Wrapper for ipython-kernel's DisplayData which allows sending multiple
-- results from the same expression.
data Display = Display [DisplayData]
| ManyDisplay [Display]
deriving (Show, Typeable, Generic)
instance Serialize Display
-- | All state stored in the kernel between executions. -- | All state stored in the kernel between executions.
data KernelState = KernelState data KernelState = KernelState
...@@ -108,9 +117,9 @@ data EvaluationResult = ...@@ -108,9 +117,9 @@ data EvaluationResult =
-- | An intermediate result which communicates what has been printed thus -- | An intermediate result which communicates what has been printed thus
-- far. -- far.
IntermediateResult { IntermediateResult {
outputs :: [DisplayData] -- ^ Display outputs. outputs :: Display -- ^ Display outputs.
} }
| FinalResult { | FinalResult {
outputs :: [DisplayData], -- ^ Display outputs. outputs :: Display, -- ^ Display outputs.
pagerOut :: String -- ^ Text to display in the IPython pager. pagerOut :: String -- ^ Text to display in the IPython pager.
} }
...@@ -252,7 +252,8 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do ...@@ -252,7 +252,8 @@ replyTo interface req@ExecuteRequest{ getCode = code } replyHeader state = do
header <- dupHeader replyHeader ClearOutputMessage header <- dupHeader replyHeader ClearOutputMessage
send $ ClearOutput header True send $ ClearOutput header True
sendOutput outs = do sendOutput (ManyDisplay manyOuts) = mapM_ sendOutput manyOuts
sendOutput (Display outs) = do
header <- dupHeader replyHeader DisplayDataMessage header <- dupHeader replyHeader DisplayDataMessage
send $ PublishDisplayData header "haskell" outs send $ PublishDisplayData header "haskell" outs
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment