diff --git a/README.md b/README.md index 52979eb..72ea5c7 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ $ rak foo --files-with-matches $ rak foo --before=2 --after=2 # lines with foo AND bar -$ rak '{.contains("foo") && .contains("bar")}' +$ rak foo --and=bar ``` DESCRIPTION @@ -104,7 +104,7 @@ pattern The pattern to search for, if no pattern was specified any other way. -Patterns can also be specified with the `--pattern`, `--and`, `--or`, `--not` and `--patterns-from` options, in which case **all** other positional arguments are considered to be path specifications. +Patterns can also be specified with the `--pattern`, `--not` and `--patterns-from` options, in which case **all** other positional arguments are considered to be path specifications. path(s) ------- @@ -374,6 +374,16 @@ Only applicable if `--csv-per-line` has been specified. Flag. If specified, indi Flag. If specified with a True value, will accept compressed files with the `.gz` (gzip) or `.bz2` (bzip2) extension, if the extension was otherwise acceptable. Will automatically decompress files for inspection. +--and=foo +--------- + +Specify **additional** pattern that should match as well as any previously specified pattern to produce a full match. + +--andnot=foo +------------ + +Specify **additional** pattern that should **not** match as well as any previously specified pattern to produce a full match. + --auto-diag ----------- @@ -1255,6 +1265,21 @@ Inserts this value in the file instead of the given line. The value can either b Indicate the Raku module that should be loaded. Only makes sense if the pattern is a `Callable`. +--not=foo +--------- + +Specify **primary** pattern that should **not** match to produce a hit. + +--or=foo +-------- + +Specify **additional** pattern that should match to produce a hit. + +--ornot=foo +----------- + +Specify **additional** pattern that should **not** match to produce a hit. + --output-dir=directory ---------------------- diff --git a/doc/App-Rak.rakudoc b/doc/App-Rak.rakudoc index 35b8951..87fb29c 100644 --- a/doc/App-Rak.rakudoc +++ b/doc/App-Rak.rakudoc @@ -24,7 +24,7 @@ $ rak foo --files-with-matches $ rak foo --before=2 --after=2 # lines with foo AND bar -$ rak '{.contains("foo") && .contains("bar")}' +$ rak foo --and=bar =end code @@ -149,9 +149,9 @@ C<--ignorecase> or C<--ignoremark> arguments will be honoured. The pattern to search for, if no pattern was specified any other way. -Patterns can also be specified with the C<--pattern>, C<--and>, C<--or>, -C<--not> and C<--patterns-from> options, in which case B -other positional arguments are considered to be path specifications. +Patterns can also be specified with the C<--pattern>, C<--not> and +C<--patterns-from> options, in which case B other positional +arguments are considered to be path specifications. =head2 path(s) @@ -485,6 +485,16 @@ Flag. If specified with a True value, will accept compressed files with the C<.gz> (gzip) or C<.bz2> (bzip2) extension, if the extension was otherwise acceptable. Will automatically decompress files for inspection. +=head2 --and=foo + +Specify B pattern that should match as well as any previously +specified pattern to produce a full match. + +=head2 --andnot=foo + +Specify B pattern that should B match as well as any +previously specified pattern to produce a full match. + =head2 --auto-diag Only applicable if C<--csv-per-line> has been specified. Flag. If @@ -1597,6 +1607,18 @@ either be a string, or a list of strings (which would add lines to the file). Indicate the Raku module that should be loaded. Only makes sense if the pattern is a C. +=head2 --not=foo + +Specify B pattern that should B match to produce a hit. + +=head2 --or=foo + +Specify B pattern that should match to produce a hit. + +=head2 --ornot=foo + +Specify B pattern that should B match to produce a hit. + =head2 --output-dir=directory Specify the name of the directory to store the results in. For each group, diff --git a/lib/App/Rak.rakumod b/lib/App/Rak.rakumod index a1669bd..fa5c7a3 100644 --- a/lib/App/Rak.rakumod +++ b/lib/App/Rak.rakumod @@ -26,9 +26,9 @@ my constant BON = "\e[1m"; # BOLD ON my constant BOFF = "\e[22m"; # BOLD OFF #- start of available options -------------------------------------------------- -#- Generated on 2024-08-04T16:31:51+02:00 by tools/makeOPTIONS.raku +#- Generated on 2024-08-16T19:20:19+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 ---------------------------------------------------- @@ -243,6 +243,7 @@ my $debug-rak; # process show rak args my @patterns; # the specified patterns (if any) my @highlights; # the pattern used for highlighting +my $seen-initial-pattern; # flag, --pattern/--patterns-from seen my $matches-only; # whether to produce matches only my $smartcase; # --smartcase my $smartmark; # --smartmark @@ -377,11 +378,14 @@ my sub add-highlight($_) { } # Helper sub to add a pattern -my sub add-pattern($pattern --> Nil) { +my sub add-pattern($pattern, :$first --> Nil) { $_ := $type && $type ne "auto" ?? Pair.new($type, $pattern) !! implicit2explicit($pattern); - @patterns.push: $_; + + $first + ?? @patterns.unshift($_) + !! @patterns.push($_); # Make sure highlightable patterns are added add-highlight($_); @@ -478,7 +482,8 @@ MEH meh-unexpected if @unexpected; # Set up the pattern - add-pattern(@positionals.shift) if !@patterns && @positionals; + add-pattern(@positionals.shift, :first) + if !$seen-initial-pattern && @positionals; # from here on out, description is a noop %global:delete; @@ -1365,6 +1370,13 @@ my sub set-listing-flag-or-Int(str $name, $value --> Nil) { } } +# Set pattern option +my sub set-additional-pattern(str $name, $value, str $prefix) { + Bool.ACCEPTS($value) + ?? meh "'--$name' must be a pattern specification, not a flag" + !! add-pattern($prefix ~ $value) +} + #------------------------------------------------------------------------------- # One subroutine for each supported option. Is assumed to do right thing for # that option by setting the appropriate global hashes. Not expected to return @@ -1404,6 +1416,14 @@ my sub option-allow-whitespace($value --> Nil) { set-csv-flag('allow-whitespace', $value); } +my sub option-and($value --> Nil) { + set-additional-pattern('and', $value, '&'); +} + +my sub option-andnot($value --> Nil) { + set-additional-pattern('andnot', $value, '&!'); +} + my sub option-auto-decompress($value --> Nil) { set-filesystem-flag('auto-decompress', $value); } @@ -1874,6 +1894,11 @@ my sub option-module($value --> Nil) { !! @modules.push($value) } +my sub option-not($value --> Nil) { + set-additional-pattern('not', $value, '!'); + $seen-initial-pattern := True; +} + my sub option-only-first($value --> Nil) { set-listing-flag-or-Int( 'only-first', @@ -1881,6 +1906,14 @@ my sub option-only-first($value --> Nil) { ); } +my sub option-or($value --> Nil) { + set-additional-pattern('or', $value, ''); +} + +my sub option-ornot($value --> Nil) { + set-additional-pattern('ornot', $value, '!'); +} + my sub option-output-dir($value --> Nil) { meh "'--output-dir' expects a directory specification" if Bool.ACCEPTS($value); @@ -1931,9 +1964,8 @@ my sub option-paths-from($value --> Nil) { } my sub option-pattern($value --> Nil) { - Bool.ACCEPTS($value) - ?? meh "'--pattern' must be a pattern specification, not a flag" - !! add-pattern($value) + set-additional-pattern('pattern', $value, ''); + $seen-initial-pattern := True; } my sub option-patterns-from($value --> Nil) { @@ -1942,6 +1974,7 @@ my sub option-patterns-from($value --> Nil) { sub read-patterns($handle) { if $handle.lines -> @lines { add-pattern($_) for @lines; + $seen-initial-pattern := True; } } diff --git a/resources/help.txt b/resources/help.txt index db2fe9d..a9f75bc 100644 --- a/resources/help.txt +++ b/resources/help.txt @@ -9,12 +9,15 @@ in --patterns-from=file option. '/ << bar >> /' Raku regex indicated by being bounded by / / '{ .ends-with("bar") }' Raku code indicated by being bounded by { } '*.starts-with("foo")' Raku WhateverCode starting with * + 's:foo bar' Multiple patterns, split on whitespace: jp:foo JSON path specification (with --json-per-xxx) §string String should match as a word ^string String should match at start of line string$ String should match at end of line ^string$ String should match with line entirely string String should match anywhere in line + !string String should NOT match anywhere in line + &string Additional string should ALSO match anywhere in line Pattern type overrides: --type=auto Interpret special markers in pattern as above (default) @@ -25,8 +28,16 @@ in --patterns-from=file option. --type=starts-with Pattern must match at start of line --type=ends-with Pattern must match at end of line --type=equal Pattern must match entirely + --type=not Pattern must NOT match + --type=split Multiple patterns, split on whitespace --type=contains Pattern must match anywhere (default if no markers) + Additional patterns: + --and=bar Must ALSO match anywhere in line + --andnot=bar Must ALSO NOT match anywhere in line + --or=bar May ALSO match anywhere in line + --ornot=bar May ALSO NOT match annywhere in line + String search pattern modifiers: --ignorecase Ignore distinction between upper, lower and title case letters --ignoremark Only compare base characters, ignore additional marks diff --git a/resources/help/pattern.txt b/resources/help/pattern.txt index d370b8c..6df2a7c 100644 --- a/resources/help/pattern.txt +++ b/resources/help/pattern.txt @@ -1,22 +1,26 @@ Pattern specification: ‒‒‒‒‒‒‒‒‒‒‒‒‒‒‒‒‒‒‒‒‒‒ -There are 3 ways in which pattern(s) can be specified: +There are 4 ways in which a primary pattern can be specified: 1. As the first positional argument 2. Or with the --pattern=foo argument -3. Or using the --patterns-from=file argument +3. Or with the --not=foo argument +4. Or using the --patterns-from=file argument -The first is easiest for a quick ad-hoc search. The second can be handy -if you want to create a short-cut to searching for a particular string with ---save. The last one allows you set up a set of patterns to be searched -for simultaneously. +The first is easiest for a quick ad-hoc search. The second and third can +be handy if you want to create a short-cut to searching for (the absence +of) a particular string with --save. The last one allows you set up a +set of patterns to be searched for simultaneously. + +The --not=foo argument negates the match, so will select items that do +**NOT** match the pattern specification. If the --patterns-from=file argument is specified, it will read the content of the given file and process each line as a pattern specification, as described below. When searching, each pattern is tried on an item, and accepted as soon as a pattern matched. Any highlighting will occur on -*that* match only. +any of the patterns specified. If a literal string is specified as a pattern, then by default any item of which the stringification contains that string, will be accepted. @@ -55,7 +59,7 @@ the string: Example: # produce any lines that have the word "bar" in them -$ rak '<< bar >>' --type=regex +$ rak --type=regex '<< bar >>' Code: @@ -110,10 +114,10 @@ omit the initial '*'. Examples: # produce uppercased lines -$ rak '.uc' --type=code +$ rak --type=code .uc # produce all lines, only uppercase the ones that have "rakudoc" in them -$ rak '.contains("rakudoc") ?? .uc !! $_' --type=code +$ rak --type=code '.contains("rakudoc") ?? .uc !! $_' JSON path: @@ -190,13 +194,14 @@ Literal strings matching can be further specialized with other values of the - starts-with item must *start* with string - ends-with item must *end* with string - equal item must be *equal* to string + - split split string on whitespace for separate patterns Examples: # produce any lines that have the word "bar" in them -$ rak bar --type=words +$ rak --type=words bar # produce any lines that start with "Example" -$ rak Example --type=starts-with +$ rak --type=starts-with Example Both literal string matching, as well as matching with a regex, are sensitive to the --smartcase, --ignorecase and --ignoremark arguments. @@ -206,11 +211,12 @@ Shortcuts: Several shortcuts are available to indicate some type of behaviour for literal strings. They are - - string string --type=contains - - §string string --type=words - - ^string string --type=starts-with - - string$ string --type=ends-with - - ^string$ string --type=equal + - string --type=contains string + - §string --type=words string + - ^string --type=starts-with string + - string$ --type=ends-with string + - ^string$ --type=equal string + - !string --type=not Examples: # produce any lines that have the word "bar" in them @@ -218,3 +224,19 @@ $ rak §bar # produce any lines that start with "Example" $ rak ^Example + +Additional patterns: + +You can also specify additional patterns that should also (not) match: + + - --and=bar Must ALSO match anywhere in line + - --andnot=bar Must ALSO NOT match anywhere in line + - --or=bar May ALSO match anywhere in line + - --ornot=bar May ALSO NOT match annywhere in line + +Examples: +# produce any lines that have the words "foo" and "bar" in them +$ rak §foo --and=§bar + +# produce any lines that start with "foo" but not have "bar" in them +$ rak ^foo --andnot=bar