 
       i += consumed
 
+      return {@tokens, index: i} if opts.untilBalanced and @ends.length is 0
+
     @closeIndentation()
+    # TODO: Make this error point to the opener!
     @error "missing #{tag}" if tag = @ends.pop()
     return @tokens if opts.rewrite is off
     (new Rewriter).rewrite @tokens

    @tag() in ['\\', '.', '?.', '?::', 'UNARY', 'MATH', 'UNARY_MATH', '+', '-', 'YIELD',
               '**', 'SHIFT', 'RELATION', 'COMPARE', 'LOGIC', 'THROW', 'EXTENDS']

  # Remove newlines from beginning and (non escaped) from end of string literals.
  removeNewlines: (str) ->
    str.replace(/^\s*\n\s*/, '')
       .replace(/([^\\]|\\\\)\s*\n\s*$/, '$1')

  # Converts newlines for string literals.
  escapeLines: (str, heredoc) ->
    # Ignore escaped backslashes and remove escaped newlines
    str = str.replace /\\[^\S\n]*(\n|\\)\s*/g, (escaped, character) ->
      if character is '\n' then '' else escaped
    if heredoc
      str.replace MULTILINER, '\\n'
    else
      str.replace /\s*\n\s*/g, ' '

  # Constructs a string token by escaping quotes and newlines.
  makeString: (body, quote, heredoc) ->
    return quote + quote unless body
    # Ignore escaped backslashes and unescape quotes
    body = body.replace /// \\( #{quote} | \\ ) ///g, (match, contents) ->
      if contents is quote then contents else match
    body = body.replace /// #{quote} ///g, '\\$&'
    quote + @escapeLines(body, heredoc) + quote

  # Throws a compiler error on the current position.
  error: (message, offset = 0) ->

  # Each tokenizing method is responsible for returning the number of characters
  # it has consumed.
  #
  # Before returning the token stream, run it through the [Rewriter](rewriter.html).
  tokenize: (code, opts = {}) ->
    @literate   = opts.literate  # Are we lexing literate CoffeeScript?
    @indent     = 0              # The current indentation level.
