Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@gdamore
Copy link
Owner

@gdamore gdamore commented Dec 13, 2025

…t init values (fixes #20)

While here make the entire color API '@safe'.

Summary by CodeRabbit

  • New Features

    • Added support for blink, reverse, and underline text formatting attributes
    • Introduced color naming and CSS hex string generation utilities
  • Refactor

    • Reorganized internal color system architecture and validity checking
    • Enhanced code safety guarantees and type annotations throughout

✏️ Tip: You can customize this high-level summary in your review settings.

…t init values (fixes #20)

While here make the entire color API '@safe'.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 13, 2025

Walkthrough

The pull request updates enum definitions and function signatures across the dcell library. The Attr enum removes explicit type declaration and introduces new bit-flag members. The Color enum is restructured with adjusted values and a new init alias. Multiple color utility functions gain @safe/@pure annotations. A new color naming API (name/css functions) is introduced to support color lookup by name. The Style struct fields are reordered.

Changes

Cohort / File(s) Summary
Attr enum expansion
source/dcell/attr.d
Removed explicit underlying type declaration (enum Attr : intenum Attr). Added new bit-flag members: blink (1 << 1), reverse (1 << 2), underline (1 << 3). Changed invalid value from -1 to 1 << 7 (positive bit value). Added init alias equating to invalid.
Color enum restructuring & API enhancements
source/dcell/color.d
Restructured Color enum: invalid now equals 1 << 24, reset defined as invalid + 1, removed none member, added init alias. Applied @safe/@pure annotations to public functions: toHex, fromHex, toRGB, isRGB, isValid, decompose, toPalette, darker, name, css. Introduced static immutable colorsByName map and new public functions name() and css() for W3C color name lookup and CSS hex string generation. Updated shared static initializer.
Test path adjustment
source/dcell/cell.d
Changed cell style test path: st.bg now set to Color.invalid instead of Color.none when st.attr is Attr.reverse.
Style struct formatting & field reordering
source/dcell/style.d
Updated copyright year to 2025. Reformatted struct declaration brace placement. Reordered public fields: moved attr field after url field (from (fg, bg, attr, url) to (fg, bg, url, attr)). Removed trailing comment block.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • source/dcell/color.d requires close attention: significant enum value restructuring, multiple new public functions with safety annotations, and new static initialization logic for colorsByName map need verification for correctness and compatibility.
  • source/dcell/attr.d: Verify bit-flag semantics and ensure the invalid value shift from -1 to 1 << 7 doesn't introduce unexpected behavior in downstream code.
  • source/dcell/style.d: Field reordering may have ABI/binary compatibility implications; confirm this change is intentional and doesn't affect serialization or FFI boundaries.
  • Cross-module consistency: Ensure the Color.invalid and Attr.invalid changes align consistently across cell.d and other dependent code.

Poem

🐰 Colors dance in shades of hex and name,
New flags flutter—blink, reverse, the same!
@safe our functions, pure our art,
Init and invalid play their part,
A colorful refactor, done with flair! 🎨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Changes include multiple out-of-scope modifications: Attr enum redesign with bit-flag optimization, Style struct field reordering, copyright year updates, and color enum restructuring beyond the linked issue scope. Separate attribute optimization and style restructuring into a dedicated PR; keep this PR focused on issue #20 color name APIs only.
Title check ❓ Inconclusive The title references main objectives (color names/css APIs, attr optimization) but is truncated and incomplete. Complete the title to reflect all changes: e.g., 'feat: Add color names and CSS APIs, optimize attr flags and set default init values'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR successfully implements issue #20 requirements: bidirectional color name/string mapping via new name() and css() functions, well-known color names via colorsByName map, and marks the color API @safe.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch gdamore/color-names

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
source/dcell/color.d (1)

829-843: Consider reverse lookup map for name() performance.

The name() function performs O(n) linear search through colorsByName (140+ entries) for each call. If this function is called frequently, consider adding a reverse lookup map (Color[string]string[Color]) populated during module initialization.

However, if this is primarily for theme file loading (not a hot path), the current implementation is acceptable.

+private static immutable string[Color] namesByColor;
+
+shared static this() @safe
+{
+    // ... existing colorsByName initialization ...
+    foreach (name, color; colorsByName)
+    {
+        namesByColor[color] = name;
+    }
+}
+
 string name(Color c, bool css = false) pure @safe
 {
-    foreach (name, color; colorsByName)
-    {
-        if (c == color)
-        {
-            return name;
-        }
-    }
+    if (auto p = c in namesByColor)
+    {
+        return *p;
+    }
     if (css)
     {
         return c.css();
     }
     return "";
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb46004 and b2c7ea7.

📒 Files selected for processing (4)
  • source/dcell/attr.d (3 hunks)
  • source/dcell/cell.d (1 hunks)
  • source/dcell/color.d (15 hunks)
  • source/dcell/style.d (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-08T19:17:33.678Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:448-494
Timestamp: 2025-12-08T19:17:33.678Z
Learning: In source/dcell/ttyscreen.d, Color.reset is defined as a value above the range of valid colors. In the sendColors() function, after emitting the reset sequence (vt.resetFgBg), Color.reset values naturally skip all subsequent color-setting logic (isRGB checks and palette range checks < 8, < 256) because they are out of range, which is the correct behavior by design.

Applied to files:

  • source/dcell/color.d
🔇 Additional comments (7)
source/dcell/attr.d (1)

16-28: Good addition of init = invalid for safe default initialization.

The changes properly align Attr with D's initialization semantics—uninitialized Attr variables will now default to invalid, preventing accidental use of uninitialized attributes. Moving invalid to a positive bit value (1 << 7) also keeps it consistent with the bit-flag pattern.

source/dcell/cell.d (1)

495-500: Test updated to use Color.invalid consistently with new Color semantics.

The change from (presumably) Color.none to Color.invalid aligns with the refactored Color enum where invalid now represents "do not change color." The test correctly validates fill operations with this style configuration.

source/dcell/style.d (1)

21-27: Field reordering is acceptable.

The struct field order change (placing url before attr) doesn't introduce any functional issues. D structs don't have implicit ordering guarantees for equality or serialization that would break here.

source/dcell/color.d (4)

23-169: Well-designed enum structure with proper default initialization.

The Color enum changes are well thought out:

  • invalid = 1 << 24 sits above the RGB value range but below the isRGB flag
  • init = invalid ensures uninitialized Color variables are explicitly invalid, preventing silent bugs
  • This aligns with the existing design where Color.reset naturally skips color-setting logic (based on learnings)

174-690: Module initializer correctly marked @safe.

The static constructor initialization of rgbValues and palValues is appropriately marked @safe, as associative array insertions are safe operations.


894-899: Good test coverage for the new naming API.

The tests correctly validate:

  • Direct name lookup for palette colors (Color.red.name)
  • Case handling (Color.darkKhaki.name == "darkkhaki")
  • CSS hex output
  • Fallback behavior when RGB colors aren't in the name map

971-972: Good test for default initialization semantics.

This test correctly validates that uninitialized Color variables default to Color.invalid due to the init = invalid alias, which is crucial for safe-by-default behavior.

Comment on lines +845 to +852
string css(Color c) pure @safe
{
if (!c.isValid)
{
return "";
}
return format("#%06X", c.toHex());
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential issue: css() may produce invalid output for Color.reset.

The isValid() check only verifies c != Color.invalid, but toHex() returns -1 for Color.reset (and other non-RGB colors not in rgbValues). Calling format("#%06X", -1) produces an invalid CSS hex string.

Consider extending the validity check:

 string css(Color c) pure @safe
 {
-    if (!c.isValid)
+    auto hex = c.toHex();
+    if (hex < 0)
     {
         return "";
     }
-    return format("#%06X", c.toHex());
+    return format("#%06X", hex);
 }
🤖 Prompt for AI Agents
In source/dcell/color.d around lines 845 to 852, css(Color c) currently only
checks c.isValid but toHex() can return -1 for Color.reset and other non-RGB
colors, causing format("#%06X", -1) to produce an invalid CSS hex; update the
validity check to also ensure the hex value is in the valid RGB range (e.g. call
auto h = c.toHex(); if (h < 0 || h > 0xFFFFFF) return ""; ) so css() only
formats legitimate 24-bit RGB values.

Comment on lines +990 to +1147

// ColorNames holds the written names of colors. Useful to present a list of
// recognized named colors.
static immutable Color[string] colorsByName;

shared static this() @safe
{
colorsByName = [
"black": Color.black,
"maroon": Color.maroon,
"green": Color.green,
"olive": Color.olive,
"navy": Color.navy,
"purple": Color.purple,
"teal": Color.teal,
"silver": Color.silver,
"gray": Color.gray,
"red": Color.red,
"lime": Color.lime,
"yellow": Color.yellow,
"blue": Color.blue,
"fuchsia": Color.fuchsia,
"aqua": Color.aqua,
"white": Color.white,
"aliceblue": Color.aliceBlue,
"antiquewhite": Color.antiqueWhite,
"aquamarine": Color.aquamarine,
"azure": Color.azure,
"beige": Color.beige,
"bisque": Color.bisque,
"blanchedalmond": Color.blanchedAlmond,
"blueviolet": Color.blueViolet,
"brown": Color.brown,
"burlywood": Color.burlyWood,
"cadetblue": Color.cadetBlue,
"chartreuse": Color.chartreuse,
"chocolate": Color.chocolate,
"coral": Color.coral,
"cornflowerblue": Color.cornflowerBlue,
"cornsilk": Color.cornsilk,
"crimson": Color.crimson,
"darkblue": Color.darkBlue,
"darkcyan": Color.darkCyan,
"darkgoldenrod": Color.darkGoldenrod,
"darkgray": Color.darkGray,
"darkgreen": Color.darkGreen,
"darkkhaki": Color.darkKhaki,
"darkmagenta": Color.darkMagenta,
"darkolivegreen": Color.darkOliveGreen,
"darkorange": Color.darkOrange,
"darkorchid": Color.darkOrchid,
"darkred": Color.darkRed,
"darksalmon": Color.darkSalmon,
"darkseagreen": Color.darkSeaGreen,
"darkslateblue": Color.darkSlateBlue,
"darkslategray": Color.darkSlateGray,
"darkturquoise": Color.darkTurquoise,
"darkviolet": Color.darkViolet,
"deeppink": Color.deepPink,
"deepskyblue": Color.deepSkyBlue,
"dimgray": Color.dimGray,
"dodgerblue": Color.dodgerBlue,
"firebrick": Color.fireBrick,
"floralwhite": Color.floralWhite,
"forestgreen": Color.forestGreen,
"gainsboro": Color.gainsboro,
"ghostwhite": Color.ghostWhite,
"gold": Color.gold,
"goldenrod": Color.goldenrod,
"greenyellow": Color.greenYellow,
"honeydew": Color.honeydew,
"hotpink": Color.hotPink,
"indianred": Color.indianRed,
"indigo": Color.indigo,
"ivory": Color.ivory,
"khaki": Color.khaki,
"lavender": Color.lavender,
"lavenderblush": Color.lavenderBlush,
"lawngreen": Color.lawnGreen,
"lemonchiffon": Color.lemonChiffon,
"lightblue": Color.lightBlue,
"lightcoral": Color.lightCoral,
"lightcyan": Color.lightCyan,
"lightgoldenrodyellow": Color.lightGoldenrodYellow,
"lightgray": Color.lightGray,
"lightgreen": Color.lightGreen,
"lightpink": Color.lightPink,
"lightsalmon": Color.lightSalmon,
"lightseagreen": Color.lightSeaGreen,
"lightskyblue": Color.lightSkyBlue,
"lightslategray": Color.lightSlateGray,
"lightsteelblue": Color.lightSteelBlue,
"lightyellow": Color.lightYellow,
"limegreen": Color.limeGreen,
"linen": Color.linen,
"mediumaquamarine": Color.mediumAquamarine,
"mediumblue": Color.mediumBlue,
"mediumorchid": Color.mediumOrchid,
"mediumpurple": Color.mediumPurple,
"mediumseagreen": Color.mediumSeaGreen,
"mediumslateblue": Color.mediumSlateBlue,
"mediumspringgreen": Color.mediumSpringGreen,
"mediumturquoise": Color.mediumTurquoise,
"mediumvioletred": Color.mediumVioletRed,
"midnightblue": Color.midnightBlue,
"mintcream": Color.mintCream,
"mistyrose": Color.mistyRose,
"moccasin": Color.moccasin,
"navajowhite": Color.navajoWhite,
"oldlace": Color.oldLace,
"olivedrab": Color.oliveDrab,
"orange": Color.orange,
"orangered": Color.orangeRed,
"orchid": Color.orchid,
"palegoldenrod": Color.paleGoldenrod,
"palegreen": Color.paleGreen,
"paleturquoise": Color.paleTurquoise,
"palevioletred": Color.paleVioletRed,
"papayawhip": Color.papayaWhip,
"peachpuff": Color.peachPuff,
"peru": Color.peru,
"pink": Color.pink,
"plum": Color.plum,
"powderblue": Color.powderBlue,
"rebeccapurple": Color.rebeccaPurple,
"rosybrown": Color.rosyBrown,
"royalblue": Color.royalBlue,
"saddlebrown": Color.saddleBrown,
"salmon": Color.salmon,
"sandybrown": Color.sandyBrown,
"seagreen": Color.seaGreen,
"seashell": Color.seashell,
"sienna": Color.sienna,
"skyblue": Color.skyBlue,
"slateblue": Color.slateBlue,
"slategray": Color.slateGray,
"snow": Color.snow,
"springgreen": Color.springGreen,
"steelblue": Color.steelBlue,
"tan": Color.tan,
"thistle": Color.thistle,
"tomato": Color.tomato,
"turquoise": Color.turquoise,
"violet": Color.violet,
"wheat": Color.wheat,
"whitesmoke": Color.whiteSmoke,
"yellowgreen": Color.yellowGreen,
"grey": Color.gray,
"dimgrey": Color.dimGray,
"darkgrey": Color.darkGray,
"darkslategrey": Color.darkSlateGray,
"lightgrey": Color.lightGray,
"lightslategrey": Color.lightSlateGray,
"slategrey": Color.slateGray,

"reset": Color.reset, // actually the terminal default
];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing public API to look up Color by name string.

The PR objectives mention "Provide a way to look up a color by name using a user-provided string (e.g., from a theme file)." While colorsByName is populated with name→Color mappings, there's no public function exposed to perform this lookup. Users would need to access colorsByName directly.

Consider adding a public lookup function:

/**
 * Look up a color by its W3C name.
 * Params:
 *   name = color name (case-insensitive)
 * Returns: The matching Color, or Color.invalid if not found.
 */
Color fromName(string name) pure @safe
{
    import std.uni : toLower;
    auto key = name.toLower;
    if (auto p = key in colorsByName)
    {
        return *p;
    }
    return Color.invalid;
}
🤖 Prompt for AI Agents
In source/dcell/color.d around lines 990 to 1147, there is a populated
colorsByName map but no public API to look up a Color by a user-provided name;
add a public function (e.g., Color fromName(string name) pure @safe) placed near
the static initializer that: normalizes the input to lowercase (use
std.uni.toLower), looks up the key in colorsByName, returns the found Color, and
returns Color.invalid when not found; ensure the function's attributes match
safety/purity expectations and its visibility is public so callers can use it
without accessing the map directly.

@gdamore gdamore merged commit 9e48945 into main Dec 13, 2025
3 checks passed
@gdamore gdamore deleted the gdamore/color-names branch December 13, 2025 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Color string API

2 participants