Merge pull request #54 from eclipse/cd_issue21

[Language Server] Support for Formatting
This commit is contained in:
Christian Dietrich 2016-07-25 11:34:31 +02:00 committed by GitHub
commit 4e1eae8b38
17 changed files with 764 additions and 8 deletions

View file

@ -0,0 +1,50 @@
/*******************************************************************************
* Copyright (c) 2016 itemis AG (http://www.itemis.com) 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.ide.tests.server
import io.typefox.lsapi.builders.RangeBuilder
import org.eclipse.xtext.ide.server.formatting.FormattingService
import org.eclipse.xtext.testing.AbstractLanguageServerTest
import org.junit.Test
/**
* Tests for {@link FormattingService}
*
* @author Christian Dietrich - Initial contribution and API
*/
class FormattingTest extends AbstractTestLangLanguageServerTest {
@Test def void testFormattingService() {
testFormatting [
model = '''type Foo{int bar} type Bar{Foo foo}'''
expectedText = '''
type Foo{
int bar
}
type Bar{
Foo foo
}
'''
]
}
@Test def void testRangeFormattingService() {
testRangeFormatting [
model = '''type Foo{int bar} type Bar{Foo foo}'''
range = new RangeBuilder[
start(0,0)
end(0,17)
].build
expectedText = '''
type Foo{
int bar
} type Bar{Foo foo}'''
]
}
}

View file

@ -7,9 +7,16 @@
*/
package org.eclipse.xtext.ide.tests.testlanguage
import org.eclipse.xtext.formatting2.IFormatter2
import org.eclipse.xtext.ide.tests.testlanguage.formatting2.TestLanguageFormatter
/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
*/
class TestLanguageRuntimeModule extends AbstractTestLanguageRuntimeModule {
def Class<? extends IFormatter2> bindIFormatter2() {
return TestLanguageFormatter;
}
}

View file

@ -0,0 +1,47 @@
/*******************************************************************************
* Copyright (c) 2016 itemis AG (http://www.itemis.com) 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.ide.tests.testlanguage.formatting2
import javax.inject.Inject
import org.eclipse.xtext.formatting2.AbstractFormatter2
import org.eclipse.xtext.formatting2.IFormattableDocument
import org.eclipse.xtext.ide.tests.testlanguage.services.TestLanguageGrammarAccess
import org.eclipse.xtext.ide.tests.testlanguage.testLanguage.Model
import org.eclipse.xtext.ide.tests.testlanguage.testLanguage.Property
import org.eclipse.xtext.ide.tests.testlanguage.testLanguage.TypeDeclaration
/**
* @author Christian Dietrich - Initial contribution and API
*/
class TestLanguageFormatter extends AbstractFormatter2 {
@Inject extension TestLanguageGrammarAccess
def dispatch void format(Model model, extension IFormattableDocument document) {
for (type : model.types) {
type.format
}
}
def dispatch void format(TypeDeclaration type, extension IFormattableDocument document) {
//type.regionFor.keyword(typeDeclarationAccess.leftCurlyBracketKeyword_2).prepend[oneSpace]
type.regionFor.keyword(typeDeclarationAccess.leftCurlyBracketKeyword_2).append[newLine]
type.regionFor.keyword(typeDeclarationAccess.rightCurlyBracketKeyword_4).prepend[newLine].append[newLine]
interior(
type.regionFor.keyword(typeDeclarationAccess.leftCurlyBracketKeyword_2),
type.regionFor.keyword(typeDeclarationAccess.rightCurlyBracketKeyword_4)
) [indent]
for (property : type.properties) {
property.format
}
}
def dispatch void format(Property property, extension IFormattableDocument document) {
property.append[newLine]
}
}

View file

