Compare commits

...

4 Commits

Author SHA1 Message Date
Julien Calixte
f5a0b45f6f feat(editor): format the buffer on save/sync (format_on_save)
Run :fmt in-core before :w/:sync emit their effect, so :sync is
fmt -> save -> commit -> push and :w saves formatted. Gated on the
format_on_save field (default on); the v0.5 .typoena.toml key will drive it.
2026-07-11 19:32:16 +02:00
Julien Calixte
cb3160541d feat(editor,firmware): add :gl fast-forward pull command
Add Effect::Pull and the `:gl` command (fetch + fast-forward only, refuse on
divergence). The editor side is host-tested; the firmware arm is a stub posting
"pull: not wired yet (v0.7)" — the on-device fetch/fast-forward in git_sync is
v0.7 work (only push is wired today).
2026-07-11 19:32:02 +02:00
Julien Calixte
1d7448ba75 feat(editor): edit the : command line with Ctrl-W / Cmd-Backspace
Handle DeleteWord (Ctrl-W) and DeleteLine (Cmd-Backspace) in Command mode:
Ctrl-W drops the previous word, Cmd-Backspace clears the line. Neither exits to
Normal on empty (unlike Backspace), so command editing stays on the line.
2026-07-11 19:30:39 +02:00
Julien Calixte
5d9591e5ea docs: rename roadmap.md to macroplan.md and refresh the plan
The file is the macroplan (plus per-version scope), so rename it to match and
retitle to "Macroplan"; update all inbound links and friendly labels across the
docs. Refresh the plan while here: v0.2 gutter built, :gl pull recorded (v0.7),
command-line editing (v0.4), and the format_on_save pref (v0.5).
2026-07-11 19:27:27 +02:00
13 changed files with 172 additions and 49 deletions

View File

@@ -15,7 +15,7 @@ by user-facing weight. References the terms in this file as canonical.
surface, expressed in this vocabulary.
[`docs/v0.1-mvp-technical.md`](docs/v0.1-mvp-technical.md) — how v0.1 is
built.
[`docs/roadmap.md`](docs/roadmap.md) — per-version scope, where new terms
[`docs/macroplan.md`](docs/macroplan.md) — per-version scope, where new terms
(e.g. multi-file **Buffer** concepts at v0.5) will enter this glossary.
## Language

View File

