-- Copyright 2016 Evan Laforge
-- This program is distributed under the terms of the GNU General Public
-- License 3.0, see COPYING or http://www.gnu.org/licenses/gpl-3.0.txt

-- | Define the protocol between the sequencer's repl port and the repl client.
module App.ReplProtocol (
    -- * types
    Query(..), NotifySeq(..)
    , Response(..)
    , CmdResult(..), Result(..), Editor(..), File(..)
    , FileType(..), file_type_extension
    , empty_result, error_result, raw

    -- * repl protocol
    , initialize
    , query_cmd, query_save_file, query_completion
    , notify
    , query_cmd_simple

    -- * seq protocol
    , seq_receive, seq_send

    -- * format
    , format_result
    , abbreviate_package_loads
) where
import qualified Control.DeepSeq as DeepSeq
import qualified Control.Exception as Exception
import qualified Data.ByteString as ByteString
import qualified Data.Text as Text
import qualified Network.Socket as Socket
import qualified System.IO as IO
import qualified System.IO.Error as IO.Error
import qualified System.Posix as Posix

import qualified Util.Lists as Lists
import qualified Util.Log as Log
import qualified Util.Network as Network
import qualified Util.PPrint as PPrint
import qualified Util.Serialize as Serialize
import           Util.Serialize (get, get_tag, put, put_tag)

import qualified App.Config as Config

import           Global


-- | This is a simple RPC mechanism.  'Query' goes to the server, which
-- responds with the matching 'Response'.
data Query =
    QSaveFile
    | QCommand !Text
    | QCompletion !Text
    -- | A notification doesn't have a corrpesponding 'Response'.
    | QNotify !NotifySeq
    deriving (Query -> Query -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Query -> Query -> Bool
$c/= :: Query -> Query -> Bool
== :: Query -> Query -> Bool
$c== :: Query -> Query -> Bool
Eq, Int -> Query -> ShowS
[Query] -> ShowS
Query -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Query] -> ShowS
$cshowList :: [Query] -> ShowS
show :: Query -> String
$cshow :: Query -> String
showsPrec :: Int -> Query -> ShowS
$cshowsPrec :: Int -> Query -> ShowS
Show)

data NotifySeq =
    -- | These are so the sequencer knows if there are open editors, so it
    -- can refuse to quit and hence orphan them.
    NEditorOpened | NEditorClosed
    deriving (NotifySeq -> NotifySeq -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: NotifySeq -> NotifySeq -> Bool
$c/= :: NotifySeq -> NotifySeq -> Bool
== :: NotifySeq -> NotifySeq -> Bool
$c== :: NotifySeq -> NotifySeq -> Bool
Eq, Int -> NotifySeq -> ShowS
[NotifySeq] -> ShowS
NotifySeq -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [NotifySeq] -> ShowS
$cshowList :: [NotifySeq] -> ShowS
show :: NotifySeq -> String
$cshow :: NotifySeq -> String
showsPrec :: Int -> NotifySeq -> ShowS
$cshowsPrec :: Int -> NotifySeq -> ShowS
Show)

data Response =
    RSaveFile !(Maybe FilePath) -- ^ current save file
    | RCommand !CmdResult
    | RCompletion ![Text] -- ^ possible completions for the prefix
    deriving (Response -> Response -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Response -> Response -> Bool
$c/= :: Response -> Response -> Bool
== :: Response -> Response -> Bool
$c== :: Response -> Response -> Bool
Eq, Int -> Response -> ShowS
[Response] -> ShowS
Response -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Response] -> ShowS
$cshowList :: [Response] -> ShowS
show :: Response -> String
$cshow :: Response -> String
showsPrec :: Int -> Response -> ShowS
$cshowsPrec :: Int -> Response -> ShowS
Show)

instance Pretty Query where
    pretty :: Query -> Text
pretty = \case
        Query
QSaveFile -> Text
"SaveFile"
        QCommand Text
