{"version":3,"file":"suggestionList.js","sourceRoot":"","sources":["../../src/jsutils/suggestionList.ts"],"names":[],"mappings":"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","sourcesContent":["import { naturalCompare } from './naturalCompare.ts';\n\n/**\n * Given an invalid input string and a list of valid options, returns a filtered\n * list of valid options sorted based on their similarity with the input.\n *\n * @internal\n */\nexport function suggestionList(\n  input: string,\n  options: ReadonlyArray<string>,\n): Array<string> {\n  const optionsByDistance = Object.create(null);\n  const lexicalDistance = new LexicalDistance(input);\n\n  const threshold = Math.floor(input.length * 0.4) + 1;\n  for (const option of options) {\n    const distance = lexicalDistance.measure(option, threshold);\n    if (distance !== undefined) {\n      optionsByDistance[option] = distance;\n    }\n  }\n\n  return Object.keys(optionsByDistance).sort((a, b) => {\n    const distanceDiff = optionsByDistance[a] - optionsByDistance[b];\n    return distanceDiff !== 0 ? distanceDiff : naturalCompare(a, b);\n  });\n}\n\n/**\n * Computes the lexical distance between strings A and B.\n *\n * The \"distance\" between two strings is given by counting the minimum number\n * of edits needed to transform string A into string B. An edit can be an\n * insertion, deletion, or substitution of a single character, or a swap of two\n * adjacent characters.\n *\n * Includes a custom alteration from Damerau-Levenshtein to treat case changes\n * as a single edit which helps identify mis-cased values with an edit distance\n * of 1.\n *\n * This distance can be useful for detecting typos in input or sorting\n *\n * @internal\n */\nclass LexicalDistance {\n  _input: string;\n  _inputLowerCase: string;\n  _inputArray: Array<number>;\n  _rows: [Array<number>, Array<number>, Array<number>];\n\n  constructor(input: string) {\n    this._input = input;\n    this._inputLowerCase = input.toLowerCase();\n    this._inputArray = stringToArray(this._inputLowerCase);\n\n    this._rows = [\n      new Array(input.length + 1).fill(0),\n      new Array(input.length + 1).fill(0),\n      new Array(input.length + 1).fill(0),\n    ];\n  }\n\n  measure(option: string, threshold: number): number | undefined {\n    if (this._input === option) {\n      return 0;\n    }\n\n    const optionLowerCase = option.toLowerCase();\n\n    // Any case change counts as a single edit\n    if (this._inputLowerCase === optionLowerCase) {\n      return 1;\n    }\n\n    let a = stringToArray(optionLowerCase);\n    let b = this._inputArray;\n\n    if (a.length < b.length) {\n      const tmp = a;\n      a = b;\n      b = tmp;\n    }\n    const aLength = a.length;\n    const bLength = b.length;\n\n    if (aLength - bLength > threshold) {\n      return undefined;\n    }\n\n    const rows = this._rows;\n    for (let j = 0; j <= bLength; j++) {\n      rows[0][j] = j;\n    }\n\n    for (let i = 1; i <= aLength; i++) {\n      const upRow = rows[(i - 1) % 3];\n      const currentRow = rows[i % 3];\n\n      let smallestCell = (currentRow[0] = i);\n      for (let j = 1; j <= bLength; j++) {\n        const cost = a[i - 1] === b[j - 1] ? 0 : 1;\n\n        let currentCell = Math.min(\n          upRow[j] + 1, // delete\n          currentRow[j - 1] + 1, // insert\n          upRow[j - 1] + cost, // substitute\n        );\n\n        if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) {\n          // transposition\n          const doubleDiagonalCell = rows[(i - 2) % 3][j - 2];\n          currentCell = Math.min(currentCell, doubleDiagonalCell + 1);\n        }\n\n        if (currentCell < smallestCell) {\n          smallestCell = currentCell;\n        }\n\n        currentRow[j] = currentCell;\n      }\n\n      // Early exit, since distance can't go smaller than smallest element of the previous row.\n      if (smallestCell > threshold) {\n        return undefined;\n      }\n    }\n\n    const distance = rows[aLength % 3][bLength];\n    return distance <= threshold ? distance : undefined;\n  }\n}\n\nfunction stringToArray(str: string): Array<number> {\n  const strLength = str.length;\n  const array = new Array(strLength);\n  for (let i = 0; i < strLength; ++i) {\n    array[i] = str.charCodeAt(i);\n  }\n  return array;\n}\n"]}