Skip to content

Commit

Permalink
[lang][ui] Auto format code on paste.
Browse files Browse the repository at this point in the history
close #372

Signed-off-by: Stéphane Galland <galland@arakhne.org>
  • Loading branch information
gallandarakhneorg committed Aug 24, 2016
1 parent 93a54d4 commit 3a7b121
Show file tree
Hide file tree
Showing 7 changed files with 422 additions and 7 deletions.
2 changes: 1 addition & 1 deletion eclipse-sarl/plugins/io.sarl.lang.mwe2/GenerateSARL2.mwe2
Expand Up @@ -829,7 +829,7 @@ Workflow {
}
ui = {
bind = "org.eclipse.xtext.ui.editor.XtextSourceViewer$Factory"
to = "org.eclipse.xtend.ide.editor.RichStringAwareSourceViewer$Factory"
to = "io.sarl.lang.ui.editor.SARLSourceViewer$Factory"
}
ui = {
bind = "org.eclipse.xtext.ui.editor.actions.IActionContributor"
Expand Down
Expand Up @@ -39,6 +39,7 @@
import io.sarl.lang.ui.contentassist.SARLProposalProvider;
import io.sarl.lang.ui.contentassist.SARLTemplateContextType;
import io.sarl.lang.ui.contentassist.SARLTemplateProposalProvider;
import io.sarl.lang.ui.editor.SARLSourceViewer;
import io.sarl.lang.ui.highlighting.SARLHighlightingCalculator;
import io.sarl.lang.ui.images.IQualifiedNameImageProvider;
import io.sarl.lang.ui.images.QualifiedPluginImageHelper;
Expand Down Expand Up @@ -80,7 +81,6 @@
import org.eclipse.xtend.ide.editor.OccurrenceComputer;
import org.eclipse.xtend.ide.editor.OverrideIndicatorModelListener;
import org.eclipse.xtend.ide.editor.OverrideIndicatorRulerAction;
import org.eclipse.xtend.ide.editor.RichStringAwareSourceViewer;
import org.eclipse.xtend.ide.editor.RichStringAwareToggleCommentAction;
import org.eclipse.xtend.ide.editor.SingleLineCommentHelper;
import org.eclipse.xtend.ide.editor.XtendDoubleClickStrategyProvider;
Expand Down Expand Up @@ -686,7 +686,7 @@ public Class<? extends IResourceUIServiceProvider> bindIResourceUIServiceProvide

// contributed by io.sarl.lang.mwe2.binding.InjectionFragment2 [Bindings required by extended Xtend API]
public Class<? extends XtextSourceViewer.Factory> bindXtextSourceViewer$Factory() {
return RichStringAwareSourceViewer.Factory.class;
return SARLSourceViewer.Factory.class;
}

// contributed by io.sarl.lang.mwe2.binding.InjectionFragment2 [Bindings required by extended Xtend API]
Expand Down
@@ -0,0 +1,161 @@
/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2016 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.sarl.lang.ui.editor;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.formatter.IContentFormatter;
import org.eclipse.xtext.ui.editor.model.IXtextDocument;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.xbase.lib.Exceptions;

/** A service that enables to do auto-formatting when a document changed.
*
* <p>FIXME: Remove if Xtext accept the patch https://github.com/eclipse/xtext-eclipse/pull/63
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public class DocumentAutoFormatter implements IDocumentAutoFormatter {

private Collection<RegionFormattingRequest> formattingRequests = Collections.synchronizedList(new ArrayList<>(1));

private IDocumentListener autoFormatListener;

private IXtextDocument document;

private IContentFormatter contentFormatter;

@Override
public void bind(IXtextDocument document, IContentFormatter contentFormatter) {
assert document != null;
assert contentFormatter != null;
this.document = document;
this.contentFormatter = contentFormatter;
}

@Override
public synchronized void beginAutoFormat() {
if (this.document != null && this.autoFormatListener == null) {
this.formattingRequests.clear();
this.autoFormatListener = new IDocumentListener() {
@Override
public void documentAboutToBeChanged(DocumentEvent event) {
//
}

@SuppressWarnings("synthetic-access")
@Override
public void documentChanged(DocumentEvent event) {
if (!Strings.isEmpty(event.getText())
&& event.getDocument() instanceof IXtextDocument) {
DocumentAutoFormatter.this.formattingRequests.add(new RegionFormattingRequest(
(IXtextDocument) event.getDocument(), event.getOffset(), event.getText().length()));
}
}
};
this.document.addDocumentListener(this.autoFormatListener);
}
}

@Override
public void endAutoFormat() {
final Collection<RegionFormattingRequest> requests;
synchronized (this) {
requests = this.formattingRequests;
this.formattingRequests = Collections.synchronizedList(new ArrayList<>(1));
if (this.autoFormatListener != null) {
final IDocumentListener listener = this.autoFormatListener;
this.autoFormatListener = null;
if (this.document != null) {
this.document.removeDocumentListener(listener);
}
}
}
if (this.contentFormatter != null) {
for (final RegionFormattingRequest request : requests) {
formatRegion(request.document, request.offset, request.length);
}
}
}

/** Called for formatting a region.
*
* @param document the document to format.
* @param offset the offset of the text to format.
* @param length the length of the text.
*/
protected void formatRegion(IXtextDocument document, int offset, int length) {
try {
final int startRegionOffset = document.getLineInformationOfOffset(
previousSiblingChar(document, offset)).getOffset();
final IRegion endLine = document.getLineInformationOfOffset(offset + length);
final int endRegionOffset = endLine.getOffset() + endLine.getLength();
final int regionLength = endRegionOffset - startRegionOffset;
for (final IRegion region : document.computePartitioning(startRegionOffset, regionLength)) {
this.contentFormatter.format(document, region);
}
} catch (BadLocationException exception) {
Exceptions.sneakyThrow(exception);
}
}

private static int previousSiblingChar(IXtextDocument document, int offset) throws BadLocationException {
int off = offset - 1;
while (off >= 0 && Character.isWhitespace(document.getChar(off))) {
--off;
}
return off;
}

/** Request for formatting a region.
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@SuppressWarnings("checkstyle:visibilitymodifier")
private static class RegionFormattingRequest {

public final IXtextDocument document;

public final int offset;

public final int length;

RegionFormattingRequest(IXtextDocument document, int offset, int length) {
this.document = document;
this.offset = offset;
this.length = length;
}

}

}
@@ -0,0 +1,61 @@
/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2016 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.sarl.lang.ui.editor;

import com.google.inject.ImplementedBy;
import org.eclipse.jface.text.formatter.IContentFormatter;
import org.eclipse.xtext.ui.editor.model.IXtextDocument;

/** A service that enables to do auto-formatting when a document changed.
*
* <p>FIXME: Remove if Xtext accept the patch https://github.com/eclipse/xtext-eclipse/pull/63
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@ImplementedBy(DocumentAutoFormatter.class)
public interface IDocumentAutoFormatter {

/** Create an instance of document auto formatter.
*
* @param document the Xtext document associated to this auto-formatter.
* @param contentFormatter the formatter of content to be used.
*/
default void bind(IXtextDocument document, IContentFormatter contentFormatter) {
//
}

/** Start auto-formating.
*/
default void beginAutoFormat() {
//
}

/** End auto-formating.
*/
default void endAutoFormat() {
//
}

}
@@ -0,0 +1,136 @@
/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2016 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.sarl.lang.ui.editor;

