Skip to content

Commit e8a7509

Browse files
committed
Fix wrapping
1 parent 931d153 commit e8a7509

File tree

4 files changed

+118
-17
lines changed

4 files changed

+118
-17
lines changed

cursorless-snippets/functionDeclaration.cursorless-snippets

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,28 @@
6262
"formatter": "snakeCase"
6363
}
6464
}
65+
},
66+
{
67+
"scope": {
68+
"langIds": [
69+
"python"
70+
],
71+
"scopeTypes": [
72+
"class"
73+
],
74+
"excludeDescendantScopeTypes": [
75+
"namedFunction"
76+
]
77+
},
78+
"body": [
79+
"def $name(self${parameterList:, }):",
80+
"\t$body"
81+
],
82+
"variables": {
83+
"name": {
84+
"formatter": "snakeCase"
85+
}
86+
}
6587
}
6688
],
6789
"description": "Function declaration",

packages/cursorless-engine/src/snippets/snippet.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -110,27 +110,13 @@ export function findMatchingSnippetDefinitionStrict(
110110
return definitions[definitionIndex];
111111
}
112112

113-
/**
114-
* [matches, index, target]
115-
*/
116-
type MatchResult = [boolean, number, Target[] | null];
117-
118113
/**
119114
* Based on the context determined by {@link target} (eg the file's language id
120115
* and containing scope), finds the best snippet definition that matches the
121116
* given context. Returns -1 if no matching snippet definition could be found.
122117
*
123-
* Prefers snippet definitions that specify a scope type. If multiple snippet
124-
* definitions specify a scope type, then we prefer the one whose matched scope
125-
* has the smallest range in the document.
126-
*
127-
* If no snippet definitions specify a scope type, then we break ties based on
128-
* which specify language id. If that doesn't break the tie, then we prefer
129-
* user defined snippets over built-in snippets.
130-
*
131-
* Although we directly check matched scope range, we don't check other
132-
* precedences (eg lang id or user vs core), because the definitions are
133-
* guaranteed to come sorted in precedence order.
118+
* We assume that the definitions are sorted in precedence order, so we just
119+
* return the first match we find.
134120
*
135121
* @param modifierStageFactory For creating containing scope modifiers
136122
* @param target The target to find a matching snippet definition for
@@ -145,6 +131,9 @@ function findMatchingSnippetDefinitionForSingleTarget(
145131
): number {
146132
const languageId = target.editor.document.languageId;
147133

134+
// We want to find the first definition that matches the given context.
135+
// Note that we just use the first match we find because the definitions are
136+
// guaranteed to come sorted in precedence order.
148137
return definitions.findIndex(({ scope }) => {
149138
if (scope == null) {
150139
return true;
@@ -164,13 +153,26 @@ function findMatchingSnippetDefinitionForSingleTarget(
164153
let matchingScopeType: SimpleScopeTypeType | undefined = undefined;
165154
for (const scopeTypeType of allScopeTypes) {
166155
try {
167-
const containingTarget = modifierStageFactory
156+
let containingTarget = modifierStageFactory
168157
.create({
169158
type: "containingScope",
170159
scopeType: { type: scopeTypeType },
171160
})
172161
.run(target)[0];
173162

163+
if (target.contentRange.isRangeEqual(containingTarget.contentRange)) {
164+
// Skip this scope if the target is exactly the same as the
165+
// containing scope, otherwise wrapping won't work, because we're
166+
// really outside the containing scope when we're wrapping
167+
containingTarget = modifierStageFactory
168+
.create({
169+
type: "containingScope",
170+
scopeType: { type: scopeTypeType },
171+
ancestorIndex: 1,
172+
})
173+
.run(target)[0];
174+
}
175+
174176
if (
175177
matchingTarget == null ||
176178
matchingTarget.contentRange.contains(containingTarget.contentRange)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
languageId: python
2+
command:
3+
version: 5
4+
spokenForm: funk wrap class
5+
action:
6+
name: wrapWithSnippet
7+
args:
8+
- {type: named, name: functionDeclaration, variableName: body}
9+
targets:
10+
- type: primitive
11+
modifiers:
12+
- type: containingScope
13+
scopeType: {type: class}
14+
usePrePhraseSnapshot: true
15+
initialState:
16+
documentContents: |-
17+
class Aaa:
18+
def bbb():
19+
pass
20+
selections:
21+
- anchor: {line: 2, character: 8}
22+
active: {line: 2, character: 8}
23+
marks: {}
24+
finalState:
25+
documentContents: |-
26+
def ():
27+
class Aaa:
28+
def bbb():
29+
pass
30+
selections:
31+
- anchor: {line: 0, character: 4}
32+
active: {line: 0, character: 4}
33+
thatMark:
34+
- type: UntypedTarget
35+
contentRange:
36+
start: {line: 0, character: 0}
37+
end: {line: 3, character: 16}
38+
isReversed: false
39+
hasExplicitRange: true
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
languageId: typescript
2+
command:
3+
version: 5
4+
spokenForm: snip funk to class
5+
action:
6+
name: insertSnippet
7+
args:
8+
- {type: named, name: functionDeclaration}
9+
targets:
10+
- type: primitive
11+
modifiers:
12+
- type: containingScope
13+
scopeType: {type: class}
14+
usePrePhraseSnapshot: true
15+
initialState:
16+
documentContents: |-
17+
class Aaa {
18+
19+
}
20+
selections:
21+
- anchor: {line: 1, character: 4}
22+
active: {line: 1, character: 4}
23+
marks: {}
24+
finalState:
25+
documentContents: |-
26+
function () {
27+
28+
}
29+
selections:
30+
- anchor: {line: 0, character: 9}
31+
active: {line: 0, character: 9}
32+
thatMark:
33+
- type: UntypedTarget
34+
contentRange:
35+
start: {line: 0, character: 0}
36+
end: {line: 2, character: 1}
37+
isReversed: false
38+
hasExplicitRange: true

0 commit comments

Comments
 (0)