Skip to content

Commit

Permalink
add code action to vscode extension for opening the local EdgeDB UI
Browse files Browse the repository at this point in the history
  • Loading branch information
zth committed Jan 23, 2024
1 parent 8730e77 commit 671eb0d
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 6 deletions.
3 changes: 3 additions & 0 deletions vscode-extension/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# main

- Add code action for opening queries in the local EdgeDB UI.
12 changes: 12 additions & 0 deletions vscode-extension/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
},
"license": "MIT",
"dependencies": {
"dotenv": "^16.3.2",
"typescript": "5.0.4"
},
"devDependencies": {
Expand Down
182 changes: 176 additions & 6 deletions vscode-extension/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { readFileSync } from "fs";
import path from "path";
import * as childProcess from "child_process";
import * as os from "os";
import * as fs from "fs";
import * as path from "path";
import * as dotenv from "dotenv";
import {
ExtensionContext,
workspace,
Expand All @@ -9,12 +12,36 @@ import {
DiagnosticSeverity,
Range,
Position,
TextDocument,
window,
commands,
env,
} from "vscode";

export async function activate(context: ExtensionContext) {
let currentWorkspacePath = workspace.workspaceFolders?.[0].uri.fsPath;
if (currentWorkspacePath == null) throw new Error("Init failed.");
let tempFilePrefix = "rescript_edgedb_" + process.pid + "_";
let tempFileId = 0;

function createFileInTempDir() {
let tempFileName = tempFilePrefix + tempFileId + ".res";
tempFileId = tempFileId + 1;
return path.join(os.tmpdir(), tempFileName);
}

function findProjectPackageJsonRoot(source: string): null | string {
let dir = path.dirname(source);
if (fs.existsSync(path.join(dir, "package.json"))) {
return dir;
} else {
if (dir === source) {
// reached top
return null;
} else {
return findProjectPackageJsonRoot(dir);
}
}
}

async function setupErrorLogWatcher(context: ExtensionContext) {
const rootFiles = await workspace.findFiles(
"**/{bsconfig,rescript}.json",
"**/node_modules/**",
Expand All @@ -40,7 +67,7 @@ export async function activate(context: ExtensionContext) {

function syncDiagnostics() {
try {
const contents = readFileSync(
const contents = fs.readFileSync(
path.resolve(dir, ".generator.edgeql.log"),
"utf-8"
);
Expand Down Expand Up @@ -113,4 +140,147 @@ export async function activate(context: ExtensionContext) {
);
}

type dataFromFile = {
content: string;
start: { line: number; col: number };
end: { line: number; col: number };
tag: string;
};

const edgeqlContent: Map<string, dataFromFile[]> = new Map();

function callRescriptEdgeDBCli(command: string[], cwd: string) {
const dotEnvLoc = path.join(cwd, ".env");
const dotEnvExists = fs.existsSync(dotEnvLoc);
const env = dotEnvExists ? dotenv.config({ path: dotEnvLoc }) : null;

return childProcess.execFileSync(
"./node_modules/.bin/rescript-edgedb",
command,
{
cwd: cwd,
env: {
...process.env,
...env?.parsed,
},
}
);
}

function updateContent(e: TextDocument) {
if (e.languageId === "rescript") {
const text = e.getText();
const cwd = findProjectPackageJsonRoot(e.fileName);
if (cwd != null && text.includes("%edgeql(")) {
const tempFile = createFileInTempDir();
fs.writeFileSync(tempFile, text);
try {
const dataFromCli = callRescriptEdgeDBCli(["extract", tempFile], cwd);
const data: dataFromFile[] = JSON.parse(dataFromCli.toString());
edgeqlContent.set(e.fileName, data);
} catch (e) {
console.error(e);
} finally {
fs.rmSync(tempFile);
}
} else {
edgeqlContent.delete(e.fileName);
}
}
}

async function setupCodeActions(context: ExtensionContext) {
context.subscriptions.push(
workspace.onDidOpenTextDocument((e) => {
updateContent(e);
})
);
context.subscriptions.push(
workspace.onDidCloseTextDocument((e) => {
updateContent(e);
})
);
context.subscriptions.push(
workspace.onDidChangeTextDocument((e) => {
updateContent(e.document);
})
);
}

export async function activate(context: ExtensionContext) {
let currentWorkspacePath = workspace.workspaceFolders?.[0].uri.fsPath;
if (currentWorkspacePath == null) throw new Error("Init failed.");

await Promise.all([setupErrorLogWatcher(context), setupCodeActions(context)]);

context.subscriptions.push(
commands.registerCommand(
"vscode-rescript-edgedb-open-ui",
async (query: string, cwd: string) => {
const url = JSON.parse(
callRescriptEdgeDBCli(["ui-url"], cwd).toString()
);
const lines = query.trim().split("\n");
// First line has the comment with the query name, so the second line will have the offset
const offset = lines[1].match(/^\s*/)?.[0].length ?? 0;
const offsetAsStr = Array.from({ length: offset })
.map((_) => " ")
.join("");

await env.clipboard.writeText(
lines
.map((l) => {
const leadingWhitespace = l.slice(0, offset);
if (leadingWhitespace === offsetAsStr) {
return l.slice(offset);
}

return l;
})
.join("\n")
);
window.showInformationMessage(
"The EdgeQL query was copied to the clipboard!\nOpening the EdgeDB UI in the browser..."
);
env.openExternal(Uri.parse(`${url}/editor`));
}
)
);

context.subscriptions.push(
languages.registerCodeActionsProvider(
{
language: "rescript",
},
{
provideCodeActions(document, range, _context, _token) {
const cwd = findProjectPackageJsonRoot(document.fileName);
const contentInFile = edgeqlContent.get(document.fileName) ?? [];
const targetWithCursor = contentInFile.find((c) => {
const start = new Position(c.start.line, c.start.col);
const end = new Position(c.end.line, c.end.col);
return (
range.start.isAfterOrEqual(start) &&
range.end.isBeforeOrEqual(end)
);
});

if (targetWithCursor != null && cwd != null) {
return [
{
title: "Open the EdgeDB UI query editor",
command: "vscode-rescript-edgedb-open-ui",
arguments: [targetWithCursor.content, cwd],
tooltip: "The EdgeQL query will be copied to the clipboard.",
},
];
}

return [];
},
}
)
);
}

export function deactivate() {}

0 comments on commit 671eb0d

Please sign in to comment.