class TaskJuggler::RichTextScanner

The RichTextScanner is used by the RichTextParser to chop the input text into digestable tokens. It specializes the TextScanner class for RichText syntax. The scanner can operate in various modes. The current mode is context dependent. The following modes are supported:

:bop : at the begining of a paragraph. :bol : at the begining of a line. :inline : in the middle of a line :nowiki : ignoring all MediaWiki special tokens :html : read anything until </html> :ref : inside of a REF [[ .. ]] :href : inside of an HREF [ .. ] :func : inside of a block <[ .. ]> or inline <- .. -> function

Public Class Methods

new(masterFile, log) click to toggle source
Calls superclass method
# File lib/taskjuggler/RichText/Scanner.rb, line 35
def initialize(masterFile, log)
  tokenPatterns = [
    # :bol mode rules
    [ :LINEBREAK, /\s*\n/, :bol, method('linebreak') ],
    [ nil, /\s+/, :bol, method('inlineMode') ],

    # :bop mode rules
    [ :PRE, / [^\n]+\n?/, :bop, method('pre') ],
    [ nil, /\s*\n/, :bop, method('linebreak') ],

    # :inline mode rules
    [ :SPACE, /[ \t\n]+/, :inline, method('space') ],

    # :bop and :bol mode rules
    [ :INLINEFUNCSTART, /<-/, [ :bop, :bol, :inline ],
      method('functionStart') ],
    [ :BLOCKFUNCSTART, /<\[/, [ :bop, :bol ], method('functionStart') ],
    [ ':TITLE*', /={2,5}/, [ :bop, :bol ], method('titleStart') ],
    [ 'TITLE*END', /={2,5}/, :inline, method('titleEnd') ],
    [ 'BULLET*', /\*{1,4}[ \t]+/, [ :bop, :bol ], method('bullet') ],
    [ 'NUMBER*', /\#{1,4}[ \t]+/, [ :bop, :bol ], method('number') ],
    [ :HLINE, /----/, [ :bop, :bol ], method('inlineMode') ],

    # :bop, :bol and :inline mode rules
    # The <nowiki> token puts the scanner into :nowiki mode.
    [ nil, /<nowiki>/, [ :bop, :bol, :inline ], method('nowikiStart') ],
    [ nil, /<html>/, [ :bop, :bol, :inline ], method('htmlStart') ],
    [ :FCOLSTART, /<fcol:([a-z]+|#[0-9A-Fa-f]{3,6})>/, [ :bop, :bol,
      :inline ],
      method('fontColorStart') ],
    [ :FCOLEND, /<\/fcol>/, [ :bop, :bol, :inline ],
      method('fontColorEnd') ],
    [ :QUOTES, /'{2,5}/, [ :bop, :bol, :inline ], method('quotes') ],
    [ :REF, /\[\[/, [ :bop, :bol, :inline ], method('refStart') ],
    [ :HREF, /\[/, [ :bop, :bol, :inline], method('hrefStart') ],
    [ :WORD, /.[^ \n\t\[<']*/, [ :bop, :bol, :inline ],
      method('inlineMode') ],

    # :nowiki mode rules
    [ nil, /<\/nowiki>/, :nowiki, method('nowikiEnd') ],
    [ :WORD, /(<(?!\/nowiki>)|[^ \t\n<])+/, :nowiki ],
    [ :SPACE, /[ \t]+/, :nowiki ],
    [ :LINEBREAK, /\s*\n/, :nowiki ],

    # :html mode rules
    [ :HTMLBLOB, /(.|\n)*<\/html>/ , :html, method('htmlEnd') ],
    [ :HTMLBLOB, /.*\n/ , :html ],

    # :ref mode rules
    [ :REFEND, /\]\]/, :ref, method('refEnd') ],
    [ :WORD, /(<(?!-)|(\](?!\])|[^|<\]]))+/, :ref ],
    [ :QUERY, /<-\w+->/, :ref, method('query') ],
    [ :LITERAL, /./, :ref ],

    # :href mode rules
    [ :HREFEND, /\]/, :href, method('hrefEnd') ],
    [ :WORD, /(<(?!-)|[^ \t\n\]<])+/, :href ],
    [ :QUERY, /<-\w+->/, :href, method('query') ],
    [ :SPACE, /[ \t\n]+/, :href ],

    # :func mode rules
    [ :INLINEFUNCEND, /->/ , :func, method('functionEnd') ],
    [ :BLOCKFUNCEND, /\]>/, :func, method('functionEnd') ],
    [ :ID, /[a-zA-Z_]\w*/, :func ],
    [ :STRING, /"(\\"|[^"])*"/, :func, method('dqString') ],
    [ :STRING, /'(\\'|[^'])*'/, :func, method('sqString') ],
    [ nil, /[ \t\n]+/, :func ],
    [ :LITERAL, /./, :func ]
  ]
  super(masterFile, log, tokenPatterns, :bop)
end

Private Instance Methods

bullet(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 138
def bullet(type, match)
  self.mode = :inline
  [ "BULLET#{match.count('*')}".intern, match ]
end
dqString(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 215
def dqString(type, match)
  # Remove first and last character and remove backslashes from quoted
  # double quotes.
  [ type, match[1..-2].gsub(/\\"/, '"') ]
end
fontColorEnd(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 167
def fontColorEnd(type, match)
  [ type, match ]
end
fontColorStart(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 148
def fontColorStart(type, match)
  self.mode = :inline
  # Extract color name from <fcol:colname>
  colName = match[6..-2]
  if colName =~ /#([0-9A-Fa-f]{3}|[0-9A-Fa-f]{6})/
    # We've got a valid hex number.
  else
    validColors = %w( black maroon green olive navy purple teal silver
                      gray red lime yellow blue fuchsia aqua white )
    unless validColors.include?(colName)
      error('bad_color_name',
            "#{colName} is not a supported color. Use one of " +
            "#{validColors.join(', ')} or #RGB where 'R', 'G' and 'B' " +
            "are one or two digit hexadecimal numbers.")
    end
  end
  [ type, colName ]
end
functionEnd(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 205
def functionEnd(type, match)
  self.mode = @funcLastMode
  @funcLastMode = nil
  [ type, match ]
end
functionStart(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 197
def functionStart(type, match)
  # When restoring :bol or :bop mode, we need to switch to :inline mode.
  @funcLastMode = (@scannerMode == :bop || @scannerMode == :bol) ?
                  :inline : @scannerMode
  self.mode = :func
  [ type, match ]
end
hrefEnd(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 240
def hrefEnd(type, match)
  self.mode = @hrefLastMode
  @hrefLastMode = nil
  [ type, match ]
end
hrefStart(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 232
def hrefStart(type, match)
  # When restoring :bol or :bop mode, we need to switch to :inline mode.
  @hrefLastMode = (@scannerMode == :bop || @scannerMode == :bol) ?
                  :inline : @scannerMode
  self.mode = :href
  [ type, match ]
end
htmlEnd(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 182
def htmlEnd(type, match)
  self.mode = :inline
  [ type, match[0..-8] ]
end
htmlStart(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 177
def htmlStart(type, match)
  self.mode = :html
  [ type, match ]
end
inlineMode(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 124
def inlineMode(type, match)
  self.mode = :inline
  [ type, match ]
end
linebreak(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 119
def linebreak(type, match)
  self.mode = :bop
  [ type, match ]
end
nowikiEnd(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 192
def nowikiEnd(type, match)
  self.mode = :inline
  [ type, match ]
end
nowikiStart(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 187
def nowikiStart(type, match)
  self.mode = :nowiki
  [ type, match ]
end
number(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 143
def number(type, match)
  self.mode = :inline
  [ "NUMBER#{match.count('#')}".intern, match ]
end
pre(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 211
def pre(type, match)
  [ type, match[1..-1] ]
end
query(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 227
def query(type, match)
  # Remove <- and ->.
  [ type, match[2..-3] ]
end
quotes(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 171
def quotes(type, match)
  self.mode = :inline
  types = [ nil, nil, :ITALIC, :BOLD , :CODE, :BOLDITALIC ]
  [ types[match.length], match ]
end
refEnd(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 251
def refEnd(type, match)
  self.mode = :inline
  [ type, match ]
end
refStart(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 246
def refStart(type, match)
  self.mode = :ref
  [ type, match ]
end
space(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 109
def space(type, match)
  if match.index("\n")
    # If the match contains a linebreak we switch to :bol mode.
    self.mode = :bol
    # And return an empty string.
    match = ''
  end
  [ type, match ]
end
sqString(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 221
def sqString(type, match)
  # Remove first and last character and remove backslashes from quoted
  # single quotes.
  [ type, match[1..-2].gsub(/\\'/, "'") ]
end
titleEnd(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 134
def titleEnd(type, match)
  [ "TITLE#{match.length - 1}END".intern, match ]
end
titleStart(type, match) click to toggle source
# File lib/taskjuggler/RichText/Scanner.rb, line 129
def titleStart(type, match)
  self.mode = :inline
  [ "TITLE#{match.length - 1}".intern, match ]
end