@ -0,0 +1,77 @@
/**
* Copyright (c) 2016 itemis AG (http://www.itemis.com) 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.ide.tests.server;
import io.typefox.lsapi.Range;
import io.typefox.lsapi.builders.RangeBuilder;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.ide.server.formatting.FormattingService;
import org.eclipse.xtext.ide.tests.server.AbstractTestLangLanguageServerTest;
import org.eclipse.xtext.testing.FormattingConfiguration;
import org.eclipse.xtext.testing.RangeFormattingConfiguration;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.junit.Test;
/**
* Tests for {@link FormattingService}
*
* @author Christian Dietrich - Initial contribution and API
*/
@SuppressWarnings("all")
public class FormattingTest extends AbstractTestLangLanguageServerTest {
@Test
public void testFormattingService() {
final Procedure1<FormattingConfiguration> _function = (FormattingConfiguration it) -> {
StringConcatenation _builder = new StringConcatenation();
_builder.append("type Foo{int bar} type Bar{Foo foo}");
it.setModel(_builder.toString());
StringConcatenation _builder_1 = new StringConcatenation();
_builder_1.append("type Foo{");
_builder_1.newLine();
_builder_1.append("\t");
_builder_1.append("int bar");
_builder_1.newLine();
_builder_1.append("}");
_builder_1.newLine();
_builder_1.append("type Bar{");
_builder_1.newLine();
_builder_1.append("\t");
_builder_1.append("Foo foo");
_builder_1.newLine();
_builder_1.append("}");
_builder_1.newLine();
it.setExpectedText(_builder_1.toString());
};
this.testFormatting(_function);
}
@Test
public void testRangeFormattingService() {
final Procedure1<RangeFormattingConfiguration> _function = (RangeFormattingConfiguration it) -> {
StringConcatenation _builder = new StringConcatenation();
_builder.append("type Foo{int bar} type Bar{Foo foo}");
it.setModel(_builder.toString());
final Procedure1<RangeBuilder> _function_1 = (RangeBuilder it_1) -> {
it_1.start(0, 0);
it_1.end(0, 17);
};
RangeBuilder _rangeBuilder = new RangeBuilder(_function_1);
Range _build = _rangeBuilder.build();
it.setRange(_build);
StringConcatenation _builder_1 = new StringConcatenation();
_builder_1.append("type Foo{");
_builder_1.newLine();
_builder_1.append("\t");
_builder_1.append("int bar");
_builder_1.newLine();
_builder_1.append("} type Bar{Foo foo}");
it.setExpectedText(_builder_1.toString());
};
this.testRangeFormatting(_function);
}
}

View file

@ -7,11 +7,16 @@
*/
package org.eclipse.xtext.ide.tests.testlanguage;
import org.eclipse.xtext.formatting2.IFormatter2;
import org.eclipse.xtext.ide.tests.testlanguage.AbstractTestLanguageRuntimeModule;
import org.eclipse.xtext.ide.tests.testlanguage.formatting2.TestLanguageFormatter;
/**
* Use this class to register components to be used at runtime / without the Equinox extension registry.
*/
@SuppressWarnings("all")
public class TestLanguageRuntimeModule extends AbstractTestLanguageRuntimeModule {
public Class<? extends IFormatter2> bindIFormatter2() {
return TestLanguageFormatter.class;
}
}

View file

