Skip to content

Commit 8c6e974

Browse files
committed
IWindowService.openWindow takes IURIToOpen
1 parent bbb102e commit 8c6e974

23 files changed

Lines changed: 140 additions & 122 deletions

File tree

src/vs/code/electron-main/app.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, powerMonitor } from 'electron';
77
import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform';
88
import { WindowsManager } from 'vs/code/electron-main/windows';
9-
import { IWindowsService, OpenContext, ActiveWindowManager } from 'vs/platform/windows/common/windows';
9+
import { IWindowsService, OpenContext, ActiveWindowManager, IURIToOpen } from 'vs/platform/windows/common/windows';
1010
import { WindowsChannel } from 'vs/platform/windows/node/windowsIpc';
1111
import { WindowsService } from 'vs/platform/windows/electron-main/windowsService';
1212
import { ILifecycleService, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
@@ -187,14 +187,14 @@ export class CodeApplication extends Disposable {
187187
});
188188
});
189189

190-
let macOpenFileURIs: URI[] = [];
190+
let macOpenFileURIs: IURIToOpen[] = [];
191191
let runningTimeout: any = null;
192192
app.on('open-file', (event: Event, path: string) => {
193193
this.logService.trace('App#open-file: ', path);
194194
event.preventDefault();
195195

196196
// Keep in array because more might come!
197-
macOpenFileURIs.push(URI.file(path));
197+
macOpenFileURIs.push({ uri: URI.file(path) });
198198

199199
// Clear previous handler if any
200200
if (runningTimeout !== null) {
@@ -606,7 +606,7 @@ export class CodeApplication extends Disposable {
606606
}
607607

608608
if (macOpenFiles && macOpenFiles.length && !hasCliArgs && !hasFolderURIs && !hasFileURIs) {
609-
return this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, urisToOpen: macOpenFiles.map(file => URI.file(file)), initialStartup: true }); // mac: open-file event received on startup
609+
return this.windowsMainService.open({ context: OpenContext.DOCK, cli: args, urisToOpen: macOpenFiles.map(file => ({ uri: URI.file(file) })), initialStartup: true }); // mac: open-file event received on startup
610610
}
611611

612612
return this.windowsMainService.open({ context, cli: args, forceNewWindow: args['new-window'] || (!hasCliArgs && args['unity-launch']), diffMode: args.diff, initialStartup: true }); // default: read paths from cli

src/vs/code/electron-main/windows.ts

Lines changed: 55 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { basename, normalize, join, dirname } from 'path';
6+
import { basename, normalize, join, dirname, extname } from 'path';
77
import * as fs from 'fs';
88
import { localize } from 'vs/nls';
99
import * as arrays from 'vs/base/common/arrays';
@@ -18,15 +18,15 @@ import { IPathWithLineAndColumn, parseLineAndColumnAware } from 'vs/code/node/pa
1818
import { ILifecycleService, UnloadReason, IWindowUnloadEvent, LifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
1919
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
2020
import { ILogService } from 'vs/platform/log/common/log';
21-
import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult, INewWindowOptions } from 'vs/platform/windows/common/windows';
21+
import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult, INewWindowOptions, IURIToOpen, URIType } from 'vs/platform/windows/common/windows';
2222
import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri } from 'vs/code/node/windowsFinder';
2323
import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
2424
import product from 'vs/platform/node/product';
2525
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
2626
import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows';
2727
import { IHistoryMainService } from 'vs/platform/history/common/history';
2828
import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
29-
import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
29+
import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
3030
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
3131
import { mnemonicButtonLabel } from 'vs/base/common/labels';
3232
import { Schemas } from 'vs/base/common/network';
@@ -118,10 +118,6 @@ interface IFileInputs {
118118
remoteAuthority?: string;
119119
}
120120

