aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEllis KenyƑ <elken@users.noreply.github.com>2022-09-03 19:22:35 +0100
committerGitHub <noreply@github.com>2022-09-03 11:22:35 -0700
commit9b745df2fabff6edad07f0c50cb6821d6d48d1b6 (patch)
treec0129bc76dece3cffe3c0126a49d568deded7414
parent4d59a9b696385fe42d10c7eb822579e4f6a408a1 (diff)
Add emacs-lisp formatting (#102)
* feat: add emacs-lisp formatting * Disable indent-tabs-mode * Add stub file for installation * Fix lint errors * fix: correctly format based on previous mode * Formatting * Fix weird indent * Add checkindent target * Update changelog * Long line * Empty commit * fix ci * revert changelog reformatting * more changelog * more Co-authored-by: Radon Rosborough <radon.neon@gmail.com> Co-authored-by: Radon Rosborough <radon@intuitiveexplanations.com>
-rw-r--r--.github/workflows/lint.yml2
-rw-r--r--CHANGELOG.md2
-rw-r--r--Makefile23
-rw-r--r--apheleia.el69
-rw-r--r--scripts/apheleia-indent.el6
-rwxr-xr-xtest/formatters/apheleia-ft.el127
-rw-r--r--test/formatters/installers/lisp-indent.bash1
-rw-r--r--test/formatters/samplecode/lisp-indent/in.el5
-rw-r--r--test/formatters/samplecode/lisp-indent/out.el5
9 files changed, 162 insertions, 78 deletions
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index e06e4a7..eb79979 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -5,7 +5,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- emacs_version: [26, 27, "master"]
+ emacs_version: [26, 27, 28, "master"]
steps:
- name: Checkout
uses: actions/checkout@v2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 90b9f49..d87b17e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,9 +21,11 @@ The format is based on [Keep a Changelog].
* [bean-format](https://github.com/beancount/beancount) for Beancount
([#101]).
* [stylua](https://github.com/JohnnyMorganz/StyLua) for Lua ([#105]).
+* Native Emacs indentation of Emacs Lisp code as a formatter ([#102]).
[#100]: https://github.com/radian-software/apheleia/pull/100
[#101]: https://github.com/radian-software/apheleia/pull/101
+[#102]: https://github.com/radian-software/apheleia/pull/102
[#105]: https://github.com/radian-software/apheleia/pull/105
[#109]: https://github.com/radian-software/apheleia/issues/109
[#110]: https://github.com/radian-software/apheleia/pull/110
diff --git a/Makefile b/Makefile
index 1f3ccd2..b5aea8f 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,5 @@
+SHELL := bash
+
VERSION ?=
CMD ?=
@@ -8,6 +10,7 @@ TAG ?= latest
# The order is important for compilation.
for_compile := *.el
for_checkdoc := *.el
+for_checkindent := *.el
.PHONY: help
help: ## Show this message
@@ -19,7 +22,7 @@ help: ## Show this message
column -t -s'|' >&2
.PHONY: lint
-lint: compile checkdoc longlines fmt-lint ## Build project and run all linters
+lint: compile checkdoc longlines checkindent fmt-lint ## Run all fast linters
.PHONY: compile
compile: ## Check for byte-compiler errors
@@ -42,6 +45,24 @@ checkdoc: ## Check for missing or poorly formatted docstrings
| grep . && exit 1 || true ;\
done
+.PHONY: checkindent
+checkindent: ## Ensure that indentation is correct
+ @tmpdir="$$(mktemp -d)"; for file in $(for_checkindent); do \
+ echo "[checkindent] $$file" >&2; \
+ emacs -Q --batch \
+ -l scripts/apheleia-indent.el \
+ --eval "(setq inhibit-message t)" \
+ --eval "(load (expand-file-name \"apheleia.el\") nil t)" \
+ --eval "(find-file \"$$file\")" \
+ --eval "(indent-region (point-min) (point-max))" \
+ --eval "(write-file \"$$tmpdir/$$file\")"; \
+ (diff <(cat "$$file" | nl -v1 -ba | \
+ sed "s/\t/: /" | sed "s/^ */$$file:/") \
+ <(cat "$$tmpdir/$$file" | nl -v1 -ba | \
+ sed "s/\t/: /" | sed "s/^ */$$file:/") ) \
+ | grep -F ">" | grep -o "[a-z].*" | grep . && exit 1 || true; \
+ done
+
.PHONY: longlines
longlines: ## Check for long lines
@scripts/check-line-length.bash
diff --git a/apheleia.el b/apheleia.el
index a0acc54..df89471 100644
--- a/apheleia.el
+++ b/apheleia.el
@@ -363,20 +363,21 @@ NO-QUERY, and CONNECTION-TYPE."
(stderr-file (apheleia--make-temp-file run-on-remote "apheleia"))
(args
(append
- (list (car command) ; argv[0]
- (not stdin) ; If stdin we don't delete the STDIN
- ; buffer text with
- ; `call-process-region'. Otherwise we
- ; send no INFILE argument to
- ; `call-process'.
- `(,stdout ,stderr-file) ; stdout buffer and stderr file.
- ; `call-process' cannot capture
- ; stderr into a separate buffer, the
- ; best we can do is save and read
- ; from a file.
- nil) ; Do not re/display stdout as output
- ; is recieved.
- (cdr command)))) ; argv[1:]
+ (list
+ ;; argv[0]
+ (car command)
+ ;; If stdin we don't delete the STDIN buffer text with
+ ;; `call-process-region'. Otherwise we send no INFILE
+ ;; argument to `call-process'.
+ (not stdin)
+ ;; stdout buffer and stderr file. `call-process' cannot
+ ;; capture stderr into a separate buffer, the best we can
+ ;; do is save and read from a file.
+ `(,stdout ,stderr-file)
+ ;; Do not re/display stdout as output is recieved.
+ nil)
+ ;; argv[1:]
+ (cdr command))))
(unwind-protect
(let ((exit-status
(cl-letf* ((message (symbol-function #'message))
@@ -656,7 +657,9 @@ See `apheleia--run-formatters' for a description of REMOTE."
(clear-files nil)
(run-on-remote (and (eq apheleia-remote-algorithm 'remote)
remote)))
- (cl-labels ((apheleia--make-temp-file-for-rcs-patch
+ (cl-labels ((;; Weird indentation because of differences in Emacs
+ ;; indentation algorithm between 27 and 28
+ apheleia--make-temp-file-for-rcs-patch
(buffer &optional fname)
;; Ensure there's a file with the contents of `buffer' on the
;; target machine. `fname', if given, refers to an existing
@@ -826,12 +829,12 @@ machine from the machine file is available on"))
arg
(eval arg)))
if val
- if (and (consp val)
- (cl-every #'stringp val))
- append val
- else if (stringp val)
- collect val
- else do (error "Result of command evaluation must be a string \
+ if (and (consp val)
+ (cl-every #'stringp val))
+ append val
+ else if (stringp val)
+ collect val
+ else do (error "Result of command evaluation must be a string \
or list of strings: %S" arg)))
`(,input-fname ,output-fname ,stdin ,@command))))
@@ -928,6 +931,7 @@ being run, for diagnostic purposes."
(gofmt . ("gofmt"))
(google-java-format . ("google-java-format" "-"))
(isort . ("isort" "-"))
+ (lisp-indent . apheleia-indent-lisp-buffer)
(ktlint . ("ktlint" "--stdin" "-F"))
(latexindent . ("latexindent" "--logfile=/dev/null"))
(mix-format . ("mix" "format" "-"))
@@ -1000,6 +1004,26 @@ rather than using this system."
(const :tag "Name of temporary file used for output" output)))
(function :tag "Formatter function"))))
+(cl-defun apheleia-indent-lisp-buffer
+ (&key buffer scratch callback &allow-other-keys)
+ "Format a Lisp BUFFER.
+Use SCRATCH as a temporary buffer and CALLBACK to apply the
+transformation.
+
+For more implementation detail, see
+`apheleia--run-formatter-function'."
+ (with-current-buffer scratch
+ (setq-local indent-line-function
+ (buffer-local-value 'indent-line-function buffer))
+ (setq-local lisp-indent-function
+ (buffer-local-value 'lisp-indent-function buffer))
+ (funcall (with-current-buffer buffer major-mode))
+ (goto-char (point-min))
+ (let ((inhibit-message t)
+ (message-log-max nil))
+ (indent-region (point-min) (point-max)))
+ (funcall callback)))
+
(defun apheleia--run-formatters
(formatters buffer remote callback &optional stdin)
"Run one or more code formatters on the current buffer.
@@ -1054,8 +1078,10 @@ function: %s" command)))
(c-mode . clang-format)
(c++-mode . clang-format)
(caml-mode . ocamlformat)
+ (common-lisp-mode . lisp-indent)
(css-mode . prettier)
(dart-mode . dart-format)
+ (emacs-lisp-mode . lisp-indent)
(elixir-mode . mix-format)
(elm-mode . elm-format)
(fish-mode . fish-indent)
@@ -1070,6 +1096,7 @@ function: %s" command)))
(latex-mode . latexindent)
(LaTeX-mode . latexindent)
(lua-mode . stylua)
+ (lisp-mode . lisp-indent)
(nix-mode . nixfmt)
(python-mode . black)
(ruby-mode . prettier)
diff --git a/scripts/apheleia-indent.el b/scripts/apheleia-indent.el
new file mode 100644
index 0000000..039cf51
--- /dev/null
+++ b/scripts/apheleia-indent.el
@@ -0,0 +1,6 @@
+;; This file has code that is evaluated in CI before the indentation
+;; of apheleia.el is checked. This is helpful because it allows us to
+;; ensure that various things are indented correctly if they require
+;; some setup for Emacs to know how to do the right thing.
+
+;; Nothing here yet though! ^_^
diff --git a/test/formatters/apheleia-ft.el b/test/formatters/apheleia-ft.el
index 26279ad..db2422b 100755
--- a/test/formatters/apheleia-ft.el
+++ b/test/formatters/apheleia-ft.el
@@ -218,65 +218,82 @@ environment variable, defaulting to all formatters."
(out-file (replace-regexp-in-string
"/in\\([^/]+\\)" "/out\\1" in-file 'fixedcase))
(exec-path
- (append `(,(expand-file-name
- "scripts/formatters"
- (file-name-directory
- (file-truename
- ;; Borrowed with love from Magit
- (let ((load-suffixes '(".el")))
- (locate-library "apheleia"))))))
- exec-path)))
- (mapc
- (lambda (arg)
- (when (memq arg '(file filepath input output inplace))
- (cl-pushnew arg syms)))
- command)
- (when (or (memq 'file syms) (memq 'filepath syms))
- (setq in-temp-real-file (apheleia-ft--write-temp-file
- in-text extension)))
- (when (or (memq 'input syms) (memq 'inplace syms))
- (setq in-temp-file (apheleia-ft--write-temp-file
- in-text extension))
- (when (memq 'inplace syms)
- (setq out-temp-file in-temp-file)))
- (when (memq 'output syms)
- (setq out-temp-file (apheleia-ft--write-temp-file
- "" extension)))
- (setq command
- (mapcar
- (lambda (arg)
- (pcase arg
- ((or `file `filepath)
- in-temp-real-file)
- ((or `input `inplace)
- in-temp-file)
- (`output
- out-temp-file)
- (_ arg)))
- command))
- (setq command (delq 'npx command))
+ (append `(,(expand-file-name
+ "scripts/formatters"
+ (file-name-directory
+ (file-truename
+ ;; Borrowed with love from Magit
+ (let ((load-suffixes '(".el")))
+ (locate-library "apheleia"))))))
+ exec-path)))
(setq stdout-buffer (get-buffer-create
(format "*apheleia-ft-stdout-%S" formatter)))
(with-current-buffer stdout-buffer
(erase-buffer))
- (setq exit-status
- (apply
- #'call-process
- (car command)
- (unless (or (memq 'file syms)
- (memq 'input syms)
- (memq 'inplace syms))
- in-file)
- (list stdout-buffer stderr-file)
- nil
- (cdr command)))
- ;; Verify that formatter succeeded.
- (unless (zerop exit-status)
- (with-temp-buffer
- (insert-file-contents stderr-file)
- (princ (buffer-string)))
- (error
- "Formatter %s exited with status %S" formatter exit-status))
+ (if (functionp command)
+ (progn
+ (setq in-temp-file (apheleia-ft--write-temp-file
+ in-text extension))
+ (with-current-buffer (find-file-noselect in-temp-file)
+ (funcall command
+ :buffer (current-buffer)
+ :scratch (current-buffer)
+ :formatter formatter
+ :callback (lambda ()))
+ (copy-to-buffer stdout-buffer (point-min) (point-max))))
+ (progn
+
+ (with-current-buffer stdout-buffer
+ (erase-buffer))
+ (mapc
+ (lambda (arg)
+ (when (memq arg '(file filepath input output inplace))
+ (cl-pushnew arg syms)))
+ command)
+ (when (or (memq 'file syms) (memq 'filepath syms))
+ (setq in-temp-real-file (apheleia-ft--write-temp-file
+ in-text extension)))
+ (when (or (memq 'input syms) (memq 'inplace syms))
+ (setq in-temp-file (apheleia-ft--write-temp-file
+ in-text extension))
+ (when (memq 'inplace syms)
+ (setq out-temp-file in-temp-file)))
+ (when (memq 'output syms)
+ (setq out-temp-file (apheleia-ft--write-temp-file
+ "" extension)))
+ (setq command
+ (mapcar
+ (lambda (arg)
+ (pcase arg
+ ((or `file `filepath)
+ in-temp-real-file)
+ ((or `input `inplace)
+ in-temp-file)
+ (`output
+ out-temp-file)
+ (_ arg)))
+ command))
+ (setq command (delq 'npx command))
+ (setq stdout-buffer (get-buffer-create
+ (format "*apheleia-ft-stdout-%S" formatter)))
+ (setq exit-status
+ (apply
+ #'call-process
+ (car command)
+ (unless (or (memq 'file syms)
+ (memq 'input syms)
+ (memq 'inplace syms))
+ in-file)
+ (list stdout-buffer stderr-file)
+ nil
+ (cdr command)))
+ ;; Verify that formatter succeeded.
+ (unless (zerop exit-status)
+ (with-temp-buffer
+ (insert-file-contents stderr-file)
+ (princ (buffer-string)))
+ (error
+ "Formatter %s exited with status %S" formatter exit-status))))
;; Verify that formatter has not touched original file.
(when in-temp-real-file
(let ((in-text-now (apheleia-ft--read-file in-temp-real-file)))
diff --git a/test/formatters/installers/lisp-indent.bash b/test/formatters/installers/lisp-indent.bash
new file mode 100644
index 0000000..97dbfb0
--- /dev/null
+++ b/test/formatters/installers/lisp-indent.bash
@@ -0,0 +1 @@
+# Nothing to do here, this formatter is pure Emacs!
diff --git a/test/formatters/samplecode/lisp-indent/in.el b/test/formatters/samplecode/lisp-indent/in.el
new file mode 100644
index 0000000..3e9200d
--- /dev/null
+++ b/test/formatters/samplecode/lisp-indent/in.el
@@ -0,0 +1,5 @@
+;; -*- indent-tabs-mode: nil -*-
+(if (and (< 3 5)
+ (= 1 1))
+ (message "true")
+(message "false"))
diff --git a/test/formatters/samplecode/lisp-indent/out.el b/test/formatters/samplecode/lisp-indent/out.el
new file mode 100644
index 0000000..2b8c5a2
--- /dev/null
+++ b/test/formatters/samplecode/lisp-indent/out.el
@@ -0,0 +1,5 @@
+;; -*- indent-tabs-mode: nil -*-
+(if (and (< 3 5)
+ (= 1 1))
+ (message "true")
+ (message "false"))