extHostApiCommands.ts 26.0 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 { URI } from 'vs/base/common/uri';
7
import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
8
import type * as vscode from 'vscode';
J
Johannes Rieken 已提交
9 10
import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters';
import * as types from 'vs/workbench/api/common/extHostTypes';
11
import { IRawColorInfo, IWorkspaceEditDto, ICallHierarchyItemDto, IIncomingCallDto, IOutgoingCallDto } from 'vs/workbench/api/common/extHost.protocol';
12
import * as modes from 'vs/editor/common/modes';
13
import * as search from 'vs/workbench/contrib/search/common/search';
J
Johannes Rieken 已提交
14
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
15
import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
J
Johannes Rieken 已提交
16
import { CustomCodeAction } from 'vs/workbench/api/common/extHostLanguageFeatures';
17
import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand, OpenIssueReporter, OpenIssueReporterArgs } from './apiCommands';
18
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
19
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
J
Johannes Rieken 已提交
20
import { IRange } from 'vs/editor/common/core/range';
21
import { IPosition } from 'vs/editor/common/core/position';
22
import { TransientMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
23 24 25 26 27 28 29

//#region --- NEW world

export class ApiCommandArgument<V, O = V> {

	static readonly Uri = new ApiCommandArgument<URI>('uri', 'Uri of a text document', v => URI.isUri(v), v => v);
	static readonly Position = new ApiCommandArgument<types.Position, IPosition>('position', 'A position in a text document', v => types.Position.isPosition(v), typeConverters.Position.from);
30
	static readonly Range = new ApiCommandArgument<types.Range, IRange>('range', 'A range in a text document', v => types.Range.isRange(v), typeConverters.Range.from);
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

	static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof types.CallHierarchyItem, typeConverters.CallHierarchyItem.to);

	constructor(
		readonly name: string,
		readonly description: string,
		readonly validate: (v: V) => boolean,
		readonly convert: (v: V) => O
	) { }
}

export class ApiCommandResult<V, O = V> {

	constructor(
		readonly description: string,
46
		readonly convert: (v: V, apiArgs: any[], cmdConverter: CommandsConverter) => O
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
	) { }
}

export class ApiCommand {

	constructor(
		readonly id: string,
		readonly internalId: string,
		readonly description: string,
		readonly args: ApiCommandArgument<any, any>[],
		readonly result: ApiCommandResult<any, any>
	) { }

	register(commands: ExtHostCommands): IDisposable {

		return commands.registerCommand(false, this.id, async (...apiArgs) => {

			const internalArgs = this.args.map((arg, i) => {
				if (!arg.validate(apiArgs[i])) {
					throw new Error(`Invalid argument '${arg.name}' when running '${this.id}', receieved: ${apiArgs[i]}`);
				}
				return arg.convert(apiArgs[i]);
			});

			const internalResult = await commands.executeCommand(this.internalId, ...internalArgs);
72
			return this.result.convert(internalResult, apiArgs, commands.converter);
73 74 75 76 77 78 79 80 81 82 83 84 85 86
		}, undefined, this._getCommandHandlerDesc());
	}

	private _getCommandHandlerDesc(): ICommandHandlerDescription {
		return {
			description: this.description,
			args: this.args,
			returns: this.result.description
		};
	}
}


