HTML, CSS, JSON language servers don't provide the documentFormattingProvider capability. Fixes #122994

This commit is contained in:
Martin Aeschlimann 2021-05-07 17:03:12 +02:00
parent dbed1fbe95
commit 81643c948f
No known key found for this signature in database
GPG key ID: 2609A01E695523E3
2 changed files with 75 additions and 29 deletions

View file

@ -5,9 +5,9 @@
import {
Connection, TextDocuments, InitializeParams, InitializeResult, RequestType,
DocumentRangeFormattingRequest, Disposable, DocumentSelector, TextDocumentPositionParams, ServerCapabilities,
DocumentRangeFormattingRequest, Disposable, TextDocumentPositionParams, ServerCapabilities,
ConfigurationRequest, ConfigurationParams, DidChangeWorkspaceFoldersNotification,
DocumentColorRequest, ColorPresentationRequest, TextDocumentSyncKind, NotificationType, RequestType0
DocumentColorRequest, ColorPresentationRequest, TextDocumentSyncKind, NotificationType, RequestType0, DocumentFormattingRequest, FormattingOptions, TextEdit
} from 'vscode-languageserver';
import {
getLanguageModes, LanguageModes, Settings, TextDocument, Position, Diagnostic, WorkspaceFolder, ColorInformation,
@ -152,7 +152,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['.', ':', '<', '"', '=', '/'] } : undefined,
hoverProvider: true,
documentHighlightProvider: true,
documentRangeFormattingProvider: initializationOptions?.provideFormatter === true,
documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true,
documentFormattingProvider: params.initializationOptions?.provideFormatter === true,
documentLinkProvider: { resolveProvider: false },
documentSymbolProvider: true,
definitionProvider: true,
@ -188,7 +189,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
}
});
let formatterRegistration: Thenable<Disposable> | null = null;
let formatterRegistrations: Thenable<Disposable>[] | null = null;
// The settings have changed. Is send on server activation as well.
connection.onDidChangeConfiguration((change) => {
@ -200,13 +201,16 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
if (dynamicFormatterRegistration) {
const enableFormatter = globalSettings && globalSettings.html && globalSettings.html.format && globalSettings.html.format.enable;
if (enableFormatter) {
if (!formatterRegistration) {
const documentSelector: DocumentSelector = [{ language: 'html' }, { language: 'handlebars' }];
formatterRegistration = connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector });
if (!formatterRegistrations) {
const documentSelector = [{ language: 'html' }, { language: 'handlebars' }];
formatterRegistrations = [
connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector }),
connection.client.register(DocumentFormattingRequest.type, { documentSelector })
];
}
} else if (formatterRegistration) {
formatterRegistration.then(r => r.dispose());
formatterRegistration = null;
} else if (formatterRegistrations) {
formatterRegistrations.forEach(p => p.then(r => r.dispose()));
formatterRegistrations = null;
}
}
});
@ -381,6 +385,29 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
}, null, `Error while computing signature help for ${signatureHelpParms.textDocument.uri}`, token);
});
async function onFormat(textDocument: TextDocumentIdentifier, range: Range | undefined, options: FormattingOptions): Promise<TextEdit[]> {
const document = documents.get(textDocument.uri);
if (document) {
let settings = await getDocumentSettings(document, () => true);
if (!settings) {
settings = globalSettings;
}
const unformattedTags: string = settings && settings.html && settings.html.format && settings.html.format.unformatted || '';
const enabledModes = { css: !unformattedTags.match(/\bstyle\b/), javascript: !unformattedTags.match(/\bscript\b/) };
return format(languageModes, document, range ?? getFullRange(document), options, settings, enabledModes);
}
return [];
}
connection.onDocumentRangeFormatting((formatParams, token) => {
return runSafe(() => onFormat(formatParams.textDocument, formatParams.range, formatParams.options), [], `Error while formatting range for ${formatParams.textDocument.uri}`, token);
});
connection.onDocumentFormatting((formatParams, token) => {
return runSafe(() => onFormat(formatParams.textDocument, undefined, formatParams.options), [], `Error while formatting ${formatParams.textDocument.uri}`, token);
});
connection.onDocumentRangeFormatting(async (formatParams, token) => {
return runSafe(async () => {
const document = documents.get(formatParams.textDocument.uri);
@ -561,3 +588,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
// Listen on the connection
connection.listen();
}
function getFullRange(document: TextDocument): Range {
return Range.create(Position.create(0, 0), document.positionAt(document.getText().length));
}

View file

@ -6,7 +6,7 @@
import {
Connection,
TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType,
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit
DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions
} from 'vscode-languageserver';
import { formatError, runSafe, runSafeAsync } from './utils/runner';
@ -138,6 +138,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
hoverProvider: true,
documentSymbolProvider: true,
documentRangeFormattingProvider: params.initializationOptions?.provideFormatter === true,
documentFormattingProvider: params.initializationOptions?.provideFormatter === true,
colorProvider: {},
foldingRangeProvider: true,
selectionRangeProvider: true,
@ -206,7 +207,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
let jsonConfigurationSettings: JSONSchemaSettings[] | undefined = undefined;
let schemaAssociations: ISchemaAssociations | SchemaConfiguration[] | undefined = undefined;
let formatterRegistration: Thenable<Disposable> | null = null;
let formatterRegistrations: Thenable<Disposable>[] | null = null;
// The settings have changed. Is send on server activation as well.
connection.onDidChangeConfiguration((change) => {
@ -224,12 +225,16 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
if (dynamicFormatterRegistration) {
const enableFormatter = settings && settings.json && settings.json.format && settings.json.format.enable;
if (enableFormatter) {
if (!formatterRegistration) {
formatterRegistration = connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector: [{ language: 'json' }, { language: 'jsonc' }] });
if (!formatterRegistrations) {
const documentSelector = [{ language: 'json' }, { language: 'jsonc' }];
formatterRegistrations = [
connection.client.register(DocumentRangeFormattingRequest.type, { documentSelector }),
connection.client.register(DocumentFormattingRequest.type, { documentSelector })
];
}
} else if (formatterRegistration) {
formatterRegistration.then(r => r.dispose());
formatterRegistration = null;
} else if (formatterRegistrations) {
formatterRegistrations.forEach(p => p.then(r => r.dispose()));
formatterRegistrations = null;
}
}
});
@ -420,19 +425,25 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
}, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token);
});
connection.onDocumentRangeFormatting((formatParams, token) => {
return runSafe(() => {
const document = documents.get(formatParams.textDocument.uri);
if (document) {
const edits = languageService.format(document, formatParams.range, formatParams.options);
if (edits.length > formatterMaxNumberOfEdits) {
const newText = TextDocument.applyEdits(document, edits);
return [TextEdit.replace(Range.create(Position.create(0, 0), document.positionAt(document.getText().length)), newText)];
}
return edits;
function onFormat(textDocument: TextDocumentIdentifier, range: Range | undefined, options: FormattingOptions): TextEdit[] {
const document = documents.get(textDocument.uri);
if (document) {
const edits = languageService.format(document, range ?? getFullRange(document), options);
if (edits.length > formatterMaxNumberOfEdits) {
const newText = TextDocument.applyEdits(document, edits);
return [TextEdit.replace(getFullRange(document), newText)];
}
return [];
}, [], `Error while formatting range for ${formatParams.textDocument.uri}`, token);
return edits;
}
return [];
}
connection.onDocumentRangeFormatting((formatParams, token) => {
return runSafe(() => onFormat(formatParams.textDocument, formatParams.range, formatParams.options), [], `Error while formatting range for ${formatParams.textDocument.uri}`, token);
});
connection.onDocumentFormatting((formatParams, token) => {
return runSafe(() => onFormat(formatParams.textDocument, undefined, formatParams.options), [], `Error while formatting ${formatParams.textDocument.uri}`, token);
});
connection.onDocumentColor((params, token) => {
@ -495,3 +506,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment)
// Listen on the connection
connection.listen();
}
function getFullRange(document: TextDocument): Range {
return Range.create(Position.create(0, 0), document.positionAt(document.getText().length));
}