t -> Text
"Command: " forall a. Semigroup a => a -> a -> a
<> Text
t
        QCompletion Text
t -> Text
"Completion: " forall a. Semigroup a => a -> a -> a
<> Text
t
        QNotify NotifySeq
notify -> forall a. Show a => a -> Text
showt NotifySeq
notify

data CmdResult = CmdResult !Result ![Log.Msg]
    deriving (CmdResult -> CmdResult -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CmdResult -> CmdResult -> Bool
$c/= :: CmdResult -> CmdResult -> Bool
== :: CmdResult -> CmdResult -> Bool
$c== :: CmdResult -> CmdResult -> Bool
Eq, Int -> CmdResult -> ShowS
[CmdResult] -> ShowS
CmdResult -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CmdResult] -> ShowS
$cshowList :: [CmdResult] -> ShowS
show :: CmdResult -> String
$cshow :: CmdResult -> String
showsPrec :: Int -> CmdResult -> ShowS
$cshowsPrec :: Int -> CmdResult -> ShowS
Show)

data Result =
    Raw !Text -- ^ Print this text directly, without formatting it.
    | Format !Text -- ^ Format and print.
    | Edit !(NonEmpty Editor) -- ^ Edit one or more files.
    deriving (Result -> Result -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Result -> Result -> Bool
$c/= :: Result -> Result -> Bool
== :: Result -> Result -> Bool
$c== :: Result -> Result -> Bool
Eq, Int -> Result -> ShowS
[Result] -> ShowS
Result -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Result] -> ShowS
$cshowList :: [Result] -> ShowS
show :: Result -> String
$cshow :: Result -> String
showsPrec :: Int -> Result -> ShowS
$cshowsPrec :: Int -> Result -> ShowS
Show)

-- | Open an editor locally.
--
-- How this works is that a Cmd will return the special 'Edit' value, which
-- will be specially interpreted in the repl to open an editor.  The editor
-- will be configured such that when it saves, it will send its buffer to
-- the sequencer via a REPL function with a string argument (configured by
-- '_on_save').  The text goes back separately via the @send@ command, since
-- the REPL itself will be blocked waiting for the editor to exit.
data Editor = Editor {
    Editor -> File
_file :: !File
    -- | Start editing on this line.
    , Editor -> Int
_line_number :: !Int
    -- | Send QCommands when the editor saves or quits.  A %s is replaced by
    -- the edited text.
    , Editor -> Maybe Text
_on_save :: !(Maybe Text)
    -- | Send on an explicit send cmd, @gs@ in vim.
    , Editor -> Maybe Text
_on_send :: !(Maybe Text)
    } deriving (Editor -> Editor -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Editor -> Editor -> Bool
$c/= :: Editor -> Editor -> Bool
== :: Editor -> Editor -> Bool
$c== :: Editor -> Editor -> Bool
Eq, Int -> Editor -> ShowS
[Editor] -> ShowS
Editor -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Editor] -> ShowS
$cshowList :: [Editor] -> ShowS
show :: Editor -> String
$cshow :: Editor -> String
showsPrec :: Int -> Editor -> ShowS
$cshowsPrec :: Int -> Editor -> ShowS
Show)

data File =
    FileName !FilePath -- ^ open this file
    | Text !FileType !Text -- ^ open this text in a temp file
    deriving (File -> File -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: File -> File -> Bool
$c/= :: File -> File -> Bool
== :: File -> File -> Bool
$c== :: File -> File -> Bool
Eq, Int -> File -> ShowS
[File] -> ShowS
File -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [File] -> ShowS
$cshowList :: [File] -> ShowS
show :: File -> String
$cshow :: File -> String
showsPrec :: Int -> File -> ShowS
$cshowsPrec :: Int -> File -> ShowS
Show)

