bixbite 0.1.0

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.
Files changed (194) hide show
  1. data/LICENSE +20 -0
  2. data/README.markdown +49 -0
  3. data/VERSION +1 -0
  4. data/bin/bixbite +73 -0
  5. data/lib/bixbite.rb +13 -0
  6. data/lib/bixbite/command.rb +14 -0
  7. data/lib/bixbite/create.rb +76 -0
  8. data/template/Rakefile +25 -0
  9. data/template/assets/bixbite/Rakefile.rb +297 -0
  10. data/template/assets/naturaldocs/NaturalDocs/Config/Languages.txt +286 -0
  11. data/template/assets/naturaldocs/NaturalDocs/Config/Topics.txt +382 -0
  12. data/template/assets/naturaldocs/NaturalDocs/Help/customizinglanguages.html +52 -0
  13. data/template/assets/naturaldocs/NaturalDocs/Help/customizingtopics.html +74 -0
  14. data/template/assets/naturaldocs/NaturalDocs/Help/documenting.html +58 -0
  15. data/template/assets/naturaldocs/NaturalDocs/Help/documenting/reference.html +146 -0
  16. data/template/assets/naturaldocs/NaturalDocs/Help/documenting/walkthrough.html +180 -0
  17. data/template/assets/naturaldocs/NaturalDocs/Help/example/Default.css +528 -0
  18. data/template/assets/naturaldocs/NaturalDocs/Help/example/NaturalDocs.js +204 -0
  19. data/template/assets/naturaldocs/NaturalDocs/Help/examples.css +90 -0
  20. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/background.png +0 -0
  21. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/leftside.png +0 -0
  22. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/logo.png +0 -0
  23. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overbody.png +0 -0
  24. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overbodybg.png +0 -0
  25. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overleftmargin.png +0 -0
  26. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overmenu.png +0 -0
  27. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/overmenubg.png +0 -0
  28. data/template/assets/naturaldocs/NaturalDocs/Help/images/header/rightside.png +0 -0
  29. data/template/assets/naturaldocs/NaturalDocs/Help/images/logo.gif +0 -0
  30. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/about.png +0 -0
  31. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/background.png +0 -0
  32. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/bottomleft.png +0 -0
  33. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/bottomright.png +0 -0
  34. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/community.png +0 -0
  35. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/customizing.png +0 -0
  36. data/template/assets/naturaldocs/NaturalDocs/Help/images/menu/using.png +0 -0
  37. data/template/assets/naturaldocs/NaturalDocs/Help/index.html +9 -0
  38. data/template/assets/naturaldocs/NaturalDocs/Help/javascript/BrowserStyles.js +77 -0
  39. data/template/assets/naturaldocs/NaturalDocs/Help/javascript/PNGHandling.js +72 -0
  40. data/template/assets/naturaldocs/NaturalDocs/Help/keywords.html +38 -0
  41. data/template/assets/naturaldocs/NaturalDocs/Help/languages.html +32 -0
  42. data/template/assets/naturaldocs/NaturalDocs/Help/menu.html +79 -0
  43. data/template/assets/naturaldocs/NaturalDocs/Help/output.html +84 -0
  44. data/template/assets/naturaldocs/NaturalDocs/Help/running.html +40 -0
  45. data/template/assets/naturaldocs/NaturalDocs/Help/styles.css +290 -0
  46. data/template/assets/naturaldocs/NaturalDocs/Help/styles.html +52 -0
  47. data/template/assets/naturaldocs/NaturalDocs/Help/troubleshooting.html +18 -0
  48. data/template/assets/naturaldocs/NaturalDocs/Info/CSSGuide.txt +947 -0
  49. data/template/assets/naturaldocs/NaturalDocs/Info/File Parsing.txt +83 -0
  50. data/template/assets/naturaldocs/NaturalDocs/Info/HTMLTestCases.pm +269 -0
  51. data/template/assets/naturaldocs/NaturalDocs/Info/Languages.txt +107 -0
  52. data/template/assets/naturaldocs/NaturalDocs/Info/NDMarkup.txt +91 -0
  53. data/template/assets/naturaldocs/NaturalDocs/Info/Symbol Management.txt +59 -0
  54. data/template/assets/naturaldocs/NaturalDocs/Info/images/Logo.png +0 -0
  55. data/template/assets/naturaldocs/NaturalDocs/JavaScript/NaturalDocs.js +836 -0
  56. data/template/assets/naturaldocs/NaturalDocs/License-GPL.txt +341 -0
  57. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/BinaryFile.pm +294 -0
  58. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder.pm +280 -0
  59. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/Base.pm +348 -0
  60. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/FramedHTML.pm +345 -0
  61. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/HTML.pm +398 -0
  62. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Builder/HTMLBase.pm +3693 -0
  63. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ClassHierarchy.pm +860 -0
  64. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ClassHierarchy/Class.pm +412 -0
  65. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ClassHierarchy/File.pm +157 -0
  66. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ConfigFile.pm +497 -0
  67. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Constants.pm +165 -0
  68. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/DefineMembers.pm +100 -0
  69. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Error.pm +305 -0
  70. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/File.pm +540 -0
  71. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ImageReferenceTable.pm +383 -0
  72. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ImageReferenceTable/Reference.pm +44 -0
  73. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ImageReferenceTable/String.pm +110 -0
  74. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages.pm +1475 -0
  75. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/ActionScript.pm +1473 -0
  76. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Ada.pm +38 -0
  77. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Advanced.pm +828 -0
  78. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Advanced/Scope.pm +95 -0
  79. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Advanced/ScopeChange.pm +70 -0
  80. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Base.pm +832 -0
  81. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/CSharp.pm +1484 -0
  82. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/PLSQL.pm +319 -0
  83. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Pascal.pm +143 -0
  84. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Perl.pm +1370 -0
  85. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Prototype.pm +92 -0
  86. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Prototype/Parameter.pm +87 -0
  87. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Simple.pm +503 -0
  88. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Languages/Tcl.pm +219 -0
  89. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Menu.pm +3406 -0
  90. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Menu/Entry.pm +201 -0
  91. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/NDMarkup.pm +76 -0
  92. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser.pm +1331 -0
  93. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser/JavaDoc.pm +464 -0
  94. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser/Native.pm +1060 -0
  95. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Parser/ParsedTopic.pm +253 -0
  96. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Project.pm +1402 -0
  97. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Project/ImageFile.pm +160 -0
  98. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Project/SourceFile.pm +113 -0
  99. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/ReferenceString.pm +334 -0
  100. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Settings.pm +1418 -0
  101. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Settings/BuildTarget.pm +66 -0
  102. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB.pm +678 -0
  103. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/Extension.pm +84 -0
  104. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/File.pm +129 -0
  105. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/Item.pm +201 -0
  106. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/ItemDefinition.pm +45 -0
  107. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SourceDB/WatchedFileDefinitions.pm +159 -0
  108. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/StatusMessage.pm +102 -0
  109. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolString.pm +212 -0
  110. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable.pm +1984 -0
  111. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/File.pm +186 -0
  112. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/IndexElement.pm +522 -0
  113. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/Reference.pm +273 -0
  114. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/ReferenceTarget.pm +97 -0
  115. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/Symbol.pm +428 -0
  116. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/SymbolTable/SymbolDefinition.pm +96 -0
  117. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Topics.pm +1319 -0
  118. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Topics/Type.pm +151 -0
  119. data/template/assets/naturaldocs/NaturalDocs/Modules/NaturalDocs/Version.pm +384 -0
  120. data/template/assets/naturaldocs/NaturalDocs/NaturalDocs +400 -0
  121. data/template/assets/naturaldocs/NaturalDocs/NaturalDocs.bat +17 -0
  122. data/template/assets/naturaldocs/NaturalDocs/Styles/Default.css +767 -0
  123. data/template/assets/naturaldocs/NaturalDocs/Styles/Roman.css +765 -0
  124. data/template/assets/naturaldocs/NaturalDocs/Styles/Small.css +763 -0
  125. data/template/assets/utilities/pngout +0 -0
  126. data/template/deploy/public_html/.htaccess +0 -0
  127. data/template/documentation/js/.htaccess +0 -0
  128. data/template/src/html/.htaccess +76 -0
  129. data/template/src/html/css/cmn/global.css +96 -0
  130. data/template/src/html/css/cmn/ie.css +15 -0
  131. data/template/src/html/css/cmn/ie6.css +15 -0
  132. data/template/src/html/images/cmn/.htaccess +0 -0
  133. data/template/src/html/images/tmp/.htaccess +0 -0
  134. data/template/src/html/includes/debug.inc +5 -0
  135. data/template/src/html/includes/footer.inc +52 -0
  136. data/template/src/html/includes/header.inc +61 -0
  137. data/template/src/html/includes/html.inc +3 -0
  138. data/template/src/html/includes/namespace.inc +19 -0
  139. data/template/src/html/includes/page.inc +151 -0
  140. data/template/src/html/index.html +35 -0
  141. data/template/src/html/js/cmn/bootstrap.js +74 -0
  142. data/template/src/html/js/cmn/global.js +142 -0
  143. data/template/src/html/js/cmn/lib/LAB.js +348 -0
  144. data/template/src/html/min/.htaccess +4 -0
  145. data/template/src/html/min/MinifyCLI.php +19 -0
  146. data/template/src/html/min/README.txt +132 -0
  147. data/template/src/html/min/builder/_index.js +242 -0
  148. data/template/src/html/min/builder/bm.js +36 -0
  149. data/template/src/html/min/builder/index.php +182 -0
  150. data/template/src/html/min/builder/ocCheck.php +36 -0
  151. data/template/src/html/min/builder/rewriteTest.js +1 -0
  152. data/template/src/html/min/config.php +187 -0
  153. data/template/src/html/min/groupsConfig.php +34 -0
  154. data/template/src/html/min/index.php +66 -0
  155. data/template/src/html/min/lib/FirePHP.php +1370 -0
  156. data/template/src/html/min/lib/HTTP/ConditionalGet.php +348 -0
  157. data/template/src/html/min/lib/HTTP/Encoder.php +326 -0
  158. data/template/src/html/min/lib/JSMin.php +314 -0
  159. data/template/src/html/min/lib/JSMinPlus.php +1872 -0
  160. data/template/src/html/min/lib/Minify.php +532 -0
  161. data/template/src/html/min/lib/Minify/Build.php +103 -0
  162. data/template/src/html/min/lib/Minify/CSS.php +83 -0
  163. data/template/src/html/min/lib/Minify/CSS/Compressor.php +250 -0
  164. data/template/src/html/min/lib/Minify/CSS/UriRewriter.php +270 -0
  165. data/template/src/html/min/lib/Minify/Cache/APC.php +130 -0
  166. data/template/src/html/min/lib/Minify/Cache/File.php +125 -0
  167. data/template/src/html/min/lib/Minify/Cache/Memcache.php +137 -0
  168. data/template/src/html/min/lib/Minify/ClosureCompiler.php +85 -0
  169. data/template/src/html/min/lib/Minify/CommentPreserver.php +90 -0
  170. data/template/src/html/min/lib/Minify/Controller/Base.php +202 -0
  171. data/template/src/html/min/lib/Minify/Controller/Files.php +78 -0
  172. data/template/src/html/min/lib/Minify/Controller/Groups.php +94 -0
  173. data/template/src/html/min/lib/Minify/Controller/MinApp.php +132 -0
  174. data/template/src/html/min/lib/Minify/Controller/Page.php +82 -0
  175. data/template/src/html/min/lib/Minify/Controller/Version1.php +118 -0
  176. data/template/src/html/min/lib/Minify/HTML.php +245 -0
  177. data/template/src/html/min/lib/Minify/ImportProcessor.php +157 -0
  178. data/template/src/html/min/lib/Minify/Lines.php +131 -0
  179. data/template/src/html/min/lib/Minify/Logger.php +45 -0
  180. data/template/src/html/min/lib/Minify/Packer.php +37 -0
  181. data/template/src/html/min/lib/Minify/Source.php +187 -0
  182. data/template/src/html/min/lib/Minify/YUICompressor.php +139 -0
  183. data/template/src/html/min/lib/Solar/Dir.php +199 -0
  184. data/template/src/html/min/lib/closure-compiler.jar +0 -0
  185. data/template/src/html/min/lib/yuicompressor-2.4.2.jar +0 -0
  186. data/template/src/html/min/utils.php +90 -0
  187. data/template/src/templates/css/template.css +7 -0
  188. data/template/src/templates/js/template.js +72 -0
  189. data/template/src/templates/template.html +18 -0
  190. data/template/src/yaml/config.yml +46 -0
  191. data/template/src/yaml/deploy.yml +35 -0
  192. data/test/bixbite_test.rb +7 -0
  193. data/test/test_helper.rb +10 -0
  194. metadata +278 -0
