{-# language NoImplicitPrelude, DoAndIfThenElse, OverloadedStrings, ExtendedDefaultRules #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE CPP #-}

-- | Description : All message type definitions.
module IHaskell.Types (
    Message(..),
    MessageHeader(..),
    MessageType(..),
    dupHeader,
    setVersion,
    Username,
    Metadata,
    BufferPath,
    replyType,
    ExecutionState(..),
    StreamType(..),
    MimeType(..),
    DisplayData(..),
    ErrorOccurred(..),
    EvaluationResult(..),
    evaluationOutputs,
    ExecuteReplyStatus(..),
    KernelState(..),
    LintStatus(..),
    Width,
    Height,
    Display(..),
    defaultKernelState,
    extractPlain,
    kernelOpts,
    KernelOpt(..),
    IHaskellDisplay(..),
    IHaskellWidget(..),
    Widget(..),
    WidgetMsg(..),
    WidgetMethod(..),
    KernelSpec(..),
    ) where

import           IHaskellPrelude

import           Data.Aeson (ToJSON (..), Value, (.=), object, Value(String))
import           Data.Function (on)
import           Data.Text (pack)
import           Data.Binary
import           GHC.Generics

import           IHaskell.IPython.Kernel

#if MIN_VERSION_aeson(2,0,0)
import qualified Data.Aeson.KeyMap as KeyMap
#else
import qualified Data.HashMap.Strict as HashMap
#endif

-- | A class for displayable Haskell types.
--
-- IHaskell's displaying of results behaves as if these two overlapping/undecidable instances also
-- existed:
--
-- > instance (Show a) => IHaskellDisplay a
-- > instance Show a where shows _ = id
class IHaskellDisplay a where
  display :: a -> IO Display

type BufferPath = [Text]

emptyBPs :: [BufferPath]
emptyBPs :: [BufferPath]
emptyBPs = []

-- | Display as an interactive widget.
class IHaskellDisplay a => IHaskellWidget a where
  -- | Target name for this widget. The actual input parameter should be ignored. By default evaluate
  -- to "jupyter.widget", which is used by IPython for its backbone widgets.
  targetName :: a -> String
  targetName a
_ = String
"jupyter.widget"

  -- | Target module for this widget. Evaluates to an empty string by default.
  targetModule :: a -> String
  targetModule a
_ = String
""

  -- | Buffer paths for this widget. Evaluates to an empty array by default.
  getBufferPaths :: a -> [BufferPath]
  getBufferPaths a
_ = [BufferPath]
emptyBPs

  -- | Get the uuid for comm associated with this widget. The widget is responsible for storing the
  -- UUID during initialization.
  getCommUUID :: a -> UUID

  -- | Called when the comm is opened. Allows additional messages to be sent after comm open.
  open :: a                -- ^ Widget to open a comm port with.
       -> (Value -> IO ()) -- ^ A function for sending messages.
       -> IO ()
  open a
_ Value -> IO ()
_ = forall (m :: * -> *) a. Monad m => a -> m a
return ()

  -- | Respond to a comm data message. Called when a message is recieved on the comm associated with
  -- the widget.
  comm :: a                -- ^ Widget which is being communicated with.
       -> Value            -- ^ Data recieved from the frontend.
       -> (Value -> IO ()) -- ^ Way to respond to the message.
       -> IO ()
  comm a
_ Value
_ Value -> IO ()
_ = forall (m :: * -> *) a. Monad m => a -> m a
return ()

  -- | Called when a comm_close is recieved from the frontend.
  close :: a               -- ^ Widget to close comm port with.
        -> Value           -- ^ Data recieved from the frontend.
        -> IO ()
  close a
_ Value
_ = forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | these instances cause the image, html etc. which look like:
--
-- > Display
-- > [Display]
-- > IO [Display]
-- > IO (IO Display)
--
-- be run the IO and get rendered (if the frontend allows it) in the pretty form.
instance IHaskellDisplay a => IHaskellDisplay (IO a) where
  display :: IO a -> IO Display
display = (forall a. IHaskellDisplay a => a -> IO Display
display forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<<)

instance IHaskellDisplay Display where
  display :: Display -> IO Display
display = forall (m :: * -> *) a. Monad m => a -> m a
return

instance IHaskellDisplay DisplayData where
  display :: DisplayData -> IO Display
display DisplayData
disp = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ [DisplayData] -> Display
Display [DisplayData
disp]

instance IHaskellDisplay a => IHaskellDisplay [a] where
  display :: [a] -> IO Display
display [a]
disps = do
    [Display]
displays <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM forall a. IHaskellDisplay a => a -> IO Display
display [a]
disps
    forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ [Display] -> Display
ManyDisplay [Display]
displays

data Widget = forall a. IHaskellWidget a => Widget a
  deriving Typeable

instance IHaskellDisplay Widget where
  display :: Widget -> IO Display
display (Widget a
widget) = forall a. IHaskellDisplay a => a -> IO Display
display a
widget

instance IHaskellWidget Widget where
  targetName :: Widget -> String
targetName (Widget a
widget) = forall a. IHaskellWidget a => a -> String
targetName a
widget
  targetModule :: Widget -> String
targetModule (Widget a
widget) = forall a. IHaskellWidget a => a -> String
targetModule a
widget
  getBufferPaths :: Widget -> [BufferPath]
getBufferPaths (Widget a
widget) = forall a. IHaskellWidget a => a -> [BufferPath]
getBufferPaths a
widget
  getCommUUID :: Widget -> UUID
getCommUUID (Widget a
widget) = forall a. IHaskellWidget a => a -> UUID
getCommUUID a
widget
  open :: Widget -> (Value -> IO ()) -> IO ()
open (Widget a
widget) = forall a. IHaskellWidget a => a -> (Value -> IO ()) -> IO ()
open a
widget
  comm :: Widget -> Value -> (Value -> IO ()) -> IO ()
comm (Widget a
widget) = forall a.
IHaskellWidget a =>
a -> Value -> (Value -> IO ()) -> IO ()
comm a
widget
  close :: Widget -> Value -> IO ()
close (Widget a
widget) = forall a. IHaskellWidget a => a -> Value -> IO ()
close a
widget

instance Show Widget where
  show :: Widget -> String
show Widget
_ = String
"<Widget>"

instance Eq Widget where
  == :: Widget -> Widget -> Bool
(==) = forall a. Eq a => a -> a -> Bool
(==) forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` forall a. IHaskellWidget a => a -> UUID
getCommUUID

-- | Wrapper for ipython-kernel's DisplayData which allows sending multiple results from the same
-- expression.
data Display = Display [DisplayData]
             | ManyDisplay [Display]
  deriving (Int -> Display -> ShowS
[Display] -> ShowS
Display -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Display] -> ShowS
$cshowList :: [Display] -> ShowS
show :: Display -> String
$cshow :: Display -> String
showsPrec :: Int -> Display -> ShowS
$cshowsPrec :: Int -> Display -> ShowS
Show, Typeable, forall x. Rep Display x -> Display
forall x. Display -> Rep Display x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Display x -> Display
$cfrom :: forall x. Display -> Rep Display x
Generic)

instance ToJSON Display where
  toJSON :: Display -> Value
toJSON (Display [DisplayData]
d) = [Pair] -> Value
object (forall a b. (a -> b) -> [a] -> [b]
map DisplayData -> Pair
displayDataToJson [DisplayData]
d)
  toJSON (ManyDisplay [Display]
d) = forall a. ToJSON a => a -> Value
toJSON [Display]
d

instance Binary Display

instance Semigroup Display where
  ManyDisplay [Display]
a <> :: Display -> Display -> Display
<> ManyDisplay [Display]
b = [Display] -> Display
ManyDisplay ([Display]
a forall a. [a] -> [a] -> [a]
++ [Display]
b)
  ManyDisplay [Display]
a <> Display
b = [Display] -> Display
ManyDisplay ([Display]
a forall a. [a] -> [a] -> [a]
++ [Display
b])
  Display
a <> ManyDisplay [Display]
b = [Display] -> Display
ManyDisplay (Display
a forall a. a -> [a] -> [a]
: [Display]
b)
  Display
a <> Display
b = [Display] -> Display
ManyDisplay [Display
a, Display
b]

instance Monoid Display where
  mempty :: Display
mempty = [DisplayData] -> Display
Display []
  mappend :: Display -> Display -> Display
mappend = forall a. Semigroup a => a -> a -> a
(<>)

-- | All state stored in the kernel between executions.
data KernelState =
       KernelState
         { KernelState -> Int
getExecutionCounter :: Int
         , KernelState -> LintStatus
getLintStatus :: LintStatus   -- Whether to use hlint, and what arguments to pass it.
         , KernelState -> Bool
useSvg :: Bool
         , KernelState -> Bool
useShowErrors :: Bool
         , KernelState -> Bool
useShowTypes :: Bool
         , KernelState -> Bool
usePager :: Bool
         , KernelState -> Map UUID Widget
openComms :: Map UUID Widget
         , KernelState -> Bool
kernelDebug :: Bool
         , KernelState -> Bool
supportLibrariesAvailable :: Bool
         }
  deriving Int -> KernelState -> ShowS
[KernelState] -> ShowS
KernelState -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [KernelState] -> ShowS
$cshowList :: [KernelState] -> ShowS
show :: KernelState -> String
$cshow :: KernelState -> String
showsPrec :: Int -> KernelState -> ShowS
$cshowsPrec :: Int -> KernelState -> ShowS
Show

defaultKernelState :: KernelState
defaultKernelState :: KernelState
defaultKernelState = KernelState
  { getExecutionCounter :: Int
getExecutionCounter = Int
1
  , getLintStatus :: LintStatus
getLintStatus = LintStatus
LintOn
  , useSvg :: Bool
useSvg = Bool
True
  , useShowErrors :: Bool
useShowErrors = Bool
False
  , useShowTypes :: Bool
useShowTypes = Bool
False
  , usePager :: Bool
usePager = Bool
True
  , openComms :: Map UUID Widget
openComms = forall a. Monoid a => a
mempty
  , kernelDebug :: Bool
kernelDebug = Bool
False
  , supportLibrariesAvailable :: Bool
supportLibrariesAvailable = Bool
True
  }

-- | Kernel options to be set via `:set` and `:option`.
data KernelOpt =
       KernelOpt
         { KernelOpt -> [String]
getOptionName :: [String] -- ^ Ways to set this option via `:option`
         , KernelOpt -> [String]
getSetName :: [String] -- ^ Ways to set this option via `:set`
         , KernelOpt -> KernelState -> KernelState
getUpdateKernelState :: KernelState -> KernelState -- ^ Function to update the kernel
                                                              -- state.
         }

kernelOpts :: [KernelOpt]
kernelOpts :: [KernelOpt]
kernelOpts =
  [ [String] -> [String] -> (KernelState -> KernelState) -> KernelOpt
KernelOpt [String
"lint"] [] forall a b. (a -> b) -> a -> b
$ \KernelState
state -> KernelState
state { getLintStatus :: LintStatus
getLintStatus = LintStatus
LintOn }
  , [String] -> [String] -> (KernelState -> KernelState) -> KernelOpt
KernelOpt [String
"no-lint"] [] forall a b. (a -> b) -> a -> b
$ \KernelState
state -> KernelState
state { getLintStatus :: LintStatus
getLintStatus = LintStatus
LintOff }
  , [String] -> [String] -> (KernelState -> KernelState) -> KernelOpt
KernelOpt [String
"svg"] [] forall a b. (a -> b) -> a -> b
$ \KernelState
state -> KernelState
state { useSvg :: Bool
useSvg = Bool
True }
  , [String] -> [String] -> (KernelState -> KernelState) -> KernelOpt
KernelOpt [String
"no-svg"] [] forall a b. (a -> b) -> a -> b
$ \KernelState
state -> KernelState
state { useSvg :: Bool
useSvg = Bool
False }
  , [String] -> [String] -> (KernelState -> KernelState) -> KernelOpt
KernelOpt [String
"show-types"] [String
"+t"] forall a b. (a -> b) -> a -> b
$ \KernelState
state -> KernelState
state { useShowTypes :: Bool
useShowTypes = Bool
True }
  , [String] -> [String] -> (KernelState -> KernelState) -> KernelOpt
KernelOpt [String
"no-show-types"] [String
"-t"] forall a b. (a -> b) -> a -> b
$ \KernelState
state -> KernelState
state { useShowTypes :: Bool
useShowTypes = Bool
False }
  , [String] -> [String] -> (KernelState -> KernelState) -> KernelOpt
KernelOpt [String
"show-errors"] [] forall a b. (a -> b) -> a -> b
$ \KernelState
state -> KernelState
state { useShowErrors :: Bool
useShowErrors = Bool
True }
  , [String] -> [String] -> (KernelState -> KernelState) -> KernelOpt
KernelOpt [String
"no-show-errors"] [] forall a b. (a -> b) -> a -> b
$ \KernelState
state -> KernelState
state { useShowErrors :: Bool
useShowErrors = Bool
False }
  , [String] -> [String] -> (KernelState -> KernelState) -> KernelOpt
KernelOpt [String
"pager"] [] forall a b. (a -> b) -> a -> b
$ \KernelState
state -> KernelState
state { usePager :: Bool
usePager = Bool
True }
  , [String] -> [String] -> (KernelState -> KernelState) -> KernelOpt
KernelOpt [String
"no-pager"] [] forall a b. (a -> b) -> a -> b
$ \KernelState
state -> KernelState
state { usePager :: Bool
usePager = Bool
False }
  ]

-- | Current HLint status.
data LintStatus = LintOn
                | LintOff
  deriving (LintStatus -> LintStatus -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: LintStatus -> LintStatus -> Bool
$c/= :: LintStatus -> LintStatus -> Bool
== :: LintStatus -> LintStatus -> Bool
$c== :: LintStatus -> LintStatus -> Bool
Eq, Int -> LintStatus -> ShowS
[LintStatus] -> ShowS
LintStatus -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [LintStatus] -> ShowS
$cshowList :: [LintStatus] -> ShowS
show :: LintStatus -> String
$cshow :: LintStatus -> String
showsPrec :: Int -> LintStatus -> ShowS
$cshowsPrec :: Int -> LintStatus -> ShowS
Show)

-- | Send JSON objects with specific formats
data WidgetMsg = Open Widget Value
               |
               -- ^ Cause the interpreter to open a new comm, and register the associated widget in
               -- the kernelState. Also sends an initial state Value with comm_open.
                Update Widget Value
               |
               -- ^ Cause the interpreter to send a comm_msg containing a state update for the
               -- widget. Can be used to send fragments of state for update. Also updates the value
               -- of widget stored in the kernelState
                View Widget
               |
               -- ^ Cause the interpreter to send a comm_msg containing a display command for the
               -- frontend.
                Close Widget Value
               |
               -- ^ Cause the interpreter to close the comm associated with the widget. Also sends
               -- data with comm_close.
                Custom Widget Value
               |
               -- ^ A [method .= custom, content = value] message
                JSONValue Widget Value
               |
               -- ^ A json object that is sent to the widget without modifications.
                DispMsg Widget Display
               |
               -- ^ A 'display_data' message, sent as a [method .= custom] comm_msg
                ClrOutput Bool
  -- ^ A 'clear_output' message, sent as a clear_output message
  deriving (Int -> WidgetMsg -> ShowS
[WidgetMsg] -> ShowS
WidgetMsg -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [WidgetMsg] -> ShowS
$cshowList :: [WidgetMsg] -> ShowS
show :: WidgetMsg -> String
$cshow :: WidgetMsg -> String
showsPrec :: Int -> WidgetMsg -> ShowS
$cshowsPrec :: Int -> WidgetMsg -> ShowS
Show, Typeable)

data WidgetMethod = UpdateState Value [BufferPath]
                  | CustomContent Value
                  | DisplayWidget

instance ToJSON WidgetMethod where
  toJSON :: WidgetMethod -> Value
toJSON WidgetMethod
DisplayWidget = [Pair] -> Value
object [Key
"method" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= (Text
"display" :: Text)]
  toJSON (UpdateState Value
v [BufferPath]
bp) = [Pair] -> Value
object [Key
"method" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= (Text
"update" :: Text), Key
"state" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= Value
v, Key
"buffer_paths" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= [BufferPath]
bp]
  toJSON (CustomContent Value
v) = [Pair] -> Value
object [Key
"method" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= (Text
"custom" :: Text), Key
"content" forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
.= Value
v]

-- | Output of evaluation.
--
-- A result can either be intermediate or final.
-- Final result has Mimebundles ('DisplayData') and Comm operations
-- ('WidgetMsg') on top of Display outputs.
data EvaluationResult
  -- | An intermediate result which communicates what has been printed thus far.
  = IntermediateResult
        !Display
  | FinalResult
        !Display
        ![DisplayData]
        ![WidgetMsg]
  deriving Int -> EvaluationResult -> ShowS
[EvaluationResult] -> ShowS
EvaluationResult -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [EvaluationResult] -> ShowS
$cshowList :: [EvaluationResult] -> ShowS
show :: EvaluationResult -> String
$cshow :: EvaluationResult -> String
showsPrec :: Int -> EvaluationResult -> ShowS
$cshowsPrec :: Int -> EvaluationResult -> ShowS
Show


evaluationOutputs :: EvaluationResult -> Display
evaluationOutputs :: EvaluationResult -> Display
evaluationOutputs EvaluationResult
er =
  case EvaluationResult
er of
    IntermediateResult Display
outputs -> Display
outputs
    FinalResult Display
outputs [DisplayData]
_ [WidgetMsg]
_ -> Display
outputs

-- | Duplicate a message header, giving it a new UUID and message type.
dupHeader :: MessageHeader -> MessageType -> IO MessageHeader
dupHeader :: MessageHeader -> MessageType -> IO MessageHeader
dupHeader MessageHeader
hdr MessageType
messageType = do
  UUID
uuid <- forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO IO UUID
random
  forall (m :: * -> *) a. Monad m => a -> m a
return MessageHeader
hdr { mhMessageId :: UUID
mhMessageId = UUID
uuid, mhMsgType :: MessageType
mhMsgType = MessageType
messageType }

-- | Modifies a header and appends the version of the Widget Messaging Protocol as metadata
setVersion :: MessageHeader  -- ^ The header to modify
           -> String         -- ^ The version to set
           -> MessageHeader  -- ^ The modified header

-- We use the 'fromList' function from "Data.HashMap.Strict" (or
-- "Data.Aeson.KeyMap") instead of the 'object' function from "Data.Aeson"
-- because 'object' returns a 'Value', but metadata needs an 'Object'.
#if MIN_VERSION_aeson(2,0,0)
setVersion :: MessageHeader -> String -> MessageHeader
setVersion MessageHeader
hdr String
v = MessageHeader
hdr { mhMetadata :: Metadata
mhMetadata = Object -> Metadata
Metadata (forall v. [(Key, v)] -> KeyMap v
KeyMap.fromList [(Key
"version", Text -> Value
String forall a b. (a -> b) -> a -> b
$ String -> Text
pack String
v)]) }
#else
setVersion hdr v = hdr { mhMetadata = Metadata (HashMap.fromList [("version", String $ pack v)]) }
#endif

-- | Whether or not an error occurred.
data ErrorOccurred = Success
                   | Failure
  deriving (Int -> ErrorOccurred -> ShowS
[ErrorOccurred] -> ShowS
ErrorOccurred -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ErrorOccurred] -> ShowS
$cshowList :: [ErrorOccurred] -> ShowS
show :: ErrorOccurred -> String
$cshow :: ErrorOccurred -> String
showsPrec :: Int -> ErrorOccurred -> ShowS
$cshowsPrec :: Int -> ErrorOccurred -> ShowS
Show, ErrorOccurred -> ErrorOccurred -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ErrorOccurred -> ErrorOccurred -> Bool
$c/= :: ErrorOccurred -> ErrorOccurred -> Bool
== :: ErrorOccurred -> ErrorOccurred -> Bool
$c== :: ErrorOccurred -> ErrorOccurred -> Bool
Eq)