forked from purescript/purescript
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMain.hs
More file actions
249 lines (219 loc) · 8.32 KB
/
Main.hs
File metadata and controls
249 lines (219 loc) · 8.32 KB
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
{-# LANGUAGE TupleSections #-}
----------------------------------------------------------------------------
--
-- Module : Main
-- Copyright : (c) Phil Freeman 2013
-- License : MIT
--
-- Maintainer : Phil Freeman <paf31@cantab.net>
-- Stability : experimental
-- Portability :
--
-- |
--
-----------------------------------------------------------------------------
module Main where
import Control.Applicative
import Control.Arrow (first, second)
import Control.Category ((>>>))
import Control.Monad.Writer
import Data.Function (on)
import Data.List
import Data.Maybe (fromMaybe)
import Data.Tuple (swap)
import Data.Version (showVersion)
import Options.Applicative
import qualified Text.PrettyPrint.ANSI.Leijen as PP
import qualified Language.PureScript as P
import qualified Paths_purescript as Paths
import System.Exit (exitFailure)
import System.IO (hPutStrLn, stderr)
import System.Directory (createDirectoryIfMissing)
import System.FilePath (takeDirectory)
import Etags
import Ctags
import qualified Language.PureScript.Docs as D
import qualified Language.PureScript.Docs.AsMarkdown as D
-- Available output formats
data Format = Markdown -- Output documentation in Markdown format
| Ctags -- Output ctags symbol index suitable for use with vi
| Etags -- Output etags symbol index suitable for use with emacs
deriving (Show, Eq, Ord)
-- | Available methods of outputting Markdown documentation
data DocgenOutput
= EverythingToStdOut
| ToStdOut [P.ModuleName]
| ToFiles [(P.ModuleName, FilePath)]
deriving (Show)
data PSCDocsOptions = PSCDocsOptions
{ pscdFormat :: Format
, pscdInputFiles :: [FilePath]
, pscdDocgen :: DocgenOutput
}
deriving (Show)
docgen :: PSCDocsOptions -> IO ()
docgen (PSCDocsOptions fmt input output) =
case fmt of
Etags -> dumpTags input dumpEtags
Ctags -> dumpTags input dumpCtags
Markdown -> do
e <- D.parseAndDesugar input [] (\_ ms -> return ms)
case e of
Left (D.ParseError err) -> do
hPutStrLn stderr $ show err
exitFailure
Left (D.SortModulesError err) -> do
hPutStrLn stderr $ P.prettyPrintMultipleErrors False err
exitFailure
Left (D.DesugarError err) -> do
hPutStrLn stderr $ P.prettyPrintMultipleErrors False err
exitFailure
Right ms' ->
case output of
EverythingToStdOut ->
putStrLn (D.renderModulesAsMarkdown ms')
ToStdOut names -> do
let (ms, missing) = takeModulesByName ms' names
guardMissing missing
putStrLn (D.renderModulesAsMarkdown ms)
ToFiles names -> do
let (ms, missing) = takeModulesByName' ms' names
guardMissing missing
let ms'' = groupBy ((==) `on` fst) . sortBy (compare `on` fst) $ map swap ms
forM_ ms'' $ \grp -> do
let fp = fst (head grp)
createDirectoryIfMissing True (takeDirectory fp)
writeFile fp (D.renderModulesAsMarkdown $ snd `map` grp)
where
guardMissing [] = return ()
guardMissing [mn] = do
hPutStrLn stderr ("psc-docs: error: unknown module \"" ++ show mn ++ "\"")
exitFailure
guardMissing mns = do
hPutStrLn stderr "psc-docs: error: unknown modules:"
forM_ mns $ \mn ->
hPutStrLn stderr (" * " ++ show mn)
exitFailure
-- |
-- Given a list of module names and a list of modules, return a list of modules
-- whose names appeared in the given name list, together with a list of names
-- for which no module could be found in the module list.
--
takeModulesByName :: [P.Module] -> [P.ModuleName] -> ([P.Module], [P.ModuleName])
takeModulesByName modules names =
first (map fst) (takeModulesByName' modules (map (,()) names))
-- |
-- Like takeModulesByName but also keeps some extra data with the module.
--
takeModulesByName' :: [P.Module] -> [(P.ModuleName, a)] -> ([(P.Module, a)], [P.ModuleName])
takeModulesByName' modules = foldl go ([], [])
where
go (ms, missing) (name, x) =
case find ((== name) . P.getModuleName) modules of
Just m -> ((m, x) : ms, missing)
Nothing -> (ms, name : missing)
dumpTags :: [FilePath] -> ([(String, P.Module)] -> [String]) -> IO ()
dumpTags input renderTags = do
e <- P.parseModulesFromFiles (fromMaybe "") <$> mapM (fmap (first Just) . parseFile) (nub input)
case e of
Left err -> do
hPutStrLn stderr (show err)
exitFailure
Right ms ->
ldump (renderTags (pairs ms))
where
pairs :: [(Maybe String, m)] -> [(String, m)]
pairs = map (first (fromMaybe ""))
ldump :: [String] -> IO ()
ldump = mapM_ putStrLn
parseFile :: FilePath -> IO (FilePath, String)
parseFile input = (,) input <$> readFile input
inputFile :: Parser FilePath
inputFile = strArgument $
metavar "FILE"
<> help "The input .purs file(s)"
instance Read Format where
readsPrec _ "etags" = [(Etags, "")]
readsPrec _ "ctags" = [(Ctags, "")]
readsPrec _ "markdown" = [(Markdown, "")]
readsPrec _ _ = []
format :: Parser Format
format = option auto $ value Markdown
<> long "format"
<> metavar "FORMAT"
<> help "Set output FORMAT (markdown | etags | ctags)"
docgenModule :: Parser String
docgenModule = strOption $
long "docgen"
<> help "A list of module names which should appear in the output. This can optionally include file paths to write individual modules to, by separating with a colon ':'. For example, Prelude:docs/Prelude.md. This option may be specified multiple times."
pscDocsOptions :: Parser (Format, [FilePath], [String])
pscDocsOptions = (,,) <$> format <*> many inputFile <*> many docgenModule
parseDocgen :: [String] -> Either String DocgenOutput
parseDocgen [] = Right EverythingToStdOut
parseDocgen xs = go xs
where
go = intersperse " "
>>> concat
>>> words
>>> map parseItem
>>> combine
data DocgenOutputItem
= IToStdOut P.ModuleName
| IToFile (P.ModuleName, FilePath)
parseItem :: String -> DocgenOutputItem
parseItem s = case elemIndex ':' s of
Just i ->
s # splitAt i
>>> first P.moduleNameFromString
>>> second (drop 1)
>>> IToFile
Nothing ->
IToStdOut (P.moduleNameFromString s)
where
infixr 1 #
(#) = flip ($)
combine :: [DocgenOutputItem] -> Either String DocgenOutput
combine [] = Right EverythingToStdOut
combine (x:xs) = foldM go (initial x) xs
where
initial (IToStdOut m) = ToStdOut [m]
initial (IToFile m) = ToFiles [m]
go (ToStdOut ms) (IToStdOut m) = Right (ToStdOut (m:ms))
go (ToFiles ms) (IToFile m) = Right (ToFiles (m:ms))
go _ _ = Left "Can't mix module names and module name/file path pairs in the same invocation."
buildOptions :: (Format, [FilePath], [String]) -> IO PSCDocsOptions
buildOptions (fmt, input, mapping) =
case parseDocgen mapping of
Right mapping' -> return (PSCDocsOptions fmt input mapping')
Left err -> do
hPutStrLn stderr "psc-docs: error in --docgen option:"
hPutStrLn stderr (" " ++ err)
exitFailure
main :: IO ()
main = execParser opts >>= buildOptions >>= docgen
where
opts = info (version <*> helper <*> pscDocsOptions) infoModList
infoModList = fullDesc <> headerInfo <> footerInfo
headerInfo = header "psc-docs - Generate Markdown documentation from PureScript source files"
footerInfo = footerDoc $ Just $ PP.vcat
[ examples, PP.empty, PP.text ("psc-docs " ++ showVersion Paths.version) ]
version :: Parser (a -> a)
version = abortOption (InfoMsg (showVersion Paths.version)) $ long "version" <> help "Show the version number" <> hidden
examples :: PP.Doc
examples =
PP.vcat $ map PP.text
[ "Examples:"
, " print documentation for Data.List to stdout:"
, " psc-docs src/**/*.purs bower_components/*/src/**/*.purs \\"
, " --docgen Data.List"
, ""
, " write documentation for Data.List to docs/Data.List.md:"
, " psc-docs src/**/*.purs bower_components/*/src/**/*.purs \\"
, " --docgen Data.List:docs/Data.List.md"
, ""
, " write documentation for Data.List to docs/Data.List.md, and"
, " documentation for Data.List.Lazy to docs/Data.List.Lazy.md:"
, " psc-docs src/**/*.purs bower_components/*/src/**/*.purs \\"
, " --docgen Data.List:docs/Data.List.md \\"
, " --docgen Data.List.Lazy:docs/Data.List.Lazy.md"
]