121-
enum URIType {
122-
FILE, FOLDER, WORKSPACE
123-
}
124-
125121
interface IPathToOpen extends IPath {
126122

127123
// the workspace for a Code instance to open
@@ -485,7 +481,7 @@ export class WindowsManager implements IWindowsMainService {
485481
// Make sure to pass focus to the most relevant of the windows if we open multiple
486482
if (usedWindows.length > 1) {
487483

488-
let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !hasArgs(openConfig.cli._) && !hasArgs(openConfig.cli['file-uri']) && !hasArgs(openConfig.cli['folder-uri']) && !hasArgs(openConfig.cli['workspace-uri']) && !(openConfig.urisToOpen && openConfig.urisToOpen.length);
484+
let focusLastActive = this.windowsState.lastActiveWindow && !openConfig.forceEmpty && !hasArgs(openConfig.cli._) && !hasArgs(openConfig.cli['file-uri']) && !hasArgs(openConfig.cli['folder-uri']) && !(openConfig.urisToOpen && openConfig.urisToOpen.length);
489485
let focusLastOpened = true;
490486
let focusLastWindow = true;
491487

@@ -847,7 +843,7 @@ export class WindowsManager implements IWindowsMainService {
847843
}
848844

849845
// Extract paths: from CLI
850-
else if (hasArgs(openConfig.cli._) || hasArgs(openConfig.cli['folder-uri']) || hasArgs(openConfig.cli['file-uri']) || hasArgs(openConfig.cli['workspace-uri'])) {
846+
else if (hasArgs(openConfig.cli._) || hasArgs(openConfig.cli['folder-uri']) || hasArgs(openConfig.cli['file-uri'])) {
851847
windowsToOpen = this.doExtractPathsFromCLI(openConfig.cli);
852848
isCommandLineOrAPICall = true;
853849
}
@@ -884,19 +880,19 @@ export class WindowsManager implements IWindowsMainService {
884880
continue;
885881
}
886882

887-
const path = this.parseUri(pathToOpen, openConfig.forceOpenWorkspaceAsFile ? URIType.FILE : URIType.FOLDER, parseOptions);
883+
const path = this.parseUri(pathToOpen.uri, pathToOpen.typeHint, parseOptions);
888884
if (path) {
889885
pathsToOpen.push(path);
890886
} else {
891887

892888
// Warn about the invalid URI or path
893889
let message, detail;
894-
if (pathToOpen.scheme === Schemas.file) {
890+
if (pathToOpen.uri.scheme === Schemas.file) {
895891
message = localize('pathNotExistTitle', "Path does not exist");
896-
detail = localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", pathToOpen.fsPath);
892+
detail = localize('pathNotExistDetail', "The path '{0}' does not seem to exist anymore on disk.", pathToOpen.uri.fsPath);
897893
} else {
898894
message = localize('uriInvalidTitle', "URI can not be opened");
899-
detail = localize('uriInvalidDetail', "The URI '{0}' is not valid and can not be opened.", pathToOpen.toString());
895+
detail = localize('uriInvalidDetail', "The URI '{0}' is not valid and can not be opened.", pathToOpen.uri.toString());
900896
}
901897
const options: Electron.MessageBoxOptions = {
902898
title: product.nameLong,
@@ -920,7 +916,7 @@ export class WindowsManager implements IWindowsMainService {
920916
// folder uris
921917
const folderUris = asArray(cli['folder-uri']);
922918
for (let folderUri of folderUris) {
923-
const path = this.parseUri(this.argToUri(folderUri), URIType.FOLDER, parseOptions);
919+
const path = this.parseUri(this.argToUri(folderUri), 'folder', parseOptions);
924920
if (path) {
925921
pathsToOpen.push(path);
926922
}
@@ -929,21 +925,12 @@ export class WindowsManager implements IWindowsMainService {
929925
// file uris
930926
const fileUris = asArray(cli['file-uri']);
931927
for (let fileUri of fileUris) {
932-
const path = this.parseUri(this.argToUri(fileUri), URIType.FILE, parseOptions);
933-
if (path) {
934-
pathsToOpen.push(path);
935-
}
936-
}
937-
938-
const workspaceUris = asArray(cli['workspace-uri']);
939-
for (let workspaceUri of workspaceUris) {
940-
const path = this.parseUri(this.argToUri(workspaceUri), URIType.WORKSPACE, parseOptions);
928+
const path = this.parseUri(this.argToUri(fileUri), 'file');
941929
if (path) {
942930
pathsToOpen.push(path);
943931
}
944932
}
945933

946-
947934
// folder or file paths
948935
const cliArgs = asArray(cli._);
949936
for (let cliArg of cliArgs) {
@@ -987,12 +974,12 @@ export class WindowsManager implements IWindowsMainService {
987974
const windowsToOpen: IPathToOpen[] = [];
988975
for (const openedWindow of openedWindows) {
989976
if (openedWindow.workspace) { // Workspaces
990-
const pathToOpen = this.parseUri(openedWindow.workspace.configPath, URIType.WORKSPACE, { remoteAuthority: openedWindow.remoteAuthority });
977+
const pathToOpen = this.parseUri(openedWindow.workspace.configPath, 'file', { remoteAuthority: openedWindow.remoteAuthority });
991978
if (pathToOpen && pathToOpen.workspace) {
992979
windowsToOpen.push(pathToOpen);
993980
}
994981
} else if (openedWindow.folderUri) { // Folders
995-
const pathToOpen = this.parseUri(openedWindow.folderUri, URIType.FOLDER, { remoteAuthority: openedWindow.remoteAuthority });
982+
const pathToOpen = this.parseUri(openedWindow.folderUri, 'folder', { remoteAuthority: openedWindow.remoteAuthority });
996983
if (pathToOpen && pathToOpen.folderUri) {
997984
windowsToOpen.push(pathToOpen);
998985
}
@@ -1042,7 +1029,7 @@ export class WindowsManager implements IWindowsMainService {
10421029
return null;
10431030
}
10441031

1045-
private parseUri(uri: URI, type: URIType, options?: IPathParseOptions): IPathToOpen | null {
1032+
private parseUri(uri: URI, typeHint?: URIType, options: IPathParseOptions = {}): IPathToOpen | null {
10461033
if (!uri || !uri.scheme) {
10471034
return null;
10481035
}
@@ -1051,16 +1038,31 @@ export class WindowsManager implements IWindowsMainService {
10511038
}
10521039

10531040
// open remote if either specified in the cli or if it's a remotehost URI
1054-
const remoteAuthority = options && options.remoteAuthority || getRemoteAuthority(uri);
1041+
const remoteAuthority = options.remoteAuthority || getRemoteAuthority(uri);
10551042

10561043
// normalize URI
10571044
uri = normalizePath(uri);
1045+
1046+
1047+
// remove trailing slash
10581048
const uriPath = uri.path;
1059-
if (uriPath.length > 2 && endsWith(uriPath, '/')) {
1060-
uri = uri.with({ path: uriPath.substr(0, uriPath.length - 1) });
1049+
if (endsWith(uriPath, '/')) {
1050+
if (uriPath.length > 2) {
1051+
// only remove if the path has some content
1052+
uri = uri.with({ path: uriPath.substr(0, uriPath.length - 1) });
1053+
}
1054+
if (!typeHint) {
1055+
typeHint = 'folder';
1056+
}
1057+
}
1058+
1059+
// if there's no type hint
1060+
if (!typeHint && (extname(uri.path) === WORKSPACE_EXTENSION || options.gotoLineMode)) {
1061+
typeHint = 'file';
10611062
}
1062-
if (type === URIType.FILE) {
1063-
if (options && options.gotoLineMode) {
1063+
1064+
if (typeHint === 'file') {
1065+
if (options.gotoLineMode) {
10641066
const parsedPath = parseLineAndColumnAware(uri.path);
10651067
return {
10661068
fileUri: uri.with({ path: parsedPath.path }),
@@ -1069,37 +1071,37 @@ export class WindowsManager implements IWindowsMainService {
10691071
remoteAuthority
10701072
};
10711073
}
1074+
if (extname(uri.path) === WORKSPACE_EXTENSION && !options.forceOpenWorkspaceAsFile) {
1075+
return {
1076+
workspace: this.workspacesMainService.getWorkspaceIdentifier(uri),
1077+
remoteAuthority
1078+
};
1079+
}
10721080
return {
10731081
fileUri: uri,
10741082
remoteAuthority
10751083
};
1076-
} else if (type === URIType.WORKSPACE) {
1077-
return {
1078-
workspace: this.workspacesMainService.getWorkspaceIdentifier(uri),
1079-
remoteAuthority
1080-
};
10811084
}
10821085
return {
10831086
folderUri: uri,
10841087
remoteAuthority
10851088
};
10861089
}
10871090

1088-
private parsePath(anyPath: string, options?: IPathParseOptions): IPathToOpen | null {
1091+
private parsePath(anyPath: string, options: IPathParseOptions): IPathToOpen | null {
10891092
if (!anyPath) {
10901093
return null;
10911094
}
10921095

10931096
let parsedPath: IPathWithLineAndColumn;
10941097

1095-
const gotoLineMode = options && options.gotoLineMode;
1096-
if (options && options.gotoLineMode) {
1098+
if (options.gotoLineMode) {
10971099
parsedPath = parseLineAndColumnAware(anyPath);
10981100
anyPath = parsedPath.path;
10991101
}
11001102

11011103
// open remote if either specified in the cli even if it is a local file. TODO: Future idea: resolve in remote host context.
1102-
const remoteAuthority = options && options.remoteAuthority;
1104+
const remoteAuthority = options.remoteAuthority;
11031105

11041106
const candidate = normalize(anyPath);
11051107
try {
@@ -1108,7 +1110,7 @@ export class WindowsManager implements IWindowsMainService {
11081110
if (candidateStat.isFile()) {
11091111

11101112
// Workspace (unless disabled via flag)
1111-
if (!options || !options.forceOpenWorkspaceAsFile) {
1113+
if (!options.forceOpenWorkspaceAsFile) {
11121114
const workspace = this.workspacesMainService.resolveWorkspaceSync(candidate);
11131115
if (workspace) {
11141116
return { workspace: { id: workspace.id, configPath: workspace.configPath }, remoteAuthority };
@@ -1118,8 +1120,8 @@ export class WindowsManager implements IWindowsMainService {
11181120
// File
11191121
return {
11201122
fileUri: URI.file(candidate),
1121-
lineNumber: gotoLineMode ? parsedPath.line : undefined,
1122-
columnNumber: gotoLineMode ? parsedPath.column : undefined,
1123+
lineNumber: options.gotoLineMode ? parsedPath.line : undefined,
1124+
columnNumber: options.gotoLineMode ? parsedPath.column : undefined,
11231125
remoteAuthority
11241126
};
11251127
}
@@ -1201,7 +1203,6 @@ export class WindowsManager implements IWindowsMainService {
12011203
}
12021204
let folderUris = asArray(openConfig.cli['folder-uri']);
12031205
let fileUris = asArray(openConfig.cli['file-uri']);
1204-
let workspaceUris = asArray(openConfig.cli['workspace-uri']);
12051206
let cliArgs = openConfig.cli._;
12061207

12071208
// Fill in previously opened workspace unless an explicit path is provided and we are not unit testing
@@ -1219,7 +1220,7 @@ export class WindowsManager implements IWindowsMainService {
12191220
if (workspaceToOpen.configPath.scheme === Schemas.file) {
12201221
cliArgs = [fsPath(workspaceToOpen.configPath)];
12211222
} else {
1222-
workspaceUris = [workspaceToOpen.configPath.toString()];
1223+
fileUris = [workspaceToOpen.configPath.toString()];
12231224
}
12241225
}
12251226
}
@@ -1238,17 +1239,12 @@ export class WindowsManager implements IWindowsMainService {
12381239
fileUris = [];
12391240
}
12401241

1241-
if (workspaceUris.length && workspaceUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.argToUri(uri)))) {
1242-
workspaceUris = [];
1243-
}
1244-
12451242
openConfig.cli._ = cliArgs;
12461243
openConfig.cli['folder-uri'] = folderUris;
12471244
openConfig.cli['file-uri'] = fileUris;
1248-
openConfig.cli['workspace-uri'] = workspaceUris;
12491245

12501246
// Open it
1251-
this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: !cliArgs.length && !folderUris.length && !fileUris.length && !workspaceUris.length, userEnv: openConfig.userEnv });
1247+
this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: !cliArgs.length && !folderUris.length && !fileUris.length, userEnv: openConfig.userEnv });
12521248
}
12531249

12541250
private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow {
@@ -1897,7 +1893,7 @@ class Dialogs {
18971893
});
18981894
}
18991895

1900-
private getFileOrFolderUris(options: IInternalNativeOpenDialogOptions): Promise<URI[]> {
1896+
private getFileOrFolderUris(options: IInternalNativeOpenDialogOptions): Promise<IURIToOpen[]> {
19011897

19021898
// Ensure dialog options
19031899
if (!options.dialogOptions) {
@@ -1935,7 +1931,12 @@ class Dialogs {
19351931
// Remember path in storage for next time
19361932
this.stateService.setItem(Dialogs.workingDirPickerStorageKey, dirname(paths[0]));
19371933

1938-
return paths.map(path => URI.file(path));
1934+
const result: IURIToOpen[] = [];
1935+
for (const path of paths) {
1936+
result.push({ uri: URI.file(path) });
1937+
}
1938+
1939+
return result;
19391940
}
19401941

19411942
return undefined;

src/vs/platform/environment/common/environment.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ export interface ParsedArgs {
1111
_: string[];
1212
'folder-uri'?: string | string[];
1313
'file-uri'?: string | string[];
14-
'workspace-uri'?: string | string[];
1514
_urls?: string[];
1615
help?: boolean;
1716
version?: boolean;

src/vs/platform/environment/node/argv.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ export const options: Option[] = [
4040
{ id: 'help', type: 'boolean', cat: 'o', alias: 'h', description: localize('help', "Print usage.") },
4141
{ id: 'folder-uri', type: 'string', cat: 'o', args: 'uri', description: localize('folderUri', "Opens a window with given folder uri(s)") },
4242
{ id: 'file-uri', type: 'string', cat: 'o', args: 'uri', description: localize('fileUri', "Opens a window with given file uri(s)") },
43-
{ id: 'workspace-uri', type: 'string', cat: 'o', args: 'uri', description: localize('workspaceUri', "Opens a window with given workspace file") },
4443

4544
{ id: 'extensions-dir', type: 'string', cat: 'e', args: 'dir', description: localize('extensionHomePath', "Set the root path for extensions.") },
4645
{ id: 'list-extensions', type: 'boolean', cat: 'e', description: localize('listExtensions', "List the installed extensions.") },

src/vs/platform/menubar/electron-main/menubar.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ export class Menubar {
424424
this.setMenu(submenu, item.submenu.items);
425425
menu.append(submenuItem);
426426
} else if (isMenubarMenuItemUriAction(item)) {
427-
menu.append(this.createOpenRecentMenuItem(item.uri, item.label, item.id, item.id === 'openRecentFile'));
427+
menu.append(this.createOpenRecentMenuItem(item.uri, item.label, item.id));
428428
} else if (isMenubarMenuItemAction(item)) {
429429
if (item.id === 'workbench.action.showAboutDialog') {
430430
this.insertCheckForUpdatesItems(menu);
@@ -462,8 +462,9 @@ export class Menubar {
462462
}
463463
}
464464

465-
private createOpenRecentMenuItem(uri: URI, label: string, commandId: string, isFile: boolean): Electron.MenuItem {
465+
private createOpenRecentMenuItem(uri: URI, label: string, commandId: string): Electron.MenuItem {
466466
const revivedUri = URI.revive(uri);
467+
const typeHint = commandId === 'openRecentFile' || commandId === 'openRecentWorkspace' ? 'file' : 'folder';
467468

468469
return new MenuItem(this.likeAction(commandId, {
469470
label,
@@ -472,9 +473,9 @@ export class Menubar {
472473
const success = this.windowsMainService.open({
473474
context: OpenContext.MENU,
474475
cli: this.environmentService.args,
475-
urisToOpen: [revivedUri],
476+
urisToOpen: [{ uri: revivedUri, typeHint }],
476477
forceNewWindow: openInNewWindow,
477-
forceOpenWorkspaceAsFile: isFile
478+
forceOpenWorkspaceAsFile: commandId === 'openRecentFile'
478479
}).length > 0;
479480

480481
if (!success) {

0 commit comments

Comments
 (0)