Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import { registerAutocomplete } from "./services/autocomplete";
import { refreshCodeLenses, registerCodeLens } from "./services/codelens";
import { runLanguageServer, stopLanguageServer } from "./lsp/server";
import { runClient, stopClient } from "./lsp/client";
import type { LiquidJavaTestApi } from "./types/test-api";

/**
* Activates the LiquidJava extension
* @param context The extension context
*/
export async function activate(context: vscode.ExtensionContext) {
export async function activate(context: vscode.ExtensionContext): Promise<LiquidJavaTestApi> {
context.subscriptions.push(extension.diagnosticsEmitter);
registerLogger(context);
extension.logger!.client.info("Activating LiquidJava extension...");

Expand All @@ -30,6 +32,22 @@ export async function activate(context: vscode.ExtensionContext) {
registerHover();
await applyItalicOverlay();
await startExtension(context);

const ready = extension.client?.isRunning()
? Promise.resolve()
: Promise.reject(new Error("LiquidJava language client did not start"));
// keep activation behavior while callers observe startup failures through ready
void ready.catch(() => {});

return {
ready,
onDiagnostics: extension.diagnosticsEmitter.event,
getState: () => ({
status: extension.status,
diagnostics: [...(extension.diagnostics ?? [])],
}),
onWebviewMessage: extension.webview!.onWebviewMessage,
};
}

/**
Expand Down
1 change: 1 addition & 0 deletions client/src/services/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export function handleLJDiagnostics(diagnostics: LJDiagnostic[]) {
extension.webview?.sendMessage({ type: "diagnostics", diagnostics });
if (extension.context)
extension.webview?.sendMessage({ type: "context", context: extension.context, errorAtCursor: extension.errorAtCursor });
extension.diagnosticsEmitter.fire(diagnostics);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A verifier crash fires no event. onDiagnostics only fires on liquidjava/diagnostics. When the verifier throws, the server catches it in LJDiagnosticsService.generateDiagnostics and sends liquidjava/failure instead, and handleLJFailure doesn't fire anything. A test waiting on onDiagnostics then sits until the 120s Mocha timeout, with nothing saying the server crashed. #130 asked for tests to be able to tell a crash apart from a slow run.

Suggest firing something from handleLJFailure too (a separate onFailure event, or a status-change event), so tests can fail fast with a clear message.

}

/**
Expand Down
1 change: 1 addition & 0 deletions client/src/services/webview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export function registerWebview(context: vscode.ExtensionContext) {

// webview provider
context.subscriptions.push(
extension.webview,
vscode.window.registerWebviewViewProvider(LiquidJavaWebviewProvider.viewType, extension.webview)
);
// show view command
Expand Down
1 change: 1 addition & 0 deletions client/src/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class ExtensionState {
// application state
file?: string;
diagnostics?: LJDiagnostic[];
readonly diagnosticsEmitter = new vscode.EventEmitter<LJDiagnostic[]>();
stateMachine?: LJStateMachine | null;
context?: LJContext;
currentSelection?: Range;
Expand Down
15 changes: 15 additions & 0 deletions client/src/types/test-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type * as vscode from "vscode";
import type { ExtensionStatus } from "../state";
import type { LJDiagnostic } from "./diagnostics";

export interface WebviewMessage {
direction: "toWebview" | "fromWebview";
message: any;
}

export interface LiquidJavaTestApi {
readonly ready: Promise<void>;
readonly onDiagnostics: vscode.Event<LJDiagnostic[]>;
getState(): { status: ExtensionStatus | undefined; diagnostics: LJDiagnostic[] };
readonly onWebviewMessage: vscode.Event<WebviewMessage>;
}
15 changes: 13 additions & 2 deletions client/src/webview/provider.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import * as vscode from 'vscode';
import { getHtml } from './html';
import { highlightRange, openFile } from '../services/editor';
import type { WebviewMessage } from '../types/test-api';

/**
* Webview provider for the LiquidJava extension
* Provides an interactive user interface for the LiquidJava diagnostics
*/
export class LiquidJavaWebviewProvider implements vscode.WebviewViewProvider {
export class LiquidJavaWebviewProvider implements vscode.WebviewViewProvider, vscode.Disposable {
public static readonly viewType = "liquidJavaView";
private view?: vscode.WebviewView;
private messageEmitter = new vscode.EventEmitter<any>();
public readonly onDidReceiveMessage = this.messageEmitter.event;
private webviewMessageEmitter = new vscode.EventEmitter<WebviewMessage>();
public readonly onWebviewMessage = this.webviewMessageEmitter.event;

constructor(private readonly extensionUri: vscode.Uri) {}

Expand All @@ -29,6 +32,7 @@ export class LiquidJavaWebviewProvider implements vscode.WebviewViewProvider {
// listen for messages coming from webview
webviewView.webview.onDidReceiveMessage(message => {
// emit the message to any external listeners
this.webviewMessageEmitter.fire({ direction: "fromWebview", message });
this.messageEmitter.fire(message);

// handle message
Expand All @@ -46,7 +50,14 @@ export class LiquidJavaWebviewProvider implements vscode.WebviewViewProvider {
* @param message
*/
public sendMessage(message: any) {
this.view?.webview.postMessage(message);
if (!this.view) return;
this.webviewMessageEmitter.fire({ direction: "toWebview", message });
this.view.webview.postMessage(message);
}

public dispose() {
this.messageEmitter.dispose();
this.webviewMessageEmitter.dispose();
}

/**
Expand Down
Loading