/* * $Id$ * * SARL is an general-purpose agent programming language. * More details on http://www.sarl.io * * Copyright (C) 2014-2017 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; } } }