Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/matthewbauer/bauer
An Emacs+Nix IDE
https://github.com/matthewbauer/bauer
dotfiles nix nixpkgs
Last synced: 26 days ago
JSON representation
An Emacs+Nix IDE
- Host: GitHub
- URL: https://github.com/matthewbauer/bauer
- Owner: matthewbauer
- License: gpl-3.0
- Created: 2017-05-21T07:04:18.000Z (over 7 years ago)
- Default Branch: master
- Last Pushed: 2024-05-03T19:02:06.000Z (6 months ago)
- Last Synced: 2024-05-03T20:42:34.199Z (6 months ago)
- Topics: dotfiles, nix, nixpkgs
- Language: Emacs Lisp
- Homepage: https://matthewbauer.us/bauer/
- Size: 2.4 MB
- Stars: 115
- Watchers: 9
- Forks: 10
- Open Issues: 3
-
Metadata Files:
- Readme: README.org
- License: LICENSE
Awesome Lists containing this project
README
#+title: bauer: an Emacs+Nix IDE
#+author: Matthew Bauer
#+email: [email protected]
#+subtitle: Bauer’s Automated Unified Emacs Realm
#+description: My Emacs configuration
#+language: en
#+options: c:nil d:t e:t f:t H:3 p:nil ':t *:t -:t ::t <:t \n:nil ^:{} |:t
#+options: arch:nil author:t broken-links:nil
#+options: creator:t date:t email:t inline:nil num:nil pri:t
#+options: prop:nil stat:t tags:nil tasks:nil tex:t timestamp:t title:t toc:nil
#+property: header-args :cache yes :comments link
#+property: header-args:emacs-lisp :results output silent
#+latex_header: \usepackage{inconsolata}
#+tags: noexport notangle
#+startup: hideblocks align entitiespretty
#+export_file_name: index
#+keywords: dotfiles config ide emacs nix bauer
#+html_head:
#+link_home: https://matthewbauer.us
#+link_up: http://matthewbauer.us/bauer/
#+version: 2.0.1This ORG-mode file generates an Emacs configuration. I am calling it an
[[https://www.gnu.org/s/emacs/][Emacs]]+[[https://nixos.org][Nix]] IDE. That is, the Emacs configuration is /integrated/ with hardcoded
Nix store paths. This provides a kind of functional Emacs configuration. The
integration between Emacs & Nix comes with lots of useful side effects. The raw
Org file can always be downloaded [[https://matthewbauer.us/bauer/README.org][here]]. You can create pull requests & issues on
[[https://github.com/matthewbauer/bauer][the GitHub repo]]. You can access my website at https://matthewbauer.us.#+TOC: headlines 2
* Usage
:PROPERTIES:
:header-args: :tangle no
:CUSTOM_ID: usage
:END:** Installing/upgrading
:PROPERTIES:
:CUSTOM_ID: install
:END:This is the script that I use to setup all of my new machines but it's portable
enough for anyone to use.To install, just run this command from your shell:
#+BEGIN_SRC shell
curl https://matthewbauer.us/bauer/install | sh
#+END_SRCOnce it’s installed, you can open Emacs at any time with this command:
#+BEGIN_SRC shell
"$HOME/.nix-profile/bin/run"
#+END_SRCIf you don't like it, it's also very easy to uninstall. Just run:
#+BEGIN_SRC shell
nix-env -e bauer
nix-collect-garbage -d
#+END_SRC** Developing
:PROPERTIES:
:CUSTOM_ID: develop
:END:After you’ve installed it, it’s easy to make changes. By default, the
configuration lives in =~/.local/share/bauer=. To make changes just follow this
process,#+BEGIN_SRC shell
cd ~/.local/share/bauer
nix-build
./result/run README.org
#+END_SRCThe last line will spawn an Emacs frame in the Git repo. Anything in that file
can be change. Once you’ve made a change, just run =M-x dev-restart= to rebuild
the configuration & restart Emacs. Make any changes you want to [[./README.org][the README.org
file]] or any of the files in [[./site-lisp][the site-lisp folder]]. Make sure you commit your
changes afterward by typing =C-c p v=, then =cc=. If you have already forked
[[https://github.com/matthewbauer/bauer][this repo on GitHub]], you can add it as a remote by typing =Mg= followed by your
GitHub username within Magit. To push to it, just type =Pr= then find your
username in the list & press enter. Pull requests are always welcome through
GitHub!** Without Nix
:PROPERTIES:
:CUSTOM_ID: nonix
:header-args: :tangle no
:END:You can also use this configuration without Nix. This just gives you the Emacs
configuration without any of the Nix integrations. All of the binaries will have
to be provided externally. To get started, run the following:#+BEGIN_SRC shell
mv ~/.emacs.d ~/.emacs.d.old
git clone https://github.com/matthewbauer/bauer \
~/.emacs.d
#+END_SRC** Note on macOS
*** Disabling AppNap
Recent versions of macOS have a "feature" called AppNap that causes unfocused
apps to sleep in the background. This often results in things like compilation
taking forever because it only does any work while focused. We almost never want
Emacs to suspend on its own, so it’s important to disable this.Luckily, it’s not very hard to do this. From the terminal, run:
#+BEGIN_SRC shell
defaults write org.gnu.Emacs NSAppSleepDisabled -bool YES
defaults write emacs NSAppSleepDisabled -bool YES
#+END_SRCI’ve seen some of the org.gnu.Emacs form posted online. However, if you start
Emacs from the command line, the second one is necessary since this is how Apple
will identify the binary.This is totally optional. If this doesn’t bother you, feel free to leave it, and
it will probably slightly improve your energy usage, since it can suspend Emacs
when unfocused.* Emacs init file
:PROPERTIES:
:header-args: :tangle yes :comments link
:CUSTOM_ID: emacs
:END:This is the main part of the IDE. It is written in Emacs Lisp & will be loaded
every time Emacs is started.** Increase GC
Increasing GC is a common way to speed up Emacs. =gc-cons-threshold= sets at
what point Emacs should invoke its garbage collector Some people set it to a
really larger number permanently. This works well until the garbage is actually
collected (then you have to wait a long time). I’ve decided to just set it
temporarily to a large number so we only garbage collect once on startup. After
that we reset it to the standard value. Read [[http://bling.github.io/blog/2016/01/18/why-are-you-changing-gc-cons-threshold/][@bling’s post]] for more info on
this.#+BEGIN_SRC emacs-lisp :padline no
;; -*- mode: emacs-lisp; coding: utf-8; -*-(defvar file-name-handler-alist-backup
file-name-handler-alist)
(setq gc-cons-threshold most-positive-fixnum
file-name-handler-alist nil)
(add-hook 'after-init-hook
(lambda ()
(garbage-collect)
(setq gc-cons-threshold
(car (get 'gc-cons-threshold 'standard-value))
file-name-handler-alist
(append
file-name-handler-alist-backup
file-name-handler-alist))))(setq read-process-output-max (* 1024 1024))
#+END_SRC** Autoloads & Misc.
Setup some initial aliases for Emacs. These give us an easy way to use these
functions without actually require'ing them. Ideally, Emacs should pick these up
through the automatic autoloading method, but that sometimes conflicts with the
compiling phases used later.#+BEGIN_SRC emacs-lisp
(eval-and-compile
(autoload 'package-installed-p "package")
(autoload 'use-package-autoload-keymap "use-package")
(autoload 'pcomplete-arg "pcomplete")
(autoload 'pcomplete--here "pcomplete")
(autoload 'tramp-tramp-file-p "tramp")
(autoload 'tramp-dissect-file-name "tramp")(defvar view-mode-map)
(defvar iso-transl-ctl-x-8-map)
(defvar dired-mode-map))
#+END_SRCWhen we are within a terminal we want to be able to use the mouse, so
=xterm-mouse-mode= is enabled here.iTerm doesn’t seem to support xterm color reporting correctly, so we use the
COLORFGBG method.#+BEGIN_SRC emacs-lisp
(unless (display-graphic-p)
(xterm-mouse-mode 1)(when (string-equal (getenv "TERM_PROGRAM") "iTerm.app")
(add-hook 'after-make-frame-functions
#'(lambda
;; Take advantage of iterm2's CSI u support (https://gitlab.com/gnachman/iterm2/-/issues/8382).
(xterm--init-modify-other-keys);; Courtesy https://emacs.stackexchange.com/a/13957, modified per
;; https://gitlab.com/gnachman/iterm2/-/issues/8382#note_365264207
(defun character-apply-modifiers (c &rest modifiers)
"Apply modifiers to the character C.
MODIFIERS must be a list of symbols amongst (meta control shift).
Return an event vector."
(if (memq 'control modifiers) (setq c (if (and (<= ?a c) (<= c ?z))
(logand c ?\x1f)
(logior (lsh 1 26) c))))
(if (memq 'meta modifiers) (setq c (logior (lsh 1 27) c)))
(if (memq 'shift modifiers) (setq c (logior (lsh 1 25) c)))
(vector c))
(when (and (boundp 'xterm-extra-capabilities) (boundp 'xterm-function-map))
(let ((c 32))
(while (<= c 126)
(mapc (lambda (x)
(define-key xterm-function-map (format (car x) c)
(apply 'character-apply-modifiers c (cdr x))))
'(;; with ?.VT100.formatOtherKeys: 0
("\e\[27;3;%d~" meta)
("\e\[27;5;%d~" control)
("\e\[27;6;%d~" control shift)
("\e\[27;7;%d~" control meta)
("\e\[27;8;%d~" control meta shift)
;; with ?.VT100.formatOtherKeys: 1
("\e\[%d;3u" meta)
("\e\[%d;5u" control)
("\e\[%d;6u" control shift)
("\e\[%d;7u" control meta)
("\e\[%d;8u" control meta shift)))
(setq c (1+ c)))))
));; ;; xterm--report-background-handler init fails, but we can still
;; ;; use COLORFGBG
;; (defvar xterm-extra-capabilities)
;; (setq xterm-extra-capabilities '(modifyOtherKeys getSelection setSelection))
(autoload 'rxvt-set-background-mode "term/rxvt")
(add-hook 'window-setup-hook 'rxvt-set-background-mode)
))
#+END_SRC** Custom config
[[./lisp/set-defaults.el][set-defaults]] provides an easy way to override the default custom files. This
means that when you customize a variable it will appear as ‘standard’ even
though it’s not what the package originally defined as the default. This is
useful for an Emacs distribution to provide /better defaults/ while still
letting the user override them. Look through the lispdoc of the package for
documentation on how this works. Eventually, this will be added to MELPA for use
in other Emacs distributions.#+BEGIN_SRC emacs-lisp
(require 'set-defaults)
#+END_SRC*** Better defaults
These are some *better* defaults for Emacs. They shouldn’t require any packages
to be installed to work (those go in use-package). In addition, they should take
almost no time to run (meaning they probably shouldn’t have custom init hooks).
The format of arguments to =set-defaults= is identical to the one used by
=custom-set-variables=.#+NAME: defaults
| Default Variable | Default Value |
|-----------------------------------------------+----------------------------------------|
| apropos-do-all | t |
| async-shell-command-buffer | 'new-buffer |
| auth-source-save-behavior | t |
| auto-revert-avoid-polling | t |
| auto-revert-check-vc-info | t |
| auto-revert-interval | 2 |
| auto-revert-verbose | nil |
| backward-delete-char-untabify-method | 'hungry |
| bidi-inhibit-bpa | t |
| bidi-paragraph-direction | 'left-to-right |
| bookmark-save-flag | 1 |
| checkdoc-spellcheck-documentation-flag | t |
| comint-input-ignoredups | t |
| comint-move-point-for-matching-input | 'end-of-line |
| comint-move-point-for-output | 'all |
| comint-process-echoes | t |
| comint-prompt-read-only | t |
| comint-scroll-to-bottom-on-input | 'this |
| compilation-always-kill | t |
| compilation-ask-about-save | nil |
| compilation-context-lines | t |
| compilation-scroll-output | 'first-error |
| compilation-skip-threshold | 1 |
| completions-cycle-threshold | t |
| completions-detailed | t |
| completions-format | 'vertical |
| completion-ignore-case | t |
| case-fold-search | t |
| cursor-in-non-selected-windows | nil |
| custom-search-field | nil |
| delete-by-moving-to-trash | t |
| delete-old-versions | t |
| dired-dwim-target | t |
| dired-hide-details-hide-symlink-targets | nil |
| dired-omit-verbose | nil |
| dired-recursive-copies | 'top |
| dired-recursive-deletes | 'top |
| ediff-window-setup-function | 'ediff-setup-windows-plain |
| eldoc-idle-delay | 0.4 |
| enable-recursive-minibuffers | t |
| eshell-cmpl-autolist | t |
| eshell-cmpl-cycle-completions | nil |
| eshell-cmpl-ignore-case | t |
| eshell-default-target-is-dot | t |
| eshell-destroy-buffer-when-process-dies | t |
| eshell-hist-ignoredups | 'erase |
| eshell-list-files-after-cd | t |
| eshell-review-quick-commands | t |
| eval-expression-print-level | nil |
| flymake-no-changes-timeout | nil |
| flymake-start-syntax-check-on-newline | nil |
| flyspell-highlight-properties | nil |
| flyspell-issue-welcome-flag | nil |
| frame-inhibit-implied-resize | t |
| history-delete-duplicates | t |
| ibuffer-default-display-maybe-show-predicates | t |
| ibuffer-expert | t |
| ibuffer-show-empty-filter-groups | nil |
| ibuffer-shrink-to-minimum-size | t |
| ibuffer-use-other-window | t |
| imenu-auto-rescan | t |
| indent-tabs-mode | nil |
| indicate-buffer-boundaries | 'left |
| indicate-empty-lines | t |
| isearch-allow-scroll | 'unlimited |
| isearch-lazy-count | t |
| isearch-yank-on-move | 'shift |
| ispell-quietly | t |
| ispell-silently-savep | t |
| jit-lock-chunk-size | 4096 |
| jit-lock-defer-time | 0.05 |
| jit-lock-stealth-time | 1.25 |
| kill-do-not-save-duplicates | t |
| kill-whole-line | t |
| load-prefer-newer | t |
| mac-command-modifier | 'meta |
| mac-option-modifier | 'super |
| mac-right-option-modifier | nil |
| mode-require-final-newline | nil |
| next-error-recenter | t |
| ns-function-modifier | 'hyper |
| ns-pop-up-frames | nil |
| nsm-save-host-names | t |
| nxml-sexp-element-flag | t |
| nxml-slash-auto-complete-flag | t |
| project-compilation-buffer-name-function | 'project-prefixed-buffer-name |
| read-buffer-completion-ignore-case | t |
| read-extended-command-predicate | 'command-completion-default-include-p |
| require-final-newline | t |
| mode-require-final-newline | t |
| resize-mini-windows | t |
| ruby-insert-encoding-magic-comment | nil |
| save-abbrevs | 'silently |
| save-interprogram-paste-before-kill | t |
| scroll-conservatively | 101 |
| scroll-preserve-screen-position | 'always |
| sentence-end-double-space | nil |
| set-mark-command-repeat-pop | t |
| shell-completion-execonly | nil |
| shell-input-autoexpand | nil |
| switch-to-buffer-in-dedicated-window | 'pop |
| switch-to-buffer-obey-display-actions | t |
| tab-always-indent | 'complete |
| term-input-autoexpand | t |
| term-input-ignoredups | t |
| tls-checktrust | t |
| undo-limit | 80000000 |
| undo-outer-limit | 120000000 |
| undo-strong-limit | 12000000 |
| uniquify-buffer-name-style | 'post-forward-angle-brackets |
| use-package-always-defer | t |
| version-control | t |
| view-inhibit-help-message | t |
| view-read-only | t |
| whitespace-line-column | 120 |
| window-combination-resize | t |
| woman-imenu | t |
| x-stretch-cursor | t |
| xref-show-definitions-function | 'xref-show-definitions-completing-read |#+BEGIN_SRC emacs-lisp :var defaults=defaults
(apply 'set-defaults
(mapcar (lambda (x) (list (intern (car x))
(if (stringp (cadr x))
(car (read-from-string (cadr x)))
(cadr x)))) defaults))
#+END_SRCMisc. defaults that don’t fit above. TODO: move these above.
#+BEGIN_SRC emacs-lisp
(when (file-exists-p user-emacs-directory)
(make-directory (expand-file-name "auto-save/" user-emacs-directory) t)
(make-directory (expand-file-name "backup/" user-emacs-directory) t)
(make-directory (expand-file-name "undo-tree/" user-emacs-directory) t))(set-defaults
'(auto-save-file-name-transforms `((".*"
,(expand-file-name "auto-save/"
user-emacs-directory) t)))
'(backup-directory-alist `((".*" .
,(expand-file-name "backup/"
user-emacs-directory))))
'(context-menu-functions
'(context-menu-ffap
context-menu-undo
context-menu-region
context-menu-middle-separator
context-menu-local
context-menu-minor
occur-context-menu
Man-context-menu
dictionary-context-menu
context-menu-buffers))
'(comint-password-prompt-regexp (concat
"\\(^ *\\|"
(regexp-opt
'("Enter" "enter" "Enter same" "enter same" "Enter the" "enter the"
"Enter Auth" "Old" "old" "New" "new" "'s" "login"
"Kerberos" "CVS" "UNIX" " SMB" "LDAP" "PEM" "SUDO"
"[sudo]" "Repeat" "Bad" "Retype")
t)
;; Allow for user name to precede password equivalent (Bug#31075).
" +.*\\)"
"\\(?:" (regexp-opt password-word-equivalents) "\\|Response\\)"
"\\(?:\\(?:, try\\)? *again\\| (empty for no passphrase)\\| (again)\\)?"
;; "[[:alpha:]]" used to be "for", which fails to match non-English.
"\\(?: [[:alpha:]]+ .+\\)?[[:blank:]]*[::៖][[:blank:]]*\\'"))
'(compilation-error-regexp-alist
'(absoft ada aix ant bash borland python-tracebacks-and-caml cmake cmake-info comma msft edg-1 edg-2
epc ftnchek gradle-kotlin gradle-android iar ibm irix java javac jikes-file maven jikes-line
clang-include gcc-include ruby-Test::Unit gmake gnu lcc makepp mips-1 mips-2 omake oracle perl
php rxp shellcheck sparc-pascal-file sparc-pascal-line sparc-pascal-example sun sun-ada watcom
4bsd gcov-file gcov-header gcov-nomark gcov-called-line gcov-never-called perl--Pod::Checker perl--Test
perl--Test2 perl--Test::Harness weblint guile-file guile-line typescript-tsc-plain typescript-tsc-pretty
("^\\(?1:\\(?:[^\11\12 0-9]\\|[0-9]+[^\0120-9]\\)\\(?:[^\12 :]\\| [^\12/-]\\|:[^\12 ]\\)*?\\):(\\(?2:[0-9]+\\),\\(?3:[0-9]+\\))-(\\(?4:[0-9]+\\),\\(?5:[0-9]+\\))" 1 (2 . 4) (3 . 5))
("^ \\([^:\n\t]+\\):\\([0-9]+\\):\\([0-9]+\\): $" 1 2 3 0)))
'(completion-styles '(basic partial-completion emacs22 substring flex))
'(custom-file (expand-file-name "settings.el" user-emacs-directory))
'(dired-listing-switches "-alhv --group-directories-first")
'(dired-omit-files "^\\.\\|^#.*#$")
'(dirtrack-list '("^[^:]*:\\(?:\\[[0-9]+m\\)*\\([^\\$# ]+\\)" 1))
'(eshell-prompt-function
(lambda ()
(concat (when (tramp-tramp-file-p default-directory)
(concat
(tramp-file-name-user
(tramp-dissect-file-name default-directory))
"@"
(tramp-file-name-host
(tramp-dissect-file-name default-directory))
" "))
(let ((dir (eshell/pwd)))
(if (string= dir (getenv "HOME")) "~"
(let ((dirname (file-name-nondirectory dir)))
(if (string= dirname "") "/" dirname))))
(if (= (user-uid) 0) " # " " $ "))))
'(eshell-visual-commands
'("vi" "screen" "top" "less" "more" "lynx" "ncftp" "pine" "tin"
"trn" "elm" "ssh" "mutt" "tmux" "htop"
"alsamixer" "watch" "elinks" "links" "nethack" "vim"
"cmus" "nmtui" "nmtui-connect" "nmtui-edit" "ncdu"
"telnet" "rlogin"))
'(eshell-visual-subcommands '(("vagrant" "ssh")))
'(find-ls-option '("-print0 | xargs -P4 -0 ls -ldN" . "-ldN"))
'(find-ls-subdir-switches "-ldN")
'(frame-title-format
'(:eval
(if (buffer-file-name)
(abbreviate-file-name (buffer-file-name))
"%b")))
'(ibuffer-formats
'((mark modified read-only " " (name 16 -1) " "
(size 6 -1 :right) " " (mode 16 16) " " filename)
(mark " " (name 16 -1) " " filename)))
'(ibuffer-never-show-predicates '("\\*magit-\\(diff\\|process\\):"))
'(ispell-extra-args '("--sug-mode=ultra"))
'(mouse-wheel-scroll-amount '(1 ((shift) . 5) ((control))))
'(minibuffer-prompt-properties
'(read-only t
cursor-intangible t
point-entered minibuffer-avoid-prompt
face minibuffer-prompt))
'(package-archives
'(("melpa" . "https://melpa.org/packages/")
("org" . "http://orgmode.org/elpa/")
("gnu" . "https://elpa.gnu.org/packages/")))
'(savehist-additional-variables '(search-ring
regexp-search-ring
kill-ring
comint-input-ring
kmacro-ring
sr-history-registry
file-name-history
tablist-name-filter))
'(tramp-default-proxies-alist
'(((regexp-quote (system-name)) nil nil)
(nil "\\`root\\'" "/ssh:%h:")
(".*" "\\`root\\'" "/ssh:%h:")))
'(uniquify-ignore-buffers-re "^\\*")
'(vc-git-diff-switches '("-w" "-U3"))
'(vc-ignore-dir-regexp
"\\(\\(\\`\\(?:[\\/][\\/][^\\/]+[\\/]\\|/\\(?:net\\|afs\\|\\.\\.\\.\\)/\\)\\'\\)\\|\\(\\`/[^/|:][^/|]*:\\)\\)\\|\\(\\`/[^/|:][^/|]*:\\)")
'(whitespace-action '(cleanup))
'(whitespace-style '(face trailing lines space-before-tab empty))
'(whitespace-global-modes '(not erc-mode ses-mode))
'(display-buffer-alist
'(("\\\\*Occur\\\\*\\\\|\\\\*grep\\\\*\\\\|\\\\*xref\\\\*\\\\|\\\\*ripgrep-search\\\\*\\\\|\\\\*rg\\\\*\\\\|\\\\*compilation\\\\*" display-buffer-reuse-window
(inhibit-same-window))
("\\\\*e?shell\\\\*" display-buffer-in-direction
(direction . bottom)
(window . root)
(window-height . 0.3))
("\\\\*grep\\\\*\\\\|\\\\*compilation\\\\*\\\\|\\\\*ripgrep-search\\\\*\\\\|\\\\*rg\\\\*" display-buffer-in-side-window
(side . right)
(slot . 0)
(window-parameters
(no-delete-other-windows . t))
(window-width . 80))
("\\\\*Help\\\\*\\\\|\\\\*helpful [^\\\\*]*\\\\*"
(display-buffer-reuse-window display-buffer-pop-up-window))
("\\\\*Ilist\\\\*" display-buffer-in-atom-window
(side . right))
("\\\\*vterm\\\\*"
(display-buffer-reuse-mode-window display-buffer-at-bottom)
(inhibit-same-window)
(mode vterm-mode vterm-copy-mode)
(dedicated . t)
(window-height . 0.3))))
'(compilation-save-buffers-predicate
(lambda ()
(let ((proj (project-current)))
(and proj
(memq (current-buffer) (project-buffers proj)))))))
#+END_SRC*** Site paths
Now, pull in generated paths from =site-paths.el=. Nix will generate this file
automatically for us & different Emacs variables will be set to their Nix
store derivation paths. Everything should work fine if you don’t have this
available, though. If you are in Emacs & already have the IDE installed you
can inspect this file by typing =C-h C-l site-paths=. It will look similar to a
=settings.el= file where each line corresponds to a customizable variable.
Unlike =settings.el=, each entry is path in the Nix store & we verify it
exists before setting it.#+BEGIN_SRC emacs-lisp
(load "site-paths" t)
#+END_SRC*** Set environment
=set-envs= is provided by [[./lisp/set-defaults.el][set-defaults]]. We can use it like
=custom-set-variables=, just it calls =setenv= instead of =setq=. All of
these entries correspond to environment variables that we want to always be
set in the Emacs process.#+BEGIN_SRC emacs-lisp
(set-envs
'("VISUAL" "emacsclient -a emacs")
'("EDITOR" "emacsclient -a emacs")
'("NODE_NO_READLINE" "1")
'("PAGER" "cat")
'("PS1" "\\W > ")
)
#+END_SRCFix broken Git on Windows.
#+BEGIN_SRC emacs-lisp
(when (eq window-system 'w32)
(setenv "GIT_ASKPASS" "git-gui--askpass"))
#+END_SRC*** Load custom file
This file allows users to override the above defaults. This will mean you
can use custom as you normally would in vanilla Emacs.#+BEGIN_SRC emacs-lisp
(when custom-file
(load custom-file t))
#+END_SRC** Setup use-package
[[https://github.com/jwiegley/use-package][use-package]] is an Emacs package by John Weigley allowing users to easily
configure other Emacs packages. It’s quite useful & it will be used
extensively in this project.Now to get =use-package= we will require =package.el= & initialize it if
site-paths is not setup (meaning we’re outside the Nix expression). Because
site-paths should be available (unless you don’t have Nix), we can skip this
step. All of this is marked ‘eval-and-compile’ to make sure the compiler picks
it up on build phase.So, there are basically two modes for using this configuration. One when
packages are installed externally (through Nix) & another where they are
installed internally. This is captured in the variable ‘needs-package-init’
which will be t when we want to use the builtin package.el & will be nil when
we want to just assume everything is available.#+BEGIN_SRC emacs-lisp
(eval-and-compile
(setq needs-package-init
(and (not (locate-library "site-paths"))
(not (and
(boundp 'use-package-list--is-running)
use-package-list--is-running)))))
#+END_SRCFirst handle using =package.el=. We will do all of the work of bootstrapping
here including running =package-initialize=, ensuring =use-package=, & =delight=
are installed.#+BEGIN_SRC emacs-lisp
(when needs-package-init
(require 'package)
(package-initialize)
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package))
(unless (package-installed-p 'delight)
(package-refresh-contents)
(package-install 'delight)))
#+END_SRCActually require =use-package=,
#+BEGIN_SRC emacs-lisp
(eval-and-compile
(require 'delight)
(require 'bind-key)
(require 'use-package))
#+END_SRCNow let’s handle the case where all of the packages are already provided.
Basically, we’ll prevent use-package from running ‘ensure’ on anything.#+BEGIN_SRC emacs-lisp
(eval-and-compile
(setq use-package-always-ensure needs-package-init)
(when (not needs-package-init)
(setq use-package-ensure-function 'ignore
package-enable-at-startup nil
package--init-file-ensured t)))
#+END_SRC** Key bindings
Using bind-key, setup some simple key bindings. None of these should overwrite
Emacs’ default keybindings. Also, they should only require vanilla Emacs to work
(non-vanilla Emacs key bindings should be put in their =use-package=
declaration). These are meant to all be as close to vanilla Emacs as possible. I
try to avoid extremely specific key binds here.What is overwritten can be seen with =M-x describe-personal-keybindings=. The
goal is to overwrite as little as possible. When it is necessary to overwrite
Emacs keybinds, documentation on why should be provided.First we include a library that provides some nice helper functions that will be
used as key bindings.#+BEGIN_SRC emacs-lisp
(require 'bauer)
(require 'files)
#+END_SRCDefine some helper functions.
#+BEGIN_SRC emacs-lisp
(defun web-search (start end)
(interactive "r")
(let ((q (buffer-substring-no-properties start end)))
(browse-url (concat "http://www.google.com/search?btnI&q="
(url-hexify-string q)))))
#+END_SRCOverride browse-url to handle man protocol better.
#+BEGIN_SRC emacs-lisp
(require 'browse-url)
(defun my-browse-url (url &rest args)
"Ask a WWW browser to load URL.
Prompt for a URL, defaulting to the URL at or before point.
Invokes a suitable browser function which does the actual job.
The variable `browse-url-browser-function' says which browser function to
use. If the URL is a mailto: URL, consult `browse-url-mailto-function'
first, if that exists.The additional ARGS are passed to the browser function. See the doc
strings of the actual functions, starting with `browse-url-browser-function',
for information about the significance of ARGS (most of the functions
ignore it).
If ARGS are omitted, the default is to pass `browse-url-new-window-flag'
as ARGS."
(interactive (browse-url-interactive-arg "URL: "))
(unless (called-interactively-p 'interactive)
(setq args (or args (list browse-url-new-window-flag))))
(when (and url-handler-mode
(not (file-name-absolute-p url))
(not (string-match "\\`[a-z]+:" url)))
(setq url (expand-file-name url)))
(let ((process-environment (copy-sequence process-environment))
(function (or (and (string-match "\\`mailto:" url)
browse-url-mailto-function)
(and (string-match "\\`man:" url)
browse-url-man-function)
browse-url-browser-function))
;; Ensure that `default-directory' exists and is readable (b#6077).
(default-directory (or (unhandled-file-name-directory default-directory)
(expand-file-name "~/"))))
;; When connected to various displays, be careful to use the display of
;; the currently selected frame, rather than the original start display,
;; which may not even exist any more.
(if (stringp (frame-parameter nil 'display))
(setenv "DISPLAY" (frame-parameter nil 'display)))
(if (and (consp function)
(not (functionp function)))
;; The `function' can be an alist; look down it for first match
;; and apply the function (which might be a lambda).
(catch 'done
(dolist (bf function)
(when (string-match (car bf) url)
(apply (cdr bf) url args)
(throw 'done t)))
(error "No browse-url-browser-function matching URL %s"
url))
;; Unbound symbols go down this leg, since void-function from
;; apply is clearer than wrong-type-argument from dolist.
(apply function url args))))(advice-add 'browse-url :override 'my-browse-url)
#+END_SRC#+BEGIN_SRC emacs-lisp
(defun my-escape-quotes (begin end)
(interactive
(if (region-active-p)
(list (region-beginning) (region-end))
(list (line-beginning-position) (line-end-position))))
(save-excursion
(save-restriction
(narrow-to-region begin end)
(goto-char (point-min))
(while (search-forward "\"" nil t)
(replace-match "\\\"" t t)))))(defun my-unescape-quotes (begin end)
(interactive
(if (region-active-p)
(list (region-beginning) (region-end))
(list (line-beginning-position) (line-end-position))))
(save-excursion
(save-restriction
(narrow-to-region begin end)
(goto-char (point-min))
(while (search-forward "\\\"" nil t)
(replace-match "\"" t t)))))
#+END_SRC#+BEGIN_SRC
(with-eval-after-load 'project
(defun project-find-regexp-with-unique-buffer (orig-fun &rest args)
"An advice function that gives project-find-regexp a unique buffer name"
(require 'xref)
(let ((xref-buffer-name (format "%s %s" xref-buffer-name (car args))))
(apply orig-fun args)))(advice-add 'project-find-regexp :around #'project-find-regexp-with-unique-buffer))
#+END_SRCNow we will call =bind-keys=. We give it keys to bind & what function to run
when those keys are pressed. Note on syntax of bind-keys: if you are unfamiliar
with how Emacs key binding works, you should read through [[https://www.masteringemacs.org/article/mastering-key-bindings-emacs][this article]]. Some
things done below include:- Make frame and window management a little bit easier. These are all used to
better navigations.
- Scale text size for different context. Defaults to 12pt fonts.
- Add evaluator keys, useful for executing lisp expressions.
- Add some read-only mode keybindings.#+NAME: keybinds
| Key combination | Action |
|-----------------+---------------------------------|
| | toggle-frame-fullscreen |
| s-C- | enlarge-window-horizontally |
| s-C- | shrink-window-horizontally |
| s-C- | shrink-window |
| s-C- | enlarge-window |
| | shrink-window |
| | enlarge-window |
| | windmove-down |
| | windmove-up |
| | windmove-left |
| | windmove-right |
| C-x 5 3 | iconify-frame |
| C-x 5 4 | toggle-frame-fullscreen |
| | other-window |
| | other-window |
| s-o | other-window |
| ESC o | other-window |
| s-1 | other-frame |
| C-c m b | eval-buffer |
| C-c m e | eval-last-sexp |
| C-c m i | eval-expression |
| C-c m d | eval-defun |
| C-c m n | eval-print-last-sexp |
| C-c m r | eval-region |
| C-c C-u | rename-uniquely |
| C-c C-o | browse-url-at-point |
| H-l | browse-url-at-point |
| H-c | compile |
| s-c | compile |
| s-r | revert-buffer |
| M-s d | find-grep-dired |
| M-s F | find-grep |
| M-s G | grep |
| C-x r q | save-buffers-kill-terminal |
| C-c C- | delete-blank-lines |
| C- | menu-bar-mode |
| C-x M-g | browse-url-at-point |
| M-s f | find-name-dired |
| s-SPC | cycle-spacing |
| C-c w w | whitespace-mode |
| M-g l | goto-line |
| | backward-kill-sexp |
| C-x v H | vc-region-history |
| C-c SPC | just-one-space |
| C-c f | flush-lines |
| C-c o | customize-option |
| C-c O | customize-group |
| C-c F | customize-face |
| C-c q | fill-region |
| C-c s | replace-string |
| C-c u | rename-uniquely |
| C-c z | clean-buffer-list |
| C-c = | count-matches |
| C-c ; | comment-or-uncomment-region |
| C-c [ | align-regexp |
| s-/ | comment-or-uncomment-region |
| M-s l | sort-lines |
| M-s m | multi-occur |
| M-s M | multi-occur-in-matching-buffers |
| C-c i i | imenu |
| s-v | scroll-down-command |
| s-M-e | end-of-defun |
| s-M-a | beginning-of-defun |
| s-? | xref-find-references |
| s-. | xref-find-definitions |
| s-, | xref-go-back |
| s-> | xref-go-forward |
| s-% | query-replace |
| C-c \ u | my-unescape-quotes |
| C-c \ e | my-escape-quotes |#+BEGIN_SRC emacs-lisp :var keybinds=keybinds
(mapc (lambda (x) (bind-key (car x) (intern (cadr x)))) keybinds)
#+END_SRCSome bauer-specific custom keybindings.
#+BEGIN_SRC emacs-lisp
(bind-keys
([f12] . next-error)
([f11] . previous-error)
([shift f12] . previous-error)
([mouse-9] . next-buffer)
([mouse-8] . previous-buffer))(bind-keys
("C-c I" . bauer-find-config)
:prefix-map bauer-git
:prefix "s-g"
("l" . magit-clone):prefix-map bauer-help
:prefix "s-h"
("k" . describe-personal-keybindings)
("p" . ffap)
("m" . man)
("w" . woman))
#+END_SRCTerminal mode key bindings follow. Scrolling in the term with the mouse should
move text.#+BEGIN_SRC emacs-lisp
(unless window-system
(global-set-key (kbd "") 'scroll-down-line)
(global-set-key (kbd "") 'scroll-up-line))
#+END_SRCmacOS-specific bindings follow. Fullscreen handling should use the macOS
feature, while by default it uses a custom Emacs stuff. In addition, drag and
drop needs a special binding.#+BEGIN_SRC emacs-lisp
(when (eq window-system 'mac)
(defun mac-fullscreen ()
(interactive)
(let ((fullscreen (frame-parameter nil 'fullscreen)))
(if (memq fullscreen '(fullscreen fullboth))
(let ((fullscreen-restore (frame-parameter nil 'fullscreen-restore)))
(if (memq fullscreen-restore '(maximized fullheight fullwidth))
(set-frame-parameter nil 'fullscreen fullscreen-restore)
(set-frame-parameter nil 'fullscreen nil)))
(modify-frame-parameters
nil `((fullscreen . fullscreen) (fullscreen-restore . ,fullscreen))))))(bind-key "C-x 5 4" 'mac-fullscreen)
(when (fboundp 'ns-drag-n-drop-as-text)
(global-set-key [M-s-drag-n-drop]
'ns-drag-n-drop-as-text)))
#+END_SRCAdd special quotes and arrows to ctrl x 8 keymap.
#+BEGIN_SRC emacs-lisp
(bind-keys
:package iso-transl
:map iso-transl-ctl-x-8-map
("' /" . "′")
("\" /" . "″")
("\" (" . "“")
("\" )" . "”")
("' (" . "‘")
("' )" . "’")
("4 < -" . "←")
("4 - >" . "→")
("4 b" . "←")
("4 f" . "→")
("4 p" . "↑")
("4 n" . "↓")
("" . "⇓")
("" . "↓")
("" . "⇐")
("" . "←")
("" . "⇒")
("" . "→")
("" . "⇑")
("" . "↑")
("," . "…"))
#+END_SRCBind help map keys.
#+BEGIN_SRC emacs-lisp
(bind-keys
:map help-map
("C-r" . woman)
("j" . woman)
("C-j" . man)
("C-s" . web-search))
#+END_SRCMore keys that have custom functions. TODO: move these above
#+BEGIN_SRC emacs-lisp
(bind-keys
("C-x ~" . (lambda () (interactive) (find-file "~")))
("C-x /" . (lambda () (interactive) (find-file "/")))
("C-x 4 C-x ~" . (lambda () (interactive) (find-file-other-window "~")))
("C-x 4 C-x /" . (lambda () (interactive) (find-file-other-window "/")))("C-x M-p" . (lambda () (interactive)
(save-excursion (other-window 1)
(quit-window))))("C-M--" . (lambda () (interactive)
(update-font-size -1 t)))
("C-M-=" . (lambda () (interactive)
(update-font-size 1 t)))
("C-M-0" . (lambda () (interactive)
(update-font-size 12 nil))))
#+END_SRCDelete current buffer
#+BEGIN_SRC
;;;###autoload
(defun delete-file-and-buffer ()
"Kill the current buffer and deletes the file it is visiting."
(interactive)
(let ((filename (buffer-file-name)))
(when filename
(if (vc-backend filename)
(vc-delete-file filename)
(progn
(delete-file filename)
(message "Deleted file %s" filename)
(kill-buffer))))))
#+END_SRC** Setup installer
Installer provides installation & upgrading functionality. You can upgrade the
IDE at any time by typing =M-x upgrade= from within Emacs. You may have to
restart Emacs for the upgrade to take place. See [[./lisp/installer.el][installer.el]] for documentation.#+BEGIN_SRC emacs-lisp
(require 'installer nil t)
#+END_SRC** Packages
:PROPERTIES:
:CUSTOM_ID: packages
:END:Each of these entries are =use-package= calls that will both install & load
the package for us. The most important are listed first in “Essentials”.
“Built-in" Emacs packages are also configured. Next comes the “Programming
Language” modes. Finally, we list some miscellaneous modes.This is an alphabetized listing of all Emacs packages needed by the IDE. To
resort, go to one of the package group headings & type =C-c ^ a=.*** Essentials
These are the best & most useful modes available to us in Emacs world.
**** aggressive-indent
[[https://github.com/Malabarba/aggressive-indent-mode][GitHub]]Automatically indent code as you type. Only enabled for Lisp currently.
#+BEGIN_SRC emacs-lisp
(use-package aggressive-indent
:hook ((emacs-lisp-mode
inferior-emacs-lisp-mode
ielm-mode
lisp-mode
inferior-lisp-mode
isp-interaction-mode
slime-repl-mode) . aggressive-indent-mode))
#+END_SRC**** consult
[[https://github.com/minad/consult][GitHub]]
A better version of ‘completing-read’ that is isn’t as heavy duty as company.
#+BEGIN_SRC emacs-lisp
(use-package orderless
:custom
(completion-styles '(substring orderless basic))
(completion-category-defaults nil)
(completion-category-overrides '((file (styles basic partial-completion)))))(use-package vertico
:hook (minibuffer-setup . cursor-intangible-mode)
:demand
:config
(vertico-mode)(defun crm-indicator (args)
(cons (format "[CRM%s] %s"
(replace-regexp-in-string
"\\`\\[.*?]\\*\\|\\[.*?]\\*\\'" ""
crm-separator)
(car args))
(cdr args)))(advice-add #'completing-read-multiple :filter-args #'crm-indicator)
)(use-package corfu
:custom
(corfu-cycle t) ;; Enable cycling for `corfu-next/previous'
(corfu-preselect 'prompt) ;; Always preselect the prompt
:bind
(:map corfu-map
("TAB" . corfu-next)
([tab] . corfu-next)
("S-TAB" . corfu-previous)
([backtab] . corfu-previous)):init
(global-corfu-mode))(use-package consult
;; Replace bindings. Lazily loaded due by `use-package'.
:bind (;; C-c bindings (mode-specific-map)
("C-c M-x" . consult-mode-command)
("C-c h" . consult-history)
("C-c k" . consult-kmacro)
;; ("C-c m" . consult-man)
("C-c i" . consult-info)
([remap Info-search] . consult-info)
;; C-x bindings (ctl-x-map)
([remap repeat-complex-command] . consult-complex-command)
([remap switch-to-buffer] . consult-buffer)
([remap switch-to-buffer-other-window] . consult-buffer-other-window)
([remap switch-to-buffer-other-frame] . consult-buffer-other-frame)
([remap bookmark-jump] . consult-bookmark)
([remap project-switch-to-buffer] . consult-project-buffer)
;; Custom M-# bindings for fast register access
("M-#" . consult-register-load)
("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
("C-M-#" . consult-register)
;; Other custom bindings
([remap yank-pop] . consult-yank-pop)
;; M-g bindings (goto-map)
("M-g e" . consult-compile-error)
("M-g f" . consult-flymake) ;; Alternative: consult-flycheck
([remap goto-line] . consult-goto-line) ;; orig. goto-line
("M-g M-g" . consult-goto-line) ;; orig. goto-line
("M-g o" . consult-outline) ;; Alternative: consult-org-heading
("M-g m" . consult-mark)
("M-g k" . consult-global-mark)
("M-g i" . consult-imenu)
("M-g I" . consult-imenu-multi)
;; M-s bindings (search-map)
("M-s d" . consult-find)
("M-s D" . consult-locate)
("M-s g" . consult-grep)
("M-s G" . consult-git-grep)
("M-s r" . consult-ripgrep)
("M-s l" . consult-line)
("M-s L" . consult-line-multi)
("M-s k" . consult-keep-lines)
("M-s u" . consult-focus-lines)
;; Isearch integration
("M-s e" . consult-isearch-history)
:map isearch-mode-map
("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-s l" . consult-line) ;; needed by consult-line to detect isearch
("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch
;; Minibuffer history
:map minibuffer-local-map
("M-s" . consult-history) ;; orig. next-matching-history-element
("M-r" . consult-history)) ;; orig. previous-matching-history-element;; Enable automatic preview at point in the *Completions* buffer. This is
;; relevant when you use the default completion UI.
:hook (completion-list-mode . consult-preview-at-point-mode);; The :init configuration is always executed (Not lazy)
:init(setq register-preview-delay 0.5
register-preview-function #'consult-register-format)(advice-add #'register-preview :override #'consult-register-window)
:config
(consult-customize
consult-theme :preview-key '(:debounce 0.2 any)
consult-ripgrep consult-git-grep consult-grep
consult-bookmark consult-recent-file consult-xref
consult--source-bookmark consult--source-file-register
consult--source-recent-file consult--source-project-recent-file
:preview-key '(:debounce 0.4 any))
)
#+END_SRC**** envrc
[[https://github.com/purcell/envrc][
GitHub]]Envrc is able to execute a project’s .envrc file for you automatically.
Unfortunately, it doesn’t support asynchronous updates yet, so this can
occasionally block Emacs.#+BEGIN_SRC emacs-lisp
(use-package inheritenv);; envrc must come late in the init.el file so add-hook adds it first
;; in `find-file-hook'.
(use-package envrc
:ensure nil
:bind (
:package project
:map project-prefix-map
("," . envrc-reload))
:custom (envrc-none-lighter nil)
:demand t
:config
(envrc-global-mode))
#+END_SRC**** dtrt-indent
[[https://github.com/jscheid/dtrt-indent][GitHub]]
This mode will try to detect your indentation.
#+BEGIN_SRC emacs-lisp
(use-package dtrt-indent
:delight
:custom (dtrt-indent-verbosity 0)
:hook (prog-mode . dtrt-indent-mode))
#+END_SRC**** Emacs shell
Emacs shell provides a shell written in Elisp. Run eshell by typing =C-c e= or =M-x eshell=.
#+BEGIN_SRC emacs-lisp
(use-package eshell
:bind (("C-c M-t" . eshell)
("C-c x" . eshell)
("C-c e" . eshell))
:hook (;; (eshell-first-time-mode-hook . eshell-read-history)
(eshell-first-time-mode-hook . (lambda () (add-hook 'eshell-expand-input-functions 'eshell-spawn-external-command))))
:preface
(defvar eshell-isearch-map
(let ((map (copy-keymap isearch-mode-map)))
(define-key map [(control ?m)] 'eshell-isearch-return)
(define-key map [return] 'eshell-isearch-return)
(define-key map [(control ?r)] 'eshell-isearch-repeat-backward)
(define-key map [(control ?s)] 'eshell-isearch-repeat-forward)
(define-key map [(control ?g)] 'eshell-isearch-abort)
(define-key map [backspace] 'eshell-isearch-delete-char)
(define-key map [delete] 'eshell-isearch-delete-char)
map)
"Keymap used in isearch in Eshell.")
(defun eshell-spawn-external-command (beg end)
"Parse and expand any history references in current input."
(save-excursion
(goto-char end)
(when (looking-back "&!" beg)
(delete-region (match-beginning 0) (match-end 0))
(goto-char beg)
(insert "spawn ")))))
#+END_SRC***** esh-help
Setup eldoc integration.
#+BEGIN_SRC emacs-lisp
(use-package esh-help
:preface
(autoload 'esh-help-eldoc-command "esh-help")
(defun esh-help-turn-on ()
(interactive)
(setq-local eldoc-documentation-function
'esh-help-eldoc-command)
(setq eldoc-documentation-function
'esh-help-eldoc-command)
(eldoc-mode 1))
:hook (eshell-mode . esh-help-turn-on))
#+END_SRC***** em-dired
#+BEGIN_SRC emacs-lisp
(use-package em-dired
:preface
(autoload 'em-dired-new "em-dired")
:ensure nil
:bind (:package dired
:map dired-mode-map
("e" . em-dired))
:hook (eshell-mode . em-dired-mode)
:init
(advice-add 'eshell :before 'em-dired-new))
#+END_SRC**** Gnus
[[http://www.gnus.org][Website]]
Gnus is an infamous email client & news reader.
#+BEGIN_SRC emacs-lisp
(use-package gnus
:hook ((dired-mode . turn-on-gnus-dired-mode)))
#+END_SRC**** GCMH
[[https://github.com/emacsmirror/gcmh][GitHub]]
Intelligently adjust Emacs garbage collection size.
#+BEGIN_SRC emacs-lisp
(use-package gcmh
:delight
:demand
:config
(gcmh-mode 1)
:custom
(gcmh-idle-delay 'auto)
(gcmh-auto-idle-delay-factor 10)
(gcmh-high-cons-threshold (* 16 1024 1024)))
#+END_SRC**** Magit
[[https://magit.vc][Website]]
Magit is a Git porcelain for Emacs. All of the features from the Git command
line are available in an intuitive Emacs buffer.#+BEGIN_SRC emacs-lisp
(use-package magit
:preface
(autoload 'magit-toplevel "magit")
(autoload 'magit-read-string-ns "magit")
(autoload 'magit-get "magit")
;; (autoload 'magit-define-popup-action "magit")
(autoload 'magit-remote-arguments "magit")
(defun magit-dired-other-window ()
(interactive)
(dired-other-window (magit-toplevel)))
;; :hook ((git-commit-mode . git-commit-save-message)
;; (git-commit-mode . turn-on-auto-fill))
:custom (magit-blame-disable-modes '(fci-mode view-mode yascroll-bar-mode))
:custom (magit-process-find-password-functions '(magit-process-password-auth-source))
:custom (magit-process-password-prompt-regexps '(
"^\\(Enter \\)?[Pp]assphrase\\( for \\(RSA \\)?key '.*'\\)?: ?$"
"^\\(Enter \\)?[Pp]assword\\( for '?\\(https?://\\)?\\(?99:[^']*\\)'?\\)?: ?$"
"Please enter the passphrase for the ssh key"
"Please enter the passphrase to unlock the OpenPGP secret key"
"^.*'s password: ?$"
"^Yubikey for .*: ?$"
"^Enter PIN for .*: ?$"
"^\\[sudo\\] password for .*: ?$"))
:custom (magit-clone-set-remote.pushDefault t)
:custom (magit-log-auto-more t)
:custom (magit-remote-add-set-remote.pushDefault t)
:custom (magit-save-repository-buffers 'dontask)
:commands (magit-clone)
:if (locate-file "git" exec-path)
:bind (("C-x g" . magit-status)
("C-x p v" . magit-status)
("C-x p m" . magit-status)
("C-x G" . magit-dispatch)
:package magit
:map magit-mode-map
("C-o" . magit-dired-other-window))
:init (setq auto-revert-buffer-list-filter 'magit-auto-revert-repository-buffer-p))
#+END_SRCMagit forge.
#+BEGIN_SRC emacs-lisp
(use-package forge
:after magit)
#+END_SRC***** git-timemachine
#+BEGIN_SRC emacs-lisp
(use-package git-timemachine
:commands (git-timemachine))
#+END_SRC**** MMM Mode
[[https://github.com/purcell/mmm-mode][GitHub]]
MMM mode lets you edit multiple languages within one buffer.
#+BEGIN_SRC emacs-lisp
(use-package mmm-mode
:custom (mmm-submode-decoration-level 2)
:config
(use-package mmm-auto
:ensure nil))
#+END_SRC**** multiple-cursors
[[https://github.com/magnars/multiple-cursors.el][
GitHub]]Multiple cursors give you more cursors. It is bound to =C->= & =C-<=.
#+BEGIN_SRC emacs-lisp
(use-package multiple-cursors
:bind
(("" . mc/mark-next-like-this)
("" . mc/mark-previous-like-this)
("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("M-" . mc/add-cursor-on-click)
("C-c C-<" . mc/mark-all-like-this)
("C-!" . mc/mark-next-symbol-like-this)
("C-S-c C-S-c" . mc/edit-lines)))
#+END_SRC**** Org
[[https://orgmode.org][Website]]
Org mode is an impressive suite of text editing solutions. It gives you an
outliner but also much much more.#+BEGIN_SRC emacs-lisp
(use-package org
:ensure org-contrib
:custom (org-latex-listings-langs
'((emacs-lisp "Lisp")
(lisp "Lisp")
(clojure "Lisp")
(c "C")
(cc "C++")
(fortran "fortran")
(perl "Perl")
(cperl "Perl")
(python "Python")
(ruby "Ruby")
(html "HTML")
(xml "XML")
(tex "TeX")
(latex "[LaTeX]TeX")
(shell-script "bash")
(gnuplot "Gnuplot")
(ocaml "Caml")
(caml "Caml")
(sql "SQL")
(sqlite "sql")
(makefile "make")
(R "r")
(nix "{}")
(nil "{}")
(yaml "{}")
(gitattributes "{}")
(gitignore "{}")
(shell "{}")
(gitconfig "{}")))
:custom (org-latex-default-packages-alist
'(("utf8" "inputenc" t
("pdflatex"))
("T1" "fontenc" t
("pdflatex"))
("" "graphicx" t nil)
("" "grffile" t nil)
("" "longtable" nil nil)
("" "wrapfig" nil nil)
("" "rotating" nil nil)
("normalem" "ulem" t nil)
("" "amsmath" t nil)
("" "textcomp" t nil)
("" "amssymb" t nil)
("" "capt-of" nil nil)
("" "hyperref" nil nil)
("" "parskip" nil nil)
("" "alltt" nil nil)
("" "upquote" nil nil)
("" "listings" nil nil)))
:custom (org-catch-invisible-edits 'smart)
:custom (org-confirm-babel-evaluate nil)
:custom (org-export-with-toc nil)
:custom (org-html-htmlize-output-type 'css)
:custom (org-log-done 'time)
:custom (org-special-ctrl-a/e t)
:custom (org-support-shift-select t)
:hook ((org-mode . (lambda ()
(add-hook 'completion-at-point-functions
'pcomplete-completions-at-point nil t)))
(org-mode . (lambda () (visual-line-mode -1)))
(org-mode . (lambda () (setq-local scroll-margin 3)))
(message-mode . turn-on-orgtbl)
;; (org-mode . (lambda ()
;; (autoload 'org-eldoc-documentation-function "org-eldoc")
;; (setq-local eldoc-documentation-function
;; 'org-eldoc-documentation-function)))
)
:config
;; (org-babel-do-load-languages 'org-babel-load-languages
;; '((sql . t)
;; (shell . t)))
)
(use-package org-download
:hook (dired-mode . org-download-enable))
(use-package org-present)
#+END_SRC**** smart-hungry-delete
[[https://github.com/hrehfeld/emacs-smart-hungry-delete][GitHub]]
Smart hungry delete automatically delete lots of whitespace in a row.
#+BEGIN_SRC emacs-lisp
(use-package smart-hungry-delete
:if (>= emacs-major-version 25)
:bind (:map prog-mode-map
("" .
smart-hungry-delete-backward-char)
("C-d" .
smart-hungry-delete-forward-char))
:hook ((prog-mode .
smart-hungry-delete-default-prog-mode-hook)
(c-mode-common .
smart-hungry-delete-default-c-mode-common-hook)
(python-mode .
smart-hungry-delete-default-c-mode-common-hook)
(text-mode .
smart-hungry-delete-default-text-mode-hook)))
#+END_SRC**** sudo-edit
[[https://github.com/nflath/sudo-edit][GitHub]]
Sudo-edit lets you open a file using sudo (it actually goes through TRAMP to
achieve this).#+BEGIN_SRC emacs-lisp
(use-package sudo-edit
:bind (("C-c C-r" . sudo-edit)))
#+END_SRC**** Theme
[[https://github.com/waymondo/apropospriate-theme][GitHub]]
This is the theme I use & it works well for this configuration. It is dark
with high contrast. We will only enable it when we are running with GUI Emacs.#+BEGIN_SRC emacs-lisp
(use-package apropospriate-theme
:if window-system
:hook ((mac-effective-appearance-change . (lambda ()
(pcase (plist-get (mac-application-state) :appearance)
("NSAppearanceNameAqua" (load-theme 'apropospriate-light t))
("NSAppearanceNameDarkAqua" (load-theme 'apropospriate-dark t))))))
:init
(add-to-list 'custom-theme-load-path
(file-name-directory
(locate-library "apropospriate-theme")))
(if (eq (frame-parameter nil 'background-mode) 'light)
(load-theme 'apropospriate-light t)
(load-theme 'apropospriate-dark t)))
#+END_SRCWhile apropospriate is the default, other themes can be used as well! For
instance spacemacs-theme can be enabled:#+BEGIN_SRC emacs-lisp :tangle no
(use-package spacemacs-common
:ensure spacemacs-theme
:if window-system
:init
(add-to-list 'custom-theme-load-path
(file-name-directory
(locate-library "spacemacs-theme-pkg")))
(load-theme 'spacemacs-dark t))
#+END_SRC**** try
[[https://github.com/larstvei/Try][GitHub]]
Try out packages without installing them.
#+BEGIN_SRC emacs-lisp
(use-package try)
#+END_SRC**** which-key
[[https://github.com/justbur/emacs-which-key][GitHub]]
which-key will tell you what key bindings are available give a prefix. Test it
out by pressing =C-x= & waiting a few seconds. Each key listed is bound to a
function.#+BEGIN_SRC emacs-lisp
(use-package which-key
:demand
:custom (which-key-lighter "")
:custom (which-key-idle-delay 0.4)
:custom (which-key-idle-secondary-delay 0.4)
:commands (which-key-mode)
:config (which-key-mode 1))
#+END_SRC*** Built-ins
These are available automatically, so these =use-package= blocks just
configure them.**** autorevert
Autorevert mode makes files update when they have changed on disk. Unfortunately
this can have some issues in cases where Emacs uses the wrong file. Need to
investigate how to fix this.#+BEGIN_SRC emacs-lisp
(use-package autorevert
:demand
:config (global-auto-revert-mode t))
#+END_SRC**** bug-reference
Provides links to bugs listed in source code.
#+BEGIN_SRC emacs-lisp
(use-package bug-reference
:custom (bug-reference-bug-regexp "\\(\\(?:[Ii]ssue \\|[Ff]ixe[ds] \\|[Rr]esolve[ds]? \\|[Cc]lose[ds]?\\|[Pp]\\(?:ull [Rr]equest\\|[Rr]\\) \\|(\\)#\\([0-9]+\\))?\\)")
:hook ((prog-mode . bug-reference-prog-mode)
(text-mode . bug-reference-mode)))
#+END_SRC**** comint
Base mode used for shell and terminal modes.
#+BEGIN_SRC emacs-lisp
(defvar comint-input-ring-prefix ": [[:digit:]]+:[[:digit:]]+;"
"Possible prefix that may come before history elements. In
Zshell with extended_history, this is useful.")(defvar ffap-url-at-point)
(use-package comint
:ensure nil
:hook ((comint-mode . (lambda () (setq-local ffap-url-at-point "/ssh:")))
(comint-mode . (lambda () (toggle-truncate-lines 1))))
:preface
(autoload 'comint-write-input-ring "comint")
(autoload 'comint-read-input-ring "comint")
(autoload 'comint-send-invisible "comint")
(defun turn-on-comint-history (history-file)
(setq comint-input-ring-file-name history-file)
(comint-read-input-ring 'silent))
(defun save-history ()
(dolist (buffer (buffer-list))
(with-current-buffer buffer
(comint-write-input-ring))))
:config
(advice-add 'comint-read-input-ring
:override (lambda (&optional silent)
(cond ((or (null comint-input-ring-file-name)
(equal comint-input-ring-file-name ""))
nil)
((not (file-readable-p comint-input-ring-file-name))
(or silent
(message "Cannot read history file %s"
comint-input-ring-file-name)))
(t
(let* ((file comint-input-ring-file-name)
(count 0)
;; Some users set HISTSIZE or `comint-input-ring-size'
;; to huge numbers. Don't allocate a huge ring right
;; away; there might not be that much history.
(ring-size (min 1500 comint-input-ring-size))
(ring (make-ring ring-size)))
(with-temp-buffer
(insert-file-contents file)
;; Save restriction in case file is already visited...
;; Watch for those date stamps in history files!
(goto-char (point-max))
(let (start end history)
(while (and (< count comint-input-ring-size)
(re-search-backward comint-input-ring-separator
nil t)
(setq end (match-beginning 0)))
(setq start
(if (re-search-backward comint-input-ring-separator
nil t)
(progn
(when (looking-at (concat comint-input-ring-separator
comint-input-ring-prefix))
;; Skip zsh extended_history stamps
(re-search-forward comint-input-ring-prefix
nil t))
(match-end 0))
(progn
(goto-char (point-min))
(if (looking-at comint-input-ring-prefix)
(progn
(re-search-forward comint-input-ring-prefix
nil t)
(match-end 0))
(point-min)))))
(setq history (buffer-substring start end))
(goto-char start)
(when (and (not (string-match comint-input-history-ignore
history))
(or (null comint-input-ignoredups)
(ring-empty-p ring)
(not (string-equal (ring-ref ring 0)
history))))
(when (= count ring-size)
(ring-extend ring (min (- comint-input-ring-size ring-size)
ring-size))
(setq ring-size (ring-size ring)))
(ring-insert-at-beginning ring history)
(setq count (1+ count))))))
(setq comint-input-ring ring
comint-input-ring-index nil)))))))(use-package comint-hyperlink
:ensure nil
:commands (comint-hyperlink-process-output)
:init (add-to-list 'comint-output-filter-functions 'comint-hyperlink-process-output))
#+END_SRC**** compile
#+BEGIN_SRC emacs-lisp
(use-package compile
:bind (("C-c C-c" . compile)
:map compilation-mode-map
("o" . compile-goto-error))
:preface
(autoload 'ansi-color-process-output "ansi-color")
(defun show-compilation ()
(interactive)
(let ((compile-buf
(catch 'found
(dolist (buf (buffer-list))
(if (string-match "\\*compilation\\*"
(buffer-name buf))
(throw 'found buf))))))
(if compile-buf
(switch-to-buffer-other-window compile-buf)
(call-interactively 'compile)))))
#+END_SRC**** conf-mode
#+BEGIN_SRC emacs-lisp
(use-package conf-mode
:mode (("/\\.merlin\\'" . conf-mode)
("_oasis\\'" . conf-mode)
("_tags\\'" . conf-mode)
("_log\\'" . conf-mode)))
#+END_SRC
**** delsel#+BEGIN_SRC emacs-lisp
(use-package delsel
:demand
:config (delete-selection-mode t))
#+END_SRC**** dired
#+BEGIN_SRC emacs-lisp
(use-package dired
:ensure nil
:preface
(autoload 'dired-get-filename "dired")
(autoload 'term-set-escape-char "term")
(defun dired-run-command (&optional filename)
"Run file at point in a new buffer."
(interactive)
(unless filename
(setq filename (expand-file-name
(dired-get-filename t t)
default-directory)))
(let ((buffer (make-term
(file-name-nondirectory filename)
filename))
(buffer-read-only nil))
(with-current-buffer buffer
;; (term-mode)
(term-char-mode)
(term-set-escape-char ?\C-x))
(set-process-sentinel
(get-buffer-process buffer)
(lambda (proc event)
(when (not (process-live-p proc))
(kill-buffer (process-buffer proc)))))
(switch-to-buffer buffer)))
:bind (("C-c J" . dired-double-jump)
:package dired
:map dired-mode-map
("C-c C-c" . compile)
("r" . term)
("M-@" . shell)
("M-*" . eshell)
("W" . browse-url-of-dired-file)
("@" . dired-run-command)))
#+END_SRC***** dired-column
#+BEGIN_SRC emacs-lisp
(use-package dired-column
:ensure nil
:bind (:package dired
:map dired-mode-map
("o" . dired-column-find-file)))
#+END_SRC***** dired-subtree
#+BEGIN_SRC emacs-lisp
(use-package dired-subtree
:bind (:package dired
:map dired-mode-map
("" . dired-subtree-toggle)
("TAB" . dired-subtree-toggle)
("" . dired-subtree-cycle)))
#+END_SRC***** dired-x
#+BEGIN_SRC emacs-lisp
(use-package dired-x
:ensure nil
:hook ((dired-mode . dired-omit-mode))
:bind (("s-\\" . dired-jump-other-window)
:package dired
:map dired-mode-map
(")" . dired-omit-mode)))
#+END_SRC**** display-line-numbers
#+BEGIN_SRC emacs-lisp
(use-package display-line-numbers
:if (>= emacs-major-version 26)
:hook ((prog-mode . display-line-numbers-mode)
(conf-mode . display-line-numbers-mode)))
#+END_SRC
**** eldocProvides some info for the thing at the point.
#+BEGIN_SRC emacs-lisp
(use-package eldoc
:hook ((emacs-lisp-mode . eldoc-mode)
(eval-expression-minibuffer-setup . eldoc-mode)
(lisp-mode-interactive-mode . eldoc-mode)
(typescript-mode . eldoc-mode)
(haskell-mode . eldoc-mode)
(python-mode . eldoc-mode)
(eshell-mode . eldoc-mode)
(org-mode . eldoc-mode)))
#+END_SRC**** electric
Setup these modes:
- electric-quote
- electric-indent
- electric-layout#+BEGIN_SRC emacs-lisp
(use-package electric
:if (>= emacs-major-version 25)
:hook ((prog-mode . electric-quote-local-mode)
(text-mode . electric-quote-local-mode)
(org-mode . electric-quote-local-mode)
(message-mode . electric-quote-local-mode)
(prog-mode . electric-indent-local-mode)
(prog-mode . electric-layout-mode)
(haskell-mode . (lambda () (electric-indent-local-mode -1)))
(nix-mode . (lambda () (electric-indent-local-mode -1)))))
#+END_SRC***** elec-pair
Setup electric-pair-mode for prog-modes. Also disable it when smartparens is
setup.#+BEGIN_SRC emacs-lisp
(use-package elec-pair
:if (>= emacs-major-version 25)
:hook
((prog-mode . electric-pair-local-mode)
(eval-expression-minibuffer-setup . electric-pair-local-mode)
(smartparens-mode . (lambda ()
(electric-pair-local-mode -1)))))
#+END_SRC**** eww
eww is enabled so we can open files in non-graphical environments.
#+BEGIN_SRC emacs-lisp
(use-package eww
:if (and (not window-system)
(not (string-equal
(getenv "TERM_PROGRAM")
"Apple_Terminal")))
:commands (eww-browse-url eww-reload)
:config
(add-hook 'eww-mode-hook (lambda ()
(add-hook 'text-scale-mode-hook (lambda (&rest _) (eww-reload t)) nil t)
(add-hook 'window-size-change-functions (lambda (&rest _) (eww-reload t)) nil t)))
:init
(setq browse-url-browser-function 'eww-browse-url))
#+END_SRC
**** executableMake scripts executable automatically.
#+BEGIN_SRC emacs-lisp
(use-package executable
:hook
((after-save .
executable-make-buffer-file-executable-if-script-p)))
#+END_SRC**** ffap
#+BEGIN_SRC emacs-lisp
(use-package ffap
:bind (([remap find-file] . find-file-at-point)
([remap find-file-other-window] . ffap-other-window)
([remap find-file-read-only] . ffap-read-only)
([remap find-alternate-file] . ffap-alternate-file)
([remap find-file-other-window] . ffap-other-window)
([remap find-file-other-frame] . ffap-other-frame)
([remap find-file-read-only-other-window] . ffap-read-only-other-window)
([remap find-file-read-only-other-frame] . ffap-read-only-other-frame)
([remap dired] . dired-at-point)
([remap dired-other-window] . ffap-dired-other-window)
([remap dired-other-frame] . ffap-dired-other-frame)
([remap list-directory] . ffap-list-directory))
:hook ((gnus-summary-mode . ffap-gnus-hook)
(gnus-article-mode . ffap-gnus-hook)
(vm-mode . ffap-ro-mode-hook)
(rmail-mode . ffap-ro-mode-hook)))
#+END_SRC
**** files#+BEGIN_SRC emacs-lisp
(use-package files
:ensure nil
;; :demand
;; :config (auto-save-visited-mode 1)
:preface
(defun find-file--line-number (orig-fun filename
&optional wildcards)
"Turn files like file.js:14:10 into file.js and going to line 14, col 10."
(save-match-data
(let* ((matched (string-match
"^\\(.*?\\):\\([0-9]+\\):?\\([0-9]*\\)$"
filename))
(line-number (and matched
(match-string 2 filename)
(string-to-number
(match-string 2 filename))))
(col-number (and matched
(match-string 3 filename)
(string-to-number (match-string 3 filename))))
(filename (if matched
(match-string 1 filename)
filename)))
(apply orig-fun (list filename wildcards))
(when line-number
;; goto-line is for interactive use
(goto-char (point-min))
(forward-line (1- line-number))
(when (> col-number 0)
(forward-char (1- col-number)))))))
:config
(advice-add 'find-file
:around #'find-file--line-number)
)
#+END_SRC**** flyspell
#+BEGIN_SRC emacs-lisp
(use-package flyspell
:if (locate-file
(if (boundp 'ispell-program-name)
ispell-program-name "aspell")
exec-path)
:hook ((text-mode . flyspell-mode)
(prog-mode . flyspell-prog-mode)
(git-commit-mode . flyspell-mode))
:bind (:map flyspell-mode-map
("C-M-i" . nil)))
#+END_SRC**** goto-addr
#+BEGIN_SRC emacs-lisp
(use-package goto-addr
:hook (((prog-mode conf-mode) . goto-address-prog-mode)
((help-mode org-mode text-mode) . goto-address-mode)
(git-commit-mode . goto-address-mode)
(shell-mode . goto-address-mode)))
#+END_SRC**** hl-line
#+BEGIN_SRC emacs-lisp
(use-package hl-line
:hook ((prog-mode . hl-line-mode)
(org-mode . hl-line-mode)
(dired-mode . hl-line-mode)))
#+END_SRC
**** ibuffer#+BEGIN_SRC emacs-lisp
(use-package ibuffer
:commands (ibuffer-do-sort-by-alphabetic)
:bind ([remap list-buffers] . ibuffer))
#+END_SRC**** imenu-list
#+BEGIN_SRC emacs-lisp
(use-package imenu-list
:bind ("s-g i" . imenu-list))
#+END_SRC***** mouse
#+BEGIN_SRC emacs-lisp
(use-package mouse
:ensure nil
:hook ((text-mode . context-menu-mode)
(prog-mode . context-menu-mode)))
#+END_SRC**** paren
#+BEGIN_SRC emacs-lisp
(use-package paren
:hook ((prog-mode . show-paren-mode)
(smartparens-mode . (lambda () (show-paren-mode -1)))))
#+END_SRC**** pp
#+BEGIN_SRC emacs-lisp
(use-package pp
:bind (([remap eval-expression] . pp-eval-expression))
;; :init
;;(global-unset-key (kbd "C-x C-e"))
:hook ((lisp-mode emacs-lisp-mode) . always-eval-sexp)
:preface
(defun always-eval-sexp ()
(define-key (current-local-map)
(kbd "C-x C-e")
'pp-eval-last-sexp)))
#+END_SRC**** prog-mode
#+BEGIN_SRC emacs-lisp
(use-package prog-mode
:ensure nil
:hook (;; (prog-mode . prettify-symbols-mode)
;; (lisp-mode . prettify-symbols-lisp)
;; (c-mode . prettify-symbols-c)
;; (c++-mode . prettify-symbols-c++)
;; ((js-mode js2-mode) . prettify-symbols-js)
(prog-mode . (lambda ()
(setq-local scroll-margin 3))))
:preface
(defun prettify-symbols-prog ()
(push '("<=" . ?≤) prettify-symbols-alist)
(push '(">=" . ?≥) prettify-symbols-alist))
(defun prettify-symbols-lisp ()
(push '("/=" . ?≠) prettify-symbols-alist)
(push '("sqrt" . ?√) prettify-symbols-alist)
(push '("not" . ?¬) prettify-symbols-alist)
(push '("and" . ?∧) prettify-symbols-alist)
(push '("or" . ?∨) prettify-symbols-alist))
(defun prettify-symbols-c ()
(push '("<=" . ?≤) prettify-symbols-alist)
(push '(">=" . ?≥) prettify-symbols-alist)
(push '("!=" . ?≠) prettify-symbols-alist)
(push '("&&" . ?∧) prettify-symbols-alist)
(push '("||" . ?∨) prettify-symbols-alist)
(push '(">>" . ?») prettify-symbols-alist)
(push '("<<" . ?«) prettify-symbols-alist))
(defun prettify-symbols-c++ ()
(push '("<=" . ?≤) prettify-symbols-alist)
(push '(">=" . ?≥) prettify-symbols-alist)
(push '("!=" . ?≠) prettify-symbols-alist)
(push '("&&" . ?∧) prettify-symbols-alist)
(push '("||" . ?∨) prettify-symbols-alist)
(push '(">>" . ?») prettify-symbols-alist)
(push '("<<" . ?«) prettify-symbols-alist)
(push '("->" . ?→) prettify-symbols-alist))
(defun prettify-symbols-js ()
(push '("function" . ?λ) prettify-symbols-alist)
(push '("=>" . ?⇒) prettify-symbols-alist)))
#+END_SRC**** persistent-mode
#+BEGIN_SRC emacs-lisp
(use-package persistent-mode
:ensure nil)
#+END_SRC**** savehist-mode
#+BEGIN_SRC emacs-lisp
(use-package savehist
:hook (after-init . savehist-mode))
#+END_SRC**** saveplace-mode
#+BEGIN_SRC emacs-lisp
(use-package saveplace
:if (>= emacs-major-version 25)
:hook (after-init . save-place-mode))
#+END_SRC**** Shell
#+BEGIN_SRC emacs-lisp
(use-package shell
:bind (("C-c C-s" . shell)
("H-s" . shell)
("M-@" . shell))
:hook ((shell-mode . ansi-color-for-comint-mode-on)
(shell-mode . dirtrack-mode)
;; (shell-mode . (lambda ()
;; (turn-on-comint-history (expand-file-name "sh-history"
;; user-emacs-directory))))
)
:config
(require 'tramp)
(defun shell-set-remote-shell-path-maybe (orig-fun &rest args)
(when (file-remote-p default-directory)
(if (file-exists-p (concat (file-remote-p default-directory) shell-file-name))
(set (make-local-variable 'explicit-shell-file-name) shell-file-name)
(if (file-exists-p (concat (file-remote-p default-directory) tramp-default-remote-shell))
(set (make-local-variable 'explicit-shell-file-name) tramp-default-remote-shell))))
(apply orig-fun args))
(advice-add 'shell :around 'shell-set-remote-shell-path-maybe))
#+END_SRC**** simple
#+BEGIN_SRC emacs-lisp
(use-package simple
:ensure nil
:demand
:bind
(("C-`" . list-processes)
([remap upcase-word] . upcase-dwim)
([remap downcase-word] . downcase-dwim)
([remap capitalize-word] . capitalize-dwim)
:map minibuffer-local-map
("" . abort-recursive-edit)
("M-TAB" . previous-complete-history-element)
("" . next-complete-history-element))
:hook ((text-mode . visual-line-mode)
(text-mode . auto-fill-mode))
:config (column-number-mode))
#+END_SRC
**** subword#+BEGIN_SRC emacs-lisp
(use-package subword
:hook ((java-mode . subword-mode)))
#+END_SRC**** so-long
#+BEGIN_SRC emacs-lisp
(use-package so-long
:if (>= emacs-major-version 27)
:demand
:config (global-so-long-mode 1))
#+END_SRC**** term
#+BEGIN_SRC emacs-lisp
(use-package term
:commands (term-char-mode)
:hook ((term-mode .
(lambda ()
(setq term-prompt-regexp
"^[^#$%>\n]*[#$%>] *")
(setq-local transient-mark-mode nil)
(auto-fill-mode -1))))
:preface
(autoload 'tramp-tramp-file-p "tramp")
(autoload 'tramp-dissect-file-name "tramp"))
#+END_SRC**** text-mode
#+BEGIN_SRC emacs-lisp
(use-package text-mode
:no-require
:ensure nil
:hook ((text-mode . turn-on-auto-fill)))
#+END_SRC**** timeclock
#+BEGIN_SRC emacs-lisp
(use-package timeclock)(require 'timeclock)
(defvar timeclock-report-mode-map
(let ((map (make-sparse-keymap)))
(define-key map [?g] #'timeclock-report-redo)
(define-key map [?q] #'timeclock-report-quit)
map)
"Keymap for `timeclock-report-mode'.")(easy-menu-define timeclock-report-mode-menu timeclock-report-mode-map
"Ledger report menu"
'("Timeclock Report"
["Reload" timeclock-report-redo]
["Quit" timeclock-report-quit]
))(define-derived-mode timeclock-report-mode special-mode "Timeclock-Report"
"A mode for viewing timeclock reports."
(hack-dir-local-variables-non-file-buffer)
(setq-local revert-buffer-function 'timeclock-report-redo))(defvar timeclock-report-buffer-name "*Timeclock Report*")
(defun timeclock-report-quit ()
(interactive)
(unless (buffer-live-p (get-buffer timeclock-report-buffer-name))
(user-error "No timeclock report buffer"))
(quit-windows-on timeclock-report-buffer-name 'kill))(defun timeclock-report-redo (&optional _ignore-auto _noconfirm)
(interactive)
(unless (derived-mode-p 'timeclock-report-mode)
(user-error "Not in a timeclock-report-mode buffer"))
(timeclock-reread-log)
(let ((cur-buf (current-buffer)))
(when (get-buffer timeclock-report-buffer-name)
(with-silent-modifications
(erase-buffer)
(timeclock-report-mode)
(timeclock-generate-report)
(newline))
(pop-to-buffer cur-buf))))(defun timeclock-report ()
"Timeclock report."
(interactive)
(timeclock-reread-log)
(with-current-buffer
(pop-to-buffer (get-buffer-create timeclock-report-buffer-name) 'display-buffer-pop-up-window)
(with-silent-modifications
(erase-buffer)
(timeclock-report-mode)
(timeclock-generate-report)
(newline))))(defun timeclock-ledger-report ()
"Timeclock ledger report."
(interactive)
(let* ((timeclock-file buffer-file-name)
(buffer-name (format "*Ledger Report [%s]*" timeclock-file))
(ledger-report-name "timelog")
(report-cmd (format "%s --color --force-color -f %s reg -D \"expr(commodity == 's')\""
(shell-quote-argument ledger-binary-path)
(shell-quote-argument timeclock-file))))
(with-current-buffer
(pop-to-buffer (get-buffer-create buffer-name))
(with-silent-modifications
(erase-buffer)
(ledger-report-mode)
(set (make-local-variable 'ledger-report-cmd) report-cmd)
(ledger-do-report report-cmd)))))(defvar timeclock-mode-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c C-i") 'timeclock-in)
(define-key map (kbd "C-c C-o") 'timeclock-out)
(define-key map (kbd "C-c C-c") 'timeclock-change)
(define-key map (kbd "C-c C-r") 'timeclock-reread-log)
(define-key map (kbd "C-c C-u") 'timeclock-update-mode-line)
(define-key map (kbd "C-c C-w") 'timeclock-when-to-leave-string)
(define-key map (kbd "C-c C-t") 'timeclock-report)
(define-key map (kbd "C-c C-e") 'timeclock-ledger-report);; Reset the `text-mode' override of this standard binding
(define-key map (kbd "C-M-i") 'completion-at-point)
map)
"Keymap for `timeclock-mode'.")(easy-menu-define timeclock-mode-menu timeclock-mode-map
"Menu for `timeclock-mode'."
'("Timeclock"
["Clock in" timeclock-in]
["Clock out" timeclock-out]
["Change project" timeclock-change]
["Reread log" timeclock-reread-log]
["Update modeline" timeclock-update-mode-line]
["When to leave" timeclock-when-to-leave-string]
"---"
["Customize Timeclock Mode" (lambda () (interactive) (customize-group 'timeclock))]
"---"
["Report" timeclock-report]
["Ledger Report" timeclock-ledger-report]
))(defgroup timeclock-faces nil "Timeclock mode highlighting" :group 'timeclock)
(defface timeclock-font-directive-face
`((t :inherit font-lock-preprocessor-face))
"Default face for timeclock"
:group 'timeclock-faces)(defface timeclock-font-uncleared-face
`((t :inherit warning))
"Uncleared face fortimeclock"
:group 'timeclock-faces)(defface timeclock-font-cleared-face
`((t :inherit shadow))
"Cleared face for timeclock"
:group 'timeclock-faces)(defface timeclock-font-comment-face
`((t :inherit font-lock-comment-face))
"Face for Timeclock comments"
:group 'timeclock-faces)(defface timeclock-font-date-face
`((t :inherit font-lock-keyword-face))
"Face for Timeclock dates"
:group 'timeclock-faces)(defun timeclock-font-face-by-state (num faces)
"Choose one of two faces depending on a timeclock directive character.
NUM specifies a match group containing the character.
FACES has the form (CLEARED UNCLEARED).
Return CLEARED if the character specifies a cleared transaction,
UNCLEARED otherwise."
(if (member (match-string num) '("I" "O"))
(nth 0 faces)
(nth 1 faces)))(defvar timeclock-font-lock-keywords
`((,(concat "^\\([IiOo]\\)"
"\\(?:[[:blank:]]+\\([^[:blank:]\n]+"
"\\(?:[[:blank:]]+[^[:blank:]\n]+\\)?\\)"
"\\(?:[[:blank:]]+\\(.*?\\)"
"\\(?:\t\\|[ \t]\\{2,\\}\\(.*?\\)"
"\\(?:\t\\|[ \t]\\{2,\\}\\(;.*\\)\\)?\\)?\\)?\\)?$")
(1 'timeclock-font-directive-face)
(2 'timeclock-font-date-face nil :lax)
(3 (timeclock-font-face-by-state 1 '(timeclock-font-cleared-face
timeclock-font-directive-face)) nil :lax)
(4 (timeclock-font-face-by-state 1 '(timeclock-font-cleared-face
timeclock-font-uncleared-face)) nil :lax)
(5 'timeclock-font-comment-face nil :lax)))
"Expressions to highlight in Timeclock mode.");;;###autoload
(define-derived-mode timeclock-mode text-mode "Timeclock"
"A major mode to timelog files."
(setq font-lock-defaults
'(timeclock-font-lock-keywords t nil nil nil))(when (and (boundp 'display-time-mode) display-time-mode)
(timeclock-mode-line-display t)));;;###autoload
(add-to-list 'auto-mode-alist '("timelog\\'" . timeclock-mode))
#+END_SRC
**** url-handlers#+BEGIN_SRC emacs-lisp
(use-package url-handlers
:ensure nil
:demand
:config (url-handler-mode))
#+END_SRC**** which-func
#+BEGIN_SRC emacs-lisp
(use-package which-func
:demand
:config (which-function-mode))
#+END_SRC
**** whitespaceShow whitespace
#+BEGIN_SRC emacs-lisp
(use-package whitespace
:hook (prog-mode . whitespace-mode))
#+END_SRC*** Programming languages
Each =use-package= declaration corresponds to =major modes= in Emacs lingo.
Each language will at least one of these major modes as well as associated
packages (for completion, syntax checking, etc.)Eglot is builtin to Emacs and can work with any language with an LSP backend.
#+BEGIN_SRC emacs-lisp
(use-package eglot
:config
:custom (eglot-ignored-server-capabilities '(:codeLensProvider :documentFormattingProvider :documentRangeFormattingProvider :documentLinkProvider :colorProvider))
:custom (eglot-report-progress nil)
:custom (eglot-sync-connect nil)
)
#+END_SRC**** CSV
[[https://elpa.gnu.org/packages/csv-mode.html][ELPA]]
#+BEGIN_SRC emacs-lisp
(use-package csv-mode
:hook ((csv-mode . (lambda () (visual-line-mode -1)))
(csv-mode . (lambda () (auto-fill-mode -1)))
(csv-mode . (lambda () (toggle-truncate-lines 1)))))
#+END_SRC**** ELF
[[https://oremacs.com/2016/08/28/elf-mode/][Website]]
#+BEGIN_SRC emacs-lisp
(use-package elf-mode
:magic ("ELF" . elf-mode))
#+END_SRC**** Emacs speaks statistics
[[https://ess.r-project.org][Website]]
#+BEGIN_SRC emacs-lisp
(use-package ess-site
:ensure ess
:no-require
:interpreter (("Rscript" . r-mode)
("r" . r-mode))
:mode (("\\.sp\\'" . S-mode)
("/R/.*\\.q\\'" . R-mode)
("\\.[qsS]\\'" . S-mode)
("\\.ssc\\'" . S-mode)
("\\.SSC\\'" . S-mode)
("\\.[rR]\\'" . R-mode)
("\\.[rR]nw\\'" . Rnw-mode)
("\\.[sS]nw\\'" . Snw-mode)
("\\.[rR]profile\\'" . R-mode)
("NAMESPACE\\'" . R-mode)
("CITATION\\'" . R-mode)
("\\.omg\\'" . omegahat-mode)
("\\.hat\\'" . omegahat-mode)
("\\.lsp\\'" . XLS-mode)
("\\.do\\'" . STA-mode)
("\\.ado\\'" . STA-mode)
("\\.[Ss][Aa][Ss]\\'" . SAS-mode)
("\\.[Ss]t\\'" . S-transcript-mode)
("\\.Sout" . S-transcript-mode)
("\\.[Rr]out" . R-transcript-mode)
("\\.Rd\\'" . Rd-mode)
("\\.[Bb][Uu][Gg]\\'" . ess-bugs-mode)
("\\.[Bb][Oo][Gg]\\'" . ess-bugs-mode)
("\\.[Bb][Mm][Dd]\\'" . ess-bugs-mode)
("\\.[Jj][Aa][Gg]\\'" . ess-jags-mode)
("\\.[Jj][Oo][Gg]\\'" . ess-jags-mode)
("\\.[Jj][Mm][Dd]\\'" . ess-jags-mode)
))
#+END_SRC**** git-modes
[[https://github.com/magit/git-modes][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package git-modes)
#+END_SRC**** Haskell
[[https://github.com/haskell/haskell-mode][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package haskell-mode
:custom (haskell-compile-cabal-build-command "cabal new-build")
:custom (haskell-hoogle-url "https://hoogle.haskell.org/?hoogle=%s")
:mode (("\\.cabal\\'" . haskell-cabal-mode))
:hook ((haskell-mode . subword-mode)
;; (haskell-mode . flyspell-prog-mode)
;; (haskell-mode . haskell-indentation-mode)
;; (haskell-mode . haskell-auto-insert-module-template)
;; (haskell-mode . haskell-decl-scan-mode)
;; (haskell-mode . turn-on-haskell-indent)
;; (haskell-mode . imenu-add-menubar-index)
;; (haskell-mode .
;; (lambda ()
;; (autoload 'haskell-doc-current-info
;; "haskell-doc")
;; (setq-local eldoc-documentation-function
;; 'haskell-doc-current-info)))
(haskell-mode . (lambda () (whitespace-mode -1)))):functions xref-push-marker-stack
:commands (haskell-session-maybe haskell-mode-find-def haskell-ident-at-point
haskell-mode-handle-generic-loc)
:bind (:map haskell-mode-map
("C-c h" . haskell-hoogle)
("C-c C-." . haskell-navigate-imports)
("C-`" . haskell-interactive-bring)
("C-c `" . haskell-interactive-bring)
("C-c C-t" . haskell-process-do-type)
("C-c C-i" . haskell-process-do-info)
("C-c C-k" . haskell-interactive-mode-clear)
("C-c c" . haskell-process-cabal)
;; ("M-." . haskell-mode-jump-to-def)
:map haskell-cabal-mode-map
("C-`" . haskell-interactive-bring)
("C-c C-k" . haskell-interactive-mode-clear)
("C-c c" . haskell-process-cabal))
:init
(add-to-list 'completion-ignored-extensions ".hi")
(require 'haskell-compile)
)
#+END_SRC**** JavaScript
#+BEGIN_SRC emacs-lisp
(use-package typescript-ts-mode
:mode (("\\.ts\\'" . typescript-ts-mode)
("\\.tsx\\'" . typescript-ts-mode)))(use-package js-mode
:ensure nil
:mode (("\\.es6\\'" . js-mode)
("\\.ejs\\'" . js-mode)
("\\.jshintrc$" . js-json-mode)
("\\.json_schema$" . js-json-mode))
:interpreter "node")
#+END_SRC**** LaTeX
***** auctexAuctex provides some helpful tools for working with LaTeX.
****** tex-mode
#+BEGIN_SRC emacs-lisp
(use-package tex-mode
:custom (TeX-auto-save t)
:custom (TeX-auto-untabify t)
:custom (TeX-electric-escape t)
:custom (TeX-parse-self t)
:hook (TeX-mode . latex-electric-env-pair-mode))
#+END_SRC
**** Lisp#+BEGIN_SRC emacs-lisp
(use-package elisp-mode
:ensure nil
:interpreter (("emacs" . emacs-lisp-mode)))
#+END_SRC***** ielm
#+BEGIN_SRC emacs-lisp
(use-package ielm
:bind ("C-c :" . ielm))
#+END_SRC**** Mach-O
View macho binaries read-only. To view in Hexl-mode raw binaries, run M-x
macho-mode to toggle then M-x hexl-mode.#+BEGIN_SRC emacs-lisp
(use-package macho-mode
:ensure nil
:magic (("\xFE\xED\xFA\xCE" . macho-mode)
("\xFE\xED\xFA\xCF" . macho-mode)
("\xCE\xFA\xED\xFE" . macho-mode)
("\xCF\xFA\xED\xFE" . macho-mode)))
#+END_SRC**** Markdown
[[https://jblevins.org/projects/markdown-mode/][Website]]
#+BEGIN_SRC emacs-lisp
(use-package markdown-mode
:mode (("README\\.md\\'" . gfm-mode)))
#+END_SRC**** Nix
***** nix-mode
[[https://github.com/NixOS/nix-mode][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package nix-mode)
#+END_SRC***** nix-shell
#+BEGIN_SRC emacs-lisp
(use-package nix-shell
:ensure nil)
#+END_SRC
***** nix-drv-mode
#+BEGIN_SRC emacs-lisp
(use-package nix-drv-mode
:ensure nil)
#+END_SRC
***** nix-update
#+BEGIN_SRC emacs-lisp
(use-package nix-update
:disabled
:bind (("C-. u" . nix-update-fetch)))
#+END_SRC
**** OCaml[[https://github.com/ocaml/tuareg][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package tuareg
:disabled
:config
;; Use Merlin if available
(when (require 'merlin nil t)
(defvar merlin-command)
(setq merlin-command 'opam)(declare-function merlin-mode "merlin.el")
(when (functionp 'merlin-document)
(define-key tuareg-mode-map (kbd "\C-c\C-h") 'merlin-document));; Run Merlin if a .merlin file in the parent dirs is detected
(add-hook 'tuareg-mode-hook
(lambda()
(let ((fn (buffer-file-name)))
(if (and fn (locate-dominating-file fn ".merlin"))
(merlin-mode)))))))
#+END_SRC**** Proof General
[[https://proofgeneral.github.io][Website]]
#+BEGIN_SRC emacs-lisp
(use-package proof-site
:ensure proofgeneral
:disabled
:custom (proof-auto-action-when-deactivating-scripting 'retract)
:custom (proof-autosend-enable nil)
:custom (proof-electric-terminator-enable t)
:custom (proof-fast-process-buffer nil)
:custom (proof-script-fly-past-comments t)
:custom (proof-shell-fiddle-frames nil)
:custom (proof-splash-enable nil)
:custom (proof-sticky-errors t)
:custom (proof-tidy-response t)
:demand
:if (not needs-package-init))
#+END_SRC**** Scala
[[https://github.com/hvesalai/emacs-scala-mode][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package scala-mode
:interpreter ("scala" . scala-mode))
#+END_SRC**** Shell
#+BEGIN_SRC emacs-lisp
(use-package sh-script
:mode (("\\.*shellrc$" . sh-mode)
("\\.*shell_profile" . sh-mode)
("\\.zsh\\'" . sh-mode)))
#+END_SRC**** texinfo
#+BEGIN_SRC emacs-lisp
(use-package texinfo)
#+END_SRC**** Terraform
[[https://github.com/hcl-emacs/terraform-mode][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package terraform-mode)
#+END_SRC**** YAML
[[https://github.com/yoshiki/yaml-mode][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package yaml-mode)
#+END_SRC*** Custom
These are all available in [[./site-lisp][./site-lisp]]. Eventually they should go into separate
repositories.**** dired-column
**** em-dired
**** installer
**** macho-mode
**** nethack#+BEGIN_SRC emacs-lisp
(use-package nethack
:ensure nil)
#+END_SRC**** nix-fontify
**** set-defaults
**** use-package-list
**** pcomplete-extra#+BEGIN_SRC emacs-lisp
(use-package pcomplete-extras
:ensure nil)
#+END_SRC*** Other
These should correspond to minor modes or helper functions. Some of them are
more helpful than others but none are /essential/.Most of these are available in MELPA.
**** browse-at-remote
[[https://github.com/rmuslimov/browse-at-remote][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package browse-at-remote
:bind ("C-c g g" . browse-at-remote))
#+END_SRC**** browse-kill-ring
[[https://github.com/T-J-Teru/browse-kill-ring][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package browse-kill-ring)
#+END_SRC**** buffer-move
[[https://github.com/lukhas/buffer-move][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package buffer-move
:bind
(("" . buf-move-up)
("" . buf-move-down)
("" . buf-move-left)
("" . buf-move-right)))
#+END_SRC**** copy-as-format
[[https://github.com/sshaw/copy-as-format][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package copy-as-format
:bind (("C-c w s" . copy-as-format-slack)
("C-c w g" . copy-as-format-github)))
#+END_SRC**** daemons
[[https://github.com/cbowdon/daemons.el][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package daemons)
#+END_SRC**** delight
[[https://elpa.gnu.org/packages/delight.html][ELPA]]
#+BEGIN_SRC emacs-lisp
(use-package delight)
#+END_SRC**** dired-rsync
[[https://github.com/stsquad/dired-rsync][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package dired-rsync
:bind (:map dired-mode-map ("C-c C-r" . dired-rsync)))
#+END_SRC**** emacs-gif-screencast
[[https://gitlab.com/ambrevar/emacs-gif-screencast][GitLab]]
#+BEGIN_SRC emacs-lisp
(use-package gif-screencast
:bind ("". gif-screencast-start-or-stop))
#+END_SRC**** ethan-wspace
#+BEGIN_SRC emacs-lisp
(use-package ethan-wspace
:config
(global-ethan-wspace-mode 1))
#+END_SRC**** git-attr
#+BEGIN_SRC emacs-lisp
(use-package git-attr-linguist
:ensure git-attr
:hook (find-file . git-attr-linguist))
#+END_SRC**** htmlize
[[https://github.com/hniksic/emacs-htmlize][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package htmlize :no-require)
#+END_SRC**** ibuffer-vc
[[https://github.com/purcell/ibuffer-vc][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package ibuffer-vc
:commands (ibuffer-vc-set-filter-groups-by-vc-root)
:hook ((ibuffer . (lambda ()
(ibuffer-vc-set-filter-groups-by-vc-root)
(unless (eq ibuffer-sorting-mode 'alphabetic)
(ibuffer-do-sort-by-alphabetic))))))
#+END_SRC**** keycast
[[https://github.com/tarsius/keycast][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package keycast)
#+END_SRC**** ledger-mode
[[https://github.com/ledger/ledger-mode][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package ledger-mode
:mode "\\.journal\\'")
#+END_SRC
**** mwim[[https://github.com/alezost/mwim.el][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package mwim
:bind (([remap move-beginning-of-line]
. mwim-beginning-of-code-or-line)
([remap move-end-of-line]
. mwim-end-of-code-or-line)))
#+END_SRC**** org-static-blog
[[https://github.com/bastibe/org-static-blog][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package org-static-blog)
#+END_SRC**** page-break-lines
[[https://github.com/purcell/page-break-lines][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package page-break-lines
:delight
:hook ((doc-mode
emacs-lisp-mode
outline-mode
prog-mode
haskell-mode
help-mode
magit-mode) . page-break-lines-mode))
#+END_SRC**** pandoc-mode
[[https://github.com/joostkremers/pandoc-mode][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package pandoc-mode
:hook ((markdown-mode . pandoc-mode)
(pandoc-mode . pandoc-load-default-settings)))
#+END_SRC**** rainbow-delimiters
[[https://github.com/Fanael/rainbow-delimiters][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package rainbow-delimiters
:hook ((emacs-lisp-mode
inferior-emacs-lisp-mode
ielm-mode
lisp-mode
inferior-lisp-mode
lisp-interaction-mode
slime-repl-mode) . rainbow-delimiters-mode))
#+END_SRC**** bats-mode
[[https://github.com/dougm/bats-mode][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package bats-mode)
#+END_SRC**** rainbow-mode
[[https://elpa.gnu.org/packages/rainbow-mode.html][ELPA]]
#+BEGIN_SRC emacs-lisp
(use-package rainbow-mode
:hook ((emacs-lisp-mode
inferior-emacs-lisp-mode
ielm-mode
lisp-mode
inferior-lisp-mode
lisp-interaction-mode
slime-repl-mode
less-css-mode
html-mode
css-mode) . rainbow-mode))
#+END_SRC**** shrink-whitespace
[[https://github.com/jcpetkovich/shrink-whitespace.el][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package shrink-whitespace
:bind ("H-SPC" . shrink-whitespace))
#+END_SRC**** string-inflection
[[https://github.com/akicho8/string-inflection][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package string-inflection
:bind (("C-c r r" . string-inflection-all-cycle)
("C-c r c" . string-inflection-camelcase)
("C-c r l" . string-inflection-lower-camelcase)
("C-c r u" . string-inflection-underscore)
("C-c r k" . string-inflection-kebab-case)
("C-c r J" . string-inflection-java-style-cycle)))
#+END_SRC**** systemd
[[https://github.com/holomorph/systemd-mode][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package systemd)
#+END_SRC**** undo-tree
[[https://elpa.gnu.org/packages/undo-tree.html][ELPA]]
#+BEGIN_SRC emacs-lisp
(use-package undo-tree
:demand
:custom (undo-tree-mode-lighter "")
:custom (undo-tree-history-directory-alist `((".*" . ,(expand-file-name "undo-tree/" user-emacs-directory))))
:config (global-undo-tree-mode))
#+END_SRC**** unfill
[[https://github.com/purcell/unfill][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package unfill
:bind ([remap fill-paragraph] . unfill-toggle))
#+END_SRC**** url-util
#+BEGIN_SRC emacs-lisp
(use-package url-util
:ensure nil
:functions (url-hexify-string url-unhex-string region-active-p)
:preface
(defun func-region (start end func)
"Run a function over the region between START and END in current buffer."
(unless (region-active-p) (error "No active region."))
(save-excursion
(let ((text (delete-and-extract-region start end)))
(insert (funcall func text)))))
(defun url-encode (start end)
"URL encode the region between START and END in current buffer."
(interactive "r")
(func-region start end 'url-hexify-string))
(defalias 'url-escape 'url-encode)
(defun url-decode (start end)
"URL decode the region between START and END in current buffer."
(interactive "r")
(func-region start end 'url-unhex-string))
(defalias 'url-unescape 'url-decode))
#+END_SRC**** xterm-mode
[[https://github.com/atomontage/xterm-color][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package xterm-color
:init
(defun advice-compilation-filter (f proc string)
(funcall f proc (xterm-color-filter string)))
(advice-add 'compilation-filter :around #'advice-compilation-filter)
)
#+END_SRC**** games
Miscellaneous games
#+BEGIN_SRC emacs-lisp
(use-package chess)
(use-package mines)
(use-package gnugo)
(use-package 2048-game)
#+END_SRC**** dumb-jump
#+BEGIN_SRC emacs-lisp
(use-package dumb-jump
:init
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate))
#+END_SRC**** visual-regexp
[[https://github.com/rmuslimov/browse-at-remote][GitHub]]
#+BEGIN_SRC emacs-lisp
(use-package visual-regexp)
#+END_SRC**** persistent-scratch
#+BEGIN_SRC emacs-lisp
(use-package persistent-scratch
:demand
:config (persistent-scratch-setup-default))
#+END_SRC** END
#+BEGIN_SRC emacs-lisp
(provide 'default)
#+END_SRC* Profiles
:PROPERTIES:
:CUSTOM_ID: profiles
:END:All of these files live outside of Emacs but are necessary for a usable
developer environment. They are basic shell profile and some git configuration
scripts as well.** =.profile=
:PROPERTIES:
:header-args: :tangle profile.sh :tangle-mode (identity #o755) :shebang "#!/usr/bin/env sh"
:END:To use this, you must create a short ~/.profile file. Here is an example,
#+BEGIN_SRC shell :tangle no :padline no
bootstrap="$HOME/.nix-profile/etc/profile"
[ -f $bootstrap ] && . $bootstrap
#+END_SRCSet some Nix variables that never seem to get set correctly. There are a few bug
that break how this works. Also need to set some common exports that are needed.
Lots of common variables need to know about the =.nix-profile= directory. We
leave the first MANPATH separator empty so it uses the default PATH lookup as
well. CVS_RSH is needed if your ever need to do cvs. HISTFILE sets where to
store the bash or zsh history. We set the history size to 10000 for good
measure. VISUAL should point to this editor.Here we setup =.profile=. First, setup exports. We leave the first part of
MANPATH empty so it resolves using the PATH-based method. Fallbacks are provided
just in case.#+BEGIN_SRC shell
export PATH="$HOME/bin":"$HOME/.nix-profile/bin":$PREFIX/bin:@PATH@${PATH:+:$PATH} \
INFOPATH="$HOME/.nix-profile/share/info":@INFOPATH@${INFOPATH:+:$INFOPATH} \
MANPATH=:"$HOME/.nix-profile/share/man":@MANPATH@${MANPATH:+:$MANPATH} \
XDG_DATA_DIRS="$HOME/.nix-profile/share":@XDG_DATA_DIRS@${XDG_DATA_DIRS:+:$XDG_DATA_DIRS} \
TERMINFO_DIRS=@TERMINFO_DIRS@${TERMINFO_DIRS:+:$TERMINFO_DIRS}if [ -z "${SSH_CONNECTION-}" ] && [ -z "${SSH_AUTH_SOCK-}" ] && command -v ssh-agent > /dev/null; then
eval $(ssh-agent) > /dev/null
fi# taken from https://github.com/unixorn/zsh-quickstart-kit/blob/main/zsh/.zshrc
if [ "$(uname -s)" = Darwin ] && [ $(/usr/bin/ssh-add -l | grep -c "The agent has no identities.") -eq 1 ]; then
# check if Monterey or higher
# https://scriptingosx.com/2020/09/macos-version-big-sur-update/
if [[ $(sw_vers -buildVersion) > 21 ]]; then
# Load all ssh keys that have pass phrases stored in macOS keychain using new flags
/usr/bin/ssh-add --apple-load-keychain
else
/usr/bin/ssh-add -qA
fi
fiHISTSIZE=50000
SAVEHIST=10000if [ -z "${TERM-}" ] || [ "$TERM" = dumb ] || [ "$TERM" = dumb-emacs-ansi ] || [ "$TERM" = eterm-color ]; then
if [ -n "${DISPLAY-}" ]; then
export VISUAL='emacsclient -a emacs -c'
else
export VISUAL='emacsclient -a emacs'
fi
elif [ -n "${PS1-}" ]; then
export VISUAL='emacsclient -a "emacs -nw" -t'
else
export VISUAL='emacsclient -a emacs'
fi
export EDITOR="$VISUAL"if [ -n "${SSH_CONNECTION-}" ]; then
export PINENTRY_USER_DATA=USE_CURSES=1
fiexport LESS=FRSMi \
INPUTRC=@INPUTRC@ \
GPG_TTY=$TTY \
CVS_RSH=sshexport SYSTEMD_LESS=$LESS
HISTCONTROL=ignoredups:erasedups:ignorespace
PROMPT_DIRTRIM=4if [ -n "${BASH_VERSION-}" ]; then
if [[ $- == *i* ]]; then
stty -ixon
fishopt -s cdable_vars \
cdspell \
checkwinsize \
histappend \
lithistshopt -s autocd \
checkjobs \
direxpand \
dirspell \
histverify \
histreedit \
no_empty_cmd_completion \
globstar \
2>/dev/null || trueif [ -n "${PS1-}" ] && [ "${TERM-}" != dumb ] && shopt -q progcomp 2>/dev/null && [ "${BASH_VERSINFO-0}" -gt 3 ]; then
. @bash_completion@/share/bash-completion/bash_completion
fiif { [ "${TERM-}" = dumb-emacs-ansi ] || [ -n "${INSIDE_EMACS-}" ]; } && [ -n "${SSH_CONNECTION-}" ]; then
PROMPT_DIRTRIM=0
PS1='\[\e[34m\]/ssh:\u@\H:$PWD\[\e[0m\] \[\e[33m\]$\[\e[0m\] '
elif [ -n "${SSH_CONNECTION-}" ]; then
PROMPT_DIRTRIM=0
PS1='\[\e[34m\]ssh://\u@\H\[\e[36m\]$PWD\[\e[0m\] \[\e[33m\]$\[\e[0m\] '
else
PS1='\[\e[34m\]\u@\h\[\e[0m\]:\[\e[36m\]\w \[\e[33m\]\$\[\e[0m\] '
fiif { [ "${TERM_PROGRAM-}" = iTerm.app ] || [ -n "${SSH_CONNECTION-}" ]; } && [ "$TERM" != dumb-emacs-ansi ] && [ "$TERM" != eterm-color ] && [ "$TERM" != dumb ]; then
PS1='\[\e]133;D;\$?\a\e]133;A\a\]'"$PS1"'\[\e]133;B\a\]'
fialias ...='../..' \
....='../../..' \
.....='../../../..' \
......='../../../../..'# if command -v direnv > /dev/null; then
# eval "$(direnv hook bash)"
# fi
fi
#+END_SRCDircolors
#+BEGIN_SRC shell
if [ "$TERM" != dumb ] && [ -n "${SHELL-}" ]; then
if [ "$TERM" = dumb-emacs-ansi ]; then
eval "$(TERM=ansi dircolors)"
else
eval "$(dircolors)"
fi
fi
#+END_SRCThen setup aliases.
#+BEGIN_SRC shell
alias rm='rm -Iv' \
mv='mv -iv' \
cp='cp -iv' \
ln='ln -iv' \
rmdir='rmdir -v' \
mkdir='mkdir -v' \
chown='chown --preserve-root -c' \
chmod='chmod --preserve-root -c' \
chgrp='chgrp --preserve-root -c' \
curl='curl --fail --globoff --location --proto-default https --retry 10 --retry-max-time 10 --no-clobber --parallel' \
wget='curl --remote-name-all --remote-time --fail --globoff --location --proto-default https --retry 10 --retry-max-time 10 --no-clobber --parallel' \
upgrade_bauer='git -C "$HOME/.local/share/bauer" pull && nix-env -if "$HOME/.local/share/bauer"' \
upgrade=upgrade_bauer \
df='df -HT' \
du='du -ch' \
gdb='gdb --quiet --args'open() {
if command -v open > /dev/null; then
command open "$@"
elif command -v xdg-open > /dev/null; then
xdg-open "$@"
else
echo Can’t find open.
exit 1
fi
}take() { mkdir -p $@ && cd ${@:$#} ; }
set -o noclobber -o notify
if [ "$TERM" != "dumb" ]; then
if [ "$TERM" != eterm-color ] && [ "${TERM_PROGRAM-}" != iTerm.app ]; then
alias l='ls -lh --hyperlink=auto --color=auto' \
ls='ls -Fh --hyperlink=auto --color=auto'
else
alias l='ls -lh --color=auto' \
ls='ls -Fh --color=auto'
fi
export CLICOLOR=1
alias grep='grep --color=auto' \
egrep='grep -E --color=auto' \
diff='diff --color=auto' \
rsync='rsync -v --progress --human-readable' \
dd='dd status=progress'
else
alias l='ls -lh' \
ls='ls -Fh' \
egrep='grep -E'
fi
#+END_SRCConfigure INSIDE_EMACS.
#+BEGIN_SRC shell
if [ "${TERM-}" = dumb ] && [ -n "${INSIDE_EMACS-}" ]; then
export TERM=dumb-emacs-ansi
if [ -z "${COLORTERM-}" ]; then
export COLORTERM=1
fi
fi
#+END_SRCLast, we source the HOME profile to make sure it is used by zsh.
#+BEGIN_SRC shell
if [ -f "$HOME/.profile" ] && [ -z "${__BAUER_SOURCED_PROFILE-}" ]; then
__BAUER_SOURCED_PROFILE=1
# shellcheck source=/dev/null
. "$HOME/.profile"
fi
#+END_SRC** =.zshrc=
:PROPERTIES:
:header-args: :tangle zshrc.sh :tangle-mode (identity #o755) :shebang "#!/usr/bin/env bash"
:CUSTOM_ID: zshrc
:END:This is a profile for use with [[http://www.zsh.org][Zsh]]. It is closely based off of [[https://github.com/robbyrussell/oh-my-zsh][oh-my-zsh]].
Setup ZSH profile. First, we just source the global profile.
#+BEGIN_SRC shell
# shellcheck source=/dev/null
. @out@/etc/profile
#+END_SRCHandle dumb options.
#+BEGIN_SRC shell
case "$TERM" in
dumb)
setopt no_zle \
no_prompt_cr \
no_prompt_subst \
no_rcs
PS1='$ '
PROMPT='$ '
whence -w precmd >/dev/null && unfunction precmd
whence -w preexec >/dev/null && unfunction preexec
return
;;
eterm*)
setopt nopromptsp \
single_line_zle
;;
dumb-emacs-ansi)
setopt no_beep
;;
esac
#+END_SRCTurn on ZSH-specific options.
#+BEGIN_SRC shell
setopt always_to_end \
auto_cd \
auto_name_dirs \
auto_pushd \
cdable_vars \
complete_in_word \
correct \
extended_history \
hist_ignore_space \
hist_reduce_blanks \
hist_save_no_dups \
hist_verify \
inc_append_history_time \
interactive_comments \
long_list_jobs \
multios \
no_list_beep \
no_flow_control \
prompt_subst \
pushd_minus \
transient_rpromptalias -g ...='../..' \
....='../../..' \
.....='../../../..' \
......='../../../../..'
#+END_SRCLoad up site-functions in ZSH. Use XDG_DATA_DIRS variable. Then we use this to
initialize completions.#+BEGIN_SRC shell
if [ "$TERM" != dumb-emacs-ansi ] && [[ -o interactive ]]; then
# shellcheck disable=SC2154
for dir in "@completions@/share" ${(s.:.)XDG_DATA_DIRS-}; do
if [ -d "$dir/zsh/site-functions" ]; then
fpath+="$dir/zsh/site-functions"
fi
if [ -d "$dir/zsh/vendor-completions" ]; then
fpath+="$dir/zsh/vendor-completions"
fi
doneautoload -U compinit
if ! [ -f "$HOME/.zcompdump" ] || [ "$(@coreutils@/bin/date +%j)" != "$(@coreutils@/bin/date +%j -r "$HOME/.zcompdump")" ]; then
compinit -i -d "$HOME/.zcompdump"
else
compinit -i -d "$HOME/.zcompdump" -C
fi
fi
#+END_SRCBind to emacs...
#+BEGIN_SRC shell
bindkey -eautoload -U tetris
zle -N tetrisautoload -U tetriscurses
zle -N tetriscursesautoload -U bracketed-paste-magic
zle -N bracketed-paste bracketed-paste-magicbindkey ' ' magic-space
bindkey ';5D' backward-word
bindkey ';5C' forward-word
bindkey '^[[1;9D' backward-word
bindkey '^[[1;9C' forward-wordautoload -U zmv
autoload -U zargsautoload -U url-quote-magic
zle -N self-insert url-quote-magicautoload -Uz up-line-or-beginning-search down-line-or-beginning-search
zle -N up-line-or-beginning-search
zle -N down-line-or-beginning-searchbindkey '^[[A' up-line-or-beginning-search
bindkey '^[OA' up-line-or-beginning-search
bindkey '^[[B' down-line-or-beginning-search
bindkey '^[OB' down-line-or-beginning-searchautoload -U edit-command-line
zle -N edit-command-line
bindkey "^X^E" edit-command-linebindkey "^[m" copy-prev-shell-word
bindkey '^[w' kill-region
bindkey '^[[Z' reverse-menu-complete
bindkey "^S" history-incremental-search-forward# unalias run-help
autoload -U run-help
alias help=run-help
bindkey "^[h" run-help
#+END_SRCZstyle completions.
#+BEGIN_SRC shell
zstyle ':completion:*' ignored-patterns '*~' '_*' '*\\\#*\\\#'
zstyle ':completion:*:*:*:users' ignored-patterns 'nixbld*' '_*' 'systemd-*' \
apache avahi daemon dbus messagebus nobody nginx nscd polkituser rpc sshd
zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS}
zstyle ':completion:*' insert-tab pending
zstyle ':completion:*' matcher-list 'm:{a-zA-Z-_}={A-Za-z_-}' 'r:|=*' 'l:|=* r:|=*'
zstyle ':completion:*' special-dirs yes
zstyle ':completion:*' squeeze-slashes yes
zstyle ':completion:*' expand yes
zstyle ':completion:*:match:*' original only
zstyle ':completion:*:approximate:*' max-errors 1 numeric
zstyle ':completion:*:*:*:*:*' menu select
zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#) ([0-9a-z-]#)*=01;34=0=01'
zstyle ':completion:*:history-words' list no
zstyle ':completion:*:history-words' remove-all-dups yes
zstyle ':completion:*:history-words' stop yes
zstyle ':completion:*:rm:*' file-patterns '*:all-files'
zstyle ':completion::complete:*' cache-path "$HOME/.zcompcache"
zstyle ':completion::complete:*' use-cache yes
zstyle '*' single-ignored show
zstyle -e ':completion:*:(ssh|scp|sftp|rsh|rsync):hosts' hosts 'reply=(${=${${(f)"$(cat {/etc/ssh_,~/.ssh/known_}hosts(|2)(N) /dev/null)"}%%[# ]*}//,/ })'
#+END_SRCTurn on prompt with colors.
#+BEGIN_SRC shell
PROMPT=
if [ -n "${SSH_CONNECTION-}" ]; then
PROMPT+='%F{blue}'
if [ "$TERM" = dumb-emacs-ansi ]; then
PROMPT+='/ssh:'
else
PROMPT+='ssh://'
fi
PROMPT+='%n@'echo "$SSH_CONNECTION" | read client_ip client_port server_ip server_port
if [[ "$server_ip" =~ fe80::* ]]; then
server_ip="$(echo "$server_ip" | sed 's,%[a-z0-9]*$,,')"
fiif [[ "$client_ip" =~ fe80::* ]]; then
client_ip="$(echo "$client_ip" | sed 's,%[a-z0-9]*$,,')"
fi# localhost
if [ "$server_ip" = 127.0.0.1 ] || [ "$server_ip" = ::1 ] || [ "$server_ip" = "$client_ip" ]; then
PROMPT+=localhost
elif [ "$TERM" = dumb-emacs-ansi ]; then
PROMPT+="${HOST-}"
else
PROMPT+=%M
fiif [ "$server_port" -ne 22 ]; then
if [ "$TERM" = dumb-emacs-ansi ]; then
PROMPT+="#"
else
PROMPT+=":"
fi
PROMPT+="$server_port"
fiunset client_ip client_port server_ip server_port hostname
if [ "$TERM" = dumb-emacs-ansi ]; then
PROMPT+=':%/%f'
else
PROMPT+='%F{cyan}%/%f'
fi
else
PROMPT+='%(!.%F{red}%B.%F{blue})%n%b%F{blue}@%m%f:%F{cyan}%4~%f'
fiPROMPT+=' %(?..%F{red}[%?]%f )%(1j.%F{green}[%j]%f .)%F{yellow}%(!.#.$)%f '
unset RPS1
HISTFILE="$HOME/.zsh_history"
REPORTTIME=4
#+END_SRCVterm
#+BEGIN_SRC shell
vterm_cmd() {
local vterm_elisp=""
while [ $# -gt 0 ]; do
vterm_elisp+="$(printf '"%s" ' "$(printf "%s" "$1" | sed -e 's|\\|\\\\|g' -e 's|"|\\"|g')")"
shift
done
printf '\e]51;E%s\e\\' "$vterm_elisp"
}update_vterm_cwd () {
printf '\e]51;A%s\e\\' "$(whoami)@$(hostname):$(pwd)"
}if [[ "$INSIDE_EMACS" = vterm ]]; then
alias clear='printf "\e]51;Evterm-clear-scrollback\e\\"; tput clear'
chpwd_functions+=(update_vterm_cwd)
update_vterm_cwd
fi
#+END_SRCUpdate eterm cwds.
#+BEGIN_SRC shell
update_eterm_cwd () {
printf '\eAnSiTc %s\n' "$PWD"
printf '\eAnSiTh %s\n' "${HOST-}"
printf '\eAnSiTu %s\n' "${USER-$LOGNAME}"
}if [ "$TERM" = "eterm-color" ]; then
chpwd_functions+=(update_eterm_cwd)
update_eterm_cwd
fi
#+END_SRCSetup Apple Terminal so that CWD is shown.
#+BEGIN_SRC shell
update_apple_terminal_cwd () {
printf '\e]7;%s\a' "file://${HOST-}${PWD// /%20}"
}if [ "${TERM_PROGRAM-}" = Apple_Terminal ] \
&& [ -z "${INSIDE_EMACS-}" ] \
&& [ -n "${TERM_SESSION_ID-}" ]; then
chpwd_functions+=(update_apple_terminal_cwd)
update_apple_terminal_cwd
fi
#+END_SRCiTerm2
#+BEGIN_SRC shell
if { [ "${TERM_PROGRAM-}" = iTerm.app ] || [ -n "${SSH_CONNECTION-}" ]; } && [ "$TERM" != dumb-emacs-ansi ] && [ "$TERM" != eterm-color ] && [ "$TERM" != dumb ] && [[ -o interactive ]]; then
iterm2_before_cmd_executes() {
printf '\e]133;C;\r\a'
}
iterm2_print_state_data() {
printf '\e]1337;RemoteHost=%s@%s\a' "${USER-$LOGNAME}" "${HOST-}"
printf '\e]1337;CurrentDir=%s\a' "$PWD"
}
iterm2_after_cmd_executes() {
printf '\e]133;D;%s\a' "$?"
iterm2_print_state_data
}
PROMPT=$'%{\e]133;A\a%}'"$PROMPT"$'%{\e]133;B\a%}'
precmd_functions+=(iterm2_after_cmd_executes)
preexec_functions+=(iterm2_before_cmd_executes)
iterm2_print_state_data
printf '\e]1337;ShellIntegrationVersion=14;shell=zsh\a'
fi
#+END_SRCdirenv
#+BEGIN_SRC shell
# if command -v direnv > /dev/null; then
# eval "$(direnv hook zsh)"
# fi
#+END_SRC** =etc-profile.sh=
:PROPERTIES:
:header-args: :tangle etc-profile.sh :tangle-mode (identity #o755) :shebang "#!/usr/bin/env sh"
:END:This just sources everything in the =/etc/profile.d= dir. =PREFIX= can be
used to reference the Nix output dir.#+BEGIN_SRC shell
PREFIX=@out@
#+END_SRCThis will source everything in =/etc/profile.d=. We skip =nix-daemon.sh= because
it is put in the global profile, and sets values incorrectly when we are in
single user mode. We need @out@ to come after $HOME so that it gets the last
word in these settings.#+BEGIN_SRC shell
if [ -n "${BASH_VERSION-}" ]; then
shopt -s nullglob
fi
for i in "$HOME"/.nix-profile/etc/profile.d/*.sh @out@/etc/profile.d/*.sh; do
if [ -r "$i" ] && [ "$(@coreutils@/bin/basename "$i")" != nix-daemon.sh ]; then
# shellcheck source=/dev/null
. "$i"
fi
done
#+END_SRC* Bootstrapping
:PROPERTIES:
:CUSTOM_ID: bootstrap
:END:** =site-paths.el.in=
:PROPERTIES:
:header-args: :tangle site-paths.el.in :comments link
:CUSTOM_ID: site-paths
:END:This file provides site-specific paths. However, it must be substituted in Nix
before we can actually run it in Emacs. To prevent Emacs from trying to run
this, I’ve set the syntax to =text=.#+BEGIN_SRC emacs-lisp
(require 'set-defaults)
(require 'subr-x)
#+END_SRC=output-directory= points to the nix-profile directory created by Nix.
Ideally, this could point to a Nix store path, but the order of building
means that we don’t know this until too late.#+BEGIN_SRC emacs-lisp
(defvar output-directory
(expand-file-name ".nix-profile" (getenv "HOME")))
(defvar zsh-command
(expand-file-name "bin/zsh" output-directory))
#+END_SRCSetup =exec-path=.
#+BEGIN_SRC emacs-lisp
(setq exec-path
(append `(,(expand-file-name "bin" output-directory)
"@bins@/bin")
(split-string "@PATH@" ":")
exec-path))
#+END_SRCSetup =man-path=.
#+BEGIN_SRC emacs-lisp
(defvar man-path (append
`(""
,(expand-file-name "share/man" output-directory)
"@manpages@/share/man")
(split-string "@MANPATH@" ":")))
#+END_SRCSetup history and stuff. This gets messy, but we need to make sure these are set
otherwise zsh will default to 500 and not save at all!#+BEGIN_SRC emacs-lisp
(defun bauer-set-history-file (sym val)
"Setter for history-file."
(setenv "HISTFILE" (eval val))
(custom-set-default sym val))(defcustom history-file (expand-file-name ".zsh_history" (getenv "HOME"))
"The history file to use."
:type 'string
:group 'bauer
:set 'bauer-set-history-file)(defvar eshell-history-size)
(defvar comint-input-ring-size)(defun bauer-set-history-size (sym val)
"Setter for history-size."
(setenv "HISTSIZE" (format "%i" val))
(setenv "SAVEHIST" (format "%i" val))
(setq eshell-history-size val
comint-input-ring-size val)
(custom-set-default sym val))(defcustom history-size 50000
"The history size to use."
:type 'integer
:group 'bauer
:set 'bauer-set-history-size)
#+END_SRCSet some more misc. vars.
#+BEGIN_SRC emacs-lisp
(append-envs ":"
`("MANPATH" ,man-path))
(prepend-envs ":"
`("PATH" ,exec-path))
(set-envs
`("XDG_DATA_DIRS" ,(string-join `(,(expand-file-name "share"
output-directory)
"@XDG_DATA_DIRS@")
":"))
`("TERMINFO_DIRS" "@TERMINFO_DIRS@")
`("INFOPATH" ,(string-join `(,(expand-file-name "share/info"
output-directory)
"@INFOPATH@")
":")))
#+END_SRCSet paths provided by Nix,
#+BEGIN_SRC emacs-lisp
(defvar gnutls-program "gnutls")
(defvar pdf2dsc-command "pdf2dsc")
(defvar dvips-command "dvips")
(defvar dvipng-command "dvipng")
(defvar xetex-command "xetex")
(defvar xelatex-command "xelatex")
(defvar luatex-command "luatex")
(defvar makeinfo-command "makeinfo")
(defvar LaTeX-command "LaTeX")
(defvar pdftex-command "pdftex")
(defvar context-command "context")
(defvar bibtex-command "bibtex")
(defvar makeindex-command "makeindex")
(defvar dvipdfmx-command "dvipdfmx")
(defvar ag-executable "ag")
(defvar ripgrep-executable "ripgrep")
(defvar lacheck-command "lacheck")
(defvar chktex-command "chktex")
(defvar ps2pdf-command "ps2pdf")(set-paths
'(company-cmake-executable "@cmake@/bin/cmake")
'(doc-view-dvipdf-program "@ghostscript@/bin/dvipdf")
'(calc-gnuplot-name "@gnuplot@/bin/gnuplot")
'(gnuplot-program "@gnuplot@/bin/gnuplot")
'(doc-view-ps2pdf-program "@ghostscript@/bin/ps2pdf")
'(epg-gpg-program "@gpg@/bin/gpg")
'(epg-gpgconf-program "@gpg@/bin/gpgconf")
'(epg-gpgsm-program "@gpg@/bin/gpgsm")
'(haskell-check-command "@hlint@/bin/hlint")
'(haskell-hoogle-command "@hoogle@/bin/hoogle")
'(haskell-hasktags-path "@hasktags@/bin/hasktags")
'(irony-cmake-executable "@cmake@/bin/cmake")
'(jka-compr-info-compress-program "@ncompress@/bin/compress")
'(jka-compr-info-uncompress-program "@ncompress@/bin/uncompress")
'(markdown-command "@markdown2@/bin/markdown2")
'(nethack-executable "@nethack@/bin/nethack")
'(org-pandoc-command "@pandoc@/bin/pandoc")
'(pandoc-binary "@pandoc@/bin/pandoc")
'(ripgrep-executable "@ripgrep@/bin/rg")
'(rtags-path "@rtags@/bin")
'(sql-ingres-program "@parallel@/bin/sql")
'(sql-interbase-program "@unixODBC@/bin/isql")
'(sql-ms-program "@freetds@/bin/osql")
'(sql-postgres-program "@freetds@/bin/osql")
'(sql-sqlite-program "@sqliteInteractive@/bin/sqlite3")
'(gnutls-program "@gnutls@/bin/gnutls-cli")
'(pdf2dsc-command "@ghostscript@/bin/pdf2dsc")
'(preview-gs-command "@texlive@/bin/rungs")
'(TeX-command "@texlive@/bin/tex")
'(LaTeX-command "@texlive@/bin/latex")
'(latex-run-command "@texlive@/bin/latex")
'(tex-run-command "@texlive@/bin/tex")
'(luatex-command "@texlive@/bin/luatex")
'(xetex-command "@texlive@/bin/xetex")
'(xelatex-command "@texlive@/bin/xelatex")
'(makeinfo-command "@texinfoInteractive@/bin/makeinfo")
'(pdftex-command "@texlive@/bin/pdftex")
'(context-command "@texlive@/bin/context")
'(bibtex-command "@texlive@/bin/bibtex")
'(dvipdfmx-command "@texlive@/bin/dvipdfmx")
'(makeindex-command "@texlive@/bin/makeindex")
'(chktex-command "@texlive@/bin/chktex")
'(lacheck-command "@texlive@/bin/lacheck")
'(dvipdfmx-command "@texlive@/bin/dvipdfmx")
'(dvips-command "@texlive@/bin/dvips")
'(dvipng-command "@texlive@/bin/dvipng")
'(ps2pdf-command "@ghostscript@/bin/ps2pdf")
'(ag-executable "@silver-searcher@/bin/ag")
'(gud-gdb-command-name "@gdb@/bin/gdb")
'(coq-prog-name "@coq@/bin/coqtop")
'(coq-dependency-analyzer "@coq@/bin/coqdep")
'(coq-compiler "@coq@/bin/coqc")
)
#+END_SRCSet some defaults that depend on the path variables below,
#+BEGIN_SRC emacs-lisp
(set-defaults
'(imap-ssl-program `(,(concat gnutls-program " --tofu -p %p %s")))
'(tls-program (concat gnutls-program " --tofu -p %p %h"))
'(preview-pdf2dsc-command
(concat pdf2dsc-command " %s.pdf %m/preview.dsc"))
'(preview-dvips-command
(concat dvips-command " -Pwww %d -o %m/preview.ps"))
'(preview-fast-dvips-command
(concat dvips-command " -Pwww %d -o %m/preview.ps"))
'(preview-dvipng-command
(concat dvipng-command
" -picky -noghostscript %d -o \"%m/prev%%03d.png\""))
'(TeX-engine-alist
`((xetex "XeTeX"
xetex-command
xelatex-command
xetex-command)
(luatex "LuaTeX" luatex-command
,(concat luatex-command " --jobname=%s")
luatex-command)))
'(TeX-command-list
`(("TeX"
,(concat "%(PDF)%(tex) %(file-line-error) "
"%(extraopts) %`%S%(PDFout)%(mode)%' %t")
TeX-run-TeX nil
(plain-tex-mode ams-tex-mode texinfo-mode)
:help "Run plain TeX")
("LaTeX" "%`%l%(mode)%' %t" TeX-run-TeX nil
(latex-mode doctex-mode)
:help "Run LaTeX")
("Makeinfo" ,(concat makeinfo-command
" %(extraopts) %t")
TeX-run-compile nil
(texinfo-mode)
:help "Run Makeinfo with Info output")
("Makeinfo HTML"
,(concat makeinfo-command
" %(extraopts) --html %t")
TeX-run-compile nil
(texinfo-mode)
:help "Run Makeinfo with HTML output")
("AmSTeX"
,(concat pdftex-command
" %(PDFout) %(extraopts) %`%S%(mode)%' %t")
TeX-run-TeX nil
(ams-tex-mode)
:help "Run AMSTeX")
("ConTeXt"
,(concat context-command
" --once --texutil %(extraopts) %(execopts)%t")
TeX-run-TeX nil
(context-mode)
:help "Run ConTeXt once")
("ConTeXt Full"
,(concat context-command
" %(extraopts) %(execopts)%t")
TeX-run-TeX nil
(context-mode)
:help "Run ConTeXt until completion")
("BibTeX" ,(concat bibtex-command " %s")
TeX-run-BibTeX nil t :help "Run BibTeX")
("Biber" "biber %s" TeX-run-Biber nil t
:help "Run Biber")
("View" "%V" TeX-run-discard-or-function t t
:help "Run Viewer")
("Print" "%p" TeX-run-command t t
:help "Print the file")
("Queue" "%q" TeX-run-background nil t
:help "View the printer queue"
:visible TeX-queue-command)
("File" ,(concat dvips-command " %d -o %f ")
TeX-run-dvips t t :help "Generate PostScript file")
("Dvips" ,(concat dvips-command " %d -o %f ")
TeX-run-dvips nil t
:help "Convert DVI file to PostScript")
("Dvipdfmx" ,(concat dvipdfmx-command " %d")
TeX-run-dvipdfmx nil t
:help "Convert DVI file to PDF with dvipdfmx")
("Ps2pdf" ,(concat ps2pdf-command " %f")
TeX-run-ps2pdf nil t
:help "Convert PostScript file to PDF")
("Index" ,(concat makeindex-command " %s")
TeX-run-index nil t
:help "Run makeindex to create index file")
("upMendex" "upmendex %s"
TeX-run-index t t
:help "Run mendex to create index file")
("Xindy" "xindy %s"
TeX-run-command nil t
:help "Run xindy to create index file")
("Check" ,(concat lacheck-command " %s")
TeX-run-compile nil
(latex-mode)
:help "Check LaTeX file for correctness")
("ChkTeX" ,(concat chktex-command " -v6 %s")
TeX-run-compile nil
(latex-mode)
:help "Check LaTeX file for common mistakes")
("Spell" "(TeX-ispell-document \"\")"
TeX-run-function nil t
:help "Spell-check the document")
("Clean" "TeX-clean"
TeX-run-function nil t
:help "Delete generated intermediate files")
("Clean All" "(TeX-clean t)" TeX-run-function nil t
:help
"Delete generated intermediate and output files")
("Other" "" TeX-run-command t t
:help "Run an arbitrary command")))
'(counsel-grep-base-command
(concat ripgrep-executable
" -i -M 120 --no-heading --line-number --color never '%s' %s"))
'(counsel-rg-base-command
(concat ripgrep-executable
" -i --no-heading --line-number %s ."))
'(counsel-ag-base-command
(concat ag-executable " --nocolor --nogroup %s"))
'(Info-directory-list '("@infopages@/share/info"))
'(tramp-remote-path
`(tramp-own-remote-path
"/run/current-system/sw/bin"
tramp-default-remote-path
"/bin"
"/usr/bin"
"/sbin"
"/usr/sbin"
"/usr/local/bin"
"/usr/local/sbin"
"/opt/bin"
"/opt/sbin"
,(expand-file-name "bin" output-directory)))
'(woman-manpath man-path)
'(Man-header-file-path
`("@sysheaders@/include"
"/usr/include"
"/usr/local/include"))
'(ffap-c-path
'("@sysheaders@/include"
"/usr/include"
"/usr/local/include"))
'(rng-schema-locating-files
`("schemas.xml"
"@schemas@"
,(expand-file-name
"schema/schemas.xml" data-directory)))
)
#+END_SRC** =bootstrap.sh=
:PROPERTIES:
:header-args: :tangle bootstrap.sh :shebang "#!/usr/bin/env sh"
:END:This is the bootstrap script that is mentioned above. We use it to install the
IDE. It ensures Nix is installed as well as that the Git repo is available &
up-to-date. Note, this script will automatically install Nix and Git for you if
they are not already installed! Use it at your own risk.Install Nix if it isn’t already.
#+BEGIN_SRC shell
{set -e
echo This script will install Nix and Git
echo if they are not already installed.if ! command -v nix-env >/dev/null 2>&1; then
case "$(uname)" in
Darwin) curl -L -s https://nixos.org/nix/install | sh -s - --darwin-use-unencrypted-nix-store-volume --daemon ;;
Linux) curl -L -s https://nixos.org/nix/install | sh -s - --daemon ;;
*) curl -L -s https://nixos.org/nix/install | sh -s ;;
esac
[ -f $HOME/.profile ] && . $HOME/.profile
PATH="$HOME/.nix-profile/bin${PATH-+:$PATH}" # in case above didn't work
fi
#+END_SRCCheck for Git & SSH. Install if it’s not there.
#+BEGIN_SRC shell
if ! command -v git >/dev/null 2>&1 || \
{ [ "$(uname)" = Darwin ] && \
[ "$(command -v git)" = /usr/bin/git ] &&
! xcode-select -p >/dev/null 2>&1; }; then
nix-env -iA nixpkgs.git 2>/dev/null || nix-env -iA nixos.git || nix --experimental-features 'nix-command flakes' profile install nixpkgs#git || nix-env -iA git -f https://nixos.org/channels/nixos-unstable/nixexprs.tar.xz
fiif ! command -v ssh >/dev/null 2>&1; then
nix-env -iA nixpkgs.openssh 2>/dev/null || nix-env -iA nixos.openssh || nix --experimental-features 'nix-command flakes' profile install nixpkgs#openssh || nix-env -iA openssh -f https://nixos.org/channels/nixos-unstable/nixexprs.tar.xz
fi
#+END_SRCIf we are in a Git repo already, we’ll pull to get latest updates.
#+BEGIN_SRC shell
if [ -d .git ]; then
git pull origin master || true
fi
#+END_SRCIf we can’t find default.nix then we’ll clone from GitHub. This will be stored in
=~/.local/share/bauer=.#+BEGIN_SRC shell
if ! [ -f default.nix ]; then
repo_dir=$HOME/.local/share/bauer
mkdir -p $(dirname $repo_dir)
if ! [ -d $repo_dir/.git ]; then
git clone https://github.com/matthewbauer/bauer $repo_dir
else
git -C $repo_dir pull
fi
cd $repo_dir
fi
#+END_SRCPull private data, which may contain some binary caches we can use.
#+BEGIN_SRC shell
if [ -z "${GIST_ID-}" ] && [ -n "$1" ] && (echo "$1" | grep -q "^[0-9a-f]\{5,40\}$"); then
GIST_ID="$1"
fiif [ -n "${GIST_ID-}" ]; then
URL=ssh://[email protected]/"$GIST_ID".git
fiif [ -z "${URL-}" ]; then
case "${USER-}" in
mbauer|matthewbauer) URL=ssh://[email protected]/matthewbauer/dotfiles.git ;;
esac
fiif [ -n "${URL-}" ]; then
echo Found Gist commit "$1", cloning now.
./gist-unpack.sh "$URL"
fi
#+END_SRCInstall our new derivation.
#+BEGIN_SRC shell
nix-env -if . || nix --experimental-features 'nix-command flakes' profile install
#+END_SRCSource the profile.
#+BEGIN_SRC shell
if ! [ -f "$HOME/.profile" ] || ! grep -q '\(source\|\.\) "\?$HOME/.nix-profile/etc/profile"\?' "$HOME/.profile"; then
echo '[ -f "$HOME/.nix-profile/etc/profile" ] && . "$HOME/.nix-profile/etc/profile"' >> "$HOME/.profile"
fiif ! [ -f "$HOME/.zshenv" ] || ! grep -q '\(source\|\.\) "\?$HOME/.nix-profile/etc/zshrc"\?' "$HOME/.zshenv"; then
echo '[ -f "$HOME/.nix-profile/etc/zshrc" ] && source "$HOME/.nix-profile/etc/zshrc"' >> "$HOME/.zshenv"
fiif ! [ -f "$HOME/.bashrc" ] || ! grep -q '\(source\|\.\) "\?$HOME/.nix-profile/etc/profile"\?' "$HOME/.bashrc"; then
echo '[ -f "$HOME/.nix-profile/etc/profile" ] && source "$HOME/.nix-profile/etc/profile"' >> "$HOME/.bashrc"
fiif ! [ -f "$HOME/.bash_profile" ] || ! grep -q '\(source\|\.\) "\?$HOME/.nix-profile/etc/profile"\?' "$HOME/.bash_profile"; then
echo '[ -f "$HOME/.nix-profile/etc/profile" ] && source "$HOME/.nix-profile/etc/profile"' >> "$HOME/.bash_profile"
fiecho To use bauer correctly, you must first source the profile.
echo
echo To do this, just run:
echo $ . $HOME/.nix-profile/etc/profileif [ -n "${ZSH_NAME-}" ]; then
echo $ . $HOME/.nix-profile/etc/zshrc
fiecho From you command line
echo You can also run either emacs or zsh to launch the environment}
#+END_SRC** =runemacs.sh=
:PROPERTIES:
:header-args: :tangle runemacs.sh :tangle-mode (identity #o755) :shebang "#!/usr/bin/env sh"
:CUSTOM_ID: runemacs
:END:Cross-platform script to execute the app. Uses =open= on macOS to get the
graphical version of Emacs.#+BEGIN_SRC shell :padline no
case $(uname) in
Darwin)
open @emacs@/Applications/Emacs.app
;;
,*)
@emacs@/bin/emacs
;;
esac
#+END_SRC
* Building
:PROPERTIES:
:CUSTOM_ID: build
:END:No tests currently exists, but some things are important to keep in mind. Some
high level goals that should be verified:- Keep closure size under 1GB (currently 800MB)
- Don’t assume users’ language, locales, time zone, etc.
- Don’t hard code any personal information
- Everything should run on Linux, and macOS.
- Nix usage is optional (see Withou Nix above).The editor and environment should be general purpose and portable
** =default.nix=: the tangler
:PROPERTIES:
:header-args: :tangle default.nix
:END:Here we being the building process. There are a couple of stages to this not
normally seen in configurations. The first part is the ‘default.nix’ which will
tangle the README.org file. This should be the only file that has to live
outside of this ORG document. This is meant to be very minimal so that once we
tangle README.org, we can get a working .nix file to import from using Nix’s
import from derivation.The function header defines some arguments you can pass. It will use the latest
nixpkgs-unstable from nixos.org. If you want to change this, you can pass in
your own pkgs variable.The small argument is a bit of a hack. Nix will only recognize args that have
been explicitly listed so we cannot rely on the ‘...’ syntax to pick up random
args.#+BEGIN_SRC nix :padline no
# -*- mode: nix; coding: utf-8; -*-
{ nixpkgs-url ?
"https://nixos.org/channels/nixpkgs-unstable/nixexprs.tar.xz"
, system ? builtins.currentSystem
, crossSystem ? null
, config ? {}
, overlays ? []
, pkgs ? import (builtins.fetchTarball nixpkgs-url) {
inherit crossSystem system config overlays;
}
, small ? true
, ...
} @ args:
#+END_SRCNow let's tangle README.org... This uses ORG’s babel functionality to generate a
.nix file. The .nix file is renamed to default.nix to replace this script.#+BEGIN_SRC nix
let
ensure = f: n: if builtins.pathExists f then f
else builtins.fetchurl
"https://matthewbauer.us/bauer/${n}";
in import (pkgs.runCommand "README" {
buildInputs = with pkgs; [ emacs git ];
} (''
install -D ${ensure ./README.org "README.org"} \
$out/README.org
cd $out
'' + pkgs.lib.optionalString (builtins.pathExists ./site-lisp) ''
cp -r ${./site-lisp} site-lisp
'' + ''
emacs --batch --quick \
-l ob-tangle \
--eval "(org-babel-tangle-file \"README.org\")" > /dev/null
cp bauer.nix default.nix
'')) { inherit ensure pkgs small system; }
#+END_SRC** =bauer.nix=: the build script
:PROPERTIES:
:header-args: :tangle bauer.nix
:END:Here we have the actual Nix file that will build our Emacs configuration. Again
we have some options that can be provided...#+BEGIN_SRC nix :padline no
# -*- mode: nix; coding: utf-8; -*-
{ system ? builtins.currentSystem
, crossSystem ? null
, config ? {}
, overlays ? []
, pkgs ? import { inherit crossSystem system config overlays; }
, evalPkgs ? pkgs
, ensure ? f: n: f
, small ? pkgs.config.bauer.small or true
, emacs-overlay ? { overlay = import (builtins.fetchTarball "https://github.com/nix-community/emacs-overlay/archive/master.tar.gz"); }
, ... }: let
inherit (pkgs) stdenv lib runCommand buildEnv config writeText;
inherit (stdenv) hostPlatform;
big = !small;
allowUnfree = config.allowUnfree or false;
#+END_SRCBy default, you will be building the "small" version which is about 500MB. The
normal version has a huge closure totalling in at over 5GB. This is extremely
useful for integrations with developer tools and manuals, but also inconvenient
when wanting to start running for the first time. If you want to enable the big
version, you can just do ‘config.bauer.small = false’ in your
~/.config/nixpkgs/config.nix file.
Alternatively, if you /want/ the small version, just do
‘nixpkgs.config.bauer.small = true’.Next we start defining some packages. R is one of the simpler ones right now,
so let's start with that.#+BEGIN_SRC nix
rEnv = pkgs.rWrapper.override {
packages = with pkgs.rPackages; [
RCurl
];
};
#+END_SRCHere we define our package set. This will just give us access to all of the
Emacs packages defined in Nixpkgs.We also define our Emacs version to use. Mitsuharo’s Emacs package is much
better for MacOS so we use that when we’re on Darwin systems. Otherwise, just
default to ‘emacs’ which should be the latest (Nixpkgs-unstable has version 26.1
currently).#+BEGIN_SRC nix
customEmacsPackages = let
pkgs' = import pkgs.path {
inherit crossSystem system config;
overlays = overlays ++ [ emacs-overlay.overlay ];
};
in pkgs'.emacsPackagesFor (if pkgs'.stdenv.hostPlatform.isDarwin then pkgs'.emacsMacport else pkgs'.emacs-git);
#+END_SRCTex live provides some LaTeX commads for us.
#+BEGIN_SRC nix
myTex = pkgs.texlive.combine {
inherit (pkgs.texlive) xetex setspace
fontspec chktex enumitem xifthen
ifmtarg filehook wrapfig inconsolata
upquote minted lastpage collection-basic
collection-binextra collection-context
collection-fontsrecommended collection-fontutils
collection-langenglish collection-latex
collection-latexrecommended collection-luatex
collection-metapost collection-texworks
collection-xetex capt-of ulem hyperref titlesec
beamer;
};
#+END_SRC*** Emacs configuration
Here, we start building up the site-paths.el file. This does a simple
substitution of all the attributes set.#+BEGIN_SRC nix
site-paths = runCommand "site-paths.el" (with pkgs; ({
inherit ripgrep silver-searcher coreutils;
inherit manpages sysheaders sysframeworks
schemas bins infopages;
inherit MANPATH PATH XDG_DATA_DIRS INFOPATH TERMINFO_DIRS;
} // (lib.optionalAttrs big {
gpg = gnupg1compat;
inherit
pandoc ghostscript
sqliteInteractive freetds
parallel unixODBC ncompress
texinfoInteractive gnuplot
gdb coq rtags gnutls;
inherit (haskellPackages) hoogle hlint;
texlive = myTex;
markdown2 = pythonPackages.markdown2;
}))) ''
substituteAll ${./site-paths.el.in} $out
substituteInPlace $out \
--subst-var-by PATH ${PATH} \
--subst-var-by INFOPATH ${INFOPATH} \
--subst-var-by MANPATH ${MANPATH} \
--subst-var-by XDG_DATA_DIRS ${XDG_DATA_DIRS} \
--subst-var-by TERMINFO_DIRS ${TERMINFO_DIRS}
'';
#+END_SRCEmacs building can be divided into phases. Each phase will run through the Elisp
once.**** Phase 1: picking up dependencies
myEmacsPackages gets a listing of all of the packages that are needed by the
Emacs configuration. use-package-list generates this list automatically.#+BEGIN_SRC nix
package-list =
evalPkgs.runCommand "package-list" {
buildInputs = [ evalPkgs.emacs ];
} ''
#+END_SRC#+BEGIN_SRC nix
emacs --batch --quick \
-L ${evalPkgs.emacsPackages.use-package
}/share/emacs/site-lisp/elpa/use-package-* \
-L ${evalPkgs.emacsPackages.delight
}/share/emacs/site-lisp/elpa/delight-* \
-L ${evalPkgs.emacsPackages.bind-key
}/share/emacs/site-lisp/elpa/bind-key-* \
-l ${ensure ./site-lisp/set-defaults.el
"site-lisp/set-defaults.el"} \
-l ${ensure ./site-lisp/bauer.el
"site-lisp/bauer.el"} \
-l ${ensure ./site-lisp/use-package-list.el
"site-lisp/use-package-list.el"} \
--eval "(use-package-list \"${./README.el}\")" \
> $out 2>/dev/null
#+END_SRC#+BEGIN_SRC nix
'';
#+END_SRC#+BEGIN_SRC nix
myEmacsPackages' = builtins.fromJSON
(builtins.readFile package-list);
#+END_SRC**** Phase 2: byte compiling
#+BEGIN_SRC nix
default = runCommand "bauer-emacs" {
buildInputs = [ pkgs.emacs pkgs.git ];
} ''
#+END_SRCInstall our lisp files. Many of these should be released into MELPA but don’t
have the time to do it currently.#+BEGIN_SRC nix
install -D ${site-paths} \
$out/share/emacs/site-lisp/site-paths.el
install -D ${./README.el} \
$out/share/emacs/site-lisp/default.el
install -D ${ensure ./site-lisp/em-dired.el
"site-lisp/em-dired.el"} \
$out/share/emacs/site-lisp/em-dired.el
install -D ${ensure ./site-lisp/dired-column.el
"site-lisp/dired-column.el"} \
$out/share/emacs/site-lisp/dired-column.el
install -D ${ensure ./site-lisp/macho-mode.el
"site-lisp/macho-mode.el"} \
$out/share/emacs/site-lisp/macho-mode.el
install -D ${ensure ./site-lisp/nethack.el
"site-lisp/nethack.el"} \
$out/share/emacs/site-lisp/nethack.el
install -D ${ensure ./site-lisp/set-defaults.el
"site-lisp/set-defaults.el"} \
$out/share/emacs/site-lisp/set-defaults.el
install -D ${ensure ./site-lisp/pcomplete-extra.el
"site-lisp/pcomplete-extra.el"} \
$out/share/emacs/site-lisp/pcomplete-extra.el
install -D ${ensure ./site-lisp/installer.el
"site-lisp/installer.el"} \
$out/share/emacs/site-lisp/installer.el
install -D ${ensure ./site-lisp/restart-emacs.el
"site-lisp/restart-emacs.el"} \
$out/share/emacs/site-lisp/restart-emacs.el
install -D ${ensure ./site-lisp/use-package-list.el
"site-lisp/use-package-list.el"} \
$out/share/emacs/site-lisp/use-package-list.el
install -D ${ensure ./site-lisp/bauer.el
"site-lisp/bauer.el"} \
$out/share/emacs/site-lisp/bauer.el
install -D ${ensure ./site-lisp/comint-hyperlink.el
"site-lisp/comint-hyperlink.el"} \
$out/share/emacs/site-lisp/comint-hyperlink.el
install -D ${ensure ./site-lisp/persistent-mode.el
"site-lisp/persistent-mode.el"} \
$out/share/emacs/site-lisp/persistent-mode.el
install -D ${ensure ./site-lisp/envrc.el
"site-lisp/envrc.el"} \
$out/share/emacs/site-lisp/envrc.el
'';
#+END_SRC**** Phase 3: wrapping into Emacs
This phase wraps the byte compiled lisp into an Emacs binary. Each package
listed in =use-package= above is pulled into the closure.requiredPackages is a function that takes two arguments.
#+BEGIN_SRC nix
requiredPackages = epkgs: map (x:
if builtins.hasAttr x epkgs
then builtins.getAttr x epkgs
else if builtins.hasAttr x pkgs.emacsPackages
then builtins.getAttr x pkgs.emacsPackages
else abort "no attribute found for use-package ${x}");
#+END_SRCNow we build our Emacs distribution.
TODO: use dump-emacs here to speed up config.
#+BEGIN_SRC nix
myEmacsPackages = buildEnv {
name = "emacs-packages-env";
paths = (requiredPackages customEmacsPackages myEmacsPackages')
++ [ customEmacsPackages.use-package customEmacsPackages.delight ];
};myEmacs = customEmacsPackages.emacsWithPackages (epkgs:
(requiredPackages epkgs myEmacsPackages')
++ [default epkgs.use-package epkgs.delight epkgs.treesit-grammars.with-all-grammars]
);
#+END_SRC*** The environment
Finally, we can actually build the environment. This just uses Nixpkgs
=buildEnv= to generate Nix paths for the different packages. Some need special handling.First, we build the info pages. This takes all of the packages listed in
=userPackages= and runs =install-info= on them. Info pages are usually found in
the =$out/share/info= directory, but in Emacs packages can be found anywhere in
=$out/share/emacs=.#+BEGIN_SRC nix
infopages = buildEnv {
name = "info-pages";
buildInputs = [ pkgs.texinfoInteractive ];
paths = userPackages ++ [customEmacsPackages.emacs]
++ lib.optional hostPlatform.isLinux pkgs.glibcInfo;
extraOutputsToInstall = [ "info" "doc" "devdoc" ];
pathsToLink = [ "/share/info" ];
postBuild = ''
shopt -s nullglob
find -L ${myEmacsPackages}/share/emacs/ -name '*.info*' -exec ln -s {} $out/share/info \;
for i in $out/share/info/*.info*; do
install-info $i $out/share/info/dir
done
'';
};
#+END_SRCNext, we build the man pages. Again, these come from =userPackages= and are in
the =/share/man= directory. In addition, some extra man pages are added like the
POSIX man pages, and the C++ stdlib man pages. Other OS-specific ones are also
included where appropriate Linux man pages (=man-pages=), and the ones from the
Apple SDK.#+BEGIN_SRC nix
manpages = buildEnv {
name = "man-pages";
ignoreCollisions = (!(config.bauer.small or false));
paths = userPackages ++ [pkgs.man-pages-posix
pkgs.stdman
# pkgs.llvmPackages.clang-manpages
# pkgs.llvmPackages.llvm-manpages
]
++ lib.optional (hostPlatform.isDarwin && big && allowUnfree)
"${apple_sdk}/usr"
++ lib.optional hostPlatform.isLinux pkgs.man-pages;
extraOutputsToInstall = [ "man" "doc" "devdoc" "devman" ];
pathsToLink = [ "/share/man" ];
};
#+END_SRCNext, build the XDG / FreeDesktop directory paths. These come from
=userPackages= and include relevnt information for Desktop files, MIME info,
menus, and icons. This updates the caches where appropriate as well. The
location of these directories is defined in the FreeDesktop specification.#+BEGIN_SRC nix
xdg-data = buildEnv {
name = "xdg-data-dirs";
buildInputs = [ pkgs.desktop-file-utils pkgs.shared-mime-info ];
paths = userPackages ++ [ customEmacsPackages.emacs pkgs.zsh ];
pathsToLink = [
"/share/applications"
"/share/mime"
"/share/menus"
"/share/icons"
];
postBuild = ''
export XDG_DATA_DIRS=$out/shareif [ -w $out/share/applications ]; then
update-desktop-database $out/share/applications
fiif [ -w $out/share/mime ] \
&& [ -w $out/share/mime/packages ]; then
update-mime-database -V $out/share/mime
fi
'';
};
#+END_SRCNext, ZSH completions are built. These all reside in the
=$out/share/zsh/site-functions= directory.#+BEGIN_SRC nix
zsh-completions = buildEnv {
name = "zsh-completions";
paths = [ pkgs.zsh-completions ] ++ userPackages; # pkgs.nix-zsh-completions
pathsToLink = [ "/share/zsh" ];
};
#+END_SRCNext, setup the binary directory. This will be put in the user’s =PATH=. We also
remove binaries starting with =.=, as they are used in Nixpkgs for the “wrapped”
version of executables.#+BEGIN_SRC nix
bins = buildEnv {
name = "bins";
paths = userPackages;
extraOutputsToInstall = [ "bin" ];
pathsToLink = [ "/bin" ];
postBuild = ''
find $out/bin -maxdepth 1 -name ".*" -type l -delete
'';
};
#+END_SRCSetup the system headers path. This is delibirately light to avoid huge
closures. Right now, only =libc= and =libcxx= are included by default.#+BEGIN_SRC nix
sysheaders = buildEnv {
name = "headers";
pathsToLink = [ "/include" ];
extraOutputsToInstall = [ "dev" ];
paths = [ stdenv.cc.libc ]
++ lib.optional hostPlatform.isDarwin pkgs.libcxx
++ lib.optional (hostPlatform.isDarwin && big && allowUnfree)
"${apple_sdk}/usr";
};
#+END_SRCAlso setup the Apple framework paths. These are a mix between lib directory and
include directory. This is oinly useful on macOS/Darwin machines.#+BEGIN_SRC nix
apple_sdk = "${pkgs.darwin.xcode}/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk";
sysframeworks = buildEnv {
name = "frameworks";
paths = lib.optionals (hostPlatform.isDarwin && big && allowUnfree)
[ "${apple_sdk}/System/Library/Frameworks"
"${apple_sdk}/System/Library/PrivateFrameworks" ];
};
#+END_SRCSetup XML schema needed for editing some docbooks in Emacs. Not entirely sure
why this is necessary, but nXML complains otherwise.#+BEGIN_SRC nix
schemas = writeText "schemas.xml" ''
'';
#+END_SRCFull listing of packages that will be made available.
#+BEGIN_SRC nix
userPackages = (with pkgs; [
# From common-path.nix
coreutils-full findutils diffutils gnused gnugrep gawk gnutar
gzip bzip2 gnumake bashInteractive patch xz# Helpful core tools
curl zsh cacert file lsof pstree which rsync
unzip man less silver-searcher ripgrep
tree gnutlsdirenv
] ++ lib.optionals big ([
git# Useful tools
isync notmuch graphviz indent
graphviz imagemagickbazaar mercurial
# Programming interpreters/compilers
myTex rEnv perl python lua coq ocaml
openjdk nodejs gcc gdbtravis v8
] ++ (lib.optional stdenv.hostPlatform.isDarwin openssh) # use /usr/bin/ ssh
++ (with netbsd; [ getent getconf ])
++ (with nodePackages; [ tern heroku node2nix ])
++ (with gitAndTools; [ hub ])
++ (with haskellPackages; [ ghc jq nix-diff cabal2nix cabal-install ])
++ (with unixtools; [ utillinux nettools procps ])
));
#+END_SRCSetup global environment variables & directories. Most of the global directories
will not exist, but the way search paths work, means that is okay. We aim to
support all directories in a common system, prioritizing ones that the user has
the most direct access to. Global directories should only contain system paths.#+BEGIN_SRC nix
global-dirs = [ "/nix/var/nix/profiles/default"
"/run/wrappers"
"/run/current-system/sw"
"/usr/local"
"/usr"
"" ];PATH = lib.concatStringsSep ":" [
(lib.makeBinPath ([ bins customEmacsPackages.emacs pkgs.zsh ]
++ global-dirs))
(lib.makeSearchPath "sbin" [ "/usr" "" ])
];
MANPATH = ":" + lib.makeSearchPathOutput "man" "share/man"
([ manpages customEmacsPackages.emacs pkgs.zsh ] ++ global-dirs);
INFOPATH = "${infopages}/share/info";
XDG_DATA_DIRS = lib.makeSearchPathOutput "xdg" "share"
([ xdg-data ] ++ global-dirs);
TERMINFO_DIRS = lib.makeSearchPathOutput "terminfo" "share/terminfo"
([ pkgs.ncurses ]);INPUTRC = builtins.toFile "inputrc" ''
$include /etc/inputrc$if mode=emacs
"\C-p": history-search-backward
"\C-n": history-search-forward
$endifset keyseq-timeout 1200
set colored-stats on
set colored-completion-prefix on
set completion-ignore-case on
set completion-prefix-display-length 3
set enable-bracketed-paste on
set expand-tilde on
set menu-complete-display-prefix on
set mark-symlinked-directories on
set show-all-if-ambiguous on
set show-all-if-unmodified on
set visible-stats on$if Bash
Space: magic-space
$endif
'';
#+END_SRCFinally, build the final environment. This only contains Emacs and ZSH, which
have been configured to use the paths above.#+BEGIN_SRC nix
in buildEnv {
name = "bauer-2.0.1";
buildInputs = [ pkgs.makeWrapper pkgs.bash ];
postBuild = ''
mkdir -p $out/etc
substituteAll ${./zshrc.sh} $out/etc/.zshrc
substituteInPlace $out/etc/.zshrc \
--subst-var-by completions ${zsh-completions} \
--subst-var-by coreutils ${pkgs.coreutils}
ln -s $out/etc/.zshrc $out/etc/zshrc
makeWrapper ${pkgs.zsh}/bin/zsh $out/bin/zsh --set ZDOTDIR $out/etc
substituteAll ${./etc-profile.sh} $out/etc/profile
substituteInPlace $out/etc/profile \
--subst-var-by coreutils ${pkgs.coreutils}
substitute ${./runemacs.sh} $out/bin/run \
--subst-var-by emacs ${myEmacs}
chmod +x $out/bin/run
patchShebangs $out/bin/run
ln -s $out/bin/run $out/bin/bauer
'';
pathsToLink = [
"/bin"
"/etc/profile.d"
"/share/applications"
] ++ lib.optional hostPlatform.isDarwin "/Applications";
meta = with lib; {
description = "Bauer's automated unified Emacs realm";
maintainers = with maintainers; [ matthewbauer ];
platforms = platforms.all;
priority = 4;
};
passthru = with lib; {
shellPath = "/bin/zsh";
run = "/bin/run";
sourceFile = "/etc/profile.d/profile";
PATH = makeSearchPath "bin" [bins];
MANPATH = makeSearchPath "share/man" [manpages];
INFOPATH = makeSearchPath "share/info" [infopages];
XDG_DATA_DIRS = makeSearchPath "share" [xdg-data];
TERMINFO_DIRS = makeSearchPath "share/terminfo" [terminfo];
emacs = myEmacs;
emacsConfig = default;
inherit myTex rEnv;
};
paths = [
myEmacs
(runCommand "my-profile" {
inherit PATH MANPATH XDG_DATA_DIRS INFOPATH TERMINFO_DIRS;
bash_completion = pkgs.bash-completion;
} ''
mkdir -p $out/etc/profile.d
substituteAll ${./profile.sh} $out/etc/profile.d/my-profile.sh
substituteInPlace $out/etc/profile.d/my-profile.sh \
--subst-var-by PATH ${PATH} \
--subst-var-by INFOPATH ${INFOPATH} \
--subst-var-by MANPATH ${MANPATH} \
--subst-var-by XDG_DATA_DIRS ${XDG_DATA_DIRS} \
--subst-var-by TERMINFO_DIRS ${TERMINFO_DIRS} \
--subst-var-by INPUTRC ${INPUTRC}
'')
];
}
#+END_SRC** Invoking it
We can build it with =nix-build=.#+BEGIN_SRC shell :results none :tangle no
nix-build
./result/bin/run
#+END_SRC
* Continuous integration
:PROPERTIES:
:CUSTOM_ID: ci
:END:Continuous integration is now through GitHub Actions. See
.github/workflows/main.yml for more info.* Extra
:PROPERTIES:
:CUSTOM_ID: extra
:END:These are some extra files that are checked in. They mostly deal with
maintainence and advanced usage of BAUER.** =update.sh=
:PROPERTIES:
:header-args: :tangle update.sh :tangle-mode (identity #o755) :shebang "#!/usr/bin/env sh"
:END:This is a simple script that I use to make sure I've updated the generated
files. It runs ORG mode tangler and then exports README.org to html.#+BEGIN_SRC shell :padline no
emacs --batch \
-l ob-tangle \
--eval "(org-babel-tangle-file \"README.org\")"
emacs README.org --batch \
--eval "(setq org-html-htmlize-output-type 'css)" \
-l nix-mode \
-f org-html-export-to-html
#+END_SRC
** =.gitignore=
:PROPERTIES:
:header-args: :tangle .gitignore
:END:If you end up with generated files, they’re easy to remove with Git. Just run
=git clean -xdf= & it will remove all of the files that match the .gitignore
rules (which should never be added to the git tree).These set up some paths for =.gitignore= that we don’t want getting put in
the repo. Start with Emacs/org-mode/LaTeX stuff.#+BEGIN_SRC gitignore :padline no
flycheck_*.el
*.elc
*.html
*.tex
*.log
*.aux
*.out
*.toc
#+END_SRCNix-related stuff. These are generate by =nix-build=.
#+BEGIN_SRC gitignore
result
result-*
#+END_SRCThese are all tangled by [[./README.org][README.org]].
#+BEGIN_SRC gitignore
README.el
bauer.nix
zshrc.sh
etc-profile.sh
runemacs.sh
gitconfig
gitignore
default.el
profile.sh
site-paths.el.in
org-init.el
org-src-*
configuration.nix
install
auto/
*~
#+END_SRC** =.gitattributes=
:PROPERTIES:
:header-args: :tangle .gitattributes
:END:Mark generated files.
#+BEGIN_SRC gitattributes :padline no
.gitattributes linguist-generated=true
.gitignore linguist-generated=true
.travis.yml linguist-generated=true
LICENSE linguist-generated=true
bootstrap.sh linguist-generated=true
config.nix linguist-generated=true
default.nix linguist-generated=true
deploy.sh linguist-generated=true
gist-unpack.sh linguist-generated=true
init.el linguist-generated=true
install linguist-generated=true
module.nix linguist-generated=true
update.sh linguist-generated=true
flake.nix linguist-generated=true
#+END_SRC
** =init.el=You can use this as part of [[~/.emacs.d/init.el][your Emacs init file]]:
#+BEGIN_SRC emacs-lisp :tangle init.el
(load
(expand-file-name "settings.el" user-emacs-directory) t)
(package-initialize)
(defvar bauer-dir user-emacs-directory)
(defvar bauer-org
(expand-file-name "README.org" bauer-dir))
(add-to-list 'load-path
(expand-file-name "site-lisp" bauer-dir))
(add-to-list 'native-comp-eln-load-path
(expand-file-name "native-lisp" bauer-dir))
(unless (file-exists-p
(expand-file-name "README.el" bauer-dir))
(let ((default-directory bauer-dir))
(autoload 'org-babel-tangle-file "ob-tangle")
(org-babel-tangle-file bauer-org
"README.el"
"emacs-lisp")))
(load (expand-file-name "README.el" bauer-dir) t)
#+END_SRCThis will boot the IDE. Note that this is done for you in the Nix-based
installation process. The above script should be used in places where Nix is
unavailable.** NixOS module
:PROPERTIES:
:header-args: :tangle module.nix :padline no
:END:I’ve provided a module suitable for use with a NixOS configuration. To use it,
just add something like the following to your configuration.nix file.#+BEGIN_SRC nix :tangle no :padline no
# -*- mode: nix; coding: utf-8; -*-
{ ... }:
{
imports = [
(builtins.fetchurl "https://matthewbauer.us/bauer/module.nix")
];
programs.bauer.enable = true;
}
#+END_SRCThe actual module implementation follow here. It will pull in some files needed
for all of this to work. It should provide ‘bauer’ as a runnable command.#+BEGIN_SRC nix
# -*- mode: nix; coding: utf-8; -*-
{ config, lib, pkgs, ... }: with lib;let
ensure = f: n: if builtins.pathExists f then f
else builtins.fetchurl
"https://matthewbauer.us/bauer/${n}";
bauer = import (ensure ./default.nix "default.nix") {
inherit pkgs;
};
in {
options = {
programs.bauer = {
enable = mkOption {
default = false;
type = types.bool;
};
};
};config = mkIf config.programs.bauer.enable {
environment = {
systemPackages = [ bauer ];
variables = with lib; {
PATH = [bauer.PATH];
XDG_DATA_DIRS = [bauer.XDG_DATA_DIRS];
TERMINFO_DIRS = [bauer.TERMINFO_DIRS];
MANPATH = [bauer.MANPATH];
INFOPATH = [bauer.INFOPATH];
};
};
};
}
#+END_SRC
** NixOS virtualbox & installerUse this to build a .ova file for demonstration purposes. This file can be
booted by VirtualBox. Useful for Windows users especially.#+BEGIN_SRC nix :tangle no :padline no
# -*- mode: nix; coding: utf-8; -*-
{ system ? builtins.currentSystem }:
let
ensure = f: n: if builtins.pathExists f then f
else builtins.fetchurl
"https://matthewbauer.us/bauer/${n}";
in {
ova = (import {
inherit system;
modules = [
(ensure ./module.nix "module.nix")
];
}).config.system.build.virtualBoxOVA;
iso = (import {
inherit system;
modules = [
(ensure ./module.nix "module.nix")
];
}).config.system.build.isoImage;
}
#+END_SRC** Flake
:PROPERTIES:
:header-args: :tangle flake.nix :padline no
:END:#+BEGIN_SRC nix :tangle flake.nix :padline no
{
description = "an Emacs+Nix IDE";inputs.nixpkgs.url = "github:nixos/nixpkgs";
inputs.emacs-overlay = {
url = "github:nix-community/emacs-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};outputs = { self, nixpkgs, emacs-overlay }: let
systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system);
nixpkgsFor = forAllSystems (system: import nixpkgs { inherit system; });
in {
packages = forAllSystems (system: {
bauer = let
pkgs = nixpkgsFor.${system};
evalPkgs = nixpkgsFor.${system};
in import (evalPkgs.runCommand "README" {
buildInputs = with evalPkgs; [ pkgs.emacs git ];
} (''
install -D ${./README.org} $out/README.org
cd $out
cp -r ${./site-lisp} site-lisp
emacs --batch --quick \
-l ob-tangle \
--eval "(org-babel-tangle-file \"README.org\")" > /dev/null
cp bauer.nix default.nix
'')) { inherit pkgs emacs-overlay evalPkgs system; };
inherit (self.packages.${system}.bauer) myTex emacs rEnv;
default = self.packages.${system}.bauer;
});apps = forAllSystems (system: {
bauer = {
type = "app";
program = "${self.packages.${system}.bauer}/bin/run";
};
default = self.apps.${system}.bauer;
});devShells = forAllSystems (system: {
bauer = with nixpkgsFor.${system}; stdenv.mkDerivation {
name = "bauer";
nativeBuildInputs = [ pkgs.emacs git ];
shellHook = ''
echo Run ./update.sh to generate files.
'';
};
default = self.devShells.${system}.bauer;
});nixosModules.bauer = {
imports = [ ./module.nix ];
};
};nixConfig.allow-import-from-derivation = true;
}
#+END_SRC
** Deploy script
:PROPERTIES:
:header-args: :tangle deploy.sh :shebang "#!/usr/bin/env sh"
:END:I use this script to deploy to the gh-pages branch. It merges master into
gh-pages and then runs ORG mode tangle and export. Unfortunately, merge
conflicts are not handled, and must be dealt with manually.#+BEGIN_SRC shell
setup() {
git stash push
git checkout gh-pages
}cleanup() {
git checkout master
git stash pop
}setup
trap cleanup EXITgit fetch origin
git reset --hard origin/gh-pages
git merge --no-edit master
./update.sh
git add .
git commit -m "Regen"
git push origin gh-pages
#+END_SRC
** Gist script
:PROPERTIES:
:header-args: :tangle gist-unpack.sh :shebang "#!/usr/bin/env bash"
:END:Private file handling for new environments. Unpack private files from a Gist
into your environment. Must have already setup SSH GitHub authentication. To set
up GitHub SSH run:#+BEGIN_SRC shell :tangle no
$ ssh-keygen
#+END_SRCThen, add =$HOME/.ssh/id_rsa=.pub through GitHub’s Web UI. Invoke the script
with:#+BEGIN_SRC shell :tangle no
$ ./gist-unpack.sh 4a7372cf458f2b322c0b7a6e7837592f
#+END_SRCFiles that can be included in the gist are:
- .authinfo: stores your passwords and credentials. Domains like smtp.gmail.com,
api.github.com, and irc.freenode.net require credentials.
- .gitconfig: stores your personal Git configuration and settings. Things like
user.email, user.name, github.user should go here. Unfortunately, it also may
have credentials for smtpmail. Ideally these would be put in .authinfo to
avoid mixing secrets. See [[https://lists.gnu.org/archive/html/guix-devel/2018-04/msg00254.html][this thread]] for more info.
- .sshconfig: stores your SSH configuration corresponding to .ssh/config. Put
global host names and relates settings here.
- settings.el: store your personal Emacs configurations. Settings like
=erc-nick=, =message-send-mail-function=, =send-mail-function=,
=smtpmail-smtp-server=, =smtpmail-smtp-service=, =user-full-name=, and
=user-mail-address= should go here.The actual script follows. The main idea is just to download all of the files
from the Gist and copy them to the =$HOME= directory. =--force= can be passed to
allow overriding your current =$HOME= files.#+BEGIN_SRC shell
if [ $# -eq 0 ]; then
echo Usage: "$0" [ GIST_ID | URL ] [ -u USER ] [ -t TOKEN ] >&2
exit 1
fiFORCE=
GIST_ID=
USER=
TOKEN=
URL=
while [ $# -gt 0 ]; do
case "$1" in
-f|--force)
echo Forcing install... >&2
FORCE=1
shift
;;
-u|--user)
if [ -n "$USER" ]; then
echo Multiple users passed! >&2
exit 1
fi
shift
USER="$1"
shift
;;
-t|--token)
if [ -n "$TOKEN" ]; then
echo Multiple tokens passed! >&2
exit 1
fi
shift
TOKEN="$1"
shift
;;
-g|--gist-id)
if [ -n "$GIST_ID" ] || [ -n "$URL" ]; then
echo Multiple URLs or Gist ids passed! >&2
exit 1
fi
shift
GIST_ID="$1"
shift
;;
-u|--url)
if [ -n "$GIST_ID" ] || [ -n "$URL" ]; then
echo Multiple URLs or Gist ids passed! >&2
exit 1
fi
shift
URL="$1"
shift
;;
https://*|ssh://*|git://*)
if [ -n "$GIST_ID" ] || [ -n "$URL" ]; then
echo Multiple URLs or Gist ids passed! >&2
exit 1
fi
URL="$1"
shift
;;
,*)
if [ -n "$GIST_ID" ] || [ -n "$URL" ]; then
echo Multiple URLs or Gist ids passed! >&2
exit 1
fi
GIST_ID="$1"
echo Using gist "$GIST_ID" >&2
shift
;;
esac
doneif [ -n "$GIST_ID" ]; then
URL=ssh://[email protected]/"$GIST_ID".git
fiif [ -z "$URL" ]; then
echo No gist id or url provided. >&2
exit 1
fimkdir -p $HOME/.ssh
echo "github.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==" >> $HOME/.ssh/known_hostsssh -T [email protected] 2> /dev/null
if [ $? -eq 255 ]; then
if ! [ -f "$HOME/.ssh/id_rsa" ]; then
echo No ssh key is available. Generating it and adding it to GitHub to continue.
ssh-keygen -t rsa -N "" -f "$HOME/.ssh/id_rsa"
fiif [ -z "$USER" ] && [ -t 1 ]; then
echo -n "GitHub Username: "; read USER
fi
if [ -z "$USER" ]; then
echo Username cannot be empty. Pass -u with your username to continue.
exit 1
fi
auth="$USER"
if [ -n "$TOKEN" ]; then
auth="$auth:$TOKEN"
fi
curl -u "$auth" -d "$(printf '{"title": "%s", "key": "%s"}' "${HOST-$(hostname)}" "$(cat $HOME/.ssh/id_rsa.pub)")" https://api.github.com/user/keys > /dev/null
figistdir="$(mktemp -d)"
setup() {
if ! git clone "$URL" "$gistdir"; then
echo Failed to clone Gist. Verify "$URL" exists >&2
echo and you have permission to access it. >&2
exit 1
fipushd "$gistdir" >/dev/null
}cleanup() {
popd >/dev/null
rm -rf "$gistdir"
}setup
trap cleanup EXITif [ -n "${BASH_VERSION-}" ]; then
shopt -s dotglob
fi
shopt -s nullglob
for f in *; do
if [ "$f" = ".git" ]; then
continue
fi
if ! [ -f "$f" ]; then
echo Skipping "$f", not a file >&2
continue
fi
DEST=
case "$f" in
settings.el) DEST="$HOME/.config/emacs/settings.el" ;;
.sshconfig) DEST="$HOME/.ssh/config" ;;
.ssh_authorized_keys) DEST="$HOME/.ssh/authorized_keys" ;;
nix.conf) DEST="$HOME/.config/nix/nix.conf" ;;
machines) DEST="$HOME/.config/nix/machines" ;;
.gitignore) DEST="$HOME/.config/git/ignore" ;;
.gitconfig) DEST="$HOME/.config/git/config" ;;
.gitattributes) DEST="$HOME/.config/git/attributes" ;;
credentials|.aws_credentials) DEST="$HOME/.aws/credentials" ;;
,*) DEST="$HOME/$f" ;;
esac
CONCAT=
case "$f" in
.authinfo) CONCAT=1 ;;
.sshconfig) CONCAT=1 ;;
.ssh_authorized_keys) CONCAT=1 ;;
.gitignore) CONCAT=1 ;;
nix.conf) CONCAT=1 ;;
machines) CONCAT=1 ;;
esac
if [ -z "$DEST" ]; then
echo Skipping "$f", no destination found >&2
continue
fi
if [ -f "$DEST" ] && [ -z "$FORCE" ] && [ -z "$CONCAT" ]; then
echo Skipping "$f", destination already exists >&2
continue
fi
mkdir -p "$(dirname "$DEST")"
if [ -n "$CONCAT" ]; then
cat "$f" >> "$DEST"
else
cp "$f" "$DEST"
fi
case "$f" of
.authinfo) chmod 600 "$DEST" ;;
"Library/LaunchAgents/"*) launchctl load -w "$HOME/$f" ;;
esac
done
#+END_SRC** LICENSE
Copyright © 2018-2020 Matthew BauerThis program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.You should have received a copy of the GNU General Public License along with
this program. If not, see .
* COMMENT Footer
# Local Variables:
# mode: org
# bug-reference-url-format: "https://github.com/matthewbauer/bauer/issues/%s"
# coding: utf-8
# compile-command: "nix build"
# fill-column: 80
# sentence-end-double-space: nil
# indent-tabs-mode: nil
# End: