Commit Graph

38 Commits

Author SHA1 Message Date
Joshua Clayton
54e55c46a2 Add missing spec name to unused.cabal 2016-06-05 07:42:18 -04:00
Joshua Clayton
6ffb098b20 Initial support of aliases based on wildcard matching
Why?
====

Dynamic languages, and Rails in particular, support some fun method
creation. One common pattern is, within RSpec, to create matchers
dynamically based on predicate methods. Two common examples are:

* `#admin?` gets converted to the matcher `#be_admin`
* `#has_active_todos?` gets converted to the matcher `#have_active_todos`

This especially comes into play when writing page objects with predicate
methods.

This change introduces the concept of aliases, a way to describe the
before/after for these transformations. This introduces a direct swap
with a wildcard value (%s), although this may change in the future to
support other transformations for pluralization, camel-casing, etc.

Externally, aliases are not grouped together by term; however, the
underlying counts are summed together, increasing the total occurrences
and likely pushing the individual method out of "high" likelihood into
"medium" or "low" likelihood.

Closes #19.
2016-06-01 22:16:44 -04:00
Joshua Clayton
4dfd788318 Extract views to a Views module
Why?
====

View logic was scattered all over the place; this introduces a views
module to encapsulate any corresponding view work into one spot.
2016-05-27 06:11:52 -04:00
Joshua Clayton
0d2470815d Simplify parsing and caching of results
Why?
====

Parsec is overkill when all that's really needed is splitting on
semicolons and converting a string to a non-negative Int.

One side-effect of this is to convert the caching mechanism from flat
text to CSV, with cassava handling (de-)serialization.

Additional
==========

Introduce ReaderT to calculate sha once per cache interaction

Previously, we were calculating the fingerprint (SHA) for match results
potentially twice, once when reading from the cache, and a second time
if no cache was found. This introduces a ReaderT to manage cache
interaction with a single fingerprint calculation.

This also abstracts what's being cached to only care about the fact that
the data can be converted to/from csv.
2016-05-26 21:37:11 -04:00
Joshua Clayton
f618d8a796 Use .gitignore to determine files for fingerprinting a project
Why?
====

Because a .gitignore file captures a fair number of project-specific
directories and files to ignore, we can use this list to reduce the
number of files to look at when determining a fingerprint for a project.

Because the fingerprint should be based on files we care about changing,
the project-specific .gitignore is a great place to start.

This drastically reduces fingerprint timing - for larger projects, or
projects with a massive number of files (e.g. anything doing anything
significant with NPM and a front-end framework), this will help make
caching usable. For normal projects, this cuts fingerprint
calculation to 10%-20% of what it was previously.

Closes #38
2016-05-26 17:19:35 -04:00
Joshua Clayton
b7aefe66d0 Bump version to 0.2.0.0 2016-05-22 06:15:00 -04:00
Joshua Clayton
43edf288e2 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
2016-05-22 06:06:09 -04:00
Joshua Clayton
307dd2030f Introduce internal yaml configuration of auto low likelihood match handling
Why?
====

Handling low likelihood configuration was previously a huge pain,
because the syntax in Haskell was fairly terse. This introduces a yaml
format internally that ships with the app covering basic cases for
Rails, Phoenix, and Haskell. I could imagine getting baselines in here
for other languages and frameworks (especially ones I've used and am
comfortable with) as a baseline.

