- Fix insert_delete target using .unwrap() causing false-positive crashes on arbitrary::Error (input exhaustion) - Fix fuzz Cargo.toml referencing wrong package name (tui-textarea vs tui-textarea-2) - Add invariant assertions (cursor bounds, lines non-empty, selection validity) to all targets - Add 4 new fuzz targets: undo_redo, selection, wrap_render, search - Expand existing targets with full delete/insert variant coverage, input_without_shortcuts, set_yank_text, set_max_histories, and clear - Add DummyBackend::resize() for variable-width wrap rendering tests
tui-textarea-2
tui-textarea-2 is a simple yet powerful text editor widget like in HTML for <a href="<a href="https://github.com/ratatui/ratatui" data-markdown-generated-content="">https://github.com/ratatui/ratatui</a>">ratatui</a> and <a href="<a href="https://github.com/fdehau/tui-rs" data-markdown-generated-content="">https://github.com/fdehau/tui-rs</a>">tui-rs</a>. Multi-line text editor can be easily put as part of your TUI application.</p> <blockquote> <p dir="auto">Maintained fork notice: this repository is maintained under <code>srothgan/tui-textarea</code> to keep compatibility updates moving (including ratatui 0.30+ support and maintenance fixes).</p> </blockquote> <p dir="auto"><strong>Features:</strong></p> <ul dir="auto"> <li>Multi-line text editor widget with basic operations (insert/delete characters, auto scrolling, ...)</li> <li>Emacs-like shortcuts (<code>C-n</code>/<code>C-p</code>/<code>C-f</code>/<code>C-b</code>, <code>M-f</code>/<code>M-b</code>, <code>C-a</code>/<code>C-e</code>, <code>C-h</code>/<code>C-d</code>, <code>C-k</code>, <code>M-<</code>/<code>M-></code>, ...)</li> <li>Undo/Redo</li> <li>Line number</li> <li>Cursor line highlight</li> <li>Search with regular expressions</li> <li>Text selection</li> <li>Mouse scrolling</li> <li>Yank support. Paste text deleted with <code>C-k</code>, <code>C-j</code>, ...</li> <li>Backend agnostic. <a href="<a href="https://docs.rs/crossterm/latest/crossterm/" data-markdown-generated-content="">https://docs.rs/crossterm/latest/crossterm/</a>">crossterm</a>, <a href="<a href="https://docs.rs/termion/latest/termion/" data-markdown-generated-content="">https://docs.rs/termion/latest/termion/</a>">termion</a>, <a href="<a href="https://docs.rs/termwiz/latest/termwiz/" data-markdown-generated-content="">https://docs.rs/termwiz/latest/termwiz/</a>">termwiz</a>, and your own backend are all supported</li> <li>Multiple textarea widgets in the same screen</li> <li>Support both <a href="<a href="https://github.com/ratatui/ratatui" data-markdown-generated-content="">https://github.com/ratatui/ratatui</a>">ratatui</a> (the fork by community) and <a href="<a href="https://github.com/fdehau/tui-rs" data-markdown-generated-content="">https://github.com/fdehau/tui-rs</a>">tui-rs</a> (the original)</li> </ul> <p dir="auto"><a href="<a href="https://docs.rs/tui-textarea-2/latest/tui_textarea/" data-markdown-generated-content="">https://docs.rs/tui-textarea-2/latest/tui_textarea/</a>">Documentation</a></p> <h2 id="user-content-examples" dir="auto">Examples</h2> <p dir="auto">Running <code>cargo run --example</code> in this repository can demonstrate usage of tui-textarea.</p> <h3 id="user-content-minimalexamplesminimalrs" dir="auto"><a href="./examples/minimal.rs"><code>minimal</code></a></h3> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-sh display">cargo run --example minimal </code></pre></div><p dir="auto">Minimal usage with <a href="<a href="https://docs.rs/crossterm/latest/crossterm/" data-markdown-generated-content="">https://docs.rs/crossterm/latest/crossterm/</a>">crossterm</a> support.</p> <img src="<a href="https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/minimal.gif" data-markdown-generated-content="">https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/minimal.gif</a>" width=539 height=172 alt="minimal example"> <h3 id="user-content-editorexampleseditorrs" dir="auto"><a href="./examples/editor.rs"><code>editor</code></a></h3> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-sh display">cargo run --example editor --features search file.txt </code></pre></div><p dir="auto">Simple text editor to edit multiple files.</p> <img src="<a href="https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/editor.gif" data-markdown-generated-content="">https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/editor.gif</a>" width=560 height=236 alt="editor example"> <h3 id="user-content-single_lineexamplessingle_liners" dir="auto"><a href="./examples/single_line.rs"><code>single_line</code></a></h3> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-sh display">cargo run --example single_line </code></pre></div><p dir="auto">Single-line input form with float number validation.</p> <img src="<a href="https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/single_line.gif" data-markdown-generated-content="">https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/single_line.gif</a>" width=539 height=92 alt="single line example"> <h3 id="user-content-splitexamplessplitrs" dir="auto"><a href="./examples/split.rs"><code>split</code></a></h3> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-sh display">cargo run --example split </code></pre></div><p dir="auto">Two split textareas in a screen and switch them. An example for multiple textarea instances.</p> <img src="<a href="https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/split.gif" data-markdown-generated-content="">https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/split.gif</a>" width=539 height=124 alt="multiple textareas example"> <h3 id="user-content-variableexamplesvariablers" dir="auto"><a href="./examples/variable.rs"><code>variable</code></a></h3> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-sh display">cargo run --example variable </code></pre></div><p dir="auto">Simple textarea with variable height following the number of lines.</p> <h3 id="user-content-vimexamplesvimrs" dir="auto"><a href="./examples/vim.rs"><code>vim</code></a></h3> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-sh display">cargo run --example vim </code></pre></div><p dir="auto">Vim-like modal text editor. Vim emulation is implemented as a state machine.</p> <img src="<a href="https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/vim.gif" data-markdown-generated-content="">https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/vim.gif</a>" width=590 height=156 alt="Vim emulation example"> <h3 id="user-content-popup_placeholderexamplespopup_placeholderrs" dir="auto"><a href="./examples/popup_placeholder.rs"><code>popup_placeholder</code></a></h3> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-sh display">cargo run --example popup_placeholder </code></pre></div><p dir="auto">Popup textarea with a placeholder text.</p> <img src="<a href="https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/placepop.gif" data-markdown-generated-content="">https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/placepop.gif</a>" width=446 height=220 alt="popup textarea with placeholder example"> <h3 id="user-content-passwordexamplespasswordrs" dir="auto"><a href="./examples/password.rs"><code>password</code></a></h3> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-sh display">cargo run --example password </code></pre></div><p dir="auto">Password input form with masking text with ●.</p> <img src="<a href="https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/password.gif" data-markdown-generated-content="">https://raw.githubusercontent.com/rhysd/ss/master/tui-textarea/password.gif</a>" width=589 height=92 alt="password example"> <h3 id="user-content-termionexamplestermionrs" dir="auto"><a href="./examples/termion.rs"><code>termion</code></a></h3> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-sh display">cargo run --example termion --no-default-features --features<span class="o">=</span>termion </code></pre></div><p dir="auto">Minimal usage with <a href="<a href="https://docs.rs/termion/latest/termion/" data-markdown-generated-content="">https://docs.rs/termion/latest/termion/</a>">termion</a> support.</p> <h3 id="user-content-termwizexamplestermwizrs" dir="auto"><a href="./examples/termwiz.rs"><code>termwiz</code></a></h3> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-sh display">cargo run --example termwiz --no-default-features --features<span class="o">=</span>termwiz </code></pre></div><p dir="auto">Minimal usage with <a href="<a href="https://docs.rs/termwiz/latest/termwiz/" data-markdown-generated-content="">https://docs.rs/termwiz/latest/termwiz/</a>">termwiz</a> support.</p> <h3 id="user-content-examples-for-tui-rs-support" dir="auto">Examples for <a href="<a href="https://github.com/fdehau/tui-rs" data-markdown-generated-content="">https://github.com/fdehau/tui-rs</a>">tui-rs</a> support</h3> <p dir="auto">All above examples use <a href="<a href="https://github.com/ratatui/ratatui" data-markdown-generated-content="">https://github.com/ratatui/ratatui</a>">ratatui</a>, but some examples provide tui-rs version. Try <code>tuirs_</code> prefix. In these cases, you need to specify features to use tui-rs and <code>--no-default-features</code> flag explicitly.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-sh display"><span class="c1"># tui-rs version of `minimal` example</span> cargo run --example tuirs_minimal --no-default-features --features<span class="o">=</span>tuirs-crossterm <span class="c1"># tui-rs version of `editor` example</span> cargo run --example tuirs_editor --no-default-features --features<span class="o">=</span>tuirs-crossterm,search file.txt <span class="c1"># tui-rs version of `termion` example</span> cargo run --example tuirs_termion --no-default-features --features<span class="o">=</span>tuirs-termion </code></pre></div><h2 id="user-content-installation" dir="auto">Installation</h2> <p dir="auto">Add <code>tui-textarea</code> crate to dependencies in your <code>Cargo.toml</code>. This enables crossterm backend support by default.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-toml display"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> <span class="nx">ratatui</span> <span class="p">=</span> <span class="s2">"*"</span> <span class="nx">tui-textarea</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">package</span> <span class="p">=</span> <span class="s2">"tui-textarea-2"</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"*"</span> <span class="p">}</span> </code></pre></div><p dir="auto">If you need text search with regular expressions, enable <code>search</code> feature. It adds <a href="<a href="https://docs.rs/regex/latest/regex/" data-markdown-generated-content="">https://docs.rs/regex/latest/regex/</a>">regex crate</a> as dependency.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-toml display"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> <span class="nx">ratatui</span> <span class="p">=</span> <span class="s2">"*"</span> <span class="nx">tui-textarea</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">package</span> <span class="p">=</span> <span class="s2">"tui-textarea-2"</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"*"</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"search"</span><span class="p">]</span> <span class="p">}</span> </code></pre></div><p dir="auto">If you're using ratatui with <a href="<a href="https://docs.rs/termion/latest/termion/" data-markdown-generated-content="">https://docs.rs/termion/latest/termion/</a>">termion</a> or <a href="<a href="https://docs.rs/termwiz/latest/termwiz/" data-markdown-generated-content="">https://docs.rs/termwiz/latest/termwiz/</a>">termwiz</a>, enable the <code>termion</code> or <code>termwiz</code> feature instead of <code>crossterm</code> feature.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-toml display"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> <span class="c"># For termion</span> <span class="nx">ratatui</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"*"</span><span class="p">,</span> <span class="nx">default-features</span> <span class="p">=</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"termion"</span><span class="p">]</span> <span class="p">}</span> <span class="nx">tui-textarea</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">package</span> <span class="p">=</span> <span class="s2">"tui-textarea-2"</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"*"</span><span class="p">,</span> <span class="nx">default-features</span> <span class="p">=</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"termion"</span><span class="p">]</span> <span class="p">}</span> <span class="c"># For termwiz</span> <span class="nx">ratatui</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"*"</span><span class="p">,</span> <span class="nx">default-features</span> <span class="p">=</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"termwiz"</span><span class="p">]</span> <span class="p">}</span> <span class="nx">tui-textarea</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">package</span> <span class="p">=</span> <span class="s2">"tui-textarea-2"</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"*"</span><span class="p">,</span> <span class="nx">default-features</span> <span class="p">=</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"termwiz"</span><span class="p">]</span> <span class="p">}</span> </code></pre></div><p dir="auto">If you're using <a href="<a href="https://github.com/fdehau/tui-rs" data-markdown-generated-content="">https://github.com/fdehau/tui-rs</a>">tui-rs</a> instead of <a href="<a href="https://github.com/ratatui/ratatui" data-markdown-generated-content="">https://github.com/ratatui/ratatui</a>">ratatui</a>, you need to enable features for using tui-rs crate and to disable default features. The following table shows feature names corresponding to the dependencies.</p> <table> <thead> <tr> <th></th> <th>crossterm</th> <th>termion</th> <th>termwiz</th> <th>Your own backend</th> </tr> </thead> <tbody> <tr> <td>ratatui</td> <td><code>crossterm</code> (enabled by default)</td> <td><code>termion</code></td> <td><code>termwiz</code></td> <td><code>no-backend</code></td> </tr> <tr> <td>tui-rs</td> <td><code>tuirs-crossterm</code></td> <td><code>tuirs-termion</code></td> <td>N/A</td> <td><code>tuirs-no-backend</code></td> </tr> </tbody> </table> <p dir="auto">For example, when you want to use the combination of <a href="<a href="https://github.com/fdehau/tui-rs" data-markdown-generated-content="">https://github.com/fdehau/tui-rs</a>">tui-rs</a> and <a href="<a href="https://docs.rs/crossterm/latest/crossterm/" data-markdown-generated-content="">https://docs.rs/crossterm/latest/crossterm/</a>">crossterm</a>,</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-toml display"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> <span class="nx">tui</span> <span class="p">=</span> <span class="s2">"*"</span> <span class="nx">tui-textarea</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">package</span> <span class="p">=</span> <span class="s2">"tui-textarea-2"</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"*"</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"tuirs-crossterm"</span><span class="p">],</span> <span class="nx">default-features</span> <span class="p">=</span> <span class="kc">false</span> <span class="p">}</span> </code></pre></div><p dir="auto">Note that <a href="<a href="https://github.com/ratatui/ratatui" data-markdown-generated-content="">https://github.com/ratatui/ratatui</a>">ratatui</a> support and <a href="<a href="https://github.com/fdehau/tui-rs" data-markdown-generated-content="">https://github.com/fdehau/tui-rs</a>">tui-rs</a> support are exclusive. When you use <a href="<a href="https://github.com/fdehau/tui-rs" data-markdown-generated-content="">https://github.com/fdehau/tui-rs</a>">tui-rs</a> support, you must disable <a href="<a href="https://github.com/ratatui/ratatui" data-markdown-generated-content="">https://github.com/ratatui/ratatui</a>">ratatui</a> support by <code>default-features = false</code>.</p> <p dir="auto">In addition to above dependencies, you also need to install <a href="<a href="https://docs.rs/crossterm/latest/crossterm/" data-markdown-generated-content="">https://docs.rs/crossterm/latest/crossterm/</a>">crossterm</a> or <a href="<a href="https://docs.rs/termion/latest/termion/" data-markdown-generated-content="">https://docs.rs/termion/latest/termion/</a>">termion</a> or <a href="<a href="https://docs.rs/termwiz/latest/termwiz/" data-markdown-generated-content="">https://docs.rs/termwiz/latest/termwiz/</a>">termwiz</a> to initialize your application and to receive key inputs. Note that the dependency versions of <a href="<a href="https://docs.rs/crossterm/latest/crossterm/" data-markdown-generated-content="">https://docs.rs/crossterm/latest/crossterm/</a>">crossterm</a> crate and <a href="<a href="https://docs.rs/termion/latest/termion/" data-markdown-generated-content="">https://docs.rs/termion/latest/termion/</a>">termion</a> crate differ between <a href="<a href="https://github.com/ratatui/ratatui" data-markdown-generated-content="">https://github.com/ratatui/ratatui</a>">ratatui</a> and <a href="<a href="https://github.com/fdehau/tui-rs" data-markdown-generated-content="">https://github.com/fdehau/tui-rs</a>">tui-rs</a>. Please make sure to use the same version that matches the package you are using. For example, <a href="<a href="https://github.com/fdehau/tui-rs" data-markdown-generated-content="">https://github.com/fdehau/tui-rs</a>">tui-rs</a> depends on <a href="<a href="https://docs.rs/crossterm/latest/crossterm/" data-markdown-generated-content="">https://docs.rs/crossterm/latest/crossterm/</a>">crossterm</a> v0.2.5 or <a href="<a href="https://docs.rs/termion/latest/termion/" data-markdown-generated-content="">https://docs.rs/termion/latest/termion/</a>">termion</a> v1.5 where both crates are older than <a href="<a href="https://github.com/ratatui/ratatui" data-markdown-generated-content="">https://github.com/ratatui/ratatui</a>">ratatui</a>'s dependencies.</p> <h2 id="user-content-minimal-usage" dir="auto">Minimal Usage</h2> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">use tui_textarea::TextArea; use crossterm::event::{Event, read}; let mut term = ratatui::Terminal::new(...); // Create an empty `TextArea` instance which manages the editor state let mut textarea = TextArea::default(); // Event loop loop { term.draw(|f| { // Get `ratatui::layout::Rect` where the editor should be rendered let rect = ...; // Render the textarea in terminal screen f.render_widget(&textarea, rect); })?; if let Event::Key(key) = read()? { // Your own key mapping to break the event loop if key.code == KeyCode::Esc { break; } // `TextArea::input` can directly handle key events from backends and update the editor state textarea.input(key); } } // Get text lines as `&[String]` println!("Lines: {:?}", textarea.lines()); </code></pre></div><p dir="auto"><code>TextArea</code> is an instance to manage the editor state. By default, it disables line numbers and highlights cursor line with underline.</p> <p dir="auto"><code>&TextArea</code> reference implements ratatui's <code>Widget</code> trait. Render it on every tick of event loop.</p> <p dir="auto"><code>TextArea::input()</code> receives inputs from tui backends. The method can take key events from backends such as <code>crossterm::event::KeyEvent</code> or <code>termion::event::Key</code> directly if the features are enabled. The method handles default key mappings as well.</p> <p dir="auto">Default key mappings are as follows:</p> <table> <thead> <tr> <th>Mappings</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td><code>Ctrl+H</code>, <code>Backspace</code></td> <td>Delete one character before cursor</td> </tr> <tr> <td><code>Ctrl+D</code>, <code>Delete</code></td> <td>Delete one character next to cursor</td> </tr> <tr> <td><code>Ctrl+M</code>, <code>Enter</code></td> <td>Insert newline</td> </tr> <tr> <td><code>Ctrl+K</code></td> <td>Delete from cursor until the end of line</td> </tr> <tr> <td><code>Ctrl+J</code></td> <td>Delete from cursor until the head of line</td> </tr> <tr> <td><code>Ctrl+W</code>, <code>Alt+H</code>, <code>Alt+Backspace</code></td> <td>Delete one word before cursor</td> </tr> <tr> <td><code>Alt+D</code>, <code>Alt+Delete</code></td> <td>Delete one word next to cursor</td> </tr> <tr> <td><code>Ctrl+U</code></td> <td>Undo</td> </tr> <tr> <td><code>Ctrl+R</code></td> <td>Redo</td> </tr> <tr> <td><code>Ctrl+C</code>, <code>Copy</code></td> <td>Copy selected text</td> </tr> <tr> <td><code>Ctrl+X</code>, <code>Cut</code></td> <td>Cut selected text</td> </tr> <tr> <td><code>Ctrl+Y</code>, <code>Paste</code></td> <td>Paste yanked text</td> </tr> <tr> <td><code>Ctrl+F</code>, <code>→</code></td> <td>Move cursor forward by one character</td> </tr> <tr> <td><code>Ctrl+B</code>, <code>←</code></td> <td>Move cursor backward by one character</td> </tr> <tr> <td><code>Ctrl+P</code>, <code>↑</code></td> <td>Move cursor up by one line</td> </tr> <tr> <td><code>Ctrl+N</code>, <code>↓</code></td> <td>Move cursor down by one line</td> </tr> <tr> <td><code>Alt+F</code>, <code>Ctrl+→</code></td> <td>Move cursor forward by word</td> </tr> <tr> <td><code>Atl+B</code>, <code>Ctrl+←</code></td> <td>Move cursor backward by word</td> </tr> <tr> <td><code>Alt+]</code>, <code>Alt+P</code>, <code>Ctrl+↑</code></td> <td>Move cursor up by paragraph</td> </tr> <tr> <td><code>Alt+[</code>, <code>Alt+N</code>, <code>Ctrl+↓</code></td> <td>Move cursor down by paragraph</td> </tr> <tr> <td><code>Ctrl+E</code>, <code>End</code>, <code>Ctrl+Alt+F</code>, <code>Ctrl+Alt+→</code></td> <td>Move cursor to the end of line</td> </tr> <tr> <td><code>Ctrl+A</code>, <code>Home</code>, <code>Ctrl+Alt+B</code>, <code>Ctrl+Alt+←</code></td> <td>Move cursor to the head of line</td> </tr> <tr> <td><code>Alt+<</code>, <code>Ctrl+Alt+P</code>, <code>Ctrl+Alt+↑</code></td> <td>Move cursor to top of lines</td> </tr> <tr> <td><code>Alt+></code>, <code>Ctrl+Alt+N</code>, <code>Ctrl+Alt+↓</code></td> <td>Move cursor to bottom of lines</td> </tr> <tr> <td><code>Ctrl+V</code>, <code>PageDown</code></td> <td>Scroll down by page</td> </tr> <tr> <td><code>Alt+V</code>, <code>PageUp</code></td> <td>Scroll up by page</td> </tr> </tbody> </table> <p dir="auto">Deleting multiple characters at once saves the deleted text to yank buffer. It can be pasted with <code>Ctrl+Y</code> later.</p> <p dir="auto">If you don't want to use default key mappings, see the 'Advanced Usage' section.</p> <h2 id="user-content-basic-usage" dir="auto">Basic Usage</h2> <h3 id="user-content-create-textarea-instance-with-text" dir="auto">Create <code>TextArea</code> instance with text</h3> <p dir="auto"><code>TextArea</code> implements <code>Default</code> trait to create an editor instance with an empty text.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">let mut textarea = TextArea::default(); </code></pre></div><p dir="auto"><code>TextArea::new()</code> creates an editor instance with text lines passed as <code>Vec<String></code>.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">let mut lines: Vec<String> = ...; let mut textarea = TextArea::new(lines); </code></pre></div><p dir="auto"><code>TextArea</code> implements <code>From<impl Iterator<Item=impl Into<String>>></code>. <code>TextArea::from()</code> can create an editor instance from any iterators whose elements can be converted to <code>String</code>.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">// Create `TextArea` from from `[&str]` let mut textarea = TextArea::from([ "this is first line", "this is second line", "this is third line", ]); // Create `TextArea` from `String` let mut text: String = ...; let mut textarea = TextArea::from(text.lines()); </code></pre></div><p dir="auto"><code>TextArea</code> also implements <code>FromIterator<impl Into<String>></code>. <code>Iterator::collect()</code> can collect strings as an editor instance. This allows to create <code>TextArea</code> reading lines from file efficiently using <code>io::BufReader</code>.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">let file = fs::File::open(path)?; let mut textarea: TextArea = io::BufReader::new(file).lines().collect::<io::Result<_>>()?; </code></pre></div><h3 id="user-content-get-text-contents-from-textarea" dir="auto">Get text contents from <code>TextArea</code></h3> <p dir="auto"><code>TextArea::lines()</code> returns text lines as <code>&[String]</code>. It borrows text contents temporarily.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">let text: String = textarea.lines().join("\n"); </code></pre></div><p dir="auto"><code>TextArea::into_lines()</code> moves <code>TextArea</code> instance into text lines as <code>Vec<String></code>. This can retrieve the text contents without any copy.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">let lines: Vec<String> = textarea.into_lines(); </code></pre></div><p dir="auto">Note that <code>TextArea</code> always contains at least one line. For example, an empty text means one empty line. This is because any text file must end with newline.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">let textarea = TextArea::default(); assert_eq!(textarea.into_lines(), [""]); </code></pre></div><h3 id="user-content-show-line-number" dir="auto">Show line number</h3> <p dir="auto">By default, <code>TextArea</code> does not show line numbers. To enable, set a style for rendering line numbers by <code>TextArea::set_line_number_style()</code>. For example, the following renders line numbers in dark gray background color.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">use ratatui::style::{Style, Color}; let style = Style::default().bg(Color::DarkGray); textarea.set_line_number_style(style); </code></pre></div><h3 id="user-content-configure-cursor-line-style" dir="auto">Configure cursor line style</h3> <p dir="auto">By default, <code>TextArea</code> renders the line at cursor with underline so that users can easily notice where the current line is. To change the style of cursor line, use <code>TextArea::set_cursor_line_style()</code>. For example, the following styles the cursor line with bold text.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">use ratatui::style::{Style, Modifier}; let style = Style::default().add_modifier(Modifier::BOLD); textarea.set_cursor_line_style(style); </code></pre></div><p dir="auto">To disable cursor line style, set the default style as follows:</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">use ratatui::style::{Style, Modifier}; textarea.set_cursor_line_style(Style::default()); </code></pre></div><h3 id="user-content-configure-tab-width" dir="auto">Configure tab width</h3> <p dir="auto">The default tab width is 4. To change it, use <code>TextArea::set_tab_length()</code> method. The following sets 2 to tab width. Typing tab key inserts 2 spaces.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">textarea.set_tab_length(2); </code></pre></div><h3 id="user-content-configure-soft-wrap-mode" dir="auto">Configure soft wrap mode</h3> <p dir="auto">By default, soft wrapping is disabled and long lines are handled by horizontal scrolling. To enable soft wrapping, set <code>TextArea::set_wrap_mode()</code> with one of the supported modes.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">use tui_textarea::WrapMode; textarea.set_wrap_mode(WrapMode::WordOrGlyph); </code></pre></div><p dir="auto">Supported modes:</p> <ul dir="auto"> <li><code>WrapMode::None</code>: Disable soft wrap (default behavior).</li> <li><code>WrapMode::Word</code>: Wrap only at word boundaries.</li> <li><code>WrapMode::Glyph</code>: Wrap at grapheme boundaries.</li> <li><code>WrapMode::WordOrGlyph</code>: Wrap at word boundaries with grapheme fallback for long words.</li> </ul> <h3 id="user-content-configure-max-history-size" dir="auto">Configure max history size</h3> <p dir="auto">By default, past 50 modifications are stored as edit history. The history is used for undo/redo. To change how many past edits are remembered, use <code>TextArea::set_max_histories()</code> method. The following remembers past 1000 changes.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">textarea.set_max_histories(1000); </code></pre></div><p dir="auto">Setting 0 disables undo/redo.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">textarea.set_max_histories(0); </code></pre></div><h3 id="user-content-text-search-with-regular-expressions" dir="auto">Text search with regular expressions</h3> <p dir="auto">To search text in textarea, set a regular expression pattern with <code>TextArea::set_search_pattern()</code> and move cursor with <code>TextArea::search_forward()</code> for forward search or <code>TextArea::search_back()</code> backward search. The regular expression is handled by <a href="<a href="https://docs.rs/regex/latest/regex/" data-markdown-generated-content="">https://docs.rs/regex/latest/regex/</a>"><code>regex</code> crate</a>.</p> <p dir="auto">Text search wraps around the textarea. When searching forward and no match found until the end of textarea, it searches the pattern from start of the file.</p> <p dir="auto">Matches are highlighted in textarea. The text style to highlight matches can be changed with <code>TextArea::set_search_style()</code>. Setting an empty string to <code>TextArea::set_search_pattern()</code> stops the text search.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">// Start text search matching to "hello" or "hi". This highlights matches in textarea but does not move cursor. // `regex::Error` is returned on invalid pattern. textarea.set_search_pattern("(hello|hi)").unwrap(); textarea.search_forward(false); // Move cursor to the next match textarea.search_back(false); // Move cursor to the previous match // Setting empty string stops the search textarea.set_search_pattern("").unwrap(); </code></pre></div><p dir="auto">No UI is provided for text search. You need to provide your own UI to input search query. It is recommended to use another <code>TextArea</code> for search form. To build a single-line input form, see 'Single-line input like <code><input></code> in HTML' in 'Advanced Usage' section below.</p> <p dir="auto"><a href="./examples/editor.rs"><code>editor</code> example</a> implements a text search with search form built on <code>TextArea</code>. See the implementation for working example.</p> <p dir="auto">To use text search, <code>search</code> feature needs to be enabled in your <code>Cargo.toml</code>. It is disabled by default to avoid depending on <code>regex</code> crate until it is necessary.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-toml display"><span class="nx">tui-textarea</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">package</span> <span class="p">=</span> <span class="s2">"tui-textarea-2"</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"*"</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"search"</span><span class="p">]</span> <span class="p">}</span> </code></pre></div><h2 id="user-content-advanced-usage" dir="auto">Advanced Usage</h2> <h3 id="user-content-single-line-input-like-input-in-html" dir="auto">Single-line input like <code><input></code> in HTML</h3> <p dir="auto">To use <code>TextArea</code> for a single-line input widget like <code><input></code> in HTML, ignore all key mappings which inserts newline.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">use crossterm::event::{Event, read}; use tui_textarea::{Input, Key}; let default_text: &str = ...; let default_text = default_text.replace(&['\n', '\r'], " "); // Ensure no new line is contained let mut textarea = TextArea::new(vec![default_text]); // Event loop loop { // ... // Using `Input` is not mandatory, but it's useful for pattern match // Ignore Ctrl+m and Enter. Otherwise handle keys as usual match read()?.into() { Input { key: Key::Char('m'), ctrl: true, alt: false } | Input { key: Key::Enter, .. } => continue, input => { textarea.input(key); } } } let text = textarea.into_lines().remove(0); // Get input text </code></pre></div><p dir="auto">See <a href="./examples/single_line.rs"><code>single_line</code> example</a> for working example.</p> <h3 id="user-content-define-your-own-key-mappings" dir="auto">Define your own key mappings</h3> <p dir="auto">All editor operations are defined as public methods of <code>TextArea</code>. To move cursor, use <code>tui_textarea::CursorMove</code> to notify how to move the cursor.</p> <table> <thead> <tr> <th>Method</th> <th>Operation</th> </tr> </thead> <tbody> <tr> <td><code>textarea.delete_char()</code></td> <td>Delete one character before cursor</td> </tr> <tr> <td><code>textarea.delete_next_char()</code></td> <td>Delete one character next to cursor</td> </tr> <tr> <td><code>textarea.insert_newline()</code></td> <td>Insert newline</td> </tr> <tr> <td><code>textarea.delete_line_by_end()</code></td> <td>Delete from cursor until the end of line</td> </tr> <tr> <td><code>textarea.delete_line_by_head()</code></td> <td>Delete from cursor until the head of line</td> </tr> <tr> <td><code>textarea.delete_word()</code></td> <td>Delete one word before cursor</td> </tr> <tr> <td><code>textarea.delete_next_word()</code></td> <td>Delete one word next to cursor</td> </tr> <tr> <td><code>textarea.clear()</code></td> <td>Clear all text</td> </tr> <tr> <td><code>textarea.undo()</code></td> <td>Undo</td> </tr> <tr> <td><code>textarea.redo()</code></td> <td>Redo</td> </tr> <tr> <td><code>textarea.copy()</code></td> <td>Copy selected text</td> </tr> <tr> <td><code>textarea.cut()</code></td> <td>Cut selected text</td> </tr> <tr> <td><code>textarea.paste()</code></td> <td>Paste yanked text</td> </tr> <tr> <td><code>textarea.start_selection()</code></td> <td>Start text selection</td> </tr> <tr> <td><code>textarea.cancel_selection()</code></td> <td>Cancel text selection</td> </tr> <tr> <td><code>textarea.select_all()</code></td> <td>Select entire text</td> </tr> <tr> <td><code>textarea.custom_highlight(range, style, priority)</code></td> <td>Add a custom highlighted range</td> </tr> <tr> <td><code>textarea.clear_custom_highlight()</code></td> <td>Clear all custom highlights</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::Forward)</code></td> <td>Move cursor forward by one character</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::Back)</code></td> <td>Move cursor backward by one character</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::Up)</code></td> <td>Move cursor up by one line</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::Down)</code></td> <td>Move cursor down by one line</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::WordForward)</code></td> <td>Move cursor forward by word</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::WordEnd)</code></td> <td>Move cursor to next end of word</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::WordBack)</code></td> <td>Move cursor backward by word</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::ParagraphForward)</code></td> <td>Move cursor up by paragraph</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::ParagraphBack)</code></td> <td>Move cursor down by paragraph</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::End)</code></td> <td>Move cursor to the end of line</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::Head)</code></td> <td>Move cursor to the head of line</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::Top)</code></td> <td>Move cursor to top of lines</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::Bottom)</code></td> <td>Move cursor to bottom of lines</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::Jump(row, col))</code></td> <td>Move cursor to (row, col) position</td> </tr> <tr> <td><code>textarea.move_cursor(CursorMove::InViewport)</code></td> <td>Move cursor to stay in the viewport</td> </tr> <tr> <td><code>textarea.set_search_pattern(pattern)</code></td> <td>Set a pattern for text search</td> </tr> <tr> <td><code>textarea.search_forward(match_cursor)</code></td> <td>Move cursor to next match of text search</td> </tr> <tr> <td><code>textarea.search_back(match_cursor)</code></td> <td>Move cursor to previous match of text search</td> </tr> <tr> <td><code>textarea.scroll(Scrolling::PageDown)</code></td> <td>Scroll down the viewport by page</td> </tr> <tr> <td><code>textarea.scroll(Scrolling::PageUp)</code></td> <td>Scroll up the viewport by page</td> </tr> <tr> <td><code>textarea.scroll(Scrolling::HalfPageDown)</code></td> <td>Scroll down the viewport by half-page</td> </tr> <tr> <td><code>textarea.scroll(Scrolling::HalfPageUp)</code></td> <td>Scroll up the viewport by half-page</td> </tr> <tr> <td><code>textarea.scroll((row, col))</code></td> <td>Scroll down the viewport to (row, col) position</td> </tr> </tbody> </table> <p dir="auto">To define your own key mappings, simply call the above methods in your code instead of <code>TextArea::input()</code> method.</p> <p dir="auto">See the <a href="./examples/vim.rs"><code>vim</code> example</a> for working example. It implements more Vim-like key modal mappings.</p> <p dir="auto">If you don't want to use default key mappings, <code>TextArea::input_without_shortcuts()</code> method can be used instead of <code>TextArea::input()</code>. The method only handles very basic operations such as inserting/deleting single characters, tabs, newlines.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">match read()?.into() { // Handle your own key mappings here // ... input => textarea.input_without_shortcuts(input), } </code></pre></div><h3 id="user-content-use-your-own-backend" dir="auto">Use your own backend</h3> <p dir="auto">ratatui and tui-rs allows to make your own backend by implementing <a href="<a href="https://docs.rs/ratatui/latest/ratatui/backend/trait.Backend.html" data-markdown-generated-content="">https://docs.rs/ratatui/latest/ratatui/backend/trait.Backend.html</a>"><code>ratatui::backend::Backend</code></a> trait. tui-textarea supports it as well. Please use <code>no-backend</code> feature for <a href="<a href="https://github.com/ratatui/ratatui" data-markdown-generated-content="">https://github.com/ratatui/ratatui</a>">ratatui</a> or <code>tuirs-no-backend</code> feature for <a href="<a href="https://github.com/fdehau/tui-rs" data-markdown-generated-content="">https://github.com/fdehau/tui-rs</a>">tui-rs</a>. They avoid adding backend crates (crossterm, termion, or termwiz) since you're using your own backend.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-toml display"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> <span class="c"># For ratatui</span> <span class="nx">tui-textarea</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">package</span> <span class="p">=</span> <span class="s2">"tui-textarea-2"</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"*"</span><span class="p">,</span> <span class="nx">default-features</span> <span class="p">=</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"no-backend"</span><span class="p">]</span> <span class="p">}</span> <span class="c"># For tui-rs</span> <span class="nx">tui-textarea</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">package</span> <span class="p">=</span> <span class="s2">"tui-textarea-2"</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"*"</span><span class="p">,</span> <span class="nx">default-features</span> <span class="p">=</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"tuirs-no-backend"</span><span class="p">]</span> <span class="p">}</span> </code></pre></div><p dir="auto"><code>tui_textarea::Input</code> is a type for backend-agnostic key input. What you need to do is converting key event in your own backend into the <code>tui_textarea::Input</code> instance. Then <code>TextArea::input()</code> method can handle the input as other backend.</p> <p dir="auto">In the following example, let's say <code>your_backend::KeyDown</code> is a key event type for your backend and <code>your_backend::read_next_key()</code> returns the next key event.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">// In your backend implementation pub enum KeyDown { Char(char), BS, Del, Esc, // ... } // Return tuple of (key, ctrlkey, altkey) pub fn read_next_key() -> (KeyDown, bool, bool) { // ... } </code></pre></div><p dir="auto">Then you can implement the logic to convert <code>your_backend::KeyDown</code> value into <code>tui_textarea::Input</code> value.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">use tui_textarea::{Input, Key}; use your_backend::KeyDown; fn keydown_to_input(key: KeyDown, ctrl: bool, alt: bool) -> Input { match key { KeyDown::Char(c) => Input { key: Key::Char(c), ctrl, alt }, KeyDown::BS => Input { key: Key::Backspace, ctrl, alt }, KeyDown::Del => Input { key: Key::Delete, ctrl, alt }, KeyDown::Esc => Input { key: Key::Esc, ctrl, alt }, // ... _ => Input::default(), } } </code></pre></div><p dir="auto">For the keys which are not handled by tui-textarea, <code>tui_textarea::Input::default()</code> is available. It returns 'null' key. An editor will do nothing with the key.</p> <p dir="auto">Finally, convert your own backend's key input type into <code>tui_textarea::Input</code> and pass it to <code>TextArea::input()</code>.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">let mut textarea = ...; // Event loop loop { // ... let (key, ctrl, alt) = your_backend::read_next_key(); if key == your_backend::KeyDown::Esc { break; // For example, quit your app on pressing Esc } textarea.input(keydown_to_input(key, ctrl, alt)); } </code></pre></div><h3 id="user-content-put-multiple-textarea-instances-in-screen" dir="auto">Put multiple <code>TextArea</code> instances in screen</h3> <p dir="auto">You don't need to do anything special. Create multiple <code>TextArea</code> instances and render widgets built from each instances.</p> <p dir="auto">The following is an example to put two textarea widgets in application and manage the focus.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">use tui_textarea::{TextArea, Input, Key}; use crossterm::event::{Event, read}; let editors = &mut [ TextArea::default(), TextArea::default(), ]; let mut focused = 0; loop { term.draw(|f| { let rects = ...; for (editor, rect) in editors.iter().zip(rects.into_iter()) { f.render_widget(editor, rect); } })?; match read()?.into() { // Switch focused textarea by Ctrl+S Input { key: Key::Char('s'), ctrl: true, .. } => focused = (focused + 1) % 2; // Handle input by the focused editor input => editors[focused].input(input), } } </code></pre></div><p dir="auto">See <a href="./examples/split.rs"><code>split</code> example</a> and <a href="./examples/editor.rs"><code>editor</code> example</a> for working example.</p> <h3 id="user-content-serializationdeserialization-support" dir="auto">Serialization/Deserialization support</h3> <p dir="auto">This crate optionally supports <a href="<a href="https://crates.io/crates/serde" data-markdown-generated-content="">https://crates.io/crates/serde</a>">serde</a> crate by enabling <code>serde</code> feature.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-toml display"><span class="p">[</span><span class="nx">dependencies</span><span class="p">]</span> <span class="nx">tui-textarea</span> <span class="p">=</span> <span class="p">{</span> <span class="nx">package</span> <span class="p">=</span> <span class="s2">"tui-textarea-2"</span><span class="p">,</span> <span class="nx">version</span> <span class="p">=</span> <span class="s2">"*"</span><span class="p">,</span> <span class="nx">features</span> <span class="p">=</span> <span class="p">[</span><span class="s2">"serde"</span><span class="p">]</span> <span class="p">}</span> </code></pre></div><p dir="auto">Values of the following types can be serialized/deserialized:</p> <ul dir="auto"> <li><code>Key</code></li> <li><code>Input</code></li> <li><code>CursorMove</code></li> <li><code>Scrolling</code></li> </ul> <p dir="auto">Here is an example for deserializing key input from JSON using <a href="<a href="https://crates.io/crates/serde_json" data-markdown-generated-content="">https://crates.io/crates/serde_json</a>">serde_json</a>.</p> <div data-attr-class="WrppH0FvV5BeGkQF:code-block-container code-overflow-scroll"><pre data-attr-class="WrppH0FvV5BeGkQF:code-block"><code data-attr-class="WrppH0FvV5BeGkQF:chroma language-rust,ignore display">use tui_textarea::Input; let json = r#" { "key": { "Char": "a" }, "ctrl": true, "alt": false, "shift": true } "#; let input: Input = serde_json::from_str(json).unwrap(); println!("{input:?}"); // Input { // key: Key::Char('a'), // ctrl: true, // alt: false, // shift: true, // } </code></pre></div><h2 id="user-content-minimum-supported-rust-version" dir="auto">Minimum Supported Rust Version</h2> <p dir="auto">MSRV of this crate is depending on <code>tui</code> crate. Currently MSRV is 1.56.1. Note that <code>ratatui</code> crate requires more recent Rust version.</p> <h2 id="user-content-versioning" dir="auto">Versioning</h2> <p dir="auto">This crate is not reaching v1.0.0 yet. There is no plan to bump the major version for now. Current versioning policy is as follows:</p> <ul dir="auto"> <li>Major: Fixed to 0</li> <li>Minor: Bump on breaking change</li> <li>Patch: Bump on new feature or bug fix</li> </ul> <h2 id="user-content-contributing-to-tui-textarea-2" dir="auto">Contributing to tui-textarea-2</h2> <p dir="auto">This project is developed <a href="<a href="https://github.com/srothgan/tui-textarea" data-markdown-generated-content="">https://github.com/srothgan/tui-textarea</a>">on GitHub</a>.</p> <p dir="auto">For feature requests or bug reports, please <a href="<a href="https://github.com/srothgan/tui-textarea/issues/new" data-markdown-generated-content="">https://github.com/srothgan/tui-textarea/issues/new</a>">create an issue</a>. For submitting patches, please <a href="<a href="https://github.com/srothgan/tui-textarea/pulls" data-markdown-generated-content="">https://github.com/srothgan/tui-textarea/pulls</a>">create a pull request</a>.</p> <p dir="auto">Please read <a href="./CONTRIBUTING.md">CONTRIBUTING.md</a> before reporting an issue or making a PR.</p> <h2 id="user-content-license" dir="auto">License</h2> <p dir="auto">tui-textarea-2 is distributed under <a href="./LICENSE">The MIT License</a>.</p> </body></html>