Module: TaskJuggler::RichTextSyntaxRules

Included in:
RichTextParser
Defined in:
lib/taskjuggler/RichText/SyntaxRules.rb

Overview

This modules contains the syntax definition for the RichTextParser. The defined syntax aims to be compatible to the most commonly used markup elements of the MediaWiki system. See en.wikipedia.org/wiki/Wikipedia:Cheatsheet for details.

Linebreaks are treated just like spaces as word separators unless it is followed by another newline or any of the start-of-line special characters. These characters start sequences that mark headlines, bullet items and such. The special meaning only gets activated when used at the start of the line.

The parser traverses the input text and creates a tree of RichTextElement objects. This is the intermediate representation that can be converted to the final output format.

Instance Method Summary collapse

Instance Method Details

#rule_blankLinesObject



411
412
413
414
415
416
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 411

def rule_blankLines
  optional
  repeatable
  pattern(%w( $LINEBREAK ))
  pattern(%w( $SPACE ))
end

#rule_blockFunctionObject



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 418

def rule_blockFunction
  pattern(%w( $BLOCKFUNCSTART $ID !functionArguments $BLOCKFUNCEND ),
          lambda {
    args = {}
    @val[2].each { |arg| args[arg[0]] = arg[1] } if @val[2]
    el = RichTextElement.new(@richTextI, :blockfunc)
    # Data is a 2 element Array with the function name and a Hash for the
    # arguments.
    unless @richTextI.richText.functionHandler(@val[1], true)
      error('bad_block_function',
            "Unsupported block function #{@val[1]}")
    end
    el.data = [@val[1], args ]
    el
  })
end

#rule_bulletList1Object



136
137
138
139
140
141
142
143
144
145
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 136

def rule_bulletList1
  optional
  repeatable
  pattern(%w( $BULLET1 !text ), lambda {
    RichTextElement.new(@richTextI, :bulletitem1, @val[1])
  })
  pattern(%w( !bulletList2 ), lambda {
    RichTextElement.new(@richTextI, :bulletlist2, @val[0])
  })
end

#rule_bulletList2Object



147
148
149
150
151
152
153
154
155
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 147

def rule_bulletList2
  repeatable
  pattern(%w( $BULLET2 !text ), lambda {
    RichTextElement.new(@richTextI, :bulletitem2, @val[1])
  })
  pattern(%w( !bulletList3 ), lambda {
    RichTextElement.new(@richTextI, :bulletlist3, @val[0])
  })
end

#rule_bulletList3Object



157
158
159
160
161
162
163
164
165
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 157

def rule_bulletList3
  repeatable
  pattern(%w( $BULLET3 !text ), lambda {
    RichTextElement.new(@richTextI, :bulletitem3, @val[1])
  })
  pattern(%w( !bulletList4 ), lambda {
    RichTextElement.new(@richTextI, :bulletlist4, @val[0])
  })
end

#rule_bulletList4Object



167
168
169
170
171
172
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 167

def rule_bulletList4
  repeatable
  pattern(%w( $BULLET4 !text ), lambda {
    RichTextElement.new(@richTextI, :bulletitem4, @val[1])
  })
end

#rule_functionArgumentsObject



454
455
456
457
458
459
460
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 454

def rule_functionArguments
  optional
  repeatable
  pattern(%w( $ID _= $STRING ), lambda {
    [ @val[0], @val[2] ]
  })
end

#rule_headlinesObject



75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 75

def rule_headlines
  pattern(%w( !title1 ), lambda {
    @val[0]
  })
  pattern(%w( !title2 ), lambda {
    @val[0]
  })
  pattern(%w( !title3 ), lambda {
    @val[0]
  })
  pattern(%w( !title4 ), lambda {
    @val[0]
  })
end

#rule_htmlBlobObject



396
397
398
399
400
401
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 396

def rule_htmlBlob
  repeatable
  pattern(%w( $HTMLBLOB ), lambda {
    @val[0]
  })
end

