Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions package-lock.json

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

6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,5 +150,9 @@
"env:source": "export $(cat .envrc | xargs)",
"vsce:publish": "sh publish.sh",
"upgrade-interactive": "npx npm-check -u"
}
},
"files": [
"out/**/*",
"vendor/**/*"
]
}
16 changes: 16 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {Config} from './config/findConfig'
import {GroqContentProvider} from './providers/content-provider'
import {GROQCodeLensProvider} from './providers/groq-codelens-provider'
import {executeGroq} from './query'
import {formatGroq} from './format'

export function activate(context: vscode.ExtensionContext) {
// needed to load sanity.cli.ts
Expand Down Expand Up @@ -88,6 +89,21 @@ export function activate(context: vscode.ExtensionContext) {
})
context.subscriptions.push(disposable)

context.subscriptions.push(
vscode.commands.registerCommand('sanity.formatGroq', async (groqQuery: string, uri: string, range: vscode.Range) => {
const document = await vscode.workspace.openTextDocument(vscode.Uri.file(uri));
const query = groqQuery;
const formattedQuery = await formatGroq(query); // Await the async function

const edit = new vscode.WorkspaceEdit();
edit.replace(document.uri, range, formattedQuery);
await vscode.workspace.applyEdit(edit);

// Refresh the CodeLens
await vscode.commands.executeCommand('vscode.executeCodeLensProvider', document.uri);
})
);

function readConfig() {
const settings = vscode.workspace.getConfiguration('sanity')
openJSONFile = settings.get('openJSONFile', false)
Expand Down
15 changes: 15 additions & 0 deletions src/format.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { loadWasm } from "./wasm-loader"

let isWasmLoaded = false;

async function formatGroq(groq: string): Promise<string> {
if (!isWasmLoaded) {
await loadWasm();
isWasmLoaded = true;
}

const formattedGroq = (global as any).groqfmt(groq); // Assuming groqfmt is exposed globally by the WASM module
return formattedGroq.result;
}

export { formatGroq };
76 changes: 46 additions & 30 deletions src/providers/groq-codelens-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,77 +3,93 @@ import {type CodeLensProvider, type TextDocument, type CancellationToken, CodeLe
interface ExtractedQuery {
content: string
uri: string
position: Position
range: Range;
}

function extractAllTemplateLiterals(document: TextDocument): ExtractedQuery[] {
const documents: ExtractedQuery[] = []
const text = document.getText()
const regExpGQL = new RegExp('groq\\s*`([\\s\\S]+?)`', 'mg')
const documents: ExtractedQuery[] = [];
const text = document.getText();
const regExpGQL = new RegExp('groq\\s*`([\\s\\S]+?)`', 'mg');

let prevIndex = 0
let result
let result;
while ((result = regExpGQL.exec(text)) !== null) {
const content = result[1]
const queryPosition = text.indexOf(content, prevIndex)
const content = result[1];
const startPosition = document.positionAt(result.index + result[0].indexOf(content));
const endPosition = document.positionAt(result.index + result[0].indexOf(content) + content.length);
const range = new Range(startPosition, endPosition);
documents.push({
content: content,
uri: document.uri.path,
position: document.positionAt(queryPosition),
})
prevIndex = queryPosition + 1
range: range,
});
}
return documents
return documents;
}

function extractAllDefineQuery(document: TextDocument): ExtractedQuery[] {
const documents: ExtractedQuery[] = []
const text = document.getText()
const pattern = '(\\s*defineQuery\\((["\'`])([\\s\\S]*?)\\2\\))'
const documents: ExtractedQuery[] = [];
const text = document.getText();
const pattern = '(\\s*defineQuery\\((["\'`])([\\s\\S]*?)\\2\\))';
const regexp = new RegExp(pattern, 'g');

let prevIndex = 0
let result
let result;
while ((result = regexp.exec(text)) !== null) {
const content = result[3]
const queryPosition = text.indexOf(result[1], prevIndex)
const content = result[3];
const startPosition = document.positionAt(result.index + result[0].indexOf(content));
const endPosition = document.positionAt(result.index + result[0].indexOf(content) + content.length);
const range = new Range(startPosition, endPosition);
documents.push({
content: content,
uri: document.uri.path,
position: document.positionAt(queryPosition),
})
prevIndex = queryPosition + 1
range: range,
});
}
return documents
return documents;
}

export class GROQCodeLensProvider implements CodeLensProvider {
constructor() {}

public provideCodeLenses(document: TextDocument, _token: CancellationToken): CodeLens[] {

if (document.languageId === 'groq') {
return [
new CodeLens(new Range(new Position(0, 0), new Position(0, 0)), {
title: 'Execute Query',
command: 'sanity.executeGroq',
arguments: [document.getText()],
}),
]
new CodeLens(new Range(new Position(0, 0), new Position(0, 0)), {
title: 'Format Query',
command: 'sanity.formatGroq',
arguments: [document.getText(), document.uri, new Position(0, 0)],
}),
];
}

// find all lines where "groq" exists
const queries: ExtractedQuery[] = [...extractAllTemplateLiterals(document), ...extractAllDefineQuery(document)]
const queries: ExtractedQuery[] = [...extractAllTemplateLiterals(document), ...extractAllDefineQuery(document)];

// add a button above each line that has groq
return queries.map((def) => {
return new CodeLens(
new Range(new Position(def.position.line, 0), new Position(def.position.line, 0)),
const lenses: CodeLens[] = [];
queries.forEach((def) => {
lenses.push(new CodeLens(
def.range,
{
title: 'Execute Query',
command: 'sanity.executeGroq',
arguments: [def.content],
}
)
})
));
lenses.push(new CodeLens(
def.range,
{
title: 'Format Query',
command: 'sanity.formatGroq',
arguments: [def.content, def.uri, def.range],
}
));
});
return lenses;
}
}
17 changes: 17 additions & 0 deletions src/wasm-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as path from 'path';
import * as fs from 'fs';
import * as util from 'util';

import '@groqfmt/wasm/dist/wasm-exec';

const go = new (global as any).Go();

async function loadWasm() {
const wasmPath = path.resolve(__dirname, '../vendor/groqfmt.wasm');

const wasmData = await util.promisify(fs.readFile)(wasmPath);
const { instance } = await WebAssembly.instantiate(wasmData, go.importObject);
go.run(instance);
}

export { loadWasm };
6 changes: 5 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,9 @@
"esModuleInterop": true,
"jsx": "react"
},
"exclude": ["node_modules", ".vscode-test", "ts-graphql-plugin"]
"exclude": ["node_modules", ".vscode-test", "ts-graphql-plugin"],
"include": [
"src/**/*.ts",
"vendor/groqfmt.wasm"
]
}
Binary file added vendor/groqfmt.wasm
Binary file not shown.