@ -0,0 +1,117 @@
/**
* Copyright (c) 2016 itemis AG (http://www.itemis.com) 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.ide.tests.testlanguage.formatting2;
import java.util.Arrays;
import javax.inject.Inject;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.formatting2.AbstractFormatter2;
import org.eclipse.xtext.formatting2.IFormattableDocument;
import org.eclipse.xtext.formatting2.IHiddenRegionFormatter;
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegion;
import org.eclipse.xtext.formatting2.regionaccess.ISemanticRegionsFinder;
import org.eclipse.xtext.ide.tests.testlanguage.services.TestLanguageGrammarAccess;
import org.eclipse.xtext.ide.tests.testlanguage.testLanguage.Model;
import org.eclipse.xtext.ide.tests.testlanguage.testLanguage.Property;
import org.eclipse.xtext.ide.tests.testlanguage.testLanguage.TypeDeclaration;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
/**
* @author Christian Dietrich - Initial contribution and API
*/
@SuppressWarnings("all")
public class TestLanguageFormatter extends AbstractFormatter2 {
@Inject
@Extension
private TestLanguageGrammarAccess _testLanguageGrammarAccess;
protected void _format(final Model model, @Extension final IFormattableDocument document) {
EList<TypeDeclaration> _types = model.getTypes();
for (final TypeDeclaration type : _types) {
document.<TypeDeclaration>format(type);
}
}
protected void _format(final TypeDeclaration type, @Extension final IFormattableDocument document) {
ISemanticRegionsFinder _regionFor = this.textRegionExtensions.regionFor(type);
TestLanguageGrammarAccess.TypeDeclarationElements _typeDeclarationAccess = this._testLanguageGrammarAccess.getTypeDeclarationAccess();
Keyword _leftCurlyBracketKeyword_2 = _typeDeclarationAccess.getLeftCurlyBracketKeyword_2();
ISemanticRegion _keyword = _regionFor.keyword(_leftCurlyBracketKeyword_2);
final Procedure1<IHiddenRegionFormatter> _function = (IHiddenRegionFormatter it) -> {
it.newLine();
};
document.append(_keyword, _function);
ISemanticRegionsFinder _regionFor_1 = this.textRegionExtensions.regionFor(type);
TestLanguageGrammarAccess.TypeDeclarationElements _typeDeclarationAccess_1 = this._testLanguageGrammarAccess.getTypeDeclarationAccess();
Keyword _rightCurlyBracketKeyword_4 = _typeDeclarationAccess_1.getRightCurlyBracketKeyword_4();
ISemanticRegion _keyword_1 = _regionFor_1.keyword(_rightCurlyBracketKeyword_4);
final Procedure1<IHiddenRegionFormatter> _function_1 = (IHiddenRegionFormatter it) -> {
it.newLine();
};
ISemanticRegion _prepend = document.prepend(_keyword_1, _function_1);
final Procedure1<IHiddenRegionFormatter> _function_2 = (IHiddenRegionFormatter it) -> {
it.newLine();
};
document.append(_prepend, _function_2);
ISemanticRegionsFinder _regionFor_2 = this.textRegionExtensions.regionFor(type);
TestLanguageGrammarAccess.TypeDeclarationElements _typeDeclarationAccess_2 = this._testLanguageGrammarAccess.getTypeDeclarationAccess();
Keyword _leftCurlyBracketKeyword_2_1 = _typeDeclarationAccess_2.getLeftCurlyBracketKeyword_2();
ISemanticRegion _keyword_2 = _regionFor_2.keyword(_leftCurlyBracketKeyword_2_1);
ISemanticRegionsFinder _regionFor_3 = this.textRegionExtensions.regionFor(type);
TestLanguageGrammarAccess.TypeDeclarationElements _typeDeclarationAccess_3 = this._testLanguageGrammarAccess.getTypeDeclarationAccess();
Keyword _rightCurlyBracketKeyword_4_1 = _typeDeclarationAccess_3.getRightCurlyBracketKeyword_4();
ISemanticRegion _keyword_3 = _regionFor_3.keyword(_rightCurlyBracketKeyword_4_1);
final Procedure1<IHiddenRegionFormatter> _function_3 = (IHiddenRegionFormatter it) -> {
it.indent();
};
document.<ISemanticRegion, ISemanticRegion>interior(_keyword_2, _keyword_3, _function_3);
EList<Property> _properties = type.getProperties();
for (final Property property : _properties) {
document.<Property>format(property);
}
}
protected void _format(final Property property, @Extension final IFormattableDocument document) {
final Procedure1<IHiddenRegionFormatter> _function = (IHiddenRegionFormatter it) -> {
it.newLine();
};
document.<Property>append(property, _function);
}
public void format(final Object model, final IFormattableDocument document) {
if (model instanceof XtextResource) {
_format((XtextResource)model, document);
return;
} else if (model instanceof Model) {
_format((Model)model, document);
return;
} else if (model instanceof Property) {
_format((Property)model, document);
return;
} else if (model instanceof TypeDeclaration) {
_format((TypeDeclaration)model, document);
return;
} else if (model instanceof EObject) {
_format((EObject)model, document);
return;
} else if (model == null) {
_format((Void)null, document);
return;
} else if (model != null) {
_format(model, document);
return;
} else {
throw new IllegalArgumentException("Unhandled parameter types: " +
Arrays.<Object>asList(model, document).toString());
}
}
}

View file

