Skip to content

Latest commit

 

History

History
821 lines (654 loc) · 24.5 KB

README.org_archive_old

File metadata and controls

821 lines (654 loc) · 24.5 KB

Archived entries from file home/dan.doom.d/README.org

Emacs X Window Management (EXWM)

I love EXWM, but retreated back to XMonad. I had issues with both Emacs and Firefox causing the main thread to block, which (in EXWM) hangs the entire system.

About

Pros:

  • System-wide UI consistency
    • X windows and Emacs windows are treated the same
      • e.g. Use Ivy to surface Firefox windows with fuzzy search
    • Key simulation allows consistent keybindings (e.g. the copy/paste bindings can be made the same between Emacs, browsers, terminals, etc…)
  • Interactively update WM configuration
    • Unlike e.g. XMonad, which requires a re-compile + restart
    • Can add new bindings and immediately use them
  • No separate WM install + config. It’s just Emacs + Elisp.

Cons:

  • Need to be careful not to block the main thread! That will lock the entire system.
    • Workaround: just spawn a secondary Emacs within the base Emacs whenever there’s a risk of blocking.
      • e.g. Before using TRAMP, spawn a fresh Emacs.
  • Less stable than XMonad, which is a tiny, well-tested Haskell program
  • Limited support for managing multiple screens.
    • It works, but it hardwires each workspace to a specific monitor.
  • Need to be careful not to leave your Emacs configuration in a broken state.
    • Fallbacks include other WMs installed (XMonad) or switching to a tty (Ctrl-Alt-f#)

Create the config directory where Doom expects it

mkdir -p ./modules/desktop/exwm

Package Declarations

;; -*- no-byte-compile: t; -*-
;;; desktop/exwm/packages.el
(package! exwm)
;; (package! exwm-firefox
;;   :recipe (:host github :repo "ieure/exwm-firefox"))
;; (package! exwm-mff
;;   :recipe (:host github :repo "ieure/exwm-mff"))
(package! xelb)
(package! exwm-edit)

EXWM Configuration

  ;;; desktop/exwm/config.el -*- lexical-binding: t; -*-
(use-package! exwm
  :init
  (setq
   mouse-autoselect-window t
   focus-follows-mouse t)
  :config
  (setq exwm-workspace-number 9))

(defun my/exwm-rename-buffer-to-title () (exwm-workspace-rename-buffer (format "%s - %s" exwm-class-name exwm-title)))
(setq exwm-workspace-show-all-buffers t
      exwm-layout-show-all-buffers t
      exwm-manage-force-tiling t)


(setq exwm-input-prefix-keys '(?\s- ))

(display-battery-mode 1)
(display-time-mode 1)


;; (setq exwm-manage-configurations
;;       '(((string= exwm-class-name "Google-chrome")
;;          workspace 0)
;;         ((string= exwm-class-name "Firefox")
;;          workspace 1)
;;         ((string= exwm-instance-name "terminator")
;;          workspace 8)
;;         ((string= exwm-instance-name "keybase")
;;          workspace 9)))


(defun my/launch (command)
  (interactive (list (read-shell-command "$ ")))
  (start-process-shell-command command nil command))

(defun my/launch-terminal ()
  (interactive)
  (my/launch "terminator"))

(defun my/launch-browser ()
  (interactive)
  (my/launch "firefox"))

(defun my/launch-emacs ()
  (interactive)
  (my/launch "emacs"))

(defun my/lock-screen ()
  (interactive)
  (my/launch "xtrlock -b"))

(defun my/volume-up ()
  (interactive)
  (my/launch "amixer sset Master unmute")
  (my/launch "amixer sset Master 5%+"))

(defun my/volume-down ()
  (interactive)
  (my/launch "amixer sset Master 5%-"))

