mirror of
https://github.com/l0ng-ai/tty7.git
synced 2026-09-21 16:02:20 +00:00
`--enter` is documented as sugar for `--key enter`, but the send dispatch counted only `args.keys`, so `tty7 send %42 --enter` answered "needs TEXT ... or a --key to press" and pressed nothing. The key list is now built before the dispatch and the dispatch counts it, so a marked address with `--enter` and nothing else runs what the pane already has typed, and a bare `send --enter` presses Enter where the caller sits. An unmarked id is deliberately left out of that promotion. #567 made the address slot take bare ids, and `send 83 --key C-c` addressing pane 83 is fine because `--key` says "press this" and nothing else. `--enter` does not: `send 2 --enter` reads at least as much like typing 2 into your own pane and running it, and turning it into a keystroke at pane 2 would be the silent retarget #567 spent its diff closing. It stays a loud error, now naming both spellings (`send %83 --enter`, `send %PANE 83 --enter`) rather than only the typing one. The reference, the bundled skill reference, `send --help` and the `--enter` help all said the old thing in slightly different words; they now say the same thing as each other and as the code. Co-authored-by: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com>
This commit is contained in:
@@ -140,6 +140,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
the arguments that need it, and a value whose quotes do not close is refused
|
||||
with an explanation under the input rather than saved as fragments. A path
|
||||
spelled with backslashes still means itself. (#551)
|
||||
- **`tty7 send --enter` now presses Enter when there is nothing to type** —
|
||||
`--enter` is shorthand for `--key enter`, but it was never counted as a key,
|
||||
so `tty7 send %42 --enter` answered "needs TEXT … or a --key to press"
|
||||
instead of running what pane 42 already had typed. It counts now, with an
|
||||
address or without one (`tty7 send --enter` presses Enter in your own pane).
|
||||
An *unmarked* id is deliberately left out: `tty7 send 83 --enter` reads as
|
||||
much like typing "83" where you are sitting as like pressing Enter in pane
|
||||
83, so it stays a loud error that names both spellings (`send %83 --enter`,
|
||||
`send %PANE 83 --enter`) rather than quietly retargeting the keystroke
|
||||
(#581).
|
||||
|
||||
## [26.8.3] - 2026-08-12
|
||||
|
||||
|
||||
@@ -202,7 +202,11 @@ pub struct SendArgs {
|
||||
#[arg(value_name = "TEXT")]
|
||||
pub second: Option<String>,
|
||||
|
||||
#[arg(long, help = "Press Enter after the text")]
|
||||
#[arg(
|
||||
long,
|
||||
help = "Press Enter after the text, or on its own when there is none \
|
||||
(= --key enter)"
|
||||
)]
|
||||
pub enter: bool,
|
||||
|
||||
// Text covers "type this command"; it cannot express the keystrokes a pane
|
||||
|
||||
@@ -484,11 +484,25 @@ fn pane_split(args: SplitArgs, ctx: &Context, backend: &mut dyn Backend) -> Resu
|
||||
fn send(args: SendArgs, ctx: &Context, backend: &mut dyn Backend) -> Result<Outcome> {
|
||||
const KEY_GAP: Duration = Duration::from_millis(200);
|
||||
|
||||
// `--enter` is the same thing as `--key enter`, and predates it. Keeping it
|
||||
// as sugar rather than deprecating it: it reads better for the overwhelming
|
||||
// case, which is typing one command and running it. Going through the same
|
||||
// parser leaves one definition of what Enter puts on the wire — and the list
|
||||
// is built here, before the dispatch, because the dispatch has to count it:
|
||||
// `send %42 --enter` used to report "needs TEXT … or a --key to press" while
|
||||
// the docs called `--enter` shorthand for exactly such a key (#581).
|
||||
let mut pressed = args.keys.clone();
|
||||
if args.enter {
|
||||
pressed.push(crate::keys::parse("enter").expect("enter is in the vocabulary"));
|
||||
}
|
||||
|
||||
// Three shapes reach here, and only the address is ever ambiguous:
|
||||
// `send %3 "text"`, `send "text"` (this pane), and — new with --key —
|
||||
// `send %3 --key C-c`, where there is no text at all and the lone
|
||||
// positional is therefore an address rather than the missing-text error it
|
||||
// has to stay in every other case.
|
||||
// has to stay in every other case. `send %3 --enter` is that third shape
|
||||
// too, with the one carve-out below: only a marked address is promoted by
|
||||
// `--enter` alone.
|
||||
//
|
||||
// The address-shaped-but-broken case must not fall through to "type it":
|
||||
// `send %3x --key C-c` used to type `%3x` into the *caller's own* pane and
|
||||
@@ -501,12 +515,26 @@ fn send(args: SendArgs, ctx: &Context, backend: &mut dyn Backend) -> Result<Outc
|
||||
(Some(first), Some(text)) => (Some(first.as_str()), Some(text.as_str())),
|
||||
(Some(first), None) => match address::parse_pane(first) {
|
||||
Ok(_) => {
|
||||
if args.keys.is_empty() {
|
||||
if pressed.is_empty() {
|
||||
bail!(
|
||||
"send needs TEXT after the pane address, or a --key to press \
|
||||
— to type '{first}' literally, name the pane too: send %PANE {first}"
|
||||
);
|
||||
}
|
||||
// A `--key` is always an explicit "press this", so it promotes
|
||||
// either spelling of the address. `--enter` is not, for an
|
||||
// *unmarked* id: `send 2 --enter` reads as "type 2 and run it"
|
||||
// far more often than "press Enter in pane 2", and #538 was
|
||||
// about never quietly retargeting a keystroke. The `%` is what
|
||||
// says which was meant, so it stays the loud error it is today.
|
||||
if args.keys.is_empty() && !first.starts_with('%') {
|
||||
bail!(
|
||||
"'{first}' is a bare pane id and --enter has nothing to type \
|
||||
— to press Enter in pane {first}: send %{first} --enter; \
|
||||
to type '{first}' and press Enter, name the pane too: \
|
||||
send %PANE {first} --enter"
|
||||
);
|
||||
}
|
||||
(Some(first.as_str()), None)
|
||||
}
|
||||
// `%` then a digit is someone writing an address, so the parse
|
||||
@@ -515,7 +543,7 @@ fn send(args: SendArgs, ctx: &Context, backend: &mut dyn Backend) -> Result<Outc
|
||||
Err(_) => (None, Some(first.as_str())),
|
||||
},
|
||||
(None, _) => {
|
||||
if args.keys.is_empty() {
|
||||
if pressed.is_empty() {
|
||||
bail!("send needs TEXT to type or a --key to press");
|
||||
}
|
||||
(None, None)
|
||||
@@ -528,14 +556,6 @@ fn send(args: SendArgs, ctx: &Context, backend: &mut dyn Backend) -> Result<Outc
|
||||
backend.send_input(pane, text.as_bytes().to_vec())?;
|
||||
already_wrote = true;
|
||||
}
|
||||
// `--enter` is the same thing as `--key enter`, and predates it. Keeping it
|
||||
// as sugar rather than deprecating it: it reads better for the overwhelming
|
||||
// case, which is typing one command and running it. Going through the same
|
||||
// parser leaves one definition of what Enter puts on the wire.
|
||||
let mut pressed = args.keys.clone();
|
||||
if args.enter {
|
||||
pressed.push(crate::keys::parse("enter").expect("enter is in the vocabulary"));
|
||||
}
|
||||
for key in &pressed {
|
||||
// Raw-mode TUIs detect a fast stream as pasted input and intentionally
|
||||
// absorb Enter as a newline — and a menu being driven by arrow keys has
|
||||
@@ -2250,29 +2270,78 @@ mod tests {
|
||||
/// A lone bare id now reads as an address, so `send 83` no longer types
|
||||
/// "83" into the caller's pane — it says it has nothing to send. That is
|
||||
/// the one behaviour this change takes away, and it has to fail loudly
|
||||
/// rather than quietly press keys somewhere else: `--enter` is not a
|
||||
/// `--key`, so it does not turn the id into a target either.
|
||||
/// rather than quietly press keys somewhere else: `--enter` presses a key
|
||||
/// now (#581), but it still does not turn an unmarked id into a target.
|
||||
#[test]
|
||||
fn a_lone_bare_id_refuses_loudly_rather_than_retargeting() {
|
||||
let ctx = Context {
|
||||
pane: Some("5".into()),
|
||||
..Context::default()
|
||||
};
|
||||
for args in [
|
||||
vec!["tty7", "send", "83"],
|
||||
vec!["tty7", "send", "83", "--enter"],
|
||||
] {
|
||||
let mut backend = mock();
|
||||
let err =
|
||||
execute(cli(&args), &ctx, &mut backend).expect_err("a bare id has nothing to send");
|
||||
assert!(err.to_string().contains("needs TEXT"), "{err}");
|
||||
// The escape hatch for typing it anyway is in the message.
|
||||
assert!(err.to_string().contains("send %PANE 83"), "{err}");
|
||||
assert!(
|
||||
backend.sent.is_empty(),
|
||||
"no keystroke reached pane 83 or pane 5"
|
||||
);
|
||||
}
|
||||
let mut backend = mock();
|
||||
let err = execute(cli(&["tty7", "send", "83"]), &ctx, &mut backend)
|
||||
.expect_err("a bare id has nothing to send");
|
||||
assert!(err.to_string().contains("needs TEXT"), "{err}");
|
||||
// The escape hatch for typing it anyway is in the message.
|
||||
assert!(err.to_string().contains("send %PANE 83"), "{err}");
|
||||
assert!(
|
||||
backend.sent.is_empty(),
|
||||
"no keystroke reached pane 83 or pane 5"
|
||||
);
|
||||
|
||||
// `--enter` counts as the keystroke it always was (#581) — but not
|
||||
// enough to promote an *unmarked* id, or `send 2 --enter` meaning "type
|
||||
// 2 and run it" would press Enter in pane 2 instead. Both ways out are
|
||||
// named, because either could have been meant.
|
||||
let mut backend = mock();
|
||||
let err = execute(cli(&["tty7", "send", "83", "--enter"]), &ctx, &mut backend)
|
||||
.expect_err("--enter alone does not make a bare id a target");
|
||||
assert!(err.to_string().contains("send %83 --enter"), "{err}");
|
||||
assert!(err.to_string().contains("send %PANE 83 --enter"), "{err}");
|
||||
assert!(
|
||||
backend.sent.is_empty(),
|
||||
"no keystroke reached pane 83 or pane 5"
|
||||
);
|
||||
}
|
||||
|
||||
/// `--enter` is documented as shorthand for `--key enter`, so it has to
|
||||
/// give a lone address something to do exactly as `--key` does — it used to
|
||||
/// report "needs TEXT … or a --key to press" and press nothing (#581).
|
||||
#[test]
|
||||
fn enter_alone_presses_enter_at_the_address_it_was_given() {
|
||||
let mut backend = mock();
|
||||
let json = json_of(run_cli(
|
||||
&["tty7", "send", "%42", "--enter"],
|
||||
&Context::default(),
|
||||
&mut backend,
|
||||
));
|
||||
assert_eq!(backend.sent, vec![(42, b"\r".to_vec())]);
|
||||
assert_eq!(json["sent"], "", "nothing was typed");
|
||||
assert_eq!(json["keys"], serde_json::json!(["enter"]));
|
||||
assert_eq!(json["enter"], true);
|
||||
|
||||
// With no address at all it is the caller's own pane, the same as
|
||||
// `send --key enter` already was.
|
||||
let ctx = Context {
|
||||
pane: Some("5".into()),
|
||||
..Context::default()
|
||||
};
|
||||
let mut backend = mock();
|
||||
run_cli(&["tty7", "send", "--enter"], &ctx, &mut backend);
|
||||
assert_eq!(backend.sent, vec![(5, b"\r".to_vec())]);
|
||||
|
||||
// The long way round stays open for a bare id, and means the same
|
||||
// thing: an explicit `--key` promotes either spelling.
|
||||
let mut backend = mock();
|
||||
run_cli(
|
||||
&["tty7", "send", "83", "--key", "enter"],
|
||||
&ctx,
|
||||
&mut backend,
|
||||
);
|
||||
assert_eq!(backend.sent, vec![(83, b"\r".to_vec())]);
|
||||
let mut backend = mock();
|
||||
run_cli(&["tty7", "send", "%83", "--enter"], &ctx, &mut backend);
|
||||
assert_eq!(backend.sent, vec![(83, b"\r".to_vec())]);
|
||||
}
|
||||
|
||||
/// The narrowing has to leave real text alone: `%` followed by a non-digit
|
||||
|
||||
@@ -86,6 +86,10 @@ pub fn send_long_help() -> String {
|
||||
--key sends a keystroke rather than characters, which is what a pane wants once \
|
||||
something is already running in it: answering a prompt that only takes arrow keys, \
|
||||
closing a TUI with escape, stopping a build with C-c. Repeat it for a sequence.\n\n\
|
||||
--enter is shorthand for --key enter: it presses Enter after TEXT, or on its own \
|
||||
when there is none, so `send %42 --enter` runs whatever is already typed in pane 42. \
|
||||
An unmarked id is not a target for it — `send 83 --enter` is refused, because it \
|
||||
reads just as much like typing 83 into your own pane; write %83 to mean the pane.\n\n\
|
||||
Keys: {}. Aliases: {}.",
|
||||
vocabulary(),
|
||||
aliases.join(", ")
|
||||
|
||||
+14
-9
@@ -93,15 +93,20 @@ share kept by the *existing* pane. Prints `%NN`. JSON: `{"pane"}`.
|
||||
|
||||
### `tty7 send [%PANE] [TEXT] [--enter] [--key KEY]…`
|
||||
|
||||
Types `TEXT` into the pane as keystrokes; `--enter` appends CR. With one
|
||||
argument the text is the argument and the pane comes from `$TTY7_PANE` — but a
|
||||
lone `%42` (or bare `42`, the shape `pane ls --json` prints) is rejected as a
|
||||
missing-text error rather than typed, unless a `--key` gives it something to
|
||||
do. A `%` followed by a digit that still doesn't parse (`%3x`) is an address
|
||||
error, never text for your own pane — while text that merely starts with `%`
|
||||
(`%s/foo/bar/`, `%!sort`) types as given, as does anything unmarked that is not
|
||||
a plain number (`3x`, `+5`). To type an address-shaped string, name the pane as
|
||||
well: `tty7 send %42 %3x`.
|
||||
Types `TEXT` into the pane as keystrokes; `--enter` is shorthand for `--key
|
||||
enter` — it appends CR to the text, or presses Enter on its own when there is
|
||||
none, so `tty7 send %42 --enter` runs whatever pane 42 already has typed. With
|
||||
one argument the text is the argument and the pane comes from `$TTY7_PANE` —
|
||||
but a lone `%42` (or bare `42`, the shape `pane ls --json` prints) is rejected
|
||||
as a missing-text error rather than typed, unless a `--key` gives it something
|
||||
to do. `--enter` is that key only for the `%`-marked spelling: `tty7 send 83
|
||||
--enter` is refused, because it reads as much like typing `83` into your own
|
||||
pane as like pressing Enter in pane 83, and the error names both ways to say
|
||||
which (`send %83 --enter`, `send %PANE 83 --enter`). A `%` followed by a digit
|
||||
that still doesn't parse (`%3x`) is an address error, never text for your own
|
||||
pane — while text that merely starts with `%` (`%s/foo/bar/`, `%!sort`) types
|
||||
as given, as does anything unmarked that is not a plain number (`3x`, `+5`). To
|
||||
type an address-shaped string, name the pane as well: `tty7 send %42 %3x`.
|
||||
|
||||
`--key` presses a key instead of typing characters, which is what a pane wants
|
||||
once something is already running in it: answering a prompt that only takes
|
||||
|
||||
@@ -118,8 +118,10 @@ read -r WS PANE < <(tty7 new --json /path/to/repo \
|
||||
```
|
||||
|
||||
`send` types text into the pane exactly as a keyboard would; `--enter` appends
|
||||
the carriage return. It does not wait and it does not tell you what happened —
|
||||
reading is a separate step, and waiting is `tty7 wait`.
|
||||
the carriage return, or presses Enter on its own when you give it no text
|
||||
(`tty7 send "$PANE" --enter` runs what is already typed there). It does not
|
||||
wait and it does not tell you what happened — reading is a separate step, and
|
||||
waiting is `tty7 wait`.
|
||||
|
||||
For keystrokes rather than characters — Ctrl-C, Escape, the arrow keys — use
|
||||
`--key` (see [Answering a prompt](#answering-a-prompt)). Typing `^C` as text
|
||||
|
||||
@@ -100,15 +100,20 @@ pane below, `--h`/`--horizontal` to the right. `--ratio` (default 0.5) is the
|
||||
share kept by the *existing* pane. Prints `%NN`. JSON: `{"pane"}`.
|
||||
|
||||
### `tty7 send [%PANE] [TEXT] [--enter] [--key KEY]…`
|
||||
Types `TEXT` into the pane as keystrokes; `--enter` appends CR. With one
|
||||
argument the text is the argument and the pane comes from `$TTY7_PANE` — but a
|
||||
lone `%42` (or bare `42`, the shape `pane ls --json` prints) is rejected as a
|
||||
missing-text error rather than typed, unless a `--key` gives it something to
|
||||
do. A `%` followed by a digit that still doesn't parse (`%3x`) is an address
|
||||
error, never text for your own pane — while text that merely starts with `%`
|
||||
(`%s/foo/bar/`, `%!sort`) types as given, as does anything unmarked that is not
|
||||
a plain number (`3x`, `+5`). To type an address-shaped string, name the pane as
|
||||
well: `tty7 send %42 %3x`.
|
||||
Types `TEXT` into the pane as keystrokes; `--enter` is shorthand for `--key
|
||||
enter` — it appends CR to the text, or presses Enter on its own when there is
|
||||
none, so `tty7 send %42 --enter` runs whatever pane 42 already has typed. With
|
||||
one argument the text is the argument and the pane comes from `$TTY7_PANE` —
|
||||
but a lone `%42` (or bare `42`, the shape `pane ls --json` prints) is rejected
|
||||
as a missing-text error rather than typed, unless a `--key` gives it something
|
||||
to do. `--enter` is that key only for the `%`-marked spelling: `tty7 send 83
|
||||
--enter` is refused, because it reads as much like typing `83` into your own
|
||||
pane as like pressing Enter in pane 83, and the error names both ways to say
|
||||
which (`send %83 --enter`, `send %PANE 83 --enter`). A `%` followed by a digit
|
||||
that still doesn't parse (`%3x`) is an address error, never text for your own
|
||||
pane — while text that merely starts with `%` (`%s/foo/bar/`, `%!sort`) types
|
||||
as given, as does anything unmarked that is not a plain number (`3x`, `+5`). To
|
||||
type an address-shaped string, name the pane as well: `tty7 send %42 %3x`.
|
||||
JSON: `{"pane","sent","enter","keys"}`.
|
||||
|
||||
`--key` presses a key instead of typing characters — the arrow keys a
|
||||
|
||||
Reference in New Issue
Block a user