Skip to content

Commit

Permalink
feat: support line continuations in values and labels
Browse files Browse the repository at this point in the history
  • Loading branch information
goto1134 committed Jan 29, 2024
1 parent 4605c7c commit 1e82ce0
Show file tree
Hide file tree
Showing 9 changed files with 1,459 additions and 847 deletions.
1,777 changes: 976 additions & 801 deletions src/gen/org/jetbrains/plugins/d2/lang/D2FlexLexer.java

Large diffs are not rendered by default.

43 changes: 25 additions & 18 deletions src/src/lang/D2Lexer.flex
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,30 @@ DOUBLE_ARROW=<{ContinuationClosure}*-(-|{ContinuationClosure})*>

Comment=#.*
BlockComment = "\"\"\""~"\"\"\""
Int=[0-9]+
Float=[0-9]+\.[0-9]+
Int=[0-9]([0-9]|{ContinuationClosure})*
Float=[0-9]([0-9]|{ContinuationClosure})*\.{ContinuationClosure}?[0-9]([0-9]|{ContinuationClosure})*
Semicolon=";"

// docs: D2 actually allows you to use any special symbols (not alphanumeric, space, or _) after the first pipe
BlockStringStart=\|+[^a-zA-Z0-9\s_|]*
String='([^'\\]|\\.)*'|\"([^\"\\]|\\\"|\'|\\)*\"
Color=\"#(([a-fA-F0-9]{2}){3}|([a-fA-F0-9]){3})\"

SingleQuotedString = '[^'\n]*'?
DoubleQuotedString = \"([^\"\n\\]|{Continuation}|{EscapeSequence})*\"?
String={SingleQuotedString}|{DoubleQuotedString}

SingleQuotedColor='#(([a-fA-F0-9]{2}){3}|([a-fA-F0-9]){3})'?
DoubleQuotedColor=\"{ContinuationClosure}?#{ContinuationClosure}?(([a-fA-F0-9]{ContinuationClosure}?[a-fA-F0-9]{ContinuationClosure}?){3}|([a-fA-F0-9]{ContinuationClosure}?){3}){ContinuationClosure}?\"?
Color={SingleQuotedColor}|{DoubleQuotedColor}

True=t{ContinuationClosure}?r{ContinuationClosure}?u{ContinuationClosure}?e
False=f{ContinuationClosure}?a{ContinuationClosure}?l{ContinuationClosure}?s{ContinuationClosure}?e

