Skip to content

Commit

Permalink
feat: add opcodes profile information (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
kobyhallx authored Nov 24, 2023
1 parent 4d845e8 commit 78e529a
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 13 deletions.
7 changes: 6 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
},
"cSpell.words": [
"acir",
"brillig",
"nargo"
]
}
24 changes: 24 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,21 @@
{
"command": "noir.restart",
"title": "Noir: Restart Language Server"
},
{
"command": "nargo.profile.hide",
"title": "Noir: Hide Profile information",
"when": "noir.profileInfoPresent == true"
}
],
"menus": {
"commandPalette": [
{
"command": "nargo.profile.hide",
"when": "noir.profileInfoPresent == true"
}
]
},
"snippets": [
{
"language": "noir",
Expand All @@ -60,6 +73,17 @@
"path": "./syntaxes/noir.tmLanguage.json"
}
],
"keybindings": [{
"command": "nargo.profile.hide",
"key": "shift+alt+ctrl+n, shift+p",
"mac": "shift+alt+cmd+n shift+p",
"when": "noir.profileInfoPresent"
},
{
"command": "nargo.profile",
"key": "shift+alt+ctrl+n p",
"mac": "shift+alt+cmd+n p"
}],
"configuration": {
"type": "object",
"title": "Noir Language Server configuration",
Expand Down
162 changes: 162 additions & 0 deletions src/EditorLineDecorationManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import {
workspace,
Disposable,
window,
TextEditorSelectionChangeEvent,
Range,
TextEditorDecorationType,
DecorationRangeBehavior,
Position,
ThemeColor,
commands,
} from "vscode";
import Client, { FileInfo, OpcodesCounts } from "./client";

const decoration: TextEditorDecorationType =
window.createTextEditorDecorationType({
after: {
margin: "0 0 0 3em",
textDecoration: "none",
},
rangeBehavior: DecorationRangeBehavior.ClosedOpen,
});

export class EditorLineDecorationManager extends Disposable {
#didChangeActiveTextEditor: Disposable;
lspClients: Map<string, Client>;

constructor(lspClients: Map<string, Client>) {
super(() => {});

this.lspClients = lspClients;

window.onDidChangeActiveTextEditor((editor) => {
this.displayAllTextDecorations();
});

}

displayAllTextDecorations() {
const document = window.activeTextEditor.document;

let workspaceFolder = workspace
.getWorkspaceFolder(document.uri)
.uri.toString();

const activeClient = this.lspClients.get(workspaceFolder);

// find file which we want to present hints for
let [fileIdKey, _] = (
Object.entries(activeClient.profileRunResult.file_map)
).find(([fileId, fileElement]) => {
return fileElement.path === document.uri.path;
}) as [string, FileInfo];

const fileId = Number(fileIdKey);

// Filter counts for file of interest
const filteredResults = activeClient.profileRunResult.opcodes_counts.filter(
([spanInfo, _]) => {
return spanInfo.file === fileId;
}
);

// Sum Opcodes for lines
const lineAccumulatedOpcodes = filteredResults
.map(([spanInfo, countInfo]) => {
const startPosition = document.positionAt(spanInfo.span.start);
const endPosition = document.positionAt(spanInfo.span.end);

const range = new Range(startPosition, endPosition);

return { range, countInfo };
})
// Lets accumulate ranges by line numbers
.reduce((accumulator, { range, countInfo }) => {
let lineInfo = accumulator[range.end.line];
if (!lineInfo) {
lineInfo = { ranges: [] };
}
lineInfo.ranges.push({ range, countInfo });
accumulator[range.end.line] = lineInfo;
return accumulator;
}, {});

// Count opcodes per line in accumulated collection
(Object.entries(lineAccumulatedOpcodes) as [number, any]).forEach(
([lineNumber, lineInfo]) => {
lineInfo.lineOpcodes = lineInfo.ranges.reduce(
({ acir_size, brillig_size }, { range, countInfo }) => {
acir_size = acir_size + countInfo.acir_size;
brillig_size = brillig_size + countInfo.brillig_size;
return { acir_size, brillig_size };
},
{ acir_size: 0, brillig_size: 0 }
);
}
);

updateDecorations(document, lineAccumulatedOpcodes);

// Used to show Hide Commands in Command Pallette
commands.executeCommand('setContext', 'noir.profileInfoPresent', true);
}

// Remove all decorations including onHover ones
hideDecorations() {
window.activeTextEditor.setDecorations(decoration, []);
}

dispose() {
this.#didChangeActiveTextEditor.dispose();
}
}

function updateDecorations(document, lineAccumulatedOpcodes: object) {
const decorations = Object.entries(lineAccumulatedOpcodes)
.flatMap(([lineNumber, lineInfo]) => {
const decorators = [];

const lineDecorators = lineDecorator(document, lineNumber, lineInfo);
decorators.push(lineDecorators);

let hoverDecorators = lineInfo.ranges.map(({ range, countInfo }) => {
const hoverMessage = `${
countInfo.acir_size ? `${countInfo.acir_size} ACIR` : ""
} ${
countInfo.brillig_size ? `${countInfo.brillig_size} Brillig` : ""
} opcodes`;
return {
range,
hoverMessage,
};
});
decorators.push(hoverDecorators);

return decorators;
})
.flat();

window.activeTextEditor.setDecorations(decoration, decorations);
}

function lineDecorator(document: any, lineNumber: string, lineInfo: any) {
const range: Range = document.lineAt(Number(lineNumber)).range;
const lineContent = `// ${
lineInfo.lineOpcodes.acir_size ? `${lineInfo.lineOpcodes.acir_size} ACIR` : ""
} ${lineInfo.brillig_size ? `${lineInfo.brillig_size} ACIR` : ""} opcodes`;
const decorator = {
range,
renderOptions: {
after: {
backgroundColor: new ThemeColor('editorInlayHint.background' ),
color: new ThemeColor('editorInlayHint.foreground' ),
contentText: lineContent,
fontWeight: "normal",
fontStyle: "normal",
textDecoration: `none; position: absolute;`,
},
},
};
return decorator;
}
33 changes: 32 additions & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
ServerCapabilities,
ServerOptions,
TextDocumentFilter,

} from "vscode-languageclient/node";

