extHostApiCommands.ts 26.8 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';
J
Johannes Rieken 已提交
15 16
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
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[]) => 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);
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
];

//#endregion


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

246
export class ExtHostApiCommands {
J
Johannes Rieken 已提交
247

248
	static register(commands: ExtHostCommands) {
249
		newCommands.forEach(command => command.register(commands));
250
		return new ExtHostApiCommands(commands).registerCommands();
251
	}
252

253
	private _commands: ExtHostCommands;
254
	private readonly _disposables = new DisposableStore();
255

256
	private constructor(commands: ExtHostCommands) {
257
		this._commands = commands;
J
Johannes Rieken 已提交
258
	}
259

J
Johannes Rieken 已提交
260
	registerCommands() {
261 262
		this._register('vscode.executeSignatureHelpProvider', this._executeSignatureHelpProvider, {
			description: 'Execute signature help provider.',
J
Johannes Rieken 已提交
263 264
			args: [
				{ name: 'uri', description: 'Uri of a text document', constraint: URI },
265
				{ name: 'position', description: 'Position in a text document', constraint: types.Position },
R
Rob Lourens 已提交
266
				{ name: 'triggerCharacter', description: '(optional) Trigger signature help when the user types the character, like `,` or `(`', constraint: (value: any) => value === undefined || typeof value === 'string' }
J
Johannes Rieken 已提交
267 268
			],
			returns: 'A promise that resolves to SignatureHelp.'
269 270 271
		});
		this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider, {
			description: 'Execute completion item provider.',
J
Johannes Rieken 已提交
272 273
			args: [
				{ name: 'uri', description: 'Uri of a text document', constraint: URI },
274
				{ name: 'position', description: 'Position in a text document', constraint: types.Position },
R
Rob Lourens 已提交
275 276
				{ name: 'triggerCharacter', description: '(optional) Trigger completion when the user types the character, like `,` or `(`', constraint: (value: any) => value === undefined || typeof value === 'string' },
				{ name: 'itemResolveCount', description: '(optional) Number of completions to resolve (too large numbers slow down completions)', constraint: (value: any) => value === undefined || typeof value === 'number' }
J
Johannes Rieken 已提交
277
			],
278
			returns: 'A promise that resolves to a CompletionList-instance.'
279 280 281
		});
		this._register('vscode.executeCodeActionProvider', this._executeCodeActionProvider, {
			description: 'Execute code action provider.',
J
Johannes Rieken 已提交
282 283
			args: [
				{ name: 'uri', description: 'Uri of a text document', constraint: URI },
284
				{ name: 'rangeOrSelection', description: 'Range in a text document. Some refactoring provider requires Selection object.', constraint: types.Range },
285
				{ name: 'kind', description: '(optional) Code action kind to return code actions for', constraint: (value: any) => !value || typeof value.value === 'string' },
286 287
				{ 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 已提交
288
			],
X
xzper 已提交
289
			returns: 'A promise that resolves to an array of Command-instances.'
290 291
		});
		this._register('vscode.executeCodeLensProvider', this._executeCodeLensProvider, {
292
			description: 'Execute CodeLens provider.',
J
Johannes Rieken 已提交
293
			args: [
294
				{ name: 'uri', description: 'Uri of a text document', constraint: URI },
295
				{ 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 已提交
296
			],
X
xzper 已提交
297
			returns: 'A promise that resolves to an array of CodeLens-instances.'
298
		});
299

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
		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.'
		});
315

316 317 318 319 320 321
		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.'
		});

322 323 324 325 326 327
		// -----------------------------------------------------------------
		// 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.
		// -----------------------------------------------------------------
328

329 330 331 332 333 334
		type ICommandHandler = (...args: any[]) => any;
		const adjustHandler = (handler: (executor: ICommandsExecutor, ...args: any[]) => any): ICommandHandler => {
			return (...args: any[]) => {
				return handler(this._commands, ...args);
			};
		};
I
isidor 已提交
335

336 337 338
		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: [
339
				{ 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) },
340
				{ 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' }
341 342 343 344 345 346 347 348
			]
		});

		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 已提交
349
				{ name: 'title', description: '(optional) Human readable title for the diff editor', constraint: (v: any) => v === undefined || typeof v === 'string' },
350 351 352
				{ name: 'options', description: '(optional) Editor options, see vscode.TextDocumentShowOptions' }
			]
		});
353

354 355 356 357
		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 已提交
358
				{ 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' }
359 360
			]
		});
361

362 363 364
		this._register(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute), {
			description: 'Removes an entry with the given path from the recently opened list.',
			args: [
A
Alex Dima 已提交
365
				{ name: 'path', description: 'Path to remove from recently opened.', constraint: (value: any) => typeof value === 'string' }
366 367
			]
		});
