/[socialtext-import]/Pod-Simple-Wiki/lib/Pod/Simple/Wiki.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /Pod-Simple-Wiki/lib/Pod/Simple/Wiki.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 4 - (show annotations)
Tue Nov 28 14:17:33 2006 UTC (17 years, 5 months ago) by dpavlin
File size: 22366 byte(s)
add support for socialtext markup to Pod::Simple::Wiki
1 package Pod::Simple::Wiki;
2
3 ###############################################################################
4 #
5 # Pod::Simple::Wiki - A class for creating Pod to Wiki filters.
6 #
7 #
8 # Copyright 2003-2005, John McNamara, jmcnamara@cpan.org
9 #
10 # Documentation after __END__
11 #
12
13 use strict;
14 #use Pod::Simple::Debug (5);
15 use Pod::Simple;
16 use vars qw(@ISA $VERSION);
17
18 @ISA = qw(Pod::Simple);
19 $VERSION = '0.05';
20
21 my $_debug = 0;
22
23
24 ###############################################################################
25 ###############################################################################
26 #
27 # The tag mappings for various Wiki text formats
28 #
29
30 my %tags = (
31 'wiki' => {
32 '<b>' => "'''",
33 '</b>' => "'''",
34 '<i>' => "''",
35 '</i>' => "''",
36 '<tt>' => '"',
37 '</tt>' => '"',
38 '<pre>' => '',
39 '</pre>' => "\n\n",
40
41 '<h1>' => "\n----\n'''",
42 '</h1>' => "'''\n\n",
43 '<h2>' => "\n'''''",
44 '</h2>' => "'''''\n\n",
45 '<h3>' => "\n''",
46 '</h3>' => "''\n\n",
47 '<h4>' => "\n",
48 '</h4>' => "\n\n",
49 },
50
51 'kwiki' => {
52 '<b>' => '*',
53 '</b>' => '*',
54 '<i>' => '/',
55 '</i>' => '/',
56 '<tt>' => '[=',
57 '</tt>' => ']',
58 '<pre>' => '',
59 '</pre>' => "\n\n",
60
61 '<h1>' => "\n----\n= ",
62 '</h1>' => " =\n\n",
63 '<h2>' => "\n== ",
64 '</h2>' => " ==\n\n",
65 '<h3>' => "\n=== ",
66 '</h3>' => " ===\n\n",
67 '<h4>' => "==== ",
68 '</h4>' => "\n\n",
69 },
70
71 'usemod' => {
72 '<b>' => '<b>',
73 '</b>' => '</b>',
74 '<i>' => '<i>',
75 '</i>' => '</i>',
76 '<tt>' => '<tt>',
77 '</tt>' => '</tt>',
78 '<pre>' => "\n<pre>\n",
79 '</pre>' => "\n</pre>\n\n",
80
81 '<h1>' => "\n= ",
82 '</h1>' => " =\n\n",
83 '<h2>' => "\n== ",
84 '</h2>' => " ==\n\n",
85 '<h3>' => "\n=== ",
86 '</h3>' => " ===\n",
87 '<h4>' => "\n==== ",
88 '</h4>' => " ====\n\n",
89 },
90
91 'usemod_classic' => {
92 '<b>' => "'''",
93 '</b>' => "'''",
94 '<i>' => "''",
95 '</i>' => "''",
96 '<tt>' => '<tt>',
97 '</tt>' => '</tt>',
98 '<pre>' => "\n<pre>\n",
99 '</pre>' => "\n</pre>\n\n",
100
101 '<h1>' => "\n= ",
102 '</h1>' => " =\n\n",
103 '<h2>' => "\n== ",
104 '</h2>' => " ==\n\n",
105 '<h3>' => "\n=== ",
106 '</h3>' => " ===\n",
107 '<h4>' => "\n==== ",
108 '</h4>' => " ====\n\n",
109 },
110
111
112 'twiki' => {
113 '<b>' => "*",
114 '</b>' => "*",
115 '<i>' => "_",
116 '</i>' => "_",
117 '<tt>' => '=',
118 '</tt>' => '=',
119 '<pre>' => "\n<verbatim>\n",
120 '</pre>' => "\n</verbatim>\n\n",
121
122 '<h1>' => "---+ ",
123 '</h1>' => "\n\n",
124 '<h2>' => "---++ ",
125 '</h2>' => "\n\n",
126 '<h3>' => "---+++ ",
127 '</h3>' => "\n\n",
128 '<h4>' => "---++++ ",
129 '</h4>' => "\n\n",
130 },
131 'wikipedia' => {
132 '<b>' => "'''",
133 '</b>' => "'''",
134 '<i>' => "''",
135 '</i>' => "''",
136 '<tt>' => '<tt>',
137 '</tt>' => '</tt>',
138 '<pre>' => "\n<code>\n",
139 '</pre>' => "\n</code>\n",
140
141 '<h1>' => "==",
142 '</h1>' => "==\n",
143 '<h2>' => "===",
144 '</h2>' => "===\n",
145 '<h3>' => "====",
146 '</h3>' => "====\n",
147 '<h4>' => "=====",
148 '</h4>' => "=====\n",
149 },
150 'socialtext' => {
151 '<b>' => '*',
152 '</b>' => '*',
153 '<i>' => '_',
154 '</i>' => '_',
155 '<tt>' => '`',
156 '</tt>' => '`',
157 '<pre>' => "\n.pre\n",
158 '</pre>' => "\n.pre\n",
159
160 '<h1>' => "^ ",
161 '</h1>' => "\n",
162 '<h2>' => "^^ ",
163 '</h2>' => "\n",
164 '<h3>' => "^^^ ",
165 '</h3>' => "\n",
166 '<h4>' => "^^^^ ",
167 '</h4>' => "\n",
168 },
169
170 );
171
172
173 ###############################################################################
174 #
175 # new()
176 #
177 # Simple constructor inheriting from Pod::Simple.
178 #
179 sub new {
180
181 my $class = shift;
182 my $format = lc shift || 'wiki';
183 $format = 'wikipedia' if $format eq 'mediawiki';
184 $format = 'wiki' unless exists $tags{$format};
185
186 my $self = Pod::Simple->new(@_);
187 $self->{_wiki_text} = '';
188 $self->{_format} = $format;
189 $self->{_tags} = $tags{$format};
190 $self->{output_fh} ||= *STDOUT{IO};
191
192 bless $self, $class;
193 return $self;
194 }
195
196
197 ###############################################################################
198 #
199 # _append()
200 #
201 # Appends some text to the buffered Wiki text.
202 #
203 sub _append {
204
205 my $self = shift;
206
207 $self->{_wiki_text} .= $_[0];
208 }
209
210
211 ###############################################################################
212 #
213 # _output()
214 #
215 # Appends some text to the buffered Wiki text and then emits it. Also resets
216 # the buffer.
217 #
218 sub _output {
219
220 my $self = shift;
221 my $text = $_[0];
222
223 $text = '' unless defined $text;
224
225 print {$self->{output_fh}} $self->{_wiki_text}, $text;
226
227 $self->{_wiki_text} = '';
228 }
229
230
231 ###############################################################################
232 #
233 # _indent_item()
234 #
235 # Indents an "over-item" to the correct level.
236 #
237 sub _indent_item {
238
239 my $self = shift;
240 my $item_type = $_[0];
241 my $item_param = $_[1];
242 my $indent_level = $self->{_item_indent};
243
244 if ($self->{_format} eq 'wiki') {
245
246 if ($item_type eq 'bullet') {
247 $self->_append("*" x $indent_level);
248 # This was the way C2 Wiki used to define a bullet list
249 # $self->_append("\t" x $indent_level . '*');
250 }
251 elsif ($item_type eq 'number') {
252 $self->_append("\t" x $indent_level . $item_param);
253 }
254 elsif ($item_type eq 'text') {
255 $self->_append("\t" x $indent_level);
256 }
257 }
258 elsif ($self->{_format} eq 'kwiki') {
259
260 if ($item_type eq 'bullet') {
261 $self->_append('*' x $indent_level . ' ');
262 }
263 elsif ($item_type eq 'number') {
264 $self->_append('0' x $indent_level . ' ');
265 }
266 elsif ($item_type eq 'text') {
267 $self->_append(";" x $indent_level . ' ');
268 }
269 }
270 elsif ($self->{_format} eq 'usemod') {
271
272 if ($item_type eq 'bullet') {
273 $self->_append('*' x $indent_level);
274 }
275 elsif ($item_type eq 'number') {
276 $self->_append('#' x $indent_level);
277 }
278 elsif ($item_type eq 'text') {
279 $self->_append(";" x $indent_level);
280 }
281 }
282 elsif ($self->{_format} eq 'twiki') {
283
284 if ($item_type eq 'bullet') {
285 $self->_append(' ' x $indent_level . "* ");
286 }
287 elsif ($item_type eq 'number') {
288 $self->_append(' ' x $indent_level . $item_param . ". ");
289 }
290 elsif ($item_type eq 'text') {
291 $self->_append(' ' x $indent_level . '$ ' );
292 }
293 }
294 elsif ($self->{_format} eq 'wikipedia') {
295
296 if ($item_type eq 'bullet') {
297 $self->_append('*' x $indent_level . ' ');
298 }
299 elsif ($item_type eq 'number') {
300 $self->_append('#' x $indent_level . ' ');
301 }
302 elsif ($item_type eq 'text') {
303 $self->_append(";" x $indent_level . ' ');
304 }
305 }
306 elsif ($self->{_format} eq 'socialtext') {
307
308 if ($item_type eq 'bullet') {
309 $self->_append('*' x $indent_level . ' ');
310 }
311 elsif ($item_type eq 'number') {
312 $self->_append('#' x $indent_level . ' ');
313 }
314 elsif ($item_type eq 'text') {
315 $self->_append(">" x $indent_level . ' ');
316 }
317 }
318 }
319
320
321 ###############################################################################
322 #
323 # _skip_headings()
324 #
325 # Formatting in headings doesn't look great or is ignored in some formats.
326 #
327 sub _skip_headings {
328
329 my $self = shift;
330
331 return (
332 $self->{_format} eq 'kwiki' and
333 ($self->{_in_head1} or
334 $self->{_in_head2} or
335 $self->{_in_head3} or
336 $self->{_in_head4})
337 );
338 }
339
340
341 ###############################################################################
342 #
343 # _append_tag()
344 #
345 # Add an open or close tag to the current text.
346 #
347 sub _append_tag {
348
349 my $self = shift;
350 my $tag = $_[0];
351
352 $self->_append($self->{_tags}->{$tag});
353 }
354
355
356 ###############################################################################
357 ###############################################################################
358 #
359 # The methods in the following section are required by Pod::Simple to handle
360 # Pod directives and elements.
361 #
362 # The methods _handle_element_start() _handle_element_end() and _handle_text()
363 # are called by Pod::Simple in response to Pod constructs. We use
364 # _handle_element_start() and _handle_element_end() to generate calls to more
365 # specific methods. This is basically a long-hand version of Pod::Simple::
366 # Methody with the addition of location tracking.
367 #
368
369
370 ###############################################################################
371 #
372 # _handle_element_start()
373 #
374 # Call a method to handle the start of a element if one has been defined.
375 # We also set a flag to indicate that we are "in" the element type.
376 #
377 sub _handle_element_start {
378
379 my $self = shift;
380 my $element = $_[0];
381
382 $element =~ tr/-/_/;
383
384 print "<$element>\n" if $_debug;
385
386 $self->{"_in_". $element}++;
387
388 if (my $method = $self->can('_start_' . $element)) {
389 $method->($self, $_[1]);
390 }
391 }
392
393
394 ###############################################################################
395 #
396 # _handle_element_end()
397 #
398 # Call a method to handle the end of a element if one has been defined.
399 # We also set a flag to indicate that we are "out" of the element type.
400 #
401 sub _handle_element_end {
402
403 my $self = shift;
404 my $element = $_[0];
405
406 $element =~ tr/-/_/;
407
408 if (my $method = $self->can('_end_' . $element)) {
409 $method->($self);
410 }
411
412 $self->{"_in_". $element}--;
413
414 print "</$element>\n" if $_debug;
415 }
416
417
418 ###############################################################################
419 #
420 # _handle_text()
421 #
422 # Perform any necessary transforms on the text. This is mainly used to escape
423 # inadvertent CamelCase words.
424 #
425 sub _handle_text {
426
427 my $self = shift;
428 my $text = $_[0];
429
430 # Only escape CamelCase in Kwiki paragraphs
431 if ($self->{_format} eq 'kwiki' and not $self->{_in_Para}) {
432 $self->{_wiki_text} .= $text;
433 return;
434 }
435
436 # Split the text into tokens but maintain the whitespace
437 my @tokens = split /(\s+)/, $text;
438
439 if ($self->{_format} eq 'wiki') {
440 for (@tokens) {
441 next unless /\S/; # Ignore the whitespace
442 next if m[^(ht|f)tp://]; # Ignore URLs
443 s/([A-Z][a-z]+)(?=[A-Z])/$1''''''/g # Escape with 6 single quotes
444
445 }
446 }
447 elsif ($self->{_format} eq 'kwiki') {
448 for (@tokens) {
449 next unless /\S/; # Ignore the whitespace
450 next if m[^(ht|f)tp://]; # Ignore URLs
451 s/([A-Z][a-z]+[A-Z]\w+)/!$1/g; # Escape with !
452 }
453 }
454 # TODO: Add usemod <nowiki> escapes
455
456 # Rejoin the tokens and whitespace.
457 $self->{_wiki_text} .= join '', @tokens;
458 }
459
460
461 ###############################################################################
462 #
463 # Functions to deal with the I<>, B<> and C<> formatting codes.
464 #
465 sub _start_I {$_[0]->_append_tag('<i>') unless $_[0]->_skip_headings()}
466 sub _start_B {$_[0]->_append_tag('<b>') unless $_[0]->_skip_headings()}
467 sub _start_C {$_[0]->_append_tag('<tt>') unless $_[0]->_skip_headings()}
468 sub _start_F {$_[0]->_start_I}
469
470 sub _end_I {$_[0]->_append_tag('</i>') unless $_[0]->_skip_headings()}
471 sub _end_B {$_[0]->_append_tag('</b>') unless $_[0]->_skip_headings()}
472 sub _end_C {$_[0]->_append_tag('</tt>') unless $_[0]->_skip_headings()}
473 sub _end_F {$_[0]->_end_I}
474
475
476 ###############################################################################
477 #
478 # Functions to deal with the Pod =head directives
479 #
480 sub _start_head1 {$_[0]->_append_tag('<h1>')}
481 sub _start_head2 {$_[0]->_append_tag('<h2>')}
482 sub _start_head3 {$_[0]->_append_tag('<h3>')}
483 sub _start_head4 {$_[0]->_append_tag('<h4>')}
484
485 sub _end_head1 {$_[0]->_append_tag('</h1>'); $_[0]->_output()}
486 sub _end_head2 {$_[0]->_append_tag('</h2>'); $_[0]->_output()}
487 sub _end_head3 {$_[0]->_append_tag('</h3>'); $_[0]->_output()}
488 sub _end_head4 {$_[0]->_append_tag('</h4>'); $_[0]->_output()}
489
490
491 ###############################################################################
492 #
493 # Functions to deal with verbatim paragraphs. We emit the text "as is" for now.
494 # TODO: escape any Wiki formatting in text such as ''code''.
495 #
496 sub _start_Verbatim {$_[0]->_append_tag('<pre>')}
497 sub _end_Verbatim {$_[0]->_append_tag('</pre>'); $_[0]->_output()}
498
499
500 ###############################################################################
501 #
502 # Functions to deal with =over ... =back regions for
503 #
504 # Bulleted lists
505 # Numbered lists
506 # Text lists
507 # Block lists
508 #
509 sub _start_over_bullet {$_[0]->{_item_indent}++}
510 sub _start_over_number {$_[0]->{_item_indent}++}
511 sub _start_over_text {$_[0]->{_item_indent}++}
512
513 sub _end_over_bullet {$_[0]->{_item_indent}--;
514 $_[0]->_output("\n") unless $_[0]->{_item_indent}}
515
516 sub _end_over_number {$_[0]->{_item_indent}--;
517 $_[0]->_output("\n") unless $_[0]->{_item_indent}}
518
519 sub _end_over_text {$_[0]->{_item_indent}--;
520 $_[0]->_output("\n") unless $_[0]->{_item_indent}}
521
522 sub _start_item_bullet {$_[0]->_indent_item('bullet')}
523 sub _start_item_number {$_[0]->_indent_item('number', $_[1]->{number})}
524 sub _start_item_text {$_[0]->_indent_item('text')}
525
526 sub _end_item_bullet {$_[0]->_output("\n")}
527 sub _end_item_number {$_[0]->_output("\n")}
528 sub _end_item_text {$_[0]->_output(":\t") if $_[0]->{_format} eq 'wiki';
529 $_[0]->_output(" ; ") if $_[0]->{_format} eq 'kwiki';
530 $_[0]->_output(":" ) if $_[0]->{_format} eq 'usemod';
531 $_[0]->_output(": " ) if $_[0]->{_format} eq 'twiki';
532 $_[0]->_output(" : ") if $_[0]->{_format} eq 'wikipedia';
533 $_[0]->_output("\n") if $_[0]->{_format} eq 'socialtext';}
534
535 sub _start_over_block {$_[0]->{_item_indent}++}
536 sub _end_over_block {$_[0]->{_item_indent}--}
537
538
539 ###############################################################################
540 #
541 # _start_Para()
542 #
543 # Special handling for paragraphs that are part of an "over" block.
544 #
545 sub _start_Para {
546
547 my $self = shift;
548 my $indent_level = $self->{_item_indent};
549
550 if ($self->{_in_over_block}) {
551
552 if ($self->{_format} eq 'wiki') {
553 $self->_append(("\t" x $indent_level) . " :\t");
554 }
555 elsif ($self->{_format} eq 'usemod') {
556 $self->_append(":" x $indent_level);
557 }
558 }
559 }
560
561
562 ###############################################################################
563 #
564 # _end_Para()
565 #
566 # Special handling for paragraphs that are part of an "over_text" block.
567 # This is mainly required be Kwiki.
568 #
569 sub _end_Para {
570
571 my $self = shift;
572
573 # Only add a newline if the paragraph isn't part of a text
574 if ($self->{_in_over_text}) {
575 # Workaround for the fact that Kwiki doesn't have a definition block
576 #$self->_output("\n") if $self->{_format} eq 'kwiki';
577 }
578 else {
579 $self->_output("\n");
580 }
581
582 $self->_output("\n")
583 }
584
585
586 1;
587
588
589 __END__
590
591 =head1 NAME
592
593 Pod::Simple::Wiki - A class for creating Pod to Wiki filters.
594
595 =head1 SYNOPSIS
596
597 To create a simple C<pod2wiki> filter:
598
599 #!/usr/bin/perl -w
600
601 use strict;
602 use Pod::Simple::Wiki;
603
604
605 my $parser = Pod::Simple::Wiki->new();
606
607 if (defined $ARGV[0]) {
608 open IN, $ARGV[0] or die "Couldn't open $ARGV[0]: $!\n";
609 } else {
610 *IN = *STDIN;
611 }
612
613 if (defined $ARGV[1]) {
614 open OUT, ">$ARGV[1]" or die "Couldn't open $ARGV[1]: $!\n";
615 } else {
616 *OUT = *STDOUT;
617 }
618
619 $parser->output_fh(*OUT);
620 $parser->parse_file(*IN);
621
622 __END__
623
624
625 =head1 DESCRIPTION
626
627 The C<Pod::Simple::Wiki> module is used for converting Pod text to Wiki text.
628
629 Pod (Plain Old Documentation) is a simple markup language used for writing Perl documentation.
630
631 A Wiki is a user extensible web site. It uses very simple mark-up that is converted to Html.
632
633 For an introduction to Wikis see: http://en.wikipedia.org/wiki/Wiki
634
635
636 =head1 METHODS
637
638 =head2 new()
639
640 The C<new> method is used to create a new L<Pod::Simple::Wiki> object. It is also used to set the output Wiki format.
641
642 my $parser1 = Pod::Simple::Wiki->new('wiki');
643 my $parser2 = Pod::Simple::Wiki->new('kwiki');
644 my $parser3 = Pod::Simple::Wiki->new(); # Defaults to 'wiki'
645
646 The currently supported formats are:
647
648 =over 4
649
650 =item wiki
651
652 This is the original Wiki format as used on Ward Cunningham's Portland repository of Patterns. The formatting rules are given at http://c2.com/cgi/wiki?TextFormattingRules
653
654 =item kwiki
655
656 This is the format as used by Brian Ingerson's CGI::Kwiki: http://search.cpan.org/dist/CGI-Kwiki/
657
658 =item usemod
659
660 This is the format used by the Usemod wikis. See: http://www.usemod.com/cgi-bin/wiki.pl?WikiFormat
661
662 =item twiki
663
664 This is the format used by TWiki wikis. See: http://www.twiki.org/
665
666 =item wikipedia or mediawiki
667
668 This is the format used by Wikipedia and MediaWiki wikis. See: http://www.wikipedia.org/
669
670 =back
671
672 If no format is specified the parser defaults to C<wiki>.
673
674 Any other parameters in C<new> will be passed on to the parent L<Pod::Simple> object. See L<Pod::Simple> for more details.
675
676
677 =head2 Other methods
678
679 Pod::Simple::Wiki inherits all of the methods of L<Pod::Simple>. See L<Pod::Simple> for more details.
680
681
682 =head1 TODO
683
684 =over 4
685
686 =item *
687
688 Add more code, more tests and a few more users if possible.
689
690 =item *
691
692 Add other Wiki formats. Send requests or patches.
693
694 =item *
695
696 Fix some of the C<=over> edge cases. See the TODOs in the test programs.
697
698 =back
699
700
701
702 =head1 SEE ALSO
703
704 This module also installs a C<pod2wiki> command line utility. See C<pod2wiki --help> for details.
705
706
707 =head1 ACKNOWLEDGEMENTS
708
709 Thanks to Sean M. Burke for C<Pod::Simple>. It may not be simple but sub-classing it is. C<:-)>
710
711 Thanks to Sam Tregar for TWiki support.
712
713 Thanks Tony Sidaway for Wikipedia/MediaWiki support.
714
715
716 =head1 AUTHOR
717
718 John McNamara jmcnamara@cpan.org
719
720
721 =head1 COPYRIGHT
722
723 © MMIII-MMV, John McNamara.
724
725 All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.
726

  ViewVC Help
Powered by ViewVC 1.1.26