diff --git a/packages/code-studio/src/console/command-history/CommandHistory.jsx b/packages/code-studio/src/console/command-history/CommandHistory.jsx index a9c02eae70..2c97971a57 100644 --- a/packages/code-studio/src/console/command-history/CommandHistory.jsx +++ b/packages/code-studio/src/console/command-history/CommandHistory.jsx @@ -156,30 +156,23 @@ class CommandHistory extends Component { * If they're not within the current viewport, will fetch them from the table * @returns {Promise} Array of selected commands */ - getSelectedCommands() { + async getSelectedCommands() { const { items, offset, selectedRanges } = this.state; const ranges = selectedRanges.slice().sort((a, b) => a[0] - b[0]); - return this.pending.add( - new Promise((resolve, reject) => { - if (ranges.length === 0) { - resolve([]); - } else if ( - ranges[0][0] >= offset && - ranges[ranges.length - 1][1] < offset + items.length - ) { - // All ranges are in the current viewport, just copy the data we've already got - resolve( - CommandHistory.getCommandsFromViewport(items, offset, ranges) - ); - } else { - // TODO: core#376 Allow copying outside of current viewport - reject( - new Error('Unable to copy commands outside of current viewport') - ); - } - }) - ); + if (ranges.length === 0) { + return []; + } + if ( + ranges[0][0] >= offset && + ranges[ranges.length - 1][1] < offset + items.length + ) { + // All ranges are in the current viewport, just copy the data we've already got + return CommandHistory.getCommandsFromViewport(items, offset, ranges); + } + const { table } = this.props; + const snapshot = await this.pending.add(table.getSnapshot(ranges)); + return [...snapshot.added].map(index => snapshot.get(index)?.name ?? ''); } /** @@ -353,7 +346,10 @@ CommandHistory.propTypes = { language: PropTypes.string.isRequired, sendToConsole: PropTypes.func.isRequired, sendToNotebook: PropTypes.func.isRequired, - table: PropTypes.shape({ size: PropTypes.number }).isRequired, + table: PropTypes.shape({ + size: PropTypes.number, + getSnapshot: PropTypes.func.isRequired, + }).isRequired, }; export default CommandHistory; diff --git a/packages/code-studio/src/console/command-history/PouchStorageTable.ts b/packages/code-studio/src/console/command-history/PouchStorageTable.ts index 1d5a16ff53..43b130612c 100644 --- a/packages/code-studio/src/console/command-history/PouchStorageTable.ts +++ b/packages/code-studio/src/console/command-history/PouchStorageTable.ts @@ -11,6 +11,7 @@ import { StorageTableListener, StorageItemSuccessErrorListener, StorageItemSuccessListener, + StorageSnapshot, } from './StorageTable'; const log = Log.module('PouchStorageTable'); @@ -252,6 +253,58 @@ export class PouchStorageTable return item; } + + async getSnapshot( + sortedRanges: [number, number][] + ): Promise> { + if (!this.currentViewport) { + throw new Error('Viewport not set'); + } + const { currentViewport: viewport } = this; + + const itemMap: Map = new Map(); + const indexes: number[] = []; + let lastIndex = -1; + + await Promise.all( + sortedRanges.map(async ([from, to]) => { + const limit = to - from + 1; + return this.db + .find({ + selector: selectorWithSearch(viewport.search), + skip: from, + limit, + sort: this.sort, + fields: ['id', 'name'], + }) + .then(findSnapshotResult => { + for (let i = 0; i < limit; i += 1) { + const index = from + i; + indexes.push(index); + itemMap.set(index, findSnapshotResult.docs[i]); + } + }); + }) + ); + + function iterator() { + return { + hasNext: () => lastIndex + 1 < indexes.length, + next: () => { + lastIndex += 1; + return { + value: indexes[lastIndex], + done: lastIndex >= indexes.length, + }; + }, + }; + } + + return { + added: { [Symbol.iterator]: iterator }, + get: (index: number) => itemMap.get(index), + }; + } } export default PouchStorageTable; diff --git a/packages/code-studio/src/console/command-history/StorageTable.ts b/packages/code-studio/src/console/command-history/StorageTable.ts index 09818f2739..2978e419fb 100644 --- a/packages/code-studio/src/console/command-history/StorageTable.ts +++ b/packages/code-studio/src/console/command-history/StorageTable.ts @@ -36,6 +36,11 @@ export type StorageTableListener = () => void; export type StorageListenerRemover = () => void; +export type StorageSnapshot = { + added: Iterable; + get(index: number): T | undefined; +}; + /** * A table for getting a list of items from storage, with id/name pairs * Can search the table with a case insensitive search @@ -57,6 +62,7 @@ export interface StorageTable { id: string, listener: StorageItemListener ): StorageListenerRemover; + getSnapshot(sortedRanges: [number, number][]): Promise>; close(): void; }