Skip to content

Commit b5b21ea

Browse files
committed
allow remote workspaces
1 parent 7efcf23 commit b5b21ea

21 files changed

Lines changed: 190 additions & 169 deletions

File tree

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

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { normalizeNFC } from 'vs/base/common/normalization';
3434
import { URI } from 'vs/base/common/uri';
3535
import { Queue, timeout } from 'vs/base/common/async';
3636
import { exists } from 'vs/base/node/pfs';
37-
import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename } from 'vs/base/common/resources';
37+
import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename, fsPath } from 'vs/base/common/resources';
3838
import { endsWith } from 'vs/base/common/strings';
3939
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
4040

@@ -102,6 +102,10 @@ interface IFileInputs {
102102
remoteAuthority?: string;
103103
}
104104

105+
enum URIType {
106+
FILE, FOLDER, WORKSPACE
107+
}
108+
105109
interface IPathToOpen extends IPath {
106110

107111
// the workspace for a Code instance to open
@@ -438,7 +442,7 @@ export class WindowsManager implements IWindowsMainService {
438442
// Make sure to pass focus to the most relevant of the windows if we open multiple
439443
if (usedWindows.length > 1) {
440444

441-
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);
445+
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);
442446
let focusLastOpened = true;
443447
let focusLastWindow = true;
444448

@@ -552,10 +556,9 @@ export class WindowsManager implements IWindowsMainService {
552556
let bestWindowOrFolder = findBestWindowOrFolderForFile({
553557
windows,
554558
newWindow: openFilesInNewWindow,
555-
reuseWindow: openConfig.forceReuseWindow,
556559
context: openConfig.context,
557560
fileUri: fileToCheck && fileToCheck.fileUri,
558-
workspaceResolver: workspace => this.workspacesMainService.resolveWorkspaceSync(workspace.configPath)
561+
workspaceResolver: workspace => workspace.configPath.scheme === Schemas.file && this.workspacesMainService.resolveWorkspaceSync(fsPath(workspace.configPath))
559562
});
560563

561564
// We found a window to open the files in
@@ -801,7 +804,7 @@ export class WindowsManager implements IWindowsMainService {
801804
}
802805

803806
// Extract paths: from CLI
804-
else if (hasArgs(openConfig.cli._) || hasArgs(openConfig.cli['folder-uri']) || hasArgs(openConfig.cli['file-uri'])) {
807+
else if (hasArgs(openConfig.cli._) || hasArgs(openConfig.cli['folder-uri']) || hasArgs(openConfig.cli['file-uri']) || hasArgs(openConfig.cli['workspace-uri'])) {
805808
windowsToOpen = this.doExtractPathsFromCLI(openConfig.cli);
806809
isCommandLineOrAPICall = true;
807810
}
@@ -838,7 +841,7 @@ export class WindowsManager implements IWindowsMainService {
838841
continue;
839842
}
840843

841-
const path = this.parseUri(pathToOpen, openConfig.forceOpenWorkspaceAsFile, parseOptions);
844+
const path = this.parseUri(pathToOpen, openConfig.forceOpenWorkspaceAsFile ? URIType.FILE : URIType.FOLDER, parseOptions);
842845
if (path) {
843846
pathsToOpen.push(path);
844847
} else {
@@ -874,7 +877,7 @@ export class WindowsManager implements IWindowsMainService {
874877
// folder uris
875878
const folderUris = asArray(cli['folder-uri']);
876879
for (let folderUri of folderUris) {
877-
const path = this.parseUri(this.argToUri(folderUri), false, parseOptions);
880+
const path = this.parseUri(this.argToUri(folderUri), URIType.FOLDER, parseOptions);
878881
if (path) {
879882
pathsToOpen.push(path);
880883
}
@@ -883,12 +886,21 @@ export class WindowsManager implements IWindowsMainService {
883886
// file uris
884887
const fileUris = asArray(cli['file-uri']);
885888
for (let fileUri of fileUris) {
886-
const path = this.parseUri(this.argToUri(fileUri), true, parseOptions);
889+
const path = this.parseUri(this.argToUri(fileUri), URIType.FILE, parseOptions);
890+
if (path) {
891+
pathsToOpen.push(path);
892+
}
893+
}
894+
895+
const workspaceUris = asArray(cli['workspace-uri']);
896+
for (let workspaceUri of workspaceUris) {
897+
const path = this.parseUri(this.argToUri(workspaceUri), URIType.WORKSPACE, parseOptions);
887898
if (path) {
888899
pathsToOpen.push(path);
889900
}
890901
}
891902

903+
892904
// folder or file paths
893905
const cliArgs = asArray(cli._);
894906
for (let cliArg of cliArgs) {
@@ -932,12 +944,12 @@ export class WindowsManager implements IWindowsMainService {
932944
const windowsToOpen: IPathToOpen[] = [];
933945
for (const openedWindow of openedWindows) {
934946
if (openedWindow.workspace) { // Workspaces
935-
const pathToOpen = this.parsePath(openedWindow.workspace.configPath, { remoteAuthority: openedWindow.remoteAuthority });
947+
const pathToOpen = this.parseUri(openedWindow.workspace.configPath, URIType.WORKSPACE, { remoteAuthority: openedWindow.remoteAuthority });
936948
if (pathToOpen && pathToOpen.workspace) {
937949
windowsToOpen.push(pathToOpen);
938950
}
939951
} else if (openedWindow.folderUri) { // Folders
940-
const pathToOpen = this.parseUri(openedWindow.folderUri, false, { remoteAuthority: openedWindow.remoteAuthority });
952+
const pathToOpen = this.parseUri(openedWindow.folderUri, URIType.FOLDER, { remoteAuthority: openedWindow.remoteAuthority });
941953
if (pathToOpen && pathToOpen.folderUri) {
942954
windowsToOpen.push(pathToOpen);
943955
}
@@ -987,7 +999,7 @@ export class WindowsManager implements IWindowsMainService {
987999
return null;
9881000
}
9891001

990-
private parseUri(uri: URI, isFile: boolean, options?: IPathParseOptions): IPathToOpen {
1002+
private parseUri(uri: URI, type: URIType, options?: IPathParseOptions): IPathToOpen {
9911003
if (!uri || !uri.scheme) {
9921004
return null;
9931005
}
@@ -1004,7 +1016,7 @@ export class WindowsManager implements IWindowsMainService {
10041016
if (uriPath.length > 2 && endsWith(uriPath, '/')) {
10051017
uri = uri.with({ path: uriPath.substr(0, uriPath.length - 1) });
10061018
}
1007-
if (isFile) {
1019+
if (type === URIType.FILE) {
10081020
if (options && options.gotoLineMode) {
10091021
const parsedPath = parseLineAndColumnAware(uri.path);
10101022
return {
@@ -1018,6 +1030,11 @@ export class WindowsManager implements IWindowsMainService {
10181030
fileUri: uri,
10191031
remoteAuthority
10201032
};
1033+
} else if (type === URIType.WORKSPACE) {
1034+
return {
1035+
workspace: this.workspacesMainService.getWorkspaceIdentifier(uri),
1036+
remoteAuthority
1037+
};
10211038
}
10221039
return {
10231040
folderUri: uri,
@@ -1141,6 +1158,7 @@ export class WindowsManager implements IWindowsMainService {
11411158
}
11421159
let folderUris = asArray(openConfig.cli['folder-uri']);
11431160
let fileUris = asArray(openConfig.cli['file-uri']);
1161+
let workspaceUris = asArray(openConfig.cli['workspace-uri']);
11441162
let cliArgs = openConfig.cli._;
11451163

11461164
// Fill in previously opened workspace unless an explicit path is provided and we are not unit testing
@@ -1155,7 +1173,11 @@ export class WindowsManager implements IWindowsMainService {
11551173
folderUris = [workspaceToOpen.toString()];
11561174
}
11571175
} else {
1158-
cliArgs = [workspaceToOpen.configPath];
1176+
if (workspaceToOpen.configPath.scheme === Schemas.file) {
1177+
cliArgs = [fsPath(workspaceToOpen.configPath)];
1178+
} else {
1179+
workspaceUris = [workspaceToOpen.configPath.toString()];
1180+
}
11591181
}
11601182
}
11611183
}
@@ -1173,12 +1195,17 @@ export class WindowsManager implements IWindowsMainService {
11731195
fileUris = [];
11741196
}
11751197

1198+
if (workspaceUris.length && workspaceUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.argToUri(uri)))) {
1199+
workspaceUris = [];
1200+
}
1201+
11761202
openConfig.cli._ = cliArgs;
11771203
openConfig.cli['folder-uri'] = folderUris;
11781204
openConfig.cli['file-uri'] = fileUris;
1205+
openConfig.cli['workspace-uri'] = workspaceUris;
11791206

11801207
// Open it
1181-
this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: !cliArgs.length && !folderUris.length && !fileUris.length, userEnv: openConfig.userEnv });
1208+
this.open({ context: openConfig.context, cli: openConfig.cli, forceNewWindow: true, forceEmpty: !cliArgs.length && !folderUris.length && !fileUris.length && !workspaceUris.length, userEnv: openConfig.userEnv });
11821209
}
11831210

11841211
private openInBrowserWindow(options: IOpenBrowserWindowOptions): ICodeWindow {
@@ -1980,7 +2007,7 @@ class WorkspacesManager {
19802007
return Promise.resolve(true);
19812008
}
19822009

1983-
if (window.openedWorkspace && isEqual(URI.file(window.openedWorkspace.configPath), path)) {
2010+
if (window.openedWorkspace && isEqual(window.openedWorkspace.configPath, path)) {
19842011
return Promise.resolve(false); // window is already opened on a workspace with that path
19852012
}
19862013

@@ -2116,7 +2143,7 @@ class WorkspacesManager {
21162143
return workspace.scheme === Schemas.file ? dirname(workspace.fsPath) : undefined;
21172144
}
21182145

2119-
const resolvedWorkspace = this.workspacesMainService.resolveWorkspaceSync(workspace.configPath);
2146+
const resolvedWorkspace = workspace.configPath.scheme === Schemas.file && this.workspacesMainService.resolveWorkspaceSync(workspace.configPath.fsPath);
21202147
if (resolvedWorkspace && resolvedWorkspace.folders.length > 0) {
21212148
for (const folder of resolvedWorkspace.folders) {
21222149
if (folder.uri.scheme === Schemas.file) {

src/vs/code/node/windowsFinder.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,14 @@ export interface ISimpleWindow {
2121
export interface IBestWindowOrFolderOptions<W extends ISimpleWindow> {
2222
windows: W[];
2323
newWindow: boolean;
24-
reuseWindow: boolean;
2524
context: OpenContext;
2625
fileUri?: URI;
2726
userHome?: string;
2827
codeSettingsFolder?: string;
2928
workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace | null;
3029
}
3130

32-
export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows, newWindow, reuseWindow, context, fileUri, workspaceResolver }: IBestWindowOrFolderOptions<W>): W | null {
31+
export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows, newWindow, context, fileUri, workspaceResolver }: IBestWindowOrFolderOptions<W>): W | null {
3332
if (!newWindow && fileUri && (context === OpenContext.DESKTOP || context === OpenContext.CLI || context === OpenContext.DOCK)) {
3433
const windowOnFilePath = findWindowOnFilePath(windows, fileUri, workspaceResolver);
3534
if (windowOnFilePath) {
@@ -42,11 +41,21 @@ export function findBestWindowOrFolderForFile<W extends ISimpleWindow>({ windows
4241
function findWindowOnFilePath<W extends ISimpleWindow>(windows: W[], fileUri: URI, workspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace | null): W | null {
4342

4443
// First check for windows with workspaces that have a parent folder of the provided path opened
45-
const workspaceWindows = windows.filter(window => !!window.openedWorkspace);
46-
for (const window of workspaceWindows) {
47-
const resolvedWorkspace = workspaceResolver(window.openedWorkspace!);
48-
if (resolvedWorkspace && resolvedWorkspace.folders.some(folder => isEqualOrParent(fileUri, folder.uri))) {
49-
return window;
44+
for (const window of windows) {
45+
const workspace = window.openedWorkspace;
46+
if (workspace) {
47+
const resolvedWorkspace = workspaceResolver(workspace);
48+
if (resolvedWorkspace) {
49+
// workspace cpuld be resolved: It's in the local file system
50+
if (resolvedWorkspace.folders.some(folder => isEqualOrParent(fileUri, folder.uri))) {
51+
return window;
52+
}
53+
} else {
54+
// use the config path instead
55+
if (isEqualOrParent(fileUri, workspace.configPath)) {
56+
return window;
57+
}
58+
}
5059
}
5160
}
5261

@@ -102,7 +111,7 @@ export function findWindowOnWorkspaceOrFolderUri<W extends ISimpleWindow>(window
102111
}
103112
for (const window of windows) {
104113
// check for workspace config path
105-
if (window.openedWorkspace && isEqual(URI.file(window.openedWorkspace.configPath), uri, !platform.isLinux /* ignorecase */)) {
114+
if (window.openedWorkspace && isEqual(window.openedWorkspace.configPath, uri)) {
106115
return window;
107116
}
108117

src/vs/code/test/node/windowsFinder.test.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@ const fixturesFolder = getPathFromAmdModule(require, './fixtures');
1515

1616
const testWorkspace: IWorkspaceIdentifier = {
1717
id: Date.now().toString(),
18-
configPath: path.join(fixturesFolder, 'workspaces.json')
18+
configPath: URI.file(path.join(fixturesFolder, 'workspaces.json'))
1919
};
2020

2121
function options(custom?: Partial<IBestWindowOrFolderOptions<ISimpleWindow>>): IBestWindowOrFolderOptions<ISimpleWindow> {
2222
return {
2323
windows: [],
2424
newWindow: false,
25-
reuseWindow: false,
2625
context: OpenContext.CLI,
2726
codeSettingsFolder: '_vscode',
2827
workspaceResolver: workspace => { return workspace === testWorkspace ? { id: testWorkspace.id, configPath: workspace.configPath, folders: toWorkspaceFolders([{ path: path.join(fixturesFolder, 'vscode_workspace_1_folder') }, { path: path.join(fixturesFolder, 'vscode_workspace_2_folder') }]) } : null!; },
@@ -52,7 +51,6 @@ suite('WindowsFinder', () => {
5251
})), null);
5352
assert.equal(findBestWindowOrFolderForFile(options({
5453
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')),
55-
reuseWindow: true
5654
})), null);
5755
assert.equal(findBestWindowOrFolderForFile(options({
5856
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')),
@@ -85,7 +83,6 @@ suite('WindowsFinder', () => {
8583
assert.equal(findBestWindowOrFolderForFile(options({
8684
windows: [lastActiveWindow, noVscodeFolderWindow],
8785
fileUri: URI.file(path.join(fixturesFolder, 'vscode_folder', 'file.txt')),
88-
reuseWindow: true
8986
})), lastActiveWindow);
9087
assert.equal(findBestWindowOrFolderForFile(options({
9188
windows,

src/vs/platform/backup/electron-main/backupMainService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ export class BackupMainService implements IBackupMainService {
253253

254254
// If the workspace has no backups, ignore it
255255
if (hasBackups) {
256-
if (await exists(workspace.configPath)) {
256+
if (workspace.configPath.scheme !== Schemas.file || await exists(workspace.configPath.fsPath)) {
257257
result.push(workspace);
258258
} else {
259259
// If the workspace has backups, but the target workspace is missing, convert backups to empty ones

src/vs/platform/backup/test/electron-main/backupMainService.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as fs from 'fs';
99
import * as os from 'os';
1010
import * as path from 'path';
1111
import * as pfs from 'vs/base/node/pfs';
12-
import { URI as Uri } from 'vs/base/common/uri';
12+
import { URI as Uri, URI } from 'vs/base/common/uri';
1313
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
1414
import { parseArgs } from 'vs/platform/environment/node/argv';
1515
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
@@ -60,7 +60,7 @@ suite('BackupMainService', () => {
6060
function toWorkspace(path: string): IWorkspaceIdentifier {
6161
return {
6262
id: createHash('md5').update(sanitizePath(path)).digest('hex'),
63-
configPath: path
63+
configPath: URI.file(path)
6464
};
6565
}
6666

@@ -73,8 +73,8 @@ suite('BackupMainService', () => {
7373
}
7474

7575
async function ensureWorkspaceExists(workspace: IWorkspaceIdentifier): Promise<IWorkspaceIdentifier> {
76-
if (!fs.existsSync(workspace.configPath)) {
77-
await pfs.writeFile(workspace.configPath, 'Hello');
76+
if (!fs.existsSync(workspace.configPath.fsPath)) {
77+
await pfs.writeFile(workspace.configPath.fsPath, 'Hello');
7878
}
7979
const backupFolder = service.toBackupPath(workspace.id);
8080
await createBackupFolder(backupFolder);

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

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

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ 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") },
4344

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

0 commit comments

Comments
 (0)