{-# 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 :: Int -> [Pos TagVal] -> [Text]
format Int
maxSeparation = ((FilePath, [Pos TagVal]) -> Text)
-> [(FilePath, [Pos TagVal])] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map ((FilePath -> [Pos TagVal] -> Text)
-> (FilePath, [Pos TagVal]) -> Text
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry FilePath -> [Pos TagVal] -> Text
formatFileTags)
    ([(FilePath, [Pos TagVal])] -> [Text])
-> ([Pos TagVal] -> [(FilePath, [Pos TagVal])])
-> [Pos TagVal]
-> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((FilePath, [Pos TagVal]) -> (FilePath, [Pos TagVal]))
-> [(FilePath, [Pos TagVal])] -> [(FilePath, [Pos TagVal])]
forall a b. (a -> b) -> [a] -> [b]
map (([Pos TagVal] -> [Pos TagVal])
-> (FilePath, [Pos TagVal]) -> (FilePath, [Pos TagVal])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int -> [Pos TagVal] -> [Pos TagVal]
dropAdjacent Int
maxSeparation))
    ([(FilePath, [Pos TagVal])] -> [(FilePath, [Pos TagVal])])
-> ([Pos TagVal] -> [(FilePath, [Pos TagVal])])
-> [Pos TagVal]
-> [(FilePath, [Pos TagVal])]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Pos TagVal -> FilePath)
-> [Pos TagVal] -> [(FilePath, [Pos TagVal])]
forall key a. Ord key => (a -> key) -> [a] -> [(key, [a])]
Util.groupOnKey (SrcPos -> FilePath
Token.posFile (SrcPos -> FilePath)
-> (Pos TagVal -> SrcPos) -> Pos TagVal -> FilePath
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pos TagVal -> SrcPos
forall a. Pos a -> SrcPos
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 :: Int -> [Pos TagVal] -> [Pos TagVal]
dropAdjacent Int
maxSeparation = ([Pos TagVal] -> [Pos TagVal]) -> [[Pos TagVal]] -> [Pos TagVal]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap [Pos TagVal] -> [Pos TagVal]
forall a. [Pos a] -> [Pos a]
dropInName([[Pos TagVal]] -> [Pos TagVal])
-> ([Pos TagVal] -> [[Pos TagVal]]) -> [Pos TagVal] -> [Pos TagVal]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Pos TagVal -> Text) -> [Pos TagVal] -> [[Pos TagVal]]
forall k a. Eq k => (a -> k) -> [a] -> [[a]]
Util.groupOn Pos TagVal -> Text
nameOf
    where
    nameOf :: Pos TagVal -> Text
nameOf = TagVal -> Text
Tag.tvName (TagVal -> Text) -> (Pos TagVal -> TagVal) -> Pos TagVal -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pos TagVal -> TagVal
forall a. Pos a -> a
Token.valOf
    lineOf :: Pos a -> Int
lineOf = Line -> Int
Token.unLine (Line -> Int) -> (Pos a -> Line) -> Pos a -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SrcPos -> Line
Token.posLine (SrcPos -> Line) -> (Pos a -> SrcPos) -> Pos a -> Line
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pos a -> SrcPos
forall a. Pos a -> SrcPos
Token.posOf
    dropInName :: [Pos a] -> [Pos a]
dropInName tag :: [Pos a]
tag@[Pos a
_] = [Pos a]
tag
    dropInName [Pos a]
tags = (Pos a -> Int) -> Int -> [Pos a] -> [Pos a]
forall a. (a -> Int) -> Int -> [a] -> [a]
Vim.dropAdjacentInFile Pos a -> Int
forall a. Pos a -> Int
lineOf Int
maxSeparation [Pos a]
tags

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

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

showt :: Show a => a -> Text
showt :: a -> Text
showt = FilePath -> Text
Text.pack (FilePath -> Text) -> (a -> FilePath) -> a -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> FilePath
forall a. Show a => a -> FilePath
show