Skip to content

Commit

Permalink
20939: Adds a DefinitionProvider, MINOR (#17)
Browse files Browse the repository at this point in the history
Adds a DefinitionProvider, which enables users to use F12 or "Go To
Definition" on symbols. It will search for the version where a '#' is in
front of it within all `.amlg` files in the project.

---------

Co-authored-by: Matt Fulp <8397318+fulpm@users.noreply.github.com>
  • Loading branch information
cademack and fulpm authored Jul 17, 2024
1 parent c86d565 commit 38b5916
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 7 deletions.
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.0.0",
"version": "2.1.0",
"type": "commonjs",
"publisher": "howso",
"displayName": "Amalgam Language",
Expand Down
8 changes: 4 additions & 4 deletions snippets/amalgam.snippets.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
"body": [
"(call_sandboxed"
],
"description": "call_sandboxed * function assoc arguments [number operation_limit] [number max_node_allocations] || Evaluates the code specified by *, isolating it from everything except for arguments, which is used as a single layer of the scope stack. This is useful when evaluating code passed by other entities that may or may not be trusted. Opcodes run from within call_sandboxed that require any form of permissions will not perform any action and will evaluate to null. If operation_limit is specified, it represents the number of operations that are allowed to be performed. If operation_limit is 0 or infinite, then an infinite of operations will be allotted, up to the limits of the current calling context. If max_node_allocations is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory, up to the current calling context's limit. If max_node_allocations is 0 or infinite and the caller also has no limit, then there is no limit to the number of nodes to be allotted as long as the machine has sufficient memory. Note that if max_node_allocations is specified while call_sandboxed is being called in a multithreaded environment, if the collective memory from all the related threads exceeds the average memory specified by call_sandboxed, that may trigger a memory limit for the call_sandboxed."
"description": "call_sandboxed * function assoc arguments [number operation_limit] [number max_node_allocations] [number max_opcode_execution_depth] || Evaluates the code specified by *, isolating it from everything except for arguments, which is used as a single layer of the scope stack. This is useful when evaluating code passed by other entities that may or may not be trusted. Opcodes run from within call_sandboxed that require any form of permissions will not perform any action and will evaluate to null. If operation_limit is specified, it represents the number of operations that are allowed to be performed. If operation_limit is 0 or infinite, then an infinite of operations will be allotted, up to the limits of the current calling context. If max_node_allocations is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory, up to the current calling context's limit. If max_node_allocations is 0 or infinite and the caller also has no limit, then there is no limit to the number of nodes to be allotted as long as the machine has sufficient memory. Note that if max_node_allocations is specified while call_sandboxed is being called in a multithreaded environment, if the collective memory from all the related threads exceeds the average memory specified by call_sandboxed, that may trigger a memory limit for the call_sandboxed. If max_opcode_execution_depth is 0 or infinite and the caller also has no limit, then there is no limit to the depth that opcodes can execute, otherwise max_opcode_execution_depth limits how deep nested opcodes will be called."
},
"(while": {
"prefix": "(while",
Expand Down Expand Up @@ -1467,20 +1467,20 @@
"body": [
"(call_entity"
],
"description": "call_entity id entity [string label_name] [assoc arguments] [number operation_limit] [number max_node_allocations] || Calls the contained entity specified by id, using the entity as the new entity context. It will evaluate to the return value of the call, null if not found. If string is specified, then it will call the label specified by string. If assoc is specified, then it will pass assoc as the arguments on the scope stack. If operation_limit is specified, it represents the number of operations that are allowed to be performed. If operation_limit is 0 or infinite, then an infinite of operations will be allotted to the entity, but only if its containing entity (the current entity) has infinite operations. The root entity has infinite computing cycles. If max_node_allocations is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory. If max_node_allocations is 0 or infinite, then there is no limit to the number of nodes to be allotted to the entity as long as the machine has sufficient memory, but only if the containing entity (the current entity) has unlimited memory access. The execution performed will use a random number stream created from the entity's random number stream."
"description": "call_entity id entity [string label_name] [assoc arguments] [number operation_limit] [number max_node_allocations] [number max_opcode_execution_depth] || Calls the contained entity specified by id, using the entity as the new entity context. It will evaluate to the return value of the call, null if not found. If string is specified, then it will call the label specified by string. If assoc is specified, then it will pass assoc as the arguments on the scope stack. If operation_limit is specified, it represents the number of operations that are allowed to be performed. If operation_limit is 0 or infinite, then an infinite of operations will be allotted to the entity, but only if its containing entity (the current entity) has infinite operations. The root entity has infinite computing cycles. If max_node_allocations is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory. If max_node_allocations is 0 or infinite, then there is no limit to the number of nodes to be allotted to the entity as long as the machine has sufficient memory, but only if the containing entity (the current entity) has unlimited memory access. If max_opcode_execution_depth is 0 or infinite and the caller also has no limit, then there is no limit to the depth that opcodes can execute, otherwise max_opcode_execution_depth limits how deep nested opcodes will be called. The execution performed will use a random number stream created from the entity's random number stream."
},
"(call_entity_get_changes": {
"prefix": "(call_entity_get_changes",
"body": [
"(call_entity_get_changes"
],
"description": "call_entity_get_changes id entity [string label_name] [assoc arguments] [number operation_limit] [number max_node_allocations] || Like call_entity returning the value in *1. However, it also returns a list of direct_assign_to_entities calls with respective data in *2, holding a log of all of the changes that have elapsed. The log may be evaluated to apply or re-apply the changes to any id passed in to the log as _."
"description": "call_entity_get_changes id entity [string label_name] [assoc arguments] [number operation_limit] [number max_node_allocations] [number max_opcode_execution_depth] || Like call_entity returning the value in *1. However, it also returns a list of direct_assign_to_entities calls with respective data in *2, holding a log of all of the changes that have elapsed. The log may be evaluated to apply or re-apply the changes to any id passed in to the log as _."
},
"(call_container": {
"prefix": "(call_container",
"body": [
"(call_container"
],
"description": "call_container string parent_label_name [assoc arguments] [number operation_limit] [number max_node_allocations] || Attempts to call the container associated with the label that begins with a caret (^); the caret indicates that the label is allowed to be accessed by contained entities. It will evaluate to the return value of the call, null if not found. The call is made on the label specified by string. If assoc is specified, then it will pass assoc as the arguments on the scope stack. The parameter accessing_entity will automatically be set to the id of the caller, regardless of the arguments. If operation_limit is specified, it represents the number of operations that are allowed to be performed. If operation_limit is 0 or infinite, then an infinite of operations will be allotted to the entity, but only if its containing entity (the current entity) has infinite operations. The root entity has infinite computing cycles. If max_node_allocations is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory. If max_node_allocations is 0 or infinite, then there is no limit to the number of nodes to be allotted to the entity as long as the machine has sufficient memory, but only if the containing entity (the current entity) has unlimited memory access. The execution performed will use a random number stream created from the entity's random number stream."
"description": "call_container string parent_label_name [assoc arguments] [number operation_limit] [number max_node_allocations] [number max_opcode_execution_depth] || Attempts to call the container associated with the label that begins with a caret (^); the caret indicates that the label is allowed to be accessed by contained entities. It will evaluate to the return value of the call, null if not found. The call is made on the label specified by string. If assoc is specified, then it will pass assoc as the arguments on the scope stack. The parameter accessing_entity will automatically be set to the id of the caller, regardless of the arguments. If operation_limit is specified, it represents the number of operations that are allowed to be performed. If operation_limit is 0 or infinite, then an infinite of operations will be allotted to the entity, but only if its containing entity (the current entity) has infinite operations. The root entity has infinite computing cycles. If max_node_allocations is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory. If max_node_allocations is 0 or infinite, then there is no limit to the number of nodes to be allotted to the entity as long as the machine has sufficient memory, but only if the containing entity (the current entity) has unlimited memory access. If max_opcode_execution_depth is 0 or infinite and the caller also has no limit, then there is no limit to the depth that opcodes can execute, otherwise max_opcode_execution_depth limits how deep nested opcodes will be called. The execution performed will use a random number stream created from the entity's random number stream."
}
}
9 changes: 9 additions & 0 deletions src/language/activate.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import * as vscode from "vscode";

