nuclide-commons-atom
Advanced tools
Comparing version 0.1.3 to 0.1.4
@@ -1,149 +0,87 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
'use strict'; | ||
/** | ||
* ActiveEditorRegistry provides abstractions for creating services that operate | ||
* on text editor contents. | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
import {Disposable} from 'atom'; | ||
import {Observable, Subject} from 'rxjs'; | ||
var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); | ||
import { | ||
observeActiveEditorsDebounced, | ||
editorChangesDebounced, | ||
} from './debounced'; | ||
var _atom = require('atom'); | ||
import {observableFromSubscribeFunction} from 'nuclide-commons/event'; | ||
import {cacheWhileSubscribed} from 'nuclide-commons/observable'; | ||
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); | ||
import {getLogger} from 'log4js'; | ||
var _debounced; | ||
import ProviderRegistry from './ProviderRegistry'; | ||
function _load_debounced() { | ||
return _debounced = require('./debounced'); | ||
} | ||
export type Provider = { | ||
priority: number, | ||
grammarScopes: Array<string>, | ||
// This overrides the updateOnEdit setting in ActiveEditorRegistry's config. | ||
updateOnEdit?: boolean, | ||
}; | ||
var _event; | ||
export type Result<T, V> = | ||
| { | ||
kind: 'not-text-editor', | ||
} | ||
| { | ||
kind: 'no-provider', | ||
grammar: atom$Grammar, | ||
} | ||
| { | ||
kind: 'provider-error', | ||
provider: T, | ||
} | ||
| { | ||
// Since providers can be slow, the pane-change and edit events are emitted immediately in case | ||
// the UI needs to clear outdated results. | ||
kind: 'pane-change', | ||
editor: atom$TextEditor, | ||
} | ||
| { | ||
kind: 'edit', | ||
editor: atom$TextEditor, | ||
} | ||
| { | ||
kind: 'save', | ||
editor: atom$TextEditor, | ||
} | ||
| { | ||
kind: 'result', | ||
result: V, | ||
// The editor that the result was computed from | ||
editor: atom$TextEditor, | ||
// The provider that computed the result | ||
// TODO Use a type paramater for this type | ||
provider: T, | ||
}; | ||
function _load_event() { | ||
return _event = require('nuclide-commons/event'); | ||
} | ||
export type ResultFunction<T, V> = ( | ||
provider: T, | ||
editor: atom$TextEditor, | ||
) => Promise<V>; | ||
var _observable; | ||
type PartialEventSources = { | ||
+activeEditors?: Observable<?atom$TextEditor>, | ||
+changesForEditor?: (editor: atom$TextEditor) => Observable<void>, | ||
+savesForEditor?: (editor: atom$TextEditor) => Observable<void>, | ||
}; | ||
function _load_observable() { | ||
return _observable = require('nuclide-commons/observable'); | ||
} | ||
export type EventSources = { | ||
activeEditors: Observable<?atom$TextEditor>, | ||
changesForEditor: (editor: atom$TextEditor) => Observable<void>, | ||
savesForEditor: (editor: atom$TextEditor) => Observable<void>, | ||
}; | ||
var _log4js; | ||
export type Config = { | ||
/** | ||
* If true, we will query providers for updates whenever the text in the editor is changed. | ||
* Otherwise, we will query only when there is a save event. | ||
*/ | ||
updateOnEdit?: boolean, | ||
}; | ||
function _load_log4js() { | ||
return _log4js = require('log4js'); | ||
} | ||
type ConcreteConfig = { | ||
updateOnEdit: boolean, | ||
}; | ||
var _ProviderRegistry; | ||
const DEFAULT_CONFIG: ConcreteConfig = { | ||
updateOnEdit: true, | ||
}; | ||
function _load_ProviderRegistry() { | ||
return _ProviderRegistry = _interopRequireDefault(require('./ProviderRegistry')); | ||
} | ||
function getConcreteConfig(config: Config): ConcreteConfig { | ||
return { | ||
...DEFAULT_CONFIG, | ||
...config, | ||
}; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const DEFAULT_CONFIG = { | ||
updateOnEdit: true | ||
}; /** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ | ||
/** | ||
* ActiveEditorRegistry provides abstractions for creating services that operate | ||
* on text editor contents. | ||
*/ | ||
function getConcreteConfig(config) { | ||
return Object.assign({}, DEFAULT_CONFIG, config); | ||
} | ||
export default class ActiveEditorRegistry<T: Provider, V> { | ||
_resultFunction: ResultFunction<T, V>; | ||
_providerRegistry: ProviderRegistry<T>; | ||
_newProviderEvents: Subject<void>; | ||
_resultsStream: Observable<Result<T, V>>; | ||
_config: ConcreteConfig; | ||
class ActiveEditorRegistry { | ||
constructor( | ||
resultFunction: ResultFunction<T, V>, | ||
config: Config = {}, | ||
eventSources: PartialEventSources = {}, | ||
) { | ||
constructor(resultFunction, config = {}, eventSources = {}) { | ||
this._config = getConcreteConfig(config); | ||
this._resultFunction = resultFunction; | ||
this._providerRegistry = new ProviderRegistry(); | ||
this._newProviderEvents = new Subject(); | ||
this._providerRegistry = new (_ProviderRegistry || _load_ProviderRegistry()).default(); | ||
this._newProviderEvents = new _rxjsBundlesRxMinJs.Subject(); | ||
this._resultsStream = this._createResultsStream({ | ||
activeEditors: eventSources.activeEditors || | ||
observeActiveEditorsDebounced(), | ||
changesForEditor: eventSources.changesForEditor || | ||
(editor => editorChangesDebounced(editor)), | ||
savesForEditor: eventSources.savesForEditor || | ||
(editor => { | ||
return observableFromSubscribeFunction(callback => | ||
editor.onDidSave(callback), | ||
).mapTo(undefined); | ||
}), | ||
activeEditors: eventSources.activeEditors || (0, (_debounced || _load_debounced()).observeActiveEditorsDebounced)(), | ||
changesForEditor: eventSources.changesForEditor || (editor => (0, (_debounced || _load_debounced()).editorChangesDebounced)(editor)), | ||
savesForEditor: eventSources.savesForEditor || (editor => { | ||
return (0, (_event || _load_event()).observableFromSubscribeFunction)(callback => editor.onDidSave(callback)).mapTo(undefined); | ||
}) | ||
}); | ||
} | ||
consumeProvider(provider: T): IDisposable { | ||
consumeProvider(provider) { | ||
this._providerRegistry.addProvider(provider); | ||
this._newProviderEvents.next(); | ||
return new Disposable(() => { | ||
return new _atom.Disposable(() => { | ||
this._providerRegistry.removeProvider(provider); | ||
@@ -153,15 +91,12 @@ }); | ||
getResultsStream(): Observable<Result<T, V>> { | ||
getResultsStream() { | ||
return this._resultsStream; | ||
} | ||
_createResultsStream(eventSources: EventSources): Observable<Result<T, V>> { | ||
_createResultsStream(eventSources) { | ||
const repeatedEditors = eventSources.activeEditors.switchMap(editor => { | ||
if (editor == null) { | ||
return Observable.of(editor); | ||
return _rxjsBundlesRxMinJs.Observable.of(editor); | ||
} | ||
return Observable.concat( | ||
Observable.of(editor), | ||
this._newProviderEvents.mapTo(editor), | ||
); | ||
return _rxjsBundlesRxMinJs.Observable.concat(_rxjsBundlesRxMinJs.Observable.of(editor), this._newProviderEvents.mapTo(editor)); | ||
}); | ||
@@ -172,31 +107,20 @@ const results = repeatedEditors.switchMap(editorArg => { | ||
if (editor == null) { | ||
return Observable.of({kind: 'not-text-editor'}); | ||
return _rxjsBundlesRxMinJs.Observable.of({ kind: 'not-text-editor' }); | ||
} | ||
return Observable.concat( | ||
// Emit a pane change event first, so that clients can do something while waiting for a | ||
// provider to give a result. | ||
Observable.of({ | ||
kind: 'pane-change', | ||
editor, | ||
}), | ||
Observable.fromPromise( | ||
this._getResultForEditor(this._getProviderForEditor(editor), editor), | ||
), | ||
this._resultsForEditor(editor, eventSources), | ||
); | ||
return _rxjsBundlesRxMinJs.Observable.concat( | ||
// Emit a pane change event first, so that clients can do something while waiting for a | ||
// provider to give a result. | ||
_rxjsBundlesRxMinJs.Observable.of({ | ||
kind: 'pane-change', | ||
editor | ||
}), _rxjsBundlesRxMinJs.Observable.fromPromise(this._getResultForEditor(this._getProviderForEditor(editor), editor)), this._resultsForEditor(editor, eventSources)); | ||
}); | ||
return cacheWhileSubscribed(results); | ||
return (0, (_observable || _load_observable()).cacheWhileSubscribed)(results); | ||
} | ||
_resultsForEditor( | ||
editor: atom$TextEditor, | ||
eventSources: EventSources, | ||
): Observable<Result<T, V>> { | ||
_resultsForEditor(editor, eventSources) { | ||
// It's possible that the active provider for an editor changes over time. | ||
// Thus, we have to subscribe to both edits and saves. | ||
return Observable.merge( | ||
eventSources.changesForEditor(editor).map(() => 'edit'), | ||
eventSources.savesForEditor(editor).map(() => 'save'), | ||
).flatMap(event => { | ||
return _rxjsBundlesRxMinJs.Observable.merge(eventSources.changesForEditor(editor).map(() => 'edit'), eventSources.savesForEditor(editor).map(() => 'save')).flatMap(event => { | ||
const provider = this._getProviderForEditor(editor); | ||
@@ -210,45 +134,42 @@ if (provider != null) { | ||
if (updateOnEdit !== (event === 'edit')) { | ||
return Observable.empty(); | ||
return _rxjsBundlesRxMinJs.Observable.empty(); | ||
} | ||
} | ||
return Observable.concat( | ||
// $FlowIssue: {kind: edit | save} <=> {kind: edit} | {kind: save} | ||
Observable.of({kind: event, editor}), | ||
Observable.fromPromise(this._getResultForEditor(provider, editor)), | ||
); | ||
return _rxjsBundlesRxMinJs.Observable.concat( | ||
// $FlowIssue: {kind: edit | save} <=> {kind: edit} | {kind: save} | ||
_rxjsBundlesRxMinJs.Observable.of({ kind: event, editor }), _rxjsBundlesRxMinJs.Observable.fromPromise(this._getResultForEditor(provider, editor))); | ||
}); | ||
} | ||
_getProviderForEditor(editor: atom$TextEditor): ?T { | ||
_getProviderForEditor(editor) { | ||
return this._providerRegistry.getProviderForEditor(editor); | ||
} | ||
async _getResultForEditor( | ||
provider: ?T, | ||
editor: atom$TextEditor, | ||
): Promise<Result<T, V>> { | ||
if (provider == null) { | ||
return { | ||
kind: 'no-provider', | ||
grammar: editor.getGrammar(), | ||
}; | ||
} | ||
try { | ||
return { | ||
kind: 'result', | ||
result: await this._resultFunction(provider, editor), | ||
provider, | ||
editor, | ||
}; | ||
} catch (e) { | ||
getLogger(this.constructor.name).error( | ||
`Error from provider for ${editor.getGrammar().scopeName}`, | ||
e, | ||
); | ||
return { | ||
provider, | ||
kind: 'provider-error', | ||
}; | ||
} | ||
_getResultForEditor(provider, editor) { | ||
var _this = this; | ||
return (0, _asyncToGenerator.default)(function* () { | ||
if (provider == null) { | ||
return { | ||
kind: 'no-provider', | ||
grammar: editor.getGrammar() | ||
}; | ||
} | ||
try { | ||
return { | ||
kind: 'result', | ||
result: yield _this._resultFunction(provider, editor), | ||
provider, | ||
editor | ||
}; | ||
} catch (e) { | ||
(0, (_log4js || _load_log4js()).getLogger)(_this.constructor.name).error(`Error from provider for ${editor.getGrammar().scopeName}`, e); | ||
return { | ||
provider, | ||
kind: 'provider-error' | ||
}; | ||
} | ||
})(); | ||
} | ||
} | ||
exports.default = ActiveEditorRegistry; |
@@ -0,1 +1,9 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _atom = require('atom'); | ||
/** | ||
@@ -8,58 +16,37 @@ * Copyright (c) 2015-present, Facebook, Inc. | ||
* | ||
* @flow | ||
* | ||
* @format | ||
*/ | ||
import type {Observable} from 'rxjs'; | ||
import {Disposable} from 'atom'; | ||
class NullTimingTracker { | ||
constructor(name: string) {} | ||
onError(error: Error) {} | ||
constructor(name) {} | ||
onError(error) {} | ||
onSuccess() {} | ||
} | ||
type TrackingEvent = { | ||
type: string, | ||
data?: Object, | ||
}; | ||
type TimingTrackerType = { | ||
onError(error: Error): void, | ||
onSuccess(): void, | ||
}; | ||
const NullService = { | ||
track(eventName: string, values?: {[key: string]: mixed}): void {}, | ||
trackEvent(event: TrackingEvent): void {}, | ||
trackEvents(events: Observable<TrackingEvent>): IDisposable { | ||
return new Disposable(); | ||
track(eventName, values) {}, | ||
trackEvent(event) {}, | ||
trackEvents(events) { | ||
return new _atom.Disposable(); | ||
}, | ||
trackImmediate( | ||
eventName: string, | ||
values?: {[key: string]: mixed}, | ||
): Promise<mixed> { | ||
trackImmediate(eventName, values) { | ||
return Promise.resolve(); | ||
}, | ||
startTracking(eventName: string): TimingTrackerType { | ||
startTracking(eventName) { | ||
return new NullTimingTracker(eventName); | ||
}, | ||
trackTiming<T>(eventName: string, operation: () => T): T { | ||
trackTiming(eventName, operation) { | ||
return operation(); | ||
}, | ||
TimingTracker: NullTimingTracker, | ||
TimingTracker: NullTimingTracker | ||
}; | ||
let service = NullService; | ||
atom.packages.serviceHub.consume( | ||
'nuclide-analytics', | ||
'0.0.0', | ||
analyticsService => { | ||
// es module export is a live binding, so modifying this updates the value | ||
// for the consumer | ||
service = analyticsService; | ||
}, | ||
); | ||
atom.packages.serviceHub.consume('nuclide-analytics', '0.0.0', analyticsService => { | ||
// es module export is a live binding, so modifying this updates the value | ||
// for the consumer | ||
service = analyticsService; | ||
}); | ||
export default service; | ||
exports.default = service; |
@@ -1,43 +0,9 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
'use strict'; | ||
import {Disposable} from 'atom'; | ||
import invariant from 'assert'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
type Item = { | ||
type: 'item', | ||
item: atom$ContextMenuItem, | ||
priority: number, | ||
}; | ||
var _atom = require('atom'); | ||
type Menu = { | ||
type: 'menu', | ||
menu: ContextMenu, | ||
priority: number, | ||
}; | ||
type InternalItem = Item | Menu; | ||
type RootMenuOptions = { | ||
type: 'root', | ||
cssSelector: string, | ||
}; | ||
type SubmenuOptions = { | ||
type: 'submenu', | ||
label: string, | ||
parent: ContextMenu, | ||
shouldDisplay?: (e: MouseEvent) => boolean, | ||
}; | ||
type MenuOptions = RootMenuOptions | SubmenuOptions; | ||
/** | ||
@@ -58,4 +24,3 @@ * This class represents a collection of context menu items that have been registered with Atom's | ||
*/ | ||
export default class ContextMenu { | ||
_menuOptions: MenuOptions; | ||
class ContextMenu { | ||
@@ -67,16 +32,3 @@ /** | ||
*/ | ||
_items: Array<InternalItem>; | ||
_needsSort: boolean; | ||
_sort: Function; | ||
/** | ||
* This is the Disposable that represents adding all of this object's menu items to Atom's own | ||
* ContextMenuManager. When a new item is added to this object, we must remove all of the items | ||
* that we previously added to the ContextMenuManager and then re-add them based on the new | ||
* ordering of priorities that results from the new item. | ||
*/ | ||
_disposable: ?IDisposable; | ||
constructor(menuOptions: MenuOptions) { | ||
constructor(menuOptions) { | ||
this._menuOptions = menuOptions; | ||
@@ -93,3 +45,11 @@ this._items = []; | ||
*/ | ||
isEmpty(): boolean { | ||
/** | ||
* This is the Disposable that represents adding all of this object's menu items to Atom's own | ||
* ContextMenuManager. When a new item is added to this object, we must remove all of the items | ||
* that we previously added to the ContextMenuManager and then re-add them based on the new | ||
* ordering of priorities that results from the new item. | ||
*/ | ||
isEmpty() { | ||
return this._items.length === 0; | ||
@@ -106,4 +66,4 @@ } | ||
*/ | ||
addItem(item: atom$ContextMenuItem, priority: number): IDisposable { | ||
const value = {type: 'item', item, priority}; | ||
addItem(item, priority) { | ||
const value = { type: 'item', item, priority }; | ||
return this._addItemToList(value); | ||
@@ -120,8 +80,8 @@ } | ||
*/ | ||
addSubmenu(contextMenu: ContextMenu, priority: number): IDisposable { | ||
const value = {type: 'menu', menu: contextMenu, priority}; | ||
addSubmenu(contextMenu, priority) { | ||
const value = { type: 'menu', menu: contextMenu, priority }; | ||
return this._addItemToList(value); | ||
} | ||
_addItemToList(value: InternalItem): IDisposable { | ||
_addItemToList(value) { | ||
this._items.push(value); | ||
@@ -133,3 +93,3 @@ this._needsSort = true; | ||
// disposed. | ||
return new Disposable(() => { | ||
return new _atom.Disposable(() => { | ||
const index = this._items.indexOf(value); | ||
@@ -149,3 +109,3 @@ this._items.splice(index, 1); | ||
*/ | ||
_sort(): void { | ||
_sort() { | ||
if (!this._needsSort) { | ||
@@ -165,6 +125,3 @@ return; | ||
this._disposable = atom.contextMenu.add({ | ||
[menuOptions.cssSelector]: items.map( | ||
this._contextMenuItemForInternalItem, | ||
this, | ||
), | ||
[menuOptions.cssSelector]: items.map(this._contextMenuItemForInternalItem, this) | ||
}); | ||
@@ -179,5 +136,3 @@ } else if (menuOptions.type === 'submenu') { | ||
/** Translates this object's internal representation of a menu item to Atom's representation. */ | ||
_contextMenuItemForInternalItem( | ||
internalItem: InternalItem, | ||
): atom$ContextMenuItem { | ||
_contextMenuItemForInternalItem(internalItem) { | ||
if (internalItem.type === 'item') { | ||
@@ -189,3 +144,7 @@ return internalItem.item; | ||
const menuOptions = internalItem.menu._menuOptions; | ||
invariant(menuOptions.type === 'submenu'); | ||
if (!(menuOptions.type === 'submenu')) { | ||
throw new Error('Invariant violation: "menuOptions.type === \'submenu\'"'); | ||
} | ||
const items = internalItem.menu._sortAndFilterItems(); | ||
@@ -195,11 +154,13 @@ return { | ||
submenu: items.map(this._contextMenuItemForInternalItem, this), | ||
shouldDisplay: menuOptions.shouldDisplay, | ||
shouldDisplay: menuOptions.shouldDisplay | ||
}; | ||
} else { | ||
invariant(false); | ||
if (!false) { | ||
throw new Error('Invariant violation: "false"'); | ||
} | ||
} | ||
} | ||
_sortAndFilterItems(): Array<InternalItem> { | ||
const items = this._items.filter((item: InternalItem) => { | ||
_sortAndFilterItems() { | ||
const items = this._items.filter(item => { | ||
if (item.type === 'item') { | ||
@@ -225,16 +186,23 @@ return true; | ||
static isEventFromContextMenu(event: Event) { | ||
static isEventFromContextMenu(event) { | ||
// Context menu commands contain a specific `detail` parameter: | ||
// https://github.com/atom/atom/blob/v1.15.0/src/main-process/context-menu.coffee#L17 | ||
return ( | ||
Array.isArray(event.detail) && | ||
event.detail[0] && | ||
(event.detail[0]: any).contextCommand | ||
); | ||
return Array.isArray(event.detail) && event.detail[0] && event.detail[0].contextCommand; | ||
} | ||
} | ||
/** Comparator used to sort menu items by priority: lower priorities appear earlier. */ | ||
function compareInternalItems(a: InternalItem, b: InternalItem): number { | ||
exports.default = ContextMenu; /** Comparator used to sort menu items by priority: lower priorities appear earlier. */ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ | ||
function compareInternalItems(a, b) { | ||
return a.priority - b.priority; | ||
} | ||
} |
@@ -1,14 +0,9 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
'use strict'; | ||
import invariant from 'assert'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = createPackage; | ||
/** | ||
@@ -33,6 +28,3 @@ * Create an Atom package from an Activation constructor. | ||
*/ | ||
export default function createPackage( | ||
moduleExports: Object, | ||
Activation: Class<any>, | ||
): void { | ||
function createPackage(moduleExports, Activation) { | ||
let activation = null; | ||
@@ -49,15 +41,13 @@ | ||
if (property === 'initialize') { | ||
throw new Error( | ||
'Your activation class contains an "initialize" method, but that work should be done in the' + | ||
' constructor.', | ||
); | ||
throw new Error('Your activation class contains an "initialize" method, but that work should be done in the' + ' constructor.'); | ||
} | ||
if (property === 'deactivate') { | ||
throw new Error( | ||
'Your activation class contains an "deactivate" method. Please use "dispose" instead.', | ||
); | ||
throw new Error('Your activation class contains an "deactivate" method. Please use "dispose" instead.'); | ||
} | ||
moduleExports[property] = function(...args) { | ||
invariant(activation != null, 'Package not initialized'); | ||
moduleExports[property] = function (...args) { | ||
if (!(activation != null)) { | ||
throw new Error('Package not initialized'); | ||
} | ||
return activation[property](...args); | ||
@@ -70,4 +60,7 @@ }; | ||
*/ | ||
moduleExports.initialize = (initialState: ?Object): void => { | ||
invariant(activation == null, 'Package already initialized'); | ||
moduleExports.initialize = initialState => { | ||
if (!(activation == null)) { | ||
throw new Error('Package already initialized'); | ||
} | ||
activation = new Activation(initialState); | ||
@@ -79,4 +72,7 @@ }; | ||
*/ | ||
moduleExports.deactivate = (): void => { | ||
invariant(activation != null, 'Package not initialized'); | ||
moduleExports.deactivate = () => { | ||
if (!(activation != null)) { | ||
throw new Error('Package not initialized'); | ||
} | ||
if (typeof activation.dispose === 'function') { | ||
@@ -87,5 +83,14 @@ activation.dispose(); | ||
}; | ||
} | ||
} /** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ | ||
function getPrototypeChain(prototype: Class<any>): Array<Class<any>> { | ||
function getPrototypeChain(prototype) { | ||
let currentPrototype = prototype; | ||
@@ -104,6 +109,6 @@ const prototypes = []; | ||
*/ | ||
function getPropertyList(prototype: Class<any>): Array<string> { | ||
function getPropertyList(prototype) { | ||
const properties = []; | ||
for (const proto of getPrototypeChain(prototype)) { | ||
if (proto === (Object: any).prototype) { | ||
if (proto === Object.prototype) { | ||
break; | ||
@@ -116,2 +121,2 @@ } | ||
return properties; | ||
} | ||
} |
121
debounced.js
@@ -1,12 +0,37 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.observeActivePaneItemDebounced = observeActivePaneItemDebounced; | ||
exports.observeActiveEditorsDebounced = observeActiveEditorsDebounced; | ||
exports.editorChangesDebounced = editorChangesDebounced; | ||
exports.editorScrollTopDebounced = editorScrollTopDebounced; | ||
exports.observeTextEditorsPositions = observeTextEditorsPositions; | ||
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); | ||
var _event; | ||
function _load_event() { | ||
return _event = require('nuclide-commons/event'); | ||
} | ||
var _textEditor; | ||
function _load_textEditor() { | ||
return _textEditor = require('./text-editor'); | ||
} | ||
const DEFAULT_PANE_DEBOUNCE_INTERVAL_MS = 100; /** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ | ||
/** | ||
@@ -21,16 +46,7 @@ * Often, we may want to respond to Atom events, but only after a buffer period | ||
import {Observable} from 'rxjs'; | ||
import {observableFromSubscribeFunction} from 'nuclide-commons/event'; | ||
import {getCursorPositions, isValidTextEditor} from './text-editor'; | ||
import invariant from 'assert'; | ||
const DEFAULT_PANE_DEBOUNCE_INTERVAL_MS = 100; | ||
const DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS = 300; | ||
const DEFAULT_POSITION_DEBOUNCE_INTERVAL_MS = 300; | ||
export function observeActivePaneItemDebounced( | ||
debounceInterval: number = DEFAULT_PANE_DEBOUNCE_INTERVAL_MS, | ||
): Observable<mixed> { | ||
return observableFromSubscribeFunction(callback => { | ||
function observeActivePaneItemDebounced(debounceInterval = DEFAULT_PANE_DEBOUNCE_INTERVAL_MS) { | ||
return (0, (_event || _load_event()).observableFromSubscribeFunction)(callback => { | ||
if (atom.workspace.getCenter != null) { | ||
@@ -43,10 +59,8 @@ return atom.workspace.getCenter().observeActivePaneItem(callback); | ||
export function observeActiveEditorsDebounced( | ||
debounceInterval: number = DEFAULT_PANE_DEBOUNCE_INTERVAL_MS, | ||
): Observable<?atom$TextEditor> { | ||
function observeActiveEditorsDebounced(debounceInterval = DEFAULT_PANE_DEBOUNCE_INTERVAL_MS) { | ||
return observeActivePaneItemDebounced(debounceInterval).map(paneItem => { | ||
if (isValidTextEditor(paneItem)) { | ||
if ((0, (_textEditor || _load_textEditor()).isValidTextEditor)(paneItem)) { | ||
// Flow cannot understand the type refinement provided by the isValidTextEditor function, | ||
// so we have to cast. | ||
return ((paneItem: any): atom$TextEditor); | ||
return paneItem; | ||
} | ||
@@ -57,46 +71,25 @@ return null; | ||
export function editorChangesDebounced( | ||
editor: atom$TextEditor, | ||
debounceInterval: number = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS, | ||
): Observable<void> { | ||
return ( | ||
observableFromSubscribeFunction(callback => editor.onDidChange(callback)) | ||
// Debounce manually rather than using editor.onDidStopChanging so that the debounce time is | ||
// configurable. | ||
.debounceTime(debounceInterval) | ||
); | ||
function editorChangesDebounced(editor, debounceInterval = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS) { | ||
return (0, (_event || _load_event()).observableFromSubscribeFunction)(callback => editor.onDidChange(callback)) | ||
// Debounce manually rather than using editor.onDidStopChanging so that the debounce time is | ||
// configurable. | ||
.debounceTime(debounceInterval); | ||
} | ||
export function editorScrollTopDebounced( | ||
editor: atom$TextEditor, | ||
debounceInterval: number = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS, | ||
): Observable<number> { | ||
return observableFromSubscribeFunction(callback => | ||
atom.views.getView(editor).onDidChangeScrollTop(callback), | ||
).debounceTime(debounceInterval); | ||
function editorScrollTopDebounced(editor, debounceInterval = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS) { | ||
return (0, (_event || _load_event()).observableFromSubscribeFunction)(callback => atom.views.getView(editor).onDidChangeScrollTop(callback)).debounceTime(debounceInterval); | ||
} | ||
export type EditorPosition = { | ||
editor: atom$TextEditor, | ||
position: atom$Point, | ||
}; | ||
// Yields null when the current pane is not an editor, | ||
// otherwise yields events on each move of the primary cursor within any Editor. | ||
export function observeTextEditorsPositions( | ||
editorDebounceInterval: number = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS, | ||
positionDebounceInterval: number = DEFAULT_POSITION_DEBOUNCE_INTERVAL_MS, | ||
): Observable<?EditorPosition> { | ||
return observeActiveEditorsDebounced( | ||
editorDebounceInterval, | ||
).switchMap(editor => { | ||
return editor == null | ||
? Observable.of(null) | ||
: getCursorPositions(editor) | ||
.debounceTime(positionDebounceInterval) | ||
.map(position => { | ||
invariant(editor != null); | ||
return {editor, position}; | ||
}); | ||
function observeTextEditorsPositions(editorDebounceInterval = DEFAULT_EDITOR_DEBOUNCE_INTERVAL_MS, positionDebounceInterval = DEFAULT_POSITION_DEBOUNCE_INTERVAL_MS) { | ||
return observeActiveEditorsDebounced(editorDebounceInterval).switchMap(editor => { | ||
return editor == null ? _rxjsBundlesRxMinJs.Observable.of(null) : (0, (_textEditor || _load_textEditor()).getCursorPositions)(editor).debounceTime(positionDebounceInterval).map(position => { | ||
if (!(editor != null)) { | ||
throw new Error('Invariant violation: "editor != null"'); | ||
} | ||
return { editor, position }; | ||
}); | ||
}); | ||
} | ||
} |
@@ -0,1 +1,10 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); | ||
// Default to "nuclide", but only for unit tests. | ||
/** | ||
@@ -8,3 +17,3 @@ * Copyright (c) 2015-present, Facebook, Inc. | ||
* | ||
* @flow | ||
* | ||
* @format | ||
@@ -18,6 +27,2 @@ */ | ||
import invariant from 'assert'; | ||
import {Observable} from 'rxjs'; | ||
// Default to "nuclide", but only for unit tests. | ||
let packageName = atom.inSpecMode() ? 'nuclide' : null; | ||
@@ -29,8 +34,11 @@ | ||
*/ | ||
function setPackageName(name: string): void { | ||
function setPackageName(name) { | ||
packageName = name; | ||
} | ||
function formatKeyPath(keyPath: string): string { | ||
invariant(packageName, 'feature-config must be used with FeatureLoader.'); | ||
function formatKeyPath(keyPath) { | ||
if (!packageName) { | ||
throw new Error('feature-config must be used with FeatureLoader.'); | ||
} | ||
return `${packageName}.${keyPath}`; | ||
@@ -51,28 +59,10 @@ } | ||
*/ | ||
function get( | ||
keyPath: string, | ||
options?: { | ||
excludeSources?: Array<string>, | ||
sources?: Array<string>, | ||
scope?: Object, | ||
}, | ||
): mixed { | ||
function get(keyPath, options) { | ||
// atom.config.get will crash if the second arg is present and undefined. | ||
// It does not crash if the second arg is missing. | ||
return atom.config.get( | ||
formatKeyPath(keyPath), | ||
...(options == null ? [] : [options]), | ||
); | ||
return atom.config.get(formatKeyPath(keyPath), ...(options == null ? [] : [options])); | ||
} | ||
function getWithDefaults<T>( | ||
keyPath: string, | ||
defaults: T, | ||
options?: { | ||
excludeSources?: Array<string>, | ||
sources?: Array<string>, | ||
scope?: Object, | ||
}, | ||
): T { | ||
const current: any = get(keyPath, options); | ||
function getWithDefaults(keyPath, defaults, options) { | ||
const current = get(keyPath, options); | ||
return current == null ? defaults : current; | ||
@@ -85,3 +75,3 @@ } | ||
*/ | ||
function getSchema(keyPath: string): atom$ConfigSchema { | ||
function getSchema(keyPath) { | ||
return atom.config.getSchema(formatKeyPath(keyPath)); | ||
@@ -95,6 +85,3 @@ } | ||
*/ | ||
function observe( | ||
keyPath: string, | ||
callback: (value: mixed) => mixed, | ||
): IDisposable { | ||
function observe(keyPath, callback) { | ||
return atom.config.observe(formatKeyPath(keyPath), callback); | ||
@@ -107,4 +94,4 @@ } | ||
*/ | ||
function observeAsStream(keyPath: string): Observable<mixed> { | ||
return Observable.create(observer => { | ||
function observeAsStream(keyPath) { | ||
return _rxjsBundlesRxMinJs.Observable.create(observer => { | ||
const disposable = observe(keyPath, observer.next.bind(observer)); | ||
@@ -119,11 +106,4 @@ return disposable.dispose.bind(disposable); | ||
*/ | ||
function onDidChange( | ||
keyPath: string, | ||
optionsOrCallback: Object | ((event: Object) => void), | ||
callback?: (event: Object) => void, | ||
): IDisposable { | ||
return atom.config.onDidChange( | ||
formatKeyPath(keyPath), | ||
...Array.prototype.slice.call(arguments, 1), | ||
); | ||
function onDidChange(keyPath, optionsOrCallback, callback) { | ||
return atom.config.onDidChange(formatKeyPath(keyPath), ...Array.prototype.slice.call(arguments, 1)); | ||
} | ||
@@ -135,14 +115,4 @@ | ||
*/ | ||
function set( | ||
keyPath: string, | ||
value: ?mixed, | ||
options?: { | ||
scopeSelector?: string, | ||
source?: string, | ||
}, | ||
): boolean { | ||
return atom.config.set( | ||
formatKeyPath(keyPath), | ||
...Array.prototype.slice.call(arguments, 1), | ||
); | ||
function set(keyPath, value, options) { | ||
return atom.config.set(formatKeyPath(keyPath), ...Array.prototype.slice.call(arguments, 1)); | ||
} | ||
@@ -154,7 +124,4 @@ | ||
*/ | ||
function setSchema(keyPath: string, schema: Object): void { | ||
return atom.config.setSchema( | ||
formatKeyPath(keyPath), | ||
...Array.prototype.slice.call(arguments, 1), | ||
); | ||
function setSchema(keyPath, schema) { | ||
return atom.config.setSchema(formatKeyPath(keyPath), ...Array.prototype.slice.call(arguments, 1)); | ||
} | ||
@@ -166,13 +133,4 @@ | ||
*/ | ||
function unset( | ||
keyPath: string, | ||
options?: { | ||
scopeSelector?: string, | ||
source?: string, | ||
}, | ||
): void { | ||
return atom.config.unset( | ||
formatKeyPath(keyPath), | ||
...Array.prototype.slice.call(arguments, 1), | ||
); | ||
function unset(keyPath, options) { | ||
return atom.config.unset(formatKeyPath(keyPath), ...Array.prototype.slice.call(arguments, 1)); | ||
} | ||
@@ -184,11 +142,11 @@ | ||
*/ | ||
function isFeatureDisabled(name: string): boolean { | ||
invariant(packageName, 'feature-config must be used with FeatureLoader.'); | ||
return ( | ||
atom.packages.isPackageDisabled(packageName) || | ||
!atom.config.get(`${packageName}.use.${name}`) | ||
); | ||
function isFeatureDisabled(name) { | ||
if (!packageName) { | ||
throw new Error('feature-config must be used with FeatureLoader.'); | ||
} | ||
return atom.packages.isPackageDisabled(packageName) || !atom.config.get(`${packageName}.use.${name}`); | ||
} | ||
export default { | ||
exports.default = { | ||
setPackageName, | ||
@@ -204,3 +162,3 @@ get, | ||
unset, | ||
isFeatureDisabled, | ||
}; | ||
isFeatureDisabled | ||
}; |
@@ -1,50 +0,26 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
'use strict'; | ||
/* global localStorage */ | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
import invariant from 'assert'; | ||
import UniversalDisposable from 'nuclide-commons/UniversalDisposable'; | ||
import featureConfig from './feature-config'; | ||
var _UniversalDisposable; | ||
type FeaturePkg = { | ||
atomConfig?: Object, | ||
consumedServices?: Object, | ||
description?: string, | ||
name: string, | ||
nuclide?: { | ||
config?: Object, | ||
}, | ||
providedServices?: Object, | ||
}; | ||
function _load_UniversalDisposable() { | ||
return _UniversalDisposable = _interopRequireDefault(require('nuclide-commons/UniversalDisposable')); | ||
} | ||
export type Feature = { | ||
dirname: string, | ||
pkg: FeaturePkg, | ||
}; | ||
var _featureConfig; | ||
type FeatureLoaderParams = { | ||
pkgName: string, | ||
features: Array<Feature>, | ||
}; | ||
function _load_featureConfig() { | ||
return _featureConfig = _interopRequireDefault(require('./feature-config')); | ||
} | ||
export default class FeatureLoader { | ||
_activationDisposable: ?UniversalDisposable; | ||
_loadDisposable: UniversalDisposable; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
_config: Object; | ||
_features: Array<Feature>; | ||
_pkgName: string; | ||
class FeatureLoader { | ||
constructor({config, features, pkgName}: FeatureLoaderParams) { | ||
constructor({ config, features, pkgName }) { | ||
this._features = features; | ||
this._loadDisposable = new UniversalDisposable(); | ||
this._loadDisposable = new (_UniversalDisposable || _load_UniversalDisposable()).default(); | ||
this._pkgName = pkgName; | ||
@@ -55,4 +31,4 @@ this._config = { | ||
collapsed: true, | ||
properties: {}, | ||
}, | ||
properties: {} | ||
} | ||
}; | ||
@@ -62,4 +38,6 @@ } | ||
// Build the config. Should occur with root package's load | ||
load(): void { | ||
invariant(!this._loadDisposable.disposed); | ||
load() { | ||
if (!!this._loadDisposable.disposed) { | ||
throw new Error('Invariant violation: "!this._loadDisposable.disposed"'); | ||
} | ||
@@ -76,9 +54,9 @@ // Add a dummy deserializer. This forces Atom to load Nuclide's main module | ||
// [1] https://github.com/atom/atom/blob/v1.9.8/src/package.coffee#L442 | ||
this._loadDisposable.add( | ||
atom.deserializers.add({ | ||
name: `${this._pkgName}.ForceMainModuleLoad`, | ||
deserialize() {}, | ||
}), | ||
); | ||
this._loadDisposable.add(atom.deserializers.add({ | ||
name: `${this._pkgName}.ForceMainModuleLoad`, | ||
deserialize() {} | ||
})); | ||
// | ||
@@ -105,3 +83,3 @@ // Build the "config" object. This determines the config defaults and | ||
type: 'boolean', | ||
default: enabled, | ||
default: enabled | ||
}; | ||
@@ -119,5 +97,3 @@ if (featurePkg.providedServices) { | ||
// Merge in the feature's config | ||
const featurePkgConfig = | ||
featurePkg.atomConfig || | ||
(featurePkg.nuclide && featurePkg.nuclide.config); | ||
const featurePkgConfig = featurePkg.atomConfig || featurePkg.nuclide && featurePkg.nuclide.config; | ||
@@ -128,13 +104,12 @@ if (featurePkgConfig) { | ||
collapsed: true, | ||
properties: {}, | ||
properties: {} | ||
}; | ||
Object.keys(featurePkgConfig).forEach(key => { | ||
this._config[name].properties[key] = { | ||
...featurePkgConfig[key], | ||
title: featurePkgConfig[key].title || key, | ||
}; | ||
this._config[name].properties[key] = Object.assign({}, featurePkgConfig[key], { | ||
title: featurePkgConfig[key].title || key | ||
}); | ||
}); | ||
} | ||
}); | ||
featureConfig.setPackageName(this._pkgName); | ||
(_featureConfig || _load_featureConfig()).default.setPackageName(this._pkgName); | ||
@@ -159,5 +134,3 @@ // Nesting loads within loads leads to reverse activation order- that is, if | ||
const enabled = atom.config.get(this.useKeyPathForFeature(feature)); | ||
const shouldEnable = enabled == null | ||
? this._config.use.properties[feature.pkg.name].default | ||
: enabled; | ||
const shouldEnable = enabled == null ? this._config.use.properties[feature.pkg.name].default : enabled; | ||
@@ -169,3 +142,6 @@ if (shouldEnable) { | ||
invariant(initialLoadDisposable != null); | ||
if (!(initialLoadDisposable != null)) { | ||
throw new Error('Invariant violation: "initialLoadDisposable != null"'); | ||
} | ||
this._loadDisposable.remove(initialLoadDisposable); | ||
@@ -178,15 +154,20 @@ initialLoadDisposable.dispose(); | ||
activate(): void { | ||
invariant(this._activationDisposable == null); | ||
activate() { | ||
if (!(this._activationDisposable == null)) { | ||
throw new Error('Invariant violation: "this._activationDisposable == null"'); | ||
} | ||
const rootPackage = atom.packages.getLoadedPackage(this._pkgName); | ||
invariant(rootPackage != null); | ||
if (!(rootPackage != null)) { | ||
throw new Error('Invariant violation: "rootPackage != null"'); | ||
} | ||
// This is a failsafe in case the `.ForceMainModuleLoad` deserializer | ||
// defined above does not register in time, or if the defer key has been set | ||
// w/o our knowledge. This can happen during OSS upgrades. | ||
localStorage.removeItem( | ||
rootPackage.getCanDeferMainModuleRequireStorageKey(), | ||
); | ||
localStorage.removeItem(rootPackage.getCanDeferMainModuleRequireStorageKey()); | ||
this._features.forEach(feature => { | ||
@@ -199,19 +180,15 @@ if (atom.config.get(this.useKeyPathForFeature(feature))) { | ||
// Watch the config to manage toggling features | ||
this._activationDisposable = new UniversalDisposable( | ||
...this._features.map(feature => | ||
atom.config.onDidChange(this.useKeyPathForFeature(feature), event => { | ||
if (event.newValue === true) { | ||
atom.packages.activatePackage(feature.dirname); | ||
} else if (event.newValue === false) { | ||
safeDeactivate(feature); | ||
} | ||
}), | ||
), | ||
); | ||
this._activationDisposable = new (_UniversalDisposable || _load_UniversalDisposable()).default(...this._features.map(feature => atom.config.onDidChange(this.useKeyPathForFeature(feature), event => { | ||
if (event.newValue === true) { | ||
atom.packages.activatePackage(feature.dirname); | ||
} else if (event.newValue === false) { | ||
safeDeactivate(feature); | ||
} | ||
}))); | ||
} | ||
deactivate(): void { | ||
invariant( | ||
this._activationDisposable && !this._activationDisposable.disposed, | ||
); | ||
deactivate() { | ||
if (!(this._activationDisposable && !this._activationDisposable.disposed)) { | ||
throw new Error('Invariant violation: "this._activationDisposable && !this._activationDisposable.disposed"'); | ||
} | ||
@@ -224,3 +201,7 @@ this._features.forEach(feature => { | ||
invariant(this._activationDisposable); // reasserting for flow | ||
if (!this._activationDisposable) { | ||
throw new Error('Invariant violation: "this._activationDisposable"'); | ||
} // reasserting for flow | ||
this._activationDisposable.dispose(); | ||
@@ -230,7 +211,7 @@ this._activationDisposable = null; | ||
getConfig(): Object { | ||
getConfig() { | ||
return this._config; | ||
} | ||
serialize(): void { | ||
serialize() { | ||
// When the root package is serialized, all of its features need to be serialized. This is an abuse of | ||
@@ -244,3 +225,3 @@ // `serialize()` since we're using it to do side effects instead of returning the serialization, | ||
useKeyPathForFeature(feature: Feature): string { | ||
useKeyPathForFeature(feature) { | ||
return `${this._pkgName}.use.${feature.pkg.name}`; | ||
@@ -250,6 +231,16 @@ } | ||
function safeDeactivate( | ||
feature: Feature, | ||
suppressSerialization: boolean = false, | ||
) { | ||
exports.default = FeatureLoader; /** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ | ||
/* global localStorage */ | ||
function safeDeactivate(feature, suppressSerialization = false) { | ||
const name = feature.pkg.name; | ||
@@ -267,3 +258,3 @@ try { | ||
function safeSerialize(feature: Feature) { | ||
function safeSerialize(feature) { | ||
const name = feature.pkg.name; | ||
@@ -280,2 +271,2 @@ try { | ||
} | ||
} | ||
} |
@@ -1,16 +0,10 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
'use strict'; | ||
import {Subject} from 'rxjs'; | ||
import type {Observable} from 'rxjs'; | ||
import invariant from 'assert'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.goToLocation = undefined; | ||
var _asyncToGenerator = _interopRequireDefault(require('async-to-generator')); | ||
/** | ||
@@ -40,52 +34,61 @@ * Opens the given file. | ||
*/ | ||
export async function goToLocation( | ||
file: string, | ||
line?: number, | ||
column?: number, | ||
center?: boolean = true, | ||
): Promise<atom$TextEditor> { | ||
// Prefer going to the current editor rather than the leftmost editor. | ||
const currentEditor = atom.workspace.getActiveTextEditor(); | ||
if (currentEditor != null && currentEditor.getPath() === file) { | ||
if (line != null) { | ||
goToLocationInEditor( | ||
currentEditor, | ||
line, | ||
column == null ? 0 : column, | ||
center, | ||
); | ||
let goToLocation = exports.goToLocation = (() => { | ||
var _ref = (0, _asyncToGenerator.default)(function* (file, line, column, center = true) { | ||
// Prefer going to the current editor rather than the leftmost editor. | ||
const currentEditor = atom.workspace.getActiveTextEditor(); | ||
if (currentEditor != null && currentEditor.getPath() === file) { | ||
if (line != null) { | ||
goToLocationInEditor(currentEditor, line, column == null ? 0 : column, center); | ||
} else { | ||
if (!(column == null)) { | ||
throw new Error('goToLocation: Cannot specify just column'); | ||
} | ||
} | ||
return currentEditor; | ||
} else { | ||
invariant(column == null, 'goToLocation: Cannot specify just column'); | ||
} | ||
return currentEditor; | ||
} else { | ||
// Obviously, calling goToLocation isn't a viable alternative here :P | ||
// eslint-disable-next-line nuclide-internal/atom-apis | ||
const editor = await atom.workspace.open(file, { | ||
initialLine: line, | ||
initialColumn: column, | ||
searchAllPanes: true, | ||
}); | ||
// Obviously, calling goToLocation isn't a viable alternative here :P | ||
// eslint-disable-next-line nuclide-internal/atom-apis | ||
const editor = yield atom.workspace.open(file, { | ||
initialLine: line, | ||
initialColumn: column, | ||
searchAllPanes: true | ||
}); | ||
if (center && line != null) { | ||
editor.scrollToBufferPosition([line, column], {center: true}); | ||
if (center && line != null) { | ||
editor.scrollToBufferPosition([line, column], { center: true }); | ||
} | ||
return editor; | ||
} | ||
return editor; | ||
} | ||
} | ||
}); | ||
const goToLocationSubject = new Subject(); | ||
return function goToLocation(_x, _x2, _x3) { | ||
return _ref.apply(this, arguments); | ||
}; | ||
})(); /** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ | ||
exports.goToLocationInEditor = goToLocationInEditor; | ||
exports.observeNavigatingEditors = observeNavigatingEditors; | ||
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
const goToLocationSubject = new _rxjsBundlesRxMinJs.Subject(); | ||
// Scrolls to the given line/column at the given editor | ||
// broadcasts the editor instance on an observable (subject) available | ||
// through the getGoToLocation | ||
export function goToLocationInEditor( | ||
editor: atom$TextEditor, | ||
line: number, | ||
column: number, | ||
center: boolean = true, | ||
): void { | ||
function goToLocationInEditor(editor, line, column, center = true) { | ||
editor.setCursorBufferPosition([line, column]); | ||
if (center) { | ||
editor.scrollToBufferPosition([line, column], {center: true}); | ||
editor.scrollToBufferPosition([line, column], { center: true }); | ||
} | ||
@@ -96,4 +99,4 @@ | ||
export function observeNavigatingEditors(): Observable<atom$TextEditor> { | ||
function observeNavigatingEditors() { | ||
return goToLocationSubject; | ||
} | ||
} |
@@ -1,26 +0,34 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
'use strict'; | ||
import invariant from 'assert'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.bufferPositionForMouseEvent = bufferPositionForMouseEvent; | ||
function bufferPositionForMouseEvent(event, editor = null) { | ||
const _editor = editor || atom.workspace.getActiveTextEditor(); | ||
export function bufferPositionForMouseEvent( | ||
event: MouseEvent, | ||
editor: ?atom$TextEditor = null, | ||
): atom$Point { | ||
const _editor = editor || atom.workspace.getActiveTextEditor(); | ||
invariant(_editor != null); | ||
if (!(_editor != null)) { | ||
throw new Error('Invariant violation: "_editor != null"'); | ||
} | ||
const view = atom.views.getView(_editor); | ||
const component = view.component; | ||
invariant(component != null); | ||
if (!(component != null)) { | ||
throw new Error('Invariant violation: "component != null"'); | ||
} | ||
// Beware, screenPositionForMouseEvent is not a public api and may change in future versions. | ||
const screenPosition = component.screenPositionForMouseEvent(event); | ||
return _editor.bufferPositionForScreenPosition(screenPosition); | ||
} | ||
} /** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ |
@@ -1,21 +0,27 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
'use strict'; | ||
import {observableFromSubscribeFunction} from 'nuclide-commons/event'; | ||
import memoizeUntilChanged from 'nuclide-commons/memoizeUntilChanged'; | ||
import {Observable, Scheduler} from 'rxjs'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.default = observePaneItemVisibility; | ||
var _event; | ||
function _load_event() { | ||
return _event = require('nuclide-commons/event'); | ||
} | ||
var _memoizeUntilChanged; | ||
function _load_memoizeUntilChanged() { | ||
return _memoizeUntilChanged = _interopRequireDefault(require('nuclide-commons/memoizeUntilChanged')); | ||
} | ||
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
// TODO(T17495608): Currently, docks don't have a way of observing their visibility so this will | ||
// have some false positives when an item is its pane's active item but its dock is hidden. | ||
export default function observePaneItemVisibility( | ||
item: Object, | ||
): Observable<boolean> { | ||
function observePaneItemVisibility(item) { | ||
// If this is a version of Atom that doesn't have Docks, return an empty observable. Until they | ||
@@ -26,3 +32,3 @@ // land, the functionality is provided by the workspace views package, which calls | ||
if (atom.workspace.getPaneContainers == null) { | ||
return Observable.empty(); | ||
return _rxjsBundlesRxMinJs.Observable.empty(); | ||
} | ||
@@ -33,31 +39,30 @@ | ||
// $FlowFixMe: Add atom.workspace.getElement() after 1.17. | ||
return observeActiveItems(atom.workspace.getElement()) | ||
.map(activeItems => activeItems.includes(item)) | ||
.distinctUntilChanged(); | ||
} | ||
return observeActiveItems(atom.workspace.getElement()).map(activeItems => activeItems.includes(item)).distinctUntilChanged(); | ||
} /** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ | ||
const observeActiveItems = memoizeUntilChanged(_cacheKey => { | ||
const observeActiveItems = (0, (_memoizeUntilChanged || _load_memoizeUntilChanged()).default)(_cacheKey => { | ||
// An observable that emits `{pane, item}` whenever the active item of a pane changes. | ||
const itemActivations = Observable.merge( | ||
// $FlowFixMe: Add `getPaneContainers()` to the type defs once Atom 1.17 lands. | ||
...atom.workspace.getPaneContainers().map(paneContainer => { | ||
const observePanes = paneContainer.observePanes.bind(paneContainer); | ||
return observableFromSubscribeFunction(observePanes).flatMap(pane => { | ||
const paneDestroyed = observableFromSubscribeFunction( | ||
pane.onDidDestroy.bind(pane), | ||
); | ||
const activeItems = observableFromSubscribeFunction( | ||
pane.observeActiveItem.bind(pane), | ||
).takeUntil(paneDestroyed); | ||
return Observable.concat( | ||
activeItems.map(item => ({pane, item})), | ||
Observable.of({pane, item: null}), | ||
); | ||
}); | ||
}), | ||
); | ||
const itemActivations = _rxjsBundlesRxMinJs.Observable.merge( | ||
// $FlowFixMe: Add `getPaneContainers()` to the type defs once Atom 1.17 lands. | ||
...atom.workspace.getPaneContainers().map(paneContainer => { | ||
const observePanes = paneContainer.observePanes.bind(paneContainer); | ||
return (0, (_event || _load_event()).observableFromSubscribeFunction)(observePanes).flatMap(pane => { | ||
const paneDestroyed = (0, (_event || _load_event()).observableFromSubscribeFunction)(pane.onDidDestroy.bind(pane)); | ||
const activeItems = (0, (_event || _load_event()).observableFromSubscribeFunction)(pane.observeActiveItem.bind(pane)).takeUntil(paneDestroyed); | ||
return _rxjsBundlesRxMinJs.Observable.concat(activeItems.map(item => ({ pane, item })), _rxjsBundlesRxMinJs.Observable.of({ pane, item: null })); | ||
}); | ||
})); | ||
// Create a map of panes to their active items. We could look this up by examining the workspace | ||
// every time; this is an optimization. | ||
const panesToActiveItem = itemActivations.scan((acc, {pane, item}) => { | ||
const panesToActiveItem = itemActivations.scan((acc, { pane, item }) => { | ||
if (item == null) { | ||
@@ -71,10 +76,6 @@ acc.delete(pane); | ||
return ( | ||
panesToActiveItem | ||
// When dragging items between panes, they'll be quickly deactivated and activated again. To | ||
// avoid doing extra work, we debounce and use the rAF scheduler. | ||
.debounceTime(0, Scheduler.animationFrame) | ||
.map(map => Array.from(map.values())) | ||
.share() | ||
); | ||
}); | ||
return panesToActiveItem | ||
// When dragging items between panes, they'll be quickly deactivated and activated again. To | ||
// avoid doing extra work, we debounce and use the rAF scheduler. | ||
.debounceTime(0, _rxjsBundlesRxMinJs.Scheduler.animationFrame).map(map => Array.from(map.values())).share(); | ||
}); |
{ | ||
"name": "nuclide-commons-atom", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"description": "Common Nuclide node modules (for use with Atom only).", | ||
@@ -9,3 +9,3 @@ "license": "SEE LICENSE IN LICENSE", | ||
"scripts": { | ||
"prepublishOnly": "../../scripts/release-transpile.js --overwrite .", | ||
"prepublish": "../../scripts/modules-prepublish.sh", | ||
"test": "atom --dev --test spec" | ||
@@ -17,3 +17,3 @@ }, | ||
"log4js": "1.1.1", | ||
"nuclide-commons": "0.1.3", | ||
"nuclide-commons": "0.1.4", | ||
"rxjs": "5.3.1", | ||
@@ -20,0 +20,0 @@ "semver": "5.3.0" |
103
projects.js
@@ -1,35 +0,46 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
'use strict'; | ||
import type {NuclideUri} from 'nuclide-commons/nuclideUri'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.getAtomProjectRelativePath = getAtomProjectRelativePath; | ||
exports.getAtomProjectRootPath = getAtomProjectRootPath; | ||
exports.relativizePathWithDirectory = relativizePathWithDirectory; | ||
exports.getDirectoryForPath = getDirectoryForPath; | ||
exports.getFileForPath = getFileForPath; | ||
exports.observeProjectPaths = observeProjectPaths; | ||
exports.onDidAddProjectPath = onDidAddProjectPath; | ||
exports.onDidRemoveProjectPath = onDidRemoveProjectPath; | ||
import {File, Directory} from 'atom'; | ||
import nuclideUri from 'nuclide-commons/nuclideUri'; | ||
var _atom = require('atom'); | ||
function getValidProjectPaths(): Array<string> { | ||
return atom.project | ||
.getDirectories() | ||
.filter(directory => { | ||
// If a remote directory path is a local `Directory` instance, the project path | ||
// isn't yet ready for consumption. | ||
if ( | ||
nuclideUri.isRemote(directory.getPath()) && | ||
directory instanceof Directory | ||
) { | ||
return false; | ||
} | ||
return true; | ||
}) | ||
.map(directory => directory.getPath()); | ||
var _nuclideUri; | ||
function _load_nuclideUri() { | ||
return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri')); | ||
} | ||
export function getAtomProjectRelativePath(path: NuclideUri): ?string { | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function getValidProjectPaths() { | ||
return atom.project.getDirectories().filter(directory => { | ||
// If a remote directory path is a local `Directory` instance, the project path | ||
// isn't yet ready for consumption. | ||
if ((_nuclideUri || _load_nuclideUri()).default.isRemote(directory.getPath()) && directory instanceof _atom.Directory) { | ||
return false; | ||
} | ||
return true; | ||
}).map(directory => directory.getPath()); | ||
} /** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ | ||
function getAtomProjectRelativePath(path) { | ||
const [projectPath, relativePath] = atom.project.relativizePath(path); | ||
@@ -42,3 +53,3 @@ if (!projectPath) { | ||
export function getAtomProjectRootPath(path: NuclideUri): ?string { | ||
function getAtomProjectRootPath(path) { | ||
const [projectPath] = atom.project.relativizePath(path); | ||
@@ -55,8 +66,6 @@ return projectPath; | ||
*/ | ||
export function relativizePathWithDirectory( | ||
path: NuclideUri, | ||
): [?Directory, NuclideUri] { | ||
function relativizePathWithDirectory(path) { | ||
for (const directory of atom.project.getDirectories()) { | ||
try { | ||
const relativePath = nuclideUri.relative(directory.getPath(), path); | ||
const relativePath = (_nuclideUri || _load_nuclideUri()).default.relative(directory.getPath(), path); | ||
return [directory, relativePath]; | ||
@@ -70,3 +79,3 @@ } catch (e) { | ||
export function getDirectoryForPath(path: NuclideUri): ?Directory { | ||
function getDirectoryForPath(path) { | ||
const [directory, relativePath] = relativizePathWithDirectory(path); | ||
@@ -79,3 +88,3 @@ if (directory == null) { | ||
export function getFileForPath(path: NuclideUri): ?File { | ||
function getFileForPath(path) { | ||
const [directory, relativePath] = relativizePathWithDirectory(path); | ||
@@ -88,5 +97,3 @@ if (directory == null) { | ||
export function observeProjectPaths( | ||
callback: (projectPath: string) => any, | ||
): IDisposable { | ||
function observeProjectPaths(callback) { | ||
getValidProjectPaths().forEach(callback); | ||
@@ -96,7 +103,5 @@ return onDidAddProjectPath(callback); | ||
export function onDidAddProjectPath( | ||
callback: (projectPath: string) => void, | ||
): IDisposable { | ||
let projectPaths: Array<string> = getValidProjectPaths(); | ||
let changing: boolean = false; | ||
function onDidAddProjectPath(callback) { | ||
let projectPaths = getValidProjectPaths(); | ||
let changing = false; | ||
return atom.project.onDidChangePaths(() => { | ||
@@ -118,7 +123,5 @@ if (changing) { | ||
export function onDidRemoveProjectPath( | ||
callback: (projectPath: string) => void, | ||
): IDisposable { | ||
let projectPaths: Array<string> = getValidProjectPaths(); | ||
let changing: boolean = false; | ||
function onDidRemoveProjectPath(callback) { | ||
let projectPaths = getValidProjectPaths(); | ||
let changing = false; | ||
return atom.project.onDidChangePaths(() => { | ||
@@ -138,2 +141,2 @@ if (changing) { | ||
}); | ||
} | ||
} |
@@ -1,20 +0,8 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
"use strict"; | ||
export type Provider = { | ||
priority: number, | ||
grammarScopes: Array<string>, | ||
}; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
class ProviderRegistry { | ||
export default class ProviderRegistry<T: Provider> { | ||
_providers: Set<T>; | ||
constructor() { | ||
@@ -24,11 +12,11 @@ this._providers = new Set(); | ||
addProvider(provider: T): void { | ||
addProvider(provider) { | ||
this._providers.add(provider); | ||
} | ||
removeProvider(provider: T): void { | ||
removeProvider(provider) { | ||
this._providers.delete(provider); | ||
} | ||
getProviderForEditor(editor: atom$TextEditor): ?T { | ||
getProviderForEditor(editor) { | ||
const grammar = editor.getGrammar().scopeName; | ||
@@ -38,3 +26,3 @@ return this.findProvider(grammar); | ||
findProvider(grammar: string): ?T { | ||
findProvider(grammar) { | ||
let bestProvider = null; | ||
@@ -53,1 +41,11 @@ let bestPriority = Number.NEGATIVE_INFINITY; | ||
} | ||
exports.default = ProviderRegistry; /** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ |
45
range.js
@@ -0,1 +1,17 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.wordAtPosition = wordAtPosition; | ||
exports.trimRange = trimRange; | ||
var _atom = require('atom'); | ||
var _range; | ||
function _load_range() { | ||
return _range = require('nuclide-commons/range'); | ||
} | ||
/** | ||
@@ -8,14 +24,7 @@ * Copyright (c) 2015-present, Facebook, Inc. | ||
* | ||
* @flow | ||
* | ||
* @format | ||
*/ | ||
import {Range} from 'atom'; | ||
import {wordAtPositionFromBuffer} from 'nuclide-commons/range'; | ||
export function wordAtPosition( | ||
editor: atom$TextEditor, | ||
position: atom$PointObject, | ||
wordRegex_: ?RegExp, | ||
): ?{wordMatch: Array<string>, range: atom$Range} { | ||
function wordAtPosition(editor, position, wordRegex_) { | ||
let wordRegex = wordRegex_; | ||
@@ -26,3 +35,3 @@ if (!wordRegex) { | ||
const buffer = editor.getBuffer(); | ||
return wordAtPositionFromBuffer(buffer, position, wordRegex); | ||
return (0, (_range || _load_range()).wordAtPositionFromBuffer)(buffer, position, wordRegex); | ||
} | ||
@@ -41,18 +50,14 @@ | ||
*/ | ||
export function trimRange( | ||
editor: atom$TextEditor, | ||
rangeToTrim: atom$Range, | ||
stopRegex: RegExp = /\S/, | ||
): atom$Range { | ||
function trimRange(editor, rangeToTrim, stopRegex = /\S/) { | ||
const buffer = editor.getBuffer(); | ||
let {start, end} = rangeToTrim; | ||
buffer.scanInRange(stopRegex, rangeToTrim, ({range, stop}) => { | ||
let { start, end } = rangeToTrim; | ||
buffer.scanInRange(stopRegex, rangeToTrim, ({ range, stop }) => { | ||
start = range.start; | ||
stop(); | ||
}); | ||
buffer.backwardsScanInRange(stopRegex, rangeToTrim, ({range, stop}) => { | ||
buffer.backwardsScanInRange(stopRegex, rangeToTrim, ({ range, stop }) => { | ||
end = range.end; | ||
stop(); | ||
}); | ||
return new Range(start, end); | ||
} | ||
return new _atom.Range(start, end); | ||
} |
@@ -1,17 +0,1 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
export type TextEdit = { | ||
oldRange: atom$Range, | ||
newText: string, | ||
// If included, this will be used to verify that the edit still applies cleanly. | ||
oldText?: string, | ||
}; | ||
"use strict"; |
@@ -1,18 +0,14 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
'use strict'; | ||
import type {NuclideUri} from 'nuclide-commons/nuclideUri'; | ||
import type {TextEdit} from './text-edit-rpc-types'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.applyTextEdits = applyTextEdits; | ||
exports.applyTextEditsToBuffer = applyTextEditsToBuffer; | ||
import invariant from 'assert'; | ||
var _textEditor; | ||
import {existingEditorForUri} from './text-editor'; | ||
function _load_textEditor() { | ||
return _textEditor = require('./text-editor'); | ||
} | ||
@@ -31,15 +27,24 @@ /** | ||
*/ | ||
export function applyTextEdits( | ||
path: NuclideUri, | ||
...edits: Array<TextEdit> | ||
): boolean { | ||
const editor = existingEditorForUri(path); | ||
invariant(editor != null); | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ | ||
function applyTextEdits(path, ...edits) { | ||
const editor = (0, (_textEditor || _load_textEditor()).existingEditorForUri)(path); | ||
if (!(editor != null)) { | ||
throw new Error('Invariant violation: "editor != null"'); | ||
} | ||
return applyTextEditsToBuffer(editor.getBuffer(), edits); | ||
} | ||
export function applyTextEditsToBuffer( | ||
buffer: atom$TextBuffer, | ||
edits: Array<TextEdit>, | ||
): boolean { | ||
function applyTextEditsToBuffer(buffer, edits) { | ||
// Special-case whole-buffer changes to minimize disruption. | ||
@@ -71,3 +76,3 @@ if (edits.length === 1 && edits[0].oldRange.isEqual(buffer.getRange())) { | ||
function applyToBuffer(buffer: atom$TextBuffer, edit: TextEdit): boolean { | ||
function applyToBuffer(buffer, edit) { | ||
if (edit.oldRange.start.row === edit.oldRange.end.row) { | ||
@@ -90,2 +95,2 @@ // A little extra validation when the old range spans only one line. In particular, this helps | ||
return true; | ||
} | ||
} |
@@ -1,20 +0,36 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* @flow | ||
* @format | ||
*/ | ||
'use strict'; | ||
import type {NuclideUri} from 'nuclide-commons/nuclideUri'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.existingEditorForUri = existingEditorForUri; | ||
exports.existingEditorForBuffer = existingEditorForBuffer; | ||
exports.getViewOfEditor = getViewOfEditor; | ||
exports.getScrollTop = getScrollTop; | ||
exports.setScrollTop = setScrollTop; | ||
exports.setPositionAndScroll = setPositionAndScroll; | ||
exports.getCursorPositions = getCursorPositions; | ||
exports.observeEditorDestroy = observeEditorDestroy; | ||
exports.enforceReadOnly = enforceReadOnly; | ||
exports.enforceSoftWrap = enforceSoftWrap; | ||
exports.observeTextEditors = observeTextEditors; | ||
exports.isValidTextEditor = isValidTextEditor; | ||
exports.centerScrollToBufferLine = centerScrollToBufferLine; | ||
import invariant from 'assert'; | ||
import {Observable} from 'rxjs'; | ||
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); | ||
import {observableFromSubscribeFunction} from 'nuclide-commons/event'; | ||
import nuclideUri from 'nuclide-commons/nuclideUri'; | ||
var _event; | ||
function _load_event() { | ||
return _event = require('nuclide-commons/event'); | ||
} | ||
var _nuclideUri; | ||
function _load_nuclideUri() { | ||
return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri')); | ||
} | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
/** | ||
@@ -24,3 +40,3 @@ * Returns a text editor that has the given path open, or null if none exists. If there are multiple | ||
*/ | ||
export function existingEditorForUri(path: NuclideUri): ?atom$TextEditor { | ||
function existingEditorForUri(path) { | ||
// This isn't ideal but realistically iterating through even a few hundred editors shouldn't be a | ||
@@ -41,5 +57,14 @@ // real problem. And if you have more than a few hundred you probably have bigger problems. | ||
*/ | ||
export function existingEditorForBuffer( | ||
buffer: atom$TextBuffer, | ||
): ?atom$TextEditor { | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the license found in the LICENSE file in | ||
* the root directory of this source tree. | ||
* | ||
* | ||
* @format | ||
*/ | ||
function existingEditorForBuffer(buffer) { | ||
// This isn't ideal but realistically iterating through even a few hundred editors shouldn't be a | ||
@@ -56,13 +81,11 @@ // real problem. And if you have more than a few hundred you probably have bigger problems. | ||
export function getViewOfEditor( | ||
editor: atom$TextEditor, | ||
): atom$TextEditorElement { | ||
function getViewOfEditor(editor) { | ||
return atom.views.getView(editor); | ||
} | ||
export function getScrollTop(editor: atom$TextEditor): number { | ||
function getScrollTop(editor) { | ||
return getViewOfEditor(editor).getScrollTop(); | ||
} | ||
export function setScrollTop(editor: atom$TextEditor, scrollTop: number): void { | ||
function setScrollTop(editor, scrollTop) { | ||
getViewOfEditor(editor).setScrollTop(scrollTop); | ||
@@ -78,32 +101,21 @@ } | ||
*/ | ||
export function setPositionAndScroll( | ||
editor: atom$TextEditor, | ||
position: atom$Point, | ||
scrollTop: number, | ||
): void { | ||
editor.setCursorBufferPosition(position, {autoscroll: false}); | ||
function setPositionAndScroll(editor, position, scrollTop) { | ||
editor.setCursorBufferPosition(position, { autoscroll: false }); | ||
setScrollTop(editor, scrollTop); | ||
} | ||
export function getCursorPositions( | ||
editor: atom$TextEditor, | ||
): Observable<atom$Point> { | ||
function getCursorPositions(editor) { | ||
// This will behave strangely in the face of multiple cursors. Consider supporting multiple | ||
// cursors in the future. | ||
const cursor = editor.getCursors()[0]; | ||
invariant(cursor != null); | ||
return Observable.merge( | ||
Observable.of(cursor.getBufferPosition()), | ||
observableFromSubscribeFunction( | ||
cursor.onDidChangePosition.bind(cursor), | ||
).map(event => event.newBufferPosition), | ||
); | ||
if (!(cursor != null)) { | ||
throw new Error('Invariant violation: "cursor != null"'); | ||
} | ||
return _rxjsBundlesRxMinJs.Observable.merge(_rxjsBundlesRxMinJs.Observable.of(cursor.getBufferPosition()), (0, (_event || _load_event()).observableFromSubscribeFunction)(cursor.onDidChangePosition.bind(cursor)).map(event => event.newBufferPosition)); | ||
} | ||
export function observeEditorDestroy( | ||
editor: atom$TextEditor, | ||
): Observable<atom$TextEditor> { | ||
return observableFromSubscribeFunction(editor.onDidDestroy.bind(editor)) | ||
.map(event => editor) | ||
.take(1); | ||
function observeEditorDestroy(editor) { | ||
return (0, (_event || _load_event()).observableFromSubscribeFunction)(editor.onDidDestroy.bind(editor)).map(event => editor).take(1); | ||
} | ||
@@ -116,3 +128,3 @@ | ||
// TODO: https://github.com/atom/atom/issues/9237. | ||
export function enforceReadOnly(textEditor: atom$TextEditor): void { | ||
function enforceReadOnly(textEditor) { | ||
const noop = () => {}; | ||
@@ -135,7 +147,7 @@ | ||
function passReadOnlyException(functionName: string) { | ||
const buffer: any = textBuffer; | ||
function passReadOnlyException(functionName) { | ||
const buffer = textBuffer; | ||
const originalFunction = buffer[functionName]; | ||
buffer[functionName] = function() { | ||
buffer[functionName] = function () { | ||
textBuffer.applyChange = originalApplyChange; | ||
@@ -152,6 +164,3 @@ const result = originalFunction.apply(textBuffer, arguments); | ||
// after mounting an editor to the workspace - here, that's watched and reset to `false`. | ||
export function enforceSoftWrap( | ||
editor: atom$TextEditor, | ||
enforcedSoftWrap: boolean, | ||
): IDisposable { | ||
function enforceSoftWrap(editor, enforcedSoftWrap) { | ||
editor.setSoftWrapped(enforcedSoftWrap); | ||
@@ -174,5 +183,3 @@ return editor.onDidChangeSoftWrapped(softWrapped => { | ||
*/ | ||
export function observeTextEditors( | ||
callback: (editor: atom$TextEditor) => mixed, | ||
): IDisposable { | ||
function observeTextEditors(callback) { | ||
// The one place where atom.workspace.observeTextEditors needs to be used. | ||
@@ -190,8 +197,6 @@ // eslint-disable-next-line nuclide-internal/atom-apis | ||
*/ | ||
export function isValidTextEditor(item: mixed): boolean { | ||
function isValidTextEditor(item) { | ||
// eslint-disable-next-line nuclide-internal/atom-apis | ||
if (atom.workspace.isTextEditor(item)) { | ||
return !nuclideUri.isBrokenDeserializedUri( | ||
((item: any): atom$TextEditor).getPath(), | ||
); | ||
return !(_nuclideUri || _load_nuclideUri()).default.isBrokenDeserializedUri(item.getPath()); | ||
} | ||
@@ -201,18 +206,9 @@ return false; | ||
export function centerScrollToBufferLine( | ||
textEditorElement: atom$TextEditorElement, | ||
bufferLineNumber: number, | ||
): void { | ||
function centerScrollToBufferLine(textEditorElement, bufferLineNumber) { | ||
const textEditor = textEditorElement.getModel(); | ||
const pixelPositionTop = textEditorElement.pixelPositionForBufferPosition([ | ||
bufferLineNumber, | ||
0, | ||
]).top; | ||
const pixelPositionTop = textEditorElement.pixelPositionForBufferPosition([bufferLineNumber, 0]).top; | ||
// Manually calculate the scroll location, instead of using | ||
// `textEditor.scrollToBufferPosition([lineNumber, 0], {center: true})` | ||
// because that API to wouldn't center the line if it was in the visible screen range. | ||
const scrollTop = | ||
pixelPositionTop + | ||
textEditor.getLineHeightInPixels() / 2 - | ||
textEditorElement.clientHeight / 2; | ||
const scrollTop = pixelPositionTop + textEditor.getLineHeightInPixels() / 2 - textEditorElement.clientHeight / 2; | ||
textEditorElement.setScrollTop(Math.max(scrollTop, 1)); | ||
@@ -223,4 +219,4 @@ | ||
textEditor.setCursorBufferPosition([bufferLineNumber, 0], { | ||
autoscroll: false, | ||
autoscroll: false | ||
}); | ||
} | ||
} |
@@ -0,1 +1,34 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.__TEST__ = exports.TextEventDispatcher = undefined; | ||
exports.observeTextEditorEvents = observeTextEditorEvents; | ||
var _atom = require('atom'); | ||
var _rxjsBundlesRxMinJs = require('rxjs/bundles/Rx.min.js'); | ||
var _debounce; | ||
function _load_debounce() { | ||
return _debounce = _interopRequireDefault(require('nuclide-commons/debounce')); | ||
} | ||
var _event; | ||
function _load_event() { | ||
return _event = require('nuclide-commons/event'); | ||
} | ||
var _textEditor; | ||
function _load_textEditor() { | ||
return _textEditor = require('./text-editor'); | ||
} | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
// A reload changes the text in the buffer, so it should trigger a refresh. | ||
/** | ||
@@ -8,18 +41,6 @@ * Copyright (c) 2015-present, Facebook, Inc. | ||
* | ||
* @flow | ||
* | ||
* @format | ||
*/ | ||
import invariant from 'assert'; | ||
import {Disposable, CompositeDisposable} from 'atom'; | ||
import {Observable} from 'rxjs'; | ||
import debounce from 'nuclide-commons/debounce'; | ||
import {observableFromSubscribeFunction} from 'nuclide-commons/event'; | ||
import {observeTextEditors} from './text-editor'; | ||
type EventCallback = (editor: TextEditor) => mixed; | ||
type Event = 'did-reload' | 'did-change' | 'did-save' | 'did-open'; | ||
// A reload changes the text in the buffer, so it should trigger a refresh. | ||
const FILE_CHANGE_EVENTS = ['did-change', 'did-reload', 'did-open']; | ||
@@ -36,11 +57,5 @@ | ||
*/ | ||
class TextCallbackContainer<CallbackArg> { | ||
class TextCallbackContainer { | ||
// grammar -> event -> callback | ||
// invariant: no empty maps or sets (they should be removed instead) | ||
_callbacks: Map<string, Map<Event, Set<(arg: CallbackArg) => mixed>>>; | ||
// event -> callback | ||
// invariant: no keys mapping to empty sets (they should be removed instead) | ||
_allGrammarCallbacks: Map<Event, Set<(arg: CallbackArg) => mixed>>; | ||
constructor() { | ||
@@ -51,12 +66,10 @@ this._callbacks = new Map(); | ||
getCallbacks( | ||
grammar: string, | ||
event: Event, | ||
): Set<(arg: CallbackArg) => mixed> { | ||
// event -> callback | ||
// invariant: no keys mapping to empty sets (they should be removed instead) | ||
getCallbacks(grammar, event) { | ||
const eventMap = this._callbacks.get(grammar); | ||
const callbacksForGrammar = this._getCallbacksFromEventMap(eventMap, event); | ||
const callbacksForAll = this._getCallbacksFromEventMap( | ||
this._allGrammarCallbacks, | ||
event, | ||
); | ||
const callbacksForAll = this._getCallbacksFromEventMap(this._allGrammarCallbacks, event); | ||
const resultSet = new Set(); | ||
@@ -71,10 +84,7 @@ const add = callback => { | ||
isEmpty(): boolean { | ||
isEmpty() { | ||
return this._callbacks.size === 0 && this._allGrammarCallbacks.size === 0; | ||
} | ||
_getCallbacksFromEventMap( | ||
eventMap: ?Map<Event, Set<(arg: CallbackArg) => mixed>>, | ||
event: Event, | ||
): Set<(arg: CallbackArg) => mixed> { | ||
_getCallbacksFromEventMap(eventMap, event) { | ||
if (!eventMap) { | ||
@@ -90,7 +100,3 @@ return new Set(); | ||
addCallback( | ||
grammarScopes: Iterable<string> | 'all', | ||
events: Iterable<Event>, | ||
callback: (arg: CallbackArg) => mixed, | ||
): void { | ||
addCallback(grammarScopes, events, callback) { | ||
if (grammarScopes === 'all') { | ||
@@ -112,7 +118,3 @@ this._addToEventMap(this._allGrammarCallbacks, events, callback); | ||
// empty maps or sets in this._callbacks | ||
removeCallback( | ||
grammarScopes: Iterable<string> | 'all', | ||
events: Iterable<Event>, | ||
callback: (arg: CallbackArg) => mixed, | ||
): void { | ||
removeCallback(grammarScopes, events, callback) { | ||
if (grammarScopes === 'all') { | ||
@@ -123,3 +125,7 @@ this._removeFromEventMap(this._allGrammarCallbacks, events, callback); | ||
const eventMap = this._callbacks.get(grammarScope); | ||
invariant(eventMap); | ||
if (!eventMap) { | ||
throw new Error('Invariant violation: "eventMap"'); | ||
} | ||
this._removeFromEventMap(eventMap, events, callback); | ||
@@ -133,7 +139,3 @@ if (eventMap.size === 0) { | ||
_addToEventMap( | ||
eventMap: Map<Event, Set<(arg: CallbackArg) => mixed>>, | ||
events: Iterable<Event>, | ||
callback: (arg: CallbackArg) => mixed, | ||
): void { | ||
_addToEventMap(eventMap, events, callback) { | ||
for (const event of events) { | ||
@@ -149,10 +151,10 @@ let callbackSet = eventMap.get(event); | ||
_removeFromEventMap( | ||
eventMap: Map<Event, Set<(arg: CallbackArg) => mixed>>, | ||
events: Iterable<Event>, | ||
callback: (arg: CallbackArg) => mixed, | ||
): void { | ||
_removeFromEventMap(eventMap, events, callback) { | ||
for (const event of events) { | ||
const callbackSet = eventMap.get(event); | ||
invariant(callbackSet); | ||
if (!callbackSet) { | ||
throw new Error('Invariant violation: "callbackSet"'); | ||
} | ||
callbackSet.delete(callback); | ||
@@ -183,9 +185,4 @@ if (callbackSet.size === 0) { | ||
*/ | ||
export class TextEventDispatcher { | ||
_callbackContainer: TextCallbackContainer<TextEditor>; | ||
class TextEventDispatcher { | ||
_editorListenerDisposable: ?CompositeDisposable; | ||
_pendingEvents: WeakMap<atom$TextBuffer, Set<Event>>; | ||
constructor() { | ||
@@ -197,7 +194,3 @@ this._callbackContainer = new TextCallbackContainer(); | ||
_onEvents( | ||
grammarScopes: Iterable<string> | 'all', | ||
events: Iterable<Event>, | ||
callback: EventCallback, | ||
) { | ||
_onEvents(grammarScopes, events, callback) { | ||
if (this._callbackContainer.isEmpty()) { | ||
@@ -208,14 +201,6 @@ this._registerEditorListeners(); | ||
// (particularly on startup). | ||
const debouncedCallback = debounce(callback, 50, true); | ||
this._callbackContainer.addCallback( | ||
grammarScopes, | ||
events, | ||
debouncedCallback, | ||
); | ||
const disposables = new Disposable(() => { | ||
this._callbackContainer.removeCallback( | ||
grammarScopes, | ||
events, | ||
debouncedCallback, | ||
); | ||
const debouncedCallback = (0, (_debounce || _load_debounce()).default)(callback, 50, true); | ||
this._callbackContainer.addCallback(grammarScopes, events, debouncedCallback); | ||
const disposables = new _atom.Disposable(() => { | ||
this._callbackContainer.removeCallback(grammarScopes, events, debouncedCallback); | ||
if (this._callbackContainer.isEmpty()) { | ||
@@ -228,26 +213,20 @@ this._deregisterEditorListeners(); | ||
onFileChange( | ||
grammarScopes: Iterable<string>, | ||
callback: EventCallback, | ||
): IDisposable { | ||
onFileChange(grammarScopes, callback) { | ||
return this._onEvents(grammarScopes, FILE_CHANGE_EVENTS, callback); | ||
} | ||
onAnyFileChange(callback: EventCallback): IDisposable { | ||
onAnyFileChange(callback) { | ||
return this._onEvents('all', FILE_CHANGE_EVENTS, callback); | ||
} | ||
onFileSave( | ||
grammarScopes: Iterable<string>, | ||
callback: EventCallback, | ||
): IDisposable { | ||
onFileSave(grammarScopes, callback) { | ||
return this._onEvents(grammarScopes, FILE_SAVE_EVENTS, callback); | ||
} | ||
onAnyFileSave(callback: EventCallback): IDisposable { | ||
onAnyFileSave(callback) { | ||
return this._onEvents('all', FILE_SAVE_EVENTS, callback); | ||
} | ||
_registerEditorListeners(): void { | ||
_registerEditorListeners() { | ||
if (!this._editorListenerDisposable) { | ||
this._editorListenerDisposable = new CompositeDisposable(); | ||
this._editorListenerDisposable = new _atom.CompositeDisposable(); | ||
} | ||
@@ -257,43 +236,31 @@ | ||
// pending events for the newly-focused TextEditor. | ||
this._getEditorListenerDisposable().add( | ||
atom.workspace.onDidChangeActivePaneItem(() => { | ||
const currentEditor = atom.workspace.getActiveTextEditor(); | ||
if (currentEditor) { | ||
const pendingEvents = this._pendingEvents.get( | ||
currentEditor.getBuffer(), | ||
); | ||
if (pendingEvents) { | ||
for (const event of pendingEvents) { | ||
this._dispatchEvents(currentEditor, event); | ||
} | ||
this._pendingEvents.delete(currentEditor.getBuffer()); | ||
this._getEditorListenerDisposable().add(atom.workspace.onDidChangeActivePaneItem(() => { | ||
const currentEditor = atom.workspace.getActiveTextEditor(); | ||
if (currentEditor) { | ||
const pendingEvents = this._pendingEvents.get(currentEditor.getBuffer()); | ||
if (pendingEvents) { | ||
for (const event of pendingEvents) { | ||
this._dispatchEvents(currentEditor, event); | ||
} | ||
this._pendingEvents.delete(currentEditor.getBuffer()); | ||
} | ||
}), | ||
); | ||
} | ||
})); | ||
this._getEditorListenerDisposable().add( | ||
observeTextEditors(editor => { | ||
const buffer = editor.getBuffer(); | ||
const makeDispatch = (event: Event) => { | ||
return () => { | ||
this._dispatchEvents(editor, event); | ||
}; | ||
this._getEditorListenerDisposable().add((0, (_textEditor || _load_textEditor()).observeTextEditors)(editor => { | ||
const buffer = editor.getBuffer(); | ||
const makeDispatch = event => { | ||
return () => { | ||
this._dispatchEvents(editor, event); | ||
}; | ||
this._getEditorListenerDisposable().add( | ||
buffer.onDidStopChanging(makeDispatch('did-change')), | ||
); | ||
this._getEditorListenerDisposable().add( | ||
buffer.onDidSave(makeDispatch('did-save')), | ||
); | ||
this._getEditorListenerDisposable().add( | ||
buffer.onDidReload(makeDispatch('did-reload')), | ||
); | ||
// During reload, many text editors are opened simultaneously. | ||
// Due to the debounce on the event callback, this means that many editors never receive | ||
// a 'did-open' event. To work around this, defer editor open events so that simultaneous | ||
// open events are properly registered as pending. | ||
setImmediate(() => this._dispatchEvents(editor, 'did-open')); | ||
}), | ||
); | ||
}; | ||
this._getEditorListenerDisposable().add(buffer.onDidStopChanging(makeDispatch('did-change'))); | ||
this._getEditorListenerDisposable().add(buffer.onDidSave(makeDispatch('did-save'))); | ||
this._getEditorListenerDisposable().add(buffer.onDidReload(makeDispatch('did-reload'))); | ||
// During reload, many text editors are opened simultaneously. | ||
// Due to the debounce on the event callback, this means that many editors never receive | ||
// a 'did-open' event. To work around this, defer editor open events so that simultaneous | ||
// open events are properly registered as pending. | ||
setImmediate(() => this._dispatchEvents(editor, 'did-open')); | ||
})); | ||
} | ||
@@ -308,9 +275,6 @@ | ||
_dispatchEvents(editor: TextEditor, event: Event): void { | ||
_dispatchEvents(editor, event) { | ||
const currentEditor = atom.workspace.getActiveTextEditor(); | ||
if (currentEditor && editor === currentEditor) { | ||
const callbacks = this._callbackContainer.getCallbacks( | ||
editor.getGrammar().scopeName, | ||
event, | ||
); | ||
const callbacks = this._callbackContainer.getCallbacks(editor.getGrammar().scopeName, event); | ||
for (const callback of callbacks) { | ||
@@ -322,6 +286,3 @@ callback(editor); | ||
// multiple panes have the same file open. | ||
} else if ( | ||
!currentEditor || | ||
editor.getBuffer() !== currentEditor.getBuffer() | ||
) { | ||
} else if (!currentEditor || editor.getBuffer() !== currentEditor.getBuffer()) { | ||
// Trigger this event next time we switch to an editor with this buffer. | ||
@@ -338,5 +299,9 @@ const buffer = editor.getBuffer(); | ||
_getEditorListenerDisposable(): CompositeDisposable { | ||
_getEditorListenerDisposable() { | ||
const disposable = this._editorListenerDisposable; | ||
invariant(disposable, 'TextEventDispatcher disposable is not initialized'); | ||
if (!disposable) { | ||
throw new Error('TextEventDispatcher disposable is not initialized'); | ||
} | ||
return disposable; | ||
@@ -346,27 +311,17 @@ } | ||
export function observeTextEditorEvents( | ||
grammarScopes: Iterable<string> | 'all', | ||
events: 'changes' | 'saves', | ||
): Observable<atom$TextEditor> { | ||
return Observable.defer(() => { | ||
exports.TextEventDispatcher = TextEventDispatcher; | ||
function observeTextEditorEvents(grammarScopes, events) { | ||
return _rxjsBundlesRxMinJs.Observable.defer(() => { | ||
const dispatcher = new TextEventDispatcher(); | ||
if (events === 'changes') { | ||
if (grammarScopes === 'all') { | ||
return observableFromSubscribeFunction(cb => | ||
dispatcher.onAnyFileChange(cb), | ||
); | ||
return (0, (_event || _load_event()).observableFromSubscribeFunction)(cb => dispatcher.onAnyFileChange(cb)); | ||
} else { | ||
return observableFromSubscribeFunction(cb => | ||
dispatcher.onFileChange(grammarScopes, cb), | ||
); | ||
return (0, (_event || _load_event()).observableFromSubscribeFunction)(cb => dispatcher.onFileChange(grammarScopes, cb)); | ||
} | ||
} else { | ||
if (grammarScopes === 'all') { | ||
return observableFromSubscribeFunction(cb => | ||
dispatcher.onAnyFileSave(cb), | ||
); | ||
return (0, (_event || _load_event()).observableFromSubscribeFunction)(cb => dispatcher.onAnyFileSave(cb)); | ||
} else { | ||
return observableFromSubscribeFunction(cb => | ||
dispatcher.onFileSave(grammarScopes, cb), | ||
); | ||
return (0, (_event || _load_event()).observableFromSubscribeFunction)(cb => dispatcher.onFileSave(grammarScopes, cb)); | ||
} | ||
@@ -377,4 +332,4 @@ } | ||
export const __TEST__ = { | ||
TextCallbackContainer, | ||
}; | ||
const __TEST__ = exports.__TEST__ = { | ||
TextCallbackContainer | ||
}; |
@@ -0,2 +1,32 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.getDocksWorkspaceViewsService = getDocksWorkspaceViewsService; | ||
exports.consumeWorkspaceViewsCompat = consumeWorkspaceViewsCompat; | ||
var _atom = require('atom'); | ||
var _semver; | ||
function _load_semver() { | ||
return _semver = _interopRequireDefault(require('semver')); | ||
} | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
/** | ||
* The object used as items in locations. This is based on the supported interface for items in Atom | ||
* panes. That way, we maintain compatibility with Atom (upstream?) and can put them in panes as-is. | ||
* | ||
* The truth is that these models can have any methods they want. Packages define ad-hoc protocols | ||
* and check to see if the item implements them. For example, atom-tabs will call `getIconName()` if | ||
* it exists. We have some of our own optional methods which, for clarity's sake, are defined here, | ||
* even though they're only used by some of our location packages. | ||
* | ||
* IMPORTANT: All properties and methods must be optional so that we maintain compatibility with | ||
* non-nuclide items. | ||
*/ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
@@ -8,3 +38,3 @@ * All rights reserved. | ||
* | ||
* @flow | ||
* | ||
* @format | ||
@@ -20,69 +50,7 @@ */ | ||
import {Disposable} from 'atom'; | ||
import semver from 'semver'; | ||
/** | ||
* The object used as items in locations. This is based on the supported interface for items in Atom | ||
* panes. That way, we maintain compatibility with Atom (upstream?) and can put them in panes as-is. | ||
* | ||
* The truth is that these models can have any methods they want. Packages define ad-hoc protocols | ||
* and check to see if the item implements them. For example, atom-tabs will call `getIconName()` if | ||
* it exists. We have some of our own optional methods which, for clarity's sake, are defined here, | ||
* even though they're only used by some of our location packages. | ||
* | ||
* IMPORTANT: All properties and methods must be optional so that we maintain compatibility with | ||
* non-nuclide items. | ||
*/ | ||
export type Viewable = atom$PaneItem & { | ||
// Used by PanelLocation to get an initial size for the panel. | ||
+getPreferredHeight?: () => number, | ||
+getPreferredWidth?: () => number, | ||
+didChangeVisibility?: (visible: boolean) => void, | ||
+getDefaultLocation?: () => string, | ||
}; | ||
export type Opener = (uri: string) => ?Viewable; | ||
export type OpenOptions = { | ||
activateItem?: boolean, | ||
activateLocation?: boolean, | ||
searchAllPanes?: boolean, | ||
}; | ||
export type Location = { | ||
activate(): void, | ||
activateItem(item: Object): void, | ||
addItem(item: Object): void, | ||
destroy(): void, | ||
destroyItem(item: Object): void, | ||
getItems(): Array<Viewable>, | ||
hideItem(item: Viewable): void, | ||
itemIsVisible(item: Viewable): boolean, | ||
serialize(): ?Object, | ||
onDidAddItem(cb: (item: Viewable) => void): IDisposable, | ||
}; | ||
export type LocationFactory = { | ||
id: string, | ||
create(serializedState: ?Object): Location, | ||
}; | ||
export type ToggleOptions = { | ||
visible?: ?boolean, | ||
}; | ||
export type WorkspaceViewsService = { | ||
addOpener(opener: Opener): IDisposable, | ||
destroyWhere(predicate: (item: Viewable) => boolean): void, | ||
open(uri: string, options?: OpenOptions): void, | ||
registerLocation(factory: LocationFactory): IDisposable, | ||
toggle(uri: string, options?: ToggleOptions): void, | ||
}; | ||
export function getDocksWorkspaceViewsService() { | ||
function getDocksWorkspaceViewsService() { | ||
return { | ||
registerLocation: () => new Disposable(() => {}), | ||
registerLocation: () => new _atom.Disposable(() => {}), | ||
addOpener: opener => atom.workspace.addOpener(opener), | ||
destroyWhere(predicate: (item: Viewable) => boolean) { | ||
destroyWhere(predicate) { | ||
atom.workspace.getPanes().forEach(pane => { | ||
@@ -96,18 +64,14 @@ pane.getItems().forEach(item => { | ||
}, | ||
open(uri: string, options?: Object): void { | ||
open(uri, options) { | ||
// eslint-disable-next-line nuclide-internal/atom-apis | ||
atom.workspace.open(uri, options); | ||
}, | ||
toggle(uri: string, options?: ?ToggleOptions): void { | ||
toggle(uri, options) { | ||
const visible = options && options.visible; | ||
if (visible === true) { | ||
// eslint-disable-next-line nuclide-internal/atom-apis | ||
atom.workspace.open(uri, {searchAllPanes: true}); | ||
atom.workspace.open(uri, { searchAllPanes: true }); | ||
} else if (visible === false) { | ||
// TODO: Add `atom.workspace.hide()` and use that instead. | ||
const hasItem = atom.workspace | ||
.getPaneItems() | ||
.some( | ||
item => typeof item.getURI === 'function' && item.getURI() === uri, | ||
); | ||
const hasItem = atom.workspace.getPaneItems().some(item => typeof item.getURI === 'function' && item.getURI() === uri); | ||
if (hasItem) { | ||
@@ -123,18 +87,12 @@ // TODO(matthewwithanm): Add this to the Flow defs once docks land | ||
} | ||
}, | ||
} | ||
}; | ||
} | ||
export function consumeWorkspaceViewsCompat( | ||
callback: (service: WorkspaceViewsService) => IDisposable, | ||
): IDisposable { | ||
if (semver.gte(atom.getVersion(), '1.17.0')) { | ||
function consumeWorkspaceViewsCompat(callback) { | ||
if ((_semver || _load_semver()).default.gte(atom.getVersion(), '1.17.0')) { | ||
callback(getDocksWorkspaceViewsService()); | ||
return new Disposable(); | ||
return new _atom.Disposable(); | ||
} | ||
return atom.packages.serviceHub.consume( | ||
'nuclide.workspace-views', | ||
'0.0.0', | ||
callback, | ||
); | ||
} | ||
return atom.packages.serviceHub.consume('nuclide.workspace-views', '0.0.0', callback); | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 3 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 3 instances in 1 package
152875
39
1881
+ Addednuclide-commons@0.1.4(transitive)
- Removednuclide-commons@0.1.3(transitive)
Updatednuclide-commons@0.1.4