const createSuspension = () => { const tasks = new Subject(); const finished = new Subject(); let isExecuting = false; let lastTask = null; tasks.switchMap(async task => { if (isExecuting){ lastTask = task; return; } isExecuting = true; let result = await task(); finished.next(result); while (lastTask != null){ const toRun = lastTask; lastTask = null; result = await toRun(); finished.next(result); } }) .publish() .connect(); return { insertTask: task => tasks.next(task), finishedTasks: finished } }
componentDidMount(): void { this._resizeSubscription = this._resizeEvents .switchMap(() => nextAnimationFrame) .subscribe(() => { this._updateContainerSize(); }); }
controller: function (cloudMetricsReader) { this.viewState = { loadingDimensions: false }; this.fetchDimensionOptions = () => { this.viewState.loadingDimensions = true; let filters = { namespace: this.alarm.namespace }; return Observable .fromPromise( cloudMetricsReader.listMetrics('aws', this.serverGroup.account, this.serverGroup.region, filters) ); }; let dimensionSubject = new Subject(); dimensionSubject .switchMap(this.fetchDimensionOptions) .subscribe((results) => { this.viewState.loadingDimensions = false; results = results || []; results.forEach(r => r.dimensions = r.dimensions || []); this.dimensionOptions = _.uniq(_.flatten(results.map(r => r.dimensions.map(d => d.name)))).sort(); }); this.updateDimensionOptions = () => { dimensionSubject.next(); }; this.removeDimension = (index) => { this.alarm.dimensions.splice(index, 1); this.updateAvailableMetrics(); }; this.$onInit = () => { this.updateDimensionOptions(); this.namespaceUpdated.subscribe(() => { this.updateDimensionOptions(); }); }; }
componentDidMount(): void { const el = nullthrows(this._rootNode); this._disposables = new UniversalDisposable( // Update the column widths when the table is resized. new ResizeObservable(el) .startWith((null: any)) .map(() => el.offsetWidth) .filter(tableWidth => tableWidth > 0) .subscribe(tableWidth => { this.setState({tableWidth}); }), this._resizeStarts .switchMap(({event: startEvent, resizerLocation}) => { const startX = startEvent.pageX; return Observable.fromEvent(document, 'mousemove') .takeUntil(Observable.fromEvent(document, 'mouseup')) .map(event => ({ deltaPx: event.pageX - startX, resizerLocation, })) .concat(Observable.of(null)); }) .subscribe(resizeOffset => { if (resizeOffset == null) { // Finalize the resize by updating the user's preferred column widths to account for // their action. Note that these preferences are only updated when columns are resized // (NOT when the table is). This is important so that, if the user resizes the table // such that a column is at its minimum width and then resizes the table back to its // orignal size, their original column widths are restored. const preferredColumnWidths = _calculatePreferredColumnWidths({ currentWidths: this._calculateColumnWidths(), tableWidth: this.state.tableWidth, minWidths: getMinWidths(this.props.columns), }); // Update the preferred distributions and end the resize. this.setState({preferredColumnWidths, resizeOffset: null}); } else { this.setState({resizeOffset}); } }), atom.commands.add(el, { 'core:move-up': event => { this.setState({usingKeyboard: true}); this._moveSelection(-1, event); }, 'core:move-down': event => { this.setState({usingKeyboard: true}); this._moveSelection(1, event); }, 'core:confirm': event => { this.setState({usingKeyboard: true}); const {rows, selectedIndex, onConfirm} = this.props; if (onConfirm == null || selectedIndex == null) { return; } const selectedRow = rows[selectedIndex]; const selectedItem = selectedRow && selectedRow.data; if (selectedItem != null) { onConfirm(selectedItem, selectedIndex); } }, }), () => { if (this._mouseMoveDisposable != null) { this._mouseMoveDisposable.dispose(); } }, ); }
return function() { var deferred; let searchConfig = { projects: { displayName: 'Projects', displayFormatter: function(entry) { let applications = entry.config && entry.config.applications ? ' (' + entry.config.applications.join(', ') + ')' : ''; let project = entry.name || entry.project; return $q.when(project + applications); }, order: 0, }, applications: { displayName: 'Applications', displayFormatter: simpleField('application'), order: 1, }, clusters: { displayName: 'Clusters', displayFormatter: simpleField('cluster'), order: 2, hideIfEmpty: true, }, serverGroups: { displayName: 'Server Groups', displayFormatter: function(entry) { return $q.when(entry.serverGroup + ' (' + entry.region + ')'); }, order: 3, hideIfEmpty: true, }, instances: { displayName: 'Instances', displayFormatter: function(entry) { let serverGroup = entry.serverGroup || 'standalone instance'; return $q.when(entry.instanceId + ' (' + serverGroup + ' - ' + entry.region + ')'); }, order: 4, hideIfEmpty: true, }, loadBalancers: { displayName: 'Load Balancers', displayFormatter: function(entry, fromRoute) { let name = fromRoute ? entry.name : entry.loadBalancer; return $q.when(name + ' (' + entry.region + ')'); }, order: 5, hideIfEmpty: true, }, securityGroups: { displayName: 'Security Groups', displayFormatter: function(entry) { return $q.when(entry.name + ' (' + entry.region + ')'); }, order: 6, hideIfEmpty: true, } }; function simpleField(field) { return function(entry) { return $q.when(entry[field]); }; } var querySubject = new Subject(); let initializeCategories = () => { let categories = {}; Object.keys(searchConfig).forEach((searchType) => { categories[searchType] = []; }); return categories; }; let formatResult = (category, entry, fromRoute) => { var config = searchConfig[category], formatter = config.displayFormatter; if (serviceDelegate.hasDelegate(entry.provider, 'search.resultFormatter')) { let providerFormatter = serviceDelegate.getDelegate(entry.provider, 'search.resultFormatter'); if (providerFormatter[category]) { formatter = providerFormatter[category]; } } return formatter(entry, fromRoute); }; querySubject .switchMap(function(query) { if (!query || !angular.isDefined(query) || query.length < 1) { return Observable.of(searchService.getFallbackResults()); } return Observable.fromPromise(searchService.search({ q: query, type: Object.keys(searchConfig), })); }) .subscribe(function(result) { var tmp = result.results.reduce(function(categories, entry) { formatResult(entry.type, entry).then((name) => entry.displayName = name); entry.href = urlBuilderService.buildFromMetadata(entry); categories[entry.type].push(entry); return categories; }, initializeCategories()); deferred.resolve(Object.keys(tmp).map(function(cat) { let config = searchConfig[cat]; return { category: config.displayName, order: config.order, hideIfEmpty: config.hideIfEmpty, results: tmp[cat], }; })); }); return { query: function(query) { deferred = $q.defer(); querySubject.next(query); return deferred.promise; }, formatRouteResult: function(type, params) { return formatResult(type, params, true); }, }; };
/** * Either retrieves the monitor object for the currently active text editor * from the cache, or creates (and caches) the new one * * The monitor object hides the complexity of maintaining the last selection * explicitly made by user and handles the expand/collapse requests * * The monitor objects are held in the state only as long as an editor's * selection is modified by the syntactic selection feature. I.e. should a * user change the selection manually, the editor is removed from the cache * and any in-flight collapse/expand requests are abandoned (ignored). */ _getTextSelectionMonitor(): ?TextEditorSelectionMonitor { const editor = atom.workspace.getActiveTextEditor(); if (editor == null) { return null; } const cachedMonitor = this._textEditorMonitors.get(editor); if (cachedMonitor != null) { return cachedMonitor; } // The collapse-expand works with a single selection only, although // this could be extended to handle multiple selections as well const selectedRanges = editor.getSelectedBufferRanges(); if (selectedRanges.length > 1) { return null; } // The anchor cursor position. // Applying a selection automatically moves the selection to either back // of the front of the selected region, so we're storing the original // location of the cursor. // This information later helps the language service to zoom in (collapse) // onto the proper sub-range of current selection when multiple children // exist. // For example, consider the line below: // foo(arg1, bar('blah'), 100000) // If the expand will be iniated when the cursor is on 'blah', this position // will be marked as anchor. Let's assume we've expanded the selection // to include the entire line, entire call to foo(). // It means that currently the cursor is at the end, after the ")" // When we issue the collapse call, having the anchor stored lets us deduct // that the child which is the second argument "bar('bla')" now needs to // be selected. const selectionAnchor = selectedRanges[0].end; // We will modify the selection in the current document, but we also are // subscribed to selection changes. We need to differentiate between // the changes made by our code from those made by the user. // Prior to making a change we set this variable to later on filter out // the notification with matching range let expectedSelection = selectedRanges[0]; // Sometimes Atom may reinterpret the range that is provided by the language // service. In this case we have one more opportunity to detecting that // the selection change is done by us. That is if it is done synchronously // it is definitely ours. Unfortunately this relies on an implementation // details, and thus is not future-proof. Still, better then terminating // the watch too soon let changingNow = false; // There are multiple events that cover selection change. const selectionChangeSignals = Observable.merge( observableFromSubscribeFunction(editor.observeSelections.bind(editor)), observableFromSubscribeFunction( editor.onDidChangeSelectionRange.bind(editor), ), ); // We want to stop listening (managing) an editor instance when it is // either closed or if its selection was manually changed by the user const stopMonitorSignal = Observable.merge( observableFromSubscribeFunction(editor.onDidDestroy.bind(editor)), selectionChangeSignals.filter(() => { if (changingNow) { return false; } const currentSelectedRanges = editor.getSelectedBufferRanges(); if ( currentSelectedRanges.length > 1 || (expectedSelection != null && !currentSelectedRanges[0].isEqual(expectedSelection)) ) { // Stop monitoring return true; } return false; }), ); // Helps take care of racing requests const runningRangeRequests: Subject<Promise<?atom$Range>> = new Subject(); const monitor = { editor, doExpand: () => runningRangeRequests.next(this._expandRange(editor)), doCollapse: () => runningRangeRequests.next(this._collapseRange(editor, selectionAnchor)), }; this._textEditorMonitors.set(editor, monitor); const subscription = runningRangeRequests .switchMap(p => p) .takeUntil(stopMonitorSignal) .finally(() => { // Stop signal received - remove the monitor this._textEditorMonitors.delete(editor); // And do not leak the subscription object this._disposables.remove(subscription); }) .subscribe( newExpected => { expectedSelection = newExpected; if (newExpected != null) { changingNow = true; editor.setSelectedBufferRange(newExpected); changingNow = false; } }, err => { logger.error('Unexpected error in sytnactic selection handling', err); }, ); this._disposables.add(subscription); return monitor; }