@@ -9,7 +9,7 @@ runs on it.
> verified in spikes; SD mount and save are now wired into the app binary. No
> release has shipped yet — v0.1's remaining gate is the boot splash and wiring
> git publish (`Ctrl-G` → push) into the app binary. Live per-item status:
> [`docs/roadmap.md`](docs/roadmap.md) · failure write-ups:
> [`docs/macroplan.md`](docs/macroplan.md) · failure write-ups:
> [`docs/postmortems/`](docs/postmortems/README.md).
---
@@ -98,22 +98,22 @@ around, not against:
Releases are frequent, and every version is a usable artifact rather than a
checkpoint. Per-version scope, current `[x]`/`[~]` marks, and the Macroplan
source live in [`docs/roadmap.md`](docs/roadmap.md).
source live in [`docs/macroplan.md`](docs/macroplan.md).
| Version | Theme | One-liner |
| ------------------------------------------------------- | ------------ | -------------------------------------------- |
| [v0.1](docs/roadmap.md#v01--mvp-it-writes-it-pushes--) | MVP | Boots, edits one file, `Ctrl-G` pushes. |
| [v0.2](docs/roadmap.md#v02--vim-navigation--) | Vim nav | Normal/Insert, motions, line numbers. |
| [v0.2.5](docs/roadmap.md#v025--international-input--) | Intl input | US-Intl dead keys: à é ê ç, `'`+space = `'`. |
| [v0.3](docs/roadmap.md#v03--vim-editing--) | Vim edit | `dd yy p`, undo/redo, counts. |
| [v0.4](docs/roadmap.md#v04--visual-mode--ex-commands--) | Visual + ex | `v V`, `:w :q :e` command line. |
| [v0.5](docs/roadmap.md#v05--file-palette--multi-file--) | Files | `Ctrl-P` over `/repo` + `/local`, buffers. |
| [v0.6](docs/roadmap.md#v06--markdown-affordances--) | Markdown | Headings, list continuation, soft-wrap. |
| [v0.7](docs/roadmap.md#v07--search--better-git--) | Search + git | `/` search, `:Gpull`. |
| [v0.8](docs/roadmap.md#v08--power-battery--sleep--) | Power | 18650 + sleep + lid switch. |
| [v0.9](docs/roadmap.md#v09--robustness--) | Robustness | Crash-safe writes, reconnect, settings. |
| [v1.0](docs/roadmap.md#v10--polish--) | Polish | Boot ≤ 3 s, fonts, themes, enclosure, guide. |
| [v1.x](docs/roadmap.md#v1x--stretch--nice-to-have) | Stretch | 10.3″ panel, multi-remote, stats, BLE. |
| [v0.1](docs/macroplan.md#v01--mvp-it-writes-it-pushes--) | MVP | Boots, edits one file, `Ctrl-G` pushes. |
| [v0.2](docs/macroplan.md#v02--vim-navigation--) | Vim nav | Normal/Insert, motions, line numbers. |
| [v0.2.5](docs/macroplan.md#v025--international-input--) | Intl input | US-Intl dead keys: à é ê ç, `'`+space = `'`. |
| [v0.3](docs/macroplan.md#v03--vim-editing--) | Vim edit | `dd yy p`, undo/redo, counts. |
| [v0.4](docs/macroplan.md#v04--visual-mode--ex-commands--) | Visual + ex | `v V`, `:w :q :e` command line. |
| [v0.5](docs/macroplan.md#v05--file-palette--multi-file--) | Files | `Ctrl-P` over `/repo` + `/local`, buffers. |
| [v0.6](docs/macroplan.md#v06--markdown-affordances--) | Markdown | Headings, list continuation, soft-wrap. |
| [v0.7](docs/macroplan.md#v07--search--better-git--) | Search + git | `/` search, `:Gpull`. |
| [v0.8](docs/macroplan.md#v08--power-battery--sleep--) | Power | 18650 + sleep + lid switch. |
| [v0.9](docs/macroplan.md#v09--robustness--) | Robustness | Crash-safe writes, reconnect, settings. |
| [v1.0](docs/macroplan.md#v10--polish--) | Polish | Boot ≤ 3 s, fonts, themes, enclosure, guide. |
| [v1.x](docs/macroplan.md#v1x--stretch--nice-to-have) | Stretch | 10.3″ panel, multi-remote, stats, BLE. |
---

View File

@@ -14,7 +14,7 @@
| [`adr.md`](adr.md) | Architecture Decision Records — the load-bearing technical choices and why. |
| [`v0.1-mvp-product.md`](v0.1-mvp-product.md) | v0.1 product design — boot, type one file, `Ctrl-S` to save, `Ctrl-G` to publish. |
| [`v0.1-mvp-technical.md`](v0.1-mvp-technical.md) | v0.1 technical design — single Rust binary on `esp-idf-rs`, modules, threads, bring-up order. |
| [`roadmap.md`](roadmap.md) | Version-by-version plan; each release is a usable artifact, not a checkpoint. |
| [`macroplan.md`](macroplan.md) | Version-by-version plan; each release is a usable artifact, not a checkpoint. |
| [`hardware.md`](hardware.md) | Part choices for the bench build and the rationale behind them. |
## Quality method

View File

@@ -12,7 +12,7 @@ Format inspired by Michael Nygard's ADR template, kept short on purpose.
[`../CONTEXT.md`](../CONTEXT.md) — project glossary: **Tracked**, **Local**,
**Save**, **Publish**, plus the principles ("writing tool, not sync engine")
that constrain [ADR-010] specifically.
[`roadmap.md`](roadmap.md) — per-version scope (v0.1 → v1.x).
[`macroplan.md`](macroplan.md) — per-version scope (v0.1 → v1.x).
[`v0.1-mvp-product.md`](v0.1-mvp-product.md) — what the v0.1 device must do.
[`v0.1-mvp-technical.md`](v0.1-mvp-technical.md) — how v0.1 is built.
[`qfd.md`](qfd.md) — Quality Function Deployment: requirements → functions →
@@ -180,7 +180,7 @@ as the price of those properties.
do not promise "instant feedback."
- Idle power on e-ink is structurally ~0, which makes the v0.8 battery
sizing exercise straightforward — see [ADR-008] and
[roadmap → v0.8](roadmap.md#v08--power-battery--sleep--).
[macroplan → v0.8](macroplan.md#v08--power-battery--sleep--).
- 10.3" e-ink upgrade path is preserved by keeping the renderer
resolution-agnostic. A _non_-e-ink swap (e.g. Sharp Memory LCD) would
invalidate [ADR-002]'s dirty-rect strategy and force a fresh medium ADR.
@@ -277,7 +277,7 @@ card alone is not enough.
[v0.1 product → provisioning](v0.1-mvp-product.md#provisioning-build-time-dev-only).
- PAT is never logged. Validated in code review.
- Rotation in v0.1 = wipe NVS and re-run setup. Proper rotation UI is v0.9
— see [roadmap → v0.9](roadmap.md#v09--robustness--).
— see [macroplan → v0.9](macroplan.md#v09--robustness--).
- Revisit if we ever want to support multiple remotes per device with
different credentials.
@@ -408,7 +408,7 @@ Sizing a battery before measuring is guessing.
scope is in [v0.1 product → out of scope](v0.1-mvp-product.md#out-of-scope-for-v01).
- We can decide cell capacity from real numbers in v0.8, not specs sheets.
- Lid-close detection / deep sleep slips to v0.8 with the battery — see
[roadmap → v0.8](roadmap.md#v08--power-battery--sleep--).
[macroplan → v0.8](macroplan.md#v08--power-battery--sleep--).
---

View File

@@ -1,9 +1,9 @@
# Roadmap — version details
# Macroplan — version details
Frequent releases. Each version is a usable artifact, not a checkpoint.
This file holds the macro-plan (Macroplan block below) and the per-version
scope. The user-facing requirements and engineering targets each release feeds
into are tracked in [`qfd.md`](qfd.md).
This file holds the `macroplan` source block (below) and the per-version scope.
The user-facing requirements and engineering targets each release feeds into are
tracked in [`qfd.md`](qfd.md).
## Status — synced 2026-07-11
@@ -49,7 +49,7 @@ name = "v0.2 navigation"
start = 2026-06-29
original = 2026-07-20
status = "on-track"
note = "Core work landed early: motions and modes already run; Ctrl-d/u and the UTF-8 buffer landed 2026-07-11; only the line-number gutter remains."
note = "Core essentially complete: motions/modes, Ctrl-d/u, the UTF-8 buffer, and the absolute line-number gutter all landed 2026-07-11 (host-tested). Only Spike 13's on-panel gutter refresh check remains before the release ships."
[[feature]]
name = "v0.2.5 international input"
@@ -76,7 +76,7 @@ note = ": command-line mechanism and :fmt done early; Visual mode not started."
name = "v0.5 palette + multi-file"
start = 2026-09-07
original = 2026-09-28
note = "Also adds the git-tracked .typoena.toml preferences file (save_on_idle, auto_sync cadence, line_numbers) and the palette `>` command mode that edits it live."
note = "Also adds the git-tracked .typoena.toml preferences file (save_on_idle, format_on_save, auto_sync cadence, line_numbers) and the palette `>` command mode that edits it live."
[[feature]]
name = "v0.6 markdown"
@@ -156,8 +156,9 @@ post-ship acceptance checks are power-pull recovery, 1000-word no-drop, and
`env!()` — the git-publish wiring landed with baked config (2026-07-11);
the `typoena.conf` migration itself is deferred to v0.9 (on-device
provisioning).
- [x] Publish on **`:sync`** (the editor's command; the roadmap originally
planned `Ctrl-G`): stage `notes.md` → commit with a timestamp message →
- [x] Publish on **`:sync`** (the editor's command; originally planned as
`Ctrl-G`): format (`:fmt`, when `format_on_save`) → save → stage `notes.md`
→ commit with a timestamp message →
fast-forward `push`; on a rejected push, fetch + reconcile then retry once
(no-op short-circuit when the tree is unchanged). **Wired into the editor and
hardware-verified 2026-07-11** — `firmware::git_sync` opens the SD `/sd/repo`,
@@ -247,7 +248,7 @@ v0.2 UTF-8-correct buffer and the ISO-8859-15 render font. Host-tested.
**Status:** deletes and counts done; **yank/paste, undo/redo, and `.` repeat
remain** — there is no register or recorded-op mechanism yet. The `d`/`c`
operator grammar and text objects landed here ahead of schedule (the roadmap
operator grammar and text objects landed here ahead of schedule (the plan
had scheduled only `dd`/`dw`/`d$`).
- [~] `x dd`, `dw dd d$` (✓); `yy p P` and repeat with `.` remain (need a register / recorded op)
@@ -268,7 +269,10 @@ those keys and gets its own trigger (exact key TBD when Visual lands). View mode
stays — it just frees `v`/`V` for Visual.
- [ ] Visual char (`v`) and line (`V`) modes, `y d c` on selections
- [~] `:` command line: `:w :q :wq :e <path>` (mechanism ✓; these commands remain)
- [~] `:` command line (mechanism ✓; `:w`/`:wq`/`:x` save, `:fmt`/`:sync`/`:gl`
wired; `:e <path>` remains, `:q` deliberately dropped — nothing to quit
to). Command-line editing added 2026-07-11: Ctrl-W deletes the previous
word, Cmd-Backspace clears the line.
- [x] Ahead of schedule / unscheduled: `:fmt` Markdown formatter
(table alignment, blank-line collapse, trailing-whitespace strip)
@@ -297,6 +301,14 @@ buffer-lifecycle risk first).
- [ ] `save_on_idle` (bool, default `true`) — auto-save the current buffer on
the existing idle pause (the ≥ 1 s typing-pause the panel already uses
for its refresh), so `:w` becomes optional rather than required.
- [ ] `format_on_save` (bool, default `true`) — run `:fmt` (table alignment,
blank-line collapse, trailing-whitespace strip) on the buffer before it
is persisted, so `:sync` is **fmt → save → commit → push** and `:w`
saves formatted. Implemented in-core 2026-07-11 (`Editor::format_on_save`,
default on); this key will drive it. **Open question:** with
`save_on_idle` also on, this reformats on every idle pause — reflowing
tables / collapsing blanks mid-session. Consider limiting fmt to
explicit `:w`/`:sync` and leaving the idle auto-save unformatted.
- [ ] `line_numbers` (bool, default `true`) — show the absolute line-number
gutter (built always-on in v0.2). Off reclaims the gutter's columns for
text; the palette `> line numbers: on/off` command toggles it live.
@@ -360,12 +372,17 @@ engine remain (snippets are net-new scope, added 2026-07-08).
- [ ] Starter set: link `[$1]($2)$0`, image `![$1]($2)$0`, fenced code block,
etc.
## v0.7 — Search + better git — [ ]
## v0.7 — Search + better git — [~]
**Status:** not started.
**Status:** the **`:gl` pull command landed in the editor** (2026-07-11,
host-tested) — `Effect::Pull` + a firmware stub; the on-device fetch +
fast-forward is still to build. Search not started.
- [ ] `/` forward search, `n N`
- [ ] `:Gpull` (fetch + fast-forward only; refuse on conflict and surface it)
- [~] `:gl` — pull: fetch + **fast-forward only**, refuse on divergence and
surface it (renamed from the planned `:Gpull`). Editor command +
`Effect::Pull` done 2026-07-11 (host-tested); the git-thread
fetch/fast-forward in `git_sync` remains (only push is wired today).
## v0.8 — Power: battery + sleep — [ ]

View File

@@ -9,7 +9,7 @@
> Notes index: [`README.md`](README.md). Docs index:
> [`../README.md`](../README.md). Backs the boot-time acceptance criterion in
> [`../v0.1-mvp-product.md`](../v0.1-mvp-product.md#acceptance-criteria) and the
> v1.0 goal in [`../roadmap.md`](../roadmap.md). Refresh cost model:
> v1.0 goal in [`../macroplan.md`](../macroplan.md). Refresh cost model:
> [`../tradeoff-curves/epd-refresh-latency.md`](../tradeoff-curves/epd-refresh-latency.md).
## The waterfall

View File

@@ -10,7 +10,7 @@ Scope: v0.1 MVP — see
[`v0.1-mvp-product.md`](v0.1-mvp-product.md) for user-facing scope and
[`v0.1-mvp-technical.md`](v0.1-mvp-technical.md) for implementation —
with the v0.2v1.0 trajectory ([README](../README.md),
[roadmap](roadmap.md)) in mind so we don't paint into a corner. Terminology
[macroplan](macroplan.md)) in mind so we don't paint into a corner. Terminology
(e.g. **Tracked**, **Local**, **Save**, **Publish**) follows the project
glossary at [`../CONTEXT.md`](../CONTEXT.md).
@@ -592,17 +592,17 @@ What a user (= me) values about the device, with importance weights on a
| W1 | Sub-second visible response to typing | 10 | [product → Write](v0.1-mvp-product.md#user-stories), [README → UX](../README.md#ux-boundaries-set-by-the-medium) |
| W2 | **Publishing** is one deliberate action away | 9 | [product → Publish](v0.1-mvp-product.md#user-stories), [CONTEXT → Publish](../CONTEXT.md#user-facing-actions) |
| W3 | Pulling power never corrupts the file | 10 | [product → Recover](v0.1-mvp-product.md#user-stories), [acceptance](v0.1-mvp-product.md#acceptance-criteria) |
| W4 | Provisioning never interrupts a writing session | 7 | [product → Provisioning](v0.1-mvp-product.md#provisioning-build-time-dev-only), [roadmap → v0.9](roadmap.md#v09--robustness--) |
| W4 | Provisioning never interrupts a writing session | 7 | [product → Provisioning](v0.1-mvp-product.md#provisioning-build-time-dev-only), [macroplan → v0.9](macroplan.md#v09--robustness--) |
| W5 | Quick boot to a writing cursor | 6 | [product → acceptance](v0.1-mvp-product.md#acceptance-criteria) (≤ 5 s) |
| W6 | Long sessions without crash / lag / drift | 9 | [product → acceptance](v0.1-mvp-product.md#acceptance-criteria) (1 h soak) |
| W7 | Nothing on the device competes with prose | 8 | [README → vision](../README.md#vision) |
| W8 | The UI never moves except when I move it | 7 | [README → UX](../README.md#ux-boundaries-set-by-the-medium) |
| W9 | Codebase absorbs the planned roadmap without rewrite | 8 | [roadmap](roadmap.md) |
| W9 | Codebase absorbs the planned roadmap without rewrite | 8 | [macroplan](macroplan.md) |
| W10 | I can repair or fork it with hobbyist tools | 5 | [README → vision](../README.md#vision) |
| W11 | Multi-day battery life (v0.8 onward) | 4 | [roadmap → v0.8](roadmap.md#v08--power-battery--sleep--) |
| W12 | Local-only file scope coexists with git scope (v0.5+) | 5 | [README → scopes](../README.md#vision), [roadmap → v0.5](roadmap.md#v05--file-palette--multi-file--) |
| W13 | Typography sets a writing-tool tone — typewriter or developer editor, never gadget | 7 | [roadmap → v1.0](roadmap.md), [README → UX](../README.md#ux-boundaries-set-by-the-medium) |
| W14 | I can carry the device and write away from a desk | 8 | [roadmap → v0.8](roadmap.md#v08--power-battery--sleep--), [README → hardware](../README.md#hardware) |
| W11 | Multi-day battery life (v0.8 onward) | 4 | [macroplan → v0.8](macroplan.md#v08--power-battery--sleep--) |
| W12 | Local-only file scope coexists with git scope (v0.5+) | 5 | [README → scopes](../README.md#vision), [macroplan → v0.5](macroplan.md#v05--file-palette--multi-file--) |
| W13 | Typography sets a writing-tool tone — typewriter or developer editor, never gadget | 7 | [macroplan → v1.0](macroplan.md), [README → UX](../README.md#ux-boundaries-set-by-the-medium) |
| W14 | I can carry the device and write away from a desk | 8 | [macroplan → v0.8](macroplan.md#v08--power-battery--sleep--), [README → hardware](../README.md#hardware) |
---

View File

@@ -8,7 +8,7 @@
>
> They ride on the `epd::Frame` `DrawTarget` and the Spike 5 partial-refresh
> path. Project overview: [`../README.md`](../README.md). Vocabulary:
> [`../CONTEXT.md`](../CONTEXT.md). Release sequence: [`roadmap.md`](roadmap.md).
> [`../CONTEXT.md`](../CONTEXT.md). Release sequence: [`macroplan.md`](macroplan.md).
These prove display/UX risks, not stack risks, so they sit outside the 17
"prove before integration" gate. **Run Spike 8 first:** it partitions the panel
@@ -150,4 +150,4 @@ risk early.
artifact-free full-refresh swap on every buffer change (leaning on Spike
11). Latency is not the risk here; buffer-state and git-staging correctness
are. (Feeds v0.5 multi-file, delete included — now authorized in
[roadmap](roadmap.md#v05--file-palette--multi-file--) v0.5.)
[macroplan](macroplan.md#v05--file-palette--multi-file--) v0.5.)

View File

@@ -3,7 +3,7 @@
> **Decision:** `auto_sync` defaults to **10 min**, and is an *opportunistic,
> rate-limited* push — not a wall-clock timer that wakes the device. See
> [Policy](#policy). Backs the `.typoena.toml` `auto_sync` key in
> [`../roadmap.md`](../roadmap.md) (v0.5), whose runtime timer lands in v0.7 and
> [`../macroplan.md`](../macroplan.md) (v0.5), whose runtime timer lands in v0.7 and
> must respect sleep (v0.8).
>
> Tradeoff-curves index: [`README.md`](README.md). Docs index:

View File

@@ -8,7 +8,7 @@
> [technical design](v0.1-mvp-technical.md) (how it's built) ·
> [ADR log](adr.md) (load-bearing decisions) ·
> [QFD](qfd.md) (requirements ↔ functions ↔ components) ·
> [roadmap](roadmap.md) (where v0.1 sits in the sequence).
> [macroplan](macroplan.md) (where v0.1 sits in the sequence).
## One-line summary

View File

@@ -6,7 +6,7 @@
> Decisions referenced inline point at [`adr.md`](adr.md). Tradeoff weights
> and the critical-performance budget live in [`qfd.md`](qfd.md). Project
> overview: [`../README.md`](../README.md). Release sequence:
> [`roadmap.md`](roadmap.md).
> [`macroplan.md`](macroplan.md).
## Architecture

View File

@@ -83,6 +83,10 @@ pub enum Effect {
/// `:sync` — publish the buffer (save, then git push). The host saves
/// first: publishing an unsaved buffer is meaningless.
Publish,
/// `:gl` — pull from the remote: fetch, then **fast-forward only**. The host
/// refuses (and surfaces) a divergence rather than merging, and never
/// touches local commits. Complements `:sync` (push) as the download half.
Pull,
}
/// A pending operator awaiting a motion or text object (`d`elete / `c`hange).
@@ -123,6 +127,10 @@ pub struct Editor {
/// (save/publish result). Shown until the next keystroke dismisses it
/// (cleared in [`Editor::handle`]); `None` means nothing to show.
notice: Option<String>,
/// Run `:fmt` on the buffer before persisting on `:w`/`:sync`, so `:sync`
/// is fmt → save → commit → push. Defaults on; the v0.5 `.typoena.toml`
/// `format_on_save` key will drive it.
format_on_save: bool,
}
/// One wrapped display line: its text and the buffer offset of its first char.
@@ -146,6 +154,7 @@ impl Editor {
shown_words: 0,
keyboard_present: false,
notice: None,
format_on_save: true,
}
}
@@ -431,7 +440,20 @@ impl Editor {
self.cmdline.clear();
self.mode = Mode::Normal;
}
// Word/line deletes and Tab aren't meaningful on a short command line.
Key::DeleteWord => {
// Readline Ctrl-W: drop trailing spaces, then the word before the
// caret — editing the `:` command line while typing it. Unlike
// Backspace, emptying the line does not cancel back to Normal.
while self.cmdline.ends_with(' ') {
self.cmdline.pop();
}
while !self.cmdline.is_empty() && !self.cmdline.ends_with(' ') {
self.cmdline.pop();
}
}
// Cmd+Backspace: clear the whole command line, staying in Command.
Key::DeleteLine => self.cmdline.clear(),
// Tab isn't meaningful on a short command line.
_ => {}
}
Effect::None
@@ -447,8 +469,21 @@ impl Editor {
self.format_buffer();
Effect::None
}
"w" | "wq" | "x" => Effect::Save,
"sync" => Effect::Publish,
"w" | "wq" | "x" => {
if self.format_on_save {
self.format_buffer();
}
Effect::Save
}
"sync" => {
// fmt → save → commit → push: format in-core here, then the host
// persists and publishes the already-formatted buffer.
if self.format_on_save {
self.format_buffer();
}
Effect::Publish
}
"gl" => Effect::Pull,
_ => Effect::None,
}
}
@@ -1603,6 +1638,35 @@ mod tests {
assert_eq!(command("sync").1, Effect::Publish);
}
#[test]
fn gl_command_signals_pull() {
assert_eq!(command("gl").1, Effect::Pull);
}
#[test]
fn sync_formats_the_buffer_before_publishing() {
// fmt → save → commit → push: `:sync` runs :fmt in-core first (default on).
let mut e = Editor::with_text("hello \nworld".to_string()); // trailing spaces
e.handle(Key::Char(':'));
for c in "sync".chars() {
e.handle(Key::Char(c));
}
let eff = e.handle(Key::Enter);
assert_eq!(eff, Effect::Publish);
assert_eq!(e.text(), "hello\nworld"); // :fmt stripped the trailing whitespace
}
#[test]
fn format_on_save_off_leaves_the_buffer_untouched() {
let mut e = Editor::with_text("hello \nworld".to_string());
e.format_on_save = false;
e.handle(Key::Char(':'));
e.handle(Key::Char('w'));
let eff = e.handle(Key::Enter);
assert_eq!(eff, Effect::Save);
assert_eq!(e.text(), "hello \nworld"); // unchanged when the pref is off
}
#[test]
fn wq_and_x_alias_save_dropping_the_quit() {
assert_eq!(command("wq").1, Effect::Save);
@@ -1758,6 +1822,42 @@ mod tests {
assert_eq!(e.draw(true).bytes().len(), display::FB_BYTES);
}
// ---- Command-line editing (Ctrl-W / Cmd-Backspace while typing `:`) ----
#[test]
fn ctrl_w_deletes_the_last_word_of_the_command_line() {
let mut e = Editor::new();
e.handle(Key::Char(':'));
for c in "sync now".chars() {
e.handle(Key::Char(c));
}
e.handle(Key::DeleteWord);
assert_eq!(e.cmdline, "sync ");
assert_eq!(e.mode(), Mode::Command); // stays on the command line
}
#[test]
fn ctrl_w_on_a_one_word_command_does_not_cancel() {
let mut e = Editor::new();
e.handle(Key::Char(':'));
e.handle(Key::Char('w'));
e.handle(Key::DeleteWord);
assert_eq!(e.cmdline, "");
assert_eq!(e.mode(), Mode::Command); // unlike Backspace, does not exit
}
#[test]
fn cmd_backspace_clears_the_command_line() {
let mut e = Editor::new();
e.handle(Key::Char(':'));
for c in "fmt".chars() {
e.handle(Key::Char(c));
}
e.handle(Key::DeleteLine);
assert_eq!(e.cmdline, "");
assert_eq!(e.mode(), Mode::Command);
}
#[test]
fn text_getter_reflects_edits() {
let e = typed("hello");

View File

@@ -175,6 +175,12 @@ fn main() -> anyhow::Result<()> {
#[cfg(not(feature = "git"))]
log::info!(":sync — saved; light build (no `git` feature) — push skipped");
}
Effect::Pull => {
// `:gl` — fetch + fast-forward from the remote. The on-device
// fetch/fast-forward on the git thread is v0.7 work (git_sync
// only exposes push today), so acknowledge and no-op for now.
ed.set_notice("pull: not wired yet (v0.7)");
}
}
// Keyboard attach/detach feeds the panel's disconnect flag.