diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 69d49d836fd..fb916b8f73d 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -356,9 +356,26 @@ export class CodeMenu { newFile = this.createMenuItem(nls.localize({ key: 'miNewFile', comment: ['&& denotes a mnemonic'] }, "&&New File"), 'workbench.action.files.newUntitledFile'); } - const open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: (menuItem, win, event) => this.windowsService.pickFileFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); - const openWorkspace = new MenuItem(this.likeAction('workbench.action.openWorkspace', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open Workspace...")), click: (menuItem, win, event) => this.windowsService.openWorkspace(void 0, { forceNewWindow: this.isOptionClick(event) }) })); - const openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: (menuItem, win, event) => this.windowsService.pickFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); + let open: Electron.MenuItem; + if (hasNoWindows) { + open = new MenuItem(this.likeAction('workbench.action.files.openFileFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open...")), click: (menuItem, win, event) => this.windowsService.pickFileFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); + } else { + open = this.createMenuItem(nls.localize({ key: 'miOpen', comment: ['&& denotes a mnemonic'] }, "&&Open..."), ['workbench.action.files.openFileFolder', 'workbench.action.files.openFileFolderInNewWindow']); + } + + let openWorkspace: Electron.MenuItem; + if (hasNoWindows) { + openWorkspace = new MenuItem(this.likeAction('workbench.action.openWorkspace', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open Workspace...")), click: (menuItem, win, event) => this.windowsService.pickWorkspaceAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); + } else { + openWorkspace = this.createMenuItem(nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open Workspace..."), ['workbench.action.openWorkspace', 'workbench.action.openWorkspaceInNewWindow']); + } + + let openFolder: Electron.MenuItem; + if (hasNoWindows) { + openFolder = new MenuItem(this.likeAction('workbench.action.files.openFolder', { label: this.mnemonicLabel(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder...")), click: (menuItem, win, event) => this.windowsService.pickFolderAndOpen({ forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }) })); + } else { + openFolder = this.createMenuItem(nls.localize({ key: 'miOpenFolder', comment: ['&& denotes a mnemonic'] }, "Open &&Folder..."), ['workbench.action.files.openFolder', 'workbench.action.files.openFolderInNewWindow']); + } let openFile: Electron.MenuItem; if (hasNoWindows) { diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 436856c01d5..346c0c37f22 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -1339,8 +1339,8 @@ export class WindowsManager implements IWindowsMainService { return result; } - public openWorkspace(win?: CodeWindow, options?: { forceNewWindow?: boolean }): void { - this.workspacesManager.openWorkspace(win, options); + public pickWorkspaceAndOpen(options: INativeOpenDialogOptions): void { + this.workspacesManager.pickWorkspaceAndOpen(options); } private onBeforeWindowUnload(e: IWindowUnloadEvent): void { @@ -1755,13 +1755,8 @@ class WorkspacesManager { }); } - public openWorkspace(window = this.windowsMainService.getLastActiveWindow(), options?: { forceNewWindow?: boolean }): void { - let defaultPath: string; - if (window && window.openedWorkspace && !this.workspacesService.isUntitledWorkspace(window.openedWorkspace)) { - defaultPath = dirname(window.openedWorkspace.configPath); - } else { - defaultPath = this.getWorkspaceDialogDefaultPath(window ? (window.openedWorkspace || window.openedFolderPath) : void 0); - } + public pickWorkspaceAndOpen(options: INativeOpenDialogOptions): void { + const window = this.windowsMainService.getWindowById(options.windowId) || this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow(); this.windowsMainService.pickFileAndOpen({ windowId: window ? window.id : void 0, @@ -1770,9 +1765,11 @@ class WorkspacesManager { title: localize('openWorkspaceTitle', "Open Workspace"), filters: WORKSPACE_FILTER, properties: ['openFile'], - defaultPath + defaultPath: options.dialogOptions && options.dialogOptions.defaultPath }, - forceNewWindow: options && options.forceNewWindow + forceNewWindow: options.forceNewWindow, + telemetryEventName: options.telemetryEventName, + telemetryExtraData: options.telemetryExtraData }); } @@ -1831,7 +1828,7 @@ class WorkspacesManager { buttonLabel: mnemonicButtonLabel(localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), title: localize('saveWorkspace', "Save Workspace"), filters: WORKSPACE_FILTER, - defaultPath: this.getWorkspaceDialogDefaultPath(workspace) + defaultPath: this.getUntitledWorkspaceSaveDialogDefaultPath(workspace) }); if (target) { @@ -1847,7 +1844,7 @@ class WorkspacesManager { } } - private getWorkspaceDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string { + private getUntitledWorkspaceSaveDialogDefaultPath(workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier): string { if (workspace) { if (isSingleFolderWorkspaceIdentifier(workspace)) { return dirname(workspace); @@ -1862,6 +1859,7 @@ class WorkspacesManager { } } } + return void 0; } } diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 5f6c19c1d55..632dccaee33 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -43,11 +43,11 @@ export interface IWindowsService { pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise; pickFileAndOpen(options: INativeOpenDialogOptions): TPromise; pickFolderAndOpen(options: INativeOpenDialogOptions): TPromise; + pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise; reloadWindow(windowId: number): TPromise; openDevTools(windowId: number): TPromise; toggleDevTools(windowId: number): TPromise; closeWorkspace(windowId: number): TPromise; - openWorkspace(windowId: number): TPromise; createAndEnterWorkspace(windowId: number, folderPaths?: string[], path?: string): TPromise; saveAndEnterWorkspace(windowId: number, path: string): TPromise; toggleFullScreen(windowId: number): TPromise; @@ -115,11 +115,11 @@ export interface IWindowService { pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise; pickFileAndOpen(options: INativeOpenDialogOptions): TPromise; pickFolderAndOpen(options: INativeOpenDialogOptions): TPromise; + pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise; reloadWindow(): TPromise; openDevTools(): TPromise; toggleDevTools(): TPromise; closeWorkspace(): TPromise; - openWorkspace(): TPromise; updateTouchBar(items: ICommandAction[][]): TPromise; createAndEnterWorkspace(folderPaths?: string[], path?: string): TPromise; saveAndEnterWorkspace(path: string): TPromise; diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 237fadf835f..10a3e7eabbf 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -20,10 +20,10 @@ export interface IWindowsChannel extends IChannel { call(command: 'pickFileFolderAndOpen', arg: INativeOpenDialogOptions): TPromise; call(command: 'pickFileAndOpen', arg: INativeOpenDialogOptions): TPromise; call(command: 'pickFolderAndOpen', arg: INativeOpenDialogOptions): TPromise; + call(command: 'pickWorkspaceAndOpen', arg: INativeOpenDialogOptions): TPromise; call(command: 'reloadWindow', arg: number): TPromise; call(command: 'toggleDevTools', arg: number): TPromise; call(command: 'closeWorkspace', arg: number): TPromise; - call(command: 'openWorkspace', arg: number): TPromise; call(command: 'createAndEnterWorkspace', arg: [number, string[], string]): TPromise; call(command: 'saveAndEnterWorkspace', arg: [number, string]): TPromise; call(command: 'toggleFullScreen', arg: number): TPromise; @@ -82,11 +82,11 @@ export class WindowsChannel implements IWindowsChannel { case 'pickFileFolderAndOpen': return this.service.pickFileFolderAndOpen(arg); case 'pickFileAndOpen': return this.service.pickFileAndOpen(arg); case 'pickFolderAndOpen': return this.service.pickFolderAndOpen(arg); + case 'pickWorkspaceAndOpen': return this.service.pickWorkspaceAndOpen(arg); case 'reloadWindow': return this.service.reloadWindow(arg); case 'openDevTools': return this.service.openDevTools(arg); case 'toggleDevTools': return this.service.toggleDevTools(arg); case 'closeWorkspace': return this.service.closeWorkspace(arg); - case 'openWorkspace': return this.service.openWorkspace(arg); case 'createAndEnterWorkspace': return this.service.createAndEnterWorkspace(arg[0], arg[1], arg[2]); case 'saveAndEnterWorkspace': return this.service.saveAndEnterWorkspace(arg[0], arg[1]); case 'toggleFullScreen': return this.service.toggleFullScreen(arg); @@ -154,6 +154,10 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('pickFolderAndOpen', options); } + pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise { + return this.channel.call('pickWorkspaceAndOpen', options); + } + reloadWindow(windowId: number): TPromise { return this.channel.call('reloadWindow', windowId); } @@ -170,10 +174,6 @@ export class WindowsChannelClient implements IWindowsService { return this.channel.call('closeWorkspace', windowId); } - openWorkspace(windowId: number): TPromise { - return this.channel.call('openWorkspace', windowId); - } - createAndEnterWorkspace(windowId: number, folderPaths?: string[], path?: string): TPromise { return this.channel.call('createAndEnterWorkspace', [windowId, folderPaths, path]); } diff --git a/src/vs/platform/windows/electron-browser/windowService.ts b/src/vs/platform/windows/electron-browser/windowService.ts index daa5a0bec47..1302176c493 100644 --- a/src/vs/platform/windows/electron-browser/windowService.ts +++ b/src/vs/platform/windows/electron-browser/windowService.ts @@ -51,6 +51,12 @@ export class WindowService implements IWindowService { return this.windowsService.pickFolderAndOpen(options); } + pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise { + options.windowId = this.windowId; + + return this.windowsService.pickWorkspaceAndOpen(options); + } + reloadWindow(): TPromise { return this.windowsService.reloadWindow(this.windowId); } @@ -67,10 +73,6 @@ export class WindowService implements IWindowService { return this.windowsService.closeWorkspace(this.windowId); } - openWorkspace(): TPromise { - return this.windowsService.openWorkspace(this.windowId); - } - createAndEnterWorkspace(folderPaths?: string[], path?: string): TPromise { return this.windowsService.createAndEnterWorkspace(this.windowId, folderPaths, path); } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 7ce06d4a2f0..6a35060fcd7 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -60,7 +60,6 @@ export interface IWindowsMainService { // methods ready(initialUserEnv: IProcessEnvironment): void; reload(win: ICodeWindow, cli?: ParsedArgs): void; - openWorkspace(win?: ICodeWindow, options?: { forceNewWindow?: boolean }): void; createAndEnterWorkspace(win: ICodeWindow, folderPaths?: string[], path?: string): TPromise; saveAndEnterWorkspace(win: ICodeWindow, path: string): TPromise; closeWorkspace(win: ICodeWindow): void; @@ -69,6 +68,7 @@ export interface IWindowsMainService { pickFileFolderAndOpen(options: INativeOpenDialogOptions): void; pickFolderAndOpen(options: INativeOpenDialogOptions): void; pickFileAndOpen(options: INativeOpenDialogOptions): void; + pickWorkspaceAndOpen(options: INativeOpenDialogOptions): void; focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow; getLastActiveWindow(): ICodeWindow; waitForWindowCloseOrLoad(windowId: number): TPromise; diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 2c3ac1c8934..119303af739 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -70,6 +70,12 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(null); } + pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise { + this.windowsMainService.pickWorkspaceAndOpen(options); + + return TPromise.as(null); + } + reloadWindow(windowId: number): TPromise { const codeWindow = this.windowsMainService.getWindowById(windowId); @@ -125,16 +131,6 @@ export class WindowsService implements IWindowsService, IDisposable { return TPromise.as(null); } - openWorkspace(windowId: number): TPromise { - const codeWindow = this.windowsMainService.getWindowById(windowId); - - if (codeWindow) { - this.windowsMainService.openWorkspace(codeWindow); - } - - return TPromise.as(null); - } - createAndEnterWorkspace(windowId: number, folderPaths?: string[], path?: string): TPromise { const codeWindow = this.windowsMainService.getWindowById(windowId); diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 78875f25837..ff061b4d369 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -48,8 +48,7 @@ export interface IWorkspaceContextService { onDidChangeWorkspaceFolders: Event; /** - * Provides access to the workspace object the platform is running with. This may be null if the workbench was opened - * without workspace (empty); + * Provides access to the workspace object the platform is running with. */ getWorkspace(): IWorkspace; diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index ea253e70218..08dd277f058 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -26,6 +26,27 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi import { IQuickOpenService, IFilePickOpenEntry, IPickOptions } from 'vs/platform/quickOpen/common/quickOpen'; import { CancellationToken } from 'vs/base/common/cancellation'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; + +export class OpenFileAction extends Action { + + static ID = 'workbench.action.files.openFile'; + static LABEL = nls.localize('openFile', "Open File..."); + + constructor( + id: string, + label: string, + @IWindowService private windowService: IWindowService, + @IHistoryService private historyService: IHistoryService, + @IWorkspaceContextService private contextService: IWorkspaceContextService + ) { + super(id, label); + } + + run(event?: any, data?: ITelemetryData): TPromise { + return this.windowService.pickFileAndOpen({ telemetryExtraData: data, dialogOptions: { defaultPath: defaultFilePath(this.contextService, this.historyService) } }); + } +} export class OpenFolderAction extends Action { @@ -35,13 +56,15 @@ export class OpenFolderAction extends Action { constructor( id: string, label: string, - @IWindowService private windowService: IWindowService + @IWindowService private windowService: IWindowService, + @IHistoryService private historyService: IHistoryService, + @IWorkspaceContextService private contextService: IWorkspaceContextService ) { super(id, label); } run(event?: any, data?: ITelemetryData): TPromise { - return this.windowService.pickFolderAndOpen({ telemetryExtraData: data }); + return this.windowService.pickFolderAndOpen({ telemetryExtraData: data, dialogOptions: { defaultPath: defaultFolderPath(this.contextService, this.historyService) } }); } } @@ -53,16 +76,57 @@ export class OpenFileFolderAction extends Action { constructor( id: string, label: string, - @IWindowService private windowService: IWindowService + @IWindowService private windowService: IWindowService, + @IHistoryService private historyService: IHistoryService, + @IWorkspaceContextService private contextService: IWorkspaceContextService ) { super(id, label); } run(event?: any, data?: ITelemetryData): TPromise { - return this.windowService.pickFileFolderAndOpen({ telemetryExtraData: data }); + return this.windowService.pickFileFolderAndOpen({ telemetryExtraData: data, dialogOptions: { defaultPath: defaultFilePath(this.contextService, this.historyService) } }); } } +export const openFileFolderInNewWindowCommand = (accessor: ServicesAccessor) => { + const { windowService, historyService, contextService } = services(accessor); + + windowService.pickFileFolderAndOpen({ forceNewWindow: true, dialogOptions: { defaultPath: defaultFilePath(contextService, historyService) } }); +}; + +export const openFolderCommand = (accessor: ServicesAccessor, forceNewWindow: boolean) => { + const { windowService, historyService, contextService } = services(accessor); + + windowService.pickFolderAndOpen({ forceNewWindow, dialogOptions: { defaultPath: defaultFolderPath(contextService, historyService) } }); +}; + +export const openFolderInNewWindowCommand = (accessor: ServicesAccessor) => { + const { windowService, historyService, contextService } = services(accessor); + + windowService.pickFolderAndOpen({ forceNewWindow: true, dialogOptions: { defaultPath: defaultFolderPath(contextService, historyService) } }); +}; + +export const openFileInNewWindowCommand = (accessor: ServicesAccessor) => { + const { windowService, historyService, contextService } = services(accessor); + + windowService.pickFileAndOpen({ forceNewWindow: true, dialogOptions: { defaultPath: defaultFilePath(contextService, historyService) } }); +}; + +export const openWorkspaceInNewWindowCommand = (accessor: ServicesAccessor) => { + const { windowService, historyService, contextService, environmentService } = services(accessor); + + windowService.pickWorkspaceAndOpen({ forceNewWindow: true, dialogOptions: { defaultPath: defaultWorkspacePath(contextService, historyService, environmentService) } }); +}; + +function services(accessor: ServicesAccessor): { windowService: IWindowService, historyService: IHistoryService, contextService: IWorkspaceContextService, environmentService: IEnvironmentService } { + return { + windowService: accessor.get(IWindowService), + historyService: accessor.get(IHistoryService), + contextService: accessor.get(IWorkspaceContextService), + environmentService: accessor.get(IEnvironmentService) + }; +} + export abstract class BaseWorkspacesAction extends Action { constructor( @@ -70,27 +134,65 @@ export abstract class BaseWorkspacesAction extends Action { label: string, protected windowService: IWindowService, protected environmentService: IEnvironmentService, - protected contextService: IWorkspaceContextService + protected contextService: IWorkspaceContextService, + protected historyService: IHistoryService ) { super(id, label); } protected pickFolders(buttonLabel: string, title: string): string[] { - let defaultPath: string; - const workspace = this.contextService.getWorkspace(); - if (workspace.folders.length > 0) { - defaultPath = dirname(workspace.folders[0].uri.fsPath); // pick the parent of the first root by default - } - return this.windowService.showOpenDialog({ buttonLabel, title, properties: ['multiSelections', 'openDirectory', 'createDirectory'], - defaultPath + defaultPath: defaultFolderPath(this.contextService, this.historyService) }); } } +function defaultFilePath(contextService: IWorkspaceContextService, historyService: IHistoryService): string { + let candidate: URI; + + // Check for last active file first... + candidate = historyService.getLastActiveFile(); + + // ...then for last active file root + if (!candidate) { + candidate = historyService.getLastActiveWorkspaceRoot('file'); + } + + return candidate ? dirname(candidate.fsPath) : void 0; +} + +function defaultFolderPath(contextService: IWorkspaceContextService, historyService: IHistoryService): string { + let candidate: URI; + + // Check for last active file root first... + candidate = historyService.getLastActiveWorkspaceRoot('file'); + + // ...then for last active file + if (!candidate) { + candidate = historyService.getLastActiveFile(); + } + + return candidate ? dirname(candidate.fsPath) : void 0; +} + +function defaultWorkspacePath(contextService: IWorkspaceContextService, historyService: IHistoryService, environmentService: IEnvironmentService): string { + + // Check for current workspace config file first... + if (contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && !isUntitledWorkspace(contextService.getWorkspace().configuration.fsPath, environmentService)) { + return dirname(contextService.getWorkspace().configuration.fsPath); + } + + // ...then fallback to default folder path + return defaultFolderPath(contextService, historyService); +} + +function isUntitledWorkspace(path: string, environmentService: IEnvironmentService): boolean { + return isParent(path, environmentService.workspacesHome, !isLinux /* ignore case */); +} + export class AddRootFolderAction extends BaseWorkspacesAction { static ID = 'workbench.action.addRootFolder'; @@ -104,9 +206,10 @@ export class AddRootFolderAction extends BaseWorkspacesAction { @IEnvironmentService environmentService: IEnvironmentService, @IInstantiationService private instantiationService: IInstantiationService, @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, - @IViewletService private viewletService: IViewletService + @IViewletService private viewletService: IViewletService, + @IHistoryService historyService: IHistoryService ) { - super(id, label, windowService, environmentService, contextService); + super(id, label, windowService, environmentService, contextService, historyService); } public run(): TPromise { @@ -144,9 +247,10 @@ export class GlobalRemoveRootFolderAction extends BaseWorkspacesAction { @IWorkspaceContextService contextService: IWorkspaceContextService, @IEnvironmentService environmentService: IEnvironmentService, @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, - @ICommandService private commandService: ICommandService + @ICommandService private commandService: ICommandService, + @IHistoryService historyService: IHistoryService ) { - super(id, label, windowService, environmentService, contextService); + super(id, label, windowService, environmentService, contextService, historyService); } public run(): TPromise { @@ -186,9 +290,10 @@ class NewWorkspaceAction extends BaseWorkspacesAction { @IWindowService windowService: IWindowService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IEnvironmentService environmentService: IEnvironmentService, - @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService + @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, + @IHistoryService historyService: IHistoryService ) { - super(id, label, windowService, environmentService, contextService); + super(id, label, windowService, environmentService, contextService, historyService); } public run(): TPromise { @@ -243,6 +348,7 @@ export class OpenFolderSettingsAction extends Action { public run(): TPromise { const workspaceFolder = this.contextService.getWorkspaceFolder(this.rootUri); + return this.commandService.executeCommand('_workbench.action.openFolderSettings', workspaceFolder); } } @@ -258,9 +364,10 @@ export class SaveWorkspaceAsAction extends BaseWorkspacesAction { @IWindowService windowService: IWindowService, @IEnvironmentService environmentService: IEnvironmentService, @IWorkspaceContextService contextService: IWorkspaceContextService, - @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService + @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService, + @IHistoryService historyService: IHistoryService ) { - super(id, label, windowService, environmentService, contextService); + super(id, label, windowService, environmentService, contextService, historyService); } public run(): TPromise { @@ -281,25 +388,13 @@ export class SaveWorkspaceAsAction extends BaseWorkspacesAction { } private getNewWorkspaceConfigPath(): string { - const workspace = this.contextService.getWorkspace(); - let defaultPath: string; - if (workspace.configuration && !this.isUntitledWorkspace(workspace.configuration.fsPath)) { - defaultPath = workspace.configuration.fsPath; - } else if (workspace.folders.length > 0) { - defaultPath = dirname(workspace.folders[0].uri.fsPath); // pick the parent of the first root by default - } - return this.windowService.showSaveDialog({ buttonLabel: mnemonicButtonLabel(nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save")), title: nls.localize('saveWorkspace', "Save Workspace"), filters: WORKSPACE_FILTER, - defaultPath + defaultPath: defaultWorkspacePath(this.contextService, this.historyService, this.environmentService) }); } - - private isUntitledWorkspace(path: string): boolean { - return isParent(path, this.environmentService.workspacesHome, !isLinux /* ignore case */); - } } export class OpenWorkspaceAction extends Action { @@ -311,12 +406,15 @@ export class OpenWorkspaceAction extends Action { id: string, label: string, @IWindowService private windowService: IWindowService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @IHistoryService private historyService: IHistoryService, + @IEnvironmentService private environmentService: IEnvironmentService ) { super(id, label); } - public run(): TPromise { - return this.windowService.openWorkspace(); + public run(event?: any, data?: ITelemetryData): TPromise { + return this.windowService.pickWorkspaceAndOpen({ telemetryExtraData: data, dialogOptions: { defaultPath: defaultWorkspacePath(this.contextService, this.historyService, this.environmentService) } }); } } diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index fa35f6a9600..94b80b9c37c 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -20,6 +20,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import URI from 'vs/base/common/uri'; import { IEditorOptions, Position as EditorPosition } from 'vs/platform/editor/common/editor'; +import { openFolderCommand, openFileInNewWindowCommand, openFileFolderInNewWindowCommand, openFolderInNewWindowCommand, openWorkspaceInNewWindowCommand } from 'vs/workbench/browser/actions/workspaceActions'; // --- List Commands @@ -420,4 +421,11 @@ export function registerCommands(): void { return void 0; }); }); + + CommandsRegistry.registerCommand('_files.pickFolderAndOpen', openFolderCommand); + + CommandsRegistry.registerCommand('workbench.action.files.openFileInNewWindow', openFileInNewWindowCommand); + CommandsRegistry.registerCommand('workbench.action.files.openFolderInNewWindow', openFolderInNewWindowCommand); + CommandsRegistry.registerCommand('workbench.action.files.openFileFolderInNewWindow', openFileFolderInNewWindowCommand); + CommandsRegistry.registerCommand('workbench.action.openWorkspaceInNewWindow', openWorkspaceInNewWindowCommand); } diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 87de097e471..63aed0c15c1 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -18,7 +18,7 @@ import { CloseEditorAction, KeybindingsReferenceAction, OpenDocumentationUrlActi import { MessagesVisibleContext } from 'vs/workbench/electron-browser/workbench'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { registerCommands } from 'vs/workbench/electron-browser/commands'; -import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, OpenFolderAsWorkspaceInNewWindowAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, OpenFolderAsWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -35,7 +35,16 @@ workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(NewWin workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseCurrentWindowAction, CloseCurrentWindowAction.ID, CloseCurrentWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W }), 'Close Window'); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SwitchWindow, SwitchWindow.ID, SwitchWindow.LABEL, { primary: null, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_W } }), 'Switch Window...'); workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(QuickSwitchWindow, QuickSwitchWindow.ID, QuickSwitchWindow.LABEL), 'Quick Switch Window...'); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory); + +if (isMacintosh) { + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', fileCategory); +} else { + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', fileCategory); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', fileCategory); +} + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', fileCategory); if (!!product.reportIssueUrl) { workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(ReportIssueAction, ReportIssueAction.ID, ReportIssueAction.LABEL), 'Help: Report Issues', helpCategory); diff --git a/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts b/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts index 36a53527512..913c3e87ccf 100644 --- a/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts +++ b/src/vs/workbench/parts/execution/electron-browser/execution.contribution.ts @@ -103,7 +103,7 @@ export abstract class AbstractOpenInTerminalAction extends Action { let pathToOpen: string; // Try workspace path first - const root = this.historyService.getLastActiveWorkspaceRoot(); + const root = this.historyService.getLastActiveWorkspaceRoot('file'); pathToOpen = this.resource ? this.resource.fsPath : (root && root.fsPath); // Otherwise check if we have an active file open diff --git a/src/vs/workbench/parts/files/browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts index 478d1d9ef84..69f877671f9 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts @@ -7,10 +7,9 @@ import nls = require('vs/nls'); import { Registry } from 'vs/platform/registry/common/platform'; import { Action, IAction } from 'vs/base/common/actions'; -import { isMacintosh } from 'vs/base/common/platform'; import { ActionItem, BaseActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions, ActionBarContributor } from 'vs/workbench/browser/actions'; -import { GlobalNewUntitledFileAction, SaveFileAsAction, OpenFileAction, ShowOpenedFileInNewWindow, CopyPathAction, GlobalCopyPathAction, RevealInOSAction, GlobalRevealInOSAction, pasteIntoFocusedFilesExplorerViewItem, FocusOpenEditorsView, FocusFilesExplorer, GlobalCompareResourcesAction, GlobalNewFileAction, GlobalNewFolderAction, RevertFileAction, SaveFilesAction, SaveAllAction, SaveFileAction, MoveFileToTrashAction, TriggerRenameFileAction, PasteFileAction, CopyFileAction, SelectResourceForCompareAction, CompareResourcesAction, NewFolderAction, NewFileAction, OpenToSideAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithSavedAction } from 'vs/workbench/parts/files/browser/fileActions'; +import { GlobalNewUntitledFileAction, SaveFileAsAction, ShowOpenedFileInNewWindow, CopyPathAction, GlobalCopyPathAction, RevealInOSAction, GlobalRevealInOSAction, pasteIntoFocusedFilesExplorerViewItem, FocusOpenEditorsView, FocusFilesExplorer, GlobalCompareResourcesAction, GlobalNewFileAction, GlobalNewFolderAction, RevertFileAction, SaveFilesAction, SaveAllAction, SaveFileAction, MoveFileToTrashAction, TriggerRenameFileAction, PasteFileAction, CopyFileAction, SelectResourceForCompareAction, CompareResourcesAction, NewFolderAction, NewFileAction, OpenToSideAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithSavedAction } from 'vs/workbench/parts/files/browser/fileActions'; import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/parts/files/browser/saveErrorHandler'; import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; @@ -19,8 +18,8 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { FileStat, Model } from 'vs/workbench/parts/files/common/explorerModel'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; -import { OpenFolderAction, OpenFileFolderAction, AddRootFolderAction, RemoveRootFolderAction, OpenFolderSettingsAction } from 'vs/workbench/browser/actions/workspaceActions'; -import { copyFocusedFilesExplorerViewItem, revealInOSFocusedFilesExplorerItem, openFocusedExplorerItemSideBySideCommand, copyPathOfFocusedExplorerItem, copyPathCommand, revealInExplorerCommand, revealInOSCommand, openFolderPickerCommand, openWindowCommand, openFileInNewWindowCommand, deleteFocusedFilesExplorerViewItemCommand, moveFocusedFilesExplorerViewItemToTrashCommand, renameFocusedFilesExplorerViewItemCommand } from 'vs/workbench/parts/files/browser/fileCommands'; +import { AddRootFolderAction, RemoveRootFolderAction, OpenFolderSettingsAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { copyFocusedFilesExplorerViewItem, revealInOSFocusedFilesExplorerItem, openFocusedExplorerItemSideBySideCommand, copyPathOfFocusedExplorerItem, copyPathCommand, revealInExplorerCommand, revealInOSCommand, openWindowCommand, deleteFocusedFilesExplorerViewItemCommand, moveFocusedFilesExplorerViewItemToTrashCommand, renameFocusedFilesExplorerViewItemCommand } from 'vs/workbench/parts/files/browser/fileCommands'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -212,17 +211,8 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRevealInOSAction registry.registerWorkbenchAction(new SyncActionDescriptor(ShowOpenedFileInNewWindow, ShowOpenedFileInNewWindow.ID, ShowOpenedFileInNewWindow.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_O) }), 'File: Open Active File in New Window', category); registry.registerWorkbenchAction(new SyncActionDescriptor(CompareWithSavedAction, CompareWithSavedAction.ID, CompareWithSavedAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_D) }), 'File: Compare Active File with Saved', category); -if (isMacintosh) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', category); -} else { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', category); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', category); -} - // Commands -CommandsRegistry.registerCommand('_files.pickFolderAndOpen', openFolderPickerCommand); CommandsRegistry.registerCommand('_files.windowOpen', openWindowCommand); -CommandsRegistry.registerCommand('workbench.action.files.openFileInNewWindow', openFileInNewWindowCommand); const explorerCommandsWeightBonus = 10; // give our commands a little bit more weight over other default list/tree commands diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 5bfcb8ac062..e0847276fb3 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -44,9 +44,8 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; import { IEditorViewState } from 'vs/editor/common/editorCommon'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; -import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; import { withFocusedFilesExplorer, revealInOSCommand, revealInExplorerCommand, copyPathCommand } from 'vs/workbench/parts/files/browser/fileCommands'; -import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @@ -1824,27 +1823,6 @@ export class RefreshExplorerView extends Action { } } -export class OpenFileAction extends Action { - - static ID = 'workbench.action.files.openFile'; - static LABEL = nls.localize('openFile', "Open File..."); - - constructor( - id: string, - label: string, - @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IWindowService private windowService: IWindowService - ) { - super(id, label); - } - - run(event?: any, data?: ITelemetryData): TPromise { - const fileResource = toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); - - return this.windowService.pickFileAndOpen({ telemetryExtraData: data, dialogOptions: { defaultPath: fileResource ? paths.dirname(fileResource.fsPath) : void 0 } }); - } -} - export class ShowOpenedFileInNewWindow extends Action { public static ID = 'workbench.action.files.showOpenedFileInNewWindow'; diff --git a/src/vs/workbench/parts/files/browser/fileCommands.ts b/src/vs/workbench/parts/files/browser/fileCommands.ts index a3f1f860180..969405abdec 100644 --- a/src/vs/workbench/parts/files/browser/fileCommands.ts +++ b/src/vs/workbench/parts/files/browser/fileCommands.ts @@ -12,7 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import URI from 'vs/base/common/uri'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { toResource } from 'vs/workbench/common/editor'; -import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -50,26 +50,11 @@ export const copyPathCommand = (accessor: ServicesAccessor, resource?: URI) => { } }; -export const openFolderPickerCommand = (accessor: ServicesAccessor, forceNewWindow: boolean) => { - const windowService = accessor.get(IWindowService); - - windowService.pickFolderAndOpen({ forceNewWindow }); -}; - export const openWindowCommand = (accessor: ServicesAccessor, paths: string[], forceNewWindow: boolean) => { const windowsService = accessor.get(IWindowsService); windowsService.openWindow(paths, { forceNewWindow }); }; -export const openFileInNewWindowCommand = (accessor: ServicesAccessor) => { - const windowService = accessor.get(IWindowService); - const editorService = accessor.get(IWorkbenchEditorService); - - const fileResource = toResource(editorService.getActiveEditorInput(), { supportSideBySide: true, filter: 'file' }); - - windowService.pickFileAndOpen({ forceNewWindow: true, dialogOptions: { defaultPath: fileResource ? paths.dirname(fileResource.fsPath) : void 0 } }); -}; - export const revealInOSCommand = (accessor: ServicesAccessor, resource?: URI) => { // Without resource, try to look at the active editor diff --git a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts index 378e96b3648..81e366ea283 100644 --- a/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts +++ b/src/vs/workbench/parts/terminal/electron-browser/terminalInstance.ts @@ -592,7 +592,7 @@ export class TerminalInstance implements ITerminalInstance { if (!this._shellLaunchConfig.executable) { this._configHelper.mergeDefaultShellPathAndArgs(this._shellLaunchConfig); } - this._initialCwd = this._getCwd(this._shellLaunchConfig, this._historyService.getLastActiveWorkspaceRoot()); + this._initialCwd = this._getCwd(this._shellLaunchConfig, this._historyService.getLastActiveWorkspaceRoot('file')); let envFromConfig: IStringDictionary; if (platform.isWindows) { envFromConfig = { ...process.env }; diff --git a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts index 8acbac70b41..1031a79131b 100644 --- a/src/vs/workbench/parts/watermark/electron-browser/watermark.ts +++ b/src/vs/workbench/parts/watermark/electron-browser/watermark.ts @@ -19,8 +19,8 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { OpenRecentAction } from 'vs/workbench/electron-browser/actions'; -import { GlobalNewUntitledFileAction, OpenFileAction } from 'vs/workbench/parts/files/browser/fileActions'; -import { OpenFolderAction, OpenFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { GlobalNewUntitledFileAction } from 'vs/workbench/parts/files/browser/fileActions'; +import { OpenFolderAction, OpenFileFolderAction, OpenFileAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ShowAllCommandsAction } from 'vs/workbench/parts/quickopen/browser/commandsHandler'; import { Parts, IPartService } from 'vs/workbench/services/part/common/partService'; import { StartAction } from 'vs/workbench/parts/debug/browser/debugActions'; diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 0c40b27b376..b00ba458b71 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -10,7 +10,7 @@ import errors = require('vs/base/common/errors'); import URI from 'vs/base/common/uri'; import { IEditor } from 'vs/editor/common/editorCommon'; import { IEditor as IBaseEditor, IEditorInput, ITextEditorOptions, IResourceInput, ITextEditorSelection, Position as GroupPosition } from 'vs/platform/editor/common/editor'; -import { Extensions as EditorExtensions, EditorInput, IEditorCloseEvent, IEditorGroup, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; +import { Extensions as EditorExtensions, EditorInput, IEditorCloseEvent, IEditorGroup, IEditorInputFactoryRegistry, toResource } from 'vs/workbench/common/editor'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { FileChangesEvent, IFileService, FileChangeType, FILES_EXCLUDE_CONFIG } from 'vs/platform/files/common/files'; @@ -776,12 +776,25 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic }).filter(input => !!input); } - public getLastActiveWorkspaceRoot(): URI { + public getLastActiveWorkspaceRoot(schemeFilter?: string): URI { + + // No Folder: return early const folders = this.contextService.getWorkspace().folders; if (folders.length === 0) { return void 0; } + // Single Folder: return early + if (folders.length === 1) { + const resource = folders[0].uri; + if (!schemeFilter || resource.scheme === schemeFilter) { + return resource; + } + + return void 0; + } + + // Multiple folders: find the last active one const history = this.getHistory(); for (let i = 0; i < history.length; i++) { const input = history[i]; @@ -790,13 +803,44 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic } const resourceInput = input as IResourceInput; + if (schemeFilter && resourceInput.resource.scheme !== schemeFilter) { + continue; + } + const resourceWorkspace = this.contextService.getWorkspaceFolder(resourceInput.resource); if (resourceWorkspace) { return resourceWorkspace.uri; } } - // fallback to first workspace - return folders[0].uri; + // fallback to first workspace matching scheme filter if any + for (let i = 0; i < folders.length; i++) { + const resource = folders[i].uri; + if (!schemeFilter || resource.scheme === schemeFilter) { + return resource; + } + } + + return void 0; + } + + public getLastActiveFile(): URI { + const history = this.getHistory(); + for (let i = 0; i < history.length; i++) { + let resource: URI; + + const input = history[i]; + if (input instanceof EditorInput) { + resource = toResource(input, { filter: 'file' }); + } else { + resource = (input as IResourceInput).resource; + } + + if (resource && resource.scheme === 'file') { + return resource; + } + } + + return void 0; } } diff --git a/src/vs/workbench/services/history/common/history.ts b/src/vs/workbench/services/history/common/history.ts index 823f3f5f073..6760ab1c091 100644 --- a/src/vs/workbench/services/history/common/history.ts +++ b/src/vs/workbench/services/history/common/history.ts @@ -58,6 +58,13 @@ export interface IHistoryService { /** * Looking at the editor history, returns the workspace root of the last file that was * inside the workspace and part of the editor history. + * + * @param schemeFilter optional filter to restrict roots by scheme. */ - getLastActiveWorkspaceRoot(): URI; + getLastActiveWorkspaceRoot(schemeFilter?: string): URI; + + /** + * Looking at the editor history, returns the resource of the last file tht was opened. + */ + getLastActiveFile(): URI; } \ No newline at end of file diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 21b9c241b60..c185124e868 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -639,12 +639,14 @@ export abstract class TextFileService implements ITextFileService { } private suggestFileName(untitledResource: URI): string { - const root = this.historyService.getLastActiveWorkspaceRoot(); - if (root) { - return URI.file(paths.join(root.fsPath, this.untitledEditorService.suggestFileName(untitledResource))).fsPath; + const untitledFileName = this.untitledEditorService.suggestFileName(untitledResource); + + const lastActiveFile = this.historyService.getLastActiveFile(); + if (lastActiveFile) { + return URI.file(paths.join(paths.dirname(lastActiveFile.fsPath), untitledFileName)).fsPath; } - return this.untitledEditorService.suggestFileName(untitledResource); + return untitledFileName; } public revert(resource: URI, options?: IRevertOptions): TPromise { diff --git a/src/vs/workbench/services/textfile/electron-browser/textFileService.ts b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts index efff4d1b7f1..351ebb5541b 100644 --- a/src/vs/workbench/services/textfile/electron-browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/textFileService.ts @@ -142,9 +142,7 @@ export class TextFileService extends AbstractTextFileService { } private getSaveDialogOptions(defaultPath?: string): Electron.SaveDialogOptions { - const options: Electron.SaveDialogOptions = { - defaultPath: defaultPath - }; + const options: Electron.SaveDialogOptions = { defaultPath }; // Filters are only enabled on Windows where they work properly if (!isWindows) { @@ -154,7 +152,7 @@ export class TextFileService extends AbstractTextFileService { interface IFilter { name: string; extensions: string[]; } // Build the file filter by using our known languages - const ext: string = paths.extname(defaultPath); + const ext: string = defaultPath ? paths.extname(defaultPath) : void 0; let matchingFilter: IFilter; const filters: IFilter[] = this.modeService.getRegisteredLanguageNames().map(languageName => { const extensions = this.modeService.getExtensions(languageName); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 1fcdca0f09c..c8d9c81ce10 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -305,9 +305,13 @@ export class TestHistoryService implements IHistoryService { return []; } - public getLastActiveWorkspaceRoot(): URI { + public getLastActiveWorkspaceRoot(schemeFilter?: string): URI { return this.root; } + + public getLastActiveFile(): URI { + return void 0; + } } export class TestMessageService implements IMessageService { @@ -906,6 +910,10 @@ export class TestWindowService implements IWindowService { return TPromise.as(void 0); } + pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise { + return TPromise.as(void 0); + } + reloadWindow(): TPromise { return TPromise.as(void 0); } @@ -922,10 +930,6 @@ export class TestWindowService implements IWindowService { return TPromise.as(void 0); } - openWorkspace(): TPromise { - return TPromise.as(void 0); - } - createAndEnterWorkspace(folderPaths?: string[], path?: string): TPromise { return TPromise.as(void 0); } @@ -1058,6 +1062,10 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } + pickWorkspaceAndOpen(options: INativeOpenDialogOptions): TPromise { + return TPromise.as(void 0); + } + reloadWindow(windowId: number): TPromise { return TPromise.as(void 0); } @@ -1074,10 +1082,6 @@ export class TestWindowsService implements IWindowsService { return TPromise.as(void 0); } - openWorkspace(windowId: number): TPromise { - return TPromise.as(void 0); - } - createAndEnterWorkspace(windowId: number, folderPaths?: string[], path?: string): TPromise { return TPromise.as(void 0); }