#rule_inlineFunctionObject



435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 435

def rule_inlineFunction
  pattern(%w( $INLINEFUNCSTART $ID !functionArguments $INLINEFUNCEND
              !space ),
          lambda {
    args = {}
    @val[2].each { |arg| args[arg[0]] = arg[1] } if @val[2]
    el = RichTextElement.new(@richTextI, :inlinefunc)
    # Data is a 2 element Array with the function name and a Hash for the
    # arguments.
    unless @richTextI.richText.functionHandler(@val[1], false)
      error('bad_inline_function',
            "Unsupported inline function #{@val[1]}")
    end
    el.data = [@val[1], args ]
    el.appendSpace = !@val[4].nil?
    el
  })
end

#rule_moreRefTokenObject



339
340
341
342
343
344
345
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 339

def rule_moreRefToken
  repeatable
  optional
  pattern(%w( _| !refToken ), lambda {
    @val[1].join
  })
end

#rule_numberList1Object



174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 174

def rule_numberList1
  repeatable
  pattern(%w( $NUMBER1 !text !blankLines ), lambda {
    el = RichTextElement.new(@richTextI, :numberitem1, @val[1])
    @numberListCounter[0] += 1
    el.data = @numberListCounter.dup
    el
  })
  pattern(%w( !numberList2 ), lambda {
    @numberListCounter[1, 2] = [ 0, 0 ]
    RichTextElement.new(@richTextI, :numberlist2, @val[0])
  })
end

#rule_numberList2Object



188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 188

def rule_numberList2
  repeatable
  pattern(%w( $NUMBER2 !text !blankLines ), lambda {
    el = RichTextElement.new(@richTextI, :numberitem2, @val[1])
    @numberListCounter[1] += 1
    el.data = @numberListCounter.dup
    el
  })
  pattern(%w( !numberList3 ), lambda {
    @numberListCounter[2] = 0
    RichTextElement.new(@richTextI, :numberlist3, @val[0])
  })
end

#rule_numberList3Object



202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 202

def rule_numberList3
  repeatable
  pattern(%w( $NUMBER3 !text !blankLines ), lambda {
    el = RichTextElement.new(@richTextI, :numberitem3, @val[1])
    @numberListCounter[2] += 1
    el.data = @numberListCounter.dup
    el
  })
  pattern(%w( !numberList4 ), lambda {
    @numberListCounter[3] = 0
    RichTextElement.new(@richTextI, :numberlist4, @val[0])
  })
end

#rule_numberList4Object



216
217
218
219
220
221
222
223
224
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 216

def rule_numberList4
  repeatable
  pattern(%w( $NUMBER4 !text !blankLines ), lambda {
    el = RichTextElement.new(@richTextI, :numberitem4, @val[1])
    @numberListCounter[3] += 1
    el.data = @numberListCounter.dup
    el
  })
end

#rule_paragraphObject



226
227
228
229
230
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 226

def rule_paragraph
  pattern(%w( !text ), lambda {
    RichTextElement.new(@richTextI, :paragraph, @val[0])
  })
end

#rule_plainTextObject



372
373
374
375
376
377
378
379
380
381
382
383
384
385
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 372

def rule_plainText
  repeatable
  optional
  pattern(%w( !htmlBlob !space ), lambda {
    el = RichTextElement.new(@richTextI, :htmlblob, @val[0].join)
    el.appendSpace = !@val[1].nil?
    el
  })
  pattern(%w( $WORD !space ), lambda {
    el = RichTextElement.new(@richTextI, :text, @val[0])
    el.appendSpace = !@val[1].nil?
    el
  })
end


285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 285

