diff options
Diffstat (limited to 'llm-tools-hl.el')
| -rw-r--r-- | llm-tools-hl.el | 105 |
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. |
| 277 | Handles ranges by returning both endpoints." | 277 | Handles 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 | 287 | Only 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. |
| 330 | LINE should start with `+`, `<`, `-`, or `=`." | 338 | LINE must start with `+`, `<`, `-`, or `=`, followed by a space, |
| 331 | (pcase (substring line 0 1) | 339 | followed 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. |
| 347 | Skips comment lines (starting with `#`) and blank lines." | 353 | Skips 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 | ||
