diff --git a/src/commands/closeUnchangedFiles.ts b/src/commands/closeUnchangedFiles.ts index ba03f2d..946b36b 100644 --- a/src/commands/closeUnchangedFiles.ts +++ b/src/commands/closeUnchangedFiles.ts @@ -5,14 +5,16 @@ import { BuiltInCommands, GlyphChars } from '../constants'; import { Container } from '../container'; import { Logger } from '../logger'; import { Messages } from '../messages'; -import { ActiveEditorTracker } from '../trackers/activeEditorTracker'; -import { ActiveEditorCommand, Commands, getCommandUri, getRepoPathOrActiveOrPrompt } from './common'; +import { Functions } from '../system'; +import { ActiveEditorCommand, Commands, getCommandUri, getRepoPathOrPrompt } from './common'; export interface CloseUnchangedFilesCommandArgs { uris?: Uri[]; } export class CloseUnchangedFilesCommand extends ActiveEditorCommand { + private _onEditorChangedFn: ((editor: TextEditor | undefined) => void) | undefined; + constructor() { super(Commands.CloseUnchangedFiles); } @@ -24,10 +26,9 @@ export class CloseUnchangedFilesCommand extends ActiveEditorCommand { if (args.uris === undefined) { args = { ...args }; - const repoPath = await getRepoPathOrActiveOrPrompt( - uri, - editor, - `Close unchanged files in which repository${GlyphChars.Ellipsis}` + const repoPath = await getRepoPathOrPrompt( + undefined, + `Close all files except those changed in which repository${GlyphChars.Ellipsis}` ); if (!repoPath) return undefined; @@ -39,55 +40,120 @@ export class CloseUnchangedFilesCommand extends ActiveEditorCommand { if (args.uris.length === 0) return commands.executeCommand(BuiltInCommands.CloseAllEditors); - const editorTracker = new ActiveEditorTracker(); + const disposable = window.onDidChangeActiveTextEditor( + Functions.debounce( + (e: TextEditor | undefined) => this._onEditorChangedFn && this._onEditorChangedFn(e), + 50 + ) + ); - let count = 0; - let previous = undefined; editor = window.activeTextEditor; - while (true) { - if (editor != null) { - if (TextEditorComparer.equals(previous, editor, { useId: true, usePosition: true })) { - break; - } - if ( - editor.document !== undefined && - (editor.document.isDirty || - args.uris.some(uri => UriComparer.equals(uri, editor!.document && editor!.document.uri))) - ) { - const lastPrevious = previous; - previous = editor; - editor = await editorTracker.awaitNext(500); + let count = 0; + let loopCount = 0; + const editors: TextEditor[] = []; - if (TextEditorComparer.equals(lastPrevious, editor, { useId: true, usePosition: true })) { + // Find out how many editors there are + while (true) { + if (editor != null) { + let found = false; + for (const e of editors) { + if (TextEditorComparer.equals(e, editor, { useId: true, usePosition: true })) { + found = true; break; } - continue; } - } + if (found) break; - previous = editor; - editor = await editorTracker.awaitClose(500); - - if (previous === undefined && editor == null) { + // Start counting at the first real editor count++; - // This is such a shitty hack, but I can't figure out any other reliable way to know that we've cycled through all the editors :( - if (count >= 4) { - break; - } + editors.push(editor); } else { - count = 0; + if (count !== 0) { + count++; + } } + + editor = await this.nextEditor(); + + loopCount++; + // Break out if we've looped 4 times and haven't found any editors + if (loopCount >= 4 && editors.length === 0) break; } - editorTracker.dispose(); + if (editors.length) { + editor = window.activeTextEditor; + + for (let i = 0; i <= count; i++) { + if ( + editor == null || + (editor.document !== undefined && + (editor.document.isDirty || + args.uris.some(uri => + UriComparer.equals(uri, editor!.document && editor!.document.uri) + ))) + ) { + editor = await this.nextEditor(); + } + else { + editor = await this.closeEditor(); + } + } + } + + disposable.dispose(); return undefined; } catch (ex) { Logger.error(ex, 'CloseUnchangedFilesCommand'); - return Messages.showGenericErrorMessage('Unable to close unchanged files'); + return Messages.showGenericErrorMessage('Unable to close all unchanged files'); } } + + private async closeEditor(timeout: number = 500): Promise { + const editor = window.activeTextEditor; + + void (await commands.executeCommand(BuiltInCommands.CloseActiveEditor)); + + if (editor !== window.activeTextEditor) { + return window.activeTextEditor; + } + + return this.waitForEditorChange(timeout); + } + + private async nextEditor(timeout: number = 500): Promise { + const editor = window.activeTextEditor; + + void (await commands.executeCommand(BuiltInCommands.NextEditor)); + + if (editor !== window.activeTextEditor) { + return window.activeTextEditor; + } + + return this.waitForEditorChange(timeout); + } + + private waitForEditorChange(timeout: number = 500): Promise { + return new Promise((resolve, reject) => { + let timer: NodeJS.Timer | undefined; + + this._onEditorChangedFn = (editor: TextEditor | undefined) => { + if (timer) { + clearTimeout(timer); + timer = undefined; + + resolve(editor); + } + }; + + timer = setTimeout(() => { + timer = undefined; + + resolve(window.activeTextEditor); + }, timeout); + }); + } } diff --git a/src/trackers/activeEditorTracker.ts b/src/trackers/activeEditorTracker.ts deleted file mode 100644 index 5123e08..0000000 --- a/src/trackers/activeEditorTracker.ts +++ /dev/null @@ -1,58 +0,0 @@ -'use strict'; -import { commands, Disposable, TextEditor, window } from 'vscode'; -import { BuiltInCommands } from '../constants'; -import { Functions } from '../system'; - -export class ActiveEditorTracker implements Disposable { - private _disposable: Disposable; - private _resolver: ((editor: TextEditor | undefined) => void) | undefined; - - constructor() { - const fn = Functions.debounce((e: TextEditor | undefined) => this._resolver && this._resolver(e), 50); - this._disposable = window.onDidChangeActiveTextEditor(fn); - } - - dispose() { - this._disposable && this._disposable.dispose(); - } - - async awaitClose(timeout: number = 500): Promise { - void this.close(); - return this.wait(timeout); - } - - async awaitNext(timeout: number = 500): Promise { - void this.next(); - return this.wait(timeout); - } - - async close(): Promise<{} | undefined> { - return commands.executeCommand(BuiltInCommands.CloseActiveEditor); - } - - async next(): Promise<{} | undefined> { - return commands.executeCommand(BuiltInCommands.NextEditor); - } - - async wait(timeout: number = 500): Promise { - const editor = await new Promise((resolve, reject) => { - let timer: NodeJS.Timer | undefined; - - this._resolver = (editor: TextEditor | undefined) => { - if (timer) { - clearTimeout(timer); - timer = undefined; - resolve(editor); - } - }; - - timer = setTimeout(() => { - resolve(window.activeTextEditor); - timer = undefined; - }, timeout); - }); - - this._resolver = undefined; - return editor; - } -}