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 _ _ ->
193 error (errorMessage "unannotated definitions")
195 Source.TypeAnnotation name tipe ->
196 let value = Docs.Value name comment (Type.fromInternalType tipe) Nothing
198 info { values = Map.insert name value (values info) }
200 Decl.Datatype name args cases ->
201 let cases' = map (second (map Type.fromInternalType)) cases
202 union = Docs.Union name comment args cases'
204 info { unions = union : unions info }
206 Decl.TypeAlias name args tipe ->
207 let alias = Docs.Alias name comment args (Type.fromInternalType tipe)
209 info { aliases = alias : aliases info }
211 Decl.Fixity assoc prec name ->
212 let infixInfo = (Decl.assocToString assoc, prec)
214 info { infixes = Map.insert name infixInfo (infixes info) }
217 error (errorMessage "ports")
220errorMessage :: String -> String
222 "Unable to deal with annotations on " ++ name ++ " at this time.\n" ++
223 "Please report your use case to <https://github.com/elm-lang/elm-compiler/issues>"
226-- FILTER OUT UNEXPORTED VALUES, ALIASES, and UNIONS
228filterValues :: Var.Listing Var.Value -> [Docs.Value] -> [Docs.Value]
229filterValues (Var.Listing exports open) values =
234 Set.fromList (Var.getValues exports)
236 filter (\value -> Set.member (Docs.valueName value) names) values
239filterAliases :: Var.Listing Var.Value -> [Docs.Alias] -> [Docs.Alias]
240filterAliases (Var.Listing exports open) aliases =
245 Set.fromList (Var.getAliases exports)
247 filter (\value -> Set.member (Docs.aliasName value) names) aliases
250filterUnions :: Var.Listing Var.Value -> [Docs.Union] -> [Docs.Union]
251filterUnions (Var.Listing exports open) unions =
255 Maybe.mapMaybe (filterUnion exportedUnions) unions
258 Map.fromList (Var.getUnions exports)
261filterUnion :: Map.Map String (Var.Listing String) -> Docs.Union -> Maybe Docs.Union
262filterUnion exportedUnions union =
263 case Map.lookup (Docs.unionName union) exportedUnions of
267 Just (Var.Listing tags unionOpen)
272 filter (\(tag, _) -> tag `elem` tags) (Docs.unionCases union)
274 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