Class: RDoc::Markup::PreProcess

Inherits:
Object
  • Object
show all
Defined in:
lib/rdoc/markup/pre_process.rb

Overview

Handle common directives that can occur in a block of text:

\:include: filename

Directives can be escaped by preceding them with a backslash.

RDoc plugin authors can register additional directives to be handled by using RDoc::Markup::PreProcess::register.

Any directive that is not built-in to RDoc (including those registered via plugins) will be stored in the metadata hash on the CodeObject the comment is attached to. See RDoc::Markup@Directives for the list of built-in directives.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(input_file_name, include_path) ⇒ PreProcess

Creates a new pre-processor for input_file_name that will look for included files in include_path



80
81
82
83
84
# File 'lib/rdoc/markup/pre_process.rb', line 80

def initialize(input_file_name, include_path)
  @input_file_name = input_file_name
  @include_path = include_path
  @options = nil
end

Instance Attribute Details

#options ⇒ Object

An RDoc::Options instance that will be filled in with overrides from directives



25
26
27
# File 'lib/rdoc/markup/pre_process.rb', line 25

def options
  @options
end

Class Method Details

.post_process(&block) ⇒ Object

Adds a post-process handler for directives. The handler will be called with the result RDoc::Comment (or text String) and the code object for the comment (if any).



32
33
34
# File 'lib/rdoc/markup/pre_process.rb', line 32

def self.post_process(&block)
  @post_processors << block
end

.post_processors ⇒ Object

Registered post-processors



39
40
41
# File 'lib/rdoc/markup/pre_process.rb', line 39

def self.post_processors
  @post_processors
end

.register(directive, &block) ⇒ Object

Registers directive as one handled by RDoc. If a block is given the directive will be replaced by the result of the block, otherwise the directive will be removed from the processed text.

The block will be called with the directive name and the directive parameter:

RDoc::Markup::PreProcess.register 'my-directive' do |directive, param|
# replace text, etc.
end


55
56
57
# File 'lib/rdoc/markup/pre_process.rb', line 55

def self.register(directive, &block)
  @registered[directive] = block
end

.registered ⇒ Object

Registered directives



62
63
64
# File 'lib/rdoc/markup/pre_process.rb', line 62

def self.registered
  @registered
end

.reset ⇒ Object

Clears all registered directives and post-processors



69
70
71
72
# File 'lib/rdoc/markup/pre_process.rb', line 69

def self.reset
  @post_processors = []
  @registered = {}
end

Instance Method Details

#find_include_file(name) ⇒ Object

Look for the given file in the directory containing the current file, and then in each of the directories specified in the RDOC_INCLUDE path



309
310
311
312
313
314
315
316
317
# File 'lib/rdoc/markup/pre_process.rb', line 309

def find_include_file(name)
  to_search = [File.dirname(@input_file_name)].concat @include_path
  to_search.each do |dir|
    full_name = File.join(dir, name)
    stat = File.stat(full_name) rescue next
    return full_name if stat.readable?
  end
  nil
end

#handle(text, code_object = nil, &block) ⇒ Object

Look for directives in the given text.

Options that we don't handle are yielded. If the block returns false the directive is restored to the text. If the block returns nil or no block was given the directive is handled according to the registered directives. If a String was returned the directive is replaced with the string.

If no matching directive was registered the directive is restored to the text.

If code_object is given and the directive is unknown then the directive's parameter is set as metadata on the code_object. See RDoc::CodeObject#metadata for details.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/rdoc/markup/pre_process.rb', line 101

