wikitext 0.1 → 0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ext/parser.c +19 -9
- data/ext/wikitext.c +1 -0
- data/spec/internal_link_spec.rb +430 -1
- data/spec/wikitext_spec.rb +10 -0
- metadata +2 -2
data/ext/parser.c
CHANGED
@@ -36,6 +36,7 @@ typedef struct
|
|
36
36
|
VALUE pending_crlf; // boolean (Qtrue or Qfalse)
|
37
37
|
VALUE autolink; // boolean (Qtrue or Qfalse)
|
38
38
|
VALUE treat_slash_as_special; // boolean (Qtrue or Qfalse)
|
39
|
+
VALUE space_to_underscore; // boolean (Qtrue or Qfalse)
|
39
40
|
VALUE special_link; // boolean (Qtrue or Qfalse): is the current link_target a "special" link?
|
40
41
|
str_t *line_ending;
|
41
42
|
int base_indent; // controlled by the :indent option to Wikitext::Parser#parse
|
@@ -548,10 +549,11 @@ inline VALUE _Wikitext_parser_trim_link_target(VALUE string)
|
|
548
549
|
|
549
550
|
// - non-printable (non-ASCII) characters converted to numeric entities
|
550
551
|
// - QUOT and AMP characters converted to named entities
|
551
|
-
// -
|
552
|
-
|
552
|
+
// - if rollback is Qtrue, there is no special treatment of spaces
|
553
|
+
// - if rollback is Qfalse, leading and trailing whitespace trimmed if trimmed
|
554
|
+
inline VALUE _Wikitext_parser_sanitize_link_target(parser_t *parser, VALUE rollback)
|
553
555
|
{
|
554
|
-
string
|
556
|
+
VALUE string = StringValue(parser->link_target); // raises if string is nil or doesn't quack like a string
|
555
557
|
char *src = RSTRING_PTR(string);
|
556
558
|
char *start = src; // remember this so we can check if we're at the start
|
557
559
|
long len = RSTRING_LEN(string);
|
@@ -605,7 +607,7 @@ inline VALUE _Wikitext_parser_sanitize_link_target(VALUE string, VALUE trim)
|
|
605
607
|
free(dest_ptr);
|
606
608
|
rb_raise(rb_eRangeError, "invalid link text (\">\" may not appear in link text)");
|
607
609
|
}
|
608
|
-
else if (*src == ' ' && src == start &&
|
610
|
+
else if (*src == ' ' && src == start && rollback == Qfalse)
|
609
611
|
start++; // we eat leading space
|
610
612
|
else if (*src >= 0x20 && *src <= 0x7e) // printable ASCII
|
611
613
|
{
|
@@ -630,7 +632,7 @@ inline VALUE _Wikitext_parser_sanitize_link_target(VALUE string, VALUE trim)
|
|
630
632
|
}
|
631
633
|
|
632
634
|
// trim trailing space if necessary
|
633
|
-
if (
|
635
|
+
if (rollback == Qfalse && non_space > dest_ptr && dest != non_space)
|
634
636
|
len = non_space - dest_ptr;
|
635
637
|
else
|
636
638
|
len = dest - dest_ptr;
|
@@ -641,7 +643,9 @@ inline VALUE _Wikitext_parser_sanitize_link_target(VALUE string, VALUE trim)
|
|
641
643
|
|
642
644
|
VALUE Wikitext_parser_sanitize_link_target(VALUE self, VALUE string)
|
643
645
|
{
|
644
|
-
|
646
|
+
parser_t parser;
|
647
|
+
parser.link_target = string;
|
648
|
+
return _Wikitext_parser_sanitize_link_target(&parser, Qfalse);
|
645
649
|
}
|
646
650
|
|
647
651
|
// encodes the input string according to RFCs 2396 and 2718
|
@@ -729,6 +733,8 @@ inline static void _Wikitext_parser_encode_link_target(parser_t *parser)
|
|
729
733
|
}
|
730
734
|
else if (*input == ' ' && input == start)
|
731
735
|
start++; // we eat leading space
|
736
|
+
else if (*input == ' ' && parser->space_to_underscore == Qtrue)
|
737
|
+
*dest++ = '_';
|
732
738
|
else // everything else gets URL-encoded
|
733
739
|
{
|
734
740
|
*dest++ = '%';
|
@@ -740,7 +746,7 @@ inline static void _Wikitext_parser_encode_link_target(parser_t *parser)
|
|
740
746
|
}
|
741
747
|
|
742
748
|
// trim trailing space if necessary
|
743
|
-
if (non_space > dest_ptr && dest
|
749
|
+
if (non_space > dest_ptr && dest != non_space)
|
744
750
|
dest_len = non_space - dest_ptr;
|
745
751
|
else
|
746
752
|
dest_len = dest - dest_ptr;
|
@@ -753,6 +759,7 @@ VALUE Wikitext_parser_encode_link_target(VALUE self, VALUE in)
|
|
753
759
|
parser_t parser;
|
754
760
|
parser.link_target = in;
|
755
761
|
parser.treat_slash_as_special = Qfalse;
|
762
|
+
parser.space_to_underscore = Qfalse;
|
756
763
|
_Wikitext_parser_encode_link_target(&parser);
|
757
764
|
return parser.link_target;
|
758
765
|
}
|
@@ -763,6 +770,7 @@ VALUE Wikitext_parser_encode_special_link_target(VALUE self, VALUE in)
|
|
763
770
|
parser_t parser;
|
764
771
|
parser.link_target = in;
|
765
772
|
parser.treat_slash_as_special = Qtrue;
|
773
|
+
parser.space_to_underscore = Qfalse;
|
766
774
|
_Wikitext_parser_encode_link_target(&parser);
|
767
775
|
return parser.link_target;
|
768
776
|
}
|
@@ -777,7 +785,7 @@ inline void _Wikitext_rollback_failed_link(parser_t *parser)
|
|
777
785
|
rb_str_cat(parser->output, link_start, sizeof(link_start) - 1);
|
778
786
|
if (!NIL_P(parser->link_target))
|
779
787
|
{
|
780
|
-
VALUE sanitized = _Wikitext_parser_sanitize_link_target(parser
|
788
|
+
VALUE sanitized = _Wikitext_parser_sanitize_link_target(parser, Qtrue);
|
781
789
|
rb_str_append(parser->output, sanitized);
|
782
790
|
if (scope_includes_separator)
|
783
791
|
{
|
@@ -823,6 +831,7 @@ VALUE Wikitext_parser_initialize(VALUE self)
|
|
823
831
|
rb_iv_set(self, "@external_link_class", rb_str_new2("external"));
|
824
832
|
rb_iv_set(self, "@mailto_class", rb_str_new2("mailto"));
|
825
833
|
rb_iv_set(self, "@internal_link_prefix", rb_str_new2("/wiki/"));
|
834
|
+
rb_iv_set(self, "@space_to_underscore", Qfalse);
|
826
835
|
rb_iv_set(self, "@treat_slash_as_special", Qtrue);
|
827
836
|
return self;
|
828
837
|
}
|
@@ -883,6 +892,7 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self)
|
|
883
892
|
parser->pending_crlf = Qfalse;
|
884
893
|
parser->autolink = rb_iv_get(self, "@autolink");
|
885
894
|
parser->treat_slash_as_special = rb_iv_get(self, "@treat_slash_as_special");
|
895
|
+
parser->space_to_underscore = rb_iv_get(self, "@space_to_underscore");
|
886
896
|
parser->special_link = Qfalse;
|
887
897
|
parser->line_ending = str_new_from_string(line_ending);
|
888
898
|
parser->base_indent = base_indent;
|
@@ -1872,7 +1882,7 @@ VALUE Wikitext_parser_parse(int argc, VALUE *argv, VALUE self)
|
|
1872
1882
|
// in internal link scope!
|
1873
1883
|
if (NIL_P(parser->link_text) || RSTRING_LEN(parser->link_text) == 0)
|
1874
1884
|
// use link target as link text
|
1875
|
-
parser->link_text = _Wikitext_parser_sanitize_link_target(parser
|
1885
|
+
parser->link_text = _Wikitext_parser_sanitize_link_target(parser, Qfalse);
|
1876
1886
|
else
|
1877
1887
|
parser->link_text = _Wikitext_parser_trim_link_target(parser->link_text);
|
1878
1888
|
_Wikitext_parser_encode_link_target(parser);
|
data/ext/wikitext.c
CHANGED
@@ -41,6 +41,7 @@ void Init_wikitext()
|
|
41
41
|
rb_define_attr(cWikitextParser, "mailto_class", Qtrue, Qtrue);
|
42
42
|
rb_define_attr(cWikitextParser, "autolink", Qtrue, Qtrue);
|
43
43
|
rb_define_attr(cWikitextParser, "treat_slash_as_special", Qtrue, Qtrue);
|
44
|
+
rb_define_attr(cWikitextParser, "space_to_underscore", Qtrue, Qtrue);
|
44
45
|
|
45
46
|
// Wikitext::Parser::Error
|
46
47
|
eWikitextParserError = rb_define_class_under(cWikitextParser, "Error", rb_eException);
|
data/spec/internal_link_spec.rb
CHANGED
@@ -16,7 +16,7 @@
|
|
16
16
|
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
17
17
|
require 'wikitext'
|
18
18
|
|
19
|
-
describe Wikitext::Parser, 'internal links' do
|
19
|
+
describe Wikitext::Parser, 'internal links (space to underscore off)' do
|
20
20
|
before do
|
21
21
|
@parser = Wikitext::Parser.new
|
22
22
|
end
|
@@ -443,3 +443,432 @@ describe Wikitext::Parser, 'internal links' do
|
|
443
443
|
end
|
444
444
|
end
|
445
445
|
end
|
446
|
+
|
447
|
+
describe Wikitext::Parser, 'internal links (space to underscore on)' do
|
448
|
+
before do
|
449
|
+
@parser = Wikitext::Parser.new
|
450
|
+
@parser.space_to_underscore = true
|
451
|
+
end
|
452
|
+
|
453
|
+
it 'should pass through unexpected link end tokens literally' do
|
454
|
+
@parser.parse('foo ]] bar').should == "<p>foo ]] bar</p>\n" # in plain scope
|
455
|
+
@parser.parse("foo '']]'' bar").should == "<p>foo <em>]]</em> bar</p>\n" # in EM scope
|
456
|
+
@parser.parse("foo ''']]''' bar").should == "<p>foo <strong>]]</strong> bar</p>\n" # in STRONG scope
|
457
|
+
@parser.parse("foo ''''']]''''' bar").should == "<p>foo <strong><em>]]</em></strong> bar</p>\n" # in STRONG_EM scope
|
458
|
+
@parser.parse('foo <tt>]]</tt> bar').should == "<p>foo <tt>]]</tt> bar</p>\n" # in TT scope
|
459
|
+
@parser.parse('= foo ]] bar =').should == "<h1>foo ]] bar</h1>\n" # in H1 scope
|
460
|
+
@parser.parse('== foo ]] bar ==').should == "<h2>foo ]] bar</h2>\n" # in H2 scope
|
461
|
+
@parser.parse('=== foo ]] bar ===').should == "<h3>foo ]] bar</h3>\n" # in H3 scope
|
462
|
+
@parser.parse('==== foo ]] bar ====').should == "<h4>foo ]] bar</h4>\n" # in H4 scope
|
463
|
+
@parser.parse('===== foo ]] bar =====').should == "<h5>foo ]] bar</h5>\n" # in H5 scope
|
464
|
+
@parser.parse('====== foo ]] bar ======').should == "<h6>foo ]] bar</h6>\n" # in H6 scope
|
465
|
+
@parser.parse('> ]]').should == "<blockquote>\n <p>]]</p>\n</blockquote>\n" # in BLOCKQUOTE scope
|
466
|
+
end
|
467
|
+
|
468
|
+
it 'should turn single words into links' do
|
469
|
+
@parser.parse('[[foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
470
|
+
end
|
471
|
+
|
472
|
+
it 'should turn multiple words into links, converting spaces into underscores' do
|
473
|
+
@parser.parse('[[foo bar]]').should == %Q{<p><a href="/wiki/foo_bar">foo bar</a></p>\n}
|
474
|
+
end
|
475
|
+
|
476
|
+
it 'should trim leading whitespace' do
|
477
|
+
@parser.parse('[[ foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
478
|
+
@parser.parse('[[ foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
479
|
+
@parser.parse('[[ foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
480
|
+
@parser.parse('[[ foo]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
481
|
+
end
|
482
|
+
|
483
|
+
it 'should trim trailing whitespace' do
|
484
|
+
@parser.parse('[[foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
485
|
+
@parser.parse('[[foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
486
|
+
@parser.parse('[[foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
487
|
+
@parser.parse('[[foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n} # was a bug (exception)
|
488
|
+
@parser.parse('[[foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n} # was a bug (crash)
|
489
|
+
end
|
490
|
+
|
491
|
+
it 'should trim leading and trailing whitespace (combined)' do
|
492
|
+
@parser.parse('[[ foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
493
|
+
@parser.parse('[[ foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
494
|
+
@parser.parse('[[ foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
495
|
+
@parser.parse('[[ foo ]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
496
|
+
end
|
497
|
+
|
498
|
+
it 'should convert embedded whitespace into underscores' do
|
499
|
+
@parser.parse('[[ foo bar ]]').should == %Q{<p><a href="/wiki/foo_bar">foo bar</a></p>\n}
|
500
|
+
@parser.parse('[[foo bar ]]').should == %Q{<p><a href="/wiki/foo_bar">foo bar</a></p>\n}
|
501
|
+
@parser.parse('[[ foo bar ]]').should == %Q{<p><a href="/wiki/foo_bar">foo bar</a></p>\n}
|
502
|
+
end
|
503
|
+
|
504
|
+
it 'should encode and sanitize quotes' do
|
505
|
+
# note how percent encoding is used in the href, and named entities in the link text
|
506
|
+
@parser.parse('[[hello "world"]]').should == %Q{<p><a href="/wiki/hello_%22world%22">hello "world"</a></p>\n}
|
507
|
+
end
|
508
|
+
|
509
|
+
it 'should encode and sanitize ampersands' do
|
510
|
+
@parser.parse('[[a & b]]').should == %Q{<p><a href="/wiki/a_%26_b">a & b</a></p>\n}
|
511
|
+
end
|
512
|
+
|
513
|
+
it 'should allow ampersand entities (special exception)' do
|
514
|
+
@parser.parse('[[a & b]]').should == %Q{<p><a href="/wiki/a_%26_b">a & b</a></p>\n}
|
515
|
+
end
|
516
|
+
|
517
|
+
it 'should allow quote entities (special exception)' do
|
518
|
+
@parser.parse('[[a " b]]').should == %Q{<p><a href="/wiki/a_%22_b">a " b</a></p>\n}
|
519
|
+
end
|
520
|
+
|
521
|
+
it 'should handle mixed scenarios (quotes, ampersands, non-ASCII characers)' do
|
522
|
+
expected = %Q{<p><a href="/wiki/foo%2c_%22bar%22_%26_baz_%e2%82%ac">foo, "bar" & baz €</a></p>\n}
|
523
|
+
@parser.parse('[[foo, "bar" & baz €]]').should == expected
|
524
|
+
end
|
525
|
+
|
526
|
+
it 'should handle links in paragraph flows' do
|
527
|
+
expected = %Q{<p>foo <a href="/wiki/bar">bar</a> baz</p>\n}
|
528
|
+
@parser.parse('foo [[bar]] baz').should == expected # was a bug
|
529
|
+
end
|
530
|
+
|
531
|
+
describe 'custom link text' do
|
532
|
+
it 'should recognize link text placed after the separator' do
|
533
|
+
@parser.parse('[[foo|bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
534
|
+
end
|
535
|
+
|
536
|
+
it 'should trim whitespace to the left of the separator' do
|
537
|
+
@parser.parse('[[foo |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
538
|
+
@parser.parse('[[foo |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
539
|
+
@parser.parse('[[foo |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
540
|
+
@parser.parse('[[foo |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
541
|
+
@parser.parse('[[foo |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
542
|
+
@parser.parse('[[foo |bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
543
|
+
end
|
544
|
+
|
545
|
+
it 'should trim whitespace to the right of the separator' do
|
546
|
+
@parser.parse('[[foo| bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
547
|
+
@parser.parse('[[foo| bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
548
|
+
@parser.parse('[[foo| bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
549
|
+
@parser.parse('[[foo| bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
550
|
+
@parser.parse('[[foo| bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
551
|
+
@parser.parse('[[foo| bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
552
|
+
end
|
553
|
+
|
554
|
+
it 'should trim whitespace on both sides of the separator (at the same time)' do
|
555
|
+
@parser.parse('[[foo | bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
556
|
+
@parser.parse('[[foo | bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
557
|
+
@parser.parse('[[foo | bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
558
|
+
@parser.parse('[[foo | bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
559
|
+
@parser.parse('[[foo | bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
560
|
+
@parser.parse('[[foo | bar]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
561
|
+
end
|
562
|
+
|
563
|
+
it 'should trim trailing whitespace from the link text' do
|
564
|
+
@parser.parse('[[foo|bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
565
|
+
@parser.parse('[[foo|bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
566
|
+
@parser.parse('[[foo|bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
567
|
+
@parser.parse('[[foo|bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
568
|
+
@parser.parse('[[foo|bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
569
|
+
@parser.parse('[[foo|bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
570
|
+
end
|
571
|
+
|
572
|
+
it 'should trim leading and trailing whitespace from the link text' do
|
573
|
+
@parser.parse('[[foo| bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
574
|
+
@parser.parse('[[foo| bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
575
|
+
@parser.parse('[[foo| bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
576
|
+
@parser.parse('[[foo| bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
577
|
+
@parser.parse('[[foo| bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
578
|
+
@parser.parse('[[foo| bar ]]').should == %Q{<p><a href="/wiki/foo">bar</a></p>\n}
|
579
|
+
end
|
580
|
+
|
581
|
+
it 'should treat a separator inside the link text as part of the link text' do
|
582
|
+
@parser.parse('[[foo|bar|baz]]').should == %Q{<p><a href="/wiki/foo">bar|baz</a></p>\n}
|
583
|
+
end
|
584
|
+
|
585
|
+
it 'should treat separators outside of links as normal text' do
|
586
|
+
@parser.parse('foo|bar').should == %Q{<p>foo|bar</p>\n}
|
587
|
+
end
|
588
|
+
|
589
|
+
it 'should allow em markup in the custom link text' do
|
590
|
+
expected = %Q{<p><a href="/wiki/foo">bar <em>baz</em></a></p>\n}
|
591
|
+
@parser.parse("[[foo|bar ''baz'']]").should == expected
|
592
|
+
end
|
593
|
+
|
594
|
+
it 'should automatically close unclosed em markup in the custom link text' do
|
595
|
+
expected = %Q{<p><a href="/wiki/foo">bar <em>baz</em></a></p>\n}
|
596
|
+
@parser.parse("[[foo|bar ''baz]]").should == expected
|
597
|
+
end
|
598
|
+
|
599
|
+
it 'should allow strong markup in the custom link text' do
|
600
|
+
expected = %Q{<p><a href="/wiki/foo">bar <strong>baz</strong></a></p>\n}
|
601
|
+
@parser.parse("[[foo|bar '''baz''']]").should == expected
|
602
|
+
end
|
603
|
+
|
604
|
+
it 'should automatically close unclosed strong markup in the custom link text' do
|
605
|
+
expected = %Q{<p><a href="/wiki/foo">bar <strong>baz</strong></a></p>\n}
|
606
|
+
@parser.parse("[[foo|bar '''baz]]").should == expected
|
607
|
+
end
|
608
|
+
|
609
|
+
it 'should allow strong/em markup in the custom link text' do
|
610
|
+
expected = %Q{<p><a href="/wiki/foo">bar <strong><em>baz</em></strong></a></p>\n}
|
611
|
+
@parser.parse("[[foo|bar '''''baz''''']]").should == expected
|
612
|
+
end
|
613
|
+
|
614
|
+
it 'should automatically close unclosed strong/em markup in the custom link text' do
|
615
|
+
expected = %Q{<p><a href="/wiki/foo">bar <strong><em>baz</em></strong></a></p>\n}
|
616
|
+
@parser.parse("[[foo|bar '''''baz]]").should == expected
|
617
|
+
end
|
618
|
+
|
619
|
+
it 'should allow tt markup in the custom link text' do
|
620
|
+
expected = %Q{<p><a href="/wiki/foo">bar <tt>baz</tt></a></p>\n}
|
621
|
+
@parser.parse('[[foo|bar <tt>baz</tt>]]').should == expected
|
622
|
+
end
|
623
|
+
|
624
|
+
it 'should automatically close unclosed tt markup in the custom link text' do
|
625
|
+
expected = %Q{<p><a href="/wiki/foo">bar <tt>baz</tt></a></p>\n}
|
626
|
+
@parser.parse('[[foo|bar <tt>baz]]').should == expected
|
627
|
+
end
|
628
|
+
|
629
|
+
it 'should allow named entities in the custom link text' do
|
630
|
+
expected = %Q{<p><a href="/wiki/foo">bar ©</a></p>\n}
|
631
|
+
@parser.parse('[[foo|bar ©]]').should == expected
|
632
|
+
|
633
|
+
# explicitly test " because it is tokenized separately from the other named entities
|
634
|
+
expected = %Q{<p><a href="/wiki/foo">bar "</a></p>\n}
|
635
|
+
@parser.parse('[[foo|bar "]]').should == expected
|
636
|
+
|
637
|
+
# explicitly test & because it is tokenized separately from the other named entities
|
638
|
+
expected = %Q{<p><a href="/wiki/foo">bar &</a></p>\n}
|
639
|
+
@parser.parse('[[foo|bar &]]').should == expected
|
640
|
+
end
|
641
|
+
|
642
|
+
it 'should allow decimal entities in the custom link text' do
|
643
|
+
expected = %Q{<p><a href="/wiki/foo">bar €</a></p>\n}
|
644
|
+
@parser.parse('[[foo|bar €]]').should == expected
|
645
|
+
end
|
646
|
+
|
647
|
+
it 'should allow hexadecimal entities in the custom link text' do
|
648
|
+
expected = %Q{<p><a href="/wiki/foo">bar €</a></p>\n}
|
649
|
+
@parser.parse('[[foo|bar €]]').should == expected
|
650
|
+
end
|
651
|
+
|
652
|
+
it 'should sanitize non-ASCII characters in the custom link text' do
|
653
|
+
expected = %Q{<p><a href="/wiki/foo">bar €</a></p>\n}
|
654
|
+
@parser.parse('[[foo|bar €]]').should == expected
|
655
|
+
end
|
656
|
+
|
657
|
+
it 'should sanitize characters that have special meaning in HTML in the custom link text' do
|
658
|
+
expected = %Q{<p><a href="/wiki/foo">bar <</a></p>\n}
|
659
|
+
@parser.parse('[[foo|bar <]]').should == expected
|
660
|
+
|
661
|
+
expected = %Q{<p><a href="/wiki/foo">bar ></a></p>\n}
|
662
|
+
@parser.parse('[[foo|bar >]]').should == expected
|
663
|
+
|
664
|
+
expected = %Q{<p><a href="/wiki/foo">bar &</a></p>\n}
|
665
|
+
@parser.parse('[[foo|bar &]]').should == expected
|
666
|
+
|
667
|
+
expected = %Q{<p><a href="/wiki/foo">bar "baz"</a></p>\n}
|
668
|
+
@parser.parse('[[foo|bar "baz"]]').should == expected
|
669
|
+
end
|
670
|
+
|
671
|
+
it 'should allow nowiki markup in the custom link text' do
|
672
|
+
expected = %Q{<p><a href="/wiki/foo">bar [[</a></p>\n}
|
673
|
+
@parser.parse("[[foo|bar <nowiki>[[</nowiki>]]").should == expected
|
674
|
+
|
675
|
+
expected = %Q{<p><a href="/wiki/foo">bar [</a></p>\n}
|
676
|
+
@parser.parse("[[foo|bar <nowiki>[</nowiki>]]").should == expected
|
677
|
+
|
678
|
+
expected = %Q{<p><a href="/wiki/foo">bar ]]</a></p>\n}
|
679
|
+
@parser.parse("[[foo|bar <nowiki>]]</nowiki>]]").should == expected
|
680
|
+
|
681
|
+
expected = %Q{<p><a href="/wiki/foo">bar ]</a></p>\n}
|
682
|
+
@parser.parse("[[foo|bar <nowiki>]</nowiki>]]").should == expected
|
683
|
+
end
|
684
|
+
end
|
685
|
+
|
686
|
+
describe 'overriding the link prefix' do
|
687
|
+
it 'should be able to override the link prefix' do
|
688
|
+
@parser.internal_link_prefix = '/custom/'
|
689
|
+
@parser.parse('[[foo]]').should == %Q{<p><a href="/custom/foo">foo</a></p>\n}
|
690
|
+
end
|
691
|
+
|
692
|
+
it 'should interpet a nil link prefix as meaning no prefix' do
|
693
|
+
@parser.internal_link_prefix = nil
|
694
|
+
@parser.parse('[[foo]]').should == %Q{<p><a href="foo">foo</a></p>\n}
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
describe 'special links' do
|
699
|
+
it 'should recognize links of the form "bug/10" as special links' do
|
700
|
+
@parser.parse('[[bug/10]]').should == %Q{<p><a href="/bug/10">bug/10</a></p>\n}
|
701
|
+
@parser.parse('[[issue/25]]').should == %Q{<p><a href="/issue/25">issue/25</a></p>\n}
|
702
|
+
@parser.parse('[[post/7]]').should == %Q{<p><a href="/post/7">post/7</a></p>\n}
|
703
|
+
end
|
704
|
+
|
705
|
+
it 'should not recognize special links when "treat_slash_as_special" is set to false' do
|
706
|
+
@parser.treat_slash_as_special = false
|
707
|
+
@parser.parse('[[bug/10]]').should == %Q{<p><a href="/wiki/bug%2f10">bug/10</a></p>\n}
|
708
|
+
@parser.parse('[[issue/25]]').should == %Q{<p><a href="/wiki/issue%2f25">issue/25</a></p>\n}
|
709
|
+
@parser.parse('[[post/7]]').should == %Q{<p><a href="/wiki/post%2f7">post/7</a></p>\n}
|
710
|
+
end
|
711
|
+
|
712
|
+
it 'should accept custom link text in conjunction with special links' do
|
713
|
+
@parser.parse('[[bug/10|bug #10]]').should == %Q{<p><a href="/bug/10">bug #10</a></p>\n}
|
714
|
+
end
|
715
|
+
|
716
|
+
it 'should ignore link prefix overrides when emitting special links' do
|
717
|
+
@parser.internal_link_prefix = '/custom/'
|
718
|
+
@parser.parse('[[bug/10]]').should == %Q{<p><a href="/bug/10">bug/10</a></p>\n}
|
719
|
+
end
|
720
|
+
|
721
|
+
it 'should not classify links as special merely because of the presence of a slash' do
|
722
|
+
# we want the syntax to be tight to minimize false positives
|
723
|
+
@parser.parse('[[foo/bar]]').should == %Q{<p><a href="/wiki/foo%2fbar">foo/bar</a></p>\n}
|
724
|
+
end
|
725
|
+
|
726
|
+
it 'should not accept special links which have a leading forward slash' do
|
727
|
+
# this is a syntax error
|
728
|
+
@parser.parse('[[/bug/10]]').should == %Q{<p><a href="/wiki/%2fbug%2f10">/bug/10</a></p>\n}
|
729
|
+
end
|
730
|
+
end
|
731
|
+
|
732
|
+
describe 'invalid links' do
|
733
|
+
it 'should not allow entities in the link text' do
|
734
|
+
@parser.parse('[[a € b]]').should == "<p>[[a € b]]</p>\n"
|
735
|
+
end
|
736
|
+
|
737
|
+
it 'should not allow URIs in the link text' do
|
738
|
+
expected = %Q{<p>[[hello <a href="http://example.com/" class="external">http://example.com/</a> world]]</p>\n}
|
739
|
+
@parser.parse('[[hello http://example.com/ world]]').should == expected
|
740
|
+
end
|
741
|
+
|
742
|
+
it 'should handle embedded [[ inside links' do
|
743
|
+
# note how first part "[[foo " in itself is invalid and so gets rejected and echoed literally
|
744
|
+
expected = %Q{<p>[[foo <a href="/wiki/bar">bar</a></p>\n}
|
745
|
+
@parser.parse('[[foo [[bar]]').should == expected
|
746
|
+
end
|
747
|
+
|
748
|
+
it 'should handled embedded ]] inside links' do
|
749
|
+
# note how the link gets terminated early and the trailing part is rejected and echoed literally
|
750
|
+
expected = %Q{<p><a href="/wiki/foo">foo</a>bar]]</p>\n}
|
751
|
+
@parser.parse('[[foo ]]bar]]').should == expected
|
752
|
+
end
|
753
|
+
|
754
|
+
it 'should handle embedded [ inside links' do
|
755
|
+
# [ is not allowed at all so the entire link is rendered invalid
|
756
|
+
expected = "<p>[[foo [bar]]</p>\n"
|
757
|
+
@parser.parse('[[foo [bar]]').should == expected
|
758
|
+
end
|
759
|
+
|
760
|
+
it 'should handle embedded ] inside links' do
|
761
|
+
# [ is not allowed at all so the entire link is rendered invalid
|
762
|
+
expected = "<p>[[foo ]bar]]</p>\n"
|
763
|
+
@parser.parse('[[foo ]bar]]').should == expected
|
764
|
+
end
|
765
|
+
|
766
|
+
describe 'unterminated link targets (end-of-file)' do
|
767
|
+
it 'should rollback and show the unterminated link' do
|
768
|
+
@parser.parse('[[foo').should == %Q{<p>[[foo</p>\n}
|
769
|
+
end
|
770
|
+
|
771
|
+
it 'should not trim leading whitespace when rolling back' do
|
772
|
+
@parser.parse('[[ foo').should == %Q{<p>[[ foo</p>\n}
|
773
|
+
@parser.parse('[[ foo').should == %Q{<p>[[ foo</p>\n}
|
774
|
+
@parser.parse('[[ foo').should == %Q{<p>[[ foo</p>\n}
|
775
|
+
@parser.parse('[[ foo').should == %Q{<p>[[ foo</p>\n}
|
776
|
+
end
|
777
|
+
|
778
|
+
it 'should not trim trailing whitespace when rolling back' do
|
779
|
+
@parser.parse('[[foo ').should == %Q{<p>[[foo </p>\n}
|
780
|
+
@parser.parse('[[foo ').should == %Q{<p>[[foo </p>\n}
|
781
|
+
@parser.parse('[[foo ').should == %Q{<p>[[foo </p>\n}
|
782
|
+
@parser.parse('[[foo ').should == %Q{<p>[[foo </p>\n}
|
783
|
+
end
|
784
|
+
|
785
|
+
it 'should not trim leadig and trailing whitespace (combined) when rolling back' do
|
786
|
+
@parser.parse('[[ foo ').should == %Q{<p>[[ foo </p>\n}
|
787
|
+
@parser.parse('[[ foo ').should == %Q{<p>[[ foo </p>\n}
|
788
|
+
@parser.parse('[[ foo ').should == %Q{<p>[[ foo </p>\n}
|
789
|
+
@parser.parse('[[ foo ').should == %Q{<p>[[ foo </p>\n}
|
790
|
+
end
|
791
|
+
end
|
792
|
+
|
793
|
+
describe 'unterminated link targets (end-of-line)' do
|
794
|
+
it 'should rollback and show the unterminated link' do
|
795
|
+
@parser.parse("[[foo\n").should == %Q{<p>[[foo</p>\n}
|
796
|
+
end
|
797
|
+
|
798
|
+
it 'should not trim leading whitespace when rolling back' do
|
799
|
+
@parser.parse("[[ foo\n").should == %Q{<p>[[ foo</p>\n}
|
800
|
+
@parser.parse("[[ foo\n").should == %Q{<p>[[ foo</p>\n}
|
801
|
+
@parser.parse("[[ foo\n").should == %Q{<p>[[ foo</p>\n}
|
802
|
+
@parser.parse("[[ foo\n").should == %Q{<p>[[ foo</p>\n}
|
803
|
+
end
|
804
|
+
|
805
|
+
it 'should not trim trailing whitespace when rolling back' do
|
806
|
+
@parser.parse("[[foo \n").should == %Q{<p>[[foo </p>\n}
|
807
|
+
@parser.parse("[[foo \n").should == %Q{<p>[[foo </p>\n}
|
808
|
+
@parser.parse("[[foo \n").should == %Q{<p>[[foo </p>\n}
|
809
|
+
@parser.parse("[[foo \n").should == %Q{<p>[[foo </p>\n}
|
810
|
+
end
|
811
|
+
|
812
|
+
it 'should not trim leading and trailing whitespace (combined) when rolling back' do
|
813
|
+
@parser.parse("[[ foo \n").should == %Q{<p>[[ foo </p>\n}
|
814
|
+
@parser.parse("[[ foo \n").should == %Q{<p>[[ foo </p>\n}
|
815
|
+
@parser.parse("[[ foo \n").should == %Q{<p>[[ foo </p>\n}
|
816
|
+
@parser.parse("[[ foo \n").should == %Q{<p>[[ foo </p>\n}
|
817
|
+
end
|
818
|
+
end
|
819
|
+
|
820
|
+
describe 'missing link text' do
|
821
|
+
it 'should use link target' do
|
822
|
+
@parser.parse('[[foo|]]').should == %Q{<p><a href="/wiki/foo">foo</a></p>\n}
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
describe 'link cut off at separator (end-of-file)' do
|
827
|
+
it 'should rollback and show the unterminated link' do
|
828
|
+
@parser.parse('[[foo|').should == %Q{<p>[[foo|</p>\n}
|
829
|
+
@parser.parse('[[foo| ').should == %Q{<p>[[foo| </p>\n}
|
830
|
+
@parser.parse('[[foo| ').should == %Q{<p>[[foo| </p>\n}
|
831
|
+
@parser.parse('[[foo| ').should == %Q{<p>[[foo| </p>\n}
|
832
|
+
@parser.parse('[[foo| ').should == %Q{<p>[[foo| </p>\n}
|
833
|
+
@parser.parse('[[foo| ').should == %Q{<p>[[foo| </p>\n}
|
834
|
+
@parser.parse('[[foo| ').should == %Q{<p>[[foo| </p>\n}
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
describe 'link cut off at separator (end-of-line)' do
|
839
|
+
it 'should rollback and show the unterminated link' do
|
840
|
+
@parser.parse("[[foo|\n").should == %Q{<p>[[foo|</p>\n}
|
841
|
+
@parser.parse("[[foo| \n").should == %Q{<p>[[foo| </p>\n}
|
842
|
+
@parser.parse("[[foo| \n").should == %Q{<p>[[foo| </p>\n}
|
843
|
+
@parser.parse("[[foo| \n").should == %Q{<p>[[foo| </p>\n}
|
844
|
+
@parser.parse("[[foo| \n").should == %Q{<p>[[foo| </p>\n}
|
845
|
+
@parser.parse("[[foo| \n").should == %Q{<p>[[foo| </p>\n}
|
846
|
+
@parser.parse("[[foo| \n").should == %Q{<p>[[foo| </p>\n}
|
847
|
+
end
|
848
|
+
end
|
849
|
+
|
850
|
+
describe 'unterminated link text (end-of-file)' do
|
851
|
+
it 'should rollback and show the unterminated link' do
|
852
|
+
@parser.parse('[[foo|hello').should == %Q{<p>[[foo|hello</p>\n}
|
853
|
+
@parser.parse('[[foo|hello ').should == %Q{<p>[[foo|hello </p>\n}
|
854
|
+
@parser.parse('[[foo|hello ').should == %Q{<p>[[foo|hello </p>\n}
|
855
|
+
@parser.parse('[[foo|hello ').should == %Q{<p>[[foo|hello </p>\n}
|
856
|
+
@parser.parse('[[foo|hello ').should == %Q{<p>[[foo|hello </p>\n}
|
857
|
+
@parser.parse('[[foo|hello ').should == %Q{<p>[[foo|hello </p>\n}
|
858
|
+
@parser.parse('[[foo|hello ').should == %Q{<p>[[foo|hello </p>\n}
|
859
|
+
end
|
860
|
+
end
|
861
|
+
|
862
|
+
describe 'unterminated link text (end-of-line)' do
|
863
|
+
it 'should rollback and show the unterminated link' do
|
864
|
+
@parser.parse("[[foo|hello\n").should == %Q{<p>[[foo|hello</p>\n}
|
865
|
+
@parser.parse("[[foo|hello \n").should == %Q{<p>[[foo|hello </p>\n}
|
866
|
+
@parser.parse("[[foo|hello \n").should == %Q{<p>[[foo|hello </p>\n}
|
867
|
+
@parser.parse("[[foo|hello \n").should == %Q{<p>[[foo|hello </p>\n}
|
868
|
+
@parser.parse("[[foo|hello \n").should == %Q{<p>[[foo|hello </p>\n}
|
869
|
+
@parser.parse("[[foo|hello \n").should == %Q{<p>[[foo|hello </p>\n}
|
870
|
+
@parser.parse("[[foo|hello \n").should == %Q{<p>[[foo|hello </p>\n}
|
871
|
+
end
|
872
|
+
end
|
873
|
+
end
|
874
|
+
end
|
data/spec/wikitext_spec.rb
CHANGED
@@ -16,6 +16,16 @@
|
|
16
16
|
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
17
17
|
require 'wikitext'
|
18
18
|
|
19
|
+
describe Wikitext::Parser do
|
20
|
+
before do
|
21
|
+
@parser = Wikitext::Parser.new
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should turn space-to-underscore off by default' do
|
25
|
+
@parser.space_to_underscore.should == false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
19
29
|
describe Wikitext::Parser, 'parsing non-ASCII input' do
|
20
30
|
before do
|
21
31
|
@parser = Wikitext::Parser.new
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wikitext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.2"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wincent Colaiuta
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-02-
|
12
|
+
date: 2008-02-18 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|