diff --git a/package.json b/package.json index 699eafbb..f6bf76d5 100644 --- a/package.json +++ b/package.json @@ -1739,8 +1739,8 @@ ], "customEditors": [ { - "viewType": "vscode-objectscript.rule", - "displayName": "Rule Editor", + "viewType": "vscode-objectscript.lowCode", + "displayName": "Low-Code Editor", "selector": [ { "filenamePattern": "*.cls" diff --git a/src/extension.ts b/src/extension.ts index d4240aa0..a51499fa 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -135,7 +135,7 @@ import { modifyProjectMetadata, } from "./commands/project"; import { loadStudioColors, loadStudioSnippets } from "./commands/studioMigration"; -import { RuleEditorProvider } from "./providers/RuleEditorProvider"; +import { LowCodeEditorProvider } from "./providers/LowCodeEditorProvider"; import { newFile, NewFileType } from "./commands/newFile"; import { FileDecorationProvider } from "./providers/FileDecorationProvider"; import { RESTDebugPanel } from "./commands/restDebugPanel"; @@ -1318,7 +1318,7 @@ export async function activate(context: vscode.ExtensionContext): Promise { vscode.commands.registerCommand("vscode-objectscript.explorer.project.addWorkspaceFolderForProject", (node) => addWorkspaceFolderForProject(node) ), - vscode.window.registerCustomEditorProvider("vscode-objectscript.rule", new RuleEditorProvider(), { + vscode.window.registerCustomEditorProvider("vscode-objectscript.lowCode", new LowCodeEditorProvider(), { webviewOptions: { retainContextWhenHidden: true, }, diff --git a/src/providers/RuleEditorProvider.ts b/src/providers/LowCodeEditorProvider.ts similarity index 69% rename from src/providers/RuleEditorProvider.ts rename to src/providers/LowCodeEditorProvider.ts index 32893c8b..012556a9 100644 --- a/src/providers/RuleEditorProvider.ts +++ b/src/providers/LowCodeEditorProvider.ts @@ -1,20 +1,22 @@ import * as vscode from "vscode"; +import { lt } from "semver"; import { AtelierAPI } from "../api"; import { loadChanges } from "../commands/compile"; import { StudioActions } from "../commands/studio"; import { clsLangId } from "../extension"; -import { cspApps, currentFile, handleError, openCustomEditors, outputChannel } from "../utils"; +import { currentFile, openCustomEditors, outputChannel } from "../utils"; -export class RuleEditorProvider implements vscode.CustomTextEditorProvider { - private static readonly _webapp: string = "/ui/interop/rule-editor"; +export class LowCodeEditorProvider implements vscode.CustomTextEditorProvider { + private readonly _rule: string = "/ui/interop/rule-editor"; + private readonly _dtl: string = "/ui/interop/dtl-editor"; - private static _errorMessage(detail: string) { + private _errorMessage(detail: string) { return vscode.window - .showErrorMessage("Cannot open Rule Editor.", { + .showErrorMessage("Cannot open Low-Code Editor.", { modal: true, detail, }) - .then(() => vscode.commands.executeCommand("workbench.action.toggleEditorType")); + .then(() => vscode.commands.executeCommand("workbench.action.reopenTextEditor")); } async resolveCustomTextEditor( @@ -24,57 +26,66 @@ export class RuleEditorProvider implements vscode.CustomTextEditorProvider { ): Promise { // Check that document is a clean, well-formed class if (document.languageId != clsLangId) { - return RuleEditorProvider._errorMessage(`${document.fileName} is not a class.`); + return this._errorMessage(`${document.fileName} is not a class.`); } if (document.isUntitled) { - return RuleEditorProvider._errorMessage(`${document.fileName} is untitled.`); + return this._errorMessage(`${document.fileName} is untitled.`); } if (document.isDirty) { - return RuleEditorProvider._errorMessage(`${document.fileName} is dirty.`); + return this._errorMessage(`${document.fileName} is dirty.`); } const file = currentFile(document); - if (file == null) { - return RuleEditorProvider._errorMessage(`${document.fileName} is a malformed class definition.`); + if (!file) { + return this._errorMessage(`${document.fileName} is a malformed class definition.`); + } + if (!vscode.workspace.fs.isWritableFileSystem(document.uri.scheme)) { + return this._errorMessage(`File system '${document.uri.scheme}' is read-only.`); } const className = file.name.slice(0, -4); const api = new AtelierAPI(document.uri); - const documentUriString = document.uri.toString(); - - // Check that the server has the webapp for the angular rule editor - const cspAppsKey = `${api.serverId}:%SYS`.toLowerCase(); - let sysCspApps: string[] | undefined = cspApps.get(cspAppsKey); - if (sysCspApps == undefined) { - sysCspApps = await api.getCSPApps(false, "%SYS").then((data) => data.result.content || []); - cspApps.set(cspAppsKey, sysCspApps); + if (!api.active) { + return this._errorMessage("Server connection is not active."); } - if (!sysCspApps.includes(RuleEditorProvider._webapp)) { - return RuleEditorProvider._errorMessage(`Server '${api.serverId}' does not support the Angular Rule Editor.`); + if (lt(api.config.serverVersion, "2023.1.0")) { + return this._errorMessage( + "Opening a low-code editor in VS Code requires InterSystems IRIS version 2023.1 or above." + ); } - // Check that the class exists on the server and is a rule class - const queryData = await api.actionQuery("SELECT Super FROM %Dictionary.ClassDefinition WHERE Name = ?", [ - className, - ]); - if ( - queryData.result.content.length && - !queryData.result.content[0].Super.split(",").includes("Ens.Rule.Definition") - ) { - // Class exists but is not a rule class - return RuleEditorProvider._errorMessage(`${className} is not a rule definition class.`); - } else if (queryData.result.content.length == 0) { + // Check that the class exists on the server and is a rule or DTL class + let webApp: string; + const queryData = await api.actionQuery( + "SELECT $LENGTH(rule.Name) AS Rule, $LENGTH(dtl.Name) AS DTL " + + "FROM %Dictionary.ClassDefinition AS dcd " + + "LEFT OUTER JOIN %Dictionary.ClassDefinition_SubclassOf('Ens.Rule.Definition') AS rule ON dcd.Name = rule.Name " + + "LEFT OUTER JOIN %Dictionary.ClassDefinition_SubclassOf('Ens.DataTransformDTL') AS dtl ON dcd.Name = dtl.Name " + + "WHERE dcd.Name = ?", + [className] + ); + if (queryData.result.content.length == 0) { // Class doesn't exist on the server - return RuleEditorProvider._errorMessage(`Class ${className} does not exist on the server.`); + return this._errorMessage(`${file.name} does not exist on the server.`); + } else if (queryData.result.content[0].Rule) { + webApp = this._rule; + } else if (queryData.result.content[0].DTL) { + if (lt(api.config.serverVersion, "2025.1.0")) { + return this._errorMessage( + "Opening the DTL editor in VS Code requires InterSystems IRIS version 2025.1 or above." + ); + } + webApp = this._dtl; + } else { + // Class exists but is not a rule or DTL class + return this._errorMessage(`${className} is neither a rule definition class nor a DTL transformation class.`); } // Add this document to the array of open custom editors + const documentUriString = document.uri.toString(); openCustomEditors.push(documentUriString); // Initialize the webview const targetOrigin = `${api.config.https ? "https" : "http"}://${api.config.host}:${api.config.port}`; - const iframeUri = `${targetOrigin}${api.config.pathPrefix}${ - RuleEditorProvider._webapp - }/index.html?$NAMESPACE=${api.config.ns.toUpperCase()}&VSCODE=1&rule=${className}`; webviewPanel.webview.options = { enableScripts: true, localResourceRoots: [], @@ -95,7 +106,9 @@ export class RuleEditorProvider implements vscode.CustomTextEditorProvider {
- +