def handle(text, code_object = nil, &block)
  if Comment === text
    comment = text
    text = text.text
  end

  # regexp helper (square brackets for optional)
  # $1      $2  $3        $4      $5
  # [prefix][\]:directive:[spaces][param]newline
  text = text.gsub(/^([ \t]*(?:#|\/?\*)?[ \t]*)(\\?):([\w-]+):([ \t]*)(.+)?(\r?\n|$)/) do
    # skip something like ':toto::'
    next $& if $4.empty? and $5 and $5[0, 1] == ':'

    # skip if escaped
    next "#$1:#$3:#$4#$5\n" unless $2.empty?

    # This is not in handle_directive because I didn't want to pass another
    # argument into it
    if comment and $3 == 'markup'
      next "#{$1.strip}\n" unless $5
      comment.format = $5.downcase
      next "#{$1.strip}\n"
    end
    handle_directive $1, $3, $5, code_object, text.encoding, &block
  end

  if comment
    comment.text = text
  else
    comment = text
  end

  run_post_processes(comment, code_object)

  text
end

#handle_directive(prefix, directive, param, code_object = nil, encoding = nil) ⇒ Object

Performs the actions described by directive and its parameter param.

code_object is used for directives that operate on a class or module. prefix is used to ensure the replacement for handled directives is correct. encoding is used for the include directive.

For a list of directives in RDoc see RDoc::Markup.

When 1.8.7 support is ditched prefix can be defaulted to ''



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/rdoc/markup/pre_process.rb', line 179

def handle_directive(prefix, directive, param, code_object = nil,
                     encoding = nil)
  blankline = "#{prefix.strip}\n"
  directive = directive.downcase

  case directive
  when 'arg', 'args'
    return "#{prefix}:#{directive}: #{param}\n" unless code_object && code_object.kind_of?(AnyMethod)

    code_object.params = param

    blankline
  when 'category'
    if Context === code_object
      section = code_object.add_section param
      code_object.temporary_section = section
    elsif AnyMethod === code_object
      code_object.section_title = param
    end

    blankline # ignore category if we're not on an RDoc::Context
  when 'doc'
    return blankline unless code_object
    code_object.document_self = true
    code_object.force_documentation = true

    blankline
  when 'enddoc'
    return blankline unless code_object
    code_object.done_documenting = true

    blankline
  when 'include'
    filename = param.split(' ', 2).first
    include_file filename, prefix, encoding
  when 'nodoc'
    return blankline unless code_object
    code_object.document_self = nil # notify nodoc
    code_object.document_children = param !~ /all/i

    blankline
  when 'notnew', 'not_new', 'not-new'
    return blankline unless AnyMethod === code_object

    code_object.dont_rename_initialize = true

    blankline
  when 'startdoc'
    return blankline unless code_object

    code_object.start_doc
    code_object.force_documentation = true

    blankline
  when 'stopdoc'
    return blankline unless code_object

    code_object.stop_doc

    blankline
  when 'yield', 'yields'
    return blankline unless code_object
    # remove parameter &block
    code_object.params = code_object.params.sub(/,?\s*&\w+/, '') if code_object.params

    code_object.block_params = param || ''

    blankline
  else
    result = yield directive, param if block_given?

    case result
    when nil
      code_object.[directive] = param if code_object

      if Markup::PreProcess.registered.include? directive
        handler = Markup::PreProcess.registered[directive]
        result = handler.call directive, param if handler
      else
        result = "#{prefix}:#{directive}: #{param}\n"
      end
    when false
      result = "#{prefix}:#{directive}: #{param}\n"
    end

    result
  end
end

#include_file(name, indent, encoding) ⇒ Object

Handles the :include: _filename_ directive.

If the first line of the included file starts with '#', and contains an encoding information in the form 'coding:' or 'coding=', it is removed.

If all lines in the included file start with a '#', this leading '#' is removed before inclusion. The included content is indented like the :include: directive.

so all content will be verbatim because of the likely space after '#'? TODO shift left the whole file content in that case TODO comment stop/start #-- and #++ in included file must be processed here



283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
# File 'lib/rdoc/markup/pre_process.rb', line 283

def include_file(name, indent, encoding)
  full_name = find_include_file name

  unless full_name
    warn "Couldn't find file to include '#{name}' from #{@input_file_name}"
    return ''
  end

  content = Encoding.read_file full_name, encoding, true
  content = Encoding.remove_magic_comment content

  # strip magic comment
  content = content.sub(/\A# .*coding[=:].*$/, '').lstrip

  # strip leading '#'s, but only if all lines start with them
  if content =~ /^[^#]/
    content.gsub(/^/, indent)
  else
    content.gsub(/^#?/, indent)
  end
end

#parse_comment(text, line_no, type) ⇒ Object

Parse comment and return [normalized_comment_text, directives_hash]



162
163
164
165
166
# File 'lib/rdoc/markup/pre_process.rb', line 162

def parse_comment(text, line_no, type)
  Comment.parse(text, @input_file_name, line_no, type) do |filename, prefix_indent|
    include_file(filename, prefix_indent, text.encoding)
  end
end

#run_post_processes(comment, code_object) ⇒ Object

Perform post preocesses to a code object



154
155
156
157
158
# File 'lib/rdoc/markup/pre_process.rb', line 154

def run_post_processes(comment, code_object)
  self.class.post_processors.each do |handler|
    handler.call comment, code_object
  end
end

#run_pre_processes(comment_text, code_object, start_line_no, type) ⇒ Object

Apply directives to a code object



140
141
142
143
144
145
146
147
148
149
150
# File 'lib/rdoc/markup/pre_process.rb', line 140

def run_pre_processes(comment_text, code_object, start_line_no, type)
  comment_text, directives = parse_comment(comment_text, start_line_no, type)
  directives.each do |directive, (param, line_no)|
    handle_directive('', directive, param, code_object)
  end
  if code_object.is_a?(AnyMethod) && (call_seq, = directives['call-seq']) && call_seq
    code_object.call_seq = call_seq.lines.map(&:chomp).reject(&:empty?).join("\n")
  end
  format, = directives['markup']
  [comment_text, format]
end