Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1833,14 +1833,24 @@ export async function activate(context: vscode.ExtensionContext): Promise<any> {
sendCommandTelemetryEvent("showAllClassMembers");
if (uri instanceof vscode.Uri) showAllClassMembers(uri);
}),
vscode.workspace.onDidSaveTextDocument((d) => {
vscode.workspace.onDidSaveTextDocument(async (d) => {
// If the document just saved is a server-side document that needs to be updated in the UI,
// then force VS Code to update the document's contents. This is needed if the document has
// been changed during a save, for example by adding or changing the Storage definition.
if (notIsfs(d.uri)) return;
const uriString = d.uri.toString();
if (fileSystemProvider.needsUpdate(uriString)) {
const activeDoc = vscode.window.activeTextEditor?.document;
let activeDoc = vscode.window.activeTextEditor?.document;
if (activeDoc?.uri.toString() != uriString) {
// The active text editor (if any) does not contain this document. Wait a short time and
// check again in case this document was saved using the "Save As..." command. In that
// case VS Code saves the document once with no content and then saves it again with content
// from the source document. During that second save our FileSystemProvider will likely
// change the content of the document so the header matches the new URI. We need to force
// VS Code to reflect that change in the editor UI.
await new Promise((resolve) => setTimeout(resolve, 50));
activeDoc = vscode.window.activeTextEditor?.document;
}
if (activeDoc && !activeDoc.isDirty && !activeDoc.isClosed && activeDoc.uri.toString() == uriString) {
// Force VS Code to refresh the file's contents in the editor tab
vscode.commands.executeCommand("workbench.action.files.revert");
Expand Down
83 changes: 44 additions & 39 deletions src/providers/FileSystemProvider/FileSystemProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
openLowCodeEditors,
compileErrorMsg,
isCompilable,
currentFileFromContent,
} from "../../utils";
import { FILESYSTEM_READONLY_SCHEMA, FILESYSTEM_SCHEMA, intLangId, macLangId } from "../../extension";
import { addIsfsFileToProject, modifyProject } from "../../commands/project";
Expand Down Expand Up @@ -65,6 +66,11 @@ class Directory implements vscode.FileStat {

type Entry = File | Directory;

/**
* Generates stub content for document `fileName` in file `uri`.
* If `sourceContent` is supplied, the name of the document in
* that content is modified to match `fileName`.
*/
export function generateFileContent(
uri: vscode.Uri,
fileName: string,
Expand All @@ -89,7 +95,6 @@ export function generateFileContent(
const className = fileName.split(".").slice(0, -1).join(".");
let content: string[] = [];
const preamble: string[] = [];

if (sourceLines.length) {
if (notIsfs(uri) && (fileName.includes(path.sep) || fileName.includes(" "))) {
// We couldn't resolve a class name from the file path,
Expand All @@ -100,11 +105,9 @@ export function generateFileContent(
// Replace that with one to match fileName.
while (sourceLines.length > 0) {
const nextLine = sourceLines.shift();
if (nextLine.toLowerCase().startsWith("class ")) {
const classLine = nextLine.split(" ");
classLine[0] = "Class";
classLine[1] = className;
content.push(...preamble, classLine.join(" "), ...sourceLines);
const classNameMatch = nextLine.match(classNameRegex);
if (classNameMatch) {
content.push(...preamble, nextLine.replace(classNameMatch[1], fileName.slice(0, -4)), ...sourceLines);
break;
}
preamble.push(nextLine);
Expand All @@ -117,7 +120,6 @@ export function generateFileContent(
} else {
content = [`Class ${className} Extends %RegisteredObject`, "{", "}"];
}

return {
content,
enc: false,
Expand All @@ -133,8 +135,8 @@ export function generateFileContent(
eol,
};
} else {
sourceLines.shift();
const routineName = fileName.split(".").slice(0, -1).join(".");
if (sourceLines[0]?.startsWith("ROUTINE ")) sourceLines.shift();
const routineName = fileName.slice(0, -4);
const routineType = fileExt != "mac" ? `[Type=${fileExt.toUpperCase()}]` : "";
if (sourceLines.length === 0 && fileExt !== "inc") {
const languageId = fileExt === "mac" ? macLangId : intLangId;
Expand All @@ -156,7 +158,7 @@ export function generateFileContent(
};
}
} else if (csp && sourceContent.length == 0) {
// 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.
// Some IRIS versions do not allow empty content to be PUT for web app files, so add a newline if the content is empty
return {
content: [""],
enc: false,
Expand Down Expand Up @@ -532,38 +534,12 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
const api = new AtelierAPI(uri);
let created = false;
let update = false;
const isCls = !csp && fileName.split(".").pop().toLowerCase() == "cls";
const fileExt = fileName.split(".").pop().toLowerCase();
// Use _lookup() instead of _lookupAsFile() so we send
// our cached mtime with the GET /doc request if we have it
return this._lookup(uri)
.then(
async (entry: File) => {
// Check cases for which we should fail the write and leave the document dirty if changed
if (isCls) {
// Check if the class name and file name match
let clsname = "";
const match = new TextDecoder().decode(content).match(classNameRegex);
if (match) {
[, clsname] = match;
}
if (clsname == "") {
throw new vscode.FileSystemError("Cannot save a malformed class");
}
if (fileName.slice(0, -4) != clsname) {
throw new vscode.FileSystemError(
"Cannot save an isfs class where the class name and file name do not match"
);
}
if (openLowCodeEditors.has(uri.toString())) {
// This class is open in a low-code editor, so any
// updates to the class will be handled by that editor
return;
}
// Check if the class is deployed
if (await isClassDeployed(fileName, api)) {
throw new vscode.FileSystemError("Cannot overwrite a deployed class");
}
}
const contentBuffer = Buffer.from(content);
const putContent = isText(uri.path.split("/").pop(), contentBuffer)
? {
Expand All @@ -574,6 +550,35 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
content: base64EncodeContent(contentBuffer),
enc: true,
};
if (!csp && ["cls", "mac", "int", "inc"].includes(fileExt)) {
const curFile = currentFileFromContent(uri, putContent.enc ? contentBuffer : putContent.content.join("\n"));
if (!curFile) {
throw new vscode.FileSystemError(
`Cannot save a malformed ${fileExt == "cls" ? "class" : fileExt == "inc" ? "include file" : "routine"}`
);
}
if (curFile.name != fileName) {
// Update the content so the name in text matches the URI
const newContent = generateFileContent(uri, fileName, content);
putContent.content = newContent.content;
putContent.enc = newContent.enc;
// Make sure the editor tab is updated to show the new content
update = true;
}
if (fileExt == "cls") {
if (openLowCodeEditors.has(uri.toString())) {
// This class is open in a low-code editor, so any
// updates to the class will be handled by that editor
return;
}
// Check if the class is deployed
if (await isClassDeployed(fileName, api)) {
throw new vscode.FileSystemError("Cannot overwrite a deployed class");
}
// Always update the editor tab for a class after saving
update = true;
}
}
if (
csp &&
!putContent.enc &&
Expand All @@ -595,7 +600,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
true
)
.then((data) => {
update = isCls || data.result.content.length > 0;
update = update || data.result.content.length > 0;
return entry;
})
.catch((error) => {
Expand Down Expand Up @@ -641,7 +646,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider {
// Create an entry in our cache for the document
return this._lookupAsFile(uri).then((entry) => {
created = true;
update = isCls || data.result.content.length > 0;
update = (!csp && fileExt == "cls") || data.result.content.length > 0;
this._fireSoon({ type: vscode.FileChangeType.Created, uri });
return entry;
});
Expand Down
Loading