368 369

		this._register(SetEditorLayoutAPICommand.ID, adjustHandler(SetEditorLayoutAPICommand.execute), {
370
			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 }] }`',
371 372 373 374
			args: [
				{ name: 'layout', description: 'The editor layout to set.', constraint: (value: EditorGroupLayout) => typeof value === 'object' && Array.isArray(value.groups) }
			]
		});
375 376 377 378

		this._register(OpenIssueReporter.ID, adjustHandler(OpenIssueReporter.execute), {
			description: 'Opens the issue reporter with the provided extension id as the selected source',
			args: [
379
				{ name: 'extensionId', description: 'extensionId to report an issue on', constraint: (value: unknown) => typeof value === 'string' || (typeof value === 'object' && typeof (value as OpenIssueReporterArgs).extensionId === 'string') }
380 381
			]
		});
382 383 384 385
	}

	// --- command impl

386
	private _register(id: string, handler: (...args: any[]) => any, description?: ICommandHandlerDescription): void {
387
		const disposable = this._commands.registerCommand(false, id, handler, this, description);
388
		this._disposables.add(disposable);
J
Johannes Rieken 已提交
389 390
	}

391
	private _executeSignatureHelpProvider(resource: URI, position: types.Position, triggerCharacter: string): Promise<types.SignatureHelp | undefined> {
392 393
		const args = {
			resource,
394
			position: position && typeConverters.Position.from(position),
395 396
			triggerCharacter
		};
397
		return this._commands.executeCommand<modes.SignatureHelp>('_executeSignatureHelpProvider', args).then(value => {
398 399 400
			if (value) {
				return typeConverters.SignatureHelp.to(value);
			}
M
Matt Bierner 已提交
401
			return undefined;
402 403
		});
	}
404

405
	private _executeCompletionItemProvider(resource: URI, position: types.Position, triggerCharacter: string, maxItemsToResolve: number): Promise<types.CompletionList | undefined> {
406 407
		const args = {
			resource,
408
			position: position && typeConverters.Position.from(position),
409 410
			triggerCharacter,
			maxItemsToResolve
411
		};
412
		return this._commands.executeCommand<modes.CompletionList>('_executeCompletionItemProvider', args).then(result => {
413
			if (result) {
414
				const items = result.suggestions.map(suggestion => typeConverters.CompletionItem.to(suggestion, this._commands.converter));
415
				return new types.CompletionList(items, result.incomplete);
416
			}
M
Matt Bierner 已提交
417
			return undefined;
418 419 420
		});
	}

J
Johannes Rieken 已提交
421
	private _executeDocumentColorProvider(resource: URI): Promise<types.ColorInformation[]> {
422 423 424 425 426
		const args = {
			resource
		};
		return this._commands.executeCommand<IRawColorInfo[]>('_executeDocumentColorProvider', args).then(result => {
			if (result) {
427
				return result.map(ci => ({ range: typeConverters.Range.to(ci.range), color: typeConverters.Color.to(ci.color) }));
428 429 430 431 432
			}
			return [];
		});
	}

433
	private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range; }): Promise<types.ColorPresentation[]> {
434 435 436
		const args = {
			resource: context.uri,
			color: typeConverters.Color.from(color),
437
			range: typeConverters.Range.from(context.range),
438 439 440 441 442 443 444 445 446
		};
		return this._commands.executeCommand<modes.IColorPresentation[]>('_executeColorPresentationProvider', args).then(result => {
			if (result) {
				return result.map(typeConverters.ColorPresentation.to);
			}
			return [];
		});
	}

447

448
	private _executeCodeActionProvider(resource: URI, rangeOrSelection: types.Range | types.Selection, kind?: string, itemResolveCount?: number): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> {
449 450
		const args = {
			resource,
451 452 453
			rangeOrSelection: types.Selection.isSelection(rangeOrSelection)
				? typeConverters.Selection.from(rangeOrSelection)
				: typeConverters.Range.from(rangeOrSelection),
454 455
			kind,
			itemResolveCount,
456
		};
M
Matt Bierner 已提交
457 458
		return this._commands.executeCommand<CustomCodeAction[]>('_executeCodeActionProvider', args)
			.then(tryMapWith(codeAction => {
459
				if (codeAction._isSynthetic) {
460 461 462
					if (!codeAction.command) {
						throw new Error('Synthetic code actions must have a command');
					}
463 464 465 466
					return this._commands.converter.fromInternal(codeAction.command);
				} else {
					const ret = new types.CodeAction(
						codeAction.title,
467
						codeAction.kind ? new types.CodeActionKind(codeAction.kind) : undefined
468
					);
469 470
					if (codeAction.edit) {
						ret.edit = typeConverters.WorkspaceEdit.to(codeAction.edit);
M
Matt Bierner 已提交
471
					}
J
Johannes Rieken 已提交
472 473 474
					if (codeAction.command) {
						ret.command = this._commands.converter.fromInternal(codeAction.command);
					}
475
					ret.isPreferred = codeAction.isPreferred;
476 477
					return ret;
				}
M
Matt Bierner 已提交
478
			}));
479
	}
J
Johannes Rieken 已提交
480

481
	private _executeCodeLensProvider(resource: URI, itemResolveCount: number): Promise<vscode.CodeLens[] | undefined> {
482
		const args = { resource, itemResolveCount };
483
		return this._commands.executeCommand<modes.CodeLens[]>('_executeCodeLensProvider', args)
M
Matt Bierner 已提交
484 485
			.then(tryMapWith(item => {
				return new types.CodeLens(
486
					typeConverters.Range.to(item.range),
487
					item.command ? this._commands.converter.fromInternal(item.command) : undefined);
M
Matt Bierner 已提交
488
			}));
489
	}
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511

	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))
				};
			}));
	}
512
}
M
Matt Bierner 已提交
513 514 515 516 517 518 519 520

function tryMapWith<T, R>(f: (x: T) => R) {
	return (value: T[]) => {
		if (Array.isArray(value)) {
			return value.map(f);
		}
		return undefined;
	};
521
}
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536

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;
}