rdoc 6.3.4.1 → 8.0.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 (205) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.md +195 -0
  3. data/History.rdoc +65 -63
  4. data/LEGAL.rdoc +7 -1
  5. data/LICENSE.rdoc +6 -0
  6. data/README.md +236 -0
  7. data/RI.md +842 -0
  8. data/TODO.rdoc +8 -7
  9. data/doc/markup_reference/markdown.md +659 -0
  10. data/doc/markup_reference/rdoc.rdoc +1169 -0
  11. data/exe/rdoc +0 -1
  12. data/lib/rdoc/{alias.rb → code_object/alias.rb} +4 -18
  13. data/lib/rdoc/{any_method.rb → code_object/any_method.rb} +50 -24
  14. data/lib/rdoc/{attr.rb → code_object/attr.rb} +16 -14
  15. data/lib/rdoc/{class_module.rb → code_object/class_module.rb} +225 -58
  16. data/lib/rdoc/{constant.rb → code_object/constant.rb} +44 -10
  17. data/lib/rdoc/code_object/context/section.rb +182 -0
  18. data/lib/rdoc/{context.rb → code_object/context.rb} +78 -106
  19. data/lib/rdoc/{extend.rb → code_object/extend.rb} +0 -1
  20. data/lib/rdoc/{include.rb → code_object/include.rb} +0 -1
  21. data/lib/rdoc/{method_attr.rb → code_object/method_attr.rb} +46 -48
  22. data/lib/rdoc/{mixin.rb → code_object/mixin.rb} +6 -4
  23. data/lib/rdoc/{normal_class.rb → code_object/normal_class.rb} +2 -3
  24. data/lib/rdoc/{normal_module.rb → code_object/normal_module.rb} +2 -3
  25. data/lib/rdoc/{require.rb → code_object/require.rb} +1 -2
  26. data/lib/rdoc/{single_class.rb → code_object/single_class.rb} +5 -1
  27. data/lib/rdoc/{top_level.rb → code_object/top_level.rb} +60 -63
  28. data/lib/rdoc/code_object.rb +11 -39
  29. data/lib/rdoc/code_objects.rb +1 -2
  30. data/lib/rdoc/comment.rb +202 -99
  31. data/lib/rdoc/cross_reference.rb +75 -62
  32. data/lib/rdoc/encoding.rb +8 -24
  33. data/lib/rdoc/erb_partial.rb +1 -2
  34. data/lib/rdoc/erbio.rb +3 -8
  35. data/lib/rdoc/generator/aliki.rb +200 -0
  36. data/lib/rdoc/generator/darkfish.rb +156 -122
  37. data/lib/rdoc/generator/json_index.rb +8 -24
  38. data/lib/rdoc/generator/markup.rb +70 -34
  39. data/lib/rdoc/generator/pot/message_extractor.rb +7 -7
  40. data/lib/rdoc/generator/pot/po.rb +1 -1
  41. data/lib/rdoc/generator/pot/po_entry.rb +8 -8
  42. data/lib/rdoc/generator/pot.rb +1 -5
  43. data/lib/rdoc/generator/ri.rb +1 -2
  44. data/lib/rdoc/generator/template/aliki/DESIGN.md +536 -0
  45. data/lib/rdoc/generator/template/aliki/_aside_toc.rhtml +8 -0
  46. data/lib/rdoc/generator/template/aliki/_footer.rhtml +23 -0
  47. data/lib/rdoc/generator/template/aliki/_head.rhtml +163 -0
  48. data/lib/rdoc/generator/template/aliki/_header.rhtml +56 -0
  49. data/lib/rdoc/generator/template/aliki/_icons.rhtml +208 -0
  50. data/lib/rdoc/generator/template/aliki/_sidebar_ancestors.rhtml +16 -0
  51. data/lib/rdoc/generator/template/aliki/_sidebar_classes.rhtml +15 -0
  52. data/lib/rdoc/generator/template/aliki/_sidebar_extends.rhtml +27 -0
  53. data/lib/rdoc/generator/template/aliki/_sidebar_includes.rhtml +27 -0
  54. data/lib/rdoc/generator/template/aliki/_sidebar_installed.rhtml +16 -0
  55. data/lib/rdoc/generator/template/aliki/_sidebar_methods.rhtml +41 -0
  56. data/lib/rdoc/generator/template/aliki/_sidebar_pages.rhtml +67 -0
  57. data/lib/rdoc/generator/template/aliki/_sidebar_search.rhtml +15 -0
  58. data/lib/rdoc/generator/template/aliki/_sidebar_sections.rhtml +21 -0
  59. data/lib/rdoc/generator/template/aliki/_sidebar_toggle.rhtml +3 -0
  60. data/lib/rdoc/generator/template/aliki/class.rhtml +230 -0
  61. data/lib/rdoc/generator/template/aliki/css/rdoc.css +2181 -0
  62. data/lib/rdoc/generator/template/aliki/index.rhtml +22 -0
  63. data/lib/rdoc/generator/template/aliki/js/aliki.js +513 -0
  64. data/lib/rdoc/generator/template/aliki/js/bash_highlighter.js +167 -0
  65. data/lib/rdoc/generator/template/aliki/js/c_highlighter.js +299 -0
  66. data/lib/rdoc/generator/template/aliki/js/search_controller.js +129 -0
  67. data/lib/rdoc/generator/template/aliki/js/search_navigation.js +105 -0
  68. data/lib/rdoc/generator/template/aliki/js/search_ranker.js +239 -0
  69. data/lib/rdoc/generator/template/aliki/js/theme-toggle.js +112 -0
  70. data/lib/rdoc/generator/template/aliki/page.rhtml +18 -0
  71. data/lib/rdoc/generator/template/aliki/servlet_not_found.rhtml +14 -0
  72. data/lib/rdoc/generator/template/aliki/servlet_root.rhtml +65 -0
  73. data/lib/rdoc/generator/template/darkfish/_footer.rhtml +3 -3
  74. data/lib/rdoc/generator/template/darkfish/_head.rhtml +35 -14
  75. data/lib/rdoc/generator/template/darkfish/_sidebar_classes.rhtml +1 -5
  76. data/lib/rdoc/generator/template/darkfish/_sidebar_extends.rhtml +11 -9
  77. data/lib/rdoc/generator/template/darkfish/_sidebar_includes.rhtml +11 -9
  78. data/lib/rdoc/generator/template/darkfish/_sidebar_installed.rhtml +7 -6
  79. data/lib/rdoc/generator/template/darkfish/_sidebar_methods.rhtml +20 -11
  80. data/lib/rdoc/generator/template/darkfish/_sidebar_pages.rhtml +25 -5
  81. data/lib/rdoc/generator/template/darkfish/_sidebar_parent.rhtml +4 -9
  82. data/lib/rdoc/generator/template/darkfish/_sidebar_search.rhtml +2 -1
  83. data/lib/rdoc/generator/template/darkfish/_sidebar_sections.rhtml +4 -4
  84. data/lib/rdoc/generator/template/darkfish/_sidebar_table_of_contents.rhtml +28 -7
  85. data/lib/rdoc/generator/template/darkfish/_sidebar_toggle.rhtml +3 -0
  86. data/lib/rdoc/generator/template/darkfish/class.rhtml +138 -90
  87. data/lib/rdoc/generator/template/darkfish/css/rdoc.css +420 -358
  88. data/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Bold.ttf +0 -0
  89. data/lib/rdoc/generator/template/darkfish/fonts/SourceCodePro-Regular.ttf +0 -0
  90. data/lib/rdoc/generator/template/darkfish/index.rhtml +14 -12
  91. data/lib/rdoc/generator/template/darkfish/js/darkfish.js +59 -3
  92. data/lib/rdoc/generator/template/darkfish/js/search.js +15 -5
  93. data/lib/rdoc/generator/template/darkfish/page.rhtml +7 -6
  94. data/lib/rdoc/generator/template/darkfish/servlet_not_found.rhtml +12 -9
  95. data/lib/rdoc/generator/template/darkfish/servlet_root.rhtml +24 -21
  96. data/lib/rdoc/generator/template/darkfish/table_of_contents.rhtml +35 -21
  97. data/lib/rdoc/generator/template/json_index/js/navigation.js +8 -8
  98. data/lib/rdoc/generator/template/json_index/js/searcher.js +48 -6
  99. data/lib/rdoc/generator.rb +6 -5
  100. data/lib/rdoc/i18n/text.rb +3 -3
  101. data/lib/rdoc/i18n.rb +1 -1
  102. data/lib/rdoc/known_classes.rb +5 -4
  103. data/lib/rdoc/markdown/entities.rb +0 -1
  104. data/lib/rdoc/markdown/literals.kpeg +0 -2
  105. data/lib/rdoc/markdown/literals.rb +0 -1
  106. data/lib/rdoc/markdown.kpeg +140 -62
  107. data/lib/rdoc/markdown.rb +1015 -873
  108. data/lib/rdoc/markup/blank_line.rb +25 -24
  109. data/lib/rdoc/markup/block_quote.rb +1 -2
  110. data/lib/rdoc/markup/document.rb +10 -11
  111. data/lib/rdoc/markup/element.rb +21 -0
  112. data/lib/rdoc/markup/formatter.rb +134 -115
  113. data/lib/rdoc/markup/hard_break.rb +30 -28
  114. data/lib/rdoc/markup/heading.rb +163 -72
  115. data/lib/rdoc/markup/include.rb +3 -4
  116. data/lib/rdoc/markup/indented_paragraph.rb +4 -5
  117. data/lib/rdoc/markup/inline_parser.rb +312 -0
  118. data/lib/rdoc/markup/list.rb +6 -7
  119. data/lib/rdoc/markup/list_item.rb +6 -7
  120. data/lib/rdoc/markup/paragraph.rb +2 -3
  121. data/lib/rdoc/markup/parser.rb +29 -19
  122. data/lib/rdoc/markup/pre_process.rb +39 -18
  123. data/lib/rdoc/markup/raw.rb +52 -56
  124. data/lib/rdoc/markup/rule.rb +2 -3
  125. data/lib/rdoc/markup/table.rb +49 -32
  126. data/lib/rdoc/markup/to_ansi.rb +58 -8
  127. data/lib/rdoc/markup/to_bs.rb +47 -38
  128. data/lib/rdoc/markup/to_html.rb +343 -105
  129. data/lib/rdoc/markup/to_html_crossref.rb +143 -58
  130. data/lib/rdoc/markup/to_html_snippet.rb +78 -76
  131. data/lib/rdoc/markup/to_joined_paragraph.rb +3 -9
  132. data/lib/rdoc/markup/to_label.rb +34 -25
  133. data/lib/rdoc/markup/to_markdown.rb +74 -51
  134. data/lib/rdoc/markup/to_rdoc.rb +141 -82
  135. data/lib/rdoc/markup/to_table_of_contents.rb +4 -5
  136. data/lib/rdoc/markup/to_test.rb +10 -3
  137. data/lib/rdoc/markup/to_tt_only.rb +17 -31
  138. data/lib/rdoc/markup/verbatim.rb +5 -6
  139. data/lib/rdoc/markup.rb +59 -707
  140. data/lib/rdoc/options.rb +228 -101
  141. data/lib/rdoc/parser/c.rb +186 -184
  142. data/lib/rdoc/parser/changelog.rb +57 -13
  143. data/lib/rdoc/parser/markdown.rb +0 -2
  144. data/lib/rdoc/parser/rbs.rb +275 -0
  145. data/lib/rdoc/parser/rd.rb +0 -1
  146. data/lib/rdoc/parser/ruby.rb +963 -2024
  147. data/lib/rdoc/parser/ruby_colorizer.rb +253 -0
  148. data/lib/rdoc/parser/simple.rb +6 -23
  149. data/lib/rdoc/parser/text.rb +0 -1
  150. data/lib/rdoc/parser.rb +35 -14
  151. data/lib/rdoc/rbs_helper.rb +186 -0
  152. data/lib/rdoc/rd/block_parser.rb +659 -11
  153. data/lib/rdoc/rd/block_parser.ry +13 -9
  154. data/lib/rdoc/rd/inline.rb +4 -5
  155. data/lib/rdoc/rd/inline_parser.rb +646 -2
  156. data/lib/rdoc/rd.rb +4 -5
  157. data/lib/rdoc/rdoc.rb +234 -90
  158. data/lib/rdoc/ri/driver.rb +172 -172
  159. data/lib/rdoc/ri/paths.rb +3 -3
  160. data/lib/rdoc/{servlet.rb → ri/servlet.rb} +28 -27
  161. data/lib/rdoc/ri/store.rb +0 -1
  162. data/lib/rdoc/ri/task.rb +3 -3
  163. data/lib/rdoc/ri.rb +5 -5
  164. data/lib/rdoc/rubygems_hook.rb +106 -25
  165. data/lib/rdoc/server.rb +460 -0
  166. data/lib/rdoc/stats/normal.rb +1 -1
  167. data/lib/rdoc/stats/quiet.rb +1 -2
  168. data/lib/rdoc/stats/verbose.rb +3 -5
  169. data/lib/rdoc/stats.rb +158 -136
  170. data/lib/rdoc/store.rb +267 -48
  171. data/lib/rdoc/task.rb +54 -28
  172. data/lib/rdoc/text.rb +60 -120
  173. data/lib/rdoc/token_stream.rb +30 -45
  174. data/lib/rdoc/tom_doc.rb +9 -15
  175. data/lib/rdoc/version.rb +3 -1
  176. data/lib/rdoc.rb +90 -52
  177. data/lib/rubygems_plugin.rb +14 -0
  178. data/man/ri.1 +2 -0
  179. data/rdoc-logo.svg +43 -0
  180. data/rdoc.gemspec +25 -200
  181. metadata +120 -53
  182. data/CONTRIBUTING.rdoc +0 -220
  183. data/ExampleMarkdown.md +0 -37
  184. data/ExampleRDoc.rdoc +0 -208
  185. data/Gemfile +0 -12
  186. data/README.rdoc +0 -129
  187. data/RI.rdoc +0 -57
  188. data/Rakefile +0 -101
  189. data/bin/console +0 -7
  190. data/bin/setup +0 -6
  191. data/lib/rdoc/anon_class.rb +0 -11
  192. data/lib/rdoc/context/section.rb +0 -232
  193. data/lib/rdoc/generator/template/darkfish/.document +0 -0
  194. data/lib/rdoc/generator/template/darkfish/_sidebar_VCS_info.rhtml +0 -19
  195. data/lib/rdoc/generator/template/darkfish/_sidebar_in_files.rhtml +0 -9
  196. data/lib/rdoc/generator/template/json_index/.document +0 -1
  197. data/lib/rdoc/ghost_method.rb +0 -7
  198. data/lib/rdoc/markup/attr_changer.rb +0 -23
  199. data/lib/rdoc/markup/attr_span.rb +0 -36
  200. data/lib/rdoc/markup/attribute_manager.rb +0 -409
  201. data/lib/rdoc/markup/attributes.rb +0 -71
  202. data/lib/rdoc/markup/regexp_handling.rb +0 -41
  203. data/lib/rdoc/meta_method.rb +0 -7
  204. data/lib/rdoc/parser/ripper_state_lex.rb +0 -590
  205. data/lib/rdoc/parser/ruby_tools.rb +0 -167
