apiCommands.ts 8.6 KB
Newer Older
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6
import * as vscode from 'vscode';
7
import { URI } from 'vs/base/common/uri';
J
Johannes Rieken 已提交
8
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
9
import { CommandsRegistry, ICommandService, ICommandHandler } from 'vs/platform/commands/common/commands';
10
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
11
import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor';
12
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
13
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
M
Martin Aeschlimann 已提交
14
import { IWindowsService, IOpenSettings, IURIToOpen } from 'vs/platform/windows/common/windows';
15
import { IDownloadService } from 'vs/platform/download/common/download';
M
Martin Aeschlimann 已提交
16
import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
17
import { IRecent } from 'vs/platform/history/common/history';
18 19 20 21 22 23 24 25 26

// -----------------------------------------------------------------
// The following commands are registered on both sides separately.
//
// We are trying to maintain backwards compatibility for cases where
// API commands are encoded as markdown links, for example.
// -----------------------------------------------------------------

export interface ICommandsExecutor {
27
	executeCommand<T>(id: string, ...args: any[]): Promise<T | undefined>;
28 29 30 31 32 33 34 35
}

function adjustHandler(handler: (executor: ICommandsExecutor, ...args: any[]) => any): ICommandHandler {
	return (accessor, ...args: any[]) => {
		return handler(accessor.get(ICommandService), ...args);
	};
}

36 37 38 39 40
interface IOpenFolderAPICommandOptions {
	forceNewWindow?: boolean;
	noRecentEntry?: boolean;
}

41 42
export class OpenFolderAPICommand {
	public static ID = 'vscode.openFolder';
43 44 45 46 47 48
	public static execute(executor: ICommandsExecutor, uri?: URI, forceNewWindow?: boolean): Promise<any>;
	public static execute(executor: ICommandsExecutor, uri?: URI, options?: IOpenFolderAPICommandOptions): Promise<any>;
	public static execute(executor: ICommandsExecutor, uri?: URI, arg: boolean | IOpenFolderAPICommandOptions = {}): Promise<any> {
		if (typeof arg === 'boolean') {
			arg = { forceNewWindow: arg };
		}
49
		if (!uri) {
50 51
			return executor.executeCommand('_files.pickFolderAndOpen', arg.forceNewWindow);
		}
52
		const options: IOpenSettings = { forceNewWindow: arg.forceNewWindow, noRecentEntry: arg.noRecentEntry };
M
Martin Aeschlimann 已提交
53
		uri = URI.revive(uri);
M
Martin Aeschlimann 已提交
54 55
		const uriToOpen: IURIToOpen = hasWorkspaceFileExtension(uri.path) ? { workspaceUri: uri } : { folderUri: uri };
		return executor.executeCommand('_files.windowOpen', [uriToOpen], options);
56 57
	}
}
58 59 60 61
CommandsRegistry.registerCommand({
	id: OpenFolderAPICommand.ID,
	handler: adjustHandler(OpenFolderAPICommand.execute),
	description: {
62 63 64
		description: 'Open a folder or workspace in the current window or new window depending on the newWindow argument. Note that opening in the same window will shutdown the current extension host process and start a new one on the given folder/workspace unless the newWindow parameter is set to true.',
		args: [
			{ name: 'uri', description: '(optional) Uri of the folder or workspace file to open. If not provided, a native dialog will ask the user for the folder', constraint: (value: any) => value === undefined || value instanceof URI },
M
Martin Aeschlimann 已提交
65
			{ name: 'options', description: '(optional) Options. Object with the following properties: `forceNewWindow `: Whether to open the folder/workspace in a new window or the same. Defaults to opening in the same window. `noRecentEntry`: Wheter the opened URI will appear in the \'Open Recent\' list. Defaults to true.  Note, for backward compatibility, options can also be of type boolean, representing the `forceNewWindow` setting.', constraint: (value: any) => value === undefined || typeof value === 'object' || typeof value === 'boolean' }
66
		]
67 68
	}
});
69

70
interface INewWindowAPICommandOptions {
M
Martin Aeschlimann 已提交
71
	reuseWindow?: boolean;
72 73 74 75 76
}

export class NewWindowAPICommand {
	public static ID = 'vscode.newWindow';
	public static execute(executor: ICommandsExecutor, options?: INewWindowAPICommandOptions): Promise<any> {
R
Rob Lourens 已提交
77
		return executor.executeCommand('_files.newWindow', options);
78 79 80 81 82 83 84 85 86 87 88 89
	}
}
CommandsRegistry.registerCommand({
	id: NewWindowAPICommand.ID,
	handler: adjustHandler(NewWindowAPICommand.execute),
	description: {
		description: 'Opens an new window',
		args: [
		]
	}
});

