atom-nuclide
Version:
A unified developer experience for web and mobile development, built as a suite of features on top of Atom to provide hackability and the support of an active community.
1,463 lines (1,288 loc) • 59.8 kB
JavaScript
Object.defineProperty(exports, '__esModule', {
value: true
});
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } }
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { var callNext = step.bind(null, 'next'); var callThrow = step.bind(null, 'throw'); function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(callNext, callThrow); } } callNext(); }); }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the license found in the LICENSE file in
* the root directory of this source tree.
*/
var _FileTreeDispatcher2;
function _FileTreeDispatcher() {
return _FileTreeDispatcher2 = _interopRequireDefault(require('./FileTreeDispatcher'));
}
var _FileTreeHelpers2;
function _FileTreeHelpers() {
return _FileTreeHelpers2 = _interopRequireDefault(require('./FileTreeHelpers'));
}
var _FileTreeHgHelpers2;
function _FileTreeHgHelpers() {
return _FileTreeHgHelpers2 = _interopRequireDefault(require('./FileTreeHgHelpers'));
}
var _FileTreeNode2;
function _FileTreeNode() {
return _FileTreeNode2 = require('./FileTreeNode');
}
var _immutable2;
function _immutable() {
return _immutable2 = _interopRequireDefault(require('immutable'));
}
var _FileTreeConstants2;
function _FileTreeConstants() {
return _FileTreeConstants2 = require('./FileTreeConstants');
}
var _atom2;
function _atom() {
return _atom2 = require('atom');
}
var _FileTreeFilterHelper2;
function _FileTreeFilterHelper() {
return _FileTreeFilterHelper2 = require('./FileTreeFilterHelper');
}
var _minimatch2;
function _minimatch() {
return _minimatch2 = require('minimatch');
}
var _nuclideHgGitBridge2;
function _nuclideHgGitBridge() {
return _nuclideHgGitBridge2 = require('../../nuclide-hg-git-bridge');
}
var _nuclideHgRpcLibHgConstants2;
function _nuclideHgRpcLibHgConstants() {
return _nuclideHgRpcLibHgConstants2 = require('../../nuclide-hg-rpc/lib/hg-constants');
}
var _nuclideLogging2;
function _nuclideLogging() {
return _nuclideLogging2 = require('../../nuclide-logging');
}
var _nuclideWorkingSetsCommon2;
function _nuclideWorkingSetsCommon() {
return _nuclideWorkingSetsCommon2 = require('../../nuclide-working-sets-common');
}
var _nuclideAnalytics2;
function _nuclideAnalytics() {
return _nuclideAnalytics2 = require('../../nuclide-analytics');
}
var _commonsNodeNuclideUri2;
function _commonsNodeNuclideUri() {
return _commonsNodeNuclideUri2 = _interopRequireDefault(require('../../commons-node/nuclideUri'));
}
// Used to ensure the version we serialized is the same version we are deserializing.
var VERSION = 1;
var DEFAULT_CONF = {
vcsStatuses: new (_immutable2 || _immutable()).default.Map(),
workingSet: new (_nuclideWorkingSetsCommon2 || _nuclideWorkingSetsCommon()).WorkingSet(),
editedWorkingSet: new (_nuclideWorkingSetsCommon2 || _nuclideWorkingSetsCommon()).WorkingSet(),
hideIgnoredNames: true,
excludeVcsIgnoredPaths: true,
ignoredPatterns: new (_immutable2 || _immutable()).default.Set(),
usePreviewTabs: false,
isEditingWorkingSet: false,
openFilesWorkingSet: new (_nuclideWorkingSetsCommon2 || _nuclideWorkingSetsCommon()).WorkingSet(),
reposByRoot: {}
};
var instance = undefined;
/**
* Implements the Flux pattern for our file tree. All state for the file tree will be kept in
* FileTreeStore and the only way to update the store is through methods on FileTreeActions. The
* dispatcher is a mechanism through which FileTreeActions interfaces with FileTreeStore.
*/
var FileTreeStore = (function () {
_createClass(FileTreeStore, null, [{
key: 'getInstance',
value: function getInstance() {
if (!instance) {
instance = new FileTreeStore();
}
return instance;
}
}, {
key: 'dispose',
value: function dispose() {
if (instance != null) {
instance.dispose();
}
instance = null;
}
}]);
function FileTreeStore() {
var _this = this;
_classCallCheck(this, FileTreeStore);
this.roots = new (_immutable2 || _immutable()).default.OrderedMap();
this._dispatcher = (_FileTreeDispatcher2 || _FileTreeDispatcher()).default.getInstance();
this._emitter = new (_atom2 || _atom()).Emitter();
this._dispatcher.register(function (payload) {
return _this._onDispatch(payload);
});
this._logger = (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)();
this._usePrefixNav = false;
this._isLoadingMap = new (_immutable2 || _immutable()).default.Map();
this._repositories = new (_immutable2 || _immutable()).default.Set();
this._conf = DEFAULT_CONF;
global.FTConf = this._conf;
this._suppressChanges = false;
this._filter = '';
this.openFilesExpanded = true;
}
/**
* Performs a breadth-first iteration over the directories of the tree starting
* with a given node. The iteration stops once a given limit of nodes (both directories
* and files) were traversed.
* The node being currently traversed can be obtained by calling .traversedNode()
* .next() returns a promise that is fulfilled when the traversal moves on to
* the next directory.
*/
/**
* TODO: Move to a [serialization class][1] and use the built-in versioning mechanism. This might
* need to be done one level higher within main.js.
*
* [1]: https://atom.io/docs/latest/behind-atom-serialization-in-atom
*/
_createClass(FileTreeStore, [{
key: 'exportData',
value: function exportData() {
var childKeyMap = {};
var expandedKeysByRoot = {};
var selectedKeysByRoot = {};
this.roots.forEach(function (root) {
var expandedKeys = [];
var selectedKeys = [];
// Grab the data of only the expanded portion of the tree.
root.traverse(function (node) {
if (node.isSelected) {
selectedKeys.push(node.uri);
}
if (!node.isExpanded) {
return false;
}
expandedKeys.push(node.uri);
if (!node.children.isEmpty()) {
childKeyMap[node.uri] = node.children.map(function (child) {
return child.uri;
}).toArray();
}
return true;
});
expandedKeysByRoot[root.uri] = expandedKeys;
selectedKeysByRoot[root.uri] = selectedKeys;
});
var rootKeys = this.roots.map(function (root) {
return root.uri;
}).toArray();
return {
version: VERSION,
childKeyMap: childKeyMap,
expandedKeysByRoot: expandedKeysByRoot,
rootKeys: rootKeys,
selectedKeysByRoot: selectedKeysByRoot,
openFilesExpanded: this.openFilesExpanded
};
}
/**
* Imports store data from a previous export.
*/
}, {
key: 'loadData',
value: function loadData(data) {
var _this2 = this;
// Ensure we are not trying to load data from an earlier version of this package.
if (data.version !== VERSION) {
return;
}
var buildNode = function buildNode(rootUri, uri) {
var rootExpandedKeys = data.expandedKeysByRoot[rootUri] || [];
var rootSelectedKeys = data.selectedKeysByRoot[rootUri] || [];
var childrenUris = data.childKeyMap[uri] || [];
var children = (_FileTreeNode2 || _FileTreeNode()).FileTreeNode.childrenFromArray(childrenUris.map(function (childUri) {
return buildNode(rootUri, childUri);
}));
var isExpanded = rootExpandedKeys.indexOf(uri) >= 0;
var isLoading = false;
if (isExpanded && (_FileTreeHelpers2 || _FileTreeHelpers()).default.isDirKey(uri)) {
_this2._fetchChildKeys(uri);
isLoading = true;
}
return new (_FileTreeNode2 || _FileTreeNode()).FileTreeNode({
uri: uri,
rootUri: rootUri,
isExpanded: isExpanded,
isSelected: rootSelectedKeys.indexOf(uri) >= 0,
isLoading: isLoading,
isTracked: false,
children: children,
isCwd: false,
connectionTitle: (_FileTreeHelpers2 || _FileTreeHelpers()).default.getDisplayTitle(rootUri) || ''
}, _this2._conf);
};
if (data.openFilesExpanded != null) {
this.openFilesExpanded = data.openFilesExpanded;
}
var normalizedAtomPaths = atom.project.getPaths().map((_commonsNodeNuclideUri2 || _commonsNodeNuclideUri()).default.ensureTrailingSeparator);
var normalizedDataPaths = data.rootKeys.map((_commonsNodeNuclideUri2 || _commonsNodeNuclideUri()).default.ensureTrailingSeparator).filter(function (rootUri) {
return (_commonsNodeNuclideUri2 || _commonsNodeNuclideUri()).default.isRemote(rootUri) || normalizedAtomPaths.indexOf(rootUri) >= 0;
});
var pathsMissingInData = normalizedAtomPaths.filter(function (rootUri) {
return normalizedDataPaths.indexOf(rootUri) === -1;
});
var combinedPaths = normalizedDataPaths.concat(pathsMissingInData);
this._setRoots(new (_immutable2 || _immutable()).default.OrderedMap(combinedPaths.map(function (rootUri) {
return [rootUri, buildNode(rootUri, rootUri)];
})));
}
}, {
key: '_setExcludeVcsIgnoredPaths',
value: function _setExcludeVcsIgnoredPaths(excludeVcsIgnoredPaths) {
this._updateConf(function (conf) {
conf.excludeVcsIgnoredPaths = excludeVcsIgnoredPaths;
});
}
}, {
key: '_setHideIgnoredNames',
value: function _setHideIgnoredNames(hideIgnoredNames) {
this._updateConf(function (conf) {
conf.hideIgnoredNames = hideIgnoredNames;
});
}
/**
* Given a list of names to ignore, compile them into minimatch patterns and
* update the store with them.
*/
}, {
key: '_setIgnoredNames',
value: function _setIgnoredNames(ignoredNames) {
var ignoredPatterns = (_immutable2 || _immutable()).default.Set(ignoredNames).map(function (ignoredName) {
if (ignoredName === '') {
return null;
}
try {
return new (_minimatch2 || _minimatch()).Minimatch(ignoredName, { matchBase: true, dot: true });
} catch (error) {
atom.notifications.addWarning('Error parsing pattern \'' + ignoredName + '\' from "Settings" > "Ignored Names"', { detail: error.message });
return null;
}
}).filter(function (pattern) {
return pattern != null;
});
this._updateConf(function (conf) {
conf.ignoredPatterns = ignoredPatterns;
});
}
}, {
key: '_onDispatch',
value: function _onDispatch(payload) {
switch (payload.actionType) {
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.DELETE_SELECTED_NODES:
this._deleteSelectedNodes();
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_CWD:
this._setCwdKey(payload.rootKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_TRACKED_NODE:
this._setTrackedNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.MOVE_TO_NODE:
this._moveToNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_ROOT_KEYS:
this._setRootKeys(payload.rootKeys);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.EXPAND_NODE:
this._expandNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.EXPAND_NODE_DEEP:
this._expandNodeDeep(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.COLLAPSE_NODE:
this._collapseNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_EXCLUDE_VCS_IGNORED_PATHS:
this._setExcludeVcsIgnoredPaths(payload.excludeVcsIgnoredPaths);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_USE_PREVIEW_TABS:
this._setUsePreviewTabs(payload.usePreviewTabs);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_USE_PREFIX_NAV:
this._setUsePrefixNav(payload.usePrefixNav);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.COLLAPSE_NODE_DEEP:
this._collapseNodeDeep(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_HIDE_IGNORED_NAMES:
this._setHideIgnoredNames(payload.hideIgnoredNames);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_IGNORED_NAMES:
this._setIgnoredNames(payload.ignoredNames);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_VCS_STATUSES:
this._setVcsStatuses(payload.rootKey, payload.vcsStatuses);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_REPOSITORIES:
this._setRepositories(payload.repositories);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_WORKING_SET:
this._setWorkingSet(payload.workingSet);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_OPEN_FILES_WORKING_SET:
this._setOpenFilesWorkingSet(payload.openFilesWorkingSet);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_WORKING_SETS_STORE:
this._setWorkingSetsStore(payload.workingSetsStore);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.START_EDITING_WORKING_SET:
this._startEditingWorkingSet(payload.editedWorkingSet);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.FINISH_EDITING_WORKING_SET:
this._finishEditingWorkingSet();
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.CHECK_NODE:
this._checkNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.UNCHECK_NODE:
this._uncheckNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_DRAG_HOVERED_NODE:
this._setDragHoveredNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.UNHOVER_NODE:
this._unhoverNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_SELECTED_NODE:
this._setSelectedNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_FOCUSED_NODE:
this._setFocusedNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.ADD_SELECTED_NODE:
this._addSelectedNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.UNSELECT_NODE:
this._unselectNode(payload.rootKey, payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.MOVE_SELECTION_UP:
this._moveSelectionUp();
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.MOVE_SELECTION_DOWN:
this._moveSelectionDown();
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.MOVE_SELECTION_TO_TOP:
this._moveSelectionToTop();
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.MOVE_SELECTION_TO_BOTTOM:
this._moveSelectionToBottom();
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.ENSURE_CHILD_NODE:
this._ensureChildNode(payload.nodeKey);
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.CLEAR_FILTER:
this.clearFilter();
break;
case (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_OPEN_FILES_EXPANDED:
this._setOpenFilesExpanded(payload.openFilesExpanded);
break;
}
}
/**
* Use the predicate function to update one or more of the roots in the file tree
*/
}, {
key: '_updateRoots',
value: function _updateRoots(predicate) {
this._setRoots(this.roots.map(predicate));
}
/**
* Use the predicate to update a node (or a branch) of the file-tree
*/
}, {
key: '_updateNodeAtRoot',
value: function _updateNodeAtRoot(rootKey, nodeKey, predicate) {
var root = this.roots.get(rootKey);
if (root == null) {
return;
}
var node = root.find(nodeKey);
if (node == null) {
return;
}
var roots = this.roots.set(rootKey, this._bubbleUp(node, predicate(node)));
this._setRoots(roots);
}
/**
* Update a node or a branch under any of the roots it was found at
*/
}, {
key: '_updateNodeAtAllRoots',
value: function _updateNodeAtAllRoots(nodeKey, predicate) {
var _this3 = this;
var roots = this.roots.map(function (root) {
var node = root.find(nodeKey);
if (node == null) {
return root;
}
return _this3._bubbleUp(node, predicate(node));
});
this._setRoots(roots);
}
/**
* Bubble the change up. The newNode is assumed to be prevNode after some manipulateion done to it
* therefore they are assumed to belong to the same parent.
*
* The method updates the child to the new node (which create a new parent instance) and call
* recursively for the parent update. Until there are no more parents and the new root is returned
*
* As the change bubbles up, and in addition to the change from the new child assignment, an
* optional predicate is also being applied to each newly created parent to support more complex
* change patterns.
*/
}, {
key: '_bubbleUp',
value: function _bubbleUp(prevNode, newNode) {
var postPredicate = arguments.length <= 2 || arguments[2] === undefined ? function (node) {
return node;
} : arguments[2];
var parent = prevNode.parent;
if (parent == null) {
return newNode;
}
var newParent = postPredicate(parent.updateChild(newNode));
return this._bubbleUp(parent, newParent, postPredicate);
}
/**
* Updates the roots, maintains their sibling relationships and fires the change event.
*/
}, {
key: '_setRoots',
value: function _setRoots(roots) {
var _this4 = this;
// Explicitly test for the empty case, otherwise configuration changes with an empty
// tree will not emit changes.
var changed = !(_immutable2 || _immutable()).default.is(roots, this.roots) || roots.isEmpty();
if (changed) {
(function () {
_this4.roots = roots;
var prevRoot = null;
roots.forEach(function (r) {
r.prevSibling = prevRoot;
if (prevRoot != null) {
prevRoot.nextSibling = r;
}
prevRoot = r;
});
if (prevRoot != null) {
prevRoot.nextSibling = null;
}
_this4._emitChange();
})();
}
}
}, {
key: '_emitChange',
value: function _emitChange() {
var _this5 = this;
if (this._suppressChanges) {
return;
}
if (this._animationFrameRequestId != null) {
window.cancelAnimationFrame(this._animationFrameRequestId);
}
this._animationFrameRequestId = window.requestAnimationFrame(function () {
var performance = global.performance;
var renderStart = performance.now();
var childrenCount = _this5.roots.reduce(function (sum, root) {
return sum + root.shownChildrenBelow;
}, 0);
_this5._emitter.emit('change');
_this5._suppressChanges = true;
_this5._checkTrackedNode();
_this5._suppressChanges = false;
_this5._animationFrameRequestId = null;
var duration = (performance.now() - renderStart).toString();
(0, (_nuclideAnalytics2 || _nuclideAnalytics()).track)('filetree-root-node-component-render', {
'filetree-root-node-component-render-duration': duration,
'filetree-root-node-component-rendered-child-count': childrenCount
});
});
}
/**
* Update the configuration for the file-tree. The direct writing to the this._conf should be
* avoided.
*/
}, {
key: '_updateConf',
value: function _updateConf(predicate) {
predicate(this._conf);
this._updateRoots(function (root) {
return root.updateConf().setRecursive(
// Remove selection from hidden nodes under this root
function (node) {
return node.containsSelection && node.containsHidden ? null : node;
}, function (node) {
if (node.shouldBeShown) {
return node;
}
// The node is hidden - unselect all nodes under it if there are any
return node.setRecursive(function (subNode) {
return subNode.containsSelection ? null : subNode;
}, function (subNode) {
return subNode.setIsSelected(false);
});
});
});
}
}, {
key: 'getTrackedNode',
value: function getTrackedNode() {
// Locate the root containing the tracked node efficiently by using the child-derived
// containsTrackedNode property
var trackedRoot = this.roots.find(function (root) {
return root.containsTrackedNode;
});
if (trackedRoot == null) {
return null;
}
var trackedNode = undefined;
// Likewise, within the root use the property to efficiently find the needed node
trackedRoot.traverse(function (node) {
if (node.isTracked) {
trackedNode = node;
}
return trackedNode == null && node.containsTrackedNode;
});
return trackedNode;
}
}, {
key: 'getRepositories',
value: function getRepositories() {
return this._repositories;
}
}, {
key: 'getWorkingSet',
value: function getWorkingSet() {
return this._conf.workingSet;
}
}, {
key: 'getWorkingSetsStore',
value: function getWorkingSetsStore() {
return this._workingSetsStore;
}
}, {
key: 'getRootKeys',
value: function getRootKeys() {
return this.roots.toArray().map(function (root) {
return root.uri;
});
}
/**
* Returns true if the store has no data, i.e. no roots, no children.
*/
}, {
key: 'isEmpty',
value: function isEmpty() {
return this.roots.isEmpty();
}
}, {
key: '_setVcsStatuses',
value: function _setVcsStatuses(rootKey, vcsStatuses) {
var _this6 = this;
// We can't build on the child-derived properties to maintain vcs statuses in the entire
// tree, since the reported VCS status may be for a node that is not yet present in the
// fetched tree, and so it it can't affect its parents statuses. To have the roots colored
// consistently we manually add all parents of all of the modified nodes up till the root
var enrichedVcsStatuses = _extends({}, vcsStatuses);
var ensurePresentParents = function ensurePresentParents(uri) {
if (uri === rootKey) {
return;
}
var current = uri;
while (current !== rootKey) {
current = (_FileTreeHelpers2 || _FileTreeHelpers()).default.getParentKey(current);
if (enrichedVcsStatuses[current] != null) {
return;
}
enrichedVcsStatuses[current] = (_nuclideHgRpcLibHgConstants2 || _nuclideHgRpcLibHgConstants()).StatusCodeNumber.MODIFIED;
}
};
Object.keys(vcsStatuses).forEach(function (uri) {
var status = vcsStatuses[uri];
if (status === (_nuclideHgRpcLibHgConstants2 || _nuclideHgRpcLibHgConstants()).StatusCodeNumber.MODIFIED || status === (_nuclideHgRpcLibHgConstants2 || _nuclideHgRpcLibHgConstants()).StatusCodeNumber.ADDED || status === (_nuclideHgRpcLibHgConstants2 || _nuclideHgRpcLibHgConstants()).StatusCodeNumber.REMOVED) {
try {
// An invalid URI might cause an exception to be thrown
ensurePresentParents(uri);
} catch (e) {
_this6._logger.error('Error enriching the VCS statuses for ' + uri, e);
}
}
});
if (this._vcsStatusesAreDifferent(rootKey, enrichedVcsStatuses)) {
this._updateConf(function (conf) {
conf.vcsStatuses = conf.vcsStatuses.set(rootKey, enrichedVcsStatuses);
});
}
}
}, {
key: '_vcsStatusesAreDifferent',
value: function _vcsStatusesAreDifferent(rootKey, newVcsStatuses) {
var currentStatuses = this._conf.vcsStatuses.get(rootKey);
if (currentStatuses == null || newVcsStatuses == null) {
if (currentStatuses !== newVcsStatuses) {
return true;
}
}
var currentKeys = Object.keys(currentStatuses);
var newKeys = Object.keys(newVcsStatuses);
if (currentKeys.length !== newKeys.length) {
return true;
}
return newKeys.some(function (key) {
return currentStatuses[key] !== newVcsStatuses[key];
});
}
}, {
key: '_setUsePreviewTabs',
value: function _setUsePreviewTabs(usePreviewTabs) {
this._updateConf(function (conf) {
conf.usePreviewTabs = usePreviewTabs;
});
}
}, {
key: '_setUsePrefixNav',
value: function _setUsePrefixNav(usePrefixNav) {
this._usePrefixNav = usePrefixNav;
}
}, {
key: 'usePrefixNav',
value: function usePrefixNav() {
return this._usePrefixNav;
}
/**
* The node child keys may either be available immediately (cached), or
* require an async fetch. If all of the children are needed it's easier to
* return as promise, to make the caller oblivious to the way children were
* fetched.
*/
}, {
key: 'promiseNodeChildKeys',
value: _asyncToGenerator(function* (rootKey, nodeKey) {
var shownChildrenUris = function shownChildrenUris(node) {
return node.children.toArray().filter(function (n) {
return n.shouldBeShown;
}).map(function (n) {
return n.uri;
});
};
var node = this.getNode(rootKey, nodeKey);
if (node == null) {
return [];
}
if (!node.isLoading) {
return shownChildrenUris(node);
}
yield this._fetchChildKeys(nodeKey);
return this.promiseNodeChildKeys(rootKey, nodeKey);
})
/**
* Uses the .containsSelection child-derived property to efficiently build the list of the
* currently selected nodes
*/
}, {
key: 'getSelectedNodes',
value: function getSelectedNodes() {
var selectedNodes = [];
this.roots.forEach(function (root) {
root.traverse(function (node) {
if (node.isSelected) {
selectedNodes.push(node);
}
return node.containsSelection;
});
});
return new (_immutable2 || _immutable()).default.List(selectedNodes);
}
/**
* Returns a node if it is the only one selected, or null otherwise
*/
}, {
key: 'getSingleSelectedNode',
value: function getSingleSelectedNode() {
var selectedNodes = this.getSelectedNodes();
if (selectedNodes.isEmpty() || selectedNodes.size > 1) {
return null;
}
return selectedNodes.first();
}
}, {
key: 'getNode',
value: function getNode(rootKey, nodeKey) {
var rootNode = this.roots.get(rootKey);
if (rootNode == null) {
return null;
}
return rootNode.find(nodeKey);
}
}, {
key: 'isEditingWorkingSet',
value: function isEditingWorkingSet() {
return this._conf.isEditingWorkingSet;
}
/**
* Builds the edited working set from the partially-child-derived .checkedStatus property
*/
}, {
key: 'getEditedWorkingSet',
value: function getEditedWorkingSet() {
return this._conf.editedWorkingSet;
}
}, {
key: 'isEditedWorkingSetEmpty',
value: function isEditedWorkingSetEmpty() {
return this.roots.every(function (root) {
return root.checkedStatus === 'clear';
});
}
}, {
key: 'getOpenFilesWorkingSet',
value: function getOpenFilesWorkingSet() {
return this._conf.openFilesWorkingSet;
}
/**
* Initiates the fetching of node's children if it's not already in the process.
* Clears the node's .isLoading property once the fetch is complete.
* Once the fetch is completed, clears the node's .isLoading property, builds the map of the
* node's children out of the fetched children URIs and a change subscription is created
* for the node to monitor future changes.
*/
}, {
key: '_fetchChildKeys',
value: function _fetchChildKeys(nodeKey) {
var _this7 = this;
var existingPromise = this._getLoading(nodeKey);
if (existingPromise != null) {
return existingPromise;
}
var promise = (_FileTreeHelpers2 || _FileTreeHelpers()).default.fetchChildren(nodeKey).then(function (childrenKeys) {
return _this7._setFetchedKeys(nodeKey, childrenKeys);
}, function (error) {
_this7._logger.error('Unable to fetch children for "' + nodeKey + '".');
_this7._logger.error('Original error: ', error);
// Unless the contents were already fetched in the past
// collapse the node and clear its loading state on error so the
// user can retry expanding it.
_this7._updateNodeAtAllRoots(nodeKey, function (node) {
if (node.wasFetched) {
return node.setIsLoading(false);
}
return node.set({ isExpanded: false, isLoading: false, children: new (_immutable2 || _immutable()).default.OrderedMap() });
});
_this7._clearLoading(nodeKey);
});
this._setLoading(nodeKey, promise);
return promise;
}
}, {
key: '_setFetchedKeys',
value: function _setFetchedKeys(nodeKey) {
var _this8 = this;
var childrenKeys = arguments.length <= 1 || arguments[1] === undefined ? [] : arguments[1];
var directory = (_FileTreeHelpers2 || _FileTreeHelpers()).default.getDirectoryByKey(nodeKey);
// The node with URI === nodeKey might be present at several roots - update them all
this._updateNodeAtAllRoots(nodeKey, function (node) {
// Maintain the order fetched from the FS
var childrenNodes = childrenKeys.map(function (uri) {
var prevNode = node.find(uri);
// If we already had a child with this URI - keep it
if (prevNode != null) {
return prevNode;
}
return new (_FileTreeNode2 || _FileTreeNode()).FileTreeNode({ uri: uri, rootUri: node.rootUri }, _this8._conf);
});
var children = (_FileTreeNode2 || _FileTreeNode()).FileTreeNode.childrenFromArray(childrenNodes);
var subscription = node.subscription || _this8._makeSubscription(nodeKey, directory);
// If the fetch indicated that some children were removed - dispose of all
// their subscriptions
var removedChildren = node.children.filter(function (n) {
return !children.has(n.name);
});
removedChildren.forEach(function (c) {
c.traverse(function (n) {
if (n.subscription != null) {
n.subscription.dispose();
}
return true;
});
});
return node.set({ isLoading: false, wasFetched: true, children: children, subscription: subscription });
});
this._clearLoading(nodeKey);
}
}, {
key: '_makeSubscription',
value: function _makeSubscription(nodeKey, directory) {
var _this9 = this;
if (directory == null) {
return null;
}
var fetchingPromise = null;
var couldMissUpdate = false;
try {
var _ret2 = (function () {
// Here we intentionally circumvent, to a degree, the logic in the _fetchChildKeys
// which wouldn't schedule a new fetch if there is already one running.
// This is fine for the most cases, but not for the subscription handling, as the
// subscription is notifying us that something has changed and if a fetch is already in
// progress then it is racing with the change. Therefore, if we detect that there was a change
// during the fetch we schedule another right after the first has finished.
var checkMissed = function checkMissed() {
fetchingPromise = null;
if (couldMissUpdate) {
fetchKeys();
}
};
var fetchKeys = function fetchKeys() {
if (fetchingPromise == null) {
couldMissUpdate = false;
fetchingPromise = _this9._fetchChildKeys(nodeKey).then(checkMissed);
} else {
couldMissUpdate = true;
}
};
// This call might fail if we try to watch a non-existing directory, or if permission denied.
return {
v: directory.onDidChange(function () {
fetchKeys();
})
};
})();
if (typeof _ret2 === 'object') return _ret2.v;
} catch (ex) {
/*
* Log error and mark the directory as dirty so the failed subscription will be attempted
* again next time the directory is expanded.
*/
this._logger.error('Cannot subscribe to directory "' + nodeKey + '"', ex);
return null;
}
}
}, {
key: '_getLoading',
value: function _getLoading(nodeKey) {
return this._isLoadingMap.get(nodeKey);
}
}, {
key: '_setLoading',
value: function _setLoading(nodeKey, value) {
this._isLoadingMap = this._isLoadingMap.set(nodeKey, value);
}
}, {
key: 'hasCwd',
value: function hasCwd() {
return this._cwdKey != null;
}
}, {
key: '_setCwdKey',
value: function _setCwdKey(cwdKey) {
this._cwdKey = cwdKey;
this._updateRoots(function (root) {
return root.setIsCwd(root.uri === cwdKey);
});
}
}, {
key: 'getFilter',
value: function getFilter() {
return this._filter;
}
}, {
key: 'addFilterLetter',
value: function addFilterLetter(letter) {
var _this10 = this;
this._filter = this._filter + letter;
this._updateRoots(function (root) {
return root.setRecursive(function (node) {
return node.containsFilterMatches ? null : node;
}, function (node) {
return (0, (_FileTreeFilterHelper2 || _FileTreeFilterHelper()).matchesFilter)(node.name, _this10._filter) ? node.set({
highlightedText: _this10._filter,
matchesFilter: true
}) : node.set({ highlightedText: '', matchesFilter: false });
});
});
this._selectFirstFilter();
this._emitChange();
}
}, {
key: 'clearFilter',
value: function clearFilter() {
this._filter = '';
this._updateRoots(function (root) {
return root.setRecursive(function (node) {
return null;
}, function (node) {
return node.set({ highlightedText: '', matchesFilter: true });
});
});
}
}, {
key: 'removeFilterLetter',
value: function removeFilterLetter() {
var _this11 = this;
var oldLength = this._filter.length;
this._filter = this._filter.substr(0, this._filter.length - 1);
if (oldLength > 1) {
this._updateRoots(function (root) {
return root.setRecursive(function (node) {
return null;
}, function (node) {
return (0, (_FileTreeFilterHelper2 || _FileTreeFilterHelper()).matchesFilter)(node.name, _this11._filter) ? node.set({
highlightedText: _this11._filter,
matchesFilter: true
}) : node.set({ highlightedText: '', matchesFilter: false });
});
});
this._emitChange();
} else if (oldLength === 1) {
this.clearFilter();
}
}
}, {
key: 'getFilterFound',
value: function getFilterFound() {
return this.roots.some(function (root) {
return root.containsFilterMatches;
});
}
/**
* Resets the node to be kept in view if no more data is being awaited. Safe to call many times
* because it only changes state if a node is being tracked.
*/
}, {
key: '_checkTrackedNode',
value: function _checkTrackedNode() {
if (
/*
* The loading map being empty is a heuristic for when loading has completed. It is inexact
* because the loading might be unrelated to the tracked node, however it is cheap and false
* positives will only last until loading is complete or until the user clicks another node in
* the tree.
*/
this._isLoadingMap.isEmpty()) {
// Loading has completed. Allow scrolling to proceed as usual.
this._clearTrackedNode();
}
}
}, {
key: '_clearLoading',
value: function _clearLoading(nodeKey) {
this._isLoadingMap = this._isLoadingMap.delete(nodeKey);
}
}, {
key: '_moveToNode',
value: _asyncToGenerator(function* (rootKey, nodeKey) {
var targetNode = this.getNode(rootKey, nodeKey);
if (targetNode == null || !targetNode.isContainer) {
return;
}
var selectedNodes = this.getSelectedNodes();
this._clearDragHover();
this._clearSelection();
try {
yield (_FileTreeHgHelpers2 || _FileTreeHgHelpers()).default.moveNodes(selectedNodes.toJS(), targetNode.uri);
} catch (e) {
atom.notifications.addError('Failed to move entries: ' + e.message);
}
})
}, {
key: '_deleteSelectedNodes',
value: _asyncToGenerator(function* () {
var selectedNodes = this.getSelectedNodes();
try {
yield (_FileTreeHgHelpers2 || _FileTreeHgHelpers()).default.deleteNodes(selectedNodes.toJS());
} catch (e) {
atom.notifications.addError('Failed to delete entries: ' + e.message);
}
})
}, {
key: '_expandNode',
value: function _expandNode(rootKey, nodeKey) {
var _this12 = this;
this._updateNodeAtRoot(rootKey, nodeKey, function (node) {
return node.setIsExpanded(true).setRecursive(function (n) {
return !n.isContainer || !n.isExpanded ? n : null;
}, function (n) {
if (n.isContainer && n.isExpanded) {
_this12._fetchChildKeys(n.uri);
return n.setIsLoading(true);
}
return n;
});
});
}
/**
* Performes a deep BFS scanning expand of contained nodes.
* returns - a promise fulfilled when the expand operation is finished
*/
}, {
key: '_expandNodeDeep',
value: function _expandNodeDeep(rootKey, nodeKey) {
var _this13 = this;
// Stop the traversal after 100 nodes were added to the tree
var itNodes = new FileTreeStoreBfsIterator(this, rootKey, nodeKey, /* limit */100);
var promise = new Promise(function (resolve) {
var expand = function expand() {
var traversedNodeKey = itNodes.traversedNode();
if (traversedNodeKey) {
_this13._expandNode(rootKey, traversedNodeKey);
var nextPromise = itNodes.next();
if (nextPromise) {
nextPromise.then(expand);
}
} else {
resolve();
}
};
expand();
});
return promise;
}
}, {
key: '_collapseNode',
value: function _collapseNode(rootKey, nodeKey) {
this._updateNodeAtRoot(rootKey, nodeKey, function (node) {
// Clear all selected nodes under the node being collapsed and dispose their subscriptions
return node.setRecursive(function (childNode) {
if (childNode.isExpanded) {
return null;
}
return childNode;
}, function (childNode) {
if (childNode.subscription != null) {
childNode.subscription.dispose();
}
if (childNode.uri === node.uri) {
return childNode.set({ isExpanded: false, subscription: null });
} else {
return childNode.set({ isSelected: false, subscription: null });
}
});
});
}
}, {
key: '_collapseNodeDeep',
value: function _collapseNodeDeep(rootKey, nodeKey) {
this._updateNodeAtRoot(rootKey, nodeKey, function (node) {
return node.setRecursive(
/* prePredicate */null, function (childNode) {
if (childNode.subscription != null) {
childNode.subscription.dispose();
}
if (childNode.uri !== node.uri) {
return childNode.set({ isExpanded: false, isSelected: false, subscription: null });
} else {
return childNode.set({ isExpanded: false, subscription: null });
}
});
});
}
}, {
key: '_setDragHoveredNode',
value: function _setDragHoveredNode(rootKey, nodeKey) {
this._clearDragHover();
this._updateNodeAtRoot(rootKey, nodeKey, function (node) {
return node.setIsDragHovered(true);
});
}
}, {
key: '_unhoverNode',
value: function _unhoverNode(rootKey, nodeKey) {
this._updateNodeAtRoot(rootKey, nodeKey, function (node) {
return node.setIsDragHovered(false);
});
}
/**
* Selects a single node and tracks it.
*/
}, {
key: '_setSelectedNode',
value: function _setSelectedNode(rootKey, nodeKey) {
this._clearSelection(rootKey, nodeKey);
this._updateNodeAtRoot(rootKey, nodeKey, function (node) {
return node.setIsSelected(true);
});
this._setTrackedNode(rootKey, nodeKey);
}
/**
* Mark a node that has been focused, similar to selected, but only true after mouseup.
*/
}, {
key: '_setFocusedNode',
value: function _setFocusedNode(rootKey, nodeKey) {
this._updateNodeAtRoot(rootKey, nodeKey, function (node) {
return node.setIsFocused(true);
});
}
/**
* Selects and focuses a node in one pass.
*/
}, {
key: '_setSelectedAndFocusedNode',
value: function _setSelectedAndFocusedNode(rootKey, nodeKey) {
this._clearSelection(rootKey, nodeKey);
this._updateNodeAtRoot(rootKey, nodeKey, function (node) {
return node.set({ isSelected: true, isFocused: true });
});
this._setTrackedNode(rootKey, nodeKey);
}
}, {
key: '_addSelectedNode',
value: function _addSelectedNode(rootKey, nodeKey) {
this._updateNodeAtRoot(rootKey, nodeKey, function (node) {
return node.setIsSelected(true);
});
}
}, {
key: '_unselectNode',
value: function _unselectNode(rootKey, nodeKey) {
this._updateNodeAtRoot(rootKey, nodeKey, function (node) {
return node.set({ isSelected: false, isFocused: false });
});
}
}, {
key: '_selectFirstFilter',
value: function _selectFirstFilter() {
var node = this.getSingleSelectedNode();
// if the current node matches the filter do nothing
if (node != null && node.matchesFilter) {
return;
}
this._moveSelectionDown();
node = this.getSingleSelectedNode();
// if the selection does not find anything up go down
if (node != null && !node.matchesFilter) {
this._moveSelectionUp();
}
}
/**
* Moves the selection one node down. In case several nodes were selected, the topmost (first in
* the natural visual order) is considered to be the reference point for the move.
*/
}, {
key: '_moveSelectionDown',
value: function _moveSelectionDown() {
if (this.roots.isEmpty()) {
return;
}
var selectedNodes = this.getSelectedNodes();
var nodeToSelect = undefined;
if (selectedNodes.isEmpty()) {
nodeToSelect = this.roots.first();
} else {
var selectedNode = selectedNodes.first();
nodeToSelect = selectedNode.findNext();
}
while (nodeToSelect != null && !nodeToSelect.matchesFilter) {
nodeToSelect = nodeToSelect.findNext();
}
if (nodeToSelect != null) {
this._setSelectedAndFocusedNode(nodeToSelect.rootUri, nodeToSelect.uri);
}
}
/**
* Moves the selection one node up. In case several nodes were selected, the topmost (first in
* the natural visual order) is considered to be the reference point for the move.
*/
}, {
key: '_moveSelectionUp',
value: function _moveSelectionUp() {
if (this.roots.isEmpty()) {
return;
}
var selectedNodes = this.getSelectedNodes();
var nodeToSelect = undefined;
if (selectedNodes.isEmpty()) {
nodeToSelect = this.roots.last().findLastRecursiveChild();
} else {
var selectedNode = selectedNodes.first();
nodeToSelect = selectedNode.findPrevious();
}
while (nodeToSelect != null && !nodeToSelect.matchesFilter) {
nodeToSelect = nodeToSelect.findPrevious();
}
if (nodeToSelect != null) {
this._setSelectedAndFocusedNode(nodeToSelect.rootUri, nodeToSelect.uri);
}
}
}, {
key: '_moveSelectionToTop',
value: function _moveSelectionToTop() {
if (this.roots.isEmpty()) {
return;
}
var nodeToSelect = this.roots.first();
if (nodeToSelect != null && !nodeToSelect.shouldBeShown) {
nodeToSelect = nodeToSelect.findNext();
}
if (nodeToSelect != null) {
this._setSelectedAndFocusedNode(nodeToSelect.uri, nodeToSelect.uri);
}
}
}, {
key: '_moveSelectionToBottom',
value: function _moveSelectionToBottom() {
if (this.roots.isEmpty()) {
return;
}
var lastRoot = this.roots.last();
var lastChild = lastRoot.findLastRecursiveChild();
this._setSelectedAndFocusedNode(lastChild.rootUri, lastChild.uri);
}
}, {
key: '_clearDragHover',
value: function _clearDragHover() {
this._updateRoots(function (root) {
return root.setRecursive(function (node) {
return node.containsDragHover ? null : node;
}, function (node) {
return node.setIsDragHovered(false);
});
});
}
// Clear selections and focuses on all nodes except an optionally specified
// current node.
}, {
key: '_clearSelection',
value: function _clearSelection(currRootKey, currNodeKey) {
this._updateRoots(function (root) {
return root.setRecursive(function (node) {
return node.containsSelection ? null : node;
}, function (node) {
return node.rootUri === currRootKey && node.uri === currNodeKey ? node : node.set({ isSelected: false, isFocused: false });
});
});
}
}, {
key: '_setRootKeys',
value: function _setRootKeys(rootKeys) {
var _this14 = this;
var rootNodes = rootKeys.map(function (rootUri) {
var root = _this14.roots.get(rootUri);
if (root != null) {
return root;
}
return new (_FileTreeNode2 || _FileTreeNode()).FileTreeNode({
uri: rootUri,
rootUri: rootUri,
connectionTitle: (_FileTreeHelpers2 || _FileTreeHelpers()).default.getDisplayTitle(rootUri) || ''
}, _this14._conf);
});
var roots = new (_immutable2 || _immutable()).default.OrderedMap(rootNodes.map(function (root) {
return [root.uri, root];
}));
var removedRoots = this.roots.filter(function (root) {
return !roots.has(root.uri);
});
removedRoots.forEach(function (root) {
return r