Merge: markdown: merge MDProcessor and MDEmitter
authorJean Privat <jean@pryen.org>
Tue, 17 Oct 2017 20:09:25 +0000 (16:09 -0400)
committerJean Privat <jean@pryen.org>
Tue, 17 Oct 2017 20:09:25 +0000 (16:09 -0400)
The emitter was unecessary.

Also done some cleaning.

Pull-Request: #2563

1  2 
lib/markdown/markdown.nit

@@@ -30,9 -30,6 +30,6 @@@ import templat
  # SEE: `String::md_to_html` for a shortcut.
  class MarkdownProcessor
  
-       # `MarkdownEmitter` used for ouput.
-       var emitter: MarkdownEmitter is noinit, protected writable
        # Work in extended mode (default).
        #
        # Behavior changes when using extended mode:
        # Default = `false`
        var no_location = false is writable
  
-       init do self.emitter = new MarkdownEmitter(self)
        # Process the mardown `input` string and return the processed output.
        fun process(input: String): Writable do
                # init processor
                parent.remove_surrounding_empty_lines
                recurse(parent, false)
                # output processed text
-               return emitter.emit(parent.kind)
+               return emit(parent.kind)
        end
  
        # Split `input` string into `MDLines` and create a parent `MDBlock` with it.
                                                pos = md.read_until(comment, pos, c)
                                        end
                                end
-                               if not comment.is_empty then last_link_ref.title = comment.write_to_string
+                               var last_link_ref = self.last_link_ref
+                               if not comment.is_empty and last_link_ref != null then
+                                       last_link_ref.title = comment.write_to_string
+                               end
                        end
                        if comment.is_empty then return false
                        return true
                return -1
        end
  
-       # Location used for next parsed token.
-       #
-       # This location can be changed by the emitter to adjust with `\n` found
-       # in the input.
-       private fun current_loc: MDLocation do return emitter.current_loc
- end
- # Emit output corresponding to blocks content.
- #
- # Blocks are created by a previous pass in `MarkdownProcessor`.
- # The emitter use a `Decorator` to select the output format.
- class MarkdownEmitter
-       # Kind of processor used for parsing.
-       type PROCESSOR: MarkdownProcessor
-       # Processor containing link refs.
-       var processor: PROCESSOR
        # Kind of decorator used for decoration.
        type DECORATOR: Decorator
  
        end
  
        # Create a new `MarkdownEmitter` using a custom `decorator`.
-       init with_decorator(processor: PROCESSOR, decorator: DECORATOR) do
-               init processor
+       init with_decorator(decorator: DECORATOR) do
                self.decorator = decorator
        end
  
                                current_loc.line_start += 1
                                current_loc.column_start = -current_pos
                        end
