Skip to content

SysML-2LS Context Menu Commands Fail in Eclipse Theia

Issue Summary

When running the SysML-2LS extension (0.8.0) in Eclipse Theia (1.56.0), context menu commands such as "Dump AST" and "Dump Meta" fail silently while other commands like "Update current document" work normally. This affects all commands that rely on retrieving AST node information based on cursor position. Root Cause

The issue stems from a data format mismatch in cursor selection information between VS Code and Theia environments. The language server expects selection data in VS Code format but receives it in Theia's format, causing findCursorNode() to return 'undefined' for cursor positions.

Files Affected

packages/syside-languageserver/src/services/lsp/execute-command-handler.ts

Original Implementation

typescript

// In file: packages/syside-languageserver/src/services/lsp/execute-command-handler.ts/**

  • Find node under current selection * @param editor * @returns AstNode if found, undefined otherwise */protected findCursorNode( editor: RegisterTextEditorCommandsRequest.Parameters): AstNode | undefined { // Add robust checks for editor parameters if (!editor || !editor.document || !editor.document.uri || !editor.selection || !editor.selection.active ) { const message = "SysML-2LS: findCursorNode called with incomplete editor parameters."; if (this.connection?.console) { this.connection.console.error(message); this.connection.console.log(JSON.stringify(editor, null, 2)); // Log the problematic editor object } else { console.error(message, editor); } return undefined; } const uri = URI.parse(editor.document.uri); // Ensure langiumDoc is used consistently after getOrCreateDocument if (!this.documents.hasDocument(uri)) return undefined; const langiumDoc = this.documents.getOrCreateDocument(uri); // Use optional chaining for parseResult and value as they might be undefined const rootCstNode = langiumDoc.parseResult?.value?.$cstNode; if (!rootCstNode) return undefined; // editor.selection.active is now confirmed to exist by the checks above const offset = langiumDoc.textDocument.offsetAt(editor.selection.active); const leafCstNode = findDeclarationNodeAtOffset( rootCstNode, offset ); return leafCstNode?.element;}

Modified Implementation

typescript

// In file: packages/syside-languageserver/src/services/lsp/execute-command-handler.ts/**

  • Find node under current selection * @param editor * @returns AstNode if found, undefined otherwise */protected findCursorNode( editor: RegisterTextEditorCommandsRequest.Parameters): AstNode | undefined { // Add robust checks for editor parameters and handle different selection formats if (!editor || !editor.document || !editor.document.uri) { const message = "SysML-2LS: findCursorNode called with missing editor or document parameters."; if (this.connection?.console) { this.connection.console.error(message); this.connection.console.log(JSON.stringify(editor, null, 2)); } else { console.error(message, editor); } return undefined; } // Handle different selection format possibilities let activePosition: { line: number; character: number } | undefined; // Case 1: selection is an array with at least one position (most likely first position in the range) if (Array.isArray(editor.selection) && editor.selection.length > 0) { activePosition = editor.selection[0]; // Use first position in range } // Case 2: selection has an 'active' property (original expected format) else if (editor.selection?.active) { activePosition = editor.selection.active; } // Case 3: selections array exists (from the logs we can see this format) else if (Array.isArray(editor.selections) && editor.selections.length > 0) { // The log showed selections as array of arrays, so we need to extract properly if (Array.isArray(editor.selections[0])) { activePosition = editor.selections[0][0]; // Use first position of first selection } else { activePosition = editor.selections[0]; // Use first selection } } if (!activePosition) { const message = "SysML-2LS: Could not determine cursor position from editor parameters."; if (this.connection?.console) { this.connection.console.error(message); this.connection.console.log(JSON.stringify(editor, null, 2)); } else { console.error(message, editor); } return undefined; } const uri = URI.parse(editor.document.uri); // Ensure langiumDoc is used consistently after getOrCreateDocument if (!this.documents.hasDocument(uri)) return undefined; const langiumDoc = this.documents.getOrCreateDocument(uri); // Use optional chaining for parseResult and value as they might be undefined const rootCstNode = langiumDoc.parseResult?.value?.$cstNode; if (!rootCstNode) return undefined; // Use our extracted activePosition instead of assuming editor.selection.active const offset = langiumDoc.textDocument.offsetAt(activePosition); const leafCstNode = findDeclarationNodeAtOffset( rootCstNode, offset ); return leafCstNode?.element;}

Related Method Also Affected

typescript

// In file: packages/syside-languageserver/src/services/lsp/execute-command-handler.ts/**

  • @returns the AST under active cursor as JSON string */@editorCommand("syside.editor.dumpAst")protected dumpAst( editor: RegisterTextEditorCommandsRequest.Parameters, _ = CancellationToken.None): unknown | undefined { const node = this.findCursorNode(editor); if (!node) return; return toJSON(node, JSONreplacer);}

Sample Data Showing Format Mismatch

In Theia, the selection data is formatted as:

json

{ "document": { "uri": "file:///path/to/example.sysml" }, "selection": [ {"line": 16, "character": 6}, {"line": 16, "character": 18} ], "selections": [ [ {"line": 16, "character": 6}, {"line": 16, "character": 18} ] ]}

Reproduction Steps

  1. Install SysML-2LS extension in Eclipse Theia
  2. Open a SysML file
  3. Right-click and select "Dump AST" from the context menu
  4. Observe no output appears, while "Update current document" works

Additional Notes

This fix makes the extension more robust by supporting both VS Code and Theia selection data formats, improving cross-platform compatibility. The fix is backward compatible and will not affect VS Code users.