Skip to content

Commit baf40ee

Browse files
committed
Clarify multiline a/i/c usage in Python API docs
1 parent b33c5dc commit baf40ee

4 files changed

Lines changed: 17 additions & 14 deletions

File tree

DEV.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ Maturin's `data` option in `pyproject.toml` points to `python/exhash.data/`. Fil
8888

8989
The Rust core has three parsing functions:
9090

91-
- `parse_commands_from_strs(&[&str])` — for the Python API; each string is one command, text blocks are the remaining lines (no `.` terminator; a trailing `.` line is literal text and the Python binding warns about this common mistake)
91+
- `parse_commands_from_strs(&[&str])` — for the Python API; each string is one command, and multiline `a/i/c` text blocks must be in that same string using newlines, e.g. `["12|abcd|c\nnew line 1\nnew line 2"]`. Do not use `.` terminators or split the inserted text into separate command entries; a trailing `.` line is literal text and the Python binding warns about this common mistake.
9292
- `parse_commands_from_script(&str)` — for script strings; commands separated by newlines, text blocks terminated by `.`
9393
- `parse_commands_from_args(&[String], &mut BufRead)` — for the CLI; each arg is a command, text blocks read from stdin terminated by `.`
9494

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ view = lnhashview_file("f.py", start=1, end=260) # end past EOF is clamped
118118

119119
### Editing
120120

121-
`exhash(text, cmds, sw=4)` takes the text and a required iterable of command strings (use `[]` for no-op). `sw` controls how far `<` and `>` shift. For `a`/`i`/`c` commands, lines after the command are the text block. Do not include an ex-style trailing `.` line here: unlike CLI/script mode, `exhash(text, cmds)` does not use one, and a final `.` line is inserted literally.
121+
`exhash(text, cmds, sw=4)` takes the text and a required iterable of command strings (use `[]` for no-op). `sw` controls how far `<` and `>` shift. For multiline `a`/`i`/`c` commands, include the inserted text in the same command string using newline characters, e.g. `["12|abcd|c\nnew line 1\nnew line 2"]`. Do not use `.` terminators, and do not split the text block into separate `cmds` entries. If you include a final `.` line, it is inserted literally and exhash emits a warning.
122122

123123
```py
124124
addr = lnhash(1, "foo") # "1|a1b2|"
@@ -133,12 +133,15 @@ res = exhash(text, [f"{a1}s/foo/FOO/", f"{a2}s/bar/BAR/"])
133133
# Hashes are checked just-in-time per command.
134134
# If earlier commands change/shift a later target line, recompute lnhash first.
135135

136-
# Append multiline text (no dot terminator)
136+
# Append multiline text in the same command string (no dot terminator)
137137
res = exhash(text, [f"{addr}a\nnew line 1\nnew line 2"])
138138

139139
# Wrong for the Python API: the trailing "." would be inserted literally
140140
# res = exhash(text, [f"{addr}a\nnew line 1\nnew line 2\n."])
141141

142+
# Also wrong: do not split the inserted text into separate cmds entries
143+
# res = exhash(text, [f"{addr}a", "new line 1", "new line 2"])
144+
142145
# Change shift width for < and >
143146
res = exhash(text, [f"{addr}>1"], sw=2)
144147

python/exhash/__init__.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ def exhash(text:str, cmds:list[str], sw:int=4):
6060
6161
`sw` controls shift width for `<` and `>` and defaults to 4.
6262
63-
For a/i/c, remaining lines in the command string are the text block.
64-
Do not include an ex-style trailing ``.`` terminator here: unlike CLI/script
65-
mode, ``exhash(text, cmds)`` does not use one. If you include a final ``.``
66-
line, it is inserted literally and exhash emits a warning.
63+
For multiline a/i/c commands, include the inserted text in the same command
64+
string using newline characters, e.g. ``["12|abcd|c\nnew line 1\nnew line 2"]``.
65+
Do not use ``.`` terminators, and do not split the text block into separate
66+
``cmds`` entries. If you include a final ``.`` line, it is inserted literally
67+
and exhash emits a warning.
6768
6869
Returns an EditResult with attributes (also accessible as dict keys):
6970
lines list of output lines
@@ -74,9 +75,6 @@ def exhash(text:str, cmds:list[str], sw:int=4):
7475
7576
Call ``res.format_diff(context=1)`` for a unified-diff-style summary.
7677
77-
`cmds` is a required iterable of command strings. For `a`/`i`/`c`, include
78-
the text block in the same command string after a newline.
79-
8078
Examples::
8179
8280
from exhash import exhash, lnhash, lnhashview
@@ -90,7 +88,7 @@ def exhash(text:str, cmds:list[str], sw:int=4):
9088

9189

9290
def exhash_file(path:str, cmds:list[str], sw:int=4, inplace:bool=False):
93-
'Like ``exhash`` but reads from file at ``path``. Uses the same no-``.``-terminator rule for a/i/c text blocks. If the file is missing and the commands are valid on empty input (for example ``0|0000|a``), it is treated as empty and created on inplace write. If ``inplace``, writes back and returns diff string.'
91+
'Like ``exhash`` but reads from file at ``path``. For multiline a/i/c commands, include the inserted text in the same command string using newline characters, e.g. ``["12|abcd|c\\nnew line 1\\nnew line 2"]``. Do not use ``.`` terminators, and do not split the text block into separate ``cmds`` entries. If the file is missing and the commands are valid on empty input (for example ``0|0000|a``), it is treated as empty and created on inplace write. If ``inplace``, writes back and returns diff string.'
9492
p = Path(path)
9593
try: text = p.read_text()
9694
except FileNotFoundError as e:

src/parse.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,11 @@ pub fn parse_commands_from_args(
8181

8282
/// Parse commands from a list of individual command strings (for programmatic APIs).
8383
///
84-
/// Each string is one command. For `a`/`i`/`c`, lines after the first are the text
85-
/// block (no `.` terminator needed; a trailing `.` line is literal text). For
86-
/// other commands, extra lines are an error.
84+
/// Each string is one command. For multiline `a`/`i`/`c`, include the text block
85+
/// in the same string using newline characters, e.g.
86+
/// `["12|abcd|c\nnew line 1\nnew line 2"]`. Do not use `.` terminators or split
87+
/// the text block into separate entries; a trailing `.` line is literal text.
88+
/// For other commands, extra lines are an error.
8789
pub fn parse_commands_from_strs(cmds: &[&str]) -> Result<Vec<Command>, EditError> {
8890
let mut out = Vec::with_capacity(cmds.len());
8991
for s in cmds {

0 commit comments

Comments
 (0)