2084 lines
68 KiB
Org Mode
2084 lines
68 KiB
Org Mode
#+TITLE: Emacs Config
|
||
#+AUTHOR: Joseph Ferano
|
||
#+PROPERTY: header-args:emacs-lisp :tangle ./init.el
|
||
#+STARTUP: overview
|
||
#+TOC: true
|
||
|
||
** Early Init
|
||
*** Filename handler alist
|
||
Skipping a bunch of regular expression searching in the file-name-handler-alist should improve start time.
|
||
|
||
#+begin_src emacs-lisp
|
||
(defvar default-file-name-handler-alist file-name-handler-alist)
|
||
(setq file-name-handler-alist nil)
|
||
#+end_src
|
||
*** Garbage Collection
|
||
|
||
The default Garbage Collector is triggered at 800 KB, way too conservative, let's bump to 512 MB.
|
||
Garbage collection is a big contributor to startup times. This fends it off, then is reset later by
|
||
enabling `gcmh-mode'. Not resetting it will cause stuttering/freezes.
|
||
|
||
#+begin_src emacs-lisp :tangle ./early-init.el
|
||
;; -*- lexical-binding: t -*-
|
||
(setq gc-cons-threshold (expt 2 32))
|
||
|
||
(add-hook 'emacs-startup-hook
|
||
(lambda ()
|
||
"Restore defalut values after init."
|
||
(setq file-name-handler-alist default-file-name-handler-alist)
|
||
(if (boundp 'after-focus-change-function)
|
||
(add-function :after after-focus-change-function
|
||
(lambda ()
|
||
(unless (frame-focus-state)
|
||
(garbage-collect))))
|
||
(add-hook 'focus-out-hook 'garbage-collect))))
|
||
|
||
(setq native-comp-async-report-warnings-errors nil)
|
||
#+end_src
|
||
|
||
Disabling these classic visual options during early-init seems to net a 0.04 ms boost in init time
|
||
|
||
#+begin_src emacs-lisp :tangle ./early-init.el
|
||
(setq max-specpdl-size 1200)
|
||
(setq max-lisp-eval-depth 800)
|
||
|
||
(scroll-bar-mode -1)
|
||
(tool-bar-mode -1)
|
||
(menu-bar-mode -1)
|
||
(tooltip-mode -1)
|
||
#+end_src
|
||
*** Enhancements
|
||
|
||
Disable package.el, since we will be using elpaca.el. According to the elpaca.el
|
||
documentation;
|
||
|
||
#+BEGIN_SRC emacs-lisp :tangle ./early-init.el
|
||
(setq package-enable-at-startup nil)
|
||
#+END_SRC
|
||
|
||
Prioritize old byte-compiled source files over newer sources. It saves us a
|
||
little IO time to skip all the mtime checks on each lookup.
|
||
|
||
#+begin_src emacs-lisp :tangle ./early-init.el
|
||
(setq load-prefer-newer nil)
|
||
|
||
(setq safe-local-variable-values
|
||
'((org-src-preserve-indentation . t)
|
||
(eval add-hook 'after-save-hook
|
||
'(lambda nil
|
||
(org-babel-tangle))
|
||
nil t)))
|
||
|
||
;; (setq server-name (format "Emacs-%d" (emacs-pid)))
|
||
;; (add-hook 'after-init-hook #'server-start)
|
||
#+end_src
|
||
|
||
Prevent instructions on how to close an emacsclient frame.
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq server-client-instructions nil)
|
||
#+end_src
|
||
|
||
Implicitly resizing the Emacs frame adds to init time. Fonts larger than the
|
||
system default can cause frame resizing, which adds to startup time.
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq frame-inhibit-implied-resize t)
|
||
#+end_src
|
||
|
||
Ignore X resources.
|
||
|
||
#+begin_src emacs-lisp
|
||
(advice-add #'x-apply-session-resources :override #'ignore)
|
||
#+end_src
|
||
*** UTF-8 Support
|
||
#+begin_src emacs-lisp :tangle ./early-init.el
|
||
(setq default-input-method nil)
|
||
(setq utf-translate-cjk-mode nil) ; disable CJK coding/encoding (Chinese/Japanese/Korean characters)
|
||
(set-language-environment 'utf-8)
|
||
(set-keyboard-coding-system 'utf-8-mac) ; For old Carbon emacs on OS X only
|
||
(setq locale-coding-system 'utf-8)
|
||
(set-default-coding-systems 'utf-8)
|
||
(set-terminal-coding-system 'utf-8)
|
||
(set-selection-coding-system 'utf-8)
|
||
(prefer-coding-system 'utf-8)
|
||
#+end_src
|
||
|
||
Finish up
|
||
#+begin_src emacs-lisp
|
||
(provide 'early-init)
|
||
;;; early-init.el ends here
|
||
#+end_src
|
||
** COMMENT Elpaca
|
||
#+BEGIN_SRC emacs-lisp
|
||
;; -*- lexical-binding: t -*-
|
||
(defvar elpaca-installer-version 0.3)
|
||
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
|
||
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
|
||
(defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
|
||
(defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
|
||
:ref nil
|
||
:files (:defaults (:exclude "extensions"))
|
||
:build (:not elpaca--activate-package)))
|
||
(let* ((repo (expand-file-name "elpaca/" elpaca-repos-directory))
|
||
(build (expand-file-name "elpaca/" elpaca-builds-directory))
|
||
(order (cdr elpaca-order))
|
||
(default-directory repo))
|
||
(add-to-list 'load-path (if (file-exists-p build) build repo))
|
||
(unless (file-exists-p repo)
|
||
(make-directory repo t)
|
||
(condition-case-unless-debug err
|
||
(if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
|
||
((zerop (call-process "git" nil buffer t "clone"
|
||
(plist-get order :repo) repo)))
|
||
((zerop (call-process "git" nil buffer t "checkout"
|
||
(or (plist-get order :ref) "--"))))
|
||
(emacs (concat invocation-directory invocation-name))
|
||
((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
|
||
"--eval" "(byte-recompile-directory \".\" 0 'force)")))
|
||
((require 'elpaca))
|
||
((elpaca-generate-autoloads "elpaca" repo)))
|
||
(kill-buffer buffer)
|
||
(error "%s" (with-current-buffer buffer (buffer-string))))
|
||
((error) (warn "%s" err) (delete-directory repo 'recursive))))
|
||
(unless (require 'elpaca-autoloads nil t)
|
||
(require 'elpaca)
|
||
(elpaca-generate-autoloads "elpaca" repo)
|
||
(load "./elpaca-autoloads")))
|
||
|
||
(add-hook 'after-init-hook #'elpaca-process-queues)
|
||
|
||
(elpaca `(,@elpaca-order))
|
||
|
||
(setq elpaca-queue-limit 30)
|
||
|
||
#+END_SRC
|
||
** Package Management
|
||
#+begin_src emacs-lisp
|
||
;; Also read: <https://protesilaos.com/codelog/2022-05-13-emacs-elpa-devel/>
|
||
(setq package-archives
|
||
'(("elpa" . "https://elpa.gnu.org/packages/")
|
||
("elpa-devel" . "https://elpa.gnu.org/devel/")
|
||
("nongnu" . "https://elpa.nongnu.org/nongnu/")
|
||
("melpa" . "https://melpa.org/packages/")))
|
||
;; Add MELPA to `package-archives'
|
||
(add-to-list 'package-archives
|
||
'("melpa" . "https://melpa.org/packages/"))
|
||
|
||
|
||
;; Proof-of-concept to install a list of packages
|
||
(mapc
|
||
(lambda (package)
|
||
(unless (package-installed-p package)
|
||
(package-install package)))
|
||
'(recentf
|
||
benchmark-init
|
||
dashboard
|
||
olivetti
|
||
;; ligature
|
||
highlight-quoted
|
||
doom-modeline
|
||
;; hydra
|
||
;; multiple-cursors
|
||
evil
|
||
evil-collection
|
||
evil-surround
|
||
evil-snipe
|
||
evil-commentary
|
||
evil-goggles
|
||
all-the-icons-ibuffer
|
||
;; ace-window
|
||
popper
|
||
centaur-tabs
|
||
all-the-icons-completion
|
||
vertico
|
||
savehist
|
||
embark
|
||
embark-consult
|
||
marginalia
|
||
all-the-icons-completion
|
||
orderless
|
||
consult
|
||
consult-eglot
|
||
dirvish
|
||
smtpmail
|
||
sendmail
|
||
age
|
||
avy
|
||
helpful
|
||
vterm
|
||
undo-fu
|
||
undo-fu-session
|
||
vundo
|
||
which-key
|
||
;; company
|
||
mono-complete
|
||
;; lsp-mode
|
||
;; lsp-ui
|
||
;; flycheck
|
||
;; dap-mode
|
||
rustic
|
||
ob-rust
|
||
typescript-mode
|
||
disaster
|
||
haskell-mode
|
||
clojure-mode
|
||
cider
|
||
fsharp-mode
|
||
go-mode
|
||
json-mode
|
||
markdown-mode
|
||
org-bullets
|
||
org-kanban
|
||
restclient
|
||
magit))
|
||
|
||
(setopt package-vc-selected-packages
|
||
'((dotenv :url "https://github.com/pkulev/dotenv.el")
|
||
(doom-themes :url "https://github.com/JosephFerano/doom-themes")))
|
||
(package-initialize)
|
||
|
||
#+end_src
|
||
** COMMENT Benchmarking
|
||
|
||
This is commented out since it adds ever so slightly to init time, but keep it around in case we
|
||
need to benchmark slow init times later.
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'benchmark-init)
|
||
(add-hook 'after-init-hook 'benchmark-init/deactivate)
|
||
#+end_src
|
||
** Misc Stuff
|
||
|
||
#+begin_SRC emacs-lisp
|
||
(setq default-directory "/home/joe/")
|
||
(setq vc-follow-symlinks t) ; Visit real file when editing a symlink without prompting.
|
||
(global-auto-revert-mode t) ; Revert buffer's file when the file changes on disk
|
||
|
||
(setq confirm-kill-emacs 'y-or-n-p)
|
||
|
||
(require 'recentf)
|
||
(recentf-mode t)
|
||
(setq recentf-auto-cleanup 10)
|
||
(setq recentf-keep '(file-remote-p file-readable-p))
|
||
(setq recentf-max-saved-items 1000)
|
||
(setq package-native-compile t)
|
||
#+end_src
|
||
|
||
This avoids those annoying *#backup#* files that get added and eventually slow down loading the file again.
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq auto-save-default nil)
|
||
(setq create-lockfiles nil)
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq use-dialog-box nil)
|
||
(fset 'yes-or-no-p 'y-or-n-p)
|
||
(setq large-file-warning-threshold 100000000)
|
||
(setq backup-directory-alist `((".*" . ,(expand-file-name "backups" user-emacs-directory))))
|
||
(setq backup-by-copying t
|
||
delete-old-versions t
|
||
kept-new-versions 6
|
||
kept-old-versions 2
|
||
version-control t)
|
||
#+END_SRC
|
||
|
||
I don't even know how you resume from GUI mode, we'll find a use for this keybinding later on
|
||
|
||
#+begin_src emacs-lisp
|
||
(when (display-graphic-p)
|
||
(global-unset-key (kbd "C-z")))
|
||
#+end_src
|
||
** Visuals
|
||
*** COMMENT Dashboard
|
||
|
||
Use Dashboard.el. First load `all-the-icons` for nicer rendering
|
||
#+begin_src emacs-lisp
|
||
|
||
;; (elpaca 'all-the-icons)
|
||
;; (package-vc-install (dashboard :ref)
|
||
(require 'dashboard)
|
||
(dashboard-setup-startup-hook)
|
||
(setq dashboard-items '((recents . 6)
|
||
(projects . 5)
|
||
(bookmarks . 5)))
|
||
(setq dashboard-startup-banner 'logo)
|
||
(setq dashboard-center-content t)
|
||
(setq dashboard-set-file-icons t)
|
||
(setq dashboard-set-navigator t)
|
||
(setq dashboard-set-heading-icons t)
|
||
(setq dashboard-projects-backend 'project-el)
|
||
|
||
(add-hook 'dashboard-mode-hook
|
||
(lambda ()
|
||
(setq-local line-spacing 12)
|
||
;; TODO: It's not jumping
|
||
(dashboard-jump-to-recents)))
|
||
|
||
;; (defun joe/launch-dashboard ()
|
||
;; "Jump to the dashboard buffer, if doesn't exists create one."
|
||
;; (interactive)
|
||
;; (switch-to-buffer dashboard-buffer-name)
|
||
;; (dashboard-mode)
|
||
;; (dashboard-insert-startupify-lists))
|
||
#+end_src
|
||
*** Olivetti
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'olivetti)
|
||
(setq olivetti-minimum-body-width 100)
|
||
(global-set-key (kbd "C-x x o") 'olivetti-mode)
|
||
#+end_src
|
||
*** Themes
|
||
|
||
#+begin_src emacs-lisp
|
||
;; Small changes to my favorite themes
|
||
|
||
;; (load-theme 'doom-nord-light))
|
||
|
||
(setq custom-safe-themes t)
|
||
|
||
(custom-set-faces
|
||
'(dashboard-items-face ((t (:inherit widget-button :weight normal)))))
|
||
|
||
#+end_src
|
||
|
||
We want to add whatever custom theme we selected to the custom variables since emacs will just
|
||
automatically read it on startup but we don't want emacs to add these settings to init.el, which in
|
||
our case is worse since we have a literate file. Send all custom variables to ~custom.el~
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
|
||
(load custom-file)
|
||
#+end_src
|
||
|
||
Save the chosen theme after picking a new one
|
||
|
||
#+begin_src emacs-lisp
|
||
(defun joe/save-current-theme (THEME)
|
||
(let ((inhibit-message t)
|
||
(message-log-max nil))
|
||
(customize-save-variable 'custom-enabled-themes custom-enabled-themes)))
|
||
(advice-add 'consult-theme :after #'joe/save-current-theme)
|
||
|
||
#+end_src
|
||
*** Other
|
||
|
||
Setup other stuff
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq ring-bell-function 'ignore)
|
||
(add-to-list 'default-frame-alist '(undecorated . t))
|
||
(add-to-list 'default-frame-alist '(fullscreen . maximized))
|
||
(add-to-list 'default-frame-alist '(vertical-scroll-bars . nil))
|
||
|
||
(add-hook 'text-mode-hook (lambda () (setq fill-column 100) (turn-on-auto-fill)))
|
||
|
||
(setq-default display-line-numbers 'relative)
|
||
(make-variable-buffer-local 'global-hl-line-mode)
|
||
|
||
(defun joe/disable-line-numbers () (display-line-numbers-mode 0))
|
||
|
||
(dolist (mode '( dashboard-mode-hook org-mode-hook term-mode-hook eww-mode-hook
|
||
vterm-mode-hook dirvish-directory-view-mode-hook eshell-mode-hook
|
||
dired-mode-hook shell-mode-hook magit-mode-hook compilation-mode-hook
|
||
mu4e-headers-mode-hook mu4e-main-mode-hook))
|
||
(add-hook mode #'joe/disable-line-numbers))
|
||
|
||
(set-window-margins nil 0)
|
||
(setq-default right-fringe-width 10)
|
||
(setq scroll-margin 0
|
||
scroll-conservatively 100000
|
||
scroll-preserve-screen-position 1)
|
||
|
||
(global-hl-line-mode +1)
|
||
(column-number-mode +1)
|
||
|
||
(modify-all-frames-parameters
|
||
'((right-divider-width . 5)
|
||
(tab-bar-lines . 0)
|
||
(internal-border-width . 12)))
|
||
|
||
(when (>= emacs-major-version 29)
|
||
(pixel-scroll-precision-mode t))
|
||
|
||
(setq inhibit-startup-screen t)
|
||
|
||
;; Don’t compact font caches during GC, in case doom modeline gets laggy issue
|
||
(setq inhibit-compacting-font-caches t)
|
||
|
||
(require 'highlight-quoted)
|
||
(add-hook 'emacs-lisp-mode-hook 'highlight-quoted-mode)
|
||
|
||
(require 'doom-modeline)
|
||
(doom-modeline-mode)
|
||
(doom-modeline-def-modeline 'main
|
||
'(bar modals bar window-number matches buffer-info remote-host buffer-position word-count selection-info)
|
||
'(parrot objed-state misc-info battery grip irc mu4e gnus github debug repl lsp bar input-method indent-info buffer-encoding bar major-mode process))
|
||
;; Show the tab names, just put this at the car of the previous list
|
||
;; workspace-name
|
||
;; Set default mode-line
|
||
(add-hook 'doom-modeline-mode-hook
|
||
(lambda ()
|
||
(doom-modeline-set-modeline 'main 'default)))
|
||
|
||
|
||
;; TODO Likely not needed anymore
|
||
(dolist (mode '(dired-mode-hook lsp-help-mode-hook fundamental-mode-hook))
|
||
(add-hook mode (lambda () (setq truncate-lines t))))
|
||
#+end_src
|
||
|
||
Ligatures... are they that useful?
|
||
#+begin_src emacs-lisp :tangle no
|
||
(require 'ligature)
|
||
(global-ligature-mode)
|
||
#+end_src
|
||
** Text
|
||
|
||
#+begin_src emacs-lisp
|
||
;; (set-face-attribute 'default nil :family "Fira Code Nerd Font Mono" :height 110)
|
||
(set-face-attribute 'default nil :family "FiraCode Nerd Font Mono" :height 110)
|
||
;; (set-face-attribute 'variable-pitch nil :family "Source Code Pro" :height 120)
|
||
(setq-default c-basic-offset 4) ;; This is annoying
|
||
(setq-default indent-tabs-mode nil)
|
||
(setq-default tab-width 4)
|
||
(setq-default line-spacing 5)
|
||
(setq indent-line-function #'indent-relative)
|
||
|
||
(set-default 'truncate-lines t)
|
||
;; (add-hook 'before-save-hook 'whitespace-cleanup)
|
||
#+end_src
|
||
** Text Editor
|
||
|
||
Emacs is an great operating system, if only it had a good text editor...
|
||
|
||
*** Text editing
|
||
#+begin_src emacs-lisp
|
||
|
||
;; TODO Find out what to do with this
|
||
(defun joe/bookmark-set-and-save ()
|
||
"Save the current buffer as a bookmark"
|
||
(interactive)
|
||
(bookmark-set)
|
||
(bookmark-save))
|
||
|
||
(global-set-key (kbd "M-z") #'zap-up-to-char)
|
||
(global-set-key (kbd "M-Z") #'zap-to-char)
|
||
|
||
(defun joe/duplicate-line-comment ()
|
||
(interactive)
|
||
(let ((col (current-column)))
|
||
(duplicate-line)
|
||
(comment-line 1)
|
||
(move-to-column col)))
|
||
|
||
(global-set-key (kbd "C-c d") 'duplicate-line)
|
||
(global-set-key (kbd "C-c C-;") 'joe/duplicate-line-comment)
|
||
|
||
(global-set-key (kbd "M-n") (kbd "C-u 1 C-v"))
|
||
(global-set-key (kbd "M-p") (kbd "C-u 1 M-v"))
|
||
|
||
(defun joe/insert-line-below ()
|
||
"Insert an empty line below the current line."
|
||
(interactive)
|
||
(save-excursion
|
||
(end-of-line)
|
||
(open-line 1)))
|
||
|
||
(defun joe/insert-line-above ()
|
||
"Insert an empty line above the current line."
|
||
(interactive)
|
||
(save-excursion
|
||
(end-of-line 0)
|
||
(open-line 1)))
|
||
|
||
(global-set-key (kbd "M-o") #'joe/insert-line-below)
|
||
(global-set-key (kbd "M-O") #'joe/insert-line-above)
|
||
|
||
#+end_src
|
||
|
||
For the longest time I had no idea why the ~(~ and ~)~ vim motions for sentences
|
||
weren't working, until I randomly saw this in someone's init.el
|
||
|
||
#+begin_src emacs-lisp
|
||
(setq sentence-end-double-space nil)
|
||
#+end_src
|
||
|
||
#+end_src
|
||
*** COMMENT Hydra
|
||
#+begin_src emacs-lisp
|
||
(require 'hydra)
|
||
|
||
(defhydra hydra-navigate (global-map "<f2>")
|
||
"Window Navigation"
|
||
("q" nil "quit")
|
||
("d" joe/smooth-scroll-half-page-down "half page down")
|
||
("u" joe/smooth-scroll-half-page-up "half page up")
|
||
("e" joe/smooth-scroll-short-down "line down")
|
||
("y" joe/smooth-scroll-short-up "line up")
|
||
("n" next-line "line down")
|
||
("p" previous-line "line up")
|
||
("M-r" move-to-window-line-top-bottom "Reposition cursor"))
|
||
#+end_src
|
||
*** COMMENT Multiple Cursors
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'multiple-cursors)
|
||
#+end_src
|
||
*** COMMENT Meow
|
||
#+begin_src emacs-lisp
|
||
(elpaca 'meow)
|
||
(defun meow-setup ()
|
||
(setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty)
|
||
(meow-motion-overwrite-define-key
|
||
'("j" . meow-next)
|
||
'("k" . meow-prev)
|
||
'("<escape>" . ignore))
|
||
(meow-leader-define-key
|
||
;; SPC j/k will run the original command in MOTION state.
|
||
'("j" . "H-j")
|
||
'("k" . "H-k")
|
||
;; Use SPC (0-9) for digit arguments.
|
||
'("1" . meow-digit-argument)
|
||
'("2" . meow-digit-argument)
|
||
'("3" . meow-digit-argument)
|
||
'("4" . meow-digit-argument)
|
||
'("5" . meow-digit-argument)
|
||
'("6" . meow-digit-argument)
|
||
'("7" . meow-digit-argument)
|
||
'("8" . meow-digit-argument)
|
||
'("9" . meow-digit-argument)
|
||
'("0" . meow-digit-argument)
|
||
'("/" . meow-keypad-describe-key)
|
||
'("?" . meow-cheatsheet))
|
||
(meow-normal-define-key
|
||
'("0" . meow-expand-0)
|
||
'("9" . meow-expand-9)
|
||
'("8" . meow-expand-8)
|
||
'("7" . meow-expand-7)
|
||
'("6" . meow-expand-6)
|
||
'("5" . meow-expand-5)
|
||
'("4" . meow-expand-4)
|
||
'("3" . meow-expand-3)
|
||
'("2" . meow-expand-2)
|
||
'("1" . meow-expand-1)
|
||
'("-" . negative-argument)
|
||
'(";" . meow-reverse)
|
||
'("," . meow-inner-of-thing)
|
||
'("." . meow-bounds-of-thing)
|
||
'("[" . meow-beginning-of-thing)
|
||
'("]" . meow-end-of-thing)
|
||
'("a" . meow-append)
|
||
'("A" . meow-open-below)
|
||
'("b" . meow-back-word)
|
||
'("B" . meow-back-symbol)
|
||
'("c" . meow-change)
|
||
'("d" . meow-delete)
|
||
'("D" . meow-backward-delete)
|
||
'("e" . meow-next-word)
|
||
'("E" . meow-next-symbol)
|
||
'("f" . meow-find)
|
||
'("g" . meow-cancel-selection)
|
||
'("G" . meow-grab)
|
||
'("h" . meow-left)
|
||
'("H" . meow-left-expand)
|
||
'("i" . meow-insert)
|
||
'("I" . meow-open-above)
|
||
'("j" . meow-next)
|
||
'("J" . meow-next-expand)
|
||
'("k" . meow-prev)
|
||
'("K" . meow-prev-expand)
|
||
'("l" . meow-right)
|
||
'("L" . meow-right-expand)
|
||
'("m" . meow-join)
|
||
'("n" . meow-search)
|
||
'("o" . meow-block)
|
||
'("O" . meow-to-block)
|
||
'("p" . meow-yank)
|
||
'("q" . meow-quit)
|
||
'("Q" . meow-goto-line)
|
||
'("r" . meow-replace)
|
||
'("R" . meow-swap-grab)
|
||
'("s" . meow-kill)
|
||
'("t" . meow-till)
|
||
'("u" . meow-undo)
|
||
'("U" . meow-undo-in-selection)
|
||
'("v" . meow-visit)
|
||
'("w" . meow-mark-word)
|
||
'("W" . meow-mark-symbol)
|
||
'("x" . meow-line)
|
||
'("X" . meow-goto-line)
|
||
'("y" . meow-save)
|
||
'("Y" . meow-sync-grab)
|
||
'("z" . meow-pop-selection)
|
||
'("'" . repeat)
|
||
'("<escape>" . ignore)))
|
||
(require 'meow)
|
||
(meow-setup)
|
||
(meow-global-mode t)
|
||
(setq scroll-preserve-screen-position nil)
|
||
#+end_src
|
||
*** COMMENT Boon
|
||
#+begin_src emacs-lisp
|
||
(defun joe/psp-scroll-down-half-page ()
|
||
(interactive)
|
||
(pixel-scroll-precision-scroll-down-page (/ (window-pixel-height) 2)))
|
||
(defun joe/psp-scroll-up-half-page ()
|
||
(interactive)
|
||
(pixel-scroll-precision-scroll-up-page (/ (window-pixel-height) 2)))
|
||
|
||
(elpaca 'boon
|
||
(require 'boon-qwerty)
|
||
(boon-mode)
|
||
(define-key boon-moves-map "h" 'backward-char)
|
||
(define-key boon-moves-map "j" 'next-line)
|
||
(define-key boon-moves-map "k" 'previous-line)
|
||
(define-key boon-moves-map "l" 'forward-char)
|
||
(define-key boon-moves-map "b" 'boon-smarter-backward)
|
||
(define-key boon-moves-map "w" 'boon-smarter-forward)
|
||
(define-key boon-moves-map "q" '("hop" . avy-goto-char-2))
|
||
(define-key boon-command-map (kbd "C-k") #'joe/scroll-down-line)
|
||
(define-key boon-command-map (kbd "C-j") #'joe/scroll-up-line)
|
||
|
||
(define-key boon-command-map (kbd "C-d") #'joe/psp-scroll-down-half-page)
|
||
(define-key boon-command-map (kbd "C-u") #'joe/psp-scroll-up-half-page)
|
||
|
||
|
||
(defun joe/scroll-up-line () (interactive) (scroll-up-line 2))
|
||
(defun joe/scroll-down-line () (interactive) (scroll-down-line 2))
|
||
|
||
(define-key boon-moves-map "H" 'backward-paragraph)
|
||
(define-key boon-moves-map "L" 'forward-paragraph)
|
||
(define-key boon-moves-map "K" 'boon-smarter-upward)
|
||
|
||
(define-key boon-moves-map "J" 'boon-smarter-downward)
|
||
|
||
(define-key boon-moves-map "o" 'boon-open-next-line-and-insert)
|
||
(define-key boon-moves-map "O" 'boon-open-line-and-insert)
|
||
|
||
(define-key boon-moves-map "i" 'boon-set-insert-like-state)
|
||
(define-key boon-moves-map "r" 'boon-replace-by-character)
|
||
(define-key boon-moves-map "y" 'boon-replace-by-character)
|
||
(define-key boon-moves-map "p" 'boon-splice)
|
||
(define-key boon-moves-map "y" 'boon-treasure-region)
|
||
|
||
(define-key ctl-x-map "s" 'save-buffer))
|
||
|
||
#+end_src
|
||
*** Evil
|
||
#+begin_src emacs-lisp
|
||
|
||
(setq evil-want-keybinding nil)
|
||
(setq evil-undo-system 'undo-fu)
|
||
(setq evil-want-C-u-scroll t)
|
||
(setq evil-want-Y-yank-to-eol t)
|
||
(setq evil-disable-insert-state-bindings t)
|
||
(setq evil-echo-state nil)
|
||
|
||
(require 'evil)
|
||
(evil-mode t)
|
||
|
||
(evil-global-set-key 'insert (kbd "C-w") #'evil-delete-backward-word)
|
||
(evil-define-key 'normal 'global (kbd "q") 'avy-goto-word-0)
|
||
|
||
;; vv to expand selection to line
|
||
(evil-global-set-key 'visual
|
||
(kbd "v")
|
||
(lambda ()
|
||
(interactive)
|
||
(evil-first-non-blank)
|
||
(exchange-point-and-mark)
|
||
(evil-end-of-line)))
|
||
|
||
|
||
(defvar joe/evil-space-mode-map (make-sparse-keymap)
|
||
"High precedence keymap.")
|
||
|
||
(define-minor-mode joe/evil-space-mode
|
||
"Global minor mode for higher precedence evil keybindings."
|
||
:global t)
|
||
|
||
(joe/evil-space-mode)
|
||
|
||
(dolist (state '(normal visual insert))
|
||
(evil-make-intercept-map
|
||
;; NOTE: This requires an evil version from 2018-03-20 or later
|
||
(evil-get-auxiliary-keymap joe/evil-space-mode-map state t t)
|
||
state))
|
||
|
||
(evil-define-key 'normal joe/evil-space-mode-map
|
||
(kbd "K") #'eldoc
|
||
(kbd "SPC t") tab-prefix-map
|
||
(kbd "SPC p") project-prefix-map
|
||
(kbd "SPC q") 'kill-buffer-and-window
|
||
(kbd "SPC k") 'kill-this-buffer
|
||
(kbd "SPC h") 'help-command
|
||
(kbd "SPC hf") 'helpful-callable
|
||
(kbd "SPC hv") 'helpful-variable
|
||
(kbd "SPC hk") 'helpful-key
|
||
(kbd "SPC ho") 'helpful-symbol
|
||
(kbd "SPC hg") 'helpful-at-point
|
||
(kbd "SPC fb") 'bookmark-jump
|
||
(kbd "SPC fr") 'consult-recent-file
|
||
(kbd "SPC ff") 'project-find-file
|
||
(kbd "SPC fa") '(lambda () (interactive) (project-find-file t))
|
||
(kbd "SPC fi") 'joe/edit-init
|
||
(kbd "SPC bl") 'mode-line-other-buffer
|
||
(kbd "SPC ba") 'switch-to-buffer
|
||
(kbd "SPC bb") 'consult-buffer
|
||
(kbd "SPC bi") 'ibuffer
|
||
(kbd "SPC bm") 'joe/toggle-buffer-mode
|
||
(kbd "SPC br") 'joe/revert-buffer-no-confirm
|
||
(kbd "SPC gg") 'magit-status
|
||
(kbd "SPC gc") 'magit-clone
|
||
(kbd "SPC ss") 'joe/vterm-here
|
||
(kbd "SPC sv") 'vterm
|
||
(kbd "SPC Ba") 'joe/bookmark-set-and-save
|
||
(kbd "SPC Bd") 'bookmark-delete
|
||
(kbd "SPC mr") 'joe/compile-run
|
||
(kbd "SPC mc") 'joe/compile-comp
|
||
(kbd "SPC ct") 'consult-theme
|
||
(kbd "SPC cl") 'consult-line
|
||
(kbd "SPC ci") 'consult-imenu
|
||
(kbd "SPC cy") 'consult-yank-from-kill-ring
|
||
(kbd "SPC cg") 'consult-ripgrep
|
||
(kbd "SPC cF") 'consult-find
|
||
(kbd "SPC co") 'consult-outline)
|
||
|
||
(define-key evil-window-map "u" #'winner-undo)
|
||
(define-key evil-window-map "U" #'winner-redo)
|
||
|
||
(defvar joe-mode-map
|
||
(let ((map (make-sparse-keymap)))
|
||
;; (define-key map (kbd "C-'") #'embark-act)
|
||
map)
|
||
"my-keys-minor-mode keymap.")
|
||
|
||
(define-key joe/evil-space-mode-map (kbd "M-'") #'embark-dwim)
|
||
(define-key joe/evil-space-mode-map (kbd "C-'") #'embark-act)
|
||
(define-key joe/evil-space-mode-map (kbd "C-/") #'comment-line)
|
||
|
||
(defun joe/scroll-up-line () (interactive) (scroll-up-line 2))
|
||
(defun joe/scroll-down-line () (interactive) (scroll-down-line 2))
|
||
(evil-global-set-key 'normal (kbd "C-e") #'joe/scroll-up-line)
|
||
(evil-global-set-key 'normal (kbd "C-y") #'joe/scroll-down-line)
|
||
|
||
|
||
(require 'evil-collection)
|
||
(evil-collection-init)
|
||
|
||
(require 'evil-surround)
|
||
(global-evil-surround-mode t)
|
||
|
||
(require 'evil-snipe)
|
||
(evil-snipe-override-mode +1)
|
||
|
||
(require 'evil-commentary)
|
||
(evil-commentary-mode t)
|
||
|
||
(require 'evil-goggles)
|
||
(evil-goggles-mode t)
|
||
(setq evil-goggles-duration 0.075)
|
||
(setq evil-goggles-pulse t)
|
||
(setq evil-goggles-async-duration 0.55)
|
||
|
||
#+end_src
|
||
** Initial Buffer
|
||
|
||
#+begin_src emacs-lisp
|
||
;; (require 'dashboard)
|
||
;; (defun dashboard-center-text (start end)
|
||
;; "Center the text between START and END."
|
||
;; (save-excursion
|
||
;; (goto-char start)
|
||
;; (let ((width 0))
|
||
;; (while (< (point) end)
|
||
;; (let* ((line-str (buffer-substring (line-beginning-position) (line-end-position)))
|
||
;; (line-length (dashboard-str-len line-str)))
|
||
;; (setq width (max width line-length)))
|
||
;; (forward-line 1))
|
||
;; (let ((prefix (propertize " " 'display `(space . (:align-to (- center ,(/ width 2)))))))
|
||
;; (add-text-properties start end `(line-prefix ,prefix indent-prefix ,prefix))))))
|
||
|
||
;; (defun dashboard-insert-center (&rest strings)
|
||
;; "Insert STRINGS in the center of the buffer."
|
||
;; (let ((start (point)))
|
||
;; (apply #'insert strings)
|
||
;; (dashboard-center-text start (point))))
|
||
|
||
(defvar joe/welcome-load-project-map (make-sparse-keymap)
|
||
"High precedence keymap.")
|
||
|
||
(defun joe/welcome-open-project ()
|
||
"Open the directory at point in dired."
|
||
(interactive)
|
||
(let ((path (file-truename (substring-no-properties
|
||
(thing-at-point 'filename)))))
|
||
(project-switch-project path)))
|
||
|
||
;; (evil-define-key 'normal 'joe/welcome-load-project-map (kbd "RET") #'joe/welcome-open-project)
|
||
|
||
(defun load-projects ()
|
||
(with-temp-buffer
|
||
(insert-file-contents project-list-file)
|
||
(goto-char (point-min))
|
||
(apply #'append (read (current-buffer)))))
|
||
|
||
(defun my-dashboard ()
|
||
"Show Welcome buffer"
|
||
(with-current-buffer (get-buffer-create "*Welcome*")
|
||
|
||
(erase-buffer)
|
||
(goto-char (point-min))
|
||
(insert-image (create-image "~/Documents/emacs-alt.png"))
|
||
(insert "\n\n\n")
|
||
(insert "Welcome to Emacs!\n\n")
|
||
;; (dashboard-insert-center "testing this thing out\n\n")
|
||
(let* ((time-str (emacs-init-time))
|
||
(time (string-to-number (car (split-string time-str)))))
|
||
(insert (format "It took %.3f seconds to start up\n\n" time)))
|
||
(insert "Happy hacking!\n\n")
|
||
(insert "Projects:\n")
|
||
(dolist (proj (load-projects))
|
||
(insert (format "\t%s\n" proj)))
|
||
;; (insert (propertize (format "\t%s\n" proj) :keymap joe/welcome-load-project-map)))
|
||
(setq cursor-type nil)
|
||
(switch-to-buffer (current-buffer))
|
||
(goto-char 86)
|
||
(display-line-numbers-mode 0)
|
||
(olivetti-mode)
|
||
(read-only-mode +1)
|
||
(current-buffer)))
|
||
|
||
(setq initial-buffer-choice #'my-dashboard)
|
||
|
||
#+end_src
|
||
** Buffers
|
||
#+begin_src emacs-lisp
|
||
(defun joe/kill-this-buffer-or-popup ()
|
||
(interactive)
|
||
"Kill the buffer normally, but if it's a popper popup, call the popper version"
|
||
(with-current-buffer (current-buffer)
|
||
(if (or (eq popper-popup-status nil)
|
||
(eq popper-popup-status 'raised))
|
||
(kill-this-buffer)
|
||
(popper-kill-latest-popup))))
|
||
|
||
(global-set-key (kbd "C-x k") #'joe/kill-this-buffer-or-popup)
|
||
(global-set-key (kbd "C-x M-k") #'kill-buffer)
|
||
(require 'all-the-icons-ibuffer)
|
||
(add-hook 'ibuffer-mode-hook #'all-the-icons-ibuffer-mode)
|
||
(global-set-key [remap list-buffers] 'ibuffer)
|
||
(global-set-key (kbd "C-x B") 'ibuffer)
|
||
(global-set-key (kbd "C-x b") 'consult-project-buffer)
|
||
(global-set-key (kbd "C-x C-b") 'consult-buffer)
|
||
|
||
(defun joe/switch-other-buffer ()
|
||
"Switch to other buffer"
|
||
(interactive)
|
||
(switch-to-buffer (other-buffer)))
|
||
|
||
(global-set-key (kbd "C-x l") 'joe/switch-other-buffer)
|
||
|
||
(save-place-mode t)
|
||
(setq save-place-file (expand-file-name "places" user-emacs-directory))
|
||
#+end_src
|
||
|
||
The theme of `C-x 4` bindings is that they operate on other windows, so this function matches that behavior.
|
||
|
||
#+begin_src emacs-lisp
|
||
(defun joe/kill-other-buffer-and-window ()
|
||
"Kill other buffer and window"
|
||
(interactive)
|
||
(other-window 1)
|
||
(kill-buffer-and-window))
|
||
(global-set-key (kbd "C-x 4 0") 'joe/kill-other-buffer-and-window)
|
||
(global-set-key (kbd "C-x C-0") 'kill-buffer-and-window)
|
||
|
||
#+end_src
|
||
** Windows
|
||
*** Window Management
|
||
|
||
#+begin_src emacs-lisp
|
||
(add-hook 'after-init-hook (lambda () (winner-mode t)))
|
||
|
||
(setq joe/popper-side-toggle 'right)
|
||
(defun joe/window-split-vertical () (interactive) (set 'joe/popper-side-toggle 'right))
|
||
(defun joe/window-split-horizontal () (interactive) (set 'joe/popper-side-toggle 'below))
|
||
|
||
(define-key ctl-x-4-map (kbd "|") #'joe/window-split-vertical)
|
||
(define-key ctl-x-4-map (kbd "-") #'joe/window-split-horizontal)
|
||
(define-key ctl-x-4-map (kbd "t") #'rotate-window)
|
||
|
||
(global-set-key (kbd "s-h") #'windmove-left)
|
||
(global-set-key (kbd "s-l") #'windmove-right)
|
||
(global-set-key (kbd "s-k") #'windmove-up)
|
||
(global-set-key (kbd "s-j") #'windmove-down)
|
||
|
||
#+end_src
|
||
|
||
Ace Window will show a hint if there are more than 2 windows, but I don't really use it
|
||
|
||
#+begin_src emacs-lisp :tangle no
|
||
|
||
(require 'ace-window)
|
||
(global-set-key (kbd "C-x o") #'ace-window)
|
||
(global-set-key (kbd "C-x C-o") #'ace-swap-window)
|
||
|
||
#+end_src
|
||
|
||
#+begin_src emacs-lisp
|
||
(global-set-key (kbd "C-`") #'window-toggle-side-windows)
|
||
|
||
(defvar joe/side-window-buffers '("^\\*Flycheck errors\\*$"
|
||
"^\\*Completions\\*$"
|
||
"^\\*Help\\*$"
|
||
"^\\*Embark.*"
|
||
"^\\*helpful.*"
|
||
"^\\*Compilation\\*$"
|
||
"^\\*SQL.*"
|
||
"^\\*HTTP Response\\*$"
|
||
"^\\*grep\\*$"
|
||
"^\\*Colors\\*$"
|
||
"^\\*Async Shell Command\\*$"))
|
||
(dolist (bufname joe/side-window-buffers)
|
||
(add-to-list 'display-buffer-alist
|
||
`(,bufname
|
||
display-buffer-in-side-window
|
||
(side . right)
|
||
(window-width . 0.43)
|
||
(slot . 0)
|
||
(window-parameters
|
||
(no-delete-other-windows . t)))))
|
||
|
||
#+end_src
|
||
*** Beframe
|
||
#+begin_src emacs-lisp
|
||
(defvar consult-buffer-sources)
|
||
(declare-function consult--buffer-state "consult")
|
||
|
||
(with-eval-after-load 'consult
|
||
(defface beframe-buffer
|
||
'((t :inherit font-lock-string-face))
|
||
"Face for `consult' framed buffers.")
|
||
|
||
(defvar beframe-consult-source
|
||
`( :name "Frame-specific buffers (current frame)"
|
||
:narrow ?F
|
||
:category buffer
|
||
:face beframe-buffer
|
||
:history beframe-history
|
||
:items ,#'beframe-buffer-names
|
||
:action ,#'switch-to-buffer
|
||
:state ,#'consult--buffer-state))
|
||
|
||
(add-to-list 'consult-buffer-sources 'beframe-consult-source))
|
||
|
||
(beframe-mode +1)
|
||
|
||
(global-set-key (kbd "s-n") #'joe/consult-buffer-vertico)
|
||
(global-set-key (kbd "s-p") #'joe/consult-buffer-vertico)
|
||
#+end_src
|
||
*** COMMENT Popper
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'popper)
|
||
(setq popper-reference-buffers
|
||
'("\\*compilation\\*" compilation-mode
|
||
"^\\*vterm\\*" vterm-mode
|
||
"^\\*Flymake.*" flymake-mode
|
||
"^\\*Flycheck.*" flycheck-error-list-mode
|
||
"^\\*lsp-help\\*" lsp-help-mode
|
||
"^\\*eldoc\\*" special-mode
|
||
"^\\*ert\\*" ert-results-mode
|
||
"^\\*HTTP Response\\*" javascript-mode
|
||
"^\\*SQL.*" sql-interactive-mode
|
||
"^\\*cargo-test\\*" cargo-test-mode
|
||
"^\\*cargo-run\\*" cargo-run-mode
|
||
"^\\*rustic-compilation\\*" rustic-compilation-mode
|
||
;; "^\\*ansi-term\\*$" term-mode
|
||
("^\\*Warnings\\*$" . hide)
|
||
help-mode
|
||
helpful-mode))
|
||
|
||
(global-set-key (kbd "C-`") 'popper-toggle-latest)
|
||
(global-set-key (kbd "C-~") 'popper-cycle)
|
||
(global-set-key (kbd "C-M-`") 'popper-toggle-type)
|
||
(require 'popper-echo)
|
||
(popper-echo-mode t)
|
||
|
||
(defun joe/get-popper-dir ()
|
||
(with-current-buffer (current-buffer)
|
||
(if (or (> (window-width) 170) (eq olivetti-mode t))
|
||
joe/popper-side-toggle
|
||
'below)))
|
||
|
||
;; TODO Consider adding checks for vertical splits and only popup on right if so
|
||
(defun joe/popper-display-func (buffer &optional _alist)
|
||
(cond
|
||
((when-let ((popup-buffer
|
||
(cl-find-if
|
||
#'popper-popup-p
|
||
(mapcar #'window-buffer (window-list)))))
|
||
(window--display-buffer
|
||
buffer (get-buffer-window popup-buffer) 'reuse
|
||
`((body-function . ,#'select-window)))))
|
||
|
||
((and (eq joe/popper-side-toggle 'right)
|
||
(window-in-direction 'left))
|
||
(message "Here")
|
||
(window--display-buffer
|
||
buffer (get-buffer-window (current-buffer)) 'reuse))
|
||
|
||
((when-let ((right-window (window-in-direction 'right))
|
||
((eq joe/popper-side-toggle 'right)))
|
||
(window--display-buffer
|
||
buffer right-window 'reuse
|
||
`((body-function . ,#'select-window)))))
|
||
|
||
((and (not (window-in-direction 'right))
|
||
(not (window-in-direction 'left)))
|
||
(display-buffer-in-direction
|
||
buffer
|
||
`((window-height . 0.45)
|
||
(window-width . 0.45)
|
||
(direction . right)
|
||
(body-function . ,#'select-window))))
|
||
(t
|
||
(display-buffer-in-direction
|
||
buffer
|
||
`((window-height . 0.45)
|
||
(window-width . 0.45)
|
||
(direction . below)
|
||
(body-function . ,#'select-window))))))
|
||
|
||
(setq popper-display-function #'joe/popper-display-func)
|
||
;; (setq popper-display-function nil)
|
||
;; (setq popper-display-function #'popper-select-popup-at-bottom)
|
||
|
||
(popper-mode t)
|
||
#+end_src
|
||
*** COMMENT Tab-bar & Tab-line
|
||
#+begin_src emacs-lisp
|
||
(global-set-key (kbd "s-n") #'tab-line-switch-to-next-tab)
|
||
(global-set-key (kbd "s-p") #'tab-line-switch-to-prev-tab)
|
||
|
||
#+end_src
|
||
*** COMMENT Scrolling
|
||
#+begin_src emacs-lisp
|
||
(require 'pixel-scroll)
|
||
(setq pixel-scroll-precision-large-scroll-height 10.0)
|
||
(setq pixel-scroll-precision-interpolation-factor 30)
|
||
|
||
|
||
(defun joe/pixel-scroll-lerp (amount direction)
|
||
(let ((half-height (* direction (/ (window-height) amount)))
|
||
(point-min-or-max (if (> direction 0) (point-min) (point-max))))
|
||
(when (or (and (pos-visible-in-window-p (point-min))
|
||
(< direction 0)))
|
||
(pixel-scroll-precision-interpolate (* 5 half-height)))
|
||
(pixel-scroll-precision-interpolate (* 5 half-height))))
|
||
|
||
(defun joe/smooth-scroll-half-page-down ()
|
||
"Smooth scroll down"
|
||
(interactive)
|
||
(joe/pixel-scroll-lerp 1.5 -1))
|
||
;; (pixel-scroll-kbd-up))
|
||
|
||
(defun joe/smooth-scroll-half-page-up ()
|
||
"Smooth scroll up"
|
||
(interactive)
|
||
(joe/pixel-scroll-lerp 1.5 1))
|
||
|
||
(defun joe/smooth-scroll-short-down ()
|
||
"Smooth scroll down"
|
||
(interactive)
|
||
(joe/pixel-scroll-lerp 8 -1))
|
||
|
||
(defun joe/smooth-scroll-short-up ()
|
||
"Smooth scroll down"
|
||
(interactive)
|
||
(joe/pixel-scroll-lerp 8 1))
|
||
|
||
;; scroll-up-command
|
||
(global-set-key (kbd "C-v") #'joe/smooth-scroll-half-page-down)
|
||
(global-set-key (kbd "M-v") #'joe/smooth-scroll-half-page-up)
|
||
(global-set-key (kbd "C-S-v") #'joe/smooth-scroll-short-down)
|
||
(global-set-key (kbd "M-S-v") #'joe/smooth-scroll-short-up)
|
||
|
||
(defun joe/scroll-other-half-down ()
|
||
(interactive)
|
||
(scroll-other-window 8))
|
||
(defun joe/scroll-other-half-up ()
|
||
(interactive)
|
||
(scroll-other-window -8))
|
||
|
||
(global-set-key (kbd "C-M-v") #'joe/scroll-other-half-down)
|
||
(global-set-key (kbd "C-M-S-V") #'joe/scroll-other-half-up)
|
||
|
||
(require 'topspace)
|
||
|
||
#+end_src
|
||
** Tabs/Workspaces
|
||
*** COMMENT Centaur Tabs
|
||
#+begin_src emacs-lisp
|
||
;; (require 'centaur-tabs)
|
||
(setq centaur-tabs-set-bar 'under)
|
||
(setq x-underline-at-descent-line t)
|
||
(setq centaur-tabs-set-close-button nil)
|
||
(setq centaur-tabs-set-icons t)
|
||
(setq centaur-tabs-show-navigation-buttons nil)
|
||
(setq centaur-tabs-set-close-button nil)
|
||
(setq centaur-tabs-set-modified-marker nil)
|
||
(setq centaur-tabs-show-new-tab-button nil)
|
||
(setq centaur-tabs-label-fixed-length 16)
|
||
|
||
(defun joe/forward-tab-or-popup ()
|
||
(interactive)
|
||
(if (popper-popup-p (current-buffer))
|
||
(popper-cycle)
|
||
(centaur-tabs-forward-tab)))
|
||
(defun joe/backward-tab-or-popup ()
|
||
(interactive)
|
||
(if (popper-popup-p (current-buffer))
|
||
(popper-cycle)
|
||
(centaur-tabs-backward-tab)))
|
||
|
||
;; (global-set-key (kbd "s-n") #'centaur-tabs-forward-tab)
|
||
;; (global-set-key (kbd "s-p") #'centaur-tabs-backward-tab)
|
||
|
||
(global-set-key (kbd "s-n") #'joe/forward-tab-or-popup)
|
||
(global-set-key (kbd "s-p") #'joe/backward-tab-or-popup)
|
||
|
||
(global-set-key (kbd "s-N") #'centaur-tabs-forward-group)
|
||
(global-set-key (kbd "s-P") #'centaur-tabs-backward-group)
|
||
(global-set-key (kbd "C-s-p") #'centaur-tabs-move-current-tab-to-left)
|
||
(global-set-key (kbd "C-s-n") #'centaur-tabs-move-current-tab-to-right)
|
||
|
||
(dolist (mode '(dashboard-mode-hook))
|
||
(add-hook mode 'centaur-tabs-local-mode))
|
||
|
||
(with-eval-after-load 'centaur-tabs
|
||
(defun joe/fix-centaur-tabs (THEME)
|
||
(centaur-tabs-mode -1)
|
||
(centaur-tabs-mode))
|
||
|
||
(advice-add 'consult-theme :after #'joe/fix-centaur-tabs)
|
||
(defun centaur-tabs-buffer-groups ()
|
||
"`centaur-tabs-buffer-groups' control buffers' group rules.
|
||
|
||
Group centaur-tabs with mode if buffer is derived from `eshell-mode' `emacs-lisp-mode' `dired-mode' `org-mode' `magit-mode'.
|
||
All buffer name start with * will group to \"Emacs\".
|
||
Other buffer group by `centaur-tabs-get-group-name' with project name."
|
||
(list
|
||
(cond
|
||
((or (derived-mode-p 'comint-mode)
|
||
(derived-mode-p 'sql-interactive-mode)
|
||
(string-match "*HTTP" (buffer-name))
|
||
(derived-mode-p 'compilation-mode))
|
||
"REPLs")
|
||
((or (and (string-equal "*" (substring (buffer-name) 0 1))
|
||
(not (string-match "*Org Src" (buffer-name))))
|
||
(memq major-mode '(magit-process-mode
|
||
magit-status-mode
|
||
magit-diff-mode
|
||
magit-log-mode
|
||
magit-file-mode
|
||
magit-blob-mode
|
||
magit-blame-mode)))
|
||
"*Buffers*")
|
||
(t
|
||
"Emacs")))))
|
||
|
||
(centaur-tabs-mode +1)
|
||
#+end_src
|
||
*** COMMENT iflipb
|
||
#+begin_src emacs-lisp
|
||
(global-set-key (kbd "s-n") #'iflipb-next-buffer)
|
||
(global-set-key (kbd "s-p") #'iflipb-previous-buffer)
|
||
(setq iflipb-permissive-flip-back t)
|
||
(setq iflipb-other-buffer-template " %s ")
|
||
(setq iflipb-current-buffer-template "<[%s]>")
|
||
(setq iflipb-buffer-list-function #'tabspaces--buffer-list)
|
||
#+end_src
|
||
*** COMMENT Tabspaces
|
||
#+begin_src emacs-lisp
|
||
(tabspaces-mode +1)
|
||
|
||
;; Filter Buffers for Consult-Buffer
|
||
(with-eval-after-load 'consult
|
||
;; hide full buffer list (still available with "b" prefix)
|
||
(consult-customize consult--source-buffer :hidden t :default nil)
|
||
;; set consult-workspace buffer list
|
||
(defvar consult--source-workspace
|
||
(list :name "Workspace Buffers"
|
||
:narrow ?w
|
||
:history 'buffer-name-history
|
||
:category 'buffer
|
||
:state #'consult--buffer-state
|
||
:default t
|
||
:items (lambda () (consult--buffer-query
|
||
:predicate #'tabspaces--local-buffer-p
|
||
:sort 'visibility
|
||
:as #'buffer-name)))
|
||
|
||
"Set workspace buffer list for consult-buffer.")
|
||
(add-to-list 'consult-buffer-sources 'consult--source-workspace))
|
||
|
||
#+end_src
|
||
*** COMMENT Tabs
|
||
#+begin_src emacs-lisp
|
||
;; (setq tab-bar-mode t)
|
||
;; (setq tab-bar-show nil)
|
||
|
||
;; (global-set-key (kbd "M-1") '(lambda () (interactive) (tab-bar-select-tab 1)))
|
||
;; (global-set-key (kbd "M-2") '(lambda () (interactive) (tab-bar-select-tab 2)))
|
||
;; (global-set-key (kbd "M-3") '(lambda () (interactive) (tab-bar-select-tab 3)))
|
||
;; (global-set-key (kbd "M-4") '(lambda () (interactive) (tab-bar-select-tab 4)))
|
||
;; (global-set-key (kbd "M-5") '(lambda () (interactive) (tab-bar-select-tab 5)))
|
||
;; (global-set-key (kbd "M-6") '(lambda () (interactive) (tab-bar-select-tab 6)))
|
||
;; (global-set-key (kbd "M-7") '(lambda () (interactive) (tab-bar-select-tab 7)))
|
||
;; (global-set-key (kbd "M-8") '(lambda () (interactive) (tab-bar-select-tab 8)))
|
||
;; (global-set-key (kbd "M-9") '(lambda () (interactive) (tab-bar-select-tab 9)))
|
||
|
||
;; (evil-global-set-key 'insert (kbd "M-1") '(lambda () (interactive) (tab-bar-select-tab 1)))
|
||
;; (evil-global-set-key 'insert (kbd "M-2") '(lambda () (interactive) (tab-bar-select-tab 2)))
|
||
;; (evil-global-set-key 'insert (kbd "M-3") '(lambda () (interactive) (tab-bar-select-tab 3)))
|
||
;; (evil-global-set-key 'insert (kbd "M-4") '(lambda () (interactive) (tab-bar-select-tab 4)))
|
||
;; (evil-global-set-key 'insert (kbd "M-5") '(lambda () (interactive) (tab-bar-select-tab 5)))
|
||
;; (evil-global-set-key 'insert (kbd "M-6") '(lambda () (interactive) (tab-bar-select-tab 6)))
|
||
;; (evil-global-set-key 'insert (kbd "M-7") '(lambda () (interactive) (tab-bar-select-tab 7)))
|
||
;; (evil-global-set-key 'insert (kbd "M-8") '(lambda () (interactive) (tab-bar-select-tab 8)))
|
||
;; (evil-global-set-key 'insert (kbd "M-9") '(lambda () (interactive) (tab-bar-select-tab 9)))
|
||
|
||
;; (evil-global-set-key 'normal (kbd "M-1") '(lambda () (interactive) (tab-bar-select-tab 1)))
|
||
;; (evil-global-set-key 'normal (kbd "M-2") '(lambda () (interactive) (tab-bar-select-tab 2)))
|
||
;; (evil-global-set-key 'normal (kbd "M-3") '(lambda () (interactive) (tab-bar-select-tab 3)))
|
||
;; (evil-global-set-key 'normal (kbd "M-4") '(lambda () (interactive) (tab-bar-select-tab 4)))
|
||
;; (evil-global-set-key 'normal (kbd "M-5") '(lambda () (interactive) (tab-bar-select-tab 5)))
|
||
;; (evil-global-set-key 'normal (kbd "M-6") '(lambda () (interactive) (tab-bar-select-tab 6)))
|
||
;; (evil-global-set-key 'normal (kbd "M-7") '(lambda () (interactive) (tab-bar-select-tab 7)))
|
||
;; (evil-global-set-key 'normal (kbd "M-8") '(lambda () (interactive) (tab-bar-select-tab 8)))
|
||
;; (evil-global-set-key 'normal (kbd "M-9") '(lambda () (interactive) (tab-bar-select-tab 9)))
|
||
|
||
#+end_src
|
||
** Projects
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'project
|
||
(setq project-vc-ignores '("target/" "bin/" "obj/"))
|
||
(add-to-list 'project-switch-commands '(magit-project-status "Magit" ?m))
|
||
(add-to-list 'project-vc-extra-root-markers ".dir-locals.el")
|
||
(setq frame-title-format "%b")
|
||
|
||
(defun joe/project-root-override (dir)
|
||
(let ((proj-name (project-try-vc dir)))
|
||
(when (and proj-name
|
||
(not (frame-parameter (selected-frame) 'explicit-name)))
|
||
(progn
|
||
(set-frame-name (file-name-nondirectory (directory-file-name (nth 2 proj-name)))))
|
||
proj-name)))
|
||
|
||
(add-hook 'project-find-functions #'joe/project-root-override)
|
||
|
||
(define-key 'ctl-x-5-prefix "n" #'set-frame-name))
|
||
|
||
#+end_src
|
||
** VEMCO
|
||
*** Vertico
|
||
#+begin_src emacs-lisp
|
||
(require 'all-the-icons-completion)
|
||
;; (require '(vertico :files (:defaults "extensions/*")
|
||
;; :includes (vertico-indexed
|
||
;; vertico-repeat
|
||
;; vertico-directory))
|
||
|
||
(require 'vertico)
|
||
(vertico-mode)
|
||
;; (elpaca 'vertico-posframe)
|
||
|
||
;; (define-key vertico-map (kbd "C-w") #'vertico-directory-delete-word)
|
||
;; (define-key vertico-map (kbd "C-r") #'vertico-repeat-)
|
||
|
||
;; (vertico-posframe-mode t)
|
||
;; (vertico-indexed-mode)
|
||
;; (setq vertico-posframe-parameters
|
||
;; '((left-fringe . 100)
|
||
;; (right-fringe . 100)))
|
||
|
||
;; (setq vertico-posframe-border-width 5)
|
||
;; (setq vertico-posframe-min-height 20)
|
||
;; (defun posframe-poshandler-slightly-below-top (info)
|
||
;; (cons (/ (- (plist-get info :parent-frame-width)
|
||
;; (plist-get info :posframe-width))
|
||
;; 2)
|
||
;; 150))
|
||
;; (setq vertico-posframe-poshandler #'posframe-poshandler-slightly-below-top)
|
||
|
||
(defun joe/consult-buffer-vertico ()
|
||
(interactive)
|
||
(let ((vertico-count 12)
|
||
(vertico-posframe-width 110)
|
||
(vertico-posframe-height 20))
|
||
(consult-buffer)))
|
||
|
||
(setq vertico-count 20
|
||
vertico-resize nil
|
||
vertico-cycle t)
|
||
(setq vertico-posframe-width 130)
|
||
(setq vertico-posframe-height 30)
|
||
(setq vertico-posframe-parameters
|
||
'((child-frame-border-width . 5)
|
||
(left-fringe . 30)
|
||
(right-fringe . 30)))
|
||
|
||
(require 'savehist)
|
||
(savehist-mode)
|
||
(add-hook 'minibuffer-setup-hook #'vertico-repeat-save)
|
||
(add-to-list 'savehist-additional-variables 'vertico-repeat-history)
|
||
|
||
(define-key vertico-map (kbd "C-M-n") #'vertico-next-group)
|
||
(define-key vertico-map (kbd "s-p") #'vertico-previous)
|
||
(define-key vertico-map (kbd "s-n") #'vertico-next)
|
||
;; #' "C-M-p" #'vertico-previous-group)
|
||
|
||
|
||
;; (elpaca 'vertico-directory)
|
||
;; :bind (:map vertico-map
|
||
;; ("RET" . vertico-directory-enter)
|
||
;; ("DEL" . vertico-directory-delete-char)
|
||
;; ("M-DEL" . vertico-directory-delete-word))
|
||
;; :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
|
||
|
||
#+end_src
|
||
*** Embark
|
||
#+begin_src emacs-lisp
|
||
(require 'embark)
|
||
|
||
(setq embark-quit-after-action '((kill-buffer . nil)))
|
||
|
||
(defun embark-which-key-indicator ()
|
||
"An embark indicator that displays keymaps using which-key.
|
||
The which-key help message will show the type and value of the
|
||
current target followed by an ellipsis if there are further
|
||
targets."
|
||
(lambda (&optional keymap targets prefix)
|
||
(if (null keymap)
|
||
(which-key--hide-popup-ignore-command)
|
||
(which-key--show-keymap
|
||
(if (eq (plist-get (car targets) :type) 'embark-become)
|
||
"Become"
|
||
(format "Act on %s '%s'%s"
|
||
(plist-get (car targets) :type)
|
||
(embark--truncate-target (plist-get (car targets) :target))
|
||
(if (cdr targets) "…" "")))
|
||
(if prefix
|
||
(pcase (lookup-key keymap prefix 'accept-default)
|
||
((and (pred keymapp) km) km)
|
||
(_ (key-binding prefix 'accept-default)))
|
||
keymap)
|
||
nil nil t (lambda (binding)
|
||
(not (string-suffix-p "-argument" (cdr binding))))))))
|
||
|
||
(setq embark-indicators
|
||
'(embark-which-key-indicator
|
||
embark-highlight-indicator
|
||
embark-isearch-highlight-indicator))
|
||
|
||
(defun embark-hide-which-key-indicator (fn &rest args)
|
||
"Hide the which-key indicator immediately when using the completing-read prompter."
|
||
(which-key--hide-popup-ignore-command)
|
||
(let ((embark-indicators
|
||
(remq #'embark-which-key-indicator embark-indicators)))
|
||
(apply fn args)))
|
||
|
||
(advice-add #'embark-completing-read-prompter
|
||
:around #'embark-hide-which-key-indicator)
|
||
|
||
#+end_src
|
||
*** Marginalia
|
||
#+begin_src emacs-lisp
|
||
(require 'marginalia)
|
||
(setq marginalia-annotators '(marginalia-annotators-heavy marginalia-annotators-light nil))
|
||
(setq marginalia-align 'right)
|
||
(setq marginalia-max-relative-age most-positive-fixnum)
|
||
(marginalia-mode)
|
||
(define-key minibuffer-local-map (kbd "M-A") #'marginalia-cycle)
|
||
(require 'all-the-icons-completion)
|
||
(all-the-icons-completion-mode)
|
||
(all-the-icons-completion-marginalia-setup)
|
||
|
||
(add-hook 'marginalia-mode-hook #'all-the-icons-completion-marginalia-setup)
|
||
|
||
#+end_src
|
||
*** Consult
|
||
#+begin_src emacs-lisp
|
||
(require 'embark-consult)
|
||
|
||
(require 'consult)
|
||
|
||
;; (require 'consult-lsp)
|
||
|
||
(global-set-key (kbd "C-. C-l") 'consult-line)
|
||
(global-set-key (kbd "C-. C-i") 'consult-imenu)
|
||
(global-set-key (kbd "C-. C-t") 'consult-theme)
|
||
(global-set-key (kbd "C-. C-r") 'consult-recent-file)
|
||
(global-set-key (kbd "C-. C-y") 'consult-yank-from-kill-ring)
|
||
|
||
#+end_src
|
||
*** Orderless
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
(require 'orderless)
|
||
(setq completion-styles '(orderless basic)
|
||
completion-category-overrides '((file (styles basic partial-completion))))
|
||
#+end_src
|
||
** Dirvish/Dired
|
||
#+begin_src emacs-lisp
|
||
(require 'dirvish)
|
||
(with-eval-after-load 'dirvish
|
||
(dirvish-override-dired-mode)
|
||
(setq delete-by-moving-to-trash t)
|
||
(setq dired-dwim-target t)
|
||
(setq dirvish-reuse-session nil)
|
||
|
||
;; (add-hook 'dired-mode-hook 'centaur-tabs-local-mode)
|
||
|
||
(dirvish-define-preview exa (file)
|
||
"Use `exa' to generate directory preview."
|
||
:require ("exa") ; tell Dirvish to check if we have the executable
|
||
(when (file-directory-p file) ; we only interest in directories here
|
||
`(shell . ("exa" "--icons" "--color=always" "--no-user" "-al" "--group-directories-first" ,file))))
|
||
(add-to-list 'dirvish-preview-dispatchers 'exa)
|
||
|
||
(setq dired-listing-switches "-l --almost-all --human-readable --time-style=long-iso --group-directories-first --no-group")
|
||
(setq dirvish-preview-dispatchers (cl-substitute 'pdf-preface 'pdf dirvish-preview-dispatchers))
|
||
(setq dirvish-attributes '(all-the-icons file-size collapse subtree-state))
|
||
|
||
(defun joe/dirvish-find-directory (dir)
|
||
(interactive "FDirvish Directory:")
|
||
(dirvish-dwim dir))
|
||
|
||
(setq dirvish-quick-access-entries
|
||
'(("h" "~/" "Home")
|
||
("d" "~/Downloads/" "Downloads")
|
||
("D" "~/Documents/" "Documents")
|
||
("b" "~/Documents/Books/" "Books")
|
||
("p" "~/Development/" "Dev")
|
||
("t" "~/TYCS/" "Teachyourselfcs")
|
||
("r" "~/Repositories" "Repos")
|
||
("B" "~/pCloudDrive/" "pCloud")))
|
||
|
||
(when (boundp 'evil-mode)
|
||
(evil-define-key 'normal dirvish-mode-map
|
||
(kbd "C-c f") #'dirvish-fd
|
||
(kbd "a") #'dirvish-quick-access
|
||
(kbd ".") #'dired-create-empty-file
|
||
(kbd "f") #'dirvish-file-info-menu
|
||
(kbd "y") #'dirvish-yank-menu
|
||
(kbd "h") #'dired-up-directory
|
||
(kbd "l") #'dired-find-file
|
||
(kbd "s") #'dirvish-quicksort
|
||
(kbd "v") #'dirvish-vc-menu
|
||
(kbd "q") #'dirvish-quit
|
||
(kbd "L") #'dirvish-history-go-forward
|
||
(kbd "H") #'dirvish-history-go-backward
|
||
(kbd "o") #'dired-open-file
|
||
(kbd "TAB") #'dirvish-subtree-toggle
|
||
(kbd "M-n") #'dirvish-narrow
|
||
(kbd "M-l") #'dirvish-ls-switches-menu
|
||
(kbd "M-m") #'dirvish-mark-menu
|
||
(kbd "M-t") #'dirvish-layout-toggle
|
||
(kbd "M-s") #'dirvish-setup-menu
|
||
(kbd "M-e") #'dirvish-emerge-menu
|
||
(kbd "M-j") #'dirvish-fd-jump)))
|
||
|
||
;; There's an issue with the keybinding precedence so we need to run this since
|
||
;; joe/evil-space-mode-map takes precedence above all else and '-' doesn't do
|
||
;; what it should
|
||
(defun joe/dirvish-up-dwim ()
|
||
(interactive)
|
||
(if (eq major-mode 'dired-mode)
|
||
(dired-up-directory)
|
||
(dirvish-dwim)))
|
||
|
||
(when (boundp 'evil-mode)
|
||
(evil-define-key 'normal joe/evil-space-mode-map (kbd "_") #'project-dired)
|
||
(evil-define-key 'normal joe/evil-space-mode-map (kbd "-") #'joe/dirvish-up-dwim))
|
||
(global-set-key (kbd "C-x d") #'dirvish-dwim)
|
||
(global-set-key (kbd "C-x C-d") #'joe/dirvish-find-directory)
|
||
|
||
(defun dired-open-file ()
|
||
"In dired, open the file named on this line."
|
||
(interactive)
|
||
(let* ((file (dired-get-filename nil t)))
|
||
(message "Opening %s..." file)
|
||
(call-process "xdg-open" nil 0 nil file)
|
||
(message "Opening %s done" file)))
|
||
|
||
#+end_src
|
||
#+begin_src emacs-lisp
|
||
|
||
#+end_src
|
||
** Avy
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'avy)
|
||
(setq avy-case-fold-search nil)
|
||
(setq avy-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l ?q ?w ?e ?r ?u ?i ?o ?p ?z ?x ?c ?v ?n ?m))
|
||
(define-key global-map (kbd "C-;") 'avy-goto-char) ;; I use this most frequently
|
||
;; (define-key global-map (kbd "C-'") 'avy-goto-line) ;; Consistent with ivy-avy
|
||
(define-key global-map (kbd "M-g c") 'avy-goto-char)
|
||
(define-key global-map (kbd "M-g e") 'avy-goto-word-0) ;; lots of candidates
|
||
(define-key global-map (kbd "M-g g") 'avy-goto-line) ;; digits behave like goto-line
|
||
(define-key global-map (kbd "M-g w") 'avy-goto-word-1) ;; first character of the word
|
||
(define-key global-map (kbd "M-g P") 'avy-pop-mark)
|
||
#+end_src
|
||
** Helpful
|
||
#+begin_src emacs-lisp
|
||
(require 'helpful)
|
||
(global-set-key (kbd "C-h f") #'helpful-callable)
|
||
(global-set-key (kbd "C-h v") #'helpful-variable)
|
||
(global-set-key (kbd "C-h k") #'helpful-key)
|
||
(global-set-key (kbd "C-h C-d") #'helpful-at-point)
|
||
(global-set-key (kbd "C-h F") #'helpful-function)
|
||
(global-set-key (kbd "C-h C") #'helpful-command)
|
||
#+end_src
|
||
** Terminals/Shells
|
||
#+begin_src emacs-lisp
|
||
(require 'vterm)
|
||
(setq vterm-shell "/bin/fish")
|
||
(setq vterm-timer-delay 0.01)
|
||
(setq vterm-buffer-name-string "VTerm - %s")
|
||
(setq vterm-max-scrollback 100000)
|
||
(setq vterm-kill-buffer-on-exit t)
|
||
|
||
(defun joe/close-popup-buffer (vterm-buf event-msg)
|
||
(unless (or (eq popper-popup-status nil)
|
||
(eq popper-popup-status 'raised))
|
||
(popper-close-latest)))
|
||
|
||
(setq vterm-exit-functions '(joe/close-popup-buffer))
|
||
|
||
(defun joe/vterm-here ()
|
||
(interactive)
|
||
(let ((vterm-buf (vterm--internal #'switch-to-buffer)))
|
||
(with-current-buffer vterm-buf
|
||
(setq popper-popup-status 'raised))))
|
||
|
||
(global-set-key (kbd "C-c t") #'vterm)
|
||
(global-set-key (kbd "C-c T") #'joe/vterm-here)
|
||
;; (setq explicit-shell-file-name "~/Development/fell/fell")
|
||
(add-hook 'shell-mode (lambda () (setq-local global-hl-line-mode nil)))
|
||
(setq shell-kill-buffer-on-exit t)
|
||
|
||
(add-hook 'vterm-mode-hook
|
||
(lambda ()
|
||
(define-key vterm-mode-map (kbd "C-c C-x") #'vterm-send-C-x)
|
||
(when (boundp 'evil-mode)
|
||
(evil-define-key 'insert vterm-mode-map (kbd "C-w") #'vterm-send-C-w)
|
||
(evil-define-key 'insert vterm-mode-map (kbd "<delete>") #'vterm-send-delete))
|
||
(setq-local global-hl-line-mode nil)))
|
||
#+end_src
|
||
|
||
VTerm is loading TRAMP along with it which slows down init time noticeably so call this after
|
||
startup. Reason we have to call this is so the vterm fucntion can call `vterm--internal`.
|
||
|
||
#+begin_src emacs-lisp :tangle no
|
||
;; (add-hook 'emacs-startup-hook (lambda () (require 'vterm)))
|
||
#+end_src
|
||
** Undo Fu
|
||
#+begin_src emacs-lisp
|
||
(require 'undo-fu)
|
||
(undo-fu-session-global-mode +1)
|
||
(setq undo-limit 6710886400) ;; 64mb.
|
||
(setq undo-strong-limit 100663296) ;; 96mb.
|
||
(setq undo-outer-limit 1006632960) ;; 960mb.
|
||
|
||
(require 'undo-fu-session)
|
||
(setq undo-fu-session-incompatible-files '("/COMMIT_EDITMSG\\'" "/git-rebase-todo\\'"))
|
||
(require 'vundo)
|
||
#+end_src
|
||
** Which Key
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'which-key)
|
||
(setq which-key-idle-delay 0.3)
|
||
(which-key-mode)
|
||
(when (boundp 'evil-mode)
|
||
(which-key-add-keymap-based-replacements evil-normal-state-map
|
||
"SPC f" '("Files")
|
||
"SPC b" '("Buffers")
|
||
"SPC B" '("Bookmarks")
|
||
"SPC c" '("Consult")
|
||
"SPC d" '("Dired")
|
||
"SPC g" '("Git")
|
||
"SPC m" '("Make")
|
||
"SPC t" '("Tabs")
|
||
"SPC p" '("Packages")
|
||
"SPC s" '("Shell (vterm)")
|
||
"SPC h" '("Help")))
|
||
#+end_src
|
||
** IDE Features
|
||
*** REPLs
|
||
#+begin_src emacs-lisp
|
||
|
||
(when (boundp 'evil-mode)
|
||
(evil-define-key 'insert comint-mode-map (kbd "C-n") 'comint-next-input)
|
||
(evil-define-key 'insert comint-mode-map (kbd "C-p") 'comint-previous-input))
|
||
#+end_src
|
||
*** Mono-complete
|
||
#+begin_src emacs-lisp
|
||
;; (evil-global-set-key 'insert (kbd "C-f") #'mono-complete-expand)
|
||
(require 'mono-complete)
|
||
(evil-global-set-key 'insert (kbd "M-/") #'mono-complete-expand-or-fallback)
|
||
(setq mono-complete-backends (list 'dabbrev 'filesystem))
|
||
(setq mono-complete-backends (list 'capf 'dabbrev 'filesystem))
|
||
|
||
(setq mono-complete-backend-capf-complete-fn #'eglot-completion-at-point)
|
||
(setq mono-complete-preview-delay 0.1)
|
||
|
||
(add-to-list 'face-remapping-alist '(mono-complete-preview-face . shadow))
|
||
(mono-complete-mode +1)
|
||
#+end_src
|
||
*** Eldoc
|
||
#+begin_src emacs-lisp
|
||
(evil-global-set-key 'normal (kbd "K") #'eldoc)
|
||
(global-eldoc-mode -1)
|
||
|
||
#+end_src
|
||
*** Eglot
|
||
#+begin_src emacs-lisp
|
||
(with-eval-after-load 'eglot
|
||
(global-eldoc-mode -1)
|
||
(flymake-mode -1)
|
||
(when (boundp 'evil-mode)
|
||
;; (evil-global-set-key 'normal (kbd "M-d") #'lsp-describe-thing-at-point)
|
||
(evil-global-set-key 'normal (kbd "SPC li") 'eglot-inlay-hints-mode)
|
||
(evil-global-set-key 'normal (kbd "<leader>cs") 'consult-eglot-symbols)))
|
||
#+end_src
|
||
*** COMMENT LSP
|
||
#+begin_src emacs-lisp
|
||
(require 'lsp-mode)
|
||
(setq lsp-enable-which-key-integration t)
|
||
(setq lsp-keymap-prefix "C-c c")
|
||
|
||
;; (setq lsp-ui-peek-always-show t)
|
||
;; (setq lsp-ui-doc-enable t)
|
||
;; TODO: Disable lsp sideline
|
||
(setq lsp-headerline-breadcrumb-enable nil)
|
||
(setq lsp-eldoc-enable-hover t)
|
||
(setq max-mini-window-height 0.25)
|
||
(setq lsp-eldoc-enable-hover t)
|
||
(setq lsp-ui-doc-enable nil)
|
||
(setq eldoc-echo-area-use-multiline-p 'truncate-sym-name-if-fit)
|
||
(setq eldoc-idle-delay 0)
|
||
(setq lsp-idle-delay 0.2)
|
||
|
||
;; Signatures
|
||
(setq lsp-signature-doc-lines t)
|
||
(setq lsp-signature-render-documentation nil)
|
||
(setq lsp-eldoc-render-all nil)
|
||
|
||
;; All this changes because we are using eglot now
|
||
(when (boundp 'evil-mode)
|
||
(evil-global-set-key 'normal (kbd "M-d") #'lsp-describe-thing-at-point)
|
||
(evil-global-set-key 'normal (kbd "<leader>lh") 'lsp-headerline-breadcrumb-mode)
|
||
(evil-global-set-key 'normal (kbd "SPC li") 'lsp-rust-analyzer-inlay-hints-mode)
|
||
(evil-global-set-key 'normal (kbd "<leader>cs") 'consult-lsp-symbols)
|
||
(evil-global-set-key 'normal (kbd "<leader>cf") 'consult-lsp-file-symbols))
|
||
|
||
;; This function allows us to extract Rust's true function signature
|
||
(cl-defmethod lsp-clients-extract-signature-on-hover (contents (_server-id (eql rust-analyzer)))
|
||
(-let* (((&hash "value") contents)
|
||
(groups (--partition-by (s-blank? it) (s-lines (s-trim value))))
|
||
(sig_group (if (s-equals? "```rust" (car (-third-item groups)))
|
||
(-third-item groups)
|
||
(car groups)))
|
||
(sig (--> sig_group
|
||
(--drop-while (s-equals? "```rust" it) it)
|
||
(--take-while (not (s-equals? "```" it)) it)
|
||
(--map (s-trim it) it)
|
||
(s-join " " it))))
|
||
(lsp--render-element (concat "```rust\n" sig "\n```"))))
|
||
|
||
(require 'lsp-ui)
|
||
|
||
#+end_src
|
||
*** COMMENT Flycheck
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'flycheck)
|
||
;; TODO Add wrapping to these functions
|
||
(when (boundp 'evil-mode)
|
||
(evil-global-set-key 'normal (kbd "M-e") #'flycheck-next-error)
|
||
(evil-global-set-key 'normal (kbd "M-E") #'flycheck-previous-error)
|
||
(evil-global-set-key 'normal (kbd "<leader>ee") 'flycheck-mode)
|
||
(evil-global-set-key 'normal (kbd "<leader>el") #'flycheck-list-errors)
|
||
(evil-global-set-key 'normal (kbd "<leader>ce") #'consult-lsp-diagnostics))
|
||
#+end_src
|
||
*** Compilation
|
||
#+begin_src emacs-lisp
|
||
(setq compilation-auto-jump-to-first-error t)
|
||
|
||
(defun joe/save-then-recompile ()
|
||
"Save the buffer before recompiling"
|
||
(interactive)
|
||
(when (buffer-file-name)
|
||
(save-buffer))
|
||
(recompile))
|
||
|
||
(defun joe/save-if-file-ignore-args (&rest _)
|
||
"Save the buffer before recompiling"
|
||
(interactive)
|
||
(joe/save-if-file))
|
||
|
||
(defun joe/save-if-file ()
|
||
"Save buffer only if it has a backing file"
|
||
(interactive)
|
||
(when (buffer-file-name)
|
||
(save-buffer)))
|
||
|
||
(add-hook 'evil-insert-state-exit-hook #'joe/save-if-file)
|
||
;; (add-hook 'after-change-functions #'joe/save-if-file-ignore-args)
|
||
;; (define-key global-map (kbd "C-x C-s) #'save-buffer)
|
||
(define-key global-map (kbd "<f9>") #'joe/save-then-recompile)
|
||
(define-key global-map (kbd "<f10>") #'compile)
|
||
|
||
(defun joe/colorize-compilation-buffer ()
|
||
(ansi-color-apply-on-region compilation-filter-start (point)))
|
||
(add-hook 'compilation-filter-hook 'joe/colorize-compilation-buffer)
|
||
|
||
(defun joe/close-compilation-if-no-warn-err (buffer string)
|
||
"Bury a compilation buffer if succeeded without warnings "
|
||
(if (and
|
||
(string-match "compilation" (buffer-name buffer))
|
||
(string-match "finished" string)
|
||
(not
|
||
(with-current-buffer (current-buffer)
|
||
(search-forward "warning" nil t))))
|
||
(run-with-timer 1.5 nil
|
||
(lambda ()
|
||
(and (eq (point) 1)
|
||
(string-match "compilation" (buffer-name (current-buffer)))
|
||
(popper-close-latest))))))
|
||
(add-hook 'compilation-finish-functions 'joe/close-compilation-if-no-warn-err)
|
||
#+end_src
|
||
** Debugging
|
||
*** COMMENT DAP
|
||
#+begin_src emacs-lisp
|
||
(require 'dap-mode)
|
||
;; (setq dap-auto-configure-features '(locals breakpoints expressions tooltip))
|
||
|
||
(require 'dap-cpptools)
|
||
(dap-cpptools-setup)
|
||
(add-hook 'dap-stopped-hook
|
||
(lambda (arg) (call-interactively #'dap-hydra)))
|
||
|
||
(setq dap-cpptools-extension-version "1.12.1")
|
||
|
||
(setq dap-default-terminal-kind "integrated")
|
||
(dap-auto-configure-mode +1)
|
||
(dap-register-debug-template
|
||
"Rust::CppTools Run Configuration"
|
||
(list :type "cppdbg"
|
||
:request "launch"
|
||
:name "Rust::Run"
|
||
:MIMode "gdb"
|
||
:miDebuggerPath "rust-gdb"
|
||
:environment []
|
||
:program "${workspaceFolder}/target/debug/kanban-tui"
|
||
:cwd "${workspaceFolder}"
|
||
:console "external"
|
||
:dap-compilation "cargo build"
|
||
:dap-compilation-dir "${workspaceFolder}"))
|
||
#+end_src
|
||
** Languages
|
||
*** Python
|
||
#+begin_src emacs-lisp
|
||
(require 'elpy)
|
||
|
||
(define-key inferior-python-mode-map (kbd "C-n") #'comint-next-input)
|
||
(define-key inferior-python-mode-map (kbd "C-p") #'comint-previous-input)
|
||
#+end_src>
|
||
*** Rust
|
||
#+begin_src emacs-lisp
|
||
(require 'rustic)
|
||
(require 'ob-rust)
|
||
;; Org-Babel
|
||
;; Disabling until we figure out how to get it working
|
||
;; (elpaca 'parsec) ;; Required by evcxr-mode
|
||
|
||
;; (elpaca
|
||
;; '(evcxr
|
||
;; :type git
|
||
;; :host github
|
||
;; :repo "serialdev/evcxr-mode"))
|
||
|
||
(add-hook 'rust-mode-hook (lambda () (electric-pair-local-mode)))
|
||
|
||
(add-hook 'rust-mode-hook
|
||
(lambda ()
|
||
(evcxr-minor-mode)
|
||
(electric-pair-local-mode)))
|
||
|
||
(with-eval-after-load 'rustic
|
||
;; Don't autostart
|
||
(setq rustic-lsp-setup-p nil)
|
||
(define-key rustic-mode-map (kbd "<f9>") #'rustic-cargo-run)
|
||
|
||
(setq lsp-rust-analyzer-server-display-inlay-hints t)
|
||
(setq lsp-rust-analyzer-display-lifetime-elision-hints-enable "always")
|
||
(setq lsp-rust-analyzer-display-chaining-hints t)
|
||
(setq lsp-rust-analyzer-display-lifetime-elision-hints-use-parameter-names t)
|
||
(setq lsp-rust-analyzer-display-closure-return-type-hints t)
|
||
(setq lsp-rust-analyzer-display-parameter-hints t)
|
||
(setq lsp-rust-analyzer-display-reborrow-hints t)
|
||
(setq lsp-rust-analyzer-cargo-watch-command "clippy"))
|
||
#+end_src
|
||
*** Elisp
|
||
#+begin_src emacs-lisp
|
||
(global-set-key (kbd "C-x C-r") 'eval-region)
|
||
(evil-define-key 'insert emacs-lisp-mode-map (kbd "C-j") 'eval-print-last-sexp)
|
||
#+end_src
|
||
*** Web
|
||
#+begin_src emacs-lisp
|
||
(require 'typescript-mode)
|
||
(setq typescript-indent-level 2)
|
||
#+end_src
|
||
*** SQL
|
||
#+begin_src emacs-lisp
|
||
(defun joe/mark-sql-defun ()
|
||
"Mark the current SQL function definition."
|
||
(interactive)
|
||
(let ((beg (save-excursion
|
||
(re-search-backward "^create or replace function [^(]+" nil t)
|
||
(match-end 0)))
|
||
(end (save-excursion
|
||
(re-search-forward "\\(language\\)" nil t)
|
||
(line-end-position))))
|
||
(when (and beg end)
|
||
(goto-char beg)
|
||
(set-mark end)))
|
||
(evil-visual-line)
|
||
(evil-visual-line))
|
||
;; This is required so that .dir-locals that read env files for credentials works
|
||
(require 'dotenv)
|
||
(defun joe/sql-mode-hook ()
|
||
(define-key sql-mode-map (kbd "C-M-h") #'joe/mark-sql-defun)
|
||
(define-key sql-mode-map (kbd "<f8>") #'sql-connect))
|
||
|
||
(add-hook 'sql-mode-hook #'joe/sql-mode-hook)
|
||
|
||
(defun joe/sql-save-history-hook ()
|
||
(let ((lval 'sql-input-ring-file-name)
|
||
(rval 'sql-product))
|
||
(if (symbol-value rval)
|
||
(let ((filename
|
||
(concat user-emacs-directory "sql/"
|
||
(symbol-name (symbol-value rval))
|
||
"-history.sql")))
|
||
(set (make-local-variable lval) filename))
|
||
(error
|
||
(format "SQL history will not be saved because %s is nil"
|
||
(symbol-name rval))))))
|
||
|
||
(add-hook 'sql-interactive-mode-hook 'joe/sql-save-history-hook)
|
||
|
||
(defun joe/sql-login-hook ()
|
||
"Custom SQL log-in behaviours. See `sql-login-hook'."
|
||
;; n.b. If you are looking for a response and need to parse the
|
||
;; response, use `sql-redirect-value' instead of `comint-send-string'.
|
||
(when (eq sql-product 'postgres)
|
||
(let ((proc (get-buffer-process (current-buffer))))
|
||
;; Output each query before executing it. (n.b. this also avoids
|
||
;; the psql prompt breaking the alignment of query results.)
|
||
(comint-send-string proc "\\set ECHO queries\n"))))
|
||
|
||
(add-hook 'sql-login-hook 'joe/sql-login-hook)
|
||
#+end_src
|
||
*** C
|
||
|
||
Design some basic functions for compiling. There's also a hook to close the popper window if there
|
||
are no warnings or errors. It will check if we are still in the compilation buffer as well as
|
||
whether the point hasn't been moved. This way, if I switched to another popper buffer, like vterm,
|
||
it doesn't close it.
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'disaster)
|
||
(defun joe/c-mode-hook ()
|
||
(local-set-key (kbd "C-x c r") (defun joe/make-run () (interactive) (compile "make run")))
|
||
(local-set-key (kbd "C-x c c") (defun joe/make () (interactive) (compile "make")))
|
||
(electric-pair-local-mode t)
|
||
(c-toggle-comment-style -1))
|
||
(add-hook 'c-mode-hook #'joe/c-mode-hook)
|
||
#+end_src
|
||
*** COMMENT Haskell
|
||
#+begin_src emacs-lisp
|
||
(require 'haskell-mode)
|
||
(setq haskell-interactive-popup-errors nil)
|
||
(when (boundp 'evil-mode)
|
||
|
||
(evil-define-key 'insert haskell-interactive-mode-map (kbd "C-n") #'haskell-interactive-mode-history-next)
|
||
(evil-define-key 'insert haskell-interactive-mode-map (kbd "C-p") #'haskell-interactive-mode-history-previous))
|
||
|
||
#+end_src
|
||
*** COMMENT Clojure
|
||
#+begin_src emacs-lisp
|
||
(require 'clojure-mode)
|
||
(require 'cider)
|
||
(setq cider-show-error-buffer 'only-in-repl)
|
||
#+end_src
|
||
*** COMMENT OCaml
|
||
#+begin_src emacs-lisp
|
||
(require 'tuareg)
|
||
(require 'dune)
|
||
(require 'utop)
|
||
(require 'merlin)
|
||
(require 'merlin-eldoc)
|
||
;; Might be worth checking out, depeding on whether we stick with flycheck or not
|
||
;; (elpaca 'flycheck-ocaml)
|
||
;; Also check this out, see if it adds anything
|
||
;; (require 'ocp-indent)
|
||
|
||
(defun opam-env ()
|
||
"Load the opam env to get the PATH variables so everything works"
|
||
(interactive nil)
|
||
(dolist (var
|
||
(car (read-from-string
|
||
(shell-command-to-string "opam config env --sexp"))))
|
||
(setenv (car var) (cadr var))))
|
||
(setq opam-share
|
||
(substring (shell-command-to-string
|
||
"opam config var share 2> /dev/null") 0 -1))
|
||
(add-to-list 'load-path (expand-file-name "emacs/site-lisp" opam-share))
|
||
#+end_src
|
||
|
||
We won't use the LSP server but rather directly talk to Merlin, since I guess LSP just wraps Merlin
|
||
and there's no need for a middle-man when it's already been implemented.
|
||
|
||
#+begin_src emacs-lisp
|
||
|
||
;; (require 'utop)
|
||
;; Use the opam installed utop
|
||
(setq utop-command "opam exec -- utop -emacs")
|
||
|
||
(let ((opam-share (ignore-errors (car (process-lines "opam" "var" "share")))))
|
||
(when (and opam-share (file-directory-p opam-share))
|
||
;; Register Merlin
|
||
(add-to-list 'load-path (expand-file-name "emacs/site-lisp" opam-share))
|
||
(autoload 'merlin-mode "merlin" nil t nil)
|
||
;; Automatically start it in OCaml buffers
|
||
(add-hook 'tuareg-mode-hook 'merlin-mode t)
|
||
(add-hook 'caml-mode-hook 'merlin-mode t)
|
||
;; Use opam switch to lookup ocamlmerlin binary
|
||
(setq merlin-command 'opam)))
|
||
#+end_src
|
||
*** COMMENT FSharp
|
||
#+begin_src emacs-lisp
|
||
(require 'fsharp-mode)
|
||
;; (elpaca 'eglot-fsharp)
|
||
|
||
#+end_src
|
||
*** COMMENT Go
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'go-mode)
|
||
;; (elpaca 'go-imports)
|
||
#+end_src
|
||
*** Other
|
||
#+begin_src emacs-lisp
|
||
(require 'json-mode)
|
||
(require 'markdown-mode)
|
||
#+end_src
|
||
** Org Mode
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'org-bullets)
|
||
|
||
(defun joe/org-init ()
|
||
(setq org-todo-keywords '((sequence "TODO" "IN-PROGRESS" "|" "DONE" "BACKLOG")))
|
||
(setq org-agenda-files '("~/todo.org"))
|
||
|
||
(org-babel-do-load-languages
|
||
'org-babel-load-languages
|
||
'((emacs-lisp . t)
|
||
(makefile . t)
|
||
(ocaml . t)
|
||
(python . t)
|
||
(C . t)
|
||
(haskell . t)
|
||
;; (rust . t)
|
||
(shell . t)))
|
||
|
||
(require 'org-tempo)
|
||
(add-to-list 'org-structure-template-alist '("el" . "src emacs-lisp"))
|
||
(add-to-list 'org-structure-template-alist '("ml" . "src ocaml"))
|
||
(add-to-list 'org-structure-template-alist '("rs" . "src rust"))
|
||
(add-to-list 'org-structure-template-alist '("py" . "src python"))
|
||
(add-to-list 'org-structure-template-alist '("hs" . "src haskell"))
|
||
(add-to-list 'org-structure-template-alist '("sh" . "src shell"))
|
||
(add-to-list 'org-structure-template-alist '("cc" . "src C :includes stdio.h stdlib.h"))
|
||
(setq org-edit-src-content-indentation 0))
|
||
(with-eval-after-load 'org (joe/org-init))
|
||
|
||
(setq org-blank-before-new-entry
|
||
'((heading . nil)
|
||
(plain-list-item . nil)))
|
||
(setq org-cycle-separator-lines 1)
|
||
(setq org-hide-emphasis-markers t)
|
||
(setq org-src-window-setup 'current-window)
|
||
|
||
(defun joe/org-hook ()
|
||
(local-set-key (kbd "C-. C-i") 'consult-org-heading)
|
||
(org-bullets-mode)
|
||
(org-indent-mode))
|
||
(add-hook 'org-mode-hook 'joe/org-hook)
|
||
|
||
(require 'org-kanban)
|
||
#+end_src
|
||
** Magit
|
||
|
||
The best git porcelain/client I've ever used. Also kill stray magit buffers left over as explained [[https://manueluberti.eu/emacs/2018/02/17/magit-bury-buffer/][here]]
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'magit)
|
||
|
||
(defun joe/magit-kill-buffers (param)
|
||
"Restore window configuration and kill all Magit buffers."
|
||
(let ((buffers (magit-mode-get-buffers)))
|
||
(magit-restore-window-configuration)
|
||
(mapc #'kill-buffer buffers)))
|
||
|
||
(when (boundp 'evil-mode)
|
||
(add-hook 'with-editor-mode-hook 'evil-insert-state))
|
||
|
||
(setq magit-bury-buffer-function #'joe/magit-kill-buffers)
|
||
(setq magit-clone-set-remote.pushDefault t)
|
||
(setq magit-clone-default-directory "~/Development/")
|
||
;; (define-key magit-mode-map "h" 'backward-char)
|
||
;; (define-key magit-mode-map "l" 'backward-char)
|
||
|
||
#+end_src
|
||
** Restclient
|
||
|
||
#+begin_src emacs-lisp
|
||
(require 'restclient)
|
||
|
||
(add-to-list 'auto-mode-alist '("\\.restclient\\'" . restclient-mode))
|
||
|
||
(with-eval-after-load 'restclient
|
||
(define-key restclient-mode-map (kbd "C-c C-c") #'restclient-http-send-current-stay-in-window)
|
||
(define-key restclient-mode-map (kbd "C-c C-v") #'restclient-http-send-current))
|
||
|
||
#+end_src
|
||
|
||
|
||
|
||
* COMMENT Local variables
|
||
;; Local Variables:
|
||
;; eval: (olivetti-mode t)
|
||
;; eval: (add-hook 'after-save-hook '(lambda () (org-babel-tangle)) nil t)
|
||
;; End:
|