Skip to content

Commit 2a92a04

Browse files
Properly handle snippet migration with multiple different snippets in one file
1 parent f3cd89b commit 2a92a04

File tree

2 files changed

+197
-20
lines changed

2 files changed

+197
-20
lines changed
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
{
2+
"mobxConstructor": {
3+
"definitions": [
4+
{
5+
"scope": {
6+
"langIds": [
7+
"typescript",
8+
"javascript",
9+
"typescriptreact",
10+
"javascriptreact"
11+
]
12+
},
13+
"body": [
14+
"constructor($parameters) {",
15+
"\tmakeAutoObservable(this);",
16+
"}"
17+
]
18+
}
19+
],
20+
"description": "Constructor using makeAutoObservable",
21+
"insertionScopeTypes": [
22+
"namedFunction"
23+
]
24+
},
25+
"constantDeclaration": {
26+
"definitions": [
27+
{
28+
"scope": {
29+
"langIds": [
30+
"typescript",
31+
"javascript",
32+
"typescriptreact",
33+
"javascriptreact"
34+
]
35+
},
36+
"body": [
37+
"const $name = ${value/^([^;]*);?$/$1/};"
38+
],
39+
"variables": {
40+
"name": {
41+
"formatter": "camelCase"
42+
}
43+
}
44+
}
45+
],
46+
"description": "Constant variable declaration",
47+
"insertionScopeTypes": [
48+
"statement"
49+
],
50+
"variables": {
51+
"value": {
52+
"wrapperScopeType": "statement"
53+
}
54+
}
55+
},
56+
"constantDeclarationWithType": {
57+
"definitions": [
58+
{
59+
"scope": {
60+
"langIds": [
61+
"typescript",
62+
"javascript",
63+
"typescriptreact",
64+
"javascriptreact"
65+
]
66+
},
67+
"body": [
68+
"const $name: $type = ${value/^([^;]*);?$/$1/};"
69+
],
70+
"variables": {
71+
"name": {
72+
"formatter": "camelCase"
73+
}
74+
}
75+
}
76+
],
77+
"description": "Constant variable declaration with type",
78+
"insertionScopeTypes": [
79+
"statement"
80+
],
81+
"variables": {
82+
"value": {
83+
"wrapperScopeType": "statement"
84+
}
85+
}
86+
},
87+
"letDeclaration": {
88+
"definitions": [
89+
{
90+
"scope": {
91+
"langIds": [
92+
"typescript",
93+
"javascript",
94+
"typescriptreact",
95+
"javascriptreact"
96+
]
97+
},
98+
"body": [
99+
"let $name = ${value/^([^;]*);?$/$1/};"
100+
],
101+
"variables": {
102+
"name": {
103+
"formatter": "camelCase"
104+
}
105+
}
106+
}
107+
],
108+
"description": "Let variable declaration",
109+
"insertionScopeTypes": [
110+
"statement"
111+
],
112+
"variables": {
113+
"value": {
114+
"wrapperScopeType": "statement"
115+
}
116+
}
117+
},
118+
"letDeclarationWithType": {
119+
"definitions": [
120+
{
121+
"scope": {
122+
"langIds": [
123+
"typescript",
124+
"javascript",
125+
"typescriptreact",
126+
"javascriptreact"
127+
]
128+
},
129+
"body": [
130+
"let $name: $type = ${value/^([^;]*);?$/$1/};"
131+
],
132+
"variables": {
133+
"name": {
134+
"formatter": "camelCase"
135+
}
136+
}
137+
}
138+
],
139+
"description": "Let variable declaration with type",
140+
"insertionScopeTypes": [
141+
"statement"
142+
],
143+
"variables": {
144+
"value": {
145+
"wrapperScopeType": "statement"
146+
}
147+
}
148+
}
149+
}

