packages feed

static-ls-0.1.2: src/StaticLS/HI.hs

{-# LANGUAGE CPP #-}

module StaticLS.HI (
    getDocs,
    getDocsBatch,
    renderNameDocs,
    NameDocs (..),
)
where

import qualified Data.IntMap as IntMap
import qualified Data.Map as Map
import Data.Maybe
import Data.Text as T
import qualified GHC
import qualified GHC.Plugins as GHC
import qualified GHC.Types.Unique.Map as GHC
import StaticLS.SDoc

data NameDocs = NameDocs
    { declComment :: Maybe [GHC.HsDoc GHC.GhcRn]
    , argComments :: IntMap.IntMap (GHC.HsDoc GHC.GhcRn)
    }

instance GHC.Outputable NameDocs where
    ppr nameDoc = GHC.ppr nameDoc.declComment

renderNameDocs :: NameDocs -> Text
renderNameDocs nameDocs =
    T.drop 1 $ -- Drop the leading space from haddock comments
        maybe "" (T.concat . fmap showGhc) nameDocs.declComment

getDocsBatch :: [GHC.Name] -> GHC.ModIface -> [NameDocs]
getDocsBatch names iface =
    case GHC.mi_docs iface of
        Nothing -> []
        Just
            GHC.Docs
                { GHC.docs_decls = decls
                , GHC.docs_args = args
                } ->
                -- Lifted out compared to `getDocs` - probably slightly
                -- more efficient though the compiler may just optimize this
                let declMap = uniqNameMapToMap decls
                    argsMap = uniqNameMapToMap args
                 in ( \name ->
                        NameDocs
                            { declComment =
                                Map.lookup (GHC.nameStableString name) declMap
                            , argComments =
                                fromMaybe mempty $
                                    Map.lookup (GHC.nameStableString name) argsMap
                            }
                    )
                        <$> names

getDocs :: GHC.Name -> GHC.ModIface -> Maybe NameDocs
getDocs name iface =
    case GHC.mi_docs iface of
        Nothing -> Nothing
        Just
            GHC.Docs
                { GHC.docs_decls = decls
                , GHC.docs_args = args
                } ->
                Just $
                    NameDocs
                        { declComment = normalizeNameLookup name decls
                        , argComments = fromMaybe mempty $ normalizeNameLookup name args
                        }

normalizeNameLookup :: GHC.Name -> GHC.UniqMap GHC.Name v -> Maybe v
normalizeNameLookup name uMap =
    Map.lookup (GHC.nameStableString name) (uniqNameMapToMap uMap)

uniqNameMapToMap :: GHC.UniqMap GHC.Name v -> Map.Map String v
uniqNameMapToMap =
    Map.fromList
        . fmap stringifyNameKeys
        . IntMap.elems
        . GHC.ufmToIntMap
        . getUniqMap
  where
    stringifyNameKeys (nameKey, v) = (GHC.nameStableString nameKey, v)

getUniqMap :: GHC.UniqMap k a -> GHC.UniqFM k (k, a)
#if MIN_VERSION_base(4,18,0)
getUniqMap = GHC.getUniqMap
#else
getUniqMap (GHC.UniqMap m) = m
#endif