diff --git a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts index 69573df0b6..704c43c59d 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/surroundingPair/index.ts @@ -99,6 +99,18 @@ function processSurroundingPairCore( try { node = languageDefinitions.getNodeAtLocation(document, range); + // Error nodes are unreliable and should be ignored. Fall back to text based + // algorithm. + if (nodeHasError(node)) { + return findSurroundingPairTextBased( + editor, + range, + null, + delimiters, + scopeType, + ); + } + textFragmentExtractor = getTextFragmentExtractor(document.languageId); } catch (err) { if ((err as Error).name === "UnsupportedLanguageError") { @@ -148,3 +160,22 @@ function processSurroundingPairCore( scopeType, ); } + +function nodeHasError(node: SyntaxNode, includeChildren = false): boolean { + if (nodeIsError(node)) { + return true; + } + if (includeChildren) { + if (node.children.some(nodeIsError)) { + return true; + } + } + if (node.parent != null) { + return nodeHasError(node.parent, true); + } + return false; +} + +function nodeIsError(node: SyntaxNode): boolean { + return node.type === "ERROR"; +} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/clearPair5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/clearPair5.yml new file mode 100644 index 0000000000..78dac972a0 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/surroundingPair/parseTree/python/clearPair5.yml @@ -0,0 +1,22 @@ +languageId: python +command: + version: 5 + spokenForm: clear pair + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: surroundingPair, delimiter: any} + usePrePhraseSnapshot: true +initialState: + documentContents: "def funk(words:list):" + selections: + - anchor: {line: 0, character: 20} + active: {line: 0, character: 20} + marks: {} +finalState: + documentContents: "def funk(words:list):" + selections: + - anchor: {line: 0, character: 19} + active: {line: 0, character: 19}