2{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
5import System.Console.CmdArgs
11import Control.Applicative ((<$>))
12import Control.Arrow (second)
14import Data.Aeson.Encode.Pretty
15import Data.Aeson.Types (Pair)
16import qualified Data.List as List
17import qualified Data.Map as Map
18import qualified Data.ByteString.Lazy as BS
19import qualified Data.Text as Text
21import qualified AST.Helpers as Help
22import qualified AST.Type as T
23import qualified AST.Declaration as D
24import qualified AST.Expression.Source as Src
25import qualified AST.Variable as Var
27import Text.Parsec hiding (newline,spaces)
28import Parse.Declaration (typeDecl,infixDecl)
29import Parse.Expression (typeAnnotation)
31import qualified Parse.Module as Module
34 { files :: [FilePath] }
35 deriving (Data,Typeable,Show,Eq)
39 { files = def &= args &= typ "FILES"
40 } &= help "Generate documentation for Elm"
41 &= summary ("Generate documentation for Elm, (c) Evan Czaplicki")
45 flags <- cmdArgs defaultFlags
46 mapM_ parseFile (files flags)
49config = Config { confIndent = 2, confCompare = keyOrder keys }
51 keys = ["tag","name","document","comment","raw","aliases","datatypes"
52 ,"values","typeVariables","type","constructors"]
54parseFile :: FilePath -> IO ()
56 source <- readFile path
57 case iParse docs source of
59 putStrLn $ "Documenting " ++ path
60 let docPath = "docs" </> replaceExtension path ".json"
61 createDirectoryIfMissing True (dropFileName docPath)
62 BS.writeFile docPath (encodePretty' config json)
64 hPutStrLn stderr $ "Parse error in " ++ path ++ " at " ++ show err
69 (name, _exports, structure) <- moduleDocs
71 return $ documentToJson name structure things
73docComment :: IParser String
76 contents <- closeComment
77 let reversed = dropWhile (`elem` " \n\r") . drop 2 $ reverse contents
78 return $ dropWhile (==' ') (reverse reversed)
80moduleDocs :: IParser (String, Var.Listing Var.Value, String)
83 (names,exports) <- Module.header
84 manyTill (string " " <|> newline <?> "more whitespace")
85 (lookAhead (string "{-|") <?> "module documentation comment")
86 structure <- docComment
87 return (List.intercalate "." names, exports, structure)
89document :: IParser [(String, D.SourceDecl, String)]
90document = onFreshLines (\t ts -> ts ++ [t]) [] docThing
92docThing :: IParser (String, D.SourceDecl, String)
93docThing = uncommentable <|> commented <|> uncommented ""
100 comment <- docComment
104 uncommented comment = do
105 (src,def) <- withSource $ choice [ typeDecl, D.Definition <$> typeAnnotation ]
106 return (comment, def, src)
109documentToJson :: String -> String -> [(String, D.SourceDecl, String)] -> Value
110documentToJson name structure things =
113 , "document" .= structure
114 , "values" .= toList values
115 , "aliases" .= toList aliases
116 , "datatypes" .= toList adts
119 (values, aliases, adts) =
120 collect Map.empty Map.empty Map.empty Map.empty things
122 toList :: Map.Map String [Pair] -> [Value]
124 map object (Map.elems dict)
126type Pairs = Map.Map String [Pair]
128collect :: Map.Map String (D.Assoc, Int) -> Pairs -> Pairs -> Pairs
129 -> [(String, D.SourceDecl, String)]
130 -> (Pairs, Pairs, Pairs)
131collect infixes types aliases adts things =
133 [] -> (Map.union customOps nonCustomOps, aliases, adts)
135 nonCustomOps = Map.mapWithKey addDefaultInfix $ Map.difference types infixes
136 addDefaultInfix name pairs
137 | all Help.isSymbol name = addInfix (D.L, 9 :: Int) pairs
140 customOps = Map.intersectionWith addInfix infixes types
141 addInfix (assoc,prec) pairs =
142 [ "associativity" .= show assoc, "precedence" .= prec ] ++ pairs
144 (comment, decl, source) : rest ->
145 let insert name fields dict = Map.insert name (obj name fields) dict
149 , "comment" .= comment
155 Src.TypeAnnotation name tipe ->
156 collect infixes (insert name [ "type" .= tipe ] types) aliases adts rest
158 Src.Definition _ _ ->
159 collect infixes types aliases adts rest
161 D.Datatype name vars ctors ->
162 let tipe = T.App (T.Type (Var.Raw name)) (map T.Var vars)
163 fields = [ "typeVariables" .= vars
164 , "constructors" .= map (ctorToJson tipe) ctors
166 in collect infixes types aliases (insert name fields adts) rest
168 D.TypeAlias name vars tipe ->
169 let fields = [ "typeVariables" .= vars, "type" .= tipe ]
170 in collect infixes types (insert name fields aliases) adts rest
173 collect infixes types aliases adts rest
175 D.Fixity assoc prec name ->
176 collect (Map.insert name (assoc,prec) infixes) types aliases adts rest
179instance Var.ToString var => ToJSON (T.Type var) where
181 object (getFields tipe)
186 let tipes = T.collectLambdas tipe in
187 [ "tag" .= ("function" :: Text.Text)
188 , "args" .= toJSON (init tipes)
189 , "result" .= toJSON (last tipes)
193 [ "tag" .= ("var" :: Text.Text)
198 [ "tag" .= ("adt" :: Text.Text)
199 , "name" .= toJSON (Var.toString name)
200 , "args" .= ([] :: [()])
203 T.App (T.Type name) ts ->
204 [ "tag" .= ("adt" :: Text.Text)
205 , "name" .= toJSON (Var.toString name)
206 , "args" .= map toJSON ts
209 T.App _ _ -> error "This is an unexpected error with elm-doc, please report it at <https://github.com/elm-lang/Elm/issues>"
211 T.Record fields ext ->
212 [ "tag" .= ("record" :: Text.Text)
213 , "fields" .= toJSON (map (toJSON . second toJSON) fields)
214 , "extension" .= toJSON ext
220ctorToJson :: T.RawType -> (String, [T.RawType]) -> Value
221ctorToJson tipe (ctor, tipes) =
222 object [ "name" .= ctor
223 , "type" .= foldr T.Lambda tipe tipes
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