From 65a7555e93e2f7375427160ca263dad16ae6493d Mon Sep 17 00:00:00 2001 From: sergeych Date: Fri, 5 Dec 2025 00:30:32 +0100 Subject: [PATCH] fix $76 add support for enum constants highlighting and initial enum documentation --- docs/OOP.md | 74 +++++++++++++++++++ docs/tutorial.md | 20 +++++ .../idea/annotators/LyngExternalAnnotator.kt | 12 +++ .../idea/highlight/LyngColorSettingsPage.kt | 1 + .../idea/highlight/LyngHighlighterColors.kt | 5 ++ site/src/jsMain/kotlin/HighlightSupport.kt | 1 + site/src/jsMain/resources/index.html | 12 +-- 7 files changed, 120 insertions(+), 5 deletions(-) diff --git a/docs/OOP.md b/docs/OOP.md index 0791d40..28f37ff 100644 --- a/docs/OOP.md +++ b/docs/OOP.md @@ -168,6 +168,80 @@ Compatibility notes: Earlier drafts and docs described a declaration‑order depth‑first linearization. Lyng now uses C3 MRO for member lookup and disambiguation. Most code should continue to work unchanged, but in rare edge cases involving diamonds or complex multiple inheritance, the chosen base for an ambiguous member may change to reflect C3. If needed, disambiguate explicitly using `this@Type.member(...)` inside class bodies or casts `(expr as Type).member(...)` from outside. +## Enums + +Lyng provides lightweight enums for representing a fixed set of named constants. Enums are classes whose instances are predefined and singletons. + +Current syntax supports simple enum declarations with just entry names: + + enum Color { + RED, GREEN, BLUE + } + +Usage: + +- Type of entries: every entry is an instance of its enum type. + + assert( Color.RED is Color ) + +- Order and names: each entry has zero‑based `ordinal` and string `name`. + + assertEquals(0, Color.RED.ordinal) + assertEquals("BLUE", Color.BLUE.name) + +- All entries as a list in declaration order: `EnumType.entries`. + + assertEquals([Color.RED, Color.GREEN, Color.BLUE], Color.entries) + +- Lookup by name: `EnumType.valueOf("NAME")` → entry. + + assertEquals(Color.GREEN, Color.valueOf("GREEN")) + +- Equality and comparison: + - Equality uses identity of entries, e.g., `Color.RED == Color.valueOf("RED")`. + - Cross‑enum comparisons are not allowed. + - Ordering comparisons use `ordinal`. + + assert( Color.RED == Color.valueOf("RED") ) + assert( Color.RED.ordinal < Color.BLUE.ordinal ) + >>> void + +### Enums with `when` + +Use `when(subject)` with equality branches for enums. See full `when` guide: [The `when` statement](when.md). + + enum Color { RED, GREEN, BLUE } + + fun describe(c) { + when(c) { + Color.RED, Color.GREEN -> "primary-like" + Color.BLUE -> "blue" + else -> "unknown" // if you pass something that is not a Color + } + } + assertEquals("primary-like", describe(Color.RED)) + assertEquals("blue", describe(Color.BLUE)) + >>> void + +### Serialization + +Enums are serialized compactly with Lynon: the encoded value stores just the entry ordinal within the enum type, which is both space‑efficient and fast. + + import lyng.serialization + + enum Color { RED, GREEN, BLUE } + + val e = Lynon.encode(Color.BLUE) + val decoded = Lynon.decode(e) + assertEquals(Color.BLUE, decoded) + >>> void + +Notes and limitations (current version): + +- Enum declarations support only simple entry lists: no per‑entry bodies, no custom constructors, and no user‑defined methods/fields on the enum itself yet. +- `name` and `ordinal` are read‑only properties of an entry. +- `entries` is a read‑only list owned by the enum type. + ## fields and visibility It is possible to add non-constructor fields: diff --git a/docs/tutorial.md b/docs/tutorial.md index 12137ee..be489b1 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -66,6 +66,26 @@ You can use blocks in if statement, as expected: limited >>> 120.0 +## Enums (quick intro) + +Lyng supports simple enums for a fixed set of named constants. Declare with `enum Name { ... }` and use entries as `Name.ENTRY`. + + enum Color { + RED, GREEN, BLUE + } + + assert( Color.RED is Color ) + assertEquals( 0, Color.RED.ordinal ) + assertEquals( "BLUE", Color.BLUE.name ) + + // All entries as a list, in order: + assertEquals( [Color.RED, Color.GREEN, Color.BLUE], Color.entries ) + + // Lookup by name: + assertEquals( Color.GREEN, Color.valueOf("GREEN") ) + +For more details (usage patterns, `when` switching, serialization), see OOP notes: [Enums in detail](OOP.md#enums). + When putting multiple statments in the same line it is convenient and recommended to use `;`: var from; var to diff --git a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/annotators/LyngExternalAnnotator.kt b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/annotators/LyngExternalAnnotator.kt index f524cf4..95ba5cd 100644 --- a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/annotators/LyngExternalAnnotator.kt +++ b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/annotators/LyngExternalAnnotator.kt @@ -262,6 +262,18 @@ class LyngExternalAnnotator : ExternalAnnotator() try { diff --git a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngColorSettingsPage.kt b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngColorSettingsPage.kt index 9209506..1226ce2 100644 --- a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngColorSettingsPage.kt +++ b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngColorSettingsPage.kt @@ -65,6 +65,7 @@ class LyngColorSettingsPage : ColorSettingsPage { AttributesDescriptor("Type (semantic)", LyngHighlighterColors.TYPE), AttributesDescriptor("Namespace (semantic)", LyngHighlighterColors.NAMESPACE), AttributesDescriptor("Parameter (semantic)", LyngHighlighterColors.PARAMETER), + AttributesDescriptor("Enum constant (semantic)", LyngHighlighterColors.ENUM_CONSTANT), ) override fun getColorDescriptors(): Array = ColorDescriptor.EMPTY_ARRAY diff --git a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngHighlighterColors.kt b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngHighlighterColors.kt index b5aa440..4714224 100644 --- a/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngHighlighterColors.kt +++ b/lyng-idea/src/main/kotlin/net/sergeych/lyng/idea/highlight/LyngHighlighterColors.kt @@ -77,4 +77,9 @@ object LyngHighlighterColors { val ANNOTATION: TextAttributesKey = TextAttributesKey.createTextAttributesKey( "LYNG_ANNOTATION", DefaultLanguageHighlighterColors.METADATA ) + + // Enum constant (declaration or usage) + val ENUM_CONSTANT: TextAttributesKey = TextAttributesKey.createTextAttributesKey( + "LYNG_ENUM_CONSTANT", DefaultLanguageHighlighterColors.STATIC_FIELD + ) } diff --git a/site/src/jsMain/kotlin/HighlightSupport.kt b/site/src/jsMain/kotlin/HighlightSupport.kt index 816d60a..c82b3dd 100644 --- a/site/src/jsMain/kotlin/HighlightSupport.kt +++ b/site/src/jsMain/kotlin/HighlightSupport.kt @@ -42,6 +42,7 @@ object SiteHighlightLocal { HighlightKind.Label -> "hl-lbl" HighlightKind.Directive -> "hl-dir" HighlightKind.Error -> "hl-err" + HighlightKind.EnumConstant -> "hl-enumc" } private fun htmlEscape(s: String): String = buildString(s.length) { diff --git a/site/src/jsMain/resources/index.html b/site/src/jsMain/resources/index.html index 7d7bee5..f4e065e 100644 --- a/site/src/jsMain/resources/index.html +++ b/site/src/jsMain/resources/index.html @@ -117,6 +117,7 @@ .hl-class { color: #5a32a3; font-weight: 600; } .hl-val { color: #1b7f5a; } .hl-var { color: #1b7f5a; text-decoration: underline dotted currentColor; } + .hl-enumc { color: #b08800; font-weight: 600; } .hl-param { color: #0969da; font-style: italic; } .hl-num { color: #005cc5; } .hl-str { color: #032f62; } @@ -139,6 +140,7 @@ [data-bs-theme="dark"] .hl-class{ color: #d2a8ff; font-weight: 700; } [data-bs-theme="dark"] .hl-val { color: #7ee787; } [data-bs-theme="dark"] .hl-var { color: #7ee787; text-decoration: underline dotted currentColor; } + [data-bs-theme="dark"] .hl-enumc{ color: #f2cc60; font-weight: 700; } [data-bs-theme="dark"] .hl-param{ color: #a5d6ff; font-style: italic; } [data-bs-theme="dark"] .hl-num { color: #79c0ff; } [data-bs-theme="dark"] .hl-str, @@ -156,7 +158,7 @@ z-index: 1020; /* above content, below navbar */ top: var(--navbar-offset, 56px); left: 0; - width: 162px; /* 10% narrower than 180px */ + width: 242px; /* 10% narrower than 180px */ text-align: center; /* Slightly asymmetric padding to visually center text within rotated band (desktop) */ /* Nudge text a bit lower on large screens */ @@ -171,9 +173,9 @@ } @media (max-width: 576px) { .corner-ribbon { - width: 135px; /* 10% narrower than 150px */ + width: 195px; /* 10% narrower than 150px */ transform: translate(-50px, 8px) rotate(-45deg); - font-size: .7rem; /* even smaller on phones */ + font-size: .6rem; /* even smaller on phones */ padding: .32rem 0 .28rem; /* keep mobile text centered too */ } } @@ -182,8 +184,8 @@
- - v1.0.3 + + v1.0.6-SNAPSHOT