--
-- Haddock - A Haskell Documentation Tool
--
-- (c) Simon Marlow 2002
--
module HaddockHtml (ppHtml) where
import Prelude hiding (div)
import HaddockVersion
import HaddockTypes
import HaddockUtil
import HsSyn
import IO
import Maybe ( fromJust, isNothing, isJust )
import FiniteMap
import List ( sortBy )
import Char ( toUpper, toLower )
import Monad ( when )
import IOExts
import Html
import qualified Html
-- -----------------------------------------------------------------------------
-- Files we need to copy from our $libdir
cssFile = "haddock.css"
iconFile = "haskell_icon.gif"
-- -----------------------------------------------------------------------------
-- Generating HTML documentation
type InstMaps =
(FiniteMap HsQName [InstHead], -- maps class names to instances
FiniteMap HsQName [InstHead]) -- maps type names to instances
ppHtml :: String
-> Maybe String
-> [(Module, Interface)]
-> FilePath -- destination directory
-> Maybe String -- CSS file
-> String -- $libdir
-> InstMaps
-> IO ()
ppHtml title source_url ifaces odir maybe_css libdir inst_maps = do
let
css_file = case maybe_css of
Nothing -> libdir ++ pathSeparator:cssFile
Just f -> f
css_destination = odir ++ pathSeparator:cssFile
icon_file = libdir ++ pathSeparator:iconFile
icon_destination = odir ++ pathSeparator:iconFile
visible_ifaces = filter visible ifaces
visible (m,i) = OptHide `notElem` iface_options i
css_contents <- readFile css_file
writeFile css_destination css_contents
icon_contents <- readFile icon_file
writeFile icon_destination icon_contents
ppHtmlContents odir title source_url (map fst visible_ifaces)
ppHtmlIndex odir title visible_ifaces
mapM_ (ppHtmlModule odir title source_url inst_maps) visible_ifaces
moduleHtmlFile :: String -> FilePath
moduleHtmlFile mod = mod ++ ".html" -- ToDo: Z-encode filename?
contentsHtmlFile = "index.html"
indexHtmlFile = "doc-index.html"
subIndexHtmlFile k a = "doc-index-" ++ k:a:".html"
footer =
tda [theclass "botbar"] <<
( toHtml "Produced by" <+>
(anchor ! [href projectUrl] << toHtml projectName) <+>
toHtml ("version " ++ projectVersion)
)
src_button source_url mod file
| Just u <- source_url =
let src_url = if (last u == '/') then u ++ file else u ++ '/':file
in
topButBox (anchor ! [href src_url] << toHtml "Source code")
| otherwise =
Html.emptyTable
parent_button mod =
case span (/= '.') (reverse mod) of
(m, '.':rest) ->
topButBox (
anchor ! [href (moduleHtmlFile (reverse rest))] << toHtml "Parent")
_ ->
Html.emptyTable
contentsButton = topButBox (anchor ! [href contentsHtmlFile] <<
toHtml "Contents")
indexButton = topButBox (anchor ! [href indexHtmlFile] << toHtml "Index")
simpleHeader title =
(tda [theclass "topbar"] <<
vanillaTable << (
(td <<
image ! [src "haskell_icon.gif", width "16", height 16, alt " " ]
) <->
(tda [theclass "title"] << toHtml title) <->
contentsButton <-> indexButton
))
pageHeader mod iface title source_url =
(tda [theclass "topbar"] <<
vanillaTable << (
(td <<
image ! [src "haskell_icon.gif", width "16", height 16, alt " "]
) <->
(tda [theclass "title"] << toHtml title) <->
src_button source_url mod (iface_filename iface) <->
parent_button mod <->
contentsButton <->
indexButton
)
) </>
tda [theclass "modulebar"] <<
(vanillaTable << (
(td << font ! [size "6"] << toHtml mod) <->
moduleInfo iface
)
)
moduleInfo iface
| Nothing <- iface_info iface = Html.emptyTable
| Just info <- iface_info iface =
tda [align "right"] << narrowTable << (
(tda [theclass "infohead"] << toHtml "Portability") <->
(tda [theclass "infoval"] << toHtml (portability info)) </>
(tda [theclass "infohead"] << toHtml "Stability") <->
(tda [theclass "infoval"] << toHtml (stability info)) </>
(tda [theclass "infohead"] << toHtml "Maintainer") <->
(tda [theclass "infoval"] << toHtml (maintainer info))
)
-- ---------------------------------------------------------------------------
-- Generate the module contents
ppHtmlContents :: FilePath -> String -> Maybe String -> [Module]
-> IO ()
ppHtmlContents odir title source_url mods = do
let tree = mkModuleTree mods
html =
header (thetitle (toHtml title) +++
thelink ! [href cssFile,
rel "stylesheet", thetype "text/css"]) +++
body << vanillaTable << (
simpleHeader title </>
ppModuleTree title tree </>
footer
)
writeFile (odir ++ pathSeparator:contentsHtmlFile) (renderHtml html)
ppModuleTree :: String -> [ModuleTree] -> HtmlTable
ppModuleTree title ts =
tda [theclass "section1"] << toHtml "Modules" </>
td << table ! [cellpadding 0, cellspacing 2] << aboves (map (mkNode []) ts)
mkNode :: [String] -> ModuleTree -> HtmlTable
mkNode ss (Node s leaf []) =
td << mkLeaf s ss leaf
mkNode ss (Node s leaf ts) =
(td << mkLeaf s ss leaf)
</>
(tda [theclass "children"] <<
vanillaTable (toHtml (aboves (map (mkNode (s:ss)) ts))))
mkLeaf s ss False = toHtml s
mkLeaf s ss True = anchor ! [href (moduleHtmlFile mod)] << toHtml s
where mod = foldr (++) "" (s' : map ('.':) ss')
(s':ss') = reverse (s:ss)
-- reconstruct the module name
data ModuleTree = Node String Bool [ModuleTree]
mkModuleTree :: [Module] -> [ModuleTree]
mkModuleTree mods = foldr addToTrees [] (map splitModule mods)
addToTrees :: [String] -> [ModuleTree] -> [ModuleTree]
addToTrees [] ts = ts
addToTrees ss [] = mkSubTree ss
addToTrees (s1:ss) (t@(Node s2 leaf subs) : ts)
| s1 > s2 = t : addToTrees (s1:ss) ts
| s1 == s2 = Node s2 (leaf || null ss) (addToTrees ss subs) : ts
| otherwise = mkSubTree (s1:ss) ++ t : ts
mkSubTree [] = []
mkSubTree (s:ss) = [Node s (null ss) (mkSubTree ss)]
splitModule :: Module -> [String]
splitModule (Module mod) = split mod
where split mod = case break (== '.') mod of
(s1, '.':s2) -> s1 : split s2
(s1, _) -> [s1]
-- ---------------------------------------------------------------------------
-- Generate the index
ppHtmlIndex :: FilePath -> String -> [(Module,Interface)] -> IO ()
ppHtmlIndex odir title ifaces = do
let html =
header (thetitle (toHtml (title ++ " (Index)")) +++
thelink ! [href cssFile,
rel "stylesheet", thetype "text/css"]) +++
body << vanillaTable << (
simpleHeader title </>
tda [theclass "section1"] << toHtml "Type/Class Index" </>
index_html tycls_index 't' </>
tda [theclass "section1"] << toHtml "Function/Constructor Index" </>
index_html var_index 'v'
)
when split_indices
(do mapM_ (do_sub_index "Type/Class" tycls_index 't') ['A'..'Z']
mapM_ (do_sub_index "Function/Constructor" var_index 'v') ['A'..'Z']
)
writeFile (odir ++ pathSeparator:indexHtmlFile) (renderHtml html)
where
split_indices = length tycls_index > 50 || length var_index > 50
index_html this_ix kind
| split_indices =
td << table ! [cellpadding 0, cellspacing 5] <<
besides [ td << anchor ! [href (subIndexHtmlFile kind c)] <<
toHtml [c]
| c <- ['A'..'Z'] ]
| otherwise =
td << table ! [cellpadding 0, cellspacing 5] <<
aboves (map indexElt this_ix)
do_sub_index descr this_ix kind c
= writeFile (odir ++ pathSeparator:subIndexHtmlFile kind c)
(renderHtml html)
where
html = header (thetitle (toHtml (title ++ " (" ++ descr ++ "Index)")) +++
thelink ! [href cssFile,
rel "stylesheet", thetype "text/css"]) +++
body << vanillaTable << (
simpleHeader title </>
tda [theclass "section1"] <<
toHtml (descr ++ " Index (" ++ c:")") </>
td << table ! [cellpadding 0, cellspacing 5] <<
aboves (map indexElt index_part)
)
index_part = [(n,stuff) | (n,stuff) <- this_ix, n `nameBeginsWith` c]
tycls_index = index isTyClsName
var_index = index (not.isTyClsName)
isTyClsName (HsTyClsName _) = True
isTyClsName _ = False
index :: (HsName -> Bool) -> [(HsName, [(Module,Bool)])]
index f = sortBy cmp (fmToList (full_index f))
where cmp (n1,_) (n2,_) = n1 `compare` n2
iface_indices f = map (getIfaceIndex f) ifaces
full_index f = foldr1 (plusFM_C (++)) (iface_indices f)
getIfaceIndex f (mod,iface) = listToFM
[ (name, [(mod, mod == mod')])
| (name, Qual mod' _) <- fmToList (iface_env iface),
f name ]
indexElt :: (HsName, [(Module,Bool)]) -> HtmlTable
indexElt (nm, entries) =
td << ppHsName nm
<-> td << (hsep [ if defining then
bold << anchor ! [href (linkId mod nm)] << toHtml mod
else
anchor ! [href (linkId mod nm)] << toHtml mod
| (Module mod, defining) <- entries ])
nameBeginsWith (HsTyClsName id) c = idBeginsWith id c
nameBeginsWith (HsVarName id) c = idBeginsWith id c
idBeginsWith (HsIdent s) c = head s `elem` [toLower c, toUpper c]
idBeginsWith (HsSymbol s) c = head s `elem` [toLower c, toUpper c]
idBeginsWith (HsSpecial s) c = head s `elem` [toLower c, toUpper c]
-- ---------------------------------------------------------------------------
-- Generate the HTML page for a module
ppHtmlModule :: FilePath -> String -> Maybe String -> InstMaps
-> (Module,Interface) -> IO ()
ppHtmlModule odir title source_url inst_maps (Module mod,iface) = do
let html =
header (thetitle (toHtml mod) +++
thelink ! [href cssFile,
rel "stylesheet", thetype "text/css"]) +++
body << vanillaTable << (
pageHeader mod iface title source_url </> s15 </>
ifaceToHtml mod iface inst_maps </> s15 </>
footer
)
writeFile (odir ++ pathSeparator:moduleHtmlFile mod) (renderHtml html)
ifaceToHtml :: String -> Interface -> InstMaps -> HtmlTable
ifaceToHtml mod iface inst_maps
= abovesSep s15 (contents: description: synopsis: maybe_doc_hdr: body)
where
exports = numberSectionHeadings (iface_exports iface)
has_doc (ExportDecl _ d) = isJust (declDoc d)
has_doc (ExportModule _) = False
has_doc _ = True
no_doc_at_all = not (any has_doc exports)
contents = td << ppModuleContents exports
description
| Just doc <- iface_doc iface
= (tda [theclass "section1"] << toHtml "Description") </>
docBox (docToHtml doc)
| otherwise
= Html.emptyTable
-- omit the synopsis if there are no documentation annotations at all
synopsis
| no_doc_at_all = Html.emptyTable
| otherwise
= (tda [theclass "section1"] << toHtml "Synopsis") </>
s15 </>
(tda [theclass "body"] << vanillaTable <<
abovesSep s8 (map (processExport True inst_maps)
(filter forSummary exports))
)
-- if the documentation doesn't begin with a section header, then
-- add one ("Documentation").
maybe_doc_hdr
| not (no_doc_at_all) =
case exports of
[] -> Html.emptyTable
ExportGroup _ _ _ : _ -> Html.emptyTable
_ -> tda [ theclass "section1" ] << toHtml "Documentation"
| otherwise = Html.emptyTable
body = map (processExport False inst_maps) exports
ppModuleContents :: [ExportItem] -> HtmlTable
ppModuleContents exports
| length sections < 2 = Html.emptyTable
| otherwise = tda [theclass "section4"] << bold << toHtml "Contents"
</> td << dlist << concatHtml sections
where
(sections, _leftovers{-should be []-}) = process 0 exports
process :: Int -> [ExportItem] -> ([Html],[ExportItem])
process n [] = ([], [])
process n items@(ExportGroup lev id doc : rest)
| lev <= n = ( [], items )
| otherwise = ( html:sections, rest2 )
where
html = (dterm << anchor ! [href ('#':id)] << docToHtml doc)
+++ mk_subsections subsections
(subsections, rest1) = process lev rest
(sections, rest2) = process n rest1
process n (_ : rest) = process n rest
mk_subsections [] = noHtml
mk_subsections ss = ddef << dlist << concatHtml ss
-- we need to assign a unique id to each section heading so we can hyperlink
-- them from the contents:
numberSectionHeadings :: [ExportItem] -> [ExportItem]
numberSectionHeadings exports = go 1 exports
where go n [] = []
go n (ExportGroup lev _ doc : es)
= ExportGroup lev (show n) doc : go (n+1) es
go n (other:es)
= other : go n es
processExport :: Bool -> InstMaps -> ExportItem -> HtmlTable
processExport summary inst_maps (ExportGroup lev id doc)
= ppDocGroup lev (anchor ! [name id] << docToHtml doc)
processExport summary inst_maps (ExportDecl x decl)
= doDecl summary inst_maps x decl
processExport summary inst_maps (ExportDoc doc)
= docBox (docToHtml doc)
processExport summary inst_maps (ExportModule (Module mod))
= declBox (toHtml "module" <+> ppHsModule mod)
forSummary (ExportGroup _ _ _) = False
forSummary (ExportDoc _) = False
forSummary _ = True
ppDocGroup lev doc
| lev == 1 = tda [ theclass "section1" ] << doc
| lev == 2 = tda [ theclass "section2" ] << doc
| lev == 3 = tda [ theclass "section3" ] << doc
| otherwise = tda [ theclass "section4" ] << doc
-- -----------------------------------------------------------------------------
-- Converting declarations to HTML
declWithDoc :: Bool -> Maybe Doc -> Html -> HtmlTable
declWithDoc True doc html_decl = declBox html_decl
declWithDoc False Nothing html_decl = declBox html_decl
declWithDoc False (Just doc) html_decl =
declBox html_decl </> docBox (docToHtml doc)
doDecl :: Bool -> InstMaps -> HsQName -> HsDecl -> HtmlTable
doDecl summary inst_maps x decl = do_decl decl
where
do_decl (HsTypeSig _ [nm] ty doc)
= ppFunSig summary nm ty doc
do_decl (HsForeignImport _ _ _ _ n ty doc)
= ppFunSig summary n ty doc
do_decl (HsTypeDecl _ nm args ty doc)
= declWithDoc summary doc (
hsep ([keyword "type", ppHsBinder summary nm]
++ map ppHsName args) <+> equals <+> ppHsType ty)
do_decl (HsNewTypeDecl loc ctx nm args con drv doc)
= ppHsDataDecl summary inst_maps True{-is newtype-} x
(HsDataDecl loc ctx nm args [con] drv doc)
-- print it as a single-constructor datatype
do_decl decl@(HsDataDecl loc ctx nm args cons drv doc)
= ppHsDataDecl summary inst_maps False{-not newtype-} x decl
do_decl decl@(HsClassDecl{})
= ppHsClassDecl summary inst_maps x decl
do_decl (HsDocGroup loc lev str)
= if summary then Html.emptyTable
else ppDocGroup lev (docToHtml str)
do_decl _ = error ("do_decl: " ++ show decl)
ppTypeSig summary nm ty = ppHsBinder summary nm <+> toHtml "::" <+> ppHsType ty
keepDecl HsTypeSig{} = True
keepDecl HsTypeDecl{} = True
keepDecl HsNewTypeDecl{} = True
keepDecl HsDataDecl{} = True
keepDecl HsClassDecl{} = True
keepDecl _ = False
-- -----------------------------------------------------------------------------
-- Data & newtype declarations
ppShortDataDecl :: Bool -> Bool -> HsDecl -> Html
ppShortDataDecl summary is_newty
(HsDataDecl loc ctx nm args [con] drv _doc) =
ppHsDataHeader summary is_newty nm args
<+> equals <+> ppShortConstr summary con
ppShortDataDecl summary is_newty
(HsDataDecl loc ctx nm args cons drv _doc) =
vanillaTable << (
declBox (ppHsDataHeader summary is_newty nm args) </>
tda [theclass "body"] << vanillaTable << (
aboves (zipWith do_constr ('=':repeat '|') cons)
)
)
where do_constr c con = declBox (toHtml [c] <+> ppShortConstr summary con)
-- The rest of the cases:
ppHsDataDecl summary (_, ty_inst_map) is_newty
x decl@(HsDataDecl loc ctx nm args cons drv doc)
| summary = declWithDoc summary doc (ppShortDataDecl summary is_newty decl)
| otherwise
= header </>
tda [theclass "body"] << vanillaTable << (
datadoc </>
constr_bit </>
instances_bit
)
where
header = declBox (ppHsDataHeader False is_newty nm args)
constr_table
| any isRecDecl cons = spacedTable5
| otherwise = spacedTable1
datadoc | isJust doc = ndocBox (docToHtml (fromJust doc))
| otherwise = Html.emptyTable
constr_bit
| null cons = Html.emptyTable
| otherwise =
constr_hdr </>
(tda [theclass "body"] << constr_table <<
aboves (map ppSideBySideConstr cons)
)
no_constr_docs = all constr_has_no_doc cons
instances = lookupFM ty_inst_map x
instances_bit
= case instances of
Nothing -> Html.emptyTable
Just [] -> Html.emptyTable
Just is ->
inst_hdr </>
tda [theclass "body"] << spacedTable1 << (
aboves (map (declBox.ppInstHead) is)
)
constr_has_no_doc (HsConDecl _ _ _ _ _ doc) = isNothing doc
constr_has_no_doc (HsRecDecl _ _ _ _ fields doc)
= isNothing doc && all field_has_no_doc fields
field_has_no_doc (HsFieldDecl nms _ doc) = isNothing doc
isRecDecl (HsRecDecl pos nm tvs ctxt fields maybe_doc) = True
isRecDecl _ = False
ppShortConstr :: Bool -> HsConDecl -> Html
ppShortConstr summary (HsConDecl pos nm tvs ctxt typeList _maybe_doc) =
ppHsConstrHdr tvs ctxt +++
hsep (ppHsBinder summary nm : map ppHsBangType typeList)
ppShortConstr summary (HsRecDecl pos nm tvs ctxt fields maybe_doc) =
ppHsConstrHdr tvs ctxt +++
ppHsBinder summary nm <+>
braces (vanillaTable << aboves (map (ppShortField summary) fields))
ppHsConstrHdr tvs ctxt
= (if null tvs then noHtml else keyword "forall" <+>
hsep (map ppHsName tvs) <+>
toHtml ". ")
+++
(if null ctxt then noHtml else ppHsContext ctxt <+> toHtml "=> ")
ppSideBySideConstr (HsConDecl pos nm tvs ctxt typeList doc) =
declBox (hsep ((ppHsConstrHdr tvs ctxt +++
ppHsBinder False nm) : map ppHsBangType typeList)) <->
maybeRDocBox doc
ppSideBySideConstr (HsRecDecl pos nm tvs ctxt fields doc) =
declBox (ppHsConstrHdr tvs ctxt +++ ppHsBinder False nm) <->
maybeRDocBox doc </>
(tda [theclass "body"] << spacedTable1 <<
aboves (map ppSideBySideField fields))
ppSideBySideField (HsFieldDecl ns ty doc) =
declBox (hsep (punctuate comma (map (ppHsBinder False) ns))
<+> toHtml "::" <+> ppHsBangType ty) <->
maybeRDocBox doc
ppHsFullConstr (HsConDecl pos nm tvs ctxt typeList doc) =
declWithDoc False doc (
hsep ((ppHsConstrHdr tvs ctxt +++
ppHsBinder False nm) : map ppHsBangType typeList)
)
ppHsFullConstr (HsRecDecl pos nm tvs ctxt fields doc) =
td << vanillaTable << (
case doc of
Nothing -> aboves [hdr, fields_html]
Just doc -> aboves [hdr, constr_doc, fields_html]
)
where hdr = declBox (ppHsConstrHdr tvs ctxt +++ ppHsBinder False nm)
constr_doc
| isJust doc = docBox (docToHtml (fromJust doc))
| otherwise = Html.emptyTable
fields_html =
td <<
table ! [width "100%", cellpadding 0, cellspacing 8] << (
aboves (map ppFullField (concat (map expandField fields)))
)
ppShortField summary (HsFieldDecl ns ty _doc)
= tda [theclass "recfield"] << (
hsep (punctuate comma (map (ppHsBinder summary) ns))
<+> toHtml "::" <+> ppHsBangType ty
)
ppFullField (HsFieldDecl [n] ty doc)
= declWithDoc False doc (
ppHsBinder False n <+> toHtml "::" <+> ppHsBangType ty
)
ppFullField _ = error "ppFullField"
expandField (HsFieldDecl ns ty doc) = [ HsFieldDecl [n] ty doc | n <- ns ]
ppHsDataHeader summary is_newty nm args =
(if is_newty then keyword "newtype" else keyword "data") <+>
ppHsBinder summary nm <+> hsep (map ppHsName args)
ppHsBangType :: HsBangType -> Html
ppHsBangType (HsBangedTy ty) = char '!' +++ ppHsAType ty
ppHsBangType (HsUnBangedTy ty) = ppHsAType ty
-- -----------------------------------------------------------------------------
-- Class declarations
ppClassHdr summ [] n tvs fds =
keyword "class"
<+> ppHsBinder summ n <+> hsep (map ppHsName tvs)
<+> ppFds fds
ppClassHdr summ ctxt n tvs fds =
keyword "class" <+> ppHsContext ctxt <+> darrow
<+> ppHsBinder summ n <+> hsep (map ppHsName tvs)
<+> ppFds fds
ppFds fds =
if null fds then noHtml else
char '|' <+> hsep (punctuate comma (map fundep fds))
where
fundep (vars1,vars2) = hsep (map ppHsName vars1) <+> toHtml "->" <+>
hsep (map ppHsName vars2)
ppShortClassDecl summary inst_maps
decl@(HsClassDecl loc ctxt nm tvs fds decls doc) =
if null decls
then declBox hdr
else declBox (hdr <+> keyword "where")
</>
(tda [theclass "body"] <<
vanillaTable <<
aboves [ ppFunSig summary n ty doc
| HsTypeSig _ [n] ty doc <- decls
]
)
where
Just c = declMainBinder decl
hdr = ppClassHdr summary ctxt nm tvs fds
ppHsClassDecl summary inst_maps@(cls_inst_map, _) orig_c
decl@(HsClassDecl loc ctxt nm tvs fds decls doc)
| summary = ppShortClassDecl summary inst_maps decl
| otherwise
= header </>
tda [theclass "body"] << vanillaTable << (
classdoc </> methods_bit </> instances_bit
)
where
Just c = declMainBinder decl
header
| null decls = declBox hdr
| otherwise = declBox (hdr <+> keyword "where")
hdr = ppClassHdr summary ctxt nm tvs fds
classdoc
| Just d <- doc = ndocBox (docToHtml d)
| otherwise = Html.emptyTable
methods_bit
| null decls = Html.emptyTable
| otherwise =
s8 </> meth_hdr </>
tda [theclass "body"] << vanillaTable << (
abovesSep s8 [ ppFunSig summary n ty doc
| HsTypeSig _ [n] ty doc <- decls
]
)
instances_bit
= case instances of
Nothing -> Html.emptyTable
Just [] -> Html.emptyTable
Just is ->
s8 </> inst_hdr </>
tda [theclass "body"] << spacedTable1 << (
aboves (map (declBox.ppInstHead) is)
)
instances = lookupFM cls_inst_map orig_c
kept_decls = filter keepDecl decls
decl_has_no_doc decl = isNothing (declDoc decl)
ppInstHead :: InstHead -> Html
ppInstHead ([],asst) = ppHsAsst asst
ppInstHead (ctxt,asst) = ppHsContext ctxt <+> darrow <+> ppHsAsst asst
-- ----------------------------------------------------------------------------
-- Type signatures
ppFunSig summary nm ty doc
| summary || no_arg_docs ty =
declWithDoc summary doc (ppTypeSig summary nm ty)
| otherwise =
declBox (ppHsBinder False nm) </>
(tda [theclass "body"] << vanillaTable << (
do_args dcolon ty </>
(if (isJust doc)
then ndocBox (docToHtml (fromJust doc))
else Html.emptyTable)
))
where
no_arg_docs (HsForAllType _ _ ty) = no_arg_docs ty
no_arg_docs (HsTyFun (HsTyDoc _ _) _) = False
no_arg_docs (HsTyFun _ r) = no_arg_docs r
no_arg_docs (HsTyDoc _ _) = False
no_arg_docs _ = True
do_args :: Html -> HsType -> HtmlTable
do_args leader (HsForAllType (Just tvs) ctxt ty)
= (declBox (
leader <+>
hsep (keyword "forall" : map ppHsName tvs ++ [toHtml "."]) <+>
ppHsContext ctxt)
<-> rdocBox noHtml) </>
do_args darrow ty
do_args leader (HsForAllType Nothing ctxt ty)
= (declBox (leader <+> ppHsContext ctxt)
<-> rdocBox noHtml) </>
do_args darrow ty
do_args leader (HsTyFun (HsTyDoc ty doc) r)
= (declBox (leader <+> ppHsBType ty) <-> rdocBox (docToHtml doc)) </>
do_args arrow r
do_args leader (HsTyFun ty r)
= (declBox (leader <+> ppHsBType ty) <-> rdocBox noHtml) </>
do_args arrow r
do_args leader (HsTyDoc ty doc)
= (declBox (leader <+> ppHsBType ty) <-> rdocBox (docToHtml doc))
do_args leader ty
= declBox (leader <+> ppHsBType ty) <-> rdocBox (noHtml)
-- -----------------------------------------------------------------------------
-- Types and contexts
ppHsAsst :: (HsQName,[HsType]) -> Html
ppHsAsst (c,args) = ppHsQName c <+> hsep (map ppHsAType args)
ppHsContext :: HsContext -> Html
ppHsContext [] = empty
ppHsContext context = parenList (map ppHsAsst context)
ppHsForAll Nothing context =
hsep [ ppHsContext context, darrow ]
ppHsForAll (Just tvs) [] =
hsep (keyword "forall" : map ppHsName tvs ++ [toHtml "."])
ppHsForAll (Just tvs) context =
hsep (keyword "forall" : map ppHsName tvs ++
[toHtml ".", ppHsContext context, darrow])
ppHsType :: HsType -> Html
ppHsType (HsForAllType maybe_tvs context htype) =
ppHsForAll maybe_tvs context <+> ppHsType htype
ppHsType (HsTyFun a b) = hsep [ppHsBType a, toHtml "->", ppHsType b]
ppHsType t = ppHsBType t
ppHsBType (HsTyDoc ty doc) = ppHsBType ty
ppHsBType (HsTyApp (HsTyCon (Qual (Module "Prelude") (HsTyClsName (HsSpecial "[]")))) b )
= brackets $ ppHsType b
ppHsBType (HsTyApp a b) = ppHsBType a <+> ppHsAType b
ppHsBType t = ppHsAType t
ppHsAType :: HsType -> Html
ppHsAType (HsTyTuple True l) = parenList . map ppHsType $ l
ppHsAType (HsTyTuple False l) = ubxParenList . map ppHsType $ l
ppHsAType (HsTyVar name) = ppHsName name
ppHsAType (HsTyCon name) = ppHsQName name
ppHsAType (HsTyApp (HsTyCon (Qual (Module "Prelude") (HsTyClsName (HsSpecial "[]")))) b )
= brackets $ ppHsType b
ppHsAType t = parens $ ppHsType t
-- -----------------------------------------------------------------------------
-- Names
linkTarget :: HsName -> Html
linkTarget nm = anchor ! [name (hsNameStr nm)] << toHtml ""
ppHsQName :: HsQName -> Html
ppHsQName (UnQual str) = ppHsName str
ppHsQName n@(Qual (Module mod) str)
| n == unit_con_name = ppHsName str
| isSpecial str = ppHsName str
| otherwise = anchor ! [href (linkId mod str)] << ppHsName str
isSpecial (HsTyClsName id) | HsSpecial _ <- id = True
isSpecial (HsVarName id) | HsSpecial _ <- id = True
isSpecial _ = False
ppHsName :: HsName -> Html
ppHsName nm = toHtml (hsNameStr nm)
hsNameStr :: HsName -> String
hsNameStr (HsTyClsName id) = ppHsIdentifier id
hsNameStr (HsVarName id) = ppHsIdentifier id
ppHsIdentifier :: HsIdentifier -> String
ppHsIdentifier (HsIdent str) = str
ppHsIdentifier (HsSymbol str) = str
ppHsIdentifier (HsSpecial str) = str
ppHsBinder :: Bool -> HsName -> Html
ppHsBinder True nm = anchor ! [href ('#':hsNameStr nm)] << ppHsBinder' nm
ppHsBinder False nm = linkTarget nm +++ bold << ppHsBinder' nm
ppHsBinder' (HsTyClsName id) = ppHsBindIdent id
ppHsBinder' (HsVarName id) = ppHsBindIdent id
ppHsBindIdent :: HsIdentifier -> Html
ppHsBindIdent (HsIdent str) = toHtml str
ppHsBindIdent (HsSymbol str) = parens (toHtml str)
ppHsBindIdent (HsSpecial str) = toHtml str
linkId :: String -> HsName -> String
linkId mod str = moduleHtmlFile mod ++ '#': hsNameStr str
ppHsModule :: String -> Html
ppHsModule mod = anchor ! [href (moduleHtmlFile mod)] << toHtml mod
-- -----------------------------------------------------------------------------
-- * Doc Markup
htmlMarkup = Markup {
markupParagraph = paragraph,
markupEmpty = toHtml "",
markupString = toHtml,
markupAppend = (+++),
markupIdentifier = tt . ppHsQName . head,
markupModule = ppHsModule,
markupEmphasis = emphasize . toHtml,
markupMonospaced = tt . toHtml,
markupUnorderedList = ulist . concatHtml . map (li <<),
markupOrderedList = olist . concatHtml . map (li <<),
markupCodeBlock = pre,
markupURL = \url -> anchor ! [href url] << toHtml url
}
-- If the doc is a single paragraph, don't surround it with <P> (this causes
-- ugly extra whitespace with some browsers).
docToHtml (DocParagraph p) = docToHtml p
docToHtml doc = markup htmlMarkup doc
-- -----------------------------------------------------------------------------
-- * Misc
hsep :: [Html] -> Html
hsep [] = noHtml
hsep htmls = foldr1 (\a b -> a+++" "+++b) htmls
infixr 8 <+>
a <+> b = Html (getHtmlElements (toHtml a) ++ HtmlString " ": getHtmlElements (toHtml b))
keyword s = thespan ! [theclass "keyword"] << toHtml s
equals = char '='
comma = char ','
char c = toHtml [c]
empty = noHtml
parens p = char '(' +++ p +++ char ')'
brackets p = char '[' +++ p +++ char ']'
braces p = char '{' +++ p +++ char '}'
punctuate :: Html -> [Html] -> [Html]
punctuate p [] = []
punctuate p (d:ds) = go d ds
where
go d [] = [d]
go d (e:es) = (d +++ p) : go e es
abovesSep :: HtmlTable -> [HtmlTable] -> HtmlTable
abovesSep p [] = Html.emptyTable
abovesSep p (d:ds) = go d ds
where
go d [] = d
go d (e:es) = d </> p </> go e es
parenList :: [Html] -> Html
parenList = parens . hsep . punctuate comma
ubxParenList :: [Html] -> Html
ubxParenList = ubxparens . hsep . punctuate comma
ubxparens p = toHtml "(#" +++ p +++ toHtml "#)"
text = strAttr "TEXT"
-- a box for displaying code
declBox :: Html -> HtmlTable
declBox html = tda [theclass "decl"] << html
-- a horrible hack to keep a box from expanding width-wise
narrowDeclBox :: Html -> HtmlTable
narrowDeclBox html = tda [theclass "decl", width "1"] << html
-- a box for displaying documentation,
-- indented and with a little padding at the top
docBox :: Html -> HtmlTable
docBox html = tda [theclass "doc"] << html
-- a box for displaying documentation, not indented.
ndocBox :: Html -> HtmlTable
ndocBox html = tda [theclass "ndoc"] << html
-- a box for displaying documentation, padded on the left a little
rdocBox :: Html -> HtmlTable
rdocBox html = tda [theclass "rdoc"] << html
maybeRDocBox :: Maybe Doc -> HtmlTable
maybeRDocBox Nothing = rdocBox (noHtml)
maybeRDocBox (Just doc) = rdocBox (docToHtml doc)
-- a box for the buttons at the top of the page
topButBox html = tda [theclass "topbut"] << html
-- a vanilla table has width 100%, no border, no padding, no spacing
-- a narrow table is the same but without width 100%.
vanillaTable = table ! [theclass "vanilla", cellspacing 0, cellpadding 0]
narrowTable = table ! [theclass "narrow", cellspacing 0, cellpadding 0]
spacedTable1 = table ! [theclass "vanilla", cellspacing 1, cellpadding 0]
spacedTable5 = table ! [theclass "vanilla", cellspacing 5, cellpadding 0]
constr_hdr = tda [ theclass "section4" ] << toHtml "Constructors"
meth_hdr = tda [ theclass "section4" ] << toHtml "Methods"
inst_hdr = tda [ theclass "section4" ] << toHtml "Instances"
dcolon = toHtml "::"
arrow = toHtml "->"
darrow = toHtml "=>"
s8, s15 :: HtmlTable
s8 = tda [ theclass "s8" ] << noHtml
s15 = tda [ theclass "s15" ] << noHtml