Moving to Doom Emacs!

This commit is contained in:
Derek Taylor
2019-12-16 20:21:19 -06:00
parent d9f2f456f1
commit d4b4c33550
683 changed files with 51877 additions and 100 deletions

View File

@@ -0,0 +1,8 @@
;;; editor/format/autoload/evil.el -*- lexical-binding: t; -*-
;;;###if (featurep! :editor evil)
;;;###autoload (autoload '+format:region "editor/format/autoload/evil" nil t)
(evil-define-operator +format:region (beg end)
"Evil ex interface to `+format/region'."
(interactive "<r>")
(+format/region beg end))

View File

@@ -0,0 +1,239 @@
;;; editor/format/autoload.el -*- lexical-binding: t; -*-
(defvar +format-region-p nil
"Is non-nil if currently reformatting a selected region, rather than the whole
buffer.")
;;;###autoload
(autoload 'format-all--probe "format-all")
(defun +format--delete-whole-line (&optional arg)
"Delete the current line without putting it in the `kill-ring'.
Derived from function `kill-whole-line'. ARG is defined as for that
function.
Stolen shamelessly from go-mode"
(setq arg (or arg 1))
(if (and (> arg 0)
(eobp)
(save-excursion (forward-visible-line 0) (eobp)))
(signal 'end-of-buffer nil))
(if (and (< arg 0)
(bobp)
(save-excursion (end-of-visible-line) (bobp)))
(signal 'beginning-of-buffer nil))
(cond ((zerop arg)
(delete-region (progn (forward-visible-line 0) (point))
(progn (end-of-visible-line) (point))))
((< arg 0)
(delete-region (progn (end-of-visible-line) (point))
(progn (forward-visible-line (1+ arg))
(unless (bobp)
(backward-char))
(point))))
((delete-region (progn (forward-visible-line 0) (point))
(progn (forward-visible-line arg) (point))))))
;;;###autoload
(defun +format--apply-rcs-patch (patch-buffer)
"Apply an RCS-formatted diff from PATCH-BUFFER to the current buffer.
Stolen shamelessly from go-mode"
(let ((target-buffer (current-buffer))
;; Relative offset between buffer line numbers and line numbers
;; in patch.
;;
;; Line numbers in the patch are based on the source file, so
;; we have to keep an offset when making changes to the
;; buffer.
;;
;; Appending lines decrements the offset (possibly making it
;; negative), deleting lines increments it. This order
;; simplifies the forward-line invocations.
(line-offset 0)
(column (current-column)))
(save-excursion
(with-current-buffer patch-buffer
(goto-char (point-min))
(while (not (eobp))
(unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
(error "Invalid rcs patch or internal error in +format--apply-rcs-patch"))
(forward-line)
(let ((action (match-string 1))
(from (string-to-number (match-string 2)))
(len (string-to-number (match-string 3))))
(cond
((equal action "a")
(let ((start (point)))
(forward-line len)
(let ((text (buffer-substring start (point))))
(with-current-buffer target-buffer
(cl-decf line-offset len)
(goto-char (point-min))
(forward-line (- from len line-offset))
(insert text)))))
((equal action "d")
(with-current-buffer target-buffer
(goto-char (point-min))
(forward-line (1- (- from line-offset)))
(cl-incf line-offset len)
(+format--delete-whole-line len)))
((error "Invalid rcs patch or internal error in +format--apply-rcs-patch")))))))
(move-to-column column)))
(defun +format--current-indentation ()
(save-excursion
(goto-char (point-min))
(skip-chars-forward " \t\n")
(current-indentation)))
;;
;; Public library
(defun +format-completing-read ()
(require 'format-all)
(let* ((fmtlist (mapcar #'symbol-name (hash-table-keys format-all--format-table)))
(fmt (completing-read "Formatter: " fmtlist)))
(if fmt (cons (intern fmt) t))))
;;;###autoload
(defun +format-probe-a (orig-fn)
"Use `+format-with' instead, if it is set."
(if +format-with
(list +format-with t)
(funcall orig-fn)))
;;;###autoload
(defun +format-buffer (formatter mode-result &optional preserve-indent-p)
"Format the source code in the current buffer.
Returns any of the following values:
'unknown No formatter is defined for this major-mode
'error Couldn't format buffer due to formatter errors
'noop Buffer is already formatted
Otherwise, returns a list: (list OUTPUT ERRORS FIRST-DIFF), where OUTPUT is the
formatted text, ERRORS are any errors in string format, and FIRST-DIFF is the
position of the first change in the buffer.
See `+format/buffer' for the interactive version of this function, and
`+format-buffer-h' to use as a `before-save-hook' hook."
(if (not formatter)
'no-formatter
(let ((f-function (gethash formatter format-all--format-table))
(executable (format-all--formatter-executable formatter))
(indent 0))
(pcase-let
((`(,output ,errput ,first-diff)
;; Since `format-all' functions (and various formatting functions,
;; like `gofmt') widen the buffer, in order to only format a region of
;; text, we must make a copy of the buffer to apply formatting to.
(let ((output (buffer-substring-no-properties (point-min) (point-max)))
(origin-buffer-file-name (buffer-file-name (buffer-base-buffer)))
(origin-default-directory default-directory))
(with-temp-buffer
(with-silent-modifications
(insert output)
;; Ensure this temp buffer _seems_ as much like the origin
;; buffer as possible.
(setq default-directory origin-default-directory
buffer-file-name origin-buffer-file-name)
;; Since we're piping a region of text to the formatter, remove
;; any leading indentation to make it look like a file.
(when preserve-indent-p
(setq indent (+format--current-indentation))
(when (> indent 0)
(indent-rigidly (point-min) (point-max) (- indent))))
(funcall f-function executable mode-result))))))
(unwind-protect
(cond ((null output) 'error)
((eq output t) 'noop)
((let ((tmpfile (make-temp-file "doom-format"))
(patchbuf (get-buffer-create " *doom format patch*"))
(coding-system-for-read coding-system-for-read)
(coding-system-for-write coding-system-for-write))
(unless IS-WINDOWS
(setq coding-system-for-read 'utf-8
coding-system-for-write 'utf-8))
(unwind-protect
(progn
(with-current-buffer patchbuf
(erase-buffer))
(with-temp-file tmpfile
(erase-buffer)
(insert output)
(when (> indent 0)
;; restore indentation without affecting new
;; indentation
(indent-rigidly (point-min) (point-max)
(max 0 (- indent (+format--current-indentation))))))
(if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile))
'noop
(+format--apply-rcs-patch patchbuf)
(list output errput first-diff)))
(kill-buffer patchbuf)
(delete-file tmpfile)))))
(unless (= 0 (length errput))
(message "Formatter error output:\n%s" errput)))))))
;;
;; Commands
;;;###autoload
(defun +format/buffer (&optional arg)
"Format the source code in the current buffer."
(interactive "P")
(let ((+format-with (or (if arg (+format-completing-read)) +format-with)))
(pcase-let ((`(,formatter ,mode-result) (format-all--probe)))
(pcase
(+format-buffer
formatter mode-result
(or +format-preserve-indentation +format-region-p))
(`no-formatter
(when (called-interactively-p 'any)
(message "No formatter specified for %s" major-mode))
nil)
(`error (message "Failed to format buffer due to errors") nil)
(`noop (message "Buffer was already formatted") nil)
(_ (message "Formatted (%s)" formatter) t)))))
;;;###autoload
(defun +format/region (beg end &optional arg)
"Runs the active formatter on the lines within BEG and END.
WARNING: this may not work everywhere. It will throw errors if the region
contains a syntax error in isolation. It is mostly useful for formatting
snippets or single lines."
(interactive "rP")
(save-restriction
(narrow-to-region beg end)
(let ((+format-region-p t))
(+format/buffer arg))))
;;;###autoload
(defun +format/region-or-buffer ()
"Runs the active formatter on the selected region (or whole buffer, if nothing
is selected)."
(interactive)
(call-interactively
(if (use-region-p)
#'+format/region
#'+format/buffer)))
;;
;; Hooks
;;;###autoload
(defun +format-enable-on-save-h ()
"Enables formatting on save."
(add-hook 'before-save-hook #'+format-buffer-h nil t))
;;;###autoload
(defalias '+format-buffer-h #'+format/buffer
"Format the source code in the current buffer with minimal feedback.
Meant for `before-save-hook'.")

View File

@@ -0,0 +1,203 @@
;;; editor/format/autoload/settings.el -*- lexical-binding: t; -*-
;; This must be redefined here because `format-all' only makes it available at
;; compile time.
(defconst +format-system-type
(cl-case system-type
(windows-nt 'windows)
(cygwin 'windows)
(darwin 'macos)
(gnu/linux 'linux)
(berkeley-unix
(save-match-data
(let ((case-fold-search t))
(cond ((string-match "freebsd" system-configuration) 'freebsd)
((string-match "openbsd" system-configuration) 'openbsd)
((string-match "netbsd" system-configuration) 'netbsd))))))
"Current operating system according to the format-all package.")
(defun +format--resolve-system (choices)
"Get first choice matching `format-all-system-type' from CHOICES."
(cl-loop for choice in choices
if (atom choice) return choice
else if (eql +format-system-type (car choice))
return (cadr choice)))
(defun +format--make-command (formatter &rest _)
`(format-all--buffer-thunk
(lambda (input)
(with-silent-modifications
(setq buffer-file-name ,(buffer-file-name (buffer-base-buffer))
default-directory ,default-directory)
(delay-mode-hooks (funcall ',major-mode))
(insert input)
(condition-case e
(progn
(doom-log "formatter (commandp) %s" #',formatter)
(call-interactively #',formatter)
(list nil ""))
(error (list t (error-message-string e))))))))
(defun +format--make-function (formatter &rest _)
`(progn
(doom-log "formatter (functionp) %s" #',formatter)
(format-all--buffer-thunk #',formatter)))
(defun +format--make-shell-command (command ok-statuses error-regexp)
(+format--make-shell-command-list (split-string command " " t)
ok-statuses error-regexp))
(defun +format--make-shell-command-list (command-list ok-statuses error-regexp)
`(let (args)
(dolist (arg ',command-list)
(cond ((stringp arg)
(push arg args))
((listp arg)
(catch 'skip
(let (subargs this)
(while (setq this (pop arg))
(cond ((not (stringp (car arg)))
(let ((val (eval (pop arg) t)))
(unless val (throw 'skip nil))
(push (format this val) subargs)))
((stringp this)
(push this subargs))))
(setq args (append subargs args)))))))
(doom-log "formatter (arglist) %s" args)
(if ,(and (or ok-statuses error-regexp) t)
(apply #'format-all--buffer-hard
',ok-statuses ,error-regexp
(reverse args))
(apply #'format-all--buffer-easy (reverse args)))))
(cl-defun +format--set (name &key function modes unset)
(declare (indent defun))
(when (and unset (not (gethash name format-all--format-table)))
(error "'%s' formatter does not exist to be unset" name))
(puthash name function format-all--format-table)
(dolist (mode (doom-enlist modes))
(cl-destructuring-bind (m &optional probe)
(doom-enlist mode)
(if unset
(puthash m (assq-delete-all name (gethash key format-all-mode-table))
format-all-mode-table)
(format-all--pushhash
m (cons name (if probe `(lambda () ,probe)))
format-all--mode-table)))))
;;;###autodef
(cl-defun set-formatter!
(name formatter &key modes filter ok-statuses error-regexp)
"Define (or modify) a formatter named NAME.
Supported keywords: :modes :install :filter :ok-statuses :error-regexp
NAME is a symbol that identifies this formatter.
FORMATTER can be a symbol referring to another formatter, a function, string or
nested list.
If a function, it should be a formatter function that
`format-all--buffer-thunk' will accept.
If a string, it is assumed to be a shell command that the buffer's text will
be piped to (through stdin).
If a list, it should represent a shell command as a list of arguments. Each
element is either a string or list (STRING ARG) where STRING is a format
string and ARG is both a predicate and argument for STRING. If ARG is nil,
STRING will be omitted from the vector.
MODES is a major mode, a list thereof, or a list of two-element sublists with
the structure: (MAJOR-MODE FORM). FORM is evaluated when the buffer is formatted
and its return value serves two purposes:
1. It is a predicate for this formatter. Assuming the MAJOR-MODE matches the
current mode, if FORM evaluates to nil, the formatter is skipped.
2. It's return value is made available to FORMATTER if it is a function or
list of shell arguments via the `mode-result' variable.
FILTER is a function that takes three arguments: the formatted output, any error
output and the position of the first change. This function must return these
three after making whatever changes you like to them. This might be useful if
the output contains ANSI color codes that need to be stripped out (as is the
case with elm-format).
OK-STATUSES and ERROR-REGEXP are ignored if FORMATTER is not a shell command.
OK-STATUSES is a list of integer exit codes that should be treated as success
codes. However, if ERROR-REGEXP is given, and the program's stderr contains that
regexp, then the formatting is considered failed even if the exit status is in
OK-STATUSES.
Basic examples:
(set-formatter! 'asmfmt \"asmfmt\" :modes '(asm-mode nasm-mode))
(set-formatter! 'black \"black -q -\")
(set-formatter! 'html-tidy \"tidy -q -indent\" :modes '(html-mode web-mode))
Advanced examples:
(set-formatter!
'clang-format
'(\"clang-format\"
(\"-assume-filename=%S\" (or buffer-file-name mode-result \"\")))
:modes
'((c-mode \".c\")
(c++-mode \".cpp\")
(java-mode \".java\")
(objc-mode \".m\")
(protobuf-mode \".proto\")))
(set-formatter! 'html-tidy
'(\"tidy\" \"-q\" \"-indent\"
(\"-xml\" (memq major-mode '(nxml-mode xml-mode))))
:modes
'(html-mode
(web-mode (and (equal \"none\" web-mode-engine)
(car (member web-mode-content-type '(\"xml\" \"html\"))))))
:ok-statuses '(0 1)
:executable \"tidy\")
(set-formatter! 'html-tidy ; overwrite predefined html-tidy formatter
'(\"tidy\" \"-q\" \"-indent\"
\"--tidy-mark\" \"no\"
\"--drop-empty-elements\" \"no\"
\"--show-body-only\" \"auto\"
(\"--indent-spaces\" \"%d\" tab-width)
(\"--indent-with-tabs\" \"%s\" (if indent-tabs-mode \"yes\" \"no\"))
(\"-xml\" (memq major-mode '(nxml-mode xml-mode))))
:ok-statuses '(0 1)))
(set-formatter! 'elm-format
\"elm-format --yes --stdin\"
:filter
(lambda (output errput first-diff)
(list output
(format-all--remove-ansi-color errput)
first-diff)))"
(declare (indent defun))
(cl-check-type name symbol)
(after! format-all
(if (null formatter)
(+format--set name
:unset t
:modes modes)
(let ((fn (funcall (cond ((stringp formatter)
#'+format--make-shell-command)
((listp formatter)
#'+format--make-shell-command-list)
((and (commandp formatter)
(not (stringp formatter)))
#'+format--make-command)
((functionp formatter)
#'+format--make-function))
formatter
ok-statuses
error-regexp)))
(cl-check-type filter (or function null))
(+format--set name
:function
`(lambda (executable mode-result)
,(if filter `(apply #',filter ,fn) fn))
:modes modes)
name))))