diff --git a/client-node-tests/package.json b/client-node-tests/package.json
index 8927303a5..995ab944f 100644
--- a/client-node-tests/package.json
+++ b/client-node-tests/package.json
@@ -44,5 +44,6 @@
"sinon": "^11.1.2",
"uuid": "^8.3.2",
"vscode-test": "^1.6.1"
- }
+ },
+ "enabledApiProposals": ["formatMultipleRanges"]
}
diff --git a/client-node-tests/src/integration.test.ts b/client-node-tests/src/integration.test.ts
index 962405ca3..edf90a5a2 100644
--- a/client-node-tests/src/integration.test.ts
+++ b/client-node-tests/src/integration.test.ts
@@ -241,7 +241,9 @@ suite('Client integration', () => {
resolveProvider: true
},
documentFormattingProvider: true,
- documentRangeFormattingProvider: true,
+ documentRangeFormattingProvider: {
+ rangesSupport: true
+ },
documentOnTypeFormattingProvider: {
firstTriggerCharacter: ':'
},
diff --git a/client-node-tests/src/servers/testServer.ts b/client-node-tests/src/servers/testServer.ts
index a6fc5bc21..c7c134ad8 100644
--- a/client-node-tests/src/servers/testServer.ts
+++ b/client-node-tests/src/servers/testServer.ts
@@ -73,7 +73,9 @@ connection.onInitialize((params: InitializeParams): any => {
resolveProvider: true
},
documentFormattingProvider: true,
- documentRangeFormattingProvider: true,
+ documentRangeFormattingProvider: {
+ rangesSupport: true
+ },
documentOnTypeFormattingProvider: {
firstTriggerCharacter: ':'
},
diff --git a/client/package.json b/client/package.json
index ab127bd41..8b8e3b1e1 100644
--- a/client/package.json
+++ b/client/package.json
@@ -15,7 +15,7 @@
"bugs": {
"url": "https://github.com/Microsoft/vscode-languageserver-node/issues"
},
- "enabledApiProposals": [],
+ "enabledApiProposals": ["formatMultipleRanges"],
"main": "./lib/node/main.js",
"browser": {
"./lib/node/main.js": "./lib/browser/main.js"
diff --git a/client/src/common/codeConverter.ts b/client/src/common/codeConverter.ts
index f8ae76717..1cb3b07a0 100644
--- a/client/src/common/codeConverter.ts
+++ b/client/src/common/codeConverter.ts
@@ -86,6 +86,8 @@ export interface Converter {
asRange(value: code.Range): proto.Range;
asRange(value: code.Range | undefined | null): proto.Range | undefined | null;
+ asRanges(values: readonly code.Range[]): proto.Range[];
+
asLocation(value: null): null;
asLocation(value: undefined): undefined;
asLocation(value: code.Location): proto.Location;
@@ -441,6 +443,10 @@ export function createConverter(uriConverter?: URIConverter): Converter {
return { start: asPosition(value.start), end: asPosition(value.end) };
}
+ function asRanges(values: readonly code.Range[]): proto.Range[] {
+ return values.map(asRange as (item: code.Range) => proto.Range);
+ }
+
function asLocation(value: code.Location): proto.Location;
function asLocation(value: undefined): undefined;
function asLocation(value: null): null;
@@ -959,6 +965,7 @@ export function createConverter(uriConverter?: URIConverter): Converter {
asSignatureHelpParams,
asWorkerPosition,
asRange,
+ asRanges,
asPosition,
asPositions,
asPositionsSync,
diff --git a/client/src/common/formatting.ts b/client/src/common/formatting.ts
index f55c3e1d0..5a56d7a34 100644
--- a/client/src/common/formatting.ts
+++ b/client/src/common/formatting.ts
@@ -2,6 +2,7 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
+///
import {
languages as Languages, Disposable, TextDocument, ProviderResult, Range as VRange, Position as VPosition, TextEdit as VTextEdit, FormattingOptions as VFormattingOptions,
@@ -9,7 +10,7 @@ import {
} from 'vscode';
import {
- ClientCapabilities, CancellationToken, ServerCapabilities, DocumentSelector, DocumentHighlightRegistrationOptions, DocumentFormattingOptions, DocumentFormattingRequest, TextDocumentRegistrationOptions, DocumentFormattingParams, DocumentRangeFormattingRegistrationOptions, DocumentRangeFormattingOptions, DocumentRangeFormattingRequest, DocumentRangeFormattingParams, DocumentOnTypeFormattingOptions, DocumentOnTypeFormattingRegistrationOptions, DocumentOnTypeFormattingRequest, DocumentOnTypeFormattingParams} from 'vscode-languageserver-protocol';
+ ClientCapabilities, CancellationToken, ServerCapabilities, DocumentSelector, DocumentHighlightRegistrationOptions, DocumentFormattingOptions, DocumentFormattingRequest, TextDocumentRegistrationOptions, DocumentFormattingParams, DocumentRangeFormattingRegistrationOptions, DocumentRangeFormattingOptions, DocumentRangeFormattingRequest, DocumentRangeFormattingParams, DocumentRangesFormattingRequest, DocumentRangesFormattingParams, DocumentOnTypeFormattingOptions, DocumentOnTypeFormattingRegistrationOptions, DocumentOnTypeFormattingRequest, DocumentOnTypeFormattingParams} from 'vscode-languageserver-protocol';
import * as UUID from './utils/uuid';
@@ -36,6 +37,10 @@ export interface ProvideDocumentRangeFormattingEditsSignature {
(this: void, document: TextDocument, range: VRange, options: VFormattingOptions, token: CancellationToken): ProviderResult;
}
+export interface ProvideDocumentRangesFormattingEditsSignature {
+ (this: void, document: TextDocument, ranges: VRange[], options: VFormattingOptions, token: CancellationToken): ProviderResult;
+}
+
export interface ProvideOnTypeFormattingEditsSignature {
(this: void, document: TextDocument, position: VPosition, ch: string, options: VFormattingOptions, token: CancellationToken): ProviderResult;
}
@@ -44,6 +49,7 @@ export interface ProvideOnTypeFormattingEditsSignature {
export interface FormattingMiddleware {
provideDocumentFormattingEdits?: (this: void, document: TextDocument, options: VFormattingOptions, token: CancellationToken, next: ProvideDocumentFormattingEditsSignature) => ProviderResult;
provideDocumentRangeFormattingEdits?: (this: void, document: TextDocument, range: VRange, options: VFormattingOptions, token: CancellationToken, next: ProvideDocumentRangeFormattingEditsSignature) => ProviderResult;
+ provideDocumentRangesFormattingEdits?: (this: void, document: TextDocument, range: VRange[], options: VFormattingOptions, token: CancellationToken, next: ProvideDocumentRangesFormattingEditsSignature) => ProviderResult;
provideOnTypeFormattingEdits?: (this: void, document: TextDocument, position: VPosition, ch: string, options: VFormattingOptions, token: CancellationToken, next: ProvideOnTypeFormattingEditsSignature) => ProviderResult;
}
@@ -102,7 +108,9 @@ export class DocumentRangeFormattingFeature extends TextDocumentLanguageFeature<
}
public fillClientCapabilities(capabilities: ClientCapabilities): void {
- ensure(ensure(capabilities, 'textDocument')!, 'rangeFormatting')!.dynamicRegistration = true;
+ const capability = ensure(ensure(capabilities, 'textDocument')!, 'rangeFormatting')!;
+ capability.dynamicRegistration = true;
+ capability.rangesSupport = true;
}
public initialize(capabilities: ServerCapabilities, documentSelector: DocumentSelector): void {
@@ -113,7 +121,7 @@ export class DocumentRangeFormattingFeature extends TextDocumentLanguageFeature<
this.register({ id: UUID.generateUuid(), registerOptions: options });
}
- protected registerLanguageProvider(options: TextDocumentRegistrationOptions): [Disposable, DocumentRangeFormattingEditProvider] {
+ protected registerLanguageProvider(options: DocumentRangeFormattingRegistrationOptions): [Disposable, DocumentRangeFormattingEditProvider] {
const selector = options.documentSelector!;
const provider: DocumentRangeFormattingEditProvider = {
provideDocumentRangeFormattingEdits: (document, range, options, token) => {
@@ -139,6 +147,31 @@ export class DocumentRangeFormattingFeature extends TextDocumentLanguageFeature<
: provideDocumentRangeFormattingEdits(document, range, options, token);
}
};
+
+ if (options.rangesSupport) {
+ provider.provideDocumentRangesFormattingEdits = (document, ranges, options, token) => {
+ const client = this._client;
+ const provideDocumentRangesFormattingEdits: ProvideDocumentRangesFormattingEditsSignature = (document, ranges, options, token) => {
+ const params: DocumentRangesFormattingParams = {
+ textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document),
+ ranges: client.code2ProtocolConverter.asRanges(ranges),
+ options: client.code2ProtocolConverter.asFormattingOptions(options, FileFormattingOptions.fromConfiguration(document))
+ };
+ return client.sendRequest(DocumentRangesFormattingRequest.type, params, token).then((result) => {
+ if (token.isCancellationRequested) {
+ return null;
+ }
+ return client.protocol2CodeConverter.asTextEdits(result, token);
+ }, (error) => {
+ return client.handleFailedRequest(DocumentRangesFormattingRequest.type, token, error, null);
+ });
+ };
+ const middleware = client.middleware;
+ return middleware.provideDocumentRangesFormattingEdits
+ ? middleware.provideDocumentRangesFormattingEdits(document, ranges, options, token, provideDocumentRangesFormattingEdits)
+ : provideDocumentRangesFormattingEdits(document, ranges, options, token);
+ };
+ }
return [Languages.registerDocumentRangeFormattingEditProvider(this._client.protocol2CodeConverter.asDocumentSelector(selector), provider), provider];
}
}
diff --git a/client/src/node/main.ts b/client/src/node/main.ts
index dac300f3a..859e0cea5 100644
--- a/client/src/node/main.ts
+++ b/client/src/node/main.ts
@@ -23,7 +23,7 @@ import semverSatisfies = require('semver/functions/satisfies');
export * from 'vscode-languageserver-protocol/node';
export * from '../common/api';
-const REQUIRED_VSCODE_VERSION = '^1.68.0'; // do not change format, updated by `updateVSCode` script
+const REQUIRED_VSCODE_VERSION = '^1.78.0'; // do not change format, updated by `updateVSCode` script
export enum TransportKind {
stdio,
diff --git a/client/typings/vscode.proposed.formatMultipleRanges.d.ts b/client/typings/vscode.proposed.formatMultipleRanges.d.ts
new file mode 100644
index 000000000..702bc14b4
--- /dev/null
+++ b/client/typings/vscode.proposed.formatMultipleRanges.d.ts
@@ -0,0 +1,29 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+declare module 'vscode' {
+
+ // https://github.com/microsoft/vscode/issues/158776
+
+
+ export interface DocumentRangeFormattingEditProvider {
+
+ /**
+ * Provide formatting edits for multiple ranges in a document.
+ *
+ * The given ranges are hints and providers can decide to format a smaller
+ * or larger range. Often this is done by adjusting the start and end
+ * of the range to full syntax nodes.
+ *
+ * @param document The document in which the command was invoked.
+ * @param ranges The ranges which should be formatted.
+ * @param options Options controlling formatting.
+ * @param token A cancellation token.
+ * @return A set of text edits or a thenable that resolves to such. The lack of a result can be
+ * signaled by returning `undefined`, `null`, or an empty array.
+ */
+ provideDocumentRangesFormattingEdits?(document: TextDocument, ranges: Range[], options: FormattingOptions, token: CancellationToken): ProviderResult;
+ }
+}
diff --git a/protocol/metaModel.json b/protocol/metaModel.json
index 8b8303746..e2190dc2e 100644
--- a/protocol/metaModel.json
+++ b/protocol/metaModel.json
@@ -1706,6 +1706,37 @@
},
"documentation": "A request to format a range in a document."
},
+ {
+ "method": "textDocument/rangesFormatting",
+ "result": {
+ "kind": "or",
+ "items": [
+ {
+ "kind": "array",
+ "element": {
+ "kind": "reference",
+ "name": "TextEdit"
+ }
+ },
+ {
+ "kind": "base",
+ "name": "null"
+ }
+ ]
+ },
+ "messageDirection": "clientToServer",
+ "params": {
+ "kind": "reference",
+ "name": "DocumentRangesFormattingParams"
+ },
+ "registrationOptions": {
+ "kind": "reference",
+ "name": "DocumentRangeFormattingRegistrationOptions"
+ },
+ "documentation": "A request to format ranges in a document.\n\n@since 3.18.0\n@proposed",
+ "since": "3.18.0",
+ "proposed": true
+ },
{
"method": "textDocument/onTypeFormatting",
"result": {
@@ -5982,6 +6013,47 @@
],
"documentation": "Registration options for a {@link DocumentRangeFormattingRequest}."
},
+ {
+ "name": "DocumentRangesFormattingParams",
+ "properties": [
+ {
+ "name": "textDocument",
+ "type": {
+ "kind": "reference",
+ "name": "TextDocumentIdentifier"
+ },
+ "documentation": "The document to format."
+ },
+ {
+ "name": "ranges",
+ "type": {
+ "kind": "array",
+ "element": {
+ "kind": "reference",
+ "name": "Range"
+ }
+ },
+ "documentation": "The ranges to format"
+ },
+ {
+ "name": "options",
+ "type": {
+ "kind": "reference",
+ "name": "FormattingOptions"
+ },
+ "documentation": "The format options"
+ }
+ ],
+ "mixins": [
+ {
+ "kind": "reference",
+ "name": "WorkDoneProgressParams"
+ }
+ ],
+ "documentation": "The parameters of a {@link DocumentRangesFormattingRequest}.\n\n@since 3.18.0\n@proposed",
+ "since": "3.18.0",
+ "proposed": true
+ },
{
"name": "DocumentOnTypeFormattingParams",
"properties": [
@@ -9477,7 +9549,19 @@
},
{
"name": "DocumentRangeFormattingOptions",
- "properties": [],
+ "properties": [
+ {
+ "name": "rangesSupport",
+ "type": {
+ "kind": "base",
+ "name": "boolean"
+ },
+ "optional": true,
+ "documentation": "Whether the server supports formatting multiple ranges at once.\n\n@since 3.18.0\n@proposed",
+ "since": "3.18.0",
+ "proposed": true
+ }
+ ],
"mixins": [
{
"kind": "reference",
@@ -12193,6 +12277,17 @@
},
"optional": true,
"documentation": "Whether range formatting supports dynamic registration."
+ },
+ {
+ "name": "rangesSupport",
+ "type": {
+ "kind": "base",
+ "name": "boolean"
+ },
+ "optional": true,
+ "documentation": "Whether the client supports formatting multiple ranges at once.\n\n@since 3.18.0\n@proposed",
+ "since": "3.18.0",
+ "proposed": true
}
],
"documentation": "Client capabilities of a {@link DocumentRangeFormattingRequest}."
diff --git a/protocol/src/common/protocol.ts b/protocol/src/common/protocol.ts
index ced2a8bd2..7143b0257 100644
--- a/protocol/src/common/protocol.ts
+++ b/protocol/src/common/protocol.ts
@@ -3426,6 +3426,14 @@ export interface DocumentRangeFormattingClientCapabilities {
* Whether range formatting supports dynamic registration.
*/
dynamicRegistration?: boolean;
+
+ /**
+ * Whether the client supports formatting multiple ranges at once.
+ *
+ * @since 3.18.0
+ * @proposed
+ */
+ rangesSupport?: boolean;
}
/**
@@ -3448,10 +3456,40 @@ export interface DocumentRangeFormattingParams extends WorkDoneProgressParams {
options: FormattingOptions;
}
+/**
+ * The parameters of a {@link DocumentRangesFormattingRequest}.
+ *
+ * @since 3.18.0
+ * @proposed
+ */
+export interface DocumentRangesFormattingParams extends WorkDoneProgressParams {
+ /**
+ * The document to format.
+ */
+ textDocument: TextDocumentIdentifier;
+
+ /**
+ * The ranges to format
+ */
+ ranges: Range[];
+
+ /**
+ * The format options
+ */
+ options: FormattingOptions;
+}
+
/**
* Provider options for a {@link DocumentRangeFormattingRequest}.
*/
export interface DocumentRangeFormattingOptions extends WorkDoneProgressOptions {
+ /**
+ * Whether the server supports formatting multiple ranges at once.
+ *
+ * @since 3.18.0
+ * @proposed
+ */
+ rangesSupport?: boolean;
}
/**
@@ -3469,6 +3507,18 @@ export namespace DocumentRangeFormattingRequest {
export const type = new ProtocolRequestType(method);
}
+/**
+ * A request to format ranges in a document.
+ *
+ * @since 3.18.0
+ * @proposed
+ */
+export namespace DocumentRangesFormattingRequest {
+ export const method: 'textDocument/rangesFormatting' = 'textDocument/rangesFormatting';
+ export const messageDirection: MessageDirection = MessageDirection.clientToServer;
+ export const type = new ProtocolRequestType(method);
+}
+
/**
* Client capabilities of a {@link DocumentOnTypeFormattingRequest}.
*/
diff --git a/testbed/server/src/server.ts b/testbed/server/src/server.ts
index 385e35ef5..96ab34818 100644
--- a/testbed/server/src/server.ts
+++ b/testbed/server/src/server.ts
@@ -143,7 +143,9 @@ connection.onInitialize((params, cancel, progress): Thenable |
resolveProvider: true
},
documentFormattingProvider: true,
- documentRangeFormattingProvider: true,
+ documentRangeFormattingProvider: {
+ rangesSupport: true
+ },
documentOnTypeFormattingProvider: {
firstTriggerCharacter: ';',
moreTriggerCharacter: ['{', '\n']