1
1
import * as vscode from "vscode" ;
2
+ import { lt } from "semver" ;
2
3
import { AtelierAPI } from "../api" ;
3
4
import { loadChanges } from "../commands/compile" ;
4
5
import { StudioActions } from "../commands/studio" ;
5
6
import { clsLangId } from "../extension" ;
6
- import { cspApps , currentFile , handleError , openCustomEditors , outputChannel } from "../utils" ;
7
+ import { currentFile , openCustomEditors , outputChannel } from "../utils" ;
7
8
8
- export class RuleEditorProvider implements vscode . CustomTextEditorProvider {
9
- private static readonly _webapp : string = "/ui/interop/rule-editor" ;
9
+ export class LowCodeEditorProvider implements vscode . CustomTextEditorProvider {
10
+ private readonly _rule : string = "/ui/interop/rule-editor" ;
11
+ private readonly _dtl : string = "/ui/interop/dtl-editor" ;
10
12
11
- private static _errorMessage ( detail : string ) {
13
+ private _errorMessage ( detail : string ) {
12
14
return vscode . window
13
- . showErrorMessage ( "Cannot open Rule Editor." , {
15
+ . showErrorMessage ( "Cannot open Low-Code Editor." , {
14
16
modal : true ,
15
17
detail,
16
18
} )
17
- . then ( ( ) => vscode . commands . executeCommand < void > ( "workbench.action.toggleEditorType " ) ) ;
19
+ . then ( ( ) => vscode . commands . executeCommand < void > ( "workbench.action.reopenTextEditor " ) ) ;
18
20
}
19
21
20
22
async resolveCustomTextEditor (
@@ -24,57 +26,66 @@ export class RuleEditorProvider implements vscode.CustomTextEditorProvider {
24
26
) : Promise < void > {
25
27
// Check that document is a clean, well-formed class
26
28
if ( document . languageId != clsLangId ) {
27
- return RuleEditorProvider . _errorMessage ( `${ document . fileName } is not a class.` ) ;
29
+ return this . _errorMessage ( `${ document . fileName } is not a class.` ) ;
28
30
}
29
31
if ( document . isUntitled ) {
30
- return RuleEditorProvider . _errorMessage ( `${ document . fileName } is untitled.` ) ;
32
+ return this . _errorMessage ( `${ document . fileName } is untitled.` ) ;
31
33
}
32
34
if ( document . isDirty ) {
33
- return RuleEditorProvider . _errorMessage ( `${ document . fileName } is dirty.` ) ;
35
+ return this . _errorMessage ( `${ document . fileName } is dirty.` ) ;
34
36
}
35
37
const file = currentFile ( document ) ;
36
- if ( file == null ) {
37
- return RuleEditorProvider . _errorMessage ( `${ document . fileName } is a malformed class definition.` ) ;
38
+ if ( ! file ) {
39
+ return this . _errorMessage ( `${ document . fileName } is a malformed class definition.` ) ;
40
+ }
41
+ if ( ! vscode . workspace . fs . isWritableFileSystem ( document . uri . scheme ) ) {
42
+ return this . _errorMessage ( `File system '${ document . uri . scheme } ' is read-only.` ) ;
38
43
}
39
44
40
45
const className = file . name . slice ( 0 , - 4 ) ;
41
46
const api = new AtelierAPI ( document . uri ) ;
42
- const documentUriString = document . uri . toString ( ) ;
43
-
44
- // Check that the server has the webapp for the angular rule editor
45
- const cspAppsKey = `${ api . serverId } :%SYS` . toLowerCase ( ) ;
46
- let sysCspApps : string [ ] | undefined = cspApps . get ( cspAppsKey ) ;
47
- if ( sysCspApps == undefined ) {
48
- sysCspApps = await api . getCSPApps ( false , "%SYS" ) . then ( ( data ) => data . result . content || [ ] ) ;
49
- cspApps . set ( cspAppsKey , sysCspApps ) ;
47
+ if ( ! api . active ) {
48
+ return this . _errorMessage ( "Server connection is not active." ) ;
50
49
}
51
- if ( ! sysCspApps . includes ( RuleEditorProvider . _webapp ) ) {
52
- return RuleEditorProvider . _errorMessage ( `Server '${ api . serverId } ' does not support the Angular Rule Editor.` ) ;
50
+ if ( lt ( api . config . serverVersion , "2023.1.0" ) ) {
51
+ return this . _errorMessage (
52
+ "Opening a low-code editor in VS Code requires InterSystems IRIS version 2023.1 or above."
53
+ ) ;
53
54
}
54
55
55
- // Check that the class exists on the server and is a rule class
56
- const queryData = await api . actionQuery ( "SELECT Super FROM %Dictionary.ClassDefinition WHERE Name = ?" , [
57
- className ,
58
- ] ) ;
59
- if (
60
- queryData . result . content . length &&
61
- ! queryData . result . content [ 0 ] . Super . split ( "," ) . includes ( " Ens.Rule.Definition" )
62
- ) {
63
- // Class exists but is not a rule class
64
- return RuleEditorProvider . _errorMessage ( ` ${ className } is not a rule definition class.` ) ;
65
- } else if ( queryData . result . content . length == 0 ) {
56
+ // Check that the class exists on the server and is a rule or DTL class
57
+ let webApp : string ;
58
+ const queryData = await api . actionQuery (
59
+ "SELECT $LENGTH(rule.Name) AS Rule, $LENGTH(dtl.Name) AS DTL " +
60
+ "FROM %Dictionary.ClassDefinition AS dcd " +
61
+ "LEFT OUTER JOIN %Dictionary.ClassDefinition_SubclassOf('Ens.Rule.Definition') AS rule ON dcd.Name = rule.Name " +
62
+ "LEFT OUTER JOIN %Dictionary.ClassDefinition_SubclassOf(' Ens.DataTransformDTL') AS dtl ON dcd.Name = dtl.Name " +
63
+ "WHERE dcd.Name = ?" ,
64
+ [ className ]
65
+ ) ;
66
+ if ( queryData . result . content . length == 0 ) {
66
67
// Class doesn't exist on the server
67
- return RuleEditorProvider . _errorMessage ( `Class ${ className } does not exist on the server.` ) ;
68
+ return this . _errorMessage ( `${ file . name } does not exist on the server.` ) ;
69
+ } else if ( queryData . result . content [ 0 ] . Rule ) {
70
+ webApp = this . _rule ;
71
+ } else if ( queryData . result . content [ 0 ] . DTL ) {
72
+ if ( lt ( api . config . serverVersion , "2025.1.0" ) ) {
73
+ return this . _errorMessage (
74
+ "Opening the DTL editor in VS Code requires InterSystems IRIS version 2025.1 or above."
75
+ ) ;
76
+ }
77
+ webApp = this . _dtl ;
78
+ } else {
79
+ // Class exists but is not a rule or DTL class
80
+ return this . _errorMessage ( `${ className } is neither a rule definition class nor a DTL transformation class.` ) ;
68
81
}
69
82
70
83
// Add this document to the array of open custom editors
84
+ const documentUriString = document . uri . toString ( ) ;
71
85
openCustomEditors . push ( documentUriString ) ;
72
86
73
87
// Initialize the webview
74
88
const targetOrigin = `${ api . config . https ? "https" : "http" } ://${ api . config . host } :${ api . config . port } ` ;
75
- const iframeUri = `${ targetOrigin } ${ api . config . pathPrefix } ${
76
- RuleEditorProvider . _webapp
77
- } /index.html?$NAMESPACE=${ api . config . ns . toUpperCase ( ) } &VSCODE=1&rule=${ className } `;
78
89
webviewPanel . webview . options = {
79
90
enableScripts : true ,
80
91
localResourceRoots : [ ] ,
@@ -95,7 +106,9 @@ export class RuleEditorProvider implements vscode.CustomTextEditorProvider {
95
106
</head>
96
107
<body>
97
108
<div id="content">
98
- <iframe id="editor" title="Rule Editor" src="${ iframeUri } " width="100%" height="100%" frameborder="0"></iframe>
109
+ <iframe id="editor" title="Low-Code Editor" src="${ targetOrigin } ${ api . config . pathPrefix } ${ webApp } /index.html?$NAMESPACE=${ api . config . ns . toUpperCase ( ) } &VSCODE=1&${
110
+ webApp == this . _rule ? "rule" : "DTL"
111
+ } =${ className } " width="100%" height="100%" frameborder="0"></iframe>
99
112
</div>
100
113
<script>
101
114
(function() {
@@ -177,13 +190,12 @@ export class RuleEditorProvider implements vscode.CustomTextEditorProvider {
177
190
editorCompatible = true ;
178
191
return ;
179
192
case "badrule" :
180
- RuleEditorProvider . _errorMessage ( event . reason ) ;
193
+ case "baddtl" :
194
+ this . _errorMessage ( event . reason ) ;
181
195
return ;
182
196
case "loaded" :
183
197
if ( ! editorCompatible ) {
184
- RuleEditorProvider . _errorMessage (
185
- "This server's Angular Rule Editor does not support embedding in VS Code."
186
- ) ;
198
+ this . _errorMessage ( "This low-code editor does not support embedding in VS Code." ) ;
187
199
} else {
188
200
// Editor is compatible so send the credentials
189
201
webviewPanel . webview . postMessage ( {
@@ -258,15 +270,25 @@ export class RuleEditorProvider implements vscode.CustomTextEditorProvider {
258
270
type : "revert" ,
259
271
} ) ;
260
272
}
261
- if ( actionToProcess . errorText != "" ) {
273
+ if ( actionToProcess . errorText !== "" ) {
262
274
outputChannel . appendLine (
263
275
`\nError executing AfterUserAction '${ event . label } ':\n${ actionToProcess . errorText } `
264
276
) ;
265
- outputChannel . show ( true ) ;
277
+ outputChannel . show ( ) ;
266
278
}
267
279
}
268
280
} )
269
- . catch ( ( error ) => handleError ( error , `Error executing AfterUserAction '${ event . label } '.` ) ) ;
281
+ . catch ( ( error ) => {
282
+ outputChannel . appendLine ( `\nError executing AfterUserAction '${ event . label } ':` ) ;
283
+ if ( error && error . errorText && error . errorText !== "" ) {
284
+ outputChannel . appendLine ( error . errorText ) ;
285
+ } else {
286
+ outputChannel . appendLine (
287
+ typeof error == "string" ? error : error instanceof Error ? error . message : JSON . stringify ( error )
288
+ ) ;
289
+ }
290
+ outputChannel . show ( ) ;
291
+ } ) ;
270
292
}
271
293
} ) ;
272
294
return ;
0 commit comments