Skip to content

Commit

Permalink
opcode documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
fulpm committed Dec 20, 2024
1 parent bca780a commit 7a4843d
Show file tree
Hide file tree
Showing 10 changed files with 1,413 additions and 267 deletions.
27 changes: 0 additions & 27 deletions bin/ConvertToVscodeSnippet.py

This file was deleted.

11 changes: 8 additions & 3 deletions bin/genSnippets.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,18 @@ async function convert(filePath) {
const output = {};

for (const item of language) {
const { parameter, description } = item;
const { parameter, description, ...docs } = item;
if (parameter != null) {
const opcode = "(" + parameter.split(" ")[0];
let params = parameter.split(" ");
if (params[0].startsWith("[")) continue;
const opcode = "(" + params[0];
params = params.slice(1).join(" ");
output[opcode] = {
prefix: opcode,
body: [opcode],
description: `${parameter} || ${description}`,
description: `${params} || ${description}`,
// Custom property for use in HoverProvider
$doc: docs,
};
}
}
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "amalgam-lang",
"version": "2.1.4",
"version": "2.2.0",
"type": "commonjs",
"publisher": "howso",
"displayName": "Amalgam Language",
Expand Down
1,497 changes: 1,271 additions & 226 deletions snippets/amalgam.snippets.json

Large diffs are not rendered by default.

80 changes: 77 additions & 3 deletions src/debugger/activate.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import * as vscode from "vscode";
import { WorkspaceFolder, DebugConfiguration, ProviderResult } from "vscode";
import { AmalgamConfigurationProvider } from "./configuration";
import { loadOpcodeDocumentation } from "./documentation";

/**
* Activate debugger.
* @param context The VS Code extension context.
* @param factory The debug adapter factory.
*/
export function activateDebug(context: vscode.ExtensionContext, factory: vscode.DebugAdapterDescriptorFactory) {
export async function activateDebug(context: vscode.ExtensionContext, factory: vscode.DebugAdapterDescriptorFactory) {
const documentation = await loadOpcodeDocumentation(context);

context.subscriptions.push(
vscode.commands.registerCommand("extension.amalgam.runEditorContents", (resource: vscode.Uri) => {
let targetResource = resource;
Expand Down Expand Up @@ -93,14 +96,15 @@ export function activateDebug(context: vscode.ExtensionContext, factory: vscode.
);

// Override default implementation of the debug hover, here we attempt to only match words that may be
// a variable name
// a variable name or whitelisted opcode expression
context.subscriptions.push(
vscode.languages.registerEvaluatableExpressionProvider("amalgam", {
provideEvaluatableExpression(
document: vscode.TextDocument,
position: vscode.Position
): vscode.ProviderResult<vscode.EvaluatableExpression> {
const VARIABLE_REGEXP = /(?<![(#"'a-z0-9_.])(?<name>[_!^a-z]+[_a-z0-9]*)(?!["'a-z0-9])/gi;
const VARIABLE_REGEXP =
/(?<![(#"'a-z0-9_.])(?<name>(?:[_!^a-z]+[_a-z0-9]*)|(?:\((?:current_index|current_value)\s*(?:\s+\w+\s*)*?\)))(?!["'a-z0-9])/gi;
const line = document.lineAt(position.line).text;

let matches: RegExpExecArray | null;
Expand Down Expand Up @@ -150,5 +154,75 @@ export function activateDebug(context: vscode.ExtensionContext, factory: vscode.
})
);

context.subscriptions.push(
vscode.languages.registerHoverProvider("amalgam", {
provideHover: function (document: vscode.TextDocument, position: vscode.Position): ProviderResult<vscode.Hover> {
const range = document.getWordRangeAtPosition(position, /\([\w<>!+-~=*/]+/);
if (range) {
const opcode = document.getText(range);
const line = document.lineAt(position.line);

if (!(opcode in documentation)) {
// unknown opcode
return undefined;
}

if (line.text.substring(0, range.start.character).includes(";")) {
// range is in a comment
return undefined;
}

const quoteRegex = /(["'])(?:(?=(\\?))\2.)*?\1/g;
let qMatches: RegExpExecArray | null;
while ((qMatches = quoteRegex.exec(line.text)) !== null) {
const qRange = new vscode.Range(
position.line,
qMatches.index,
position.line,
qMatches.index + qMatches[0].length
);
if (qRange.contains(range)) {
// range is in a string
return undefined;
}
}

const doc = documentation[opcode];
const sections: vscode.MarkdownString[] = [];
// Render opcode signature
const header = new vscode.MarkdownString();
let headerText = opcode;
if (doc.parameters) {
headerText += `\n\t${doc.parameters}\n)`;
} else {
headerText += ")";
}
if (doc.output) {
// Add the output as a comment on the end
headerText += " ; -> " + doc.output;
}
header.appendCodeblock(headerText, "amalgam");
sections.push(header);
sections.push(new vscode.MarkdownString("---"));
// Render description if defined
if (doc.description) {
const content = new vscode.MarkdownString();
content.appendText(doc.description);
sections.push(content);
}
// Render examples if defined
if (doc.example) {
const content = new vscode.MarkdownString();
content.appendMarkdown("##### Examples:");
content.appendCodeblock(doc.example, "amalgam");
sections.push(content);
}
return new vscode.Hover(sections, range);
}
return undefined;
},
})
);

context.subscriptions.push(vscode.debug.registerDebugAdapterDescriptorFactory("amalgam", factory));
}
41 changes: 41 additions & 0 deletions src/debugger/documentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as vscode from "vscode";
import { TextDecoder } from "node:util";

export type OpcodeDefinition = {
parameters: string;
description: string;
output: string;
example: string;
concurrency: boolean;
};

export type OpcodeIndex = Record<string, OpcodeDefinition>;

/**
* Load opcode documentation from the bundled extension files.
* @param context The vscode extension context.
* @returns The opcode documentation.
*/
export async function loadOpcodeDocumentation(context: vscode.ExtensionContext): Promise<OpcodeIndex> {
const filepath = vscode.Uri.joinPath(context.extensionUri, "snippets", "amalgam.snippets.json");
const data = await vscode.workspace.fs.readFile(filepath);
const decoder = new TextDecoder("utf-8");
try {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const document: Record<string, any> = JSON.parse(decoder.decode(data));
return Object.entries(document).reduce((map, [key, value]) => {
if (value.description) {
const [parameters, description] = value.description.split(" || ", 2);
map[key] = {
...value.$doc,
parameters,
description,
};
}
return map;
}, {});
} catch (reason) {
console.warn("Failed to read Amalgam snippets");
}
return {};
}
2 changes: 1 addition & 1 deletion src/debugger/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export enum RuntimeStackType {
CALL = "Call", // The amalgam "call" stack
CONSTRUCTION = "Construction", // The construction stack
INTERPRET_NODE = "Interpret node", // Standard debugger call stack Amalgam<54.3.8
OPCODE = "Opcode" // Standard debugger call stack Amalgam>=54.3.8
OPCODE = "Opcode", // Standard debugger call stack Amalgam>=54.3.8
}

/** The types of breakpoints */
Expand Down
10 changes: 9 additions & 1 deletion src/debugger/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,16 @@ export class AmalgamDebugSession extends LoggingDebugSession {
case "watch":
rv = await this.runtime.evaluate(args.expression);
break;
case "variables":
case "hover":
if (args.expression.startsWith("(") && args.expression.endsWith(")")) {
// Evaluate opcode expression
rv = await this.runtime.evaluate(args.expression);
} else {
// Get variable value
rv = await this.runtime.getVariablePreview(args.expression);
}
break;
case "variables":
case "clipboard":
default: {
const [category, expr] = args.expression.split(" ");
Expand Down
6 changes: 3 additions & 3 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import { InlineDebugAdapterFactory, ExternalDebugAdapterFactory } from "./debugg
*/
const runMode: "external" | "inline" = "inline";

export function activate(context: vscode.ExtensionContext) {
export async function activate(context: vscode.ExtensionContext) {
// debug adapters can be run in different ways by using a vscode.DebugAdapterDescriptorFactory:
switch (runMode) {
case "inline":
// Run the debug adapter inside the extension and directly talk to it
activateDebug(context, new InlineDebugAdapterFactory());
await activateDebug(context, new InlineDebugAdapterFactory());
break;
case "external":
// Run the debug adapter as a separate process
activateDebug(context, new ExternalDebugAdapterFactory());
await activateDebug(context, new ExternalDebugAdapterFactory());
break;
default:
throw new Error(`Run mode '${runMode}' not implemented`);
Expand Down

0 comments on commit 7a4843d

Please sign in to comment.