Skip to content

Commit

Permalink
Provide basic experimental formatter which supports invalid XML
Browse files Browse the repository at this point in the history
Fixes eclipse-lemminx#1195

Signed-off-by: azerr <[email protected]>
  • Loading branch information
angelozerr committed Apr 24, 2022
1 parent 6113791 commit 337fe5d
Show file tree
Hide file tree
Showing 26 changed files with 5,604 additions and 823 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -323,27 +323,17 @@ public CompletableFuture<List<Either<SymbolInformation, DocumentSymbol>>> docume

@Override
public CompletableFuture<List<? extends TextEdit>> formatting(DocumentFormattingParams params) {
return computeAsync((cancelChecker) -> {
String uri = params.getTextDocument().getUri();
TextDocument document = getDocument(uri);
if (document == null) {
return null;
}
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
CompositeSettings settings = new CompositeSettings(getSharedSettings(), params.getOptions());
return getXMLLanguageService().format(document, null, settings);
return getXMLLanguageService().format(xmlDocument, null, settings);
});
}

@Override
public CompletableFuture<List<? extends TextEdit>> rangeFormatting(DocumentRangeFormattingParams params) {
return computeAsync((cancelChecker) -> {
String uri = params.getTextDocument().getUri();
TextDocument document = getDocument(uri);
if (document == null) {
return null;
}
return computeDOMAsync(params.getTextDocument(), (cancelChecker, xmlDocument) -> {
CompositeSettings settings = new CompositeSettings(getSharedSettings(), params.getOptions());
return getXMLLanguageService().format(document, params.getRange(), settings);
return getXMLLanguageService().format(xmlDocument, params.getRange(), settings);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ public String lineText(int lineNumber) throws BadLocationException {
return text.substring(line.offset, line.offset + line.length);
}

public int lineOffsetAt(int position) throws BadLocationException {
ILineTracker lineTracker = getLineTracker();
Line line = lineTracker.getLineInformationOfOffset(position);
return line.offset;
}

public String lineDelimiter(int lineNumber) throws BadLocationException {
ILineTracker lineTracker = getLineTracker();
String lineDelimiter = lineTracker.getLineDelimiter(lineNumber);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
package org.eclipse.lemminx.dom;

import org.eclipse.lemminx.utils.StringUtils;
import org.w3c.dom.DOMException;

/**
Expand Down Expand Up @@ -61,7 +62,8 @@ public String getWholeText() {
*/
@Override
public boolean isElementContentWhitespace() {
throw new UnsupportedOperationException();
String text = getOwnerDocument().getOwnerDocument().getText();
return StringUtils.isWhitespace(text, getStart(), getEnd());
}

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelCodeLensParticipant;
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelCompletionParticipant;
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelDocumentLinkParticipant;
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelFormatterParticipant;
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelHoverParticipant;
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelSymbolsProviderParticipant;
import org.eclipse.lemminx.extensions.contentmodel.participants.ContentModelTypeDefinitionParticipant;
Expand Down Expand Up @@ -85,6 +86,8 @@ public class ContentModelPlugin implements IXMLExtension {

private DocumentTelemetryParticipant documentTelemetryParticipant;

private ContentModelFormatterParticipant formatterParticipant;

public ContentModelPlugin() {
completionParticipant = new ContentModelCompletionParticipant();
hoverParticipant = new ContentModelHoverParticipant();
Expand Down Expand Up @@ -207,7 +210,9 @@ public void start(InitializeParams params, XMLExtensionsRegistry registry) {
documentTelemetryParticipant = new DocumentTelemetryParticipant(registry.getTelemetryManager(),
contentModelManager);
registry.registerDocumentLifecycleParticipant(documentTelemetryParticipant);

formatterParticipant = new ContentModelFormatterParticipant(contentModelManager);
registry.registerFormatterParticipant(formatterParticipant);

// Register custom commands to re-validate XML files
IXMLCommandService commandService = registry.getCommandService();
if (commandService != null) {
Expand Down Expand Up @@ -236,7 +241,8 @@ public void stop(XMLExtensionsRegistry registry) {
registry.unregisterSymbolsProviderParticipant(symbolsProviderParticipant);
registry.unregisterCodeLensParticipant(codeLensParticipant);
registry.unregisterDocumentLifecycleParticipant(documentTelemetryParticipant);

registry.unregisterFormatterParticipant(formatterParticipant);

// Un-register custom commands to re-validate XML files
IXMLCommandService commandService = registry.getCommandService();
if (commandService != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,22 @@ default String getName(String prefix) {
*/
String getDocumentURI();

/**
* Returns true if the element is a string type (ex : xs:string) and false
* otherwise.
*
* @return true if the element is a string type (ex : xs:string) and false
* otherwise.
*/
boolean isStringType();

/**
* Returns true if the element can contains text and element both and false
* otherwise.
*
* @return true if the element can contains text and element both and false
* otherwise.
*/
boolean isMixedContent();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/**
* Copyright (c) 2022 Red Hat, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*/
package org.eclipse.lemminx.extensions.contentmodel.participants;

import java.util.Collection;

import org.eclipse.lemminx.dom.DOMElement;
import org.eclipse.lemminx.extensions.contentmodel.model.CMDocument;
import org.eclipse.lemminx.extensions.contentmodel.model.CMElementDeclaration;
import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lemminx.services.extensions.format.IFormatterParticipant;
import org.eclipse.lemminx.services.format.FormatElementCategory;
import org.eclipse.lemminx.services.format.XMLFormattingConstraints;
import org.eclipse.lemminx.settings.SharedSettings;

/**
* Formatter participant which uses XSD/DTD grammar information to know the
* {@link FormatElementCategory} of a given element.
*
* <p>
*
* This participant is enabled when 'xml.format.grammarAwareFormatting' setting
* is set to true.
*
* </p>
*
* @author Angelo ZERR
*
*/
public class ContentModelFormatterParticipant implements IFormatterParticipant {

private static final String GRAMMAR_AWARE_FORMATTING = "grammarAwareFormatting";

private final ContentModelManager contentModelManager;

public ContentModelFormatterParticipant(ContentModelManager contentModelManager) {
this.contentModelManager = contentModelManager;
}

@Override
public FormatElementCategory getFormatElementCategory(DOMElement element,
XMLFormattingConstraints parentConstraints, SharedSettings sharedSettings) {
Boolean enabled = sharedSettings.getFormattingSettings().getBoolean(GRAMMAR_AWARE_FORMATTING);
if (enabled != null && !enabled) {
return null;
}

Collection<CMDocument> cmDocuments = contentModelManager.findCMDocument(element);
for (CMDocument cmDocument : cmDocuments) {
CMElementDeclaration cmElement = cmDocument.findCMElement(element);
if (cmElement != null) {
if (cmElement.isStringType()) {
return FormatElementCategory.PreserveSpace;
}
if (cmElement.isMixedContent()) {
return FormatElementCategory.MixedContent;
}
}
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,14 @@ public int getIndex() {
public String getDocumentURI() {
return document.getURI();
}

@Override
public boolean isStringType() {
return false;
}

@Override
public boolean isMixedContent() {
return super.type == XMLElementDecl.TYPE_MIXED;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,11 @@ public Collection<CMElementDeclaration> getPossibleElements(DOMElement parentEle
/**
* Returns the possible elements declaration if the given declaration is an
* xs:any and null otherwise.
*
*
* @param declaration the element, wildcard declaration.
* @return the possible elements declaration if the given declaration is an
* xs:any and null otherwise.
*
*
*/
private Collection<CMElementDeclaration> getXSAnyElements(Object declaration) {
short processContents = getXSAnyProcessContents(declaration);
Expand All @@ -230,7 +230,7 @@ private Collection<CMElementDeclaration> getXSAnyElements(Object declaration) {
/**
* Returns the value of the xs:any/@processContents if the given element is a
* xs:any and {@link #PC_UNKWOWN} otherwise.
*
*
* @param declaration the element, wildcard declaration.
* @return the value of the xs:any/@processContents if the given element is a
* xs:any and {@link #PC_UNKWOWN} otherwise.
Expand All @@ -248,7 +248,7 @@ private static short getXSAnyProcessContents(Object declaration) {
/**
* Returns list of element (QName) of child elements of the given parent element
* upon the given offset
*
*
* @param parentElement the parent element
* @param offset the offset where child element must be belong to
* @return list of element (QName) of child elements of the given parent element
Expand Down Expand Up @@ -355,7 +355,7 @@ public String getDocumentation(ISharedSettingsRequest request) {
/**
* Returns list of xs:annotation from the element declaration or type
* declaration.
*
*
* @return list of xs:annotation from the element declaration or type
* declaration.
*/
Expand Down Expand Up @@ -490,4 +490,31 @@ public String getDocumentURI() {
SchemaGrammar schemaGrammar = document.getOwnerSchemaGrammar(elementDeclaration);
return CMXSDDocument.getSchemaURI(schemaGrammar);
}

@Override
public boolean isStringType() {
XSTypeDefinition typeDefinition = elementDeclaration.getTypeDefinition();
if (typeDefinition != null) {
XSSimpleTypeDefinition simpleDefinition = null;
if (typeDefinition.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) {
simpleDefinition = (XSSimpleTypeDefinition) typeDefinition;
} else if (typeDefinition.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
simpleDefinition = ((XSComplexTypeDefinition) typeDefinition).getSimpleType();
}
if (simpleDefinition != null) {
return "string".equals(simpleDefinition.getName());
}
}
return false;
}

@Override
public boolean isMixedContent() {
XSTypeDefinition typeDefinition = elementDeclaration.getTypeDefinition();
if (typeDefinition != null && typeDefinition.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
XSComplexTypeDefinition complexTypeDefinition = (XSComplexTypeDefinition) typeDefinition;
return complexTypeDefinition.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_MIXED;
}
return false;
}
}
Loading

0 comments on commit 337fe5d

Please sign in to comment.