function visit(rule, callback) {
  const result = callback(rule)

  if (result === false) {
    return result
  }

  if ('captures' in rule && rule.captures) map(rule.captures)
  if ('beginCaptures' in rule && rule.beginCaptures) map(rule.beginCaptures)
  if ('endCaptures' in rule && rule.endCaptures) map(rule.endCaptures)
  if ('whileCaptures' in rule && rule.whileCaptures) map(rule.whileCaptures)
  if ('repository' in rule && rule.repository) map(rule.repository)
  if ('injections' in rule && rule.injections) map(rule.injections)
  if ('patterns' in rule && rule.patterns) set(rule.patterns)

  // Keep.
  return true

  /**
   * @param {Array<Rule>} values
   *   List of rules to visit.
   */
  function set(values) {
    let index = -1
    while (++index < values.length) {
      const result = visit(values[index], callback)
      if (result === false) {
        values.splice(index, 1)
        index--
      }
    }
  }

  /**
   * @param {Record<string, Rule>} values
   *   Map of rules to visit.
   */
  function map(values) {
    /** @type {string} */
    let key

    for (key in values) {
      if (Object.hasOwn(values, key)) {
        const result = visit(values[key], callback)
        if (result === false) {
          delete values[key]
        }
      }
    }
  }
}
@param {Rule} rule
  Rule to visit.
@param {(rule: Rule) => boolean | undefined} callback
  Callback called for each rule.
@returns {boolean}
  Whether to keep the rule.