diff --git a/org.eclipse.lsp4e/.project b/org.eclipse.lsp4e/.project
index 6486494b5..31c95a6d2 100644
--- a/org.eclipse.lsp4e/.project
+++ b/org.eclipse.lsp4e/.project
@@ -25,6 +25,11 @@
+
+ org.eclipse.pde.ds.core.builder
+
+
+
org.eclipse.m2e.core.maven2Nature
diff --git a/org.eclipse.lsp4e/META-INF/MANIFEST.MF b/org.eclipse.lsp4e/META-INF/MANIFEST.MF
index 54d16fb9d..d61b4ac1e 100644
--- a/org.eclipse.lsp4e/META-INF/MANIFEST.MF
+++ b/org.eclipse.lsp4e/META-INF/MANIFEST.MF
@@ -41,7 +41,8 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="3.12.0",
org.eclipse.ui.browser,
org.eclipse.ui.intro;bundle-version="3.5.200",
com.google.guava;bundle-version="30.1.0",
- org.eclipse.e4.core.commands
+ org.eclipse.e4.core.commands,
+ org.eclipse.compare.core
Bundle-ClassPath: .
Bundle-Localization: plugin
Bundle-ActivationPolicy: lazy
@@ -57,3 +58,4 @@ Bundle-Vendor: Eclipse.org
Import-Package: com.google.common.base,
com.google.gson;version="2.7.0"
Automatic-Module-Name: org.eclipse.lsp4e
+Service-Component: OSGI-INF/org.eclipse.lsp4e.DefaultFormatOnSave.xml
diff --git a/org.eclipse.lsp4e/OSGI-INF/org.eclipse.lsp4e.DefaultFormatOnSave.xml b/org.eclipse.lsp4e/OSGI-INF/org.eclipse.lsp4e.DefaultFormatOnSave.xml
new file mode 100644
index 000000000..0d804594d
--- /dev/null
+++ b/org.eclipse.lsp4e/OSGI-INF/org.eclipse.lsp4e.DefaultFormatOnSave.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/org.eclipse.lsp4e/build.properties b/org.eclipse.lsp4e/build.properties
index cce94c934..69bab11a2 100644
--- a/org.eclipse.lsp4e/build.properties
+++ b/org.eclipse.lsp4e/build.properties
@@ -7,5 +7,6 @@ bin.includes = META-INF/,\
icons/,\
.options,\
resources/,\
- schema/
+ schema/,\
+ OSGI-INF/
src.includes = schema/
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DefaultFormatOnSave.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DefaultFormatOnSave.java
new file mode 100644
index 000000000..26e22e1bc
--- /dev/null
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DefaultFormatOnSave.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation.
+ * 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:
+ * See git history
+ *******************************************************************************/
+package org.eclipse.lsp4e;
+
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.Region;
+import org.osgi.service.component.annotations.Component;
+
+@Component(property = { "service.ranking:Integer=0" })
+public class DefaultFormatOnSave implements IFormatOnSave {
+
+ @Override
+ public boolean isEnabledFor(IDocument document) {
+ return false;
+ }
+
+ /**
+ * Formats the full file
+ */
+ @Override
+ public IRegion[] getFormattingRegions(ITextFileBuffer buffer) {
+ return new IRegion[] { new Region(0, buffer.getDocument().getLength()) };
+ }
+
+}
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DocumentContentSynchronizer.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DocumentContentSynchronizer.java
index deb5c560f..4ee1d484f 100644
--- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DocumentContentSynchronizer.java
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/DocumentContentSynchronizer.java
@@ -16,6 +16,7 @@
import java.io.File;
import java.net.URI;
import java.util.Collections;
+import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@@ -31,6 +32,7 @@
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.ServiceCaller;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jface.preference.IPreferenceStore;
@@ -38,6 +40,9 @@
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.MultiTextSelection;
+import org.eclipse.lsp4e.operations.format.LSPFormatter;
import org.eclipse.lsp4e.ui.Messages;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
@@ -65,12 +70,16 @@ final class DocumentContentSynchronizer implements IDocumentListener {
private final @NonNull IDocument document;
private final @NonNull URI fileUri;
private final TextDocumentSyncKind syncKind;
+ private final LSPFormatter formatter = new LSPFormatter();
private int version = 0;
private DidChangeTextDocumentParams changeParams;
private long openSaveStamp;
private IPreferenceStore store;
+
+ private final ServiceCaller formatOnSave = new ServiceCaller<>(getClass(), IFormatOnSave.class);
+
public DocumentContentSynchronizer(@NonNull LanguageServerWrapper languageServerWrapper,
@NonNull LanguageServer languageServer,
@NonNull IDocument document, TextDocumentSyncKind syncKind) {
@@ -221,6 +230,9 @@ private int lsToWillSaveWaitUntilTimeout() {
public void documentAboutToBeSaved() {
if (!serverSupportsWillSaveWaitUntil()) {
+ if (formatOnSave != null) {
+ formatOnSave.call(f -> formatDocument(f.isEnabledFor(document), f.getFormattingRegions(LSPEclipseUtils.toBuffer(document))));
+ }
return;
}
@@ -257,6 +269,27 @@ public void documentAboutToBeSaved() {
}
}
+ private void formatDocument(boolean enable, IRegion[] formattingRegions) {
+ if (enable && document != null && formattingRegions != null) {
+ try {
+ var textSelection = new MultiTextSelection(document, formattingRegions);
+ formatter.requestFormatting(document, textSelection).get(lsToWillSaveWaitUntilTimeout(), TimeUnit.SECONDS)
+ .ifPresent(edits -> {
+ try {
+ edits.apply();
+ } catch (final ConcurrentModificationException ex) {
+ ServerMessageHandler.showMessage(Messages.LSPFormatHandler_DiscardedFormat, new MessageParams(MessageType.Error, Messages.LSPFormatHandler_DiscardedFormatResponse));
+ } catch (BadLocationException e) {
+ LanguageServerPlugin.logError(e);
+ }
+ });
+ } catch (BadLocationException | InterruptedException | ExecutionException
+ | TimeoutException e) {
+ LanguageServerPlugin.logError(e);
+ }
+ }
+ }
+
public void documentSaved(IFileBuffer buffer) {
if (openSaveStamp >= buffer.getModificationStamp()) {
return;
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/IFormatOnSave.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/IFormatOnSave.java
new file mode 100644
index 000000000..06f9c0273
--- /dev/null
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/IFormatOnSave.java
@@ -0,0 +1,34 @@
+/*******************************************************************************
+ * Copyright (c) 2023 Contributors to the Eclipse Foundation.
+ * 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:
+ * See git history
+ *******************************************************************************/
+package org.eclipse.lsp4e;
+
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+public interface IFormatOnSave {
+
+ /**
+ * Checks whether formatting shall be performed prior to file buffer saving.
+ * @param document
+ * @return true if document buffer shall be formatted prior saving
+ */
+ boolean isEnabledFor(IDocument document);
+
+ /**
+ * Get the regions to be formatted (e.g. full file, lines edited this session, lines edited vs. source control baseline).
+ * @param buffer the buffer to compare contents from
+ * @return regions to be formatted before saving.
+ */
+ IRegion[] getFormattingRegions(ITextFileBuffer buffer);
+
+}
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java
index 4f29822fb..6b2b12efa 100644
--- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LSPEclipseUtils.java
@@ -355,7 +355,7 @@ public static CallHierarchyPrepareParams toCallHierarchyPrepareParams(int offset
}
- private static ITextFileBuffer toBuffer(IDocument document) {
+ public static ITextFileBuffer toBuffer(IDocument document) {
ITextFileBufferManager bufferManager = FileBuffers.getTextFileBufferManager();
if (bufferManager == null)
return null;