{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
module System.Texrunner.Online
( OnlineTex
, runOnlineTex
, runOnlineTex'
, hbox
, hsize
, showthe
, onlineTexParser
, texPutStrLn
, TexStreams
, getInStream
, getOutStream
, clearUnblocking
) where
import Control.Applicative
import Control.Monad (void)
import Control.Monad.Reader
import qualified Data.Attoparsec.ByteString as A
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as C8
import qualified Data.ByteString.Lazy.Char8 as LC8
import Data.List (find)
import Data.Maybe
import Data.Monoid
import qualified Data.Traversable as T
import System.Directory
import System.FilePath
import System.IO
import System.IO.Streams as Streams
import System.IO.Streams.Attoparsec
import System.IO.Temp
import System.Process as P (runInteractiveProcess)
import System.Texrunner.Parse
newtype OnlineTex a = OnlineTex {forall a.
OnlineTex a
-> ReaderT (OutputStream ByteString, InputStream ByteString) IO a
runOnlineTexT :: ReaderT TexStreams IO a}
deriving (forall a b. a -> OnlineTex b -> OnlineTex a
forall a b. (a -> b) -> OnlineTex a -> OnlineTex b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> OnlineTex b -> OnlineTex a
$c<$ :: forall a b. a -> OnlineTex b -> OnlineTex a
fmap :: forall a b. (a -> b) -> OnlineTex a -> OnlineTex b
$cfmap :: forall a b. (a -> b) -> OnlineTex a -> OnlineTex b
Functor, Functor OnlineTex
forall a. a -> OnlineTex a
forall a b. OnlineTex a -> OnlineTex b -> OnlineTex a
forall a b. OnlineTex a -> OnlineTex b -> OnlineTex b
forall a b. OnlineTex (a -> b) -> OnlineTex a -> OnlineTex b
forall a b c.
(a -> b -> c) -> OnlineTex a -> OnlineTex b -> OnlineTex c
forall (f :: * -> *).
Functor f
-> (forall a. a -> f a)
-> (forall a b. f (a -> b) -> f a -> f b)
-> (forall a b c. (a -> b -> c) -> f a -> f b -> f c)
-> (forall a b. f a -> f b -> f b)
-> (forall a b. f a -> f b -> f a)
-> Applicative f
<* :: forall a b. OnlineTex a -> OnlineTex b -> OnlineTex a
$c<* :: forall a b. OnlineTex a -> OnlineTex b -> OnlineTex a
*> :: forall a b. OnlineTex a -> OnlineTex b -> OnlineTex b
$c*> :: forall a b. OnlineTex a -> OnlineTex b -> OnlineTex b
liftA2 :: forall a b c.
(a -> b -> c) -> OnlineTex a -> OnlineTex b -> OnlineTex c
$cliftA2 :: forall a b c.
(a -> b -> c) -> OnlineTex a -> OnlineTex b -> OnlineTex c
<*> :: forall a b. OnlineTex (a -> b) -> OnlineTex a -> OnlineTex b
$c<*> :: forall a b. OnlineTex (a -> b) -> OnlineTex a -> OnlineTex b
pure :: forall a. a -> OnlineTex a
$cpure :: forall a. a -> OnlineTex a
Applicative, Applicative OnlineTex
forall a. a -> OnlineTex a
forall a b. OnlineTex a -> OnlineTex b -> OnlineTex b
forall a b. OnlineTex a -> (a -> OnlineTex b) -> OnlineTex b
forall (m :: * -> *).
Applicative m
-> (forall a b. m a -> (a -> m b) -> m b)
-> (forall a b. m a -> m b -> m b)
-> (forall a. a -> m a)
-> Monad m
return :: forall a. a -> OnlineTex a
$creturn :: forall a. a -> OnlineTex a
>> :: forall a b. OnlineTex a -> OnlineTex b -> OnlineTex b
$c>> :: forall a b. OnlineTex a -> OnlineTex b -> OnlineTex b
>>= :: forall a b. OnlineTex a -> (a -> OnlineTex b) -> OnlineTex b
$c>>= :: forall a b. OnlineTex a -> (a -> OnlineTex b) -> OnlineTex b
Monad, Monad OnlineTex
forall a. IO a -> OnlineTex a
forall (m :: * -> *).
Monad m -> (forall a. IO a -> m a) -> MonadIO m
liftIO :: forall a. IO a -> OnlineTex a
$cliftIO :: forall a. IO a -> OnlineTex a
MonadIO, MonadReader TexStreams)
runOnlineTex :: String
-> [String]
-> ByteString
-> OnlineTex a
-> IO a
runOnlineTex :: forall a. String -> [String] -> ByteString -> OnlineTex a -> IO a
runOnlineTex String
command [String]
args ByteString
preamble OnlineTex a
process =
(\(a
a,TexLog
_,Maybe ByteString
_) -> a
a) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a.
String
-> [String]
-> ByteString
-> OnlineTex a
-> IO (a, TexLog, Maybe ByteString)
runOnlineTex' String
command [String]
args ByteString
preamble OnlineTex a
process
runOnlineTex' :: String
-> [String]
-> ByteString
-> OnlineTex a
-> IO (a, TexLog, Maybe LC8.ByteString)
runOnlineTex' :: forall a.
String
-> [String]
-> ByteString
-> OnlineTex a
-> IO (a, TexLog, Maybe ByteString)
runOnlineTex' String
command [String]
args ByteString
preamble OnlineTex a
process =
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
String -> (String -> m a) -> m a
withSystemTempDirectory String
"onlinetex." forall a b. (a -> b) -> a -> b
$ \String
path -> do
(OutputStream ByteString
outS, InputStream ByteString
inS, ProcessHandle
h) <- String
-> Maybe [(String, String)]
-> String
-> [String]
-> ByteString
-> IO
(OutputStream ByteString, InputStream ByteString, ProcessHandle)
mkTexHandles String
path forall a. Maybe a
Nothing String
command [String]
args ByteString
preamble
a
a <- forall a b c. (a -> b -> c) -> b -> a -> c
flip forall r (m :: * -> *) a. ReaderT r m a -> r -> m a
runReaderT (OutputStream ByteString
outS, InputStream ByteString
inS) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a.
OnlineTex a
-> ReaderT (OutputStream ByteString, InputStream ByteString) IO a
runOnlineTexT forall a b. (a -> b) -> a -> b
$ OnlineTex a
process
forall a. Maybe a -> OutputStream a -> IO ()
write forall a. Maybe a
Nothing OutputStream ByteString
outS
ExitCode
_ <- ProcessHandle -> IO ExitCode
waitForProcess ProcessHandle
h
Maybe String
pdfPath <- forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((forall a. Eq a => a -> a -> Bool
==String
".pdf") forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
takeExtension) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO [String]
getDirectoryContents String
path
Maybe ByteString
pdfFile <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
T.mapM (String -> IO ByteString
LC8.readFile forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String
path String -> String -> String
</>)) Maybe String
pdfPath
Maybe String
logPath <- forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((forall a. Eq a => a -> a -> Bool
==String
".log") forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
takeExtension) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> IO [String]
getDirectoryContents String
path
Maybe ByteString
logFile <- forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
T.mapM (String -> IO ByteString
C8.readFile forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String
path String -> String -> String
</>)) Maybe String
logPath
forall (m :: * -> *) a. Monad m => a -> m a
return (a
a, ByteString -> TexLog
parseLog forall a b. (a -> b) -> a -> b
$ forall a. a -> Maybe a -> a
fromMaybe ByteString
"" Maybe ByteString
logFile, Maybe ByteString
pdfFile)
hbox :: Fractional n => ByteString -> OnlineTex (Box n)
hbox :: forall n. Fractional n => ByteString -> OnlineTex (Box n)
hbox ByteString
str = do
OnlineTex ()
clearUnblocking
ByteString -> OnlineTex ()
texPutStrLn forall a b. (a -> b) -> a -> b
$ ByteString
"\\setbox0=\\hbox{" forall a. Semigroup a => a -> a -> a
<> ByteString
str forall a. Semigroup a => a -> a -> a
<> ByteString
"}\n\\showbox0\n"
forall a. Parser a -> OnlineTex a
onlineTexParser forall n. Fractional n => Parser (Box n)
parseBox
showthe :: Fractional n => ByteString -> OnlineTex n
showthe :: forall n. Fractional n => ByteString -> OnlineTex n
showthe ByteString
str = do
OnlineTex ()
clearUnblocking
ByteString -> OnlineTex ()
texPutStrLn forall a b. (a -> b) -> a -> b
$ ByteString
"\\showthe" forall a. Semigroup a => a -> a -> a
<> ByteString
str
forall a. Parser a -> OnlineTex a
onlineTexParser forall n. Fractional n => Parser n
parseUnit
hsize :: Fractional n => OnlineTex n
hsize :: forall n. Fractional n => OnlineTex n
hsize = forall n. Box n -> n
boxWidth forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall n. Fractional n => ByteString -> OnlineTex (Box n)
hbox ByteString
"\\line{\\hfill}"
onlineTexParser :: A.Parser a -> OnlineTex a
onlineTexParser :: forall a. Parser a -> OnlineTex a
onlineTexParser Parser a
p = OnlineTex (InputStream ByteString)
getInStream forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall r. Parser r -> InputStream ByteString -> IO r
parseFromStream Parser a
p
texPutStrLn :: ByteString -> OnlineTex ()
texPutStrLn :: ByteString -> OnlineTex ()
texPutStrLn ByteString
a = OnlineTex (OutputStream ByteString)
getOutStream forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Maybe a -> OutputStream a -> IO ()
write (forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString -> ByteString
C8.append ByteString
a ByteString
"\n")
type TexStreams = (OutputStream ByteString, InputStream ByteString)
getOutStream :: OnlineTex (OutputStream ByteString)
getOutStream :: OnlineTex (OutputStream ByteString)
getOutStream = forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
reader forall a b. (a, b) -> a
fst
getInStream :: OnlineTex (InputStream ByteString)
getInStream :: OnlineTex (InputStream ByteString)
getInStream = forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
reader forall a b. (a, b) -> b
snd
clearUnblocking :: OnlineTex ()
clearUnblocking :: OnlineTex ()
clearUnblocking = OnlineTex (InputStream ByteString)
getInStream forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (f :: * -> *) a. Functor f => f a -> f ()
void forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. InputStream a -> IO (Maybe a)
Streams.read
mkTexHandles :: FilePath
-> Maybe [(String, String)]
-> String
-> [String]
-> ByteString
-> IO (OutputStream ByteString,
InputStream ByteString,
ProcessHandle)
mkTexHandles :: String
-> Maybe [(String, String)]
-> String
-> [String]
-> ByteString
-> IO
(OutputStream ByteString, InputStream ByteString, ProcessHandle)
mkTexHandles String
dir Maybe [(String, String)]
env String
command [String]
args ByteString
preamble = do
(OutputStream ByteString
outStream, InputStream ByteString
inStream, InputStream ByteString
_, ProcessHandle
h) <- String
-> [String]
-> Maybe String
-> Maybe [(String, String)]
-> IO
(OutputStream ByteString, InputStream ByteString,
InputStream ByteString, ProcessHandle)
runInteractiveProcess'
String
command
[String]
args
(forall a. a -> Maybe a
Just String
dir)
Maybe [(String, String)]
env
forall a. Maybe a -> OutputStream a -> IO ()
write (forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ ByteString
"\\tracingonline=1"
forall a. Semigroup a => a -> a -> a
<> ByteString
"\\showboxdepth=1"
forall a. Semigroup a => a -> a -> a
<> ByteString
"\\showboxbreadth=1"
forall a. Semigroup a => a -> a -> a
<> ByteString
"\\scrollmode\n"
) OutputStream ByteString
outStream
forall a. Maybe a -> OutputStream a -> IO ()
write (forall a. a -> Maybe a
Just ByteString
preamble) OutputStream ByteString
outStream
forall (m :: * -> *) a. Monad m => a -> m a
return (OutputStream ByteString
outStream, InputStream ByteString
inStream, ProcessHandle
h)
runInteractiveProcess'
:: FilePath
-> [String]
-> Maybe FilePath
-> Maybe [(String,String)]
-> IO (OutputStream ByteString,
InputStream ByteString,
InputStream ByteString,
ProcessHandle)
runInteractiveProcess' :: String
-> [String]
-> Maybe String
-> Maybe [(String, String)]
-> IO
(OutputStream ByteString, InputStream ByteString,
InputStream ByteString, ProcessHandle)
runInteractiveProcess' String
cmd [String]
args Maybe String
wd Maybe [(String, String)]
env = do
(Handle
hin, Handle
hout, Handle
herr, ProcessHandle
ph) <- String
-> [String]
-> Maybe String
-> Maybe [(String, String)]
-> IO (Handle, Handle, Handle, ProcessHandle)
P.runInteractiveProcess String
cmd [String]
args Maybe String
wd Maybe [(String, String)]
env
Handle -> BufferMode -> IO ()
hSetBuffering Handle
hin BufferMode
LineBuffering
OutputStream ByteString
sIn <- Handle -> IO (OutputStream ByteString)
Streams.handleToOutputStream Handle
hin forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
forall b a. IO b -> OutputStream a -> IO (OutputStream a)
Streams.atEndOfOutput (Handle -> IO ()
hClose Handle
hin) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
forall a. OutputStream a -> IO (OutputStream a)
Streams.lockingOutputStream
InputStream ByteString
sOut <- Handle -> IO (InputStream ByteString)
Streams.handleToInputStream Handle
hout forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
forall b a. IO b -> InputStream a -> IO (InputStream a)
Streams.atEndOfInput (Handle -> IO ()
hClose Handle
hout) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
forall a. InputStream a -> IO (InputStream a)
Streams.lockingInputStream
InputStream ByteString
sErr <- Handle -> IO (InputStream ByteString)
Streams.handleToInputStream Handle
herr forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
forall b a. IO b -> InputStream a -> IO (InputStream a)
Streams.atEndOfInput (Handle -> IO ()
hClose Handle
herr) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>=
forall a. InputStream a -> IO (InputStream a)
Streams.lockingInputStream
forall (m :: * -> *) a. Monad m => a -> m a
return (OutputStream ByteString
sIn, InputStream ByteString
sOut, InputStream ByteString
sErr, ProcessHandle
ph)