-                       var mt = processor.token_at(text, current_pos)
+                       var mt = token_at(text, current_pos)
                        if (token != null and not token isa TokenNone) and
                        (mt.is_same_type(token) or
                        (token isa TokenEmStar and mt isa TokenStrongStar) or
@@@ -659,72 -637,72 +637,72 @@@ en
  # Default decorator used is `HTMLDecorator`.
  interface Decorator
  
-       # Kind of emitter used for decoration.
-       type EMITTER: MarkdownEmitter
+       # Kind of processor used
+       type PROCESSOR: MarkdownProcessor
  
        # Render a single plain char.
        #
        # Redefine this method to add special escaping for plain text.
-       fun add_char(v: EMITTER, c: Char) do v.addc c
+       fun add_char(v: PROCESSOR, c: Char) do v.addc c
  
        # Render a ruler block.
-       fun add_ruler(v: EMITTER, block: BlockRuler) is abstract
+       fun add_ruler(v: PROCESSOR, block: BlockRuler) is abstract
  
        # Render a headline block with corresponding level.
-       fun add_headline(v: EMITTER, block: BlockHeadline) is abstract
+       fun add_headline(v: PROCESSOR, block: BlockHeadline) is abstract
  
        # Render a paragraph block.
-       fun add_paragraph(v: EMITTER, block: BlockParagraph) is abstract
+       fun add_paragraph(v: PROCESSOR, block: BlockParagraph) is abstract
  
        # Render a code or fence block.
-       fun add_code(v: EMITTER, block: BlockCode) is abstract
+       fun add_code(v: PROCESSOR, block: BlockCode) is abstract
  
        # Render a blockquote.
-       fun add_blockquote(v: EMITTER, block: BlockQuote) is abstract
+       fun add_blockquote(v: PROCESSOR, block: BlockQuote) is abstract
  
        # Render an unordered list.
-       fun add_unorderedlist(v: EMITTER, block: BlockUnorderedList) is abstract
+       fun add_unorderedlist(v: PROCESSOR, block: BlockUnorderedList) is abstract
  
        # Render an ordered list.
-       fun add_orderedlist(v: EMITTER, block: BlockOrderedList) is abstract
+       fun add_orderedlist(v: PROCESSOR, block: BlockOrderedList) is abstract
  
        # Render a list item.
-       fun add_listitem(v: EMITTER, block: BlockListItem) is abstract
+       fun add_listitem(v: PROCESSOR, block: BlockListItem) is abstract
  
        # Render an emphasis text.
-       fun add_em(v: EMITTER, text: Text) is abstract
+       fun add_em(v: PROCESSOR, text: Text) is abstract
  
        # Render a strong text.
-       fun add_strong(v: EMITTER, text: Text) is abstract
+       fun add_strong(v: PROCESSOR, text: Text) is abstract
  
        # Render a strike text.
        #
        # Extended mode only (see `MarkdownProcessor::ext_mode`)
-       fun add_strike(v: EMITTER, text: Text) is abstract
+       fun add_strike(v: PROCESSOR, text: Text) is abstract
  
        # Render a link.
-       fun add_link(v: EMITTER, link: Text, name: Text, comment: nullable Text) is abstract
+       fun add_link(v: PROCESSOR, link: Text, name: Text, comment: nullable Text) is abstract
  
        # Render an image.
-       fun add_image(v: EMITTER, link: Text, name: Text, comment: nullable Text) is abstract
+       fun add_image(v: PROCESSOR, link: Text, name: Text, comment: nullable Text) is abstract
  
        # Render an abbreviation.
-       fun add_abbr(v: EMITTER, name: Text, comment: Text) is abstract
+       fun add_abbr(v: PROCESSOR, name: Text, comment: Text) is abstract
  
        # Render a code span reading from a buffer.
-       fun add_span_code(v: EMITTER, buffer: Text, from, to: Int) is abstract
+       fun add_span_code(v: PROCESSOR, buffer: Text, from, to: Int) is abstract
  
        # Render a text and escape it.
-       fun append_value(v: EMITTER, value: Text) is abstract
+       fun append_value(v: PROCESSOR, value: Text) is abstract
  
        # Render code text from buffer and escape it.
-       fun append_code(v: EMITTER, buffer: Text, from, to: Int) is abstract
+       fun append_code(v: PROCESSOR, buffer: Text, from, to: Int) is abstract
  
        # Render a character escape.
-       fun escape_char(v: EMITTER, char: Char) is abstract
+       fun escape_char(v: PROCESSOR, char: Char) is abstract
  
        # Render a line break
-       fun add_line_break(v: EMITTER) is abstract
+       fun add_line_break(v: PROCESSOR) is abstract
  
        # Generate a new html valid id from a `String`.
        fun strip_id(txt: String): String is abstract
@@@ -757,7 -735,9 +735,9 @@@ class HTMLDecorato
  
        redef fun add_headline(v, block) do
                # save headline
-               var txt = block.block.first_line.value
+               var line = block.block.first_line
+               if line == null then return
+               var txt = line.value
                var id = strip_id(txt)
                var lvl = block.depth
                headlines[id] = new HeadLine(id, txt, lvl)
@@@ -1021,8 -1001,8 +1001,8 @@@ class MDBloc
        fun split(line: MDLine): MDBlock do
                # location for new block
                var new_loc = new MDLocation(
-                       first_line.location.line_start,
-                       first_line.location.column_start,
+                       first_line.as(not null).location.line_start,
+                       first_line.as(not null).location.column_start,
                        line.location.line_end,
                        line.location.column_end)
                # create block
                if first_line == null then
                        last_line = null
                else
-                       first_line.prev = null
+                       first_line.as(not null).prev = null
                        # update current block loc
-                       location.line_start = first_line.location.line_start
-                       location.column_start = first_line.location.column_start
+                       location.line_start = first_line.as(not null).location.line_start
+                       location.column_start = first_line.as(not null).location.column_start
                end
                if first_block == null then
                        first_block = block
                        last_block = block
                else
-                       last_block.next = block
+                       last_block.as(not null).next = block
                        last_block = block
                end
                return block
                        first_line = line
                        last_line = line
                else
-                       last_line.next_empty = line.is_empty
-                       line.prev_empty = last_line.is_empty
+                       last_line.as(not null).next_empty = line.is_empty
+                       line.prev_empty = last_line.as(not null).is_empty
                        line.prev = last_line
-                       last_line.next = line
+                       last_line.as(not null).next = line
                        last_line = line
                end
        end
                if line.prev == null then
                        first_line = line.next
                else
-                       line.prev.next = line.next
+                       line.prev.as(not null).next = line.next
                end
                if line.next == null then
                        last_line = line.prev
                else
-                       line.next.prev = line.prev
+                       line.next.as(not null).prev = line.prev
                end
                line.prev = null
                line.next = null
                        text.append "\n"
                        line = line.next
                end
 +              var block = first_block
 +              while block != null do
 +                      text.append block.text
 +                      text.append "\n"
 +                      block = block.next
 +              end
                return text.write_to_string
        end
  end
@@@ -1158,10 -1132,10 +1138,10 @@@ abstract class Bloc
        var block: MDBlock
  
        # Output `self` using `v.decorator`.
-       fun emit(v: MarkdownEmitter) do v.emit_in(self)
+       fun emit(v: MarkdownProcessor) do v.emit_in(self)
  
        # Emit the containts of `self`, lines or blocks.
-       fun emit_in(v: MarkdownEmitter) do
+       fun emit_in(v: MarkdownProcessor) do
                block.remove_surrounding_empty_lines
                if block.has_lines then
                        emit_lines(v)
        end
  
        # Emit lines contained in `block`.
-       fun emit_lines(v: MarkdownEmitter) do
+       fun emit_lines(v: MarkdownProcessor) do
                var tpl = v.push_buffer
                var line = block.first_line
                while line != null do
        end
  
        # Emit sub-blocks contained in `block`.
-       fun emit_blocks(v: MarkdownEmitter) do
+       fun emit_blocks(v: MarkdownProcessor) do
                var block = self.block.first_block
                while block != null do
                        v.push_loc(block.location)
@@@ -1313,6 -1287,7 +1293,7 @@@ class BlockHeadlin
                if depth > 0 then return
                var level = 0
                var line = block.first_line
+               if line == null then return
                if line.is_empty then return
                var start = line.leading
                while start < line.value.length and line.value[start] == '#' do
@@@ -1352,6 -1327,7 +1333,7 @@@ abstract class BlockLis
        # Split list block into list items sub-blocks.
        private fun init_block(v: MarkdownProcessor) do
                var line = block.first_line
+               if line == null then return
                line = line.next
                while line != null do
                        var t = v.line_kind(line)
@@@ -1484,8 -1460,8 +1466,8 @@@ class MDLin
                leading = 0
                trailing = 0
                is_empty = true
-               if prev != null then prev.next_empty = true
-               if next != null then next.prev_empty = true
+               if prev != null then prev.as(not null).next_empty = true
+               if next != null then next.as(not null).prev_empty = true
        end
  
        # Number or leading spaces on this line.
        # Used by `check_html`.
        private fun read_xml_comment(first_line: MDLine, start: Int): Int do
                var line: nullable MDLine = first_line
-               if start + 3 < line.value.length then
-                       if line.value[2] == '-' and line.value[3] == '-' then
+               if start + 3 < line.as(not null).value.length then
+                       if line.as(not null).value[2] == '-' and line.as(not null).value[3] == '-' then
                                var pos = start + 4
                                while line != null do
                                        while pos < line.value.length and line.value[pos] != '-' do
@@@ -1661,7 -1637,7 +1643,7 @@@ class LineEmpt
        super Line
  
        redef fun process(v) do
-               v.current_line = v.current_line.next
+               v.current_line = v.current_line.as(not null).next
        end
  end
  
@@@ -1673,7 -1649,7 +1655,7 @@@ class LineOthe
        redef fun process(v) do
                var line = v.current_line
                # go to block end
-               var was_empty = line.prev_empty
+               var was_empty = line.as(not null).prev_empty
                while line != null and not line.is_empty do
                        var t = v.line_kind(line)
                        if (v.in_list or v.ext_mode) and t isa LineList then
                        line = line.next
                end
                # build block
+               var current_block = v.current_block.as(not null)
                if line != null and not line.is_empty then
-                       var block = v.current_block.split(line.prev.as(not null))
+                       var block = current_block.split(line.prev.as(not null))
                        if v.in_list and not was_empty then
                                block.kind = new BlockNone(block)
                        else
                                block.kind = new BlockParagraph(block)
                        end
-                       v.current_block.remove_leading_empty_lines
+                       current_block.remove_leading_empty_lines
                else
                        var block: MDBlock
                        if line != null then
-                               block = v.current_block.split(line)
+                               block = current_block.split(line)
                        else
-                               block = v.current_block.split(v.current_block.last_line.as(not null))
+                               block = current_block.split(current_block.last_line.as(not null))
                        end
                        if v.in_list and (line == null or not line.is_empty) and not was_empty then
                                block.kind = new BlockNone(block)
                        else
                                block.kind = new BlockParagraph(block)
                        end
-                       v.current_block.remove_leading_empty_lines
+                       current_block.remove_leading_empty_lines
                end
-               v.current_line = v.current_block.first_line
+               v.current_line = current_block.first_line
        end
  end
  
@@@ -1726,15 -1703,16 +1709,16 @@@ class LineCod
                        line = line.next
                end
                # split at block end line
+               var current_block = v.current_block.as(not null)
                var block: MDBlock
                if line != null then
-                       block = v.current_block.split(line.prev.as(not null))
+                       block = current_block.split(line.prev.as(not null))
                else
-                       block = v.current_block.split(v.current_block.last_line.as(not null))
+                       block = current_block.split(current_block.last_line.as(not null))
                end
                block.kind = new BlockCode(block)
                block.remove_surrounding_empty_lines
-               v.current_line = v.current_block.first_line
+               v.current_line = current_block.first_line
        end
  end
  
@@@ -1744,12 -1722,14 +1728,14 @@@ class LineXM
  
        redef fun process(v) do
                var line = v.current_line
+               if line == null then return
+               var current_block = v.current_block.as(not null)
                var prev = line.prev
-               if prev != null then v.current_block.split(prev)
-               var block = v.current_block.split(line.xml_end_line.as(not null))
+               if prev != null then current_block.split(prev)
+               var block = current_block.split(line.xml_end_line.as(not null))
                block.kind = new BlockXML(block)
-               v.current_block.remove_leading_empty_lines
-               v.current_line = v.current_block.first_line
+               current_block.remove_leading_empty_lines
+               v.current_line = current_block.first_line
        end
  end
  
@@@ -1759,6 -1739,7 +1745,7 @@@ class LineBlockquot
  
        redef fun process(v) do
                var line = v.current_line
+               var current_block = v.current_block.as(not null)
                # go to bquote end
                while line != null do
                        if not line.is_empty and (line.prev_empty and
                # build sub block
                var block: MDBlock
                if line != null then
-                       block = v.current_block.split(line.prev.as(not null))
+                       block = current_block.split(line.prev.as(not null))
                else
-                       block = v.current_block.split(v.current_block.last_line.as(not null))
+                       block = current_block.split(current_block.last_line.as(not null))
                end
                var kind = new BlockQuote(block)
                block.kind = kind
                kind.remove_block_quote_prefix(block)
                v.current_line = line
                v.recurse(block, false)
-               v.current_line = v.current_block.first_line
+               v.current_line = current_block.first_line
        end
  end
  
@@@ -1789,11 -1770,13 +1776,13 @@@ class LineH
  
        redef fun process(v) do
                var line = v.current_line
-               if line.prev != null then v.current_block.split(line.prev.as(not null))
-               var block = v.current_block.split(line.as(not null))
+               if line == null then return
+               var current_block = v.current_block.as(not null)
+               if line.prev != null then current_block.split(line.prev.as(not null))
+               var block = current_block.split(line)
                block.kind = new BlockRuler(block)
-               v.current_block.remove_leading_empty_lines
-               v.current_line = v.current_block.first_line
+               current_block.remove_leading_empty_lines
+               v.current_line = current_block.first_line
        end
  end
  
@@@ -1803,7 -1786,8 +1792,8 @@@ class LineFenc
  
        redef fun process(v) do
                # go to fence end
-               var line = v.current_line.next
+               var line = v.current_line.as(not null).next
+               var current_block = v.current_block.as(not null)
                while line != null do
                        if v.line_kind(line) isa LineFence then break
                        line = line.next
                # build fence block
                var block: MDBlock
                if line != null then
-                       block = v.current_block.split(line.prev.as(not null))
+                       block = current_block.split(line.prev.as(not null))
                else
-                       block = v.current_block.split(v.current_block.last_line.as(not null))
+                       block = current_block.split(current_block.last_line.as(not null))
                end
                block.remove_surrounding_empty_lines
-               var meta = block.first_line.value.meta_from_fence
+               var meta = block.first_line.as(not null).value.meta_from_fence
                block.kind = new BlockFence(block, meta)
-               block.first_line.clear
+               block.first_line.as(not null).clear
                var last = block.last_line
                if last != null and v.line_kind(last) isa LineFence then
-                       block.last_line.clear
+                       block.last_line.as(not null).clear
                end
                block.remove_surrounding_empty_lines
                v.current_line = line
@@@ -1837,14 -1821,16 +1827,16 @@@ class LineHeadlin
  
        redef fun process(v) do
                var line = v.current_line
+               if line == null then return
+               var current_block = v.current_block.as(not null)
                var lprev = line.prev
-               if lprev != null then v.current_block.split(lprev)
-               var block = v.current_block.split(line.as(not null))
+               if lprev != null then current_block.split(lprev)
+               var block = current_block.split(line)
                var kind = new BlockHeadline(block)
                block.kind = kind
                kind.transform_headline(block)
-               v.current_block.remove_leading_empty_lines
-               v.current_line = v.current_block.first_line
+               current_block.remove_leading_empty_lines
+               v.current_line = current_block.first_line
        end
  end
  
@@@ -1854,16 -1840,18 +1846,18 @@@ class LineHeadline
  
        redef fun process(v) do
                var line = v.current_line
+               if line == null then return
+               var current_block = v.current_block.as(not null)
                var lprev = line.prev
-               if lprev != null then v.current_block.split(lprev)
-               line.next.clear
-               var block = v.current_block.split(line.as(not null))
+               if lprev != null then current_block.split(lprev)
+               line.next.as(not null).clear
+               var block = current_block.split(line)
                var kind = new BlockHeadline(block)
                kind.depth = 1
                kind.transform_headline(block)
                block.kind = kind
-               v.current_block.remove_leading_empty_lines
-               v.current_line = v.current_block.first_line
+               current_block.remove_leading_empty_lines
+               v.current_line = current_block.first_line
        end
  end
  
@@@ -1873,16 -1861,18 +1867,18 @@@ class LineHeadline
  
        redef fun process(v) do
                var line = v.current_line
+               if line == null then return
+               var current_block = v.current_block.as(not null)
                var lprev = line.prev
-               if lprev != null then v.current_block.split(lprev)
-               line.next.clear
-               var block = v.current_block.split(line.as(not null))
+               if lprev != null then current_block.split(lprev)
+               line.next.as(not null).clear
+               var block = current_block.split(line)
                var kind = new BlockHeadline(block)
                kind.depth = 2
                kind.transform_headline(block)
                block.kind = kind
-               v.current_block.remove_leading_empty_lines
-               v.current_line = v.current_block.first_line
+               current_block.remove_leading_empty_lines
+               v.current_line = current_block.first_line
        end
  end
  
@@@ -1901,19 -1891,20 +1897,20 @@@ abstract class LineLis
                        line = line.next
                end
                # build list block
+               var current_block = v.current_block.as(not null)
                var list: MDBlock
                if line != null then
-                       list = v.current_block.split(line.prev.as(not null))
+                       list = current_block.split(line.prev.as(not null))
                else
-                       list = v.current_block.split(v.current_block.last_line.as(not null))
+                       list = current_block.split(current_block.last_line.as(not null))
                end
                var kind = block_kind(list)
                list.kind = kind
-               list.first_line.prev_empty = false
-               list.last_line.next_empty = false
+               list.first_line.as(not null).prev_empty = false
+               list.last_line.as(not null).next_empty = false
                list.remove_surrounding_empty_lines
-               list.first_line.prev_empty = false
-               list.last_line.next_empty = false
+               list.first_line.as(not null).prev_empty = false
+               list.last_line.as(not null).next_empty = false
                kind.init_block(v)
                var block = list.first_block
                while block != null do
@@@ -1968,7 -1959,7 +1965,7 @@@ abstract class Toke
        var char: Char
  
        # Output that token using `MarkdownEmitter::decorator`.
-       fun emit(v: MarkdownEmitter) do v.decorator.add_char(v, char)
+       fun emit(v: MarkdownProcessor) do v.decorator.add_char(v, char)
  end
  
  # A token without a specific meaning.
@@@ -2036,14 -2027,15 +2033,15 @@@ abstract class TokenCod
        super Token
  
        redef fun emit(v) do
+               var current_text = v.current_text.as(not null)
                var a = pos + next_pos + 1
-               var b = v.processor.find_token(v.current_text.as(not null), a, self)
+               var b = v.find_token(current_text, a, self)
                if b > 0 then
                        v.current_pos = b + next_pos
-                       while a < b and v.current_text[a] == ' ' do a += 1
+                       while a < b and current_text[a] == ' ' do a += 1
                        if a < b then
-                               while v.current_text[b - 1] == ' ' do b -= 1
-                               v.decorator.add_span_code(v, v.current_text.as(not null), a, b)
+                               while current_text[b - 1] == ' ' do b -= 1
+                               v.decorator.add_span_code(v, current_text, a, b)
                        end
                else
                        v.addc char
@@@ -2096,11 -2088,12 +2094,12 @@@ abstract class TokenLinkOrImag
        end
  
        # Emit the hyperlink as link or image.
-       private fun emit_hyper(v: MarkdownEmitter) is abstract
+       private fun emit_hyper(v: MarkdownProcessor) is abstract
  
        # Check if the link is a valid link.
-       private fun check_link(v: MarkdownEmitter, out: FlatBuffer, start: Int, token: Token): Int do
+       private fun check_link(v: MarkdownProcessor, out: FlatBuffer, start: Int, token: Token): Int do
                var md = v.current_text
+               if md == null then return -1
                var pos
                if token isa TokenLink then
                        pos = start + 1
                pos += 1
                pos = md.skip_spaces(pos)
                if pos < start then
-                       var tid = name.write_to_string.to_lower
-                       if v.processor.link_refs.has_key(tid) then
-                               var lr = v.processor.link_refs[tid]
+                       var tid = name.as(not null).write_to_string.to_lower
+                       if v.link_refs.has_key(tid) then
+                               var lr = v.link_refs[tid]
                                is_abbrev = lr.is_abbrev
                                link = lr.link
                                comment = lr.title
                        else
                                id = name
                        end
-                       var tid = id.write_to_string.to_lower
-                       if v.processor.link_refs.has_key(tid) then
-                               var lr = v.processor.link_refs[tid]
+                       var tid = id.as(not null).write_to_string.to_lower
+                       if v.link_refs.has_key(tid) then
+                               var lr = v.link_refs[tid]
                                link = lr.link
                                comment = lr.title
                        end
                else
-                       var tid = name.write_to_string.replace("\n", " ").to_lower
-                       if v.processor.link_refs.has_key(tid) then
-                               var lr = v.processor.link_refs[tid]
+                       var tid = name.as(not null).write_to_string.replace("\n", " ").to_lower
+                       if v.link_refs.has_key(tid) then
+                               var lr = v.link_refs[tid]
                                link = lr.link
                                comment = lr.title
                                pos = old_pos
@@@ -2226,7 -2219,7 +2225,7 @@@ class TokenHTM
  
        # Is the HTML valid?
        # Also take care of link and mailto shortcuts.
-       private fun check_html(v: MarkdownEmitter, out: FlatBuffer, md: Text, start: Int): Int do
+       private fun check_html(v: MarkdownProcessor, out: FlatBuffer, md: Text, start: Int): Int do
                # check for auto links
                var tmp = new FlatBuffer
                var pos = md.read_until(tmp, start + 1, ':', ' ', '>', '\n')
@@@ -2307,7 -2300,7 +2306,7 @@@ class TokenEscap
  
        redef fun emit(v) do
                v.current_pos += 1
-               v.addc v.current_text[v.current_pos]
+               v.addc v.current_text.as(not null)[v.current_pos]
        end
  end