2015-07-28 22:44:53 +02:00
|
|
|
|
# Copyright (C) 2004–2015 Alex Schroeder <alex@gnu.org>
|
2004-06-17 01:10:15 +00:00
|
|
|
|
#
|
|
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
2009-02-13 10:26:51 +00:00
|
|
|
|
# the Free Software Foundation; either version 3 of the License, or
|
2004-06-17 01:10:15 +00:00
|
|
|
|
# (at your option) any later version.
|
|
|
|
|
|
#
|
|
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
|
|
#
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License
|
2009-02-13 10:26:51 +00:00
|
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2004-06-17 01:10:15 +00:00
|
|
|
|
|
2015-03-27 03:01:01 +02:00
|
|
|
|
use strict;
|
2015-08-18 10:48:03 +02:00
|
|
|
|
use v5.10;
|
2015-03-27 03:01:01 +02:00
|
|
|
|
|
2014-08-24 21:31:00 +02:00
|
|
|
|
AddModuleDescription('markup.pl', 'Markup Extension');
|
2004-08-18 13:01:48 +00:00
|
|
|
|
|
2015-04-10 13:31:28 +03:00
|
|
|
|
our ($q, $bol, @MyRules, %RuleOrder, @MyInitVariables);
|
|
|
|
|
|
our (%MarkupPairs, %MarkupForcedPairs, %MarkupSingles, %MarkupLines,
|
|
|
|
|
|
$MarkupQuotes, $MarkupQuoteTable);
|
2006-03-06 00:14:22 +00:00
|
|
|
|
|
2006-03-06 00:26:50 +00:00
|
|
|
|
$MarkupQuotes = 1;
|
2009-03-13 22:28:29 +00:00
|
|
|
|
|
|
|
|
|
|
# $MarkupQuotes 'hi' "hi" I'm Favored in
|
|
|
|
|
|
# 0 'hi' "hi" I'm Typewriters
|
|
|
|
|
|
# 1 ‘hi’ “hi” I’m Britain and North America
|
|
|
|
|
|
# 2 ‹hi› «hi» I’m France and Italy
|
|
|
|
|
|
# 3 ›hi‹ »hi« I’m Germany
|
|
|
|
|
|
# 4 ‚hi’ „hi” I’m Germany
|
|
|
|
|
|
|
|
|
|
|
|
# 0 1 2 3 4
|
|
|
|
|
|
$MarkupQuoteTable = [[ "'", "'", '"', '"' , "'" ], # 0
|
2006-03-06 01:53:29 +00:00
|
|
|
|
['‘', '’', '”', '“', '’'], # 1
|
|
|
|
|
|
['‹', '›', '»', '«', '’'], # 2
|
|
|
|
|
|
['›', '‹', '«', '»', '’'], # 3
|
|
|
|
|
|
['‚', '‘', '“', '„', '’'], # 4
|
|
|
|
|
|
];
|
2004-06-17 01:10:15 +00:00
|
|
|
|
|
2009-03-13 22:28:29 +00:00
|
|
|
|
# $MarkupQuoteTable->[2]->[0] ‹
|
|
|
|
|
|
# $MarkupQuoteTable->[2]->[1] ›
|
|
|
|
|
|
# $MarkupQuoteTable->[2]->[2] »
|
|
|
|
|
|
# $MarkupQuoteTable->[2]->[3] «
|
|
|
|
|
|
# $MarkupQuoteTable->[2]->[4] ’
|
|
|
|
|
|
|
2004-06-17 01:10:15 +00:00
|
|
|
|
push(@MyRules, \&MarkupRule);
|
2004-07-14 14:51:23 +00:00
|
|
|
|
# The ---- rule in usemod.pl conflicts with the --- rule
|
|
|
|
|
|
$RuleOrder{\&MarkupRule} = 150;
|
|
|
|
|
|
|
2004-08-18 13:01:48 +00:00
|
|
|
|
%MarkupPairs = ('*' => 'b',
|
|
|
|
|
|
'/' => 'i',
|
2004-09-02 20:34:52 +00:00
|
|
|
|
'_' => ['em', {'style'=>'text-decoration: underline; font-style: normal;'}],
|
2004-08-18 13:01:48 +00:00
|
|
|
|
'~' => 'em',
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2015-03-30 00:20:27 +02:00
|
|
|
|
%MarkupForcedPairs = ("{{{\n" => ['pre', {}, '}}}'], # don't use undef instead of {}
|
2005-08-23 02:48:13 +00:00
|
|
|
|
'##' => 'code',
|
|
|
|
|
|
'%%' => 'span',
|
|
|
|
|
|
'**' => 'b',
|
|
|
|
|
|
'//' => 'i',
|
|
|
|
|
|
'__' => ['em', {'style'=>'text-decoration: underline; font-style: normal;'}],
|
|
|
|
|
|
'~~' => 'em',
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2004-08-18 13:01:48 +00:00
|
|
|
|
# This could be done using macros, however: If we convert to the
|
|
|
|
|
|
# numbered entity, the next person editing finds it hard to read. If
|
|
|
|
|
|
# we convert to a unicode character, it is no longer obvious how to
|
|
|
|
|
|
# achieve it.
|
|
|
|
|
|
%MarkupSingles = ('...' => '…', # HORIZONTAL ELLIPSIS
|
|
|
|
|
|
'---' => '—', # EM DASH
|
|
|
|
|
|
'-- ' => '– ', # EN DASH
|
2009-11-07 19:30:17 +00:00
|
|
|
|
'-> ' => '→ ', # RIGHTWARDS ARROW, NO-BREAK SPACE
|
2009-11-07 19:32:35 +00:00
|
|
|
|
'<-' => '←',
|
|
|
|
|
|
'<--' => '←',
|
|
|
|
|
|
'-->' => '→',
|
|
|
|
|
|
'=>' => '⇒',
|
|
|
|
|
|
'==>' => '⇒',
|
|
|
|
|
|
'<=>' => '⇔',
|
|
|
|
|
|
'+/-' => '±',
|
2004-08-18 13:01:48 +00:00
|
|
|
|
);
|
|
|
|
|
|
|
2004-09-27 21:38:08 +00:00
|
|
|
|
%MarkupLines = ('>' => 'pre',
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2015-03-17 07:48:29 +01:00
|
|
|
|
# either a single letter, or a string that begins with a single letter and ends with a non-space
|
|
|
|
|
|
my $words = '([A-Za-z\x{0080}-\x{fffd}](?:[-%.,:;\'"!?0-9 A-Za-z\x{0080}-\x{fffd}]*?[-%.,:;\'"!?0-9A-Za-z\x{0080}-\x{fffd}])?)';
|
|
|
|
|
|
# zero-width assertion to prevent km/h from counting
|
|
|
|
|
|
my $nowordstart = '(?:(?<=[^-0-9A-Za-z\x{0080}-\x{fffd}])|^)';
|
2004-08-18 13:01:48 +00:00
|
|
|
|
# zero-width look-ahead assertion to prevent km/h from counting
|
2015-03-17 07:48:29 +01:00
|
|
|
|
my $nowordend = '(?=[^-0-9A-Za-z\x{0080}-\x{fffd}]|$)';
|
2004-08-18 13:01:48 +00:00
|
|
|
|
|
|
|
|
|
|
my $markup_pairs_re = '';
|
2005-09-10 03:18:29 +00:00
|
|
|
|
my $markup_forced_pairs_re = '';
|
2004-08-18 13:01:48 +00:00
|
|
|
|
my $markup_singles_re = '';
|
2004-09-27 21:38:08 +00:00
|
|
|
|
my $markup_lines_re = '';
|
2004-08-18 13:01:48 +00:00
|
|
|
|
|
2006-08-18 23:57:36 +00:00
|
|
|
|
# do not add all block elements, because not all of them make sense,
|
|
|
|
|
|
# as they cannot be nested -- thus it would not be possible to put
|
|
|
|
|
|
# list items inside a list element, for example.
|
|
|
|
|
|
my %block_element = map { $_ => 1 } qw(p blockquote address div h1 h2
|
|
|
|
|
|
h3 h4 h5 h6 pre);
|
|
|
|
|
|
|
2005-01-04 09:57:56 +00:00
|
|
|
|
# do this later so that the user can customize the vars
|
|
|
|
|
|
push(@MyInitVariables, \&MarkupInit);
|
|
|
|
|
|
|
|
|
|
|
|
sub MarkupInit {
|
|
|
|
|
|
$markup_pairs_re = '\G([' . join('', (map { quotemeta(QuoteHtml($_)) }
|
|
|
|
|
|
keys(%MarkupPairs))) . '])';
|
2015-03-17 07:48:29 +01:00
|
|
|
|
$markup_pairs_re = qr/${nowordstart}${markup_pairs_re}${words}\1${nowordend}/;
|
2005-09-10 03:18:29 +00:00
|
|
|
|
$markup_forced_pairs_re = '\G(' . join('|', (map { quotemeta(QuoteHtml($_)) }
|
|
|
|
|
|
keys(%MarkupForcedPairs))) . ')';
|
|
|
|
|
|
$markup_forced_pairs_re = qr/$markup_forced_pairs_re/;
|
2005-01-04 09:57:56 +00:00
|
|
|
|
$markup_singles_re = '\G(' . join('|', (map { quotemeta(QuoteHtml($_)) }
|
2008-03-26 21:57:19 +00:00
|
|
|
|
sort {$b cmp $a} # longer regex first
|
2005-01-04 09:57:56 +00:00
|
|
|
|
keys(%MarkupSingles))) . ')';
|
|
|
|
|
|
$markup_singles_re = qr/$markup_singles_re/;
|
|
|
|
|
|
$markup_lines_re = '\G(' . join('|', (map { quotemeta(QuoteHtml($_)) }
|
|
|
|
|
|
keys(%MarkupLines))) . ')(.*\n?)';
|
|
|
|
|
|
$markup_lines_re = qr/$markup_lines_re/;
|
|
|
|
|
|
}
|
2004-09-27 21:38:08 +00:00
|
|
|
|
|
|
|
|
|
|
sub MarkupTag {
|
|
|
|
|
|
my ($tag, $str) = @_;
|
|
|
|
|
|
my ($start, $end);
|
|
|
|
|
|
if (ref($tag)) {
|
|
|
|
|
|
my $arrayref = $tag;
|
|
|
|
|
|
my ($tag, $hashref) = @{$arrayref};
|
|
|
|
|
|
my %hash = %{$hashref};
|
|
|
|
|
|
$start = $end = $tag;
|
|
|
|
|
|
foreach my $attr (keys %hash) {
|
|
|
|
|
|
$start .= ' ' . $attr . '="' . $hash{$attr} . '"';
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
$start = $end = $tag;
|
|
|
|
|
|
}
|
2006-08-18 23:57:36 +00:00
|
|
|
|
my $result = "<$start>$str</$end>";
|
|
|
|
|
|
$result = CloseHtmlEnvironments() . $result . AddHtmlEnvironment('p')
|
|
|
|
|
|
if $block_element{$start};
|
|
|
|
|
|
return $result;
|
2004-08-18 13:01:48 +00:00
|
|
|
|
}
|
2004-07-14 14:51:23 +00:00
|
|
|
|
|
2004-06-17 01:10:15 +00:00
|
|
|
|
sub MarkupRule {
|
2015-08-23 21:22:12 +03:00
|
|
|
|
if ($bol and %MarkupLines and m/$markup_lines_re/cg) {
|
2004-09-27 21:38:08 +00:00
|
|
|
|
my ($tag, $str) = ($1, $2);
|
|
|
|
|
|
$str = $q->span($tag) . $str;
|
2015-08-23 21:22:12 +03:00
|
|
|
|
while (m/$markup_lines_re/cg) {
|
2004-09-27 21:38:08 +00:00
|
|
|
|
$str .= $q->span($1) . $2;
|
2004-09-02 20:34:52 +00:00
|
|
|
|
}
|
2005-08-23 02:48:13 +00:00
|
|
|
|
return CloseHtmlEnvironments()
|
|
|
|
|
|
. MarkupTag($MarkupLines{UnquoteHtml($tag)}, $str)
|
|
|
|
|
|
. AddHtmlEnvironment('p');
|
2015-08-23 21:22:12 +03:00
|
|
|
|
} elsif (%MarkupSingles and m/$markup_singles_re/cg) {
|
2013-04-12 22:59:50 +02:00
|
|
|
|
return $MarkupSingles{UnquoteHtml($1)};
|
2015-08-23 21:22:12 +03:00
|
|
|
|
} elsif (%MarkupForcedPairs and m/$markup_forced_pairs_re/cg) {
|
2005-09-10 03:18:29 +00:00
|
|
|
|
my $tag = $1;
|
2006-08-18 23:57:36 +00:00
|
|
|
|
my $start = $tag;
|
2005-09-10 03:18:29 +00:00
|
|
|
|
my $end = $tag;
|
|
|
|
|
|
# handle different end tag
|
|
|
|
|
|
my $data = $MarkupForcedPairs{UnquoteHtml($tag)};
|
|
|
|
|
|
if (ref($data)) {
|
|
|
|
|
|
my @data = @{$data};
|
2006-08-18 23:57:36 +00:00
|
|
|
|
$start = $data[0] if $data[0];
|
2005-09-10 03:18:29 +00:00
|
|
|
|
$end = $data[2] if $data[2];
|
|
|
|
|
|
}
|
2005-10-14 21:12:55 +00:00
|
|
|
|
my $endre = quotemeta($end);
|
2006-08-18 23:57:36 +00:00
|
|
|
|
$endre .= '[ \t]*\n?' if $block_element{$start}; # skip trailing whitespace if block
|
2005-10-14 21:12:55 +00:00
|
|
|
|
# may match the empty string, or multiple lines, but may not span
|
|
|
|
|
|
# paragraphs.
|
2015-08-23 21:22:12 +03:00
|
|
|
|
if ($endre and m/\G$endre/cg) {
|
2005-10-14 21:12:55 +00:00
|
|
|
|
return $tag . $end;
|
2015-08-23 21:22:12 +03:00
|
|
|
|
} elsif ($tag eq $end && m/\G((:?.+?\n)*?.+?)$endre/cg) { # may not span paragraphs
|
2005-10-14 21:12:55 +00:00
|
|
|
|
return MarkupTag($data, $1);
|
2015-08-23 21:22:12 +03:00
|
|
|
|
} elsif ($tag ne $end && m/\G((:?.|\n)+?)$endre/cg) {
|
2005-10-14 21:12:55 +00:00
|
|
|
|
return MarkupTag($data, $1);
|
2005-09-10 03:18:29 +00:00
|
|
|
|
} else {
|
|
|
|
|
|
return $tag;
|
|
|
|
|
|
}
|
2015-08-23 21:22:12 +03:00
|
|
|
|
} elsif (%MarkupPairs and m/$markup_pairs_re/cg) {
|
2004-09-27 21:38:08 +00:00
|
|
|
|
return MarkupTag($MarkupPairs{UnquoteHtml($1)}, $2);
|
2015-08-23 21:22:12 +03:00
|
|
|
|
} elsif ($MarkupPairs{'/'} and m|\G~/|cg) {
|
2004-08-18 13:01:48 +00:00
|
|
|
|
return '~/'; # fix ~/elisp/ example
|
2015-08-23 21:22:12 +03:00
|
|
|
|
} elsif ($MarkupPairs{'/'} and m|\G(/[-A-Za-z0-9\x{0080}-\x{fffd}/]+/$words/)|cg) {
|
2004-08-18 13:01:48 +00:00
|
|
|
|
return $1; # fix /usr/share/lib/! example
|
2009-03-13 22:28:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
# "foo
|
|
|
|
|
|
elsif ($MarkupQuotes and (m/\G(?<=[[:space:]])"/cg
|
|
|
|
|
|
or pos == 0 and m/\G"/cg)) {
|
2006-03-11 18:10:43 +00:00
|
|
|
|
return $MarkupQuoteTable->[$MarkupQuotes]->[3];
|
2009-03-13 22:28:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
# foo"
|
|
|
|
|
|
elsif ($MarkupQuotes and (m/\G"(?=[[:space:][:punct:]])/cg
|
2006-03-06 00:14:22 +00:00
|
|
|
|
or m/\G"\z/cg)) {
|
2006-03-06 01:53:29 +00:00
|
|
|
|
return $MarkupQuoteTable->[$MarkupQuotes]->[2];
|
2009-03-13 22:28:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
# foo."
|
|
|
|
|
|
elsif ($MarkupQuotes and (m/\G(?<=[[:punct:]])"/cg)) {
|
2006-03-06 01:53:29 +00:00
|
|
|
|
return $MarkupQuoteTable->[$MarkupQuotes]->[3];
|
2009-03-13 22:28:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
# single quotes at the beginning of the buffer
|
|
|
|
|
|
elsif ($MarkupQuotes and pos == 0 and m/\G'/cg) {
|
|
|
|
|
|
return $MarkupQuoteTable->[$MarkupQuotes]->[0];
|
|
|
|
|
|
}
|
|
|
|
|
|
# 'foo
|
|
|
|
|
|
elsif ($MarkupQuotes and (m/\G(?<=[[:space:]])'/cg
|
|
|
|
|
|
or pos == 0 and m/\G'/cg)) {
|
2006-03-06 01:53:29 +00:00
|
|
|
|
return $MarkupQuoteTable->[$MarkupQuotes]->[0];
|
2009-03-13 22:28:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
# foo'
|
|
|
|
|
|
elsif ($MarkupQuotes and (m/\G'(?=[[:space:][:punct:]])/cg
|
|
|
|
|
|
or m/\G'\z/cg)) {
|
2006-03-06 01:53:29 +00:00
|
|
|
|
return $MarkupQuoteTable->[$MarkupQuotes]->[1];
|
2009-03-13 22:28:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
# foo's
|
|
|
|
|
|
elsif ($MarkupQuotes and m/\G(?<![[:space:]])'(?![[:space:][:punct:]])/cg) {
|
2006-03-06 01:53:29 +00:00
|
|
|
|
return $MarkupQuoteTable->[$MarkupQuotes]->[4];
|
2004-06-17 01:10:15 +00:00
|
|
|
|
}
|
2015-02-27 12:10:18 +02:00
|
|
|
|
return;
|
2004-06-17 01:10:15 +00:00
|
|
|
|
}
|