File Coverage

blib/lib/Circle/Net/Matrix/Utils.pm
Criterion Covered Total %
statement 41 41 100.0
branch 11 12 91.6
condition 5 8 62.5
subroutine 6 6 100.0
pod 1 1 100.0
total 64 68 94.1


line stmt bran cond sub pod time code
1             # You may distribute under the terms of the GNU General Public License
2             #
3             # (C) Paul Evans, 2017 -- leonerd@leonerd.org.uk
4              
5             package Circle::Net::Matrix::Utils;
6              
7 2     2   48099 use strict;
  2         3  
  2         49  
8 2     2   6 use warnings;
  2         3  
  2         64  
9              
10             our $VERSION = '0.03';
11              
12 2     2   7 use Carp;
  2         2  
  2         105  
13              
14 2     2   7 use Exporter 'import';
  2         22  
  2         69  
15             our @EXPORT_OK = qw(
16             parse_markdownlike
17             );
18              
19 2     2   518 use String::Tagged;
  2         5504  
  2         483  
20              
21             my %DEFAULT_PATTERNS = (
22             '*' => 'italic',
23             '**' => 'bold',
24             '`' => 'monospace',
25             );
26              
27             =head1 FUNCTIONS
28              
29             =cut
30              
31             =head2 parse_markdownlike
32              
33             $st = parse_markdownlike( $str )
34              
35             Attempts to parse some markdown-like formatting tags from the input C<$str>,
36             returning a L instance representing it.
37             In particular, the following markup is recognised
38              
39             *italic*
40             **bold**
41             `monospace`
42              
43             This parser does not attempt to be a complete, nor a fully-compatible Markdown
44             parser, but simply tries to provide a useful function for entering formatted
45             messages.
46              
47             =cut
48              
49             sub parse_markdownlike
50             {
51 4     4 1 4144 my ( $str, $patterns ) = @_;
52              
53 4   50     19 $patterns //= \%DEFAULT_PATTERNS;
54 4 50       11 keys %$patterns or croak "Cannot parse_markdownlike with no marker patterns";
55              
56 4         5 my %tags = ();
57              
58 12         21 my $match_marker = join "|", map { quotemeta }
59 4         17 sort { length $b <=> length $a } # longest first so ** wins over *
  12         17  
60             keys %$patterns;
61 4         37 $match_marker = qr/$match_marker/;
62              
63 4         16 my $ret = String::Tagged->new;
64              
65 4         46 while( length $str ) {
66 10 100       90 if( $str =~ m/(\w?)($match_marker)(\w?)/ ) {
67 7         10 my $word_before = length $1;
68 7         7 my $marker = $2;
69 7         11 my $markstart = $-[2];
70 7         11 my $markend = $+[2];
71 7         8 my $word_after = length $3;
72              
73             # prefix before marker
74 7 100       23 $ret->append_tagged( substr( $str, 0, $markstart ), %tags ) if $markstart > 0;
75              
76 7 100 66     171 if( $word_after and !$word_before ) {
    100 66        
77 3         6 $tags{ $patterns->{$marker} } = 1;
78             }
79             elsif( $word_before and !$word_after ) {
80 3         4 delete $tags{ $patterns->{$marker} };
81             }
82             else {
83             # This isn't actually a tag start/stop so ignore it and continue
84 1         4 $ret->append_tagged( substr( $str, $markstart, $markend - $markstart ), %tags );
85             }
86              
87 7         27 substr( $str, 0, $markend ) = "";
88             }
89             else {
90 3         7 $ret->append_tagged( $str, %tags );
91 3         78 last;
92             }
93             }
94              
95 4 100       11 return $ret if $ret->tagnames;
96 2         16 return $ret->str;
97             }
98              
99             0x55AA;