diff --git a/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts b/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts index 8b99a39f4..568442c13 100644 --- a/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts +++ b/packages/cursorless-engine/src/languages/getTextFragmentExtractor.ts @@ -8,7 +8,6 @@ import { getNodeMatcher } from "./getNodeMatcher"; import { stringTextFragmentExtractor as htmlStringTextFragmentExtractor } from "./html"; import { stringTextFragmentExtractor as rubyStringTextFragmentExtractor } from "./ruby"; import { stringTextFragmentExtractor as scssStringTextFragmentExtractor } from "./scss"; -import { stringTextFragmentExtractor as typescriptStringTextFragmentExtractor } from "./typescript"; export type TextFragmentExtractor = ( node: SyntaxNode, @@ -131,14 +130,6 @@ const textFragmentExtractors: Record< "html", htmlStringTextFragmentExtractor, ), - javascript: constructDefaultTextFragmentExtractor( - "javascript", - typescriptStringTextFragmentExtractor, - ), - javascriptreact: constructDefaultTextFragmentExtractor( - "javascriptreact", - typescriptStringTextFragmentExtractor, - ), latex: fullDocumentTextFragmentExtractor, markdown: fullDocumentTextFragmentExtractor, ruby: constructDefaultTextFragmentExtractor( @@ -154,14 +145,6 @@ const textFragmentExtractors: Record< scssStringTextFragmentExtractor, ), rust: constructDefaultTextFragmentExtractor("rust"), - typescript: constructDefaultTextFragmentExtractor( - "typescript", - typescriptStringTextFragmentExtractor, - ), - typescriptreact: constructDefaultTextFragmentExtractor( - "typescriptreact", - typescriptStringTextFragmentExtractor, - ), xml: constructDefaultTextFragmentExtractor( "xml", htmlStringTextFragmentExtractor, diff --git a/packages/cursorless-engine/src/languages/typescript.ts b/packages/cursorless-engine/src/languages/typescript.ts index 23e0a51ad..23470a4cc 100644 --- a/packages/cursorless-engine/src/languages/typescript.ts +++ b/packages/cursorless-engine/src/languages/typescript.ts @@ -1,160 +1,12 @@ import { SimpleScopeTypeType } from "@cursorless/common"; -import type { SyntaxNode } from "web-tree-sitter"; -import { NodeMatcherAlternative, SelectionWithEditor } from "../typings/Types"; -import { patternFinder } from "../util/nodeFinders"; -import { - argumentMatcher, - cascadingMatcher, - conditionMatcher, - createPatternMatchers, - matcher, - patternMatcher, - trailingMatcher, -} from "../util/nodeMatchers"; -import { - childRangeSelector, - extendForwardPastOptional, - getNodeInternalRange, - getNodeRange, - unwrapSelectionExtractor, -} from "../util/nodeSelectors"; -import { branchMatcher } from "./branchMatcher"; -import { elseExtractor, elseIfExtractor } from "./elseIfExtractor"; -import { ternaryBranchMatcher } from "./ternaryBranchMatcher"; - -// Generated by the following command: -// > curl https://raw.githubusercontent.com/tree-sitter/tree-sitter-typescript/4c20b54771e4b390ee058af2930feb2cd55f2bf8/typescript/src/node-types.json \ -// | jq '[.[] | select(.type == "statement" or .type == "declaration") | .subtypes[].type]' -const STATEMENT_TYPES = [ - "abstract_class_declaration", - "ambient_declaration", - "break_statement", - "class_declaration", - "continue_statement", - "debugger_statement", - "declaration", - "do_statement", - "empty_statement", - "enum_declaration", - "export_statement", - "expression_statement", - "for_in_statement", - "for_statement", - "function_declaration", - "function_signature", - "generator_function_declaration", - "if_statement", - "import_alias", - "import_statement", - "interface_declaration", - "internal_module", - "labeled_statement", - "lexical_declaration", - "module", - "return_statement", - // "statement_block", This is disabled since we want the whole statement and not just the block - "switch_statement", - "throw_statement", - "try_statement", - "type_alias_declaration", - "variable_declaration", - "while_statement", - "with_statement", -]; - -const mapTypes = ["object", "object_pattern"]; -const listTypes = ["array", "array_pattern"]; +import { NodeMatcherAlternative } from "../typings/Types"; +import { argumentMatcher, createPatternMatchers } from "../util/nodeMatchers"; const nodeMatchers: Partial< Record > = { - map: mapTypes, - list: listTypes, - string: ["string", "template_string"], collectionItem: "jsx_attribute", - collectionKey: trailingMatcher( - [ - "pair[key]", - "jsx_attribute.property_identifier!", - "object_type.property_signature[name]!", - "shorthand_property_identifier", - ], - [":"], - ), - ifStatement: "if_statement", - comment: "comment", - regularExpression: "regex", - className: ["class_declaration[name]", "class[name]"], - functionCall: ["call_expression", "new_expression"], - functionCallee: cascadingMatcher( - patternMatcher("call_expression[function]"), - matcher( - patternFinder("new_expression"), - childRangeSelector(["arguments"], []), - ), - ), - statement: cascadingMatcher( - matcher( - patternFinder( - "property_signature", - "public_field_definition", - "abstract_method_signature", - ), - extendForwardPastOptional(";"), - ), - patternMatcher( - ...STATEMENT_TYPES.map((type) => `export_statement?.${type}`), - "method_definition", - ), - ), - condition: cascadingMatcher( - patternMatcher("ternary_expression[condition]"), - conditionMatcher( - "if_statement[condition]", - "for_statement[condition]", - "while_statement[condition]", - "do_statement[condition]", - ), - ), - ["private.switchStatementSubject"]: matcher( - patternFinder("switch_statement[value]"), - unwrapSelectionExtractor, - ), - branch: cascadingMatcher( - patternMatcher("switch_case"), - matcher(patternFinder("else_clause"), elseExtractor("if_statement")), - matcher(patternFinder("if_statement"), elseIfExtractor()), - branchMatcher("try_statement", ["catch_clause", "finally_clause"]), - ternaryBranchMatcher("ternary_expression", [1, 2]), - ), - class: [ - "export_statement?.class_declaration", // export class | class - "export_statement?.abstract_class_declaration", // export abstract class | abstract class - "export_statement.class", // export default class - ], argumentOrParameter: argumentMatcher("formal_parameters", "arguments"), - // XML, JSX - attribute: ["jsx_attribute"], }; export const patternMatchers = createPatternMatchers(nodeMatchers); - -export function stringTextFragmentExtractor( - node: SyntaxNode, - _selection: SelectionWithEditor, -) { - if ( - node.type === "string_fragment" || - node.type === "regex_pattern" || - node.type === "jsx_text" - ) { - return getNodeRange(node); - } - - if (node.type === "template_string") { - // Exclude starting and ending quotation marks - return getNodeInternalRange(node); - } - - return null; -} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee5.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee5.yml index d7860bc9b..416c3ddce 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee5.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee5.yml @@ -13,7 +13,7 @@ initialState: active: {line: 0, character: 24} marks: {} finalState: - documentContents: let test = new () + documentContents: let test = () selections: - - anchor: {line: 0, character: 15} - active: {line: 0, character: 15} + - anchor: {line: 0, character: 11} + active: {line: 0, character: 11} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee6.yml index 26ab2b805..051a48e28 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee6.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee6.yml @@ -13,7 +13,7 @@ initialState: active: {line: 0, character: 28} marks: {} finalState: - documentContents: let test = new () + documentContents: let test = () selections: - - anchor: {line: 0, character: 15} - active: {line: 0, character: 15} + - anchor: {line: 0, character: 11} + active: {line: 0, character: 11} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee7.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee7.yml index 9987c91ce..1697ce71e 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee7.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/changeCallee7.yml @@ -13,7 +13,7 @@ initialState: active: {line: 0, character: 24} marks: {} finalState: - documentContents: let test = new () + documentContents: let test = () selections: - - anchor: {line: 0, character: 15} - active: {line: 0, character: 15} + - anchor: {line: 0, character: 11} + active: {line: 0, character: 11} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType6.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType6.yml new file mode 100644 index 000000000..c0a5aab13 --- /dev/null +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/chuckType6.yml @@ -0,0 +1,23 @@ +languageId: typescript +command: + version: 6 + spokenForm: chuck type + action: + name: remove + target: + type: primitive + modifiers: + - type: containingScope + scopeType: {type: type} + usePrePhraseSnapshot: true +initialState: + documentContents: foo + selections: + - anchor: {line: 0, character: 1} + active: {line: 0, character: 1} + marks: {} +finalState: + documentContents: foo + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/takeEveryKey.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryKey.yml similarity index 75% rename from packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/takeEveryKey.yml rename to packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryKey.yml index 39840a14b..131b05eba 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/takeEveryKey.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryKey.yml @@ -1,8 +1,8 @@ languageId: typescript command: version: 1 - spokenForm: take every key - action: setSelection + spokenForm: change every key + action: clearAndSetSelection targets: - type: primitive modifier: {type: containingScope, scopeType: collectionKey, includeSiblings: true} @@ -19,11 +19,11 @@ initialState: finalState: documentContents: |- const value = { - key1: "hello", - key2: "there", + : "hello", + : "there", }; selections: - anchor: {line: 1, character: 4} - active: {line: 1, character: 8} + active: {line: 1, character: 4} - anchor: {line: 2, character: 4} - active: {line: 2, character: 8} + active: {line: 2, character: 4} diff --git a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/takeEveryKey2.yml b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryKey2.yml similarity index 74% rename from packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/takeEveryKey2.yml rename to packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryKey2.yml index aa603ec32..eb4325475 100644 --- a/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/takeEveryKey2.yml +++ b/packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/typescript/clearEveryKey2.yml @@ -1,8 +1,8 @@ languageId: typescript command: version: 1 - spokenForm: take every key - action: setSelection + spokenForm: change every key + action: clearAndSetSelection targets: - type: primitive modifier: {type: containingScope, scopeType: collectionKey, includeSiblings: true} @@ -19,11 +19,11 @@ initialState: finalState: documentContents: |- { - foo: "hello", - bar, + : "hello", + , } selections: - anchor: {line: 1, character: 4} - active: {line: 1, character: 7} + active: {line: 1, character: 4} - anchor: {line: 2, character: 4} - active: {line: 2, character: 7} + active: {line: 2, character: 4} diff --git a/queries/javascript.core.scm b/queries/javascript.core.scm index 6fa676b5f..b9745d7f3 100644 --- a/queries/javascript.core.scm +++ b/queries/javascript.core.scm @@ -352,20 +352,9 @@ (#not-parent-type? @_.domain expression_statement) ) -;; Match nodes at field `value` of their parent node, setting leading delimiter -;; to be the range until the previous named node -( - (_ - (_)? @value.leading.start.endOf - . - value: (_) @value @value.leading.end.startOf - ) @_.domain - (#not-type? @_.domain variable_declarator) -) - ;;!! const aaa = {bbb}; ;;! ^^^ -(shorthand_property_identifier) @value +(shorthand_property_identifier) @collectionKey @value ;;!! return 0; ;;! ^ @@ -415,6 +404,263 @@ ;;!! const aaa = {bbb: 0, ccc: 0}; ;;! ************** (object - "{" @value.iteration.start.endOf - "}" @value.iteration.end.startOf + "{" @collectionKey.iteration.start.endOf @value.iteration.start.endOf + "}" @collectionKey.iteration.end.startOf @value.iteration.end.startOf +) + +;;!! "string" +;;!! `string` +;;! ^^^^^^^^ +[ + (string) + (template_string) +] @string + +;;!! // comment +;;! ^^^^^^^^^^ +(comment) @comment + +;;!! /\w+/ +;;! ^^^^^ +(regex) @regularExpression + +[ + (string_fragment) + (comment) + (regex_pattern) +] @textFragment + +( + (template_string) @textFragment + (#child-range! @textFragment 0 -1 true true) +) + +;;!! { value: 0 } +;;! ^^^^^^^^^^^^ +[ + (object) + (object_pattern) +] @map + +;;!! [ 0 ] +;;! ^^^^^ +[ + (array) + (array_pattern) +] @list + +;;!! if () {} +;;! ^^^^^^^^ +(if_statement) @ifStatement + +;;!! switch (value) {} +;;! ^^^^^ +;;! ----------------- +(switch_statement + value: (_) @private.switchStatementSubject + (#child-range! @private.switchStatementSubject 0 -1 true true) +) @_.domain + +;;!! foo() +;;! ^^^^^ +;;!! new Foo() +;;! ^^^^^^^^^ +[ + (call_expression) + (new_expression) +] @functionCall + +;;!! foo() +;;! ^^^ +;;! ----- +(call_expression + function: (_) @functionCallee +) @_.domain + +;;!! new Foo() +;;! ^^^^^^^ +;;! --------- +(new_expression + (arguments) @functionCallee.end.startOf +) @functionCallee.start.startOf @_.domain + +;;!! class Foo {} +;;! ^^^^^^^^^^^^ +( + [ + (class_declaration + name: (_) @className + ) + (class + name: (_) @className + ) + ] @class @_.domain + (#not-parent-type? @class export_statement) +) + +;;!! export class Foo {} +;;! ^^^^^^^^^^^^^^^^^^^ +(export_statement + [ + (class_declaration + name: (_) @className + ) + (class + name: (_) @className + ) + ] +) @class @_.domain + +;;!! true ? 0 : 1; +;;! ^^^^ +;;! ^ ^ +;;! -------------- +(ternary_expression + condition: (_) @condition + consequence: (_) @branch +) @condition.domain +(ternary_expression + alternative: (_) @branch +) + +;;!! for (let i = 0; i < 2; ++i) {} +;;! ^^^^^ +;;! ------------------------------ +(for_statement + condition: (_) @condition + (#child-range! @condition 0 -1 false true) +) @_.domain + +;;!! while (true) {} +;;! ^^^^ +(while_statement + condition: (_) @condition + (#child-range! @condition 0 -1 true true) +) @_.domain + +;;!! do {} while (true); +;;! ^^^^ +(do_statement + condition: (_) @condition + (#child-range! @condition 0 -1 true true) +) @_.domain + +;;!! case 0: {} +;;! ^^^^^^^^^^ +;;! ^ +(switch_case + value: (_) @condition +) @branch @condition.domain + +;;!! switch () {} +;;! ^^^^^^^^^^^^ +(switch_statement) @branch.iteration @condition.iteration + +;;!! if () {} +;;! ^^^^^^^^ +( + (if_statement + condition: (_) @condition + consequence: (_) @branch.end.endOf @branch.removal.end.endOf + alternative: (_ + (if_statement) @branch.removal.end.startOf + )? + ) @branch.start.startOf @branch.removal.start.startOf @condition.domain + (#not-parent-type? @condition.domain "else_clause") + (#child-range! @condition 0 -1 true true) +) + +;;!! else if () {} +;;! ^^^^^^^^^^^^^ +(else_clause + (if_statement + condition: (_) @condition + consequence: (_) @branch.end.endOf @condition.domain.end.endOf + ) + (#child-range! @condition 0 -1 true true) +) @branch.start.startOf @condition.domain.start.startOf + +;;!! else {} +;;! ^^^^^^^ +(else_clause + (statement_block) +) @branch + +;;!! if () {} else if () {} else {} +;;! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +( + (if_statement) @branch.iteration + (#not-parent-type? @branch.iteration "else_clause") +) + +;;!! try () {} +;;! ^^^^^^^^^ +(try_statement + "try" @branch.start + body: (_) @branch.end +) + +;;!! catch () {} +;;! ^^^^^^^^^^^ +(try_statement + handler: (_) @branch +) + +;;!! finally {} +;;! ^^^^^^^^^^ +(try_statement + finalizer: (_) @branch +) + +;;!! try () {} catch () {} finally {} +;;! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +(try_statement) @branch.iteration + +;;!! { value: 0 } +;;! ^^^^^ +;;! xxxxxxx +;;!! { value: 0 } +;;! ^ +;;! xxx +;;! -------- +(pair + key: (_) @collectionKey @collectionKey.trailing.start.endOf @value.leading.start.endOf + value: (_) @value @collectionKey.trailing.end.startOf @value.leading.end.startOf +) @_.domain + +;; Statements that are not a child of an export statement +;; Generated by the following command: +;; > curl https://raw.githubusercontent.com/tree-sitter/tree-sitter-typescript/4c20b54771e4b390ee058af2930feb2cd55f2bf8/typescript/src/node-types.json \ +;; | jq '[.[] | select(.type == "statement" or .type == "declaration") | .subtypes[].type]' +( + [ + (break_statement) + (class_declaration) + (continue_statement) + (debugger_statement) + (declaration) + (do_statement) + (empty_statement) + (export_statement) + (expression_statement) + (for_in_statement) + (for_statement) + (generator_function_declaration) + (if_statement) + (import_statement) + (labeled_statement) + (lexical_declaration) + (return_statement) + ;; (statement_block), This is disabled since we want the whole statement and not just the block + (switch_statement) + (throw_statement) + (try_statement) + (variable_declaration) + (while_statement) + (with_statement) + + ;; Manually added1 + (method_definition) + ] @statement + (#not-parent-type? @statement export_statement) ) diff --git a/queries/javascript.jsx.scm b/queries/javascript.jsx.scm index 129f9da20..2753e13d4 100644 --- a/queries/javascript.jsx.scm +++ b/queries/javascript.jsx.scm @@ -82,6 +82,16 @@ ">" @name.startOf @_.domain.end ) +;;!! +;;! ^^^^^^^^^ +(jsx_attribute) @attribute + +;;!! +;;! ^^^ +(jsx_attribute + (property_identifier) @collectionKey +) @_.domain + ;;!! ;;! ^^^^^ ;;! xxxxxx @@ -91,12 +101,20 @@ (_) @value @value.leading.end.startOf ) @_.domain +;;!! +;;! ^^^^ (jsx_self_closing_element - "<" @value.iteration.start.endOf - "/" @value.iteration.end.startOf + "<" @attribute.iteration.start.endOf @collectionKey.iteration.start.endOf @value.iteration.start.endOf + "/" @attribute.iteration.end.startOf @collectionKey.iteration.end.startOf @value.iteration.end.startOf ) +;;!! +;;! ^^^ (jsx_opening_element - "<" @value.iteration.start.endOf - ">" @value.iteration.end.startOf + "<" @attribute.iteration.start.endOf @collectionKey.iteration.start.endOf @value.iteration.start.endOf + ">" @attribute.iteration.end.startOf @collectionKey.iteration.end.startOf @value.iteration.end.startOf ) + +;;!!
text
+;;! ^^^^ +(jsx_text) @textFragment diff --git a/queries/typescript.core.scm b/queries/typescript.core.scm index d1692906e..f2a6998cb 100644 --- a/queries/typescript.core.scm +++ b/queries/typescript.core.scm @@ -151,21 +151,28 @@ (#has-multiple-children-of-type? @dummy variable_declarator) ) -;; Generic type matcher -( +;;!! function ccc(aaa: string, bbb?: string) {} +;;! ^^^^^^ ^^^^^^ +(formal_parameters (_ - [ - type: (_ - (_) @type - ) - return_type: (_ - (_) @type - ) - ] @type.removal + pattern: (_) @type.leading.start.endOf + type: (_ + ":" + (_) @type @type.leading.end.startOf + ) ) @_.domain - (#not-type? @_.domain variable_declarator) ) +;;!! function ccc(): string {} +;;! ^^^^^^ +(function_declaration + parameters: (_) @type.leading.end.endOf + return_type: (_ + ":" + (_) @type @type.leading.end.startOf + ) +) @_.domain + ;;!! new Aaa() ;;! ^^^^^^^^ (new_expression @@ -180,7 +187,8 @@ ;;! ^^^^^^ ^^^^^^ (type_arguments (_) @type -) + (#not-parent-type? @dummy type_assertion) +) @dummy ;;!! function foo() {} ;;! ^ @@ -229,3 +237,69 @@ (predefined_type) ] @type @_.leading.end.startOf ) @_.domain + +;;!! abstract class MyClass {} +;;! ^^^^^^^^^^^^^^^^^^^^^^^^^ +( + (abstract_class_declaration + name: (_) @className + ) @class @_.domain + (#not-parent-type? @class export_statement) +) + +;;!! export abstract class MyClass {} +;;! ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +(export_statement + (abstract_class_declaration + name: (_) @className + ) +) @class @_.domain + +;;!! class MyClass {} +;;! ^^^^^^^ +;;! ---------------- +(abstract_class_declaration + name: (_) @name +) @_.domain + +;;!! interface Type { name: string; } +;;! ^^^^ +;;! xxxxxx +;;! ------------ +(property_signature + name: (_) @collectionKey @collectionKey.trailing.start.endOf @type.leading.start.endOf + type: (_ + ":" + (_) @type @collectionKey.trailing.end.startOf @type.leading.end.startOf + ) +) @_.domain + +;;!! interface Type { name: string; } +;;! ^^^^^^^^^^^^^^^^^ +(object_type) @collectionKey.iteration + +;; Non-exported statements +( + [ + (ambient_declaration) + (abstract_class_declaration) + (enum_declaration) + (function_signature) + (import_alias) + (interface_declaration) + (internal_module) + (module) + (type_alias_declaration) + ] @statement + (#not-parent-type? @statement export_statement) +) + +;; Statements with optional trailing `;` +( + [ + (property_signature) + (public_field_definition) + (abstract_method_signature) + ] @statement.start + ";"? @statement.end +) diff --git a/queries/typescript.scm b/queries/typescript.scm index f885360bd..f98a44550 100644 --- a/queries/typescript.scm +++ b/queries/typescript.scm @@ -8,9 +8,6 @@ ;;! -------- (type_assertion (type_arguments - [ - (generic_type) - (predefined_type) - ] @type + (_) @type ) @_.removal ) @_.domain