From 826b9c669ee2687aa7dc7d8901aa9fefe656fcbe Mon Sep 17 00:00:00 2001 From: Andor Kesselman Date: Sat, 7 Jan 2023 14:01:12 +0530 Subject: [PATCH 1/9] d2 fmt --- d2-mode.el | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/d2-mode.el b/d2-mode.el index 9801a57..61dceda 100644 --- a/d2-mode.el +++ b/d2-mode.el @@ -130,6 +130,28 @@ STR is the declaration." (cons (- l (line-number-at-pos)) (current-indentation)) (cons -1 -1))))) +(defcustom d2fmt-command "d2fmt" + "The 'd2fmt' command." + :type 'string + :group 'd2) + +(defcustom d2fmt-args nil + "Additional arguments to pass to d2fmt." + :type '(repeat string) + :group 'd2) + +(defcustom d2fmt-show-errors 'buffer + "Where to display d2fmt error output. +It can either be displayed in its own buffer, in the echo area, or not at all. +Please note that Emacs outputs to the echo area when writing +files and will overwrite d2fmt's echo output if used from inside +a `before-save-hook'." + :type '(choice + (const :tag "Own buffer" buffer) + (const :tag "Echo area" echo) + (const :tag "None" nil)) + :group 'd2) + (defun d2-compile () "Compile the current d2 file using d2." (interactive) @@ -199,6 +221,81 @@ Optional argument BROWSE whether to open the browser." (interactive) (browse-url "https://github.com/terrastruct/d2")) + +(defun d2fmt() + "Format the current buffer according to the formatting tool. + + Inspired by gofmt. + + The tool used can be set via ‘d2fmt-command’ (default: d2fmt) and additional + arguments can be set as a list via ‘d2fmt-args’." + + (interactive) + (let ((tmpfile (make-nearby-temp-file "d2fmt" nil ".d2")) + (patchbuf (get-buffer-create "*d2fmt patch*")) + (errbuf (if gofmt-show-errors (get-buffer-create "*d2fmt Errors*"))) + (coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8) + our-gofmt-args) + + (unwind-protect + (save-restriction + (widen) + (if errbuf + (with-current-buffer errbuf + (setq buffer-read-only nil) + (erase-buffer))) + (with-current-buffer patchbuf + (erase-buffer)) + + (write-region nil nil tmpfile) + + (message "Calling d2fmt: %s %s" d2fmt-command our-gofmt-args) + (if (zerop (apply #'process-file gofmt-command nil errbuf nil our-gofmt-args)) + (progn + (if (zerop (let ((local-copy (file-local-copy tmpfile))) + (unwind-protect + (call-process-region + (point-min) (point-max) "diff" nil patchbuf + nil "-n" "-" (or local-copy tmpfile)) + (when local-copy (delete-file local-copy))))) + (message "Buffer is already d2fmt") + (go--apply-rcs-patch patchbuf) + (message "Applied d2fmt")) + (if errbuf (d2--kill-error-buffer errbuf))) + (message "Could not apply d2fmt") + (if errbuf (d2fmt--process-errors (buffer-file-name) tmpfile errbuf)))) + (kill-buffer patchbuf) + (delete-file tmpfile)))) + + + +(defun d2fmt--kill-error-buffer (errbuf) + (let ((win (get-buffer-window errbuf))) + (if win + (quit-window t win) + (kill-buffer errbuf)))) + +(defun d2fmt--process-errors (filename tmpfile errbuf) + (with-current-buffer errbuf + (if (eq d2fmt-show-errors 'echo) + (progn + (message "%s" (buffer-string)) + (d2fmt--kill-error-buffer errbuf)) + ;; Convert the gofmt stderr to something understood by the compilation mode. + (goto-char (point-min)) + (insert "d2fmt errors:\n") + (let ((truefile + (if (--is-goimports-p) + (concat (file-name-directory filename) (file-name-nondirectory tmpfile)) + tmpfile))) + (while (search-forward-regexp + (concat "^\\(" (regexp-quote (file-local-name truefile)) + "\\):") + nil t) + (replace-match (file-name-nondirectory filename) t t nil 1))) + (compilation-mode) + (display-buffer errbuf)))) (defvar d2-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "C-c C-c") 'd2-compile) @@ -221,6 +318,17 @@ Optional argument BROWSE whether to open the browser." (setq-local comment-end "") (setq-local comment-start-skip "%%+ *")) + +(defun d2fmt-before-save () + "Add this to .emacs to run gofmt on the current buffer when saving: +\(add-hook 'before-save-hook 'gofmt-before-save). +Note that this will cause ‘go-mode’ to get loaded the first time +you save any file, kind of defeating the point of autoloading." + + (interactive) + (when (eq major-mode 'd2-mode) (d2fmt))) + + (provide 'd2-mode) ;;; d2-mode.el ends here From d5781b5490354211524cfb634abda62a8c097ac7 Mon Sep 17 00:00:00 2001 From: Andor Kesselman Date: Sat, 7 Jan 2023 14:11:14 +0530 Subject: [PATCH 2/9] fix docstring --- d2-mode.el | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/d2-mode.el b/d2-mode.el index 61dceda..da0fd04 100644 --- a/d2-mode.el +++ b/d2-mode.el @@ -225,7 +225,7 @@ Optional argument BROWSE whether to open the browser." (defun d2fmt() "Format the current buffer according to the formatting tool. - Inspired by gofmt. + Inspired by d2fmt. The tool used can be set via ‘d2fmt-command’ (default: d2fmt) and additional arguments can be set as a list via ‘d2fmt-args’." @@ -233,10 +233,10 @@ Optional argument BROWSE whether to open the browser." (interactive) (let ((tmpfile (make-nearby-temp-file "d2fmt" nil ".d2")) (patchbuf (get-buffer-create "*d2fmt patch*")) - (errbuf (if gofmt-show-errors (get-buffer-create "*d2fmt Errors*"))) + (errbuf (if d2fmt-show-errors (get-buffer-create "*d2fmt Errors*"))) (coding-system-for-read 'utf-8) (coding-system-for-write 'utf-8) - our-gofmt-args) + our-d2fmt-args) (unwind-protect (save-restriction @@ -250,8 +250,8 @@ Optional argument BROWSE whether to open the browser." (write-region nil nil tmpfile) - (message "Calling d2fmt: %s %s" d2fmt-command our-gofmt-args) - (if (zerop (apply #'process-file gofmt-command nil errbuf nil our-gofmt-args)) + (message "Calling d2fmt: %s %s" d2fmt-command our-d2fmt-args) + (if (zerop (apply #'process-file d2fmt-command nil errbuf nil our-d2fmt-args)) (progn (if (zerop (let ((local-copy (file-local-copy tmpfile))) (unwind-protect @@ -260,7 +260,6 @@ Optional argument BROWSE whether to open the browser." nil "-n" "-" (or local-copy tmpfile)) (when local-copy (delete-file local-copy))))) (message "Buffer is already d2fmt") - (go--apply-rcs-patch patchbuf) (message "Applied d2fmt")) (if errbuf (d2--kill-error-buffer errbuf))) (message "Could not apply d2fmt") @@ -271,25 +270,28 @@ Optional argument BROWSE whether to open the browser." (defun d2fmt--kill-error-buffer (errbuf) + "Utility function that kill the error buffer. +Argument ERRBUF the buffer which contains the error message." (let ((win (get-buffer-window errbuf))) (if win (quit-window t win) (kill-buffer errbuf)))) (defun d2fmt--process-errors (filename tmpfile errbuf) + "Utility function to process errors +Argument FILENAME the input file. +Argument TMPFILE the path to the temporary file. +Argument TMPFILE error buffer." (with-current-buffer errbuf (if (eq d2fmt-show-errors 'echo) (progn (message "%s" (buffer-string)) (d2fmt--kill-error-buffer errbuf)) - ;; Convert the gofmt stderr to something understood by the compilation mode. + ;; Convert the d2fmt stderr to something understood by the compilation mode. (goto-char (point-min)) (insert "d2fmt errors:\n") (let ((truefile - (if (--is-goimports-p) - (concat (file-name-directory filename) (file-name-nondirectory tmpfile)) - tmpfile))) - (while (search-forward-regexp + (while (search-forward-regexp (concat "^\\(" (regexp-quote (file-local-name truefile)) "\\):") nil t) @@ -320,9 +322,9 @@ Optional argument BROWSE whether to open the browser." (defun d2fmt-before-save () - "Add this to .emacs to run gofmt on the current buffer when saving: -\(add-hook 'before-save-hook 'gofmt-before-save). -Note that this will cause ‘go-mode’ to get loaded the first time + "Add this to .emacs to run d2fmt on the current buffer when saving: +\(add-hook 'before-save-hook 'd2fmt-before-save). +Note that this will cause ‘d2-mode’ to get loaded the first time you save any file, kind of defeating the point of autoloading." (interactive) From ee0d55fb534efc74d91daddbaf40c8d6e777790a Mon Sep 17 00:00:00 2001 From: Andor Kesselman Date: Sat, 7 Jan 2023 14:15:19 +0530 Subject: [PATCH 3/9] removed d2fmt command --- d2-mode.el | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/d2-mode.el b/d2-mode.el index da0fd04..fafddf8 100644 --- a/d2-mode.el +++ b/d2-mode.el @@ -130,11 +130,6 @@ STR is the declaration." (cons (- l (line-number-at-pos)) (current-indentation)) (cons -1 -1))))) -(defcustom d2fmt-command "d2fmt" - "The 'd2fmt' command." - :type 'string - :group 'd2) - (defcustom d2fmt-args nil "Additional arguments to pass to d2fmt." :type '(repeat string) @@ -224,11 +219,11 @@ Optional argument BROWSE whether to open the browser." (defun d2fmt() "Format the current buffer according to the formatting tool. - - Inspired by d2fmt. - - The tool used can be set via ‘d2fmt-command’ (default: d2fmt) and additional - arguments can be set as a list via ‘d2fmt-args’." +Code was heavily inspired by gofmt. +https://github.com/dominikh/go-mode.el/\ +blob/166dfb1e090233c4609a50c2ec9f57f113c1da72/go-mode.el +The tool used can be set via ‘d2-location’ (default: d2fmt) and additional +arguments can be set as a list via ‘d2fmt-args’." (interactive) (let ((tmpfile (make-nearby-temp-file "d2fmt" nil ".d2")) @@ -250,8 +245,8 @@ Optional argument BROWSE whether to open the browser." (write-region nil nil tmpfile) - (message "Calling d2fmt: %s %s" d2fmt-command our-d2fmt-args) - (if (zerop (apply #'process-file d2fmt-command nil errbuf nil our-d2fmt-args)) + (message "Calling d2fmt: %s %s" d2-location our-d2fmt-args) + (if (zerop (apply #'process-file d2-location nil errbuf nil our-d2fmt-args)) (progn (if (zerop (let ((local-copy (file-local-copy tmpfile))) (unwind-protect From 385ae038e170dfad95706afb203a95a00a8a05d2 Mon Sep 17 00:00:00 2001 From: Andor Kesselman Date: Sat, 7 Jan 2023 14:16:48 +0530 Subject: [PATCH 4/9] fixed line spacing --- d2-mode.el | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/d2-mode.el b/d2-mode.el index fafddf8..eec9c60 100644 --- a/d2-mode.el +++ b/d2-mode.el @@ -262,8 +262,6 @@ arguments can be set as a list via ‘d2fmt-args’." (kill-buffer patchbuf) (delete-file tmpfile)))) - - (defun d2fmt--kill-error-buffer (errbuf) "Utility function that kill the error buffer. Argument ERRBUF the buffer which contains the error message." @@ -293,6 +291,7 @@ Argument TMPFILE error buffer." (replace-match (file-name-nondirectory filename) t t nil 1))) (compilation-mode) (display-buffer errbuf)))) + (defvar d2-mode-map (let ((map (make-sparse-keymap))) (define-key map (kbd "C-c C-c") 'd2-compile) @@ -315,7 +314,6 @@ Argument TMPFILE error buffer." (setq-local comment-end "") (setq-local comment-start-skip "%%+ *")) - (defun d2fmt-before-save () "Add this to .emacs to run d2fmt on the current buffer when saving: \(add-hook 'before-save-hook 'd2fmt-before-save). From 285b3e0ccb3d521eefba36e180e129337c374e04 Mon Sep 17 00:00:00 2001 From: Andor Kesselman Date: Sat, 7 Jan 2023 14:24:25 +0530 Subject: [PATCH 5/9] updated README with d2fmt instructions --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 07fe388..7f57c70 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,14 @@ Note: All compile commands will open the output in a buffer to view the resultin ## Customization +### `d2fmt` on Save + +Add this to your `init.el` file to auto fmt on save + +```elisp +(add-hook 'before-save-hook #d2fmt-before-save) +``` + ### `d2` binary location You can specify the location of `d2` with the variable `d2-location`, the default assumes you have the binary in your `PATH` (and for that you probably want/need to install [`d2`](https://github.com/andorsk/d2-mode)). From 5580f8cbcc6c4687f97d9f2ab4841eb082fe10be Mon Sep 17 00:00:00 2001 From: Andor Kesselman Date: Sat, 7 Jan 2023 16:52:25 +0530 Subject: [PATCH 6/9] wip: cleanup --- d2-mode.el | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/d2-mode.el b/d2-mode.el index eec9c60..906f505 100644 --- a/d2-mode.el +++ b/d2-mode.el @@ -230,8 +230,8 @@ arguments can be set as a list via ‘d2fmt-args’." (patchbuf (get-buffer-create "*d2fmt patch*")) (errbuf (if d2fmt-show-errors (get-buffer-create "*d2fmt Errors*"))) (coding-system-for-read 'utf-8) - (coding-system-for-write 'utf-8) - our-d2fmt-args) + (out-d2fmt-args "") + (coding-system-for-write 'utf-8)) (unwind-protect (save-restriction @@ -246,7 +246,7 @@ arguments can be set as a list via ‘d2fmt-args’." (write-region nil nil tmpfile) (message "Calling d2fmt: %s %s" d2-location our-d2fmt-args) - (if (zerop (apply #'process-file d2-location nil errbuf nil our-d2fmt-args)) + (if (zerop (apply #'process-file d2-location nil errbuf nil "")) (progn (if (zerop (let ((local-copy (file-local-copy tmpfile))) (unwind-protect @@ -256,7 +256,7 @@ arguments can be set as a list via ‘d2fmt-args’." (when local-copy (delete-file local-copy))))) (message "Buffer is already d2fmt") (message "Applied d2fmt")) - (if errbuf (d2--kill-error-buffer errbuf))) + (if errbuf (d2fmt--kill-error-buffer errbuf))) (message "Could not apply d2fmt") (if errbuf (d2fmt--process-errors (buffer-file-name) tmpfile errbuf)))) (kill-buffer patchbuf) @@ -283,7 +283,7 @@ Argument TMPFILE error buffer." ;; Convert the d2fmt stderr to something understood by the compilation mode. (goto-char (point-min)) (insert "d2fmt errors:\n") - (let ((truefile + (let ((truefile tmpfile)) (while (search-forward-regexp (concat "^\\(" (regexp-quote (file-local-name truefile)) "\\):") From 22ad6ea2c48c5a867b6edc13ef93daed4fe88f1e Mon Sep 17 00:00:00 2001 From: Andor Kesselman Date: Sat, 7 Jan 2023 16:59:15 +0530 Subject: [PATCH 7/9] small tweaks --- d2-mode.el | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2-mode.el b/d2-mode.el index 906f505..8ac51d3 100644 --- a/d2-mode.el +++ b/d2-mode.el @@ -230,7 +230,7 @@ arguments can be set as a list via ‘d2fmt-args’." (patchbuf (get-buffer-create "*d2fmt patch*")) (errbuf (if d2fmt-show-errors (get-buffer-create "*d2fmt Errors*"))) (coding-system-for-read 'utf-8) - (out-d2fmt-args "") + (our-d2fmt-args "") (coding-system-for-write 'utf-8)) (unwind-protect @@ -271,10 +271,10 @@ Argument ERRBUF the buffer which contains the error message." (kill-buffer errbuf)))) (defun d2fmt--process-errors (filename tmpfile errbuf) - "Utility function to process errors + "Utility function that provides error handling. Argument FILENAME the input file. Argument TMPFILE the path to the temporary file. -Argument TMPFILE error buffer." +Argument ERRBUF error buffer." (with-current-buffer errbuf (if (eq d2fmt-show-errors 'echo) (progn From ec18ae006ddcde3712ff8c424bb64dbad14923dc Mon Sep 17 00:00:00 2001 From: Andor Kesselman Date: Sat, 7 Jan 2023 19:29:47 +0530 Subject: [PATCH 8/9] styling updates --- d2-mode.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/d2-mode.el b/d2-mode.el index 8ac51d3..dbc97c6 100644 --- a/d2-mode.el +++ b/d2-mode.el @@ -316,7 +316,7 @@ Argument ERRBUF error buffer." (defun d2fmt-before-save () "Add this to .emacs to run d2fmt on the current buffer when saving: -\(add-hook 'before-save-hook 'd2fmt-before-save). +\=(add-hook 'before-save-hook 'd2fmt-before-save). Note that this will cause ‘d2-mode’ to get loaded the first time you save any file, kind of defeating the point of autoloading." From 303df66ae94fa41fa409504e85675d68018aa053 Mon Sep 17 00:00:00 2001 From: Andor Kesselman Date: Sat, 7 Jan 2023 19:58:56 +0530 Subject: [PATCH 9/9] wip: d2-format. checkpoint --- d2-mode.el | 59 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/d2-mode.el b/d2-mode.el index dbc97c6..8e1a15f 100644 --- a/d2-mode.el +++ b/d2-mode.el @@ -130,16 +130,16 @@ STR is the declaration." (cons (- l (line-number-at-pos)) (current-indentation)) (cons -1 -1))))) -(defcustom d2fmt-args nil - "Additional arguments to pass to d2fmt." +(defcustom d2format-args nil + "Additional arguments to pass to d2format." :type '(repeat string) :group 'd2) -(defcustom d2fmt-show-errors 'buffer - "Where to display d2fmt error output. +(defcustom d2format-show-errors 'buffer + "Where to display d2format error output. It can either be displayed in its own buffer, in the echo area, or not at all. Please note that Emacs outputs to the echo area when writing -files and will overwrite d2fmt's echo output if used from inside +files and will overwrite d2format's echo output if used from inside a `before-save-hook'." :type '(choice (const :tag "Own buffer" buffer) @@ -217,20 +217,20 @@ Optional argument BROWSE whether to open the browser." (browse-url "https://github.com/terrastruct/d2")) -(defun d2fmt() +(defun d2format() "Format the current buffer according to the formatting tool. Code was heavily inspired by gofmt. https://github.com/dominikh/go-mode.el/\ blob/166dfb1e090233c4609a50c2ec9f57f113c1da72/go-mode.el -The tool used can be set via ‘d2-location’ (default: d2fmt) and additional -arguments can be set as a list via ‘d2fmt-args’." +The tool used can be set via ‘d2-location’ (default: d2format) and additional +arguments can be set as a list via ‘d2format-args’." (interactive) - (let ((tmpfile (make-nearby-temp-file "d2fmt" nil ".d2")) - (patchbuf (get-buffer-create "*d2fmt patch*")) - (errbuf (if d2fmt-show-errors (get-buffer-create "*d2fmt Errors*"))) + (let ((tmpfile (make-nearby-temp-file "d2format" nil ".d2")) + (patchbuf (get-buffer-create "*d2format patch*")) + (errbuf (if d2format-show-errors (get-buffer-create "*d2format Errors*"))) (coding-system-for-read 'utf-8) - (our-d2fmt-args "") + (our-d2format-args "") (coding-system-for-write 'utf-8)) (unwind-protect @@ -245,24 +245,27 @@ arguments can be set as a list via ‘d2fmt-args’." (write-region nil nil tmpfile) - (message "Calling d2fmt: %s %s" d2-location our-d2fmt-args) + (message "Calling d2format: %s %s" d2-location "fmt") + (if (zerop (apply #'process-file d2-location nil errbuf nil "")) + (message "processing file") (progn (if (zerop (let ((local-copy (file-local-copy tmpfile))) + (message (concat "running local copy " local-copy)) (unwind-protect (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" (or local-copy tmpfile)) (when local-copy (delete-file local-copy))))) - (message "Buffer is already d2fmt") - (message "Applied d2fmt")) - (if errbuf (d2fmt--kill-error-buffer errbuf))) - (message "Could not apply d2fmt") - (if errbuf (d2fmt--process-errors (buffer-file-name) tmpfile errbuf)))) + (message "Buffer is already d2format") + (message "Applied d2format")) + (if errbuf (d2format--kill-error-buffer errbuf))) + (message "Could not apply d2format") + (if errbuf (d2format--process-errors (buffer-file-name) tmpfile errbuf)))) (kill-buffer patchbuf) (delete-file tmpfile)))) -(defun d2fmt--kill-error-buffer (errbuf) +(defun d2format--kill-error-buffer (errbuf) "Utility function that kill the error buffer. Argument ERRBUF the buffer which contains the error message." (let ((win (get-buffer-window errbuf))) @@ -270,19 +273,19 @@ Argument ERRBUF the buffer which contains the error message." (quit-window t win) (kill-buffer errbuf)))) -(defun d2fmt--process-errors (filename tmpfile errbuf) +(defun d2format--process-errors (filename tmpfile errbuf) "Utility function that provides error handling. Argument FILENAME the input file. Argument TMPFILE the path to the temporary file. Argument ERRBUF error buffer." (with-current-buffer errbuf - (if (eq d2fmt-show-errors 'echo) + (if (eq d2format-show-errors 'echo) (progn (message "%s" (buffer-string)) - (d2fmt--kill-error-buffer errbuf)) - ;; Convert the d2fmt stderr to something understood by the compilation mode. + (d2format--kill-error-buffer errbuf)) + ;; Convert the d2format stderr to something understood by the compilation mode. (goto-char (point-min)) - (insert "d2fmt errors:\n") + (insert "d2format errors:\n") (let ((truefile tmpfile)) (while (search-forward-regexp (concat "^\\(" (regexp-quote (file-local-name truefile)) @@ -314,14 +317,14 @@ Argument ERRBUF error buffer." (setq-local comment-end "") (setq-local comment-start-skip "%%+ *")) -(defun d2fmt-before-save () - "Add this to .emacs to run d2fmt on the current buffer when saving: -\=(add-hook 'before-save-hook 'd2fmt-before-save). +(defun d2format-before-save () + "Add this to .emacs to run d2format on the current buffer when saving: +\=(add-hook 'before-save-hook 'd2format-before-save). Note that this will cause ‘d2-mode’ to get loaded the first time you save any file, kind of defeating the point of autoloading." (interactive) - (when (eq major-mode 'd2-mode) (d2fmt))) + (when (eq major-mode 'd2-mode) (d2format))) (provide 'd2-mode)