Skip to content

Commit

Permalink
Provide API to get access to the workspace configuration file (#37421) (
Browse files Browse the repository at this point in the history
#72490)

* Provide API to get access to the workspace configuration file (#37421)

* fix untitled ID

* update docs
  • Loading branch information
bpasero committed Apr 17, 2019
1 parent 2335966 commit c72cfe0
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,14 @@ suite('workspace-namespace', () => {
});

test('rootPath', () => {
if (vscode.workspace.rootPath) {
assert.ok(pathEquals(vscode.workspace.rootPath, join(__dirname, '../../testWorkspace')));
}
assert.ok(pathEquals(vscode.workspace.rootPath!, join(__dirname, '../../testWorkspace')));
assert.throws(() => (vscode.workspace as any).rootPath = 'farboo');
});

test('workspaceFile', () => {
assert.ok(!vscode.workspace.workspaceFile);
});

test('workspaceFolders', () => {
if (vscode.workspace.workspaceFolders) {
assert.equal(vscode.workspace.workspaceFolders.length, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,18 @@ suite('workspace-namespace', () => {
teardown(closeAllEditors);

test('rootPath', () => {
if (vscode.workspace.rootPath) {
assert.ok(pathEquals(vscode.workspace.rootPath, join(__dirname, '../../testWorkspace')));
}
assert.ok(pathEquals(vscode.workspace.rootPath!, join(__dirname, '../../testWorkspace')));
});

test('workspaceFile', () => {
assert.ok(pathEquals(vscode.workspace.workspaceFile!.fsPath, join(__dirname, '../../testworkspace.code-workspace')));
});

test('workspaceFolders', () => {
if (vscode.workspace.workspaceFolders) {
assert.equal(vscode.workspace.workspaceFolders.length, 2);
assert.ok(pathEquals(vscode.workspace.workspaceFolders[0].uri.fsPath, join(__dirname, '../../testWorkspace')));
assert.ok(pathEquals(vscode.workspace.workspaceFolders[1].uri.fsPath, join(__dirname, '../../testWorkspace2')));
assert.ok(pathEquals(vscode.workspace.workspaceFolders[1].name, 'Test Workspace 2'));
}
assert.equal(vscode.workspace.workspaceFolders!.length, 2);
assert.ok(pathEquals(vscode.workspace.workspaceFolders![0].uri.fsPath, join(__dirname, '../../testWorkspace')));
assert.ok(pathEquals(vscode.workspace.workspaceFolders![1].uri.fsPath, join(__dirname, '../../testWorkspace2')));
assert.ok(pathEquals(vscode.workspace.workspaceFolders![1].name, 'Test Workspace 2'));
});

test('getWorkspaceFolder', () => {
Expand Down
39 changes: 39 additions & 0 deletions src/vs/vscode.proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1371,4 +1371,43 @@ declare module 'vscode' {
group?: string;
}
//#endregion

//#region Workspace URI Ben

export namespace workspace {

/**
* The location of the workspace file, for example:
*
* `file:///Users/name/Development/myProject.code-workspace`
*
* or
*
* `untitled:1555503116870`
*
* for a workspace that is untitled and not yet saved.
*
* Depending on the workspace that is opened, the value will be:
* * `undefined` when no workspace or a single folder is opened
* * the path of the workspace file as `Uri` otherwise. if the workspace
* is untitled, the returned URI will use the `untitled:` scheme
*
* The location can e.g. be used with the `vscode.openFolder` command to
* open the workspace again after it has been closed.
*
* **Example:**
* ```typescript
* vscode.commands.executeCommand('vscode.openFolder', uriOfWorkspace);
* ```
*
* **Note:** it is not advised to use `workspace.workspaceFile` to write
* configuration data into the file. You can use `workspace.getConfiguration().update()`
* for that purpose which will work both when a single folder is opened as
* well as an untitled or saved workspace.
*/
export const workspaceFile: Uri | undefined;
}

//#endregion

}
6 changes: 5 additions & 1 deletion src/vs/workbench/api/browser/mainThreadWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape, IWorkspaceData, ITextSearchComplete } from '../common/extHost.protocol';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { isEqualOrParent } from 'vs/base/common/resources';

@extHostNamedCustomer(MainContext.MainThreadWorkspace)
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
Expand All @@ -40,7 +42,8 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
@IStatusbarService private readonly _statusbarService: IStatusbarService,
@IWindowService private readonly _windowService: IWindowService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@ILabelService private readonly _labelService: ILabelService
@ILabelService private readonly _labelService: ILabelService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace);
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace)));
Expand Down Expand Up @@ -110,6 +113,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
}
return {
configuration: workspace.configuration || undefined,
isUntitled: workspace.configuration ? isEqualOrParent(workspace.configuration, this._environmentService.untitledWorkspacesHome) : false,
folders: workspace.folders,
id: workspace.id,
name: this._labelService.getWorkspaceLabel(workspace)
Expand Down
3 changes: 2 additions & 1 deletion src/vs/workbench/api/common/apiCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { IWindowsService, IOpenSettings, IURIToOpen } from 'vs/platform/windows/
import { IDownloadService } from 'vs/platform/download/common/download';
import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
import { IRecent } from 'vs/platform/history/common/history';
import { Schemas } from 'vs/base/common/network';

// -----------------------------------------------------------------
// The following commands are registered on both sides separately.
Expand Down Expand Up @@ -51,7 +52,7 @@ export class OpenFolderAPICommand {
}
const options: IOpenSettings = { forceNewWindow: arg.forceNewWindow, noRecentEntry: arg.noRecentEntry };
uri = URI.revive(uri);
const uriToOpen: IURIToOpen = hasWorkspaceFileExtension(uri.path) ? { workspaceUri: uri } : { folderUri: uri };
const uriToOpen: IURIToOpen = (hasWorkspaceFileExtension(uri.path) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri };
return executor.executeCommand('_files.windowOpen', [uriToOpen], options);
}
}
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export interface IStaticWorkspaceData {
id: string;
name: string;
configuration?: UriComponents | null;
isUntitled?: boolean | null;
}

export interface IWorkspaceData extends IStaticWorkspaceData {
Expand Down
34 changes: 27 additions & 7 deletions src/vs/workbench/api/common/extHostWorkspace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { Counter } from 'vs/base/common/numbers';
import { isLinux } from 'vs/base/common/platform';
import { basenameOrAuthority, dirname, isEqual, relativePath } from 'vs/base/common/resources';
import { basenameOrAuthority, dirname, isEqual, relativePath, basename } from 'vs/base/common/resources';
import { compare } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
Expand All @@ -24,6 +24,7 @@ import * as vscode from 'vscode';
import { ExtHostWorkspaceShape, IWorkspaceData, MainThreadMessageServiceShape, MainThreadWorkspaceShape, IMainContext, MainContext, IStaticWorkspaceData } from './extHost.protocol';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { Barrier } from 'vs/base/common/async';
import { Schemas } from 'vs/base/common/network';

export interface IExtHostWorkspaceProvider {
getWorkspaceFolder2(uri: vscode.Uri, resolveParent?: boolean): Promise<vscode.WorkspaceFolder | undefined>;
Expand Down Expand Up @@ -67,7 +68,7 @@ class ExtHostWorkspaceImpl extends Workspace {
return { workspace: null, added: [], removed: [] };
}

const { id, name, folders } = data;
const { id, name, folders, configuration, isUntitled } = data;
const newWorkspaceFolders: vscode.WorkspaceFolder[] = [];

// If we have an existing workspace, we try to find the folders that match our
Expand Down Expand Up @@ -95,7 +96,7 @@ class ExtHostWorkspaceImpl extends Workspace {
// make sure to restore sort order based on index
newWorkspaceFolders.sort((f1, f2) => f1.index < f2.index ? -1 : 1);

const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders);
const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders, configuration ? URI.revive(configuration) : null, !!isUntitled);
const { added, removed } = delta(oldWorkspace ? oldWorkspace.workspaceFolders : [], workspace.workspaceFolders, compareWorkspaceFolderByUri);

return { workspace, added, removed };
Expand All @@ -115,8 +116,8 @@ class ExtHostWorkspaceImpl extends Workspace {
private readonly _workspaceFolders: vscode.WorkspaceFolder[] = [];
private readonly _structure = TernarySearchTree.forPaths<vscode.WorkspaceFolder>();

constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[]) {
super(id, folders.map(f => new WorkspaceFolder(f)));
constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[], configuration: URI | null, private _isUntitled: boolean) {
super(id, folders.map(f => new WorkspaceFolder(f)), configuration);

// setup the workspace folder data structure
folders.forEach(folder => {
Expand All @@ -129,6 +130,10 @@ class ExtHostWorkspaceImpl extends Workspace {
return this._name;
}

get isUntitled(): boolean {
return this._isUntitled;
}

get workspaceFolders(): vscode.WorkspaceFolder[] {
return this._workspaceFolders.slice(0);
}
Expand Down Expand Up @@ -175,7 +180,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac

this._proxy = mainContext.getProxy(MainContext.MainThreadWorkspace);
this._messageService = mainContext.getProxy(MainContext.MainThreadMessageService);
this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, []) : undefined;
this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled) : undefined;
}

$initializeWorkspace(data: IWorkspaceData): void {
Expand All @@ -197,6 +202,20 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
return this._actualWorkspace ? this._actualWorkspace.name : undefined;
}

get workspaceFile(): vscode.Uri | undefined {
if (this._actualWorkspace) {
if (this._actualWorkspace.configuration) {
if (this._actualWorkspace.isUntitled) {
return URI.from({ scheme: Schemas.untitled, path: basename(dirname(this._actualWorkspace.configuration)) }); // Untitled Worspace: return untitled URI
}

return this._actualWorkspace.configuration; // Workspace: return the configuration location
}
}

return undefined;
}

private get _actualWorkspace(): ExtHostWorkspaceImpl | undefined {
return this._unconfirmedWorkspace || this._confirmedWorkspace;
}
Expand Down Expand Up @@ -365,7 +384,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
id: this._actualWorkspace.id,
name: this._actualWorkspace.name,
configuration: this._actualWorkspace.configuration,
folders
folders,
isUntitled: this._actualWorkspace.isUntitled
} as IWorkspaceData, this._actualWorkspace).workspace || undefined;
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/vs/workbench/api/node/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,12 @@ export function createApiFactory(
set name(value) {
throw errors.readonly();
},
get workspaceFile() {
return extHostWorkspace.workspaceFile;
},
set workspaceFile(value) {
throw errors.readonly();
},
updateWorkspaceFolders: (index, deleteCount, ...workspaceFoldersToAdd) => {
return extHostWorkspace.updateWorkspaceFolders(extension, index, deleteCount || 0, ...workspaceFoldersToAdd);
},
Expand Down
19 changes: 17 additions & 2 deletions src/vs/workbench/contrib/files/browser/fileCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import * as nls from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import { toResource, IEditorCommandsContext, SideBySideEditor } from 'vs/workbench/common/editor';
import { IWindowsService, IWindowService, IURIToOpen, IOpenSettings, INewWindowOptions } from 'vs/platform/windows/common/windows';
import { IWindowsService, IWindowService, IURIToOpen, IOpenSettings, INewWindowOptions, isWorkspaceToOpen } from 'vs/platform/windows/common/windows';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
Expand Down Expand Up @@ -38,9 +38,11 @@ import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ILabelService } from 'vs/platform/label/common/label';
import { onUnexpectedError } from 'vs/base/common/errors';
import { basename, toLocalResource } from 'vs/base/common/resources';
import { basename, toLocalResource, joinPath } from 'vs/base/common/resources';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { UNTITLED_WORKSPACE_NAME } from 'vs/platform/workspaces/common/workspaces';

// Commands

Expand Down Expand Up @@ -81,6 +83,19 @@ export const REMOVE_ROOT_FOLDER_LABEL = nls.localize('removeFolderFromWorkspace'
export const openWindowCommand = (accessor: ServicesAccessor, urisToOpen: IURIToOpen[], options?: IOpenSettings) => {
if (Array.isArray(urisToOpen)) {
const windowService = accessor.get(IWindowService);
const environmentService = accessor.get(IEnvironmentService);

// rewrite untitled: workspace URIs to the absolute path on disk
urisToOpen = urisToOpen.map(uriToOpen => {
if (isWorkspaceToOpen(uriToOpen) && uriToOpen.workspaceUri.scheme === Schemas.untitled) {
return {
workspaceUri: joinPath(environmentService.untitledWorkspacesHome, uriToOpen.workspaceUri.path, UNTITLED_WORKSPACE_NAME)
};
}

return uriToOpen;
});

windowService.openWindow(urisToOpen, options);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { parseExtensionDevOptions } from '../common/extensionDevOptions';
import { VSBuffer } from 'vs/base/common/buffer';
import { IExtensionHostDebugService } from 'vs/workbench/services/extensions/common/extensionHostDebug';
import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions';
import { isEqualOrParent } from 'vs/base/common/resources';

export class ExtensionHostProcessWorker implements IExtensionHostStarter {

Expand Down Expand Up @@ -400,7 +401,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : {
configuration: withNullAsUndefined(workspace.configuration),
id: workspace.id,
name: this._labelService.getWorkspaceLabel(workspace)
name: this._labelService.getWorkspaceLabel(workspace),
isUntitled: workspace.configuration ? isEqualOrParent(workspace.configuration, this._environmentService.untitledWorkspacesHome) : false
},
resolvedExtensions: [],
hostExtensions: [],
Expand Down

0 comments on commit c72cfe0

Please sign in to comment.