Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/API-Reference/command/Commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ Closes all open files
## FILE\_CLOSE\_LIST
Closes files from list

**Kind**: global variable
<a name="FILE_PIN"></a>

## FILE\_PIN
Pins the selected file

**Kind**: global variable
<a name="FILE_UNPIN"></a>

## FILE\_UNPIN
Unpins the selected file

**Kind**: global variable
<a name="FILE_REOPEN_CLOSED"></a>

Expand Down
42 changes: 42 additions & 0 deletions docs/API-Reference/view/MainViewManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,48 @@ Adds the given file list to the end of the workingset.
Switch between panes

**Kind**: global function
<a name="pinFile"></a>

## pinFile(paneId, file) ⇒ <code>boolean</code>
This function is called from DocumentCommandHandlers.js handleFilePin function
it is to pin the file. the pinned file is displayed before the normal files in the working set and tab bar
we also prevent the pinned files from being closed during bulk close operations

**Kind**: global function
**Returns**: <code>boolean</code> - true if the file was pinned, false if it was already pinned or not in the working set

| Param | Type | Description |
| --- | --- | --- |
| paneId | <code>string</code> | the id of the pane in which to pin the file or ACTIVE_PANE |
| file | <code>File</code> | the File to pin |

<a name="unpinFile"></a>

## unpinFile(paneId, file) ⇒ <code>boolean</code>
called from DocumentCommandHandlers.js handleFileUnpin function,
to unpin the file

**Kind**: global function
**Returns**: <code>boolean</code> - true if the file was unpinned, false if it wasn't pinned

| Param | Type | Description |
| --- | --- | --- |
| paneId | <code>string</code> | the id of the pane in which to unpin the file or ACTIVE_PANE |
| file | <code>File</code> | the File to unpin |

<a name="isPathPinned"></a>

## isPathPinned(paneId, path) ⇒ <code>boolean</code>
checks if a file path is pinned in the working set.

**Kind**: global function
**Returns**: <code>boolean</code> - true if the file is pinned

| Param | Type | Description |
| --- | --- | --- |
| paneId | <code>string</code> | the id of the pane in which to check or ACTIVE_PANE |
| path | <code>string</code> | the full path to check |

<a name="traverseToNextViewByMRU"></a>

