[formatter] Use configurable line separator

https://bugs.eclipse.org/bugs/show_bug.cgi?id=347782
This commit is contained in:
Jan Koehnlein 2012-03-01 14:46:45 +01:00
parent 08be34013d
commit a29baf41f1
11 changed files with 143 additions and 25 deletions

View file

@ -0,0 +1,21 @@
/*******************************************************************************
* Copyright (c) 2012 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.formatting;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.parsetree.reconstr.ITokenStream;
/**
* @author Jan Koehnlein - Initial contribution and API
* @since 2.3
*/
public interface IFormatterExtension {
ITokenStream createFormatterStream(EObject context, String initalIndentation, ITokenStream outputStream, boolean preserveWhitespaces);
}

View file

@ -13,10 +13,15 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.formatting.IElementMatcherProvider;
import org.eclipse.xtext.formatting.IElementMatcherProvider.IElementMatcher;
import org.eclipse.xtext.formatting.IIndentationInformation;
import org.eclipse.xtext.formatting.ILineSeparatorInformation;
import org.eclipse.xtext.formatting.IWhitespaceInformationProvider;
import org.eclipse.xtext.formatting.impl.AbstractFormattingConfig.ElementPattern;
import org.eclipse.xtext.parsetree.reconstr.IHiddenTokenHelper;
import org.eclipse.xtext.parsetree.reconstr.ITokenStream;
@ -43,13 +48,11 @@ public abstract class AbstractDeclarativeFormatter extends BaseFormatter {
@Inject
private IHiddenTokenHelper hiddenTokenHelper;
@Inject(optional = true)
private IIndentationInformation indentInfo = new IIndentationInformation() {
public String getIndentString() {
return "\t";
}
};
@Inject
private IWhitespaceInformationProvider whitespaceInformationProvider;
private URI contextResourceURI;
@Inject
private IElementMatcherProvider matcherProvider;
@ -60,10 +63,21 @@ public abstract class AbstractDeclarativeFormatter extends BaseFormatter {
return new FormattingConfigBasedStream(out, indent, getConfig(), createMatcher(), hiddenTokenHelper,
preserveWhitespaces);
}
/**
* @since 2.3
*/
@Override
public ITokenStream createFormatterStream(EObject context, String indent, ITokenStream out, boolean preserveWhitespaces) {
if(context != null)
contextResourceURI = EcoreUtil2.getNormalizedResourceURI(context);
return new FormattingConfigBasedStream(out, indent, getConfig(), createMatcher(), hiddenTokenHelper,
preserveWhitespaces);
}
@SuppressWarnings("deprecation")
protected FormattingConfig createFormattingConfig() {
FormattingConfig cfg = new FormattingConfig(grammarAccess, hiddenTokenHelper, indentInfo);
FormattingConfig cfg = new FormattingConfig2(grammarAccess, hiddenTokenHelper, getIndentInfo(), getLineSeparatorInfo());
cfg.setWhitespaceRule(getWSRule());
return cfg;
}
@ -89,9 +103,16 @@ public abstract class AbstractDeclarativeFormatter extends BaseFormatter {
}
protected IIndentationInformation getIndentInfo() {
return indentInfo;
return whitespaceInformationProvider.getIndentationInformation(contextResourceURI);
}
/**
* @since 2.3
*/
protected ILineSeparatorInformation getLineSeparatorInfo() {
return whitespaceInformationProvider.getLineSeparatorInformation(contextResourceURI);
}
protected IElementMatcherProvider getMatcherProvider() {
return matcherProvider;
}

View file

@ -8,17 +8,26 @@
package org.eclipse.xtext.formatting.impl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.formatting.IFormatter;
import org.eclipse.xtext.formatting.IFormatterExtension;
import org.eclipse.xtext.parsetree.reconstr.ITokenStream;
/**
* @author Moritz Eysholdt - Initial contribution and API
*/
public abstract class AbstractFormatter implements IFormatter {
public abstract class AbstractFormatter implements IFormatter, IFormatterExtension {
public ITokenStream createFormatterStream(String initalIndentation,
ITokenStream outputStream, boolean preserveWhitespaces) {
return null;
}
/**
* @since 2.3
*/
public ITokenStream createFormatterStream(EObject context, String initalIndentation, ITokenStream outputStream,
boolean preserveWhitespaces) {
return createFormatterStream(initalIndentation, outputStream, preserveWhitespaces);
}
}

View file

@ -18,6 +18,7 @@ import org.eclipse.xtext.parsetree.reconstr.ITokenStreamExtension;
*/
public abstract class AbstractTokenStream implements ITokenStreamExtension {
public void flush() throws IOException {
}
@ -32,4 +33,5 @@ public abstract class AbstractTokenStream implements ITokenStreamExtension {
*/
public void init(ParserRule startRule) {
}
}

View file

@ -20,7 +20,7 @@ public class BaseFormatter extends AbstractFormatter {
@Inject
protected IGrammarAccess grammar;
protected TerminalRule getWSRule() {
// FIXME: make this configurable
return (TerminalRule) GrammarUtil.findRuleForName(grammar.getGrammar(),

View file

@ -16,6 +16,7 @@ import java.util.regex.Pattern;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.formatting.IFormatter;
import org.eclipse.xtext.formatting.IFormatterExtension;
import org.eclipse.xtext.formatting.INodeModelStreamer;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.ILeafNode;
@ -75,7 +76,11 @@ public class DefaultNodeModelFormatter extends AbstractNodeModelFormatter {
String indent = getIndentation(root, offset);
TokenStringBuffer buf = new TokenStringBuffer();
ITokenStream out = offset == 0 ? buf : new FilterFirstWhitespaceStream(buf);
ITokenStream fmt = formatter.createFormatterStream(indent, out, false);
ITokenStream fmt;
if(formatter instanceof IFormatterExtension)
fmt = ((IFormatterExtension) formatter).createFormatterStream(root.getSemanticElement(), indent, out, false);
else
fmt = formatter.createFormatterStream(indent, out, false);
try {
ITextRegion range = nodeModelStreamer.feedTokenStream(fmt, root, offset, length);
return new FormattedRegion(range.getOffset(), range.getLength(), buf.toString());
@ -99,13 +104,13 @@ public class DefaultNodeModelFormatter extends AbstractNodeModelFormatter {
}
// go backwards until first linewrap
Pattern p = Pattern.compile("\\n([ \\t]*)");
Pattern p = Pattern.compile("(\\n|\\r)([ \\t]*)");
for (int i = r.size() - 1; i >= 0; i--) {
Matcher m = p.matcher(r.get(i).getText());
if (m.find()) {
String ind = m.group(1);
String ind = m.group(2);
while (m.find())
ind = m.group(1);
ind = m.group(2);
return ind;
}
}

View file

@ -170,6 +170,10 @@ public class FormattingConfig extends AbstractFormattingConfig {
whitespaceRule = rule;
}
/**
* @deprecated use {@link FormattingConfig2} instead
*/
@Deprecated
public FormattingConfig(IGrammarAccess grammarAccess, IHiddenTokenHelper hiddenTokenHelper,
IIndentationInformation indentInfo) {
super(grammarAccess, hiddenTokenHelper);

View file

@ -0,0 +1,33 @@
/*******************************************************************************
* Copyright (c) 2012 itemis AG (http://www.itemis.eu) and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.xtext.formatting.impl;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.formatting.IIndentationInformation;
import org.eclipse.xtext.formatting.ILineSeparatorInformation;
import org.eclipse.xtext.parsetree.reconstr.IHiddenTokenHelper;
/**
* @author Jan Koehnlein - Initial contribution and API
* @since 2.3
*/
@SuppressWarnings("deprecation")
public class FormattingConfig2 extends FormattingConfig {
protected ILineSeparatorInformation lineSeparatorInfo;
public FormattingConfig2(IGrammarAccess grammarAccess, IHiddenTokenHelper hiddenTokenHelper,
IIndentationInformation indentInfo, ILineSeparatorInformation lineSeparatorInfo) {
super(grammarAccess, hiddenTokenHelper, indentInfo);
this.lineSeparatorInfo = lineSeparatorInfo;
}
public ILineSeparatorInformation getLineSeparatorInfo() {
return lineSeparatorInfo;
}
}

View file

@ -277,9 +277,11 @@ public class FormattingConfigBasedStream extends BaseTokenStream {
if (lastNLIndex >= 0)
return (value.length() - lastNLIndex) - 1;
if (preserveSpaces && leadingWS != null) {
int lastNLIndexInLeadingWs = leadingWS.lastIndexOf('\n');
int lastNLIndexInLeadingWs = leadingWS.lastIndexOf(getLineSeparator());
if (lastNLIndexInLeadingWs >= 0)
return ((leadingWS.length() - lastNLIndexInLeadingWs) + value.length()) - 1;
// TODO Moritz: replaced -1 by -getLineSeparator.length()
// is that correct?
return ((leadingWS.length() - lastNLIndexInLeadingWs) + value.length()) - getLineSeparator().length();
}
return -1;
}
@ -288,7 +290,7 @@ public class FormattingConfigBasedStream extends BaseTokenStream {
if (leadingWS == null)
return -1;
int c = 0, i = -1;
while ((i = leadingWS.indexOf('\n', i + 1)) >= 0)
while ((i = leadingWS.indexOf(getLineSeparator(), i + getLineSeparator().length())) >= 0)
c++;
return c;
}
@ -316,7 +318,7 @@ public class FormattingConfigBasedStream extends BaseTokenStream {
if (e instanceof SpaceLocator)
return false;
}
return hiddenTokenHelper.getWhitespaceRuleFor(hiddenTokenDefinition, "\n") != null;
return hiddenTokenHelper.getWhitespaceRuleFor(hiddenTokenDefinition, getLineSeparator()) != null;
}
@Override
@ -345,14 +347,25 @@ public class FormattingConfigBasedStream extends BaseTokenStream {
protected boolean preserveSpaces;
public FormattingConfigBasedStream(ITokenStream out, String indentation, FormattingConfig cfg,
public FormattingConfigBasedStream(ITokenStream out, String initialIndentation, FormattingConfig cfg,
IElementMatcher<ElementPattern> matcher, IHiddenTokenHelper hiddenTokenHelper, boolean preserveSpaces) {
super(out);
this.cfg = cfg;
this.matcher = matcher;
this.hiddenTokenHelper = hiddenTokenHelper;
this.preserveSpaces = preserveSpaces;
this.indentationPrefix = indentation == null ? "" : indentation;
this.indentationPrefix = initialIndentation == null ? "" : initialIndentation;
}
/**
* @since 2.3
*/
protected String getLineSeparator() {
if (cfg instanceof FormattingConfig2) {
return ((FormattingConfig2) cfg).getLineSeparatorInfo().getLineSeparator();
} else {
return System.getProperty("line.separator");
}
}
protected void addLineEntry(EObject grammarElement, String value, boolean isHidden) throws IOException {

View file

@ -16,12 +16,13 @@ import java.util.List;
import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.formatting.IFormatter;
import org.eclipse.xtext.formatting.IFormatterExtension;
import org.eclipse.xtext.parsetree.reconstr.IParseTreeConstructor.TreeConstructionReport;
import org.eclipse.xtext.parsetree.reconstr.impl.TokenStringBuffer;
import org.eclipse.xtext.parsetree.reconstr.impl.WriterTokenStream;
import org.eclipse.xtext.util.ReplaceRegion;
import org.eclipse.xtext.resource.SaveOptions;
import org.eclipse.xtext.serializer.ISerializer;
import org.eclipse.xtext.util.ReplaceRegion;
import org.eclipse.xtext.validation.IConcreteSyntaxValidator;
import com.google.inject.Inject;
@ -52,7 +53,11 @@ public class Serializer implements ISerializer {
throw new IConcreteSyntaxValidator.InvalidConcreteSyntaxException(
"These errors need to be fixed before the model can be serialized.", diagnostics);
}
ITokenStream formatterTokenStream = formatter.createFormatterStream(null, tokenStream, !options.isFormatting());
ITokenStream formatterTokenStream;
if(formatter instanceof IFormatterExtension)
formatterTokenStream = ((IFormatterExtension) formatter).createFormatterStream(obj, null, tokenStream, !options.isFormatting());
else
formatterTokenStream = formatter.createFormatterStream(null, tokenStream, !options.isFormatting());
TreeConstructionReport report = parseTreeReconstructor.serializeSubtree(obj, formatterTokenStream);
formatterTokenStream.flush();
return report;

View file

@ -18,6 +18,7 @@ import org.eclipse.emf.common.util.Diagnostic;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.formatting.IFormatter;
import org.eclipse.xtext.formatting.IFormatterExtension;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.parsetree.reconstr.ITokenStream;
@ -97,7 +98,11 @@ public class Serializer implements ISerializer {
}
ISerializationDiagnostic.Acceptor errors = ISerializationDiagnostic.EXCEPTION_THROWING_ACCEPTOR;
ITokenStream formatterTokenStream = formatter.createFormatterStream(null, tokenStream, !options.isFormatting());
ITokenStream formatterTokenStream;
if(formatter instanceof IFormatterExtension)
formatterTokenStream = ((IFormatterExtension) formatter).createFormatterStream(obj, null, tokenStream, !options.isFormatting());
else
formatterTokenStream = formatter.createFormatterStream(null, tokenStream, !options.isFormatting());
EObject context = getContext(obj);
ISequenceAcceptor acceptor = new TokenStreamSequenceAdapter(formatterTokenStream, errors);
serialize(obj, context, acceptor, errors);