-----------------------------------------------------------------------------
-- |
-- Module : Haddock.Backends.Html
-- Copyright : (c) Simon Marlow 2003-2006,
-- David Waern 2006-2009
-- License : BSD-like
--
-- Maintainer : haddock@projects.haskell.org
-- Stability : experimental
-- Portability : portable
-----------------------------------------------------------------------------
module Haddock.Backends.Html (
ppHtml, copyHtmlBits,
ppHtmlIndex, ppHtmlContents,
ppHtmlHelpFiles
) where
import Prelude hiding (div)
import Haddock.Backends.DevHelp
import Haddock.Backends.HH
import Haddock.Backends.HH2
import Haddock.ModuleTree
import Haddock.Types
import Haddock.Version
import Haddock.Utils
import Haddock.Utils.Html hiding ( name, title, p )
import qualified Haddock.Utils.Html as Html
import Haddock.GhcUtils
import Control.Exception ( bracket )
import Control.Monad ( when, unless, join )
import Data.Char ( toUpper )
import Data.List ( sortBy, groupBy )
import Data.Maybe
import Foreign.Marshal.Alloc ( allocaBytes )
import System.IO ( IOMode(..), hClose, hGetBuf, hPutBuf, openFile )
import System.Directory hiding ( copyFile )
import System.FilePath hiding ( (</>) )
import Data.Map ( Map )
import qualified Data.Map as Map hiding ( Map )
import Data.Function
import Data.Ord ( comparing )
import GHC hiding ( NoLink, moduleInfo )
import Name
import Module
import RdrName hiding ( Qual, is_explicit )
import FastString ( unpackFS )
import BasicTypes ( IPName(..), Boxity(..) )
import Outputable ( ppr, showSDoc, Outputable )
-- the base, module and entity URLs for the source code and wiki links.
type SourceURLs = (Maybe String, Maybe String, Maybe String)
type WikiURLs = (Maybe String, Maybe String, Maybe String)
-- -----------------------------------------------------------------------------
-- Generating HTML documentation
ppHtml :: String
-> Maybe String -- package
-> [Interface]
-> FilePath -- destination directory
-> Maybe (Doc GHC.RdrName) -- prologue text, maybe
-> Maybe String -- the Html Help format (--html-help)
-> SourceURLs -- the source URL (--source)
-> WikiURLs -- the wiki URL (--wiki)
-> Maybe String -- the contents URL (--use-contents)
-> Maybe String -- the index URL (--use-index)
-> Bool -- whether to use unicode in output (--use-unicode)
-> IO ()
ppHtml doctitle maybe_package ifaces odir prologue maybe_html_help_format
maybe_source_url maybe_wiki_url
maybe_contents_url maybe_index_url unicode = do
let
visible_ifaces = filter visible ifaces
visible i = OptHide `notElem` ifaceOptions i
when (not (isJust maybe_contents_url)) $
ppHtmlContents odir doctitle maybe_package
maybe_html_help_format maybe_index_url maybe_source_url maybe_wiki_url
(map toInstalledIface visible_ifaces)
False -- we don't want to display the packages in a single-package contents
prologue
when (not (isJust maybe_index_url)) $
ppHtmlIndex odir doctitle maybe_package maybe_html_help_format
maybe_contents_url maybe_source_url maybe_wiki_url
(map toInstalledIface visible_ifaces)
when (not (isJust maybe_contents_url && isJust maybe_index_url)) $
ppHtmlHelpFiles doctitle maybe_package ifaces odir maybe_html_help_format []
mapM_ (ppHtmlModule odir doctitle
maybe_source_url maybe_wiki_url
maybe_contents_url maybe_index_url unicode) visible_ifaces
ppHtmlHelpFiles
:: String -- doctitle
-> Maybe String -- package
-> [Interface]
-> FilePath -- destination directory
-> Maybe String -- the Html Help format (--html-help)
-> [FilePath] -- external packages paths
-> IO ()
ppHtmlHelpFiles doctitle maybe_package ifaces odir maybe_html_help_format pkg_paths = do
let
visible_ifaces = filter visible ifaces
visible i = OptHide `notElem` ifaceOptions i
-- Generate index and contents page for Html Help if requested
case maybe_html_help_format of
Nothing -> return ()
Just "mshelp" -> ppHHProject odir doctitle maybe_package visible_ifaces pkg_paths
Just "mshelp2" -> do
ppHH2Files odir maybe_package visible_ifaces pkg_paths
ppHH2Collection odir doctitle maybe_package
Just "devhelp" -> ppDevHelpFile odir doctitle maybe_package visible_ifaces
Just format -> fail ("The "++format++" format is not implemented")
copyFile :: FilePath -> FilePath -> IO ()
copyFile fromFPath toFPath =
(bracket (openFile fromFPath ReadMode) hClose $ \hFrom ->
bracket (openFile toFPath WriteMode) hClose $ \hTo ->
allocaBytes bufferSize $ \buffer ->
copyContents hFrom hTo buffer)
where
bufferSize = 1024
copyContents hFrom hTo buffer = do
count <- hGetBuf hFrom buffer bufferSize
when (count > 0) $ do
hPutBuf hTo buffer count
copyContents hFrom hTo buffer
copyHtmlBits :: FilePath -> FilePath -> Maybe FilePath -> IO ()
copyHtmlBits odir libdir maybe_css = do
let
libhtmldir = joinPath [libdir, "html"]
css_file = case maybe_css of
Nothing -> joinPath [libhtmldir, cssFile]
Just f -> f
css_destination = joinPath [odir, cssFile]
copyLibFile f = do
copyFile (joinPath [libhtmldir, f]) (joinPath [odir, f])
copyFile css_file css_destination
mapM_ copyLibFile [ iconFile, plusFile, minusFile, jsFile, framesFile ]
footer :: HtmlTable
footer =
tda [theclass "botbar"] <<
( toHtml "Produced by" <+>
(anchor ! [href projectUrl] << toHtml projectName) <+>
toHtml ("version " ++ projectVersion)
)
srcButton :: SourceURLs -> Maybe Interface -> HtmlTable
srcButton (Just src_base_url, _, _) Nothing =
topButBox (anchor ! [href src_base_url] << toHtml "Source code")
srcButton (_, Just src_module_url, _) (Just iface) =
let url = spliceURL (Just $ ifaceOrigFilename iface)
(Just $ ifaceMod iface) Nothing Nothing src_module_url
in topButBox (anchor ! [href url] << toHtml "Source code")
srcButton _ _ =
Html.emptyTable
spliceURL :: Maybe FilePath -> Maybe Module -> Maybe GHC.Name ->
Maybe SrcSpan -> String -> String
spliceURL maybe_file maybe_mod maybe_name maybe_loc url = run url
where
file = fromMaybe "" maybe_file
mdl = case maybe_mod of
Nothing -> ""
Just m -> moduleString m
(name, kind) =
case maybe_name of
Nothing -> ("","")
Just n | isValOcc (nameOccName n) -> (escapeStr (getOccString n), "v")
| otherwise -> (escapeStr (getOccString n), "t")
line = case maybe_loc of
Nothing -> ""
Just span_ -> show $ srcSpanStartLine span_
run "" = ""
run ('%':'M':rest) = mdl ++ run rest
run ('%':'F':rest) = file ++ run rest
run ('%':'N':rest) = name ++ run rest
run ('%':'K':rest) = kind ++ run rest
run ('%':'L':rest) = line ++ run rest
run ('%':'%':rest) = "%" ++ run rest
run ('%':'{':'M':'O':'D':'U':'L':'E':'}':rest) = mdl ++ run rest
run ('%':'{':'F':'I':'L':'E':'}':rest) = file ++ run rest
run ('%':'{':'N':'A':'M':'E':'}':rest) = name ++ run rest
run ('%':'{':'K':'I':'N':'D':'}':rest) = kind ++ run rest
run ('%':'{':'M':'O':'D':'U':'L':'E':'/':'.':'/':c:'}':rest) =
map (\x -> if x == '.' then c else x) mdl ++ run rest
run ('%':'{':'F':'I':'L':'E':'/':'/':'/':c:'}':rest) =
map (\x -> if x == '/' then c else x) file ++ run rest
run ('%':'{':'L':'I':'N':'E':'}':rest) = line ++ run rest
run (c:rest) = c : run rest
wikiButton :: WikiURLs -> Maybe Module -> HtmlTable
wikiButton (Just wiki_base_url, _, _) Nothing =
topButBox (anchor ! [href wiki_base_url] << toHtml "User Comments")
wikiButton (_, Just wiki_module_url, _) (Just mdl) =
let url = spliceURL Nothing (Just mdl) Nothing Nothing wiki_module_url
in topButBox (anchor ! [href url] << toHtml "User Comments")
wikiButton _ _ =
Html.emptyTable
contentsButton :: Maybe String -> HtmlTable
contentsButton maybe_contents_url
= topButBox (anchor ! [href url] << toHtml "Contents")
where url = maybe contentsHtmlFile id maybe_contents_url
indexButton :: Maybe String -> HtmlTable
indexButton maybe_index_url
= topButBox (anchor ! [href url] << toHtml "Index")
where url = maybe indexHtmlFile id maybe_index_url
simpleHeader :: String -> Maybe String -> Maybe String
-> SourceURLs -> WikiURLs -> HtmlTable
simpleHeader doctitle maybe_contents_url maybe_index_url
maybe_source_url maybe_wiki_url =
(tda [theclass "topbar"] <<
vanillaTable << (
(td <<
image ! [src "haskell_icon.gif", width "16", height 16, alt " " ]
) <->
(tda [theclass "title"] << toHtml doctitle) <->
srcButton maybe_source_url Nothing <->
wikiButton maybe_wiki_url Nothing <->
contentsButton maybe_contents_url <-> indexButton maybe_index_url
))
pageHeader :: String -> Interface -> String
-> SourceURLs -> WikiURLs
-> Maybe String -> Maybe String -> HtmlTable
pageHeader mdl iface doctitle
maybe_source_url maybe_wiki_url
maybe_contents_url maybe_index_url =
(tda [theclass "topbar"] <<
vanillaTable << (
(td <<
image ! [src "haskell_icon.gif", width "16", height 16, alt " "]
) <->
(tda [theclass "title"] << toHtml doctitle) <->
srcButton maybe_source_url (Just iface) <->
wikiButton maybe_wiki_url (Just $ ifaceMod iface) <->
contentsButton maybe_contents_url <->
indexButton maybe_index_url
)
) </>
tda [theclass "modulebar"] <<
(vanillaTable << (
(td << font ! [size "6"] << toHtml mdl) <->
moduleInfo iface
)
)
moduleInfo :: Interface -> HtmlTable
moduleInfo iface =
let
info = ifaceInfo iface
doOneEntry :: (String, (HaddockModInfo GHC.Name) -> Maybe String) -> Maybe HtmlTable
doOneEntry (fieldName,field) = case field info of
Nothing -> Nothing
Just fieldValue ->
Just ((tda [theclass "infohead"] << toHtml fieldName)
<-> (tda [theclass "infoval"]) << toHtml fieldValue)
entries :: [HtmlTable]
entries = mapMaybe doOneEntry [
("Portability",hmi_portability),
("Stability",hmi_stability),
("Maintainer",hmi_maintainer)
]
in
case entries of
[] -> Html.emptyTable
_ -> tda [align "right"] << narrowTable << (foldl1 (</>) entries)
-- ---------------------------------------------------------------------------
-- Generate the module contents
ppHtmlContents
:: FilePath
-> String
-> Maybe String
-> Maybe String
-> Maybe String
-> SourceURLs
-> WikiURLs
-> [InstalledInterface] -> Bool -> Maybe (Doc GHC.RdrName)
-> IO ()
ppHtmlContents odir doctitle
maybe_package maybe_html_help_format maybe_index_url
maybe_source_url maybe_wiki_url ifaces showPkgs prologue = do
let tree = mkModuleTree showPkgs
[(instMod iface, toInstalledDescription iface) | iface <- ifaces]
html =
header
(documentCharacterEncoding +++
thetitle (toHtml doctitle) +++
styleSheet +++
(script ! [src jsFile, thetype "text/javascript"] $ noHtml)) +++
body << vanillaTable << (
simpleHeader doctitle Nothing maybe_index_url
maybe_source_url maybe_wiki_url </>
ppPrologue doctitle prologue </>
ppModuleTree doctitle tree </>
s15 </>
footer
)
createDirectoryIfMissing True odir
writeFile (joinPath [odir, contentsHtmlFile]) (renderHtml html)
-- XXX: think of a better place for this?
ppHtmlContentsFrame odir doctitle ifaces
-- Generate contents page for Html Help if requested
case maybe_html_help_format of
Nothing -> return ()
Just "mshelp" -> ppHHContents odir doctitle maybe_package tree
Just "mshelp2" -> ppHH2Contents odir doctitle maybe_package tree
Just "devhelp" -> return ()
Just format -> fail ("The "++format++" format is not implemented")
ppPrologue :: String -> Maybe (Doc GHC.RdrName) -> HtmlTable
ppPrologue _ Nothing = Html.emptyTable
ppPrologue title (Just doc) =
(tda [theclass "section1"] << toHtml title) </>
docBox (rdrDocToHtml doc)
ppModuleTree :: String -> [ModuleTree] -> HtmlTable
ppModuleTree _ ts =
tda [theclass "section1"] << toHtml "Modules" </>
td << vanillaTable2 << htmlTable
where
genTable tbl id_ [] = (tbl, id_)
genTable tbl id_ (x:xs) = genTable (tbl </> u) id' xs
where
(u,id') = mkNode [] x 0 id_
(htmlTable,_) = genTable emptyTable 0 ts
mkNode :: [String] -> ModuleTree -> Int -> Int -> (HtmlTable,Int)
mkNode ss (Node s leaf pkg short ts) depth id_ = htmlNode
where
htmlNode = case ts of
[] -> (td_pad_w 1.25 depth << htmlModule <-> shortDescr <-> htmlPkg,id_)
_ -> (td_w depth << (collapsebutton id_s +++ htmlModule) <-> shortDescr <-> htmlPkg </>
(td_subtree << sub_tree), id')
mod_width = 50::Int {-em-}
td_pad_w :: Double -> Int -> Html -> HtmlTable
td_pad_w pad depth_ =
tda [thestyle ("padding-left: " ++ show pad ++ "em;" ++
"width: " ++ show (mod_width - depth_*2) ++ "em")]
td_w depth_ =
tda [thestyle ("width: " ++ show (mod_width - depth_*2) ++ "em")]
td_subtree =
tda [thestyle ("padding: 0; padding-left: 2em")]
shortDescr :: HtmlTable
shortDescr = case short of
Nothing -> td empty
Just doc -> tda [theclass "rdoc"] (origDocToHtml doc)
htmlModule
| leaf = ppModule (mkModule (stringToPackageId pkgName)
(mkModuleName mdl)) ""
| otherwise = toHtml s
-- ehm.. TODO: change the ModuleTree type
(htmlPkg, pkgName) = case pkg of
Nothing -> (td << empty, "")
Just p -> (td << toHtml p, p)
mdl = foldr (++) "" (s' : map ('.':) ss')
(s':ss') = reverse (s:ss)
-- reconstruct the module name
id_s = "n." ++ show id_
(sub_tree,id') = genSubTree emptyTable (id_+1) ts
genSubTree :: HtmlTable -> Int -> [ModuleTree] -> (Html,Int)
genSubTree htmlTable id__ [] = (sub_tree_, id__)
where
sub_tree_ = collapsed vanillaTable2 id_s htmlTable
genSubTree htmlTable id__ (x:xs) = genSubTree (htmlTable </> u) id__' xs
where
(u,id__') = mkNode (s:ss) x (depth+1) id__
-- The URL for source and wiki links, and the current module
type LinksInfo = (SourceURLs, WikiURLs)
-- | Turn a module tree into a flat list of full module names. E.g.,
-- @
-- A
-- +-B
-- +-C
-- @
-- becomes
-- @["A", "A.B", "A.B.C"]@
flatModuleTree :: [InstalledInterface] -> [Html]
flatModuleTree ifaces =
map (uncurry ppModule' . head)
. groupBy ((==) `on` fst)
. sortBy (comparing fst)
$ mods
where
mods = [ (moduleString mdl, mdl) | mdl <- map instMod ifaces ]
ppModule' txt mdl =
anchor ! [href ((moduleHtmlFile mdl)), target mainFrameName]
<< toHtml txt
ppHtmlContentsFrame :: FilePath -> String -> [InstalledInterface] -> IO ()
ppHtmlContentsFrame odir doctitle ifaces = do
let mods = flatModuleTree ifaces
html =
header
(documentCharacterEncoding +++
thetitle (toHtml doctitle) +++
styleSheet +++
(script ! [src jsFile, thetype "text/javascript"] $ noHtml)) +++
body << vanillaTable << Html.p << (
foldr (+++) noHtml (map (+++br) mods))
createDirectoryIfMissing True odir
writeFile (joinPath [odir, frameIndexHtmlFile]) (renderHtml html)
-- ---------------------------------------------------------------------------
-- Generate the index
ppHtmlIndex :: FilePath
-> String
-> Maybe String
-> Maybe String
-> Maybe String
-> SourceURLs
-> WikiURLs
-> [InstalledInterface]
-> IO ()
ppHtmlIndex odir doctitle maybe_package maybe_html_help_format
maybe_contents_url maybe_source_url maybe_wiki_url ifaces = do
let html =
header (documentCharacterEncoding +++
thetitle (toHtml (doctitle ++ " (Index)")) +++
styleSheet +++
(script ! [src jsFile, thetype "text/javascript"] $ noHtml)) +++
body << vanillaTable << (
simpleHeader doctitle maybe_contents_url Nothing
maybe_source_url maybe_wiki_url </>
index_html
)
createDirectoryIfMissing True odir
when split_indices $
mapM_ (do_sub_index index) initialChars
writeFile (joinPath [odir, indexHtmlFile]) (renderHtml html)
-- Generate index and contents page for Html Help if requested
case maybe_html_help_format of
Nothing -> return ()
Just "mshelp" -> ppHHIndex odir maybe_package ifaces
Just "mshelp2" -> ppHH2Index odir maybe_package ifaces
Just "devhelp" -> return ()
Just format -> fail ("The "++format++" format is not implemented")
where
index_html
| split_indices =
tda [theclass "section1"] <<
toHtml ("Index") </>
indexInitialLetterLinks
| otherwise =
td << setTrClass (table ! [identifier "indexlist", cellpadding 0, cellspacing 5] <<
aboves (map indexElt index))
-- an arbitrary heuristic:
-- too large, and a single-page will be slow to load
-- too small, and we'll have lots of letter-indexes with only one
-- or two members in them, which seems inefficient or
-- unnecessarily hard to use.
split_indices = length index > 150
setTrClass :: Html -> Html
setTrClass (Html xs) = Html $ map f xs
where
f (HtmlTag name attrs inner)
| map toUpper name == "TR" = HtmlTag name (theclass "indexrow":attrs) inner
| otherwise = HtmlTag name attrs (setTrClass inner)
f x = x
indexInitialLetterLinks =
td << setTrClass (table ! [cellpadding 0, cellspacing 5] <<
besides [ td << anchor ! [href (subIndexHtmlFile c)] <<
toHtml [c]
| c <- initialChars
, any ((==c) . toUpper . head . fst) index ])
-- todo: what about names/operators that start with Unicode
-- characters?
-- Exports beginning with '_' can be listed near the end,
-- presumably they're not as important... but would be listed
-- with non-split index!
initialChars = [ 'A'..'Z' ] ++ ":!#$%&*+./<=>?@\\^|-~" ++ "_"
do_sub_index this_ix c
= unless (null index_part) $
writeFile (joinPath [odir, subIndexHtmlFile c]) (renderHtml html)
where
html = header (documentCharacterEncoding +++
thetitle (toHtml (doctitle ++ " (Index)")) +++
styleSheet) +++
body << vanillaTable << (
simpleHeader doctitle maybe_contents_url Nothing
maybe_source_url maybe_wiki_url </>
indexInitialLetterLinks </>
tda [theclass "section1"] <<
toHtml ("Index (" ++ c:")") </>
td << setTrClass (table ! [identifier "indexlist", cellpadding 0, cellspacing 5] <<
aboves (map indexElt index_part) )
)
index_part = [(n,stuff) | (n,stuff) <- this_ix, toUpper (head n) == c]
index :: [(String, Map GHC.Name [(Module,Bool)])]
index = sortBy cmp (Map.toAscList full_index)
where cmp (n1,_) (n2,_) = map toUpper n1 `compare` map toUpper n2
-- for each name (a plain string), we have a number of original HsNames that
-- it can refer to, and for each of those we have a list of modules
-- that export that entity. Each of the modules exports the entity
-- in a visible or invisible way (hence the Bool).
full_index :: Map String (Map GHC.Name [(Module,Bool)])
full_index = Map.fromListWith (flip (Map.unionWith (++)))
(concat (map getIfaceIndex ifaces))
getIfaceIndex iface =
[ (getOccString name
, Map.fromList [(name, [(mdl, name `elem` instVisibleExports iface)])])
| name <- instExports iface ]
where mdl = instMod iface
indexElt :: (String, Map GHC.Name [(Module,Bool)]) -> HtmlTable
indexElt (str, entities) =
case Map.toAscList entities of
[(nm,entries)] ->
tda [ theclass "indexentry" ] << toHtml str <->
indexLinks nm entries
many_entities ->
tda [ theclass "indexentry" ] << toHtml str </>
aboves (map doAnnotatedEntity (zip [1..] many_entities))
doAnnotatedEntity :: (Integer, (Name, [(Module, Bool)])) -> HtmlTable
doAnnotatedEntity (j,(nm,entries))
= tda [ theclass "indexannot" ] <<
toHtml (show j) <+> parens (ppAnnot (nameOccName nm)) <->
indexLinks nm entries
ppAnnot n | not (isValOcc n) = toHtml "Type/Class"
| isDataOcc n = toHtml "Data Constructor"
| otherwise = toHtml "Function"
indexLinks nm entries =
tda [ theclass "indexlinks" ] <<
hsep (punctuate comma
[ if visible then
linkId mdl (Just nm) << toHtml (moduleString mdl)
else
toHtml (moduleString mdl)
| (mdl, visible) <- entries ])
-- ---------------------------------------------------------------------------
-- Generate the HTML page for a module
ppHtmlModule
:: FilePath -> String
-> SourceURLs -> WikiURLs
-> Maybe String -> Maybe String -> Bool
-> Interface -> IO ()
ppHtmlModule odir doctitle
maybe_source_url maybe_wiki_url
maybe_contents_url maybe_index_url unicode iface = do
let
mdl = ifaceMod iface
mdl_str = moduleString mdl
html =
header (documentCharacterEncoding +++
thetitle (toHtml mdl_str) +++
styleSheet +++
(script ! [src jsFile, thetype "text/javascript"] $ noHtml) +++
(script ! [thetype "text/javascript"]
-- XXX: quoting errors possible?
<< Html [HtmlString ("window.onload = function () {setSynopsis(\"mini_"
++ moduleHtmlFile mdl ++ "\")};")])
) +++
body << vanillaTable << (
pageHeader mdl_str iface doctitle
maybe_source_url maybe_wiki_url
maybe_contents_url maybe_index_url </> s15 </>
ifaceToHtml maybe_source_url maybe_wiki_url iface unicode </> s15 </>
footer
)
createDirectoryIfMissing True odir
writeFile (joinPath [odir, moduleHtmlFile mdl]) (renderHtml html)
ppHtmlModuleMiniSynopsis odir doctitle iface unicode
ppHtmlModuleMiniSynopsis :: FilePath -> String -> Interface -> Bool -> IO ()
ppHtmlModuleMiniSynopsis odir _doctitle iface unicode = do
let mdl = ifaceMod iface
html =
header
(documentCharacterEncoding +++
thetitle (toHtml $ moduleString mdl) +++
styleSheet +++
(script ! [src jsFile, thetype "text/javascript"] $ noHtml)) +++
body << thediv ! [ theclass "outer" ] << (
(thediv ! [theclass "mini-topbar"]
<< toHtml (moduleString mdl)) +++
miniSynopsis mdl iface unicode)
createDirectoryIfMissing True odir
writeFile (joinPath [odir, "mini_" ++ moduleHtmlFile mdl]) (renderHtml html)
ifaceToHtml :: SourceURLs -> WikiURLs -> Interface -> Bool -> HtmlTable
ifaceToHtml maybe_source_url maybe_wiki_url iface unicode
= abovesSep s15 (contents ++ description: synopsis: maybe_doc_hdr: bdy)
where
exports = numberSectionHeadings (ifaceRnExportItems iface)
-- todo: if something has only sub-docs, or fn-args-docs, should
-- it be measured here and thus prevent omitting the synopsis?
has_doc (ExportDecl _ doc _ _) = isJust (fst doc)
has_doc (ExportNoDecl _ _) = False
has_doc (ExportModule _) = False
has_doc _ = True
no_doc_at_all = not (any has_doc exports)
contents = case ppModuleContents exports of
Nothing -> []
Just x -> [td << vanillaTable << x]
description
= case ifaceRnDoc iface of
Nothing -> Html.emptyTable
Just doc -> (tda [theclass "section1"] << toHtml "Description") </>
docBox (docToHtml doc)
-- 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 linksInfo unicode)
(filter forSummary exports))
)
-- if the documentation doesn't begin with a section header, then
-- add one ("Documentation").
maybe_doc_hdr
= case exports of
[] -> Html.emptyTable
ExportGroup _ _ _ : _ -> Html.emptyTable
_ -> tda [ theclass "section1" ] << toHtml "Documentation"
bdy = map (processExport False linksInfo unicode) exports
linksInfo = (maybe_source_url, maybe_wiki_url)
miniSynopsis :: Module -> Interface -> Bool -> Html
miniSynopsis mdl iface unicode =
thediv ! [ theclass "mini-synopsis" ]
<< hsep (map (processForMiniSynopsis mdl unicode) $ exports)
where
exports = numberSectionHeadings (ifaceRnExportItems iface)
processForMiniSynopsis :: Module -> Bool -> ExportItem DocName -> Html
processForMiniSynopsis mdl unicode (ExportDecl (L _loc decl0) _doc _ _insts) =
thediv ! [theclass "decl" ] <<
case decl0 of
TyClD d@(TyFamily{}) -> ppTyFamHeader True False d unicode
TyClD d@(TyData{tcdTyPats = ps})
| Nothing <- ps -> keyword "data" <++> ppTyClBinderWithVarsMini mdl d
| Just _ <- ps -> keyword "data" <++> keyword "instance"
<++> ppTyClBinderWithVarsMini mdl d
TyClD d@(TySynonym{tcdTyPats = ps})
| Nothing <- ps -> keyword "type" <++> ppTyClBinderWithVarsMini mdl d
| Just _ <- ps -> keyword "type" <++> keyword "instance"
<++> ppTyClBinderWithVarsMini mdl d
TyClD d@(ClassDecl {}) ->
keyword "class" <++> ppTyClBinderWithVarsMini mdl d
SigD (TypeSig (L _ n) (L _ _)) ->
let nm = docNameOcc n
in ppNameMini mdl nm
_ -> noHtml
processForMiniSynopsis _ _ (ExportGroup lvl _id txt) =
let heading
| lvl == 1 = h1
| lvl == 2 = h2
| lvl >= 3 = h3
| otherwise = error "bad group level"
in heading << docToHtml txt
processForMiniSynopsis _ _ _ = noHtml
ppNameMini :: Module -> OccName -> Html
ppNameMini mdl nm =
anchor ! [ href (moduleNameUrl mdl nm)
, target mainFrameName ]
<< ppBinder' nm
ppTyClBinderWithVarsMini :: Module -> TyClDecl DocName -> Html
ppTyClBinderWithVarsMini mdl decl =
let n = unLoc $ tcdLName decl
ns = tyvarNames $ tcdTyVars decl
in ppTypeApp n ns (ppNameMini mdl . docNameOcc) ppTyName
ppModuleContents :: [ExportItem DocName] -> Maybe HtmlTable
ppModuleContents exports
| length sections == 0 = Nothing
| otherwise = Just (tda [theclass "section4"] << bold << toHtml "Contents"
</> td << dlist << concatHtml sections)
where
(sections, _leftovers{-should be []-}) = process 0 exports
process :: Int -> [ExportItem DocName] -> ([Html],[ExportItem DocName])
process _ [] = ([], [])
process n items@(ExportGroup lev id0 doc : rest)
| lev <= n = ( [], items )
| otherwise = ( html:secs, rest2 )
where
html = (dterm << linkedAnchor id0 << docToHtml doc)
+++ mk_subsections ssecs
(ssecs, rest1) = process lev rest
(secs, 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 DocName] -> [ExportItem DocName]
numberSectionHeadings exports = go 1 exports
where go :: Int -> [ExportItem DocName] -> [ExportItem DocName]
go _ [] = []
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 -> LinksInfo -> Bool -> (ExportItem DocName) -> HtmlTable
processExport _ _ _ (ExportGroup lev id0 doc)
= ppDocGroup lev (namedAnchor id0 << docToHtml doc)
processExport summary links unicode (ExportDecl decl doc subdocs insts)
= ppDecl summary links decl doc insts subdocs unicode
processExport _ _ _ (ExportNoDecl y [])
= declBox (ppDocName y)
processExport _ _ _ (ExportNoDecl y subs)
= declBox (ppDocName y <+> parenList (map ppDocName subs))
processExport _ _ _ (ExportDoc doc)
= docBox (docToHtml doc)
processExport _ _ _ (ExportModule mdl)
= declBox (toHtml "module" <+> ppModule mdl "")
forSummary :: (ExportItem DocName) -> Bool
forSummary (ExportGroup _ _ _) = False
forSummary (ExportDoc _) = False
forSummary _ = True
ppDocGroup :: Int -> Html -> HtmlTable
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
declWithDoc :: Bool -> LinksInfo -> SrcSpan -> DocName -> Maybe (Doc DocName) -> Html -> HtmlTable
declWithDoc True _ _ _ _ html_decl = declBox html_decl
declWithDoc False links loc nm Nothing html_decl = topDeclBox links loc nm html_decl
declWithDoc False links loc nm (Just doc) html_decl =
topDeclBox links loc nm html_decl </> docBox (docToHtml doc)
-- TODO: use DeclInfo DocName or something
ppDecl :: Bool -> LinksInfo -> LHsDecl DocName ->
DocForDecl DocName -> [DocInstance DocName] -> [(DocName, DocForDecl DocName)] -> Bool -> HtmlTable
ppDecl summ links (L loc decl) (mbDoc, fnArgsDoc) instances subdocs unicode = case decl of
TyClD d@(TyFamily {}) -> ppTyFam summ False links loc mbDoc d unicode
TyClD d@(TyData {})
| Nothing <- tcdTyPats d -> ppDataDecl summ links instances subdocs loc mbDoc d unicode
| Just _ <- tcdTyPats d -> ppDataInst summ links loc mbDoc d
TyClD d@(TySynonym {})
| Nothing <- tcdTyPats d -> ppTySyn summ links loc (mbDoc, fnArgsDoc) d unicode
| Just _ <- tcdTyPats d -> ppTyInst summ False links loc mbDoc d unicode
TyClD d@(ClassDecl {}) -> ppClassDecl summ links instances loc mbDoc subdocs d unicode
SigD (TypeSig (L _ n) (L _ t)) -> ppFunSig summ links loc (mbDoc, fnArgsDoc) n t unicode
ForD d -> ppFor summ links loc (mbDoc, fnArgsDoc) d unicode
InstD _ -> Html.emptyTable
_ -> error "declaration not supported by ppDecl"
ppFunSig :: Bool -> LinksInfo -> SrcSpan -> DocForDecl DocName ->
DocName -> HsType DocName -> Bool -> HtmlTable
ppFunSig summary links loc doc docname typ unicode =
ppTypeOrFunSig summary links loc docname typ doc
(ppTypeSig summary occname typ unicode, ppBinder False occname, dcolon unicode) unicode
where
occname = docNameOcc docname
ppTypeOrFunSig :: Bool -> LinksInfo -> SrcSpan -> DocName -> HsType DocName ->
DocForDecl DocName -> (Html, Html, Html) -> Bool -> HtmlTable
ppTypeOrFunSig summary links loc docname typ (doc, argDocs) (pref1, pref2, sep) unicode
| summary || Map.null argDocs = declWithDoc summary links loc docname doc pref1
| otherwise = topDeclBox links loc docname pref2 </>
(tda [theclass "body"] << vanillaTable << (
do_args 0 sep typ </>
(case doc of
Just d -> ndocBox (docToHtml d)
Nothing -> Html.emptyTable)
))
where
argDocHtml n = case Map.lookup n argDocs of
Just adoc -> docToHtml adoc
Nothing -> noHtml
do_largs n leader (L _ t) = do_args n leader t
do_args :: Int -> Html -> (HsType DocName) -> HtmlTable
do_args n leader (HsForAllTy Explicit tvs lctxt ltype)
= (argBox (
leader <+>
hsep (forallSymbol unicode : ppTyVars tvs ++ [dot]) <+>
ppLContextNoArrow lctxt unicode)
<-> rdocBox noHtml) </>
do_largs n (darrow unicode) ltype
do_args n leader (HsForAllTy Implicit _ lctxt ltype)
| not (null (unLoc lctxt))
= (argBox (leader <+> ppLContextNoArrow lctxt unicode)
<-> rdocBox noHtml) </>
do_largs n (darrow unicode) ltype
-- if we're not showing any 'forall' or class constraints or
-- anything, skip having an empty line for the context.
| otherwise
= do_largs n leader ltype
do_args n leader (HsFunTy lt r)
= (argBox (leader <+> ppLFunLhType unicode lt) <-> rdocBox (argDocHtml n))
</> do_largs (n+1) (arrow unicode) r
do_args n leader t
= argBox (leader <+> ppType unicode t) <-> rdocBox (argDocHtml n)
ppTyVars :: [LHsTyVarBndr DocName] -> [Html]
ppTyVars tvs = map ppTyName (tyvarNames tvs)
tyvarNames :: [LHsTyVarBndr DocName] -> [Name]
tyvarNames = map (getName . hsTyVarName . unLoc)
ppFor :: Bool -> LinksInfo -> SrcSpan -> DocForDecl DocName -> ForeignDecl DocName -> Bool -> HtmlTable
ppFor summary links loc doc (ForeignImport (L _ name) (L _ typ) _) unicode
= ppFunSig summary links loc doc name typ unicode
ppFor _ _ _ _ _ _ = error "ppFor"
-- we skip type patterns for now
ppTySyn :: Bool -> LinksInfo -> SrcSpan -> DocForDecl DocName -> TyClDecl DocName -> Bool -> HtmlTable
ppTySyn summary links loc doc (TySynonym (L _ name) ltyvars _ ltype) unicode
= ppTypeOrFunSig summary links loc name (unLoc ltype) doc
(full, hdr, spaceHtml +++ equals) unicode
where
hdr = hsep ([keyword "type", ppBinder summary occ] ++ ppTyVars ltyvars)
full = hdr <+> equals <+> ppLType unicode ltype
occ = docNameOcc name
ppTySyn _ _ _ _ _ _ = error "declaration not supported by ppTySyn"
ppTypeSig :: Bool -> OccName -> HsType DocName -> Bool -> Html
ppTypeSig summary nm ty unicode = ppBinder summary nm <+> dcolon unicode <+> ppType unicode ty
ppTyName :: Name -> Html
ppTyName name
| isNameSym name = parens (ppName name)
| otherwise = ppName name
--------------------------------------------------------------------------------
-- Type families
--------------------------------------------------------------------------------
ppTyFamHeader :: Bool -> Bool -> TyClDecl DocName -> Bool -> Html
ppTyFamHeader summary associated decl unicode =
(case tcdFlavour decl of
TypeFamily
| associated -> keyword "type"
| otherwise -> keyword "type family"
DataFamily
| associated -> keyword "data"
| otherwise -> keyword "data family"
) <+>
ppTyClBinderWithVars summary decl <+>
case tcdKind decl of
Just kind -> dcolon unicode <+> ppKind kind
Nothing -> empty
ppTyFam :: Bool -> Bool -> LinksInfo -> SrcSpan -> Maybe (Doc DocName) ->
TyClDecl DocName -> Bool -> HtmlTable
ppTyFam summary associated links loc mbDoc decl unicode
| summary = declWithDoc summary links loc docname mbDoc
(ppTyFamHeader True associated decl unicode)
| associated, isJust mbDoc = header_ </> bodyBox << doc
| associated = header_
| null instances, isJust mbDoc = header_ </> bodyBox << doc
| null instances = header_
| isJust mbDoc = header_ </> bodyBox << (doc </> instancesBit)
| otherwise = header_ </> bodyBox << instancesBit
where
docname = tcdName decl
header_ = topDeclBox links loc docname (ppTyFamHeader summary associated decl unicode)
doc = ndocBox . docToHtml . fromJust $ mbDoc
instId = collapseId (getName docname)
instancesBit = instHdr instId </>
tda [theclass "body"] <<
collapsed thediv instId (
spacedTable1 << (
aboves (map (ppDocInstance unicode) instances)
)
)
-- TODO: get the instances
instances = []
--------------------------------------------------------------------------------
-- Indexed data types
--------------------------------------------------------------------------------
ppDataInst :: a
ppDataInst = undefined
--------------------------------------------------------------------------------
-- Indexed newtypes
--------------------------------------------------------------------------------
-- TODO
-- ppNewTyInst = undefined
--------------------------------------------------------------------------------
-- Indexed types
--------------------------------------------------------------------------------
ppTyInst :: Bool -> Bool -> LinksInfo -> SrcSpan -> Maybe (Doc DocName) ->
TyClDecl DocName -> Bool -> HtmlTable
ppTyInst summary associated links loc mbDoc decl unicode
| summary = declWithDoc summary links loc docname mbDoc
(ppTyInstHeader True associated decl unicode)
| isJust mbDoc = header_ </> bodyBox << doc
| otherwise = header_
where
docname = tcdName decl
header_ = topDeclBox links loc docname (ppTyInstHeader summary associated decl unicode)
doc = case mbDoc of
Just d -> ndocBox (docToHtml d)
Nothing -> Html.emptyTable
ppTyInstHeader :: Bool -> Bool -> TyClDecl DocName -> Bool -> Html
ppTyInstHeader _ _ decl unicode =
keyword "type instance" <+>
ppAppNameTypes (tcdName decl) typeArgs unicode
where
typeArgs = map unLoc . fromJust . tcdTyPats $ decl
--------------------------------------------------------------------------------
-- Associated Types
--------------------------------------------------------------------------------
ppAssocType :: Bool -> LinksInfo -> DocForDecl DocName -> LTyClDecl DocName -> Bool -> HtmlTable
ppAssocType summ links doc (L loc decl) unicode =
case decl of
TyFamily {} -> ppTyFam summ True links loc (fst doc) decl unicode
TySynonym {} -> ppTySyn summ links loc doc decl unicode
_ -> error "declaration type not supported by ppAssocType"
--------------------------------------------------------------------------------
-- TyClDecl helpers
--------------------------------------------------------------------------------
-- | Print a type family / newtype / data / class binder and its variables
ppTyClBinderWithVars :: Bool -> TyClDecl DocName -> Html
ppTyClBinderWithVars summ decl =
ppAppDocNameNames summ (unLoc $ tcdLName decl) (tyvarNames $ tcdTyVars decl)
--------------------------------------------------------------------------------
-- Type applications
--------------------------------------------------------------------------------
-- | Print an application of a DocName and a list of HsTypes
ppAppNameTypes :: DocName -> [HsType DocName] -> Bool -> Html
ppAppNameTypes n ts unicode = ppTypeApp n ts ppDocName (ppParendType unicode)
-- | Print an application of a DocName and a list of Names
ppAppDocNameNames :: Bool -> DocName -> [Name] -> Html
ppAppDocNameNames summ n ns =
ppTypeApp n ns (ppBinder summ . docNameOcc) ppTyName
-- | General printing of type applications
ppTypeApp :: DocName -> [a] -> (DocName -> Html) -> (a -> Html) -> Html
ppTypeApp n (t1:t2:rest) ppDN ppT
| operator, not . null $ rest = parens opApp <+> hsep (map ppT rest)
| operator = opApp
where
operator = isNameSym . getName $ n
opApp = ppT t1 <+> ppDN n <+> ppT t2
ppTypeApp n ts ppDN ppT = ppDN n <+> hsep (map ppT ts)
-------------------------------------------------------------------------------
-- Contexts
-------------------------------------------------------------------------------
ppLContext, ppLContextNoArrow :: Located (HsContext DocName) -> Bool -> Html
ppLContext = ppContext . unLoc
ppLContextNoArrow = ppContextNoArrow . unLoc
ppContextNoArrow :: HsContext DocName -> Bool -> Html
ppContextNoArrow [] _ = empty
ppContextNoArrow cxt unicode = pp_hs_context (map unLoc cxt) unicode
ppContextNoLocs :: [HsPred DocName] -> Bool -> Html
ppContextNoLocs [] _ = empty
ppContextNoLocs cxt unicode = pp_hs_context cxt unicode <+> darrow unicode
ppContext :: HsContext DocName -> Bool -> Html
ppContext cxt unicode = ppContextNoLocs (map unLoc cxt) unicode
pp_hs_context :: [HsPred DocName] -> Bool -> Html
pp_hs_context [] _ = empty
pp_hs_context [p] unicode = ppPred unicode p
pp_hs_context cxt unicode = parenList (map (ppPred unicode) cxt)
ppPred :: Bool -> HsPred DocName -> Html
ppPred unicode (HsClassP n ts) = ppAppNameTypes n (map unLoc ts) unicode
ppPred unicode (HsEqualP t1 t2) = ppLType unicode t1 <+> toHtml "~" <+> ppLType unicode t2
ppPred unicode (HsIParam (IPName n) t)
= toHtml "?" +++ ppDocName n <+> dcolon unicode <+> ppLType unicode t
-------------------------------------------------------------------------------
-- Class declarations
-------------------------------------------------------------------------------
ppClassHdr :: Bool -> Located [LHsPred DocName] -> DocName
-> [Located (HsTyVarBndr DocName)] -> [Located ([DocName], [DocName])]
-> Bool -> Html
ppClassHdr summ lctxt n tvs fds unicode =
keyword "class"
<+> (if not . null . unLoc $ lctxt then ppLContext lctxt unicode else empty)
<+> ppAppDocNameNames summ n (tyvarNames $ tvs)
<+> ppFds fds unicode
ppFds :: [Located ([DocName], [DocName])] -> Bool -> Html
ppFds fds unicode =
if null fds then noHtml else
char '|' <+> hsep (punctuate comma (map (fundep . unLoc) fds))
where
fundep (vars1,vars2) = hsep (map ppDocName vars1) <+> arrow unicode <+>
hsep (map ppDocName vars2)
ppShortClassDecl :: Bool -> LinksInfo -> TyClDecl DocName -> SrcSpan -> [(DocName, DocForDecl DocName)] -> Bool -> HtmlTable
ppShortClassDecl summary links (ClassDecl lctxt lname tvs fds sigs _ ats _) loc subdocs unicode =
if null sigs && null ats
then (if summary then declBox else topDeclBox links loc nm) hdr
else (if summary then declBox else topDeclBox links loc nm) (hdr <+> keyword "where")
</>
(
bodyBox <<
aboves
(
[ ppAssocType summary links doc at unicode | at <- ats
, let doc = lookupAnySubdoc (tcdName $ unL at) subdocs ] ++
[ ppFunSig summary links loc doc n typ unicode
| L _ (TypeSig (L _ n) (L _ typ)) <- sigs
, let doc = lookupAnySubdoc n subdocs ]
)
)
where
hdr = ppClassHdr summary lctxt (unLoc lname) tvs fds unicode
nm = unLoc lname
ppShortClassDecl _ _ _ _ _ _ = error "declaration type not supported by ppShortClassDecl"
ppClassDecl :: Bool -> LinksInfo -> [DocInstance DocName] -> SrcSpan
-> Maybe (Doc DocName) -> [(DocName, DocForDecl DocName)]
-> TyClDecl DocName -> Bool -> HtmlTable
ppClassDecl summary links instances loc mbDoc subdocs
decl@(ClassDecl lctxt lname ltyvars lfds lsigs _ ats _) unicode
| summary = ppShortClassDecl summary links decl loc subdocs unicode
| otherwise = classheader </> bodyBox << (classdoc </> body_ </> instancesBit)
where
classheader
| null lsigs = topDeclBox links loc nm (hdr unicode)
| otherwise = topDeclBox links loc nm (hdr unicode <+> keyword "where")
nm = unLoc $ tcdLName decl
hdr = ppClassHdr summary lctxt (unLoc lname) ltyvars lfds
classdoc = case mbDoc of
Nothing -> Html.emptyTable
Just d -> ndocBox (docToHtml d)
body_
| null lsigs, null ats = Html.emptyTable
| null ats = s8 </> methHdr </> bodyBox << methodTable
| otherwise = s8 </> atHdr </> bodyBox << atTable </>
s8 </> methHdr </> bodyBox << methodTable
methodTable =
abovesSep s8 [ ppFunSig summary links loc doc n typ unicode
| L _ (TypeSig (L _ n) (L _ typ)) <- lsigs
, let doc = lookupAnySubdoc n subdocs ]
atTable = abovesSep s8 $ [ ppAssocType summary links doc at unicode | at <- ats
, let doc = lookupAnySubdoc (tcdName $ unL at) subdocs ]
instId = collapseId (getName nm)
instancesBit
| null instances = Html.emptyTable
| otherwise
= s8 </> instHdr instId </>
tda [theclass "body"] <<
collapsed thediv instId (
spacedTable1 << aboves (map (ppDocInstance unicode) instances)
)
ppClassDecl _ _ _ _ _ _ _ _ = error "declaration type not supported by ppShortClassDecl"
-- | Print a possibly commented instance. The instance header is printed inside
-- an 'argBox'. The comment is printed to the right of the box in normal comment
-- style.
ppDocInstance :: Bool -> DocInstance DocName -> HtmlTable
ppDocInstance unicode (instHead, maybeDoc) =
argBox (ppInstHead unicode instHead) <-> maybeRDocBox maybeDoc
ppInstHead :: Bool -> InstHead DocName -> Html
ppInstHead unicode ([], n, ts) = ppAppNameTypes n ts unicode
ppInstHead unicode (ctxt, n, ts) = ppContextNoLocs ctxt unicode <+> ppAppNameTypes n ts unicode
lookupAnySubdoc :: (Eq name1) =>
name1 -> [(name1, DocForDecl name2)] -> DocForDecl name2
lookupAnySubdoc n subdocs = case lookup n subdocs of
Nothing -> noDocForDecl
Just docs -> docs
-- -----------------------------------------------------------------------------
-- Data & newtype declarations
-- TODO: print contexts
ppShortDataDecl :: Bool -> LinksInfo -> SrcSpan -> TyClDecl DocName -> Bool -> Html
ppShortDataDecl summary links loc dataDecl unicode
| [lcon] <- cons, ResTyH98 <- resTy =
ppDataHeader summary dataDecl unicode
<+> equals <+> ppShortConstr summary (unLoc lcon) unicode
| [] <- cons = ppDataHeader summary dataDecl unicode
| otherwise = vanillaTable << (
case resTy of
ResTyH98 -> dataHeader </>
tda [theclass "body"] << vanillaTable << (
aboves (zipWith doConstr ('=':repeat '|') cons)
)
ResTyGADT _ -> dataHeader </>
tda [theclass "body"] << vanillaTable << (
aboves (map doGADTConstr cons)
)
)
where
dataHeader =
(if summary then declBox else topDeclBox links loc docname)
((ppDataHeader summary dataDecl unicode) <+>
case resTy of ResTyGADT _ -> keyword "where"; _ -> empty)
doConstr c con = declBox (toHtml [c] <+> ppShortConstr summary (unLoc con) unicode)
doGADTConstr con = declBox (ppShortConstr summary (unLoc con) unicode)
docname = unLoc . tcdLName $ dataDecl
cons = tcdCons dataDecl
resTy = (con_res . unLoc . head) cons
ppDataDecl :: Bool -> LinksInfo -> [DocInstance DocName] ->
[(DocName, DocForDecl DocName)] ->
SrcSpan -> Maybe (Doc DocName) -> TyClDecl DocName -> Bool -> HtmlTable
ppDataDecl summary links instances subdocs loc mbDoc dataDecl unicode
| summary = declWithDoc summary links loc docname mbDoc
(ppShortDataDecl summary links loc dataDecl unicode)
| otherwise
= (if validTable then (</>) else const) header_ $
tda [theclass "body"] << vanillaTable << (
datadoc </>
constrBit </>
instancesBit
)
where
docname = unLoc . tcdLName $ dataDecl
cons = tcdCons dataDecl
resTy = (con_res . unLoc . head) cons
header_ = topDeclBox links loc docname (ppDataHeader summary dataDecl unicode
<+> whereBit)
whereBit
| null cons = empty
| otherwise = case resTy of
ResTyGADT _ -> keyword "where"
_ -> empty
constrTable
| any isRecCon cons = spacedTable5
| otherwise = spacedTable1
datadoc = case mbDoc of
Just doc -> ndocBox (docToHtml doc)
Nothing -> Html.emptyTable
constrBit
| null cons = Html.emptyTable
| otherwise = constrHdr </> (
tda [theclass "body"] << constrTable <<
aboves (map (ppSideBySideConstr subdocs unicode) cons)
)
instId = collapseId (getName docname)
instancesBit
| null instances = Html.emptyTable
| otherwise
= instHdr instId </>
tda [theclass "body"] <<
collapsed thediv instId (
spacedTable1 << aboves (map (ppDocInstance unicode) instances
)
)
validTable = isJust mbDoc || not (null cons) || not (null instances)
isRecCon :: Located (ConDecl a) -> Bool
isRecCon lcon = case con_details (unLoc lcon) of
RecCon _ -> True
_ -> False
ppShortConstr :: Bool -> ConDecl DocName -> Bool -> Html
ppShortConstr summary con unicode = case con_res con of
ResTyH98 -> case con_details con of
PrefixCon args -> header_ unicode +++ hsep (ppBinder summary occ : map (ppLParendType unicode) args)
RecCon fields -> header_ unicode +++ ppBinder summary occ <+>
doRecordFields fields
InfixCon arg1 arg2 -> header_ unicode +++
hsep [ppLParendType unicode arg1, ppBinder summary occ, ppLParendType unicode arg2]
ResTyGADT resTy -> case con_details con of
-- prefix & infix could use hsConDeclArgTys if it seemed to
-- simplify the code.
PrefixCon args -> doGADTCon args resTy
-- display GADT records with the new syntax,
-- Constr :: (Context) => { field :: a, field2 :: b } -> Ty (a, b)
-- (except each field gets its own line in docs, to match
-- non-GADT records)
RecCon fields -> ppBinder summary occ <+> dcolon unicode <+> hsep [
ppForAll forall ltvs lcontext unicode,
doRecordFields fields,
arrow unicode <+> ppLType unicode resTy ]
InfixCon arg1 arg2 -> doGADTCon [arg1, arg2] resTy
where
doRecordFields fields = braces (vanillaTable <<
aboves (map (ppShortField summary unicode) fields))
doGADTCon args resTy = ppBinder summary occ <+> dcolon unicode <+> hsep [
ppForAll forall ltvs lcontext unicode,
ppLType unicode (foldr mkFunTy resTy args) ]
header_ = ppConstrHdr forall tyVars context
occ = docNameOcc . unLoc . con_name $ con
ltvs = con_qvars con
tyVars = tyvarNames ltvs
lcontext = con_cxt con
context = unLoc (con_cxt con)
forall = con_explicit con
mkFunTy a b = noLoc (HsFunTy a b)
-- ppConstrHdr is for (non-GADT) existentials constructors' syntax
#if __GLASGOW_HASKELL__ == 612
ppConstrHdr :: HsExplicitForAll -> [Name] -> HsContext DocName -> Bool -> Html
#else
ppConstrHdr :: HsExplicitFlag -> [Name] -> HsContext DocName -> Bool -> Html
#endif
ppConstrHdr forall tvs ctxt unicode
= (if null tvs then noHtml else ppForall)
+++
(if null ctxt then noHtml else ppContextNoArrow ctxt unicode <+> darrow unicode +++ toHtml " ")
where
ppForall = case forall of
Explicit -> forallSymbol unicode <+> hsep (map ppName tvs) <+> toHtml ". "
Implicit -> empty
ppSideBySideConstr :: [(DocName, DocForDecl DocName)] -> Bool -> LConDecl DocName -> HtmlTable
ppSideBySideConstr subdocs unicode (L _ con) = case con_res con of
ResTyH98 -> case con_details con of
PrefixCon args ->
argBox (hsep ((header_ unicode +++ ppBinder False occ) : map (ppLParendType unicode) args))
<-> maybeRDocBox mbDoc
RecCon fields ->
argBox (header_ unicode +++ ppBinder False occ) <->
maybeRDocBox mbDoc
</>
doRecordFields fields
InfixCon arg1 arg2 ->
argBox (hsep [header_ unicode+++ppLParendType unicode arg1, ppBinder False occ, ppLParendType unicode arg2])
<-> maybeRDocBox mbDoc
ResTyGADT resTy -> case con_details con of
-- prefix & infix could also use hsConDeclArgTys if it seemed to
-- simplify the code.
PrefixCon args -> doGADTCon args resTy
cd@(RecCon fields) -> doGADTCon (hsConDeclArgTys cd) resTy
</> doRecordFields fields
InfixCon arg1 arg2 -> doGADTCon [arg1, arg2] resTy
where
doRecordFields fields =
(tda [theclass "body"] << spacedTable1 <<
aboves (map (ppSideBySideField subdocs unicode) fields))
doGADTCon args resTy = argBox (ppBinder False occ <+> dcolon unicode <+> hsep [
ppForAll forall ltvs (con_cxt con) unicode,
ppLType unicode (foldr mkFunTy resTy args) ]
) <-> maybeRDocBox mbDoc
header_ = ppConstrHdr forall tyVars context
occ = docNameOcc . unLoc . con_name $ con
ltvs = con_qvars con
tyVars = tyvarNames (con_qvars con)
context = unLoc (con_cxt con)
forall = con_explicit con
-- don't use "con_doc con", in case it's reconstructed from a .hi file,
-- or also because we want Haddock to do the doc-parsing, not GHC.
-- 'join' is in Maybe.
mbDoc = join $ fmap fst $ lookup (unLoc $ con_name con) subdocs
mkFunTy a b = noLoc (HsFunTy a b)
ppSideBySideField :: [(DocName, DocForDecl DocName)] -> Bool -> ConDeclField DocName -> HtmlTable
ppSideBySideField subdocs unicode (ConDeclField (L _ name) ltype _) =
argBox (ppBinder False (docNameOcc name)
<+> dcolon unicode <+> ppLType unicode ltype) <-> maybeRDocBox mbDoc
where
-- don't use cd_fld_doc for same reason we don't use con_doc above
mbDoc = join $ fmap fst $ lookup name subdocs
{-
ppHsFullConstr :: HsConDecl -> Html
ppHsFullConstr (HsConDecl _ nm tvs ctxt typeList doc) =
declWithDoc False doc (
hsep ((ppHsConstrHdr tvs ctxt +++
ppHsBinder False nm) : map ppHsBangType typeList)
)
ppHsFullConstr (HsRecDecl _ nm tvs ctxt fields doc) =
td << vanillaTable << (
case doc of
Nothing -> aboves [hdr, fields_html]
Just _ -> 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 :: Bool -> Bool -> ConDeclField DocName -> HtmlTable
ppShortField summary unicode (ConDeclField (L _ name) ltype _)
= tda [theclass "recfield"] << (
ppBinder summary (docNameOcc name)
<+> dcolon unicode <+> ppLType unicode ltype
)
{-
ppFullField :: HsFieldDecl -> Html
ppFullField (HsFieldDecl [n] ty doc)
= declWithDoc False doc (
ppHsBinder False n <+> dcolon <+> ppHsBangType ty
)
ppFullField _ = error "ppFullField"
expandField :: HsFieldDecl -> [HsFieldDecl]
expandField (HsFieldDecl ns ty doc) = [ HsFieldDecl [n] ty doc | n <- ns ]
-}
-- | Print the LHS of a data\/newtype declaration.
-- Currently doesn't handle 'data instance' decls or kind signatures
ppDataHeader :: Bool -> TyClDecl DocName -> Bool -> Html
ppDataHeader summary decl unicode
| not (isDataDecl decl) = error "ppDataHeader: illegal argument"
| otherwise =
-- newtype or data
(if tcdND decl == NewType then keyword "newtype" else keyword "data") <+>
-- context
ppLContext (tcdCtxt decl) unicode <+>
-- T a b c ..., or a :+: b
ppTyClBinderWithVars summary decl
-- ----------------------------------------------------------------------------
-- Types and contexts
ppKind :: Outputable a => a -> Html
ppKind k = toHtml $ showSDoc (ppr k)
{-
ppForAll Implicit _ lctxt = ppCtxtPart lctxt
ppForAll Explicit ltvs lctxt =
hsep (keyword "forall" : ppTyVars ltvs ++ [dot]) <+> ppCtxtPart lctxt
-}
ppBang :: HsBang -> Html
ppBang HsNoBang = empty
ppBang _ = toHtml "!" -- Unpacked args is an implementation detail,
tupleParens :: Boxity -> [Html] -> Html
tupleParens Boxed = parenList
tupleParens Unboxed = ubxParenList
{-
ppType :: HsType DocName -> Html
ppType t = case t of
t@(HsForAllTy expl ltvs lcontext ltype) -> ppForAllTy t <+> ppLType ltype
HsTyVar n -> ppDocName n
HsBangTy HsStrict lt -> toHtml "!" <+> ppLType lt
HsBangTy HsUnbox lt -> toHtml "!!" <+> ppLType lt
HsAppTy a b -> ppLType a <+> ppLType b
HsFunTy a b -> hsep [ppLType a, toHtml "->", ppLType b]
HsListTy t -> brackets $ ppLType t
HsPArrTy t -> toHtml "[:" +++ ppLType t +++ toHtml ":]"
HsTupleTy Boxed ts -> parenList $ map ppLType ts
HsTupleTy Unboxed ts -> ubxParenList $ map ppLType ts
HsOpTy a n b -> ppLType a <+> ppLDocName n <+> ppLType b
HsParTy t -> parens $ ppLType t
HsNumTy n -> toHtml (show n)
HsPredTy p -> ppPred p
HsKindSig t k -> hsep [ppLType t, dcolon, ppKind k]
HsSpliceTy _ -> error "ppType"
HsDocTy t _ -> ppLType t
-}
--------------------------------------------------------------------------------
-- Rendering of HsType
--------------------------------------------------------------------------------
pREC_TOP, pREC_FUN, pREC_OP, pREC_CON :: Int
pREC_TOP = (0 :: Int) -- type in ParseIface.y in GHC
pREC_FUN = (1 :: Int) -- btype in ParseIface.y in GHC
-- Used for LH arg of (->)
pREC_OP = (2 :: Int) -- Used for arg of any infix operator
-- (we don't keep their fixities around)
pREC_CON = (3 :: Int) -- Used for arg of type applicn:
-- always parenthesise unless atomic
maybeParen :: Int -- Precedence of context
-> Int -- Precedence of top-level operator
-> Html -> Html -- Wrap in parens if (ctxt >= op)
maybeParen ctxt_prec op_prec p | ctxt_prec >= op_prec = parens p
| otherwise = p
ppLType, ppLParendType, ppLFunLhType :: Bool -> Located (HsType DocName) -> Html
ppLType unicode y = ppType unicode (unLoc y)
ppLParendType unicode y = ppParendType unicode (unLoc y)
ppLFunLhType unicode y = ppFunLhType unicode (unLoc y)
ppType, ppParendType, ppFunLhType :: Bool -> HsType DocName -> Html
ppType unicode ty = ppr_mono_ty pREC_TOP ty unicode
ppParendType unicode ty = ppr_mono_ty pREC_CON ty unicode
ppFunLhType unicode ty = ppr_mono_ty pREC_FUN ty unicode
-- Drop top-level for-all type variables in user style
-- since they are implicit in Haskell
#if __GLASGOW_HASKELL__ == 612
ppForAll :: HsExplicitForAll -> [Located (HsTyVarBndr DocName)]
#else
ppForAll :: HsExplicitFlag -> [Located (HsTyVarBndr DocName)]
#endif
-> Located (HsContext DocName) -> Bool -> Html
ppForAll expl tvs cxt unicode
| show_forall = forall_part <+> ppLContext cxt unicode
| otherwise = ppLContext cxt unicode
where
show_forall = not (null tvs) && is_explicit
is_explicit = case expl of {Explicit -> True; Implicit -> False}
forall_part = hsep (forallSymbol unicode : ppTyVars tvs) +++ dot
ppr_mono_lty :: Int -> LHsType DocName -> Bool -> Html
ppr_mono_lty ctxt_prec ty unicode = ppr_mono_ty ctxt_prec (unLoc ty) unicode
ppr_mono_ty :: Int -> HsType DocName -> Bool -> Html
ppr_mono_ty ctxt_prec (HsForAllTy expl tvs ctxt ty) unicode
= maybeParen ctxt_prec pREC_FUN $
hsep [ppForAll expl tvs ctxt unicode, ppr_mono_lty pREC_TOP ty unicode]
ppr_mono_ty _ (HsBangTy b ty) u = ppBang b +++ ppLParendType u ty
ppr_mono_ty _ (HsTyVar name) _ = ppDocName name
ppr_mono_ty ctxt_prec (HsFunTy ty1 ty2) u = ppr_fun_ty ctxt_prec ty1 ty2 u
ppr_mono_ty _ (HsTupleTy con tys) u = tupleParens con (map (ppLType u) tys)
ppr_mono_ty _ (HsKindSig ty kind) u = parens (ppr_mono_lty pREC_TOP ty u <+> dcolon u <+> ppKind kind)
ppr_mono_ty _ (HsListTy ty) u = brackets (ppr_mono_lty pREC_TOP ty u)
ppr_mono_ty _ (HsPArrTy ty) u = pabrackets (ppr_mono_lty pREC_TOP ty u)
ppr_mono_ty _ (HsPredTy p) u = parens (ppPred u p)
ppr_mono_ty _ (HsNumTy n) _ = toHtml (show n) -- generics only
ppr_mono_ty _ (HsSpliceTy {}) _ = error "ppr_mono_ty HsSpliceTy"
#if __GLASGOW_HASKELL__ == 612
ppr_mono_ty _ (HsSpliceTyOut {}) _ = error "ppr_mono_ty HsQuasiQuoteTy"
#else
ppr_mono_ty _ (HsQuasiQuoteTy {}) _ = error "ppr_mono_ty HsQuasiQuoteTy"
#endif
ppr_mono_ty _ (HsRecTy {}) _ = error "ppr_mono_ty HsRecTy"
ppr_mono_ty ctxt_prec (HsAppTy fun_ty arg_ty) unicode
= maybeParen ctxt_prec pREC_CON $
hsep [ppr_mono_lty pREC_FUN fun_ty unicode, ppr_mono_lty pREC_CON arg_ty unicode]
ppr_mono_ty ctxt_prec (HsOpTy ty1 op ty2) unicode
= maybeParen ctxt_prec pREC_FUN $
ppr_mono_lty pREC_OP ty1 unicode <+> ppr_op <+> ppr_mono_lty pREC_OP ty2 unicode
where
ppr_op = if not (isSymOcc occName) then quote (ppLDocName op) else ppLDocName op
occName = docNameOcc . unLoc $ op
ppr_mono_ty ctxt_prec (HsParTy ty) unicode
-- = parens (ppr_mono_lty pREC_TOP ty)
= ppr_mono_lty ctxt_prec ty unicode
ppr_mono_ty ctxt_prec (HsDocTy ty _) unicode
= ppr_mono_lty ctxt_prec ty unicode
ppr_fun_ty :: Int -> LHsType DocName -> LHsType DocName -> Bool -> Html
ppr_fun_ty ctxt_prec ty1 ty2 unicode
= let p1 = ppr_mono_lty pREC_FUN ty1 unicode
p2 = ppr_mono_lty pREC_TOP ty2 unicode
in
maybeParen ctxt_prec pREC_FUN $
hsep [p1, arrow unicode <+> p2]
-- ----------------------------------------------------------------------------
-- Names
ppOccName :: OccName -> Html
ppOccName = toHtml . occNameString
ppRdrName :: RdrName -> Html
ppRdrName = ppOccName . rdrNameOcc
ppLDocName :: Located DocName -> Html
ppLDocName (L _ d) = ppDocName d
ppDocName :: DocName -> Html
ppDocName (Documented name mdl) =
linkIdOcc mdl (Just occName) << ppOccName occName
where occName = nameOccName name
ppDocName (Undocumented name) = toHtml (getOccString name)
linkTarget :: OccName -> Html
linkTarget n = namedAnchor (nameAnchorId n) << toHtml ""
ppName :: Name -> Html
ppName name = toHtml (getOccString name)
ppBinder :: Bool -> OccName -> Html
-- The Bool indicates whether we are generating the summary, in which case
-- the binder will be a link to the full definition.
ppBinder True n = linkedAnchor (nameAnchorId n) << ppBinder' n
ppBinder False n = linkTarget n +++ bold << ppBinder' n
ppBinder' :: OccName -> Html
ppBinder' n
| isVarSym n = parens $ ppOccName n
| otherwise = ppOccName n
linkId :: Module -> Maybe Name -> Html -> Html
linkId mdl mbName = linkIdOcc mdl (fmap nameOccName mbName)
linkIdOcc :: Module -> Maybe OccName -> Html -> Html
linkIdOcc mdl mbName = anchor ! [href uri]
where
uri = case mbName of
Nothing -> moduleUrl mdl
Just name -> moduleNameUrl mdl name
ppModule :: Module -> String -> Html
ppModule mdl ref = anchor ! [href ((moduleHtmlFile mdl) ++ ref)]
<< toHtml (moduleString mdl)
-- -----------------------------------------------------------------------------
-- * Doc Markup
parHtmlMarkup :: (a -> Html) -> (a -> Bool) -> DocMarkup a Html
parHtmlMarkup ppId isTyCon = Markup {
markupParagraph = paragraph,
markupEmpty = toHtml "",
markupString = toHtml,
markupAppend = (+++),
markupIdentifier = tt . ppId . choose,
markupModule = \m -> let (mdl,ref) = break (=='#') m in ppModule (mkModuleNoPackage mdl) ref,
markupEmphasis = emphasize . toHtml,
markupMonospaced = tt . toHtml,
markupUnorderedList = ulist . concatHtml . map (li <<),
markupPic = \path -> image ! [src path],
markupOrderedList = olist . concatHtml . map (li <<),
markupDefList = dlist . concatHtml . map markupDef,
markupCodeBlock = pre,
markupURL = \url -> anchor ! [href url] << toHtml url,
markupAName = \aname -> namedAnchor aname << toHtml "",
markupExample = examplesToHtml
}
where
-- If an id can refer to multiple things, we give precedence to type
-- constructors. This should ideally be done during renaming from RdrName
-- to Name, but since we will move this process from GHC into Haddock in
-- the future, we fix it here in the meantime.
-- TODO: mention this rule in the documentation.
choose [] = error "empty identifier list in HsDoc"
choose [x] = x
choose (x:y:_)
| isTyCon x = x
| otherwise = y
examplesToHtml l = (pre $ concatHtml $ map exampleToHtml l) ! [theclass "screen"]
exampleToHtml (Example expression result) = htmlExample
where
htmlExample = htmlPrompt +++ htmlExpression +++ (toHtml $ unlines result)
htmlPrompt = (thecode . toHtml $ "ghci> ") ! [theclass "prompt"]
htmlExpression = (strong . thecode . toHtml $ expression ++ "\n") ! [theclass "userinput"]
markupDef :: (HTML a, HTML b) => (a, b) -> Html
markupDef (a,b) = dterm << a +++ ddef << b
htmlMarkup :: DocMarkup DocName Html
htmlMarkup = parHtmlMarkup ppDocName (isTyConName . getName)
htmlOrigMarkup :: DocMarkup Name Html
htmlOrigMarkup = parHtmlMarkup ppName isTyConName
htmlRdrMarkup :: DocMarkup RdrName Html
htmlRdrMarkup = parHtmlMarkup ppRdrName isRdrTc
-- If the doc is a single paragraph, don't surround it with <P> (this causes
-- ugly extra whitespace with some browsers).
docToHtml :: Doc DocName -> Html
docToHtml doc = markup htmlMarkup (unParagraph (markup htmlCleanup doc))
origDocToHtml :: Doc Name -> Html
origDocToHtml doc = markup htmlOrigMarkup (unParagraph (markup htmlCleanup doc))
rdrDocToHtml :: Doc RdrName -> Html
rdrDocToHtml doc = markup htmlRdrMarkup (unParagraph (markup htmlCleanup doc))
-- If there is a single paragraph, then surrounding it with <P>..</P>
-- can add too much whitespace in some browsers (eg. IE). However if
-- we have multiple paragraphs, then we want the extra whitespace to
-- separate them. So we catch the single paragraph case and transform it
-- here.
unParagraph :: Doc a -> Doc a
unParagraph (DocParagraph d) = d
--NO: This eliminates line breaks in the code block: (SDM, 6/5/2003)
--unParagraph (DocCodeBlock d) = (DocMonospaced d)
unParagraph doc = doc
htmlCleanup :: DocMarkup a (Doc a)
htmlCleanup = idMarkup {
markupUnorderedList = DocUnorderedList . map unParagraph,
markupOrderedList = DocOrderedList . map unParagraph
}
-- -----------------------------------------------------------------------------
-- * Misc
hsep :: [Html] -> Html
hsep [] = noHtml
hsep htmls = foldr1 (\a b -> a+++" "+++b) htmls
infixr 8 <+>, <++>
(<+>) :: Html -> Html -> Html
a <+> b = Html (getHtmlElements (toHtml a) ++ HtmlString " ": getHtmlElements (toHtml b))
(<++>) :: Html -> Html -> Html
a <++> b = a +++ spaceHtml +++ b
keyword :: String -> Html
keyword s = thespan ! [theclass "keyword"] << toHtml s
equals, comma :: Html
equals = char '='
comma = char ','
char :: Char -> Html
char c = toHtml [c]
empty :: Html
empty = noHtml
quote :: Html -> Html
quote h = char '`' +++ h +++ '`'
parens, brackets, pabrackets, braces :: Html -> Html
parens h = char '(' +++ h +++ char ')'
brackets h = char '[' +++ h +++ char ']'
pabrackets h = toHtml "[:" +++ h +++ toHtml ":]"
braces h = char '{' +++ h +++ char '}'
punctuate :: Html -> [Html] -> [Html]
punctuate _ [] = []
punctuate h (d0:ds) = go d0 ds
where
go d [] = [d]
go d (e:es) = (d +++ h) : go e es
abovesSep :: HtmlTable -> [HtmlTable] -> HtmlTable
abovesSep _ [] = Html.emptyTable
abovesSep h (d0:ds) = go d0 ds
where
go d [] = d
go d (e:es) = d </> h </> go e es
parenList :: [Html] -> Html
parenList = parens . hsep . punctuate comma
ubxParenList :: [Html] -> Html
ubxParenList = ubxparens . hsep . punctuate comma
ubxparens :: Html -> Html
ubxparens h = toHtml "(#" +++ h +++ toHtml "#)"
{-
text :: Html
text = strAttr "TEXT"
-}
-- a box for displaying code
declBox :: Html -> HtmlTable
declBox html = tda [theclass "decl"] << html
-- a box for top level documented names
-- it adds a source and wiki link at the right hand side of the box
topDeclBox :: LinksInfo -> SrcSpan -> DocName -> Html -> HtmlTable
topDeclBox ((_,_,Nothing), (_,_,Nothing)) _ _ html = declBox html
topDeclBox ((_,_,maybe_source_url), (_,_,maybe_wiki_url))
loc name html =
tda [theclass "topdecl"] <<
( table ! [theclass "declbar"] <<
((tda [theclass "declname"] << html)
<-> srcLink
<-> wikiLink)
)
where srcLink =
case maybe_source_url of
Nothing -> Html.emptyTable
Just url -> tda [theclass "declbut"] <<
let url' = spliceURL (Just fname) (Just origMod)
(Just n) (Just loc) url
in anchor ! [href url'] << toHtml "Source"
wikiLink =
case maybe_wiki_url of
Nothing -> Html.emptyTable
Just url -> tda [theclass "declbut"] <<
let url' = spliceURL (Just fname) (Just mdl)
(Just n) (Just loc) url
in anchor ! [href url'] << toHtml "Comments"
-- For source links, we want to point to the original module,
-- because only that will have the source.
-- TODO: do something about type instances. They will point to
-- the module defining the type family, which is wrong.
origMod = nameModule n
-- Name must be documented, otherwise we wouldn't get here
Documented n mdl = name
fname = unpackFS (srcSpanFile loc)
-- a box for displaying an 'argument' (some code which has text to the
-- right of it). Wrapping is not allowed in these boxes, whereas it is
-- in a declBox.
argBox :: Html -> HtmlTable
argBox html = tda [theclass "arg"] << 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 DocName) -> HtmlTable
maybeRDocBox Nothing = rdocBox (noHtml)
maybeRDocBox (Just doc) = rdocBox (docToHtml doc)
-- a box for the buttons at the top of the page
topButBox :: Html -> HtmlTable
topButBox html = tda [theclass "topbut"] << html
bodyBox :: Html -> HtmlTable
bodyBox html = tda [theclass "body"] << vanillaTable << html
-- a vanilla table has width 100%, no border, no padding, no spacing
-- a narrow table is the same but without width 100%.
vanillaTable, vanillaTable2, narrowTable :: Html -> Html
vanillaTable = table ! [theclass "vanilla", cellspacing 0, cellpadding 0]
vanillaTable2 = table ! [theclass "vanilla2", cellspacing 0, cellpadding 0]
narrowTable = table ! [theclass "narrow", cellspacing 0, cellpadding 0]
spacedTable1, spacedTable5 :: Html -> Html
spacedTable1 = table ! [theclass "vanilla", cellspacing 1, cellpadding 0]
spacedTable5 = table ! [theclass "vanilla", cellspacing 5, cellpadding 0]
constrHdr, methHdr, atHdr :: HtmlTable
constrHdr = tda [ theclass "section4" ] << toHtml "Constructors"
methHdr = tda [ theclass "section4" ] << toHtml "Methods"
atHdr = tda [ theclass "section4" ] << toHtml "Associated Types"
instHdr :: String -> HtmlTable
instHdr id_ =
tda [ theclass "section4" ] << (collapsebutton id_ +++ toHtml " Instances")
dcolon, arrow, darrow, forallSymbol :: Bool -> Html
dcolon unicode = toHtml (if unicode then "∷" else "::")
arrow unicode = toHtml (if unicode then "→" else "->")
darrow unicode = toHtml (if unicode then "⇒" else "=>")
forallSymbol unicode = if unicode then toHtml "∀" else keyword "forall"
dot :: Html
dot = toHtml "."
s8, s15 :: HtmlTable
s8 = tda [ theclass "s8" ] << noHtml
s15 = tda [ theclass "s15" ] << noHtml
-- | Generate a named anchor
--
-- This actually generates two anchor tags, one with the name unescaped, and one
-- with the name URI-escaped. This is needed because Opera 9.52 (and later
-- versions) needs the name to be unescaped, while IE 7 needs it to be escaped.
--
namedAnchor :: String -> Html -> Html
namedAnchor n = (anchor ! [Html.name n]) . (anchor ! [Html.name (escapeStr n)])
--
-- A section of HTML which is collapsible via a +/- button.
--
-- TODO: Currently the initial state is non-collapsed. Change the 'minusFile'
-- below to a 'plusFile' and the 'display:block;' to a 'display:none;' when we
-- use cookies from JavaScript to have a more persistent state.
collapsebutton :: String -> Html
collapsebutton id_ =
image ! [ src minusFile, theclass "coll", onclick ("toggle(this,'" ++ id_ ++ "')"), alt "show/hide" ]
collapsed :: (HTML a) => (Html -> Html) -> String -> a -> Html
collapsed fn id_ html =
fn ! [identifier id_, thestyle "display:block;"] << html
-- A quote is a valid part of a Haskell identifier, but it would interfere with
-- the ECMA script string delimiter used in collapsebutton above.
collapseId :: Name -> String
collapseId nm = "i:" ++ escapeStr (getOccString nm)
linkedAnchor :: String -> Html -> Html
linkedAnchor frag = anchor ! [href hr_]
where hr_ | null frag = ""
| otherwise = '#': escapeStr frag
documentCharacterEncoding :: Html
documentCharacterEncoding =
meta ! [httpequiv "Content-Type", content "text/html; charset=UTF-8"]
styleSheet :: Html
styleSheet =
thelink ! [href cssFile, rel "stylesheet", thetype "text/css"]