Commit 16a4fcad authored by Sumit Sahrawat's avatar Sumit Sahrawat

More minor fixes

- Make hlint happy.
- Reformat using hindent.
- Add explanatory comments in some places.
- Use Control.Monad.foldM for IHaskell.Eval.Widgets.widgetHandler
parent 6a1e9120
...@@ -71,15 +71,16 @@ mkButton = do ...@@ -71,15 +71,16 @@ mkButton = do
ttip <- newIORef "" ttip <- newIORef ""
dis <- newIORef False dis <- newIORef False
sty <- newIORef None sty <- newIORef None
fun <- newIORef (\_ -> return ()) fun <- newIORef $ const $ return ()
let b = Button { uuid = commUUID let b = Button
, description = desc { uuid = commUUID
, tooltip = ttip , description = desc
, disabled = dis , tooltip = ttip
, buttonStyle = sty , disabled = dis
, clickHandler = fun , buttonStyle = sty
} , clickHandler = fun
}
-- Open a comm for this widget, and store it in the kernel state -- Open a comm for this widget, and store it in the kernel state
widgetSendOpen b (toJSON ButtonInitData) (toJSON b) widgetSendOpen b (toJSON ButtonInitData) (toJSON b)
...@@ -87,9 +88,8 @@ mkButton = do ...@@ -87,9 +88,8 @@ mkButton = do
-- Return the button widget -- Return the button widget
return b return b
-- | Send an update msg for a button, with custom json. Make it easy -- | Send an update msg for a button, with custom json. Make it easy to update fragments of the
-- to update fragments of the state, by accepting a Pair instead of a -- state, by accepting a Pair instead of a Value.
-- Value.
update :: Button -> [Pair] -> IO () update :: Button -> [Pair] -> IO ()
update b v = widgetSendUpdate b . toJSON . object $ v update b v = widgetSendUpdate b . toJSON . object $ v
...@@ -125,7 +125,7 @@ setButtonStatus b stat = do ...@@ -125,7 +125,7 @@ setButtonStatus b stat = do
-- | Toggle the button -- | Toggle the button
toggleButtonStatus :: Button -> IO () toggleButtonStatus :: Button -> IO ()
toggleButtonStatus b = do toggleButtonStatus b = do
oldVal <- isDisabled b oldVal <- getButtonStatus b
let newVal = not oldVal let newVal = not oldVal
modify b disabled newVal modify b disabled newVal
update b ["disabled" .= newVal] update b ["disabled" .= newVal]
...@@ -144,7 +144,7 @@ getButtonTooltip = readIORef . tooltip ...@@ -144,7 +144,7 @@ getButtonTooltip = readIORef . tooltip
-- | Check whether the button is enabled / disabled -- | Check whether the button is enabled / disabled
getButtonStatus :: Button -> IO Bool getButtonStatus :: Button -> IO Bool
getButtonStatus = not . readIORef . disabled getButtonStatus = fmap not . readIORef . disabled
-- | Set a function to be activated on click -- | Set a function to be activated on click
setClickHandler :: Button -> (Button -> IO ()) -> IO () setClickHandler :: Button -> (Button -> IO ()) -> IO ()
......
...@@ -293,8 +293,10 @@ replyTo _ HistoryRequest{} replyHeader state = do ...@@ -293,8 +293,10 @@ replyTo _ HistoryRequest{} replyHeader state = do
} }
return (state, reply) return (state, reply)
-- | Handle comm messages
handleComm :: (Message -> IO ()) -> KernelState -> Message -> MessageHeader -> Interpreter KernelState handleComm :: (Message -> IO ()) -> KernelState -> Message -> MessageHeader -> Interpreter KernelState
handleComm send kernelState req replyHeader = do handleComm send kernelState req replyHeader = do
-- MVars to hold intermediate data during publishing
displayed <- liftIO $ newMVar [] displayed <- liftIO $ newMVar []
updateNeeded <- liftIO $ newMVar False updateNeeded <- liftIO $ newMVar False
pagerOutput <- liftIO $ newMVar [] pagerOutput <- liftIO $ newMVar []
...@@ -306,8 +308,14 @@ handleComm send kernelState req replyHeader = do ...@@ -306,8 +308,14 @@ handleComm send kernelState req replyHeader = do
head <- dupHeader replyHeader CommDataMessage head <- dupHeader replyHeader CommDataMessage
send $ CommData head uuid value send $ CommData head uuid value
toUsePager = usePager kernelState toUsePager = usePager kernelState
run = capturedIO publish kernelState
-- Create a publisher according to current state, use that to build
-- a function that executes an IO action and publishes the output to
-- the frontend simultaneously.
let run = capturedIO publish kernelState
publish = publishResult send replyHeader displayed updateNeeded pagerOutput toUsePager publish = publishResult send replyHeader displayed updateNeeded pagerOutput toUsePager
-- Notify the frontend that the kernel is busy
busyHeader <- liftIO $ dupHeader replyHeader StatusMessage busyHeader <- liftIO $ dupHeader replyHeader StatusMessage
liftIO . send $ PublishStatus busyHeader Busy liftIO . send $ PublishStatus busyHeader Busy
...@@ -326,6 +334,7 @@ handleComm send kernelState req replyHeader = do ...@@ -326,6 +334,7 @@ handleComm send kernelState req replyHeader = do
liftIO $ publish $ FinalResult disp (if toUsePager then pgrOut else []) [] liftIO $ publish $ FinalResult disp (if toUsePager then pgrOut else []) []
return kernelState { openComms = Map.delete uuid widgets } return kernelState { openComms = Map.delete uuid widgets }
-- Notify the frontend that the kernel is idle once again
idleHeader <- liftIO $ dupHeader replyHeader StatusMessage idleHeader <- liftIO $ dupHeader replyHeader StatusMessage
liftIO . send $ PublishStatus idleHeader Idle liftIO . send $ PublishStatus idleHeader Idle
......
...@@ -87,7 +87,6 @@ import IHaskell.Eval.Util ...@@ -87,7 +87,6 @@ import IHaskell.Eval.Util
import IHaskell.Eval.Widgets import IHaskell.Eval.Widgets
import IHaskell.BrokenPackages import IHaskell.BrokenPackages
import qualified IHaskell.IPython.Message.UUID as UUID import qualified IHaskell.IPython.Message.UUID as UUID
import IHaskell.Eval.Widgets
import StringUtils (replace, split, strip, rstrip) import StringUtils (replace, split, strip, rstrip)
import Paths_ihaskell (version) import Paths_ihaskell (version)
...@@ -228,7 +227,7 @@ initializeImports = do ...@@ -228,7 +227,7 @@ initializeImports = do
dropFirstAndLast = reverse . drop 1 . reverse . drop 1 dropFirstAndLast = reverse . drop 1 . reverse . drop 1
toImportStmt :: String -> String toImportStmt :: String -> String
toImportStmt = printf importFmt . concat . map capitalize . dropFirstAndLast . split "-" toImportStmt = printf importFmt . concatMap capitalize . dropFirstAndLast . split "-"
displayImports = map toImportStmt displayPackages displayImports = map toImportStmt displayPackages
...@@ -242,7 +241,7 @@ initializeImports = do ...@@ -242,7 +241,7 @@ initializeImports = do
-- | Give a value for the `it` variable. -- | Give a value for the `it` variable.
initializeItVariable :: Interpreter () initializeItVariable :: Interpreter ()
initializeItVariable = do initializeItVariable =
-- This is required due to the way we handle `it` in the wrapper statements - if it doesn't exist, -- This is required due to the way we handle `it` in the wrapper statements - if it doesn't exist,
-- the first statement will fail. -- the first statement will fail.
void $ runStmt "let it = ()" RunToCompletion void $ runStmt "let it = ()" RunToCompletion
...@@ -344,6 +343,8 @@ evaluate kernelState code output widgetHandler = do ...@@ -344,6 +343,8 @@ evaluate kernelState code output widgetHandler = do
storeItCommand execCount = Statement $ printf "let it%d = it" execCount storeItCommand execCount = Statement $ printf "let it%d = it" execCount
-- | Compile a string and extract a value from it. Effectively extract the result of an expression
-- from inside the notebook environment.
extractValue :: Typeable a => String -> Interpreter a extractValue :: Typeable a => String -> Interpreter a
extractValue expr = do extractValue expr = do
compiled <- dynCompileExpr expr compiled <- dynCompileExpr expr
...@@ -502,7 +503,7 @@ evalCommand output (Directive SetDynFlag flagsStr) state = safely state $ do ...@@ -502,7 +503,7 @@ evalCommand output (Directive SetDynFlag flagsStr) state = safely state $ do
} }
else do else do
-- Apply all IHaskell flag updaters to the state to get the new state -- Apply all IHaskell flag updaters to the state to get the new state
let state' = (foldl' (.) id (map (fromJust . ihaskellFlagUpdater) ihaskellFlags)) state let state' = foldl' (.) id (map (fromJust . ihaskellFlagUpdater) ihaskellFlags) state
errs <- setFlags ghcFlags errs <- setFlags ghcFlags
let display = let display =
case errs of case errs of
...@@ -763,7 +764,7 @@ evalCommand _ (Directive GetDoc query) state = safely state $ do ...@@ -763,7 +764,7 @@ evalCommand _ (Directive GetDoc query) state = safely state $ do
return $ hoogleResults state results return $ hoogleResults state results
evalCommand output (Statement stmt) state = wrapExecution state $ evalStatementOrIO output state evalCommand output (Statement stmt) state = wrapExecution state $ evalStatementOrIO output state
(Left stmt) (CapturedStmt stmt)
evalCommand output (Expression expr) state = do evalCommand output (Expression expr) state = do
write state $ "Expression:\n" ++ expr write state $ "Expression:\n" ++ expr
...@@ -792,7 +793,7 @@ evalCommand output (Expression expr) state = do ...@@ -792,7 +793,7 @@ evalCommand output (Expression expr) state = do
-- If it typechecks as a DecsQ, we do not want to display the DecsQ, we just want the -- If it typechecks as a DecsQ, we do not want to display the DecsQ, we just want the
-- declaration made. -- declaration made.
do do
write state $ "Suppressing display for template haskell declaration" write state "Suppressing display for template haskell declaration"
GHC.runDecls expr GHC.runDecls expr
return return
EvalOut EvalOut
...@@ -802,24 +803,23 @@ evalCommand output (Expression expr) state = do ...@@ -802,24 +803,23 @@ evalCommand output (Expression expr) state = do
, evalPager = "" , evalPager = ""
, evalMsgs = [] , evalMsgs = []
} }
else do else if canRunDisplay
if canRunDisplay then
then do -- Use the display. As a result, `it` is set to the output.
-- Use the display. As a result, `it` is set to the output. useDisplay displayExpr
useDisplay displayExpr else do
else do -- Evaluate this expression as though it's just a statement. The output is bound to 'it', so we can
-- Evaluate this expression as though it's just a statement. The output is bound to 'it', so we can -- then use it.
-- then use it. evalOut <- evalCommand output (Statement expr) state
evalOut <- evalCommand output (Statement expr) state
let out = evalResult evalOut
let out = evalResult evalOut showErr = isShowError out
showErr = isShowError out
-- If evaluation failed, return the failure. If it was successful, we may be able to use the
-- If evaluation failed, return the failure. If it was successful, we may be able to use the -- IHaskellDisplay typeclass.
-- IHaskellDisplay typeclass. return $ if not showErr || useShowErrors state
return $ if not showErr || useShowErrors state then evalOut
then evalOut else postprocessShowError evalOut
else postprocessShowError evalOut
where where
-- Try to evaluate an action. Return True if it succeeds and False if it throws an exception. The -- Try to evaluate an action. Return True if it succeeds and False if it throws an exception. The
...@@ -990,7 +990,7 @@ doLoadModule name modName = do ...@@ -990,7 +990,7 @@ doLoadModule name modName = do
oldTargets <- getTargets oldTargets <- getTargets
-- Add a target, but make sure targets are unique! -- Add a target, but make sure targets are unique!
addTarget target addTarget target
getTargets >>= return . (nubBy ((==) `on` targetId)) >>= setTargets getTargets >>= return . nubBy ((==) `on` targetId) >>= setTargets
result <- load LoadAllTargets result <- load LoadAllTargets
-- Reset the context, since loading things screws it up. -- Reset the context, since loading things screws it up.
...@@ -1052,8 +1052,11 @@ keepingItVariable act = do ...@@ -1052,8 +1052,11 @@ keepingItVariable act = do
goStmt $ printf "let it = %s" itVariable goStmt $ printf "let it = %s" itVariable
act act
data Captured a = CapturedStmt String
| CapturedIO (IO a)
capturedEval :: (String -> IO ()) -- ^ Function used to publish intermediate output. capturedEval :: (String -> IO ()) -- ^ Function used to publish intermediate output.
-> Either String (IO a) -- ^ Statement to evaluate. -> Captured a -- ^ Statement to evaluate.
-> Interpreter (String, RunResult) -- ^ Return the output and result. -> Interpreter (String, RunResult) -- ^ Return the output and result.
capturedEval output stmt = do capturedEval output stmt = do
-- Generate random variable names to use so that we cannot accidentally override the variables by -- Generate random variable names to use so that we cannot accidentally override the variables by
...@@ -1099,8 +1102,8 @@ capturedEval output stmt = do ...@@ -1099,8 +1102,8 @@ capturedEval output stmt = do
goStmt :: String -> Ghc RunResult goStmt :: String -> Ghc RunResult
goStmt s = runStmt s RunToCompletion goStmt s = runStmt s RunToCompletion
runWithResult (Left str) = goStmt str runWithResult (CapturedStmt str) = goStmt str
runWithResult (Right io) = do runWithResult (CapturedIO io) = do
status <- gcatch (liftIO io >> return NoException) (return . AnyException) status <- gcatch (liftIO io >> return NoException) (return . AnyException)
return $ return $
case status of case status of
...@@ -1185,20 +1188,21 @@ capturedIO :: Publisher -> KernelState -> IO a -> Interpreter Display ...@@ -1185,20 +1188,21 @@ capturedIO :: Publisher -> KernelState -> IO a -> Interpreter Display
capturedIO publish state action = do capturedIO publish state action = do
let showError = return . displayError . show let showError = return . displayError . show
handler e@SomeException{} = showError e handler e@SomeException{} = showError e
gcatch (evalStatementOrIO publish state (Right action)) handler gcatch (evalStatementOrIO publish state (CapturedIO action)) handler
evalStatementOrIO :: Publisher -> KernelState -> Either String (IO a) -> Interpreter Display -- | Evaluate a @Captured@, and then publish the final result to the frontend. Returns the final
-- Display.
evalStatementOrIO :: Publisher -> KernelState -> Captured a -> Interpreter Display
evalStatementOrIO publish state cmd = do evalStatementOrIO publish state cmd = do
let output str = publish . IntermediateResult $ Display [plain str] let output str = publish . IntermediateResult $ Display [plain str]
(printed, result) <- case cmd of case cmd of
Left stmt -> do CapturedStmt stmt ->
write state $ "Statement:\n" ++ stmt write state $ "Statement:\n" ++ stmt
capturedEval output (Left stmt) CapturedIO io ->
Right io -> do write state "Evaluating Action"
write state $ "evalStatementOrIO in Action"
capturedEval output (Right io)
(printed, result) <- capturedEval output cmd
case result of case result of
RunOk names -> do RunOk names -> do
dflags <- getSessionDynFlags dflags <- getSessionDynFlags
......
...@@ -12,6 +12,7 @@ import IHaskellPrelude ...@@ -12,6 +12,7 @@ import IHaskellPrelude
import Control.Concurrent.Chan (writeChan) import Control.Concurrent.Chan (writeChan)
import Control.Concurrent.STM (atomically) import Control.Concurrent.STM (atomically)
import Control.Concurrent.STM.TChan import Control.Concurrent.STM.TChan
import Control.Monad (foldM)
import Data.Aeson import Data.Aeson
import qualified Data.Map as Map import qualified Data.Map as Map
import System.IO.Unsafe (unsafePerformIO) import System.IO.Unsafe (unsafePerformIO)
...@@ -57,6 +58,8 @@ widgetSendView = queue . View . Widget ...@@ -57,6 +58,8 @@ widgetSendView = queue . View . Widget
widgetSendClose :: IHaskellWidget a => a -> Value -> IO () widgetSendClose :: IHaskellWidget a => a -> Value -> IO ()
widgetSendClose = widgetSend Close widgetSendClose = widgetSend Close
-- | Handle a single widget message. Takes necessary actions according to the message type, such as
-- opening comms, storing and updating widget representation in the kernel state etc.
handleMessage :: (Message -> IO ()) handleMessage :: (Message -> IO ())
-> MessageHeader -> MessageHeader
-> KernelState -> KernelState
...@@ -66,7 +69,7 @@ handleMessage send replyHeader state msg = do ...@@ -66,7 +69,7 @@ handleMessage send replyHeader state msg = do
let oldComms = openComms state let oldComms = openComms state
case msg of case msg of
(Open widget initVal stateVal) -> do Open widget initVal stateVal -> do
let target = targetName widget let target = targetName widget
uuid = getCommUUID widget uuid = getCommUUID widget
present = isJust $ Map.lookup uuid oldComms present = isJust $ Map.lookup uuid oldComms
...@@ -78,6 +81,7 @@ handleMessage send replyHeader state msg = do ...@@ -78,6 +81,7 @@ handleMessage send replyHeader state msg = do
head <- dupHeader replyHeader CommDataMessage head <- dupHeader replyHeader CommDataMessage
send $ CommData head uuid val send $ CommData head uuid val
-- If the widget is present, don't open it again.
if present if present
then return state then return state
else do else do
...@@ -94,7 +98,7 @@ handleMessage send replyHeader state msg = do ...@@ -94,7 +98,7 @@ handleMessage send replyHeader state msg = do
-- Store the widget in the kernelState -- Store the widget in the kernelState
return newState return newState
(Close widget value) -> do Close widget value -> do
let target = targetName widget let target = targetName widget
uuid = getCommUUID widget uuid = getCommUUID widget
present = isJust $ Map.lookup uuid oldComms present = isJust $ Map.lookup uuid oldComms
...@@ -102,6 +106,7 @@ handleMessage send replyHeader state msg = do ...@@ -102,6 +106,7 @@ handleMessage send replyHeader state msg = do
newComms = Map.delete uuid $ openComms state newComms = Map.delete uuid $ openComms state
newState = state { openComms = newComms } newState = state { openComms = newComms }
-- If the widget is not present in the state, we don't close it.
if present if present
then do then do
header <- dupHeader replyHeader CommCloseMessage header <- dupHeader replyHeader CommCloseMessage
...@@ -109,24 +114,28 @@ handleMessage send replyHeader state msg = do ...@@ -109,24 +114,28 @@ handleMessage send replyHeader state msg = do
return newState return newState
else return state else return state
(View widget) -> do View widget -> do
let uuid = getCommUUID widget let uuid = getCommUUID widget
present = isJust $ Map.lookup uuid oldComms present = isJust $ Map.lookup uuid oldComms
-- If the widget is present, we send a display message on it's comm.
when present $ do when present $ do
header <- dupHeader replyHeader CommDataMessage header <- dupHeader replyHeader CommDataMessage
send . CommData header uuid $ toJSON DisplayWidget send . CommData header uuid $ toJSON DisplayWidget
return state return state
(Update widget value) -> do Update widget value -> do
-- Assume that a state update means that it is time the stored widget also gets updated. Thus -- Assume that a state update means that it is time the stored widget also gets updated. Thus
-- replace the stored widget with the copy passed in the CommMsg. -- replace the stored widget with the copy passed in the CommMsg.
let uuid = getCommUUID widget let uuid = getCommUUID widget
present = isJust $ Map.lookup uuid oldComms present = isJust $ Map.lookup uuid oldComms
-- The update inside the kernel state happens here.
newComms = Map.insert uuid widget oldComms newComms = Map.insert uuid widget oldComms
newState = state { openComms = newComms } newState = state { openComms = newComms }
-- If the widget is present, we send an update message on its comm. We also replace the widget
-- stored in the kernel state with the one provided here.
if present if present
then do then do
header <- dupHeader replyHeader CommDataMessage header <- dupHeader replyHeader CommDataMessage
...@@ -134,12 +143,10 @@ handleMessage send replyHeader state msg = do ...@@ -134,12 +143,10 @@ handleMessage send replyHeader state msg = do
return newState return newState
else return state else return state
-- Handle messages one-by-one, while updating state simultaneously
widgetHandler :: (Message -> IO ()) widgetHandler :: (Message -> IO ())
-> MessageHeader -> MessageHeader
-> KernelState -> KernelState
-> [WidgetMsg] -> [WidgetMsg]
-> IO KernelState -> IO KernelState
widgetHandler _ _ state [] = return state widgetHandler sender header = foldM (handleMessage sender header)
widgetHandler sender header state (x:xs) = do
newState <- handleMessage sender header state x
widgetHandler sender header newState xs
...@@ -14,14 +14,20 @@ import IHaskell.Types ...@@ -14,14 +14,20 @@ import IHaskell.Types
ihaskellCSS :: String ihaskellCSS :: String
ihaskellCSS = [hereFile|html/custom.css|] ihaskellCSS = [hereFile|html/custom.css|]
-- Publish outputs, ignore any CommMsgs -- | Publish evaluation results, ignore any CommMsgs. This function can be used to create a function
publishResult :: (Message -> IO ()) -- of type (EvaluationResult -> IO ()), which can be used to publish results to the frontend. The
-> MessageHeader -- resultant function shares some state between different calls by storing it inside the MVars
-> MVar [Display] -- passed while creating it using this function. Pager output is accumulated in the MVar passed for
-> MVar Bool -- this purpose if a pager is being used (indicated by an argument), and sent to the frontend
-> MVar [DisplayData] -- otherwise.
-> Bool publishResult :: (Message -> IO ()) -- ^ A function to send messages
-> EvaluationResult -> MessageHeader -- ^ Message header to use for reply
-> MVar [Display] -- ^ A MVar to use for displays
-> MVar Bool -- ^ A mutable boolean to decide whether the output need to be cleared and
-- redrawn
-> MVar [DisplayData] -- ^ A MVar to use for storing pager output
-> Bool -- ^ Whether to use the pager
-> EvaluationResult -- ^ The evaluation result
-> IO () -> IO ()
publishResult send replyHeader displayed updateNeeded pagerOutput usePager result = do publishResult send replyHeader displayed updateNeeded pagerOutput usePager result = do
let final = let final =
......
...@@ -147,4 +147,3 @@ putChar = liftIO . P.putChar ...@@ -147,4 +147,3 @@ putChar = liftIO . P.putChar
print :: (MonadIO m, Show a) => a -> m () print :: (MonadIO m, Show a) => a -> m ()
print = liftIO . P.print print = liftIO . P.print
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