module Yi.Tag
(
lookupTag,
importTagTable,
hintTags,
completeTag,
Tag,
TagTable(..),
getTags,
setTags,
resetTags,
getTagsFileList,
setTagsFileList
)
where
import Prelude (map, words, lines, readFile)
import Yi.Prelude
import Yi.Editor
import Yi.Dynamic
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, read lineno))
parseTagLine _ = Nothing
importTagTable :: FilePath -> IO TagTable
importTagTable filename = do
friendlyName <- expandTilda filename
tagStr <- 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