From 57b8d5fd8ee881a3c9aa2d05f6e578394af2af69 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sun, 18 May 2025 00:07:30 +0200 Subject: [PATCH] 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. --- modules/lang/beancount/autoload.el | 59 ++++++++++++++++++++++++++++++ modules/lang/beancount/config.el | 9 +++-- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/modules/lang/beancount/autoload.el b/modules/lang/beancount/autoload.el index 07ff15283..933d3b255 100644 --- a/modules/lang/beancount/autoload.el +++ b/modules/lang/beancount/autoload.el @@ -239,3 +239,62 @@ Return non-nil if successful." (concat beancount-timestamped-directive-regexp "\\|" beancount-transaction-regexp))) ('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))) diff --git a/modules/lang/beancount/config.el b/modules/lang/beancount/config.el index 3ea48bd8f..44ffcb1b2 100644 --- a/modules/lang/beancount/config.el +++ b/modules/lang/beancount/config.el @@ -26,9 +26,12 @@ :around #'beancount--fava-filter (funcall fn process (ansi-color-filter-apply output))) - (defadvice! +beancount--widen-before-flymake-bean-check--run-a (fn &rest args) - :around #'flymake-bean-check--run - (save-restriction (widen) (apply fn args))) + ;; HACK: Widens the buffer so flymake never operates on partial buffer + ;; contents. Also replaces any relative file paths in include and document + ;; 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 :m "[[" #'+beancount/previous-transaction