diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/CharacterScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/CharacterScopeHandler.ts index 42d972409a..15caab5bd7 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/CharacterScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/CharacterScopeHandler.ts @@ -1,10 +1,10 @@ +import { Direction, ScopeType } from "@cursorless/common"; import { imap } from "itertools"; import { NestedScopeHandler } from "."; -import { generateMatchesInRange } from "../../../util/getMatchesInRange"; -import { Direction, ScopeType } from "@cursorless/common"; import { getMatcher } from "../../../tokenizer"; -import { testRegex } from "../../../util/regex"; +import { generateMatchesInRange } from "../../../util/getMatchesInRange"; import { PlainTarget } from "../../targets"; +import { isPreferredOverHelper } from "./isPreferredOverHelper"; import type { TargetScope } from "./scope.types"; /** @@ -50,37 +50,13 @@ export class CharacterScopeHandler extends NestedScopeHandler { scopeA: TargetScope, scopeB: TargetScope, ): boolean | undefined { - const { - editor: { document }, - } = scopeA; const { identifierMatcher } = getMatcher(this.languageId); - - const textA = document.getText(scopeA.domain); - const textB = document.getText(scopeB.domain); - // Regexes indicating preferences. We prefer identifiers, preferred // symbols, then nonwhitespace. - const matchers = [ + return isPreferredOverHelper(scopeA, scopeB, [ identifierMatcher, PREFERRED_SYMBOLS_REGEX, NONWHITESPACE_REGEX, - ]; - - for (const matcher of matchers) { - // NB: Don't directly use `test` here because global regexes are stateful - // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec#finding_successive_matches - const aMatchesRegex = testRegex(matcher, textA); - const bMatchesRegex = testRegex(matcher, textB); - - if (aMatchesRegex && !bMatchesRegex) { - return true; - } - - if (bMatchesRegex && !aMatchesRegex) { - return false; - } - } - - return undefined; + ]); } } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TokenScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TokenScopeHandler.ts index 43212f80db..38e00dca88 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TokenScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/TokenScopeHandler.ts @@ -1,10 +1,10 @@ +import { Direction } from "@cursorless/common"; import { imap } from "itertools"; import { NestedScopeHandler } from "."; -import { generateMatchesInRange } from "../../../util/getMatchesInRange"; -import { Direction } from "@cursorless/common"; import { getMatcher } from "../../../tokenizer"; -import { testRegex } from "../../../util/regex"; +import { generateMatchesInRange } from "../../../util/getMatchesInRange"; import { TokenTarget } from "../../targets"; +import { isPreferredOverHelper } from "./isPreferredOverHelper"; import type { TargetScope } from "./scope.types"; const PREFERRED_SYMBOLS_REGEX = /[$]/; @@ -39,33 +39,12 @@ export class TokenScopeHandler extends NestedScopeHandler { scopeA: TargetScope, scopeB: TargetScope, ): boolean | undefined { - const { - editor: { document }, - } = scopeA; const { identifierMatcher } = getMatcher(this.languageId); - - const textA = document.getText(scopeA.domain); - const textB = document.getText(scopeB.domain); - // Regexes indicating preferences. We prefer identifiers then preferred // symbols. - const matchers = [identifierMatcher, PREFERRED_SYMBOLS_REGEX]; - - for (const matcher of matchers) { - // NB: Don't directly use `test` here because global regexes are stateful - // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec#finding_successive_matches - const aMatchesRegex = testRegex(matcher, textA); - const bMatchesRegex = testRegex(matcher, textB); - - if (aMatchesRegex && !bMatchesRegex) { - return true; - } - - if (bMatchesRegex && !aMatchesRegex) { - return false; - } - } - - return undefined; + return isPreferredOverHelper(scopeA, scopeB, [ + identifierMatcher, + PREFERRED_SYMBOLS_REGEX, + ]); } } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/isPreferredOverHelper.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/isPreferredOverHelper.ts new file mode 100644 index 0000000000..c82a1c11ab --- /dev/null +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/isPreferredOverHelper.ts @@ -0,0 +1,31 @@ +import { testRegex } from "../../../util/regex"; +import type { TargetScope } from "./scope.types"; + +export function isPreferredOverHelper( + scopeA: TargetScope, + scopeB: TargetScope, + matchers: RegExp[], +): boolean | undefined { + const { + editor: { document }, + } = scopeA; + const textA = document.getText(scopeA.domain); + const textB = document.getText(scopeB.domain); + + for (const matcher of matchers) { + // NB: Don't directly use `test` here because global regexes are stateful + // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec#finding_successive_matches + const aMatchesRegex = testRegex(matcher, textA); + const bMatchesRegex = testRegex(matcher, textB); + + if (aMatchesRegex && !bMatchesRegex) { + return true; + } + + if (bMatchesRegex && !aMatchesRegex) { + return false; + } + } + + return undefined; +}