The cutest Emacs distribution
Witchmacs is a very simple and basic configuration file for Emacs. From here you can edit, remove, or add many aspects of Emacs you want. I encourage you to do so, because Emacs works best when it’s customized to your own liking!
What’s the difference between a villain and a super villain? Presentation!
Currently, the appeal of Witchmacs is seeing a cute Marisa picture everytime you start up Emacs. Also, it is a very basic config which means you can easily add, edit or remove things as you like. You can use this as a building block when making your OWN Emacs config!
Ogres are like onions
They smell?
Yes - No! layers! onions have layers! ogres have layers!
As opposed to many other Emacs distributions, Witchmacs has ZERO customization layers which means you can just jump in, look at the config file and start editing away! After all, Emacs works best when it’s customized to your own liking!
git clone https://github.com/snackon/Witchmacs ~/.emacs.d
After running this command, the first time you run Emacs will download all of the specified packages in the config.org file, so please be patient!
Also, to get autocompletion on C, C++ and Java files, you’ll need to install the corresponding company backend servers first (you’ll be automatically prompted to do so the first time you open a relevant file)
clang
as backend for C and C++ autocompletion
llvm
to install irony server
(optional) mvn
maven for java project handling through meghanada
- Add and configure some extra packages
Make dashboard cooler- Make dashboard EVEN cooler!
- Maybe add rotating dashboard pictures
Make custom Witchmacs theme- (Maybe) Make custom theme for other Marisa color schemes or different touhous
Clean up/organize init.el- Clean up/organize config.org
- Optimize startup time
Minor quality-of-life modifications for a more pleasant Emacs experience
Emacs breaks certain modes when it has line-numbers-mode enabled, (like docview or ansi-term) so I utilize the approach of only enabling it on some major modes rather than globally
(add-hook 'prog-mode-hook 'display-line-numbers-mode)
(add-hook 'text-mode-hook 'display-line-numbers-mode)
(show-paren-mode 1)
(setq inhibit-startup-message t)
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)
(setq x-select-enable-clipboard t)
(setq make-backup-files nil)
(setq auto-save-default nil)
(setq scroll-conservatively 100)
(setq ring-bell-function 'ignore)
(setq-default tab-width 4)
(setq-default standard-indent 4)
(setq c-basic-offset tab-width)
(setq-default electric-indent-inhibit t)
(setq-default indent-tabs-mode t)
(setq backward-delete-char-untabify-method 'nil)
(global-prettify-symbols-mode t)
(setq electric-pair-pairs '(
(?\{ . ?\})
(?\( . ?\))
(?\[ . ?\])
(?\" . ?\")
))
(electric-pair-mode t)
(defun split-and-follow-horizontally ()
(interactive)
(split-window-below)
(balance-windows)
(other-window 1))
(global-set-key (kbd "C-x 2") 'split-and-follow-horizontally)
(defun split-and-follow-vertically ()
(interactive)
(split-window-right)
(balance-windows)
(other-window 1))
(global-set-key (kbd "C-x 3") 'split-and-follow-vertically)
(defalias 'yes-or-no-p 'y-or-n-p)
Super - Control - <arrow>
(global-set-key (kbd "s-C-<left>") 'shrink-window-horizontally)
(global-set-key (kbd "s-C-<right>") 'enlarge-window-horizontally)
(global-set-key (kbd "s-C-<down>") 'shrink-window)
(global-set-key (kbd "s-C-<up>") 'enlarge-window)
(global-hl-line-mode t)
(setq use-package-always-defer t)
One of the main selling points of Emacs! no Emacs distribution is complete without sensible and well-defined org-mode defaults
(use-package org
:config
(add-hook 'org-mode-hook 'org-indent-mode)
(add-hook 'org-mode-hook
'(lambda ()
(visual-line-mode 1))))
(use-package org-indent
:diminish org-indent-mode)
(use-package htmlize
:ensure t)
We are using Emacs, so we might as well implement as many tools from our workflow into it as possible
Eshell cannot handle ncurses programs and in certain interpreters (Python, GHCi) selecting previous commands does not work (for now). I recommend using eshell for light cli work, and using your external terminal emulator of choice for heavier tasks
(setq eshell-prompt-regexp "^[^αλ\n]*[αλ] ")
(setq eshell-prompt-function
(lambda nil
(concat
(if (string= (eshell/pwd) (getenv "HOME"))
(propertize "~" 'face `(:foreground "#99CCFF"))
(replace-regexp-in-string
(getenv "HOME")
(propertize "~" 'face `(:foreground "#99CCFF"))
(propertize (eshell/pwd) 'face `(:foreground "#99CCFF"))))
(if (= (user-uid) 0)
(propertize " α " 'face `(:foreground "#FF6666"))
(propertize " λ " 'face `(:foreground "#A6E22E"))))))
(setq eshell-highlight-prompt nil)
(defalias 'open 'find-file-other-window)
(defalias 'clean 'eshell/clear-scrollback)
(defun eshell/sudo-open (filename)
"Open a file as root in Eshell."
(let ((qual-filename (if (string-match "^/" filename)
filename
(concat (expand-file-name (eshell/pwd)) "/" filename))))
(switch-to-buffer
(find-file-noselect
(concat "/sudo::" qual-filename)))))
(defun eshell-other-window ()
"Create or visit an eshell buffer."
(interactive)
(if (not (get-buffer "*eshell*"))
(progn
(split-window-sensibly (selected-window))
(other-window 1)
(eshell))
(switch-to-buffer-other-window "*eshell*")))
(global-set-key (kbd "<s-C-return>") 'eshell-other-window)
Auto-package-update automatically updates and removes old packages
(use-package auto-package-update
:defer nil
:ensure t
:config
(setq auto-package-update-delete-old-versions t)
(setq auto-package-update-hide-results t)
(auto-package-update-maybe))
Diminish hides minor modes to prevent cluttering your mode line
(use-package diminish
:ensure t)
22/04/2019: This macro was provided by user ld43 after I couldn’t figure out how to make diminish work by being at the top of the config file.
;(defmacro diminish-built-in (&rest modes)
; "Accepts a list MODES of built-in emacs modes and generates `with-eval-after-load` diminish forms based on the file implementing the mode functionality for each mode."
; (declare (indent defun))
; (let* ((get-file-names (lambda (pkg) (file-name-base (symbol-file pkg))))
; (diminish-files (mapcar get-file-names modes))
; (zip-diminish (-zip modes diminish-files)))
; `(progn
; ,@(cl-loop for (mode . file) in zip-diminish
; collect `(with-eval-after-load ,file
; (diminish (quote ,mode)))))))
; This bit goes in init.el
;(diminish-built-in
; beacon-mode
; which-key-mode
; page-break-lines-mode
; undo-tree-mode
; eldoc-mode
; abbrev-mode
; irony-mode
; company-mode
; meghanada-mode)
27/05/2019: Since the diminish functionality was always built-in in use-package, there was never a point in using a diminish config. lol silly me
I tried spaceline and didn’t like it. What I did like was its theme
(use-package spaceline
:ensure t)
I prefer powerline over spaceline, but the default powerline themes don’t work for me for whatever reason, so I use the spaceline theme
(use-package powerline
:ensure t
:init
(spaceline-spacemacs-theme)
:hook
('after-init-hook) . 'powerline-reset)
The frontend of Witchmacs; without this there’d be no Marisa in your Emacs startup screen
(use-package dashboard
:ensure t
:defer nil
:preface
(defun update-config ()
"Update Witchmacs to the latest version."
(interactive)
(let ((dir (expand-file-name user-emacs-directory)))
(if (file-exists-p dir)
(progn
(message "Witchmacs is updating!")
(cd dir)
(shell-command "git pull")
(message "Update finished. Switch to the messages buffer to see changes and then restart Emacs"))
(message "\"%s\" doesn't exist." dir))))
(defun create-scratch-buffer ()
"Create a scratch buffer"
(interactive)
(switch-to-buffer (get-buffer-create "*scratch*"))
(lisp-interaction-mode))
:config
(dashboard-setup-startup-hook)
(setq dashboard-items '((recents . 5)))
(setq dashboard-banner-logo-title "W I T C H M A C S - The cutest Emacs distribution!")
(setq dashboard-startup-banner "~/.emacs.d/marivector.png")
(setq dashboard-center-content t)
(setq dashboard-show-shortcuts nil)
(setq dashboard-set-init-info t)
(setq dashboard-init-info (format "%d packages loaded in %s"
(length package-activated-list) (emacs-init-time)))
(setq dashboard-set-footer nil)
(setq dashboard-set-navigator t)
(setq dashboard-navigator-buttons
`(;; line1
((,nil
"Witchmacs on github"
"Open Witchmacs' github page on your browser"
(lambda (&rest _) (browse-url "https://github.com/snackon/witchmacs"))
'default)
(nil
"Witchmacs crash course"
"Open Witchmacs' introduction to Emacs"
(lambda (&rest _) (find-file "~/.emacs.d/Witcheat.org"))
'default)
(nil
"Update Witchmacs"
"Get the latest Witchmacs update. Check out the github commits for changes!"
(lambda (&rest _) (update-config))
'default)
)
;; line 2
((,nil
"Open scratch buffer"
"Switch to the scratch buffer"
(lambda (&rest _) (create-scratch-buffer))
'default)
(nil
"Open config.org"
"Open Witchmacs' configuration file for easy editing"
(lambda (&rest _) (find-file "~/.emacs.d/config.org"))
'default)))))
If you pay close attention to the code in dashboard, you’ll notice that it uses custom functions defined under the :preface use-package block. I wrote all of those functions by looking at other people’s Emacs distributions (Mainly Centaur Emacs) and then experimenting and adapting them to Witchmacs. If you dig around, you’ll find the same things I did - maybe even more!
22/05/19: On this day, the main maintainers of the dashboard package have added built-in fuinctionality to display init and package load time, thing that I already had implemented much earlier on my own. I have left here my implementation for historical purposes
;(insert (concat
; (propertize (format "%d packages loaded in %s"
; (length package-activated-list) (emacs-init-time))
; 'face 'font-lock-comment-face)))
;
;(dashboard-center-line)
Incredibly useful package; if you are in the middle of a command and don’t know what to type next, just wait a second and you’ll get a nice buffer with all possible completions
(use-package which-key
:ensure t
:diminish which-key-mode
:init
(which-key-mode))
When doing C-s to search, you get this very nice and neat mini-buffer that you can traverse with the arrow keys (or C-n and C-p) and then press <RET> to select where you want to go
(use-package swiper
:ensure t
:bind ("C-s" . 'swiper))
Vim keybindings in Emacs. Please note that Witchmacs has NO other evil-mode compatibility packages because I like to KISS. This might change in the future
(use-package evil
:ensure t
:defer nil
:init
(setq evil-want-keybinding nil)
(setq evil-want-C-u-scroll t)
:config
(evil-mode 1))
;(use-package evil-collection
; :after evil
; :ensure t
; :config
; (evil-collection-init))
You might find beacon an unnecesary package but I find it very neat. It briefly highlights the cursor position when switching to a new window or buffer
(use-package beacon
:ensure t
:diminish beacon-mode
:init
(beacon-mode 1))
Avy is a very useful package; instead of having to move your cursor to a line that is very far away, just do M - s and type the character that you want to move to
(use-package avy
:ensure t
:bind
("M-s" . avy-goto-char))
Switch window is a neat package because instead of having to painstakingly do C - x o until you’re in the window you want to edit, you can just do C - x o and pick the one you want to move to according to the letter it is assigned to
(use-package switch-window
:ensure t
:config
(setq switch-window-input-style 'minibuffer)
(setq switch-window-increase 4)
(setq switch-window-threshold 2)
(setq switch-window-shortcut-style 'qwerty)
(setq switch-window-qwerty-shortcuts
'("a" "s" "d" "f" "j" "k" "l"))
:bind
([remap other-window] . switch-window))
For the longest time I used the default way of switching and killing buffers in Emacs. Same for finding files. Ido-mode made these three tasks IMMENSELY easier and more intuitive. Please not that I still use the default way M - x works because I believe all you really need for it is which-key
(use-package ido
:init
(ido-mode 1)
:config
(setq ido-enable-flex-matching nil)
(setq ido-create-new-buffer 'always)
(setq ido-everywhere t))
(use-package ido-vertical-mode
:ensure t
:init
(ido-vertical-mode 1))
; This enables arrow keys to select while in ido mode. If you want to
; instead use the default Emacs keybindings, change it to
; "'C-n-and-C-p-only"
(setq ido-vertical-define-keys 'C-n-C-p-up-and-down)
Utilize asynchronous processes whenever possible
(use-package async
:ensure t
:init
(dired-async-mode 1))
(use-package page-break-lines
:ensure t
:diminish (page-break-lines-mode visual-line-mode))
(use-package undo-tree
:ensure t
:diminish undo-tree-mode)
Neat side-bar file and project explorer
(use-package treemacs
:ensure t
:defer t
:init
(with-eval-after-load 'winum
(define-key winum-keymap (kbd "M-0") #'treemacs-select-window))
:config
(progn
(setq treemacs-collapse-dirs (if (executable-find "python3") 3 0)
treemacs-deferred-git-apply-delay 0.5
treemacs-display-in-side-window t
treemacs-eldoc-display t
treemacs-file-event-delay 5000
treemacs-file-follow-delay 0.2
treemacs-follow-after-init t
treemacs-git-command-pipe ""
treemacs-goto-tag-strategy 'refetch-index
treemacs-indentation 2
treemacs-indentation-string " "
treemacs-is-never-other-window nil
treemacs-max-git-entries 5000
treemacs-missing-project-action 'ask
treemacs-no-png-images nil
treemacs-no-delete-other-windows t
treemacs-project-follow-cleanup nil
treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory)
treemacs-recenter-distance 0.1
treemacs-recenter-after-file-follow nil
treemacs-recenter-after-tag-follow nil
treemacs-recenter-after-project-jump 'always
treemacs-recenter-after-project-expand 'on-distance
treemacs-show-cursor nil
treemacs-show-hidden-files t
treemacs-silent-filewatch nil
treemacs-silent-refresh nil
treemacs-sorting 'alphabetic-desc
treemacs-space-between-root-nodes t
treemacs-tag-follow-cleanup t
treemacs-tag-follow-delay 1.5
treemacs-width 30)
(treemacs-resize-icons 11)
(treemacs-follow-mode t)
(treemacs-filewatch-mode t)
(treemacs-fringe-indicator-mode t)
(pcase (cons (not (null (executable-find "git")))
(not (null (executable-find "python3"))))
(`(t . t)
(treemacs-git-mode 'deferred))
(`(t . _)
(treemacs-git-mode 'simple))))
:bind
(:map global-map
("M-0" . treemacs-select-window)
("C-x t 1" . treemacs-delete-other-windows)
("C-x t t" . treemacs)
("C-x t B" . treemacs-bookmark)
("C-x t C-t" . treemacs-find-file)
("C-x t M-t" . treemacs-find-tag)))
(use-package treemacs-evil
:after treemacs evil
:ensure t)
(use-package treemacs-icons-dired
:after treemacs dired
:ensure t
:config (treemacs-icons-dired-mode))
Git porcelain for Emacs
(use-package magit
:ensure t)
(use-package eldoc
:diminish eldoc-mode)
(use-package abbrev
:diminish abbrev-mode)
Company is the autocompletion frontend that takes all the backends and gives you possible autocompletions when writing programs
(use-package company
:ensure t
:diminish (meghanada-mode company-mode irony-mode)
:config
(setq company-idle-delay 0)
(setq company-minimum-prefix-length 3)
(define-key company-active-map (kbd "M-n") nil)
(define-key company-active-map (kbd "M-p") nil)
(define-key company-active-map (kbd "C-n") #'company-select-next)
(define-key company-active-map (kbd "C-p") #'company-select-previous)
(define-key company-active-map (kbd "SPC") #'company-abort)
:hook
((java-mode c-mode c++-mode) . company-mode))
Yasnippet provides useful snippets, nothing to do with Company but still useful when used in conjuction with it
(use-package yasnippet
:ensure t
:diminish yas-minor-mode
:hook
((c-mode c++-mode) . yas-minor-mode)
:config
(yas-reload-all))
(use-package yasnippet-snippets
:ensure t)
Irony is the company backend for C and C++
(use-package company-c-headers
:defer nil
:ensure t)
(use-package company-irony
:defer nil
:ensure t
:config
(setq company-backends '((company-c-headers
company-dabbrev-code
company-irony))))
(use-package irony
:defer nil
:ensure t
:config
:hook
((c++-mode c-mode) . irony-mode)
('irony-mode-hook) . 'irony-cdb-autosetup-compile-options)
I use meghanada to provide very neat autocompletion when editing Java files. I use it mainly for autocompletion but there’s a ton of other things it can do, automatic imports, for example.
(use-package meghanada
:ensure t
:defer nil
:config
(add-hook 'java-mode-hook
(lambda ()
(meghanada-mode t)))
(setq meghanada-java-path "java")
(setq meghanada-maven-path "mvn"))