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

Skip to content

Conversation

@gdamore
Copy link
Owner

@gdamore gdamore commented Dec 13, 2025

This adds a nice string-aware, grapheme-aware mechansim for writing to the screen. A stateful cursor and a stateful style are used, which then allows write to be used simply.

A range API is also provided, but only works for simple cases that do not use grapheme clusters (however most interfaces don't need grapheme clusters, and indeed many terminal emulators don't treat them properly anyway.)

While here, a number of methods were marked @safe, although this is not comprehensive.

Summary by CodeRabbit

  • New Features

    • Style and position properties to control text output; new write overloads for different string types.
    • Demo now centers text and renders a center marker.
  • Improvements

    • Safer, more robust core APIs and constructors; writing respects current style, position, wrapping and cursor behavior.
  • Tests

    • Expanded unit tests for writing, grapheme/Unicode handling and wrapping.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 13, 2025

Walkthrough

Adds Screen style/position properties and write overloads; extends CellBuffer with style, position, put/write methods and safety annotations; updates TtyScreen to use the new API; updates hello demo to center text via Screen.position and Screen.write.

Changes

Cohort / File(s) Summary
Screen interface
source/dcell/screen.d
Removed setStyle(Style); added @property Style style() const @safe, `@property Style style(const(Style)) `@safe, @property Coord position() const @safe, `@property Coord position(const(Coord)) `@safe, and `void write(string
TtyScreen implementation
source/dcell/ttyscreen.d
Replaced defStyle with style/style_ usage; removed setStyle; added style/position getters/setters and write overloads; updated clear/write/fill paths to use cells.style and cells.position.
CellBuffer and Cell
source/dcell/cell.d
Added public fields Style style; and Coord position;; added put(Grapheme), put(Char) template and generic write(Str) template; imported std.uni; broadened @safe/nothrow/@nogc annotations across methods/constructors/accessors; added/updated tests for put/write and grapheme/wrap behavior.
Hello demo
demos/hello/source/hello.d
Renamed emitStrcenterStr with signature centerStr(Screen s, int y, Style style, string str); replaced per-character centering with setting s.style, s.position, and s.write(str); draws central red 'X' via direct cell assignment.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Demo as Hello Demo
participant Screen as TtyScreen (Screen)
participant Buffer as CellBuffer
participant Term as Terminal/TTY

Demo->>Screen: set position, set style
Demo->>Screen: write("Hello...") (string)
Screen->>Buffer: write(string) (use position, style; wrap if needed)
Buffer-->>Screen: update cells (advance position)
Screen->>Term: render updated cells (flush to terminal)
Note over Buffer,Screen: writes respect style and position state

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review focus:
    • source/dcell/cell.d: newly added put/write methods, unicode grapheme handling, and wide-ranging safety/nogc/nothrow annotations.
    • source/dcell/ttyscreen.d and source/dcell/screen.d: migration from setStyle/defStyle to properties and ensuring write overloads behave consistently (wrapping, cursor advances).
    • demos/hello/source/hello.d: verify demo uses new API correctly and that direct cell assignment for the center 'X' interacts safely with Buffer state.

Possibly related PRs

Poem

🐰 I center words with nimble paws,
I set the style and mind the laws,
A little X to mark the heart,
I write, I hop, the pixels start —
Happy hops for a demo's art!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly describes the main objective: adding convenience methods for writing to screen, and references the fixed issues.
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-put-cellbuf

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: 1

🧹 Nitpick comments (1)
demos/hello/source/hello.d (1)

34-37: Remove redundant position and style assignments.

Lines 34-35 set position and style, but centerStr immediately overwrites both. These assignments have no effect.

Apply this diff:

-    s.position = Coord(size.x / 2 - 9, size.y / 2 - 1);
-    s.style = style;
     centerStr(s, size.y / 2 - 1, style, " Hello There! ");
     centerStr(s, size.y / 2 + 1, def, " Press ESC to exit. ");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cdd593 and a39a14b.

📒 Files selected for processing (4)
  • demos/hello/source/hello.d (2 hunks)
  • source/dcell/cell.d (20 hunks)
  • source/dcell/screen.d (2 hunks)
  • source/dcell/ttyscreen.d (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-12-08T19:10:45.993Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").

Applied to files:

  • source/dcell/ttyscreen.d
🔇 Additional comments (11)
source/dcell/cell.d (7)

16-16: LGTM!

The std.uni import is necessary for the grapheme-aware text handling introduced in the write method.


51-51: LGTM!

Adding nothrow to these simple accessor methods is correct and strengthens the safety guarantees.

Also applies to: 76-76


162-176: LGTM!

The safety annotations on these helper methods are appropriate—they perform simple calculations without unsafe operations, allocations, or exceptions.


444-450: LGTM!

The addition of stateful style and position fields enables the simplified write API mentioned in the PR objectives. The fields will be default-initialized appropriately.


495-498: LGTM!

The character overload correctly delegates to the grapheme version, enabling formatted writer support for simple use cases.


502-508: LGTM!

The grapheme-aware iteration ensures proper handling of complex Unicode, including combining characters and emoji sequences.


623-660: LGTM!

Comprehensive unit tests covering the new functionality including wrapping behavior, double-width character handling, and style application.

source/dcell/screen.d (2)

14-15: LGTM!

These imports support the new write method implementations and string type constraints.


228-245: LGTM!

The interface additions cleanly extend the Screen API with stateful style/position and simplified write methods, aligning with the PR objectives.

demos/hello/source/hello.d (1)

18-23: LGTM!

The refactored centerStr function cleanly demonstrates the new write API, calculating the centered position and using s.write for output.

source/dcell/ttyscreen.d (1)

508-547: LGTM!

The implementation correctly delegates to the underlying CellBuffer, providing a clean implementation of the Screen interface methods.

@gdamore gdamore force-pushed the gdamore-put-cellbuf branch from a39a14b to d03e6c6 Compare December 13, 2025 23:26
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

♻️ Duplicate comments (1)
source/dcell/cell.d (1)

452-488: Previous issue resolved - size_.x now used correctly.

The field access size_.x is now properly used on lines 467 and 481 instead of the incorrect size.x method call flagged in the previous review.

The put(Grapheme) method correctly handles:

  • Zero-width characters (no position change)
  • Single-width characters (advance and wrap)
  • Double-width characters (clear following cell, advance by 2, wrap)
🧹 Nitpick comments (1)
source/dcell/screen.d (1)

14-15: Unused imports in interface file.

std.range and std.traits are imported but don't appear to be used in this interface file. These may have been added in anticipation of future use or copied from the implementation.

Consider removing unused imports:

-import std.range;
-import std.traits;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a39a14b and d03e6c6.

📒 Files selected for processing (4)
  • demos/hello/source/hello.d (2 hunks)
  • source/dcell/cell.d (20 hunks)
  • source/dcell/screen.d (2 hunks)
  • source/dcell/ttyscreen.d (5 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").
📚 Learning: 2025-12-08T19:10:45.993Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").

Applied to files:

  • source/dcell/ttyscreen.d
📚 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/ttyscreen.d
🔇 Additional comments (6)
source/dcell/screen.d (1)

222-240: LGTM - well-designed interface additions.

The new style and position properties with corresponding write overloads provide a clean stateful API for screen writing. The @safe annotations are consistent and appropriate.

demos/hello/source/hello.d (1)

33-34: LGTM - Clean use of new Screen API.

The refactored code demonstrates the new stateful style, position, and write API effectively.

source/dcell/ttyscreen.d (2)

503-542: LGTM - Clean delegation to CellBuffer.

The style, position, and write methods properly delegate to the underlying CellBuffer, maintaining a single source of truth. The @safe annotations are appropriate.


666-668: Correct use of style property for reading.

Reading style to pass to sendColors and sendAttrs works correctly since a copy is sufficient for these read-only operations.

source/dcell/cell.d (2)

500-508: LGTM - Clean write implementation.

The write method properly iterates through graphemes using byGrapheme, ensuring correct handling of combining characters and grapheme clusters. The documentation comment appropriately warns about the put(Char) limitation with formatted writers.


624-660: Comprehensive unit tests for new functionality.

The tests cover key scenarios including:

  • Basic character output and positioning
  • Style application
  • Line wrapping behavior
  • Double-width character handling (full-width yen sign)
  • Placeholder cell for wide characters

This adds a nice string-aware, grapheme-aware mechansim for writing to the
screen.  A stateful cursor and a stateful style are used, which then allows
write to be used simply.

The setStyle method is replaced by just a normal style property.

A range API is also provided, but only works for simple cases that do not use
grapheme clusters (however most interfaces don't need grapheme clusters, and
indeed many terminal emulators don't treat them properly anyway.)

While here, a number of methods were marked `@safe`, although this is not
comprehensive.
@gdamore gdamore force-pushed the gdamore-put-cellbuf branch from 65e90c4 to 18bfa88 Compare December 13, 2025 23:38
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: 0

♻️ Duplicate comments (1)
demos/hello/source/hello.d (1)

18-23: Clean use of the new Screen API.

The centerStr helper demonstrates the new style, position, and write API effectively.

Note: As flagged in a previous review, str.length gives byte count rather than display width, which works correctly here since the demo strings are ASCII. This is acceptable for a demo.

🧹 Nitpick comments (2)
source/dcell/screen.d (1)

221-238: LGTM! Clean interface additions for stateful writing.

The new style and position properties along with the write overloads provide a convenient API for screen writing. The @safe annotations are appropriate.

Consider adding doc comments for the write methods to maintain consistency with other methods in the interface, explaining the behavior (e.g., that position advances as content is written, wrapping behavior).

source/dcell/cell.d (1)

452-488: Wide character handling: second cell style not set.

In the width == 2 case (lines 474-486), the second cell's text is cleared but its style is not updated. If the second cell previously had a different style, it will retain that old style (though it displays nothing). This is likely benign since the cell displays no content, but for consistency, consider also setting the style:

             case 2:
                 position.x++;
                 if (isLegal(position))
                 {
                     cells[index(position)].text = "";
+                    cells[index(position)].style = style;
                 }

This ensures the "placeholder" cell for wide characters has consistent styling if it's ever inspected or if terminal behavior differs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d03e6c6 and 18bfa88.

📒 Files selected for processing (4)
  • demos/hello/source/hello.d (2 hunks)
  • source/dcell/cell.d (20 hunks)
  • source/dcell/screen.d (1 hunks)
  • source/dcell/ttyscreen.d (4 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-12-08T19:10:45.993Z
Learnt from: gdamore
Repo: gdamore/dcell PR: 27
File: source/dcell/ttyscreen.d:763-766
Timestamp: 2025-12-08T19:10:45.993Z
Learning: In source/dcell/ttyscreen.d, the newTtyScreen() factory function intentionally does not accept a term parameter because the TtyScreen constructor handles environment variable resolution internally with an optional term parameter. Users needing to override the terminal type should call the constructor directly: new TtyScreen(newDevTty(), "custom-term").

Applied to files:

  • source/dcell/ttyscreen.d
📚 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/ttyscreen.d
🔇 Additional comments (11)
source/dcell/ttyscreen.d (4)

161-162: LGTM! Style initialization now correctly mutates the stored style.

The initialization now correctly sets cells.style.bg and cells.style.fg directly on the CellBuffer's style field, addressing the previous review feedback.


503-542: LGTM! Clean delegation to CellBuffer.

The property implementations and write methods correctly delegate to the underlying CellBuffer, maintaining a single source of truth for style and position state.


554-554: Potentially unused field style_.

The private field style_ at line 554 appears to be superseded by cells.style for the public API. However, it's still used internally in drawCell (line 755, 774) and clearScreen (line 668) to track the terminal's current style state (what was last sent to the terminal), which is distinct from the buffer's default style (cells.style).

Please verify this is intentional: style_ tracks what the terminal currently displays, while cells.style is the default for new writes. If so, consider adding a comment to clarify the distinction.


666-668: Verify: clearScreen uses buffer style, assigns to terminal style.

This correctly sends the buffer's current style to the terminal and then updates style_ to match. The logic is sound for resetting the terminal state during a clear operation.

demos/hello/source/hello.d (1)

33-34: LGTM!

The usage of centerStr for displaying the two messages is clean and demonstrates the intended API.

source/dcell/cell.d (6)

16-16: LGTM!

The std.uni import is needed for Grapheme and byGrapheme used in the new put and write methods.


51-51: LGTM!

Adding nothrow to text() getter is appropriate since it only returns a field.


76-76: LGTM!

Adding nothrow to width() getter is appropriate since it only returns a field.


444-450: Public fields for mutable state.

The style and position fields are public, which is simple but allows external code to modify them inconsistently. This is acceptable for the current design where these are explicitly documented as stateful cursor properties. The simplicity is appropriate for the use case.


500-508: LGTM! Grapheme-aware string writing.

The write method correctly iterates by grapheme using byGrapheme, ensuring proper handling of grapheme clusters (combining characters, emoji sequences, etc.). This aligns with the PR objective of providing grapheme-aware screen writing.


623-660: Comprehensive unit tests for new functionality.

The tests cover:

  • Basic put operations
  • Position and style state management
  • Wrapping behavior at line boundaries
  • Wide character (full-width yen) handling including the placeholder cell

Good coverage of the new API.

@gdamore gdamore merged commit 5edab4b into main Dec 13, 2025
5 checks passed
@gdamore gdamore deleted the gdamore-put-cellbuf branch December 13, 2025 23:44
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.

2 participants