import java.lang.reflect.Field;

import javax.inject.Inject;

import com.google.inject.MembersInjector;
import com.google.inject.Provider;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRewriteTarget;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.source.IOverviewRuler;
import org.eclipse.jface.text.source.IVerticalRuler;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.xtend.ide.editor.RichStringAwareSourceViewer;
import org.eclipse.xtend.ide.editor.TypedRegionMerger;
import org.eclipse.xtext.ui.editor.XtextSourceViewer;
import org.eclipse.xtext.ui.editor.model.IXtextDocument;
import org.eclipse.xtext.xbase.lib.Exceptions;

/** Viewer of SARL code.
*
* <p>Based on the Xtend implementation, extended with the auto-formating feature when pasting.
* FIXME: Remove if Xtext accept the patch https://github.com/eclipse/xtext-eclipse/pull/63
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public class SARLSourceViewer extends RichStringAwareSourceViewer {

@Inject
private Provider<IDocumentAutoFormatter> autoFormatterProvider;

/** Constructor.
*
* @param parent the container.
* @param ruler the vertical ruler.
* @param overviewRuler the overview ruler.
* @param showsAnnotationOverview the annotation shower.
* @param styles the styles.
*/
public SARLSourceViewer(Composite parent, IVerticalRuler ruler, IOverviewRuler overviewRuler,
boolean showsAnnotationOverview, int styles) {
super(parent, ruler, overviewRuler, showsAnnotationOverview, styles);
}

/** Replies the document auto-formatter.
*
* @return the service.
*/
public IDocumentAutoFormatter getDocumentAutoFormatter() {
final IDocument document = getDocument();
if (document instanceof IXtextDocument) {
final IDocumentAutoFormatter formatter = this.autoFormatterProvider.get();
formatter.bind((IXtextDocument) document, this.fContentFormatter);
return formatter;
}
return new IDocumentAutoFormatter() {
//
};
}

@Override
public void doOperation(int operation) {
if (operation == ITextOperationTarget.PASTE) {
final IRewriteTarget target = getRewriteTarget();
target.beginCompoundChange();
final IDocumentAutoFormatter formatter = getDocumentAutoFormatter();
formatter.beginAutoFormat();
try {
super.doOperation(operation);
} finally {
formatter.endAutoFormat();
target.endCompoundChange();
}
} else {
super.doOperation(operation);
}
}

/** Factory of SARL code viewer.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
public static class Factory implements XtextSourceViewer.Factory {

@Inject
private TypedRegionMerger merger;

@Inject
private MembersInjector<SARLSourceViewer> memberInjector;

@Override
public XtextSourceViewer createSourceViewer(Composite parent, IVerticalRuler ruler,
IOverviewRuler overviewRuler, boolean showsAnnotationOverview, int styles) {
final SARLSourceViewer result = new SARLSourceViewer(parent, ruler, overviewRuler, showsAnnotationOverview, styles);
try {
final Field field = RichStringAwareSourceViewer.class.getDeclaredField("merger"); //$NON-NLS-1$
field.setAccessible(true);
field.set(result, this.merger);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException exception) {
Exceptions.sneakyThrow(exception);
}
this.memberInjector.injectMembers(result);
return result;
}

}

}

0 comments on commit 3a7b121

Please sign in to comment.