{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Plugin.GhcTags.Tag
(
Tag (..)
, compareTags
, tagFilePath
, TagName (..)
, TagFile (..)
, TagKind (..)
, GhcKind (..)
, charToGhcKind
, ghcKindToChar
, TagField (..)
, ghcTagToTag
, combineTags
) where
import Data.Function (on)
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import FastString ( FastString (..)
)
import SrcLoc ( SrcSpan (..)
, srcSpanFile
, srcSpanStartLine
)
import Plugin.GhcTags.Generate
( GhcTag (..)
, GhcKind (..)
, TagField (..)
, charToGhcKind
, ghcKindToChar
)
newtype TagName = TagName { getTagName :: Text }
deriving (Eq, Ord, Show)
newtype TagFile = TagFile { getTagFile :: String }
deriving (Eq, Ord, Show)
tagFilePath :: Tag -> FilePath
tagFilePath = getTagFile . tagFile
data TagKind
= GhcKind !GhcKind
| CharKind !Char
| NoKind
deriving (Eq, Ord, Show)
data Tag = Tag
{ tagName :: !TagName
, tagKind :: !TagKind
, tagFile :: !TagFile
, tagAddr :: !(Either Int Text)
, tagFields :: ![TagField]
}
deriving (Eq, Show)
compareTags :: Tag -> Tag -> Ordering
compareTags t0 t1 | on (/=) tagName t0 t1 = on compare tagName t0 t1
| on (/=) getTkClass t0 t1 = on compare getTkClass t0 t1
| on (/=) tagFile t0 t1 = on compare tagFile t0 t1
| on (/=) tagAddr t0 t1 = on compare tagAddr t0 t1
| on (/=) tagKind t0 t1 = on compare tagKind t0 t1
| otherwise = EQ
where
getTkClass :: Tag -> Maybe GhcKind
getTkClass t = case tagKind t of
GhcKind TkTypeClass -> Just TkTypeClass
GhcKind TkTypeClassInstance -> Just TkTypeClassInstance
GhcKind TkTypeFamily -> Just TkTypeFamily
GhcKind TkTypeFamilyInstance -> Just TkTypeFamilyInstance
GhcKind TkDataTypeFamily -> Just TkDataTypeFamily
GhcKind TkDataTypeFamilyInstance -> Just TkDataTypeFamilyInstance
_ -> Nothing
ghcTagToTag :: GhcTag -> Maybe Tag
ghcTagToTag GhcTag { gtSrcSpan, gtTag, gtKind, gtFields } =
case gtSrcSpan of
UnhelpfulSpan {} -> Nothing
RealSrcSpan realSrcSpan ->
Just $ Tag { tagName = TagName (Text.decodeUtf8 $ fs_bs gtTag)
, tagFile = TagFile (Text.unpack $ Text.decodeUtf8 $ fs_bs (srcSpanFile realSrcSpan))
, tagAddr = Left (srcSpanStartLine realSrcSpan)
, tagKind = GhcKind gtKind
, tagFields = gtFields
}
combineTags :: [Tag] -> [Tag] -> [Tag]
combineTags [] ts1 = ts1
combineTags ts0@(t : _) ts1 = go ts0 ts1
where
modPath = tagFilePath t
go as@(a : as') bs@(b : bs')
| tagFilePath b == modPath = go as bs'
| otherwise = case a `compareTags` b of
LT -> a : go as' bs
EQ -> a : go as' bs'
GT -> b : go as bs'
go [] bs = filter (\b -> tagFilePath b /= modPath) bs
go as [] = as
{-# INLINE go #-}