module Yi.Tag
(
lookupTag,
importTagTable,
hintTags,
completeTag,
Tag,
TagTable(..),
getTags,
setTags,
resetTags,
getTagsFileList,
setTagsFileList
)
where
import Prelude (map, words, lines, readFile, reads)
import Yi.Prelude
import Yi.Editor
import Yi.Dynamic
import qualified Data.ByteString as BS
import qualified Data.ByteString.UTF8 as BS8
import Data.Maybe (mapMaybe)
import Data.List (isPrefixOf)
import System.FilePath (takeFileName, takeDirectory, FilePath, (</>))
import System.FriendlyPath
import Data.Map (Map, fromList, lookup, keys)
import Data.List.Split (splitOn)
import qualified Data.Trie as Trie
import Data.Binary
import Data.DeriveTH
newtype Tags = Tags (Maybe TagTable) deriving Typeable
instance Initializable Tags where
initial = Tags Nothing
newtype TagsFileList = TagsFileList [FilePath] deriving Typeable
instance Initializable TagsFileList where
initial = TagsFileList ["tags"]
type Tag = String
data TagTable = TagTable { tagFileName :: FilePath
, tagBaseDir :: FilePath
, tagFileMap :: Map Tag (FilePath, Int)
, tagTrie :: Trie.Trie
} deriving Typeable
lookupTag :: Tag -> TagTable -> Maybe (FilePath, Int)
lookupTag tag tagTable = do
(file, line) <- Data.Map.lookup tag $ tagFileMap tagTable
return $ (tagBaseDir tagTable </> file, line)
readCTags :: String -> Map Tag (FilePath, Int)
readCTags =
fromList . mapMaybe (parseTagLine . words) . lines
where parseTagLine (tag:tagfile:lineno:_) =
if "!_TAG_" `isPrefixOf` tag then Nothing
else Just (tag, (tagfile, fst . head . reads $ lineno))
parseTagLine _ = Nothing
importTagTable :: FilePath -> IO TagTable
importTagTable filename = do
friendlyName <- expandTilda filename
tagStr <- fmap BS8.toString $ BS.readFile friendlyName
let ctags = readCTags tagStr
return $ TagTable { tagFileName = takeFileName filename,
tagBaseDir = takeDirectory filename,
tagFileMap = ctags,
tagTrie = Trie.fromList $ keys ctags
}
hintTags :: TagTable -> String -> [String]
hintTags tags prefix = map (prefix ++) $ Trie.possibleSuffixes prefix $ tagTrie tags
completeTag :: TagTable -> String -> String
completeTag tags prefix = prefix ++ (Trie.certainSuffix prefix $ tagTrie tags)
setTags :: TagTable -> EditorM ()
setTags = setDynamic . Tags . Just
resetTags :: EditorM ()
resetTags = setDynamic $ Tags Nothing
getTags :: EditorM (Maybe TagTable)
getTags = do
Tags t <- getDynamic
return t
setTagsFileList :: String -> EditorM ()
setTagsFileList fps = do
resetTags
setDynamic $ TagsFileList (splitOn "," fps)
getTagsFileList :: EditorM [FilePath]
getTagsFileList = do
TagsFileList fps <- getDynamic
return fps
$(derives [makeBinary] [''Tags, ''TagTable, ''TagsFileList])
instance YiVariable Tags
instance YiVariable TagsFileList