mirror of
https://github.com/joshuaclayton/unused.git
synced 2024-08-15 07:40:46 +03:00
Attempt to find and load tags automatically
Why? ==== Frequency of a tool's usage is determined by how easy it is to use the tool. By having to pipe in ctags files all the time, and not provide any guidance to the user, this program is merely a toy, since it's hard to get right, and harder to explore. This modifies the default behavior to look for a ctags file in a few common locations, and lets the user choose a custom location if she so chooses. Resolves #35
This commit is contained in:
parent
c5f2a38e80
commit
43edf288e2
22
README.md
22
README.md
@ -64,17 +64,27 @@ your `$PATH`.
|
||||
|
||||
## Using Unused
|
||||
|
||||
`unused` reads from a pipe expecting a series of tokens to search the codebase
|
||||
for.
|
||||
`unused` attempts to read from common tags file locations (`.git/tags`,
|
||||
`tags`, and `tmp/tags`).
|
||||
|
||||
The recommended way to do this is to clean up your tags file and pipe it in:
|
||||
In an application where the tags file exists, run:
|
||||
|
||||
```sh
|
||||
cat .git/tags | cut -f1 | sort -u | unused
|
||||
unused
|
||||
```
|
||||
|
||||
My end goal is to have the latter rolled up into unused itself, so you can
|
||||
navigate to a directory, run `unused`, and everything works as expected.
|
||||
If you want to specify a custom tags file, or load tokens from somewhere else,
|
||||
run:
|
||||
|
||||
```sh
|
||||
cat .custom/tags | unused --stdin
|
||||
```
|
||||
|
||||
To view more usage options, run:
|
||||
|
||||
```sh
|
||||
unused --help
|
||||
```
|
||||
|
||||
## Requirements
|
||||
|
||||
|
43
app/Main.hs
43
app/Main.hs
@ -8,8 +8,9 @@ import Unused.Types (ParseResponse, RemovalLikelihood(..))
|
||||
import Unused.ResultsClassifier
|
||||
import Unused.ResponseFilter (withOneOccurrence, withLikelihoods, ignoringPaths)
|
||||
import Unused.Grouping (CurrentGrouping(..), groupedResponses)
|
||||
import Unused.CLI (SearchRunner(..), withoutCursor, renderHeader, executeSearch, printParseError, printSearchResults, resetScreen, withInterruptHandler)
|
||||
import Unused.CLI (SearchRunner(..), withoutCursor, renderHeader, executeSearch, printParseError, printMissingTagsFileError, printSearchResults, resetScreen, withInterruptHandler)
|
||||
import Unused.Cache
|
||||
import Unused.TagsSource
|
||||
|
||||
data Options = Options
|
||||
{ oSearchRunner :: SearchRunner
|
||||
@ -19,6 +20,7 @@ data Options = Options
|
||||
, oIgnoredPaths :: [String]
|
||||
, oGrouping :: CurrentGrouping
|
||||
, oWithCache :: Bool
|
||||
, oFromStdIn :: Bool
|
||||
}
|
||||
|
||||
main :: IO ()
|
||||
@ -27,35 +29,42 @@ main = withInterruptHandler $
|
||||
(withInfo parseOptions pHeader pDescription pFooter)
|
||||
where
|
||||
pHeader = "Unused: Analyze potentially unused code"
|
||||
pDescription = "Unused allows a developer to pipe in a list of tokens to\
|
||||
\ search through in directory to determine likelihood a\
|
||||
\ token can be removed. Requires tokens be piped into the\
|
||||
\ program seperated by newlines. See CLI USAGE below."
|
||||
pFooter = "CLI USAGE: $ cat path/to/ctags | cut -f1 | sort -u | unused"
|
||||
pDescription = "Unused allows a developer to leverage an existing tags file\
|
||||
\ (located at .git/tags, tags, or tmp/tags) to identify tokens\
|
||||
\ in a codebase that are unused."
|
||||
pFooter = "CLI USAGE: $ unused"
|
||||
|
||||
run :: Options -> IO ()
|
||||
run options = withoutCursor $ do
|
||||
hSetBuffering stdout NoBuffering
|
||||
|
||||
terms <- pure . lines =<< getContents
|
||||
renderHeader terms
|
||||
terms' <- calculateTagInput options
|
||||
|
||||
languageConfig <- loadLanguageConfig
|
||||
case terms' of
|
||||
(Left e) -> printMissingTagsFileError e
|
||||
(Right terms) -> do
|
||||
renderHeader terms
|
||||
|
||||
results <- withCache options $ unlines <$> executeSearch (oSearchRunner options) terms
|
||||
languageConfig <- loadLanguageConfig
|
||||
|
||||
let response = parseLines languageConfig results
|
||||
results <- withCache options $ unlines <$> executeSearch (oSearchRunner options) terms
|
||||
|
||||
resetScreen
|
||||
let response = parseLines languageConfig results
|
||||
|
||||
either printParseError (printSearchResults . groupedResponses (oGrouping options)) $
|
||||
optionFilters options response
|
||||
resetScreen
|
||||
|
||||
either printParseError (printSearchResults . groupedResponses (oGrouping options)) $
|
||||
optionFilters options response
|
||||
|
||||
return ()
|
||||
|
||||
loadLanguageConfig :: IO [LanguageConfiguration]
|
||||
loadLanguageConfig = either (const []) id <$> loadConfig
|
||||
|
||||
calculateTagInput :: Options -> IO (Either TagSearchOutcome [String])
|
||||
calculateTagInput Options{ oFromStdIn = True } = loadTagsFromPipe
|
||||
calculateTagInput Options{ oFromStdIn = False } = loadTagsFromFile
|
||||
|
||||
withCache :: Options -> IO String -> IO String
|
||||
withCache Options{ oWithCache = True } = cached
|
||||
withCache Options{ oWithCache = False } = id
|
||||
@ -88,6 +97,7 @@ parseOptions =
|
||||
<*> parseIgnorePaths
|
||||
<*> parseGroupings
|
||||
<*> parseWithCache
|
||||
<*> parseFromStdIn
|
||||
|
||||
parseSearchRunner :: Parser SearchRunner
|
||||
parseSearchRunner =
|
||||
@ -153,3 +163,8 @@ parseWithCache = switch $
|
||||
short 'c'
|
||||
<> long "with-cache"
|
||||
<> help "Write to cache and read when available"
|
||||
|
||||
parseFromStdIn :: Parser Bool
|
||||
parseFromStdIn = switch $
|
||||
long "stdin"
|
||||
<> help "Read tags from STDIN"
|
||||
|
@ -4,5 +4,6 @@ module Unused.CLI
|
||||
|
||||
import Unused.CLI.Search as X
|
||||
import Unused.CLI.SearchError as X
|
||||
import Unused.CLI.MissingTagsFileError as X
|
||||
import Unused.CLI.SearchResult as X
|
||||
import Unused.CLI.Util as X
|
||||
|
48
src/Unused/CLI/MissingTagsFileError.hs
Normal file
48
src/Unused/CLI/MissingTagsFileError.hs
Normal file
@ -0,0 +1,48 @@
|
||||
module Unused.CLI.MissingTagsFileError
|
||||
( printMissingTagsFileError
|
||||
) where
|
||||
|
||||
import Unused.TagsSource
|
||||
import Unused.CLI.Util
|
||||
|
||||
printMissingTagsFileError :: TagSearchOutcome -> IO ()
|
||||
printMissingTagsFileError e = do
|
||||
setSGR [SetColor Background Vivid Red]
|
||||
setSGR [SetColor Foreground Vivid White]
|
||||
setSGR [SetConsoleIntensity BoldIntensity]
|
||||
|
||||
putStrLn "\nThere was a problem finding a tags file.\n"
|
||||
|
||||
setSGR [Reset]
|
||||
|
||||
printOutcomeMessage e
|
||||
putStr "\n"
|
||||
|
||||
setSGR [SetConsoleIntensity BoldIntensity]
|
||||
putStr "If you're generating a ctags file to a custom location, "
|
||||
putStrLn "you can pipe it into unused:"
|
||||
setSGR [Reset]
|
||||
|
||||
putStrLn " cat custom/ctags | unused --stdin"
|
||||
|
||||
putStr "\n"
|
||||
|
||||
setSGR [SetConsoleIntensity BoldIntensity]
|
||||
putStrLn "You can find out more about Exuberant Ctags here:"
|
||||
setSGR [Reset]
|
||||
putStrLn " http://ctags.sourceforge.net/"
|
||||
|
||||
putStr "\n"
|
||||
|
||||
setSGR [SetConsoleIntensity BoldIntensity]
|
||||
putStrLn "You can read about a good git-based Ctags workflow here:"
|
||||
setSGR [Reset]
|
||||
putStrLn " http://tbaggery.com/2011/08/08/effortless-ctags-with-git.html"
|
||||
|
||||
putStr "\n"
|
||||
|
||||
printOutcomeMessage :: TagSearchOutcome -> IO ()
|
||||
printOutcomeMessage (TagsFileNotFound directoriesSearched) = do
|
||||
putStrLn "Looked for a 'tags' file in the following directories:\n"
|
||||
mapM_ (\d -> putStrLn $ "* " ++ d) directoriesSearched
|
||||
|
41
src/Unused/TagsSource.hs
Normal file
41
src/Unused/TagsSource.hs
Normal file
@ -0,0 +1,41 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module Unused.TagsSource
|
||||
( TagSearchOutcome(..)
|
||||
, loadTagsFromFile
|
||||
, loadTagsFromPipe
|
||||
) where
|
||||
|
||||
import Data.List (nub)
|
||||
import System.Directory (findFile)
|
||||
import Unused.Regex (matchRegex)
|
||||
import qualified Data.Text as T
|
||||
|
||||
data TagSearchOutcome
|
||||
= TagsFileNotFound [String]
|
||||
|
||||
loadTagsFromPipe :: IO (Either TagSearchOutcome [String])
|
||||
loadTagsFromPipe = fmap (Right . tokensFromTags) getContents
|
||||
|
||||
loadTagsFromFile :: IO (Either TagSearchOutcome [String])
|
||||
loadTagsFromFile = fmap (fmap tokensFromTags) tagsContent
|
||||
|
||||
tokensFromTags :: String -> [String]
|
||||
tokensFromTags =
|
||||
filter tagRemovalRegex . nub . map token . tokenLocations
|
||||
where
|
||||
tokenLocations = map (T.splitOn "\t" . T.pack) . lines
|
||||
token = T.unpack . head
|
||||
|
||||
tagRemovalRegex :: String -> Bool
|
||||
tagRemovalRegex = not . matchRegex "^!_TAG"
|
||||
|
||||
tagsContent :: IO (Either TagSearchOutcome String)
|
||||
tagsContent = findFile possibleTagsFileDirectories "tags" >>= eitherReadFile
|
||||
|
||||
eitherReadFile :: Maybe String -> IO (Either TagSearchOutcome String)
|
||||
eitherReadFile Nothing = return $ Left $ TagsFileNotFound possibleTagsFileDirectories
|
||||
eitherReadFile (Just path) = Right <$> readFile path
|
||||
|
||||
possibleTagsFileDirectories :: [String]
|
||||
possibleTagsFileDirectories = [".git", "tmp", "."]
|
@ -33,9 +33,11 @@ library
|
||||
, Unused.LikelihoodCalculator
|
||||
, Unused.Cache
|
||||
, Unused.Cache.DirectoryFingerprint
|
||||
, Unused.TagsSource
|
||||
, Unused.CLI
|
||||
, Unused.CLI.Search
|
||||
, Unused.CLI.SearchError
|
||||
, Unused.CLI.MissingTagsFileError
|
||||
, Unused.CLI.SearchResult
|
||||
, Unused.CLI.SearchResult.ColumnFormatter
|
||||
, Unused.CLI.Util
|
||||
|
Loading…
Reference in New Issue
Block a user