diff --git a/core/core-eval.el b/core/core-eval.el index 03b09750e..2ccf23c24 100644 --- a/core/core-eval.el +++ b/core/core-eval.el @@ -19,7 +19,25 @@ :init (add-hook 'quickrun/mode-hook 'linum-mode) :config (setq quickrun-focus-p nil) - (def-popup! "*quickrun*" :align below :size 10)) + (def-popup! "*quickrun*" :align below :size 10) + + ;;; Popup hacks + (defun doom*quickrun-close-popup (&optional _ _ _ _) + "Allows us to re-run quickrun from inside the quickrun buffer." + (let ((buffer (get-buffer quickrun/buffer-name)) + window) + (when buffer + (setq window (get-buffer-window buffer)) + (shut-up! (quickrun/kill-running-process)) + (doom/popup-close window nil t)))) + (advice-add 'quickrun :before 'doom*quickrun-close-popup) + (advice-add 'quickrun-region :before 'doom*quickrun-close-popup) + + (defun doom|quickrun-after-run () + "Ensures window is scrolled to BOF" + (with-selected-window (get-buffer-window quickrun/buffer-name) + (goto-char (point-min)))) + (add-hook 'quickrun-after-run-hook 'doom|quickrun-after-run)) (use-package repl-toggle :commands (rtog/toggle-repl rtog/add-repl) @@ -76,7 +94,37 @@ ;; TODO does this work with shackle? (advice-add 'realgud-cmdbuf-init :after 'doom:def-debug-on) - (advice-add 'realgud:cmd-quit :after 'doom:def-debug-off)) + (advice-add 'realgud:cmd-quit :after 'doom:def-debug-off) + + ;; Monkey-patch `realgud:run-process' to run in a popup. + ;; TODO Find a more elegant advice-based solution + ;; FIXME Causes realgud:cmd-* to focus popup on every invocation + (defun realgud:run-process(debugger-name script-filename cmd-args minibuffer-history &optional no-reset) + (let ((cmd-buf)) + (setq cmd-buf + (apply 'realgud-exec-shell debugger-name script-filename + (car cmd-args) no-reset (cdr cmd-args))) + (let ((process (get-buffer-process cmd-buf))) + (if (and process (eq 'run (process-status process))) + (progn + (pop-to-buffer cmd-buf) + (define-key evil-emacs-state-local-map (kbd "ESC ESC") 'doom/debug-quit) + (realgud:track-set-debugger debugger-name) + (realgud-cmdbuf-info-in-debugger?= 't) + (realgud-cmdbuf-info-cmd-args= cmd-args) + (when cmd-buf + (switch-to-buffer cmd-buf) + (when realgud-cmdbuf-info + (let* ((info realgud-cmdbuf-info) + (cmd-args (realgud-cmdbuf-info-cmd-args info)) + (cmd-str (mapconcat 'identity cmd-args " "))) + (set minibuffer-history + (list-utils-uniq (cons cmd-str (eval minibuffer-history)))))))) + ;; else + (progn + (if cmd-buf (switch-to-buffer cmd-buf)) + (message "Error running command: %s" (mapconcat 'identity cmd-args " "))))) + cmd-buf))) (provide 'core-eval) ;;; core-eval.el ends here diff --git a/core/core-evil.el b/core/core-evil.el index d6652c05e..7d5de55e0 100644 --- a/core/core-evil.el +++ b/core/core-evil.el @@ -69,108 +69,50 @@ (def-popup! "*evil-registers*" :align below :size 0.3) (def-popup! "*Command Line*" :align below :size 8 :select t) - ;; Evil hacks - (progn ; evil hacks - (advice-add 'evil-force-normal-state :after 'doom*evil-esc-quit) - (defun doom*evil-esc-quit () - "Close popups, disable search highlights and quit the minibuffer if open." - (if (eq major-mode 'help-mode) - (doom/popup-close) - (let ((minib-p (minibuffer-window-active-p (minibuffer-window))) - (evil-hl-p (evil-ex-hl-active-p 'evil-ex-search))) - (when minib-p (abort-recursive-edit)) - (when evil-hl-p (evil-ex-nohighlight)) - ;; Close non-repl popups and clean up `doom-popup-windows' - (unless (or minib-p evil-hl-p - (memq (get-buffer-window) doom-popup-windows)) - (mapc (lambda (w) - (if (window-live-p w) - (with-selected-window w - (unless (derived-mode-p 'comint-mode) - (doom/popup-close w))) - (doom/popup-remove w))) - doom-popup-windows))))) + ;;; Evil hacks + ;; Don't interfere with neotree + auto move to new split + (advice-add 'evil-window-split :around 'doom*evil-window-split) + (advice-add 'evil-window-vsplit :around 'doom*evil-window-vsplit) + ;; Integrate evil's command window into shackle + (advice-add 'evil-command-window :override 'doom*evil-command-window) + (add-hook 'evil-command-window-mode-hook 'doom-hide-mode-line-mode) + ;; Close popups, disable search highlights and quit the minibuffer if open + (advice-add 'evil-force-normal-state :after 'doom*evil-esc-quit) - ;; Fix harmless (yet disruptive) error reporting w/ hidden buffers caused by - ;; workgroups killing windows - ;; TODO Delete timer on dead windows - (defadvice evil-ex-hl-do-update-highlight - (around evil-ex-hidden-buffer-ignore-errors activate) - (ignore-errors ad-do-it)) + ;; Fix harmless (yet disruptive) error reporting w/ hidden buffers caused by + ;; workgroups killing windows + ;; TODO Delete timer on dead windows? + (defadvice evil-ex-hl-do-update-highlight + (around evil-ex-hidden-buffer-ignore-errors activate) + (ignore-errors ad-do-it)) - ;; Hide keystroke display while isearch is active - (add-hook! isearch-mode (setq echo-keystrokes 0)) - (add-hook! isearch-mode-end (setq echo-keystrokes 0.02)) + ;; Hide keystroke display while isearch is active + (add-hook! isearch-mode (setq echo-keystrokes 0)) + (add-hook! isearch-mode-end (setq echo-keystrokes 0.02)) - (after! evil-snipe - (def-repeat! evil-snipe-f evil-snipe-repeat evil-snipe-repeat-reverse) - (def-repeat! evil-snipe-F evil-snipe-repeat evil-snipe-repeat-reverse) - (def-repeat! evil-snipe-t evil-snipe-repeat evil-snipe-repeat-reverse) - (def-repeat! evil-snipe-T evil-snipe-repeat evil-snipe-repeat-reverse) - (def-repeat! evil-snipe-s evil-snipe-repeat evil-snipe-repeat-reverse) - (def-repeat! evil-snipe-S evil-snipe-repeat evil-snipe-repeat-reverse) - (def-repeat! evil-snipe-x evil-snipe-repeat evil-snipe-repeat-reverse) - (def-repeat! evil-snipe-X evil-snipe-repeat evil-snipe-repeat-reverse)) + (after! evil-snipe + (def-repeat! evil-snipe-f evil-snipe-repeat evil-snipe-repeat-reverse) + (def-repeat! evil-snipe-F evil-snipe-repeat evil-snipe-repeat-reverse) + (def-repeat! evil-snipe-t evil-snipe-repeat evil-snipe-repeat-reverse) + (def-repeat! evil-snipe-T evil-snipe-repeat evil-snipe-repeat-reverse) + (def-repeat! evil-snipe-s evil-snipe-repeat evil-snipe-repeat-reverse) + (def-repeat! evil-snipe-S evil-snipe-repeat evil-snipe-repeat-reverse) + (def-repeat! evil-snipe-x evil-snipe-repeat evil-snipe-repeat-reverse) + (def-repeat! evil-snipe-X evil-snipe-repeat evil-snipe-repeat-reverse)) + (after! evil-visualstar + (def-repeat! evil-visualstar/begin-search-forward + evil-ex-search-next evil-ex-search-previous) + (def-repeat! evil-visualstar/begin-search-backward + evil-ex-search-previous evil-ex-search-next)) + (def-repeat! evil-ex-search-next evil-ex-search-next evil-ex-search-previous) + (def-repeat! evil-ex-search-previous evil-ex-search-next evil-ex-search-previous) + (def-repeat! evil-ex-search-forward evil-ex-search-next evil-ex-search-previous) + (def-repeat! evil-ex-search-backward evil-ex-search-next evil-ex-search-previous) - (after! evil-visualstar - (def-repeat! evil-visualstar/begin-search-forward - evil-ex-search-next evil-ex-search-previous) - (def-repeat! evil-visualstar/begin-search-backward - evil-ex-search-previous evil-ex-search-next)) - - (def-repeat! evil-ex-search-next evil-ex-search-next evil-ex-search-previous) - (def-repeat! evil-ex-search-previous evil-ex-search-next evil-ex-search-previous) - (def-repeat! evil-ex-search-forward evil-ex-search-next evil-ex-search-previous) - (def-repeat! evil-ex-search-backward evil-ex-search-next evil-ex-search-previous) - - ;; A monkey patch to add all of vim's file ex substitution flags to evil-mode. - (defun evil-ex-replace-special-filenames (file-name) - "Replace special symbols in FILE-NAME." - (let ((case-fold-search nil) - (regexp (concat "\\(?:^\\|[^\\\\]\\)" - "\\([#%@]\\)" - "\\(\\(?::\\(?:[phtreS~.]\\|g?s[^: $]+\\)\\)*\\)"))) - (dolist (match (s-match-strings-all regexp file-name)) - (let ((flags (split-string (caddr match) ":" t)) - (path (file-relative-name - (pcase (cadr match) - ("@" (doom/project-root)) - ("%" (buffer-file-name)) - ("#" (and (other-buffer) (buffer-file-name (other-buffer))))) - default-directory)) - flag global) - (when path - (while flags - (setq flag (pop flags)) - (when (string-suffix-p "\\" flag) - (setq flag (concat flag (pop flags)))) - (when (string-prefix-p "gs" flag) - (setq global t flag (string-remove-prefix "g" flag))) - (setq path - (or (pcase (substring flag 0 1) - ("p" (expand-file-name path)) - ("~" (file-relative-name path "~")) - ("." (file-relative-name path default-directory)) - ("h" (directory-file-name path)) - ("t" (file-name-nondirectory (directory-file-name path))) - ("r" (file-name-sans-extension path)) - ("e" (file-name-extension path)) - ("s" (let* ((args (evil-delimited-arguments (substring flag 1) 2)) - (pattern (evil-transform-vim-style-regexp (car args))) - (replace (cadr args))) - (replace-regexp-in-string - (if global pattern (concat "\\(" pattern "\\).*\\'")) - (evil-transform-vim-style-regexp replace) path t t - (unless global 1)))) - ("S" (shell-quote-argument path)) - (t path)) - ""))) - (setq file-name - (replace-regexp-in-string (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)" - (string-trim-left (car match))) - path file-name t t 1))))) - ;; Clean up - (setq file-name (replace-regexp-in-string regexp "\\1" file-name t))))) + ;; monkey patch `evil-ex-replace-special-filenames' to add most of vim's file + ;; ex substitution flags to evil-mode + (advice-add 'evil-ex-replace-special-filenames + :override 'doom*evil-ex-replace-special-filenames) ;; Extra argument types for highlight buffer (or global) regexp matches (evil-ex-define-argument-type buffer-match :runner doom/evil-ex-buffer-match) diff --git a/core/core-popup.el b/core/core-popup.el index ad33dd7f4..b602a11b7 100644 --- a/core/core-popup.el +++ b/core/core-popup.el @@ -1,8 +1,8 @@ ;;; core-popup.el --- taming stray windows -;; I use a slew of hackery to get Emacs to treat 'pop-up' windows in a sane and "modern" -;; way (whatever that means). It goes through great lengths to tame helm, flycheck, help -;; buffers--*even* the beast that is org-mode. +;; I use a slew of hackery to get Emacs to treat 'pop-ups' consistently. It goes +;; through great lengths to tame helm, flycheck, help buffers--*even* the beast +;; that is org-mode. ;; ;; Be warned, an update could break any of this. @@ -14,7 +14,7 @@ ("^\\*.+-Profiler-Report .+\\*$" :align below :size 0.3 :regexp t) ("*esup*" :align below :size 0.4 :noselect t) ("*minor-modes*" :align below :size 0.5 :noselect t) - ("*eval*" :align below :size 20) + ("*eval*" :align below :size 16 :noselect t) ;; Emacs ("*Pp Eval Output*" :align below :size 0.3) ("*Apropos*" :align below :size 0.3) @@ -30,30 +30,24 @@ ("*vc-change-log*" :align below :size 15 :select t) (vc-annotate-mode :same t))) - (defvar doom-popup-windows '() - "A list of windows that have been opened via shackle. Do not touch this!") - (defvar doom-last-popup nil - "The last (important) popup buffer.") - (defvar doom-prev-buffer nil - "The buffer from which the popup was invoked.") - (defvar-local doom-popup-protect nil - "If non-nil, this popup buffer won't be killed when closed.") - - (defvar doom-popup-inescapable-modes - '(compilation-mode comint-mode "^\\*doom.*\\*$") - "A list of modes that should not be closeable with a single ESC.") - (defvar doom-popup-protect-modes - '(messages-buffer-mode esup-mode help-mode tabulated-list-mode comint-mode) - "A list of modes that shouldn't be killed and can be revived.") + ;; :noesc = Can't be closed with a single ESC + ;; :nokill = Won't be killed when closed + ;; :modeline = Show the modeline + (defvar doom-popup-rules + '(("^\\*doom\\*$" :noesc :nokill :modeline) + ("^\\*doom.*\\*$" :noesc :nokill) + (compilation-mode :noesc) + (help-mode :noesc) + (comint-mode :noesc :nokill) + (eshell-mode :noesc :nokill) + (messages-buffer-mode :nokill) + (esup-mode :noesc) + (tabulated-list-mode :noesc))) ;; There is no shackle-popup hook, so I hacked one in - (defvar doom-popup-pre-hook '() "Hooks run after a popup is opened.") - (defvar doom-popup-post-hook '() "Hooks run before a popup is opened.") - (advice-add 'shackle-display-buffer :before 'doom*run-popup-pre-hooks) - (advice-add 'shackle-display-buffer :after 'doom*run-popup-post-hooks) - (add-hook 'doom-popup-post-hook 'doom|popup-init) ; Keep track of popups - (add-hook 'doom-popup-post-hook 'doom|hide-mode-line) ; No mode line in popups - ;; Prevents popups from messaging with windows-moving functions + (advice-add 'shackle-display-buffer :around 'doom*popup-init) + ;; Don't mess with popups + (advice-add 'balance-windows :around 'doom*save-popup) (advice-add 'doom/evil-window-move :around 'doom*save-popup)) @@ -61,47 +55,11 @@ ;; Hacks ;; -(defun doom-popup-magit-hacks () - ;; Some wrassling must be done to get magit to kill itself, and trigger my - ;; shackle popup hooks. - (setq magit-bury-buffer-function - (lambda (&rest _) (doom/popup-close (selected-window))) - magit-display-buffer-function - (lambda (b) - (funcall (if (doom/popup-p (selected-window)) 'switch-to-buffer 'doom/popup-buffer) b) - (get-buffer-window b)) - magit-display-file-buffer-function - (lambda (b) - (when doom-prev-buffer - (select-window (get-buffer-window doom-prev-buffer))) - (switch-to-buffer b)))) - -(after! evil - ;; The evil command window has a mind of its own (uses `switch-to-buffer'). We - ;; monkey patch it to use pop-to-buffer. - (defun doom*evil-command-window (hist cmd-key execute-fn) - (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) - (doom|hide-mode-line))) - (advice-add 'evil-command-window :override 'doom*evil-command-window)) - (after! help-mode - ;; Following links in help buffers sometimes uses itself or other-window. We - ;; want it only to replace the buffer we opened the popup from. To do this we - ;; must redefine these three button types to use `doom/popup-save' and - ;; `switch-to-buffer' rather than `pop-to-buffer'. + ;; Following links in help buffers sometimes uses itself or other-window + ;; (annoying!). It should only replace the buffer we opened the popup from. To + ;; fix this these three button types need to be redefined to stow away the + ;; popups then follow the link from the last buffer, using `doom/popup-save'. (define-button-type 'help-function-def :supertype 'help-xref 'help-function (lambda (fun file) @@ -111,11 +69,10 @@ (let ((location (find-function-search-for-symbol fun nil file))) (doom/popup-save - (switch-to-buffer (car location))) + (switch-to-buffer (car location) nil t)) (if (cdr location) (goto-char (cdr location)) - (message "Unable to find location in file")))) - 'help-echo (purecopy "mouse-2, RET: find function's definition")) + (message "Unable to find location in file"))))) (define-button-type 'help-variable-def :supertype 'help-xref @@ -124,11 +81,10 @@ (setq file (help-C-file-name var 'var))) (let ((location (find-variable-noselect var file))) (doom/popup-save - (switch-to-buffer (car location))) + (switch-to-buffer (car location) nil t)) (if (cdr location) (goto-char (cdr location)) - (message "Unable to find location in file")))) - 'help-echo (purecopy "mouse-2, RET: find variable's definition")) + (message "Unable to find location in file"))))) (define-button-type 'help-face-def :supertype 'help-xref @@ -137,99 +93,10 @@ (let ((location (find-function-search-for-symbol fun 'defface file))) (doom/popup-save - (switch-to-buffer (car location))) + (switch-to-buffer (car location) nil t)) (if (cdr location) (goto-char (cdr location)) - (message "Unable to find location in file")))) - 'help-echo (purecopy "mouse-2, RET: find face's definition"))) - -(after! quickrun - ;; This allows us to rerun code from inside a quickrun buffer. - (defun doom*quickrun-close-popup (&optional _ _ _ _) - (let ((buffer (get-buffer quickrun/buffer-name)) - window) - (when buffer - (setq window (get-buffer-window buffer)) - (shut-up! (quickrun/kill-running-process)) - (doom/popup-close window nil t)))) - (advice-add 'quickrun :before 'doom*quickrun-close-popup) - (advice-add 'quickrun-region :before 'doom*quickrun-close-popup) - - ;; Ensures window is scrolled to BOF - (defun doom|quickrun-after-run () - (with-selected-window (get-buffer-window quickrun/buffer-name) - (goto-char (point-min)))) - (add-hook 'quickrun-after-run-hook 'doom|quickrun-after-run) - (add-hook 'quickrun/mode-hook 'doom|hide-mode-line)) - -(add-hook! org-load - ;; Ensures org-src-edit yields control of its buffer to shackle. - (defun org-src-switch-to-buffer (buffer context) - (pop-to-buffer buffer)) - - ;; And these for org-todo, org-link and org-agenda - (defun org-pop-to-buffer-same-window (&optional buffer-or-name norecord label) - "Pop to buffer specified by BUFFER-OR-NAME in the selected window." - (display-buffer buffer-or-name)) - - (defun org-switch-to-buffer-other-window (&rest args) - (mapc (lambda (b) - (let ((buf (if (stringp b) (get-buffer-create b) b))) - (pop-to-buffer buf t t))) - args)) - - ;; Taming Org-agenda! - (defun doom/org-agenda-quit () - "Necessary to finagle org-agenda into shackle popups and behave properly on quit." - (interactive) - (if org-agenda-columns-active - (org-columns-quit) - (let ((buf (current-buffer))) - (and (not (eq org-agenda-window-setup 'current-window)) - (not (one-window-p)) - (delete-window)) - (kill-buffer buf) - (setq org-agenda-archives-mode nil - org-agenda-buffer nil)))) - - (after! org-agenda - (map! :map org-agenda-mode-map - :e "" 'doom/org-agenda-quit - :e "ESC" 'doom/org-agenda-quit - :e [escape] 'doom/org-agenda-quit - "q" 'doom/org-agenda-quit - "Q" 'doom/org-agenda-quit))) - -(after! realgud - ;; This allows realgud debuggers to run in a popup. - ;; TODO Find a more elegant advice-based solution - ;; FIXME Causes realgud:cmd-* to focus popup on every invocation - (defun realgud:run-process(debugger-name script-filename cmd-args minibuffer-history &optional no-reset) - (let ((cmd-buf)) - (setq cmd-buf - (apply 'realgud-exec-shell debugger-name script-filename - (car cmd-args) no-reset (cdr cmd-args))) - (let ((process (get-buffer-process cmd-buf))) - (if (and process (eq 'run (process-status process))) - (progn - (pop-to-buffer cmd-buf) - (define-key evil-emacs-state-local-map (kbd "ESC ESC") 'doom/debug-quit) - (realgud:track-set-debugger debugger-name) - (realgud-cmdbuf-info-in-debugger?= 't) - (realgud-cmdbuf-info-cmd-args= cmd-args) - (when cmd-buf - (switch-to-buffer cmd-buf) - (when realgud-cmdbuf-info - (let* ((info realgud-cmdbuf-info) - (cmd-args (realgud-cmdbuf-info-cmd-args info)) - (cmd-str (mapconcat 'identity cmd-args " "))) - (set minibuffer-history - (list-utils-uniq (cons cmd-str (eval minibuffer-history)))))))) - ;; else - (progn - (if cmd-buf (switch-to-buffer cmd-buf)) - (message "Error running command: %s" (mapconcat 'identity cmd-args " "))))) - cmd-buf))) + (message "Unable to find location in file")))))) (provide 'core-popup) ;;; core-popup.el ends here diff --git a/core/core-project.el b/core/core-project.el index 2a2cdcf8e..0fd4dce85 100644 --- a/core/core-project.el +++ b/core/core-project.el @@ -88,7 +88,7 @@ neo-banner-message nil) :config (evil-set-initial-state 'neotree-mode 'motion) - (add-hook 'neo-after-create-hook 'doom|hide-mode-line) + (add-hook 'neo-after-create-hook 'doom-hide-mode-line-mode) ;; Don't mess with neotree on wg-related window-config changes (advice-add 'doom/undo-window-change :around 'doom*save-neotree) diff --git a/core/core-ui.el b/core/core-ui.el index 2f7e54474..33462222c 100644 --- a/core/core-ui.el +++ b/core/core-ui.el @@ -69,8 +69,8 @@ (set-fontset-font t 'unicode doom-unicode-font)) ;; Hide mode-line in help/compile window -(add-hook 'help-mode-hook 'doom|hide-mode-line) -(add-hook 'compilation-mode-hook 'doom|hide-mode-line) +(add-hook 'help-mode-hook 'doom-hide-mode-line-mode) +(add-hook 'compilation-mode-hook 'doom-hide-mode-line-mode) ;; On by default in Emacs 25. I'll enable it manually, so disable it globally (when (and (> emacs-major-version 24) (featurep 'eldoc)) diff --git a/core/core-vcs.el b/core/core-vcs.el index 9e35a6585..ab1b9058b 100644 --- a/core/core-vcs.el +++ b/core/core-vcs.el @@ -51,9 +51,23 @@ (use-package magit :commands (magit-status) :config - (doom-popup-magit-hacks) (def-popup! (:custom (lambda (b &rest _) (derived-mode-p 'magit-mode))) :align below :size 0.5) + + ;; Some wrassling must be done to get magit to kill itself, and trigger + ;; shackle popup hooks. + (setq magit-bury-buffer-function + (lambda (&rest _) (doom/popup-close (selected-window))) + magit-display-buffer-function + (lambda (b) + (funcall (if (doom/popup-p (selected-window)) 'switch-to-buffer 'doom/popup-buffer) b) + (get-buffer-window b)) + magit-display-file-buffer-function + (lambda (b) + (when doom-prev-buffer + (select-window (get-buffer-window doom-prev-buffer))) + (switch-to-buffer b))) + (add-hook 'magit-mode-hook 'turn-off-evil-snipe-override-mode) (require 'evil-magit) diff --git a/core/defuns/defuns-evil.el b/core/defuns/defuns-evil.el index 4139996f9..580259ea5 100644 --- a/core/defuns/defuns-evil.el +++ b/core/defuns/defuns-evil.el @@ -170,5 +170,83 @@ (hs-toggle-hiding) (call-interactively 'evilmi-jump-items))) +;;;###autoload +(defun doom*evil-command-window (hist cmd-key execute-fn) + "The evil command window has a mind of its own (uses `switch-to-buffer'). We +monkey patch it to use pop-to-buffer." + (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))) + +;;;###autoload +(defun doom*evil-esc-quit () + "Close popups, disable search highlights and quit the minibuffer if open." + (let ((minib-p (minibuffer-window-active-p (minibuffer-window))) + (evil-hl-p (evil-ex-hl-active-p 'evil-ex-search))) + (when minib-p (abort-recursive-edit)) + (when evil-hl-p (evil-ex-nohighlight)) + ;; Close non-repl popups and clean up `doom-popup-windows' + (unless (or minib-p evil-hl-p (bound-and-true-p doom-popup-mode)) + (doom/popup-close-all)))) + +;;;###autoload +(defun doom*evil-ex-replace-special-filenames (file-name) + "Replace special symbols in FILE-NAME." + (let ((case-fold-search nil) + (regexp (concat "\\(?:^\\|[^\\\\]\\)" + "\\([#%@]\\)" + "\\(\\(?::\\(?:[phtreS~.]\\|g?s[^: $]+\\)\\)*\\)"))) + (dolist (match (s-match-strings-all regexp file-name)) + (let ((flags (split-string (caddr match) ":" t)) + (path (file-relative-name + (pcase (cadr match) + ("@" (doom/project-root)) + ("%" (buffer-file-name)) + ("#" (and (other-buffer) (buffer-file-name (other-buffer))))) + default-directory)) + flag global) + (when path + (while flags + (setq flag (pop flags)) + (when (string-suffix-p "\\" flag) + (setq flag (concat flag (pop flags)))) + (when (string-prefix-p "gs" flag) + (setq global t flag (string-remove-prefix "g" flag))) + (setq path + (or (pcase (substring flag 0 1) + ("p" (expand-file-name path)) + ("~" (file-relative-name path "~")) + ("." (file-relative-name path default-directory)) + ("h" (directory-file-name path)) + ("t" (file-name-nondirectory (directory-file-name path))) + ("r" (file-name-sans-extension path)) + ("e" (file-name-extension path)) + ("s" (let* ((args (evil-delimited-arguments (substring flag 1) 2)) + (pattern (evil-transform-vim-style-regexp (car args))) + (replace (cadr args))) + (replace-regexp-in-string + (if global pattern (concat "\\(" pattern "\\).*\\'")) + (evil-transform-vim-style-regexp replace) path t t + (unless global 1)))) + ("S" (shell-quote-argument path)) + (t path)) + ""))) + (setq file-name + (replace-regexp-in-string (format "\\(?:^\\|[^\\\\]\\)\\(%s\\)" + (string-trim-left (car match))) + path file-name t t 1))))) + (setq file-name (replace-regexp-in-string regexp "\\1" file-name t)))) + (provide 'defuns-evil) ;;; defuns-evil.el ends here diff --git a/core/defuns/defuns-helm.el b/core/defuns/defuns-helm.el index 87ee5cc64..26849e24d 100644 --- a/core/defuns/defuns-helm.el +++ b/core/defuns/defuns-helm.el @@ -82,8 +82,7 @@ buffers." ;;;###autoload (defun doom*helm-hide-header (source &optional force) - (setq header-line-format nil) - (doom|hide-mode-line)) + (doom-hide-mode-line-mode +1)) ;;;###autoload (defun doom*helm-hide-source-header-maybe () diff --git a/core/defuns/defuns-popups.el b/core/defuns/defuns-popups.el index c7615b0e2..e0e61cfe3 100644 --- a/core/defuns/defuns-popups.el +++ b/core/defuns/defuns-popups.el @@ -1,133 +1,159 @@ ;;; defuns-popups.el +(defvar doom-last-popup nil + "The last popup buffer open (or group thereof).") + +(defvar-local doom-popup-rule nil + "A list of rules applied to this popup.") + +(defvar doom-popup-mode-map + (let ((map (make-sparse-keymap))) + (define-key map [remap doom/kill-real-buffer] 'doom/popup-close) + (define-key map [remap evil-window-move-very-bottom] 'ignore) + (define-key map [remap evil-window-move-very-top] 'ignore) + (define-key map [remap evil-window-move-far-left] 'ignore) + (define-key map [remap evil-window-move-far-right] 'ignore) + (define-key map [remap evil-window-split] 'ignore) + (define-key map [remap evil-window-vsplit] 'ignore) + map) + "Active keymap in popup windows.") + +(advice-add 'doom/evil-window-move :around 'doom*popup-window-move) + ;;;###autoload -(defun doom/popup-remove (window) - (setq doom-popup-windows (delete window doom-popup-windows)) - (unless (and doom-prev-buffer (not (buffer-live-p doom-prev-buffer))) - (setq doom-prev-buffer nil))) +(defun doom*popup-window-move (orig-fn &rest args) + (unless (doom/popup-p) + (apply orig-fn args))) ;;;###autoload (defun doom/popup-p (&optional window) - "Whether WINDOW is a shackle popup window or not." - (and doom-popup-windows - (--any? (if (window-live-p it) t (doom/popup-remove it) nil) - doom-popup-windows) - (if window - (memq window doom-popup-windows) - t))) + "Whether WINDOW is a shackle popup window or not. If WINDOW is nil, use +current window." + (let ((window (or window (selected-window)))) + (and (window-live-p window) + (with-selected-window window (bound-and-true-p doom-popup-mode)) + window))) + +;;;###autoload +(defun doom/popups-p () + "Whether there is a popup window open and alive somewhere." + (and doom-last-popup + (window-live-p (get-buffer-window doom-last-popup)))) ;;;###autoload (defmacro doom/popup-save (&rest body) - `(let ((popup-p (doom/popup-p))) - (when popup-p (doom/popup-close-all t t)) - ,@body + "Close popups before BODY and restore them afterwards." + `(let ((popup-p (doom/popups-p)) + (in-popup-p (doom/popup-p))) (when popup-p - (save-selected-window - (doom/popup-last-buffer))))) + (doom/popup-close-all t t)) + (prog1 + ,@body + (when popup-p + (if in-popup-p + (doom/popup-last-buffer) + (save-excursion (doom/popup-last-buffer))))))) ;;;###autoload (defun doom/popup-buffer (buffer &optional plist) "Display BUFFER in a shackle popup." - (let* ((buffer-name (if (stringp buffer) buffer (buffer-name buffer))) - (buffer (get-buffer-create buffer-name))) - (shackle-display-buffer buffer nil (or plist (shackle-match buffer-name))) - (setq doom-last-popup buffer))) + (let ((buffer-name (cond ((stringp buffer) buffer) + ((bufferp buffer) (buffer-name buffer)) + (t (error "Not a valid buffer"))))) + (shackle-display-buffer + (get-buffer-create buffer-name) + nil (or plist (shackle-match buffer-name))))) ;;;###autoload (defun doom/popup-close (&optional window dont-kill dont-redraw) "Find and close the currently active popup (if available)." (interactive) - (let ((dont-kill (or doom-popup-protect dont-kill))) - (when (and (not window) (doom/popup-p (selected-window))) - (setq window (selected-window))) - (when (and window (window-live-p window)) - ;; REPL buffer - (cond ((and (derived-mode-p 'comint-mode) - (featurep 'repl-toggle) - repl-toggle-mode) - (setq rtog/--last-buffer nil)) - ((eq major-mode 'messages-buffer-mode) - (bury-buffer) - (setq dont-kill t))) - (doom/popup-remove window) - (unless dont-kill - (let ((kill-buffer-query-functions - (delq 'process-kill-buffer-query-function - kill-buffer-query-functions))) - (kill-buffer (window-buffer window)))) + (setq window (or window (selected-window))) + (when (doom/popup-p window) + (with-selected-window window + ;; If REPL... + (when (bound-and-true-p repl-toggle-mode) + (setq rtog/--last-buffer nil)) + (if (not (or dont-kill (memq :nokill doom-popup-rule))) + (let ((kill-buffer-query-functions + (delq 'process-kill-buffer-query-function + kill-buffer-query-functions))) + (kill-buffer (window-buffer window))) + (doom-popup-mode -1)) (delete-window window) - (unless (or dont-redraw (>= emacs-major-version 25)) + (unless (and (< emacs-major-version 25) (not dont-redraw)) (redraw-frame))))) ;;;###autoload -(defun doom/popup-close-all (&optional dont-kill-buffers dont-redraw) - "Closes all popup windows (and kills the buffers if DONT-KILL-BUFFERS is non-nil)" +(defun doom/popup-close-all (&optional dont-kill dont-redraw) + "Closes all popups (kill them if DONT-KILL-BUFFERS is non-nil). Then redraw +the display (unless DONT-REDRAW is non-nil)." (interactive) - (mapc (lambda (w) (doom/popup-close w dont-kill-buffers t)) - doom-popup-windows) - (unless (or dont-redraw (>= emacs-major-version 25)) - (redraw-frame)) - (setq doom-popup-windows nil)) - -;;;###autoload -(defun doom/popup-toggle () - "Toggles the popup window, reopening the last popup (if available)." - (interactive) - (if (doom/popup-p) - (doom/popup-close t) - (doom/popup-last-buffer))) + (mapc (lambda (w) (doom/popup-close w dont-kill t)) + (doom/get-visible-windows (buffer-list))) + (when (< emacs-major-version 25) + (unless dont-redraw + (redraw-frame)))) ;;;###autoload (defun doom/popup-last-buffer () - "Pop up the last popup buffer." + "Restore the last popup." (interactive) - (unless (and doom-last-popup - (buffer-live-p doom-last-popup)) + (unless (buffer-live-p doom-last-popup) (setq doom-last-popup nil) (error "No popup to restore")) (doom/popup-buffer doom-last-popup)) ;;;###autoload (defun doom/popup-messages () - "Pop up the *Messages* buffer." + "Pop up the messages buffer." (interactive) - (doom/popup-buffer "*Messages*") - (with-current-buffer "*Messages*" - (doom|hide-mode-line) - (goto-char (point-max)))) + (doom/popup-buffer (messages-buffer)) + (goto-char (point-max))) ;;;###autoload -(defun doom*run-popup-pre-hooks (&rest _) - (unless (and doom-prev-buffer - (doom/popup-p (get-buffer-window doom-prev-buffer))) - (setq doom-prev-buffer (current-buffer)))) - -;;;###autoload -(defun doom*run-popup-post-hooks (&rest _) - (with-current-buffer shackle-last-buffer - (run-hooks 'doom-popup-post-hook))) - -;;;###autoload -(defun doom|popup-init () - (add-to-list 'doom-popup-windows (get-buffer-window)) - (unless (one-window-p) - (ignore-errors - (local-set-key [escape escape] 'doom/popup-close) - (let ((map evil-normal-state-local-map)) - (define-key map [escape escape] 'doom/popup-close) - (unless (and (apply #'derived-mode-p doom-popup-inescapable-modes) - (--any? (string-match-p it (buffer-name)) (-filter 'stringp doom-popup-inescapable-modes))) - (define-key map [escape] 'doom/popup-close) - (define-key map (kbd "ESC") 'doom/popup-close))) - (when (or (apply #'derived-mode-p doom-popup-protect-modes) - (--any? (string-match-p it buffer-name) (-filter 'stringp doom-popup-protect-modes))) - (setq-local doom-popup-protect t) - (setq doom-last-popup (current-buffer)))))) +(defun doom*popup-init (orig-fn &rest args) + "Enable `doom-popup-mode' in every popup window and returns the window." + (let ((window (apply orig-fn args))) + (with-current-buffer (window-buffer window) + (doom-popup-mode +1)) + ;; NOTE orig-fn returns a window, so `doom*popup-init' must too + window)) ;;;###autoload (defun doom*save-popup (orig-fun &rest args) "Prevents messing up a popup buffer on window changes" (doom/popup-save (apply orig-fun args))) +(put 'doom-popup-mode 'permanent-local t) +(put 'doom-popup-mode-map 'permanent-local t) + +;;;###autoload +(define-minor-mode doom-popup-mode + "Pop ups" + :init-value nil + :keymap doom-popup-mode-map + :global nil + (let ((rules (or doom-popup-rule + (--any (let ((key (car it))) + (when (cond ((symbolp key) + (or (eq major-mode key) + (derived-mode-p key))) + ((and (stringp key) buffer-file-name) + (string-match-p key buffer-file-name))) + (cdr it))) + doom-popup-rules)))) + (setq doom-last-popup (current-buffer)) + (setq-local doom-popup-rule rules) + ;; (set-window-dedicated-p (selected-window) doom-popup-mode) + (unless (memq :noesc rules) + (make-local-variable 'doom-popup-mode-map) + (let ((map doom-popup-mode-map)) + (define-key map [remap evil-force-normal-state] 'doom/popup-close) + (define-key map [escape] 'doom/popup-close) + (define-key map (kbd "ESC") 'doom/popup-close))) + (unless (memq :modeline rules) + (doom-hide-mode-line-mode (if doom-popup-mode +1 -1))))) + (provide 'defuns-popups) ;;; defuns-popups.el ends here diff --git a/core/defuns/defuns-ui.el b/core/defuns/defuns-ui.el index 8471c1c59..ab551a79e 100644 --- a/core/defuns/defuns-ui.el +++ b/core/defuns/defuns-ui.el @@ -61,10 +61,6 @@ (imenu-list-minor-mode -1)))) (doom/get-visible-buffers (doom/get-real-buffers)))) -;;;###autoload -(defun doom|hide-mode-line (&rest _) - (setq mode-line-format nil)) - ;;;###autoload (defun doom/eldoc-show-in-mode-line (input) "Display string STR in the mode-line next to minibuffer." @@ -92,5 +88,23 @@ (sit-for eldoc-show-in-mode-line-delay)))) (force-mode-line-update))) +(defvar-local doom-hide-mode-line nil) +(defvar-local doom--mode-line nil) +;;;###autoload +(define-minor-mode doom-hide-mode-line-mode + "Minor mode to hide the mode-line in the current buffer." + :init-value nil + :global nil + :variable doom-hide-mode-line + (if doom-hide-mode-line + (setq doom--mode-line mode-line-format + mode-line-format nil) + (setq mode-line-format doom--mode-line + doom--mode-line nil)) + (force-mode-line-update) + ;; Apparently force-mode-line-update is not always enough to + ;; redisplay the mode-line + (redraw-display)) + (provide 'defuns-ui) ;;; defuns-ui.el ends here diff --git a/core/defuns/defuns-window.el b/core/defuns/defuns-window.el index a995a2b46..31438f1f0 100644 --- a/core/defuns/defuns-window.el +++ b/core/defuns/defuns-window.el @@ -1,17 +1,17 @@ ;;; defuns-window.el --- library for acting on windows ;;;###autoload -(defun doom/evil-window-split () +(defun doom*evil-window-split (orig-fn &rest args) (interactive) (doom/neotree-save - (call-interactively 'evil-window-split) + (apply orig-fn args) (evil-window-down 1))) ;;;###autoload -(defun doom/evil-window-vsplit () +(defun doom*evil-window-vsplit (orig-fn &rest args) (interactive) (doom/neotree-save - (call-interactively 'evil-window-vsplit) + (apply orig-fn args) (evil-window-right 1))) ;;;###autoload diff --git a/core/defuns/defuns-workgroup.el b/core/defuns/defuns-workgroup.el index f55dda6b0..52fc4fa1f 100644 --- a/core/defuns/defuns-workgroup.el +++ b/core/defuns/defuns-workgroup.el @@ -194,15 +194,15 @@ ;;;###autoload (defun doom/close-window-or-workgroup () (interactive) - (if (memq (get-buffer-window) doom-popup-windows) + (if (doom/popup-p) (doom/popup-close) - (doom/kill-real-buffer) - (if (and (one-window-p t) - (> (length (wg-workgroup-list)) 1)) - (if (string= (wg-workgroup-name (wg-current-workgroup)) wg-first-wg-name) - (evil-window-delete) - (doom:workgroup-delete)) - (evil-window-delete)))) + (when (doom/kill-real-buffer) + (if (and (one-window-p t) + (> (length (wg-workgroup-list)) 1)) + (if (string= (wg-workgroup-name (wg-current-workgroup)) wg-first-wg-name) + (evil-window-delete) + (doom:workgroup-delete)) + (evil-window-delete))))) (provide 'defuns-workgroup) ;;; defuns-workgroup.el ends here diff --git a/modules/module-elisp.el b/modules/module-elisp.el index 36868fa2e..b5c9649bb 100644 --- a/modules/module-elisp.el +++ b/modules/module-elisp.el @@ -116,7 +116,7 @@ :n "tr" 'doom/ert-rerun-test :n "ta" 'doom/ert-run-all-tests :n "ts" 'doom/ert-run-test) - (add-hook 'ert-results-mode-hook 'doom|hide-mode-line)) + (add-hook 'ert-results-mode-hook 'doom-hide-mode-line-mode)) (provide 'module-elisp) ;;; module-elisp.el ends here diff --git a/modules/module-processing.el b/modules/module-processing.el index cee659f39..da7de7d92 100644 --- a/modules/module-processing.el +++ b/modules/module-processing.el @@ -4,7 +4,7 @@ :when IS-MAC :commands (processing-mode processing-find-sketch) :mode "\\.pde$" - :init (add-hook 'processing-compilation-mode-hook 'doom|hide-mode-line) + :init (add-hook 'processing-compilation-mode-hook 'doom-hide-mode-line-mode) :config (def-builder! processing-mode processing-sketch-build) (def-popup! "*processing-compilation*" :align below :size 10 :noselect t) diff --git a/private/my-bindings.el b/private/my-bindings.el index 7e7813b5d..0a67e927c 100644 --- a/private/my-bindings.el +++ b/private/my-bindings.el @@ -14,9 +14,8 @@ "M-/" 'evil-commentary-line "A-/" 'evil-commentary-line "M-b" 'doom:build - "A-`" 'os-switch-to-term - "C-`" 'doom/popup-toggle - "C-~" 'doom:repl + "C-`" 'doom/popup-last-buffer + "M-~" 'doom/eshell ;; Text-scaling "M-0" (λ! (text-scale-set 0)) "M-=" 'text-scale-increase @@ -304,7 +303,7 @@ ;; help-mode (:after help-mode (:map help-map - "e" 'doom/popup-messages) + "e" 'view-echo-area-messages) (:map help-mode-map :n "]]" 'help-go-forward :n "[[" 'help-go-back