Skip to content

Commit 05dad7e

Browse files
committed
Merge branch 'develop' into upgrade-color
2 parents eaf82d3 + 086cc9e commit 05dad7e

File tree

5 files changed

+268
-33
lines changed

5 files changed

+268
-33
lines changed

src/main/kotlin/org/elm/ide/color/ElmColorSettingsPage.kt

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,71 +10,89 @@ class ElmColorSettingsPage : ColorSettingsPage {
1010
private val ATTRS = ElmColor.values().map { it.attributesDescriptor }.toTypedArray()
1111

1212
override fun getDisplayName() =
13-
"Elm"
13+
"Elm"
1414

1515
override fun getIcon() =
16-
ElmIcons.FILE
16+
ElmIcons.FILE
1717

1818
override fun getAttributeDescriptors() =
19-
ATTRS
19+
ATTRS
2020

2121
override fun getColorDescriptors(): Array<ColorDescriptor> =
22-
ColorDescriptor.EMPTY_ARRAY
22+
ColorDescriptor.EMPTY_ARRAY
2323

2424
override fun getHighlighter() =
25-
ElmSyntaxHighlighter()
25+
ElmSyntaxHighlighter()
2626

2727
override fun getAdditionalHighlightingTagToDescriptorMap() =
28-
// special tags in [demoText] for semantic highlighting
29-
mapOf(
30-
"type" to ElmColor.TYPE_EXPR,
31-
"variant" to ElmColor.UNION_VARIANT,
32-
"accessor" to ElmColor.RECORD_FIELD_ACCESSOR,
33-
"field" to ElmColor.RECORD_FIELD,
34-
"func_decl" to ElmColor.DEFINITION_NAME
28+
// special tags in [demoText] for semantic highlighting
29+
mapOf(
30+
"type" to ElmColor.TYPE_EXPR,
31+
"type_decl" to ElmColor.TYPE_DECLARATION,
32+
"variant" to ElmColor.UNION_VARIANT,
33+
"accessor" to ElmColor.RECORD_FIELD_ACCESSOR,
34+
"field" to ElmColor.RECORD_FIELD,
35+
"func_decl" to ElmColor.DEFINITION_NAME,
36+
37+
"fn_arg" to ElmColor.FUNCTION_ARGUMENT,
38+
"fn_loc" to ElmColor.LOCAL_FUNCTION,
39+
"fn_loc_arg" to ElmColor.LOCAL_FUNCTION_ARGUMENT,
40+
"pattern_arg" to ElmColor.PATTERN_ARGUMENT,
41+
"fn_inl_arg" to ElmColor.INLINE_FUNCTION_ARGUMENT,
42+
43+
"ext_module" to ElmColor.EXTERNAL_MODULE,
44+
"ext_fn_call" to ElmColor.EXTERNAL_FUNCTION_CALL,
45+
"port" to ElmColor.PORT,
46+
3547
).mapValues { it.value.textAttributesKey }
3648

3749
override fun getDemoText() =
38-
demoCodeText
50+
demoCodeText
3951
}
4052

4153
private const val demoCodeText = """
4254
module Todo exposing (..)
4355
44-
import Html exposing (div, h1, ul, li, text)
56+
import <ext_module>Html</ext_module> exposing (<ext_fn_call>div</ext_fn_call>, <ext_fn_call>h1</ext_fn_call>, <ext_fn_call>ul</ext_fn_call>, <ext_fn_call>li</ext_fn_call>, <ext_fn_call>text</ext_fn_call>)
4557
4658
-- a single line comment
4759
48-
type alias Model =
60+
port <port>samplePort</port> : () -> <type>Cmd msg</type>
61+
62+
type alias <type_decl>Model</type_decl> =
4963
{ <field>page</field> : <type>Int</type>
5064
, <field>title</field> : <type>String</type>
5165
, <field>stepper</field> : <type>Int</type> -> <type>Int</type>
5266
}
5367
54-
type Msg <type>a</type>
68+
type <type_decl>Msg</type_decl> <type>a</type>
5569
= <variant>ModeA</variant>
5670
| <variant>ModeB</variant> <type>Maybe a</type>
5771
5872
<func_decl>update</func_decl> : <type>Msg</type> -> <type>Model</type> -> ( <type>Model</type>, <type>Cmd Msg</type> )
59-
<func_decl>update</func_decl> msg model =
60-
case msg of
61-
<variant>ModeA</variant> ->
62-
{ model
73+
<func_decl>update</func_decl> <fn_arg>msg</fn_arg> <fn_arg>model</fn_arg> =
74+
let
75+
<fn_loc>localFunction</fn_loc> <fn_loc_arg>a</fn_loc_arg> =
76+
<fn_loc_arg>a</fn_loc_arg>
77+
in
78+
case <fn_arg>msg</fn_arg> of
79+
<variant>ModeB</variant> <pattern_arg>maybe</pattern_arg> ->
80+
{ <fn_arg>model</fn_arg>
6381
| <field>page</field> = 0
64-
, <field>title</field> = "Mode A"
65-
, <field>stepper</field> = (\k -> k + 1)
82+
, <field>title</field> = "Mode " ++ (<fn_loc>localFunction</fn_loc> "B")
83+
, <field>stepper</field> = (\<fn_inl_arg>k</fn_inl_arg> -> <fn_inl_arg>k</fn_inl_arg> + 1)
6684
}
6785
! []
6886
6987
<func_decl>view</func_decl> : <type>Model</type> -> <type>Html.Html Msg</type>
70-
<func_decl>view</func_decl> model =
88+
<func_decl>view</func_decl> <fn_arg>model</fn_arg> =
7189
let
72-
<func_decl>itemify</func_decl> label =
73-
li [] [ text label ]
90+
<func_decl>itemify</func_decl> <fn_arg>label</fn_arg> =
91+
<ext_fn_call>li</ext_fn_call> [] [ <ext_fn_call>text</ext_fn_call> <fn_arg>label</fn_arg> ]
7492
in
75-
div []
76-
[ h1 [] [ text "Chapter One" ]
77-
, ul []
78-
(List.map <accessor>.value</accessor> model.<field>items</field>)
93+
<ext_fn_call>div</ext_fn_call> []
94+
[ <ext_fn_call>h1</ext_fn_call> [] [ <ext_fn_call>text</ext_fn_call> "Chapter One" ]
95+
, <ext_fn_call>ul</ext_fn_call> []
96+
(<ext_module>List.</ext_module><ext_fn_call>map</ext_fn_call> <accessor>.value</accessor> <fn_arg>model</fn_arg>.<field>items</field>)
7997
]
8098
"""

src/main/kotlin/org/elm/ide/color/ElmColors.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ enum class ElmColor(humanName: String, default: TextAttributesKey) {
2626
EQ("Punctuation//Equals", Default.OPERATION_SIGN),
2727
PIPE("Punctuation//Pipe", Default.OPERATION_SIGN),
2828

29+
FUNCTION_ARGUMENT("Function Argument", Default.PARAMETER),
30+
LOCAL_FUNCTION("Local Function//Declaration", Default.INSTANCE_METHOD),
31+
LOCAL_FUNCTION_ARGUMENT("Local Function//Argument", Default.LOCAL_VARIABLE),
32+
PATTERN_ARGUMENT("Pattern Argument", Default.LOCAL_VARIABLE),
33+
INLINE_FUNCTION_ARGUMENT("Inline Function Argument", Default.LOCAL_VARIABLE),
34+
35+
EXTERNAL_MODULE("Module//Qualifier", Default.CLASS_REFERENCE),
36+
EXTERNAL_FUNCTION_CALL("Module//Function Call", Default.STATIC_METHOD),
37+
PORT("Port", Default.CLASS_REFERENCE),
38+
2939
/**
3040
* The name of a definition.
3141
*
@@ -43,7 +53,8 @@ enum class ElmColor(humanName: String, default: TextAttributesKey) {
4353
*
4454
* e.g. 'String' and 'Cmd msg' in 'foo : String -> Cmd msg'
4555
*/
46-
TYPE_EXPR("Type", Default.CLASS_REFERENCE),
56+
TYPE_EXPR("Type//Reference", Default.CLASS_REFERENCE),
57+
TYPE_DECLARATION("Type//Declaration", Default.CLASS_REFERENCE),
4758

4859
RECORD_FIELD("Records//Field", Default.INSTANCE_FIELD),
4960
RECORD_FIELD_ACCESSOR("Records//Field Accessor", Default.STATIC_FIELD);

src/main/kotlin/org/elm/ide/highlight/ElmSyntaxHighlightAnnotator.kt

Lines changed: 155 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import com.intellij.lang.annotation.HighlightSeverity
77
import com.intellij.psi.PsiElement
88
import com.intellij.psi.util.PsiTreeUtil
99
import org.elm.ide.color.ElmColor
10+
import org.elm.lang.core.psi.ancestors
1011
import org.elm.lang.core.psi.elements.*
12+
import org.elm.lang.core.psi.isTopLevel
1113

1214

1315
class ElmSyntaxHighlightAnnotator : Annotator {
@@ -18,6 +20,12 @@ class ElmSyntaxHighlightAnnotator : Annotator {
1820

1921
private fun AnnotationHolder.highlight(element: PsiElement) {
2022
when (element) {
23+
is ElmTypeDeclaration -> typeDecl(element)
24+
is ElmTypeAliasDeclaration -> typeAliasDecl(element)
25+
is ElmImportClause -> importClause(element)
26+
is ElmPortAnnotation -> portAnnotation(element)
27+
is ElmRecordBaseIdentifier -> recordUpdate(element)
28+
is ElmLowerPattern -> functionArgument(element)
2129
is ElmValueDeclaration -> valueDeclaration(element)
2230
is ElmTypeAnnotation -> typeAnnotation(element)
2331
is ElmUpperCaseQID -> upperCaseQID(element)
@@ -28,6 +36,142 @@ class ElmSyntaxHighlightAnnotator : Annotator {
2836
is ElmUnionVariant -> unionVariant(element.upperCaseIdentifier)
2937
is ElmTypeVariable -> typeExpr(element)
3038
is ElmLowerTypeName -> typeExpr(element)
39+
is ElmValueExpr -> valueExpr(element)
40+
}
41+
}
42+
43+
private fun AnnotationHolder.typeDecl(element: ElmTypeDeclaration) {
44+
applyColor(element.nameIdentifier, ElmColor.TYPE_DECLARATION)
45+
}
46+
47+
private fun AnnotationHolder.typeAliasDecl(element: ElmTypeAliasDeclaration) {
48+
applyColor(element.nameIdentifier, ElmColor.TYPE_DECLARATION)
49+
}
50+
51+
private fun AnnotationHolder.importClause(element: ElmImportClause) {
52+
applyColor(element.moduleQID, ElmColor.EXTERNAL_MODULE)
53+
element.exposingList?.exposedTypeList?.forEach { applyColor(it, ElmColor.TYPE_EXPR) }
54+
element.exposingList?.exposedValueList?.forEach { applyColor(it, ElmColor.EXTERNAL_FUNCTION_CALL) }
55+
}
56+
57+
private fun AnnotationHolder.portAnnotation(element: ElmPortAnnotation) {
58+
applyColor(element.lowerCaseIdentifier, ElmColor.PORT)
59+
}
60+
61+
private fun AnnotationHolder.recordUpdate(element: ElmRecordBaseIdentifier) {
62+
annotateArgument(element)
63+
}
64+
65+
private fun AnnotationHolder.annotateArgument(element: PsiElement): Boolean {
66+
val parentFunction = element.ancestors
67+
.filterIsInstance<ElmValueDeclaration>()
68+
.firstOrNull {
69+
it.functionDeclarationLeft?.namedParameters?.any { p -> p.nameIdentifier.text == element.text } ?: false
70+
}
71+
if (parentFunction != null) {
72+
if (parentFunction.isTopLevel) {
73+
applyColor(element, ElmColor.FUNCTION_ARGUMENT)
74+
} else {
75+
applyColor(element, ElmColor.LOCAL_FUNCTION_ARGUMENT)
76+
}
77+
return true
78+
}
79+
80+
val isInlineFunctionArgument = element.ancestors
81+
.filterIsInstance<ElmAnonymousFunctionExpr>()
82+
.firstOrNull {
83+
it.namedParameters.any { p -> p.nameIdentifier.text == element.text }
84+
} != null
85+
if (isInlineFunctionArgument) {
86+
applyColor(element, ElmColor.INLINE_FUNCTION_ARGUMENT)
87+
return true
88+
}
89+
90+
val isUnionPattern =
91+
element.ancestors.any { it is ElmCaseOfBranch && it.destructuredNames.any { name -> name.nameIdentifier.text == element.text } }
92+
if (isUnionPattern) {
93+
applyColor(element, ElmColor.PATTERN_ARGUMENT)
94+
return true
95+
}
96+
97+
return false
98+
}
99+
100+
private fun AnnotationHolder.valueExpr(element: ElmValueExpr) {
101+
if (element.flavor == Flavor.BareConstructor || element.flavor == Flavor.QualifiedConstructor) {
102+
return
103+
}
104+
105+
if (annotateArgument(element)) {
106+
return
107+
}
108+
109+
val isLocalFunction = element.ancestors
110+
.filterIsInstance<ElmLetInExpr>()
111+
.any {
112+
it.valueDeclarationList.any { decl -> decl.functionDeclarationLeft?.lowerCaseIdentifier?.text == element.text }
113+
}
114+
if (isLocalFunction) {
115+
applyColor(element, ElmColor.LOCAL_FUNCTION)
116+
return
117+
}
118+
119+
val functionDeclaration =
120+
element.elmFile.children.any { it is ElmValueDeclaration && it.functionDeclarationLeft?.name == element.text }
121+
if (functionDeclaration) {
122+
applyColor(element, ElmColor.DEFINITION_NAME)
123+
return
124+
}
125+
126+
val isPortAnnotation =
127+
element.elmFile.children.any { it is ElmPortAnnotation && it.lowerCaseIdentifier.text == element.text }
128+
if (isPortAnnotation) {
129+
applyColor(element, ElmColor.PORT)
130+
return
131+
}
132+
133+
val qid = element.valueQID
134+
if (qid != null && qid.isQualified == true) {
135+
qid.qualifiers.forEach {
136+
applyColor(it, ElmColor.EXTERNAL_MODULE)
137+
}
138+
applyColor(qid.lowerCaseIdentifier, ElmColor.EXTERNAL_FUNCTION_CALL)
139+
return
140+
}
141+
142+
applyColor(element, ElmColor.EXTERNAL_FUNCTION_CALL)
143+
}
144+
145+
private fun AnnotationHolder.functionArgument(element: ElmLowerPattern) {
146+
val parentFunction = PsiTreeUtil.getParentOfType(
147+
element,
148+
ElmFunctionDeclarationLeft::class.java
149+
);
150+
if (parentFunction != null) {
151+
if (parentFunction.isTopLevel) {
152+
applyColor(element, ElmColor.FUNCTION_ARGUMENT)
153+
} else {
154+
applyColor(element, ElmColor.LOCAL_FUNCTION_ARGUMENT)
155+
}
156+
return
157+
}
158+
159+
val anonymousFunction = PsiTreeUtil.getParentOfType(
160+
element,
161+
ElmAnonymousFunctionExpr::class.java
162+
);
163+
if (anonymousFunction != null) {
164+
applyColor(element, ElmColor.INLINE_FUNCTION_ARGUMENT)
165+
return
166+
}
167+
168+
val unionPattern = PsiTreeUtil.getParentOfType(
169+
element,
170+
ElmUnionPattern::class.java
171+
);
172+
if (unionPattern != null) {
173+
applyColor(element, ElmColor.PATTERN_ARGUMENT)
174+
return
31175
}
32176
}
33177

@@ -62,7 +206,13 @@ class ElmSyntaxHighlightAnnotator : Annotator {
62206

63207
private fun AnnotationHolder.valueDeclaration(declaration: ElmValueDeclaration) {
64208
declaration.declaredNames(includeParameters = false).forEach {
65-
applyColor(it.nameIdentifier, ElmColor.DEFINITION_NAME)
209+
if (it is ElmFunctionDeclarationLeft) {
210+
if (it.ancestors.filterIsInstance<ElmLetInExpr>().any()) {
211+
applyColor(it.nameIdentifier, ElmColor.LOCAL_FUNCTION)
212+
} else {
213+
applyColor(it.nameIdentifier, ElmColor.DEFINITION_NAME)
214+
}
215+
}
66216
}
67217
}
68218

@@ -80,6 +230,9 @@ class ElmSyntaxHighlightAnnotator : Annotator {
80230
}
81231

82232
private fun AnnotationHolder.applyColor(element: PsiElement, color: ElmColor) {
83-
newSilentAnnotation(HighlightSeverity.INFORMATION).textAttributes(color.textAttributesKey).range(element.textRange).create()
233+
newSilentAnnotation(HighlightSeverity.INFORMATION)
234+
.textAttributes(color.textAttributesKey)
235+
.range(element.textRange)
236+
.create()
84237
}
85238
}

src/main/kotlin/org/elm/workspace/commandLineTools/ElmFormatCLI.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ class ElmFormatCLI(private val elmFormatExecutablePath: Path) {
4545
class UnknownFailure(msg: String? = null, cause: Throwable?) : ElmFormatResult(msg ?: "Something went wrong running elm-format", cause)
4646
}
4747

48+
private fun isBadSyntaxErrorMessage(msg: String): Boolean {
49+
return msg.contains("SYNTAX PROBLEM", ignoreCase = true) // Elm-format 0.8.4 and below
50+
|| msg.contains("Unable to parse file") // Elm-format 0.8.5 and above
51+
}
52+
4853
fun formatDocumentAndSetText(project: Project, document: Document, version: Version, addToUndoStack: Boolean): ElmFormatResult {
4954
val processOutput = try {
5055
ProgressManager.getInstance().runProcessWithProgressSynchronously<ProcessOutput, ExecutionException>({
@@ -59,7 +64,12 @@ class ElmFormatCLI(private val elmFormatExecutablePath: Path) {
5964
}
6065
}
6166

62-
if (processOutput.isNotSuccess) return ElmFormatResult.UnknownFailure("Process output exit code was non-zero", cause = null)
67+
if (processOutput.isNotSuccess) {
68+
if (isBadSyntaxErrorMessage(processOutput.stderr)) {
69+
return ElmFormatResult.BadSyntax()
70+
}
71+
return ElmFormatResult.UnknownFailure("Process output exit code was non-zero", cause = null)
72+
}
6373

6474
val formatted = processOutput.stdout
6575
val source = document.text

0 commit comments

Comments
 (0)