diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..9209ef5 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,2 @@ +node_modules +out diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..f47f171 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,19 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint', 'prettier'], + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + rules: { + 'comma-spacing': ['error', { before: false, after: true }], + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', // or "error" + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + }, + ], + 'prettier/prettier': 'error', + }, +}; diff --git a/.github/workflows/formatting.yml b/.github/workflows/formatting.yml new file mode 100644 index 0000000..49c7de2 --- /dev/null +++ b/.github/workflows/formatting.yml @@ -0,0 +1,35 @@ +name: Formatting + +on: + pull_request: + merge_group: + push: + branches: + - master + +# This will cancel previous runs when a branch or PR is updated +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + eslint: + name: eslint + runs-on: ubuntu-latest + timeout-minutes: 30 + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - uses: actions/setup-node@v3 + with: + node-version: 18.17.1 + cache: 'npm' + cache-dependency-path: 'package-lock.json' + + - name: Install dependencies + run: npm ci + + - name: Run `npm run lint` + run: npm run lint diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..ef937f9 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "parser": "typescript", + "printWidth": 120, + "singleQuote": true, + "trailingComma": "all" +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 453ea9d..e72ffef 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,9 +2,5 @@ "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, - "cSpell.words": [ - "acir", - "brillig", - "nargo" - ] + "cSpell.words": ["acir", "brillig", "nargo"] } diff --git a/package.json b/package.json index 04b132d..2babf1a 100644 --- a/package.json +++ b/package.json @@ -134,6 +134,7 @@ "package": "vsce package", "test-compile": "tsc -p ./", "format": "prettier --write .", + "lint": "NODE_NO_WARNINGS=1 eslint . --ext .ts --ignore-path ./.eslintignore --max-warnings 0", "check-format": "prettier --check .", "deploy": "vsce publish" }, @@ -145,10 +146,12 @@ "@types/node": "^12.12.0", "@types/vscode": "1.67.0", "@types/which": "^3.0.0", - "@typescript-eslint/parser": "^5.59.9", + "@typescript-eslint/eslint-plugin": "^6.7.3", + "@typescript-eslint/parser": "^6.7.3", "esbuild": "^0.17.19", - "eslint": "^8.42.0", - "prettier": "^2.8.8", + "eslint": "^8.55.0", + "eslint-plugin-prettier": "^5.0.0", + "prettier": "3.0.3", "typescript": "^5.1.3", "vsce": "^2.15.0" } diff --git a/src/EditorLineDecorationManager.ts b/src/EditorLineDecorationManager.ts index 0c5ff0c..5ddab4c 100644 --- a/src/EditorLineDecorationManager.ts +++ b/src/EditorLineDecorationManager.ts @@ -2,24 +2,27 @@ 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, - }); + TextDocument, +} from 'vscode'; +import Client, { FileInfo, OpcodesCounts } from './client'; + +const decoration: TextEditorDecorationType = window.createTextEditorDecorationType({ + after: { + margin: '0 0 0 3em', + textDecoration: 'none', + }, + rangeBehavior: DecorationRangeBehavior.ClosedOpen, +}); + +type LineAccumulatedOpcodes = { + ranges: { range: Range; countInfo: OpcodesCounts }[]; + lineOpcodes: { acir_size: number; brillig_size: number }; +}; export class EditorLineDecorationManager extends Disposable { #didChangeActiveTextEditor: Disposable; @@ -30,7 +33,7 @@ export class EditorLineDecorationManager extends Disposable { this.lspClients = lspClients; - window.onDidChangeActiveTextEditor((editor) => { + window.onDidChangeActiveTextEditor((_editor) => { this.displayAllTextDecorations(); }); } @@ -39,69 +42,64 @@ export class EditorLineDecorationManager extends Disposable { const document = window?.activeTextEditor?.document; if (document) { - let workspaceFolder = workspace - .getWorkspaceFolder(document.uri) - .uri.toString(); + const workspaceFolder = workspace.getWorkspaceFolder(document.uri).uri.toString(); const activeClient = this.lspClients.get(workspaceFolder); if (activeClient?.profileRunResult) { // 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 [fileIdKey, _] = Object.entries(activeClient.profileRunResult.file_map).find( + ([_fileId, fileElement]) => 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; - } - ); + 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 }; + const lineAccumulatedOpcodes: Record = + 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; }, - { acir_size: 0, brillig_size: 0 } + {} as Record, ); - } - ); - updateDecorations(document, lineAccumulatedOpcodes); + // Count opcodes per line in accumulated collection + (Object.values(lineAccumulatedOpcodes) as LineAccumulatedOpcodes[]).forEach((lineInfo) => { + lineInfo.lineOpcodes = lineInfo.ranges.reduce( + ({ acir_size, brillig_size }, { 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 as Record); // Used to show Hide Commands in Command Pallette - commands.executeCommand("setContext", "noir.profileInfoPresent", true); + commands.executeCommand('setContext', 'noir.profileInfoPresent', true); } } } @@ -116,7 +114,7 @@ export class EditorLineDecorationManager extends Disposable { } } -function updateDecorations(document, lineAccumulatedOpcodes: object) { +function updateDecorations(document: TextDocument, lineAccumulatedOpcodes: Record) { const decorations = Object.entries(lineAccumulatedOpcodes) .flatMap(([lineNumber, lineInfo]) => { const decorators = []; @@ -124,11 +122,9 @@ function updateDecorations(document, lineAccumulatedOpcodes: object) { 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` : "" + const 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, @@ -144,22 +140,20 @@ function updateDecorations(document, lineAccumulatedOpcodes: object) { window?.activeTextEditor?.setDecorations(decoration, decorations); } -function lineDecorator(document: any, lineNumber: string, lineInfo: any) { +function lineDecorator(document: TextDocument, lineNumber: string, lineInfo: LineAccumulatedOpcodes) { 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 lineContent = `// ${lineInfo.lineOpcodes.acir_size ? `${lineInfo.lineOpcodes.acir_size} ACIR` : ''} ${ + lineInfo.lineOpcodes.brillig_size ? `${lineInfo.lineOpcodes.brillig_size} Brillig` : '' + } opcodes`; const decorator = { range, renderOptions: { after: { - backgroundColor: new ThemeColor("editorInlayHint.background"), - color: new ThemeColor("editorInlayHint.foreground"), + backgroundColor: new ThemeColor('editorInlayHint.background'), + color: new ThemeColor('editorInlayHint.foreground'), contentText: lineContent, - fontWeight: "normal", - fontStyle: "normal", + fontWeight: 'normal', + fontStyle: 'normal', textDecoration: `none; position: absolute;`, }, }, diff --git a/src/client.ts b/src/client.ts index 9234337..f114fcf 100644 --- a/src/client.ts +++ b/src/client.ts @@ -12,7 +12,7 @@ import { OutputChannel, CancellationToken, TestRunRequest, -} from "vscode"; +} from 'vscode'; import { LanguageClient, @@ -20,11 +20,10 @@ import { ServerCapabilities, ServerOptions, TextDocumentFilter, +} from 'vscode-languageclient/node'; -} from "vscode-languageclient/node"; - -import { extensionName, languageId } from "./constants"; -import findNargo from "./find-nargo"; +import { extensionName, languageId } from './constants'; +import findNargo from './find-nargo'; type NargoCapabilities = { nargo?: { @@ -73,30 +72,30 @@ export type NargoProfileRunResult = { type RunTestResult = { id: string; - result: "pass" | "fail" | "error"; + result: 'pass' | 'fail' | 'error'; message: string; }; function globFromUri(uri: Uri, glob: string) { // globs always need to use `/` - return `${uri.fsPath}${glob}`.replaceAll("\\", "/"); + return `${uri.fsPath}${glob}`.replaceAll('\\', '/'); } function getLspCommand(uri: Uri) { - let config = workspace.getConfiguration("noir", uri); + const config = workspace.getConfiguration('noir', uri); - let lspEnabled = config.get("enableLSP"); + const lspEnabled = config.get('enableLSP'); if (!lspEnabled) { return; } - let command = config.get("nargoPath") || findNargo(); + const command = config.get('nargoPath') || findNargo(); - let flags = config.get("nargoFlags") || ""; + const flags = config.get('nargoFlags') || ''; // Remove empty strings from the flags list - let args = ["lsp", ...flags.split(" ")].filter((arg) => arg !== ""); + const args = ['lsp', ...flags.split(' ')].filter((arg) => arg !== ''); return [command, args] as const; } @@ -113,33 +112,33 @@ export default class Client extends LanguageClient { }; constructor(uri: Uri, workspaceFolder?: WorkspaceFolder) { - let outputChannel = window.createOutputChannel(extensionName, languageId); + const outputChannel = window.createOutputChannel(extensionName, languageId); - let [command, args] = getLspCommand(uri); + const [command, args] = getLspCommand(uri); - let documentSelector: TextDocumentFilter[] = []; + const documentSelector: TextDocumentFilter[] = []; if (workspaceFolder) { documentSelector.push({ - scheme: "file", + scheme: 'file', language: languageId, // Glob starts with `/` because it just appends both segments - pattern: `${globFromUri(uri, "/**/*")}`, + pattern: `${globFromUri(uri, '/**/*')}`, }); } else { documentSelector.push({ scheme: uri.scheme, language: languageId, - pattern: `${globFromUri(uri, "")}`, + pattern: `${globFromUri(uri, '')}`, }); } - let clientOptions: LanguageClientOptions = { + const clientOptions: LanguageClientOptions = { documentSelector, workspaceFolder, outputChannel, }; - let serverOptions: ServerOptions = { + const serverOptions: ServerOptions = { command, args, }; @@ -152,7 +151,7 @@ export default class Client extends LanguageClient { this.#output = outputChannel; // TODO: Figure out how to do type-safe onNotification - this.onNotification("nargo/tests/update", (testData: NargoTests) => { + this.onNotification('nargo/tests/update', (testData: NargoTests) => { this.#updateTests(testData); }); @@ -160,16 +159,16 @@ export default class Client extends LanguageClient { fillClientCapabilities: () => {}, initialize: (capabilities: ServerCapabilities & NargoCapabilities) => { outputChannel.appendLine(`${JSON.stringify(capabilities)}`); - if (typeof capabilities.nargo?.tests !== "undefined") { + if (typeof capabilities.nargo?.tests !== 'undefined') { this.#testController = tests.createTestController( // We prefix with our ID namespace but we also tie these to the URI since they need to be unique `NoirWorkspaceTests-${uri.toString()}`, - "Noir Workspace Tests" + 'Noir Workspace Tests', ); if (capabilities.nargo.tests.fetch) { // TODO: reload a single test if provided as the function argument - this.#testController.resolveHandler = async (test) => { + this.#testController.resolveHandler = async (_test) => { await this.#fetchTests(); }; this.#testController.refreshHandler = async (token) => { @@ -179,18 +178,18 @@ export default class Client extends LanguageClient { if (capabilities.nargo.tests.run) { this.#testController.createRunProfile( - "Run Tests", + 'Run Tests', TestRunProfileKind.Run, async (request, token) => { await this.#runTest(request, token); }, - true + true, ); } } }, getState: () => { - return { kind: "static" }; + return { kind: 'static' }; }, dispose: () => { if (this.#testController) { @@ -201,13 +200,13 @@ export default class Client extends LanguageClient { } async refreshProfileInfo() { - const response = await this.sendRequest("nargo/profile/run", { package: ""}); + const response = await this.sendRequest('nargo/profile/run', { package: '' }); this.profileRunResult = response; } async #fetchTests() { - const response = await this.sendRequest("nargo/tests", {}); + const response = await this.sendRequest('nargo/tests', {}); response.forEach((testData) => { this.#createTests(testData); @@ -215,11 +214,7 @@ export default class Client extends LanguageClient { } async #refreshTests(token: CancellationToken) { - const response = await this.sendRequest( - "nargo/tests", - {}, - token - ); + const response = await this.sendRequest('nargo/tests', {}, token); response.forEach((testData) => { this.#updateTests(testData); }); @@ -248,22 +243,26 @@ export default class Client extends LanguageClient { // but this is fine because the test pass/fail icons are propagated upward if (test.parent) { // If we have these tests, the server will be able to run them with this message - const { id, result, message } = await this.sendRequest( - "nargo/tests/run", + const { + id: _, + result, + message, + } = await this.sendRequest( + 'nargo/tests/run', { id: test.id, }, - token + token, ); // TODO: Handle `test.id !== id`. I'm not sure if it is possible for this to happen in normal usage - if (result === "pass") { + if (result === 'pass') { run.passed(test); continue; } - if (result === "fail" || result === "error") { + if (result === 'fail' || result === 'error') { run.failed(test, new TestMessage(message)); continue; } @@ -277,17 +276,10 @@ export default class Client extends LanguageClient { } #createTests(testData: NargoTests) { - let pkg = this.#testController.createTestItem( - testData.package, - testData.package - ); + const pkg = this.#testController.createTestItem(testData.package, testData.package); testData.tests.forEach((test) => { - let item = this.#testController.createTestItem( - test.id, - test.label, - Uri.parse(test.uri) - ); + const item = this.#testController.createTestItem(test.id, test.label, Uri.parse(test.uri)); item.range = test.range; pkg.children.add(item); }); @@ -297,8 +289,8 @@ export default class Client extends LanguageClient { #updateTests(testData: NargoTests) { // This function wasn't added until vscode 1.81.0 so we check for it - if (typeof this.#testController.invalidateTestResults === "function") { - let pkg = this.#testController.items.get(testData.package); + if (typeof this.#testController.invalidateTestResults === 'function') { + const pkg = this.#testController.items.get(testData.package); this.#testController.invalidateTestResults(pkg); } @@ -306,8 +298,8 @@ export default class Client extends LanguageClient { } async start(): Promise { - let command = this.#command; - let args = this.#args.join(" "); + const command = this.#command; + const args = this.#args.join(' '); this.info(`Starting LSP client using command: ${command} ${args}`); await super.start(); diff --git a/src/constants.ts b/src/constants.ts index 249a1e3..1e612aa 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,3 +1,3 @@ -export let extensionName = "Noir Language Server"; +export const extensionName = 'Noir Language Server'; -export let languageId = "noir"; +export const languageId = 'noir'; diff --git a/src/extension.ts b/src/extension.ts index 631cd16..094f4ba 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -35,17 +35,16 @@ import { ProcessExecution, window, ProgressLocation, -} from "vscode"; +} from 'vscode'; -import { languageId } from "./constants"; -import Client from "./client"; -import findNargo from "./find-nargo"; -import { lspClients, editorLineDecorationManager } from "./noir"; +import { languageId } from './constants'; +import Client from './client'; +import findNargo from './find-nargo'; +import { lspClients, editorLineDecorationManager } from './noir'; -let activeCommands: Map = new Map(); +const activeCommands: Map = new Map(); - -let activeMutex: Set = new Set(); +const activeMutex: Set = new Set(); function mutex(key: string, fn: (...args: unknown[]) => Promise) { return (...args) => { @@ -58,7 +57,7 @@ function mutex(key: string, fn: (...args: unknown[]) => Promise) { // Rethrow on a "next tick" to break out of the promise wrapper queueMicrotask(() => { throw err; - }) + }), ) .finally(() => { activeMutex.delete(key); @@ -67,9 +66,9 @@ function mutex(key: string, fn: (...args: unknown[]) => Promise) { } function dirpathFromUri(uri: Uri): string { - let dirPath = uri.toString(); - if (!dirPath.endsWith("/")) { - return dirPath + "/"; + const dirPath = uri.toString(); + if (!dirPath.endsWith('/')) { + return dirPath + '/'; } return dirPath; } @@ -89,9 +88,9 @@ function sortWorkspaceFolders(): string[] { } function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder { - let sorted = sortWorkspaceFolders(); - for (let element of sorted) { - let dirpath = dirpathFromUri(folder.uri); + const sorted = sortWorkspaceFolders(); + for (const element of sorted) { + const dirpath = dirpathFromUri(folder.uri); if (dirpath.startsWith(element)) { return workspace.getWorkspaceFolder(Uri.parse(element))!; } @@ -99,36 +98,33 @@ function getOuterMostWorkspaceFolder(folder: WorkspaceFolder): WorkspaceFolder { return folder; } -let INTERNAL_COMMANDS = [ - { type: "nargo", command: "test", group: TaskGroup.Test }, - { type: "nargo", command: "compile", group: TaskGroup.Build }, - { type: "nargo", command: "info", group: TaskGroup.Build }, - { type: "nargo", command: "execute", group: TaskGroup.Build }, +const INTERNAL_COMMANDS = [ + { type: 'nargo', command: 'test', group: TaskGroup.Test }, + { type: 'nargo', command: 'compile', group: TaskGroup.Build }, + { type: 'nargo', command: 'info', group: TaskGroup.Build }, + { type: 'nargo', command: 'execute', group: TaskGroup.Build }, ]; function registerCommands(uri: Uri) { - let file = uri.toString(); - let config = workspace.getConfiguration("noir", uri); + const file = uri.toString(); + const config = workspace.getConfiguration('noir', uri); - let nargoPath = config.get("nargoPath") || findNargo(); + const nargoPath = config.get('nargoPath') || findNargo(); - let nargoFlags = config.get("nargoFlags") || []; + const nargoFlags = config.get('nargoFlags') || []; - let commands$: Disposable[] = []; - for (let { type, command, group } of INTERNAL_COMMANDS) { - let internalName = `${type}.${command}`; - let displayName = `${type} ${command}`; - let command$ = commands.registerCommand(internalName, async (...args) => { - let task = new Task( + const commands$: Disposable[] = []; + for (const { type, command, group } of INTERNAL_COMMANDS) { + const internalName = `${type}.${command}`; + const displayName = `${type} ${command}`; + const command$ = commands.registerCommand(internalName, async (...args) => { + const task = new Task( { type, command }, TaskScope.Workspace, displayName, languageId, - new ProcessExecution( - nargoPath, - [command].concat(nargoFlags).concat(args) - ), - [] + new ProcessExecution(nargoPath, [command].concat(nargoFlags).concat(args)), + [], ); task.group = group; // We set `isBackground` to `true` to avoid showing the internal task as "recently used" @@ -146,48 +142,38 @@ function registerCommands(uri: Uri) { commands$.push(command$); } - - let profileCommand$ = commands.registerCommand( - "nargo.profile", - async (...args) => { - - window.withProgress({ + const profileCommand$ = commands.registerCommand('nargo.profile', async (..._args) => { + window.withProgress( + { location: ProgressLocation.Window, cancellable: false, - title: 'Getting Profile Information' - }, async (progress) => { - - progress.report({ increment: 0 }); - - let workspaceFolder = workspace.getWorkspaceFolder(uri).uri.toString(); + title: 'Getting Profile Information', + }, + async (progress) => { + progress.report({ increment: 0 }); + + const workspaceFolder = workspace.getWorkspaceFolder(uri).uri.toString(); const activeClient = lspClients.get(workspaceFolder); - + await activeClient.refreshProfileInfo(); editorLineDecorationManager.displayAllTextDecorations(); - - progress.report({ increment: 100 }); - }); - } - ); + progress.report({ increment: 100 }); + }, + ); + }); commands$.push(profileCommand$); - let hideProfileInformationCommand$ = commands.registerCommand( - "nargo.profile.hide", - async (...args) => { - - editorLineDecorationManager.hideDecorations(); - - } - ); + const hideProfileInformationCommand$ = commands.registerCommand('nargo.profile.hide', async (..._args) => { + editorLineDecorationManager.hideDecorations(); + }); commands$.push(hideProfileInformationCommand$); - activeCommands.set(file, Disposable.from(...commands$)); } function disposeCommands(uri: Uri) { - let file = uri.toString(); - let commands$ = activeCommands.get(file); + const file = uri.toString(); + const commands$ = activeCommands.get(file); commands$.dispose(); } @@ -208,18 +194,18 @@ function disposeWorkspaceCommands(workspaceFolder: WorkspaceFolder) { } async function addFileClient(uri: Uri) { - let file = uri.toString(); + const file = uri.toString(); if (!lspClients.has(file)) { // Start the client. This will also launch the server - let client = new Client(uri); + const client = new Client(uri); lspClients.set(file, client); await client.start(); } } async function removeFileClient(uri: Uri) { - let file = uri.toString(); - let client = lspClients.get(file); + const file = uri.toString(); + const client = lspClients.get(file); if (client) { await client.stop(); lspClients.delete(file); @@ -227,18 +213,18 @@ async function removeFileClient(uri: Uri) { } async function addWorkspaceClient(workspaceFolder: WorkspaceFolder) { - let workspacePath = workspaceFolder.uri.toString(); + const workspacePath = workspaceFolder.uri.toString(); if (!lspClients.has(workspacePath)) { // Start the client. This will also launch the server - let client = new Client(workspaceFolder.uri, workspaceFolder); + const client = new Client(workspaceFolder.uri, workspaceFolder); lspClients.set(workspacePath, client); await client.start(); } } async function removeWorkspaceClient(workspaceFolder: WorkspaceFolder) { - let workspacePath = workspaceFolder.uri.toString(); - let client = lspClients.get(workspacePath); + const workspacePath = workspaceFolder.uri.toString(); + const client = lspClients.get(workspacePath); if (client) { await client.stop(); lspClients.delete(workspacePath); @@ -246,20 +232,18 @@ async function removeWorkspaceClient(workspaceFolder: WorkspaceFolder) { } async function restartAllClients() { - for (let client of lspClients.values()) { + for (const client of lspClients.values()) { await client.restart(); } } -async function didOpenTextDocument( - document: TextDocument -): Promise { +async function didOpenTextDocument(document: TextDocument): Promise { // We are only interested in language mode text if (document.languageId !== languageId) { return Disposable.from(); } - let uri = document.uri; + const uri = document.uri; let folder = workspace.getWorkspaceFolder(uri); let configHandler; if (folder) { @@ -269,33 +253,30 @@ async function didOpenTextDocument( await addWorkspaceClient(folder); registerWorkspaceCommands(folder); - configHandler = mutex( - folder.uri.toString(), - async (e: ConfigurationChangeEvent) => { - if (e.affectsConfiguration("noir.nargoFlags", folder.uri)) { - disposeWorkspaceCommands(folder); - await removeWorkspaceClient(folder); - await addWorkspaceClient(folder); - registerWorkspaceCommands(folder); - } - - if (e.affectsConfiguration("noir.nargoPath", folder.uri)) { - disposeWorkspaceCommands(folder); - await removeWorkspaceClient(folder); - await addWorkspaceClient(folder); - registerWorkspaceCommands(folder); - } - - if (e.affectsConfiguration("noir.enableLSP", folder.uri)) { - await removeWorkspaceClient(folder); - await addWorkspaceClient(folder); - } + configHandler = mutex(folder.uri.toString(), async (e: ConfigurationChangeEvent) => { + if (e.affectsConfiguration('noir.nargoFlags', folder.uri)) { + disposeWorkspaceCommands(folder); + await removeWorkspaceClient(folder); + await addWorkspaceClient(folder); + registerWorkspaceCommands(folder); } - ); + + if (e.affectsConfiguration('noir.nargoPath', folder.uri)) { + disposeWorkspaceCommands(folder); + await removeWorkspaceClient(folder); + await addWorkspaceClient(folder); + registerWorkspaceCommands(folder); + } + + if (e.affectsConfiguration('noir.enableLSP', folder.uri)) { + await removeWorkspaceClient(folder); + await addWorkspaceClient(folder); + } + }); } else { // We only want to handle `file:` and `untitled:` schemes because // vscode sends `output:` schemes for markdown responses from our LSP - if (uri.scheme !== "file" && uri.scheme !== "untitled") { + if (uri.scheme !== 'file' && uri.scheme !== 'untitled') { return Disposable.from(); } @@ -303,29 +284,26 @@ async function didOpenTextDocument( await addFileClient(uri); registerFileCommands(uri); - configHandler = mutex( - uri.toString(), - async (e: ConfigurationChangeEvent) => { - if (e.affectsConfiguration("noir.nargoFlags", uri)) { - disposeFileCommands(uri); - await removeFileClient(uri); - await addFileClient(uri); - registerFileCommands(uri); - } - - if (e.affectsConfiguration("noir.nargoPath", uri)) { - disposeFileCommands(uri); - await removeFileClient(uri); - await addFileClient(uri); - registerFileCommands(uri); - } - - if (e.affectsConfiguration("noir.enableLSP", uri)) { - await removeFileClient(uri); - await addFileClient(uri); - } + configHandler = mutex(uri.toString(), async (e: ConfigurationChangeEvent) => { + if (e.affectsConfiguration('noir.nargoFlags', uri)) { + disposeFileCommands(uri); + await removeFileClient(uri); + await addFileClient(uri); + registerFileCommands(uri); } - ); + + if (e.affectsConfiguration('noir.nargoPath', uri)) { + disposeFileCommands(uri); + await removeFileClient(uri); + await addFileClient(uri); + registerFileCommands(uri); + } + + if (e.affectsConfiguration('noir.enableLSP', uri)) { + await removeFileClient(uri); + await addFileClient(uri); + } + }); } return workspace.onDidChangeConfiguration(configHandler); @@ -339,33 +317,26 @@ async function didChangeWorkspaceFolders(event: WorkspaceFoldersChangeEvent) { // up when a file is opened // Remove any clients for workspaces that were closed - for (let folder of event.removed) { + for (const folder of event.removed) { await removeWorkspaceClient(folder); } } export async function activate(context: ExtensionContext): Promise { - let didOpenTextDocument$ = - workspace.onDidOpenTextDocument(didOpenTextDocument); - let didChangeWorkspaceFolders$ = workspace.onDidChangeWorkspaceFolders( - didChangeWorkspaceFolders - ); - let restart$ = commands.registerCommand("noir.restart", restartAllClients); - - context.subscriptions.push( - didOpenTextDocument$, - didChangeWorkspaceFolders$, - restart$ - ); - - for (let doc of workspace.textDocuments) { + const didOpenTextDocument$ = workspace.onDidOpenTextDocument(didOpenTextDocument); + const didChangeWorkspaceFolders$ = workspace.onDidChangeWorkspaceFolders(didChangeWorkspaceFolders); + const restart$ = commands.registerCommand('noir.restart', restartAllClients); + + context.subscriptions.push(didOpenTextDocument$, didChangeWorkspaceFolders$, restart$); + + for (const doc of workspace.textDocuments) { const disposable = await didOpenTextDocument(doc); context.subscriptions.push(disposable); } } export async function deactivate(): Promise { - for (let client of lspClients.values()) { + for (const client of lspClients.values()) { await client.stop(); } } diff --git a/src/find-nargo.ts b/src/find-nargo.ts index 3ea15e4..04d40de 100644 --- a/src/find-nargo.ts +++ b/src/find-nargo.ts @@ -1,6 +1,6 @@ -import which from "which"; +import which from 'which'; -const nargoBinaries = ["nargo"]; +const nargoBinaries = ['nargo']; export default function findNargo() { for (const bin of nargoBinaries) { @@ -12,5 +12,5 @@ export default function findNargo() { // Not found } } - throw new Error("Unable to locate any nargo binary. Did you install it?"); + throw new Error('Unable to locate any nargo binary. Did you install it?'); } diff --git a/src/noir.ts b/src/noir.ts index b053598..5829e77 100644 --- a/src/noir.ts +++ b/src/noir.ts @@ -1,6 +1,6 @@ -import { EditorLineDecorationManager } from "./EditorLineDecorationManager"; -import Client from "./client"; +import { EditorLineDecorationManager } from './EditorLineDecorationManager'; +import Client from './client'; -export let lspClients: Map = new Map(); +export const lspClients: Map = new Map(); export const editorLineDecorationManager = new EditorLineDecorationManager(lspClients); diff --git a/syntaxes/noir.tmLanguage.json b/syntaxes/noir.tmLanguage.json index fb9c881..429c3c6 100644 --- a/syntaxes/noir.tmLanguage.json +++ b/syntaxes/noir.tmLanguage.json @@ -324,4 +324,4 @@ } }, "scopeName": "source.nr" -} \ No newline at end of file +}