提交 50dba370 编写于 作者: M Martin Aeschlimann

Support file-uris as arguments

上级 0218d1d3
......@@ -64,6 +64,7 @@ import { IMenubarService } from 'vs/platform/menubar/common/menubar';
import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService';
import { MenubarChannel } from 'vs/platform/menubar/common/menubarIpc';
import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay';
import { asArray } from 'vs/code/node/args';
export class CodeApplication {
......@@ -466,12 +467,16 @@ export class CodeApplication {
// Open our first window
const macOpenFiles = (<any>global).macOpenFiles as string[];
const context = !!process.env['VSCODE_CLI'] ? OpenContext.CLI : OpenContext.DESKTOP;
if (args['new-window'] && args._.length === 0 && (args['folder-uri'] || []).length === 0) {
const cliArgs = args._ || [];
const folderURIs = asArray(args['folder-uri']);
const fileURIs = asArray(args['file-uri']);
if (args['new-window'] && !cliArgs.length && !folderURIs.length && !fileURIs.length) {
this.windowsMainService.open({ context, cli: args, forceNewWindow: true, forceEmpty: true, initialStartup: true }); // new window if "-n" was used without paths
} else if (macOpenFiles && macOpenFiles.length && (!args._ || !args._.length || !args['folder-uri'] || !args['folder-uri'].length)) {
} else if (macOpenFiles && macOpenFiles.length && !cliArgs.length && !folderURIs.length && !fileURIs.length) {
this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, urisToOpen: macOpenFiles.map(file => URI.file(file)), initialStartup: true }); // mac: open-file event received on startup
} else {
this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!args._.length && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli
this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!cliArgs.length && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli
}
}
......
......@@ -20,6 +20,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import URI, { UriComponents } from 'vs/base/common/uri';
import { BrowserWindow } from 'electron';
import { Event } from 'vs/base/common/event';
import { asArray } from 'vs/code/node/args';
export const ID = 'launchService';
export const ILaunchService = createDecorator<ILaunchService>(ID);
......@@ -178,7 +179,7 @@ export class LaunchService implements ILaunchService {
}
// Start without file/folder arguments
else if (args._.length === 0 && (args['folder-uri'] || []).length === 0) {
else if (args._.length === 0 && !asArray(args['folder-uri'].length && !asArray(args['file-uri'].length))) {
let openNewWindow = false;
// Force new window
......
......@@ -527,18 +527,18 @@ export class Menubar {
}
}
private createOpenRecentMenuItem(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, commandId: string, isFile: boolean): Electron.MenuItem {
private createOpenRecentMenuItem(workspaceOrFile: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI, commandId: string, isFile: boolean): Electron.MenuItem {
let label: string;
let uri: URI;
if (isSingleFolderWorkspaceIdentifier(workspace)) {
label = unmnemonicLabel(getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService, { verbose: true }));
uri = workspace;
} else if (isWorkspaceIdentifier(workspace)) {
label = getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService, { verbose: true });
uri = URI.file(workspace.configPath);
if (isSingleFolderWorkspaceIdentifier(workspaceOrFile) && !isFile) {
label = unmnemonicLabel(getWorkspaceLabel(workspaceOrFile, this.environmentService, this.uriDisplayService, { verbose: true }));
uri = workspaceOrFile;
} else if (isWorkspaceIdentifier(workspaceOrFile)) {
label = getWorkspaceLabel(workspaceOrFile, this.environmentService, this.uriDisplayService, { verbose: true });
uri = URI.file(workspaceOrFile.configPath);
} else {
uri = URI.file(workspace);
label = unmnemonicLabel(this.uriDisplayService.getLabel(uri));
label = unmnemonicLabel(this.uriDisplayService.getLabel(workspaceOrFile));
uri = workspaceOrFile;
}
return new MenuItem(this.likeAction(commandId, {
......@@ -554,7 +554,7 @@ export class Menubar {
}).length > 0;
if (!success) {
this.historyMainService.removeFromRecentlyOpened([workspace]);
this.historyMainService.removeFromRecentlyOpened([workspaceOrFile]);
}
}
}, false));
......
......@@ -14,6 +14,7 @@ import { IBackupMainService } from 'vs/platform/backup/common/backup';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { IStateService } from 'vs/platform/state/common/state';
import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window';
import { asArray } from 'vs/code/node/args';
import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, app } from 'electron';
import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/paths';
import { ILifecycleService, UnloadReason, IWindowUnloadEvent } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
......@@ -365,8 +366,8 @@ export class WindowsManager implements IWindowsMainService {
pathsToOpen = pathsToOpen.filter(path => !path.folderUri);
}
let filesToOpen = pathsToOpen.filter(path => !!path.filePath && !path.createFilePath);
let filesToCreate = pathsToOpen.filter(path => !!path.filePath && path.createFilePath);
let filesToOpen = pathsToOpen.filter(path => !!path.fileUri && !path.createFilePath);
let filesToCreate = pathsToOpen.filter(path => !!path.fileUri && path.createFilePath);
// When run with --diff, take the files to open as files to diff
// if there are exactly two files provided.
......@@ -391,7 +392,7 @@ export class WindowsManager implements IWindowsMainService {
//
// These are windows to open to show either folders or files (including diffing files or creating them)
//
const foldersToOpen = arrays.distinct(pathsToOpen.filter(win => win.folderUri && !win.filePath).map(win => win.folderUri), folder => getComparisonKey(folder)); // prevent duplicates
const foldersToOpen = arrays.distinct(pathsToOpen.filter(win => win.folderUri && !win.fileUri).map(win => win.folderUri), folder => getComparisonKey(folder)); // prevent duplicates
//
// These are windows to restore because of hot-exit or from previous session (only performed once on startup!)
......@@ -413,7 +414,7 @@ export class WindowsManager implements IWindowsMainService {
//
// These are empty windows to open
//
const emptyToOpen = pathsToOpen.filter(win => !win.workspace && !win.folderUri && !win.filePath && !win.backupPath).length;
const emptyToOpen = pathsToOpen.filter(win => !win.workspace && !win.folderUri && !win.fileUri && !win.backupPath).length;
// Open based on config
const usedWindows = this.doOpen(openConfig, workspacesToOpen, workspacesToRestore, foldersToOpen, foldersToRestore, emptyToRestore, emptyToOpen, filesToOpen, filesToCreate, filesToDiff, filesToWait, foldersToAdd);
......@@ -421,7 +422,7 @@ export class WindowsManager implements IWindowsMainService {
// Make sure to pass focus to the most relevant of the windows if we open multiple
if (usedWindows.length > 1) {
let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !(openConfig.cli['folder-uri'] || []).length && !(openConfig.urisToOpen || []).length;
let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !openConfig.cli._.length && !asArray(openConfig.cli['file-uri']).length && !asArray(openConfig.cli['folder-uri']).length && !asArray(openConfig.urisToOpen).length;
let focusLastOpened = true;
let focusLastWindow = true;
......@@ -463,13 +464,13 @@ export class WindowsManager implements IWindowsMainService {
// Also do not add paths when files are opened for diffing, only if opened individually
if (!usedWindows.some(w => w.isExtensionDevelopmentHost) && !openConfig.cli.diff) {
const recentlyOpenedWorkspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[] = [];
const recentlyOpenedFiles: string[] = [];
const recentlyOpenedFiles: URI[] = [];
pathsToOpen.forEach(win => {
if (win.workspace || win.folderUri) {
recentlyOpenedWorkspaces.push(win.workspace || win.folderUri);
} else if (win.filePath) {
recentlyOpenedFiles.push(win.filePath);
} else if (win.fileUri) {
recentlyOpenedFiles.push(win.fileUri);
}
});
......@@ -539,7 +540,7 @@ export class WindowsManager implements IWindowsMainService {
newWindow: openFilesInNewWindow,
reuseWindow: openConfig.forceReuseWindow,
context: openConfig.context,
filePath: fileToCheck && fileToCheck.filePath,
fileUri: fileToCheck && fileToCheck.fileUri,
workspaceResolver: workspace => this.workspacesMainService.resolveWorkspaceSync(workspace.configPath)
});
......@@ -789,7 +790,7 @@ export class WindowsManager implements IWindowsMainService {
}
// Extract paths: from CLI
else if (openConfig.cli._.length > 0 || (openConfig.cli['folder-uri'] || []).length > 0) {
else if (openConfig.cli._.length > 0 || asArray(openConfig.cli['folder-uri']).length > 0 || asArray(openConfig.cli['file-uri']).length > 0) {
windowsToOpen = this.doExtractPathsFromCLI(openConfig.cli);
isCommandLineOrAPICall = true;
}
......@@ -819,7 +820,7 @@ export class WindowsManager implements IWindowsMainService {
private doExtractPathsFromAPI(openConfig: IOpenConfiguration): IPath[] {
let pathsToOpen = openConfig.urisToOpen.map(pathToOpen => {
const path = this.parseUri(pathToOpen, { gotoLineMode: openConfig.cli && openConfig.cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile });
const path = this.parseUri(pathToOpen, openConfig.forceOpenWorkspaceAsFile, { gotoLineMode: openConfig.cli && openConfig.cli.goto, forceOpenWorkspaceAsFile: openConfig.forceOpenWorkspaceAsFile });
// Warn if the requested path to open does not exist
if (!path) {
......@@ -848,10 +849,15 @@ export class WindowsManager implements IWindowsMainService {
const pathsToOpen = [];
// folder uris
if (cli['folder-uri'] && cli['folder-uri'].length) {
const arg = cli['folder-uri'];
const folderUris: string[] = typeof arg === 'string' ? [arg] : arg;
pathsToOpen.push(...arrays.coalesce(folderUris.map(candidate => this.parseUri(this.parseFolderUriArg(candidate), { ignoreFileNotFound: true, gotoLineMode: cli.goto }))));
const folderUris = asArray(cli['folder-uri']);
if (folderUris.length) {
pathsToOpen.push(...arrays.coalesce(folderUris.map(candidate => this.parseUri(this.parseUriArg(candidate), false, { ignoreFileNotFound: true, gotoLineMode: cli.goto }))));
}
// file uris
const fileUris = asArray(cli['file-uri']);
if (fileUris.length) {
pathsToOpen.push(...arrays.coalesce(fileUris.map(candidate => this.parseUri(this.parseUriArg(candidate), true, { ignoreFileNotFound: true, gotoLineMode: cli.goto }))));
}
// folder or file paths
......@@ -892,7 +898,7 @@ export class WindowsManager implements IWindowsMainService {
// folder (if path is valid)
else if (lastActiveWindow.folderUri) {
const validatedFolder = this.parseUri(lastActiveWindow.folderUri);
const validatedFolder = this.parseUri(lastActiveWindow.folderUri, false);
if (validatedFolder && validatedFolder.folderUri) {
return [validatedFolder];
}
......@@ -923,7 +929,7 @@ export class WindowsManager implements IWindowsMainService {
if (lastActiveWindow && lastActiveWindow.folderUri) {
folderCandidates.push(lastActiveWindow.folderUri);
}
windowsToOpen.push(...folderCandidates.map(candidate => this.parseUri(candidate)).filter(window => window && window.folderUri));
windowsToOpen.push(...folderCandidates.map(candidate => this.parseUri(candidate, false)).filter(window => window && window.folderUri));
// Windows that were Empty
if (restoreWindows === 'all') {
......@@ -963,7 +969,7 @@ export class WindowsManager implements IWindowsMainService {
return restoreWindows;
}
private parseFolderUriArg(arg: string): URI {
private parseUriArg(arg: string): URI {
// Do not support if user has passed folder path on Windows
if (isWindows && /^([a-z])\:(.*)$/i.test(arg)) {
return null;
......@@ -971,7 +977,7 @@ export class WindowsManager implements IWindowsMainService {
return URI.parse(arg);
}
private parseUri(anyUri: URI, options?: { ignoreFileNotFound?: boolean, gotoLineMode?: boolean, forceOpenWorkspaceAsFile?: boolean; }): IPathToOpen {
private parseUri(anyUri: URI, isFile: boolean, options?: { ignoreFileNotFound?: boolean, gotoLineMode?: boolean, forceOpenWorkspaceAsFile?: boolean; }): IPathToOpen {
if (!anyUri || !anyUri.scheme) {
return null;
}
......@@ -979,7 +985,11 @@ export class WindowsManager implements IWindowsMainService {
if (anyUri.scheme === Schemas.file) {
return this.parsePath(anyUri.fsPath, options);
}
if (isFile) {
return {
fileUri: anyUri
};
}
return {
folderUri: anyUri
};
......@@ -1014,7 +1024,7 @@ export class WindowsManager implements IWindowsMainService {
// File
return {
filePath: candidate,
fileUri: URI.file(candidate),
lineNumber: gotoLineMode ? parsedPath.line : void 0,
columnNumber: gotoLineMode ? parsedPath.column : void 0
};
......@@ -1030,10 +1040,11 @@ export class WindowsManager implements IWindowsMainService {
}
}
} catch (error) {
this.historyMainService.removeFromRecentlyOpened([candidate]); // since file does not seem to exist anymore, remove from recent
const fileUri = URI.file(candidate);
this.historyMainService.removeFromRecentlyOpened([fileUri]); // since file does not seem to exist anymore, remove from recent
if (options && options.ignoreFileNotFound) {
return { filePath: candidate, createFilePath: true }; // assume this is a file that does not yet exist
return { fileUri, createFilePath: true }; // assume this is a file that does not yet exist
}
}
......@@ -1093,38 +1104,46 @@ export class WindowsManager implements IWindowsMainService {
return;
}
let folderUris = asArray(openConfig.cli['folder-uri']);
let fileUris = asArray(openConfig.cli['file-uri']);
let cliArgs = openConfig.cli._;
// Fill in previously opened workspace unless an explicit path is provided and we are not unit testing
if (openConfig.cli._.length === 0 && (openConfig.cli['folder-uri'] || []).length === 0 && !openConfig.cli.extensionTestsPath) {
if (!cliArgs.length && !folderUris.length && !fileUris.length && !openConfig.cli.extensionTestsPath) {
const extensionDevelopmentWindowState = this.windowsState.lastPluginDevelopmentHostWindow;
const workspaceToOpen = extensionDevelopmentWindowState && (extensionDevelopmentWindowState.workspace || extensionDevelopmentWindowState.folderUri);
if (workspaceToOpen) {
if (isSingleFolderWorkspaceIdentifier(workspaceToOpen)) {
if (workspaceToOpen.scheme === Schemas.file) {
openConfig.cli._ = [workspaceToOpen.fsPath];
cliArgs = [workspaceToOpen.fsPath];
} else {
openConfig.cli['folder-uri'] = [workspaceToOpen.toString()];
folderUris = [workspaceToOpen.toString()];
}
} else {
openConfig.cli._ = [workspaceToOpen.configPath];
cliArgs = [workspaceToOpen.configPath];
}
}
}
// Make sure we are not asked to open a workspace or folder that is already opened
if (openConfig.cli._.some(path => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, URI.file(path)))) {
openConfig.cli._ = [];
}
if (openConfig.cli['folder-uri']) {
const arg = openConfig.cli['folder-uri'];
const folderUris: string[] = typeof arg === 'string' ? [arg] : arg;
if (folderUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.parseFolderUriArg(uri)))) {
openConfig.cli['folder-uri'] = [];
}
if (cliArgs.length && cliArgs.some(path => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, URI.file(path)))) {
cliArgs = [];
}
if (folderUris.length && folderUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.parseUriArg(uri)))) {
folderUris = [];
}
if (fileUris.length && fileUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.parseUriArg(uri)))) {
fileUris = [];
}
openConfig.cli._ = cliArgs;
openConfig.cli['folder-uri'] = folderUris;
openConfig.cli['file-uri'] = fileUris;
// Open it
this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: openConfig.cli._.length === 0 && (openConfig.cli['folder-uri'] || []).length === 0, userEnv: openConfig.userEnv });
this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: !cliArgs.length && !folderUris.length && !fileUris.length, userEnv: openConfig.userEnv });
}
private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
/**
* Converts an arument into to an array
* @param arg a argument value. Can be undefined, en entry or an array
*/
export function asArray<T>(arg: T | T[] | undefined): T[] {
if (arg) {
if (Array.isArray(arg)) {
return arg;
}
return [arg];
}
return [];
}
\ No newline at end of file
......@@ -9,14 +9,13 @@ import * as platform from 'vs/base/common/platform';
import * as paths from 'vs/base/common/paths';
import { OpenContext } from 'vs/platform/windows/common/windows';
import { IWorkspaceIdentifier, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { Schemas } from 'vs/base/common/network';
import URI from 'vs/base/common/uri';
import { hasToIgnoreCase, isEqual } from 'vs/base/common/resources';
import { hasToIgnoreCase, isEqual, isEqualOrParent } from 'vs/base/common/resources';
export interface ISimpleWindow {
openedWorkspace?: IWorkspaceIdentifier;
openedFolderUri?: URI;
openedFilePath?: string;
openedFileUri?: URI;
extensionDevelopmentPath?: string;
lastFocusTime: number;
}
......@@ -26,15 +25,15 @@ export interface IBestWindowOrFolderOptions<W extends ISimpleWindow> {
newWindow: boolean;
reuseWindow: boolean;
context: OpenContext;
filePath?: string;
fileUri?: URI;
userHome?: string;
codeSettingsFolder?: string;
workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace;
}
export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows, newWindow, reuseWindow, context, filePath, workspaceResolver }: IBestWindowOrFolderOptions<W>): W {
if (!newWindow && filePath && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) {
const windowOnFilePath = findWindowOnFilePath(windows, filePath, workspaceResolver);
export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows, newWindow, reuseWindow, context, fileUri, workspaceResolver }: IBestWindowOrFolderOptions<W>): W {
if (!newWindow && fileUri && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) {
const windowOnFilePath = findWindowOnFilePath(windows, fileUri, workspaceResolver);
if (windowOnFilePath) {
return windowOnFilePath;
}
......@@ -43,20 +42,20 @@ export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows
return !newWindow ? getLastActiveWindow(windows) : null;
}
function findWindowOnFilePath<W extends ISimpleWindow>(windows: W[], filePath: string, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace): W {
function findWindowOnFilePath<W extends ISimpleWindow>(windows: W[], fileUri: URI, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace): W {
// First check for windows with workspaces that have a parent folder of the provided path opened
const workspaceWindows = windows.filter(window => !!window.openedWorkspace);
for (let i = 0; i < workspaceWindows.length; i++) {
const window = workspaceWindows[i];
const resolvedWorkspace = workspaceResolver(window.openedWorkspace);
if (resolvedWorkspace && resolvedWorkspace.folders.some(folder => folder.uri.scheme === Schemas.file && paths.isEqualOrParent(filePath, folder.uri.fsPath, !platform.isLinux /* ignorecase */))) {
if (resolvedWorkspace && resolvedWorkspace.folders.some(folder => isEqualOrParent(fileUri, folder.uri, hasToIgnoreCase(fileUri)))) {
return window;
}
}
// Then go with single folder windows that are parent of the provided file path
const singleFolderWindowsOnFilePath = windows.filter(window => window.openedFolderUri && window.openedFolderUri.scheme === Schemas.file && paths.isEqualOrParent(filePath, window.openedFolderUri.fsPath, !platform.isLinux /* ignorecase */));
const singleFolderWindowsOnFilePath = windows.filter(window => window.openedFolderUri && isEqualOrParent(fileUri, window.openedFolderUri, hasToIgnoreCase(fileUri)));
if (singleFolderWindowsOnFilePath.length) {
return singleFolderWindowsOnFilePath.sort((a, b) => -(a.openedFolderUri.path.length - b.openedFolderUri.path.length))[0];
}
......
......@@ -45,32 +45,32 @@ suite('WindowsFinder', () => {
test('New window without folder when no windows exist', () => {
assert.equal(findBestWindowOrFolderForFile(options()), null);
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'))
})), null);
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')),
newWindow: true
})), null);
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')),
reuseWindow: true
})), null);
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')),
context: OpenContext.API
})), null);
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt')
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt'))
})), null);
assert.equal(findBestWindowOrFolderForFile(options({
filePath: path.join(fixturesFolder, 'vscode_folder', 'new_folder', 'new_file.txt')
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'new_folder', 'new_file.txt'))
})), null);
});
test('New window without folder when windows exist', () => {
assert.equal(findBestWindowOrFolderForFile(options({
windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'),
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')),
newWindow: true
})), null);
});
......@@ -81,16 +81,16 @@ suite('WindowsFinder', () => {
})), lastActiveWindow);
assert.equal(findBestWindowOrFolderForFile(options({
windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder2', 'file.txt')
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder2', 'file.txt'))
})), lastActiveWindow);
assert.equal(findBestWindowOrFolderForFile(options({
windows: [lastActiveWindow, noVscodeFolderWindow],
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt'),
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')),
reuseWindow: true
})), lastActiveWindow);
assert.equal(findBestWindowOrFolderForFile(options({
windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'),
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')),
context: OpenContext.API
})), lastActiveWindow);
});
......@@ -98,16 +98,16 @@ suite('WindowsFinder', () => {
test('Existing window with folder', () => {
assert.equal(findBestWindowOrFolderForFile(options({
windows,
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'file.txt')
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'file.txt'))
})), noVscodeFolderWindow);
assert.equal(findBestWindowOrFolderForFile(options({
windows,
filePath: path.join(fixturesFolder, 'vscode_folder', 'file.txt')
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt'))
})), vscodeFolderWindow);
const window: ISimpleWindow = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'nested_folder')) };
assert.equal(findBestWindowOrFolderForFile(options({
windows: [window],
filePath: path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt')
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'nested_folder', 'subfolder', 'file.txt'))
})), window);
});
......@@ -116,7 +116,7 @@ suite('WindowsFinder', () => {
const nestedFolderWindow: ISimpleWindow = { lastFocusTime: 1, openedFolderUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder')) };
assert.equal(findBestWindowOrFolderForFile(options({
windows: [window, nestedFolderWindow],
filePath: path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder', 'subfolder', 'file.txt')
fileUri: URI.file(path.join(fixturesFolder, 'no_vscode_folder', 'nested_folder', 'subfolder', 'file.txt'))
})), nestedFolderWindow);
});
......@@ -124,7 +124,7 @@ suite('WindowsFinder', () => {
const window: ISimpleWindow = { lastFocusTime: 1, openedWorkspace: testWorkspace };
assert.equal(findBestWindowOrFolderForFile(options({
windows: [window],
filePath: path.join(fixturesFolder, 'vscode_workspace_2_folder', 'nested_vscode_folder', 'subfolder', 'file.txt')
fileUri: URI.file(path.join(fixturesFolder, 'vscode_workspace_2_folder', 'nested_vscode_folder', 'subfolder', 'file.txt'))
})), window);
});
});
......@@ -9,6 +9,7 @@ export interface ParsedArgs {
[arg: string]: any;
_: string[];
'folder-uri'?: string | string[];
'file-uri'?: string | string[];
_urls?: string[];
help?: boolean;
version?: boolean;
......
......@@ -19,6 +19,7 @@ const options: minimist.Opts = {
'user-data-dir',
'extensions-dir',
'folder-uri',
'file-uri',
'extensionDevelopmentPath',
'extensionTestsPath',
'install-extension',
......@@ -145,7 +146,6 @@ export function parseArgs(args: string[]): ParsedArgs {
const optionsHelp: { [name: string]: string; } = {
'-d, --diff <file> <file>': localize('diff', "Compare two files with each other."),
'--folder-uri <uri>': localize('folder uri', "Opens a window with given folder uri(s)"),
'-a, --add <dir>': localize('add', "Add folder(s) to the last active window."),
'-g, --goto <file:line[:character]>': localize('goto', "Open a file at the path on the specified line and character position."),
'-n, --new-window': localize('newWindow', "Force to open a new window."),
......@@ -154,7 +154,9 @@ const optionsHelp: { [name: string]: string; } = {
'--locale <locale>': localize('locale', "The locale to use (e.g. en-US or zh-TW)."),
'--user-data-dir <dir>': localize('userDataDir', "Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code."),
'-v, --version': localize('version', "Print version."),
'-h, --help': localize('help', "Print usage.")
'-h, --help': localize('help', "Print usage."),
'--folder-uri <uri>': localize('folder uri', "Opens a window with given folder uri(s)"),
'--file-uri <uri>': localize('file uri', "Opens a window with given file uri(s)")
};
const extensionsHelp: { [name: string]: string; } = {
......
......@@ -9,12 +9,13 @@ import { IPath } from 'vs/platform/windows/common/windows';
import { Event as CommonEvent } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import URI from 'vs/base/common/uri';
export const IHistoryMainService = createDecorator<IHistoryMainService>('historyMainService');
export interface IRecentlyOpened {
workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[];
files: string[];
files: URI[];
}
export interface IHistoryMainService {
......@@ -22,9 +23,9 @@ export interface IHistoryMainService {
onRecentlyOpenedChange: CommonEvent<void>;
addRecentlyOpened(workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], files: string[]): void;
addRecentlyOpened(workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], files: URI[]): void;
getRecentlyOpened(currentWorkspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier, currentFiles?: IPath[]): IRecentlyOpened;
removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): void;
removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[]): void;
clearRecentlyOpened(): void;
updateWindowsJumpList(): void;
......
......@@ -7,7 +7,7 @@
import * as nls from 'vs/nls';
import * as arrays from 'vs/base/common/arrays';
import { trim } from 'vs/base/common/strings';
import { trim, startsWith } from 'vs/base/common/strings';
import { IStateService } from 'vs/platform/state/common/state';
import { app } from 'electron';
import { ILogService } from 'vs/platform/log/common/log';
......@@ -27,7 +27,7 @@ import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay';
interface ISerializedRecentlyOpened {
workspaces: (IWorkspaceIdentifier | string | UriComponents)[];
files: string[];
files: (string | UriComponents)[];
}
export class HistoryMainService implements IHistoryMainService {
......@@ -66,7 +66,7 @@ export class HistoryMainService implements IHistoryMainService {
this.addRecentlyOpened([e.workspace], []);
}
addRecentlyOpened(workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], files: string[]): void {
addRecentlyOpened(workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], files: URI[]): void {
if ((workspaces && workspaces.length > 0) || (files && files.length > 0)) {
const mru = this.getRecentlyOpened();
......@@ -88,13 +88,13 @@ export class HistoryMainService implements IHistoryMainService {
// Files
if (Array.isArray(files)) {
files.forEach((path) => {
mru.files.unshift(path);
files.forEach((fileUri) => {
mru.files.unshift(fileUri);
mru.files = arrays.distinct(mru.files, file => this.distinctFn(file));
// Add to recent documents (Windows only, macOS later)
if (isWindows) {
app.addRecentDocument(path);
if (isWindows && fileUri.scheme === Schemas.file) {
app.addRecentDocument(fileUri.fsPath);
}
});
}
......@@ -113,7 +113,7 @@ export class HistoryMainService implements IHistoryMainService {
}
}
removeFromRecentlyOpened(pathsToRemove: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): void {
removeFromRecentlyOpened(pathsToRemove: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[]): void {
const mru = this.getRecentlyOpened();
let update = false;
......@@ -143,7 +143,10 @@ export class HistoryMainService implements IHistoryMainService {
}
// Remove file
index = arrays.firstIndex(mru.files, file => typeof pathToRemove === 'string' && isEqual(file, pathToRemove, !isLinux /* ignorecase */));
const pathToRemoveURI = pathToRemove instanceof URI ? pathToRemove : typeof pathToRemove === 'string' ? URI.file(pathToRemove) : null;
if (pathToRemoveURI) {
index = arrays.firstIndex(mru.files, file => areResourcesEqual(file, pathToRemoveURI, hasToIgnoreCase(pathToRemoveURI)));
}
if (index >= 0) {
mru.files.splice(index, 1);
update = true;
......@@ -178,17 +181,27 @@ export class HistoryMainService implements IHistoryMainService {
let maxEntries = HistoryMainService.MAX_MACOS_DOCK_RECENT_ENTRIES;
// Take up to maxEntries/2 workspaces
const workspaces = mru.workspaces.filter(w => !(isSingleFolderWorkspaceIdentifier(w) && w.scheme !== Schemas.file));
for (let i = 0; i < workspaces.length && i < HistoryMainService.MAX_MACOS_DOCK_RECENT_ENTRIES / 2; i++) {
const workspace = workspaces[i];
app.addRecentDocument(isSingleFolderWorkspaceIdentifier(workspace) ? workspace.scheme === Schemas.file ? workspace.fsPath : workspace.toString() : workspace.configPath);
maxEntries--;
let nEntries = 0;
for (let i = 0; i < mru.workspaces.length && nEntries < HistoryMainService.MAX_MACOS_DOCK_RECENT_ENTRIES / 2; i++) {
const workspace = mru.workspaces[i];
if (isSingleFolderWorkspaceIdentifier(workspace)) {
if (workspace.scheme === Schemas.file) {
app.addRecentDocument(workspace.fsPath);
nEntries++;
}
} else {
app.addRecentDocument(workspace.configPath);
nEntries++;
}
}
// Take up to maxEntries files
for (let i = 0; i < mru.files.length && i < maxEntries; i++) {
for (let i = 0; i < mru.files.length && nEntries < maxEntries; i++) {
const file = mru.files[i];
app.addRecentDocument(file);
if (file.scheme === Schemas.file) {
app.addRecentDocument(file.fsPath);
nEntries++;
}
}
}
......@@ -202,7 +215,7 @@ export class HistoryMainService implements IHistoryMainService {
getRecentlyOpened(currentWorkspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier, currentFiles?: IPath[]): IRecentlyOpened {
let workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[];
let files: string[];
let files: URI[];
// Get from storage
const storedRecents = this.getRecentlyOpenedFromStorage();
......@@ -221,7 +234,7 @@ export class HistoryMainService implements IHistoryMainService {
// Add currently files to open to the beginning if any
if (currentFiles) {
files.unshift(...currentFiles.map(f => f.filePath));
files.unshift(...currentFiles.map(f => f.fileUri));
}
// Clear those dupes
......@@ -234,22 +247,19 @@ export class HistoryMainService implements IHistoryMainService {
return { workspaces, files };
}
private distinctFn(workspaceOrFile: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string): string {
if (isSingleFolderWorkspaceIdentifier(workspaceOrFile)) {
private distinctFn(workspaceOrFile: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI): string {
if (workspaceOrFile instanceof URI) {
return getComparisonKey(workspaceOrFile);
}
if (typeof workspaceOrFile === 'string') {
return isLinux ? workspaceOrFile : workspaceOrFile.toLowerCase();
}
return workspaceOrFile.id;
}
private getRecentlyOpenedFromStorage(): IRecentlyOpened {
const storedRecents: ISerializedRecentlyOpened = this.stateService.getItem<ISerializedRecentlyOpened>(HistoryMainService.recentlyOpenedStorageKey) || { workspaces: [], files: [] };
const result: IRecentlyOpened = { workspaces: [], files: storedRecents.files };
const result: IRecentlyOpened = { workspaces: [], files: [] };
for (const workspace of storedRecents.workspaces) {
if (typeof workspace === 'string') {
// folder paths were strings <= 1.25
result.workspaces.push(URI.file(workspace));
} else if (isWorkspaceIdentifier(workspace)) {
result.workspaces.push(workspace);
......@@ -257,11 +267,19 @@ export class HistoryMainService implements IHistoryMainService {
result.workspaces.push(URI.revive(workspace));
}
}
for (const file of storedRecents.files) {
if (typeof file === 'string') {
// file paths were strings <= 1.25
result.workspaces.push(URI.file(file));
} else {
result.workspaces.push(URI.revive(file));
}
}
return result;
}
private saveRecentlyOpened(recent: IRecentlyOpened): void {
const serialized: ISerializedRecentlyOpened = { workspaces: [], files: recent.files };
const serialized: ISerializedRecentlyOpened = { workspaces: [], files: [] };
for (const workspace of recent.workspaces) {
if (isSingleFolderWorkspaceIdentifier(workspace)) {
serialized.workspaces.push(<UriComponents>workspace.toJSON());
......@@ -269,6 +287,9 @@ export class HistoryMainService implements IHistoryMainService {
serialized.workspaces.push(workspace);
}
}
for (const file of recent.files) {
serialized.workspaces.push(<UriComponents>file.toJSON());
}
this.stateService.setItem(HistoryMainService.recentlyOpenedStorageKey, recent);
}
......@@ -302,7 +323,19 @@ export class HistoryMainService implements IHistoryMainService {
// so we need to update our list of recent paths with the choice of the user to not add them again
// Also: Windows will not show our custom category at all if there is any entry which was removed
// by the user! See https://github.com/Microsoft/vscode/issues/15052
this.removeFromRecentlyOpened(app.getJumpListSettings().removedItems.filter(r => !!r.args).map(r => trim(r.args, '"')));
let toRemove: (ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier)[] = [];
for (let item of app.getJumpListSettings().removedItems) {
const args = item.args;
if (args) {
if (startsWith(args, '--folderUri')) {
toRemove.push(URI.parse(args.substring(13, args.length - 1)));
} else {
let configPath = trim(args, '"');
toRemove.push({ id: this.workspacesMainService.getWorkspaceId(configPath), configPath });
}
}
}
this.removeFromRecentlyOpened(toRemove);
// Add entries
jumpList.push({
......@@ -310,19 +343,15 @@ export class HistoryMainService implements IHistoryMainService {
name: nls.localize('recentFolders', "Recent Workspaces"),
items: this.getRecentlyOpened().workspaces.slice(0, 7 /* limit number of entries here */).map(workspace => {
const title = getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService);
const description = isSingleFolderWorkspaceIdentifier(workspace) ? nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), this.uriDisplayService.getLabel(dirname(workspace))) : nls.localize('codeWorkspace', "Code Workspace");
let description;
let args;
// use quotes to support paths with whitespaces
if (isSingleFolderWorkspaceIdentifier(workspace)) {
if (workspace.scheme === Schemas.file) {
args = `"${workspace.fsPath}"`;
} else {
args = `--folderUri "${workspace.path}"`;
}
description = nls.localize('folderDesc', "{0} {1}", getBaseLabel(workspace), this.uriDisplayService.getLabel(dirname(workspace)));
args = `--folderUri "${workspace.toString()}"`;
} else {
description = nls.localize('codeWorkspace', "Code Workspace");
args = `"${workspace.configPath}"`;
}
return <Electron.JumpListItem>{
type: 'task',
title,
......
......@@ -126,8 +126,8 @@ export interface IWindowsService {
saveAndEnterWorkspace(windowId: number, path: string): TPromise<IEnterWorkspaceResult>;
toggleFullScreen(windowId: number): TPromise<void>;
setRepresentedFilename(windowId: number, fileName: string): TPromise<void>;
addRecentlyOpened(files: string[]): TPromise<void>;
removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise<void>;
addRecentlyOpened(files: URI[]): TPromise<void>;
removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[]): TPromise<void>;
clearRecentlyOpened(): TPromise<void>;
getRecentlyOpened(windowId: number): TPromise<IRecentlyOpened>;
focusWindow(windowId: number): TPromise<void>;
......@@ -295,7 +295,7 @@ export enum ReadyState {
export interface IPath {
// the file path to open within a Code instance
filePath?: string;
fileUri?: URI;
// the line number in the file path to open
lineNumber?: number;
......
......@@ -40,8 +40,8 @@ export interface IWindowsChannel extends IChannel {
call(command: 'saveAndEnterWorkspace', arg: [number, string]): TPromise<IEnterWorkspaceResult>;
call(command: 'toggleFullScreen', arg: number): TPromise<void>;
call(command: 'setRepresentedFilename', arg: [number, string]): TPromise<void>;
call(command: 'addRecentlyOpened', arg: string[]): TPromise<void>;
call(command: 'removeFromRecentlyOpened', arg: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise<void>;
call(command: 'addRecentlyOpened', arg: URI[]): TPromise<void>;
call(command: 'removeFromRecentlyOpened', arg: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI)[]): TPromise<void>;
call(command: 'clearRecentlyOpened'): TPromise<void>;
call(command: 'getRecentlyOpened', arg: number): TPromise<IRecentlyOpened>;
call(command: 'showPreviousWindowTab'): TPromise<void>;
......@@ -141,9 +141,9 @@ export class WindowsChannel implements IWindowsChannel {
case 'setRepresentedFilename': return this.service.setRepresentedFilename(arg[0], arg[1]);
case 'addRecentlyOpened': return this.service.addRecentlyOpened(arg);
case 'removeFromRecentlyOpened': {
let paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[] = arg;
let paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI)[] = arg;
if (Array.isArray(paths)) {
paths = paths.map(path => isWorkspaceIdentifier(path) || typeof path === 'string' ? path : URI.revive(path));
paths = paths.map(path => isWorkspaceIdentifier(path) ? path : URI.revive(path));
}
return this.service.removeFromRecentlyOpened(paths);
}
......@@ -262,11 +262,11 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('setRepresentedFilename', [windowId, fileName]);
}
addRecentlyOpened(files: string[]): TPromise<void> {
addRecentlyOpened(files: URI[]): TPromise<void> {
return this.channel.call('addRecentlyOpened', files);
}
removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise<void> {
removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI)[]): TPromise<void> {
return this.channel.call('removeFromRecentlyOpened', paths);
}
......
......@@ -226,14 +226,14 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
return TPromise.as(null);
}
addRecentlyOpened(files: string[]): TPromise<void> {
addRecentlyOpened(files: URI[]): TPromise<void> {
this.logService.trace('windowsService#addRecentlyOpened');
this.historyService.addRecentlyOpened(void 0, files);
return TPromise.as(null);
}
removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string)[]): TPromise<void> {
removeFromRecentlyOpened(paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[]): TPromise<void> {
this.logService.trace('windowsService#removeFromRecentlyOpened');
this.historyService.removeFromRecentlyOpened(paths);
......
......@@ -35,7 +35,6 @@ import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/co
import { Disposable } from 'vs/base/common/lifecycle';
import { addDisposableListener, EventType } from 'vs/base/browser/dom';
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay';
export interface IDraggedResource {
resource: URI;
......@@ -166,8 +165,7 @@ export class ResourcesDropHandler {
@IBackupFileService private backupFileService: IBackupFileService,
@IUntitledEditorService private untitledEditorService: IUntitledEditorService,
@IEditorService private editorService: IEditorService,
@IConfigurationService private configurationService: IConfigurationService,
@IUriDisplayService private uriDisplayService: IUriDisplayService
@IConfigurationService private configurationService: IConfigurationService
) {
}
......@@ -189,7 +187,7 @@ export class ResourcesDropHandler {
// Add external ones to recently open list unless dropped resource is a workspace
const filesToAddToHistory = untitledOrFileResources.filter(d => d.isExternal && d.resource.scheme === Schemas.file).map(d => d.resource);
if (filesToAddToHistory.length) {
this.windowsService.addRecentlyOpened(filesToAddToHistory.map(resource => this.uriDisplayService.getLabel(resource)));
this.windowsService.addRecentlyOpened(filesToAddToHistory);
}
const editors: IResourceEditor[] = untitledOrFileResources.map(untitledOrFileResource => ({
......
......@@ -491,19 +491,19 @@ export class MenubarPart extends Part {
return this.currentEnableMenuBarMnemonics ? label : label.replace(/&&(.)/g, '$1');
}
private createOpenRecentMenuAction(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, commandId: string, isFile: boolean): IAction {
private createOpenRecentMenuAction(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI, commandId: string, isFile: boolean): IAction {
let label: string;
let uri: URI;
if (isSingleFolderWorkspaceIdentifier(workspace)) {
if (isSingleFolderWorkspaceIdentifier(workspace) && !isFile) {
label = getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService, { verbose: true });
uri = workspace;
} else if (isWorkspaceIdentifier(workspace)) {
label = getWorkspaceLabel(workspace, this.environmentService, this.uriDisplayService, { verbose: true });
uri = URI.file(workspace.configPath);
} else {
uri = URI.file(workspace);
uri = workspace;
label = this.uriDisplayService.getLabel(uri);
}
......
......@@ -721,13 +721,13 @@ export abstract class BaseOpenRecentAction extends Action {
.then(({ workspaces, files }) => this.openRecent(workspaces, files));
}
private openRecent(recentWorkspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], recentFiles: string[]): void {
private openRecent(recentWorkspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[], recentFiles: URI[]): void {
function toPick(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string, separator: ISeparator, fileKind: FileKind, environmentService: IEnvironmentService, uriDisplayService: IUriDisplayService, action: IAction): IFilePickOpenEntry {
function toPick(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI, separator: ISeparator, fileKind: FileKind, environmentService: IEnvironmentService, uriDisplayService: IUriDisplayService, action: IAction): IFilePickOpenEntry {
let resource: URI;
let label: string;
let description: string;
if (isSingleFolderWorkspaceIdentifier(workspace)) {
if (isSingleFolderWorkspaceIdentifier(workspace) && fileKind !== FileKind.FILE) {
resource = workspace;
label = getWorkspaceLabel(workspace, environmentService, uriDisplayService);
description = uriDisplayService.getLabel(resource.with({ path: paths.dirname(resource.path) }));
......@@ -736,7 +736,7 @@ export abstract class BaseOpenRecentAction extends Action {
label = getWorkspaceLabel(workspace, environmentService, uriDisplayService);
description = uriDisplayService.getLabel(dirname(resource));
} else {
resource = URI.file(workspace);
resource = workspace;
label = getBaseLabel(workspace);
description = uriDisplayService.getLabel(dirname(resource));
}
......@@ -785,7 +785,7 @@ class RemoveFromRecentlyOpened extends Action implements IPickOpenAction {
static readonly LABEL = nls.localize('remove', "Remove from Recently Opened");
constructor(
private path: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | string),
private path: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI),
@IWindowsService private windowsService: IWindowsService
) {
super(RemoveFromRecentlyOpened.ID, RemoveFromRecentlyOpened.LABEL);
......
......@@ -19,7 +19,8 @@ import { range } from 'vs/base/common/arrays';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { InEditorZenModeContext, NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor';
import { ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import URI from 'vs/base/common/uri';
// --- List Commands
......@@ -550,7 +551,7 @@ export function registerCommands(): void {
win: { primary: void 0 }
});
CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: string | ISingleFolderWorkspaceIdentifier) {
CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string) {
const windowsService = accessor.get(IWindowsService);
return windowsService.removeFromRecentlyOpened([path]).then(() => void 0);
......
......@@ -401,7 +401,7 @@ export class ElectronWindow extends Themable {
// In wait mode, listen to changes to the editors and wait until the files
// are closed that the user wants to wait for. When this happens we delete
// the wait marker file to signal to the outside that editing is done.
const resourcesToWaitFor = request.filesToWait.paths.map(p => URI.file(p.filePath));
const resourcesToWaitFor = request.filesToWait.paths.map(p => p.fileUri);
const waitMarkerFile = URI.file(request.filesToWait.waitMarkerFilePath);
const unbind = this.editorService.onDidCloseEditor(() => {
if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) {
......@@ -432,7 +432,7 @@ export class ElectronWindow extends Themable {
private toInputs(paths: IPath[], isNew: boolean): IResourceEditor[] {
return paths.map(p => {
const resource = URI.file(p.filePath);
const resource = p.fileUri;
let input: IResourceInput | IUntitledResourceInput;
if (isNew) {
input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput;
......
......@@ -490,7 +490,7 @@ export class Workbench extends Disposable implements IPartService {
// Listen to editor closing (if we run with --wait)
const filesToWait = this.workbenchParams.configuration.filesToWait;
if (filesToWait) {
const resourcesToWaitFor = filesToWait.paths.map(p => URI.file(p.filePath));
const resourcesToWaitFor = filesToWait.paths.map(p => p.fileUri);
const waitMarkerFile = URI.file(filesToWait.waitMarkerFilePath);
const listenerDispose = this.editorService.onDidCloseEditor(() => this.onEditorClosed(listenerDispose, resourcesToWaitFor, waitMarkerFile));
......@@ -805,7 +805,7 @@ export class Workbench extends Disposable implements IPartService {
}
return paths.map(p => {
const resource = URI.file(p.filePath);
const resource = p.fileUri;
let input: IResourceInput | IUntitledResourceInput;
if (isNew) {
input = { filePath: resource.fsPath, options: { pinned: true } } as IUntitledResourceInput;
......
......@@ -356,11 +356,11 @@ export class WorkspaceStats implements IWorkbenchContribution {
private findFolder({ filesToOpen, filesToCreate, filesToDiff }: IWindowConfiguration): URI {
if (filesToOpen && filesToOpen.length) {
return this.parentURI(URI.file(filesToOpen[0].filePath));
return this.parentURI(filesToOpen[0].fileUri);
} else if (filesToCreate && filesToCreate.length) {
return this.parentURI(URI.file(filesToCreate[0].filePath));
return this.parentURI(filesToCreate[0].fileUri);
} else if (filesToDiff && filesToDiff.length) {
return this.parentURI(URI.file(filesToDiff[0].filePath));
return this.parentURI(filesToDiff[0].fileUri);
}
return undefined;
}
......
......@@ -258,7 +258,7 @@ class WelcomePage {
return this.editorService.openEditor(this.editorInput, { pinned: false });
}
private onReady(container: HTMLElement, recentlyOpened: TPromise<{ files: string[]; workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]; }>, installedExtensions: TPromise<IExtensionStatus[]>): void {
private onReady(container: HTMLElement, recentlyOpened: TPromise<{ files: URI[]; workspaces: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)[]; }>, installedExtensions: TPromise<IExtensionStatus[]>): void {
const enabled = isWelcomePageEnabled(this.configurationService);
const showOnStartup = <HTMLInputElement>container.querySelector('#showOnStartup');
if (enabled) {
......
......@@ -606,7 +606,7 @@ export class HistoryService extends Disposable implements IHistoryService {
const input = arg1 as IResourceInput;
this.windowService.removeFromRecentlyOpened([input.resource.fsPath]);
this.windowService.removeFromRecentlyOpened([input.resource]);
}
private isFileOpened(resource: URI, group: IEditorGroup): boolean {
......
......@@ -1199,11 +1199,11 @@ export class TestWindowsService implements IWindowsService {
return TPromise.as(void 0);
}
addRecentlyOpened(files: string[]): TPromise<void> {
addRecentlyOpened(files: URI[]): TPromise<void> {
return TPromise.as(void 0);
}
removeFromRecentlyOpened(paths: string[]): TPromise<void> {
removeFromRecentlyOpened(paths: URI[]): TPromise<void> {
return TPromise.as(void 0);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册