-
Notifications
You must be signed in to change notification settings - Fork 3
feat: Add new color names/css APIs, also optimize attr and set defaul… #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
WalkthroughThe 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 Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✨ Finishing touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this 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 forname()performance.The
name()function performs O(n) linear search throughcolorsByName(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
📒 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 ofinit = invalidfor safe default initialization.The changes properly align
Attrwith D's initialization semantics—uninitializedAttrvariables will now default toinvalid, preventing accidental use of uninitialized attributes. Movinginvalidto 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 useColor.invalidconsistently with new Color semantics.The change from (presumably)
Color.nonetoColor.invalidaligns with the refactored Color enum whereinvalidnow 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
urlbeforeattr) 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 << 24sits above the RGB value range but below theisRGBflaginit = invalidensures uninitializedColorvariables are explicitly invalid, preventing silent bugs- This aligns with the existing design where
Color.resetnaturally skips color-setting logic (based on learnings)
174-690: Module initializer correctly marked@safe.The static constructor initialization of
rgbValuesandpalValuesis 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
Colorvariables default toColor.invaliddue to theinit = invalidalias, which is crucial for safe-by-default behavior.
| string css(Color c) pure @safe | ||
| { | ||
| if (!c.isValid) | ||
| { | ||
| return ""; | ||
| } | ||
| return format("#%06X", c.toHex()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
|
|
||
| // 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 | ||
| ]; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
…t init values (fixes #20)
While here make the entire color API '@safe'.
Summary by CodeRabbit
New Features
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.