fast-tags-0.0.6: src/Main.hs
{-# LANGUAGE ScopedTypeVariables #-}
{- | Tagify haskell source.
The key features are to be fast, incremental (i.e. merge tags with one
file into existing tags), work on partially edited code, and work with
hsc. That way I can hook it to the editor's save action and always keep
the tags up to date.
-}
module Main where
import Control.Monad
import qualified Data.Either as Either
import qualified Data.List as List
import qualified Data.Text as Text
import qualified Data.Text.IO as Text.IO
import qualified System.Console.GetOpt as GetOpt
import qualified System.Environment as Environment
import qualified System.Exit
import qualified System.IO as IO
import FastTags
main :: IO ()
main = do
args <- Environment.getArgs
(flags, inputs) <- case GetOpt.getOpt GetOpt.Permute options args of
(flags, inputs, []) -> return (flags, inputs)
(_, _, errs) -> usage $ "flag errors:\n" ++ List.intercalate ", " errs
when (null inputs) $
usage "no input files\n"
let output = last $ "tags" : [fn | Output fn <- flags]
verbose = Verbose `elem` flags
oldTags <- fmap (maybe [vimMagicLine] Text.lines) $
catchENOENT $ Text.IO.readFile output
-- This will merge and sort the new tags. But I don't run it on the
-- the result of merging the old and new tags, so tags from another
-- file won't be sorted properly. To do that I'd have to parse all the
-- old tags and run processAll on all of them, which is a hassle.
-- TODO try it and see if it really hurts performance that much.
newTags <- fmap (processAll . concat) $
forM (zip [0..] inputs) $ \(i :: Int, fn) -> do
tags <- processFile fn
-- This has the side-effect of forcing the the tags, which is
-- essential if I'm tagging a lot of files at once.
let (warnings, newTags) = Either.partitionEithers tags
forM_ warnings $ \warn -> do
IO.hPutStrLn IO.stderr warn
when verbose $ do
let line = show i ++ " of " ++ show (length inputs - 1)
++ ": " ++ fn
putStr $ '\r' : line ++ replicate (78 - length line) ' '
IO.hFlush IO.stdout
return newTags
when verbose $ putChar '\n'
let write = if output == "-" then Text.IO.hPutStr IO.stdout
else Text.IO.writeFile output
write $ Text.unlines (mergeTags inputs oldTags newTags)
where
usage msg = do
putStr $ GetOpt.usageInfo
(msg ++ "\nusage: fast-tags file1 file2 ...") options
System.Exit.exitFailure
data Flag = Output FilePath | Verbose
deriving (Eq, Show)
options :: [GetOpt.OptDescr Flag]
options =
[ GetOpt.Option ['o'] [] (GetOpt.ReqArg Output "filename")
"output file, defaults to 'tags'"
, GetOpt.Option ['v'] [] (GetOpt.NoArg Verbose)
"print files as they are tagged, useful to track down slow files"
]