@@ -21,21 +21,8 @@ def lnhashview_file(path:str, start:int=None, end:int=None) -> list[str]:
2121 return _lnhashview (Path (path ).read_text (), start , end )
2222
2323
24- def exhash_result (results :list [dict ]) -> str :
25- 'Format modified lines from exhash result dicts in lnhash view format.'
26- if not isinstance (results , list ): raise TypeError ("results must be a list[dict]" )
27- out = []
28- for r in results :
29- if not isinstance (r , dict ): raise TypeError ("results must be a list[dict]" )
30- lines , hashes , modified = r .get ("lines" ), r .get ("hashes" ), r .get ("modified" )
31- if not isinstance (lines , list ) or not isinstance (hashes , list ) or not isinstance (modified , list ):
32- raise TypeError ("each result must include list fields: lines, hashes, modified" )
33- out += [f"{ hashes [i - 1 ]} { lines [i - 1 ]} " for i in modified if isinstance (i , int ) and 0 < i <= len (hashes )]
34- return '\n ' .join (out )
35-
36-
37- def exhash (text :str , cmds :list [str ], sw :int = 4 ) -> dict :
38- """Verified line-addressed editor. Apply commands to `text`, return a result dict.
24+ def exhash (text :str , cmds :list [str ], sw :int = 4 ):
25+ """Verified line-addressed editor. Apply commands to `text`, return an EditResult.
3926
4027 Commands primarily use lnhash addresses: ``lineno|hash|cmd`` where hash is
4128 a 4-char hex content hash. Use ``lnhashview(text)`` or
@@ -76,11 +63,14 @@ def exhash(text:str, cmds:list[str], sw:int=4) -> dict:
7663 For a/i/c, remaining lines in the command string are the text block
7764 (no '.' terminator needed, unlike the CLI).
7865
79- Returns a dict with:
66+ Returns an EditResult with attributes (also accessible as dict keys) :
8067 lines list of output lines
8168 hashes lnhash for each output line
8269 modified 1-based line numbers of modified/added lines
8370 deleted 1-based line numbers of removed lines (in original)
71+ origins for each output line, the 1-based original line number (None if inserted)
72+
73+ Call ``res.format_diff(context=1)`` for a unified-diff-style summary.
8474
8575 `cmds` is a required iterable of command strings. For `a`/`i`/`c`, include
8676 the text block in the same command string after a newline.
@@ -92,16 +82,16 @@ def exhash(text:str, cmds:list[str], sw:int=4) -> dict:
9282 addr = lnhash(1, "foo") # "1|a1b2|"
9383 res = exhash(text, [f"{addr}s/foo/baz/"])
9484 print(res["lines"]) # ["baz", "bar"]
95- "\\ n".join(res["lines"]) # "baz\\ nbar"
96- res = exhash(text, [f"{addr}a\\ nnew line 1\\ nnew line 2"])
85+ print(res.format_diff()) # unified-diff-style summary
9786 """
98- r = _exhash (text , * cmds , sw = sw )
99- return dict (lines = r .lines , hashes = r .hashes , modified = r .modified , deleted = r .deleted )
87+ return _exhash (text , * cmds , sw = sw )
10088
10189
102- def exhash_file (path :str , cmds :list [str ], sw :int = 4 , inplace :bool = False ) -> dict :
103- 'Like ``exhash`` but reads from file at ``path``. If ``inplace``, writes result back (atomically on success only) .'
90+ def exhash_file (path :str , cmds :list [str ], sw :int = 4 , inplace :bool = False ):
91+ 'Like ``exhash`` but reads from file at ``path``. If ``inplace``, writes back and returns diff string .'
10492 text = Path (path ).read_text ()
105- r = exhash (text , cmds , sw = sw )
106- if inplace : Path (path ).write_text ('\n ' .join (r ['lines' ]) + '\n ' if r ['lines' ] else '' )
93+ r = _exhash (text , * cmds , sw = sw )
94+ if inplace :
95+ Path (path ).write_text ('\n ' .join (r ['lines' ]) + '\n ' if r ['lines' ] else '' )
96+ return r .format_diff ()
10797 return r
0 commit comments