Skip to content

Commit f5a48c0

Browse files
committed
feat: setup file pinning architecture
1 parent 004e6b8 commit f5a48c0

3 files changed

Lines changed: 183 additions & 5 deletions

File tree

src/document/DocumentCommandHandlers.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2002,12 +2002,33 @@ define(function (require, exports, module) {
20022002
});
20032003
}
20042004

2005-
function handleFilePin() {
2006-
// @devansh: implement the pin functionality here...
2005+
/**
2006+
* we call this function when the user selects the 'Pin' option from the context menu
2007+
* so we pin the file. the pinned file is displayed before the normal files in the working set and tab bar
2008+
* we also prevent the pinned files from being closed during bulk close operations
2009+
* @param {{file: File, paneId: string}} commandData - the file to pin and paneId,
2010+
* if unavailable we use the current file and active pane
2011+
*/
2012+
function handleFilePin(commandData = {}) {
2013+
const file = commandData.file || MainViewManager.getCurrentlyViewedFile();
2014+
const paneId = commandData.paneId || MainViewManager.ACTIVE_PANE;
2015+
2016+
if (file) {
2017+
MainViewManager.pinFile(paneId, file);
2018+
}
20072019
}
20082020

2009-
function handleFileUnpin() {
2010-
// @devansh: implement the unpin functionality here...
2021+
/**
2022+
* this unpins the file so it goes back to normal behavior in the working set and tab bar
2023+
* @param {{file: File, paneId: string}} commandData - read the JSDoc for handleFilePin
2024+
*/
2025+
function handleFileUnpin(commandData = {}) {
2026+
const file = commandData.file || MainViewManager.getCurrentlyViewedFile();
2027+
const paneId = commandData.paneId || MainViewManager.ACTIVE_PANE;
2028+
2029+
if (file) {
2030+
MainViewManager.unpinFile(paneId, file);
2031+
}
20112032
}
20122033

20132034
/** Show the selected sidebar (tree or workingset) item in Finder/Explorer */

src/view/MainViewManager.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,6 +1001,68 @@ define(function (require, exports, module) {
10011001
exports.trigger("_workingSetDisableAutoSort", pane.id);
10021002
}
10031003

1004+
/**
1005+
* This function is called from DocumentCommandHandlers.js handleFilePin function
1006+
* it is to pin the file. the pinned file is displayed before the normal files in the working set and tab bar
1007+
* we also prevent the pinned files from being closed during bulk close operations
1008+
*
1009+
* @param {!string} paneId - the id of the pane in which to pin the file or ACTIVE_PANE
1010+
* @param {!File} file - the File to pin
1011+
* @return {boolean} true if the file was pinned, false if it was already pinned or not in the working set
1012+
*/
1013+
function pinFile(paneId, file) {
1014+
const pane = _getPane(paneId);
1015+
if (!pane) {
1016+
return false;
1017+
}
1018+
1019+
const result = pane.pinPath(file.fullPath);
1020+
if (result) {
1021+
exports.trigger("workingSetPinned", file, pane.id);
1022+
exports.trigger("workingSetSort", pane.id);
1023+
}
1024+
1025+
return result;
1026+
}
1027+
1028+
/**
1029+
* called from DocumentCommandHandlers.js handleFileUnpin function,
1030+
* to unpin the file
1031+
*
1032+
* @param {!string} paneId - the id of the pane in which to unpin the file or ACTIVE_PANE
1033+
* @param {!File} file - the File to unpin
1034+
* @return {boolean} true if the file was unpinned, false if it wasn't pinned
1035+
*/
1036+
function unpinFile(paneId, file) {
1037+
const pane = _getPane(paneId);
1038+
if (!pane) {
1039+
return false;
1040+
}
1041+
1042+
const result = pane.unpinPath(file.fullPath);
1043+
if (result) {
1044+
exports.trigger("workingSetUnpinned", file, pane.id);
1045+
}
1046+
1047+
return result;
1048+
}
1049+
1050+
/**
1051+
* checks if a file path is pinned in the working set.
1052+
*
1053+
* @param {!string} paneId - the id of the pane in which to check or ACTIVE_PANE
1054+
* @param {!string} path - the full path to check
1055+
* @return {boolean} true if the file is pinned
1056+
*/
1057+
function isPathPinned(paneId, path) {
1058+
const pane = _getPane(paneId);
1059+
if (!pane) {
1060+
return false;
1061+
}
1062+
1063+
return pane.isPathPinned(path);
1064+
}
1065+
10041066
/**
10051067
* Get the next or previous file in the MRU list.
10061068
* @param {!number} direction - Must be 1 or -1 to traverse forward or backward
@@ -1808,6 +1870,11 @@ define(function (require, exports, module) {
18081870
exports.getWorkingSetSize = getWorkingSetSize;
18091871
exports.getWorkingSet = getWorkingSet;
18101872

1873+
// Pin Management
1874+
exports.pinFile = pinFile;
1875+
exports.unpinFile = unpinFile;
1876+
exports.isPathPinned = isPathPinned;
1877+
18111878
// Pane state
18121879
exports.cacheScrollState = cacheScrollState;
18131880
exports.restoreAdjustedScrollState = restoreAdjustedScrollState;

src/view/Pane.js

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,82 @@ define(function (require, exports, module) {
515515
this._viewListAddedOrder = [];
516516
this._views = {};
517517
this._currentView = null;
518+
this._pinnedPaths = new Set();
518519
this.showInterstitial(true);
519520
};
520521

522+
/**
523+
* this pins a file path
524+
* @param {string} path - the full path of the file to pin
525+
* @return {boolean} true if the file was pinned, false if it was already pinned or not in the view list
526+
*/
527+
Pane.prototype.pinPath = function (path) {
528+
// check if already pinned, if it is then ignore
529+
if (this.isPathPinned(path)) {
530+
return false;
531+
}
532+
533+
// check if the file is present in the open view list, if not then also ignore
534+
const index = this.findInViewList(path);
535+
if (index === -1) {
536+
return false;
537+
}
538+
539+
// add the file path to the pinned paths set
540+
// we also reorder the view list as pinned files should appear before regular files
541+
this._pinnedPaths.add(path);
542+
this._reorderViewListForPinning();
543+
return true;
544+
};
545+
546+
/**
547+
* this unpins a file path.
548+
* @param {string} path - the full path of the file to unpin
549+
* @return {boolean} true if the file was unpinned, false if it wasn't pinned
550+
*/
551+
Pane.prototype.unpinPath = function (path) {
552+
// if the file is already unpinned, we just ignore
553+
if (!this.isPathPinned(path)) {
554+
return false;
555+
}
556+
557+
// we just remove the file path from the pinned paths set
558+
// here we don't need to reorder the view list, the file can stay in its current position
559+
this._pinnedPaths.delete(path);
560+
return true;
561+
};
562+
563+
/**
564+
* checks if a file path is pinned
565+
* @param {string} path - the full path of the file
566+
* @return {boolean} true if the file is pinned false otherwise
567+
*/
568+
Pane.prototype.isPathPinned = function (path) {
569+
return this._pinnedPaths.has(path);
570+
};
571+
572+
/**
573+
* this function is responsible to reorder the view list such that pinned files appear first
574+
* the pinned files and regular files both maintain their relative order
575+
* @private
576+
*/
577+
Pane.prototype._reorderViewListForPinning = function () {
578+
const self = this;
579+
580+
this._viewList.sort(function (a, b) {
581+
// just the regular comparison sorting logic based on whether the files are pinned or not
582+
const aPinned = self._pinnedPaths.has(a.fullPath);
583+
const bPinned = self._pinnedPaths.has(b.fullPath);
584+
if (aPinned && !bPinned) {
585+
return -1;
586+
}
587+
if (!aPinned && bPinned) {
588+
return 1;
589+
}
590+
return 0;
591+
});
592+
};
593+
521594
/**
522595
* Creates a pane event namespaced to this pane
523596
* (pass an empty string to generate just the namespace key to pass to jQuery to turn off all events handled by this pane)
@@ -948,6 +1021,8 @@ define(function (require, exports, module) {
9481021
this._viewList.splice(index, 1);
9491022
this._viewListMRUOrder.splice(this.findInViewListMRUOrder(file.fullPath), 1);
9501023
this._viewListAddedOrder.splice(this.findInViewListAddedOrder(file.fullPath), 1);
1024+
// also remove from pinned paths if it was pinned
1025+
this._pinnedPaths.delete(file.fullPath);
9511026
}
9521027

9531028
// Destroy the view
@@ -1514,6 +1589,7 @@ define(function (require, exports, module) {
15141589
Pane.prototype.loadState = function (state) {
15151590
var filesToAdd = [],
15161591
viewStates = {},
1592+
pinnedPaths = [],
15171593
activeFile,
15181594
data,
15191595
self = this;
@@ -1530,10 +1606,22 @@ define(function (require, exports, module) {
15301606
if (entry.viewState) {
15311607
viewStates[entry.file] = entry.viewState;
15321608
}
1609+
if (entry.pinned) {
1610+
pinnedPaths.push(entry.file);
1611+
}
15331612
});
15341613

15351614
this.addListToViewList(filesToAdd);
15361615

1616+
// restore pinned state
1617+
pinnedPaths.forEach(function (path) {
1618+
self._pinnedPaths.add(path);
1619+
});
1620+
// reorder to put pinned files first
1621+
if (pinnedPaths.length > 0) {
1622+
this._reorderViewListForPinning();
1623+
}
1624+
15371625
ViewStateManager.addViewStates(viewStates);
15381626

15391627
activeFile = activeFile || getInitialViewFilePath();
@@ -1551,6 +1639,7 @@ define(function (require, exports, module) {
15511639
*/
15521640
Pane.prototype.saveState = function () {
15531641
var result = [],
1642+
self = this,
15541643
currentlyViewedPath = this.getCurrentlyViewedPath();
15551644

15561645
// Save the current view state first
@@ -1570,7 +1659,8 @@ define(function (require, exports, module) {
15701659
result.push({
15711660
file: file.fullPath,
15721661
active: (file.fullPath === currentlyViewedPath),
1573-
viewState: ViewStateManager.getViewState(file)
1662+
viewState: ViewStateManager.getViewState(file),
1663+
pinned: self._pinnedPaths.has(file.fullPath)
15741664
});
15751665
}
15761666
});

0 commit comments

Comments
 (0)