data FileType = NoType | Ky | TScore
    deriving (FileType -> FileType -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: FileType -> FileType -> Bool
$c/= :: FileType -> FileType -> Bool
== :: FileType -> FileType -> Bool
$c== :: FileType -> FileType -> Bool
Eq, Int -> FileType -> ShowS
[FileType] -> ShowS
FileType -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FileType] -> ShowS
$cshowList :: [FileType] -> ShowS
show :: FileType -> String
$cshow :: FileType -> String
showsPrec :: Int -> FileType -> ShowS
$cshowsPrec :: Int -> FileType -> ShowS
Show, FileType
forall a. a -> a -> Bounded a
maxBound :: FileType
$cmaxBound :: FileType
minBound :: FileType
$cminBound :: FileType
Bounded, Int -> FileType
FileType -> Int
FileType -> [FileType]
FileType -> FileType
FileType -> FileType -> [FileType]
FileType -> FileType -> FileType -> [FileType]
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: FileType -> FileType -> FileType -> [FileType]
$cenumFromThenTo :: FileType -> FileType -> FileType -> [FileType]
enumFromTo :: FileType -> FileType -> [FileType]
$cenumFromTo :: FileType -> FileType -> [FileType]
enumFromThen :: FileType -> FileType -> [FileType]
$cenumFromThen :: FileType -> FileType -> [FileType]
enumFrom :: FileType -> [FileType]
$cenumFrom :: FileType -> [FileType]
fromEnum :: FileType -> Int
$cfromEnum :: FileType -> Int
toEnum :: Int -> FileType
$ctoEnum :: Int -> FileType
pred :: FileType -> FileType
$cpred :: FileType -> FileType
succ :: FileType -> FileType
$csucc :: FileType -> FileType
Enum)

file_type_extension :: FileType -> String
file_type_extension :: FileType -> String
file_type_extension = \case
    FileType
NoType -> String
""
    FileType
Ky -> String
".ky"
    FileType
TScore -> String
".tscore"

empty_result :: Result
empty_result :: Result
empty_result = Text -> Result
Raw Text
""

error_result :: Text -> CmdResult
error_result :: Text -> CmdResult
error_result Text
msg = Result -> [Msg] -> CmdResult
CmdResult Result
empty_result [Stack => Priority -> Maybe Stack -> Text -> Msg
Log.msg Priority
Log.Error forall a. Maybe a
Nothing Text
msg]

raw :: Text -> CmdResult
raw :: Text -> CmdResult
raw Text
msg = Result -> [Msg] -> CmdResult
CmdResult (Text -> Result
Raw Text
msg) []

-- * repl protocol

initialize :: IO a -> IO a
initialize :: forall a. IO a -> IO a
initialize IO a
app = forall a. IO a -> IO a
Socket.withSocketsDo forall a b. (a -> b) -> a -> b
$ do
    Signal -> Handler -> Maybe SignalSet -> IO Handler
Posix.installHandler Signal
Posix.sigPIPE (IO () -> Handler
Posix.Catch IO ()
sigpipe) forall a. Maybe a
Nothing
    IO a
app
    where
    sigpipe :: IO ()
sigpipe = Handle -> String -> IO ()
IO.hPutStrLn Handle
IO.stderr
        String
"caught SIGPIPE, reader must have closed the socket"

-- | Client send and receive.
query :: Network.Addr -> Query -> IO (Either Exception.IOException Response)
query :: Addr -> Query -> IO (Either IOException Response)
query Addr
addr Query
query = forall e a. Exception e => IO a -> IO (Either e a)
Exception.try forall a b. (a -> b) -> a -> b
$ forall a. Addr -> (Handle -> IO a) -> IO a
Network.withHandle Addr
addr forall a b. (a -> b) -> a -> b
$ \Handle
hdl -> do
    Handle -> Query -> IO ()
repl_send Handle
hdl Query
query
    Handle -> IO ()
IO.hFlush Handle
hdl
    Handle -> IO Response
repl_receive Handle
hdl

