dotfiles/.config/emacs/init.org

10 KiB
Raw Blame History

Init.El Literate

Basics

(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(defun display-startup-echo-area-message ()
  (message (format "Emacs %s loaded from %s"
            emacs-version user-emacs-directory)))

(global-set-key (kbd "C-c C-r") 'eval-region)
(setq initial-scratch-message (concat ";; *SCRATCH*\n"
                                      ";; C-c C-b (eval-buffer)  C-c C-r (eval-region)\n"
                                      ";; C-x C-s will let you choose where to save\n\n; "))

;; Display
;; Setting this in early-init doesn't seem to work
(set-frame-position (selected-frame) 100 10)
(set-face-attribute 'default nil :height 160)
(add-to-list 'bdf-directory-list "~/Library/Fonts")

(custom-theme-set-faces
 'user
 '(variable-pitch ((t (:foundry "apple" :family "IBM Plex Sans" :weight normal :height 170))))
 '(fixed-pitch ((t (:foundry "apple" :family "Fira Code" :height 160)))))
(set-face-attribute 'default nil :foundry "apple" :family "Fira Code" :height 160)
(set-face-attribute 'variable-pitch nil :foundry "apple" :family "IBM Plex Sans" :height 170)

;;(global-display-line-numbers-mode 1)
(add-hook 'prog-mode-hook (lambda () (display-line-numbers-mode 1)))
(add-hook 'text-mode-hook (lambda () (display-line-numbers-mode 1)))

;; I think this saves the last cursor pos or something
(require 'saveplace)
(setq-default save-place t)
(setq save-place-file (expand-file-name ".places" user-emacs-directory))

;; Some better defaults?
(setq make-backup-files nil)
(setq auto-save-default nil)
(setq-default major-mode 'text-mode)
(setq sentence-end-double-space nil)

;; Always tabs except for *.go?
;; TODO: set up language mode packages
(setq-default c-basic-offset  4
              tab-width       4
              indent-tabs-mode nil)
;; Some modes
(recentf-mode 1)
(show-paren-mode 1)
(setq electric-pair-inhibit-predicate 'electric-pair-conservative-inhibit)
(electric-pair-mode 1)
(winner-mode 1)

;; IDK
(add-hook 'prog-mode-hook #'subword-mode)
(add-hook 'minibuffer-setup-hook #'subword-mode)

;; elisp
(add-hook 'emacs-lisp-mode-hook
          (lambda ()
            (local-set-key (kbd "C-c C-x") #'ielm)
            (local-set-key (kbd "C-c C-c") #'eval-defun)
            (local-set-key (kbd "C-c C-b") #'eval-buffer)))

Helper functions

Hopefully I'll be consistent with the convention of using ./ for all self-defined functions.

To search for these in completion systems, use \./ (\o/ :D). ./ stands for DOTSLASH

The function below is adapted from a blog post (link lost, sorry) which describes how you can define a function to quickly insert a src code block.

I've adapted it to automatically use emacs-lisp :tangle yes if the file currently being edited is in the user-emacs-directory, since I use a literate emacs configuration and the only time I'll use this function when I'm in the user emacs directory is when I'm editing my literate org configuration.

Otherwise (editing any other files) it will read input from the minibuffer for text to insert after the #+BEGIN_SRC. The original function used ido completing read with a list of known code types and I've nuked it and just let the user append what ever they want after #+BEGIN_SRC: this could be useful for adding :stuff like this after the code-type.

(defun ./org-insert-src ()
  "Insert SRC Block with prompted code type.

Uses 'emacs-lisp :tangle yes' if currently in user-emacs-directory."
  (interactive)
  (let ((code-type (if (string=
                        (file-truename user-emacs-directory)
                        (file-name-directory (buffer-file-name)))
                       "emacs-lisp :tangle yes"
                     (read-from-minibuffer "#+BEGIN_SRC "))))
  (progn
    (newline)
    (insert (format "#+BEGIN_SRC %s\n" code-type))
    (newline)
    (insert "#+END_SRC\n")
    (previous-line 2)
    (org-edit-src-code))))

This is also another helper function for editing my literate configuration. It uses the #+BEGIN_SRC content from the current code block (that the cursor is in) and splits the block into two by inserting #+END_SRC and copying whatever #+BEGIN_SRC <...> that was used in the current code block.

  • save-excursion allows execution of some expressions then move the point back to where it was (before the save-excursion call).
  • Note that the re-search-backward seems to be case-insensitive, surprisingly, so both BEGIN_SRC and begin_src could be matched.
(defun ./org-split-src ()
  "Split current src block into two blocks at point.

Retains src properties."
  (interactive)
  (insert "#+END_SRC\n\n")
  (save-excursion
    (re-search-backward "^\\s-*#\\+begin_src")
    (defvar beginsrc (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
  (insert beginsrc)
  (previous-line))

Load file and directory

https://www.emacswiki.org/emacs/LoadingLispFiles#h5o-2 Thanks!

This is used for loading my modules/ dir in packages.el.

(defun ./load-directory (dir)
  "Load *.el files in a given directory"
  (let ((load-it (lambda (f)
                   (load-file (concat (file-name-as-directory dir) f)))))
    (mapc load-it (directory-files dir nil "\\.el$"))))

TBH this is such a small function but like, does save some parens y'know.

(defun ./load-file-if-exists (file)
  "Same as load-file but NOP if file does not exist"
  (if (file-exists-p file)
      (load-file file)))

Toggle window split

https://www.emacswiki.org/emacs/ToggleWindowSplit

(defun toggle-window-split ()
  (interactive)
  (if (= (count-windows) 2)
      (let* ((this-win-buffer (window-buffer))
             (next-win-buffer (window-buffer (next-window)))
             (this-win-edges (window-edges (selected-window)))
             (next-win-edges (window-edges (next-window)))
             (this-win-2nd (not (and (<= (car this-win-edges)
                                         (car next-win-edges))
                                     (<= (cadr this-win-edges)
                                         (cadr next-win-edges)))))
             (splitter
              (if (= (car this-win-edges)
                     (car (window-edges (next-window))))
                  'split-window-horizontally
                'split-window-vertically)))
        (delete-other-windows)
        (let ((first-win (selected-window)))
          (funcall splitter)
          (if this-win-2nd (other-window 1))
          (set-window-buffer (selected-window) this-win-buffer)
          (set-window-buffer (next-window) next-win-buffer)
          (select-window first-win)
          (if this-win-2nd (other-window 1))))))

Packages - Elpaca

Bootstrap installation of Elpaca

(defvar elpaca-installer-version 0.5)
(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)
    (when (< emacs-major-version 28) (require 'subr-x))
    (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)))
            (progn (message "%s" (buffer-string)) (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))

Note that disabling the built-in package.el is in early-init.el, which also includes setting of frame size and others.

Enable use-package for elpaca and load my package definitions in packages.org

;; Install use-package support
(elpaca elpaca-use-package
  ;; Enable :elpaca use-package keyword.
  (elpaca-use-package-mode)
  ;; Assume :elpaca t unless otherwise specified.
  (setq elpaca-use-package-by-default t))

;; Block until current queue processed.
(elpaca-wait)

;;When installing a package which modifies a form used at the top-level
;;(e.g. a package which adds a use-package key word),
;;use `elpaca-wait' to block until that package has been installed/configured.
;;For example:
;;(use-package general :demand t)
;;(elpaca-wait)

(load (expand-file-name "packages.el" user-emacs-directory))

Below is useful when elpaca installs a package for the first time, and I passed my org literate config files from calling emacs, which would have already been opened, this prevents the right hooks from packages to be loaded; hence I should source my packages config again.

TODO: doesn't seem to work if called interactively, but if I'm here and use C-x C-e on the ( load ...) call it works doesn't trigger elpaca?

(defun ./reload-packages.el ()
  "Source packages.el again"
  (interactive)
  (load (expand-file-name "packages.el" user-emacs-directory)))