UNPKG

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.

622 lines (568 loc) 23.4 kB
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; }; })(); /* * 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. */ 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 }; } var _FileTreeConstants2; function _FileTreeConstants() { return _FileTreeConstants2 = require('./FileTreeConstants'); } var _commonsNodeDebounce2; function _commonsNodeDebounce() { return _commonsNodeDebounce2 = _interopRequireDefault(require('../../commons-node/debounce')); } var _atom2; function _atom() { return _atom2 = require('atom'); } var _FileTreeDispatcher2; function _FileTreeDispatcher() { return _FileTreeDispatcher2 = _interopRequireDefault(require('./FileTreeDispatcher')); } var _FileTreeHelpers2; function _FileTreeHelpers() { return _FileTreeHelpers2 = _interopRequireDefault(require('./FileTreeHelpers')); } var _FileTreeStore2; function _FileTreeStore() { return _FileTreeStore2 = require('./FileTreeStore'); } var _immutable2; function _immutable() { return _immutable2 = _interopRequireDefault(require('immutable')); } var _semver2; function _semver() { return _semver2 = _interopRequireDefault(require('semver')); } var _nuclideHgGitBridge2; function _nuclideHgGitBridge() { return _nuclideHgGitBridge2 = require('../../nuclide-hg-git-bridge'); } var _nuclideHgRpc2; function _nuclideHgRpc() { return _nuclideHgRpc2 = require('../../nuclide-hg-rpc'); } var _nuclideLogging2; function _nuclideLogging() { return _nuclideLogging2 = require('../../nuclide-logging'); } var _commonsNodeNuclideUri2; function _commonsNodeNuclideUri() { return _commonsNodeNuclideUri2 = _interopRequireDefault(require('../../commons-node/nuclideUri')); } 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 FileTreeActions = (function () { _createClass(FileTreeActions, null, [{ key: 'getInstance', value: function getInstance() { if (!instance) { instance = new FileTreeActions(); } return instance; } }]); function FileTreeActions() { _classCallCheck(this, FileTreeActions); this._dispatcher = (_FileTreeDispatcher2 || _FileTreeDispatcher()).default.getInstance(); this._store = (_FileTreeStore2 || _FileTreeStore()).FileTreeStore.getInstance(); this._subscriptionForRepository = new (_immutable2 || _immutable()).default.Map(); } _createClass(FileTreeActions, [{ key: 'setCwd', value: function setCwd(rootKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_CWD, rootKey: rootKey }); } }, { key: 'setRootKeys', value: function setRootKeys(rootKeys) { var existingRootKeySet = new (_immutable2 || _immutable()).default.Set(this._store.getRootKeys()); var addedRootKeys = new (_immutable2 || _immutable()).default.Set(rootKeys).subtract(existingRootKeySet); this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_ROOT_KEYS, rootKeys: rootKeys }); for (var rootKey of addedRootKeys) { this.expandNode(rootKey, rootKey); } } }, { key: 'clearFilter', value: function clearFilter() { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.CLEAR_FILTER }); } }, { key: 'expandNode', value: function expandNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.EXPAND_NODE, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'expandNodeDeep', value: function expandNodeDeep(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.EXPAND_NODE_DEEP, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'deleteSelectedNodes', value: function deleteSelectedNodes() { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.DELETE_SELECTED_NODES }); } // Makes sure a specific child exists for a given node. If it does not exist, temporarily // create it and initiate a fetch. This feature is exclusively for expanding to a node deep // in a tree. }, { key: 'ensureChildNode', value: function ensureChildNode(nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.ENSURE_CHILD_NODE, nodeKey: nodeKey }); } }, { key: 'collapseNode', value: function collapseNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.COLLAPSE_NODE, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'collapseNodeDeep', value: function collapseNodeDeep(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.COLLAPSE_NODE_DEEP, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'setExcludeVcsIgnoredPaths', value: function setExcludeVcsIgnoredPaths(excludeVcsIgnoredPaths) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_EXCLUDE_VCS_IGNORED_PATHS, excludeVcsIgnoredPaths: excludeVcsIgnoredPaths }); } }, { key: 'setHideIgnoredNames', value: function setHideIgnoredNames(hideIgnoredNames) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_HIDE_IGNORED_NAMES, hideIgnoredNames: hideIgnoredNames }); } }, { key: 'setIgnoredNames', value: function setIgnoredNames(ignoredNames) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_IGNORED_NAMES, ignoredNames: ignoredNames }); } }, { key: 'setTrackedNode', value: function setTrackedNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_TRACKED_NODE, nodeKey: nodeKey, rootKey: rootKey }); } }, { key: 'moveToNode', value: function moveToNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.MOVE_TO_NODE, nodeKey: nodeKey, rootKey: rootKey }); } }, { key: 'setUsePreviewTabs', value: function setUsePreviewTabs(usePreviewTabs) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_USE_PREVIEW_TABS, usePreviewTabs: usePreviewTabs }); } }, { key: 'setUsePrefixNav', value: function setUsePrefixNav(usePrefixNav) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_USE_PREFIX_NAV, usePrefixNav: usePrefixNav }); } }, { key: 'confirmNode', value: function confirmNode(rootKey, nodeKey) { var pending = arguments.length <= 2 || arguments[2] === undefined ? false : arguments[2]; var node = this._store.getNode(rootKey, nodeKey); if (node == null) { return; } if (node.isContainer) { var actionType = node.isExpanded ? (_FileTreeConstants2 || _FileTreeConstants()).ActionType.COLLAPSE_NODE : (_FileTreeConstants2 || _FileTreeConstants()).ActionType.EXPAND_NODE; this._dispatcher.dispatch({ actionType: actionType, nodeKey: nodeKey, rootKey: rootKey }); } else { var openOptions = { activatePane: true, searchAllPanes: true }; // TODO: Make the following the default once Nuclide only supports Atom v1.6.0+ if ((_semver2 || _semver()).default.gte(atom.getVersion(), '1.6.0')) { openOptions = _extends({}, openOptions, { pending: true }); } atom.workspace.open((_FileTreeHelpers2 || _FileTreeHelpers()).default.keyToPath(nodeKey), openOptions); } } }, { key: 'keepPreviewTab', value: function keepPreviewTab() { // TODO: Make the following the default once Nuclide only supports Atom v1.6.0+ if ((_semver2 || _semver()).default.gte(atom.getVersion(), '1.6.0')) { var activePane = atom.workspace.getActivePane(); if (activePane != null) { activePane.clearPendingItem(); } } else { var activePaneItem = atom.workspace.getActivePaneItem(); if (activePaneItem != null) { atom.commands.dispatch(atom.views.getView(activePaneItem), 'tabs:keep-preview-tab'); } } } }, { key: 'openSelectedEntrySplit', value: function openSelectedEntrySplit(nodeKey, orientation, side) { var pane = atom.workspace.getActivePane(); atom.workspace.openURIInPane((_FileTreeHelpers2 || _FileTreeHelpers()).default.keyToPath(nodeKey), pane.split(orientation, side)); } }, { key: 'setVcsStatuses', value: function setVcsStatuses(rootKey, vcsStatuses) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_VCS_STATUSES, rootKey: rootKey, vcsStatuses: vcsStatuses }); } /** * Updates the root repositories to match the provided directories. */ }, { key: 'updateRepositories', value: _asyncToGenerator(function* (rootDirectories) { var _this = this; var rootKeys = rootDirectories.map(function (directory) { return (_FileTreeHelpers2 || _FileTreeHelpers()).default.dirPathToKey(directory.getPath()); }); var rootRepos = yield Promise.all(rootDirectories.map(function (directory) { return (0, (_nuclideHgGitBridge2 || _nuclideHgGitBridge()).repositoryForPath)(directory.getPath()); })); // t7114196: Given the current implementation of HgRepositoryClient, each root directory will // always correspond to a unique instance of HgRepositoryClient. Ideally, if multiple subfolders // of an Hg repo are used as project roots in Atom, only one HgRepositoryClient should be // created. // Group all of the root keys by their repository, excluding any that don't belong to a // repository. var rootKeysForRepository = (_immutable2 || _immutable()).default.List(rootKeys).groupBy(function (rootKey, index) { return rootRepos[index]; }).filter(function (v, k) { return k != null; }).map(function (v) { return new (_immutable2 || _immutable()).default.Set(v); }); var prevRepos = this._store.getRepositories(); // Let the store know we have some new repos! var nextRepos = new (_immutable2 || _immutable()).default.Set(rootKeysForRepository.keys()); this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_REPOSITORIES, repositories: nextRepos }); var removedRepos = prevRepos.subtract(nextRepos); var addedRepos = nextRepos.subtract(prevRepos); // TODO: Rewrite `_repositoryAdded` to return the subscription instead of adding it to a map as // a side effect. The map can be created here with something like // `subscriptions = Immutable.Map(repos).map(this._repositoryAdded)`. Since // `_repositoryAdded` will no longer be about side effects, it should then be renamed. // `_repositoryRemoved` could probably be inlined here. That would leave this function as // the only one doing side-effects. // Unsubscribe from removedRepos. removedRepos.forEach(function (repo) { return _this._repositoryRemoved(repo, rootKeysForRepository); }); // Create subscriptions for addedRepos. addedRepos.forEach(function (repo) { return _this._repositoryAdded(repo, rootKeysForRepository); }); }) }, { key: 'updateWorkingSet', value: function updateWorkingSet(workingSet) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_WORKING_SET, workingSet: workingSet }); } }, { key: 'updateOpenFilesWorkingSet', value: function updateOpenFilesWorkingSet(openFilesWorkingSet) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_OPEN_FILES_WORKING_SET, openFilesWorkingSet: openFilesWorkingSet }); } }, { key: 'updateWorkingSetsStore', value: function updateWorkingSetsStore(workingSetsStore) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_WORKING_SETS_STORE, workingSetsStore: workingSetsStore }); } }, { key: 'startEditingWorkingSet', value: function startEditingWorkingSet(editedWorkingSet) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.START_EDITING_WORKING_SET, editedWorkingSet: editedWorkingSet }); } }, { key: 'finishEditingWorkingSet', value: function finishEditingWorkingSet() { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.FINISH_EDITING_WORKING_SET }); } }, { key: 'checkNode', value: function checkNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.CHECK_NODE, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'uncheckNode', value: function uncheckNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.UNCHECK_NODE, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'setDragHoveredNode', value: function setDragHoveredNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_DRAG_HOVERED_NODE, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'setSelectedNode', value: function setSelectedNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_SELECTED_NODE, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'setFocusedNode', value: function setFocusedNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_FOCUSED_NODE, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'addSelectedNode', value: function addSelectedNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.ADD_SELECTED_NODE, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'unselectNode', value: function unselectNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.UNSELECT_NODE, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'unhoverNode', value: function unhoverNode(rootKey, nodeKey) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.UNHOVER_NODE, rootKey: rootKey, nodeKey: nodeKey }); } }, { key: 'moveSelectionUp', value: function moveSelectionUp() { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.MOVE_SELECTION_UP }); } }, { key: 'moveSelectionDown', value: function moveSelectionDown() { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.MOVE_SELECTION_DOWN }); } }, { key: 'moveSelectionToTop', value: function moveSelectionToTop() { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.MOVE_SELECTION_TO_TOP }); } }, { key: 'moveSelectionToBottom', value: function moveSelectionToBottom() { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.MOVE_SELECTION_TO_BOTTOM }); } }, { key: 'setOpenFilesExpanded', value: function setOpenFilesExpanded(openFilesExpanded) { this._dispatcher.dispatch({ actionType: (_FileTreeConstants2 || _FileTreeConstants()).ActionType.SET_OPEN_FILES_EXPANDED, openFilesExpanded: openFilesExpanded }); } }, { key: '_repositoryAdded', value: _asyncToGenerator(function* (repo, rootKeysForRepository) { // We support HgRepositoryClient and GitRepositoryAsync objects. if (repo.getType() !== 'hg' && repo.getType() !== 'git' || repo.async == null) { return; } var asyncRepo = repo.async; yield asyncRepo.refreshStatus(); var statusCodeForPath = this._getCachedPathStatuses(repo); for (var rootKeyForRepo of rootKeysForRepository.get(repo)) { this.setVcsStatuses(rootKeyForRepo, statusCodeForPath); } // Now that the initial VCS statuses are set, subscribe to changes to the Repository so that the // VCS statuses are kept up to date. var debouncedChangeStatuses = (0, (_commonsNodeDebounce2 || _commonsNodeDebounce()).default)(this._onDidChangeStatusesForRepository.bind(this, repo, rootKeysForRepository), /* wait */1000, /* immediate */false); // Different repo types emit different events at individual and refresh updates. // Hence, the need to debounce and listen to both change types. var changeStatusesSubscriptions = new (_atom2 || _atom()).CompositeDisposable(); changeStatusesSubscriptions.add(asyncRepo.onDidChangeStatuses(debouncedChangeStatuses), asyncRepo.onDidChangeStatus(debouncedChangeStatuses)); this._subscriptionForRepository = this._subscriptionForRepository.set(repo, changeStatusesSubscriptions); }) /** * Fetches a consistent object map from absolute file paths to * their corresponding `StatusCodeNumber` for easy representation with the file tree. */ }, { key: '_getCachedPathStatuses', value: function _getCachedPathStatuses(repo) { var asyncRepo = repo.async; var statuses = asyncRepo.getCachedPathStatuses(); var relativeCodePaths = undefined; if (asyncRepo.getType() === 'hg') { // `hg` already comes from `HgRepositoryClient` in `StatusCodeNumber` format. relativeCodePaths = statuses; } else { relativeCodePaths = {}; // Transform `git` bit numbers to `StatusCodeNumber` format. var StatusCodeNumber = (_nuclideHgRpc2 || _nuclideHgRpc()).hgConstants.StatusCodeNumber; for (var relativePath in statuses) { var gitStatusNumber = statuses[relativePath]; var statusCode = undefined; if (asyncRepo.isStatusNew(gitStatusNumber)) { statusCode = StatusCodeNumber.UNTRACKED; } else if (asyncRepo.isStatusStaged(gitStatusNumber)) { statusCode = StatusCodeNumber.ADDED; } else if (asyncRepo.isStatusModified(gitStatusNumber)) { statusCode = StatusCodeNumber.MODIFIED; } else if (asyncRepo.isStatusIgnored(gitStatusNumber)) { statusCode = StatusCodeNumber.IGNORED; } else if (asyncRepo.isStatusDeleted(gitStatusNumber)) { statusCode = StatusCodeNumber.REMOVED; } else { (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)().warn('Unrecognized git status number ' + gitStatusNumber); statusCode = StatusCodeNumber.MODIFIED; } relativeCodePaths[relativePath] = statusCode; } } var repoRoot = repo.getWorkingDirectory(); var absoluteCodePaths = {}; for (var relativePath in relativeCodePaths) { var absolutePath = (_commonsNodeNuclideUri2 || _commonsNodeNuclideUri()).default.join(repoRoot, relativePath); absoluteCodePaths[absolutePath] = relativeCodePaths[relativePath]; } return absoluteCodePaths; } }, { key: '_onDidChangeStatusesForRepository', value: function _onDidChangeStatusesForRepository(repo, rootKeysForRepository) { for (var rootKey of rootKeysForRepository.get(repo)) { this.setVcsStatuses(rootKey, this._getCachedPathStatuses(repo)); } } }, { key: '_repositoryRemoved', value: function _repositoryRemoved(repo) { var disposable = this._subscriptionForRepository.get(repo); if (!disposable) { // There is a small chance that the add/remove of the Repository could happen so quickly that // the entry for the repo in _subscriptionForRepository has not been set yet. // TODO: Report a soft error for this. return; } this._subscriptionForRepository = this._subscriptionForRepository.delete(repo); disposable.dispose(); } }]); return FileTreeActions; })(); module.exports = FileTreeActions;