From 2997ef7c1a2228641b6655e6b4e4cae650870394 Mon Sep 17 00:00:00 2001 From: Elizabeth Mattijsen Date: Sat, 24 Sep 2022 20:32:58 +0200 Subject: [PATCH] 0.0.97 --- Changes | 7 +- META6.json | 4 +- README.md | 126 +++++++++++++--- doc/App-Rak.rakudoc | 138 +++++++++++++++--- lib/App/Rak.rakumod | 267 +++++++++++++++++++++------------- resources/help.txt | 5 +- resources/help/filesystem.txt | 2 + resources/help/general.txt | 1 + resources/help/listing.txt | 2 +- 9 files changed, 409 insertions(+), 143 deletions(-) diff --git a/Changes b/Changes index 34cfd0a..fe86313 100644 --- a/Changes +++ b/Changes @@ -1,8 +1,10 @@ Revision history for App-Rak {{$NEXT}} + +0.0.97 2022-09-24T20:30:23+02:00 - Add extensions groups for #cro and #html - - Bump dependency on rak to get better --under-version-control semantics + - Bump dependency on rak for various fixes - Bump dependency on String::Utils to get selective importing - Bump dependency on as-cli-arguments to get Pair support - Complete rewrite of argument handling. Instead of feeding the @@ -15,11 +17,14 @@ Revision history for App-Rak - Add support for --proximate - Add support for --human back in - Add support for --json-per-elem + - Add support for --dont-catch - Change dependency from JSON::Fast to JSON::Fast::Hyper to allow for hypering --json-per-elem - Fix issue with using a regex and --edit - Bump dependency on META::constants for more resiliency - Bump dependency on CLI::Version for more resiliency + - Document the --exec and --shell options + - Document --only-first properly (instead of as --first-only) 0.0.96 2022-09-12T12:07:41+02:00 - Fix snafu with argument parsing rework bleeding into the distribution diff --git a/META6.json b/META6.json index 1be3a78..b317425 100644 --- a/META6.json +++ b/META6.json @@ -14,7 +14,7 @@ "highlighter:ver<0.0.14>:auth", "JSON::Fast::Hyper:ver<0.0.3>:auth", "META::constants:ver<0.0.3>:auth", - "rak:ver<0.0.24>:auth", + "rak:ver<0.0.25>:auth", "String::Utils:ver<0.0.12>:auth" ], "description": "21st century grep / find / ack / ag / rg on steroids", @@ -55,5 +55,5 @@ ], "test-depends": [ ], - "version": "0.0.96" + "version": "0.0.97" } diff --git a/README.md b/README.md index cae9615..ab4983f 100644 --- a/README.md +++ b/README.md @@ -196,8 +196,8 @@ Indicate the number of lines that should be shown **around** any line that match Flag. Indicate whether just the number of lines with matches should be calculated. When specified with a `True` value, will show a "N matches in M files" by default, and if the `:files-with-matches` (or `files-without matches`) option is also specified with a `True` value, will just show total counts. See also `stats-only`. ---created ---------- +--created=condition +------------------- If specified, indicates the `Callable` that should return True to include a file in the selection of files to be checked. The creation time of the file (number of seconds since epoch, as a `Num` value) will be passed as the only argument. @@ -227,6 +227,11 @@ If specified, indicates the `Callable` that should return True to include a file If specified, indicates the `Callable` that should return True to have a directory be included for further recursions in file selection. The basename of the directory will be passed as the only argument. Defaults to all directories that do not start with a period. Can specify as a flag to include **all** directories for recursion. +--dont-catch +------------ + +Flag. If specified with a trueish value, will **not** catch any error during processing, but will throw any error again. Defaults to `False`, making sure that errors **will** be caught. Mainly intended for debugging and error reporting. + --dryrun -------- @@ -247,16 +252,21 @@ Only applicable if `--csv-per-line` has been specified. Indicate a line ending d Only applicable if `--csv-per-line` has been specified. Indicates the escape character to be used to escape characters in a field. Defaults to **double quote**. +--exec=invocation +----------------- + +If specified, indicates the name of a program and its arguments to be executed. Any `$_` in the invocation string will be replaced by the file being checked. The file will be included if the program runs to a successful conclusion. + --extensions=spec ----------------- Indicate the extensions of the filenames that should be inspected. By default, no limitation on filename extensions will be done. -Extensions can be specified as a comma-separated list, or one of the predefined groups, indicated by `#name`. +Extensions can be specified as a comma-separated list of either a a predefined group of extensions (indicated by `#name`), or a single extension. ```bash -# inspect files with extensions used by Raku -$ rak foo --extensions=#raku +# inspect files with extensions used by Raku and Perl +$ rak foo --extensions=#raku,#perl # inspect files with Markdown content $ rak foo --extensions=md,markdown @@ -265,7 +275,9 @@ $ rak foo --extensions=md,markdown $ rak foo --extensions= ``` -Predefined groups are `#raku`, `#perl`, `#c`, `#c++`, `#yaml`, <#ruby> `#python`, `#markdown` and `#text`. +Predefined groups are `#raku`, `#perl`, `#cro`, `#text` `#c`, `#c++`, `#yaml`, `#ruby`, `#python`, `#html`, `#markdown`, `#json`, `#jsonl`, `#csv`, `#config` and `#text`. + +The `--list-known-extensions` argument can be used to see which predefined groups of extensions are supported, and which extensions they cover. --file=condition ---------------- @@ -312,10 +324,10 @@ Flag. If specified with a true value, will **not** look at the contents of the s Flag. If specified with a true value, will override any file or directory filter settings and include all possible files for inspection. ---first-only[=N] +--only-first[=N] ---------------- -Indicate the number of matches to show. If specified without a value, will default to **1**. Defaults to show all possible matches. +Indicate the **overall** number of matches to show. If specified without a value, will default to **1**. Defaults to **1000** if a human is watching, otherwise defaults to returning all possible matches. --formula=[none] ---------------- @@ -348,6 +360,8 @@ $ rak --find --gid='* > 20' $ rak --find --gid=20 ``` +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --group=condition ----------------- @@ -368,6 +382,8 @@ $ rak --find --group=staff $ rak --find --group='!staff' ``` +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --group-matches --------------- @@ -383,11 +399,15 @@ If specified, indicates the `Callable` that should return True to include a file Flag. If specified with a trueish value, will only select files that do have the SETGID bit set in their attributes. Use negation `--/has-setgid` to only select files that do **not** have the SETGID bit set. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --has-setuid ------------ Flag. If specified with a trueish value, will only select files that do have the SETUID bit set in their attributes. Use negation `--/has-setuid` to only select files that do **not** have the SETUID bit set. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --help [area-of-interest] ------------------------- @@ -436,6 +456,11 @@ Indicate the string that should be used at the end of the pattern found in a lin Indicate the string that should be used at the end of the pattern found in a line. Specifying implies `--highlight`ing implicitly. If `highlight` is explicitely specified with a trueish value, will default to the terminal code to start **bold**. +--human +------- + +Flag. Indicate that search results should be presented in a human readable manner. This means: filenames shown on a separate line, line numbers shown, and highlighting performed. Defaults to `True` if `STDOUT` is a TTY (aka, someone is actually watching the search results), otherwise defaults to `False`. + --ignorecase ------------ @@ -451,6 +476,8 @@ Flag. If specified with a trueish value, indicates that any matching should be d If specified, indicates the `Callable` that should return True to include a file in the selection of files to be checked. The inode number of the file on the filesystem, will be passed as the only argument. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --invert-match -------------- @@ -474,46 +501,64 @@ Flag. If specified with a trueish value, will only select files that do not cont Flag. If specified with a trueish value, will only select files that can be executed by the current user. Use negation `--/is-executable` to only select files that are **not** executable by the current user. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-group-executable --------------------- Flag. If specified with a trueish value, will only select files that can be executed by members of the group of the owner. Use negation `--/is-group-executable` to only select files that are **not** executable by the members of the group of the owner. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-group-readable ------------------- Flag. If specified with a trueish value, will only select files that can be read by members of the group of the owner. Use negation `--/is-group-readable` to only select files that are **not** readable by the members of the group of the owner. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-group-writable ------------------- Flag. If specified with a trueish value, will only select files that can be written to by members of the group of the owner. Use negation `--/is-group-writable` to only select files that are **not** writable by the members of the group of the owner. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-owned-by-group ------------------- Flag. If specified with a trueish value, will only select files that are owned by the group of the current user. Use negation `--/is-owned-by-group` to only select files that are **not** owned by the group of the current user. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-owned-by-user ------------------ Flag. If specified with a trueish value, will only select files that are owned by current user. Use negation `--/is-owned-by-user` to only select files that are **not** owned by the current user. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-owner-executable --------------------- Flag. If specified with a trueish value, will only select files that can be executed by the owner. Use negation `--/is-owner-executable` to only select files that are **not** executable by the owner. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-owner-readable ------------------- Flag. If specified with a trueish value, will only select files that can be read by the owner. Use negation `--/is-owner-readable` to only select files that are **not** readable by the owner. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-owner-writable ------------------- Flag. If specified with a trueish value, will only select files that can be written to by the owner. Use negation `--/is-owner-writable` to only select files that are **not** writable by the owner. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-readable ------------- @@ -524,31 +569,52 @@ Flag. If specified with a trueish value, will only select files that can be read Flag. If specified with a trueish value, will only select files that do have the STICKY bit set in their attributes. Use negation `--/is-sticky` to only select files that do **not** have the STICKY bit set. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-symbolic-link ------------------ Flag. If specified with a trueish value, will only select files that are symbolic links. Use negation `--/is-symbolic-link` to only select files that are **not** symbolic links. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-world-executable --------------------- Flag. If specified with a trueish value, will only select files that can be executed by anybody. Use negation `--/is-group-executable` to only select files that are **not** executable by anybody. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-world-readable ------------------- Flag. If specified with a trueish value, will only select files that can be read by anybody. Use negation `--/is-world-readable` to only select files that are **not** readable by anybody. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-world-writable ------------------- Flag. If specified with a trueish value, will only select files that can be written to by anybody. Use negation `--/is-world-writable` to only select files that can **not** be written to by anybody. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --is-writable ------------- Flag. If specified with a trueish value, will only select files that can be written to by the current user. Use negation `--/is-writable` to only select files that can **not** be written to by the current user. +--json-per-elem +--------------- + +Flag. Only makes sense if the pattern is a `Callable`. If specified with a `True` value, indicates that each selected file will be interpreted as JSON, and if valid, will then produce all elements of the outermost data structure to the pattern for introspection. If the data structure is a hash, then key/value `Pair`s will be produced. + +If the Callable returns a true value, the element will be shown. If the returned value is a string, that string will be mentioned. For example when searching the list of modules in the zef ecosystem (which consists of an array of hashes): + +```bash +$ rak '{ $_ with . }' META.json --json-per-elem +``` + --json-per-file --------------- @@ -630,18 +696,20 @@ Flag. If specified with a true value, will show all known extension groups and t Flag. Indicate whether only the matched pattern should be produced, rather than the line in which the pattern was found. Defaults to `False`. Frequently used in conjunction with `--per-file`. Will show separated by space if multiple matches are found on the same line. ---max-matches-per-file ----------------------- +--max-matches-per-file[=N] +-------------------------- -Indicate the maximum number of matches that should be produced per file. By default, will produce **all** possible matches in a file. +Indicate the maximum number of matches that should be produced per file. If specified as a flag, will assume **1** for its value. By default, will produce **all** possible matches in a file. ----meta-modified ----------------- +---meta-modified=condition +-------------------------- If specified, indicates the `Callable` that should return True to include a file in the selection of files to be checked. The modification time of meta information of the file (number of seconds since epoch, as a `Num` value) will be passed as the only argument. See "CHECKING TIMES ON FILES" for more information about features that can be used inside the `Callable`. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --mode=condition ---------------- @@ -652,8 +720,10 @@ If specified, indicates the `Callable` that should return True to include a file $ rak --find --mode='{ $_ +& 0o1000 }' ``` ---modified ----------- +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + +--modified=condition +-------------------- If specified, indicates the `Callable` that should return True to include a file in the selection of files to be checked. The modification time of the file (number of seconds since epoch, as a `Num` value) will be passed as the only argument. @@ -686,7 +756,7 @@ $ rak '{ .subst("foo","bar") if .starts-with("#") }' --modify-files Inserts this value in the file instead of the given line. The value can either be a string, or a list of strings. ---module=foo +--module=Foo ------------ Indicate the Raku module that should be loaded. Only makes sense if the pattern is executable code. @@ -696,8 +766,8 @@ Indicate the Raku module that should be loaded. Only makes sense if the pattern Indicate the path of the file in which the result of the search should be placed. Defaults to `STDOUT`. ---pager -------- +--pager=name +------------ Indicate the name (and arguments) of a pager program to be used to page through the generated output. Defaults to the `RAK_PAGER` environment variable. If that isn't specified either, then no pager program will be run. @@ -752,6 +822,11 @@ Indicate whether matching should be done per file, rather than per line. If spec $ rak foo --per-file='*.lines(:!chomp).head(10).join' ``` +--proximate=[N] +--------------- + +Indicates whether matched lines should be grouped together that are within N lines of each other. This is useful for visually picking out matches that appear close to other matches. If specified as a flag, indicates a proximation of **1**. Defaults to **0**, indication no proximation. + --quietly --------- @@ -777,6 +852,8 @@ Flag. Indicate whether directories that didn't match the `--dir` specification, Flag. Indicate whether directories that are actually symbolic links, should be recursed into. Defaults to `False`. +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --repository=dir ---------------- @@ -847,6 +924,11 @@ Flag. Indicate whether filenames should be shown. Defaults to `True`. Flag. Indicate whether line numbers should be shown. Defaults to `True`. +--shell=invocation +------------------ + +If specified, indicates the command(s) to be executed in a shell. Any `$_` in the invocation string will be replaced by the file being checked. The file will be included if the shell command(s) run to a successful conclusion. + --silently[=out,err] -------------------- @@ -885,7 +967,7 @@ Only applicable if `--csv-per-line` has been specified. Flag. If specified with Indicate the maximum size a line may have before it will be summarized. Defaults to `160` if `STDOUT` is a TTY (aka, someone is actually watching the search results), otherwise defaults to `Inf` effectively (indicating no summarization will ever occur). - * --type[=words|starts-with|ends-with|contains] + * --type=words|starts-with|ends-with|contains Only makes sense if the pattern is a string. With `words` specified, will look for pattern as a word in a line, with `starts-with` will look for the pattern at the beginning of a line, with `ends-with` will look for the pattern at the end of a line, with `contains` will look for the pattern at any position in a line. @@ -907,6 +989,8 @@ $ rak --find --uid='* > 500' $ rak --find --uid=501 ``` +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --under-version-control[=git] ----------------------------- @@ -937,10 +1021,12 @@ $ rak --find --user=liz,wendy $ rak --find --user='!liz,wendy' ``` +NOTE: support of this feature depends on Raku supporting that feature on the current operating system. + --version --------- -Flag. If the only argument, shows the name and version of the script, and the system it is running on. +Flag. If the only argument, shows the name and version of the script, and the system it is running on. Additionally specify `--verbose` to see more information. --vimgrep --------- diff --git a/doc/App-Rak.rakudoc b/doc/App-Rak.rakudoc index 8893248..644bf54 100644 --- a/doc/App-Rak.rakudoc +++ b/doc/App-Rak.rakudoc @@ -254,7 +254,7 @@ in M files" by default, and if the C<:files-with-matches> (or C) option is also specified with a C value, will just show total counts. See also C. -=head2 --created +=head2 --created=condition If specified, indicates the C that should return True to include a file in the selection of files to be checked. The creation time of the file @@ -297,6 +297,13 @@ of the directory will be passed as the only argument. Defaults to all directories that do not start with a period. Can specify as a flag to include B directories for recursion. +=head2 --dont-catch + +Flag. If specified with a trueish value, will B catch any error +during processing, but will throw any error again. Defaults to C, +making sure that errors B be caught. Mainly intended for debugging +and error reporting. + =head2 --dryrun Flag. Indicate to B actually make any changes to any content @@ -323,18 +330,26 @@ Only applicable if C<--csv-per-line> has been specified. Indicates the escape character to be used to escape characters in a field. Defaults to B. +=head2 --exec=invocation + +If specified, indicates the name of a program and its arguments to be +executed. Any C<$_> in the invocation string will be replaced by the file +being checked. The file will be included if the program runs to a successful +conclusion. + =head2 --extensions=spec Indicate the extensions of the filenames that should be inspected. By default, no limitation on filename extensions will be done. -Extensions can be specified as a comma-separated list, or one of -the predefined groups, indicated by C<#name>. +Extensions can be specified as a comma-separated list of either a +a predefined group of extensions (indicated by C<#name>), or a single +extension. =begin code :lang -# inspect files with extensions used by Raku -$ rak foo --extensions=#raku +# inspect files with extensions used by Raku and Perl +$ rak foo --extensions=#raku,#perl # inspect files with Markdown content $ rak foo --extensions=md,markdown @@ -344,8 +359,12 @@ $ rak foo --extensions= =end code -Predefined groups are C<#raku>, C<#perl>, C<#c>, C<#c++>, C<#yaml>, <#ruby> -C<#python>, C<#markdown> and C<#text>. +Predefined groups are C<#raku>, C<#perl>, C<#cro>, C<#text> C<#c>, C<#c++>, +C<#yaml>, C<#ruby>, C<#python>, C<#html>, C<#markdown>, C<#json>, C<#jsonl>, +C<#csv>, C<#config> and C<#text>. + +The C<--list-known-extensions> argument can be used to see which predefined +groups of extensions are supported, and which extensions they cover. =head2 --file=condition @@ -399,10 +418,11 @@ virtual file. Flag. If specified with a true value, will override any file or directory filter settings and include all possible files for inspection. -=head2 --first-only[=N] +=head2 --only-first[=N] -Indicate the number of matches to show. If specified without a value, will -default to B<1>. Defaults to show all possible matches. +Indicate the B number of matches to show. If specified without a +value, will default to B<1>. Defaults to B<1000> if a human is watching, +otherwise defaults to returning all possible matches. =head2 --formula=[none] @@ -438,6 +458,9 @@ $ rak --find --gid=20 =end code +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --group=condition If specified, indicates the C that should return True to include a @@ -463,6 +486,9 @@ $ rak --find --group='!staff' =end code +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --group-matches Flag. Indicate whether matches of a file should be grouped together by @@ -481,12 +507,18 @@ Flag. If specified with a trueish value, will only select files that do have the SETGID bit set in their attributes. Use negation C<--/has-setgid> to only select files that do B have the SETGID bit set. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --has-setuid Flag. If specified with a trueish value, will only select files that do have the SETUID bit set in their attributes. Use negation C<--/has-setuid> to only select files that do B have the SETUID bit set. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --help [area-of-interest] Show argument documentation, possibly extended by giving the area of @@ -552,6 +584,9 @@ If specified, indicates the C that should return True to include a file in the selection of files to be checked. The inode number of the file on the filesystem, will be passed as the only argument. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --invert-match Flag. If specified with a trueish value, will negate the result of any @@ -574,6 +609,9 @@ Flag. If specified with a trueish value, will only select files that can be executed by the current user. Use negation C<--/is-executable> to only select files that are B executable by the current user. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-group-executable Flag. If specified with a trueish value, will only select files that can be @@ -581,6 +619,9 @@ executed by members of the group of the owner. Use negation C<--/is-group-executable> to only select files that are B executable by the members of the group of the owner. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-group-readable Flag. If specified with a trueish value, will only select files that can be @@ -588,6 +629,9 @@ read by members of the group of the owner. Use negation C<--/is-group-readable> to only select files that are B readable by the members of the group of the owner. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-group-writable Flag. If specified with a trueish value, will only select files that can be @@ -595,6 +639,9 @@ written to by members of the group of the owner. Use negation C<--/is-group-writable> to only select files that are B writable by the members of the group of the owner. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-owned-by-group Flag. If specified with a trueish value, will only select files that are @@ -602,30 +649,45 @@ owned by the group of the current user. Use negation C<--/is-owned-by-group> to only select files that are B owned by the group of the current user. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-owned-by-user Flag. If specified with a trueish value, will only select files that are owned by current user. Use negation C<--/is-owned-by-user> to only select files that are B owned by the current user. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-owner-executable Flag. If specified with a trueish value, will only select files that can be executed by the owner. Use negation C<--/is-owner-executable> to only select files that are B executable by the owner. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-owner-readable Flag. If specified with a trueish value, will only select files that can be read by the owner. Use negation C<--/is-owner-readable> to only select files that are B readable by the owner. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-owner-writable Flag. If specified with a trueish value, will only select files that can be written to by the owner. Use negation C<--/is-owner-writable> to only select files that are B writable by the owner. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-readable Flag. If specified with a trueish value, will only select files that can be @@ -638,30 +700,45 @@ Flag. If specified with a trueish value, will only select files that do have the STICKY bit set in their attributes. Use negation C<--/is-sticky> to only select files that do B have the STICKY bit set. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-symbolic-link Flag. If specified with a trueish value, will only select files that are symbolic links. Use negation C<--/is-symbolic-link> to only select files that are B symbolic links. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-world-executable Flag. If specified with a trueish value, will only select files that can be executed by anybody. Use negation C<--/is-group-executable> to only select files that are B executable by anybody. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-world-readable Flag. If specified with a trueish value, will only select files that can be read by anybody. Use negation C<--/is-world-readable> to only select files that are B readable by anybody. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-world-writable Flag. If specified with a trueish value, will only select files that can be written to by anybody. Use negation C<--/is-world-writable> to only select files that can B be written to by anybody. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --is-writable Flag. If specified with a trueish value, will only select files that can be @@ -791,12 +868,13 @@ than the line in which the pattern was found. Defaults to C. Frequently used in conjunction with C<--per-file>. Will show separated by space if multiple matches are found on the same line. -=head2 --max-matches-per-file +=head2 --max-matches-per-file[=N] Indicate the maximum number of matches that should be produced per file. -By default, will produce B possible matches in a file. +If specified as a flag, will assume B<1> for its value. By default, will +produce B possible matches in a file. -=head2 ---meta-modified +=head2 ---meta-modified=condition If specified, indicates the C that should return True to include a file in the selection of files to be checked. The modification time of meta @@ -806,6 +884,9 @@ will be passed as the only argument. See "CHECKING TIMES ON FILES" for more information about features that can be used inside the C. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --mode=condition If specified, indicates the C that should return True to include a @@ -819,7 +900,10 @@ $ rak --find --mode='{ $_ +& 0o1000 }' =end code -=head2 --modified +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + +=head2 --modified=condition If specified, indicates the C that should return True to include a file in the selection of files to be checked. The modification time of the @@ -863,7 +947,7 @@ $ rak '{ .subst("foo","bar") if .starts-with("#") }' --modify-files Inserts this value in the file instead of the given line. The value can either be a string, or a list of strings. -=head2 --module=foo +=head2 --module=Foo Indicate the Raku module that should be loaded. Only makes sense if the pattern is executable code. @@ -873,7 +957,7 @@ pattern is executable code. Indicate the path of the file in which the result of the search should be placed. Defaults to C. -=head2 --pager +=head2 --pager=name Indicate the name (and arguments) of a pager program to be used to page through the generated output. Defaults to the C environment @@ -946,7 +1030,7 @@ $ rak foo --per-file='*.lines(:!chomp).head(10).join' =end code -==head2 --proximate=[N] +=head2 --proximate=[N] Indicates whether matched lines should be grouped together that are within N lines of each other. This is useful for visually picking out matches that @@ -982,6 +1066,9 @@ encountered. Defaults to C. Flag. Indicate whether directories that are actually symbolic links, should be recursed into. Defaults to C. +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --repository=dir Indicate the directory that should be searched for Raku module loading. @@ -1061,6 +1148,12 @@ Flag. Indicate whether filenames should be shown. Defaults to C. Flag. Indicate whether line numbers should be shown. Defaults to C. +=head2 --shell=invocation + +If specified, indicates the command(s) to be executed in a shell. Any C<$_> +in the invocation string will be replaced by the file being checked. The +file will be included if the shell command(s) run to a successful conclusion. + =head2 --silently[=out,err] Flag and option. Only applicable if the pattern is a C. Indicates @@ -1103,7 +1196,7 @@ Defaults to C<160> if C is a TTY (aka, someone is actually watching the search results), otherwise defaults to C effectively (indicating no summarization will ever occur). -=item --type[=words|starts-with|ends-with|contains] +=item --type=words|starts-with|ends-with|contains Only makes sense if the pattern is a string. With C specified, will look for pattern as a word in a line, with C will @@ -1134,6 +1227,9 @@ $ rak --find --uid=501 =end code +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --under-version-control[=git] Indicate whether to only select files that are under some form of version @@ -1171,10 +1267,14 @@ $ rak --find --user='!liz,wendy' =end code +NOTE: support of this feature depends on Raku supporting that feature on +the current operating system. + =head2 --version Flag. If the only argument, shows the name and version of the script, and -the system it is running on. +the system it is running on. Additionally specify C<--verbose> to see more +information. =head2 --vimgrep diff --git a/lib/App/Rak.rakumod b/lib/App/Rak.rakumod index 7183011..511e769 100644 --- a/lib/App/Rak.rakumod +++ b/lib/App/Rak.rakumod @@ -1,10 +1,11 @@ + # The modules that we need here, with their full identities use as-cli-arguments:ver<0.0.6>:auth; # as-cli-arguments use Edit::Files:ver<0.0.4>:auth; # edit-files use has-word:ver<0.0.3>:auth; # has-word use highlighter:ver<0.0.14>:auth; # columns highlighter matches use JSON::Fast::Hyper:ver<0.0.3>:auth; # from-json to-json -use rak:ver<0.0.24>:auth; # rak +use rak:ver<0.0.25>:auth; # rak use String::Utils:ver<0.0.12>:auth ; # The epoch value when process started @@ -15,9 +16,9 @@ my constant BON = "\e[1m"; # BOLD ON my constant BOFF = "\e[22m"; # BOLD OFF #- start of available options -------------------------------------------------- -#- Generated on 2022-09-22T19:43:49+02:00 by tools/makeOPTIONS.raku +#- Generated on 2022-09-24T19:33:44+02:00 by tools/makeOPTIONS.raku #- PLEASE DON'T CHANGE ANYTHING BELOW THIS LINE -my str @options = ; +my str @options = ; #- PLEASE DON'T CHANGE ANYTHING ABOVE THIS LINE #- end of available options ---------------------------------------------------- @@ -26,6 +27,7 @@ my constant %falsies = # our own changed => 'meta-modified', run => 'exec', + first-only => 'only-first', # from ack A => 'after-context', @@ -122,7 +124,8 @@ my sub seconds($format) { } # Make sure we remember if there's a human watching (terminal connected) -my $isa-tty := $*OUT.t; +my $reading-from-stdin := !$*IN.t; +my $writing-to-stdout := $*OUT.t; # Set up default extension sets my constant %exts = @@ -150,6 +153,10 @@ my constant @known-extensions = %exts.values.flat.unique.sort; # Place to keep tagged configurations my $config-file := $*HOME.add('.rak-config.json'); +# Links to optional classes +my $GitBlameFile; +my $TextCSV; + # Variables for grouping options given my $verbose; # process verbose my $pager; # process pager if defined @@ -314,6 +321,21 @@ my sub main(@ARGS) is export { exit; } + # STDIN sanity checking + if @positionals && @positionals.head eq '-' { + if $reading-from-stdin { + @positionals.shift; + meh "Cannot specify additional paths when reading from STDIN" + if @positionals; + } + else { + $reading-from-stdin := True; + } + } + elsif @positionals == 1 && @positionals.head.IO.f { + %listing := False if %result:!exists; + } + # Perform the actual action $action-for ?? ::("&action-$action-for")() !! action-per-line(); $*OUT.close if $pager; @@ -501,7 +523,7 @@ my sub HELP($text, @keys, :$verbose) { my $header := "$SCRIPT - " ~ DESCRIPTION; say $header; say "-" x $header.chars; - say $isa-tty + say $writing-to-stdout ?? $text.lines.map({ !.starts-with(" ") && .ends-with(":") ?? BON ~ $_ ~ BOFF !! $_ }).join("\n") @@ -541,7 +563,7 @@ TEXT # Show the results my sub rak-results() { - my $human := %listing:delete // $isa-tty; + my $human := %listing:delete // $writing-to-stdout; my $show-filename := %listing:delete // True; my $break := %listing:delete; my $group-matches := %listing:delete; @@ -552,11 +574,11 @@ my sub rak-results() { # Set up human defaults if $human { - $break := "" unless $break.defined; - $group-matches := True unless $group-matches.defined; - $highlight := True unless $highlight.defined; - $trim := True unless $trim.defined; - $only-first := 10000 unless $only-first.defined; + $break := "" unless $break.defined; + $group-matches := True unless $group-matches.defined; + $highlight := True unless $highlight.defined; + $trim := True unless $trim.defined; + $only-first := 1000 unless $only-first.defined; $proximate := 1 if !$proximate.defined && $group-matches; } my $has-break := %listing:delete // $break.defined; @@ -914,8 +936,28 @@ my sub external-execution(str $name, $value --> Nil) { !! (%filesystem{$name} := $value); } +# check Text::CSV availability +my sub check-TextCSV(str $name) { + unless $TextCSV { + CATCH { meh-not-installed 'Text::CSV', $name } + require Text::CSV; + $TextCSV := Text::CSV; + } +} + +# check Git::Blame::File availability +my sub check-GitBlameFile(str $name) { + unless $GitBlameFile { + CATCH { meh-not-installed 'Git::Blame::File', $name } + require Git::Blame::File; + $GitBlameFile := Git::Blame::File; + } +} + # handle additional CSV parameters my sub set-csv-flag(str $name, $value --> Nil) { + check-TextCSV($name); + Bool.ACCEPTS($value) ?? (%csv{$name} := $value) !! meh("'--$name' can only be specified as a flag"); @@ -954,6 +996,14 @@ my sub set-result-Int(str $name, $value --> Nil) { ?? (%result{$name} := $integer) !! meh "'--$name' can only be an integer value, not '$value'"; } +my sub set-result-flag-or-Int(str $name, $value --> Nil) { + with $value.Int { + %result{$name} := $_; + } + else { + meh "'--$name' must either be an integer or a flag"; + } +} # Set listing options my sub set-listing-flag(str $name, $value --> Nil) { @@ -1030,14 +1080,12 @@ my sub option-before-context($value --> Nil) { } my sub option-blame-per-file($value --> Nil) { - CATCH { meh-not-installed 'Git::Blame::File', 'blame-per-file' } - require Git::Blame::File; + check-GitBlameFile('blame-per-file'); set-action('blame-per-file', $value); } my sub option-blame-per-line($value --> Nil) { - CATCH { meh-not-installed 'Git::Blame::File', 'blame-per-line' } - require Git::Blame::File; + check-GitBlameFile('blame-per-line'); set-action('blame-per-line', $value); } @@ -1073,18 +1121,8 @@ my sub option-created($value --> Nil) { } my sub option-csv-per-line($value --> Nil) { - CATCH { meh-not-installed 'Text::CSV', 'csv-per-line' } - require Text::CSV; + check-TextCSV('csv-per-line'); set-action('csv-per-line', $value); -# -# setup-producer 'csv-per-line', 'produce-many', { -# %args //= True; -# my $csv := Text::CSV.new(|%csv); -# -# %rak := codify-extensions %exts<#csv> -# unless %rak; -# -> $io { $csv.getline-all($io.open) } -# } } my sub option-degree($value --> Nil) { @@ -1101,6 +1139,10 @@ my sub option-dir($value --> Nil) { !! convert-to-matcher($value); } +my sub option-dont-catch($value --> Nil) { + set-rak-flag('dont-catch', $value); +} + my sub option-dryrun($value --> Nil) { Bool.ACCEPTS($value) ?? (%modify := $value) @@ -1118,8 +1160,7 @@ my sub option-encoding($value --> Nil) { } my sub option-eol($value --> Nil) { - CATCH { meh-not-installed 'Text::CSV', 'eol' } - require Text::CSV; + check-TextCSV('eol'); my constant %line-endings = lf => "\n", @@ -1380,7 +1421,7 @@ my sub option-matches-only($value --> Nil) { } my sub option-max-matches-per-file($value --> Nil) { - set-result-Int('max-matches-per-file', $value); + set-result-flag-or-Int('max-matches-per-file', $value); } my sub option-meta-modified($value --> Nil) { @@ -1514,19 +1555,8 @@ my sub option-shell($value --> Nil) { } my sub option-show-blame($value --> Nil) { - CATCH { meh-not-installed 'Git::Blame::File', 'show-blame' } - require Git::Blame::File; - set-listing-flag('show-blame', $value); -# -# setup-mapper 'show-blame', -> $source, @matches { -# my @line-numbers = @matches.map: *.key; -# with Git::Blame::File.new($source, :@line-numbers) -> $blamer { -# $source => $blamer.lines.Slip -# } -# else { -# $source => @matches.map({ .key ~ ':' ~ .value }).Slip -# } -# } + check-GitBlameFile('show-blame'); + set-result-flag('show-blame', $value); } my sub option-show-filename($value --> Nil) { @@ -1637,6 +1667,10 @@ my sub meh-filesystem($name --> Nil) is hidden-from-backtrace { meh-what($name, %filesystem, 'filesystem'); } +my sub meh-modify($name --> Nil) is hidden-from-backtrace { + meh-what($name, %modify, 'modify') +} + my sub meh-result($name --> Nil) is hidden-from-backtrace { meh-what($name, %result, 'result'); } @@ -1660,7 +1694,12 @@ my sub maybe-meh-together(*@keys) is hidden-from-backtrace { } my sub move-filesystem-options-to-rak(--> Nil) { - if %filesystem { + if $reading-from-stdin { + meh "Cannot use &mm(%filesystem.keys.sort) when reading from STDIN" + if %filesystem; + %listing := False; + } + elsif %filesystem { if %filesystem { maybe-meh-together 'under-version-control', %filesystem< dir file recurse-symlinked-dir recurse-unmatched-dir @@ -1768,10 +1807,24 @@ my sub move-result-options-to-rak(--> Nil) { if %result:delete { %rak := True; %rak := True; + + # Only interested in number of files + if %result:delete { + my int $seen; + %rak := True; + %rak := -> $, @files --> Empty { + LAST sayer $seen == 0 + ?? "No files" + !! $seen == 1 + ?? "One file" + !! "$seen files"; + $seen = @files.elems; + } + } } # Only interested in number of matches / files - if %result:delete { + elsif %result:delete { my @files; %rak := True; %rak := -> $io, @matches --> Empty { @@ -1790,6 +1843,19 @@ my sub move-result-options-to-rak(--> Nil) { @files.push: Pair.new: $io.relative, @matches.elems; } } + + # Wanna blame + elsif %result:delete { + %rak := -> $io, @matches { + my @line-numbers = @matches.map: *.key; + with $GitBlameFile.new($io, :@line-numbers) -> $blamer { + $io => $blamer.lines.Slip + } + else { + $io => @matches.map({ .key ~ ':' ~ .value }).Slip + } + } + } } %rak ,= %result; @@ -1815,15 +1881,17 @@ my sub activate-output-options() { # up during option processing. my sub action-blame-per-file(--> Nil) { - meh-for 'blame-per-file', ; + meh-for 'blame-per-file', ; prepare-needle; + %filesystem := True; move-filesystem-options-to-rak; - %rak := 1; - %rak := -> $io { - try Git::Blame::File.new($io) - } + %listing := False if %listing:!exists; + %listing := False if %listing:!exists; + + %rak := 1; + %rak := -> $io { $GitBlameFile.new($io) } activate-output-options; run-rak; @@ -1832,15 +1900,15 @@ my sub action-blame-per-file(--> Nil) { } my sub action-blame-per-line(--> Nil) { - meh-for 'blame-per-line', ; + meh-for 'blame-per-line', ; prepare-needle; + %filesystem := True; move-filesystem-options-to-rak; - %rak := 1; - %rak := -> $io { - (try Git::Blame::File.new($io).lines) // Empty - } + %rak := 1; + %rak := True; + %rak := -> $io { $GitBlameFile.new($io).lines } activate-output-options; run-rak; @@ -1849,7 +1917,7 @@ my sub action-blame-per-line(--> Nil) { } my sub action-checkout(--> Nil) { - meh-for 'checkout', ; + meh-for 'checkout', ; prepare-needle; @@ -1906,23 +1974,16 @@ my sub action-checkout(--> Nil) { } my sub action-csv-per-line(--> Nil) { + meh-for 'csv-per-line', ; prepare-needle; - %filesystem //= codify-extensions %exts<#csv>; + %filesystem //= codify-extensions %exts<#csv> + unless $reading-from-stdin; move-filesystem-options-to-rak; - if %listing:delete { - # no action needed - } - elsif %listing:delete { - %rak := True; - } - else { - %rak := True; - } - %csv := True unless %csv:exists; - my $csv := Text::CSV.new(|%csv); + my $csv := $TextCSV.new(|%csv); + %csv = (); %rak := -> $io { $csv.getline-all($io.open) } activate-output-options; @@ -1935,7 +1996,7 @@ my sub action-edit(--> Nil) { %rak := $_ with %result:delete; - meh-for 'edit', ; + meh-for 'edit', ; prepare-needle; move-filesystem-options-to-rak; @@ -1957,12 +2018,14 @@ my sub action-edit(--> Nil) { %rak := -> $source, @matches --> Empty { LAST edit-files @files, :$editor; - my $path := $source.relative; + my $path := $source.relative; + my $target := Regex.ACCEPTS($needle) ?? $needle !! $pattern; + @files.append: @matches.map: { - $path => .key => columns( - .value, $pattern, + $path => .key => (columns( + .value, $target, :$ignorecase, :$ignoremark, |(:$type if $type) - ).head + ).head // 0) } } } @@ -1980,22 +2043,13 @@ my sub action-help(--> Nil) { } my sub action-json-per-file(--> Nil) { - meh-for 'json-per-file', ; + meh-for 'json-per-file', ; prepare-needle; - %filesystem //= codify-extensions %exts<#json>; + %filesystem //= codify-extensions %exts<#json> + unless $reading-from-stdin; move-filesystem-options-to-rak; - if %listing:delete { - # no action needed - } - elsif %listing:delete { - %rak := True; - } - else { - %rak := True; - } - my $enc := %rak:delete // 'utf8-c8'; %rak := -> $io { try from-json $io.slurp(:$enc) } @@ -2006,17 +2060,24 @@ my sub action-json-per-file(--> Nil) { } my sub action-json-per-elem(--> Nil) { - meh-for 'json-per-elem', ; + meh-for 'json-per-elem', ; prepare-needle; - %filesystem //= codify-extensions %exts<#json>; + %filesystem //= codify-extensions %exts<#json> + unless $reading-from-stdin; move-filesystem-options-to-rak; if %listing:delete { # no action needed } elsif %listing:delete { - %rak := True; + %rak := True; + } + elsif %result:delete { + %rak := True; + } + elsif %result:delete { + %rak := True; } else { %rak := True; @@ -2036,17 +2097,24 @@ my sub action-json-per-elem(--> Nil) { } my sub action-json-per-line(--> Nil) { - meh-for 'json-per-line', ; + meh-for 'json-per-line', ; prepare-needle; - %filesystem //= codify-extensions %exts<#jsonl>; + %filesystem //= codify-extensions %exts<#jsonl> + unless $reading-from-stdin; move-filesystem-options-to-rak; if %listing:delete { # no action needed } elsif %listing:delete { - %rak := True; + %rak := True; + } + elsif %result:delete { + %rak := True; + } + elsif %result:delete { + %rak := True; } else { %rak := True; @@ -2066,7 +2134,7 @@ my sub action-json-per-line(--> Nil) { } my sub action-list-custom-options(--> Nil) { - meh-for 'list-custom-options', ; + meh-for 'list-custom-options', ; activate-output-options; my $format := '%' ~ %config.keys>>.chars.max ~ 's: %s'; @@ -2076,7 +2144,7 @@ my sub action-list-custom-options(--> Nil) { } my sub action-list-known-extensions(--> Nil) { - meh-for 'list-known-extensions', ; + meh-for 'list-known-extensions', ; activate-output-options; printf("%9s: %s\n", .key, .value.map({$_ || '(none)'}).Str) @@ -2166,7 +2234,7 @@ my sub action-modify-files(--> Nil) { } my sub action-per-file(--> Nil) { - meh-for 'per-file', ; + meh-for 'per-file', ; prepare-needle; move-filesystem-options-to-rak; @@ -2182,7 +2250,7 @@ my sub action-per-file(--> Nil) { } my sub action-per-line(--> Nil) { - meh-for 'per-line', ; + meh-for 'per-line', ; prepare-needle; move-filesystem-options-to-rak; @@ -2206,20 +2274,21 @@ my sub action-vimgrep(--> Nil) { %rak := $_ with %result:delete; - meh-for 'vimgrep', ; + meh-for 'vimgrep', ; prepare-needle; move-filesystem-options-to-rak; %rak := -> $source, @matches --> Empty { - my $path := $source.relative; + my $path := $source.relative; + my $target := Regex.ACCEPTS($needle) ?? $needle !! $pattern; sayer $path ~ ':' ~ .key - ~ ':' ~ columns( - .value, $pattern, + ~ ':' ~ (columns( + .value, $target, :$ignorecase, :$ignoremark, |(:$type if $type) - ).head + ).head // "0") ~ ':' ~ .value for @matches; } diff --git a/resources/help.txt b/resources/help.txt index 8f24084..36522cc 100644 --- a/resources/help.txt +++ b/resources/help.txt @@ -48,6 +48,7 @@ Filesystem filters: --blocks Number of filesystem blocks used by file --created=expression Check on epoch when file was created --device-number Number of device on which file is located + --exec Run program, include if successful --filesize Number of bytes of data used by file --hard-links Number of hard-links to file on filesystem --inode Inode number of file on filesystem @@ -72,6 +73,7 @@ Filesystem filters: --meta-modified=expr Check on epoch when meta-info of file was last changed --mode The full mode value of the file --modified=expression Check on epoch when file was last changed + --shell Run shell command, include if successful --user=selector File is owned by user names / expression --uid=expression File is owned by given uid @@ -106,9 +108,9 @@ Listing modifiers: --highlight-before=xxx String to put before match --highlight-after=yyy String to put after match --proximate[=N] Grouping of matched lines + --only-first[=N] Show only the N results, N defaults to 1 --output-file=filename Send output to indicated file (default: STDOUT) --pager Pager to use (default: ACK_PAGER // none) - --first-only[=N] Show only the N results, N defaults to 1 --stats Also show statistics Content modifiers: @@ -137,6 +139,7 @@ General options: pattern | string | code | haystack | result | listing content | resource | special | option | general | philosophy examples + --dont-catch Let code exceptions be thrown with backtrace --list-known-extensions Show all known extensions and their groups --version Show version information --verbose Be more verbose, if applicable diff --git a/resources/help/filesystem.txt b/resources/help/filesystem.txt index 09487f0..55e62e1 100644 --- a/resources/help/filesystem.txt +++ b/resources/help/filesystem.txt @@ -3,6 +3,7 @@ Filesystem filters: --blocks Number of filesystem blocks used by file --created=expression Check on epoch when file was created --device-number Number of device on which file is located + --exec Run program, include if successful --filesize Number of bytes of data used by file --hard-links Number of hard-links to file on filesystem --inode Inode number of file on filesystem @@ -27,5 +28,6 @@ Filesystem filters: --meta-modified=expr Check on epoch when meta-info of file was last changed --mode The full mode value of the file --modified=expression Check on epoch when file was last changed + --shell Run shell command, include if successful --user=selector File is owned by user names / expression --uid=expression File is owned by given uid diff --git a/resources/help/general.txt b/resources/help/general.txt index 32ac1c1..bed8f53 100644 --- a/resources/help/general.txt +++ b/resources/help/general.txt @@ -4,6 +4,7 @@ General options: pattern | string | code | haystack | result | listing content | resource | edit | option | general | philosophy examples + --dont-catch Let code exceptions be thrown with backtrace --list-known-extensions Show all known extensions and their groups --version Show version information --verbose Be more verbose, if applicable diff --git a/resources/help/listing.txt b/resources/help/listing.txt index d527521..0c1be2f 100644 --- a/resources/help/listing.txt +++ b/resources/help/listing.txt @@ -12,7 +12,7 @@ Listing modifiers: --highlight-before=xxx String to put before match --highlight-after=yyy String to put after match --proximate[=N] Grouping of matched lines + --only-first[=N] Show only the N results, N defaults to 1 --output-file=filename Send output to indicated file (default: STDOUT) --pager Pager to use (default: ACK_PAGER // none) - --first-only[=N] Show only the N results, N defaults to 1 --stats Also show statistics