Skip to content

Commit bdb66bb

Browse files
authored
Bug Fixes (#84)
1 parent a861277 commit bdb66bb

23 files changed

Lines changed: 187 additions & 112 deletions

client/src/extension.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { runClient, stopClient } from "./lsp/client";
1919
*/
2020
export async function activate(context: vscode.ExtensionContext) {
2121
registerLogger(context);
22-
extension.logger.client.info("Activating LiquidJava extension...");
22+
extension.logger!.client.info("Activating LiquidJava extension...");
2323

2424
registerStatusBar(context);
2525
registerCommands(context);
@@ -48,27 +48,27 @@ export async function deactivate() {
4848
export async function startExtension(context: vscode.ExtensionContext) {
4949
// check if already running
5050
if (extension.client || extension.serverProcess) {
51-
extension.logger.client.info("LiquidJava is already running");
51+
extension.logger!.client.info("LiquidJava is already running");
5252
return;
5353
}
54-
extension.logger.client.info("Starting LiquidJava...");
54+
extension.logger!.client.info("Starting LiquidJava...");
5555

5656
// find java executable path
5757
const javaExecutablePath = findJavaExecutable();
5858
if (!javaExecutablePath) {
5959
vscode.window.showErrorMessage("LiquidJava - Java Runtime Not Found in JAVA_HOME or PATH");
60-
extension.logger.client.error("Java Runtime not found in JAVA_HOME or PATH");
60+
extension.logger!.client.error("Java Runtime not found in JAVA_HOME or PATH");
6161
updateStatusBar("stopped");
6262
return;
6363
}
64-
extension.logger.client.info("Using Java at: " + javaExecutablePath);
64+
extension.logger!.client.info("Using Java at: " + javaExecutablePath);
6565

6666
// start server
67-
extension.logger.client.info("Starting LiquidJava language server...");
67+
extension.logger!.client.info("Starting LiquidJava language server...");
6868
const port = await runLanguageServer(context, javaExecutablePath);
6969

7070
// start client
71-
extension.logger.client.info("Starting LiquidJava client...");
71+
extension.logger!.client.info("Starting LiquidJava client...");
7272
await runClient(context, port);
7373
}
7474

client/src/lsp/client.ts

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,17 @@ import { handleContext } from '../services/context';
1515
* @param port The port number the server is running on
1616
*/
1717
export async function runClient(context: vscode.ExtensionContext, port: number) {
18-
const serverOptions: ServerOptions = () => {
19-
return new Promise(async (resolve, reject) => {
20-
try {
21-
extension.socket = await connectToPort(port);
22-
resolve({
23-
writer: extension.socket,
24-
reader: extension.socket,
25-
});
26-
} catch (error) {
27-
await stopClient("Failed to connect to server");
28-
reject(error);
18+
const serverOptions: ServerOptions = async () => {
19+
try {
20+
extension.socket = await connectToPort(port)
21+
return {
22+
writer: extension.socket,
23+
reader: extension.socket,
2924
}
30-
});
25+
} catch (error) {
26+
await stopClient("Failed to connect to server")
27+
throw error
28+
}
3129
};
3230
const clientOptions: LanguageClientOptions = {
3331
documentSelector: [{ language: "java" }],
@@ -38,7 +36,7 @@ export async function runClient(context: vscode.ExtensionContext, port: number)
3836

3937
try {
4038
await extension.client.start();
41-
extension.logger.client.info("Extension is ready");
39+
extension.logger!.client.info("Extension is ready");
4240

4341
extension.client.onNotification("liquidjava/diagnostics", (diagnostics: LJDiagnostic[]) => {
4442
handleLJDiagnostics(diagnostics);
@@ -53,8 +51,8 @@ export async function runClient(context: vscode.ExtensionContext, port: number)
5351
await onActiveFileChange(editor);
5452
}
5553
} catch (e) {
56-
vscode.window.showErrorMessage("LiquidJava failed to initialize: " + e.toString());
57-
extension.logger.client.error("Failed to initialize: " + e.toString());
54+
vscode.window.showErrorMessage("LiquidJava failed to initialize: " + String(e));
55+
extension.logger!.client.error("Failed to initialize: " + String(e));
5856
await stopClient("Failed to initialize");
5957
}
6058

@@ -74,17 +72,17 @@ export async function runClient(context: vscode.ExtensionContext, port: number)
7472
*/
7573
export async function stopClient(reason: string) {
7674
if (!extension.client && !extension.serverProcess && !extension.socket) {
77-
extension.logger.client.info("Extension already stopped");
75+
extension.logger!.client.info("Extension already stopped");
7876
return;
7977
}
80-
extension.logger.client.info("Stopping LiquidJava extension: " + reason);
78+
extension.logger!.client.info("Stopping LiquidJava extension: " + reason);
8179
updateStatusBar("stopped");
8280

8381
// stop client
8482
try {
8583
await extension.client?.stop();
8684
} catch (e) {
87-
extension.logger.client.error("Error stopping client: " + e);
85+
extension.logger!.client.error("Error stopping client: " + e);
8886
} finally {
8987
extension.client = undefined;
9088
}
@@ -93,8 +91,8 @@ export async function stopClient(reason: string) {
9391
try {
9492
extension.socket?.destroy();
9593
} catch (e) {
96-
extension.logger.client.error("Error closing socket: " + e);
94+
extension.logger!.client.error("Error closing socket: " + e);
9795
} finally {
9896
extension.socket = undefined;
9997
}
100-
}
98+
}

client/src/lsp/server.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,33 @@ import { DEBUG_MODE, DEBUG_PORT, SERVER_JAR } from '../utils/constants';
1414
export async function runLanguageServer(context: vscode.ExtensionContext, javaExecutablePath: string): Promise<number> {
1515
const port = DEBUG_MODE ? DEBUG_PORT : await getAvailablePort();
1616
if (DEBUG_MODE) {
17-
extension.logger.client.info("DEBUG MODE: Using fixed port " + port);
17+
extension.logger!.client.info("DEBUG MODE: Using fixed port " + port);
1818
return port;
1919
}
20-
extension.logger.client.info("Running language server on port " + port);
20+
extension.logger!.client.info("Running language server on port " + port);
2121

2222
const jarPath = path.resolve(context.extensionPath, "dist", "server", SERVER_JAR);
2323
const args = ["-jar", jarPath, port.toString()];
24+
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
2425
const options = {
25-
cwd: normalizeFilePath(vscode.workspace.workspaceFolders[0].uri.fsPath), // root path
26+
cwd: workspaceFolder ? normalizeFilePath(workspaceFolder.uri.fsPath) : context.extensionPath, // root path
2627
};
27-
extension.logger.client.info("Creating language server process...");
28+
extension.logger!.client.info("Creating language server process...");
2829
extension.serverProcess = child_process.spawn(javaExecutablePath, args, options);
2930

3031
// listen to process events
31-
extension.serverProcess.stdout.on("data", (data) => {
32+
extension.serverProcess.stdout?.on("data", (data) => {
3233
const message = data.toString().trim();
33-
extension.logger.server.info(message);
34+
extension.logger!.server.info(message);
3435
});
35-
extension.serverProcess.stderr.on("data", (data) => {
36-
extension.logger.server.error(data.toString().trim())
36+
extension.serverProcess.stderr?.on("data", (data) => {
37+
extension.logger!.server.error(data.toString().trim())
3738
});
3839
extension.serverProcess.on("error", (err) => {
39-
extension.logger.server.error(`Failed to start: ${err}`)
40+
extension.logger!.server.error(`Failed to start: ${err}`)
4041
});
4142
extension.serverProcess.on("close", (code) => {
42-
extension.logger.server.info(`Process exited with code ${code}`);
43+
extension.logger!.server.info(`Process exited with code ${code}`);
4344
extension.serverProcess = undefined;
4445
});
4546
return port;
@@ -52,4 +53,4 @@ export async function runLanguageServer(context: vscode.ExtensionContext, javaEx
5253
export async function stopLanguageServer() {
5354
await killProcess(extension.serverProcess);
5455
extension.serverProcess = undefined;
55-
}
56+
}

client/src/services/autocomplete.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ function getContextCompletionItems(context: LJContext, file: string, annotation:
5353
}
5454
return [];
5555
}
56-
const inScope = extension.context.visibleVars !== null;
56+
const inScope = context.visibleVars !== null;
5757
const varsInScope = filterDuplicateVariables(filterInstanceVariables([...context.visibleVars || []]));
5858
const itemsHandlers: Record<CompletionItemKind, () => vscode.CompletionItem[]> = {
5959
vars: () => getVariableCompletionItems(varsInScope),
@@ -217,7 +217,7 @@ function getActiveLiquidJavaAnnotation(document: vscode.TextDocument, position:
217217
let lastAnnotationName: LJAnnotation | null = null;
218218
while ((match = LIQUIDJAVA_ANNOTATION_START.exec(textUntilCursor)) !== null) {
219219
lastAnnotationStart = match.index;
220-
lastAnnotationName = match[2] as LJAnnotation || null;
220+
lastAnnotationName = match[2] ? match[2] as LJAnnotation : null;
221221
}
222222
if (lastAnnotationStart === -1 || !lastAnnotationName) return null;
223223

@@ -242,7 +242,8 @@ function getReceiverBeforeDot(document: vscode.TextDocument, position: vscode.Po
242242
const prefix = document.lineAt(position.line).text.slice(0, position.character);
243243
const match = prefix.match(/((?:old\s*\(\s*this\s*\))|(?:[A-Za-z_]\w*))\.\w*$/);
244244
if (!match) return null;
245-
const receiver = match[1].trim();
245+
const receiver = match[1]?.trim();
246+
if (!receiver) return null;
246247
if (/^old\s*\(\s*this\s*\)$/.test(receiver)) return "old(this)";
247248
return receiver;
248249
}

client/src/services/context.ts

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@ import { getOriginalVariableName } from "../utils/utils";
55

66
export function handleContext(context: LJContext) {
77
extension.context = context;
8-
extension.logger?.client.info(JSON.stringify(context.methods, null, 2))
98
if (!extension.file || !extension.currentSelection) return;
109

1110
// update variables based on new context in current selection
1211
const { allVars, visibleVars } = getSelectionContextVariables(extension.file, extension.currentSelection);
1312
extension.context.visibleVars = visibleVars;
1413
extension.context.allVars = allVars;
1514
updateErrorAtCursor();
16-
extension.webview.sendMessage({ type: "context", context: extension.context, errorAtCursor: extension.errorAtCursor });
15+
extension.webview?.sendMessage({ type: "context", context: extension.context, errorAtCursor: extension.errorAtCursor });
1716
}
1817

1918
export function getSelectionContextVariables(file: string, selection: Range): { visibleVars: LJVariable[]; allVars: LJVariable[] } {
@@ -28,32 +27,37 @@ export function getSelectionContextVariables(file: string, selection: Range): {
2827

2928
export function updateErrorAtCursor() {
3029
if (!extension.file || !extension.currentSelection) return;
30+
const file = extension.file;
31+
const selection = extension.currentSelection;
3132
const errors: RefinementMismatchError[] = extension.diagnostics?.filter(d => d.type === 'refinement-error' || d.type === 'state-refinement-error') as RefinementMismatchError[] || [];
32-
const scopes = extension.context?.fileScopes[extension.file] || [];
33+
const scopes = extension.context?.fileScopes[file] || [];
3334
const errorAtCursor = errors.find(error => {
34-
if (!error.position) return false;
35-
const sameFile = error.position.file === extension.file;
36-
const beforeCursor = isPositionBefore(error.position, extension.currentSelection);
35+
const position = error.position;
36+
if (!position) return false;
37+
const sameFile = position.file === file;
38+
const beforeCursor = isPositionBefore(position, selection);
3739
if (!sameFile || !beforeCursor) return false;
3840
// check if error is within a scope that contains the cursor
39-
const errorScope = scopes.find(scope => isRangeWithin(error.position, scope));
40-
return errorScope && isRangeWithin(extension.currentSelection, errorScope);
41+
const errorScope = scopes.find(scope => isRangeWithin(position, scope));
42+
return errorScope && isRangeWithin(selection, errorScope);
4143
});
4244
extension.errorAtCursor = errorAtCursor;
4345
}
4446

4547
function getVariablesInScope(variables: LJVariable[], file: string, selection: Range): LJVariable[] {
46-
const scopes = extension.context.fileScopes[file] || [];
48+
const scopes = extension.context?.fileScopes[file] || [];
4749
const enclosingScopes = scopes.filter(scope => isRangeWithin(selection, scope));
48-
return variables.filter(v =>
49-
v.position?.file === file &&
50-
enclosingScopes.some(scope => isRangeWithin(v.position, scope))
51-
);
50+
return variables.filter(v => {
51+
const position = v.position;
52+
if (!position) return false
53+
return position.file === file &&
54+
enclosingScopes.some(scope => isRangeWithin(position, scope))
55+
});
5256
}
5357

5458
function getVisibleVariables(variables: LJVariable[], file: string, selection: Range, useAnnotationPosition: boolean): LJVariable[] {
5559
const isCollapsedRange = selection.lineStart === selection.lineEnd && selection.colStart === selection.colEnd;
56-
const fileScopes = isCollapsedRange ? (extension.context.fileScopes[file] || []) : [];
60+
const fileScopes = isCollapsedRange ? (extension.context?.fileScopes[file] || []) : [];
5761
return variables.filter((variable) => {
5862
// variable must be declared in the same file
5963
if (!variable.position || variable.position?.file !== file) return false;
@@ -147,4 +151,4 @@ function normalizeVariableRefinements(variables: LJVariable[]): LJVariable[] {
147151
}
148152
return [v];
149153
});
150-
}
154+
}

client/src/services/editor.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ export async function openFile(filePath: string, line: number, character: number
1616
if (rangeToHighlight) highlightRange(editor, rangeToHighlight);
1717
}
1818

19-
export function highlightRange(editor: vscode.TextEditor, range: Range) {
19+
export function highlightRange(editor: vscode.TextEditor | undefined, range: Range | null) {
20+
if (!editor) return;
2021
if (!range) {
21-
editor.setDecorations(highlight, []);
22+
editor.setDecorations(highlight, [])
2223
return;
2324
}
2425
const nativeRange = new vscode.Range(
@@ -27,4 +28,4 @@ export function highlightRange(editor: vscode.TextEditor, range: Range) {
2728
)
2829
editor.setDecorations(highlight, [{ range: nativeRange }])
2930
editor.revealRange(nativeRange, vscode.TextEditorRevealType.InCenter)
30-
}
31+
}

client/src/services/hover.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ function getHoveredVariable(document: vscode.TextDocument, position: vscode.Posi
4949
colEnd: wordRange.end.character
5050
};
5151
const { allVars } = getSelectionContextVariables(file, positionAfterVariable);
52-
return allVars.find(variable => getOriginalVariableName(variable.name) === hoveredWord);
52+
return allVars.find(variable => getOriginalVariableName(variable.name) === hoveredWord) || null;
5353
}
5454

5555
async function getHoveredMethod(document: vscode.TextDocument, position: vscode.Position): Promise<LJMethod | null> {

client/src/services/state-machine.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { LJStateMachine } from "../types/fsm";
77
* @param document The text document
88
*/
99
export async function updateStateMachine(document: vscode.TextDocument) {
10-
const sm: LJStateMachine = await extension.client?.sendRequest("liquidjava/fsm", { uri: document.uri.toString() });
10+
const sm = await extension.client?.sendRequest<LJStateMachine>("liquidjava/fsm", { uri: document.uri.toString() });
1111

1212
// dont update diagram if it hasnt changed to a new one
1313
if (!sm || JSON.stringify(sm) === JSON.stringify(extension.stateMachine)) return;

client/src/services/status-bar.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ export function registerStatusBar(context: vscode.ExtensionContext) {
3535
*/
3636
export function updateStatusBar(state: StatusBarState) {
3737
const color = state === "stopped" ? "errorForeground" : "statusBar.foreground";
38+
if (!extension.statusBar) return;
3839
extension.statusBar.color = new vscode.ThemeColor(color);
3940
extension.statusBar.text = icons[state] + " LiquidJava";
4041
extension.statusBar.tooltip = statusText[state];
41-
}
42+
}

client/src/services/webview.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,17 @@ export function registerWebview(context: vscode.ExtensionContext) {
1818
context.subscriptions.push(
1919
vscode.commands.registerCommand("liquidjava.showView", async (diagnostic?: DiagnosticRevealTarget) => {
2020
await vscode.commands.executeCommand("liquidJavaView.focus");
21-
if (diagnostic) extension.webview.sendMessage({ type: "revealDiagnostic", diagnostic });
21+
if (diagnostic) extension.webview?.sendMessage({ type: "revealDiagnostic", diagnostic });
2222
})
2323
);
2424
// listen for messages from the webview
2525
context.subscriptions.push(
2626
extension.webview.onDidReceiveMessage(message => {
2727
if (message.type === "ready") {
28-
if (extension.file) extension.webview.sendMessage({ type: "file", file: extension.file });
29-
if (extension.diagnostics) extension.webview.sendMessage({ type: "diagnostics", diagnostics: extension.diagnostics });
30-
if (extension.context) extension.webview.sendMessage({ type: "context", context: extension.context , errorAtCursor: extension.errorAtCursor });
31-
if (extension.stateMachine) extension.webview.sendMessage({ type: "fsm", sm: extension.stateMachine });
28+
if (extension.file) extension.webview?.sendMessage({ type: "file", file: extension.file });
29+
if (extension.diagnostics) extension.webview?.sendMessage({ type: "diagnostics", diagnostics: extension.diagnostics });
30+
if (extension.context) extension.webview?.sendMessage({ type: "context", context: extension.context , errorAtCursor: extension.errorAtCursor });
31+
if (extension.stateMachine) extension.webview?.sendMessage({ type: "fsm", sm: extension.stateMachine });
3232
}
3333
})
3434
);

0 commit comments

Comments
 (0)