summaryrefslogtreecommitdiff
path: root/llm-tools-hl.el
diff options
context:
space:
mode:
authorVineet Kumar <git@vineetk.net>2026-05-26 02:39:12 -0400
committerVineet Kumar <git@vineetk.net>2026-05-26 03:21:07 -0400
commit5f58df0ee61c74c329a081cff56f48cb89541da5 (patch)
tree2b51bff78f9da5b992d849a4761d3ca32f16bcd8 /llm-tools-hl.el
parent4e2c04ec27a7e69272dcede8efaa7fc2d36898cf (diff)
fix anchor extraction and operation parsing to handle payload lines
- avoid processing payload and comment lines via filter. - add guard on short strings (2 and under) just in case. - fixed args-out-of-range crash when payload lines appeared in patches.
Diffstat (limited to 'llm-tools-hl.el')
-rw-r--r--llm-tools-hl.el105
1 files changed, 57 insertions, 48 deletions
diff --git a/llm-tools-hl.el b/llm-tools-hl.el
index d745953..b095a6e 100644
--- a/llm-tools-hl.el
+++ b/llm-tools-hl.el
@@ -160,19 +160,19 @@ single string."
160(defun llm-tools--hl-validate-insert-has-payload (ops) 160(defun llm-tools--hl-validate-insert-has-payload (ops)
161 "Check that all insert ops have at least one payload line." 161 "Check that all insert ops have at least one payload line."
162 (mapcan (lambda (op) 162 (mapcan (lambda (op)
163 (when (and (memq (hl-verify-op-type op) '(insert-after insert-before)) 163 (when (and (memq (hl-verify-op-type op) '(insert-after insert-before))
164 (null (hl-verify-op-payload op))) 164 (null (hl-verify-op-payload op)))
165 (list (format "Insert operation (%s) has no payload lines following it" 165 (list (format "Insert operation (%s) has no payload lines following it"
166 (hl-verify-op-type op))))) 166 (hl-verify-op-type op)))))
167 ops)) 167 ops))
168 168
169(defun llm-tools--hl-validate-delete-no-payload (ops) 169(defun llm-tools--hl-validate-delete-no-payload (ops)
170 "Check that delete ops have no payload lines." 170 "Check that delete ops have no payload lines."
171 (mapcan (lambda (op) 171 (mapcan (lambda (op)
172 (when (and (eq (hl-verify-op-type op) 'delete) 172 (when (and (eq (hl-verify-op-type op) 'delete)
173 (hl-verify-op-payload op)) 173 (hl-verify-op-payload op))
174 (list "Delete operation (-) must not have payload lines"))) 174 (list "Delete operation (-) must not have payload lines")))
175 ops)) 175 ops))
176 176
177(defun llm-tools--hl-validate-insert-anchor (anchor) 177(defun llm-tools--hl-validate-insert-anchor (anchor)
178 "Return error string if ANCHOR is malformed for insert, else nil." 178 "Return error string if ANCHOR is malformed for insert, else nil."
@@ -199,14 +199,14 @@ single string."
199(defun llm-tools--hl-validate-anchors (ops) 199(defun llm-tools--hl-validate-anchors (ops)
200 "Check that all anchors are well-formed." 200 "Check that all anchors are well-formed."
201 (mapcan (lambda (op) 201 (mapcan (lambda (op)
202 (pcase (hl-verify-op-type op) 202 (pcase (hl-verify-op-type op)
203 ((or 'insert-after 'insert-before) 203 ((or 'insert-after 'insert-before)
204 (when-let ((err (llm-tools--hl-validate-insert-anchor (hl-verify-op-anchor op)))) 204 (when-let ((err (llm-tools--hl-validate-insert-anchor (hl-verify-op-anchor op))))
205 (list err))) 205 (list err)))
206 ((or 'delete 'replace) 206 ((or 'delete 'replace)
207 (when-let ((err (llm-tools--hl-validate-range-anchor (hl-verify-op-anchor op)))) 207 (when-let ((err (llm-tools--hl-validate-range-anchor (hl-verify-op-anchor op))))
208 (list err))))) 208 (list err)))))
209 ops)) 209 ops))
210 210
211(defun llm-tools--hl-verify-section (section) 211(defun llm-tools--hl-verify-section (section)
212 "Validate a single SECTION. Returns a list of error strings." 212 "Validate a single SECTION. Returns a list of error strings."
@@ -275,7 +275,7 @@ and the cdr is the remainder of that section."
275(defun llm-tools--hl-extract-anchors-from-op-line (line) 275(defun llm-tools--hl-extract-anchors-from-op-line (line)
276 "Return a list of anchor strings from a single operation LINE. 276 "Return a list of anchor strings from a single operation LINE.
277Handles ranges by returning both endpoints." 277Handles ranges by returning both endpoints."
278 (when (length> line 0) 278 (when (and (stringp line) (length> line 2))
279 (let ((op (char-to-string (elt line 0))) 279 (let ((op (char-to-string (elt line 0)))
280 (anchor (substring line 2))) 280 (anchor (substring line 2)))
281 (pcase op 281 (pcase op
@@ -283,10 +283,18 @@ Handles ranges by returning both endpoints."
283 ((or "-" "=") (split-string anchor "\\.\\.")))))) 283 ((or "-" "=") (split-string anchor "\\.\\."))))))
284 284
285(defun llm-tools--hl-extract-anchors-from-section (section-body) 285(defun llm-tools--hl-extract-anchors-from-section (section-body)
286 "Return all anchors from a section body (list of strings)." 286 "Return all anchors from a section body (string).
287 (flatten-list 287Only lines starting with +, <, -, or = are considered."
288 (mapcar #'llm-tools--hl-extract-anchors-from-op-line 288 (let ((ops-lines
289 (string-lines section-body)))) 289 (cl-remove-if-not
290 (lambda (line)
291 (let ((trimmed (string-trim line)))
292 (and (not (string-empty-p trimmed))
293 (not (string-prefix-p "#" trimmed))
294 (not (string-prefix-p "~" trimmed))
295 (member (substring trimmed 0 1) '("+" "<" "-" "=")))))
296 (string-lines section-body))))
297 (flatten-list (mapcar #'llm-tools--hl-extract-anchors-from-op-line ops-lines))))
290 298
291(defun llm-tools--hl-get-anchors (patch) 299(defun llm-tools--hl-get-anchors (patch)
292 "Returns an alist of (FILENAME . ANCHORS) from PATCH. 300 "Returns an alist of (FILENAME . ANCHORS) from PATCH.
@@ -326,39 +334,40 @@ line in the on-disk file."
326 (string-to-number (substring anchor 0 (- (length anchor) 2)))) 334 (string-to-number (substring anchor 0 (- (length anchor) 2))))
327 335
328(defun llm-tools--hl-parse-op-line (line) 336(defun llm-tools--hl-parse-op-line (line)
329 "Parse a single operation LINE into a `hl-op` struct. 337 "Parse a single operation LINE into an `hl-op` struct.
330LINE should start with `+`, `<`, `-`, or `=`." 338LINE must start with `+`, `<`, `-`, or `=`, followed by a space,
331 (pcase (substring line 0 1) 339followed by an anchor (or range)."
332 ("+" 340 (string-match "^[+<=-] \\(.*\\)" line) ; returns index or nil
333 (make-hl-op :type 'insert-after 341 (unless (match-beginning 0)
334 :anchor (substring line 2))) 342 (error "Invalid operation line: %S" line))
335 ("<" 343 (let* ((op-char (substring line 0 1))
336 (make-hl-op :type 'insert-before 344 (anchor (match-string 1 line)))
337 :anchor (substring line 2))) 345 (pcase op-char
338 ("-" 346 ("+" (make-hl-op :type 'insert-after :anchor anchor))
339 (make-hl-op :type 'delete 347 ("<" (make-hl-op :type 'insert-before :anchor anchor))
340 :range (split-string (substring line 2) "\\.\\."))) 348 ("-" (make-hl-op :type 'delete :range (split-string anchor "\\.\\.")))
341 ("=" 349 ("=" (make-hl-op :type 'replace :range (split-string anchor "\\.\\."))))))
342 (make-hl-op :type 'replace
343 :range (split-string (substring line 2) "\\.\\.")))))
344 350
345(defun llm-tools--hl-parse-section (section) 351(defun llm-tools--hl-parse-section (section)
346 "Parse SECTION (body after `@@ PATH`) into a list of `hl-op` structs. 352 "Parse SECTION (body after `@@ PATH`) into a list of `hl-op` structs.
347Skips comment lines (starting with `#`) and blank lines." 353Skips comment lines (starting with `#`) and blank lines."
348 (let (ops op) 354 (let (ops op)
349 (dolist (line (string-lines section)) 355 (dolist (line (string-lines section))
350 (when (string-match "\\S-" line) 356 (let ((trimmed (string-trim-left line)))
351 (let ((op-char (substring line 0 1))) 357 (when (string-match "\\S-" trimmed)
352 (pcase op-char 358 (let ((op-char (substring trimmed 0 1)))
353 ("~" 359 (pcase op-char
354 (when op 360 ("~"
355 (setf (hl-op-payload op) 361 (when op
356 (append (hl-op-payload op) 362 (setf (hl-op-payload op)
357 (list (substring line 1)))))) 363 (append (hl-op-payload op)
358 ("#" nil) 364 (list (substring trimmed 1))))))
359 (_ 365 ("#" nil)
360 (when op (push op ops)) 366 (_
361 (setq op (llm-tools--hl-parse-op-line line))))))) 367 (when op (push op ops))
368 (if (< (length trimmed) 3)
369 (error "Malformed operation line (too short): %S" trimmed))
370 (setq op (llm-tools--hl-parse-op-line trimmed))))))))
362 (when op (push op ops)) 371 (when op (push op ops))
363 (nreverse ops))) 372 (nreverse ops)))
364 373