@@ -0,0 +1,253 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'prism'
4
+ require 'set'
5
+
6
+ # Ruby code syntax highlighter.
7
+ # Colorize result is an array of +RDoc::Parser::RubyColorizer::ColoredToken+
8
+ # Actual color for each token kind is determined elsewhere (e.g., HTML generator)
9
+ module RDoc::Parser::RubyColorizer
10
+
11
+ ColoredToken = Struct.new(:kind, :text)
12
+
13
+ # Prism operator token types except assignment '='
14
+ OP_TOKENS = %i[
15
+ AMPERSAND AMPERSAND_AMPERSAND
16
+ BANG BANG_EQUAL BANG_TILDE CARET COLON COLON_COLON
17
+ EQUAL_EQUAL EQUAL_GREATER EQUAL_TILDE
18
+ GREATER GREATER_GREATER
19
+ LESS LESS_EQUAL LESS_EQUAL_GREATER LESS_LESS
20
+ MINUS MINUS_GREATER PERCENT PIPE PIPE_PIPE PLUS
21
+ QUESTION_MARK SLASH STAR STAR_STAR TILDE
22
+ UAMPERSAND UMINUS UPLUS USTAR USTAR_STAR
23
+ ].to_set
24
+
25
+ # Prism token type to ColoredToken kind map
26
+ TOKEN_TYPE_MAP = {
27
+ IDENTIFIER: :identifier,
28
+ METHOD_NAME: :identifier,
29
+ INSTANCE_VARIABLE: :ivar,
30
+ CLASS_VARIABLE: :identifier,
31
+ GLOBAL_VARIABLE: :identifier,
32
+ BACK_REFERENCE: :identifier,
33
+ NUMBERED_REFERENCE: :identifier,
34
+ CONSTANT: :constant,
35
+ LABEL: :value,
36
+ INTEGER: :value,
37
+ INTEGER_IMAGINARY: :value,
38
+ INTEGER_RATIONAL: :value,
39
+ INTEGER_RATIONAL_IMAGINARY: :value,
40
+ FLOAT: :value,
41
+ FLOAT_IMAGINARY: :value,
42
+ FLOAT_RATIONAL: :value,
43
+ FLOAT_RATIONAL_IMAGINARY: :value,
44
+ COMMENT: :comment,
45
+ EMBDOC_BEGIN: :comment,
46
+ EMBDOC_LINE: :comment,
47
+ EMBDOC_END: :comment
48
+ }
49
+
50
+ class << self
51
+
52
+ # Colorize the entire +code+ and returns colored token stream.
53
+ def colorize(code)
54
+ result = Prism.parse_lex(code)
55
+ program_node, unordered_tokens = result.value
56
+ prism_tokens = unordered_tokens.map(&:first).sort_by! { |token| token.location.start_offset }
57
+ partial_colorize(code, program_node, prism_tokens, 0, code.bytesize)
58
+ end
59
+
60
+ # Colorize partial +node+ in +whole_code+ and returns colored token stream.
61
+ def partial_colorize(whole_code, node, prism_tokens, start_offset = nil, end_offset = nil)
62
+ start_offset ||= node.location.start_offset
63
+ end_offset ||= node.location.end_offset
64
+ visitor = NodeColorizeVisitor.new
65
+ node.accept(visitor)
66
+ prior_tokens = visitor.tokens.sort_by {|_, start_offset, _| start_offset }
67
+ normal_tokens = normal_tokens(slice_by_location(prism_tokens, start_offset, end_offset))
68
+ colored_tokens = unify_tokens(whole_code, prior_tokens, normal_tokens, start_offset, end_offset)
69
+ colored_tokens.unshift(ColoredToken.new(:plain, ' ' * node.location.start_column)) if node.location.start_column > 0
70
+ colored_tokens
71
+ end
72
+
73
+ private
74
+
75
+ def slice_by_location(items, start_offset, end_offset)
76
+ start_index = items.bsearch_index { |item| item.location.end_offset > start_offset } || items.size
77
+ end_index = items.bsearch_index { |item| item.location.start_offset >= end_offset } || items.size
78
+ items[start_index...end_index]
79
+ end
80
+
81
+ # Unify prior tokens and normal tokens into a single token stream.
82
+ # Prior tokens have higher priority than normal tokens.
83
+ # Also adds missing text (spaces, newlines, etc.) as :plain tokens
84
+ # so that the entire range is covered.
85
+ def unify_tokens(whole_code, prior_tokens, normal_tokens, start_offset, end_offset)
86
+ tokens = []
87
+ offset = start_offset
88
+
89
+ # Add missing text such as spaces and newlines as a separate :plain token
90
+ flush = -> next_offset {
91
+ return if offset == next_offset
92
+
93
+ whole_code.byteslice(offset...next_offset).scan(/\n|\s+|[^\s]+/) do |text|
94
+ tokens << ColoredToken.new(:plain, text)
95
+ end
96
+ }
97
+
98
+ until prior_tokens.empty? && normal_tokens.empty?
99
+ ptok = prior_tokens.first
100
+ ntok = normal_tokens.first
101
+ if ntok && (!ptok || ntok[2] <= ptok[1])
102
+ token = normal_tokens.shift
103
+ else
104
+ token = prior_tokens.shift
105
+ end
106
+ kind, start_pos, end_pos = token
107
+ next if start_pos < offset
108
+
109
+ flush.call(start_pos)
110
+ tokens << ColoredToken.new(kind, whole_code.byteslice(start_pos...end_pos))
111
+ offset = end_pos
112
+ end
113
+ flush.call(end_offset)
114
+ tokens
115
+ end
116
+
117
+ # Convert normal Prism tokens to [kind, start_offset, end_offset]
118
+ def normal_tokens(tokens)
119
+ tokens.map do |token,|
120
+ kind =
121
+ if token.type.start_with?('KEYWORD_')
122
+ :keyword
123
+ elsif OP_TOKENS.include?(token.type.to_sym)
124
+ :operator
125
+ else
126
+ TOKEN_TYPE_MAP[token.type] || :plain
127
+ end
128
+ [kind, token.location.start_offset, token.location.end_offset]
129
+ end
130
+ end
131
+ end
132
+
133
+ # Visitor to determine node colorizing which can't be determined by tokens.
134
+ # STRING_CONTENT/EMBEXPR_BEGIN/EMBEXPR_END in string/regexp/symbol have different colorizing
135
+ class NodeColorizeVisitor < Prism::Visitor # :nodoc:
136
+ attr_reader :tokens
137
+
138
+ def initialize
139
+ @tokens = []
140
+ end
141
+
142
+ def visit_symbol_node(node)
143
+ # SymbolNode#location may contain heredoc content and closing
144
+ # e.g., `<<A; :\\\nA\nsymbol`
145
+ # So we need to colorize opening, content and closing separately.
146
+ push_location(:symbol, node.opening_loc)
147
+ push_location(:symbol, node.value_loc)
148
+ push_location(:symbol, node.closing_loc)
149
+ end
150
+
151
+ def visit_interpolated_symbol_node(node)
152
+ push_location(:symbol, node.opening_loc)
153
+ handle_interpolated_parts(:symbol, node.parts)
154
+ push_location(:symbol, node.closing_loc)
155
+ end
156
+
157
+ def visit_regular_expression_node(node)
158
+ push_location(:regexp, node.location)
159
+ end
160
+
161
+ def visit_interpolated_regular_expression_node(node)
162
+ push_location(:regexp, node.opening_loc)
163
+ handle_interpolated_parts(:regexp, node.parts)
164
+ push_location(:regexp, node.closing_loc)
165
+ end
166
+
167
+ alias visit_match_last_line_node visit_regular_expression_node
168
+ alias visit_interpolated_match_last_line_node visit_interpolated_regular_expression_node
169
+
170
+ def visit_string_node(node)
171
+ # Node's location may not cover the entire string literal.
172
+ # For example, in a heredoc string, the node's location covers only the heredoc opening.
173
+ # We need to colorize opening, content and closing separately.
174
+ push_location(:string, node.opening_loc)
175
+ push_location(:string, node.content_loc)
176
+ push_location(:string, node.closing_loc)
177
+ end
178
+
179
+ def visit_interpolated_string_node(node)
180
+ push_location(:string, node.opening_loc)
181
+ handle_interpolated_parts(:string, node.parts)
182
+ push_location(:string, node.closing_loc)
183
+ end
184
+
185
+ def visit_x_string_node(node)
186
+ # Same as visit_string_node, node.location of <<`X` only covers opening,
187
+ # so we need to colorize opening, content and closing separately.
188
+ push_location(:x_string, node.opening_loc)
189
+ push_location(:x_string, node.content_loc)
190
+ push_location(:x_string, node.closing_loc)
191
+ end
192
+
193
+ def visit_interpolated_x_string_node(node)
194
+ push_location(:x_string, node.opening_loc)
195
+ handle_interpolated_parts(:x_string, node.parts)
196
+ push_location(:x_string, node.closing_loc)
197
+ end
198
+
199
+ def visit_array_node(node)
200
+ super
201
+ # Colorize %w[...] array literal like string literals, and %i[...] like symbol literals
202
+ case node.opening
203
+ when /\A%[wW].\z/
204
+ push_location(:string, node.opening_loc)
205
+ push_location(:string, node.closing_loc)
206
+ when /\A%[iI].\z/
207
+ push_location(:symbol, node.opening_loc)
208
+ push_location(:symbol, node.closing_loc)
209
+ end
210
+ end
211
+
212
+ def visit_def_node(node)
213
+ # For special colorizing of method name in def node
214
+ # e.g., `def <=>; end`
215
+ push_location(:identifier, node.name_loc)
216
+ super
217
+ end
218
+
219
+ private
220
+
221
+ def push_location(kind, location)
222
+ # Only push tokens that have a non-zero length
223
+ if location && location.start_offset < location.end_offset
224
+ @tokens << [kind, location.start_offset, location.end_offset]
225
+ end
226
+ end
227
+
228
+ def handle_interpolated_parts(kind, parts)
229
+ # StringNode, EmbeddedStatementsNode brackets, and EmbeddedVariableNode hash in
230
+ # interpolated regexp/symbol/string parts should be colored as regexp/symbol/string respectively.
231
+ parts.each do |part|
232
+ case part
233
+ when Prism::StringNode
234
+ # InterpolatedStringNode#parts may have its own opening/closing. e.g., `'a' "b"`
235
+ push_location(kind, part.opening_loc)
236
+ push_location(kind, part.content_loc)
237
+ push_location(kind, part.closing_loc)
238
+ when Prism::InterpolatedStringNode
239
+ # InterpolatedStringNode#parts may contain InterpolatedStringNode. e.g., `'a' "#{}"`
240
+ part.accept(self)
241
+ when Prism::EmbeddedStatementsNode
242
+ push_location(kind, part.opening_loc)
243
+ push_location(kind, part.closing_loc)
244
+ part.accept(self)
245
+ when Prism::EmbeddedVariableNode
246
+ push_location(kind, part.operator_loc)
247
+ end
248
+ end
249
+ end
250
+ end
251
+
252
+ private_constant :NodeColorizeVisitor
253
+ end
@@ -14,22 +14,22 @@ class RDoc::Parser::Simple < RDoc::Parser
14
14
  ##