import { AmalgamDocumentSymbolProvider } from "./symbols";
import { AmalgamDefinitionProvider } from "./definitions";

/**
* Activate language features.
Expand All @@ -12,4 +14,11 @@ export function activateLanguage(context: vscode.ExtensionContext) {
new AmalgamDocumentSymbolProvider()
)
);

context.subscriptions.push(
vscode.languages.registerDefinitionProvider(
{ scheme: "file", language: "amalgam" },
new AmalgamDefinitionProvider()
)
);
}
66 changes: 66 additions & 0 deletions src/language/definitions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as vscode from "vscode";

export class AmalgamDefinitionProvider implements vscode.DefinitionProvider {
async provideDefinition(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken
): Promise<vscode.Definition | vscode.LocationLink[] | undefined> {
const wordRange = document.getWordRangeAtPosition(position);
if (!wordRange) {
return undefined;
}
let word = document.getText(wordRange);

// Check for label scope character before the word
if (wordRange.start.character > 0) {
const charBeforeWord = document.getText(new vscode.Range(wordRange.start.translate(0, -1), wordRange.start));
if (charBeforeWord === "!") {
word = "!" + word;
} else if (charBeforeWord === "^") {
word = "\\^" + word;
}
}
// Find a label definition by meeting the following criteria:
// 1. The label word is not preceded by a semicolon, representing a comment
// 2. The label word is only immediately preceded by a single `#`
// 3. The label word is immediately followed by at least one whitespace or the end of the line
// We then capture the text leading up to the matched definition and the definition word itself
const definitionPattern = new RegExp(`^([^;]*)(?<!#)(#${word})(?:\\s+|$).*$`);

// Get all Amalgam file URIs in the workspace
const workspaceFolder = vscode.workspace.getWorkspaceFolder(document.uri);
if (!workspaceFolder) {
return undefined;
}
const files = await vscode.workspace.findFiles(
new vscode.RelativePattern(workspaceFolder.uri, "**/**.amlg"),
undefined,
undefined,
token
);

// For each file check each line for matches to the definition pattern
const locations: vscode.Location[] = [];
for (const uri of files) {
if (token.isCancellationRequested) {
return undefined;
}

const file = await vscode.workspace.openTextDocument(uri);
for (let i = 0; i < file.lineCount; i++) {
if (token.isCancellationRequested) {
return undefined;
}
const line = file.lineAt(i);
const matches = definitionPattern.exec(line.text);
if (matches) {
const definitionPosition = new vscode.Position(i, matches[1].length);
locations.push(new vscode.Location(file.uri, definitionPosition));
}
}
}

return locations;
}
}

0 comments on commit 38b5916

Please sign in to comment.