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.
275 lines (235 loc) • 11.2 kB
JavaScript
Object.defineProperty(exports, '__esModule', {
value: true
});
/*
* 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 _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 _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 _commonsAtomTextEditor2;
function _commonsAtomTextEditor() {
return _commonsAtomTextEditor2 = require('../../commons-atom/text-editor');
}
var _commonsNodeString2;
function _commonsNodeString() {
return _commonsNodeString2 = require('../../commons-node/string');
}
var _NavigationStack2;
function _NavigationStack() {
return _NavigationStack2 = require('./NavigationStack');
}
var _assert2;
function _assert() {
return _assert2 = _interopRequireDefault(require('assert'));
}
var _commonsNodeNuclideUri2;
function _commonsNodeNuclideUri() {
return _commonsNodeNuclideUri2 = _interopRequireDefault(require('../../commons-node/nuclideUri'));
}
var _Location2;
function _Location() {
return _Location2 = require('./Location');
}
function log(message) {}
// Uncomment this to debug
// console.log(message);
// Handles the state machine that responds to various atom events.
//
// After a Nav move, any non-nav moves or scroll changes update the current
// nav location. So that a nav-stack move(forward/backwards) will return
// position and scroll after the non-nav move/scrolls.
//
// When doing a forwards/backwards nav-stack move, ignore all events
// until the move is complete.
//
// There are several user scenarios, each which spawn different event orders:
// - startup - open file
// - onActivate, activePaneStopChanging
// - changing tabs
// - onActivate, activePaneStopChanging
// - atom.workspace.open of closed file
// - create, scroll, activate, open, scroll, activePaneStopChanging
// - atom.workspace.open of open file, no scroll or move
// - activate, open, activePaneStopChanging
// - atom.workspace.open of open file, with move
// - activate, position, open, scroll, activePaneStopChanging
// - atom.workspace.open of current file
// - position, open, scroll
//
// - nuclide-atom-helpers.goToLocationInEditor
// - position, onOptInNavigation, [scroll]
//
// In general, when we get a new event, if the editor is not the current,
// then we push a new element on the nav stack; if the editor of the new event
// does match the top of the nav stack, then we update the top of the nav stack
// with the new location.
//
// This works except for the last case of open into the current file.
//
// To deal with the above - we do the following hack:
// If an open occurs and it is not within an activate/activePaneStopChanging pair,
// and the top matches the newly opened editor, then we have the last case
// of 'open within the current file'. So, we restore the current top to its
// previous location before pushing a new top.
var NavigationStackController = (function () {
function NavigationStackController() {
_classCallCheck(this, NavigationStackController);
this._navigationStack = new (_NavigationStack2 || _NavigationStack()).NavigationStack();
this._isNavigating = false;
this._inActivate = false;
this._lastLocation = null;
}
_createClass(NavigationStackController, [{
key: '_updateStackLocation',
value: function _updateStackLocation(editor) {
if (this._isNavigating) {
return;
}
// See discussion below on event order ...
var previousEditor = this._navigationStack.getCurrentEditor();
if (previousEditor === editor) {
var previousLocation = this._navigationStack.getCurrent();
(0, (_assert2 || _assert()).default)(previousLocation != null && previousLocation.type === 'editor');
this._lastLocation = _extends({}, previousLocation);
}
this._navigationStack.attemptUpdate((0, (_Location2 || _Location()).getLocationOfEditor)(editor));
}
}, {
key: 'updatePosition',
value: function updatePosition(editor, newBufferPosition) {
log('updatePosition ' + newBufferPosition.row + ', ' + (newBufferPosition.column + ' ' + (0, (_commonsNodeString2 || _commonsNodeString()).maybeToString)(editor.getPath())));
this._updateStackLocation(editor);
}
// scrollTop is in Pixels
}, {
key: 'updateScroll',
value: function updateScroll(editor, scrollTop) {
log('updateScroll ' + scrollTop + ' ' + (0, (_commonsNodeString2 || _commonsNodeString()).maybeToString)(editor.getPath()));
this._updateStackLocation(editor);
}
}, {
key: 'onCreate',
value: function onCreate(editor) {
log('onCreate ' + (0, (_commonsNodeString2 || _commonsNodeString()).maybeToString)(editor.getPath()));
this._navigationStack.editorOpened(editor);
this._updateStackLocation(editor);
}
}, {
key: 'onDestroy',
value: function onDestroy(editor) {
log('onDestroy ' + (0, (_commonsNodeString2 || _commonsNodeString()).maybeToString)(editor.getPath()));
this._navigationStack.editorClosed(editor);
}
// Open is always preceded by activate, unless opening the current file
}, {
key: 'onOpen',
value: function onOpen(editor) {
log('onOpen ' + (0, (_commonsNodeString2 || _commonsNodeString()).maybeToString)(editor.getPath()));
// Hack alert, an atom.workspace.open of a location in the current editor,
// we get the location update before the onDidOpen event, and we don't get
// an activate/onDidStopChangingActivePaneItem pair. So here,
// we restore top of the stack to the previous location before pushing a new
// nav stack entry.
if (!this._inActivate && this._lastLocation != null && this._lastLocation.editor === editor && this._navigationStack.getCurrentEditor() === editor) {
this._navigationStack.attemptUpdate(this._lastLocation);
this._navigationStack.push((0, (_Location2 || _Location()).getLocationOfEditor)(editor));
} else {
this._updateStackLocation(editor);
}
this._lastLocation = null;
}
}, {
key: 'onActivate',
value: function onActivate(editor) {
log('onActivate ' + (0, (_commonsNodeString2 || _commonsNodeString()).maybeToString)(editor.getPath()));
this._inActivate = true;
this._updateStackLocation(editor);
}
}, {
key: 'onActiveStopChanging',
value: function onActiveStopChanging(editor) {
log('onActivePaneStopChanging ' + (0, (_commonsNodeString2 || _commonsNodeString()).maybeToString)(editor.getPath()));
this._inActivate = false;
}
}, {
key: 'onOptInNavigation',
value: function onOptInNavigation(editor) {
log('onOptInNavigation ' + (0, (_commonsNodeString2 || _commonsNodeString()).maybeToString)(editor.getPath()));
// Opt-in navigation is handled in the same way as a file open with no preceeding activation
this.onOpen(editor);
}
// When closing a project path, we remove all stack entries contained in that
// path which are not also contained in a project path which is remaining open.
}, {
key: 'removePath',
value: function removePath(removedPath, remainingDirectories) {
log('Removing path ' + removedPath + ' remaining: ' + JSON.stringify(remainingDirectories));
this._navigationStack.filter(function (location) {
var uri = (0, (_Location2 || _Location()).getPathOfLocation)(location);
return uri == null || !(_commonsNodeNuclideUri2 || _commonsNodeNuclideUri()).default.contains(removedPath, uri) || remainingDirectories.find(function (directory) {
return (_commonsNodeNuclideUri2 || _commonsNodeNuclideUri()).default.contains(directory, uri);
}) != null;
});
}
}, {
key: '_navigateTo',
value: _asyncToGenerator(function* (location) {
(0, (_assert2 || _assert()).default)(!this._isNavigating);
if (location == null) {
return;
}
this._isNavigating = true;
try {
var editor = yield (0, (_Location2 || _Location()).editorOfLocation)(location);
// Note that this will not actually update the scroll position
// The scroll position update will happen on the next tick.
log('navigating to: ' + location.scrollTop + ' ' + JSON.stringify(location.bufferPosition));
(0, (_commonsAtomTextEditor2 || _commonsAtomTextEditor()).setPositionAndScroll)(editor, location.bufferPosition, location.scrollTop);
} finally {
this._isNavigating = false;
}
})
}, {
key: 'navigateForwards',
value: _asyncToGenerator(function* () {
log('navigateForwards');
if (!this._isNavigating) {
yield this._navigateTo(this._navigationStack.next());
}
})
}, {
key: 'navigateBackwards',
value: _asyncToGenerator(function* () {
log('navigateBackwards');
if (!this._isNavigating) {
yield this._navigateTo(this._navigationStack.previous());
}
})
// For Testing.
}, {
key: 'getLocations',
value: function getLocations() {
return this._navigationStack.getLocations();
}
// For Testing.
}, {
key: 'getIndex',
value: function getIndex() {
return this._navigationStack.getIndex();
}
}]);
return NavigationStackController;
})();
exports.NavigationStackController = NavigationStackController;
// Indicates that we're processing a forward/backwards navigation in the stack.
// While processing a navigation stack move we don't update the nav stack.
// Indicates that we are in the middle of a activate/onDidStopChangingActivePaneItem
// pair of events.
// The last location update we've seen. See discussion below on event order.