File Coverage

blib/lib/App/Greple/under.pm
Criterion Covered Total %
statement 32 73 43.8
branch 0 16 0.0
condition n/a
subroutine 11 15 73.3
pod 0 4 0.0
total 43 108 39.8


line stmt bran cond sub pod time code
1             package App::Greple::under;
2 1     1   420205 use 5.024;
  1         5  
3 1     1   8 use warnings;
  1         2  
  1         130  
4              
5             our $VERSION = "1.00";
6              
7             =encoding utf-8
8              
9             =head1 NAME
10              
11             App::Greple::under - greple under-line module
12              
13             =head1 SYNOPSIS
14              
15             greple -Munder::line ...
16              
17             greple -Munder::mise ... | greple -Munder::place
18              
19             =head1 DESCRIPTION
20              
21             This module is intended to clarify highlighting points without ANSI
22             sequencing when highlighting by ANSI sequencing is not possible for
23             some reason.
24              
25             The following command searches for a paragraph that contains all the
26             words specified.
27              
28             greple 'license agreements software freedom' LICENSE -p
29              
30             =for html

31            
32            

33              
34             By default, the emphasis should be indicated by underlining it on the
35             next line.
36              
37             greple -Munder::line 'license agreements software freedom' LICENSE -p
38              
39             Above command will produce output like this:
40              
41             ┌───────────────────────────────────────────────────────────────────────┐
42             │ The license agreements of most software companies try to keep users │
43             │ ▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔ │
44             │ at the mercy of those companies. By contrast, our General Public │
45             │ License is intended to guarantee your freedom to share and change free│
46             │ ▔▔▔▔▔▔▔ │
47             │ software--to make sure the software is free for all its users. The │
48             │ ▔▔▔▔▔▔▔▔ ▔▔▔▔▔▔▔▔ │
49             │ General Public License applies to the Free Software Foundation's │
50             │ software and to any other program whose authors commit to using it. │
51             │ ▔▔▔▔▔▔▔▔ │
52             │ You can use it for your programs, too. │
53             └───────────────────────────────────────────────────────────────────────┘
54              
55             =for html

56            
57            

58              
59             If you want to process the search results before underlining them,
60             process them in the C<-Munder::mise> module and then pass them through
61             the C<-Munder::place> module.
62              
63             greple -Munder::mise ... | ... | greple -Munder::place
64              
65             =for html

66            
67            

68              
69             =head1 MODULE OPTION
70              
71             =head2 B<--config>
72              
73             Set config parameters.
74              
75             greple -Munder::line --config type=eighth -- ...
76              
77             Configuable parameters:
78              
79             =over 4
80              
81             =item C
82              
83             Set under-line type.
84              
85             =item C
86              
87             Set under-line sequence. The given string is broken down into single
88             character sequences.
89              
90             =back
91              
92             =head2 B<--show-colormap>
93              
94             Print custom colormaps separated by whitespace characters. You can
95             read them into an array by L like this:
96              
97             read -a MAP < <(greple -Munder::place --show-colormap --)
98              
99             =head1 SEE ALSO
100              
101             L
102              
103             =head1 AUTHOR
104              
105             Kazumasa Utashiro
106              
107             =head1 LICENSE
108              
109             Copyright ©︎ 2024-2025 Kazumasa Utashiro.
110              
111             This library is free software; you can redistribute it and/or modify
112             it under the same terms as Perl itself.
113              
114             =cut
115              
116 1     1   6 use Exporter 'import';
  1         2  
  1         114  
117             our @EXPORT_OK = qw(%config &config &finalize);
118             our %EXPORT_TAGS = (all => \@EXPORT_OK);
119              
120 1     1   664 use App::Greple::Common qw(@color_list);
  1         533  
  1         161  
121 1     1   745 use Term::ANSIColor::Concise qw(ansi_code);
  1         86697  
  1         97  
122 1     1   917 use Text::ANSI::Fold;
  1         78832  
  1         104  
123 1     1   756 use Text::ANSI::Fold::Util qw(ansi_width);
  1         951  
  1         73  
124 1     1   848 use Hash::Util qw(lock_keys);
  1         4212  
  1         7  
125 1     1   118 use Data::Dumper;
  1         3  
  1         77  
126              
127 1     1   703 use Getopt::EX::Config qw(config);
  1         20972  
  1         10  