-- | Like 'query', but don't expect a response.
notify :: Network.Addr -> NotifySeq -> IO (Either Exception.IOException ())
notify :: Addr -> NotifySeq -> IO (Either IOException ())
notify Addr
addr NotifySeq
notify = forall e a. Exception e => IO a -> IO (Either e a)
Exception.try forall a b. (a -> b) -> a -> b
$ forall a. Addr -> (Handle -> IO a) -> IO a
Network.withHandle Addr
addr forall a b. (a -> b) -> a -> b
$ \Handle
hdl -> do
    Handle -> Query -> IO ()
repl_send Handle
hdl forall a b. (a -> b) -> a -> b
$ NotifySeq -> Query
QNotify NotifySeq
notify
    Handle -> IO ()
IO.hFlush Handle
hdl

-- | Send a 'QCommand'.
query_cmd :: Network.Addr -> Text -> IO CmdResult
query_cmd :: Addr -> Text -> IO CmdResult
query_cmd Addr
addr Text
cmd = Addr -> Query -> IO (Either IOException Response)
query Addr
addr (Text -> Query
QCommand Text
cmd) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. \case
    Right (RCommand CmdResult
result) -> CmdResult
result
    Right Response
response -> Text -> CmdResult
raw forall a b. (a -> b) -> a -> b
$ Text
"unexpected response: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> Text
showt Response
response
    Left IOException
exc
        | IOException -> Bool
IO.Error.isDoesNotExistError IOException
exc -> Text -> CmdResult
raw forall a b. (a -> b) -> a -> b
$
            Text
"addr (" forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> Text
showt Addr
addr forall a. Semigroup a => a -> a -> a
<> Text
") threw ENOENT,\
            \ this can happen if karya is not running, or it's stuck."
        | Bool
otherwise -> Text -> CmdResult
raw forall a b. (a -> b) -> a -> b
$ Text
"exception: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> Text
showt IOException
exc

-- | A simple one-shot 'query_cmd'.
query_cmd_simple :: Text -> IO Text
query_cmd_simple :: Text -> IO Text
query_cmd_simple Text
cmd =
    CmdResult -> Text
format_result forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Addr -> Text -> IO CmdResult
query_cmd (String -> Addr
Network.Unix String
Config.repl_socket_name) Text
cmd

-- | Ask for the current save filename.  Nothing for an error, and Just Nothing
-- for no save file.
query_save_file :: Network.Addr -> IO (Maybe (Maybe FilePath))
query_save_file :: Addr -> IO (Maybe (Maybe String))
query_save_file Addr
addr = Addr -> Query -> IO (Either IOException Response)
query Addr
addr Query
QSaveFile forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Right (RSaveFile Maybe String
fname) -> forall (m :: * -> *) a. Monad m => a -> m a
return (forall a. a -> Maybe a
Just Maybe String
fname)
    Left IOException
exc | IOException -> Bool
IO.Error.isDoesNotExistError IOException
exc -> forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing
    Either IOException Response
response -> do
        forall (m :: * -> *). (Stack, LogMonad m) => Text -> m ()
Log.error forall a b. (a -> b) -> a -> b
$ Text
"unexpected response to QSaveFile: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> Text
showt Either IOException Response
response
        forall (m :: * -> *) a. Monad m => a -> m a
return forall a. Maybe a
Nothing

query_completion :: Network.Addr -> Text -> IO [Text]
query_completion :: Addr -> Text -> IO [Text]
query_completion Addr
addr Text
prefix = Addr -> Query -> IO (Either IOException Response)
query Addr
addr (Text -> Query
QCompletion Text
prefix) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Right (RCompletion [Text]
words) -> forall (m :: * -> *) a. Monad m => a -> m a
return [Text]
words
    Left IOException
exc | IOException -> Bool
IO.Error.isDoesNotExistError IOException
exc -> forall (m :: * -> *) a. Monad m => a -> m a
return []
    Either IOException Response
