+
Skip to content

gwb/dot-emacs

Repository files navigation

There are a number of small irritants that need to be fixed:

[x] Fix orderless so it does case-insensitive and regexp matches.

Basics

Emacs 28 fixes

(use-package gwb-fix-obsolescence
  :demand t)

Better defaults

(setq ring-bell-function 'ignore)
(fset 'yes-or-no-p 'y-or-n-p)
(show-paren-mode)
(set-fill-column 80)
(setq column-number-mode t)
(setq-default indent-tabs-mode nil)
(setq uniquify-buffer-name-style 'forward)
(setq eshell-cmpl-ignore-case t) ;; ignore case when completing filename
(setq initial-major-mode 'fundamental-mode) ; Scratch buffer default mode => faster load
(global-set-key (kbd "C-x C-b") 'ibuffer)
(global-set-key (kbd "M-<down>") 'scroll-other-window)
(global-set-key (kbd "M-<up>") 'scroll-other-window-down)
(setq disabled-command-function nil)    ; re-enable disabled commands

Make sure emacs treats everything as unicode by default.

(set-language-environment "utf-8")

Fix osx issue when using dired.

(when (string= system-type "darwin")       
  (setq dired-use-ls-dired nil))

Mouse operations for terminal

This is useful only in the terminal – not the GUI

(xterm-mouse-mode t)

; Mouse wheel: scroll up/down; control-wheel for pgup/pgdn.
(defun wheel-scroll-up   ()   (lambda () (interactive) (scroll-up 2)))
(defun wheel-scroll-down ()   (lambda () (interactive) (scroll-down 2)))
(defun wheel-scroll-pgup ()   (lambda () (interactive) (scroll-up 20)))
(defun wheel-scroll-pgdown () (lambda () (interactive) (scroll-down 20)))

(define-key global-map [mouse-5] (wheel-scroll-up))
(define-key global-map [mouse-4] (wheel-scroll-down))
(define-key global-map [C-mouse-5] (wheel-scroll-pgup))
(define-key global-map [C-mouse-4] (wheel-scroll-pgdown))

Loads customization file

When modifying variables defined in packages, the right approach is to use the function `custom-set-variables’, not `setq’. See the following comment on stackoverflow. The downside is that variables set that way are then appended automatically to the init.el file, making things untidy. The following dumps this “automatically generated code” in a different file and loads it.

(setq-default custom-file (expand-file-name ".custom.el" user-emacs-directory))
(when (file-exists-p custom-file)
  (load custom-file))

Theming and fonts

Fonts and faces

For fonts and faces, we need to deal with two cases:

  • when emacs is started with a daemon
  • when emacs is started without a daemon

Depending on which it is, the frame will be created at different times, so we need to apply modifications at different times.

First we define the attributes to be applied

(defun gwb/after-frame-set-face (frame)
  (when (display-graphic-p frame)
    (message "Faces were set for default, fixed and variable pitch for this frame")
    (set-face-attribute 'default frame :font "Fira Code Retina" :height 120)
    (set-face-attribute 'fixed-pitch frame :font "Fira Code Retina" :height 120)
    (set-face-attribute 'variable-pitch frame :font "Cantarell" :height 130 :weight 'regular)))

When started with a daemon, no frame is created so need to add hook for when frame is created.

(add-hook 'after-make-frame-functions 'gwb/after-frame-set-face)

When created without a daemon, frame is created before the above hook is setup so this wouldn’t work. Instead we need to apply the function to whatever frames already exist.

(mapc 'gwb/after-frame-set-face (frame-list))

Themes

To switch themes during usage: M-X counsel-load-theme I was worreid that using doom-themes would massively slow down startup time, but I guess the folks working on Doom are doing a very good job because this is adding only 30ms to load time (compared to using whatever emacs’ default theme is), which I can live with.

(use-package doom-themes
  :ensure t
  :init (load-theme 'doom-tomorrow-day t)
  :config
  (doom-themes-org-config)              ; font-locks code blocks in org mode
  )

Navigation: search and windows

Window utilities

(use-package ace-window
  :ensure t
  :bind ("M-o" . 'ace-window)
  :config
  ;; increase size of leading char
  (custom-set-faces
   '(aw-leading-char-face
     ((t (:foreground "red" :height 3.0))))))

Search improvements

Visual Regexp

Alternatively, you can also test regexps interactively using emacs’s built-in regexp builder (M-x re-builder)

(use-package visual-regexp
  :ensure t
  :bind (("C-c r" . 'vr/replace)
         ("C-c q" . 'vr/query-replace)))

isearch

(use-package isearch
  :bind
  (:map isearch-mode-map
        ("M-m" . gwb/isearch-yank-region)
        ([remap isearch-yank-word-or-char] . gwb/isearch-yank-word-at-point))

  :init
  ;; custom functions
  (defun gwb/region-text ()
    (interactive)
    (buffer-substring (region-beginning) (region-end)))

  (defun gwb/isearch-yank-region ()
    "Yanks the current active region to the isearch minibuffer.
      The point is moved to the beginning of the region at the end of 
      the operation, so the first match is always the current region."
    (interactive)
    (let ((word (gwb/region-text))
          (end-word (region-end)))
      (deactivate-mark)
      (goto-char end-word)
      (backward-word)
      (isearch-yank-string word)))

  (defun gwb/isearch-yank-word-at-point ()
    "Yanks the word at point to the isearch minibuffer. This is 
       intended to replace the functionality of `isearch-yank-word-or-char' 
       mapped to C-s C-w, the behavior of which I don't like."
    (interactive)
    (let ((word (word-at-point t)))
      (forward-word)
      (backward-word)
      (isearch-yank-string word)))

  (defun gwb/goto-other-end ()
    "If search forward, return to beginning of match. If search backward, do 
nothing (already goes to beginning automatically"
    (if (< isearch-other-end (point))
        (goto-char isearch-other-end)))

  (defun gwb/isearch-exit ()
    "Modifies the isearch-exit function to return to beginning of 
word if succesful match"
    (interactive)
    (if (and search-nonincremental-instead
             (= 0 (length isearch-string)))
        (let ((isearch-nonincremental t))
          (isearch-edit-string)) ;; this calls isearch-done as well
      (isearch-done))
    (gwb/goto-other-end)
    (isearch-clean-overlays))


  (add-hook 'isearch-mode-hook
            (lambda ()
              (define-key isearch-mode-map "\r"
                          'gwb/isearch-exit)))

  :config
  ;; changes highlighting for active and passive matches
  (set-face-attribute 'lazy-highlight nil :background "tan1")
  (set-face-attribute 'isearch nil :background "SkyBlue1")

  ;; spaces in search separate different search terms instead
  ;; instad of being interpreted literally
  (setq search-whitespace-regexp ".*")
  (setq isearch-lax-whitespace t))

deadgrep

I added the option to specify the directory where the search should be performed. If you prefix the search command by C-u, you will prompted for a directory.

 (use-package deadgrep
   :ensure t
   :demand t
   :init
   (defun gwb-deadgrep (search-term)
     (interactive (list (deadgrep--read-search-term)))
     (let ((dir (when current-prefix-arg
		   (message (read-directory-name "where? " default-directory))))
	    (current-prefix-arg nil)) 
	(deadgrep search-term dir)))

   :bind
   (("M-s g" . gwb-deadgrep)))

Avy

(use-package avy
  :ensure t
  :bind (("M-j" . avy-goto-char-timer)))

Ergonomics

The following package help with emacs’s ergnomics, discoverability, etc..

which-key

which-key provides key-binding completion in mini buffer.

(use-package which-key
  :ensure t 
  :config
  (which-key-mode))

vertico

(use-package vertico
  :ensure t
  :init
  (setq completion-in-region-function
	  (lambda (&rest args)
	    (apply (if vertico-mode
		       #'consult-completion-in-region
		     #'completion--in-region)
		   args)))
  (vertico-mode))

  (use-package vertico-directory
    :after vertico
    :ensure nil
    ;; More convenient directory navigation commands
    :bind (:map vertico-map
		  ("RET" . vertico-directory-enter)
		  ("DEL" . vertico-directory-delete-char)
		  ("M-DEL" . vertico-directory-delete-word))
    ;; Tidy shadowed file names
    :hook (rfn-eshadow-update-overlay . vertico-directory-tidy))

;; allows vertico to sort by history position
(use-package savehist
  :init
  (savehist-mode))

orderless

(use-package orderless
  :ensure t
  :demand t
  :custom
  (setq completion-styles '(orderless basic)
	  completion-category-defaults nil
	  completion-category-overrides '((file (styles partial-completion))
					  (symbol (styles
						   ;; orderless-strict-leading-initialism
						   orderless-literal
						   orderless-regexp))))
  :config
  (setq completion-styles '(orderless)))

marginalia

(use-package marginalia
  :ensure t
  :init
  (marginalia-mode)
  :bind
  (:map minibuffer-local-map
	  ("M-A" . marginalia-cycle)))

embark

(use-package embark
  :ensure t
  :bind
  (("C-." . embark-act)
   ("M-." . embark-dwim)))

consult

(use-package recentf
  :commands (recentf-mode
	       recentf-add-file
	       recentf-apply-filename-handlers))

(use-package consult
  :ensure t
  :config                               ;; or :init?
  (recentf-mode)
  :bind
  (("C-x b" . consult-buffer)
   ("M-y" . consult-yank-pop)
   ("M-g g" . consult-goto-line)
   ("M-g o" . consult-outline)))
(use-package embark-consult
  :ensure t
  :after (embark consult))

corfu

(use-package corfu
  :ensure t
  :init
  (global-corfu-mode)
  :custom
  (corfu-cycle t)
  :bind
  (:map corfu-map
	  ("TAB" . corfu-next)
	  ([tab] . corfu-next)
	  ("S-TAB" . corfu-previous)
	  ([backtab] . corfu-previous)))

Helpful

(use-package helpful
  :ensure t
  :bind
  ([remap describe-function] . helpful-callable)
  ([remap describe-command] . helpful-command)
  ([remap describe-variable] . helpful-variable)
  ([remap describe-key] . helpful-key))

Minor: useful but less important packages

Projectile

(use-package projectile
  :ensure t
  :bind-keymap
  ("C-c p" . projectile-command-map)
  :config
  (projectile-mode +1)
  )
    

(w) grep

Make grep buffer writable. Allows one to edit occur buffers by:

  • Running C-x C-q to make occur buffer writable
  • … making whatever change
  • Running C-x C-s to save changes. The changes will be written in to the source files.
(use-package wgrep :ensure t :defer 5)

Command-log-mode

Displays all emacs commands used during usage. Useful for debugging and learning.

Usage:

  • First: M-x command-log-mode
  • Then: “C-c x l” to display log in different buffer
(use-package command-log-mode
  :ensure t
  :commands (command-log-mode)
  :bind ("C-c x l" . clm/toggle-command-log-buffer))

Yasnippet

(use-package yasnippet
  :ensure t
  :hook (python-mode . yas-minor-mode))

Hydra

This needs to be loaded early

(use-package hydra
:ensure t
:demand t)

Magit

(use-package magit
  :ensure t
  :defer 5
  :bind ("C-x g" . 'magit-status))

Org-mode

(use-package org
  :defer t
  :config
  ;; indent mode
  (add-hook 'org-mode-hook 'org-indent-mode)

  ;; line wrap
  (add-hook 'org-mode-hook
            (lambda ()
              (visual-line-mode 1)))

  ;; some basic directories
  (setq org-directory "~/org")
  (setq org-default-notes-file "~/org/refile.org")
  (setq org-agenda-files (quote ("~/org")))

  ;;keybindings
  (global-set-key (kbd "C-c a") 'org-agenda)
  (global-set-key (kbd "C-c b") 'org-switchb)
  (global-set-key (kbd "C-c l") 'org-store-link)
  (global-set-key (kbd "C-c i") 'org-indent-mode)
  (global-set-key (kbd "C-c c") 'org-capture)

  ;; some basic configs
  (setq org-loop-over-headlines-in-active-region t)
  (setq org-log-done t)
  (setq org-archive-mark-done nil)
  (setq org-archive-location "~/org/archive/%s_archive::")

  ;; custom keywords + selection
  (setq org-todo-keywords
        '((sequence "TODO(t)" "|" "POSTPONED(p)" "CANCELLED(c)" "DONE(d)")
          (sequence "IDEA(i)" "|" "IMPLEMENTED")
          (sequence "TO-READ(r)" "|" "READ")))

  (setq org-use-fast-todo-selection t)


  ;; fonts

  (defun gwb/org-font-setup ()
    (dolist (face '((org-level-1 . 1.5)
                    (org-level-2 . 1.3)
                    (org-level-3 . 1.1)
                    (org-level-4 . 1.0)
                    (org-level-5 . 1.1)
                    (org-level-6 . 1.1)
                    (org-level-7 . 1.1)
                    (org-level-8 . 1.1)))
      (set-face-attribute (car face) nil :font "Cantarell" :weight 'regular :height (cdr face)))

    ;; Ensure that anything that should be fixed-pitch in Org files appears that way
    (set-face-attribute 'org-block nil    :foreground nil :inherit 'fixed-pitch)
    (set-face-attribute 'org-table nil    :inherit 'fixed-pitch)
    (set-face-attribute 'org-formula nil  :inherit 'fixed-pitch)
    (set-face-attribute 'org-code nil     :inherit '(shadow fixed-pitch))
    (set-face-attribute 'org-table nil    :inherit '(shadow fixed-pitch))
    (set-face-attribute 'org-verbatim nil :inherit '(shadow fixed-pitch))
    (set-face-attribute 'org-special-keyword nil :inherit '(font-lock-comment-face fixed-pitch))
    (set-face-attribute 'org-meta-line nil :inherit '(font-lock-comment-face fixed-pitch))
    (set-face-attribute 'org-checkbox nil  :inherit 'fixed-pitch))

  (defun gwb/org-font-setup-daemon (frame)
    (gwb/org-font-setup))

  (add-hook 'after-make-frame-functions 'gwb/org-font-setup-daemon)

  (gwb/org-font-setup)

  ;; capture

  (setq org-capture-templates
        (quote (("t" "todo" entry (file+headline "~/org/refile.org" "Tasks")
                 "* TODO %?\n %i\n (%U) %a")
                ("b" "book to read" entry (file+headline "~/org/books.org" "To read")
                 "* TO-READ %?\n %i\n")
                ("i" "idea" entry (file+headline "~/org/refile.org" "Ideas")
                 "* IDEA %?\n %i\n")
                ("n" "note" entry (file+headline "~/org/refile.org" "Notes")
                 "* %? :NOTE:\n (%U) %a"))))

  ;; refiling
  (setq org-refile-targets (quote ((nil :maxlevel . 9)				 
                                   (org-agenda-files :maxlevel . 9))))

  (setq org-refile-use-outline-path 'file)
  (setq org-goto-interface 'outline-path-completion)
  (setq org-outline-path-complete-in-steps nil)
  (setq org-refile-allow-creating-parent-nodes 'confirm)

  (org-babel-do-load-languages
   'org-babel-load-languages
   '((R . t)
     (emacs-lisp . t)
     (dot . t)
     (latex . t)))
  )

Org-bullet

(use-package org-bullets
  :ensure t
  :after org
  :hook (org-mode . org-bullets-mode)
  :custom
  (org-bullets-bullet-list '("" "" "" "" "" "" "")))

Dired

(defun gwb-dired-kill-hidden nil
  (interactive)
  (dired-mark-files-regexp "^\\.")
  (dired-do-kill-lines))


  (use-package dired
    :bind
    (:map dired-mode-map
          ("." . gwb-dired-kill-hidden))
    :config
    (setq insert-directory-program "gls")
    (setq dired-listing-switches "-alh --group-directories-first")
    (setq dired-dwim-target t) ;; dired will try to gess target directory when copying, etc...
    )

Company

(use-package company
  :ensure t
  :hook ((c-mode . company-mode)
	   ;(ess-r-mode . company-mode)
	   ;(inferior-ess-r-mode . company-mode)
	   )
  :bind (:map company-active-map
		("C-n" . company-select-next-or-abort)
		("C-p" . company-select-previous-or-abort))
  :config
  (setq company-idle-delay nil))
(use-package company-c-headers
  :ensure t
  :after (company)
  :config
  (add-to-list 'company-backends 'company-c-headers)
  (add-to-list 'company-c-headers-path-system "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include"))
(use-package gwb-indent
  :after company)

Outlining

(use-package outline
  :commands outline-minor-mode
  :init
  (add-hook 'emacs-lisp-mode-hook
            #'(lambda () (setq-local outline-regexp "[;\f][;\f] [*\f]+"))))

(add-hook 'prog-mode-hook 'outline-minor-mode)
(add-hook 'prog-mode-hook 'hs-minor-mode)
(use-package bicycle
  :ensure t
  :after outline
  :bind (:map outline-minor-mode-map
		("M-S-]" . bicycle-cycle)
		("C-M-]" . bicycle-cycle-global)))

Programming languages

Inherited by all

(add-hook 'prog-mode-hook #'(lambda () (setq-local tab-always-indent 'complete)))
(use-package eglot
  :ensure t
  :config
  (add-to-list 'eglot-server-programs
               '((rust-ts-mode rust-mode) .
                 ("rust-analyzer" :initializationOptions (:check (:command "clippy")))))
  )

Misc languages

I have played around, at some point or another, with many programming languages. This section deals with the languages for which the config is minimal (or non-existent).

;; (use-package julia-mode
;;   :mode "\\.jl\\'")

(use-package markdown-mode
  :ensure t
  :mode (("\\.md\\'" . markdown-mode)
         ("\\.Rmd\\'" . markdown-mode)))

;; (use-package elm-mode
;;   :mode "\\.elm\\'")

;; (use-package haskell-mode
;;   :mode "\\.hs\\'"
;;   :hook (haskell-mode . interactive-haskell-mode))

(use-package rust-mode
  :mode "\\.rs\\'"
  :hook (rust-mode . eglot-ensure)
  :init
  (add-hook 'rust-mode (lambda () (setq indent-tabs-mode nil))))

Lisp languages

  (use-package lispy
    :ensure t
    :hook ((emacs-lisp-mode . lispy-mode)
           (scheme-mode . lispy-mode)
           (gerbil-mode .lispy-mode))
    :bind (:map lispy-mode-map
                ("M-o" . nil)))

  ;; (use-package racket-mode 
  ;;   :ensure t
  ;;   :mode "\\.rkt\\'"
  ;;   :config
  ;;   (setq tab-always-indent 'complete)
  ;;   (require 'racket-xp)
  ;;   (add-hook 'racket-mode-hook #'racket-xp-mode))

  (use-package slime
    :commands slime
    :init
    (setq  slime-lisp-modes nil)
    (setq inferior-lisp-program "sbcl")
    :config
    (remove-hook 'lisp-mode-hook 'slime-lisp-mode-hook)
    (load (expand-file-name "~/quicklisp/slime-helper.el"))
    )

(use-package sly
  :commands sly
  :init
  (setq inferior-lisp-program "sbcl")
  :config
  (setq lispy-use-sly t)
  )

  (use-package emacs-lisp-mode
    :hook (emacs-lisp-mode . hs-minor-mode)
    :bind (:map emacs-lisp-mode-map
                ("M-[" . hs-hide-all)
                ("M-]" . hs-show-all)))

common lisp

(use-package lisp-mode
  :hook (lisp-mode . lispy-mode))

Gerbil

I’ll be using Gerbil as my default scheme for now:

  • I’ve installed it with homebrew (see: brew info gerbil-scheme)
  • Executable is /usr/loca/bin/gxi

Since Gerbil piggy backs on Gambit, you need that mode as well.

The relevant .el files are in .emacs.d/copy-lisp as gerbil-mode.el and gambit.el. They were copied from the gerbil / gambit installs:

  • /usr/local/share/emacs/site-lisp/gambit-scheme/gambit.el
  • /usr/local/share/emacs/site-lisp/gerbil-scheme/gerbil-mode.el

To start a repl when editing gerbil code (.ss) just do M-x run-scheme. Do C-c C-c to eval sexp. Check C-h v gerbil-mode-map for commands.

See also https://gerbil.scheme.org/guide/emacs.html#use-package-example-configuration for info on how to use gerbil with emacs.

(use-package gerbil-mode
  :mode "\\.ss\\'"
  :hook
  (inferior-scheme-mode-hook . gambit-inferior-mode)
  :config
  (require 'gambit)
  (setf scheme-program-name "/usr/local/bin/gxi")
  (add-hook 'inferior-scheme-mode-hook 'gambit-inferior-mode)
  :bind (:map gerbil-mode-map ("C-c C-c" . scheme-send-definition))
  )  

(use-package gambit
  :bind (:map inferior-scheme-mode-map ("C-c C--" . gambit-kill-last-popup)))

BQN

(use-package bqn-mode
  :ensure t
  :init (require 'gwb-bqn)
  :commands (bqn-comint-buffer)
  :config (add-hook 'bqn-comint-mode-hook #'gwb-amend-bqn-comint-mode)
  :bind (:map bqn-mode-map
		("C-c C-c" . bqn-comint-send-dwim)))

K

(use-package k-mode
  :mode "\\.k\\'"
  :commands (k-mode-run-k)
  :custom
  (k-mode-repl-bin-path "/Users/gwb/Hacks/repos/k/k")
  )

J

(use-package j-mode
 :ensure t
 :custom
 (j-console-cmd "jconsole"))

R

ESS is (used to be?) fiddly to setup correctly.

  • I used to have both a version installed from the website, and one from MELPA… this was creating all sorts of issues. I have now removed the version from the website (it was a very old version), and kept only the MELPA version. NOTE: the version on the website is very very old (2019) while the MELPA version (i.e. the devel version) is updated very regularly. => make sure to stick to the MELPA version
  • To load ESS, we used to need to include a (require ‘ess-site) statement. This is no longer the case, as per the documentation (see here page 7 – or search ‘use-package’ in the ESS manual).
  • The first (use-package ess :defer t) sets up ESS, deferring the loading. When the loading is triggered by an autoload event (e.g. visiting an R file), ESS loads the ess-r-mode. The binding needs to be set in a separate ess-r-mode use-package because the ess-r-mode-map is defined by the ess-r-mode package, so if we put the bindings in the first one, the mode maps are not defined at the moment when they are evaluated.
(use-package ess
  :ensure t
  :defer t)

(use-package ess-r-mode
  :hook
  ((ess-r-mode . hs-minor-mode)
   (ess-r-mode . outline-minor-mode))
  :bind
  (:map
   ess-r-mode-map
   ;("TAB" . gwb-indent-for-tab-command)
   ("_" . ess-insert-assign)
   ("M-[" . hs-hide-all)
   ("M-]" . hs-show-all)
   :map
   inferior-ess-r-mode-map
   ;("TAB" . gwb-indent-for-tab-command)
   ("_" . ess-insert-assign)
   ("M-[" . hs-hide-all)
   ("M-]" . hs-show-all)))

Below are my customizations for ESS. A few comments:

(use-package gwb-essr
  :after ess-r-mode
  ;; :demand t
  :commands (gwb-essr-configure-iess gwb-essr-configure-ess-r)
  :hook
  ((inferior-ess-r-mode . gwb-essr-configure-iess)
   (ess-r-mode . gwb-essr-configure-ess-r))
  :bind
  (:map
   ess-r-mode-map
   ("%" . gwb-essr-insert-pipe-maybe)
   ("M-TAB" . gwb-essr-toggle-hide-function)
   :map
   inferior-ess-r-mode-map
   ("%" . gwb-essr-insert-pipe-maybe)
   ("M-TAB" . gwb-essr-toggle-hide-function))
  :config
  (advice-add 'ess-r-object-completion :filter-return #'gwb-essr--add-docsig))

C / C++

(defhydra dumb-jump-hydra (:hint nil :color blue)
    "
Dumb jump
"
    ("j" dumb-jump-go "Go")
    ("o" dumb-jump-go-other-window "Other window")
    ("e" dumb-jump-go-prefer-external "Go external")
    ("x" dumb-jump-go-prefer-external-other-window "Go external other window")
    ("i" dumb-jump-go-prompt "Prompt")
    ("l" dumb-jump-quick-look "Quick look")
    ("b" dumb-jump-back "Back"))
(use-package cc-mode
  :init
  (defun gwb/clang-capf-init ()
    (add-hook 'completion-at-point-functions #'clang-capf nil t))
  (defun gwb/dumb-jump-init ()
    (add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
    (setq xref-show-definitions-function #'xref-show-definitions-completing-read))
  :defer t
  :config
  (setq c-default-style "linux")
  (setq c-basic-offset 4)
  (add-hook 'c-mode-hook #'gwb/clang-capf-init)
  (add-hook 'c-mode-hook #'gwb/dumb-jump-init)

  :bind (:map c-mode-map
              ("TAB" . indent-for-tab-command)
              ("C-j" . dumb-jump-hydra/body)
              ("M-[" . hs-show-all)
              ("M-]" . hs-hide-all)
              ("C-]" . hs-toggle-hiding)))
(use-package c++-mode
  :hook ((c++-mode . eglot-ensure)))

;; (use-package eglot
;;   :defer t
;;   :config
;;   (add-to-list 'eglot-server-programs '(c++-mode . ("/usr/local/opt/llvm/bin/clangd"))))

Zig

(use-package zig-mode :ensure t)

Latex

(use-package auctex
  :mode ("\\.tex\\'" . TeX-latex-mode)
  :config
  (require 'reftex)
  (add-hook 'LaTeX-mode-hook 'turn-on-reftex)
  (setq reftex-plug-into-AUCTeX t)

  ;; Auxtex
  (setq TeX-auto-save t)
  (setq TeX-parse-self t)

  ;; PDF search
  (add-hook 'LaTeX-mode-hook 'TeX-source-correlate-mode)
  (add-hook 'LaTeX-mode-hook 'LaTeX-math-mode)

  (setq TeX-PDF-mode t)
  (when (eq system-type 'darwin)
    (setq TeX-view-program-selection '((output-pdf "PDF Viewer")))
    (setq TeX-view-program-list
  '(("PDF Viewer" "/Applications/Skim.app/Contents/SharedSupport/displayline -b -g %n %o %b")))
    )


  ;; (use-packag auctex-latexmk) 
  (require 'auctex-latexmk)
  (auctex-latexmk-setup)
  (setq auctex-latexmk-inherit-TeX-PDF-mode t)

  ;; Only change sectioning colour
  (setq font-latex-fontify-sectioning 'color)
  ;; super-/sub-script on baseline
  (setq font-latex-fontify-script nil) ; might not keep this line.. I like smaller {sub/super}scripts
  (setq font-latex-script-display (quote (nil)))
  ;; Do not change super-/sub-script font


  (setq font-latex-deactivated-keyword-classes
        '("italic-command" "bold-command" "italic-declaration" "bold-declaration"))
  )

Python

Description of the setup

The python setup in emacs is a wee bit complicated. There are generally roughly 4 parts:

  1. The major mode that provides things like syntax highlighting, bindings to the interpreter, etc…
  2. An “orchestrating minor mode” that communicates with outside binaries or other minor modes to provide things “ide-like” features like completion, linting, formatting, etc…
  3. The suite of binaries, other minor modes providing the individual functionalities listed above
  4. The minor modes that deal with the display of information (corfu/company, flymake/flycheck, etc…)

There are a few options for each of these: picking the right components and having them working nicely in concert requires some fiddling.

Major mode:

I’m using emacs’s built-in `python-mode`. I’m not sure there are any popular alternatives at the moment. The syntax highlighting it provides is not great. A specific alternative for syntax-highlighting uses `tree-sitter`. I should investigate that at some point.

TODO: investigate `tree-sitter`

Orchestrating minor mode:

This is the biggest decision-point. I used rely on `elpy` for it, but it was fiddly and unreliable for me. I stopped using it in frustration and went back to using only python-mode but I missed having things like code signature in the minibuffer, decent auto-completion, etc..

I have now switched to `lsp-mode` which is a client interface for the Language Server Protocol. It provides a generic client infrastructure that simplifies the task of writing concrete clients for specific languages. The important thing to understand about the lsp setup is that it involves 3 components:

  1. lsp-mode: the emacs package that provides the generic interface
  2. the language server. This is not an emacs-specific thing: these are external programs that provides language services for specific languages, and that the client connects to. For popular languages, there may be a few different server options to choose from. For python, there are 3 options:
    • pyls
    • pylsp
    • pyright

    The `pyls` server was developped by Palantir and seems to have been abandonned – or at least, it seems to no longer be supported. The `pylsp` server is a fork of `pyls` that is still maintained. The `pyright` server is supported by microsoft.

  3. The concrete client implementation. As I said, `lsp-mode` provides the generic client interface (i.e. you can use lsp-mode for many different languages) but you still need a specific client implementation. Generally you need an specific implementation for each language server. There can be different implementations for a given server, but that doesn’t seem to be the case at the moment. The clients are:
    • lsp-pyls => included in lsp-mode
    • lsp-pylsp => included in lsp-mode
    • lsp-pyright => provided by the `lsp-pyright` package

I am currently using `pylsp` server (installed via `pip3 install python-lsp-server`) and the `lsp-pylsp` client included in the `lsp-mode` emacs package that also provides `lsp-mode`.

Additional tools

The lsp server (so in my case `pylsp`) relies on external tools to deliver some of its optional functionalities. To complicate things further, there may be several tools to choose from for given functionalitites: it really is an embarassement of riches! Below are some of the optional functionalities (non-exhaustive) that `pylsp` can provide and some of the tools that can be used to provide them:

  • type-checking: pylsp-mypy. Note: the `pyright` server seems to do type-checking out of the box, but pylsp requires the `pylsp-mypy`, which can be installed with a simple `pip install pylsp-mypy` (make sure its in the same environment).
  • Error + Pep8 style checking: flake8 or pylint (there are in fact many other options, but these are the main ones)

    I’ve decided to go with flake8 for now because the `lsp-pylsp` client shipped with `lsp-mode` has better support for it than for pylint – by which I mean that it makes it easier to specify configs for flake8 that will then be sent to the server.

  • Reformatting: autopep8 vs yapf (again, many more options)

    I’m currently not using an automatic formatter. If decide to do so, `yapf` seems to be the preferred option.

  • Completion and refactoring: jedi or rope.

    I’ve been using the default, which is `jedi`, although I’ve been using it for completion mostly, not refactoring. From the `rope` website, it seems that `rope` is focused on recactoring. At this point, I mostly care about completion so I’ll stick to `jedi`.

  • Poetry: I’m experimenting with poetry as a dependency manager.
    • M-x poetry will start the menu with options, etc..
    • To use the packages installed with poetry in the repl, you need to activate the virtualenv (M-x poetry activate) then just M-x run-python. Note that you need to install ipython in the virtualenv for it to work (M-x poetry add ipython). Preferably, install it as a dev dependency.
    • To use the lsp server (which gives you completion, etc..) with poetry, you need to install the python-sp-server as a dev dependency in poetry, then activate the virtualenv, then visit a python file, etc…

Complementary emacs modes

Finally, `lsp-mode` relies on other emacs packages for certain functionalities. E.g.

  • Completion: can use company or the built-in completion-at-point facilities (in which case, we can use things like corfu, etc…)
  • Flycheck or Flymake. `Flymake` is built into emacs but `Flycheck` is the recommended option for `lsp-mode`. I’ve tried both and I like `Flycheck`:
    • It has nice introspection facilities so you can see what’s happening with the mode. E.g. `M-X flycheck-verify-setup` is very informative. Note that when used in concert with `lsp-mode`, flycheck basically uses `lsp` as it’s “checking” backend.
    • You can list all errors easily `M-x list-flycheck-errors` (flymake can probably do that as well)
    • You can jump to the next error `C-c ! n`

Config

As described above, some additional packages, modules must exist for my config to work optimally. Thankfully, nothing breaks if I don’t have things installed: the optional features just won’t be turned on. So in addition to ‘lsp-mode’, I rely on the following:

  • [emacs package] flycheck
  • [pip install] flake8
  • [pip install] pylsp-mypy
  • [pip install] jedi
;; (use-package lsp-mode
;;   :ensure t
;;   :defer t                   ; ok to defer, will be loaded when needed
;;   :config
;;   ;; the pyright server has higher precedence than pylsp so need to disable it so
;;   ;; lsp uses pylsp
;;   ;; (setq lsp-clangd-binary-path "/usr/local/opt/llvm/bin/clangd")
;;   (setq lsp-disabled-clients (cons 'pyright lsp-disabled-clients)))

Additional notes on lsp-mode:

The `lsp-mode` package provides helpful messages / debugging facilities to see what’s happening with the server / client communication. e.g.

  • The lsp-log buffer
  • The lsp-stderr buffer
  • M-x lsp-describe-session

A note about completion: it works by adding its own backend in the list of `completion-at-point-functions`. The backend is called `lsp-completion-at-point`. You should see it if you type `C-h v completion-at-point-functions`.

(defun gwb-py-get-menu nil
  (interactive)
  (occur "# \\*"))

(use-package python
  :ensure nil
  :mode ("\\.py\\'" . python-mode)
  :interpreter ("python" "python3")
  ;;:hook
  ;; (python-mode . (lambda ()
  ;;                                       ;(require 'lsp-pyright)
  ;;                  (lsp)))
  ;;                                       ;:bind
  ;;                                       ;(:map python-mode-map
  ;;                                       ;      ("TAB" . gwb-indent-for-tab-command))
  :config
  ;; => uncomment two below
  (setq python-shell-interpreter "ipython3")
  (setq python-shell-interpreter-args "-i --simple-prompt")
  :bind
  (:map python-mode-map
        ("C-c =" . gwb-py-get-menu))
  )

**Pesky warning in repl**

When upgrading ipython to use python 3.11, I started seeing the following error:

Warning (python): Your ‘python-shell-interpreter’ doesn’t seem to support readline, yet ‘python-shell-completion-native-enable’ was t and “ipython3” is not part of the ‘python-shell-completion-native-disabled-interpreters’ list. Native completions have been disabled locally. Consider installing the python package “readline”.

I’ve had similar issues in the past. I took the time to track this down, and apparently it comes down to the `readline` module that is built into python. Different pythons use readline modules linking to different c readline libraries: `libedit` or `gnureadline`. You can check which version your ipython uses (assuming that you want to use ipython as your repl)

import readline print(readline.__doc__)

If you see:

Importing this module enables command line editing using libedit readline.

Then you’ll see warning: you need to make sure your interpreter uses the gnureadline version. I don’t know how to force pick a version of ipython3 that has gnureadline. Instead, I’m using the following trick:

  1. pip install gnureadline
  2. automatically run some code on ipython startup so it tricks python into importing gnureadline when it wants to import readline. The python code you want to run is

    import gnureadline import sys sys.modules[“readline”] = gnureadline

    To make sure ipython runs it on startup you need to to run

    ipython3 profile create

    from the command line. This will create a ~.ipython/profile_default/ipython_config.py file with a bunch of stuff commented out in it. You just need to make sure that you have the following in the file:

    c = get_config() #noqa c.InteractiveShellApp.exec_lines = [‘import gnureadline’, ‘import sys’, ‘sys.modules[“readline”] = gnureadline’]

    You also need to make sure that you’ve passe the “-i –simple-prompt” arguments to python-shell-interpreter-args (as in the above config).

terminals

Useful keybindings for terminal:

  • C-c C-k: term-char-mode (can’t use usual emacs bindings)
  • C-c C-j: term-line-mode (can use emacs bindings)
  • C-c C-p: jump to last prompt
  • M-p: travel history
(use-package term
  :commands term
  :config
  (setq term-prompt-regexp "^[^#$%>\\n]*[#$%>] *"))

(use-package eterm-256color
  :hook (term-mode . eterm-256color-mode))

Custom functions

Spotify custom utilities

(use-package elspot
  :commands hydra-spotify/body)

Hugo utilities

(use-package gwb-hugo
  :commands gwb-run-hugo-server) ;; allows to quickly start and kill hugo servers

Better Occur behavior

(defun gwb/kill-occur-buffer-window (&rest args)
    (delete-window (get-buffer-window "*Occur*")))

(defun gwb/switch-to-occur-buffer (&rest args)
  (let ((buffer-window (get-buffer-window "*Occur*")))
    (when buffer-window
      (select-window buffer-window))))

(advice-add 'occur-mode-goto-occurrence :after #'gwb/kill-occur-buffer-window)
(advice-add 'occur :after #'gwb/switch-to-occur-buffer)

Line movement

(defun gwb/move-beginning-of-line (arg)
  "moves first to first non-whitespace characters. If already there moves to 
to beginning of line"
  (interactive "^p")
  (setq arg (or arg 1))
  (when (/= arg 1)
    (let ((line-move-visual nil))
      (forward-line (1- arg))))

  (let ((orig-point (point)))
    (back-to-indentation)
    (when (= orig-point (point))
      (move-beginning-of-line 1))))

(global-set-key [remap move-beginning-of-line]
                'gwb/move-beginning-of-line)

Note: interestingly, CMD + SHIFT is mapped to super (s). e.g. CMD+SHIFT+SPC maps to s-SPC.

Custom keymap with extras

(defun gwb/display-this-buffer-other-window ()
  (interactive)
  (switch-to-buffer-other-window (buffer-name)))

(defun gwb/mark-word-at-point ()
  (interactive)
  (let ((word (word-at-point t)))
    (progn
      (forward-word)
      (backward-word)
      (set-mark-command 'nil)
      (search-forward word))))

(defun gwb/latex-note ()
  "Inserts my `note' template, and automatically turns on latex (auctex) mode"
  (interactive)
  (insert-file-contents-literally "~/.emacs.default/my-latex-templates/note.tex")
  (latex-mode))

(defun gwb/flip-windows ()
  "flips the buffers in split-screen windows"
  (interactive)
  (unless (= 2 (count-windows))
    (error "Only works with two windows."))
  (let ((this-buffer (window-buffer (selected-window)))
        (alt-buffer (window-buffer (previous-window))))
    (set-window-buffer (previous-window) this-buffer)
    (set-window-buffer (selected-window) alt-buffer)
    (select-window (previous-window))))


(defun gwb/edit-config ()
  "edits README.org"
  (interactive)
  (find-file "~/.emacs.d/README.org"))


(defun gwb/copy-to-osx (start end)
  (interactive "r")
  (shell-command-on-region start end "pbcopy"))

;; For some reason, the ring bell function gets reactivated
;; every time my laptop goes to sleep on mac os Big Sur... Need
;; a shortcut to quickly set this.

(defun gwb-mute-alerts ()
  (interactive)
  (setq ring-bell-function 'ignore))

;; custom function
(defun gwb/indent-org-block ()
  (interactive)
  (when (org-in-src-block-p)
    (org-edit-special)
    (indent-region (point-min) (point-max))
    (org-edit-src-exit)))


(defvar gwb-custom-keymap nil "my keymap..")

(setq gwb-custom-keymap (make-sparse-keymap))
(global-set-key (kbd "C-c x") gwb-custom-keymap)
(global-set-key (kbd "M-SPC") gwb-custom-keymap)

(define-key gwb-custom-keymap (kbd "m") 'gwb/mark-word-at-point)
(define-key gwb-custom-keymap (kbd "n") 'gwb/latex-note)
(define-key gwb-custom-keymap (kbd "o") 'gwb/flip-windows)
(define-key gwb-custom-keymap (kbd ".") 'gwb/edit-config)
(define-key gwb-custom-keymap (kbd "w") 'gwb/copy-to-osx)
(define-key gwb-custom-keymap (kbd "<") 'gwb-mute-alerts)
(define-key gwb-custom-keymap (kbd "TAB") 'gwb/indent-org-block)
(define-key gwb-custom-keymap (kbd "s") 'hydra-spotify/body)
(define-key gwb-custom-keymap (kbd "u") 'undo-tree-visualize)
(define-key gwb-custom-keymap (kbd "d") 'gwb/display-this-buffer-other-window)

About

My emacs config

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载