@@ -18,6 +18,7 @@ import {
1818 openLowCodeEditors ,
1919 compileErrorMsg ,
2020 isCompilable ,
21+ currentFileFromContent ,
2122} from "../../utils" ;
2223import { FILESYSTEM_READONLY_SCHEMA , FILESYSTEM_SCHEMA , intLangId , macLangId } from "../../extension" ;
2324import { addIsfsFileToProject , modifyProject } from "../../commands/project" ;
@@ -65,6 +66,11 @@ class Directory implements vscode.FileStat {
6566
6667type Entry = File | Directory ;
6768
69+ /**
70+ * Generates stub content for document `fileName` in file `uri`.
71+ * If `sourceContent` is supplied, the name of the document in
72+ * that content is modified to match `fileName`.
73+ */
6874export function generateFileContent (
6975 uri : vscode . Uri ,
7076 fileName : string ,
@@ -89,7 +95,6 @@ export function generateFileContent(
8995 const className = fileName . split ( "." ) . slice ( 0 , - 1 ) . join ( "." ) ;
9096 let content : string [ ] = [ ] ;
9197 const preamble : string [ ] = [ ] ;
92-
9398 if ( sourceLines . length ) {
9499 if ( notIsfs ( uri ) && ( fileName . includes ( path . sep ) || fileName . includes ( " " ) ) ) {
95100 // We couldn't resolve a class name from the file path,
@@ -100,11 +105,9 @@ export function generateFileContent(
100105 // Replace that with one to match fileName.
101106 while ( sourceLines . length > 0 ) {
102107 const nextLine = sourceLines . shift ( ) ;
103- if ( nextLine . toLowerCase ( ) . startsWith ( "class " ) ) {
104- const classLine = nextLine . split ( " " ) ;
105- classLine [ 0 ] = "Class" ;
106- classLine [ 1 ] = className ;
107- content . push ( ...preamble , classLine . join ( " " ) , ...sourceLines ) ;
108+ const classNameMatch = nextLine . match ( classNameRegex ) ;
109+ if ( classNameMatch ) {
110+ content . push ( ...preamble , nextLine . replace ( classNameMatch [ 1 ] , fileName . slice ( 0 , - 4 ) ) , ...sourceLines ) ;
108111 break ;
109112 }
110113 preamble . push ( nextLine ) ;
@@ -117,7 +120,6 @@ export function generateFileContent(
117120 } else {
118121 content = [ `Class ${ className } Extends %RegisteredObject` , "{" , "}" ] ;
119122 }
120-
121123 return {
122124 content,
123125 enc : false ,
@@ -133,8 +135,8 @@ export function generateFileContent(
133135 eol,
134136 } ;
135137 } else {
136- sourceLines . shift ( ) ;
137- const routineName = fileName . split ( "." ) . slice ( 0 , - 1 ) . join ( "." ) ;
138+ if ( sourceLines [ 0 ] ?. startsWith ( "ROUTINE " ) ) sourceLines . shift ( ) ;
139+ const routineName = fileName . slice ( 0 , - 4 ) ;
138140 const routineType = fileExt != "mac" ? `[Type=${ fileExt . toUpperCase ( ) } ]` : "" ;
139141 if ( sourceLines . length === 0 && fileExt !== "inc" ) {
140142 const languageId = fileExt === "mac" ? macLangId : intLangId ;
@@ -156,7 +158,7 @@ export function generateFileContent(
156158 } ;
157159 }
158160 } else if ( csp && sourceContent . length == 0 ) {
159- // Some IRIS versions do not allow empty content to be PUT for CSP files, so add a newline if the content is empty. See DP-442552.
161+ // Some IRIS versions do not allow empty content to be PUT for web app files, so add a newline if the content is empty
160162 return {
161163 content : [ "" ] ,
162164 enc : false ,
@@ -532,38 +534,12 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
532534 const api = new AtelierAPI ( uri ) ;
533535 let created = false ;
534536 let update = false ;
535- const isCls = ! csp && fileName . split ( "." ) . pop ( ) . toLowerCase ( ) == "cls" ;
537+ const fileExt = fileName . split ( "." ) . pop ( ) . toLowerCase ( ) ;
536538 // Use _lookup() instead of _lookupAsFile() so we send
537539 // our cached mtime with the GET /doc request if we have it
538540 return this . _lookup ( uri )
539541 . then (
540542 async ( entry : File ) => {
541- // Check cases for which we should fail the write and leave the document dirty if changed
542- if ( isCls ) {
543- // Check if the class name and file name match
544- let clsname = "" ;
545- const match = new TextDecoder ( ) . decode ( content ) . match ( classNameRegex ) ;
546- if ( match ) {
547- [ , clsname ] = match ;
548- }
549- if ( clsname == "" ) {
550- throw new vscode . FileSystemError ( "Cannot save a malformed class" ) ;
551- }
552- if ( fileName . slice ( 0 , - 4 ) != clsname ) {
553- throw new vscode . FileSystemError (
554- "Cannot save an isfs class where the class name and file name do not match"
555- ) ;
556- }
557- if ( openLowCodeEditors . has ( uri . toString ( ) ) ) {
558- // This class is open in a low-code editor, so any
559- // updates to the class will be handled by that editor
560- return ;
561- }
562- // Check if the class is deployed
563- if ( await isClassDeployed ( fileName , api ) ) {
564- throw new vscode . FileSystemError ( "Cannot overwrite a deployed class" ) ;
565- }
566- }
567543 const contentBuffer = Buffer . from ( content ) ;
568544 const putContent = isText ( uri . path . split ( "/" ) . pop ( ) , contentBuffer )
569545 ? {
@@ -574,6 +550,35 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
574550 content : base64EncodeContent ( contentBuffer ) ,
575551 enc : true ,
576552 } ;
553+ if ( ! csp && [ "cls" , "mac" , "int" , "inc" ] . includes ( fileExt ) ) {
554+ const curFile = currentFileFromContent ( uri , putContent . enc ? contentBuffer : putContent . content . join ( "\n" ) ) ;
555+ if ( ! curFile ) {
556+ throw new vscode . FileSystemError (
557+ `Cannot save a malformed ${ fileExt == "cls" ? "class" : fileExt == "inc" ? "include file" : "routine" } `
558+ ) ;
559+ }
560+ if ( curFile . name != fileName ) {
561+ // Update the content so the name in text matches the URI
562+ const newContent = generateFileContent ( uri , fileName , content ) ;
563+ putContent . content = newContent . content ;
564+ putContent . enc = newContent . enc ;
565+ // Make sure the editor tab is updated to show the new content
566+ update = true ;
567+ }
568+ if ( fileExt == "cls" ) {
569+ if ( openLowCodeEditors . has ( uri . toString ( ) ) ) {
570+ // This class is open in a low-code editor, so any
571+ // updates to the class will be handled by that editor
572+ return ;
573+ }
574+ // Check if the class is deployed
575+ if ( await isClassDeployed ( fileName , api ) ) {
576+ throw new vscode . FileSystemError ( "Cannot overwrite a deployed class" ) ;
577+ }
578+ // Always update the editor tab for a class after saving
579+ update = true ;
580+ }
581+ }
577582 if (
578583 csp &&
579584 ! putContent . enc &&
@@ -595,7 +600,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
595600 true
596601 )
597602 . then ( ( data ) => {
598- update = isCls || data . result . content . length > 0 ;
603+ update = update || data . result . content . length > 0 ;
599604 return entry ;
600605 } )
601606 . catch ( ( error ) => {
@@ -641,7 +646,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
641646 // Create an entry in our cache for the document
642647 return this . _lookupAsFile ( uri ) . then ( ( entry ) => {
643648 created = true ;
644- update = isCls || data . result . content . length > 0 ;
649+ update = ( ! csp && fileExt == "cls" ) || data . result . content . length > 0 ;
645650 this . _fireSoon ( { type : vscode . FileChangeType . Created , uri } ) ;
646651 return entry ;
647652 } ) ;
0 commit comments