1{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
4import System.Console.CmdArgs
10import Control.Applicative ((<$>))
11import Control.Arrow (second)
12import Control.Monad (when)
13import qualified Data.Aeson.Encode.Pretty as Json
14import qualified Data.ByteString.Lazy.Char8 as BS
15import qualified Data.Map as Map
16import qualified Data.Maybe as Maybe
17import qualified Data.Set as Set
19import qualified AST.Declaration as Decl
20import qualified AST.Expression.Source as Source
21import qualified AST.Variable as Var
23import Text.Parsec hiding (newline, spaces)
24import qualified Parse.Declaration as Parse (typeDecl, infixDecl)
25import qualified Parse.Expression as Parse (typeAnnotation)
26import qualified Parse.Helpers as Parse
27import qualified Parse.Module as Parse (header)
28import qualified Elm.Compiler.Module as Module
29import qualified Elm.Compiler.Type.Extract as Type
30import qualified Elm.Docs as Docs
37 , output :: Maybe FilePath
39 deriving (Data,Typeable,Show,Eq)
44 { input = def &= args &= typ "FILE"
46 , output = Nothing &= typFile
47 &= help "file name for generated JSON documentation"
49 } &= help "Generate documentation for Elm"
50 &= summary "Generate documentation for Elm, (c) Evan Czaplicki"
53-- GENERATE DOCUMENTATION
57 do flags <- cmdArgs defaultFlags
58 let inputFileName = input flags
60 exists <- doesFileExist inputFileName
64 then "Error: you must provide a file to document!"
65 else "Error: could not find a file named '" ++ inputFileName ++ "'"
68 source <- readFile inputFileName
69 case Parse.iParse documentation source of
71 let json = Json.encodePretty' config docs in
73 Nothing -> BS.putStrLn json
75 do createDirectoryIfMissing True (dropFileName docPath)
76 BS.writeFile docPath json
79 do hPutStrLn stderr (show err)
87 , Json.confCompare = Json.keyOrder keys
91 [ "tag", "name", "comment", "aliases", "types"
92 , "values", "func", "args", "type", "cases"
98documentation :: Parse.IParser Docs.Documentation
100 do optional Parse.freshLine
101 (names, exports) <- Parse.header
103 manyTill (string " " <|> Parse.newline <?> "more whitespace")
104 (lookAhead (string "{-|") <?> "module documentation comment")
106 overview <- docComment
108 decls <- allDeclarations
110 let (aliases, unions, values) = categorizeDeclarations decls
116 (filterAliases exports aliases)
117 (filterUnions exports unions)
118 (filterValues exports values)
121docComment :: Parse.IParser String
123 do try (string "{-|")
124 contents <- Parse.closeComment
127 dropWhile (`elem` " \n\r") . drop 2 $ reverse contents
129 return $ dropWhile (==' ') (reverse reversed)
132allDeclarations :: Parse.IParser [(String, Decl.SourceDecl)]
134 Parse.onFreshLines (:) [] declaration
137declaration :: Parse.IParser (String, Decl.SourceDecl)
139 uncommentable <|> commented <|> uncommented ""
142 (,) "" <$> Parse.infixDecl
145 do comment <- docComment
149 uncommented comment =
151 <$> choice [ Parse.typeDecl, Decl.Definition <$> Parse.typeAnnotation ]
154-- CATEGORIZE DECLARATIONS
156data CategoryInfo = CategoryInfo
157 { aliases :: [Docs.Alias]
158 , unions :: [Docs.Union]
159 , values :: Map.Map String Docs.Value
160 , infixes :: Map.Map String (String, Int)
164emptyInfo :: CategoryInfo
166 CategoryInfo [] [] Map.empty Map.empty
169categorizeDeclarations :: [(String, Decl.SourceDecl)] -> ([Docs.Alias], [Docs.Union], [Docs.Value])
170categorizeDeclarations decls =
171 (aliases, unions, Map.elems values)
173 (CategoryInfo aliases unions rawValues infixes) =
174 foldr collectInfo emptyInfo decls
178 (Map.intersectionWith addInfixInfo rawValues infixes)
182addInfixInfo :: Docs.Value -> (String, Int) -> Docs.Value
183addInfixInfo value infixInfo =
184 value { Docs.valueAssocPrec = Just infixInfo }
187collectInfo :: (String, Decl.SourceDecl) -> CategoryInfo -> CategoryInfo
188collectInfo (comment, decl) info =
190 Decl.Definition def ->
192 Source.Definition _ _ -> error errorMessage
193 Source.TypeAnnotation name tipe ->
194 let value = Docs.Value name comment (Type.fromInternalType tipe) Nothing
196 info { values = Map.insert name value (values info) }
198 Decl.Datatype name args cases ->
199 let cases' = map (second (map Type.fromInternalType)) cases
200 union = Docs.Union name comment args cases'
202 info { unions = union : unions info }
204 Decl.TypeAlias name args tipe ->
205 let alias = Docs.Alias name comment args (Type.fromInternalType tipe)
207 info { aliases = alias : aliases info }
209 Decl.Fixity assoc prec name ->
210 let infixInfo = (Decl.assocToString assoc, prec)
212 info { infixes = Map.insert name infixInfo (infixes info) }
218errorMessage :: String
220 "there appears to be a bug in this tool.\n" ++
221 "Please report it to <https://github.com/elm-lang/Elm/issues>"
224-- FILTER OUT UNEXPORTED VALUES, ALIASES, and UNIONS
226filterValues :: Var.Listing Var.Value -> [Docs.Value] -> [Docs.Value]
227filterValues (Var.Listing exports open) values =
232 Set.fromList (Var.getValues exports)
234 filter (\value -> Set.member (Docs.valueName value) names) values
237filterAliases :: Var.Listing Var.Value -> [Docs.Alias] -> [Docs.Alias]
238filterAliases (Var.Listing exports open) aliases =
243 Set.fromList (Var.getAliases exports)
245 filter (\value -> Set.member (Docs.aliasName value) names) aliases
248filterUnions :: Var.Listing Var.Value -> [Docs.Union] -> [Docs.Union]
249filterUnions (Var.Listing exports open) unions =
253 Maybe.mapMaybe (filterUnion exportedUnions) unions
256 Map.fromList (Var.getUnions exports)
259filterUnion :: Map.Map String (Var.Listing String) -> Docs.Union -> Maybe Docs.Union
260filterUnion exportedUnions union =
261 case Map.lookup (Docs.unionName union) exportedUnions of
265 Just (Var.Listing tags unionOpen)
270 filter (\(tag, _) -> tag `elem` tags) (Docs.unionCases union)
272 Just (union { Docs.unionCases = publicTags })
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list