Skip to content

Commit 603dbd0

Browse files
purplePtrace-andreasonpre-commit-ci[bot]pokey
authored
Feature/rust support (#775)
* initial commit * add function call * wrapping things up * combining ideas from other PR * adding tests + struct things * Cleanup * Add self parameter matcher * Cleanup * WIP * Add value support * Add change state test * Treat struct field type as `type` * Treat traits as `type` * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix rust statements * initial commit * add function call * wrapping things up * combining ideas from other PR * adding tests + struct things * Cleanup * Add self parameter matcher * Cleanup * WIP * Add value support * Add change state test * Treat struct field type as `type` * Treat traits as `type` * WIP * WIP * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add support for return values from anywhere in the function * WIP * WIP * WIP * WIP * WIP * Support attributes * WIP * Add tests for function return type * Add tests for `name` scope in function * Add tests for changing a type of the value * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * WIP * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * WIP * Clean up imports * Cleanup * WIP * Support `type` scope in more places Co-authored-by: Trace Andreason <[email protected]> Co-authored-by: Trace Andreason <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Pokey Rule <[email protected]>
1 parent b0837f1 commit 603dbd0

File tree

96 files changed

+3115
-3
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+3115
-3
lines changed

src/languages/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export const supportedLanguageIds = [
1717
"ruby",
1818
"scala",
1919
"scss",
20+
"rust",
2021
"typescript",
2122
"typescriptreact",
2223
"xml",

src/languages/getNodeMatcher.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import scala from "./scala";
2121
import { patternMatchers as scss } from "./scss";
2222
import go from "./go";
2323
import { patternMatchers as ruby } from "./ruby";
24+
import rust from "./rust";
2425
import { UnsupportedLanguageError } from "../errors";
2526
import { SupportedLanguageId } from "./constants";
2627

@@ -70,6 +71,7 @@ const languageMatchers: Record<
7071
ruby,
7172
scala,
7273
scss,
74+
rust,
7375
typescript,
7476
typescriptreact: typescript,
7577
xml: html,

src/languages/getTextFragmentExtractor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ const textFragmentExtractors: Record<
179179
"scss",
180180
scssStringTextFragmentExtractor
181181
),
182+
rust: constructDefaultTextFragmentExtractor("rust"),
182183
typescript: constructDefaultTextFragmentExtractor(
183184
"typescript",
184185
typescriptStringTextFragmentExtractor

src/languages/rust.ts

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
import { TextEditor } from "vscode";
2+
import { SyntaxNode } from "web-tree-sitter";
3+
import { SimpleScopeTypeType } from "../typings/targetDescriptor.types";
4+
import { NodeMatcherAlternative, SelectionWithContext } from "../typings/Types";
5+
import { patternFinder } from "../util/nodeFinders";
6+
import {
7+
ancestorChainNodeMatcher,
8+
argumentMatcher,
9+
cascadingMatcher,
10+
createPatternMatchers,
11+
leadingMatcher,
12+
matcher,
13+
patternMatcher,
14+
trailingMatcher,
15+
} from "../util/nodeMatchers";
16+
import {
17+
makeNodePairSelection,
18+
makeRangeFromPositions,
19+
} from "../util/nodeSelectors";
20+
21+
// Generated by the following command:
22+
// `curl https://raw.githubusercontent.com/tree-sitter/tree-sitter-rust/36ae187ed6dd3803a8a89dbb54f3124c8ee74662/src/node-types.STATEMENT_TYPES | jq '[.[] | select(.type == "_declaration_statement") | .subtypes[].type, "expression_statement"]'`
23+
const STATEMENT_TYPES = [
24+
"associated_type",
25+
"attribute_item",
26+
"const_item",
27+
"empty_statement",
28+
"enum_item",
29+
"extern_crate_declaration",
30+
"foreign_mod_item",
31+
"impl_item",
32+
"inner_attribute_item",
33+
"let_declaration",
34+
"macro_definition",
35+
"macro_invocation",
36+
"function_item",
37+
"function_signature_item",
38+
"mod_item",
39+
"static_item",
40+
"struct_item",
41+
"trait_item",
42+
"type_item",
43+
"union_item",
44+
"use_declaration",
45+
"expression_statement",
46+
];
47+
48+
/**
49+
* Scope types allowed to be parents of a statement
50+
*/
51+
const STATEMENT_PARENT_TYPES = ["source_file", "block", "declaration_list"];
52+
53+
/**
54+
* Returns "impl_item[type]" node higher in the chain
55+
* @param node The node which we will start our search from
56+
* @returns node or null
57+
*/
58+
function implItemTypeFinder(node: SyntaxNode) {
59+
if (
60+
node.parent?.type === "impl_item" &&
61+
node.parent?.childForFieldName("type")?.equals(node)
62+
) {
63+
return node;
64+
}
65+
return null;
66+
}
67+
68+
function traitBoundExtractor(
69+
editor: TextEditor,
70+
node: SyntaxNode
71+
): SelectionWithContext {
72+
return {
73+
selection: makeNodePairSelection(node.children[1], node.lastNamedChild!),
74+
context: {
75+
leadingDelimiterRange: makeRangeFromPositions(
76+
node.children[0].startPosition,
77+
node.children[1].startPosition
78+
),
79+
},
80+
};
81+
}
82+
83+
/**
84+
* Returns the return value node for a given block if we are in a block that has
85+
* a return value. If the return value expression uses the return keyword then
86+
* we return the value itself otherwise we just return the expression
87+
* @param node The node which we might match
88+
* @returns The return value node
89+
*/
90+
function returnValueFinder(node: SyntaxNode) {
91+
if (node.type !== "block") {
92+
return null;
93+
}
94+
95+
const { lastNamedChild } = node;
96+
97+
// The return expression will always be the last statement or expression in
98+
// the block
99+
if (lastNamedChild == null) {
100+
return null;
101+
}
102+
103+
// If the final name child is an expression statement not a raw expression
104+
// then we only treat it as a return value if it is a return expression.
105+
// Otherwise it is just a normal statement that doesn't return anything
106+
if (lastNamedChild.type === "expression_statement") {
107+
const expression = lastNamedChild.child(0)!;
108+
109+
if (expression.type === "return_expression") {
110+
return expression.child(1);
111+
}
112+
113+
return null;
114+
}
115+
116+
// Any other type of statement is not a return statement so we should not
117+
// match it
118+
if (STATEMENT_TYPES.includes(lastNamedChild.type)) {
119+
return null;
120+
}
121+
122+
// NB: At this point we have now excluded all statement types of the only other
123+
// possible node is an expression node
124+
125+
// If it is a return expression then we zoom down to the actual value of the
126+
// return expression. This happens when they say `return foo` with no
127+
// trailing semicolon
128+
if (lastNamedChild.type === "return_expression") {
129+
return lastNamedChild.child(1);
130+
}
131+
132+
// At this point it is an expression which is not a return expression so we
133+
// just return it as the return value of the block
134+
return lastNamedChild;
135+
}
136+
137+
const nodeMatchers: Partial<
138+
Record<SimpleScopeTypeType, NodeMatcherAlternative>
139+
> = {
140+
statement: ancestorChainNodeMatcher(
141+
[
142+
patternFinder(...STATEMENT_PARENT_TYPES),
143+
patternFinder(...STATEMENT_TYPES),
144+
],
145+
1
146+
),
147+
string: ["raw_string_literal", "string_literal"],
148+
ifStatement: ["if_expression", "if_let_expression"],
149+
functionCall: ["call_expression", "macro_invocation", "struct_expression"],
150+
functionCallee: "call_expression[function]",
151+
comment: ["line_comment", "block_comment"],
152+
list: ["array_expression", "tuple_expression"],
153+
collectionItem: argumentMatcher("array_expression", "tuple_expression"),
154+
namedFunction: "function_item",
155+
type: cascadingMatcher(
156+
leadingMatcher(
157+
[
158+
"let_declaration[type]",
159+
"parameter[type]",
160+
"field_declaration[type]",
161+
"const_item[type]",
162+
],
163+
[":"]
164+
),
165+
matcher(
166+
patternFinder(
167+
"constrained_type_parameter[bounds]",
168+
"where_predicate[bounds]"
169+
),
170+
traitBoundExtractor
171+
),
172+
leadingMatcher(["function_item[return_type]"], ["->"]),
173+
matcher(implItemTypeFinder),
174+
patternMatcher(
175+
"struct_item",
176+
"trait_item",
177+
"impl_item",
178+
"array_type[element]"
179+
)
180+
),
181+
functionName: ["function_item[name]"],
182+
anonymousFunction: "closure_expression",
183+
argumentOrParameter: argumentMatcher(
184+
"arguments",
185+
"parameters",
186+
"meta_arguments",
187+
"type_parameters"
188+
),
189+
name: [
190+
"let_declaration.identifier!",
191+
"parameter.identifier!",
192+
"function_item[name]",
193+
"struct_item[name]",
194+
"enum_item[name]",
195+
"trait_item[name]",
196+
"const_item[name]",
197+
"meta_item.identifier!",
198+
"let_declaration[pattern]",
199+
"constrained_type_parameter[left]",
200+
"where_predicate[left]",
201+
],
202+
class: ["struct_item", "struct_expression", "enum_item"],
203+
className: ["struct_item[name]", "enum_item[name]", "trait_item[name]"],
204+
value: cascadingMatcher(
205+
leadingMatcher(["let_declaration[value]"], ["="]),
206+
patternMatcher("meta_item[value]"),
207+
matcher(returnValueFinder)
208+
),
209+
attribute: trailingMatcher(["mutable_specifier", "attribute_item"]),
210+
};
211+
212+
export default createPatternMatchers(nodeMatchers);
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
languageId: rust
2+
command:
3+
spokenForm: change arg air
4+
version: 2
5+
targets:
6+
- type: primitive
7+
mark: {type: decoratedSymbol, symbolColor: default, character: a}
8+
modifiers:
9+
- type: containingScope
10+
scopeType: {type: argumentOrParameter}
11+
usePrePhraseSnapshot: true
12+
action: {name: clearAndSetSelection}
13+
initialState:
14+
documentContents: |
15+
fn foo(x: &'a mut u32) -> &'static mut u16 {}
16+
selections:
17+
- anchor: {line: 0, character: 45}
18+
active: {line: 0, character: 45}
19+
marks:
20+
default.a:
21+
start: {line: 0, character: 12}
22+
end: {line: 0, character: 13}
23+
finalState:
24+
documentContents: |
25+
fn foo() -> &'static mut u16 {}
26+
selections:
27+
- anchor: {line: 0, character: 7}
28+
active: {line: 0, character: 7}
29+
thatMark:
30+
- anchor: {line: 0, character: 7}
31+
active: {line: 0, character: 7}
32+
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: a}, modifiers: [{type: containingScope, scopeType: {type: argumentOrParameter}}]}]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
languageId: rust
2+
command:
3+
spokenForm: change arg blue air
4+
version: 2
5+
targets:
6+
- type: primitive
7+
mark: {type: decoratedSymbol, symbolColor: blue, character: a}
8+
modifiers:
9+
- type: containingScope
10+
scopeType: {type: argumentOrParameter}
11+
usePrePhraseSnapshot: true
12+
action: {name: clearAndSetSelection}
13+
initialState:
14+
documentContents: |
15+
struct S<'a, 'b: 'a, T>;
16+
selections:
17+
- anchor: {line: 1, character: 0}
18+
active: {line: 1, character: 0}
19+
marks:
20+
blue.a:
21+
start: {line: 0, character: 18}
22+
end: {line: 0, character: 19}
23+
finalState:
24+
documentContents: |
25+
struct S<'a, , T>;
26+
selections:
27+
- anchor: {line: 0, character: 13}
28+
active: {line: 0, character: 13}
29+
thatMark:
30+
- anchor: {line: 0, character: 13}
31+
active: {line: 0, character: 13}
32+
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: blue, character: a}, modifiers: [{type: containingScope, scopeType: {type: argumentOrParameter}}]}]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
languageId: rust
2+
command:
3+
spokenForm: change arg sun
4+
version: 2
5+
targets:
6+
- type: primitive
7+
mark: {type: decoratedSymbol, symbolColor: default, character: s}
8+
modifiers:
9+
- type: containingScope
10+
scopeType: {type: argumentOrParameter}
11+
usePrePhraseSnapshot: true
12+
action: {name: clearAndSetSelection}
13+
initialState:
14+
documentContents: >-
15+
fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32
16+
{
17+
18+
}
19+
selections:
20+
- anchor: {line: 1, character: 1}
21+
active: {line: 1, character: 1}
22+
marks:
23+
default.s:
24+
start: {line: 0, character: 20}
25+
end: {line: 0, character: 27}
26+
finalState:
27+
documentContents: |-
28+
fn some_function<, U: Clone + Debug>(t: &T, u: &U) -> i32 {
29+
}
30+
selections:
31+
- anchor: {line: 0, character: 17}
32+
active: {line: 0, character: 17}
33+
thatMark:
34+
- anchor: {line: 0, character: 17}
35+
active: {line: 0, character: 17}
36+
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: s}, modifiers: [{type: containingScope, scopeType: {type: argumentOrParameter}}]}]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
languageId: rust
2+
command:
3+
spokenForm: change arg trap
4+
version: 2
5+
targets:
6+
- type: primitive
7+
mark: {type: decoratedSymbol, symbolColor: default, character: t}
8+
modifiers:
9+
- type: containingScope
10+
scopeType: {type: argumentOrParameter}
11+
usePrePhraseSnapshot: true
12+
action: {name: clearAndSetSelection}
13+
initialState:
14+
documentContents: |
15+
struct S<'a, 'b: 'a, T> {};
16+
selections:
17+
- anchor: {line: 1, character: 0}
18+
active: {line: 1, character: 0}
19+
marks:
20+
default.t:
21+
start: {line: 0, character: 21}
22+
end: {line: 0, character: 22}
23+
finalState:
24+
documentContents: |
25+
struct S<'a, 'b: 'a, > {};
26+
selections:
27+
- anchor: {line: 0, character: 21}
28+
active: {line: 0, character: 21}
29+
thatMark:
30+
- anchor: {line: 0, character: 21}
31+
active: {line: 0, character: 21}
32+
fullTargets: [{type: primitive, mark: {type: decoratedSymbol, symbolColor: default, character: t}, modifiers: [{type: containingScope, scopeType: {type: argumentOrParameter}}]}]

0 commit comments

Comments
 (0)