From 1786787e40796e3d86c0b54428eee058daa28a27 Mon Sep 17 00:00:00 2001 From: Pluto Date: Thu, 10 Apr 2025 19:17:02 +0530 Subject: [PATCH 1/5] feat: add placeholder tabs when file is selected from file tree --- src/extensionsIntegrated/TabBar/main.js | 99 +++++++++++++++++-------- src/styles/Extn-TabBar.less | 39 ++++++++++ 2 files changed, 106 insertions(+), 32 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/main.js b/src/extensionsIntegrated/TabBar/main.js index b1a778f93b..0d21bdeb75 100644 --- a/src/extensionsIntegrated/TabBar/main.js +++ b/src/extensionsIntegrated/TabBar/main.js @@ -23,7 +23,6 @@ define(function (require, exports, module) { const _ = require("thirdparty/lodash"); const AppInit = require("utils/AppInit"); const MainViewManager = require("view/MainViewManager"); - const EditorManager = require("editor/EditorManager"); const FileSystem = require("filesystem/FileSystem"); const PreferencesManager = require("preferences/PreferencesManager"); const CommandManager = require("command/CommandManager"); @@ -135,16 +134,20 @@ define(function (require, exports, module) { const isPaneActive = (paneId === currentActivePane); const isDirty = Helper._isFileModified(FileSystem.getFileForPath(entry.path)); + const isPlaceholder = entry.isPlaceholder === true; - // create tab with active class + // create tab with all the appropriate classes const $tab = $( `
-
-
-
-
` + ${isActive ? 'active' : ''} + ${isDirty ? 'dirty' : ''} + ${isPlaceholder ? 'placeholder' : ''}" + data-path="${entry.path}" + title="${entry.path}"> +
+
+
+ ` ); // Add the file icon @@ -228,6 +231,35 @@ define(function (require, exports, module) { createTabBar(); } + // Check for active files not in working set in any pane + const activePane = MainViewManager.getActivePaneId(); + const firstPaneFile = MainViewManager.getCurrentlyViewedFile("first-pane"); + const secondPaneFile = MainViewManager.getCurrentlyViewedFile("second-pane"); + + // when a file is opened from the filetree and is not present in the working set, then it is a placeholder + let firstPanePlaceholder = null; + let secondPanePlaceholder = null; + + // Check if active file in first pane is not in the working set + if (firstPaneFile && + !Global.firstPaneWorkingSet.some(entry => entry.path === firstPaneFile.fullPath)) { + firstPanePlaceholder = { + path: firstPaneFile.fullPath, + name: firstPaneFile.name, + isPlaceholder: true + }; + } + + // Check if active file in second pane is not in the working set + if (secondPaneFile && + !Global.secondPaneWorkingSet.some(entry => entry.path === secondPaneFile.fullPath)) { + secondPanePlaceholder = { + path: secondPaneFile.fullPath, + name: secondPaneFile.name, + isPlaceholder: true + }; + } + if (Global.secondPaneWorkingSet.length === 1 && (!$('#phoenix-tab-bar-2').length || $('#phoenix-tab-bar-2').is(':hidden'))) { createTabBar(); @@ -237,7 +269,7 @@ define(function (require, exports, module) { // Update first pane's tabs if ($firstTabBar.length) { $firstTabBar.empty(); - if (Global.firstPaneWorkingSet.length > 0) { + if (Global.firstPaneWorkingSet.length > 0 || firstPanePlaceholder) { // get the count of tabs that we want to display in the tab bar (from preference settings) // from preference settings or working set whichever smaller @@ -251,17 +283,15 @@ define(function (require, exports, module) { let displayedEntries = Global.firstPaneWorkingSet.slice(0, tabsCountP1); - const activeEditor = EditorManager.getActiveEditor(); - const activePath = activeEditor ? activeEditor.document.file.fullPath : null; - if (activePath && !displayedEntries.some(entry => entry.path === activePath)) { - let activeEntry = Global.firstPaneWorkingSet.find(entry => entry.path === activePath); - if (activeEntry) { - displayedEntries[displayedEntries.length - 1] = activeEntry; - } - } + // Add working set tabs displayedEntries.forEach(function (entry) { $firstTabBar.append(createTab(entry, "first-pane")); }); + + // Add placeholder tab if needed + if (firstPanePlaceholder) { + $firstTabBar.append(createTab(firstPanePlaceholder, "first-pane")); + } } } @@ -269,7 +299,7 @@ define(function (require, exports, module) { // Update second pane's tabs if ($secondTabBar.length) { $secondTabBar.empty(); - if (Global.secondPaneWorkingSet.length > 0) { + if (Global.secondPaneWorkingSet.length > 0 || secondPanePlaceholder) { let tabsCountP2 = Math.min(Global.secondPaneWorkingSet.length, Preference.tabBarNumberOfTabs); if (Preference.tabBarNumberOfTabs < 0) { @@ -277,31 +307,28 @@ define(function (require, exports, module) { } let displayedEntries2 = Global.secondPaneWorkingSet.slice(0, tabsCountP2); - const activeEditor = EditorManager.getActiveEditor(); - const activePath = activeEditor ? activeEditor.document.file.fullPath : null; - if (activePath && !displayedEntries2.some(entry => entry.path === activePath)) { - let activeEntry = Global.secondPaneWorkingSet.find(entry => entry.path === activePath); - if (activeEntry) { - displayedEntries2[displayedEntries2.length - 1] = activeEntry; - } - } + + // Add working set tabs displayedEntries2.forEach(function (entry) { $secondTabBar.append(createTab(entry, "second-pane")); }); + + // Add placeholder tab if needed + if (secondPanePlaceholder) { + $secondTabBar.append(createTab(secondPanePlaceholder, "second-pane")); + } } } - // if no files are present in a pane, we want to hide the tab bar for that pane - if (Global.firstPaneWorkingSet.length === 0 && ($('#phoenix-tab-bar'))) { + // if no files are present in a pane and no placeholder, we want to hide the tab bar for that pane + if (Global.firstPaneWorkingSet.length === 0 && !firstPanePlaceholder && ($('#phoenix-tab-bar'))) { Helper._hideTabBar($('#phoenix-tab-bar'), $('#overflow-button')); } - if (Global.secondPaneWorkingSet.length === 0 && ($('#phoenix-tab-bar-2'))) { + if (Global.secondPaneWorkingSet.length === 0 && !secondPanePlaceholder && ($('#phoenix-tab-bar-2'))) { Helper._hideTabBar($('#phoenix-tab-bar-2'), $('#overflow-button-2')); } - const activePane = MainViewManager.getActivePaneId(); - // Now that tabs are updated, scroll to the active tab if necessary. if ($firstTabBar.length) { Overflow.toggleOverflowVisibility("first-pane"); @@ -396,7 +423,15 @@ define(function (require, exports, module) { const currentActivePane = MainViewManager.getActivePaneId(); const isPaneActive = (paneId === currentActivePane); const currentFile = MainViewManager.getCurrentlyViewedFile(currentActivePane); - if(isPaneActive && currentFile && currentFile.fullPath === filePath) { + + // Check if this is a placeholder tab + if ($(this).hasClass('placeholder')) { + // Add the file to the working set when placeholder tab is clicked + const fileObj = FileSystem.getFileForPath(filePath); + MainViewManager.addToWorkingSet(paneId, fileObj); + } + + if (isPaneActive && currentFile && currentFile.fullPath === filePath) { return; } CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath, paneId: paneId }); diff --git a/src/styles/Extn-TabBar.less b/src/styles/Extn-TabBar.less index 2087ab7a2a..dc2910897e 100644 --- a/src/styles/Extn-TabBar.less +++ b/src/styles/Extn-TabBar.less @@ -240,6 +240,45 @@ background-color: rgba(255, 255, 255, 0.1); } +.tab.placeholder .tab-name { + font-style: italic; + color: #888; +} + +.dark .tab.placeholder .tab-name { + color: #888; +} + +.tab.placeholder.active .tab-name { + color: #666; +} + +.dark .tab.placeholder.active .tab-name { + color: #aaa; +} + +.tab.placeholder::after { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 0.12rem; + background-color: #b4b2b2; +} + +.dark .tab.placeholder::after { + background-color: #666; +} + +.tab.placeholder.active::after { + background-color: #0078D7; +} + +.dark .tab.placeholder.active::after { + background-color: #75BEFF; +} + .tab.dragging { opacity: 0.7; transform: scale(0.95); From 48beab55bc9e458f17aa3258fbb808484e20f1dc Mon Sep 17 00:00:00 2001 From: Pluto Date: Thu, 10 Apr 2025 19:43:49 +0530 Subject: [PATCH 2/5] fix: tab bar not appearing when only a placeholder tab is present --- src/extensionsIntegrated/TabBar/main.js | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/main.js b/src/extensionsIntegrated/TabBar/main.js index 0d21bdeb75..8f96a62eaa 100644 --- a/src/extensionsIntegrated/TabBar/main.js +++ b/src/extensionsIntegrated/TabBar/main.js @@ -221,16 +221,6 @@ define(function (require, exports, module) { // Get all files from the working set. refer to `global.js` getAllFilesFromWorkingSet(); - // When there is only one file, we enforce the creation of the tab bar - // this is done because, given the situation: - // In a vertical split, when no files are present in 'second-pane' so the tab bar is hidden. - // Now, when the user adds a file in 'second-pane', the tab bar should be shown but since updateTabs() only, - // updates the tabs, so the tab bar never gets created. - if (Global.firstPaneWorkingSet.length === 1 && - (!$('#phoenix-tab-bar').length || $('#phoenix-tab-bar').is(':hidden'))) { - createTabBar(); - } - // Check for active files not in working set in any pane const activePane = MainViewManager.getActivePaneId(); const firstPaneFile = MainViewManager.getCurrentlyViewedFile("first-pane"); @@ -260,7 +250,13 @@ define(function (require, exports, module) { }; } - if (Global.secondPaneWorkingSet.length === 1 && + // create the tab bar if there's a placeholder or a file in the working set + if ((Global.firstPaneWorkingSet.length > 0 || firstPanePlaceholder) && + (!$('#phoenix-tab-bar').length || $('#phoenix-tab-bar').is(':hidden'))) { + createTabBar(); + } + + if ((Global.secondPaneWorkingSet.length > 0 || secondPanePlaceholder) && (!$('#phoenix-tab-bar-2').length || $('#phoenix-tab-bar-2').is(':hidden'))) { createTabBar(); } From edb88ad0841f79078c9ffb9c37e447da64ea9faf Mon Sep 17 00:00:00 2001 From: Pluto Date: Thu, 10 Apr 2025 20:26:30 +0530 Subject: [PATCH 3/5] fix: add directory name in placeholder tab when tab with same name already exists --- src/extensionsIntegrated/TabBar/main.js | 43 +++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/main.js b/src/extensionsIntegrated/TabBar/main.js index 8f96a62eaa..fb4d14df9f 100644 --- a/src/extensionsIntegrated/TabBar/main.js +++ b/src/extensionsIntegrated/TabBar/main.js @@ -25,6 +25,7 @@ define(function (require, exports, module) { const MainViewManager = require("view/MainViewManager"); const FileSystem = require("filesystem/FileSystem"); const PreferencesManager = require("preferences/PreferencesManager"); + const FileUtils = require("file/FileUtils"); const CommandManager = require("command/CommandManager"); const Commands = require("command/Commands"); const DocumentManager = require("document/DocumentManager"); @@ -236,7 +237,8 @@ define(function (require, exports, module) { firstPanePlaceholder = { path: firstPaneFile.fullPath, name: firstPaneFile.name, - isPlaceholder: true + isPlaceholder: true, + displayName: firstPaneFile.name // for now we initialize with name, will check for duplicates later }; } @@ -246,11 +248,46 @@ define(function (require, exports, module) { secondPanePlaceholder = { path: secondPaneFile.fullPath, name: secondPaneFile.name, - isPlaceholder: true + isPlaceholder: true, + displayName: secondPaneFile.name }; } - // create the tab bar if there's a placeholder or a file in the working set + // check for duplicate file names between placeholder tabs and working set entries + if (firstPanePlaceholder) { + // if any working set file has the same name as the placeholder + const hasDuplicate = Global.firstPaneWorkingSet.some(entry => + entry.name === firstPanePlaceholder.name + ); + + if (hasDuplicate) { + // extract directory name from path + const path = firstPanePlaceholder.path; + const parentDir = FileUtils.getDirectoryPath(path); + const dirParts = parentDir.split("/"); + const parentDirName = dirParts[dirParts.length - 2] || ""; + + // Update displayName with directory + firstPanePlaceholder.displayName = parentDirName + "/" + firstPanePlaceholder.name; + } + } + + if (secondPanePlaceholder) { + const hasDuplicate = Global.secondPaneWorkingSet.some(entry => + entry.name === secondPanePlaceholder.name + ); + + if (hasDuplicate) { + const path = secondPanePlaceholder.path; + const parentDir = FileUtils.getDirectoryPath(path); + const dirParts = parentDir.split("/"); + const parentDirName = dirParts[dirParts.length - 2] || ""; + + secondPanePlaceholder.displayName = parentDirName + "/" + secondPanePlaceholder.name; + } + } + + // Create tab bar if there's a placeholder or a file in the working set if ((Global.firstPaneWorkingSet.length > 0 || firstPanePlaceholder) && (!$('#phoenix-tab-bar').length || $('#phoenix-tab-bar').is(':hidden'))) { createTabBar(); From 16a51a12add74c4336aaed5ab4ef72e2a1fa36c5 Mon Sep 17 00:00:00 2001 From: Pluto Date: Thu, 10 Apr 2025 23:25:12 +0530 Subject: [PATCH 4/5] fix: scroll to active tab not working for placeholder tabs --- src/extensionsIntegrated/TabBar/overflow.js | 30 +++++++++++++++------ src/styles/Extn-TabBar.less | 5 ++++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/extensionsIntegrated/TabBar/overflow.js b/src/extensionsIntegrated/TabBar/overflow.js index 317989e877..169e6e63fd 100644 --- a/src/extensionsIntegrated/TabBar/overflow.js +++ b/src/extensionsIntegrated/TabBar/overflow.js @@ -75,6 +75,7 @@ define(function (require, exports, module) { name: $tab.find('.tab-name').text(), isActive: $tab.hasClass('active'), isDirty: $tab.hasClass('dirty'), + isPlaceholder: $tab.hasClass('placeholder'), $icon: $tab.find('.tab-icon').clone() }; @@ -148,17 +149,22 @@ define(function (require, exports, module) { `; + // add placeholder class to style it differently + const placeholderClass = item.isPlaceholder ? ' placeholder-name' : ''; + // return html for this item return { html: - ``, + ``, enabled: true }; }); @@ -223,6 +229,14 @@ define(function (require, exports, module) { // Set the active pane and open the file MainViewManager.setActivePaneId(paneId); CommandManager.execute(Commands.FILE_OPEN, { fullPath: filePath }); + + // get the tab bar element based on paneId and scroll to the active tab + // we use setTimeout to ensure that the DOM has updated after the file open command + setTimeout(function () { + const $tabBarElement = paneId === "first-pane" ? + $("#phoenix-tab-bar") : $("#phoenix-tab-bar-2"); + scrollToActiveTab($tabBarElement); + }, 100); } }); diff --git a/src/styles/Extn-TabBar.less b/src/styles/Extn-TabBar.less index dc2910897e..fb4288f541 100644 --- a/src/styles/Extn-TabBar.less +++ b/src/styles/Extn-TabBar.less @@ -403,4 +403,9 @@ .empty-pane-drop-target { border: 2px dashed #6db6ff !important; +} + +.dropdown-tab-item.placeholder-item .tab-name-container, +.tab-name-container.placeholder-name { + font-style: italic; } \ No newline at end of file From 3441c7b3c19586f45468ca6574fe068defdd19cf Mon Sep 17 00:00:00 2001 From: Pluto Date: Thu, 10 Apr 2025 23:36:33 +0530 Subject: [PATCH 5/5] fix: active tab styling for placeholder tabs in split pane --- src/extensionsIntegrated/TabBar/main.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/extensionsIntegrated/TabBar/main.js b/src/extensionsIntegrated/TabBar/main.js index fb4d14df9f..eaec425696 100644 --- a/src/extensionsIntegrated/TabBar/main.js +++ b/src/extensionsIntegrated/TabBar/main.js @@ -177,6 +177,13 @@ define(function (require, exports, module) { $tab.addClass('active-in-inactive-pane'); } + // if this is a placeholder tab in inactive pane, we need to use the brown styling + // instead of the blue one for active tabs + if (isPlaceholder && isActive && !isPaneActive) { + $tab.removeClass('active'); + $tab.addClass('active-in-inactive-pane'); + } + return $tab; }