From 65fab51e0bb76a2555883d9a28c0fbce25da432a Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Tue, 6 Jun 2023 08:12:18 -0400 Subject: [PATCH] Support jsx tag scopes using tree-sitter queries (#1508) - Fixes #1478 - Depends on https://github.com/cursorless-dev/cursorless/pull/1507 ## Checklist - [x] Support "every name" while we're here - [x] Arg lists - [x] Functions in class - [x] Assignment statements in function body - [x] Assignment statements at top level - [x] Properties in class - [x] Members of interface - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [ ] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [ ] I have not broken the cheatsheet --------- Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> --- .../src/languages/typescript.ts | 59 +----------------- .../src/util/nodeSelectors.ts | 19 ------ .../typescript/clearEveryElement.yml | 32 ++++++++++ .../typescript/clearEveryElement2.yml | 32 ++++++++++ .../typescript/clearEveryElement3.yml | 32 ++++++++++ .../typescript/clearEveryElement4.yml | 32 ++++++++++ .../languages/typescript/clearEveryEndTag.yml | 32 ++++++++++ .../typescript/clearEveryEndTag2.yml | 32 ++++++++++ .../languages/typescript/clearEveryName.yml | 28 +++++++++ .../languages/typescript/clearEveryName2.yml | 32 ++++++++++ .../languages/typescript/clearEveryName3.yml | 28 +++++++++ .../languages/typescript/clearEveryName4.yml | 36 +++++++++++ .../languages/typescript/clearEveryName5.yml | 24 ++++++++ .../languages/typescript/clearEveryName6.yml | 30 ++++++++++ .../typescript/clearEveryStartTag.yml | 32 ++++++++++ .../typescript/clearEveryStartTag2.yml | 32 ++++++++++ .../languages/typescript/clearEveryTags.yml | 36 +++++++++++ .../languages/typescript/clearEveryTags2.yml | 36 +++++++++++ .../languages/typescript/clearNameTags.yml | 26 ++++++++ queries/javascript.core.scm | 29 +++++++++ queries/javascript.jsx.scm | 60 +++++++++++++++++++ queries/javascript.scm | 5 ++ queries/javascriptreact.scm | 1 + queries/typescript.scm | 13 ++++ queries/typescriptreact.scm | 2 + 25 files changed, 644 insertions(+), 76 deletions(-) create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement4.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryEndTag.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryEndTag2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName3.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName4.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName5.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName6.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryStartTag.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryStartTag2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryTags.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryTags2.yml create mode 100644 packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearNameTags.yml create mode 100644 queries/javascript.core.scm create mode 100644 queries/javascript.jsx.scm create mode 100644 queries/javascript.scm create mode 100644 queries/javascriptreact.scm create mode 100644 queries/typescript.scm create mode 100644 queries/typescriptreact.scm diff --git a/packages/cursorless-engine/src/languages/typescript.ts b/packages/cursorless-engine/src/languages/typescript.ts index 206e22d378..04d8bca7da 100644 --- a/packages/cursorless-engine/src/languages/typescript.ts +++ b/packages/cursorless-engine/src/languages/typescript.ts @@ -1,11 +1,11 @@ -import { Selection, SimpleScopeTypeType, TextEditor } from "@cursorless/common"; +import { SimpleScopeTypeType } from "@cursorless/common"; import type { SyntaxNode } from "web-tree-sitter"; import { NodeMatcher, NodeMatcherAlternative, SelectionWithEditor, } from "../typings/Types"; -import { patternFinder, typedNodeFinder } from "../util/nodeFinders"; +import { patternFinder } from "../util/nodeFinders"; import { argumentMatcher, cascadingMatcher, @@ -20,12 +20,10 @@ import { extendForwardPastOptional, getNodeInternalRange, getNodeRange, - jsxFragmentExtractor, pairSelectionExtractor, selectWithLeadingDelimiter, simpleSelectionExtractor, unwrapSelectionExtractor, - xmlElementExtractor, } from "../util/nodeSelectors"; import { branchMatcher } from "./branchMatcher"; import { elseExtractor, elseIfExtractor } from "./elseIfExtractor"; @@ -71,42 +69,6 @@ const STATEMENT_TYPES = [ "with_statement", ]; -/** Handles jsx fragment start or end tag, eg the `<>` in `<>foo` **/ -function getJsxFragmentTag(isStartTag: boolean): NodeMatcher { - return matcher( - typedNodeFinder("jsx_fragment"), - (editor: TextEditor, node: SyntaxNode) => { - const [start, end] = isStartTag - ? [node.children[0], node.children[1]] - : [node.children.at(-3)!, node.children.at(-1)!]; - return { - selection: new Selection( - start.startPosition.row, - start.startPosition.column, - end.endPosition.row, - end.endPosition.column, - ), - context: {}, - }; - }, - ); -} - -const getStartTag = cascadingMatcher( - patternMatcher("jsx_element.jsx_opening_element!"), - getJsxFragmentTag(true), -); -const getEndTag = cascadingMatcher( - patternMatcher("jsx_element.jsx_closing_element!"), - getJsxFragmentTag(false), -); - -const getTags = (selection: SelectionWithEditor, node: SyntaxNode) => { - const startTag = getStartTag(selection, node); - const endTag = getEndTag(selection, node); - return startTag != null && endTag != null ? startTag.concat(endTag) : null; -}; - function typeMatcher(): NodeMatcher { const delimiterSelector = selectWithLeadingDelimiter(":"); return function (selection: SelectionWithEditor, node: SyntaxNode) { @@ -214,13 +176,6 @@ const nodeMatchers: Partial< ), ifStatement: "if_statement", anonymousFunction: ["arrow_function", "function"], - name: [ - "*[name]", - "optional_parameter.identifier!", - "required_parameter.identifier!", - "augmented_assignment_expression[left]", - "assignment_expression[left]", - ], comment: "comment", regularExpression: "regex", className: ["class_declaration[name]", "class[name]"], @@ -337,16 +292,6 @@ const nodeMatchers: Partial< argumentOrParameter: argumentMatcher("formal_parameters", "arguments"), // XML, JSX attribute: ["jsx_attribute"], - xmlElement: cascadingMatcher( - matcher( - typedNodeFinder("jsx_element", "jsx_self_closing_element"), - xmlElementExtractor, - ), - matcher(typedNodeFinder("jsx_fragment"), jsxFragmentExtractor), - ), - xmlBothTags: getTags, - xmlStartTag: getStartTag, - xmlEndTag: getEndTag, }; export const patternMatchers = createPatternMatchers(nodeMatchers); diff --git a/packages/cursorless-engine/src/util/nodeSelectors.ts b/packages/cursorless-engine/src/util/nodeSelectors.ts index 54ba7e6e25..23e5f77244 100644 --- a/packages/cursorless-engine/src/util/nodeSelectors.ts +++ b/packages/cursorless-engine/src/util/nodeSelectors.ts @@ -427,25 +427,6 @@ export function xmlElementExtractor( return selection; } -export function jsxFragmentExtractor( - editor: TextEditor, - node: SyntaxNode, -): SelectionWithContext { - const selection = simpleSelectionExtractor(editor, node); - - // Interior range for an element is found by excluding the start and end nodes. - const startPosition = node.children[1].endPosition; - const endPosition = node.children.at(-3)!.startPosition; - selection.context.interiorRange = new Range( - startPosition.row, - startPosition.column, - endPosition.row, - endPosition.column, - ); - - return selection; -} - export function getInsertionDelimiter( editor: TextEditor, leadingDelimiterRange: Range | undefined, diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement.yml new file mode 100644 index 0000000000..2781402059 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement.yml @@ -0,0 +1,32 @@ +languageId: typescriptreact +command: + version: 5 + spokenForm: clear every element + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: xmlElement} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + + ccc + ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + + + + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement2.yml new file mode 100644 index 0000000000..e55b5bb079 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement2.yml @@ -0,0 +1,32 @@ +languageId: typescriptreact +command: + version: 5 + spokenForm: clear every element + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: xmlElement} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + <> + ccc + ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + <> + + + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement3.yml new file mode 100644 index 0000000000..0cb1aab6f1 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement3.yml @@ -0,0 +1,32 @@ +languageId: typescriptreact +command: + version: 5 + spokenForm: clear every element + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: xmlElement} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + <> + <>ccc + <>ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + <> + + + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement4.yml new file mode 100644 index 0000000000..3e7b333ad8 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryElement4.yml @@ -0,0 +1,32 @@ +languageId: typescriptreact +command: + version: 5 + spokenForm: clear every element + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: xmlElement} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + + <>ccc + <>ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + + + + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryEndTag.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryEndTag.yml new file mode 100644 index 0000000000..8d617f046b --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryEndTag.yml @@ -0,0 +1,32 @@ +languageId: typescriptreact +command: + version: 5 + spokenForm: clear every end tag + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: xmlEndTag} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + <> + <>ccc + <>ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + <> + <>ccc + <>ddd + + selections: + - anchor: {line: 1, character: 9} + active: {line: 1, character: 9} + - anchor: {line: 2, character: 9} + active: {line: 2, character: 9} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryEndTag2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryEndTag2.yml new file mode 100644 index 0000000000..e0cf807f97 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryEndTag2.yml @@ -0,0 +1,32 @@ +languageId: typescriptreact +command: + version: 5 + spokenForm: clear every end tag + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: xmlEndTag} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + + ccc + ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + + ccc + ddd + + selections: + - anchor: {line: 1, character: 12} + active: {line: 1, character: 12} + - anchor: {line: 2, character: 12} + active: {line: 2, character: 12} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName.yml new file mode 100644 index 0000000000..308c953690 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName.yml @@ -0,0 +1,28 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear every name + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + const aaa = "bbb"; + const ccc = "ddd"; + selections: + - anchor: {line: 1, character: 18} + active: {line: 1, character: 18} + marks: {} +finalState: + documentContents: |- + const = "bbb"; + const = "ddd"; + selections: + - anchor: {line: 0, character: 6} + active: {line: 0, character: 6} + - anchor: {line: 1, character: 6} + active: {line: 1, character: 6} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName2.yml new file mode 100644 index 0000000000..0dc9c29572 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName2.yml @@ -0,0 +1,32 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear every name + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + function eee() { + const aaa = "bbb"; + const ccc = "ddd"; + } + selections: + - anchor: {line: 2, character: 22} + active: {line: 2, character: 22} + marks: {} +finalState: + documentContents: |- + function eee() { + const = "bbb"; + const = "ddd"; + } + selections: + - anchor: {line: 1, character: 10} + active: {line: 1, character: 10} + - anchor: {line: 2, character: 10} + active: {line: 2, character: 10} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName3.yml new file mode 100644 index 0000000000..c10620d5f4 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName3.yml @@ -0,0 +1,28 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear every name + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + function eee() {} + function fff() {} + selections: + - anchor: {line: 1, character: 17} + active: {line: 1, character: 17} + marks: {} +finalState: + documentContents: |- + function () {} + function () {} + selections: + - anchor: {line: 0, character: 9} + active: {line: 0, character: 9} + - anchor: {line: 1, character: 9} + active: {line: 1, character: 9} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName4.yml new file mode 100644 index 0000000000..5e6295a034 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName4.yml @@ -0,0 +1,36 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear every name + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + class Aaa { + bbb = 0; + eee() {} + fff() {} + } + selections: + - anchor: {line: 3, character: 12} + active: {line: 3, character: 12} + marks: {} +finalState: + documentContents: |- + class Aaa { + = 0; + () {} + () {} + } + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} + - anchor: {line: 3, character: 4} + active: {line: 3, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName5.yml new file mode 100644 index 0000000000..16a7b4e0d7 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName5.yml @@ -0,0 +1,24 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear every name + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: "function aaa(bbb: string, ccc: string) {}" + selections: + - anchor: {line: 0, character: 37} + active: {line: 0, character: 37} + marks: {} +finalState: + documentContents: "function aaa(: string, : string) {}" + selections: + - anchor: {line: 0, character: 13} + active: {line: 0, character: 13} + - anchor: {line: 0, character: 23} + active: {line: 0, character: 23} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName6.yml new file mode 100644 index 0000000000..75566e697e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryName6.yml @@ -0,0 +1,30 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear every name + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: name} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + class Ccc { + constructor(private aaa: string, bbb: string) {} + } + selections: + - anchor: {line: 1, character: 48} + active: {line: 1, character: 48} + marks: {} +finalState: + documentContents: |- + class Ccc { + constructor(private : string, : string) {} + } + selections: + - anchor: {line: 1, character: 24} + active: {line: 1, character: 24} + - anchor: {line: 1, character: 34} + active: {line: 1, character: 34} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryStartTag.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryStartTag.yml new file mode 100644 index 0000000000..e78be18880 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryStartTag.yml @@ -0,0 +1,32 @@ +languageId: typescriptreact +command: + version: 5 + spokenForm: clear every start tag + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: xmlStartTag} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + <> + <>ccc + <>ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + <> + ccc + ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryStartTag2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryStartTag2.yml new file mode 100644 index 0000000000..a864a4987d --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryStartTag2.yml @@ -0,0 +1,32 @@ +languageId: typescriptreact +command: + version: 5 + spokenForm: clear every start tag + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: xmlStartTag} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + + ccc + ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + + ccc + ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryTags.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryTags.yml new file mode 100644 index 0000000000..e6cfa27321 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryTags.yml @@ -0,0 +1,36 @@ +languageId: typescriptreact +command: + version: 5 + spokenForm: clear every tags + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: xmlBothTags} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + <> + <>ccc + <>ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + <> + ccc + ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + - anchor: {line: 1, character: 7} + active: {line: 1, character: 7} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} + - anchor: {line: 2, character: 7} + active: {line: 2, character: 7} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryTags2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryTags2.yml new file mode 100644 index 0000000000..ab3f030013 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryTags2.yml @@ -0,0 +1,36 @@ +languageId: typescriptreact +command: + version: 5 + spokenForm: clear every tags + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: everyScope + scopeType: {type: xmlBothTags} + usePrePhraseSnapshot: true +initialState: + documentContents: |- + + ccc + ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + marks: {} +finalState: + documentContents: |- + + ccc + ddd + + selections: + - anchor: {line: 1, character: 4} + active: {line: 1, character: 4} + - anchor: {line: 1, character: 7} + active: {line: 1, character: 7} + - anchor: {line: 2, character: 4} + active: {line: 2, character: 4} + - anchor: {line: 2, character: 7} + active: {line: 2, character: 7} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearNameTags.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearNameTags.yml new file mode 100644 index 0000000000..b9deee7194 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearNameTags.yml @@ -0,0 +1,26 @@ +languageId: typescriptreact +command: + version: 5 + spokenForm: clear name tags + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: name} + - type: containingScope + scopeType: {type: xmlBothTags} + usePrePhraseSnapshot: true +initialState: + documentContents: <>aaa + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + marks: {} +finalState: + documentContents: <>aaa + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + - anchor: {line: 0, character: 7} + active: {line: 0, character: 7} diff --git a/queries/javascript.core.scm b/queries/javascript.core.scm new file mode 100644 index 0000000000..93ec9109aa --- /dev/null +++ b/queries/javascript.core.scm @@ -0,0 +1,29 @@ +(_ + name: (_) @name +) @_.domain + +(augmented_assignment_expression + left: (_) @name +) @_.domain + +(assignment_expression + left: (_) @name +) @_.domain + +[ + (program) + (formal_parameters) +] @name.iteration + +( + (_ + body: (_ + . + "{" @name.iteration.start + "}" @name.iteration.end + . + ) + ) + (#end-position! @name.iteration.start) + (#start-position! @name.iteration.end) +) diff --git a/queries/javascript.jsx.scm b/queries/javascript.jsx.scm new file mode 100644 index 0000000000..84b95ceba4 --- /dev/null +++ b/queries/javascript.jsx.scm @@ -0,0 +1,60 @@ +( + (jsx_element) @xmlElement @_.interior @_.iteration + (#child-range! @_.interior 0 -1 true true) + (#child-range! @_.iteration 0 -1 true true) +) + +( + (jsx_element) @xmlStartTag.iteration @xmlEndTag.iteration @xmlBothTags.iteration + (#child-range! @xmlStartTag.iteration 0 -1 true true) + (#child-range! @xmlEndTag.iteration 0 -1 true true) + (#child-range! @xmlBothTags.iteration 0 -1 true true) +) + +(jsx_element + (jsx_opening_element) @xmlStartTag @xmlBothTags + (#allow-multiple! @xmlBothTags) +) @_.domain + +(jsx_element + (jsx_closing_element) @xmlEndTag @xmlBothTags + (#allow-multiple! @xmlBothTags) +) @_.domain + +(jsx_self_closing_element) @xmlElement + +;; JSX fragments, eg <>foo +( + (jsx_fragment) @xmlElement @_.interior @_.iteration + (#child-range! @_.interior 1 -3 true true) + (#child-range! @_.iteration 1 -3 true true) +) + +( + (jsx_fragment) @xmlStartTag.iteration @xmlEndTag.iteration @xmlBothTags.iteration + (#child-range! @xmlStartTag.iteration 1 -3 true true) + (#child-range! @xmlEndTag.iteration 1 -3 true true) + (#child-range! @xmlBothTags.iteration 1 -3 true true) +) + +( + (jsx_fragment) @xmlStartTag @xmlBothTags @_.domain + (#child-range! @xmlStartTag 0 1) + (#child-range! @xmlBothTags 0 1) + (#allow-multiple! @xmlBothTags) +) + +( + (jsx_fragment) @xmlEndTag @xmlBothTags @_.domain + (#child-range! @xmlEndTag -3) + (#child-range! @xmlBothTags -3) + (#allow-multiple! @xmlBothTags) +) + +( + (jsx_fragment + "<" @_.domain.start + ">" @name @_.domain.end + ) + (#start-position! @name) +) diff --git a/queries/javascript.scm b/queries/javascript.scm new file mode 100644 index 0000000000..1c1e8ff4a2 --- /dev/null +++ b/queries/javascript.scm @@ -0,0 +1,5 @@ +;; We include javascript.jsx.scm because jsx scopes technically work in +;; javascript files even if they're not technically javascriptreact file type. + +;; import javascript.jsx.scm +;; import javascript.core.scm diff --git a/queries/javascriptreact.scm b/queries/javascriptreact.scm new file mode 100644 index 0000000000..cf0dc39773 --- /dev/null +++ b/queries/javascriptreact.scm @@ -0,0 +1 @@ +;; import javascript.scm diff --git a/queries/typescript.scm b/queries/typescript.scm new file mode 100644 index 0000000000..df1cd0f456 --- /dev/null +++ b/queries/typescript.scm @@ -0,0 +1,13 @@ +;; Note that we don't just import `javascript.scm` because that includes +;; `javascript.jsx.scm`, and tree-sitter would complain because those node +;; types are not defined in the typescript grammar. + +;; import javascript.core.scm + +(optional_parameter + (identifier) @name +) @_.domain + +(required_parameter + (identifier) @name +) @_.domain diff --git a/queries/typescriptreact.scm b/queries/typescriptreact.scm new file mode 100644 index 0000000000..792c4b5133 --- /dev/null +++ b/queries/typescriptreact.scm @@ -0,0 +1,2 @@ +;; import javascriptreact.scm +;; import typescript.scm