/** * Copyright (c) 2012 Cloudsmith Inc. and other contributors, as listed below. * 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 * * Contributors: * Cloudsmith * */ package org.cloudsmith.geppetto.pp.dsl.formatting; import org.cloudsmith.geppetto.pp.dsl.formatting.PPSemanticLayout.ResourceStyle; import org.cloudsmith.geppetto.pp.dsl.formatting.PPSemanticLayout.StatementStyle; import org.cloudsmith.geppetto.pp.dsl.services.PPGrammarAccess; import org.cloudsmith.geppetto.pp.dsl.services.PPGrammarAccess.ResourceExpressionElements; import org.cloudsmith.xtext.dommodel.IDomNode.NodeType; import org.cloudsmith.xtext.dommodel.formatter.css.DefaultStylesheetProvider; import org.cloudsmith.xtext.dommodel.formatter.css.DomCSS; import org.cloudsmith.xtext.dommodel.formatter.css.IFunctionFactory; import org.cloudsmith.xtext.dommodel.formatter.css.IStyleFactory; import org.cloudsmith.xtext.dommodel.formatter.css.Select; import org.cloudsmith.xtext.dommodel.formatter.css.Select.Selector; import org.cloudsmith.xtext.dommodel.formatter.css.StyleSet; import com.google.common.collect.Iterables; import com.google.inject.Inject; /** * Provides the style sheet for PP. * */ public class PPStylesheetProvider extends DefaultStylesheetProvider { @Inject private IStyleFactory styles; @Inject private IFunctionFactory functions; @Inject PPGrammarAccess grammarAccess; @Override public DomCSS get() { // look up grammar elements and create reusable selectors to make the style sheet more readable // final Selector attributeOperationsComma = Select.grammar(grammarAccess.getAttributeOperationsAccess().getCommaKeyword_1_0_0()); final Selector resourceBodyTitleColon = Select.grammar(grammarAccess.getResourceBodyAccess().getColonKeyword_0_1()); final Selector resourceSingleBodyTitle = Select.node(ResourceStyle.SINGLEBODY_TITLE); final Selector resourceSingleBodyNoTitle = Select.node(ResourceStyle.SINGLEBODY_NO_TITLE); final Selector resourceCompactable = Select.node(ResourceStyle.COMPACTABLE); final Selector inline = Select.node(StatementStyle.INLINE); final Selector notInline = Select.not(Select.node(StatementStyle.INLINE)); final Selector lambdaLeftBrace = Select.grammar( grammarAccess.getJava8LambdaAccess().getLeftCurlyBracketKeyword_5(), grammarAccess.getRubyLambdaAccess().getLAMBDATerminalRuleCall_0()); final Selector lambdaRightBrace = Select.grammar( grammarAccess.getJava8LambdaAccess().getRightCurlyBracketKeyword_7(), grammarAccess.getRubyLambdaAccess().getRightCurlyBracketKeyword_6()); final Selector inCompactableResource = Select.containment(resourceCompactable); final Selector inASingleBodiesResourceWithTitle = Select.containment(resourceSingleBodyTitle); final Selector inASingleBodiesResourceWithoutTitle = Select.containment(resourceSingleBodyNoTitle); final ResourceExpressionElements resourceExpressionAccess = grammarAccess.getResourceExpressionAccess(); final Selector resourceLeftCurlyBracket = Select.grammar(resourceExpressionAccess.findKeywords("{")); final Selector resourceRightCurlyBracket = Select.grammar(resourceExpressionAccess.findKeywords("}")); final Selector resourceBodySemicolon = Select.grammar(resourceExpressionAccess.findKeywords(";")); final Selector optionalResourceEndBodySemicolon = Select.grammar( resourceExpressionAccess.getSemicolonKeyword_0_1_2_2(), // resourceExpressionAccess.getSemicolonKeyword_1_3_2()); final Selector elseAndElsifKeywords = Select.grammar(Iterables.concat( grammarAccess.getIfExpressionAccess().findKeywords("else", "elsif"), // grammarAccess.getElseIfExpressionAccess().findKeywords("else", "elsif"))); final Selector relationshipEdgeOperator = Select.grammar(grammarAccess.getRelationshipExpressionAccess().getOpNameEdgeOperatorParserRuleCall_1_1_0()); final Selector atExpressionLeftBracket = Select.grammar(grammarAccess.getAtExpressionAccess().getLeftSquareBracketKeyword_1_1()); // interpolation final Selector interpolationStart = Select.grammar( grammarAccess.getTextExpressionAccess().getDollarSignLeftCurlyBracketKeyword_1_1(), // grammarAccess.getUnquotedStringAccess().getDollarSignLeftCurlyBracketKeyword_1()); final Selector interpolationEnd = Select.grammar( grammarAccess.getTextExpressionAccess().getRightCurlyBracketKeyword_1_3(), // grammarAccess.getUnquotedStringAccess().getRightCurlyBracketKeyword_3()); final StyleSet resourceRightCurlyStyleNoDedent = StyleSet.withImmutableStyles(// styles.oneLineBreak(), // styles.noSpaceUnlessWrapped(), // styles.dedent(0)); final StyleSet noSpaceUnlessWrappedNoLine = StyleSet.withImmutableStyles( styles.noSpaceUnlessWrapped(), styles.noLineBreak()); // final StyleSet noSpaceNoLine = StyleSet.withImmutableStyles(styles.noSpace(), styles.noLineBreak()); final StyleSet noSpaceOneLine = StyleSet.withImmutableStyles( styles.noSpaceUnlessWrapped(), styles.oneLineBreak()); final StyleSet noSpaceOneLineOptionallyTwo = StyleSet.withImmutableStyles( styles.noSpaceUnlessWrapped(), styles.lineBreaks(1, 1, 2)); final StyleSet noSpaceTwoLines = StyleSet.withImmutableStyles( styles.noSpaceUnlessWrapped(), styles.lineBreaks(2, 2, 2)); final StyleSet oneSpaceNoLine = StyleSet.withImmutableStyles(styles.oneSpace(), styles.noLineBreak()); final StyleSet oneOptionalSpaceNoLine = StyleSet.withImmutableStyles(// styles.oneSpace(), styles.noLineBreak()); final StyleSet oneSpaceOneOptionalLine = StyleSet.withImmutableStyles(// styles.oneSpace(), styles.lineBreaks(0, 0, 1, true, true)); DomCSS css = super.get(); css.addRules( // RESOURCE // Resource expression with single titled body, should allow body to have title on same line // so, no line break, but one space here Select.and(inASingleBodiesResourceWithTitle, Select.whitespaceAfter(resourceLeftCurlyBracket)) // .withStyles( // styles.noLineBreak(), // styles.oneSpace(), // styles.indent(0))// .withRuleName("WsAfterLeftCurlyBracket.InASingleBodiedResourceWithTitle"), // Resource expression with single body without title, should not indent, as the body is // indented Select.and(inASingleBodiesResourceWithoutTitle, Select.whitespaceAfter(resourceLeftCurlyBracket)) // .withStyles( // styles.oneLineBreak(), // styles.indent(0)) // .withRuleName("WsAfterLeftCurlyBracket.InASingleBodiedResourceWithoutTitle"), Select.and(Select.important(inCompactableResource), Select.whitespaceBefore(resourceRightCurlyBracket)) // .withStyles(styles.noLineBreak(), styles.oneSpace()) // .withRuleName("WsBeforeRightCurlyBracket-Compactable"), // Select.and(inASingleBodiesResourceWithTitle, Select.whitespaceBefore(resourceRightCurlyBracket)) // .withStyle(resourceRightCurlyStyleNoDedent)// .withRuleName("WsBeforeRightCurlyBracket.InASingleBodiesResourceWithTitle."), Select.and(inASingleBodiesResourceWithoutTitle, Select.whitespaceBefore(resourceRightCurlyBracket)) // .withStyle(resourceRightCurlyStyleNoDedent)// .withRuleName("WsBeforeRightCurlyBracket.InASingleBodiesResourceWithoutTitle."), // break bodies apart on body semicolon Select.whitespaceAfter(resourceBodySemicolon).withStyles(// styles.lineBreaks(2, 2, 2)).withRuleName("WsAfterResourceBodySemicolon"), Select.whitespaceBefore(resourceBodySemicolon).withStyles(// styles.noSpaceUnlessWrapped(), // styles.noLineBreak())// .withRuleName("WsBeforeResourceBodySemicolon"), // only one line break after the end semicolon Select.whitespaceAfter(Select.important(optionalResourceEndBodySemicolon))// .withStyles(// styles.noSpaceUnlessWrapped(), // styles.oneLineBreak())// .withRuleName("WsAfterImportantResourceEndBodySemicolon"), // Note: Closing Resource "}" has DEDENT BREAK "}" BREAK by default // RESOURCE BODY // No space before the resource body title colon Select.whitespaceBefore(resourceBodyTitleColon)// .withStyles(styles.noSpaceUnlessWrapped())// .withRuleName("WsBeforeResourceBodyTitleColon"), // A resource body has its attribute operations indented after the title ':' // If compact, stay on the same line Select.and(inCompactableResource, Select.whitespaceAfter(resourceBodyTitleColon)) // .withStyle(oneSpaceNoLine) // .withRuleName("WsAfterResourceBodyTitleColon-Compactable"), // Select.whitespaceAfter(resourceBodyTitleColon)// .withStyles( // styles.oneLineBreak())// .withRuleName("WsAfterResourceBodyTitleColon"), // // ATTRIBUTE OPERATIONS have each operation on a separate line // a => b, c => d // -------^ // If compact, stay on the same line Select.and(inCompactableResource, Select.whitespaceAfter(attributeOperationsComma)) // .withStyle(oneSpaceNoLine) // .withRuleName("WsAfterAttributeOperationsComma-Compactable"), // Select.whitespaceAfter(attributeOperationsComma)// .withStyles(styles.oneLineBreak())// .withRuleName("WsAfterAttributeOperationsComma"), // xxx [] // ---^ Select.whitespaceBefore(atExpressionLeftBracket)// .withStyles(styles.noSpaceUnlessWrapped(), // styles.noLineBreak())// .withRuleName("WsbeforeAtExpressionLeftBracket"), // @ name // -^ // @@ name // --^ Select.whitespaceAfter(Select.keyword("@"))// .withStyle(noSpaceUnlessWrappedNoLine)// .withRuleName("WsAfterKeyword@"), Select.whitespaceAfter( Select.grammar(grammarAccess.getVirtualNameOrReferenceAccess().getExportedATBooleanParserRuleCall_1_0()))// .withStyle(noSpaceUnlessWrappedNoLine)// .withRuleName("WsAfterExportedMarker@"), Select.whitespaceBefore(elseAndElsifKeywords)// .withStyle(oneSpaceNoLine)// .withRuleName("WsBeforeKeywordsElseAndElseif"), // Select.whitespaceAfter( Select.grammar(grammarAccess.getUnaryMinusExpressionAccess().getHyphenMinusKeyword_0()))// .withStyle(noSpaceUnlessWrappedNoLine)// .withRuleName("WsAfterUnaryMinus"), // Select.whitespaceAfter(Select.grammar(grammarAccess.getNotExpressionAccess().getExclamationMarkKeyword_0()))// .withStyle(noSpaceUnlessWrappedNoLine)// .withRuleName("WsAfterUnaryNotEclamationMark"), // // Relationships are typically resource {} -> resource {} Select.whitespaceBefore(relationshipEdgeOperator)// .withStyle(oneSpaceNoLine)// .withRuleName("WsBeforeRelationshipEdgeOperator"), // Select.whitespaceAfter(relationshipEdgeOperator)// .withStyle(oneSpaceOneOptionalLine)// .withRuleName("WsAfterRelationshipEdgeOperator"), // // --Selector Expression Select.whitespaceBefore( Select.grammar(grammarAccess.getSelectorExpressionAccess().getCommaKeyword_1_2_0_3()))// .withStyle(noSpaceUnlessWrappedNoLine)// .withRuleName("WsBeforeSelectorExpressionEndComma"), // Select.whitespaceAfter( Select.grammar(grammarAccess.getSelectorExpressionAccess().getCommaKeyword_1_2_0_2_0_0()))// .withStyle(noSpaceOneLine)// .withRuleName("WsAfterSelectorExpressionComma"), // Select.whitespaceAfter(// Select.grammar(grammarAccess.getSelectorExpressionAccess().getRightCurlyBracketKeyword_1_2_0_4())) // .withStyle(oneOptionalSpaceNoLine) // .withRuleName("WsAfterSelectorRightBrace"), // // --Interpolation interpolationStart.withStyle(styles.indent())// .withRuleName("InterpolationStart"), // Select.whitespaceAfter(interpolationStart)// .withStyles(// styles.noSpace(), // non wrappable styles.noLineBreak()) // may contain a complex expression .withRuleName("WsAfterInterpolationStart"), // Select.whitespaceBefore(interpolationEnd)// .withStyles(// styles.noSpace(), // non wrappable styles.noLineBreak(), // styles.dedent()) // may contain a complex expression .withRuleName("WsBeforeInterpolationEnd"), // Select.whitespaceAfter(interpolationEnd) // .withStyles(// styles.noLineBreak()) // .withRuleName("WsAfterInterpolationEnd"), // // --Statements // ws before subsequent statements in a block // non inline Select.whitespaceBefore(Select.and(// Select.not(Select.node(StatementStyle.INLINE)), // Select.node(StatementStyle.STATEMENT), // Select.node(StatementStyle.BLOCK), // Select.not(Select.node(StatementStyle.FIRST))))// .withStyle(noSpaceTwoLines)// .withRuleName("WsBeforeNonFirstBlockStatement"), Select.whitespaceBefore(Select.and(// Select.not(Select.node(StatementStyle.INLINE)), // Select.node(StatementStyle.STATEMENT), // Select.not(Select.node(StatementStyle.FIRST))))// .withStyle(noSpaceOneLineOptionallyTwo)// .withRuleName("WsBeforeNonFirstStatement"), // // inline // Although inlined, gets same treatment since it is a block Select.whitespaceBefore(Select.and(// Select.node(StatementStyle.INLINE), // Select.node(StatementStyle.STATEMENT), // Select.node(StatementStyle.BLOCK), // Select.not(Select.node(StatementStyle.FIRST))))// .withStyle(noSpaceTwoLines)// .withRuleName("WsBeforeNonFirstBlockStatement"), Select.whitespaceBefore(Select.and(// Select.node(StatementStyle.INLINE), Select.node(StatementStyle.STATEMENT), // Select.not(Select.node(StatementStyle.FIRST))))// .withStyles(styles.oneSpace(), styles.noLineBreak())// .withRuleName("WsBeforeNonFirstStatement"), // // styles.noSpace(), styles.lineBreaks(functions.oneLineBreakUnlessPredecessorIsLinebreakingComment())), // No space between function name and () Select.whitespaceBefore( Select.grammar(grammarAccess.getFunctionCallAccess().getLeftParenthesisKeyword_1_0_0_1()))// // Select.grammar(grammarAccess.getFunctionCallAccess().getLeftParenthesisKeyword_1_1()))// .withStyle(noSpaceUnlessWrappedNoLine) // .withRuleName("WsBeforeFunctionCallLeftParenthesis"), // PP parser includes trailing WS in ML comment, and SL comment always ends with line break // These two rules allows the WS before a comment to have a break (even if in an expression) // and one space after (unless the predecessor is whitespace terminated). Select.whitespaceBefore(Select.node(NodeType.COMMENT))// .withStyles(// styles.oneSpace(),// styles.lineBreaks(0, 0, 2, false, false))// .withRuleName("WsBeforeComment"), Select.whitespaceAfter(Select.node(NodeType.COMMENT))// .withStyles(// styles.spacing(functions.oneSpaceUnlessPredecessorIsWhitespaceTerminated()),// styles.lineBreaks(0, 0, 2, false, false))// .withRuleName("WsAfterComment"), // Method Call Select.whitespaceBefore(Select.grammar(grammarAccess.getMethodCallAccess().getFullStopKeyword_1_1())) // .withStyles(styles.noSpace(), styles.noLineBreak()).withRuleName("WsBeforeDot"), Select.whitespaceAfter(Select.grammar(grammarAccess.getMethodCallAccess().getFullStopKeyword_1_1())) // .withStyles(styles.noSpace(), styles.noLineBreak()).withRuleName("WsAfterDot"), // The x.y() should not have whitespace before and after the opening '(' - the parenthesis are // optional (the ')' is handled like all parentheses, but the opening is special Select.whitespaceBefore( Select.grammar(grammarAccess.getMethodCallAccess().getParenthesizedLPARBooleanParserRuleCall_1_2_1_0_0()))// .withStyles(styles.noSpace(), styles.noLineBreak()) // .withRuleName("WsBeforeMethodCall_LPAR"), // No space after '(' Select.whitespaceAfter( Select.grammar(grammarAccess.getMethodCallAccess().getParenthesizedLPARBooleanParserRuleCall_1_2_1_0_0()))// .withStyles(styles.noSpaceUnlessWrapped())// .withRuleName("NoSpaceAfterMethodCallLeftParenthesis"), // // Lambdas Select.whitespaceAfter(Select.grammar(grammarAccess.getJava8LambdaAccess().getVerticalLineKeyword_0())) // .withStyle(styles.noSpace()).withRuleName("WsAfterLeftJ8Pipe"), Select.whitespaceBefore(Select.grammar(grammarAccess.getJava8LambdaAccess().getVerticalLineKeyword_3())) // .withStyle(styles.noSpace()).withRuleName("WsBeforeRightJ8Pipe"), Select.whitespaceBefore(Select.grammar(grammarAccess.getRubyLambdaAccess().getVerticalLineKeyword_1())) // .withStyle(styles.noSpace()).withRuleName("WsBeforeLeftRPipe"), Select.whitespaceAfter(Select.grammar(grammarAccess.getRubyLambdaAccess().getVerticalLineKeyword_1())) // .withStyle(styles.noSpace()).withRuleName("WsAfterLeftRPipe"), Select.whitespaceBefore(Select.grammar(grammarAccess.getRubyLambdaAccess().getVerticalLineKeyword_4())) // .withStyle(styles.noSpace()).withRuleName("WsBeforeRightRPipe"), Select.whitespaceAfter( Select.grammar(grammarAccess.getRubyLambdaAccess().getVerticalLineKeyword_4()).and(inline)) // .withStyles(styles.oneSpace(), styles.indent(0), styles.noLineBreak()).withRuleName( "WsAfterInlineRightRPipe"), Select.whitespaceAfter(Select.grammar(grammarAccess.getRubyLambdaAccess().getVerticalLineKeyword_4())) // .withStyles(styles.indent(), styles.oneLineBreak()).withRuleName("WsAfterRightRPipe"), // Start indent on lambda '{' that is inline (in case there is a break) Select.whitespaceAfter(lambdaLeftBrace.and(inline)).// withStyles( // styles.indent(), // styles.oneSpace(), // styles.noLineBreak())// .withRuleName("WsAfterInlineLambdaLeftBrace"), // // Stop indent on '}' and emit one space before Select.whitespaceBefore(lambdaRightBrace.and(inline))// .withStyles( // styles.oneSpace(), // styles.dedent(), // styles.noLineBreak()) // .withRuleName("WsAfterInlineLambdaRightBrace"), // // No space and optinal one linebreak after a lambda '}' if inline Select.whitespaceAfter(lambdaRightBrace.and(inline))// .withStyles( // styles.noSpaceUnlessWrapped(), // styles.lineBreaks(0, 1, 2)) // .withRuleName("DefaultCSS.BreakLineAfterRightCurlyBrace"), // Separator Select.whitespaceBefore( Select.grammar(grammarAccess.getSeparatorExpressionAccess().getSemicolonKeyword_1())) // .withStyles(styles.noSpace(), styles.noLineBreak()).withRuleName("WsBeforeSeparator"), // optional linebreak if non inline Select.whitespaceAfter( // Select.and( notInline, Select.grammar(grammarAccess.getSeparatorExpressionAccess().getSemicolonKeyword_1()))) // .withStyles(styles.oneSpace(), styles.lineBreaks(0, 1, 1, true, true)) // .withRuleName("WsAfterSeparator"), // no linebreak if inlining Select.whitespaceAfter(Select.and( // Select.grammar(grammarAccess.getSeparatorExpressionAccess().getSemicolonKeyword_1()), // inline))// .withStyles(styles.oneSpace(), styles.noLineBreak()) // .withRuleName("WsAfterSeparator") // , // Select.whitespaceBefore(Select.grammar(grammarAccess.getLiteralListAccess().getEndCommaParserRuleCall_3())).withStyle(// // noSpaceNoLine) ); return css; } }