def rule_plainTextWithLinks
  pattern(%w( !plainText ), lambda {
    @val[0]
  })
  pattern(%w( $REF !refToken !moreRefToken $REFEND !space ), lambda {
    v1 = @val[1].join
    if v1.index(':')
      protocol, locator = v1.split(':')
    else
      protocol = nil
    end
    el = nil
    if protocol == 'File'
      el = RichTextElement.new(@richTextI, :img)
      unless (index = locator.rindex('.'))
        error('rt_file_no_ext', "File name without extension: #{locator}")
      end
      extension = locator[index + 1..-1].downcase
      unless %w( jpg gif png svg ).include?(extension)
        error('rt_file_bad_ext', "Unsupported file type: #{extension}")
      end
      el.data = img = RichTextImage.new(locator)
      if @val[2]
        @val[2].each do |token|
          if token[0, 4] == 'alt='
            img.altText = token[4..-1]
          elsif %w( top middle bottom baseline sub super text-top
                    text-bottom ).include?(token)
            img.verticalAlign = token
          else
            error('rt_bad_file_option',
                  "Unknown option '#{token}' for file reference " +
                  "#{v1}.")
          end
        end
      end
    else
      val = @val[2] || v1
      el = RichTextElement.new(@richTextI, :ref,
                               RichTextElement.new(@richTextI, :text, val))
      el.data = v1
      el.appendSpace = !@val[4].nil?
    end
    el
  })
  pattern(%w( $HREF !wordWithQueries !space !plainTextWithQueries
              $HREFEND !space ), lambda {
    el = RichTextElement.new(@richTextI, :href, @val[3] || @val[1])
    el.data = RichTextElement.new(@richTextI, :richtext, @val[1])
    el.appendSpace = !@val[5].nil?
    el
  })
end

#rule_plainTextWithQueriesObject



387
388
389
390
391
392
393
394
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 387

def rule_plainTextWithQueries
  repeatable
  optional
  pattern(%w( !wordWithQueries !space ), lambda {
    @val[0][-1].appendSpace = true if @val[1]
    @val[0]
  })
end

#rule_preObject



90
91
92
93
94
95
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 90

def rule_pre
  repeatable
  pattern(%w( $PRE ), lambda {
    @val[0]
  })
end

#rule_refTokenObject



347
348
349
350
351
352
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 347

def rule_refToken
  repeatable
  pattern(%w( $WORD ), lambda {
    @val[0]
  })
end

#rule_richtextObject

This is the entry node.



34
35
36
37
38
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 34

def rule_richtext
  pattern(%w( !sections . ), lambda {
    RichTextElement.new(@richTextI, :richtext, @val[0])
  })
end

#rule_sectionObject

The following syntax elements are all block elements that can span multiple lines.



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 50

def rule_section
  pattern(%w( !headlines ), lambda {
    @val[0]
  })
  pattern(%w( $HLINE ), lambda {
    RichTextElement.new(@richTextI, :hline, @val[0])
  })
  pattern(%w( !paragraph ), lambda {
    @val[0]
  })
  pattern(%w( !pre ), lambda {
    RichTextElement.new(@richTextI, :pre, @val[0].join)
  })
  pattern(%w( !bulletList1 ), lambda {
    RichTextElement.new(@richTextI, :bulletlist1, @val[0])
  })
  pattern(%w( !numberList1 ), lambda {
    @numberListCounter = [ 0, 0, 0, 0 ]
    RichTextElement.new(@richTextI, :numberlist1, @val[0])
  })
  pattern(%w( !blockFunction ), lambda {
    @val[0]
  })
end

#rule_sectionsObject



40
41
42
43
44
45
46
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 40

def rule_sections
  optional
  repeatable
  pattern(%w( !section !blankLines ), lambda {
    @val[0]
  })
end

#rule_spaceObject



403
404
405
406
407
408
409
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 403

def rule_space
  optional
  repeatable
  pattern(%w( $SPACE ), lambda {
    true
  })
end

#rule_textObject



232
233
234
235
236
237
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 232

def rule_text
  pattern(%w( !textWithSpace ), lambda {
    @val[0].last.appendSpace = false
    @val[0]
  })
end

#rule_textWithSpaceObject



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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 239