const newCommands: ApiCommand[] = [
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
	// -- document highlights
	new ApiCommand(
		'vscode.executeDocumentHighlights', '_executeDocumentHighlights', 'Execute document highlight provider.',
		[ApiCommandArgument.Uri, ApiCommandArgument.Position],
		new ApiCommandResult<modes.DocumentHighlight[], types.DocumentHighlight[] | undefined>('A promise that resolves to an array of SymbolInformation and DocumentSymbol instances.', tryMapWith(typeConverters.DocumentHighlight.to))
	),
	// -- document symbols
	new ApiCommand(
		'vscode.executeDocumentSymbolProvider', '_executeDocumentSymbolProvider', 'Execute document symbol provider.',
		[ApiCommandArgument.Uri],
		new ApiCommandResult<modes.DocumentSymbol[], vscode.SymbolInformation[] | undefined>('A promise that resolves to an array of DocumentHighlight-instances.', (value, apiArgs) => {

			if (isFalsyOrEmpty(value)) {
				return undefined;
			}
			class MergedInfo extends types.SymbolInformation implements vscode.DocumentSymbol {
				static to(symbol: modes.DocumentSymbol): MergedInfo {
					const res = new MergedInfo(
						symbol.name,
						typeConverters.SymbolKind.to(symbol.kind),
						symbol.containerName || '',
						new types.Location(apiArgs[0], typeConverters.Range.to(symbol.range))
					);
					res.detail = symbol.detail;
					res.range = res.location.range;
					res.selectionRange = typeConverters.Range.to(symbol.selectionRange);
					res.children = symbol.children ? symbol.children.map(MergedInfo.to) : [];
					return res;
				}

				detail!: string;
				range!: vscode.Range;
				selectionRange!: vscode.Range;
				children!: vscode.DocumentSymbol[];
				containerName!: string;
			}
			return value.map(MergedInfo.to);

		})
	),
	// -- formatting
	new ApiCommand(
		'vscode.executeFormatDocumentProvider', '_executeFormatDocumentProvider', 'Execute document format provider.',
		[ApiCommandArgument.Uri, new ApiCommandArgument('options', 'Formatting options', _ => true, v => v)],
J
Johannes Rieken 已提交
131
		new ApiCommandResult<modes.TextEdit[], types.TextEdit[] | undefined>('A promise that resolves to an array of TextEdits.', tryMapWith(typeConverters.TextEdit.to))
132 133 134 135
	),
	new ApiCommand(
		'vscode.executeFormatRangeProvider', '_executeFormatRangeProvider', 'Execute range format provider.',
		[ApiCommandArgument.Uri, ApiCommandArgument.Range, new ApiCommandArgument('options', 'Formatting options', _ => true, v => v)],
J
Johannes Rieken 已提交
136
		new ApiCommandResult<modes.TextEdit[], types.TextEdit[] | undefined>('A promise that resolves to an array of TextEdits.', tryMapWith(typeConverters.TextEdit.to))
137 138 139 140
	),
	new ApiCommand(
		'vscode.executeFormatOnTypeProvider', '_executeFormatOnTypeProvider', 'Execute format on type provider.',
		[ApiCommandArgument.Uri, ApiCommandArgument.Position, new ApiCommandArgument('ch', 'Trigger character', v => typeof v === 'string', v => v), new ApiCommandArgument('options', 'Formatting options', _ => true, v => v)],
J
Johannes Rieken 已提交
141
		new ApiCommandResult<modes.TextEdit[], types.TextEdit[] | undefined>('A promise that resolves to an array of TextEdits.', tryMapWith(typeConverters.TextEdit.to))
142 143 144
	),
	// -- go to symbol (definition, type definition, declaration, impl, references)
	new ApiCommand(
B
Brett Cannon 已提交
145
		'vscode.executeDefinitionProvider', '_executeDefinitionProvider', 'Execute all definition providers.',
146
		[ApiCommandArgument.Uri, ApiCommandArgument.Position],
147
		new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location or LocationLink instances.', mapLocationOrLocationLink)
148 149 150 151
	),
	new ApiCommand(
		'vscode.executeTypeDefinitionProvider', '_executeTypeDefinitionProvider', 'Execute all type definition providers.',
		[ApiCommandArgument.Uri, ApiCommandArgument.Position],
152
		new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location or LocationLink instances.', mapLocationOrLocationLink)
153 154 155 156
	),
	new ApiCommand(
		'vscode.executeDeclarationProvider', '_executeDeclarationProvider', 'Execute all declaration providers.',
		[ApiCommandArgument.Uri, ApiCommandArgument.Position],
157
		new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location or LocationLink instances.', mapLocationOrLocationLink)
158 159 160 161
	),
	new ApiCommand(
		'vscode.executeImplementationProvider', '_executeImplementationProvider', 'Execute all implementation providers.',
		[ApiCommandArgument.Uri, ApiCommandArgument.Position],
162
		new ApiCommandResult<(modes.Location | modes.LocationLink)[], (types.Location | vscode.LocationLink)[] | undefined>('A promise that resolves to an array of Location or LocationLink instances.', mapLocationOrLocationLink)
163 164 165 166 167 168 169 170
	),
	new ApiCommand(
		'vscode.executeReferenceProvider', '_executeReferenceProvider', 'Execute all reference providers.',
		[ApiCommandArgument.Uri, ApiCommandArgument.Position],
		new ApiCommandResult<modes.Location[], types.Location[] | undefined>('A promise that resolves to an array of Location-instances.', tryMapWith(typeConverters.location.to))
	),
	// -- hover
	new ApiCommand(
B
Brett Cannon 已提交
171
		'vscode.executeHoverProvider', '_executeHoverProvider', 'Execute all hover providers.',
172 173 174 175 176 177
		[ApiCommandArgument.Uri, ApiCommandArgument.Position],
		new ApiCommandResult<modes.Hover[], types.Hover[] | undefined>('A promise that resolves to an array of Hover-instances.', tryMapWith(typeConverters.Hover.to))
	),
	// -- selection range
	new ApiCommand(
		'vscode.executeSelectionRangeProvider', '_executeSelectionRangeProvider', 'Execute selection range provider.',
178
		[ApiCommandArgument.Uri, new ApiCommandArgument<types.Position[], IPosition[]>('position', 'A positions in a text document', v => Array.isArray(v) && v.every(v => types.Position.isPosition(v)), v => v.map(typeConverters.Position.from))],
179 180 181 182 183 184 185 186 187 188
		new ApiCommandResult<IRange[][], types.SelectionRange[]>('A promise that resolves to an array of ranges.', result => {
			return result.map(ranges => {
				let node: types.SelectionRange | undefined;
				for (const range of ranges.reverse()) {
					node = new types.SelectionRange(typeConverters.Range.to(range), node);
				}
				return node!;
			});
		})
	),
189 190
	// -- symbol search
	new ApiCommand(
B
Brett Cannon 已提交
191
		'vscode.executeWorkspaceSymbolProvider', '_executeWorkspaceSymbolProvider', 'Execute all workspace symbol providers.',
192 193 194 195 196 197 198 199 200 201 202 203
		[new ApiCommandArgument('query', 'Search string', v => typeof v === 'string', v => v)],
		new ApiCommandResult<[search.IWorkspaceSymbolProvider, search.IWorkspaceSymbol[]][], types.SymbolInformation[]>('A promise that resolves to an array of SymbolInformation-instances.', value => {
			const result: types.SymbolInformation[] = [];
			if (Array.isArray(value)) {
				for (let tuple of value) {
					result.push(...tuple[1].map(typeConverters.WorkspaceSymbol.to));
				}
			}
			return result;
		})
	),
	// --- call hierarchy
204 205 206
	new ApiCommand(
		'vscode.prepareCallHierarchy', '_executePrepareCallHierarchy', 'Prepare call hierarchy at a position inside a document',
		[ApiCommandArgument.Uri, ApiCommandArgument.Position],
J
Johannes Rieken 已提交
207
		new ApiCommandResult<ICallHierarchyItemDto[], types.CallHierarchyItem[]>('A CallHierarchyItem or undefined', v => v.map(typeConverters.CallHierarchyItem.to))
208 209 210 211 212 213 214 215 216 217 218
	),
	new ApiCommand(
		'vscode.provideIncomingCalls', '_executeProvideIncomingCalls', 'Compute incoming calls for an item',
		[ApiCommandArgument.CallHierarchyItem],
		new ApiCommandResult<IIncomingCallDto[], types.CallHierarchyIncomingCall[]>('A CallHierarchyItem or undefined', v => v.map(typeConverters.CallHierarchyIncomingCall.to))
	),
	new ApiCommand(
		'vscode.provideOutgoingCalls', '_executeProvideOutgoingCalls', 'Compute outgoing calls for an item',
		[ApiCommandArgument.CallHierarchyItem],
		new ApiCommandResult<IOutgoingCallDto[], types.CallHierarchyOutgoingCall[]>('A CallHierarchyItem or undefined', v => v.map(typeConverters.CallHierarchyOutgoingCall.to))
	),
219 220 221 222 223 224 225 226 227 228 229 230 231
	// --- rename
	new ApiCommand(
		'vscode.executeDocumentRenameProvider', '_executeDocumentRenameProvider', 'Execute rename provider.',
		[ApiCommandArgument.Uri, ApiCommandArgument.Position, new ApiCommandArgument('newName', 'The new symbol name', v => typeof v === 'string', v => v)],
		new ApiCommandResult<IWorkspaceEditDto, types.WorkspaceEdit | undefined>('A promise that resolves to a WorkspaceEdit.', value => {
			if (!value) {
				return undefined;
			}
			if (value.rejectReason) {
				throw new Error(value.rejectReason);
			}
			return typeConverters.WorkspaceEdit.to(value);
		})
232 233 234 235 236 237
	),
	// --- links
	new ApiCommand(
		'vscode.executeLinkProvider', '_executeLinkProvider', 'Execute document link provider.',
		[ApiCommandArgument.Uri, new ApiCommandArgument('linkResolveCount', '(optional) Number of links that should be resolved, only when links are unresolved.', v => typeof v === 'number' || typeof v === 'undefined', v => v)],
		new ApiCommandResult<modes.ILink[], vscode.DocumentLink[]>('A promise that resolves to an array of DocumentLink-instances.', value => value.map(typeConverters.DocumentLink.to))
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
	),
	// --- completions
	new ApiCommand(
		'vscode.executeCompletionItemProvider', '_executeCompletionItemProvider', 'Execute completion item provider.',
		[
			ApiCommandArgument.Uri,
			ApiCommandArgument.Position,
			new ApiCommandArgument('triggerCharacter', '(optional) Trigger completion when the user types the character, like `,` or `(`', v => typeof v === 'string' || typeof v === 'undefined', v => v),
			new ApiCommandArgument('itemResolveCount', '(optional) Number of completions to resolve (too large numbers slow down completions)', v => typeof v === 'number' || typeof v === 'undefined', v => v)
		],
		new ApiCommandResult<modes.CompletionList, vscode.CompletionList>('A promise that resolves to a CompletionList-instance.', (value, _args, converter) => {
			if (!value) {
				return new types.CompletionList([]);
			}
			const items = value.suggestions.map(suggestion => typeConverters.CompletionItem.to(suggestion, converter));
			return new types.CompletionList(items, value.incomplete);
		})
255 256 257 258 259 260 261 262 263 264 265 266
	),
	// --- signature help
	new ApiCommand(
		'vscode.executeSignatureHelpProvider', '_executeSignatureHelpProvider', 'Execute signature help provider.',
		[ApiCommandArgument.Uri, ApiCommandArgument.Position, new ApiCommandArgument('triggerCharacter', '(optional) Trigger signature help when the user types the character, like `,` or `(`', v => typeof v === 'string' || typeof v === 'undefined', v => v)],
		new ApiCommandResult<modes.SignatureHelp, vscode.SignatureHelp | undefined>('A promise that resolves to SignatureHelp.', value => {
			if (value) {
				return typeConverters.SignatureHelp.to(value);
			}
			return undefined;
		})
	),
267 268 269 270 271 272
];

