Skip to content

Commit eb56383

Browse files
committed
Add State Machine Parsing
1 parent b4077e4 commit eb56383

11 files changed

Lines changed: 376 additions & 30 deletions

File tree

client/src/extension.ts

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { connectToPort, findJavaExecutable, getAvailablePort, killProcess } from
99
import { SERVER_JAR, DEBUG_MODE, DEBUG_PORT } from "./constants";
1010
import { LiquidJavaWebviewProvider } from "./webview/provider";
1111
import { LJDiagnostic } from "./types";
12+
import { createMermaidDiagram } from "./fsm";
1213

1314
let serverProcess: child_process.ChildProcess;
1415
let client: LanguageClient;
@@ -22,22 +23,18 @@ let currentFile: string | undefined;
2223

2324
/**
2425
* Activates the LiquidJava extension
25-
* @param context The extension context
26+
* @param context
2627
*/
2728
export async function activate(context: vscode.ExtensionContext) {
2829
initLogging(context);
2930
initStatusBar(context);
3031
initCommandPalette(context);
3132
initWebview(context);
33+
initFileEvents(context);
3234
initHover();
3335

3436
logger.client.info("Activating LiquidJava extension...");
3537

36-
const activeEditor = vscode.window.activeTextEditor;
37-
if (activeEditor && activeEditor.document.languageId === "java") {
38-
currentFile = activeEditor.document.uri.fsPath;
39-
webviewProvider?.sendMessage({ type: "file", file: currentFile });
40-
}
4138
await applyItalicOverlay();
4239

4340
// find java executable path
@@ -69,7 +66,7 @@ export async function deactivate() {
6966

7067
/**
7168
* Initializes logging for the extension with an output channel
72-
* @param context The extension context
69+
* @param context
7370
*/
7471
function initLogging(context: vscode.ExtensionContext) {
7572
outputChannel = vscode.window.createOutputChannel("LiquidJava");
@@ -81,7 +78,7 @@ function initLogging(context: vscode.ExtensionContext) {
8178

8279
/**
8380
* Initializes the status bar for the extension
84-
* @param context The extension context
81+
* @param context
8582
*/
8683
function initStatusBar(context: vscode.ExtensionContext) {
8784
statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
@@ -94,7 +91,7 @@ function initStatusBar(context: vscode.ExtensionContext) {
9491

9592
/**
9693
* Initializes the command palette for the extension
97-
* @param context The extension context
94+
* @param context
9895
*/
9996
function initCommandPalette(context: vscode.ExtensionContext) {
10097
context.subscriptions.push(
@@ -113,7 +110,7 @@ function initCommandPalette(context: vscode.ExtensionContext) {
113110

114111
/**
115112
* Initializes the webview panel for the extension
116-
* @param context The extension context
113+
* @param context
117114
*/
118115
function initWebview(context: vscode.ExtensionContext) {
119116
webviewProvider = new LiquidJavaWebviewProvider(context.extensionUri);
@@ -138,15 +135,6 @@ function initWebview(context: vscode.ExtensionContext) {
138135
}
139136
})
140137
);
141-
// listen for active text editor changes
142-
context.subscriptions.push(
143-
vscode.window.onDidChangeActiveTextEditor(editor => {
144-
if (editor && editor.document.languageId === "java") {
145-
currentFile = editor.document.uri.fsPath;
146-
webviewProvider?.sendMessage({ type: "file", file: currentFile });
147-
}
148-
})
149-
);
150138
}
151139

152140
/**
@@ -172,9 +160,37 @@ function initHover() {
172160
});
173161
}
174162

163+
164+
/**
165+
* Initializes file system event listeners
166+
* @param context
167+
*/
168+
function initFileEvents(context: vscode.ExtensionContext) {
169+
// listen for active text editor changes
170+
context.subscriptions.push(
171+
vscode.window.onDidChangeActiveTextEditor(editor => {
172+
if (!editor || editor.document.languageId !== "java") return;
173+
handleActiveFileChange(editor);
174+
175+
}),
176+
vscode.workspace.onDidSaveTextDocument(document => {
177+
if (document.uri.scheme !== 'file' || document.languageId !== "java") return;
178+
requestStateMachine(document)
179+
})
180+
);
181+
}
182+
183+
async function requestStateMachine(document: vscode.TextDocument) {
184+
const sm: StateMachine = await client?.sendRequest("liquidjava/fsm", { uri: document.uri.toString() });
185+
if (!sm) return;
186+
187+
const diagram = createMermaidDiagram(sm);
188+
console.log(diagram);
189+
}
190+
175191
/**
176192
* Updates the status bar with the current state
177-
* @param state The state of the status bar: "loading", "stopped", "passed" or "failed"
193+
* @param state
178194
*/
179195
function updateStatusBar(state: "loading" | "stopped" | "passed" | "failed") {
180196
const icons = {
@@ -190,8 +206,8 @@ function updateStatusBar(state: "loading" | "stopped" | "passed" | "failed") {
190206

191207
/**
192208
* Runs the LiquidJava language server
193-
* @param context The extension context
194-
* @param javaExecutablePath The path to the Java executable
209+
* @param context
210+
* @param javaExecutablePath
195211
* @returns A promise to the port number the server is running on
196212
*/
197213
async function runLanguageServer(context: vscode.ExtensionContext, javaExecutablePath: string): Promise<number> {
@@ -230,8 +246,8 @@ async function runLanguageServer(context: vscode.ExtensionContext, javaExecutabl
230246

231247
/**
232248
* Starts the client and connects it to the language server
233-
* @param context The extension context
234-
* @param port The port the server is running on
249+
* @param context
250+
* @param port
235251
*/
236252
async function runClient(context: vscode.ExtensionContext, port: number) {
237253
const serverOptions: ServerOptions = () => {
@@ -270,6 +286,11 @@ async function runClient(context: vscode.ExtensionContext, port: number) {
270286
client.onNotification("liquidjava/diagnostics", (diagnostics: LJDiagnostic[]) => {
271287
handleLJDiagnostics(diagnostics);
272288
});
289+
290+
const editor = vscode.window.activeTextEditor;
291+
if (editor && editor.document.languageId === "java") {
292+
handleActiveFileChange(editor);
293+
}
273294
} catch (e) {
274295
vscode.window.showErrorMessage("LiquidJava failed to initialize: " + e.toString());
275296
logger.client.error("Failed to initialize: " + e.toString());
@@ -288,7 +309,7 @@ async function runClient(context: vscode.ExtensionContext, port: number) {
288309

289310
/**
290311
* Stops the LiquidJava extension
291-
* @param reason The reason for stopping the extension
312+
* @param reason
292313
*/
293314
async function stopExtension(reason: string) {
294315
if (!client && !serverProcess && !socket) {
@@ -323,7 +344,7 @@ async function stopExtension(reason: string) {
323344

324345
/**
325346
* Handles LiquidJava diagnostics received from the language server
326-
* @param diagnostics The LiquidJava diagnostics
347+
* @param diagnostics
327348
*/
328349
function handleLJDiagnostics(diagnostics: LJDiagnostic[]) {
329350
const containsError = diagnostics.some(d => d.category === "error");
@@ -335,3 +356,13 @@ function handleLJDiagnostics(diagnostics: LJDiagnostic[]) {
335356
webviewProvider?.sendMessage({ type: "diagnostics", diagnostics });
336357
currentDiagnostics = diagnostics;
337358
}
359+
360+
/**
361+
* Handles active file change events
362+
* @param editor
363+
*/
364+
function handleActiveFileChange(editor: vscode.TextEditor) {
365+
currentFile = editor.document.uri.fsPath;
366+
webviewProvider?.sendMessage({ type: "file", file: currentFile });
367+
requestStateMachine(editor.document);
368+
}

client/src/fsm.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
/**
3+
* Converts a StateMachine object to a Mermaid state diagram string
4+
* @param sm
5+
* @returns Mermaid diagram string
6+
*/
7+
export function createMermaidDiagram(sm: StateMachine): string {
8+
const lines: string[] = [];
9+
10+
// header
11+
lines.push('stateDiagram-v2');
12+
13+
// initial state
14+
lines.push(` [*] --> ${sm.initial}`);
15+
16+
// transitions
17+
sm.transitions.forEach(transition => {
18+
lines.push(` ${transition.from} --> ${transition.to} : ${transition.on}`);
19+
});
20+
21+
return lines.join('\n');
22+
}

client/src/types/fsm.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type StateMachine = {
2+
className: string;
3+
initial: string;
4+
states: string[];
5+
transitions: { from: string; to: string; on: string }[];
6+
};

client/src/webview/renderers/diagnostics/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ export function renderTranslationTable(translationTable: TranslationTable): stri
3333

3434
return /*html*/`
3535
<div class="translation-table">
36-
<h3>Translation Table</h3>
36+
<h3>Context Variables</h3>
3737
<table>
3838
<thead>
3939
<tr>
4040
<th>Variable</th>
41-
<th>Code</th>
41+
<th>Source</th>
4242
<th>Location</th>
4343
</tr>
4444
</thead>

server/src/main/java/LJDiagnosticsService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
import org.eclipse.lsp4j.services.TextDocumentService;
1515
import org.eclipse.lsp4j.services.WorkspaceService;
1616

17-
import dtos.DiagnosticConverter;
1817
import liquidjava.diagnostics.LJDiagnostic;
18+
import utils.DiagnosticConverter;
1919
import utils.PathUtils;
2020

2121
public class LJDiagnosticsService implements TextDocumentService, WorkspaceService {

server/src/main/java/LJLanguageServer.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,15 @@
77
import org.eclipse.lsp4j.WorkspaceFoldersOptions;
88
import org.eclipse.lsp4j.WorkspaceServerCapabilities;
99
import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
10+
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
1011
import org.eclipse.lsp4j.services.LanguageServer;
1112
import org.eclipse.lsp4j.services.TextDocumentService;
1213
import org.eclipse.lsp4j.services.WorkspaceService;
1314

15+
import dtos.uri.Uri;
16+
import fsm.StateMachine;
17+
import fsm.StateMachineParser;
18+
1419
public class LJLanguageServer implements LanguageServer {
1520

1621
private final LJDiagnosticsService diagnosticsService;
@@ -76,4 +81,11 @@ public void connect(LJLanguageClient client) {
7681
public void setTrace(Object params) {
7782
// suppress notification
7883
}
84+
85+
@JsonRequest("liquidjava/fsm")
86+
public CompletableFuture<StateMachine> fsm(Uri uri) {
87+
return CompletableFuture.supplyAsync(() -> {
88+
return StateMachineParser.parse(uri.uri());
89+
});
90+
}
7991
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package dtos.uri;
2+
3+
public record Uri(String uri) {}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package fsm;
2+
3+
import java.util.List;
4+
5+
/**
6+
* Represents a state machine
7+
*/
8+
public record StateMachine(
9+
String className,
10+
String initial,
11+
List<String> states,
12+
List<StateMachineTransition> transitions
13+
) { }

0 commit comments

Comments
 (0)