Major optimization refactor, across the board
+ enable lexical-scope everywhere (lexical-binding = t): ~5-10% faster startup; ~5-20% general boost + reduce consing, function calls & garbage collection by preferring cl-loop & dolist over lambda closures (for mapc[ar], add-hook, and various cl-lib filter/map/reduce functions) -- where possible + prefer functions with dedicated opcodes, like assq (see byte-defop's in bytecomp.el for more) + prefer pcase & cond (faster) over cl-case + general refactor for code readability + ensure naming & style conventions are adhered to + appease byte-compiler by marking unused variables with underscore + defer minor mode activation to after-init, emacs-startup or window-setup hooks; a customization opportunity for users + ensures custom functionality won't interfere with startup.
This commit is contained in:
parent
64a142b3fc
commit
c7254e7bdc
154 changed files with 1101 additions and 1118 deletions
|
@ -1,4 +1,4 @@
|
|||
;;; autoload.el
|
||||
;;; core/autoload/buffers.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar-local doom-buffer--narrowed-origin nil)
|
||||
|
||||
|
@ -25,20 +25,19 @@ the buffer (if narrowed).
|
|||
|
||||
Inspired from http://demonastery.org/2013/04/emacs-evil-narrow-region/"
|
||||
(interactive "r")
|
||||
(if (region-active-p)
|
||||
(progn
|
||||
(deactivate-mark)
|
||||
(when clone-p
|
||||
(let ((old-buf (current-buffer)))
|
||||
(switch-to-buffer (clone-indirect-buffer nil nil))
|
||||
(setq doom-buffer--narrowed-origin old-buf)))
|
||||
(narrow-to-region beg end))
|
||||
(if doom-buffer--narrowed-origin
|
||||
(progn
|
||||
(kill-this-buffer)
|
||||
(switch-to-buffer doom-buffer--narrowed-origin)
|
||||
(setq doom-buffer--narrowed-origin nil))
|
||||
(widen))))
|
||||
(cond ((region-active-p)
|
||||
(deactivate-mark)
|
||||
(when clone-p
|
||||
(let ((old-buf (current-buffer)))
|
||||
(switch-to-buffer (clone-indirect-buffer nil nil))
|
||||
(setq doom-buffer--narrowed-origin old-buf)))
|
||||
(narrow-to-region beg end))
|
||||
(doom-buffer--narrowed-origin
|
||||
(kill-this-buffer)
|
||||
(switch-to-buffer doom-buffer--narrowed-origin)
|
||||
(setq doom-buffer--narrowed-origin nil))
|
||||
(t
|
||||
(widen))))
|
||||
|
||||
|
||||
;; Buffer Life and Death ;;;;;;;;;;;;;;;
|
||||
|
@ -52,11 +51,12 @@ the current workspace."
|
|||
(persp-buffer-list-restricted)
|
||||
(buffer-list)))
|
||||
(project-root (and project-p (doom-project-root t))))
|
||||
(if project-root
|
||||
(funcall (if (eq project-p 'not) #'cl-remove-if #'cl-remove-if-not)
|
||||
(lambda (b) (projectile-project-buffer-p b project-root))
|
||||
buffers)
|
||||
buffers)))
|
||||
(cond (project-root
|
||||
(cl-loop for buf in buffers
|
||||
if (projectile-project-buffer-p buf project-root)
|
||||
collect buf))
|
||||
(t
|
||||
buffers))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-real-buffers-list (&optional buffer-list)
|
||||
|
@ -99,8 +99,9 @@ only the buried buffers in BUFFER-LIST (a list of BUFFER-OR-NAMEs)."
|
|||
(defun doom-matching-buffers (pattern &optional buffer-list)
|
||||
"Get a list of all buffers (in the current workspace OR in BUFFER-LIST) that
|
||||
match the regex PATTERN."
|
||||
(cl-remove-if-not (lambda (buf) (string-match-p pattern (buffer-name buf)))
|
||||
(or buffer-list (doom-buffer-list))))
|
||||
(cl-loop for buf in (or buffer-list (doom-buffer-list))
|
||||
when (string-match-p pattern (buffer-name buf))
|
||||
collect buf))
|
||||
|
||||
(defun doom--cycle-real-buffers (&optional n)
|
||||
"Switch to the next buffer N times (previous, if N < 0), skipping over unreal
|
||||
|
@ -119,7 +120,7 @@ buffers. If there's nothing left, switch to `doom-fallback-buffer'. See
|
|||
;; BUFFERS? Because `switch-to-next-buffer' and
|
||||
;; `switch-to-prev-buffer' properly update buffer list order.
|
||||
(while (not (memq (current-buffer) buffers))
|
||||
(dotimes (i (abs n))
|
||||
(dotimes (_i (abs n))
|
||||
(funcall move-func))))))
|
||||
(when (eq (current-buffer) (doom-fallback-buffer))
|
||||
(cd project-dir))
|
||||
|
@ -201,14 +202,15 @@ switched to a real buffer."
|
|||
;;;###autoload
|
||||
(defun doom-kill-buffer-and-windows (buffer)
|
||||
"Kill the buffer and delete all the windows it's displayed in."
|
||||
(unless (one-window-p t)
|
||||
(mapc (lambda (win) (unless (one-window-p t) (delete-window win)))
|
||||
(get-buffer-window-list buffer)))
|
||||
(dolist (window (get-buffer-window-list buffer))
|
||||
(unless (one-window-p t)
|
||||
(delete-window window)))
|
||||
(kill-buffer buffer))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-kill-process-buffers ()
|
||||
"Kill all processes that have no visible associated buffers."
|
||||
"Kill all processes that have no visible associated buffers. Return number of
|
||||
processes killed."
|
||||
(interactive)
|
||||
(let ((n 0))
|
||||
(dolist (p (process-list))
|
||||
|
@ -220,7 +222,7 @@ switched to a real buffer."
|
|||
(not (buffer-live-p process-buffer)))))
|
||||
(message "Killing %s" (process-name p))
|
||||
(delete-process p)
|
||||
(setq n (1+ n)))))
|
||||
(cl-incf n))))
|
||||
n))
|
||||
|
||||
;;;###autoload
|
||||
|
@ -253,11 +255,11 @@ belong to the current project in this workspace."
|
|||
"Kill all other buffers in this workspace. If PROJECT-P, kill only the other
|
||||
buffers that belong to the current project."
|
||||
(interactive "P")
|
||||
(let ((buffers (doom-buffer-list project-p)))
|
||||
(mapc (lambda (buf)
|
||||
(unless (eq buf (current-buffer))
|
||||
(doom-kill-buffer-and-windows buf)))
|
||||
buffers)
|
||||
(let ((buffers (doom-buffer-list project-p))
|
||||
(current-buffer (current-buffer)))
|
||||
(dolist (buf buffers)
|
||||
(unless (eq buf current-buffer)
|
||||
(doom-kill-buffer-and-windows buf)))
|
||||
(when (called-interactively-p 'interactive)
|
||||
(message "Killed %s buffers" (length buffers)))))
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; debug.el
|
||||
;;; core/autoload/debug.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/what-face (&optional pos)
|
||||
|
@ -22,8 +22,9 @@
|
|||
;;;###autoload
|
||||
(defun doom-active-minor-modes ()
|
||||
"Get a list of active minor-mode symbols."
|
||||
(cl-remove-if (lambda (m) (and (boundp m) (symbol-value m)))
|
||||
minor-mode-list))
|
||||
(cl-loop for mode in minor-mode-list
|
||||
unless (and (boundp mode) (symbol-value mode))
|
||||
collect mode))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/what-minor-mode (mode)
|
||||
|
@ -44,13 +45,13 @@ selection of all minor-modes, active or not."
|
|||
(declare (interactive-only t))
|
||||
(interactive)
|
||||
(if-let (bad-hosts
|
||||
(loop for bad
|
||||
in `("https://wrong.host.badssl.com/"
|
||||
"https://self-signed.badssl.com/")
|
||||
if (condition-case e
|
||||
(url-retrieve bad (lambda (retrieved) t))
|
||||
(error nil))
|
||||
collect bad))
|
||||
(cl-loop for bad
|
||||
in '("https://wrong.host.badssl.com/"
|
||||
"https://self-signed.badssl.com/")
|
||||
if (condition-case _e
|
||||
(url-retrieve bad (lambda (_retrieved) t))
|
||||
(error nil))
|
||||
collect bad))
|
||||
(error (format "tls seems to be misconfigured (it got %s)."
|
||||
bad-hosts))
|
||||
(url-retrieve "https://badssl.com"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; editor.el
|
||||
;;; core/autoload/editor.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/sudo-find-file (file)
|
||||
|
@ -65,7 +65,7 @@ If already there, do nothing."
|
|||
(looking-at-p match-str))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/dumb-indent (&optional smart)
|
||||
(defun doom/dumb-indent ()
|
||||
"Inserts a tab character (or spaces x tab-width)."
|
||||
(interactive)
|
||||
(if indent-tabs-mode
|
||||
|
@ -142,11 +142,13 @@ possible, or just one char if that's not possible."
|
|||
"Checks if point is surrounded by {} [] () delimiters and adds a
|
||||
space on either side of the point if so."
|
||||
(interactive)
|
||||
(let ((command (or (command-remapping #'self-insert-command) #'self-insert-command)))
|
||||
(if (doom--surrounded-p)
|
||||
(progn (call-interactively command)
|
||||
(save-excursion (call-interactively command)))
|
||||
(call-interactively command))))
|
||||
(let ((command (or (command-remapping #'self-insert-command)
|
||||
#'self-insert-command)))
|
||||
(cond ((doom--surrounded-p)
|
||||
(call-interactively command)
|
||||
(save-excursion (call-interactively command)))
|
||||
(t
|
||||
(call-interactively command)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/deflate-space-maybe ()
|
||||
|
@ -155,22 +157,24 @@ spaces on either side of the point if so. Resorts to
|
|||
`doom/backward-delete-whitespace-to-column' otherwise."
|
||||
(interactive)
|
||||
(save-match-data
|
||||
(if (doom--surrounded-p)
|
||||
(let ((whitespace-match (match-string 1)))
|
||||
(cond ((not whitespace-match)
|
||||
(call-interactively #'delete-backward-char))
|
||||
((string-match "\n" whitespace-match)
|
||||
(funcall (if (featurep 'evil) #'evil-delete #'delete-region)
|
||||
(point-at-bol) (point))
|
||||
(call-interactively #'delete-backward-char)
|
||||
(save-excursion (call-interactively #'delete-char)))
|
||||
(t (just-one-space 0))))
|
||||
(doom/backward-delete-whitespace-to-column))))
|
||||
(cond ((doom--surrounded-p)
|
||||
(let ((whitespace-match (match-string 1)))
|
||||
(cond ((not whitespace-match)
|
||||
(call-interactively #'delete-backward-char))
|
||||
((string-match "\n" whitespace-match)
|
||||
(funcall (if (featurep 'evil) #'evil-delete #'delete-region)
|
||||
(point-at-bol) (point))
|
||||
(call-interactively #'delete-backward-char)
|
||||
(save-excursion (call-interactively #'delete-char)))
|
||||
(t (just-one-space 0)))))
|
||||
(t
|
||||
(doom/backward-delete-whitespace-to-column)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/newline-and-indent ()
|
||||
"Inserts a newline and possibly indents it. Also cotinues comments if executed
|
||||
from a commented line."
|
||||
"Inserts a newline and possibly indents it. Also continues comments if
|
||||
executed from a commented line; handling special cases for certain languages
|
||||
with weak native support."
|
||||
(interactive)
|
||||
(cond ((sp-point-in-string)
|
||||
(newline))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; ../core/autoload/help.el
|
||||
;;; core/autoload/help.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/describe-setting (setting)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; memoize.el
|
||||
;;; core/autoload/memoize.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defvar doom-memoized-table (make-hash-table :test 'equal :size 10)
|
||||
|
@ -23,7 +23,9 @@ and the value is the function's return value.")
|
|||
"Create a memoize'd function. NAME, ARGLIST, DOCSTRING and BODY
|
||||
have the same meaning as in `defun'."
|
||||
(declare (indent defun) (doc-string 3))
|
||||
`(progn
|
||||
`(,(if (bound-and-true-p byte-compile-current-file)
|
||||
'with-no-warnings
|
||||
'progn)
|
||||
(defun ,name ,arglist ,@body)
|
||||
(doom-memoize ',name)))
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; message.el
|
||||
;;; core/autoload/message.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defconst doom-message-fg
|
||||
'((reset . 0)
|
||||
|
@ -40,12 +40,12 @@
|
|||
"An alternative to `format' that strips out ANSI codes if used in an
|
||||
interactive session."
|
||||
`(cl-flet*
|
||||
(,@(mapcar
|
||||
(lambda (rule)
|
||||
`(,(car rule)
|
||||
(lambda (message &rest args)
|
||||
(apply #'doom-ansi-apply ',(car rule) message args))))
|
||||
(append doom-message-fg doom-message-bg doom-message-fx))
|
||||
(,@(cl-loop for rule
|
||||
in (append doom-message-fg doom-message-bg doom-message-fx)
|
||||
collect
|
||||
`(,(car rule)
|
||||
(lambda (message &rest args)
|
||||
(apply #'doom-ansi-apply ',(car rule) message args))))
|
||||
(color (symbol-function 'doom-ansi-apply)))
|
||||
(format ,message ,@args)))
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; ../core/autoload/minibuffer.el
|
||||
;;; core/autoload/minibuffer.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/minibuffer-kill-word ()
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; packages.el
|
||||
;;; core/autoload/packages.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar doom--last-refresh nil)
|
||||
|
||||
|
@ -15,10 +14,11 @@
|
|||
(progn
|
||||
(message "Refreshing package archives")
|
||||
(package-refresh-contents (not doom-debug-mode))
|
||||
(let ((i 0))
|
||||
(while package--downloads-in-progress
|
||||
(sleep-for 0 250))
|
||||
(persistent-soft-store 'last-pkg-refresh t "emacs" 900)))
|
||||
(cl-loop for i from 0
|
||||
while (and package--downloads-in-progress
|
||||
(<= i 10))
|
||||
do (sleep-for 0 250))
|
||||
(persistent-soft-store 'last-pkg-refresh t "emacs" 900))
|
||||
(error
|
||||
(doom-refresh-clear-cache)
|
||||
(message "Failed to refresh packages: %s" (cadr ex))))))
|
||||
|
@ -85,7 +85,7 @@ list of the package."
|
|||
(plist-get (cdr (assq name doom-packages)) :freeze))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-get-packages (&optional backend)
|
||||
(defun doom-get-packages ()
|
||||
"Retrieves a list of explicitly installed packages (i.e. non-dependencies).
|
||||
Each element is a cons cell, whose car is the package symbol and whose cdr is
|
||||
the quelpa recipe (if any).
|
||||
|
@ -96,12 +96,12 @@ the packages relevant to that backend.
|
|||
Warning: this function is expensive; it re-evaluates all of doom's config files.
|
||||
Be careful not to use it in a loop."
|
||||
(doom-initialize-packages t)
|
||||
(delq nil
|
||||
(mapcar (lambda (pkgsym)
|
||||
(or (assq pkgsym doom-packages)
|
||||
(list (car (assq pkgsym package-alist)))))
|
||||
(cl-delete-duplicates
|
||||
(append doom-core-packages (mapcar #'car doom-packages))))))
|
||||
(cl-loop with packages = (append doom-core-packages (mapcar #'car doom-packages))
|
||||
for sym in (cl-delete-duplicates packages)
|
||||
if (or (assq sym doom-packages)
|
||||
(and (assq sym package-alist)
|
||||
(list sym)))
|
||||
collect it))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-get-depending-on (name)
|
||||
|
@ -124,11 +124,11 @@ containing (PACKAGE-SYMBOL OLD-VERSION-LIST NEW-VERSION-LIST).
|
|||
If INCLUDE-FROZEN-P is non-nil, check frozen packages as well.
|
||||
|
||||
Used by `doom/packages-update'."
|
||||
(let ((pkgs (mapcar #'car (doom-get-packages))))
|
||||
(delq nil
|
||||
(mapcar #'doom-package-outdated-p
|
||||
(if include-frozen-p pkgs
|
||||
(cl-remove-if #'doom-package-frozen-p pkgs))))))
|
||||
(cl-loop for pkg in (doom-get-packages)
|
||||
if (or (and (doom-package-frozen-p (car pkg))
|
||||
include-frozen-p)
|
||||
(doom-package-outdated-p (car pkg)))
|
||||
collect it))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-get-orphaned-packages ()
|
||||
|
@ -152,13 +152,14 @@ If INCLUDE-IGNORED-P is non-nil, includes missing packages that are ignored,
|
|||
i.e. they have an :ignore property.
|
||||
|
||||
Used by `doom/packages-install'."
|
||||
(cl-remove-if (lambda (pkgsym)
|
||||
(let ((pkg (car pkgsym)))
|
||||
(or (assq pkg package-alist)
|
||||
(unless include-ignored-p (doom-package-ignored-p pkg))
|
||||
(and (not (plist-get (assq pkg doom-packages) :pin))
|
||||
(assq pkg package--builtins)))))
|
||||
(doom-get-packages)))
|
||||
(cl-loop for pkgsym in (doom-get-packages)
|
||||
unless
|
||||
(let ((pkg (car pkgsym)))
|
||||
(or (assq pkg package-alist)
|
||||
(unless include-ignored-p (doom-package-ignored-p pkg))
|
||||
(and (not (plist-get (assq pkg doom-packages) :pin))
|
||||
(assq pkg package--builtins))))
|
||||
collect pkgsym))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom*package-delete (desc &rest _)
|
||||
|
@ -180,16 +181,14 @@ Used by `doom/packages-install'."
|
|||
|
||||
(defun doom--packages-choose (prompt)
|
||||
(doom-initialize)
|
||||
(let* ((table (mapcar
|
||||
(lambda (p) (cons (package-desc-full-name p) p))
|
||||
(delq nil
|
||||
(mapcar (lambda (p) (unless (package-built-in-p p) p))
|
||||
(apply #'append (mapcar #'cdr package-alist))))))
|
||||
(name (completing-read
|
||||
prompt
|
||||
(mapcar #'car table)
|
||||
nil t)))
|
||||
(cdr (assoc name table))))
|
||||
(let ((table (cl-loop for pkg in package-alist
|
||||
unless (package-built-in-p (cdr pkg))
|
||||
collect (cons (package-desc-full-name (cdr pkg))
|
||||
(cdr pkg)))))
|
||||
(cdr (assoc (completing-read prompt
|
||||
(mapcar #'car table)
|
||||
nil t)
|
||||
table))))
|
||||
|
||||
(defmacro doom--condition-case! (&rest body)
|
||||
`(condition-case ex
|
||||
|
@ -229,20 +228,21 @@ example; the package name can be omitted)."
|
|||
(user-error "%s is already installed" name))
|
||||
(let ((plist (or plist (cdr (assq name doom-packages))))
|
||||
(inhibit-message (not doom-debug-mode))
|
||||
(recipe (plist-get plist :recipe)))
|
||||
(recipe (plist-get plist :recipe))
|
||||
quelpa-upgrade-p)
|
||||
(cond (recipe (quelpa recipe))
|
||||
(t (package-install name))))
|
||||
(when (package-installed-p name)
|
||||
(cl-pushnew (cons name plist) doom-packages :test #'eq :key #'car)
|
||||
t))
|
||||
(t (package-install name)))
|
||||
(when (package-installed-p name)
|
||||
(cl-pushnew (cons name plist) doom-packages :test #'eq :key #'car)
|
||||
t)))
|
||||
|
||||
(defun doom-update-package (name)
|
||||
(defun doom-update-package (name &optional force-p)
|
||||
"Updates package NAME if it is out of date, using quelpa or package.el as
|
||||
appropriate."
|
||||
(doom-initialize)
|
||||
(unless (package-installed-p name)
|
||||
(user-error "%s isn't installed" name))
|
||||
(when (doom-package-outdated-p name)
|
||||
(when (or force-p (doom-package-outdated-p name))
|
||||
(let ((inhibit-message (not doom-debug-mode))
|
||||
(desc (cadr (assq name package-alist))))
|
||||
(pcase (doom-package-backend name)
|
||||
|
@ -258,12 +258,11 @@ appropriate."
|
|||
(package-compute-transaction (list archive) (package-desc-reqs archive))
|
||||
(package-compute-transaction () (list (list archive))))))
|
||||
(package-download-transaction packages))))
|
||||
(when-let (old-dir (package-desc-dir desc))
|
||||
(when (file-directory-p old-dir)
|
||||
(delete-directory old-dir t)))
|
||||
(version-list-<
|
||||
(package-desc-version desc)
|
||||
(package-desc-version (cadr (assq name package-alist)))))))
|
||||
(unless (doom-package-outdated-p name)
|
||||
(when-let (old-dir (package-desc-dir desc))
|
||||
(when (file-directory-p old-dir)
|
||||
(delete-directory old-dir t)))
|
||||
t))))
|
||||
|
||||
(defun doom-delete-package (name &optional force-p)
|
||||
"Uninstalls package NAME if it exists, and clears it from `quelpa-cache'."
|
||||
|
@ -358,7 +357,7 @@ appropriate."
|
|||
(message! "Updating %s" (car pkg))
|
||||
(doom--condition-case!
|
||||
(message!
|
||||
(let ((result (doom-update-package (car pkg))))
|
||||
(let ((result (doom-update-package (car pkg) t)))
|
||||
(color (if result 'green 'red)
|
||||
" %s"
|
||||
(if result "DONE" "FAILED"))))))
|
||||
|
@ -450,7 +449,7 @@ calls."
|
|||
(if (y-or-n-p (format "%s will be updated from %s to %s. Update?"
|
||||
package old-v-str new-v-str))
|
||||
(message "%s %s (%s => %s)"
|
||||
(if (doom-update-package package) "Updated" "Failed to update")
|
||||
(if (doom-update-package package t) "Updated" "Failed to update")
|
||||
package old-v-str new-v-str)
|
||||
(message "Aborted")))
|
||||
(message "%s is up-to-date" package))))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; popups.el
|
||||
;;; core/autoload/popups.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-popup-p (&optional target)
|
||||
|
@ -13,7 +13,8 @@ current window if omitted."
|
|||
|
||||
;;;###autoload
|
||||
(defun doom-popup-buffer (buffer &rest plist)
|
||||
"Display BUFFER in a shackle popup. See `shackle-rules' for possible rules."
|
||||
"Display BUFFER in a shackle popup. See `shackle-rules' for possible rules.
|
||||
Returns the new popup window."
|
||||
(declare (indent defun))
|
||||
(unless (bufferp buffer)
|
||||
(error "%s is not a valid buffer" buffer))
|
||||
|
@ -106,9 +107,7 @@ only close popups that have an :autoclose property in their rule (see
|
|||
`shackle-rules')."
|
||||
(interactive)
|
||||
(when-let (popups (doom-popup-windows))
|
||||
(let ((orig-win (selected-window))
|
||||
success
|
||||
doom-popup-remember-history)
|
||||
(let (success doom-popup-remember-history)
|
||||
(setq doom-popup-history (mapcar #'doom--popup-data popups))
|
||||
(dolist (window popups)
|
||||
(when (or force-p
|
||||
|
@ -145,6 +144,7 @@ only close popups that have an :autoclose property in their rule (see
|
|||
|
||||
;;;###autoload
|
||||
(defun doom-popup-prop (prop &optional window)
|
||||
"Returns a `doom-popup-rules' PROPerty from WINDOW."
|
||||
(or (plist-get (or (if window
|
||||
(buffer-local-value 'doom-popup-rules (window-buffer window))
|
||||
doom-popup-rules)
|
||||
|
@ -157,6 +157,7 @@ only close popups that have an :autoclose property in their rule (see
|
|||
|
||||
;;;###autoload
|
||||
(defun doom-popup-side (&optional window)
|
||||
"Return what side a popup WINDOW came from ('left 'right 'above or 'below)."
|
||||
(let ((align (doom-popup-prop :align window)))
|
||||
(when (eq align t)
|
||||
(setq align shackle-default-alignment))
|
||||
|
@ -166,6 +167,7 @@ only close popups that have an :autoclose property in their rule (see
|
|||
|
||||
;;;###autoload
|
||||
(defun doom-popup-size (&optional window)
|
||||
"Return the size of a popup WINDOW."
|
||||
(let ((side (doom-popup-side window)))
|
||||
(cond ((memq side '(left right))
|
||||
(window-width window))
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
;;; system.el
|
||||
(provide 'core-lib-system)
|
||||
;;; core/autoload/system.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-system-os (&optional os)
|
||||
|
@ -17,9 +16,8 @@ is given, returns t if it matches the current system, and nil otherwise."
|
|||
((memq system-type '(windows-nt cygwin))
|
||||
'windows)
|
||||
(t (error "Unknown OS: %s" system-type)))))
|
||||
(if os
|
||||
(eq os type)
|
||||
type)))
|
||||
(or (and os (eq os type))
|
||||
type)))
|
||||
|
||||
;;;###autoload
|
||||
(defun doom-sh (command &rest args)
|
||||
|
@ -34,6 +32,7 @@ is given, returns t if it matches the current system, and nil otherwise."
|
|||
(t
|
||||
(princ (shell-command-to-string (apply #'format command args)))))))
|
||||
|
||||
(defvar tramp-verbose)
|
||||
;;;###autoload
|
||||
(defun doom-sudo (command &rest args)
|
||||
"Like `doom-sh', but runs as root (prompts for password)."
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; ../core/autoload/test.el
|
||||
;;; core/autoload/test.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defmacro def-test-group! (name &rest body)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; ui.el
|
||||
;;; core/autoload/ui.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/toggle-fullscreen ()
|
||||
|
@ -15,7 +15,7 @@
|
|||
(interactive "P")
|
||||
(cond ((featurep 'nlinum)
|
||||
(nlinum-mode (or arg (if nlinum-mode -1 +1))))
|
||||
((featurep 'linum-mode)
|
||||
((featurep 'linum)
|
||||
(linum-mode (or arg (if linum-mode -1 +1))))
|
||||
(t
|
||||
(error "No line number plugin detected"))))
|
||||
|
@ -38,7 +38,6 @@ window changes before then, the undo expires."
|
|||
(delete-other-windows)))
|
||||
|
||||
(defvar doom--window-enlargened nil)
|
||||
|
||||
;;;###autoload
|
||||
(defun doom/window-enlargen ()
|
||||
"Enlargen the current window. Activate again to undo."
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; core-editor.el --- filling the editor shaped hole in the Emacs OS
|
||||
;;; core-editor.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar doom-large-file-size 1
|
||||
"Size (in MB) above which the user will be prompted to open the file literally
|
||||
|
@ -48,10 +48,12 @@ modes are active and the buffer is read-only.")
|
|||
vc-follow-symlinks t)
|
||||
|
||||
;; ediff: use existing frame instead of creating a new one
|
||||
(add-hook! 'ediff-load-hook
|
||||
(defun doom|init-ediff ()
|
||||
(setq ediff-diff-options "-w"
|
||||
ediff-split-window-function #'split-window-horizontally
|
||||
ediff-window-setup-function #'ediff-setup-windows-plain)) ; no extra frames
|
||||
;; no extra frames
|
||||
ediff-window-setup-function #'ediff-setup-windows-plain))
|
||||
(add-hook 'ediff-load-hook #'doom|init-ediff)
|
||||
|
||||
(defun doom|dont-kill-scratch-buffer ()
|
||||
"Don't kill the scratch buffer."
|
||||
|
@ -61,8 +63,7 @@ modes are active and the buffer is read-only.")
|
|||
|
||||
(defun doom*delete-trailing-whitespace (orig-fn &rest args)
|
||||
"Don't affect trailing whitespace on current line."
|
||||
(let ((spaces (1- (current-column)))
|
||||
(linestr (buffer-substring-no-properties
|
||||
(let ((linestr (buffer-substring-no-properties
|
||||
(line-beginning-position)
|
||||
(line-end-position))))
|
||||
(apply orig-fn args)
|
||||
|
@ -106,7 +107,7 @@ fundamental-mode) for performance sake."
|
|||
savehist-autosave-interval nil ; save on kill only
|
||||
savehist-additional-variables '(kill-ring search-ring regexp-search-ring)
|
||||
save-place-file (concat doom-cache-dir "saveplace"))
|
||||
(add-hook! 'after-init-hook #'(savehist-mode save-place-mode))
|
||||
(add-hook! 'emacs-startup-hook #'(savehist-mode save-place-mode))
|
||||
|
||||
;; Keep track of recently opened files
|
||||
(def-package! recentf
|
||||
|
@ -145,10 +146,12 @@ fundamental-mode) for performance sake."
|
|||
(t (error "%s is an invalid action for :editorconfig" action)))))
|
||||
|
||||
:config
|
||||
(editorconfig-mode +1)
|
||||
;; Show whitespace in tabs indentation mode
|
||||
(add-hook! 'editorconfig-custom-hooks
|
||||
(if indent-tabs-mode (whitespace-mode +1))))
|
||||
(add-hook 'emacs-startup-hook #'editorconfig-mode)
|
||||
|
||||
(defun doom|editorconfig-whitespace-mode-maybe (&rest _)
|
||||
"Show whitespace-mode when file uses TABS (ew)."
|
||||
(if indent-tabs-mode (whitespace-mode +1)))
|
||||
(add-hook 'editorconfig-custom-hooks #'doom|editorconfig-whitespace-mode-maybe))
|
||||
|
||||
;; Auto-close delimiters and blocks as you type
|
||||
(def-package! smartparens
|
||||
|
@ -161,7 +164,7 @@ fundamental-mode) for performance sake."
|
|||
sp-max-pair-length 3)
|
||||
|
||||
:config
|
||||
(smartparens-global-mode 1)
|
||||
(add-hook 'emacs-startup-hook #'smartparens-global-mode)
|
||||
(require 'smartparens-config)
|
||||
;; Smartparens interferes with Replace mode
|
||||
(add-hook 'evil-replace-state-entry-hook #'turn-off-smartparens-mode)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
;;; core-keybinds.el
|
||||
;;; core-keybinds.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; A centralized keybinds system, integrated with `which-key' to preview
|
||||
;; available keys, and `evil', if it's enabled. All built into one powerful
|
||||
;; macro: `map!'.
|
||||
;; available keybindings. All built into one powerful macro: `map!'. If evil is
|
||||
;; never loaded, then evil bindings set with `map!' will be ignored.
|
||||
|
||||
(defvar doom-leader-key "SPC"
|
||||
"The leader prefix key, for global commands.")
|
||||
|
@ -11,13 +11,13 @@
|
|||
"The localleader prefix key, for major-mode specific commands.")
|
||||
|
||||
(defvar doom-evil-state-alist
|
||||
'(("n" . normal)
|
||||
("v" . visual)
|
||||
("i" . insert)
|
||||
("e" . emacs)
|
||||
("o" . operator)
|
||||
("m" . motion)
|
||||
("r" . replace))
|
||||
'((?n . normal)
|
||||
(?v . visual)
|
||||
(?i . insert)
|
||||
(?e . emacs)
|
||||
(?o . operator)
|
||||
(?m . motion)
|
||||
(?r . replace))
|
||||
"A list of cons cells that map a letter to a evil state symbol.")
|
||||
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
|||
;; embolden local bindings
|
||||
(set-face-attribute 'which-key-local-map-description-face nil :weight 'bold)
|
||||
(which-key-setup-side-window-bottom)
|
||||
(which-key-mode +1))
|
||||
(add-hook 'window-setup-hook #'which-key-mode))
|
||||
|
||||
|
||||
;;
|
||||
|
@ -56,14 +56,11 @@ IGNORE is a list of keyword letters that should be ignored.
|
|||
|
||||
For example, :nvi will map to (list 'normal 'visual 'insert). See
|
||||
`doom-evil-state-alist' to customize this."
|
||||
(delq nil
|
||||
(mapcar (lambda (l)
|
||||
(let ((state (cdr (assoc l doom-evil-state-alist))))
|
||||
(unless state
|
||||
(unless (or (eq ignore t) (member l ignore))
|
||||
(error "not a valid state: %s" l)))
|
||||
state))
|
||||
(split-string (substring (symbol-name keyword) 1) "" t))))
|
||||
(cl-loop for l across (substring (symbol-name keyword) 1)
|
||||
if (cdr (assq l doom-evil-state-alist))
|
||||
collect it
|
||||
else if (not (or (eq ignore t) (member l ignore)))
|
||||
do (error "not a valid state: %s" l)))
|
||||
|
||||
|
||||
;; Register keywords for proper indentation (see `map!')
|
||||
|
@ -79,6 +76,11 @@ For example, :nvi will map to (list 'normal 'visual 'insert). See
|
|||
(put ':localleader 'lisp-indent-function 'defun)
|
||||
(put ':textobj 'lisp-indent-function 'defun)
|
||||
|
||||
;; specials
|
||||
(defvar doom--keymaps nil)
|
||||
(defvar doom--prefix nil)
|
||||
(defvar doom--defer nil)
|
||||
|
||||
(defmacro map! (&rest rest)
|
||||
"A nightmare of a key-binding macro that will use `evil-define-key*',
|
||||
`define-key', `local-set-key' and `global-set-key' depending on context and
|
||||
|
@ -130,9 +132,9 @@ Example
|
|||
(:when IS-MAC
|
||||
:n \"M-s\" 'some-fn
|
||||
:i \"M-o\" (lambda (interactive) (message \"Hi\"))))"
|
||||
(let ((keymaps (if (boundp 'keymaps) keymaps))
|
||||
(prefix (if (boundp 'prefix) prefix))
|
||||
(defer (if (boundp 'defer) defer))
|
||||
(let ((doom--keymaps doom--keymaps)
|
||||
(doom--prefix doom--prefix)
|
||||
(doom--defer doom--defer)
|
||||
local key def states forms desc modes)
|
||||
(while rest
|
||||
(setq key (pop rest))
|
||||
|
@ -156,19 +158,19 @@ Example
|
|||
(:unless (push `(if (not ,(pop rest)) ,(macroexpand `(map! ,@rest))) forms) (setq rest '()))
|
||||
(:after (push `(after! ,(pop rest) ,(macroexpand `(map! ,@rest))) forms) (setq rest '()))
|
||||
(:desc (setq desc (pop rest)))
|
||||
(:map* (setq defer t) (push :map rest))
|
||||
(:map* (setq doom--defer t) (push :map rest))
|
||||
(:map
|
||||
(setq keymaps
|
||||
(setq doom--keymaps
|
||||
(let ((car (pop rest)))
|
||||
(if (listp car) car (list car)))))
|
||||
(:mode
|
||||
(setq modes
|
||||
(let ((car (pop rest)))
|
||||
(if (listp car) car (list car))))
|
||||
(unless keymaps
|
||||
(setq keymaps
|
||||
(mapcar (lambda (m) (intern (format "%s-map" (symbol-name m))))
|
||||
modes))))
|
||||
(unless doom--keymaps
|
||||
(setq doom--keymaps
|
||||
(cl-loop for m in modes
|
||||
collect (intern (format "%s-map" (symbol-name m)))))))
|
||||
(:textobj
|
||||
(let* ((key (pop rest))
|
||||
(inner (pop rest))
|
||||
|
@ -178,20 +180,20 @@ Example
|
|||
forms)))
|
||||
(:prefix
|
||||
(let ((def (pop rest)))
|
||||
(setq prefix `(vconcat ,prefix (kbd ,def)))
|
||||
(setq doom--prefix `(vconcat ,doom--prefix (kbd ,def)))
|
||||
(when desc
|
||||
(push `(doom--keybind-register ,(key-description (eval prefix))
|
||||
(push `(doom--keybind-register ,(key-description (eval doom--prefix))
|
||||
,desc ',modes)
|
||||
forms)
|
||||
(setq desc nil))))
|
||||
(otherwise ; might be a state prefix
|
||||
(_ ; might be a state doom--prefix
|
||||
(setq states (doom--keyword-to-states key '("L")))
|
||||
(let (case-fold-search)
|
||||
(when (string-match-p "L" (symbol-name key))
|
||||
(setq local t)
|
||||
(cond ((= (length states) 0)
|
||||
(user-error "local keybinding for %s must accompany another state" key))
|
||||
((> (length keymaps) 0)
|
||||
((> (length doom--keymaps) 0)
|
||||
(user-error "local keybinding for %s cannot accompany a keymap" key))))))))
|
||||
|
||||
;; It's a key-def pair
|
||||
|
@ -205,8 +207,8 @@ Example
|
|||
(setq key `(kbd ,key)))
|
||||
((stringp key)
|
||||
(setq key (kbd key))))
|
||||
(when prefix
|
||||
(setq key (append prefix (list key))))
|
||||
(when doom--prefix
|
||||
(setq key (append doom--prefix (list key))))
|
||||
(unless (> (length rest) 0)
|
||||
(user-error "map! has no definition for %s key" key))
|
||||
(setq def (pop rest))
|
||||
|
@ -214,10 +216,10 @@ Example
|
|||
(push `(doom--keybind-register ,(key-description (eval key))
|
||||
,desc ',modes)
|
||||
forms))
|
||||
(cond ((and keymaps states)
|
||||
(cond ((and doom--keymaps states)
|
||||
(unless (featurep 'evil) (throw 'skip 'evil))
|
||||
(dolist (keymap keymaps)
|
||||
(push `(,(if defer 'evil-define-key 'evil-define-key*)
|
||||
(dolist (keymap doom--keymaps)
|
||||
(push `(,(if doom--defer 'evil-define-key 'evil-define-key*)
|
||||
',states ,keymap ,key ,def)
|
||||
forms)))
|
||||
(states
|
||||
|
@ -227,8 +229,8 @@ Example
|
|||
,(intern (format "evil-%s-state-%smap" state (if local "local-" "")))
|
||||
,key ,def)
|
||||
forms)))
|
||||
(keymaps
|
||||
(dolist (keymap keymaps)
|
||||
(doom--keymaps
|
||||
(dolist (keymap doom--keymaps)
|
||||
(push `(define-key ,keymap ,key ,def) forms)))
|
||||
(t (push `(,(if local 'local-set-key 'global-set-key) ,key ,def)
|
||||
forms))))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; core-lib.el
|
||||
;;; core-lib.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; I don't use use-package for these to save on the `fboundp' lookups it does
|
||||
;; for its :commands property. I use dolists instead of mapc to avoid extra
|
||||
|
@ -35,23 +35,21 @@
|
|||
'default-directory
|
||||
(or root `(doom-project-root))))))
|
||||
((listp paths)
|
||||
(let (forms)
|
||||
(dolist (i paths (nreverse forms))
|
||||
(push (doom--resolve-paths i root) forms))))
|
||||
(cl-loop for i in paths
|
||||
collect (doom--resolve-paths i root)))
|
||||
(t paths)))
|
||||
|
||||
(defun doom--resolve-hooks (hooks)
|
||||
(let ((quoted-p (eq (car-safe hooks) 'quote))
|
||||
ret-hooks)
|
||||
(let ((quoted-p (eq (car-safe hooks) 'quote)))
|
||||
(when quoted-p
|
||||
(setq hooks (cadr hooks)))
|
||||
(dolist (hook (if (listp hooks) hooks (list hooks)) (nreverse ret-hooks))
|
||||
(push (cond ((eq (car-safe hook) 'quote)
|
||||
(cadr hook))
|
||||
(quoted-p hook)
|
||||
(t
|
||||
(intern (format "%s-hook" (symbol-name hook)))))
|
||||
ret-hooks))))
|
||||
(cl-loop with hooks = (if (listp hooks) hooks (list hooks))
|
||||
for hook in hooks
|
||||
if (eq (car-safe hook) 'quote)
|
||||
collect (cadr hook)
|
||||
else if quoted-p
|
||||
collect hook
|
||||
else collect (intern (format "%s-hook" (symbol-name hook))))))
|
||||
|
||||
|
||||
;;
|
||||
|
@ -67,8 +65,7 @@
|
|||
"A smart wrapper around `with-eval-after-load'. Supresses warnings during
|
||||
compilation."
|
||||
(declare (indent defun) (debug t))
|
||||
`(,(if (or (not (boundp 'byte-compile-current-file))
|
||||
(not byte-compile-current-file)
|
||||
`(,(if (or (not (bound-and-true-p byte-compile-current-file))
|
||||
(if (symbolp feature)
|
||||
(require feature nil :no-error)
|
||||
(load feature :no-message :no-error)))
|
||||
|
@ -139,12 +136,13 @@ Examples:
|
|||
Body forms can access the hook's arguments through the let-bound variable
|
||||
`args'."
|
||||
(declare (indent defun) (debug t))
|
||||
(let ((hook-fn (if (boundp 'hook-fn) hook-fn))
|
||||
hook append-p local-p)
|
||||
(let ((hook-fn 'add-hook)
|
||||
append-p local-p)
|
||||
(while (keywordp (car args))
|
||||
(pcase (pop args)
|
||||
(:append (setq append-p t))
|
||||
(:local (setq local-p t))))
|
||||
(:local (setq local-p t))
|
||||
(:remove (setq hook-fn 'remove-hook))))
|
||||
(let ((hooks (doom--resolve-hooks (pop args)))
|
||||
(funcs
|
||||
(let ((val (car args)))
|
||||
|
@ -157,7 +155,7 @@ Body forms can access the hook's arguments through the let-bound variable
|
|||
(dolist (fn funcs)
|
||||
(setq fn (if (symbolp fn)
|
||||
`(function ,fn)
|
||||
`(lambda (&rest args) ,@args)))
|
||||
`(lambda (&rest _) ,@args)))
|
||||
(dolist (hook hooks)
|
||||
(push (cond ((eq hook-fn 'remove-hook)
|
||||
`(remove-hook ',hook ,fn ,local-p))
|
||||
|
@ -169,17 +167,16 @@ Body forms can access the hook's arguments through the let-bound variable
|
|||
(defmacro remove-hook! (&rest args)
|
||||
"Convenience macro for `remove-hook'. Takes the same arguments as
|
||||
`add-hook!'."
|
||||
(let ((hook-fn 'remove-hook))
|
||||
(macroexpand `(add-hook! ,@args))))
|
||||
`(add-hook! :remove ,@args))
|
||||
|
||||
(defmacro associate! (mode &rest plist)
|
||||
"Associate a minor mode to certain patterns and project files."
|
||||
(declare (indent 1))
|
||||
(unless noninteractive
|
||||
(let* ((modes (plist-get plist :modes))
|
||||
(match (plist-get plist :match))
|
||||
(files (plist-get plist :files))
|
||||
(pred-form (plist-get plist :when)))
|
||||
(let ((modes (plist-get plist :modes))
|
||||
(match (plist-get plist :match))
|
||||
(files (plist-get plist :files))
|
||||
(pred-form (plist-get plist :when)))
|
||||
(cond ((or files modes pred-form)
|
||||
(when (and files
|
||||
(not (or (listp files)
|
||||
|
@ -196,9 +193,8 @@ Body forms can access the hook's arguments through the let-bound variable
|
|||
,(or pred-form t))
|
||||
(,mode 1)))
|
||||
,@(if (and modes (listp modes))
|
||||
(let (forms)
|
||||
(dolist (hook (doom--resolve-hooks modes) (nreverse forms))
|
||||
(push `(add-hook ',hook ',hook-name) forms)))
|
||||
(cl-loop for hook in (doom--resolve-hooks modes)
|
||||
collect `(add-hook ',hook ',hook-name))
|
||||
`((add-hook 'after-change-major-mode-hook ',hook-name))))))
|
||||
(match
|
||||
`(push (cons ,match ',mode) doom-auto-minor-mode-alist))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; core-os.el
|
||||
;;; core-os.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defconst IS-MAC (eq system-type 'darwin))
|
||||
(defconst IS-LINUX (eq system-type 'gnu/linux))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; core-packages.el
|
||||
;;; core-packages.el --- package management system -*- lexical-binding: t; -*-
|
||||
|
||||
;; Emacs package management is opinionated. Unfortunately, so am I. I've bound
|
||||
;; together `use-package', `quelpa' and package.el to create my own,
|
||||
|
@ -76,7 +76,7 @@ base by `doom!' and for calculating how many packages exist.")
|
|||
|
||||
(defvar doom--refresh-p nil)
|
||||
|
||||
(setq load-prefer-newer noninteractive
|
||||
(setq load-prefer-newer (or noninteractive doom-debug-mode)
|
||||
package--init-file-ensured t
|
||||
package-user-dir (expand-file-name "elpa" doom-packages-dir)
|
||||
package-enable-at-startup nil
|
||||
|
@ -96,7 +96,7 @@ base by `doom!' and for calculating how many packages exist.")
|
|||
|
||||
use-package-always-defer t
|
||||
use-package-always-ensure nil
|
||||
use-package-expand-minimally (eval-when-compile (not doom-debug-mode))
|
||||
use-package-expand-minimally (not doom-debug-mode)
|
||||
use-package-debug nil
|
||||
use-package-verbose doom-debug-mode
|
||||
use-package-minimum-reported-time (if doom-debug-mode 0 0.1)
|
||||
|
@ -144,7 +144,7 @@ to speed up startup."
|
|||
;; Also, in some edge cases involving package initialization during a
|
||||
;; non-interactive session, `package-initialize' fails to fill `load-path'.
|
||||
;; If we want something done right, do it ourselves!
|
||||
(setq doom--package-load-path (directory-files package-user-dir t "^\\w" t)
|
||||
(setq doom--package-load-path (directory-files package-user-dir t "^[^.]" t)
|
||||
load-path (append load-path doom--package-load-path))
|
||||
|
||||
;; Ensure core packages are installed
|
||||
|
@ -164,7 +164,7 @@ to speed up startup."
|
|||
|
||||
(setq doom-init-p t)))
|
||||
|
||||
(defun doom-initialize-autoloads (&optional inhibit-reload-p)
|
||||
(defun doom-initialize-autoloads ()
|
||||
"Ensures that `doom-autoload-file' exists and is loaded. Otherwise run
|
||||
`doom/reload-autoloads' to generate it."
|
||||
(unless (file-exists-p doom-autoload-file)
|
||||
|
@ -189,15 +189,16 @@ This aggressively reloads core autoload files."
|
|||
(funcall load-fn (expand-file-name "init.el" doom-emacs-dir))
|
||||
(funcall load-fn (doom-module-path :private user-login-name "init.el"))
|
||||
(when load-p
|
||||
(mapc (lambda (file) (funcall load-fn file t))
|
||||
(append (nreverse (file-expand-wildcards (concat doom-core-dir "core*.el")))
|
||||
(file-expand-wildcards (concat doom-core-dir "autoload/*.el"))
|
||||
(doom--module-paths "config.el")))))
|
||||
(cl-loop for file
|
||||
in (append (nreverse (file-expand-wildcards (expand-file-name "core*.el" doom-core-dir)))
|
||||
(file-expand-wildcards (expand-file-name "autoload/*.el" doom-core-dir))
|
||||
(doom--module-paths "config.el"))
|
||||
do (funcall load-fn file t))))
|
||||
(when (or force-p (not doom-packages))
|
||||
(setq doom-packages nil)
|
||||
(funcall load-fn (expand-file-name "packages.el" doom-core-dir))
|
||||
(mapc (lambda (file) (funcall load-fn file t))
|
||||
(doom--module-paths "packages.el")))))
|
||||
(dolist (file (doom--module-paths "packages.el"))
|
||||
(funcall load-fn file t)))))
|
||||
|
||||
(defun doom-initialize-modules (modules)
|
||||
"Adds MODULES to `doom-modules'. MODULES must be in mplist format.
|
||||
|
@ -213,15 +214,12 @@ This aggressively reloads core autoload files."
|
|||
(error "No namespace specified on `doom!' for %s" m))
|
||||
((eq m '*)
|
||||
(doom-initialize-modules
|
||||
(cons mode
|
||||
(mapcar
|
||||
(lambda (dir) (intern (file-name-nondirectory dir)))
|
||||
(cl-remove-if-not
|
||||
#'file-directory-p
|
||||
(directory-files (expand-file-name
|
||||
(substring (symbol-name mode) 1)
|
||||
doom-modules-dir)
|
||||
t "^\\w"))))))
|
||||
(cl-loop with modpath = (expand-file-name (substring (symbol-name mode) 1) doom-modules-dir)
|
||||
for path in (directory-files modpath t "^\\w")
|
||||
if (file-directory-p path)
|
||||
collect (intern (file-name-nondirectory path)) into paths
|
||||
finally return (cons mode paths)
|
||||
finally do (message "== %s %s" mode paths))))
|
||||
(t
|
||||
(doom--enable-module mode m))))))
|
||||
|
||||
|
@ -243,11 +241,9 @@ This aggressively reloads core autoload files."
|
|||
(defun doom--module-pairs ()
|
||||
"Returns `doom-modules' as a list of (MODULE . SUBMODULE) cons cells. The list
|
||||
is sorted by order of insertion."
|
||||
(let (pairs)
|
||||
(when (hash-table-p doom-modules)
|
||||
(maphash (lambda (key value) (push (cons (car key) (cdr key)) pairs))
|
||||
doom-modules)
|
||||
(nreverse pairs))))
|
||||
(when (hash-table-p doom-modules)
|
||||
(cl-loop for key being the hash-keys of doom-modules
|
||||
collect (cons (car key) (cdr key)))))
|
||||
|
||||
(defun doom--module-paths (&optional append-file)
|
||||
"Returns a list of absolute file paths to modules, with APPEND-FILE added, if
|
||||
|
@ -291,19 +287,17 @@ byte-compilation."
|
|||
|
||||
(unless noninteractive
|
||||
(load ,(doom-module-path :private user-login-name "init") t t)
|
||||
,@(let (forms)
|
||||
(dolist (module (doom--module-pairs))
|
||||
(push `(require! ,(car module) ,(cdr module) t) forms))
|
||||
(unless (doom-module-loaded-p :private (intern user-login-name))
|
||||
(push `(require! :private ,user-login-name t) forms))
|
||||
(nreverse forms))
|
||||
,@(cl-loop for (module . submodule) in (doom--module-pairs)
|
||||
collect `(require! ,module ,submodule t))
|
||||
,(unless (doom-module-loaded-p :private (intern user-login-name))
|
||||
`(require! :private ,user-login-name t))
|
||||
|
||||
(when (display-graphic-p)
|
||||
(require 'server)
|
||||
(unless (server-running-p)
|
||||
(server-start)))
|
||||
|
||||
(add-hook 'after-init-hook #'doom--display-benchmark t))))
|
||||
(add-hook 'emacs-startup-hook #'doom--display-benchmark t))))
|
||||
|
||||
(defmacro def-package! (name &rest plist)
|
||||
"A thin wrapper around `use-package'."
|
||||
|
@ -425,7 +419,7 @@ SUBMODULE is a symbol."
|
|||
;; Commands
|
||||
;;
|
||||
|
||||
(defun doom/reload (&optional ignorable-p)
|
||||
(defun doom/reload ()
|
||||
"Reload `load-path' and recompile files (if necessary). Useful if you
|
||||
modify/update packages outside of emacs. Automatically called (through the
|
||||
server, if necessary) by `doom/packages-install', `doom/packages-update' and
|
||||
|
@ -434,7 +428,7 @@ server, if necessary) by `doom/packages-install', `doom/packages-update' and
|
|||
(cond (noninteractive
|
||||
(message "Reloading...")
|
||||
(require 'server)
|
||||
(unless (ignore-errors (server-eval-at "server" '(doom/reload t)))
|
||||
(unless (ignore-errors (server-eval-at "server" '(doom/reload)))
|
||||
(message "Recompiling")
|
||||
(doom/recompile)))
|
||||
(t
|
||||
|
@ -454,35 +448,34 @@ In modules, checks modules/*/autoload.el and modules/*/autoload/*.el.
|
|||
Rerun this whenever init.el is modified. You can also use `make autoloads` from
|
||||
the commandline."
|
||||
(interactive)
|
||||
;; This function must not use `cl-lib', autoloaded functions or external
|
||||
;; dependencies. It must assume nothing is set up!
|
||||
;; This function must not use autoloaded functions or external dependencies.
|
||||
;; It must assume nothing is set up!
|
||||
(doom-initialize-packages (not noninteractive))
|
||||
(let ((generated-autoload-file doom-autoload-file)
|
||||
(autoload-files
|
||||
(let ((evil-p (featurep! :feature evil))
|
||||
(targets
|
||||
(file-expand-wildcards
|
||||
(expand-file-name "autoload/*.el" doom-core-dir))))
|
||||
(dolist (path (doom--module-paths))
|
||||
(let ((auto-dir (expand-file-name "autoload" path))
|
||||
(auto-file (expand-file-name "autoload.el" path)))
|
||||
(when (file-exists-p auto-file)
|
||||
(push auto-file autoload-files))
|
||||
(push auto-file targets))
|
||||
(when (file-directory-p auto-dir)
|
||||
(mapc (lambda (file)
|
||||
;; Make evil*.el autoload files a special case; don't load
|
||||
;; them unless evil is enabled.
|
||||
(unless (and (string-prefix-p "evil" (file-name-nondirectory file))
|
||||
(not (featurep! :feature evil)))
|
||||
(push file autoload-files)))
|
||||
(file-expand-wildcards (expand-file-name "*.el" auto-dir) t)))))
|
||||
(when (file-exists-p generated-autoload-file)
|
||||
(delete-file generated-autoload-file)
|
||||
(dolist (file (file-expand-wildcards (expand-file-name "*.el" auto-dir) t))
|
||||
;; Make evil*.el autoload files a special case; don't load
|
||||
;; them unless evil is enabled.
|
||||
(unless (and (string-prefix-p "evil" (file-name-nondirectory file))
|
||||
(not evil-p))
|
||||
(push file targets))))))
|
||||
(when (file-exists-p doom-autoload-file)
|
||||
(delete-file doom-autoload-file)
|
||||
(message "Deleted old autoloads.el"))
|
||||
(dolist (file (nreverse autoload-files))
|
||||
(dolist (file (nreverse targets))
|
||||
(let ((inhibit-message (not doom-debug-mode)))
|
||||
(update-file-autoloads file))
|
||||
(update-file-autoloads file nil doom-autoload-file))
|
||||
(message "Scanned %s" (file-relative-name file doom-emacs-dir)))
|
||||
(condition-case ex
|
||||
(let ((buf (get-file-buffer generated-autoload-file)))
|
||||
(let ((buf (get-file-buffer doom-autoload-file)))
|
||||
(unwind-protect
|
||||
(with-current-buffer buf
|
||||
(save-buffer)
|
||||
|
@ -490,7 +483,7 @@ the commandline."
|
|||
(message "Finished generating autoloads.el!"))
|
||||
(kill-buffer buf)))
|
||||
('error
|
||||
(delete-file generated-autoload-file)
|
||||
(delete-file doom-autoload-file)
|
||||
(error "Couldn't evaluate autoloads.el: %s" (cadr ex))))))
|
||||
|
||||
(defun doom/compile (&optional lite-p only-recompile-p)
|
||||
|
@ -506,46 +499,50 @@ If ONLY-RECOMPILE-P is non-nil, only recompile out-of-date files."
|
|||
;; Ensure all relevant config files are loaded and up-to-date. This way we
|
||||
;; don't need eval-when-compile and require blocks scattered all over.
|
||||
(doom-initialize-packages t t)
|
||||
(let ((targets (append (list "init.el" doom-core-dir)
|
||||
(unless lite-p (doom--module-paths))))
|
||||
(let ((targets
|
||||
(cond ((equal (car command-line-args-left) "--")
|
||||
(cl-loop for file in command-line-args-left
|
||||
if (file-exists-p file)
|
||||
collect (expand-file-name file)
|
||||
finally do (setq command-line-args-list nil)) )
|
||||
(t
|
||||
(append (list (expand-file-name "init.el" doom-emacs-dir)
|
||||
doom-core-dir)
|
||||
(unless lite-p (doom--module-paths))))))
|
||||
(total-success 0)
|
||||
(total-fail 0)
|
||||
(total-nocomp 0)
|
||||
el-files)
|
||||
(mapc (lambda (file)
|
||||
(when (or (not only-recompile-p)
|
||||
(let ((elc-file (byte-compile-dest-file file)))
|
||||
(and (file-exists-p elc-file)
|
||||
(file-newer-than-file-p file elc-file))))
|
||||
(let ((result (byte-compile-file file))
|
||||
(short-name (file-relative-name file doom-emacs-dir)))
|
||||
(cl-incf
|
||||
(cond ((eq result 'no-byte-compile)
|
||||
(message! (dark (white "Ignored %s" short-name)))
|
||||
total-nocomp)
|
||||
((null result)
|
||||
(message! (red "Failed to compile %s" short-name))
|
||||
total-fail)
|
||||
(t
|
||||
(message! (green "Compiled %s" short-name))
|
||||
total-success))))))
|
||||
(dolist (path targets (reverse el-files))
|
||||
(let ((path (expand-file-name path doom-emacs-dir)))
|
||||
(cond ((file-directory-p path)
|
||||
(setq el-files (append (directory-files-recursively path "\\.el$") el-files)))
|
||||
((file-exists-p path)
|
||||
(push path el-files))
|
||||
(t
|
||||
(error "Invalid path: %s" path))))))
|
||||
(message!
|
||||
(bold
|
||||
(color (if (zerop total-fail) 'green 'red)
|
||||
"%s %s file(s) %s"
|
||||
(if only-recompile-p "Recompiled" "Compiled")
|
||||
(format (if el-files "%d/%d" "%d")
|
||||
total-success
|
||||
(- (length el-files) total-nocomp))
|
||||
(format "(%s not compiled)" total-nocomp))))))
|
||||
(total-nocomp 0))
|
||||
(let ((el-files (cl-loop for path in targets
|
||||
if (file-directory-p path)
|
||||
nconc (nreverse (directory-files-recursively path "\\.el$"))
|
||||
else if (file-exists-p path)
|
||||
collect path)))
|
||||
(dolist (file el-files)
|
||||
(when (or (not only-recompile-p)
|
||||
(let ((elc-file (byte-compile-dest-file file)))
|
||||
(and (file-exists-p elc-file)
|
||||
(file-newer-than-file-p file elc-file))))
|
||||
(let ((result (byte-compile-file file))
|
||||
(short-name (file-relative-name file doom-emacs-dir)))
|
||||
(cl-incf
|
||||
(cond ((eq result 'no-byte-compile)
|
||||
(message! (dark (white "Ignored %s" short-name)))
|
||||
total-nocomp)
|
||||
((null result)
|
||||
(message! (red "Failed to compile %s" short-name))
|
||||
total-fail)
|
||||
(t
|
||||
(message! (green "Compiled %s" short-name))
|
||||
total-success))))))
|
||||
(message!
|
||||
(bold
|
||||
(color (if (= total-fail 0) 'green 'red)
|
||||
"%s %s file(s) %s"
|
||||
(if only-recompile-p "Recompiled" "Compiled")
|
||||
(format (if el-files "%d/%d" "%d")
|
||||
total-success
|
||||
(- (length el-files) total-nocomp))
|
||||
(format "(%s ignored)" total-nocomp)))))))
|
||||
|
||||
(defun doom/recompile ()
|
||||
"Recompile any compiled *.el files in your Emacs configuration."
|
||||
|
@ -554,12 +551,6 @@ If ONLY-RECOMPILE-P is non-nil, only recompile out-of-date files."
|
|||
;; In case `load-path' has changed (e.g. after an update)
|
||||
(byte-recompile-file (expand-file-name "core.el" doom-core-dir) t))
|
||||
|
||||
(defun doom/compile-lite ()
|
||||
"A light-weight version of `doom/compile' which only compiles core files in
|
||||
your emacs configuration (init.el and core/**/*.el)."
|
||||
(interactive)
|
||||
(doom/compile t))
|
||||
|
||||
(defun doom/clean-cache ()
|
||||
"Clear local cache (`doom-cache-dir'). You may need to restart Emacs for some
|
||||
components to feel its effects."
|
||||
|
@ -571,15 +562,14 @@ components to feel its effects."
|
|||
"Delete all compiled elc files in DOOM emacs, excluding compiled ELPA/QUELPA
|
||||
package files."
|
||||
(interactive)
|
||||
(let ((elc-files (append
|
||||
(let ((init-elc (expand-file-name "init.elc" doom-emacs-dir)))
|
||||
(if (file-exists-p init-elc) (list init-elc)))
|
||||
(directory-files-recursively doom-core-dir "\\.elc$")
|
||||
(directory-files-recursively doom-modules-dir "\\.elc$"))))
|
||||
(if elc-files
|
||||
(dolist (file elc-files)
|
||||
(delete-file file)
|
||||
(message "Deleting %s" (file-relative-name file doom-emacs-dir)))
|
||||
(let ((targets (append (list (expand-file-name "init.elc" doom-emacs-dir))
|
||||
(directory-files-recursively doom-core-dir "\\.elc$")
|
||||
(directory-files-recursively doom-modules-dir "\\.elc$"))))
|
||||
(unless (cl-loop for path in targets
|
||||
if (file-exists-p path)
|
||||
collect path
|
||||
and do (delete-file path)
|
||||
and do (message "Deleted %s" (file-relative-name path)))
|
||||
(message "Everything is clean"))))
|
||||
|
||||
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
;;; core-popups.el --- taming sudden yet inevitable windows
|
||||
;;; core-popups.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; I want a "real"-buffer-first policy in my Emacsian utpoia; popup buffers
|
||||
;; ought to be second-class citizens to "real" buffers. No need for a wall or
|
||||
;; controversial immigration policies -- all we need is `shackle'.
|
||||
;; controversial immigration policies -- all we need is `shackle' (and it will
|
||||
;; actually work).
|
||||
;;
|
||||
;; The gist is: popups should always be displayed on one side of the frame, away
|
||||
;; from 'real' buffers; they should be easy to dispose of when we don't want to
|
||||
;; see them; and easily brought back in case we change our minds. Also, popups
|
||||
;; should typically have no mode-line.
|
||||
;; The gist is: popups should be displayed on one side of the frame, away from
|
||||
;; 'real' buffers. They should be easy to dispose of when we don't want to see
|
||||
;; them and easily brought back in case we change our minds. Also, popups should
|
||||
;; typically have no mode-line.
|
||||
;;
|
||||
;; Be warned, this requires a lot of hackery and voodoo that could break with an
|
||||
;; Be warned, this requires a lot of hackery voodoo that could break with an
|
||||
;; emacs update or an update to any of the packages it tries to tame (like helm
|
||||
;; or org-mode).
|
||||
|
||||
|
@ -43,7 +44,8 @@ is enabled/disabled.'")
|
|||
;; Bootstrap
|
||||
;;
|
||||
|
||||
(def-package! shackle :demand t
|
||||
(def-package! shackle
|
||||
:demand t
|
||||
:init
|
||||
(setq shackle-default-alignment 'below
|
||||
shackle-default-size 8
|
||||
|
@ -85,10 +87,8 @@ is enabled/disabled.'")
|
|||
("^ \\*" :regexp t :size 12 :noselect t :autokill t :autoclose t)))
|
||||
|
||||
:config
|
||||
(if (display-graphic-p)
|
||||
(shackle-mode +1)
|
||||
(add-transient-hook! 'after-make-frame-functions (shackle-mode +1))
|
||||
(add-hook 'after-init-hook 'shackle-mode))
|
||||
(add-transient-hook! 'after-make-frame-functions (shackle-mode +1))
|
||||
(add-hook 'window-setup-hook #'shackle-mode)
|
||||
|
||||
(defun doom*shackle-always-align (plist)
|
||||
"Ensure popups are always aligned and selected by default. Eliminates the need
|
||||
|
@ -418,7 +418,7 @@ the command buffer."
|
|||
(t
|
||||
(magit-display-buffer-traditional buffer))))
|
||||
|
||||
(defun doom-magit-quit-window (kill-buffer)
|
||||
(defun doom-magit-quit-window (_kill-buffer)
|
||||
"Close the current magit window properly."
|
||||
(let ((last (current-buffer)))
|
||||
(cond ((when-let (dest (doom-buffers-in-mode
|
||||
|
@ -438,7 +438,7 @@ the command buffer."
|
|||
|
||||
|
||||
(after! mu4e
|
||||
(defun doom*mu4e-popup-window (buf height)
|
||||
(defun doom*mu4e-popup-window (buf _height)
|
||||
(doom-popup-buffer buf :size 10 :noselect t)
|
||||
buf)
|
||||
(advice-add #'mu4e~temp-window :override #'doom*mu4e-popup-window))
|
||||
|
@ -501,7 +501,7 @@ you came from."
|
|||
|
||||
;; Ensure these settings are attached to org-load-hook as late as possible,
|
||||
;; giving other modules a chance to add their own hooks.
|
||||
(add-hook! 'after-init-hook
|
||||
(add-hook! 'emacs-startup-hook
|
||||
(add-hook! 'org-load-hook
|
||||
(set! :popup
|
||||
'("*Calendar*" :size 0.4 :noselect t)
|
||||
|
@ -532,7 +532,7 @@ you came from."
|
|||
;; `org-edit-src-code' simply clones and narrows the buffer to a src block,
|
||||
;; so we are secretly manipulating the same buffer. Since truely killing it
|
||||
;; would kill the original org buffer we've got to do things differently.
|
||||
(defun doom*org-src-switch-to-buffer (buffer context)
|
||||
(defun doom*org-src-switch-to-buffer (buffer _context)
|
||||
(if (eq org-src-window-setup 'switch-invisibly)
|
||||
(set-buffer buffer)
|
||||
(pop-to-buffer buffer)))
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
;;; core-projects.el --- tools for getting around your project
|
||||
|
||||
;; I want Emacs to be aware of the projects. `projectile' provides tools for
|
||||
;; digging through project files and exposing an API I can use to make other
|
||||
;; plugins/features project-aware.
|
||||
;;; core-projects.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar doom-project-hook nil
|
||||
"Hook run when a project is enabled. The name of the project's mode and its
|
||||
state are passed in.")
|
||||
|
||||
(def-package! projectile :demand t
|
||||
(def-package! projectile
|
||||
:demand t
|
||||
:init
|
||||
(setq projectile-cache-file (concat doom-cache-dir "projectile.cache")
|
||||
projectile-enable-caching (not noninteractive)
|
||||
|
@ -24,7 +21,7 @@ state are passed in.")
|
|||
"build.gradle"))
|
||||
|
||||
:config
|
||||
(projectile-mode +1)
|
||||
(add-hook 'after-init-hook #'projectile-mode)
|
||||
|
||||
(setq projectile-other-file-alist
|
||||
(append '(("less" "css")
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; core-ui.el --- draw me like one of your French editors
|
||||
;; core-ui.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar doom-ui-fringe-size '4 "Default fringe width")
|
||||
|
||||
|
@ -40,9 +40,6 @@
|
|||
|
||||
(fset #'yes-or-no-p #'y-or-n-p) ; y/n instead of yes/no
|
||||
|
||||
;; auto-enabled in Emacs 25+; I'd rather enable it manually
|
||||
(global-eldoc-mode -1)
|
||||
|
||||
;; show typed keystrokes in minibuffer
|
||||
(setq echo-keystrokes 0.02)
|
||||
;; ...but hide them while isearch is active
|
||||
|
@ -94,7 +91,7 @@ local value, whether or not it's permanent-local. Therefore, we cycle
|
|||
(setq show-paren-delay 0.1
|
||||
show-paren-highlight-openparen t
|
||||
show-paren-when-point-inside-paren t)
|
||||
(show-paren-mode +1)
|
||||
(add-hook 'window-setup-hook #'show-paren-mode)
|
||||
|
||||
;;; More reliable inter-window border
|
||||
;; The native border "consumes" a pixel of the fringe on righter-most splits,
|
||||
|
@ -102,7 +99,7 @@ local value, whether or not it's permanent-local. Therefore, we cycle
|
|||
(setq-default window-divider-default-places t
|
||||
window-divider-default-bottom-width 1
|
||||
window-divider-default-right-width 1)
|
||||
(window-divider-mode +1)
|
||||
(add-hook 'window-setup-hook #'window-divider-mode)
|
||||
|
||||
;; like diminish, but for major-modes. [pedantry intensifies]
|
||||
(defvar doom-ui-mode-names
|
||||
|
@ -122,13 +119,17 @@ mode is detected.")
|
|||
;; Bootstrap
|
||||
;;
|
||||
|
||||
;; auto-enabled in Emacs 25+; I'd rather enable it manually
|
||||
(global-eldoc-mode -1)
|
||||
|
||||
;; draw me like one of your French editors
|
||||
(tooltip-mode -1) ; relegate tooltips to echo area only
|
||||
(menu-bar-mode -1)
|
||||
(when (fboundp 'tool-bar-mode)
|
||||
(tool-bar-mode -1))
|
||||
(when (display-graphic-p)
|
||||
(scroll-bar-mode -1)
|
||||
;; buffer name in frame title
|
||||
;; buffer name in frame title
|
||||
(setq-default frame-title-format '("DOOM Emacs"))
|
||||
;; standardize fringe width
|
||||
(push (cons 'left-fringe doom-ui-fringe-size) default-frame-alist)
|
||||
|
@ -191,7 +192,7 @@ file."
|
|||
(set-buffer-modified-p nil))
|
||||
nil)
|
||||
|
||||
(add-hook! (highlight-indentation-mode highlight-indentation-current-column-mode)
|
||||
(defun doom|init-highlight-indentation ()
|
||||
(if (or highlight-indentation-mode highlight-indentation-current-column-mode)
|
||||
(progn
|
||||
(doom|inject-trailing-whitespace)
|
||||
|
@ -199,7 +200,9 @@ file."
|
|||
(add-hook 'after-save-hook #'doom|inject-trailing-whitespace nil t))
|
||||
(remove-hook 'before-save-hook #'delete-trailing-whitespace t)
|
||||
(remove-hook 'after-save-hook #'doom|inject-trailing-whitespace t)
|
||||
(delete-trailing-whitespace))))
|
||||
(delete-trailing-whitespace)))
|
||||
(add-hook! (highlight-indentation-mode highlight-indentation-current-column-mode)
|
||||
#'doom|init-highlight-indentation))
|
||||
|
||||
;; For modes that don't adequately highlight numbers
|
||||
(def-package! highlight-numbers :commands highlight-numbers-mode)
|
||||
|
@ -265,8 +268,8 @@ file."
|
|||
;; indicators for empty lines past EOF
|
||||
(def-package! vi-tilde-fringe
|
||||
:when (display-graphic-p)
|
||||
:demand t
|
||||
:config (global-vi-tilde-fringe-mode t))
|
||||
:commands global-vi-tilde-fringe-mode
|
||||
:init (add-hook 'window-setup-hook #'global-vi-tilde-fringe-mode))
|
||||
|
||||
;; For a distractions-free-like UI, that dynamically resizes margets and can
|
||||
;; center a buffer.
|
||||
|
@ -292,12 +295,11 @@ file."
|
|||
(byte-compile #',sym))))))
|
||||
|
||||
(defsubst doom--prepare-modeline-segments (segments)
|
||||
(let (segs)
|
||||
(dolist (seg segments (nreverse segs))
|
||||
(push (if (stringp seg)
|
||||
seg
|
||||
(list (intern (format "doom-modeline-segment--%s" (symbol-name seg)))))
|
||||
segs))))
|
||||
(cl-loop for seg in segments
|
||||
if (stringp seg)
|
||||
collect seg
|
||||
else
|
||||
collect (list (intern (format "doom-modeline-segment--%s" (symbol-name seg))))))
|
||||
|
||||
(defmacro def-modeline! (name lhs &optional rhs)
|
||||
"Defines a modeline format and byte-compiles it. NAME is a symbol to identify
|
||||
|
|
62
core/core.el
62
core/core.el
|
@ -1,16 +1,16 @@
|
|||
;;; core.el --- The heart of the beast
|
||||
;;; core.el --- the heart of the beast -*- lexical-binding: t; -*-
|
||||
|
||||
;;; Naming conventions:
|
||||
;;
|
||||
;; doom-... public variables or functions (non-interactive)
|
||||
;; doom-... public variables or non-interactive functions
|
||||
;; doom--... private anything (non-interactive), not safe for direct use
|
||||
;; doom/... an interactive function
|
||||
;; doom/... an interactive function; safe for M-x or keybinding
|
||||
;; doom:... an evil operator, motion or command
|
||||
;; doom|... hook function
|
||||
;; doom*... advising functions
|
||||
;; ...! a macro or function that configures DOOM
|
||||
;; %... functions used for in-snippet logic
|
||||
;; +... Any of the above, but part of a module, e.g. +emacs-lisp|init-hook
|
||||
;; +... Any of the above but part of a module, e.g. `+emacs-lisp|init-hook'
|
||||
;;
|
||||
;; Autoloaded functions are in core/autoload/*.el and modules/*/*/autoload.el or
|
||||
;; modules/*/*/autoload/*.el.
|
||||
|
@ -94,7 +94,7 @@ there are problems.")
|
|||
;; History & backup settings (save nothing, that's what git is for)
|
||||
auto-save-default nil
|
||||
create-lockfiles nil
|
||||
history-length 1000
|
||||
history-length 500
|
||||
make-backup-files nil
|
||||
;; files
|
||||
abbrev-file-name (concat doom-local-dir "abbrev.el")
|
||||
|
@ -117,6 +117,7 @@ there are problems.")
|
|||
(advice-add #'display-startup-echo-area-message :override #'ignore)
|
||||
(setq inhibit-startup-message t
|
||||
inhibit-startup-echo-area-message user-login-name
|
||||
inhibit-default-init t
|
||||
initial-major-mode 'fundamental-mode
|
||||
initial-scratch-message nil)
|
||||
|
||||
|
@ -150,34 +151,41 @@ enable multiple minor modes for the same regexp.")
|
|||
|
||||
;;;
|
||||
;; Bootstrap
|
||||
(setq gc-cons-threshold 402653184
|
||||
gc-cons-percentage 0.6)
|
||||
(eval-and-compile
|
||||
(defvar doom--file-name-handler-alist file-name-handler-alist)
|
||||
(setq gc-cons-threshold 402653184
|
||||
gc-cons-percentage 0.6
|
||||
file-name-handler-alist nil)
|
||||
|
||||
(let (file-name-handler-alist)
|
||||
(require 'cl-lib)
|
||||
(eval-and-compile
|
||||
(require 'core-packages (concat doom-core-dir "core-packages")))
|
||||
(eval-when-compile
|
||||
(doom-initialize))
|
||||
(setq load-path (eval-when-compile load-path)
|
||||
doom--package-load-path (eval-when-compile doom--package-load-path))
|
||||
(require 'core-packages (concat doom-core-dir "core-packages")))
|
||||
|
||||
;;; Let 'er rip
|
||||
(require 'core-lib)
|
||||
(require 'core-os) ; consistent behavior across Oses
|
||||
(with-demoted-errors "AUTOLOAD ERROR: %s"
|
||||
(require 'autoloads doom-autoload-file t))
|
||||
(eval-when-compile
|
||||
(doom-initialize))
|
||||
|
||||
(unless noninteractive
|
||||
(require 'core-ui) ; draw me like one of your French editors
|
||||
(require 'core-popups) ; taming sudden yet inevitable windows
|
||||
(require 'core-editor) ; baseline configuration for text editing
|
||||
(require 'core-projects) ; making Emacs project-aware
|
||||
(require 'core-keybinds))) ; centralized keybind system + which-key
|
||||
(setq load-path (eval-when-compile load-path)
|
||||
doom--package-load-path (eval-when-compile doom--package-load-path))
|
||||
|
||||
(add-hook! '(after-init-hook doom-reload-hook)
|
||||
;;; Let 'er rip
|
||||
(require 'core-lib)
|
||||
(require 'core-os) ; consistent behavior across Oses
|
||||
(with-demoted-errors "AUTOLOAD ERROR: %s"
|
||||
(require 'autoloads doom-autoload-file t))
|
||||
|
||||
(unless noninteractive
|
||||
(require 'core-ui) ; draw me like one of your French editors
|
||||
(require 'core-popups) ; taming sudden yet inevitable windows
|
||||
(require 'core-editor) ; baseline configuration for text editing
|
||||
(require 'core-projects) ; making Emacs project-aware
|
||||
(require 'core-keybinds)) ; centralized keybind system + which-key
|
||||
|
||||
(defun doom|init ()
|
||||
(setq gc-cons-threshold 16777216
|
||||
gc-cons-percentage 0.1))
|
||||
gc-cons-percentage 0.1
|
||||
file-name-handler-alist doom--file-name-handler-alist))
|
||||
|
||||
(add-hook! '(emacs-startup-hook doom-reload-hook)
|
||||
#'doom|init)
|
||||
|
||||
(provide 'core)
|
||||
;;; core.el ends here
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; init.el
|
||||
;;; init.el -*- lexical-binding: t; -*-
|
||||
;;
|
||||
;; Author: Henrik Lissner <henrik@lissner.net>
|
||||
;; URL: https://github.com/hlissner/.emacs.d
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
;;; app/email/autoload/email.el
|
||||
;;; app/email/autoload/email.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun =email ()
|
||||
"Start email client."
|
||||
(interactive)
|
||||
(call-interactively 'mu4e))
|
||||
(call-interactively #'mu4e))
|
||||
|
||||
;;;###autoload
|
||||
(defun +email/compose ()
|
||||
"Compose a new email."
|
||||
(interactive)
|
||||
;; TODO Interactively select email account
|
||||
(call-interactively 'mu4e-compose-new))
|
||||
(call-interactively #'mu4e-compose-new))
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; app/email/autoload/evil.el
|
||||
;;; app/email/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +email/mark (&optional beg end)
|
||||
|
@ -8,11 +8,12 @@
|
|||
(let* ((beg (or (and (region-active-p) evil-visual-beginning) (line-beginning-position)))
|
||||
(end (or (and (region-active-p) evil-visual-end) (line-end-position)))
|
||||
(key (this-command-keys))
|
||||
(command (car (cl-find-if (lambda (mark) (equal (car (plist-get (cdr mark) :char)) key))
|
||||
mu4e-marks))))
|
||||
(command
|
||||
(car (cl-find-if (lambda (mark) (equal (car (plist-get (cdr mark) :char)) key))
|
||||
mu4e-marks))))
|
||||
(unless command
|
||||
(error "Not a valid mark command: %s" key))
|
||||
(when (featurep 'evil)
|
||||
(when (bound-and-true-p evil-mode)
|
||||
(evil-normal-state))
|
||||
(goto-char beg)
|
||||
(dotimes (_ (count-lines beg end))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; app/email/config.el
|
||||
;;; app/email/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; I want to live in Emacs. Living is incomplete without email, so Emacs needs
|
||||
;; to give me the ability to read, search, write and send my email. It does so
|
||||
|
@ -16,9 +16,9 @@
|
|||
|
||||
(def-setting! :email (label letvars &optional default)
|
||||
"Registers an email address for mu4e."
|
||||
(let* ((name (or (cdr (assq 'user-full-name letvars)) user-full-name))
|
||||
(address (cdr (assq 'user-mail-address letvars))))
|
||||
(dolist (var letvars letvars)
|
||||
(let ((name (or (cdr (assq 'user-full-name letvars)) user-full-name))
|
||||
(address (cdr (assq 'user-mail-address letvars))))
|
||||
(dolist (var letvars)
|
||||
(let ((val (cdr var)))
|
||||
(when (and (stringp val) (string-match-p "%s" val))
|
||||
(setcdr var (format val label)))))
|
||||
|
@ -96,15 +96,15 @@
|
|||
("mime:image/*" "Messages with images" ?p)))
|
||||
|
||||
;; Add a column to display what email account the email belongs to.
|
||||
(push '(:account
|
||||
:name "Account"
|
||||
:shortname "Account"
|
||||
:help "Which account this email belongs to"
|
||||
:function
|
||||
(lambda (msg)
|
||||
(let ((maildir (mu4e-message-field msg :maildir)))
|
||||
(format "%s" (substring maildir 1 (string-match-p "/" maildir 1))))))
|
||||
mu4e-header-info-custom)
|
||||
(cl-pushnew '(:account
|
||||
:name "Account"
|
||||
:shortname "Account"
|
||||
:help "Which account this email belongs to"
|
||||
:function
|
||||
(lambda (msg)
|
||||
(let ((maildir (mu4e-message-field msg :maildir)))
|
||||
(format "%s" (substring maildir 1 (string-match-p "/" maildir 1))))))
|
||||
mu4e-header-info-custom)
|
||||
|
||||
;; In my workflow, emails won't be moved at all. Only their flags/labels are
|
||||
;; changed. Se we redefine the trash and refile marks not to do any moving.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; app/present/autoload.el
|
||||
;;; app/present/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; --- impatient-mode -------------------------------------------------------------
|
||||
|
||||
|
@ -14,8 +14,9 @@
|
|||
(+present--cleanup-impatient-mode)))
|
||||
|
||||
(defun +present--cleanup-impatient-mode ()
|
||||
(unless (cl-remove-if-not (lambda (buf) (buffer-local-value 'impatient-mode buf))
|
||||
(doom-buffer-list))
|
||||
(unless (cl-loop for buf in (doom-buffer-list)
|
||||
if (buffer-local-value 'impatient-mode buf)
|
||||
return t)
|
||||
(httpd-stop)
|
||||
(remove-hook 'kill-buffer-hook '+present--cleanup-impatient-mode)))
|
||||
|
||||
|
@ -60,8 +61,9 @@
|
|||
(text-scale-set +present-scale)))
|
||||
|
||||
(defun +present--cleanup-org-tree-slides-mode ()
|
||||
(unless (cl-remove-if-not (lambda (buf) (buffer-local-value 'org-tree-slide-mode buf))
|
||||
(doom-buffers-in-mode 'org-mode))
|
||||
(unless (cl-loop for buf in (doom-buffers-in-mode 'org-mode)
|
||||
if (buffer-local-value 'org-tree-slide-mode buf)
|
||||
return t)
|
||||
(org-tree-slide-mode -1)
|
||||
(remove-hook 'kill-buffer-hook #'+present--cleanup-org-tree-slides-mode)))
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; app/present/config.el
|
||||
;;; app/present/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Sometimes you just get that urge to show off. I don't have a fancy car, so
|
||||
;; my code or Emacs will have to do.
|
||||
|
@ -68,7 +68,8 @@
|
|||
(add-hook! 'org-tree-slide-mode-after-narrow-hook
|
||||
#'(+present|detect-slide +present|add-overlays org-display-inline-images))
|
||||
|
||||
(add-hook! 'org-tree-slide-mode-hook
|
||||
(defun +present|org-tree-present-mode ()
|
||||
"TODO"
|
||||
(doom/window-zoom)
|
||||
(let ((arg (if org-tree-slide-mode +1 -1)))
|
||||
(centered-window-mode arg)
|
||||
|
@ -87,8 +88,10 @@
|
|||
(set-face-attribute 'org-level-2 nil :height 1.0)
|
||||
(+present|remove-overlays)
|
||||
(org-remove-inline-images)))))
|
||||
(add-hook 'org-tree-slide-mode-hook #'+present|org-tree-present-mode)
|
||||
|
||||
(defun +present*org-tree-slide-narrow-exclude-header (orig-fn &rest args)
|
||||
"TODO"
|
||||
(cl-letf (((symbol-function 'org-narrow-to-subtree)
|
||||
(lambda () (save-excursion
|
||||
(save-match-data
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; app/rss/autoload.el
|
||||
;;; app/rss/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun =rss ()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; app/rss/config.el
|
||||
;;; app/rss/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; This is an opinionated workflow that turns Emacs into an RSS reader, inspired
|
||||
;; by apps Reeder and Readkit. It can be invoked via `=rss'. Otherwise, if you
|
||||
|
@ -31,8 +31,8 @@
|
|||
(make-directory elfeed-db-directory t)
|
||||
|
||||
;; Ensure elfeed buffers are treated as real
|
||||
(push (lambda (buf) (string-match-p "^\\*elfeed" (buffer-name buf)))
|
||||
doom-real-buffer-functions)
|
||||
(cl-pushnew (lambda (buf) (string-match-p "^\\*elfeed" (buffer-name buf)))
|
||||
doom-real-buffer-functions)
|
||||
|
||||
;; Enhance readability of a post
|
||||
(add-hook 'elfeed-show-mode-hook #'+rss|elfeed-wrap)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; app/twitter/autoload.el
|
||||
;;; app/twitter/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun =twitter ()
|
||||
|
@ -7,7 +7,7 @@
|
|||
(delete-other-windows)
|
||||
(condition-case ex
|
||||
(progn
|
||||
(call-interactively 'twit)
|
||||
(call-interactively #'twit)
|
||||
(unless (get-buffer (car twittering-initial-timeline-spec-string))
|
||||
(error "Failed to open twitter"))
|
||||
(switch-to-buffer (car twittering-initial-timeline-spec-string))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; app/twitter/config.el
|
||||
;;; app/twitter/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! twittering-mode
|
||||
:commands twit
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
;;; app/write/autoload.el
|
||||
;;; app/write/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(define-minor-mode +write-mode
|
||||
"TODO"
|
||||
:init-value nil
|
||||
:keymap nil
|
||||
(let ((arg (if +write-mode +1 -1))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; company.el
|
||||
;;; completion/company/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/complete ()
|
||||
|
@ -15,17 +15,18 @@
|
|||
C-x C-l."
|
||||
(interactive (list 'interactive))
|
||||
(require 'company)
|
||||
(unless (bound-and-true-p company-mode) (company-mode))
|
||||
(let ((lines (split-string
|
||||
(replace-regexp-in-string
|
||||
"^[\t\s]+" ""
|
||||
(concat (buffer-substring-no-properties (point-min) (line-beginning-position))
|
||||
(buffer-substring-no-properties (line-end-position) (point-max))))
|
||||
"\\(\r\n\\|[\n\r]\\)" t)))
|
||||
(cl-case command
|
||||
(interactive (company-begin-backend '+company/whole-lines))
|
||||
(prefix (company-grab-line "^[\t\s]*\\(.+\\)" 1))
|
||||
(candidates (all-completions arg lines)))))
|
||||
(pcase command
|
||||
('interactive (company-begin-backend '+company/whole-lines))
|
||||
('prefix (company-grab-line "^[\t\s]*\\(.+\\)" 1))
|
||||
('candidates
|
||||
(all-completions
|
||||
arg
|
||||
(split-string
|
||||
(replace-regexp-in-string
|
||||
"^[\t\s]+" ""
|
||||
(concat (buffer-substring-no-properties (point-min) (line-beginning-position))
|
||||
(buffer-substring-no-properties (line-end-position) (point-max))))
|
||||
"\\(\r\n\\|[\n\r]\\)" t)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +company/dict-or-keywords ()
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
;;; completion/company/config.el
|
||||
;;; completion/company/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-setting! :company-backend (modes backends)
|
||||
"Register company BACKENDS to MODES."
|
||||
(let* ((modes (if (listp modes) modes (list modes)))
|
||||
(backends (if (listp backends) backends (list backends)))
|
||||
(def-name (intern (format "doom--init-company-%s"
|
||||
(mapconcat #'identity (mapcar #'symbol-name modes) "-"))))
|
||||
(quoted (eq (car-safe backends) 'quote)))
|
||||
(mapconcat #'identity (mapcar #'symbol-name modes) "-")))))
|
||||
;; TODO more type checks
|
||||
`(prog1
|
||||
(defun ,def-name ()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
;;; emacs/ido/config.el
|
||||
;;; completion/ido/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! ido
|
||||
:init
|
||||
:config
|
||||
(setq ido-ignore-buffers
|
||||
'("\\` " "^\\*ESS\\*" "^\\*Messages\\*" "^\\*Help\\*" "^\\*Buffer"
|
||||
"^\\*.*Completions\\*$" "^\\*Ediff" "^\\*tramp" "^\\*cvs-"
|
||||
|
@ -16,7 +16,6 @@
|
|||
ido-enable-last-directory-history t
|
||||
ido-save-directory-list-file (concat doom-cache-dir "ido.last"))
|
||||
|
||||
:config
|
||||
(push "\\`.DS_Store$" ido-ignore-files)
|
||||
(push "Icon\\?$" ido-ignore-files)
|
||||
|
||||
|
@ -24,15 +23,15 @@
|
|||
(ido-everywhere 1)
|
||||
(require 'ido-ubiquitous)
|
||||
|
||||
(add-hook! ido-setup
|
||||
(defun +ido|init ()
|
||||
(require 'ido-vertical-mode)
|
||||
(require 'flx-ido)
|
||||
(require 'crm-custom)
|
||||
|
||||
(map! :map (ido-common-completion-map ido-completion-map ido-file-completion-map)
|
||||
"C-n" #'ido-next-match
|
||||
"C-p" #'ido-prev-match
|
||||
"C-w" #'ido-delete-backward-word-updir))
|
||||
(add-hook 'ido-setup-hook #'+ido|init)
|
||||
|
||||
(defun +ido*sort-mtime ()
|
||||
"Sort ido filelist by mtime instead of alphabetically."
|
||||
|
@ -43,16 +42,16 @@
|
|||
(sixth (file-attributes (concat ido-current-directory b)))
|
||||
(sixth (file-attributes (concat ido-current-directory a)))))))
|
||||
(ido-to-end ;; move . files to end (again)
|
||||
(delq nil (mapcar
|
||||
(lambda (x) (and (char-equal (string-to-char x) ?.) x))
|
||||
ido-temp-list))))
|
||||
(cl-loop for x in ido-temp-list
|
||||
if (char-equal (string-to-char x) ?.)
|
||||
collect x)))
|
||||
(advice-add #'ido-sort-mtime :override #'+ido*sort-mtime)
|
||||
(add-hook! (ido-make-file-list ido-make-dir-list) #'+ido*sort-mtime)
|
||||
|
||||
(defun +ido|setup-home-keybind ()
|
||||
"Go to $HOME with ~"
|
||||
(define-key ido-file-completion-map (kbd "~")
|
||||
(λ! (if (looking-back "/")
|
||||
(λ! (if (looking-back "/" (point-min))
|
||||
(insert "~/")
|
||||
(call-interactively #'self-insert-command)))))
|
||||
(add-hook 'ido-setup-hook #'+ido|setup-home-keybind))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; emacs/ido/packages.el
|
||||
;;; completion/ido/packages.el
|
||||
|
||||
(package! flx-ido)
|
||||
(package! ido-ubiquitous)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; completion/ivy/autoload/evil.el
|
||||
;;; completion/ivy/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload (autoload '+ivy:swiper "completion/ivy/autoload/evil" nil t)
|
||||
(evil-define-command +ivy:swiper (&optional search)
|
||||
|
@ -19,7 +19,7 @@
|
|||
(defvar +ivy--file-search-recursion-p t)
|
||||
(defvar +ivy--file-search-all-files-p nil)
|
||||
|
||||
(defun +ivy--file-search (engine beg end query &optional directory prompt)
|
||||
(defun +ivy--file-search (engine beg end query &optional directory)
|
||||
(let* ((directory (or directory (doom-project-root)))
|
||||
(recursion-p +ivy--file-search-recursion-p)
|
||||
(all-files-p +ivy--file-search-all-files-p)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; completion/ivy/autoload/ivy.el
|
||||
;;; completion/ivy/autoload/ivy.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Show more information in ivy-switch-buffer; and only display
|
||||
;; workgroup-relevant buffers.
|
||||
|
@ -6,33 +6,35 @@
|
|||
(let ((min-name 5)
|
||||
(min-mode 5)
|
||||
(proot (doom-project-root)))
|
||||
(mapcar
|
||||
(lambda (b) (format (format "%%-%ds %%-%ds %%s" min-name min-mode)
|
||||
(nth 0 b)
|
||||
(nth 1 b)
|
||||
(or (nth 2 b) "")))
|
||||
(mapcar (lambda (b)
|
||||
(with-current-buffer b
|
||||
(let ((buffer-name (buffer-name b))
|
||||
(mode-name (symbol-name major-mode)))
|
||||
(when (> (length buffer-name) min-name)
|
||||
(setq min-name (+ (length buffer-name) 15)))
|
||||
(when (> (length mode-name) min-mode)
|
||||
(setq min-mode (+ (length mode-name) 3)))
|
||||
(list (concat
|
||||
(propertize buffer-name
|
||||
'face (cond ((string-match-p "^ ?\\*" buffer-name)
|
||||
'font-lock-comment-face)
|
||||
((not (string= proot (doom-project-root)))
|
||||
'font-lock-keyword-face)
|
||||
(buffer-read-only
|
||||
'error)))
|
||||
(when (and buffer-file-name (buffer-modified-p))
|
||||
(propertize "[+]" 'face 'doom-modeline-buffer-modified)))
|
||||
(propertize mode-name 'face 'font-lock-constant-face)
|
||||
(when buffer-file-name
|
||||
(abbreviate-file-name (file-name-directory buffer-file-name)))))))
|
||||
(or buffer-list (doom-buffer-list))))))
|
||||
(cl-loop for buf in (or buffer-list (doom-buffer-list))
|
||||
collect
|
||||
(destructuring-bind (type mode path)
|
||||
(+ivy--get-buffer-attrs buf proot)
|
||||
(format (format "%%-%ds %%-%ds %%s" min-name min-mode)
|
||||
type mode (or path ""))))))
|
||||
|
||||
(defun +ivy--get-buffer-attrs (b &optional project-root)
|
||||
(with-current-buffer b
|
||||
(let ((buffer-name (buffer-name b))
|
||||
(mode-name (symbol-name major-mode)))
|
||||
(when (> (length buffer-name) min-name)
|
||||
(setq min-name (+ (length buffer-name) 15)))
|
||||
(when (> (length mode-name) min-mode)
|
||||
(setq min-mode (+ (length mode-name) 3)))
|
||||
(list (concat
|
||||
(propertize buffer-name
|
||||
'face (cond ((string-match-p "^ ?\\*" buffer-name)
|
||||
'font-lock-comment-face)
|
||||
((and project-root
|
||||
(not (string= project-root (doom-project-root))))
|
||||
'font-lock-keyword-face)
|
||||
(buffer-read-only
|
||||
'error)))
|
||||
(when (and buffer-file-name (buffer-modified-p))
|
||||
(propertize "[+]" 'face 'doom-modeline-buffer-modified)))
|
||||
(propertize mode-name 'face 'font-lock-constant-face)
|
||||
(when buffer-file-name
|
||||
(abbreviate-file-name (file-name-directory buffer-file-name)))))))
|
||||
|
||||
(defun +ivy--select-buffer-action (buffer)
|
||||
(ivy--switch-buffer-action
|
||||
|
@ -66,54 +68,54 @@ limit to buffers in the current workspace."
|
|||
:keymap ivy-switch-buffer-map
|
||||
:caller '+ivy/switch-workspace-buffer))
|
||||
|
||||
;; TODO refactor ivy task candidate functions (messy!)
|
||||
(defun +ivy--tasks-candidates (tasks)
|
||||
"Generate a list of task tags (specified by `+ivy-task-tags') for
|
||||
`+ivy/tasks'."
|
||||
(let* ((max-type-width (seq-max (mapcar #'length (mapcar #'car +ivy-task-tags))))
|
||||
(max-desc-width (seq-max (mapcar #'length (mapcar #'cl-cdadr tasks))))
|
||||
(max-width (max 25 (min (- (frame-width) (+ max-type-width 1))
|
||||
max-desc-width)))
|
||||
(fmt (format "%%-%ds %%-%ds%%s%%s:%%s" max-type-width max-desc-width))
|
||||
lines)
|
||||
(dolist (alist tasks (nreverse lines))
|
||||
(let-alist alist
|
||||
(push (format fmt
|
||||
(propertize .type 'face (cdr (assoc .type +ivy-task-tags)))
|
||||
(substring .desc 0 (min max-desc-width (length .desc)))
|
||||
(propertize " | " 'face 'font-lock-comment-face)
|
||||
(propertize (abbreviate-file-name .file) 'face 'font-lock-keyword-face)
|
||||
(propertize .line 'face 'font-lock-constant-face))
|
||||
lines)))))
|
||||
(let* ((max-type-width
|
||||
(cl-loop for task in +ivy-task-tags maximize (length (car task))))
|
||||
(max-desc-width
|
||||
(cl-loop for task in tasks maximize (length (cl-cdadr task))))
|
||||
(max-width (max (- (frame-width) (1+ max-type-width) max-desc-width)
|
||||
25)))
|
||||
(cl-loop
|
||||
with fmt = (format "%%-%ds %%-%ds%%s%%s:%%s" max-type-width max-width)
|
||||
for alist in tasks
|
||||
collect
|
||||
(let-alist alist
|
||||
(format fmt
|
||||
(propertize .type 'face (cdr (assoc .type +ivy-task-tags)))
|
||||
(substring .desc 0 (min max-desc-width (length .desc)))
|
||||
(propertize " | " 'face 'font-lock-comment-face)
|
||||
(propertize (abbreviate-file-name .file) 'face 'font-lock-keyword-face)
|
||||
(propertize .line 'face 'font-lock-constant-face))))))
|
||||
|
||||
(defun +ivy--tasks (target)
|
||||
(let (case-fold-search)
|
||||
(delq
|
||||
nil
|
||||
(mapcar (lambda (x)
|
||||
(save-match-data
|
||||
(when (string-match (concat "^\\([^:]+\\):\\([0-9]+\\):.+\\("
|
||||
(string-join (mapcar #'car +ivy-task-tags) "\\|")
|
||||
"\\):?\\s-*\\(.+\\)")
|
||||
x)
|
||||
`((type . ,(match-string 3 x))
|
||||
(desc . ,(match-string 4 x))
|
||||
(file . ,(match-string 1 x))
|
||||
(line . ,(match-string 2 x))))))
|
||||
(let ((command (or (let ((bin (executable-find "rg")))
|
||||
(and bin (concat bin " --line-number")))
|
||||
(let ((bin (executable-find "ag")))
|
||||
(and bin (concat bin " --numbers")))
|
||||
(error "Neither ripgrep or the_silver_searcher is available")))
|
||||
(args (concat " -- "
|
||||
(shell-quote-argument
|
||||
(concat "\\s("
|
||||
(string-join (mapcar #'car +ivy-task-tags) "|")
|
||||
")([\\s:]|\\([^)]+\\):?)")))))
|
||||
(when-let (out (shell-command-to-string
|
||||
(format "%s -H -S --no-heading %s %s"
|
||||
command args target)))
|
||||
(split-string out "\n" t)))))))
|
||||
(let* (case-fold-search
|
||||
(task-tags (mapcar #'car +ivy-task-tags))
|
||||
(cmd
|
||||
(format "%s -H -S --no-heading -- %s %s"
|
||||
(or (when-let (bin (executable-find "rg"))
|
||||
(concat bin " --line-number"))
|
||||
(when-let (bin (executable-find "ag"))
|
||||
(concat bin " --numbers"))
|
||||
(error "ripgrep & the_silver_searcher are unavailable"))
|
||||
(shell-quote-argument
|
||||
(concat "\\s("
|
||||
(string-join task-tags "|")
|
||||
")([\\s:]|\\([^)]+\\):?)"))
|
||||
target)))
|
||||
(save-match-data
|
||||
(cl-loop with out = (shell-command-to-string cmd)
|
||||
for x in (and out (split-string out "\n" t))
|
||||
when (string-match
|
||||
(concat "^\\([^:]+\\):\\([0-9]+\\):.+\\("
|
||||
(string-join task-tags "\\|")
|
||||
"\\):?\\s-*\\(.+\\)")
|
||||
x)
|
||||
collect `((type . ,(match-string 3 x))
|
||||
(desc . ,(match-string 4 x))
|
||||
(file . ,(match-string 1 x))
|
||||
(line . ,(match-string 2 x)))))))
|
||||
|
||||
(defun +ivy--tasks-open-action (x)
|
||||
"Jump to the file and line of the current task."
|
||||
|
@ -180,27 +182,27 @@ counsel-rg)."
|
|||
(defun +ivy/wgrep-occur ()
|
||||
"Invoke the search+replace wgrep buffer on the current ag/rg search results."
|
||||
(interactive)
|
||||
(if (not (window-minibuffer-p))
|
||||
(user-error "No completion session is active")
|
||||
(require 'wgrep)
|
||||
(let* ((caller (ivy-state-caller ivy-last))
|
||||
(occur-fn (plist-get ivy--occurs-list caller))
|
||||
(buffer
|
||||
(generate-new-buffer
|
||||
(format "*ivy-occur%s \"%s\"*"
|
||||
(if caller (concat " " (prin1-to-string caller)) "")
|
||||
ivy-text))))
|
||||
(with-current-buffer buffer
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer)
|
||||
(funcall occur-fn))
|
||||
(setf (ivy-state-text ivy-last) ivy-text)
|
||||
(setq ivy-occur-last ivy-last)
|
||||
(setq-local ivy--directory ivy--directory))
|
||||
(ivy-exit-with-action
|
||||
`(lambda (_)
|
||||
(pop-to-buffer ,buffer)
|
||||
(ivy-wgrep-change-to-wgrep-mode))))))
|
||||
(unless (window-minibuffer-p)
|
||||
(user-error "No completion session is active"))
|
||||
(require 'wgrep)
|
||||
(let* ((caller (ivy-state-caller ivy-last))
|
||||
(occur-fn (plist-get ivy--occurs-list caller))
|
||||
(buffer
|
||||
(generate-new-buffer
|
||||
(format "*ivy-occur%s \"%s\"*"
|
||||
(if caller (concat " " (prin1-to-string caller)) "")
|
||||
ivy-text))))
|
||||
(with-current-buffer buffer
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer)
|
||||
(funcall occur-fn))
|
||||
(setf (ivy-state-text ivy-last) ivy-text)
|
||||
(setq ivy-occur-last ivy-last)
|
||||
(setq-local ivy--directory ivy--directory))
|
||||
(ivy-exit-with-action
|
||||
`(lambda (_)
|
||||
(pop-to-buffer ,buffer)
|
||||
(ivy-wgrep-change-to-wgrep-mode)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +ivy-yas-prompt (prompt choices &optional display-fn)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
;;; completion/ivy/packages.el
|
||||
;;; completion/ivy/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +ivy-task-tags '(("TODO" . warning)
|
||||
("FIXME" . error))
|
||||
(defvar +ivy-task-tags
|
||||
'(("TODO" . warning)
|
||||
("FIXME" . error))
|
||||
"An alist of tags for `+ivy/tasks' to include in its search, whose CDR is the
|
||||
face to render it with.")
|
||||
|
||||
|
@ -20,7 +21,8 @@ session)."
|
|||
;; Packages
|
||||
;;
|
||||
|
||||
(def-package! ivy :demand t
|
||||
(def-package! ivy
|
||||
:demand t
|
||||
:config
|
||||
(setq ivy-height 12
|
||||
ivy-do-completion-in-region nil
|
||||
|
@ -38,7 +40,7 @@ session)."
|
|||
(after! magit (setq magit-completing-read-function #'ivy-completing-read))
|
||||
(after! yasnippet (push #'+ivy-yas-prompt yas-prompt-functions))
|
||||
|
||||
(ivy-mode +1)
|
||||
(add-hook 'window-setup-hook #'ivy-mode)
|
||||
|
||||
(map! :map ivy-mode-map
|
||||
[remap describe-face] #'counsel-describe-face
|
||||
|
@ -79,12 +81,16 @@ session)."
|
|||
|
||||
;; Configure `counsel-rg', `counsel-ag' & `counsel-pt'
|
||||
(set! :popup 'ivy-occur-grep-mode :size (+ 2 ivy-height) :regexp t :autokill t)
|
||||
|
||||
(dolist (cmd '(counsel-ag counsel-rg counsel-pt))
|
||||
(ivy-add-actions
|
||||
cmd
|
||||
'(("O" +ivy-git-grep-other-window-action "open in other window"))))
|
||||
|
||||
;; 1. Remove character limit from `counsel-ag-function'
|
||||
;; 2. Disable ivy's over-zealous parentheses quoting behavior (if i want
|
||||
;; literal parentheses, I'll escape them myself).
|
||||
;; 3. This may need to be updated frequently, to meet changes upstream
|
||||
;; 4. counsel-ag, counsel-rg and counsel-pt all use this function
|
||||
(advice-add #'counsel-ag-function :override #'+ivy*counsel-ag-function))
|
||||
|
||||
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
;;; feature/debug/autoload.el
|
||||
|
||||
;;;###autoload
|
||||
(defun +debug/quit ()
|
||||
(interactive)
|
||||
(ignore-errors (call-interactively 'realgud:cmd-quit))
|
||||
(doom/popup-close)
|
||||
(evil-normal-state))
|
||||
|
||||
|
||||
;;;###autoload (autoload '+debug:debug-toggle-breakpoint "feature/debug/autoload" nil t)
|
||||
;;;###autoload (autoload '+debug:run "feature/debug/autoload" nil t)
|
||||
|
||||
(after! evil
|
||||
(evil-define-command +debug:run (&optional path)
|
||||
"Initiate debugger for current major mode"
|
||||
(interactive "<f>")
|
||||
(let ((default-directory (doom-project-root)))
|
||||
(cond ((memq major-mode '(c-mode c++-mode))
|
||||
(realgud:gdb (if path (concat "gdb " path))))
|
||||
((memq major-mode '(ruby-mode enh-ruby-mode))
|
||||
(doom:repl nil (format "run '%s'" (file-name-nondirectory (or path buffer-file-name)))))
|
||||
((eq major-mode 'sh-mode)
|
||||
(let ((shell sh-shell))
|
||||
(when (string= shell "sh")
|
||||
(setq shell "bash"))
|
||||
(cond ((string= shell "bash")
|
||||
(realgud:bashdb (if path (concat "bashdb " path))))
|
||||
((string= shell "zsh")
|
||||
(realgud:zshdb (if path (concat "zshdb " path))))
|
||||
(t (user-error "No shell debugger for %s" shell)))))
|
||||
;; TODO Add python debugging
|
||||
((memq major-mode '(js-mode js2-mode js3-mode))
|
||||
(realgud:trepanjs))
|
||||
((eq major-mode 'haskell-mode)
|
||||
(haskell-debug))
|
||||
(t (user-error "No debugger for %s" major-mode)))))
|
||||
|
||||
(evil-define-command +debug:debug-toggle-breakpoint (&optional bang)
|
||||
(interactive "<!>")
|
||||
(call-interactively (if bang 'realgud:cmd-clear 'realgud:cmd-break))))
|
||||
|
9
modules/feature/debug/autoload/debug.el
Normal file
9
modules/feature/debug/autoload/debug.el
Normal file
|
@ -0,0 +1,9 @@
|
|||
;;; feature/debug/autoload/debug.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +debug/quit ()
|
||||
(interactive)
|
||||
(ignore-errors (call-interactively 'realgud:cmd-quit))
|
||||
(doom/popup-close)
|
||||
(evil-normal-state))
|
||||
|
31
modules/feature/debug/autoload/evil.el
Normal file
31
modules/feature/debug/autoload/evil.el
Normal file
|
@ -0,0 +1,31 @@
|
|||
;;; feature/debug/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload (autoload '+debug:run "feature/debug/autoload/evil" nil t)
|
||||
(evil-define-command +debug:run (&optional path)
|
||||
"Initiate debugger for current major mode"
|
||||
(interactive "<f>")
|
||||
(let ((default-directory (doom-project-root)))
|
||||
(cond ((memq major-mode '(c-mode c++-mode))
|
||||
(realgud:gdb (if path (concat "gdb " path))))
|
||||
((memq major-mode '(ruby-mode enh-ruby-mode))
|
||||
(doom:repl nil (format "run '%s'" (file-name-nondirectory (or path buffer-file-name)))))
|
||||
((eq major-mode 'sh-mode)
|
||||
(let ((shell sh-shell))
|
||||
(when (string= shell "sh")
|
||||
(setq shell "bash"))
|
||||
(cond ((string= shell "bash")
|
||||
(realgud:bashdb (if path (concat "bashdb " path))))
|
||||
((string= shell "zsh")
|
||||
(realgud:zshdb (if path (concat "zshdb " path))))
|
||||
(t (user-error "No shell debugger for %s" shell)))))
|
||||
;; TODO Add python debugging
|
||||
((memq major-mode '(js-mode js2-mode js3-mode))
|
||||
(realgud:trepanjs))
|
||||
((eq major-mode 'haskell-mode)
|
||||
(haskell-debug))
|
||||
(t (user-error "No debugger for %s" major-mode)))))
|
||||
|
||||
;;;###autoload (autoload '+debug:toggle-breakpoint "feature/debug/autoload/evil" nil t)
|
||||
(evil-define-command +debug:toggle-breakpoint (&optional bang)
|
||||
(interactive "<!>")
|
||||
(call-interactively (if bang 'realgud:cmd-clear 'realgud:cmd-break)))
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/debug/config.el
|
||||
;;; feature/debug/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! realgud
|
||||
:commands (realgud:gdb realgud:trepanjs realgud:bashdb realgud:zshdb)
|
||||
|
@ -11,7 +11,7 @@
|
|||
;; (def-tmp-excmd! doom:def-debug-on doom:def-debug-off
|
||||
;; ("n[ext]" . realgud:cmd-next)
|
||||
;; ("s[tep]" . realgud:cmd-step)
|
||||
;; ("b[reak]" . doom:debug-toggle-breakpoint)
|
||||
;; ("b[reak]" . +debug:toggle-breakpoint)
|
||||
;; ("c[ontinue]" . realgud:cmd-continue))
|
||||
;; (advice-add #'realgud-cmdbuf-init :after #'doom:def-debug-on)
|
||||
;; (advice-add #'realgud:cmd-quit :after #'doom:def-debug-off)
|
||||
|
@ -21,28 +21,26 @@
|
|||
;; FIXME Causes realgud:cmd-* to focus popup on every invocation
|
||||
(defun +debug*realgud-run-process
|
||||
(debugger-name script-filename cmd-args minibuffer-history &optional no-reset)
|
||||
(let ((cmd-buf))
|
||||
(setq cmd-buf
|
||||
(apply #'realgud-exec-shell debugger-name script-filename
|
||||
(car cmd-args) no-reset (cdr cmd-args)))
|
||||
(let ((process (get-buffer-process cmd-buf)))
|
||||
(if (and process (eq 'run (process-status process)))
|
||||
(progn
|
||||
(pop-to-buffer cmd-buf)
|
||||
(define-key evil-emacs-state-local-map (kbd "ESC ESC") #'+debug/quit)
|
||||
(realgud:track-set-debugger debugger-name)
|
||||
(realgud-cmdbuf-info-in-debugger?= 't)
|
||||
(realgud-cmdbuf-info-cmd-args= cmd-args)
|
||||
(when cmd-buf
|
||||
(switch-to-buffer cmd-buf)
|
||||
(when realgud-cmdbuf-info
|
||||
(let* ((info realgud-cmdbuf-info)
|
||||
(cmd-args (realgud-cmdbuf-info-cmd-args info))
|
||||
(cmd-str (mapconcat #'identity cmd-args " ")))
|
||||
(set minibuffer-history
|
||||
(list-utils-uniq (cons cmd-str (eval minibuffer-history))))))))
|
||||
(if cmd-buf (switch-to-buffer cmd-buf))
|
||||
(message "Error running command: %s" (mapconcat #'identity cmd-args " "))))
|
||||
(let* ((cmd-buf (apply #'realgud-exec-shell debugger-name script-filename
|
||||
(car cmd-args) no-reset (cdr cmd-args)))
|
||||
(process (get-buffer-process cmd-buf)))
|
||||
(cond ((and process (eq 'run (process-status process)))
|
||||
(pop-to-buffer cmd-buf)
|
||||
(define-key evil-emacs-state-local-map (kbd "ESC ESC") #'+debug/quit)
|
||||
(realgud:track-set-debugger debugger-name)
|
||||
(realgud-cmdbuf-info-in-debugger?= 't)
|
||||
(realgud-cmdbuf-info-cmd-args= cmd-args)
|
||||
(when cmd-buf
|
||||
(switch-to-buffer cmd-buf)
|
||||
(when realgud-cmdbuf-info
|
||||
(let* ((info realgud-cmdbuf-info)
|
||||
(cmd-args (realgud-cmdbuf-info-cmd-args info))
|
||||
(cmd-str (mapconcat #'identity cmd-args " ")))
|
||||
(set minibuffer-history
|
||||
(list-utils-uniq (cons cmd-str (eval minibuffer-history))))))))
|
||||
(t
|
||||
(if cmd-buf (switch-to-buffer cmd-buf))
|
||||
(message "Error running command: %s" (mapconcat #'identity cmd-args " "))))
|
||||
cmd-buf))
|
||||
(advice-add #'realgud:run-process :override #'+debug*realgud-run-process))
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/eval/autoload/build.el
|
||||
;;; feature/eval/autoload/build.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar-local +eval-last-builder nil
|
||||
"The last builder run in the current buffer.")
|
||||
|
@ -22,9 +22,7 @@ functions.")
|
|||
:key 'cdr))
|
||||
(if (= (length builders) 1)
|
||||
(car builders)
|
||||
(when-let (builder (completing-read "Build: "
|
||||
(mapcar #'car builders)
|
||||
nil t))
|
||||
(when-let (builder (completing-read "Build: " (mapcar #'car builders) nil t))
|
||||
(assq (intern builder) builders)))))
|
||||
|
||||
;;;###autoload
|
||||
|
@ -36,9 +34,8 @@ functions.")
|
|||
(error "No builder for this buffer"))))
|
||||
(unless builder
|
||||
(error "Builder not found in registered builders"))
|
||||
(let* ((name (car builder))
|
||||
(plist (cdr builder))
|
||||
(fn (plist-get plist :fn)))
|
||||
(let ((name (car builder))
|
||||
(fn (plist-get (cdr builder) :fn)))
|
||||
(message "Running %s" name)
|
||||
(if (or (functionp fn)
|
||||
(and (symbolp fn) (fboundp fn)))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/eval/autoload/eval.el
|
||||
;;; feature/eval/autoload/eval.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +eval/buffer ()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/eval/autoload/evil.el
|
||||
;;; feature/eval/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload (autoload '+eval:region "feature/eval/autoload/evil" nil t)
|
||||
(evil-define-operator +eval:region (beg end)
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
;;; feature/eval/autoload/repl.el
|
||||
;;; feature/eval/autoload/repl.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +eval-repl-buffer nil
|
||||
"The buffer of the last open repl.")
|
||||
|
||||
(defun +eval--ensure-in-repl-buffer (&optional command)
|
||||
(if (eq (current-buffer) +eval-repl-buffer)
|
||||
t
|
||||
(if (and +eval-repl-buffer (buffer-live-p +eval-repl-buffer))
|
||||
(if-let (win (get-buffer-window +eval-repl-buffer))
|
||||
(select-window win)
|
||||
(doom-popup-buffer +eval-repl-buffer))
|
||||
(when command
|
||||
(let ((repl-buffer (save-window-excursion (call-interactively command))))
|
||||
(unless (bufferp repl-buffer)
|
||||
(error "REPL command didn't return a buffer"))
|
||||
(with-current-buffer repl-buffer (+eval-repl-mode +1))
|
||||
(setq +eval-repl-buffer repl-buffer)
|
||||
(select-window (doom-popup-buffer repl-buffer)))))
|
||||
(when (eq (current-buffer) +eval-repl-buffer)
|
||||
(goto-char (if (and (derived-mode-p 'comint-mode)
|
||||
(cdr comint-last-prompt))
|
||||
(cdr comint-last-prompt)
|
||||
(point-max)))
|
||||
t)))
|
||||
(or (eq (current-buffer) +eval-repl-buffer)
|
||||
(progn
|
||||
(if (and +eval-repl-buffer (buffer-live-p +eval-repl-buffer))
|
||||
(if-let (win (get-buffer-window +eval-repl-buffer))
|
||||
(select-window win)
|
||||
(doom-popup-buffer +eval-repl-buffer))
|
||||
(when command
|
||||
(let ((repl-buffer (save-window-excursion (call-interactively command))))
|
||||
(unless (bufferp repl-buffer)
|
||||
(error "REPL command didn't return a buffer"))
|
||||
(with-current-buffer repl-buffer (+eval-repl-mode +1))
|
||||
(setq +eval-repl-buffer repl-buffer)
|
||||
(select-window (doom-popup-buffer repl-buffer)))))
|
||||
(when (eq (current-buffer) +eval-repl-buffer)
|
||||
(goto-char (if (and (derived-mode-p 'comint-mode)
|
||||
(cdr comint-last-prompt))
|
||||
(cdr comint-last-prompt)
|
||||
(point-max)))
|
||||
t))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +eval/repl ()
|
||||
|
@ -31,8 +31,8 @@ the cursor at the prompt."
|
|||
(interactive)
|
||||
(when-let (command (cdr (assq major-mode +eval-repls)))
|
||||
(when (+eval--ensure-in-repl-buffer command)
|
||||
(when (and (featurep 'evil) evil-mode)
|
||||
(call-interactively 'evil-append-line))
|
||||
(when (bound-and-true-p evil-mode)
|
||||
(call-interactively #'evil-append-line))
|
||||
t)))
|
||||
|
||||
;;;###autoload
|
||||
|
@ -43,8 +43,8 @@ execute it immediately after."
|
|||
(let ((selection (buffer-substring-no-properties beg end)))
|
||||
(unless (+eval--ensure-in-repl-buffer)
|
||||
(error "No REPL open"))
|
||||
(when (and (featurep 'evil) evil-mode)
|
||||
(call-interactively 'evil-append-line))
|
||||
(when (bound-and-true-p evil-mode)
|
||||
(call-interactively #'evil-append-line))
|
||||
(insert (string-trim selection))
|
||||
(when auto-execute-p
|
||||
;; I don't use `comint-send-input' because different REPLs may have their
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/eval/config.el
|
||||
;;; feature/eval/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;
|
||||
;; Code building
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/evil/autoload/evil-mc.el
|
||||
;;; feature/evil/autoload/evil-mc.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +evil/mc-toggle-cursors ()
|
||||
|
@ -21,9 +21,7 @@ cursors."
|
|||
(interactive)
|
||||
(cond ((memq evil-this-type '(block line))
|
||||
(let ((col (evil-column))
|
||||
(line-at-pt (line-number-at-pos))
|
||||
(beg evil-visual-beginning)
|
||||
(end evil-visual-end))
|
||||
(line-at-pt (line-number-at-pos)))
|
||||
;; Fix off-by-one error
|
||||
(when (= evil-visual-direction 1)
|
||||
(cl-decf col)
|
||||
|
@ -36,7 +34,7 @@ cursors."
|
|||
(move-to-column col)
|
||||
(when (= (current-column) col)
|
||||
(evil-mc-make-cursor-here))))
|
||||
beg
|
||||
evil-visual-beginning
|
||||
(if (eq evil-this-type 'line) (1- evil-visual-end) evil-visual-end)
|
||||
nil)
|
||||
(evil-exit-visual-state))))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/evil/autoload/evil.el
|
||||
;;; feature/evil/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
|
||||
(eval-when-compile (require 'subr-x))
|
||||
|
||||
|
@ -36,16 +36,13 @@ flags. See http://vimdoc.sourceforge.net/htmldoc/cmdline.html#filename-modifiers
|
|||
"\\([#%]\\)"
|
||||
"\\(\\(?::\\(?:[PphtreS~.]\\|g?s[^:\t\n ]+\\)\\)*\\)"))
|
||||
(matches
|
||||
(let ((all-strings ())
|
||||
(i 0))
|
||||
(while (and (< i (length file-name))
|
||||
(string-match regexp file-name i))
|
||||
(setq i (1+ (match-beginning 0)))
|
||||
(let (strings)
|
||||
(push (dotimes (i (/ (length (match-data)) 2) (nreverse strings))
|
||||
(push (match-string i file-name) strings))
|
||||
all-strings)))
|
||||
(nreverse all-strings))))
|
||||
(cl-loop with i = 0
|
||||
while (and (< i (length file-name))
|
||||
(string-match regexp file-name i))
|
||||
do (setq i (1+ (match-beginning 0)))
|
||||
and collect
|
||||
(cl-loop for j to (/ (length (match-data)) 2)
|
||||
collect (match-string j file-name)))))
|
||||
(dolist (match matches)
|
||||
(let ((flags (split-string (car (cdr (cdr match))) ":" t))
|
||||
(path (and buffer-file-name
|
||||
|
@ -113,16 +110,18 @@ evil-window-move-* (e.g. `evil-window-move-far-left')"
|
|||
(doom-popup-p that-window))
|
||||
(setq that-buffer nil that-window nil))
|
||||
(if (not (or that-window (one-window-p t)))
|
||||
(funcall (case direction
|
||||
('left 'evil-window-move-far-left)
|
||||
('right 'evil-window-move-far-right)
|
||||
('up 'evil-window-move-very-top)
|
||||
('down 'evil-window-move-very-bottom)))
|
||||
(funcall (pcase direction
|
||||
('left #'evil-window-move-far-left)
|
||||
('right #'evil-window-move-far-right)
|
||||
('up #'evil-window-move-very-top)
|
||||
('down #'evil-window-move-very-bottom)))
|
||||
(unless that-window
|
||||
(setq that-window
|
||||
(split-window this-window nil (cond ((eq direction 'up) 'above)
|
||||
((eq direction 'down) 'below)
|
||||
(t direction))))
|
||||
(split-window this-window nil
|
||||
(pcase direction
|
||||
('up 'above)
|
||||
('down 'below)
|
||||
(_ direction))))
|
||||
(with-selected-window that-window
|
||||
(switch-to-buffer doom-buffer))
|
||||
(setq that-buffer (window-buffer that-window)))
|
||||
|
@ -172,20 +171,22 @@ evil-window-move-* (e.g. `evil-window-move-far-left')"
|
|||
|
||||
;; --- custom arg handlers ----------------
|
||||
|
||||
(defvar +evil--flag nil)
|
||||
|
||||
(defun +evil--ex-match-init (name &optional face update-hook)
|
||||
(with-current-buffer evil-ex-current-buffer
|
||||
(cond
|
||||
((eq flag 'start)
|
||||
((eq +evil--flag 'start)
|
||||
(evil-ex-make-hl name
|
||||
:face (or face 'evil-ex-substitute-matches)
|
||||
:update-hook (or update-hook #'evil-ex-pattern-update-ex-info))
|
||||
(setq flag 'update))
|
||||
(setq +evil--flag 'update))
|
||||
|
||||
((eq flag 'stop)
|
||||
((eq +evil--flag 'stop)
|
||||
(evil-ex-delete-hl name)))))
|
||||
|
||||
(defun +evil--ex-buffer-match (arg &optional hl-name flags beg end)
|
||||
(when (and (eq flag 'update)
|
||||
(when (and (eq +evil--flag 'update)
|
||||
evil-ex-substitute-highlight-all
|
||||
(not (zerop (length arg))))
|
||||
(condition-case lossage
|
||||
|
@ -209,21 +210,24 @@ evil-window-move-* (e.g. `evil-window-move-far-left')"
|
|||
|
||||
;;;###autoload
|
||||
(defun +evil-ex-buffer-match (flag &optional arg)
|
||||
(let ((hl-name 'evil-ex-buffer-match))
|
||||
(let ((hl-name 'evil-ex-buffer-match)
|
||||
(+evil--flag flag))
|
||||
(with-selected-window (minibuffer-selected-window)
|
||||
(+evil--ex-match-init hl-name)
|
||||
(+evil--ex-buffer-match arg hl-name (list (if evil-ex-substitute-global ?g))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +evil-ex-global-match (flag &optional arg)
|
||||
(let ((hl-name 'evil-ex-global-match))
|
||||
(let ((hl-name 'evil-ex-global-match)
|
||||
(+evil--flag flag))
|
||||
(with-selected-window (minibuffer-selected-window)
|
||||
(+evil--ex-match-init hl-name)
|
||||
(+evil--ex-buffer-match arg hl-name nil (point-min) (point-max)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +evil-ex-global-delim-match (flag &optional arg)
|
||||
(let ((hl-name 'evil-ex-global-delim-match))
|
||||
(let ((hl-name 'evil-ex-global-delim-match)
|
||||
(+evil--flag flag))
|
||||
(with-selected-window (minibuffer-selected-window)
|
||||
(+evil--ex-match-init hl-name)
|
||||
(let ((result (car-safe (evil-delimited-arguments arg 2))))
|
||||
|
@ -249,7 +253,7 @@ evil-window-move-* (e.g. `evil-window-move-far-left')"
|
|||
"A wrapper around `evil-delete' for `wgrep' buffers that will invoke
|
||||
`wgrep-mark-deletion' on lines you try to delete."
|
||||
(interactive "<R><x><y>")
|
||||
(condition-case ex
|
||||
(condition-case _ex
|
||||
(evil-delete beg end type register yank-handler)
|
||||
('text-read-only
|
||||
(evil-apply-on-block
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/evil/autoload/files.el
|
||||
;;; feature/evil/autoload/files.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defun +evil--forget-file (old-path &optional new-path)
|
||||
"Ensure `recentf', `projectile' and `save-place' forget OLD-PATH."
|
||||
|
@ -23,11 +23,9 @@ kills the buffer. If FORCE-P, force the deletion (don't ask for confirmation)."
|
|||
(buf (current-buffer)))
|
||||
(cond ((not (file-exists-p fname))
|
||||
(error "File doesn't exist: %s" fname))
|
||||
|
||||
((not (or force-p (y-or-n-p (format "Really delete %s?" fbase))))
|
||||
(message "Aborted")
|
||||
nil)
|
||||
|
||||
(t
|
||||
(unwind-protect
|
||||
(progn (delete-file fname) t)
|
||||
|
@ -38,8 +36,7 @@ kills the buffer. If FORCE-P, force the deletion (don't ask for confirmation)."
|
|||
;; to real buffers (`doom-real-buffer-p')
|
||||
(doom-force-kill-buffer buf t)
|
||||
(+evil--forget-file fname)
|
||||
(message "Successfully deleted %s" short-path)
|
||||
)))))))
|
||||
(message "Successfully deleted %s" short-path))))))))
|
||||
|
||||
(defun +evil--copy-file (old-path new-path &optional force-p)
|
||||
(let* ((new-path (expand-file-name new-path))
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
;;; feature/evil/autoload/folds.el
|
||||
;;; feature/evil/autoload/folds.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; It's frustrating how hideshow is a decent code folding implementation, but it
|
||||
;; won't let you create custom folds. Meanwhile, evil-vimish-fold offers custom
|
||||
;; folds, but essentially ignores any other type of folding (indent or custom
|
||||
;; markers, which hs-minor-mode gives you).
|
||||
;; `hideshow' is a decent code folding implementation, but it won't let you
|
||||
;; create custom folds. `evil-vimish-fold' offers custom folds, but essentially
|
||||
;; ignores any other type of folding (indent or custom markers, which
|
||||
;; hs-minor-mode gives you).
|
||||
;;
|
||||
;; So this is my effort to combine them.
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/evil/config.el
|
||||
;;; feature/evil/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; I'm a vimmer at heart. Its modal philosophy suits me better, and this module
|
||||
;; strives to make Emacs a much better vim than vim was.
|
||||
|
@ -41,7 +41,7 @@
|
|||
shift-select-mode nil)
|
||||
|
||||
:config
|
||||
(evil-mode +1)
|
||||
(add-hook 'emacs-startup-hook #'evil-mode)
|
||||
(evil-select-search-module 'evil-search-module 'evil-search)
|
||||
|
||||
(set! :popup
|
||||
|
@ -170,7 +170,7 @@ across windows."
|
|||
|
||||
|
||||
(def-package! evil-easymotion
|
||||
:defer 1
|
||||
:after evil-snipe
|
||||
:config
|
||||
(defvar +evil--snipe-repeat-fn
|
||||
(evilem-create #'evil-snipe-repeat
|
||||
|
@ -185,7 +185,6 @@ across windows."
|
|||
(setq evil-embrace-show-help-p nil)
|
||||
(evil-embrace-enable-evil-surround-integration)
|
||||
|
||||
;; Defuns
|
||||
(defun +evil--embrace-get-pair (char)
|
||||
(if-let (pair (cdr-safe (assoc (string-to-char char) evil-surround-pairs-alist)))
|
||||
pair
|
||||
|
@ -234,15 +233,15 @@ across windows."
|
|||
|
||||
|
||||
(def-package! evil-escape
|
||||
:demand t
|
||||
:commands evil-escape-mode
|
||||
:init
|
||||
(setq evil-escape-excluded-states '(normal visual multiedit emacs)
|
||||
evil-escape-excluded-major-modes '(neotree-mode)
|
||||
evil-escape-key-sequence "jk"
|
||||
evil-escape-delay 0.25)
|
||||
|
||||
(add-hook 'emacs-startup-hook #'evil-escape-mode)
|
||||
:config
|
||||
(evil-escape-mode +1)
|
||||
(map! :irvo "C-g" #'evil-escape))
|
||||
|
||||
|
||||
|
@ -330,7 +329,7 @@ the new algorithm is confusing, like in python or ruby."
|
|||
(?\] "[]})]")
|
||||
(?\; "[;:]")))
|
||||
:config
|
||||
(evil-snipe-override-mode +1))
|
||||
(add-hook 'emacs-startup-hook #'evil-snipe-override-mode))
|
||||
|
||||
|
||||
(def-package! evil-surround
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/file-templates/config.el
|
||||
;;; feature/file-templates/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(require! :feature snippets)
|
||||
|
||||
|
@ -31,12 +31,13 @@
|
|||
(overlay-get yas--active-field-overlay 'yas--field)))
|
||||
(evil-initialize-state 'insert))))
|
||||
|
||||
(defun +file-templates|add (regexp trigger mode &optional project-only-p)
|
||||
(define-auto-insert
|
||||
regexp
|
||||
(vector `(lambda () (+file-templates--expand ,trigger ',mode ,project-only-p)))))
|
||||
(defun +file-templates-add (args)
|
||||
(destructuring-bind (regexp trigger mode &optional project-only-p) args
|
||||
(define-auto-insert
|
||||
regexp
|
||||
(vector `(lambda () (+file-templates--expand ,trigger ',mode ,project-only-p))))))
|
||||
|
||||
(mapc (lambda (args) (apply #'+file-templates|add args))
|
||||
(mapc #'+file-templates-add
|
||||
;; General
|
||||
'(("/\\.gitignore$" "__" gitignore-mode)
|
||||
("/Dockerfile$" "__" dockerfile-mode)
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
;;; `(file-relative-name buffer-file-name doom-modules-dir)`
|
||||
;;; `(file-relative-name buffer-file-name doom-modules-dir)` -*- lexical-binding: t; -*-
|
||||
|
||||
$0
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; `(file-name-nondirectory buffer-file-name)`${1: --- ${2:description}}
|
||||
;;; `(file-name-nondirectory buffer-file-name)`${1: --- ${2:description}} -*- lexical-binding: t; -*-
|
||||
|
||||
$0
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/jump/autoload/evil.el
|
||||
;;; feature/jump/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload (autoload '+jump:online "feature/jump/autoload/evil" nil t)
|
||||
(evil-define-command +jump:online (query &optional bang)
|
||||
|
@ -6,11 +6,12 @@
|
|||
reuse it on consecutive uses of this command. If BANG, always prompt for search
|
||||
engine."
|
||||
(interactive "<a><!>")
|
||||
(setq query (or query (thing-at-point 'symbol t)))
|
||||
(unless query
|
||||
(user-error "The search query is empty"))
|
||||
(let ((engine (or (and (not bang) (bound-and-true-p +jump--online-last))
|
||||
(completing-read (format "Search on (%s): " query)
|
||||
(mapcar #'car +jump-search-url-alist)
|
||||
nil t))))
|
||||
(+jump/online engine query)))
|
||||
(let ((query (or query (thing-at-point 'symbol t))))
|
||||
(unless query
|
||||
(user-error "The search query is empty"))
|
||||
(+jump/online
|
||||
(or (and (not bang) (bound-and-true-p +jump--online-last))
|
||||
(completing-read (format "Search on (%s): " query)
|
||||
(mapcar #'car +jump-search-url-alist)
|
||||
nil t))
|
||||
query)))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/jump/autoload.el
|
||||
;;; feature/jump/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +jump--rg-installed-p (executable-find "rg"))
|
||||
(defvar +jump--ag-installed-p (executable-find "ag"))
|
||||
|
@ -42,12 +42,11 @@ Tries xref and falls back to `dumb-jump', then rg/ag, then
|
|||
|
||||
((and (featurep 'evil)
|
||||
evil-mode
|
||||
(let ((bounds (bounds-of-thing-at-point 'symbol))
|
||||
(orig-pt (point)))
|
||||
(destructuring-bind (beg end) (bounds-of-thing-at-point 'symbol)
|
||||
(evil-goto-definition)
|
||||
(let ((pt (point)))
|
||||
(not (and (>= pt (car bounds))
|
||||
(< pt (cdr bounds))))))))
|
||||
(not (and (>= pt beg)
|
||||
(< pt end)))))))
|
||||
|
||||
(t (user-error "Couldn't find '%s'" sym)))))
|
||||
|
||||
|
@ -58,9 +57,8 @@ Tries xref and falls back to `dumb-jump', then rg/ag, then
|
|||
Tries `xref-find-references' and falls back to rg/ag."
|
||||
(interactive)
|
||||
(let ((sym (thing-at-point 'symbol t)))
|
||||
(cond ((progn
|
||||
(ignore-errors (xref-find-references sym)
|
||||
t)))
|
||||
(cond ((ignore-errors (xref-find-references sym)
|
||||
t))
|
||||
|
||||
((and sym
|
||||
(featurep 'counsel)
|
||||
|
@ -87,7 +85,7 @@ Interactively, you are prompted to choose a source from
|
|||
(mapcar #'car +jump-search-url-alist)
|
||||
nil t))
|
||||
(thing-at-point 'symbol t)))
|
||||
(condition-case ex
|
||||
(condition-case _ex
|
||||
(let ((url (cdr (assoc where +jump-search-url-alist))))
|
||||
(unless url
|
||||
(error "'%s' is an invalid search engine" where))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/jump/config.el
|
||||
;;; feature/jump/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; "What am I looking at?"
|
||||
;;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/snippets/autoload/evil.el
|
||||
;;; feature/snippets/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +snippets/expand-on-region ()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/snippets/autoload/snippets.el
|
||||
;;; feature/snippets/autoload/snippets.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +snippets/goto-start-of-field ()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/snippets/config.el
|
||||
;;; feature/snippets/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Snippets! I've thrown together a few hacks to make `yasnippet' and `evil'
|
||||
;; behave together.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/spellcheck/config.el
|
||||
;;; feature/spellcheck/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! flyspell ; built-in
|
||||
:commands flyspell-mode
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/syntax-checker/config.el
|
||||
;;; feature/syntax-checker/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; pkg-info doesn't get autoloaded when `flycheck-version' needs it, so we do
|
||||
;; it ourselves:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/version-control/+git.el
|
||||
;;; feature/version-control/+git.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! gitconfig-mode
|
||||
:mode "/\\.?git/?config$"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/version-control/autoload.el
|
||||
;;; feature/version-control/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +vcs-root ()
|
||||
|
@ -15,8 +15,8 @@
|
|||
"Open the website for the current version controlled file. Fallback to
|
||||
repository root."
|
||||
(interactive)
|
||||
(let ((git-link-open-in-browser t))
|
||||
(call-interactively 'git-link)))
|
||||
(destructuring-bind (beg end) (if buffer-file-name (git-link--get-region))
|
||||
(git-link (git-link--select-remote) beg end)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +vcs/git-browse-issues ()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/version-control/config.el
|
||||
;;; feature/version-control/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(setq vc-make-backup-files nil)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/workspaces/autoload/evil.el
|
||||
;;; feature/workspaces/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload (autoload '+workspace:save-session "feature/workspaces/autoload/evil" nil t)
|
||||
(evil-define-command +workspace:save-session (&optional bang name)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/workspaces/autoload/workspaces.el
|
||||
;;; feature/workspaces/autoload/workspaces.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +workspace-workspace-file "_workspaces"
|
||||
"The file basename in which to store single workspace perspectives.")
|
||||
|
@ -139,12 +139,11 @@ perspective or its hash table."
|
|||
(persp-frame-switch name))
|
||||
|
||||
(defun +workspace--generate-id ()
|
||||
(let ((numbers (mapcar (lambda (it) (string-to-number (substring it 1)))
|
||||
(cl-remove-if-not (lambda (it) (string-match-p "^#[0-9]+$" it))
|
||||
(+workspace-list)))))
|
||||
(if numbers
|
||||
(1+ (car (sort numbers (lambda (it other) (> it other)))))
|
||||
1)))
|
||||
(or (cl-loop for name in (+workspace-list)
|
||||
when (string-match-p "^#[0-9]+$" name)
|
||||
maximize (string-to-number (substring name 1)) into max
|
||||
finally return (if max (1+ max)))
|
||||
1))
|
||||
|
||||
(defun +workspace-protected-p (name)
|
||||
(or (equal name persp-nil-name)
|
||||
|
@ -373,17 +372,16 @@ the workspace and move to the next."
|
|||
|
||||
(defun +workspace--tabline (&optional names)
|
||||
(let ((names (or names (+workspace-list)))
|
||||
(current-name (+workspace-current-name))
|
||||
(i 0))
|
||||
(current-name (+workspace-current-name)))
|
||||
(mapconcat
|
||||
#'identity
|
||||
(mapcar (lambda (it)
|
||||
(cl-incf i)
|
||||
(propertize (format " [%d] %s " i it)
|
||||
'face (if (equal current-name it)
|
||||
'+workspace-tab-selected-face
|
||||
'+workspace-tab-face)))
|
||||
names)
|
||||
(cl-loop for name in names
|
||||
for i to (length names)
|
||||
collect
|
||||
(propertize (format " [%d] %s " i name)
|
||||
'face (if (equal current-name name)
|
||||
'+workspace-tab-selected-face
|
||||
'+workspace-tab-face)))
|
||||
" ")))
|
||||
|
||||
(defun +workspace--message-body (message &optional type)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; feature/workspaces/config.el
|
||||
;;; feature/workspaces/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; `persp-mode' gives me workspaces, a workspace-restricted `buffer-list', and
|
||||
;; file-based session persistence. I had used workgroups2 before this, but
|
||||
|
@ -24,7 +24,8 @@ renamed.")
|
|||
;; Plugins
|
||||
;;
|
||||
|
||||
(def-package! persp-mode :demand t
|
||||
(def-package! persp-mode
|
||||
:demand t
|
||||
:config
|
||||
(setq persp-autokill-buffer-on-remove 'kill-weak
|
||||
persp-nil-name "nil"
|
||||
|
@ -44,35 +45,34 @@ renamed.")
|
|||
|
||||
(defun +workspaces|init (&rest _)
|
||||
(unless persp-mode
|
||||
(persp-mode +1))
|
||||
;; The default perspective persp-mode makes (defined by `persp-nil-name') is
|
||||
;; special and doesn't actually represent a real persp object, so buffers
|
||||
;; can't really be assigned to it, among other quirks. We create a *real*
|
||||
;; main workspace to fill this role.
|
||||
(persp-add-new +workspaces-main)
|
||||
;; Switch to it if we aren't auto-loading the last session
|
||||
(when (or (= persp-auto-resume-time -1)
|
||||
(equal (safe-persp-name (get-current-persp)) persp-nil-name))
|
||||
(persp-frame-switch +workspaces-main)))
|
||||
(persp-mode +1)
|
||||
;; The default perspective persp-mode makes (defined by `persp-nil-name')
|
||||
;; is special and doesn't actually represent a real persp object, so
|
||||
;; buffers can't really be assigned to it, among other quirks. We create a
|
||||
;; *real* main workspace to fill this role.
|
||||
(persp-add-new +workspaces-main)
|
||||
;; Switch to it if we aren't auto-loading the last session
|
||||
(when (or (= persp-auto-resume-time -1)
|
||||
(equal (safe-persp-name (get-current-persp)) persp-nil-name))
|
||||
(persp-frame-switch +workspaces-main))))
|
||||
|
||||
(add-hook! 'after-init-hook
|
||||
(if (display-graphic-p)
|
||||
(+workspaces|init)
|
||||
(add-hook 'after-make-frame-functions #'+workspaces|init)))
|
||||
(add-hook 'emacs-startup-hook #'+workspaces|init)
|
||||
(add-hook 'after-make-frame-functions #'+workspaces|init)
|
||||
|
||||
(define-key persp-mode-map [remap delete-window] #'+workspace/close-window-or-workspace)
|
||||
|
||||
;; Spawn a perspective for each new frame
|
||||
(setq persp-init-new-frame-behaviour-override nil
|
||||
persp-interactive-init-frame-behaviour-override
|
||||
(lambda (frame &optional new-frame-p)
|
||||
(lambda (frame &optional _new-frame-p)
|
||||
(select-frame frame)
|
||||
(+workspace/new)
|
||||
(set-frame-parameter frame 'assoc-persp (+workspace-current-name))))
|
||||
|
||||
(defun +workspaces*delete-frame-and-persp (frame)
|
||||
"Delete workspace associated with current frame IF it has no real buffers."
|
||||
(when (and (string= (or (frame-parameter frame 'assoc-persp) "") (+workspace-current-name))
|
||||
(when (and (string= (or (frame-parameter frame 'assoc-persp) "")
|
||||
(+workspace-current-name))
|
||||
(not (delq (doom-fallback-buffer) (doom-real-buffers-list))))
|
||||
(+workspace/delete persp-name)))
|
||||
(add-hook 'delete-frame-functions #'+workspaces*delete-frame-and-persp)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/assembly/config.el
|
||||
;;; lang/assembly/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! mips-mode :mode "\\.mips$")
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/cc/autoload.el
|
||||
;;; lang/cc/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +cc/autoclose->-maybe ()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/cc/config.el --- C, C++, and Objective-C
|
||||
;;; lang/cc/config.el --- c, c++, and obj-c -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! cc-mode
|
||||
:commands (c-mode c++-mode objc-mode java-mode)
|
||||
|
@ -67,7 +67,7 @@
|
|||
(c-set-offset 'arglist-intro '+)
|
||||
(c-set-offset 'arglist-close '0)
|
||||
|
||||
(defun +cc--c-lineup-inclass (langelem)
|
||||
(defun +cc--c-lineup-inclass (_langelem)
|
||||
(if (memq major-mode '(c-mode c++-mode))
|
||||
(let ((inclass (assq 'inclass c-syntactic-context)))
|
||||
(save-excursion
|
||||
|
@ -115,12 +115,15 @@
|
|||
(def-package! irony
|
||||
:after cc-mode
|
||||
:commands irony-install-server
|
||||
:init
|
||||
(add-hook! 'c-mode-common-hook
|
||||
(when (memq major-mode '(c-mode c++-mode objc-mode))
|
||||
(irony-mode +1)))
|
||||
:config
|
||||
:preface
|
||||
(setq irony-server-install-prefix (concat doom-etc-dir "irony-server/"))
|
||||
:init
|
||||
(defun +cc|init-irony-mode ()
|
||||
(when (memq major-mode '(c-mode c++-mode objc-mode))
|
||||
(when (file-directory-p irony-server-install-prefix)
|
||||
(irony-mode +1))))
|
||||
(add-hook 'c-mode-common-hook #'+cc|init-irony-mode)
|
||||
:config
|
||||
(add-hook! 'irony-mode-hook #'(irony-eldoc flycheck-mode))
|
||||
(add-hook! 'c++-mode-hook
|
||||
(make-local-variable 'irony-additional-clang-options)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/clojure/config.el
|
||||
;;; lang/clojure/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! clojure-mode
|
||||
:mode "\\.clj$")
|
||||
|
@ -6,5 +6,4 @@
|
|||
(def-package! cider
|
||||
:commands (cider-jack-in cider-mode)
|
||||
:config
|
||||
(setq
|
||||
nrepl-hide-special-buffers t))
|
||||
(setq nrepl-hide-special-buffers t))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; module-crystal.el
|
||||
;;; lang/crystal/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! crystal-mode
|
||||
:mode "\\.cr$"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; module-csharp.el
|
||||
;;; lang/csharp/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! csharp-mode :mode "\\.cs$")
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; module-data.el
|
||||
;;; lang/data/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(push '("/sxhkdrc" . conf-mode) auto-mode-alist)
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
;;; lang/elixir/autoload/elixir.el
|
||||
;;; lang/elixir/autoload/elixir.el -*- lexical-binding: t; -*-
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
;;; lang/elixir/config.el
|
||||
;;; lang/elixir/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! elixir-mode
|
||||
:mode ("\\.ex$")
|
||||
:mode "\\.ex$"
|
||||
:init
|
||||
(add-hook 'elixir-mode-hook #'turn-off-smartparens-mode)
|
||||
(add-hook 'elixir-mode-hook #'alchemist)
|
||||
(add-hook! 'elixir-mode-hook #'(turn-off-smartparens-mode alchemist))
|
||||
:config
|
||||
(set! :company-backend 'elixir-mode '(alchemist-company company-yasnippet)))
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/elm/config.el
|
||||
;;; lang/elm/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! elm-mode
|
||||
:mode "\\.elm$"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/emacs-lisp/autoload.el
|
||||
;;; lang/emacs-lisp/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +emacs-lisp/repl ()
|
||||
|
@ -20,18 +20,16 @@ to a pop up buffer."
|
|||
(buf (get-buffer-create "*doom eval*"))
|
||||
(inhibit-read-only t)
|
||||
lines)
|
||||
(unwind-protect
|
||||
(progn
|
||||
(with-current-buffer buf
|
||||
(read-only-mode +1)
|
||||
(erase-buffer)
|
||||
(setq-local scroll-margin 0)
|
||||
(emacs-lisp-mode)
|
||||
(prin1 result buf)
|
||||
(pp-buffer)
|
||||
(setq lines (count-lines (point-min) (point-max)))
|
||||
(goto-char (point-min))
|
||||
(if (> lines 1)
|
||||
(doom-popup-buffer buf)
|
||||
(message "%s" (buffer-substring (point-min) (point-max)))
|
||||
(kill-buffer buf)))))))
|
||||
(with-current-buffer buf
|
||||
(read-only-mode +1)
|
||||
(erase-buffer)
|
||||
(setq-local scroll-margin 0)
|
||||
(emacs-lisp-mode)
|
||||
(prin1 result buf)
|
||||
(pp-buffer)
|
||||
(setq lines (count-lines (point-min) (point-max)))
|
||||
(goto-char (point-min))
|
||||
(if (> lines 1)
|
||||
(doom-popup-buffer buf)
|
||||
(message "%s" (buffer-substring (point-min) (point-max)))
|
||||
(kill-buffer buf)))))
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
;;; lang/emacs-lisp/config.el
|
||||
;;; lang/emacs-lisp/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! elisp-mode ; built-in
|
||||
:mode ("/Cask$" . emacs-lisp-mode)
|
||||
:init
|
||||
(add-hook 'emacs-lisp-mode-hook #'+emacs-lisp|hook)
|
||||
|
||||
:config
|
||||
(set! :repl 'emacs-lisp-mode #'+emacs-lisp/repl)
|
||||
(set! :eval 'emacs-lisp-mode #'+emacs-lisp-eval)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/go/autoload.el
|
||||
;;; lang/go/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
;; TODO (defun +go/build ())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/go/config.el
|
||||
;;; lang/go/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! go-mode
|
||||
:mode "\\.go$"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; module-haskell.el
|
||||
;;; lang/haskell/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! haskell-mode
|
||||
:mode "\\.hs$"
|
||||
|
@ -10,7 +10,6 @@
|
|||
(load "haskell-mode-autoloads" nil t)
|
||||
|
||||
(set! :repl 'haskell-mode #'switch-to-haskell)
|
||||
|
||||
(push ".hi" completion-ignored-extensions)
|
||||
|
||||
(autoload 'switch-to-haskell "inf-haskell" nil t)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/java/config.el
|
||||
;;; lang/java/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; yasnippet defuns
|
||||
;;;###autoload
|
||||
|
@ -9,7 +9,7 @@
|
|||
|
||||
;;;###autoload
|
||||
(defun +java-android-mode-in-tags (&rest tags)
|
||||
(-contains? tags (android-mode-tag-name)))
|
||||
(cl-find (android-mode-tag-name) tags))
|
||||
|
||||
;;;###autoload
|
||||
(defun +java-android-mode-tag-name ()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/java/config.el
|
||||
;;; lang/java/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! meghanada
|
||||
:commands meghanada-mode
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/javascript/+screeps.el
|
||||
;;; lang/javascript/+screeps.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; TODO Constants may be out-of-date
|
||||
|
||||
|
@ -171,8 +171,9 @@
|
|||
:match "/screeps/.+$"
|
||||
:modes (+javascript-npm-mode))
|
||||
|
||||
(add-hook! '+javascript-screeps-mode-hook
|
||||
(defun +javascript|init-screeps-mode ()
|
||||
(when (eq major-mode 'js2-mode)
|
||||
(push 'javascript-jshint flycheck-disabled-checkers)
|
||||
(cl-pushnew 'javascript-jshint flycheck-disabled-checkers)
|
||||
(setq js2-additional-externs (append '("_") screeps-objects screeps-constants))))
|
||||
(add-hook '+javascript-screeps-mode-hook #'+javascript|init-screeps-mode)
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/javascript/autoload.el
|
||||
;;; lang/javascript/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +javascript-npm-conf (make-hash-table :test 'equal))
|
||||
|
||||
|
@ -17,13 +17,13 @@ ignore the cache."
|
|||
;;;###autoload
|
||||
(defun +javascript-npm-dep-p (packages &optional project-root refresh-p)
|
||||
(when-let (data (and (bound-and-true-p +javascript-npm-mode)
|
||||
(+javascript-npm-conf)))
|
||||
(+javascript-npm-conf project-root refresh-p)))
|
||||
(let ((deps (append (cdr (assq 'dependencies data))
|
||||
(cdr (assq 'devDependencies data)))))
|
||||
(cond ((listp packages)
|
||||
(funcall (if (eq (car packages) 'and)
|
||||
'cl-every
|
||||
'cl-some)
|
||||
#'cl-every
|
||||
#'cl-some)
|
||||
(lambda (pkg) (assq pkg deps))
|
||||
(if (listp packages) packages (list packages))))
|
||||
((symbolp packages)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;; lang/javascript/config.el
|
||||
;;; lang/javascript/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(load! +screeps)
|
||||
|
||||
|
@ -17,12 +17,13 @@
|
|||
(add-hook! 'js2-mode-hook (setq js-switch-indent-offset js-indent-level))
|
||||
|
||||
;; Favor local eslint over global, if available
|
||||
(add-hook! 'flycheck-mode-hook
|
||||
(defun +javascript|init-flycheck-elint ()
|
||||
(when (derived-mode-p 'js-mode 'js2-mode)
|
||||
(when-let ((eslint (expand-file-name "node_modules/eslint/bin/eslint.js" (doom-project-root)))
|
||||
(exists-p (file-exists-p eslint))
|
||||
(executable-p (file-executable-p eslint)))
|
||||
(setq-local flycheck-javascript-eslint-executable eslint))))
|
||||
(add-hook 'flycheck-mode-hook #'+javascript|init-flycheck-elint)
|
||||
|
||||
(set! :repl 'js2-mode '+javascript/repl)
|
||||
(set! :electric 'js2-mode :chars '(?\} ?\) ?.))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/julia/autoload.el
|
||||
;;; lang/julia/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +julia/repl ()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/julia/config.el
|
||||
;;; lang/julia/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package julia-mode
|
||||
:mode "\\.jl$"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/latex/config.el
|
||||
;;; lang/latex/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +latex-bibtex-dir "~/work/writing/biblio/"
|
||||
"Where bibtex files are kept.")
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
;;; lang/lua/autoload.el
|
||||
;;; lang/lua/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +lua/repl ()
|
||||
"Open Lua REPL."
|
||||
(interactive)
|
||||
(lua-start-process "lua" "lua")
|
||||
(pop-to-buffer lua-process-buffer))
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/lua/config.el --- lua + Love2D
|
||||
;;; lang/lua/config.el --- lua + Love2D -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! lua-mode
|
||||
:mode "\\.lua$"
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
;;; lang/markdown/autoload.el
|
||||
;;; lang/markdown/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Implement strike-through formatting
|
||||
(defvar +text--markdown-regex-del
|
||||
(defvar +markdown--regex-del
|
||||
"\\(^\\|[^\\]\\)\\(\\(~\\{2\\}\\)\\([^ \n \\]\\|[^ \n ]\\(?:.\\|\n[^\n]\\)*?[^\\ ]\\)\\(\\3\\)\\)")
|
||||
|
||||
;;;###autoload
|
||||
|
@ -11,12 +11,13 @@
|
|||
(let ((delim "~~"))
|
||||
(if (markdown-use-region-p)
|
||||
;; Active region
|
||||
(let ((bounds (markdown-unwrap-things-in-region
|
||||
(region-beginning) (region-end)
|
||||
+text--markdown-regex-del 2 4)))
|
||||
(markdown-wrap-or-insert delim delim nil (car bounds) (cdr bounds)))
|
||||
(destructuring-bind (beg end)
|
||||
(markdown-unwrap-things-in-region
|
||||
(region-beginning) (region-end)
|
||||
+markdown--regex-del 2 4)
|
||||
(markdown-wrap-or-insert delim delim nil beg end))
|
||||
;; Bold markup removal, bold word at point, or empty markup insertion
|
||||
(if (thing-at-point-looking-at +text--markdown-regex-del)
|
||||
(if (thing-at-point-looking-at +markdown--regex-del)
|
||||
(markdown-unwrap-thing-at-point nil 2 4)
|
||||
(markdown-wrap-or-insert delim delim 'word nil nil)))))
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/markdown/config.el
|
||||
;;; lang/markdown/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! markdown-mode
|
||||
:mode ("\\.m\\(d\\|arkdown\\)$" "/README$"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/php/autoload.el
|
||||
;;; lang/php/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +php-composer-conf (make-hash-table :test 'equal))
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
;;; lang/php/config.el
|
||||
;;; lang/php/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; (def-package! hack-mode
|
||||
;; :mode "\\.hh$"
|
||||
|
@ -52,7 +52,8 @@
|
|||
;; company will set up itself
|
||||
(advice-add #'php-extras-company-setup :override #'ignore)
|
||||
:config
|
||||
(setq php-extras-eldoc-functions-file (concat doom-etc-dir "php-extras-eldoc-functions"))
|
||||
(setq php-extras-eldoc-functions-file
|
||||
(concat doom-etc-dir "php-extras-eldoc-functions"))
|
||||
|
||||
;; Make expensive php-extras generation async
|
||||
(unless (file-exists-p (concat php-extras-eldoc-functions-file ".el"))
|
||||
|
|
|
@ -1,25 +1,20 @@
|
|||
;;; lang/purescript/config.el
|
||||
;;; lang/purescript/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! purescript-mode
|
||||
:mode "\\.purs$"
|
||||
:config
|
||||
(add-hook! 'purescript-mode-hook #'flycheck-mode)
|
||||
(add-hook! 'purescript-mode-hook #'company-mode)
|
||||
(add-hook! 'purescript-mode-hook #'purescript-indentation-mode)
|
||||
(add-hook! 'purescript-mode-hook #'rainbow-delimiters-mode)
|
||||
(load "purescript-mode-autoloads" nil t)
|
||||
)
|
||||
(add-hook! 'purescript-mode-hook
|
||||
#'(flycheck-mode purescript-indentation-mode rainbow-delimiters-mode))
|
||||
|
||||
(load "purescript-mode-autoloads" nil t))
|
||||
|
||||
;; (def-package! flycheck-purescript
|
||||
;; :after purescript-mode
|
||||
;; :config
|
||||
;; (add-hook! 'flycheck-mode-hook #'flycheck-purescript-setup)
|
||||
;; )
|
||||
;; (add-hook 'flycheck-mode-hook #'flycheck-purescript-setup))
|
||||
|
||||
(def-package! psc-ide
|
||||
:after purescript-mode
|
||||
:config
|
||||
(require 'psc-ide)
|
||||
(add-hook! 'purescript-mode-hook #'psc-ide-mode)
|
||||
)
|
||||
(add-hook 'purescript-mode-hook #'psc-ide-mode))
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue