flyde-vscode
Version:
Flyde is an open-source, visual programming language. It runs in the IDE, integrates with existing TypeScript code, both browser and Node.js.
230 lines (184 loc) • 6.91 kB
text/typescript
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from "vscode";
import { FlydeEditorEditorProvider } from "./flydeEditor";
import * as execa from "execa";
var fp = require("find-free-port");
import { initFlydeDevServer } from "@flyde/dev-server/dist/lib";
import { join } from "path";
import TelemetryReporter from "@vscode/extension-telemetry";
import { activateReporter, reportEvent } from "./telemetry";
import { Template, getTemplates, scaffoldTemplate } from "./templateUtils";
// the application insights key (also known as instrumentation key)
// telemetry reporter
let reporter: TelemetryReporter;
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
let server: any;
let process: execa.ExecaChildProcess;
const FLYDE_DEFAULT_SERVER_PORT = 8545;
export function activate(context: vscode.ExtensionContext) {
// eslint-disable-next-line @typescript-eslint/naming-convention
const { Uri } = vscode;
const { fs } = vscode.workspace;
const firstWorkspace =
vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders[0];
const fileRoot = firstWorkspace ? firstWorkspace.uri.fsPath : "";
// ensure it gets properly disposed. Upon disposal the events will be flushed
context.subscriptions.push(activateReporter());
reportEvent("activate");
const mainOutputChannel = vscode.window.createOutputChannel("Flyde");
const debugOutputChannel = vscode.window.createOutputChannel("Flyde (Debug)");
let currentTheme = vscode.window.activeColorTheme;
let useDarkMode =
currentTheme.kind !== vscode.ColorThemeKind.Light &&
currentTheme.kind !== vscode.ColorThemeKind.HighContrastLight;
vscode.window.onDidChangeActiveColorTheme((theme) => {
useDarkMode =
theme.kind !== vscode.ColorThemeKind.Light &&
theme.kind !== vscode.ColorThemeKind.HighContrastLight;
});
fp(FLYDE_DEFAULT_SERVER_PORT).then(([port]: [number]) => {
reportEvent("devServerStart");
const editorStaticsRoot = join(__dirname, "../editor-build");
const cleanServer = initFlydeDevServer({
port,
root: fileRoot,
editorStaticsRoot,
});
context.subscriptions.push({
dispose() {
cleanServer();
},
});
context.subscriptions.push(
FlydeEditorEditorProvider.register(context, {
port,
mainOutputChannel,
debugOutputChannel,
darkMode: useDarkMode,
})
);
});
const openAsTextHandler = async (uri: vscode.Uri) => {
const document = await vscode.workspace.openTextDocument(uri);
// Show the document in the text editor
await vscode.window.showTextDocument(document);
reporter.sendTelemetryEvent("openAsText");
};
context.subscriptions.push(
vscode.commands.registerCommand("flyde.openAsText", openAsTextHandler)
);
function getWorkspaceRootPath(): vscode.Uri | undefined {
// Check if there is an open workspace
if (
vscode.workspace.workspaceFolders &&
vscode.workspace.workspaceFolders.length > 0
) {
// Return the path of the first workspace folder
return vscode.workspace.workspaceFolders[0].uri;
}
}
context.subscriptions.push(
vscode.commands.registerCommand(
"flyde.newVisualFlow",
async (dirName: vscode.Uri) => {
reportEvent("newVisualFlow:start");
let folderOrFileUri = dirName ?? getWorkspaceRootPath(); // folder will be undefined when triggered by keybinding
if (!folderOrFileUri) {
vscode.window.showErrorMessage("No folder or file selected");
return;
}
const folderStat = await fs.stat(folderOrFileUri);
const folderUri =
folderStat.type === vscode.FileType.Directory
? folderOrFileUri
: vscode.Uri.joinPath(folderOrFileUri, "..");
const templates = getTemplates();
const template = await vscode.window.showQuickPick<
Template & { label: string }
>(
templates.map((t) => ({
...t,
label: t.name,
description: t.tags.join(", "),
detail: t.description,
}))
);
if (!template) {
vscode.window.showWarningMessage("No template selected, aborting");
return;
}
const fileName = await vscode.window.showInputBox({
title: "Flow file name",
value: "NewFlow",
});
if (!fileName) {
vscode.window.showWarningMessage("No file name passed, aborting");
return;
}
const targetPath = Uri.joinPath(folderUri, fileName + ".flyde");
if (
(await fs.readFile(targetPath).then(
() => true,
() => false
)) === true
) {
vscode.window.showErrorMessage(`File ${targetPath} already exists!`);
return;
}
try {
reportEvent("newVisualFlow:before", { template: template.name });
scaffoldTemplate(template, folderUri.fsPath, fileName);
vscode.commands.executeCommand(
"vscode.openWith",
targetPath,
"flydeEditor"
);
reportEvent("newVisualFlow:success", { template: template.name });
vscode.window.showInformationMessage(
`New flow created at ${fileName}.flyde!`
);
} catch (error) {
vscode.window.showErrorMessage(`Error creating flow: ${error}`);
}
}
)
);
context.subscriptions.push(
vscode.commands.registerCommand("flyde.setOpenAiToken", async () => {
const token = await vscode.window.showInputBox({
title:
"Please enter your OpenAI API key (will be stored in your settings for future use, you can also set it manually or clear it in the settings)",
value: "",
ignoreFocusOut: true,
password: true,
});
if (!token) {
vscode.window.showWarningMessage("No token passed, aborting");
return;
}
await vscode.workspace
.getConfiguration()
.update("flyde.openAiToken", token, vscode.ConfigurationTarget.Global);
vscode.window.showInformationMessage("OpenAI API Token set");
})
);
context.subscriptions.push(
vscode.commands.registerCommand("flyde.clearOpenAiToken", async () => {
await vscode.workspace
.getConfiguration()
.update("flyde.openAiToken", "", vscode.ConfigurationTarget.Global);
vscode.window.showInformationMessage("OpenAI API Token cleared");
})
);
}
// this method is called when your extension is deactivated
export function deactivate() {
if (server) {
server.close();
}
if (process) {
process.kill();
}
}