diff options
| author | Vineet Kumar <git@vineetk.net> | 2026-05-24 20:21:33 -0400 |
|---|---|---|
| committer | Vineet Kumar <git@vineetk.net> | 2026-05-24 20:36:53 -0400 |
| commit | f0ecdc6a955b7d0f9020aba3a8e2c46c7e22f9d5 (patch) | |
| tree | 3b92b893543ca9cbc5540d55aa749660e1a53ee0 | |
| parent | 45b881a7f0b02a00e3f54beb329c8f938830f6ba (diff) | |
add beginning of hashline verification
| -rw-r--r-- | llm-tools-hl.el | 107 |
1 files changed, 102 insertions, 5 deletions
diff --git a/llm-tools-hl.el b/llm-tools-hl.el index 5a984e8..1ec13ef 100644 --- a/llm-tools-hl.el +++ b/llm-tools-hl.el | |||
| @@ -89,12 +89,9 @@ If INCLUDE-CONTENT is non-nil, append `|LINE' after each hash." | |||
| 89 | "Return FILE contents in hashline format from lines BEG to END." | 89 | "Return FILE contents in hashline format from lines BEG to END." |
| 90 | (llm-tools--hl-format-lines (llm-tools--hl-file-lines file beg end) beg t)) | 90 | (llm-tools--hl-format-lines (llm-tools--hl-file-lines file beg end) beg t)) |
| 91 | 91 | ||
| 92 | ;; FIXME might be redundant with =llm-tools--hl-file-line-hash= if I'm only | ||
| 93 | ;; verifying the hash of the anchors and telling LLM to re-read the | ||
| 94 | ;; file when they don't match | ||
| 95 | (defun llm-tools--hl-file-read-hashes (file) | 92 | (defun llm-tools--hl-file-read-hashes (file) |
| 96 | "Return FILE contents in hashline format without content." | 93 | "TODO rewrite docstring to be less redundant. Return FILE contents in hashline format without content as a list of hashline-formatted hashes." |
| 97 | (llm-tools--hl-format-lines (llm-tools--hl-file-lines file))) | 94 | (string-lines (llm-tools--hl-format-lines (llm-tools--hl-file-lines file)))) |
| 98 | 95 | ||
| 99 | (defun llm-tools--hl-file-line-hash (file n) | 96 | (defun llm-tools--hl-file-line-hash (file n) |
| 100 | "Return the hash of the Nth line in FILE." | 97 | "Return the hash of the Nth line in FILE." |
| @@ -107,4 +104,104 @@ If INCLUDE-CONTENT is non-nil, append `|LINE' after each hash." | |||
| 107 | ;; rather, the llm would not format it like the reads. it's just going to provide text according to what the anchors it gives say | 104 | ;; rather, the llm would not format it like the reads. it's just going to provide text according to what the anchors it gives say |
| 108 | ;; so I just need to verify if the anchors didn't change | 105 | ;; so I just need to verify if the anchors didn't change |
| 109 | 106 | ||
| 107 | ;;;; Diff Parsing | ||
| 108 | (defun llm-tools--split-patch-sections (patch) | ||
| 109 | "Return a list of (FILENAME . REST) cons cells from PATCH. | ||
| 110 | Each section starts with '@@ PATH' on the first line; the car is PATH | ||
| 111 | and the cdr is the remainder of that section." | ||
| 112 | (let ((pos (string-match "^@@" patch))) | ||
| 113 | (when pos | ||
| 114 | (let (sections | ||
| 115 | (start pos)) | ||
| 116 | (while (string-match "\n@@" patch (1+ pos)) | ||
| 117 | (let ((nl (match-beginning 0))) | ||
| 118 | (push (substring patch start (1+ nl)) sections) | ||
| 119 | (setq start (1+ nl))) | ||
| 120 | (setq pos (match-end 0))) | ||
| 121 | (push (substring patch start) sections) | ||
| 122 | (mapcar (lambda (sec) | ||
| 123 | (let ((end (string-match "\n" sec))) | ||
| 124 | (cons (substring sec 3 end) | ||
| 125 | (substring sec end)))) | ||
| 126 | (nreverse sections)))))) | ||
| 127 | |||
| 128 | (defun llm-tools--hl-get-anchors (patch) | ||
| 129 | "Returns an alist of (FILENAME . ANCHORS) from PATCH. | ||
| 130 | ANCHORS is a sorted, deduplicated list of all line anchors referenced by | ||
| 131 | the operations in that file's sections. Each anchor is a string like | ||
| 132 | \"5ff\" or a list of two strings from a range like | ||
| 133 | (\"3gv\" \"6be\")." | ||
| 134 | (let ((sections (split-patch-sections patch))) | ||
| 135 | (delete-dups | ||
| 136 | (mapcar (lambda (patch) | ||
| 137 | (cons (car patch) | ||
| 138 | (delete-dups | ||
| 139 | (sort | ||
| 140 | (flatten-list | ||
| 141 | (mapcar (lambda (line) | ||
| 142 | (if (length> line 0) | ||
| 143 | (let ((op (char-to-string (elt line 0))) | ||
| 144 | (anchor (substring line 2))) | ||
| 145 | (pcase op | ||
| 146 | ((or "+" "<") anchor) | ||
| 147 | ((or "-" "=") (split-string anchor "\\.\\.")))))) | ||
| 148 | (string-lines (cdr patch)))))))) | ||
| 149 | sections)))) | ||
| 150 | |||
| 151 | (defun llm-tools--hl-verify (patch) | ||
| 152 | "TODO rewrite docstring to be lses verbose? Verifies the anchors exist in PATCH." | ||
| 153 | (let ((patch-anchors (llm-tools--hl-get-anchors patch))) | ||
| 154 | (mapcar (lambda (section) | ||
| 155 | (let* ((file (car section)) | ||
| 156 | (anchors (delete "EOF" (cdr section))) | ||
| 157 | (hl (llm-tools--hl-file-read-hashes file))) | ||
| 158 | ;; cl-subsetp uses eql by default which compares by identity and not content | ||
| 159 | (cons file (cl-subsetp anchors hl :test #'equal)))) | ||
| 160 | patch-anchors))) | ||
| 161 | |||
| 162 | (let* ((patch "\ | ||
| 163 | # Replace one line (the payload must re-emit the original indentation) | ||
| 164 | @@ mod.ts | ||
| 165 | = 1vx..1vx | ||
| 166 | ~const TITLE = \"Mrs\"; | ||
| 167 | |||
| 168 | # Replace a full multiline statement (widen to a self-contained boundary) | ||
| 169 | @@ mod.ts | ||
| 170 | = 3gv..6be | ||
| 171 | ~ return [ | ||
| 172 | ~ \"Mrs\", | ||
| 173 | ~ name?.trim() || \"guest\", | ||
| 174 | ~ ].join(\" \"); | ||
| 175 | |||
| 176 | # Insert AFTER/BEFORE a line | ||
| 177 | @@ mod.ts | ||
| 178 | + 4ei | ||
| 179 | ~ \"Dr\", | ||
| 180 | < 5ff | ||
| 181 | ~ \"Dr\", | ||
| 182 | |||
| 183 | # Append to file | ||
| 184 | @@ mod.ts | ||
| 185 | + EOF | ||
| 186 | ~export const done = true; | ||
| 187 | |||
| 188 | # Delete a line | ||
| 189 | @@ test.el | ||
| 190 | - 5ff..5ff | ||
| 191 | |||
| 192 | # Blank a line (replace with LF) | ||
| 193 | @@ mod.ts | ||
| 194 | = 5bb..5bb | ||
| 195 | ")) | ||
| 196 | (string-join | ||
| 197 | (delete-dups | ||
| 198 | (remq nil | ||
| 199 | (mapcar (lambda (elem) | ||
| 200 | (unless (cdr elem) | ||
| 201 | (format "%s had incorrect anchors. Use the 'read_file' tool to get correct anchors." | ||
| 202 | (car elem)))) | ||
| 203 | (llm-tools--hl-verify patch)))) | ||
| 204 | "\n")) | ||
| 205 | |||
| 110 | (provide 'llm-tools-hl) | 206 | (provide 'llm-tools-hl) |
| 207 | |||
