{-# 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