diff --git a/src/project/WorkingSetView.js b/src/project/WorkingSetView.js index 5807d52649..a8a8631581 100644 --- a/src/project/WorkingSetView.js +++ b/src/project/WorkingSetView.js @@ -1000,27 +1000,43 @@ define(function (require, exports, module) { } }; + /** - * Adds full directory names to elements representing passed files in working tree + * adds the directory name to external project files + * when the directory length is more than 3, we show it in this format: `/.../` + * otherwise the full path * @private - * @param {Array.} filesPathList - list of fullPath strings + * @param {Array.} externalProjectFiles - the list of the external project files */ - WorkingSetView.prototype._addFullDirectoryNamesToWorkingTreeFiles = function (filesPathList) { - // filesList must have at least two files in it for this to make sense - if (!filesPathList.length) { + WorkingSetView.prototype._addDirectoryNameToExternalProjectFiles = function (externalProjectFiles) { + if(!externalProjectFiles.length) { return; } - // Go through open files and add directories to appropriate entries this.$openFilesContainer.find("ul > li").each(function () { const $li = $(this); let filePath = $li.data(_FILE_KEY).fullPath; - const io = filesPathList.indexOf(filePath); + const io = externalProjectFiles.indexOf(filePath); if (io !== -1) { let dirPath = path.dirname(filePath); - dirPath = Phoenix.app.getDisplayPath(dirPath); - const $dir = $(``) - .html(" — " + dirPath); + // this will be displayed on hover + const displayPath = Phoenix.app.getDisplayPath(dirPath); + + // use the platform specific separator for splitting + const separator = brackets.platform === "win" ? "\\" : "/"; + let dirSplit = displayPath.split(separator); + + let truncatedPath = displayPath; // truncatedPath value will be shown in the UI + if (dirSplit.length > 3) { + // dirSplit[0] maybe empty sometimes: + // - In browsers, for paths starting with "/" (e.g., "/fs/path/to/file") + // - In desktop app, for absolute paths on Linux/Mac (e.g., "/root/fs/path/to/file") + let rootDirName = dirSplit[0] ? dirSplit[0] : dirSplit[1]; + truncatedPath = rootDirName + separator + "\u2026" + separator + dirSplit[dirSplit.length - 1]; + } + + const $dir = $(``) + .html(" — " + truncatedPath); $li.children("a").append($dir); } }); @@ -1084,7 +1100,7 @@ define(function (require, exports, module) { } }); - self._addFullDirectoryNamesToWorkingTreeFiles(externalProjectFiles); + self._addDirectoryNameToExternalProjectFiles(externalProjectFiles); // Go through the map and solve the arrays with length over 1. Ignore the rest. _.forEach(map, function (value) { diff --git a/test/spec/WorkingSetView-integ-test.js b/test/spec/WorkingSetView-integ-test.js index 564daa0a9f..d3c4e91a14 100644 --- a/test/spec/WorkingSetView-integ-test.js +++ b/test/spec/WorkingSetView-integ-test.js @@ -226,30 +226,117 @@ define(function (require, exports, module) { expect($list.find(".directory").length).toBe(0); }); - it("should show full path next to the file name when file is outside current project", async function () { - // Count currently opened files + it("should show directory path for external files", async function () { var workingSetListItemCountBeforeTest = workingSetListItemCount; - // First we need to open another file + // Open an external file (outside current project) await openAndMakeDirty(externalProjectTestPath + "/test.js"); // Wait for file to be added to the working set await awaitsFor(function () { return workingSetListItemCount === workingSetListItemCountBeforeTest + 1; }); - // Two files with the same name file_one.js should be now opened var $list = testWindow.$(".open-files-container > ul"); - const fullPathSpan = $list.find(".directory"); - expect(fullPathSpan.length).toBe(1); - expect(fullPathSpan[0].innerHTML.includes(Phoenix.app.getDisplayPath(externalProjectTestPath))).toBe(true); + const directorySpan = $list.find(".directory"); - // Now close last opened file to hide the directories again - DocumentManager.getCurrentDocument()._markClean(); // so we can close without a save dialog - await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE), "timeout on FILE_CLOSE", 1000); + // Should show directory information for external files + expect(directorySpan.length).toBe(1); - // there should be no more directories shown + // Verify the title attribute contains the full display path + const fullDisplayPath = directorySpan.attr('title'); + expect(fullDisplayPath).toBe(testWindow.Phoenix.app.getDisplayPath(externalProjectTestPath)); + + // Verify the displayed text contains the expected format + const displayedText = directorySpan.text(); + expect(displayedText.length).toBeGreaterThan(0); + expect(displayedText.includes(" — ")).toBe(true); // Should have the separator + + // Clean up + DocumentManager.getCurrentDocument()._markClean(); + await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE), "timeout on FILE_CLOSE", 1000); expect($list.find(".directory").length).toBe(0); }); + it("should truncate very long external file paths with ellipsis", async function () { + // Create platform-appropriate test paths + var isWindows = testWindow.brackets.platform === "win"; + var isMac = testWindow.brackets.platform === "mac"; + var separator = isWindows ? "\\" : "/"; + var isNativeApp = testWindow.Phoenix.isNativeApp; + + // Define expected path formats for different environments + var mockLongPath, expectedTruncatedPath; + + if (isNativeApp) { + // Desktop app path formats + if (isWindows) { + mockLongPath = "C:\\Users\\TestUser\\Documents\\Very\\Long\\Project\\Structure\\Directory"; + expectedTruncatedPath = "C:" + separator + "…" + separator + "Directory"; + } else if (isMac) { + mockLongPath = "/Users/TestUser/Documents/Very/Long/Project/Structure/Directory"; + expectedTruncatedPath = "Users" + separator + "…" + separator + "Directory"; + } else { // Linux + mockLongPath = "/home/TestUser/Documents/Very/Long/Project/Structure/Directory"; + expectedTruncatedPath = "home" + separator + "…" + separator + "Directory"; + } + } else { + // Browser path formats + if (isWindows) { + // For default/internal projects in browser + mockLongPath = "/fs/Users/TestUser/Documents/Very/Long/Project/Structure/Directory"; + expectedTruncatedPath = "fs" + separator + "…" + separator + "Directory"; + } else { + // For Mac/Linux in browser + mockLongPath = "/fs/home/TestUser/Documents/Very/Long/Project/Structure/Directory"; + expectedTruncatedPath = "fs" + separator + "…" + separator + "Directory"; + } + } + + // Now test the actual WorkingSetView implementation with a guaranteed long path + var workingSetListItemCountBeforeTest = workingSetListItemCount; + + // Mock Phoenix.app.getDisplayPath to return our test path + var originalGetDisplayPath = testWindow.Phoenix.app.getDisplayPath; + testWindow.Phoenix.app.getDisplayPath = function(path) { + if (path.includes(externalProjectTestPath)) { + return mockLongPath; + } + return originalGetDisplayPath.call(this, path); + }; + + try { + await openAndMakeDirty(externalProjectTestPath + "/test.js"); + + // Wait for file to be added to the working set + await awaitsFor(function () { return workingSetListItemCount === workingSetListItemCountBeforeTest + 1; }); + + var $list = testWindow.$(".open-files-container > ul"); + const directorySpan = $list.find(".directory"); + expect(directorySpan.length).toBe(1); + + // Verify the title attribute contains the full display path + const fullDisplayPath = directorySpan.attr('title'); + expect(fullDisplayPath).toBe(mockLongPath); + + // Get the displayed text + const displayedText = directorySpan.text(); + + // This is the critical test - if truncation is working, it MUST contain ellipsis + expect(displayedText.includes("…")).toBe(true); + + // Should match our expected truncated path format + expect(displayedText.includes(expectedTruncatedPath)).toBe(true); + + // Clean up + DocumentManager.getCurrentDocument()._markClean(); + await awaitsForDone(CommandManager.execute(Commands.FILE_CLOSE), "timeout on FILE_CLOSE", 1000); + expect($list.find(".directory").length).toBe(0); + + } finally { + // Restore original function + testWindow.Phoenix.app.getDisplayPath = originalGetDisplayPath; + } + }); + it("should show different directory names, when two files of the same name are opened, located in folders with same name", async function () { // Count currently opened files var workingSetListItemCountBeforeTest = workingSetListItemCount;