Repository: marktext/marktext
File: src/muya/lib/contentState/searchCtrl.js (JavaScript)

Code before:
  if (!isRegexp) {
    regStr = value.replace(SPECIAL_CHAR_REG, (p) => {
      return p === '\\' ? '\\\\' : `\\${p}`
    })
  }

  if (isWholeWord) {
    regStr = `\\b${regStr}\\b`
  }

  try {
    // Add try catch expression because not all string can generate a valid RegExp. for example `\`.
    SEARCH_REG = new RegExp(regStr, flag)
    return execall(SEARCH_REG, text)
  } catch (err) {
    return []
  }
}

const searchCtrl = ContentState => {
  ContentState.prototype.replaceOne = function (match, value) {
    const { start, end, key } = match
    const block = this.getBlock(key)
    const { text } = block

    block.text = text.substring(0, start) + value + text.substring(end)
  }

  ContentState.prototype.replace = function (replaceValue, opt = { isSingle: true }) {
    const { isSingle } = opt
    delete opt.isSingle
    const searchOptions = Object.assign({}, defaultSearchOption, opt)
    const { matches, value, index } = this.searchMatches
    if (matches.length) {
      if (isSingle) {
        this.replaceOne(matches[index], replaceValue)
      } else {
        // replace all
        for (const match of matches) {
          this.replaceOne(match, replaceValue)
        }
      }
      const highlightIndex = index < matches.length - 1 ? index : index - 1
      this.search(value, { ...searchOptions, highlightIndex: isSingle ? highlightIndex : -1 })
    }
  }

  ContentState.prototype.setCursorToHighlight = function () {
    const { matches, index } = this.searchMatches
    const match = matches[index]


Review comment (suggestion):
`groupIndex` can be `-1` if you use `$0`.

```suggestion
        if (groupIndex >= 0 && groupIndex < match.subMatches.length) {
```

Code after:
  if (!isRegexp) {
    regStr = value.replace(SPECIAL_CHAR_REG, (p) => {
      return p === '\\' ? '\\\\' : `\\${p}`
    })
  }

  if (isWholeWord) {
    regStr = `\\b${regStr}\\b`
  }

  try {
    // Add try catch expression because not all string can generate a valid RegExp. for example `\`.
    SEARCH_REG = new RegExp(regStr, flag)
    return execall(SEARCH_REG, text)
  } catch (err) {
    return []
  }
}

const searchCtrl = ContentState => {
  ContentState.prototype.buildRegexValue = function (match, value) {
    const groups = value.match(/\$(\d)*/g)
    if (groups) {
      for (const regexGroup of groups) {
        const groupIndex = regexGroup[1] - 1
        if (groupIndex < match.subMatches.length) {
          value = value.replace(regexGroup, match.subMatches[groupIndex])
        }
      }
    }
    return value
  }

  ContentState.prototype.replaceOne = function (match, value) {
    const { start, end, key } = match
    const block = this.getBlock(key)
    const { text } = block
    block.text = text.substring(0, start) + value + text.substring(end)
  }

  ContentState.prototype.replace = function (replaceValue, opt = { isSingle: true }) {
    const { isSingle } = opt
    delete opt.isSingle
    const searchOptions = Object.assign({}, defaultSearchOption, opt)
    const { matches, value, index } = this.searchMatches
    if (matches.length) {
      if (opt.isRegexp) {
        replaceValue = this.buildRegexValue(matches[index], replaceValue)
      }
      if (isSingle) {
        this.replaceOne(matches[index], replaceValue)