Skip to content

Commit

Permalink
Surfacing error diagnostics (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
bmingles committed Jul 16, 2024
1 parent 7737bcb commit d851f61
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 7 deletions.
9 changes: 9 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ export function activate(context: vscode.ExtensionContext) {
let selectedDhService: DhcService | null = null;
let connectionOptions = createConnectionOptions();

const diagnosticsCollection =
vscode.languages.createDiagnosticCollection('python');

const outputChannel = vscode.window.createOutputChannel('Deephaven', 'log');
const debugOutputChannel = new OutputChannelWithHistory(
context,
Expand Down Expand Up @@ -54,9 +57,15 @@ export function activate(context: vscode.ExtensionContext) {
context.subscriptions
);

// Clear diagnostics on save
vscode.workspace.onDidSaveTextDocument(doc => {
diagnosticsCollection.set(doc.uri, []);
});

const dhcServiceRegistry = new DhServiceRegistry(
DhcService,
new ExtendedMap<string, vscode.WebviewPanel>(),
diagnosticsCollection,
outputChannel,
toaster
);
Expand Down
44 changes: 37 additions & 7 deletions src/services/DhService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@ import * as vscode from 'vscode';
import type { dh as DhcType } from '../dh/dhc-types';
import { hasErrorCode } from '../util/typeUtils';
import { ConnectionAndSession, Disposable } from '../common';
import { ExtendedMap, formatTimestamp, Logger, Toaster } from '../util';
import {
ExtendedMap,
formatTimestamp,
Logger,
parseServerError,
Toaster,
} from '../util';
import { EventDispatcher } from './EventDispatcher';

const logger = new Logger('DhService');
Expand Down Expand Up @@ -35,23 +41,26 @@ export abstract class DhService<TDH, TClient>
constructor(
serverUrl: string,
panelRegistry: ExtendedMap<string, vscode.WebviewPanel>,
diagnosticsCollection: vscode.DiagnosticCollection,
outputChannel: vscode.OutputChannel,
toaster: Toaster
) {
super();

this.serverUrl = serverUrl;
this.panelRegistry = panelRegistry;
this.diagnosticsCollection = diagnosticsCollection;
this.outputChannel = outputChannel;
this.toaster = toaster;
}

public readonly serverUrl: string;
protected readonly subscriptions: (() => void)[] = [];

protected outputChannel: vscode.OutputChannel;
protected toaster: Toaster;
private panelRegistry: ExtendedMap<string, vscode.WebviewPanel>;
protected readonly outputChannel: vscode.OutputChannel;
protected readonly toaster: Toaster;
private readonly panelRegistry: ExtendedMap<string, vscode.WebviewPanel>;
private readonly diagnosticsCollection: vscode.DiagnosticCollection;
private cachedCreateClient: Promise<TClient> | null = null;
private cachedCreateSession: Promise<ConnectionAndSession<
DhcType.IdeConnection,
Expand Down Expand Up @@ -199,9 +208,8 @@ export abstract class DhService<TDH, TClient>
return;
}

// this.outputChannel.appendLine(
// `Sending${selectionOnly ? ' selected' : ''} code to: ${this.serverUrl}`
// );
// Clear previous diagnostics when cmd starts running
this.diagnosticsCollection.set(editor.document.uri, []);

if (this.session == null) {
await this.initDh();
Expand Down Expand Up @@ -251,6 +259,28 @@ export abstract class DhService<TDH, TClient>
this.outputChannel.appendLine(error);
this.toaster.error('An error occurred when running a command');

const { line, value } = parseServerError(error);

if (line != null) {
// If selectionOnly is true, the line number in the error will be
// relative to the selection
const offsetLine = selectionOnly
? line + editor.selection.start.line - 1
: line - 1;

const lineLength = editor.document.lineAt(offsetLine).text.length;

// Diagnostic representing the line of code that produced the server error
const diagnostic: vscode.Diagnostic = {
message: value == null ? error : `${value}\n${error}`,
severity: vscode.DiagnosticSeverity.Error,
range: new vscode.Range(offsetLine, 0, offsetLine, lineLength),
source: 'deephaven',
};

this.diagnosticsCollection.set(editor.document.uri, [diagnostic]);
}

return;
}

Expand Down
2 changes: 2 additions & 0 deletions src/services/DhServiceRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export class DhServiceRegistry<T extends DhcService> extends CacheService<
constructor(
serviceFactory: DhcServiceConstructor<T>,
panelRegistry: ExtendedMap<string, vscode.WebviewPanel>,
diagnosticsCollection: vscode.DiagnosticCollection,
outputChannel: vscode.OutputChannel,
toaster: Toaster
) {
Expand All @@ -23,6 +24,7 @@ export class DhServiceRegistry<T extends DhcService> extends CacheService<
const dhService = new serviceFactory(
serverUrl,
panelRegistry,
diagnosticsCollection,
outputChannel,
toaster
);
Expand Down
1 change: 1 addition & 0 deletions src/services/DhcService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const logger = new Logger('DhcService');
export type DhcServiceConstructor<T extends DhcService> = new (
serverUrl: string,
panelRegistry: ExtendedMap<string, vscode.WebviewPanel>,
diagnosticsCollection: vscode.DiagnosticCollection,
outputChannel: vscode.OutputChannel,
toaster: Toaster
) => T;
Expand Down
46 changes: 46 additions & 0 deletions src/util/errorUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Logger } from './Logger';

const logger = new Logger('errorUtils');

export interface ParsedError {
[key: string]: string | number | undefined;
type?: string;
value?: string;
line?: number;
namespace?: string;
file?: string;
traceback?: string;
}

/**
* Parse a server error string into a key-value object.
* @param error
*/
export function parseServerError(error: string): ParsedError {
const errorDetails: ParsedError = {};
const lines = error.split('\n');

if (lines[0] !== 'java.lang.RuntimeException: Error in Python interpreter:') {
logger.debug('Unrecognized error type:', error);
return errorDetails;
}

while (lines.length) {
const line = lines.shift()!;

const [key, value] = line.split(':');

if (key && value) {
// Once we hit the Traceback, accumulate remaining lines
if (key === 'Traceback (most recent call last)') {
errorDetails.traceback = [value, ...lines].join('\n');
break;
}

errorDetails[key.toLowerCase()] =
key === 'Line' ? Number(value.trim()) : value.trim();
}
}

return errorDetails;
}
1 change: 1 addition & 0 deletions src/util/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * from './downloadUtils';
export * from './errorUtils';
export * from './ExtendedMap';
export * from './isDisposable';
export * from './Logger';
Expand Down

0 comments on commit d851f61

Please sign in to comment.