packages/cursorless-vscode/src/migrateSnippets.ts

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,26 @@ async function migrateFile(
6666
const fileName = path.basename(filePath, CURSORLESS_SNIPPETS_SUFFIX);
6767
const snippetFile = await readLegacyFile(filePath);
6868
const communitySnippetFile: SnippetFile = { snippets: [] };
69+
const snippetNames = Object.keys(snippetFile);
70+
const useHeader = snippetNames.length === 1;
6971
let hasSkippedSnippet = false;
7072

71-
for (const snippetName in snippetFile) {
73+
for (const snippetName of snippetNames) {
7274
const snippet = snippetFile[snippetName];
7375
const phrase =
7476
spokenForms.insertion[snippetName] ??
7577
spokenForms.insertionWithPhrase[snippetName];
78+
const phrases = phrase ? [phrase] : undefined;
7679

77-
communitySnippetFile.header = {
78-
name: snippetName,
79-
description: snippet.description,
80-
phrases: phrase ? [phrase] : undefined,
81-
variables: parseVariables(spokenForms, snippetName, snippet.variables),
82-
insertionScopes: snippet.insertionScopeTypes,
83-
};
80+
if (useHeader) {
81+
communitySnippetFile.header = {
82+
name: snippetName,
83+
description: snippet.description,
84+
phrases: phrases,
85+
variables: parseVariables(spokenForms, snippetName, snippet.variables),
86+
insertionScopes: snippet.insertionScopeTypes,
87+
};
88+
}
8489

8590
for (const def of snippet.definitions) {
8691
if (
@@ -91,11 +96,20 @@ async function migrateFile(
9196
continue;
9297
}
9398
communitySnippetFile.snippets.push({
94-
body: def.body.map((line) => line.replaceAll("\t", " ")),
99+
name: useHeader ? undefined : snippetName,
100+
description: useHeader ? undefined : snippet.description,
101+
phrases: useHeader ? undefined : phrases,
102+
insertionScopes: useHeader ? undefined : snippet.insertionScopeTypes,
95103
languages: def.scope?.langIds,
96-
variables: parseVariables(spokenForms, snippetName, def.variables),
104+
variables: parseVariables(
105+
spokenForms,
106+
snippetName,
107+
useHeader ? undefined : snippet.variables,
108+
def.variables,
109+
),
97110
// SKIP: def.scope?.scopeTypes
98111
// SKIP: def.scope?.excludeDescendantScopeTypes
112+
body: def.body.map((line) => line.replaceAll("\t", " ")),
99113
});
100114
}
101115
}
@@ -131,22 +145,36 @@ async function migrateFile(
131145
function parseVariables(
132146
spokenForms: SpokenForms,
133147
snippetName: string,
134-
variables?: Record<string, SnippetVariableLegacy>,
148+
snippetVariables?: Record<string, SnippetVariableLegacy>,
149+
defVariables?: Record<string, SnippetVariableLegacy>,
135150
): SnippetVariable[] {
136-
return Object.entries(variables ?? {}).map(
137-
([name, variable]): SnippetVariable => {
151+
const map: Record<string, SnippetVariable> = {};
152+
153+
const add = (name: string, variable: SnippetVariableLegacy) => {
154+
if (!map[name]) {
138155
const phrase = spokenForms.wrapper[`${snippetName}.${name}`];
139-
return {
156+
map[name] = {
140157
name,
141158
wrapperPhrases: phrase ? [phrase] : undefined,
142-
wrapperScope: variable.wrapperScopeType,
143-
insertionFormatters: variable.formatter
144-
? getFormatter(variable.formatter)
145-
: undefined,
146-
// SKIP: variable.description
147159
};
148-
},
160+
}
161+
if (variable.wrapperScopeType) {
162+
map[name].wrapperScope = variable.wrapperScopeType;
163+
}
164+
if (variable.formatter) {
165+
map[name].insertionFormatters = getFormatter(variable.formatter);
166+
}
167+
// SKIP: variable.description
168+
};
169+
170+
Object.entries(snippetVariables ?? {}).forEach(([name, variable]) =>
171+
add(name, variable),
172+
);
173+
Object.entries(defVariables ?? {}).forEach(([name, variable]) =>
174+
add(name, variable),
149175
);
176+
177+
return Object.values(map);
150178
}
151179

152180
// Convert Cursorless formatters to Talon community formatters

0 commit comments

Comments
 (0)