docs: function factory(type) {
    return tokenize
  
    // Tokenizer for a bound `type`.
    function tokenize(value, location) {
      var self = this
      var offset = self.offset
      var tokens = []
      var methods = self[type + 'Methods']
      var tokenizers = self[type + 'Tokenizers']
      var line = location.line
      var column = location.column
      var index
      var length
      var method
      var name
      var matched
      var valueLength
  
      // Trim white space only lines.
      if (!value) {
        return tokens
      }
  
      // Expose on `eat`.
      eat.now = now
      eat.file = self.file
  
      // Sync initial offset.
      updatePosition('')
  
      // Iterate over `value`, and iterate over all tokenizers.  When one eats
      // something, re-iterate with the remaining value.  If no tokenizer eats,
      // something failed (should not happen) and an exception is thrown.
      while (value) {
        index = -1
        length = methods.length
        matched = false
  
        while (++index < length) {
          name = methods[index]
          method = tokenizers[name]
  
          if (
            method &&
            /* istanbul ignore next */ (!method.onlyAtStart || self.atStart) &&
            (!method.notInList || !self.inList) &&
            (!method.notInBlock || !self.inBlock) &&
            (!method.notInLink || !self.inLink)
          ) {
            valueLength = value.length
  
            method.apply(self, [eat, value])
  
            matched = valueLength !== value.length
  
            if (matched) {
              break
            }
          }
        }
  
        /* istanbul ignore if */
        if (!matched) {
          self.file.fail(new Error('Infinite loop'), eat.now())
        }
      }
  
      self.eof = now()
  
      return tokens
  
      // Update line, column, and offset based on `value`.
      function updatePosition(subvalue) {
        var lastIndex = -1
        var index = subvalue.indexOf('\n')
  
        while (index !== -1) {
          line++
          lastIndex = index
          index = subvalue.indexOf('\n', index + 1)
        }
  
        if (lastIndex === -1) {
          column += subvalue.length
        } else {
          column = subvalue.length - lastIndex
        }
  
        if (line in offset) {
          if (lastIndex !== -1) {
            column += offset[line]
          } else if (column <= offset[line]) {
            column = offset[line] + 1
          }
        }
      }
  
      // Get offset.  Called before the first character is eaten to retrieve the
      // range’s offsets.
      function getOffset() {
        var indentation = []
        var pos = line + 1
  
        // Done.  Called when the last character is eaten to retrieve the range’s
        // offsets.
        return function() {
          var last = line + 1
  
          while (pos < last) {
            indentation.push((offset[pos] || 0) + 1)
  
            pos++
          }
  
          return indentation
        }
      }
  
      // Get the current position.
      function now() {
        var pos = {line: line, column: column}
  
        pos.offset = self.toOffset(pos)
  
        return pos
      }
  
      // Store position information for a node.
      function Position(start) {
        this.start = start
        this.end = now()
      }
  
      // Throw when a value is incorrectly eaten.  This shouldn’t happen but will
      // throw on new, incorrect rules.
      function validateEat(subvalue) {
        /* istanbul ignore if */
        if (value.substring(0, subvalue.length) !== subvalue) {
          // Capture stack-trace.
          self.file.fail(
            new Error(
              'Incorrectly eaten value: please report this warning on https://git.io/vg5Ft'
            ),
            now()
          )
        }
      }
  
      // Mark position and patch `node.position`.
      function position() {
        var before = now()
  
        return update
  
        // Add the position to a node.
        function update(node, indent) {
          var prev = node.position
          var start = prev ? prev.start : before
          var combined = []
          var n = prev && prev.end.line
          var l = before.line
  
          node.position = new Position(start)
  
          // If there was already a `position`, this node was merged.  Fixing
          // `start` wasn’t hard, but the indent is different.  Especially
          // because some information, the indent between `n` and `l` wasn’t
          // tracked.  Luckily, that space is (should be?) empty, so we can
          // safely check for it now.
          if (prev && indent && prev.indent) {
            combined = prev.indent
  
            if (n < l) {
              while (++n < l) {
                combined.push((offset[n] || 0) + 1)
              }
  
              combined.push(before.column)
            }
  
            indent = combined.concat(indent)
          }
  
          node.position.indent = indent || []
  
          return node
        }
      }
  
      // Add `node` to `parent`s children or to `tokens`.  Performs merges where
      // possible.
      function add(node, parent) {
        var children = parent ? parent.children : tokens
        var prev = children[children.length - 1]
        var fn
  
        if (
          prev &&
          node.type === prev.type &&
          (node.type === 'text' || node.type === 'blockquote') &&
          mergeable(prev) &&
          mergeable(node)
        ) {
          fn = node.type === 'text' ? mergeText : mergeBlockquote
          node = fn.call(self, prev, node)
        }
  
        if (node !== prev) {
          children.push(node)
        }
  
        if (self.atStart && tokens.length !== 0) {
          self.exitStart()
        }
  
        return node
      }
  
      // Remove `subvalue` from `value`.  `subvalue` must be at the start of
      // `value`.
      function eat(subvalue) {
        var indent = getOffset()
        var pos = position()
        var current = now()
  
        validateEat(subvalue)
  
        apply.reset = reset
        reset.test = test
        apply.test = test
  
        value = value.substring(subvalue.length)
  
        updatePosition(subvalue)
  
        indent = indent()
  
        return apply
  
        // Add the given arguments, add `position` to the returned node, and
        // return the node.
        function apply(node, parent) {
          return pos(add(pos(node), parent), indent)
        }
  
        // Functions just like apply, but resets the content: the line and
        // column are reversed, and the eaten value is re-added.   This is
        // useful for nodes with a single type of content, such as lists and
        // tables.  See `apply` above for what parameters are expected.
        function reset() {
          var node = apply.apply(null, arguments)
  
          line = current.line
          column = current.column
          value = subvalue + value
  
          return node
        }
  
        // Test the position, after eating, and reverse to a not-eaten state.
        function test() {
          var result = pos({})
  
          line = current.line
          column = current.column
          value = subvalue + value
  
          return result.position
        }
      }
    }
  }