def rule_textWithSpace
  repeatable
  pattern(%w( !plainTextWithLinks ), lambda {
    @val[0]
  })
  pattern(%w( !inlineFunction ), lambda {
    @val[0]
  })
  pattern(%w( $ITALIC !space !plainTextWithLinks $ITALIC !space ), lambda {
    el = RichTextElement.new(@richTextI, :italic, @val[2])
    # Since the italic end marker will disappear we need to make sure
    # there was no space before it.
    @val[2].last.appendSpace = false if @val[2].last
    el.appendSpace = !@val[4].nil?
    el
  })
  pattern(%w( $BOLD !space !plainTextWithLinks $BOLD !space ), lambda {
    el = RichTextElement.new(@richTextI, :bold, @val[2])
    @val[2].last.appendSpace = false if @val[2].last
    el.appendSpace = !@val[4].nil?
    el
  })
  pattern(%w( $CODE !space !plainTextWithLinks $CODE !space ), lambda {
    el = RichTextElement.new(@richTextI, :code, @val[2])
    @val[2].last.appendSpace = false if @val[2].last
    el.appendSpace = !@val[4].nil?
    el
  })
  pattern(%w( $BOLDITALIC !space !plainTextWithLinks $BOLDITALIC !space ),
          lambda {
    el = RichTextElement.new(@richTextI,
                        :bold, RichTextElement.new(@richTextI,
                                                   :italic, @val[2]))
    @val[2].last.appendSpace = false if @val[2].last
    el.appendSpace = !@val[4].nil?
    el
  })
  pattern(%w( $FCOLSTART !space !plainTextWithLinks $FCOLEND !space ),
          lambda {
    el = RichTextElement.new(@richTextI, :fontCol, @val[2])
    el.data = @val[0]
    el.appendSpace = !@val[4].nil?
    el
  })
end

#rule_title1Object



97
98
99
100
101
102
103
104
105
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 97

def rule_title1
  pattern(%w( $TITLE1 !space !text $TITLE1END ), lambda {
    el = RichTextElement.new(@richTextI, :title1, @val[2])
    @sectionCounter[0] += 1
    @sectionCounter[1] = @sectionCounter[2] = 0
    el.data = @sectionCounter.dup
    el
  })
end

#rule_title2Object



107
108
109
110
111
112
113
114
115
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 107

def rule_title2
  pattern(%w( $TITLE2 !space !text $TITLE2END ), lambda {
    el = RichTextElement.new(@richTextI, :title2, @val[2])
    @sectionCounter[1] += 1
    @sectionCounter[2] = 0
    el.data = @sectionCounter.dup
    el
  })
end

#rule_title3Object



117
118
119
120
121
122
123
124
125
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 117

def rule_title3
  pattern(%w( $TITLE3 !space !text $TITLE3END ), lambda {
    el = RichTextElement.new(@richTextI, :title3, @val[2])
    @sectionCounter[2] += 1
    @sectionCounter[3] = 0
    el.data = @sectionCounter.dup
    el
  })
end

#rule_title4Object



127
128
129
130
131
132
133
134
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 127

def rule_title4
  pattern(%w( $TITLE4 !space !text $TITLE4END ), lambda {
    el = RichTextElement.new(@richTextI, :title4, @val[2])
    @sectionCounter[3] += 1
    el.data = @sectionCounter.dup
    el
  })
end

#rule_wordWithQueriesObject



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/taskjuggler/RichText/SyntaxRules.rb', line 354

def rule_wordWithQueries
  repeatable
  pattern(%w( $WORD ), lambda {
    RichTextElement.new(@richTextI, :text, @val[0])
  })
  pattern(%w( $QUERY ), lambda {
    # The <-attributeID-> syntax is a shortcut for an embedded query
    # inline function. It can only be used within a ReportTableCell
    # context that provides a property and a scope property.
    el = RichTextElement.new(@richTextI, :inlinefunc)
    # Data is a 2 element Array with the function name and a Hash for the
    # arguments.
    el.data = ['query', { 'attribute' => @val[0] } ]
    el
  })

end