@@ -0,0 +1,1060 @@
1
+ ###############################################################################
2
+ #
3
+ # Package: NaturalDocs::Parser::Native
4
+ #
5
+ ###############################################################################
6
+ #
7
+ # A package that converts comments from Natural Docs' native format into <NaturalDocs::Parser::ParsedTopic> objects.
8
+ # Unlike most second-level packages, these are packages and not object classes.
9
+ #
10
+ ###############################################################################
11
+
12
+ # This file is part of Natural Docs, which is Copyright (C) 2003-2008 Greg Valure
13
+ # Natural Docs is licensed under the GPL
14
+
15
+
16
+ use strict;
17
+ use integer;
18
+
19
+ package NaturalDocs::Parser::Native;
20
+
21
+
22
+ ###############################################################################
23
+ # Group: Variables
24
+
25
+
26
+ # Return values of TagType(). Not documented here.
27
+ use constant POSSIBLE_OPENING_TAG => 1;
28
+ use constant POSSIBLE_CLOSING_TAG => 2;
29
+ use constant NOT_A_TAG => 3;
30
+
31
+
32
+ #
33
+ # var: package
34
+ #
35
+ # A <SymbolString> representing the package normal topics will be a part of at the current point in the file. This is a package variable
36
+ # because it needs to be reserved between function calls.
37
+ #
38
+ my $package;
39
+
40
+ #
41
+ # hash: functionListIgnoredHeadings
42
+ #
43
+ # An existence hash of all the headings that prevent the parser from creating function list symbols. Whenever one of
44
+ # these headings are used in a function list topic, symbols are not created from definition lists until the next heading. The keys
45
+ # are in all lowercase.
46
+ #
47
+ my %functionListIgnoredHeadings = ( 'parameters' => 1,
48
+ 'parameter' => 1,
49
+ 'params' => 1,
50
+ 'param' => 1,
51
+ 'arguments' => 1,
52
+ 'argument' => 1,
53
+ 'args' => 1,
54
+ 'arg' => 1 );
55
+
56
+
57
+ ###############################################################################
58
+ # Group: Interface Functions
59
+
60
+
61
+ #
62
+ # Function: Start
63
+ #
64
+ # This will be called whenever a file is about to be parsed. It allows the package to reset its internal state.
65
+ #
66
+ sub Start
67
+ {
68
+ my ($self) = @_;
69
+ $package = undef;
70
+ };
71
+
72
+
73
+ #
74
+ # Function: IsMine
75
+ #
76
+ # Examines the comment and returns whether it is *definitely* Natural Docs content, i.e. it is owned by this package. Note
77
+ # that a comment can fail this function and still be interpreted as a Natural Docs content, for example a JavaDoc-styled comment
78
+ # that doesn't have header lines but no JavaDoc tags either.
79
+ #
80
+ # Parameters:
81
+ #
82
+ # commentLines - An arrayref of the comment lines. Must have been run through <NaturalDocs::Parser->CleanComment()>.
83
+ # isJavaDoc - Whether the comment was JavaDoc-styled.
84
+ #
85
+ # Returns:
86
+ #
87
+ # Whether the comment is *definitely* Natural Docs content.
88
+ #
89
+ sub IsMine #(string[] commentLines, bool isJavaDoc)
90
+ {
91
+ my ($self, $commentLines, $isJavaDoc) = @_;
92
+
93
+ # Skip to the first line with content.
94
+ my $line = 0;
95
+
96
+ while ($line < scalar @$commentLines && !length $commentLines->[$line])
97
+ { $line++; };
98
+
99
+ return $self->ParseHeaderLine($commentLines->[$line]);
100
+ };
101
+
102
+
103
+
104
+ #
105
+ # Function: ParseComment
106
+ #
107
+ # This will be called whenever a comment capable of containing Natural Docs content is found.
108
+ #
109
+ # Parameters:
110
+ #
111
+ # commentLines - An arrayref of the comment lines. Must have been run through <NaturalDocs::Parser->CleanComment()>.
112
+ # *The original memory will be changed.*
113
+ # isJavaDoc - Whether the comment is JavaDoc styled.
114
+ # lineNumber - The line number of the first of the comment lines.
115
+ # parsedTopics - A reference to the array where any new <NaturalDocs::Parser::ParsedTopics> should be placed.
116
+ #
117
+ # Returns:
118
+ #
119
+ # The number of parsed topics added to the array, or zero if none.
120
+ #
121
+ sub ParseComment #(commentLines, isJavaDoc, lineNumber, parsedTopics)
122
+ {
123
+ my ($self, $commentLines, $isJavaDoc, $lineNumber, $parsedTopics) = @_;
124
+
125
+ my $topicCount = 0;
126
+ my $prevLineBlank = 1;
127
+ my $inCodeSection = 0;
128
+
129
+ my ($type, $scope, $isPlural, $title, $symbol);
130
+ #my $package; # package variable.
131
+ my ($newKeyword, $newTitle);
132
+
133
+ my $index = 0;
134
+
135
+ my $bodyStart = 0;
136
+ my $bodyEnd = 0; # Not inclusive.
137
+
138
+ while ($index < scalar @$commentLines)
139
+ {
140
+ # Everything but leading whitespace was removed beforehand.
141
+
142
+ # If we're in a code section...
143
+ if ($inCodeSection)
144
+ {
145
+ if ($commentLines->[$index] =~ /^ *\( *(?:end|finish|done)(?: +(?:table|code|example|diagram))? *\)$/i)
146
+ { $inCodeSection = undef; };
147
+
148
+ $prevLineBlank = 0;
149
+ $bodyEnd++;
150
+ }
151
+
152
+ # If the line is empty...
153
+ elsif (!length($commentLines->[$index]))
154
+ {
155
+ $prevLineBlank = 1;
156
+
157
+ if ($topicCount)
158
+ { $bodyEnd++; };
159
+ }
160
+
161
+ # If the line has a recognized header and the previous line is blank...
162
+ elsif ($prevLineBlank && (($newKeyword, $newTitle) = $self->ParseHeaderLine($commentLines->[$index])) )
163
+ {
164
+ # Process the previous one, if any.
165
+
166
+ if ($topicCount)
167
+ {
168
+ if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
169
+ { $package = undef; };
170
+
171
+ my $body = $self->FormatBody($commentLines, $bodyStart, $bodyEnd, $type, $isPlural);
172
+ my $newTopic = $self->MakeParsedTopic($type, $title, $package, $body, $lineNumber + $bodyStart - 1, $isPlural);
173
+ push @$parsedTopics, $newTopic;
174
+
175
+ $package = $newTopic->Package();
176
+ };
177
+
178
+ $title = $newTitle;
179
+
180
+ my $typeInfo;
181
+ ($type, $typeInfo, $isPlural) = NaturalDocs::Topics->KeywordInfo($newKeyword);
182
+ $scope = $typeInfo->Scope();
183
+
184
+ $bodyStart = $index + 1;
185
+ $bodyEnd = $index + 1;
186
+
187
+ $topicCount++;
188
+
189
+ $prevLineBlank = 0;
190
+ }
191
+
192
+ # If we're on a non-empty, non-header line of a JavaDoc-styled comment and we haven't started a topic yet...
193
+ elsif ($isJavaDoc && !$topicCount)
194
+ {
195
+ $type = undef;
196
+ $scope = ::SCOPE_NORMAL(); # The scope repair and topic merging processes will handle if this is a class topic.
197
+ $isPlural = undef;
198
+ $title = undef;
199
+ $symbol = undef;
200
+
201
+ $bodyStart = $index;
202
+ $bodyEnd = $index + 1;
203
+
204
+ $topicCount++;
205
+
206
+ $prevLineBlank = undef;
207
+ }
208
+
209
+ # If we're on a normal content line within a topic
210
+ elsif ($topicCount)
211
+ {
212
+ $prevLineBlank = 0;
213
+ $bodyEnd++;
214
+
215
+ if ($commentLines->[$index] =~ /^ *\( *(?:(?:start|begin)? +)?(?:table|code|example|diagram) *\)$/i)
216
+ { $inCodeSection = 1; };
217
+ };
218
+
219
+
220
+ $index++;
221
+ };
222
+
223
+
224
+ # Last one, if any. This is the only one that gets the prototypes.
225
+ if ($bodyStart)
226
+ {
227
+ if ($scope == ::SCOPE_START() || $scope == ::SCOPE_END())
228
+ { $package = undef; };
229
+
230
+ my $body = $self->FormatBody($commentLines, $bodyStart, $bodyEnd, $type, $isPlural);
231
+ my $newTopic = $self->MakeParsedTopic($type, $title, $package, $body, $lineNumber + $bodyStart - 1, $isPlural);
232
+ push @$parsedTopics, $newTopic;
233
+ $topicCount++;
234
+
235
+ $package = $newTopic->Package();
236
+ };
237
+
238
+ return $topicCount;
239
+ };
240
+
241
+
242
+ #
243
+ # Function: ParseHeaderLine
244
+ #
245
+ # If the passed line is a topic header, returns the array ( keyword, title ). Otherwise returns an empty array.
246
+ #
247
+ sub ParseHeaderLine #(line)
248
+ {
249
+ my ($self, $line) = @_;
250
+
251
+ if ($line =~ /^ *([a-z0-9 ]*[a-z0-9]): +(.*)$/i)
252
+ {
253
+ my ($keyword, $title) = ($1, $2);
254
+
255
+ # We need to do it this way because if you do "if (ND:T->KeywordInfo($keyword)" and the last element of the array it
256
+ # returns is false, the statement is false. That is really retarded, but there it is.
257
+ my ($type, undef, undef) = NaturalDocs::Topics->KeywordInfo($keyword);
258
+
259
+ if ($type)
260
+ { return ($keyword, $title); }
261
+ else
262
+ { return ( ); };
263
+ }
264
+ else
265
+ { return ( ); };
266
+ };
267
+
268
+
269
+
270
+ ###############################################################################
271
+ # Group: Support Functions
272
+
273
+
274
+ #
275
+ # Function: MakeParsedTopic
276
+ #
277
+ # Creates a <NaturalDocs::Parser::ParsedTopic> object for the passed parameters. Scope is gotten from
278
+ # the package variable <package> instead of from the parameters. The summary is generated from the body.
279
+ #
280
+ # Parameters:
281
+ #
282
+ # type - The <TopicType>. May be undef for headerless topics.
283
+ # title - The title of the topic. May be undef for headerless topics.
284
+ # package - The package <SymbolString> the topic appears in.
285
+ # body - The topic's body in <NDMarkup>.
286
+ # lineNumber - The topic's line number.
287
+ # isList - Whether the topic is a list.
288
+ #
289
+ # Returns:
290
+ #
291
+ # The <NaturalDocs::Parser::ParsedTopic> object.
292
+ #
293
+ sub MakeParsedTopic #(type, title, package, body, lineNumber, isList)
294
+ {
295
+ my ($self, $type, $title, $package, $body, $lineNumber, $isList) = @_;
296
+
297
+ my $summary;
298
+
299
+ if (defined $body)
300
+ { $summary = NaturalDocs::Parser->GetSummaryFromBody($body); };
301
+
302
+ return NaturalDocs::Parser::ParsedTopic->New($type, $title, $package, undef, undef, $summary,
303
+ $body, $lineNumber, $isList);
304
+ };
305
+
306
+
307
+ #
308
+ # Function: FormatBody
309
+ #
310
+ # Converts the section body to <NDMarkup>.
311
+ #
312
+ # Parameters:
313
+ #
314
+ # commentLines - The arrayref of comment lines.
315
+ # startingIndex - The starting index of the body to format.
316
+ # endingIndex - The ending index of the body to format, *not* inclusive.
317
+ # type - The type of the section. May be undef for headerless comments.
318
+ # isList - Whether it's a list topic.
319
+ #
320
+ # Returns:
321
+ #
322
+ # The body formatted in <NDMarkup>.
323
+ #
324
+ sub FormatBody #(commentLines, startingIndex, endingIndex, type, isList)
325
+ {
326
+ my ($self, $commentLines, $startingIndex, $endingIndex, $type, $isList) = @_;
327
+
328
+ use constant TAG_NONE => 1;
329
+ use constant TAG_PARAGRAPH => 2;
330
+ use constant TAG_BULLETLIST => 3;
331
+ use constant TAG_DESCRIPTIONLIST => 4;
332
+ use constant TAG_HEADING => 5;
333
+ use constant TAG_PREFIXCODE => 6;
334
+ use constant TAG_TAGCODE => 7;
335
+
336
+ my %tagEnders = ( TAG_NONE() => '',
337
+ TAG_PARAGRAPH() => '</p>',
338
+ TAG_BULLETLIST() => '</li></ul>',
339
+ TAG_DESCRIPTIONLIST() => '</dd></dl>',
340
+ TAG_HEADING() => '</h>',
341
+ TAG_PREFIXCODE() => '</code>',
342
+ TAG_TAGCODE() => '</code>' );
343
+
344
+ my $topLevelTag = TAG_NONE;
345
+
346
+ my $output;
347
+ my $textBlock;
348
+ my $prevLineBlank = 1;
349
+
350
+ my $codeBlock;
351
+ my $removedCodeSpaces;
352
+
353
+ my $ignoreListSymbols;
354
+
355
+ my $index = $startingIndex;
356
+
357
+ while ($index < $endingIndex)
358
+ {
359
+ # If we're in a tagged code section...
360
+ if ($topLevelTag == TAG_TAGCODE)
361
+ {
362
+ if ($commentLines->[$index] =~ /^ *\( *(?:end|finish|done)(?: +(?:table|code|example|diagram))? *\)$/i)
363
+ {
364
+ $codeBlock =~ s/\n+$//;
365
+ $output .= NaturalDocs::NDMarkup->ConvertAmpChars($codeBlock) . '</code>';
366
+ $codeBlock = undef;
367
+ $topLevelTag = TAG_NONE;
368
+ $prevLineBlank = undef;
369
+ }
370
+ else
371
+ {
372
+ $self->AddToCodeBlock($commentLines->[$index], \$codeBlock, \$removedCodeSpaces);
373
+ };
374
+ }
375
+
376
+ # If the line starts with a code designator...
377
+ elsif ($commentLines->[$index] =~ /^ *[>:|](.*)$/)
378
+ {
379
+ my $code = $1;
380
+
381
+ if ($topLevelTag == TAG_PREFIXCODE)
382
+ {
383
+ $self->AddToCodeBlock($code, \$codeBlock, \$removedCodeSpaces);
384
+ }
385
+ else # $topLevelTag != TAG_PREFIXCODE
386
+ {
387
+ if (defined $textBlock)
388
+ {
389
+ $output .= $self->RichFormatTextBlock($textBlock) . $tagEnders{$topLevelTag};
390
+ $textBlock = undef;
391
+ };
392
+
393
+ $topLevelTag = TAG_PREFIXCODE;
394
+ $output .= '<code>';
395
+ $self->AddToCodeBlock($code, \$codeBlock, \$removedCodeSpaces);
396
+ };
397
+ }
398
+
399
+ # If we're not in either code style...
400
+ else
401
+ {
402
+ # Strip any leading whitespace.
403
+ $commentLines->[$index] =~ s/^ +//;
404
+
405
+ # If we were in a prefixed code section...
406
+ if ($topLevelTag == TAG_PREFIXCODE)
407
+ {
408
+ $codeBlock =~ s/\n+$//;
409
+ $output .= NaturalDocs::NDMarkup->ConvertAmpChars($codeBlock) . '</code>';
410
+ $codeBlock = undef;
411
+ $topLevelTag = TAG_NONE;
412
+ $prevLineBlank = undef;
413
+ };
414
+
415
+
416
+ # If the line is blank...
417
+ if (!length($commentLines->[$index]))
418
+ {
419
+ # End a paragraph. Everything else ignores it for now.
420
+ if ($topLevelTag == TAG_PARAGRAPH)
421
+ {
422
+ $output .= $self->RichFormatTextBlock($textBlock) . '</p>';
423
+ $textBlock = undef;
424
+ $topLevelTag = TAG_NONE;
425
+ };
426
+
427
+ $prevLineBlank = 1;
428
+ }
429
+
430
+ # If the line starts with a bullet...
431
+ elsif ($commentLines->[$index] =~ /^[-\*o+] +([^ ].*)$/ &&
432
+ substr($1, 0, 2) ne '- ') # Make sure "o - Something" is a definition, not a bullet.
433
+ {
434
+ my $bulletedText = $1;
435
+
436
+ if (defined $textBlock)
437
+ { $output .= $self->RichFormatTextBlock($textBlock); };
438
+
439
+ if ($topLevelTag == TAG_BULLETLIST)
440
+ {
441
+ $output .= '</li><li>';
442
+ }
443
+ else #($topLevelTag != TAG_BULLETLIST)
444
+ {
445
+ $output .= $tagEnders{$topLevelTag} . '<ul><li>';
446
+ $topLevelTag = TAG_BULLETLIST;
447
+ };
448
+
449
+ $textBlock = $bulletedText;
450
+
451
+ $prevLineBlank = undef;
452
+ }
453
+
454
+ # If the line looks like a description list entry...
455
+ elsif ($commentLines->[$index] =~ /^(.+?) +- +([^ ].*)$/ && $topLevelTag != TAG_PARAGRAPH)
456
+ {
457
+ my $entry = $1;
458
+ my $description = $2;
459
+
460
+ if (defined $textBlock)
461
+ { $output .= $self->RichFormatTextBlock($textBlock); };
462
+
463
+ if ($topLevelTag == TAG_DESCRIPTIONLIST)
464
+ {
465
+ $output .= '</dd>';
466
+ }
467
+ else #($topLevelTag != TAG_DESCRIPTIONLIST)
468
+ {
469
+ $output .= $tagEnders{$topLevelTag} . '<dl>';
470
+ $topLevelTag = TAG_DESCRIPTIONLIST;
471
+ };
472
+
473
+ if (($isList && !$ignoreListSymbols) || $type eq ::TOPIC_ENUMERATION())
474
+ {
475
+ $output .= '<ds>' . NaturalDocs::NDMarkup->ConvertAmpChars($entry) . '</ds><dd>';
476
+ }
477
+ else
478
+ {
479
+ $output .= '<de>' . NaturalDocs::NDMarkup->ConvertAmpChars($entry) . '</de><dd>';
480
+ };
481
+
482
+ $textBlock = $description;
483
+
484
+ $prevLineBlank = undef;
485
+ }
486
+
487
+ # If the line could be a header...
488
+ elsif ($prevLineBlank && $commentLines->[$index] =~ /^(.*)([^ ]):$/)
489
+ {
490
+ my $headerText = $1 . $2;
491
+
492
+ if (defined $textBlock)
493
+ {
494
+ $output .= $self->RichFormatTextBlock($textBlock);
495
+ $textBlock = undef;
496
+ }
497
+
498
+ $output .= $tagEnders{$topLevelTag};
499
+ $topLevelTag = TAG_NONE;
500
+
501
+ $output .= '<h>' . $self->RichFormatTextBlock($headerText) . '</h>';
502
+
503
+ if ($type eq ::TOPIC_FUNCTION() && $isList)
504
+ {
505
+ $ignoreListSymbols = exists $functionListIgnoredHeadings{lc($headerText)};
506
+ };
507
+
508
+ $prevLineBlank = undef;
509
+ }
510
+
511
+ # If the line looks like a code tag...
512
+ elsif ($commentLines->[$index] =~ /^\( *(?:(?:start|begin)? +)?(?:table|code|example|diagram) *\)$/i)
513
+ {
514
+ if (defined $textBlock)
515
+ {
516
+ $output .= $self->RichFormatTextBlock($textBlock);
517
+ $textBlock = undef;
518
+ };
519
+
520
+ $output .= $tagEnders{$topLevelTag} . '<code>';
521
+ $topLevelTag = TAG_TAGCODE;
522
+ }
523
+
524
+ # If the line looks like an inline image...
525
+ elsif ($commentLines->[$index] =~ /^(\( *see +)([^\)]+?)( *\))$/i)
526
+ {
527
+ if (defined $textBlock)
528
+ {
529
+ $output .= $self->RichFormatTextBlock($textBlock);
530
+ $textBlock = undef;
531
+ };
532
+
533
+ $output .= $tagEnders{$topLevelTag};
534
+ $topLevelTag = TAG_NONE;
535
+
536
+ $output .= '<img mode="inline" target="' . NaturalDocs::NDMarkup->ConvertAmpChars($2) . '" '
537
+ . 'original="' . NaturalDocs::NDMarkup->ConvertAmpChars($1 . $2 . $3) . '">';
538
+
539
+ $prevLineBlank = undef;
540
+ }
541
+
542
+ # If the line isn't any of those, we consider it normal text.
543
+ else
544
+ {
545
+ # A blank line followed by normal text ends lists. We don't handle this when we detect if the line's blank because
546
+ # we don't want blank lines between list items to break the list.
547
+ if ($prevLineBlank && ($topLevelTag == TAG_BULLETLIST || $topLevelTag == TAG_DESCRIPTIONLIST))
548
+ {
549
+ $output .= $self->RichFormatTextBlock($textBlock) . $tagEnders{$topLevelTag} . '<p>';
550
+
551
+ $topLevelTag = TAG_PARAGRAPH;
552
+ $textBlock = undef;
553
+ }
554
+
555
+ elsif ($topLevelTag == TAG_NONE)
556
+ {
557
+ $output .= '<p>';
558
+ $topLevelTag = TAG_PARAGRAPH;
559
+ # textBlock will already be undef.
560
+ };
561
+
562
+ if (defined $textBlock)
563
+ { $textBlock .= ' '; };
564
+
565
+ $textBlock .= $commentLines->[$index];
566
+
567
+ $prevLineBlank = undef;
568
+ };
569
+ };
570
+
571
+ $index++;
572
+ };
573
+
574
+ # Clean up anything left dangling.
575
+ if (defined $textBlock)
576
+ {
577
+ $output .= $self->RichFormatTextBlock($textBlock) . $tagEnders{$topLevelTag};
578
+ }
579
+ elsif (defined $codeBlock)
580
+ {
581
+ $codeBlock =~ s/\n+$//;
582
+ $output .= NaturalDocs::NDMarkup->ConvertAmpChars($codeBlock) . '</code>';
583
+ };
584
+
585
+ return $output;
586
+ };
587
+
588
+
589
+ #
590
+ # Function: AddToCodeBlock
591
+ #
592
+ # Adds a line of text to a code block, handling all the indentation processing required.
593
+ #
594
+ # Parameters:
595
+ #
596
+ # line - The line of text to add.
597
+ # codeBlockRef - A reference to the code block to add it to.
598
+ # removedSpacesRef - A reference to a variable to hold the number of spaces removed. It needs to be stored between calls.
599
+ # It will reset itself automatically when the code block codeBlockRef points to is undef.
600
+ #
601
+ sub AddToCodeBlock #(line, codeBlockRef, removedSpacesRef)
602
+ {
603
+ my ($self, $line, $codeBlockRef, $removedSpacesRef) = @_;
604
+
605
+ $line =~ /^( *)(.*)$/;
606
+ my ($spaces, $code) = ($1, $2);
607
+
608
+ if (!defined $$codeBlockRef)
609
+ {
610
+ if (length($code))
611
+ {
612
+ $$codeBlockRef = $code . "\n";
613
+ $$removedSpacesRef = length($spaces);
614
+ };
615
+ # else ignore leading line breaks.
616
+ }
617
+
618
+ elsif (length $code)
619
+ {
620
+ # Make sure we have the minimum amount of spaces to the left possible.
621
+ if (length($spaces) != $$removedSpacesRef)
622
+ {
623
+ my $spaceDifference = abs( length($spaces) - $$removedSpacesRef );
624
+ my $spacesToAdd = ' ' x $spaceDifference;
625
+
626
+ if (length($spaces) > $$removedSpacesRef)
627
+ {
628
+ $$codeBlockRef .= $spacesToAdd;
629
+ }
630
+ else
631
+ {
632
+ $$codeBlockRef =~ s/^(.)/$spacesToAdd . $1/gme;
633
+ $$removedSpacesRef = length($spaces);
634
+ };
635
+ };
636
+
637
+ $$codeBlockRef .= $code . "\n";
638
+ }
639
+
640
+ else # (!length $code)
641
+ {
642
+ $$codeBlockRef .= "\n";
643
+ };
644
+ };
645
+
646
+
647
+ #
648
+ # Function: RichFormatTextBlock
649
+ #
650
+ # Applies rich <NDMarkup> formatting to a chunk of text. This includes both amp chars, formatting tags, and link tags.
651
+ #
652
+ # Parameters:
653
+ #
654
+ # text - The block of text to format.
655
+ #
656
+ # Returns:
657
+ #
658
+ # The formatted text block.
659
+ #
660
+ sub RichFormatTextBlock #(text)
661
+ {
662
+ my ($self, $text) = @_;
663
+ my $output;
664
+
665
+
666
+ # First find bare urls, e-mail addresses, and images. We have to do this before the split because they may contain underscores
667
+ # or asterisks. We have to mark the tags with \x1E and \x1F so they don't get confused with angle brackets from the comment.
668
+ # We can't convert the amp chars beforehand because we need lookbehinds in the regexps below and they need to be
669
+ # constant length. Sucks, huh?
670
+
671
+ $text =~ s{
672
+ # The previous character can't be an alphanumeric or an opening angle bracket.
673
+ (?<! [a-z0-9<] )
674
+
675
+ # Optional mailto:. Ignored in output.
676
+ (?:mailto\:)?
677
+
678
+ # Begin capture
679
+ (
680
+
681
+ # The user portion. Alphanumeric and - _. Dots can appear between, but not at the edges or more than
682
+ # one in a row.
683
+ (?: [a-z0-9\-_]+ \. )* [a-z0-9\-_]+
684
+
685
+ @
686
+
687
+ # The domain. Alphanumeric and -. Dots same as above, however, there must be at least two sections
688
+ # and the last one must be two to four alphanumeric characters (.com, .uk, .info, .203 for IP addresses)
689
+ (?: [a-z0-9\-]+ \. )+ [a-z]{2,4}
690
+
691
+ # End capture.
692
+ )
693
+
694
+ # The next character can't be an alphanumeric, which should prevent .abcde from matching the two to
695
+ # four character requirement, or a closing angle bracket.
696
+ (?! [a-z0-9>] )
697
+
698
+ }
699
+
700
+ {"\x1E" . 'email target="' . NaturalDocs::NDMarkup->ConvertAmpChars($1) . '" '
701
+ . 'name="' . NaturalDocs::NDMarkup->ConvertAmpChars($1) . '"' . "\x1F"}igxe;
702
+
703
+ $text =~ s{
704
+ # The previous character can't be an alphanumeric or an opening angle bracket.
705
+ (?<! [a-z0-9<] )
706
+
707
+ # Begin capture.
708
+ (
709
+
710
+ # URL must start with one of the acceptable protocols.
711
+ (?:http|https|ftp|news|file)\:
712
+
713
+ # The acceptable URL characters as far as I know.
714
+ [a-z0-9\-\=\~\@\#\%\&\_\+\/\;\:\?\*\.\,]*
715
+
716
+ # The URL characters minus period and comma. If it ends on them, they're probably intended as
717
+ # punctuation.
718
+ [a-z0-9\-\=\~\@\#\%\&\_\+\/\;\:\?\*]
719
+
720
+ # End capture.
721
+ )
722
+
723
+ # The next character must not be an acceptable character or a closing angle bracket. This will prevent the URL
724
+ # from ending early just to get a match.
725
+ (?! [a-z0-9\-\=\~\@\#\%\&\_\+\/\;\:\?\*\>] )
726
+
727
+ }
728
+
729
+ {"\x1E" . 'url target="' . NaturalDocs::NDMarkup->ConvertAmpChars($1) . '" '
730
+ . 'name="' . NaturalDocs::NDMarkup->ConvertAmpChars($1) . '"' . "\x1F"}igxe;
731
+
732
+
733
+ # Find image links. Inline images should already be pulled out by now.
734
+
735
+ $text =~ s{(\( *see +)([^\)]+?)( *\))}
736
+ {"\x1E" . 'img mode="link" target="' . NaturalDocs::NDMarkup->ConvertAmpChars($2) . '" '
737
+ . 'original="' . NaturalDocs::NDMarkup->ConvertAmpChars($1 . $2 . $3) . '"' . "\x1F"}gie;
738
+
739
+
740
+
741
+ # Split the text from the potential tags.
742
+
743
+ my @tempTextBlocks = split(/([\*_<>\x1E\x1F])/, $text);
744
+
745
+ # Since the symbols are considered dividers, empty strings could appear between two in a row or at the beginning/end of the
746
+ # array. This could seriously screw up TagType(), so we need to get rid of them.
747
+ my @textBlocks;
748
+
749
+ while (scalar @tempTextBlocks)
750
+ {
751
+ my $tempTextBlock = shift @tempTextBlocks;
752
+
753
+ if (length $tempTextBlock)
754
+ { push @textBlocks, $tempTextBlock; };
755
+ };
756
+
757
+
758
+ my $bold;
759
+ my $underline;
760
+ my $underlineHasWhitespace;
761
+
762
+ my $index = 0;
763
+
764
+ while ($index < scalar @textBlocks)
765
+ {
766
+ if ($textBlocks[$index] eq "\x1E")
767
+ {
768
+ $output .= '<';
769
+ $index++;
770
+
771
+ while ($textBlocks[$index] ne "\x1F")
772
+ {
773
+ $output .= $textBlocks[$index];
774
+ $index++;
775
+ };
776
+
777
+ $output .= '>';
778
+ }
779
+
780
+ elsif ($textBlocks[$index] eq '<' && $self->TagType(\@textBlocks, $index) == POSSIBLE_OPENING_TAG)
781
+ {
782
+ my $endingIndex = $self->ClosingTag(\@textBlocks, $index, undef);
783
+
784
+ if ($endingIndex != -1)
785
+ {
786
+ my $linkText;
787
+ $index++;
788
+
789
+ while ($index < $endingIndex)
790
+ {
791
+ $linkText .= $textBlocks[$index];
792
+ $index++;
793
+ };
794
+ # Index will be incremented again at the end of the loop.
795
+
796
+ $linkText = NaturalDocs::NDMarkup->ConvertAmpChars($linkText);
797
+
798
+ if ($linkText =~ /^(?:mailto\:)?((?:[a-z0-9\-_]+\.)*[a-z0-9\-_]+@(?:[a-z0-9\-]+\.)+[a-z]{2,4})$/i)
799
+ { $output .= '<email target="' . $1 . '" name="' . $1 . '">'; }
800
+ elsif ($linkText =~ /^(?:http|https|ftp|news|file)\:/i)
801
+ { $output .= '<url target="' . $linkText . '" name="' . $linkText . '">'; }
802
+ else
803
+ { $output .= '<link target="' . $linkText . '" name="' . $linkText . '" original="&lt;' . $linkText . '&gt;">'; };
804
+ }
805
+
806
+ else # it's not a link.
807
+ {
808
+ $output .= '&lt;';
809
+ };
810
+ }
811
+
812
+ elsif ($textBlocks[$index] eq '*')
813
+ {
814
+ my $tagType = $self->TagType(\@textBlocks, $index);
815
+
816
+ if ($tagType == POSSIBLE_OPENING_TAG && $self->ClosingTag(\@textBlocks, $index, undef) != -1)
817
+ {
818
+ # ClosingTag() makes sure tags aren't opened multiple times in a row.
819
+ $bold = 1;
820
+ $output .= '<b>';
821
+ }
822
+ elsif ($bold && $tagType == POSSIBLE_CLOSING_TAG)
823
+ {
824
+ $bold = undef;
825
+ $output .= '</b>';
826
+ }
827
+ else
828
+ {
829
+ $output .= '*';
830
+ };
831
+ }
832
+
833
+ elsif ($textBlocks[$index] eq '_')
834
+ {
835
+ my $tagType = $self->TagType(\@textBlocks, $index);
836
+
837
+ if ($tagType == POSSIBLE_OPENING_TAG && $self->ClosingTag(\@textBlocks, $index, \$underlineHasWhitespace) != -1)
838
+ {
839
+ # ClosingTag() makes sure tags aren't opened multiple times in a row.
840
+ $underline = 1;
841
+ #underlineHasWhitespace is set by ClosingTag().
842
+ $output .= '<u>';
843
+ }
844
+ elsif ($underline && $tagType == POSSIBLE_CLOSING_TAG)
845
+ {
846
+ $underline = undef;
847
+ #underlineHasWhitespace will be reset by the next opening underline.
848
+ $output .= '</u>';
849
+ }
850
+ elsif ($underline && !$underlineHasWhitespace)
851
+ {
852
+ # If there's no whitespace between underline tags, all underscores are replaced by spaces so
853
+ # _some_underlined_text_ becomes <u>some underlined text</u>. The standard _some underlined text_
854
+ # will work too.
855
+ $output .= ' ';
856
+ }
857
+ else
858
+ {
859
+ $output .= '_';
860
+ };
861
+ }
862
+
863
+ else # plain text or a > that isn't part of a link
864
+ {
865
+ $output .= NaturalDocs::NDMarkup->ConvertAmpChars($textBlocks[$index]);
866
+ };
867
+
868
+ $index++;
869
+ };
870
+
871
+ return $output;
872
+ };
873
+
874
+
875
+ #
876
+ # Function: TagType
877
+ #
878
+ # Returns whether the tag is a possible opening or closing tag, or neither. "Possible" because it doesn't check if an opening tag is
879
+ # closed or a closing tag is opened, just whether the surrounding characters allow it to be a candidate for a tag. For example, in
880
+ # "A _B" the underscore is a possible opening underline tag, but in "A_B" it is not. Support function for <RichFormatTextBlock()>.
881
+ #
882
+ # Parameters:
883
+ #
884
+ # textBlocks - A reference to an array of text blocks.
885
+ # index - The index of the tag.
886
+ #
887
+ # Returns:
888
+ #
889
+ # POSSIBLE_OPENING_TAG, POSSIBLE_CLOSING_TAG, or NOT_A_TAG.
890
+ #
891
+ sub TagType #(textBlocks, index)
892
+ {
893
+ my ($self, $textBlocks, $index) = @_;
894
+
895
+
896
+ # Possible opening tags
897
+
898
+ if ( ( $textBlocks->[$index] =~ /^[\*_<]$/ ) &&
899
+
900
+ # Before it must be whitespace, the beginning of the text, or ({["'-/*_.
901
+ ( $index == 0 || $textBlocks->[$index-1] =~ /[\ \t\n\(\{\[\"\'\-\/\*\_]$/ ) &&
902
+
903
+ # Notes for 2.0: Include Spanish upside down ! and ? as well as opening quotes (66) and apostrophes (6). Look into
904
+ # Unicode character classes as well.
905
+
906
+ # After it must be non-whitespace.
907
+ ( $index + 1 < scalar @$textBlocks && $textBlocks->[$index+1] !~ /^[\ \t\n]/) &&
908
+
909
+ # Make sure we don't accept <<, <=, <-, or *= as opening tags.
910
+ ( $textBlocks->[$index] ne '<' || $textBlocks->[$index+1] !~ /^[<=-]/ ) &&
911
+ ( $textBlocks->[$index] ne '*' || $textBlocks->[$index+1] !~ /^[\=\*]/ ) &&
912
+
913
+ # Make sure we don't accept * or _ before it unless it's <.
914
+ ( $textBlocks->[$index] eq '<' || $index == 0 || $textBlocks->[$index-1] !~ /[\*\_]$/) )
915
+ {
916
+ return POSSIBLE_OPENING_TAG;
917
+ }
918
+
919
+
920
+ # Possible closing tags
921
+
922
+ elsif ( ( $textBlocks->[$index] =~ /^[\*_>]$/) &&
923
+
924
+ # After it must be whitespace, the end of the text, or )}].,!?"';:-/*_.
925
+ ( $index + 1 == scalar @$textBlocks || $textBlocks->[$index+1] =~ /^[ \t\n\)\]\}\.\,\!\?\"\'\;\:\-\/\*\_]/ ||
926
+ # Links also get plurals, like <link>s, <linx>es, <link>'s, and <links>'.
927
+ ( $textBlocks->[$index] eq '>' && $textBlocks->[$index+1] =~ /^(?:es|s|\')/ ) ) &&
928
+
929
+ # Notes for 2.0: Include closing quotes (99) and apostrophes (9). Look into Unicode character classes as well.
930
+
931
+ # Before it must be non-whitespace.
932
+ ( $index != 0 && $textBlocks->[$index-1] !~ /[ \t\n]$/ ) &&
933
+
934
+ # Make sure we don't accept >>, ->, or => as closing tags. >= is already taken care of.
935
+ ( $textBlocks->[$index] ne '>' || $textBlocks->[$index-1] !~ /[>=-]$/ ) &&
936
+
937
+ # Make sure we don't accept * or _ after it unless it's >.
938
+ ( $textBlocks->[$index] eq '>' || $textBlocks->[$index+1] !~ /[\*\_]$/) )
939
+ {
940
+ return POSSIBLE_CLOSING_TAG;
941
+ }
942
+
943
+ else
944
+ {
945
+ return NOT_A_TAG;
946
+ };
947
+
948
+ };
949
+
950
+
951
+ #
952
+ # Function: ClosingTag
953
+ #
954
+ # Returns whether a tag is closed or not, where it's closed if it is, and optionally whether there is any whitespace between the
955
+ # tags. Support function for <RichFormatTextBlock()>.
956
+ #
957
+ # The results of this function are in full context, meaning that if it says a tag is closed, it can be interpreted as that tag in the
958
+ # final output. It takes into account any spoiling factors, like there being two opening tags in a row.
959
+ #
960
+ # Parameters:
961
+ #
962
+ # textBlocks - A reference to an array of text blocks.
963
+ # index - The index of the opening tag.
964
+ # hasWhitespaceRef - A reference to the variable that will hold whether there is whitespace between the tags or not. If
965
+ # undef, the function will not check. If the tag is not closed, the variable will not be changed.
966
+ #
967
+ # Returns:
968
+ #
969
+ # If the tag is closed, it returns the index of the closing tag and puts whether there was whitespace between the tags in
970
+ # hasWhitespaceRef if it was specified. If the tag is not closed, it returns -1 and doesn't touch the variable pointed to by
971
+ # hasWhitespaceRef.
972
+ #
973
+ sub ClosingTag #(textBlocks, index, hasWhitespace)
974
+ {
975
+ my ($self, $textBlocks, $index, $hasWhitespaceRef) = @_;
976
+
977
+ my $hasWhitespace;
978
+ my $closingTag;
979
+
980
+ if ($textBlocks->[$index] eq '*' || $textBlocks->[$index] eq '_')
981
+ { $closingTag = $textBlocks->[$index]; }
982
+ elsif ($textBlocks->[$index] eq '<')
983
+ { $closingTag = '>'; }
984
+ else
985
+ { return -1; };
986
+
987
+ my $beginningIndex = $index;
988
+ $index++;
989
+
990
+ while ($index < scalar @$textBlocks)
991
+ {
992
+ if ($textBlocks->[$index] eq '<' && $self->TagType($textBlocks, $index) == POSSIBLE_OPENING_TAG)
993
+ {
994
+ # If we hit a < and we're checking whether a link is closed, it's not. The first < becomes literal and the second one
995
+ # becomes the new link opening.
996
+ if ($closingTag eq '>')
997
+ {
998
+ return -1;
999
+ }
1000
+
1001
+ # If we're not searching for the end of a link, we have to skip the link because formatting tags cannot appear within
1002
+ # them. That's of course provided it's closed.
1003
+ else
1004
+ {
1005
+ my $linkHasWhitespace;
1006
+
1007
+ my $endIndex = $self->ClosingTag($textBlocks, $index,
1008
+ ($hasWhitespaceRef && !$hasWhitespace ? \$linkHasWhitespace : undef) );
1009
+
1010
+ if ($endIndex != -1)
1011
+ {
1012
+ if ($linkHasWhitespace)
1013
+ { $hasWhitespace = 1; };
1014
+
1015
+ # index will be incremented again at the end of the loop, which will bring us past the link's >.
1016
+ $index = $endIndex;
1017
+ };
1018
+ };
1019
+ }
1020
+
1021
+ elsif ($textBlocks->[$index] eq $closingTag)
1022
+ {
1023
+ my $tagType = $self->TagType($textBlocks, $index);
1024
+
1025
+ if ($tagType == POSSIBLE_CLOSING_TAG)
1026
+ {
1027
+ # There needs to be something between the tags for them to count.
1028
+ if ($index == $beginningIndex + 1)
1029
+ { return -1; }
1030
+ else
1031
+ {
1032
+ # Success!
1033
+
1034
+ if ($hasWhitespaceRef)
1035
+ { $$hasWhitespaceRef = $hasWhitespace; };
1036
+
1037
+ return $index;
1038
+ };
1039
+ }
1040
+
1041
+ # If there are two opening tags of the same type, the first becomes literal and the next becomes part of a tag.
1042
+ elsif ($tagType == POSSIBLE_OPENING_TAG)
1043
+ { return -1; }
1044
+ }
1045
+
1046
+ elsif ($hasWhitespaceRef && !$hasWhitespace)
1047
+ {
1048
+ if ($textBlocks->[$index] =~ /[ \t\n]/)
1049
+ { $hasWhitespace = 1; };
1050
+ };
1051
+
1052
+ $index++;
1053
+ };
1054
+
1055
+ # Hit the end of the text blocks if we're here.
1056
+ return -1;
1057
+ };
1058
+
1059
+
1060
+ 1;