Open
Conversation
Remove the native curses gem dependency and replace it with a pure Ruby Terminal module that talks directly to the terminal via ANSI/xterm escape sequences. This eliminates the C extension dependency, simplifying installation and improving portability. New Terminal module provides: - Screen init/teardown via ANSI escape sequences - Differential screen updates (virtual vs physical buffer diffing) - Escape sequence input parser (CSI, SS3, UTF-8) - Terminal::Window and Terminal::Pad matching the curses API surface - SIGWINCH handling for terminal resize - Suspend/resume support Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
noutrefresh now records the cursor's screen coordinates via Terminal.set_cursor, and doupdate emits a cursor-move escape sequence to that position before showing the cursor. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace IO.console_size with a direct ioctl call using TIOCGWINSZ, which reads the current window size from the kernel and gives accurate results. Falls back to LINES/COLUMNS env vars if the ioctl fails. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three related causes: 1. The SIGWINCH handler called \e[2J while the terminal's SGR state could be anything (e.g. the modeline's reverse-video). \e[2J fills with the current background, so cleared cells showed the modeline face instead of the default background. Fixed by emitting \e[0m before \e[2J. 2. flush_diff skips cells that match the physical model. The physical screen was initialised with blank spaces (default attrs), matching the virtual screen's own spaces, so those cells were never re-rendered — they kept the wrong background left by the tainted \e[2J. Fixed by initialising the post-resize physical screen with a NUL sentinel char that never matches any real virtual cell, forcing a complete re-render. 3. After doupdate the terminal could be left in the last face's SGR state (e.g. reverse-video from the modeline). Fixed by always emitting \e[0m after the cursor-positioning sequence in doupdate. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR replaces the Curses-based rendering/input layer with a pure-Ruby ANSI terminal backend, updating core windowing logic and tests to use the new Textbringer::Terminal APIs.
Changes:
- Remove the
cursesgem runtime dependency and route windowing/rendering through a newTextbringer::Terminalimplementation. - Add terminal primitives for attributes, input decoding, screen buffering, windows, and pads (ANSI diff-based rendering + escape-sequence key parsing).
- Update/extend test suite to cover the new terminal input and screen buffer behavior, and migrate existing Curses-coupled assertions to Terminal equivalents.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| textbringer.gemspec | Drops the curses runtime dependency. |
| lib/textbringer.rb | Ensures the new terminal layer is loaded by default. |
| lib/textbringer/window.rb | Switches Window implementation from Curses to Textbringer::Terminal APIs and key name mapping. |
| lib/textbringer/floating_window.rb | Replaces Curses sizing/pad usage with Terminal sizing/pads. |
| lib/textbringer/face.rb | Replaces Curses color-pair + attribute constants with Terminal equivalents. |
| lib/textbringer/color.rb | Replaces Curses color constants with Terminal color constants. |
| lib/textbringer/commands/misc.rb | Implements suspend/resume by closing and reinitializing the terminal and forcing redraw/resize. |
| lib/textbringer/commands/lsp.rb | Replaces Curses.cols with Terminal.cols for signature window sizing. |
| lib/textbringer/terminal.rb | New ANSI terminal driver: raw mode, alt screen, SIGWINCH handling, diff flush, cursor management. |
| lib/textbringer/terminal/attributes.rb | New attribute + color constants and SGR generation. |
| lib/textbringer/terminal/input.rb | New escape-sequence-based input reader with keycode mapping. |
| lib/textbringer/terminal/screen_buffer.rb | New virtual/physical screen buffers and diff-to-ANSI output generator. |
| lib/textbringer/terminal/window.rb | New window abstraction backed by ScreenBuffer with noutrefresh/get_char. |
| lib/textbringer/terminal/pad.rb | New pad abstraction backed by ScreenBuffer with rectangular noutrefresh. |
| test/test_helper.rb | Reworks test fakes/stubs from Curses to Terminal and swaps fake Window/Pad constants. |
| test/textbringer/test_window.rb | Updates key/input + default color tests to use Terminal keycodes/default colors. |
| test/textbringer/test_face.rb | Updates attribute assertions to use Terminal constants. |
| test/textbringer/test_color.rb | Updates color assertions and color-count manipulation to Terminal. |
| test/textbringer/commands/test_buffers.rb | Updates special key injection to Terminal keycodes. |
| test/textbringer/terminal/test_screen_buffer.rb | Adds direct unit tests for ScreenBuffer/Cell and diff output. |
| test/textbringer/terminal/test_input.rb | Adds direct unit tests for escape-sequence parsing and key mappings. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+32
to
+36
| # Query terminal size | ||
| update_size | ||
| # Set up raw mode | ||
| @old_stty = `stty -g`.chomp | ||
| system("stty raw -echo -icanon -isig") |
| @input_reader = nil | ||
| @lines = 24 | ||
| @cols = 80 | ||
| @old_tio = nil |
| else | ||
| # Alt + character | ||
| if next_byte < 0x80 | ||
| ch = next_byte.chr |
Comment on lines
+231
to
+234
| # Push a resize event | ||
| @input_reader&.instance_variable_get(:@buf)&.push( | ||
| Input::KEY_RESIZE | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.