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.

1,182 lines (1,076 loc) 55.9 kB
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 _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); 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 _createDecoratedClass = (function () { function defineProperties(target, descriptors, initializers) { for (var i = 0; i < descriptors.length; i++) { var descriptor = descriptors[i]; var decorators = descriptor.decorators; var key = descriptor.key; delete descriptor.key; delete descriptor.decorators; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor || descriptor.initializer) descriptor.writable = true; if (decorators) { for (var f = 0; f < decorators.length; f++) { var decorator = decorators[f]; if (typeof decorator === 'function') { descriptor = decorator(target, key, descriptor) || descriptor; } else { throw new TypeError('The decorator for method ' + descriptor.key + ' is of the invalid type ' + typeof decorator); } } if (descriptor.initializer !== undefined) { initializers[key] = descriptor; continue; } } Object.defineProperty(target, key, descriptor); } } return function (Constructor, protoProps, staticProps, protoInitializers, staticInitializers) { if (protoProps) defineProperties(Constructor.prototype, protoProps, protoInitializers); if (staticProps) defineProperties(Constructor, staticProps, staticInitializers); 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 _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 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 _nuclideArcanistRpcLibUtils2; function _nuclideArcanistRpcLibUtils() { return _nuclideArcanistRpcLibUtils2 = require('../../nuclide-arcanist-rpc/lib/utils'); } var _atom2; function _atom() { return _atom2 = require('atom'); } var _electron2; function _electron() { return _electron2 = require('electron'); } var _constants2; function _constants() { return _constants2 = require('./constants'); } var _assert2; function _assert() { return _assert2 = _interopRequireDefault(require('assert')); } var _nuclideHgGitBridge2; function _nuclideHgGitBridge() { return _nuclideHgGitBridge2 = require('../../nuclide-hg-git-bridge'); } var _nuclideAnalytics2; function _nuclideAnalytics() { return _nuclideAnalytics2 = require('../../nuclide-analytics'); } var _commonsNodePromise2; function _commonsNodePromise() { return _commonsNodePromise2 = require('../../commons-node/promise'); } var _commonsNodeCollection2; function _commonsNodeCollection() { return _commonsNodeCollection2 = require('../../commons-node/collection'); } var _commonsNodeStream2; function _commonsNodeStream() { return _commonsNodeStream2 = require('../../commons-node/stream'); } var _commonsNodeNuclideUri2; function _commonsNodeNuclideUri() { return _commonsNodeNuclideUri2 = _interopRequireDefault(require('../../commons-node/nuclideUri')); } var _RepositoryStack2; function _RepositoryStack() { return _RepositoryStack2 = _interopRequireDefault(require('./RepositoryStack')); } var _rxjsBundlesRxUmdMinJs2; function _rxjsBundlesRxUmdMinJs() { return _rxjsBundlesRxUmdMinJs2 = _interopRequireDefault(require('rxjs/bundles/Rx.umd.min.js')); } var _notifications2; function _notifications() { return _notifications2 = require('./notifications'); } var _commonsAtomTextEditor2; function _commonsAtomTextEditor() { return _commonsAtomTextEditor2 = require('../../commons-atom/text-editor'); } var _nuclideLogging2; function _nuclideLogging() { return _nuclideLogging2 = require('../../nuclide-logging'); } var _nuclideRemoteConnection2; function _nuclideRemoteConnection() { return _nuclideRemoteConnection2 = require('../../nuclide-remote-connection'); } var ACTIVE_FILE_UPDATE_EVENT = 'active-file-update'; var CHANGE_REVISIONS_EVENT = 'did-change-revisions'; var ACTIVE_BUFFER_CHANGE_MODIFIED_EVENT = 'active-buffer-change-modified'; var DID_UPDATE_STATE_EVENT = 'did-update-state'; function getRevisionUpdateMessage(phabricatorRevision) { return '\n\n# Updating ' + phabricatorRevision.name + '\n#\n# Enter a brief description of the changes included in this update.\n# The first line is used as subject, next lines as comment.'; } var MAX_DIALOG_FILE_STATUS_COUNT = 20; // Returns a string with all newline strings, '\\n', converted to literal newlines, '\n'. function convertNewlines(message) { return message.replace(/\\n/g, '\n'); } function getInitialFileChangeState() { return { fromRevisionTitle: 'No file selected', toRevisionTitle: 'No file selected', filePath: '', oldContents: '', newContents: '', compareRevisionInfo: null }; } function viewModeToDiffOption(viewMode) { switch (viewMode) { case (_constants2 || _constants()).DiffMode.COMMIT_MODE: return (_constants2 || _constants()).DiffOption.DIRTY; case (_constants2 || _constants()).DiffMode.PUBLISH_MODE: return (_constants2 || _constants()).DiffOption.LAST_COMMIT; case (_constants2 || _constants()).DiffMode.BROWSE_MODE: return (_constants2 || _constants()).DiffOption.COMPARE_COMMIT; default: throw new Error('Unrecognized view mode!'); } } function getFileStatusListMessage(fileChanges) { var message = ''; if (fileChanges.size < MAX_DIALOG_FILE_STATUS_COUNT) { for (var _ref3 of fileChanges) { var _ref2 = _slicedToArray(_ref3, 2); var filePath = _ref2[0]; var statusCode = _ref2[1]; message += '\n' + (_constants2 || _constants()).FileChangeStatusToPrefix[statusCode] + atom.project.relativize(filePath); } } else { message = '\n more than ' + MAX_DIALOG_FILE_STATUS_COUNT + ' files (check using `hg status`)'; } return message; } function hgRepositoryForPath(filePath) { // Calling atom.project.repositoryForDirectory gets the real path of the directory, // which is another round-trip and calls the repository providers to get an existing repository. // Instead, the first match of the filtering here is the only possible match. var repository = (0, (_nuclideHgGitBridge2 || _nuclideHgGitBridge()).repositoryForPath)(filePath); if (repository == null || repository.getType() !== 'hg') { var _type = repository ? repository.getType() : 'no repository'; throw new Error('Diff view only supports `Mercurial` repositories, ' + ('but found `' + _type + '` at path: `' + filePath + '`')); } return repository; } function notifyRevisionStatus(phabRevision, statusMessage) { var message = 'Revision ' + statusMessage; if (phabRevision == null) { atom.notifications.addSuccess(message, { nativeFriendly: true }); return; } var name = phabRevision.name; var url = phabRevision.url; message = 'Revision \'' + name + '\' ' + statusMessage; atom.notifications.addSuccess(message, { dismissable: true, buttons: [{ className: 'icon icon-globe', onDidClick: function onDidClick() { (_electron2 || _electron()).shell.openExternal(url); }, text: 'Open in Phabricator' }], nativeFriendly: true }); } var DiffViewModel = (function () { function DiffViewModel() { var _this = this; _classCallCheck(this, DiffViewModel); this._emitter = new (_atom2 || _atom()).Emitter(); this._subscriptions = new (_atom2 || _atom()).CompositeDisposable(); this._activeSubscriptions = new (_atom2 || _atom()).CompositeDisposable(); this._uiProviders = []; this._repositoryStacks = new Map(); this._repositorySubscriptions = new Map(); this._isActive = false; this._publishUpdates = new (_rxjsBundlesRxUmdMinJs2 || _rxjsBundlesRxUmdMinJs()).default.Subject(); this._state = { viewMode: (_constants2 || _constants()).DiffMode.BROWSE_MODE, commitMessage: null, commitMode: (_constants2 || _constants()).CommitMode.COMMIT, commitModeState: (_constants2 || _constants()).CommitModeState.READY, publishMessage: null, publishMode: (_constants2 || _constants()).PublishMode.CREATE, publishModeState: (_constants2 || _constants()).PublishModeState.READY, headRevision: null, dirtyFileChanges: new Map(), selectedFileChanges: new Map(), showNonHgRepos: true }; this._serializedUpdateActiveFileDiff = (0, (_commonsNodePromise2 || _commonsNodePromise()).serializeAsyncCall)(function () { return _this._updateActiveFileDiff(); }); this._setActiveFileState(getInitialFileChangeState()); this._updateRepositories(); this._subscriptions.add(atom.project.onDidChangePaths(this._updateRepositories.bind(this))); } _createDecoratedClass(DiffViewModel, [{ key: '_updateRepositories', value: function _updateRepositories() { var repositories = new Set(atom.project.getRepositories().filter(function (repository) { return repository != null && repository.getType() === 'hg'; })); // Dispose removed projects repositories, if any. for (var _ref43 of this._repositoryStacks) { var _ref42 = _slicedToArray(_ref43, 2); var repository = _ref42[0]; var repositoryStack = _ref42[1]; if (repositories.has(repository)) { continue; } if (this._activeRepositoryStack === repositoryStack) { this._activeRepositoryStack = null; } repositoryStack.dispose(); this._repositoryStacks.delete(repository); var subscriptions = this._repositorySubscriptions.get(repository); (0, (_assert2 || _assert()).default)(subscriptions); subscriptions.dispose(); this._repositorySubscriptions.delete(repository); } // Add the new project repositories, if any. for (var repository of repositories) { if (this._repositoryStacks.has(repository)) { continue; } var hgRepository = repository; this._createRepositoryStack(hgRepository); } // Update active repository stack, if needed. // This will make sure we have a repository stack active whenever we have // a mercurial repository added to the project. if (this._activeRepositoryStack == null && this._repositoryStacks.size > 0) { this._setActiveRepositoryStack(Array.from(this._repositoryStacks.values())[0]); } this._updateDirtyChangedStatus(); this._updateSelectedFileChanges(); // Clear the active diff state if it was from a repo that's now removed. var filePath = this._activeFileState.filePath; if (filePath && !repositories.has((0, (_nuclideHgGitBridge2 || _nuclideHgGitBridge()).repositoryForPath)(filePath))) { (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)().info('Diff View\'s active buffer was belonging to a removed project.\n' + 'Clearing the UI state.'); this._activeSubscriptions.dispose(); this._setActiveFileState(getInitialFileChangeState()); } } }, { key: '_createRepositoryStack', value: function _createRepositoryStack(repository) { var _this2 = this; var repositoryStack = new (_RepositoryStack2 || _RepositoryStack()).default(repository, viewModeToDiffOption(this._state.viewMode)); var subscriptions = new (_atom2 || _atom()).CompositeDisposable(); subscriptions.add(repositoryStack.onDidUpdateDirtyFileChanges(this._updateDirtyChangedStatus.bind(this)), repositoryStack.onDidUpdateSelectedFileChanges(this._updateSelectedFileChanges.bind(this)), repositoryStack.onDidChangeRevisions(function (revisionsState) { _this2._updateChangedRevisions(repositoryStack, revisionsState, true).catch((_notifications2 || _notifications()).notifyInternalError); })); this._repositoryStacks.set(repository, repositoryStack); this._repositorySubscriptions.set(repository, subscriptions); if (this._isActive) { repositoryStack.activate(); } return repositoryStack; } }, { key: '_updateDirtyChangedStatus', value: function _updateDirtyChangedStatus() { var dirtyFileChanges = (0, (_commonsNodeCollection2 || _commonsNodeCollection()).mapUnion).apply(undefined, _toConsumableArray(Array.from(this._repositoryStacks.values()).map(function (repositoryStack) { return repositoryStack.getDirtyFileChanges(); }))); this._updateViewChangedFilesStatus(dirtyFileChanges); } }, { key: 'getActiveStackDirtyFileChanges', value: function getActiveStackDirtyFileChanges() { if (this._activeRepositoryStack == null) { return new Map(); } else { return this._activeRepositoryStack.getDirtyFileChanges(); } } }, { key: '_updateSelectedFileChanges', value: function _updateSelectedFileChanges() { var selectedFileChanges = (0, (_commonsNodeCollection2 || _commonsNodeCollection()).mapUnion).apply(undefined, _toConsumableArray(Array.from(this._repositoryStacks.values()).map(function (repositoryStack) { return repositoryStack.getSelectedFileChanges(); }))); this._updateViewChangedFilesStatus(null, selectedFileChanges); } }, { key: '_updateViewChangedFilesStatus', value: function _updateViewChangedFilesStatus(dirtyFileChanges_, selectedFileChanges_) { var _this3 = this; var dirtyFileChanges = dirtyFileChanges_; var selectedFileChanges = selectedFileChanges_; if (dirtyFileChanges == null) { dirtyFileChanges = this._state.dirtyFileChanges; } if (selectedFileChanges == null) { selectedFileChanges = this._state.selectedFileChanges; } var filteredFileChanges = undefined; var showNonHgRepos = undefined; var activeRepositorySelector = function activeRepositorySelector() { return true; }; if (this._activeRepositoryStack != null) { (function () { var projectDirectory = _this3._activeRepositoryStack.getRepository().getProjectDirectory(); activeRepositorySelector = function (filePath) { return (_commonsNodeNuclideUri2 || _commonsNodeNuclideUri()).default.contains(projectDirectory, filePath); }; })(); } switch (this._state.viewMode) { case (_constants2 || _constants()).DiffMode.COMMIT_MODE: case (_constants2 || _constants()).DiffMode.PUBLISH_MODE: // Commit mode only shows the changes of the active repository. filteredFileChanges = (0, (_commonsNodeCollection2 || _commonsNodeCollection()).mapFilter)(selectedFileChanges, activeRepositorySelector); // Publish mode only shows the changes of the active repository. filteredFileChanges = (0, (_commonsNodeCollection2 || _commonsNodeCollection()).mapFilter)(selectedFileChanges, activeRepositorySelector); showNonHgRepos = false; break; case (_constants2 || _constants()).DiffMode.BROWSE_MODE: // Broswe mode shows all changes from all repositories. filteredFileChanges = selectedFileChanges; showNonHgRepos = true; break; default: throw new Error('Unrecognized view mode!'); } this._setState(_extends({}, this._state, { dirtyFileChanges: dirtyFileChanges, selectedFileChanges: filteredFileChanges, showNonHgRepos: showNonHgRepos })); } }, { key: '_updateChangedRevisions', value: _asyncToGenerator(function* (repositoryStack, revisionsState, reloadFileDiffState) { if (repositoryStack !== this._activeRepositoryStack) { return; } (0, (_nuclideAnalytics2 || _nuclideAnalytics()).track)('diff-view-update-timeline-revisions', { revisionsCount: '' + revisionsState.revisions.length }); this._onUpdateRevisionsState(revisionsState); // Update the active file, if changed. var filePath = this._activeFileState.filePath; if (!filePath || !reloadFileDiffState) { return; } this._serializedUpdateActiveFileDiff(); }) }, { key: '_updateActiveFileDiff', value: _asyncToGenerator(function* () { var filePath = this._activeFileState.filePath; if (!filePath) { return; } // Capture the view state before the update starts. var _state = this._state; var viewMode = _state.viewMode; var commitMode = _state.commitMode; var _ref5 = yield this._fetchFileDiff(filePath); var committedContents = _ref5.committedContents; var filesystemContents = _ref5.filesystemContents; var revisionInfo = _ref5.revisionInfo; if (this._activeFileState.filePath !== filePath || this._state.viewMode !== viewMode || this._state.commitMode !== commitMode) { // The state have changed since the update started, and there must be another // scheduled update. Hence, we return early to allow it to go through. return; } yield this._updateDiffStateIfChanged(filePath, committedContents, filesystemContents, revisionInfo); }) }, { key: '_onUpdateRevisionsState', value: function _onUpdateRevisionsState(revisionsState) { this._emitter.emit(CHANGE_REVISIONS_EVENT, revisionsState); this._loadModeState(true); } }, { key: 'setPublishMessage', value: function setPublishMessage(publishMessage) { this._setState(_extends({}, this._state, { publishMessage: publishMessage })); } }, { key: 'setCommitMessage', value: function setCommitMessage(commitMessage) { this._setState(_extends({}, this._state, { commitMessage: commitMessage })); } }, { key: 'setViewMode', value: function setViewMode(viewMode) { var loadModeState = arguments.length <= 1 || arguments[1] === undefined ? true : arguments[1]; if (viewMode === this._state.viewMode) { return; } (0, (_nuclideAnalytics2 || _nuclideAnalytics()).track)('diff-view-switch-mode', { viewMode: viewMode }); this._setState(_extends({}, this._state, { viewMode: viewMode })); if (this._activeRepositoryStack != null) { this._activeRepositoryStack.setDiffOption(viewModeToDiffOption(this._state.viewMode)); } this._updateViewChangedFilesStatus(); if (loadModeState) { this._loadModeState(false); } this._serializedUpdateActiveFileDiff(); } }, { key: '_loadModeState', value: function _loadModeState(resetState) { if (resetState) { this._setState(_extends({}, this._state, { commitMessage: null, publishMessage: null })); } switch (this._state.viewMode) { case (_constants2 || _constants()).DiffMode.COMMIT_MODE: this._loadCommitModeState(); break; case (_constants2 || _constants()).DiffMode.PUBLISH_MODE: this._loadPublishModeState().catch((_notifications2 || _notifications()).notifyInternalError); break; } } }, { key: '_findFilePathToDiffInDirectory', value: function _findFilePathToDiffInDirectory(directoryPath) { var repositoryStack = this._getRepositoryStackForPath(directoryPath); var hgRepository = repositoryStack.getRepository(); var projectDirectory = hgRepository.getProjectDirectory(); function getMatchingFileChange(filePaths, parentPath) { return filePaths.filter(function (filePath) { return (_commonsNodeNuclideUri2 || _commonsNodeNuclideUri()).default.contains(parentPath, filePath); })[0]; } var dirtyFilePaths = Array.from(repositoryStack.getDirtyFileChanges().keys()); // Try to match dirty file changes in the selected directory, // Then lookup for changes in the project directory. var matchedFilePaths = [getMatchingFileChange(dirtyFilePaths, directoryPath), getMatchingFileChange(dirtyFilePaths, projectDirectory)]; return matchedFilePaths[0] || matchedFilePaths[1]; } }, { key: 'diffEntity', value: function diffEntity(entityOption) { var diffPath = null; if (entityOption.file != null) { diffPath = entityOption.file; } else if (entityOption.directory != null) { diffPath = this._findFilePathToDiffInDirectory(entityOption.directory); } if (diffPath == null) { var repository = (0, (_nuclideHgGitBridge2 || _nuclideHgGitBridge()).repositoryForPath)(entityOption.file || entityOption.directory || ''); if (repository != null && repository.getType() === 'hg' && this._repositoryStacks.has(repository)) { var repositoryStack = this._repositoryStacks.get(repository); (0, (_assert2 || _assert()).default)(repositoryStack); this._setActiveRepositoryStack(repositoryStack); } else if (this._activeRepositoryStack == null) { // This can only happen none of the project folders are Mercurial repositories. // However, this is caught earlier with a better error message. throw new Error('No active repository stack and non-diffable entity:' + JSON.stringify(entityOption)); } else { (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)().error('Non diffable entity:', entityOption); } } var viewMode = entityOption.viewMode; var commitMode = entityOption.commitMode; if (viewMode !== this._state.viewMode || commitMode !== this._state.commitMode) { if (viewMode === (_constants2 || _constants()).DiffMode.COMMIT_MODE) { (0, (_assert2 || _assert()).default)(commitMode, 'DIFF: Commit Mode not set!'); this.setViewMode((_constants2 || _constants()).DiffMode.COMMIT_MODE, false); this.setCommitMode(commitMode, false); this._loadModeState(true); } else if (viewMode) { this.setViewMode(viewMode); } } if (diffPath != null) { // Diff the file after setting the view mode to compare against the right thing. this._diffFilePath(diffPath); } } }, { key: '_diffFilePath', value: function _diffFilePath(filePath) { var _this4 = this; if (filePath === this._activeFileState.filePath) { return; } this._setActiveFileState(_extends({}, getInitialFileChangeState(), { filePath: filePath })); this._activeSubscriptions.dispose(); this._activeSubscriptions = new (_atom2 || _atom()).CompositeDisposable(); // TODO(most): Show progress indicator: t8991676 var buffer = (0, (_commonsAtomTextEditor2 || _commonsAtomTextEditor()).bufferForUri)(filePath); this._activeSubscriptions.add(buffer.onDidReload(function () { return _this4._onActiveBufferReload(filePath, buffer).catch((_notifications2 || _notifications()).notifyInternalError); })); this._activeSubscriptions.add(buffer.onDidDestroy(function () { (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)().info('Diff View\'s active buffer has been destroyed.\n' + 'The underlying file could have been removed.'); _this4._activeSubscriptions.dispose(); _this4._setActiveFileState(getInitialFileChangeState()); })); this._activeSubscriptions.add(buffer.onDidChangeModified(this.emitActiveBufferChangeModified.bind(this))); // Modified events could be late that it doesn't capture the latest edits / state changes. // Hence, it's safe to re-emit changes when stable from changes. this._activeSubscriptions.add(buffer.onDidStopChanging(this.emitActiveBufferChangeModified.bind(this))); (0, (_nuclideAnalytics2 || _nuclideAnalytics()).track)('diff-view-open-file', { filePath: filePath }); this._updateActiveDiffState(filePath).catch((_notifications2 || _notifications()).notifyInternalError); } }, { key: '_onActiveBufferReload', value: _asyncToGenerator(function* (filePath, buffer) { var _activeFileState = this._activeFileState; var committedContents = _activeFileState.oldContents; var revisionInfo = _activeFileState.compareRevisionInfo; if (revisionInfo == null) { // The file could be just loaded. return; } yield this._updateDiffStateIfChanged(filePath, committedContents, buffer.getText(), revisionInfo); }) }, { key: 'emitActiveBufferChangeModified', value: function emitActiveBufferChangeModified() { this._emitter.emit(ACTIVE_BUFFER_CHANGE_MODIFIED_EVENT); } }, { key: 'onDidActiveBufferChangeModified', value: function onDidActiveBufferChangeModified(callback) { return this._emitter.on(ACTIVE_BUFFER_CHANGE_MODIFIED_EVENT, callback); } }, { key: 'isActiveBufferModified', value: function isActiveBufferModified() { var filePath = this._activeFileState.filePath; var buffer = (0, (_commonsAtomTextEditor2 || _commonsAtomTextEditor()).bufferForUri)(filePath); return buffer.isModified(); } }, { key: '_updateDiffStateIfChanged', value: _asyncToGenerator(function* (filePath, committedContents, filesystemContents, revisionInfo) { if (this._activeFileState.filePath !== filePath) { return; } var updatedDiffState = { committedContents: committedContents, filesystemContents: filesystemContents, revisionInfo: revisionInfo }; return this._updateDiffState(filePath, updatedDiffState); }) }, { key: 'setNewContents', value: function setNewContents(newContents) { this._setActiveFileState(_extends({}, this._activeFileState, { newContents: newContents })); } }, { key: 'setRevision', value: function setRevision(revision) { (0, (_nuclideAnalytics2 || _nuclideAnalytics()).track)('diff-view-set-revision'); var repositoryStack = this._activeRepositoryStack; (0, (_assert2 || _assert()).default)(repositoryStack, 'There must be an active repository stack!'); this._activeFileState = _extends({}, this._activeFileState, { compareRevisionInfo: revision }); repositoryStack.setRevision(revision).catch((_notifications2 || _notifications()).notifyInternalError); } }, { key: 'getActiveFileState', value: function getActiveFileState() { return this._activeFileState; } }, { key: 'getPublishUpdates', value: function getPublishUpdates() { return this._publishUpdates; } }, { key: '_updateActiveDiffState', value: _asyncToGenerator(function* (filePath) { if (!filePath) { return; } var fileDiffState = yield this._fetchFileDiff(filePath); yield this._updateDiffState(filePath, fileDiffState); }) }, { key: '_updateDiffState', value: _asyncToGenerator(function* (filePath, fileDiffState) { var oldContents = fileDiffState.committedContents; var newContents = fileDiffState.filesystemContents; var revisionInfo = fileDiffState.revisionInfo; var hash = revisionInfo.hash; var bookmarks = revisionInfo.bookmarks; var newFileState = { filePath: filePath, oldContents: oldContents, newContents: newContents, compareRevisionInfo: revisionInfo, fromRevisionTitle: '' + hash + (bookmarks.length === 0 ? '' : ' - (' + bookmarks.join(', ') + ')'), toRevisionTitle: 'Filesystem / Editor' }; this._setActiveFileState(newFileState); // TODO(most): Fix: this assumes that the editor contents aren't changed while // fetching the comments, that's okay now because we don't fetch them. yield this._updateInlineComponents(); }) }, { key: '_setActiveFileState', value: function _setActiveFileState(state) { this._activeFileState = state; this._emitter.emit(ACTIVE_FILE_UPDATE_EVENT, this._activeFileState); } }, { key: '_fetchFileDiff', decorators: [(0, (_nuclideAnalytics2 || _nuclideAnalytics()).trackTiming)('diff-view.hg-state-update')], value: _asyncToGenerator(function* (filePath) { var repositoryStack = this._getRepositoryStackForPath(filePath); var _ref6 = yield Promise.all([repositoryStack.fetchHgDiff(filePath), this._setActiveRepositoryStack(repositoryStack)]); var _ref62 = _slicedToArray(_ref6, 1); var hgDiff = _ref62[0]; // Intentionally fetch the filesystem contents after getting the committed contents // to make sure we have the latest filesystem version. var buffer = yield (0, (_commonsAtomTextEditor2 || _commonsAtomTextEditor()).loadBufferForUri)(filePath); return _extends({}, hgDiff, { filesystemContents: buffer.getText() }); }) }, { key: '_getRepositoryStackForPath', value: function _getRepositoryStackForPath(filePath) { var hgRepository = hgRepositoryForPath(filePath); var repositoryStack = this._repositoryStacks.get(hgRepository); (0, (_assert2 || _assert()).default)(repositoryStack, 'There must be an repository stack for a given repository!'); return repositoryStack; } }, { key: '_setActiveRepositoryStack', value: _asyncToGenerator(function* (repositoryStack) { if (this._activeRepositoryStack === repositoryStack) { return; } this._activeRepositoryStack = repositoryStack; repositoryStack.setDiffOption(viewModeToDiffOption(this._state.viewMode)); if (!this._isActive) { return; } var revisionsState = yield repositoryStack.getCachedRevisionsStatePromise(); this._updateChangedRevisions(repositoryStack, revisionsState, false); }) }, { key: 'saveActiveFile', decorators: [(0, (_nuclideAnalytics2 || _nuclideAnalytics()).trackTiming)('diff-view.save-file')], value: function saveActiveFile() { var filePath = this._activeFileState.filePath; (0, (_nuclideAnalytics2 || _nuclideAnalytics()).track)('diff-view-save-file', { filePath: filePath }); return this._saveFile(filePath).catch((_notifications2 || _notifications()).notifyInternalError); } }, { key: 'publishDiff', decorators: [(0, (_nuclideAnalytics2 || _nuclideAnalytics()).trackTiming)('diff-view.publish-diff')], value: _asyncToGenerator(function* (publishMessage) { this._setState(_extends({}, this._state, { publishMessage: publishMessage, publishModeState: (_constants2 || _constants()).PublishModeState.AWAITING_PUBLISH })); var publishMode = this._state.publishMode; (0, (_nuclideAnalytics2 || _nuclideAnalytics()).track)('diff-view-publish', { publishMode: publishMode }); var commitMessage = publishMode === (_constants2 || _constants()).PublishMode.CREATE ? publishMessage : null; var cleanResult = undefined; try { cleanResult = yield this._promptToCleanDirtyChanges(commitMessage); } catch (error) { atom.notifications.addError('Error clearning dirty changes', { detail: error.message, dismissable: true, nativeFriendly: true }); } if (cleanResult == null) { this._setState(_extends({}, this._state, { publishModeState: (_constants2 || _constants()).PublishModeState.READY })); return; } var _cleanResult = cleanResult; var amended = _cleanResult.amended; var allowUntracked = _cleanResult.allowUntracked; try { switch (publishMode) { case (_constants2 || _constants()).PublishMode.CREATE: // Create uses `verbatim` and `n` answer buffer // and that implies that untracked files will be ignored. var createdPhabricatorRevision = yield this._createPhabricatorRevision(publishMessage, amended); notifyRevisionStatus(createdPhabricatorRevision, 'created'); break; case (_constants2 || _constants()).PublishMode.UPDATE: var updatedPhabricatorRevision = yield this._updatePhabricatorRevision(publishMessage, allowUntracked); notifyRevisionStatus(updatedPhabricatorRevision, 'updated'); break; default: throw new Error('Unknown publish mode \'' + publishMode + '\''); } // Populate Publish UI with the most recent data after a successful push. this._setState(_extends({}, this._state, { publishModeState: (_constants2 || _constants()).PublishModeState.READY })); this.setViewMode((_constants2 || _constants()).DiffMode.BROWSE_MODE); } catch (error) { atom.notifications.addError('Couldn\'t Publish to Phabricator', { detail: error.message, nativeFriendly: true }); this._setState(_extends({}, this._state, { publishModeState: (_constants2 || _constants()).PublishModeState.PUBLISH_ERROR })); } }) }, { key: '_promptToCleanDirtyChanges', value: _asyncToGenerator(function* (commitMessage) { var activeStack = this._activeRepositoryStack; (0, (_assert2 || _assert()).default)(activeStack != null, 'No active repository stack when cleaning dirty changes'); var hgRepo = activeStack.getRepository(); var checkingStatusNotification = atom.notifications.addInfo('Running `hg status` to check dirty changes to Add/Amend/Revert', { dismissable: true }); yield hgRepo.getStatuses([hgRepo.getProjectDirectory()]); checkingStatusNotification.dismiss(); var dirtyFileChanges = activeStack.getDirtyFileChanges(); var shouldAmend = false; var amended = false; var allowUntracked = false; if (dirtyFileChanges.size === 0) { return { amended: amended, allowUntracked: allowUntracked }; } var untrackedChanges = new Map(Array.from(dirtyFileChanges.entries()).filter(function (fileChange) { return fileChange[1] === (_constants2 || _constants()).FileChangeStatus.UNTRACKED; })); if (untrackedChanges.size > 0) { var untrackedChoice = atom.confirm({ message: 'You have untracked files in your working copy:', detailedMessage: getFileStatusListMessage(untrackedChanges), buttons: ['Cancel', 'Add', 'Allow Untracked'] }); (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)().info('Untracked changes choice:', untrackedChoice); if (untrackedChoice === 0) /* Cancel */{ return null; } else if (untrackedChoice === 1) /* Add */{ yield activeStack.addAll(Array.from(untrackedChanges.keys())); shouldAmend = true; } else if (untrackedChoice === 2) /* Allow Untracked */{ allowUntracked = true; } } var revertableChanges = new Map(Array.from(dirtyFileChanges.entries()).filter(function (fileChange) { return fileChange[1] !== (_constants2 || _constants()).FileChangeStatus.UNTRACKED; })); if (revertableChanges.size > 0) { var cleanChoice = atom.confirm({ message: 'You have uncommitted changes in your working copy:', detailedMessage: getFileStatusListMessage(revertableChanges), buttons: ['Cancel', 'Revert', 'Amend'] }); (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)().info('Dirty changes clean choice:', cleanChoice); if (cleanChoice === 0) /* Cancel */{ return null; } else if (cleanChoice === 1) /* Revert */{ var canRevertFilePaths = Array.from(dirtyFileChanges.entries()).filter(function (fileChange) { return fileChange[1] !== (_constants2 || _constants()).FileChangeStatus.UNTRACKED; }).map(function (fileChange) { return fileChange[0]; }); yield activeStack.revert(canRevertFilePaths); } else if (cleanChoice === 2) /* Amend */{ shouldAmend = true; } } if (shouldAmend) { yield activeStack.amend(commitMessage); amended = true; } return { amended: amended, allowUntracked: allowUntracked }; }) }, { key: '_getArcanistFilePath', value: function _getArcanistFilePath() { var filePath = this._activeFileState.filePath; if (filePath === '' && this._activeRepositoryStack != null) { filePath = this._activeRepositoryStack.getRepository().getProjectDirectory(); } return filePath; } }, { key: '_createPhabricatorRevision', value: _asyncToGenerator(function* (publishMessage, amended) { var filePath = this._getArcanistFilePath(); var lastCommitMessage = yield this._loadActiveRepositoryLatestCommitMessage(); var activeRepositoryStack = this._activeRepositoryStack; (0, (_assert2 || _assert()).default)(activeRepositoryStack, 'No active repository stack'); if (!amended && publishMessage !== lastCommitMessage) { (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)().info('Amending commit with the updated message'); yield activeRepositoryStack.amend(publishMessage); atom.notifications.addSuccess('Commit amended with the updated message'); } this._publishUpdates.next({ level: 'log', text: 'Creating new revision...\n' }); var stream = (0, (_nuclideRemoteConnection2 || _nuclideRemoteConnection()).getArcanistServiceByNuclideUri)(filePath).createPhabricatorRevision(filePath).refCount(); yield this._processArcanistOutput(stream); var asyncHgRepo = activeRepositoryStack.getRepository().async; var headCommitMessagePromise = asyncHgRepo.getHeadCommitMessage(); // Invalidate the current revisions state because the current commit info has changed. activeRepositoryStack.getRevisionsStatePromise(); var commitMessage = yield headCommitMessagePromise; if (commitMessage == null) { return null; } return (0, (_nuclideArcanistRpcLibUtils2 || _nuclideArcanistRpcLibUtils()).getPhabricatorRevisionFromCommitMessage)(commitMessage); }) }, { key: '_updatePhabricatorRevision', value: _asyncToGenerator(function* (publishMessage, allowUntracked) { var filePath = this._getArcanistFilePath(); var _ref7 = yield this._getActiveHeadRevisionDetails(); var phabricatorRevision = _ref7.phabricatorRevision; (0, (_assert2 || _assert()).default)(phabricatorRevision != null, 'A phabricator revision must exist to update!'); var updateTemplate = getRevisionUpdateMessage(phabricatorRevision).trim(); var userUpdateMessage = publishMessage.replace(updateTemplate, '').trim(); if (userUpdateMessage.length === 0) { throw new Error('Cannot update revision with empty message'); } this._publishUpdates.next({ level: 'log', text: 'Updating revision `' + phabricatorRevision.name + '`...\n' }); var stream = (0, (_nuclideRemoteConnection2 || _nuclideRemoteConnection()).getArcanistServiceByNuclideUri)(filePath).updatePhabricatorRevision(filePath, userUpdateMessage, allowUntracked).refCount(); yield this._processArcanistOutput(stream); return phabricatorRevision; }) }, { key: '_processArcanistOutput', value: _asyncToGenerator(function* (stream_) { var _default$Observable, _this5 = this; var stream = stream_; var fatalError = false; stream = stream // Split stream into single lines. .flatMap(function (message) { var lines = []; for (var fd of ['stderr', 'stdout']) { var out = message[fd]; if (out != null) { out = out.replace(/\n$/, ''); for (var line of out.split('\n')) { lines.push(_defineProperty({}, fd, line)); } } } return lines; }) // Unpack JSON .flatMap(function (message) { var stdout = message.stdout; var messages = []; if (stdout != null) { var decodedJSON = null; try { decodedJSON = JSON.parse(stdout); } catch (err) { messages.push({ type: 'phutil:out', message: stdout + '\n' }); (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)().error('Invalid JSON encountered: ' + stdout); } if (decodedJSON != null) { messages.push(decodedJSON); } } if (message.stderr != null) { messages.push({ type: 'phutil:err', message: message.stderr + '\n' }); } return messages; }) // Process message type. .flatMap(function (decodedJSON) { var messages = []; switch (decodedJSON.type) { case 'phutil:out': case 'phutil:out:raw': messages.push({ level: 'log', text: decodedJSON.message }); break; case 'phutil:err': messages.push({ level: 'error', text: decodedJSON.message }); break; case 'error': messages.push({ level: 'error', text: decodedJSON.message }); fatalError = true; break; default: (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)().info('Unhandled message type:', decodedJSON.type, 'Message payload:', decodedJSON.message); break; } return messages; }) // Split messages on new line characters. .flatMap(function (message) { var splitMessages = []; // Split on newlines without removing new line characters. This will remove empty // strings but that's OK. for (var part of message.text.split(/^/m)) { splitMessages.push({ level: message.level, text: part }); } return splitMessages; }); var levelStreams = []; var _loop = function (_level) { var levelStream = stream.filter(function (message) { return message.level === _level; }).share(); levelStreams.push((0, (_commonsNodeStream2 || _commonsNodeStream()).bufferUntil)(levelStream, function (message) { return message.text.endsWith('\n'); })); }; for (var _level of ['log', 'error']) { _loop(_level); } yield (_default$Observable = (_rxjsBundlesRxUmdMinJs2 || _rxjsBundlesRxUmdMinJs()).default.Observable).merge.apply(_default$Observable, levelStreams).do(function (messages) { if (messages.length > 0) { _this5._publishUpdates.next({ level: messages[0].level, text: messages.map(function (message) { return message.text; }).join('') }); } }).toPromise().catch(function (error) { fatalError = true; }); if (fatalError) { throw new Error('Failed publish to Phabricator\n' + 'You could have missed test plan or mistyped reviewers.\n' + 'Please fix and try again.'); } }) }, { key: '_saveFile', value: _asyncToGenerator(function* (filePath) { var buffer = (0, (_commonsAtomTextEditor2 || _commonsAtomTextEditor()).bufferForUri)(filePath); if (buffer == null) { throw new Error('Could not find file buffer to save: `' + filePath + '`'); } try { yield buffer.save(); } catch (err) { throw new Error('Could not save file buffer: `' + filePath + '` - ' + err.toString()); } }) }, { key: 'onDidUpdateState', value: function onDidUpdateState(callback) { return this._emitter.on(DID_UPDATE_STATE_EVENT, callback); } }, { key: 'onRevisionsUpdate', value: function onRevisionsUpdate(callback) { return this._emitter.on(CHANGE_REVISIONS_EVENT, callback); } }, { key: 'onActiveFileUpdates', value: function onActiveFileUpdates(callback) { return this._emitter.on(ACTIVE_FILE_UPDATE_EVENT, callback); } }, { key: '_updateInlineComponents', value: _asyncToGenerator(function* () { var filePath = this._activeFileState.filePath; if (!filePath) { return; } var inlineComponents = yield this._fetchInlineComponents(filePath); if (filePath !== this._activeFileState.filePath) { return; } this._setActiveFileState(_extends({}, this._activeFileState, { inlineComponents: inlineComponents })); }) }, { key: '_fetchInlineComponents', decorators: [(0, (_nuclideAnalytics2 || _nuclideAnalytics()).trackTiming)('diff-view.fetch-comments')], value: _asyncToGenerator(function* (filePath) { // TODO(most): Fix UI rendering and re-introduce: t8174332 // provider.composeUiElements(filePath) var uiElementPromises = this._uiProviders.map(function (provider) { return Promise.resolve([]); }); var uiComponentLists = yield Promise.all(uiElementPromises); // Flatten uiComponentLists from list of lists of components to a list of components. var uiComponents = [].concat.apply([], uiComponentLists); return uiComponents; }) }, { key: 'setUiProviders', value: function setUiProviders(uiProviders) { this._uiProviders = uiProviders; this._updateInlineComponents().catch((_notifications2 || _notifications()).notifyInternalError); } }, { key: '_loadCommitModeState', value: _asyncToGenerator(function* () { this._setState(_extends({}, this._state, { commitModeState: (_constants2 || _constants()).CommitModeState.LOADING_COMMIT_MESSAGE })); var commitMessage = null; try { if (this._state.commitMessage != null) { commitMessage = this._state.commitMessage; } else if (this._state.commitMode === (_constants2 || _constants()).CommitMode.COMMIT) { commitMessage = yield this._loadActiveRepositoryTemplateCommitMessage(); } else { commitMessage = yield this._loadActiveRepositoryLatestCommitMessage(); } } catch (error) { (0, (_notifications2 || _notifications()).notifyInternalError)(error); } finally { this._setState(_extends({}, this._state, { commitMessage: commitMessage, commitModeState: (_constants2 || _constants()).CommitModeState.READY })); } }) }, { key: '_loadPublishModeState', value: _asyncToGenerator(function* () { if (this._state.publishModeState === (_constants2 || _constants()).PublishModeState.AWAITING_PUBLISH) { // That must be an a update triggered by an `amend` operation, // done as part of diffing. return; } var publishMessage = this._state.publishMessage; this._setState(_extends({}, this._state, { publishMode: (_constants2 || _constants()).PublishMode.CREATE, publishModeState: (_constants2 || _constants()).PublishModeState.LOADING_PUBLISH_MESSAGE, publishMessage: null, headRevision: null })); var _ref8 = yield this._getActiveHeadRevisionDetails(); var headRevision = _ref8.headRevision; var phabricatorRevision = _ref8.phabricatorRevision; if (publishMessage == null || publishMessage.length === 0) { publishMessage = phabricatorRevision != null ? getRevisionUpdateMessage(phabricatorRevision) : headRevision.description; } this._setState(_extends({}, this._state, { publishMode: phabricatorRevision != null ? (_constants2 || _constants()).PublishMode.UPDATE : (_constants2 || _constants()).PublishMode.CREATE, publishModeState: (_constants2 || _constants()).PublishModeState.READY, publishMessage: publishMessage, headRevision: headRevision })); }) }, { key: '_getActiveHeadRevisionDetails', value: _asyncToGenerator(function* () { var revisionsState = yield this.getActiveRevisionsState(); if (revisionsState == null) { throw new Error('Cannot Load Publish View: No active file or repository'); } var revisions = revisi