fix(beancount): flymake-bean: false positives from relative paths

Beancount tools and Fava recognize relative paths in include and
document directives, and documents options. However, flymake-bean pipes
the buffer's contents to bean-check via /dev/stdin, so paths are
resolved relative to /dev instead of the location of the containing
beancount file, resulting in file errors. This commit expands those
relatives paths before sending the buffer's contents to bean-check to
resolve these false positives.
This commit is contained in:
Henrik Lissner
2025-05-18 00:07:30 +02:00
parent bda27228eb
commit 57b8d5fd8e
2 changed files with 65 additions and 3 deletions

View File

@ -239,3 +239,62 @@ Return non-nil if successful."
(concat beancount-timestamped-directive-regexp (concat beancount-timestamped-directive-regexp
"\\|" beancount-transaction-regexp))) "\\|" beancount-transaction-regexp)))
('search-failed (goto-char pos) nil)))) ('search-failed (goto-char pos) nil))))
;;;###autoload
(defun +beancount--flymake-bean-check--run-a (report-fn &rest _ignored)
(unless (executable-find flymake-bean-check-executable)
(error "The executable %s doesn't exist. See `flymake-bean-check-executable'"
flymake-bean-check-executable))
(when (and flymake-bean-check-process
(process-live-p flymake-bean-check-process))
(kill-process flymake-bean-check-process))
(let* ((source (current-buffer))
(buffer (generate-new-buffer "*flymake-bean-check*"))
(cache-file (flymake-bean-check-cache-filename (buffer-file-name))))
(setq flymake-bean-check-process
(make-process :buffer buffer
:name "flymake-bean-check"
:noquery t
:connection-type 'pipe
:command (list flymake-bean-check-executable
"/dev/stdin"
"--cache-filename" cache-file)
:sentinel
(lambda (proc _event)
(when (memq (process-status proc) '(exit signal))
(unwind-protect
(with-current-buffer buffer
(goto-char (point-min))
(let (result)
(while (re-search-forward flymake-bean-check-location-regexp
nil t)
(pcase-let*
((message (match-string 2))
(`(,begin . ,end) (flymake-diag-region
source
(string-to-number (match-string 1)))))
(push (flymake-make-diagnostic source begin end
:error message)
result)))
(funcall report-fn (nreverse result))))
(kill-buffer buffer))))))
(process-send-string
flymake-bean-check-process
(save-restriction
(widen)
(with-temp-buffer
(save-excursion (insert-buffer-substring source))
(while (re-search-forward (rx bol
(or (seq (= 4 num) "-" (= 2 num) "-" (= 2 num) (+ " ")
"document" (+ " ")
(+ (or alnum ":" "_" "-")))
"include"
(seq "option" (+ " ") "\"documents\""))
(+ " ") "\""
(group (+ (not "\""))))
nil t)
(replace-match (expand-file-name
(match-string-no-properties 1))
t t nil 1))
(buffer-substring-no-properties (point-min) (point-max)))))
(process-send-eof flymake-bean-check-process)))

View File

@ -26,9 +26,12 @@
:around #'beancount--fava-filter :around #'beancount--fava-filter
(funcall fn process (ansi-color-filter-apply output))) (funcall fn process (ansi-color-filter-apply output)))
(defadvice! +beancount--widen-before-flymake-bean-check--run-a (fn &rest args) ;; HACK: Widens the buffer so flymake never operates on partial buffer
:around #'flymake-bean-check--run ;; contents. Also replaces any relative file paths in include and document
(save-restriction (widen) (apply fn args))) ;; directives with an absolute path, so bean-check doesn't throw false
;; positives due to flymake-bean's implementation.
(advice-add #'flymake-bean-check--run :override #'+beancount--flymake-bean-check--run-a)
(map! :map beancount-mode-map (map! :map beancount-mode-map
:m "[[" #'+beancount/previous-transaction :m "[[" #'+beancount/previous-transaction