blob: 5c77b58969bf7955f7097dbaf3baca46b01133bb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 Rogue Wave Software Inc. and others.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Michał Niewrzał (Rogue Wave Software Inc.) - initial implementation
* Lucas Bullen (Red Hat Inc.) - [Bug 517428] Requests sent before initialization
* - [Bug 528848] Formatting Request should include FormattingOptions
*******************************************************************************/
package org.eclipse.lsp4e.operations.format;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.lsp4e.LSPEclipseUtils;
import org.eclipse.lsp4e.LanguageServerPlugin;
import org.eclipse.lsp4e.LanguageServiceAccessor;
import org.eclipse.lsp4e.LanguageServiceAccessor.LSPDocumentInfo;
import org.eclipse.lsp4j.DocumentFormattingParams;
import org.eclipse.lsp4j.DocumentRangeFormattingParams;
import org.eclipse.lsp4j.FormattingOptions;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.ServerCapabilities;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextEdit;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
public class LSPFormatter {
public void applyEdits(IDocument document, List<? extends TextEdit> edits) {
LSPEclipseUtils.applyEdits(document, edits);
}
public CompletableFuture<List<? extends TextEdit>> requestFormatting(@NonNull IDocument document,
@NonNull ITextSelection textSelection) {
Collection<@NonNull LSPDocumentInfo> infos = LanguageServiceAccessor.getLSPDocumentInfosFor(document,
LSPFormatter::supportFormatting);
if (infos.isEmpty()) {
return CompletableFuture.completedFuture(Collections.emptyList());
}
// TODO consider a better strategy for that, maybe iterate on all LS until one gives a result
LSPDocumentInfo info = infos.iterator().next();
try {
return requestFormatting(info, textSelection);
} catch (BadLocationException e) {
LanguageServerPlugin.logError(e);
}
return CompletableFuture.completedFuture(Collections.emptyList());
}
private CompletableFuture<List<? extends TextEdit>> requestFormatting(LSPDocumentInfo info,
ITextSelection textSelection) throws BadLocationException {
TextDocumentIdentifier docId = new TextDocumentIdentifier(info.getFileUri().toString());
ServerCapabilities capabilities = info.getCapabilites();
IPreferenceStore store = EditorsUI.getPreferenceStore();
int tabWidth = store.getInt(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH);
boolean insertSpaces = store.getBoolean(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_SPACES_FOR_TABS);
// use range formatting if standard formatting is not supported or text is selected
if (capabilities != null
&& isDocumentRangeFormattingSupported(capabilities)
&& (!isDocumentFormattingSupported(capabilities)
|| textSelection.getLength() != 0)) {
DocumentRangeFormattingParams params = new DocumentRangeFormattingParams();
params.setTextDocument(docId);
params.setOptions(new FormattingOptions(tabWidth, insertSpaces));
boolean fullFormat = textSelection.getLength() == 0;
Position start = LSPEclipseUtils.toPosition(fullFormat ? 0 : textSelection.getOffset(), info.getDocument());
Position end = LSPEclipseUtils.toPosition(
fullFormat ? info.getDocument().getLength() : textSelection.getOffset() + textSelection.getLength(),
info.getDocument());
params.setRange(new Range(start, end));
return info.getInitializedLanguageClient()
.thenComposeAsync(server -> server.getTextDocumentService().rangeFormatting(params));
}
DocumentFormattingParams params = new DocumentFormattingParams();
params.setTextDocument(docId);
params.setOptions(new FormattingOptions(tabWidth, insertSpaces));
return info.getInitializedLanguageClient()
.thenComposeAsync(server -> server.getTextDocumentService().formatting(params));
}
private static boolean isDocumentRangeFormattingSupported(ServerCapabilities capabilities) {
return LSPEclipseUtils.hasCapability(capabilities.getDocumentRangeFormattingProvider());
}
private static boolean isDocumentFormattingSupported(ServerCapabilities capabilities) {
return LSPEclipseUtils.hasCapability(capabilities.getDocumentFormattingProvider());
}
public static boolean supportFormatting(ServerCapabilities capabilities) {
return isDocumentFormattingSupported(capabilities)
|| isDocumentRangeFormattingSupported(capabilities);
}
}