@ -46,7 +46,7 @@ import org.eclipse.xtend.lib.annotations.Data
def PositionImpl getPosition(int offset) {
val l = contents.length
if (offset < 0 || offset >= l)
if (offset < 0 || offset > l)
throw new IndexOutOfBoundsException(offset + " text was : " + contents)
val char NL = '\n'

View file

@ -55,6 +55,7 @@ import io.typefox.lsapi.services.LanguageServer
import io.typefox.lsapi.services.TextDocumentService
import io.typefox.lsapi.services.WindowService
import io.typefox.lsapi.services.WorkspaceService
import java.util.Collections
import java.util.List
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
@ -74,6 +75,7 @@ import org.eclipse.xtext.resource.FileExtensionProvider
import org.eclipse.xtext.resource.IMimeTypeProvider
import org.eclipse.xtext.resource.IResourceServiceProvider
import org.eclipse.xtext.validation.Issue
import org.eclipse.xtext.ide.server.formatting.FormattingService
/**
* @author Sven Efftinge - Initial contribution and API
@ -116,6 +118,8 @@ import org.eclipse.xtext.validation.Issue
resolveProvider = false
triggerCharacters = #["."]
]
documentFormattingProvider = true
documentRangeFormattingProvider = true
]
result.supportedLanguages = newArrayList()
for (serviceProvider : languagesRegistry.extensionToFactoryMap.values.filter(IResourceServiceProvider).toSet) {
@ -436,11 +440,35 @@ import org.eclipse.xtext.validation.Issue
}
override formatting(DocumentFormattingParams params) {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
return requestManager.runRead[ cancelIndicator |
val uri = params.textDocument.uri.toUri
val resourceServiceProvider = uri.resourceServiceProvider
val formatterService = resourceServiceProvider?.get(FormattingService)
if (formatterService === null)
return Collections.emptyList
return workspaceManager.doRead(uri) [ document, resource |
val offset = 0
val length = document.contents.length
return formatterService.format(resource, document, offset, length)
]
]
}
override rangeFormatting(DocumentRangeFormattingParams params) {
throw new UnsupportedOperationException("TODO: auto-generated method stub")
return requestManager.runRead[ cancelIndicator |
val uri = params.textDocument.uri.toUri
val resourceServiceProvider = uri.resourceServiceProvider
val formatterService = resourceServiceProvider?.get(FormattingService)
if (formatterService === null)
return Collections.emptyList
return workspaceManager.doRead(uri) [ document, resource |
val offset = document.getOffSet(params.range.start)
val length = document.getOffSet(params.range.end) - offset
return formatterService.format(resource, document, offset, length)
]
]
}
override onTypeFormatting(DocumentOnTypeFormattingParams params) {

View file

@ -0,0 +1,78 @@
/*******************************************************************************
* Copyright (c) 2016 itemis AG (http://www.itemis.com) 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.ide.server.formatting
import com.google.inject.Inject
import com.google.inject.Provider
import io.typefox.lsapi.TextEdit
import io.typefox.lsapi.impl.RangeImpl
import io.typefox.lsapi.impl.TextEditImpl
import java.util.List
import org.eclipse.xtext.formatting2.FormatterRequest
import org.eclipse.xtext.formatting2.IFormatter2
import org.eclipse.xtext.formatting2.regionaccess.TextRegionAccessBuilder
import org.eclipse.xtext.ide.server.Document
import org.eclipse.xtext.preferences.ITypedPreferenceValues
import org.eclipse.xtext.resource.XtextResource
import org.eclipse.xtext.util.ITextRegion
import org.eclipse.xtext.util.TextRegion
/**
* Language Service Implementation for Formatting and Range-Formatting
*
* @author Christian Dietrich - Initial contribution and API
* @since 2.11
*/
class FormattingService {
@Inject(optional = true) Provider<IFormatter2> formatter2Provider
@Inject Provider<FormatterRequest> formatterRequestProvider
@Inject TextRegionAccessBuilder regionBuilder
def List<TextEdit> format(XtextResource resource, Document document, int offset, int length) {
if (formatter2Provider !== null) {
val replacements = format2(resource, new TextRegion(offset, length), null)
return replacements.map[
r | document.toTextEdit(r.replacementText, r.offset, r.length)
].<TextEdit>toList
} else {
return <TextEdit>newArrayList
}
}
protected def TextEdit toTextEdit(Document document, String formattedText, int startOffset, int length) {
new TextEditImpl => [
newText = formattedText
range = new RangeImpl => [
start = document.getPosition(startOffset)
end = document.getPosition(startOffset+length)
]
]
}
protected def format2(XtextResource resource, ITextRegion selection, ITypedPreferenceValues preferences) {
val request = formatterRequestProvider.get()
request.allowIdentityEdits = false
request.formatUndefinedHiddenRegionsOnly = false
if (selection !== null)
request.regions = #[selection]
if (preferences !== null) {
request.preferences = preferences
}
val regionAccess = regionBuilder.forNodeModel(resource).create()
request.textRegionAccess = regionAccess
val formatter2 = formatter2Provider.get();
val replacements = formatter2.format(request)
return replacements
}
}

View file

@ -56,7 +56,7 @@ public class Document {
public PositionImpl getPosition(final int offset) {
final int l = this.contents.length();
if (((offset < 0) || (offset >= l))) {
if (((offset < 0) || (offset > l))) {
String _plus = (Integer.valueOf(offset) + " text was : ");
String _plus_1 = (_plus + this.contents);
throw new IndexOutOfBoundsException(_plus_1);

View file

@ -92,6 +92,7 @@ import org.eclipse.xtext.ide.server.concurrent.CancellableIndicator;
import org.eclipse.xtext.ide.server.concurrent.RequestManager;
import org.eclipse.xtext.ide.server.contentassist.ContentAssistService;
import org.eclipse.xtext.ide.server.findReferences.WorkspaceResourceAccess;
import org.eclipse.xtext.ide.server.formatting.FormattingService;
import org.eclipse.xtext.ide.server.hover.HoverService;
import org.eclipse.xtext.ide.server.symbol.DocumentSymbolService;
import org.eclipse.xtext.ide.server.symbol.WorkspaceSymbolService;
@ -176,6 +177,8 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Win
};
CompletionOptionsImpl _doubleArrow = ObjectExtensions.<CompletionOptionsImpl>operator_doubleArrow(_completionOptionsImpl, _function_1);
it.setCompletionProvider(_doubleArrow);
it.setDocumentFormattingProvider(Boolean.valueOf(true));
it.setDocumentRangeFormattingProvider(Boolean.valueOf(true));
};
ServerCapabilitiesImpl _doubleArrow = ObjectExtensions.<ServerCapabilitiesImpl>operator_doubleArrow(_serverCapabilitiesImpl, _function);
result.setCapabilities(_doubleArrow);
@ -735,12 +738,58 @@ public class LanguageServerImpl implements LanguageServer, WorkspaceService, Win
@Override
public CompletableFuture<List<? extends TextEdit>> formatting(final DocumentFormattingParams params) {
throw new UnsupportedOperationException("TODO: auto-generated method stub");
final Function1<CancelIndicator, List<? extends TextEdit>> _function = (CancelIndicator cancelIndicator) -> {
TextDocumentIdentifier _textDocument = params.getTextDocument();
String _uri = _textDocument.getUri();
final URI uri = this._uriExtensions.toUri(_uri);
final IResourceServiceProvider resourceServiceProvider = this.languagesRegistry.getResourceServiceProvider(uri);
FormattingService _get = null;
if (resourceServiceProvider!=null) {
_get=resourceServiceProvider.<FormattingService>get(FormattingService.class);
}
final FormattingService formatterService = _get;
if ((formatterService == null)) {
return Collections.<TextEdit>emptyList();
}
final Function2<Document, XtextResource, List<TextEdit>> _function_1 = (Document document, XtextResource resource) -> {
final int offset = 0;
String _contents = document.getContents();
final int length = _contents.length();
return formatterService.format(resource, document, offset, length);
};
return this.workspaceManager.<List<? extends TextEdit>>doRead(uri, _function_1);
};
return this.requestManager.<List<? extends TextEdit>>runRead(_function);
}
@Override
public CompletableFuture<List<? extends TextEdit>> rangeFormatting(final DocumentRangeFormattingParams params) {
throw new UnsupportedOperationException("TODO: auto-generated method stub");
final Function1<CancelIndicator, List<? extends TextEdit>> _function = (CancelIndicator cancelIndicator) -> {
TextDocumentIdentifier _textDocument = params.getTextDocument();
String _uri = _textDocument.getUri();
final URI uri = this._uriExtensions.toUri(_uri);
final IResourceServiceProvider resourceServiceProvider = this.languagesRegistry.getResourceServiceProvider(uri);
FormattingService _get = null;
if (resourceServiceProvider!=null) {
_get=resourceServiceProvider.<FormattingService>get(FormattingService.class);
}
final FormattingService formatterService = _get;
if ((formatterService == null)) {
return Collections.<TextEdit>emptyList();
}
final Function2<Document, XtextResource, List<TextEdit>> _function_1 = (Document document, XtextResource resource) -> {
Range _range = params.getRange();
Position _start = _range.getStart();
final int offset = document.getOffSet(_start);
Range _range_1 = params.getRange();
Position _end = _range_1.getEnd();
int _offSet = document.getOffSet(_end);
final int length = (_offSet - offset);
return formatterService.format(resource, document, offset, length);
};
return this.workspaceManager.<List<? extends TextEdit>>doRead(uri, _function_1);
};
return this.requestManager.<List<? extends TextEdit>>runRead(_function);
}
@Override

View file

@ -0,0 +1,103 @@
/**
* Copyright (c) 2016 itemis AG (http://www.itemis.com) 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.ide.server.formatting;
import com.google.inject.Inject;
import com.google.inject.Provider;
import io.typefox.lsapi.TextEdit;
import io.typefox.lsapi.impl.PositionImpl;
import io.typefox.lsapi.impl.RangeImpl;
import io.typefox.lsapi.impl.TextEditImpl;
import java.util.Collections;
import java.util.List;
import org.eclipse.xtext.formatting2.FormatterRequest;
import org.eclipse.xtext.formatting2.IFormatter2;
import org.eclipse.xtext.formatting2.regionaccess.ITextRegionAccess;
import org.eclipse.xtext.formatting2.regionaccess.ITextReplacement;
import org.eclipse.xtext.formatting2.regionaccess.TextRegionAccessBuilder;
import org.eclipse.xtext.ide.server.Document;
import org.eclipse.xtext.preferences.ITypedPreferenceValues;
import org.eclipse.xtext.resource.XtextResource;
import org.eclipse.xtext.util.ITextRegion;
import org.eclipse.xtext.util.TextRegion;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.ListExtensions;
import org.eclipse.xtext.xbase.lib.ObjectExtensions;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
/**
* Language Service Implementation for Formatting and Range-Formatting
*
* @author Christian Dietrich - Initial contribution and API
* @since 2.11
*/
@SuppressWarnings("all")
public class FormattingService {
@Inject(optional = true)
private Provider<IFormatter2> formatter2Provider;
@Inject
private Provider<FormatterRequest> formatterRequestProvider;
@Inject
private TextRegionAccessBuilder regionBuilder;
public List<TextEdit> format(final XtextResource resource, final Document document, final int offset, final int length) {
if ((this.formatter2Provider != null)) {
TextRegion _textRegion = new TextRegion(offset, length);
final List<ITextReplacement> replacements = this.format2(resource, _textRegion, null);
final Function1<ITextReplacement, TextEdit> _function = (ITextReplacement r) -> {
String _replacementText = r.getReplacementText();
int _offset = r.getOffset();
int _length = r.getLength();
return this.toTextEdit(document, _replacementText, _offset, _length);
};
List<TextEdit> _map = ListExtensions.<ITextReplacement, TextEdit>map(replacements, _function);
return IterableExtensions.<TextEdit>toList(_map);
} else {
return CollectionLiterals.<TextEdit>newArrayList();
}
}
protected TextEdit toTextEdit(final Document document, final String formattedText, final int startOffset, final int length) {
TextEditImpl _textEditImpl = new TextEditImpl();
final Procedure1<TextEditImpl> _function = (TextEditImpl it) -> {
it.setNewText(formattedText);
RangeImpl _rangeImpl = new RangeImpl();
final Procedure1<RangeImpl> _function_1 = (RangeImpl it_1) -> {
PositionImpl _position = document.getPosition(startOffset);
it_1.setStart(_position);
PositionImpl _position_1 = document.getPosition((startOffset + length));
it_1.setEnd(_position_1);
};
RangeImpl _doubleArrow = ObjectExtensions.<RangeImpl>operator_doubleArrow(_rangeImpl, _function_1);
it.setRange(_doubleArrow);
};
return ObjectExtensions.<TextEditImpl>operator_doubleArrow(_textEditImpl, _function);
}
protected List<ITextReplacement> format2(final XtextResource resource, final ITextRegion selection, final ITypedPreferenceValues preferences) {
final FormatterRequest request = this.formatterRequestProvider.get();
request.setAllowIdentityEdits(false);
request.setFormatUndefinedHiddenRegionsOnly(false);
if ((selection != null)) {
request.setRegions(Collections.<ITextRegion>unmodifiableList(CollectionLiterals.<ITextRegion>newArrayList(selection)));
}
if ((preferences != null)) {
request.setPreferences(preferences);
}
TextRegionAccessBuilder _forNodeModel = this.regionBuilder.forNodeModel(resource);
final ITextRegionAccess regionAccess = _forNodeModel.create();
request.setTextRegionAccess(regionAccess);
final IFormatter2 formatter2 = this.formatter2Provider.get();
final List<ITextReplacement> replacements = formatter2.format(request);
return replacements;
}
}

View file

@ -52,6 +52,11 @@ import org.eclipse.xtext.util.Files
import org.eclipse.xtext.util.Modules2
import org.junit.Assert
import org.junit.Before
import io.typefox.lsapi.impl.RangeImpl
import io.typefox.lsapi.impl.PositionImpl
import io.typefox.lsapi.builders.DocumentFormattingParamsBuilder
import org.eclipse.xtext.ide.server.Document
import io.typefox.lsapi.builders.DocumentRangeFormattingParamsBuilder
/**
* @author Sven Efftinge - Initial contribution and API
@ -318,7 +323,42 @@ abstract class AbstractLanguageServerTest implements Consumer<PublishDiagnostics
}
def void assertEquals(String expected, String actual) {
Assert.assertEquals(expected.replace('\t', ' '), actual)
Assert.assertEquals(expected.replace('\t', ' '), actual.replace('\t', ' '))
}
protected def testFormatting((FormattingConfiguration)=>void configurator) {
val extension configuration = new FormattingConfiguration
configuration.filePath = 'MyModel.' + fileExtension
configurator.apply(configuration)
val fileUri = filePath -> model
initialize
open(fileUri, model)
val changes = languageServer.formatting(new DocumentFormattingParamsBuilder [
textDocument(fileUri)
].build)
val result = new Document(1, model).applyChanges(<TextEdit>newArrayList(changes.get()).reverse)
assertEquals(configuration.expectedText, result.contents)
}
protected def testRangeFormatting((RangeFormattingConfiguration)=>void configurator) {
val extension configuration = new RangeFormattingConfiguration
configuration.filePath = 'MyModel.' + fileExtension
configurator.apply(configuration)
val fileUri = filePath -> model
initialize
open(fileUri, model)
val changes = languageServer.rangeFormatting(new DocumentRangeFormattingParamsBuilder [
textDocument(fileUri)
range(configuration.range)
].build)
val result = new Document(1, model).applyChanges(<TextEdit>newArrayList(changes.get()).reverse)
assertEquals(configuration.expectedText, result.contents)
}
}
@ -366,3 +406,16 @@ class TextDocumentConfiguration {
String model = ''
String filePath
}
@Accessors
class FormattingConfiguration extends TextDocumentConfiguration {
String expectedText = ''
}
@Accessors
class RangeFormattingConfiguration extends FormattingConfiguration {
Range range = new RangeImpl=>[
start = new PositionImpl(0,0)
end = new PositionImpl(0,1)
]
}

View file

@ -19,6 +19,8 @@ import io.typefox.lsapi.CompletionList;
import io.typefox.lsapi.Diagnostic;
import io.typefox.lsapi.DidCloseTextDocumentParams;
import io.typefox.lsapi.DidOpenTextDocumentParams;
import io.typefox.lsapi.DocumentFormattingParams;
import io.typefox.lsapi.DocumentRangeFormattingParams;
import io.typefox.lsapi.Hover;
import io.typefox.lsapi.InitializeParams;
import io.typefox.lsapi.InitializeResult;
@ -34,6 +36,8 @@ import io.typefox.lsapi.TextDocumentPositionParams;
import io.typefox.lsapi.TextEdit;
import io.typefox.lsapi.builders.DidCloseTextDocumentParamsBuilder;
import io.typefox.lsapi.builders.DidOpenTextDocumentParamsBuilder;
import io.typefox.lsapi.builders.DocumentFormattingParamsBuilder;
import io.typefox.lsapi.builders.DocumentRangeFormattingParamsBuilder;
import io.typefox.lsapi.builders.InitializeParamsBuilder;
import io.typefox.lsapi.builders.ReferenceParamsBuilder;
import io.typefox.lsapi.builders.TextDocumentItemBuilder;
@ -48,6 +52,7 @@ import java.io.FileWriter;
import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@ -57,6 +62,7 @@ import org.eclipse.xtend.lib.annotations.Accessors;
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.LanguageInfo;
import org.eclipse.xtext.ide.server.Document;
import org.eclipse.xtext.ide.server.LanguageServerImpl;
import org.eclipse.xtext.ide.server.ServerModule;
import org.eclipse.xtext.ide.server.UriExtensions;
@ -64,7 +70,9 @@ import org.eclipse.xtext.ide.server.concurrent.RequestManager;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.testing.DefinitionTestConfiguration;
import org.eclipse.xtext.testing.DocumentSymbolConfiguraiton;
import org.eclipse.xtext.testing.FormattingConfiguration;
import org.eclipse.xtext.testing.HoverTestConfiguration;
import org.eclipse.xtext.testing.RangeFormattingConfiguration;
import org.eclipse.xtext.testing.ReferenceTestConfiguration;
import org.eclipse.xtext.testing.TestCompletionConfiguration;
import org.eclipse.xtext.testing.WorkspaceSymbolConfiguraiton;
@ -72,9 +80,11 @@ import org.eclipse.xtext.util.CancelIndicator;
import org.eclipse.xtext.util.Files;
import org.eclipse.xtext.util.Modules2;
import org.eclipse.xtext.xbase.lib.CollectionLiterals;
import org.eclipse.xtext.xbase.lib.Conversions;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xbase.lib.Extension;
import org.eclipse.xtext.xbase.lib.Functions.Function1;
import org.eclipse.xtext.xbase.lib.ListExtensions;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.eclipse.xtext.xbase.lib.Pure;
import org.eclipse.xtext.xbase.lib.StringExtensions;
@ -597,7 +607,74 @@ public abstract class AbstractLanguageServerTest implements Consumer<PublishDiag
public void assertEquals(final String expected, final String actual) {
String _replace = expected.replace("\t", " ");
Assert.assertEquals(_replace, actual);
String _replace_1 = actual.replace("\t", " ");
Assert.assertEquals(_replace, _replace_1);
}
protected void testFormatting(final Procedure1<? super FormattingConfiguration> configurator) {
try {
@Extension
final FormattingConfiguration configuration = new FormattingConfiguration();
configuration.setFilePath(("MyModel." + this.fileExtension));
configurator.apply(configuration);
String _filePath = configuration.getFilePath();
String _model = configuration.getModel();
final String fileUri = this.operator_mappedTo(_filePath, _model);
this.initialize();
String _model_1 = configuration.getModel();
this.open(fileUri, _model_1);
final Procedure1<DocumentFormattingParamsBuilder> _function = (DocumentFormattingParamsBuilder it) -> {
it.textDocument(fileUri);
};
DocumentFormattingParamsBuilder _documentFormattingParamsBuilder = new DocumentFormattingParamsBuilder(_function);
DocumentFormattingParams _build = _documentFormattingParamsBuilder.build();
final CompletableFuture<List<? extends TextEdit>> changes = this.languageServer.formatting(_build);
String _model_2 = configuration.getModel();
Document _document = new Document(1, _model_2);
List<? extends TextEdit> _get = changes.get();
ArrayList<TextEdit> _newArrayList = CollectionLiterals.<TextEdit>newArrayList(((TextEdit[])Conversions.unwrapArray(_get, TextEdit.class)));
List<TextEdit> _reverse = ListExtensions.<TextEdit>reverse(_newArrayList);
final Document result = _document.applyChanges(_reverse);
String _expectedText = configuration.getExpectedText();
String _contents = result.getContents();
this.assertEquals(_expectedText, _contents);
} catch (Throwable _e) {
throw Exceptions.sneakyThrow(_e);
}
}
protected void testRangeFormatting(final Procedure1<? super RangeFormattingConfiguration> configurator) {
try {
@Extension
final RangeFormattingConfiguration configuration = new RangeFormattingConfiguration();
configuration.setFilePath(("MyModel." + this.fileExtension));
configurator.apply(configuration);
String _filePath = configuration.getFilePath();
String _model = configuration.getModel();
final String fileUri = this.operator_mappedTo(_filePath, _model);
this.initialize();
String _model_1 = configuration.getModel();
this.open(fileUri, _model_1);
final Procedure1<DocumentRangeFormattingParamsBuilder> _function = (DocumentRangeFormattingParamsBuilder it) -> {
it.textDocument(fileUri);
Range _range = configuration.getRange();
it.range(_range);
};
DocumentRangeFormattingParamsBuilder _documentRangeFormattingParamsBuilder = new DocumentRangeFormattingParamsBuilder(_function);
DocumentRangeFormattingParams _build = _documentRangeFormattingParamsBuilder.build();
final CompletableFuture<List<? extends TextEdit>> changes = this.languageServer.rangeFormatting(_build);
String _model_2 = configuration.getModel();
Document _document = new Document(1, _model_2);
List<? extends TextEdit> _get = changes.get();
ArrayList<TextEdit> _newArrayList = CollectionLiterals.<TextEdit>newArrayList(((TextEdit[])Conversions.unwrapArray(_get, TextEdit.class)));
List<TextEdit> _reverse = ListExtensions.<TextEdit>reverse(_newArrayList);
final Document result = _document.applyChanges(_reverse);
String _expectedText = configuration.getExpectedText();
String _contents = result.getContents();
this.assertEquals(_expectedText, _contents);
} catch (Throwable _e) {
throw Exceptions.sneakyThrow(_e);
}
}
protected String toExpectation(final Object elements) {

View file

@ -0,0 +1,27 @@
/**
* Copyright (c) 2016 TypeFox GmbH (http://www.typefox.io) 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.testing;
import org.eclipse.xtend.lib.annotations.Accessors;
import org.eclipse.xtext.testing.TextDocumentConfiguration;
import org.eclipse.xtext.xbase.lib.Pure;
@Accessors
@SuppressWarnings("all")
public class FormattingConfiguration extends TextDocumentConfiguration {
private String expectedText = "";
@Pure
public String getExpectedText() {
return this.expectedText;
}
public void setExpectedText(final String expectedText) {
this.expectedText = expectedText;
}
}

View file

@ -0,0 +1,38 @@
/**
* Copyright (c) 2016 TypeFox GmbH (http://www.typefox.io) 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.testing;
import io.typefox.lsapi.Range;
import io.typefox.lsapi.impl.PositionImpl;
import io.typefox.lsapi.impl.RangeImpl;
import org.eclipse.xtend.lib.annotations.Accessors;
import org.eclipse.xtext.testing.FormattingConfiguration;
import org.eclipse.xtext.xbase.lib.ObjectExtensions;
import org.eclipse.xtext.xbase.lib.Procedures.Procedure1;
import org.eclipse.xtext.xbase.lib.Pure;
@Accessors
@SuppressWarnings("all")
public class RangeFormattingConfiguration extends FormattingConfiguration {
private Range range = ObjectExtensions.<RangeImpl>operator_doubleArrow(new RangeImpl(),
((Procedure1<RangeImpl>) (RangeImpl it) -> {
PositionImpl _positionImpl = new PositionImpl(0, 0);
it.setStart(_positionImpl);
PositionImpl _positionImpl_1 = new PositionImpl(0, 1);
it.setEnd(_positionImpl_1);
}));
@Pure
public Range getRange() {
return this.range;
}
public void setRange(final Range range) {
this.range = range;
}
}