128              
129             my $config = Getopt::EX::Config->new(
130             type => 'overline',
131             space => ' ',
132             sequence => '',
133             'custom-colormap' => 1,
134             'show-colormap' => 0,
135             );
136              
137             sub finalize {
138 0     0 0   our($mod, $argv) = @_;
139 0           $config->deal_with(
140             $argv,
141             map("$_=s", qw(type space sequence)),
142             map("$_!" , qw(custom-colormap show-colormap)),
143             );
144 0 0         if (not $config->{'custom-colormap'}) {
145 0           $mod->setopt('--use-custom-colormap' => '$');
146             }
147             }
148              
149             sub prologue {
150 0 0   0 0   if ($config->{"show-colormap"}) {
151 0           prepare();
152 0           print "@color_list\n";
153 0           exit 0;
154             }
155             }
156              
157             $Term::ANSIColor::Concise::NO_RESET_EL = 1;
158             Text::ANSI::Fold->configure(expand => 1);
159              
160             my %marks = (
161 1     1   409 eighth => [ "\N{UPPER ONE EIGHTH BLOCK}" ],
  1         4  
  1         10  
162             half => [ "\N{UPPER HALF BLOCK}" ],
163             overline => [ "\N{OVERLINE}" ],
164             macron => [ "\N{MACRON}" ],
165             caret => [ "^" ],
166             ring => [ "\N{NBSP}\N{COMBINING RING ABOVE}" ],
167             sign => [ qw( + - ~ ) ],
168             number => [ "0" .. "9" ],
169             alphabet => [ "a" .. "z", "A" .. "Z" ],
170             block => [
171             "\N{UPPER ONE EIGHTH BLOCK}",
172             "\N{UPPER HALF BLOCK}",
173             "\N{FULL BLOCK}",
174             ],
175             vertical => [
176             "\N{BOX DRAWINGS LIGHT VERTICAL}",
177             "\N{BOX DRAWINGS LIGHT DOUBLE DASH VERTICAL}",
178             "\N{BOX DRAWINGS LIGHT TRIPLE DASH VERTICAL}",
179             "\N{BOX DRAWINGS LIGHT QUADRUPLE DASH VERTICAL}",
180             "\N{BOX DRAWINGS HEAVY VERTICAL}",
181             "\N{BOX DRAWINGS HEAVY DOUBLE DASH VERTICAL}",
182             "\N{BOX DRAWINGS HEAVY TRIPLE DASH VERTICAL}",
183             "\N{BOX DRAWINGS HEAVY QUADRUPLE DASH VERTICAL}",
184             ],
185             up => [
186             "\N{BOX DRAWINGS LIGHT UP}",
187             "\N{BOX DRAWINGS LIGHT UP AND HORIZONTAL}",
188             "\N{BOX DRAWINGS UP LIGHT AND HORIZONTAL HEAVY}",
189             "\N{BOX DRAWINGS HEAVY UP}",
190             "\N{BOX DRAWINGS HEAVY UP AND HORIZONTAL}",
191             "\N{BOX DRAWINGS UP HEAVY AND HORIZONTAL LIGHT}",
192             "\N{BOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE}",
193             "\N{BOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE}",
194             "\N{BOX DRAWINGS DOUBLE UP AND HORIZONTAL}",
195             ],
196             );
197              
198             my $re;
199             my %index;
200             my @marks;
201              
202             sub prepare {
203 0 0   0 0   @color_list == 0 and die "color table is not available.\n";
204              
205 0           my @ansi = map { ansi_code($_) } @color_list;
  0            
206 0           my @ansi_re = map { s/\\\e/\\e/gr } map { quotemeta($_) } @ansi;
  0            
  0            
207 0           %index = map { $ansi[$_] => $_ } keys @ansi;
  0            
208 0           my $reset_re = qr/(?:\e\[[0;]*[mK])+/;
209 0           $re = do {
210 0           local $" = '|';
211 0           qr/(?@ansi_re) (?[^\e]*) (?$reset_re)/x;
212             };
213 0 0         if (my $s = $config->{sequence}) {
    0          
214 0           @marks = grep { ! /\A\s\z/ } $s =~ /\X/g;
  0            
215             }
216             elsif (my $mark = $marks{$config->{type}}) {
217 0           @marks = $mark->@*;
218             }
219             else {
220 0           die "$config->{type}: invalid type.\n";
221             }
222             }
223              
224             sub line {
225 0 0   0 0   prepare() if not $re;
226 0           while (<>) {
227 0           local @_;
228 0           my @under;
229             my $pos;
230 0           while (/\G (?
.*?) $re /xgp) { 
231 0           push @_, $+{pre}, $+{text};
232 0           my $mark = $marks[$index{$+{ansi}} % @marks];
233             push @under,
234             $config->{space} x ansi_width($+{pre}),
235 0           $mark x ansi_width($+{text});
236 0           $pos = pos;
237             }
238 0 0         if (not defined $pos) {
239 0           print;
240 0           next;
241             }
242 0 0         if ($pos < length($_)) {
243 0           push @_, substr($_, $pos);
244             }
245 0           print join '', @_;
246 0           print join '', @under, "\n";
247             }
248             }
249              
250             1;
251              
252             __DATA__