response -> do
        forall (m :: * -> *). (Stack, LogMonad m) => Text -> m ()
Log.error forall a b. (a -> b) -> a -> b
$ Text
"unexpected response to QCompletion: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> Text
showt Either IOException Response
response
        forall (m :: * -> *) a. Monad m => a -> m a
return []

-- * low level implementation

-- Specialize 'send' and 'receive' to keep types consistent between seq and
-- repl.

seq_receive :: IO.Handle -> IO Query
seq_receive :: Handle -> IO Query
seq_receive = forall a. Serialize a => Handle -> IO a
receive

seq_send :: IO.Handle -> Response -> IO ()
seq_send :: Handle -> Response -> IO ()
seq_send = forall a. Serialize a => Handle -> a -> IO ()
send

repl_send :: IO.Handle -> Query -> IO ()
repl_send :: Handle -> Query -> IO ()
repl_send = forall a. Serialize a => Handle -> a -> IO ()
send

repl_receive :: IO.Handle -> IO Response
repl_receive :: Handle -> IO Response
repl_receive = forall a. Serialize a => Handle -> IO a
receive

-- | Write a msg size and then the msg.
send :: Serialize.Serialize a => IO.Handle -> a -> IO ()
send :: forall a. Serialize a => Handle -> a -> IO ()
send Handle
hdl a
msg = do
    Handle -> ByteString -> IO ()
ByteString.hPut Handle
hdl (forall a. Serialize a => a -> ByteString
Serialize.encode (ByteString -> Int
ByteString.length ByteString
bytes))
    Handle -> ByteString -> IO ()
ByteString.hPut Handle
hdl ByteString
bytes
    where bytes :: ByteString
bytes = forall a. Serialize a => a -> ByteString
Serialize.encode a
msg

receive :: Serialize.Serialize a => IO.Handle -> IO a
receive :: forall a. Serialize a => Handle -> IO a
receive Handle
hdl = do
    let int_bytes :: Int
int_bytes = ByteString -> Int
ByteString.length (forall a. Serialize a => a -> ByteString
Serialize.encode (Int
0 :: Int))
    ByteString
size <- Handle -> Int -> IO ByteString
ByteString.hGet Handle
hdl Int
int_bytes
    -- If the app just quit, say because it got a 'quit' cmd, the accept loop
    -- will continue to live long enough to accept another query, but then
    -- close the socket.  A proper fix might be to block the accept loop while
    -- it handles a cmd, but it's a tiny corner case and this seems to do just
    -- as well.
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (ByteString
size forall a. Eq a => a -> a -> Bool
== ByteString
"") forall a b. (a -> b) -> a -> b
$
        forall e a. Exception e => e -> IO a
Exception.throwIO forall a b. (a -> b) -> a -> b
$ IOErrorType
-> String -> Maybe Handle -> Maybe String -> IOException
IO.Error.mkIOError IOErrorType
IO.Error.doesNotExistErrorType
            String
"client closed handle" forall a. Maybe a
Nothing forall a. Maybe a
Nothing
    Int
size <- forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (forall (m :: * -> *) a. (Stack, MonadIO m) => Text -> m a
errorIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
txt) forall (m :: * -> *) a. Monad m => a -> m a
return (forall a. Serialize a => ByteString -> Either String a
Serialize.decode ByteString
size)
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (forall (m :: * -> *) a. (Stack, MonadIO m) => Text -> m a
errorIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
txt) forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Serialize a => ByteString -> Either String a
Serialize.decode
        forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Handle -> Int -> IO ByteString
ByteString.hGet Handle
hdl Int
size

-- * format

-- | Format the response and strip trailing whitespace.
format_result :: CmdResult -> Text
format_result :: CmdResult -> Text
format_result (CmdResult Result
response [Msg]
logs_) =
    Text -> Text