(setq exwm-workspace-minibuffer-position 'nil)

(exwm-input-set-key (kbd "s-:") #'eval-expression)

;; https://emacs.stackexchange.com/questions/33326/how-do-i-cut-and-paste-effectively-between-applications-while-using-exwm
(defun my/exwm-input-line-mode ()
  "Set exwm window to line-mode and show mode line"
  (call-interactively #'exwm-input-grab-keyboard))

(defun my/exwm-input-char-mode ()
  "Set exwm window to char-mode and hide mode line"
  (call-interactively #'exwm-input-release-keyboard))

(defun my/exwm-input-toggle-mode ()
  "Toggle between line- and char-mode"
  (interactive)
  (with-current-buffer (window-buffer)
    (when (eq major-mode 'exwm-mode)
      (if (equal (second (second mode-line-process)) "line")
          (my/exwm-input-char-mode)
        (my/exwm-input-line-mode)))))

(defun my/toggle-exwm-input-line-mode-passthrough ()
  (interactive)
  (if exwm-input-line-mode-passthrough
      (progn
        (setq exwm-input-line-mode-passthrough nil)
        (message "App receives all the keys now (with some simulation)"))
    (progn
      (setq exwm-input-line-mode-passthrough t)
      (message "emacs receives all the keys now")))
  (force-mode-line-update))

(exwm-input-set-key (kbd "s-;") 'my/toggle-exwm-input-line-mode-passthrough)



;; Switch to last workspace
(defvar my/exwm-workspace-previous-index 0 "The previous active workspace index.")

(defun my/exwm-workspace--current-to-previous-index (_x &optional _y)
  (setq my/exwm-workspace-previous-index exwm-workspace-current-index))

(advice-add 'exwm-workspace-switch :before #'my/exwm-workspace--current-to-previous-index)

(defun my/exwm-workspace-switch-to-previous ()
  (interactive)
  "Switch to the previous active workspace."
  (let ((index my/exwm-workspace-previous-index))
    (exwm-workspace-switch index)))

(defun my/switch-to-last-buffer ()
  "Switch to last open buffer in current window."
  (interactive)
  (switch-to-buffer (other-buffer (current-buffer) 1)))

;; Re-use muscle memory from 6 years of an xmonad setup
(exwm-input-set-key (kbd "s-p") #'dmenu)
(exwm-input-set-key (kbd "s-P") #'counsel-linux-app)
(exwm-input-set-key (kbd "s-s") #'password-store-copy)
(exwm-input-set-key (kbd "s-<return>") #'my/launch-terminal)
(exwm-input-set-key (kbd "s-.") #'my/switch-to-last-buffer)
(exwm-input-set-key (kbd "s-,") #'my/exwm-workspace-switch-to-previous)
(exwm-input-set-key (kbd "s-i") #'my/launch-browser)
(exwm-input-set-key (kbd "s-b") 'switch-to-buffer)
(exwm-input-set-key (kbd "s-M-O") #'my/lock-screen)
(exwm-input-set-key (kbd "s-<up>") #'my/volume-up)
(exwm-input-set-key (kbd "s-<down>") #'my/volume-down)
;; (exwm-input-set-key (kbd "s-<print>") #'my/screen-to-clipboard)

(exwm-input-set-key (kbd "s-R") #'doom/reload)
(exwm-input-set-key (kbd "s-Q") #'kill-emacs)

(exwm-input-set-key (kbd "s-m") #'bury-buffer)
(exwm-input-set-key (kbd "s-M") #'unbury-buffer)

(exwm-input-set-key (kbd "s-j") #'other-window)
(exwm-input-set-key (kbd "s-k") #'rev-other-window)

(exwm-input-set-key (kbd "s-J") #'previous-buffer)
(exwm-input-set-key (kbd "s-K") #'next-buffer)

(exwm-input-set-key (kbd "s-h") 'shrink-window)
(exwm-input-set-key (kbd "s-l") 'enlarge-window)
(exwm-input-set-key (kbd "s-H") 'shrink-window-horizontally)
(exwm-input-set-key (kbd "s-L") 'enlarge-window-horizontally)

(exwm-input-set-key (kbd "s-/") 'winner-undo)
(exwm-input-set-key (kbd "s-?") 'winner-redo)

(exwm-input-set-key (kbd "s-'") 'exwm-edit--compose)

(exwm-input-set-key (kbd "s-w") 'delete-window)
(exwm-input-set-key (kbd "s-q") 'kill-this-buffer)

(exwm-input-set-key (kbd "s-C") 'cfw:open-org-calendar)

(exwm-input-set-key (kbd "s-x") 'counsel-M-x)

(exwm-input-set-key (kbd "s-t") 'vterm)

(exwm-input-set-key (kbd "s-<f7>") 'my/monitor-screen-layout)
(exwm-input-set-key (kbd "s-<f8>") 'my/laptop-screen-layout)

(mapcar (lambda (i)
          (exwm-input-set-key (kbd (format "s-%d" i))
                              `(lambda ()
                                 (interactive)
                                 (exwm-workspace-switch-create ,i))))
        (number-sequence 0 9))

;; Configure firefox to open every tab as a new window instead
;; http://p.hagelb.org/exwm-ff-tabs.html
(add-hook 'exwm-manage-finish-hook
          (lambda ()
            ;; these have their own Emacs simulation installed (e.g. Surfingkeys)
            (if (or (string= exwm-class-name "Firefox")
                    (string= exwm-class-name "Google-chrome")
                    (string= exwm-class-name "Atom"))
                (progn
                  (exwm-input-set-local-simulation-keys
                   `(([?\s-w] . [?\C-w])
                     ([?\M-w] . [?\C-c])
                     ([?\C-y] . [?\C-v])
                     ([?\C-w] . [?\C-x])))
                  (exwm-layout-hide-mode-line))
              (exwm-layout-show-mode-line))))

;; (add-hook 'exwm-update-title-hook
;;           (defun my/exwm-title-hook ()
;;             (when (string-match "Firefox" exwm-class-name)
;;               (exwm-workspace-rename-buffer exwm-title))))

(add-hook 'exwm-update-title-hook 'my/exwm-rename-buffer-to-title)

(setq browse-url-firefox-arguments '("-new-window"))

(setq exwm-input-simulation-keys
      '(
        ;; movement
        ([?\C-b] . [left])
        ([?\M-b] . [C-left])
        ([?\C-f] . [right])
        ([?\M-f] . [C-right])
        ([?\C-p] . [up])
        ([?\C-n] . [down])
        ([?\C-e] . [end])
        ([?\M-v] . [prior])
        ([?\C-v] . [next])
        ([?\C-d] . [delete])
        ;; undo
        ([?\C-/] . [?\C-z])

        ;; Interferes with Slack
        ;; ([?\C-k] . [S-end delete])

        ;; cut/copy/paste.
        ([?\C-w] . [?\C-x])
        ([?\M-w] . [?\C-c])
        ([?\C-y] . [?\C-v])
        ;; search
        ([?\C-s] . [?\C-f])))

(define-ibuffer-column exwm-class (:name "Class")
  (if (bound-and-true-p exwm-class-name)
      exwm-class-name
    ""))
(define-ibuffer-column exwm-instance (:name "Instance")
  (if (bound-and-true-p exwm-instance-name)
      exwm-instance-name
    ""))
(define-ibuffer-column exwm-urgent (:name "U")
  (if (bound-and-true-p exwm--hints-urgency)
      "U"
    " "))

(defun my/exwm-ibuffer (&optional other-window)
  (interactive "P")
  (let ((name (buffer-name)))
    (ibuffer other-window
             "*exwm-ibuffer*"
             '((mode . exwm-mode))
             nil nil nil
             '((mark exwm-urgent
                     " "
                     (name 64 64 :left :elide)
                     " "
                     (exwm-class 20 -1 :left)
                     " "
                     (exwm-instance 10 -1 :left))))
    (ignore-errors (ibuffer-jump-to-buffer name))))

(exwm-input-set-key (kbd "s-o") #'my/exwm-ibuffer)

(use-package! exwm-edit
  :init
  ;; Otherwise it steals C-c ' from org
  (setq exwm-edit-bind-default-keys nil))

(defun my/exwm-start-in-char-mode ()
  (when (or (string-prefix-p "terminator" exwm-instance-name)
            (string-prefix-p "emacs" exwm-instance-name)
            (string-prefix-p "next" exwm-instance-name))
    (exwm-input-release-keyboard (exwm--buffer->id (window-buffer)))))
(add-hook 'exwm-manage-finish-hook 'my/exwm-start-in-char-mode)

(require 'exwm-randr)
;; FIXME
(setq exwm-randr-workspace-monitor-plist '(0 "eDP-1"
                                             1 "HDMI-1"
                                             1 "HDMI-1"
                                             2 "HDMI-1"
                                             3 "HDMI-1"
                                             4 "HDMI-1"
                                             5 "HDMI-1"
                                             6 "HDMI-1"
                                             7 "HDMI-1"
                                             8 "HDMI-1"
                                             9 "HDMI-1"))


(require 'exwm-randr)
(exwm-randr-enable)

;; (exwm-enable)

;; (use-package! exwm-mff
;;   :config
;;   (exwm-mff-mode 1))

Launch Script

#!/bin/bash

# Disable access control for the current user.
xhost +SI:localuser:$USER

# Identify the home of our gtkrc file, important for setting styles of
# gtk-based applications
export GTK2_RC_FILES="$HOME/.gtkrc-2.0"


# Make Java applications aware this is a non-reparenting window manager.
export _JAVA_AWT_WM_NONREPARENTING=1

# Bind caps to ctrl
setxkbmap -option 'ctrl:nocaps'

# set keyboard rate
xset r rate 160 50

xsetroot -solid black

# Set default cursor.
xsetroot -cursor_name left_ptr

# Nix + direnv
# lorri daemon &

# Email sync
offlineimap &

# Uncomment the following block to use the exwm-xim module.
# export XMODIFIERS=@im=exwm-xim
# export GTK_IM_MODULE=xim
# export QT_IM_MODULE=xim
# export CLUTTER_IM_MODULE=xim

source ~/.profile

# Sync Doom
# ~/.emacs.d/bin/doom sync

# Finally start Emacs
exec ~/.emacs.d/bin/doom run

XSession Configuration

This gets picked up by DM

[Desktop Entry]
Encoding=UTF-8
Name=EXWM
Comment=Emacs X WM
Exec=/home/dan/.doom.d/modules/desktop/exwm/launch-exwm.sh
Type=XSession

Deft: Note search

;; Helpful command: (deft-refresh)
(setq deft-recursive t
      ;; Otherwise too slow
      deft-file-limit 100)

(map! "<f8>" 'my/deft)
(defun my/deft ()
  (interactive)
  (let ((deft-directory org-roam-directory))
    (deft)))

org-roam-server: Graph visualization / navigation

(use-package! org-roam-server
  :config
  (setq org-roam-server-host "127.0.0.1"
        org-roam-server-port 8081
        org-roam-server-authenticate nil
        org-roam-server-export-inline-images t
        org-roam-server-serve-files nil
        org-roam-server-served-file-extensions '("pdf" "mp4" "ogv")
        org-roam-server-network-poll t
        org-roam-server-network-arrows nil
        org-roam-server-network-label-truncate t
        org-roam-server-network-label-truncate-length 60
        org-roam-server-network-label-wrap-length 20))

org-journal: Managing daily journal files

(use-package! org-journal
  :after org
  :config
  (customize-set-variable 'org-journal-dir (concat org-roam-directory "journal"))
  (customize-set-variable 'org-journal-file-format "private-%Y-%m-%d.org")
  (customize-set-variable 'org-journal-date-prefix "#+TITLE: ")
  (customize-set-variable 'org-journal-time-prefix "* ")
  (customize-set-variable 'org-journal-time-format "")
  (customize-set-variable 'org-journal-carryover-items "TODO=\"TODO\"")
  (customize-set-variable 'org-journal-date-format "%Y-%m-%d")
  (map! :leader
        (:prefix-map ("n" . "notes")
         (:prefix ("j" . "journal")
          :desc "Today" "t" #'org-journal-today)))
  (defun org-journal-today ()
    (interactive)
    (org-journal-new-entry t)))

wgrep

Replaced by deadgrep-edit-mode

(use-package! wgrep
  :config
  (autoload 'wgrep-deadgrep-setup "wgrep-deadgrep")
  (add-hook 'deadgrep-finished-hook 'wgrep-deadgrep-setup))

Old

(use-package! ob-rust)

cargo-script required for org-babel blocks (otherwise each requires a main function)

cargo install cargo-script

Performance Tweaks

Swiper

Seems like a good solution to swiper being slow is to just use swiper-isearch, but I find swiper’s handling of multiple results on a line more convenient most of the time.

Instead, I follow advice from this Reddit comment to make swiper ignore visual line mode. Seems to help for now.

(setq swiper-use-visual-line nil)
(setq swiper-use-visual-line-p (lambda (a) nil))

Ivy

Ivy allows you to find the input to a command by incrementally searching the space of all valid inputs. It’s well-supported in Doom.

(after! ivy
  ;; Causes open buffers and recentf to be combined in ivy-switch-buffer
  (setq ivy-use-virtual-buffers t
        counsel-find-file-at-point t
        ivy-wrap nil
        ivy-posframe-display-functions-alist '((t . ivy-posframe-display-at-frame-top-center))
        ivy-posframe-height-alist '((t . 20))
        ivy-posframe-parameters '((internal-border-width . 1))
        ivy-posframe-width 100)
  (add-hook 'eshell-mode-hook
            (lambda ()
              (eshell-cmpl-initialize)
              (define-key eshell-mode-map (kbd "M-r") 'counsel-esh-history)))
  (add-to-dk-keymap
   '(("g" . +ivy/project-search)
     ("h" . +ivy/projectile-find-file)
     ("i" . counsel-semantic-or-imenu)
     ("j" . ivy-switch-buffer))))

Debugging

(use-package! dap-mode
  :config
  ;; (dap-ui-mode)
  ;; (dap-ui-controls-mode 1)

  (require 'dap-lldb)
  (require 'dap-gdb-lldb)
  ;; installs .extension/vscode
  (dap-gdb-lldb-setup)
  (dap-register-debug-template
   "Rust::LLDB Run Configuration"
   (list :type "lldb"
         :request "launch"
         :name "LLDB::Run"
	 :gdbpath "rust-lldb"
         :target nil
         :cwd nil)))

For Rust

Had to build lldb from source to get lldb-mi. Required installing lldb and liblldb-dev to build.

Hit when debugin with char types: rust-lang/rust#29154 Floating point exception when debugging with lldb on Mac…

undefinederror: need to add support for DW_TAG_base_type 'char' encoded with DW_ATE = 0x8, bit_size = 32

Also, breaks swiper :(

Poly Markdown

Used with *.jmd literate Julia files (see Weave.jl)

(use-package! poly-markdown)

Additional Web Dev Tooling

Including the Doom javascript module does most of the work…

(use-package! jest
  :hook
  (typescript-mode . jest-minor-mode))

Better Org Babel

Fast Block Insertion

(use-package! company-org-block
  :custom
  (company-org-block-edit-style 'inline) ;; 'auto, 'prompt, or 'inline
  :hook ((org-mode . (lambda ()
                       (setq-local company-backends '(company-org-block))
                       (company-mode +1)))))

Custom rustic-org-babel

~/.emacs.d/.local/straight/repos/rustic/rustic-orb-babel.el

Added to Rustic optional lib load

(customize-set-variable 'rustic-babel-display-compilation-buffer t)
(add-to-list 'org-structure-template-alist '("or" . "src org-rust"))
(customize-set-variable 'rustic-babel-format-src-block t)

Org-babel setup

Remote

(defun my/register-remote-rust-analyzer ()
  (interactive)
  (lsp-register-client
   (make-lsp-client
    :new-connection (lsp-tramp-connection "rust-analyzer")
    ;; (lsp-tramp-connection
    ;;  (lambda ()
    ;;    `(,(or (executable-find
    ;;            (cl-first lsp-rust-analyzer-server-command))
    ;;           (lsp-package-path 'rust-analyzer)
    ;;           "rust-analyzer")
    ;;      ,@(cl-rest lsp-rust-analyzer-server-args))))
    :remote? t
    :major-modes '(rust-mode rustic-mode)
    :initialization-options 'lsp-rust-analyzer--make-init-options
    :notification-handlers (ht<-alist lsp-rust-notification-handlers)
    :action-handlers (ht ("rust-analyzer.runSingle" #'lsp-rust--analyzer-run-single))
    :library-folders-fn (lambda (_workspace) lsp-rust-library-directories)
    :after-open-fn (lambda ()
                     (when lsp-rust-analyzer-server-display-inlay-hints
                       (lsp-rust-analyzer-inlay-hints-mode)))
    :ignore-messages nil
    :server-id 'rust-analyzer-remote)))

Over TRAMP

GitHub - brotzeit/rustic: Rust development environment for Emacs

(defun start-file-process-shell-command@around (start-file-process-shell-command name buffer &rest args)
  "Start a program in a subprocess.  Return the process object for it. Similar to `start-process-shell-command', but calls `start-file-process'."
  ;; On remote hosts, the local `shell-file-name' might be useless.
  (let ((command (mapconcat 'identity args " ")))
    (funcall start-file-process-shell-command name buffer command)))

(advice-add 'start-file-process-shell-command :around #'start-file-process-shell-command@around)
(with-eval-after-load "lsp-rust"
  (lsp-register-client
   (make-lsp-client
    :new-connection (lsp-tramp-connection "rust-analyzer")
    :remote? t
    :major-modes '(rust-mode rustic-mode)
    :initialization-options 'lsp-rust-analyzer--make-init-options
    :notification-handlers (ht<-alist lsp-rust-notification-handlers)
    :action-handlers (ht ("rust-analyzer.runSingle" #'lsp-rust--analyzer-run-single))
    :library-folders-fn (lambda (_workspace) lsp-rust-library-directories)
    :after-open-fn (lambda ()
                     (when lsp-rust-analyzer-server-display-inlay-hints
                       (lsp-rust-analyzer-inlay-hints-mode)))
    :ignore-messages nil
    :server-id 'rust-analyzer-remote)))