mirror of
https://github.com/joshuaclayton/unused.git
synced 2024-08-16 08:10:55 +03:00
Allow for rg in addition to ag
Why? ==== rg is oftentimes faster for searching across a codebase than ag; however, it's newer and potentially more unfamiliar, so ag still remains the default. This is an introduction to supporting rg, but without updating the Homebrew recipe yet. Given a trial run, I can imagine switching to it as a default eventually.
This commit is contained in:
parent
596efc8734
commit
64f62d0141
@ -363,6 +363,10 @@ Unused leverages [Ag](https://github.com/ggreer/the_silver_searcher) to
|
|||||||
analyze the codebase; as such, you'll need to have `ag` available in your
|
analyze the codebase; as such, you'll need to have `ag` available in your
|
||||||
`$PATH`. This is set as an explicit dependency in Homebrew.
|
`$PATH`. This is set as an explicit dependency in Homebrew.
|
||||||
|
|
||||||
|
Alternatively, if you'd like to use
|
||||||
|
[RipGrep](https://github.com/BurntSushi/ripgrep), you can do so with the
|
||||||
|
`--search rg` flag. Be sure to have RipGrep installed first.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
To run the test suite, run:
|
To run the test suite, run:
|
||||||
|
@ -21,7 +21,7 @@ import Unused.Parser (parseResults)
|
|||||||
import Unused.ResponseFilter (withOneOccurrence, withLikelihoods, ignoringPaths)
|
import Unused.ResponseFilter (withOneOccurrence, withLikelihoods, ignoringPaths)
|
||||||
import Unused.ResultsClassifier (ParseConfigError, LanguageConfiguration(..), loadAllConfigurations)
|
import Unused.ResultsClassifier (ParseConfigError, LanguageConfiguration(..), loadAllConfigurations)
|
||||||
import Unused.TagsSource (TagSearchOutcome, loadTagsFromFile, loadTagsFromPipe)
|
import Unused.TagsSource (TagSearchOutcome, loadTagsFromFile, loadTagsFromPipe)
|
||||||
import Unused.TermSearch (SearchResults(..), SearchTerm, fromResults)
|
import Unused.TermSearch (SearchResults(..), SearchBackend(..), SearchTerm, fromResults)
|
||||||
import Unused.Types (TermMatchSet, RemovalLikelihood(..))
|
import Unused.Types (TermMatchSet, RemovalLikelihood(..))
|
||||||
|
|
||||||
type AppConfig = MonadReader Options
|
type AppConfig = MonadReader Options
|
||||||
@ -45,6 +45,7 @@ data Options = Options
|
|||||||
, oWithoutCache :: Bool
|
, oWithoutCache :: Bool
|
||||||
, oFromStdIn :: Bool
|
, oFromStdIn :: Bool
|
||||||
, oCommitCount :: Maybe Int
|
, oCommitCount :: Maybe Int
|
||||||
|
, oSearchBackend :: SearchBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
runProgram :: Options -> IO ()
|
runProgram :: Options -> IO ()
|
||||||
@ -57,10 +58,14 @@ run = do
|
|||||||
terms <- termsWithAlternatesFromConfig
|
terms <- termsWithAlternatesFromConfig
|
||||||
|
|
||||||
liftIO $ renderHeader terms
|
liftIO $ renderHeader terms
|
||||||
results <- withCache . (`executeSearch` terms) =<< searchRunner
|
backend <- searchBackend
|
||||||
|
results <- withCache . flip (executeSearch backend) terms =<< searchRunner
|
||||||
|
|
||||||
printResults =<< retrieveGitContext =<< fmap (`parseResults` results) loadAllConfigs
|
printResults =<< retrieveGitContext =<< fmap (`parseResults` results) loadAllConfigs
|
||||||
|
|
||||||
|
searchBackend :: AppConfig m => m SearchBackend
|
||||||
|
searchBackend = asks oSearchBackend
|
||||||
|
|
||||||
termsWithAlternatesFromConfig :: App [SearchTerm]
|
termsWithAlternatesFromConfig :: App [SearchTerm]
|
||||||
termsWithAlternatesFromConfig = do
|
termsWithAlternatesFromConfig = do
|
||||||
aliases <- concatMap lcTermAliases <$> loadAllConfigs
|
aliases <- concatMap lcTermAliases <$> loadAllConfigs
|
||||||
|
16
app/Main.hs
16
app/Main.hs
@ -6,6 +6,7 @@ import qualified Data.Maybe as M
|
|||||||
import Options.Applicative
|
import Options.Applicative
|
||||||
import Unused.CLI (SearchRunner(..))
|
import Unused.CLI (SearchRunner(..))
|
||||||
import Unused.Grouping (CurrentGrouping(..))
|
import Unused.Grouping (CurrentGrouping(..))
|
||||||
|
import Unused.TermSearch (SearchBackend(..))
|
||||||
import Unused.Types (RemovalLikelihood(..))
|
import Unused.Types (RemovalLikelihood(..))
|
||||||
import Unused.Util (stringToInt)
|
import Unused.Util (stringToInt)
|
||||||
|
|
||||||
@ -38,6 +39,7 @@ parseOptions =
|
|||||||
<*> parseWithoutCache
|
<*> parseWithoutCache
|
||||||
<*> parseFromStdIn
|
<*> parseFromStdIn
|
||||||
<*> parseCommitCount
|
<*> parseCommitCount
|
||||||
|
<*> parseSearchBackend
|
||||||
|
|
||||||
parseSearchRunner :: Parser SearchRunner
|
parseSearchRunner :: Parser SearchRunner
|
||||||
parseSearchRunner =
|
parseSearchRunner =
|
||||||
@ -116,3 +118,17 @@ parseCommitCount =
|
|||||||
commitParser = optional $ strOption $
|
commitParser = optional $ strOption $
|
||||||
long "commits"
|
long "commits"
|
||||||
<> help "Number of recent commit SHAs to display per token"
|
<> help "Number of recent commit SHAs to display per token"
|
||||||
|
|
||||||
|
parseSearchBackend :: Parser SearchBackend
|
||||||
|
parseSearchBackend = M.fromMaybe Ag <$> maybeBackend
|
||||||
|
where
|
||||||
|
maybeBackend = optional $ parseBackend <$> parseBackendOption
|
||||||
|
parseBackendOption =
|
||||||
|
strOption $
|
||||||
|
long "search"
|
||||||
|
<> help "[Allowed: ag, rg] Select searching backend"
|
||||||
|
|
||||||
|
parseBackend :: String -> SearchBackend
|
||||||
|
parseBackend "ag" = Ag
|
||||||
|
parseBackend "rg" = Rg
|
||||||
|
parseBackend _ = Ag
|
||||||
|
@ -16,11 +16,11 @@ renderHeader terms = do
|
|||||||
U.resetScreen
|
U.resetScreen
|
||||||
V.analysisHeader terms
|
V.analysisHeader terms
|
||||||
|
|
||||||
executeSearch :: SearchRunner -> [TS.SearchTerm] -> IO TS.SearchResults
|
executeSearch :: TS.SearchBackend -> SearchRunner -> [TS.SearchTerm] -> IO TS.SearchResults
|
||||||
executeSearch runner terms = do
|
executeSearch backend runner terms = do
|
||||||
renderHeader terms
|
renderHeader terms
|
||||||
runSearch runner terms <* U.resetScreen
|
runSearch backend runner terms <* U.resetScreen
|
||||||
|
|
||||||
runSearch :: SearchRunner -> [TS.SearchTerm] -> IO TS.SearchResults
|
runSearch :: TS.SearchBackend -> SearchRunner -> [TS.SearchTerm] -> IO TS.SearchResults
|
||||||
runSearch SearchWithProgress = I.progressWithIndicator TS.search I.createProgressBar
|
runSearch b SearchWithProgress = I.progressWithIndicator (TS.search b) I.createProgressBar
|
||||||
runSearch SearchWithoutProgress = I.progressWithIndicator TS.search I.createSpinner
|
runSearch b SearchWithoutProgress = I.progressWithIndicator (TS.search b) I.createSpinner
|
||||||
|
@ -1,20 +1,33 @@
|
|||||||
module Unused.TermSearch
|
module Unused.TermSearch
|
||||||
( SearchResults(..)
|
( SearchResults(..)
|
||||||
|
, SearchBackend(..)
|
||||||
, SearchTerm
|
, SearchTerm
|
||||||
, search
|
, search
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.Maybe as M
|
import qualified Data.Maybe as M
|
||||||
|
import GHC.IO.Exception (ExitCode(ExitSuccess))
|
||||||
import qualified System.Process as P
|
import qualified System.Process as P
|
||||||
import Unused.TermSearch.Internal (commandLineOptions, parseSearchResult)
|
import Unused.TermSearch.Internal (commandLineOptions, parseSearchResult)
|
||||||
import Unused.TermSearch.Types (SearchResults(..))
|
import Unused.TermSearch.Types (SearchResults(..), SearchBackend(..))
|
||||||
import Unused.Types (SearchTerm, searchTermToString)
|
import Unused.Types (SearchTerm, searchTermToString)
|
||||||
|
|
||||||
search :: SearchTerm -> IO SearchResults
|
search :: SearchBackend -> SearchTerm -> IO SearchResults
|
||||||
search t =
|
search backend t =
|
||||||
SearchResults . M.mapMaybe (parseSearchResult t) <$> (lines <$> ag (searchTermToString t))
|
SearchResults . M.mapMaybe (parseSearchResult backend t) <$> (lines <$> performSearch backend (searchTermToString t))
|
||||||
|
|
||||||
ag :: String -> IO String
|
performSearch :: SearchBackend -> String -> IO String
|
||||||
ag t = do
|
performSearch b t = extractSearchResults b <$> searchOutcome
|
||||||
(_, results, _) <- P.readProcessWithExitCode "ag" (commandLineOptions t) ""
|
where
|
||||||
return results
|
searchOutcome =
|
||||||
|
P.readProcessWithExitCode
|
||||||
|
(backendToCommand b)
|
||||||
|
(commandLineOptions b t)
|
||||||
|
""
|
||||||
|
backendToCommand Rg = "rg"
|
||||||
|
backendToCommand Ag = "ag"
|
||||||
|
|
||||||
|
extractSearchResults :: SearchBackend -> (ExitCode, String, String) -> String
|
||||||
|
extractSearchResults Rg (ExitSuccess, stdout, _) = stdout
|
||||||
|
extractSearchResults Rg (_, _, stderr) = stderr
|
||||||
|
extractSearchResults Ag (_, stdout, _) = stdout
|
||||||
|
@ -6,26 +6,38 @@ module Unused.TermSearch.Internal
|
|||||||
import qualified Data.Char as C
|
import qualified Data.Char as C
|
||||||
import qualified Data.Maybe as M
|
import qualified Data.Maybe as M
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
import Unused.TermSearch.Types (SearchBackend(..))
|
||||||
import Unused.Types (SearchTerm(..), TermMatch(..))
|
import Unused.Types (SearchTerm(..), TermMatch(..))
|
||||||
import Unused.Util (stringToInt)
|
import Unused.Util (stringToInt)
|
||||||
|
|
||||||
commandLineOptions :: String -> [String]
|
commandLineOptions :: SearchBackend -> String -> [String]
|
||||||
commandLineOptions t =
|
commandLineOptions backend t =
|
||||||
if regexSafeTerm t
|
if regexSafeTerm t
|
||||||
then ["(\\W|^)" ++ t ++ "(\\W|$)", "."] ++ baseFlags
|
then regexFlags backend t ++ baseFlags backend
|
||||||
else [t, ".", "-Q"] ++ baseFlags
|
else nonRegexFlags backend t ++ baseFlags backend
|
||||||
where
|
|
||||||
baseFlags = ["-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
|
||||||
|
|
||||||
parseSearchResult :: SearchTerm -> String -> Maybe TermMatch
|
parseSearchResult :: SearchBackend -> SearchTerm -> String -> Maybe TermMatch
|
||||||
parseSearchResult term =
|
parseSearchResult backend term =
|
||||||
maybeTermMatch . map T.unpack . T.splitOn ":" . T.pack
|
maybeTermMatch backend . map T.unpack . T.splitOn ":" . T.pack
|
||||||
where
|
where
|
||||||
maybeTermMatch [_, path, count] = Just $ toTermMatch term path $ countInt count
|
maybeTermMatch Rg [path, count] = Just $ toTermMatch term path $ countInt count
|
||||||
maybeTermMatch _ = Nothing
|
maybeTermMatch Rg _ = Nothing
|
||||||
|
maybeTermMatch Ag [_, path, count] = Just $ toTermMatch term path $ countInt count
|
||||||
|
maybeTermMatch Ag _ = Nothing
|
||||||
countInt = M.fromMaybe 0 . stringToInt
|
countInt = M.fromMaybe 0 . stringToInt
|
||||||
toTermMatch (OriginalTerm t) path = TermMatch t path Nothing
|
toTermMatch (OriginalTerm t) path = TermMatch t path Nothing
|
||||||
toTermMatch (AliasTerm t a) path = TermMatch t path (Just a)
|
toTermMatch (AliasTerm t a) path = TermMatch t path (Just a)
|
||||||
|
|
||||||
regexSafeTerm :: String -> Bool
|
regexSafeTerm :: String -> Bool
|
||||||
regexSafeTerm = all (\c -> C.isAlphaNum c || c == '_' || c == '-')
|
regexSafeTerm = all (\c -> C.isAlphaNum c || c == '_' || c == '-')
|
||||||
|
|
||||||
|
nonRegexFlags :: SearchBackend -> String -> [String]
|
||||||
|
nonRegexFlags Rg t = [t, ".", "-F"]
|
||||||
|
nonRegexFlags Ag t = [t, ".", "-Q"]
|
||||||
|
|
||||||
|
baseFlags :: SearchBackend -> [String]
|
||||||
|
baseFlags Rg = ["-c", "-j", "1"]
|
||||||
|
baseFlags Ag = ["-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||||
|
|
||||||
|
regexFlags :: SearchBackend -> String -> [String]
|
||||||
|
regexFlags _ t = ["(\\W|^)" ++ t ++ "(\\W|$)", "."]
|
||||||
|
@ -2,8 +2,11 @@
|
|||||||
|
|
||||||
module Unused.TermSearch.Types
|
module Unused.TermSearch.Types
|
||||||
( SearchResults(..)
|
( SearchResults(..)
|
||||||
|
, SearchBackend(..)
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Unused.Types (TermMatch)
|
import Unused.Types (TermMatch)
|
||||||
|
|
||||||
|
data SearchBackend = Ag | Rg
|
||||||
|
|
||||||
newtype SearchResults = SearchResults { fromResults :: [TermMatch] } deriving (Monoid)
|
newtype SearchResults = SearchResults { fromResults :: [TermMatch] } deriving (Monoid)
|
||||||
|
@ -5,6 +5,7 @@ module Unused.TermSearch.InternalSpec
|
|||||||
|
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
import Unused.TermSearch.Internal
|
import Unused.TermSearch.Internal
|
||||||
|
import Unused.TermSearch.Types
|
||||||
import Unused.Types
|
import Unused.Types
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
@ -14,17 +15,25 @@ spec :: Spec
|
|||||||
spec = parallel $ do
|
spec = parallel $ do
|
||||||
describe "commandLineOptions" $ do
|
describe "commandLineOptions" $ do
|
||||||
it "does not use regular expressions when the term contains non-word characters" $ do
|
it "does not use regular expressions when the term contains non-word characters" $ do
|
||||||
commandLineOptions "can_do_things?" `shouldBe` ["can_do_things?", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
commandLineOptions Ag "can_do_things?" `shouldBe` ["can_do_things?", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||||
commandLineOptions "no_way!" `shouldBe` ["no_way!", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
commandLineOptions Ag "no_way!" `shouldBe` ["no_way!", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||||
commandLineOptions "[]=" `shouldBe` ["[]=", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
commandLineOptions Ag "[]=" `shouldBe` ["[]=", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||||
commandLineOptions "window.globalOverride" `shouldBe` ["window.globalOverride", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
commandLineOptions Ag "window.globalOverride" `shouldBe` ["window.globalOverride", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||||
|
|
||||||
it "uses regular expression match with surrounding non-word matches for accuracy" $
|
commandLineOptions Rg "can_do_things?" `shouldBe` ["can_do_things?", ".", "-F", "-c", "-j", "1"]
|
||||||
commandLineOptions "awesome_method" `shouldBe` ["(\\W|^)awesome_method(\\W|$)", ".", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
commandLineOptions Rg "no_way!" `shouldBe` ["no_way!", ".", "-F", "-c", "-j", "1"]
|
||||||
|
commandLineOptions Rg "[]=" `shouldBe` ["[]=", ".", "-F", "-c", "-j", "1"]
|
||||||
|
commandLineOptions Rg "window.globalOverride" `shouldBe` ["window.globalOverride", ".", "-F", "-c", "-j", "1"]
|
||||||
|
|
||||||
|
it "uses regular expression match with surrounding non-word matches for accuracy" $ do
|
||||||
|
commandLineOptions Ag "awesome_method" `shouldBe` ["(\\W|^)awesome_method(\\W|$)", ".", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||||
|
commandLineOptions Rg "awesome_method" `shouldBe` ["(\\W|^)awesome_method(\\W|$)", ".", "-c", "-j", "1"]
|
||||||
|
|
||||||
describe "parseSearchResult" $ do
|
describe "parseSearchResult" $ do
|
||||||
it "parses normal results from `ag` to a TermMatch" $
|
it "parses normal results from `ag` to a TermMatch" $ do
|
||||||
parseSearchResult (OriginalTerm "method_name") ":app/models/foo.rb:123" `shouldBe` (Just $ TermMatch "method_name" "app/models/foo.rb" Nothing 123)
|
parseSearchResult Ag (OriginalTerm "method_name") ":app/models/foo.rb:123" `shouldBe` (Just $ TermMatch "method_name" "app/models/foo.rb" Nothing 123)
|
||||||
|
parseSearchResult Rg (OriginalTerm "method_name") "app/models/foo.rb:123" `shouldBe` (Just $ TermMatch "method_name" "app/models/foo.rb" Nothing 123)
|
||||||
|
|
||||||
it "returns Nothing when it cannot parse" $
|
it "returns Nothing when it cannot parse" $ do
|
||||||
parseSearchResult (OriginalTerm "method_name") "" `shouldBe` Nothing
|
parseSearchResult Ag (OriginalTerm "method_name") "" `shouldBe` Nothing
|
||||||
|
parseSearchResult Rg (OriginalTerm "method_name") "" `shouldBe` Nothing
|
||||||
|
Loading…
Reference in New Issue
Block a user