diff --git a/data/fixtures/recorded/languages/lua/changeString2.yml b/data/fixtures/recorded/languages/lua/changeString2.yml new file mode 100644 index 0000000000..6bd5692747 --- /dev/null +++ b/data/fixtures/recorded/languages/lua/changeString2.yml @@ -0,0 +1,23 @@ +languageId: lua +command: + version: 7 + spokenForm: change string + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: surroundingPair, delimiter: string} + usePrePhraseSnapshot: true +initialState: + documentContents: "[[aaa]]" + selections: + - anchor: {line: 0, character: 2} + active: {line: 0, character: 2} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts index e5d42f8d0a..bdea535cb6 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/delimiterMaps.ts @@ -6,10 +6,12 @@ import { unsafeKeys } from "../../../util/object"; type IndividualDelimiterText = string | string[]; -export const delimiterToText: Record< +type DelimiterMap = Record< SimpleSurroundingPairName, [IndividualDelimiterText, IndividualDelimiterText] -> = Object.freeze({ +>; + +const delimiterToText: DelimiterMap = Object.freeze({ angleBrackets: [ ["", "/>"], @@ -26,6 +28,24 @@ export const delimiterToText: Record< squareBrackets: ["[", "]"], }); +// FIXME: Probably remove these as part of +// https://github.com/cursorless-dev/cursorless/issues/1812#issuecomment-1691493746 +const delimiterToTextOverrides: Record> = { + nix: { + singleQuotes: ["''", "''"], + }, + + lua: { + // FIXME: Add special double square brackets + // see https://github.com/cursorless-dev/cursorless/pull/2012#issuecomment-1808214409 + // see also https://github.com/cursorless-dev/cursorless/issues/1812#issuecomment-1691493746 + doubleQuotes: [ + ['"', "[["], + ['"', "]]"], + ], + }, +}; + export const leftToRightMap: Record = Object.fromEntries( Object.values(delimiterToText), ); @@ -47,3 +67,35 @@ export const complexDelimiterMap: Record< "angleBrackets", ], }; + +/** + * Given a language id, returns a list of all possible delimiters for that + * language. + * + * Allows us to support languages where the parse tree gives type names to nodes + * that don't correspond to the actual delimiter. + * + * Note that we pass in `undefined` if we are in a text fragment, because then + * we won't be using a parse tree. + * + * FIXME: Probably remove these as part of + * https://github.com/cursorless-dev/cursorless/issues/1812#issuecomment-1691493746 + * + * @param languageId The language id, or `undefined` if in a text fragment + * @returns A list of all possible delimiters for that language + */ +export function getSimpleDelimiterMap( + languageId: string | undefined, +): Record< + SimpleSurroundingPairName, + [IndividualDelimiterText, IndividualDelimiterText] +> { + if (languageId != null && languageId in delimiterToTextOverrides) { + return { + ...delimiterToText, + ...delimiterToTextOverrides[languageId], + }; + } + + return delimiterToText; +} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairParseTreeBased.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairParseTreeBased.ts index d684996ef7..8ea522c6d7 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairParseTreeBased.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairParseTreeBased.ts @@ -66,7 +66,10 @@ export function findSurroundingPairParseTreeBased( ) { const document: TextDocument = editor.document; - const individualDelimiters = getIndividualDelimiters(delimiters); + const individualDelimiters = getIndividualDelimiters( + document.languageId, + delimiters, + ); const delimiterTextToDelimiterInfoMap = Object.fromEntries( individualDelimiters.map((individualDelimiter) => [ diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairTextBased.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairTextBased.ts index d0be1d0398..1b24505dce 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairTextBased.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/findSurroundingPairTextBased.ts @@ -74,7 +74,7 @@ export function findSurroundingPairTextBased( const document: TextDocument = editor.document; const fullRange = allowableRange ?? document.range; - const individualDelimiters = getIndividualDelimiters(delimiters); + const individualDelimiters = getIndividualDelimiters(undefined, delimiters); const delimiterTextToDelimiterInfoMap = Object.fromEntries( individualDelimiters.map((individualDelimiter) => [ diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts index e53be5981b..e63a8a8d69 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/getIndividualDelimiters.ts @@ -1,6 +1,6 @@ import { SimpleSurroundingPairName, isString } from "@cursorless/common"; import { IndividualDelimiter } from "./types"; -import { delimiterToText } from "./delimiterMaps"; +import { getSimpleDelimiterMap } from "./delimiterMaps"; import { concat, uniq } from "lodash"; /** @@ -8,12 +8,16 @@ import { concat, uniq } from "lodash"; * a single right or left delimiter. Each item contains information such as a * reference to delimiter name, the text to expect, etc. * + * @param languageId The language id, or `undefined` if in a text fragment * @param delimiters The delimiter names - * @returns A list of information about all possible left / right delimiter instances + * @returns A list of information about all possible left / right delimiter + * instances */ export function getIndividualDelimiters( + languageId: string | undefined, delimiters: SimpleSurroundingPairName[], ): IndividualDelimiter[] { + const delimiterToText = getSimpleDelimiterMap(languageId); return delimiters.flatMap((delimiter) => { const [leftDelimiter, rightDelimiter] = delimiterToText[delimiter];