## traverseToNextViewByMRU(direction) ⇒ <code>Object</code>
Expand Down
39 changes: 39 additions & 0 deletions docs/API-Reference/view/Pane.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ const Pane = brackets.getModule("view/Pane")
* [.ITEM_NOT_FOUND](#Pane+ITEM_NOT_FOUND)
* [.ITEM_FOUND_NO_SORT](#Pane+ITEM_FOUND_NO_SORT)
* [.ITEM_FOUND_NEEDS_SORT](#Pane+ITEM_FOUND_NEEDS_SORT)
* [.pinPath(path)](#Pane+pinPath) ⇒ <code>boolean</code>
* [.unpinPath(path)](#Pane+unpinPath) ⇒ <code>boolean</code>
* [.isPathPinned(path)](#Pane+isPathPinned) ⇒ <code>boolean</code>
* [.mergeFrom(other)](#Pane+mergeFrom)
* [.destroy()](#Pane+destroy)
* [.getViewList()](#Pane+getViewList) ⇒ <code>Array.&lt;File&gt;</code>
Expand Down Expand Up @@ -143,6 +146,42 @@ and the workingset needs to be resorted

**Kind**: instance constant of [<code>Pane</code>](#Pane)
**See**: [reorderItem](#Pane+reorderItem)
<a name="Pane+pinPath"></a>

### pane.pinPath(path) ⇒ <code>boolean</code>
this pins a file path

**Kind**: instance method of [<code>Pane</code>](#Pane)
**Returns**: <code>boolean</code> - true if the file was pinned, false if it was already pinned or not in the view list

| Param | Type | Description |
| --- | --- | --- |
| path | <code>string</code> | the full path of the file to pin |

<a name="Pane+unpinPath"></a>

### pane.unpinPath(path) ⇒ <code>boolean</code>
this unpins a file path.

**Kind**: instance method of [<code>Pane</code>](#Pane)
**Returns**: <code>boolean</code> - true if the file was unpinned, false if it wasn't pinned

| Param | Type | Description |
| --- | --- | --- |
| path | <code>string</code> | the full path of the file to unpin |

<a name="Pane+isPathPinned"></a>

### pane.isPathPinned(path) ⇒ <code>boolean</code>
checks if a file path is pinned

**Kind**: instance method of [<code>Pane</code>](#Pane)
**Returns**: <code>boolean</code> - true if the file is pinned false otherwise

| Param | Type | Description |
| --- | --- | --- |
| path | <code>string</code> | the full path of the file |

<a name="Pane+mergeFrom"></a>

### pane.mergeFrom(other)
Expand Down
6 changes: 6 additions & 0 deletions src/command/Commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ define(function (require, exports, module) {
/** Closes files from list */
exports.FILE_CLOSE_LIST = "file.close_list"; // DocumentCommandHandlers.js handleFileCloseList()

/** Pins the selected file */
exports.FILE_PIN = "file.pin"; // DocumentCommandHandlers.js handleFilePin()

/** Unpins the selected file */
exports.FILE_UNPIN = "file.unpin"; // DocumentCommandHandlers.js handleFileUnpin()

/** Reopens last closed file */
exports.FILE_REOPEN_CLOSED = "file.reopen_closed"; // DocumentCommandHandlers.js handleReopenClosed()

Expand Down
14 changes: 13 additions & 1 deletion src/command/DefaultMenus.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ define(function (require, exports, module) {
Menus = require("command/Menus"),
Strings = require("strings"),
MainViewManager = require("view/MainViewManager"),
CommandManager = require("command/CommandManager");
CommandManager = require("command/CommandManager"),
WorkingSetView = require("project/WorkingSetView");

/**
* Disables menu items present in items if enabled is true.
Expand Down Expand Up @@ -68,6 +69,14 @@ define(function (require, exports, module) {
Commands.NAVIGATE_OPEN_IN_DEFAULT_APP]);
});
}

// Pin/Unpin logic: to hide the option when its not applicable
const contextFile = WorkingSetView.getContext();
if (contextFile) {
const isPinned = MainViewManager.isPathPinned(MainViewManager.ACTIVE_PANE, contextFile.fullPath);
CommandManager.get(Commands.FILE_PIN).setEnabled(!isPinned);
CommandManager.get(Commands.FILE_UNPIN).setEnabled(isPinned);
}
}

const isBrowser = !Phoenix.isNativeApp;
Expand Down Expand Up @@ -320,6 +329,9 @@ define(function (require, exports, module) {
subMenu.addMenuItem(Commands.NAVIGATE_OPEN_IN_DEFAULT_APP);
}
workingset_cmenu.addMenuDivider();
workingset_cmenu.addMenuItem(Commands.FILE_PIN, null, null, null, { hideWhenCommandDisabled: true });
workingset_cmenu.addMenuItem(Commands.FILE_UNPIN, null, null, null, { hideWhenCommandDisabled: true });
workingset_cmenu.addMenuDivider();
workingset_cmenu.addMenuItem(Commands.FILE_COPY);
workingset_cmenu.addMenuItem(Commands.FILE_COPY_PATH);
workingset_cmenu.addMenuItem(Commands.FILE_DUPLICATE);
Expand Down
31 changes: 31 additions & 0 deletions src/document/DocumentCommandHandlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -2002,6 +2002,35 @@ define(function (require, exports, module) {
});
}

/**
* we call this function when the user selects the 'Pin' option from the context menu
* so we pin the file. the pinned file is displayed before the normal files in the working set and tab bar
* we also prevent the pinned files from being closed during bulk close operations
* @param {{file: File, paneId: string}} commandData - the file to pin and paneId,
* if unavailable we use the current file and active pane
*/
function handleFilePin(commandData = {}) {
const file = commandData.file || MainViewManager.getCurrentlyViewedFile();
const paneId = commandData.paneId || MainViewManager.ACTIVE_PANE;

if (file) {
MainViewManager.pinFile(paneId, file);
}
}

/**
* this unpins the file so it goes back to normal behavior in the working set and tab bar
* @param {{file: File, paneId: string}} commandData - read the JSDoc for handleFilePin
*/
function handleFileUnpin(commandData = {}) {
const file = commandData.file || MainViewManager.getCurrentlyViewedFile();
const paneId = commandData.paneId || MainViewManager.ACTIVE_PANE;

if (file) {
MainViewManager.unpinFile(paneId, file);
}
}

/** Show the selected sidebar (tree or workingset) item in Finder/Explorer */
function handleShowInOS() {
var entry = ProjectManager.getSelectedItem();
Expand Down Expand Up @@ -2380,6 +2409,8 @@ define(function (require, exports, module) {
CommandManager.register(Strings.CMD_FILE_SAVE_AS, Commands.FILE_SAVE_AS, handleFileSaveAs);
CommandManager.register(Strings.CMD_FILE_RENAME, Commands.FILE_RENAME, handleFileRename);
CommandManager.register(Strings.CMD_FILE_DELETE, Commands.FILE_DELETE, handleFileDelete);
CommandManager.register(Strings.CMD_FILE_PIN, Commands.FILE_PIN, handleFilePin);
CommandManager.register(Strings.CMD_FILE_UNPIN, Commands.FILE_UNPIN, handleFileUnpin);

// Close Commands
CommandManager.register(Strings.CMD_FILE_CLOSE, Commands.FILE_CLOSE, handleFileClose);
Expand Down
41 changes: 41 additions & 0 deletions src/extensionsIntegrated/TabBar/drag-drop.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ define(function (require, exports, module) {
const MainViewManager = require("view/MainViewManager");
const CommandManager = require("command/CommandManager");
const Commands = require("command/Commands");
const FileSystem = require("filesystem/FileSystem");

/**
* These variables track the drag and drop state of tabs
Expand Down Expand Up @@ -307,6 +308,46 @@ define(function (require, exports, module) {
newPosition--;
}
MainViewManager._moveWorkingSetItem(paneId, draggedIndex, newPosition);

// we check if the dragged file is pinned, cause if it is we might need to unpin it,
// if it is dropped after an unpinned file
const isDraggedFilePinned = MainViewManager.isPathPinned(paneId, draggedPath);

if (isDraggedFilePinned && newPosition > 0) {
const newWorkingSet = MainViewManager.getWorkingSet(paneId);
const prevFilePath = newWorkingSet[newPosition - 1].fullPath;

// if the prev file is not pinned, we unpin this file too!
if (!MainViewManager.isPathPinned(paneId, prevFilePath)) {
const fileObj = FileSystem.getFileForPath(draggedPath);

// we consciously enable the unpin command here, because if the tab is pinned,
// the unpin command will be disabled by default as the last context menu item
// and the FILE_UNPIN command will not execute
CommandManager.get(Commands.FILE_UNPIN).setEnabled(true);
CommandManager.execute(Commands.FILE_UNPIN, { file: fileObj, paneId: paneId });
}
}

// if the dragged file is not pinned, we check if it should be pinned,
// if it is dropped before a pinned file
if (!isDraggedFilePinned) {
const newWorkingSet = MainViewManager.getWorkingSet(paneId);

// check if there's a file after this one
if (newPosition < newWorkingSet.length - 1) {
const nextFilePath = newWorkingSet[newPosition + 1].fullPath;

// if the next file is pinned, we pin this file too!
if (MainViewManager.isPathPinned(paneId, nextFilePath)) {
const fileObj = FileSystem.getFileForPath(draggedPath);

// we consciously enable the pin command here, same reason as above
CommandManager.get(Commands.FILE_PIN).setEnabled(true);
CommandManager.execute(Commands.FILE_PIN, { file: fileObj, paneId: paneId });
}
}
}
}
}

Expand Down
25 changes: 23 additions & 2 deletions src/extensionsIntegrated/TabBar/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ define(function (require, exports, module) {

const isDirty = Helper._isFileModified(FileSystem.getFileForPath(entry.path));
const isPlaceholder = entry.isPlaceholder === true;
const isPinned = MainViewManager.isPathPinned(paneId, entry.path);

let gitStatus = ""; // this will be shown in the tooltip when a tab is hovered
let gitStatusClass = ""; // for styling
Expand Down Expand Up @@ -173,12 +174,15 @@ define(function (require, exports, module) {
${isActive ? "active" : ""}
${isDirty ? "dirty" : ""}
${isPlaceholder ? "placeholder" : ""}
${isPinned ? "pinned" : ""}
${gitStatusClass}"
data-path="${entry.path}"
title="${Phoenix.app.getDisplayPath(entry.path)}${gitStatus ? " (" + gitStatus + ")" : ""}">
<div class="tab-icon"></div>
<div class="tab-name"></div>
<div class="tab-close"><i class="fa-solid fa-times"></i></div>
${isPinned ?
'<div class="tab-pin"><i class="fa-solid fa-thumbtack"></i></div>' :
'<div class="tab-close"><i class="fa-solid fa-times"></i></div>'}
</div>`
);

Expand Down Expand Up @@ -514,6 +518,18 @@ define(function (require, exports, module) {

CommandManager.execute(Commands.FILE_CLOSE, { file: fileObj, paneId: paneId }); // close the file
}

// check if the clicked element is the pin icon, if yes then we need to unpin it
if ($(event.target).hasClass("fa-thumbtack") || $(event.target).closest(".tab-pin").length) {
event.preventDefault();
event.stopPropagation();

// we consciously enable the unpin command here, because if the tab is pinned,
// the unpin command will be disabled by default as the last context menu item
// and the user will not be able to unpin the tab
CommandManager.get(Commands.FILE_UNPIN).setEnabled(true);
CommandManager.execute(Commands.FILE_UNPIN, { file: fileObj, paneId: paneId });
}
});

// add listener for tab close button to show the tooltip along with the keybinding
Expand All @@ -538,6 +554,9 @@ define(function (require, exports, module) {
if ($(event.target).hasClass("fa-times") || $(event.target).closest(".tab-close").length) {
return;
}
if ($(event.target).hasClass("fa-thumbtack") || $(event.target).closest(".tab-pin").length) {
return;
}

// Get the file path from the data-path attribute of the parent tab
const filePath = $(this).attr("data-path");
Expand Down Expand Up @@ -655,7 +674,9 @@ define(function (require, exports, module) {
"workingSetAddList",
"workingSetRemoveList",
"workingSetUpdate",
"_workingSetDisableAutoSort"
"_workingSetDisableAutoSort",
"workingSetPinned",
"workingSetUnpinned"
];
MainViewManager.off(events.join(" "), updateTabs);
MainViewManager.on(events.join(" "), updateTabs);
Expand Down
24 changes: 24 additions & 0 deletions src/extensionsIntegrated/TabBar/more-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ define(function (require, exports, module) {
const CommandManager = require("command/CommandManager");
const Commands = require("command/Commands");
const FileSystem = require("filesystem/FileSystem");
const MainViewManager = require("view/MainViewManager");
const Menus = require("command/Menus");
const Strings = require("strings");

Expand All @@ -43,6 +44,22 @@ define(function (require, exports, module) {
// this is set inside the showMoreOptionsContextMenu. read that func for more details
let _currentTabContext = { filePath: null, paneId: null };

/**
* this function is called before the context menu is shown
* it updates the menu items based on the current tab context
* so we only show the relevant options
*/
function _updateMenuItems() {
// PIN/UNPIN logic
const isPinned = MainViewManager.isPathPinned(
_currentTabContext.paneId,
_currentTabContext.filePath
);

CommandManager.get(Commands.FILE_PIN).setEnabled(!isPinned);
CommandManager.get(Commands.FILE_UNPIN).setEnabled(isPinned);
}

// gets the working set (list of open files) for the given pane
function _getWorkingSet(paneId) {
return paneId === "first-pane" ? Global.firstPaneWorkingSet : Global.secondPaneWorkingSet;
Expand Down Expand Up @@ -151,11 +168,18 @@ define(function (require, exports, module) {
menu.addMenuItem(TABBAR_CLOSE_SAVED_TABS);
menu.addMenuItem(TABBAR_CLOSE_ALL);
menu.addMenuDivider();
menu.addMenuItem(Commands.FILE_PIN, null, null, null, { hideWhenCommandDisabled: true });
menu.addMenuItem(Commands.FILE_UNPIN, null, null, null, { hideWhenCommandDisabled: true });
menu.addMenuDivider();
menu.addMenuItem(Commands.FILE_RENAME);
menu.addMenuItem(Commands.FILE_DELETE);
menu.addMenuItem(Commands.NAVIGATE_SHOW_IN_FILE_TREE);
menu.addMenuDivider();
menu.addMenuItem(Commands.FILE_REOPEN_CLOSED);

// _updateMenuItems function disables the button which are not needed for the current tab
// and those items are then hidden by the menu system automatically because of the hideWhenCommandDisabled flag
menu.on("beforeContextMenuOpen", _updateMenuItems);
}

module.exports = {
Expand Down
2 changes: 2 additions & 0 deletions src/nls/root/strings.js
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,8 @@ define({
"CMD_PROJECT_SETTINGS": "Project Settings\u2026",
"CMD_FILE_RENAME": "Rename",
"CMD_FILE_DELETE": "Delete",
"CMD_FILE_PIN": "Pin",
"CMD_FILE_UNPIN": "Unpin",
"CMD_INSTALL_EXTENSION": "Install Extension\u2026",
"CMD_EXTENSION_MANAGER": "Extension Manager\u2026",
"CMD_FILE_REFRESH": "Refresh File Tree",
Expand Down
Loading
Loading