This also paves the way for searching for user-provided additions and
loading those configurations in addition to what we have here.
2016-05-21 05:34:18 -04:00
Joshua Clayton
44ab0a1435 Read unchanged results from the cache
At some point, this also needs to md5 the tags list itself and factor
that in (since if the tagging algorithm changes, and new tokens get
uncovered, it'd invalidate the cache)
2016-05-16 21:48:36 -04:00
Joshua Clayton
0e966c9302 Test Util.groupBy 2016-05-15 05:53:29 -04:00
Joshua Clayton
97f083fc2c Use regex in ag for simple words
Why?
====

ag supports using regular expressions for searches; however, the -Q
flag, which was previously always used, resulted in literal search
results.

By searching literal matches, it would potentially return too many
results. For example, with a `me` method in a controller, it'd match
words like `awesome` or `method`.

This introduces a check where, if the token being searched is only
composed of word characters (`[A-Za-z0-9_]`), it'll switch over to use
regular expressions with ag and surround the token with non-word matches
on either end. The goal here is to reduce false-positives in matches.
2016-05-14 08:14:54 -04:00
Joshua Clayton
bcbc1b6462 Allow search result grouping
Why?
====

Grouping results can be helpful to view information differently, e.g. to
see highest-offending files or to remove grouping entirely.

This introduces a flag to allow overriding the default group (two levels
of directory)
2016-05-14 06:36:01 -04:00
Joshua Clayton
a8a9d250e3 Parallelize search
Why?
====

Searching hundreds or thousands of tokens with ag can be slow; this
introduces parallel processing of search so results are returned more
quickly.
2016-05-13 14:46:23 -04:00
Joshua Clayton
2ec73ac3b3 Consolidate progress indicator handling 2016-05-13 14:18:23 -04:00
Joshua Clayton
4097ac4aa8 Bump version to 0.1.1.0 2016-05-12 18:11:14 -04:00
Joshua Clayton
7f0e701823 Extract internal parsing handling to separate module
Why?
====

Parsing lines of results was somewhat unreliable, and terms with odd
characters were causing problems. This:

* extracts parsing into an Unused.Parser.Internal module for ease of
  testing
* fixes cases where certain tokens weren't matching
2016-05-12 18:02:59 -04:00
Joshua Clayton
cf542044df Warn on everything, error on anything 2016-05-11 05:32:42 -04:00
Joshua Clayton
86146ad4a4 Update cabal file to reflect proper information 2016-05-11 05:28:01 -04:00
Joshua Clayton
2650e1f040 Improve likelihood calculation and include reasons for evaluation
Why?
====

A simple calculation ("yes, this should be removed" or "no, this is
probably fine") is frankly not enough information for someone evaluating
their codebase to understand why we made the decision.

This introduces a removal reason, so a user understands why we ranked it
the way we did, and adds additional logic around a method and its tests
to determine if a method exists and is only being used in the tests (if
so, it should probably be deleted).

This is done with an Occurrances record, which is created for total
files, test code, and non-test code. The test code logic is somewhat
naive but works in most cases. It doesn't ensure a particular directory,
in the case that tests live alongside source code (e.g. Go), and
captures RSpec cases as well.
2016-05-11 05:18:55 -04:00
Joshua Clayton
4947e54f27 Better manage column formatting
Why?
====

Formatting each column requires context on the column, as well as
information on alignment. This extracts the column formatting logic to a
specific formatter.

ColumnFormatter is coupled to the order of columns/data displayed to the
user.
2016-05-10 06:37:31 -04:00
Joshua Clayton
11d35a6263 Hook into interrupt to trigger other behavior
Why?
====

Unused hides the cursor and potentially does other things to the window that
may leave it in an odd state. This introduces a hook to run any state
cleanup, including re-enabling the cursor, when a user sends a SIGINT to
the program.
2016-05-10 05:35:28 -04:00
Joshua Clayton
5952306873 Run optimizations 2016-05-09 08:29:09 -04:00
Joshua Clayton
f7a2e1a287 Add Hspec and tests around parsing 2016-05-08 06:54:34 -04:00
Joshua Clayton
bfca8cae19 Error on warnings 2016-05-07 06:23:38 -04:00
Joshua Clayton
53bd914fd9 Remove unnecessary dependency on mtl 2016-05-07 06:10:19 -04:00
Joshua Clayton
1f5db58f5e Move additional functionality out of Types and into separate modules 2016-05-07 06:10:15 -04:00
Joshua Clayton
a924cb99f3 Extract ProgressBar to have a similar interface to Spinner 2016-05-06 21:43:22 -04:00
Joshua Clayton
0f4e056641 Introduce a Spinner when no progress bar is displayed
Why?
====

With a spinner in place, there's visual indication that something's
happening.
2016-05-06 17:25:20 -04:00
Joshua Clayton
e34f6951f1 Add optparse-applicative for flags
This introduces the optparse-applicative library for parsing out any
subcommands/flags/args. Currently only supports --no-progress (-P).
2016-05-05 17:53:53 -04:00
Joshua Clayton
67e52ed017 Initial pass at RemovalLikelihood calculator 2016-05-03 21:41:03 -04:00
Joshua Clayton
49b7b65b17 Improve output by grouping by directory structure 2016-05-03 05:22:50 -04:00
Joshua Clayton
3ddf0631a9 Display progress bar when running ag 2016-05-02 06:05:31 -04:00
Joshua Clayton
9f006ffd3c Filter results to matches with one file and one occurrence 2016-04-30 05:49:04 -04:00
Joshua Clayton
2d4939cb47 Reorganize data structure to handle parsing errors
This extracts the previous data structure from groupBy into an actual
Data.Map.Strict String [TermMatch], as well as another type
(ParseResponse) capturing invalid and valid responses.
2016-04-29 05:28:03 -04:00
Joshua Clayton
ed72d2405a Initial pass at adding color to output 2016-04-29 04:46:31 -04:00
Joshua Clayton
a5230f163e Parse results into matches 2016-04-28 22:21:18 -04:00
Joshua Clayton
1249a0e823 Search for terms from a tags file
This returns a list of each match, files, and counts based on the
current directory.
2016-04-28 17:42:58 -04:00
Joshua Clayton
8931c08f93 Initial 2016-04-28 05:37:06 -04:00