{-# LANGUAGE OverloadedStrings #-}
-- | Functions specific to emacs tags.
module FastTags.Emacs (format, formatTag) where
import Data.Monoid ((<>))
import qualified Data.Text as Text
import Data.Text (Text)

import qualified FastTags.Tag as Tag
import qualified FastTags.Token as Token
import qualified FastTags.Util as Util
import qualified FastTags.Vim as Vim


format :: Int -> [Token.Pos Tag.TagVal] -> [Text]
format maxSeparation = map (uncurry formatFileTags)
    . map (fmap (dropAdjacent maxSeparation))
    . Util.groupOnKey (Token.posFile . Token.posOf)

-- | Like 'Vim.dropAdjacent', but since emacs isn't incremental it deals with
-- TagVals, not tag file lines.  Also the tags are already grouped by file.
dropAdjacent :: Int -> [Token.Pos Tag.TagVal] -> [Token.Pos Tag.TagVal]
dropAdjacent maxSeparation = concatMap dropInName. Util.groupOn nameOf
    where
    nameOf = Tag.tvName . Token.valOf
    lineOf = Token.unLine . Token.posLine . Token.posOf
    dropInName tag@[_] = tag
    dropInName tags = Vim.dropAdjacentInFile lineOf maxSeparation tags

formatFileTags :: FilePath -> [Token.Pos Tag.TagVal] -> Text
formatFileTags file tags = Text.concat
    [ "\x0c\n", Text.pack file, ","
    , showt (Text.length tagsText), "\n", tagsText
    ]
    where tagsText = Text.unlines $ map formatTag tags

-- https://en.wikipedia.org/wiki/Ctags#Etags_2
formatTag :: Token.Pos Tag.TagVal -> Text
formatTag (Token.Pos pos tag) = Text.concat
    [ text, "\x7f"
    , matching, "\x01"
    , linenum , ",", offset
    ]
    where
    text = (Token.posPrefix pos) <> (Token.posSuffix pos)
    matching = Tag.tvName tag
    linenum = showt $ Token.unLine (Token.posLine pos)
    offset = showt $ Token.unOffset (Token.posOffset pos) - (Text.length $ Token.posPrefix pos)

showt :: Show a => a -> Text
showt = Text.pack . show