diff --git a/packages/cursorless-engine/src/languages/typescript.ts b/packages/cursorless-engine/src/languages/typescript.ts index 04d8bca7da4..19de623c5c9 100644 --- a/packages/cursorless-engine/src/languages/typescript.ts +++ b/packages/cursorless-engine/src/languages/typescript.ts @@ -175,7 +175,6 @@ const nodeMatchers: Partial< patternMatcher("yield_expression.~yield!"), ), ifStatement: "if_statement", - anonymousFunction: ["arrow_function", "function"], comment: "comment", regularExpression: "regex", className: ["class_declaration[name]", "class[name]"], @@ -227,22 +226,12 @@ const nodeMatchers: Partial< "export_statement.class", // export default class ], functionName: [ - // function - "function_declaration[name]", - // generator function - "generator_function_declaration[name]", - // export default function - "function[name]", // class method "method_definition[name]", // abstract class method "abstract_method_signature[name]", // class arrow method "public_field_definition[name].arrow_function", - // const foo = function() { } - "variable_declarator[name].function", - // const foo = () => { } - "variable_declarator[name].arrow_function", // foo = function() { } "assignment_expression[left].function", // foo = () => { } @@ -250,28 +239,15 @@ const nodeMatchers: Partial< ], namedFunction: cascadingMatcher( patternMatcher( - // [export] function - "export_statement?.function_declaration", - // export default function - // NB: We require export statement because otherwise it is an anonymous - // function - "export_statement.function", - // export default arrow - "export_statement.arrow_function", // class method "method_definition", // class arrow method "public_field_definition.arrow_function", // [export] const foo = function() { } - "export_statement?.lexical_declaration.variable_declarator.function", - // [export] const foo = () => { } - "export_statement?.lexical_declaration.variable_declarator.arrow_function", // foo = function() { } "assignment_expression.function", // foo = () => { } "assignment_expression.arrow_function", - // foo = function*() { } - "generator_function_declaration", ), // abstract class method matcher( diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk10.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk10.yml new file mode 100644 index 00000000000..71f628f8e02 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk10.yml @@ -0,0 +1,22 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: const foo = function *bar() {} + selections: + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} + marks: {} +finalState: + documentContents: "const foo = " + selections: + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk11.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk11.yml new file mode 100644 index 00000000000..7feeafdd19c --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk11.yml @@ -0,0 +1,18 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: const foo = function *bar() {} + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk12.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk12.yml new file mode 100644 index 00000000000..c0d95bbe84b --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk12.yml @@ -0,0 +1,22 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: export default function *() {} + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk4.yml new file mode 100644 index 00000000000..97c7460f743 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk4.yml @@ -0,0 +1,22 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: export function *aaa() {} + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk5.yml new file mode 100644 index 00000000000..fdc0063dfc1 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk5.yml @@ -0,0 +1,22 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: (function foo() {}) + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +finalState: + documentContents: () + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk6.yml new file mode 100644 index 00000000000..03f64276c14 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk6.yml @@ -0,0 +1,18 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: (function () {}) + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk7.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk7.yml new file mode 100644 index 00000000000..e495cbfe548 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk7.yml @@ -0,0 +1,22 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: export const myFunk = () => {}; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk8.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk8.yml new file mode 100644 index 00000000000..08f782e335e --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk8.yml @@ -0,0 +1,22 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: const foo = function bar() {} + selections: + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} + marks: {} +finalState: + documentContents: "const foo = " + selections: + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk9.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk9.yml new file mode 100644 index 00000000000..8d2963bf705 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunk9.yml @@ -0,0 +1,18 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: namedFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: const foo = function bar() {} + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName2.yml new file mode 100644 index 00000000000..7926381d370 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName2.yml @@ -0,0 +1,22 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk name + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: functionName} + usePrePhraseSnapshot: true +initialState: + documentContents: export function *aaa() {} + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: export function *() {} + selections: + - anchor: {line: 0, character: 17} + active: {line: 0, character: 17} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName3.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName3.yml new file mode 100644 index 00000000000..56823dad79f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName3.yml @@ -0,0 +1,22 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk name + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: functionName} + usePrePhraseSnapshot: true +initialState: + documentContents: (function foo() {}) + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +finalState: + documentContents: (function () {}) + selections: + - anchor: {line: 0, character: 10} + active: {line: 0, character: 10} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName4.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName4.yml new file mode 100644 index 00000000000..054fcd26b5f --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName4.yml @@ -0,0 +1,22 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk name + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: functionName} + usePrePhraseSnapshot: true +initialState: + documentContents: export const myFunk = () => {}; + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +finalState: + documentContents: export const = () => {}; + selections: + - anchor: {line: 0, character: 13} + active: {line: 0, character: 13} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName5.yml new file mode 100644 index 00000000000..0da9351070d --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName5.yml @@ -0,0 +1,22 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk name + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: functionName} + usePrePhraseSnapshot: true +initialState: + documentContents: const foo = function bar() {} + selections: + - anchor: {line: 0, character: 12} + active: {line: 0, character: 12} + marks: {} +finalState: + documentContents: const foo = function () {} + selections: + - anchor: {line: 0, character: 21} + active: {line: 0, character: 21} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName6.yml new file mode 100644 index 00000000000..fda10309b58 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearFunkName6.yml @@ -0,0 +1,18 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear funk name + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: functionName} + usePrePhraseSnapshot: true +initialState: + documentContents: const foo = function bar() {} + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearLambda5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearLambda5.yml new file mode 100644 index 00000000000..fcec61dc966 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearLambda5.yml @@ -0,0 +1,22 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear lambda + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: anonymousFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: (function () {}) + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +finalState: + documentContents: () + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearLambda6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearLambda6.yml new file mode 100644 index 00000000000..0b2641cc14d --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearLambda6.yml @@ -0,0 +1,18 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear lambda + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: anonymousFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: (function bar() {}) + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearLambda7.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearLambda7.yml new file mode 100644 index 00000000000..2f6b3d76754 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearLambda7.yml @@ -0,0 +1,18 @@ +languageId: typescript +command: + version: 5 + spokenForm: clear lambda + action: {name: clearAndSetSelection} + targets: + - type: primitive + modifiers: + - type: containingScope + scopeType: {type: anonymousFunction} + usePrePhraseSnapshot: true +initialState: + documentContents: (function *bar() {}) + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +thrownError: {name: NoContainingScopeError} diff --git a/queries/javascript.core.scm b/queries/javascript.core.scm index 93ec9109aac..7e5f3f7629f 100644 --- a/queries/javascript.core.scm +++ b/queries/javascript.core.scm @@ -1,3 +1,5 @@ +;; import javascript.function.scm + (_ name: (_) @name ) @_.domain diff --git a/queries/javascript.function.scm b/queries/javascript.function.scm new file mode 100644 index 00000000000..a694e70c473 --- /dev/null +++ b/queries/javascript.function.scm @@ -0,0 +1,113 @@ +;; Anonymous functions +[ + ;; function() {} + (function + !name + ) + ;; function *() {} + (generator_function + !name + ) + ;; () => {} + (arrow_function) +] @anonymousFunction + +;; If we export an anonymous function as default, it semantically feels like a +;; named function. +(export_statement + [ + ;; export default function() {} + (function + !name + ) + ;; export default function *() {} + (generator_function + !name + ) + ;; export default () => {} + (arrow_function) + ] +) @namedFunction + +;; Named functions without export +( + [ + ;; function foo() {} + (function_declaration + name: (_) @functionName + ) + + ;; function *foo() {} + (generator_function_declaration + name: (_) @functionName + ) + + ;; (function foo() {}) + ;; Technically this doesn't need to exclude parent type of export_statement, + ;; because it can't actually appear as a direct child of an export_statement + ;; if it has a name, because it would be a function_declaration instead. it + ;; would need parentheses around it to be a function expression. + (function + name: (_) @functionName + ) + + ;; (function *foo() {}) + ;; Technically this doesn't need to exclude parent type of export_statement, + ;; for the same reason as the above with `function`. + (generator_function + name: (_) @functionName + ) + + ;; const foo = () => {} + ;; const foo = function() {} + ;; const foo = function *() {} + (lexical_declaration + (variable_declarator + name: (_) @functionName + [ + (function + !name + ) + (generator_function + !name + ) + (arrow_function) + ] + ) + ) + ] @namedFunction @functionName.domain + (#not-parent-type? @namedFunction export_statement) +) + +;; Exported named functions +(export_statement + [ + ;; export [default] function foo() {} + (function_declaration + name: (_) @functionName + ) + + ;; export [default] function *foo() {} + (generator_function_declaration + name: (_) @functionName + ) + + ;; export [default] const foo = () => {} + ;; export [default] const foo = function() {} + ;; export [default] const foo = function *() {} + (lexical_declaration + (variable_declarator + name: (_) @functionName + [ + (function + !name + ) + (generator_function + !name + ) + (arrow_function) + ] + ) + ) + ] +) @namedFunction @functionName.domain