UNPKG

cm-tarnation

Version:

An alternative parser for CodeMirror 6

91 lines (78 loc) 2.82 kB
/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import type { Matcher, VariableTable } from "../../types" import { pointsMatch, toPoints } from "../../util" /** Matcher that takes a string pattern, and matches that against an input. */ export class StringMatcher implements Matcher { /** Internal codepoint representation of the pattern. */ private declare points: number[] /** The original length of the pattern. */ private declare length: number /** If true, the pattern has to check for casing when matching. */ private declare cased: boolean /** * @param str - The source string. * @param ignoreCase - If `true`, matches will be case-insensitive. * @param variables - A variable table to use when expanding variables. */ constructor(str: string, ignoreCase = false, variables?: VariableTable) { if (variables) str = expandVariables(str, variables) if (ignoreCase) str = str.toLowerCase() this.cased = ignoreCase && str.toLowerCase() !== str.toUpperCase() this.length = str.length this.points = toPoints(str) } /** * Tests to see if this pattern matches the given string. * * @param str - The string to match. * @param pos - The position to start matching at. */ test(str: string, pos: number) { if (this.length > str.length) return false if (pointsMatch(this.points, str, pos)) return true // less optimized code path due to lowercasing requirement if (this.cased) { const lowered = str.slice(pos, pos + this.length).toLowerCase() return pointsMatch(this.points, lowered, 0) } return false } /** * Returns the results of attempting to match a string against this pattern. * * @param str - The string to match. * @param pos - The position to start matching at. */ match(str: string, pos: number) { if (this.test(str, pos)) { return { total: str.slice(pos, pos + this.length), captures: null, length: this.length } } return null } } /** * Expands the variables in a pattern source using a variable table. * * @param src - The source to expand. * @param variables - The variable table to use. */ function expandVariables(src: string, variables: VariableTable) { let depth = 0 while (/@\w/.test(src) && depth < 5) { depth++ src = src.replace(/@(\w+)/g, (_, ident: string) => { const value = variables[ident] if (typeof value !== "string") { throw new Error(`Variable ${ident} is not a string`) } return value }) } return src }