//#endregion


//#region OLD world
J
Johannes Rieken 已提交
273

274
export class ExtHostApiCommands {
J
Johannes Rieken 已提交
275

276
	static register(commands: ExtHostCommands) {
277
		newCommands.forEach(command => command.register(commands));
278
		return new ExtHostApiCommands(commands).registerCommands();
279
	}
280

281
	private _commands: ExtHostCommands;
282
	private readonly _disposables = new DisposableStore();
283

284
	private constructor(commands: ExtHostCommands) {
285
		this._commands = commands;
J
Johannes Rieken 已提交
286
	}
287

J
Johannes Rieken 已提交
288
	registerCommands() {
289

290 291
		this._register('vscode.executeCodeActionProvider', this._executeCodeActionProvider, {
			description: 'Execute code action provider.',
J
Johannes Rieken 已提交
292 293
			args: [
				{ name: 'uri', description: 'Uri of a text document', constraint: URI },
294
				{ name: 'rangeOrSelection', description: 'Range in a text document. Some refactoring provider requires Selection object.', constraint: types.Range },
295
				{ name: 'kind', description: '(optional) Code action kind to return code actions for', constraint: (value: any) => !value || typeof value.value === 'string' },
296 297
				{ name: 'itemResolveCount', description: '(optional) Number of code actions to resolve (too large numbers slow down code actions)', constraint: (value: any) => value === undefined || typeof value === 'number' }

J
Johannes Rieken 已提交
298
			],
X
xzper 已提交
299
			returns: 'A promise that resolves to an array of Command-instances.'
300 301
		});
		this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider, {
302
			description: 'Execute CodeLens provider.',
J
Johannes Rieken 已提交
303
			args: [
304
				{ name: 'uri', description: 'Uri of a text document', constraint: URI },
305
				{ name: 'itemResolveCount', description: '(optional) Number of lenses that should be resolved and returned. Will only return resolved lenses, will impact performance)', constraint: (value: any) => value === undefined || typeof value === 'number' }
J
Johannes Rieken 已提交
306
			],
X
xzper 已提交
307
			returns: 'A promise that resolves to an array of CodeLens-instances.'
308
		});
309

310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
		this._register('vscode.executeDocumentColorProvider', this._executeDocumentColorProvider, {
			description: 'Execute document color provider.',
			args: [
				{ name: 'uri', description: 'Uri of a text document', constraint: URI },
			],
			returns: 'A promise that resolves to an array of ColorInformation objects.'
		});
		this._register('vscode.executeColorPresentationProvider', this._executeColorPresentationProvider, {
			description: 'Execute color presentation provider.',
			args: [
				{ name: 'color', description: 'The color to show and insert', constraint: types.Color },
				{ name: 'context', description: 'Context object with uri and range' }
			],
			returns: 'A promise that resolves to an array of ColorPresentation objects.'
		});
325

326 327 328 329 330 331
		this._register('vscode.resolveNotebookContentProviders', this._resolveNotebookContentProviders, {
			description: 'Resolve Notebook Content Providers',
			args: [],
			returns: 'A promise that resolves to an array of NotebookContentProvider static info objects.'
		});

332 333 334 335 336 337
		// -----------------------------------------------------------------
		// 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.
		// -----------------------------------------------------------------
338

339 340 341 342 343 344
		type ICommandHandler = (...args: any[]) => any;
		const adjustHandler = (handler: (executor: ICommandsExecutor, ...args: any[]) => any): ICommandHandler => {
			return (...args: any[]) => {
				return handler(this._commands, ...args);
			};
		};
I
isidor 已提交
345

346 347 348
		this._register(OpenFolderAPICommand.ID, adjustHandler(OpenFolderAPICommand.execute), {
			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: [
349
				{ 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 || URI.isUri(value) },
350
				{ 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`: Whether 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' }
351 352 353 354 355 356 357 358
			]
		});

		this._register(DiffAPICommand.ID, adjustHandler(DiffAPICommand.execute), {
			description: 'Opens the provided resources in the diff editor to compare their contents.',
			args: [
				{ name: 'left', description: 'Left-hand side resource of the diff editor', constraint: URI },
				{ name: 'right', description: 'Right-hand side resource of the diff editor', constraint: URI },
R
Rob Lourens 已提交
359
				{ name: 'title', description: '(optional) Human readable title for the diff editor', constraint: (v: any) => v === undefined || typeof v === 'string' },
360 361 362
				{ name: 'options', description: '(optional) Editor options, see vscode.TextDocumentShowOptions' }
			]
		});
363

364 365 366 367
		this._register(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute), {
			description: 'Opens the provided resource in the editor. Can be a text or binary file, or a http(s) url. If you need more control over the options for opening a text file, use vscode.window.showTextDocument instead.',
			args: [
				{ name: 'resource', description: 'Resource to open', constraint: URI },
R
Rob Lourens 已提交
368
				{ name: 'columnOrOptions', description: '(optional) Either the column in which to open or editor options, see vscode.TextDocumentShowOptions', constraint: (v: any) => v === undefined || typeof v === 'number' || typeof v === 'object' }
369 370
			]
		});
371

372 373 374
		this._register(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute), {
			description: 'Removes an entry with the given path from the recently opened list.',
			args: [
A
Alex Dima 已提交
375
				{ name: 'path', description: 'Path to remove from recently opened.', constraint: (value: any) => typeof value === 'string' }
376 377
			]
		});
378 379

		this._register(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute), {
380
			description: 'Sets the editor layout. The layout is described as object with an initial (optional) orientation (0 = horizontal, 1 = vertical) and an array of editor groups within. Each editor group can have a size and another array of editor groups that will be laid out orthogonal to the orientation. If editor group sizes are provided, their sum must be 1 to be applied per row or column. Example for a 2x2 grid: `{ orientation: 0, groups: [{ groups: [{}, {}], size: 0.5 }, { groups: [{}, {}], size: 0.5 }] }`',
381 382 383 384
			args: [
				{ name: 'layout', description: 'The editor layout to set.', constraint: (value: EditorGroupLayout) => typeof value === 'object' && Array.isArray(value.groups) }
			]
		});
385 386 387 388

		this._register(OpenIssueReporter.ID, adjustHandler(OpenIssueReporter.execute), {
			description: 'Opens the issue reporter with the provided extension id as the selected source',
			args: [
389
				{ name: 'extensionId', description: 'extensionId to report an issue on', constraint: (value: unknown) => typeof value === 'string' || (typeof value === 'object' && typeof (value as OpenIssueReporterArgs).extensionId === 'string') }
390 391
			]
		});
392 393 394 395
	}

	// --- command impl

J
Johannes Rieken 已提交
396 397 398
	/**
	 * @deprecated use the ApiCommand instead
	 */
399
	private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void {
400
		const disposable = this._commands.registerCommand(false, id, handler, this, description);
401
		this._disposables.add(disposable);
J
Johannes Rieken 已提交
402 403
	}

J
Johannes Rieken 已提交
404
	private _executeDocumentColorProvider(resource: URI): Promise<types.ColorInformation[]> {
405 406 407 408 409
		const args = {
			resource
		};
		return this._commands.executeCommand<IRawColorInfo[]>('_executeDocumentColorProvider', args).then(result => {
			if (result) {
410
				return result.map(ci => ({ range: typeConverters.Range.to(ci.range), color: typeConverters.Color.to(ci.color) }));
411 412 413 414 415
			}
			return [];
		});
	}

416
	private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range; }): Promise<types.ColorPresentation[]> {
417 418 419
		const args = {
			resource: context.uri,
			color: typeConverters.Color.from(color),
420
			range: typeConverters.Range.from(context.range),
421 422 423 424 425 426 427 428 429
		};
		return this._commands.executeCommand<modes.IColorPresentation[]>('_executeColorPresentationProvider', args).then(result => {
			if (result) {
				return result.map(typeConverters.ColorPresentation.to);
			}
			return [];
		});
	}

430

431
	private _executeCodeActionProvider(resource: URI, rangeOrSelection: types.Range | types.Selection, kind?: string, itemResolveCount?: number): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> {
432 433
		const args = {
			resource,
434 435 436
			rangeOrSelection: types.Selection.isSelection(rangeOrSelection)
				? typeConverters.Selection.from(rangeOrSelection)
				: typeConverters.Range.from(rangeOrSelection),
437 438
			kind,
			itemResolveCount,
439
		};
M
Matt Bierner 已提交
440 441
		return this._commands.executeCommand<CustomCodeAction[]>('_executeCodeActionProvider', args)
			.then(tryMapWith(codeAction => {
442
				if (codeAction._isSynthetic) {
443 444 445
					if (!codeAction.command) {
						throw new Error('Synthetic code actions must have a command');
					}
446 447 448 449
					return this._commands.converter.fromInternal(codeAction.command);
				} else {
					const ret = new types.CodeAction(
						codeAction.title,
450
						codeAction.kind ? new types.CodeActionKind(codeAction.kind) : undefined
451
					);
452 453
					if (codeAction.edit) {
						ret.edit = typeConverters.WorkspaceEdit.to(codeAction.edit);
M
Matt Bierner 已提交
454
					}
J
Johannes Rieken 已提交
455 456 457
					if (codeAction.command) {
						ret.command = this._commands.converter.fromInternal(codeAction.command);
					}
458
					ret.isPreferred = codeAction.isPreferred;
459 460
					return ret;
				}
M
Matt Bierner 已提交
461
			}));
462
	}
J
Johannes Rieken 已提交
463

464
	private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Promise<vscode.CodeLens[] | undefined> {
465
		const args = { resource, itemResolveCount };
466
		return this._commands.executeCommand<modes.CodeLens[]>('_executeCodeLensProvider', args)
M
Matt Bierner 已提交
467 468
			.then(tryMapWith(item => {
				return new types.CodeLens(
469
					typeConverters.Range.to(item.range),
470
					item.command ? this._commands.converter.fromInternal(item.command) : undefined);
M
Matt Bierner 已提交
471
			}));
472
	}
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494

	private _resolveNotebookContentProviders(): Promise<{
		viewType: string;
		displayName: string;
		filenamePattern: vscode.NotebookFilenamePattern[];
		options: vscode.NotebookDocumentContentOptions;
	}[] | undefined> {
		return this._commands.executeCommand<{
			viewType: string;
			displayName: string;
			options: { transientOutputs: boolean; transientMetadata: TransientMetadata };
			filenamePattern: (string | types.RelativePattern | { include: string | types.RelativePattern, exclude: string | types.RelativePattern })[]
		}[]>('_resolveNotebookContentProvider')
			.then(tryMapWith(item => {
				return {
					viewType: item.viewType,
					displayName: item.displayName,
					options: { transientOutputs: item.options.transientOutputs, transientMetadata: item.options.transientMetadata },
					filenamePattern: item.filenamePattern.map(pattern => typeConverters.NotebookExclusiveDocumentPattern.to(pattern))
				};
			}));
	}
495
}
M
Matt Bierner 已提交
496 497 498 499 500 501 502 503

function tryMapWith<T, R>(f: (x: T) => R) {
	return (value: T[]) => {
		if (Array.isArray(value)) {
			return value.map(f);
		}
		return undefined;
	};
504
}
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519

function mapLocationOrLocationLink(values: (modes.Location | modes.LocationLink)[]): (types.Location | vscode.LocationLink)[] | undefined {
	if (!Array.isArray(values)) {
		return undefined;
	}
	const result: (types.Location | vscode.LocationLink)[] = [];
	for (const item of values) {
		if (modes.isLocationLink(item)) {
			result.push(typeConverters.DefinitionLink.to(item));
		} else {
			result.push(typeConverters.location.to(item));
		}
	}
	return result;
}