From 740d1b300012443eacf3bb612a8758eb3ebac00a Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Fri, 5 Jul 2024 18:20:35 +0100 Subject: [PATCH 1/6] Mark as readonly deployed classes and source-control-protected ISFS documents --- src/providers/FileSystemProvider/File.ts | 1 + .../FileSystemProvider/FileSystemProvider.ts | 56 +++++++++++++++++-- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/providers/FileSystemProvider/File.ts b/src/providers/FileSystemProvider/File.ts index 801c02da..fd4776c5 100644 --- a/src/providers/FileSystemProvider/File.ts +++ b/src/providers/FileSystemProvider/File.ts @@ -5,6 +5,7 @@ export class File implements vscode.FileStat { public ctime: number; public mtime: number; public size: number; + public permissions?: vscode.FilePermission; public fileName: string; public name: string; public data?: Uint8Array; diff --git a/src/providers/FileSystemProvider/FileSystemProvider.ts b/src/providers/FileSystemProvider/FileSystemProvider.ts index 37454a2b..9f190797 100644 --- a/src/providers/FileSystemProvider/FileSystemProvider.ts +++ b/src/providers/FileSystemProvider/FileSystemProvider.ts @@ -7,13 +7,21 @@ import { fireOtherStudioAction, OtherStudioAction, StudioActions } from "../../c import { projectContentsFromUri, studioOpenDialogFromURI } from "../../utils/FileProviderUtil"; import { classNameRegex, + getServerName, isClassDeployed, notNull, outputChannel, redirectDotvscodeRoot, workspaceFolderOfUri, } from "../../utils/index"; -import { config, FILESYSTEM_SCHEMA, intLangId, macLangId, workspaceState } from "../../extension"; +import { + config, + FILESYSTEM_READONLY_SCHEMA, + FILESYSTEM_SCHEMA, + intLangId, + macLangId, + workspaceState, +} from "../../extension"; import { addIsfsFileToProject, modifyProject } from "../../commands/project"; import { DocumentContentProvider } from "../DocumentContentProvider"; import { Document, UserAction } from "../../api/atelier"; @@ -184,13 +192,53 @@ export class FileSystemProvider implements vscode.FileSystemProvider { this._fireSoon({ type: vscode.FileChangeType.Changed, uri }); } - public stat(uri: vscode.Uri): Promise { + public async stat(uri: vscode.Uri): Promise { + let entryPromise: Promise; + let result: Entry; const redirectedUri = redirectDotvscodeRoot(uri); if (redirectedUri.path !== uri.path) { // When redirecting the /.vscode subtree we must fill in as-yet-unvisited folders to fix https://github.com/intersystems-community/vscode-objectscript/issues/1143 - return this._lookup(redirectedUri, true); + entryPromise = this._lookup(redirectedUri, true); + } else { + entryPromise = this._lookup(uri); + } + + // If this is our readonly variant there's no point checking server-side whether the file sould be marked readonly + if (uri.scheme === FILESYSTEM_READONLY_SCHEMA) { + return entryPromise; + } + + if (entryPromise instanceof File) { + // previously resolved as a file + result = entryPromise; + } else if (entryPromise instanceof Promise && uri.path.split("/").pop()?.split(".").length > 1) { + // apparently a file, so resolve ahead of adding permissions + result = await entryPromise; + } else { + // otherwise return the promise + return entryPromise; + } + + // + if (result instanceof File) { + const api = new AtelierAPI(uri); + const serverName = getServerName(uri); + if (serverName.slice(-4).toLowerCase() == ".cls") { + if (await isClassDeployed(serverName, api)) { + result.permissions |= vscode.FilePermission.Readonly; + return result; + } + } + + // Does server-side source control report it as editable? + const query = "select * from %Atelier_v1_Utils.Extension_GetStatus(?)"; + const statusObj = await api.actionQuery(query, [serverName]); + const docStatus = statusObj.result.content.pop(); + if (docStatus) { + result.permissions = docStatus.editable ? undefined : result.permissions | vscode.FilePermission.Readonly; + } } - return this._lookup(uri); + return result; } public async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { From b540581140d2600dcad70572fbdd024fb62e1d01 Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Fri, 5 Jul 2024 18:29:26 +0100 Subject: [PATCH 2/6] Add `objectscript.serverSourceControl.respectEditableStatus` setting --- package.json | 5 +++++ .../FileSystemProvider/FileSystemProvider.ts | 12 +++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 12ee93b7..a10e4b1e 100644 --- a/package.json +++ b/package.json @@ -1312,6 +1312,11 @@ "type": "boolean", "scope": "resource" }, + "objectscript.serverSourceControl.respectEditableStatus": { + "description": "Set ISFS document readonly if GetStatus method of server-side source control class returns Editable=0", + "type": "boolean", + "scope": "resource" + }, "objectscript.export": { "type": "object", "description": "Control what to export from the server into the local folder.", diff --git a/src/providers/FileSystemProvider/FileSystemProvider.ts b/src/providers/FileSystemProvider/FileSystemProvider.ts index 9f190797..b81828a5 100644 --- a/src/providers/FileSystemProvider/FileSystemProvider.ts +++ b/src/providers/FileSystemProvider/FileSystemProvider.ts @@ -231,11 +231,13 @@ export class FileSystemProvider implements vscode.FileSystemProvider { } // Does server-side source control report it as editable? - const query = "select * from %Atelier_v1_Utils.Extension_GetStatus(?)"; - const statusObj = await api.actionQuery(query, [serverName]); - const docStatus = statusObj.result.content.pop(); - if (docStatus) { - result.permissions = docStatus.editable ? undefined : result.permissions | vscode.FilePermission.Readonly; + if (vscode.workspace.getConfiguration("objectscript.serverSourceControl", uri)?.get("respectEditableStatus")) { + const query = "select * from %Atelier_v1_Utils.Extension_GetStatus(?)"; + const statusObj = await api.actionQuery(query, [serverName]); + const docStatus = statusObj.result.content.pop(); + if (docStatus) { + result.permissions = docStatus.editable ? undefined : result.permissions | vscode.FilePermission.Readonly; + } } } return result; From d13f2bef518d7693074e1851116f8eed2fd93741 Mon Sep 17 00:00:00 2001 From: John Murray Date: Fri, 12 Jul 2024 14:43:00 +0100 Subject: [PATCH 3/6] Improve description of `respectEditableStatus` Co-authored-by: Brett Saviano --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ac511baa..9c47c7b3 100644 --- a/package.json +++ b/package.json @@ -1313,7 +1313,7 @@ "scope": "resource" }, "objectscript.serverSourceControl.respectEditableStatus": { - "description": "Set ISFS document readonly if GetStatus method of server-side source control class returns Editable=0", + "markdownDescription": "Set `isfs` document readonly if GetStatus method of server-side source control class returns Editable = 0.", "type": "boolean", "scope": "resource" }, From 5ae048b99734cedb47787005288fde78cd64358e Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Fri, 12 Jul 2024 14:49:18 +0100 Subject: [PATCH 4/6] Apply feedback --- src/providers/FileSystemProvider/FileSystemProvider.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/providers/FileSystemProvider/FileSystemProvider.ts b/src/providers/FileSystemProvider/FileSystemProvider.ts index b81828a5..a9a8ed55 100644 --- a/src/providers/FileSystemProvider/FileSystemProvider.ts +++ b/src/providers/FileSystemProvider/FileSystemProvider.ts @@ -7,7 +7,6 @@ import { fireOtherStudioAction, OtherStudioAction, StudioActions } from "../../c import { projectContentsFromUri, studioOpenDialogFromURI } from "../../utils/FileProviderUtil"; import { classNameRegex, - getServerName, isClassDeployed, notNull, outputChannel, @@ -222,7 +221,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { // if (result instanceof File) { const api = new AtelierAPI(uri); - const serverName = getServerName(uri); + const serverName = isCSPFile(uri) ? uri.path : uri.path.slice(1).replace(/\//g, "."); if (serverName.slice(-4).toLowerCase() == ".cls") { if (await isClassDeployed(serverName, api)) { result.permissions |= vscode.FilePermission.Readonly; @@ -234,7 +233,7 @@ export class FileSystemProvider implements vscode.FileSystemProvider { if (vscode.workspace.getConfiguration("objectscript.serverSourceControl", uri)?.get("respectEditableStatus")) { const query = "select * from %Atelier_v1_Utils.Extension_GetStatus(?)"; const statusObj = await api.actionQuery(query, [serverName]); - const docStatus = statusObj.result.content.pop(); + const docStatus = statusObj.result?.content?.pop(); if (docStatus) { result.permissions = docStatus.editable ? undefined : result.permissions | vscode.FilePermission.Readonly; } From 6c38c58f384d1a3cedeafbc763f7da90dbdacd53 Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Fri, 12 Jul 2024 14:57:08 +0100 Subject: [PATCH 5/6] Be consistently explicit about `false` defaults on boolean config properties --- package.json | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 9c47c7b3..a7399d4b 100644 --- a/package.json +++ b/package.json @@ -1310,12 +1310,14 @@ "objectscript.serverSourceControl.disableOtherActionTriggers": { "description": "Prevent server-side source control 'other action' triggers from firing.", "type": "boolean", - "scope": "resource" + "scope": "resource", + "default": false }, "objectscript.serverSourceControl.respectEditableStatus": { "markdownDescription": "Set `isfs` document readonly if GetStatus method of server-side source control class returns Editable = 0.", "type": "boolean", - "scope": "resource" + "scope": "resource", + "default": false }, "objectscript.export": { "type": "object", @@ -1379,11 +1381,13 @@ }, "atelier": { "description": "Export source code as Atelier did it, with packages as subfolders. This setting only affects classes, routines, include files and DFI files.", - "type": "boolean" + "type": "boolean", + "default": false }, "generated": { "description": "Export generated source code files, such as INTs generated from classes.", - "type": "boolean" + "type": "boolean", + "default": false }, "filter": { "markdownDescription": "SQL filter to limit what to export. The filter is applied to document names using the [LIKE predicate](https://irisdocs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_like) (i.e. `Name LIKE '%filter%'`).", @@ -1402,11 +1406,13 @@ }, "noStorage": { "description": "Strip the storage definition on export. Can be useful when working across multiple systems.", - "type": "boolean" + "type": "boolean", + "default": false }, "dontExportIfNoChanges": { "description": "Do not rewrite the local file if the content is identical to what came from the server.", - "type": "boolean" + "type": "boolean", + "default": false }, "maxConcurrentConnections": { "description": "Maximum number of concurrent export connections. (0 = unlimited)", @@ -1414,7 +1420,8 @@ }, "mapped": { "description": "Export source code files mapped from a non-default database.", - "type": "boolean" + "type": "boolean", + "default": false } } }, @@ -1693,7 +1700,8 @@ }, "stopOnEntry": { "type": "boolean", - "description": "Automatically stop target after attach. If not specified, target does not stop." + "description": "Automatically stop target after attach. If not specified, target does not stop.", + "default": false } } } From 6e7695a23da44d9db2c1de41ce29d72815a25a57 Mon Sep 17 00:00:00 2001 From: gjsjohnmurray Date: Fri, 12 Jul 2024 22:27:09 +0100 Subject: [PATCH 6/6] Revert addition of boolean property defaults in `objectscript.export` --- package.json | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index a7399d4b..ba5d3d05 100644 --- a/package.json +++ b/package.json @@ -1381,13 +1381,11 @@ }, "atelier": { "description": "Export source code as Atelier did it, with packages as subfolders. This setting only affects classes, routines, include files and DFI files.", - "type": "boolean", - "default": false + "type": "boolean" }, "generated": { "description": "Export generated source code files, such as INTs generated from classes.", - "type": "boolean", - "default": false + "type": "boolean" }, "filter": { "markdownDescription": "SQL filter to limit what to export. The filter is applied to document names using the [LIKE predicate](https://irisdocs.intersystems.com/irislatest/csp/docbook/DocBook.UI.Page.cls?KEY=RSQL_like) (i.e. `Name LIKE '%filter%'`).", @@ -1406,13 +1404,11 @@ }, "noStorage": { "description": "Strip the storage definition on export. Can be useful when working across multiple systems.", - "type": "boolean", - "default": false + "type": "boolean" }, "dontExportIfNoChanges": { "description": "Do not rewrite the local file if the content is identical to what came from the server.", - "type": "boolean", - "default": false + "type": "boolean" }, "maxConcurrentConnections": { "description": "Maximum number of concurrent export connections. (0 = unlimited)", @@ -1420,8 +1416,7 @@ }, "mapped": { "description": "Export source code files mapped from a non-default database.", - "type": "boolean", - "default": false + "type": "boolean" } } },