Text.stripEnd forall a b. (a -> b) -> a -> b
$ [Text] -> Text
Text.unlines forall a b. (a -> b) -> a -> b
$
        (if forall (t :: * -> *) a. Foldable t => t a -> Bool
null [Msg]
logs then [] else Text
"Logs:" forall a. a -> [a] -> [a]
: forall a b. (a -> b) -> [a] -> [b]
map forall a. Pretty a => a -> Text
pretty [Msg]
logs forall a. [a] -> [a] -> [a]
++ [Text
""])
            forall a. [a] -> [a] -> [a]
++ [Result -> Text
format Result
response]
    where logs :: [Msg]
logs = [Msg] -> [Msg]
abbreviate_package_loads [Msg]
logs_

format :: Result -> Text
format :: Result -> Text
format (Raw Text
val) = Text
val
format (Format Text
val) = String -> Text
txt forall a b. (a -> b) -> a -> b
$ ShowS
PPrint.format_str forall a b. (a -> b) -> a -> b
$ Text -> String
untxt Text
val
format (Edit NonEmpty Editor
editors) = Text
"Edit: " forall a. Semigroup a => a -> a -> a
<> forall a. Show a => a -> Text
showt (forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Editor -> File
_file NonEmpty Editor
editors)

abbreviate_package_loads :: [Log.Msg] -> [Log.Msg]
abbreviate_package_loads :: [Msg] -> [Msg]
abbreviate_package_loads [Msg]
logs = [Msg]
loaded forall a. [a] -> [a] -> [a]
++ forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. Msg -> Bool
package_log) [Msg]
logs
    where
    loaded :: [Msg]
loaded =
        [ Stack => Priority -> Maybe Stack -> Text -> Msg
Log.msg Priority
Log.Notice forall a. Maybe a
Nothing forall a b. (a -> b) -> a -> b
$
            Text
"Loaded " forall a. Semigroup a => a -> a -> a
<> String -> Text
Text.pack (forall a. Show a => a -> String
show Int
packages) forall a. Semigroup a => a -> a -> a
<> Text
" packages"
        | Int
packages forall a. Ord a => a -> a -> Bool
> Int
0
        ]
    packages :: Int
packages =
        forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Int
Lists.count ((Text
"Loading package" `Text.isPrefixOf`) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Msg -> Text
Log.msg_text) [Msg]
logs
    package_log :: Msg -> Bool
package_log Msg
log = forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (Text -> Text -> Bool
`Text.isPrefixOf` Msg -> Text
Log.msg_text Msg
log)
        [Text
"Loading package", Text
"linking ...", Text
"done."]

-- * instances

instance Serialize.Serialize Query where
    put :: Putter Query
put Query
QSaveFile = Word8 -> Put
put_tag Word8
0
    put (QCommand Text
a) = Word8 -> Put
put_tag Word8
1 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put Text
a
    put (QCompletion Text
a) = Word8 -> Put
put_tag Word8
2 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put Text
a
    put (QNotify NotifySeq
a) = Word8 -> Put
put_tag Word8
3 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put NotifySeq
a
    get :: Get Query
get = Get Word8
get_tag forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Word8
0 -> forall (m :: * -> *) a. Monad m => a -> m a
return Query
QSaveFile
        Word8
1 -> Text -> Query
QCommand forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get
        Word8
2 -> Text -> Query
QCompletion forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get
        Word8
3 -> NotifySeq -> Query
QNotify forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get
        Word8
tag -> forall a. String -> Word8 -> Get a
Serialize.bad_tag String
"Query" Word8
tag

instance Serialize.Serialize NotifySeq where
    put :: Putter NotifySeq
put = \case
        NotifySeq
NEditorOpened -> Word8 -> Put
put_tag Word8
0
        NotifySeq
NEditorClosed -> Word8 -> Put
put_tag Word8
1
    get :: Get NotifySeq
get = Get Word8
get_tag forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Word8
0 -> forall (m :: * -> *) a. Monad m => a -> m a
return NotifySeq
NEditorOpened
        Word8
