~ubuntu-branches/ubuntu/trusty/ruby1.9/trusty

« back to all changes in this revision

Viewing changes to lib/rdoc/markup/fragments.rb

  • Committer: Bazaar Package Importer
  • Author(s): Stephan Hermann
  • Date: 2008-05-16 12:37:06 UTC
  • mfrom: (1.1.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20080516123706-r4llcdfd35aobrjv
Tags: 1.9.0.1-1ubuntu1
* Merge from debian unstable, remaining changes:
  - Robustify check for target_os, fixing build failure on lpia.
* debian/control:
  - ruby1.9 pkg: moved rdoc1.9 suggestion to depends. (LP: #228345)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
require 'rdoc/markup'
 
2
require 'rdoc/markup/lines'
 
3
 
 
4
class RDoc::Markup
 
5
 
 
6
  ##
 
7
  # A Fragment is a chunk of text, subclassed as a paragraph, a list
 
8
  # entry, or verbatim text.
 
9
 
 
10
  class Fragment
 
11
    attr_reader   :level, :param, :txt
 
12
    attr_accessor :type
 
13
 
 
14
    ######
 
15
    # This is a simple factory system that lets us associate fragement
 
16
    # types (a string) with a subclass of fragment
 
17
 
 
18
    TYPE_MAP = {}
 
19
 
 
20
    def self.type_name(name)
 
21
      TYPE_MAP[name] = self
 
22
    end
 
23
 
 
24
    def self.for(line)
 
25
      klass =  TYPE_MAP[line.type] ||
 
26
        raise("Unknown line type: '#{line.type.inspect}:' '#{line.text}'")
 
27
      return klass.new(line.level, line.param, line.flag, line.text)
 
28
    end
 
29
 
 
30
    def initialize(level, param, type, txt)
 
31
      @level = level
 
32
      @param = param
 
33
      @type  = type
 
34
      @txt   = ""
 
35
      add_text(txt) if txt
 
36
    end
 
37
 
 
38
    def add_text(txt)
 
39
      @txt << " " if @txt.length > 0
 
40
      @txt << txt.tr_s("\n ", "  ").strip
 
41
    end
 
42
 
 
43
    def to_s
 
44
      "L#@level: #{self.class.name.split('::')[-1]}\n#@txt"
 
45
    end
 
46
 
 
47
  end
 
48
 
 
49
  ##
 
50
  # A paragraph is a fragment which gets wrapped to fit. We remove all
 
51
  # newlines when we're created, and have them put back on output.
 
52
 
 
53
  class Paragraph < Fragment
 
54
    type_name :PARAGRAPH
 
55
  end
 
56
 
 
57
  class BlankLine < Paragraph
 
58
    type_name :BLANK
 
59
  end
 
60
 
 
61
  class Heading < Paragraph
 
62
    type_name :HEADING
 
63
 
 
64
    def head_level
 
65
      @param.to_i
 
66
    end
 
67
  end
 
68
 
 
69
  ##
 
70
  # A List is a fragment with some kind of label
 
71
 
 
72
  class ListBase < Paragraph
 
73
    LIST_TYPES = [
 
74
      :BULLET,
 
75
      :NUMBER,
 
76
      :UPPERALPHA,
 
77
      :LOWERALPHA,
 
78
      :LABELED,
 
79
      :NOTE,
 
80
    ]
 
81
  end
 
82
 
 
83
  class ListItem < ListBase
 
84
    type_name :LIST
 
85
 
 
86
    def to_s
 
87
      text = if [:NOTE, :LABELED].include? type then
 
88
               "#{@param}: #{@txt}"
 
89
             else
 
90
               @txt
 
91
             end
 
92
 
 
93
      "L#@level: #{type} #{self.class.name.split('::')[-1]}\n#{text}"
 
94
    end
 
95
 
 
96
  end
 
97
 
 
98
  class ListStart < ListBase
 
99
    def initialize(level, param, type)
 
100
      super(level, param, type, nil)
 
101
    end
 
102
  end
 
103
 
 
104
  class ListEnd < ListBase
 
105
    def initialize(level, type)
 
106
      super(level, "", type, nil)
 
107
    end
 
108
  end
 
109
 
 
110
  ##
 
111
  # Verbatim code contains lines that don't get wrapped.
 
112
 
 
113
  class Verbatim < Fragment
 
114
    type_name  :VERBATIM
 
115
 
 
116
    def add_text(txt)
 
117
      @txt << txt.chomp << "\n"
 
118
    end
 
119
 
 
120
  end
 
121
 
 
122
  ##
 
123
  # A horizontal rule
 
124
 
 
125
  class Rule < Fragment
 
126
    type_name :RULE
 
127
  end
 
128
 
 
129
  ##
 
130
  # Collect groups of lines together. Each group will end up containing a flow
 
131
  # of text.
 
132
 
 
133
  class LineCollection
 
134
 
 
135
    def initialize
 
136
      @fragments = []
 
137
    end
 
138
 
 
139
    def add(fragment)
 
140
      @fragments << fragment
 
141
    end
 
142
 
 
143
    def each(&b)
 
144
      @fragments.each(&b)
 
145
    end
 
146
 
 
147
    def to_a # :nodoc:
 
148
      @fragments.map {|fragment| fragment.to_s}
 
149
    end
 
150
 
 
151
    ##
 
152
    # Factory for different fragment types
 
153
 
 
154
    def fragment_for(*args)
 
155
      Fragment.for(*args)
 
156
    end
 
157
 
 
158
    ##
 
159
    # Tidy up at the end
 
160
 
 
161
    def normalize
 
162
      change_verbatim_blank_lines
 
163
      add_list_start_and_ends
 
164
      add_list_breaks
 
165
      tidy_blank_lines
 
166
    end
 
167
 
 
168
    def to_s
 
169
      @fragments.join("\n----\n")
 
170
    end
 
171
 
 
172
    def accept(am, visitor)
 
173
      visitor.start_accepting
 
174
 
 
175
      @fragments.each do |fragment|
 
176
        case fragment
 
177
        when Verbatim
 
178
          visitor.accept_verbatim(am, fragment)
 
179
        when Rule
 
180
          visitor.accept_rule(am, fragment)
 
181
        when ListStart
 
182
          visitor.accept_list_start(am, fragment)
 
183
        when ListEnd
 
184
          visitor.accept_list_end(am, fragment)
 
185
        when ListItem
 
186
          visitor.accept_list_item(am, fragment)
 
187
        when BlankLine
 
188
          visitor.accept_blank_line(am, fragment)
 
189
        when Heading
 
190
          visitor.accept_heading(am, fragment)
 
191
        when Paragraph
 
192
          visitor.accept_paragraph(am, fragment)
 
193
        end
 
194
      end
 
195
 
 
196
      visitor.end_accepting
 
197
    end
 
198
 
 
199
    private
 
200
 
 
201
    # If you have:
 
202
    #
 
203
    #    normal paragraph text.
 
204
    #
 
205
    #       this is code
 
206
    #   
 
207
    #       and more code
 
208
    #
 
209
    # You'll end up with the fragments Paragraph, BlankLine, Verbatim,
 
210
    # BlankLine, Verbatim, BlankLine, etc.
 
211
    #
 
212
    # The BlankLine in the middle of the verbatim chunk needs to be changed to
 
213
    # a real verbatim newline, and the two verbatim blocks merged
 
214
 
 
215
    def change_verbatim_blank_lines
 
216
      frag_block = nil
 
217
      blank_count = 0
 
218
      @fragments.each_with_index do |frag, i|
 
219
        if frag_block.nil?
 
220
          frag_block = frag if Verbatim === frag
 
221
        else
 
222
          case frag
 
223
          when Verbatim
 
224
            blank_count.times { frag_block.add_text("\n") }
 
225
            blank_count = 0
 
226
            frag_block.add_text(frag.txt)
 
227
            @fragments[i] = nil    # remove out current fragment
 
228
          when BlankLine
 
229
            if frag_block
 
230
              blank_count += 1
 
231
              @fragments[i] = nil
 
232
            end
 
233
          else
 
234
            frag_block = nil
 
235
            blank_count = 0
 
236
          end
 
237
        end
 
238
      end
 
239
      @fragments.compact!
 
240
    end
 
241
 
 
242
    ##
 
243
    # List nesting is implicit given the level of indentation. Make it
 
244
    # explicit, just to make life a tad easier for the output processors
 
245
 
 
246
    def add_list_start_and_ends
 
247
      level = 0
 
248
      res = []
 
249
      type_stack = []
 
250
 
 
251
      @fragments.each do |fragment|
 
252
        # $stderr.puts "#{level} : #{fragment.class.name} : #{fragment.level}"
 
253
        new_level = fragment.level
 
254
        while (level < new_level)
 
255
          level += 1
 
256
          type = fragment.type
 
257
          res << ListStart.new(level, fragment.param, type) if type
 
258
          type_stack.push type
 
259
          # $stderr.puts "Start: #{level}"
 
260
        end
 
261
 
 
262
        while level > new_level
 
263
          type = type_stack.pop
 
264
          res << ListEnd.new(level, type) if type
 
265
          level -= 1
 
266
          # $stderr.puts "End: #{level}, #{type}"
 
267
        end
 
268
 
 
269
        res << fragment
 
270
        level = fragment.level
 
271
      end
 
272
      level.downto(1) do |i|
 
273
        type = type_stack.pop
 
274
        res << ListEnd.new(i, type) if type
 
275
      end
 
276
 
 
277
      @fragments = res
 
278
    end
 
279
 
 
280
    ##
 
281
    # Inserts start/ends between list entries at the same level that have
 
282
    # different element types
 
283
 
 
284
    def add_list_breaks
 
285
      res = @fragments
 
286
 
 
287
      @fragments = []
 
288
      list_stack = []
 
289
 
 
290
      res.each do |fragment|
 
291
        case fragment
 
292
        when ListStart
 
293
          list_stack.push fragment
 
294
        when ListEnd
 
295
          start = list_stack.pop
 
296
          fragment.type = start.type
 
297
        when ListItem
 
298
          l = list_stack.last
 
299
          if fragment.type != l.type
 
300
            @fragments << ListEnd.new(l.level, l.type)
 
301
            start = ListStart.new(l.level, fragment.param, fragment.type)
 
302
            @fragments << start
 
303
            list_stack.pop
 
304
            list_stack.push start
 
305
          end
 
306
        else
 
307
          ;
 
308
        end
 
309
        @fragments << fragment
 
310
      end
 
311
    end
 
312
 
 
313
    ##
 
314
    # Tidy up the blank lines:
 
315
    # * change Blank/ListEnd into ListEnd/Blank
 
316
    # * remove blank lines at the front
 
317
 
 
318
    def tidy_blank_lines
 
319
      (@fragments.size - 1).times do |i|
 
320
        if BlankLine === @fragments[i] and ListEnd === @fragments[i+1] then
 
321
          @fragments[i], @fragments[i+1] = @fragments[i+1], @fragments[i]
 
322
        end
 
323
      end
 
324
 
 
325
      # remove leading blanks
 
326
      @fragments.each_with_index do |f, i|
 
327
        break unless f.kind_of? BlankLine
 
328
        @fragments[i] = nil
 
329
      end
 
330
 
 
331
      @fragments.compact!
 
332
    end
 
333
 
 
334
  end
 
335
 
 
336
end
 
337