migrate input
This commit is contained in:
+84
-28
@@ -18,7 +18,10 @@ repository at `main`, and this workspace's `Cargo.lock` (zed at `4b47ceb`,
|
||||
of dead weight are gone. `history.rs` moved to phase 2 once it turned out its
|
||||
only consumer is `input/state.rs`. No dependency became unused, so the pruning
|
||||
step is a no-op (four dependencies were already unused before this work).
|
||||
- **Phases 2-5: not started.**
|
||||
- **Phase 2: landed.** `input/` runs on base's editing engine. 6,715 lines of
|
||||
engine and history are gone and 306 are written, taking `crates/ui/src/input`
|
||||
from 6,573 lines to 321. Six call sites changed, all named in phase 2 below.
|
||||
- **Phases 3-5: not started.**
|
||||
- One pre-existing, unrelated breakage was found; see
|
||||
[A pre-existing wasm blocker](#a-pre-existing-wasm-blocker).
|
||||
|
||||
@@ -46,7 +49,7 @@ deeper than `ui::<module>::<Item>`:
|
||||
| Module | Items | Consumer files |
|
||||
| --- | --- | --- |
|
||||
| crate root (`Icon`, `IconName`, `h_flex`, `v_flex`, `divider`, `Root`, `TitleBar`, `Sizable`, `Selectable`, `Disableable`, `StyledExt`, `WindowExtension`, `InteractiveElementExt`) | 13 | 17 |
|
||||
| `input` (`InputState`, `Input`, `InputEvent`) | 3 | 10 |
|
||||
| `input` (`InputState`, `Input`, `InputEvent`; plus `TextareaState` and `Textarea` after phase 2) | 3, then 5 | 10 |
|
||||
| `button` (`Button`, `ButtonVariants`) | 2 | 14 |
|
||||
| `dock` (`Panel`, `PanelView`, `DockArea`, `DockItem`, `DockPlacement`, `PanelEvent`, `ClosePanel`) | 7 | 10 |
|
||||
| `notification`, `avatar`, `menu`, `scroll`, `group_box`, `indicator`, `switch`, `modal`, `tooltip` | 12 | 16 |
|
||||
@@ -108,7 +111,7 @@ dragging, both of which are projected.
|
||||
|
||||
| `ui` module | LOC | Plan | `gpui-base` counterpart |
|
||||
| --- | --- | --- | --- |
|
||||
| `input/` (state, element, display_map, rope_ext, mask_pattern, movement, selection, indent, mode, change, cursor, blink_cursor, clear_button) | 6,929 | Replace; keep `ui::input::{Input, InputEvent, InputState}` as the import path | `Input`/`InputState`, `Textarea`/`TextareaState`, `Editor` |
|
||||
| `input/` (input, clear_button) | 6,573 | Replace; keep `ui::input::{Input, InputEvent, InputState}` as the import path. 321 lines remain, and the engine paints itself through `InputEditorStyle` | `InputState`/`TextareaState` (`InputBaseState` in two modes) plus the `InputBase` frame |
|
||||
| `list/` | 1,477 | Delete | GPUI's own `list` (already in use) |
|
||||
| `checkbox.rs` | 312 | Delete | `Checkbox` |
|
||||
| `scroll/` (scrollbar, scrollable, scrollable_mask) | 1,332 | Replace; keep the `ScrollableElement` and `Scrollbar` names | `Scrollbar`, `ScrollableMask` |
|
||||
@@ -120,13 +123,13 @@ dragging, both of which are projected.
|
||||
| `button.rs` | 626 | Skin: base behavior plus coop's existing variant tables | `Button`, `StateStyle` |
|
||||
| `switch.rs` | 287 | Skin | `Switch`, `SwitchTrack`, `SwitchThumb` |
|
||||
| `avatar.rs` | 141 | Skin | `Avatar`, `AvatarImage`, `AvatarFallback` |
|
||||
| `history.rs` | 184 | Defer to phase 2 | `UndoHistory`, not `History`: base's `History` is navigation (back/forward), while `UndoHistory` is the grouped undo/redo with `max_undos`, `group_interval`, `start_grouping`/`end_grouping`, and `set_ignoring` in place of the fork's `pub(crate) ignore` field. Its only consumer is `input/state.rs`, which phase 2 replaces |
|
||||
| `history.rs` | 184 | Delete. Base's input keeps its own `UndoManager`, and `UndoHistory` is a separate public utility the input never touches, so nothing has to be re-based. Its only consumer was `input/state.rs` | — |
|
||||
| `index_path.rs`, `element_ext.rs`, `event.rs`, `focusable.rs` | 156 | Delete | `IndexPath`, `ElementExt`, `InteractiveElementExt`. `FocusableCycle` has no counterpart — base's `FocusableExt` is a different concept (whether a component draws a focus ring) — so it is dropped rather than re-based |
|
||||
| `styled.rs`, `actions.rs`, `animation.rs` | 305 | Keep `ui::StyledExt`, `Size`, and `Sizable` as the app's import. `Selectable`, `Disableable`, and `Collapsible` now come from `gpui_base::component_traits`; the local three-line `h_flex`/`v_flex` wrappers stay rather than delegating to base's identical ones | `styled`, `StateStyle` |
|
||||
| `icon.rs`, `kbd.rs`, `divider.rs`, `skeleton.rs`, `group_box.rs`, `indicator.rs` | 1,023 | Keep; no base equivalent, these are the design system | — |
|
||||
| `menu/` | 2,208 | Keep; base has no menu. Optional later: re-base anchoring and dismissal on `Popup`/`Positioner` | `Popup` (optional) |
|
||||
| `dock/` + `tab/` | 3,356 | Keep for now; see phase 5 | base dock (different contract) |
|
||||
| `root.rs`, `window_ext.rs`, `title_bar.rs` | 965 | Keep; app shell. `Root` continues to host the dialog and toast layers and `focused_input` | — |
|
||||
| `root.rs`, `window_ext.rs`, `title_bar.rs` | 965 | Keep; app shell. `Root` continues to host the dialog and toast layers. Its `focused_input` field and the two `WindowExtension` methods that read it are gone — the only thing that ever set them was the deleted input paint hook, and no crate consumed them | — |
|
||||
|
||||
Roughly 10k lines are removed, 3k are re-expressed as thin skins, and 8k are kept.
|
||||
|
||||
@@ -230,30 +233,82 @@ touched are the two manifests, `ui/src/lib.rs`, `ui/src/styled.rs`, and
|
||||
acceptance — launching the app and walking the settings dialog and chat panel — has to
|
||||
be done by hand and has not been run.
|
||||
|
||||
### Phase 2 — `input/` (the largest single win, ~6.9k lines)
|
||||
### Phase 2 — `input/` (the largest single win) — landed
|
||||
|
||||
The mapping is close to 1:1 with what the app actually uses:
|
||||
`crates/ui/src/input` is three files and 321 lines: a rewritten 299-line `input.rs`, a
|
||||
7-line `mod.rs` that re-exports base, and the untouched 15-line `clear_button.rs`. Deleted:
|
||||
`state.rs`, `element.rs`, `display_map/`, `rope_ext.rs`, `mask_pattern.rs`, `movement.rs`,
|
||||
`selection.rs`, `indent.rs`, `mode.rs`, `change.rs`, `cursor.rs`, `blink_cursor.rs`, and
|
||||
`history.rs` — 6,715 lines.
|
||||
|
||||
| Coop today | `gpui-base` |
|
||||
The names the application imports are unchanged, but two of them are base's now:
|
||||
|
||||
| Coop before | `ui::input` now |
|
||||
| --- | --- |
|
||||
| `InputState::new(window, cx).placeholder(..)` | same |
|
||||
| `.auto_grow(1, 20)` (chat composer) | `TextareaState::auto_grow(2, 8)` with `Textarea` |
|
||||
| `.masked(true)` (nsec, password, key) | `InputState::masked(true)`, `unmask_value()` |
|
||||
| `.set_value(value, window, cx)` | `set_value(value, window, cx)` |
|
||||
| `InputEvent::{Change, PressEnter, Focus, Blur}` | identical variants |
|
||||
| `Input::new(&state).appearance(false)` | coop's `Input` keeps these chrome options |
|
||||
| `InputState`, one struct that became multi-line through `auto_grow`/`multi_line` | `InputState` = `InputBaseState<InputMode>` and `TextareaState` = `InputBaseState<TextareaMode>`; multi-line is a property of the state's kind |
|
||||
| `Input`, one element that rendered whatever kind of state it was given | `Input` for `InputState` and `Textarea` for `TextareaState` — one generic element, two names |
|
||||
| `InputEvent::{Change, PressEnter, Focus, Blur}` | identical |
|
||||
| `history::History` and `HistoryItem` | gone; `Change` keeps no trait impl |
|
||||
|
||||
Known gaps to reconcile here, verified against the 0.6.1 source before starting:
|
||||
`clean_on_escape()`, `set_loading()` (called from `crates/workspace/src/sidebar/mod.rs`),
|
||||
and the `InputEditorStyle` hook that has to be filled from coop tokens. Everything else
|
||||
in `input/` — `display_map`, `rope_ext`, `mask_pattern`, `movement`, `selection`,
|
||||
`indent`, `mode`, `element` — is deleted. Afterwards, `ropey`, `sum_tree`,
|
||||
`lsp-types`, `tree-sitter`, `regex`, `unicode-segmentation`, and `uuid` can probably
|
||||
leave `crates/ui`'s manifest.
|
||||
The styled element is a frame around base's engine rather than the engine itself. Base's
|
||||
`InputBaseState::render` registers the key context, the focus handle, every editing
|
||||
action, the text element and the editor scrollbar, so the coop element no longer carries
|
||||
any of it. What is left is chrome — background, radius, font size, prefix and suffix
|
||||
slots, clear button, mask toggle, loading indicator — plus three projections onto the
|
||||
state:
|
||||
|
||||
Surfaces to re-verify: the chat composer (auto-grow, Enter to send, IME), the subject
|
||||
line, the settings dialog, profile, relay and messaging lists, the import/restore/backup
|
||||
dialogs, and sidebar search.
|
||||
- `set_editor_style(InputEditorStyle)`, filling `foreground`, `muted_foreground`,
|
||||
`selection` and `caret` from `text`, `text_muted`, `selection` and `cursor`. Base
|
||||
resolves any color left transparent from its own palette, and that palette is only a
|
||||
projection of coop's, so every color coop paints with is named rather than left to
|
||||
resolve. The remaining fields stay at base's defaults: coop configures no highlighter,
|
||||
no diagnostics and no gutter.
|
||||
- `set_editor_paddings(Edges<Pixels>)`, for multi-line only, resolved from the same `Size`
|
||||
table the single-line frame applies, through the window's rem size. Base puts that
|
||||
padding on the text element itself so the text, the gutter and the scrollbar share one
|
||||
inset; putting it on the frame *and* passing it here would double it. Passing the
|
||||
frame's own value is also what keeps the scrollbar where the fork drew it.
|
||||
- `set_disabled` and `set_text_align`, replacing the fork's direct writes to `state.size`,
|
||||
`state.disabled` and `state.text_align`.
|
||||
|
||||
`history.rs` folded in as predicted, with one correction: it did not need re-basing at
|
||||
all. Base's engine owns an `UndoManager`, and `gpui_base::UndoHistory` — the grouped
|
||||
undo/redo, not the back/forward `History` — is a separate utility the engine never
|
||||
reaches for. Removing `pub mod history` is safe because nothing outside `crates/ui`
|
||||
referenced it.
|
||||
|
||||
The gaps named before the phase started all resolved in base's favor: `clean_on_escape()`
|
||||
and `set_loading()` both exist in 0.6.1, and `InputEditorStyle` is the third piece.
|
||||
|
||||
**Six call sites changed, and none of them is churn:**
|
||||
|
||||
| Call site | Change | Why |
|
||||
| --- | --- | --- |
|
||||
| `chat_ui` composer | `InputState` → `TextareaState`, `Input::new` → `Textarea::new` | multi-line is the state's kind, not a layout flag |
|
||||
| `workspace`, profile bio | the same, and `.multi_line(true)` is dropped | the same; `auto_grow(3, 8)` is unchanged |
|
||||
| `workspace`, sidebar | `set_loading(status, cx)` → `set_loading(status, window, cx)` | base's signature takes the window |
|
||||
| `workspace`, sidebar | the `.loading` field read → `.presentation().is_loading()` | `loading` is private; `InputPresentation` is the facade for reading it |
|
||||
| `ui::window_ext` | `focused_input` and `has_focused_input` are removed | their only implementation was the deleted paint hook, and no crate consumed them |
|
||||
| `ui::init` | `input::init(cx)` is removed | `gpui_base::init` binds the same keys, and its set is a strict superset |
|
||||
|
||||
Three visible differences survive, all of them base's, none of them a color, radius or
|
||||
spacing value:
|
||||
|
||||
- **The mask character is `•`, not `*`.** Base's `MASK_CHAR` is a private constant, so the
|
||||
fork's `*` cannot be restored. It shows only in masked inputs.
|
||||
- **The caret is `0.85 × line_height` at every size.** The fork scaled it by `Size` (0.75
|
||||
at small, 1.0 at large) from a `size` field base does not have.
|
||||
- **Inputs are tab stops.** Base builds the state's focus handle with `tab_stop(true)`;
|
||||
the fork's frame was not a tab stop, so Tab skipped text fields and now lands on them.
|
||||
|
||||
Two things came along with `InputBase` that the fork's plain `div` did not do: the frame
|
||||
carries the `TextInput` accessibility role, and a left click anywhere in the frame —
|
||||
including the padding outside the text element — focuses the input. The second has to be
|
||||
restated on the frame because base handles its own mouse events on the inner element only.
|
||||
|
||||
Surfaces to re-verify by hand: the chat composer (auto-grow, Enter to send, IME), the
|
||||
profile bio, the subject line, the settings dialog, the relay and messaging lists, the
|
||||
import/restore/backup dialogs, and the sidebar search field.
|
||||
|
||||
### Phase 3 — overlays and feedback
|
||||
|
||||
@@ -298,9 +353,10 @@ There is no UI test suite to lean on, so each phase gets the same treatment:
|
||||
densest single smoke surface (Button, GroupBox, Switch, Input, DropdownMenu,
|
||||
PopupMenuItem), followed by the chat panel and the sidebar.
|
||||
- For phase 4, record before/after screenshots per module.
|
||||
- Keep the call-site diff at zero for phases 1, 3, and 4; if a call site has to change
|
||||
because base has no equivalent (`set_loading` is the known candidate), list it in the
|
||||
pull request.
|
||||
- Keep the call-site diff at zero where the phase claims it — phases 1 and 3–4 do; if a
|
||||
call site has to change because base has no equivalent, list it in the pull request.
|
||||
Phase 2 needed six, tabulated above, and the list is the record of what "no equivalent"
|
||||
turned out to mean in practice.
|
||||
|
||||
### A pre-existing wasm blocker
|
||||
|
||||
@@ -345,7 +401,7 @@ compile for `wasm32-unknown-unknown`".
|
||||
| --- | --- | --- | --- |
|
||||
| 1 | Phase 0: `gpui` moves to the `gpui-pre` package, `gpui_tokio` vendored | root `Cargo.toml`, `Cargo.lock`, `web/Cargo.toml`, new `crates/gpui_tokio`; `crates/state` needed no edit | landed |
|
||||
| 2 | Phase 1: base wiring, `sync_base`, deletions | `crates/theme` | landed |
|
||||
| 3 | Phase 2: input, plus `history.rs` → `UndoHistory` and the `ropey`/`sum_tree`/… pruning | none, or the named gaps | not started |
|
||||
| 3 | Phase 2: input, plus `history.rs` and the `ropey`/`sum_tree`/`lsp-types`/`regex`/`unicode-segmentation`/`tree-sitter` pruning | `crates/workspace`, `crates/chat_ui` (six call sites); no manifest outside `crates/ui` | landed |
|
||||
| 4 | Phase 3: popover, modal, notification, tooltip | none | not started |
|
||||
| 5–10 | Phase 4: one leaf module each | none | not started |
|
||||
| later | Phase 5: dock, as its own plan | `crates/workspace`, `crates/chat_ui` | not started |
|
||||
|
||||
Reference in New Issue
Block a user