summaryrefslogtreecommitdiff
path: root/lib/rdoc/markup/preprocess.rb
blob: cefb498916efefbc1a7f976710d9fc8257c40d72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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
require 'rdoc/markup'

##
# Handle common directives that can occur in a block of text:
#
# : include : filename
#
# RDoc plugin authors can register additional directives to be handled through
# RDoc::Markup::PreProcess::register

class RDoc::Markup::PreProcess

  @registered = {}

  ##
  # 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.

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

  ##
  # Registered directives

  def self.registered
    @registered
  end

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

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

  ##
  # Look for directives in a chunk of +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 param is set as metadata on the
  # +code_object+.  See RDoc::CodeObject#metadata

  def handle text, code_object = nil
    text.gsub!(/^([ \t]*#?[ \t]*):(\w+):([ \t]*)(.+)?\n/) do
      next $& if $3.empty? and $4 and $4[0, 1] == ':'

      prefix    = $1
      directive = $2.downcase
      param     = $4

      case directive
      when 'include' then
        filename = param.split[0]
        include_file filename, prefix

      else
        result = yield directive, param if block_given?

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

        result
      end
    end

    text
  end

  ##
  # Include a file, indenting it correctly.

  def include_file(name, indent)
    if full_name = find_include_file(name) then
      content = if defined?(Encoding) then
                  File.binread full_name
                else
                  File.read full_name
                end
      # HACK determine content type and force encoding
      content = content.sub(/\A# .*coding[=:].*$/, '').lstrip

      # strip leading '#'s, but only if all lines start with them
      if content =~ /^[^#]/ then
        content.gsub(/^/, indent)
      else
        content.gsub(/^#?/, indent)
      end
    else
      warn "Couldn't find file to include '#{name}' from #{@input_file_name}"
      ''
    end
  end

  ##
  # 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

  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

end