Unverified Commit 726999ae authored by Vaibhav Sagar's avatar Vaibhav Sagar Committed by GitHub

Merge pull request #928 from erikd/topic/warnings-other

Turn on and fix more warnings
parents 50ff0ff7 196ccac9
......@@ -31,15 +31,14 @@ import Bag
import ErrUtils hiding (ErrMsg)
import FastString
#if MIN_VERSION_ghc(8,4,0)
import GHC hiding (Located, Parsed)
import GHC hiding (Located, Parsed, parser)
#else
import GHC hiding (Located)
import GHC hiding (Located, parser)
#endif
import Lexer
import Lexer hiding (buffer)
import OrdList
import Outputable hiding ((<>))
import SrcLoc hiding (Located)
import StringBuffer
import qualified SrcLoc as SrcLoc
import StringBuffer hiding (len)
import qualified Language.Haskell.GHC.HappyParser as Parse
......@@ -74,12 +73,48 @@ data Located a = Located {
data Parser a = Parser (P a)
-- Our parsers.
parserStatement = Parser Parse.fullStatement
parserImport = Parser Parse.fullImport
parserDeclaration = Parser Parse.fullDeclaration
parserExpression = Parser Parse.fullExpression
parserTypeSignature = Parser Parse.fullTypeSignature
parserModule = Parser Parse.fullModule
#if MIN_VERSION_ghc(8,4,0)
parserStatement :: Parser (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
#else
parserStatement :: Parser (Maybe (LStmt RdrName (LHsExpr RdrName)))
#endif
parserStatement = Parser Parse.fullStatement
#if MIN_VERSION_ghc(8,4,0)
parserImport :: Parser (LImportDecl GhcPs)
#else
parserImport :: Parser (LImportDecl RdrName)
#endif
parserImport = Parser Parse.fullImport
#if MIN_VERSION_ghc(8,4,0)
parserDeclaration :: Parser (OrdList (LHsDecl GhcPs))
#else
parserDeclaration :: Parser (OrdList (LHsDecl RdrName))
#endif
parserDeclaration = Parser Parse.fullDeclaration
#if MIN_VERSION_ghc(8,4,0)
parserExpression :: Parser (LHsExpr GhcPs)
#else
parserExpression :: Parser (LHsExpr RdrName)
#endif
parserExpression = Parser Parse.fullExpression
#if MIN_VERSION_ghc(8,4,0)
parserTypeSignature :: Parser (SrcLoc.Located (OrdList (LHsDecl GhcPs)))
#else
parserTypeSignature :: Parser (SrcLoc.Located (OrdList (LHsDecl RdrName)))
#endif
parserTypeSignature = Parser Parse.fullTypeSignature
#if MIN_VERSION_ghc(8,4,0)
parserModule :: Parser (SrcLoc.Located (HsModule GhcPs))
#else
parserModule :: Parser (SrcLoc.Located (HsModule RdrName))
#endif
parserModule = Parser Parse.fullModule
-- | Run a GHC parser on a string. Return success or failure with
-- associated information for both.
......@@ -87,7 +122,7 @@ runParser :: DynFlags -> Parser a -> String -> ParseOutput a
runParser flags (Parser parser) str =
-- Create an initial parser state.
let filename = "<interactive>"
location = mkRealSrcLoc (mkFastString filename) 1 1
location = SrcLoc.mkRealSrcLoc (mkFastString filename) 1 1
buffer = stringToStringBuffer str
parseState = mkPState flags buffer location in
-- Convert a GHC parser output into our own.
......@@ -95,48 +130,29 @@ runParser flags (Parser parser) str =
where
toParseOut :: ParseResult a -> ParseOutput a
#if MIN_VERSION_ghc(8,4,0)
toParseOut (PFailed _ span@(RealSrcSpan realSpan) err) =
toParseOut (PFailed _ spn@(RealSrcSpan realSpan) err) =
#else
toParseOut (PFailed span@(RealSrcSpan realSpan) err) =
toParseOut (PFailed spn@(RealSrcSpan realSpan) err) =
#endif
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags span err
line = srcLocLine $ realSrcSpanStart realSpan
col = srcLocCol $ realSrcSpanStart realSpan
in Failure errMsg $ Loc line col
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags spn err
ln = srcLocLine $ SrcLoc.realSrcSpanStart realSpan
col = srcLocCol $ SrcLoc.realSrcSpanStart realSpan
in Failure errMsg $ Loc ln col
#if MIN_VERSION_ghc(8,4,0)
toParseOut (PFailed _ span err) =
toParseOut (PFailed _ spn err) =
#else
toParseOut (PFailed span err) =
toParseOut (PFailed spn err) =
#endif
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags span err
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags spn err
in Failure errMsg $ Loc 0 0
toParseOut (POk parseState result) =
let parseEnd = realSrcSpanStart $ last_loc parseState
endLine = srcLocLine parseEnd
endCol = srcLocCol parseEnd
(before, after) = splitAtLoc endLine endCol str
in Parsed result
toParseOut (POk _parseState result) =
Parsed result
-- Convert the bag of errors into an error string.
printErrorBag bag = joinLines . map show $ bagToList bag
-- | Split a string at a given line and column. The column is included in
-- the second part of the split.
splitAtLoc :: LineNumber -> ColumnNumber -> String -> (String, String)
splitAtLoc line col string =
if line > length (lines string)
then (string, "")
else (before, after)
where
(beforeLines, afterLines) = splitAt line $ lines string
theLine = last beforeLines
(beforeChars, afterChars) = splitAt (col - 1) theLine
before = joinLines (init beforeLines) ++ '\n' : beforeChars
after = joinLines $ afterChars : afterLines
-- Not the same as 'unlines', due to trailing \n
joinLines :: [String] -> String
joinLines = intercalate "\n"
......@@ -151,7 +167,7 @@ layoutChunks :: String -> [Located String]
layoutChunks = joinQuasiquotes . go 1
where
go :: LineNumber -> String -> [Located String]
go line = filter (not . null . unloc) . map (fmap strip) . layoutLines line . lines
go ln = filter (not . null . unloc) . map (fmap strip) . layoutLines ln . lines
-- drop spaces on left and right
strip = dropRight . dropLeft
......@@ -165,13 +181,13 @@ layoutChunks = joinQuasiquotes . go 1
layoutLines _ [] = []
-- Use the indent of the first line to find the end of the first block.
layoutLines lineIdx all@(firstLine:rest) =
layoutLines lineIdx xs@(firstLine:rest) =
let firstIndent = indentLevel firstLine
blockEnded line = indentLevel line <= firstIndent in
blockEnded ln = indentLevel ln <= firstIndent in
case findIndex blockEnded rest of
-- If the first block doesn't end, return the whole string, since
-- that just means the block takes up the entire string.
Nothing -> [Located lineIdx $ intercalate "\n" all]
Nothing -> [Located lineIdx $ intercalate "\n" xs]
-- We found the end of the block. Split this bit out and recurse.
Just idx ->
......@@ -213,6 +229,7 @@ removeComments = removeOneLineComments . removeMultilineComments 0 0
where
dropLine = removeOneLineComments . dropWhile (/= '\n')
removeMultilineComments :: Int -> Int -> String -> String
removeMultilineComments nesting pragmaNesting str =
case str of
-- Don't remove comments after cmd directives
......@@ -253,8 +270,8 @@ removeComments = removeOneLineComments . removeMultilineComments 0 0
-- Take a part of a string that ends in an unescaped quote.
takeString str = case str of
escaped@('\\':'"':rest) -> escaped
'"':rest -> "\""
escaped@('\\':'"':_) -> escaped
'"':_ -> "\""
x:xs -> x:takeString xs
[] -> []
......
......@@ -18,6 +18,7 @@ cabal-version: >=1.16
library
build-tools: happy, cpphs
ghc-options: -Wall
exposed-modules: Language.Haskell.GHC.Parser,
Language.Haskell.GHC.HappyParser
-- other-modules:
......
......@@ -17,7 +17,6 @@ import HsSyn
import OrdList
-- compiler/parser
import RdrHsSyn
import Lexer
-- compiler/basicTypes
......
......@@ -17,12 +17,8 @@ import HsSyn
import OrdList
-- compiler/parser
import RdrHsSyn
import Lexer
-- compiler/basicTypes
import RdrName
fullStatement :: P (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
fullStatement = parseStmt
......
name: ipython-kernel
version: 0.9.1.0
version: 0.10.0.0
synopsis: A library for creating kernels for IPython frontends
description: ipython-kernel is a library for communicating with frontends for the interactive IPython framework. It is used extensively in IHaskell, the interactive Haskell environment.
......@@ -24,10 +24,11 @@ flag examples
library
ghc-options: -Wall
exposed-modules: IHaskell.IPython.Kernel
IHaskell.IPython.Types
IHaskell.IPython.ZeroMQ
IHaskell.IPython.Message.Writer
IHaskell.IPython.Message.Parser
IHaskell.IPython.Message.UUID
IHaskell.IPython.EasyKernel
......@@ -38,6 +39,7 @@ library
aeson ,
bytestring ,
cereal ,
cereal-text ,
containers ,
cryptonite ,
directory ,
......
......@@ -23,7 +23,7 @@
-- logos, help text, and so forth.
module IHaskell.IPython.EasyKernel (easyKernel, installKernelspec, KernelConfig(..)) where
import Data.Aeson (decode, encode)
import Data.Aeson (decode, encode, toJSON)
import qualified Data.ByteString.Lazy as BL
......@@ -32,7 +32,7 @@ import System.Process (rawSystem)
import Control.Concurrent (MVar, readChan, writeChan, newMVar, readMVar, modifyMVar_)
import Control.Monad.IO.Class (MonadIO(..))
import Control.Monad (forever, when, unless, void)
import Control.Monad (forever, when, void)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
......@@ -40,10 +40,8 @@ import qualified Data.Text as T
import IHaskell.IPython.Kernel
import IHaskell.IPython.Message.UUID as UUID
import IHaskell.IPython.Types
import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist,
getHomeDirectory, getTemporaryDirectory)
import System.Directory (createDirectoryIfMissing, getTemporaryDirectory)
import System.FilePath ((</>))
import System.Exit (exitSuccess)
import System.IO (openFile, IOMode(ReadMode))
......@@ -53,7 +51,7 @@ import System.IO (openFile, IOMode(ReadMode))
-- running cells, and the type of final results of cells, respectively.
data KernelConfig m output result =
KernelConfig
{
{
-- | Info on the language of the kernel.
kernelLanguageInfo :: LanguageInfo
-- | Write all the files into the kernel directory, including `kernel.js`, `logo-64x64.svg`, and any
......@@ -122,19 +120,12 @@ createReplyHeader :: MonadIO m => MessageHeader -> m MessageHeader
createReplyHeader parent = do
-- Generate a new message UUID.
newMessageId <- liftIO UUID.random
let repType = fromMaybe err (replyType $ msgType parent)
err = error $ "No reply for message " ++ show (msgType parent)
let repType = fromMaybe err (replyType $ mhMsgType parent)
err = error $ "No reply for message " ++ show (mhMsgType parent)
return $ MessageHeader (mhIdentifiers parent) (Just parent) (Map.fromList [])
newMessageId (mhSessionId parent) (mhUsername parent) repType
return
MessageHeader
{ identifiers = identifiers parent
, parentHeader = Just parent
, metadata = Map.fromList []
, messageId = newMessageId
, sessionId = sessionId parent
, username = username parent
, msgType = repType
}
-- | Execute an IPython kernel for a config. Your 'main' action should call this as the last thing
-- it does.
......@@ -145,16 +136,14 @@ easyKernel :: MonadIO m
-> m ()
easyKernel profileFile config = do
prof <- liftIO $ getProfile profileFile
zmq@(Channels shellReqChan shellRepChan ctrlReqChan ctrlRepChan iopubChan _) <- liftIO $ serveProfile
prof
False
zmq <- liftIO $ serveProfile prof False
execCount <- liftIO $ newMVar 0
forever $ do
req <- liftIO $ readChan shellReqChan
req <- liftIO $ readChan (shellRequestChannel zmq)
repHeader <- createReplyHeader (header req)
when (debug config) . liftIO $ print req
reply <- replyTo config execCount zmq req repHeader
liftIO $ writeChan shellRepChan reply
liftIO $ writeChan (shellRequestChannel zmq) reply
replyTo :: MonadIO m
=> KernelConfig m output result
......@@ -180,17 +169,17 @@ replyTo config _ interface KernelInfoRequest{} replyHeader = do
, status = Ok
}
replyTo config _ _ CommInfoRequest{} replyHeader =
replyTo _ _ _ CommInfoRequest{} replyHeader =
return
CommInfoReply
{ header = replyHeader
, commInfo = Map.empty }
replyTo config _ interface ShutdownRequest { restartPending = pending } replyHeader = do
replyTo _ _ interface ShutdownRequest { restartPending = pending } replyHeader = do
liftIO $ writeChan (shellReplyChannel interface) $ ShutdownReply replyHeader pending
liftIO exitSuccess
replyTo config execCount interface req@ExecuteRequest { getCode = code } replyHeader = do
replyTo config execCount interface req@ExecuteRequest{} replyHeader = do
let send = writeChan (iopubChannel interface)
busyHeader <- dupHeader replyHeader StatusMessage
......@@ -205,7 +194,7 @@ replyTo config execCount interface req@ExecuteRequest { getCode = code } replyHe
send $ PublishDisplayData
outputHeader
(displayOutput config x)
in run config code clearOutput sendOutput
in run config (getCode req) clearOutput sendOutput
liftIO . send $ PublishDisplayData outputHeader (displayResult config res)
......@@ -254,4 +243,4 @@ dupHeader :: MonadIO m => MessageHeader -> MessageType -> m MessageHeader
dupHeader hdr mtype =
do
uuid <- liftIO UUID.random
return hdr { messageId = uuid, msgType = mtype }
return hdr { mhMessageId = uuid, mhMsgType = mtype }
......@@ -3,7 +3,6 @@
module IHaskell.IPython.Kernel (module X) where
import IHaskell.IPython.Types as X
import IHaskell.IPython.Message.Writer as X
import IHaskell.IPython.Message.Parser as X
import IHaskell.IPython.Message.UUID as X
import IHaskell.IPython.ZeroMQ as X
......@@ -8,15 +8,14 @@
-- the low-level 0MQ interface.
module IHaskell.IPython.Message.Parser (parseMessage) where
import Control.Applicative ((<|>), (<$>), (<*>))
import Data.Aeson ((.:), (.:?), (.!=), decode, Result(..), Object, Value(..))
import Data.Aeson.Types (parse, parseEither)
import Control.Applicative ((<$>), (<*>))
import Data.Aeson ((.:), (.:?), (.!=), decode, FromJSON, Result(..), Object, Value(..))
import Data.Aeson.Types (Parser, parse, parseEither)
import Data.ByteString hiding (unpack)
import qualified Data.ByteString.Lazy as Lazy
import Data.HashMap.Strict as HM
import Data.Map (Map)
import Data.Maybe (fromMaybe)
import Data.Text (Text)
import Data.Text (Text, unpack)
import Debug.Trace
import IHaskell.IPython.Types
......@@ -32,7 +31,7 @@ parseMessage :: [ByteString] -- ^ The list of identifiers sent with the message.
-> Message -- ^ A parsed message.
parseMessage idents headerData parentHeader metadata content =
let header = parseHeader idents headerData parentHeader metadata
messageType = msgType header
messageType = mhMsgType header
messageWithoutHeader = parser messageType $ Lazy.fromStrict content
in messageWithoutHeader { header = header }
......@@ -43,15 +42,7 @@ parseHeader :: [ByteString] -- ^ The list of identifiers.
-> ByteString -- ^ The metadata, or "{}" for an empty map.
-> MessageHeader -- The resulting message header.
parseHeader idents headerData parentHeader metadata =
MessageHeader
{ identifiers = idents
, parentHeader = parentResult
, metadata = metadataMap
, messageId = messageUUID
, sessionId = sessionUUID
, username = username
, msgType = messageType
}
MessageHeader idents parentResult metadataMap messageUUID sessionUUID username messageType
where
-- Decode the header data and the parent header data into JSON objects. If the parent header data is
-- absent, just have Nothing instead.
......@@ -180,6 +171,7 @@ displayDataParser = requestParser $ \obj -> do
let displayDatas = makeDisplayDatas dataDict
return $ PublishDisplayData noHeader displayDatas
requestParser :: FromJSON a => (a -> Parser Message) -> LByteString -> Message
requestParser parser content =
case parseEither parser decoded of
Right parsed -> parsed
......@@ -218,6 +210,7 @@ inputMessageParser = requestParser $ \obj -> do
executionCount <- obj .: "execution_count"
return $ Input noHeader code executionCount
getDisplayDatas :: Maybe Object -> [DisplayData]
getDisplayDatas Nothing = []
getDisplayDatas (Just dataDict) = makeDisplayDatas dataDict
......
......@@ -3,7 +3,7 @@
-- Generate, parse, and pretty print UUIDs for use with IPython.
module IHaskell.IPython.Message.UUID (UUID, random, randoms, uuidToString) where
import Control.Applicative ((<$>), (<*>))
import Control.Applicative ((<$>))
import Control.Monad (mzero, replicateM)
import Data.Aeson
import Data.Text (pack)
......
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-name-shadowing -fno-warn-unused-matches #-}
-- | Description : @ToJSON@ for Messages
--
-- This module contains the @ToJSON@ instance for @Message@.
module IHaskell.IPython.Message.Writer (ToJSON(..)) where
import Data.Aeson
import Data.Aeson.Types (Pair)
import Data.Aeson.Parser (json)
import Data.Map (Map)
import Data.Monoid (mempty)
import Data.Text (Text, pack)
import Data.Text.Encoding (encodeUtf8)
import qualified Data.Map as Map
import IHaskell.IPython.Types
import Data.Maybe (fromMaybe)
instance ToJSON LanguageInfo where
toJSON info = object
[ "name" .= languageName info
, "version" .= languageVersion info
, "file_extension" .= languageFileExtension info
, "codemirror_mode" .= languageCodeMirrorMode info
, "pygments_lexer" .= languagePygmentsLexer info
]
-- Convert message bodies into JSON.
instance ToJSON Message where
toJSON rep@KernelInfoReply{} =
object
[ "protocol_version" .= protocolVersion rep
, "banner" .= banner rep
, "implementation" .= implementation rep
, "implementation_version" .= implementationVersion rep
, "language_info" .= languageInfo rep
, "status" .= show (status rep)
]
toJSON CommInfoReply
{ header = header
, commInfo = commInfo
} =
object
[ "comms" .= Map.map (\comm -> object ["target_name" .= comm]) commInfo
, "status" .= string "ok"
]
toJSON ExecuteRequest
{ getCode = code
, getSilent = silent
, getStoreHistory = storeHistory
, getAllowStdin = allowStdin
, getUserExpressions = userExpressions
} =
object
[ "code" .= code
, "silent" .= silent
, "store_history" .= storeHistory
, "allow_stdin" .= allowStdin
, "user_expressions" .= userExpressions
]
toJSON ExecuteReply { status = status, executionCounter = counter, pagerOutput = pager } =
object
[ "status" .= show status
, "execution_count" .= counter
, "payload" .=
if null pager
then []
else mkPayload pager
, "user_expressions" .= emptyMap
]
where
mkPayload o = [ object
[ "source" .= string "page"
, "start" .= Number 0
, "data" .= object (map displayDataToJson o)
]
]
toJSON PublishStatus { executionState = executionState } =
object ["execution_state" .= executionState]
toJSON PublishStream { streamType = streamType, streamContent = content } =
object ["data" .= content, "name" .= streamType]
toJSON PublishDisplayData { displayData = datas } =
object
["metadata" .= object [], "data" .= object (map displayDataToJson datas)]
toJSON PublishOutput { executionCount = execCount, reprText = reprText } =
object
[ "data" .= object ["text/plain" .= reprText]
, "execution_count" .= execCount
, "metadata" .= object []
]
toJSON PublishInput { executionCount = execCount, inCode = code } =
object ["execution_count" .= execCount, "code" .= code]
toJSON (CompleteReply _ matches start end metadata status) =
object
[ "matches" .= matches
, "cursor_start" .= start
, "cursor_end" .= end
, "metadata" .= metadata
, "status" .= if status
then string "ok"
else "error"
]
toJSON i@InspectReply{} =
object
[ "status" .= if inspectStatus i
then string "ok"
else "error"
, "data" .= object (map displayDataToJson . inspectData $ i)
, "metadata" .= object []
, "found" .= inspectStatus i
]
toJSON ShutdownReply { restartPending = restart } =
object ["restart" .= restart
, "status" .= string "ok"
]
toJSON ClearOutput { wait = wait } =
object ["wait" .= wait]
toJSON RequestInput { inputPrompt = prompt } =
object ["prompt" .= prompt]
toJSON req@CommOpen{} =
object
[ "comm_id" .= commUuid req
, "target_name" .= commTargetName req
, "target_module" .= commTargetModule req
, "data" .= commData req
]
toJSON req@CommData{} =
object ["comm_id" .= commUuid req, "data" .= commData req]
toJSON req@CommClose{} =
object ["comm_id" .= commUuid req, "data" .= commData req]
toJSON req@HistoryReply{} =
object ["history" .= map tuplify (historyReply req)
, "status" .= string "ok"
]
where
tuplify (HistoryReplyElement sess linum res) = (sess, linum, case res of
Left inp -> toJSON inp
Right (inp, out) -> toJSON out)
toJSON req@IsCompleteReply{} =
object pairs
where
pairs =
case reviewResult req of
CodeComplete -> status "complete"
CodeIncomplete ind -> status "incomplete" ++ indent ind
CodeInvalid -> status "invalid"
CodeUnknown -> status "unknown"
status x = ["status" .= pack x]
indent x = ["indent" .= pack x]
toJSON body = error $ "Do not know how to convert to JSON for message " ++ show body
-- | Print an execution state as "busy", "idle", or "starting".
instance ToJSON ExecutionState where
toJSON Busy = String "busy"
toJSON Idle = String "idle"
toJSON Starting = String "starting"
-- | Print a stream as "stdin" or "stdout" strings.
instance ToJSON StreamType where
toJSON Stdin = String "stdin"
toJSON Stdout = String "stdout"
-- | Convert a MIME type and value into a JSON dictionary pair.
displayDataToJson :: DisplayData -> (Text, Value)
displayDataToJson (DisplayData MimeJson dataStr) =
pack (show MimeJson) .= fromMaybe (String "") (decodeStrict (encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData MimeVegalite dataStr) =
pack (show MimeVegalite) .= fromMaybe (String "") (decodeStrict (encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData MimeVega dataStr) =
pack (show MimeVega) .= fromMaybe (String "") (decodeStrict (encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData mimeType dataStr) =
pack (show mimeType) .= String dataStr
----- Constants -----
emptyMap :: Map String String
emptyMap = mempty
emptyList :: [Int]
emptyList = []
ints :: [Int] -> [Int]
ints = id
string :: String -> String
string = id
......@@ -6,8 +6,8 @@ module IHaskell.IPython.Types (
-- * IPython kernel profile
Profile(..),
Transport(..),
Port(..),
IP(..),
Port,
IP,
-- * IPython kernelspecs
KernelSpec(..),
......@@ -15,12 +15,12 @@ module IHaskell.IPython.Types (
-- * IPython messaging protocol
Message(..),
MessageHeader(..),
Username(..),
Metadata(..),
Username,
Metadata,
MessageType(..),
CodeReview(..),
Width(..),
Height(..),
Width,
Height,
StreamType(..),
ExecutionState(..),
ExecuteReplyStatus(..),
......@@ -38,11 +38,15 @@ module IHaskell.IPython.Types (
import Control.Applicative ((<$>), (<*>))
import Data.Aeson
import Data.Aeson.Types (typeMismatch)
import Data.ByteString (ByteString)
import Data.List (find)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import Data.Serialize
import Data.Text (Text)
import Data.Serialize.Text ()
import Data.Text (Text, pack)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import Data.Typeable
......@@ -117,7 +121,7 @@ instance ToJSON Transport where
-------------------- IPython Kernelspec Types ----------------------
data KernelSpec =
KernelSpec
{
{
-- | Name shown to users to describe this kernel (e.g. "Haskell")
kernelDisplayName :: String
-- | Name for the kernel; unique kernel identifier (e.g. "haskell")
......@@ -140,13 +144,13 @@ instance ToJSON KernelSpec where
-- | A message header with some metadata.
data MessageHeader =
MessageHeader
{ identifiers :: [ByteString] -- ^ The identifiers sent with the message.
, parentHeader :: Maybe MessageHeader -- ^ The parent header, if present.
, metadata :: Metadata -- ^ A dict of metadata.
, messageId :: UUID -- ^ A unique message UUID.
, sessionId :: UUID -- ^ A unique session UUID.
, username :: Username -- ^ The user who sent this message.
, msgType :: MessageType -- ^ The message type.
{ mhIdentifiers :: [ByteString] -- ^ The identifiers sent with the message.
, mhParentHeader :: Maybe MessageHeader -- ^ The parent header, if present.
, mhMetadata :: Metadata -- ^ A dict of metadata.
, mhMessageId :: UUID -- ^ A unique message UUID.
, mhSessionId :: UUID -- ^ A unique session UUID.
, mhUsername :: Username -- ^ The user who sent this message.
, mhMsgType :: MessageType -- ^ The message type.
}
deriving (Show, Read)
......@@ -154,11 +158,11 @@ data MessageHeader =
-- all the record fields.
instance ToJSON MessageHeader where
toJSON header = object
[ "msg_id" .= messageId header
, "session" .= sessionId header
, "username" .= username header
[ "msg_id" .= mhMessageId header
, "session" .= mhSessionId header
, "username" .= mhUsername header
, "version" .= ("5.0" :: String)
, "msg_type" .= showMessageType (msgType header)
, "msg_type" .= showMessageType (mhMsgType header)
]
-- | A username for the source of a message.
......@@ -280,6 +284,15 @@ data LanguageInfo =
}
deriving (Show, Eq)
instance ToJSON LanguageInfo where
toJSON info = object
[ "name" .= languageName info
, "version" .= languageVersion info
, "file_extension" .= languageFileExtension info
, "codemirror_mode" .= languageCodeMirrorMode info
, "pygments_lexer" .= languagePygmentsLexer info
]
data CodeReview = CodeComplete
| CodeIncomplete String -- ^ String to be used to indent next line of input
| CodeInvalid
......@@ -472,6 +485,146 @@ data Message =
| SendNothing -- Dummy message; nothing is sent.
deriving Show
-- Convert message bodies into JSON.
instance ToJSON Message where
toJSON rep@KernelInfoReply{} =
object
[ "protocol_version" .= protocolVersion rep
, "banner" .= banner rep
, "implementation" .= implementation rep
, "implementation_version" .= implementationVersion rep
, "language_info" .= languageInfo rep
, "status" .= show (status rep)
]
toJSON CommInfoReply
{ header = header
, commInfo = commInfo
} =
object
[ "comms" .= Map.map (\comm -> object ["target_name" .= comm]) commInfo
, "status" .= string "ok"
]
toJSON ExecuteRequest
{ getCode = code
, getSilent = silent
, getStoreHistory = storeHistory
, getAllowStdin = allowStdin
, getUserExpressions = userExpressions
} =
object
[ "code" .= code
, "silent" .= silent
, "store_history" .= storeHistory
, "allow_stdin" .= allowStdin
, "user_expressions" .= userExpressions
]
toJSON ExecuteReply { status = status, executionCounter = counter, pagerOutput = pager } =
object
[ "status" .= show status
, "execution_count" .= counter
, "payload" .=
if null pager
then []
else mkPayload pager
, "user_expressions" .= emptyMap
]
where
mkPayload o = [ object
[ "source" .= string "page"
, "start" .= Number 0
, "data" .= object (map displayDataToJson o)
]
]
toJSON PublishStatus { executionState = executionState } =
object ["execution_state" .= executionState]
toJSON PublishStream { streamType = streamType, streamContent = content } =
object ["data" .= content, "name" .= streamType]
toJSON PublishDisplayData { displayData = datas } =
object
["metadata" .= object [], "data" .= object (map displayDataToJson datas)]
toJSON PublishOutput { executionCount = execCount, reprText = reprText } =
object
[ "data" .= object ["text/plain" .= reprText]
, "execution_count" .= execCount
, "metadata" .= object []
]
toJSON PublishInput { executionCount = execCount, inCode = code } =
object ["execution_count" .= execCount, "code" .= code]
toJSON (CompleteReply _ matches start end metadata status) =
object
[ "matches" .= matches
, "cursor_start" .= start
, "cursor_end" .= end
, "metadata" .= metadata
, "status" .= if status
then string "ok"
else "error"
]
toJSON i@InspectReply{} =
object
[ "status" .= if inspectStatus i
then string "ok"
else "error"
, "data" .= object (map displayDataToJson . inspectData $ i)
, "metadata" .= object []
, "found" .= inspectStatus i
]
toJSON ShutdownReply { restartPending = restart } =
object ["restart" .= restart
, "status" .= string "ok"
]
toJSON ClearOutput { wait = wait } =
object ["wait" .= wait]
toJSON RequestInput { inputPrompt = prompt } =
object ["prompt" .= prompt]
toJSON req@CommOpen{} =
object
[ "comm_id" .= commUuid req
, "target_name" .= commTargetName req
, "target_module" .= commTargetModule req
, "data" .= commData req
]
toJSON req@CommData{} =
object ["comm_id" .= commUuid req, "data" .= commData req]
toJSON req@CommClose{} =
object ["comm_id" .= commUuid req, "data" .= commData req]
toJSON req@HistoryReply{} =
object ["history" .= map tuplify (historyReply req)
, "status" .= string "ok"
]
where
tuplify (HistoryReplyElement sess linum res) = (sess, linum, case res of
Left inp -> toJSON inp
Right (inp, out) -> toJSON out)
toJSON req@IsCompleteReply{} =
object pairs
where
pairs =
case reviewResult req of
CodeComplete -> status "complete"
CodeIncomplete ind -> status "incomplete" ++ indent ind
CodeInvalid -> status "invalid"
CodeUnknown -> status "unknown"
status x = ["status" .= pack x]
indent x = ["indent" .= pack x]
toJSON body = error $ "Do not know how to convert to JSON for message " ++ show body
-- | Ways in which the frontend can request history. TODO: Implement fields as described in
-- messaging spec.
data HistoryAccessType = HistoryRange
......@@ -497,6 +650,7 @@ instance FromJSON ExecuteReplyStatus where
parseJSON (String "ok") = return Ok
parseJSON (String "error") = return Err
parseJSON (String "abort") = return Abort
parseJSON invalid = typeMismatch "ExecuteReplyStatus" invalid
instance Show ExecuteReplyStatus where
show Ok = "ok"
......@@ -513,6 +667,13 @@ instance FromJSON ExecutionState where
parseJSON (String "busy") = return Busy
parseJSON (String "idle") = return Idle
parseJSON (String "starting") = return Starting
parseJSON invalid = typeMismatch "ExecutionState" invalid
-- | Print an execution state as "busy", "idle", or "starting".
instance ToJSON ExecutionState where
toJSON Busy = String "busy"
toJSON Idle = String "idle"
toJSON Starting = String "starting"
-- | Input and output streams.
data StreamType = Stdin
......@@ -524,6 +685,13 @@ instance FromJSON StreamType where
parseJSON (String "stdin") = return Stdin
parseJSON (String "stdout") = return Stdout
parseJSON (String "stderr") = return Stderr
parseJSON invalid = typeMismatch "StreamType" invalid
-- | Print a stream as "stdin" or "stdout" strings.
instance ToJSON StreamType where
toJSON Stdin = String "stdin"
toJSON Stdout = String "stdout"
toJSON Stderr = String "stderr"
-- | Get the reply message type for a request message type.
replyType :: MessageType -> Maybe MessageType
......@@ -547,11 +715,6 @@ data DisplayData = DisplayData MimeType Text
instance Show DisplayData where
show _ = "DisplayData"
-- Allow DisplayData serialization
instance Serialize Text where
put str = put (Text.encodeUtf8 str)
get = Text.decodeUtf8 <$> get
instance Serialize DisplayData
instance Serialize MimeType
......@@ -583,6 +746,7 @@ extractPlain disps =
case find isPlain disps of
Nothing -> ""
Just (DisplayData PlainText bytestr) -> Text.unpack bytestr
Just _ -> ""
where
isPlain (DisplayData mime _) = mime == PlainText
......@@ -617,3 +781,21 @@ instance Read MimeType where
readsPrec _ "application/vnd.vega.v2+json" = [(MimeVega, "")]
readsPrec _ "application/vnd.vegalite.v1+json" = [(MimeVegalite, "")]
readsPrec _ "application/vdom.v1+json" = [(MimeVdom, "")]
readsPrec _ _ = []
-- | Convert a MIME type and value into a JSON dictionary pair.
displayDataToJson :: DisplayData -> (Text, Value)
displayDataToJson (DisplayData MimeJson dataStr) =
pack (show MimeJson) .= fromMaybe (String "") (decodeStrict (Text.encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData MimeVegalite dataStr) =
pack (show MimeVegalite) .= fromMaybe (String "") (decodeStrict (Text.encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData MimeVega dataStr) =
pack (show MimeVega) .= fromMaybe (String "") (decodeStrict (Text.encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData mimeType dataStr) =
pack (show mimeType) .= String dataStr
string :: String -> String
string = id
emptyMap :: Map String String
emptyMap = mempty
......@@ -27,11 +27,10 @@ import qualified Data.ByteString.Lazy as LBS
import Data.Char
import Data.Monoid ((<>))
import qualified Data.Text.Encoding as Text
import System.ZMQ4 as ZMQ4 hiding (stdin)
import System.ZMQ4 as ZMQ4
import Text.Read (readMaybe)
import IHaskell.IPython.Message.Parser
import IHaskell.IPython.Message.Writer ()
import IHaskell.IPython.Types
-- | The channel interface to the ZeroMQ sockets. All communication is done via Messages, which are
......@@ -39,7 +38,7 @@ import IHaskell.IPython.Types
-- should functionally serve as high-level sockets which speak Messages instead of ByteStrings.
data ZeroMQInterface =
Channels
{
{
-- | A channel populated with requests from the frontend.
shellRequestChannel :: Chan Message
-- | Writing to this channel causes a reply to be sent to the frontend.
......@@ -90,16 +89,16 @@ serveProfile profile debug = do
-- Create the context in a separate thread that never finishes. If withContext or withSocket
-- complete, the context or socket become invalid.
forkIO $ withContext $ \context -> do
_ <- forkIO $ withContext $ \ctxt -> do
-- Serve on all sockets.
forkIO $ serveSocket context Rep (hbPort profile) $ heartbeat channels
forkIO $ serveSocket context Router (controlPort profile) $ control debug channels
forkIO $ serveSocket context Router (shellPort profile) $ shell debug channels
_ <- forkIO $ serveSocket ctxt Rep (hbPort profile) $ heartbeat channels
_ <- forkIO $ serveSocket ctxt Router (controlPort profile) $ control debug channels
_ <- forkIO $ serveSocket ctxt Router (shellPort profile) $ shell debug channels
-- The context is reference counted in this thread only. Thus, the last serveSocket cannot be
-- asynchronous, because otherwise context would be garbage collectable - since it would only be
-- The ctxt is reference counted in this thread only. Thus, the last serveSocket cannot be
-- asynchronous, because otherwise ctxt would be garbage collectable - since it would only be
-- used in other threads. Thus, keep the last serveSocket in this thread.
serveSocket context Pub (iopubPort profile) $ iopub debug channels
serveSocket ctxt Pub (iopubPort profile) $ iopub debug channels
return channels
......@@ -132,9 +131,9 @@ parsePort s = readMaybe num
num = reverse (takeWhile isNumber (reverse s))
bindLocalEphemeralPort :: Socket a -> IO Int
bindLocalEphemeralPort socket = do
bind socket $ "tcp://127.0.0.1:*"
endpointString <- lastEndpoint socket
bindLocalEphemeralPort sock = do
bind sock $ "tcp://127.0.0.1:*"
endpointString <- lastEndpoint sock
case parsePort endpointString of
Nothing ->
fail $ "internalError: IHaskell.IPython.ZeroMQ.bindLocalEphemeralPort encountered a port index that could not be interpreted as an int."
......@@ -152,19 +151,19 @@ withEphemeralPorts :: ByteString -- ^ HMAC encryption key
withEphemeralPorts key debug callback = do
channels <- newZeroMQInterface key
-- Create the ZMQ4 context
withContext $ \context -> do
withContext $ \ctxt -> do
-- Create the sockets to communicate with.
withSocket context Rep $ \heartbeatSocket -> do
withSocket context Router $ \controlportSocket -> do
withSocket context Router $ \shellportSocket -> do
withSocket context Pub $ \iopubSocket -> do
withSocket ctxt Rep $ \heartbeatSocket -> do
withSocket ctxt Router $ \controlportSocket -> do
withSocket ctxt Router $ \shellportSocket -> do
withSocket ctxt Pub $ \iopubSocket -> do
-- Bind each socket to a local port, getting the port chosen.
hbPort <- bindLocalEphemeralPort heartbeatSocket
controlPort <- bindLocalEphemeralPort controlportSocket
shellPort <- bindLocalEphemeralPort shellportSocket
iopubPort <- bindLocalEphemeralPort iopubSocket
hbPt <- bindLocalEphemeralPort heartbeatSocket
controlPt <- bindLocalEphemeralPort controlportSocket
shellPt <- bindLocalEphemeralPort shellportSocket
iopubPt <- bindLocalEphemeralPort iopubSocket
-- Create object to store ephemeral ports
let ports = ZeroMQEphemeralPorts { ephHbPort = hbPort, ephControlPort = controlPort, ephShellPort = shellPort, ephIOPubPort = iopubPort, ephSignatureKey = key }
let ports = ZeroMQEphemeralPorts hbPt controlPt shellPt iopubPt key
-- Launch actions to listen to communicate between channels and cockets.
_ <- forkIO $ forever $ heartbeat channels heartbeatSocket
_ <- forkIO $ forever $ control debug channels controlportSocket
......@@ -180,44 +179,44 @@ serveStdin profile = do
-- Create the context in a separate thread that never finishes. If withContext or withSocket
-- complete, the context or socket become invalid.
forkIO $ withContext $ \context ->
_ <- forkIO $ withContext $ \ctxt ->
-- Serve on all sockets.
serveSocket context Router (stdinPort profile) $ \socket -> do
serveSocket ctxt Router (stdinPort profile) $ \sock -> do
-- Read the request from the interface channel and send it.
readChan reqChannel >>= sendMessage False (signatureKey profile) socket
readChan reqChannel >>= sendMessage False (signatureKey profile) sock
-- Receive a response and write it to the interface channel.
receiveMessage False socket >>= writeChan repChannel
receiveMessage False sock >>= writeChan repChannel
return $ StdinChannel reqChannel repChannel
-- | Serve on a given socket in a separate thread. Bind the socket in the | given context and then
-- loop the provided action, which should listen | on the socket and respond to any events.
-- | Serve on a given sock in a separate thread. Bind the sock in the | given context and then
-- loop the provided action, which should listen | on the sock and respond to any events.
serveSocket :: SocketType a => Context -> a -> Port -> (Socket a -> IO b) -> IO ()
serveSocket context socketType port action = void $
withSocket context socketType $ \socket -> do
bind socket $ "tcp://127.0.0.1:" ++ show port
forever $ action socket
serveSocket ctxt socketType port action = void $
withSocket ctxt socketType $ \sock -> do
bind sock $ "tcp://127.0.0.1:" ++ show port
forever $ action sock
-- | Listener on the heartbeat port. Echoes back any data it was sent.
heartbeat :: ZeroMQInterface -> Socket Rep -> IO ()
heartbeat _ socket = do
heartbeat _ sock = do
-- Read some data.
request <- receive socket
request <- receive sock
-- Send it back.
send socket [] request
send sock [] request
-- | Listener on the shell port. Reads messages and writes them to | the shell request channel. For
-- each message, reads a response from the | shell reply channel of the interface and sends it back
-- to the frontend.
shell :: Bool -> ZeroMQInterface -> Socket Router -> IO ()
shell debug channels socket = do
shell debug channels sock = do
-- Receive a message and write it to the interface channel.
receiveMessage debug socket >>= writeChan requestChannel
receiveMessage debug sock >>= writeChan requestChannel
-- Read the reply from the interface channel and send it.
readChan replyChannel >>= sendMessage debug (hmacKey channels) socket
readChan replyChannel >>= sendMessage debug (hmacKey channels) sock
where
requestChannel = shellRequestChannel channels
......@@ -227,12 +226,12 @@ shell debug channels socket = do
-- each message, reads a response from the | shell reply channel of the interface and sends it back
-- to the frontend.
control :: Bool -> ZeroMQInterface -> Socket Router -> IO ()
control debug channels socket = do
control debug channels sock = do
-- Receive a message and write it to the interface channel.
receiveMessage debug socket >>= writeChan requestChannel
receiveMessage debug sock >>= writeChan requestChannel
-- Read the reply from the interface channel and send it.
readChan replyChannel >>= sendMessage debug (hmacKey channels) socket
readChan replyChannel >>= sendMessage debug (hmacKey channels) sock
where
requestChannel = controlRequestChannel channels
......@@ -241,33 +240,33 @@ control debug channels socket = do
-- | Send messages via the iopub channel. | This reads messages from the ZeroMQ iopub interface
-- channel | and then writes the messages to the socket.
iopub :: Bool -> ZeroMQInterface -> Socket Pub -> IO ()
iopub debug channels socket =
readChan (iopubChannel channels) >>= sendMessage debug (hmacKey channels) socket
iopub debug channels sock =
readChan (iopubChannel channels) >>= sendMessage debug (hmacKey channels) sock
-- | Attempt to send a message along the socket, returning true if successful.
trySendMessage :: Sender a => String -> Bool -> ByteString -> Socket a -> Message -> IO Bool
trySendMessage nm debug hmacKey socket message = do
trySendMessage _ debug hmackey sock msg = do
let zmqErrorHandler :: ZMQError -> IO Bool
zmqErrorHandler e
-- Ignore errors if we cannot send. We may want to forward this to the thread that tried put the
-- message in the Chan initially.
| errno e == 38 = return False
| otherwise = throwIO e
(sendMessage debug hmacKey socket message >> return True) `catch` zmqErrorHandler
(sendMessage debug hmackey sock msg >> return True) `catch` zmqErrorHandler
-- | Send messages via the iopub channel. This reads messages from the ZeroMQ iopub interface
-- channel and then writes the messages to the socket. This is a checked implementation which will
-- stop if the socket is closed.
checkedIOpub :: Bool -> ZeroMQInterface -> Socket Pub -> IO ()
checkedIOpub debug channels socket = do
checkedIOpub debug channels sock = do
msg <- readChan (iopubChannel channels)
cont <- trySendMessage "io" debug (hmacKey channels) socket msg
cont <- trySendMessage "io" debug (hmacKey channels) sock msg
when cont $
checkedIOpub debug channels socket
checkedIOpub debug channels sock
-- | Receive and parse a message from a socket.
receiveMessage :: Receiver a => Bool -> Socket a -> IO Message
receiveMessage debug socket = do
receiveMessage debug sock = do
-- Read all identifiers until the identifier/message delimiter.
idents <- readUntil "<IDS|MSG>"
......@@ -285,12 +284,11 @@ receiveMessage debug socket = do
putStr "Content: "
Char.putStrLn content
let message = parseMessage idents headerData parentHeader metadata content
return message
return $ parseMessage idents headerData parentHeader metadata content
where
-- Receive the next piece of data from the socket.
next = receive socket
next = receive sock
-- Read data from the socket until we hit an ending string. Return all data as a list, which does
-- not include the ending string.
......@@ -306,10 +304,10 @@ receiveMessage debug socket = do
-- socket. Sign it using HMAC with SHA-256 using the provided key.
sendMessage :: Sender a => Bool -> ByteString -> Socket a -> Message -> IO ()
sendMessage _ _ _ SendNothing = return ()
sendMessage debug hmacKey socket message = do
sendMessage debug hmackey sock msg = do
when debug $ do
putStr "Message: "
print message
print msg
putStr "Sent: "
print content
......@@ -325,8 +323,8 @@ sendMessage debug hmacKey socket message = do
sendLast content
where
sendPiece = send socket [SendMore]
sendLast = send socket []
sendPiece = send sock [SendMore]
sendLast = send sock []
-- Encode to a strict bytestring.
encodeStrict :: ToJSON a => a -> ByteString
......@@ -338,12 +336,12 @@ sendMessage debug hmacKey socket message = do
-- Compute the HMAC SHA-256 signature of a bytestring message.
hmac :: ByteString -> ByteString
hmac = Char.pack . show . (HMAC.hmacGetDigest :: HMAC.HMAC SHA256 -> Hash.Digest SHA256) . HMAC.hmac hmacKey
hmac = Char.pack . show . (HMAC.hmacGetDigest :: HMAC.HMAC SHA256 -> Hash.Digest SHA256) . HMAC.hmac hmackey
-- Pieces of the message.
head = header message
parentHeaderStr = maybe "{}" encodeStrict $ parentHeader head
idents = identifiers head
hdr = header msg
parentHeaderStr = maybe "{}" encodeStrict $ mhParentHeader hdr
idents = mhIdentifiers hdr
metadata = "{}"
content = encodeStrict message
headStr = encodeStrict head
content = encodeStrict msg
headStr = encodeStrict hdr
......@@ -195,7 +195,7 @@ runKernel kOpts profileSrc = do
installHandler keyboardSignal (CatchOnce $ putStrLn "Press Ctrl-C again to quit kernel.")
Nothing
isCommMessage req = msgType (header req) `elem` [CommDataMessage, CommCloseMessage]
isCommMessage req = mhMsgType (header req) `elem` [CommDataMessage, CommCloseMessage]
-- Initial kernel state.
initialKernelState :: IO (MVar KernelState)
......@@ -206,19 +206,11 @@ createReplyHeader :: MessageHeader -> Interpreter MessageHeader
createReplyHeader parent = do
-- Generate a new message UUID.
newMessageId <- liftIO UUID.random
let repType = fromMaybe err (replyType $ msgType parent)
err = error $ "No reply for message " ++ show (msgType parent)
let repType = fromMaybe err (replyType $ mhMsgType parent)
err = error $ "No reply for message " ++ show (mhMsgType parent)
return
MessageHeader
{ identifiers = identifiers parent
, parentHeader = Just parent
, metadata = Map.fromList []
, messageId = newMessageId
, sessionId = sessionId parent
, username = username parent
, msgType = repType
}
return $ MessageHeader (mhIdentifiers parent) (Just parent) mempty
newMessageId (mhSessionId parent) (mhUsername parent) repType
-- | Compute a reply to a message.
replyTo :: ZeroMQInterface -> Message -> MessageHeader -> KernelState -> Interpreter (KernelState, Message)
......@@ -432,7 +424,7 @@ handleComm send kernelState req replyHeader = do
newState <- case Map.lookup uuid widgets of
Nothing -> return kernelState
Just (Widget widget) ->
case msgType $ header req of
case mhMsgType $ header req of
CommDataMessage -> do
disp <- run $ comm widget dat communicate
pgrOut <- liftIO $ readMVar pOut
......
......@@ -33,7 +33,6 @@ import GHC.IO.Handle
import GHC.IO.Handle.Types
import System.Posix.IO
import System.IO.Unsafe
import qualified Data.Map as Map
import IHaskell.IPython.Types
import IHaskell.IPython.ZeroMQ
......@@ -88,15 +87,8 @@ getInputLine dir = do
-- Send a request for input.
uuid <- UUID.random
parentHdr <- fromJust . readMay <$> readFile (dir ++ "/.last-req-header")
let hdr = MessageHeader
{ username = username parentHdr
, identifiers = identifiers parentHdr
, parentHeader = Just parentHdr
, messageId = uuid
, sessionId = sessionId parentHdr
, metadata = Map.fromList []
, msgType = InputRequestMessage
}
let hdr = MessageHeader (mhIdentifiers parentHdr) (Just parentHdr) mempty
uuid (mhSessionId parentHdr) (mhUsername parentHdr) InputRequestMessage
let msg = RequestInput hdr ""
writeChan req msg
......
......@@ -39,7 +39,7 @@ module IHaskell.Types (
import IHaskellPrelude
import Data.Aeson (ToJSON, Value, (.=), object)
import Data.Aeson (ToJSON (..), Value, (.=), object)
import Data.Function (on)
import Data.Serialize
import GHC.Generics
......@@ -268,4 +268,4 @@ dupHeader :: MessageHeader -> MessageType -> IO MessageHeader
dupHeader hdr messageType = do
uuid <- liftIO random
return hdr { messageId = uuid, msgType = messageType }
return hdr { mhMessageId = uuid, mhMsgType = messageType }
......@@ -21,6 +21,7 @@ extra-deps: []
ghc-options:
# Eventually we want "$locals": -Wall -Werror
ghc-parser: -Wall -Werror
ihaskell: -Wall -Werror
nix:
......
......@@ -25,6 +25,7 @@ extra-deps:
ghc-options:
# Eventually we want "$locals": -Wall -Werror
ghc-parser: -Wall -Werror
ihaskell: -Wall -Werror
nix:
......
......@@ -19,6 +19,7 @@ packages:
ghc-options:
# Eventually we want "$locals": -Wall -Werror
ghc-parser: -Wall -Werror
ihaskell: -Wall -Werror
allow-newer: true
......
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