Repository: zowe/zowe-explorer-vscode
Pull request #3477: Bug fixes on open recent member command
Author: login: JWaters02
Labels: name: size/L
Created: 2025-02-21T20:19:21Z | Merged: 2025-03-20T15:19:18Z

## Proposed changes

<!-- Describe the big picture of your changes here to communicate to the maintainers why we should accept this pull request. If it fixes a bug or resolves a feature request, be sure to link to that issue. -->

Fixes both parts of #3447 

1. Keybind `ctrl + alt + r` has a couple of conflicts with system keybinds, so when you press the keybinding, vscode does not even attempt to call the command. I noticed that ctrl + alt + t is free (and I have quite a lot of extensions installed, so hopefully it is fine? Unless macOS has something). So changed it to that.
2. Fixed the bug with profile not being found causing an error
3. Fixed the bug where profiles being in the wrong case on uss files would be not found
4. Added open recent member into the command palette (this is nice QoL for me, a command palette enjoyer)

## Release Notes

<!-- Include the Milestone Number and a small description of your change that will be added to the changelog. -->
<!-- If there is a linked issue, it should have the same milestone as this PR. -->

Milestone: 3.1.2

Changelog:
- The "Open Recent Member" command can now be triggered from the command palette.
- Fixed issue where selecting a recent member to open from a profile name that no longer exists would cause an error. [#3447](https://github.com/zowe/zowe-explorer-vscode/issues/3447)
- Fixed issue with a conflicting keybinding for `Open Recent Member`, changed keybinding to `Ctrl`+`Alt`+`t` for Windows and `⌘ Cmd`+`⌥ Opt`+`t` for macOS. [#3447](https://github.com/zowe/zowe-explorer-vscode/issues/3447)

## Types of changes

<!-- What types of changes does your code introduce to Zowe Explorer? Put an `x` in all boxes that apply. -->

- [x] Bug fix (non-breaking change which fixes an issue)
- [x] Enhancement (non-breaking change which adds or improves functionality)
- [ ] Breaking change (a change that would cause existing functionality to not work as expected)
- [ ] Documentation (Markdown, README updates)
- [x] Other (please specify above in "Proposed changes" section): Keybinding change

## Checklist

<!-- Put an `x` in all boxes that apply. You can also fill these out after creating the PR. If you're unsure about any of them, don't hesitate to ask. We're here to help! This checklist will be used as reference for both the contributor and the reviewer. -->

**General**

- [x] I have read the [CONTRIBUTOR GUIDANCE](https://github.com/zowe/zowe-explorer-vscode/wiki/Best-Practices:-Contributor-Guidance) wiki
- [ ] All PR dependencies have been merged and published (if applicable)
- [ ] A GIF or screenshot is included in the PR for visual changes
- [x] The pre-publish command has been executed:
  - **v2 and below:** `yarn workspace vscode-extension-for-zowe vscode:prepublish`
  - **v3:** `pnpm --filter vscode-extension-for-zowe vscode:prepublish`

**Code coverage**

- [x] There is coverage for the code that I have added
- [x] I have added new test cases and they are passing
- [x] I have manually tested the changes

**Deployment**

- [ ] I have added developer documentation (if applicable)
- [ ] Documentation should be added to Zowe Docs
  - If you're an outside contributor, please post in the [#zowe-doc Slack channel](https://openmainframeproject.slack.com/archives/CC961JYMQ) to coordinate documentation.
  - Otherwise, please check with the rest of the squad about any needed documentation before merging.
- [ ] These changes may need ported to the appropriate branches (list here): 3.1.2

## Further comments

<!-- If this is a relatively large or complex change, kick off the discussion by explaining why you chose the solution you did, what alternatives you considered, etc... -->

No mention of open recent member command in zowe docs, so nothing to be changed there in relation to keybinds changes.



Diff:
diff --git a/packages/zowe-explorer/CHANGELOG.md b/packages/zowe-explorer/CHANGELOG.md
index 520c5c3faf..47a9ede089 100644
--- a/packages/zowe-explorer/CHANGELOG.md
+++ b/packages/zowe-explorer/CHANGELOG.md
@@ -15,6 +15,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen
 - Implemented drag and drop functionality of data sets within and across LPARs. [#3413](https://github.com/zowe/zowe-explorer-vscode/pull/3413)
 - Renamed "Zowe Explorer z/OS Console" to "z/OS Console". [#3445](https://github.com/zowe/zowe-explorer-vscode/pull/3445)
 - Adopted ZE APIs new `directConnectLogin` and `directConnectLogout` methods for login and logout actions NOT using the tokenType `apimlAuthenticationToken`. [#3346](https://github.com/zowe/zowe-explorer-vscode/issues/3346)
+- The "Open Recent Member" command can now be triggered from the command palette. [#3477](https://github.com/zowe/zowe-explorer-vscode/pull/3477)
 
 ### Bug fixes
 
@@ -26,6 +27,9 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen
 - Fixed z/OS Console panel background colour to be in sync with the rest of the VS Code styling. [#3445](https://github.com/zowe/zowe-explorer-vscode/pull/3445)
 - Fixed an issue seen with outdated profile information in the z/OS tree view data during upload and download of data set and USS files [#3457](https://github.com/zowe/zowe-explorer-vscode/issues/3457)
 - Fixed issue where deleting too many nodes at once would cause the confirmation prompt to be oversized. [#3254](https://github.com/zowe/zowe-explorer-vscode/issues/3254)
+- Fixed issue where selecting a recent member to open from a profile name that no longer exists would cause an error. [#3447](https://github.com/zowe/zowe-explorer-vscode/issues/3447)
+- Fixed issue with a conflicting keybinding for `Open Recent Member`, changed keybinding to `Ctrl`+`Alt`+`t` for Windows and `⌘ Cmd`+`⌥ Opt`+`t` for macOS. [#3447](https://github.com/zowe/zowe-explorer-vscode/issues/3447)
+- Fixed issue where saving data set member to history would save only the member name instead of the full path. [#3447](https://github.com/zowe/zowe-explorer-vscode/pull/3477)
 - Fixed an issue with UNIX file edit attributes refresh button not updating/reverting values correctly. [#3238](https://github.com/zowe/zowe-explorer-vscode/issues/3238)
 - Fixed an issue seen where extender favorites not showing in the tree views. [#3470](https://github.com/zowe/zowe-explorer-vscode/issues/3470)
 - Fixed an issue where selecting items in table views would reset the column sort order. [#3473](https://github.com/zowe/zowe-explorer-vscode/issues/3473)
diff --git a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedActions.unit.test.ts b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedActions.unit.test.ts
index b778a71125..723be26c3c 100644
--- a/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedActions.unit.test.ts
+++ b/packages/zowe-explorer/__tests__/__unit__/trees/shared/SharedActions.unit.test.ts
@@ -460,6 +460,94 @@ describe("Shared Actions Unit Tests - Function openRecentMemberPrompt", () => {
         await SharedActions.openRecentMemberPrompt(testDatasetTree, testUSSTree);
         expect(testUSSTree.openItemFromPath).toHaveBeenCalledWith(`/node1/node2/node3.txt`, blockMocks.ussSessionNode);
     });
+
+    it("Tests that openRecentMemberPrompt shows 'Profile not found' message when session is not valid", async () => {
+        const globalMocks = await createGlobalMocks();
+        const blockMocks = createBlockMocks(globalMocks);
+
+        const quickPickContent = createQuickPickContent("[invalid]: node", [], globalMocks.qpPlaceholder);
+        const testDatasetTree = createDatasetTree(blockMocks.datasetSessionNode, globalMocks.treeView);
+        mocked(testDatasetTree.getFileHistory).mockReturnValueOnce([`[invalid]: node`]);
+        const testUSSTree = createUSSTree([], [blockMocks.ussSessionNode], globalMocks.treeView);
+        mocked(testUSSTree.getFileHistory).mockReturnValueOnce([]);
+        mocked(Gui.createQuickPick).mockReturnValue(quickPickContent);
+        jest.spyOn(Gui, "resolveQuickPick").mockResolvedValueOnce(new FilterDescriptor("node"));
+
+        const showMessageSpy = jest.spyOn(Gui, "showMessage");
+
+        await SharedActions.openRecentMemberPrompt(testDatasetTree, testUSSTree);
+        expect(showMessageSpy).toHaveBeenCalledWith("Profile not found.");
+    });
+
+    it("Tests that openRecentMemberPrompt handles profile names with different casings for Data Set", async () => {
+        const globalMocks = await createGlobalMocks();
+        const blockMocks = createBlockMocks(globalMocks);
+
+        const dsNode = new ZoweDatasetNode({
+            label: "node",
+            collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
+            parentNode: blockMocks.datasetSessionNode,
+            session: globalMocks.session,
+        });
+        dsNode.contextValue = Constants.DS_DS_CONTEXT;
+        const qpItem = new FilterDescriptor(dsNode.label as string);
+        const quickPickContent = createQuickPickContent("[SESTEST]: node", [qpItem], globalMocks.qpPlaceholder);
+
+        const testDatasetTree = createDatasetTree(blockMocks.datasetSessionNode, globalMocks.treeView);
+        mocked(testDatasetTree.getFileHistory).mockReturnValueOnce([`[SESTEST]: node`]);
+        const testUSSTree = createUSSTree([], [blockMocks.ussSessionNode], globalMocks.treeView);
+        mocked(testUSSTree.getFileHistory).mockReturnValueOnce([]);
+        mocked(Gui.createQuickPick).mockReturnValue(quickPickContent);
+        jest.spyOn(Gui, "resolveQuickPick").mockResolvedValueOnce(qpItem);
+
+        await SharedActions.openRecentMemberPrompt(testDatasetTree, testUSSTree);
+        expect(testDatasetTree.openItemFromPath).toHaveBeenCalledWith(`[SESTEST]: node`, blockMocks.datasetSessionNode);
+    });
+
+    it("Tests that openRecentMemberPrompt handles profile names with different casings for USS", async () => {
+        const globalMocks = await createGlobalMocks();
+        const blockMocks = createBlockMocks(globalMocks);
+
+        const node = new ZoweUSSNode({
+            label: "node3.txt",
+            collapsibleState: vscode.TreeItemCollapsibleState.None,
+            parentNode: blockMocks.ussSessionNode,
+            profile: globalMocks.imperativeProfile,
+            parentPath: "/node1/node2",
+        });
+        node.contextValue = Constants.DS_DS_CONTEXT;
+        const qpItem = new FilterDescriptor(node.label as string);
+        const quickPickContent = createQuickPickContent("[SESTEST]: /node1/node2/node3.txt", [qpItem], globalMocks.qpPlaceholder);
+
+        const testDatasetTree = createDatasetTree(blockMocks.datasetSessionNode, globalMocks.treeView);
+        mocked(testDatasetTree.getFileHistory).mockReturnValueOnce([]);
+        const testUSSTree = createUSSTree([], [blockMocks.ussSessionNode], globalMocks.treeView);
+        mocked(testUSSTree.getFileHistory).mockReturnValueOnce([`[SESTEST]: /node1/node2/node3.txt`]);
+        mocked(Gui.createQuickPick).mockReturnValue(quickPickContent);
+        jest.spyOn(Gui, "resolveQuickPick").mockResolvedValueOnce(qpItem);
+
+        await SharedActions.openRecentMemberPrompt(testDatasetTree, testUSSTree);
+        expect(testUSSTree.openItemFromPath).toHaveBeenCalledWith(`/node1/node2/node3.txt`, blockMocks.ussSessionNode);
+    });
+
+    it("Tests that openRecentMemberPrompt shows a UI message when the profile is not found on a USS path and returns early", async () => {
+        const globalMocks = await createGlobalMocks();
+        const blockMocks = createBlockMocks(globalMocks);
+
+        const quickPickContent = createQuickPickContent("[invalid]: /profile/dir/file.txt", [], globalMocks.qpPlaceholder);
+        const testDatasetTree = createDatasetTree(blockMocks.datasetSessionNode, globalMocks.treeView);
+        mocked(testDatasetTree.getFileHistory).mockReturnValueOnce([]);
+        const testUSSTree = createUSSTree([], [blockMocks.ussSessionNode], globalMocks.treeView);
+        mocked(testUSSTree.getFileHistory).mockReturnValueOnce([`[invalid]: /profile/dir/file.txt`]);
+        mocked(Gui.createQuickPick).mockReturnValueOnce(quickPickContent);
+        jest.spyOn(Gui, "resolveQuickPick").mockResolvedValueOnce(new FilterDescriptor("[invalid]: /profile/dir/file.txt"));
+        const showMessageSpy = jest.spyOn(Gui, "showMessage");
+
+        await SharedActions.openRecentMemberPrompt(testDatasetTree, testUSSTree);
+        expect(showMessageSpy).toHaveBeenCalledWith("Profile not found.");
+        expect(testDatasetTree.openItemFromPath).not.toHaveBeenCalled();
+        expect(testUSSTree.openItemFromPath).not.toHaveBeenCalled();
+    });
 });
 
 describe("Shared Actions Unit Tests - Function returnIconState", () => {
diff --git a/packages/zowe-explorer/l10n/bundle.l10n.json b/packages/zowe-explorer/l10n/bundle.l10n.json
index 3b4b8e9047..500e0e1daa 100644
--- a/packages/zowe-explorer/l10n/bundle.l10n.json
+++ b/packages/zowe-explorer/l10n/bundle.l10n.json
@@ -198,42 +198,6 @@
   "Profile auth error": "Profile auth error",
   "Profile is not authenticated, please log in to continue": "Profile is not authenticated, please log in to continue",
   "Retrieving response from USS list API": "Retrieving response from USS list API",
-  "The 'move' function is not implemented for this USS API.": "The 'move' function is not implemented for this USS API.",
-  "Failed to move {0}/File path": {
-    "message": "Failed to move {0}",
-    "comment": [
-      "File path"
-    ]
-  },
-  "Profile does not exist for this file.": "Profile does not exist for this file.",
-  "Saving USS file...": "Saving USS file...",
-  "Failed to rename {0}/File path": {
-    "message": "Failed to rename {0}",
-    "comment": [
-      "File path"
-    ]
-  },
-  "Failed to delete {0}/File name": {
-    "message": "Failed to delete {0}",
-    "comment": [
-      "File name"
-    ]
-  },
-  "No error details given": "No error details given",
-  "Error fetching destination {0} for paste action: {1}/USS pathError message": {
-    "message": "Error fetching destination {0} for paste action: {1}",
-    "comment": [
-      "USS path",
-      "Error message"
-    ]
-  },
-  "Failed to copy {0} to {1}/Source pathDestination path": {
-    "message": "Failed to copy {0} to {1}",
-    "comment": [
-      "Source path",
-      "Destination path"
-    ]
-  },
   "Downloaded: {0}/Download time": {
     "message": "Downloaded: {0}",
     "comment": [
@@ -295,6 +259,42 @@
   "initializeUSSFavorites.error.buttonRemove": "initializeUSSFavorites.error.buttonRemove",
   "File does not exist. It may have been deleted.": "File does not exist. It may have been deleted.",
   "Pulling from Mainframe...": "Pulling from Mainframe...",
+  "The 'move' function is not implemented for this USS API.": "The 'move' function is not implemented for this USS API.",
+  "Failed to move {0}/File path": {
+    "message": "Failed to move {0}",
+    "comment": [
+      "File path"
+    ]
+  },
+  "Profile does not exist for this file.": "Profile does not exist for this file.",
+  "Saving USS file...": "Saving USS file...",
+  "Failed to rename {0}/File path": {
+    "message": "Failed to rename {0}",
+    "comment": [
+      "File path"
+    ]
+  },
+  "Failed to delete {0}/File name": {
+    "message": "Failed to delete {0}",
+    "comment": [
+      "File name"
+    ]
+  },
+  "No error details given": "No error details given",
+  "Error fetching destination {0} for paste action: {1}/USS pathError message": {
+    "message": "Error fetching destination {0} for paste action: {1}",
+    "comment": [
+      "USS path",
+      "Error message"
+    ]
+  },
+  "Failed to copy {0} to {1}/Source pathDestination path": {
+    "message": "Failed to copy {0} to {1}",
+    "comment": [
+      "Source path",
+      "Destination path"
+    ]
+  },
   "{0} location/Node type": {
     "message": "{0} location",
     "comment": [
@@ -377,6 +377,7 @@
   "You must enter a pattern.": "You must enter a pattern.",
   "Prompting the user to choose a recent member for editing": "Prompting the user to choose a recent member for editing",
   "Select a recent member to open": "Select a recent member to open",
+  "Profile not found.": "Profile not found.",
   "No recent members found.": "No recent members found.",
   "Use the search button to display jobs": "Use the search button to display jobs",
   "No spool files found": "No spool files found",
diff --git a/packages/zowe-explorer/l10n/poeditor.json b/packages/zowe-explorer/l10n/poeditor.json
index 8e267f4b82..f49e95cbb0 100644
--- a/packages/zowe-explorer/l10n/poeditor.json
+++ b/packages/zowe-explorer/l10n/poeditor.json
@@ -404,6 +404,9 @@
   "zowe.security.secureCredentialsEnabled": {
     "Allow credentials to be stored securely. If disabled and autoStore is set to true, z/OS credentials are stored as clear text in zowe.config.json.": ""
   },
+  "zowe.openRecentMember": {
+    "Open Recent Member": ""
+  },
   "issueTsoCmd": {
     "Issue TSO Command": ""
   },
@@ -615,15 +618,6 @@
   "Profile auth error": "",
   "Profile is not authenticated, please log in to continue": "",
   "Retrieving response from USS list API": "",
-  "The 'move' function is not implemented for this USS API.": "",
-  "Failed to move {0}": "",
-  "Profile does not exist for this file.": "",
-  "Saving USS file...": "",
-  "Failed to rename {0}": "",
-  "Failed to delete {0}": "",
-  "No error details given": "",
-  "Error fetching destination {0} for paste action: {1}": "",
-  "Failed to copy {0} to {1}": "",
   "Downloaded: {0}": "",
   "Encoding: {0}": "",
   "Binary": "",
@@ -649,6 +643,15 @@
   "initializeUSSFavorites.error.buttonRemove": "",
   "File does not exist. It may have been deleted.": "",
   "Pulling from Mainframe...": "",
+  "The 'move' function is not implemented for this USS API.": "",
+  "Failed to move {0}": "",
+  "Profile does not exist for this file.": "",
+  "Saving USS file...": "",
+  "Failed to rename {0}": "",
+  "Failed to delete {0}": "",
+  "No error details given": "",
+  "Error fetching destination {0} for paste action: {1}": "",
+  "Failed to copy {0} to {1}": "",
   "{0} location": "",
   "Choose a location to create the {0}": "",
   "Name of file or directory": "",
@@ -691,6 +694,7 @@
   "You must enter a pattern.": "",
   "Prompting the user to choose a recent member for editing": "",
   "Select a recent member to open": "",
+  "Profile not found.": "",
   "No recent members found.": "",
   "Use the search button to display jobs": "",
   "No spool files found": "",
diff --git a/packages/zowe-explorer/package.json b/packages/zowe-explorer/package.json
index 27a6ac13b2..a11df70582 100644
--- a/packages/zowe-explorer/package.json
+++ b/packages/zowe-explorer/package.json
@@ -88,8 +88,8 @@
     "keybindings": [
       {
         "command": "zowe.openRecentMember",
-        "key": "ctrl+alt+r",
-        "mac": "cmd+alt+r"
+        "key": "ctrl+alt+t",
+        "mac": "cmd+alt+t"
       },
       {
         "command": "zowe.searchInAllLoadedItems",
@@ -682,6 +682,11 @@
         "command": "zowe.placeholderCommand",
         "title": "%zowe.placeholderCommand%",
         "enablement": "false"
+      },
+      {
+        "command": "zowe.openRecentMember",
+        "title": "%zowe.openRecentMember%",
+        "category": "Zowe Explorer"
       }
     ],
     "menus": {
@@ -1601,6 +1606,11 @@
         {
           "command": "zowe.deleteProfile",
           "when": "never"
+        },
+        {
+          "command": "zowe.openRecentMember",
+          "when": "true",
+          "group": "navigation"
         }
       ]
     },
diff --git a/packages/zowe-explorer/package.nls.json b/packages/zowe-explorer/package.nls.json
index 992e688712..e8e56dd1d5 100644
--- a/packages/zowe-explorer/package.nls.json
+++ b/packages/zowe-explorer/package.nls.json
@@ -134,6 +134,7 @@
   "zowe.pollInterval.info": "Default interval (in milliseconds) when polling spool files.",
   "zowe.security.checkForCustomCredentialManagers": "Check for any installed VS Code extensions for handling credentials when activating Zowe Explorer",
   "zowe.security.secureCredentialsEnabled": "Allow credentials to be stored securely. If disabled and autoStore is set to true, z/OS credentials are stored as clear text in zowe.config.json.",
+  "zowe.openRecentMember": "Open Recent Member",
   "issueTsoCmd": "Issue TSO Command",
   "deleteProfile": "Delete a Profile Permanently...",
   "renameDataSet": "Rename Data Set",
diff --git a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts
index f7420988ad..338798fbec 100644
--- a/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts
+++ b/packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts
@@ -694,7 +694,13 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod
                     await vscode.commands.executeCommand("vscode.open", this.resourceUri);
                 }
                 if (datasetProvider) {
-                    datasetProvider.addFileHistory(`[${this.getProfileName()}]: ${this.label as string}`);
+                    let criteria = `[${this.getProfileName()}]: `;
+                    if (SharedContext.isDsMember(this)) {
+                        criteria = `${criteria}${this.getParent().label as string}(${this.label as string})`;
+                    } else {
+                        criteria = `${criteria}${this.label as string}`;
+                    }
+                    datasetProvider.addFileHistory(criteria);
                 }
             } catch (err) {
                 await AuthUtils.errorHandling(err, { apiType: ZoweExplorerApiType.Mvs, profile: this.getProfile() });
diff --git a/packages/zowe-explorer/src/trees/shared/SharedActions.ts b/packages/zowe-explorer/src/trees/shared/SharedActions.ts
index 59c688eae7..3d5ac6ed95 100644
--- a/packages/zowe-explorer/src/trees/shared/SharedActions.ts
+++ b/packages/zowe-explorer/src/trees/shared/SharedActions.ts
@@ -187,13 +187,21 @@ export class SharedActions {
             if (pattern.indexOf("/") > -1) {
                 // USS file was selected
                 const filePath = pattern.substring(pattern.indexOf("/"));
-                const sessionNode = ussTree.mSessionNodes.find((sessNode) => sessNode.getProfileName() === sessionName);
+                const sessionNode = ussTree.mSessionNodes.find((sessNode) => sessNode.label.toString().toLowerCase() === sessionName.toLowerCase());
+                if (!sessionNode) {
+                    Gui.showMessage(vscode.l10n.t("Profile not found."));
+                    return;
+                }
                 await ussTree.openItemFromPath(filePath, sessionNode);
             } else {
                 // Data set was selected
                 const sessionNode = datasetTree.mSessionNodes.find(
                     (sessNode) => sessNode.label.toString().toLowerCase() === sessionName.toLowerCase()
                 );
+                if (!sessionNode) {
+                    Gui.showMessage(vscode.l10n.t("Profile not found."));
+                    return;
+                }
                 await datasetTree.openItemFromPath(pattern, sessionNode);
             }
         } else {


Reviews:
user: login: traeok
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-02-27T14:53:57Z
user: login: zFernand0
body: Changes LGTM! 😋 
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-03-06T19:14:14Z
user: login: traeok
body: The issue I encountered is now resolved. Thanks again for including that bug fix!

However, I've run into an issue where, after opening a data set member, the member is no longer added to that list:

![ZE-OpenRecentMember-Issue2](https://github.com/user-attachments/assets/eeed10bb-1184-4013-8d23-6c6aabbd6e39)

This doesn't happen all the time, but seems to be after I open a file without selecting an encoding.

---

Also, maybe we can discuss this as a parking lot in the next ZE standup, but I do have some concerns about how we match against the session name (coercing using `toLowerCase`). Maybe this could be resolved separately since I could see this being "breaking" / involving more refactoring to address recent members that were already added in all caps.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T20:19:45Z
user: login: JWaters02
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-06T21:39:53Z
user: login: t1m0thyj
body: LGTM, thanks @JWaters02!
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-03-13T14:56:09Z
user: login: zFernand0
body: LGTM! 😋 
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-18T17:51:59Z
user: login: traeok
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-19T13:54:38Z

Review comments:
diff_hunk: @@ -11,6 +11,7 @@ All notable changes to the "vscode-extension-for-zowe" extension will be documen
 - Implemented drag and drop functionality of data sets within and across LPARs. [#3413](https://github.com/zowe/zowe-explorer-vscode/pull/3413)
 - Renamed "Zowe Explorer z/OS Console" to "z/OS Console". [#3445](https://github.com/zowe/zowe-explorer-vscode/pull/3445)
 - Adopted ZE APIs new `directConnectLogin` and `directConnectLogout` methods for login and logout actions NOT using the tokenType `apimlAuthenticationToken`. [#3346](https://github.com/zowe/zowe-explorer-vscode/issues/3346)
+- The "Open Recent Member" command can now be triggered from the command palette.
path: packages/zowe-explorer/CHANGELOG.md
user: login: traeok
body: Can you link your PR on this changelog entry? It identifies where the change was made and helps us give proper credits in the GitHub release 😋 
created_at: 2025-02-27T14:43:32Z
author_association: MEMBER
diff_hunk: @@ -187,13 +187,21 @@ export class SharedActions {
             if (pattern.indexOf("/") > -1) {
                 // USS file was selected
                 const filePath = pattern.substring(pattern.indexOf("/"));
-                const sessionNode = ussTree.mSessionNodes.find((sessNode) => sessNode.getProfileName() === sessionName);
+                const sessionNode = ussTree.mSessionNodes.find((sessNode) => sessNode.label.toString().toLowerCase() === sessionName.toLowerCase());
path: packages/zowe-explorer/src/trees/shared/SharedActions.ts
user: login: zFernand0
body: I guess I haven't had a need to do this, but do we not support case-sensitive profile names? 😋 

created_at: 2025-03-06T19:14:04Z
author_association: MEMBER
diff_hunk: @@ -689,7 +689,13 @@ export class ZoweDatasetNode extends ZoweTreeNode implements IZoweDatasetTreeNod
                     await vscode.commands.executeCommand("vscode.open", this.resourceUri);
                 }
                 if (datasetProvider) {
-                    datasetProvider.addFileHistory(`[${this.getProfileName()}]: ${this.label as string}`);
+                    let criteria = `[${this.getProfileName()}]: `;
+                    if (SharedContext.isDsMember(this)) {
+                        criteria = `${criteria}${this.getParent().label as string}(${this.label as string})`;
+                    } else {
+                        criteria = `${criteria}${this.label as string}`;
+                    }
+                    datasetProvider.addFileHistory(criteria);
path: packages/zowe-explorer/src/trees/dataset/ZoweDatasetNode.ts
user: login: traeok
body: Thanks for fixing how the recent member is saved 🙏 
created_at: 2025-03-06T20:03:03Z
author_association: MEMBER
diff_hunk: @@ -187,13 +187,21 @@ export class SharedActions {
             if (pattern.indexOf("/") > -1) {
                 // USS file was selected
                 const filePath = pattern.substring(pattern.indexOf("/"));
-                const sessionNode = ussTree.mSessionNodes.find((sessNode) => sessNode.getProfileName() === sessionName);
+                const sessionNode = ussTree.mSessionNodes.find((sessNode) => sessNode.label.toString().toLowerCase() === sessionName.toLowerCase());
path: packages/zowe-explorer/src/trees/shared/SharedActions.ts
user: login: JWaters02
body: Good point, left a comment about it below https://github.com/zowe/zowe-explorer-vscode/pull/3477#issuecomment-2704986548
created_at: 2025-03-06T21:39:53Z
author_association: MEMBER
diff_hunk: @@ -187,13 +187,21 @@ export class SharedActions {
             if (pattern.indexOf("/") > -1) {
                 // USS file was selected
                 const filePath = pattern.substring(pattern.indexOf("/"));
-                const sessionNode = ussTree.mSessionNodes.find((sessNode) => sessNode.getProfileName() === sessionName);
+                const sessionNode = ussTree.mSessionNodes.find((sessNode) => sessNode.label.toString().toLowerCase() === sessionName.toLowerCase());
+                if (!sessionNode) {
+                    Gui.showMessage(vscode.l10n.t("Profile not found."));
path: packages/zowe-explorer/src/trees/shared/SharedActions.ts
user: login: t1m0thyj
body: In the case that the profile is not found, should we delete the invalid recent entry from the list? Perhaps we could also add a prompt to the message asking the user if they want to keep or delete the invalid entry.

Just an idea, not a request changes for this PR - we could address this as a future enhancement 😋 
created_at: 2025-03-13T14:56:03Z
author_association: MEMBER

PR comments:
user: login: JWaters02
created_at: 2025-02-21T20:21:13Z
body: Why did DCO check fail? I signed off my commit with my GPG key. You can see the verified tag on my commit... is it because the DCO CI checks only for the "signed off by email" and does not take into account GPG keys?

Update: Amended the commit by manually adding off the signed off by description and force pushed, seemed to work. 
author_association: MEMBER
user: login: codecov[bot]
created_at: 2025-02-21T20:22:13Z
body: ## [Codecov](https://app.codecov.io/gh/zowe/zowe-explorer-vscode/pull/3477?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zowe) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 93.50%. Comparing base [(`d448c98`)](https://app.codecov.io/gh/zowe/zowe-explorer-vscode/commit/d448c988015d967425f99d02c9f72e9255818e34?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zowe) to head [(`117e934`)](https://app.codecov.io/gh/zowe/zowe-explorer-vscode/commit/117e934d5d79ba46a24454fdc040c199a727182b?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zowe).
> Report is 1 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main    #3477   +/-   ##
=======================================
  Coverage   93.49%   93.50%           
=======================================
  Files         120      120           
  Lines       12892    12903   +11     
  Branches     2977     2981    +4     
=======================================
+ Hits        12054    12065   +11     
  Misses        837      837           
  Partials        1        1           
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/zowe/zowe-explorer-vscode/pull/3477?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zowe).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=zowe).

<details><summary>🚀 New features to boost your workflow: </summary>

- ❄ [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
- 📦 [JS Bundle Analysis](https://docs.codecov.com/docs/javascript-bundle-analysis): Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
</details>
author_association: NONE
user: login: traeok
created_at: 2025-02-27T14:53:45Z
body: Thanks for working on this @JWaters02 - I'm running into an issue where trying to use the "Open Recent Member" results in an error that the "Node does not exist. It may have been deleted":

![ZE-OpenRecentMember-Issue](https://github.com/user-attachments/assets/b0c68f99-d405-4346-98b3-6bce6165586b)
author_association: MEMBER
user: login: JWaters02
created_at: 2025-02-28T10:32:43Z
body: > Thanks for working on this @JWaters02 - I'm running into an issue where trying to use the "Open Recent Member" results in an error that the "Node does not exist. It may have been deleted":

Thanks I'll look into it


author_association: MEMBER
user: login: JWaters02
created_at: 2025-03-06T14:34:05Z
body: > I'm running into an issue where trying to use the "Open Recent Member" results in an error that the "Node does not exist. It may have been deleted":

This appears to be a seperate bug where the member being saved is only saving its name to the history, not its full PDS + member name like it used to be done in v2. 

![image](https://github.com/user-attachments/assets/8da88fc7-c8fa-4e1e-bec0-c3084d4ecf23)

I don't think this is technically within scope of this PR as this PR only fixes open recent member, while the bug is caused by the saving to history, which would probably be related to #3446 

But I'll look into it anyway as it does directly break this feature, and if it's an easy one I'll probably include it in this PR anyway.
author_association: MEMBER
user: login: JWaters02
created_at: 2025-03-06T14:51:13Z
body: Okay, fixed the bug in openDs. Therefore, this bug isn't related to #3446 because in that bug, openDs is not being called at all. So I will include this fix in this PR.
author_association: MEMBER
user: login: JWaters02
created_at: 2025-03-06T21:30:35Z
body: > This doesn't happen all the time, but seems to be after I open a file without selecting an encoding.

Yes, this is a bug reported in #3446 where I found that openDs is not being called when opening a file by just clicking on it, but opening it by opening with encoding does work as it explicitly calls the openDs function which adds the file to history.

> Also, maybe we can discuss this as a parking lot in the next ZE standup, but I do have some concerns about how we match against the session name (coercing using toLowerCase). Maybe this could be resolved separately since I could see this being "breaking" / involving more refactoring to address recent members that were already added in all caps.

Case sensitive profile names didn't occur to me because when stuff is added to the history they are converted into all-caps for some reason:
https://github.com/zowe/zowe-explorer-vscode/blob/8e57200bd2fab9fc7d364b2d8daca5d1a62a451e/packages/zowe-explorer/src/tools/ZowePersistentFilters.ts#L94-L97

To allow backwards compatibility with existing history, the check would always have to make sure the whole profile names are in the same case between history and current because the cases are not preserved... so yeah something worth to discuss next week, whether basically we want to maintain backwards compatibility with old history knowing this edge case exists (that two profiles with same names but different casings would be treated as the same and cause an issue), or do away with the backwards compatibility, or some other solution I haven't thought of.
author_association: MEMBER
user: login: traeok
created_at: 2025-03-06T22:07:50Z
body: > > This doesn't happen all the time, but seems to be after I open a file without selecting an encoding.
> 
> Yes, this is a bug reported in #3446 where I found that openDs is not being called when opening a file by just clicking on it, but opening it by opening with encoding does work as it explicitly calls the openDs function which adds the file to history.

Ah I see, thanks for clarifying on that - in the future, we'll need to refactor how we add the data set to file history. Ultimately, we want the node's command to stay as `vscode.open` so that a double-click operation opens the file and a single click opens the file in preview mode. When we used a different command on the node, the user experience was not great - errors would bubble up from VS Code when double clicking on files (which is still the case in ZE v2), and we had no way of accurately determining whether the user double-clicked the node, as our "open data set/USS" action would run twice 😬 not ideal...

I think we can leverage the `onDidOpenTextDocument` event to know when to add the resource to the file history. But I'm definitely not requesting that as part of this PR, just wanted to give context on why we moved away from calling a function to open nodes in the editor 😋 
author_association: MEMBER