90 91
export class DiffAPICommand {
	public static ID = 'vscode.diff';
J
Johannes Rieken 已提交
92
	public static execute(executor: ICommandsExecutor, left: URI, right: URI, label: string, options?: vscode.TextDocumentShowOptions): Promise<any> {
93 94 95 96
		return executor.executeCommand('_workbench.diff', [
			left, right,
			label,
			undefined,
97
			typeConverters.TextEditorOptions.from(options),
98
			options ? typeConverters.ViewColumn.from(options.viewColumn) : undefined
99 100 101 102 103 104 105
		]);
	}
}
CommandsRegistry.registerCommand(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute));

export class OpenAPICommand {
	public static ID = 'vscode.open';
J
Johannes Rieken 已提交
106
	public static execute(executor: ICommandsExecutor, resource: URI, columnOrOptions?: vscode.ViewColumn | vscode.TextDocumentShowOptions, label?: string): Promise<any> {
107 108
		let options: ITextEditorOptions | undefined;
		let position: EditorViewColumn | undefined;
109 110 111

		if (columnOrOptions) {
			if (typeof columnOrOptions === 'number') {
112
				position = typeConverters.ViewColumn.from(columnOrOptions);
113
			} else {
114
				options = typeConverters.TextEditorOptions.from(columnOrOptions);
115
				position = typeConverters.ViewColumn.from(columnOrOptions.viewColumn);
116 117 118 119 120 121
			}
		}

		return executor.executeCommand('_workbench.open', [
			resource,
			options,
J
Joao Moreno 已提交
122 123
			position,
			label
124 125 126 127 128
		]);
	}
}
CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute));

M
Martin Aeschlimann 已提交
129
CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, uri: URI) {
130
	const windowsService = accessor.get(IWindowsService);
M
Martin Aeschlimann 已提交
131
	return windowsService.removeFromRecentlyOpened([uri]).then(() => undefined);
132 133
});

134 135
export class RemoveFromRecentlyOpenedAPICommand {
	public static ID = 'vscode.removeFromRecentlyOpened';
M
Martin Aeschlimann 已提交
136 137 138 139 140 141
	public static execute(executor: ICommandsExecutor, path: string | URI): Promise<any> {
		if (typeof path === 'string') {
			path = path.match(/^[^:/?#]+:\/\//) ? URI.parse(path) : URI.file(path);
		} else {
			path = URI.revive(path); // called from extension host
		}
142 143 144 145
		return executor.executeCommand('_workbench.removeFromRecentlyOpened', path);
	}
}
CommandsRegistry.registerCommand(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute));
146

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
interface RecentEntry {
	uri: URI;
	type: 'workspace' | 'folder' | 'file';
	label?: string;
}

CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async function (accessor: ServicesAccessor, recentEntry: RecentEntry) {
	const windowsService = accessor.get(IWindowsService);
	const workspacesService = accessor.get(IWorkspacesService);
	let recent: IRecent | undefined = undefined;
	const uri = recentEntry.uri;
	const label = recentEntry.label;
	if (recentEntry.type === 'workspace') {
		const workspace = await workspacesService.getWorkspaceIdentifier(uri);
		recent = { workspace, label };
	} else if (recentEntry.type === 'folder') {
		recent = { folderUri: uri, label };
	} else {
		recent = { fileUri: uri, label };
	}
	return windowsService.addRecentlyOpened([recent]);
});

170 171
export class SetEditorLayoutAPICommand {
	public static ID = 'vscode.setEditorLayout';
J
Johannes Rieken 已提交
172
	public static execute(executor: ICommandsExecutor, layout: EditorGroupLayout): Promise<any> {
173 174 175
		return executor.executeCommand('layoutEditorGroups', layout);
	}
}
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
CommandsRegistry.registerCommand({
	id: SetEditorLayoutAPICommand.ID,
	handler: adjustHandler(SetEditorLayoutAPICommand.execute),
	description: {
		description: 'Set Editor Layout',
		args: [{
			name: 'args',
			schema: {
				'type': 'object',
				'required': ['groups'],
				'properties': {
					'orientation': {
						'type': 'number',
						'default': 0,
						'enum': [0, 1]
					},
					'groups': {
						'$ref': '#/definitions/editorGroupsSchema', // defined in keybindingService.ts ...
						'default': [{}, {}],
					}
				}
			}
		}]
	}
});
201 202 203

CommandsRegistry.registerCommand('_workbench.downloadResource', function (accessor: ServicesAccessor, resource: URI) {
	const downloadService = accessor.get(IDownloadService);
204 205
	return downloadService.download(resource).then(location => URI.file(location));
});