-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMain.hs
128 lines (112 loc) · 4.54 KB
/
Main.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
{-# LANGUAGE LambdaCase, NamedFieldPuns, OverloadedStrings, ScopedTypeVariables #-}
module Main where
import Prelude hiding (FilePath)
import Data.String (fromString)
import Data.Maybe (fromMaybe)
import Compile
import Control.Arrow
import Control.Monad
import Control.Monad.Except
import Control.Monad.Catch
import Control.Concurrent (threadDelay)
import qualified Data.Text.IO as T
import Options.Applicative hiding (Parser)
import Text.LaTeX.Base.Parser
import TranslateTex (translate)
import DecoratedTex (decorate)
import Filesystem as F
import Filesystem.Path.CurrentOS
import System.FSNotify
import Types
import Paths_proof (getDataFileName)
data AppData = AppData
{ inputPath :: FilePath
, outputDir :: Maybe FilePath
, watch :: Bool
}
opts :: ParserInfo AppData
opts = info (helper <*> optParser)
( fullDesc
<> progDesc "Compile FILEPATH to html"
<> header "proof - a markup language for structured mathematics")
where
optParser = AppData
<$> argument path (metavar "FILE" <> help "Path to the input proof file")
<*> optional (option path (
long "output" <>
short 'o' <>
metavar "OUTPUTDIR" <>
help "Path of output directory. Default is '.'"))
<*> switch (long "watch" <> short 'w' <> help "Recompile on file-change")
where
path :: ReadM FilePath
path = eitherReader (Right . fromString)
outputPath :: FilePath -> FilePath
outputPath p = replaceExtension p "html"
texReader :: (MonadIO m, Functor m) => FilePath -> Err m RawDocument
texReader = (ExceptT . liftIO . fmap (left show) . parseLaTeXFile . encodeString) >=> decorate >=> translate
-- `copyDirectory src dst` works as follows:
-- Let src = initialpath/dir. Then src gets moved to dst/dir assuming src
-- and dst are both directories
copyDirectory :: (MonadIO m, MonadCatch m) => FilePath -> FilePath -> Err m ()
copyDirectory = \src dst -> do
e <- liftIO ((&&) <$> isDirectory src <*> isDirectory dst)
if e then go src dst else throwError "Directories do not exist" -- TODO: Better error
where
leafName = last . splitDirectories
copyError p = throwError ("Error copying file \"" ++ show p ++ "\"")
go :: (MonadIO m, MonadCatch m) => FilePath -> FilePath -> Err m ()
go src dst = do
catch (liftIO $ createDirectory False dst')
(\(_::IOError) -> throwError ("Directory \"" ++ show dst' ++ "\" already exists."))
fs <- liftIO $ listDirectory src
forM_ fs $ \p ->
liftIO (isFile p) >>= \case
True -> liftIO (copyFile p (dst' </> filename p)) `catch` (\(_::IOError) -> copyError p)
False -> go p dst'
where dst' = dst </> leafName src
loadResources :: (MonadIO m, MonadCatch m, Applicative m) => Err m Resources
loadResources =
Resources <$> mapM readDataFile ["src/css/proof.css"]
<*> mapM readDataFile ["lib/js/jquery.min.js", "src/js/proof.js"]
where
readDataFile = (`catch` (\(_::IOError) ->throwError "Could not read data files"))
. liftIO . (T.readFile <=< getDataFileName)
pkgPath :: FilePath -> FilePath -> FilePath
pkgPath inputPath outputDir = outputDir </> addExtension (basename inputPath) "proofpkg"
compileAndOutput :: (MonadIO m, MonadCatch m, Applicative m) => FilePath -> FilePath -> Err m ()
compileAndOutput inputPath outputDir = do
let p = pkgPath inputPath outputDir
htmlPath = p </> "index.html"
html <- join (compile <$> loadResources <*> texReader inputPath)
-- TODO: Dangerous to remove directories. Remove when you merge
liftIO $ do
isDirectory p >>= flip when (removeTree p)
createDirectory False p
liftIO (getDataFilePath "lib") >>= \l -> copyDirectory l p
liftIO $ do
forM_ ["src/js/proof.js", "src/css/proof.css"] $ \q ->
getDataFilePath q >>= \q' -> copyFile q' (p </> filename q)
T.writeFile (encodeString htmlPath) html
where
getDataFilePath = fmap decodeString . getDataFileName . encodeString
main :: IO ()
main = do
AppData { inputPath, outputDir, watch } <- execParser opts
run inputPath outputDir
when watch (setupWatch inputPath outputDir)
where
setupWatch inputPath outputDir =
withManager $ \wm -> void $ do
watchDir wm (directory inputPath) fEvent $ \_ -> do
putStr "File changed. Recompiling..."
run inputPath outputDir
putStrLn "Done."
forever $ threadDelay maxBound
where
fEvent = \case { Modified p _ -> p == inputPath; _ -> False }
run inputPath outputDir =
runExceptT (compileAndOutput inputPath out) >>= \case
Left e -> putStrLn e
Right _ -> return ()
where out = fromMaybe (directory inputPath) outputDir