Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions bake-format.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
;;; bake-format.el --- Format Makefiles using mbake -*- lexical-binding: t; -*-

;; Version: 0.1.0
;; Keywords: languages, tools
;; URL: https://github.com/EbodShojaei/bake
;; Package-Requires: ((emacs "26.1") (reformatter "0.3"))

;;; Commentary:

;; Provides `bake-format-buffer', `bake-format-region', and
;; `bake-format-on-save-mode' for formatting Makefiles via mbake.
;;
;; Requires the mbake Python package to be installed:
;; pip install mbake
;;
;; Basic setup in your Emacs config:
;;
;; (require 'bake-format)
;; (bake-format-setup)
;;
;; This enables on-save formatting in all `makefile-mode' buffers.
;; To enable it manually in a buffer: M-x bake-format-on-save-mode
;; To format once without enabling the mode: M-x bake-format-buffer

;;; Code:

(require 'reformatter)

(defgroup bake-format nil
"Format Makefiles using the mbake formatter."
:group 'languages
:link '(url-link "https://github.com/EbodShojaei/bake"))

(defcustom bake-format-command "mbake"
"Name or full path of the mbake executable."
:type 'string
:group 'bake-format)

(reformatter-define bake-format
:program bake-format-command
:args '("format" "--stdin")
:lighter " BakeFmt"
:group 'bake-format)

;;;###autoload
(defun bake-format-setup ()
"Enable `bake-format-on-save-mode' in all `makefile-mode' buffers.
Call this in your Emacs init file after loading bake-format."
(add-hook 'makefile-mode-hook #'bake-format-on-save-mode))

(provide 'bake-format)
;;; bake-format.el ends here
128 changes: 128 additions & 0 deletions flymake-bake.el
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
;;; flymake-bake.el --- Flymake backend for Makefiles using mbake -*- lexical-binding: t; -*-

;; Version: 0.1.0
;; Keywords: languages, tools
;; URL: https://github.com/EbodShojaei/bake
;; Package-Requires: ((emacs "26.1"))

;;; Commentary:

;; Provides a Flymake backend that runs `mbake validate' on the current
;; Makefile buffer and reports diagnostics inline.
;;
;; Note: `mbake validate' does not support --stdin; it requires a real
;; file on disk. This backend writes the buffer contents to a temporary
;; file, runs validation against it, then cleans up.
;;
;; Requires the mbake Python package to be installed:
;; pip install mbake
;;
;; Basic setup in your Emacs config:
;;
;; (require 'flymake-bake)
;; (flymake-bake-setup)
;;
;; Or to enable it manually in a single buffer:
;; M-x flymake-bake-load
;; M-x flymake-mode

;;; Code:

(defgroup flymake-bake nil
"Flymake backend for Makefiles using mbake."
:group 'languages
:link '(url-link "https://github.com/EbodShojaei/bake"))

(defcustom flymake-bake-program "mbake"
"Name or full path of the mbake executable."
:group 'flymake-bake
:type 'string)

(defcustom flymake-bake-program-args '("validate")
"Arguments passed to mbake before the filename.
The temporary file path is always appended as the final argument."
:group 'flymake-bake
:type '(repeat string))

;; mbake validate output format: "<file>:<line>: <message>"
;; The filename portion is the temp file path, so we match on anything
;; up to the first colon to stay flexible.
(defvar flymake-bake--output-regex
"^[^:\n]+:\\([0-9]+\\): \\(.*\\)"
"Regexp matching mbake validate diagnostic output.
Group 1 is the line number, group 2 is the message.")

(defvar-local flymake-bake--process nil
"Current flymake-bake checker process for this buffer.")

(defun flymake-bake--run-checker (report-fn &rest _args)
"Run `mbake validate' on a temp file and report diagnostics to REPORT-FN."
;; Cancel any existing process for this buffer.
(when (and flymake-bake--process
(process-live-p flymake-bake--process))
(kill-process flymake-bake--process))

(let* ((source-buffer (current-buffer))
;; Write buffer to a named temp file so mbake validate can read it.
;; Use .mk extension so mbake recognises it as a Makefile.
(tmp-file (make-temp-file "flymake-bake-" nil ".mk"))
(command (append (list flymake-bake-program)
flymake-bake-program-args
(list tmp-file))))
;; Populate the temp file with the current buffer contents.
(write-region (point-min) (point-max) tmp-file nil 'silent)

(setq flymake-bake--process
(make-process
:name "flymake-bake"
:buffer (generate-new-buffer " *flymake-bake*")
:command command
:noquery t
:connection-type 'pipe
:sentinel
(lambda (process _event)
(when (eq (process-status process) 'exit)
(unwind-protect
(if (buffer-live-p source-buffer)
(with-current-buffer (process-buffer process)
(goto-char (point-min))
(let ((diagnostics nil))
(while (re-search-forward
flymake-bake--output-regex nil t)
(let* ((line (string-to-number (match-string 1)))
(msg (match-string 2))
(region (flymake-diag-region
source-buffer line))
(diag (flymake-make-diagnostic
source-buffer
(car region)
(cdr region)
:error
msg)))
(push diag diagnostics)))
(funcall report-fn diagnostics)))
;; Source buffer was killed before we finished; nothing to do.
(funcall report-fn nil))
;; Always clean up, regardless of errors.
(ignore-errors (delete-file tmp-file))
(kill-buffer (process-buffer process)))))))))

;;;###autoload
(defun flymake-bake-load ()
"Register the mbake Flymake backend in the current buffer.
Enable `flymake-mode' separately, or use `flymake-bake-setup' to
do both automatically via a hook."
(interactive)
(add-hook 'flymake-diagnostic-functions #'flymake-bake--run-checker nil t))

;;;###autoload
(defun flymake-bake-setup ()
"Enable the mbake Flymake backend in all `makefile-mode' buffers.
Call this in your Emacs init file after loading flymake-bake."
(add-hook 'makefile-mode-hook
(lambda ()
(flymake-bake-load)
(flymake-mode 1))))

(provide 'flymake-bake)
;;; flymake-bake.el ends here
Loading