Skip to content

Terminal emulator core added (v3 Sprint)#91

Merged
AkshajSinghal merged 4 commits into
TruFoundation:mainfrom
exlier:main
Jul 20, 2026
Merged

Terminal emulator core added (v3 Sprint)#91
AkshajSinghal merged 4 commits into
TruFoundation:mainfrom
exlier:main

Conversation

@exlier

@exlier exlier commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

The current implementation stays lightweight and testable while leaving a clear path to a more complete emulator later.

Crates used for easing future development:

  • vte: strong choice for ANSI/VT escape parsing
  • nu-ansi-term: useful if we want richer prompt/colors later

Closes #81

Summary by CodeRabbit

  • New Features
    • Added a fixed-size terminal emulator with text rendering, Unicode support, cursor movement, and basic control/ANSI handling (newlines, carriage returns, tabs, backspace, clearing, scrolling).
    • Exposed the terminal as part of the library and updated the REPL to use the terminal’s styled prompt.
  • Tests
    • Added unit tests for Unicode rendering, newline/cursor placement, overwriting behavior, and screen clearing (including cursor position).
  • Chores
    • Updated dependencies to support ANSI parsing and terminal rendering utilities.

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a public Terminal emulator with fixed-size screen storage, cursor movement, text and control-sequence handling, scrolling, styled prompting, and tests for rendering and clearing behavior.

Changes

Terminal emulator

Layer / File(s) Summary
Terminal contract and integration
Cargo.toml, src/lib.rs, src/main.rs, src/terminal.rs
Adds terminal dependencies, exposes the module, defines the Terminal API and initialized buffer, and wires the REPL prompt through terminal.prompt().
Text and control-sequence processing
src/terminal.rs
Processes printable text, control characters, wrapping, scrolling, cursor movement, clearing, SGR styles, and selected CSI escape sequences through vte.
Terminal behavior tests
tests/terminal_emulator.rs
Tests Unicode rendering, newline placement, overwriting, cursor gaps, clearing commands, and cursor preservation.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant REPL
  participant Terminal
  participant Parser
  participant Buffer
  REPL->>Terminal: prompt()
  Terminal-->>REPL: styled prompt
  REPL->>Terminal: write(input)
  Terminal->>Parser: parse input bytes
  Parser->>Buffer: dispatch text and CSI events
  Buffer-->>Terminal: update screen and cursor
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly reflects the main change: adding a terminal emulator core for the v3 sprint.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
tests/terminal_emulator.rs (1)

11-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Test name implies cursor motion is tested, but it only exercises newlines.

The test handles_newlines_and_cursor_motion writes standard text and \n, but does not verify any actual horizontal cursor motion (like \b, \t, or CSI H/C/D sequences). Consider expanding this test with cursor motion assertions, or renaming it to accurately reflect its current contents.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/terminal_emulator.rs` around lines 11 - 19, Rename
handles_newlines_and_cursor_motion to accurately describe that it only tests
newline handling, unless you expand it to assert actual horizontal cursor
motion. Keep the existing newline screen assertions unchanged if choosing the
rename.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/terminal.rs`:
- Around line 42-63: Refactor the terminal screen storage and writing logic
around write_char into a fixed character-cell grid so output is placed at
cursor_col rather than appended. Update advance_tab and cursor movement handling
to preserve/render space-filled gaps, and ensure writes overwrite existing cells
while padding rows with spaces when the cursor moves beyond current content;
keep scrolling and screen rendering consistent with the grid representation.
- Around line 155-176: Update clear_to_end and clear_line so erase operations
preserve both cursor_row and cursor_col. For the current row, remove content
from cursor_col through the line’s end rather than clearing the entire row, and
ensure clear_to_end applies the appropriate clearing to subsequent rows. Use a
character-cell-safe representation or bounds handling so truncation never panics
when cursor_col intersects a multi-byte character.
- Around line 185-190: Update parse_cursor_location so splitting on ';'
preserves empty parameters instead of filtering them out. Parse each positional
value independently, treating an empty or missing row/column as the default
value 1 before saturating subtraction, so sequences such as ";10" produce row 1
and column 10.
- Around line 71-78: Update the CSI parsing loop around chars.next() so only
bytes in the final-byte range 0x40..=0x7E set final_byte and terminate parsing;
continue collecting parameter and private-marker bytes in 0x20..=0x3F, including
'?' and ';', so sequences such as CSI ? 25 h remain intact.

---

Nitpick comments:
In `@tests/terminal_emulator.rs`:
- Around line 11-19: Rename handles_newlines_and_cursor_motion to accurately
describe that it only tests newline handling, unless you expand it to assert
actual horizontal cursor motion. Keep the existing newline screen assertions
unchanged if choosing the rename.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 2ed20d46-b78c-4cd7-946a-c95c12f0f8f8

📥 Commits

Reviewing files that changed from the base of the PR and between bc11eb3 and 08412ff.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml
  • src/lib.rs
  • src/terminal.rs
  • tests/terminal_emulator.rs

Comment thread src/terminal.rs
Comment thread src/terminal.rs Outdated
Comment thread src/terminal.rs
Comment thread src/terminal.rs Outdated

@exlier exlier left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TruShell has been upgraded

The shell now uses the requested crates to build a stronger terminal foundation:

  • vte for VT/ANSI escape parsing
  • nu-ansi-term for a richer, colorized prompt

What changed

  • Added the new dependencies in Cargo.toml
  • Implemented a terminal buffer and escape-sequence handling in terminal.rs
  • Switched the REPL prompt in main.rs to a styled cyan prompt
  • Added regression coverage in terminal_emulator.rs