15
15
  # Prepare to parse a plain file
16
16
 
17
- def initialize(top_level, file_name, content, options, stats)
17
+ def initialize(top_level, content, options, stats)
18
18
  super
19
19
 
20
20
  preprocess = RDoc::Markup::PreProcess.new @file_name, @options.rdoc_include
21
21
 
22
- @content = preprocess.handle @content, @top_level
22
+ content = RDoc::Text.expand_tabs(@content)
23
+ @content, = preprocess.run_pre_processes(content, @top_level, 1, :simple)
23
24
  end
24
25
 
25
26
  ##
26
27
  # Extract the file contents and attach them to the TopLevel as a comment
27
28
 
28
29
  def scan
29
- comment = remove_coding_comment @content
30
- comment = remove_private_comment comment
30
+ content = remove_coding_comment @content
31
31
 
32
- comment = RDoc::Comment.new comment, @top_level
32
+ comment = RDoc::Comment.new content, @top_level
33
33
 
34
34
  @top_level.comment = comment
35
35
  @top_level
@@ -38,24 +38,7 @@ class RDoc::Parser::Simple < RDoc::Parser
38
38
  ##
39
39
  # Removes the encoding magic comment from +text+
40
40
 
41
- def remove_coding_comment text
41
+ def remove_coding_comment(text)
42
42
  text.sub(/\A# .*coding[=:].*$/, '')
43
43
  end
44
-
45
- ##
46
- # Removes private comments.
47
- #
48
- # Unlike RDoc::Comment#remove_private this implementation only looks for two
49
- # dashes at the beginning of the line. Three or more dashes are considered
50
- # to be a rule and ignored.
51
-
52
- def remove_private_comment comment
53
- # Workaround for gsub encoding for Ruby 1.9.2 and earlier
54
- empty = ''
55
- empty = RDoc::Encoding.change_encoding empty, comment.encoding
56
-
57
- comment = comment.gsub(%r%^--\n.*?^\+\+\n?%m, empty)
58
- comment.sub(%r%^--\n.*%m, empty)
59
- end
60
-
61
44
  end
@@ -9,4 +9,3 @@
9
9
 
10
10
  module RDoc::Parser::Text
11
11
  end
12
-
data/lib/rdoc/parser.rb CHANGED
@@ -91,7 +91,7 @@ class RDoc::Parser
91
91
  # Checks if +file+ is a zip file in disguise. Signatures from
92
92
  # http://www.garykessler.net/library/file_sigs.html
93
93
 
94
- def self.zip? file
94
+ def self.zip?(file)
95
95
  zip_signature = File.read file, 4
96
96
 
97
97
  zip_signature == "PK\x03\x04" or
@@ -104,7 +104,7 @@ class RDoc::Parser
104
104
  ##
105
105
  # Return a parser that can handle a particular extension
106
106
 
107
- def self.can_parse file_name
107
+ def self.can_parse(file_name)
108
108
  parser = can_parse_by_name file_name
109
109
 
110
110
  # HACK Selenium hides a jar file using a .txt extension
@@ -117,7 +117,7 @@ class RDoc::Parser
117
117
  # Returns a parser that can handle the extension for +file_name+. This does
118
118
  # not depend upon the file being readable.
119
119
 
120
- def self.can_parse_by_name file_name
120
+ def self.can_parse_by_name(file_name)
121
121
  _, parser = RDoc::Parser.parsers.find { |regexp,| regexp =~ file_name }
122
122
 
123
123
  # The default parser must not parse binary files
@@ -125,9 +125,11 @@ class RDoc::Parser
125
125
  return parser if ext_name.empty?
126
126
 
127
127
  if parser == RDoc::Parser::Simple and ext_name !~ /txt|rdoc/ then
128
- case check_modeline file_name
128
+ case mode = check_modeline(file_name)
129
129
  when nil, 'rdoc' then # continue
130
- else return nil
130
+ else
131
+ RDoc::Parser.parsers.find { |_, p| return p if mode.casecmp?(p.name[/\w+\z/]) }
132
+ return nil
131
133
  end
132
134
  end
133
135
 
@@ -138,7 +140,7 @@ class RDoc::Parser
138
140
  ##
139
141
  # Returns the file type from the modeline in +file_name+
140
142
 
141
- def self.check_modeline file_name
143
+ def self.check_modeline(file_name)
142
144
  line = File.open file_name do |io|
143
145
  io.gets
144
146
  end
@@ -164,7 +166,8 @@ class RDoc::Parser
164
166
  # Finds and instantiates the correct parser for the given +file_name+ and
165
167
  # +content+.
166
168
 
167
- def self.for top_level, file_name, content, options, stats
169
+ def self.for(top_level, content, options, stats)
170
+ file_name = top_level.absolute_name
168
171
  return if binary? file_name
169
172
 
170
173
  parser = use_markup content
@@ -188,7 +191,7 @@ class RDoc::Parser
188
191
 
189
192
  content = remove_modeline content
190
193
 
191
- parser.new top_level, file_name, content, options, stats
194
+ parser.new top_level, content, options, stats
192
195
  rescue SystemCallError
193
196
  nil
194
197
  end
@@ -205,7 +208,7 @@ class RDoc::Parser
205
208
  ##
206
209
  # Removes an emacs-style modeline from the first line of the document
207
210
 
208
- def self.remove_modeline content
211
+ def self.remove_modeline(content)
209
212
  content.sub(/\A.*-\*-\s*(.*?\S)\s*-\*-.*\r?\n/, '')
210
213
  end
211
214
 
@@ -226,7 +229,7 @@ class RDoc::Parser
226
229
  #
227
230
  # Any comment style may be used to hide the markup comment.
228
231
 
229
- def self.use_markup content
232
+ def self.use_markup(content)
230
233
  markup = content.lines.first(3).grep(/markup:\s+(\w+)/) { $1 }.first
231
234
 
232
235
  return unless markup
@@ -249,12 +252,12 @@ class RDoc::Parser
249
252
  # RDoc::Markup::PreProcess object is created which allows processing of
250
253
  # directives.
251
254
 
252
- def initialize top_level, file_name, content, options, stats
255
+ def initialize(top_level, content, options, stats)
253
256
  @top_level = top_level
254
257
  @top_level.parser = self.class
255
258
  @store = @top_level.store
256
259
 
257
- @file_name = file_name
260
+ @file_name = top_level.absolute_name
258
261
  @content = content
259
262
  @options = options
260
263
  @stats = stats
@@ -263,9 +266,25 @@ class RDoc::Parser
263
266
  @preprocess.options = @options
264
267
  end
265
268
 
266
- autoload :RubyTools, 'rdoc/parser/ruby_tools'
267
- autoload :Text, 'rdoc/parser/text'
269
+ autoload :Text, "#{__dir__}/parser/text"
268
270
 
271
+ ##
272
+ # Normalizes tabs in +body+
273
+
274
+ def handle_tab_width(body)
275
+ if /\t/ =~ body
276
+ tab_width = @options.tab_width
277
+ body.split(/\n/).map do |line|
278
+ 1 while line.gsub!(/\t+/) do
279
+ b, e = $~.offset(0)
280
+ ' ' * (tab_width * (e-b) - b % tab_width)
281
+ end
282
+ line
283
+ end.join "\n"
284
+ else
285
+ body
286
+ end
287
+ end
269
288
  end
270
289
 
271
290
  # simple must come first in order to show up last in the parsers list
@@ -274,4 +293,6 @@ require_relative 'parser/c'
274
293
  require_relative 'parser/changelog'
275
294
  require_relative 'parser/markdown'
276
295
  require_relative 'parser/rd'
296
+ require_relative 'parser/rbs'
277
297
  require_relative 'parser/ruby'
298
+ require_relative 'parser/ruby_colorizer'
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'erb'
4
+ require 'pathname'
5
+ require 'rbs'
6
+ require 'rdoc/markup/formatter'
7
+
8
+ ##
9
+ # RBS type signature support.
10
+ # Loads type information from .rbs files, validates inline annotations,
11
+ # and converts type signatures to HTML with linked type names.
12
+
13
+ module RDoc
14
+ module RbsHelper
15
+ class << self
16
+
17
+ ##
18
+ # Returns true if +sig+ is a valid RBS method type signature.
19
+
20
+ def valid_method_type?(sig)
21
+ RBS::Parser.parse_method_type(sig, require_eof: true)
22
+ true
23
+ rescue RBS::ParsingError
24
+ false
25
+ end
26
+
27
+ ##
28
+ # Returns true if +sig+ is a valid RBS type signature.
29
+
30
+ def valid_type?(sig)
31
+ RBS::Parser.parse_type(sig, require_eof: true)
32
+ true
33
+ rescue RBS::ParsingError
34
+ false
35
+ end
36
+
37
+ ##
38
+ # Loads RBS signatures from the given directories.
39
+ # Returns a Hash mapping "ClassName#method_name" => ["type sig string", ...].
40
+
41
+ def load_signatures(*dirs)
42
+ loader = RBS::EnvironmentLoader.new
43
+ dirs.each { |dir| loader.add(path: Pathname(dir)) }
44
+
45
+ env = RBS::Environment.new
46
+ loader.load(env: env)
47
+
48
+ signatures = {}
49
+
50
+ env.class_decls.each do |type_name, entry|
51
+ class_name = type_name.to_s.delete_prefix('::')
52
+
53
+ entry.each_decl do |decl|
54
+ decl.members.each do |member|
55
+ case member
56
+ when RBS::AST::Members::MethodDefinition
57
+ sigs = member.overloads.map { |o| o.method_type.to_s }
58
+ method_keys_for(class_name, member).each do |key|
59
+ signatures[key] ||= sigs
60
+ end
61
+ when RBS::AST::Members::AttrReader, RBS::AST::Members::AttrWriter, RBS::AST::Members::AttrAccessor
62
+ key = member.kind == :singleton ? "#{class_name}.#{member.name}" : "#{class_name}##{member.name}"
63
+ signatures[key] ||= [member.type.to_s]
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ signatures
70
+ end
71
+
72
+ ##
73
+ # Converts type signature lines to HTML with type names linked to
74
+ # their documentation pages. Uses the RBS parser to extract type
75
+ # name locations precisely.
76
+ #
77
+ # +lines+ is an Array of signature line strings.
78
+ # +lookup+ is a Hash mapping type names to their doc paths.
79
+ # +from_path+ is the current page path for generating relative URLs.
80
+ #
81
+ # Returns escaped HTML with +->+ replaced by +→+.
82
+
83
+ def signature_to_html(lines, lookup:, from_path:)
84
+ lines.map { |line|
85
+ link_type_names_in_line(line, lookup, from_path).gsub('-&gt;', '&rarr;')
86
+ }.join("\n")
87
+ end
88
+
89
+ private
90
+
91
+ # `def self?.foo: ...` produces a member whose kind is :singleton_instance —
92
+ # it defines both Class.foo (singleton) and a private Class#foo (instance),
93
+ # so we need to register the signature under both keys.
94
+ def method_keys_for(class_name, member)
95
+ case member.kind
96
+ when :singleton
97
+ ["#{class_name}.#{member.name}"]
98
+ when :singleton_instance
99
+ ["#{class_name}.#{member.name}", "#{class_name}##{member.name}"]
100
+ else
101
+ ["#{class_name}##{member.name}"]
102
+ end
103
+ end
104
+
105
+ def link_type_names_in_line(line, lookup, from_path)
106
+ escaped = ERB::Util.html_escape(line)
107
+
108
+ locs = collect_type_name_locations(line)
109
+ return escaped if locs.empty?
110
+
111
+ result = escaped.dup
112
+
113
+ # Replace type names with links, working backwards to preserve positions.
114
+ # HTML escaping (e.g. -> becomes -&gt;) shifts positions, so we
115
+ # re-escape the prefix to find the correct offset in the result.
116
+ locs.sort_by { |l| -l[:start] }.each do |loc|
117
+ name = loc[:name]
118
+ next unless (target_path = lookup[name])
119
+
120
+ prefix = ERB::Util.html_escape(line[0...loc[:start]])
121
+ escaped_name = ERB::Util.html_escape(name)
122
+ start_in_escaped = prefix.length
123
+ end_in_escaped = start_in_escaped + escaped_name.length
124
+
125
+ href = ERB::Util.html_escape(::RDoc::Markup::Formatter.gen_relative_url(from_path, target_path))
126
+ result[start_in_escaped...end_in_escaped] =
127
+ "<a href=\"#{href}\" class=\"rbs-type\">#{escaped_name}</a>"
128
+ end
129
+
130
+ result
131
+ end
132
+
133
+ ##
134
+ # Extracts type name locations from a signature line using the RBS parser.
135
+
136
+ def collect_type_name_locations(line)
137
+ locs = []
138
+
139
+ begin
140
+ mt = RBS::Parser.parse_method_type(line, require_eof: true)
141
+ rescue RBS::ParsingError
142
+ begin
143
+ type = RBS::Parser.parse_type(line, require_eof: true)
144
+ collect_from_type(type, locs)
145
+ return locs
146
+ rescue RBS::ParsingError
147
+ return locs
148
+ end
149
+ end
150
+
151
+ mt.type.each_param { |p| collect_from_type(p.type, locs) }
152
+ if mt.block
153
+ mt.block.type.each_param { |p| collect_from_type(p.type, locs) }
154
+ collect_from_type(mt.block.type.return_type, locs)
155
+ end
156
+ collect_from_type(mt.type.return_type, locs)
157
+
158
+ locs
159
+ end
160
+
161
+ ##
162
+ # Recursively collects type name locations from an RBS type AST node.
163
+
164
+ def collect_from_type(type, locs)
165
+ case type
166
+ when RBS::Types::ClassInstance
167
+ name = type.name.to_s.delete_prefix('::')
168
+ if type.location
169
+ name_loc = type.location[:name] || type.location
170
+ locs << { name: name, start: name_loc.end_pos - name.length }
171
+ end
172
+ type.args.each { |a| collect_from_type(a, locs) }
173
+ when RBS::Types::Union, RBS::Types::Intersection, RBS::Types::Tuple
174
+ type.types.each { |t| collect_from_type(t, locs) }
175
+ when RBS::Types::Optional
176
+ collect_from_type(type.type, locs)
177
+ when RBS::Types::Record
178
+ type.all_fields.each_value { |t| collect_from_type(t, locs) }
179
+ when RBS::Types::Proc
180
+ type.type.each_param { |p| collect_from_type(p.type, locs) }
181
+ collect_from_type(type.type.return_type, locs)
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end