diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index cdc18e821840bd632c88475656b265e3579a1afa..a2cdf2f52a193030309131b3455760a456ab3331 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -353,16 +353,16 @@ export class VSCodeWindow { return this._readyState; } - private registerNavigationListenerOn(command: 'swipe' | 'app-command', back: 'left' | 'browser-backward', forward: 'right' | 'browser-forward') { + private registerNavigationListenerOn(command: 'swipe' | 'app-command', back: 'left' | 'browser-backward', forward: 'right' | 'browser-forward', acrossEditors: boolean) { this._win.on(command, (e, cmd) => { if (this.readyState !== ReadyState.READY) { return; // window must be ready } if (cmd === back) { - this.send('vscode:runAction', 'workbench.action.navigateBack'); + this.send('vscode:runAction', acrossEditors ? 'workbench.action.openPreviousRecentlyUsedEditor' : 'workbench.action.navigateBack'); } else if (cmd === forward) { - this.send('vscode:runAction', 'workbench.action.navigateForward'); + this.send('vscode:runAction', acrossEditors ? 'workbench.action.openNextRecentlyUsedEditor' : 'workbench.action.navigateForward'); } }); } @@ -393,7 +393,7 @@ export class VSCodeWindow { }); // App commands support - this.registerNavigationListenerOn('app-command', 'browser-backward', 'browser-forward'); + this.registerNavigationListenerOn('app-command', 'browser-backward', 'browser-forward', false); // Handle code that wants to open links this._win.webContents.on('new-window', (event: Event, url: string) => { @@ -457,7 +457,7 @@ export class VSCodeWindow { if (platform.isMacintosh) { const config = this.configurationService.getConfiguration(); if (config && config.workbench && config.workbench.editor && config.workbench.editor.swipeToNavigate) { - this.registerNavigationListenerOn('swipe', 'left', 'right'); + this.registerNavigationListenerOn('swipe', 'left', 'right', true); } else { this._win.removeAllListeners('swipe'); } diff --git a/src/vs/platform/editor/common/editor.ts b/src/vs/platform/editor/common/editor.ts index b6b52b4e3039474b4d08587581c5a023e6c643b8..81dc32ec8bc88c076c11de64cc62882c1e215231 100644 --- a/src/vs/platform/editor/common/editor.ts +++ b/src/vs/platform/editor/common/editor.ts @@ -248,6 +248,11 @@ export interface IEditorOptions { */ revealIfVisible?: boolean; + /** + * Will reveal the editor if it is already opened (even when not visible) in any of the opened editor groups. + */ + revealIfOpened?: boolean; + /** * An editor that is pinned remains in the editor stack even when another editor is being opened. * An editor that is not pinned will always get replaced by another editor that is not pinned. diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 2c9e9bb9edb2ab6b05f9abe9aef9a0fdf7b0ca4d..51cbe1aef5da0619427a37d0029d1e8652be8770 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -32,7 +32,7 @@ import { CloseEditorsInGroupAction, CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, JoinTwoGroupsAction, KeepEditorAction, CloseOtherEditorsInGroupAction, OpenToSideAction, RevertAndCloseEditorAction, NavigateBetweenGroupsAction, FocusActiveGroupAction, FocusFirstGroupAction, FocusSecondGroupAction, FocusThirdGroupAction, EvenGroupWidthsAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup, ShowEditorsInGroupOneAction, toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, CloseRightEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, ReopenClosedEditorAction, OpenPreviousRecentlyUsedEditorInGroupAction, NAVIGATE_IN_GROUP_ONE_PREFIX, - OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, ClearEditorHistoryAction, ShowEditorsInGroupTwoAction, MoveEditorRightInGroupAction, OpenNextEditorInGroup, OpenPreviousEditorInGroup, + OpenPreviousEditorFromHistoryAction, ShowAllEditorsAction, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, ClearEditorHistoryAction, ShowEditorsInGroupTwoAction, MoveEditorRightInGroupAction, OpenNextEditorInGroup, OpenPreviousEditorInGroup, OpenNextRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction, NAVIGATE_IN_GROUP_TWO_PREFIX, ShowEditorsInGroupThreeAction, NAVIGATE_IN_GROUP_THREE_PREFIX, FocusLastEditorInStackAction, OpenNextRecentlyUsedEditorInGroupAction, MoveEditorToPreviousGroupAction, MoveEditorToNextGroupAction, MoveEditorLeftInGroupAction, ClearRecentFilesAction } from 'vs/workbench/browser/parts/editor/editorActions'; import * as editorCommands from 'vs/workbench/browser/parts/editor/editorCommands'; @@ -318,6 +318,8 @@ Registry.as(QuickOpenExtensions.Quickopen).registerQuickOpen const category = nls.localize('view', "View"); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextEditorInGroup, OpenNextEditorInGroup.ID, OpenNextEditorInGroup.LABEL), 'View: Open Next Editor in Group', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditorInGroup, OpenPreviousEditorInGroup.ID, OpenPreviousEditorInGroup.LABEL), 'View: Open Next Recently Used Editor in Group', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorAction, OpenNextRecentlyUsedEditorAction.ID, OpenNextRecentlyUsedEditorAction.LABEL), 'View: Open Next Recently Used Editor'); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorAction, OpenPreviousRecentlyUsedEditorAction.ID, OpenPreviousRecentlyUsedEditorAction.LABEL), 'View: Open Previous Recently Used Editor'); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextRecentlyUsedEditorInGroupAction, OpenNextRecentlyUsedEditorInGroupAction.ID, OpenNextRecentlyUsedEditorInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyCode.Tab } }), 'Open Next Recently Used Editor in Group'); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousRecentlyUsedEditorInGroupAction, OpenPreviousRecentlyUsedEditorInGroupAction.ID, OpenPreviousRecentlyUsedEditorInGroupAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Tab, mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.Tab } }), 'Open Previous Recently Used Editor in Group'); registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllEditorsAction, ShowAllEditorsAction.ID, ShowAllEditorsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_P), mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Tab } }), 'View: Show All Editors', category); diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index ff2c5ec840412a4b5f1756f48eed7a7c72fe4967..4419e710283278f09cfffe3951d8f7494e963ded 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -1321,6 +1321,38 @@ export class OpenPreviousEditorFromHistoryAction extends Action { } } +export class OpenNextRecentlyUsedEditorAction extends Action { + + public static ID = 'workbench.action.openNextRecentlyUsedEditor'; + public static LABEL = nls.localize('openNextRecentlyUsedEditor', "Open Next Recently Used Editor"); + + constructor(id: string, label: string, @IHistoryService private historyService: IHistoryService) { + super(id, label); + } + + public run(): TPromise { + this.historyService.forward(true); + + return TPromise.as(null); + } +} + +export class OpenPreviousRecentlyUsedEditorAction extends Action { + + public static ID = 'workbench.action.openPreviousRecentlyUsedEditor'; + public static LABEL = nls.localize('openPreviousRecentlyUsedEditor', "Open Previous Recently Used Editor"); + + constructor(id: string, label: string, @IHistoryService private historyService: IHistoryService) { + super(id, label); + } + + public run(): TPromise { + this.historyService.back(true); + + return TPromise.as(null); + } +} + export class ClearEditorHistoryAction extends Action { public static ID = 'workbench.action.clearEditorHistory'; diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index c7b22982d3a1e044ea00fd061c733d1bbf4ed56f..95bee5479241071018f55ba58aa8606ca3d91f98 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -1329,7 +1329,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService // Respect option to reveal an editor if it is open (not necessarily visible) const skipRevealIfOpen = (options && options.index) || arg1 === true /* open to side */ || typeof arg1 === 'number' /* open specific group */; - if (!skipRevealIfOpen && this.revealIfOpen) { + if (!skipRevealIfOpen && (this.revealIfOpen || (options && options.revealIfOpened))) { const group = this.stacks.findGroup(input); if (group) { return this.stacks.positionOfGroup(group); diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 2e4bdaba8d824eec1ca92d6b883215e29222c421..c89cfe4455b96ca52c08a05c9245769aec5a208d 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -513,6 +513,7 @@ export class EditorOptions implements IEditorOptions { options.preserveFocus = settings.preserveFocus; options.forceOpen = settings.forceOpen; options.revealIfVisible = settings.revealIfVisible; + options.revealIfOpened = settings.revealIfOpened; options.pinned = settings.pinned; options.index = settings.index; options.inactive = settings.inactive; @@ -528,6 +529,7 @@ export class EditorOptions implements IEditorOptions { this.preserveFocus = other.preserveFocus; this.forceOpen = other.forceOpen; this.revealIfVisible = other.revealIfVisible; + this.revealIfOpened = other.revealIfOpened; this.pinned = other.pinned; this.index = other.index; this.inactive = other.inactive; @@ -552,6 +554,11 @@ export class EditorOptions implements IEditorOptions { */ public revealIfVisible: boolean; + /** + * Will reveal the editor if it is already opened (even when not visible) in any of the opened editor groups. + */ + public revealIfOpened: boolean; + /** * An editor that is pinned remains in the editor stack even when another editor is being opened. * An editor that is not pinned will always get replaced by another editor that is not pinned. @@ -586,7 +593,7 @@ export class TextEditorOptions extends EditorOptions { public static from(input: IBaseResourceInput): TextEditorOptions { let options: TextEditorOptions = null; if (input && input.options) { - if (input.options.selection || input.options.viewState || input.options.forceOpen || input.options.revealIfVisible || input.options.preserveFocus || input.options.pinned || input.options.inactive || typeof input.options.index === 'number') { + if (input.options.selection || input.options.viewState || input.options.forceOpen || input.options.revealIfVisible || input.options.revealIfOpened || input.options.preserveFocus || input.options.pinned || input.options.inactive || typeof input.options.index === 'number') { options = new TextEditorOptions(); } @@ -603,6 +610,10 @@ export class TextEditorOptions extends EditorOptions { options.revealIfVisible = true; } + if (input.options.revealIfOpened) { + options.revealIfOpened = true; + } + if (input.options.preserveFocus) { options.preserveFocus = true; } @@ -639,6 +650,7 @@ export class TextEditorOptions extends EditorOptions { options.preserveFocus = settings.preserveFocus; options.forceOpen = settings.forceOpen; options.revealIfVisible = settings.revealIfVisible; + options.revealIfOpened = settings.revealIfOpened; options.pinned = settings.pinned; options.index = settings.index; options.inactive = settings.inactive; @@ -783,6 +795,7 @@ export class TextDiffEditorOptions extends TextEditorOptions { options.preserveFocus = settings.preserveFocus; options.forceOpen = settings.forceOpen; options.revealIfVisible = settings.revealIfVisible; + options.revealIfOpened = settings.revealIfOpened; options.pinned = settings.pinned; options.index = settings.index; diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index b8edd0860b2b1d7b4e37ad4eaf6e4560bd31c124..e745057fb92f3f0aa9bc2db5474bb1c65cc76811 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -248,17 +248,67 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic } } - public forward(): void { + public forward(acrossEditors?: boolean): void { if (this.stack.length > this.index + 1) { - this.index++; - this.navigate(); + if (acrossEditors) { + this.doForwardAcrossEditors(); + } else { + this.doForwardInEditors(); + } + } + } + + private doForwardInEditors(): void { + this.index++; + this.navigate(); + } + + private doForwardAcrossEditors(): void { + let currentIndex = this.index; + const currentEntry = this.stack[this.index]; + + // Find the next entry that does not match our current entry + while (this.stack.length > currentIndex + 1) { + currentIndex++; + + const previousEntry = this.stack[currentIndex]; + if (!this.matches(currentEntry.input, previousEntry.input)) { + this.index = currentIndex; + this.navigate(true /* across editors */); + break; + } } } - public back(): void { + public back(acrossEditors?: boolean): void { if (this.index > 0) { - this.index--; - this.navigate(); + if (acrossEditors) { + this.doBackAcrossEditors(); + } else { + this.doBackInEditors(); + } + } + } + + private doBackInEditors(): void { + this.index--; + this.navigate(); + } + + private doBackAcrossEditors(): void { + let currentIndex = this.index; + const currentEntry = this.stack[this.index]; + + // Find the next previous entry that does not match our current entry + while (currentIndex > 0) { + currentIndex--; + + const previousEntry = this.stack[currentIndex]; + if (!this.matches(currentEntry.input, previousEntry.input)) { + this.index = currentIndex; + this.navigate(true /* across editors */); + break; + } } } @@ -271,14 +321,14 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic this.recentlyClosedFiles = []; } - private navigate(): void { + private navigate(acrossEditors?: boolean): void { const entry = this.stack[this.index]; let options = entry.options; - if (options) { - options.revealIfVisible = true; + if (options && !acrossEditors /* ignore line/col options when going across editors */) { + options.revealIfOpened = true; } else { - options = { revealIfVisible: true }; + options = { revealIfOpened: true }; } this.navigatingInStack = true; diff --git a/src/vs/workbench/services/history/common/history.ts b/src/vs/workbench/services/history/common/history.ts index d76384abd5c99470e3d9074dfaeb43b23f84a03e..fde4c51dccbb82c9ce7469162408f3aa78707d80 100644 --- a/src/vs/workbench/services/history/common/history.ts +++ b/src/vs/workbench/services/history/common/history.ts @@ -25,13 +25,19 @@ export interface IHistoryService { /** * Navigate forwards in history. + * + * @param acrossEditors instructs the history to skip navigation entries that + * are only within the same document. */ - forward(): void; + forward(acrossEditors?: boolean): void; /** * Navigate backwards in history. + * + * @param acrossEditors instructs the history to skip navigation entries that + * are only within the same document. */ - back(): void; + back(acrossEditors?: boolean): void; /** * Removes an entry from history.