diff --git a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts index 0676e2d1e7..ed2d7b0220 100644 --- a/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts +++ b/packages/zowe-explorer-api/src/tree/IZoweTreeNode.ts @@ -155,9 +155,9 @@ export interface IZoweDatasetTreeNode extends IZoweTreeNode { */ filter?: DatasetFilter; /** - * List of child nodes downloaded in binary format + * List of child nodes and user-selected encodings */ - binaryFiles?: Record; + encodingMap?: Record; /** * Binary indicator. Default false (text) */ @@ -185,10 +185,11 @@ export interface IZoweDatasetTreeNode extends IZoweTreeNode { */ setEtag?(etag: string); /** - * Specifies the field as binary - * @param binary true is a binary file otherwise false + * Sets the codepage value for the file + * + * @param {string} */ - setBinary?(binary: boolean); + setEncoding?(encoding: string); } /** @@ -204,8 +205,13 @@ export interface IZoweUSSTreeNode extends IZoweTreeNode { shortLabel?: string; /** * List of child nodes downloaded in binary format + * @deprecated Use `encodingMap` instead */ binaryFiles?: Record; + /** + * List of child nodes and user-selected encodings + */ + encodingMap?: Record; /** * Binary indicator. Default false (text) */ @@ -257,9 +263,16 @@ export interface IZoweUSSTreeNode extends IZoweTreeNode { rename?(newNamePath: string); /** * Specifies the field as binary + * @deprecated Use `setEncoding` instead * @param binary true is a binary file otherwise false */ setBinary?(binary: boolean); + /** + * Sets the codepage value for the file + * + * @param {string} + */ + setEncoding?(encoding: string); // /** // * Opens the text document // * @return vscode.TextDocument diff --git a/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts b/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts index 9fb883fbc3..4f3373a5ad 100644 --- a/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts +++ b/packages/zowe-explorer-api/src/tree/ZoweTreeNode.ts @@ -26,7 +26,7 @@ export class ZoweTreeNode extends vscode.TreeItem { public fullPath = ""; public dirty = false; public children: IZoweTreeNode[] = []; - public binaryFiles = {}; + public encodingMap = {}; public binary = false; public shortLabel = ""; diff --git a/packages/zowe-explorer/i18n/sample/src/shared/utils.i18n.json b/packages/zowe-explorer/i18n/sample/src/shared/utils.i18n.json index 9eec229010..20ccf1d48f 100644 --- a/packages/zowe-explorer/i18n/sample/src/shared/utils.i18n.json +++ b/packages/zowe-explorer/i18n/sample/src/shared/utils.i18n.json @@ -22,10 +22,9 @@ "zowe.shared.utils.promptForEncoding.binary.description": "Raw data representation", "zowe.shared.utils.promptForEncoding.other.label": "Other", "zowe.shared.utils.promptForEncoding.other.description": "Specify another codepage", - "zowe.shared.utils.promptForEncoding.current.description": "Current (local)", - "zowe.shared.utils.promptForEncoding.tagged.description": "Tagged (remote)", - "zowe.shared.utils.promptForEncoding.untagged.description": "Untagged", - "zowe.shared.utils.promptForEncoding.profile.description": "Inherit from profile", - "zowe.shared.utils.promptForEncoding.qp.placeHolder": "Choose an encoding", + "zowe.shared.utils.promptForEncoding.profile.description": "From profile {0}", + "zowe.shared.utils.promptForEncoding.tagged.description": "USS file tag", + "zowe.shared.utils.promptForEncoding.qp.title": "Choose encoding for {0}", + "zowe.shared.utils.promptForEncoding.qp.placeHolder": "Current encoding is {0}", "zowe.shared.utils.promptForEncoding.input.placeHolder": "Enter a codepage in the format 1047 or IBM-1047" } diff --git a/packages/zowe-explorer/src/dataset/DatasetTree.ts b/packages/zowe-explorer/src/dataset/DatasetTree.ts index a60485f969..dcf4c33027 100644 --- a/packages/zowe-explorer/src/dataset/DatasetTree.ts +++ b/packages/zowe-explorer/src/dataset/DatasetTree.ts @@ -517,6 +517,7 @@ export class DatasetTree extends ZoweTreeProvider implements IZoweTree { const encoding = await promptForEncoding(node); - if (encoding === undefined) { - return; - } else if (encoding === "binary") { - node.setBinary(true); - node.encoding = null; - } else { - node.setBinary(false); - node.encoding = encoding; + if (encoding !== undefined) { + node.setEncoding(encoding); + await dsActions.openPS(node, true, false, this); } - await dsActions.openPS(node, true, false, this); } } diff --git a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts index 441404ef73..abdf435233 100644 --- a/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts +++ b/packages/zowe-explorer/src/dataset/ZoweDatasetNode.ts @@ -54,7 +54,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod public memberPattern = ""; public dirty = true; public children: ZoweDatasetNode[] = []; - public binaryFiles = {}; + public encodingMap = {}; public binary = false; public errorDetails: zowe.imperative.ImperativeError; public ongoingActions: Record> = {}; @@ -78,11 +78,13 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod mParent: IZoweDatasetTreeNode, session: zowe.imperative.Session, contextOverride?: string, + encoding?: "text" | "binary" | string, private etag?: string, profile?: zowe.imperative.IProfileLoaded ) { super(label, collapsibleState, mParent, session, profile); - + this.binary = encoding === "binary"; + this.encoding = this.binary ? undefined : encoding === "text" ? null : encoding; if (contextOverride) { this.contextValue = contextOverride; } else if (collapsibleState !== vscode.TreeItemCollapsibleState.None) { @@ -201,6 +203,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod null, undefined, undefined, + undefined, this.getProfile() ); elementChildren[temp.label.toString()] = temp; @@ -213,6 +216,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod null, globals.DS_FILE_ERROR_CONTEXT, undefined, + undefined, this.getProfile() ); temp.errorDetails = item.error; // Save imperative error to avoid extra z/OS requests @@ -226,6 +230,7 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod null, globals.DS_MIGRATED_FILE_CONTEXT, undefined, + undefined, this.getProfile() ); elementChildren[temp.label.toString()] = temp; @@ -247,17 +252,20 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod null, globals.VSAM_CONTEXT, undefined, + undefined, this.getProfile() ); } } else if (contextually.isSessionNotFav(this)) { // Creates a ZoweDatasetNode for a PS + const cachedEncoding = this.getSessionNode().encodingMap[item.dsname]; const temp = new ZoweDatasetNode( item.dsname, vscode.TreeItemCollapsibleState.None, this, null, undefined, + cachedEncoding, undefined, this.getProfile() ); @@ -266,12 +274,14 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod } else { // Creates a ZoweDatasetNode for a PDS member const memberInvalid = item.member?.includes("\ufffd"); + const cachedEncoding = this.getSessionNode().encodingMap[`${item.dsname as string}(${item.member as string})`]; const temp = new ZoweDatasetNode( item.member, vscode.TreeItemCollapsibleState.None, this, null, memberInvalid ? globals.DS_FILE_ERROR_CONTEXT : undefined, + cachedEncoding, undefined, this.getProfile() ); @@ -489,19 +499,26 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod return responses; } - public setBinary(binary: boolean): void { - ZoweLogger.trace("ZoweDatasetNode.setBinary called."); + public setEncoding(encoding: "text" | "binary" | string): void { + ZoweLogger.trace("ZoweDatasetNode.setEncoding called."); if (!(this.contextValue.startsWith(globals.DS_DS_CONTEXT) || this.contextValue.startsWith(globals.DS_MEMBER_CONTEXT))) { - throw new Error(`Cannot set node with context ${this.contextValue} as binary`); + throw new Error(`Cannot set encoding for node with context ${this.contextValue}`); } - this.binary = binary; const isMemberNode = this.contextValue.startsWith(globals.DS_MEMBER_CONTEXT); - if (this.binary) { + if (encoding === "binary") { this.contextValue = isMemberNode ? globals.DS_MEMBER_BINARY_CONTEXT : globals.DS_DS_BINARY_CONTEXT; - this.getSessionNode().binaryFiles[this.fullPath] = true; + this.binary = true; + this.encoding = undefined; } else { this.contextValue = isMemberNode ? globals.DS_MEMBER_CONTEXT : globals.DS_DS_CONTEXT; - delete this.getSessionNode().binaryFiles[this.fullPath]; + this.binary = false; + this.encoding = encoding === "text" ? null : encoding; + } + const fullPath = isMemberNode ? `${this.getParent().label as string}(${this.label as string})` : (this.label as string); + if (this.binary || this.encoding != null) { + this.getSessionNode().encodingMap[fullPath] = encoding; + } else { + delete this.getSessionNode().encodingMap[fullPath]; } if (this.getParent() && this.getParent().contextValue === globals.FAV_PROFILE_CONTEXT) { this.contextValue += globals.FAV_SUFFIX; diff --git a/packages/zowe-explorer/src/dataset/actions.ts b/packages/zowe-explorer/src/dataset/actions.ts index 05cfc0e2fa..bbc72033ce 100644 --- a/packages/zowe-explorer/src/dataset/actions.ts +++ b/packages/zowe-explorer/src/dataset/actions.ts @@ -424,7 +424,7 @@ export async function createMember(parent: api.IZoweDatasetTreeNode, datasetProv datasetProvider.refreshElement(parent); await openPS( - new ZoweDatasetNode(name, vscode.TreeItemCollapsibleState.None, parent, null, undefined, undefined, parent.getProfile()), + new ZoweDatasetNode(name, vscode.TreeItemCollapsibleState.None, parent, null, undefined, undefined, undefined, parent.getProfile()), false, true, datasetProvider diff --git a/packages/zowe-explorer/src/shared/utils.ts b/packages/zowe-explorer/src/shared/utils.ts index b35e5ae3c2..55e4f3c5d5 100644 --- a/packages/zowe-explorer/src/shared/utils.ts +++ b/packages/zowe-explorer/src/shared/utils.ts @@ -412,7 +412,20 @@ export async function compareFileContent( } } -export async function promptForEncoding(node: IZoweDatasetTreeNode | IZoweUSSTreeNode, taggedEncoding?: string): Promise { +export function getCachedEncoding(node: T): string { + if (isZoweUSSTreeNode(node)) { + return (node.getSessionNode() as IZoweUSSTreeNode).encodingMap[node.fullPath] as string; + } else { + const isMemberNode = node.contextValue.startsWith(globals.DS_MEMBER_CONTEXT); + const fullPath = isMemberNode ? `${node.getParent().label as string}(${node.label as string})` : (node.label as string); + return (node.getSessionNode() as IZoweDatasetTreeNode).encodingMap[fullPath] as string; + } +} + +export async function promptForEncoding( + node: IZoweDatasetTreeNode | IZoweUSSTreeNode, + taggedEncoding?: string +): Promise<"text" | "binary" | string | undefined> { const ebcdicItem: vscode.QuickPickItem = { label: localize("zowe.shared.utils.promptForEncoding.ebcdic.label", "EBCDIC"), description: localize("zowe.shared.utils.promptForEncoding.ebcdic.description", "z/OS default codepage"), @@ -425,74 +438,60 @@ export async function promptForEncoding(node: IZoweDatasetTreeNode | IZoweUSSTre label: localize("zowe.shared.utils.promptForEncoding.other.label", "Other"), description: localize("zowe.shared.utils.promptForEncoding.other.description", "Specify another codepage"), }; - - const fileItems: vscode.QuickPickItem[] = []; - let currentEncoding = node.encoding; - if (node.binary) { - currentEncoding = binaryItem.label; - } else if (node.encoding === null) { - currentEncoding = ebcdicItem.label; - } - if (currentEncoding != null) { - fileItems.push({ - label: currentEncoding, - description: localize("zowe.shared.utils.promptForEncoding.current.description", "Current (local)"), + const items: vscode.QuickPickItem[] = [ebcdicItem, binaryItem, otherItem, globals.SEPARATORS.RECENT]; + const profile = node.getProfile(); + if (profile.profile?.encoding != null) { + items.splice(0, 0, { + label: profile.profile?.encoding, + description: localize("zowe.shared.utils.promptForEncoding.profile.description", "From profile {0}", profile.name), }); } if (taggedEncoding != null) { - if (taggedEncoding === currentEncoding.toLowerCase()) { - fileItems[0].description += ", " + localize("zowe.shared.utils.promptForEncoding.tagged.description", "Tagged (remote)"); - } else { - fileItems.push({ - label: taggedEncoding, - description: localize("zowe.shared.utils.promptForEncoding.tagged.description", "Tagged (remote)"), - }); - } - } else if (currentEncoding != null) { - fileItems[0].description += ", " + localize("zowe.shared.utils.promptForEncoding.untagged.description", "Untagged"); - } - if (fileItems.length > 0) { - fileItems.push(globals.SEPARATORS.BLANK); - } - - const globalItems: vscode.QuickPickItem[] = [ebcdicItem, binaryItem, otherItem, globals.SEPARATORS.RECENT]; - const profileEncoding = node.getProfile().profile?.encoding; - if (profileEncoding != null) { - globalItems.splice(0, 0, { - label: profileEncoding, - description: localize("zowe.shared.utils.promptForEncoding.profile.description", "Inherit from profile"), + items.splice(0, 0, { + label: taggedEncoding, + description: localize("zowe.shared.utils.promptForEncoding.tagged.description", "USS file tag"), }); } - const recentItems: vscode.QuickPickItem[] = []; + let currentEncoding = node.encoding ?? getCachedEncoding(node); + if (node.binary || currentEncoding === "binary") { + currentEncoding = binaryItem.label; + } else if (node.encoding === null || currentEncoding === "text") { + currentEncoding = ebcdicItem.label; + } const encodingHistory = ZoweLocalStorage.getValue("encodingHistory") ?? []; if (encodingHistory.length > 0) { for (const encoding of encodingHistory) { - recentItems.push({ label: encoding }); + items.push({ label: encoding }); } } else { // Pre-populate recent list with some common encodings - recentItems.push({ label: "IBM-1047" }, { label: "ISO8859-1" }); + items.push({ label: "IBM-1047" }, { label: "ISO8859-1" }); } let encoding = ( - await Gui.showQuickPick([...fileItems, ...globalItems, ...recentItems], { - placeHolder: localize("zowe.shared.utils.promptForEncoding.qp.placeHolder", "Choose an encoding"), + await Gui.showQuickPick(items, { + title: localize("zowe.shared.utils.promptForEncoding.qp.title", "Choose encoding for {0}", node.label as string), + placeHolder: + currentEncoding && localize("zowe.shared.utils.promptForEncoding.qp.placeHolder", "Current encoding is {0}", currentEncoding), }) )?.label; switch (encoding) { case ebcdicItem.label: - encoding = null; + encoding = "text"; break; case binaryItem.label: encoding = "binary"; break; case otherItem.label: encoding = await Gui.showInputBox({ + title: localize("zowe.shared.utils.promptForEncoding.qp.title", "Choose encoding for {0}", node.label as string), placeHolder: localize("zowe.shared.utils.promptForEncoding.input.placeHolder", "Enter a codepage in the format 1047 or IBM-1047"), }); - encodingHistory.push(encoding); - ZoweLocalStorage.setValue("encodingHistory", encodingHistory.slice(0, globals.MAX_FILE_HISTORY)); + if (encoding != null) { + encodingHistory.push(encoding); + ZoweLocalStorage.setValue("encodingHistory", encodingHistory.slice(0, globals.MAX_FILE_HISTORY)); + } break; } return encoding; diff --git a/packages/zowe-explorer/src/uss/USSTree.ts b/packages/zowe-explorer/src/uss/USSTree.ts index 5a8237caed..641cb743d1 100644 --- a/packages/zowe-explorer/src/uss/USSTree.ts +++ b/packages/zowe-explorer/src/uss/USSTree.ts @@ -387,7 +387,15 @@ export class USSTree extends ZoweTreeProvider implements IZoweTree child === remote) !== undefined; - } // Get specific node based on label and parent tree (session / favorites) const nodes: IZoweUSSTreeNode[] = concatChildNodes(sesNode ? [sesNode] : ussFileProvider.mSessionNodes); const node = nodes.find((zNode) => { diff --git a/packages/zowe-explorer/src/uss/utils.ts b/packages/zowe-explorer/src/uss/utils.ts index 19d2a619ae..763c1730a1 100644 --- a/packages/zowe-explorer/src/uss/utils.ts +++ b/packages/zowe-explorer/src/uss/utils.ts @@ -74,21 +74,19 @@ export function disposeClipboardContents(): void { } export async function autoDetectEncoding(node: IZoweUSSTreeNode, profile?: imperative.IProfileLoaded): Promise { - if (node.encoding !== undefined) { + if (node.binary || node.encoding !== undefined) { return; } const ussApi = ZoweExplorerApiRegister.getUssApi(profile ?? node.getProfile()); if (ussApi.getTag != null) { const taggedEncoding = await ussApi.getTag(node.fullPath); if (taggedEncoding === "binary" || taggedEncoding === "mixed") { - node.setBinary(true); - node.encoding = undefined; + node.setEncoding("binary"); } else { - node.setBinary(false); - node.encoding = taggedEncoding !== "untagged" ? taggedEncoding : null; + node.setEncoding(taggedEncoding !== "untagged" ? taggedEncoding : "text"); } } else { - node.binary = await ussApi.isFileTagBinOrAscii(node.fullPath); - node.encoding = undefined; + const isBinary = await ussApi.isFileTagBinOrAscii(node.fullPath); + node.setEncoding(isBinary ? "binary" : "text"); } }