diff --git a/DEVELOPMENT b/DEVELOPMENT deleted file mode 100644 index e69de29b..00000000 diff --git a/index.js b/index.js index 951b2196..8cf8d1c3 100644 --- a/index.js +++ b/index.js @@ -1,70 +1,80 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -/* eslint-disable rulesdir/no-commonjs */ +var _fs = _interopRequireDefault(require('fs')); + +var _path = _interopRequireDefault(require('path')); + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _FeatureLoader; + +function _load_FeatureLoader() { + return _FeatureLoader = _interopRequireDefault(require('nuclide-commons-atom/FeatureLoader')); +} + +var _displayNuclideWarning; + +function _load_displayNuclideWarning() { + return _displayNuclideWarning = _interopRequireDefault(require('./display-nuclide-warning')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -import fs from 'fs'; // eslint-disable-next-line rulesdir/prefer-nuclide-uri -import path from 'path'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import FeatureLoader from 'nuclide-commons-atom/FeatureLoader'; -import displayNuclideWarning from './display-nuclide-warning'; +const featureDir = _path.default.join(__dirname, 'modules/atom-ide-ui/pkg'); /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ -const featureDir = path.join(__dirname, 'modules/atom-ide-ui/pkg'); -const features = fs - .readdirSync(featureDir) - .map(item => { - const dirname = path.join(featureDir, item); - try { - const pkgJson = fs.readFileSync( - path.join(dirname, 'package.json'), - 'utf8', - ); - return { - path: dirname, - pkg: JSON.parse(pkgJson), - }; - } catch (err) { - if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { - throw err; - } +/* eslint-disable rulesdir/no-commonjs */ + +const features = _fs.default.readdirSync(featureDir).map(item => { + const dirname = _path.default.join(featureDir, item); + try { + const pkgJson = _fs.default.readFileSync(_path.default.join(dirname, 'package.json'), 'utf8'); + return { + path: dirname, + pkg: JSON.parse(pkgJson) + }; + } catch (err) { + if (err.code !== 'ENOENT' && err.code !== 'ENOTDIR') { + throw err; } - }) - .filter(Boolean); + } +}).filter(Boolean); /** * Use a unified package loader to load all the feature packages. * See the following post for more context: * https://nuclide.io/blog/2016/01/13/Nuclide-v0.111.0-The-Unified-Package/ */ -let disposables: ?UniversalDisposable; -const featureLoader = new FeatureLoader({ +let disposables; +const featureLoader = new (_FeatureLoader || _load_FeatureLoader()).default({ path: __dirname, config: {}, - features, + features }); featureLoader.load(); module.exports = { config: featureLoader.getConfig(), activate() { - disposables = new UniversalDisposable( - require('nuclide-commons-ui'), - atom.packages.onDidActivatePackage(pkg => { - if (pkg.name === 'nuclide') { - displayNuclideWarning(); - } - }), - ); + disposables = new (_UniversalDisposable || _load_UniversalDisposable()).default(require('nuclide-commons-ui'), atom.packages.onDidActivatePackage(pkg => { + if (pkg.name === 'nuclide') { + (0, (_displayNuclideWarning || _load_displayNuclideWarning()).default)(); + } + })); featureLoader.activate(); }, deactivate() { @@ -76,5 +86,5 @@ module.exports = { }, serialize() { featureLoader.serialize(); - }, -}; + } +}; \ No newline at end of file diff --git a/modules/atom-ide-ui/index.js b/modules/atom-ide-ui/index.js index 93932742..a726efc4 100644 --- a/modules/atom-ide-ui/index.js +++ b/modules/atom-ide-ui/index.js @@ -1,108 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -export type { - BusySignalOptions, - BusySignalService, -} from './pkg/atom-ide-busy-signal/lib/types'; - -export type { - CodeAction, - CodeActionProvider, -} from './pkg/atom-ide-code-actions/lib/types'; - -export type { - CodeFormatProvider, - RangeCodeFormatProvider, - FileCodeFormatProvider, - OnTypeCodeFormatProvider, - OnSaveCodeFormatProvider, -} from './pkg/atom-ide-code-format/lib/types'; - -export type { - CodeHighlightProvider, -} from './pkg/atom-ide-code-highlight/lib/types'; - -export type { - Datatip, - DatatipProvider, - DatatipService, - MarkedString, - ModifierDatatipProvider, - ModifierKey, -} from './pkg/atom-ide-datatip/lib/types'; - -export type { - Definition, - DefinitionProvider, - DefinitionPreviewProvider, - DefinitionQueryResult, -} from './pkg/atom-ide-definitions/lib/types'; - -export type { - CallbackDiagnosticProvider, - DiagnosticFix, - DiagnosticInvalidationCallback, - DiagnosticInvalidationMessage, - DiagnosticMessage, - DiagnosticMessages, - DiagnosticMessageKind, - DiagnosticMessageType, - DiagnosticProvider, - DiagnosticProviderUpdate, - DiagnosticTrace, - DiagnosticUpdateCallback, - IndieLinterDelegate, - LinterMessage, - LinterMessageV1, - LinterMessageV2, - LinterProvider, - LinterTrace, - ObservableDiagnosticProvider, - RegisterIndieLinter, -} from './pkg/atom-ide-diagnostics/lib/types'; - -export type { - FindReferencesProvider, - FindReferencesReturn, - Reference, -} from './pkg/atom-ide-find-references/lib/types'; - -export type { - Outline, - OutlineProvider, - OutlineTree, - ResultsStreamProvider, -} from './pkg/atom-ide-outline-view/lib/types'; - -export type { - Signature, - SignatureHelp, - SignatureHelpProvider, - SignatureParameter, -} from './pkg/atom-ide-signature-help/lib/types'; - -export type { - HyperclickProvider, - HyperclickSuggestion, -} from './pkg/hyperclick/lib/types'; - -export type { - ConsoleApi, - ConsoleService, - Level as ConsoleLevel, - Message as ConsoleMessage, - OutputProviderStatus, - OutputService, - RegisterExecutorFunction, - SourceInfo as ConsoleSourceInfo, -} from './pkg/atom-ide-console/lib/types'; +'use strict'; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusyMessageInstance.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusyMessageInstance.js index cec5474a..edf2de1e 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusyMessageInstance.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusyMessageInstance.js @@ -1,41 +1,29 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -import type {BusyMessage} from './types'; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.BusyMessageInstance = undefined; -import invariant from 'assert'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; +var _UniversalDisposable; -export class BusyMessageInstance { - // These things are set at construction-time: - _publishCallback: () => void; - _creationOrder: number; - _waitingFor: 'computer' | 'user'; - _onDidClick: ?() => void; - _disposables: UniversalDisposable; - _titleElement: HTMLElement = document.createElement('span'); +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +class BusyMessageInstance { // These things might be modified afterwards: - _currentTitle: ?string = null; - _isVisibleForDebounce: boolean = true; - _isVisibleForFile: boolean = true; - _revealTooltip: boolean = false; - - constructor( - publishCallback: () => void, - creationOrder: number, - waitingFor: 'computer' | 'user', - onDidClick: ?() => void, - disposables: UniversalDisposable, - ) { + + // These things are set at construction-time: + constructor(publishCallback, creationOrder, waitingFor, onDidClick, disposables) { + this._titleElement = document.createElement('span'); + this._currentTitle = null; + this._isVisibleForDebounce = true; + this._isVisibleForFile = true; + this._revealTooltip = false; + this._publishCallback = publishCallback; this._creationOrder = creationOrder; this._waitingFor = waitingFor; @@ -43,12 +31,15 @@ export class BusyMessageInstance { this._disposables = disposables; } - get waitingFor(): 'computer' | 'user' { + get waitingFor() { return this._waitingFor; } - setTitle(val: string): void { - invariant(!this._disposables.disposed); + setTitle(val) { + if (!!this._disposables.disposed) { + throw new Error('Invariant violation: "!this._disposables.disposed"'); + } + if (this._currentTitle === val) { return; } @@ -69,49 +60,66 @@ export class BusyMessageInstance { } } - getTitleElement(): ?HTMLElement { + getTitleElement() { return this._titleElement; } - setIsVisibleForDebounce(val: boolean): void { - invariant(!this._disposables.disposed); + setIsVisibleForDebounce(val) { + if (!!this._disposables.disposed) { + throw new Error('Invariant violation: "!this._disposables.disposed"'); + } + this._isVisibleForDebounce = val; this._publishCallback(); } - setIsVisibleForFile(val: boolean): void { - invariant(!this._disposables.disposed); + setIsVisibleForFile(val) { + if (!!this._disposables.disposed) { + throw new Error('Invariant violation: "!this._disposables.disposed"'); + } + this._isVisibleForFile = val; this._publishCallback(); } - isVisible(): boolean { - invariant(!this._disposables.disposed); - return ( - this._isVisibleForFile && - this._isVisibleForDebounce && - this._currentTitle != null - ); + isVisible() { + if (!!this._disposables.disposed) { + throw new Error('Invariant violation: "!this._disposables.disposed"'); + } + + return this._isVisibleForFile && this._isVisibleForDebounce && this._currentTitle != null; } - setRevealTooltip(val: boolean): void { + setRevealTooltip(val) { this._revealTooltip = val; } - shouldRevealTooltip(): boolean { + shouldRevealTooltip() { return this._revealTooltip; } - compare(that: BusyMessageInstance): number { + compare(that) { return this._creationOrder - that._creationOrder; } - dispose(): void { + dispose() { this._disposables.dispose(); this._currentTitle = null; this._publishCallback(); } } -// This is how we declare that a type fulfills an interface in Flow: -(((null: any): BusyMessageInstance): BusyMessage); +exports.BusyMessageInstance = BusyMessageInstance; // This is how we declare that a type fulfills an interface in Flow: +/** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +null; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusySignalSingleton.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusySignalSingleton.js index a736a344..f3baef31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusySignalSingleton.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/BusySignalSingleton.js @@ -1,3 +1,13 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,23 +16,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {BusySignalOptions, BusyMessage} from './types'; -import type {MessageStore} from './MessageStore'; - -export default class BusySignalSingleton { - _messageStore: MessageStore; +class BusySignalSingleton { - constructor(messageStore: MessageStore) { + constructor(messageStore) { this._messageStore = messageStore; } dispose() {} - reportBusy(title: string, options?: BusySignalOptions): BusyMessage { + reportBusy(title, options) { return this._messageStore.add(title, options || {}); } @@ -33,16 +39,17 @@ export default class BusySignalSingleton { * Used to indicate that some work is ongoing while the given asynchronous * function executes. */ - async reportBusyWhile( - title: string, - f: () => Promise, - options?: BusySignalOptions, - ): Promise { - const busySignal = this.reportBusy(title, options); - try { - return await f(); - } finally { - busySignal.dispose(); - } + reportBusyWhile(title, f, options) { + var _this = this; + + return (0, _asyncToGenerator.default)(function* () { + const busySignal = _this.reportBusy(title, options); + try { + return yield f(); + } finally { + busySignal.dispose(); + } + })(); } } +exports.default = BusySignalSingleton; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/MessageStore.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/MessageStore.js index 7a54f563..12c4966c 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/MessageStore.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/MessageStore.js @@ -1,3 +1,36 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.MessageStore = undefined; + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _collection; + +function _load_collection() { + return _collection = require('nuclide-commons/collection'); +} + +var _BusyMessageInstance; + +function _load_BusyMessageInstance() { + return _BusyMessageInstance = require('./BusyMessageInstance'); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// The "busy debounce delay" is for busy messages that were created with the +// 'debounce' option set to true. The icon and tooltip message won't appear +// until this many milliseconds have elapsed; if the busy message gets disposed +// before this time, then the user won't see anything. /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,100 +39,87 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {BusyMessage, BusySignalOptions} from './types'; - -import invariant from 'assert'; -import {Observable, BehaviorSubject} from 'rxjs'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {arrayEqual} from 'nuclide-commons/collection'; -import {BusyMessageInstance} from './BusyMessageInstance'; - -// The "busy debounce delay" is for busy messages that were created with the -// 'debounce' option set to true. The icon and tooltip message won't appear -// until this many milliseconds have elapsed; if the busy message gets disposed -// before this time, then the user won't see anything. const BUSY_DEBOUNCE_DELAY = 300; -export class MessageStore { - _counter: number = 0; - _messages: Set = new Set(); - _currentVisibleMessages: Array = []; - _messageStream: BehaviorSubject< - Array, - > = new BehaviorSubject([]); +class MessageStore { + constructor() { + this._counter = 0; + this._messages = new Set(); + this._currentVisibleMessages = []; + this._messageStream = new _rxjsBundlesRxMinJs.BehaviorSubject([]); + } - getMessageStream(): Observable> { + getMessageStream() { return this._messageStream; } - dispose(): void { + dispose() { const messagesToDispose = [...this._messages]; for (const message of messagesToDispose) { message.dispose(); } - invariant(this._messages.size === 0); + + if (!(this._messages.size === 0)) { + throw new Error('Invariant violation: "this._messages.size === 0"'); + } + this._messageStream.complete(); } - _publish(): void { - const visibleMessages = [...this._messages] - .filter(m => m.isVisible()) - .sort((m1, m2) => m1.compare(m2)); + _publish() { + const visibleMessages = [...this._messages].filter(m => m.isVisible()).sort((m1, m2) => m1.compare(m2)); // We only send out on messageStream when the list of visible // BusyMessageInstance object identities has changed, e.g. when ones // are made visible or invisible or new ones are created. We don't send // out just on title change. - if (!arrayEqual(this._currentVisibleMessages, visibleMessages)) { + if (!(0, (_collection || _load_collection()).arrayEqual)(this._currentVisibleMessages, visibleMessages)) { this._messageStream.next(visibleMessages); this._currentVisibleMessages = visibleMessages; } } - add(title: string, options: BusySignalOptions): BusyMessage { + add(title, options) { this._counter++; const creationOrder = this._counter; - const waitingFor = - options != null && options.waitingFor != null - ? options.waitingFor - : 'computer'; + const waitingFor = options != null && options.waitingFor != null ? options.waitingFor : 'computer'; const onDidClick = options == null ? null : options.onDidClick; - const messageDisposables = new UniversalDisposable(); - - const message = new BusyMessageInstance( - this._publish.bind(this), - creationOrder, - waitingFor, - onDidClick, - messageDisposables, - ); + const messageDisposables = new (_UniversalDisposable || _load_UniversalDisposable()).default(); + + const message = new (_BusyMessageInstance || _load_BusyMessageInstance()).BusyMessageInstance(this._publish.bind(this), creationOrder, waitingFor, onDidClick, messageDisposables); this._messages.add(message); messageDisposables.add(() => this._messages.delete(message)); // debounce defaults 'true' for busy-signal, and 'false' for action-required - const debounceRaw: ?boolean = options == null ? null : options.debounce; - const debounce: boolean = - debounceRaw == null ? waitingFor === 'computer' : debounceRaw; + const debounceRaw = options == null ? null : options.debounce; + const debounce = debounceRaw == null ? waitingFor === 'computer' : debounceRaw; if (debounce) { message.setIsVisibleForDebounce(false); // After the debounce time, we'll check whether the messageId is still // around (i.e. hasn't yet been disposed), and if so we'll display it. - let timeoutId = ((0: any): TimeoutID); + let timeoutId = 0; const teardown = () => clearTimeout(timeoutId); timeoutId = setTimeout(() => { - invariant(!messageDisposables.disposed); - invariant(this._messages.has(message)); + if (!!messageDisposables.disposed) { + throw new Error('Invariant violation: "!messageDisposables.disposed"'); + } + + if (!this._messages.has(message)) { + throw new Error('Invariant violation: "this._messages.has(message)"'); + } // If the message was disposed, then it should have already called // clearTimeout, so this timeout handler shouldn't have been invoked. // And also the message should have been removed from this._messages. // So both tests above should necessary fail. // If the messageStore was disposed, then every message in it should // already have been disposed, as per above. + + messageDisposables.remove(teardown); message.setIsVisibleForDebounce(true); }, BUSY_DEBOUNCE_DELAY); @@ -110,10 +130,7 @@ export class MessageStore { message.setIsVisibleForFile(false); const file = options.onlyForFile; const teardown = atom.workspace.observeActivePaneItem(item => { - const activePane = - item != null && typeof item.getPath === 'function' - ? String(item.getPath()) - : null; + const activePane = item != null && typeof item.getPath === 'function' ? String(item.getPath()) : null; const newVisible = activePane === file; message.setIsVisibleForFile(newVisible); }); @@ -133,3 +150,4 @@ export class MessageStore { return message; } } +exports.MessageStore = MessageStore; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/StatusBarTile.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/StatusBarTile.js index 5c5aafc5..4ab78c61 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/StatusBarTile.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/StatusBarTile.js @@ -1,3 +1,43 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _react = _interopRequireWildcard(require('react')); + +var _reactDom = _interopRequireDefault(require('react-dom')); + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _collection; + +function _load_collection() { + return _collection = require('nuclide-commons/collection'); +} + +var _Icon; + +function _load_Icon() { + return _Icon = require('nuclide-commons-ui/Icon'); +} + +var _BusyMessageInstance; + +function _load_BusyMessageInstance() { + return _BusyMessageInstance = require('./BusyMessageInstance'); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +// We want to be the furthest left on the right side of the status bar so as not to leave a +// conspicuous gap (or cause jitter) when nothing is busy. /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,70 +46,47 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {Observable} from 'rxjs'; - -import invariant from 'assert'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {arrayCompact} from 'nuclide-commons/collection'; -import {Icon} from 'nuclide-commons-ui/Icon'; -import {BusyMessageInstance} from './BusyMessageInstance'; - -// We want to be the furthest left on the right side of the status bar so as not to leave a -// conspicuous gap (or cause jitter) when nothing is busy. const STATUS_BAR_PRIORITY = 1000; -type Props = { - waitingForComputer: boolean, - waitingForUser: boolean, - onDidClick: ?() => void, -}; - -function StatusBarTileComponent(props: Props) { +function StatusBarTileComponent(props) { let element; if (props.waitingForUser) { - element = ; + element = _react.createElement((_Icon || _load_Icon()).Icon, { className: 'busy-signal-status-bar', icon: 'unverified' }); } else if (props.waitingForComputer) { - element =
; + element = _react.createElement('div', { className: 'busy-signal-status-bar loading-spinner-tiny' }); } else { element = null; } if (props.onDidClick != null) { - element = {element}; + element = _react.createElement( + 'a', + { onClick: props.onDidClick }, + element + ); } return element; } -export default class StatusBarTile { - _item: HTMLElement; - _tile: atom$StatusBarTile; - _tooltip: ?IDisposable; - _disposables: UniversalDisposable; - _messages: Array = []; - _isMouseOverItem: boolean = false; - _isMouseOverTooltip: number = 0; - _leaveTimeoutId: ?TimeoutID; - - constructor( - statusBar: atom$StatusBar, - messageStream: Observable>, - ) { +class StatusBarTile { + + constructor(statusBar, messageStream) { + this._messages = []; + this._isMouseOverItem = false; + this._isMouseOverTooltip = 0; + this._item = document.createElement('div'); this._tile = this._createTile(statusBar); - this._disposables = new UniversalDisposable( - messageStream.subscribe(messages => this._handleMessages(messages)), - ); + this._disposables = new (_UniversalDisposable || _load_UniversalDisposable()).default(messageStream.subscribe(messages => this._handleMessages(messages))); } - dispose(): void { - ReactDOM.unmountComponentAtNode(this._item); + dispose() { + _reactDom.default.unmountComponentAtNode(this._item); this._tile.destroy(); if (this._tooltip != null) { this._tooltip.dispose(); @@ -77,7 +94,7 @@ export default class StatusBarTile { this._disposables.dispose(); } - _createTile(statusBar: atom$StatusBar): atom$StatusBarTile { + _createTile(statusBar) { const item = this._item; item.className = 'inline-block'; item.addEventListener('mouseenter', () => { @@ -91,29 +108,24 @@ export default class StatusBarTile { }); const tile = statusBar.addRightTile({ item, - priority: STATUS_BAR_PRIORITY, + priority: STATUS_BAR_PRIORITY }); return tile; } - _handleMessages(messages: Array): void { + _handleMessages(messages) { this._messages = messages; - const onDidClicks = arrayCompact(messages.map(m => m._onDidClick)); + const onDidClicks = (0, (_collection || _load_collection()).arrayCompact)(messages.map(m => m._onDidClick)); - const props: Props = { + const props = { waitingForComputer: messages.some(m => m.waitingFor === 'computer'), waitingForUser: messages.some(m => m.waitingFor === 'user'), - onDidClick: - onDidClicks.length > 0 - ? () => onDidClicks.forEach(callback => callback()) - : null, + onDidClick: onDidClicks.length > 0 ? () => onDidClicks.forEach(callback => callback()) : null }; - ReactDOM.render(, this._item); + _reactDom.default.render(_react.createElement(StatusBarTileComponent, props), this._item); - const revealTooltip = messages.some(message => - message.shouldRevealTooltip(), - ); + const revealTooltip = messages.some(message => message.shouldRevealTooltip()); if (this._tooltip != null) { // If the user already had the tooltip up, then we'll either // refresh it or hide it. No matter what, we'll have to unmount it. @@ -121,10 +133,7 @@ export default class StatusBarTile { // There are two reasons to refresh the tooltip (bringing it back): // 1) the mouse was previously over the tile or the tooltip // 2) one of the messages is marked with 'reveal tooltip' - if ( - messages.length > 0 && - (revealTooltip || this._isMouseOverItem || this._isMouseOverTooltip) - ) { + if (messages.length > 0 && (revealTooltip || this._isMouseOverItem || this._isMouseOverTooltip)) { this._ensureTooltip(); } else { this._isMouseOverItem = false; @@ -134,7 +143,7 @@ export default class StatusBarTile { } } - _disposeTooltip(): void { + _disposeTooltip() { if (this._tooltip != null) { this._tooltip.dispose(); this._tooltip = null; @@ -142,7 +151,7 @@ export default class StatusBarTile { } } - _ensureTooltip(): void { + _ensureTooltip() { if (this._tooltip != null) { return; } @@ -152,14 +161,18 @@ export default class StatusBarTile { body.appendChild(document.createElement('br')); } const titleElement = message.getTitleElement(); - invariant(titleElement != null); + + if (!(titleElement != null)) { + throw new Error('Invariant violation: "titleElement != null"'); + } + body.appendChild(titleElement); } this._tooltip = atom.tooltips.add(this._item, { item: body, delay: 0, - trigger: 'manual', + trigger: 'manual' }); const tooltipAtomObjects = atom.tooltips.tooltips.get(this._item); if (tooltipAtomObjects != null) { @@ -177,12 +190,8 @@ export default class StatusBarTile { } } - _startLeaveTimeoutIfNecessary(): void { - if ( - !this._isMouseOverItem && - this._isMouseOverTooltip === 0 && - this._leaveTimeoutId == null - ) { + _startLeaveTimeoutIfNecessary() { + if (!this._isMouseOverItem && this._isMouseOverTooltip === 0 && this._leaveTimeoutId == null) { this._leaveTimeoutId = setTimeout(() => { this._disposeTooltip(); // Currently visible messages should no longer reveal the tooltip again. @@ -191,10 +200,11 @@ export default class StatusBarTile { } } - _stopLeaveTimeout(): void { + _stopLeaveTimeout() { if (this._leaveTimeoutId != null) { clearTimeout(this._leaveTimeoutId); this._leaveTimeoutId = null; } } } +exports.default = StatusBarTile; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/main.js index 0dadbe3e..effc9d5c 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/main.js @@ -1,3 +1,37 @@ +'use strict'; + +var _createPackage; + +function _load_createPackage() { + return _createPackage = _interopRequireDefault(require('nuclide-commons-atom/createPackage')); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _BusySignalSingleton; + +function _load_BusySignalSingleton() { + return _BusySignalSingleton = _interopRequireDefault(require('./BusySignalSingleton')); +} + +var _MessageStore; + +function _load_MessageStore() { + return _MessageStore = require('./MessageStore'); +} + +var _StatusBarTile; + +function _load_StatusBarTile() { + return _StatusBarTile = _interopRequireDefault(require('./StatusBarTile')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,45 +40,32 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {BusySignalService} from './types'; - -import createPackage from 'nuclide-commons-atom/createPackage'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import BusySignalSingleton from './BusySignalSingleton'; -import {MessageStore} from './MessageStore'; -import StatusBarTile from './StatusBarTile'; - class Activation { - _disposables: UniversalDisposable; - _service: BusySignalService; - _messageStore: MessageStore; constructor() { - this._messageStore = new MessageStore(); - this._service = new BusySignalSingleton(this._messageStore); - this._disposables = new UniversalDisposable(this._messageStore); + this._messageStore = new (_MessageStore || _load_MessageStore()).MessageStore(); + this._service = new (_BusySignalSingleton || _load_BusySignalSingleton()).default(this._messageStore); + this._disposables = new (_UniversalDisposable || _load_UniversalDisposable()).default(this._messageStore); } dispose() { this._disposables.dispose(); } - consumeStatusBar(statusBar: atom$StatusBar): IDisposable { + consumeStatusBar(statusBar) { // Avoid retaining StatusBarTile by wrapping it. - const disposable = new UniversalDisposable( - new StatusBarTile(statusBar, this._messageStore.getMessageStream()), - ); + const disposable = new (_UniversalDisposable || _load_UniversalDisposable()).default(new (_StatusBarTile || _load_StatusBarTile()).default(statusBar, this._messageStore.getMessageStream())); this._disposables.add(disposable); return disposable; } - provideBusySignal(): BusySignalService { + provideBusySignal() { return this._service; } } -createPackage(module.exports, Activation); +(0, (_createPackage || _load_createPackage()).default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/types.js index 38a0a1cd..a726efc4 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/lib/types.js @@ -1,57 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -export type BusySignalOptions = {| - // Can say that a busy signal will only appear when a given file is open. - // Default = null, meaning the busy signal applies to all files. - onlyForFile?: NuclideUri, - // Is user waiting for computer to finish a task? (traditional busy spinner) - // or is the computer waiting for user to finish a task? (action required) - // Default = spinner. - waitingFor?: 'computer' | 'user', - // Debounce it? default = true for busy-signal, and false for action-required. - debounce?: boolean, - // If onClick is set, then the tooltip will be clickable. Default = null. - onDidClick?: () => void, - // If set to true, the busy signal tooltip will be immediately revealed - // when it first becomes visible (without explicit mouse interaction). - revealTooltip?: boolean, -|}; - -export type BusySignalService = { - // Activates the busy signal with the given title and returns the promise - // from the provided callback. - // The busy signal automatically deactivates when the returned promise - // either resolves or rejects. - reportBusyWhile( - title: string, - f: () => Promise, - options?: BusySignalOptions, - ): Promise, - - // Activates the busy signal. Set the title in the returned BusySignal - // object (you can update the title multiple times) and dispose it when done. - reportBusy(title: string, options?: BusySignalOptions): BusyMessage, - - // This is a no-op. When someone consumes the busy service, they get back a - // reference to the single shared instance, so disposing of it would be wrong. - dispose(): void, -}; - -export type BusyMessage = { - // You can set/update the title. - setTitle(title: string): void, - // Dispose of the signal when done to make it go away. - dispose(): void, -}; +'use strict'; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/spec/BusySignalInstance-spec.js b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/spec/BusySignalInstance-spec.js index ca086019..7d1bdd71 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-busy-signal/spec/BusySignalInstance-spec.js +++ b/modules/atom-ide-ui/pkg/atom-ide-busy-signal/spec/BusySignalInstance-spec.js @@ -1,3 +1,27 @@ +'use strict'; + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +var _fsPromise; + +function _load_fsPromise() { + return _fsPromise = _interopRequireDefault(require('nuclide-commons/fsPromise')); +} + +var _MessageStore; + +function _load_MessageStore() { + return _MessageStore = require('../lib/MessageStore'); +} + +var _BusySignalSingleton; + +function _load_BusySignalSingleton() { + return _BusySignalSingleton = _interopRequireDefault(require('../lib/BusySignalSingleton')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,65 +30,41 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {BusySignalOptions} from '../lib/types'; - -import fsPromise from 'nuclide-commons/fsPromise'; -import {MessageStore} from '../lib/MessageStore'; -import BusySignalSingleton from '../lib/BusySignalSingleton'; - describe('BusySignalSingleton', () => { - let messageStore: MessageStore; - let singleton: BusySignalSingleton; - let messages: Array>; - const options: BusySignalOptions = {debounce: false}; + let messageStore; + let singleton; + let messages; + const options = { debounce: false }; beforeEach(() => { - messageStore = new MessageStore(); - singleton = new BusySignalSingleton(messageStore); + messageStore = new (_MessageStore || _load_MessageStore()).MessageStore(); + singleton = new (_BusySignalSingleton || _load_BusySignalSingleton()).default(messageStore); messages = []; - messageStore - .getMessageStream() - .skip(1) - .subscribe(elements => { - const strings = [...elements].map(element => { - const titleElement = element.getTitleElement(); - const child = - titleElement != null && titleElement.childNodes.length >= 1 - ? titleElement.childNodes[0] - : {}; - return child.data != null && typeof child.data === 'string' - ? child.data - : ''; - }); - messages.push(strings); + messageStore.getMessageStream().skip(1).subscribe(elements => { + const strings = [...elements].map(element => { + const titleElement = element.getTitleElement(); + const child = titleElement != null && titleElement.childNodes.length >= 1 ? titleElement.childNodes[0] : {}; + return child.data != null && typeof child.data === 'string' ? child.data : ''; }); + messages.push(strings); + }); }); it('should record messages before and after a call', () => { expect(messages.length).toBe(0); singleton.reportBusyWhile('foo', () => Promise.resolve(5), options); expect(messages.length).toBe(1); - waitsFor( - () => messages.length === 2, - 'It should publish a second message', - 100, - ); + waitsFor(() => messages.length === 2, 'It should publish a second message', 100); }); it("should send the 'done' message even if the promise rejects", () => { - singleton - .reportBusyWhile('foo', () => Promise.reject(new Error()), options) - .catch(() => {}); + singleton.reportBusyWhile('foo', () => Promise.reject(new Error()), options).catch(() => {}); expect(messages.length).toBe(1); - waitsFor( - () => messages.length === 2, - 'It should publish a second message', - 100, - ); + waitsFor(() => messages.length === 2, 'It should publish a second message', 100); }); it('should properly display duplicate messages', () => { @@ -86,18 +86,18 @@ describe('BusySignalSingleton', () => { }); describe('when onlyForFile is provided', () => { - let editor1: atom$TextEditor = (null: any); - let editor2: atom$TextEditor = (null: any); - let editor3: atom$TextEditor = (null: any); + let editor1 = null; + let editor2 = null; + let editor3 = null; let file2; beforeEach(() => { - waitsForPromise(async () => { - editor1 = await atom.workspace.open(await fsPromise.tempfile()); - file2 = await fsPromise.tempfile(); - editor2 = await atom.workspace.open(file2); - editor3 = await atom.workspace.open(); - }); + waitsForPromise((0, _asyncToGenerator.default)(function* () { + editor1 = yield atom.workspace.open((yield (_fsPromise || _load_fsPromise()).default.tempfile())); + file2 = yield (_fsPromise || _load_fsPromise()).default.tempfile(); + editor2 = yield atom.workspace.open(file2); + editor3 = yield atom.workspace.open(); + })); }); afterEach(() => { @@ -107,10 +107,9 @@ describe('BusySignalSingleton', () => { it('should only display for the proper text editor', () => { atom.workspace.getActivePane().activateItem(editor1); - const disposable = singleton.reportBusy('foo', { - onlyForFile: file2, - ...options, - }); + const disposable = singleton.reportBusy('foo', Object.assign({ + onlyForFile: file2 + }, options)); expect(messages).toEqual([]); atom.workspace.getActivePane().activateItem(editor2); @@ -132,21 +131,18 @@ describe('BusySignalSingleton', () => { }); it('correctly sets revealTooltip when provided', () => { - waitsForPromise(async () => { + waitsForPromise((0, _asyncToGenerator.default)(function* () { function getCurrentMessages() { - return messageStore - .getMessageStream() - .take(1) - .toPromise(); + return messageStore.getMessageStream().take(1).toPromise(); } singleton.reportBusy('foo', { debounce: false, - revealTooltip: true, + revealTooltip: true }); - const curMessages = await getCurrentMessages(); + const curMessages = yield getCurrentMessages(); expect(curMessages.length).toBe(1); expect(curMessages[0].shouldRevealTooltip()).toBe(true); - }); + })); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/CodeActionManager.js b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/CodeActionManager.js index 0bfc072d..1a6945fe 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/CodeActionManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/CodeActionManager.js @@ -1,206 +1,201 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import {observeActiveEditorsDebounced} from 'nuclide-commons-atom/debounced'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {arrayCompact, arrayFlatten} from 'nuclide-commons/collection'; -import {Observable} from 'rxjs'; -import {getLogger} from 'log4js'; - -import type { - RegisterIndieLinter, - IndieLinterDelegate, - LinterMessageV2, -} from '../../../index'; -import type { - DiagnosticMessage, - DiagnosticUpdater, -} from '../../atom-ide-diagnostics/lib/types'; -import type {CodeAction, CodeActionProvider, CodeActionFetcher} from './types'; - -const TIP_DELAY_MS = 500; - -async function actionsToMessage( - location: {file: string, position: atom$RangeLike}, - actions: Array, -): Promise { - const titles = await Promise.all(actions.map(r => r.getTitle())); - const solutions = titles.map((title, i) => ({ - title, - position: location.position, - apply: actions[i].apply.bind(actions[i]), - })); - return { - location, - solutions, - excerpt: 'Select an action', - severity: 'info', - kind: 'action', +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.CodeActionManager = undefined; + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +let actionsToMessage = (() => { + var _ref = (0, _asyncToGenerator.default)(function* (location, actions) { + const titles = yield Promise.all(actions.map(function (r) { + return r.getTitle(); + })); + const solutions = titles.map(function (title, i) { + return { + title, + position: location.position, + apply: actions[i].apply.bind(actions[i]) + }; + }); + return { + location, + solutions, + excerpt: 'Select an action', + severity: 'info', + kind: 'action' + }; + }); + + return function actionsToMessage(_x, _x2) { + return _ref.apply(this, arguments); }; +})(); + +var _debounced; + +function _load_debounced() { + return _debounced = require('nuclide-commons-atom/debounced'); +} + +var _ProviderRegistry; + +function _load_ProviderRegistry() { + return _ProviderRegistry = _interopRequireDefault(require('nuclide-commons-atom/ProviderRegistry')); +} + +var _event; + +function _load_event() { + return _event = require('nuclide-commons/event'); } -export class CodeActionManager { - _providerRegistry: ProviderRegistry; - _disposables: UniversalDisposable; - _linterDelegate: ?IndieLinterDelegate; - _diagnosticUpdater: ?DiagnosticUpdater; +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _collection; + +function _load_collection() { + return _collection = require('nuclide-commons/collection'); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _log4js; + +function _load_log4js() { + return _log4js = require('log4js'); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const TIP_DELAY_MS = 500; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +class CodeActionManager { constructor() { - this._providerRegistry = new ProviderRegistry(); - this._disposables = new UniversalDisposable(this._selectionSubscriber()); + this._providerRegistry = new (_ProviderRegistry || _load_ProviderRegistry()).default(); + this._disposables = new (_UniversalDisposable || _load_UniversalDisposable()).default(this._selectionSubscriber()); } dispose() { this._disposables.dispose(); } - addProvider(provider: CodeActionProvider): IDisposable { + addProvider(provider) { const disposable = this._providerRegistry.addProvider(provider); this._disposables.add(disposable); return disposable; } - consumeDiagnosticUpdates(diagnosticUpdater: DiagnosticUpdater): IDisposable { + consumeDiagnosticUpdates(diagnosticUpdater) { this._diagnosticUpdater = diagnosticUpdater; - return new UniversalDisposable(() => { + return new (_UniversalDisposable || _load_UniversalDisposable()).default(() => { this._diagnosticUpdater = null; }); } - consumeIndie(register: RegisterIndieLinter): IDisposable { + consumeIndie(register) { const linterDelegate = register({ name: 'Code Actions', - supportedMessageKinds: ['action'], + supportedMessageKinds: ['action'] }); this._disposables.add(linterDelegate); this._linterDelegate = linterDelegate; - return new UniversalDisposable(() => { + return new (_UniversalDisposable || _load_UniversalDisposable()).default(() => { this._disposables.remove(linterDelegate); this._linterDelegate = null; }); } - async _genAllCodeActions( - editor: atom$TextEditor, - range: atom$Range, - diagnostics: Array, - ): Promise> { - const codeActionRequests = []; - for (const provider of this._providerRegistry.getAllProvidersForEditor( - editor, - )) { - codeActionRequests.push( - provider.getCodeActions(editor, range, diagnostics), - ); - } - return arrayFlatten(arrayCompact(await Promise.all(codeActionRequests))); + _genAllCodeActions(editor, range, diagnostics) { + var _this = this; + + return (0, _asyncToGenerator.default)(function* () { + const codeActionRequests = []; + for (const provider of _this._providerRegistry.getAllProvidersForEditor(editor)) { + codeActionRequests.push(provider.getCodeActions(editor, range, diagnostics)); + } + return (0, (_collection || _load_collection()).arrayFlatten)((0, (_collection || _load_collection()).arrayCompact)((yield Promise.all(codeActionRequests)))); + })(); } - createCodeActionFetcher(): CodeActionFetcher { + createCodeActionFetcher() { return { getCodeActionForDiagnostic: (diagnostic, editor) => { if (diagnostic.range) { - const {range} = diagnostic; + const { range } = diagnostic; return this._genAllCodeActions(editor, range, [diagnostic]); } return Promise.resolve([]); - }, + } }; } // Listen to buffer range selection changes and trigger code action providers // when ranges change. - _selectionSubscriber(): rxjs$Subscription { + _selectionSubscriber() { // Patterned after highlightEditors of CodeHighlightManager. - return observeActiveEditorsDebounced(0) - .switchMap( - // Get selections for the active editor. - editor => { - if (editor == null) { - return Observable.empty(); - } - const destroyEvents = observableFromSubscribeFunction( - editor.onDidDestroy.bind(editor), - ); - const selections = observableFromSubscribeFunction( - editor.onDidChangeSelectionRange.bind(editor), - ) - .switchMap( - event => - // Remove 0-character selections since it's just cursor movement. - event.newBufferRange.isEmpty() - ? Observable.of(null) - : Observable.of(event.newBufferRange) - .delay(TIP_DELAY_MS) // Delay the emission of the range. - .startWith(null), // null the range immediately when selection changes. - ) - .distinctUntilChanged() - .takeUntil(destroyEvents); - return selections.map( - range => (range == null ? null : {editor, range}), - ); - }, - ) - .switchMap( - // Get a message for the provided selection. - (selection: ?{editor: atom$TextEditor, range: atom$Range}) => { - if (selection == null) { - return Observable.of(null); - } - const {editor, range} = selection; - const file = editor.getBuffer().getPath(); - if (file == null) { - return Observable.empty(); - } - const diagnostics = - this._diagnosticUpdater == null - ? [] - : this._diagnosticUpdater - .getFileMessageUpdates(file) - .messages.filter( - message => - message.range && message.range.intersectsWith(range), - ); - return Observable.fromPromise( - this._genAllCodeActions(editor, range, diagnostics), - ).switchMap(actions => { - // Only produce a message if we have actions to display. - if (actions.length > 0) { - return actionsToMessage({file, position: range}, actions); - } else { - return Observable.empty(); - } - }); - }, - ) - .distinctUntilChanged() - .catch((e, caught) => { - getLogger('code-actions').error( - 'Error getting code actions on selection', - e, - ); - return caught; - }) - .subscribe(message => { - if (this._linterDelegate == null) { - return; - } - if (message == null) { - this._linterDelegate.clearMessages(); + return (0, (_debounced || _load_debounced()).observeActiveEditorsDebounced)(0).switchMap( + // Get selections for the active editor. + editor => { + if (editor == null) { + return _rxjsBundlesRxMinJs.Observable.empty(); + } + const destroyEvents = (0, (_event || _load_event()).observableFromSubscribeFunction)(editor.onDidDestroy.bind(editor)); + const selections = (0, (_event || _load_event()).observableFromSubscribeFunction)(editor.onDidChangeSelectionRange.bind(editor)).switchMap(event => + // Remove 0-character selections since it's just cursor movement. + event.newBufferRange.isEmpty() ? _rxjsBundlesRxMinJs.Observable.of(null) : _rxjsBundlesRxMinJs.Observable.of(event.newBufferRange).delay(TIP_DELAY_MS) // Delay the emission of the range. + .startWith(null) // null the range immediately when selection changes. + ).distinctUntilChanged().takeUntil(destroyEvents); + return selections.map(range => range == null ? null : { editor, range }); + }).switchMap( + // Get a message for the provided selection. + selection => { + if (selection == null) { + return _rxjsBundlesRxMinJs.Observable.of(null); + } + const { editor, range } = selection; + const file = editor.getBuffer().getPath(); + if (file == null) { + return _rxjsBundlesRxMinJs.Observable.empty(); + } + const diagnostics = this._diagnosticUpdater == null ? [] : this._diagnosticUpdater.getFileMessageUpdates(file).messages.filter(message => message.range && message.range.intersectsWith(range)); + return _rxjsBundlesRxMinJs.Observable.fromPromise(this._genAllCodeActions(editor, range, diagnostics)).switchMap(actions => { + // Only produce a message if we have actions to display. + if (actions.length > 0) { + return actionsToMessage({ file, position: range }, actions); } else { - this._linterDelegate.setAllMessages([message]); + return _rxjsBundlesRxMinJs.Observable.empty(); } }); + }).distinctUntilChanged().catch((e, caught) => { + (0, (_log4js || _load_log4js()).getLogger)('code-actions').error('Error getting code actions on selection', e); + return caught; + }).subscribe(message => { + if (this._linterDelegate == null) { + return; + } + if (message == null) { + this._linterDelegate.clearMessages(); + } else { + this._linterDelegate.setAllMessages([message]); + } + }); } } +exports.CodeActionManager = CodeActionManager; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/main.js index cc068c15..4ed863e7 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/main.js @@ -1,3 +1,19 @@ +'use strict'; + +var _createPackage; + +function _load_createPackage() { + return _createPackage = _interopRequireDefault(require('nuclide-commons-atom/createPackage')); +} + +var _CodeActionManager; + +function _load_CodeActionManager() { + return _CodeActionManager = require('./CodeActionManager'); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,43 +22,35 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import createPackage from 'nuclide-commons-atom/createPackage'; -import {CodeActionManager} from './CodeActionManager'; - -import type {RegisterIndieLinter} from '../../../index'; -import type {CodeActionProvider, CodeActionFetcher} from './types'; -import type {DiagnosticUpdater} from '../../atom-ide-diagnostics/lib/types'; - class Activation { - _codeActionManager: CodeActionManager; constructor() { - this._codeActionManager = new CodeActionManager(); + this._codeActionManager = new (_CodeActionManager || _load_CodeActionManager()).CodeActionManager(); } dispose() { this._codeActionManager.dispose(); } - consumeCodeActionProvider(provider: CodeActionProvider) { + consumeCodeActionProvider(provider) { return this._codeActionManager.addProvider(provider); } - consumeDiagnosticUpdates(diagnosticUpdater: DiagnosticUpdater) { + consumeDiagnosticUpdates(diagnosticUpdater) { return this._codeActionManager.consumeDiagnosticUpdates(diagnosticUpdater); } - provideCodeActionFetcher(): CodeActionFetcher { + provideCodeActionFetcher() { return this._codeActionManager.createCodeActionFetcher(); } - consumeIndie(register: RegisterIndieLinter) { + consumeIndie(register) { return this._codeActionManager.consumeIndie(register); } } -createPackage(module.exports, Activation); +(0, (_createPackage || _load_createPackage()).default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/types.js index 003f5366..a726efc4 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-actions/lib/types.js @@ -1,42 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {DiagnosticMessage} from '../../../pkg/atom-ide-diagnostics/lib/types'; - -export interface CodeAction { - apply(): Promise; - getTitle(): Promise; - dispose(): void; -} - -export type CodeActionProvider = { - +grammarScopes?: Array, - priority: number, - getCodeActions( - editor: atom$TextEditor, - range: atom$Range, - diagnostics: Array, - ): Promise>, -}; - -/** - * atom-ide-code-actions provides a CodeActionFetcher which offers an API to - * request CodeActions from all CodeAction providers. For now, CodeActionFetcher - * can only fetch CodeActions for a Diagnostic. In the future, this API can be - * extended to provide a stream of CodeActions based on the cursor position. - */ -export type CodeActionFetcher = { - getCodeActionForDiagnostic: ( - diagnostic: DiagnosticMessage, - editor: atom$TextEditor, - ) => Promise>, -}; +'use strict'; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-actions/spec/CodeActionManager-spec.js b/modules/atom-ide-ui/pkg/atom-ide-code-actions/spec/CodeActionManager-spec.js index 825aaf85..64367a85 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-actions/spec/CodeActionManager-spec.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-actions/spec/CodeActionManager-spec.js @@ -1,19 +1,22 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -import os from 'os'; -import nuclideUri from 'nuclide-commons/nuclideUri'; +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); -import {CodeActionManager} from '../lib/CodeActionManager'; +var _os = _interopRequireDefault(require('os')); + +var _nuclideUri; + +function _load_nuclideUri() { + return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri')); +} + +var _CodeActionManager; + +function _load_CodeActionManager() { + return _CodeActionManager = require('../lib/CodeActionManager'); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } describe('CodeActionManager', () => { let manager; @@ -22,39 +25,39 @@ describe('CodeActionManager', () => { let editor; beforeEach(() => { jasmine.useMockClock(); - waitsForPromise(async () => { - editor = await atom.workspace.open( - nuclideUri.join(os.tmpdir(), 'test.txt'), - ); + waitsForPromise((0, _asyncToGenerator.default)(function* () { + editor = yield atom.workspace.open((_nuclideUri || _load_nuclideUri()).default.join(_os.default.tmpdir(), 'test.txt')); editor.setText('abc\ndef\nghi'); - manager = new CodeActionManager(); + manager = new (_CodeActionManager || _load_CodeActionManager()).CodeActionManager(); provider = { priority: 1, grammarScopes: ['text.plain.null-grammar'], - async getCodeActions(_e, _r, _d) { - return []; - }, + getCodeActions(_e, _r, _d) { + return (0, _asyncToGenerator.default)(function* () { + return []; + })(); + } }; delegate = { - clearMessages: () => {}, - setAllMessages: _messages => {}, + clearMessages: function () {}, + setAllMessages: function (_messages) {} }; - manager._linterDelegate = (delegate: any); + manager._linterDelegate = delegate; manager.addProvider(provider); - }); + })); }); it('finds code actions on highlight change and updates linter', () => { - const actions = [ - { - apply() {}, - async getTitle() { + const actions = [{ + apply() {}, + getTitle() { + return (0, _asyncToGenerator.default)(function* () { return 'Mock action'; - }, - dispose() {}, + })(); }, - ]; + dispose() {} + }]; const spyActions = spyOn(provider, 'getCodeActions').andReturn(actions); const spyLinter = spyOn(delegate, 'setAllMessages'); @@ -64,18 +67,22 @@ describe('CodeActionManager', () => { advanceClock(501); }); - waitsFor( - () => spyLinter.wasCalled, - 'should have called setAllMessages', - 750, - ); + waitsFor(() => spyLinter.wasCalled, 'should have called setAllMessages', 750); runs(() => { expect(spyActions).toHaveBeenCalled(); expect(spyLinter).toHaveBeenCalled(); - expect( - (spyLinter.mostRecentCall.args: any)[0][0].solutions.length, - ).toEqual(1); + expect(spyLinter.mostRecentCall.args[0][0].solutions.length).toEqual(1); }); }); -}); +}); /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/CodeFormatManager.js b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/CodeFormatManager.js index f6c92237..03f719db 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/CodeFormatManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/CodeFormatManager.js @@ -1,66 +1,90 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -import type {BusySignalService} from '../../atom-ide-busy-signal/lib/types'; -import type { - RangeCodeFormatProvider, - FileCodeFormatProvider, - OnTypeCodeFormatProvider, - OnSaveCodeFormatProvider, -} from './types'; - -import {Range} from 'atom'; -import {Observable, Subject} from 'rxjs'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {completingSwitchMap, microtask} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import { - observeEditorDestroy, - observeTextEditors, -} from 'nuclide-commons-atom/text-editor'; -import {applyTextEditsToBuffer} from 'nuclide-commons-atom/text-edit'; -import {getFormatOnSave, getFormatOnType} from './config'; -import {getLogger} from 'log4js'; +Object.defineProperty(exports, "__esModule", { + value: true +}); -// Save events are critical, so don't allow providers to block them. -const SAVE_TIMEOUT = 2500; +var _atom = require('atom'); -type FormatEvent = - | { - type: 'command' | 'save' | 'new-save', - editor: atom$TextEditor, - } - | { - type: 'type', - editor: atom$TextEditor, - edit: atom$AggregatedTextEditEvent, - }; - -export default class CodeFormatManager { - _subscriptions: UniversalDisposable; - _rangeProviders: ProviderRegistry; - _fileProviders: ProviderRegistry; - _onTypeProviders: ProviderRegistry; - _onSaveProviders: ProviderRegistry; - _busySignalService: ?BusySignalService; +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _event; + +function _load_event() { + return _event = require('nuclide-commons/event'); +} + +var _nuclideUri; + +function _load_nuclideUri() { + return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri')); +} + +var _observable; + +function _load_observable() { + return _observable = require('nuclide-commons/observable'); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _ProviderRegistry; + +function _load_ProviderRegistry() { + return _ProviderRegistry = _interopRequireDefault(require('nuclide-commons-atom/ProviderRegistry')); +} + +var _textEditor; + +function _load_textEditor() { + return _textEditor = require('nuclide-commons-atom/text-editor'); +} + +var _textEdit; + +function _load_textEdit() { + return _textEdit = require('nuclide-commons-atom/text-edit'); +} + +var _config; + +function _load_config() { + return _config = require('./config'); +} + +var _log4js; + +function _load_log4js() { + return _log4js = require('log4js'); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// Save events are critical, so don't allow providers to block them. +const SAVE_TIMEOUT = 2500; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +class CodeFormatManager { constructor() { - this._subscriptions = new UniversalDisposable(this._subscribeToEvents()); - this._rangeProviders = new ProviderRegistry(); - this._fileProviders = new ProviderRegistry(); - this._onTypeProviders = new ProviderRegistry(); - this._onSaveProviders = new ProviderRegistry(); + this._subscriptions = new (_UniversalDisposable || _load_UniversalDisposable()).default(this._subscribeToEvents()); + this._rangeProviders = new (_ProviderRegistry || _load_ProviderRegistry()).default(); + this._fileProviders = new (_ProviderRegistry || _load_ProviderRegistry()).default(); + this._onTypeProviders = new (_ProviderRegistry || _load_ProviderRegistry()).default(); + this._onSaveProviders = new (_ProviderRegistry || _load_ProviderRegistry()).default(); } /** @@ -69,69 +93,44 @@ export default class CodeFormatManager { * By handling all events in a central location, we ensure that no buffer * runs into race conditions with simultaneous formatters. */ - _subscribeToEvents(): rxjs$Subscription { + _subscribeToEvents() { // Events from the explicit Atom command. - const commandEvents = observableFromSubscribeFunction(callback => - atom.commands.add( - 'atom-text-editor', - 'code-format:format-code', - callback, - ), - ).switchMap(() => { + const commandEvents = (0, (_event || _load_event()).observableFromSubscribeFunction)(callback => atom.commands.add('atom-text-editor', 'code-format:format-code', callback)).switchMap(() => { const editor = atom.workspace.getActiveTextEditor(); if (!editor) { - return Observable.empty(); + return _rxjsBundlesRxMinJs.Observable.empty(); } - return Observable.of({type: 'command', editor}); + return _rxjsBundlesRxMinJs.Observable.of({ type: 'command', editor }); }); // Events from editor actions (saving, typing). - const editorEvents = observableFromSubscribeFunction( - observeTextEditors, - ).mergeMap(editor => this._getEditorEventStream(editor)); - - return ( - Observable.merge(commandEvents, editorEvents) - // Group events by buffer to prevent simultaneous formatting operations. - .groupBy( - event => event.editor.getBuffer(), - event => event, - grouped => - observableFromSubscribeFunction(callback => - grouped.key.onDidDestroy(callback), - ), - ) - .mergeMap(events => - // Make sure we halt everything when the editor gets destroyed. - events.let(completingSwitchMap(event => this._handleEvent(event))), - ) - .subscribe() - ); + const editorEvents = (0, (_event || _load_event()).observableFromSubscribeFunction)((_textEditor || _load_textEditor()).observeTextEditors).mergeMap(editor => this._getEditorEventStream(editor)); + + return _rxjsBundlesRxMinJs.Observable.merge(commandEvents, editorEvents) + // Group events by buffer to prevent simultaneous formatting operations. + .groupBy(event => event.editor.getBuffer(), event => event, grouped => (0, (_event || _load_event()).observableFromSubscribeFunction)(callback => grouped.key.onDidDestroy(callback))).mergeMap(events => + // Make sure we halt everything when the editor gets destroyed. + events.let((0, (_observable || _load_observable()).completingSwitchMap)(event => this._handleEvent(event)))).subscribe(); } /** * Returns a stream of all typing and saving operations from the editor. */ - _getEditorEventStream(editor: atom$TextEditor): Observable { - const changeEvents = observableFromSubscribeFunction(callback => - editor.getBuffer().onDidChangeText(callback), - ); + _getEditorEventStream(editor) { + const changeEvents = (0, (_event || _load_event()).observableFromSubscribeFunction)(callback => editor.getBuffer().onDidChangeText(callback)); - const saveEvents = Observable.create(observer => { + const saveEvents = _rxjsBundlesRxMinJs.Observable.create(observer => { const realSave = editor.save; - const newSaves = new Subject(); + const newSaves = new _rxjsBundlesRxMinJs.Subject(); // HACK: intercept the real TextEditor.save and handle it ourselves. // Atom has no way of injecting content into the buffer asynchronously // before a save operation. // If we try to format after the save, and then save again, // it's a poor user experience (and also races the text buffer's reload). - const editor_ = (editor: any); + const editor_ = editor; editor_.save = () => { newSaves.next('new-save'); - return this._safeFormatCodeOnSave(editor) - .takeUntil(newSaves) - .toPromise() - .then(() => realSave.call(editor)); + return this._safeFormatCodeOnSave(editor).takeUntil(newSaves).toPromise().then(() => realSave.call(editor)); }; const subscription = newSaves.subscribe(observer); return () => { @@ -144,83 +143,59 @@ export default class CodeFormatManager { // We need to capture when editors are about to be destroyed in order to // interrupt any pending formatting operations. (Otherwise, we may end up // attempting to save a destroyed editor!) - const willDestroyEvents = observableFromSubscribeFunction(cb => - atom.workspace.onWillDestroyPaneItem(cb), - ).filter(event => event.item === editor); - - return Observable.merge( - changeEvents.map(edit => ({type: 'type', editor, edit})), - saveEvents.map(type => ({type, editor})), - ).takeUntil( - Observable.merge(observeEditorDestroy(editor), willDestroyEvents), - ); + const willDestroyEvents = (0, (_event || _load_event()).observableFromSubscribeFunction)(cb => atom.workspace.onWillDestroyPaneItem(cb)).filter(event => event.item === editor); + + return _rxjsBundlesRxMinJs.Observable.merge(changeEvents.map(edit => ({ type: 'type', editor, edit })), saveEvents.map(type => ({ type, editor }))).takeUntil(_rxjsBundlesRxMinJs.Observable.merge((0, (_textEditor || _load_textEditor()).observeEditorDestroy)(editor), willDestroyEvents)); } - _handleEvent(event: FormatEvent): Observable { - const {editor} = event; + _handleEvent(event) { + const { editor } = event; switch (event.type) { case 'command': - return this._formatCodeInTextEditor(editor) - .map(result => { - if (!result) { - throw new Error('No code formatting providers found!'); - } - }) - .catch(err => { - atom.notifications.addError( - `Failed to format code: ${err.message}`, - { - detail: err.detail, - }, - ); - return Observable.empty(); + return this._formatCodeInTextEditor(editor).map(result => { + if (!result) { + throw new Error('No code formatting providers found!'); + } + }).catch(err => { + atom.notifications.addError(`Failed to format code: ${err.message}`, { + detail: err.detail }); + return _rxjsBundlesRxMinJs.Observable.empty(); + }); case 'type': - return this._formatCodeOnTypeInTextEditor(editor, event.edit).catch( - err => { - getLogger('code-format').warn( - 'Failed to format code on type:', - err, - ); - return Observable.empty(); - }, - ); + return this._formatCodeOnTypeInTextEditor(editor, event.edit).catch(err => { + (0, (_log4js || _load_log4js()).getLogger)('code-format').warn('Failed to format code on type:', err); + return _rxjsBundlesRxMinJs.Observable.empty(); + }); case 'save': - return ( - this._safeFormatCodeOnSave(editor) - // Fire-and-forget the original save function. - // This is actually async for remote files, but we don't use the result. - // NOTE: finally is important, as saves should still fire on unsubscribe. - .finally(() => editor.getBuffer().save()) - ); + return this._safeFormatCodeOnSave(editor) + // Fire-and-forget the original save function. + // This is actually async for remote files, but we don't use the result. + // NOTE: finally is important, as saves should still fire on unsubscribe. + .finally(() => editor.getBuffer().save()); case 'new-save': - return Observable.empty(); + return _rxjsBundlesRxMinJs.Observable.empty(); default: - return Observable.throw(`unknown event type ${event.type}`); + return _rxjsBundlesRxMinJs.Observable.throw(`unknown event type ${event.type}`); } } // Checks whether contents are same in the buffer post-format, throwing if // anything has changed. - _checkContentsAreSame(before: string, after: string): void { + _checkContentsAreSame(before, after) { if (before !== after) { - throw new Error( - 'The file contents were changed before formatting was complete.', - ); + throw new Error('The file contents were changed before formatting was complete.'); } } // Formats code in the editor specified, returning whether or not a // code formatter completed successfully. - _formatCodeInTextEditor( - editor: atom$TextEditor, - range?: atom$Range, - ): Observable { - return Observable.defer(() => { + _formatCodeInTextEditor(editor, range) { + return _rxjsBundlesRxMinJs.Observable.defer(() => { const buffer = editor.getBuffer(); const selectionRange = range || editor.getSelectedBufferRange(); - const {start: selectionStart, end: selectionEnd} = selectionRange; - let formatRange: atom$Range; + const { start: selectionStart, end: selectionEnd } = selectionRange; + let formatRange; if (selectionRange.isEmpty()) { // If no selection is done, then, the whole file is wanted to be formatted. formatRange = buffer.getRange(); @@ -232,47 +207,29 @@ export default class CodeFormatManager { // or (2) at the first column of the line AFTER their selection. In both cases // we snap the formatRange to end at the first column of the line after their // selection.) - formatRange = new Range( - [selectionStart.row, 0], - selectionEnd.column === 0 ? selectionEnd : [selectionEnd.row + 1, 0], - ); + formatRange = new _atom.Range([selectionStart.row, 0], selectionEnd.column === 0 ? selectionEnd : [selectionEnd.row + 1, 0]); } const rangeProvider = this._rangeProviders.getProviderForEditor(editor); const fileProvider = this._fileProviders.getProviderForEditor(editor); const contents = editor.getText(); - if ( - rangeProvider != null && - // When formatting the entire file, prefer file-based providers. - (!formatRange.isEqual(buffer.getRange()) || fileProvider == null) - ) { - return Observable.defer(() => - this._reportBusy( - editor, - rangeProvider.formatCode(editor, formatRange), - ), - ).map(edits => { + if (rangeProvider != null && ( + // When formatting the entire file, prefer file-based providers. + !formatRange.isEqual(buffer.getRange()) || fileProvider == null)) { + return _rxjsBundlesRxMinJs.Observable.defer(() => this._reportBusy(editor, rangeProvider.formatCode(editor, formatRange))).map(edits => { // Throws if contents have changed since the time of triggering format code. this._checkContentsAreSame(contents, editor.getText()); - if (!applyTextEditsToBuffer(editor.getBuffer(), edits)) { + if (!(0, (_textEdit || _load_textEdit()).applyTextEditsToBuffer)(editor.getBuffer(), edits)) { throw new Error('Could not apply edits to text buffer.'); } return true; }); } else if (fileProvider != null) { - return Observable.defer(() => - this._reportBusy( - editor, - fileProvider.formatEntireFile(editor, formatRange), - ), - ).map(({newCursor, formatted}) => { + return _rxjsBundlesRxMinJs.Observable.defer(() => this._reportBusy(editor, fileProvider.formatEntireFile(editor, formatRange))).map(({ newCursor, formatted }) => { // Throws if contents have changed since the time of triggering format code. this._checkContentsAreSame(contents, editor.getText()); buffer.setTextViaDiff(formatted); - const newPosition = - newCursor != null - ? buffer.positionForCharacterIndex(newCursor) - : editor.getCursorBufferPosition(); + const newPosition = newCursor != null ? buffer.positionForCharacterIndex(newCursor) : editor.getCursorBufferPosition(); // We call setCursorBufferPosition even when there is no newCursor, // because it unselects the text selection. @@ -280,24 +237,21 @@ export default class CodeFormatManager { return true; }); } else { - return Observable.of(false); + return _rxjsBundlesRxMinJs.Observable.of(false); } }); } - _formatCodeOnTypeInTextEditor( - editor: atom$TextEditor, - aggregatedEvent: atom$AggregatedTextEditEvent, - ): Observable { - return Observable.defer(() => { + _formatCodeOnTypeInTextEditor(editor, aggregatedEvent) { + return _rxjsBundlesRxMinJs.Observable.defer(() => { // Don't try to format changes with multiple cursors. if (aggregatedEvent.changes.length !== 1) { - return Observable.empty(); + return _rxjsBundlesRxMinJs.Observable.empty(); } const event = aggregatedEvent.changes[0]; // This also ensures the non-emptiness of event.newText for below. - if (!shouldFormatOnType(event) || !getFormatOnType()) { - return Observable.empty(); + if (!shouldFormatOnType(event) || !(0, (_config || _load_config()).getFormatOnType)()) { + return _rxjsBundlesRxMinJs.Observable.empty(); } // In the case of bracket-matching, we use the last character because that's // the character that will usually cause a reformat (i.e. `}` instead of `{`). @@ -305,7 +259,7 @@ export default class CodeFormatManager { const provider = this._onTypeProviders.getProviderForEditor(editor); if (provider == null) { - return Observable.empty(); + return _rxjsBundlesRxMinJs.Observable.empty(); } const contents = editor.getText(); @@ -322,94 +276,70 @@ export default class CodeFormatManager { // We want to wait until the cursor has actually moved before we issue a // format request, so that we format at the right position (and potentially // also let any other event handlers have their go). - return microtask - .switchMap(() => - provider.formatAtPosition( - editor, - editor.getCursorBufferPosition().translate([0, -1]), - character, - ), - ) - .map(edits => { - if (edits.length === 0) { - return; - } - this._checkContentsAreSame(contents, editor.getText()); - // Note that this modification is not in a transaction, so it applies as a - // separate editing event than the character typing. This means that you - // can undo just the formatting by attempting to undo once, and then undo - // your actual code by undoing again. - if (!applyTextEditsToBuffer(editor.getBuffer(), edits)) { - throw new Error('Could not apply edits to text buffer.'); - } - }); + return (_observable || _load_observable()).microtask.switchMap(() => provider.formatAtPosition(editor, editor.getCursorBufferPosition().translate([0, -1]), character)).map(edits => { + if (edits.length === 0) { + return; + } + this._checkContentsAreSame(contents, editor.getText()); + // Note that this modification is not in a transaction, so it applies as a + // separate editing event than the character typing. This means that you + // can undo just the formatting by attempting to undo once, and then undo + // your actual code by undoing again. + if (!(0, (_textEdit || _load_textEdit()).applyTextEditsToBuffer)(editor.getBuffer(), edits)) { + throw new Error('Could not apply edits to text buffer.'); + } + }); }); } - _safeFormatCodeOnSave(editor: atom$TextEditor): Observable { - return this._formatCodeOnSaveInTextEditor(editor) - .timeout(SAVE_TIMEOUT) - .catch(err => { - getLogger('code-format').warn('Failed to format code on save:', err); - return Observable.empty(); - }); + _safeFormatCodeOnSave(editor) { + return this._formatCodeOnSaveInTextEditor(editor).timeout(SAVE_TIMEOUT).catch(err => { + (0, (_log4js || _load_log4js()).getLogger)('code-format').warn('Failed to format code on save:', err); + return _rxjsBundlesRxMinJs.Observable.empty(); + }); } - _formatCodeOnSaveInTextEditor(editor: atom$TextEditor): Observable { + _formatCodeOnSaveInTextEditor(editor) { const saveProvider = this._onSaveProviders.getProviderForEditor(editor); if (saveProvider != null) { - return Observable.defer(() => - this._reportBusy(editor, saveProvider.formatOnSave(editor), false), - ).map(edits => { - applyTextEditsToBuffer(editor.getBuffer(), edits); + return _rxjsBundlesRxMinJs.Observable.defer(() => this._reportBusy(editor, saveProvider.formatOnSave(editor), false)).map(edits => { + (0, (_textEdit || _load_textEdit()).applyTextEditsToBuffer)(editor.getBuffer(), edits); }); - } else if (getFormatOnSave()) { - return this._formatCodeInTextEditor( - editor, - editor.getBuffer().getRange(), - ).ignoreElements(); + } else if ((0, (_config || _load_config()).getFormatOnSave)()) { + return this._formatCodeInTextEditor(editor, editor.getBuffer().getRange()).ignoreElements(); } - return Observable.empty(); + return _rxjsBundlesRxMinJs.Observable.empty(); } - _reportBusy( - editor: atom$TextEditor, - promise: Promise, - revealTooltip?: boolean = true, - ): Promise { + _reportBusy(editor, promise, revealTooltip = true) { const busySignalService = this._busySignalService; if (busySignalService != null) { const path = editor.getPath(); - const displayPath = - path != null ? nuclideUri.basename(path) : ''; - return busySignalService.reportBusyWhile( - `Formatting code in ${displayPath}`, - () => promise, - {revealTooltip}, - ); + const displayPath = path != null ? (_nuclideUri || _load_nuclideUri()).default.basename(path) : ''; + return busySignalService.reportBusyWhile(`Formatting code in ${displayPath}`, () => promise, { revealTooltip }); } return promise; } - addRangeProvider(provider: RangeCodeFormatProvider): IDisposable { + addRangeProvider(provider) { return this._rangeProviders.addProvider(provider); } - addFileProvider(provider: FileCodeFormatProvider): IDisposable { + addFileProvider(provider) { return this._fileProviders.addProvider(provider); } - addOnTypeProvider(provider: OnTypeCodeFormatProvider): IDisposable { + addOnTypeProvider(provider) { return this._onTypeProviders.addProvider(provider); } - addOnSaveProvider(provider: OnSaveCodeFormatProvider): IDisposable { + addOnSaveProvider(provider) { return this._onSaveProviders.addProvider(provider); } - consumeBusySignal(busySignalService: BusySignalService): IDisposable { + consumeBusySignal(busySignalService) { this._busySignalService = busySignalService; - return new UniversalDisposable(() => { + return new (_UniversalDisposable || _load_UniversalDisposable()).default(() => { this._busySignalService = null; }); } @@ -419,7 +349,8 @@ export default class CodeFormatManager { } } -function shouldFormatOnType(event: atom$TextEditEvent): boolean { +exports.default = CodeFormatManager; +function shouldFormatOnType(event) { // There's not a direct way to figure out what caused this edit event. There // are three cases that we want to pay attention to: // @@ -453,12 +384,10 @@ function shouldFormatOnType(event: atom$TextEditEvent): boolean { * inserting an extra bracket, so we just assume that any pair of brackets that * bracket-matcher recognizes was a pair matched by the package. */ -function isBracketPair(typedText: string): boolean { +function isBracketPair(typedText) { if (atom.packages.getActivePackage('bracket-matcher') == null) { return false; } - const validBracketPairs: Array = (atom.config.get( - 'bracket-matcher.autocompleteCharacters', - ): any); + const validBracketPairs = atom.config.get('bracket-matcher.autocompleteCharacters'); return validBracketPairs.indexOf(typedText) !== -1; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/config.js b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/config.js index 8c50f9e4..c6ff1528 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/config.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/config.js @@ -1,27 +1,34 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -import featureConfig from 'nuclide-commons-atom/feature-config'; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getFormatOnSave = getFormatOnSave; +exports.getFormatOnType = getFormatOnType; -export function getFormatOnSave(): boolean { - const formatOnSave = (featureConfig.get( - 'atom-ide-code-format.formatOnSave', - ): any); - return formatOnSave == null ? false : formatOnSave; -} +var _featureConfig; -export function getFormatOnType(): boolean { - return featureConfig.getWithDefaults( - 'atom-ide-code-format.formatOnType', - false, - ); +function _load_featureConfig() { + return _featureConfig = _interopRequireDefault(require('nuclide-commons-atom/feature-config')); } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function getFormatOnSave() { + const formatOnSave = (_featureConfig || _load_featureConfig()).default.get('atom-ide-code-format.formatOnSave'); + return formatOnSave == null ? false : formatOnSave; +} /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +function getFormatOnType() { + return (_featureConfig || _load_featureConfig()).default.getWithDefaults('atom-ide-code-format.formatOnType', false); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/main.js index 946f6d7b..04f6c87f 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/main.js @@ -1,3 +1,19 @@ +'use strict'; + +var _createPackage; + +function _load_createPackage() { + return _createPackage = _interopRequireDefault(require('nuclide-commons-atom/createPackage')); +} + +var _CodeFormatManager; + +function _load_CodeFormatManager() { + return _CodeFormatManager = _interopRequireDefault(require('./CodeFormatManager')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,38 +22,20 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {BusySignalService} from '../../atom-ide-busy-signal/lib/types'; -import type { - CodeFormatProvider, - RangeCodeFormatProvider, - FileCodeFormatProvider, - OnTypeCodeFormatProvider, - OnSaveCodeFormatProvider, -} from './types'; - -import createPackage from 'nuclide-commons-atom/createPackage'; -import CodeFormatManager from './CodeFormatManager'; - class Activation { - codeFormatManager: CodeFormatManager; constructor() { - this.codeFormatManager = new CodeFormatManager(); + this.codeFormatManager = new (_CodeFormatManager || _load_CodeFormatManager()).default(); } - consumeLegacyProvider(provider: CodeFormatProvider): IDisposable { + consumeLegacyProvider(provider) { // Legacy providers used `selector` / `inclusionPriority`. - provider.grammarScopes = - provider.grammarScopes || - (provider.selector != null ? provider.selector.split(', ') : null); - provider.priority = - provider.priority != null - ? provider.priority - : provider.inclusionPriority != null ? provider.inclusionPriority : 0; + provider.grammarScopes = provider.grammarScopes || (provider.selector != null ? provider.selector.split(', ') : null); + provider.priority = provider.priority != null ? provider.priority : provider.inclusionPriority != null ? provider.inclusionPriority : 0; if (provider.formatCode) { return this.consumeRangeProvider(provider); } else if (provider.formatEntireFile) { @@ -50,23 +48,23 @@ class Activation { throw new Error('Invalid code format provider'); } - consumeRangeProvider(provider: RangeCodeFormatProvider): IDisposable { + consumeRangeProvider(provider) { return this.codeFormatManager.addRangeProvider(provider); } - consumeFileProvider(provider: FileCodeFormatProvider): IDisposable { + consumeFileProvider(provider) { return this.codeFormatManager.addFileProvider(provider); } - consumeOnTypeProvider(provider: OnTypeCodeFormatProvider): IDisposable { + consumeOnTypeProvider(provider) { return this.codeFormatManager.addOnTypeProvider(provider); } - consumeOnSaveProvider(provider: OnSaveCodeFormatProvider): IDisposable { + consumeOnSaveProvider(provider) { return this.codeFormatManager.addOnSaveProvider(provider); } - consumeBusySignal(busySignalService: BusySignalService): IDisposable { + consumeBusySignal(busySignalService) { return this.codeFormatManager.consumeBusySignal(busySignalService); } @@ -75,4 +73,4 @@ class Activation { } } -createPackage(module.exports, Activation); +(0, (_createPackage || _load_createPackage()).default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/types.js index 971a82d6..a726efc4 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-format/lib/types.js @@ -1,96 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {TextEdit} from 'nuclide-commons-atom/text-edit'; - -/** - * A brief overview of the different code formatting providers: - * - * == Range formatters == - * These accept a range to format and return a list of edits to apply. - * These will always be preferred over file formatters when a range is selected. - * - * == File formatters == - * These always return the result of formatting the entire file. - * To compensate, they can return a custom cursor position to avoid disruption. - * These will be preferred over range formatters for whole-file formatting. - * - * == onType formatters == - * These are run after every typing event in a selected editor. - * - * == onSave formatters == - * These are run whenever a selected editor is saved. - * If the global format-on-save option is enabled, then file/range formatters - * will be triggered on save (even if no save formatters are provided). - * Obviously, save formatters are preferred in this case. - */ - -/** - * Formats the range specified, and returns a list of text edits to apply. - * Text edits must be non-overlapping and preferably in reverse-sorted order. - */ -export type RangeCodeFormatProvider = {| - formatCode: ( - editor: atom$TextEditor, - range: atom$Range, - ) => Promise>, - priority: number, - grammarScopes: Array, -|}; - -/** - * Formats the range specified, but returns the entire file (along with the new cursor position). - * Useful for less-flexible providers like clang-format. - */ -export type FileCodeFormatProvider = {| - formatEntireFile: ( - editor: atom$TextEditor, - range: atom$Range, - ) => Promise<{ - newCursor?: number, - formatted: string, - }>, - priority: number, - grammarScopes: Array, -|}; - -/** - * Formats around the given position, and returns a list of text edits to - * apply, similar to `formatCode`. The provider determines the exact - * range to format based on what's at that position. - * - * This will automatically triggered after every typing event. - */ -export type OnTypeCodeFormatProvider = {| - formatAtPosition: ( - editor: atom$TextEditor, - position: atom$Point, - triggerCharacter: string, - ) => Promise>, - priority: number, - grammarScopes: Array, -|}; - -/** - * Formats files after save events. - */ -export type OnSaveCodeFormatProvider = {| - formatOnSave: (editor: atom$TextEditor) => Promise>, - priority: number, - grammarScopes: Array, -|}; - -export type CodeFormatProvider = - | RangeCodeFormatProvider - | FileCodeFormatProvider - | OnTypeCodeFormatProvider - | OnSaveCodeFormatProvider; +'use strict'; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-format/spec/CodeFormatManager-spec.js b/modules/atom-ide-ui/pkg/atom-ide-code-format/spec/CodeFormatManager-spec.js index 51faa61e..b9507c33 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-format/spec/CodeFormatManager-spec.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-format/spec/CodeFormatManager-spec.js @@ -1,3 +1,31 @@ +'use strict'; + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +var _atom = require('atom'); + +var _temp; + +function _load_temp() { + return _temp = _interopRequireDefault(require('temp')); +} + +var _config; + +function _load_config() { + return _config = _interopRequireWildcard(require('../lib/config')); +} + +var _CodeFormatManager; + +function _load_CodeFormatManager() { + return _CodeFormatManager = _interopRequireDefault(require('../lib/CodeFormatManager')); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,83 +34,76 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import {Range} from 'atom'; -import temp from 'temp'; -import * as config from '../lib/config'; -import CodeFormatManager from '../lib/CodeFormatManager'; - describe('CodeFormatManager', () => { let textEditor; beforeEach(() => { - waitsForPromise(async () => { - temp.track(); - const file = temp.openSync(); - textEditor = await atom.workspace.open(file.path); - }); + waitsForPromise((0, _asyncToGenerator.default)(function* () { + (_temp || _load_temp()).default.track(); + const file = (_temp || _load_temp()).default.openSync(); + textEditor = yield atom.workspace.open(file.path); + })); }); it('formats an editor on request', () => { - waitsForPromise(async () => { - const manager = new CodeFormatManager(); + waitsForPromise((0, _asyncToGenerator.default)(function* () { + const manager = new (_CodeFormatManager || _load_CodeFormatManager()).default(); manager.addRangeProvider({ grammarScopes: ['text.plain.null-grammar'], priority: 1, - formatCode: () => - Promise.resolve([ - { - oldRange: new Range([0, 0], [0, 3]), - oldText: 'abc', - newText: 'def', - }, - ]), + formatCode: function () { + return Promise.resolve([{ + oldRange: new _atom.Range([0, 0], [0, 3]), + oldText: 'abc', + newText: 'def' + }]); + } }); textEditor.setText('abc'); - atom.commands.dispatch( - atom.views.getView(textEditor), - 'code-format:format-code', - ); - waitsFor(() => textEditor.getText() === 'def'); - }); + atom.commands.dispatch(atom.views.getView(textEditor), 'code-format:format-code'); + waitsFor(function () { + return textEditor.getText() === 'def'; + }); + })); }); it('format an editor using formatEntireFile', () => { - waitsForPromise(async () => { - const manager = new CodeFormatManager(); + waitsForPromise((0, _asyncToGenerator.default)(function* () { + const manager = new (_CodeFormatManager || _load_CodeFormatManager()).default(); manager.addFileProvider({ grammarScopes: ['text.plain.null-grammar'], priority: 1, - formatEntireFile: () => Promise.resolve({formatted: 'ghi'}), + formatEntireFile: function () { + return Promise.resolve({ formatted: 'ghi' }); + } }); textEditor.setText('abc'); - atom.commands.dispatch( - atom.views.getView(textEditor), - 'code-format:format-code', - ); - waitsFor(() => textEditor.getText() === 'ghi'); - }); + atom.commands.dispatch(atom.views.getView(textEditor), 'code-format:format-code'); + waitsFor(function () { + return textEditor.getText() === 'ghi'; + }); + })); }); it('formats an editor on type', () => { - waitsForPromise(async () => { - spyOn(config, 'getFormatOnType').andReturn(true); - const manager = new CodeFormatManager(); + waitsForPromise((0, _asyncToGenerator.default)(function* () { + spyOn(_config || _load_config(), 'getFormatOnType').andReturn(true); + const manager = new (_CodeFormatManager || _load_CodeFormatManager()).default(); const provider = { grammarScopes: ['text.plain.null-grammar'], priority: 1, - formatAtPosition: () => - Promise.resolve([ - { - oldRange: new Range([0, 0], [0, 3]), - oldText: 'abc', - newText: 'def', - }, - ]), + formatAtPosition: function () { + return Promise.resolve([{ + oldRange: new _atom.Range([0, 0], [0, 3]), + oldText: 'abc', + newText: 'def' + }]); + } }; const spy = spyOn(provider, 'formatAtPosition').andCallThrough(); manager.addOnTypeProvider(provider); @@ -92,46 +113,49 @@ describe('CodeFormatManager', () => { textEditor.insertText('b'); textEditor.insertText('c'); - waitsFor(() => textEditor.getText() === 'def'); - runs(() => { + waitsFor(function () { + return textEditor.getText() === 'def'; + }); + runs(function () { // Debouncing should ensure only one format call. expect(spy.callCount).toBe(1); }); - }); + })); }); it('formats an editor on save', () => { - waitsForPromise(async () => { - spyOn(config, 'getFormatOnSave').andReturn(true); - const manager = new CodeFormatManager(); + waitsForPromise((0, _asyncToGenerator.default)(function* () { + spyOn(_config || _load_config(), 'getFormatOnSave').andReturn(true); + const manager = new (_CodeFormatManager || _load_CodeFormatManager()).default(); manager.addOnSaveProvider({ grammarScopes: ['text.plain.null-grammar'], priority: 1, - formatOnSave: () => - Promise.resolve([ - { - oldRange: new Range([0, 0], [0, 3]), - oldText: 'abc', - newText: 'def', - }, - ]), + formatOnSave: function () { + return Promise.resolve([{ + oldRange: new _atom.Range([0, 0], [0, 3]), + oldText: 'abc', + newText: 'def' + }]); + } }); textEditor.setText('abc'); - await textEditor.save(); + yield textEditor.save(); expect(textEditor.getText()).toBe('def'); - }); + })); }); it('should still save on timeout', () => { - waitsForPromise(async () => { + waitsForPromise((0, _asyncToGenerator.default)(function* () { jasmine.Clock.useMock(); - spyOn(config, 'getFormatOnSave').andReturn(true); - const manager = new CodeFormatManager(); + spyOn(_config || _load_config(), 'getFormatOnSave').andReturn(true); + const manager = new (_CodeFormatManager || _load_CodeFormatManager()).default(); manager.addRangeProvider({ grammarScopes: ['text.plain.null-grammar'], priority: 1, - formatCode: () => new Promise(() => {}), + formatCode: function () { + return new Promise(function () {}); + } }); const spy = spyOn(textEditor.getBuffer(), 'save').andCallThrough(); @@ -139,20 +163,28 @@ describe('CodeFormatManager', () => { const savePromise = Promise.resolve(textEditor.save()); // The first save should be pushed through after the 2nd. - waitsFor(() => spy.callCount === 1); + waitsFor(function () { + return spy.callCount === 1; + }); - runs(() => { + runs(function () { jasmine.Clock.tick(3000); }); // Hitting the timeout will force the 2nd save through. - waitsFor(() => spy.callCount === 2); + waitsFor(function () { + return spy.callCount === 2; + }); // The real save should still go through. - waitsForPromise(() => savePromise); + waitsForPromise(function () { + return savePromise; + }); // Sanity check. - runs(() => expect(spy.callCount).toBe(2)); - }); + runs(function () { + return expect(spy.callCount).toBe(2); + }); + })); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/CodeHighlightManager.js b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/CodeHighlightManager.js index cb79ee91..920b4583 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/CodeHighlightManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/CodeHighlightManager.js @@ -1,3 +1,51 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +var _log4js; + +function _load_log4js() { + return _log4js = require('log4js'); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _event; + +function _load_event() { + return _event = require('nuclide-commons/event'); +} + +var _observable; + +function _load_observable() { + return _observable = require('nuclide-commons/observable'); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _ProviderRegistry; + +function _load_ProviderRegistry() { + return _ProviderRegistry = _interopRequireDefault(require('nuclide-commons-atom/ProviderRegistry')); +} + +var _debounced; + +function _load_debounced() { + return _debounced = require('nuclide-commons-atom/debounced'); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,135 +54,100 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {CodeHighlightProvider} from './types'; - -import {getLogger} from 'log4js'; -import {Observable} from 'rxjs'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {fastDebounce, toggle} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import {observeActiveEditorsDebounced} from 'nuclide-commons-atom/debounced'; - const CURSOR_DELAY_MS = 250; // Apply a much higher debounce to text changes to avoid disrupting the typing experience. const CHANGE_TOGGLE_MS = 2500; -export default class CodeHighlightManager { - _subscriptions: UniversalDisposable; - _providers: ProviderRegistry; - _markers: Array; +class CodeHighlightManager { constructor() { - this._providers = new ProviderRegistry(); + this._providers = new (_ProviderRegistry || _load_ProviderRegistry()).default(); this._markers = []; - this._subscriptions = new UniversalDisposable(this._highlightEditors()); + this._subscriptions = new (_UniversalDisposable || _load_UniversalDisposable()).default(this._highlightEditors()); } - _highlightEditors(): rxjs$Subscription { - return observeActiveEditorsDebounced(0) - .do(() => this._destroyMarkers()) - .switchMap(editor => { - if (editor == null) { - return Observable.empty(); - } - const cursorPositions = observableFromSubscribeFunction( - editor.onDidChangeCursorPosition.bind(editor), - ) - .filter( - // If we're moving around inside highlighted ranges, that's fine. - event => - !this._isPositionInHighlightedRanges( - editor, - event.newBufferPosition, - ), - ) - .do(() => this._destroyMarkers()) // Immediately clear previous markers. - .let(fastDebounce(CURSOR_DELAY_MS)) - .startWith((null: any)) // Immediately kick off a highlight event. - .map(() => editor.getCursorBufferPosition()); - - // Changing text triggers a CHANGE_TOGGLE_MS period in which cursor changes are ignored. - // We'll model this as one stream that emits 'false' and another that debounces 'true's. - const changeEvents = observableFromSubscribeFunction( - editor.onDidChange.bind(editor), - ) - .do(() => this._destroyMarkers()) - .share(); - - const changeToggles = Observable.merge( - Observable.of(true), - changeEvents.mapTo(false), - changeEvents.let(fastDebounce(CHANGE_TOGGLE_MS)).mapTo(true), - ); - - const destroyEvents = observableFromSubscribeFunction( - editor.onDidDestroy.bind(editor), - ); - - return cursorPositions - .let(toggle(changeToggles)) - .switchMap(async position => { - return { - editor, - ranges: await this._getHighlightedRanges(editor, position), - }; - }) - .takeUntil(destroyEvents); - }) - .subscribe(({editor, ranges}) => { - if (ranges != null) { - this._highlightRanges(editor, ranges); - } - }); + _highlightEditors() { + var _this = this; + + return (0, (_debounced || _load_debounced()).observeActiveEditorsDebounced)(0).do(() => this._destroyMarkers()).switchMap(editor => { + if (editor == null) { + return _rxjsBundlesRxMinJs.Observable.empty(); + } + const cursorPositions = (0, (_event || _load_event()).observableFromSubscribeFunction)(editor.onDidChangeCursorPosition.bind(editor)).filter( + // If we're moving around inside highlighted ranges, that's fine. + event => !this._isPositionInHighlightedRanges(editor, event.newBufferPosition)).do(() => this._destroyMarkers()) // Immediately clear previous markers. + .let((0, (_observable || _load_observable()).fastDebounce)(CURSOR_DELAY_MS)).startWith(null) // Immediately kick off a highlight event. + .map(() => editor.getCursorBufferPosition()); + + // Changing text triggers a CHANGE_TOGGLE_MS period in which cursor changes are ignored. + // We'll model this as one stream that emits 'false' and another that debounces 'true's. + const changeEvents = (0, (_event || _load_event()).observableFromSubscribeFunction)(editor.onDidChange.bind(editor)).do(() => this._destroyMarkers()).share(); + + const changeToggles = _rxjsBundlesRxMinJs.Observable.merge(_rxjsBundlesRxMinJs.Observable.of(true), changeEvents.mapTo(false), changeEvents.let((0, (_observable || _load_observable()).fastDebounce)(CHANGE_TOGGLE_MS)).mapTo(true)); + + const destroyEvents = (0, (_event || _load_event()).observableFromSubscribeFunction)(editor.onDidDestroy.bind(editor)); + + return cursorPositions.let((0, (_observable || _load_observable()).toggle)(changeToggles)).switchMap((() => { + var _ref = (0, _asyncToGenerator.default)(function* (position) { + return { + editor, + ranges: yield _this._getHighlightedRanges(editor, position) + }; + }); + + return function (_x) { + return _ref.apply(this, arguments); + }; + })()).takeUntil(destroyEvents); + }).subscribe(({ editor, ranges }) => { + if (ranges != null) { + this._highlightRanges(editor, ranges); + } + }); } - async _getHighlightedRanges( - editor: atom$TextEditor, - position: atom$Point, - ): Promise> { - const provider = this._providers.getProviderForEditor(editor); - if (!provider) { - return null; - } - - try { - return await provider.highlight(editor, position); - } catch (e) { - getLogger('code-highlight').error('Error getting code highlights', e); - return null; - } + _getHighlightedRanges(editor, position) { + var _this2 = this; + + return (0, _asyncToGenerator.default)(function* () { + const provider = _this2._providers.getProviderForEditor(editor); + if (!provider) { + return null; + } + + try { + return yield provider.highlight(editor, position); + } catch (e) { + (0, (_log4js || _load_log4js()).getLogger)('code-highlight').error('Error getting code highlights', e); + return null; + } + })(); } - _highlightRanges(editor: atom$TextEditor, ranges: Array): void { + _highlightRanges(editor, ranges) { this._destroyMarkers(); this._markers = ranges.map(range => editor.markBufferRange(range, {})); this._markers.forEach(marker => { editor.decorateMarker(marker, { type: 'highlight', - class: 'code-highlight-marker', + class: 'code-highlight-marker' }); }); } - _isPositionInHighlightedRanges( - editor: atom$TextEditor, - position: atom$Point, - ): boolean { - return this._markers - .map(marker => marker.getBufferRange()) - .some(range => range.containsPoint(position)); + _isPositionInHighlightedRanges(editor, position) { + return this._markers.map(marker => marker.getBufferRange()).some(range => range.containsPoint(position)); } - _destroyMarkers(): void { + _destroyMarkers() { this._markers.splice(0).forEach(marker => marker.destroy()); } - addProvider(provider: CodeHighlightProvider): IDisposable { + addProvider(provider) { return this._providers.addProvider(provider); } @@ -143,3 +156,4 @@ export default class CodeHighlightManager { this._destroyMarkers(); } } +exports.default = CodeHighlightManager; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/main.js index 6bdcf457..1c2b2bb3 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/main.js @@ -1,34 +1,42 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {CodeHighlightProvider} from './types'; - -import createPackage from 'nuclide-commons-atom/createPackage'; -import CodeHighlightManager from './CodeHighlightManager'; +'use strict'; + +var _createPackage; + +function _load_createPackage() { + return _createPackage = _interopRequireDefault(require('nuclide-commons-atom/createPackage')); +} + +var _CodeHighlightManager; + +function _load_CodeHighlightManager() { + return _CodeHighlightManager = _interopRequireDefault(require('./CodeHighlightManager')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } class Activation { - _codeHighlightManager: CodeHighlightManager; constructor() { - this._codeHighlightManager = new CodeHighlightManager(); + this._codeHighlightManager = new (_CodeHighlightManager || _load_CodeHighlightManager()).default(); } dispose() { this._codeHighlightManager.dispose(); } - addProvider(provider: CodeHighlightProvider): IDisposable { + addProvider(provider) { return this._codeHighlightManager.addProvider(provider); } -} +} /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ -createPackage(module.exports, Activation); +(0, (_createPackage || _load_createPackage()).default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/types.js index 167d70c1..9a390c31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/lib/types.js @@ -1,20 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -export type CodeHighlightProvider = { - highlight( - editor: atom$TextEditor, - bufferPosition: atom$Point, - ): Promise>, - priority: number, - grammarScopes: Array, -}; +"use strict"; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/spec/CodeHighlightManager-spec.js b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/spec/CodeHighlightManager-spec.js index 0e32da1a..d87c02ea 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-code-highlight/spec/CodeHighlightManager-spec.js +++ b/modules/atom-ide-ui/pkg/atom-ide-code-highlight/spec/CodeHighlightManager-spec.js @@ -1,3 +1,25 @@ +'use strict'; + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +var _os = _interopRequireDefault(require('os')); + +var _nuclideUri; + +function _load_nuclideUri() { + return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri')); +} + +var _atom = require('atom'); + +var _CodeHighlightManager; + +function _load_CodeHighlightManager() { + return _CodeHighlightManager = _interopRequireDefault(require('../lib/CodeHighlightManager')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,40 +28,34 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import os from 'os'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {Point, Range} from 'atom'; - -import CodeHighlightManager from '../lib/CodeHighlightManager'; - describe('CodeHighlightManager', () => { let manager; let provider; let editor; beforeEach(() => { jasmine.useMockClock(); - waitsForPromise(async () => { - editor = await atom.workspace.open( - nuclideUri.join(os.tmpdir(), 'test.txt'), - ); + waitsForPromise((0, _asyncToGenerator.default)(function* () { + editor = yield atom.workspace.open((_nuclideUri || _load_nuclideUri()).default.join(_os.default.tmpdir(), 'test.txt')); editor.setText('abc\ndef\nghi'); - manager = new CodeHighlightManager(); + manager = new (_CodeHighlightManager || _load_CodeHighlightManager()).default(); provider = { priority: 1, grammarScopes: ['text.plain.null-grammar'], - highlight: (_editor, position) => Promise.resolve([]), + highlight: function (_editor, position) { + return Promise.resolve([]); + } }; manager.addProvider(provider); - }); + })); }); it('updates highlights on cursor move', () => { - const ranges = [new Range([0, 0], [0, 3])]; + const ranges = [new _atom.Range([0, 0], [0, 3])]; const spy = spyOn(provider, 'highlight').andReturn(ranges); // Just opening the editor should trigger highlights. @@ -52,8 +68,8 @@ describe('CodeHighlightManager', () => { waitsFor(() => manager._markers.length === 1); runs(() => { - ranges[0] = new Range([1, 0], [1, 3]); - editor.setCursorBufferPosition(new Point(1, 0)); + ranges[0] = new _atom.Range([1, 0], [1, 3]); + editor.setCursorBufferPosition(new _atom.Point(1, 0)); advanceClock(300); // trigger debounce // Old markers should be cleared immediately. expect(manager._markers.length).toBe(0); @@ -64,13 +80,11 @@ describe('CodeHighlightManager', () => { // If we're still inside the range, don't fire a new event. runs(() => { - editor.setCursorBufferPosition(new Point(1, 1)); + editor.setCursorBufferPosition(new _atom.Point(1, 1)); expect(spy.callCount).toBe(2); }); - waitsForPromise(() => - atom.workspace.open(nuclideUri.join(os.tmpdir(), 'test2.txt')), - ); + waitsForPromise(() => atom.workspace.open((_nuclideUri || _load_nuclideUri()).default.join(_os.default.tmpdir(), 'test2.txt'))); runs(() => { // Opening a new editor should clear out old markers. @@ -80,7 +94,7 @@ describe('CodeHighlightManager', () => { }); it('updates highlights on change', () => { - const ranges = [new Range([0, 0], [0, 1])]; + const ranges = [new _atom.Range([0, 0], [0, 1])]; const spy = spyOn(provider, 'highlight').andReturn(ranges); runs(() => { @@ -99,4 +113,4 @@ describe('CodeHighlightManager', () => { expect(manager._markers.length).toBe(0); }); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/getCurrentExecutorId.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/getCurrentExecutorId.js index 37e662b8..4ee4fd43 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/getCurrentExecutorId.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/getCurrentExecutorId.js @@ -1,22 +1,24 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -import type {AppState} from './types'; - -export default function getCurrentExecutorId(state: AppState): ?string { - let {currentExecutorId} = state; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = getCurrentExecutorId; +function getCurrentExecutorId(state) { + let { currentExecutorId } = state; if (currentExecutorId == null) { const firstExecutor = Array.from(state.executors.values())[0]; currentExecutorId = firstExecutor && firstExecutor.id; } return currentExecutorId; -} +} /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/main.js index 28d247e9..d7cd9abc 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/main.js @@ -1,3 +1,91 @@ +'use strict'; + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +var _immutable; + +function _load_immutable() { + return _immutable = require('immutable'); +} + +var _createPackage; + +function _load_createPackage() { + return _createPackage = _interopRequireDefault(require('nuclide-commons-atom/createPackage')); +} + +var _destroyItemWhere; + +function _load_destroyItemWhere() { + return _destroyItemWhere = require('nuclide-commons-atom/destroyItemWhere'); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _reduxObservable; + +function _load_reduxObservable() { + return _reduxObservable = require('nuclide-commons/redux-observable'); +} + +var _event; + +function _load_event() { + return _event = require('nuclide-commons/event'); +} + +var _featureConfig; + +function _load_featureConfig() { + return _featureConfig = _interopRequireDefault(require('nuclide-commons-atom/feature-config')); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _Actions; + +function _load_Actions() { + return _Actions = _interopRequireWildcard(require('./redux/Actions')); +} + +var _Epics; + +function _load_Epics() { + return _Epics = _interopRequireWildcard(require('./redux/Epics')); +} + +var _Reducers; + +function _load_Reducers() { + return _Reducers = _interopRequireDefault(require('./redux/Reducers')); +} + +var _Console; + +function _load_Console() { + return _Console = require('./ui/Console'); +} + +var _redux; + +function _load_redux() { + return _redux = require('redux'); +} + +var _ToolbarUtils; + +function _load_ToolbarUtils() { + return _ToolbarUtils = require('nuclide-commons-ui/ToolbarUtils'); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,109 +94,39 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type { - AppState, - ConsolePersistedState, - ConsoleService, - SourceInfo, - Message, - OutputProvider, - OutputProviderStatus, - OutputService, - Record, - RegisterExecutorFunction, - WatchEditorFunction, - Store, -} from './types'; -import type {CreatePasteFunction} from './types'; - -import {List} from 'immutable'; -import createPackage from 'nuclide-commons-atom/createPackage'; -import {destroyItemWhere} from 'nuclide-commons-atom/destroyItemWhere'; -import {Observable} from 'rxjs'; -import { - combineEpics, - createEpicMiddleware, -} from 'nuclide-commons/redux-observable'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import featureConfig from 'nuclide-commons-atom/feature-config'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as Actions from './redux/Actions'; -import * as Epics from './redux/Epics'; -import Reducers from './redux/Reducers'; -import {Console, WORKSPACE_VIEW_URI} from './ui/Console'; -import invariant from 'assert'; -import {applyMiddleware, createStore} from 'redux'; -import {makeToolbarButtonSpec} from 'nuclide-commons-ui/ToolbarUtils'; - -const MAXIMUM_SERIALIZED_MESSAGES_CONFIG = - 'atom-ide-console.maximumSerializedMessages'; -const MAXIMUM_SERIALIZED_HISTORY_CONFIG = - 'atom-ide-console.maximumSerializedHistory'; +const MAXIMUM_SERIALIZED_MESSAGES_CONFIG = 'atom-ide-console.maximumSerializedMessages'; +const MAXIMUM_SERIALIZED_HISTORY_CONFIG = 'atom-ide-console.maximumSerializedHistory'; class Activation { - _disposables: UniversalDisposable; - _rawState: ?Object; - _store: Store; - constructor(rawState: ?Object) { + constructor(rawState) { this._rawState = rawState; - this._disposables = new UniversalDisposable( - atom.contextMenu.add({ - '.console-record': [ - { - label: 'Copy Message', - command: 'console:copy-message', - }, - ], - }), - atom.commands.add('.console-record', 'console:copy-message', event => { - const el = event.target; - if (el == null || typeof el.innerText !== 'string') { - return; - } - atom.clipboard.write(el.innerText); - }), - atom.commands.add('atom-workspace', 'console:clear', () => - this._getStore().dispatch(Actions.clearRecords()), - ), - featureConfig.observe( - 'atom-ide-console.maximumMessageCount', - (maxMessageCount: any) => { - this._getStore().dispatch( - Actions.setMaxMessageCount(maxMessageCount), - ); - }, - ), - Observable.combineLatest( - observableFromSubscribeFunction(cb => - atom.config.observe('editor.fontSize', cb), - ), - featureConfig.observeAsStream('atom-ide-console.consoleFontScale'), - (fontSize, consoleFontScale) => fontSize * parseFloat(consoleFontScale), - ) - .map(Actions.setFontSize) - .subscribe(this._store.dispatch), - this._registerCommandAndOpener(), - ); + this._disposables = new (_UniversalDisposable || _load_UniversalDisposable()).default(atom.contextMenu.add({ + '.console-record': [{ + label: 'Copy Message', + command: 'console:copy-message' + }] + }), atom.commands.add('.console-record', 'console:copy-message', event => { + const el = event.target; + if (el == null || typeof el.innerText !== 'string') { + return; + } + atom.clipboard.write(el.innerText); + }), atom.commands.add('atom-workspace', 'console:clear', () => this._getStore().dispatch((_Actions || _load_Actions()).clearRecords())), (_featureConfig || _load_featureConfig()).default.observe('atom-ide-console.maximumMessageCount', maxMessageCount => { + this._getStore().dispatch((_Actions || _load_Actions()).setMaxMessageCount(maxMessageCount)); + }), _rxjsBundlesRxMinJs.Observable.combineLatest((0, (_event || _load_event()).observableFromSubscribeFunction)(cb => atom.config.observe('editor.fontSize', cb)), (_featureConfig || _load_featureConfig()).default.observeAsStream('atom-ide-console.consoleFontScale'), (fontSize, consoleFontScale) => fontSize * parseFloat(consoleFontScale)).map((_Actions || _load_Actions()).setFontSize).subscribe(this._store.dispatch), this._registerCommandAndOpener()); } - _getStore(): Store { + _getStore() { if (this._store == null) { const initialState = deserializeAppState(this._rawState); - const epics = Object.keys(Epics) - .map(k => Epics[k]) - .filter(epic => typeof epic === 'function'); - const rootEpic = combineEpics(...epics); - this._store = createStore( - Reducers, - initialState, - applyMiddleware(createEpicMiddleware(rootEpic)), - ); + const epics = Object.keys(_Epics || _load_Epics()).map(k => (_Epics || _load_Epics())[k]).filter(epic => typeof epic === 'function'); + const rootEpic = (0, (_reduxObservable || _load_reduxObservable()).combineEpics)(...epics); + this._store = (0, (_redux || _load_redux()).createStore)((_Reducers || _load_Reducers()).default, initialState, (0, (_redux || _load_redux()).applyMiddleware)((0, (_reduxObservable || _load_reduxObservable()).createEpicMiddleware)(rootEpic))); } return this._store; } @@ -117,80 +135,78 @@ class Activation { this._disposables.dispose(); } - consumeToolBar(getToolBar: toolbar$GetToolbar): void { + consumeToolBar(getToolBar) { const toolBar = getToolBar('nuclide-console'); - toolBar.addButton( - makeToolbarButtonSpec({ - icon: 'terminal', - callback: 'console:toggle', - tooltip: 'Toggle Console', - priority: 700, - }), - ); + toolBar.addButton((0, (_ToolbarUtils || _load_ToolbarUtils()).makeToolbarButtonSpec)({ + icon: 'terminal', + callback: 'console:toggle', + tooltip: 'Toggle Console', + priority: 700 + })); this._disposables.add(() => { toolBar.removeItems(); }); } - consumePasteProvider(provider: any): IDisposable { - const createPaste: CreatePasteFunction = provider.createPaste; - this._getStore().dispatch(Actions.setCreatePasteFunction(createPaste)); - return new UniversalDisposable(() => { + consumePasteProvider(provider) { + const createPaste = provider.createPaste; + this._getStore().dispatch((_Actions || _load_Actions()).setCreatePasteFunction(createPaste)); + return new (_UniversalDisposable || _load_UniversalDisposable()).default(() => { if (this._getStore().getState().createPasteFunction === createPaste) { - this._getStore().dispatch(Actions.setCreatePasteFunction(null)); + this._getStore().dispatch((_Actions || _load_Actions()).setCreatePasteFunction(null)); } }); } - consumeWatchEditor(watchEditor: WatchEditorFunction): IDisposable { - this._getStore().dispatch(Actions.setWatchEditor(watchEditor)); - return new UniversalDisposable(() => { + consumeWatchEditor(watchEditor) { + this._getStore().dispatch((_Actions || _load_Actions()).setWatchEditor(watchEditor)); + return new (_UniversalDisposable || _load_UniversalDisposable()).default(() => { if (this._getStore().getState().watchEditor === watchEditor) { - this._getStore().dispatch(Actions.setWatchEditor(null)); + this._getStore().dispatch((_Actions || _load_Actions()).setWatchEditor(null)); } }); } - provideAutocomplete(): atom$AutocompleteProvider { + provideAutocomplete() { const activation = this; return { labels: ['nuclide-console'], selector: '*', // Copies Chrome devtools and puts history suggestions at the bottom. suggestionPriority: -1, - async getSuggestions(request) { - // History provides suggestion only on exact match to current input. - const prefix = request.editor.getText(); - const history = activation._getStore().getState().history; - // Use a set to remove duplicates. - const seen = new Set(history); - return Array.from(seen) - .filter(text => text.startsWith(prefix)) - .map(text => ({text, replacementPrefix: prefix})); - }, + getSuggestions(request) { + return (0, _asyncToGenerator.default)(function* () { + // History provides suggestion only on exact match to current input. + const prefix = request.editor.getText(); + const history = activation._getStore().getState().history; + // Use a set to remove duplicates. + const seen = new Set(history); + return Array.from(seen).filter(function (text) { + return text.startsWith(prefix); + }).map(function (text) { + return { text, replacementPrefix: prefix }; + }); + })(); + } }; } - _registerCommandAndOpener(): UniversalDisposable { - return new UniversalDisposable( - atom.workspace.addOpener(uri => { - if (uri === WORKSPACE_VIEW_URI) { - return new Console({store: this._getStore()}); - } - }), - () => destroyItemWhere(item => item instanceof Console), - atom.commands.add('atom-workspace', 'console:toggle', () => { - atom.workspace.toggle(WORKSPACE_VIEW_URI); - }), - ); + _registerCommandAndOpener() { + return new (_UniversalDisposable || _load_UniversalDisposable()).default(atom.workspace.addOpener(uri => { + if (uri === (_Console || _load_Console()).WORKSPACE_VIEW_URI) { + return new (_Console || _load_Console()).Console({ store: this._getStore() }); + } + }), () => (0, (_destroyItemWhere || _load_destroyItemWhere()).destroyItemWhere)(item => item instanceof (_Console || _load_Console()).Console), atom.commands.add('atom-workspace', 'console:toggle', () => { + atom.workspace.toggle((_Console || _load_Console()).WORKSPACE_VIEW_URI); + })); } - deserializeConsole(state: ConsolePersistedState): Console { - return new Console({ + deserializeConsole(state) { + return new (_Console || _load_Console()).Console({ store: this._getStore(), initialFilterText: state.filterText, initialEnableRegExpFilter: state.enableRegExpFilter, - initialUnselectedSourceIds: state.unselectedSourceIds, + initialUnselectedSourceIds: state.unselectedSourceIds }); } @@ -205,7 +221,7 @@ class Activation { * package is disabled). This will remove the source from the Console UI's filter list (as long as * there aren't any remaining messages from the source). */ - provideConsole(): ConsoleService { + provideConsole() { // Create a local, nullable reference so that the service consumers don't keep the Activation // instance in memory. let activation = this; @@ -213,61 +229,67 @@ class Activation { activation = null; }); - return (sourceInfo: SourceInfo) => { - invariant(activation != null); + return sourceInfo => { + if (!(activation != null)) { + throw new Error('Invariant violation: "activation != null"'); + } + let disposed; - activation._getStore().dispatch(Actions.registerSource(sourceInfo)); + activation._getStore().dispatch((_Actions || _load_Actions()).registerSource(sourceInfo)); const console = { // TODO: Update these to be (object: any, ...objects: Array): void. - log(object: string): void { - console.append({text: object, level: 'log'}); + log(object) { + console.append({ text: object, level: 'log' }); }, - warn(object: string): void { - console.append({text: object, level: 'warning'}); + warn(object) { + console.append({ text: object, level: 'warning' }); }, - error(object: string): void { - console.append({text: object, level: 'error'}); + error(object) { + console.append({ text: object, level: 'error' }); }, - info(object: string): void { - console.append({text: object, level: 'info'}); + info(object) { + console.append({ text: object, level: 'info' }); }, - append(message: Message): void { - invariant(activation != null && !disposed); - activation._getStore().dispatch( - Actions.recordReceived({ - text: message.text, - level: message.level, - data: message.data, - tags: message.tags, - scopeName: message.scopeName, - sourceId: sourceInfo.id, - kind: message.kind || 'message', - timestamp: new Date(), // TODO: Allow this to come with the message? - repeatCount: 1, - }), - ); + append(message) { + if (!(activation != null && !disposed)) { + throw new Error('Invariant violation: "activation != null && !disposed"'); + } + + activation._getStore().dispatch((_Actions || _load_Actions()).recordReceived({ + text: message.text, + level: message.level, + data: message.data, + tags: message.tags, + scopeName: message.scopeName, + sourceId: sourceInfo.id, + kind: message.kind || 'message', + timestamp: new Date(), // TODO: Allow this to come with the message? + repeatCount: 1 + })); }, - setStatus(status: OutputProviderStatus): void { - invariant(activation != null && !disposed); - activation - ._getStore() - .dispatch(Actions.updateStatus(sourceInfo.id, status)); + setStatus(status) { + if (!(activation != null && !disposed)) { + throw new Error('Invariant violation: "activation != null && !disposed"'); + } + + activation._getStore().dispatch((_Actions || _load_Actions()).updateStatus(sourceInfo.id, status)); }, - dispose(): void { - invariant(activation != null); + dispose() { + if (!(activation != null)) { + throw new Error('Invariant violation: "activation != null"'); + } + if (!disposed) { disposed = true; - activation - ._getStore() - .dispatch(Actions.removeSource(sourceInfo.id)); + activation._getStore().dispatch((_Actions || _load_Actions()).removeSource(sourceInfo.id)); } - }, + } }; return console; }; } - provideOutputService(): OutputService { + provideOutputService() { // Create a local, nullable reference so that the service consumers don't keep the Activation // instance in memory. let activation = this; @@ -276,23 +298,22 @@ class Activation { }); return { - registerOutputProvider(outputProvider: OutputProvider): IDisposable { - invariant(activation != null, 'Output service used after deactivation'); - activation - ._getStore() - .dispatch(Actions.registerOutputProvider(outputProvider)); - return new UniversalDisposable(() => { + registerOutputProvider(outputProvider) { + if (!(activation != null)) { + throw new Error('Output service used after deactivation'); + } + + activation._getStore().dispatch((_Actions || _load_Actions()).registerOutputProvider(outputProvider)); + return new (_UniversalDisposable || _load_UniversalDisposable()).default(() => { if (activation != null) { - activation - ._getStore() - .dispatch(Actions.unregisterOutputProvider(outputProvider)); + activation._getStore().dispatch((_Actions || _load_Actions()).unregisterOutputProvider(outputProvider)); } }); - }, + } }; } - provideRegisterExecutor(): RegisterExecutorFunction { + provideRegisterExecutor() { // Create a local, nullable reference so that the service consumers don't keep the Activation // instance in memory. let activation = this; @@ -301,66 +322,55 @@ class Activation { }); return executor => { - invariant( - activation != null, - 'Executor registration attempted after deactivation', - ); - activation._getStore().dispatch(Actions.registerExecutor(executor)); - return new UniversalDisposable(() => { + if (!(activation != null)) { + throw new Error('Executor registration attempted after deactivation'); + } + + activation._getStore().dispatch((_Actions || _load_Actions()).registerExecutor(executor)); + return new (_UniversalDisposable || _load_UniversalDisposable()).default(() => { if (activation != null) { - activation._getStore().dispatch(Actions.unregisterExecutor(executor)); + activation._getStore().dispatch((_Actions || _load_Actions()).unregisterExecutor(executor)); } }); }; } - serialize(): Object { + serialize() { if (this._store == null) { return {}; } - const maximumSerializedMessages: number = (featureConfig.get( - MAXIMUM_SERIALIZED_MESSAGES_CONFIG, - ): any); - const maximumSerializedHistory: number = (featureConfig.get( - MAXIMUM_SERIALIZED_HISTORY_CONFIG, - ): any); + const maximumSerializedMessages = (_featureConfig || _load_featureConfig()).default.get(MAXIMUM_SERIALIZED_MESSAGES_CONFIG); + const maximumSerializedHistory = (_featureConfig || _load_featureConfig()).default.get(MAXIMUM_SERIALIZED_HISTORY_CONFIG); return { - records: this._store - .getState() - .records.slice(-maximumSerializedMessages) - .toArray(), - history: this._store.getState().history.slice(-maximumSerializedHistory), + records: this._store.getState().records.slice(-maximumSerializedMessages).toArray(), + history: this._store.getState().history.slice(-maximumSerializedHistory) }; } } -function deserializeAppState(rawState: ?Object): AppState { +function deserializeAppState(rawState) { return { executors: new Map(), createPasteFunction: null, currentExecutorId: null, - records: - rawState && rawState.records - ? List(rawState.records.map(deserializeRecord)) - : List(), + records: rawState && rawState.records ? (0, (_immutable || _load_immutable()).List)(rawState.records.map(deserializeRecord)) : (0, (_immutable || _load_immutable()).List)(), history: rawState && rawState.history ? rawState.history : [], providers: new Map(), providerStatuses: new Map(), // This value will be replaced with the value form the config. We just use `POSITIVE_INFINITY` // here to conform to the AppState type defintion. - maxMessageCount: Number.POSITIVE_INFINITY, + maxMessageCount: Number.POSITIVE_INFINITY }; } -function deserializeRecord(record: Object): Record { - return { - ...record, - timestamp: parseDate(record.timestamp) || new Date(0), - }; +function deserializeRecord(record) { + return Object.assign({}, record, { + timestamp: parseDate(record.timestamp) || new Date(0) + }); } -function parseDate(raw: ?string): ?Date { +function parseDate(raw) { if (raw == null) { return null; } @@ -368,4 +378,4 @@ function parseDate(raw: ?string): ?Date { return isNaN(date.getTime()) ? null : date; } -createPackage(module.exports, Activation); +(0, (_createPackage || _load_createPackage()).default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/recordsChanged.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/recordsChanged.js index ab27f72a..8577bdab 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/recordsChanged.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/recordsChanged.js @@ -1,3 +1,22 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = recordsChanged; + +var _idx; + +function _load_idx() { + return _idx = _interopRequireDefault(require('idx')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Check to see if the records have changed. This is optimized to take advantage of the knowledge + * knowledge that record lists are only ever appended. + */ /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,25 +25,14 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {DisplayableRecord} from './types'; +function recordsChanged(a, b) { + var _ref, _ref2; -import idx from 'idx'; - -/** - * Check to see if the records have changed. This is optimized to take advantage of the knowledge - * knowledge that record lists are only ever appended. - */ -export default function recordsChanged( - a: Array, - b: Array, -): boolean { - return ( - a.length !== b.length || idx(last(a), _ => _.id) !== idx(last(b), _ => _.id) - ); + return a.length !== b.length || ((_ref = last(a)) != null ? _ref.id : _ref) !== ((_ref2 = last(b)) != null ? _ref2.id : _ref2); } -const last = arr => arr[arr.length - 1]; +const last = arr => arr[arr.length - 1]; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Actions.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Actions.js index 66bf0761..842334b9 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Actions.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Actions.js @@ -1,3 +1,25 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.clearRecords = clearRecords; +exports.recordReceived = recordReceived; +exports.registerExecutor = registerExecutor; +exports.execute = execute; +exports.registerOutputProvider = registerOutputProvider; +exports.registerRecordProvider = registerRecordProvider; +exports.registerSource = registerSource; +exports.unregisterRecordProvider = unregisterRecordProvider; +exports.unregisterOutputProvider = unregisterOutputProvider; +exports.selectExecutor = selectExecutor; +exports.setMaxMessageCount = setMaxMessageCount; +exports.removeSource = removeSource; +exports.unregisterExecutor = unregisterExecutor; +exports.updateStatus = updateStatus; +exports.setCreatePasteFunction = setCreatePasteFunction; +exports.setWatchEditor = setWatchEditor; +exports.setFontSize = setFontSize; /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,68 +28,54 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type { - Action, - Executor, - OutputProvider, - OutputProviderStatus, - Record, - RecordProvider, - SourceInfo, - WatchEditorFunction, -} from '../types'; - -import type {CreatePasteFunction} from '../types'; - -export const CLEAR_RECORDS = 'CLEAR_RECORDS'; -export const SET_CREATE_PASTE_FUNCTION = 'SET_CREATE_PASTE_FUNCTION'; -export const SET_WATCH_EDITOR_FUNCTION = 'SET_WATCH_EDITOR_FUNCTION'; -export const REGISTER_EXECUTOR = 'REGISTER_EXECUTOR'; -export const EXECUTE = 'EXECUTE'; -export const REGISTER_RECORD_PROVIDER = 'REGISTER_RECORD_PROVIDER'; -export const SELECT_EXECUTOR = 'SELECT_EXECUTOR'; -export const SET_MAX_MESSAGE_COUNT = 'SET_MAX_MESSAGE_COUNT'; -export const RECORD_RECEIVED = 'RECORD_RECEIVED'; -export const REGISTER_SOURCE = 'REGISTER_SOURCE'; -export const REMOVE_SOURCE = 'REMOVE_SOURCE'; -export const UPDATE_STATUS = 'UPDATE_STATUS'; -export const SET_FONT_SIZE = 'SET_FONT_SIZE'; - -export function clearRecords(): Action { - return {type: CLEAR_RECORDS}; -} - -export function recordReceived(record: Record): Action { +const CLEAR_RECORDS = exports.CLEAR_RECORDS = 'CLEAR_RECORDS'; +const SET_CREATE_PASTE_FUNCTION = exports.SET_CREATE_PASTE_FUNCTION = 'SET_CREATE_PASTE_FUNCTION'; +const SET_WATCH_EDITOR_FUNCTION = exports.SET_WATCH_EDITOR_FUNCTION = 'SET_WATCH_EDITOR_FUNCTION'; +const REGISTER_EXECUTOR = exports.REGISTER_EXECUTOR = 'REGISTER_EXECUTOR'; +const EXECUTE = exports.EXECUTE = 'EXECUTE'; +const REGISTER_RECORD_PROVIDER = exports.REGISTER_RECORD_PROVIDER = 'REGISTER_RECORD_PROVIDER'; +const SELECT_EXECUTOR = exports.SELECT_EXECUTOR = 'SELECT_EXECUTOR'; +const SET_MAX_MESSAGE_COUNT = exports.SET_MAX_MESSAGE_COUNT = 'SET_MAX_MESSAGE_COUNT'; +const RECORD_RECEIVED = exports.RECORD_RECEIVED = 'RECORD_RECEIVED'; +const REGISTER_SOURCE = exports.REGISTER_SOURCE = 'REGISTER_SOURCE'; +const REMOVE_SOURCE = exports.REMOVE_SOURCE = 'REMOVE_SOURCE'; +const UPDATE_STATUS = exports.UPDATE_STATUS = 'UPDATE_STATUS'; +const SET_FONT_SIZE = exports.SET_FONT_SIZE = 'SET_FONT_SIZE'; + +function clearRecords() { + return { type: CLEAR_RECORDS }; +} + +function recordReceived(record) { return { type: RECORD_RECEIVED, - payload: {record}, + payload: { record } }; } -export function registerExecutor(executor: Executor): Action { +function registerExecutor(executor) { return { type: REGISTER_EXECUTOR, - payload: {executor}, + payload: { executor } }; } -export function execute(code: string): Action { +function execute(code) { return { type: EXECUTE, - payload: {code}, + payload: { code } }; } -export function registerOutputProvider(outputProvider: OutputProvider): Action { +function registerOutputProvider(outputProvider) { // Transform the messages into actions and merge them into the action stream. // TODO: Add enabling/disabling of registered source and only subscribe when enabled. That // way, we won't trigger cold observer side-effects when we don't need the results. - return registerRecordProvider({ - ...outputProvider, + return registerRecordProvider(Object.assign({}, outputProvider, { records: outputProvider.messages.map(message => ({ // We duplicate the properties here instead of using spread because Flow (currently) has some // issues with spread. @@ -81,91 +89,82 @@ export function registerOutputProvider(outputProvider: OutputProvider): Action { sourceId: outputProvider.id, scopeName: null, // Eventually, we'll want to allow providers to specify custom timestamps for records. - timestamp: new Date(), - })), - }); + timestamp: new Date() + })) + })); } -export function registerRecordProvider(recordProvider: RecordProvider): Action { +function registerRecordProvider(recordProvider) { return { type: REGISTER_RECORD_PROVIDER, - payload: {recordProvider}, + payload: { recordProvider } }; } -export function registerSource(source: SourceInfo): Action { +function registerSource(source) { return { type: REGISTER_SOURCE, - payload: {source}, + payload: { source } }; } -export function unregisterRecordProvider( - recordProvider: RecordProvider, -): Action { +function unregisterRecordProvider(recordProvider) { return removeSource(recordProvider.id); } -export function unregisterOutputProvider( - outputProvider: OutputProvider, -): Action { +function unregisterOutputProvider(outputProvider) { return removeSource(outputProvider.id); } -export function selectExecutor(executorId: string): Action { +function selectExecutor(executorId) { return { type: SELECT_EXECUTOR, - payload: {executorId}, + payload: { executorId } }; } -export function setMaxMessageCount(maxMessageCount: number): Action { +function setMaxMessageCount(maxMessageCount) { return { type: SET_MAX_MESSAGE_COUNT, - payload: {maxMessageCount}, + payload: { maxMessageCount } }; } -export function removeSource(sourceId: string): Action { +function removeSource(sourceId) { return { type: REMOVE_SOURCE, - payload: {sourceId}, + payload: { sourceId } }; } -export function unregisterExecutor(executor: Executor): Action { +function unregisterExecutor(executor) { return removeSource(executor.id); } -export function updateStatus( - providerId: string, - status: OutputProviderStatus, -): Action { +function updateStatus(providerId, status) { return { type: UPDATE_STATUS, - payload: {providerId, status}, + payload: { providerId, status } }; } -export function setCreatePasteFunction( - createPasteFunction: ?CreatePasteFunction, -): Action { +function setCreatePasteFunction(createPasteFunction) { return { type: SET_CREATE_PASTE_FUNCTION, - payload: {createPasteFunction}, + payload: { createPasteFunction } }; } -export function setWatchEditor(watchEditor: ?WatchEditorFunction): Action { +function setWatchEditor(watchEditor) { return { type: SET_WATCH_EDITOR_FUNCTION, - payload: {watchEditor}, + payload: { watchEditor } }; } -export function setFontSize(fontSize: number): Action { +function setFontSize(fontSize) { return { type: SET_FONT_SIZE, - payload: {fontSize}, + payload: { fontSize } }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Epics.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Epics.js index 1d2d4f02..4aca056b 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Epics.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Epics.js @@ -1,3 +1,39 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.registerExecutorEpic = registerExecutorEpic; +exports.executeEpic = executeEpic; +exports.registerRecordProviderEpic = registerRecordProviderEpic; + +var _event; + +function _load_event() { + return _event = require('nuclide-commons/event'); +} + +var _Actions; + +function _load_Actions() { + return _Actions = _interopRequireWildcard(require('./Actions')); +} + +var _getCurrentExecutorId; + +function _load_getCurrentExecutorId() { + return _getCurrentExecutorId = _interopRequireDefault(require('../getCurrentExecutorId')); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +/** + * Register a record provider for every executor. + */ /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,41 +42,28 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {Action, Store} from '../types'; -import type {ActionsObservable} from 'nuclide-commons/redux-observable'; +function registerExecutorEpic(actions, store) { + return actions.ofType((_Actions || _load_Actions()).REGISTER_EXECUTOR).map(action => { + if (!(action.type === (_Actions || _load_Actions()).REGISTER_EXECUTOR)) { + throw new Error('Invariant violation: "action.type === Actions.REGISTER_EXECUTOR"'); + } -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import * as Actions from './Actions'; -import getCurrentExecutorId from '../getCurrentExecutorId'; -import invariant from 'assert'; -import {Observable} from 'rxjs'; - -/** - * Register a record provider for every executor. - */ -export function registerExecutorEpic( - actions: ActionsObservable, - store: Store, -): Observable { - return actions.ofType(Actions.REGISTER_EXECUTOR).map(action => { - invariant(action.type === Actions.REGISTER_EXECUTOR); - const {executor} = action.payload; - return Actions.registerRecordProvider({ + const { executor } = action.payload; + return (_Actions || _load_Actions()).registerRecordProvider({ id: executor.id, // $FlowIssue: Flow is having some trouble with the spread here. - records: executor.output.map(message => ({ - ...message, + records: executor.output.map(message => Object.assign({}, message, { kind: 'response', sourceId: executor.id, scopeName: null, // The output won't be in the language's grammar. // Eventually, we'll want to allow providers to specify custom timestamps for records. timestamp: new Date(), - executor, - })), + executor + })) }); }); } @@ -48,78 +71,72 @@ export function registerExecutorEpic( /** * Execute the provided code using the current executor. */ -export function executeEpic( - actions: ActionsObservable, - store: Store, -): Observable { - return actions.ofType(Actions.EXECUTE).flatMap(action => { - invariant(action.type === Actions.EXECUTE); - const {code} = action.payload; - const currentExecutorId = getCurrentExecutorId(store.getState()); +function executeEpic(actions, store) { + return actions.ofType((_Actions || _load_Actions()).EXECUTE).flatMap(action => { + if (!(action.type === (_Actions || _load_Actions()).EXECUTE)) { + throw new Error('Invariant violation: "action.type === Actions.EXECUTE"'); + } + + const { code } = action.payload; + const currentExecutorId = (0, (_getCurrentExecutorId || _load_getCurrentExecutorId()).default)(store.getState()); // flowlint-next-line sketchy-null-string:off - invariant(currentExecutorId); + + if (!currentExecutorId) { + throw new Error('Invariant violation: "currentExecutorId"'); + } const executor = store.getState().executors.get(currentExecutorId); - invariant(executor != null); + + if (!(executor != null)) { + throw new Error('Invariant violation: "executor != null"'); + } // TODO: Is this the best way to do this? Might want to go through nuclide-executors and have // that register output sources? - return ( - Observable.of( - Actions.recordReceived({ - // Eventually, we'll want to allow providers to specify custom timestamps for records. - timestamp: new Date(), - sourceId: currentExecutorId, - kind: 'request', - level: 'log', - text: code, - scopeName: executor.scopeName, - data: null, - repeatCount: 1, - }), - ) - // Execute the code as a side-effect. - .finally(() => { - executor.send(code); - }) - ); + + + return _rxjsBundlesRxMinJs.Observable.of((_Actions || _load_Actions()).recordReceived({ + // Eventually, we'll want to allow providers to specify custom timestamps for records. + timestamp: new Date(), + sourceId: currentExecutorId, + kind: 'request', + level: 'log', + text: code, + scopeName: executor.scopeName, + data: null, + repeatCount: 1 + })) + // Execute the code as a side-effect. + .finally(() => { + executor.send(code); + }); }); } -export function registerRecordProviderEpic( - actions: ActionsObservable, - store: Store, -): Observable { - return actions.ofType(Actions.REGISTER_RECORD_PROVIDER).flatMap(action => { - invariant(action.type === Actions.REGISTER_RECORD_PROVIDER); - const {recordProvider} = action.payload; +function registerRecordProviderEpic(actions, store) { + return actions.ofType((_Actions || _load_Actions()).REGISTER_RECORD_PROVIDER).flatMap(action => { + if (!(action.type === (_Actions || _load_Actions()).REGISTER_RECORD_PROVIDER)) { + throw new Error('Invariant violation: "action.type === Actions.REGISTER_RECORD_PROVIDER"'); + } + + const { recordProvider } = action.payload; // Transform the messages into actions and merge them into the action stream. // TODO: Add enabling/disabling of registered source and only subscribe when enabled. That // way, we won't trigger cold observer side-effects when we don't need the results. - const messageActions = recordProvider.records.map(Actions.recordReceived); + const messageActions = recordProvider.records.map((_Actions || _load_Actions()).recordReceived); // TODO: Can this be delayed until sometime after registration? - const statusActions = - typeof recordProvider.observeStatus === 'function' - ? observableFromSubscribeFunction(recordProvider.observeStatus).map( - status => Actions.updateStatus(recordProvider.id, status), - ) - : Observable.empty(); - - const unregisteredEvents = actions - .ofType(Actions.REMOVE_SOURCE) - .filter(a => { - invariant(a.type === Actions.REMOVE_SOURCE); - return a.payload.sourceId === recordProvider.id; - }); - - return Observable.merge( - Observable.of( - Actions.registerSource({...recordProvider, name: recordProvider.id}), - ), - messageActions, - statusActions, - ).takeUntil(unregisteredEvents); + const statusActions = typeof recordProvider.observeStatus === 'function' ? (0, (_event || _load_event()).observableFromSubscribeFunction)(recordProvider.observeStatus).map(status => (_Actions || _load_Actions()).updateStatus(recordProvider.id, status)) : _rxjsBundlesRxMinJs.Observable.empty(); + + const unregisteredEvents = actions.ofType((_Actions || _load_Actions()).REMOVE_SOURCE).filter(a => { + if (!(a.type === (_Actions || _load_Actions()).REMOVE_SOURCE)) { + throw new Error('Invariant violation: "a.type === Actions.REMOVE_SOURCE"'); + } + + return a.payload.sourceId === recordProvider.id; + }); + + return _rxjsBundlesRxMinJs.Observable.merge(_rxjsBundlesRxMinJs.Observable.of((_Actions || _load_Actions()).registerSource(Object.assign({}, recordProvider, { name: recordProvider.id }))), messageActions, statusActions).takeUntil(unregisteredEvents); }); -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Reducers.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Reducers.js index 310e591e..2254f550 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Reducers.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/redux/Reducers.js @@ -1,3 +1,30 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = accumulateState; + +var _immutable; + +function _load_immutable() { + return _immutable = require('immutable'); +} + +var _collection; + +function _load_collection() { + return _collection = require('nuclide-commons/collection'); +} + +var _Actions; + +function _load_Actions() { + return _Actions = _interopRequireWildcard(require('./Actions')); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,190 +33,149 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {Action, AppState, Record} from '../types'; - -import {List} from 'immutable'; -import {arrayEqual} from 'nuclide-commons/collection'; -import * as Actions from './Actions'; - -const RECORD_PROPERTIES_TO_COMPARE = [ - 'text', - 'level', - 'scopeName', - 'sourceId', - 'kind', -]; - -function shouldAccumulateRecordCount( - recordA: Record, - recordB: Record, -): boolean { - if ( - String(recordA.sourceId) - .toLowerCase() - .includes('debugger') || - String(recordB.sourceId) - .toLowerCase() - .includes('debugger') - ) { +const RECORD_PROPERTIES_TO_COMPARE = ['text', 'level', 'scopeName', 'sourceId', 'kind']; + +function shouldAccumulateRecordCount(recordA, recordB) { + if (String(recordA.sourceId).toLowerCase().includes('debugger') || String(recordB.sourceId).toLowerCase().includes('debugger')) { return false; } - const areRelevantPropertiesEqual = RECORD_PROPERTIES_TO_COMPARE.every( - prop => recordA[prop] === recordB[prop], - ); + const areRelevantPropertiesEqual = RECORD_PROPERTIES_TO_COMPARE.every(prop => recordA[prop] === recordB[prop]); // if data exists, we should not accumulate this into the previous record const doesDataExist = recordA.data || recordB.data; const recATags = recordA.tags; const recBTags = recordB.tags; - const areTagsEqual = - (!recATags && !recBTags) || - (recATags && recBTags && arrayEqual(recATags, recBTags)); - - return ( - areRelevantPropertiesEqual && - !Boolean(doesDataExist) && - Boolean(areTagsEqual) - ); + const areTagsEqual = !recATags && !recBTags || recATags && recBTags && (0, (_collection || _load_collection()).arrayEqual)(recATags, recBTags); + + return areRelevantPropertiesEqual && !Boolean(doesDataExist) && Boolean(areTagsEqual); } -export default function accumulateState( - state: AppState, - action: Action, -): AppState { +function accumulateState(state, action) { switch (action.type) { - case Actions.RECORD_RECEIVED: { - const {record} = action.payload; - let nextRecords = state.records; - - // check if the message is exactly the same as the previous one, if so - // we add a count to it. - const lastRecord = nextRecords.last(); - if ( - lastRecord != null && - shouldAccumulateRecordCount(lastRecord, record) - ) { - // Update the last record. Don't use `splice()` because that's O(n) - const updatedRecord: Record = { - ...lastRecord, - repeatCount: lastRecord.repeatCount + 1, - timestamp: record.timestamp, - }; - nextRecords = nextRecords.pop().push(updatedRecord); - } else { - nextRecords = nextRecords.push(record); - } + case (_Actions || _load_Actions()).RECORD_RECEIVED: + { + const { record } = action.payload; + let nextRecords = state.records; - if (nextRecords.size > state.maxMessageCount) { - // We could only have gone over by one. - nextRecords = nextRecords.shift(); - } + // check if the message is exactly the same as the previous one, if so + // we add a count to it. + const lastRecord = nextRecords.last(); + if (lastRecord != null && shouldAccumulateRecordCount(lastRecord, record)) { + // Update the last record. Don't use `splice()` because that's O(n) + const updatedRecord = Object.assign({}, lastRecord, { + repeatCount: lastRecord.repeatCount + 1, + timestamp: record.timestamp + }); + nextRecords = nextRecords.pop().push(updatedRecord); + } else { + nextRecords = nextRecords.push(record); + } + + if (nextRecords.size > state.maxMessageCount) { + // We could only have gone over by one. + nextRecords = nextRecords.shift(); + } - return { - ...state, - records: nextRecords, - }; - } - case Actions.SET_MAX_MESSAGE_COUNT: { - const {maxMessageCount} = action.payload; - if (maxMessageCount <= 0) { - return state; + return Object.assign({}, state, { + records: nextRecords + }); + } + case (_Actions || _load_Actions()).SET_MAX_MESSAGE_COUNT: + { + const { maxMessageCount } = action.payload; + if (maxMessageCount <= 0) { + return state; + } + return Object.assign({}, state, { + maxMessageCount, + records: state.records.slice(-maxMessageCount) + }); + } + case (_Actions || _load_Actions()).REGISTER_SOURCE: + { + const { source } = action.payload; + return Object.assign({}, state, { + providers: new Map(state.providers).set(source.id, Object.assign({}, source, { + name: source.name || source.id + })) + }); + } + case (_Actions || _load_Actions()).CLEAR_RECORDS: + { + return Object.assign({}, state, { + records: (0, (_immutable || _load_immutable()).List)() + }); + } + case (_Actions || _load_Actions()).REGISTER_EXECUTOR: + { + const { executor } = action.payload; + return Object.assign({}, state, { + executors: new Map(state.executors).set(executor.id, executor) + }); + } + case (_Actions || _load_Actions()).SELECT_EXECUTOR: + { + const { executorId } = action.payload; + return Object.assign({}, state, { + currentExecutorId: executorId + }); + } + case (_Actions || _load_Actions()).REMOVE_SOURCE: + { + const { sourceId } = action.payload; + const providers = new Map(state.providers); + const providerStatuses = new Map(state.providerStatuses); + const executors = new Map(state.executors); + providers.delete(sourceId); + providerStatuses.delete(sourceId); + executors.delete(sourceId); + return Object.assign({}, state, { + providers, + providerStatuses, + executors + }); + } + case (_Actions || _load_Actions()).UPDATE_STATUS: + { + const { status, providerId } = action.payload; + return Object.assign({}, state, { + providerStatuses: new Map(state.providerStatuses).set(providerId, status) + }); + } + case (_Actions || _load_Actions()).EXECUTE: + { + const command = action.payload.code; + return Object.assign({}, state, { + history: state.history.concat(command).slice(-1000) + }); + } + case (_Actions || _load_Actions()).SET_CREATE_PASTE_FUNCTION: + { + const { createPasteFunction } = action.payload; + return Object.assign({}, state, { + createPasteFunction + }); + } + case (_Actions || _load_Actions()).SET_WATCH_EDITOR_FUNCTION: + { + const { watchEditor } = action.payload; + return Object.assign({}, state, { + watchEditor + }); + } + case (_Actions || _load_Actions()).SET_FONT_SIZE: + { + const { fontSize } = action.payload; + return Object.assign({}, state, { + fontSize + }); } - return { - ...state, - maxMessageCount, - records: state.records.slice(-maxMessageCount), - }; - } - case Actions.REGISTER_SOURCE: { - const {source} = action.payload; - return { - ...state, - providers: new Map(state.providers).set(source.id, { - ...source, - name: source.name || source.id, - }), - }; - } - case Actions.CLEAR_RECORDS: { - return { - ...state, - records: List(), - }; - } - case Actions.REGISTER_EXECUTOR: { - const {executor} = action.payload; - return { - ...state, - executors: new Map(state.executors).set(executor.id, executor), - }; - } - case Actions.SELECT_EXECUTOR: { - const {executorId} = action.payload; - return { - ...state, - currentExecutorId: executorId, - }; - } - case Actions.REMOVE_SOURCE: { - const {sourceId} = action.payload; - const providers = new Map(state.providers); - const providerStatuses = new Map(state.providerStatuses); - const executors = new Map(state.executors); - providers.delete(sourceId); - providerStatuses.delete(sourceId); - executors.delete(sourceId); - return { - ...state, - providers, - providerStatuses, - executors, - }; - } - case Actions.UPDATE_STATUS: { - const {status, providerId} = action.payload; - return { - ...state, - providerStatuses: new Map(state.providerStatuses).set( - providerId, - status, - ), - }; - } - case Actions.EXECUTE: { - const command = action.payload.code; - return { - ...state, - history: state.history.concat(command).slice(-1000), - }; - } - case Actions.SET_CREATE_PASTE_FUNCTION: { - const {createPasteFunction} = action.payload; - return { - ...state, - createPasteFunction, - }; - } - case Actions.SET_WATCH_EDITOR_FUNCTION: { - const {watchEditor} = action.payload; - return { - ...state, - watchEditor, - }; - } - case Actions.SET_FONT_SIZE: { - const {fontSize} = action.payload; - return { - ...state, - fontSize, - }; - } } return state; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/types.js index b01bd33d..a726efc4 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/types.js @@ -1,270 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {Observable} from 'rxjs'; -import type {Level as TaskLevelType} from 'nuclide-commons/process'; -import type {List} from 'immutable'; -import type {EvaluationResult} from 'nuclide-commons-ui/TextRenderer'; -import type {ExpansionResult} from 'nuclide-commons-ui/LazyNestedValueComponent'; - -export type Level = TaskLevelType | Color; -type Color = - | 'red' - | 'orange' - | 'yellow' - | 'green' - | 'blue' - | 'purple' - | 'violet' - | 'rainbow'; - -type MessageKind = 'message' | 'request' | 'response'; - -// A regular message, emitted by output providers. -export type Message = { - text: string, - level: Level, - data?: EvaluationResult, - tags?: ?Array, - kind?: ?MessageKind, - scopeName?: ?string, -}; - -// A normalized type used internally to represent all possible kinds of messages. Responses and -// Messages are transformed into these. -// Make sure areRecordsIdentical in reducers.js is up to date with these fields -export type Record = { - text: string, - level: Level, - tags?: ?Array, - repeatCount: number, - - kind: MessageKind, - sourceId: string, - scopeName: ?string, - data?: ?EvaluationResult, - timestamp: Date, - - executor?: Executor, -}; - -export type AppState = { - createPasteFunction: ?CreatePasteFunction, - currentExecutorId: ?string, - executors: Map, - maxMessageCount: number, - // We use Immutable for the records list so that adding an item is O(1). However, rendering the - // items after the addition is O(n), so it's important that we schedule and throttle our renders - // or we'll lose the benefit of an O(1) insertion. - records: List, - history: Array, - providers: Map, - providerStatuses: Map, -}; - -// A special type used internally by the Console component to represent each record that is -// displayed with its height. This is stored at the component level since the expansion state of any -// record (which affects its height) is unique to each Console pane (whereas the records themselves -// are shared between all Console panes). The height is needed for partial rendering. -export type DisplayableRecord = { - id: number, - record: Record, - height: number, - expansionStateId: Object, -}; - -export type RecordHeightChangeHandler = ( - recordId: number, - newHeight: number, - callback: () => void, -) => void; - -export type OutputProviderStatus = 'starting' | 'running' | 'stopped'; - -type BasicOutputProvider = { - messages: Observable, - // The source can't be part of the message because we want to be able to populate a filter menu - // before we even have any messages. - id: string, - getProperties?: (objectId: string) => Observable, -}; - -type ControllableOutputProviderProps = { - observeStatus(callback: (status: OutputProviderStatus) => mixed): IDisposable, - start(): void, - stop(): void, -}; - -type ControllableOutputProvider = BasicOutputProvider & - ControllableOutputProviderProps; - -export type OutputProvider = BasicOutputProvider | ControllableOutputProvider; - -type BasicRecordProvider = { - records: Observable, - id: string, - getProperties?: (objectId: string) => Observable, -}; - -type ControllableRecordProvider = BasicRecordProvider & - ControllableOutputProviderProps; - -export type RecordProvider = BasicRecordProvider | ControllableRecordProvider; - -export type Source = { - id: string, - name: string, - status: OutputProviderStatus, - start?: () => void, - stop?: () => void, -}; - -export type OutputService = { - registerOutputProvider(outputProvider: OutputProvider): IDisposable, -}; - -export type SourceInfo = { - id: string, - name: string, - start?: () => void, - stop?: () => void, -}; -export type ConsoleService = (options: SourceInfo) => ConsoleApi; -export type ConsoleApi = { - setStatus(status: OutputProviderStatus): void, - append(message: Message): void, - dispose(): void, - - // TODO: Update these to be (object: any, ...objects: Array): void. - log(object: string, _: void): void, - error(object: string, _: void): void, - warn(object: string, _: void): void, - info(object: string, _: void): void, -}; - -export type ConsolePersistedState = { - deserializer: 'nuclide.Console', - filterText?: string, - enableRegExpFilter?: boolean, - unselectedSourceIds?: Array, -}; - -export type Executor = { - id: string, - name: string, - send(message: string): void, - output: Observable, - scopeName: string, - provideSymbols?: (prefix: string) => Array, - getProperties?: (objectId: string) => Observable, -}; - -export type RegisterExecutorFunction = (executor: Executor) => IDisposable; - -export type WatchEditorFunction = ( - editor: atom$TextEditor, - labels?: Array, -) => IDisposable; - -export type PasteOptions = { - language?: ?string, - title?: ?string, -}; - -export type CreatePasteFunction = ( - message: string, - options: PasteOptions, - source: string, -) => Promise; - -export type Store = { - getState(): AppState, - dispatch(action: Action): void, -}; - -export type Action = - | { - type: 'CLEAR_RECORDS', - } - | { - type: 'REGISTER_EXECUTOR', - payload: { - executor: Executor, - }, - } - | { - type: 'EXECUTE', - payload: { - code: string, - }, - } - | { - type: 'RECORD_RECEIVED', - payload: { - record: Record, - }, - } - | { - type: 'REGISTER_RECORD_PROVIDER', - payload: { - recordProvider: RecordProvider, - }, - } - | { - type: 'REGISTER_SOURCE', - payload: { - source: SourceInfo, - }, - } - | { - type: 'REMOVE_SOURCE', - payload: { - sourceId: string, - }, - } - | { - type: 'SELECT_EXECUTOR', - payload: { - executorId: string, - }, - } - | { - type: 'SET_CREATE_PASTE_FUNCTION', - payload: { - createPasteFunction: ?CreatePasteFunction, - }, - } - | { - type: 'SET_WATCH_EDITOR_FUNCTION', - payload: { - watchEditor: ?WatchEditorFunction, - }, - } - | { - type: 'SET_MAX_MESSAGE_COUNT', - payload: { - maxMessageCount: number, - }, - } - | { - type: 'UPDATE_STATUS', - payload: { - providerId: string, - status: OutputProviderStatus, - }, - } - | { - type: 'SET_FONT_SIZE', - payload: { - fontSize: number, - }, - }; +'use strict'; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/Console.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/Console.js index 78cdb32a..c315fe7d 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/Console.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/Console.js @@ -1,3 +1,208 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Console = exports.WORKSPACE_VIEW_URI = undefined; + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +let serializeRecordObject = (() => { + var _ref2 = (0, _asyncToGenerator.default)(function* (executor, visited, data, text, level) { + const getText = function (record) { + let indent = ''; + for (let i = 0; i < level; i++) { + indent += '\t'; + } + return indent + (record.description != null ? record.description : record.value != null ? record.value : ''); + }; + + if (data.objectId == null) { + // Leaf node. + return text + getText(data); + } + + const id = data.objectId; + if (visited.has(id)) { + // Guard against cycles. + return text; + } + + visited.add(id); + + if (executor.getProperties == null) { + return text; + } + + const childProperties = (yield executor.getProperties(id).toPromise()) || []; + const serializedProps = childProperties.map(function (childProp) { + return serializeRecordObject(executor, visited, childProp.value, '', level + 1); + }); + return getText(data) + '\n' + (yield Promise.all(serializedProps)).join('\n'); + }); + + return function serializeRecordObject(_x, _x2, _x3, _x4, _x5) { + return _ref2.apply(this, arguments); + }; +})(); + +let createPaste = (() => { + var _ref3 = (0, _asyncToGenerator.default)(function* (createPasteImpl, records) { + const linePromises = records.filter(function (displayable) { + return displayable.record.kind === 'message' || displayable.record.kind === 'request' || displayable.record.kind === 'response'; + }).map((() => { + var _ref4 = (0, _asyncToGenerator.default)(function* (displayable) { + const record = displayable.record; + const level = record.level != null ? record.level.toString().toUpperCase() : 'LOG'; + const timestamp = record.timestamp.toLocaleString(); + let text = record.text || record.data && record.data.value || ERROR_TRANSCRIBING_MESSAGE; + + if (record.kind === 'response' && record.data != null && record.data.objectId != null && record.data.objectId !== '') { + const executor = record.executor; + if (executor != null) { + // If the record has a data object, and the object has an ID, + // recursively expand the nodes of the object and serialize it + // for the paste. + text = yield serializeRecordObject(executor, new Set(), record.data, '', 0); + } + } + + return `[${level}][${record.sourceId}][${timestamp}]\t ${text}`; + }); + + return function (_x8) { + return _ref4.apply(this, arguments); + }; + })()); + + const lines = (yield Promise.all(linePromises)).join('\n'); + + if (lines === '') { + // Can't create an empty paste! + atom.notifications.addWarning('There is nothing in your console to Paste! Check your console filters and try again.'); + return; + } + + atom.notifications.addInfo('Creating Paste...'); + + try { + const uri = yield createPasteImpl(lines, { + title: 'Nuclide Console Paste' + }, 'console paste'); + atom.notifications.addSuccess(`Created Paste at ${uri}`); + } catch (error) { + if (error.stdout == null) { + atom.notifications.addError(`Failed to create paste: ${String(error.message || error)}`); + return; + } + const errorMessages = error.stdout.trim().split('\n').map(JSON.parse).map(function (e) { + return e.message; + }); + atom.notifications.addError('Failed to create paste', { + detail: errorMessages.join('\n'), + dismissable: true + }); + } + }); + + return function createPaste(_x6, _x7) { + return _ref3.apply(this, arguments); + }; +})(); + +var _observePaneItemVisibility; + +function _load_observePaneItemVisibility() { + return _observePaneItemVisibility = _interopRequireDefault(require('nuclide-commons-atom/observePaneItemVisibility')); +} + +var _Model; + +function _load_Model() { + return _Model = _interopRequireDefault(require('nuclide-commons/Model')); +} + +var _shallowequal; + +function _load_shallowequal() { + return _shallowequal = _interopRequireDefault(require('shallowequal')); +} + +var _bindObservableAsProps; + +function _load_bindObservableAsProps() { + return _bindObservableAsProps = require('nuclide-commons-ui/bindObservableAsProps'); +} + +var _renderReactRoot; + +function _load_renderReactRoot() { + return _renderReactRoot = require('nuclide-commons-ui/renderReactRoot'); +} + +var _memoizeUntilChanged; + +function _load_memoizeUntilChanged() { + return _memoizeUntilChanged = _interopRequireDefault(require('nuclide-commons/memoizeUntilChanged')); +} + +var _observable; + +function _load_observable() { + return _observable = require('nuclide-commons/observable'); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _RegExpFilter; + +function _load_RegExpFilter() { + return _RegExpFilter = require('nuclide-commons-ui/RegExpFilter'); +} + +var _getCurrentExecutorId; + +function _load_getCurrentExecutorId() { + return _getCurrentExecutorId = _interopRequireDefault(require('../getCurrentExecutorId')); +} + +var _Actions; + +function _load_Actions() { + return _Actions = _interopRequireWildcard(require('../redux/Actions')); +} + +var _ConsoleView; + +function _load_ConsoleView() { + return _ConsoleView = _interopRequireDefault(require('./ConsoleView')); +} + +var _immutable; + +function _load_immutable() { + return _immutable = require('immutable'); +} + +var _react = _interopRequireWildcard(require('react')); + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// Other Nuclide packages (which cannot import this) depend on this URI. If this +// needs to be changed, grep for CONSOLE_VIEW_URI and ensure that the URIs match. + + +// +// State unique to this particular Console instance +// /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,71 +211,15 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ /* eslint-env browser */ -import type { - ConsolePersistedState, - DisplayableRecord, - OutputProviderStatus, - Record, - Source, - Store, - SourceInfo, -} from '../types'; -import type {CreatePasteFunction} from '../types'; -import type {RegExpFilterChange} from 'nuclide-commons-ui/RegExpFilter'; -import type {Executor} from '../types'; - -import observePaneItemVisibility from 'nuclide-commons-atom/observePaneItemVisibility'; -import Model from 'nuclide-commons/Model'; -import shallowEqual from 'shallowequal'; -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import {renderReactRoot} from 'nuclide-commons-ui/renderReactRoot'; -import memoizeUntilChanged from 'nuclide-commons/memoizeUntilChanged'; -import {toggle} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {nextAnimationFrame} from 'nuclide-commons/observable'; -import {getFilterPattern} from 'nuclide-commons-ui/RegExpFilter'; -import getCurrentExecutorId from '../getCurrentExecutorId'; -import * as Actions from '../redux/Actions'; -import ConsoleView from './ConsoleView'; -import {List} from 'immutable'; -import * as React from 'react'; -import {Observable, ReplaySubject} from 'rxjs'; - -type Options = { - store: Store, - initialFilterText?: string, - initialEnableRegExpFilter?: boolean, - initialUnselectedSourceIds?: Array, -}; - -// -// State unique to this particular Console instance -// -type State = { - displayableRecords: Array, - filterText: string, - enableRegExpFilter: boolean, - unselectedSourceIds: Array, -}; - -type BoundActionCreators = { - execute: (code: string) => void, - selectExecutor: (executorId: string) => void, - clearRecords: () => void, -}; +const WORKSPACE_VIEW_URI = exports.WORKSPACE_VIEW_URI = 'atom://nuclide/console'; -// Other Nuclide packages (which cannot import this) depend on this URI. If this -// needs to be changed, grep for CONSOLE_VIEW_URI and ensure that the URIs match. -export const WORKSPACE_VIEW_URI = 'atom://nuclide/console'; - -const ERROR_TRANSCRIBING_MESSAGE = - "// Nuclide couldn't find the right text to display"; +const ERROR_TRANSCRIBING_MESSAGE = "// Nuclide couldn't find the right text to display"; const INITIAL_RECORD_HEIGHT = 21; /** @@ -78,59 +227,95 @@ const INITIAL_RECORD_HEIGHT = 21; * (via `getElement()`). That view is bound to both global state (from the store) and view-specific * state (from this instance's `_model`). */ -export class Console { - _actionCreators: BoundActionCreators; +class Console { - // Associates Records with their display state (height, expansionStateId). - _displayableRecords: WeakMap; + constructor(options) { + var _this = this; + + this._getSourcesMemoized = (0, (_memoizeUntilChanged || _load_memoizeUntilChanged()).default)(getSources, opts => opts, (a, b) => (0, (_shallowequal || _load_shallowequal()).default)(a, b)); + + this._resetAllFilters = () => { + this._selectSources(this._getSources().map(s => s.id)); + this._model.setState({ filterText: '' }); + }; + + this._createPaste = (0, _asyncToGenerator.default)(function* () { + const displayableRecords = _this._getDisplayableRecords(); + const createPasteImpl = _this._store.getState().createPasteFunction; + if (createPasteImpl == null) { + return; + } + return createPaste(createPasteImpl, displayableRecords); + }); + + this._selectSources = selectedSourceIds => { + const sourceIds = this._getSources().map(source => source.id); + const unselectedSourceIds = sourceIds.filter(sourceId => selectedSourceIds.indexOf(sourceId) === -1); + this._model.setState({ unselectedSourceIds }); + }; + + this._updateFilter = change => { + const { text, isRegExp } = change; + this._model.setState({ + filterText: text, + enableRegExpFilter: isRegExp + }); + }; + + this._handleDisplayableRecordHeightChange = (recordId, newHeight, callback) => { + const { records } = this._store.getState(); + const nextDisplayableRecords = Array(records.size); + records.forEach((record, i) => { + let displayableRecord = this._toDisplayableRecord(record); + if (displayableRecord.id === recordId) { + // Update the record with the new height. + displayableRecord = Object.assign({}, displayableRecord, { + height: newHeight + }); + this._displayableRecords.set(record, displayableRecord); + } + nextDisplayableRecords[i] = displayableRecord; + }); - _nextRecordId: number; - _titleChanges: Observable; - _model: Model; - _store: Store; - _element: ?HTMLElement; - _destroyed: ReplaySubject; + this._model.setState({ displayableRecords: nextDisplayableRecords }); + requestAnimationFrame(callback); + }; - constructor(options: Options) { const { store, initialFilterText, initialEnableRegExpFilter, - initialUnselectedSourceIds, + initialUnselectedSourceIds } = options; - this._model = new Model({ + this._model = new (_Model || _load_Model()).default({ displayableRecords: [], filterText: initialFilterText == null ? '' : initialFilterText, enableRegExpFilter: Boolean(initialEnableRegExpFilter), - unselectedSourceIds: - initialUnselectedSourceIds == null ? [] : initialUnselectedSourceIds, + unselectedSourceIds: initialUnselectedSourceIds == null ? [] : initialUnselectedSourceIds }); this._store = store; this._nextRecordId = 0; this._displayableRecords = new WeakMap(); - this._destroyed = new ReplaySubject(1); - - this._titleChanges = Observable.combineLatest( - this._model.toObservable(), - // $FlowIssue: Flow doesn't know about Symbol.observable - Observable.from(store), - ) - .takeUntil(this._destroyed) - .map(() => this.getTitle()) - .distinctUntilChanged() - .share(); + this._destroyed = new _rxjsBundlesRxMinJs.ReplaySubject(1); + + this._titleChanges = _rxjsBundlesRxMinJs.Observable.combineLatest(this._model.toObservable(), + // $FlowIssue: Flow doesn't know about Symbol.observable + _rxjsBundlesRxMinJs.Observable.from(store)).takeUntil(this._destroyed).map(() => this.getTitle()).distinctUntilChanged().share(); } - getIconName(): string { + // Associates Records with their display state (height, expansionStateId). + + + getIconName() { return 'terminal'; } // Get the pane item's title. If there's only one source selected, we'll use that to make a more // descriptive title. - getTitle(): string { + getTitle() { const enabledProviderCount = this._store.getState().providers.size; - const {unselectedSourceIds} = this._model.state; + const { unselectedSourceIds } = this._model.state; // Calling `_getSources()` is (currently) expensive because it needs to search all the records // for sources that have been disabled but still have records. We try to avoid calling it if we @@ -142,9 +327,7 @@ export class Console { // If there's only one source selected, use its name in the tab title. const sources = this._getSources(); if (sources.length - unselectedSourceIds.length === 1) { - const selectedSource = sources.find( - source => unselectedSourceIds.indexOf(source.id) === -1, - ); + const selectedSource = sources.find(source => unselectedSourceIds.indexOf(source.id) === -1); if (selectedSource) { return `Console: ${selectedSource.name}`; } @@ -153,21 +336,21 @@ export class Console { return 'Console'; } - getDefaultLocation(): string { + getDefaultLocation() { return 'bottom'; } - getURI(): string { + getURI() { return WORKSPACE_VIEW_URI; } - onDidChangeTitle(callback: (title: string) => mixed): IDisposable { - return new UniversalDisposable(this._titleChanges.subscribe(callback)); + onDidChangeTitle(callback) { + return new (_UniversalDisposable || _load_UniversalDisposable()).default(this._titleChanges.subscribe(callback)); } - _getSources(): Array { - const {providers, providerStatuses, records} = this._store.getState(); - return this._getSourcesMemoized({providers, providerStatuses, records}); + _getSources() { + const { providers, providerStatuses, records } = this._store.getState(); + return this._getSourcesMemoized({ providers, providerStatuses, records }); } // Memoize `getSources()`. Unfortunately, since we look for unrepresented sources in the record @@ -175,203 +358,118 @@ export class Console { // TODO: Consider removing records when their source is removed. This will likely require adding // the ability to enable and disable sources so, for example, when the debugger is no longer // active, it still remains in the source list. - _getSourcesMemoized = memoizeUntilChanged( - getSources, - opts => opts, - (a, b) => shallowEqual(a, b), - ); - destroy(): void { + + destroy() { this._destroyed.next(); } - copy(): Console { + copy() { return new Console({ store: this._store, initialFilterText: this._model.state.filterText, initialEnableRegExpFilter: this._model.state.enableRegExpFilter, - initialUnselectedSourceIds: this._model.state.unselectedSourceIds, + initialUnselectedSourceIds: this._model.state.unselectedSourceIds }); } - _getBoundActionCreators(): BoundActionCreators { + _getBoundActionCreators() { if (this._actionCreators == null) { this._actionCreators = { execute: code => { - this._store.dispatch(Actions.execute(code)); + this._store.dispatch((_Actions || _load_Actions()).execute(code)); }, selectExecutor: executorId => { - this._store.dispatch(Actions.selectExecutor(executorId)); + this._store.dispatch((_Actions || _load_Actions()).selectExecutor(executorId)); }, clearRecords: () => { - this._store.dispatch(Actions.clearRecords()); - }, + this._store.dispatch((_Actions || _load_Actions()).clearRecords()); + } }; } return this._actionCreators; } - _resetAllFilters = (): void => { - this._selectSources(this._getSources().map(s => s.id)); - this._model.setState({filterText: ''}); - }; - - _createPaste = async (): Promise => { - const displayableRecords = this._getDisplayableRecords(); - const createPasteImpl = this._store.getState().createPasteFunction; - if (createPasteImpl == null) { - return; - } - return createPaste(createPasteImpl, displayableRecords); - }; - - _getFilterInfo(): { - invalid: boolean, - selectedSourceIds: Array, - filteredRecords: Array, - } { - const {pattern, invalid} = getFilterPattern( - this._model.state.filterText, - this._model.state.enableRegExpFilter, - ); + _getFilterInfo() { + const { pattern, invalid } = (0, (_RegExpFilter || _load_RegExpFilter()).getFilterPattern)(this._model.state.filterText, this._model.state.enableRegExpFilter); const sources = this._getSources(); - const selectedSourceIds = sources - .map(source => source.id) - .filter( - sourceId => - this._model.state.unselectedSourceIds.indexOf(sourceId) === -1, - ); - - const filteredRecords = filterRecords( - this._getDisplayableRecords(), - selectedSourceIds, - pattern, - sources.length !== selectedSourceIds.length, - ); + const selectedSourceIds = sources.map(source => source.id).filter(sourceId => this._model.state.unselectedSourceIds.indexOf(sourceId) === -1); + + const filteredRecords = filterRecords(this._getDisplayableRecords(), selectedSourceIds, pattern, sources.length !== selectedSourceIds.length); return { invalid, selectedSourceIds, - filteredRecords, + filteredRecords }; } - getElement(): HTMLElement { + getElement() { if (this._element != null) { return this._element; } const actionCreators = this._getBoundActionCreators(); - const props = Observable.combineLatest( - this._model.toObservable(), - // $FlowIssue: Flow doesn't know about Symbol.observable - Observable.from(this._store), - ) - // Don't re-render when the console isn't visible. - .let(toggle(observePaneItemVisibility(this))) - .audit(() => nextAnimationFrame) - .map(([localState, globalState]) => { - const { - invalid, - selectedSourceIds, - filteredRecords, - } = this._getFilterInfo(); - - const currentExecutorId = getCurrentExecutorId(globalState); - const currentExecutor = - currentExecutorId != null - ? globalState.executors.get(currentExecutorId) - : null; - - return { - invalidFilterInput: invalid, - execute: actionCreators.execute, - selectExecutor: actionCreators.selectExecutor, - clearRecords: actionCreators.clearRecords, - createPaste: - globalState.createPasteFunction == null ? null : this._createPaste, - watchEditor: globalState.watchEditor, - currentExecutor, - unselectedSourceIds: localState.unselectedSourceIds, - filterText: localState.filterText, - enableRegExpFilter: localState.enableRegExpFilter, - displayableRecords: filteredRecords, - filteredRecordCount: - globalState.records.size - filteredRecords.length, - history: globalState.history, - sources: this._getSources(), - selectedSourceIds, - selectSources: this._selectSources, - executors: globalState.executors, - getProvider: id => globalState.providers.get(id), - updateFilter: this._updateFilter, - onDisplayableRecordHeightChange: this - ._handleDisplayableRecordHeightChange, - resetAllFilters: this._resetAllFilters, - fontSize: globalState.fontSize, - }; - }); + const props = _rxjsBundlesRxMinJs.Observable.combineLatest(this._model.toObservable(), + // $FlowIssue: Flow doesn't know about Symbol.observable + _rxjsBundlesRxMinJs.Observable.from(this._store)) + // Don't re-render when the console isn't visible. + .let((0, (_observable || _load_observable()).toggle)((0, (_observePaneItemVisibility || _load_observePaneItemVisibility()).default)(this))).audit(() => (_observable || _load_observable()).nextAnimationFrame).map(([localState, globalState]) => { + const { + invalid, + selectedSourceIds, + filteredRecords + } = this._getFilterInfo(); + + const currentExecutorId = (0, (_getCurrentExecutorId || _load_getCurrentExecutorId()).default)(globalState); + const currentExecutor = currentExecutorId != null ? globalState.executors.get(currentExecutorId) : null; + + return { + invalidFilterInput: invalid, + execute: actionCreators.execute, + selectExecutor: actionCreators.selectExecutor, + clearRecords: actionCreators.clearRecords, + createPaste: globalState.createPasteFunction == null ? null : this._createPaste, + watchEditor: globalState.watchEditor, + currentExecutor, + unselectedSourceIds: localState.unselectedSourceIds, + filterText: localState.filterText, + enableRegExpFilter: localState.enableRegExpFilter, + displayableRecords: filteredRecords, + filteredRecordCount: globalState.records.size - filteredRecords.length, + history: globalState.history, + sources: this._getSources(), + selectedSourceIds, + selectSources: this._selectSources, + executors: globalState.executors, + getProvider: id => globalState.providers.get(id), + updateFilter: this._updateFilter, + onDisplayableRecordHeightChange: this._handleDisplayableRecordHeightChange, + resetAllFilters: this._resetAllFilters, + fontSize: globalState.fontSize + }; + }); - const StatefulConsoleView = bindObservableAsProps(props, ConsoleView); - return (this._element = renderReactRoot()); + const StatefulConsoleView = (0, (_bindObservableAsProps || _load_bindObservableAsProps()).bindObservableAsProps)(props, (_ConsoleView || _load_ConsoleView()).default); + return this._element = (0, (_renderReactRoot || _load_renderReactRoot()).renderReactRoot)(_react.createElement(StatefulConsoleView, null)); } - serialize(): ConsolePersistedState { + serialize() { const { filterText, enableRegExpFilter, - unselectedSourceIds, + unselectedSourceIds } = this._model.state; return { deserializer: 'nuclide.Console', filterText, enableRegExpFilter, - unselectedSourceIds, + unselectedSourceIds }; } - _selectSources = (selectedSourceIds: Array): void => { - const sourceIds = this._getSources().map(source => source.id); - const unselectedSourceIds = sourceIds.filter( - sourceId => selectedSourceIds.indexOf(sourceId) === -1, - ); - this._model.setState({unselectedSourceIds}); - }; - - _updateFilter = (change: RegExpFilterChange): void => { - const {text, isRegExp} = change; - this._model.setState({ - filterText: text, - enableRegExpFilter: isRegExp, - }); - }; - - _handleDisplayableRecordHeightChange = ( - recordId: number, - newHeight: number, - callback: () => void, - ): void => { - const {records} = this._store.getState(); - const nextDisplayableRecords = Array(records.size); - records.forEach((record, i) => { - let displayableRecord = this._toDisplayableRecord(record); - if (displayableRecord.id === recordId) { - // Update the record with the new height. - displayableRecord = { - ...displayableRecord, - height: newHeight, - }; - this._displayableRecords.set(record, displayableRecord); - } - nextDisplayableRecords[i] = displayableRecord; - }); - - this._model.setState({displayableRecords: nextDisplayableRecords}); - requestAnimationFrame(callback); - }; - - _getDisplayableRecords(): Array { - const {records} = this._store.getState(); + _getDisplayableRecords() { + const { records } = this._store.getState(); const displayableRecords = Array(records.size); records.forEach((record, i) => { displayableRecords[i] = this._toDisplayableRecord(record); @@ -384,7 +482,7 @@ export class Console { * per-Console instance because the same record can have different heights in different * containers. */ - _toDisplayableRecord(record: Record): DisplayableRecord { + _toDisplayableRecord(record) { const displayableRecord = this._displayableRecords.get(record); if (displayableRecord != null) { return displayableRecord; @@ -393,34 +491,28 @@ export class Console { id: this._nextRecordId++, record, height: INITIAL_RECORD_HEIGHT, - expansionStateId: {}, + expansionStateId: {} }; this._displayableRecords.set(record, newDisplayableRecord); return newDisplayableRecord; } } -function getSources(options: { - records: List, - providers: Map, - providerStatuses: Map, -}): Array { - const {providers, providerStatuses, records} = options; +exports.Console = Console; +function getSources(options) { + const { providers, providerStatuses, records } = options; // Convert the providers to a map of sources. - const mapOfSources = new Map( - Array.from(providers.entries()).map(([k, provider]) => { - const source = { - id: provider.id, - name: provider.id, - status: providerStatuses.get(provider.id) || 'stopped', - start: - typeof provider.start === 'function' ? provider.start : undefined, - stop: typeof provider.stop === 'function' ? provider.stop : undefined, - }; - return [k, source]; - }), - ); + const mapOfSources = new Map(Array.from(providers.entries()).map(([k, provider]) => { + const source = { + id: provider.id, + name: provider.id, + status: providerStatuses.get(provider.id) || 'stopped', + start: typeof provider.start === 'function' ? provider.start : undefined, + stop: typeof provider.stop === 'function' ? provider.stop : undefined + }; + return [k, source]; + })); // Some providers may have been unregistered, but still have records. Add sources for them too. // TODO: Iterating over all the records to get this every time we get a new record is inefficient. @@ -431,7 +523,7 @@ function getSources(options: { name: record.sourceId, status: 'stopped', start: undefined, - stop: undefined, + stop: undefined }); } }); @@ -439,165 +531,18 @@ function getSources(options: { return Array.from(mapOfSources.values()); } -function filterRecords( - displayableRecords: Array, - selectedSourceIds: Array, - filterPattern: ?RegExp, - filterSources: boolean, -): Array { +function filterRecords(displayableRecords, selectedSourceIds, filterPattern, filterSources) { if (!filterSources && filterPattern == null) { return displayableRecords; } - return displayableRecords.filter(({record}) => { + return displayableRecords.filter(({ record }) => { // Only filter regular messages if (record.kind !== 'message') { return true; } const sourceMatches = selectedSourceIds.indexOf(record.sourceId) !== -1; - return ( - sourceMatches && - (filterPattern == null || filterPattern.test(record.text)) - ); + return sourceMatches && (filterPattern == null || filterPattern.test(record.text)); }); -} - -async function serializeRecordObject( - executor: Executor, - visited: Set, - data: { - objectId?: string, - description?: string, - value?: string, - }, - text: string, - level: number, -): Promise { - const getText = record => { - let indent = ''; - for (let i = 0; i < level; i++) { - indent += '\t'; - } - return ( - indent + - (record.description != null - ? record.description - : record.value != null ? record.value : '') - ); - }; - - if (data.objectId == null) { - // Leaf node. - return text + getText(data); - } - - const id = data.objectId; - if (visited.has(id)) { - // Guard against cycles. - return text; - } - - visited.add(id); - - if (executor.getProperties == null) { - return text; - } - - const childProperties = (await executor.getProperties(id).toPromise()) || []; - const serializedProps = childProperties.map(childProp => { - return serializeRecordObject( - executor, - visited, - childProp.value, - '', - level + 1, - ); - }); - return getText(data) + '\n' + (await Promise.all(serializedProps)).join('\n'); -} - -async function createPaste( - createPasteImpl: CreatePasteFunction, - records: Array, -): Promise { - const linePromises = records - .filter( - displayable => - displayable.record.kind === 'message' || - displayable.record.kind === 'request' || - displayable.record.kind === 'response', - ) - .map(async displayable => { - const record = displayable.record; - const level = - record.level != null ? record.level.toString().toUpperCase() : 'LOG'; - const timestamp = record.timestamp.toLocaleString(); - let text = - record.text || - (record.data && record.data.value) || - ERROR_TRANSCRIBING_MESSAGE; - - if ( - record.kind === 'response' && - record.data != null && - record.data.objectId != null && - record.data.objectId !== '' - ) { - const executor = record.executor; - if (executor != null) { - // If the record has a data object, and the object has an ID, - // recursively expand the nodes of the object and serialize it - // for the paste. - text = await serializeRecordObject( - executor, - new Set(), - record.data, - '', - 0, - ); - } - } - - return `[${level}][${record.sourceId}][${timestamp}]\t ${text}`; - }); - - const lines = (await Promise.all(linePromises)).join('\n'); - - if (lines === '') { - // Can't create an empty paste! - atom.notifications.addWarning( - 'There is nothing in your console to Paste! Check your console filters and try again.', - ); - return; - } - - atom.notifications.addInfo('Creating Paste...'); - - try { - const uri = await createPasteImpl( - lines, - { - title: 'Nuclide Console Paste', - }, - 'console paste', - ); - atom.notifications.addSuccess(`Created Paste at ${uri}`); - } catch (error) { - if (error.stdout == null) { - atom.notifications.addError( - `Failed to create paste: ${String(error.message || error)}`, - ); - return; - } - const errorMessages = error.stdout - .trim() - .split('\n') - .map(JSON.parse) - .map(e => e.message); - atom.notifications.addError('Failed to create paste', { - detail: errorMessages.join('\n'), - dismissable: true, - }); - } -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleHeader.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleHeader.js index 5ac9e9f6..8a023caa 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleHeader.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleHeader.js @@ -1,174 +1,212 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {Source} from '../types'; -import type {RegExpFilterChange} from 'nuclide-commons-ui/RegExpFilter'; - -import {LoadingSpinner} from 'nuclide-commons-ui/LoadingSpinner'; -import * as React from 'react'; -import {ModalMultiSelect} from 'nuclide-commons-ui/ModalMultiSelect'; -import RegExpFilter from 'nuclide-commons-ui/RegExpFilter'; -import {Toolbar} from 'nuclide-commons-ui/Toolbar'; -import {ToolbarLeft} from 'nuclide-commons-ui/ToolbarLeft'; -import {ToolbarRight} from 'nuclide-commons-ui/ToolbarRight'; -import addTooltip from 'nuclide-commons-ui/addTooltip'; - -import {Button, ButtonSizes} from 'nuclide-commons-ui/Button'; -import invariant from 'assert'; - -type Props = { - clear: () => void, - createPaste: ?() => Promise, - invalidFilterInput: boolean, - enableRegExpFilter: boolean, - onFilterChange: (change: RegExpFilterChange) => void, - selectedSourceIds: Array, - sources: Array, - onSelectedSourcesChange: (sourceIds: Array) => void, - filterText: string, -}; - -export default class ConsoleHeader extends React.Component { - _handleClearButtonClick = (event: SyntheticMouseEvent<>): void => { - this.props.clear(); - }; - - _handleCreatePasteButtonClick = (event: SyntheticMouseEvent<>): void => { - if (this.props.createPaste != null) { - this.props.createPaste(); - } - }; +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _LoadingSpinner; + +function _load_LoadingSpinner() { + return _LoadingSpinner = require('nuclide-commons-ui/LoadingSpinner'); +} + +var _react = _interopRequireWildcard(require('react')); + +var _ModalMultiSelect; + +function _load_ModalMultiSelect() { + return _ModalMultiSelect = require('nuclide-commons-ui/ModalMultiSelect'); +} + +var _RegExpFilter; + +function _load_RegExpFilter() { + return _RegExpFilter = _interopRequireDefault(require('nuclide-commons-ui/RegExpFilter')); +} + +var _Toolbar; + +function _load_Toolbar() { + return _Toolbar = require('nuclide-commons-ui/Toolbar'); +} + +var _ToolbarLeft; + +function _load_ToolbarLeft() { + return _ToolbarLeft = require('nuclide-commons-ui/ToolbarLeft'); +} + +var _ToolbarRight; + +function _load_ToolbarRight() { + return _ToolbarRight = require('nuclide-commons-ui/ToolbarRight'); +} + +var _addTooltip; + +function _load_addTooltip() { + return _addTooltip = _interopRequireDefault(require('nuclide-commons-ui/addTooltip')); +} - _handleFilterChange = (value: RegExpFilterChange): void => { - this.props.onFilterChange(value); - }; +var _Button; - _renderProcessControlButton(source: Source): ?React.Element { +function _load_Button() { + return _Button = require('nuclide-commons-ui/Button'); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +class ConsoleHeader extends _react.Component { + constructor(...args) { + var _temp; + + return _temp = super(...args), this._handleClearButtonClick = event => { + this.props.clear(); + }, this._handleCreatePasteButtonClick = event => { + if (this.props.createPaste != null) { + this.props.createPaste(); + } + }, this._handleFilterChange = value => { + this.props.onFilterChange(value); + }, this._renderOption = optionProps => { + const { option } = optionProps; + const source = this.props.sources.find(s => s.id === option.value); + + if (!(source != null)) { + throw new Error('Invariant violation: "source != null"'); + } + + const startingSpinner = source.status !== 'starting' ? null : _react.createElement((_LoadingSpinner || _load_LoadingSpinner()).LoadingSpinner, { + className: 'inline-block console-process-starting-spinner', + size: 'EXTRA_SMALL' + }); + return _react.createElement( + 'span', + null, + option.label, + startingSpinner, + this._renderProcessControlButton(source) + ); + }, _temp; + } + + _renderProcessControlButton(source) { let action; let label; let icon; switch (source.status) { case 'starting': - case 'running': { - action = source.stop; - label = 'Stop Process'; - icon = 'primitive-square'; - break; - } - case 'stopped': { - action = source.start; - label = 'Start Process'; - icon = 'triangle-right'; - break; - } + case 'running': + { + action = source.stop; + label = 'Stop Process'; + icon = 'primitive-square'; + break; + } + case 'stopped': + { + action = source.start; + label = 'Start Process'; + icon = 'triangle-right'; + break; + } } if (action == null) { return; } const clickHandler = event => { event.stopPropagation(); - invariant(action != null); + + if (!(action != null)) { + throw new Error('Invariant violation: "action != null"'); + } + action(); }; - return ( - + return _react.createElement( + (_Button || _load_Button()).Button, + { + className: 'pull-right console-process-control-button', + icon: icon, + onClick: clickHandler }, + label ); } - _renderOption = (optionProps: { - option: {label: string, value: string}, - }): React.Element => { - const {option} = optionProps; - const source = this.props.sources.find(s => s.id === option.value); - invariant(source != null); - const startingSpinner = - source.status !== 'starting' ? null : ( - - ); - return ( - - {option.label} - {startingSpinner} - {this._renderProcessControlButton(source)} - - ); - }; - - render(): React.Node { - const options = this.props.sources - .slice() - .sort((a, b) => sortAlpha(a.name, b.name)) - .map(source => ({ - label: source.id, - value: source.name, - })); + render() { + const options = this.props.sources.slice().sort((a, b) => sortAlpha(a.name, b.name)).map(source => ({ + label: source.id, + value: source.name + })); const MultiSelectOption = this._renderOption; - const pasteButton = - this.props.createPaste == null ? null : ( - - ); + const pasteButton = this.props.createPaste == null ? null : _react.createElement( + (_Button || _load_Button()).Button, + { + className: 'inline-block', + size: (_Button || _load_Button()).ButtonSizes.SMALL, + onClick: this._handleCreatePasteButtonClick, + ref: (0, (_addTooltip || _load_addTooltip()).default)({ + title: 'Creates a Paste from the current contents of the console' + }) }, + 'Create Paste' + ); - return ( - - - - - - - {pasteButton} - - - + return _react.createElement( + (_Toolbar || _load_Toolbar()).Toolbar, + { location: 'top' }, + _react.createElement( + (_ToolbarLeft || _load_ToolbarLeft()).ToolbarLeft, + null, + _react.createElement((_ModalMultiSelect || _load_ModalMultiSelect()).ModalMultiSelect, { + labelComponent: MultiSelectLabel, + optionComponent: MultiSelectOption, + size: (_Button || _load_Button()).ButtonSizes.SMALL, + options: options, + value: this.props.selectedSourceIds, + onChange: this.props.onSelectedSourcesChange, + className: 'inline-block' + }), + _react.createElement((_RegExpFilter || _load_RegExpFilter()).default, { + value: { + text: this.props.filterText, + isRegExp: this.props.enableRegExpFilter, + invalid: this.props.invalidFilterInput + }, + onChange: this._handleFilterChange + }) + ), + _react.createElement( + (_ToolbarRight || _load_ToolbarRight()).ToolbarRight, + null, + pasteButton, + _react.createElement( + (_Button || _load_Button()).Button, + { + size: (_Button || _load_Button()).ButtonSizes.SMALL, + onClick: this._handleClearButtonClick }, + 'Clear' + ) + ) ); } } -function sortAlpha(a: string, b: string): number { +exports.default = ConsoleHeader; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +function sortAlpha(a, b) { const aLower = a.toLowerCase(); const bLower = b.toLowerCase(); if (aLower < bLower) { @@ -179,15 +217,13 @@ function sortAlpha(a: string, b: string): number { return 0; } -type LabelProps = { - selectedOptions: Array<{value: string, label: string}>, -}; - -function MultiSelectLabel(props: LabelProps): React.Element { - const {selectedOptions} = props; - const label = - selectedOptions.length === 1 - ? selectedOptions[0].label - : `${selectedOptions.length} Sources`; - return Showing: {label}; -} +function MultiSelectLabel(props) { + const { selectedOptions } = props; + const label = selectedOptions.length === 1 ? selectedOptions[0].label : `${selectedOptions.length} Sources`; + return _react.createElement( + 'span', + null, + 'Showing: ', + label + ); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleView.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleView.js index ed6a2edc..babb452a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/ConsoleView.js @@ -1,99 +1,172 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type { - DisplayableRecord, - Executor, - OutputProvider, - RecordHeightChangeHandler, - Source, - WatchEditorFunction, -} from '../types'; -import type {RegExpFilterChange} from 'nuclide-commons-ui/RegExpFilter'; - -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import debounce from 'nuclide-commons/debounce'; -import * as React from 'react'; -import {Observable} from 'rxjs'; -import FilteredMessagesReminder from './FilteredMessagesReminder'; -import OutputTable from './OutputTable'; -import ConsoleHeader from './ConsoleHeader'; -import InputArea from './InputArea'; -import PromptButton from './PromptButton'; -import NewMessagesNotification from './NewMessagesNotification'; -import invariant from 'assert'; -import shallowEqual from 'shallowequal'; -import recordsChanged from '../recordsChanged'; -import StyleSheet from 'nuclide-commons-ui/StyleSheet'; - -type Props = { - displayableRecords: Array, - history: Array, - clearRecords: () => void, - createPaste: ?() => Promise, - watchEditor: ?WatchEditorFunction, - execute: (code: string) => void, - currentExecutor: ?Executor, - executors: Map, - invalidFilterInput: boolean, - enableRegExpFilter: boolean, - selectedSourceIds: Array, - selectExecutor: (executorId: string) => void, - selectSources: (sourceIds: Array) => void, - sources: Array, - updateFilter: (change: RegExpFilterChange) => void, - getProvider: (id: string) => ?OutputProvider, - onDisplayableRecordHeightChange: RecordHeightChangeHandler, - filteredRecordCount: number, - filterText: string, - resetAllFilters: () => void, - fontSize: number, -}; - -type State = { - unseenMessages: boolean, -}; +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _debounce; + +function _load_debounce() { + return _debounce = _interopRequireDefault(require('nuclide-commons/debounce')); +} + +var _react = _interopRequireWildcard(require('react')); + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _FilteredMessagesReminder; + +function _load_FilteredMessagesReminder() { + return _FilteredMessagesReminder = _interopRequireDefault(require('./FilteredMessagesReminder')); +} + +var _OutputTable; + +function _load_OutputTable() { + return _OutputTable = _interopRequireDefault(require('./OutputTable')); +} + +var _ConsoleHeader; + +function _load_ConsoleHeader() { + return _ConsoleHeader = _interopRequireDefault(require('./ConsoleHeader')); +} + +var _InputArea; + +function _load_InputArea() { + return _InputArea = _interopRequireDefault(require('./InputArea')); +} + +var _PromptButton; + +function _load_PromptButton() { + return _PromptButton = _interopRequireDefault(require('./PromptButton')); +} + +var _NewMessagesNotification; + +function _load_NewMessagesNotification() { + return _NewMessagesNotification = _interopRequireDefault(require('./NewMessagesNotification')); +} + +var _shallowequal; + +function _load_shallowequal() { + return _shallowequal = _interopRequireDefault(require('shallowequal')); +} + +var _recordsChanged; + +function _load_recordsChanged() { + return _recordsChanged = _interopRequireDefault(require('../recordsChanged')); +} + +var _StyleSheet; + +function _load_StyleSheet() { + return _StyleSheet = _interopRequireDefault(require('nuclide-commons-ui/StyleSheet')); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // Maximum time (ms) for the console to try scrolling to the bottom. -const MAXIMUM_SCROLLING_TIME = 3000; +const MAXIMUM_SCROLLING_TIME = 3000; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ let count = 0; -export default class ConsoleView extends React.Component { - _disposables: UniversalDisposable; - _isScrolledNearBottom: boolean; - _id: number; +class ConsoleView extends _react.Component { - // Used when _scrollToBottom is called. The console optimizes message loading - // so scrolling to the bottom once doesn't always scroll to the bottom since - // more messages can be loaded after. - _isScrollingToBottom: boolean; - _scrollingThrottle: rxjs$Subscription; + constructor(props) { + super(props); - _outputTable: ?OutputTable; + this._getExecutor = id => { + return this.props.executors.get(id); + }; + + this._getProvider = id => { + return this.props.getProvider(id); + }; + + this._executePrompt = code => { + this.props.execute(code); + // Makes the console to scroll to the bottom. + this._isScrolledNearBottom = true; + }; + + this._handleScroll = (offsetHeight, scrollHeight, scrollTop) => { + this._handleScrollEnd(offsetHeight, scrollHeight, scrollTop); + }; + + this._handleOutputTable = ref => { + this._outputTable = ref; + }; + + this._scrollToBottom = () => { + if (!this._outputTable) { + return; + } + + this._outputTable.scrollToBottom(); + + this.setState({ unseenMessages: false }); + }; + + this._startScrollToBottom = () => { + if (!this._isScrollingToBottom) { + this._isScrollingToBottom = true; + + this._scrollingThrottle = _rxjsBundlesRxMinJs.Observable.timer(MAXIMUM_SCROLLING_TIME).subscribe(() => { + this._isScrollingToBottom = false; + }); + } + + this._scrollToBottom(); + }; + + this._stopScrollToBottom = () => { + this._isScrollingToBottom = false; + this._scrollingThrottle.unsubscribe(); + }; + + this._shouldScrollToBottom = () => { + return this._isScrolledNearBottom || this._isScrollingToBottom; + }; - constructor(props: Props) { - super(props); this.state = { - unseenMessages: false, + unseenMessages: false }; - this._disposables = new UniversalDisposable(); + this._disposables = new (_UniversalDisposable || _load_UniversalDisposable()).default(); this._isScrolledNearBottom = true; this._isScrollingToBottom = false; - (this: any)._handleScrollEnd = debounce(this._handleScrollEnd, 100); + this._handleScrollEnd = (0, (_debounce || _load_debounce()).default)(this._handleScrollEnd, 100); this._id = count++; } - componentDidMount(): void { + // Used when _scrollToBottom is called. The console optimizes message loading + // so scrolling to the bottom once doesn't always scroll to the bottom since + // more messages can be loaded after. + + + componentDidMount() { // Wait for `` to render itself via react-virtualized before scrolling and // re-measuring; Otherwise, the scrolled location will be inaccurate, preventing the Console // from auto-scrolling. @@ -105,184 +178,133 @@ export default class ConsoleView extends React.Component { }); } - componentWillUnmount(): void { + componentWillUnmount() { this._disposables.dispose(); } - componentDidUpdate(prevProps: Props): void { + componentDidUpdate(prevProps) { // If records are added while we're scrolled to the bottom (or very very close, at least), // automatically scroll. - if ( - this._isScrolledNearBottom && - recordsChanged( - prevProps.displayableRecords, - this.props.displayableRecords, - ) - ) { + if (this._isScrolledNearBottom && (0, (_recordsChanged || _load_recordsChanged()).default)(prevProps.displayableRecords, this.props.displayableRecords)) { this._startScrollToBottom(); } } - _renderPromptButton(): React.Element { - invariant(this.props.currentExecutor != null); - const {currentExecutor} = this.props; + _renderPromptButton() { + if (!(this.props.currentExecutor != null)) { + throw new Error('Invariant violation: "this.props.currentExecutor != null"'); + } + + const { currentExecutor } = this.props; const options = Array.from(this.props.executors.values()).map(executor => ({ id: executor.id, - label: executor.name, + label: executor.name })); - return ( - - ); + return _react.createElement((_PromptButton || _load_PromptButton()).default, { + value: currentExecutor.id, + onChange: this.props.selectExecutor, + options: options, + children: currentExecutor.name + }); } - _isScrolledToBottom( - offsetHeight: number, - scrollHeight: number, - scrollTop: number, - ): boolean { + _isScrolledToBottom(offsetHeight, scrollHeight, scrollTop) { return scrollHeight - (offsetHeight + scrollTop) < 5; } - componentWillReceiveProps(nextProps: Props): void { + componentWillReceiveProps(nextProps) { // If the messages were cleared, hide the notification. if (nextProps.displayableRecords.length === 0) { this._isScrolledNearBottom = true; - this.setState({unseenMessages: false}); + this.setState({ unseenMessages: false }); } else if ( - // If we receive new messages after we've scrolled away from the bottom, show the "new - // messages" notification. - !this._isScrolledNearBottom && - recordsChanged( - this.props.displayableRecords, - nextProps.displayableRecords, - ) - ) { - this.setState({unseenMessages: true}); + // If we receive new messages after we've scrolled away from the bottom, show the "new + // messages" notification. + !this._isScrolledNearBottom && (0, (_recordsChanged || _load_recordsChanged()).default)(this.props.displayableRecords, nextProps.displayableRecords)) { + this.setState({ unseenMessages: true }); } } - shouldComponentUpdate(nextProps: Props, nextState: State): boolean { - return ( - !shallowEqual(this.props, nextProps) || - !shallowEqual(this.state, nextState) - ); + shouldComponentUpdate(nextProps, nextState) { + return !(0, (_shallowequal || _load_shallowequal()).default)(this.props, nextProps) || !(0, (_shallowequal || _load_shallowequal()).default)(this.state, nextState); } - _getExecutor = (id: string): ?Executor => { - return this.props.executors.get(id); - }; - - _getProvider = (id: string): ?OutputProvider => { - return this.props.getProvider(id); - }; - - render(): React.Node { - return ( -
- - - {/* - We need an extra wrapper element here in order to have the new messages notification stick - to the bottom of the scrollable area (and not scroll with it). - - console-font-size is defined in main.js and updated via a user setting - */} -
-
- - =0.53.0) Flow suppress - ref={this._handleOutputTable} - displayableRecords={this.props.displayableRecords} - showSourceLabels={this.props.selectedSourceIds.length > 1} - fontSize={this.props.fontSize} - getExecutor={this._getExecutor} - getProvider={this._getProvider} - onScroll={this._handleScroll} - onDisplayableRecordHeightChange={ - this.props.onDisplayableRecordHeightChange - } - shouldScrollToBottom={this._shouldScrollToBottom} - /> - -
- {this._renderPrompt()} -
-
+ ` + }), + _react.createElement((_ConsoleHeader || _load_ConsoleHeader()).default, { + clear: this.props.clearRecords, + createPaste: this.props.createPaste, + invalidFilterInput: this.props.invalidFilterInput, + enableRegExpFilter: this.props.enableRegExpFilter, + filterText: this.props.filterText, + selectedSourceIds: this.props.selectedSourceIds, + sources: this.props.sources, + onFilterChange: this.props.updateFilter, + onSelectedSourcesChange: this.props.selectSources + }), + _react.createElement( + 'div', + { className: 'console-body', id: 'console-font-size-' + this._id }, + _react.createElement( + 'div', + { className: 'console-scroll-pane-wrapper' }, + _react.createElement((_FilteredMessagesReminder || _load_FilteredMessagesReminder()).default, { + filteredRecordCount: this.props.filteredRecordCount, + onReset: this.props.resetAllFilters + }), + _react.createElement((_OutputTable || _load_OutputTable()).default + // $FlowFixMe(>=0.53.0) Flow suppress + , { ref: this._handleOutputTable, + displayableRecords: this.props.displayableRecords, + showSourceLabels: this.props.selectedSourceIds.length > 1, + fontSize: this.props.fontSize, + getExecutor: this._getExecutor, + getProvider: this._getProvider, + onScroll: this._handleScroll, + onDisplayableRecordHeightChange: this.props.onDisplayableRecordHeightChange, + shouldScrollToBottom: this._shouldScrollToBottom + }), + _react.createElement((_NewMessagesNotification || _load_NewMessagesNotification()).default, { + visible: this.state.unseenMessages, + onClick: this._startScrollToBottom + }) + ), + this._renderPrompt() + ) ); } - _renderPrompt(): ?React.Element { - const {currentExecutor} = this.props; + _renderPrompt() { + const { currentExecutor } = this.props; if (currentExecutor == null) { return; } - return ( -
- {this._renderPromptButton()} - -
+ return _react.createElement( + 'div', + { className: 'console-prompt' }, + this._renderPromptButton(), + _react.createElement((_InputArea || _load_InputArea()).default, { + scopeName: currentExecutor.scopeName, + onSubmit: this._executePrompt, + history: this.props.history, + watchEditor: this.props.watchEditor + }) ); } - _executePrompt = (code: string): void => { - this.props.execute(code); - // Makes the console to scroll to the bottom. - this._isScrolledNearBottom = true; - }; - - _handleScroll = ( - offsetHeight: number, - scrollHeight: number, - scrollTop: number, - ): void => { - this._handleScrollEnd(offsetHeight, scrollHeight, scrollTop); - }; - - _handleScrollEnd( - offsetHeight: number, - scrollHeight: number, - scrollTop: number, - ): void { - const isScrolledToBottom = this._isScrolledToBottom( - offsetHeight, - scrollHeight, - scrollTop, - ); + _handleScrollEnd(offsetHeight, scrollHeight, scrollTop) { + const isScrolledToBottom = this._isScrolledToBottom(offsetHeight, scrollHeight, scrollTop); if (this._isScrollingToBottom && !isScrolledToBottom) { this._scrollToBottom(); @@ -290,46 +312,10 @@ export default class ConsoleView extends React.Component { this._isScrolledNearBottom = isScrolledToBottom; this._stopScrollToBottom(); this.setState({ - unseenMessages: - this.state.unseenMessages && !this._isScrolledNearBottom, + unseenMessages: this.state.unseenMessages && !this._isScrolledNearBottom }); } } - _handleOutputTable = (ref: OutputTable): void => { - this._outputTable = ref; - }; - - _scrollToBottom = (): void => { - if (!this._outputTable) { - return; - } - - this._outputTable.scrollToBottom(); - - this.setState({unseenMessages: false}); - }; - - _startScrollToBottom = (): void => { - if (!this._isScrollingToBottom) { - this._isScrollingToBottom = true; - - this._scrollingThrottle = Observable.timer( - MAXIMUM_SCROLLING_TIME, - ).subscribe(() => { - this._isScrollingToBottom = false; - }); - } - - this._scrollToBottom(); - }; - - _stopScrollToBottom = (): void => { - this._isScrollingToBottom = false; - this._scrollingThrottle.unsubscribe(); - }; - - _shouldScrollToBottom = (): boolean => { - return this._isScrolledNearBottom || this._isScrollingToBottom; - }; } +exports.default = ConsoleView; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/FilteredMessagesReminder.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/FilteredMessagesReminder.js index 8a56f66b..0b7e2c1a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/FilteredMessagesReminder.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/FilteredMessagesReminder.js @@ -1,47 +1,64 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -import * as React from 'react'; +Object.defineProperty(exports, "__esModule", { + value: true +}); -type Props = { - filteredRecordCount: number, - onReset: () => void, -}; +var _react = _interopRequireWildcard(require('react')); -export default class FilteredMessagesReminder extends React.Component { - handleClick = (e: SyntheticEvent<>) => { - e.preventDefault(); - this.props.onReset(); - }; +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } - render(): React.Node { - const {filteredRecordCount} = this.props; +class FilteredMessagesReminder extends _react.Component { + constructor(...args) { + var _temp; + + return _temp = super(...args), this.handleClick = e => { + e.preventDefault(); + this.props.onReset(); + }, _temp; + } + + render() { + const { filteredRecordCount } = this.props; if (filteredRecordCount === 0) { return null; } - return ( -
-
-
-            {filteredRecordCount}{' '}
-            {filteredRecordCount === 1 ? 'message is' : 'messages are'} hidden
-            by filters.
-          
-
- -
Show all messages.
-
-
+ return _react.createElement( + 'div', + { className: 'console-filtered-reminder' }, + _react.createElement( + 'div', + { style: { flex: 1 } }, + _react.createElement( + 'pre', + null, + filteredRecordCount, + ' ', + filteredRecordCount === 1 ? 'message is' : 'messages are', + ' hidden by filters.' + ) + ), + _react.createElement( + 'a', + { href: '#', onClick: this.handleClick }, + _react.createElement( + 'pre', + null, + 'Show all messages.' + ) + ) ); } } +exports.default = FilteredMessagesReminder; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/InputArea.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/InputArea.js index 9b1d4a43..0cb6f7c4 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/InputArea.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/InputArea.js @@ -1,3 +1,31 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _react = _interopRequireWildcard(require('react')); + +var _reactDom = _interopRequireDefault(require('react-dom')); + +var _AtomTextEditor; + +function _load_AtomTextEditor() { + return _AtomTextEditor = require('nuclide-commons-ui/AtomTextEditor'); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,156 +34,124 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {WatchEditorFunction} from '../types'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import {AtomTextEditor} from 'nuclide-commons-ui/AtomTextEditor'; -import {Observable} from 'rxjs'; - -type Props = { - onSubmit: (value: string) => mixed, - scopeName: ?string, - history: Array, - watchEditor: ?WatchEditorFunction, -}; - -type State = { - historyIndex: number, - draft: string, -}; - const ENTER_KEY_CODE = 13; const UP_KEY_CODE = 38; const DOWN_KEY_CODE = 40; -export default class InputArea extends React.Component { - _keySubscription: ?rxjs$ISubscription; - _textEditorModel: ?atom$TextEditor; +class InputArea extends _react.Component { - constructor(props: Props) { + constructor(props) { super(props); - this.state = { - historyIndex: -1, - draft: '', - }; - } - _submit = (): void => { - // Clear the text and trigger the `onSubmit` callback - const editor = this._textEditorModel; - if (editor == null) { - return; - } - - const text = editor.getText(); - if (text === '') { - return; - } - - editor.setText(''); // Clear the text field. - this.props.onSubmit(text); - this.setState({historyIndex: -1}); - }; - - _attachLabel = (editor: atom$TextEditor): IDisposable => { - const {watchEditor} = this.props; - const disposable = new UniversalDisposable(); - if (watchEditor) { - disposable.add(watchEditor(editor, ['nuclide-console'])); - } - return disposable; - }; - - _handleTextEditor = (component: ?AtomTextEditor): void => { - if (this._keySubscription) { - this._textEditorModel = null; - this._keySubscription.unsubscribe(); - } - if (component) { - this._textEditorModel = component.getModel(); - const el = ReactDOM.findDOMNode(component); - this._keySubscription = Observable.fromEvent(el, 'keydown').subscribe( - this._handleKeyDown, - ); - } - }; - - _handleKeyDown = (event: KeyboardEvent): void => { - const editor = this._textEditorModel; - // Detect AutocompletePlus menu element: https://git.io/vddLi - const isAutocompleteOpen = - document.querySelector('autocomplete-suggestion-list') != null; - if (editor == null) { - return; - } - if (event.which === ENTER_KEY_CODE) { - event.preventDefault(); - event.stopImmediatePropagation(); - - if (event.ctrlKey) { - editor.insertNewline(); + this._submit = () => { + // Clear the text and trigger the `onSubmit` callback + const editor = this._textEditorModel; + if (editor == null) { return; } - this._submit(); - } else if (event.which === UP_KEY_CODE) { - if (this.props.history.length === 0 || isAutocompleteOpen) { + const text = editor.getText(); + if (text === '') { return; } - event.preventDefault(); - event.stopImmediatePropagation(); - const historyIndex = Math.min( - this.state.historyIndex + 1, - this.props.history.length - 1, - ); - if (this.state.historyIndex === -1) { - this.setState({historyIndex, draft: editor.getText()}); - } else { - this.setState({historyIndex}); + + editor.setText(''); // Clear the text field. + this.props.onSubmit(text); + this.setState({ historyIndex: -1 }); + }; + + this._attachLabel = editor => { + const { watchEditor } = this.props; + const disposable = new (_UniversalDisposable || _load_UniversalDisposable()).default(); + if (watchEditor) { + disposable.add(watchEditor(editor, ['nuclide-console'])); } - editor.setText( - this.props.history[this.props.history.length - historyIndex - 1], - ); - } else if (event.which === DOWN_KEY_CODE) { - if (this.props.history.length === 0 || isAutocompleteOpen) { + return disposable; + }; + + this._handleTextEditor = component => { + if (this._keySubscription) { + this._textEditorModel = null; + this._keySubscription.unsubscribe(); + } + if (component) { + this._textEditorModel = component.getModel(); + const el = _reactDom.default.findDOMNode(component); + this._keySubscription = _rxjsBundlesRxMinJs.Observable.fromEvent(el, 'keydown').subscribe(this._handleKeyDown); + } + }; + + this._handleKeyDown = event => { + const editor = this._textEditorModel; + // Detect AutocompletePlus menu element: https://git.io/vddLi + const isAutocompleteOpen = document.querySelector('autocomplete-suggestion-list') != null; + if (editor == null) { return; } - event.preventDefault(); - event.stopImmediatePropagation(); - const historyIndex = Math.max(this.state.historyIndex - 1, -1); - this.setState({historyIndex}); - if (historyIndex === -1) { - editor.setText(this.state.draft); - } else { - editor.setText( - this.props.history[this.props.history.length - historyIndex - 1], - ); + if (event.which === ENTER_KEY_CODE) { + event.preventDefault(); + event.stopImmediatePropagation(); + + if (event.ctrlKey) { + editor.insertNewline(); + return; + } + + this._submit(); + } else if (event.which === UP_KEY_CODE) { + if (this.props.history.length === 0 || isAutocompleteOpen) { + return; + } + event.preventDefault(); + event.stopImmediatePropagation(); + const historyIndex = Math.min(this.state.historyIndex + 1, this.props.history.length - 1); + if (this.state.historyIndex === -1) { + this.setState({ historyIndex, draft: editor.getText() }); + } else { + this.setState({ historyIndex }); + } + editor.setText(this.props.history[this.props.history.length - historyIndex - 1]); + } else if (event.which === DOWN_KEY_CODE) { + if (this.props.history.length === 0 || isAutocompleteOpen) { + return; + } + event.preventDefault(); + event.stopImmediatePropagation(); + const historyIndex = Math.max(this.state.historyIndex - 1, -1); + this.setState({ historyIndex }); + if (historyIndex === -1) { + editor.setText(this.state.draft); + } else { + editor.setText(this.props.history[this.props.history.length - historyIndex - 1]); + } } - } - }; - - render(): React.Node { - const grammar = - this.props.scopeName == null - ? null - : atom.grammars.grammarForScopeName(this.props.scopeName); - return ( -
- -
+ }; + + this.state = { + historyIndex: -1, + draft: '' + }; + } + + render() { + const grammar = this.props.scopeName == null ? null : atom.grammars.grammarForScopeName(this.props.scopeName); + return _react.createElement( + 'div', + { className: 'console-input-wrapper' }, + _react.createElement((_AtomTextEditor || _load_AtomTextEditor()).AtomTextEditor, { + ref: this._handleTextEditor, + grammar: grammar, + gutterHidden: true, + autoGrow: true, + lineNumberGutterVisible: false, + onConfirm: this._submit, + onInitialized: this._attachLabel + }) ); } } +exports.default = InputArea; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/NewMessagesNotification.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/NewMessagesNotification.js index 8d6a9404..ca1175df 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/NewMessagesNotification.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/NewMessagesNotification.js @@ -1,3 +1,21 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _classnames; + +function _load_classnames() { + return _classnames = _interopRequireDefault(require('classnames')); +} + +var _react = _interopRequireWildcard(require('react')); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,33 +24,21 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import classnames from 'classnames'; -import * as React from 'react'; - -type Props = { - onClick: () => mixed, - visible: boolean, -}; - -export default class NewMessagesNotification extends React.Component { - render(): React.Node { - const className = classnames( - 'console-new-messages-notification', - 'badge', - 'badge-info', - { - visible: this.props.visible, - }, - ); - return ( -
- - New Messages -
+class NewMessagesNotification extends _react.Component { + render() { + const className = (0, (_classnames || _load_classnames()).default)('console-new-messages-notification', 'badge', 'badge-info', { + visible: this.props.visible + }); + return _react.createElement( + 'div', + { className: className, onClick: this.props.onClick }, + _react.createElement('span', { className: 'console-new-messages-notification-icon icon icon-nuclicon-arrow-down' }), + 'New Messages' ); } } +exports.default = NewMessagesNotification; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/OutputTable.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/OutputTable.js index 1889fd6f..8de5f657 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/OutputTable.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/OutputTable.js @@ -1,243 +1,221 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type { - DisplayableRecord, - Executor, - OutputProvider, - Record, - RecordHeightChangeHandler, -} from '../types'; - -import Hasher from 'nuclide-commons/Hasher'; -import * as React from 'react'; -import List from 'react-virtualized/dist/commonjs/List'; -import RecordView from './RecordView'; -import recordsChanged from '../recordsChanged'; -import {ResizeSensitiveContainer} from 'nuclide-commons-ui/ResizeSensitiveContainer'; - -type Props = { - displayableRecords: Array, - showSourceLabels: boolean, - fontSize: number, - getExecutor: (id: string) => ?Executor, - getProvider: (id: string) => ?OutputProvider, - onScroll: ( - offsetHeight: number, - scrollHeight: number, - scrollTop: number, - ) => void, - onDisplayableRecordHeightChange: RecordHeightChangeHandler, - shouldScrollToBottom: () => boolean, -}; - -type State = { - width: number, - height: number, -}; - -type RowRendererParams = { - index: number, - key: string, - style: Object, - isScrolling: boolean, -}; - -type RowHeightParams = { - index: number, -}; +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _Hasher; + +function _load_Hasher() { + return _Hasher = _interopRequireDefault(require('nuclide-commons/Hasher')); +} + +var _react = _interopRequireWildcard(require('react')); + +var _List; + +function _load_List() { + return _List = _interopRequireDefault(require('react-virtualized/dist/commonjs/List')); +} + +var _RecordView; + +function _load_RecordView() { + return _RecordView = _interopRequireDefault(require('./RecordView')); +} + +var _recordsChanged; + +function _load_recordsChanged() { + return _recordsChanged = _interopRequireDefault(require('../recordsChanged')); +} + +var _ResizeSensitiveContainer; + +function _load_ResizeSensitiveContainer() { + return _ResizeSensitiveContainer = require('nuclide-commons-ui/ResizeSensitiveContainer'); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } -/* eslint-disable react/no-unused-prop-types */ -type OnScrollParams = { - clientHeight: number, - scrollHeight: number, - scrollTop: number, -}; /* eslint-enable react/no-unused-prop-types */ // The number of extra rows to render beyond what is visible -const OVERSCAN_COUNT = 5; -export default class OutputTable extends React.Component { - _hasher: Hasher; - // This is a from react-virtualized (untyped library) - _list: ?React.Element; - _wrapper: ?HTMLElement; - _renderedRecords: Map; - // The currently rendered range. - _startIndex: number; - _stopIndex: number; +/* eslint-disable react/no-unused-prop-types */ +const OVERSCAN_COUNT = 5; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +class OutputTable extends _react.Component { - constructor(props: Props) { + // The currently rendered range. + constructor(props) { super(props); - this._hasher = new Hasher(); + + this._handleListRender = opts => { + this._startIndex = opts.startIndex; + this._stopIndex = opts.stopIndex; + }; + + this._getExecutor = id => { + return this.props.getExecutor(id); + }; + + this._getProvider = id => { + return this.props.getProvider(id); + }; + + this._renderRow = rowMetadata => { + const { index, style } = rowMetadata; + const displayableRecord = this.props.displayableRecords[index]; + const { record } = displayableRecord; + return _react.createElement( + 'div', + { + key: this._hasher.getHash(displayableRecord.record), + className: 'console-table-row-wrapper', + style: style }, + _react.createElement((_RecordView || _load_RecordView()).default, { + ref: view => { + if (view != null) { + this._renderedRecords.set(record, view); + } else { + this._renderedRecords.delete(record); + } + }, + getExecutor: this._getExecutor, + getProvider: this._getProvider, + displayableRecord: displayableRecord, + showSourceLabel: this.props.showSourceLabels, + onHeightChange: this._handleRecordHeightChange + }) + ); + }; + + this._getRowHeight = ({ index }) => { + return this.props.displayableRecords[index].height; + }; + + this._handleTableWrapper = tableWrapper => { + this._wrapper = tableWrapper; + }; + + this._handleListRef = listRef => { + this._list = listRef; + }; + + this._handleResize = (height, width) => { + this.setState({ + width, + height + }); + + // When this component resizes, the inner records will + // also resize and potentially have their heights change + // So we measure all of their heights again here + this._renderedRecords.forEach(recordView => recordView.measureAndNotifyHeight()); + }; + + this._handleRecordHeightChange = (recordId, newHeight) => { + this.props.onDisplayableRecordHeightChange(recordId, newHeight, () => { + // The react-virtualized List component is provided the row heights + // through a function, so it has no way of knowing that a row's height + // has changed unless we explicitly notify it to recompute the heights. + if (this._list == null) { + return; + } + // $FlowIgnore Untyped react-virtualized List component method + this._list.recomputeRowHeights(); + + // If we are already scrolled to the bottom, scroll to ensure that the scrollbar remains at + // the bottom. This is important not just for if the last record changes height through user + // interaction (e.g. expanding a debugger variable), but also because this is the mechanism + // through which the record's true initial height is reported. Therefore, we may have scrolled + // to the bottom, and only afterwards received its true height. In this case, it's important + // that we then scroll to the new bottom. + if (this.props.shouldScrollToBottom()) { + this.scrollToBottom(); + } + }); + }; + + this._onScroll = ({ + clientHeight, + scrollHeight, + scrollTop + }) => { + this.props.onScroll(clientHeight, scrollHeight, scrollTop); + }; + + this._hasher = new (_Hasher || _load_Hasher()).default(); this._renderedRecords = new Map(); this.state = { width: 0, - height: 0, + height: 0 }; this._startIndex = 0; this._stopIndex = 0; } + // This is a from react-virtualized (untyped library) - componentDidUpdate(prevProps: Props, prevState: State): void { - if ( - this._list != null && - recordsChanged( - prevProps.displayableRecords, - this.props.displayableRecords, - ) - ) { + + componentDidUpdate(prevProps, prevState) { + if (this._list != null && (0, (_recordsChanged || _load_recordsChanged()).default)(prevProps.displayableRecords, this.props.displayableRecords)) { // $FlowIgnore Untyped react-virtualized List method this._list.recomputeRowHeights(); } if (prevProps.fontSize !== this.props.fontSize) { - this._renderedRecords.forEach(recordView => - recordView.measureAndNotifyHeight(), - ); + this._renderedRecords.forEach(recordView => recordView.measureAndNotifyHeight()); } } - render(): React.Node { + render() { return ( // $FlowFixMe(>=0.53.0) Flow suppress - - {this._containerRendered() ? ( - =0.53.0) Flow suppress - ref={this._handleListRef} - height={this.state.height} - width={this.state.width} - rowCount={this.props.displayableRecords.length} - rowHeight={this._getRowHeight} - rowRenderer={this._renderRow} - overscanRowCount={OVERSCAN_COUNT} - onScroll={this._onScroll} - onRowsRendered={this._handleListRender} - /> - ) : null} - + _react.createElement( + (_ResizeSensitiveContainer || _load_ResizeSensitiveContainer()).ResizeSensitiveContainer, + { + className: 'console-table-wrapper native-key-bindings', + onResize: this._handleResize, + tabIndex: '1' }, + this._containerRendered() ? _react.createElement((_List || _load_List()).default + // $FlowFixMe(>=0.53.0) Flow suppress + , { ref: this._handleListRef, + height: this.state.height, + width: this.state.width, + rowCount: this.props.displayableRecords.length, + rowHeight: this._getRowHeight, + rowRenderer: this._renderRow, + overscanRowCount: OVERSCAN_COUNT, + onScroll: this._onScroll, + onRowsRendered: this._handleListRender + }) : null + ) ); } - _handleListRender = (opts: {startIndex: number, stopIndex: number}): void => { - this._startIndex = opts.startIndex; - this._stopIndex = opts.stopIndex; - }; - - scrollToBottom(): void { + scrollToBottom() { if (this._list != null) { // $FlowIgnore Untyped react-virtualized List method this._list.scrollToRow(this.props.displayableRecords.length - 1); } } - _getExecutor = (id: string): ?Executor => { - return this.props.getExecutor(id); - }; - - _getProvider = (id: string): ?OutputProvider => { - return this.props.getProvider(id); - }; - - _renderRow = (rowMetadata: RowRendererParams): React.Element => { - const {index, style} = rowMetadata; - const displayableRecord = this.props.displayableRecords[index]; - const {record} = displayableRecord; - return ( -
- { - if (view != null) { - this._renderedRecords.set(record, view); - } else { - this._renderedRecords.delete(record); - } - }} - getExecutor={this._getExecutor} - getProvider={this._getProvider} - displayableRecord={displayableRecord} - showSourceLabel={this.props.showSourceLabels} - onHeightChange={this._handleRecordHeightChange} - /> -
- ); - }; - - _containerRendered(): boolean { + _containerRendered() { return this.state.width !== 0 && this.state.height !== 0; } - _getRowHeight = ({index}: RowHeightParams): number => { - return this.props.displayableRecords[index].height; - }; - - _handleTableWrapper = (tableWrapper: HTMLElement): void => { - this._wrapper = tableWrapper; - }; - - _handleListRef = (listRef: React.Element): void => { - this._list = listRef; - }; - - _handleResize = (height: number, width: number): void => { - this.setState({ - width, - height, - }); - - // When this component resizes, the inner records will - // also resize and potentially have their heights change - // So we measure all of their heights again here - this._renderedRecords.forEach(recordView => - recordView.measureAndNotifyHeight(), - ); - }; - - _handleRecordHeightChange = (recordId: number, newHeight: number): void => { - this.props.onDisplayableRecordHeightChange(recordId, newHeight, () => { - // The react-virtualized List component is provided the row heights - // through a function, so it has no way of knowing that a row's height - // has changed unless we explicitly notify it to recompute the heights. - if (this._list == null) { - return; - } - // $FlowIgnore Untyped react-virtualized List component method - this._list.recomputeRowHeights(); - - // If we are already scrolled to the bottom, scroll to ensure that the scrollbar remains at - // the bottom. This is important not just for if the last record changes height through user - // interaction (e.g. expanding a debugger variable), but also because this is the mechanism - // through which the record's true initial height is reported. Therefore, we may have scrolled - // to the bottom, and only afterwards received its true height. In this case, it's important - // that we then scroll to the new bottom. - if (this.props.shouldScrollToBottom()) { - this.scrollToBottom(); - } - }); - }; - - _onScroll = ({ - clientHeight, - scrollHeight, - scrollTop, - }: OnScrollParams): void => { - this.props.onScroll(clientHeight, scrollHeight, scrollTop); - }; } +exports.default = OutputTable; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/PromptButton.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/PromptButton.js index 3e44e2da..8635dd50 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/PromptButton.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/PromptButton.js @@ -1,60 +1,65 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import invariant from 'assert'; -import * as React from 'react'; -import electron from 'electron'; - -const {remote} = electron; -invariant(remote != null); - -type PromptOption = { - id: string, - label: string, -}; - -type Props = { - value: string, - onChange: (value: string) => void, - children: ?any, - options: Array, -}; - -export default class PromptButton extends React.Component { - _disposables: IDisposable; - - render(): React.Node { - return ( - - {this.props.children} - - - ); - } +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _react = _interopRequireWildcard(require('react')); + +var _electron = _interopRequireDefault(require('electron')); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +const { remote } = _electron.default; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ - _handleClick = (event: SyntheticMouseEvent<>): void => { - const currentWindow = remote.getCurrentWindow(); - const menu = new remote.Menu(); - // TODO: Sort alphabetically by label - this.props.options.forEach(option => { - menu.append( - new remote.MenuItem({ +if (!(remote != null)) { + throw new Error('Invariant violation: "remote != null"'); +} + +class PromptButton extends _react.Component { + constructor(...args) { + var _temp; + + return _temp = super(...args), this._handleClick = event => { + const currentWindow = remote.getCurrentWindow(); + const menu = new remote.Menu(); + // TODO: Sort alphabetically by label + this.props.options.forEach(option => { + menu.append(new remote.MenuItem({ type: 'checkbox', checked: this.props.value === option.id, label: option.label, - click: () => this.props.onChange(option.id), - }), - ); - }); - menu.popup(currentWindow, event.clientX, event.clientY); - }; + click: () => this.props.onChange(option.id) + })); + }); + menu.popup(currentWindow, event.clientX, event.clientY); + }, _temp; + } + + render() { + return _react.createElement( + 'span', + { className: 'console-prompt-wrapper', onClick: this._handleClick }, + _react.createElement( + 'span', + { className: 'console-prompt-label' }, + this.props.children + ), + _react.createElement('span', { className: 'icon icon-chevron-right' }) + ); + } + } +exports.default = PromptButton; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/RecordView.js b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/RecordView.js index e3a0ed80..56eaaabb 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/RecordView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/lib/ui/RecordView.js @@ -1,3 +1,75 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _classnames; + +function _load_classnames() { + return _classnames = _interopRequireDefault(require('classnames')); +} + +var _MeasuredComponent; + +function _load_MeasuredComponent() { + return _MeasuredComponent = require('nuclide-commons-ui/MeasuredComponent'); +} + +var _react = _interopRequireWildcard(require('react')); + +var _LazyNestedValueComponent; + +function _load_LazyNestedValueComponent() { + return _LazyNestedValueComponent = require('nuclide-commons-ui/LazyNestedValueComponent'); +} + +var _SimpleValueComponent; + +function _load_SimpleValueComponent() { + return _SimpleValueComponent = _interopRequireDefault(require('nuclide-commons-ui/SimpleValueComponent')); +} + +var _shallowequal; + +function _load_shallowequal() { + return _shallowequal = _interopRequireDefault(require('shallowequal')); +} + +var _TextRenderer; + +function _load_TextRenderer() { + return _TextRenderer = require('nuclide-commons-ui/TextRenderer'); +} + +var _debounce; + +function _load_debounce() { + return _debounce = _interopRequireDefault(require('nuclide-commons/debounce')); +} + +var _observable; + +function _load_observable() { + return _observable = require('nuclide-commons/observable'); +} + +var _string; + +function _load_string() { + return _string = require('nuclide-commons/string'); +} + +var _featureConfig; + +function _load_featureConfig() { + return _featureConfig = _interopRequireDefault(require('nuclide-commons-atom/feature-config')); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,54 +78,46 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type { - Level, - Record, - DisplayableRecord, - Executor, - OutputProvider, -} from '../types'; - -import classnames from 'classnames'; -import {MeasuredComponent} from 'nuclide-commons-ui/MeasuredComponent'; -import * as React from 'react'; -import {LazyNestedValueComponent} from 'nuclide-commons-ui/LazyNestedValueComponent'; -import SimpleValueComponent from 'nuclide-commons-ui/SimpleValueComponent'; -import shallowEqual from 'shallowequal'; -import {TextRenderer} from 'nuclide-commons-ui/TextRenderer'; -import debounce from 'nuclide-commons/debounce'; -import {nextAnimationFrame} from 'nuclide-commons/observable'; -import {URL_REGEX} from 'nuclide-commons/string'; -import featureConfig from 'nuclide-commons-atom/feature-config'; - -type Props = { - displayableRecord: DisplayableRecord, - showSourceLabel: boolean, - getExecutor: (id: string) => ?Executor, - getProvider: (id: string) => ?OutputProvider, - onHeightChange: (recordId: number, newHeight: number) => void, -}; - const ONE_DAY = 1000 * 60 * 60 * 24; -export default class RecordView extends React.Component { - _wrapper: ?HTMLElement; - _debouncedMeasureAndNotifyHeight: () => void; - _rafDisposable: ?rxjs$Subscription; +class RecordView extends _react.Component { - constructor(props: Props) { + constructor(props) { super(props); // The MeasuredComponent can call this many times in quick succession as the // child components render, so we debounce it since we only want to know about // the height change once everything has settled down - (this: any)._debouncedMeasureAndNotifyHeight = debounce( - this.measureAndNotifyHeight, - 10, - ); + + this.measureAndNotifyHeight = () => { + // This method is called after the necessary DOM mutations have + // already occurred, however it is possible that the updates have + // not been flushed to the screen. So the height change update + // is deferred until the rendering is complete so that + // this._wrapper.offsetHeight gives us the correct final height + if (this._rafDisposable != null) { + this._rafDisposable.unsubscribe(); + } + this._rafDisposable = (_observable || _load_observable()).nextAnimationFrame.subscribe(() => { + if (this._wrapper == null) { + return; + } + const { offsetHeight } = this._wrapper; + const { displayableRecord, onHeightChange } = this.props; + if (offsetHeight !== displayableRecord.height) { + onHeightChange(displayableRecord.id, offsetHeight); + } + }); + }; + + this._handleRecordWrapper = wrapper => { + this._wrapper = wrapper; + }; + + this._debouncedMeasureAndNotifyHeight = (0, (_debounce || _load_debounce()).default)(this.measureAndNotifyHeight, 10); } componentDidMount() { @@ -68,13 +132,17 @@ export default class RecordView extends React.Component { } } - _renderContent(displayableRecord: DisplayableRecord): React.Element { - const {record} = displayableRecord; + _renderContent(displayableRecord) { + const { record } = displayableRecord; if (record.kind === 'request') { // TODO: We really want to use a text editor to render this so that we can get syntax // highlighting, but they're just too expensive. Figure out a less-expensive way to get syntax // highlighting. - return
{record.text || ' '}
; + return _react.createElement( + 'pre', + null, + record.text || ' ' + ); } else if (record.kind === 'response') { const executor = this.props.getExecutor(record.sourceId); return this._renderNestedValueComponent(displayableRecord, executor); @@ -84,128 +152,106 @@ export default class RecordView extends React.Component { } else { // If there's not text, use a space to make sure the row doesn't collapse. const text = record.text || ' '; - return
{parseText(text)}
; + return _react.createElement( + 'pre', + null, + parseText(text) + ); } } - shouldComponentUpdate(nextProps: Props): boolean { - return !shallowEqual(this.props, nextProps); + shouldComponentUpdate(nextProps) { + return !(0, (_shallowequal || _load_shallowequal()).default)(this.props, nextProps); } - _renderNestedValueComponent( - displayableRecord: DisplayableRecord, - provider: ?OutputProvider | ?Executor, - ): React.Element { - const {record, expansionStateId} = displayableRecord; + _renderNestedValueComponent(displayableRecord, provider) { + const { record, expansionStateId } = displayableRecord; const getProperties = provider == null ? null : provider.getProperties; const type = record.data == null ? null : record.data.type; const simpleValueComponent = getComponent(type); - return ( - - ); + return _react.createElement((_LazyNestedValueComponent || _load_LazyNestedValueComponent()).LazyNestedValueComponent, { + className: 'console-lazy-nested-value', + evaluationResult: record.data, + fetchChildren: getProperties, + simpleValueComponent: simpleValueComponent, + shouldCacheChildren: true, + expansionStateId: expansionStateId + }); } - render(): React.Node { - const {displayableRecord} = this.props; - const {record} = displayableRecord; - const {level, kind, timestamp, sourceId} = record; + render() { + const { displayableRecord } = this.props; + const { record } = displayableRecord; + const { level, kind, timestamp, sourceId } = record; - const classNames = classnames('console-record', `level-${level || 'log'}`, { + const classNames = (0, (_classnames || _load_classnames()).default)('console-record', `level-${level || 'log'}`, { request: kind === 'request', - response: kind === 'response', + response: kind === 'response' }); const iconName = getIconName(record); // flowlint-next-line sketchy-null-string:off - const icon = iconName ? : null; - const sourceLabel = this.props.showSourceLabel ? ( - - {sourceId} - + const icon = iconName ? _react.createElement('span', { className: `icon icon-${iconName}` }) : null; + const sourceLabel = this.props.showSourceLabel ? _react.createElement( + 'span', + { + className: `console-record-source-label ${getHighlightClassName(level)}` }, + sourceId ) : null; let renderedTimestamp; if (timestamp != null) { - const timestampLabel = - Date.now() - timestamp > ONE_DAY - ? timestamp.toLocaleString() - : timestamp.toLocaleTimeString(); - renderedTimestamp = ( -
{timestampLabel}
+ const timestampLabel = Date.now() - timestamp > ONE_DAY ? timestamp.toLocaleString() : timestamp.toLocaleTimeString(); + renderedTimestamp = _react.createElement( + 'div', + { className: 'console-record-timestamp' }, + timestampLabel ); } - return ( - - {/* $FlowFixMe(>=0.53.0) Flow suppress */} -
- {icon} -
- {displayableRecord.record.repeatCount > 1 && ( -
- {displayableRecord.record.repeatCount} -
- )} -
- {this._renderContent(displayableRecord)} -
-
- {sourceLabel} - {renderedTimestamp} -
-
+ return _react.createElement( + (_MeasuredComponent || _load_MeasuredComponent()).MeasuredComponent, + { + onMeasurementsChanged: this._debouncedMeasureAndNotifyHeight }, + _react.createElement( + 'div', + { ref: this._handleRecordWrapper, className: classNames }, + icon, + _react.createElement( + 'div', + { className: 'console-record-content-wrapper' }, + displayableRecord.record.repeatCount > 1 && _react.createElement( + 'div', + { className: 'console-record-duplicate-number' }, + displayableRecord.record.repeatCount + ), + _react.createElement( + 'div', + { className: 'console-record-content' }, + this._renderContent(displayableRecord) + ) + ), + sourceLabel, + renderedTimestamp + ) ); } - measureAndNotifyHeight = () => { - // This method is called after the necessary DOM mutations have - // already occurred, however it is possible that the updates have - // not been flushed to the screen. So the height change update - // is deferred until the rendering is complete so that - // this._wrapper.offsetHeight gives us the correct final height - if (this._rafDisposable != null) { - this._rafDisposable.unsubscribe(); - } - this._rafDisposable = nextAnimationFrame.subscribe(() => { - if (this._wrapper == null) { - return; - } - const {offsetHeight} = this._wrapper; - const {displayableRecord, onHeightChange} = this.props; - if (offsetHeight !== displayableRecord.height) { - onHeightChange(displayableRecord.id, offsetHeight); - } - }); - }; - - _handleRecordWrapper = (wrapper: HTMLElement) => { - this._wrapper = wrapper; - }; } -function getComponent(type: ?string): React.ComponentType { +exports.default = RecordView; +function getComponent(type) { switch (type) { case 'text': - return props => TextRenderer(props.evaluationResult); + return props => (0, (_TextRenderer || _load_TextRenderer()).TextRenderer)(props.evaluationResult); case 'boolean': case 'string': case 'number': case 'object': default: - return SimpleValueComponent; + return (_SimpleValueComponent || _load_SimpleValueComponent()).default; } } -function getHighlightClassName(level: Level): string { +function getHighlightClassName(level) { switch (level) { case 'info': return 'highlight-info'; @@ -220,7 +266,7 @@ function getHighlightClassName(level: Level): string { } } -function getIconName(record: Record): ?string { +function getIconName(record) { switch (record.kind) { case 'request': return 'chevron-right'; @@ -245,7 +291,7 @@ function getIconName(record: Record): ?string { * that only they can know (e.g. relative paths output in BUCK messages). For now, however, we'll * just use some pattern settings and hardcode the patterns we care about. */ -function parseText(text: string): Array> { +function parseText(text) { const chunks = []; let lastIndex = 0; let index = 0; @@ -258,25 +304,19 @@ function parseText(text: string): Array> { const matchedText = match[0]; // Add all the text since our last match. - chunks.push( - text.slice(lastIndex, CLICKABLE_RE.lastIndex - matchedText.length), - ); + chunks.push(text.slice(lastIndex, CLICKABLE_RE.lastIndex - matchedText.length)); lastIndex = CLICKABLE_RE.lastIndex; let href; if (match[1] != null) { // It's a diff - const url = toString( - featureConfig.get('atom-ide-console.diffUrlPattern'), - ); + const url = toString((_featureConfig || _load_featureConfig()).default.get('atom-ide-console.diffUrlPattern')); if (url !== '') { href = url.replace('%s', matchedText); } } else if (match[2] != null) { // It's a task - const url = toString( - featureConfig.get('atom-ide-console.taskUrlPattern'), - ); + const url = toString((_featureConfig || _load_featureConfig()).default.get('atom-ide-console.taskUrlPattern')); if (url !== '') { href = url.replace('%s', matchedText.slice(1)); } @@ -286,15 +326,12 @@ function parseText(text: string): Array> { } chunks.push( - // flowlint-next-line sketchy-null-string:off - href ? ( - - {matchedText} - - ) : ( - matchedText - ), - ); + // flowlint-next-line sketchy-null-string:off + href ? _react.createElement( + 'a', + { key: `r${index}`, href: href, target: '_blank' }, + matchedText + ) : matchedText); index++; } @@ -307,11 +344,9 @@ function parseText(text: string): Array> { const DIFF_PATTERN = '\\b[dD][1-9][0-9]{5,}\\b'; const TASK_PATTERN = '\\b[tT]\\d+\\b'; -const CLICKABLE_PATTERNS = `(${DIFF_PATTERN})|(${TASK_PATTERN})|${ - URL_REGEX.source -}`; +const CLICKABLE_PATTERNS = `(${DIFF_PATTERN})|(${TASK_PATTERN})|${(_string || _load_string()).URL_REGEX.source}`; const CLICKABLE_RE = new RegExp(CLICKABLE_PATTERNS, 'g'); -function toString(value: mixed): string { +function toString(value) { return typeof value === 'string' ? value : ''; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/spec/Epics-spec.js b/modules/atom-ide-ui/pkg/atom-ide-console/spec/Epics-spec.js index 2a6f1143..bea024db 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/spec/Epics-spec.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/spec/Epics-spec.js @@ -1,59 +1,74 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {AppState} from '../lib/types'; - -import {ActionsObservable} from 'nuclide-commons/redux-observable'; -import * as Actions from '../lib/redux/Actions'; -import * as Epics from '../lib/redux/Epics'; -import invariant from 'assert'; -import {Observable} from 'rxjs'; +'use strict'; + +var _reduxObservable; + +function _load_reduxObservable() { + return _reduxObservable = require('nuclide-commons/redux-observable'); +} + +var _Actions; + +function _load_Actions() { + return _Actions = _interopRequireWildcard(require('../lib/redux/Actions')); +} + +var _Epics; + +function _load_Epics() { + return _Epics = _interopRequireWildcard(require('../lib/redux/Epics')); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } describe('Epics', () => { describe('registerOutputProviderEpic', () => { it('observes the status', () => { const mockStore = { dispatch: () => {}, - getState: () => (({}: any): AppState), + getState: () => ({}) }; let setStatus; const provider = { id: 'test', - messages: Observable.never(), + messages: _rxjsBundlesRxMinJs.Observable.never(), observeStatus: cb => { setStatus = cb; }, start: () => {}, - stop: () => {}, + stop: () => {} }; - const actions = new ActionsObservable( - Observable.of(Actions.registerOutputProvider(provider)), - ); + const actions = new (_reduxObservable || _load_reduxObservable()).ActionsObservable(_rxjsBundlesRxMinJs.Observable.of((_Actions || _load_Actions()).registerOutputProvider(provider))); let results = []; - Epics.registerRecordProviderEpic(actions, mockStore).subscribe( - results.push.bind(results), - ); - invariant(setStatus != null); + (_Epics || _load_Epics()).registerRecordProviderEpic(actions, mockStore).subscribe(results.push.bind(results)); + + if (!(setStatus != null)) { + throw new Error('Invariant violation: "setStatus != null"'); + } + setStatus('running'); setStatus('stopped'); setStatus('running'); - results = results.filter(action => action.type === Actions.UPDATE_STATUS); + results = results.filter(action => action.type === (_Actions || _load_Actions()).UPDATE_STATUS); expect(results.length).toBe(3); - expect( - results.map(action => { - invariant(action.type === Actions.UPDATE_STATUS); - return action.payload.status; - }), - ).toEqual(['running', 'stopped', 'running']); + expect(results.map(action => { + if (!(action.type === (_Actions || _load_Actions()).UPDATE_STATUS)) { + throw new Error('Invariant violation: "action.type === Actions.UPDATE_STATUS"'); + } + + return action.payload.status; + })).toEqual(['running', 'stopped', 'running']); }); }); -}); +}); /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/spec/Reducers-spec.js b/modules/atom-ide-ui/pkg/atom-ide-console/spec/Reducers-spec.js index febf34f8..6781dd50 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/spec/Reducers-spec.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/spec/Reducers-spec.js @@ -1,21 +1,33 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {Action, Executor} from '../lib/types'; - -import * as Actions from '../lib/redux/Actions'; -import Reducers from '../lib/redux/Reducers'; -import * as Immutable from 'immutable'; -import {Observable} from 'rxjs'; +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.createDummyExecutor = createDummyExecutor; + +var _Actions; + +function _load_Actions() { + return _Actions = _interopRequireWildcard(require('../lib/redux/Actions')); +} + +var _Reducers; + +function _load_Reducers() { + return _Reducers = _interopRequireDefault(require('../lib/redux/Reducers')); +} + +var _immutable; + +function _load_immutable() { + return _immutable = _interopRequireWildcard(require('immutable')); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } const emptyAppState = { createPasteFunction: null, @@ -25,9 +37,19 @@ const emptyAppState = { providers: new Map(), providerStatuses: new Map(), providerSubscriptions: new Map(), - records: Immutable.List(), - history: [], -}; + records: (_immutable || _load_immutable()).List(), + history: [] +}; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ describe('createStateStream', () => { describe('RECORD_RECEIVED', () => { @@ -35,28 +57,24 @@ describe('createStateStream', () => { let initialRecords; beforeEach(() => { - initialRecords = Immutable.List(); - const initialState = { - ...emptyAppState, + initialRecords = (_immutable || _load_immutable()).List(); + const initialState = Object.assign({}, emptyAppState, { maxMessageCount: 2, - records: initialRecords, - }; + records: initialRecords + }); const actions = []; for (let i = 0; i < 5; i++) { actions.push({ - type: Actions.RECORD_RECEIVED, + type: (_Actions || _load_Actions()).RECORD_RECEIVED, payload: { record: { level: 'info', - text: i.toString(), - }, - }, + text: i.toString() + } + } }); } - finalState = ((actions: any): Array).reduce( - Reducers, - initialState, - ); + finalState = actions.reduce((_Reducers || _load_Reducers()).default, initialState); }); it('adds records', () => { @@ -68,10 +86,7 @@ describe('createStateStream', () => { }); it('truncates the least recent records', () => { - expect(finalState.records.map(record => record.text).toArray()).toEqual([ - '3', - '4', - ]); + expect(finalState.records.map(record => record.text).toArray()).toEqual(['3', '4']); }); it("doesn't mutate the original records list", () => { @@ -85,25 +100,19 @@ describe('createStateStream', () => { beforeEach(() => { initialProviders = new Map(); - const initialState = { - ...emptyAppState, - providers: initialProviders, - }; - const actions = [ - { - type: Actions.REGISTER_SOURCE, - payload: { - source: { - id: 'test', - records: Observable.empty(), - }, - }, - }, - ]; - finalState = ((actions: any): Array).reduce( - Reducers, - initialState, - ); + const initialState = Object.assign({}, emptyAppState, { + providers: initialProviders + }); + const actions = [{ + type: (_Actions || _load_Actions()).REGISTER_SOURCE, + payload: { + source: { + id: 'test', + records: _rxjsBundlesRxMinJs.Observable.empty() + } + } + }]; + finalState = actions.reduce((_Reducers || _load_Reducers()).default, initialState); }); it('adds providers to the registry', () => { @@ -120,24 +129,21 @@ describe('createStateStream', () => { let finalState; beforeEach(() => { - initialRecords = Immutable.List([ - { - kind: 'message', - sourceId: 'Test', - level: 'info', - text: 'test', - scopeName: null, - timestamp: new Date('2017-01-01T12:34:56.789Z'), - data: null, - repeatCount: 1, - }, - ]); - const initialState = { - ...emptyAppState, - records: initialRecords, - }; - const actions = [{type: Actions.CLEAR_RECORDS}]; - finalState = actions.reduce(Reducers, initialState); + initialRecords = (_immutable || _load_immutable()).List([{ + kind: 'message', + sourceId: 'Test', + level: 'info', + text: 'test', + scopeName: null, + timestamp: new Date('2017-01-01T12:34:56.789Z'), + data: null, + repeatCount: 1 + }]); + const initialState = Object.assign({}, emptyAppState, { + records: initialRecords + }); + const actions = [{ type: (_Actions || _load_Actions()).CLEAR_RECORDS }]; + finalState = actions.reduce((_Reducers || _load_Reducers()).default, initialState); }); it('clears the records', () => { @@ -158,23 +164,20 @@ describe('createStateStream', () => { beforeEach(() => { dummyExecutor = createDummyExecutor('a'); initialExecutors = new Map([['a', dummyExecutor]]); - initialState = { - ...emptyAppState, - executors: initialExecutors, - }; + initialState = Object.assign({}, emptyAppState, { + executors: initialExecutors + }); }); describe('REGISTER_EXECUTOR', () => { beforeEach(() => { - const actions = [ - { - type: Actions.REGISTER_EXECUTOR, - payload: { - executor: createDummyExecutor('b'), - }, - }, - ]; - finalState = actions.reduce(Reducers, initialState); + const actions = [{ + type: (_Actions || _load_Actions()).REGISTER_EXECUTOR, + payload: { + executor: createDummyExecutor('b') + } + }]; + finalState = actions.reduce((_Reducers || _load_Reducers()).default, initialState); }); it('adds an executor', () => { @@ -188,8 +191,8 @@ describe('createStateStream', () => { describe('unregisterExecutor', () => { beforeEach(() => { - const actions = [Actions.unregisterExecutor(dummyExecutor)]; - finalState = actions.reduce(Reducers, initialState); + const actions = [(_Actions || _load_Actions()).unregisterExecutor(dummyExecutor)]; + finalState = actions.reduce((_Reducers || _load_Reducers()).default, initialState); }); it('removes an executor', () => { @@ -203,12 +206,12 @@ describe('createStateStream', () => { }); }); -export function createDummyExecutor(id: string): Executor { +function createDummyExecutor(id) { return { id, name: id, scopeName: 'text.plain', - send: (code: string) => {}, - output: Observable.create(observer => {}), + send: code => {}, + output: _rxjsBundlesRxMinJs.Observable.create(observer => {}) }; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-console/spec/getCurrentExecutorId-spec.js b/modules/atom-ide-ui/pkg/atom-ide-console/spec/getCurrentExecutorId-spec.js index fb406fad..c31e3fad 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-console/spec/getCurrentExecutorId-spec.js +++ b/modules/atom-ide-ui/pkg/atom-ide-console/spec/getCurrentExecutorId-spec.js @@ -1,40 +1,57 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import getCurrentExecutorId from '../lib/getCurrentExecutorId'; -import * as Immutable from 'immutable'; -import {createDummyExecutor} from './Reducers-spec'; +'use strict'; + +var _getCurrentExecutorId; + +function _load_getCurrentExecutorId() { + return _getCurrentExecutorId = _interopRequireDefault(require('../lib/getCurrentExecutorId')); +} + +var _immutable; + +function _load_immutable() { + return _immutable = _interopRequireWildcard(require('immutable')); +} + +var _ReducersSpec; + +function _load_ReducersSpec() { + return _ReducersSpec = require('./Reducers-spec'); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const baseAppState = { createPasteFunction: null, currentExecutorId: 'a', maxMessageCount: Number.POSITIVE_INFINITY, - executors: new Map([['a', createDummyExecutor('a')]]), + executors: new Map([['a', (0, (_ReducersSpec || _load_ReducersSpec()).createDummyExecutor)('a')]]), providers: new Map(), providerStatuses: new Map(), - records: Immutable.List(), - history: [], -}; + records: (_immutable || _load_immutable()).List(), + history: [] +}; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ describe('getCurrentExecutorId', () => { it('gets the current executor', () => { - expect(getCurrentExecutorId(baseAppState)).toBe('a'); + expect((0, (_getCurrentExecutorId || _load_getCurrentExecutorId()).default)(baseAppState)).toBe('a'); }); it('returns an executor even if the current id is null', () => { - const appState = { - ...baseAppState, - currentExecutorId: null, - }; - expect(getCurrentExecutorId(appState)).toBe('a'); + const appState = Object.assign({}, baseAppState, { + currentExecutorId: null + }); + expect((0, (_getCurrentExecutorId || _load_getCurrentExecutorId()).default)(appState)).toBe('a'); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipComponent.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipComponent.js index 2d420476..81d7a3b8 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipComponent.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipComponent.js @@ -1,81 +1,99 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {Datatip} from './types'; - -import * as React from 'react'; - -import {maybeToString} from 'nuclide-commons/string'; -import MarkedStringDatatip from './MarkedStringDatatip'; - -export const DATATIP_ACTIONS = Object.freeze({ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DatatipComponent = exports.DATATIP_ACTIONS = undefined; + +var _react = _interopRequireWildcard(require('react')); + +var _string; + +function _load_string() { + return _string = require('nuclide-commons/string'); +} + +var _MarkedStringDatatip; + +function _load_MarkedStringDatatip() { + return _MarkedStringDatatip = _interopRequireDefault(require('./MarkedStringDatatip')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +const DATATIP_ACTIONS = exports.DATATIP_ACTIONS = Object.freeze({ PIN: 'PIN', - CLOSE: 'CLOSE', + CLOSE: 'CLOSE' }); const IconsForAction = { [DATATIP_ACTIONS.PIN]: 'pin', - [DATATIP_ACTIONS.CLOSE]: 'x', + [DATATIP_ACTIONS.CLOSE]: 'x' }; -type DatatipComponentProps = { - action: string, - actionTitle: string, - className?: string, - datatip: Datatip, - onActionClick: Function, -}; +class DatatipComponent extends _react.Component { + constructor(...args) { + var _temp; -export class DatatipComponent extends React.Component { - handleActionClick = (event: SyntheticEvent<>) => { - this.props.onActionClick(); - }; + return _temp = super(...args), this.handleActionClick = event => { + this.props.onActionClick(); + }, _temp; + } - render(): React.Node { - const { + render() { + const _props = this.props, + { className, action, actionTitle, datatip, - onActionClick, - ...props - } = this.props; + onActionClick + } = _props, + props = _objectWithoutProperties(_props, ['className', 'action', 'actionTitle', 'datatip', 'onActionClick']); let content; if (datatip.component != null) { - content = ; + content = _react.createElement(datatip.component, null); } else if (datatip.markedStrings != null) { - content = ; + content = _react.createElement((_MarkedStringDatatip || _load_MarkedStringDatatip()).default, { markedStrings: datatip.markedStrings }); } let actionButton = null; if (action != null && IconsForAction[action] != null) { const actionIcon = IconsForAction[action]; - actionButton = ( -
- ); + actionButton = _react.createElement('div', { + className: `datatip-pin-button icon-${actionIcon}`, + onClick: this.handleActionClick, + title: actionTitle + }); } - return ( -
-
{content}
- {actionButton} -
+ return _react.createElement( + 'div', + Object.assign({ + className: `${(0, (_string || _load_string()).maybeToString)(className)} datatip-container` + }, props), + _react.createElement( + 'div', + { className: 'datatip-content' }, + content + ), + actionButton ); } } +exports.DatatipComponent = DatatipComponent; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipManager.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipManager.js index c31ccd79..f195ef52 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipManager.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/DatatipManager.js @@ -1,72 +1,183 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -/* global performance */ +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DatatipManager = undefined; -import type { - AnyDatatipProvider, - Datatip, - DatatipProvider, - ModifierDatatipProvider, - ModifierKey, - PinnedDatatipOptions, -} from './types'; - -import {Range} from 'atom'; -import {asyncFind} from 'nuclide-commons/promise'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import analytics from 'nuclide-commons-atom/analytics'; -import debounce from 'nuclide-commons/debounce'; -import featureConfig from 'nuclide-commons-atom/feature-config'; -import idx from 'idx'; -import performanceNow from 'nuclide-commons/performanceNow'; -import {Observable} from 'rxjs'; -import {getLogger} from 'log4js'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import {observeTextEditors} from 'nuclide-commons-atom/text-editor'; -import { - getModifierKeysFromMouseEvent, - getModifierKeyFromKeyboardEvent, -} from './getModifierKeys'; - -import {DatatipComponent, DATATIP_ACTIONS} from './DatatipComponent'; -import isScrollable from './isScrollable'; -import {PinnedDatatip} from './PinnedDatatip'; - -const DEFAULT_DATATIP_DEBOUNCE_DELAY = 1000; -const DEFAULT_DATATIP_INTERACTED_DEBOUNCE_DELAY = 1000; +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); -type PinClickHandler = (editor: atom$TextEditor, datatip: Datatip) => void; +let getDatatipResults = (() => { + var _ref5 = (0, _asyncToGenerator.default)(function* (providers, editor, position, invoke) { + const filteredDatatipProviders = Array.from(providers.getAllProvidersForEditor(editor)); + if (filteredDatatipProviders.length === 0) { + return []; + } -type DatatipResult = { - datatip: Datatip, - provider: AnyDatatipProvider, -}; + const promises = filteredDatatipProviders.map((() => { + var _ref6 = (0, _asyncToGenerator.default)(function* (provider) { + const name = getProviderName(provider); + const timingTracker = new (_analytics || _load_analytics()).default.TimingTracker(name + '.datatip'); + try { + const datatip = yield invoke(provider); + if (!datatip) { + return null; + } + + timingTracker.onSuccess(); + + const result = { + datatip, + provider + }; + return result; + } catch (e) { + timingTracker.onError(e); + (0, (_log4js || _load_log4js()).getLogger)('datatip').error(`Error getting datatip from provider ${name}`, e); + return null; + } + }); + + return function (_x5) { + return _ref6.apply(this, arguments); + }; + })()); + if ((_featureConfig || _load_featureConfig()).default.get('atom-ide-datatip.onlyTopDatatip')) { + const result = yield (0, (_promise || _load_promise()).asyncFind)(promises, function (x) { + return x; + }); + return result != null ? [result] : []; + } else { + return (yield Promise.all(promises)).filter(Boolean); + } + }); + + return function getDatatipResults(_x, _x2, _x3, _x4) { + return _ref5.apply(this, arguments); + }; +})(); + +var _atom = require('atom'); + +var _promise; + +function _load_promise() { + return _promise = require('nuclide-commons/promise'); +} + +var _react = _interopRequireWildcard(require('react')); + +var _reactDom = _interopRequireDefault(require('react-dom')); + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _analytics; + +function _load_analytics() { + return _analytics = _interopRequireDefault(require('nuclide-commons-atom/analytics')); +} + +var _debounce; + +function _load_debounce() { + return _debounce = _interopRequireDefault(require('nuclide-commons/debounce')); +} + +var _featureConfig; + +function _load_featureConfig() { + return _featureConfig = _interopRequireDefault(require('nuclide-commons-atom/feature-config')); +} + +var _idx; + +function _load_idx() { + return _idx = _interopRequireDefault(require('idx')); +} + +var _performanceNow; + +function _load_performanceNow() { + return _performanceNow = _interopRequireDefault(require('nuclide-commons/performanceNow')); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _log4js; + +function _load_log4js() { + return _log4js = require('log4js'); +} + +var _ProviderRegistry; + +function _load_ProviderRegistry() { + return _ProviderRegistry = _interopRequireDefault(require('nuclide-commons-atom/ProviderRegistry')); +} + +var _textEditor; + +function _load_textEditor() { + return _textEditor = require('nuclide-commons-atom/text-editor'); +} -function getProviderName(provider: AnyDatatipProvider): string { +var _getModifierKeys; + +function _load_getModifierKeys() { + return _getModifierKeys = require('./getModifierKeys'); +} + +var _DatatipComponent; + +function _load_DatatipComponent() { + return _DatatipComponent = require('./DatatipComponent'); +} + +var _isScrollable; + +function _load_isScrollable() { + return _isScrollable = _interopRequireDefault(require('./isScrollable')); +} + +var _PinnedDatatip; + +function _load_PinnedDatatip() { + return _PinnedDatatip = require('./PinnedDatatip'); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const DEFAULT_DATATIP_DEBOUNCE_DELAY = 1000; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +/* global performance */ + +const DEFAULT_DATATIP_INTERACTED_DEBOUNCE_DELAY = 1000; + +function getProviderName(provider) { if (provider.providerName == null) { - getLogger('datatip').error('Datatip provider has no name', provider); + (0, (_log4js || _load_log4js()).getLogger)('datatip').error('Datatip provider has no name', provider); return 'unknown'; } return provider.providerName; } -function getBufferPosition( - editor: TextEditor, - editorView: atom$TextEditorElement, - event: ?MouseEvent, -): null | atom$Point { +function getBufferPosition(editor, editorView, event) { if (!event) { return null; } @@ -78,152 +189,81 @@ function getBufferPosition( const screenPosition = text.screenPositionForMouseEvent(event); const pixelPosition = text.pixelPositionForMouseEvent(event); - const pixelPositionFromScreenPosition = text.pixelPositionForScreenPosition( - screenPosition, - ); + const pixelPositionFromScreenPosition = text.pixelPositionForScreenPosition(screenPosition); // Distance (in pixels) between screenPosition and the cursor. - const horizontalDistance = - pixelPosition.left - pixelPositionFromScreenPosition.left; + const horizontalDistance = pixelPosition.left - pixelPositionFromScreenPosition.left; // `screenPositionForMouseEvent.column` cannot exceed the current line length. // This is essentially a heuristic for "mouse cursor is to the left or right // of text content". - if ( - pixelPosition.left <= 0 || - horizontalDistance > editor.getDefaultCharWidth() - ) { + if (pixelPosition.left <= 0 || horizontalDistance > editor.getDefaultCharWidth()) { return null; } return editor.bufferPositionForScreenPosition(screenPosition); } -async function getDatatipResults( - providers: ProviderRegistry, - editor: atom$TextEditor, - position: atom$Point, - invoke: TProvider => Promise, -): Promise> { - const filteredDatatipProviders = Array.from( - providers.getAllProvidersForEditor(editor), - ); - if (filteredDatatipProviders.length === 0) { - return []; - } - - const promises = filteredDatatipProviders.map( - async (provider: TProvider): Promise => { - const name = getProviderName(provider); - const timingTracker = new analytics.TimingTracker(name + '.datatip'); - try { - const datatip: ?Datatip = await invoke(provider); - if (!datatip) { - return null; - } - - timingTracker.onSuccess(); - - const result: DatatipResult = { - datatip, - provider, - }; - return result; - } catch (e) { - timingTracker.onError(e); - getLogger('datatip').error( - `Error getting datatip from provider ${name}`, - e, - ); - return null; - } - }, - ); - if (featureConfig.get('atom-ide-datatip.onlyTopDatatip')) { - const result = await asyncFind(promises, x => x); - return result != null ? [result] : []; - } else { - return (await Promise.all(promises)).filter(Boolean); - } -} - -type PinnableDatatipProps = { - datatip: Datatip, - editor: atom$TextEditor, - onPinClick: PinClickHandler, -}; - function PinnableDatatip({ datatip, editor, - onPinClick, -}: PinnableDatatipProps): React.Element { + onPinClick +}) { let action; let actionTitle; // Datatips are pinnable by default, unless explicitly specified // otherwise. if (datatip.pinnable !== false) { - action = DATATIP_ACTIONS.PIN; + action = (_DatatipComponent || _load_DatatipComponent()).DATATIP_ACTIONS.PIN; actionTitle = 'Pin this Datatip'; } return ( // $FlowFixMe(>=0.53.0) Flow suppress - onPinClick(editor, datatip)} - /> + _react.createElement((_DatatipComponent || _load_DatatipComponent()).DatatipComponent, { + action: action, + actionTitle: actionTitle, + datatip: datatip, + onActionClick: () => onPinClick(editor, datatip) + }) ); } -function mountDatatipWithMarker( - editor: atom$TextEditor, - element: HTMLElement, - range: atom$Range, - renderedProviders: React.Element, - position: atom$Point, -): IDisposable { +function mountDatatipWithMarker(editor, element, range, renderedProviders, position) { // Highlight the text indicated by the datatip's range. const highlightMarker = editor.markBufferRange(range, { - invalidate: 'never', + invalidate: 'never' }); editor.decorateMarker(highlightMarker, { type: 'highlight', - class: 'datatip-highlight-region', + class: 'datatip-highlight-region' }); // The actual datatip should appear at the trigger position. - const overlayMarker = editor.markBufferRange(new Range(position, position), { - invalidate: 'never', + const overlayMarker = editor.markBufferRange(new _atom.Range(position, position), { + invalidate: 'never' }); editor.decorateMarker(overlayMarker, { type: 'overlay', position: 'tail', - item: element, + item: element }); - return new UniversalDisposable( - () => highlightMarker.destroy(), - () => overlayMarker.destroy(), - // The editor may not mount the marker until the next update. - // It's not safe to render anything until that point, as datatips - // often need to measure their size in the DOM. - Observable.from(editor.getElement().getNextUpdatePromise()).subscribe( - () => { - element.style.display = 'block'; - ReactDOM.render(renderedProviders, element); - }, - ), - ); + return new (_UniversalDisposable || _load_UniversalDisposable()).default(() => highlightMarker.destroy(), () => overlayMarker.destroy(), + // The editor may not mount the marker until the next update. + // It's not safe to render anything until that point, as datatips + // often need to measure their size in the DOM. + _rxjsBundlesRxMinJs.Observable.from(editor.getElement().getNextUpdatePromise()).subscribe(() => { + element.style.display = 'block'; + _reactDom.default.render(renderedProviders, element); + })); } const DatatipState = Object.freeze({ HIDDEN: 'HIDDEN', FETCHING: 'FETCHING', - VISIBLE: 'VISIBLE', + VISIBLE: 'VISIBLE' }); -type State = $Keys; -function ensurePositiveNumber(value: any, defaultValue: number): number { + +function ensurePositiveNumber(value, defaultValue) { if (typeof value !== 'number' || value < 0) { return defaultValue; } @@ -231,40 +271,14 @@ function ensurePositiveNumber(value: any, defaultValue: number): number { } class DatatipManagerForEditor { - _blacklistedPosition: ?atom$Point; - _datatipElement: HTMLElement; - _datatipProviders: ProviderRegistry; - _modifierDatatipProviders: ProviderRegistry; - _datatipState: State; - _editor: atom$TextEditor; - _editorView: atom$TextEditorElement; - _insideDatatip: boolean; - _lastHiddenTime: number; - _lastFetchedFromCursorPosition: boolean; - _lastMoveEvent: ?MouseEvent; - _lastPosition: ?atom$Point; - _lastResultsPromise: ?Promise>; - _heldKeys: Set; - _markerDisposable: ?IDisposable; - _pinnedDatatips: Set; - _range: ?atom$Range; - _shouldDropNextMouseMoveAfterFocus: boolean; - _startFetchingDebounce: () => void; - _hideIfOutsideDebounce: () => void; - _subscriptions: UniversalDisposable; - _interactedWith: boolean; - _checkedScrollable: boolean; - _isScrollable: boolean; - - constructor( - editor: atom$TextEditor, - datatipProviders: ProviderRegistry, - modifierDatatipProviders: ProviderRegistry, - ) { + + constructor(editor, datatipProviders, modifierDatatipProviders) { + _initialiseProps.call(this); + this._editor = editor; this._editorView = atom.views.getView(editor); this._pinnedDatatips = new Set(); - this._subscriptions = new UniversalDisposable(); + this._subscriptions = new (_UniversalDisposable || _load_UniversalDisposable()).default(); this._datatipProviders = datatipProviders; this._modifierDatatipProviders = modifierDatatipProviders; this._datatipElement = document.createElement('div'); @@ -278,179 +292,126 @@ class DatatipManagerForEditor { this._lastFetchedFromCursorPosition = false; this._shouldDropNextMouseMoveAfterFocus = false; - this._subscriptions.add( - featureConfig.observe('atom-ide-datatip.datatipDebounceDelay', () => - this._setStartFetchingDebounce(), - ), - featureConfig.observe( - 'atom-ide-datatip.datatipInteractedWithDebounceDelay', - () => this._setHideIfOutsideDebounce(), - ), - Observable.fromEvent(this._editorView, 'focus').subscribe(e => { - this._shouldDropNextMouseMoveAfterFocus = true; - if (!this._insideDatatip) { - this._setState(DatatipState.HIDDEN); - } - }), - Observable.fromEvent(this._editorView, 'blur').subscribe(e => { - if (!this._insideDatatip) { - this._setState(DatatipState.HIDDEN); - } - }), - Observable.fromEvent(this._editorView, 'mousemove').subscribe(e => { - this._lastFetchedFromCursorPosition = false; - if (this._shouldDropNextMouseMoveAfterFocus) { - this._shouldDropNextMouseMoveAfterFocus = false; - return; - } + this._subscriptions.add((_featureConfig || _load_featureConfig()).default.observe('atom-ide-datatip.datatipDebounceDelay', () => this._setStartFetchingDebounce()), (_featureConfig || _load_featureConfig()).default.observe('atom-ide-datatip.datatipInteractedWithDebounceDelay', () => this._setHideIfOutsideDebounce()), _rxjsBundlesRxMinJs.Observable.fromEvent(this._editorView, 'focus').subscribe(e => { + this._shouldDropNextMouseMoveAfterFocus = true; + if (!this._insideDatatip) { + this._setState(DatatipState.HIDDEN); + } + }), _rxjsBundlesRxMinJs.Observable.fromEvent(this._editorView, 'blur').subscribe(e => { + if (!this._insideDatatip) { + this._setState(DatatipState.HIDDEN); + } + }), _rxjsBundlesRxMinJs.Observable.fromEvent(this._editorView, 'mousemove').subscribe(e => { + this._lastFetchedFromCursorPosition = false; + if (this._shouldDropNextMouseMoveAfterFocus) { + this._shouldDropNextMouseMoveAfterFocus = false; + return; + } - this._lastMoveEvent = e; - this._heldKeys = getModifierKeysFromMouseEvent(e); - if (this._datatipState === DatatipState.HIDDEN) { - this._startFetchingDebounce(); - } else { - this._hideIfOutside(); - } - }), - Observable.fromEvent(this._editorView, 'mouseleave').subscribe(() => { - this._lastMoveEvent = null; + this._lastMoveEvent = e; + this._heldKeys = (0, (_getModifierKeys || _load_getModifierKeys()).getModifierKeysFromMouseEvent)(e); + if (this._datatipState === DatatipState.HIDDEN) { + this._startFetchingDebounce(); + } else { this._hideIfOutside(); - }), - Observable.fromEvent(this._editorView, 'mousedown').subscribe(e => { - let node = e.target; - while (node != null) { - if (node === this._datatipElement) { - return; - } - node = node.parentNode; + } + }), _rxjsBundlesRxMinJs.Observable.fromEvent(this._editorView, 'mouseleave').subscribe(() => { + this._lastMoveEvent = null; + this._hideIfOutside(); + }), _rxjsBundlesRxMinJs.Observable.fromEvent(this._editorView, 'mousedown').subscribe(e => { + let node = e.target; + while (node != null) { + if (node === this._datatipElement) { + return; } + node = node.parentNode; + } - this._hideOrCancel(); - }), - Observable.fromEvent(this._editorView, 'keydown').subscribe(e => { - const modifierKey = getModifierKeyFromKeyboardEvent(e); - if (modifierKey) { - // On Windows, key repeat applies to modifier keys too! - // So it's quite possible that we hit this twice without hitting keyup. - if (this._heldKeys.has(modifierKey)) { - return; - } - this._heldKeys.add(modifierKey); - if (this._datatipState !== DatatipState.HIDDEN) { - this._fetchInResponseToKeyPress(); - } - } else { - this._hideOrCancel(); - } - }), - Observable.fromEvent(this._editorView, 'keyup').subscribe(e => { - const modifierKey = getModifierKeyFromKeyboardEvent(e); - if (modifierKey) { - this._heldKeys.delete(modifierKey); - if (this._datatipState !== DatatipState.HIDDEN) { - this._fetchInResponseToKeyPress(); - } + this._hideOrCancel(); + }), _rxjsBundlesRxMinJs.Observable.fromEvent(this._editorView, 'keydown').subscribe(e => { + const modifierKey = (0, (_getModifierKeys || _load_getModifierKeys()).getModifierKeyFromKeyboardEvent)(e); + if (modifierKey) { + // On Windows, key repeat applies to modifier keys too! + // So it's quite possible that we hit this twice without hitting keyup. + if (this._heldKeys.has(modifierKey)) { + return; } - }), - Observable.fromEvent(this._datatipElement, 'wheel').subscribe(e => { - // We'll mark this as an 'interaction' only if the scroll target was scrollable. - // This requires going over the ancestors, so only check this once. - // If it comes back as false, we won't bother checking again. - if (!this._checkedScrollable) { - this._isScrollable = isScrollable(this._datatipElement, e); - this._checkedScrollable = true; + this._heldKeys.add(modifierKey); + if (this._datatipState !== DatatipState.HIDDEN) { + this._fetchInResponseToKeyPress(); } - if (this._isScrollable) { - this._interactedWith = true; - e.stopPropagation(); + } else { + this._hideOrCancel(); + } + }), _rxjsBundlesRxMinJs.Observable.fromEvent(this._editorView, 'keyup').subscribe(e => { + const modifierKey = (0, (_getModifierKeys || _load_getModifierKeys()).getModifierKeyFromKeyboardEvent)(e); + if (modifierKey) { + this._heldKeys.delete(modifierKey); + if (this._datatipState !== DatatipState.HIDDEN) { + this._fetchInResponseToKeyPress(); } - }), - Observable.fromEvent(this._datatipElement, 'mousedown').subscribe(() => { + } + }), _rxjsBundlesRxMinJs.Observable.fromEvent(this._datatipElement, 'wheel').subscribe(e => { + // We'll mark this as an 'interaction' only if the scroll target was scrollable. + // This requires going over the ancestors, so only check this once. + // If it comes back as false, we won't bother checking again. + if (!this._checkedScrollable) { + this._isScrollable = (0, (_isScrollable || _load_isScrollable()).default)(this._datatipElement, e); + this._checkedScrollable = true; + } + if (this._isScrollable) { this._interactedWith = true; - }), - Observable.fromEvent(this._datatipElement, 'mouseenter').subscribe(() => { - this._insideDatatip = true; - this._hideIfOutside(); - }), - Observable.fromEvent(this._datatipElement, 'mouseleave').subscribe(() => { - this._insideDatatip = false; - this._hideIfOutside(); - }), - this._editorView.onDidChangeScrollTop(() => { - this._lastMoveEvent = null; - if (this._datatipState === DatatipState.VISIBLE) { - this._setState(DatatipState.HIDDEN); - } - }), - this._editor.getBuffer().onDidChangeText(() => { - if (this._datatipState === DatatipState.VISIBLE) { - this._setState(DatatipState.HIDDEN); - } - }), - atom.commands.add( - 'atom-text-editor', - 'datatip:toggle', - this._toggleDatatip, - ), - atom.commands.add( - 'atom-text-editor', - 'datatip:copy-to-clipboard', - this._copyDatatipToClipboard, - ), - ); + e.stopPropagation(); + } + }), _rxjsBundlesRxMinJs.Observable.fromEvent(this._datatipElement, 'mousedown').subscribe(() => { + this._interactedWith = true; + }), _rxjsBundlesRxMinJs.Observable.fromEvent(this._datatipElement, 'mouseenter').subscribe(() => { + this._insideDatatip = true; + this._hideIfOutside(); + }), _rxjsBundlesRxMinJs.Observable.fromEvent(this._datatipElement, 'mouseleave').subscribe(() => { + this._insideDatatip = false; + this._hideIfOutside(); + }), this._editorView.onDidChangeScrollTop(() => { + this._lastMoveEvent = null; + if (this._datatipState === DatatipState.VISIBLE) { + this._setState(DatatipState.HIDDEN); + } + }), this._editor.getBuffer().onDidChangeText(() => { + if (this._datatipState === DatatipState.VISIBLE) { + this._setState(DatatipState.HIDDEN); + } + }), atom.commands.add('atom-text-editor', 'datatip:toggle', this._toggleDatatip), atom.commands.add('atom-text-editor', 'datatip:copy-to-clipboard', this._copyDatatipToClipboard)); } _fetchInResponseToKeyPress() { if (this._lastFetchedFromCursorPosition) { this._startFetching(() => this._editor.getCursorBufferPosition()); } else { - this._startFetching(() => - getBufferPosition(this._editor, this._editorView, this._lastMoveEvent), - ); + this._startFetching(() => getBufferPosition(this._editor, this._editorView, this._lastMoveEvent)); } } - _setStartFetchingDebounce(): void { - this._startFetchingDebounce = debounce( - () => { - this._startFetching(() => - getBufferPosition( - this._editor, - this._editorView, - this._lastMoveEvent, - ), - ); - }, - ensurePositiveNumber( - (featureConfig.get('atom-ide-datatip.datatipDebounceDelay'): any), - DEFAULT_DATATIP_DEBOUNCE_DELAY, - ), - /* immediate */ false, - ); + _setStartFetchingDebounce() { + this._startFetchingDebounce = (0, (_debounce || _load_debounce()).default)(() => { + this._startFetching(() => getBufferPosition(this._editor, this._editorView, this._lastMoveEvent)); + }, ensurePositiveNumber((_featureConfig || _load_featureConfig()).default.get('atom-ide-datatip.datatipDebounceDelay'), DEFAULT_DATATIP_DEBOUNCE_DELAY), + /* immediate */false); } - _setHideIfOutsideDebounce(): void { - this._hideIfOutsideDebounce = debounce( - () => { - this._hideIfOutsideImmediate(); - }, - ensurePositiveNumber( - (featureConfig.get( - 'atom-ide-datatip.datatipInteractedWithDebounceDelay', - ): any), - DEFAULT_DATATIP_INTERACTED_DEBOUNCE_DELAY, - ), - /* immediate */ false, - ); + _setHideIfOutsideDebounce() { + this._hideIfOutsideDebounce = (0, (_debounce || _load_debounce()).default)(() => { + this._hideIfOutsideImmediate(); + }, ensurePositiveNumber((_featureConfig || _load_featureConfig()).default.get('atom-ide-datatip.datatipInteractedWithDebounceDelay'), DEFAULT_DATATIP_INTERACTED_DEBOUNCE_DELAY), + /* immediate */false); } - dispose(): void { + dispose() { this._setState(DatatipState.HIDDEN); this._subscriptions.dispose(); this._datatipElement.remove(); } - _setState(newState: State): void { + _setState(newState) { const oldState = this._datatipState; this._datatipState = newState; @@ -462,162 +423,135 @@ class DatatipManagerForEditor { } } - async _startFetching(getPosition: () => ?atom$Point): Promise { - const position = getPosition(); - if (!position) { - return; - } + _startFetching(getPosition) { + var _this = this; - const data = await this._fetchAndRender(position); - if (data == null) { - this._setState(DatatipState.HIDDEN); - return; - } - if (this._datatipState !== DatatipState.FETCHING) { - this._setState(DatatipState.HIDDEN); - } + return (0, _asyncToGenerator.default)(function* () { + const position = getPosition(); + if (!position) { + return; + } - if ( - this._blacklistedPosition && - data.range && - data.range.containsPoint(this._blacklistedPosition) - ) { - this._setState(DatatipState.HIDDEN); - return; - } + const data = yield _this._fetchAndRender(position); + if (data == null) { + _this._setState(DatatipState.HIDDEN); + return; + } + if (_this._datatipState !== DatatipState.FETCHING) { + _this._setState(DatatipState.HIDDEN); + } - const currentPosition = getPosition(); - if ( - !currentPosition || - !data.range || - !data.range.containsPoint(currentPosition) - ) { - this._setState(DatatipState.HIDDEN); - return; - } + if (_this._blacklistedPosition && data.range && data.range.containsPoint(_this._blacklistedPosition)) { + _this._setState(DatatipState.HIDDEN); + return; + } - if (this._isHoveringOverPinnedTip()) { - this._setState(DatatipState.HIDDEN); - return; - } + const currentPosition = getPosition(); + if (!currentPosition || !data.range || !data.range.containsPoint(currentPosition)) { + _this._setState(DatatipState.HIDDEN); + return; + } - this._setState(DatatipState.VISIBLE); - this._interactedWith = false; - this._checkedScrollable = false; - this._range = data.range; + if (_this._isHoveringOverPinnedTip()) { + _this._setState(DatatipState.HIDDEN); + return; + } - if (this._markerDisposable) { - this._markerDisposable.dispose(); - } - this._markerDisposable = mountDatatipWithMarker( - this._editor, - this._datatipElement, - data.range, - data.renderedProviders, - currentPosition, - ); + _this._setState(DatatipState.VISIBLE); + _this._interactedWith = false; + _this._checkedScrollable = false; + _this._range = data.range; + + if (_this._markerDisposable) { + _this._markerDisposable.dispose(); + } + _this._markerDisposable = mountDatatipWithMarker(_this._editor, _this._datatipElement, data.range, data.renderedProviders, currentPosition); + })(); } - async _fetch(position: atom$Point): Promise> { - this._setState(DatatipState.FETCHING); + _fetch(position) { + var _this2 = this; - let results: Promise>; - if ( - this._lastPosition != null && - position.isEqual(this._lastPosition) && - this._lastResultsPromise != null - ) { - results = this._lastResultsPromise; - } else { - this._lastResultsPromise = getDatatipResults( - this._datatipProviders, - this._editor, - position, - provider => provider.datatip(this._editor, position), - ); - results = this._lastResultsPromise; - this._lastPosition = position; - } + return (0, _asyncToGenerator.default)(function* () { + _this2._setState(DatatipState.FETCHING); + + let results; + if (_this2._lastPosition != null && position.isEqual(_this2._lastPosition) && _this2._lastResultsPromise != null) { + results = _this2._lastResultsPromise; + } else { + _this2._lastResultsPromise = getDatatipResults(_this2._datatipProviders, _this2._editor, position, function (provider) { + return provider.datatip(_this2._editor, position); + }); + results = _this2._lastResultsPromise; + _this2._lastPosition = position; + } - return (await results).concat( - await getDatatipResults( - this._modifierDatatipProviders, - this._editor, - position, - provider => - provider.modifierDatatip(this._editor, position, this._heldKeys), - ), - ); + return (yield results).concat((yield getDatatipResults(_this2._modifierDatatipProviders, _this2._editor, position, function (provider) { + return provider.modifierDatatip(_this2._editor, position, _this2._heldKeys); + }))); + })(); } - async _fetchAndRender( - position: atom$Point, - ): Promise, - }> { - const datatipsAndProviders = await this._fetch(position); - if (datatipsAndProviders.length === 0) { - return null; - } + _fetchAndRender(position) { + var _this3 = this; - const range = datatipsAndProviders[0].datatip.range; - analytics.track('datatip-popup', { - scope: this._editor.getGrammar().scopeName, - providerName: getProviderName(datatipsAndProviders[0].provider), - rangeStartRow: String(range.start.row), - rangeStartColumn: String(range.start.column), - rangeEndRow: String(range.end.row), - rangeEndColumn: String(range.end.column), - }); + return (0, _asyncToGenerator.default)(function* () { + const datatipsAndProviders = yield _this3._fetch(position); + if (datatipsAndProviders.length === 0) { + return null; + } + + const range = datatipsAndProviders[0].datatip.range; + (_analytics || _load_analytics()).default.track('datatip-popup', { + scope: _this3._editor.getGrammar().scopeName, + providerName: getProviderName(datatipsAndProviders[0].provider), + rangeStartRow: String(range.start.row), + rangeStartColumn: String(range.start.column), + rangeEndRow: String(range.end.row), + rangeEndColumn: String(range.end.column) + }); + + const renderedProviders = _react.createElement( + 'div', + null, + datatipsAndProviders.map(function ({ datatip, provider }) { + return _react.createElement(PinnableDatatip, { + datatip: datatip, + editor: _this3._editor, + key: getProviderName(provider), + onPinClick: _this3._handlePinClicked + }); + }) + ); - const renderedProviders = ( -
- {datatipsAndProviders.map(({datatip, provider}) => ( - - ))} -
- ); - - return { - range, - renderedProviders, - }; + return { + range, + renderedProviders + }; + })(); } - _isHoveringOverPinnedTip(): boolean { + _isHoveringOverPinnedTip() { const pinnedDataTips = Array.from(this._pinnedDatatips.values()); const hoveringTips = pinnedDataTips.filter(dt => dt.isHovering()); return hoveringTips != null && hoveringTips.length > 0; } - _hideDatatip(): void { + _hideDatatip() { this._lastHiddenTime = performance.now(); if (this._markerDisposable) { this._markerDisposable.dispose(); this._markerDisposable = null; } this._range = null; - ReactDOM.unmountComponentAtNode(this._datatipElement); + _reactDom.default.unmountComponentAtNode(this._datatipElement); this._datatipElement.style.display = 'none'; } - _hideOrCancel(): void { - if ( - this._datatipState === DatatipState.HIDDEN || - this._datatipState === DatatipState.FETCHING - ) { + _hideOrCancel() { + if (this._datatipState === DatatipState.HIDDEN || this._datatipState === DatatipState.FETCHING) { if (this._blacklistedPosition == null) { - this._blacklistedPosition = getBufferPosition( - this._editor, - this._editorView, - this._lastMoveEvent, - ); + this._blacklistedPosition = getBufferPosition(this._editor, this._editorView, this._lastMoveEvent); } return; } @@ -625,7 +559,7 @@ class DatatipManagerForEditor { this._setState(DatatipState.HIDDEN); } - _hideIfOutside(): void { + _hideIfOutside() { if (this._datatipState !== DatatipState.VISIBLE) { return; } @@ -637,7 +571,7 @@ class DatatipManagerForEditor { } } - _hideIfOutsideImmediate(): void { + _hideIfOutsideImmediate() { if (this._datatipState !== DatatipState.VISIBLE) { return; } @@ -650,60 +584,52 @@ class DatatipManagerForEditor { return; } - const currentPosition = getBufferPosition( - this._editor, - this._editorView, - this._lastMoveEvent, - ); - if ( - currentPosition && - this._range && - this._range.containsPoint(currentPosition) - ) { + const currentPosition = getBufferPosition(this._editor, this._editorView, this._lastMoveEvent); + if (currentPosition && this._range && this._range.containsPoint(currentPosition)) { return; } this._setState(DatatipState.HIDDEN); } - createPinnedDataTip( - datatip: Datatip, - editor: TextEditor, - options?: PinnedDatatipOptions, - ): PinnedDatatip { - const pinnedDatatip = new PinnedDatatip(datatip, editor, { - ...options, + createPinnedDataTip(datatip, editor, options) { + const pinnedDatatip = new (_PinnedDatatip || _load_PinnedDatatip()).PinnedDatatip(datatip, editor, Object.assign({}, options, { onDispose: () => { this._pinnedDatatips.delete(pinnedDatatip); }, hideDataTips: () => { this._hideDatatip(); - }, - }); + } + })); return pinnedDatatip; } - _handlePinClicked = (editor: TextEditor, datatip: Datatip) => { - analytics.track('datatip-pinned-open'); - const startTime = performanceNow(); +} + +var _initialiseProps = function () { + var _this4 = this; + + this._handlePinClicked = (editor, datatip) => { + (_analytics || _load_analytics()).default.track('datatip-pinned-open'); + const startTime = (0, (_performanceNow || _load_performanceNow()).default)(); this._setState(DatatipState.HIDDEN); - this._pinnedDatatips.add( - new PinnedDatatip(datatip, editor, { - onDispose: pinnedDatatip => { - this._pinnedDatatips.delete(pinnedDatatip); - analytics.track('datatip-pinned-close', { - duration: performanceNow() - startTime, - }); - }, - hideDataTips: () => { - this._hideDatatip(); - }, - position: 'end-of-line', - }), - ); + this._pinnedDatatips.add(new (_PinnedDatatip || _load_PinnedDatatip()).PinnedDatatip(datatip, editor, { + onDispose: pinnedDatatip => { + this._pinnedDatatips.delete(pinnedDatatip); + (_analytics || _load_analytics()).default.track('datatip-pinned-close', { + duration: (0, (_performanceNow || _load_performanceNow()).default)() - startTime + }); + }, + hideDataTips: () => { + this._hideDatatip(); + }, + position: 'end-of-line' + })); }; - _toggleDatatip = (e?: atom$CustomEvent) => { + this._toggleDatatip = e => { + var _ref, _ref2; + if (atom.workspace.getActiveTextEditor() !== this._editor) { return; } @@ -712,24 +638,19 @@ class DatatipManagerForEditor { // keydown, which is going to be triggered before the key binding which is // evaluated on keyup. // $FlowFixMe (v0.54.1 <) - const maybeEventType = idx(e, _ => _.originalEvent.type); + const maybeEventType = (_ref = e) != null ? (_ref2 = _ref.originalEvent) != null ? _ref2.type : _ref2 : _ref; // Unfortunately, when you do keydown of the shortcut, it's going to // hide it, we need to make sure that when we do keyup, it doesn't show // it up right away. We assume that a keypress is done within 100ms // and don't show it again if it was hidden so soon. - const forceShow = - maybeEventType === 'keydown' && - performance.now() - this._lastHiddenTime > 100; + const forceShow = maybeEventType === 'keydown' && performance.now() - this._lastHiddenTime > 100; const forceHide = maybeEventType === 'keyup'; - const forceToggle = - maybeEventType !== 'keydown' && maybeEventType !== 'keyup'; + const forceToggle = maybeEventType !== 'keydown' && maybeEventType !== 'keyup'; if ( - // if we have event information, prefer that for determining show/hide - forceShow || - (forceToggle && this._datatipState === DatatipState.HIDDEN) - ) { + // if we have event information, prefer that for determining show/hide + forceShow || forceToggle && this._datatipState === DatatipState.HIDDEN) { this._lastFetchedFromCursorPosition = true; this._startFetching(() => this._editor.getCursorScreenPosition()); } else if (forceHide || forceToggle) { @@ -737,19 +658,21 @@ class DatatipManagerForEditor { } }; - _copyDatatipToClipboard = async () => { - if (atom.workspace.getActiveTextEditor() !== this._editor) { + this._copyDatatipToClipboard = (0, _asyncToGenerator.default)(function* () { + var _ref3, _ref4; + + if (atom.workspace.getActiveTextEditor() !== _this4._editor) { return; } - const pos = this._editor.getCursorScreenPosition(); + const pos = _this4._editor.getCursorScreenPosition(); if (pos == null) { return; } - const results: Array = await this._fetch(pos); - this._setState(DatatipState.HIDDEN); + const results = yield _this4._fetch(pos); + _this4._setState(DatatipState.HIDDEN); - const tip = idx(results, _ => _[0].datatip); + const tip = (_ref3 = results) != null ? (_ref4 = _ref3[0]) != null ? _ref4.datatip : _ref4 : _ref3; if (tip == null || tip.markedStrings == null) { return; } @@ -759,72 +682,55 @@ class DatatipManagerForEditor { return; } - const value = markedStrings.map(string => string.value).join(); + const value = markedStrings.map(function (string) { + return string.value; + }).join(); if (value === '') { return; } atom.clipboard.write(value); - atom.notifications.addInfo( - `Copied data tip to clipboard: \`\`\`${value}\`\`\``, - ); - }; -} + atom.notifications.addInfo(`Copied data tip to clipboard: \`\`\`${value}\`\`\``); + }); +}; -export class DatatipManager { - _datatipProviders: ProviderRegistry; - _modifierDatatipProviders: ProviderRegistry; - _editorManagers: Map; - _subscriptions: UniversalDisposable; +class DatatipManager { constructor() { - this._subscriptions = new UniversalDisposable(); + this._subscriptions = new (_UniversalDisposable || _load_UniversalDisposable()).default(); this._editorManagers = new Map(); - this._datatipProviders = new ProviderRegistry(); - this._modifierDatatipProviders = new ProviderRegistry(); - - this._subscriptions.add( - observeTextEditors(editor => { - const manager = new DatatipManagerForEditor( - editor, - this._datatipProviders, - this._modifierDatatipProviders, - ); - this._editorManagers.set(editor, manager); - const disposable = new UniversalDisposable(() => { - manager.dispose(); - this._editorManagers.delete(editor); - }); - this._subscriptions.add(disposable); - editor.onDidDestroy(() => disposable.dispose()); - }), - ); + this._datatipProviders = new (_ProviderRegistry || _load_ProviderRegistry()).default(); + this._modifierDatatipProviders = new (_ProviderRegistry || _load_ProviderRegistry()).default(); + + this._subscriptions.add((0, (_textEditor || _load_textEditor()).observeTextEditors)(editor => { + const manager = new DatatipManagerForEditor(editor, this._datatipProviders, this._modifierDatatipProviders); + this._editorManagers.set(editor, manager); + const disposable = new (_UniversalDisposable || _load_UniversalDisposable()).default(() => { + manager.dispose(); + this._editorManagers.delete(editor); + }); + this._subscriptions.add(disposable); + editor.onDidDestroy(() => disposable.dispose()); + })); } - addProvider(provider: DatatipProvider): IDisposable { + addProvider(provider) { return this._datatipProviders.addProvider(provider); } - addModifierProvider(provider: ModifierDatatipProvider): IDisposable { + addModifierProvider(provider) { return this._modifierDatatipProviders.addProvider(provider); } - createPinnedDataTip( - datatip: Datatip, - editor: TextEditor, - options?: PinnedDatatipOptions, - ): PinnedDatatip { + createPinnedDataTip(datatip, editor, options) { const manager = this._editorManagers.get(editor); if (!manager) { - throw new Error( - 'Trying to create a pinned data tip on an editor that has ' + - 'no datatip manager', - ); + throw new Error('Trying to create a pinned data tip on an editor that has ' + 'no datatip manager'); } return manager.createPinnedDataTip(datatip, editor, options); } - dispose(): void { + dispose() { this._subscriptions.dispose(); this._editorManagers.forEach(manager => { manager.dispose(); @@ -832,3 +738,4 @@ export class DatatipManager { this._editorManagers = new Map(); } } +exports.DatatipManager = DatatipManager; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringDatatip.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringDatatip.js index 315ecd90..655a3735 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringDatatip.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringDatatip.js @@ -1,3 +1,27 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _marked; + +function _load_marked() { + return _marked = _interopRequireDefault(require('marked')); +} + +var _react = _interopRequireWildcard(require('react')); + +var _MarkedStringSnippet; + +function _load_MarkedStringSnippet() { + return _MarkedStringSnippet = _interopRequireDefault(require('./MarkedStringSnippet')); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,39 +30,31 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {MarkedString} from './types'; - -import marked from 'marked'; -import * as React from 'react'; - -import MarkedStringSnippet from './MarkedStringSnippet'; - -type Props = { - markedStrings: Array, -}; - -export default class MarkedStringDatatip extends React.PureComponent { - render(): React.Node { +class MarkedStringDatatip extends _react.PureComponent { + render() { const elements = this.props.markedStrings.map((chunk, i) => { if (chunk.type === 'markdown') { - return ( -
- ); + return _react.createElement('div', { + className: 'datatip-marked-container', + dangerouslySetInnerHTML: { + __html: (0, (_marked || _load_marked()).default)(chunk.value, { sanitize: true }) + }, + key: i + }); } else { - return ; + return _react.createElement((_MarkedStringSnippet || _load_MarkedStringSnippet()).default, Object.assign({ key: i }, chunk)); } }); - return
{elements}
; + return _react.createElement( + 'div', + { className: 'datatip-marked' }, + elements + ); } } +exports.default = MarkedStringDatatip; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringSnippet.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringSnippet.js index 4337d8e3..778f615c 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringSnippet.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/MarkedStringSnippet.js @@ -1,59 +1,65 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import {TextBuffer} from 'atom'; -import * as React from 'react'; -import {AtomTextEditor} from 'nuclide-commons-ui/AtomTextEditor'; +'use strict'; -// Complex types can end up being super long. Truncate them. -const MAX_LENGTH = 100; +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _atom = require('atom'); + +var _react = _interopRequireWildcard(require('react')); + +var _AtomTextEditor; + +function _load_AtomTextEditor() { + return _AtomTextEditor = require('nuclide-commons-ui/AtomTextEditor'); +} -type Props = { - value: string, - grammar: atom$Grammar, -}; +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } -type State = { - isExpanded: boolean, -}; +// Complex types can end up being super long. Truncate them. +const MAX_LENGTH = 100; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ -export default class MarkedStringSnippet extends React.Component { - state = { - isExpanded: false, - }; +class MarkedStringSnippet extends _react.Component { + constructor(...args) { + var _temp; - render(): React.Node { - const {grammar, value} = this.props; + return _temp = super(...args), this.state = { + isExpanded: false + }, _temp; + } + + render() { + const { grammar, value } = this.props; const shouldTruncate = value.length > MAX_LENGTH && !this.state.isExpanded; - const buffer = new TextBuffer( - shouldTruncate ? value.substr(0, MAX_LENGTH) + '...' : value, - ); - return ( -
) => { - this.setState({isExpanded: !this.state.isExpanded}); + const buffer = new _atom.TextBuffer(shouldTruncate ? value.substr(0, MAX_LENGTH) + '...' : value); + return _react.createElement( + 'div', + { + className: 'datatip-marked-text-editor-container', + onClick: e => { + this.setState({ isExpanded: !this.state.isExpanded }); e.stopPropagation(); - }}> - -
+ } }, + _react.createElement((_AtomTextEditor || _load_AtomTextEditor()).AtomTextEditor, { + className: 'datatip-marked-text-editor', + gutterHidden: true, + readOnly: true, + syncTextContents: false, + autoGrow: true, + grammar: grammar, + textBuffer: buffer + }) ); } } +exports.default = MarkedStringSnippet; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/PinnedDatatip.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/PinnedDatatip.js index a0681ba0..9158861a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/PinnedDatatip.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/PinnedDatatip.js @@ -1,94 +1,79 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {Datatip, PinnedDatatipPosition} from './types'; - -type Position = { - x: number, - y: number, -}; - -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import {Observable} from 'rxjs'; -import invariant from 'assert'; -import classnames from 'classnames'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -import {DatatipComponent, DATATIP_ACTIONS} from './DatatipComponent'; -import isScrollable from './isScrollable'; - -const LINE_END_MARGIN = 20; +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.PinnedDatatip = undefined; + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +var _react = _interopRequireWildcard(require('react')); + +var _reactDom = _interopRequireDefault(require('react-dom')); + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _classnames; + +function _load_classnames() { + return _classnames = _interopRequireDefault(require('classnames')); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _DatatipComponent; + +function _load_DatatipComponent() { + return _DatatipComponent = require('./DatatipComponent'); +} + +var _isScrollable; + +function _load_isScrollable() { + return _isScrollable = _interopRequireDefault(require('./isScrollable')); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const LINE_END_MARGIN = 20; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ let _mouseMove$; -function documentMouseMove$(): Observable { +function documentMouseMove$() { if (_mouseMove$ == null) { - _mouseMove$ = Observable.fromEvent(document, 'mousemove'); + _mouseMove$ = _rxjsBundlesRxMinJs.Observable.fromEvent(document, 'mousemove'); } return _mouseMove$; } let _mouseUp$; -function documentMouseUp$(): Observable { +function documentMouseUp$() { if (_mouseUp$ == null) { - _mouseUp$ = Observable.fromEvent(document, 'mouseup'); + _mouseUp$ = _rxjsBundlesRxMinJs.Observable.fromEvent(document, 'mouseup'); } return _mouseUp$; } -export type PinnedDatatipParams = { - onDispose: (pinnedDatatip: PinnedDatatip) => void, - hideDataTips: () => void, - // Defaults to 'end-of-line'. - position?: PinnedDatatipPosition, - // Defaults to true. - showRangeHighlight?: boolean, -}; - -export class PinnedDatatip { - _boundDispose: Function; - _boundHandleMouseDown: Function; - _boundHandleMouseEnter: Function; - _boundHandleMouseLeave: Function; - _boundHandleCapturedClick: Function; - _mouseUpTimeout: ?TimeoutID; - _hostElement: HTMLElement; - _marker: ?atom$Marker; - _rangeDecoration: ?atom$Decoration; - _mouseSubscription: ?rxjs$ISubscription; - _subscriptions: UniversalDisposable; - _datatip: Datatip; - _editor: TextEditor; - _hostElement: HTMLElement; - _boundDispose: Function; - _dragOrigin: ?Position; - _isDragging: boolean; - _offset: Position; - _isHovering: boolean; - _checkedScrollable: boolean; - _isScrollable: boolean; - _hideDataTips: () => void; - _position: PinnedDatatipPosition; - _showRangeHighlight: boolean; - - constructor( - datatip: Datatip, - editor: TextEditor, - params: PinnedDatatipParams, - ) { - this._subscriptions = new UniversalDisposable(); - this._subscriptions.add( - new UniversalDisposable(() => params.onDispose(this)), - ); +class PinnedDatatip { + + constructor(datatip, editor, params) { + this._subscriptions = new (_UniversalDisposable || _load_UniversalDisposable()).default(); + this._subscriptions.add(new (_UniversalDisposable || _load_UniversalDisposable()).default(() => params.onDispose(this))); this._datatip = datatip; this._editor = editor; this._marker = null; @@ -103,75 +88,62 @@ export class PinnedDatatip { this._checkedScrollable = false; this._isScrollable = false; - this._subscriptions.add( - Observable.fromEvent(this._hostElement, 'wheel').subscribe(e => { - if (!this._checkedScrollable) { - this._isScrollable = isScrollable(this._hostElement, e); - this._checkedScrollable = true; - } - if (this._isScrollable) { - e.stopPropagation(); - } - }), - ); - this._hostElement.addEventListener( - 'mouseenter', - this._boundHandleMouseEnter, - ); - this._hostElement.addEventListener( - 'mouseleave', - this._boundHandleMouseLeave, - ); - this._subscriptions.add( - new UniversalDisposable(() => { - this._hostElement.removeEventListener( - 'mouseenter', - this._boundHandleMouseEnter, - ); - this._hostElement.removeEventListener( - 'mouseleave', - this._boundHandleMouseLeave, - ); - }), - ); + this._subscriptions.add(_rxjsBundlesRxMinJs.Observable.fromEvent(this._hostElement, 'wheel').subscribe(e => { + if (!this._checkedScrollable) { + this._isScrollable = (0, (_isScrollable || _load_isScrollable()).default)(this._hostElement, e); + this._checkedScrollable = true; + } + if (this._isScrollable) { + e.stopPropagation(); + } + })); + this._hostElement.addEventListener('mouseenter', this._boundHandleMouseEnter); + this._hostElement.addEventListener('mouseleave', this._boundHandleMouseLeave); + this._subscriptions.add(new (_UniversalDisposable || _load_UniversalDisposable()).default(() => { + this._hostElement.removeEventListener('mouseenter', this._boundHandleMouseEnter); + this._hostElement.removeEventListener('mouseleave', this._boundHandleMouseLeave); + })); this._mouseUpTimeout = null; - this._offset = {x: 0, y: 0}; + this._offset = { x: 0, y: 0 }; this._isDragging = false; this._dragOrigin = null; this._isHovering = false; this._hideDataTips = params.hideDataTips; this._position = params.position == null ? 'end-of-line' : params.position; - this._showRangeHighlight = - params.showRangeHighlight == null ? true : params.showRangeHighlight; + this._showRangeHighlight = params.showRangeHighlight == null ? true : params.showRangeHighlight; this.render(); } - handleMouseEnter(event: MouseEvent): void { + handleMouseEnter(event) { this._isHovering = true; this._hideDataTips(); } - handleMouseLeave(event: MouseEvent): void { + handleMouseLeave(event) { this._isHovering = false; } - isHovering(): boolean { + isHovering() { return this._isHovering; } - handleGlobalMouseMove(event: Event): void { - const evt: MouseEvent = (event: any); - const {_dragOrigin} = this; - invariant(_dragOrigin); + handleGlobalMouseMove(event) { + const evt = event; + const { _dragOrigin } = this; + + if (!_dragOrigin) { + throw new Error('Invariant violation: "_dragOrigin"'); + } + this._isDragging = true; this._offset = { x: evt.clientX - _dragOrigin.x, - y: evt.clientY - _dragOrigin.y, + y: evt.clientY - _dragOrigin.y }; this.render(); } - handleGlobalMouseUp(): void { + handleGlobalMouseUp() { // If the datatip was moved, push the effects of mouseUp to the next tick, // in order to allow cancellation of captured events (e.g. clicks on child components). this._mouseUpTimeout = setTimeout(() => { @@ -183,34 +155,28 @@ export class PinnedDatatip { }, 0); } - _ensureMouseSubscriptionDisposed(): void { + _ensureMouseSubscriptionDisposed() { if (this._mouseSubscription != null) { this._mouseSubscription.unsubscribe(); this._mouseSubscription = null; } } - handleMouseDown(event: Event): void { - const evt: MouseEvent = (event: any); + handleMouseDown(event) { + const evt = event; this._dragOrigin = { x: evt.clientX - this._offset.x, - y: evt.clientY - this._offset.y, + y: evt.clientY - this._offset.y }; this._ensureMouseSubscriptionDisposed(); - this._mouseSubscription = documentMouseMove$() - .takeUntil(documentMouseUp$()) - .subscribe( - (e: MouseEvent) => { - this.handleGlobalMouseMove(e); - }, - (error: any) => {}, - () => { - this.handleGlobalMouseUp(); - }, - ); + this._mouseSubscription = documentMouseMove$().takeUntil(documentMouseUp$()).subscribe(e => { + this.handleGlobalMouseMove(e); + }, error => {}, () => { + this.handleGlobalMouseUp(); + }); } - handleCapturedClick(event: SyntheticEvent<>): void { + handleCapturedClick(event) { if (this._isDragging) { event.stopPropagation(); } else { @@ -220,92 +186,80 @@ export class PinnedDatatip { } // Update the position of the pinned datatip. - _updateHostElementPosition(): void { - const {_editor, _datatip, _hostElement, _offset, _position} = this; - const {range} = _datatip; + _updateHostElementPosition() { + const { _editor, _datatip, _hostElement, _offset, _position } = this; + const { range } = _datatip; _hostElement.style.display = 'block'; switch (_position) { case 'end-of-line': const charWidth = _editor.getDefaultCharWidth(); - const lineLength = _editor.getBuffer().getLines()[range.start.row] - .length; - _hostElement.style.top = - -_editor.getLineHeightInPixels() + _offset.y + 'px'; - _hostElement.style.left = - (lineLength - range.end.column) * charWidth + - LINE_END_MARGIN + - _offset.x + - 'px'; + const lineLength = _editor.getBuffer().getLines()[range.start.row].length; + _hostElement.style.top = -_editor.getLineHeightInPixels() + _offset.y + 'px'; + _hostElement.style.left = (lineLength - range.end.column) * charWidth + LINE_END_MARGIN + _offset.x + 'px'; break; case 'above-range': - _hostElement.style.bottom = - _editor.getLineHeightInPixels() + - _hostElement.clientHeight - - _offset.y + - 'px'; + _hostElement.style.bottom = _editor.getLineHeightInPixels() + _hostElement.clientHeight - _offset.y + 'px'; _hostElement.style.left = _offset.x + 'px'; break; default: - (_position: empty); + _position; throw Error(`Unexpected PinnedDatatip position: ${this._position}`); } } - async render(): Promise { - const {_editor, _datatip, _hostElement, _isDragging, _isHovering} = this; + render() { + var _this = this; - let rangeClassname = 'datatip-highlight-region'; - if (_isHovering) { - rangeClassname += ' datatip-highlight-region-active'; - } + return (0, _asyncToGenerator.default)(function* () { + const { _editor, _datatip, _hostElement, _isDragging, _isHovering } = _this; + + let rangeClassname = 'datatip-highlight-region'; + if (_isHovering) { + rangeClassname += ' datatip-highlight-region-active'; + } - if (this._marker == null) { - const marker: atom$Marker = _editor.markBufferRange(_datatip.range, { - invalidate: 'never', - }); - this._marker = marker; - _editor.decorateMarker(marker, { - type: 'overlay', - position: 'head', - item: this._hostElement, - }); - if (this._showRangeHighlight) { - this._rangeDecoration = _editor.decorateMarker(marker, { + if (_this._marker == null) { + const marker = _editor.markBufferRange(_datatip.range, { + invalidate: 'never' + }); + _this._marker = marker; + _editor.decorateMarker(marker, { + type: 'overlay', + position: 'head', + item: _this._hostElement + }); + if (_this._showRangeHighlight) { + _this._rangeDecoration = _editor.decorateMarker(marker, { + type: 'highlight', + class: rangeClassname + }); + } + yield _editor.getElement().getNextUpdatePromise(); + // Guard against disposals during the await. + if (marker.isDestroyed() || _editor.isDestroyed()) { + return; + } + } else if (_this._rangeDecoration != null) { + _this._rangeDecoration.setProperties({ type: 'highlight', - class: rangeClassname, + class: rangeClassname }); } - await _editor.getElement().getNextUpdatePromise(); - // Guard against disposals during the await. - if (marker.isDestroyed() || _editor.isDestroyed()) { - return; - } - } else if (this._rangeDecoration != null) { - this._rangeDecoration.setProperties({ - type: 'highlight', - class: rangeClassname, - }); - } - ReactDOM.render( - , - _hostElement, - ); - this._updateHostElementPosition(); + _reactDom.default.render(_react.createElement((_DatatipComponent || _load_DatatipComponent()).DatatipComponent, { + action: (_DatatipComponent || _load_DatatipComponent()).DATATIP_ACTIONS.CLOSE, + actionTitle: 'Close this datatip', + className: (0, (_classnames || _load_classnames()).default)(_isDragging ? 'datatip-dragging' : '', 'datatip-pinned'), + datatip: _datatip, + onActionClick: _this._boundDispose, + onMouseDown: _this._boundHandleMouseDown, + onClickCapture: _this._boundHandleCapturedClick + }), _hostElement); + _this._updateHostElementPosition(); + })(); } - dispose(): void { + dispose() { if (this._mouseUpTimeout != null) { clearTimeout(this._mouseUpTimeout); } @@ -315,8 +269,9 @@ export class PinnedDatatip { if (this._mouseSubscription != null) { this._mouseSubscription.unsubscribe(); } - ReactDOM.unmountComponentAtNode(this._hostElement); + _reactDom.default.unmountComponentAtNode(this._hostElement); this._hostElement.remove(); this._subscriptions.dispose(); } } +exports.PinnedDatatip = PinnedDatatip; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/getModifierKeys.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/getModifierKeys.js index 0ec9fd8d..a0806027 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/getModifierKeys.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/getModifierKeys.js @@ -1,3 +1,17 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getModifierKeysFromMouseEvent = getModifierKeysFromMouseEvent; +exports.getModifierKeyFromKeyboardEvent = getModifierKeyFromKeyboardEvent; + +var _types; + +function _load_types() { + return _types = require('./types'); +} + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,40 +20,35 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {ModifierKey} from './types'; -import {ModifierKeys} from './types'; - const KEYNAME_TO_PROPERTY = { - Meta: ModifierKeys.META, - Shift: ModifierKeys.SHIFT, - Alt: ModifierKeys.ALT, - Control: ModifierKeys.CTRL, + Meta: (_types || _load_types()).ModifierKeys.META, + Shift: (_types || _load_types()).ModifierKeys.SHIFT, + Alt: (_types || _load_types()).ModifierKeys.ALT, + Control: (_types || _load_types()).ModifierKeys.CTRL }; -export function getModifierKeysFromMouseEvent(e: MouseEvent): Set { - const keys: Set = new Set(); +function getModifierKeysFromMouseEvent(e) { + const keys = new Set(); if (e.metaKey) { - keys.add(ModifierKeys.META); + keys.add((_types || _load_types()).ModifierKeys.META); } if (e.shiftKey) { - keys.add(ModifierKeys.SHIFT); + keys.add((_types || _load_types()).ModifierKeys.SHIFT); } if (e.altKey) { - keys.add(ModifierKeys.ALT); + keys.add((_types || _load_types()).ModifierKeys.ALT); } if (e.ctrlKey) { - keys.add(ModifierKeys.CTRL); + keys.add((_types || _load_types()).ModifierKeys.CTRL); } return keys; } -export function getModifierKeyFromKeyboardEvent( - e: KeyboardEvent, -): ?ModifierKey { +function getModifierKeyFromKeyboardEvent(e) { return KEYNAME_TO_PROPERTY[e.key]; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/isScrollable.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/isScrollable.js index 2e3fcf0a..adc72e09 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/isScrollable.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/isScrollable.js @@ -1,3 +1,9 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = isScrollable; /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,23 +12,17 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -export default function isScrollable( - element: Element, - wheelEvent: WheelEvent, -): boolean { - let node: ?Element = ((wheelEvent.target: any): Element); +function isScrollable(element, wheelEvent) { + let node = wheelEvent.target; while (node != null && node !== element) { - if ( - node.scrollHeight > node.clientHeight || - node.scrollWidth > node.clientWidth - ) { + if (node.scrollHeight > node.clientHeight || node.scrollWidth > node.clientWidth) { return true; } - node = ((node.parentNode: any): Element); + node = node.parentNode; } return false; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/main.js index 3b4b9434..c4bdb806 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/main.js @@ -1,34 +1,42 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {DatatipService} from './types'; - -import createPackage from 'nuclide-commons-atom/createPackage'; -import {DatatipManager} from './DatatipManager'; +'use strict'; + +var _createPackage; + +function _load_createPackage() { + return _createPackage = _interopRequireDefault(require('nuclide-commons-atom/createPackage')); +} + +var _DatatipManager; + +function _load_DatatipManager() { + return _DatatipManager = require('./DatatipManager'); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } class Activation { - _datatipManager: DatatipManager; constructor() { - this._datatipManager = new DatatipManager(); + this._datatipManager = new (_DatatipManager || _load_DatatipManager()).DatatipManager(); } - provideDatatipService(): DatatipService { + provideDatatipService() { return this._datatipManager; } dispose() { this._datatipManager.dispose(); } -} +} /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ -createPackage(module.exports, Activation); +(0, (_createPackage || _load_createPackage()).default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/types.js index aad7d4f8..8f8cebc6 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-datatip/lib/types.js @@ -1,3 +1,8 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,7 +11,7 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ @@ -15,79 +20,11 @@ * You can register providers (which will be triggered on mouseover) or manually * create pinned datatips on-demand. */ -export type DatatipService = { - addProvider(provider: DatatipProvider): IDisposable, - addModifierProvider(provider: ModifierDatatipProvider): IDisposable, - createPinnedDataTip( - datatip: Datatip, - editor: TextEditor, - options?: PinnedDatatipOptions, - ): IDisposable, -}; - -export type PinnedDatatipOptions = {| - // Defaults to 'end-of-line'. - position?: PinnedDatatipPosition, - // Defaults to true. - showRangeHighlight?: boolean, -|}; - -export type PinnedDatatipPosition = 'end-of-line' | 'above-range'; - -export type DatatipProvider = { - priority: number, - grammarScopes?: Array, - // A unique name for the provider to be used for analytics. - // It is recommended that it be the name of the provider's package. - providerName: string, - datatip( - editor: atom$TextEditor, - bufferPosition: atom$Point, - ): Promise, -}; - -export type ModifierDatatipProvider = { - priority: number, - grammarScopes?: Array, - providerName: string, - modifierDatatip( - editor: atom$TextEditor, - bufferPosition: atom$Point, - heldKeys: Set, - ): Promise, -}; - -export type AnyDatatipProvider = DatatipProvider | ModifierDatatipProvider; - -export type Datatip = - | {| - component: React$ComponentType, - range: atom$Range, - pinnable?: boolean, - |} - | {| - markedStrings: Array, - range: atom$Range, - pinnable?: boolean, - |}; - -// Borrowed from the LSP API. -export type MarkedString = - | { - type: 'markdown', - value: string, - } - | { - type: 'snippet', - grammar: atom$Grammar, - value: string, - }; - -export const ModifierKeys = Object.freeze({ +const ModifierKeys = exports.ModifierKeys = Object.freeze({ META: 'metaKey', SHIFT: 'shiftKey', ALT: 'altKey', - CTRL: 'ctrlKey', + CTRL: 'ctrlKey' }); -export type ModifierKey = 'metaKey' | 'shiftKey' | 'altKey' | 'ctrlKey'; +// Borrowed from the LSP API. \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/DefinitionCache.js b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/DefinitionCache.js index 57e6958f..5f05ba7a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/DefinitionCache.js +++ b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/DefinitionCache.js @@ -1,3 +1,34 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +var _range; + +function _load_range() { + return _range = require('nuclide-commons-atom/range'); +} + +var _range2; + +function _load_range2() { + return _range2 = require('nuclide-commons/range'); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// An atom$Range-aware, single-item cache for the common case of requerying +// a definition (such as previewing hyperclick and then jumping to the +// destination). It invalidates whenever the originating editor changes. /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,63 +37,47 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {DefinitionQueryResult} from './types'; -import {wordAtPosition} from 'nuclide-commons-atom/range'; -import {isPositionInRange} from 'nuclide-commons/range'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -// An atom$Range-aware, single-item cache for the common case of requerying -// a definition (such as previewing hyperclick and then jumping to the -// destination). It invalidates whenever the originating editor changes. class DefinitionCache { - _cachedResultEditor: ?atom$TextEditor; - _cachedResultPromise: ?Promise; - _cachedResultRange: ?atom$Range; - _disposables: UniversalDisposable = new UniversalDisposable(); + constructor() { + this._disposables = new (_UniversalDisposable || _load_UniversalDisposable()).default(); + } dispose() { this._disposables.dispose(); } - async get( - editor: atom$TextEditor, - position: atom$Point, - getImpl: () => Promise, - ): Promise { - // queryRange is often a list of one range - if ( - this._cachedResultRange != null && - this._cachedResultEditor === editor && - isPositionInRange(position, this._cachedResultRange) - ) { - return this._cachedResultPromise; - } - - // invalidate whenever the buffer changes - const invalidateAndStopListening = () => { - this._cachedResultEditor = null; - this._cachedResultRange = null; - this._cachedResultRange = null; - this._disposables.remove(editorDisposables); - editorDisposables.dispose(); - }; - const editorDisposables = new UniversalDisposable( - editor.getBuffer().onDidChangeText(invalidateAndStopListening), - editor.onDidDestroy(invalidateAndStopListening), - ); - this._disposables.add(editorDisposables); - - const wordGuess = wordAtPosition(editor, position); - this._cachedResultRange = wordGuess && wordGuess.range; - this._cachedResultEditor = editor; - this._cachedResultPromise = getImpl(); - - return this._cachedResultPromise; + get(editor, position, getImpl) { + var _this = this; + + return (0, _asyncToGenerator.default)(function* () { + // queryRange is often a list of one range + if (_this._cachedResultRange != null && _this._cachedResultEditor === editor && (0, (_range2 || _load_range2()).isPositionInRange)(position, _this._cachedResultRange)) { + return _this._cachedResultPromise; + } + + // invalidate whenever the buffer changes + const invalidateAndStopListening = function () { + _this._cachedResultEditor = null; + _this._cachedResultRange = null; + _this._cachedResultRange = null; + _this._disposables.remove(editorDisposables); + editorDisposables.dispose(); + }; + const editorDisposables = new (_UniversalDisposable || _load_UniversalDisposable()).default(editor.getBuffer().onDidChangeText(invalidateAndStopListening), editor.onDidDestroy(invalidateAndStopListening)); + _this._disposables.add(editorDisposables); + + const wordGuess = (0, (_range || _load_range()).wordAtPosition)(editor, position); + _this._cachedResultRange = wordGuess && wordGuess.range; + _this._cachedResultEditor = editor; + _this._cachedResultPromise = getImpl(); + + return _this._cachedResultPromise; + })(); } } -export default DefinitionCache; +exports.default = DefinitionCache; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/getPreviewDatatipFromDefinitionResult.js b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/getPreviewDatatipFromDefinitionResult.js index 807171ef..c60a210e 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/getPreviewDatatipFromDefinitionResult.js +++ b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/getPreviewDatatipFromDefinitionResult.js @@ -1,97 +1,106 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import analytics from 'nuclide-commons-atom/analytics'; -import {getDefinitionPreview as getLocalFileDefinitionPreview} from 'nuclide-commons/symbol-definition-preview'; - -import React from 'react'; -import type {Datatip} from '../../atom-ide-datatip/lib/types'; - -import type {Definition, DefinitionPreviewProvider} from './types'; - -export default (async function getPreviewDatatipFromDefinition( - range: atom$Range, - definitions: Array, - definitionPreviewProvider: ?DefinitionPreviewProvider, - grammar: atom$Grammar, -): Promise { - if (definitions.length === 1) { - const definition = definitions[0]; - // Some providers (e.g. Flow) return negative positions. - if (definition.position.row < 0) { - return null; - } +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); - const definitionPreview = await getPreview( - definition, - definitionPreviewProvider, - ); +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); - if (definitionPreview == null) { - return null; +let getPreview = (() => { + var _ref2 = (0, _asyncToGenerator.default)(function* (definition, definitionPreviewProvider) { + let getDefinitionPreviewFn; + if (definitionPreviewProvider == null) { + getDefinitionPreviewFn = (_symbolDefinitionPreview || _load_symbolDefinitionPreview()).getDefinitionPreview; + } else { + getDefinitionPreviewFn = definitionPreviewProvider.getDefinitionPreview.bind(definitionPreviewProvider); } - // if mimetype is image return image component with base-64 encoded - // image contents, otherwise use markedStrings - if (definitionPreview.mime.startsWith('image/')) { + return (_analytics || _load_analytics()).default.trackTiming('hyperclickPreview.getDefinitionPreview', function () { + return getDefinitionPreviewFn(definition); + }); + }); + + return function getPreview(_x5, _x6) { + return _ref2.apply(this, arguments); + }; +})(); + +var _analytics; + +function _load_analytics() { + return _analytics = _interopRequireDefault(require('nuclide-commons-atom/analytics')); +} + +var _symbolDefinitionPreview; + +function _load_symbolDefinitionPreview() { + return _symbolDefinitionPreview = require('nuclide-commons/symbol-definition-preview'); +} + +var _react = _interopRequireDefault(require('react')); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +exports.default = (() => { + var _ref = (0, _asyncToGenerator.default)(function* (range, definitions, definitionPreviewProvider, grammar) { + if (definitions.length === 1) { + const definition = definitions[0]; + // Some providers (e.g. Flow) return negative positions. + if (definition.position.row < 0) { + return null; + } + + const definitionPreview = yield getPreview(definition, definitionPreviewProvider); + + if (definitionPreview == null) { + return null; + } + + // if mimetype is image return image component with base-64 encoded + // image contents, otherwise use markedStrings + if (definitionPreview.mime.startsWith('image/')) { + return { + component: function () { + return _react.default.createElement('img', { + src: `data:${definitionPreview.mime};${definitionPreview.encoding},${definitionPreview.contents}` + }); + }, + range + }; + } return { - component: () => ( - - ), - range, - }; - } - return { - markedStrings: [ - { + markedStrings: [{ type: 'snippet', value: definitionPreview.contents, - grammar, - }, - ], - range, - }; - } + grammar + }], + range + }; + } - return { - markedStrings: [ - { + return { + markedStrings: [{ type: 'markdown', value: `${definitions.length} definitions found. Click to jump.`, - grammar, - }, - ], - range, - }; -}); + grammar + }], + range + }; + }); -async function getPreview( - definition: Definition, - definitionPreviewProvider: ?DefinitionPreviewProvider, -) { - let getDefinitionPreviewFn; - if (definitionPreviewProvider == null) { - getDefinitionPreviewFn = getLocalFileDefinitionPreview; - } else { - getDefinitionPreviewFn = definitionPreviewProvider.getDefinitionPreview.bind( - definitionPreviewProvider, - ); + function getPreviewDatatipFromDefinition(_x, _x2, _x3, _x4) { + return _ref.apply(this, arguments); } - return analytics.trackTiming('hyperclickPreview.getDefinitionPreview', () => - getDefinitionPreviewFn(definition), - ); -} + return getPreviewDatatipFromDefinition; +})(); /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/main.js index 3e7cc906..24c6792c 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/main.js @@ -1,3 +1,77 @@ +'use strict'; + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +var _log4js; + +function _load_log4js() { + return _log4js = require('log4js'); +} + +var _atom = require('atom'); + +var _analytics; + +function _load_analytics() { + return _analytics = _interopRequireDefault(require('nuclide-commons-atom/analytics')); +} + +var _createPackage; + +function _load_createPackage() { + return _createPackage = _interopRequireDefault(require('nuclide-commons-atom/createPackage')); +} + +var _featureConfig; + +function _load_featureConfig() { + return _featureConfig = _interopRequireDefault(require('nuclide-commons-atom/feature-config')); +} + +var _range; + +function _load_range() { + return _range = require('nuclide-commons-atom/range'); +} + +var _nuclideUri; + +function _load_nuclideUri() { + return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri')); +} + +var _ProviderRegistry; + +function _load_ProviderRegistry() { + return _ProviderRegistry = _interopRequireDefault(require('nuclide-commons-atom/ProviderRegistry')); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _goToLocation; + +function _load_goToLocation() { + return _goToLocation = require('nuclide-commons-atom/go-to-location'); +} + +var _DefinitionCache; + +function _load_DefinitionCache() { + return _DefinitionCache = _interopRequireDefault(require('./DefinitionCache')); +} + +var _getPreviewDatatipFromDefinitionResult; + +function _load_getPreviewDatatipFromDefinitionResult() { + return _getPreviewDatatipFromDefinitionResult = _interopRequireDefault(require('./getPreviewDatatipFromDefinitionResult')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,222 +80,172 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ // This package provides Hyperclick results for any language which provides a // DefinitionProvider. -import type { - HyperclickProvider, - HyperclickSuggestion, -} from '../../hyperclick/lib/types'; - -import type { - Datatip, - DatatipService, - ModifierDatatipProvider, - ModifierKey, -} from '../../atom-ide-datatip/lib/types'; - -import type { - DefinitionQueryResult, - DefinitionProvider, - DefinitionPreviewProvider, -} from './types'; - -import invariant from 'assert'; -import {getLogger} from 'log4js'; -import {Range} from 'atom'; - -import analytics from 'nuclide-commons-atom/analytics'; -import createPackage from 'nuclide-commons-atom/createPackage'; -import FeatureConfig from 'nuclide-commons-atom/feature-config'; -import {wordAtPosition} from 'nuclide-commons-atom/range'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import ProviderRegistry from 'nuclide-commons-atom/ProviderRegistry'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; - -import DefinitionCache from './DefinitionCache'; -import getPreviewDatatipFromDefinitionResult from './getPreviewDatatipFromDefinitionResult'; - class Activation { - _providers: ProviderRegistry; - _definitionPreviewProvider: ?DefinitionPreviewProvider; - _definitionCache: DefinitionCache; - _disposables: UniversalDisposable; - _triggerKeys: Set; constructor() { - this._providers = new ProviderRegistry(); - this._definitionCache = new DefinitionCache(); + this._providers = new (_ProviderRegistry || _load_ProviderRegistry()).default(); + this._definitionCache = new (_DefinitionCache || _load_DefinitionCache()).default(); this._triggerKeys = new Set(); - this._disposables = new UniversalDisposable( - FeatureConfig.observe( - getPlatformKeys(process.platform), - (newValue: ?string) => { - this._triggerKeys = (new Set( - // flowlint-next-line sketchy-null-string:off - newValue ? newValue.split(',') : null, - ): Set); - }, - ), - ); + this._disposables = new (_UniversalDisposable || _load_UniversalDisposable()).default((_featureConfig || _load_featureConfig()).default.observe(getPlatformKeys(process.platform), newValue => { + this._triggerKeys = new Set( + // flowlint-next-line sketchy-null-string:off + newValue ? newValue.split(',') : null); + })); } dispose() { this._disposables.dispose(); } - async _getDefinition( - editor: atom$TextEditor, - position: atom$Point, - ): Promise { - for (const provider of this._providers.getAllProvidersForEditor(editor)) { - try { - // eslint-disable-next-line no-await-in-loop - const result = await provider.getDefinition(editor, position); - if (result != null) { - if (result.queryRange == null) { - const match = wordAtPosition(editor, position, { - includeNonWordCharacters: false, - }); - result.queryRange = [ - match != null ? match.range : new Range(position, position), - ]; + _getDefinition(editor, position) { + var _this = this; + + return (0, _asyncToGenerator.default)(function* () { + for (const provider of _this._providers.getAllProvidersForEditor(editor)) { + try { + // eslint-disable-next-line no-await-in-loop + const result = yield provider.getDefinition(editor, position); + if (result != null) { + if (result.queryRange == null) { + const match = (0, (_range || _load_range()).wordAtPosition)(editor, position, { + includeNonWordCharacters: false + }); + result.queryRange = [match != null ? match.range : new _atom.Range(position, position)]; + } + return result; } - return result; + } catch (err) { + (0, (_log4js || _load_log4js()).getLogger)('atom-ide-definitions').error(`Error getting definition for ${String(editor.getPath())}`, err); } - } catch (err) { - getLogger('atom-ide-definitions').error( - `Error getting definition for ${String(editor.getPath())}`, - err, - ); } - } - return null; + return null; + })(); } - async getSuggestion( - editor: atom$TextEditor, - position: atom$Point, - ): Promise { - const result = await this._definitionCache.get(editor, position, () => - this._getDefinition(editor, position), - ); + getSuggestion(editor, position) { + var _this2 = this; - if (result == null) { - return null; - } - - const {queryRange, definitions} = result; - invariant(definitions.length > 0); - // queryRange might be null coming out of the provider, but the output - // of _getDefinition has ensured it's not null. - invariant(queryRange != null); - - function createCallback(definition) { - return () => { - goToLocation(definition.path, { - line: definition.position.row, - column: definition.position.column, - }); - }; - } - - function createTitle(definition) { - const filePath = - definition.projectRoot == null - ? definition.path - : nuclideUri.relative(definition.projectRoot, definition.path); - if (definition.name == null) { - // Fall back to just displaying the path:line. - return `${filePath}:${definition.position.row + 1}`; + return (0, _asyncToGenerator.default)(function* () { + const result = yield _this2._definitionCache.get(editor, position, function () { + return _this2._getDefinition(editor, position); + }); + + if (result == null) { + return null; + } + + const { queryRange, definitions } = result; + + if (!(definitions.length > 0)) { + throw new Error('Invariant violation: "definitions.length > 0"'); + } + // queryRange might be null coming out of the provider, but the output + // of _getDefinition has ensured it's not null. + + + if (!(queryRange != null)) { + throw new Error('Invariant violation: "queryRange != null"'); + } + + function createCallback(definition) { + return () => { + (0, (_goToLocation || _load_goToLocation()).goToLocation)(definition.path, { + line: definition.position.row, + column: definition.position.column + }); + }; } - return `${definition.name} (${filePath})`; - } - - if (definitions.length === 1) { - return { - range: queryRange, - callback: createCallback(definitions[0]), - }; - } else { - return { - range: queryRange, - callback: definitions.map(definition => { - return { - title: createTitle(definition), - callback: createCallback(definition), - }; - }), - }; - } + + function createTitle(definition) { + const filePath = definition.projectRoot == null ? definition.path : (_nuclideUri || _load_nuclideUri()).default.relative(definition.projectRoot, definition.path); + if (definition.name == null) { + // Fall back to just displaying the path:line. + return `${filePath}:${definition.position.row + 1}`; + } + return `${definition.name} (${filePath})`; + } + + if (definitions.length === 1) { + return { + range: queryRange, + callback: createCallback(definitions[0]) + }; + } else { + return { + range: queryRange, + callback: definitions.map(function (definition) { + return { + title: createTitle(definition), + callback: createCallback(definition) + }; + }) + }; + } + })(); } - async getPreview( - editor: atom$TextEditor, - position: atom$Point, - heldKeys: Set, - ): Promise { - if ( - !this._triggerKeys || + getPreview(editor, position, heldKeys) { + var _this3 = this; + + return (0, _asyncToGenerator.default)(function* () { + if (!_this3._triggerKeys || // are the required keys held down? - !Array.from(this._triggerKeys).every(key => heldKeys.has(key)) - ) { - return; - } + !Array.from(_this3._triggerKeys).every(function (key) { + return heldKeys.has(key); + })) { + return; + } - const result = await this._getDefinition(editor, position); - if (result == null) { - return null; - } - const queryRange = result.queryRange; - // queryRange might be null coming out of the provider, but the output - // of _getDefinition has ensured it's not null. - invariant(queryRange != null); - - const grammar = editor.getGrammar(); - const previewDatatip = getPreviewDatatipFromDefinitionResult( - queryRange[0], - result.definitions, - this._definitionPreviewProvider, - grammar, - ); - - // flowlint-next-line sketchy-null-mixed:off - if (previewDatatip != null && previewDatatip.markedStrings) { - analytics.track('hyperclick-preview-popup', { - grammar: grammar.name, - definitionCount: result.definitions.length, - }); - } + const result = yield _this3._getDefinition(editor, position); + if (result == null) { + return null; + } + const queryRange = result.queryRange; + // queryRange might be null coming out of the provider, but the output + // of _getDefinition has ensured it's not null. + + if (!(queryRange != null)) { + throw new Error('Invariant violation: "queryRange != null"'); + } + + const grammar = editor.getGrammar(); + const previewDatatip = (0, (_getPreviewDatatipFromDefinitionResult || _load_getPreviewDatatipFromDefinitionResult()).default)(queryRange[0], result.definitions, _this3._definitionPreviewProvider, grammar); + + // flowlint-next-line sketchy-null-mixed:off + if (previewDatatip != null && previewDatatip.markedStrings) { + (_analytics || _load_analytics()).default.track('hyperclick-preview-popup', { + grammar: grammar.name, + definitionCount: result.definitions.length + }); + } - return previewDatatip; + return previewDatatip; + })(); } - consumeDefinitionProvider(provider: DefinitionProvider): IDisposable { + consumeDefinitionProvider(provider) { const disposable = this._providers.addProvider(provider); this._disposables.add(disposable); return disposable; } - consumeDefinitionPreviewProvider(provider: DefinitionPreviewProvider) { + consumeDefinitionPreviewProvider(provider) { this._definitionPreviewProvider = provider; } - consumeDatatipService(service: DatatipService): IDisposable { - const datatipProvider: ModifierDatatipProvider = { + consumeDatatipService(service) { + const datatipProvider = { providerName: 'hyperclick-preview', priority: 1, - modifierDatatip: ( - editor: atom$TextEditor, - bufferPosition: atom$Point, - heldKeys: Set, - ) => this.getPreview(editor, bufferPosition, heldKeys), + modifierDatatip: (editor, bufferPosition, heldKeys) => this.getPreview(editor, bufferPosition, heldKeys) }; const disposable = service.addModifierProvider(datatipProvider); @@ -229,11 +253,11 @@ class Activation { return disposable; } - getHyperclickProvider(): HyperclickProvider { + getHyperclickProvider() { return { priority: 20, providerName: 'atom-ide-definitions', - getSuggestion: (editor, position) => this.getSuggestion(editor, position), + getSuggestion: (editor, position) => this.getSuggestion(editor, position) }; } } @@ -247,4 +271,4 @@ function getPlatformKeys(platform) { return 'hyperclick.linuxTriggerKeys'; } -createPackage(module.exports, Activation); +(0, (_createPackage || _load_createPackage()).default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/types.js index bc79cea7..a726efc4 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-definitions/lib/types.js @@ -1,61 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -export type Definition = { - // Path of the file in which the definition is located. - path: NuclideUri, - // First character of the definition's identifier. - // e.g. "F" in `class Foo {}` - position: atom$Point, - // Optional: the range of the entire definition. - // e.g. "c" to "}" in `class Foo {}` - range?: atom$Range, - // Optional: `name` and `projectRoot` can be provided to display a more human-readable title - // inside of Hyperclick when there are multiple definitions. - name?: string, - // If provided, `projectRoot` will be used to display a relativized version of `path`. - projectRoot?: NuclideUri, - // `language` may be used by consumers to identify the source of definitions. - language: string, -}; - -// Definition queries supply a point. -// The returned queryRange is the range within which the returned definition is valid. -// Typically queryRange spans the containing identifier around the query point. -export type DefinitionQueryResult = { - queryRange: ?Array, - definitions: Array, -}; - -// Provides definitions for a set of language grammars. -export type DefinitionProvider = { - // If there are multiple providers for a given grammar, - // the one with the highest priority will be used. - priority: number, - grammarScopes: Array, - getDefinition: ( - editor: TextEditor, - position: atom$Point, - ) => Promise, -}; - -export type DefinitionPreviewProvider = { - getDefinitionPreview( - definition: Definition, - ): Promise, -}; +'use strict'; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-definitions/spec/DefinitionHyperclick-spec.js b/modules/atom-ide-ui/pkg/atom-ide-definitions/spec/DefinitionHyperclick-spec.js index 86116593..cd99cb4b 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-definitions/spec/DefinitionHyperclick-spec.js +++ b/modules/atom-ide-ui/pkg/atom-ide-definitions/spec/DefinitionHyperclick-spec.js @@ -1,3 +1,17 @@ +'use strict'; + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +var _atom = require('atom'); + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,52 +20,32 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type { - HyperclickProvider, - HyperclickSuggestion, -} from '../../hyperclick/lib/types'; -import type {DefinitionProvider} from '../lib/types'; - -import {Point, Range, TextEditor} from 'atom'; -import invariant from 'assert'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - describe('DefinitionHyperclick', () => { - let provider: ?HyperclickProvider; - const definitionProvider: DefinitionProvider = { + let provider; + const definitionProvider = { priority: 20, name: '', grammarScopes: ['text.plain.null-grammar'], - getDefinition: () => Promise.resolve(null), + getDefinition: () => Promise.resolve(null) }; - let editor: TextEditor; - const position = new Point(0, 0); + let editor; + const position = new _atom.Point(0, 0); let goToLocation; let disposables; beforeEach(() => { atom.packages.activatePackage('atom-ide-definitions'); - editor = new TextEditor(); - - goToLocation = spyOn( - require('nuclide-commons-atom/go-to-location'), - 'goToLocation', - ); - - disposables = new UniversalDisposable( - atom.packages.serviceHub.provide( - 'definitions', - '0.1.0', - definitionProvider, - ), - atom.packages.serviceHub.consume('hyperclick', '0.1.0', x => { - provider = x; - }), - ); + editor = new _atom.TextEditor(); + + goToLocation = spyOn(require('nuclide-commons-atom/go-to-location'), 'goToLocation'); + + disposables = new (_UniversalDisposable || _load_UniversalDisposable()).default(atom.packages.serviceHub.provide('definitions', '0.1.0', definitionProvider), atom.packages.serviceHub.consume('hyperclick', '0.1.0', x => { + provider = x; + })); }); afterEach(() => { @@ -59,112 +53,137 @@ describe('DefinitionHyperclick', () => { }); it('no definition service', () => { - waitsForPromise(async () => { - spyOn(editor, 'getGrammar').andReturn({scopeName: 'blah'}); - invariant(provider != null); - invariant(provider.getSuggestion != null); - const result = await provider.getSuggestion(editor, position); + waitsForPromise((0, _asyncToGenerator.default)(function* () { + spyOn(editor, 'getGrammar').andReturn({ scopeName: 'blah' }); + + if (!(provider != null)) { + throw new Error('Invariant violation: "provider != null"'); + } + + if (!(provider.getSuggestion != null)) { + throw new Error('Invariant violation: "provider.getSuggestion != null"'); + } + + const result = yield provider.getSuggestion(editor, position); expect(result).toBe(null); - }); + })); }); it('no definition', () => { - waitsForPromise(async () => { + waitsForPromise((0, _asyncToGenerator.default)(function* () { const spy = spyOn(definitionProvider, 'getDefinition').andReturn(null); - invariant(provider != null); - invariant(provider.getSuggestion != null); - const result = await provider.getSuggestion(editor, position); + + if (!(provider != null)) { + throw new Error('Invariant violation: "provider != null"'); + } + + if (!(provider.getSuggestion != null)) { + throw new Error('Invariant violation: "provider.getSuggestion != null"'); + } + + const result = yield provider.getSuggestion(editor, position); expect(result).toBe(null); expect(spy).toHaveBeenCalledWith(editor, position); - }); + })); }); it('definition - single', () => { - waitsForPromise(async () => { + waitsForPromise((0, _asyncToGenerator.default)(function* () { const definition = { - queryRange: [new Range(new Point(1, 1), new Point(1, 5))], - definitions: [ - { - path: 'path1', - position: new Point(1, 2), - range: null, - id: 'symbol-name', - name: null, - projectRoot: null, - }, - ], + queryRange: [new _atom.Range(new _atom.Point(1, 1), new _atom.Point(1, 5))], + definitions: [{ + path: 'path1', + position: new _atom.Point(1, 2), + range: null, + id: 'symbol-name', + name: null, + projectRoot: null + }] }; - const spy = spyOn(definitionProvider, 'getDefinition').andReturn( - Promise.resolve(definition), - ); + const spy = spyOn(definitionProvider, 'getDefinition').andReturn(Promise.resolve(definition)); - invariant(provider != null); - invariant(provider.getSuggestion != null); - const result = await provider.getSuggestion(editor, position); + if (!(provider != null)) { + throw new Error('Invariant violation: "provider != null"'); + } + + if (!(provider.getSuggestion != null)) { + throw new Error('Invariant violation: "provider.getSuggestion != null"'); + } + + const result = yield provider.getSuggestion(editor, position); + + if (!(result != null)) { + throw new Error('Invariant violation: "result != null"'); + } - invariant(result != null); expect(result.range).toEqual(definition.queryRange); expect(spy).toHaveBeenCalledWith(editor, position); expect(goToLocation).not.toHaveBeenCalled(); - invariant(result != null); - invariant(result.callback != null); - invariant(typeof result.callback === 'function'); + if (!(result != null)) { + throw new Error('Invariant violation: "result != null"'); + } + + if (!(result.callback != null)) { + throw new Error('Invariant violation: "result.callback != null"'); + } + + if (!(typeof result.callback === 'function')) { + throw new Error('Invariant violation: "typeof result.callback === \'function\'"'); + } + result.callback(); - expect(goToLocation).toHaveBeenCalledWith('path1', {line: 1, column: 2}); - }); + expect(goToLocation).toHaveBeenCalledWith('path1', { line: 1, column: 2 }); + })); }); it('definition - multiple', () => { - waitsForPromise(async () => { + waitsForPromise((0, _asyncToGenerator.default)(function* () { const defs = { - queryRange: [new Range(new Point(1, 1), new Point(1, 5))], - definitions: [ - { - path: '/a/b/path1', - position: new Point(1, 2), - range: null, - id: 'symbol-name', - name: 'd1', - projectRoot: '/a', - }, - { - path: '/a/b/path2', - position: new Point(3, 4), - range: null, - id: 'symbol-name2', - name: 'd2', - projectRoot: '/a', - }, - { - path: '/a/b/path3', - position: new Point(3, 4), - range: null, - id: 'symbol-without-name', - projectRoot: '/a', - }, - ], + queryRange: [new _atom.Range(new _atom.Point(1, 1), new _atom.Point(1, 5))], + definitions: [{ + path: '/a/b/path1', + position: new _atom.Point(1, 2), + range: null, + id: 'symbol-name', + name: 'd1', + projectRoot: '/a' + }, { + path: '/a/b/path2', + position: new _atom.Point(3, 4), + range: null, + id: 'symbol-name2', + name: 'd2', + projectRoot: '/a' + }, { + path: '/a/b/path3', + position: new _atom.Point(3, 4), + range: null, + id: 'symbol-without-name', + projectRoot: '/a' + }] }; - const spy = spyOn(definitionProvider, 'getDefinition').andReturn( - Promise.resolve(defs), - ); - - invariant(provider != null); - invariant(provider.getSuggestion != null); - const result: ?HyperclickSuggestion = await provider.getSuggestion( - editor, - position, - ); - - invariant(result != null); + const spy = spyOn(definitionProvider, 'getDefinition').andReturn(Promise.resolve(defs)); + + if (!(provider != null)) { + throw new Error('Invariant violation: "provider != null"'); + } + + if (!(provider.getSuggestion != null)) { + throw new Error('Invariant violation: "provider.getSuggestion != null"'); + } + + const result = yield provider.getSuggestion(editor, position); + + if (!(result != null)) { + throw new Error('Invariant violation: "result != null"'); + } + expect(result.range).toEqual(defs.queryRange); expect(spy).toHaveBeenCalledWith(editor, position); expect(goToLocation).not.toHaveBeenCalled(); - const callbacks: Array<{ - title: string, - callback: () => mixed, - }> = (result.callback: any); + const callbacks = result.callback; expect(callbacks.length).toBe(3); expect(callbacks[0].title).toBe('d1 (b/path1)'); @@ -177,39 +196,50 @@ describe('DefinitionHyperclick', () => { callbacks[1].callback(); expect(goToLocation).toHaveBeenCalledWith('/a/b/path2', { line: 3, - column: 4, + column: 4 }); - }); + })); }); it('falls back to lower-priority providers', () => { - waitsForPromise(async () => { + waitsForPromise((0, _asyncToGenerator.default)(function* () { const def = { - queryRange: [new Range(new Point(1, 1), new Point(1, 5))], - definitions: [ - { - path: 'path1', - position: new Point(1, 2), - range: null, - id: 'symbol-name', - name: null, - projectRoot: null, - }, - ], + queryRange: [new _atom.Range(new _atom.Point(1, 1), new _atom.Point(1, 5))], + definitions: [{ + path: 'path1', + position: new _atom.Point(1, 2), + range: null, + id: 'symbol-name', + name: null, + projectRoot: null + }] }; const newProvider = { priority: 10, name: '', grammarScopes: ['text.plain.null-grammar'], - getDefinition: () => Promise.resolve(def), + getDefinition: function () { + return Promise.resolve(def); + } }; atom.packages.serviceHub.provide('definitions', '0.1.0', newProvider); - invariant(provider != null); - invariant(provider.getSuggestion != null); - const result = await provider.getSuggestion(editor, position); + + if (!(provider != null)) { + throw new Error('Invariant violation: "provider != null"'); + } + + if (!(provider.getSuggestion != null)) { + throw new Error('Invariant violation: "provider.getSuggestion != null"'); + } + + const result = yield provider.getSuggestion(editor, position); expect(result).not.toBe(null); - invariant(result != null); + + if (!(result != null)) { + throw new Error('Invariant violation: "result != null"'); + } + expect(result.range).toEqual(def.queryRange); - }); + })); }); -}); +}); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/DiagnosticsViewModel.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/DiagnosticsViewModel.js index 97344845..750d1794 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/DiagnosticsViewModel.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/DiagnosticsViewModel.js @@ -1,3 +1,108 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DiagnosticsViewModel = exports.WORKSPACE_VIEW_URI = undefined; + +var _dockForLocation; + +function _load_dockForLocation() { + return _dockForLocation = _interopRequireDefault(require('nuclide-commons-atom/dock-for-location')); +} + +var _goToLocation; + +function _load_goToLocation() { + return _goToLocation = require('nuclide-commons-atom/go-to-location'); +} + +var _memoizeUntilChanged; + +function _load_memoizeUntilChanged() { + return _memoizeUntilChanged = _interopRequireDefault(require('nuclide-commons/memoizeUntilChanged')); +} + +var _nuclideUri; + +function _load_nuclideUri() { + return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri')); +} + +var _observePaneItemVisibility; + +function _load_observePaneItemVisibility() { + return _observePaneItemVisibility = _interopRequireDefault(require('nuclide-commons-atom/observePaneItemVisibility')); +} + +var _collection; + +function _load_collection() { + return _collection = require('nuclide-commons/collection'); +} + +var _observable; + +function _load_observable() { + return _observable = require('nuclide-commons/observable'); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _react = _interopRequireDefault(require('react')); + +var _analytics; + +function _load_analytics() { + return _analytics = _interopRequireDefault(require('nuclide-commons-atom/analytics')); +} + +var _Model; + +function _load_Model() { + return _Model = _interopRequireDefault(require('nuclide-commons/Model')); +} + +var _renderReactRoot; + +function _load_renderReactRoot() { + return _renderReactRoot = require('nuclide-commons-ui/renderReactRoot'); +} + +var _bindObservableAsProps; + +function _load_bindObservableAsProps() { + return _bindObservableAsProps = require('nuclide-commons-ui/bindObservableAsProps'); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _RegExpFilter; + +function _load_RegExpFilter() { + return _RegExpFilter = require('nuclide-commons-ui/RegExpFilter'); +} + +var _GroupUtils; + +function _load_GroupUtils() { + return _GroupUtils = _interopRequireWildcard(require('./GroupUtils')); +} + +var _DiagnosticsView; + +function _load_DiagnosticsView() { + return _DiagnosticsView = _interopRequireDefault(require('./ui/DiagnosticsView')); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,136 +111,57 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {IconName} from 'nuclide-commons-ui/Icon'; -import type {Props} from './ui/DiagnosticsView'; -import type {DiagnosticGroup, GlobalViewState} from './types'; -import type {DiagnosticMessage} from '../../atom-ide-diagnostics/lib/types'; -import type {RegExpFilterChange} from 'nuclide-commons-ui/RegExpFilter'; - -import dockForLocation from 'nuclide-commons-atom/dock-for-location'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; -import memoizeUntilChanged from 'nuclide-commons/memoizeUntilChanged'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import observePaneItemVisibility from 'nuclide-commons-atom/observePaneItemVisibility'; -import {arrayEqual, areSetsEqual} from 'nuclide-commons/collection'; -import {fastDebounce} from 'nuclide-commons/observable'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import React from 'react'; -import analytics from 'nuclide-commons-atom/analytics'; -import Model from 'nuclide-commons/Model'; -import {renderReactRoot} from 'nuclide-commons-ui/renderReactRoot'; -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import {Observable} from 'rxjs'; -import {getFilterPattern} from 'nuclide-commons-ui/RegExpFilter'; -import * as GroupUtils from './GroupUtils'; -import DiagnosticsView from './ui/DiagnosticsView'; - -type SerializedDiagnosticsViewModel = { - deserializer: 'atom-ide-ui.DiagnosticsViewModel', - state: { - hiddenGroups: Array, - }, -}; +const WORKSPACE_VIEW_URI = exports.WORKSPACE_VIEW_URI = 'atom://nuclide/diagnostics'; + +class DiagnosticsViewModel { + + constructor(globalStates) { + _initialiseProps.call(this); -type State = {| - hiddenGroups: Set, - selectedMessage: ?DiagnosticMessage, - textFilter: {| - text: string, - isRegExp: boolean, - invalid: boolean, - pattern: ?RegExp, - |}, -|}; - -export const WORKSPACE_VIEW_URI = 'atom://nuclide/diagnostics'; - -export class DiagnosticsViewModel { - _element: ?HTMLElement; - _model: Model; - _props: Observable; - _disposables: IDisposable; - - constructor(globalStates: Observable) { // Memoize `_filterDiagnostics()` - (this: any)._filterDiagnostics = memoizeUntilChanged( - this._filterDiagnostics, - (diagnostics, pattern, hiddenGroups, filterPath) => ({ - diagnostics, - pattern, - hiddenGroups, - filterPath, - }), - (a, b) => - patternsAreEqual(a.pattern, b.pattern) && - areSetsEqual(a.hiddenGroups, b.hiddenGroups) && - arrayEqual(a.diagnostics, b.diagnostics) && - a.filterPath === b.filterPath, - ); - - const {pattern, invalid} = getFilterPattern('', false); - this._model = new Model({ + this._filterDiagnostics = (0, (_memoizeUntilChanged || _load_memoizeUntilChanged()).default)(this._filterDiagnostics, (diagnostics, pattern, hiddenGroups, filterPath) => ({ + diagnostics, + pattern, + hiddenGroups, + filterPath + }), (a, b) => patternsAreEqual(a.pattern, b.pattern) && (0, (_collection || _load_collection()).areSetsEqual)(a.hiddenGroups, b.hiddenGroups) && (0, (_collection || _load_collection()).arrayEqual)(a.diagnostics, b.diagnostics) && a.filterPath === b.filterPath); + + const { pattern, invalid } = (0, (_RegExpFilter || _load_RegExpFilter()).getFilterPattern)('', false); + this._model = new (_Model || _load_Model()).default({ // TODO: Get this from constructor/serialization. hiddenGroups: new Set(), - textFilter: {text: '', isRegExp: false, pattern, invalid}, - selectedMessage: null, + textFilter: { text: '', isRegExp: false, pattern, invalid }, + selectedMessage: null }); - const visibility = observePaneItemVisibility(this).distinctUntilChanged(); - this._disposables = new UniversalDisposable( - visibility - .let(fastDebounce(1000)) - .distinctUntilChanged() - .filter(Boolean) - .subscribe(() => { - analytics.track('diagnostics-show-table'); - }), - ); + const visibility = (0, (_observePaneItemVisibility || _load_observePaneItemVisibility()).default)(this).distinctUntilChanged(); + this._disposables = new (_UniversalDisposable || _load_UniversalDisposable()).default(visibility.let((0, (_observable || _load_observable()).fastDebounce)(1000)).distinctUntilChanged().filter(Boolean).subscribe(() => { + (_analytics || _load_analytics()).default.track('diagnostics-show-table'); + })); // Combine the state that's shared between instances, the state that's unique to this instance, // and unchanging callbacks, to get the props for our component. - const props = Observable.combineLatest( - globalStates, - this._model.toObservable(), - visibility, - (globalState, instanceState, isVisible) => ({ - ...globalState, - ...instanceState, - isVisible, - diagnostics: this._filterDiagnostics( - globalState.diagnostics, - instanceState.textFilter.pattern, - instanceState.hiddenGroups, - globalState.filterByActiveTextEditor - ? globalState.pathToActiveTextEditor - : null, - ), - onTypeFilterChange: this._handleTypeFilterChange, - onTextFilterChange: this._handleTextFilterChange, - selectMessage: this._selectMessage, - gotoMessageLocation: goToDiagnosticLocation, - supportedMessageKinds: globalState.supportedMessageKinds, - }), - ); + const props = _rxjsBundlesRxMinJs.Observable.combineLatest(globalStates, this._model.toObservable(), visibility, (globalState, instanceState, isVisible) => Object.assign({}, globalState, instanceState, { + isVisible, + diagnostics: this._filterDiagnostics(globalState.diagnostics, instanceState.textFilter.pattern, instanceState.hiddenGroups, globalState.filterByActiveTextEditor ? globalState.pathToActiveTextEditor : null), + onTypeFilterChange: this._handleTypeFilterChange, + onTextFilterChange: this._handleTextFilterChange, + selectMessage: this._selectMessage, + gotoMessageLocation: goToDiagnosticLocation, + supportedMessageKinds: globalState.supportedMessageKinds + })); this._props = this._trackVisibility(props); } // If autoVisibility setting is on, then automatically show/hide on changes. - _trackVisibility(props: Observable): Observable { + _trackVisibility(props) { let lastDiagnostics = []; return props.do(newProps => { - if ( - newProps.autoVisibility && - !arrayEqual( - newProps.diagnostics, - lastDiagnostics, - (a, b) => a.text === b.text, - ) - ) { + if (newProps.autoVisibility && !(0, (_collection || _load_collection()).arrayEqual)(newProps.diagnostics, lastDiagnostics, (a, b) => a.text === b.text)) { const pane = atom.workspace.paneForItem(this); if (newProps.diagnostics.length > 0 && !newProps.isVisible) { // We want to call workspace.open but it has no option to @@ -144,7 +170,7 @@ export class DiagnosticsViewModel { // https://github.com/atom/atom/issues/16007 if (pane != null) { pane.activateItem(this); - const dock = dockForLocation(pane.getContainer().getLocation()); + const dock = (0, (_dockForLocation || _load_dockForLocation()).default)(pane.getContainer().getLocation()); if (dock != null) { dock.show(); } @@ -153,10 +179,7 @@ export class DiagnosticsViewModel { // Only hide the diagnostics if it's the only item in its pane. if (pane != null) { const items = pane.getItems(); - if ( - items.length === 1 && - items[0] instanceof DiagnosticsViewModel - ) { + if (items.length === 1 && items[0] instanceof DiagnosticsViewModel) { atom.workspace.hide(this); } } @@ -166,40 +189,40 @@ export class DiagnosticsViewModel { }); } - destroy(): void { + destroy() { this._disposables.dispose(); } - getTitle(): string { + getTitle() { return 'Diagnostics'; } - getIconName(): IconName { + getIconName() { return 'law'; } - getURI(): string { + getURI() { return WORKSPACE_VIEW_URI; } - getDefaultLocation(): string { + getDefaultLocation() { return 'bottom'; } - serialize(): SerializedDiagnosticsViewModel { - const {hiddenGroups} = this._model.state; + serialize() { + const { hiddenGroups } = this._model.state; return { deserializer: 'atom-ide-ui.DiagnosticsViewModel', state: { - hiddenGroups: [...hiddenGroups], - }, + hiddenGroups: [...hiddenGroups] + } }; } - getElement(): HTMLElement { + getElement() { if (this._element == null) { - const Component = bindObservableAsProps(this._props, DiagnosticsView); - const element = renderReactRoot(); + const Component = (0, (_bindObservableAsProps || _load_bindObservableAsProps()).bindObservableAsProps)(this._props, (_DiagnosticsView || _load_DiagnosticsView()).default); + const element = (0, (_renderReactRoot || _load_renderReactRoot()).renderReactRoot)(_react.default.createElement(Component, null)); element.classList.add('diagnostics-ui'); this._element = element; } @@ -209,35 +232,11 @@ export class DiagnosticsViewModel { /** * Toggle the filter. */ - _handleTypeFilterChange = (type: DiagnosticGroup): void => { - const {hiddenGroups} = this._model.state; - const hidden = hiddenGroups.has(type); - const nextHiddenTypes = new Set(hiddenGroups); - if (hidden) { - nextHiddenTypes.delete(type); - } else { - nextHiddenTypes.add(type); - } - this._model.setState({hiddenGroups: nextHiddenTypes}); - }; - _handleTextFilterChange = (value: RegExpFilterChange): void => { - const {text, isRegExp} = value; - // TODO: Fuzzy if !isRegExp? - const {invalid, pattern} = getFilterPattern(text, isRegExp); - this._model.setState({ - textFilter: {text, isRegExp, invalid, pattern}, - }); - }; - _filterDiagnostics( - diagnostics: Array, - pattern: ?RegExp, - hiddenGroups: Set, - filterByPath: ?string, - ): Array { + _filterDiagnostics(diagnostics, pattern, hiddenGroups, filterByPath) { return diagnostics.filter(message => { - if (hiddenGroups.has(GroupUtils.getGroup(message))) { + if (hiddenGroups.has((_GroupUtils || _load_GroupUtils()).getGroup(message))) { return false; } if (filterByPath != null && message.filePath !== filterByPath) { @@ -246,45 +245,63 @@ export class DiagnosticsViewModel { if (pattern == null) { return true; } - return ( - (message.text != null && pattern.test(message.text)) || - (message.html != null && pattern.test(message.html)) || - pattern.test(message.providerName) || - pattern.test(message.filePath) - ); + return message.text != null && pattern.test(message.text) || message.html != null && pattern.test(message.html) || pattern.test(message.providerName) || pattern.test(message.filePath); }); } - _selectMessage = (message: DiagnosticMessage): void => { - this._model.setState({selectedMessage: message}); - }; } -function goToDiagnosticLocation( - message: DiagnosticMessage, - options: {|focusEditor: boolean|}, -): void { +exports.DiagnosticsViewModel = DiagnosticsViewModel; + +var _initialiseProps = function () { + this._handleTypeFilterChange = type => { + const { hiddenGroups } = this._model.state; + const hidden = hiddenGroups.has(type); + const nextHiddenTypes = new Set(hiddenGroups); + if (hidden) { + nextHiddenTypes.delete(type); + } else { + nextHiddenTypes.add(type); + } + this._model.setState({ hiddenGroups: nextHiddenTypes }); + }; + + this._handleTextFilterChange = value => { + const { text, isRegExp } = value; + // TODO: Fuzzy if !isRegExp? + const { invalid, pattern } = (0, (_RegExpFilter || _load_RegExpFilter()).getFilterPattern)(text, isRegExp); + this._model.setState({ + textFilter: { text, isRegExp, invalid, pattern } + }); + }; + + this._selectMessage = message => { + this._model.setState({ selectedMessage: message }); + }; +}; + +function goToDiagnosticLocation(message, options) { // TODO: what should we do for project-path diagnostics? - if (nuclideUri.endsWithSeparator(message.filePath)) { + if ((_nuclideUri || _load_nuclideUri()).default.endsWithSeparator(message.filePath)) { return; } - analytics.track('diagnostics-panel-goto-location'); + (_analytics || _load_analytics()).default.track('diagnostics-panel-goto-location'); const uri = message.filePath; // If initialLine is N, Atom will navigate to line N+1. // Flow sometimes reports a row of -1, so this ensures the line is at least one. const line = Math.max(message.range ? message.range.start.row : 0, 0); const column = 0; - goToLocation(uri, { + (0, (_goToLocation || _load_goToLocation()).goToLocation)(uri, { line, column, activatePane: options.focusEditor, - pending: true, + pending: true }); } -function patternsAreEqual(a: ?RegExp, b: ?RegExp) { +function patternsAreEqual(a, b) { if (a === b) { return true; } @@ -294,10 +311,5 @@ function patternsAreEqual(a: ?RegExp, b: ?RegExp) { if (a == null || b == null) { return false; } - return ( - a.source === b.source && - a.global === b.global && - a.multiline === b.multiline && - a.ignoreCase === b.ignoreCase - ); -} + return a.source === b.source && a.global === b.global && a.multiline === b.multiline && a.ignoreCase === b.ignoreCase; +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/GroupUtils.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/GroupUtils.js index 29757cc7..d1c29cc0 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/GroupUtils.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/GroupUtils.js @@ -1,3 +1,12 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.getGroup = getGroup; +exports.getDisplayName = getDisplayName; +exports.getIcon = getIcon; +exports.getHighestPriorityGroup = getHighestPriorityGroup; /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,32 +15,24 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {IconName} from 'nuclide-commons-ui/Icon'; -import type {DiagnosticMessage} from '../../atom-ide-diagnostics/lib/types'; -import type {DiagnosticGroup} from './types'; - -import invariant from 'assert'; +const PRIORITIZED_GROUPS = ['review', 'errors', 'warnings', 'info', 'action']; -const PRIORITIZED_GROUPS: Array = [ - 'review', - 'errors', - 'warnings', - 'info', - 'action', -]; - -export function getGroup(message: DiagnosticMessage): DiagnosticGroup { - const {kind} = message; +function getGroup(message) { + const { kind } = message; switch (kind) { case 'lint': case null: case undefined: - invariant(message.type !== 'Hint'); + if (!(message.type !== 'Hint')) { + throw new Error('Invariant violation: "message.type !== \'Hint\'"'); + } // We have a separate button for each severity. + + switch (message.type) { case 'Error': return 'errors'; @@ -40,7 +41,7 @@ export function getGroup(message: DiagnosticMessage): DiagnosticGroup { case 'Info': return 'info'; default: - (message.type: empty); + message.type; throw new Error(`Invalid message severity: ${message.type}`); } case 'review': @@ -48,12 +49,12 @@ export function getGroup(message: DiagnosticMessage): DiagnosticGroup { case 'action': return 'action'; default: - (kind: empty); + kind; throw new Error(`Invalid message kind: ${kind}`); } } -export function getDisplayName(group: DiagnosticGroup): string { +function getDisplayName(group) { switch (group) { case 'errors': return 'Errors'; @@ -66,12 +67,12 @@ export function getDisplayName(group: DiagnosticGroup): string { case 'action': return 'Actions'; default: - (group: empty); + group; throw new Error(`Invalid group: ${group}`); } } -export function getIcon(group: DiagnosticGroup): IconName { +function getIcon(group) { switch (group) { case 'errors': return 'nuclicon-error'; @@ -84,18 +85,16 @@ export function getIcon(group: DiagnosticGroup): IconName { case 'action': return 'light-bulb'; default: - (group: empty); + group; throw new Error(`Invalid filter type: ${group}`); } } -export function getHighestPriorityGroup( - groups: Set, -): DiagnosticGroup { +function getHighestPriorityGroup(groups) { for (const group of PRIORITIZED_GROUPS) { if (groups.has(group)) { return group; } } throw new Error(`Invalid group set: ${[...groups].toString()}`); -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/KeyboardShortcuts.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/KeyboardShortcuts.js index 60875b93..77a02f41 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/KeyboardShortcuts.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/KeyboardShortcuts.js @@ -1,91 +1,56 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type { - DiagnosticTrace, - DiagnosticMessage, - DiagnosticUpdater, -} from '../../atom-ide-diagnostics/lib/types'; - -import invariant from 'assert'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _goToLocation; + +function _load_goToLocation() { + return _goToLocation = require('nuclide-commons-atom/go-to-location'); +} + +var _event; + +function _load_event() { + return _event = require('nuclide-commons/event'); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // TODO(peterhal): The current index should really live in the DiagnosticStore. -export default class KeyboardShortcuts { - _subscriptions: UniversalDisposable; - _diagnostics: Array; - _index: ?number; - _traceIndex: ?number; +class KeyboardShortcuts { - constructor(diagnosticUpdater: DiagnosticUpdater) { + constructor(diagnosticUpdater) { this._index = null; this._diagnostics = []; - this._subscriptions = new UniversalDisposable(); + this._subscriptions = new (_UniversalDisposable || _load_UniversalDisposable()).default(); const first = () => this.setIndex(0); const last = () => this.setIndex(this._diagnostics.length - 1); - this._subscriptions.add( - observableFromSubscribeFunction( - diagnosticUpdater.observeMessages, - ).subscribe(diagnostics => { - this._index = null; - this._traceIndex = null; - this._diagnostics = diagnostics; - }), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-first-diagnostic', - first, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-last-diagnostic', - last, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-next-diagnostic', - () => { - this._index == null ? first() : this.setIndex(this._index + 1); - }, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-previous-diagnostic', - () => { - this._index == null ? last() : this.setIndex(this._index - 1); - }, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-next-diagnostic-trace', - () => { - this.nextTrace(); - }, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:go-to-previous-diagnostic-trace', - () => { - this.previousTrace(); - }, - ), - ); + this._subscriptions.add((0, (_event || _load_event()).observableFromSubscribeFunction)(diagnosticUpdater.observeMessages).subscribe(diagnostics => { + this._index = null; + this._traceIndex = null; + this._diagnostics = diagnostics; + }), atom.commands.add('atom-workspace', 'diagnostics:go-to-first-diagnostic', first), atom.commands.add('atom-workspace', 'diagnostics:go-to-last-diagnostic', last), atom.commands.add('atom-workspace', 'diagnostics:go-to-next-diagnostic', () => { + this._index == null ? first() : this.setIndex(this._index + 1); + }), atom.commands.add('atom-workspace', 'diagnostics:go-to-previous-diagnostic', () => { + this._index == null ? last() : this.setIndex(this._index - 1); + }), atom.commands.add('atom-workspace', 'diagnostics:go-to-next-diagnostic-trace', () => { + this.nextTrace(); + }), atom.commands.add('atom-workspace', 'diagnostics:go-to-previous-diagnostic-trace', () => { + this.previousTrace(); + })); } - setIndex(index: number): void { + setIndex(index) { this._traceIndex = null; if (this._diagnostics.length === 0) { this._index = null; @@ -95,22 +60,28 @@ export default class KeyboardShortcuts { this.gotoCurrentIndex(); } - gotoCurrentIndex(): void { - invariant(this._index != null); - invariant(this._traceIndex == null); + gotoCurrentIndex() { + if (!(this._index != null)) { + throw new Error('Invariant violation: "this._index != null"'); + } + + if (!(this._traceIndex == null)) { + throw new Error('Invariant violation: "this._traceIndex == null"'); + } + const diagnostic = this._diagnostics[this._index]; const range = diagnostic.range; if (range == null) { - goToLocation(diagnostic.filePath); + (0, (_goToLocation || _load_goToLocation()).goToLocation)(diagnostic.filePath); } else { - goToLocation(diagnostic.filePath, { + (0, (_goToLocation || _load_goToLocation()).goToLocation)(diagnostic.filePath, { line: range.start.row, - column: range.start.column, + column: range.start.column }); } } - nextTrace(): void { + nextTrace() { const traces = this.currentTraces(); if (traces == null) { return; @@ -126,13 +97,12 @@ export default class KeyboardShortcuts { this.gotoCurrentIndex(); } - previousTrace(): void { + previousTrace() { const traces = this.currentTraces(); if (traces == null) { return; } - let candidateTrace = - this._traceIndex == null ? traces.length - 1 : this._traceIndex - 1; + let candidateTrace = this._traceIndex == null ? traces.length - 1 : this._traceIndex - 1; while (candidateTrace >= 0) { if (this.trySetCurrentTrace(traces, candidateTrace)) { return; @@ -143,7 +113,7 @@ export default class KeyboardShortcuts { this.gotoCurrentIndex(); } - currentTraces(): ?Array { + currentTraces() { if (this._index == null) { return null; } @@ -152,23 +122,31 @@ export default class KeyboardShortcuts { } // TODO: Should filter out traces whose location matches the main diagnostic's location? - trySetCurrentTrace( - traces: Array, - traceIndex: number, - ): boolean { + trySetCurrentTrace(traces, traceIndex) { const trace = traces[traceIndex]; if (trace.filePath != null && trace.range != null) { this._traceIndex = traceIndex; - goToLocation(trace.filePath, { + (0, (_goToLocation || _load_goToLocation()).goToLocation)(trace.filePath, { line: trace.range.start.row, - column: trace.range.start.column, + column: trace.range.start.column }); return true; } return false; } - dispose(): void { + dispose() { this._subscriptions.dispose(); } } +exports.default = KeyboardShortcuts; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/aim.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/aim.js index 567e663d..73609476 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/aim.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/aim.js @@ -1,83 +1,60 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -import {Observable} from 'rxjs'; - -type Point = {x: number, y: number}; - -const VECTOR_DURATION = 100; - -const distance = (a: Point, b: Point): number => { +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.hoveringOrAiming = hoveringOrAiming; + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +const VECTOR_DURATION = 100; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +const distance = (a, b) => { return Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2)); }; -const eventToPoint = (e: MouseEvent): Point => ({ +const eventToPoint = e => ({ x: e.clientX, - y: e.clientY, + y: e.clientY }); // Combine mouseenter and mouseleave to create an observable of hovering state. -function areHovering(element: HTMLElement): Observable { - return Observable.merge( - Observable.fromEvent(element, 'mouseenter').mapTo(true), - Observable.fromEvent(element, 'mouseleave').mapTo(false), - ); +function areHovering(element) { + return _rxjsBundlesRxMinJs.Observable.merge(_rxjsBundlesRxMinJs.Observable.fromEvent(element, 'mouseenter').mapTo(true), _rxjsBundlesRxMinJs.Observable.fromEvent(element, 'mouseleave').mapTo(false)); } -function findCorners(node: HTMLElement): [Point, Point, Point, Point] { - const {left, width, top, height} = node.getBoundingClientRect(); - return [ - {x: left, y: top}, // Top left - {x: left + width, y: top}, // Top right - {x: left, y: top + height}, // Bottom left - {x: left + width, y: top + height}, // Bottom right - ]; +function findCorners(node) { + const { left, width, top, height } = node.getBoundingClientRect(); + return [{ x: left, y: top }, // Top left + { x: left + width, y: top }, // Top right + { x: left, y: top + height }, // Bottom left + { x: left + width, y: top + height }]; } -function areAiming(from: HTMLElement, to: HTMLElement): Observable { +function areAiming(from, to) { const [topLeft, topRight, bottomLeft, bottomRight] = findCorners(to); - const toBelowFrom = - to.getBoundingClientRect().top >= from.getBoundingClientRect().bottom; + const toBelowFrom = to.getBoundingClientRect().top >= from.getBoundingClientRect().bottom; // For now we assume that `to` is always to the right of `from` and that // `from` is always strictly above or below `to`. A more robust solution would // be to find the two corner of `to` that form the largest angle from the // center of `from` - const [cornerA, cornerB] = toBelowFrom - ? [topRight, bottomLeft] - : [topLeft, bottomRight]; + const [cornerA, cornerB] = toBelowFrom ? [topRight, bottomLeft] : [topLeft, bottomRight]; - return Observable.fromEvent(document, 'mousemove') - .map(eventToPoint) - .auditTime(VECTOR_DURATION) - .map(mouse => distance(mouse, cornerA) + distance(mouse, cornerB)) - .pairwise() - .map(([prevDist, currentDist]) => prevDist > currentDist) - .distinctUntilChanged(); + return _rxjsBundlesRxMinJs.Observable.fromEvent(document, 'mousemove').map(eventToPoint).auditTime(VECTOR_DURATION).map(mouse => distance(mouse, cornerA) + distance(mouse, cornerB)).pairwise().map(([prevDist, currentDist]) => prevDist > currentDist).distinctUntilChanged(); } -export function hoveringOrAiming( - from: HTMLElement, - to: HTMLElement, -): Observable { - return Observable.concat( - areHovering(from) - .startWith(true) - .takeWhile(Boolean), - Observable.combineLatest( - areAiming(from, to).startWith(true), - areHovering(to).startWith(false), - (aiming, hovering) => aiming || hovering, - ), - ).distinctUntilChanged(); -} +function hoveringOrAiming(from, to) { + return _rxjsBundlesRxMinJs.Observable.concat(areHovering(from).startWith(true).takeWhile(Boolean), _rxjsBundlesRxMinJs.Observable.combineLatest(areAiming(from, to).startWith(true), areHovering(to).startWith(false), (aiming, hovering) => aiming || hovering)).distinctUntilChanged(); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/getDiagnosticDatatip.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/getDiagnosticDatatip.js index edb1e6bc..158a6ff2 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/getDiagnosticDatatip.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/getDiagnosticDatatip.js @@ -1,65 +1,87 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {Datatip} from '../../atom-ide-datatip/lib/types'; -import type { - DiagnosticUpdater, - DiagnosticMessage, -} from '../../atom-ide-diagnostics/lib/types'; - -import invariant from 'assert'; -import * as React from 'react'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import {DiagnosticsPopup} from './ui/DiagnosticsPopup'; - -const gotoLine = (file: string, line: number) => goToLocation(file, {line}); - -function makeDatatipComponent( - messages: Array, - diagnosticUpdater: DiagnosticUpdater, -): React.ComponentType<*> { +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +var _react = _interopRequireWildcard(require('react')); + +var _event; + +function _load_event() { + return _event = require('nuclide-commons/event'); +} + +var _goToLocation; + +function _load_goToLocation() { + return _goToLocation = require('nuclide-commons-atom/go-to-location'); +} + +var _bindObservableAsProps; + +function _load_bindObservableAsProps() { + return _bindObservableAsProps = require('nuclide-commons-ui/bindObservableAsProps'); +} + +var _DiagnosticsPopup; + +function _load_DiagnosticsPopup() { + return _DiagnosticsPopup = require('./ui/DiagnosticsPopup'); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const gotoLine = (file, line) => (0, (_goToLocation || _load_goToLocation()).goToLocation)(file, { line }); /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +function makeDatatipComponent(messages, diagnosticUpdater) { const fixer = message => diagnosticUpdater.applyFix(message); - return bindObservableAsProps( - observableFromSubscribeFunction(cb => - diagnosticUpdater.observeCodeActionsForMessage(cb), - ).map(codeActionsForMessage => ({ - messages, - fixer, - goToLocation: gotoLine, - codeActionsForMessage, - })), - DiagnosticsPopup, - ); + return (0, (_bindObservableAsProps || _load_bindObservableAsProps()).bindObservableAsProps)((0, (_event || _load_event()).observableFromSubscribeFunction)(cb => diagnosticUpdater.observeCodeActionsForMessage(cb)).map(codeActionsForMessage => ({ + messages, + fixer, + goToLocation: gotoLine, + codeActionsForMessage + })), (_DiagnosticsPopup || _load_DiagnosticsPopup()).DiagnosticsPopup); } -export default (async function getDiagnosticDatatip( - editor: TextEditor, - position: atom$Point, - messagesAtPosition: Array, - diagnosticUpdater: DiagnosticUpdater, -): Promise { - let range = null; - for (const message of messagesAtPosition) { - if (message.range != null) { - range = range == null ? message.range : message.range.union(range); +exports.default = (() => { + var _ref = (0, _asyncToGenerator.default)(function* (editor, position, messagesAtPosition, diagnosticUpdater) { + let range = null; + for (const message of messagesAtPosition) { + if (message.range != null) { + range = range == null ? message.range : message.range.union(range); + } } + diagnosticUpdater.fetchCodeActions(editor, messagesAtPosition); + + if (!(range != null)) { + throw new Error('Invariant violation: "range != null"'); + } + + return { + component: makeDatatipComponent(messagesAtPosition, diagnosticUpdater), + pinnable: false, + range + }; + }); + + function getDiagnosticDatatip(_x, _x2, _x3, _x4) { + return _ref.apply(this, arguments); } - diagnosticUpdater.fetchCodeActions(editor, messagesAtPosition); - invariant(range != null); - return { - component: makeDatatipComponent(messagesAtPosition, diagnosticUpdater), - pinnable: false, - range, - }; -}); + + return getDiagnosticDatatip; +})(); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/gutter.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/gutter.js index 47c5030d..7edb39fd 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/gutter.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/gutter.js @@ -1,3 +1,82 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.applyUpdateToEditor = applyUpdateToEditor; + +var _classnames; + +function _load_classnames() { + return _classnames = _interopRequireDefault(require('classnames')); +} + +var _atom = require('atom'); + +var _react = _interopRequireWildcard(require('react')); + +var _reactDom = _interopRequireDefault(require('react-dom')); + +var _event; + +function _load_event() { + return _event = require('nuclide-commons/event'); +} + +var _observable; + +function _load_observable() { + return _observable = require('nuclide-commons/observable'); +} + +var _goToLocation; + +function _load_goToLocation() { + return _goToLocation = require('nuclide-commons-atom/go-to-location'); +} + +var _range; + +function _load_range() { + return _range = require('nuclide-commons-atom/range'); +} + +var _analytics; + +function _load_analytics() { + return _analytics = _interopRequireDefault(require('nuclide-commons-atom/analytics')); +} + +var _bindObservableAsProps; + +function _load_bindObservableAsProps() { + return _bindObservableAsProps = require('nuclide-commons-ui/bindObservableAsProps'); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _DiagnosticsPopup; + +function _load_DiagnosticsPopup() { + return _DiagnosticsPopup = require('./ui/DiagnosticsPopup'); +} + +var _GroupUtils; + +function _load_GroupUtils() { + return _GroupUtils = _interopRequireWildcard(require('./GroupUtils')); +} + +var _aim; + +function _load_aim() { + return _aim = require('./aim'); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,33 +85,10 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type { - DiagnosticUpdater, - DiagnosticMessage, - DiagnosticMessages, -} from '../../atom-ide-diagnostics/lib/types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; - -import classnames from 'classnames'; -import {Range} from 'atom'; -import invariant from 'assert'; -import * as React from 'react'; -import ReactDOM from 'react-dom'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {completingSwitchMap} from 'nuclide-commons/observable'; -import {goToLocation as atomGoToLocation} from 'nuclide-commons-atom/go-to-location'; -import {wordAtPosition} from 'nuclide-commons-atom/range'; -import analytics from 'nuclide-commons-atom/analytics'; -import {bindObservableAsProps} from 'nuclide-commons-ui/bindObservableAsProps'; -import {Observable} from 'rxjs'; -import {DiagnosticsPopup} from './ui/DiagnosticsPopup'; -import * as GroupUtils from './GroupUtils'; -import {hoveringOrAiming} from './aim'; - const GUTTER_ID = 'diagnostics-gutter'; // TODO(mbolin): Make it so that when mousing over an element with this CSS class (or specifically, @@ -51,7 +107,7 @@ const HIGHLIGHT_CSS_LEVELS = { Error: 'diagnostics-gutter-ui-highlight-error', Warning: 'diagnostics-gutter-ui-highlight-warning', Info: 'diagnostics-gutter-ui-highlight-info', - Hint: '', + Hint: '' }; const GUTTER_CSS_GROUPS = { @@ -60,17 +116,13 @@ const GUTTER_CSS_GROUPS = { warnings: 'diagnostics-gutter-ui-gutter-warning', info: 'diagnostics-gutter-ui-gutter-info', action: 'diagnostics-gutter-ui-gutter-action', - hidden: '', + hidden: '' }; -const editorToMarkers: WeakMap> = new WeakMap(); -const itemToEditor: WeakMap = new WeakMap(); +const editorToMarkers = new WeakMap(); +const itemToEditor = new WeakMap(); -export function applyUpdateToEditor( - editor: TextEditor, - update: DiagnosticMessages, - diagnosticUpdater: DiagnosticUpdater, -): void { +function applyUpdateToEditor(editor, update, diagnosticUpdater) { let gutter = editor.gutterWithName(GUTTER_ID); if (!gutter) { // TODO(jessicalin): Determine an appropriate priority so that the gutter: @@ -84,7 +136,7 @@ export function applyUpdateToEditor( name: GUTTER_ID, visible: false, // Priority is -200 by default and 0 is the line number - priority: -1000, + priority: -1000 }); } @@ -102,8 +154,8 @@ export function applyUpdateToEditor( markers = new Set(); } - const rowToMessage: Map> = new Map(); - function addMessageForRow(message: DiagnosticMessage, row: number) { + const rowToMessage = new Map(); + function addMessageForRow(message, row) { let messages = rowToMessage.get(row); if (!messages) { messages = []; @@ -113,16 +165,10 @@ export function applyUpdateToEditor( } for (const message of update.messages) { - const wordRange = - message.range != null && message.range.isEmpty() - ? wordAtPosition(editor, message.range.start) - : null; + const wordRange = message.range != null && message.range.isEmpty() ? (0, (_range || _load_range()).wordAtPosition)(editor, message.range.start) : null; const range = wordRange != null ? wordRange.range : message.range; - const highlightCssClass = classnames( - HIGHLIGHT_CSS, - HIGHLIGHT_CSS_LEVELS[message.type], - ); + const highlightCssClass = (0, (_classnames || _load_classnames()).default)(HIGHLIGHT_CSS, HIGHLIGHT_CSS_LEVELS[message.type]); let highlightMarker; if (range) { @@ -141,9 +187,7 @@ export function applyUpdateToEditor( for (let line = range.start.row; line <= range.end.row; line++) { let start; let end; - const lineText = editor.getTextInBufferRange( - new Range([line, 0], [line + 1, 0]), - ); + const lineText = editor.getTextInBufferRange(new _atom.Range([line, 0], [line + 1, 0])); if (line === range.start.row) { start = range.start.column; @@ -160,12 +204,10 @@ export function applyUpdateToEditor( end = lineText.length; } - highlightMarker = editor.markBufferRange( - new Range([line, start], [line, end]), - ); + highlightMarker = editor.markBufferRange(new _atom.Range([line, start], [line, end])); editor.decorateMarker(highlightMarker, { type: 'highlight', - class: highlightCssClass, + class: highlightCssClass }); markers.add(highlightMarker); } @@ -177,14 +219,10 @@ export function applyUpdateToEditor( // Find all of the gutter markers for the same row and combine them into one marker/popup. for (const [row, messages] of rowToMessage.entries()) { // This marker adds some UI to the gutter. - const {item, dispose} = createGutterItem( - messages, - diagnosticUpdater, - gutter, - ); + const { item, dispose } = createGutterItem(messages, diagnosticUpdater, gutter); itemToEditor.set(item, editor); const gutterMarker = editor.markBufferPosition([row, 0]); - gutter.decorateMarker(gutterMarker, {item}); + gutter.decorateMarker(gutterMarker, { item }); gutterMarker.onDidDestroy(dispose); markers.add(gutterMarker); } @@ -198,15 +236,11 @@ export function applyUpdateToEditor( } } -function createGutterItem( - messages: Array, - diagnosticUpdater: DiagnosticUpdater, - gutter: atom$Gutter, -): {item: HTMLElement, dispose: () => void} { +function createGutterItem(messages, diagnosticUpdater, gutter) { // Determine which group to display. const messageGroups = new Set(); - messages.forEach(msg => messageGroups.add(GroupUtils.getGroup(msg))); - const group = GroupUtils.getHighestPriorityGroup(messageGroups); + messages.forEach(msg => messageGroups.add((_GroupUtils || _load_GroupUtils()).getGroup(msg))); + const group = (_GroupUtils || _load_GroupUtils()).getHighestPriorityGroup(messageGroups); const item = document.createElement('span'); const groupClassName = GUTTER_CSS_GROUPS[group]; @@ -214,74 +248,61 @@ function createGutterItem( // Add the icon const icon = document.createElement('span'); - icon.className = `icon icon-${GroupUtils.getIcon(group)}`; + icon.className = `icon icon-${(_GroupUtils || _load_GroupUtils()).getIcon(group)}`; item.appendChild(icon); - const spawnPopup = (): Observable => { - return Observable.create(observer => { - const goToLocation = (path: string, line: number) => { + const spawnPopup = () => { + return _rxjsBundlesRxMinJs.Observable.create(observer => { + const goToLocation = (path, line) => { // Before we jump to the location, we want to close the popup. const column = 0; - atomGoToLocation(path, {line, column}); + (0, (_goToLocation || _load_goToLocation()).goToLocation)(path, { line, column }); observer.complete(); }; - const popupElement = showPopupFor( - messages, - item, - goToLocation, - diagnosticUpdater, - gutter, - ); + const popupElement = showPopupFor(messages, item, goToLocation, diagnosticUpdater, gutter); observer.next(popupElement); return () => { - ReactDOM.unmountComponentAtNode(popupElement); - invariant(popupElement.parentNode != null); + _reactDom.default.unmountComponentAtNode(popupElement); + + if (!(popupElement.parentNode != null)) { + throw new Error('Invariant violation: "popupElement.parentNode != null"'); + } + popupElement.parentNode.removeChild(popupElement); }; }); }; - const hoverSubscription = Observable.fromEvent(item, 'mouseenter') - .exhaustMap((event: MouseEvent) => { - return spawnPopup() - .let( - completingSwitchMap((popupElement: HTMLElement) => { - const innerPopupElement = popupElement.firstChild; - invariant(innerPopupElement instanceof HTMLElement); - - // Events which should cause the popup to close. - return Observable.merge( - hoveringOrAiming(item, innerPopupElement), - // This makes sure that the popup disappears when you ctrl+tab to switch tabs. - observableFromSubscribeFunction(cb => - atom.workspace.onDidChangeActivePaneItem(cb), - ).mapTo(false), - ); - }), - ) - .takeWhile(Boolean); - }) - .subscribe(); + const hoverSubscription = _rxjsBundlesRxMinJs.Observable.fromEvent(item, 'mouseenter').exhaustMap(event => { + return spawnPopup().let((0, (_observable || _load_observable()).completingSwitchMap)(popupElement => { + const innerPopupElement = popupElement.firstChild; + + if (!(innerPopupElement instanceof HTMLElement)) { + throw new Error('Invariant violation: "innerPopupElement instanceof HTMLElement"'); + } + + // Events which should cause the popup to close. + + + return _rxjsBundlesRxMinJs.Observable.merge((0, (_aim || _load_aim()).hoveringOrAiming)(item, innerPopupElement), + // This makes sure that the popup disappears when you ctrl+tab to switch tabs. + (0, (_event || _load_event()).observableFromSubscribeFunction)(cb => atom.workspace.onDidChangeActivePaneItem(cb)).mapTo(false)); + })).takeWhile(Boolean); + }).subscribe(); const dispose = () => hoverSubscription.unsubscribe(); - return {item, dispose}; + return { item, dispose }; } /** * Shows a popup for the diagnostic just below the specified item. */ -function showPopupFor( - messages: Array, - item: HTMLElement, - goToLocation: (filePath: NuclideUri, line: number) => mixed, - diagnosticUpdater: DiagnosticUpdater, - gutter: atom$Gutter, -): HTMLElement { +function showPopupFor(messages, item, goToLocation, diagnosticUpdater, gutter) { // The popup will be an absolutely positioned child element of so that it appears // on top of everything. - const workspaceElement = atom.views.getView((atom.workspace: Object)); + const workspaceElement = atom.views.getView(atom.workspace); const hostElement = document.createElement('div'); hostElement.classList.add('diagnostics-gutter-popup'); // $FlowFixMe check parentNode for null @@ -290,50 +311,53 @@ function showPopupFor( const { bottom: itemBottom, top: itemTop, - height: itemHeight, + height: itemHeight } = item.getBoundingClientRect(); // $FlowFixMe atom$Gutter.getElement is not a documented API, but it beats using a query selector. const gutterContainer = gutter.getElement(); - const {right: gutterRight} = gutterContainer.getBoundingClientRect(); + const { right: gutterRight } = gutterContainer.getBoundingClientRect(); const trackedFixer = (...args) => { diagnosticUpdater.applyFix(...args); - analytics.track('diagnostics-gutter-autofix'); + (_analytics || _load_analytics()).default.track('diagnostics-gutter-autofix'); }; - const trackedGoToLocation = (filePath: NuclideUri, line: number) => { + const trackedGoToLocation = (filePath, line) => { goToLocation(filePath, line); - analytics.track('diagnostics-gutter-goto-location'); + (_analytics || _load_analytics()).default.track('diagnostics-gutter-goto-location'); }; const editor = itemToEditor.get(item); - invariant(editor != null); + + if (!(editor != null)) { + throw new Error('Invariant violation: "editor != null"'); + } + diagnosticUpdater.fetchCodeActions(editor, messages); const popupTop = itemBottom; - const BoundPopup = bindObservableAsProps( - observableFromSubscribeFunction(cb => - diagnosticUpdater.observeCodeActionsForMessage(cb), - ).map(codeActionsForMessage => ({ - style: {left: gutterRight, top: popupTop, position: 'absolute'}, - messages, - fixer: trackedFixer, - goToLocation: trackedGoToLocation, - codeActionsForMessage, - })), - DiagnosticsPopup, - ); - ReactDOM.render(, hostElement); + const BoundPopup = (0, (_bindObservableAsProps || _load_bindObservableAsProps()).bindObservableAsProps)((0, (_event || _load_event()).observableFromSubscribeFunction)(cb => diagnosticUpdater.observeCodeActionsForMessage(cb)).map(codeActionsForMessage => ({ + style: { left: gutterRight, top: popupTop, position: 'absolute' }, + messages, + fixer: trackedFixer, + goToLocation: trackedGoToLocation, + codeActionsForMessage + })), (_DiagnosticsPopup || _load_DiagnosticsPopup()).DiagnosticsPopup); + _reactDom.default.render(_react.createElement(BoundPopup, null), hostElement); // Check to see whether the popup is within the bounds of the TextEditor. If not, display it above // the glyph rather than below it. const editorElement = atom.views.getView(editor); const { top: editorTop, - height: editorHeight, + height: editorHeight } = editorElement.getBoundingClientRect(); const popupElement = hostElement.firstElementChild; - invariant(popupElement instanceof HTMLElement); + + if (!(popupElement instanceof HTMLElement)) { + throw new Error('Invariant violation: "popupElement instanceof HTMLElement"'); + } + const popupHeight = popupElement.clientHeight; if (itemTop + itemHeight + popupHeight > editorTop + editorHeight) { popupElement.style.top = `${popupTop - popupHeight - itemHeight}px`; @@ -343,11 +367,11 @@ function showPopupFor( return hostElement; } finally { messages.forEach(message => { - analytics.track('diagnostics-gutter-show-popup', { + (_analytics || _load_analytics()).default.track('diagnostics-gutter-show-popup', { 'diagnostics-provider': message.providerName, // flowlint-next-line sketchy-null-string:off - 'diagnostics-message': message.text || message.html || '', + 'diagnostics-message': message.text || message.html || '' }); }); } -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/main.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/main.js index abe3fc0c..e8a16672 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/main.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/main.js @@ -1,162 +1,220 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type { - DatatipProvider, - DatatipService, -} from '../../atom-ide-datatip/lib/types'; - -import type { - DiagnosticMessage, - DiagnosticMessages, - DiagnosticUpdater, -} from '../../atom-ide-diagnostics/lib/types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {GlobalViewState} from './types'; - -import invariant from 'assert'; - -import analytics from 'nuclide-commons-atom/analytics'; - -import idx from 'idx'; -import {areSetsEqual} from 'nuclide-commons/collection'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {fastDebounce} from 'nuclide-commons/observable'; -import KeyboardShortcuts from './KeyboardShortcuts'; -import Model from 'nuclide-commons/Model'; -import createPackage from 'nuclide-commons-atom/createPackage'; -import {observeTextEditors} from 'nuclide-commons-atom/text-editor'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import {DiagnosticsViewModel, WORKSPACE_VIEW_URI} from './DiagnosticsViewModel'; -import StatusBarTile from './ui/StatusBarTile'; -import {applyUpdateToEditor} from './gutter'; -import getDiagnosticDatatip from './getDiagnosticDatatip'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; -import featureConfig from 'nuclide-commons-atom/feature-config'; -import {destroyItemWhere} from 'nuclide-commons-atom/destroyItemWhere'; -import {isValidTextEditor} from 'nuclide-commons-atom/text-editor'; -import {Observable} from 'rxjs'; -import showActionsMenu from './showActionsMenu'; -import showAtomLinterWarning from './showAtomLinterWarning'; - -const MAX_OPEN_ALL_FILES = 20; -const SHOW_TRACES_SETTING = 'atom-ide-diagnostics-ui.showDiagnosticTraces'; +'use strict'; + +var _analytics; + +function _load_analytics() { + return _analytics = _interopRequireDefault(require('nuclide-commons-atom/analytics')); +} + +var _idx; + +function _load_idx() { + return _idx = _interopRequireDefault(require('idx')); +} + +var _collection; + +function _load_collection() { + return _collection = require('nuclide-commons/collection'); +} + +var _nuclideUri; + +function _load_nuclideUri() { + return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri')); +} + +var _observable; + +function _load_observable() { + return _observable = require('nuclide-commons/observable'); +} + +var _KeyboardShortcuts; + +function _load_KeyboardShortcuts() { + return _KeyboardShortcuts = _interopRequireDefault(require('./KeyboardShortcuts')); +} + +var _Model; + +function _load_Model() { + return _Model = _interopRequireDefault(require('nuclide-commons/Model')); +} + +var _createPackage; + +function _load_createPackage() { + return _createPackage = _interopRequireDefault(require('nuclide-commons-atom/createPackage')); +} + +var _textEditor; + +function _load_textEditor() { + return _textEditor = require('nuclide-commons-atom/text-editor'); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _event; -type ActivationState = {| - filterByActiveTextEditor: boolean, -|}; +function _load_event() { + return _event = require('nuclide-commons/event'); +} + +var _DiagnosticsViewModel; + +function _load_DiagnosticsViewModel() { + return _DiagnosticsViewModel = require('./DiagnosticsViewModel'); +} + +var _StatusBarTile; + +function _load_StatusBarTile() { + return _StatusBarTile = _interopRequireDefault(require('./ui/StatusBarTile')); +} + +var _gutter; + +function _load_gutter() { + return _gutter = require('./gutter'); +} -type DiagnosticsState = {| - ...ActivationState, - diagnosticUpdater: ?DiagnosticUpdater, -|}; +var _getDiagnosticDatatip; + +function _load_getDiagnosticDatatip() { + return _getDiagnosticDatatip = _interopRequireDefault(require('./getDiagnosticDatatip')); +} + +var _goToLocation; + +function _load_goToLocation() { + return _goToLocation = require('nuclide-commons-atom/go-to-location'); +} + +var _featureConfig; + +function _load_featureConfig() { + return _featureConfig = _interopRequireDefault(require('nuclide-commons-atom/feature-config')); +} + +var _destroyItemWhere; + +function _load_destroyItemWhere() { + return _destroyItemWhere = require('nuclide-commons-atom/destroyItemWhere'); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _showActionsMenu; + +function _load_showActionsMenu() { + return _showActionsMenu = _interopRequireDefault(require('./showActionsMenu')); +} + +var _showAtomLinterWarning; + +function _load_showAtomLinterWarning() { + return _showAtomLinterWarning = _interopRequireDefault(require('./showAtomLinterWarning')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const MAX_OPEN_ALL_FILES = 20; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +const SHOW_TRACES_SETTING = 'atom-ide-diagnostics-ui.showDiagnosticTraces'; class Activation { - _subscriptions: UniversalDisposable; - _model: Model; - _statusBarTile: ?StatusBarTile; - _fileDiagnostics: WeakMap>; - _globalViewStates: ?Observable; - - constructor(state: ?Object): void { - this._subscriptions = new UniversalDisposable( - this.registerOpenerAndCommand(), - this._registerActionsMenu(), - showAtomLinterWarning(), - ); - this._model = new Model({ - filterByActiveTextEditor: - idx(state, _ => _.filterByActiveTextEditor) === true, - diagnosticUpdater: null, + + constructor(state) { + var _ref; + + this._subscriptions = new (_UniversalDisposable || _load_UniversalDisposable()).default(this.registerOpenerAndCommand(), this._registerActionsMenu(), (0, (_showAtomLinterWarning || _load_showAtomLinterWarning()).default)()); + this._model = new (_Model || _load_Model()).default({ + filterByActiveTextEditor: ((_ref = state) != null ? _ref.filterByActiveTextEditor : _ref) === true, + diagnosticUpdater: null }); this._fileDiagnostics = new WeakMap(); } - consumeDatatipService(service: DatatipService): IDisposable { - const datatipProvider: DatatipProvider = { + consumeDatatipService(service) { + const datatipProvider = { // show this datatip for every type of file providerName: 'diagnostics-datatip', // Diagnostic datatips should have higher priority than most other datatips. priority: 10, datatip: (editor, position) => { - const messagesAtPosition = this._getMessagesAtPosition( - editor, - position, - ); - const {diagnosticUpdater} = this._model.state; + const messagesAtPosition = this._getMessagesAtPosition(editor, position); + const { diagnosticUpdater } = this._model.state; if (messagesAtPosition.length === 0 || diagnosticUpdater == null) { return Promise.resolve(null); } - return getDiagnosticDatatip( - editor, - position, - messagesAtPosition, - diagnosticUpdater, - ); - }, + return (0, (_getDiagnosticDatatip || _load_getDiagnosticDatatip()).default)(editor, position, messagesAtPosition, diagnosticUpdater); + } }; const disposable = service.addProvider(datatipProvider); this._subscriptions.add(disposable); return disposable; } - consumeDiagnosticUpdates(diagnosticUpdater: DiagnosticUpdater): IDisposable { + consumeDiagnosticUpdates(diagnosticUpdater) { this._getStatusBarTile().consumeDiagnosticUpdates(diagnosticUpdater); this._subscriptions.add(gutterConsumeDiagnosticUpdates(diagnosticUpdater)); // Currently, the DiagnosticsView is designed to work with only one DiagnosticUpdater. if (this._model.state.diagnosticUpdater != null) { - return new UniversalDisposable(); + return new (_UniversalDisposable || _load_UniversalDisposable()).default(); } - this._model.setState({diagnosticUpdater}); + this._model.setState({ diagnosticUpdater }); const atomCommandsDisposable = addAtomCommands(diagnosticUpdater); this._subscriptions.add(atomCommandsDisposable); this._subscriptions.add( - // Track diagnostics for all active editors. - observeTextEditors((editor: TextEditor) => { - this._fileDiagnostics.set(editor, []); - // TODO: this is actually inefficient - this filters all file events - // by their path, so this is actually O(N^2) in the number of editors. - // We should merge the store and UI packages to get direct access. - const subscription = getEditorDiagnosticUpdates( - editor, - diagnosticUpdater, - ) - .finally(() => { - this._subscriptions.remove(subscription); - this._fileDiagnostics.delete(editor); - }) - .subscribe(update => { - this._fileDiagnostics.set(editor, update.messages); - }); - this._subscriptions.add(subscription); - }), - ); - return new UniversalDisposable(atomCommandsDisposable, () => { - invariant(this._model.state.diagnosticUpdater === diagnosticUpdater); - this._model.setState({diagnosticUpdater: null}); + // Track diagnostics for all active editors. + (0, (_textEditor || _load_textEditor()).observeTextEditors)(editor => { + this._fileDiagnostics.set(editor, []); + // TODO: this is actually inefficient - this filters all file events + // by their path, so this is actually O(N^2) in the number of editors. + // We should merge the store and UI packages to get direct access. + const subscription = getEditorDiagnosticUpdates(editor, diagnosticUpdater).finally(() => { + this._subscriptions.remove(subscription); + this._fileDiagnostics.delete(editor); + }).subscribe(update => { + this._fileDiagnostics.set(editor, update.messages); + }); + this._subscriptions.add(subscription); + })); + return new (_UniversalDisposable || _load_UniversalDisposable()).default(atomCommandsDisposable, () => { + if (!(this._model.state.diagnosticUpdater === diagnosticUpdater)) { + throw new Error('Invariant violation: "this._model.state.diagnosticUpdater === diagnosticUpdater"'); + } + + this._model.setState({ diagnosticUpdater: null }); }); } - consumeStatusBar(statusBar: atom$StatusBar): void { + consumeStatusBar(statusBar) { this._getStatusBarTile().consumeStatusBar(statusBar); } - deserializeDiagnosticsViewModel(): DiagnosticsViewModel { + deserializeDiagnosticsViewModel() { return this._createDiagnosticsViewModel(); } - dispose(): void { + dispose() { this._subscriptions.dispose(); if (this._statusBarTile) { this._statusBarTile.dispose(); @@ -164,15 +222,15 @@ class Activation { } } - serialize(): ActivationState { - const {filterByActiveTextEditor} = this._model.state; + serialize() { + const { filterByActiveTextEditor } = this._model.state; return { - filterByActiveTextEditor, + filterByActiveTextEditor }; } - _createDiagnosticsViewModel(): DiagnosticsViewModel { - return new DiagnosticsViewModel(this._getGlobalViewStates()); + _createDiagnosticsViewModel() { + return new (_DiagnosticsViewModel || _load_DiagnosticsViewModel()).DiagnosticsViewModel(this._getGlobalViewStates()); } /** @@ -181,203 +239,115 @@ class Activation { * only have one copy of the diagnostics panel so this is mostly a theoretical distinction, * however, each copy should have its own sorting, filtering, etc. */ - _getGlobalViewStates(): Observable { + _getGlobalViewStates() { if (this._globalViewStates == null) { const packageStates = this._model.toObservable(); - const updaters = packageStates - .map(state => state.diagnosticUpdater) - .distinctUntilChanged(); - - const diagnosticsStream = updaters - .switchMap( - updater => - updater == null - ? Observable.of([]) - : observableFromSubscribeFunction(updater.observeMessages), - ) - .map(diagnostics => diagnostics.filter(d => d.type !== 'Hint')) - .let(fastDebounce(100)) - .startWith([]); - - const showTracesStream: Observable< - boolean, - > = (featureConfig.observeAsStream(SHOW_TRACES_SETTING): any); + const updaters = packageStates.map(state => state.diagnosticUpdater).distinctUntilChanged(); + + const diagnosticsStream = updaters.switchMap(updater => updater == null ? _rxjsBundlesRxMinJs.Observable.of([]) : (0, (_event || _load_event()).observableFromSubscribeFunction)(updater.observeMessages)).map(diagnostics => diagnostics.filter(d => d.type !== 'Hint')).let((0, (_observable || _load_observable()).fastDebounce)(100)).startWith([]); + + const showTracesStream = (_featureConfig || _load_featureConfig()).default.observeAsStream(SHOW_TRACES_SETTING); const setShowTraces = showTraces => { - featureConfig.set(SHOW_TRACES_SETTING, showTraces); + (_featureConfig || _load_featureConfig()).default.set(SHOW_TRACES_SETTING, showTraces); }; - const showDirectoryColumnStream: Observable< - boolean, - > = (featureConfig.observeAsStream( - 'atom-ide-diagnostics-ui.showDirectoryColumn', - ): any); + const showDirectoryColumnStream = (_featureConfig || _load_featureConfig()).default.observeAsStream('atom-ide-diagnostics-ui.showDirectoryColumn'); - const autoVisibilityStream: Observable< - boolean, - > = (featureConfig.observeAsStream( - 'atom-ide-diagnostics-ui.autoVisibility', - ): any); + const autoVisibilityStream = (_featureConfig || _load_featureConfig()).default.observeAsStream('atom-ide-diagnostics-ui.autoVisibility'); const pathToActiveTextEditorStream = getActiveEditorPaths(); - const filterByActiveTextEditorStream = packageStates - .map(state => state.filterByActiveTextEditor) - .distinctUntilChanged(); + const filterByActiveTextEditorStream = packageStates.map(state => state.filterByActiveTextEditor).distinctUntilChanged(); const setFilterByActiveTextEditor = filterByActiveTextEditor => { - this._model.setState({filterByActiveTextEditor}); + this._model.setState({ filterByActiveTextEditor }); }; - const supportedMessageKindsStream = updaters - .switchMap( - updater => - updater == null - ? Observable.of(new Set(['lint'])) - : observableFromSubscribeFunction( - updater.observeSupportedMessageKinds.bind(updater), - ), - ) - .distinctUntilChanged(areSetsEqual); - - const uiConfigStream = updaters.switchMap( - updater => - updater == null - ? Observable.of([]) - : observableFromSubscribeFunction( - updater.observeUiConfig.bind(updater), - ), - ); - - this._globalViewStates = Observable.combineLatest( - diagnosticsStream, - filterByActiveTextEditorStream, - pathToActiveTextEditorStream, - showTracesStream, - showDirectoryColumnStream, - autoVisibilityStream, - supportedMessageKindsStream, - uiConfigStream, - // $FlowFixMe - ( - diagnostics, - filterByActiveTextEditor, - pathToActiveTextEditor, - showTraces, - showDirectoryColumn, - autoVisibility, - supportedMessageKinds, - uiConfig, - ) => ({ - diagnostics, - filterByActiveTextEditor, - pathToActiveTextEditor, - showTraces, - showDirectoryColumn, - autoVisibility, - onShowTracesChange: setShowTraces, - onFilterByActiveTextEditorChange: setFilterByActiveTextEditor, - supportedMessageKinds, - uiConfig, - }), - ); + const supportedMessageKindsStream = updaters.switchMap(updater => updater == null ? _rxjsBundlesRxMinJs.Observable.of(new Set(['lint'])) : (0, (_event || _load_event()).observableFromSubscribeFunction)(updater.observeSupportedMessageKinds.bind(updater))).distinctUntilChanged((_collection || _load_collection()).areSetsEqual); + + const uiConfigStream = updaters.switchMap(updater => updater == null ? _rxjsBundlesRxMinJs.Observable.of([]) : (0, (_event || _load_event()).observableFromSubscribeFunction)(updater.observeUiConfig.bind(updater))); + + this._globalViewStates = _rxjsBundlesRxMinJs.Observable.combineLatest(diagnosticsStream, filterByActiveTextEditorStream, pathToActiveTextEditorStream, showTracesStream, showDirectoryColumnStream, autoVisibilityStream, supportedMessageKindsStream, uiConfigStream, + // $FlowFixMe + (diagnostics, filterByActiveTextEditor, pathToActiveTextEditor, showTraces, showDirectoryColumn, autoVisibility, supportedMessageKinds, uiConfig) => ({ + diagnostics, + filterByActiveTextEditor, + pathToActiveTextEditor, + showTraces, + showDirectoryColumn, + autoVisibility, + onShowTracesChange: setShowTraces, + onFilterByActiveTextEditorChange: setFilterByActiveTextEditor, + supportedMessageKinds, + uiConfig + })); } return this._globalViewStates; } - registerOpenerAndCommand(): IDisposable { - const commandDisposable = atom.commands.add( - 'atom-workspace', - 'diagnostics:toggle-table', - () => { - atom.workspace.toggle(WORKSPACE_VIEW_URI); - }, - ); - return new UniversalDisposable( - atom.workspace.addOpener(uri => { - if (uri === WORKSPACE_VIEW_URI) { - return this._createDiagnosticsViewModel(); - } - }), - () => { - destroyItemWhere(item => item instanceof DiagnosticsViewModel); - }, - commandDisposable, - ); + registerOpenerAndCommand() { + const commandDisposable = atom.commands.add('atom-workspace', 'diagnostics:toggle-table', () => { + atom.workspace.toggle((_DiagnosticsViewModel || _load_DiagnosticsViewModel()).WORKSPACE_VIEW_URI); + }); + return new (_UniversalDisposable || _load_UniversalDisposable()).default(atom.workspace.addOpener(uri => { + if (uri === (_DiagnosticsViewModel || _load_DiagnosticsViewModel()).WORKSPACE_VIEW_URI) { + return this._createDiagnosticsViewModel(); + } + }), () => { + (0, (_destroyItemWhere || _load_destroyItemWhere()).destroyItemWhere)(item => item instanceof (_DiagnosticsViewModel || _load_DiagnosticsViewModel()).DiagnosticsViewModel); + }, commandDisposable); } - _registerActionsMenu(): IDisposable { - return atom.commands.add( - 'atom-text-editor', - 'diagnostics:show-actions-at-position', - () => { - const editor = atom.workspace.getActiveTextEditor(); - const {diagnosticUpdater} = this._model.state; - if (editor == null || diagnosticUpdater == null) { - return; - } - const position = editor.getCursorBufferPosition(); - const messagesAtPosition = this._getMessagesAtPosition( - editor, - position, - ); - if (messagesAtPosition.length === 0) { - return; - } - showActionsMenu( - editor, - position, - messagesAtPosition, - diagnosticUpdater, - ); - }, - ); + _registerActionsMenu() { + return atom.commands.add('atom-text-editor', 'diagnostics:show-actions-at-position', () => { + const editor = atom.workspace.getActiveTextEditor(); + const { diagnosticUpdater } = this._model.state; + if (editor == null || diagnosticUpdater == null) { + return; + } + const position = editor.getCursorBufferPosition(); + const messagesAtPosition = this._getMessagesAtPosition(editor, position); + if (messagesAtPosition.length === 0) { + return; + } + (0, (_showActionsMenu || _load_showActionsMenu()).default)(editor, position, messagesAtPosition, diagnosticUpdater); + }); } - _getStatusBarTile(): StatusBarTile { + _getStatusBarTile() { if (!this._statusBarTile) { - this._statusBarTile = new StatusBarTile(); + this._statusBarTile = new (_StatusBarTile || _load_StatusBarTile()).default(); } return this._statusBarTile; } - _getMessagesAtPosition( - editor: atom$TextEditor, - position: atom$Point, - ): Array { + _getMessagesAtPosition(editor, position) { const messagesForFile = this._fileDiagnostics.get(editor); if (messagesForFile == null) { return []; } - return messagesForFile.filter( - message => message.range != null && message.range.containsPoint(position), - ); + return messagesForFile.filter(message => message.range != null && message.range.containsPoint(position)); } } -function gutterConsumeDiagnosticUpdates( - diagnosticUpdater: DiagnosticUpdater, -): IDisposable { - const subscriptions = new UniversalDisposable(); - subscriptions.add( - observeTextEditors((editor: TextEditor) => { - const subscription = getEditorDiagnosticUpdates(editor, diagnosticUpdater) - .finally(() => { - subscriptions.remove(subscription); - }) - .subscribe(update => { - // Although the subscription should be cleaned up on editor destroy, - // the very act of destroying the editor can trigger diagnostic updates. - // Thus this callback can still be triggered after the editor is destroyed. - if (!editor.isDestroyed()) { - applyUpdateToEditor(editor, update, diagnosticUpdater); - } - }); - subscriptions.add(subscription); - }), - ); +function gutterConsumeDiagnosticUpdates(diagnosticUpdater) { + const subscriptions = new (_UniversalDisposable || _load_UniversalDisposable()).default(); + subscriptions.add((0, (_textEditor || _load_textEditor()).observeTextEditors)(editor => { + const subscription = getEditorDiagnosticUpdates(editor, diagnosticUpdater).finally(() => { + subscriptions.remove(subscription); + }).subscribe(update => { + // Although the subscription should be cleaned up on editor destroy, + // the very act of destroying the editor can trigger diagnostic updates. + // Thus this callback can still be triggered after the editor is destroyed. + if (!editor.isDestroyed()) { + (0, (_gutter || _load_gutter()).applyUpdateToEditor)(editor, update, diagnosticUpdater); + } + }); + subscriptions.add(subscription); + })); return subscriptions; } -function addAtomCommands(diagnosticUpdater: DiagnosticUpdater): IDisposable { +function addAtomCommands(diagnosticUpdater) { const fixAllInCurrentFile = () => { const editor = atom.workspace.getActiveTextEditor(); if (editor == null) { @@ -387,52 +357,34 @@ function addAtomCommands(diagnosticUpdater: DiagnosticUpdater): IDisposable { if (path == null) { return; } - analytics.track('diagnostics-autofix-all-in-file'); + (_analytics || _load_analytics()).default.track('diagnostics-autofix-all-in-file'); diagnosticUpdater.applyFixesForFile(path); }; const openAllFilesWithErrors = () => { - analytics.track('diagnostics-panel-open-all-files-with-errors'); - observableFromSubscribeFunction(diagnosticUpdater.observeMessages) - .first() - .subscribe((messages: Array) => { - const errorsToOpen = getTopMostErrorLocationsByFilePath(messages); - - if (errorsToOpen.size > MAX_OPEN_ALL_FILES) { - atom.notifications.addError( - `Diagnostics: Will not open more than ${MAX_OPEN_ALL_FILES} files`, - ); - return; - } + (_analytics || _load_analytics()).default.track('diagnostics-panel-open-all-files-with-errors'); + (0, (_event || _load_event()).observableFromSubscribeFunction)(diagnosticUpdater.observeMessages).first().subscribe(messages => { + const errorsToOpen = getTopMostErrorLocationsByFilePath(messages); - const column = 0; - errorsToOpen.forEach((line, uri) => goToLocation(uri, {line, column})); - }); + if (errorsToOpen.size > MAX_OPEN_ALL_FILES) { + atom.notifications.addError(`Diagnostics: Will not open more than ${MAX_OPEN_ALL_FILES} files`); + return; + } + + const column = 0; + errorsToOpen.forEach((line, uri) => (0, (_goToLocation || _load_goToLocation()).goToLocation)(uri, { line, column })); + }); }; - return new UniversalDisposable( - atom.commands.add( - 'atom-workspace', - 'diagnostics:fix-all-in-current-file', - fixAllInCurrentFile, - ), - atom.commands.add( - 'atom-workspace', - 'diagnostics:open-all-files-with-errors', - openAllFilesWithErrors, - ), - new KeyboardShortcuts(diagnosticUpdater), - ); + return new (_UniversalDisposable || _load_UniversalDisposable()).default(atom.commands.add('atom-workspace', 'diagnostics:fix-all-in-current-file', fixAllInCurrentFile), atom.commands.add('atom-workspace', 'diagnostics:open-all-files-with-errors', openAllFilesWithErrors), new (_KeyboardShortcuts || _load_KeyboardShortcuts()).default(diagnosticUpdater)); } -function getTopMostErrorLocationsByFilePath( - messages: Array, -): Map { - const errorLocations: Map = new Map(); +function getTopMostErrorLocationsByFilePath(messages) { + const errorLocations = new Map(); messages.forEach(message => { const filePath = message.filePath; - if (nuclideUri.endsWithSeparator(filePath)) { + if ((_nuclideUri || _load_nuclideUri()).default.endsWithSeparator(filePath)) { return; } @@ -451,58 +403,27 @@ function getTopMostErrorLocationsByFilePath( return errorLocations; } -function getActiveEditorPaths(): Observable { +function getActiveEditorPaths() { const center = atom.workspace.getCenter(); - return ( - observableFromSubscribeFunction(center.observeActivePaneItem.bind(center)) - .map(paneItem => (isValidTextEditor(paneItem) ? paneItem : null)) - // We want the stream to contain the last valid text editor. Normally that means just ignoring - // non-editors, except initially, when there hasn't been an active editor yet. - .filter((paneItem, index) => paneItem != null || index === 0) - .switchMap(textEditor_ => { - const textEditor: ?atom$TextEditor = (textEditor_: any); - if (textEditor == null) { - return Observable.of(null); - } - // An observable that emits the editor path and then, when the editor's destroyed, null. - return Observable.concat( - Observable.of(textEditor.getPath()), - observableFromSubscribeFunction( - textEditor.onDidDestroy.bind(textEditor), - ) - .take(1) - .mapTo(null), - ); - }) - .distinctUntilChanged() - ); + return (0, (_event || _load_event()).observableFromSubscribeFunction)(center.observeActivePaneItem.bind(center)).map(paneItem => (0, (_textEditor || _load_textEditor()).isValidTextEditor)(paneItem) ? paneItem : null) + // We want the stream to contain the last valid text editor. Normally that means just ignoring + // non-editors, except initially, when there hasn't been an active editor yet. + .filter((paneItem, index) => paneItem != null || index === 0).switchMap(textEditor_ => { + const textEditor = textEditor_; + if (textEditor == null) { + return _rxjsBundlesRxMinJs.Observable.of(null); + } + // An observable that emits the editor path and then, when the editor's destroyed, null. + return _rxjsBundlesRxMinJs.Observable.concat(_rxjsBundlesRxMinJs.Observable.of(textEditor.getPath()), (0, (_event || _load_event()).observableFromSubscribeFunction)(textEditor.onDidDestroy.bind(textEditor)).take(1).mapTo(null)); + }).distinctUntilChanged(); } -function getEditorDiagnosticUpdates( - editor: atom$TextEditor, - diagnosticUpdater: DiagnosticUpdater, -): Observable { - return observableFromSubscribeFunction(editor.onDidChangePath.bind(editor)) - .startWith(editor.getPath()) - .switchMap( - filePath => - filePath != null - ? observableFromSubscribeFunction(cb => - diagnosticUpdater.observeFileMessages(filePath, cb), - ) - : Observable.empty(), - ) - .map(diagnosticMessages => { - return { - ...diagnosticMessages, - messages: diagnosticMessages.messages.filter( - diagnostic => diagnostic.type !== 'Hint', - ), - }; - }) - .takeUntil( - observableFromSubscribeFunction(editor.onDidDestroy.bind(editor)), - ); +function getEditorDiagnosticUpdates(editor, diagnosticUpdater) { + return (0, (_event || _load_event()).observableFromSubscribeFunction)(editor.onDidChangePath.bind(editor)).startWith(editor.getPath()).switchMap(filePath => filePath != null ? (0, (_event || _load_event()).observableFromSubscribeFunction)(cb => diagnosticUpdater.observeFileMessages(filePath, cb)) : _rxjsBundlesRxMinJs.Observable.empty()).map(diagnosticMessages => { + return Object.assign({}, diagnosticMessages, { + messages: diagnosticMessages.messages.filter(diagnostic => diagnostic.type !== 'Hint') + }); + }).takeUntil((0, (_event || _load_event()).observableFromSubscribeFunction)(editor.onDidDestroy.bind(editor))); } -createPackage(module.exports, Activation); +(0, (_createPackage || _load_createPackage()).default)(module.exports, Activation); \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showActionsMenu.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showActionsMenu.js index 71a29dee..13ca501b 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showActionsMenu.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showActionsMenu.js @@ -1,111 +1,103 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type { - DiagnosticUpdater, - DiagnosticMessage, -} from '../../atom-ide-diagnostics/lib/types'; - -import invariant from 'assert'; -import electron from 'electron'; -import {Observable} from 'rxjs'; -import {arrayCompact, arrayFlatten} from 'nuclide-commons/collection'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; - -const {remote} = electron; -invariant(remote != null); +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = showActionsMenu; + +var _electron = _interopRequireDefault(require('electron')); + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +var _collection; + +function _load_collection() { + return _collection = require('nuclide-commons/collection'); +} + +var _event; + +function _load_event() { + return _event = require('nuclide-commons/event'); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const { remote } = _electron.default; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +if (!(remote != null)) { + throw new Error('Invariant violation: "remote != null"'); +} const CODE_ACTIONS_TIMEOUT = 2000; -export default function showActionsMenu( - editor: TextEditor, - position: atom$Point, - messagesAtPosition: Array, - diagnosticUpdater: DiagnosticUpdater, -): IDisposable { +function showActionsMenu(editor, position, messagesAtPosition, diagnosticUpdater) { diagnosticUpdater.fetchCodeActions(editor, messagesAtPosition); - return new UniversalDisposable( - observableFromSubscribeFunction(cb => - diagnosticUpdater.observeCodeActionsForMessage(cb), - ) - .filter(codeActionsForMessage => { - return messagesAtPosition.every(message => - codeActionsForMessage.has(message), - ); - }) - .take(1) - .race(Observable.of(new WeakMap()).delay(CODE_ACTIONS_TIMEOUT)) - .subscribe(codeActionsForMessage => { - const currentWindow = remote.getCurrentWindow(); - const menu = new remote.Menu(); - const fixes = arrayCompact( - messagesAtPosition.map(message => { - const {fix} = message; - if (fix == null) { - return null; - } - const fixTitle = fix.title == null ? 'Fix' : fix.title; - return { - title: `${fixTitle} (${message.providerName})`, - apply: () => diagnosticUpdater.applyFix(message), - }; - }), - ); - const actions = arrayFlatten( - messagesAtPosition.map(message => { - const codeActions = codeActionsForMessage.get(message); - if (codeActions == null) { - return []; - } - return Array.from(codeActions).map(([title, codeAction]) => ({ - title, - apply: () => codeAction.apply(), - })); - }), - ); - - [...fixes, ...actions].forEach(action => { - menu.append( - new remote.MenuItem({ - type: 'normal', - label: action.title, - click: () => { - action.apply(); - }, - }), - ); - }); - - const screenPosition = editor.screenPositionForBufferPosition(position); - const editorView = atom.views.getView(editor); - const pixelPosition = editorView.pixelPositionForScreenPosition( - screenPosition, - ); - // Pixel coordinates are relative to the editor's scroll view. - const scrollView = editorView.querySelector('.scroll-view'); - invariant(scrollView != null); - const boundingRect = scrollView.getBoundingClientRect(); - menu.popup( - currentWindow, - Math.round( - boundingRect.left + pixelPosition.left - editorView.getScrollLeft(), - ), - Math.round( - boundingRect.top + pixelPosition.top - editorView.getScrollTop(), - ), - 0, - ); - }), - ); -} + return new (_UniversalDisposable || _load_UniversalDisposable()).default((0, (_event || _load_event()).observableFromSubscribeFunction)(cb => diagnosticUpdater.observeCodeActionsForMessage(cb)).filter(codeActionsForMessage => { + return messagesAtPosition.every(message => codeActionsForMessage.has(message)); + }).take(1).race(_rxjsBundlesRxMinJs.Observable.of(new WeakMap()).delay(CODE_ACTIONS_TIMEOUT)).subscribe(codeActionsForMessage => { + const currentWindow = remote.getCurrentWindow(); + const menu = new remote.Menu(); + const fixes = (0, (_collection || _load_collection()).arrayCompact)(messagesAtPosition.map(message => { + const { fix } = message; + if (fix == null) { + return null; + } + const fixTitle = fix.title == null ? 'Fix' : fix.title; + return { + title: `${fixTitle} (${message.providerName})`, + apply: () => diagnosticUpdater.applyFix(message) + }; + })); + const actions = (0, (_collection || _load_collection()).arrayFlatten)(messagesAtPosition.map(message => { + const codeActions = codeActionsForMessage.get(message); + if (codeActions == null) { + return []; + } + return Array.from(codeActions).map(([title, codeAction]) => ({ + title, + apply: () => codeAction.apply() + })); + })); + + [...fixes, ...actions].forEach(action => { + menu.append(new remote.MenuItem({ + type: 'normal', + label: action.title, + click: () => { + action.apply(); + } + })); + }); + + const screenPosition = editor.screenPositionForBufferPosition(position); + const editorView = atom.views.getView(editor); + const pixelPosition = editorView.pixelPositionForScreenPosition(screenPosition); + // Pixel coordinates are relative to the editor's scroll view. + const scrollView = editorView.querySelector('.scroll-view'); + + if (!(scrollView != null)) { + throw new Error('Invariant violation: "scrollView != null"'); + } + + const boundingRect = scrollView.getBoundingClientRect(); + menu.popup(currentWindow, Math.round(boundingRect.left + pixelPosition.left - editorView.getScrollLeft()), Math.round(boundingRect.top + pixelPosition.top - editorView.getScrollTop()), 0); + })); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showAtomLinterWarning.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showAtomLinterWarning.js index 38ec763f..071cf587 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showAtomLinterWarning.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/showAtomLinterWarning.js @@ -1,3 +1,32 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = showAtomLinterWarning; + +var _featureConfig; + +function _load_featureConfig() { + return _featureConfig = _interopRequireDefault(require('nuclide-commons-atom/feature-config')); +} + +var _event; + +function _load_event() { + return _event = require('nuclide-commons/event'); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,85 +35,52 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import featureConfig from 'nuclide-commons-atom/feature-config'; -import {observableFromSubscribeFunction} from 'nuclide-commons/event'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import {Observable} from 'rxjs'; - const LINTER_PACKAGE = 'linter'; -function observePackageIsEnabled(): Observable { - return Observable.merge( - Observable.of(atom.packages.isPackageActive(LINTER_PACKAGE)), - observableFromSubscribeFunction( - atom.packages.onDidActivatePackage.bind(atom.packages), - ) - .filter(pkg => pkg.name === LINTER_PACKAGE) - .mapTo(true), - observableFromSubscribeFunction( - atom.packages.onDidDeactivatePackage.bind(atom.packages), - ) - .filter(pkg => pkg.name === LINTER_PACKAGE) - .mapTo(false), - ); +function observePackageIsEnabled() { + return _rxjsBundlesRxMinJs.Observable.merge(_rxjsBundlesRxMinJs.Observable.of(atom.packages.isPackageActive(LINTER_PACKAGE)), (0, (_event || _load_event()).observableFromSubscribeFunction)(atom.packages.onDidActivatePackage.bind(atom.packages)).filter(pkg => pkg.name === LINTER_PACKAGE).mapTo(true), (0, (_event || _load_event()).observableFromSubscribeFunction)(atom.packages.onDidDeactivatePackage.bind(atom.packages)).filter(pkg => pkg.name === LINTER_PACKAGE).mapTo(false)); } -function disableLinter(): void { +function disableLinter() { atom.packages.disablePackage(LINTER_PACKAGE); } -function disableDiagnostics(): void { - featureConfig.set('use.atom-ide-diagnostics-ui', false); +function disableDiagnostics() { + (_featureConfig || _load_featureConfig()).default.set('use.atom-ide-diagnostics-ui', false); } -export default function showAtomLinterWarning(): IDisposable { - const packageName = featureConfig.getPackageName(); - return new UniversalDisposable( - observePackageIsEnabled() - .distinctUntilChanged() - .switchMap(enabled => { - if (!enabled) { - return Observable.empty(); +function showAtomLinterWarning() { + const packageName = (_featureConfig || _load_featureConfig()).default.getPackageName(); + return new (_UniversalDisposable || _load_UniversalDisposable()).default(observePackageIsEnabled().distinctUntilChanged().switchMap(enabled => { + if (!enabled) { + return _rxjsBundlesRxMinJs.Observable.empty(); + } + const notification = atom.notifications.addInfo('Choose a linter UI', { + description: 'You have both `linter` and `atom-ide-diagnostics` enabled, which will both ' + 'display lint results for Linter-based packages.\n\n' + 'To avoid duplicate results, please disable one of the packages.' + (packageName === 'nuclide' ? '\n\nNote that Flow and Hack errors are not compatible with `linter`.' : ''), + dismissable: true, + buttons: [{ + text: 'Disable Linter', + onDidClick() { + disableLinter(); } - const notification = atom.notifications.addInfo('Choose a linter UI', { - description: - 'You have both `linter` and `atom-ide-diagnostics` enabled, which will both ' + - 'display lint results for Linter-based packages.\n\n' + - 'To avoid duplicate results, please disable one of the packages.' + - (packageName === 'nuclide' - ? '\n\nNote that Flow and Hack errors are not compatible with `linter`.' - : ''), - dismissable: true, - buttons: [ - { - text: 'Disable Linter', - onDidClick() { - disableLinter(); - }, - }, - { - text: 'Disable Diagnostics', - onDidClick() { - disableDiagnostics(); - atom.notifications.addInfo('Re-enabling Diagnostics', { - description: - 'To re-enable diagnostics, please enable "Diagnostics" under the "Enabled Features" ' + - `section in \`${packageName}\` settings.`, - }); - }, - }, - ], - }); - return Observable.create(() => ({ - unsubscribe() { - notification.dismiss(); - }, - })); - }) - .subscribe(), - ); -} + }, { + text: 'Disable Diagnostics', + onDidClick() { + disableDiagnostics(); + atom.notifications.addInfo('Re-enabling Diagnostics', { + description: 'To re-enable diagnostics, please enable "Diagnostics" under the "Enabled Features" ' + `section in \`${packageName}\` settings.` + }); + } + }] + }); + return _rxjsBundlesRxMinJs.Observable.create(() => ({ + unsubscribe() { + notification.dismiss(); + } + })); + }).subscribe()); +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/sortDiagnostics.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/sortDiagnostics.js index c5ad3aaa..4e6d7e31 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/sortDiagnostics.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/sortDiagnostics.js @@ -1,3 +1,14 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = sortDiagnostics; + + +/* + * Sorts the diagnostics according to given column and sort direction + */ /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,35 +17,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {Row} from 'nuclide-commons-ui/Table'; -import type { - DiagnosticMessageKind, - DiagnosticMessageType, -} from '../../atom-ide-diagnostics/lib/types'; -import type {DisplayDiagnostic} from './ui/DiagnosticsTable'; +function sortDiagnostics(diagnostics, sortedColumnName, sortDescending) { + const compare = SORT_FUNCTIONS[sortedColumnName]; -import invariant from 'assert'; + if (!(compare != null)) { + throw new Error('Invariant violation: "compare != null"'); + } + // Don't sort in place. -type DiagnosticsComparison = ( - a: Row, - b: Row, -) => number; -/* - * Sorts the diagnostics according to given column and sort direction - */ -export default function sortDiagnostics( - diagnostics: Array>, - sortedColumnName: $Keys, - sortDescending: boolean, -): Array> { - const compare = SORT_FUNCTIONS[sortedColumnName]; - invariant(compare != null); - // Don't sort in place. const sorted = diagnostics.slice().sort(compare); // We can't just reverse the sign of the comparison function because that would maintain the // ordering of "equal" items with respect to eachother. @@ -42,53 +37,19 @@ export default function sortDiagnostics( } const SORT_FUNCTIONS = { - classification: compose( - compareClassification, - compareSource, - comparePath, - compareDescription, - ), - providerName: compose( - compareSource, - compareClassification, - compareDescription, - comparePath, - ), - description: compose( - compareDescription, - compareSource, - compareClassification, - comparePath, - ), - dir: compose( - comparePath, - compareSource, - compareClassification, - compareDescription, - ), - location: compose( - compareBasename, - comparePath, - compareClassification, - compareSource, - compareDescription, - ), - line: compose( - compareBasename, - comparePath, - compareClassification, - compareSource, - compareDescription, - ), + classification: compose(compareClassification, compareSource, comparePath, compareDescription), + providerName: compose(compareSource, compareClassification, compareDescription, comparePath), + description: compose(compareDescription, compareSource, compareClassification, comparePath), + dir: compose(comparePath, compareSource, compareClassification, compareDescription), + location: compose(compareBasename, comparePath, compareClassification, compareSource, compareDescription), + line: compose(compareBasename, comparePath, compareClassification, compareSource, compareDescription) }; /** * Compose comparison functions so that, when one identifies the items as equal, the subsequent * functions are used to resolve the abiguity. */ -function compose( - ...comparisons: Array -): DiagnosticsComparison { +function compose(...comparisons) { return (a, b) => { for (const compare of comparisons) { const val = compare(a, b); @@ -100,28 +61,13 @@ function compose( }; } -function compareClassification( - a: Row, - b: Row, -): number { - return ( - compareClassificationKind( - a.data.classification.kind, - b.data.classification.kind, - ) || - compareClassificationSeverity( - a.data.classification.severity, - b.data.classification.severity, - ) - ); +function compareClassification(a, b) { + return compareClassificationKind(a.data.classification.kind, b.data.classification.kind) || compareClassificationSeverity(a.data.classification.severity, b.data.classification.severity); } const KIND_ORDER = ['review', 'lint']; -function compareClassificationKind( - a: ?DiagnosticMessageKind, - b: ?DiagnosticMessageKind, -): number { +function compareClassificationKind(a, b) { const aKind = a || 'lint'; const bKind = b || 'lint'; return KIND_ORDER.indexOf(aKind) - KIND_ORDER.indexOf(bKind); @@ -129,31 +75,19 @@ function compareClassificationKind( const SEVERITY_ORDER = ['Info', 'Warning', 'Error']; -function compareClassificationSeverity( - a: DiagnosticMessageType, - b: DiagnosticMessageType, -): number { +function compareClassificationSeverity(a, b) { return SEVERITY_ORDER.indexOf(a) - SEVERITY_ORDER.indexOf(b); } -function compareSource( - a: Row, - b: Row, -): number { +function compareSource(a, b) { return compareStrings(a.data.providerName, b.data.providerName); } -function compareDescription( - a: Row, - b: Row, -): number { +function compareDescription(a, b) { return compareStrings(a.data.description.text, b.data.description.text); } -function comparePath( - a: Row, - b: Row, -): number { +function comparePath(a, b) { const aLocation = a.data.location; const bLocation = b.data.location; if (aLocation == null && bLocation == null) { @@ -169,17 +103,12 @@ function comparePath( if (pathComparison !== 0) { return pathComparison; } - const aLine = - aLocation.locationInFile == null ? 0 : aLocation.locationInFile.line; - const bLine = - bLocation.locationInFile == null ? 0 : bLocation.locationInFile.line; + const aLine = aLocation.locationInFile == null ? 0 : aLocation.locationInFile.line; + const bLine = bLocation.locationInFile == null ? 0 : bLocation.locationInFile.line; return compareNumbers(aLine, bLine); } -function compareBasename( - a: Row, - b: Row, -): number { +function compareBasename(a, b) { const aLocationInFile = a.data.location && a.data.location.locationInFile; const bLocationInFile = b.data.location && b.data.location.locationInFile; if (aLocationInFile == null && bLocationInFile == null) { @@ -191,16 +120,13 @@ function compareBasename( if (bLocationInFile == null) { return 1; } - return ( - compareStrings(aLocationInFile.basename, bLocationInFile.basename) || - compareNumbers(aLocationInFile.line, bLocationInFile.line) - ); + return compareStrings(aLocationInFile.basename, bLocationInFile.basename) || compareNumbers(aLocationInFile.line, bLocationInFile.line); } -function compareStrings(a: string, b: string): number { +function compareStrings(a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); } -function compareNumbers(a: number, b: number): number { +function compareNumbers(a, b) { return a - b; -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/types.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/types.js index b56120b0..a726efc4 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/types.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/types.js @@ -1,40 +1 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type { - DiagnosticMessage, - DiagnosticMessageKind, - UiConfig, -} from '../../atom-ide-diagnostics/lib/types'; - -// We group diagnostics based on kind and severity. -export type DiagnosticGroup = - | 'errors' - | 'warnings' - | 'info' - | 'review' - | 'action'; - -// State that's shared between every diagnostics panel instance. -export type GlobalViewState = { - diagnostics: Array, - pathToActiveTextEditor: ?NuclideUri, - filterByActiveTextEditor: boolean, - onFilterByActiveTextEditorChange: (isChecked: boolean) => mixed, - showDirectoryColumn: boolean, - autoVisibility: boolean, - showTraces: boolean, - onShowTracesChange: (isChecked: boolean) => mixed, - supportedMessageKinds: Set, - uiConfig: UiConfig, -}; +'use strict'; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsCodeActions.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsCodeActions.js index 4465e985..ce88845b 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsCodeActions.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsCodeActions.js @@ -1,64 +1,80 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -import type {CodeAction} from '../../../atom-ide-code-actions/lib/types'; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = DiagnosticsCodeActions; -import {TextEditor} from 'atom'; -import * as React from 'react'; -import {Button} from 'nuclide-commons-ui/Button'; -import {ButtonGroup} from 'nuclide-commons-ui/ButtonGroup'; +var _atom = require('atom'); + +var _react = _interopRequireWildcard(require('react')); + +var _Button; + +function _load_Button() { + return _Button = require('nuclide-commons-ui/Button'); +} + +var _ButtonGroup; + +function _load_ButtonGroup() { + return _ButtonGroup = require('nuclide-commons-ui/ButtonGroup'); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } // Maximum number of CodeActions to show for a given Diagnostic. -const MAX_CODE_ACTIONS = 4; +const MAX_CODE_ACTIONS = 4; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ -export default function DiagnosticsCodeActions(props: { - codeActions: Map, -}): React.Element { - return ( -
- {Array.from(props.codeActions.entries()) - .splice(0, MAX_CODE_ACTIONS) - // TODO: (seansegal) T21130259 Display a "more" indicator when there are many CodeActions. - .map(([title, codeAction], i) => { - return ( - - - - ); - })} -
+function DiagnosticsCodeActions(props) { + return _react.createElement( + 'div', + { className: 'diagnostics-code-actions' }, + Array.from(props.codeActions.entries()).splice(0, MAX_CODE_ACTIONS) + // TODO: (seansegal) T21130259 Display a "more" indicator when there are many CodeActions. + .map(([title, codeAction], i) => { + return _react.createElement( + (_ButtonGroup || _load_ButtonGroup()).ButtonGroup, + { key: i }, + _react.createElement( + (_Button || _load_Button()).Button, + { + className: 'diagnostics-code-action-button', + size: 'EXTRA_SMALL', + onClick: () => { + // TODO: (seansegal) T21130332 Display CodeAction status indicators + codeAction.apply().catch(handleCodeActionFailure).then(() => { + // Return focus to the editor after clicking. + const activeItem = atom.workspace.getActivePaneItem(); + if (activeItem && activeItem instanceof _atom.TextEditor) { + activeItem.element.focus(); + } + }); + } }, + _react.createElement( + 'span', + { className: 'inline-block' }, + title + ) + ) + ); + }) ); } -function handleCodeActionFailure(error: ?Error) { +function handleCodeActionFailure(error) { atom.notifications.addWarning('Code action could not be applied', { description: error ? error.message : '', - dismissable: true, + dismissable: true }); -} +} \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessage.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessage.js index e8cb1d46..768d4cb0 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessage.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessage.js @@ -1,3 +1,38 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DiagnosticsMessageNoHeader = exports.DiagnosticsMessage = undefined; + +var _react = _interopRequireWildcard(require('react')); + +var _Button; + +function _load_Button() { + return _Button = require('nuclide-commons-ui/Button'); +} + +var _ButtonGroup; + +function _load_ButtonGroup() { + return _ButtonGroup = require('nuclide-commons-ui/ButtonGroup'); +} + +var _DiagnosticsMessageText; + +function _load_DiagnosticsMessageText() { + return _DiagnosticsMessageText = require('./DiagnosticsMessageText'); +} + +var _DiagnosticsTraceItem; + +function _load_DiagnosticsTraceItem() { + return _DiagnosticsTraceItem = require('./DiagnosticsTraceItem'); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,37 +41,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {DiagnosticMessage} from '../../../atom-ide-diagnostics/lib/types'; - -import * as React from 'react'; -import {Button, ButtonTypes} from 'nuclide-commons-ui/Button'; -import {ButtonGroup} from 'nuclide-commons-ui/ButtonGroup'; -import {DiagnosticsMessageText} from './DiagnosticsMessageText'; -import {DiagnosticsTraceItem} from './DiagnosticsTraceItem'; - -type DiagnosticsMessageProps = { - // these are processed in traceElements below - /* eslint-disable react/no-unused-prop-types */ - message: DiagnosticMessage, - goToLocation: (path: string, line: number) => mixed, - fixer: (message: DiagnosticMessage) => void, - children?: React.Node, - /* eslint-enable react/no-unused-prop-types */ -}; - const PROVIDER_CLASS_NAME = { Error: 'highlight-error', Warning: 'highlight-warning', Info: 'highlight-info', - Hint: '', + Hint: '' }; -function diagnosticHeader(props: DiagnosticsMessageProps) { - const {message, fixer} = props; +function diagnosticHeader(props) { + const { message, fixer } = props; const providerClassName = PROVIDER_CLASS_NAME[message.type]; let fixButton = null; if (message.fix != null) { @@ -44,55 +61,63 @@ function diagnosticHeader(props: DiagnosticsMessageProps) { fixer(message); }; const speculative = message.fix.speculative === true; - const buttonType = speculative ? undefined : ButtonTypes.SUCCESS; - fixButton = ( - + const buttonType = speculative ? undefined : (_Button || _load_Button()).ButtonTypes.SUCCESS; + fixButton = _react.createElement( + (_Button || _load_Button()).Button, + { buttonType: buttonType, size: 'EXTRA_SMALL', onClick: applyFix }, + // flowlint-next-line sketchy-null-string:off + message.fix.title || 'Fix' ); } - return ( -
- {fixButton} - {message.providerName} -
+ return _react.createElement( + 'div', + { className: 'diagnostics-popup-header' }, + _react.createElement( + (_ButtonGroup || _load_ButtonGroup()).ButtonGroup, + null, + fixButton + ), + _react.createElement( + 'span', + { className: providerClassName }, + message.providerName + ) ); } -function traceElements(props: DiagnosticsMessageProps) { - const {message, goToLocation} = props; - return message.trace && message.trace.length ? ( -
- {message.trace.map((traceItem, i) => ( - - ))} -
+function traceElements(props) { + const { message, goToLocation } = props; + return message.trace && message.trace.length ? _react.createElement( + 'div', + { className: 'diagnostics-popup-trace' }, + message.trace.map((traceItem, i) => _react.createElement((_DiagnosticsTraceItem || _load_DiagnosticsTraceItem()).DiagnosticsTraceItem, { + key: i, + trace: traceItem, + goToLocation: goToLocation + })) ) : null; } -export const DiagnosticsMessage = (props: DiagnosticsMessageProps) => { - return ( -
- {diagnosticHeader(props)} -
- -
- {traceElements(props)} - {props.children} -
+const DiagnosticsMessage = exports.DiagnosticsMessage = props => { + return _react.createElement( + 'div', + null, + diagnosticHeader(props), + _react.createElement( + 'div', + { className: 'diagnostics-popup-message' }, + _react.createElement((_DiagnosticsMessageText || _load_DiagnosticsMessageText()).DiagnosticsMessageText, { message: props.message }) + ), + traceElements(props), + props.children ); }; -export const DiagnosticsMessageNoHeader = (props: DiagnosticsMessageProps) => { - return ( -
- - {traceElements(props)} -
+const DiagnosticsMessageNoHeader = exports.DiagnosticsMessageNoHeader = props => { + return _react.createElement( + 'div', + null, + _react.createElement((_DiagnosticsMessageText || _load_DiagnosticsMessageText()).DiagnosticsMessageText, { message: props.message }), + traceElements(props) ); -}; +}; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessageText.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessageText.js index 303ac8b3..382b960a 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessageText.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsMessageText.js @@ -1,39 +1,19 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -import invariant from 'assert'; -import * as React from 'react'; -import {shell} from 'electron'; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DiagnosticsMessageText = undefined; +exports.separateUrls = separateUrls; -type DiagnosticsMessageTextProps = { - preserveNewlines?: boolean, // defaults to true - message: { - html?: string, - text?: string, - }, -}; +var _react = _interopRequireWildcard(require('react')); -type UrlOrText = - | { - isUrl: true, - url: string, - } - | { - isUrl: false, - text: string, - }; +var _electron = require('electron'); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } // Exported for testing. -export function separateUrls(message: string): Array { +function separateUrls(message) { // Don't match periods at the end of URLs, because those are usually just to // end the sentence and not actually part of the URL. Optionally match // parameters following a question mark. @@ -42,84 +22,91 @@ export function separateUrls(message: string): Array { const mainUrl = /https?:\/\/[\w/.%-]*[\w/-]/.source; // characters allowed in query/fragment, disallow `.` at the end const queryChars = /[\w-~%&+.!=:@/?]*[\w-~%&+!=:@/?]/.source; - const urlRegex = new RegExp( - `${mainUrl}(?:\\?${queryChars})?(?:#${queryChars})?`, - 'g', - ); + const urlRegex = new RegExp(`${mainUrl}(?:\\?${queryChars})?(?:#${queryChars})?`, 'g'); const urls = message.match(urlRegex); const nonUrls = message.split(urlRegex); - const parts: Array = [ - { - isUrl: false, - text: nonUrls[0], - }, - ]; + const parts = [{ + isUrl: false, + text: nonUrls[0] + }]; for (let i = 1; i < nonUrls.length; i++) { - invariant(urls != null); + if (!(urls != null)) { + throw new Error('Invariant violation: "urls != null"'); + } + parts.push({ isUrl: true, - url: urls[i - 1], + url: urls[i - 1] }); parts.push({ isUrl: false, - text: nonUrls[i], + text: nonUrls[i] }); } return parts; -} +} /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ const LEADING_WHITESPACE_RE = /^\s+/; const NBSP = '\xa0'; -function renderRowWithLinks( - message: string, - rowIndex: number, -): React.Element { - const messageWithWhitespace = message.replace( - LEADING_WHITESPACE_RE, - whitespace => NBSP.repeat(whitespace.length), - ); +function renderRowWithLinks(message, rowIndex) { + const messageWithWhitespace = message.replace(LEADING_WHITESPACE_RE, whitespace => NBSP.repeat(whitespace.length)); const parts = separateUrls(messageWithWhitespace).map((part, index) => { if (!part.isUrl) { return part.text; } else { const openUrl = () => { - shell.openExternal(part.url); + _electron.shell.openExternal(part.url); }; - return ( - - {part.url} - + return _react.createElement( + 'a', + { href: '#', key: index, onClick: openUrl }, + part.url ); } }); return ( // We need to use a span here instead of a div so that `text-overflow: ellipsis` works. - - {parts} -
-
+ _react.createElement( + 'span', + { key: rowIndex }, + parts, + _react.createElement('br', null) + ) ); } -export const DiagnosticsMessageText = (props: DiagnosticsMessageTextProps) => { - const {message} = props; +const DiagnosticsMessageText = exports.DiagnosticsMessageText = props => { + const { message } = props; if (message.html != null) { - return ( - - ); + return _react.createElement('span', { + title: message.text, + dangerouslySetInnerHTML: { __html: message.html } + }); } else if (message.text != null) { - const rows = - props.preserveNewlines !== false - ? message.text.split('\n') - : [message.text]; - return {rows.map(renderRowWithLinks)}; + const rows = props.preserveNewlines !== false ? message.text.split('\n') : [message.text]; + return _react.createElement( + 'span', + { title: message.text }, + rows.map(renderRowWithLinks) + ); } else { - return Diagnostic lacks message.; + return _react.createElement( + 'span', + null, + 'Diagnostic lacks message.' + ); } -}; +}; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsPopup.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsPopup.js index 63148915..d8cc0ff7 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsPopup.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsPopup.js @@ -1,88 +1,95 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ - -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {DiagnosticMessage} from '../../../atom-ide-diagnostics/lib/types'; -import type {CodeAction} from '../../../atom-ide-code-actions/lib/types'; - -import * as React from 'react'; -import classnames from 'classnames'; -import {mapUnion} from 'nuclide-commons/collection'; -import {DiagnosticsMessage} from './DiagnosticsMessage'; -import DiagnosticsCodeActions from './DiagnosticsCodeActions'; - -type DiagnosticsPopupProps = { - messages: Array, - goToLocation: (filePath: NuclideUri, line: number) => mixed, - fixer: (message: DiagnosticMessage) => void, - codeActionsForMessage?: Map>, -}; +'use strict'; -function renderMessage( - fixer: (message: DiagnosticMessage) => void, - goToLocation: (filePath: NuclideUri, line: number) => mixed, - codeActionsForMessage: ?Map>, - message: DiagnosticMessage, - index: number, -): React.Element { - const className = classnames( - // native-key-bindings and tabIndex=-1 are both needed to allow copying the text in the popup. - 'native-key-bindings', - 'diagnostics-popup-diagnostic', - { - 'diagnostics-popup-error': message.type === 'Error', - 'diagnostics-popup-warning': message.type === 'Warning', - 'diagnostics-popup-info': message.type === 'Info', - }, - ); +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DiagnosticsPopup = undefined; + +var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); + +var _react = _interopRequireWildcard(require('react')); + +var _classnames; + +function _load_classnames() { + return _classnames = _interopRequireDefault(require('classnames')); +} + +var _collection; + +function _load_collection() { + return _collection = require('nuclide-commons/collection'); +} + +var _DiagnosticsMessage; + +function _load_DiagnosticsMessage() { + return _DiagnosticsMessage = require('./DiagnosticsMessage'); +} + +var _DiagnosticsCodeActions; + +function _load_DiagnosticsCodeActions() { + return _DiagnosticsCodeActions = _interopRequireDefault(require('./DiagnosticsCodeActions')); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; } /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ + +function renderMessage(fixer, goToLocation, codeActionsForMessage, message, index) { + const className = (0, (_classnames || _load_classnames()).default)( + // native-key-bindings and tabIndex=-1 are both needed to allow copying the text in the popup. + 'native-key-bindings', 'diagnostics-popup-diagnostic', { + 'diagnostics-popup-error': message.type === 'Error', + 'diagnostics-popup-warning': message.type === 'Warning', + 'diagnostics-popup-info': message.type === 'Info' + }); const codeActions = getCodeActions(message, codeActionsForMessage); - return ( -
- - {codeActions && codeActions.size ? ( - - ) : null} - -
+ return _react.createElement( + 'div', + { className: className, key: index, tabIndex: -1 }, + _react.createElement( + (_DiagnosticsMessage || _load_DiagnosticsMessage()).DiagnosticsMessage, + { + fixer: fixer, + goToLocation: goToLocation, + message: message }, + codeActions && codeActions.size ? _react.createElement((_DiagnosticsCodeActions || _load_DiagnosticsCodeActions()).default, { codeActions: codeActions }) : null + ) ); } -function getCodeActions( - message: DiagnosticMessage, - codeActionsForMessage: ?Map>, -): ?Map { +function getCodeActions(message, codeActionsForMessage) { const codeActionMaps = []; if (message.actions != null && message.actions.length > 0) { - codeActionMaps.push( - new Map( - message.actions.map(action => { - return [ - action.title, - { - async getTitle() { - return action.title; - }, - async apply() { - action.apply(); - }, - dispose() {}, - }, - ]; - }), - ), - ); + codeActionMaps.push(new Map(message.actions.map(action => { + return [action.title, { + getTitle() { + return (0, _asyncToGenerator.default)(function* () { + return action.title; + })(); + }, + apply() { + return (0, _asyncToGenerator.default)(function* () { + action.apply(); + })(); + }, + dispose() {} + }]; + }))); } if (codeActionsForMessage) { const actions = codeActionsForMessage.get(message); @@ -90,17 +97,17 @@ function getCodeActions( codeActionMaps.push(actions); } } - return codeActionMaps.length > 0 ? mapUnion(...codeActionMaps) : null; + return codeActionMaps.length > 0 ? (0, (_collection || _load_collection()).mapUnion)(...codeActionMaps) : null; } // TODO move LESS styles to nuclide-ui -export const DiagnosticsPopup = (props: DiagnosticsPopupProps) => { - const {fixer, goToLocation, codeActionsForMessage, messages, ...rest} = props; - return ( -
- {messages.map( - renderMessage.bind(null, fixer, goToLocation, codeActionsForMessage), - )} -
+const DiagnosticsPopup = props => { + const { fixer, goToLocation, codeActionsForMessage, messages } = props, + rest = _objectWithoutProperties(props, ['fixer', 'goToLocation', 'codeActionsForMessage', 'messages']); + return _react.createElement( + 'div', + Object.assign({ className: 'diagnostics-popup' }, rest), + messages.map(renderMessage.bind(null, fixer, goToLocation, codeActionsForMessage)) ); }; +exports.DiagnosticsPopup = DiagnosticsPopup; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTable.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTable.js index bcb189c3..64ab6b36 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTable.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTable.js @@ -1,3 +1,93 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _classnames; + +function _load_classnames() { + return _classnames = _interopRequireDefault(require('classnames')); +} + +var _idx; + +function _load_idx() { + return _idx = _interopRequireDefault(require('idx')); +} + +var _memoizeUntilChanged; + +function _load_memoizeUntilChanged() { + return _memoizeUntilChanged = _interopRequireDefault(require('nuclide-commons/memoizeUntilChanged')); +} + +var _UniversalDisposable; + +function _load_UniversalDisposable() { + return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); +} + +var _humanizePath; + +function _load_humanizePath() { + return _humanizePath = _interopRequireDefault(require('nuclide-commons-atom/humanizePath')); +} + +var _collection; + +function _load_collection() { + return _collection = require('nuclide-commons/collection'); +} + +var _react = _interopRequireWildcard(require('react')); + +var _nuclideUri; + +function _load_nuclideUri() { + return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri')); +} + +var _goToLocation; + +function _load_goToLocation() { + return _goToLocation = require('nuclide-commons-atom/go-to-location'); +} + +var _Table; + +function _load_Table() { + return _Table = require('nuclide-commons-ui/Table'); +} + +var _sortDiagnostics; + +function _load_sortDiagnostics() { + return _sortDiagnostics = _interopRequireDefault(require('../sortDiagnostics')); +} + +var _DiagnosticsMessage; + +function _load_DiagnosticsMessage() { + return _DiagnosticsMessage = require('./DiagnosticsMessage'); +} + +var _DiagnosticsMessageText; + +function _load_DiagnosticsMessageText() { + return _DiagnosticsMessageText = require('./DiagnosticsMessageText'); +} + +var _Icon; + +function _load_Icon() { + return _Icon = require('nuclide-commons-ui/Icon'); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,158 +96,47 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type { - DiagnosticMessage, - DiagnosticMessageKind, - DiagnosticMessageType, -} from '../../../atom-ide-diagnostics/lib/types'; -import type {NuclideUri} from 'nuclide-commons/nuclideUri'; -import type {Column, Row} from 'nuclide-commons-ui/Table'; -import type {IconName} from 'nuclide-commons-ui/Icon'; - -import classnames from 'classnames'; -import invariant from 'assert'; -import idx from 'idx'; -import memoizeUntilChanged from 'nuclide-commons/memoizeUntilChanged'; -import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; -import humanizePath from 'nuclide-commons-atom/humanizePath'; -import {insideOut, arrayEqual} from 'nuclide-commons/collection'; -import * as React from 'react'; -import nuclideUri from 'nuclide-commons/nuclideUri'; -import {goToLocation} from 'nuclide-commons-atom/go-to-location'; -import {Table} from 'nuclide-commons-ui/Table'; -import sortDiagnostics from '../sortDiagnostics'; -import {DiagnosticsMessageNoHeader} from './DiagnosticsMessage'; -import {DiagnosticsMessageText} from './DiagnosticsMessageText'; -import {Icon} from 'nuclide-commons-ui/Icon'; - const DIAGNOSTICS_TO_ROWS_TRACES_MAP = new WeakMap(); const DIAGNOSTICS_TO_ROWS_NO_TRACES_MAP = new WeakMap(); // text is always used for sorting, while we render the element. -type DescriptionField = { - diagnostic: DiagnosticMessage, - showTraces: boolean, - text: string, - isPlainText: boolean, -}; -type Location = {| - fullPath: NuclideUri, - locationInFile: ?{| - basename: string, - line: number, - |}, -|}; - -export type DisplayDiagnostic = { - +classification: { - kind: DiagnosticMessageKind, - severity: DiagnosticMessageType, - }, - +providerName: string, - +description: { - showTraces: boolean, - diagnostic: DiagnosticMessage, - text: string, - isPlainText: boolean, - }, - +dir: string, - +location: ?Location, - +line: ?number, -}; - -type ColumnName = $Keys; // Maximum number of results to render in the table before truncating and displaying a "Max results // reached" message. const MAX_RESULTS_COUNT = 1000; -type Props = { - diagnostics: Array, - selectedMessage: ?DiagnosticMessage, - gotoMessageLocation: ( - message: DiagnosticMessage, - options: {|focusEditor: boolean|}, - ) => void, - selectMessage: (message: DiagnosticMessage) => void, - showFileName: ?boolean, - showDirectoryColumn: boolean, - showTraces: boolean, -}; - -type State = { - sortDescending: boolean, - sortedColumn: ColumnName, -}; - -export default class DiagnosticsTable extends React.PureComponent< - Props, - State, -> { - _previousSelectedIndex: number = -1; - _table: ?Table; - _disposables: ?UniversalDisposable; +class DiagnosticsTable extends _react.PureComponent { - constructor(props: Props) { + constructor(props) { super(props); // Memoize `_getRows()` - (this: any)._getRows = memoizeUntilChanged( - this._getRows, - (diagnostics, showTraces) => ({diagnostics, showTraces}), - (a, b) => - a.showTraces === b.showTraces && - arrayEqual(a.diagnostics, b.diagnostics), - ); + + _initialiseProps.call(this); + + this._getRows = (0, (_memoizeUntilChanged || _load_memoizeUntilChanged()).default)(this._getRows, (diagnostics, showTraces) => ({ diagnostics, showTraces }), (a, b) => a.showTraces === b.showTraces && (0, (_collection || _load_collection()).arrayEqual)(a.diagnostics, b.diagnostics)); this.state = { sortDescending: true, - sortedColumn: 'classification', + sortedColumn: 'classification' }; } - componentWillUnmount(): void { - invariant(this._disposables != null); - this._disposables.dispose(); - } - - _handleSort = (sortedColumn: ColumnName, sortDescending: boolean): void => { - this.setState({ - sortedColumn, - sortDescending, - }); - }; - - _handleSelectTableRow = ( - item: {diagnostic: DiagnosticMessage}, - index: number, - event: Event | SyntheticEvent<*>, - ): void => { - this.props.selectMessage(item.diagnostic); - // Users navigating with the keyboard may just be moving through items on their way to another. - // If they have pending pane items enabled, it's not a big deal if we open the editor anyway. - // But if they don't, we could wind up opening a ton of files they didn't even care about so, - // to be safe, we won't do anything in that case. - if ( - event.type !== 'click' && - !atom.config.get('core.allowPendingPaneItems') - ) { - return; + componentWillUnmount() { + if (!(this._disposables != null)) { + throw new Error('Invariant violation: "this._disposables != null"'); } - this.props.gotoMessageLocation(item.diagnostic, {focusEditor: false}); - }; - _handleConfirmTableRow = (item: {diagnostic: DiagnosticMessage}): void => { - this.props.gotoMessageLocation(item.diagnostic, {focusEditor: true}); - }; + this._disposables.dispose(); + } - _getColumns(): Array> { - const {showFileName, showDirectoryColumn} = this.props; + _getColumns() { + const { showFileName, showDirectoryColumn } = this.props; // These need to add up to 1. // TODO: Update the Table component so that we can have more control over this (and provide @@ -179,7 +158,7 @@ export default class DiagnosticsTable extends React.PureComponent< title: 'Path', width: DIR_WIDTH, shouldRightAlign: true, - cellClassName: 'nuclide-diagnostics-ui-cell-dir', + cellClassName: 'nuclide-diagnostics-ui-cell-dir' }); descriptionWidth -= DIR_WIDTH; } @@ -189,7 +168,7 @@ export default class DiagnosticsTable extends React.PureComponent< key: 'location', title: 'File Name', width: FILENAME_WIDTH, - cellClassName: 'nuclide-diagnostics-ui-cell-filename', + cellClassName: 'nuclide-diagnostics-ui-cell-filename' }); descriptionWidth -= FILENAME_WIDTH; } else { @@ -200,57 +179,37 @@ export default class DiagnosticsTable extends React.PureComponent< title: 'Line', shouldRightAlign: true, width: LINE_NUMBER_WIDTH, - minWidth: 60, + minWidth: 60 }); descriptionWidth -= LINE_NUMBER_WIDTH; } - return [ - { - component: TypeComponent, - key: 'classification', - title: 'Type', - width: TYPE_WIDTH, - minWidth: 55, - cellClassName: 'nuclide-diagnostics-ui-cell-classification', - }, - { - key: 'providerName', - title: 'Source', - width: SOURCE_WIDTH, - minWidth: 100, - }, - { - component: this._renderDescription, - key: 'description', - title: 'Description', - width: descriptionWidth, - cellClassName: 'nuclide-diagnostics-ui-cell-description', - }, - ...filePathColumns, - ]; + return [{ + component: TypeComponent, + key: 'classification', + title: 'Type', + width: TYPE_WIDTH, + minWidth: 55, + cellClassName: 'nuclide-diagnostics-ui-cell-classification' + }, { + key: 'providerName', + title: 'Source', + width: SOURCE_WIDTH, + minWidth: 100 + }, { + component: this._renderDescription, + key: 'description', + title: 'Description', + width: descriptionWidth, + cellClassName: 'nuclide-diagnostics-ui-cell-description' + }, ...filePathColumns]; } // False positive for this lint rule? // eslint-disable-next-line react/no-unused-prop-types - _renderDescription = (props: {data: DescriptionField}) => { - const {showTraces, diagnostic, text, isPlainText} = props.data; - return showTraces - ? DiagnosticsMessageNoHeader({ - message: diagnostic, - goToLocation: (file: string, line: number) => - goToLocation(file, {line}), - fixer: () => {}, - }) - : DiagnosticsMessageText({ - preserveNewlines: showTraces, - message: {text, html: isPlainText ? undefined : text}, - }); - }; - _getSortOptions( - columns: Array>, - ): {|sortedColumn: ColumnName, sortDescending: boolean|} { + + _getSortOptions(columns) { // If the column the user sorted by has been removed, return the default sorting. We do this // (instead of updating the state) so that if the column gets added back we can return to // sorting by that. @@ -258,78 +217,72 @@ export default class DiagnosticsTable extends React.PureComponent< if (!columnKeys.includes(this.state.sortedColumn)) { return { sortedColumn: 'classification', - sortDescending: true, + sortDescending: true }; } // Otherwise, return the sorting they've chosen. return { sortedColumn: this.state.sortedColumn, - sortDescending: this.state.sortDescending, + sortDescending: this.state.sortDescending }; } - render(): React.Node { - const {diagnostics, selectedMessage, showTraces} = this.props; + render() { + const { diagnostics, selectedMessage, showTraces } = this.props; const columns = this._getColumns(); - const {sortedColumn, sortDescending} = this._getSortOptions(columns); + const { sortedColumn, sortDescending } = this._getSortOptions(columns); const diagnosticRows = this._getRows(diagnostics, showTraces); - let sortedRows = this._sortRows( - diagnosticRows, - sortedColumn, - sortDescending, - ); + let sortedRows = this._sortRows(diagnosticRows, sortedColumn, sortDescending); let maxResultsMessage; if (sortedRows.length > MAX_RESULTS_COUNT) { sortedRows = sortedRows.slice(0, MAX_RESULTS_COUNT); - maxResultsMessage = ( -
- Max results ({MAX_RESULTS_COUNT}) reached. Fix diagnostics or show - only diagnostics for the current file to view more. -
+ maxResultsMessage = _react.createElement( + 'div', + { className: 'highlight-warning diagnostics-ui-table-message' }, + 'Max results (', + MAX_RESULTS_COUNT, + ') reached. Fix diagnostics or show only diagnostics for the current file to view more.' ); } const selectedIndex = this._findSelectedIndex(selectedMessage, sortedRows); - return ( -
- { - this._table = table; - }} - collapsable={true} - columns={columns} - emptyComponent={EmptyComponent} - fixedHeader={true} - maxBodyHeight="99999px" - rows={sortedRows} - sortable={true} - onSort={this._handleSort} - sortedColumn={sortedColumn} - sortDescending={sortDescending} - selectable={true} - selectedIndex={selectedIndex} - onSelect={this._handleSelectTableRow} - onConfirm={this._handleConfirmTableRow} - enableKeyboardNavigation={true} - /> - {maxResultsMessage} - + 'diagnostics-ui-table-container-empty': sortedRows.length === 0 + }) }, + _react.createElement((_Table || _load_Table()).Table, { + ref: table => { + this._table = table; + }, + collapsable: true, + columns: columns, + emptyComponent: EmptyComponent, + fixedHeader: true, + maxBodyHeight: '99999px', + rows: sortedRows, + sortable: true, + onSort: this._handleSort, + sortedColumn: sortedColumn, + sortDescending: sortDescending, + selectable: true, + selectedIndex: selectedIndex, + onSelect: this._handleSelectTableRow, + onConfirm: this._handleConfirmTableRow, + enableKeyboardNavigation: true + }), + maxResultsMessage ); } - focus(): void { + focus() { if (this._table != null) { this._table.focus(); } } - _findSelectedIndex( - selectedMessage: ?DiagnosticMessage, - rows: Array>, - ): number { + _findSelectedIndex(selectedMessage, rows) { if (selectedMessage == null) { return -1; } @@ -338,8 +291,8 @@ export default class DiagnosticsTable extends React.PureComponent< let bestRankedIndex = -1; // Look for the closest match, starting with the previously selected index. - for (const [row, i] of insideOut(rows, this._previousSelectedIndex)) { - const {diagnostic} = row.data.description; + for (const [row, i] of (0, (_collection || _load_collection()).insideOut)(rows, this._previousSelectedIndex)) { + const { diagnostic } = row.data.description; if (diagnostic === selectedMessage) { bestRankedIndex = i; break; @@ -359,34 +312,30 @@ export default class DiagnosticsTable extends React.PureComponent< return bestRankedIndex; } - _getRows( - diagnostics: Array, - showTraces: boolean, - ): Array> { - const diagnosticsToRows = showTraces - ? DIAGNOSTICS_TO_ROWS_TRACES_MAP - : DIAGNOSTICS_TO_ROWS_NO_TRACES_MAP; + _getRows(diagnostics, showTraces) { + const diagnosticsToRows = showTraces ? DIAGNOSTICS_TO_ROWS_TRACES_MAP : DIAGNOSTICS_TO_ROWS_NO_TRACES_MAP; return diagnostics.map(diagnostic => { let row = diagnosticsToRows.get(diagnostic); if (row == null) { - const {dir, location} = getLocation(diagnostic); + var _ref, _ref2; + + const { dir, location } = getLocation(diagnostic); row = { data: { classification: { kind: diagnostic.kind || 'lint', - severity: diagnostic.type, + severity: diagnostic.type }, providerName: diagnostic.providerName, - description: { + description: Object.assign({ showTraces, - diagnostic, - ...getMessageContent(diagnostic, showTraces), - }, + diagnostic + }, getMessageContent(diagnostic, showTraces)), dir, location, diagnostic, - line: idx(location, _ => _.locationInFile.line), - }, + line: (_ref = location) != null ? (_ref2 = _ref.locationInFile) != null ? _ref2.line : _ref2 : _ref + } }; diagnosticsToRows.set(diagnostic, row); } @@ -395,36 +344,74 @@ export default class DiagnosticsTable extends React.PureComponent< } // TODO: Memoize this so we don't recompute unnecessarily. - _sortRows( - rows: Array>, - sortedColumn: $Keys, - descending: boolean, - ): Array> { - return sortDiagnostics(rows, sortedColumn, descending); + _sortRows(rows, sortedColumn, descending) { + return (0, (_sortDiagnostics || _load_sortDiagnostics()).default)(rows, sortedColumn, descending); } } -const EmptyComponent = () => ( -
No diagnostic messages
-); +exports.default = DiagnosticsTable; + +var _initialiseProps = function () { + this._previousSelectedIndex = -1; -type Classification = { - kind: DiagnosticMessageKind, - severity: DiagnosticMessageType, + this._handleSort = (sortedColumn, sortDescending) => { + this.setState({ + sortedColumn, + sortDescending + }); + }; + + this._handleSelectTableRow = (item, index, event) => { + this.props.selectMessage(item.diagnostic); + // Users navigating with the keyboard may just be moving through items on their way to another. + // If they have pending pane items enabled, it's not a big deal if we open the editor anyway. + // But if they don't, we could wind up opening a ton of files they didn't even care about so, + // to be safe, we won't do anything in that case. + if (event.type !== 'click' && !atom.config.get('core.allowPendingPaneItems')) { + return; + } + this.props.gotoMessageLocation(item.diagnostic, { focusEditor: false }); + }; + + this._handleConfirmTableRow = item => { + this.props.gotoMessageLocation(item.diagnostic, { focusEditor: true }); + }; + + this._renderDescription = props => { + const { showTraces, diagnostic, text, isPlainText } = props.data; + return showTraces ? (0, (_DiagnosticsMessage || _load_DiagnosticsMessage()).DiagnosticsMessageNoHeader)({ + message: diagnostic, + goToLocation: (file, line) => (0, (_goToLocation || _load_goToLocation()).goToLocation)(file, { line }), + fixer: () => {} + }) : (0, (_DiagnosticsMessageText || _load_DiagnosticsMessageText()).DiagnosticsMessageText)({ + preserveNewlines: showTraces, + message: { text, html: isPlainText ? undefined : text } + }); + }; }; -function TypeComponent(props: {data: Classification}): React.Element { +const EmptyComponent = () => _react.createElement( + 'div', + { className: 'diagnostics-ui-empty-component' }, + 'No diagnostic messages' +); + +function TypeComponent(props) { const classification = props.data; const iconName = getIconName(classification); - return ; + return _react.createElement((_Icon || _load_Icon()).Icon, { icon: iconName }); } -function getIconName(classification: Classification): IconName { - const {kind, severity} = classification; +function getIconName(classification) { + const { kind, severity } = classification; if (kind === 'review') { return 'nuclicon-comment-discussion'; } - invariant(severity !== 'Hint'); + + if (!(severity !== 'Hint')) { + throw new Error('Invariant violation: "severity !== \'Hint\'"'); + } + switch (severity) { case 'Warning': return 'nuclicon-warning'; @@ -433,22 +420,16 @@ function getIconName(classification: Classification): IconName { case 'Info': return 'info'; default: - (severity: empty); + severity; throw new Error(`Invalid severity: ${severity}`); } } -function getMessageContent( - diagnostic: DiagnosticMessage, - showTraces: boolean, -): {text: string, isPlainText: boolean} { +function getMessageContent(diagnostic, showTraces) { let text = ''; let isPlainText = true; const traces = diagnostic.trace || []; - const allMessages: Array<{html?: string, text?: string}> = [ - diagnostic, - ...traces, - ]; + const allMessages = [diagnostic, ...traces]; for (const message of allMessages) { // TODO: A mix of html and text diagnostics will yield a wonky sort ordering. if (message.html != null) { @@ -460,64 +441,79 @@ function getMessageContent( throw new Error('Neither text nor html property defined on message'); } } - return {text: text.trim(), isPlainText}; + return { text: text.trim(), isPlainText }; } -function DirComponent(props: {data: string}): React.Element { +function DirComponent(props) { return ( // We're abusing `direction: rtl` here so we need the LRM to keep the slash on the right. -
- ‎{nuclideUri.normalizeDir(props.data)}‎ -
+ _react.createElement( + 'div', + { className: 'nuclide-diagnostics-ui-dir-cell-contents' }, + '\u200E', + (_nuclideUri || _load_nuclideUri()).default.normalizeDir(props.data), + '\u200E' + ) ); } -function FilenameComponent(props: {data: ?Location}): React.Element { +function FilenameComponent(props) { const locationInFile = props.data && props.data.locationInFile; if (locationInFile == null) { // This is a project diagnostic. - return {DASH}; + return _react.createElement( + 'span', + null, + DASH + ); } - const {basename, line} = locationInFile; - return ( - - {basename} - :{line} - + const { basename, line } = locationInFile; + return _react.createElement( + 'span', + null, + basename, + _react.createElement( + 'span', + { className: 'nuclide-diagnostics-ui-line-number' }, + ':', + line + ) ); } -function LineNumberComponent(props: {data: ?number}): React.Element { +function LineNumberComponent(props) { const line = props.data; // Show a dash if this is a project diagnostic. - return {line == null ? DASH : line}; + return _react.createElement( + 'span', + null, + line == null ? DASH : line + ); } -function getLocation( - diagnostic: DiagnosticMessage, -): {dir: string, location: ?Location} { - const {filePath, range} = diagnostic; +function getLocation(diagnostic) { + const { filePath, range } = diagnostic; const line = range ? range.start.row + 1 : 0; - const humanized = humanizePath(filePath); - if (nuclideUri.endsWithSeparator(humanized)) { + const humanized = (0, (_humanizePath || _load_humanizePath()).default)(filePath); + if ((_nuclideUri || _load_nuclideUri()).default.endsWithSeparator(humanized)) { // It's a directory. return { dir: humanized, location: { fullPath: filePath, - locationInFile: null, - }, + locationInFile: null + } }; } - const {dir, base: basename} = nuclideUri.parsePath(humanized); + const { dir, base: basename } = (_nuclideUri || _load_nuclideUri()).default.parsePath(humanized); return { dir, location: { fullPath: filePath, - locationInFile: {basename, line}, - }, + locationInFile: { basename, line } + } }; } @@ -525,17 +521,12 @@ function getLocation( * Compute a number indicating the relative similarity of two messages. The smaller the number, the * more similar. (`null` indicates not at all similar.) */ -function compareMessages(a: DiagnosticMessage, b: DiagnosticMessage): ?number { +function compareMessages(a, b) { const aKind = a.kind || 'lint'; const bKind = b.kind || 'lint'; const aFilePath = a.filePath; const bFilePath = b.filePath; - if ( - aKind !== bKind || - a.providerName !== b.providerName || - a.type !== b.type || - aFilePath !== bFilePath - ) { + if (aKind !== bKind || a.providerName !== b.providerName || a.type !== b.type || aFilePath !== bFilePath) { return null; } const aRange = a.range; @@ -556,4 +547,4 @@ function compareMessages(a: DiagnosticMessage, b: DiagnosticMessage): ?number { return Math.abs(aRange.start.row - bRange.start.row); } -const DASH = '\u2014'; +const DASH = '\u2014'; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTraceItem.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTraceItem.js index 7087e3aa..dc8fa9c5 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTraceItem.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsTraceItem.js @@ -1,28 +1,23 @@ -/** - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @flow - * @format - */ +'use strict'; -import type {DiagnosticTrace} from '../../../atom-ide-diagnostics/lib/types'; +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.DiagnosticsTraceItem = undefined; -import * as React from 'react'; -import {DiagnosticsMessageText} from './DiagnosticsMessageText'; +var _react = _interopRequireWildcard(require('react')); -type DiagnosticsTraceItemProps = { - trace: DiagnosticTrace, - goToLocation: (path: string, line: number) => mixed, -}; +var _DiagnosticsMessageText; + +function _load_DiagnosticsMessageText() { + return _DiagnosticsMessageText = require('./DiagnosticsMessageText'); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } // TODO move LESS styles to nuclide-ui -export const DiagnosticsTraceItem = (props: DiagnosticsTraceItemProps) => { - const {trace, goToLocation} = props; +const DiagnosticsTraceItem = exports.DiagnosticsTraceItem = props => { + const { trace, goToLocation } = props; let locSpan = null; // Local variable so that the type refinement holds in the onClick handler. const path = trace.filePath; @@ -33,23 +28,36 @@ export const DiagnosticsTraceItem = (props: DiagnosticsTraceItemProps) => { if (trace.range) { locString += `:${trace.range.start.row + 1}`; } - const onClick = (event: SyntheticMouseEvent<>) => { + const onClick = event => { event.stopPropagation(); goToLocation(path, Math.max(trace.range ? trace.range.start.row : 0, 0)); }; - locSpan = ( - - :{' '} - - {locString} - - + locSpan = _react.createElement( + 'span', + null, + ':', + ' ', + _react.createElement( + 'a', + { href: '#', onClick: onClick }, + locString + ) ); } - return ( -
- - {locSpan} -
+ return _react.createElement( + 'div', + null, + _react.createElement((_DiagnosticsMessageText || _load_DiagnosticsMessageText()).DiagnosticsMessageText, { message: trace }), + locSpan ); -}; +}; /** + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * + * @format + */ \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsView.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsView.js index 1f1a1d7e..00c70ac9 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsView.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/DiagnosticsView.js @@ -1,3 +1,90 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _analytics; + +function _load_analytics() { + return _analytics = _interopRequireDefault(require('nuclide-commons-atom/analytics')); +} + +var _DiagnosticsTable; + +function _load_DiagnosticsTable() { + return _DiagnosticsTable = _interopRequireDefault(require('./DiagnosticsTable')); +} + +var _showModal; + +function _load_showModal() { + return _showModal = _interopRequireDefault(require('nuclide-commons-ui/showModal')); +} + +var _Toggle; + +function _load_Toggle() { + return _Toggle = require('nuclide-commons-ui/Toggle'); +} + +var _Toolbar; + +function _load_Toolbar() { + return _Toolbar = require('nuclide-commons-ui/Toolbar'); +} + +var _ToolbarLeft; + +function _load_ToolbarLeft() { + return _ToolbarLeft = require('nuclide-commons-ui/ToolbarLeft'); +} + +var _ToolbarRight; + +function _load_ToolbarRight() { + return _ToolbarRight = require('nuclide-commons-ui/ToolbarRight'); +} + +var _react = _interopRequireWildcard(require('react')); + +var _Button; + +function _load_Button() { + return _Button = require('nuclide-commons-ui/Button'); +} + +var _ButtonGroup; + +function _load_ButtonGroup() { + return _ButtonGroup = require('nuclide-commons-ui/ButtonGroup'); +} + +var _FilterButton; + +function _load_FilterButton() { + return _FilterButton = _interopRequireDefault(require('./FilterButton')); +} + +var _RegExpFilter; + +function _load_RegExpFilter() { + return _RegExpFilter = _interopRequireDefault(require('nuclide-commons-ui/RegExpFilter')); +} + +var _SettingsModal; + +function _load_SettingsModal() { + return _SettingsModal = _interopRequireDefault(require('./SettingsModal')); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Dismissable panel that displays the diagnostics from nuclide-diagnostics-store. + */ /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,72 +93,49 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type { - DiagnosticMessage, - DiagnosticMessageKind, - UiConfig, -} from '../../../atom-ide-diagnostics/lib/types'; -import type {DiagnosticGroup} from '../types'; -import type { - RegExpFilterChange, - RegExpFilterValue, -} from 'nuclide-commons-ui/RegExpFilter'; - -import analytics from 'nuclide-commons-atom/analytics'; -import DiagnosticsTable from './DiagnosticsTable'; -import showModal from 'nuclide-commons-ui/showModal'; -import {Toggle} from 'nuclide-commons-ui/Toggle'; -import {Toolbar} from 'nuclide-commons-ui/Toolbar'; -import {ToolbarLeft} from 'nuclide-commons-ui/ToolbarLeft'; -import {ToolbarRight} from 'nuclide-commons-ui/ToolbarRight'; -import * as React from 'react'; -import {Button, ButtonSizes} from 'nuclide-commons-ui/Button'; -import {ButtonGroup} from 'nuclide-commons-ui/ButtonGroup'; -import FilterButton from './FilterButton'; -import RegExpFilter from 'nuclide-commons-ui/RegExpFilter'; -import SettingsModal from './SettingsModal'; - -export type Props = { - diagnostics: Array, - filterByActiveTextEditor: boolean, - onFilterByActiveTextEditorChange: (isChecked: boolean) => mixed, - showDirectoryColumn: boolean, - showTraces: boolean, - onShowTracesChange: (isChecked: boolean) => mixed, - gotoMessageLocation: ( - message: DiagnosticMessage, - options: {|focusEditor: boolean|}, - ) => void, - selectMessage: (message: DiagnosticMessage) => void, - selectedMessage: ?DiagnosticMessage, - supportedMessageKinds: Set, - uiConfig: UiConfig, - isVisible: boolean, - // Used by the DiagnosticsViewModel. - autoVisibility: boolean, // eslint-disable-line react/no-unused-prop-types - - hiddenGroups: Set, - onTypeFilterChange: (type: DiagnosticGroup) => mixed, - textFilter: RegExpFilterValue, - onTextFilterChange: (change: RegExpFilterChange) => mixed, -}; +class DiagnosticsView extends _react.Component { + constructor(...args) { + var _temp; -/** - * Dismissable panel that displays the diagnostics from nuclide-diagnostics-store. - */ -export default class DiagnosticsView extends React.Component { - _table: ?DiagnosticsTable; + return _temp = super(...args), this._showSettings = () => { + (0, (_showModal || _load_showModal()).default)(() => _react.createElement((_SettingsModal || _load_SettingsModal()).default, { config: this.props.uiConfig })); + }, this._handleShowTracesChange = isChecked => { + (_analytics || _load_analytics()).default.track('diagnostics-panel-toggle-show-traces', { + isChecked: isChecked.toString() + }); + this.props.onShowTracesChange.call(null, isChecked); + }, this._handleFilterByActiveTextEditorChange = shouldFilter => { + (_analytics || _load_analytics()).default.track('diagnostics-panel-toggle-current-file', { + isChecked: shouldFilter.toString() + }); + this.props.onFilterByActiveTextEditorChange.call(null, shouldFilter); + }, this._openAllFilesWithErrors = () => { + atom.commands.dispatch(atom.views.getView(atom.workspace), 'diagnostics:open-all-files-with-errors'); + }, this._handleFocus = event => { + if (this._table == null) { + return; + } + let el = event.target; + while (el != null) { + if (el.tagName === 'INPUT' || el.tagName === 'BUTTON') { + return; + } + el = el.parentElement; + } + this._table.focus(); + }, _temp; + } - shouldComponentUpdate(nextProps: Props): boolean { + shouldComponentUpdate(nextProps) { return nextProps.isVisible; } - render(): React.Element { - const {diagnostics, showDirectoryColumn, showTraces} = this.props; + render() { + const { diagnostics, showDirectoryColumn, showTraces } = this.props; const groups = ['errors', 'warnings', 'info']; if (this.props.supportedMessageKinds.has('review')) { @@ -81,124 +145,90 @@ export default class DiagnosticsView extends React.Component { groups.push('action'); } - const showFullDescriptionToggle = diagnostics.find( - diagnostic => - // flowlint-next-line sketchy-null-string:off - diagnostic.trace || (diagnostic.text && diagnostic.text.includes('\n')), - ); + const showFullDescriptionToggle = diagnostics.find(diagnostic => + // flowlint-next-line sketchy-null-string:off + diagnostic.trace || diagnostic.text && diagnostic.text.includes('\n')); - return ( -
- - - - {groups.map(group => ( - { - this.props.onTypeFilterChange(group); - }} - /> - ))} - - - {/* TODO: This will probably change to a dropdown to also accommodate Head Changes */} - - - - {showFullDescriptionToggle ? ( - - ) : null} - -
+ width: '100%' + } }, + _react.createElement( + (_Toolbar || _load_Toolbar()).Toolbar, + { location: 'top' }, + _react.createElement( + (_ToolbarLeft || _load_ToolbarLeft()).ToolbarLeft, + null, + _react.createElement( + (_ButtonGroup || _load_ButtonGroup()).ButtonGroup, + { className: 'inline-block' }, + groups.map(group => _react.createElement((_FilterButton || _load_FilterButton()).default, { + key: group, + group: group, + selected: !this.props.hiddenGroups.has(group), + onClick: () => { + this.props.onTypeFilterChange(group); + } + })) + ), + _react.createElement((_RegExpFilter || _load_RegExpFilter()).default, { + value: this.props.textFilter, + onChange: this.props.onTextFilterChange + }), + _react.createElement((_Toggle || _load_Toggle()).Toggle, { + className: 'inline-block', + onChange: this._handleFilterByActiveTextEditorChange, + toggled: this.props.filterByActiveTextEditor, + label: 'Current File Only' + }) + ), + _react.createElement( + (_ToolbarRight || _load_ToolbarRight()).ToolbarRight, + null, + showFullDescriptionToggle ? _react.createElement((_Toggle || _load_Toggle()).Toggle, { + className: 'inline-block', + onChange: this._handleShowTracesChange, + toggled: this.props.showTraces, + label: 'Full Description' + }) : null, + _react.createElement( + (_Button || _load_Button()).Button, + { + onClick: this._openAllFilesWithErrors, + size: (_Button || _load_Button()).ButtonSizes.SMALL, + disabled: diagnostics.length === 0, + className: 'inline-block', + title: 'Open All' }, + 'Open All' + ), + _react.createElement((_Button || _load_Button()).Button, { + icon: 'gear', + size: (_Button || _load_Button()).ButtonSizes.SMALL, + onClick: this._showSettings + }) + ) + ), + _react.createElement((_DiagnosticsTable || _load_DiagnosticsTable()).default, { + ref: table => { + this._table = table; + }, + showFileName: !this.props.filterByActiveTextEditor, + diagnostics: diagnostics, + showDirectoryColumn: showDirectoryColumn, + showTraces: showTraces, + selectedMessage: this.props.selectedMessage, + selectMessage: this.props.selectMessage, + gotoMessageLocation: this.props.gotoMessageLocation + }) ); } - _showSettings = (): void => { - showModal(() => ); - }; - - _handleShowTracesChange = (isChecked: boolean): void => { - analytics.track('diagnostics-panel-toggle-show-traces', { - isChecked: isChecked.toString(), - }); - this.props.onShowTracesChange.call(null, isChecked); - }; - - _handleFilterByActiveTextEditorChange = (shouldFilter: boolean): void => { - analytics.track('diagnostics-panel-toggle-current-file', { - isChecked: shouldFilter.toString(), - }); - this.props.onFilterByActiveTextEditorChange.call(null, shouldFilter); - }; - - _openAllFilesWithErrors = (): void => { - atom.commands.dispatch( - atom.views.getView(atom.workspace), - 'diagnostics:open-all-files-with-errors', - ); - }; - - _handleFocus = (event: SyntheticMouseEvent<*>): void => { - if (this._table == null) { - return; - } - let el = event.target; - while (el != null) { - if (el.tagName === 'INPUT' || el.tagName === 'BUTTON') { - return; - } - el = (el: any).parentElement; - } - this._table.focus(); - }; } +exports.default = DiagnosticsView; \ No newline at end of file diff --git a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/FilterButton.js b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/FilterButton.js index a7885750..adea11cf 100644 --- a/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/FilterButton.js +++ b/modules/atom-ide-ui/pkg/atom-ide-diagnostics-ui/lib/ui/FilterButton.js @@ -1,3 +1,26 @@ +'use strict'; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.default = FilterButton; + +var _Button; + +function _load_Button() { + return _Button = require('nuclide-commons-ui/Button'); +} + +var _react = _interopRequireWildcard(require('react')); + +var _GroupUtils; + +function _load_GroupUtils() { + return _GroupUtils = _interopRequireWildcard(require('../GroupUtils')); +} + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + /** * Copyright (c) 2017-present, Facebook, Inc. * All rights reserved. @@ -6,33 +29,19 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @flow + * * @format */ -import type {DiagnosticGroup} from '../types'; - -import {Button, ButtonSizes} from 'nuclide-commons-ui/Button'; -import * as React from 'react'; -import * as GroupUtils from '../GroupUtils'; - -type Props = {| - group: DiagnosticGroup, - selected: boolean, - onClick: () => mixed, -|}; - -export default function FilterButton(props: Props): React.Node { - const {selected, group} = props; - const displayName = GroupUtils.getDisplayName(group); +function FilterButton(props) { + const { selected, group } = props; + const displayName = (_GroupUtils || _load_GroupUtils()).getDisplayName(group); const title = props.selected ? `Hide ${displayName}` : `Show ${displayName}`; - return ( -