1 -> forall (m :: * -> *) a. Monad m => a -> m a
return NotifySeq
NEditorClosed
        Word8
tag -> forall a. String -> Word8 -> Get a
Serialize.bad_tag String
"NotifySeq" Word8
tag

instance Serialize.Serialize Response where
    put :: Putter Response
put (RSaveFile Maybe String
a) = Word8 -> Put
put_tag Word8
0 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put Maybe String
a
    put (RCommand CmdResult
a) = Word8 -> Put
put_tag Word8
1 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put CmdResult
a
    put (RCompletion [Text]
a) = Word8 -> Put
put_tag Word8
2 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put [Text]
a
    get :: Get Response
get = Get Word8
get_tag forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Word8
0 -> Maybe String -> Response
RSaveFile forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get
        Word8
1 -> CmdResult -> Response
RCommand forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get
        Word8
2 -> [Text] -> Response
RCompletion forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get
        Word8
tag -> forall a. String -> Word8 -> Get a
Serialize.bad_tag String
"Response" Word8
tag

instance Serialize.Serialize CmdResult where
    put :: Putter CmdResult
put (CmdResult Result
a [Msg]
b) = forall a. Serialize a => Putter a
put Result
a forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put [Msg]
b
    get :: Get CmdResult
get = Result -> [Msg] -> CmdResult
CmdResult forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. Serialize a => Get a
get

instance Serialize.Serialize Result where
    put :: Putter Result
put (Raw Text
a) = Word8 -> Put
put_tag Word8
0 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put Text
a
    put (Format Text
a) = Word8 -> Put
put_tag Word8
1 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put Text
a
    put (Edit NonEmpty Editor
a) = Word8 -> Put
put_tag Word8
2 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put NonEmpty Editor
a
    get :: Get Result
get = Get Word8
get_tag forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Word8
0 -> Text -> Result
Raw forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get
        Word8
1 -> Text -> Result
Format forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get
        Word8
2 -> NonEmpty Editor -> Result
Edit forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get
        Word8
tag -> forall a. String -> Word8 -> Get a
Serialize.bad_tag String
"Result" Word8
tag

instance Serialize.Serialize Editor where
    put :: Putter Editor
put (Editor File
a Int
b Maybe Text
c Maybe Text
d) = forall a. Serialize a => Putter a
put File
a forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put Int
b forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put Maybe Text
c forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put Maybe Text
d
    get :: Get Editor
get = File -> Int -> Maybe Text -> Maybe Text -> Editor
Editor forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. Serialize a => Get a
get forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. Serialize a => Get a
get forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. Serialize a => Get a
get

instance Serialize.Serialize File where
    put :: Putter File
put (FileName String
a) = Word8 -> Put
put_tag Word8
0 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put String
a
    put (Text FileType
a Text
b) = Word8 -> Put
put_tag Word8
1 forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put FileType
a forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> forall a. Serialize a => Putter a
put Text
b
    get :: Get File
get = Get Word8
get_tag forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Word8
0 -> String -> File
FileName forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get
        Word8
1 -> FileType -> Text -> File
Text forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Serialize a => Get a
get forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. Serialize a => Get a
get
        Word8
tag -> forall a. String -> Word8 -> Get a
Serialize.bad_tag String
"File" Word8
tag

instance Serialize.Serialize FileType where
    put :: Putter FileType
put = forall a. Enum a => a -> Put
Serialize.put_enum_unsafe
    get :: Get FileType
get = forall a. (Bounded a, Enum a) => Get a
Serialize.get_enum_unsafe

instance DeepSeq.NFData CmdResult where
    rnf :: CmdResult -> ()
rnf (CmdResult Result
a [Msg]
b) = Result
a seq :: forall a b. a -> b -> b
`seq` [Msg]
b seq :: forall a b. a -> b -> b
`seq` ()