-
-
Notifications
You must be signed in to change notification settings - Fork 91
Added voice command to migrate Cursorless snippet to community format #2747
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 26 commits
3754e88
325d9a6
6d48577
2362952
e858d69
92b187e
d92e383
5dcc518
bfabb54
2ee5a90
7646f6e
b585a44
2ac4d44
d51cea3
216309d
67f6133
c980df5
a813db4
cd073e0
4e185e2
821038f
820e3e4
456c3e8
efeec1b
3f4be9c
9515360
5321c28
40bc300
1a85c84
555028c
3995ca4
45aa9dc
c6ef011
050cbc2
c45a55b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,109 @@ | ||||||
import type { | ||||||
SnippetMap, | ||||||
SnippetVariable as SnippetVariableLegacy, | ||||||
} from "@cursorless/common"; | ||||||
import * as fs from "node:fs/promises"; | ||||||
import * as path from "node:path"; | ||||||
import { | ||||||
serializeSnippetFile, | ||||||
type SnippetDocument, | ||||||
type SnippetVariable, | ||||||
} from "talon-snippets"; | ||||||
import * as vscode from "vscode"; | ||||||
import type { VscodeSnippets } from "./VscodeSnippets"; | ||||||
|
||||||
export async function migrateSnippets( | ||||||
snippets: VscodeSnippets, | ||||||
targetDirectory: string, | ||||||
) { | ||||||
const userSnippetsDir = snippets.getUserDirectoryStrict(); | ||||||
const files = await snippets.getSnippetPaths(userSnippetsDir); | ||||||
|
||||||
for (const file of files) { | ||||||
await migrateFile(targetDirectory, file); | ||||||
} | ||||||
|
||||||
await vscode.window.showInformationMessage( | ||||||
`${files.length} snippet files migrated successfully!`, | ||||||
); | ||||||
} | ||||||
|
||||||
async function migrateFile(targetDirectory: string, filePath: string) { | ||||||
const fileName = path.basename(filePath, ".cursorless-snippets"); | ||||||
|
||||||
const snippetFile = await readLegacyFile(filePath); | ||||||
const communitySnippetFile: SnippetDocument[] = []; | ||||||
|
||||||
for (const snippetName in snippetFile) { | ||||||
const snippet = snippetFile[snippetName]; | ||||||
|
||||||
communitySnippetFile.push({ | ||||||
name: snippetName, | ||||||
variables: parseVariables(snippet.variables), | ||||||
insertionScopes: snippet.insertionScopeTypes, | ||||||
description: snippet.description, | ||||||
}); | ||||||
|
||||||
for (const def of snippet.definitions) { | ||||||
communitySnippetFile.push({ | ||||||
body: def.body.map((line) => line.replaceAll("\t", " ")), | ||||||
languages: def.scope?.langIds, | ||||||
variables: parseVariables(def.variables), | ||||||
// SKIP: def.scope?.scopeTypes | ||||||
// SKIP: def.scope?.excludeDescendantScopeTypes | ||||||
}); | ||||||
} | ||||||
} | ||||||
|
||||||
let destinationPath = path.join(targetDirectory, `${fileName}.snippet`); | ||||||
if (await fileExists(destinationPath)) { | ||||||
destinationPath = path.join( | ||||||
targetDirectory, | ||||||
`${fileName}_CONFLICT.snippet`, | ||||||
); | ||||||
} | ||||||
await writeCommunityFile(communitySnippetFile, destinationPath); | ||||||
} | ||||||
|
||||||
function parseVariables( | ||||||
variables?: Record<string, SnippetVariableLegacy>, | ||||||
): SnippetVariable[] { | ||||||
return Object.entries(variables ?? {}).map( | ||||||
([name, variable]): SnippetVariable => { | ||||||
return { | ||||||
name, | ||||||
wrapperScope: variable.wrapperScopeType, | ||||||
insertionFormatters: variable.formatter | ||||||
? [variable.formatter] | ||||||
: undefined, | ||||||
// SKIP: variable.description | ||||||
}; | ||||||
}, | ||||||
); | ||||||
} | ||||||
|
||||||
async function fileExists(filePath: string): Promise<boolean> { | ||||||
try { | ||||||
await fs.access(filePath); | ||||||
return true; | ||||||
} catch (_e) { | ||||||
return false; | ||||||
} | ||||||
} | ||||||
|
||||||
async function readLegacyFile(filePath: string): Promise<SnippetMap> { | ||||||
const content = await fs.readFile(filePath, "utf8"); | ||||||
if (content.length === 0) { | ||||||
return {}; | ||||||
} | ||||||
return JSON.parse(content); | ||||||
} | ||||||
|
||||||
async function writeCommunityFile( | ||||||
snippetFile: SnippetDocument[], | ||||||
filePath: string, | ||||||
) { | ||||||
const snippetText = serializeSnippetFile(snippetFile); | ||||||
const file = await fs.open(filePath, "w"); | ||||||
|
const file = await fs.open(filePath, "w"); | |
const file = await fs.open(filePath, "wx"); |
'wx': Like 'w' but fails if the path exists.
Then catch and put your conflict filename behavior in the catch block.
phillco marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.