mirror of
https://github.com/NCBM/plyngent.git
synced 2026-07-23 05:55:16 +08:00
core/cli: multiline \"\"\" input and /edit via EDITOR
Add triple-quote block messages in the REPL and /edit to compose a turn in $EDITOR (temp file). Empty multiline or empty edit cancels.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
@@ -14,9 +14,6 @@ from plyngent.cli.editor import (
|
||||
resolve_config_path,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_get_editor(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("EDITOR", raising=False)
|
||||
@@ -66,6 +63,42 @@ def test_open_in_editor_missing_editor(tmp_path: Path, monkeypatch: pytest.Monke
|
||||
open_in_editor(tmp_path / "x.toml", editor=None)
|
||||
|
||||
|
||||
def test_edit_text_in_editor(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
from plyngent.cli.editor import edit_text_in_editor
|
||||
|
||||
def fake_run(argv: list[str], check: bool = False) -> object:
|
||||
del check
|
||||
path = Path(argv[-1])
|
||||
_ = path.write_text("hello from editor\n", encoding="utf-8")
|
||||
|
||||
class Result:
|
||||
returncode: int = 0
|
||||
|
||||
return Result()
|
||||
|
||||
monkeypatch.setenv("EDITOR", "true")
|
||||
monkeypatch.setattr("plyngent.cli.editor.subprocess.run", fake_run)
|
||||
assert edit_text_in_editor("seed") == "hello from editor"
|
||||
|
||||
|
||||
def test_edit_text_empty_cancels(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
from plyngent.cli.editor import edit_text_in_editor
|
||||
|
||||
def fake_run(argv: list[str], check: bool = False) -> object:
|
||||
del check
|
||||
path = Path(argv[-1])
|
||||
_ = path.write_text(" \n", encoding="utf-8")
|
||||
|
||||
class Result:
|
||||
returncode: int = 0
|
||||
|
||||
return Result()
|
||||
|
||||
monkeypatch.setenv("EDITOR", "true")
|
||||
monkeypatch.setattr("plyngent.cli.editor.subprocess.run", fake_run)
|
||||
assert edit_text_in_editor("") is None
|
||||
|
||||
|
||||
def test_prompt_edit_no_editor(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
monkeypatch.delenv("EDITOR", raising=False)
|
||||
assert prompt_edit_config(tmp_path / "p.toml") is False
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from plyngent.cli.input_text import (
|
||||
assemble_multiline,
|
||||
finish_triple_quote_line,
|
||||
parse_triple_quote_line,
|
||||
read_repl_entry,
|
||||
)
|
||||
|
||||
|
||||
def test_parse_same_line_block() -> None:
|
||||
assert parse_triple_quote_line('"""hello"""') == ("hello", True)
|
||||
assert parse_triple_quote_line(' """hi""" ') == ("hi", True)
|
||||
|
||||
|
||||
def test_parse_open_block() -> None:
|
||||
assert parse_triple_quote_line('"""first') == ("first", False)
|
||||
assert parse_triple_quote_line('"""') == ("", False)
|
||||
|
||||
|
||||
def test_parse_not_block() -> None:
|
||||
assert parse_triple_quote_line("hello") is None
|
||||
assert parse_triple_quote_line("/help") is None
|
||||
|
||||
|
||||
def test_finish_closer() -> None:
|
||||
assert finish_triple_quote_line('"""') == ("", True)
|
||||
assert finish_triple_quote_line('last line"""') == ("last line", True)
|
||||
assert finish_triple_quote_line("middle") == ("middle", False)
|
||||
|
||||
|
||||
def test_assemble_multiline() -> None:
|
||||
lines = iter(["line two", '"""'])
|
||||
text = assemble_multiline(
|
||||
'"""line one',
|
||||
read_line=lambda: next(lines),
|
||||
)
|
||||
assert text == "line one\nline two"
|
||||
|
||||
|
||||
def test_assemble_empty_cancels() -> None:
|
||||
lines = iter(['"""'])
|
||||
assert assemble_multiline('"""', read_line=lambda: next(lines)) is None
|
||||
|
||||
|
||||
def test_read_repl_entry_slash() -> None:
|
||||
assert read_repl_entry(read_line=lambda: "/status") == "/status"
|
||||
|
||||
|
||||
def test_read_repl_entry_simple() -> None:
|
||||
assert read_repl_entry(read_line=lambda: " hi ") == "hi"
|
||||
|
||||
|
||||
def test_read_repl_entry_empty() -> None:
|
||||
assert read_repl_entry(read_line=lambda: " ") is None
|
||||
|
||||
|
||||
def test_read_repl_entry_multiline() -> None:
|
||||
seq = iter(['"""a', "b", '"""'])
|
||||
text = read_repl_entry(read_line=lambda: next(seq), echo=lambda _s: None)
|
||||
assert text == "a\nb"
|
||||
@@ -134,6 +134,20 @@ async def test_quit(state: ReplState) -> None:
|
||||
assert await handle_slash(state, "/quit") is False
|
||||
|
||||
|
||||
async def test_edit_sets_pending(
|
||||
state: ReplState,
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
capsys: pytest.CaptureFixture[str],
|
||||
) -> None:
|
||||
monkeypatch.setattr(
|
||||
"plyngent.cli.editor.edit_text_in_editor",
|
||||
lambda *a, **k: "composed message",
|
||||
)
|
||||
assert await handle_slash(state, "/edit") is True
|
||||
assert state.pending_user_text == "composed message"
|
||||
assert "characters ready" in capsys.readouterr().out
|
||||
|
||||
|
||||
async def test_new_and_sessions(state: ReplState, capsys: pytest.CaptureFixture[str]) -> None:
|
||||
first = state.session_id
|
||||
assert await handle_slash(state, "/new other") is True
|
||||
|
||||
Reference in New Issue
Block a user