import { extensionName, languageId } from "./constants";
Expand All @@ -46,6 +47,30 @@ type NargoTests = {
}[];
};

export type FileId = number;
export type FileInfo = {
path: string;
source: string;
};

export type Span = {
start: number;
end: number;
};
export type SpanInFile = {
file: FileId;
span: Span;
};
export type OpcodesCounts = {
acir_size: number;
brillig_size: number;
};

export type NargoProfileRunResult = {
file_map: Map<FileId, FileInfo>;
opcodes_counts: [[SpanInFile, OpcodesCounts]];
};

type RunTestResult = {
id: string;
result: "pass" | "fail" | "error";
Expand Down Expand Up @@ -81,7 +106,7 @@ export default class Client extends LanguageClient {
#command: string;
#args: string[];
#output: OutputChannel;

profileRunResult: NargoProfileRunResult;
// This function wasn't added until vscode 1.81.0 so fake the type
#testController: TestController & {
invalidateTestResults?: (item: TestItem) => void;
Expand Down Expand Up @@ -175,6 +200,12 @@ export default class Client extends LanguageClient {
});
}

async refreshProfileInfo() {
const response = await this.sendRequest<NargoProfileRunResult>("nargo/profile/run", { package: ""});

this.profileRunResult = response;
}

async #fetchTests() {
const response = await this.sendRequest<NargoTests[]>("nargo/tests", {});

Expand Down
Loading

0 comments on commit 78e529a

Please sign in to comment.