mirror of
https://github.com/joshuaclayton/unused.git
synced 2024-07-14 16:10:28 +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
|
||||
`$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
|
||||
|
||||
To run the test suite, run:
|
||||
|
@ -21,7 +21,7 @@ import Unused.Parser (parseResults)
|
||||
import Unused.ResponseFilter (withOneOccurrence, withLikelihoods, ignoringPaths)
|
||||
import Unused.ResultsClassifier (ParseConfigError, LanguageConfiguration(..), loadAllConfigurations)
|
||||
import Unused.TagsSource (TagSearchOutcome, loadTagsFromFile, loadTagsFromPipe)
|
||||
import Unused.TermSearch (SearchResults(..), SearchTerm, fromResults)
|
||||
import Unused.TermSearch (SearchResults(..), SearchBackend(..), SearchTerm, fromResults)
|
||||
import Unused.Types (TermMatchSet, RemovalLikelihood(..))
|
||||
|
||||
type AppConfig = MonadReader Options
|
||||
@ -45,6 +45,7 @@ data Options = Options
|
||||
, oWithoutCache :: Bool
|
||||
, oFromStdIn :: Bool
|
||||
, oCommitCount :: Maybe Int
|
||||
, oSearchBackend :: SearchBackend
|
||||
}
|
||||
|
||||
runProgram :: Options -> IO ()
|
||||
@ -57,10 +58,14 @@ run = do
|
||||
terms <- termsWithAlternatesFromConfig
|
||||
|
||||
liftIO $ renderHeader terms
|
||||
results <- withCache . (`executeSearch` terms) =<< searchRunner
|
||||
backend <- searchBackend
|
||||
results <- withCache . flip (executeSearch backend) terms =<< searchRunner
|
||||
|
||||
printResults =<< retrieveGitContext =<< fmap (`parseResults` results) loadAllConfigs
|
||||
|
||||
searchBackend :: AppConfig m => m SearchBackend
|
||||
searchBackend = asks oSearchBackend
|
||||
|
||||
termsWithAlternatesFromConfig :: App [SearchTerm]
|
||||
termsWithAlternatesFromConfig = do
|
||||
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 Unused.CLI (SearchRunner(..))
|
||||
import Unused.Grouping (CurrentGrouping(..))
|
||||
import Unused.TermSearch (SearchBackend(..))
|
||||
import Unused.Types (RemovalLikelihood(..))
|
||||
import Unused.Util (stringToInt)
|
||||
|
||||
@ -38,6 +39,7 @@ parseOptions =
|
||||
<*> parseWithoutCache
|
||||
<*> parseFromStdIn
|
||||
<*> parseCommitCount
|
||||
<*> parseSearchBackend
|
||||
|
||||
parseSearchRunner :: Parser SearchRunner
|
||||
parseSearchRunner =
|
||||
@ -116,3 +118,17 @@ parseCommitCount =
|
||||
commitParser = optional $ strOption $
|
||||
long "commits"
|
||||
<> 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
|
||||
V.analysisHeader terms
|
||||
|
||||
executeSearch :: SearchRunner -> [TS.SearchTerm] -> IO TS.SearchResults
|
||||
executeSearch runner terms = do
|
||||
executeSearch :: TS.SearchBackend -> SearchRunner -> [TS.SearchTerm] -> IO TS.SearchResults
|
||||
executeSearch backend runner terms = do
|
||||
renderHeader terms
|
||||
runSearch runner terms <* U.resetScreen
|
||||
runSearch backend runner terms <* U.resetScreen
|
||||
|
||||
runSearch :: SearchRunner -> [TS.SearchTerm] -> IO TS.SearchResults
|
||||
runSearch SearchWithProgress = I.progressWithIndicator TS.search I.createProgressBar
|
||||
runSearch SearchWithoutProgress = I.progressWithIndicator TS.search I.createSpinner
|
||||
runSearch :: TS.SearchBackend -> SearchRunner -> [TS.SearchTerm] -> IO TS.SearchResults
|
||||
runSearch b SearchWithProgress = I.progressWithIndicator (TS.search b) I.createProgressBar
|
||||
runSearch b SearchWithoutProgress = I.progressWithIndicator (TS.search b) I.createSpinner
|
||||
|
@ -1,20 +1,33 @@
|
||||
module Unused.TermSearch
|
||||
( SearchResults(..)
|
||||
, SearchBackend(..)
|
||||
, SearchTerm
|
||||
, search
|
||||
) where
|
||||
|
||||
import qualified Data.Maybe as M
|
||||
import GHC.IO.Exception (ExitCode(ExitSuccess))
|
||||
import qualified System.Process as P
|
||||
import Unused.TermSearch.Internal (commandLineOptions, parseSearchResult)
|
||||
import Unused.TermSearch.Types (SearchResults(..))
|
||||
import Unused.TermSearch.Types (SearchResults(..), SearchBackend(..))
|
||||
import Unused.Types (SearchTerm, searchTermToString)
|
||||
|
||||
search :: SearchTerm -> IO SearchResults
|
||||
search t =
|
||||
SearchResults . M.mapMaybe (parseSearchResult t) <$> (lines <$> ag (searchTermToString t))
|
||||
search :: SearchBackend -> SearchTerm -> IO SearchResults
|
||||
search backend t =
|
||||
SearchResults . M.mapMaybe (parseSearchResult backend t) <$> (lines <$> performSearch backend (searchTermToString t))
|
||||
|
||||
ag :: String -> IO String
|
||||
ag t = do
|
||||
(_, results, _) <- P.readProcessWithExitCode "ag" (commandLineOptions t) ""
|
||||
return results
|
||||
performSearch :: SearchBackend -> String -> IO String
|
||||
performSearch b t = extractSearchResults b <$> searchOutcome
|
||||
where
|
||||
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.Maybe as M
|
||||
import qualified Data.Text as T
|
||||
import Unused.TermSearch.Types (SearchBackend(..))
|
||||
import Unused.Types (SearchTerm(..), TermMatch(..))
|
||||
import Unused.Util (stringToInt)
|
||||
|
||||
commandLineOptions :: String -> [String]
|
||||
commandLineOptions t =
|
||||
commandLineOptions :: SearchBackend -> String -> [String]
|
||||
commandLineOptions backend t =
|
||||
if regexSafeTerm t
|
||||
then ["(\\W|^)" ++ t ++ "(\\W|$)", "."] ++ baseFlags
|
||||
else [t, ".", "-Q"] ++ baseFlags
|
||||
where
|
||||
baseFlags = ["-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||
then regexFlags backend t ++ baseFlags backend
|
||||
else nonRegexFlags backend t ++ baseFlags backend
|
||||
|
||||
parseSearchResult :: SearchTerm -> String -> Maybe TermMatch
|
||||
parseSearchResult term =
|
||||
maybeTermMatch . map T.unpack . T.splitOn ":" . T.pack
|
||||
parseSearchResult :: SearchBackend -> SearchTerm -> String -> Maybe TermMatch
|
||||
parseSearchResult backend term =
|
||||
maybeTermMatch backend . map T.unpack . T.splitOn ":" . T.pack
|
||||
where
|
||||
maybeTermMatch [_, path, count] = Just $ toTermMatch term path $ countInt count
|
||||
maybeTermMatch _ = Nothing
|
||||
maybeTermMatch Rg [path, count] = Just $ toTermMatch term path $ countInt count
|
||||
maybeTermMatch Rg _ = Nothing
|
||||
maybeTermMatch Ag [_, path, count] = Just $ toTermMatch term path $ countInt count
|
||||
maybeTermMatch Ag _ = Nothing
|
||||
countInt = M.fromMaybe 0 . stringToInt
|
||||
toTermMatch (OriginalTerm t) path = TermMatch t path Nothing
|
||||
toTermMatch (AliasTerm t a) path = TermMatch t path (Just a)
|
||||
|
||||
regexSafeTerm :: String -> Bool
|
||||
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
|
||||
( SearchResults(..)
|
||||
, SearchBackend(..)
|
||||
) where
|
||||
|
||||
import Unused.Types (TermMatch)
|
||||
|
||||
data SearchBackend = Ag | Rg
|
||||
|
||||
newtype SearchResults = SearchResults { fromResults :: [TermMatch] } deriving (Monoid)
|
||||
|
@ -5,6 +5,7 @@ module Unused.TermSearch.InternalSpec
|
||||
|
||||
import Test.Hspec
|
||||
import Unused.TermSearch.Internal
|
||||
import Unused.TermSearch.Types
|
||||
import Unused.Types
|
||||
|
||||
main :: IO ()
|
||||
@ -14,17 +15,25 @@ spec :: Spec
|
||||
spec = parallel $ do
|
||||
describe "commandLineOptions" $ 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 "no_way!" `shouldBe` ["no_way!", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||
commandLineOptions "[]=" `shouldBe` ["[]=", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||
commandLineOptions "window.globalOverride" `shouldBe` ["window.globalOverride", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||
commandLineOptions Ag "can_do_things?" `shouldBe` ["can_do_things?", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||
commandLineOptions Ag "no_way!" `shouldBe` ["no_way!", ".", "-Q", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||
commandLineOptions Ag "[]=" `shouldBe` ["[]=", ".", "-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 "awesome_method" `shouldBe` ["(\\W|^)awesome_method(\\W|$)", ".", "-c", "--ackmate", "--ignore-dir", "tmp/unused"]
|
||||
commandLineOptions Rg "can_do_things?" `shouldBe` ["can_do_things?", ".", "-F", "-c", "-j", "1"]
|
||||
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
|
||||
it "parses normal results from `ag` to a TermMatch" $
|
||||
parseSearchResult (OriginalTerm "method_name") ":app/models/foo.rb:123" `shouldBe` (Just $ TermMatch "method_name" "app/models/foo.rb" Nothing 123)
|
||||
it "parses normal results from `ag` to a TermMatch" $ do
|
||||
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" $
|
||||
parseSearchResult (OriginalTerm "method_name") "" `shouldBe` Nothing
|
||||
it "returns Nothing when it cannot parse" $ do
|
||||
parseSearchResult Ag (OriginalTerm "method_name") "" `shouldBe` Nothing
|
||||
parseSearchResult Rg (OriginalTerm "method_name") "" `shouldBe` Nothing
|
||||
|
Loading…
Reference in New Issue
Block a user