Compare commits

...

4 Commits

Author SHA1 Message Date
Josh McKinney
720303e806 fix: Align clear() semantics with contract (#2320) 2026-01-13 04:46:06 -08:00
dependabot[bot]
5f0331ec89 build(deps): bump taiki-e/install-action from 2.65.13 to 2.66.1 (#2344)
Bumps
[taiki-e/install-action](https://github.com/taiki-e/install-action) from
2.65.13 to 2.66.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/taiki-e/install-action/releases">taiki-e/install-action's
releases</a>.</em></p>
<blockquote>
<h2>2.66.1</h2>
<ul>
<li>
<p>Update <code>tombi@latest</code> to 0.7.18.</p>
</li>
<li>
<p>Update <code>ubi@latest</code> to 0.9.0.</p>
</li>
</ul>
<h2>2.66.0</h2>
<ul>
<li>
<p>Support <code>mdbook-mermaid-ssr</code>. (<a
href="https://redirect.github.com/taiki-e/install-action/pull/1400">#1400</a>,
thanks <a
href="https://github.com/CommanderStorm"><code>@​CommanderStorm</code></a>)</p>
</li>
<li>
<p>Improve support for Windows with MSYS2 bash.</p>
</li>
<li>
<p>Documentation improvements.</p>
</li>
</ul>
<h2>2.65.16</h2>
<ul>
<li>
<p>Update <code>zola@latest</code> to 0.22.0.</p>
</li>
<li>
<p>Update <code>wasmtime@latest</code> to 40.0.1.</p>
</li>
<li>
<p>Update <code>vacuum@latest</code> to 0.23.2.</p>
</li>
<li>
<p>Update <code>uv@latest</code> to 0.9.24.</p>
</li>
<li>
<p>Update <code>typos@latest</code> to 1.42.0.</p>
</li>
<li>
<p>Update <code>tombi@latest</code> to 0.7.16.</p>
</li>
<li>
<p>Update <code>syft@latest</code> to 1.40.0.</p>
</li>
<li>
<p>Update <code>protoc@latest</code> to 3.33.3.</p>
</li>
<li>
<p>Update <code>prek@latest</code> to 0.2.27.</p>
</li>
<li>
<p>Update <code>mise@latest</code> to 2026.1.1.</p>
</li>
<li>
<p>Update <code>cargo-nextest@latest</code> to 0.9.120.</p>
</li>
<li>
<p>Update <code>cargo-deny@latest</code> to 0.19.0.</p>
</li>
</ul>
<h2>2.65.15</h2>
<ul>
<li>
<p>Update <code>parse-dockerfile@latest</code> to 0.1.3.</p>
</li>
<li>
<p>Update <code>parse-changelog@latest</code> to 0.6.15.</p>
</li>
<li>
<p>Update <code>cargo-llvm-cov@latest</code> to 0.6.23.</p>
</li>
<li>
<p>Update <code>cargo-hack@latest</code> to 0.6.41.</p>
</li>
<li>
<p>Update <code>cargo-minimal-versions@latest</code> to 0.1.35.</p>
</li>
<li>
<p>Update <code>cargo-no-dev-deps@latest</code> to 0.2.20.</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/taiki-e/install-action/blob/main/CHANGELOG.md">taiki-e/install-action's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<p>All notable changes to this project will be documented in this
file.</p>
<p>This project adheres to <a href="https://semver.org">Semantic
Versioning</a>.</p>
<!-- raw HTML omitted -->
<h2>[Unreleased]</h2>
<ul>
<li>
<p>Update <code>protoc@latest</code> to 3.33.4.</p>
</li>
<li>
<p>Update <code>knope@latest</code> to 0.22.0.</p>
</li>
</ul>
<h2>[2.66.1] - 2026-01-11</h2>
<ul>
<li>
<p>Update <code>tombi@latest</code> to 0.7.18.</p>
</li>
<li>
<p>Update <code>ubi@latest</code> to 0.9.0.</p>
</li>
</ul>
<h2>[2.66.0] - 2026-01-10</h2>
<ul>
<li>
<p>Support <code>mdbook-mermaid-ssr</code>. (<a
href="https://redirect.github.com/taiki-e/install-action/pull/1400">#1400</a>,
thanks <a
href="https://github.com/CommanderStorm"><code>@​CommanderStorm</code></a>)</p>
</li>
<li>
<p>Improve support for Windows with MSYS2 bash.</p>
</li>
<li>
<p>Documentation improvements.</p>
</li>
</ul>
<h2>[2.65.16] - 2026-01-10</h2>
<ul>
<li>
<p>Update <code>zola@latest</code> to 0.22.0.</p>
</li>
<li>
<p>Update <code>wasmtime@latest</code> to 40.0.1.</p>
</li>
<li>
<p>Update <code>vacuum@latest</code> to 0.23.2.</p>
</li>
<li>
<p>Update <code>uv@latest</code> to 0.9.24.</p>
</li>
<li>
<p>Update <code>typos@latest</code> to 1.42.0.</p>
</li>
<li>
<p>Update <code>tombi@latest</code> to 0.7.16.</p>
</li>
<li>
<p>Update <code>syft@latest</code> to 1.40.0.</p>
</li>
<li>
<p>Update <code>protoc@latest</code> to 3.33.3.</p>
</li>
<li>
<p>Update <code>prek@latest</code> to 0.2.27.</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3522286d40"><code>3522286</code></a>
Release 2.66.1</li>
<li><a
href="0ed1c967ed"><code>0ed1c96</code></a>
Update changelog</li>
<li><a
href="e69513dca4"><code>e69513d</code></a>
Update <code>tombi@latest</code> to 0.7.18</li>
<li><a
href="4c5e3dc538"><code>4c5e3dc</code></a>
Update <code>ubi@latest</code> to 0.9.0</li>
<li><a
href="e2cc276530"><code>e2cc276</code></a>
Update <code>tombi@latest</code> to 0.7.17</li>
<li><a
href="8fb4e4b842"><code>8fb4e4b</code></a>
Update .deny.toml</li>
<li><a
href="83961fc0a2"><code>83961fc</code></a>
Release 2.66.0</li>
<li><a
href="c761556c39"><code>c761556</code></a>
Update changelog</li>
<li><a
href="f19ed92a93"><code>f19ed92</code></a>
Workaround MSYS2 bash issue</li>
<li><a
href="e53d9e37e3"><code>e53d9e3</code></a>
docs: Clarify &quot;tool is supported&quot; doesn't mean &quot;tool is
trusted or reviewed b...</li>
<li>Additional commits viewable in <a
href="0e76c5c569...3522286d40">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=taiki-e/install-action&package-manager=github_actions&previous-version=2.65.13&new-version=2.66.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 10:27:34 +03:00
dependabot[bot]
eeed03cba2 build(deps): bump EmbarkStudios/cargo-deny-action from 2.0.14 to 2.0.15 (#2345)
Bumps
[EmbarkStudios/cargo-deny-action](https://github.com/embarkstudios/cargo-deny-action)
from 2.0.14 to 2.0.15.
<details>
<summary>Commits</summary>
<ul>
<li><a
href="3fd3802e88"><code>3fd3802</code></a>
Bump to 0.19.0</li>
<li>See full diff in <a
href="76cd80eb77...3fd3802e88">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=EmbarkStudios/cargo-deny-action&package-manager=github_actions&previous-version=2.0.14&new-version=2.0.15)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 10:27:20 +03:00
dependabot[bot]
c3b575e24e build(deps): bump crate-ci/typos from 1.41.0 to 1.42.0 (#2346)
Bumps [crate-ci/typos](https://github.com/crate-ci/typos) from 1.41.0 to
1.42.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/releases">crate-ci/typos's
releases</a>.</em></p>
<blockquote>
<h2>v1.42.0</h2>
<h2>[1.42.0] - 2026-01-07</h2>
<h3>Features</h3>
<ul>
<li>Dictionary updates</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/crate-ci/typos/blob/master/CHANGELOG.md">crate-ci/typos's
changelog</a>.</em></p>
<blockquote>
<h1>Change Log</h1>
<p>All notable changes to this project will be documented in this
file.</p>
<p>The format is based on <a href="https://keepachangelog.com/">Keep a
Changelog</a>
and this project adheres to <a href="https://semver.org/">Semantic
Versioning</a>.</p>
<!-- raw HTML omitted -->
<h2>[Unreleased] - ReleaseDate</h2>
<h2>[1.42.0] - 2026-01-07</h2>
<h3>Features</h3>
<ul>
<li>Dictionary updates</li>
</ul>
<h2>[1.41.0] - 2025-12-31</h2>
<h3>Features</h3>
<ul>
<li>Updated the dictionary with the <a
href="https://redirect.github.com/crate-ci/typos/issues/1431">December
2025</a> changes</li>
</ul>
<h2>[1.40.1] - 2025-12-29</h2>
<h3>Fixes</h3>
<ul>
<li>Treat <code>incrementer</code> and <code>incrementor</code> the same
for now</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Don't correct ITerm2</li>
</ul>
<h2>[1.40.0] - 2025-11-26</h2>
<h3>Features</h3>
<ul>
<li>Updated the dictionary with the <a
href="https://redirect.github.com/crate-ci/typos/issues/1405">November
2025</a> changes</li>
</ul>
<h2>[1.39.2] - 2025-11-13</h2>
<h3>Fixes</h3>
<ul>
<li>Don't offer <code>entry</code> as a correction for
<code>entrys</code></li>
</ul>
<h2>[1.39.1] - 2025-11-12</h2>
<h3>Features</h3>
<ul>
<li>Make <code>--help</code> more vibrant</li>
</ul>
<h2>[1.39.0] - 2025-10-31</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bb4666ad77"><code>bb4666a</code></a>
chore: Release</li>
<li><a
href="6995a8a1d2"><code>6995a8a</code></a>
chore: Release</li>
<li><a
href="e0227ba473"><code>e0227ba</code></a>
docs: Update changelog</li>
<li><a
href="c4054dbccf"><code>c4054db</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1460">#1460</a>
from epage/wiki</li>
<li><a
href="596a0bd11d"><code>596a0bd</code></a>
feat(dict): Extend from misspell's list</li>
<li><a
href="2d459793db"><code>2d45979</code></a>
refactor(misspell): Reformat dict to look like ours</li>
<li><a
href="54e1366637"><code>54e1366</code></a>
feat(dict): Extend from codespell's list</li>
<li><a
href="74e0660315"><code>74e0660</code></a>
feat(dict): Extend from wikipedia's list</li>
<li><a
href="a2568ec213"><code>a2568ec</code></a>
Merge pull request <a
href="https://redirect.github.com/crate-ci/typos/issues/1459">#1459</a>
from epage/update</li>
<li><a
href="c360cf6c3d"><code>c360cf6</code></a>
fix(wikipedia): Update dict</li>
<li>Additional commits viewable in <a
href="5c19779cb5...bb4666ad77">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=crate-ci/typos&package-manager=github_actions&previous-version=1.41.0&new-version=1.42.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)


</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 10:27:05 +03:00
5 changed files with 188 additions and 90 deletions

View File

@@ -37,7 +37,7 @@ jobs:
toolchain: nightly
components: rustfmt
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2
- uses: taiki-e/install-action@0e76c5c569f13f7eb21e8e5b26fe710062b57b62 # v2
- uses: taiki-e/install-action@3522286d40783523f9c7880e33f785905b4c20d0 # v2
with:
tool: taplo-cli
- run: cargo xtask format --check
@@ -51,7 +51,7 @@ jobs:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- uses: crate-ci/typos@5c19779cb52ea50e151f5a10333ccd269227b5ae # master
- uses: crate-ci/typos@bb4666ad77b539a6b4ce4eda7ebb6de553704021 # master
# Check for any disallowed dependencies in the codebase due to license / security issues.
# See <https://github.com/EmbarkStudios/cargo-deny>
@@ -68,7 +68,7 @@ jobs:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- uses: EmbarkStudios/cargo-deny-action@76cd80eb775d7bbbd2d80292136d74d39e1b4918 # v2
- uses: EmbarkStudios/cargo-deny-action@3fd3802e88374d3fe9159b834c7714ec57d6c979 # v2
with:
rust-toolchain: stable
log-level: info
@@ -139,7 +139,7 @@ jobs:
with:
toolchain: stable
components: llvm-tools
- uses: taiki-e/install-action@0e76c5c569f13f7eb21e8e5b26fe710062b57b62 # v2
- uses: taiki-e/install-action@3522286d40783523f9c7880e33f785905b4c20d0 # v2
with:
tool: cargo-llvm-cov
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2
@@ -165,7 +165,7 @@ jobs:
- uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master
with:
toolchain: ${{ matrix.toolchain }}
- uses: taiki-e/install-action@0e76c5c569f13f7eb21e8e5b26fe710062b57b62 # v2
- uses: taiki-e/install-action@3522286d40783523f9c7880e33f785905b4c20d0 # v2
with:
tool: cargo-hack
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2
@@ -204,7 +204,7 @@ jobs:
with:
persist-credentials: false
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2
- uses: taiki-e/install-action@0e76c5c569f13f7eb21e8e5b26fe710062b57b62 # v2
- uses: taiki-e/install-action@3522286d40783523f9c7880e33f785905b4c20d0 # v2
with:
tool: cargo-rdme
- run: cargo xtask readme --check
@@ -226,7 +226,7 @@ jobs:
- uses: dtolnay/install@74f735cdf643820234e37ae1c4089a08fd266d8a # master
with:
crate: cargo-docs-rs
- uses: taiki-e/install-action@0e76c5c569f13f7eb21e8e5b26fe710062b57b62 # v2
- uses: taiki-e/install-action@3522286d40783523f9c7880e33f785905b4c20d0 # v2
with:
tool: cargo-hack
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2
@@ -244,7 +244,7 @@ jobs:
- uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master
with:
toolchain: stable
- uses: taiki-e/install-action@0e76c5c569f13f7eb21e8e5b26fe710062b57b62 # v2
- uses: taiki-e/install-action@3522286d40783523f9c7880e33f785905b4c20d0 # v2
with:
tool: cargo-hack
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2
@@ -265,7 +265,7 @@ jobs:
- uses: dtolnay/rust-toolchain@f7ccc83f9ed1e5b9c81d8a67d7ad1a747e22a561 # master
with:
toolchain: stable
- uses: taiki-e/install-action@0e76c5c569f13f7eb21e8e5b26fe710062b57b62 # v2
- uses: taiki-e/install-action@3522286d40783523f9c7880e33f785905b4c20d0 # v2
with:
tool: cargo-hack
- uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2

View File

@@ -109,19 +109,28 @@ use crate::layout::{Position, Size};
mod test;
pub use self::test::TestBackend;
/// Enum representing the different types of clearing operations that can be performed
/// on the terminal screen.
/// Defines which region of the terminal's visible display area is cleared.
///
/// Clearing operates on character cells in the active display surface. It does not move, hide, or
/// reset the cursor position. If the cursor lies inside the cleared region, the character cell at
/// the cursor position is cleared as well.
///
/// Clearing applies to the terminal's visible display area, not just content previously drawn by
/// Ratatui. No guarantees are made about scrollback, history, or off-screen buffers.
#[derive(Debug, Display, EnumString, Clone, Copy, Eq, PartialEq, Hash)]
pub enum ClearType {
/// Clear the entire screen.
/// Clears all character cells in the visible display area.
All,
/// Clear everything after the cursor.
/// Clears all character cells from the cursor position (inclusive) through the end of the
/// display area.
AfterCursor,
/// Clear everything before the cursor.
/// Clears all character cells from the start of the display area through the cursor position
/// (inclusive).
BeforeCursor,
/// Clear the current line.
/// Clears all character cells in the cursor's current line.
CurrentLine,
/// Clear everything from the cursor until the next newline.
/// Clears all character cells from the cursor position (inclusive) to the end of the current
/// line.
UntilNewLine,
}
@@ -237,7 +246,14 @@ pub trait Backend {
self.set_cursor_position(Position { x, y })
}
/// Clears the whole terminal screen
/// Clears all character cells in the terminal's visible display area.
///
/// This operation preserves the cursor position. If the cursor lies within the cleared
/// region, the character cell at the cursor position is cleared. No guarantees are made about
/// scrollback, history, or off-screen buffers.
///
/// This is equivalent to calling [`clear_region`](Self::clear_region) with
/// [`ClearType::All`].
///
/// # Example
///
@@ -251,7 +267,13 @@ pub trait Backend {
/// ```
fn clear(&mut self) -> Result<(), Self::Error>;
/// Clears a specific region of the terminal specified by the [`ClearType`] parameter
/// Clears a specific region of the terminal's visible display area, as defined by
/// [`ClearType`].
///
/// This operation preserves the cursor position. If the cursor lies within the cleared
/// region, the character cell at the cursor position is cleared. Clearing applies to the
/// active display surface only and does not make guarantees about scrollback, history, or
/// off-screen buffers.
///
/// This method is optional and may not be implemented by all backends. The default
/// implementation calls [`clear`] if the `clear_type` is [`ClearType::All`] and returns an

View File

@@ -288,12 +288,12 @@ impl Backend for TestBackend {
let region = match clear_type {
ClearType::All => return self.clear(),
ClearType::AfterCursor => {
let index = self.buffer.index_of(self.pos.0, self.pos.1) + 1;
let index = self.buffer.index_of(self.pos.0, self.pos.1);
&mut self.buffer.content[index..]
}
ClearType::BeforeCursor => {
let index = self.buffer.index_of(self.pos.0, self.pos.1);
&mut self.buffer.content[..index]
&mut self.buffer.content[..=index]
}
ClearType::CurrentLine => {
let line_start_index = self.buffer.index_of(0, self.pos.1);
@@ -633,7 +633,7 @@ mod tests {
backend.assert_buffer_lines([
"aaaaaaaaaa",
"aaaaaaaaaa",
"aaaa ",
"aaa ",
" ",
" ",
]);
@@ -657,7 +657,7 @@ mod tests {
" ",
" ",
" ",
" aaaaa",
" aaaa",
"aaaaaaaaaa",
]);
}

View File

@@ -1,6 +1,6 @@
use crate::backend::{Backend, ClearType};
use crate::buffer::Buffer;
use crate::layout::Position;
use crate::buffer::{Buffer, Cell};
use crate::layout::{Position, Rect};
use crate::terminal::{Frame, Terminal, Viewport};
impl<B: Backend> Terminal<B> {
@@ -101,41 +101,77 @@ impl<B: Backend> Terminal<B> {
/// - [`Viewport::Inline`]: clears after the viewport's origin, leaving any content above the
/// viewport untouched.
///
/// Current behavior: for [`Viewport::Inline`], clearing runs from the viewport origin through
/// the end of the visible display area, not just the viewport's rectangle. This is an
/// implementation detail rather than a contract; do not rely on it.
///
/// This preserves the cursor position.
///
/// This also resets the "previous" buffer so the next [`Terminal::flush`] redraws the full
/// viewport. [`Terminal::resize`] calls this internally.
///
/// Implementation note: this uses [`ClearType::AfterCursor`] starting at the viewport origin.
pub fn clear(&mut self) -> Result<(), B::Error> {
let original_cursor = self.backend.get_cursor_position()?;
match self.viewport {
Viewport::Fullscreen => self.backend.clear_region(ClearType::All)?,
Viewport::Inline(_) => {
self.backend
.set_cursor_position(self.viewport_area.as_position())?;
// TODO: `ClearType::AfterCursor` is exclusive of the cursor cell in `TestBackend`
// (and in terminals that interpret this as "after" rather than "from"), which can
// leave the viewport origin cell uncleared. Consider switching to a clear that
// includes the cursor cell when fixing clear semantics.
self.backend.clear_region(ClearType::AfterCursor)?;
}
Viewport::Fixed(_) => {
let area = self.viewport_area;
for y in area.top()..area.bottom() {
// TODO: Fixed viewports can start at x > 0 and have a limited width. Clearing
// from x = 0 clears outside the viewport. Consider clearing only within
// `viewport_area` (respecting both x offset and width) when fixing clear
// semantics.
self.backend.set_cursor_position(Position { x: 0, y })?;
// TODO: `ClearType::AfterCursor` is exclusive of the cursor cell in
// `TestBackend`, so the first cell of each cleared row can remain. Consider a
// clear mode that includes the cursor cell when fixing clear semantics.
self.backend.clear_region(ClearType::AfterCursor)?;
}
self.clear_fixed_viewport(area)?;
}
}
self.backend.set_cursor_position(original_cursor)?;
// Reset the back buffer to make sure the next update will redraw everything.
self.buffers[1 - self.current].reset();
Ok(())
}
/// Clears a fixed viewport using terminal clear commands when possible.
///
/// Terminal clear commands can be faster than per-cell updates.
fn clear_fixed_viewport(&mut self, area: Rect) -> Result<(), B::Error> {
if area.is_empty() {
return Ok(());
}
let size = self.backend.size()?;
let is_full_width = area.x == 0 && area.width == size.width;
let ends_at_bottom = area.bottom() == size.height;
if is_full_width && ends_at_bottom {
self.backend.set_cursor_position(area.as_position())?;
self.backend.clear_region(ClearType::AfterCursor)?;
} else if is_full_width {
self.clear_full_width_rows(area)?;
} else {
self.clear_region_cells(area)?;
}
Ok(())
}
/// Clears full-width rows using line clear commands.
///
/// This avoids per-cell writes when the viewport spans the full width.
fn clear_full_width_rows(&mut self, area: Rect) -> Result<(), B::Error> {
for y in area.top()..area.bottom() {
self.backend.set_cursor_position(Position { x: 0, y })?;
self.backend.clear_region(ClearType::CurrentLine)?;
}
Ok(())
}
/// Clears a non-full-width region by writing empty cells directly.
///
/// This is used when line-based clears would affect cells outside the viewport.
fn clear_region_cells(&mut self, area: Rect) -> Result<(), B::Error> {
let clear_cell = Cell::default();
let updates = area.positions().map(|pos| (pos.x, pos.y, &clear_cell));
self.backend.draw(updates)?;
Ok(())
}
}
#[cfg(test)]
@@ -222,83 +258,123 @@ mod tests {
#[test]
fn clear_inline_clears_after_viewport_origin_and_resets_back_buffer() {
// Characterization test:
// The current implementation clears using ClearType::AfterCursor, which is exclusive of
// the cursor cell. This yields somewhat surprising results (the origin cell is left
// untouched). We'll fix the clear semantics later; this test locks down current behavior.
//
// Inline clear is implemented as:
// 1) move the backend cursor to the viewport origin
// 2) call ClearType::AfterCursor once
//
// Note: TestBackend's ClearType::AfterCursor clears *after the cursor position*, keeping
// the cell at the cursor intact, and clears through the end of the screen buffer.
let mut backend = TestBackend::with_lines(["aaa", "bbb", "ccc"]);
backend.set_cursor_position((0, 1)).unwrap();
let mut terminal = Terminal::with_options(
backend,
TerminalOptions {
viewport: Viewport::Inline(1),
},
)
.unwrap();
let mut backend = TestBackend::with_lines([
"before 1 ",
"before 2 ",
"viewport 1",
"viewport 2",
"after 1 ",
"after 2 ",
]);
backend
.set_cursor_position(Position { x: 2, y: 2 })
.unwrap();
let options = TerminalOptions {
viewport: Viewport::Inline(2),
};
let mut terminal = Terminal::with_options(backend, options).unwrap();
terminal
.backend_mut()
.set_cursor_position(Position { x: 2, y: 2 })
.unwrap();
terminal.buffers[1][(2, 1)] = Cell::new("x");
terminal.buffers[1][(2, 2)] = Cell::new("x");
terminal.clear().unwrap();
terminal
.backend()
.assert_buffer_lines(["aaa", "b ", " "]);
// Inline viewport is anchored to the cursor row (y = 2) with height 2. Clear runs from
// the viewport origin through the end of the display, including the rows after it.
terminal.backend().assert_buffer_lines([
"before 1 ",
"before 2 ",
" ",
" ",
" ",
" ",
]);
assert_eq!(
terminal.buffers[1 - terminal.current],
Buffer::empty(terminal.viewport_area)
);
// The inline branch also explicitly sets the cursor to the viewport origin before
// clearing, so the backend cursor ends up at that origin.
assert_eq!(
terminal.backend().cursor_position(),
Position { x: 0, y: 1 }
Position { x: 2, y: 2 }
);
}
#[test]
fn clear_fixed_clears_viewport_rows_and_resets_back_buffer() {
// Characterization test:
// The current implementation clears using ClearType::AfterCursor, which is exclusive of
// the cursor cell. This yields somewhat surprising results (each row's first cell is left
// untouched, and TestBackend clears through the end of the screen). We'll fix the clear
// semantics later; this test locks down current behavior.
//
// Fixed clear is implemented as: for each viewport row, set the cursor to the start of
// the row (x = 0) and call ClearType::AfterCursor.
//
// Note: TestBackend's ClearType::AfterCursor clears from *after the cursor* through the
// end of the screen buffer (not just the current line). That means the first iteration
// clears everything below the viewport's first row too.
let backend = TestBackend::with_lines(["aaa", "bbb", "ccc"]);
let mut terminal = Terminal::with_options(
backend,
TerminalOptions {
viewport: Viewport::Fixed(Rect::new(0, 1, 3, 2)),
},
)
.unwrap();
// For full-width fixed viewports that reach the terminal bottom, clear uses
// ClearType::AfterCursor starting at the viewport origin.
let mut backend = TestBackend::with_lines(["before 1 ", "viewport 1", "viewport 2"]);
backend.set_cursor_position((2, 0)).unwrap();
let options = TerminalOptions {
viewport: Viewport::Fixed(Rect::new(0, 1, 10, 2)),
};
let mut terminal = Terminal::with_options(backend, options).unwrap();
terminal.buffers[1][(2, 1)] = Cell::new("x");
terminal.clear().unwrap();
terminal
.backend()
.assert_buffer_lines(["aaa", "b ", " "]);
.assert_buffer_lines(["before 1 ", " ", " "]);
assert_eq!(
terminal.buffers[1 - terminal.current],
Buffer::empty(terminal.viewport_area)
);
// The fixed branch sets the cursor for each row it processes; after the loop, the cursor
// is left at the start of the last processed row.
assert_eq!(
terminal.backend().cursor_position(),
Position { x: 0, y: 2 }
Position { x: 2, y: 0 }
);
}
#[test]
fn clear_fixed_full_width_not_at_bottom() {
let mut backend =
TestBackend::with_lines(["before 1 ", "viewport 1", "viewport 2", "after 1 "]);
backend.set_cursor_position((1, 0)).unwrap();
let options = TerminalOptions {
viewport: Viewport::Fixed(Rect::new(0, 1, 10, 2)),
};
let mut terminal = Terminal::with_options(backend, options).unwrap();
terminal.clear().unwrap();
terminal.backend().assert_buffer_lines([
"before 1 ",
" ",
" ",
"after 1 ",
]);
assert_eq!(
terminal.backend().cursor_position(),
Position { x: 1, y: 0 }
);
}
#[test]
fn clear_fixed_respects_non_full_width_viewport() {
let mut backend =
TestBackend::with_lines(["before 1 ", "viewport 1", "viewport 2", "after 1 "]);
backend.set_cursor_position((3, 0)).unwrap();
let options = TerminalOptions {
viewport: Viewport::Fixed(Rect::new(1, 1, 3, 2)),
};
let mut terminal = Terminal::with_options(backend, options).unwrap();
terminal.clear().unwrap();
terminal.backend().assert_buffer_lines([
"before 1 ",
"v port 1",
"v port 2",
"after 1 ",
]);
assert_eq!(
terminal.backend().cursor_position(),
Position { x: 3, y: 0 }
);
}
}

View File

@@ -572,7 +572,7 @@ mod tests {
"1111111111",
"2222222222",
"INSERTLINE",
"4 ",
" ",
" ",
" ",
" ",
@@ -639,7 +639,7 @@ mod tests {
"5555555555",
"INSERTED1 ",
"INSERTED2 ",
"8 ",
" ",
" ",
" ",
" ",