EscapeSequence=\\.
ValueSymbol=[^\s;{}\[\]#]
LabelSymbol=[^\s;{}#]
UnquotedLabelString = [{LabelSymbol}&&[^|]]+({SpaceContinuation}+{LabelSymbol}+)*
// [] is not supported - it is array
ValueSymbol=[{LabelSymbol}&&[^\[\]]]
UnquotedString = [{ValueSymbol}&&[^|]]+({SpaceContinuation}+{ValueSymbol}+)*

IdSymbol=[[^:.<>&\\-]&&{ValueSymbol}]
IdDashSubstring=-{SpaceContinuation}*([{IdSymbol}&&[^->*]]|{EscapeSequence})
Expand All @@ -83,6 +96,7 @@ ExclamationId=(\!({SpaceContinuation}*([[^&]&&{IdSymbol}]|{EscapeSequence}|{IdDa
AllowedFirstInId=([{IdSymbol}&&[^|!('\"$@]]|{EscapeSequence}|{IdDashSubstring})
RegularId={AllowedFirstInId}{IdBody}
Id=({RegularId}|{ExclamationId})
WhiteSpaceWithoutNewLines={SpaceContinuation}+
WhiteSpace={SpaceContinuationNewLine}+

LBrace="{"
Expand All @@ -91,13 +105,6 @@ RBrace="}"
LBracket="["
RBracket="]"

UnquotedLabelStringFragment=[^\s{}|;]+
UnquotedLabelString={UnquotedLabelStringFragment}([ \t]+{UnquotedLabelStringFragment})*

// [] is not supported - it is array
UnquotedStringFragment=[^\s{}[]|;]+
UnquotedString={UnquotedStringFragment}([ \t]+{UnquotedStringFragment})*

%states LABEL_STATE PROPERTY_VALUE_BEGIN_STATE PROPERTY_VALUE_STATE BLOCK_STRING_LANG_STATE BLOCK_STRING_BODY_STATE ARRAY_STATE

%%
Expand Down Expand Up @@ -143,16 +150,16 @@ UnquotedString={UnquotedStringFragment}([ \t]+{UnquotedStringFragment})*

{Int} { return INT; }
{Float} { return FLOAT; }
"true" { return TRUE; }
"false" { return FALSE; }
{True} { return TRUE; }
{False} { return FALSE; }
{Color} { return COLOR; }
{String} { return STRING; }
{LBracket} { yybegin(ARRAY_STATE); return LBRACKET; }
{RBracket} { return RBRACKET; }
{UnquotedString} { return UNQUOTED_STRING; }

[ \t]+ { return WHITE_SPACE; }
[\r\n]+ { yybegin(YYINITIAL); return WHITE_SPACE; }
{WhiteSpaceWithoutNewLines} { return WHITE_SPACE; }
{WhiteSpace} { yybegin(YYINITIAL); return WHITE_SPACE; }
{Semicolon} { yybegin(YYINITIAL); return SEMICOLON; }

// inline shape definition: {shape: person}
Expand All @@ -174,8 +181,8 @@ UnquotedString={UnquotedStringFragment}([ \t]+{UnquotedStringFragment})*

{String} { return STRING; }
{UnquotedLabelString} { return UNQUOTED_STRING; }
[ \t]+ { return WHITE_SPACE; }
[\r\n]+ { yybegin(YYINITIAL); return WHITE_SPACE; }
{WhiteSpaceWithoutNewLines} { return WHITE_SPACE; }
{WhiteSpace} { yybegin(YYINITIAL); return WHITE_SPACE; }
{LBrace} { yybegin(YYINITIAL); return LBRACE; }
// inline shape definition: {shape: person}
{RBrace} { yybegin(YYINITIAL); return RBRACE; }
Expand Down
24 changes: 18 additions & 6 deletions src/src/lang/psi/ShapeProperty.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,34 @@ sealed interface PropertyValue : PsiElement
class OtherValue(node: ASTNode) : AstWrapperPsiElement(node), PropertyValue

class StringValue(node: ASTNode) : AstWrapperPsiElement(node), PropertyValue, ColorValueProvider {
override fun getColor(): Color? = NAMED_COLORS.get(text.removeSurrounding("\""))
override fun getColor(): Color? {
val unquotedText = when {
text.startsWith('"') -> text.removeSurrounding("\"")
else -> text.removeSurrounding("'")
}
return NAMED_COLORS.get(unquotedText.removeLineContinuations())

Check notice on line 29 in src/src/lang/psi/ShapeProperty.kt

View workflow job for this annotation

GitHub Actions / Inspect code

Explicit 'get' or 'set' call

Should be replaced with indexing
}
}

class UnquotedStringValue(node: ASTNode) : AstWrapperPsiElement(node), PropertyValue, ColorValueProvider {
override fun getColor(): Color? = NAMED_COLORS.get(text)
override fun getColor(): Color? = NAMED_COLORS.get(text.removeLineContinuations())
}

class ColorValue(node: ASTNode) : AstWrapperPsiElement(node), PropertyValue, ColorValueProvider {
override fun getColor(): Color? = ColorHexUtil.fromHexOrNull(text.removeSurrounding("\""))
override fun getColor(): Color? = ColorHexUtil.fromHexOrNull(text.removeSurrounding("\"").removeLineContinuations())
}

// toString doesn't print node element type
sealed class AstWrapperPsiElement(private val node: ASTNode) : ASTDelegatePsiElement() {
override fun getParent(): PsiElement? = SharedImplUtil.getParent(node)
override fun getParent(): PsiElement? = SharedImplUtil.getParent(node)

override fun getNode(): ASTNode = node
override fun getNode(): ASTNode = node

override fun toString(): String = javaClass.simpleName
override fun toString(): String = javaClass.simpleName
}

val CONTINUATION: Regex = Regex("\\\\\\n[ \\t\\f]*")

fun String.removeLineContinuations(): String {
return replace(CONTINUATION, "");

Check warning on line 53 in src/src/lang/psi/ShapeProperty.kt

View workflow job for this annotation

GitHub Actions / Inspect code

Redundant semicolon

Redundant semicolon
}
15 changes: 5 additions & 10 deletions src/testResources/psi/classes and array.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ D2 File
PsiWhiteSpace(' ')
StringValue
PsiElement(STRING)('"qweqwr"')
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
PsiWhiteSpace('\n ')
ASTWrapperPsiElement(SUB_ID_PROPERTY_MAP)
ASTWrapperPsiElement(ID_PROPERTY_KEY)
PsiElement(STRING)('"324:"')
Expand All @@ -28,17 +27,15 @@ D2 File
PsiWhiteSpace(' ')
StringValue
PsiElement(STRING)('""')
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
PsiWhiteSpace('\n ')
ASTWrapperPsiElement(ID_PROPERTY)
ASTWrapperPsiElement(ID_PROPERTY_KEY)
PsiElement(SIMPLE_RESERVED_KEYWORDS)('icon')
PsiElement(COLON)(':')
PsiWhiteSpace(' ')
UnquotedStringValue
PsiElement(UNQUOTED_STRING)('https://play.d2lang.com/assets/icons/d2-logo.svg')
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
PsiWhiteSpace('\n ')
PsiElement(RBRACE)('}')
PsiWhiteSpace('\n ')
ASTWrapperPsiElement(SUB_ID_PROPERTY_MAP)
Expand All @@ -55,8 +52,7 @@ D2 File
PsiWhiteSpace(' ')
UnquotedStringValue
PsiElement(UNQUOTED_STRING)('circle')
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
PsiWhiteSpace('\n ')
ASTWrapperPsiElement(ID_PROPERTY)
ASTWrapperPsiElement(ID_PROPERTY_KEY)
PsiElement(STYLE_KEYWORD)('style')
Expand All @@ -66,8 +62,7 @@ D2 File
PsiWhiteSpace(' ')
OtherValue
PsiElement(INT)('0')
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
PsiWhiteSpace('\n ')
PsiElement(RBRACE)('}')
PsiWhiteSpace('\n')
PsiElement(RBRACE)('}')
Expand Down
15 changes: 5 additions & 10 deletions src/testResources/psi/parsing.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ D2 File
PsiWhiteSpace(' ')
UnquotedStringValue
PsiElement(UNQUOTED_STRING)('page')
PsiWhiteSpace('\n\n')
PsiWhiteSpace(' ')
PsiWhiteSpace('\n\n ')
PsiComment(COMMENT)('# still boolean or int literals is properly highlighted')
PsiWhiteSpace('\n ')
ShapeProperty
Expand Down Expand Up @@ -119,8 +118,7 @@ D2 File
PsiWhiteSpace(' ')
UnquotedStringValue
PsiElement(UNQUOTED_STRING)('stored_data')
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
PsiWhiteSpace('\n ')
ShapeProperty
ShapePropertyKey
PsiElement(STYLE_KEYWORD)('style')
Expand All @@ -130,8 +128,7 @@ D2 File
PsiWhiteSpace(' ')
OtherValue
PsiElement(TRUE)('true')
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
PsiWhiteSpace('\n ')
PsiElement(RBRACE)('}')
PsiWhiteSpace('\n\n ')
ShapeConnection
Expand Down Expand Up @@ -202,8 +199,7 @@ D2 File
PsiWhiteSpace(' ')
UnquotedStringValue
PsiElement(UNQUOTED_STRING)('cylinder')
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
PsiWhiteSpace('\n ')
ShapeProperty
ShapePropertyKey
PsiElement(STYLE_KEYWORD)('style')
Expand All @@ -213,8 +209,7 @@ D2 File
PsiWhiteSpace(' ')
OtherValue
PsiElement(TRUE)('true')
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
PsiWhiteSpace('\n ')
PsiElement(RBRACE)('}')
PsiWhiteSpace('\n ')
PsiElement(RBRACE)('}')
Expand Down
3 changes: 1 addition & 2 deletions src/testResources/psi/style object.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ D2 File
PsiWhiteSpace(' ')
OtherValue
PsiElement(INT)('10')
PsiWhiteSpace('\n')
PsiWhiteSpace(' ')
PsiWhiteSpace('\n ')
PsiElement(RBRACE)('}')
PsiWhiteSpace('\n')
PsiElement(RBRACE)('}')
Expand Down
60 changes: 60 additions & 0 deletions src/testResources/psi/values and labels.d2
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
legalString: ab
legalString: a\
b

legalString: 1
legalString: 1.1

legalString: '#111111'
legalString: "#111111"
legalString: "#111\
111"

legalString: ''
legalString: '\'
legalString: '\\'
legalString: '\n'
legalString: ""
legalString: "\\"
legalString: "'"
legalString: "\""
legalString: "\n"

logs.style.stroke: "#694024"
logs.style.stroke: '#694024'
logs.style.stroke: "#694\
024"

logs.style.multiple: false
logs.style.multiple: f\
alse
logs.style.multiple: fa\
lse
logs.style.multiple: fal\
se
logs.style.multiple: fals\
e

logs.style.multiple: f\
\
\
alse

logs.style.multiple: true
logs.style.multiple: t\
rue
logs.style.multiple: tr\
ue
logs.style.multiple: tru\
e

logs.style.font-size: 10
logs.style.font-size: 1\
0

logs.style.opacity: 0.4
logs.style.opacity: 0\
.5
logs.style.opacity: 0.\
6

Loading

0 comments on commit 1e82ce0

Please sign in to comment.