mirror of
https://gitlab.com/dwt1/dotfiles.git
synced 2026-04-22 02:50:24 +10:00
Moving to Doom Emacs!
This commit is contained in:
11
.emacs.d/modules/ui/deft/README.org
Normal file
11
.emacs.d/modules/ui/deft/README.org
Normal file
@@ -0,0 +1,11 @@
|
||||
#+TITLE: :ui deft
|
||||
|
||||
[[https://jblevins.org/projects/deft/][Deft]] is a major mode for browsing and filtering notes written in plain text
|
||||
formats, such as org-mode, markdown, and LaTeX.
|
||||
|
||||
To use this module, in your config file set the value of the variable ~deft-directory~ to the folder in which you
|
||||
want to keep your notes.
|
||||
|
||||
The default note format is org-mode. You can change this by setting the value of
|
||||
the variable ~deft-default-extension~. Changing the value to ~"md"~ for example,
|
||||
will change the default note format to markdown.
|
||||
32
.emacs.d/modules/ui/deft/config.el
Normal file
32
.emacs.d/modules/ui/deft/config.el
Normal file
@@ -0,0 +1,32 @@
|
||||
;;; ui/deft/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! deft
|
||||
:commands deft
|
||||
:init
|
||||
(setq deft-extensions '("org" "md" "tex" "txt")
|
||||
deft-default-extension "org"
|
||||
;; de-couples filename and note title:
|
||||
deft-use-filename-as-title nil
|
||||
deft-use-filter-string-for-filename t
|
||||
deft-org-mode-title-prefix t
|
||||
;; converts the filter string into a readable file-name using kebab-case:
|
||||
deft-file-naming-rules
|
||||
'((noslash . "-")
|
||||
(nospace . "-")
|
||||
(case-fn . downcase)))
|
||||
:config
|
||||
;; start filtering immediately
|
||||
(set-evil-initial-state! 'deft-mode 'insert)
|
||||
(map! :map deft-mode-map
|
||||
:localleader
|
||||
"RET" #'deft-new-file-named
|
||||
"a" #'deft-archive-file
|
||||
"c" #'deft-filter-clear
|
||||
"d" #'deft-delete-file
|
||||
"f" #'deft-find-file
|
||||
"g" #'deft-refresh
|
||||
"l" #'deft-filter
|
||||
"n" #'deft-new-file
|
||||
"r" #'deft-rename-file
|
||||
"s" #'deft-toggle-sort-method
|
||||
"t" #'deft-toggle-incremental-search))
|
||||
4
.emacs.d/modules/ui/deft/packages.el
Normal file
4
.emacs.d/modules/ui/deft/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/deft/packages.el
|
||||
|
||||
(package! deft)
|
||||
20
.emacs.d/modules/ui/doom-dashboard/README.org
Normal file
20
.emacs.d/modules/ui/doom-dashboard/README.org
Normal file
@@ -0,0 +1,20 @@
|
||||
#+TITLE: ui/doom-dashboard
|
||||
#+DATE: October 9, 2019
|
||||
#+SINCE: v1.3
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
|
||||
* Description
|
||||
This module gives Doom Emacs a dashboard buffer.
|
||||
|
||||
It is loosely inspired by Atom's dashboard.
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
* Prerequisites
|
||||
This module only requires that ~all-the-icons~'s icon fonts are installed. Use ~M-x all-the-icons-install-fonts~ to do so.
|
||||
28
.emacs.d/modules/ui/doom-dashboard/autoload.el
Normal file
28
.emacs.d/modules/ui/doom-dashboard/autoload.el
Normal file
@@ -0,0 +1,28 @@
|
||||
;;; ui/doom-dashboard/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defun +doom-dashboard--help-echo ()
|
||||
(when-let* ((btn (button-at (point)))
|
||||
(msg (button-get btn 'help-echo)))
|
||||
(message "%s" msg)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +doom-dashboard/open (frame)
|
||||
"Switch to the dashboard in the current window, of the current FRAME."
|
||||
(interactive (list (selected-frame)))
|
||||
(with-selected-frame frame
|
||||
(switch-to-buffer (doom-fallback-buffer))
|
||||
(+doom-dashboard-reload t)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +doom-dashboard/forward-button (n)
|
||||
"Like `forward-button', but don't wrap."
|
||||
(interactive "p")
|
||||
(forward-button n nil)
|
||||
(+doom-dashboard--help-echo))
|
||||
|
||||
;;;###autoload
|
||||
(defun +doom-dashboard/backward-button (n)
|
||||
"Like `backward-button', but don't wrap."
|
||||
(interactive "p")
|
||||
(backward-button n nil)
|
||||
(+doom-dashboard--help-echo))
|
||||
455
.emacs.d/modules/ui/doom-dashboard/config.el
Normal file
455
.emacs.d/modules/ui/doom-dashboard/config.el
Normal file
@@ -0,0 +1,455 @@
|
||||
;;; ui/doom-dashboard/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +doom-dashboard-name "*doom*"
|
||||
"The name to use for the dashboard buffer.")
|
||||
|
||||
(defvar +doom-dashboard-functions
|
||||
'(doom-dashboard-widget-banner
|
||||
doom-dashboard-widget-shortmenu
|
||||
doom-dashboard-widget-loaded
|
||||
doom-dashboard-widget-footer)
|
||||
"List of widget functions to run in the dashboard buffer to construct the
|
||||
dashboard. These functions take no arguments and the dashboard buffer is current
|
||||
while they run.")
|
||||
|
||||
(defvar +doom-dashboard-banner-file "default.png"
|
||||
"The path to the image file to be used in on the dashboard. The path is
|
||||
relative to `+doom-dashboard-banner-dir'. If nil, always use the ASCII banner.")
|
||||
|
||||
(defvar +doom-dashboard-banner-dir (concat (dir!) "/banners/")
|
||||
"Where to look for `+doom-dashboard-banner-file'.")
|
||||
|
||||
(defvar +doom-dashboard-banner-padding '(4 . 4)
|
||||
"Number of newlines to pad the banner with, above and below, respectively.")
|
||||
|
||||
(defvar +doom-dashboard-inhibit-refresh nil
|
||||
"If non-nil, the doom buffer won't be refreshed.")
|
||||
|
||||
(defvar +doom-dashboard-inhibit-functions ()
|
||||
"A list of functions which take no arguments. If any of them return non-nil,
|
||||
dashboard reloading is inhibited.")
|
||||
|
||||
(defvar +doom-dashboard-pwd-policy 'last-project
|
||||
"The policy to use when setting the `default-directory' in the dashboard.
|
||||
|
||||
Possible values:
|
||||
|
||||
'last-project the `doom-project-root' of the last open buffer
|
||||
'last the `default-directory' of the last open buffer
|
||||
a FUNCTION a function run with the `default-directory' of the last
|
||||
open buffer, that returns a directory path
|
||||
a STRING a fixed path
|
||||
nil `default-directory' will never change")
|
||||
|
||||
(defvar +doom-dashboard-menu-sections
|
||||
'(("Reload last session"
|
||||
:icon (all-the-icons-octicon "history" :face 'font-lock-keyword-face)
|
||||
:when (cond ((require 'persp-mode nil t)
|
||||
(file-exists-p (expand-file-name persp-auto-save-fname persp-save-dir)))
|
||||
((require 'desktop nil t)
|
||||
(file-exists-p (desktop-full-file-name))))
|
||||
:face (:inherit (font-lock-keyword-face bold))
|
||||
:action doom/quickload-session)
|
||||
("Open org-agenda"
|
||||
:icon (all-the-icons-octicon "calendar" :face 'font-lock-keyword-face)
|
||||
:when (fboundp 'org-agenda)
|
||||
:action org-agenda)
|
||||
("Recently opened files"
|
||||
:icon (all-the-icons-octicon "file-text" :face 'font-lock-keyword-face)
|
||||
:action recentf-open-files)
|
||||
("Open project"
|
||||
:icon (all-the-icons-octicon "briefcase" :face 'font-lock-keyword-face)
|
||||
:action projectile-switch-project)
|
||||
("Jump to bookmark"
|
||||
:icon (all-the-icons-octicon "bookmark" :face 'font-lock-keyword-face)
|
||||
:action bookmark-jump)
|
||||
("Open private configuration"
|
||||
:icon (all-the-icons-octicon "tools" :face 'font-lock-keyword-face)
|
||||
:when (file-directory-p doom-private-dir)
|
||||
:action doom/open-private-config)
|
||||
("Search Documentation"
|
||||
:icon (all-the-icons-octicon "book" :face 'font-lock-keyword-face)
|
||||
:action doom/help-search))
|
||||
"An alist of menu buttons used by `doom-dashboard-widget-shortmenu'. Each
|
||||
element is a cons cell (LABEL . PLIST). LABEL is a string to display after the
|
||||
icon and before the key string.
|
||||
|
||||
PLIST can have the following properties:
|
||||
|
||||
:icon FORM
|
||||
Uses the return value of FORM as an icon (can be literal string).
|
||||
:key STRING
|
||||
The keybind displayed next to the button.
|
||||
:when FORM
|
||||
If FORM returns nil, don't display this button.
|
||||
:face FACE
|
||||
Displays the icon and text with FACE (a face symbol).
|
||||
:action FORM
|
||||
Run FORM when the button is pushed.")
|
||||
|
||||
;;
|
||||
(defvar +doom-dashboard--last-cwd nil)
|
||||
(defvar +doom-dashboard--width 80)
|
||||
(defvar +doom-dashboard--old-fringe-indicator fringe-indicator-alist)
|
||||
(defvar +doom-dashboard--pwd-alist ())
|
||||
(defvar +doom-dashboard--reload-timer nil)
|
||||
|
||||
(defvar all-the-icons-scale-factor)
|
||||
(defvar all-the-icons-default-adjust)
|
||||
|
||||
|
||||
;;
|
||||
;;; Bootstrap
|
||||
|
||||
(defun +doom-dashboard-init-h ()
|
||||
"Initializes Doom's dashboard."
|
||||
(unless noninteractive
|
||||
;; Ensure the dashboard becomes Emacs' go-to buffer when there's nothing
|
||||
;; else to show.
|
||||
(setq doom-fallback-buffer-name +doom-dashboard-name
|
||||
initial-buffer-choice #'doom-fallback-buffer)
|
||||
(unless fancy-splash-image
|
||||
(setq fancy-splash-image
|
||||
(expand-file-name +doom-dashboard-banner-file
|
||||
+doom-dashboard-banner-dir)))
|
||||
(when (equal (buffer-name) "*scratch*")
|
||||
(set-window-buffer nil (doom-fallback-buffer))
|
||||
(if (daemonp)
|
||||
(add-hook 'after-make-frame-functions #'+doom-dashboard-reload-frame-h)
|
||||
(+doom-dashboard-reload)))
|
||||
;; Ensure the dashboard is up-to-date whenever it is switched to or resized.
|
||||
(add-hook 'window-configuration-change-hook #'+doom-dashboard-resize-h)
|
||||
(add-hook 'window-size-change-functions #'+doom-dashboard-resize-h)
|
||||
(add-hook 'doom-switch-buffer-hook #'+doom-dashboard-reload-maybe-h)
|
||||
(add-hook 'delete-frame-functions #'+doom-dashboard-reload-frame-h)
|
||||
;; `persp-mode' integration: update `default-directory' when switching perspectives
|
||||
(add-hook 'persp-created-functions #'+doom-dashboard--persp-record-project-h)
|
||||
(add-hook 'persp-activated-functions #'+doom-dashboard--persp-detect-project-h)
|
||||
(add-hook 'persp-before-switch-functions #'+doom-dashboard--persp-record-project-h)))
|
||||
|
||||
(add-hook 'doom-init-ui-hook #'+doom-dashboard-init-h)
|
||||
|
||||
|
||||
;;
|
||||
;;; Major mode
|
||||
|
||||
(define-derived-mode +doom-dashboard-mode special-mode
|
||||
(format "DOOM v%s" doom-version)
|
||||
"Major mode for the DOOM dashboard buffer."
|
||||
:syntax-table nil
|
||||
:abbrev-table nil
|
||||
(buffer-disable-undo)
|
||||
(setq truncate-lines t)
|
||||
(setq-local whitespace-style nil)
|
||||
(setq-local show-trailing-whitespace nil)
|
||||
(setq-local hscroll-margin 0)
|
||||
(setq-local tab-width 2)
|
||||
;; Don't scroll to follow cursor
|
||||
(setq-local scroll-preserve-screen-position nil)
|
||||
(setq-local auto-hscroll-mode nil)
|
||||
(cl-loop for (car . _cdr) in fringe-indicator-alist
|
||||
collect (cons car nil) into alist
|
||||
finally do (setq fringe-indicator-alist alist))
|
||||
;; Ensure point is always on a button
|
||||
(add-hook 'post-command-hook #'+doom-dashboard-reposition-point-h nil t))
|
||||
|
||||
(define-key! +doom-dashboard-mode-map
|
||||
[left-margin mouse-1] #'ignore
|
||||
[remap forward-button] #'+doom-dashboard/forward-button
|
||||
[remap backward-button] #'+doom-dashboard/backward-button
|
||||
"n" #'forward-button
|
||||
"p" #'backward-button
|
||||
"C-n" #'forward-button
|
||||
"C-p" #'backward-button
|
||||
[down] #'forward-button
|
||||
[up] #'backward-button
|
||||
[tab] #'forward-button
|
||||
[backtab] #'backward-button
|
||||
|
||||
;; Evil remaps
|
||||
[remap evil-next-line] #'forward-button
|
||||
[remap evil-previous-line] #'backward-button
|
||||
[remap evil-next-visual-line] #'forward-button
|
||||
[remap evil-previous-visual-line] #'backward-button
|
||||
[remap evil-paste-pop-next] #'forward-button
|
||||
[remap evil-paste-pop] #'backward-button
|
||||
[remap evil-delete] #'ignore
|
||||
[remap evil-delete-line] #'ignore
|
||||
[remap evil-insert] #'ignore
|
||||
[remap evil-append] #'ignore
|
||||
[remap evil-replace] #'ignore
|
||||
[remap evil-replace-state] #'ignore
|
||||
[remap evil-change] #'ignore
|
||||
[remap evil-change-line] #'ignore
|
||||
[remap evil-visual-char] #'ignore
|
||||
[remap evil-visual-line] #'ignore)
|
||||
|
||||
|
||||
;;
|
||||
;;; Hooks
|
||||
|
||||
(defun +doom-dashboard-reposition-point-h ()
|
||||
"Trap the point in the buttons."
|
||||
(when (region-active-p)
|
||||
(setq deactivate-mark t)
|
||||
(when (bound-and-true-p evil-local-mode)
|
||||
(evil-change-to-previous-state)))
|
||||
(or (ignore-errors
|
||||
(if (button-at (point))
|
||||
(forward-button 0)
|
||||
(backward-button 1)))
|
||||
(ignore-errors
|
||||
(goto-char (point-min))
|
||||
(forward-button 1))))
|
||||
|
||||
(defun +doom-dashboard-reload-maybe-h ()
|
||||
"Reload the dashboard or its state.
|
||||
|
||||
If this isn't a dashboard buffer, move along, but record its `default-directory'
|
||||
if the buffer is real. See `doom-real-buffer-p' for an explanation for what
|
||||
'real' means.
|
||||
|
||||
If this is the dashboard buffer, reload it completely."
|
||||
(cond ((+doom-dashboard-p (current-buffer))
|
||||
(let (+doom-dashboard-inhibit-refresh)
|
||||
(ignore-errors (+doom-dashboard-reload))))
|
||||
((and (not (file-remote-p default-directory))
|
||||
(doom-real-buffer-p (current-buffer)))
|
||||
(setq +doom-dashboard--last-cwd default-directory)
|
||||
(+doom-dashboard-update-pwd))))
|
||||
|
||||
(defun +doom-dashboard-reload-frame-h (_frame)
|
||||
"Reload the dashboard after a brief pause. This is necessary for new frames,
|
||||
whose dimensions may not be fully initialized by the time this is run."
|
||||
(when (timerp +doom-dashboard--reload-timer)
|
||||
(cancel-timer +doom-dashboard--reload-timer)) ; in case this function is run rapidly
|
||||
(setq +doom-dashboard--reload-timer (run-with-timer 0.1 nil #'+doom-dashboard-reload t)))
|
||||
|
||||
(defun +doom-dashboard-resize-h (&rest _)
|
||||
"Recenter the dashboard, and reset its margins and fringes."
|
||||
(let (buffer-list-update-hook
|
||||
window-configuration-change-hook
|
||||
window-size-change-functions)
|
||||
(let ((windows (get-buffer-window-list (doom-fallback-buffer) nil t)))
|
||||
(dolist (win windows)
|
||||
(set-window-start win 0)
|
||||
(set-window-fringes win 0 0)
|
||||
(set-window-margins
|
||||
win (max 0 (/ (- (window-total-width win) +doom-dashboard--width) 2))))
|
||||
(when windows
|
||||
(with-current-buffer (doom-fallback-buffer)
|
||||
(save-excursion
|
||||
(with-silent-modifications
|
||||
(goto-char (point-min))
|
||||
(delete-region (line-beginning-position)
|
||||
(save-excursion (skip-chars-forward "\n")
|
||||
(point)))
|
||||
(insert (make-string
|
||||
(max 0 (- (/ (window-height (get-buffer-window)) 2)
|
||||
(round (/ (+ (count-lines (point-min) (point-max))
|
||||
(car +doom-dashboard-banner-padding))
|
||||
2))))
|
||||
?\n)))))))))
|
||||
|
||||
(defun +doom-dashboard--persp-detect-project-h (&rest _)
|
||||
"Check for a `last-project-root' parameter in the perspective, and set the
|
||||
dashboard's `default-directory' to it if it exists.
|
||||
|
||||
This and `+doom-dashboard--persp-record-project-h' provides `persp-mode' integration with
|
||||
the Doom dashboard. It ensures that the dashboard is always in the correct
|
||||
project (which may be different across perspective)."
|
||||
(when (bound-and-true-p persp-mode)
|
||||
(when-let (pwd (persp-parameter 'last-project-root))
|
||||
(+doom-dashboard-update-pwd pwd))))
|
||||
|
||||
(defun +doom-dashboard--persp-record-project-h (&optional persp &rest _)
|
||||
"Record the last `doom-project-root' for the current perspective. See
|
||||
`+doom-dashboard--persp-detect-project-h' for more information."
|
||||
(when (bound-and-true-p persp-mode)
|
||||
(set-persp-parameter
|
||||
'last-project-root (doom-project-root)
|
||||
(if (persp-p persp)
|
||||
persp
|
||||
(get-current-persp)))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Library
|
||||
|
||||
(defun +doom-dashboard-p (buffer)
|
||||
"Returns t if BUFFER is the dashboard buffer."
|
||||
(eq buffer (get-buffer +doom-dashboard-name)))
|
||||
|
||||
(defun +doom-dashboard-update-pwd (&optional pwd)
|
||||
"Update `default-directory' in the Doom dashboard buffer. What it is set to is
|
||||
controlled by `+doom-dashboard-pwd-policy'."
|
||||
(if pwd
|
||||
(with-current-buffer (doom-fallback-buffer)
|
||||
(doom-log "Changed dashboard's PWD to %s" pwd)
|
||||
(setq-local default-directory pwd))
|
||||
(let ((new-pwd (+doom-dashboard--get-pwd)))
|
||||
(when (and new-pwd (file-accessible-directory-p new-pwd))
|
||||
(+doom-dashboard-update-pwd
|
||||
(concat (directory-file-name new-pwd)
|
||||
"/"))))))
|
||||
|
||||
(defun +doom-dashboard-reload (&optional force)
|
||||
"Update the DOOM scratch buffer (or create it, if it doesn't exist)."
|
||||
(when (or (and (not +doom-dashboard-inhibit-refresh)
|
||||
(get-buffer-window (doom-fallback-buffer))
|
||||
(not (window-minibuffer-p (frame-selected-window)))
|
||||
(not (run-hook-with-args-until-success '+doom-dashboard-inhibit-functions)))
|
||||
force)
|
||||
(with-current-buffer (doom-fallback-buffer)
|
||||
(doom-log "Reloading dashboard at %s" (format-time-string "%T"))
|
||||
(with-silent-modifications
|
||||
(let ((pt (point)))
|
||||
(unless (eq major-mode '+doom-dashboard-mode)
|
||||
(+doom-dashboard-mode))
|
||||
(erase-buffer)
|
||||
(run-hooks '+doom-dashboard-functions)
|
||||
(goto-char pt)
|
||||
(+doom-dashboard-reposition-point-h))
|
||||
(+doom-dashboard-resize-h)
|
||||
(+doom-dashboard--persp-detect-project-h)
|
||||
(+doom-dashboard-update-pwd)
|
||||
(current-buffer)))))
|
||||
|
||||
;; helpers
|
||||
(defun +doom-dashboard--center (len s)
|
||||
(concat (make-string (ceiling (max 0 (- len (length s))) 2) ? )
|
||||
s))
|
||||
|
||||
(defun +doom-dashboard--get-pwd ()
|
||||
(let ((lastcwd +doom-dashboard--last-cwd)
|
||||
(policy +doom-dashboard-pwd-policy))
|
||||
(cond ((null policy)
|
||||
default-directory)
|
||||
((stringp policy)
|
||||
(expand-file-name policy lastcwd))
|
||||
((functionp policy)
|
||||
(funcall policy lastcwd))
|
||||
((null lastcwd)
|
||||
default-directory)
|
||||
((eq policy 'last-project)
|
||||
(let ((cwd default-directory))
|
||||
(or (doom-project-root lastcwd)
|
||||
cwd)))
|
||||
((eq policy 'last)
|
||||
lastcwd)
|
||||
((warn "`+doom-dashboard-pwd-policy' has an invalid value of '%s'"
|
||||
policy)))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Widgets
|
||||
|
||||
(defun doom-dashboard-widget-banner ()
|
||||
(let ((point (point)))
|
||||
(mapc (lambda (line)
|
||||
(insert (propertize (+doom-dashboard--center +doom-dashboard--width line)
|
||||
'face 'font-lock-comment-face) " ")
|
||||
(insert "\n"))
|
||||
'("================= =============== =============== ======== ========"
|
||||
"\\\\ . . . . . . .\\\\ //. . . . . . .\\\\ //. . . . . . .\\\\ \\\\. . .\\\\// . . //"
|
||||
"||. . ._____. . .|| ||. . ._____. . .|| ||. . ._____. . .|| || . . .\\/ . . .||"
|
||||
"|| . .|| ||. . || || . .|| ||. . || || . .|| ||. . || ||. . . . . . . ||"
|
||||
"||. . || || . .|| ||. . || || . .|| ||. . || || . .|| || . | . . . . .||"
|
||||
"|| . .|| ||. _-|| ||-_ .|| ||. . || || . .|| ||. _-|| ||-_.|\\ . . . . ||"
|
||||
"||. . || ||-' || || `-|| || . .|| ||. . || ||-' || || `|\\_ . .|. .||"
|
||||
"|| . _|| || || || || ||_ . || || . _|| || || || |\\ `-_/| . ||"
|
||||
"||_-' || .|/ || || \\|. || `-_|| ||_-' || .|/ || || | \\ / |-_.||"
|
||||
"|| ||_-' || || `-_|| || || ||_-' || || | \\ / | `||"
|
||||
"|| `' || || `' || || `' || || | \\ / | ||"
|
||||
"|| .===' `===. .==='.`===. .===' /==. | \\/ | ||"
|
||||
"|| .==' \\_|-_ `===. .===' _|_ `===. .===' _-|/ `== \\/ | ||"
|
||||
"|| .==' _-' `-_ `=' _-' `-_ `=' _-' `-_ /| \\/ | ||"
|
||||
"|| .==' _-' '-__\\._-' '-_./__-' `' |. /| | ||"
|
||||
"||.==' _-' `' | /==.||"
|
||||
"==' _-' E M A C S \\/ `=="
|
||||
"\\ _-' `-_ /"
|
||||
" `'' ``'"))
|
||||
(when (and (display-graphic-p)
|
||||
(stringp fancy-splash-image)
|
||||
(file-readable-p fancy-splash-image))
|
||||
(let ((image (create-image (fancy-splash-image-file))))
|
||||
(add-text-properties
|
||||
point (point) `(display ,image rear-nonsticky (display)))
|
||||
(save-excursion
|
||||
(goto-char point)
|
||||
(insert (make-string
|
||||
(truncate
|
||||
(max 0 (+ 1 (/ (- +doom-dashboard--width
|
||||
(car (image-size image nil)))
|
||||
2))))
|
||||
? ))))
|
||||
(insert (make-string (or (cdr +doom-dashboard-banner-padding) 0)
|
||||
?\n)))))
|
||||
|
||||
(defun doom-dashboard-widget-loaded ()
|
||||
(insert
|
||||
"\n\n"
|
||||
(propertize
|
||||
(+doom-dashboard--center
|
||||
+doom-dashboard--width
|
||||
(doom-display-benchmark-h 'return))
|
||||
'face 'font-lock-comment-face)
|
||||
"\n"))
|
||||
|
||||
(defun doom-dashboard-widget-shortmenu ()
|
||||
(let ((all-the-icons-scale-factor 1.45)
|
||||
(all-the-icons-default-adjust -0.02))
|
||||
(insert "\n")
|
||||
(dolist (section +doom-dashboard-menu-sections)
|
||||
(cl-destructuring-bind (label &key icon action when face) section
|
||||
(when (and (fboundp action)
|
||||
(or (null when)
|
||||
(eval when t)))
|
||||
(insert
|
||||
(+doom-dashboard--center
|
||||
(- +doom-dashboard--width 1)
|
||||
(let ((icon (if (stringp icon) icon (eval icon t))))
|
||||
(format (format "%s%%s%%-10s" (if icon "%3s\t" "%3s"))
|
||||
(or icon "")
|
||||
(with-temp-buffer
|
||||
(insert-text-button
|
||||
label
|
||||
'action
|
||||
`(lambda (_)
|
||||
(call-interactively (or (command-remapping #',action)
|
||||
#',action)))
|
||||
'face (or face 'font-lock-keyword-face)
|
||||
'follow-link t
|
||||
'help-echo
|
||||
(format "%s (%s)" label
|
||||
(propertize (symbol-name action) 'face 'font-lock-constant-face)))
|
||||
(format "%-37s" (buffer-string)))
|
||||
;; Lookup command keys dynamically
|
||||
(or (when-let (key (where-is-internal action nil t))
|
||||
(with-temp-buffer
|
||||
(save-excursion (insert (key-description key)))
|
||||
(while (re-search-forward "<\\([^>]+\\)>" nil t)
|
||||
(let ((str (match-string 1)))
|
||||
(replace-match
|
||||
(upcase (if (< (length str) 3)
|
||||
str
|
||||
(substring str 0 3))))))
|
||||
(propertize (buffer-string) 'face 'font-lock-constant-face)))
|
||||
""))))
|
||||
(if (display-graphic-p)
|
||||
"\n\n"
|
||||
"\n")))))))
|
||||
|
||||
(defun doom-dashboard-widget-footer ()
|
||||
(insert
|
||||
"\n"
|
||||
(+doom-dashboard--center
|
||||
(- +doom-dashboard--width 2)
|
||||
(with-temp-buffer
|
||||
(insert-text-button (or (all-the-icons-octicon "octoface" :face 'all-the-icons-green :height 1.3 :v-adjust -0.15)
|
||||
(propertize "github" 'face 'font-lock-keyword-face))
|
||||
'action (lambda (_) (browse-url "https://github.com/hlissner/doom-emacs"))
|
||||
'follow-link t
|
||||
'help-echo "Open Doom Emacs github page")
|
||||
(buffer-string)))
|
||||
"\n"))
|
||||
@@ -0,0 +1,40 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/doom-dashboard/test/test-doom-dashboard.el
|
||||
|
||||
(require 'core-projects)
|
||||
(require 'projectile)
|
||||
(require! :ui doom-dashboard)
|
||||
|
||||
(describe "ui/doom-dashboard"
|
||||
:var (default-directory projectile-enable-caching)
|
||||
(before-all
|
||||
(setq projectile-enable-caching nil
|
||||
doom-fallback-buffer-name +doom-dashboard-name))
|
||||
|
||||
(before-each (projectile-mode +1))
|
||||
(after-each (projectile-mode -1))
|
||||
|
||||
(describe "get-pwd"
|
||||
:var (+doom-dashboard--last-cwd)
|
||||
(before-each
|
||||
(setq +doom-dashboard--last-cwd doom-core-dir
|
||||
default-directory doom-core-dir))
|
||||
(it "returns the current directory when policy is nil"
|
||||
(let (+doom-dashboard-pwd-policy)
|
||||
(expect (+doom-dashboard--get-pwd) :to-equal default-directory)))
|
||||
(it "returns a path if policy is a path"
|
||||
(let ((+doom-dashboard-pwd-policy "~"))
|
||||
(expect (+doom-dashboard--get-pwd) :to-equal (expand-file-name "~"))))
|
||||
(it "returns return value of policy as a function"
|
||||
(let ((+doom-dashboard-pwd-policy (lambda (x) "x")))
|
||||
(expect (+doom-dashboard--get-pwd) :to-equal "x")))
|
||||
(it "returns last cwd if policy is 'last"
|
||||
(let ((+doom-dashboard-pwd-policy 'last))
|
||||
(expect (+doom-dashboard--get-pwd) :to-equal doom-core-dir)))
|
||||
(it "returns last project if policy is 'last-project"
|
||||
(let ((+doom-dashboard-pwd-policy 'last-project))
|
||||
(expect (+doom-dashboard--get-pwd) :to-equal doom-emacs-dir))))
|
||||
|
||||
(describe "dashboard-p"
|
||||
(it "changes the fallback buffer to the dashboard buffer"
|
||||
(expect (+doom-dashboard-p (doom-fallback-buffer))))))
|
||||
8
.emacs.d/modules/ui/doom-quit/README.org
Normal file
8
.emacs.d/modules/ui/doom-quit/README.org
Normal file
@@ -0,0 +1,8 @@
|
||||
#+TITLE: ui/doom-quit
|
||||
#+DATE: February 19, 2017
|
||||
#+SINCE: v2.0
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
A silly module that prompts you with messages when you try to quit, like DOOM
|
||||
did. Some quotes are from Doom's quit-message list. Others are random, nerdy
|
||||
references that no decent human being has any business recognizing.
|
||||
32
.emacs.d/modules/ui/doom-quit/config.el
Normal file
32
.emacs.d/modules/ui/doom-quit/config.el
Normal file
@@ -0,0 +1,32 @@
|
||||
;;; ui/doom-quit/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +doom-quit-messages
|
||||
'(;; from Doom 1
|
||||
"Please don't leave, there's more demons to toast!"
|
||||
"Let's beat it -- This is turning into a bloodbath!"
|
||||
"I wouldn't leave if I were you. DOS is much worse."
|
||||
"Don't leave yet -- There's a demon around that corner!"
|
||||
"Ya know, next time you come in here I'm gonna toast ya."
|
||||
"Go ahead and leave. See if I care."
|
||||
"Are you sure you want to quit this great editor?"
|
||||
;; Custom
|
||||
"(setq nothing t everything 'permitted)"
|
||||
"Emacs will remember that."
|
||||
"Emacs, Emacs never changes."
|
||||
"Hey! Hey, M-x listen!"
|
||||
"I'm the man who's going to burn your house down! With lemons!"
|
||||
"It's not like I'll miss you or anything, b-baka!"
|
||||
"Okay, look. We've both said a lot of things you're going to regret..."
|
||||
"Wake up, Mr. Stallman. Wake up and smell the ashes."
|
||||
"You are *not* prepared!")
|
||||
"A list of quit messages, picked randomly by `+doom-quit'. Taken from
|
||||
http://doom.wikia.com/wiki/Quit_messages and elsewhere.")
|
||||
|
||||
(defun +doom-quit-fn (&rest _)
|
||||
(doom-quit-p
|
||||
(format "%s Quit?"
|
||||
(nth (random (length +doom-quit-messages))
|
||||
+doom-quit-messages))))
|
||||
|
||||
;;
|
||||
(setq confirm-kill-emacs #'+doom-quit-fn)
|
||||
93
.emacs.d/modules/ui/doom/README.org
Normal file
93
.emacs.d/modules/ui/doom/README.org
Normal file
@@ -0,0 +1,93 @@
|
||||
#+TITLE: ui/doom
|
||||
#+DATE: October 9, 2019
|
||||
#+SINCE: v1.3
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#plugins][Plugins]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
- [[#configuration][Configuration]]
|
||||
- [[#changing-theme][Changing theme]]
|
||||
- [[#changing-fonts][Changing fonts]]
|
||||
- [[#troubleshooting][Troubleshooting]]
|
||||
- [[#strange-font-symbols][Strange font symbols]]
|
||||
- [[#ugly-background-colors-in-tty-emacs-for-daemon-users][Ugly background colors in tty Emacs for daemon users]]
|
||||
|
||||
* Description
|
||||
This module gives Doom its signature look: powered by the =doom-one= theme
|
||||
(inspired by Atom's One Dark theme) and =solaire-mode=.
|
||||
|
||||
+ A colorscheme inspired by Atom's One Dark theme (now available in a separate
|
||||
plugin: [[https://github.com/hlissner/emacs-doom-theme/][doom-themes]])
|
||||
+ A custom folded-region indicator for ~hideshow~
|
||||
+ "Thin bar" fringe bitmaps for ~git-gutter-fringe~
|
||||
+ File-visiting buffers are slightly brighter (thanks to solaire-mode)
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/hlissner/emacs-doom-themes][doom-themes]]
|
||||
+ [[https://github.com/hlissner/emacs-solaire-mode][solaire-mode]]
|
||||
|
||||
* Prerequisites
|
||||
This module has no prereqisites.
|
||||
|
||||
* Configuration
|
||||
** Changing theme
|
||||
Although this module uses the ~doom-one~ theme by default, [[https://github.com/hlissner/emacs-doom-theme/][doom-themes]] offers a number of alternatives:
|
||||
|
||||
+ *doom-one:* doom-themes' flagship theme, inspired by [[https://atom.io/][Atom's]] One Dark themes
|
||||
+ *doom-vibrant:* a more vibrant version of doom-one
|
||||
+ *doom-molokai:* based on Textmate's monokai
|
||||
+ *doom-nova:* adapted from [[https://trevordmiller.com/projects/nova][Nova]]
|
||||
+ *doom-one-light:* light version of doom-one
|
||||
+ *doom-peacock:* based on Peacock from [[https://daylerees.github.io/][daylerees' themes]]
|
||||
+ *doom-tomorrow-night:* by [[https://github.com/ChrisKempson/Tomorrow-Theme][Chris Kempson]]
|
||||
|
||||
This can be changed by changing the ~doom-theme~ variable, e.g.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(setq doom-theme 'doom-molokai)
|
||||
#+END_SRC
|
||||
|
||||
** Changing fonts
|
||||
core/core-ui.el has four relevant variables:
|
||||
|
||||
+ ~doom-font~ :: the default font to use in Doom Emacs.
|
||||
+ ~doom-big-font~ :: the font to use when ~doom-big-font-mode~ is enabled.
|
||||
+ ~doom-variable-font~ :: the font to use when ~variable-pitch-mode~ is active (or where the ~variable-pitch~ face is used).
|
||||
+ ~doom-unicode-font~ :: the font used to display unicode symbols. This is ignored if the =:ui unicode= module is enabled.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(setq doom-font (font-spec :family "Fira Mono" :size 12)
|
||||
doom-variable-pitch-font (font-spec :family "Fira Sans")
|
||||
doom-unicode-font (font-spec :family "DejaVu Sans Mono")
|
||||
doom-big-font (font-spec :family "Fira Mono" :size 19))
|
||||
#+END_SRC
|
||||
|
||||
* Troubleshooting
|
||||
** Strange font symbols
|
||||
If you're seeing strange unicode symbols, this is likely because you don't have
|
||||
~all-the-icons~'s font icon installed. You can install them with ~M-x
|
||||
all-the-icons-install-fonts~.
|
||||
|
||||
** Ugly background colors in tty Emacs for daemon users
|
||||
=solaire-mode= is an aesthetic plugin that makes file-visiting buffers brighter
|
||||
than the rest of the Emacs' frame (to visually differentiate temporary windows
|
||||
or sidebars from editing windows). This looks great in GUI Emacs, but can look
|
||||
questionable in the terminal.
|
||||
|
||||
It disables itself if you start tty Emacs with ~emacs -nw~, but if you create a
|
||||
tty frame from a daemon (which solaire-mode cannot anticipate), you'll get an
|
||||
ugly background instead.
|
||||
|
||||
If you only use Emacs in the terminal, your best bet is to disable the
|
||||
solaire-mode package:
|
||||
|
||||
#+BEGIN_SRC elisp
|
||||
;; in ~/.doom.d/packages.el
|
||||
(package! solaire-mode :disable t)
|
||||
#+END_SRC
|
||||
96
.emacs.d/modules/ui/doom/config.el
Normal file
96
.emacs.d/modules/ui/doom/config.el
Normal file
@@ -0,0 +1,96 @@
|
||||
;;; ui/doom/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +doom-solaire-themes
|
||||
'((doom-challenger-deep . t)
|
||||
(doom-city-lights . t)
|
||||
(doom-dracula . t)
|
||||
(doom-molokai)
|
||||
(doom-nord . t)
|
||||
(doom-nord-light . t)
|
||||
(doom-nova)
|
||||
(doom-one . t)
|
||||
(doom-one-light . t)
|
||||
(doom-opera . t)
|
||||
(doom-snazzy . t)
|
||||
(doom-solarized-dark . t)
|
||||
(doom-solarized-light)
|
||||
(doom-spacegrey . t)
|
||||
(doom-tomorrow-day . t)
|
||||
(doom-tomorrow-night . t)
|
||||
(doom-vibrant))
|
||||
"An alist of themes that support `solaire-mode'. If CDR is t, then
|
||||
`solaire-mode-swap-bg' will be used automatically, when the theme is loaded.")
|
||||
|
||||
|
||||
;;
|
||||
;;; Packages
|
||||
|
||||
(use-package! doom-themes
|
||||
:defer t
|
||||
:init
|
||||
(unless doom-theme
|
||||
(setq doom-theme 'doom-one))
|
||||
:config
|
||||
;; improve integration w/ org-mode
|
||||
(add-hook 'doom-load-theme-hook #'doom-themes-org-config)
|
||||
;; more Atom-esque file icons for neotree/treemacs
|
||||
(when (featurep! :ui neotree)
|
||||
(add-hook 'doom-load-theme-hook #'doom-themes-neotree-config)
|
||||
(setq doom-themes-neotree-enable-variable-pitch t
|
||||
doom-themes-neotree-file-icons 'simple
|
||||
doom-themes-neotree-line-spacing 2))
|
||||
(when (featurep! :ui treemacs)
|
||||
(add-hook 'doom-load-theme-hook #'doom-themes-treemacs-config)))
|
||||
|
||||
|
||||
(use-package! solaire-mode
|
||||
:when (or (daemonp) (display-graphic-p))
|
||||
:defer t
|
||||
:init
|
||||
(add-hook! 'doom-load-theme-hook :append
|
||||
(defun +doom-solaire-mode-swap-bg-maybe-h ()
|
||||
(pcase-let ((`(,_theme . ,swap) (assq doom-theme +doom-solaire-themes)))
|
||||
(require 'solaire-mode)
|
||||
(if swap (solaire-mode-swap-bg)))))
|
||||
:config
|
||||
;; fringe can become unstyled when deleting or focusing frames
|
||||
(add-hook 'focus-in-hook #'solaire-mode-reset)
|
||||
;; Prevent color glitches when reloading either DOOM or loading a new theme
|
||||
(add-hook! '(doom-load-theme-hook doom-reload-hook) :append
|
||||
#'solaire-mode-reset)
|
||||
;; org-capture takes an org buffer and narrows it. The result is erroneously
|
||||
;; considered an unreal buffer, so solaire-mode must be restored.
|
||||
(add-hook 'org-capture-mode-hook #'turn-on-solaire-mode)
|
||||
|
||||
;; On Emacs 26+, when point is on the last line and solaire-mode is remapping
|
||||
;; the hl-line face, hl-line's highlight bleeds into the rest of the window
|
||||
;; after eob. On Emacs 27 this no longer happens.
|
||||
(unless EMACS27+
|
||||
(defun +doom--line-range-fn ()
|
||||
(cons (line-beginning-position)
|
||||
(cond ((let ((eol (line-end-position)))
|
||||
(and (= eol (point-max))
|
||||
(/= eol (line-beginning-position))))
|
||||
(1- (line-end-position)))
|
||||
((or (eobp)
|
||||
(= (line-end-position 2) (point-max)))
|
||||
(line-end-position))
|
||||
((line-beginning-position 2)))))
|
||||
(setq hl-line-range-function #'+doom--line-range-fn))
|
||||
|
||||
;; Because fringes can't be given a buffer-local face, they can look odd, so
|
||||
;; we remove them in the minibuffer and which-key popups (they serve no
|
||||
;; purpose there anyway).
|
||||
(add-hook! 'solaire-mode-hook
|
||||
(defun +doom-disable-fringes-in-minibuffer-h (&rest _)
|
||||
(set-window-fringes (minibuffer-window) 0 0 nil)))
|
||||
|
||||
(defadvice! +doom--no-fringes-in-which-key-buffer-a (&rest _)
|
||||
:after 'which-key--show-buffer-side-window
|
||||
(+doom-disable-fringes-in-minibuffer-h)
|
||||
(set-window-fringes (get-buffer-window which-key--buffer) 0 0 nil))
|
||||
|
||||
(add-hook! '(minibuffer-setup-hook window-configuration-change-hook)
|
||||
#'+doom-disable-fringes-in-minibuffer-h)
|
||||
|
||||
(solaire-global-mode +1))
|
||||
6
.emacs.d/modules/ui/doom/packages.el
Normal file
6
.emacs.d/modules/ui/doom/packages.el
Normal file
@@ -0,0 +1,6 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/doom/packages.el
|
||||
|
||||
(package! doom-themes)
|
||||
(package! solaire-mode)
|
||||
|
||||
7
.emacs.d/modules/ui/fill-column/autoload.el
Normal file
7
.emacs.d/modules/ui/fill-column/autoload.el
Normal file
@@ -0,0 +1,7 @@
|
||||
;;; ui/fill-column/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload (autoload 'hl-fill-column-mode "hl-fill-column" nil t)
|
||||
|
||||
;;;###autoload
|
||||
(add-hook! '(text-mode-hook prog-mode-hook conf-mode-hook)
|
||||
#'hl-fill-column-mode)
|
||||
4
.emacs.d/modules/ui/fill-column/packages.el
Normal file
4
.emacs.d/modules/ui/fill-column/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/fill-column/packages.el
|
||||
|
||||
(package! hl-fill-column)
|
||||
13
.emacs.d/modules/ui/hl-todo/README.org
Normal file
13
.emacs.d/modules/ui/hl-todo/README.org
Normal file
@@ -0,0 +1,13 @@
|
||||
#+TITLE: :ui hl-todo
|
||||
|
||||
This module adds syntax highlighting for TODO/FIXME/NOTE tags in programming major-modes.
|
||||
|
||||
What keywords are highlighted (and their color) can be customized through ~hl-todo-keyword-faces~.
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;; the default
|
||||
(setq hl-todo-keyword-faces
|
||||
`(("TODO" . ,(face-foreground 'warning))
|
||||
("FIXME" . ,(face-foreground 'error))
|
||||
("NOTE" . ,(face-foreground 'success))))
|
||||
#+END_SRC
|
||||
28
.emacs.d/modules/ui/hl-todo/config.el
Normal file
28
.emacs.d/modules/ui/hl-todo/config.el
Normal file
@@ -0,0 +1,28 @@
|
||||
;;; ui/hl-todo/packages.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! hl-todo
|
||||
:hook (prog-mode . hl-todo-mode)
|
||||
:config
|
||||
(setq hl-todo-highlight-punctuation ":"
|
||||
hl-todo-keyword-faces
|
||||
`(("TODO" warning bold)
|
||||
("FIXME" error bold)
|
||||
("HACK" font-lock-constant-face bold)
|
||||
("REVIEW" font-lock-keyword-face bold)
|
||||
("NOTE" success bold)
|
||||
("DEPRECATED" font-lock-doc-face bold)))
|
||||
|
||||
;; Use a more primitive todo-keyword detection method in major modes that
|
||||
;; don't use/have a valid syntax table entry for comments.
|
||||
(add-hook! '(pug-mode-hook haml-mode-hook)
|
||||
(defun +hl-todo--use-face-detection-h ()
|
||||
"Use a different, more primitive method of locating todo keywords."
|
||||
(set (make-local-variable 'hl-todo-keywords)
|
||||
'(((lambda (limit)
|
||||
(let (case-fold-search)
|
||||
(and (re-search-forward hl-todo-regexp limit t)
|
||||
(memq 'font-lock-comment-face (doom-enlist (get-text-property (point) 'face))))))
|
||||
(1 (hl-todo-get-face) t t))))
|
||||
(when hl-todo-mode
|
||||
(hl-todo-mode -1)
|
||||
(hl-todo-mode +1)))))
|
||||
4
.emacs.d/modules/ui/hl-todo/packages.el
Normal file
4
.emacs.d/modules/ui/hl-todo/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/hl-todo/packages.el
|
||||
|
||||
(package! hl-todo)
|
||||
31
.emacs.d/modules/ui/hydra/README.org
Normal file
31
.emacs.d/modules/ui/hydra/README.org
Normal file
@@ -0,0 +1,31 @@
|
||||
#+TITLE: ui/hydra
|
||||
#+DATE: october 29, 2019
|
||||
#+SINCE: 2.0
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#plugins][Plugins]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
- [[#configuration][Configuration]]
|
||||
|
||||
* Description
|
||||
This module adds hydra to Doom Emacs, as well as a few custom built hydras to
|
||||
start with :
|
||||
|
||||
+ A hydra to control windows ~+hydra/window-nav/body~
|
||||
+ A hydra to control text zoom level ~+hydra/text-zoom/body~
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/abo-abo/hydra][hydra]]
|
||||
|
||||
* Prerequisites
|
||||
This module has no prereqisites.
|
||||
|
||||
* Configuration
|
||||
Configuring this module is only setting bindings to the provided hydra, or
|
||||
creating your own ones !
|
||||
47
.emacs.d/modules/ui/hydra/autoload/windows.el
Normal file
47
.emacs.d/modules/ui/hydra/autoload/windows.el
Normal file
@@ -0,0 +1,47 @@
|
||||
;;; ui/hydra/autoload/windows.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload (autoload '+hydra/text-zoom/body "ui/hydra/autoload/windows" nil t)
|
||||
(defhydra +hydra/text-zoom (:hint t :color red)
|
||||
"
|
||||
Text zoom: _j_:zoom in, _k_:zoom out, _0_:reset
|
||||
"
|
||||
("j" doom/increase-font-size "in")
|
||||
("k" doom/decrease-font-size "out")
|
||||
("0" doom/reset-font-size "reset"))
|
||||
|
||||
;;;###autoload (autoload '+hydra/window-nav/body "ui/hydra/autoload/windows" nil t)
|
||||
(defhydra +hydra/window-nav (:hint nil)
|
||||
"
|
||||
Split: _v_ert _s_:horz
|
||||
Delete: _c_lose _o_nly
|
||||
Switch Window: _h_:left _j_:down _k_:up _l_:right
|
||||
Buffers: _p_revious _n_ext _b_:select _f_ind-file
|
||||
Resize: _H_:splitter left _J_:splitter down _K_:splitter up _L_:splitter right
|
||||
Move: _a_:up _z_:down _i_menu
|
||||
"
|
||||
("z" scroll-up-line)
|
||||
("a" scroll-down-line)
|
||||
("i" idomenu)
|
||||
|
||||
("h" windmove-left)
|
||||
("j" windmove-down)
|
||||
("k" windmove-up)
|
||||
("l" windmove-right)
|
||||
|
||||
("p" previous-buffer)
|
||||
("n" next-buffer)
|
||||
("b" switch-to-buffer)
|
||||
("f" find-file)
|
||||
|
||||
("s" split-window-below)
|
||||
("v" split-window-right)
|
||||
|
||||
("c" delete-window)
|
||||
("o" delete-other-windows)
|
||||
|
||||
("H" hydra-move-splitter-left)
|
||||
("J" hydra-move-splitter-down)
|
||||
("K" hydra-move-splitter-up)
|
||||
("L" hydra-move-splitter-right)
|
||||
|
||||
("q" nil))
|
||||
15
.emacs.d/modules/ui/hydra/config.el
Normal file
15
.emacs.d/modules/ui/hydra/config.el
Normal file
@@ -0,0 +1,15 @@
|
||||
;;; ui/hydra/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! hydra-examples
|
||||
:commands (hydra-move-splitter-up
|
||||
hydra-move-splitter-down
|
||||
hydra-move-splitter-right
|
||||
hydra-move-splitter-left))
|
||||
|
||||
;;;###package hydra
|
||||
(setq lv-use-separator t)
|
||||
|
||||
(defadvice! +hydra--inhibit-window-switch-hooks-a (orig-fn)
|
||||
:around #'lv-window
|
||||
(let ((doom-inhibit-switch-window-hooks t))
|
||||
(funcall orig-fn)))
|
||||
4
.emacs.d/modules/ui/hydra/packages.el
Normal file
4
.emacs.d/modules/ui/hydra/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/hydra/packages.el
|
||||
|
||||
(package! hydra)
|
||||
13
.emacs.d/modules/ui/indent-guides/config.el
Normal file
13
.emacs.d/modules/ui/indent-guides/config.el
Normal file
@@ -0,0 +1,13 @@
|
||||
;;; ui/indent-guides/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! highlight-indent-guides
|
||||
:hook ((prog-mode text-mode conf-mode) . highlight-indent-guides-mode)
|
||||
:init
|
||||
(setq highlight-indent-guides-method 'character)
|
||||
:config
|
||||
(add-hook 'focus-in-hook #'highlight-indent-guides-auto-set-faces)
|
||||
;; `highlight-indent-guides' breaks in these modes
|
||||
(add-hook! '(visual-line-mode-hook org-indent-mode-hook)
|
||||
(defun +indent-guides-disable-maybe-h ()
|
||||
(when highlight-indent-guides-mode
|
||||
(highlight-indent-guides-mode -1)))))
|
||||
4
.emacs.d/modules/ui/indent-guides/packages.el
Normal file
4
.emacs.d/modules/ui/indent-guides/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/indent-guides/packages.el
|
||||
|
||||
(package! highlight-indent-guides)
|
||||
558
.emacs.d/modules/ui/modeline/+light.el
Normal file
558
.emacs.d/modules/ui/modeline/+light.el
Normal file
@@ -0,0 +1,558 @@
|
||||
;;; ui/modeline/+default.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; This is a slimmed down version of `doom-modeline' that manipulates
|
||||
;; `mode-line-format' directly. Its purpose is to be truer to the original goal
|
||||
;; of Doom's modeline: to be more performant and minimalistic alternative to
|
||||
;; other modeline packages and to be abstraction-light. Too much abstraction is
|
||||
;; too much magic.
|
||||
;;
|
||||
;; Warning: this is still a WIP!
|
||||
|
||||
(defun +modeline--set-var-and-refresh-bars-fn (&optional symbol value)
|
||||
(when symbol
|
||||
(set-default symbol value))
|
||||
(when doom-init-time
|
||||
(+modeline-refresh-bars-h)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Variables
|
||||
|
||||
(defcustom +modeline-height 31
|
||||
"The height of the modeline.
|
||||
|
||||
This is enforced by the xpm bitmap bar in `+modeline-bar'. Without it (and in
|
||||
the terminal), this variable does nothing.
|
||||
|
||||
Use `setq!' to adjust this variable live, as it will trigger an refresh of the
|
||||
bars in the modeline. `setq' will not."
|
||||
:type 'integer
|
||||
:set #'+modeline--set-var-and-refresh-bars-fn)
|
||||
|
||||
(defcustom +modeline-bar-width 3
|
||||
"The width of the bar in the modeline.
|
||||
|
||||
If nil, the bar will be made transparent and 1 pixel wide, as to be invisible,
|
||||
but without sacrificing its ability to enforce `+modeline-height'.
|
||||
|
||||
Use `setq!' to adjust this variable live, as it will trigger an refresh of the
|
||||
bars in the modeline. `setq' will not."
|
||||
:type 'integer
|
||||
:set #'+modeline--set-var-and-refresh-bars-fn)
|
||||
|
||||
(defvar +modeline-format-alist ()
|
||||
"An alist of modeline formats defined with `def-modeline!'.
|
||||
|
||||
Each entry's CAR is the name and CDR is a cons cell whose CAR is the left-hand
|
||||
side of the modeline, and whose CDR is the right-hand side.")
|
||||
|
||||
|
||||
;;
|
||||
;;; Faces
|
||||
|
||||
(defface +modeline-bar '((t (:inherit highlight)))
|
||||
"Face used for left-most bar on the mode-line of an active window.")
|
||||
|
||||
(defface +modeline-bar-inactive '((t (:inherit mode-line-inactive)))
|
||||
"Face used for left-most bar on the mode-line of an inactive window.")
|
||||
|
||||
(defface +modeline-highlight
|
||||
'((t (:inherit mode-line-highlight)))
|
||||
"Face used for highlighted modeline panels (like search counts).")
|
||||
|
||||
(defface +modeline-alternate-highlight
|
||||
'((t (:inherit mode-line-highlight)))
|
||||
"Alternative face used for highlighted modeline panels (like search counts).")
|
||||
|
||||
|
||||
;;
|
||||
;;; Helpers
|
||||
|
||||
(defvar +modeline--redisplayed-p nil)
|
||||
(defadvice! modeline-recalculate-height-a (&optional _force &rest _ignored)
|
||||
"Ensure that window resizing functions take modeline height into account."
|
||||
:before '(fit-window-to-buffer resize-temp-buffer-window)
|
||||
(unless +modeline--redisplayed-p
|
||||
(setq-local +modeline--redisplayed-p t)
|
||||
(redisplay t)))
|
||||
|
||||
;;; `active'
|
||||
(defvar +modeline--active-window (selected-window))
|
||||
|
||||
(defun +modeline-active ()
|
||||
"Return non-nil if the selected window has an active modeline."
|
||||
(eq (selected-window) +modeline--active-window))
|
||||
|
||||
(add-hook! 'pre-redisplay-functions
|
||||
(defun +modeline-set-selected-window-h (&rest _)
|
||||
"Track the active modeline's window in `+modeline--active-window'."
|
||||
(let ((win (selected-window)))
|
||||
(unless (minibuffer-window-active-p win)
|
||||
(setq +modeline--active-window (frame-selected-window))))))
|
||||
|
||||
(defun +modeline--make-xpm (color width height)
|
||||
"Create an XPM bitmap via COLOR, WIDTH and HEIGHT. Inspired by `powerline''s `pl/+modeline--make-xpm'."
|
||||
(propertize
|
||||
" " 'display
|
||||
(let ((data (make-list height (make-list width 1)))
|
||||
(color (or color "None")))
|
||||
(ignore-errors
|
||||
(create-image
|
||||
(concat
|
||||
(format "/* XPM */\nstatic char * percent[] = {\n\"%i %i 2 1\",\n\". c %s\",\n\" c %s\","
|
||||
(length (car data))
|
||||
(length data)
|
||||
color
|
||||
color)
|
||||
(apply #'concat
|
||||
(cl-loop with idx = 0
|
||||
with len = (length data)
|
||||
for dl in data
|
||||
do (cl-incf idx)
|
||||
collect
|
||||
(concat "\""
|
||||
(cl-loop for d in dl
|
||||
if (= d 0) collect (string-to-char " ")
|
||||
else collect (string-to-char "."))
|
||||
(if (eq idx len) "\"};" "\",\n")))))
|
||||
'xpm t :ascent 'center)))))
|
||||
|
||||
(defun +modeline-format-icon (icon label &optional face help-echo voffset)
|
||||
(propertize (concat (all-the-icons-material
|
||||
icon
|
||||
:face face
|
||||
:height 1.1
|
||||
:v-adjust (or voffset -0.225))
|
||||
(propertize label 'face face))
|
||||
'help-echo help-echo))
|
||||
|
||||
(defun set-modeline! (name &optional default)
|
||||
"Set the modeline to NAME.
|
||||
If DEFAULT is non-nil, apply to all future buffers. Modelines are defined with
|
||||
`def-modeline!'."
|
||||
(if-let (format (assq name +modeline-format-alist))
|
||||
(cl-destructuring-bind (lhs . rhs) (cdr format)
|
||||
(if default
|
||||
(setq-default +modeline-format-left lhs
|
||||
+modeline-format-right rhs)
|
||||
(setq +modeline-format-left lhs
|
||||
+modeline-format-right rhs)))
|
||||
(error "Could not find %S modeline format" name)))
|
||||
|
||||
(defun set-modeline-hook! (hooks name)
|
||||
"Set the modeline to NAME on HOOKS.
|
||||
See `def-modeline!' on how modelines are defined."
|
||||
(let ((fn (intern (format "+modeline-set-%s-format-h" name))))
|
||||
(dolist (hook (doom-enlist hooks))
|
||||
(add-hook hook fn))))
|
||||
|
||||
(defmacro def-modeline! (name lhs rhs)
|
||||
"Define a modeline format by NAME.
|
||||
LHS and RHS are the formats representing the left and right hand side of the
|
||||
mode-line, respectively. See the variable `format-mode-line' for details on what
|
||||
LHS and RHS will accept."
|
||||
`(progn
|
||||
(setf (alist-get ',name +modeline-format-alist)
|
||||
(cons ,lhs ,rhs))
|
||||
(defun ,(intern (format "+modeline-set-%s-format-h" name)) (&rest _)
|
||||
"TODO"
|
||||
(set-modeline! ',name))))
|
||||
|
||||
(defmacro def-modeline-var! (name body &optional docstring &rest plist)
|
||||
"TODO"
|
||||
(unless (stringp docstring)
|
||||
(push docstring plist)
|
||||
(setq docstring nil))
|
||||
`(progn
|
||||
(,(if (plist-get plist :local) 'defvar-local 'defvar)
|
||||
,name ,body ,docstring)
|
||||
(put ',name 'risky-local-variable t)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Segments
|
||||
|
||||
(def-modeline-var! +modeline-format-left nil
|
||||
"The left-hand side of the modeline."
|
||||
:local t)
|
||||
|
||||
(def-modeline-var! +modeline-format-right nil
|
||||
"The right-hand side of the modeline."
|
||||
:local t)
|
||||
|
||||
|
||||
;;; `+modeline-bar'
|
||||
(progn
|
||||
(def-modeline-var! +modeline-bar "")
|
||||
(def-modeline-var! +modeline-inactive-bar "")
|
||||
|
||||
(add-hook! '(doom-init-ui-hook doom-load-theme-hook) :append
|
||||
(defun +modeline-refresh-bars-h ()
|
||||
(let ((width (or +modeline-bar-width 1))
|
||||
(height (max +modeline-height 0)))
|
||||
(setq +modeline-bar
|
||||
(+modeline--make-xpm
|
||||
(and +modeline-bar-width
|
||||
(face-background '+modeline-bar nil 'inherit))
|
||||
width height)
|
||||
+modeline-inactive-bar
|
||||
(+modeline--make-xpm
|
||||
(and +modeline-bar-width
|
||||
(face-background '+modeline-bar-inactive nil 'inherit))
|
||||
width height)))))
|
||||
|
||||
(add-hook! 'doom-change-font-size-hook
|
||||
(defun +modeline-adjust-height-h ()
|
||||
(defvar +modeline--old-height +modeline-height)
|
||||
(let ((default-height +modeline--old-height)
|
||||
(scale (or (frame-parameter nil 'font-scale) 0)))
|
||||
(setq +modeline-height
|
||||
(if (> scale 0)
|
||||
(+ default-height (* (or (frame-parameter nil 'font-scale) 1)
|
||||
doom-font-increment))
|
||||
default-height))
|
||||
(when doom-init-time
|
||||
(+modeline-refresh-bars-h))))))
|
||||
|
||||
|
||||
;;; `+modeline-matches'
|
||||
(progn
|
||||
(use-package! anzu
|
||||
:after-call isearch-mode
|
||||
:config
|
||||
;; anzu and evil-anzu expose current/total state that can be displayed in the
|
||||
;; mode-line.
|
||||
(defadvice! +modeline-fix-anzu-count-a (positions here)
|
||||
"Calulate anzu counts via POSITIONS and HERE."
|
||||
:override #'anzu--where-is-here
|
||||
(cl-loop for (start . end) in positions
|
||||
collect t into before
|
||||
when (and (>= here start) (<= here end))
|
||||
return (length before)
|
||||
finally return 0))
|
||||
|
||||
(setq anzu-cons-mode-line-p nil) ; manage modeline segment ourselves
|
||||
;; Ensure anzu state is cleared when searches & iedit are done
|
||||
(add-hook 'isearch-mode-end-hook #'anzu--reset-status 'append)
|
||||
(add-hook 'iedit-mode-end-hook #'anzu--reset-status)
|
||||
(advice-add #'evil-force-normal-state :before #'anzu--reset-status)
|
||||
;; Fix matches segment mirroring across all buffers
|
||||
(mapc #'make-variable-buffer-local
|
||||
'(anzu--total-matched anzu--current-position anzu--state
|
||||
anzu--cached-count anzu--cached-positions anzu--last-command
|
||||
anzu--last-isearch-string anzu--overflow-p)))
|
||||
|
||||
(use-package! evil-anzu
|
||||
:when (featurep! :editor evil)
|
||||
:after-call (evil-ex-start-search evil-ex-start-word-search evil-ex-search-activate-highlight))
|
||||
|
||||
(defun +modeline--anzu ()
|
||||
"Show the match index and total number thereof.
|
||||
Requires `anzu', also `evil-anzu' if using `evil-mode' for compatibility with
|
||||
`evil-search'."
|
||||
(when (and (bound-and-true-p anzu--state)
|
||||
(not (bound-and-true-p iedit-mode)))
|
||||
(propertize
|
||||
(let ((here anzu--current-position)
|
||||
(total anzu--total-matched))
|
||||
(cond ((eq anzu--state 'replace-query)
|
||||
(format " %d replace " anzu--cached-count))
|
||||
((eq anzu--state 'replace)
|
||||
(format " %d/%d " here total))
|
||||
(anzu--overflow-p
|
||||
(format " %s+ " total))
|
||||
(t
|
||||
(format " %s/%d " here total))))
|
||||
'face (if (+modeline-active) '+modeline-highlight))))
|
||||
|
||||
(defun +modeline--evil-substitute ()
|
||||
"Show number of matches for evil-ex substitutions and highlights in real time."
|
||||
(when (and (bound-and-true-p evil-local-mode)
|
||||
(or (assq 'evil-ex-substitute evil-ex-active-highlights-alist)
|
||||
(assq 'evil-ex-global-match evil-ex-active-highlights-alist)
|
||||
(assq 'evil-ex-buffer-match evil-ex-active-highlights-alist)))
|
||||
(propertize
|
||||
(let ((range (if evil-ex-range
|
||||
(cons (car evil-ex-range) (cadr evil-ex-range))
|
||||
(cons (line-beginning-position) (line-end-position))))
|
||||
(pattern (car-safe (evil-delimited-arguments evil-ex-argument 2))))
|
||||
(if pattern
|
||||
(format " %s matches " (how-many pattern (car range) (cdr range)))
|
||||
" - "))
|
||||
'face (if (+modeline-active) '+modeline-highlight))))
|
||||
|
||||
(defun +modeline--multiple-cursors ()
|
||||
"Show the number of multiple cursors."
|
||||
(when (bound-and-true-p evil-mc-cursor-list)
|
||||
(let ((count (length evil-mc-cursor-list)))
|
||||
(when (> count 0)
|
||||
(let ((face (cond ((not (+modeline-active)) 'mode-line-inactive)
|
||||
(evil-mc-frozen '+modeline-highlight)
|
||||
('+modeline-alternate-highlight))))
|
||||
(concat (propertize " " 'face face)
|
||||
(all-the-icons-faicon "i-cursor" :face face :v-adjust -0.0575)
|
||||
(propertize " " 'face `(:inherit (variable-pitch ,face)))
|
||||
(propertize (format "%d " count)
|
||||
'face face)))))))
|
||||
|
||||
(defun +modeline--overlay< (a b)
|
||||
"Sort overlay A and B."
|
||||
(< (overlay-start a) (overlay-start b)))
|
||||
|
||||
(defun +modeline--iedit ()
|
||||
"Show the number of iedit regions matches + what match you're on."
|
||||
(when (and (bound-and-true-p iedit-mode)
|
||||
(bound-and-true-p iedit-occurrences-overlays))
|
||||
(propertize
|
||||
(let ((this-oc (or (let ((inhibit-message t))
|
||||
(iedit-find-current-occurrence-overlay))
|
||||
(save-excursion
|
||||
(iedit-prev-occurrence)
|
||||
(iedit-find-current-occurrence-overlay))))
|
||||
(length (length iedit-occurrences-overlays)))
|
||||
(format " %s/%d "
|
||||
(if this-oc
|
||||
(- length
|
||||
(length (memq this-oc (sort (append iedit-occurrences-overlays nil)
|
||||
#'+modeline--overlay<)))
|
||||
-1)
|
||||
"-")
|
||||
length))
|
||||
'face (if (+modeline-active) '+modeline-highlight))))
|
||||
|
||||
(defun +modeline--macro-recording ()
|
||||
"Display current Emacs or evil macro being recorded."
|
||||
(when (and (+modeline-active)
|
||||
(or defining-kbd-macro
|
||||
executing-kbd-macro))
|
||||
(let ((sep (propertize " " 'face '+modeline-highlight)))
|
||||
(concat sep
|
||||
(propertize (if (bound-and-true-p evil-this-macro)
|
||||
(char-to-string evil-this-macro)
|
||||
"Macro")
|
||||
'face '+modeline-highlight)
|
||||
sep
|
||||
(all-the-icons-octicon "triangle-right"
|
||||
:face '+modeline-highlight
|
||||
:v-adjust -0.05)
|
||||
sep))))
|
||||
|
||||
(def-modeline-var! +modeline-matches
|
||||
'(:eval
|
||||
(let ((meta (concat (+modeline--macro-recording)
|
||||
(+modeline--anzu)
|
||||
(+modeline--evil-substitute)
|
||||
(+modeline--iedit)
|
||||
(+modeline--multiple-cursors))))
|
||||
(or (and (not (equal meta "")) meta)
|
||||
" %I ")))))
|
||||
|
||||
|
||||
;;; `+modeline-modes'
|
||||
(def-modeline-var! +modeline-modes ; remove minor modes
|
||||
'(""
|
||||
(:propertize mode-name
|
||||
face bold
|
||||
mouse-face +modeline-highlight)
|
||||
mode-line-process
|
||||
"%n"
|
||||
" "))
|
||||
|
||||
|
||||
;;; `+modeline-buffer-identification'
|
||||
(def-modeline-var! +modeline-buffer-identification ; slightly more informative buffer id
|
||||
'((:eval
|
||||
(propertize
|
||||
(let ((buffer-file-name (buffer-file-name (buffer-base-buffer))))
|
||||
(or (when buffer-file-name
|
||||
(if-let (project (doom-project-root buffer-file-name))
|
||||
(let ((filename (or buffer-file-truename (file-truename buffer-file-name))))
|
||||
(file-relative-name filename (concat project "..")))))
|
||||
"%b"))
|
||||
'face (cond ((buffer-modified-p)
|
||||
'(error bold mode-line-buffer-id))
|
||||
((+modeline-active)
|
||||
'mode-line-buffer-id))
|
||||
'help-echo buffer-file-name))
|
||||
(buffer-read-only (:propertize " RO" face warning))))
|
||||
|
||||
|
||||
;;; `+modeline-position'
|
||||
(def-modeline-var! +modeline-position '(" %l:%C %p "))
|
||||
|
||||
|
||||
;;; `+modeline-checker'
|
||||
(progn
|
||||
(def-modeline-var! +modeline-checker nil
|
||||
"Displays color-coded error status & icon for the current buffer."
|
||||
:local t)
|
||||
|
||||
(add-hook! '(flycheck-status-changed-functions
|
||||
flycheck-mode-hook)
|
||||
(defun +modeline-checker-update (&optional status)
|
||||
"Update flycheck text via STATUS."
|
||||
(setq +modeline-checker
|
||||
(pcase status
|
||||
(`finished
|
||||
(if flycheck-current-errors
|
||||
(let-alist (flycheck-count-errors flycheck-current-errors)
|
||||
(let ((error (or .error 0))
|
||||
(warning (or .warning 0))
|
||||
(info (or .info 0)))
|
||||
(+modeline-format-icon "do_not_disturb_alt"
|
||||
(number-to-string (+ error warning info))
|
||||
(cond ((> error 0) 'error)
|
||||
((> warning 0) 'warning)
|
||||
('success))
|
||||
(format "Errors: %d, Warnings: %d, Debug: %d"
|
||||
error
|
||||
warning
|
||||
info))))
|
||||
(+modeline-format-icon "check" "" 'success)))
|
||||
(`running (+modeline-format-icon "access_time" "*" 'font-lock-comment-face "Running..."))
|
||||
(`errored (+modeline-format-icon "sim_card_alert" "!" 'error "Errored!"))
|
||||
(`interrupted (+modeline-format-icon "pause" "!" 'font-lock-comment-face "Interrupted"))
|
||||
(`suspicious (+modeline-format-icon "priority_high" "!" 'error "Suspicious")))))))
|
||||
|
||||
|
||||
;;; `+modeline-selection-info'
|
||||
(progn
|
||||
(defsubst +modeline--column (pos)
|
||||
"Get the column of the position `POS'."
|
||||
(save-excursion (goto-char pos)
|
||||
(current-column)))
|
||||
|
||||
(def-modeline-var! +modeline-selection-info
|
||||
'(:eval
|
||||
(when (or mark-active
|
||||
(and (bound-and-true-p evil-local-mode)
|
||||
(eq evil-state 'visual)))
|
||||
(cl-destructuring-bind (beg . end)
|
||||
(if (boundp 'evil-local-mode)
|
||||
(cons evil-visual-beginning evil-visual-end)
|
||||
(cons (region-beginning) (region-end)))
|
||||
(propertize
|
||||
(let ((lines (count-lines beg (min end (point-max)))))
|
||||
(concat " "
|
||||
(cond ((or (bound-and-true-p rectangle-mark-mode)
|
||||
(and (bound-and-true-p evil-visual-selection)
|
||||
(eq 'block evil-visual-selection)))
|
||||
(let ((cols (abs (- (+modeline--column end)
|
||||
(+modeline--column beg)))))
|
||||
(format "%dx%dB" lines cols)))
|
||||
((and (bound-and-true-p evil-visual-selection)
|
||||
(eq evil-visual-selection 'line))
|
||||
(format "%dL" lines))
|
||||
((> lines 1)
|
||||
(format "%dC %dL" (- end beg) lines))
|
||||
((format "%dC" (- end beg))))
|
||||
(when (derived-mode-p 'text-mode)
|
||||
(format " %dW" (count-words beg end)))
|
||||
" "))
|
||||
'face (if (+modeline-active) 'success)))))
|
||||
"Information about the current selection, such as how many characters and
|
||||
lines are selected, or the NxM dimensions of a block selection.")
|
||||
|
||||
(defun +modeline-add-selection-segment-h ()
|
||||
(add-to-list '+modeline-format-left '+modeline-selection-info 'append))
|
||||
(defun +modeline-remove-selection-segment-h ()
|
||||
(delq! '+modeline-selection-info +modeline-format-left))
|
||||
|
||||
(if (featurep 'evil)
|
||||
(progn
|
||||
(add-hook 'evil-visual-state-entry-hook #'+modeline-add-selection-segment-h)
|
||||
(add-hook 'evil-visual-state-exit-hook #'+modeline-remove-selection-segment-h))
|
||||
(add-hook 'activate-mark-hook #'+modeline-add-selection-segment-h)
|
||||
(add-hook 'deactivate-mark-hook #'+modeline-remove-selection-segment-h)))
|
||||
|
||||
|
||||
;;; `+modeline-encoding'
|
||||
(def-modeline-var! +modeline-encoding
|
||||
'(:eval
|
||||
(concat (pcase (coding-system-eol-type buffer-file-coding-system)
|
||||
(0 " LF ")
|
||||
(1 " RLF ")
|
||||
(2 " CR "))
|
||||
(let ((sys (coding-system-plist buffer-file-coding-system)))
|
||||
(if (memq (plist-get sys :category)
|
||||
'(coding-category-undecided coding-category-utf-8))
|
||||
"UTF-8"
|
||||
(upcase (symbol-name (plist-get sys :name)))))
|
||||
" ")))
|
||||
|
||||
|
||||
;;
|
||||
;;; Default modeline
|
||||
|
||||
(def-modeline! :main
|
||||
'(""
|
||||
+modeline-matches
|
||||
" "
|
||||
+modeline-buffer-identification
|
||||
+modeline-position)
|
||||
'(""
|
||||
mode-line-misc-info
|
||||
+modeline-modes
|
||||
(vc-mode (" "
|
||||
,(all-the-icons-octicon "git-branch" :v-adjust 0.0)
|
||||
vc-mode " "))
|
||||
" "
|
||||
+modeline-encoding
|
||||
(+modeline-checker ("" +modeline-checker " "))))
|
||||
|
||||
(def-modeline! project
|
||||
`(" "
|
||||
,(all-the-icons-octicon
|
||||
"file-directory"
|
||||
:face 'bold
|
||||
:v-adjust -0.05
|
||||
:height 1.25)
|
||||
(:propertize (" " (:eval (abbreviate-file-name default-directory)))
|
||||
face bold))
|
||||
'("" +modeline-modes))
|
||||
|
||||
(def-modeline! special
|
||||
'("" +modeline-matches
|
||||
" " +modeline-buffer-identification)
|
||||
'("" +modeline-modes))
|
||||
|
||||
;; TODO (def-modeline! pdf ...)
|
||||
;; TODO (def-modeline! helm ...)
|
||||
|
||||
|
||||
;;
|
||||
;;; Bootstrap
|
||||
|
||||
(size-indication-mode +1) ; filesize in modeline
|
||||
|
||||
(setq-default
|
||||
mode-line-format
|
||||
'(""
|
||||
+modeline-bar
|
||||
+modeline-format-left
|
||||
(:eval
|
||||
(propertize
|
||||
" "
|
||||
'display
|
||||
`((space :align-to (- (+ right right-fringe right-margin)
|
||||
,(string-width
|
||||
(format-mode-line '("" +modeline-format-right))))))))
|
||||
+modeline-format-right))
|
||||
(with-current-buffer "*Messages*"
|
||||
(setq mode-line-format (default-value 'mode-line-format)))
|
||||
|
||||
|
||||
;; Other modes
|
||||
(set-modeline! :main 'default)
|
||||
(set-modeline-hook! '+doom-dashboard-mode-hook 'project)
|
||||
(set-modeline-hook! 'pdf-tools-enabled-hook 'pdf)
|
||||
(set-modeline-hook! '(special-mode-hook
|
||||
image-mode-hook
|
||||
circe-mode-hook)
|
||||
'special)
|
||||
|
||||
(add-hook! 'magit-mode-hook
|
||||
(defun +modeline-init-project-or-hide-h ()
|
||||
(if (eq major-mode 'magit-status-mode)
|
||||
(set-modeline! 'project)
|
||||
(hide-mode-line-mode +1))))
|
||||
120
.emacs.d/modules/ui/modeline/README.org
Normal file
120
.emacs.d/modules/ui/modeline/README.org
Normal file
@@ -0,0 +1,120 @@
|
||||
#+TITLE: ui/modeline
|
||||
#+DATE: July 29, 2018
|
||||
#+SINCE: v2.0.9
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC_2:noexport:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#plugins][Plugins]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
- [[#usage][Usage]]
|
||||
- [[#hiding-the-modeline][Hiding the modeline]]
|
||||
- [[#switching-the-modeline-and-header-line][Switching the modeline and header line]]
|
||||
- [[#configuration][Configuration]]
|
||||
- [[#changing-the-default-modeline][Changing the default modeline]]
|
||||
- [[#activating-a-format][Activating a format]]
|
||||
- [[#defining-a-modeline-format][Defining a modeline format]]
|
||||
- [[#defining-a-modeline-segment][Defining a modeline segment]]
|
||||
- [[#extracting-dooms-modeline-into-your-config][Extracting Doom's modeline into your config]]
|
||||
- [[#troubleshooting][Troubleshooting]]
|
||||
- [[#where-are-my-minor-modes][Where are my minor modes?]]
|
||||
- [[#icons-in-my-modeline-look-strange][Icons in my modeline look strange]]
|
||||
- [[#appendix][Appendix]]
|
||||
- [[#autodefs][Autodefs]]
|
||||
- [[#variables][Variables]]
|
||||
- [[#faces][Faces]]
|
||||
|
||||
* Description
|
||||
This module provides an Atom-inspired, minimalistic modeline for Doom Emacs,
|
||||
powered by [[https://github.com/seagle0128/doom-modeline][the doom-modeline package]] (where you can find screenshots).
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/seagle0128/doom-modeline][doom-modeline]]
|
||||
+ [[https://github.com/syohex/emacs-anzu][anzu]]
|
||||
+ [[https://github.com/syohex/emacs-evil-anzu][evil-anzu]]
|
||||
|
||||
* Prerequisites
|
||||
This module has no prerequisites.
|
||||
|
||||
* Usage
|
||||
** TODO Hiding the modeline
|
||||
|
||||
** TODO Switching the modeline and header line
|
||||
|
||||
* Configuration
|
||||
** TODO Changing the default modeline
|
||||
|
||||
** TODO Activating a format
|
||||
|
||||
** TODO Defining a modeline format
|
||||
|
||||
** TODO Defining a modeline segment
|
||||
|
||||
** TODO Extracting Doom's modeline into your config
|
||||
|
||||
* Troubleshooting
|
||||
** Where are my minor modes?
|
||||
I rarely need to know what minor modes are active, so I removed them. ~M-x
|
||||
doom/describe-active-minor-mode~ was written to substitute for it.
|
||||
|
||||
** TODO Icons in my modeline look strange
|
||||
|
||||
* Appendix
|
||||
** Autodefs
|
||||
+ ~def-modeline-format! NAME LEFT &optional RIGHT~
|
||||
+ ~def-modeline-segment! NAME &rest REST~
|
||||
+ ~set-modeline! NAME &optional DEFAULT~
|
||||
** Variables
|
||||
+ doom-modeline-height
|
||||
+ doom-modeline-bar-width
|
||||
+ doom-modeline-buffer-file-name-style
|
||||
+ doom-modeline-icon
|
||||
+ doom-modeline-major-mode-icon
|
||||
+ doom-modeline-major-mode-color-icon
|
||||
+ doom-modeline-buffer-state-icon
|
||||
+ doom-modeline-buffer-modification-icon
|
||||
+ doom-modeline-minor-modes
|
||||
+ doom-modeline-enable-word-count
|
||||
+ doom-modeline-buffer-encoding
|
||||
+ doom-modeline-indent-info
|
||||
+ doom-modeline-checker-simple-format
|
||||
+ doom-modeline-vcs-max-length
|
||||
+ doom-modeline-persp-name
|
||||
+ doom-modeline-lsp
|
||||
+ doom-modeline-github
|
||||
+ doom-modeline-github-interval
|
||||
+ doom-modeline-env-version
|
||||
+ doom-modeline-mu4e
|
||||
+ doom-modeline-irc
|
||||
+ doom-modeline-irc-stylize
|
||||
** Faces
|
||||
+ doom-modeline-buffer-path
|
||||
+ doom-modeline-buffer-file
|
||||
+ doom-modeline-buffer-modified
|
||||
+ doom-modeline-buffer-major-mode
|
||||
+ doom-modeline-buffer-minor-mode
|
||||
+ doom-modeline-project-parent-dir
|
||||
+ doom-modeline-project-dir
|
||||
+ doom-modeline-project-root-dir
|
||||
+ doom-modeline-highlight
|
||||
+ doom-modeline-panel
|
||||
+ doom-modeline-debug
|
||||
+ doom-modeline-info
|
||||
+ doom-modeline-warning
|
||||
+ doom-modeline-urgent
|
||||
+ doom-modeline-unread-number
|
||||
+ doom-modeline-bar
|
||||
+ doom-modeline-inactive-bar
|
||||
+ doom-modeline-evil-emacs-state
|
||||
+ doom-modeline-evil-insert-state
|
||||
+ doom-modeline-evil-motion-state
|
||||
+ doom-modeline-evil-normal-state
|
||||
+ doom-modeline-evil-operator-state
|
||||
+ doom-modeline-evil-visual-state
|
||||
+ doom-modeline-evil-replace-state
|
||||
+ doom-modeline-persp-name
|
||||
+ doom-modeline-persp-buffer-not-in-persp
|
||||
48
.emacs.d/modules/ui/modeline/autoload.el
Normal file
48
.emacs.d/modules/ui/modeline/autoload.el
Normal file
@@ -0,0 +1,48 @@
|
||||
;;; ui/modeline/autoload/modeline.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autodef
|
||||
(defalias 'def-modeline-format! #'doom-modeline-def-modeline)
|
||||
|
||||
;;;###autodef
|
||||
(defalias 'def-modeline-segment! #'doom-modeline-def-segment)
|
||||
|
||||
;;;###autodef
|
||||
(defalias 'set-modeline! #'doom-modeline-set-modeline)
|
||||
|
||||
|
||||
(defvar +modeline--old-bar-height nil)
|
||||
;;;###autoload
|
||||
(defun +modeline-resize-for-font-h ()
|
||||
"Adjust the modeline's height when the font size is changed by
|
||||
`doom/increase-font-size' or `doom/decrease-font-size'.
|
||||
|
||||
Meant for `doom-change-font-size-hook'."
|
||||
(unless +modeline--old-bar-height
|
||||
(setq +modeline--old-bar-height doom-modeline-height))
|
||||
(let ((default-height +modeline--old-bar-height)
|
||||
(scale (or (frame-parameter nil 'font-scale) 0)))
|
||||
(if (> scale 0)
|
||||
(let* ((font-size (string-to-number
|
||||
(aref (doom--font-name (frame-parameter nil 'font)
|
||||
(selected-frame))
|
||||
xlfd-regexp-pixelsize-subnum)))
|
||||
(scale (frame-parameter nil 'font-scale)))
|
||||
(setq doom-modeline-height (+ default-height (* scale doom-font-increment))))
|
||||
(setq doom-modeline-height default-height))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +modeline-update-env-in-all-windows-h (&rest _)
|
||||
"Update version strings in all buffers."
|
||||
(dolist (window (window-list))
|
||||
(with-selected-window window
|
||||
(doom-modeline-update-env)
|
||||
(force-mode-line-update))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +modeline-clear-env-in-all-windows-h (&rest _)
|
||||
"Blank out version strings in all buffers."
|
||||
(dolist (buffer (buffer-list))
|
||||
(with-current-buffer buffer
|
||||
(setq doom-modeline-env--version
|
||||
(bound-and-true-p doom-modeline-load-string))))
|
||||
(force-mode-line-update t))
|
||||
79
.emacs.d/modules/ui/modeline/config.el
Normal file
79
.emacs.d/modules/ui/modeline/config.el
Normal file
@@ -0,0 +1,79 @@
|
||||
;;; ui/modeline/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(when (featurep! +light)
|
||||
(load! "+light"))
|
||||
|
||||
|
||||
(use-package! doom-modeline
|
||||
:unless (featurep! +light)
|
||||
:hook (after-init . doom-modeline-mode)
|
||||
:init
|
||||
(unless after-init-time
|
||||
;; prevent flash of unstyled modeline at startup
|
||||
(setq-default mode-line-format nil))
|
||||
;; We display project info in the modeline ourselves
|
||||
(setq projectile-dynamic-mode-line nil)
|
||||
;; Set these early so they don't trigger variable watchers
|
||||
(setq doom-modeline-bar-width 3
|
||||
doom-modeline-github nil
|
||||
doom-modeline-mu4e nil
|
||||
doom-modeline-persp-name nil
|
||||
doom-modeline-minor-modes nil
|
||||
doom-modeline-major-mode-icon nil
|
||||
doom-modeline-buffer-file-name-style 'relative-from-project)
|
||||
|
||||
;; Fix modeline icons in daemon-spawned graphical frames. We have our own
|
||||
;; mechanism for disabling all-the-icons, so we don't need doom-modeline to do
|
||||
;; it for us. However, this may cause unwanted padding in the modeline in
|
||||
;; daemon-spawned terminal frames. If it bothers you, you may prefer
|
||||
;; `doom-modeline-icon' set to `nil'.
|
||||
(when (daemonp)
|
||||
(setq doom-modeline-icon t))
|
||||
:config
|
||||
;; Fix an issue where these two variables aren't defined in TTY Emacs on MacOS
|
||||
(defvar mouse-wheel-down-event nil)
|
||||
(defvar mouse-wheel-up-event nil)
|
||||
|
||||
(size-indication-mode +1) ; filesize in modeline
|
||||
(column-number-mode +1) ; cursor column in modeline
|
||||
|
||||
(add-hook 'doom-change-font-size-hook #'+modeline-resize-for-font-h)
|
||||
(add-hook 'doom-load-theme-hook #'doom-modeline-refresh-bars)
|
||||
|
||||
(add-hook '+doom-dashboard-mode-hook #'doom-modeline-set-project-modeline)
|
||||
|
||||
(add-hook! 'magit-mode-hook
|
||||
(defun +modeline-hide-in-non-status-buffer-h ()
|
||||
"Show minimal modeline in magit-status buffer, no modeline elsewhere."
|
||||
(if (eq major-mode 'magit-status-mode)
|
||||
(doom-modeline-set-project-modeline)
|
||||
(hide-mode-line-mode))))
|
||||
|
||||
;; Remove unused segments & extra padding
|
||||
(doom-modeline-def-modeline 'main
|
||||
'(bar window-number matches buffer-info remote-host buffer-position selection-info)
|
||||
'(objed-state misc-info persp-name irc mu4e github debug input-method buffer-encoding lsp major-mode process vcs checker))
|
||||
|
||||
(doom-modeline-def-modeline 'special
|
||||
'(bar window-number matches buffer-info-simple buffer-position selection-info)
|
||||
'(objed-state misc-info persp-name debug input-method irc-buffers buffer-encoding lsp major-mode process checker))
|
||||
|
||||
(doom-modeline-def-modeline 'project
|
||||
'(bar window-number buffer-default-directory)
|
||||
'(misc-info mu4e github debug battery " " major-mode process))
|
||||
|
||||
;; Some functions modify the buffer, causing the modeline to show a false
|
||||
;; modified state, so force them to behave.
|
||||
(defadvice! +modeline--inhibit-modification-hooks-a (orig-fn &rest args)
|
||||
:around #'ws-butler-after-save
|
||||
(with-silent-modifications (apply orig-fn args)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Extensions
|
||||
(use-package! anzu
|
||||
:after-call isearch-mode)
|
||||
|
||||
(use-package! evil-anzu
|
||||
:when (featurep! :editor evil)
|
||||
:after-call evil-ex-start-search evil-ex-start-word-search evil-ex-search-activate-highlight))
|
||||
8
.emacs.d/modules/ui/modeline/packages.el
Normal file
8
.emacs.d/modules/ui/modeline/packages.el
Normal file
@@ -0,0 +1,8 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/modeline/packages.el
|
||||
|
||||
(unless (featurep! +light)
|
||||
(package! doom-modeline))
|
||||
(package! anzu)
|
||||
(when (featurep! :editor evil)
|
||||
(package! evil-anzu))
|
||||
37
.emacs.d/modules/ui/nav-flash/README.org
Normal file
37
.emacs.d/modules/ui/nav-flash/README.org
Normal file
@@ -0,0 +1,37 @@
|
||||
#+TITLE: ui/nav-flash
|
||||
#+DATE: June 4, 2017
|
||||
#+SINCE: v2.0
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#description][Description]]
|
||||
- [[#plugins][Plugins]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
- [[#configuration][Configuration]]
|
||||
|
||||
* Description
|
||||
This module flashes the line around the cursor after any significant motion, to
|
||||
make it easy to follow after big operations.
|
||||
|
||||
#+begin_quote
|
||||
Tremendously helpful on large, 1600p+ or 4K displays.
|
||||
#+end_quote
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/rolandwalker/nav-flash][nav-flash]]
|
||||
|
||||
* Prerequisites
|
||||
This module has no dependencies.
|
||||
|
||||
* Configuration
|
||||
By default, ~nav-flash~ will be triggered whenever ~recenter~ is called or an
|
||||
entry is added to the jump-list (maanaged by better-jumper).
|
||||
|
||||
~recenter~ is called after many hooks and commands, such as:
|
||||
|
||||
+ better-jumper-post-jump-hook
|
||||
+ rtags-after-find-file-hook
|
||||
+ org-follow-link-hook
|
||||
+ imenu-after-jump-hook
|
||||
+ counsel-grep-post-action-hook
|
||||
+ dumb-jump-after-jump-hook
|
||||
45
.emacs.d/modules/ui/nav-flash/autoload.el
Normal file
45
.emacs.d/modules/ui/nav-flash/autoload.el
Normal file
@@ -0,0 +1,45 @@
|
||||
;;; ui/nav-flash/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +nav-flash--last-point nil)
|
||||
|
||||
;;;###autoload
|
||||
(defun +nav-flash-blink-cursor (&rest _)
|
||||
"Blinks the current line in the current window, to make it clear where the
|
||||
cursor has landed (typically after a large motion, like switching windows or
|
||||
jumping to another part of the file)."
|
||||
(unless (minibufferp)
|
||||
(nav-flash-show)
|
||||
;; only show in the current window
|
||||
(overlay-put compilation-highlight-overlay 'window (selected-window))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +nav-flash-blink-cursor-maybe (&rest _)
|
||||
"Like `+nav-flash-blink-cursor', but no-ops if in special-mode or term-mode,
|
||||
or triggered from one of `+nav-flash-exclude-commands'."
|
||||
(unless (or (memq this-command +nav-flash-exclude-commands)
|
||||
(bound-and-true-p so-long-minor-mode)
|
||||
(derived-mode-p 'so-long-mode 'special-mode 'term-mode)
|
||||
(and (equal (point-marker) (car +nav-flash--last-point))
|
||||
(equal (selected-window) (cdr +nav-flash--last-point))))
|
||||
(+nav-flash-blink-cursor)
|
||||
(setq +nav-flash--last-point (cons (point-marker) (selected-window)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +nav-flash-delayed-blink-cursor-h (&rest _)
|
||||
"Like `+nav-flash-blink-cursor', but links after a tiny pause, in case it
|
||||
isn't clear at run-time if the point will be in the correct window/buffer (like
|
||||
for `org-follow-link-hook')."
|
||||
(run-at-time 0.1 nil #'+nav-flash-blink-cursor-h))
|
||||
|
||||
;;;###autoload
|
||||
(defalias '+nav-flash-blink-cursor-h #'+nav-flash-blink-cursor)
|
||||
;;;###autoload
|
||||
(defalias '+nav-flash-blink-cursor-maybe-h #'+nav-flash-blink-cursor-maybe)
|
||||
;;;###autoload
|
||||
(defalias '+nav-flash-blink-cursor-a #'+nav-flash-blink-cursor-maybe)
|
||||
|
||||
;;;###autoload
|
||||
(defun +nav-flash/blink-cursor (&rest _)
|
||||
"Blink current line using `nav-flash'."
|
||||
(interactive)
|
||||
(+nav-flash-blink-cursor))
|
||||
33
.emacs.d/modules/ui/nav-flash/config.el
Normal file
33
.emacs.d/modules/ui/nav-flash/config.el
Normal file
@@ -0,0 +1,33 @@
|
||||
;;; ui/nav-flash/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +nav-flash-exclude-commands
|
||||
'(mouse-set-point mouse-drag-region evil-mouse-drag-region +org/dwim-at-point
|
||||
org-find-file org-find-file-at-mouse)
|
||||
"A list of commands that should not trigger nav-flash.")
|
||||
|
||||
(use-package! nav-flash
|
||||
:defer t
|
||||
:init
|
||||
;; NOTE In :tools lookup `recenter' is hooked to a bunch of jumping
|
||||
;; commands, which will trigger nav-flash.
|
||||
(add-hook! '(imenu-after-jump-hook
|
||||
better-jumper-post-jump-hook
|
||||
counsel-grep-post-action-hook
|
||||
dumb-jump-after-jump-hook)
|
||||
#'+nav-flash-blink-cursor-maybe-h)
|
||||
|
||||
(add-hook 'doom-switch-window-hook #'+nav-flash-blink-cursor-maybe-h)
|
||||
|
||||
;; `org'
|
||||
(add-hook 'org-follow-link-hook #'+nav-flash-delayed-blink-cursor-h)
|
||||
|
||||
;; `saveplace'
|
||||
(advice-add #'save-place-find-file-hook :after #'+nav-flash-blink-cursor-a)
|
||||
|
||||
;; `evil'
|
||||
(advice-add #'evil-window-top :after #'+nav-flash-blink-cursor-a)
|
||||
(advice-add #'evil-window-middle :after #'+nav-flash-blink-cursor-a)
|
||||
(advice-add #'evil-window-bottom :after #'+nav-flash-blink-cursor-a)
|
||||
|
||||
;; Bound to `ga' for evil users
|
||||
(advice-add #'what-cursor-position :after #'+nav-flash-blink-cursor-a))
|
||||
4
.emacs.d/modules/ui/nav-flash/packages.el
Normal file
4
.emacs.d/modules/ui/nav-flash/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/nav-flash/packages.el
|
||||
|
||||
(package! nav-flash)
|
||||
9
.emacs.d/modules/ui/neotree/README.org
Normal file
9
.emacs.d/modules/ui/neotree/README.org
Normal file
@@ -0,0 +1,9 @@
|
||||
#+TITLE: :evil neotree
|
||||
|
||||
This module brings a side panel for browsing project files, inspired by vim's
|
||||
NERDTree.
|
||||
|
||||
#+begin_quote
|
||||
Sure, there's dired and projectile, but sometimes I'd like a bird's eye view of
|
||||
a project.
|
||||
#+end_quote
|
||||
71
.emacs.d/modules/ui/neotree/autoload.el
Normal file
71
.emacs.d/modules/ui/neotree/autoload.el
Normal file
@@ -0,0 +1,71 @@
|
||||
;;; ui/neotree/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; `neotree-show' and `neotree-find' don't respect the current project, and open
|
||||
;; neotree in `default-directory'. `+neotree/open' and `neotree/find-this-file'
|
||||
;; will ensure the neotree pane is always rooted in the project root.
|
||||
|
||||
;;;###autoload
|
||||
(defun +neotree/open ()
|
||||
"Open the neotree window in the current project."
|
||||
(interactive)
|
||||
(require 'neotree)
|
||||
(if (neo-global--window-exists-p)
|
||||
(neotree-hide)
|
||||
(neotree-dir (or (doom-project-root)
|
||||
default-directory))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +neotree/find-this-file ()
|
||||
"Open the neotree window in the current project, and find the current file."
|
||||
(interactive)
|
||||
(let ((path buffer-file-name)
|
||||
(project-root (or (doom-project-root)
|
||||
default-directory)))
|
||||
(require 'neotree)
|
||||
(cond ((and (neo-global--window-exists-p)
|
||||
(get-buffer-window neo-buffer-name t))
|
||||
(neotree-find path project-root)
|
||||
(neotree-refresh))
|
||||
((not (and (neo-global--window-exists-p)
|
||||
(equal (file-truename (neo-global--with-buffer neo-buffer--start-node))
|
||||
(file-truename project-root))))
|
||||
(neotree-dir project-root)
|
||||
(neotree-find path project-root))
|
||||
(t
|
||||
(neotree-find path project-root)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +neotree/collapse-or-up ()
|
||||
"Collapse an expanded directory node or go to the parent node."
|
||||
(interactive)
|
||||
(when-let (node (neo-buffer--get-filename-current-line))
|
||||
(if (file-directory-p node)
|
||||
(if (neo-buffer--expanded-node-p node)
|
||||
(+neotree/collapse)
|
||||
(neotree-select-up-node))
|
||||
(neotree-select-up-node))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +neotree/collapse ()
|
||||
"Collapse a neotree node."
|
||||
(interactive)
|
||||
(when-let (node (neo-buffer--get-filename-current-line))
|
||||
(when (file-directory-p node)
|
||||
(neo-buffer--set-expand node nil)
|
||||
(neo-buffer--refresh t))
|
||||
(when neo-auto-indent-point
|
||||
(neo-point-auto-indent))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +neotree/expand-or-open ()
|
||||
"Expand or open a neotree node."
|
||||
(interactive)
|
||||
(when-let (node (neo-buffer--get-filename-current-line))
|
||||
(cond ((file-directory-p node)
|
||||
(neo-buffer--set-expand node t)
|
||||
(neo-buffer--refresh t)
|
||||
(when neo-auto-indent-point
|
||||
(forward-line)
|
||||
(neo-point-auto-indent)))
|
||||
(t
|
||||
(call-interactively #'neotree-enter)))))
|
||||
81
.emacs.d/modules/ui/neotree/config.el
Normal file
81
.emacs.d/modules/ui/neotree/config.el
Normal file
@@ -0,0 +1,81 @@
|
||||
;;; ui/neotree/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! neotree
|
||||
:commands (neotree-show
|
||||
neotree-hide
|
||||
neotree-toggle
|
||||
neotree-dir
|
||||
neotree-find
|
||||
neo-global--with-buffer
|
||||
neo-global--window-exists-p)
|
||||
:config
|
||||
(setq neo-create-file-auto-open nil
|
||||
neo-auto-indent-point nil
|
||||
neo-autorefresh nil
|
||||
neo-mode-line-type 'none
|
||||
neo-window-width 24
|
||||
neo-show-updir-line nil
|
||||
neo-theme 'icons
|
||||
neo-banner-message nil
|
||||
neo-confirm-create-file #'off-p
|
||||
neo-confirm-create-directory #'off-p
|
||||
neo-show-hidden-files nil
|
||||
neo-keymap-style 'concise
|
||||
neo-show-hidden-files t
|
||||
neo-hidden-regexp-list
|
||||
'(;; vcs folders
|
||||
"^\\.\\(?:git\\|hg\\|svn\\)$"
|
||||
;; compiled files
|
||||
"\\.\\(?:pyc\\|o\\|elc\\|lock\\|css.map\\|class\\)$"
|
||||
;; generated files, caches or local pkgs
|
||||
"^\\(?:node_modules\\|vendor\\|.\\(project\\|cask\\|yardoc\\|sass-cache\\)\\)$"
|
||||
;; org-mode folders
|
||||
"^\\.\\(?:sync\\|export\\|attach\\)$"
|
||||
;; temp files
|
||||
"~$"
|
||||
"^#.*#$"))
|
||||
|
||||
(set-popup-rule! "^ ?\\*NeoTree" :ignore t)
|
||||
|
||||
(after! winner
|
||||
(add-to-list 'winner-boring-buffers neo-buffer-name))
|
||||
|
||||
;; The cursor always sits at bol. `+neotree--fix-cursor-h' and
|
||||
;; `+neotree--indent-cursor-a' change that behavior so that the cursor is
|
||||
;; always on the first non-blank character on the line, in the neo buffer.
|
||||
(add-hook! 'neo-enter-hook
|
||||
(defun +neotree-fix-cursor-h (&rest _)
|
||||
(with-current-buffer neo-global--buffer
|
||||
(+neotree--indent-cursor-a))))
|
||||
|
||||
(defadvice! +neotree--indent-cursor-a (&rest _)
|
||||
:after '(neotree-next-line neotree-previous-line)
|
||||
(beginning-of-line)
|
||||
(skip-chars-forward " \t\r"))
|
||||
|
||||
(map! :map neotree-mode-map
|
||||
:n "g" nil
|
||||
:n "TAB" #'neotree-quick-look
|
||||
:n "RET" #'neotree-enter
|
||||
:n [tab] #'neotree-quick-look
|
||||
:n [return] #'neotree-enter
|
||||
:n "DEL" #'evil-window-prev
|
||||
:n "c" #'neotree-create-node
|
||||
:n "r" #'neotree-rename-node
|
||||
:n "d" #'neotree-delete-node
|
||||
:n "j" #'neotree-next-line
|
||||
:n "k" #'neotree-previous-line
|
||||
:n "n" #'neotree-next-line
|
||||
:n "p" #'neotree-previous-line
|
||||
:n "h" #'+neotree/collapse-or-up
|
||||
:n "l" #'+neotree/expand-or-open
|
||||
:n "J" #'neotree-select-next-sibling-node
|
||||
:n "K" #'neotree-select-previous-sibling-node
|
||||
:n "H" #'neotree-select-up-node
|
||||
:n "L" #'neotree-select-down-node
|
||||
:n "G" #'evil-goto-line
|
||||
:n "gg" #'evil-goto-first-line
|
||||
:n "v" #'neotree-enter-vertical-split
|
||||
:n "s" #'neotree-enter-horizontal-split
|
||||
:n "q" #'neotree-hide
|
||||
:n "R" #'neotree-refresh))
|
||||
4
.emacs.d/modules/ui/neotree/packages.el
Normal file
4
.emacs.d/modules/ui/neotree/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/neotree/packages.el
|
||||
|
||||
(package! neotree)
|
||||
23
.emacs.d/modules/ui/ophints/README.org
Normal file
23
.emacs.d/modules/ui/ophints/README.org
Normal file
@@ -0,0 +1,23 @@
|
||||
#+TITLE: ui/ophints
|
||||
#+DATE: June 4, 2017
|
||||
#+SINCE: v2.0
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#plugins][Plugins]]
|
||||
|
||||
* Description
|
||||
This module provides op-hints (operation hinting), i.e. visual feedback for
|
||||
certain operations. It highlights regions of text that the last operation (like
|
||||
yank) acted on.
|
||||
|
||||
Uses ~evil-goggles~ for evil users and ~volatile-highlights~ otherwise.
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/edkolev/evil-goggles/][evil-goggles]]*
|
||||
+ [[https://github.com/k-talo/volatile-highlights.el][volatile-highlights]]*
|
||||
33
.emacs.d/modules/ui/ophints/config.el
Normal file
33
.emacs.d/modules/ui/ophints/config.el
Normal file
@@ -0,0 +1,33 @@
|
||||
;;; ui/ophints/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! evil-goggles
|
||||
:when (featurep! :editor evil)
|
||||
:after-call pre-command-hook
|
||||
:init
|
||||
(setq evil-goggles-duration 0.1
|
||||
evil-goggles-pulse nil ; too slow
|
||||
;; evil-goggles provides a good indicator of what has been affected.
|
||||
;; delete/change is obvious, so I'd rather disable it for these.
|
||||
evil-goggles-enable-delete nil
|
||||
evil-goggles-enable-change nil)
|
||||
:config
|
||||
(pushnew! evil-goggles--commands
|
||||
'(+evil:yank-unindented
|
||||
:face evil-goggles-yank-face
|
||||
:switch evil-goggles-enable-yank
|
||||
:advice evil-goggles--generic-async-advice)
|
||||
'(+eval:region
|
||||
:face evil-goggles-yank-face
|
||||
:switch evil-goggles-enable-yank
|
||||
:advice evil-goggles--generic-async-advice))
|
||||
(evil-goggles-mode +1))
|
||||
|
||||
|
||||
(use-package! volatile-highlights
|
||||
:unless (featurep! :editor evil)
|
||||
:after-call pre-command-hook
|
||||
:config
|
||||
(volatile-highlights-mode)
|
||||
(after! undo-tree
|
||||
(vhl/define-extension 'undo-tree 'undo-tree-yank 'undo-tree-move)
|
||||
(vhl/install-extension 'undo-tree)))
|
||||
6
.emacs.d/modules/ui/ophints/packages.el
Normal file
6
.emacs.d/modules/ui/ophints/packages.el
Normal file
@@ -0,0 +1,6 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/ophints/packages.el
|
||||
|
||||
(if (featurep! :editor evil)
|
||||
(package! evil-goggles)
|
||||
(package! volatile-highlights))
|
||||
344
.emacs.d/modules/ui/popup/+hacks.el
Normal file
344
.emacs.d/modules/ui/popup/+hacks.el
Normal file
@@ -0,0 +1,344 @@
|
||||
;;; ui/popup/+hacks.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; What follows are all the hacks needed to get various parts of Emacs and other
|
||||
;; plugins to cooperate with the popup management system. Essentially, it comes
|
||||
;; down to:
|
||||
;;
|
||||
;; 1. Making plugins that control their own window environment less greedy (e.g.
|
||||
;; org agenda, which tries to reconfigure the entire frame by deleting all
|
||||
;; other windows just to pop up one tiny window).
|
||||
;; 2. Forcing plugins to use `display-buffer' and `pop-to-buffer' instead of
|
||||
;; `switch-to-buffer' (which is unaffected by `display-buffer-alist', which
|
||||
;; this module heavily relies on).
|
||||
;; 3. Closing popups (temporarily) before functions that are highly destructive
|
||||
;; to the illusion of popup control get run (with the use of the
|
||||
;; `save-popups!' macro).
|
||||
;;
|
||||
;; Keep in mind, all this black magic may break in future updates, and will need
|
||||
;; to be watched carefully for corner cases. Also, once this file is loaded,
|
||||
;; many of its changes are irreversible without restarting Emacs! I don't like
|
||||
;; it either, but I will address this over time.
|
||||
;;
|
||||
;; Hacks should be kept in alphabetical order, named after the feature they
|
||||
;; modify, and should follow a ;;;## package-name header line (if not using
|
||||
;; `after!' or `use-package!').
|
||||
|
||||
;;
|
||||
;;; Core functions
|
||||
|
||||
;; Don't try to resize popup windows
|
||||
(advice-add #'balance-windows :around #'+popup-save-a)
|
||||
|
||||
(defun +popup/quit-window ()
|
||||
"The regular `quit-window' sometimes kills the popup buffer and switches to a
|
||||
buffer that shouldn't be in a popup. We prevent that by remapping `quit-window'
|
||||
to this commmand."
|
||||
(interactive)
|
||||
(let ((orig-buffer (current-buffer)))
|
||||
(quit-window)
|
||||
(when (and (eq orig-buffer (current-buffer))
|
||||
(+popup-window-p))
|
||||
(+popup/close))))
|
||||
(global-set-key [remap quit-window] #'+popup/quit-window)
|
||||
|
||||
|
||||
;;
|
||||
;;; External functions
|
||||
|
||||
;;;###package buff-menu
|
||||
(define-key Buffer-menu-mode-map (kbd "RET") #'Buffer-menu-other-window)
|
||||
|
||||
|
||||
;;;###package company
|
||||
(defadvice! +popup--dont-select-me-a (orig-fn &rest args)
|
||||
:around #'company-show-doc-buffer
|
||||
(let ((+popup--inhibit-select t))
|
||||
(apply orig-fn args)))
|
||||
|
||||
|
||||
;;;###package eshell
|
||||
(progn
|
||||
(setq eshell-destroy-buffer-when-process-dies t)
|
||||
|
||||
;; When eshell runs a visual command (see `eshell-visual-commands'), it spawns
|
||||
;; a term buffer to run it in, but where it spawns it is the problem...
|
||||
(defadvice! +popup--eshell-undedicate-popup (&rest _)
|
||||
"Force spawned term buffer to share with the eshell popup (if necessary)."
|
||||
:before #'eshell-exec-visual
|
||||
(when (+popup-window-p)
|
||||
(set-window-dedicated-p nil nil)
|
||||
(add-transient-hook! #'eshell-query-kill-processes :after
|
||||
(set-window-dedicated-p nil t)))))
|
||||
|
||||
|
||||
;;;###package evil
|
||||
(progn
|
||||
;; Make evil-mode cooperate with popups
|
||||
(defadvice! +popup--evil-command-window-a (hist cmd-key execute-fn)
|
||||
"Monkey patch the evil command window to use `pop-to-buffer' instead of
|
||||
`switch-to-buffer', allowing the popup manager to handle it."
|
||||
:override #'evil-command-window
|
||||
(when (eq major-mode 'evil-command-window-mode)
|
||||
(user-error "Cannot recursively open command line window"))
|
||||
(dolist (win (window-list))
|
||||
(when (equal (buffer-name (window-buffer win))
|
||||
"*Command Line*")
|
||||
(kill-buffer (window-buffer win))
|
||||
(delete-window win)))
|
||||
(setq evil-command-window-current-buffer (current-buffer))
|
||||
(ignore-errors (kill-buffer "*Command Line*"))
|
||||
(with-current-buffer (pop-to-buffer "*Command Line*")
|
||||
(setq-local evil-command-window-execute-fn execute-fn)
|
||||
(setq-local evil-command-window-cmd-key cmd-key)
|
||||
(evil-command-window-mode)
|
||||
(evil-command-window-insert-commands hist)))
|
||||
|
||||
(defadvice! +popup--evil-command-window-execute-a ()
|
||||
"Execute the command under the cursor in the appropriate buffer, rather than
|
||||
the command buffer."
|
||||
:override #'evil-command-window-execute
|
||||
(interactive)
|
||||
(let ((result (buffer-substring (line-beginning-position)
|
||||
(line-end-position)))
|
||||
(execute-fn evil-command-window-execute-fn)
|
||||
(execute-window (get-buffer-window evil-command-window-current-buffer))
|
||||
(popup (selected-window)))
|
||||
(if execute-window
|
||||
(select-window execute-window)
|
||||
(user-error "Originating buffer is no longer active"))
|
||||
;; (kill-buffer "*Command Line*")
|
||||
(delete-window popup)
|
||||
(funcall execute-fn result)
|
||||
(setq evil-command-window-current-buffer nil)))
|
||||
|
||||
;; Don't mess with popups
|
||||
(advice-add #'+evil--window-swap :around #'+popup-save-a)
|
||||
(advice-add #'evil-window-move-very-bottom :around #'+popup-save-a)
|
||||
(advice-add #'evil-window-move-very-top :around #'+popup-save-a)
|
||||
(advice-add #'evil-window-move-far-left :around #'+popup-save-a)
|
||||
(advice-add #'evil-window-move-far-right :around #'+popup-save-a))
|
||||
|
||||
|
||||
;;;###package help-mode
|
||||
(after! help-mode
|
||||
(defun +popup--switch-from-popup (location)
|
||||
(let (origin enable-local-variables)
|
||||
(save-popups!
|
||||
(switch-to-buffer (car location) nil t)
|
||||
(if (not (cdr location))
|
||||
(message "Unable to find location in file")
|
||||
(goto-char (cdr location))
|
||||
(recenter)
|
||||
(setq origin (selected-window))))
|
||||
(select-window origin)))
|
||||
|
||||
;; Help buffers use `pop-to-window' to decide where to open followed links,
|
||||
;; which can be unpredictable. It should *only* replace the original buffer we
|
||||
;; opened the popup from. To fix this these three button types need to be
|
||||
;; redefined to set aside the popup before following a link.
|
||||
(define-button-type 'help-function-def
|
||||
:supertype 'help-xref
|
||||
'help-function
|
||||
(lambda (fun file)
|
||||
(require 'find-func)
|
||||
(when (eq file 'C-source)
|
||||
(setq file (help-C-file-name (indirect-function fun) 'fun)))
|
||||
(+popup--switch-from-popup (find-function-search-for-symbol fun nil file))))
|
||||
|
||||
(define-button-type 'help-variable-def
|
||||
:supertype 'help-xref
|
||||
'help-function
|
||||
(lambda (var &optional file)
|
||||
(when (eq file 'C-source)
|
||||
(setq file (help-C-file-name var 'var)))
|
||||
(+popup--switch-from-popup (find-variable-noselect var file))))
|
||||
|
||||
(define-button-type 'help-face-def
|
||||
:supertype 'help-xref
|
||||
'help-function
|
||||
(lambda (fun file)
|
||||
(require 'find-func)
|
||||
(+popup--switch-from-popup (find-function-search-for-symbol fun 'defface file)))))
|
||||
|
||||
|
||||
;;;###package helpful
|
||||
(defadvice! +popup--helpful-open-in-origin-window-a (button)
|
||||
"Open links in non-popup, originating window rather than helpful's window."
|
||||
:override #'helpful--navigate
|
||||
(let ((path (substring-no-properties (button-get button 'path)))
|
||||
enable-local-variables
|
||||
origin)
|
||||
(save-popups!
|
||||
(find-file path)
|
||||
(when-let (pos (get-text-property button 'position
|
||||
(marker-buffer button)))
|
||||
(goto-char pos))
|
||||
(setq origin (selected-window))
|
||||
(recenter))
|
||||
(select-window origin)))
|
||||
|
||||
|
||||
;;;###package helm
|
||||
;;;###package helm-ag
|
||||
(when (featurep! :completion helm)
|
||||
(setq helm-default-display-buffer-functions '(+popup-display-buffer-stacked-side-window-fn))
|
||||
|
||||
;; Fix #897: "cannot open side window" error when TAB-completing file links
|
||||
(defadvice! +popup--helm-hide-org-links-popup-a (orig-fn &rest args)
|
||||
:around #'org-insert-link
|
||||
(cl-letf* ((old-org-completing-read (symbol-function 'org-completing-read))
|
||||
((symbol-function 'org-completing-read)
|
||||
(lambda (&rest args)
|
||||
(when-let (win (get-buffer-window "*Org Links*"))
|
||||
;; While helm is opened as a popup, it will mistaken the
|
||||
;; *Org Links* popup for the "originated window", and will
|
||||
;; target it for actions invoked by the user. However, since
|
||||
;; *Org Links* is a popup too (they're dedicated side
|
||||
;; windows), Emacs complains about being unable to split a
|
||||
;; side window. The simple fix: get rid of *Org Links*!
|
||||
(delete-window win)
|
||||
;; But it must exist for org to clean up later.
|
||||
(get-buffer-create "*Org Links*"))
|
||||
(apply old-org-completing-read args))))
|
||||
(apply #'funcall-interactively orig-fn args)))
|
||||
|
||||
;; Fix left-over popup window when closing persistent help for `helm-M-x'
|
||||
(defadvice! +popup--helm-elisp--persistent-help-a (candidate _fun &optional _name)
|
||||
:before #'helm-elisp--persistent-help
|
||||
(let (win)
|
||||
(when (and (helm-attr 'help-running-p)
|
||||
(string= candidate (helm-attr 'help-current-symbol))
|
||||
(setq win (get-buffer-window (get-buffer (help-buffer)))))
|
||||
(delete-window win)))))
|
||||
|
||||
|
||||
;;;###package Info
|
||||
(defadvice! +popup--switch-to-info-window-a (&rest _)
|
||||
:after #'info-lookup-symbol
|
||||
(when-let (win (get-buffer-window "*info*"))
|
||||
(when (+popup-window-p win)
|
||||
(select-window win))))
|
||||
|
||||
|
||||
;;;###package multi-term
|
||||
(setq multi-term-buffer-name "doom terminal")
|
||||
|
||||
|
||||
;;;###package neotree
|
||||
(after! neotree
|
||||
(advice-add #'neo-util--set-window-width :override #'ignore)
|
||||
(advice-remove #'balance-windows #'ad-Advice-balance-windows))
|
||||
|
||||
|
||||
;;;###package org
|
||||
(after! org
|
||||
;; Org has a scorched-earth window management policy I'm not fond of. i.e. it
|
||||
;; kills all other windows just so it can monopolize the frame. No thanks. We
|
||||
;; can do better ourselves.
|
||||
(defadvice! +popup--suppress-delete-other-windows-a (orig-fn &rest args)
|
||||
:around '(org-add-log-note
|
||||
org-capture-place-template
|
||||
org-export--dispatch-ui
|
||||
org-agenda-get-restriction-and-command
|
||||
org-fast-tag-selection
|
||||
org-fast-todo-selection)
|
||||
(if +popup-mode
|
||||
(cl-letf (((symbol-function #'delete-other-windows)
|
||||
(symbol-function #'ignore)))
|
||||
(apply orig-fn args))
|
||||
(apply orig-fn args)))
|
||||
|
||||
(defadvice! +popup--org-fix-popup-window-shrinking-a (orig-fn &rest args)
|
||||
"Hides the mode-line in *Org tags* buffer so you can actually see its
|
||||
content and displays it in a side window without deleting all other windows.
|
||||
Ugh, such an ugly hack."
|
||||
:around '(org-fast-tag-selection
|
||||
org-fast-todo-selection)
|
||||
(if +popup-mode
|
||||
(cl-letf* ((old-fit-buffer-fn (symbol-function #'org-fit-window-to-buffer))
|
||||
((symbol-function #'org-fit-window-to-buffer)
|
||||
(lambda (&optional window max-height min-height shrink-only)
|
||||
(when-let (buf (window-buffer window))
|
||||
(delete-window window)
|
||||
(select-window
|
||||
(setq window (display-buffer-at-bottom buf nil)))
|
||||
(with-current-buffer buf
|
||||
(setq mode-line-format nil)))
|
||||
(funcall old-fit-buffer-fn window max-height min-height shrink-only))))
|
||||
(apply orig-fn args))
|
||||
(apply orig-fn args)))
|
||||
|
||||
;; Ensure todo, agenda, and other minor popups are delegated to the popup system.
|
||||
(defadvice! +popup--org-pop-to-buffer-a (orig-fn buf &optional norecord)
|
||||
"Use `pop-to-buffer' instead of `switch-to-buffer' to open buffer.'"
|
||||
:around #'org-switch-to-buffer-other-window
|
||||
(if +popup-mode
|
||||
(pop-to-buffer buf nil norecord)
|
||||
(funcall orig-fn buf norecord))))
|
||||
|
||||
|
||||
;;;###package persp-mode
|
||||
(defadvice! +popup--persp-mode-restore-popups-a (&rest _)
|
||||
"Restore popup windows when loading a perspective from file."
|
||||
:after #'persp-load-state-from-file
|
||||
(dolist (window (window-list))
|
||||
(when (+popup-parameter 'popup window)
|
||||
(+popup--init window nil))))
|
||||
|
||||
|
||||
;;;###package pdf-tools
|
||||
(after! pdf-tools
|
||||
(setq tablist-context-window-display-action
|
||||
'((+popup-display-buffer-stacked-side-window-fn)
|
||||
(side . left)
|
||||
(slot . 2)
|
||||
(window-height . 0.3)
|
||||
(inhibit-same-window . t))
|
||||
pdf-annot-list-display-buffer-action
|
||||
'((+popup-display-buffer-stacked-side-window-fn)
|
||||
(side . left)
|
||||
(slot . 3)
|
||||
(inhibit-same-window . t))))
|
||||
|
||||
|
||||
;;;###package profiler
|
||||
(defadvice! +popup--profiler-report-find-entry-in-other-window-a (orig-fn function)
|
||||
:around #'profiler-report-find-entry
|
||||
(cl-letf (((symbol-function 'find-function)
|
||||
(symbol-function 'find-function-other-window)))
|
||||
(funcall orig-fn function)))
|
||||
|
||||
|
||||
;;;###package wgrep
|
||||
(progn
|
||||
;; close the popup after you're done with a wgrep buffer
|
||||
(advice-add #'wgrep-abort-changes :after #'+popup-close-a)
|
||||
(advice-add #'wgrep-finish-edit :after #'+popup-close-a))
|
||||
|
||||
|
||||
;;;###package which-key
|
||||
(after! which-key
|
||||
(when (eq which-key-popup-type 'side-window)
|
||||
(setq which-key-popup-type 'custom
|
||||
which-key-custom-popup-max-dimensions-function (lambda (_) (which-key--side-window-max-dimensions))
|
||||
which-key-custom-hide-popup-function #'which-key--hide-buffer-side-window
|
||||
which-key-custom-show-popup-function
|
||||
(lambda (act-popup-dim)
|
||||
(cl-letf (((symbol-function 'display-buffer-in-side-window)
|
||||
(lambda (buffer alist)
|
||||
(+popup-display-buffer-stacked-side-window-fn
|
||||
buffer (append '((vslot . -9999)) alist)))))
|
||||
(which-key--show-buffer-side-window act-popup-dim))))))
|
||||
|
||||
|
||||
;;;###package windmove
|
||||
;; Users should be able to hop into popups easily, but Elisp shouldn't.
|
||||
(defadvice! +popup--ignore-window-parameters-a (orig-fn &rest args)
|
||||
"Allow *interactive* window moving commands to traverse popups."
|
||||
:around '(windmove-up windmove-down windmove-left windmove-right)
|
||||
(cl-letf (((symbol-function #'windmove-find-other-window)
|
||||
(lambda (dir &optional arg window)
|
||||
(window-in-direction
|
||||
(pcase dir (`up 'above) (`down 'below) (_ dir))
|
||||
window (bound-and-true-p +popup-mode) arg windmove-wrap-around t))))
|
||||
(apply orig-fn args)))
|
||||
140
.emacs.d/modules/ui/popup/README.org
Normal file
140
.emacs.d/modules/ui/popup/README.org
Normal file
@@ -0,0 +1,140 @@
|
||||
#+TITLE: ui/popup
|
||||
#+DATE: January 6, 2018
|
||||
#+SINCE: v2.0.9
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
- [[#configuration][Configuration]]
|
||||
- [[#set-popup-rule-and-set-popup-rules][~set-popup-rule!~ and ~set-popup-rules!~]]
|
||||
- [[#disabling-hidden-mode-line-in-popups][Disabling hidden mode-line in popups]]
|
||||
- [[#appendix][Appendix]]
|
||||
- [[#commands][Commands]]
|
||||
- [[#library][Library]]
|
||||
- [[#hacks][Hacks]]
|
||||
|
||||
* Description
|
||||
This module provides a customizable popup window management system.
|
||||
|
||||
Not all windows are created equally. Some are less important. Some I want gone
|
||||
once they have served their purpose, like code output or a help buffer. Others I
|
||||
want to stick around, like a scratch buffer or org-capture popup.
|
||||
|
||||
More than that, popups ought to be the second class citizens of my editor;
|
||||
spawned off to the side, discarded with the push of a button (e.g. =ESC= or
|
||||
=C-g=), and easily restored if I want to see them again. Of course, this system
|
||||
should clean up after itself and kill off buffers I mark as transient.
|
||||
|
||||
** Module Flags
|
||||
+ =+all= Enables fallback rules to ensure all temporary/special buffers (whose
|
||||
name begins with a space or asterix) are treated as popups.
|
||||
+ =+defaults= Enables reasonable default popup rules for a variety of buffers.
|
||||
|
||||
* Prerequisites
|
||||
This module has no external prerequisites.
|
||||
|
||||
* Configuration
|
||||
** ~set-popup-rule!~ and ~set-popup-rules!~
|
||||
This module has two functions for defining your own rules for popups:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(set-popup-rule! PREDICATE &key IGNORE ACTIONS SIDE SIZE WIDTH HEIGHT SLOT VSLOT TTL QUIT SELECT MODELINE AUTOSAVE PARAMETERS)
|
||||
(set-popup-rules! &rest RULESETS)
|
||||
#+END_SRC
|
||||
|
||||
~PREDICATE~ is a predicate function or regexp string to match against the
|
||||
buffer's name. You'll find comprehensive documentation on the other keywords in
|
||||
~set-popup-rule!~'s docstring (=SPC h f set-popup-rule!=).
|
||||
|
||||
#+begin_quote
|
||||
Popup rules end up in ~display-buffer-alist~, which instructs ~display-buffer~
|
||||
calls on how to set up windows for buffers that meet certain conditions.
|
||||
However, some plugins can avoid it entirely if they use ~set-buffer~ or
|
||||
~switch-to-buffer~, which don't obey ~display-buffer-alist~.
|
||||
#+end_quote
|
||||
|
||||
Multiple popup rules can be defined with ~set-popup-rules!~:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(set-popup-rules!
|
||||
'(("^ \\*" :slot -1) ; fallback rule for special buffers
|
||||
("^\\*" :select t)
|
||||
("^\\*Completions" :slot -1 :ttl 0)
|
||||
("^\\*\\(?:scratch\\|Messages\\)" :ttl t)
|
||||
("^\\*Help" :slot -1 :size 0.2 :select t)
|
||||
("^\\*doom:"
|
||||
:size 0.35 :select t :modeline t :quit t :ttl t)))
|
||||
#+END_SRC
|
||||
|
||||
Omitted parameters in a ~set-popup-rules!~ will use the defaults set in
|
||||
~+popup-defaults~.
|
||||
|
||||
** Disabling hidden mode-line in popups
|
||||
By default, the mode-line is hidden in popups. To disable this, you can either:
|
||||
|
||||
1. Change the default ~:modeline~ property in ~+popup-defaults~:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;; add to $DOOMDIR/config.el
|
||||
(plist-put +popup-defaults :modeline t)
|
||||
#+END_SRC
|
||||
|
||||
A value of ~t~ will instruct popups to use the default mode-line. Any
|
||||
popup rule with a ~:modeline~ property can still override this.
|
||||
|
||||
2. Completely disable management of the mode-line in popups:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
;; add to ~/.doom.d/config.el
|
||||
(remove-hook '+popup-buffer-mode-hook #'+popup-set-modeline-on-enable-h)
|
||||
#+END_SRC
|
||||
|
||||
* Appendix
|
||||
** Commands
|
||||
+ ~+popup/other~ (aliased to ~other-popup~, bound to ~C-x p~)
|
||||
+ ~+popup/toggle~
|
||||
+ ~+popup/close~
|
||||
+ ~+popup/close-all~
|
||||
+ ~+popup/toggle~
|
||||
+ ~+popup/restore~
|
||||
+ ~+popup/raise~
|
||||
** Library
|
||||
+ Functions
|
||||
+ ~+popup-window-p WINDOW~
|
||||
+ ~+popup-buffer-p BUFFER~
|
||||
+ ~+popup-buffer BUFFER &optional ALIST~
|
||||
+ ~+popup-parameter PARAMETER &optional WINDOW~
|
||||
+ ~+popup-parameter-fn PARAMETER &optional WINDOW~
|
||||
+ ~+popup-windows~
|
||||
+ Macros
|
||||
+ ~without-popups!~
|
||||
+ ~save-popups!~
|
||||
+ Hooks
|
||||
+ ~+popup-adjust-fringes-h~
|
||||
+ ~+popup|set-modeline~
|
||||
+ ~+popup-close-on-escape-h~
|
||||
+ ~+popup-cleanup-rules-h~
|
||||
+ Minor modes
|
||||
+ ~+popup-mode~
|
||||
+ ~+popup-buffer-mode~
|
||||
** Hacks
|
||||
+ =help-mode= has been advised to follow file links in the buffer you were in
|
||||
before entering the popup, rather than in a new window.
|
||||
+ =wgrep= buffers are advised to close themselves when aborting or committing
|
||||
changes.
|
||||
+ =persp-mode= is advised to restore popup windows when loading a session from
|
||||
file.
|
||||
+ Interactive calls to ~windmove-*~ commands (used by ~evil-window-*~ commands)
|
||||
will ignore the ~no-other-window~ window parameter, allowing you to switch to
|
||||
popup windows as if they're ordinary windows.
|
||||
+ ~balance-windows~ has been advised to close popups while it does its business,
|
||||
then restore them afterwards.
|
||||
+ =neotree= advises ~balance-windows~, which causes major slow-downs when paired
|
||||
with our ~balance-window~ advice, so we removes neotree's advice.
|
||||
+ =org-mode= is an ongoing (and huge) effort. It has a scorched-earth window
|
||||
management system I'm not fond of. ie. it kills all windows and monopolizes
|
||||
the frame. On top of that, it /really/ likes to use ~switch-to-buffer~ for
|
||||
most of its buffer management, which completely bypasses
|
||||
~display-buffer-alist~. Some work has gone into reversing this.
|
||||
617
.emacs.d/modules/ui/popup/autoload/popup.el
Normal file
617
.emacs.d/modules/ui/popup/autoload/popup.el
Normal file
@@ -0,0 +1,617 @@
|
||||
;;; ui/popup/autoload/popup.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +popup--internal nil)
|
||||
|
||||
(defun +popup--remember (windows)
|
||||
"Remember WINDOWS (a list of windows) for later restoration."
|
||||
(cl-assert (cl-every #'windowp windows) t)
|
||||
(setq +popup--last
|
||||
(cl-loop for w in windows
|
||||
collect (cons (window-buffer w)
|
||||
(window-state-get w)))))
|
||||
|
||||
(defun +popup--kill-buffer (buffer ttl)
|
||||
"Tries to kill BUFFER, as was requested by a transient timer. If it fails, eg.
|
||||
the buffer is visible, then set another timer and try again later."
|
||||
(when (buffer-live-p buffer)
|
||||
(let ((inhibit-quit t)
|
||||
(kill-buffer-hook (remq '+popup-kill-buffer-hook-h kill-buffer-hook)))
|
||||
(cond ((get-buffer-window buffer t)
|
||||
(with-current-buffer buffer
|
||||
(setq +popup--timer
|
||||
(run-at-time ttl nil #'+popup--kill-buffer buffer ttl))))
|
||||
((eq ttl 0)
|
||||
(kill-buffer buffer))
|
||||
((with-demoted-errors "Error killing transient buffer: %s"
|
||||
(with-current-buffer buffer
|
||||
(let (confirm-kill-processes)
|
||||
(when-let (process (get-buffer-process buffer))
|
||||
(kill-process process))
|
||||
(let (kill-buffer-query-functions)
|
||||
;; HACK The debugger backtrace buffer, when killed, called
|
||||
;; `top-level'. This causes jumpiness when the popup
|
||||
;; manager tries to clean it up.
|
||||
(cl-letf (((symbol-function #'top-level) #'ignore))
|
||||
(kill-buffer buffer)))))))))))
|
||||
|
||||
(defun +popup--delete-window (window)
|
||||
"Do housekeeping before destroying a popup window.
|
||||
|
||||
+ Disables `+popup-buffer-mode' so that any hooks attached to it get a chance to
|
||||
run and do cleanup of its own.
|
||||
+ Either kills the buffer or sets a transient timer, if the window has a
|
||||
`transient' window parameter (see `+popup-window-parameters').
|
||||
+ And finally deletes the window!"
|
||||
(let ((buffer (window-buffer window))
|
||||
(inhibit-quit t))
|
||||
(and (buffer-file-name buffer)
|
||||
(buffer-modified-p buffer)
|
||||
(let ((autosave (+popup-parameter 'autosave window)))
|
||||
(cond ((eq autosave 't))
|
||||
((null autosave)
|
||||
(y-or-n-p "Popup buffer is modified. Save it?"))
|
||||
((functionp autosave)
|
||||
(funcall autosave buffer))))
|
||||
(with-current-buffer buffer (save-buffer)))
|
||||
(let ((ignore-window-parameters t))
|
||||
(if-let (wconf (window-parameter window 'saved-wconf))
|
||||
(set-window-configuration wconf)
|
||||
(delete-window window)))
|
||||
(unless (window-live-p window)
|
||||
(with-current-buffer buffer
|
||||
(set-buffer-modified-p nil)
|
||||
(+popup-buffer-mode -1)
|
||||
(unless +popup--inhibit-transient
|
||||
(let ((ttl (+popup-parameter 'ttl window)))
|
||||
(when (eq ttl 't)
|
||||
(setq ttl (plist-get +popup-defaults :ttl)))
|
||||
(cond ((null ttl))
|
||||
((functionp ttl)
|
||||
(funcall ttl buffer))
|
||||
((not (integerp ttl))
|
||||
(signal 'wrong-type-argument (list 'integerp ttl)))
|
||||
((= ttl 0)
|
||||
(+popup--kill-buffer buffer 0))
|
||||
((add-hook 'kill-buffer-hook #'+popup-kill-buffer-hook-h nil t)
|
||||
(setq +popup--timer
|
||||
(run-at-time ttl nil #'+popup--kill-buffer
|
||||
buffer ttl))))))))))
|
||||
|
||||
(defun +popup--delete-other-windows (window)
|
||||
"Fixes `delete-other-windows' when used from a popup window."
|
||||
(when-let (window (ignore-errors (+popup/raise window)))
|
||||
(let ((ignore-window-parameters t))
|
||||
(delete-other-windows window)))
|
||||
nil)
|
||||
|
||||
(defun +popup--normalize-alist (alist)
|
||||
"Merge `+popup-default-alist' and `+popup-default-parameters' with ALIST."
|
||||
(when alist
|
||||
(let ((alist ; handle defaults
|
||||
(cl-remove-duplicates
|
||||
(append alist +popup-default-alist)
|
||||
:key #'car-safe :from-end t))
|
||||
(parameters
|
||||
(cl-remove-duplicates
|
||||
(append (cdr (assq 'window-parameters alist))
|
||||
+popup-default-parameters)
|
||||
:key #'car-safe :from-end t)))
|
||||
;; handle `size'
|
||||
(when-let* ((size (cdr (assq 'size alist)))
|
||||
(side (or (cdr (assq 'side alist)) 'bottom))
|
||||
(param (if (memq side '(left right))
|
||||
'window-width
|
||||
'window-height)))
|
||||
(setq list (assq-delete-all 'size alist))
|
||||
(setf (alist-get param alist) size))
|
||||
(setf (alist-get 'window-parameters alist)
|
||||
parameters)
|
||||
;; Fixes #1305: addresses an edge case where a popup with a :size, :width
|
||||
;; or :height greater than the current frame's dimensions causes
|
||||
;; hanging/freezing (a bug in Emacs' `display-buffer' API perhaps?)
|
||||
(let ((width (cdr (assq 'window-width alist)))
|
||||
(height (cdr (assq 'window-height alist))))
|
||||
(setf (alist-get 'window-width alist)
|
||||
(if (numberp width)
|
||||
(min width (frame-width))
|
||||
width))
|
||||
(setf (alist-get 'window-height alist)
|
||||
(if (numberp height)
|
||||
(min height (frame-height))
|
||||
height))
|
||||
alist))))
|
||||
|
||||
(defun +popup--split-window (window size side)
|
||||
"Ensure a non-dedicated/popup window is selected when splitting a window."
|
||||
(unless +popup--internal
|
||||
(cl-loop for win
|
||||
in (cons (or window (selected-window))
|
||||
(window-list nil 0 window))
|
||||
unless (+popup-window-p win)
|
||||
return (setq window win)))
|
||||
(let ((ignore-window-parameters t))
|
||||
(split-window window size side)))
|
||||
|
||||
(defun +popup--maybe-select-window (window origin)
|
||||
"Select a window based on `+popup--inhibit-select' and this window's `select' parameter."
|
||||
(unless +popup--inhibit-select
|
||||
(let ((select (+popup-parameter 'select window)))
|
||||
(if (functionp select)
|
||||
(funcall select window origin)
|
||||
(select-window (if select window origin))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup--init (window &optional alist)
|
||||
"Initializes a popup window. Run any time a popup is opened. It sets the
|
||||
default window parameters for popup windows, clears leftover transient timers
|
||||
and enables `+popup-buffer-mode'."
|
||||
(with-selected-window window
|
||||
(setq alist (delq (assq 'actions alist) alist))
|
||||
(set-window-parameter window 'popup t)
|
||||
(set-window-parameter window 'split-window #'+popup--split-window)
|
||||
(set-window-parameter window 'delete-window #'+popup--delete-window)
|
||||
(set-window-parameter window 'delete-other-windows #'+popup--delete-other-windows)
|
||||
(set-window-dedicated-p window 'popup)
|
||||
(window-preserve-size
|
||||
window (memq (window-parameter window 'window-side)
|
||||
'(left right))
|
||||
t)
|
||||
(+popup-buffer-mode +1)
|
||||
(run-hooks '+popup-create-window-hook)))
|
||||
|
||||
|
||||
;;
|
||||
;; Public library
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-buffer-p (&optional buffer)
|
||||
"Return non-nil if BUFFER is a popup buffer. Defaults to the current buffer."
|
||||
(when +popup-mode
|
||||
(let ((buffer (or buffer (current-buffer))))
|
||||
(and (bufferp buffer)
|
||||
(buffer-live-p buffer)
|
||||
(buffer-local-value '+popup-buffer-mode buffer)
|
||||
buffer))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-window-p (&optional window)
|
||||
"Return non-nil if WINDOW is a popup window. Defaults to the current window."
|
||||
(when +popup-mode
|
||||
(let ((window (or window (selected-window))))
|
||||
(and (windowp window)
|
||||
(window-live-p window)
|
||||
(or (window-parameter window 'popup)
|
||||
(window-parameter window 'no-other-window))
|
||||
window))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-buffer (buffer &optional alist)
|
||||
"Open BUFFER in a popup window. ALIST describes its features."
|
||||
(let* ((origin (selected-window))
|
||||
(window-min-height 3)
|
||||
(alist (+popup--normalize-alist alist))
|
||||
(actions (or (cdr (assq 'actions alist))
|
||||
+popup-default-display-buffer-actions)))
|
||||
(or (let* ((alist (remove (assq 'window-width alist) alist))
|
||||
(alist (remove (assq 'window-height alist) alist))
|
||||
(window (display-buffer-reuse-window buffer alist)))
|
||||
(when window
|
||||
(+popup--maybe-select-window window origin)
|
||||
window))
|
||||
(when-let (popup (cl-loop for func in actions
|
||||
if (funcall func buffer alist)
|
||||
return it))
|
||||
(+popup--init popup alist)
|
||||
(+popup--maybe-select-window popup origin)
|
||||
popup))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-parameter (parameter &optional window)
|
||||
"Fetch the window PARAMETER (symbol) of WINDOW"
|
||||
(window-parameter (or window (selected-window)) parameter))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-parameter-fn (parameter &optional window &rest args)
|
||||
"Fetch the window PARAMETER (symbol) of WINDOW. If it is a function, run it
|
||||
with ARGS to get its return value."
|
||||
(let ((val (+popup-parameter parameter window)))
|
||||
(if (functionp val)
|
||||
(apply val args)
|
||||
val)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-windows ()
|
||||
"Returns a list of all popup windows."
|
||||
(cl-remove-if-not #'+popup-window-p (window-list)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-shrink-to-fit (&optional window)
|
||||
"Shrinks WINDOW to fit the buffer contents, if the buffer isn't empty.
|
||||
|
||||
Uses `shrink-window-if-larger-than-buffer'."
|
||||
(unless window
|
||||
(setq window (selected-window)))
|
||||
(unless (= (- (point-max) (point-min)) 0)
|
||||
(shrink-window-if-larger-than-buffer window)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-alist-from-window-state (state)
|
||||
"Convert window STATE (from `window-state-get') to a `display-buffer' alist."
|
||||
(let* ((params (alist-get 'parameters state)))
|
||||
`((side . ,(alist-get 'window-side params))
|
||||
(window-width . ,(alist-get 'total-width state))
|
||||
(window-height . ,(alist-get 'total-height state))
|
||||
(window-parameters ,@params))))
|
||||
|
||||
|
||||
;;
|
||||
;; Hooks
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-adjust-fringes-h ()
|
||||
"Hides the fringe in popup windows, restoring them if `+popup-buffer-mode' is
|
||||
disabled."
|
||||
(let ((f (if (bound-and-true-p +popup-buffer-mode) 0)))
|
||||
(set-window-fringes nil f f fringes-outside-margins)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-adjust-margins-h ()
|
||||
"Creates padding for the popup window determined by `+popup-margin-width',
|
||||
restoring it if `+popup-buffer-mode' is disabled."
|
||||
(when +popup-margin-width
|
||||
(unless (memq (window-parameter nil 'window-side) '(left right))
|
||||
(let ((m (if (bound-and-true-p +popup-buffer-mode) +popup-margin-width)))
|
||||
(set-window-margins nil m m)))))
|
||||
|
||||
(defvar hide-mode-line-format)
|
||||
;;;###autoload
|
||||
(defun +popup-set-modeline-on-enable-h ()
|
||||
"Don't show modeline in popup windows without a `modeline' window-parameter.
|
||||
Possible values for this parameter are:
|
||||
|
||||
t show the mode-line as normal
|
||||
nil hide the modeline entirely (the default)
|
||||
a function `mode-line-format' is set to its return value
|
||||
|
||||
Any non-nil value besides the above will be used as the raw value for
|
||||
`mode-line-format'."
|
||||
(when (bound-and-true-p +popup-buffer-mode)
|
||||
(let ((modeline (+popup-parameter 'modeline)))
|
||||
(cond ((eq modeline 't))
|
||||
((null modeline)
|
||||
;; TODO use `mode-line-format' window parameter instead (emacs 26+)
|
||||
(hide-mode-line-mode +1))
|
||||
((let ((hide-mode-line-format
|
||||
(if (functionp modeline)
|
||||
(funcall modeline)
|
||||
modeline)))
|
||||
(hide-mode-line-mode +1)))))))
|
||||
(put '+popup-set-modeline-on-enable-h 'permanent-local-hook t)
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-unset-modeline-on-disable-h ()
|
||||
"Restore the modeline when `+popup-buffer-mode' is deactivated."
|
||||
(when (and (not (bound-and-true-p +popup-buffer-mode))
|
||||
(bound-and-true-p hide-mode-line-mode))
|
||||
(hide-mode-line-mode -1)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-close-on-escape-h ()
|
||||
"If called inside a popup, try to close that popup window (see
|
||||
`+popup/close'). If called outside, try to close all popup windows (see
|
||||
`+popup/close-all')."
|
||||
(if (+popup-window-p)
|
||||
(+popup/close)
|
||||
(+popup/close-all)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-cleanup-rules-h ()
|
||||
"Cleans up any duplicate popup rules."
|
||||
(interactive)
|
||||
(setq +popup--display-buffer-alist
|
||||
(cl-delete-duplicates +popup--display-buffer-alist
|
||||
:key #'car :test #'equal :from-end t))
|
||||
(when +popup-mode
|
||||
(setq display-buffer-alist +popup--display-buffer-alist)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-kill-buffer-hook-h ()
|
||||
"TODO"
|
||||
(when-let (window (get-buffer-window))
|
||||
(when (+popup-window-p window)
|
||||
(let ((+popup--inhibit-transient t))
|
||||
(+popup--delete-window window)))))
|
||||
|
||||
|
||||
;;
|
||||
;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defalias 'other-popup #'+popup/other)
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup/buffer ()
|
||||
"Open this buffer in a popup window."
|
||||
(interactive)
|
||||
(let ((+popup-default-display-buffer-actions
|
||||
'(+popup-display-buffer-stacked-side-window-fn))
|
||||
(display-buffer-alist +popup--display-buffer-alist)
|
||||
(buffer (current-buffer)))
|
||||
(push (+popup-make-rule "." +popup-defaults) display-buffer-alist)
|
||||
(bury-buffer)
|
||||
(pop-to-buffer buffer)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup/other ()
|
||||
"Cycle through popup windows, like `other-window'. Ignores regular windows."
|
||||
(interactive)
|
||||
(if-let (popups (+popup-windows))
|
||||
(select-window (if (+popup-window-p)
|
||||
(let ((window (selected-window)))
|
||||
(or (car-safe (cdr (memq window popups)))
|
||||
(car (delq window popups))
|
||||
(car popups)))
|
||||
(car popups)))
|
||||
(user-error "No popups are open")))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup/close (&optional window force-p)
|
||||
"Close WINDOW, if it's a popup window.
|
||||
|
||||
This will do nothing if the popup's `quit' window parameter is either nil or
|
||||
'other. This window parameter is ignored if FORCE-P is non-nil."
|
||||
(interactive
|
||||
(list (selected-window)
|
||||
current-prefix-arg))
|
||||
(let ((window (or window (selected-window))))
|
||||
(when (and (+popup-window-p window)
|
||||
(or force-p
|
||||
(memq (+popup-parameter-fn 'quit window window)
|
||||
'(t current))))
|
||||
(when +popup--remember-last
|
||||
(+popup--remember (list window)))
|
||||
(delete-window window)
|
||||
t)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup/close-all (&optional force-p)
|
||||
"Close all open popup windows.
|
||||
|
||||
This will ignore popups with an `quit' parameter that is either nil or 'current.
|
||||
This window parameter is ignored if FORCE-P is non-nil."
|
||||
(interactive "P")
|
||||
(let (targets +popup--remember-last)
|
||||
(dolist (window (+popup-windows))
|
||||
(when (or force-p
|
||||
(memq (+popup-parameter-fn 'quit window window)
|
||||
'(t other)))
|
||||
(push window targets)))
|
||||
(when targets
|
||||
(+popup--remember targets)
|
||||
(mapc #'delete-window targets)
|
||||
t)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup/toggle ()
|
||||
"If popups are open, close them. If they aren't, restore the last one or open
|
||||
the message buffer in a popup window."
|
||||
(interactive)
|
||||
(let ((+popup--inhibit-transient t))
|
||||
(cond ((+popup-windows) (+popup/close-all t))
|
||||
((ignore-errors (+popup/restore)))
|
||||
((display-buffer (get-buffer "*Messages*"))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup/restore ()
|
||||
"Restore the last popups that were closed, if any."
|
||||
(interactive)
|
||||
(unless +popup--last
|
||||
(error "No popups to restore"))
|
||||
(cl-loop for (buffer . state) in +popup--last
|
||||
if (buffer-live-p buffer)
|
||||
do (+popup-buffer buffer (+popup-alist-from-window-state state)))
|
||||
(setq +popup--last nil)
|
||||
t)
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup/raise (window &optional arg)
|
||||
"Raise the current popup window into a regular window.
|
||||
If prefix ARG, raise the current popup into a new window."
|
||||
(interactive
|
||||
(list (selected-window) current-prefix-arg))
|
||||
(cl-check-type window window)
|
||||
(unless (+popup-window-p window)
|
||||
(user-error "Cannot raise a non-popup window"))
|
||||
(let ((buffer (current-buffer))
|
||||
(+popup--inhibit-transient t)
|
||||
+popup--remember-last)
|
||||
(+popup/close window 'force)
|
||||
(if arg
|
||||
(pop-to-buffer buffer)
|
||||
(switch-to-buffer buffer))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup/diagnose ()
|
||||
"Reveal what popup rule will be used for the current buffer."
|
||||
(interactive)
|
||||
(or (cl-loop with bname = (buffer-name)
|
||||
for (pred . action) in display-buffer-alist
|
||||
if (and (functionp pred) (funcall pred bname action))
|
||||
return (cons pred action)
|
||||
else if (and (stringp pred) (string-match-p pred bname))
|
||||
return (cons pred action))
|
||||
(message "No popup rule for this buffer")))
|
||||
|
||||
|
||||
;;
|
||||
;;; Advice
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-close-a (&rest _)
|
||||
"TODO"
|
||||
(+popup/close nil t))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-save-a (orig-fn &rest args)
|
||||
"Sets aside all popups before executing the original function, usually to
|
||||
prevent the popup(s) from messing up the UI (or vice versa)."
|
||||
(save-popups! (apply orig-fn args)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-display-buffer-fullframe-fn (buffer alist)
|
||||
"Displays the buffer fullscreen."
|
||||
(let ((wconf (current-window-configuration)))
|
||||
(when-let (window (or (display-buffer-reuse-window buffer alist)
|
||||
(display-buffer-same-window buffer alist)
|
||||
(display-buffer-pop-up-window buffer alist)
|
||||
(display-buffer-use-some-window buffer alist)))
|
||||
(set-window-parameter window 'saved-wconf wconf)
|
||||
(add-to-list 'window-persistent-parameters '(saved-wconf . t))
|
||||
(delete-other-windows window)
|
||||
window)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-display-buffer-stacked-side-window-fn (buffer alist)
|
||||
"A `display-buffer' action that serves as an alternative to
|
||||
`display-buffer-in-side-window', but allows for stacking popups with the `vslot'
|
||||
alist entry.
|
||||
|
||||
Accepts the same arguments as `display-buffer-in-side-window'. You must set
|
||||
`window--sides-inhibit-check' to non-nil for this work properly."
|
||||
(let* ((side (or (cdr (assq 'side alist)) 'bottom))
|
||||
(slot (or (cdr (assq 'slot alist)) 0))
|
||||
(vslot (or (cdr (assq 'vslot alist)) 0))
|
||||
(left-or-right (memq side '(left right)))
|
||||
(display-buffer-mark-dedicated (or display-buffer-mark-dedicated 'popup)))
|
||||
|
||||
(cond ((not (memq side '(top bottom left right)))
|
||||
(error "Invalid side %s specified" side))
|
||||
((not (numberp slot))
|
||||
(error "Invalid slot %s specified" slot))
|
||||
((not (numberp vslot))
|
||||
(error "Invalid vslot %s specified" vslot)))
|
||||
|
||||
(let* ((major (get-window-with-predicate
|
||||
(lambda (window)
|
||||
(and (eq (window-parameter window 'window-side) side)
|
||||
(eq (window-parameter window 'window-vslot) vslot)))
|
||||
nil))
|
||||
(reversed (window--sides-reverse-on-frame-p (selected-frame)))
|
||||
(windows
|
||||
(cond ((window-live-p major)
|
||||
(list major))
|
||||
((window-valid-p major)
|
||||
(let* ((first (window-child major))
|
||||
(next (window-next-sibling first))
|
||||
(windows (list next first)))
|
||||
(setq reversed (> (window-parameter first 'window-slot)
|
||||
(window-parameter next 'window-slot)))
|
||||
(while (setq next (window-next-sibling next))
|
||||
(setq windows (cons next windows)))
|
||||
(if reversed windows (nreverse windows))))))
|
||||
(slots (if major (max 1 (window-child-count major))))
|
||||
(max-slots
|
||||
(nth (plist-get '(left 0 top 1 right 2 bottom 3) side)
|
||||
window-sides-slots))
|
||||
(window--sides-inhibit-check t)
|
||||
window this-window this-slot prev-window next-window
|
||||
best-window best-slot abs-slot)
|
||||
|
||||
(cond ((and (numberp max-slots) (<= max-slots 0))
|
||||
nil)
|
||||
((not windows)
|
||||
(cl-letf (((symbol-function 'window--make-major-side-window-next-to)
|
||||
(lambda (_side) (frame-root-window (selected-frame)))))
|
||||
(when-let (window (window--make-major-side-window buffer side slot alist))
|
||||
(set-window-parameter window 'window-vslot vslot)
|
||||
(add-to-list 'window-persistent-parameters '(window-vslot . writable))
|
||||
window)))
|
||||
(t
|
||||
;; Scan windows on SIDE.
|
||||
(catch 'found
|
||||
(dolist (window windows)
|
||||
(setq this-slot (window-parameter window 'window-slot))
|
||||
(cond ((not (numberp this-slot)))
|
||||
((= this-slot slot) ; A window with a matching slot found
|
||||
(setq this-window window)
|
||||
(throw 'found t))
|
||||
(t
|
||||
;; Check if this window has a better slot value wrt the
|
||||
;; slot of the window we want.
|
||||
(setq abs-slot
|
||||
(if (or (and (> this-slot 0) (> slot 0))
|
||||
(and (< this-slot 0) (< slot 0)))
|
||||
(abs (- slot this-slot))
|
||||
(+ (abs slot) (abs this-slot))))
|
||||
(unless (and best-slot (<= best-slot abs-slot))
|
||||
(setq best-window window)
|
||||
(setq best-slot abs-slot))
|
||||
(if reversed
|
||||
(cond
|
||||
((<= this-slot slot)
|
||||
(setq next-window window))
|
||||
((not prev-window)
|
||||
(setq prev-window window)))
|
||||
(cond
|
||||
((<= this-slot slot)
|
||||
(setq prev-window window))
|
||||
((not next-window)
|
||||
(setq next-window window))))))))
|
||||
|
||||
;; `this-window' is the first window with the same SLOT.
|
||||
;; `prev-window' is the window with the largest slot < SLOT. A new
|
||||
;; window will be created after it.
|
||||
;; `next-window' is the window with the smallest slot > SLOT. A new
|
||||
;; window will be created before it.
|
||||
;; `best-window' is the window with the smallest absolute
|
||||
;; difference of its slot and SLOT.
|
||||
(or (and this-window
|
||||
;; Reuse `this-window'.
|
||||
(with-current-buffer buffer
|
||||
(setq window--sides-shown t))
|
||||
(window--display-buffer
|
||||
buffer this-window 'reuse alist))
|
||||
(and (or (not max-slots) (< slots max-slots))
|
||||
(or (and next-window
|
||||
;; Make new window before `next-window'.
|
||||
(let ((next-side (if left-or-right 'above 'left))
|
||||
(+popup--internal t)
|
||||
(window-combination-resize 'side))
|
||||
(setq window
|
||||
(ignore-errors (split-window next-window nil next-side)))))
|
||||
(and prev-window
|
||||
;; Make new window after `prev-window'.
|
||||
(let ((prev-side (if left-or-right 'below 'right))
|
||||
(+popup--internal t)
|
||||
(window-combination-resize 'side))
|
||||
(setq window
|
||||
(ignore-errors (split-window prev-window nil prev-side))))))
|
||||
(set-window-parameter window 'window-slot slot)
|
||||
(with-current-buffer buffer
|
||||
(setq window--sides-shown t))
|
||||
(window--display-buffer
|
||||
buffer window 'window alist))
|
||||
(and best-window
|
||||
;; Reuse `best-window'.
|
||||
(progn
|
||||
;; Give best-window the new slot value.
|
||||
(set-window-parameter best-window 'window-slot slot)
|
||||
(with-current-buffer buffer
|
||||
(setq window--sides-shown t))
|
||||
(window--display-buffer
|
||||
buffer best-window 'reuse alist)))))))))
|
||||
|
||||
|
||||
;;
|
||||
;; Emacs backwards compatibility
|
||||
|
||||
(unless EMACS27+
|
||||
(defadvice! +popup--set-window-dedicated-a (window)
|
||||
"Ensure `window--display-buffer' respects `display-buffer-mark-dedicated'.
|
||||
|
||||
This was not so until recent Emacs 27 builds, where it causes breaking errors.
|
||||
This advice ensures backwards compatibility for Emacs <= 26 users."
|
||||
:filter-return #'window--display-buffer
|
||||
(when (and (windowp window) display-buffer-mark-dedicated)
|
||||
(set-window-dedicated-p window display-buffer-mark-dedicated))
|
||||
window))
|
||||
190
.emacs.d/modules/ui/popup/autoload/settings.el
Normal file
190
.emacs.d/modules/ui/popup/autoload/settings.el
Normal file
@@ -0,0 +1,190 @@
|
||||
;;; ui/popup/autoload/settings.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defvar +popup--display-buffer-alist nil)
|
||||
|
||||
;;;###autoload
|
||||
(defvar +popup-defaults
|
||||
(list :side 'bottom
|
||||
:height 0.16
|
||||
:width 40
|
||||
:quit t
|
||||
:select #'ignore
|
||||
:ttl 5)
|
||||
"Default properties for popup rules defined with `set-popup-rule!'.")
|
||||
|
||||
;;;###autoload
|
||||
(defun +popup-make-rule (predicate plist)
|
||||
(if (plist-get plist :ignore)
|
||||
(list predicate nil)
|
||||
(let* ((plist (append plist +popup-defaults))
|
||||
(alist
|
||||
`((actions . ,(plist-get plist :actions))
|
||||
(side . ,(plist-get plist :side))
|
||||
(size . ,(plist-get plist :size))
|
||||
(window-width . ,(plist-get plist :width))
|
||||
(window-height . ,(plist-get plist :height))
|
||||
(slot . ,(plist-get plist :slot))
|
||||
(vslot . ,(plist-get plist :vslot))))
|
||||
(params
|
||||
`((ttl . ,(plist-get plist :ttl))
|
||||
(quit . ,(plist-get plist :quit))
|
||||
(select . ,(plist-get plist :select))
|
||||
(modeline . ,(plist-get plist :modeline))
|
||||
(autosave . ,(plist-get plist :autosave))
|
||||
,@(plist-get plist :parameters))))
|
||||
`(,predicate (+popup-buffer)
|
||||
,@alist
|
||||
(window-parameters ,@params)))))
|
||||
|
||||
;;;###autodef
|
||||
(defun set-popup-rule! (predicate &rest plist)
|
||||
"Define a popup rule.
|
||||
|
||||
These rules affect buffers displayed with `pop-to-buffer' and `display-buffer'
|
||||
(or their siblings). Buffers displayed with `switch-to-buffer' (and its
|
||||
variants) will not be affected by these rules (as they are unaffected by
|
||||
`display-buffer-alist', which powers the popup management system).
|
||||
|
||||
PREDICATE can be either a) a regexp string (matched against the buffer's name)
|
||||
or b) a function that takes two arguments (a buffer name and the ACTION argument
|
||||
of `display-buffer') and returns a boolean.
|
||||
|
||||
PLIST can be made up of any of the following properties:
|
||||
|
||||
:ignore BOOL
|
||||
If BOOL is non-nil, popups matching PREDICATE will not be handled by the popup
|
||||
system. Use this for buffers that have their own window management system like
|
||||
magit or helm.
|
||||
|
||||
:actions ACTIONS
|
||||
ACTIONS is a list of functions or an alist containing (FUNCTION . ALIST). See
|
||||
`display-buffer''s second argument for more information on its format and what
|
||||
it accepts. If omitted, `+popup-default-display-buffer-actions' is used.
|
||||
|
||||
:side 'bottom|'top|'left|'right
|
||||
Which side of the frame to open the popup on. This is only respected if
|
||||
`+popup-display-buffer-stacked-side-window-fn' or `display-buffer-in-side-window'
|
||||
is in :actions or `+popup-default-display-buffer-actions'.
|
||||
|
||||
:size/:width/:height FLOAT|INT|FN
|
||||
Determines the size of the popup. If more tha one of these size properties are
|
||||
given :size always takes precedence, and is mapped with window-width or
|
||||
window-height depending on what :side the popup is opened. Setting a height
|
||||
for a popup that opens on the left or right is harmless, but comes into play
|
||||
if two popups occupy the same :vslot.
|
||||
|
||||
If a FLOAT (0 < x < 1), the number represents how much of the window will be
|
||||
consumed by the popup (a percentage).
|
||||
If an INT, the number determines the size in lines (height) or units of
|
||||
character width (width).
|
||||
If a function, it takes one argument: the popup window, and can do whatever it
|
||||
wants with it, typically resize it, like `+popup-shrink-to-fit'.
|
||||
|
||||
:slot/:vslot INT
|
||||
(This only applies to popups with a :side and only if :actions is blank or
|
||||
contains the `+popup-display-buffer-stacked-side-window-fn' action) These control
|
||||
how multiple popups are laid out. INT can be any integer, positive and
|
||||
negative.
|
||||
|
||||
:slot controls lateral positioning (e.g. the horizontal positioning for
|
||||
top/bottom popups, or vertical positioning for left/right popups).
|
||||
:vslot controls popup stacking (from the edge of the frame toward the center).
|
||||
|
||||
Let's assume popup A and B are opened with :side 'bottom, in that order.
|
||||
If they possess the same :slot and :vslot, popup B will replace popup A.
|
||||
If popup B has a higher :slot, it will open to the right of popup A.
|
||||
If popup B has a lower :slot, it will open to the left of popup A.
|
||||
If popup B has a higher :vslot, it will open above popup A.
|
||||
If popup B has a lower :vslot, it will open below popup A.
|
||||
|
||||
:ttl INT|BOOL|FN
|
||||
Stands for time-to-live. It can be t, an integer, nil or a function. This
|
||||
controls how (and if) the popup system will clean up after the popup.
|
||||
|
||||
If any non-zero integer, wait that many seconds before killing the buffer (and
|
||||
any associated processes).
|
||||
If 0, the buffer is immediately killed.
|
||||
If nil, the buffer won't be killed and is left to its own devices.
|
||||
If t, resort to the default :ttl in `+popup-defaults'. If none exists, this is
|
||||
the same as nil.
|
||||
If a function, it takes one argument: the target popup buffer. The popup
|
||||
system does nothing else and ignores the function's return value.
|
||||
|
||||
:quit FN|BOOL|'other|'current
|
||||
Can be t, 'other, 'current, nil, or a function. This determines the behavior
|
||||
of the ESC/C-g keys in or outside of popup windows.
|
||||
|
||||
If t, close the popup if ESC/C-g is pressed anywhere.
|
||||
If 'other, close this popup if ESC/C-g is pressed outside of any popup. This
|
||||
is great for popups you may press ESC/C-g a lot in.
|
||||
If 'current, close the current popup if ESC/C-g is pressed from inside of the
|
||||
popup. This makes it harder to accidentally close a popup until you really
|
||||
want to.
|
||||
If nil, pressing ESC/C-g will never close this popup.
|
||||
If a function, it takes one argument: the to-be-closed popup window, and is
|
||||
run when ESC/C-g is pressed while that popup is open. It must return one of
|
||||
the other values to determine the fate of the popup.
|
||||
|
||||
:select BOOL|FN
|
||||
Can be a boolean or function. The boolean determines whether to focus the
|
||||
popup window after it opens (non-nil) or focus the origin window (nil).
|
||||
|
||||
If a function, it takes two arguments: the popup window and originating window
|
||||
(where you were before the popup opened). The popup system does nothing else
|
||||
and ignores the function's return value.
|
||||
|
||||
:modeline BOOL|FN|LIST
|
||||
Can be t (show the default modeline), nil (show no modeline), a function that
|
||||
returns a modeline format or a valid value for `mode-line-format' to be used
|
||||
verbatim. The function takes no arguments and is run in the context of the
|
||||
popup buffer.
|
||||
|
||||
:autosave BOOL|FN
|
||||
This parameter determines what to do with modified buffers when closing popup
|
||||
windows. It accepts t, 'ignore, a function or nil.
|
||||
|
||||
If t, no prompts. Just save them automatically (if they're file-visiting
|
||||
buffers). Same as 'ignore for non-file-visiting buffers.
|
||||
If nil (the default), prompt the user what to do if the buffer is
|
||||
file-visiting and modified.
|
||||
If 'ignore, no prompts, no saving. Just silently kill it.
|
||||
If a function, it is run with one argument: the popup buffer, and must return
|
||||
non-nil to save or nil to do nothing (but no prompts).
|
||||
|
||||
:parameters ALIST
|
||||
An alist of custom window parameters. See `(elisp)Window Parameters'.
|
||||
|
||||
If any of these are omitted, defaults derived from `+popup-defaults' will be
|
||||
used.
|
||||
|
||||
\(fn PREDICATE &key IGNORE ACTIONS SIDE SIZE WIDTH HEIGHT SLOT VSLOT TTL QUIT SELECT MODELINE AUTOSAVE PARAMETERS)"
|
||||
(declare (indent defun))
|
||||
(push (+popup-make-rule predicate plist) +popup--display-buffer-alist)
|
||||
(when (bound-and-true-p +popup-mode)
|
||||
(setq display-buffer-alist +popup--display-buffer-alist))
|
||||
+popup--display-buffer-alist)
|
||||
|
||||
;;;###autodef
|
||||
(defun set-popup-rules! (&rest rulesets)
|
||||
"Defines multiple popup rules.
|
||||
|
||||
Every entry in RULESETS should be a list of alists where the CAR is the
|
||||
predicate and CDR is a plist. See `set-popup-rule!' for details on the predicate
|
||||
and plist.
|
||||
|
||||
Example:
|
||||
|
||||
(set-popup-rules!
|
||||
'((\"^ \\*\" :slot 1 :vslot -1 :size #'+popup-shrink-to-fit)
|
||||
(\"^\\*\" :slot 1 :vslot -1 :select t))
|
||||
'((\"^\\*Completions\" :slot -1 :vslot -2 :ttl 0)
|
||||
(\"^\\*Compil\\(?:ation\\|e-Log\\)\" :size 0.3 :ttl 0 :quit t)))"
|
||||
(declare (indent 0))
|
||||
(dolist (rules rulesets)
|
||||
(dolist (rule rules)
|
||||
(push (+popup-make-rule (car rule) (cdr rule))
|
||||
+popup--display-buffer-alist)))
|
||||
(when (bound-and-true-p +popup-mode)
|
||||
(setq display-buffer-alist +popup--display-buffer-alist))
|
||||
+popup--display-buffer-alist)
|
||||
175
.emacs.d/modules/ui/popup/config.el
Normal file
175
.emacs.d/modules/ui/popup/config.el
Normal file
@@ -0,0 +1,175 @@
|
||||
;;; ui/popup/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defconst +popup-window-parameters '(ttl quit select modeline popup)
|
||||
"A list of custom parameters to be added to `window-persistent-parameters'.
|
||||
Modifying this has no effect, unless done before ui/popup loads.")
|
||||
|
||||
(defvar +popup-default-display-buffer-actions
|
||||
'(+popup-display-buffer-stacked-side-window-fn)
|
||||
"The functions to use to display the popup buffer.")
|
||||
|
||||
(defvar +popup-default-alist
|
||||
'((window-height . 0.16) ; remove later
|
||||
(reusable-frames . visible))
|
||||
"The default alist for `display-buffer-alist' rules.")
|
||||
|
||||
(defvar +popup-default-parameters
|
||||
'((transient . t) ; remove later
|
||||
(quit . t) ; remove later
|
||||
(select . ignore) ; remove later
|
||||
(no-other-window . t))
|
||||
"The default window parameters.")
|
||||
|
||||
(defvar +popup-margin-width 1
|
||||
"Size of the margins to give popup windows. Set this to nil to disable margin
|
||||
adjustment.")
|
||||
|
||||
(defvar +popup--inhibit-transient nil)
|
||||
(defvar +popup--inhibit-select nil)
|
||||
(defvar +popup--old-display-buffer-alist nil)
|
||||
(defvar +popup--remember-last t)
|
||||
(defvar +popup--last nil)
|
||||
(defvar-local +popup--timer nil)
|
||||
|
||||
|
||||
;;
|
||||
;; Global modes
|
||||
|
||||
(defvar +popup-mode-map (make-sparse-keymap)
|
||||
"Active keymap in a session with the popup system enabled. See
|
||||
`+popup-mode'.")
|
||||
|
||||
(defvar +popup-buffer-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(when (featurep! :editor evil)
|
||||
;; For maximum escape coverage in emacs state buffers; this only works in
|
||||
;; GUI Emacs, in tty Emacs use C-g instead
|
||||
(define-key map [escape] #'doom/escape))
|
||||
map)
|
||||
"Active keymap in popup windows. See `+popup-buffer-mode'.")
|
||||
|
||||
(define-minor-mode +popup-mode
|
||||
"Global minor mode representing Doom's popup management system."
|
||||
:init-value nil
|
||||
:global t
|
||||
:keymap +popup-mode-map
|
||||
(cond (+popup-mode
|
||||
(add-hook 'doom-escape-hook #'+popup-close-on-escape-h 'append)
|
||||
(setq +popup--old-display-buffer-alist display-buffer-alist
|
||||
display-buffer-alist +popup--display-buffer-alist
|
||||
window--sides-inhibit-check t)
|
||||
(dolist (prop +popup-window-parameters)
|
||||
(push (cons prop 'writable) window-persistent-parameters)))
|
||||
(t
|
||||
(remove-hook 'doom-escape-hook #'+popup-close-on-escape-h)
|
||||
(setq display-buffer-alist +popup--old-display-buffer-alist
|
||||
window--sides-inhibit-check nil)
|
||||
(+popup-cleanup-rules-h)
|
||||
(dolist (prop +popup-window-parameters)
|
||||
(delq (assq prop window-persistent-parameters)
|
||||
window-persistent-parameters)))))
|
||||
|
||||
(define-minor-mode +popup-buffer-mode
|
||||
"Minor mode for individual popup windows.
|
||||
|
||||
It is enabled when a buffer is displayed in a popup window and disabled when
|
||||
that window has been changed or closed."
|
||||
:init-value nil
|
||||
:keymap +popup-buffer-mode-map
|
||||
(if (not +popup-buffer-mode)
|
||||
(remove-hook 'after-change-major-mode-hook #'+popup-set-modeline-on-enable-h t)
|
||||
(add-hook 'after-change-major-mode-hook #'+popup-set-modeline-on-enable-h
|
||||
nil 'local)
|
||||
(when (timerp +popup--timer)
|
||||
(remove-hook 'kill-buffer-hook #'+popup-kill-buffer-hook-h t)
|
||||
(cancel-timer +popup--timer)
|
||||
(setq +popup--timer nil))))
|
||||
|
||||
(put '+popup-buffer-mode 'permanent-local t)
|
||||
(put '+popup-buffer-mode 'permanent-local-hook t)
|
||||
(put '+popup-set-modeline-on-enable-h 'permanent-local-hook t)
|
||||
|
||||
|
||||
;;
|
||||
;; Macros
|
||||
|
||||
(defmacro with-popup-rules! (rules &rest body)
|
||||
"Evaluate BODY with popup RULES. RULES is a list of popup rules. Each rule
|
||||
should match the arguments of `+popup-define' or the :popup setting."
|
||||
(declare (indent defun))
|
||||
`(let ((+popup--display-buffer-alist +popup--old-display-buffer-alist)
|
||||
display-buffer-alist)
|
||||
(set-popup-rules! ,rules)
|
||||
(when (bound-and-true-p +popup-mode)
|
||||
(setq display-buffer-alist +popup--display-buffer-alist))
|
||||
,@body))
|
||||
|
||||
(defmacro save-popups! (&rest body)
|
||||
"Sets aside all popups before executing the original function, usually to
|
||||
prevent the popup(s) from messing up the UI (or vice versa)."
|
||||
`(let* ((in-popup-p (+popup-buffer-p))
|
||||
(popups (+popup-windows))
|
||||
(+popup--inhibit-transient t)
|
||||
+popup--last)
|
||||
(dolist (p popups)
|
||||
(+popup/close p 'force))
|
||||
(unwind-protect
|
||||
(progn ,@body)
|
||||
(when popups
|
||||
(let ((origin (selected-window)))
|
||||
(+popup/restore)
|
||||
(unless in-popup-p
|
||||
(select-window origin)))))))
|
||||
|
||||
|
||||
;;
|
||||
;; Default popup rules & bootstrap
|
||||
|
||||
(set-popup-rules!
|
||||
(when (featurep! +all)
|
||||
'(("^\\*" :slot 1 :vslot -1 :select t)
|
||||
("^ \\*" :slot 1 :vslot -1 :size +popup-shrink-to-fit)))
|
||||
(when (featurep! +defaults)
|
||||
'(("^\\*Completions" :ignore t)
|
||||
("^\\*\\(?:[Cc]ompil\\(?:ation\\|e-Log\\)\\|Messages\\)"
|
||||
:vslot -2 :size 0.3 :autosave t :quit t :ttl nil)
|
||||
("^\\*\\(?:doom \\|Pp E\\)" ; transient buffers (no interaction required)
|
||||
:vslot -3 :size +popup-shrink-to-fit :autosave t :select ignore :quit t :ttl 0)
|
||||
("^\\*doom:" ; editing buffers (interaction required)
|
||||
:vslot -4 :size 0.35 :autosave t :select t :modeline t :quit nil :ttl t)
|
||||
("^\\*doom:\\(?:v?term\\|eshell\\)-popup" ; editing buffers (interaction required)
|
||||
:vslot -5 :size 0.35 :select t :modeline t :quit nil :ttl nil)
|
||||
("^\\*\\(?:Wo\\)?Man "
|
||||
:vslot -6 :size 0.45 :select t :quit t :ttl 0)
|
||||
("^\\*Calc"
|
||||
:vslot -7 :side bottom :size 0.4 :select t :quit nil :ttl 0)
|
||||
("^\\*Customize"
|
||||
:slot 2 :side right :select t :quit t)
|
||||
("^ \\*undo-tree\\*"
|
||||
:slot 2 :side left :size 20 :select t :quit t)
|
||||
;; `help-mode', `helpful-mode'
|
||||
("^\\*[Hh]elp"
|
||||
:slot 2 :vslot -8 :size 0.35 :select t)
|
||||
("^\\*eww\\*" ; `eww' (and used by dash docsets)
|
||||
:vslot -11 :size 0.35 :select t)
|
||||
("^\\*info\\*$" ; `Info-mode'
|
||||
:slot 2 :vslot 2 :size 0.45 :select t)))
|
||||
'(("^\\*Warnings" :vslot 99 :size 0.25)
|
||||
("^\\*Backtrace" :vslot 99 :size 0.4 :quit nil)
|
||||
("^\\*CPU-Profiler-Report " :side bottom :vslot 100 :slot 1 :height 0.4 :width 0.5 :quit nil)
|
||||
("^\\*Memory-Profiler-Report " :side bottom :vslot 100 :slot 2 :height 0.4 :width 0.5 :quit nil)
|
||||
("^\\*\\(?:Proced\\|timer-list\\|Process List\\|Abbrevs\\|Output\\|Occur\\|unsent mail\\)\\*" :ignore t)))
|
||||
|
||||
(add-hook 'doom-init-ui-hook #'+popup-mode 'append)
|
||||
|
||||
(add-hook! '+popup-buffer-mode-hook
|
||||
#'+popup-adjust-fringes-h
|
||||
#'+popup-adjust-margins-h
|
||||
#'+popup-set-modeline-on-enable-h
|
||||
#'+popup-unset-modeline-on-disable-h)
|
||||
|
||||
|
||||
;;
|
||||
;; Hacks
|
||||
|
||||
(load! "+hacks")
|
||||
212
.emacs.d/modules/ui/popup/test/test-popup.el
Normal file
212
.emacs.d/modules/ui/popup/test/test-popup.el
Normal file
@@ -0,0 +1,212 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/popup/test/test-popup.el
|
||||
|
||||
(require! :ui popup)
|
||||
|
||||
(describe "ui/popup"
|
||||
:var (display-buffer-alist
|
||||
+popup-default-display-buffer-actions
|
||||
+popup--display-buffer-alist
|
||||
+popup-defaults
|
||||
wconf)
|
||||
|
||||
(before-all
|
||||
(delete-other-windows)
|
||||
(switch-to-buffer "*scratch*")
|
||||
(setq wconf (current-window-configuration))
|
||||
(+popup-mode +1))
|
||||
(after-all
|
||||
(+popup-mode -1))
|
||||
|
||||
(before-each
|
||||
(setq display-buffer-alist nil
|
||||
+popup--display-buffer-alist nil
|
||||
+popup-default-display-buffer-actions '(+popup-display-buffer-stacked-side-window-fn)
|
||||
+popup-defaults '(:side bottom :select ignore :ttl nil :slot 1 :vslot 1)))
|
||||
(after-each
|
||||
(set-window-configuration wconf))
|
||||
|
||||
(describe "set-popup-rule!"
|
||||
(it "sets popup rules"
|
||||
(set-popup-rule! "does-not-exist" :size 10)
|
||||
(let ((rule (cdr (assoc "does-not-exist" display-buffer-alist))))
|
||||
(expect rule :to-contain '(+popup-buffer))
|
||||
(expect rule :to-contain '(size . 10))))
|
||||
(it "shadows old rules"
|
||||
(set-popup-rule! "a" :size 10)
|
||||
(set-popup-rule! "a" :size 20)
|
||||
(expect (cdr (assoc "a" display-buffer-alist))
|
||||
:to-contain '(size . 20)))
|
||||
(it "resolves to defaults"
|
||||
(let ((+popup-defaults '(:size 5)))
|
||||
(set-popup-rule! "a")
|
||||
(expect (cdr (assoc "a" display-buffer-alist))
|
||||
:to-contain '(size . 5)))))
|
||||
|
||||
(describe "popup rules"
|
||||
:var (origin a b c d e f g)
|
||||
(before-all (setq origin (current-buffer)))
|
||||
(before-each
|
||||
(dolist (name '(a b c d e f g))
|
||||
(set name (get-buffer-create (symbol-name name)))))
|
||||
(after-each
|
||||
(let (kill-buffer-query-functions kill-buffer-hook)
|
||||
(dolist (x (list a b c d e f g))
|
||||
(ignore-errors (delete-window (get-buffer-window x)))
|
||||
(kill-buffer x))))
|
||||
|
||||
(describe "slot positioning"
|
||||
(before-each
|
||||
(set-popup-rules!
|
||||
'(("a" :slot 1 :vslot 1)
|
||||
("b" :slot 2 :vslot 1)
|
||||
("c" :slot 1 :vslot 2)
|
||||
("d" :slot 2 :vslot 2)
|
||||
("e" :slot 1 :vslot 3)
|
||||
("f" :slot 1 :vslot 3)
|
||||
("g"))))
|
||||
|
||||
(it "replaces popups with the same slots"
|
||||
(mapc #'display-buffer (list e f))
|
||||
(expect (length (+popup-windows)) :to-be 1))
|
||||
|
||||
(it "replaces popups among multiple that have the same slots"
|
||||
(let ((first (display-buffer a))
|
||||
(second (display-buffer b))
|
||||
(third (display-buffer e))
|
||||
(fourth (display-buffer f)))
|
||||
(expect (+popup-windows) :to-have-same-items-as
|
||||
(list first second fourth))))
|
||||
|
||||
(describe ":slot"
|
||||
(it "opens left of others if lower"
|
||||
(let ((first (display-buffer b))
|
||||
(second (display-buffer a)))
|
||||
(expect (length (+popup-windows)) :to-be 2)
|
||||
(expect (window-in-direction 'left first t)
|
||||
:to-equal second)))
|
||||
(it "opens right of others if higher"
|
||||
(let ((first (display-buffer a))
|
||||
(second (display-buffer b)))
|
||||
(expect (length (+popup-windows)) :to-be 2)
|
||||
(expect (window-in-direction 'right first t)
|
||||
:to-equal second)))
|
||||
(it "obeys default :slot"
|
||||
(let ((window (display-buffer g)))
|
||||
(expect (window-parameter window 'window-slot) :to-be 1)
|
||||
(expect (window-parameter window 'window-vslot) :to-be 1))))
|
||||
|
||||
(describe ":vslot"
|
||||
;; TODO Implement this, somehow
|
||||
(xit "opens lower :vslot popups above others"
|
||||
(let ((first (display-buffer c))
|
||||
(second (display-buffer a)))
|
||||
(expect (length (+popup-windows)) :to-be 2)
|
||||
(expect (window-in-direction 'above first t)
|
||||
:to-equal second)))
|
||||
(it "opens higher :vslot popups below others"
|
||||
(let ((first (display-buffer c))
|
||||
(second (display-buffer e)))
|
||||
(expect (length (+popup-windows)) :to-be 2)
|
||||
(expect (window-in-direction 'below first t)
|
||||
:to-equal second)))))
|
||||
|
||||
(describe ":select"
|
||||
(it "selects the popup if non-nil"
|
||||
(set-popup-rule! "^a$" :select t)
|
||||
(display-buffer a)
|
||||
(expect (current-buffer) :to-equal a))
|
||||
(it "selects the originating window if nil"
|
||||
(set-popup-rule! "^a$" :select nil)
|
||||
(display-buffer a)
|
||||
(expect (current-buffer) :to-equal origin))
|
||||
(it "fall back to base selection if passed #'ignore"
|
||||
(spy-on 'ignore)
|
||||
(set-popup-rule! "^a$" :select #'ignore)
|
||||
(save-window-excursion
|
||||
(display-buffer a)
|
||||
(expect (current-buffer) :to-equal origin))
|
||||
(save-window-excursion
|
||||
(pop-to-buffer a)
|
||||
(expect (current-buffer) :to-equal a))
|
||||
(expect 'ignore :to-have-been-called-times 2)))
|
||||
|
||||
(describe ":modeline"
|
||||
(it "disables the mode-line if nil"
|
||||
(set-popup-rule! "a" :modeline nil :select t)
|
||||
(display-buffer a)
|
||||
(expect mode-line-format :to-be nil))
|
||||
(it "uses the default mode-line if t"
|
||||
(set-popup-rule! "a" :modeline t :select t)
|
||||
(display-buffer a)
|
||||
(expect mode-line-format :to-equal (default-value 'mode-line-format)))
|
||||
(it "uses a predefined mode-line if passed a symbol"
|
||||
(set-popup-rule! "a" :modeline '("x") :select t)
|
||||
(display-buffer a)
|
||||
(expect mode-line-format :to-equal '("x")))
|
||||
(it "runs the handler if passed a function"
|
||||
(set-popup-rule! "a" :modeline (lambda () (setq mode-line-format '("x"))) :select t)
|
||||
(display-buffer a)
|
||||
(expect mode-line-format :to-equal '("x"))))
|
||||
|
||||
;; TODO
|
||||
(xdescribe ":autosave")
|
||||
|
||||
(describe ":quit"
|
||||
(it "will close from anywhere if :quit = t"
|
||||
(set-popup-rule! "a" :quit t)
|
||||
(save-window-excursion
|
||||
(display-buffer a)
|
||||
(call-interactively #'+popup/close-all)
|
||||
(expect (get-buffer-window a) :to-be nil))
|
||||
(save-window-excursion
|
||||
(pop-to-buffer a)
|
||||
(call-interactively #'+popup/close)
|
||||
(expect (get-buffer-window a) :to-be nil)))
|
||||
(it "will only close from outside if :quit = 'other"
|
||||
(set-popup-rule! "a" :quit 'other)
|
||||
(save-window-excursion
|
||||
(display-buffer a)
|
||||
(call-interactively #'+popup/close-all)
|
||||
(expect (get-buffer-window a) :to-be nil))
|
||||
(save-window-excursion
|
||||
(pop-to-buffer a)
|
||||
(call-interactively #'+popup/close)
|
||||
(expect (get-buffer-window a))))
|
||||
(it "will only close from inside if :quit = 'current"
|
||||
(set-popup-rule! "a" :quit 'current)
|
||||
(save-window-excursion
|
||||
(display-buffer a)
|
||||
(call-interactively #'+popup/close-all)
|
||||
(expect (get-buffer-window a)))
|
||||
(save-window-excursion
|
||||
(pop-to-buffer a)
|
||||
(call-interactively #'+popup/close)
|
||||
(expect (get-buffer-window a) :to-be nil)))
|
||||
(it "never close a if :quit = nil"
|
||||
(set-popup-rule! "a" :quit nil)
|
||||
(save-window-excursion
|
||||
(display-buffer a)
|
||||
(call-interactively #'+popup/close-all)
|
||||
(expect (get-buffer-window a)))
|
||||
(save-window-excursion
|
||||
(pop-to-buffer a)
|
||||
(call-interactively #'+popup/close)
|
||||
(expect (get-buffer-window a)))))
|
||||
|
||||
;; TODO
|
||||
(xdescribe ":ttl")
|
||||
(xdescribe ":size")
|
||||
(xdescribe ":width")
|
||||
(xdescribe ":height")
|
||||
(xdescribe ":side")
|
||||
(xdescribe ":actions"))
|
||||
|
||||
;; TODO
|
||||
(xdescribe "predicate functions"
|
||||
(describe "buffer-p")
|
||||
(describe "window-p"))
|
||||
|
||||
;; TODO
|
||||
(xdescribe "save-popups!")
|
||||
(xdescribe "with-popup-rules!"))
|
||||
122
.emacs.d/modules/ui/pretty-code/+fira.el
Normal file
122
.emacs.d/modules/ui/pretty-code/+fira.el
Normal file
@@ -0,0 +1,122 @@
|
||||
;;; ui/pretty-code/+fira.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +pretty-code-fira-code-font-name "Fira Code Symbol"
|
||||
"Name of the fira code ligature font.")
|
||||
|
||||
(defvar +pretty-code-fira-code-font-ligatures
|
||||
'(("www" . #Xe100)
|
||||
("**" . #Xe101)
|
||||
("***" . #Xe102)
|
||||
("**/" . #Xe103)
|
||||
("*>" . #Xe104)
|
||||
("*/" . #Xe105)
|
||||
("\\\\" . #Xe106)
|
||||
("\\\\\\" . #Xe107)
|
||||
("{-" . #Xe108)
|
||||
("[]" . #Xe109)
|
||||
("::" . #Xe10a)
|
||||
(":::" . #Xe10b)
|
||||
(":=" . #Xe10c)
|
||||
("!!" . #Xe10d)
|
||||
("!=" . #Xe10e)
|
||||
("!==" . #Xe10f)
|
||||
("-}" . #Xe110)
|
||||
("--" . #Xe111)
|
||||
("---" . #Xe112)
|
||||
("-->" . #Xe113)
|
||||
("->" . #Xe114)
|
||||
("->>" . #Xe115)
|
||||
("-<" . #Xe116)
|
||||
("-<<" . #Xe117)
|
||||
("-~" . #Xe118)
|
||||
("#{" . #Xe119)
|
||||
("#[" . #Xe11a)
|
||||
("##" . #Xe11b)
|
||||
("###" . #Xe11c)
|
||||
("####" . #Xe11d)
|
||||
("#(" . #Xe11e)
|
||||
("#?" . #Xe11f)
|
||||
("#_" . #Xe120)
|
||||
("#_(" . #Xe121)
|
||||
(".-" . #Xe122)
|
||||
(".=" . #Xe123)
|
||||
(".." . #Xe124)
|
||||
("..<" . #Xe125)
|
||||
("..." . #Xe126)
|
||||
("?=" . #Xe127)
|
||||
("??" . #Xe128)
|
||||
(";;" . #Xe129)
|
||||
("/*" . #Xe12a)
|
||||
("/**" . #Xe12b)
|
||||
("/=" . #Xe12c)
|
||||
("/==" . #Xe12d)
|
||||
("/>" . #Xe12e)
|
||||
("//" . #Xe12f)
|
||||
("///" . #Xe130)
|
||||
("&&" . #Xe131)
|
||||
("||" . #Xe132)
|
||||
("||=" . #Xe133)
|
||||
("|=" . #Xe134)
|
||||
("|>" . #Xe135)
|
||||
("^=" . #Xe136)
|
||||
("$>" . #Xe137)
|
||||
("++" . #Xe138)
|
||||
("+++" . #Xe139)
|
||||
("+>" . #Xe13a)
|
||||
("=:=" . #Xe13b)
|
||||
("==" . #Xe13c)
|
||||
("===" . #Xe13d)
|
||||
("==>" . #Xe13e)
|
||||
("=>" . #Xe13f)
|
||||
("=>>" . #Xe140)
|
||||
("=<" . #Xe141)
|
||||
("=<<" . #Xe142)
|
||||
("=/=" . #Xe143)
|
||||
(">-" . #Xe144)
|
||||
(">=" . #Xe145)
|
||||
(">=>" . #Xe146)
|
||||
(">>" . #Xe147)
|
||||
(">>-" . #Xe148)
|
||||
(">>=" . #Xe149)
|
||||
(">>>" . #Xe14a)
|
||||
("<*" . #Xe14b)
|
||||
("<*>" . #Xe14c)
|
||||
("<|" . #Xe14d)
|
||||
("<|>" . #Xe14e)
|
||||
("<$" . #Xe14f)
|
||||
("<$>" . #Xe150)
|
||||
("<!--" . #Xe151)
|
||||
("<-" . #Xe152)
|
||||
("<--" . #Xe153)
|
||||
("<->" . #Xe154)
|
||||
("<+" . #Xe155)
|
||||
("<+>" . #Xe156)
|
||||
("<=" . #Xe157)
|
||||
("<==" . #Xe158)
|
||||
("<=>" . #Xe159)
|
||||
("<=<" . #Xe15a)
|
||||
("<>" . #Xe15b)
|
||||
("<<" . #Xe15c)
|
||||
("<<-" . #Xe15d)
|
||||
("<<=" . #Xe15e)
|
||||
("<<<" . #Xe15f)
|
||||
("<~" . #Xe160)
|
||||
("<~~" . #Xe161)
|
||||
("</" . #Xe162)
|
||||
("</>" . #Xe163)
|
||||
("~@" . #Xe164)
|
||||
("~-" . #Xe165)
|
||||
("~=" . #Xe166)
|
||||
("~>" . #Xe167)
|
||||
("~~" . #Xe168)
|
||||
("~~>" . #Xe169)
|
||||
("%%" . #Xe16a)))
|
||||
|
||||
(defun +pretty-code-setup-fira-ligatures-h ()
|
||||
(set-fontset-font t '(#Xe100 . #Xe16f) +pretty-code-fira-code-font-name)
|
||||
(setq-default prettify-symbols-alist
|
||||
(append prettify-symbols-alist
|
||||
(mapcar #'+pretty-code--correct-symbol-bounds
|
||||
+pretty-code-fira-code-font-ligatures))))
|
||||
|
||||
(add-hook 'doom-init-ui-hook #'+pretty-code-setup-fira-ligatures-h)
|
||||
58
.emacs.d/modules/ui/pretty-code/+hasklig.el
Normal file
58
.emacs.d/modules/ui/pretty-code/+hasklig.el
Normal file
@@ -0,0 +1,58 @@
|
||||
;;; ui/pretty-code/+hasklig.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +pretty-code-hasklig-font-name "Hasklig"
|
||||
"Name of the hasklig ligature font.")
|
||||
|
||||
(defvar +pretty-code-hasklig-font-ligatures
|
||||
'(("&&" . #Xe100)
|
||||
("***" . #Xe101)
|
||||
("*>" . #Xe102)
|
||||
("\\\\" . #Xe103)
|
||||
("||" . #Xe104)
|
||||
("|>" . #Xe105)
|
||||
("::" . #Xe106)
|
||||
("==" . #Xe107)
|
||||
("===" . #Xe108)
|
||||
("==>" . #Xe109)
|
||||
("=>" . #Xe10a)
|
||||
("=<<" . #Xe10b)
|
||||
("!!" . #Xe10c)
|
||||
(">>" . #Xe10d)
|
||||
(">>=" . #Xe10e)
|
||||
(">>>" . #Xe10f)
|
||||
(">>-" . #Xe110)
|
||||
(">-" . #Xe111)
|
||||
("->" . #Xe112)
|
||||
("-<" . #Xe113)
|
||||
("-<<" . #Xe114)
|
||||
("<*" . #Xe115)
|
||||
("<*>" . #Xe116)
|
||||
("<|" . #Xe117)
|
||||
("<|>" . #Xe118)
|
||||
("<$>" . #Xe119)
|
||||
("<>" . #Xe11a)
|
||||
("<-" . #Xe11b)
|
||||
("<<" . #Xe11c)
|
||||
("<<<" . #Xe11d)
|
||||
("<+>" . #Xe11e)
|
||||
(".." . #Xe11f)
|
||||
("..." . #Xe120)
|
||||
("++" . #Xe121)
|
||||
("+++" . #Xe122)
|
||||
("/=" . #Xe123)
|
||||
(":::" . #Xe124)
|
||||
(">=>" . #Xe125)
|
||||
("->>" . #Xe126)
|
||||
("<=>" . #Xe127)
|
||||
("<=<" . #Xe128)
|
||||
("<->" . #Xe129)))
|
||||
|
||||
|
||||
(defun +pretty-code-setup-hasklig-ligatures-h ()
|
||||
(set-fontset-font t '(#Xe100 . #Xe129) +pretty-code-hasklig-font-name)
|
||||
(setq-default prettify-symbols-alist
|
||||
(append prettify-symbols-alist
|
||||
(mapcar #'+pretty-code--correct-symbol-bounds
|
||||
+pretty-code-hasklig-font-ligatures))))
|
||||
|
||||
(add-hook 'doom-init-ui-hook #'+pretty-code-setup-hasklig-ligatures-h)
|
||||
232
.emacs.d/modules/ui/pretty-code/+iosevka.el
Normal file
232
.emacs.d/modules/ui/pretty-code/+iosevka.el
Normal file
@@ -0,0 +1,232 @@
|
||||
;;; ui/pretty-code/+iosevka.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +pretty-code-iosevka-font-name "Iosevka"
|
||||
"Name of the iosevka ligature font.")
|
||||
|
||||
(defvar +pretty-code-iosevka-font-ligatures
|
||||
'(;; Double-ended hyphen arrows
|
||||
("<->" . #Xe100)
|
||||
("<-->" . #Xe101)
|
||||
("<--->" . #Xe102)
|
||||
("<---->" . #Xe103)
|
||||
("<----->" . #Xe104)
|
||||
;; Double-ended equals arrows
|
||||
("<=>" . #Xe105)
|
||||
("<==>" . #Xe106)
|
||||
("<===>" . #Xe107)
|
||||
("<====>" . #Xe108)
|
||||
("<=====>" . #Xe109)
|
||||
;; Double-ended asterisk operators
|
||||
("<**>" . #Xe10a)
|
||||
("<***>" . #Xe10b)
|
||||
("<****>" . #Xe10c)
|
||||
("<*****>" . #Xe10d)
|
||||
;; HTML comments
|
||||
("<!--" . #Xe10e)
|
||||
("<!---" . #Xe10f)
|
||||
;; Three-char ops with discards
|
||||
("<$" . #Xe110)
|
||||
("<$>" . #Xe111)
|
||||
("$>" . #Xe112)
|
||||
("<." . #Xe113)
|
||||
("<.>" . #Xe114)
|
||||
(".>" . #Xe115)
|
||||
("<*" . #Xe116)
|
||||
("<*>" . #Xe117)
|
||||
("*>" . #Xe118)
|
||||
("<\\" . #Xe119)
|
||||
("<\\>" . #Xe11a)
|
||||
("\\>" . #Xe11b)
|
||||
("</" . #Xe11c)
|
||||
("</>" . #Xe11d)
|
||||
("/>" . #Xe11e)
|
||||
("<\"" . #Xe11f)
|
||||
("<\">" . #Xe120)
|
||||
("\">" . #Xe121)
|
||||
("<'" . #Xe122)
|
||||
("<'>" . #Xe123)
|
||||
("'>" . #Xe124)
|
||||
("<^" . #Xe125)
|
||||
("<^>" . #Xe126)
|
||||
("^>" . #Xe127)
|
||||
("<&" . #Xe128)
|
||||
("<&>" . #Xe129)
|
||||
("&>" . #Xe12a)
|
||||
("<%" . #Xe12b)
|
||||
("<%>" . #Xe12c)
|
||||
("%>" . #Xe12d)
|
||||
("<@" . #Xe12e)
|
||||
("<@>" . #Xe12f)
|
||||
("@>" . #Xe130)
|
||||
("<#" . #Xe131)
|
||||
("<#>" . #Xe132)
|
||||
("#>" . #Xe133)
|
||||
("<+" . #Xe134)
|
||||
("<+>" . #Xe135)
|
||||
("+>" . #Xe136)
|
||||
("<-" . #Xe137)
|
||||
("<->" . #Xe138)
|
||||
("->" . #Xe139)
|
||||
("<!" . #Xe13a)
|
||||
("<!>" . #Xe13b)
|
||||
("!>" . #Xe13c)
|
||||
("<?" . #Xe13d)
|
||||
("<?>" . #Xe13e)
|
||||
("?>" . #Xe13f)
|
||||
("<|" . #Xe140)
|
||||
("<|>" . #Xe141)
|
||||
("|>" . #Xe142)
|
||||
("<:" . #Xe143)
|
||||
("<:>" . #Xe144)
|
||||
(":>" . #Xe145)
|
||||
;; Colons
|
||||
("::" . #Xe146)
|
||||
(":::" . #Xe147)
|
||||
("::::" . #Xe148)
|
||||
;; Arrow-like operators
|
||||
("->" . #Xe149)
|
||||
("->-" . #Xe14a)
|
||||
("->--" . #Xe14b)
|
||||
("->>" . #Xe14c)
|
||||
("->>-" . #Xe14d)
|
||||
("->>--" . #Xe14e)
|
||||
("->>>" . #Xe14f)
|
||||
("->>>-" . #Xe150)
|
||||
("->>>--" . #Xe151)
|
||||
("-->" . #Xe152)
|
||||
("-->-" . #Xe153)
|
||||
("-->--" . #Xe154)
|
||||
("-->>" . #Xe155)
|
||||
("-->>-" . #Xe156)
|
||||
("-->>--" . #Xe157)
|
||||
("-->>>" . #Xe158)
|
||||
("-->>>-" . #Xe159)
|
||||
("-->>>--" . #Xe15a)
|
||||
(">-" . #Xe15b)
|
||||
(">--" . #Xe15c)
|
||||
(">>-" . #Xe15d)
|
||||
(">>--" . #Xe15e)
|
||||
(">>>-" . #Xe15f)
|
||||
(">>>--" . #Xe160)
|
||||
("=>" . #Xe161)
|
||||
("=>=" . #Xe162)
|
||||
("=>==" . #Xe163)
|
||||
("=>>" . #Xe164)
|
||||
("=>>=" . #Xe165)
|
||||
("=>>==" . #Xe166)
|
||||
("=>>>" . #Xe167)
|
||||
("=>>>=" . #Xe168)
|
||||
("=>>>==" . #Xe169)
|
||||
("==>" . #Xe16a)
|
||||
("==>=" . #Xe16b)
|
||||
("==>==" . #Xe16c)
|
||||
("==>>" . #Xe16d)
|
||||
("==>>=" . #Xe16e)
|
||||
("==>>==" . #Xe16f)
|
||||
("==>>>" . #Xe170)
|
||||
("==>>>=" . #Xe171)
|
||||
("==>>>==" . #Xe172)
|
||||
(">=" . #Xe173)
|
||||
(">==" . #Xe174)
|
||||
(">>=" . #Xe175)
|
||||
(">>==" . #Xe176)
|
||||
(">>>=" . #Xe177)
|
||||
(">>>==" . #Xe178)
|
||||
("<-" . #Xe179)
|
||||
("-<-" . #Xe17a)
|
||||
("--<-" . #Xe17b)
|
||||
("<<-" . #Xe17c)
|
||||
("-<<-" . #Xe17d)
|
||||
("--<<-" . #Xe17e)
|
||||
("<<<-" . #Xe17f)
|
||||
("-<<<-" . #Xe180)
|
||||
("--<<<-" . #Xe181)
|
||||
("<--" . #Xe182)
|
||||
("-<--" . #Xe183)
|
||||
("--<--" . #Xe184)
|
||||
("<<--" . #Xe185)
|
||||
("-<<--" . #Xe186)
|
||||
("--<<--" . #Xe187)
|
||||
("<<<--" . #Xe188)
|
||||
("-<<<--" . #Xe189)
|
||||
("--<<<--" . #Xe18a)
|
||||
("-<" . #Xe18b)
|
||||
("--<" . #Xe18c)
|
||||
("-<<" . #Xe18d)
|
||||
("--<<" . #Xe18e)
|
||||
("-<<<" . #Xe18f)
|
||||
("--<<<" . #Xe190)
|
||||
("<=" . #Xe191)
|
||||
("=<=" . #Xe192)
|
||||
("==<=" . #Xe193)
|
||||
("<<=" . #Xe194)
|
||||
("=<<=" . #Xe195)
|
||||
("==<<=" . #Xe196)
|
||||
("<<<=" . #Xe197)
|
||||
("=<<<=" . #Xe198)
|
||||
("==<<<=" . #Xe199)
|
||||
("<==" . #Xe19a)
|
||||
("=<==" . #Xe19b)
|
||||
("==<==" . #Xe19c)
|
||||
("<<==" . #Xe19d)
|
||||
("=<<==" . #Xe19e)
|
||||
("==<<==" . #Xe19f)
|
||||
("<<<==" . #Xe1a0)
|
||||
("=<<<==" . #Xe1a1)
|
||||
("==<<<==" . #Xe1a2)
|
||||
("=<" . #Xe1a3)
|
||||
("==<" . #Xe1a4)
|
||||
("=<<" . #Xe1a5)
|
||||
("==<<" . #Xe1a6)
|
||||
("=<<<" . #Xe1a7)
|
||||
("==<<<" . #Xe1a8)
|
||||
;; Monadic operators
|
||||
(">=>" . #Xe1a9)
|
||||
(">->" . #Xe1aa)
|
||||
(">-->" . #Xe1ab)
|
||||
(">==>" . #Xe1ac)
|
||||
("<=<" . #Xe1ad)
|
||||
("<-<" . #Xe1ae)
|
||||
("<--<" . #Xe1af)
|
||||
("<==<" . #Xe1b0)
|
||||
;; Composition operators
|
||||
(">>" . #Xe1b1)
|
||||
(">>>" . #Xe1b2)
|
||||
("<<" . #Xe1b3)
|
||||
("<<<" . #Xe1b4)
|
||||
;; Lens operators
|
||||
(":+" . #Xe1b5)
|
||||
(":-" . #Xe1b6)
|
||||
(":=" . #Xe1b7)
|
||||
("+:" . #Xe1b8)
|
||||
("-:" . #Xe1b9)
|
||||
("=:" . #Xe1ba)
|
||||
("=^" . #Xe1bb)
|
||||
("=+" . #Xe1bc)
|
||||
("=-" . #Xe1bd)
|
||||
("=*" . #Xe1be)
|
||||
("=/" . #Xe1bf)
|
||||
("=%" . #Xe1c0)
|
||||
("^=" . #Xe1c1)
|
||||
("+=" . #Xe1c2)
|
||||
("-=" . #Xe1c3)
|
||||
("*=" . #Xe1c4)
|
||||
("/=" . #Xe1c5)
|
||||
("%=" . #Xe1c6)
|
||||
;; Logical
|
||||
("/\\" . #Xe1c7)
|
||||
("\\/" . #Xe1c8)
|
||||
;; Semigroup/monoid operators
|
||||
("<>" . #Xe1c9)
|
||||
("<+" . #Xe1ca)
|
||||
("<+>" . #Xe1cb)
|
||||
("+>" . #Xe1cc))
|
||||
"Defines the character mappings for ligatures for Iosevka.")
|
||||
|
||||
(defun +pretty-code-setup-iosevka-ligatures-h ()
|
||||
(set-fontset-font t '(#Xe100 . #Xe1cc) +pretty-code-iosevka-font-name)
|
||||
(setq-default prettify-symbols-alist
|
||||
(append prettify-symbols-alist
|
||||
+pretty-code-iosevka-font-ligatures)))
|
||||
|
||||
(add-hook 'doom-init-ui-hook #'+pretty-code-setup-iosevka-ligatures-h)
|
||||
259
.emacs.d/modules/ui/pretty-code/+pragmata-pro.el
Normal file
259
.emacs.d/modules/ui/pretty-code/+pragmata-pro.el
Normal file
@@ -0,0 +1,259 @@
|
||||
;;; ui/pretty-code/+pragmata-pro.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +pretty-code-pragmata-pro-font-name "PragmataPro"
|
||||
"Name of the Pragmata Pro ligature font.")
|
||||
|
||||
(defvar +pretty-code-pragmata-pro-font-ligatures
|
||||
'(("[ERROR]" . #XE2C0)
|
||||
("[DEBUG]" . #XE2C1)
|
||||
("[INFO]" . #XE2C2)
|
||||
("[WARN]" . #XE2C3)
|
||||
("[WARNING]" . #XE2C4)
|
||||
("[ERR]" . #XE2C5)
|
||||
("[FATAL]" . #XE2C6)
|
||||
("[TRACE]" . #XE2C7)
|
||||
("[FIXME]" . #XE2C8)
|
||||
("[TODO]" . #XE2C9)
|
||||
("[BUG]" . #XE2CA)
|
||||
("[NOTE]" . #XE2CB)
|
||||
("[HACK]" . #XE2CC)
|
||||
("[MARK]" . #XE2CD)
|
||||
("# ERROR" . #XE2F0)
|
||||
("# DEBUG" . #XE2F1)
|
||||
("# INFO" . #XE2F2)
|
||||
("# WARN" . #XE2F3)
|
||||
("# WARNING" . #XE2F4)
|
||||
("# ERR" . #XE2F5)
|
||||
("# FATAL" . #XE2F6)
|
||||
("# TRACE" . #XE2F7)
|
||||
("# FIXME" . #XE2F8)
|
||||
("# TODO" . #XE2F9)
|
||||
("# BUG" . #XE2FA)
|
||||
("# NOTE" . #XE2FB)
|
||||
("# HACK" . #XE2FC)
|
||||
("# MARK" . #XE2FD)
|
||||
("// ERROR" . #XE2E0)
|
||||
("// DEBUG" . #XE2E1)
|
||||
("// INFO" . #XE2E2)
|
||||
("// WARN" . #XE2E3)
|
||||
("// WARNING". #XE2E4)
|
||||
("// ERR" . #XE2E5)
|
||||
("// FATAL" . #XE2E6)
|
||||
("// TRACE" . #XE2E7)
|
||||
("// FIXME" . #XE2E8)
|
||||
("// TODO" . #XE2E9)
|
||||
("// BUG" . #XE2EA)
|
||||
("// NOTE" . #XE2EB)
|
||||
("// HACK" . #XE2EC)
|
||||
("// MARK" . #XE2ED)
|
||||
("!!" . #XE900)
|
||||
("!=" . #XE901)
|
||||
("!==" . #XE902)
|
||||
("!!!" . #XE903)
|
||||
("!≡" . #XE904)
|
||||
("!≡≡" . #XE905)
|
||||
("!>" . #XE906)
|
||||
("!=<" . #XE907)
|
||||
("#(" . #XE920)
|
||||
("#_" . #XE921)
|
||||
("#{" . #XE922)
|
||||
("#?" . #XE923)
|
||||
("#>" . #XE924)
|
||||
("##" . #XE925)
|
||||
("#_(" . #XE926)
|
||||
("%=" . #XE930)
|
||||
("%>" . #XE931)
|
||||
("%>%" . #XE932)
|
||||
("%<%" . #XE933)
|
||||
("&%" . #XE940)
|
||||
("&&" . #XE941)
|
||||
("&*" . #XE942)
|
||||
("&+" . #XE943)
|
||||
("&-" . #XE944)
|
||||
("&/" . #XE945)
|
||||
("&=" . #XE946)
|
||||
("&&&" . #XE947)
|
||||
("&>" . #XE948)
|
||||
("$>" . #XE955)
|
||||
("***" . #XE960)
|
||||
("*=" . #XE961)
|
||||
("*/" . #XE962)
|
||||
("*>" . #XE963)
|
||||
("++" . #XE970)
|
||||
("+++" . #XE971)
|
||||
("+=" . #XE972)
|
||||
("+>" . #XE973)
|
||||
("++=" . #XE974)
|
||||
("--" . #XE980)
|
||||
("-<" . #XE981)
|
||||
("-<<" . #XE982)
|
||||
("-=" . #XE983)
|
||||
("->" . #XE984)
|
||||
("->>" . #XE985)
|
||||
("---" . #XE986)
|
||||
("-->" . #XE987)
|
||||
("-+-" . #XE988)
|
||||
("-\\/" . #XE989)
|
||||
("-|>" . #XE98A)
|
||||
("-<|" . #XE98B)
|
||||
(".." . #XE990)
|
||||
("..." . #XE991)
|
||||
("..<" . #XE992)
|
||||
(".>" . #XE993)
|
||||
(".~" . #XE994)
|
||||
(".=" . #XE995)
|
||||
("/*" . #XE9A0)
|
||||
("//" . #XE9A1)
|
||||
("/>" . #XE9A2)
|
||||
("/=" . #XE9A3)
|
||||
("/==" . #XE9A4)
|
||||
("///" . #XE9A5)
|
||||
("/**" . #XE9A6)
|
||||
(":::" . #XE9AF)
|
||||
("::" . #XE9B0)
|
||||
(":=" . #XE9B1)
|
||||
(":≡" . #XE9B2)
|
||||
(":>" . #XE9B3)
|
||||
(":=>" . #XE9B4)
|
||||
(":(" . #XE9B5)
|
||||
(":-(" . #XE9B6)
|
||||
(":)" . #XE9B7)
|
||||
(":-)" . #XE9B8)
|
||||
(":/" . #XE9B9)
|
||||
(":\\" . #XE9BA)
|
||||
(":3" . #XE9BB)
|
||||
(":D" . #XE9BC)
|
||||
(":P" . #XE9BD)
|
||||
(":>:" . #XE9BE)
|
||||
(":<:" . #XE9BF)
|
||||
("<$>" . #XE9C0)
|
||||
("<*" . #XE9C1)
|
||||
("<*>" . #XE9C2)
|
||||
("<+>" . #XE9C3)
|
||||
("<-" . #XE9C4)
|
||||
("<<" . #XE9C5)
|
||||
("<<<" . #XE9C6)
|
||||
("<<=" . #XE9C7)
|
||||
("<=" . #XE9C8)
|
||||
("<=>" . #XE9C9)
|
||||
("<>" . #XE9CA)
|
||||
("<|>" . #XE9CB)
|
||||
("<<-" . #XE9CC)
|
||||
("<|" . #XE9CD)
|
||||
("<=<" . #XE9CE)
|
||||
("<~" . #XE9CF)
|
||||
("<~~" . #XE9D0)
|
||||
("<<~" . #XE9D1)
|
||||
("<$" . #XE9D2)
|
||||
("<+" . #XE9D3)
|
||||
("<!>" . #XE9D4)
|
||||
("<@>" . #XE9D5)
|
||||
("<#>" . #XE9D6)
|
||||
("<%>" . #XE9D7)
|
||||
("<^>" . #XE9D8)
|
||||
("<&>" . #XE9D9)
|
||||
("<?>" . #XE9DA)
|
||||
("<.>" . #XE9DB)
|
||||
("</>" . #XE9DC)
|
||||
("<\\>" . #XE9DD)
|
||||
("<\">" . #XE9DE)
|
||||
("<:>" . #XE9DF)
|
||||
("<~>" . #XE9E0)
|
||||
("<**>" . #XE9E1)
|
||||
("<<^" . #XE9E2)
|
||||
("<!" . #XE9E3)
|
||||
("<@" . #XE9E4)
|
||||
("<#" . #XE9E5)
|
||||
("<%" . #XE9E6)
|
||||
("<^" . #XE9E7)
|
||||
("<&" . #XE9E8)
|
||||
("<?" . #XE9E9)
|
||||
("<." . #XE9EA)
|
||||
("</" . #XE9EB)
|
||||
("<\\" . #XE9EC)
|
||||
("<\"" . #XE9ED)
|
||||
("<:" . #XE9EE)
|
||||
("<->" . #XE9EF)
|
||||
("<!--" . #XE9F0)
|
||||
("<--" . #XE9F1)
|
||||
("<~<" . #XE9F2)
|
||||
("<==>" . #XE9F3)
|
||||
("<|-" . #XE9F4)
|
||||
("<<|" . #XE9F5)
|
||||
("<-<" . #XE9F7)
|
||||
("<-->" . #XE9F8)
|
||||
("<<==" . #XE9F9)
|
||||
("<==" . #XE9FA)
|
||||
("=<<" . #XEA00)
|
||||
("==" . #XEA01)
|
||||
("===" . #XEA02)
|
||||
("==>" . #XEA03)
|
||||
("=>" . #XEA04)
|
||||
("=~" . #XEA05)
|
||||
("=>>" . #XEA06)
|
||||
("=/=" . #XEA07)
|
||||
("=~=" . #XEA08)
|
||||
("==>>" . #XEA09)
|
||||
("≡≡" . #XEA10)
|
||||
("≡≡≡" . #XEA11)
|
||||
("≡:≡" . #XEA12)
|
||||
(">-" . #XEA20)
|
||||
(">=" . #XEA21)
|
||||
(">>" . #XEA22)
|
||||
(">>-" . #XEA23)
|
||||
(">>=" . #XEA24)
|
||||
(">>>" . #XEA25)
|
||||
(">=>" . #XEA26)
|
||||
(">>^" . #XEA27)
|
||||
(">>|" . #XEA28)
|
||||
(">!=" . #XEA29)
|
||||
(">->" . #XEA2A)
|
||||
("??" . #XEA40)
|
||||
("?~" . #XEA41)
|
||||
("?=" . #XEA42)
|
||||
("?>" . #XEA43)
|
||||
("???" . #XEA44)
|
||||
("?." . #XEA45)
|
||||
("^=" . #XEA48)
|
||||
("^." . #XEA49)
|
||||
("^?" . #XEA4A)
|
||||
("^.." . #XEA4B)
|
||||
("^<<" . #XEA4C)
|
||||
("^>>" . #XEA4D)
|
||||
("^>" . #XEA4E)
|
||||
("\\\\" . #XEA50)
|
||||
("\\>" . #XEA51)
|
||||
("\\/-" . #XEA52)
|
||||
("@>" . #XEA57)
|
||||
("|=" . #XEA60)
|
||||
("||" . #XEA61)
|
||||
("|>" . #XEA62)
|
||||
("|||" . #XEA63)
|
||||
("|+|" . #XEA64)
|
||||
("|->" . #XEA65)
|
||||
("|-->" . #XEA66)
|
||||
("|=>" . #XEA67)
|
||||
("|==>" . #XEA68)
|
||||
("|>-" . #XEA69)
|
||||
("|<<" . #XEA6A)
|
||||
("||>" . #XEA6B)
|
||||
("|>>" . #XEA6C)
|
||||
("|-" . #XEA6D)
|
||||
("||-" . #XEA6E)
|
||||
("~=" . #XEA70)
|
||||
("~>" . #XEA71)
|
||||
("~~>" . #XEA72)
|
||||
("~>>" . #XEA73)
|
||||
("[[" . #XEA80)
|
||||
("]]" . #XEA81)
|
||||
("\">" . #XEA90)
|
||||
("_|_" . #XEA97))
|
||||
"Defines the character mappings for ligatures for Pragmata Pro.")
|
||||
|
||||
(defun +pretty-code-setup-pragmata-pro-ligatures-h ()
|
||||
(setq-default prettify-symbols-alist
|
||||
(append prettify-symbols-alist
|
||||
(mapcar #'+pretty-code--correct-symbol-bounds
|
||||
+pretty-code-pragmata-pro-font-ligatures))))
|
||||
|
||||
(add-hook 'doom-init-ui-hook #'+pretty-code-setup-pragmata-pro-ligatures-h)
|
||||
55
.emacs.d/modules/ui/pretty-code/autoload.el
Normal file
55
.emacs.d/modules/ui/pretty-code/autoload.el
Normal file
@@ -0,0 +1,55 @@
|
||||
;;; ui/pretty-code/settings.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defvar +pretty-code-symbols-alist '((t))
|
||||
"An alist containing a mapping of major modes to its value for
|
||||
`prettify-symbols-alist'.")
|
||||
|
||||
;;;###autodef
|
||||
(defun set-pretty-symbols! (modes &rest plist)
|
||||
"Associates string patterns with icons in certain major-modes.
|
||||
|
||||
MODES is a major mode symbol or a list of them.
|
||||
PLIST is a property list whose keys must match keys in `+pretty-code-symbols',
|
||||
and whose values are strings representing the text to be replaced with that
|
||||
symbol. If the car of PLIST is nil, then unset any pretty symbols previously
|
||||
defined for MODES.
|
||||
|
||||
The following properties are special:
|
||||
|
||||
:alist ALIST
|
||||
Appends ALIST to `prettify-symbols-alist' literally, without mapping text to
|
||||
`+pretty-code-symbols'.
|
||||
:merge BOOL
|
||||
If non-nil, merge with previously defined `prettify-symbols-alist',
|
||||
otherwise overwrite it.
|
||||
|
||||
For example, the rule for emacs-lisp-mode is very simple:
|
||||
|
||||
(set-pretty-symbols! 'emacs-lisp-mode
|
||||
:lambda \"lambda\")
|
||||
|
||||
This will replace any instances of \"lambda\" in emacs-lisp-mode with the symbol
|
||||
assicated with :lambda in `+pretty-code-symbols'.
|
||||
|
||||
Pretty symbols can be unset for emacs-lisp-mode with:
|
||||
|
||||
(set-pretty-symbols! 'emacs-lisp-mode nil)"
|
||||
(declare (indent defun))
|
||||
(if (null (car-safe plist))
|
||||
(dolist (mode (doom-enlist modes))
|
||||
(delq (assq mode +pretty-code-symbols-alist)
|
||||
+pretty-code-symbols-alist))
|
||||
(let (results merge key)
|
||||
(while plist
|
||||
(pcase (setq key (pop plist))
|
||||
(:merge (setq merge (pop plist)))
|
||||
(:alist (setq results (append (pop plist) results)))
|
||||
(_
|
||||
(when-let (char (plist-get +pretty-code-symbols key))
|
||||
(push (cons (pop plist) char) results)))))
|
||||
(dolist (mode (doom-enlist modes))
|
||||
(unless merge
|
||||
(delq (assq mode +pretty-code-symbols-alist)
|
||||
+pretty-code-symbols-alist))
|
||||
(push (cons mode results) +pretty-code-symbols-alist)))))
|
||||
93
.emacs.d/modules/ui/pretty-code/config.el
Normal file
93
.emacs.d/modules/ui/pretty-code/config.el
Normal file
@@ -0,0 +1,93 @@
|
||||
;;; ui/pretty-code/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +pretty-code-symbols
|
||||
'(;; org
|
||||
:name "»"
|
||||
:src_block "»"
|
||||
:src_block_end "«"
|
||||
;; Functional
|
||||
:lambda "λ"
|
||||
:def "ƒ"
|
||||
:composition "∘"
|
||||
:map "↦"
|
||||
;; Types
|
||||
:null "∅"
|
||||
:true "𝕋"
|
||||
:false "𝔽"
|
||||
:int "ℤ"
|
||||
:float "ℝ"
|
||||
:str "𝕊"
|
||||
:bool "𝔹"
|
||||
;; Flow
|
||||
:not "¬"
|
||||
:in "∈"
|
||||
:not-in "∉"
|
||||
:and "∧"
|
||||
:or "∨"
|
||||
:for "∀"
|
||||
:some "∃"
|
||||
:return "⟼"
|
||||
:yield "⟻"
|
||||
;; Other
|
||||
:tuple "⨂"
|
||||
:pipe "" ;; FIXME: find a non-private char
|
||||
:dot "•")
|
||||
"Options plist for `set-pretty-symbols!'.
|
||||
|
||||
This should not contain any symbols from the Unicode Private Area! There is no
|
||||
universal way of getting the correct symbol as that area varies from font to
|
||||
font.")
|
||||
|
||||
(defun +pretty-code--correct-symbol-bounds (ligature-alist)
|
||||
"Prepend non-breaking spaces to a ligature.
|
||||
|
||||
This way `compose-region' (called by `prettify-symbols-mode') will use the
|
||||
correct width of the symbols instead of the width measured by `char-width'."
|
||||
(let ((len (length (car ligature-alist)))
|
||||
(acc (list (cdr ligature-alist))))
|
||||
(while (> len 1)
|
||||
(setq acc (cons #X00a0 (cons '(Br . Bl) acc))
|
||||
len (1- len)))
|
||||
(cons (car ligature-alist) acc)))
|
||||
|
||||
(defvar +pretty-code-enabled-modes t
|
||||
"List of major modes in which `prettify-symbols-mode' should be enabled.
|
||||
If t, enable it everywhere. If the first element is 'not, enable it in any mode
|
||||
besides what is listed.")
|
||||
|
||||
;; When you get to the right edge, it goes back to how it normally prints
|
||||
(setq prettify-symbols-unprettify-at-point 'right-edge)
|
||||
|
||||
(defun +pretty-code-init-pretty-symbols-h ()
|
||||
"Enable `prettify-symbols-mode'.
|
||||
|
||||
If in fundamental-mode, or a mode derived from special, comint, eshell or term
|
||||
modes, this function does nothing.
|
||||
|
||||
Otherwise it builds `prettify-code-symbols-alist' according to
|
||||
`+pretty-code-symbols-alist' for the current major-mode."
|
||||
(unless (or (eq major-mode 'fundamental-mode)
|
||||
(eq (get major-mode 'mode-class) 'special)
|
||||
(derived-mode-p 'comint-mode 'eshell-mode 'term-mode))
|
||||
(when (or (eq +pretty-code-enabled-modes t)
|
||||
(if (eq (car +pretty-code-enabled-modes) 'not)
|
||||
(not (memq major-mode (cdr +pretty-code-enabled-modes)))
|
||||
(memq major-mode +pretty-code-enabled-modes)))
|
||||
(setq prettify-symbols-alist
|
||||
(append (cdr (assq major-mode +pretty-code-symbols-alist))
|
||||
(default-value 'prettify-symbols-alist)))
|
||||
(when prettify-symbols-mode
|
||||
(prettify-symbols-mode -1))
|
||||
(prettify-symbols-mode +1))))
|
||||
|
||||
(add-hook 'after-change-major-mode-hook #'+pretty-code-init-pretty-symbols-h)
|
||||
|
||||
;; Font-specific ligature support
|
||||
(cond ((featurep! +fira)
|
||||
(load! "+fira"))
|
||||
((featurep! +iosevka)
|
||||
(load! "+iosevka"))
|
||||
((featurep! +hasklig)
|
||||
(load! "+hasklig"))
|
||||
((featurep! +pragmata-pro)
|
||||
(load! "+pragmata-pro")))
|
||||
18
.emacs.d/modules/ui/tabs/README.org
Normal file
18
.emacs.d/modules/ui/tabs/README.org
Normal file
@@ -0,0 +1,18 @@
|
||||
#+TITLE: ui/tabs
|
||||
#+DATE: July 12, 2019
|
||||
#+SINCE: v2.1
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC_3:noexport:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#plugins][Plugins]]
|
||||
|
||||
* Description
|
||||
This module adds an Atom-esque tab bar to the Emacs UI.
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/ema2159/centaur-tabs][centaur-tabs]]
|
||||
77
.emacs.d/modules/ui/tabs/autoload.el
Normal file
77
.emacs.d/modules/ui/tabs/autoload.el
Normal file
@@ -0,0 +1,77 @@
|
||||
;;; ui/tabs/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun +tabs-buffer-predicate (buffer)
|
||||
"TODO"
|
||||
(or (memq buffer (window-parameter nil 'tab-buffers))
|
||||
(eq buffer (doom-fallback-buffer))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun +tabs/close-tab-or-window ()
|
||||
"TODO"
|
||||
(interactive)
|
||||
(call-interactively
|
||||
(cond ((cdr (window-parameter nil 'tab-buffers))
|
||||
#'kill-current-buffer)
|
||||
((fboundp '+workspace/close-window-or-workspace)
|
||||
#'+workspace/close-window-or-workspace)
|
||||
(#'delete-window))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Advice
|
||||
|
||||
;;;###autoload
|
||||
(defun +tabs-kill-current-buffer-a (&rest _)
|
||||
(+tabs-remove-buffer-h))
|
||||
|
||||
;;;###autoload
|
||||
(defun +tabs-bury-buffer-a (orig-fn &rest args)
|
||||
(if centaur-tabs-mode
|
||||
(let ((b (current-buffer)))
|
||||
(apply orig-fn args)
|
||||
(unless (eq b (current-buffer))
|
||||
(with-current-buffer b
|
||||
(+tabs-remove-buffer-h))))
|
||||
(apply orig-fn args)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +tabs-kill-tab-maybe-a (tab)
|
||||
(let ((buffer (centaur-tabs-tab-value tab)))
|
||||
(with-current-buffer buffer
|
||||
;; `kill-current-buffer' is advised not to kill buffers visible in another
|
||||
;; window, so it behaves better than `kill-buffer'.
|
||||
(kill-current-buffer))
|
||||
(centaur-tabs-display-update)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Hooks
|
||||
|
||||
;;;###autoload
|
||||
(defun +tabs-add-buffer-h ()
|
||||
(when (and centaur-tabs-mode
|
||||
(doom-real-buffer-p (current-buffer)))
|
||||
(let* ((this-buf (current-buffer))
|
||||
(buffers (window-parameter nil 'tab-buffers)))
|
||||
(cl-pushnew this-buf buffers)
|
||||
(add-hook 'kill-buffer-hook #'+tabs-remove-buffer-h nil t)
|
||||
(set-window-parameter nil 'tab-buffers buffers))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +tabs-remove-buffer-h ()
|
||||
(when centaur-tabs-mode
|
||||
(set-window-parameter
|
||||
nil
|
||||
'tab-buffers (delete (current-buffer)
|
||||
(window-parameter nil 'tab-buffers)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +tabs-new-window-h ()
|
||||
(when centaur-tabs-mode
|
||||
(unless (window-parameter nil 'tab-buffers)
|
||||
(+tabs-add-buffer-h))))
|
||||
71
.emacs.d/modules/ui/tabs/config.el
Normal file
71
.emacs.d/modules/ui/tabs/config.el
Normal file
@@ -0,0 +1,71 @@
|
||||
;;; ui/tabs/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! centaur-tabs
|
||||
:after-call after-find-file dired-initial-position-hook
|
||||
:init
|
||||
(setq centaur-tabs-height 28
|
||||
centaur-tabs-set-bar 'left
|
||||
centaur-tabs-set-modified-marker t)
|
||||
|
||||
:config
|
||||
(add-hook! 'centaur-tabs-mode-hook
|
||||
(defun +tabs-init-frames-h ()
|
||||
(dolist (frame (frame-list))
|
||||
(if (not centaur-tabs-mode)
|
||||
(set-frame-parameter frame 'buffer-predicate (frame-parameter frame 'old-buffer-predicate))
|
||||
(set-frame-parameter frame 'old-buffer-predicate (frame-parameter frame 'buffer-predicate))
|
||||
(set-frame-parameter frame 'buffer-predicate #'+tabs-buffer-predicate)))))
|
||||
|
||||
(add-to-list 'window-persistent-parameters '(tab-buffers . t))
|
||||
|
||||
(defun +tabs-window-buffer-list-fn ()
|
||||
(centaur-tabs-filter-out
|
||||
'centaur-tabs-hide-tab-cached
|
||||
(delq nil
|
||||
(cl-mapcar #'(lambda (b)
|
||||
(cond
|
||||
;; Always include the current buffer.
|
||||
((eq (current-buffer) b) b)
|
||||
((buffer-file-name b) b)
|
||||
((char-equal ?\ (aref (buffer-name b) 0)) nil)
|
||||
((buffer-live-p b) b)))
|
||||
(window-parameter nil 'tab-buffers)))))
|
||||
|
||||
(defun +tabs-buffer-groups-fn ()
|
||||
(list
|
||||
(cond ((or (string-equal "*" (substring (buffer-name) 0 1))
|
||||
(memq major-mode '(magit-process-mode
|
||||
magit-status-mode
|
||||
magit-diff-mode
|
||||
magit-log-mode
|
||||
magit-file-mode
|
||||
magit-blob-mode
|
||||
magit-blame-mode
|
||||
)))
|
||||
"Emacs")
|
||||
((derived-mode-p 'eshell-mode)
|
||||
"EShell")
|
||||
((derived-mode-p 'dired-mode)
|
||||
"Dired")
|
||||
((centaur-tabs-get-group-name (current-buffer))))))
|
||||
|
||||
(setq centaur-tabs-buffer-list-function #'+tabs-window-buffer-list-fn
|
||||
centaur-tabs-buffer-groups-function #'+tabs-buffer-groups-fn)
|
||||
|
||||
(advice-add #'centaur-tabs-buffer-close-tab :override #'+tabs-kill-tab-maybe-a)
|
||||
(advice-add #'bury-buffer :around #'+tabs-bury-buffer-a)
|
||||
(advice-add #'kill-current-buffer :before #'+tabs-kill-current-buffer-a)
|
||||
(add-hook 'doom-switch-buffer-hook #'+tabs-add-buffer-h)
|
||||
(add-hook 'doom-switch-window-hook #'+tabs-new-window-h)
|
||||
|
||||
(add-hook '+doom-dashboard-mode-hook #'centaur-tabs-local-mode)
|
||||
|
||||
(map! (:map centaur-tabs-mode-map
|
||||
[remap delete-window] #'+tabs/close-tab-or-window
|
||||
[remap +workspace/close-window-or-workspace] #'+tabs/close-tab-or-window)
|
||||
(:after persp-mode
|
||||
:map persp-mode-map
|
||||
[remap delete-window] #'+tabs/close-tab-or-window
|
||||
[remap +workspace/close-window-or-workspace] #'+tabs/close-tab-or-window))
|
||||
|
||||
(centaur-tabs-mode +1))
|
||||
4
.emacs.d/modules/ui/tabs/packages.el
Normal file
4
.emacs.d/modules/ui/tabs/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/tabs/packages.el
|
||||
|
||||
(package! centaur-tabs)
|
||||
41
.emacs.d/modules/ui/treemacs/autoload.el
Normal file
41
.emacs.d/modules/ui/treemacs/autoload.el
Normal file
@@ -0,0 +1,41 @@
|
||||
;;; ui/treemacs/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defun +treemacs--init ()
|
||||
(require 'treemacs)
|
||||
(let ((origin-buffer (current-buffer)))
|
||||
(cl-letf (((symbol-function 'treemacs-workspace->is-empty?)
|
||||
(symbol-function 'ignore)))
|
||||
(treemacs--init))
|
||||
(dolist (project (treemacs-workspace->projects (treemacs-current-workspace)))
|
||||
(treemacs-do-remove-project-from-workspace project))
|
||||
(with-current-buffer origin-buffer
|
||||
(let ((project-root (or (doom-project-root) default-directory)))
|
||||
(treemacs-do-add-project-to-workspace
|
||||
(treemacs--canonical-path project-root)
|
||||
(doom-project-name project-root)))
|
||||
(setq treemacs--ready-to-follow t)
|
||||
(when (or treemacs-follow-after-init treemacs-follow-mode)
|
||||
(treemacs--follow)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +treemacs/toggle ()
|
||||
"Initialize or toggle treemacs.
|
||||
|
||||
Ensures that only the current project is present and all other projects have
|
||||
been removed.
|
||||
|
||||
Use `treemacs' command for old functionality."
|
||||
(interactive)
|
||||
(require 'treemacs)
|
||||
(pcase (treemacs-current-visibility)
|
||||
(`visible (delete-window (treemacs-get-local-window)))
|
||||
(_ (+treemacs--init))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +treemacs/find-file (arg)
|
||||
"Open treemacs (if necessary) and find current file."
|
||||
(interactive "P")
|
||||
(let ((origin-buffer (current-buffer)))
|
||||
(+treemacs--init)
|
||||
(with-current-buffer origin-buffer
|
||||
(treemacs-find-file arg))))
|
||||
45
.emacs.d/modules/ui/treemacs/config.el
Normal file
45
.emacs.d/modules/ui/treemacs/config.el
Normal file
@@ -0,0 +1,45 @@
|
||||
;;; ui/treemacs/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(setq treemacs-follow-after-init t
|
||||
treemacs-is-never-other-window t
|
||||
treemacs-sorting 'alphabetic-case-insensitive-asc
|
||||
treemacs-persist-file (concat doom-cache-dir "treemacs-persist")
|
||||
treemacs-last-error-persist-file (concat doom-cache-dir "treemacs-last-error-persist"))
|
||||
|
||||
|
||||
(after! treemacs
|
||||
(set-popup-rule! "^ \\*Treemacs"
|
||||
:side treemacs-position
|
||||
:size treemacs-width
|
||||
:quit nil
|
||||
:ttl 0)
|
||||
|
||||
;; Don't follow the cursor
|
||||
(treemacs-follow-mode -1)
|
||||
|
||||
;; Allow ace-window to target treemacs windows
|
||||
(after! ace-window
|
||||
(delq! 'treemacs-mode aw-ignored-buffers)))
|
||||
|
||||
|
||||
(use-package! treemacs-evil
|
||||
:when (featurep! :editor evil +everywhere)
|
||||
:after treemacs
|
||||
:config
|
||||
(define-key! evil-treemacs-state-map
|
||||
[return] #'treemacs-RET-action
|
||||
[tab] #'treemacs-TAB-action
|
||||
"TAB" #'treemacs-TAB-action
|
||||
;; REVIEW Fix #1875 to be consistent with C-w {v,s}, but this should really
|
||||
;; be considered upstream.
|
||||
"o v" #'treemacs-visit-node-horizontal-split
|
||||
"o s" #'treemacs-visit-node-vertical-split))
|
||||
|
||||
|
||||
(use-package! treemacs-projectile
|
||||
:after treemacs)
|
||||
|
||||
|
||||
(use-package! treemacs-magit
|
||||
:when (featurep! :tools magit)
|
||||
:after treemacs magit)
|
||||
9
.emacs.d/modules/ui/treemacs/packages.el
Normal file
9
.emacs.d/modules/ui/treemacs/packages.el
Normal file
@@ -0,0 +1,9 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/treemacs/packages.el
|
||||
|
||||
(package! treemacs)
|
||||
(when (featurep! :editor evil +everywhere)
|
||||
(package! treemacs-evil))
|
||||
(package! treemacs-projectile)
|
||||
(when (featurep! :tools magit)
|
||||
(package! treemacs-magit))
|
||||
10
.emacs.d/modules/ui/unicode/README.org
Normal file
10
.emacs.d/modules/ui/unicode/README.org
Normal file
@@ -0,0 +1,10 @@
|
||||
#+TITLE: :ui unicode
|
||||
|
||||
This unicode extends Doom's ability to display non-English unicode.
|
||||
|
||||
This is for non-English Emacs users, for whom Doom's built-in unicode support in insufficient.
|
||||
|
||||
When this module is enabled...
|
||||
|
||||
+ The first time you run Emacs a unicode cache will be generated -- this will take a while!
|
||||
+ Doom will ignore the ~doom-unicode-font~ variable and the ~:unicode-font~ setting.
|
||||
21
.emacs.d/modules/ui/unicode/autoload.el
Normal file
21
.emacs.d/modules/ui/unicode/autoload.el
Normal file
@@ -0,0 +1,21 @@
|
||||
;;; ui/unicode/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(add-hook! 'doom-init-ui-hook
|
||||
(defun +unicode-init-fonts-h ()
|
||||
"Set up `unicode-fonts' to eventually run; accommodating the daemon, if
|
||||
necessary."
|
||||
(setq-default bidi-display-reordering t
|
||||
doom-unicode-font nil)
|
||||
(if initial-window-system
|
||||
(+unicode-setup-fonts-h (selected-frame))
|
||||
(add-hook 'after-make-frame-functions #'+unicode-setup-fonts-h))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +unicode-setup-fonts-h (&optional frame)
|
||||
"Initialize `unicode-fonts', if in a GUI session."
|
||||
(when (and frame (display-graphic-p frame))
|
||||
(with-selected-frame frame
|
||||
(require 'unicode-fonts)
|
||||
;; NOTE will impact startup time on first run
|
||||
(unicode-fonts-setup))))
|
||||
4
.emacs.d/modules/ui/unicode/packages.el
Normal file
4
.emacs.d/modules/ui/unicode/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/unicode/packages.el
|
||||
|
||||
(package! unicode-fonts)
|
||||
27
.emacs.d/modules/ui/vc-gutter/autoload.el
Normal file
27
.emacs.d/modules/ui/vc-gutter/autoload.el
Normal file
@@ -0,0 +1,27 @@
|
||||
;;; ui/vc-gutter/autoload.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :ui hydra)
|
||||
|
||||
;;;###autoload (autoload '+vc/gutter-hydra/body "ui/vc-gutter/autoload" nil t)
|
||||
(defhydra +vc/gutter-hydra
|
||||
(:body-pre (git-gutter-mode 1) :hint nil)
|
||||
"
|
||||
[git gutter]
|
||||
Movement Hunk Actions Misc. +%-4s(car (git-gutter:statistic))/ -%-4s(cdr (git-gutter:statistic))
|
||||
╭──────────────────────────────────┴────────────────╯
|
||||
^_g_^ [_s_] stage [_R_] set start Rev
|
||||
^_k_^ [_r_] revert
|
||||
^↑ ^ [_m_] mark
|
||||
^↓ ^ [_p_] popup ╭─────────────────────
|
||||
^_j_^ │[_q_] quit
|
||||
^_G_^ │[_Q_] Quit and disable"
|
||||
("j" (progn (git-gutter:next-hunk 1) (recenter)))
|
||||
("k" (progn (git-gutter:previous-hunk 1) (recenter)))
|
||||
("g" (progn (goto-char (point-min)) (git-gutter:next-hunk 1)))
|
||||
("G" (progn (goto-char (point-min)) (git-gutter:previous-hunk 1)))
|
||||
("s" git-gutter:stage-hunk)
|
||||
("r" git-gutter:revert-hunk)
|
||||
("m" git-gutter:mark-hunk)
|
||||
("p" git-gutter:popup-hunk)
|
||||
("R" git-gutter:set-start-revision)
|
||||
("q" nil :color blue)
|
||||
("Q" (git-gutter-mode -1) :color blue))
|
||||
111
.emacs.d/modules/ui/vc-gutter/config.el
Normal file
111
.emacs.d/modules/ui/vc-gutter/config.el
Normal file
@@ -0,0 +1,111 @@
|
||||
;;; ui/vc-gutter/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +vc-gutter-in-margin nil
|
||||
"If non-nil, use the margin for diffs instead of the fringe.")
|
||||
|
||||
(defvar +vc-gutter-in-remote-files nil
|
||||
"If non-nil, enable the vc gutter in remote files (e.g. open through TRAMP).")
|
||||
|
||||
(defvar +vc-gutter-diff-unsaved-buffer nil
|
||||
"If non-nil, `diff-hl-flydiff-mode' will be activated. This allows on-the-fly
|
||||
diffing, even for unsaved buffers.")
|
||||
|
||||
(defvar +vc-gutter-default-style t
|
||||
"If non-nil, enable the default look of the vc gutter. This means subtle thin
|
||||
bitmaps on the left, an arrow bitmap for flycheck, and flycheck indicators moved
|
||||
to the right fringe.")
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(use-package! git-gutter
|
||||
:commands git-gutter:revert-hunk git-gutter:stage-hunk
|
||||
:init
|
||||
(add-hook! 'find-file-hook
|
||||
(defun +vc-gutter-init-maybe-h ()
|
||||
"Enable `git-gutter-mode' in the current buffer.
|
||||
|
||||
If the buffer doesn't represent an existing file, `git-gutter-mode's activation
|
||||
is deferred until the file is saved. Respects `git-gutter:disabled-modes'."
|
||||
(when (or +vc-gutter-in-remote-files
|
||||
(not (file-remote-p (or buffer-file-name default-directory))))
|
||||
(if (not buffer-file-name)
|
||||
(add-hook 'after-save-hook #'+vc-gutter-init-maybe-h nil 'local)
|
||||
(when (and (vc-backend buffer-file-name)
|
||||
(progn
|
||||
(require 'git-gutter)
|
||||
(not (memq major-mode git-gutter:disabled-modes))))
|
||||
(if (and (display-graphic-p)
|
||||
(require 'git-gutter-fringe nil t))
|
||||
(progn
|
||||
(setq-local git-gutter:init-function #'git-gutter-fr:init)
|
||||
(setq-local git-gutter:view-diff-function #'git-gutter-fr:view-diff-infos)
|
||||
(setq-local git-gutter:clear-function #'git-gutter-fr:clear)
|
||||
(setq-local git-gutter:window-width -1))
|
||||
(setq-local git-gutter:init-function 'nil)
|
||||
(setq-local git-gutter:view-diff-function #'git-gutter:view-diff-infos)
|
||||
(setq-local git-gutter:clear-function #'git-gutter:clear-diff-infos)
|
||||
(setq-local git-gutter:window-width 1))
|
||||
(git-gutter-mode +1)
|
||||
(remove-hook 'after-save-hook #'+vc-gutter-init-maybe-h 'local))))))
|
||||
|
||||
;; Disable in Org mode, as per
|
||||
;; <https://github.com/syl20bnr/spacemacs/issues/10555> and
|
||||
;; <https://github.com/syohex/emacs-git-gutter/issues/24>. Apparently, the
|
||||
;; mode-enabling function for global minor modes gets called for new buffers
|
||||
;; while they are still in `fundamental-mode', before a major mode has been
|
||||
;; assigned. I don't know why this is the case, but adding `fundamental-mode'
|
||||
;; here fixes the issue.
|
||||
(setq git-gutter:disabled-modes '(fundamental-mode image-mode pdf-view-mode))
|
||||
|
||||
;; standardize default fringe width
|
||||
(if (fboundp 'fringe-mode) (fringe-mode '4))
|
||||
:config
|
||||
(set-popup-rule! "^\\*git-gutter" :select nil :size '+popup-shrink-to-fit)
|
||||
|
||||
;; Update git-gutter on focus (in case I was using git externally)
|
||||
(add-hook 'focus-in-hook #'git-gutter:update-all-windows)
|
||||
|
||||
(add-hook! '(doom-escape-hook doom-switch-window-hook) :append
|
||||
(defun +vc-gutter-update-h (&rest _)
|
||||
"Refresh git-gutter on ESC. Return nil to prevent shadowing other
|
||||
`doom-escape-hook' hooks."
|
||||
(when (and git-gutter-mode
|
||||
(not (memq this-command '(git-gutter:stage-hunk
|
||||
git-gutter:revert-hunk))))
|
||||
(ignore (git-gutter)))))
|
||||
;; update git-gutter when using magit commands
|
||||
(advice-add #'magit-stage-file :after #'+vc-gutter-update-h)
|
||||
(advice-add #'magit-unstage-file :after #'+vc-gutter-update-h)
|
||||
|
||||
(defadvice! +vc-gutter--fix-linearity-of-hunks-a (diffinfos is-reverse)
|
||||
"Fixes `git-gutter:next-hunk' and `git-gutter:previous-hunk' sometimes
|
||||
jumping to random hunks."
|
||||
:override #'git-gutter:search-near-diff-index
|
||||
(cl-position-if (let ((lineno (line-number-at-pos)))
|
||||
(lambda (line)
|
||||
(funcall (if is-reverse #'> #'<) lineno line)))
|
||||
diffinfos
|
||||
:key #'git-gutter-hunk-start-line
|
||||
:from-end is-reverse)))
|
||||
|
||||
|
||||
;; subtle diff indicators in the fringe
|
||||
(when +vc-gutter-default-style
|
||||
(after! git-gutter-fringe
|
||||
;; places the git gutter outside the margins.
|
||||
(setq-default fringes-outside-margins t)
|
||||
;; thin fringe bitmaps
|
||||
(define-fringe-bitmap 'git-gutter-fr:added [224]
|
||||
nil nil '(center repeated))
|
||||
(define-fringe-bitmap 'git-gutter-fr:modified [224]
|
||||
nil nil '(center repeated))
|
||||
(define-fringe-bitmap 'git-gutter-fr:deleted [128 192 224 240]
|
||||
nil nil 'bottom)
|
||||
;; let diff have left fringe, flycheck can have right fringe
|
||||
(after! flycheck
|
||||
(setq flycheck-indication-mode 'right-fringe)
|
||||
;; A non-descript, left-pointing arrow
|
||||
(define-fringe-bitmap 'flycheck-fringe-bitmap-double-arrow
|
||||
[16 48 112 240 112 48 16] nil nil 'center))))
|
||||
4
.emacs.d/modules/ui/vc-gutter/packages.el
Normal file
4
.emacs.d/modules/ui/vc-gutter/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/vc-gutter/packages.el
|
||||
|
||||
(package! git-gutter-fringe)
|
||||
5
.emacs.d/modules/ui/vi-tilde-fringe/autoload.el
Normal file
5
.emacs.d/modules/ui/vi-tilde-fringe/autoload.el
Normal file
@@ -0,0 +1,5 @@
|
||||
;;; ui/vi-tilde-fringe/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(add-hook! '(prog-mode-hook text-mode-hook conf-mode-hook)
|
||||
#'vi-tilde-fringe-mode)
|
||||
4
.emacs.d/modules/ui/vi-tilde-fringe/packages.el
Normal file
4
.emacs.d/modules/ui/vi-tilde-fringe/packages.el
Normal file
@@ -0,0 +1,4 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/vi-tilde-fringe/packages.el
|
||||
|
||||
(package! vi-tilde-fringe)
|
||||
57
.emacs.d/modules/ui/window-select/README.org
Normal file
57
.emacs.d/modules/ui/window-select/README.org
Normal file
@@ -0,0 +1,57 @@
|
||||
#+TITLE: ui/window-select
|
||||
#+DATE: October 8, 2017
|
||||
#+SINCE: v2.0.7
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#packages][Packages]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
- [[#features][Features]]
|
||||
- [[#configuration][Configuration]]
|
||||
- [[#ace-window][ace-window]]
|
||||
- [[#switch-window][switch-window]]
|
||||
|
||||
* Description
|
||||
This module provides several methods for selecting windows without the use of
|
||||
the mouse or spatial navigation (e.g. =C-w {h,j,k,l}=).
|
||||
|
||||
The command ~other-window~ is remapped to either ~switch-window~ or
|
||||
~ace-window~, depending on which backend you've enabled. It is bound to ~C-x o~
|
||||
(and ~C-w C-w~ for evil users).
|
||||
|
||||
It also provides numbered windows and selection with the ~winum~ package, if
|
||||
desired. Evil users can jump to window N in =C-w <N>= (where N is a number
|
||||
between 0 and 9). Non evil users have =C-x w <N>= instead.
|
||||
|
||||
** Module Flags
|
||||
+ =+switch-window= Use the switch-window package as the backend, instead of
|
||||
ace-window (avy).
|
||||
+ =+numbers= Enable numbered windows and window selection (using winum).
|
||||
|
||||
** Packages
|
||||
+ [[https://github.com/dimitri/switch-window][switch-window]] (if =+switch-window=)
|
||||
+ [[https://github.com/abo-abo/ace-window][ace-window]] (if =+switch-window= isn't enabled)
|
||||
+ [[https://github.com/deb0ch/emacs-winum][winum]] (if =+numbers=)
|
||||
|
||||
* Prerequisites
|
||||
This module has no additional dependencies.
|
||||
|
||||
* TODO Features
|
||||
* Configuration
|
||||
This module provides two backends, both providing the same functionality, but
|
||||
with different visual cues. They are =ace-window= and =switch-window=.
|
||||
|
||||
** ace-window
|
||||
The first character of the buffers changes to a highlighted, user-selectable
|
||||
character.
|
||||
|
||||
+ Pros: the content of the buffers are always visible.
|
||||
+ Cons: The displayed characters are small and difficult to see.
|
||||
|
||||
** switch-window
|
||||
Replaces the entire buffer with large letters.
|
||||
|
||||
+ Pros: The displayed characters are /really/ easy to see.
|
||||
+ Cons: You can't see the contents of the buffers.
|
||||
39
.emacs.d/modules/ui/window-select/config.el
Normal file
39
.emacs.d/modules/ui/window-select/config.el
Normal file
@@ -0,0 +1,39 @@
|
||||
;;; ui/window-select/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(use-package! switch-window
|
||||
:when (featurep! +switch-window)
|
||||
:defer t
|
||||
:init
|
||||
(global-set-key [remap other-window] #'switch-window)
|
||||
:config
|
||||
(setq switch-window-shortcut-style 'qwerty
|
||||
switch-window-qwerty-shortcuts '("a" "s" "d" "f" "g" "h" "j" "k" "l")))
|
||||
|
||||
|
||||
(use-package! ace-window
|
||||
:unless (featurep! +switch-window)
|
||||
:defer t
|
||||
:init
|
||||
(global-set-key [remap other-window] #'ace-window)
|
||||
:config
|
||||
(setq aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l)
|
||||
aw-scope 'frame
|
||||
aw-background t))
|
||||
|
||||
|
||||
(use-package! winum
|
||||
:when (featurep! +numbers)
|
||||
:after-call doom-switch-window-hook
|
||||
:config
|
||||
(winum-mode +1)
|
||||
(map! :map evil-window-map
|
||||
"0" #'winum-select-window-0-or-10
|
||||
"1" #'winum-select-window-1
|
||||
"2" #'winum-select-window-2
|
||||
"3" #'winum-select-window-3
|
||||
"4" #'winum-select-window-4
|
||||
"5" #'winum-select-window-5
|
||||
"6" #'winum-select-window-6
|
||||
"7" #'winum-select-window-7
|
||||
"8" #'winum-select-window-8
|
||||
"9" #'winum-select-window-9))
|
||||
9
.emacs.d/modules/ui/window-select/packages.el
Normal file
9
.emacs.d/modules/ui/window-select/packages.el
Normal file
@@ -0,0 +1,9 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/window-select/packages.el
|
||||
|
||||
(if (featurep! +switch-window)
|
||||
(package! switch-window)
|
||||
(package! ace-window))
|
||||
|
||||
(when (featurep! +numbers)
|
||||
(package! winum))
|
||||
104
.emacs.d/modules/ui/workspaces/README.org
Normal file
104
.emacs.d/modules/ui/workspaces/README.org
Normal file
@@ -0,0 +1,104 @@
|
||||
#+TITLE: ui/workspaces
|
||||
#+DATE: February 4, 2017
|
||||
#+SINCE: v1.3
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[#description][Description]]
|
||||
- [[#module-flags][Module Flags]]
|
||||
- [[#packages][Packages]]
|
||||
- [[#prerequisites][Prerequisites]]
|
||||
- [[#features][Features]]
|
||||
- [[#isolated-buffer-list][Isolated buffer-list]]
|
||||
- [[#automatic-workspaces][Automatic workspaces]]
|
||||
- [[#session-persistence][Session persistence]]
|
||||
- [[#workspace-persistence][Workspace persistence]]
|
||||
- [[#appendix][Appendix]]
|
||||
- [[#commands--keybindings][Commands & Keybindings]]
|
||||
- [[#api][API]]
|
||||
|
||||
* Description
|
||||
This module adds support for workspaces, powered by persp_mode, as well as a API
|
||||
for manipulating them.
|
||||
|
||||
#+begin_quote
|
||||
There are many ways to use workspaces. I spawn a workspace per task. Say I'm
|
||||
working in the main workspace, when I realize there is a bug in another part of
|
||||
my project. I open a new workspace and deal with it in there. In the meantime, I
|
||||
need to check my email, so mu4e gets its own workspace.
|
||||
|
||||
Once I've completed the task, I close the workspace and return to main.
|
||||
#+end_quote
|
||||
|
||||
** Module Flags
|
||||
This module provides no flags.
|
||||
|
||||
** Packages
|
||||
+ [[https://github.com/Bad-ptr/persp-mode.el][persp-mode]]
|
||||
|
||||
* Prerequisites
|
||||
This module has no additional dependencies.
|
||||
|
||||
* Features
|
||||
** Isolated buffer-list
|
||||
When persp-mode is active, ~doom-buffer-list~ becomes workspace-restricted. You
|
||||
can overcome this by using ~buffer-list~.
|
||||
|
||||
** Automatic workspaces
|
||||
A workspace is automatically created (and switched to) when you:
|
||||
|
||||
+ Create a new frame (with =make-frame=; bound to =M-N= by default).
|
||||
+ Switch to a project using ~projectile-switch-project~.
|
||||
|
||||
** Session persistence
|
||||
By default, your session is autosaved when you quit Emacs (or disable
|
||||
~persp-mode~). You can load a previous session with ~M-x
|
||||
+workspace/load-session~ or ~:sl[oad]~ (ex command).
|
||||
|
||||
You can supply either a name to load a specific session to replace your current
|
||||
one.
|
||||
|
||||
** Workspace persistence
|
||||
If you'd like to save a specific workspace, use ~M-x +workspace/save~, which can
|
||||
be loaded into the current session (as another workspace) with ~M-x
|
||||
+workspace/load~.
|
||||
|
||||
* Appendix
|
||||
** Commands & Keybindings
|
||||
Here is a list of available commands, their default keybindings (defined in
|
||||
[[../../config/default/+evil-bindings.el][private/default/+bindings.el]]), and corresponding ex commands (if any -- defined
|
||||
in [[../../editor/evil/+commands.el][private/default/+evil-commands.el]]).
|
||||
|
||||
| command | key / ex command | description |
|
||||
|-----------------------------------+----------------------------+------------------------------------------------------------|
|
||||
| ~+workspace/new~ | =SPC TAB n= | Create a new, blank workspace |
|
||||
| ~+workspace/display~ | =SPC TAB TAB= | Display open workspaces in the mode-line |
|
||||
| ~+workspace/load~ | =SPC TAB l= | Load a saved workspace into the current session |
|
||||
| ~+workspace/restore-last-session~ | =SPC TAB R= | Restore last session |
|
||||
| ~+workspace/rename~ | =SPC TAB r= | Rename the current workspace |
|
||||
| ~+workspace/save~ | =SPC TAB s= | Save the current workspace to a file |
|
||||
| ~+workspace/switch-to~ | =SPC TAB .= | Switch to an open workspace |
|
||||
| ~+workspace/other~ | =SPC TAB `= | Switch to last workspace |
|
||||
| ~+workspace/switch-left~ | =SPC TAB [= / =[ w= / =gT= | Switch to previous workspace |
|
||||
| ~+workspace/switch-right~ | =SPC TAB ]= / =] w= / =gt= | Switch to next workspace |
|
||||
| ~+workspace/delete~ | =SPC TAB d= | Delete the current workspace |
|
||||
| ~+workspace/kill-session~ | =SPC TAB x= / =:sclear= | Clears the current session (kills all windows and buffers) |
|
||||
|
||||
** API
|
||||
+ ~+workspace-list~ -> list<Struct>
|
||||
+ ~+workspace-list-names~ -> list<string>
|
||||
+ ~+workspace-buffer-list &optional PERSP~ -> bool
|
||||
+ ~+workspace-p OBJ~ -> bool
|
||||
+ ~+workspace-exists-p NAME~ -> bool
|
||||
+ ~+workspace-get NAME &optional NOERROR~ -> Struct
|
||||
+ ~+workspace-current &optional FRAME WINDOW~ -> Struct
|
||||
+ ~+workspace-current-name~ -> string
|
||||
+ ~+workspace-load NAME~
|
||||
+ ~+workspace-load-session NAME~
|
||||
+ ~+workspace-save NAME~
|
||||
+ ~+workspace-save-session NAME~
|
||||
+ ~+workspace-new NAME~
|
||||
+ ~+workspace-rename NAME NEW-NAME~
|
||||
+ ~+workspace-delete NAME &optional INHIBIT-KILL-P~
|
||||
+ ~+workspace-switch NAME &optional AUTO-CREATE-P~
|
||||
+ ~+workspace-protected-p NAME~ -> bool
|
||||
39
.emacs.d/modules/ui/workspaces/autoload/evil.el
Normal file
39
.emacs.d/modules/ui/workspaces/autoload/evil.el
Normal file
@@ -0,0 +1,39 @@
|
||||
;;; ui/workspaces/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :editor evil)
|
||||
|
||||
;;;###autoload (autoload '+workspace:save "ui/workspaces/autoload/evil" nil t)
|
||||
(evil-define-command +workspace:save (&optional name)
|
||||
"Ex wrapper around `+workspace/save-session'."
|
||||
(interactive "<a>") (+workspace/save name))
|
||||
|
||||
;;;###autoload (autoload '+workspace:load "ui/workspaces/autoload/evil" nil t)
|
||||
(evil-define-command +workspace:load (&optional name)
|
||||
"Ex wrapper around `+workspace/load-session'."
|
||||
(interactive "<a>") (+workspace/load name))
|
||||
|
||||
;;;###autoload (autoload '+workspace:new "ui/workspaces/autoload/evil" nil t)
|
||||
(evil-define-command +workspace:new (bang name)
|
||||
"Ex wrapper around `+workspace/new'. If BANG, clone the current workspace."
|
||||
(interactive "<!><a>") (+workspace/new name bang))
|
||||
|
||||
;;;###autoload (autoload '+workspace:rename "ui/workspaces/autoload/evil" nil t)
|
||||
(evil-define-command +workspace:rename (new-name)
|
||||
"Ex wrapper around `+workspace/rename'."
|
||||
(interactive "<a>") (+workspace/rename new-name))
|
||||
|
||||
;;;###autoload (autoload '+workspace:delete "ui/workspaces/autoload/evil" nil t)
|
||||
(evil-define-command +workspace:delete ()
|
||||
"Ex wrapper around `+workspace/delete'."
|
||||
(interactive) (+workspace/delete (+workspace-current-name)))
|
||||
|
||||
;;;###autoload (autoload '+workspace:switch-next "ui/workspaces/autoload/evil" nil t)
|
||||
(evil-define-command +workspace:switch-next (&optional count)
|
||||
"Switch to next workspace. If COUNT, switch to COUNT-th workspace."
|
||||
(interactive "<c>")
|
||||
(if count (+workspace/switch-to count) (+workspace/cycle +1)))
|
||||
|
||||
;;;###autoload (autoload '+workspace:switch-previous "ui/workspaces/autoload/evil" nil t)
|
||||
(evil-define-command +workspace:switch-previous (&optional count)
|
||||
"Switch to previous workspace. If COUNT, switch to COUNT-th workspace."
|
||||
(interactive "<c>")
|
||||
(if count (+workspace/switch-to count) (+workspace/cycle -1)))
|
||||
531
.emacs.d/modules/ui/workspaces/autoload/workspaces.el
Normal file
531
.emacs.d/modules/ui/workspaces/autoload/workspaces.el
Normal file
@@ -0,0 +1,531 @@
|
||||
;;; feature/workspaces/autoload/workspaces.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +workspace--last nil)
|
||||
(defvar +workspace--index 0)
|
||||
|
||||
;;;###autoload
|
||||
(defface +workspace-tab-selected-face '((t (:inherit highlight)))
|
||||
"The face for selected tabs displayed by `+workspace/display'"
|
||||
:group 'persp-mode)
|
||||
|
||||
;;;###autoload
|
||||
(defface +workspace-tab-face '((t (:inherit default)))
|
||||
"The face for selected tabs displayed by `+workspace/display'"
|
||||
:group 'persp-mode)
|
||||
|
||||
|
||||
;;
|
||||
;;; Library
|
||||
|
||||
(defun +workspace--protected-p (name)
|
||||
(equal name persp-nil-name))
|
||||
|
||||
(defun +workspace--generate-id ()
|
||||
(or (cl-loop for name in (+workspace-list-names)
|
||||
when (string-match-p "^#[0-9]+$" name)
|
||||
maximize (string-to-number (substring name 1)) into max
|
||||
finally return (if max (1+ max)))
|
||||
1))
|
||||
|
||||
|
||||
;;; Predicates
|
||||
;;;###autoload
|
||||
(defalias #'+workspace-p #'perspective-p
|
||||
"Return t if OBJ is a perspective hash table.")
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-exists-p (name)
|
||||
"Returns t if NAME is the name of an existing workspace."
|
||||
(member name (+workspace-list-names)))
|
||||
|
||||
;;;###autoload
|
||||
(defalias #'+workspace-contains-buffer-p #'persp-contain-buffer-p
|
||||
"Return non-nil if BUFFER is in WORKSPACE (defaults to current workspace).")
|
||||
|
||||
|
||||
;;; Getters
|
||||
;;;###autoload
|
||||
(defalias #'+workspace-current #'get-current-persp
|
||||
"Return the currently active workspace.")
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-get (name &optional noerror)
|
||||
"Return a workspace named NAME. Unless NOERROR is non-nil, this throws an
|
||||
error if NAME doesn't exist."
|
||||
(cl-check-type name string)
|
||||
(when-let (persp (persp-get-by-name name))
|
||||
(cond ((+workspace-p persp) persp)
|
||||
((not noerror)
|
||||
(error "No workspace called '%s' was found" name)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-current-name ()
|
||||
"Get the name of the current workspace."
|
||||
(safe-persp-name (+workspace-current)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-list ()
|
||||
"Return a list of workspace structs (satisifes `+workspace-p')."
|
||||
;; We don't use `hash-table-values' because it doesn't ensure order in older
|
||||
;; versions of Emacs
|
||||
(cdr (cl-loop for persp being the hash-values of *persp-hash*
|
||||
collect persp)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-list-names ()
|
||||
"Return the list of names of open workspaces."
|
||||
(mapcar #'safe-persp-name (+workspace-list)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-buffer-list (&optional persp)
|
||||
"Return a list of buffers in PERSP.
|
||||
|
||||
The buffer list is ordered by recency (same as `buffer-list').
|
||||
|
||||
PERSP can be a string (name of a workspace) or a workspace (satisfies
|
||||
`+workspace-p'). If nil or omitted, it defaults to the current workspace."
|
||||
(let ((persp (or persp (+workspace-current))))
|
||||
(unless (+workspace-p persp)
|
||||
(user-error "Not in a valid workspace (%s)" persp))
|
||||
(persp-buffers persp)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-orphaned-buffer-list ()
|
||||
"Return a list of buffers that aren't associated with any perspective."
|
||||
(cl-remove-if #'persp--buffer-in-persps (buffer-list)))
|
||||
|
||||
|
||||
;;; Actions
|
||||
;;;###autoload
|
||||
(defun +workspace-load (name)
|
||||
"Loads a single workspace (named NAME) into the current session. Can only
|
||||
retrieve perspectives that were explicitly saved with `+workspace-save'.
|
||||
|
||||
Returns t if successful, nil otherwise."
|
||||
(when (+workspace-exists-p name)
|
||||
(user-error "A workspace named '%s' already exists." name))
|
||||
(persp-load-from-file-by-names
|
||||
(expand-file-name +workspaces-data-file persp-save-dir)
|
||||
*persp-hash* (list name))
|
||||
(+workspace-exists-p name))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-save (name)
|
||||
"Saves a single workspace (NAME) from the current session. Can be loaded again
|
||||
with `+workspace-load'. NAME can be the string name of a workspace or its
|
||||
perspective hash table.
|
||||
|
||||
Returns t on success, nil otherwise."
|
||||
(unless (+workspace-exists-p name)
|
||||
(error "'%s' is an invalid workspace" name))
|
||||
(let ((fname (expand-file-name +workspaces-data-file persp-save-dir)))
|
||||
(persp-save-to-file-by-names fname *persp-hash* (list name))
|
||||
(and (member name (persp-list-persp-names-in-file fname))
|
||||
t)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-new (name)
|
||||
"Create a new workspace named NAME. If one already exists, return nil.
|
||||
Otherwise return t on success, nil otherwise."
|
||||
(when (+workspace--protected-p name)
|
||||
(error "Can't create a new '%s' workspace" name))
|
||||
(when (+workspace-exists-p name)
|
||||
(error "A workspace named '%s' already exists" name))
|
||||
(let ((persp (persp-add-new name))
|
||||
(+popup--inhibit-transient t))
|
||||
(save-window-excursion
|
||||
(let ((ignore-window-parameters t)
|
||||
(+popup--inhibit-transient t))
|
||||
(persp-delete-other-windows))
|
||||
(switch-to-buffer (doom-fallback-buffer))
|
||||
(setf (persp-window-conf persp)
|
||||
(funcall persp-window-state-get-function (selected-frame))))
|
||||
persp))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-rename (name new-name)
|
||||
"Rename the current workspace named NAME to NEW-NAME. Returns old name on
|
||||
success, nil otherwise."
|
||||
(when (+workspace--protected-p name)
|
||||
(error "Can't rename '%s' workspace" name))
|
||||
(persp-rename new-name (+workspace-get name)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-delete (workspace &optional inhibit-kill-p)
|
||||
"Delete the workspace denoted by WORKSPACE, which can be the name of a perspective
|
||||
or its hash table. If INHIBIT-KILL-P is non-nil, don't kill this workspace's
|
||||
buffers."
|
||||
(unless (stringp workspace)
|
||||
(setq workspace (persp-name workspace)))
|
||||
(when (+workspace--protected-p workspace)
|
||||
(error "Can't delete '%s' workspace" workspace))
|
||||
(+workspace-get workspace) ; error checking
|
||||
(persp-kill workspace inhibit-kill-p)
|
||||
(not (+workspace-exists-p workspace)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-switch (name &optional auto-create-p)
|
||||
"Switch to another workspace named NAME (a string).
|
||||
|
||||
If AUTO-CREATE-P is non-nil, create the workspace if it doesn't exist, otherwise
|
||||
throws an error."
|
||||
(unless (+workspace-exists-p name)
|
||||
(if auto-create-p
|
||||
(+workspace-new name)
|
||||
(error "%s is not an available workspace" name)))
|
||||
(let ((old-name (+workspace-current-name)))
|
||||
(setq +workspace--last
|
||||
(or (and (not (string= old-name persp-nil-name))
|
||||
old-name)
|
||||
+workspaces-main))
|
||||
(persp-frame-switch name)
|
||||
(equal (+workspace-current-name) name)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defalias '+workspace/restore-last-session #'doom/quickload-session)
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/load (name)
|
||||
"Load a workspace and switch to it. If called with C-u, try to reload the
|
||||
current workspace (by name) from session files."
|
||||
(interactive
|
||||
(list
|
||||
(if current-prefix-arg
|
||||
(+workspace-current-name)
|
||||
(completing-read
|
||||
"Workspace to load: "
|
||||
(persp-list-persp-names-in-file
|
||||
(expand-file-name +workspaces-data-file persp-save-dir))))))
|
||||
(if (not (+workspace-load name))
|
||||
(+workspace-error (format "Couldn't load workspace %s" name))
|
||||
(+workspace/switch-to name)
|
||||
(+workspace/display)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/save (name)
|
||||
"Save the current workspace. If called with C-u, autosave the current
|
||||
workspace."
|
||||
(interactive
|
||||
(list
|
||||
(if current-prefix-arg
|
||||
(+workspace-current-name)
|
||||
(completing-read "Workspace to save: " (+workspace-list-names)))))
|
||||
(if (+workspace-save name)
|
||||
(+workspace-message (format "'%s' workspace saved" name) 'success)
|
||||
(+workspace-error (format "Couldn't save workspace %s" name))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/rename (new-name)
|
||||
"Rename the current workspace."
|
||||
(interactive (list (read-from-minibuffer "New workspace name: ")))
|
||||
(condition-case-unless-debug ex
|
||||
(let* ((current-name (+workspace-current-name))
|
||||
(old-name (+workspace-rename current-name new-name)))
|
||||
(unless old-name
|
||||
(error "Failed to rename %s" current-name))
|
||||
(+workspace-message (format "Renamed '%s'->'%s'" old-name new-name) 'success))
|
||||
('error (+workspace-error ex t))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/delete (name)
|
||||
"Delete this workspace. If called with C-u, prompts you for the name of the
|
||||
workspace to delete."
|
||||
(interactive
|
||||
(let ((current-name (+workspace-current-name)))
|
||||
(list
|
||||
(if current-prefix-arg
|
||||
(completing-read (format "Delete workspace (default: %s): " current-name)
|
||||
(+workspace-list-names)
|
||||
nil nil nil nil current-name)
|
||||
current-name))))
|
||||
(condition-case-unless-debug ex
|
||||
;; REVIEW refactor me
|
||||
(let ((workspaces (+workspace-list-names)))
|
||||
(if (not (member name workspaces))
|
||||
(+workspace-message (format "'%s' workspace doesn't exist" name) 'warn)
|
||||
(cond ((delq (selected-frame) (persp-frames-with-persp (get-frame-persp)))
|
||||
(user-error "Can't close workspace, it's visible in another frame"))
|
||||
((not (equal (+workspace-current-name) name))
|
||||
(+workspace-delete name))
|
||||
((cdr workspaces)
|
||||
(+workspace-delete name)
|
||||
(+workspace-switch
|
||||
(if (+workspace-exists-p +workspace--last)
|
||||
+workspace--last
|
||||
(car (+workspace-list-names))))
|
||||
(unless (doom-buffer-frame-predicate (window-buffer))
|
||||
(switch-to-buffer (doom-fallback-buffer))))
|
||||
(t
|
||||
(+workspace-switch +workspaces-main t)
|
||||
(unless (string= (car workspaces) +workspaces-main)
|
||||
(+workspace-delete name))
|
||||
(doom/kill-all-buffers (doom-buffer-list))))
|
||||
(+workspace-message (format "Deleted '%s' workspace" name) 'success)))
|
||||
('error (+workspace-error ex t))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/kill-session ()
|
||||
"Delete the current session, all workspaces, windows and their buffers."
|
||||
(interactive)
|
||||
(unless (cl-every #'+workspace-delete (+workspace-list-names))
|
||||
(+workspace-error "Could not clear session"))
|
||||
(+workspace-switch +workspaces-main t)
|
||||
(doom/kill-all-buffers (buffer-list)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/kill-session-and-quit ()
|
||||
"Kill emacs without saving anything."
|
||||
(interactive)
|
||||
(let ((persp-auto-save-opt 0))
|
||||
(kill-emacs)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/new (&optional name clone-p)
|
||||
"Create a new workspace named NAME. If CLONE-P is non-nil, clone the current
|
||||
workspace, otherwise the new workspace is blank."
|
||||
(interactive "iP")
|
||||
(unless name
|
||||
(setq name (format "#%s" (+workspace--generate-id))))
|
||||
(condition-case e
|
||||
(cond ((+workspace-exists-p name)
|
||||
(error "%s already exists" name))
|
||||
(clone-p (persp-copy name t))
|
||||
(t
|
||||
(+workspace-switch name t)
|
||||
(+workspace/display)))
|
||||
((debug error) (+workspace-error (cadr e) t))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/switch-to (index)
|
||||
"Switch to a workspace at a given INDEX. A negative number will start from the
|
||||
end of the workspace list."
|
||||
(interactive
|
||||
(list (or current-prefix-arg
|
||||
(if (featurep! :completion ivy)
|
||||
(ivy-read "Switch to workspace: "
|
||||
(+workspace-list-names)
|
||||
:caller #'+workspace/switch-to
|
||||
:preselect (+workspace-current-name))
|
||||
(completing-read "Switch to workspace: " (+workspace-list-names))))))
|
||||
(when (and (stringp index)
|
||||
(string-match-p "^[0-9]+$" index))
|
||||
(setq index (string-to-number index)))
|
||||
(condition-case-unless-debug ex
|
||||
(let ((names (+workspace-list-names))
|
||||
(old-name (+workspace-current-name)))
|
||||
(cond ((numberp index)
|
||||
(let ((dest (nth index names)))
|
||||
(unless dest
|
||||
(error "No workspace at #%s" (1+ index)))
|
||||
(+workspace-switch dest)))
|
||||
((stringp index)
|
||||
(+workspace-switch index t))
|
||||
(t
|
||||
(error "Not a valid index: %s" index)))
|
||||
(unless (called-interactively-p 'interactive)
|
||||
(if (equal (+workspace-current-name) old-name)
|
||||
(+workspace-message (format "Already in %s" old-name) 'warn)
|
||||
(+workspace/display))))
|
||||
('error (+workspace-error (cadr ex) t))))
|
||||
|
||||
;;;###autoload
|
||||
(dotimes (i 9)
|
||||
(defalias (intern (format "+workspace/switch-to-%d" i))
|
||||
(lambda () (interactive) (+workspace/switch-to i))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/switch-to-final ()
|
||||
"Switch to the final workspace in open workspaces."
|
||||
(interactive)
|
||||
(+workspace/switch-to (car (last (+workspace-list-names)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/other ()
|
||||
"Switch to the last activated workspace."
|
||||
(interactive)
|
||||
(+workspace/switch-to +workspace--last))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/cycle (n)
|
||||
"Cycle n workspaces to the right (default) or left."
|
||||
(interactive (list 1))
|
||||
(let ((current-name (+workspace-current-name)))
|
||||
(if (equal current-name persp-nil-name)
|
||||
(+workspace-switch +workspaces-main t)
|
||||
(condition-case-unless-debug ex
|
||||
(let* ((persps (+workspace-list-names))
|
||||
(perspc (length persps))
|
||||
(index (cl-position current-name persps)))
|
||||
(when (= perspc 1)
|
||||
(user-error "No other workspaces"))
|
||||
(+workspace/switch-to (% (+ index n perspc) perspc))
|
||||
(unless (called-interactively-p 'interactive)
|
||||
(+workspace/display)))
|
||||
('user-error (+workspace-error (cadr ex) t))
|
||||
('error (+workspace-error ex t))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/switch-left () (interactive) (+workspace/cycle -1))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/switch-right () (interactive) (+workspace/cycle +1))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/close-window-or-workspace ()
|
||||
"Close the selected window. If it's the last window in the workspace, either
|
||||
close the workspace (as well as its associated frame, if one exists) and move to
|
||||
the next."
|
||||
(interactive)
|
||||
(let ((delete-window-fn (if (featurep 'evil) #'evil-window-delete #'delete-window)))
|
||||
(if (window-dedicated-p)
|
||||
(funcall delete-window-fn)
|
||||
(let ((current-persp-name (+workspace-current-name)))
|
||||
(cond ((or (+workspace--protected-p current-persp-name)
|
||||
(cdr (doom-visible-windows)))
|
||||
(funcall delete-window-fn))
|
||||
|
||||
((cdr (+workspace-list-names))
|
||||
(let ((frame-persp (frame-parameter nil 'workspace)))
|
||||
(if (string= frame-persp (+workspace-current-name))
|
||||
(delete-frame)
|
||||
(+workspace/delete current-persp-name))))
|
||||
|
||||
((+workspace-error "Can't delete last workspace" t)))))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Tabs display in minibuffer
|
||||
|
||||
(defun +workspace--tabline (&optional names)
|
||||
(let ((names (or names (+workspace-list-names)))
|
||||
(current-name (+workspace-current-name)))
|
||||
(mapconcat
|
||||
#'identity
|
||||
(cl-loop for name in names
|
||||
for i to (length names)
|
||||
collect
|
||||
(propertize (format " [%d] %s " (1+ i) name)
|
||||
'face (if (equal current-name name)
|
||||
'+workspace-tab-selected-face
|
||||
'+workspace-tab-face)))
|
||||
" ")))
|
||||
|
||||
(defun +workspace--message-body (message &optional type)
|
||||
(concat (+workspace--tabline)
|
||||
(propertize " | " 'face 'font-lock-comment-face)
|
||||
(propertize (format "%s" message)
|
||||
'face (pcase type
|
||||
('error 'error)
|
||||
('warn 'warning)
|
||||
('success 'success)
|
||||
('info 'font-lock-comment-face)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-message (message &optional type)
|
||||
"Show an 'elegant' message in the echo area next to a listing of workspaces."
|
||||
(message "%s" (+workspace--message-body message type)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace-error (message &optional noerror)
|
||||
"Show an 'elegant' error in the echo area next to a listing of workspaces."
|
||||
(funcall (if noerror #'message #'error)
|
||||
"%s" (+workspace--message-body message 'error)))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspace/display ()
|
||||
"Display a list of workspaces (like tabs) in the echo area."
|
||||
(interactive)
|
||||
(let (message-log-max)
|
||||
(message "%s" (+workspace--tabline))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Hooks
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspaces-delete-associated-workspace-h (&optional frame)
|
||||
"Delete workspace associated with current frame.
|
||||
A workspace gets associated with a frame when a new frame is interactively
|
||||
created."
|
||||
(when persp-mode
|
||||
(unless frame
|
||||
(setq frame (selected-frame)))
|
||||
(let ((frame-persp (frame-parameter frame 'workspace)))
|
||||
(when (string= frame-persp (+workspace-current-name))
|
||||
(+workspace/delete frame-persp)))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspaces-associate-frame-fn (frame &optional _new-frame-p)
|
||||
"Create a blank, new perspective and associate it with FRAME."
|
||||
(when persp-mode
|
||||
(if (not (persp-frame-list-without-daemon))
|
||||
(+workspace-switch +workspaces-main t)
|
||||
(with-selected-frame frame
|
||||
(+workspace-switch (format "#%s" (+workspace--generate-id)) t)
|
||||
(unless (doom-real-buffer-p (current-buffer))
|
||||
(switch-to-buffer (doom-fallback-buffer)))
|
||||
(set-frame-parameter frame 'workspace (+workspace-current-name))
|
||||
;; ensure every buffer has a buffer-predicate
|
||||
(persp-set-frame-buffer-predicate frame))
|
||||
(run-at-time 0.1 nil #'+workspace/display))))
|
||||
|
||||
(defvar +workspaces--project-dir nil)
|
||||
;;;###autoload
|
||||
(defun +workspaces-set-project-action-fn ()
|
||||
"A `projectile-switch-project-action' that sets the project directory for
|
||||
`+workspaces-switch-to-project-h'."
|
||||
(setq +workspaces--project-dir default-directory))
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspaces-switch-to-project-h (&optional dir)
|
||||
"Creates a workspace dedicated to a new project. If one already exists, switch
|
||||
to it. If in the main workspace and it's empty, recycle that workspace, without
|
||||
renaming it.
|
||||
|
||||
Afterwords, runs `+workspaces-switch-project-function'. By default, this prompts
|
||||
the user to open a file in the new project.
|
||||
|
||||
This be hooked to `projectile-after-switch-project-hook'."
|
||||
(when dir
|
||||
(setq +workspaces--project-dir dir))
|
||||
(when (and persp-mode +workspaces--project-dir)
|
||||
(unwind-protect
|
||||
(if (and (not (null +workspaces-on-switch-project-behavior))
|
||||
(or (eq +workspaces-on-switch-project-behavior t)
|
||||
(+workspace-buffer-list)))
|
||||
(let* ((persp
|
||||
(let ((project-name (doom-project-name +workspaces--project-dir)))
|
||||
(or (+workspace-get project-name t)
|
||||
(+workspace-new project-name))))
|
||||
(new-name (persp-name persp)))
|
||||
(+workspace-switch new-name)
|
||||
(with-current-buffer (doom-fallback-buffer)
|
||||
(setq default-directory +workspaces--project-dir))
|
||||
(unless current-prefix-arg
|
||||
(funcall +workspaces-switch-project-function +workspaces--project-dir))
|
||||
(+workspace-message
|
||||
(format "Switched to '%s' in new workspace" new-name)
|
||||
'success))
|
||||
(with-current-buffer (doom-fallback-buffer)
|
||||
(setq default-directory +workspaces--project-dir)
|
||||
(message "Switched to '%s'" (doom-project-name +workspaces--project-dir)))
|
||||
(with-demoted-errors "Workspace error: %s"
|
||||
(+workspace-rename (+workspace-current-name) (doom-project-name +workspaces--project-dir)))
|
||||
(unless current-prefix-arg
|
||||
(funcall +workspaces-switch-project-function +workspaces--project-dir)))
|
||||
(setq +workspaces--project-dir nil))))
|
||||
|
||||
|
||||
;;
|
||||
;;; Advice
|
||||
|
||||
;;;###autoload
|
||||
(defun +workspaces-autosave-real-buffers-a (orig-fn &rest args)
|
||||
"Don't autosave if no real buffers are open."
|
||||
(when (doom-real-buffer-list)
|
||||
(apply orig-fn args))
|
||||
t)
|
||||
222
.emacs.d/modules/ui/workspaces/config.el
Normal file
222
.emacs.d/modules/ui/workspaces/config.el
Normal file
@@ -0,0 +1,222 @@
|
||||
;;; ui/workspaces/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; `persp-mode' gives me workspaces, a workspace-restricted `buffer-list', and
|
||||
;; file-based session persistence. I used workgroups2 before this, but abandoned
|
||||
;; it because it was unstable and slow; `persp-mode' is neither (and still
|
||||
;; maintained).
|
||||
;;
|
||||
;; NOTE persp-mode requires `workgroups' for file persistence in Emacs 24.4.
|
||||
|
||||
(defvar +workspaces-main "main"
|
||||
"The name of the primary and initial workspace, which cannot be deleted.")
|
||||
|
||||
(defvar +workspaces-switch-project-function #'doom-project-find-file
|
||||
"The function to run after `projectile-switch-project' or
|
||||
`counsel-projectile-switch-project'. This function must take one argument: the
|
||||
new project directory.")
|
||||
|
||||
(defvar +workspaces-on-switch-project-behavior 'non-empty
|
||||
"Controls the behavior of workspaces when switching to a new project.
|
||||
|
||||
Can be one of the following:
|
||||
|
||||
t Always create a new workspace for the project
|
||||
'non-empty Only create a new workspace if the current one already has buffers
|
||||
associated with it.
|
||||
nil Never create a new workspace on project switch.")
|
||||
|
||||
;; FIXME actually use this for wconf bookmark system
|
||||
(defvar +workspaces-data-file "_workspaces"
|
||||
"The basename of the file to store single workspace perspectives. Will be
|
||||
stored in `persp-save-dir'.")
|
||||
|
||||
(defvar +workspace--old-uniquify-style nil)
|
||||
|
||||
|
||||
;;
|
||||
;; Packages
|
||||
|
||||
(use-package! persp-mode
|
||||
:commands persp-switch-to-buffer
|
||||
:init
|
||||
(add-hook! 'doom-init-modules-hook
|
||||
(defun +workspaces-init-h ()
|
||||
(unless noninteractive
|
||||
;; Remove default buffer predicate so persp-mode can put in its own
|
||||
(delq! 'buffer-predicate default-frame-alist 'assq)
|
||||
(require 'persp-mode)
|
||||
(if (daemonp)
|
||||
(add-hook 'after-make-frame-functions #'persp-mode-start-and-remove-from-make-frame-hook)
|
||||
(persp-mode +1)))))
|
||||
:config
|
||||
(setq persp-autokill-buffer-on-remove 'kill-weak
|
||||
persp-nil-hidden t
|
||||
persp-auto-save-fname "autosave"
|
||||
persp-save-dir (concat doom-etc-dir "workspaces/")
|
||||
persp-set-last-persp-for-new-frames t
|
||||
persp-switch-to-added-buffer nil
|
||||
persp-remove-buffers-from-nil-persp-behaviour nil
|
||||
persp-auto-resume-time -1 ; Don't auto-load on startup
|
||||
persp-auto-save-opt (if noninteractive 0 1)) ; auto-save on kill
|
||||
|
||||
(advice-add #'persp-asave-on-exit :around #'+workspaces-autosave-real-buffers-a)
|
||||
|
||||
(add-hook! '(persp-mode-hook persp-after-load-state-functions)
|
||||
(defun +workspaces-ensure-main-workspace-h (&rest _)
|
||||
"Ensure the main workspace exists and the nil workspace is never active."
|
||||
(when persp-mode
|
||||
(let (persp-before-switch-functions)
|
||||
;; The default perspective persp-mode creates (`persp-nil-name') is
|
||||
;; special and doesn't 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.
|
||||
(unless (persp-get-by-name +workspaces-main)
|
||||
(persp-add-new +workspaces-main))
|
||||
;; Switch to it if we're in the nil perspective
|
||||
(dolist (frame (frame-list))
|
||||
(when (string= (safe-persp-name (get-current-persp frame)) persp-nil-name)
|
||||
(persp-frame-switch +workspaces-main frame)
|
||||
;; Fix #319: the warnings buffer gets swallowed by creating
|
||||
;; `+workspaces-main', so we display it manually, if it exists.
|
||||
(when-let (warnings (get-buffer "*Warnings*"))
|
||||
(save-excursion
|
||||
(display-buffer-in-side-window
|
||||
warnings '((window-height . shrink-window-if-larger-than-buffer)))))))))))
|
||||
|
||||
(add-hook! 'persp-mode-hook
|
||||
(defun +workspaces-init-persp-mode-h ()
|
||||
(cond (persp-mode
|
||||
;; `uniquify' breaks persp-mode. It renames old buffers, which causes
|
||||
;; errors when switching between perspective (their buffers are
|
||||
;; serialized by name and persp-mode expects them to have the same
|
||||
;; name when restored).
|
||||
(when uniquify-buffer-name-style
|
||||
(setq +workspace--old-uniquify-style uniquify-buffer-name-style))
|
||||
(setq uniquify-buffer-name-style nil)
|
||||
;; Ensure `persp-kill-buffer-query-function' is last
|
||||
(remove-hook 'kill-buffer-query-functions #'persp-kill-buffer-query-function)
|
||||
(add-hook 'kill-buffer-query-functions #'persp-kill-buffer-query-function t)
|
||||
;; Restrict buffer list to workspace
|
||||
(advice-add #'doom-buffer-list :override #'+workspace-buffer-list))
|
||||
(t
|
||||
(when +workspace--old-uniquify-style
|
||||
(setq uniquify-buffer-name-style +workspace--old-uniquify-style))
|
||||
(advice-remove #'doom-buffer-list #'+workspace-buffer-list)))))
|
||||
|
||||
;; We don't rely on the built-in mechanism for auto-registering a buffer to
|
||||
;; the current workspace; some buffers slip through the cracks. Instead, we
|
||||
;; add buffers when they are switched to.
|
||||
(setq persp-add-buffer-on-find-file nil
|
||||
persp-add-buffer-on-after-change-major-mode nil)
|
||||
|
||||
(add-hook! '(doom-switch-buffer-hook server-visit-hook)
|
||||
(defun +workspaces-add-current-buffer-h ()
|
||||
"Add current buffer to focused perspective."
|
||||
(and persp-mode
|
||||
(not (persp-buffer-filtered-out-p
|
||||
(current-buffer)
|
||||
persp-add-buffer-on-after-change-major-mode-filter-functions))
|
||||
(persp-add-buffer (current-buffer) (get-current-persp) nil nil))))
|
||||
|
||||
(add-hook 'persp-add-buffer-on-after-change-major-mode-filter-functions
|
||||
#'doom-unreal-buffer-p)
|
||||
|
||||
(defadvice! +workspaces--evil-alternate-buffer-a (&optional window)
|
||||
"Make `evil-alternate-buffer' ignore buffers outside the current workspace."
|
||||
:override #'evil-alternate-buffer
|
||||
(let* ((prev-buffers
|
||||
(if persp-mode
|
||||
(cl-remove-if-not #'persp-contain-buffer-p (window-prev-buffers)
|
||||
:key #'car)
|
||||
(window-prev-buffers)))
|
||||
(head (car prev-buffers)))
|
||||
(if (eq (car head) (window-buffer window))
|
||||
(cadr prev-buffers)
|
||||
head)))
|
||||
|
||||
;; Delete the current workspace if closing the last open window
|
||||
(define-key! persp-mode-map
|
||||
[remap delete-window] #'+workspace/close-window-or-workspace
|
||||
[remap evil-window-delete] #'+workspace/close-window-or-workspace)
|
||||
|
||||
;; per-frame workspaces
|
||||
(setq persp-init-frame-behaviour t
|
||||
persp-init-new-frame-behaviour-override nil
|
||||
persp-interactive-init-frame-behaviour-override #'+workspaces-associate-frame-fn
|
||||
persp-emacsclient-init-frame-behaviour-override #'+workspaces-associate-frame-fn)
|
||||
(add-hook 'delete-frame-functions #'+workspaces-delete-associated-workspace-h)
|
||||
|
||||
;; per-project workspaces, but reuse current workspace if empty
|
||||
(setq projectile-switch-project-action #'+workspaces-set-project-action-fn
|
||||
counsel-projectile-switch-project-action
|
||||
'(1 ("o" +workspaces-switch-to-project-h "open project in new workspace")
|
||||
("O" counsel-projectile-switch-project-action "jump to a project buffer or file")
|
||||
("f" counsel-projectile-switch-project-action-find-file "jump to a project file")
|
||||
("d" counsel-projectile-switch-project-action-find-dir "jump to a project directory")
|
||||
("b" counsel-projectile-switch-project-action-switch-to-buffer "jump to a project buffer")
|
||||
("m" counsel-projectile-switch-project-action-find-file-manually "find file manually from project root")
|
||||
("w" counsel-projectile-switch-project-action-save-all-buffers "save all project buffers")
|
||||
("k" counsel-projectile-switch-project-action-kill-buffers "kill all project buffers")
|
||||
("r" counsel-projectile-switch-project-action-remove-known-project "remove project from known projects")
|
||||
("c" counsel-projectile-switch-project-action-compile "run project compilation command")
|
||||
("C" counsel-projectile-switch-project-action-configure "run project configure command")
|
||||
("e" counsel-projectile-switch-project-action-edit-dir-locals "edit project dir-locals")
|
||||
("v" counsel-projectile-switch-project-action-vc "open project in vc-dir / magit / monky")
|
||||
("s" (lambda (project)
|
||||
(let ((projectile-switch-project-action
|
||||
(lambda () (call-interactively #'+ivy/project-search))))
|
||||
(counsel-projectile-switch-project-by-name project))) "search project")
|
||||
("xs" counsel-projectile-switch-project-action-run-shell "invoke shell from project root")
|
||||
("xe" counsel-projectile-switch-project-action-run-eshell "invoke eshell from project root")
|
||||
("xt" counsel-projectile-switch-project-action-run-term "invoke term from project root")
|
||||
("X" counsel-projectile-switch-project-action-org-capture "org-capture into project")))
|
||||
|
||||
(add-hook 'projectile-after-switch-project-hook #'+workspaces-switch-to-project-h)
|
||||
|
||||
;; Fix #1973: visual selection surviving workspace changes
|
||||
(add-hook 'persp-before-deactivate-functions #'deactivate-mark)
|
||||
|
||||
;; Fix #1017: stop session persistence from restoring a broken posframe
|
||||
(after! posframe
|
||||
(add-hook! 'persp-after-load-state-functions
|
||||
(defun +workspaces-delete-all-posframes-h (&rest _)
|
||||
(posframe-delete-all))))
|
||||
|
||||
;; Fix #1525: Ignore dead buffers in PERSP's buffer list
|
||||
(defun +workspaces-dead-buffer-p (buf)
|
||||
(not (buffer-live-p buf)))
|
||||
(add-hook 'persp-filter-save-buffers-functions #'+workspaces-dead-buffer-p)
|
||||
|
||||
;;
|
||||
;; eshell
|
||||
(persp-def-buffer-save/load
|
||||
:mode 'eshell-mode :tag-symbol 'def-eshell-buffer
|
||||
:save-vars '(major-mode default-directory))
|
||||
;; compile
|
||||
(persp-def-buffer-save/load
|
||||
:mode 'compilation-mode :tag-symbol 'def-compilation-buffer
|
||||
:save-vars
|
||||
'(major-mode default-directory compilation-directory compilation-environment compilation-arguments))
|
||||
;; Restore indirect buffers
|
||||
(defvar +workspaces--indirect-buffers-to-restore nil)
|
||||
(persp-def-buffer-save/load
|
||||
:tag-symbol 'def-indirect-buffer
|
||||
:predicate #'buffer-base-buffer
|
||||
:save-function (lambda (buf tag vars)
|
||||
(list tag (buffer-name buf) vars
|
||||
(buffer-name (buffer-base-buffer buf))))
|
||||
:load-function (lambda (savelist &rest _rest)
|
||||
(cl-destructuring-bind (buf-name _vars base-buf-name &rest _)
|
||||
(cdr savelist)
|
||||
(push (cons buf-name base-buf-name)
|
||||
+workspaces--indirect-buffers-to-restore)
|
||||
nil)))
|
||||
(add-hook! 'persp-after-load-state-functions
|
||||
(defun +workspaces-reload-indirect-buffers-h (&rest _)
|
||||
(dolist (ibc +workspaces--indirect-buffers-to-restore)
|
||||
(cl-destructuring-bind (buffer-name . base-buffer-name) ibc
|
||||
(when (buffer-live-p (get-buffer base-buffer-name))
|
||||
(when (get-buffer buffer-name)
|
||||
(setq buffer-name (generate-new-buffer-name buffer-name)))
|
||||
(make-indirect-buffer bb buffer-name t))))
|
||||
(setq +workspaces--indirect-buffers-to-restore nil))))
|
||||
5
.emacs.d/modules/ui/workspaces/packages.el
Normal file
5
.emacs.d/modules/ui/workspaces/packages.el
Normal file
@@ -0,0 +1,5 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/workspaces/packages.el
|
||||
|
||||
(package! persp-mode)
|
||||
|
||||
124
.emacs.d/modules/ui/workspaces/test/test-workspaces.el
Normal file
124
.emacs.d/modules/ui/workspaces/test/test-workspaces.el
Normal file
@@ -0,0 +1,124 @@
|
||||
;; -*- no-byte-compile: t; -*-
|
||||
;;; ui/workspaces/test/test-workspaces.el
|
||||
|
||||
(describe "ui/workspaces"
|
||||
:var (persp-auto-resume-time
|
||||
persp-auto-save-opt
|
||||
persp-switch-to-added-buffer
|
||||
persp-autokill-persp-when-removed-last-buffer
|
||||
persp-autokill-buffer-on-remove
|
||||
in1 in2 out1 out2
|
||||
persp1 persp1-name persp2 persp2-name
|
||||
wconf)
|
||||
|
||||
(require! :ui workspaces)
|
||||
(require 'persp-mode)
|
||||
|
||||
(before-all
|
||||
(delete-other-windows))
|
||||
|
||||
(before-each
|
||||
(switch-to-buffer "*scratch*")
|
||||
(setq wconf (current-window-configuration)
|
||||
persp-auto-resume-time -1
|
||||
persp-auto-save-opt 0
|
||||
persp-switch-to-added-buffer nil
|
||||
persp-autokill-persp-when-removed-last-buffer nil
|
||||
persp-autokill-buffer-on-remove nil
|
||||
in1 (get-buffer-create "in1")
|
||||
in2 (get-buffer-create "in2")
|
||||
out1 (get-buffer-create "out1")
|
||||
out2 (get-buffer-create "out2"))
|
||||
(doom-set-buffer-real in1 t)
|
||||
(doom-set-buffer-real out1 t)
|
||||
(let (noninteractive)
|
||||
(persp-mode +1)
|
||||
(let (persp-before-switch-functions persp-activated-functions)
|
||||
(setq persp1-name +workspaces-main
|
||||
persp1 (persp-add-new persp1-name)
|
||||
persp2-name "test"
|
||||
persp2 (persp-add-new persp2-name))
|
||||
(persp-switch persp1-name)
|
||||
(persp-add-buffer (list in1 in2) persp1))))
|
||||
|
||||
(after-each
|
||||
(let (kill-buffer-query-functions kill-buffer-hook)
|
||||
(let (noninteractive ignore-window-parameters)
|
||||
(dolist (persp (persp-names))
|
||||
(ignore-errors (persp-kill persp)))
|
||||
(persp-mode -1))
|
||||
(set-window-configuration wconf)
|
||||
(mapc #'kill-buffer (list in1 in2 out1 out2))))
|
||||
|
||||
;;
|
||||
(describe "switch"
|
||||
(it "throws an error when switching to a non-existent workspace"
|
||||
(expect (+workspace-switch "non-existent") :to-throw))
|
||||
(it "switches to a valid workspace"
|
||||
(+workspace-switch persp2-name)
|
||||
(expect (+workspace-current-name) :to-equal persp2-name)))
|
||||
|
||||
(describe "current"
|
||||
(it "returns the current workspace persp"
|
||||
(expect (+workspace-p (+workspace-current)))
|
||||
(expect (+workspace-current) :to-equal (get-current-persp)))
|
||||
(it "returns the current workspace's name"
|
||||
(expect (+workspace-current-name) :to-equal persp1-name)
|
||||
(persp-switch (persp-name persp2))
|
||||
(expect (+workspace-current-name) :to-equal persp2-name)))
|
||||
|
||||
(describe "exists-p"
|
||||
(it "returns t for valid workspaces"
|
||||
(expect (+workspace-exists-p persp1-name)))
|
||||
(it "returns t for non-current (but valid) workspaces"
|
||||
(expect (+workspace-exists-p persp2-name)))
|
||||
(it "returns nil for non-existent workspaces"
|
||||
(expect (+workspace-exists-p "non-existent") :to-be nil)))
|
||||
|
||||
(describe "buffer membership"
|
||||
(it "returns t for buffers in current workspace"
|
||||
(expect (+workspace-contains-buffer-p in1)))
|
||||
(it "returns nil for buffers outside of current workspace"
|
||||
(expect (+workspace-contains-buffer-p out1) :to-be nil))
|
||||
(xit "returns a list of orphaned buffers"
|
||||
(expect (+workspace-orphaned-buffer-list) :to-contain out2)))
|
||||
|
||||
(describe "list"
|
||||
(it "returns a list of names"
|
||||
(expect (+workspace-list-names)
|
||||
:to-have-same-items-as (list persp1-name persp2-name)))
|
||||
(it "returns a list of perspective structs"
|
||||
(expect (+workspace-list)
|
||||
:to-have-same-items-as (list persp1 persp2))))
|
||||
|
||||
(describe "CRUD"
|
||||
(it "creates new workspaces"
|
||||
(+workspace-new "X")
|
||||
(expect (+workspace-list-names) :to-contain "X"))
|
||||
(it "renames an existing workspace"
|
||||
(+workspace-rename persp2-name "X")
|
||||
(expect (persp-name persp2) :to-equal "X")
|
||||
(expect (+workspace-list-names)
|
||||
:to-have-same-items-as (list persp1-name "X")))
|
||||
(it "deletes a live workspace"
|
||||
(+workspace-delete persp2-name)
|
||||
(expect (+workspace-list-names) :not :to-contain persp2-name)))
|
||||
|
||||
(describe "command"
|
||||
(describe "close-window-or-workspace"
|
||||
(before-each
|
||||
(+workspace-switch persp2-name)
|
||||
(split-window)
|
||||
(expect (length (doom-visible-windows)) :to-be 2))
|
||||
(it "kills window if more than one window"
|
||||
(quiet! (+workspace/close-window-or-workspace))
|
||||
(expect (length (doom-visible-windows)) :to-be 1))
|
||||
(it "kills workspace on last window"
|
||||
(quiet! (+workspace/close-window-or-workspace)
|
||||
(+workspace/close-window-or-workspace))
|
||||
(expect (+workspace-current-name) :to-equal persp1-name)))
|
||||
|
||||
(describe "rename"
|
||||
(it "renames the current workspace"
|
||||
(quiet! (+workspace/rename "X"))
|
||||
(expect (+workspace-current-name) :to-equal "X")))))
|
||||
Reference in New Issue
Block a user