Skip to content

Commit

Permalink
Merge pull request #71 from anboralabs/fix-formatting
Browse files Browse the repository at this point in the history
Fix formatting
  • Loading branch information
dalgarins authored Jun 8, 2021
2 parents 34170bf + f3f578d commit b0f4060
Show file tree
Hide file tree
Showing 21 changed files with 451 additions and 157 deletions.
8 changes: 4 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import org.jetbrains.grammarkit.tasks.*

plugins {
id 'java'
id 'org.jetbrains.intellij' version '0.6.5'
id 'org.jetbrains.kotlin.jvm' version '1.4.10'
id "org.jetbrains.grammarkit" version "2021.1.2"
id "org.jetbrains.grammarkit" version "2021.1.3"
}

apply plugin: 'org.jetbrains.grammarkit'

import org.jetbrains.grammarkit.tasks.*

group 'co.anbora.labs'
version '2.5.8'
version '2.6.0'

repositories {
mavenCentral()
Expand Down
5 changes: 3 additions & 2 deletions src/main/grammar/FirebaseRules.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
elementTypeClass="co.anbora.labs.firebase.lang.core.psi.FirebaseElementType"
tokenTypeClass="co.anbora.labs.firebase.lang.core.psi.FirebaseTokenType"

extends(".+Expression") = Expression
extends(".+Expr") = Expression

name(".*Expr")="expr"
Expand Down Expand Up @@ -205,7 +204,9 @@ TernaryExpr ::= Expression '?' Expression ':' Expression

IdentifierExpr ::= IDENTIFIER
DotExpr ::= Expression DOT Expression
CallExpr ::= Expression '(' ParameterStatement? ')'
CallExpr ::= Expression CallArguments
CallArguments ::= '(' ParameterStatement? ')'
//CallArguments ::= '(' (Expression &(','|')'))? ')' { pin = 1 }

LiteralExpr ::= LiteralStatement
| BooleanStatement
Expand Down
5 changes: 5 additions & 0 deletions src/main/html/change-notes.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
<br>
Plugin updates:
<ul>
<li><b>2.6.0</b> <em>(2021-06-07)</em> - Code Style</li>
<ul>
<li>Added code style configuration. </li>
<li>Fixed issue with code formatting. </li>
</ul>
<li><b>2.5.9</b> <em>(2021-05-27)</em> - Fixed minor issues - Android version</li>
<ul>
<li>Added ternary operator </li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package co.anbora.labs.firebase.ide.color

import co.anbora.labs.firebase.ide.highlight.FirebaseSyntaxHighlighter
import co.anbora.labs.firebase.ide.icons.FirebaseIcons
import co.anbora.labs.firebase.lang.FirebaseRulesLanguage.LANGUAGE_DEMO_TEXT
import com.intellij.openapi.editor.colors.TextAttributesKey
import com.intellij.openapi.fileTypes.SyntaxHighlighter
import com.intellij.openapi.options.colors.AttributesDescriptor
Expand All @@ -23,37 +24,7 @@ class FirebaseColorSettingPage: ColorSettingsPage {

override fun getHighlighter(): SyntaxHighlighter = FirebaseSyntaxHighlighter()

override fun getDemoText(): String =
"""rules_version = '2';
service cloud.firestore {
// Allow the requestor to read or delete any resource on a path under the
// user directory.
match /users/{userId}/{anyUserFile=**} {
allow read, delete: if request.auth != null && request.auth.uid == userId;
}
match /databases/{database}/documents {
// True if the user is signed in or the requested data is 'public'
function signedInOrPublic() {
return request.auth.uid != null || resource.data.visibility == 'public';
}
match /{role}/{document=**} {
allow read, write: if
request.time < timestamp.date(2020, 9, 23) && role in request.auth.token.authorities;
}
}
// Allow the requestor to create or update their own images.
// When 'request.method' == 'delete' this rule and the one matching
// any path under the user directory would both match and the `delete`
// would be permitted.
match /users/{userId}/images/{imageId} {
// Whether to permit the request depends on the logical OR of all
// matched rules. This means that even if this rule did not explicitly
// allow the 'delete' the earlier rule would have.
allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
}
}"""
override fun getDemoText(): String = LANGUAGE_DEMO_TEXT

override fun getAdditionalHighlightingTagToDescriptorMap(): MutableMap<String, TextAttributesKey> = mutableMapOf()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Use of this source code is governed by the MIT license that can be
* found in the LICENSE file.
*/

package co.anbora.labs.firebase.ide.formatter

import com.intellij.formatting.Alignment
import com.intellij.lang.ASTNode
import com.intellij.psi.tree.IElementType
import com.intellij.psi.tree.TokenSet

interface FirebaseAlignmentStrategy {
/**
* Requests current strategy for alignment to use for given child.
*/
fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment?

/**
* Always returns `null`.
*/
object NullStrategy : FirebaseAlignmentStrategy {
override fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment? = null
}

/**
* Apply this strategy only when child element is in [tt].
*/
fun alignIf(vararg tt: IElementType): FirebaseAlignmentStrategy = alignIf(TokenSet.create(*tt))

/**
* Apply this strategy only when child element type matches [filterSet].
*/
fun alignIf(filterSet: TokenSet): FirebaseAlignmentStrategy =
object : FirebaseAlignmentStrategy {
override fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment? =
if (child.elementType in filterSet) {
this@FirebaseAlignmentStrategy.getAlignment(child, parent, childCtx)
} else {
null
}
}

/**
* Apply this strategy only when [predicate] passes.
*/
fun alignIf(predicate: (child: ASTNode, parent: ASTNode?, ctx: FirebaseFmtContext) -> Boolean): FirebaseAlignmentStrategy =
object : FirebaseAlignmentStrategy {
override fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment? =
if (predicate(child, parent, childCtx)) {
this@FirebaseAlignmentStrategy.getAlignment(child, parent, childCtx)
} else {
null
}
}

/**
* Returns [NullStrategy] if [condition] is `false`. Useful for making strategies configurable.
*/
fun alignIf(condition: Boolean): FirebaseAlignmentStrategy =
if (condition) {
this
} else {
NullStrategy
}

companion object {
/**
* Always returns [alignment].
*/
fun wrap(alignment: Alignment = Alignment.createAlignment()): FirebaseAlignmentStrategy =
object : FirebaseAlignmentStrategy {
override fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment? =
alignment
}

/**
* Always returns [FirebaseFmtContext.sharedAlignment]
*/
fun shared(): FirebaseAlignmentStrategy =
object : FirebaseAlignmentStrategy {
override fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment? =
childCtx.sharedAlignment
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package co.anbora.labs.firebase.ide.formatter

import co.anbora.labs.firebase.ide.formatter.impl.createSpacingBuilder
import com.intellij.formatting.Alignment
import com.intellij.formatting.SpacingBuilder
import com.intellij.psi.codeStyle.CodeStyleSettings

data class FirebaseFmtContext private constructor(
val commonSettings: CodeStyleSettings,
val spacingBuilder: SpacingBuilder,
val sharedAlignment: Alignment? = null
) {
companion object {
fun create(settings: CodeStyleSettings): FirebaseFmtContext {
return FirebaseFmtContext(settings, createSpacingBuilder(settings))
}
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package co.anbora.labs.firebase.ide.formatter

import co.anbora.labs.firebase.ide.formatter.impl.*
import co.anbora.labs.firebase.lang.core.psi.FirebaseRulesTypes.*
import com.intellij.formatting.*
import com.intellij.lang.ASTNode
import com.intellij.psi.formatter.common.AbstractBlock
Expand All @@ -8,32 +10,49 @@ class FirebaseFormatterBlock(
node: ASTNode,
wrap: Wrap? = null,
alignment: Alignment? = null,
val spacingBuilder: SpacingBuilder
private val indent: Indent?,
val ctx: FirebaseFmtContext
): AbstractBlock(node, wrap, alignment) {

override fun isLeaf(): Boolean = node.firstChildNode == null
override fun getIndent(): Indent? = indent

override fun getIndent(): Indent?
= computeIndent()

override fun getChildAttributes(newChildIndex: Int): ChildAttributes
= computeChildAttributes()

override fun getSpacing(child1: Block?, child2: Block): Spacing?
= computeSpacing(child1, child2)
override fun getSpacing(child1: Block?, child2: Block): Spacing? = computeSpacing(child1, child2, ctx)

override fun getSubBlocks(): List<Block> = mySubBlocks

private val mySubBlocks: List<Block> by lazy { buildChildren() }

override fun buildChildren(): List<Block> {
val sharedAlignment = when (node.elementType) {
FUNCTION_PARAMETER_LIST -> Alignment.createAlignment()
FUNCTION_PARAMETER -> ctx.sharedAlignment
else -> null
}
val alignment = getAlignmentStrategy()

return node.getChildren(null)
.filter { !it.isWhitespaceOrEmpty() }
.map { childNode: ASTNode ->
val childCtx = ctx.copy(sharedAlignment = sharedAlignment)
val indent = computeIndent(childNode)
FirebaseFormatterBlock(
node = childNode,
spacingBuilder = spacingBuilder
alignment = alignment.getAlignment(childNode, node, childCtx),
indent = indent,
wrap = null,
ctx = childCtx
)
}
}

override fun getChildAttributes(newChildIndex: Int): ChildAttributes {
val indent = when {
node.elementType == CONDITIONAL_BLOCK -> Indent.getNoneIndent()
node.isDelimitedBlock -> Indent.getNormalIndent()
// Otherwise we don't want any indentation (null means continuation indent)
else -> Indent.getNoneIndent()
}
return ChildAttributes(indent, null)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,10 @@ import com.intellij.formatting.*
class FirebaseFormattingModelBuilder: FormattingModelBuilder {

override fun createModel(formattingContext: FormattingContext): FormattingModel {
val ctx = FirebaseFmtContext.create(formattingContext.codeStyleSettings)
return FormattingModelProvider.createFormattingModelForPsiFile(
formattingContext.containingFile,
FirebaseFormatterBlock(
node = formattingContext.node,
spacingBuilder = createSpacingBuilder(formattingContext.codeStyleSettings)
),
FirebaseFormatterBlock(formattingContext.node, null, null, Indent.getNoneIndent(), ctx),
formattingContext.codeStyleSettings
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package co.anbora.labs.firebase.ide.formatter.impl

import co.anbora.labs.firebase.ide.formatter.FirebaseAlignmentStrategy
import co.anbora.labs.firebase.ide.formatter.FirebaseFormatterBlock
import co.anbora.labs.firebase.lang.core.psi.FirebaseRulesTypes.CALL_ARGUMENTS
import co.anbora.labs.firebase.lang.core.psi.FirebaseRulesTypes.FUNCTION_PARAMETER_LIST

fun FirebaseFormatterBlock.getAlignmentStrategy(): FirebaseAlignmentStrategy = when (node.elementType) {
FUNCTION_PARAMETER_LIST, CALL_ARGUMENTS ->
FirebaseAlignmentStrategy
.shared()
.alignUnlessBlockDelim()
.alignIf(ctx.commonSettings.ALIGN_MULTILINE_PARAMETERS)
else -> FirebaseAlignmentStrategy.NullStrategy

}

fun FirebaseAlignmentStrategy.alignUnlessBlockDelim(): FirebaseAlignmentStrategy =
alignIf { c, p, _ -> !c.isDelimiterOfCurrentBlock(p) }
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package co.anbora.labs.firebase.ide.formatter.impl

import co.anbora.labs.firebase.ide.formatter.FirebaseFormatterBlock
import co.anbora.labs.firebase.lang.core.psi.*
import co.anbora.labs.firebase.lang.core.psi.FirebaseRulesTypes.CONDITIONAL_BLOCK
import com.intellij.formatting.Indent
import com.intellij.lang.ASTNode

fun FirebaseFormatterBlock.computeIndent(child: ASTNode): Indent? {
// val parentType = node.elementType
val parentPsi = node.psi
// val childType = child.elementType
val childPsi = child.psi
return when {
node.isDelimitedBlock -> getNormalIndentIfNotCurrentBlockDelimiter(child, node)
// do not indent statements
childPsi.prevSibling == null -> Indent.getNoneIndent()
// let a =
// 92;
// =>
// let a =
// 92;
// except if RefExpr as lhs of assignment expr
// childPsi is MoveExpr
// && (parentType == LET_EXPR || parentType == ASSIGNMENT_EXPR || parentType == CONST_DEF) -> Indent.getNormalIndent()
childPsi is FirebaseRulesExpression
&& parentPsi is FirebaseRulesVariableStatement -> Indent.getNormalIndent()

childPsi is FirebaseRulesConditionalBlock
&& parentPsi is FirebaseRulesConditionalStatement -> Indent.getNormalIndent()

childPsi is FirebaseRulesConditionalStatement -> Indent.getNormalIndent()

childPsi is FirebaseRulesConditionalBlock -> Indent.getNormalIndent()
// if (true)
// create()
// else
// delete()
parentPsi is FirebaseRulesServiceBlock
|| parentPsi is FirebaseRulesMatchBlock
|| parentPsi is FirebaseRulesFunctionBlock
|| parentPsi is FirebaseRulesReturnBlock -> Indent.getNormalIndent()

// binary expressions, chain calls
// no indent on it's own, use parent indent
parentPsi is FirebaseRulesExpression -> Indent.getIndent(Indent.Type.NONE, true, true)

else -> Indent.getNoneIndent()
}
}

fun getNormalIndentIfNotCurrentBlockDelimiter(child: ASTNode, parent: ASTNode): Indent =
if (child.isDelimiterOfCurrentBlock(parent)) {
Indent.getNoneIndent()
} else {
if (parent.elementType == CONDITIONAL_BLOCK) {
Indent.getNoneIndent()
} else {
Indent.getNormalIndent()
}
}
Loading

0 comments on commit b0f4060

Please sign in to comment.