From 9f8150a9fcb84d340112f5140a18c5f3ff0af528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Czajka?= <62751+lukaszcz@users.noreply.github.com> Date: Mon, 11 Nov 2024 18:49:53 +0100 Subject: [PATCH] Fix error underlining and popup problems (#153) * Updates Juvix version to 0.6.8 * Closes #152 * Makes silent typechecking (on change or save) call a task, which solves the problem of underlined errors not updating. * Adds the `--vscode` option to the invocation of the `typecheck` Juvix command. This makes the extension not compatible with Juvix versions earlier than 0.6.8. --- .vscode/launch.json | 2 +- juvix.version | 2 +- package-lock.json | 4 +- package.json | 35 ++++++++------- src/check.ts | 79 ++++------------------------------ src/config.ts | 5 +++ src/extension.ts | 4 -- src/formatter.ts | 1 + src/highlighting.ts | 12 +++++- src/tasks.ts | 14 +++++- src/utils/autorunDisposable.ts | 30 ------------- src/utils/base.ts | 29 ------------- 12 files changed, 57 insertions(+), 160 deletions(-) delete mode 100644 src/utils/autorunDisposable.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 9c23f01..102b461 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,5 +1,5 @@ { - "version": "0.2.4", + "version": "0.2.5", "configurations": [ { "name": "Run Extension", diff --git a/juvix.version b/juvix.version index 05e8a45..fae59ca 100644 --- a/juvix.version +++ b/juvix.version @@ -1 +1 @@ -0.6.6 +0.6.8 diff --git a/package-lock.json b/package-lock.json index 81e8bbe..9ba70d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "juvix-mode", - "version": "0.2.4", + "version": "0.2.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "juvix-mode", - "version": "0.2.4", + "version": "0.2.5", "license": "GPL-3.0", "dependencies": { "@vscode/vsce": "^2.15.0", diff --git a/package.json b/package.json index 86077bb..928e7cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "juvix-mode", - "version": "0.2.4", + "version": "0.2.5", "license": "GPL-3.0", "description": "Juvix Mode for VSCode", "displayName": "Juvix", @@ -442,23 +442,17 @@ "owner": "juvixerror", "source": "Juvix Error", "fileLocation": "autoDetect", - "pattern": [ - { - "kind": "location", - "regexp": "^(.+):(\\d+)(?:-(\\d+))?:(\\d+)-(\\d+): (\\w+).*", - "file": 1, - "line": 2, - "endLine": 3, - "column": 4, - "endColumn": 5, - "severity": 6 - }, - { - "regexp": "(.*)", - "message": 1, - "loop": true - } - ] + "pattern": { + "kind": "location", + "regexp": "^(.+):(\\d+)(?:-(\\d+))?:(\\d+)-(\\d+):\\s+(\\w+):(.*)$", + "file": 1, + "line": 2, + "endLine": 3, + "column": 4, + "endColumn": 5, + "severity": 6, + "message": 7 + } } ], "commands": [ @@ -818,6 +812,11 @@ "default": true, "description": "Disable ANSI formatting." }, + "juvix-mode.vscodeErrors": { + "type": "boolean", + "default": true, + "description": "Enable VSCode compatible error formatting." + }, "juvix-mode.showNameIds": { "type": "boolean", "default": false, diff --git a/src/check.ts b/src/check.ts index 8e89803..b9113ea 100644 --- a/src/check.ts +++ b/src/check.ts @@ -1,92 +1,29 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - import * as vscode from 'vscode'; import * as user from './config'; -import { isJuvixFile, runShellCommand, getDiagnosticFromError } from './utils/base'; +import { isJuvixFile } from './utils/base'; import { logger } from './utils/debug'; -import { setJuvixCommandStatusBarItem, inProgressJuvixCommandStatusBar, showExecResultJuvixStatusBar } from './statusbar'; -export async function activate(context: vscode.ExtensionContext, diagnosticCollection: vscode.DiagnosticCollection) { +export async function activate(context: vscode.ExtensionContext, typecheckTask: vscode.Task) { const config = new user.JuvixConfig(); - const command = 'juvix-mode.typecheck-silent'; - - const commandHandler = async (doc: vscode.TextDocument, content: string) => { - const activeEditor = vscode.window.activeTextEditor; - - if (activeEditor && activeEditor.document == doc) { - if (doc && isJuvixFile(doc)) { - const filePath = doc.fileName; - - const typecheckerCall = [ - config.getJuvixExec(), - config.getGlobalFlags(), - 'typecheck', - config.getTypeckeckFlags(), - filePath, - ].join(' '); - - inProgressJuvixCommandStatusBar('Typecheck'); - const { stdout, stderr, status } = await runShellCommand(typecheckerCall, content); - - if (status !== 0) { - showExecResultJuvixStatusBar(false, 'Typecheck', stderr); - const diag = getDiagnosticFromError(stderr); - if (diag) - diagnosticCollection.set(doc.uri, [diag]); - } - else { - showExecResultJuvixStatusBar(true, 'Typecheck', stdout); - diagnosticCollection.delete(doc.uri); - } - return { stdout, stderr, status }; - } - } - return undefined; - }; - - context.subscriptions.push( - vscode.commands.registerCommand(command, commandHandler), - ); - - - context.subscriptions.push( - vscode.workspace.onDidCloseTextDocument(doc => diagnosticCollection.delete(doc.uri)) - ); - - context.subscriptions.push( - vscode.window.onDidChangeActiveTextEditor(_ => { - setJuvixCommandStatusBarItem(); - } - )); - switch (config.typecheckOn()) { case 'change': context.subscriptions.push( - vscode.workspace.onDidChangeTextDocument(e => { + vscode.workspace.onDidChangeTextDocument(async e => { const doc = e.document; const activeEditor = vscode.window.activeTextEditor; if (activeEditor && activeEditor.document === doc && isJuvixFile(doc)) - vscode.commands.executeCommand( - 'juvix-mode.typecheck-silent', - doc, - doc.getText(), - ) + await vscode.tasks.executeTask(typecheckTask); }), ); break; case 'save': context.subscriptions.push( - vscode.workspace.onDidSaveTextDocument(doc => { + vscode.workspace.onDidSaveTextDocument(async doc => { const activeEditor = vscode.window.activeTextEditor; - if (activeEditor && activeEditor.document === doc && isJuvixFile(doc)) - vscode.commands.executeCommand( - 'juvix-mode.typecheck-silent', - doc, - doc.getText(), - ) + if (activeEditor && activeEditor.document === doc && isJuvixFile(doc)) { + await vscode.tasks.executeTask(typecheckTask); + } }), ); break; diff --git a/src/config.ts b/src/config.ts index a75e522..50d8d63 100644 --- a/src/config.ts +++ b/src/config.ts @@ -121,6 +121,10 @@ export class JuvixConfig { return this.workspaceConfig.get('noColors', true); } + public vscodeErrors(): boolean { + return this.workspaceConfig.get('vscodeErrors', true); + } + public showNameIds(): boolean { return this.workspaceConfig.get('showNameIds', false); } @@ -160,6 +164,7 @@ export class JuvixConfig { public getGlobalFlags(): string { const flags: string[] = []; if (this.noColors()) flags.push('--no-colors'); + if (this.vscodeErrors()) flags.push('--vscode'); if (this.showNameIds()) flags.push('--show-name-ids'); if (this.noTermination()) flags.push('--no-termination'); if (this.noPositivity()) flags.push('--no-positivity'); diff --git a/src/extension.ts b/src/extension.ts index 62050d5..41c2373 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -53,10 +53,6 @@ export async function activate(context: vscode.ExtensionContext) { ]; modules.forEach(module => module.activate(context)); - let juvixDiagnosticCollection = vscode.languages.createDiagnosticCollection('juvix'); - check.activate(context, juvixDiagnosticCollection); - context.subscriptions.push(juvixDiagnosticCollection); - vscode.commands.executeCommand('setContext', 'juvix-mode:ready', true); } } diff --git a/src/formatter.ts b/src/formatter.ts index a9d0995..c60f68e 100644 --- a/src/formatter.ts +++ b/src/formatter.ts @@ -34,6 +34,7 @@ export function activate(_context: vscode.ExtensionContext) { // this is the way to protect from unexpected behaviour of the `format` command return stdout !== '' ? [vscode.TextEdit.replace(range, stdout)] : []; } else { + // TODO: this should be parsed with the problem matcher const errMsg: string = res.stderr.toString(); logger.warn(errMsg); return []; diff --git a/src/highlighting.ts b/src/highlighting.ts index 93d88fd..0c31a05 100644 --- a/src/highlighting.ts +++ b/src/highlighting.ts @@ -31,7 +31,6 @@ map that associates a file path to the corresponding information for that file. export async function activate(context: vscode.ExtensionContext) { if (!config.enableSemanticSyntax()) return; try { - const semanticTokensProvider = new Highlighter(); const highlighterProvider = vscode.languages.registerDocumentSemanticTokensProvider( { language: 'Juvix', scheme: 'file' }, @@ -101,6 +100,13 @@ export const legend: vscode.SemanticTokensLegend = (function () { })(); export class Highlighter implements vscode.DocumentSemanticTokensProvider { + private _onDidChangeSemanticTokens = new vscode.EventEmitter(); + public onDidChangeSemanticTokens = this._onDidChangeSemanticTokens.event; + + public triggerRehighlighting(): void { + this._onDidChangeSemanticTokens.fire(); + } + async provideDocumentSemanticTokens( document: vscode.TextDocument, _token: vscode.CancellationToken, @@ -184,7 +190,7 @@ export class Highlighter implements vscode.DocumentSemanticTokensProvider { ? tk.interval.length : tk.interval.endCol : contentLines[l].length; - // lineLength represent the number of symbols we can see on the screen. + // lineLength represents the number of symbols we can see on the screen. // However, in js, length for unicode symbols works not as expected, but // rather shows the code units number. // So we need to actually calculate all the code units. @@ -267,3 +273,5 @@ export class Highlighter implements vscode.DocumentSemanticTokensProvider { return token; } } + +export const semanticTokensProvider = new Highlighter(); diff --git a/src/tasks.ts b/src/tasks.ts index c346123..16ddcba 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -5,6 +5,7 @@ import * as vscode from 'vscode'; import * as user from './config'; import { logger } from './utils/debug'; +import * as check from './check'; import { inProgressJuvixCommandStatusBar, showExecResultJuvixStatusBar } from './statusbar'; export const TASK_TYPE = 'Juvix'; @@ -37,6 +38,7 @@ export async function activate(context: vscode.ExtensionContext) { const cmdName = task.name.replace(' ', '-'); let useCmdName; if (cmdName === 'typecheck-silent') { + check.activate(context, task); useCmdName = 'typecheck'; } else { useCmdName = cmdName; @@ -68,7 +70,6 @@ export async function activate(context: vscode.ExtensionContext) { context.subscriptions.push(initDisp); const disp = vscode.tasks.onDidEndTaskProcess(e => { - if (e.execution.task.name === task.name) { if (e.exitCode === 0) { showExecResultJuvixStatusBar(true, useCmdName, ''); @@ -107,7 +108,13 @@ export class JuvixTaskProvider implements vscode.TaskProvider { command: 'typecheck', args: [config.getTypeckeckFlags(), '${file}'], group: vscode.TaskGroup.Build, - reveal: vscode.TaskRevealKind.Silent, + reveal: vscode.TaskRevealKind.Always, + }, + { + command: 'typecheck-silent', + args: [config.getTypeckeckFlags(), '${file}'], + group: vscode.TaskGroup.Build, + reveal: vscode.TaskRevealKind.Never, }, { command: 'compile', @@ -227,6 +234,9 @@ export async function JuvixTask( case 'update-dependencies': exec = new vscode.ShellExecution(JuvixExec + `dependencies update`); break; + case 'typecheck-silent': + exec = new vscode.ShellExecution(JuvixExec + ` typecheck ${fl}`); + break; default: exec = new vscode.ShellExecution(JuvixExec + ` ${input}`); break; diff --git a/src/utils/autorunDisposable.ts b/src/utils/autorunDisposable.ts deleted file mode 100644 index 96a37b4..0000000 --- a/src/utils/autorunDisposable.ts +++ /dev/null @@ -1,30 +0,0 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ - -/* - * Adapted from vscode-lean sources. - * See https://github.com/leanprover/vscode-lean - */ - -import { autorun } from 'mobx'; -import { Disposable } from 'vscode'; - -/** - * Like `autorun`, but more suited for working with Disposables. - * The `disposables` passed to `reaction` will be disposed when the reaction is triggered again. - */ -export function autorunDisposable( - reaction: (disposables: Disposable[]) => void, -): Disposable { - let lastDisposable = new Array(); - return { - dispose: autorun(() => { - for (const d of lastDisposable) { - d.dispose(); - } - lastDisposable = []; - reaction(lastDisposable); - }), - }; -} diff --git a/src/utils/base.ts b/src/utils/base.ts index e74d56c..b1e8390 100644 --- a/src/utils/base.ts +++ b/src/utils/base.ts @@ -1,6 +1,3 @@ -/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ import { spawn, spawnSync } from 'child_process'; import * as vscode from 'vscode'; import { Mutex } from 'async-mutex'; @@ -81,29 +78,3 @@ export async function runShellCommand(command: string, input?: string): Promise< } }); } - -const regexJuvixError = /(?.*):(?\d+):(?\d+)-?(?\d+)?:\s(?.*):\s?(?((\n|.)*))/g; - -export function getDiagnosticFromError(output: string): vscode.Diagnostic | undefined { - const { file, line, begin_col, end_col, err_type, msg } = regexJuvixError.exec(output)!.groups!; - if (!msg || !line || !begin_col) return undefined; - const range = new vscode.Range( - new vscode.Position(parseInt(line) - 1, parseInt(begin_col) - 1), - new vscode.Position(parseInt(line) - 1, parseInt(end_col ?? begin_col) - 1), - ); - let severity; - switch (err_type) { - case 'error': - severity = vscode.DiagnosticSeverity.Error; - break; - case 'warning': - severity = vscode.DiagnosticSeverity.Warning; - break; - case 'info': - severity = vscode.DiagnosticSeverity.Information; - break; - } - const diag = new vscode.Diagnostic(range, msg, severity); - diag.source = 'Juvix'; - return diag; -}