Skip to content

Commit b0f4060

Browse files
authored
Merge pull request #71 from anboralabs/fix-formatting
Fix formatting
2 parents 34170bf + f3f578d commit b0f4060

21 files changed

+451
-157
lines changed

build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
import org.jetbrains.grammarkit.tasks.*
2-
31
plugins {
42
id 'java'
53
id 'org.jetbrains.intellij' version '0.6.5'
64
id 'org.jetbrains.kotlin.jvm' version '1.4.10'
7-
id "org.jetbrains.grammarkit" version "2021.1.2"
5+
id "org.jetbrains.grammarkit" version "2021.1.3"
86
}
97

108
apply plugin: 'org.jetbrains.grammarkit'
119

10+
import org.jetbrains.grammarkit.tasks.*
11+
1212
group 'co.anbora.labs'
13-
version '2.5.8'
13+
version '2.6.0'
1414

1515
repositories {
1616
mavenCentral()

src/main/grammar/FirebaseRules.bnf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
elementTypeClass="co.anbora.labs.firebase.lang.core.psi.FirebaseElementType"
1414
tokenTypeClass="co.anbora.labs.firebase.lang.core.psi.FirebaseTokenType"
1515

16-
extends(".+Expression") = Expression
1716
extends(".+Expr") = Expression
1817

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

206205
IdentifierExpr ::= IDENTIFIER
207206
DotExpr ::= Expression DOT Expression
208-
CallExpr ::= Expression '(' ParameterStatement? ')'
207+
CallExpr ::= Expression CallArguments
208+
CallArguments ::= '(' ParameterStatement? ')'
209+
//CallArguments ::= '(' (Expression &(','|')'))? ')' { pin = 1 }
209210

210211
LiteralExpr ::= LiteralStatement
211212
| BooleanStatement

src/main/html/change-notes.html

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
<br>
77
Plugin updates:
88
<ul>
9+
<li><b>2.6.0</b> <em>(2021-06-07)</em> - Code Style</li>
10+
<ul>
11+
<li>Added code style configuration. </li>
12+
<li>Fixed issue with code formatting. </li>
13+
</ul>
914
<li><b>2.5.9</b> <em>(2021-05-27)</em> - Fixed minor issues - Android version</li>
1015
<ul>
1116
<li>Added ternary operator </li>

src/main/kotlin/co/anbora/labs/firebase/ide/color/FirebaseColorSettingPage.kt

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package co.anbora.labs.firebase.ide.color
22

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

2425
override fun getHighlighter(): SyntaxHighlighter = FirebaseSyntaxHighlighter()
2526

26-
override fun getDemoText(): String =
27-
"""rules_version = '2';
28-
service cloud.firestore {
29-
// Allow the requestor to read or delete any resource on a path under the
30-
// user directory.
31-
match /users/{userId}/{anyUserFile=**} {
32-
allow read, delete: if request.auth != null && request.auth.uid == userId;
33-
}
34-
35-
match /databases/{database}/documents {
36-
// True if the user is signed in or the requested data is 'public'
37-
function signedInOrPublic() {
38-
return request.auth.uid != null || resource.data.visibility == 'public';
39-
}
40-
match /{role}/{document=**} {
41-
allow read, write: if
42-
request.time < timestamp.date(2020, 9, 23) && role in request.auth.token.authorities;
43-
}
44-
}
45-
// Allow the requestor to create or update their own images.
46-
// When 'request.method' == 'delete' this rule and the one matching
47-
// any path under the user directory would both match and the `delete`
48-
// would be permitted.
49-
50-
match /users/{userId}/images/{imageId} {
51-
// Whether to permit the request depends on the logical OR of all
52-
// matched rules. This means that even if this rule did not explicitly
53-
// allow the 'delete' the earlier rule would have.
54-
allow write: if request.auth != null && request.auth.uid == userId && imageId.matches('*.png');
55-
}
56-
}"""
27+
override fun getDemoText(): String = LANGUAGE_DEMO_TEXT
5728

5829
override fun getAdditionalHighlightingTagToDescriptorMap(): MutableMap<String, TextAttributesKey> = mutableMapOf()
5930
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Use of this source code is governed by the MIT license that can be
3+
* found in the LICENSE file.
4+
*/
5+
6+
package co.anbora.labs.firebase.ide.formatter
7+
8+
import com.intellij.formatting.Alignment
9+
import com.intellij.lang.ASTNode
10+
import com.intellij.psi.tree.IElementType
11+
import com.intellij.psi.tree.TokenSet
12+
13+
interface FirebaseAlignmentStrategy {
14+
/**
15+
* Requests current strategy for alignment to use for given child.
16+
*/
17+
fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment?
18+
19+
/**
20+
* Always returns `null`.
21+
*/
22+
object NullStrategy : FirebaseAlignmentStrategy {
23+
override fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment? = null
24+
}
25+
26+
/**
27+
* Apply this strategy only when child element is in [tt].
28+
*/
29+
fun alignIf(vararg tt: IElementType): FirebaseAlignmentStrategy = alignIf(TokenSet.create(*tt))
30+
31+
/**
32+
* Apply this strategy only when child element type matches [filterSet].
33+
*/
34+
fun alignIf(filterSet: TokenSet): FirebaseAlignmentStrategy =
35+
object : FirebaseAlignmentStrategy {
36+
override fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment? =
37+
if (child.elementType in filterSet) {
38+
this@FirebaseAlignmentStrategy.getAlignment(child, parent, childCtx)
39+
} else {
40+
null
41+
}
42+
}
43+
44+
/**
45+
* Apply this strategy only when [predicate] passes.
46+
*/
47+
fun alignIf(predicate: (child: ASTNode, parent: ASTNode?, ctx: FirebaseFmtContext) -> Boolean): FirebaseAlignmentStrategy =
48+
object : FirebaseAlignmentStrategy {
49+
override fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment? =
50+
if (predicate(child, parent, childCtx)) {
51+
this@FirebaseAlignmentStrategy.getAlignment(child, parent, childCtx)
52+
} else {
53+
null
54+
}
55+
}
56+
57+
/**
58+
* Returns [NullStrategy] if [condition] is `false`. Useful for making strategies configurable.
59+
*/
60+
fun alignIf(condition: Boolean): FirebaseAlignmentStrategy =
61+
if (condition) {
62+
this
63+
} else {
64+
NullStrategy
65+
}
66+
67+
companion object {
68+
/**
69+
* Always returns [alignment].
70+
*/
71+
fun wrap(alignment: Alignment = Alignment.createAlignment()): FirebaseAlignmentStrategy =
72+
object : FirebaseAlignmentStrategy {
73+
override fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment? =
74+
alignment
75+
}
76+
77+
/**
78+
* Always returns [FirebaseFmtContext.sharedAlignment]
79+
*/
80+
fun shared(): FirebaseAlignmentStrategy =
81+
object : FirebaseAlignmentStrategy {
82+
override fun getAlignment(child: ASTNode, parent: ASTNode?, childCtx: FirebaseFmtContext): Alignment? =
83+
childCtx.sharedAlignment
84+
}
85+
}
86+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package co.anbora.labs.firebase.ide.formatter
2+
3+
import co.anbora.labs.firebase.ide.formatter.impl.createSpacingBuilder
4+
import com.intellij.formatting.Alignment
5+
import com.intellij.formatting.SpacingBuilder
6+
import com.intellij.psi.codeStyle.CodeStyleSettings
7+
8+
data class FirebaseFmtContext private constructor(
9+
val commonSettings: CodeStyleSettings,
10+
val spacingBuilder: SpacingBuilder,
11+
val sharedAlignment: Alignment? = null
12+
) {
13+
companion object {
14+
fun create(settings: CodeStyleSettings): FirebaseFmtContext {
15+
return FirebaseFmtContext(settings, createSpacingBuilder(settings))
16+
}
17+
}
18+
19+
}
Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package co.anbora.labs.firebase.ide.formatter
22

3+
import co.anbora.labs.firebase.ide.formatter.impl.*
4+
import co.anbora.labs.firebase.lang.core.psi.FirebaseRulesTypes.*
35
import com.intellij.formatting.*
46
import com.intellij.lang.ASTNode
57
import com.intellij.psi.formatter.common.AbstractBlock
@@ -8,32 +10,49 @@ class FirebaseFormatterBlock(
810
node: ASTNode,
911
wrap: Wrap? = null,
1012
alignment: Alignment? = null,
11-
val spacingBuilder: SpacingBuilder
13+
private val indent: Indent?,
14+
val ctx: FirebaseFmtContext
1215
): AbstractBlock(node, wrap, alignment) {
1316

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

16-
override fun getIndent(): Indent?
17-
= computeIndent()
18-
19-
override fun getChildAttributes(newChildIndex: Int): ChildAttributes
20-
= computeChildAttributes()
21-
22-
override fun getSpacing(child1: Block?, child2: Block): Spacing?
23-
= computeSpacing(child1, child2)
20+
override fun getSpacing(child1: Block?, child2: Block): Spacing? = computeSpacing(child1, child2, ctx)
2421

2522
override fun getSubBlocks(): List<Block> = mySubBlocks
26-
2723
private val mySubBlocks: List<Block> by lazy { buildChildren() }
2824

2925
override fun buildChildren(): List<Block> {
26+
val sharedAlignment = when (node.elementType) {
27+
FUNCTION_PARAMETER_LIST -> Alignment.createAlignment()
28+
FUNCTION_PARAMETER -> ctx.sharedAlignment
29+
else -> null
30+
}
31+
val alignment = getAlignmentStrategy()
32+
3033
return node.getChildren(null)
3134
.filter { !it.isWhitespaceOrEmpty() }
3235
.map { childNode: ASTNode ->
36+
val childCtx = ctx.copy(sharedAlignment = sharedAlignment)
37+
val indent = computeIndent(childNode)
3338
FirebaseFormatterBlock(
3439
node = childNode,
35-
spacingBuilder = spacingBuilder
40+
alignment = alignment.getAlignment(childNode, node, childCtx),
41+
indent = indent,
42+
wrap = null,
43+
ctx = childCtx
3644
)
3745
}
3846
}
47+
48+
override fun getChildAttributes(newChildIndex: Int): ChildAttributes {
49+
val indent = when {
50+
node.elementType == CONDITIONAL_BLOCK -> Indent.getNoneIndent()
51+
node.isDelimitedBlock -> Indent.getNormalIndent()
52+
// Otherwise we don't want any indentation (null means continuation indent)
53+
else -> Indent.getNoneIndent()
54+
}
55+
return ChildAttributes(indent, null)
56+
}
57+
3958
}

src/main/kotlin/co/anbora/labs/firebase/ide/formatter/FirebaseFormattingModelBuilder.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ import com.intellij.formatting.*
55
class FirebaseFormattingModelBuilder: FormattingModelBuilder {
66

77
override fun createModel(formattingContext: FormattingContext): FormattingModel {
8+
val ctx = FirebaseFmtContext.create(formattingContext.codeStyleSettings)
89
return FormattingModelProvider.createFormattingModelForPsiFile(
910
formattingContext.containingFile,
10-
FirebaseFormatterBlock(
11-
node = formattingContext.node,
12-
spacingBuilder = createSpacingBuilder(formattingContext.codeStyleSettings)
13-
),
11+
FirebaseFormatterBlock(formattingContext.node, null, null, Indent.getNoneIndent(), ctx),
1412
formattingContext.codeStyleSettings
1513
)
1614
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package co.anbora.labs.firebase.ide.formatter.impl
2+
3+
import co.anbora.labs.firebase.ide.formatter.FirebaseAlignmentStrategy
4+
import co.anbora.labs.firebase.ide.formatter.FirebaseFormatterBlock
5+
import co.anbora.labs.firebase.lang.core.psi.FirebaseRulesTypes.CALL_ARGUMENTS
6+
import co.anbora.labs.firebase.lang.core.psi.FirebaseRulesTypes.FUNCTION_PARAMETER_LIST
7+
8+
fun FirebaseFormatterBlock.getAlignmentStrategy(): FirebaseAlignmentStrategy = when (node.elementType) {
9+
FUNCTION_PARAMETER_LIST, CALL_ARGUMENTS ->
10+
FirebaseAlignmentStrategy
11+
.shared()
12+
.alignUnlessBlockDelim()
13+
.alignIf(ctx.commonSettings.ALIGN_MULTILINE_PARAMETERS)
14+
else -> FirebaseAlignmentStrategy.NullStrategy
15+
16+
}
17+
18+
fun FirebaseAlignmentStrategy.alignUnlessBlockDelim(): FirebaseAlignmentStrategy =
19+
alignIf { c, p, _ -> !c.isDelimiterOfCurrentBlock(p) }
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package co.anbora.labs.firebase.ide.formatter.impl
2+
3+
import co.anbora.labs.firebase.ide.formatter.FirebaseFormatterBlock
4+
import co.anbora.labs.firebase.lang.core.psi.*
5+
import co.anbora.labs.firebase.lang.core.psi.FirebaseRulesTypes.CONDITIONAL_BLOCK
6+
import com.intellij.formatting.Indent
7+
import com.intellij.lang.ASTNode
8+
9+
fun FirebaseFormatterBlock.computeIndent(child: ASTNode): Indent? {
10+
// val parentType = node.elementType
11+
val parentPsi = node.psi
12+
// val childType = child.elementType
13+
val childPsi = child.psi
14+
return when {
15+
node.isDelimitedBlock -> getNormalIndentIfNotCurrentBlockDelimiter(child, node)
16+
// do not indent statements
17+
childPsi.prevSibling == null -> Indent.getNoneIndent()
18+
// let a =
19+
// 92;
20+
// =>
21+
// let a =
22+
// 92;
23+
// except if RefExpr as lhs of assignment expr
24+
// childPsi is MoveExpr
25+
// && (parentType == LET_EXPR || parentType == ASSIGNMENT_EXPR || parentType == CONST_DEF) -> Indent.getNormalIndent()
26+
childPsi is FirebaseRulesExpression
27+
&& parentPsi is FirebaseRulesVariableStatement -> Indent.getNormalIndent()
28+
29+
childPsi is FirebaseRulesConditionalBlock
30+
&& parentPsi is FirebaseRulesConditionalStatement -> Indent.getNormalIndent()
31+
32+
childPsi is FirebaseRulesConditionalStatement -> Indent.getNormalIndent()
33+
34+
childPsi is FirebaseRulesConditionalBlock -> Indent.getNormalIndent()
35+
// if (true)
36+
// create()
37+
// else
38+
// delete()
39+
parentPsi is FirebaseRulesServiceBlock
40+
|| parentPsi is FirebaseRulesMatchBlock
41+
|| parentPsi is FirebaseRulesFunctionBlock
42+
|| parentPsi is FirebaseRulesReturnBlock -> Indent.getNormalIndent()
43+
44+
// binary expressions, chain calls
45+
// no indent on it's own, use parent indent
46+
parentPsi is FirebaseRulesExpression -> Indent.getIndent(Indent.Type.NONE, true, true)
47+
48+
else -> Indent.getNoneIndent()
49+
}
50+
}
51+
52+
fun getNormalIndentIfNotCurrentBlockDelimiter(child: ASTNode, parent: ASTNode): Indent =
53+
if (child.isDelimiterOfCurrentBlock(parent)) {
54+
Indent.getNoneIndent()
55+
} else {
56+
if (parent.elementType == CONDITIONAL_BLOCK) {
57+
Indent.getNoneIndent()
58+
} else {
59+
Indent.getNormalIndent()
60+
}
61+
}

0 commit comments

Comments
 (0)