Verification

I verified the change by running:

cargo test --tests

@exlier

exlier commented Jul 20, 2026

Copy link
Copy Markdown
Contributor Author

What Changed:

  • Updated the terminal buffer in terminal.rs to use a fixed character-cell grid for cursor-based output instead of appending to row strings.
  • Made writes overwrite existing cells and preserve space-filled gaps so cursor movement and rendering stay consistent.
  • Updated clear-line and clear-to-end behavior so they erase from the current cursor column onward while preserving the cursor position and row.
  • Added regression tests in terminal_emulator.rs for overwrite, gap preservation, and cursor-preserving erase behavior.
  • Verified the changes by running:
    • cargo test --test terminal_emulator
    • Result: 4 passed, 0 failed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/terminal.rs (2)

71-85: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Fix line wrapping logic.

Because cursor_col is aggressively clamped to self.width - 1 after every character, the wrap condition self.cursor_col + width > self.width is mathematically impossible to reach. Characters at the end of a line will indefinitely overwrite the last cell without ever triggering a newline.

To fix this, allow cursor_col to increment up to self.width (representing the delayed-wrap state), and trigger the newline before writing the next character. The if self.cursor_row >= self.height check here is also redundant because newline() already handles scrolling.

🐛 Proposed fix
             _ => {
                 let width = 1usize;
-                if self.cursor_col + width > self.width {
+                if self.cursor_col >= self.width {
                     self.newline();
                 }
 
-                if self.cursor_row >= self.height {
-                    self.scroll_up();
-                }
-
                 let row = &mut self.rows[self.cursor_row];
                 row[self.cursor_col] = ch;
-                self.cursor_col = (self.cursor_col + width).min(self.width.saturating_sub(1));
+                self.cursor_col += width;
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/terminal.rs` around lines 71 - 85, Update the character-writing branch in
the terminal rendering logic to preserve a delayed-wrap state: allow cursor_col
to advance through self.width instead of clamping to self.width - 1, so the
existing wrap check triggers newline before writing the next character. Remove
the redundant cursor_row bounds check and rely on newline() to handle scrolling,
while preserving row writes for in-bounds cursor positions.

96-103: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve the character under the cursor on backspace.

Standard terminal emulators treat the backspace character (\b or 0x08) strictly as a cursor movement command. It moves the cursor to the left but does not erase the character. Destructive backspace is typically handled by programs using the sequence \b \b. Overwriting the cell with a space breaks standard terminal expectations.

🐛 Proposed fix
     fn backspace(&mut self) {
         if self.cursor_col > 0 {
             self.cursor_col -= 1;
-            if let Some(row) = self.rows.get_mut(self.cursor_row) {
-                row[self.cursor_col] = ' ';
-            }
         }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/terminal.rs` around lines 96 - 103, Update Terminal::backspace so it only
moves cursor_col left when the cursor is not at the beginning, and remove the
row mutation that replaces the underlying character with a space. Preserve the
character under the cursor while retaining the existing boundary behavior.
🧹 Nitpick comments (1)
src/terminal.rs (1)

196-200: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Simplify the closure to fix a Clippy warning.

♻️ Proposed fix
         let params: Vec<i64> = params
             .iter()
-            .flat_map(|param| param.iter().copied().map(|value| i64::from(value)))
+            .flat_map(|param| param.iter().copied().map(i64::from))
             .collect();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/terminal.rs` around lines 196 - 200, In csi_dispatch, simplify the nested
flat_map closure used to convert Params values into Vec<i64> so it satisfies
Clippy, preserving the existing flattening and i64 conversion behavior.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/terminal.rs`:
- Around line 111-119: Update clear_screen to erase all cell contents without
modifying cursor_row or cursor_col; remove the cursor reset so the cursor
remains exactly at its position before the clear.
- Around line 218-234: Update the cursor movement handlers for commands A, B, C,
and D to normalize an explicit zero parameter to an amount of 1, while retaining
1 as the default when the parameter is omitted. Preserve the existing saturating
and boundary-clamping behavior in each movement branch.

---

Outside diff comments:
In `@src/terminal.rs`:
- Around line 71-85: Update the character-writing branch in the terminal
rendering logic to preserve a delayed-wrap state: allow cursor_col to advance
through self.width instead of clamping to self.width - 1, so the existing wrap
check triggers newline before writing the next character. Remove the redundant
cursor_row bounds check and rely on newline() to handle scrolling, while
preserving row writes for in-bounds cursor positions.
- Around line 96-103: Update Terminal::backspace so it only moves cursor_col
left when the cursor is not at the beginning, and remove the row mutation that
replaces the underlying character with a space. Preserve the character under the
cursor while retaining the existing boundary behavior.

---

Nitpick comments:
In `@src/terminal.rs`:
- Around line 196-200: In csi_dispatch, simplify the nested flat_map closure
used to convert Params values into Vec<i64> so it satisfies Clippy, preserving
the existing flattening and i64 conversion behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4670c25e-ba8f-460c-be5c-a5ff720d4687

📥 Commits

Reviewing files that changed from the base of the PR and between 08412ff and 846687f.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml
  • src/main.rs
  • src/terminal.rs
  • tests/terminal_emulator.rs

Comment thread src/terminal.rs
Comment thread src/terminal.rs
@AkshajSinghal
AkshajSinghal merged commit 3986eca into TruFoundation:main Jul 20, 2026
2 checks passed
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.

Terminal emulator core (VT/Unicode)

2 participants