text: 'use strict'
  
  var trim = require('trim')
  var repeat = require('repeat-string')
  var decimal = require('is-decimal')
  var getIndent = require('../util/get-indentation')
  var removeIndent = require('../util/remove-indentation')
  var interrupt = require('../util/interrupt')
  
  module.exports = list
  
  var asterisk = '*'
  var underscore = '_'
  var plusSign = '+'
  var dash = '-'
  var dot = '.'
  var space = ' '
  var lineFeed = '\n'
  var tab = '\t'
  var rightParenthesis = ')'
  var lowercaseX = 'x'
  
  var tabSize = 4
  var looseListItemExpression = /\n\n(?!\s*$)/
  var taskItemExpression = /^\[([ X\tx])][ \t]/
  var bulletExpression = /^([ \t]*)([*+-]|\d+[.)])( {1,4}(?! )| |\t|$|(?=\n))([^\n]*)/
  var pedanticBulletExpression = /^([ \t]*)([*+-]|\d+[.)])([ \t]+)/
  var initialIndentExpression = /^( {1,4}|\t)?/gm
  
  function list(eat, value, silent) {
    var self = this
    var commonmark = self.options.commonmark
    var pedantic = self.options.pedantic
    var tokenizers = self.blockTokenizers
    var interuptors = self.interruptList
    var index = 0
    var length = value.length
    var start = null
    var size = 0
    var queue
    var ordered
    var character
    var marker
    var nextIndex
    var startIndex
    var prefixed
    var currentMarker
    var content
    var line
    var previousEmpty
    var empty
    var items
    var allLines
    var emptyLines
    var item
    var enterTop
    var exitBlockquote
    var spread = false
    var node
    var now
    var end
    var indented
  
    while (index < length) {
      character = value.charAt(index)
  
      if (character === tab) {
        size += tabSize - (size % tabSize)
      } else if (character === space) {
        size++
      } else {
        break
      }
  
      index++
    }
  
    if (size >= tabSize) {
      return
    }
  
    character = value.charAt(index)
  
    if (character === asterisk || character === plusSign || character === dash) {
      marker = character
      ordered = false
    } else {
      ordered = true
      queue = ''
  
      while (index < length) {
        character = value.charAt(index)
  
        if (!decimal(character)) {
          break
        }
  
        queue += character
        index++
      }
  
      character = value.charAt(index)
  
      if (
        !queue ||
        !(character === dot || (commonmark && character === rightParenthesis))
      ) {
        return
      }
  
      start = parseInt(queue, 10)
      marker = character
    }
  
    character = value.charAt(++index)
  
    if (
      character !== space &&
      character !== tab &&
      (pedantic || (character !== lineFeed && character !== ''))
    ) {
      return
    }
  
    if (silent) {
      return true
    }
  
    index = 0
    items = []
    allLines = []
    emptyLines = []
  
    while (index < length) {
      nextIndex = value.indexOf(lineFeed, index)
      startIndex = index
      prefixed = false
      indented = false
  
      if (nextIndex === -1) {
        nextIndex = length
      }
  
      end = index + tabSize
      size = 0
  
      while (index < length) {
        character = value.charAt(index)
  
        if (character === tab) {
          size += tabSize - (size % tabSize)
        } else if (character === space) {
          size++
        } else {
          break
        }
  
        index++
      }
  
      if (size >= tabSize) {
        indented = true
      }
  
      if (item && size >= item.indent) {
        indented = true
      }
  
      character = value.charAt(index)
      currentMarker = null
  
      if (!indented) {
        if (
          character === asterisk ||
          character === plusSign ||
          character === dash
        ) {
          currentMarker = character
          index++
          size++
        } else {
          queue = ''
  
          while (index < length) {
            character = value.charAt(index)
  
            if (!decimal(character)) {
              break
            }
  
            queue += character
            index++
          }
  
          character = value.charAt(index)
          index++
  
          if (
            queue &&
            (character === dot || (commonmark && character === rightParenthesis))
          ) {
            currentMarker = character
            size += queue.length + 1
          }
        }
  
        if (currentMarker) {
          character = value.charAt(index)
  
          if (character === tab) {
            size += tabSize - (size % tabSize)
            index++
          } else if (character === space) {
            end = index + tabSize
  
            while (index < end) {
              if (value.charAt(index) !== space) {
                break
              }
  
              index++
              size++
            }
  
            if (index === end && value.charAt(index) === space) {
              index -= tabSize - 1
              size -= tabSize - 1
            }
          } else if (character !== lineFeed && character !== '') {
            currentMarker = null
          }
        }
      }
  
      if (currentMarker) {
        if (!pedantic && marker !== currentMarker) {
          break
        }
  
        prefixed = true
      } else {
        if (!commonmark && !indented && value.charAt(startIndex) === space) {
          indented = true
        } else if (commonmark && item) {
          indented = size >= item.indent || size > tabSize
        }
  
        prefixed = false
        index = startIndex
      }
  
      line = value.slice(startIndex, nextIndex)
      content = startIndex === index ? line : value.slice(index, nextIndex)
  
      if (
        currentMarker === asterisk ||
        currentMarker === underscore ||
        currentMarker === dash
      ) {
        if (tokenizers.thematicBreak.call(self, eat, line, true)) {
          break
        }
      }
  
      previousEmpty = empty
      empty = !prefixed && !trim(content).length
  
      if (indented && item) {
        item.value = item.value.concat(emptyLines, line)
        allLines = allLines.concat(emptyLines, line)
        emptyLines = []
      } else if (prefixed) {
        if (emptyLines.length !== 0) {
          spread = true
          item.value.push('')
          item.trail = emptyLines.concat()
        }
  
        item = {
          value: [line],
          indent: size,
          trail: []
        }
  
        items.push(item)
        allLines = allLines.concat(emptyLines, line)
        emptyLines = []
      } else if (empty) {
        if (previousEmpty && !commonmark) {
          break
        }
  
        emptyLines.push(line)
      } else {
        if (previousEmpty) {
          break
        }
  
        if (interrupt(interuptors, tokenizers, self, [eat, line, true])) {
          break
        }
  
        item.value = item.value.concat(emptyLines, line)
        allLines = allLines.concat(emptyLines, line)
        emptyLines = []
      }
  
      index = nextIndex + 1
    }
  
    node = eat(allLines.join(lineFeed)).reset({
      type: 'list',
      ordered: ordered,
      start: start,
      spread: spread,
      children: []
    })
  
    enterTop = self.enterList()
    exitBlockquote = self.enterBlock()
    index = -1
    length = items.length
  
    while (++index < length) {
      item = items[index].value.join(lineFeed)
      now = eat.now()
  
      eat(item)(listItem(self, item, now), node)
  
      item = items[index].trail.join(lineFeed)
  
      if (index !== length - 1) {
        item += lineFeed
      }
  
      eat(item)
    }
  
    enterTop()
    exitBlockquote()
  
    return node
  }
  
  function listItem(ctx, value, position) {
    var offsets = ctx.offset
    var fn = ctx.options.pedantic ? pedanticListItem : normalListItem
    var checked = null
    var task
    var indent
  
    value = fn.apply(null, arguments)
  
    if (ctx.options.gfm) {
      task = value.match(taskItemExpression)
  
      if (task) {
        indent = task[0].length
        checked = task[1].toLowerCase() === lowercaseX
        offsets[position.line] += indent
        value = value.slice(indent)
      }
    }
  
    return {
      type: 'listItem',
      spread: looseListItemExpression.test(value),
      checked: checked,
      children: ctx.tokenizeBlock(value, position)
    }
  }
  
  // Create a list-item using overly simple mechanics.
  function pedanticListItem(ctx, value, position) {
    var offsets = ctx.offset
    var line = position.line
  
    // Remove the list-item’s bullet.
    value = value.replace(pedanticBulletExpression, replacer)
  
    // The initial line was also matched by the below, so we reset the `line`.
    line = position.line
  
    return value.replace(initialIndentExpression, replacer)
  
    // A simple replacer which removed all matches, and adds their length to
    // `offset`.
    function replacer($0) {
      offsets[line] = (offsets[line] || 0) + $0.length
      line++
  
      return ''
    }
  }
  
  // Create a list-item using sane mechanics.
  function normalListItem(ctx, value, position) {
    var offsets = ctx.offset
    var line = position.line
    var max
    var bullet
    var rest
    var lines
    var trimmedLines
    var index
    var length
  
    // Remove the list-item’s bullet.
    value = value.replace(bulletExpression, replacer)
  
    lines = value.split(lineFeed)
  
    trimmedLines = removeIndent(value, getIndent(max).indent).split(lineFeed)
  
    // We replaced the initial bullet with something else above, which was used
    // to trick `removeIndentation` into removing some more characters when
    // possible.  However, that could result in the initial line to be stripped
    // more than it should be.
    trimmedLines[0] = rest
  
    offsets[line] = (offsets[line] || 0) + bullet.length
    line++
  
    index = 0
    length = lines.length
  
    while (++index < length) {
      offsets[line] =
        (offsets[line] || 0) + lines[index].length - trimmedLines[index].length
      line++
    }
  
    return trimmedLines.join(lineFeed)
  
    /* eslint-disable-next-line max-params */
    function replacer($0, $1, $2, $3, $4) {
      bullet = $1 + $2 + $3
      rest = $4
  
      // Make sure that the first nine numbered list items can indent with an
      // extra space.  That is, when the bullet did not receive an extra final
      // space.
      if (Number($2) < 10 && bullet.length % 2 === 1) {
        $2 = space + $2
      }
  
      max = $1 + repeat(space, $2.length) + $3
  
      return max + rest
    }
  }
  