PLEASE BE AWARE THIS IS ALPHA SOFTWARE AND NOT ONLY WILL THE API CHANGE BUT IT WILL PROBABLY EAT YOUR CHILDREN. YOU HAVE BEEN WARNED.
Lens based Haskell refactoring toolkit.
Study after study shows that the very best designers produce structures that are faster, smaller, simpler, clearer, and produced with less effort. The differences between the great and the average approach an order of magnitude.
- Fred Brooks
Refactorio - Optical Refactoring Tool
Usage: refactorio [--io] EXPR [-t|--target TARGET] [-g|--glob GLOB]
[--prelude MODULE] ([-a|--ask] | [-p|--preview] |
[-m|--modify] | [--review] | [-s|--search]) ([--haskell|--hs]
| [--html] | [--javascript|--js] | [--c] | [--json] | [--xml]
| [--yaml])
Available options:
--io Add IO to expr type ('ByteString -> IO ByteString')
EXPR A Haskell expression of type 'ByteString ->
ByteString'
-t,--target TARGET One or more files/directories to traverse (defaults
to stdin, use '-' to force stdin)
-g,--glob GLOB Glob matches to include (eg '*.ini', 'f??b?r.c')
--prelude MODULE Use a specific Prelude module
-a,--ask Ask before changing files (default)
-p,--preview Only show the changes that would be made
-m,--modify Make the changes and summarize changed filenames
--review Make the changes and show details of changes
-s,--search Activate alternate search mode (Haskell mode only
right now)
--haskell,--hs Include .hs files and make Haskell ops available
--html Include .htm(l) files and make XML ops available
--javascript,--js Include .js files and make JavaScript ops available
--c Include .c files and make C ops available
--json Include .json files and make JSON ops available
--xml Include .xml files and make XML ops available
--yaml Include .y(a)ml files and make YAML ops available
-h,--help Show this help text
In all modes Refactorio traverses one or more files executing a ByteString -> ByteString
function on them. For a given file if the function does not change
the input then no output is logged for that file. If the function does change
the file then what happens next is dependent on the mode.
Any ByteString -> ByteString
function definition will work, for example:
BS.reverse
BS.take 1024
over convert Text.toUpper
# or equivalently
convert %~ Text.toUpper
key "foo" . _Number *~ 3
This is the default mode. In this mode Refactorio will show you the changes that it's about to make and prompt you on a file by file basis whether you want to accept the changes or not. If you accept, the file will be replaced, if not the file will be left intact (or you can 'Q'uit at any time).
not touch any files. You can think of it sort of like a --dry-run
.
In this mode Refactorio will just show all the changes that it would make, but
In this mode Refactorio will make changes to all files without asking but will show the full set of changes as they are made.
This is basically a --quiet
style mode that makes all changes without
confirmation and just reports which files changed with no further details.
This is a special mode which currently only works with
haskell-src-exts(-prisms)
which provide SrcSpanInfo
s which represent the
portion of the source code that a given feature occupies. This mode can be
easily made to work with any other lenses that can provide access to similar
information and we are exploring ways to broaden it's usefulness further.
Here are a few examples to whet your appetite. For more see examples.
"Upper case the string values of the array found under 'quiz.sport.q1.options'."
"Multiply the value of the key 'version' by 32."
TODO: Find a good way to deal with HTML. (problems with each of `hexpat-lens`, `hexml-lens`, `xml-html-conduit-lens` and `xml-lens`)
C'mon, you've never needed to "find all the authors with names longer than 15 characters and then sort all of the letters in their name that are above 'm' in place?" Pshaw.
Drop regex's in anywhere you like, eg. "uppercase and reverse the characters in the value of the JSON object at this key that match this regular expression":
Reach inside eg. gzipped files and do what you gotta do:
Example is temporarily unavailable, but goes something like:
refactorio -t foo.json.gz --json 'convert . from Z.gzipped . from convert . key \"baz\" . _Number *~ 85'
The beginnings of convert
and convertTo/convertFrom
(along with a
/an
)
exist which can be used like so:
When types can be infered:
% refactorio --html -t /tmp/voltron/src 'convert %~ Text.toUpper'
When types have to be clarified:
% refactorio --html -t /tmp/voltron/src 'convertTo(a::LByteString).xml...name %~ Text.toUpper'
TODO: revamp after re-wiring
Try these on your projects:
refactorio --haskell _Module.biplate._ModuleName.end
refactorio --haskell _Module.biplate._Int.end
refactorio --haskell _Module.biplate._String.end
refactorio --haskell _Module.biplate._FieldUpdate.end
refactorio --haskell _Module.biplate._Frac.end
refactorio --haskell '_Module.biplate._Int.filtered(odd.view target).end'
refactorio --haskell '_Module.biplate._Int.filtered(even.view target).end'
refactorio --haskell '_Module.biplate._Int.filtered((>10).view target).end'
There are more examples here.
For now the easiest way to get it working is to stack install .
in this
directory. Then commands like this should work:
refactorio --json 'key "foo" . key "bar" . _Number *~ 15' -t ./examples
or
cat Somefile.hs | refactorio -s --haskell _Module.biplate._ModuleName.end -
(where the -t
/--target
is a file or directory to process and can be outside
of the refactorio project root).
In addition to the general warning about this being alpha quality software, no work has been done on performance and in fact in some cases even reasonable performance has been sacrificed in the name of expediency.
In some places I've resorted to outright hacks, such as when parsing YAML by
reading it as YAML, writing it as JSON for processing it with lens-aeson
,
then converting it back.
In addition to being slow some of these tricks will do things like cause a file to be reformatted by the serialization round-trip even if your function doesn't make an actual semantic change.
Refactorio has a hardcoded set of qualified imports which you can find somewhere around here.
Currently the only unqualified import that Refactorio mandates is
Text.Regex.Quote
which brings the r
QuasiQuoter into scope.
In addition Refactorio attempts to load a single "Prelude" which can bring
unqualified names into scope. Various lens packages for different formats have
conflicting imports (eg. _String in lens-aeson
and in
haskell-src-exts-prisms
) so to avoid conflicts for now if you pass a "special
mode" flag (eg. --haskell
, --json
, etc) Refactorio will attempt to use
Refactorio.Prelude.{ModeName}
(eg Refactorio.Prelude.Haskell
, etc). If no
special mode is provided, Refactorio.Prelude.Basic
is used.