git clone https://github.com/daedreth/UncleDavesEmacs ~/.emacs.d
Keep in mind that you should remove your own ~/.emacs and your ~/.emacs.d prior to cloning this configuration. The next time you launch Emacs (and I recommend launching emacs in the tty for the first time) it’s going to download a good amount of packages and configure them for you, you might see warnings and errors being displayed as well, those are safe to ignore as long as you relaunch emacs and see none.
The configuration, much like emacs is self documented, I highly recommend reading through my ramblings in their entirety.
You are free to pick out bits and pieces of this and apply them.
So long as you have a working use-package
configuration (take a peek at my init.el),
there should be absolutely no issues.
I highly encourage you to read up on the prose around the packages you are interested in,
I do my absolute best to describe the configuration as well as I only can, this might help you on your emacs journey!
Have some fun with it, add your own keybindings, change parameters, it’s your editor in the end.
Since we are going to use emacs as our window manager, it would be nice to have some external software to make our life easier.
xorg-server
, for obvious reasons. (there is no wayland support as of now)pulsemixer
, available fromhttps://github.com/GeorgeFilipkin/pulsemixer
.imagemagick
, if you are going to be using emacs to take screenshots.ibus-daemon
, if you need multiple keyboard input options (emacs handles them well on its own but ibus is nice).terminus-font
, for it to work out of the box, if you don’t want terminus you need to edit yourinit.el
.
- A browser (sadly, the built in xwidgets-webkit thingie is unreliable).
- A composite manager (highly recommended, I personally use
compton
, the built in one does not work as intended.). noto-cjk
, for all the fonts imaginable.slock
, if you would like to lock the screen.
sudo
, properly configured for your user.
mpd
, since we are going to be using EMMS for music playback, I recommend setting up a working mpd server.mpv
, for video playback.
clang
, for c/c++ completion.sbcl
, for clisp completion and repl.virtualenv
, for python completion.lua
, for obvious reasons.
pip install jedi flake8 autopep8
, here, a one line install.
We utilize use-package to handle downloading and configuring other packages painlessly.
With init.el
written the way it is, emacs checks for the presence of use-package
on launch and downloads it and installs if necessary.
el-Get
is too old and not configurable enough.
As you may have noticed, as you scroll down my bit of prose, this is the configuration file itself.
This configuration is written in org-mode
, which is a great emacs package and a great markup language.
On launch, this file is being sourced into ~/.emacs.d/init.el
, the prose is being automatically
stripped (as to not affect performance) and the remaining config.el
file is parsed.
This is also the reason why your org-mode configuration file is never called init.org
.
This entire config is meant to be used as a full on desktop environment, it is tailored to sit on top of xorg and be awesome.
It is perfectly possible to use it without exwm and emms, as a regular emacs config, just make sure to delete the unwanted sections,
ex.g EXWM, Audio Control, EMMS and all the launchers.
There is barely any learning curve to using exwm
, since it makes x-windows act as regular buffers as much as possible,
so chances are, as long as you know your way around emacs itself, you will instinctively know how to make use of its features.
I do my best to not pollute keymaps with my own keybindings. Most of the bindings I defined myself utilize the Super key (noted as s-
).
This is the least used modifier key together with Shift (noted as S-
), thus using those, chances are all the bindings you already know
and love are going to work flawlessly. For all the others, just keep on reading.
The most important part of every configuration.
My new favourite one I guess, really decent default values.
(use-package zerodark-theme
:ensure t
:init
(load-theme 'zerodark t))
The theme is great, really, but some of the concepts just suck with powerline.
(let ((class '((class color) (min-colors 89)))
(default (if (true-color-p) "#abb2bf" "#afafaf"))
(light (if (true-color-p) "#ccd4e3" "#d7d7d7"))
(background (if (true-color-p) "#282c34" "#333333"))
(background-dark (if (true-color-p) "#24282f" "#222222"))
(background-darker (if (true-color-p) "#22252c" "#222222"))
(mode-line-inactive (if "#1c2129" "#222222"))
(mode-line-active (if (true-color-p) "#6f337e" "#875f87"))
(background-lighter (if (true-color-p) "#3a3f4b" "#5f5f5f"))
(background-red (if (true-color-p) "#4c3840" "#5f5f5f"))
(bright-background-red (if (true-color-p) "#744a5b" "#744a5b"))
(background-purple (if (true-color-p) "#48384c" "#5f5f5f"))
(background-blue (if (true-color-p) "#38394c" "#444444"))
(bright-background-blue (if (true-color-p) "#4e5079" "#4e5079"))
(background-green (if (true-color-p) "#3d4a41" "#5f5f5f"))
(bright-background-green (if (true-color-p) "#3f6d54" "#3f6d54"))
(background-orange (if (true-color-p) "#4a473d" "#5f5f5f"))
(hl-line (if (true-color-p) "#2c323b" "#333333"))
(grey (if (true-color-p) "#cccccc" "#cccccc"))
(grey-dark (if (true-color-p) "#666666" "#666666"))
(highlight (if (true-color-p) "#3e4451" "#5f5f5f"))
(comment (if (true-color-p) "#687080" "#707070"))
(orange (if (true-color-p) "#da8548" "#d7875f"))
(orange-light (if (true-color-p) "#ddbd78" "#d7af87"))
(red (if (true-color-p) "#ff6c6b" "#ff5f5f"))
(purple (if (true-color-p) "#c678dd" "#d787d7"))
(purple-dark (if (true-color-p) "#64446d" "#5f5f5f"))
(blue (if (true-color-p) "#61afef" "#5fafff"))
(blue-dark (if (true-color-p) "#1f5582" "#005f87"))
(green (if (true-color-p) "#98be65" "#87af5f"))
(green-light (if (true-color-p) "#9eac8c" "#afaf87"))
(peach "PeachPuff3")
(diff-added-background (if (true-color-p) "#284437" "#284437"))
(diff-added-refined-background (if (true-color-p) "#1e8967" "#1e8967"))
(diff-removed-background (if (true-color-p) "#583333" "#580000"))
(diff-removed-refined-background (if (true-color-p) "#b33c49" "#b33c49"))
(diff-current-background (if (true-color-p) "#29457b" "#29457b"))
(diff-current-refined-background (if (true-color-p) "#4174ae" "#4174ae")))
(custom-theme-set-faces
'zerodark
`(fancy-battery-charging ((,class (:background ,background-blue :height 1.0 :bold t))))
`(fancy-battery-discharging ((,class (:background ,background-blue :height 1.0))))
`(fancy-battery-critical ((,class (:background ,background-blue :height 1.0))))
;; mode line stuff
`(mode-line ((,class (:background ,background-blue :height 1.0 :foreground ,blue
:distant-foreground ,background-blue
:box ,(when zerodark-use-paddings-in-mode-line
(list :line-width 6 :color background-blue))))))
`(mode-line-inactive ((,class (:background ,background-blue :height 1.0 :foreground ,default
:distant-foreground ,background-blue
:box ,(when zerodark-use-paddings-in-mode-line
(list :line-width 6 :color background-blue))))))
`(header-line ((,class (:inherit mode-line-inactive))))
`(powerline-active0 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
:distant-foreground ,background-blue))))
`(powerline-active1 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
:distant-foreground ,background-blue))))
`(powerline-active2 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
:distant-foreground ,background-blue))))
`(powerline-inactive0 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
:distant-foreground ,background-blue))))
`(powerline-inactive1 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
distant-foreground ,background-blue))))
`(powerline-inactive2 ((,class (:height 1.0 :foreground ,blue :background ,background-blue
:distant-foreground ,background-blue))))
`(dashboard-heading-face ((,class (:background ,background :foreground ,blue
:bold t :height 1.2))))
`(dashboard-banner-logo-title-face ((,class (:background ,background :foreground ,blue
:bold t :height 1.2))))
`(widget-button ((,class (:background ,background :foreground ,default :bold nil
:underline t :height 0.9))))
;; erc stuff
`(erc-nick-default-face ((,class :foreground ,blue :background ,background :weight bold)))
;; org stuff
`(outline-1 ((,class (:foreground ,blue :weight bold :height 1.8 :bold nil))))
`(outline-2 ((,class (:foreground ,purple :weight bold :height 1.7 :bold nil))))
`(outline-3 ((,class (:foreground ,peach :weight bold :height 1.6 :bold nil))))
`(outline-4 ((,class (:foreground ,green-light :weight bold :height 1.5 :bold nil))))
`(outline-5 ((,class (:foreground ,blue :weight bold :height 1.4 :bold nil))))
`(outline-6 ((,class (:foreground ,purple :weight bold :height 1.3 :bold nil))))
`(outline-7 ((,class (:foreground ,peach :weight bold :height 1.2 :bold nil))))
`(outline-8 ((,class (:foreground ,green-light :weight bold :height 1.1 :bold nil))))
`(org-block-begin-line ((,class (:background ,background-blue :foreground ,blue
:bold t :height 1.0))))
`(org-block-end-line ((,class (:background ,background-blue :foreground ,blue
:bold t :height 1.0))))))
These are setting that do not depend on packages and are built-in enhancements to the UI.
We use an actual replacement for it, keep reading or head directly to dashboard
.
(setq inhibit-startup-message t)
If you like using any of those, change -1
to 1
.
(tool-bar-mode -1)
(menu-bar-mode -1)
(scroll-bar-mode -1)
This is annoying, remove this line if you like being visually reminded of events.
(setq ring-bell-function 'ignore)
(setq locale-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(prefer-coding-system 'utf-8)
hl-line
is awesome! It’s not very awesome in the terminal version of emacs though, so we don’t use that.
Besides, it’s only used for programming.
(when window-system (add-hook 'prog-mode-hook 'hl-line-mode))
Changes lambda
to an actual symbol and a few others as well, only in the GUI version though.
(when window-system
(use-package pretty-mode
:ensure t
:config
(global-pretty-mode t)))
I don’t use either, you might want to turn those from nil
to t
if you do.
(setq make-backup-files nil)
(setq auto-save-default nil)
(defalias 'yes-or-no-p 'y-or-n-p)
Lets us use asynchronous processes wherever possible, pretty useful.
(use-package async
:ensure t
:init (dired-async-mode 1))
Everything regarding the WM or DE-like functionality is bundled here, remove the entire section if you do not wish to use exwm
.
The only time I actually had to use comments, this is for ease of removal if you happen to not like exwm.
(use-package exwm
:ensure t
:config
;; necessary to configure exwm manually
(require 'exwm-config)
;; fringe size, most people prefer 1
(fringe-mode 3)
;; emacs as a daemon, use "emacsclient <filename>" to seamlessly edit files from the terminal directly in the exwm instance
(server-start)
;; this fixes issues with ido mode, if you use helm, get rid of it
(exwm-config-ido)
;; a number between 1 and 9, exwm creates workspaces dynamically so I like starting out with 1
(setq exwm-workspace-number 1)
;; this is a way to declare truly global/always working keybindings
;; this is a nifty way to go back from char mode to line mode without using the mouse
(exwm-input-set-key (kbd "s-r") #'exwm-reset)
(exwm-input-set-key (kbd "s-k") #'exwm-workspace-delete)
(exwm-input-set-key (kbd "s-w") #'exwm-workspace-swap)
;; the next loop will bind s-<number> to switch to the corresponding workspace
(dotimes (i 10)
(exwm-input-set-key (kbd (format "s-%d" i))
`(lambda ()
(interactive)
(exwm-workspace-switch-create ,i))))
;; the simplest launcher, I keep it in only if dmenu eventually stopped working or something
(exwm-input-set-key (kbd "s-&")
(lambda (command)
(interactive (list (read-shell-command "$ ")))
(start-process-shell-command command nil command)))
;; an easy way to make keybindings work *only* in line mode
(push ?\C-q exwm-input-prefix-keys)
(define-key exwm-mode-map [?\C-q] #'exwm-input-send-next-key)
;; simulation keys are keys that exwm will send to the exwm buffer upon inputting a key combination
(exwm-input-set-simulation-keys
'(
;; movement
([?\C-b] . left)
([?\M-b] . C-left)
([?\C-f] . right)
([?\M-f] . C-right)
([?\C-p] . up)
([?\C-n] . down)
([?\C-a] . home)
([?\C-e] . end)
([?\M-v] . prior)
([?\C-v] . next)
([?\C-d] . delete)
([?\C-k] . (S-end delete))
;; cut/paste
([?\C-w] . ?\C-x)
([?\M-w] . ?\C-c)
([?\C-y] . ?\C-v)
;; search
([?\C-s] . ?\C-f)))
;; this little bit will make sure that XF86 keys work in exwm buffers as well
(dolist (k '(XF86AudioLowerVolume
XF86AudioRaiseVolume
XF86PowerOff
XF86AudioMute
XF86AudioPlay
XF86AudioStop
XF86AudioPrev
XF86AudioNext
XF86ScreenSaver
XF68Back
XF86Forward
Scroll_Lock
print))
(cl-pushnew k exwm-input-prefix-keys))
;; this just enables exwm, it started automatically once everything is ready
(exwm-enable))
Since I do not use a GUI launcher and do not have an external one like dmenu or rofi, I figured the best way to launch my most used applications would be direct emacsy keybindings.
Who would’ve thought this was available, together with ido-vertical it’s a nice large menu with its own cache for most launched applications.
(use-package dmenu
:ensure t
:bind
("s-SPC" . 'dmenu))
I guess this goes without saying but you absolutely have to change the arguments to suit the software that you are using. What good is a launcher for discord if you don’t use it at all.
(defun exwm-async-run (name)
(interactive)
(start-process name nil name))
(defun daedreth/launch-discord ()
(interactive)
(exwm-async-run "discord"))
(defun daedreth/launch-browser ()
(interactive)
(exwm-async-run "qutebrowser"))
(defun daedreth/lock-screen ()
(interactive)
(exwm-async-run "slock"))
(defun daedreth/shutdown ()
(interactive)
(start-process "halt" nil "sudo" "halt"))
These can be modified as well, suit yourself.
(global-set-key (kbd "s-d") 'daedreth/launch-discord)
(global-set-key (kbd "<s-tab>") 'daedreth/launch-browser)
(global-set-key (kbd "<XF86ScreenSaver>") 'daedreth/lock-screen)
(global-set-key (kbd "<XF86PowerOff>") 'daedreth/shutdown)
This is a set of bindings to my XF86 keys that invokes pulsemixer with the correct parameters
It goes without saying that you are free to modify the modifier as you see fit, 4 is good enough for me though.
(defconst volumeModifier "4")
(defun audio/mute ()
(interactive)
(start-process "audio-mute" nil "pulsemixer" "--toggle-mute"))
(defun audio/raise-volume ()
(interactive)
(start-process "raise-volume" nil "pulsemixer" "--change-volume" (concat "+" volumeModifier)))
(defun audio/lower-volume ()
(interactive)
(start-process "lower-volume" nil "pulsemixer" "--change-volume" (concat "-" volumeModifier)))
You can also change those if you’d like, but I highly recommend keeping ‘em the same, chances are, they will just work.
(global-set-key (kbd "<XF86AudioMute>") 'audio/mute)
(global-set-key (kbd "<XF86AudioRaiseVolume>") 'audio/raise-volume)
(global-set-key (kbd "<XF86AudioLowerVolume>") 'audio/lower-volume)
I don’t need scrot to take screenshots, or shutter or whatever tools you might have. This is enough. These won’t work in the terminal version or the virtual console, obvious reasons.
(defun daedreth/take-screenshot ()
"Takes a fullscreen screenshot of the current workspace"
(interactive)
(when window-system
(loop for i downfrom 3 to 1 do
(progn
(message (concat (number-to-string i) "..."))
(sit-for 1)))
(message "Cheese!")
(sit-for 1)
(start-process "screenshot" nil "import" "-window" "root"
(concat (getenv "HOME") "/" (subseq (number-to-string (float-time)) 0 10) ".png"))
(message "Screenshot taken!")))
(global-set-key (kbd "<print>") 'daedreth/take-screenshot)
(defun daedreth/take-screenshot-region ()
"Takes a screenshot of a region selected by the user."
(interactive)
(when window-system
(call-process "import" nil nil nil ".newScreen.png")
(call-process "convert" nil nil nil ".newScreen.png" "-shave" "1x1"
(concat (getenv "HOME") "/" (subseq (number-to-string (float-time)) 0 10) ".png"))
(call-process "rm" nil nil nil ".newScreen.png")))
(global-set-key (kbd "<Scroll_Lock>") 'daedreth/take-screenshot-region)
I use qutebrowser, so that’s what I’ll set up.
(setq browse-url-browser-function 'browse-url-generic
browse-url-generic-program "qutebrowser")
Projectile is an awesome project manager, mostly because it recognizes directories
with a .git
directory as projects and helps you manage them accordingly.
This makes sure that everything can be a project.
(use-package projectile
:ensure t
:init
(projectile-mode 1))
(global-set-key (kbd "<f5>") 'projectile-compile-project)
This is your new startup screen, together with projectile it works in unison and provides you with a quick look into your latest projects and files. Change the welcome message to whatever string you want and change the numbers to suit your liking, I find 5 to be enough.
(use-package dashboard
:ensure t
:config
(dashboard-setup-startup-hook)
(setq dashboard-startup-banner "~/.emacs.d/img/dashLogo.png")
(setq dashboard-items '((recents . 5)
(projects . 5)))
(setq dashboard-banner-logo-title ""))
The modeline is the heart of emacs, it offers information at all times, it’s persistent and verbose enough to gain a full understanding of modes and states you are in.
Due to the fact that we attempt to use emacs as a desktop environment replacement, and external bar showing the time, the battery percentage and more system info would be great to have. I have however abandoned polybar in favor of a heavily modified modeline, this offers me more space on the screen and better integration.
One modeline-related setting that is missing and is instead placed at the bottom is diminish
.
I may not use spacemacs, since I do not like evil-mode and find spacemacs incredibly bloated and slow, however it would be stupid not to acknowledge the best parts about it, the theme and their modified powerline setup.
This enables spaceline, it looks better and works very well with my theme of choice.
(use-package spaceline
:ensure t
:config
(require 'spaceline-config)
(setq spaceline-buffer-encoding-abbrev-p nil)
(setq spaceline-line-column-p nil)
(setq spaceline-line-p nil)
(setq powerline-default-separator (quote arrow))
(spaceline-spacemacs-theme))
(setq powerline-default-separator nil)
Show the current line and column for your cursor.
We are not going to have relative-linum-mode
in every major mode, so this is useful.
(setq line-number-mode t)
(setq column-number-mode t)
If you prefer the 12hr-format, change the variable to nil
instead of t
.
(setq display-time-24hr-format t)
(setq display-time-format "%H:%M - %d %B %Y")
This turns on the clock globally.
(display-time-mode 1)
A package called fancy-battery
will be used if we are in GUI emacs, otherwise the built in battery-mode will be used.
Fancy battery has very odd colors if used in the tty, hence us disabling it.
(use-package fancy-battery
:ensure t
:config
(setq fancy-battery-show-percentage t)
(setq battery-update-interval 15)
(if window-system
(fancy-battery-mode)
(display-battery-mode)))
A teeny-tiny system monitor that can be enabled or disabled at runtime, useful for checking performance with power-hungry processes in ansi-term
symon can be toggled on and off with Super + h
.
(use-package symon
:ensure t
:bind
("s-h" . symon-mode))
Abandoning sunrise-commander. The repos are dead and I’m looking for something better anyway.
I have used urxvt for years, and I miss it sometimes, but ansi-term is enough for most of my tasks.
I don’t know why this is a thing, but asking me what shell to launch every single time I open a terminal makes me want to slap babies, this gets rid of it. This goes without saying but you can replace bash with your shell of choice.
(defvar my-term-shell "/bin/bash")
(defadvice ansi-term (before force-bash)
(interactive (list my-term-shell)))
(ad-activate 'ansi-term)
In loving memory of bspwm, Super + Enter opens a new terminal, old habits die hard.
(global-set-key (kbd "<s-return>") 'ansi-term)
One of the most important things about a text editor is how efficient you manage to be when using it, how much time do basic tasks take you and so on and so forth. One of those tasks is moving around files and buffers, whatever you may use emacs for you will be jumping around buffers like it’s serious business, the following set of enhancements aims to make it easier.
As a great emacs user once said:
Do me the favor, do me the biggest favor, matter of fact do yourself the biggest favor and integrate those into your workflow.
(use-package ivy
:ensure t)
I don’t know to be honest, but this little bit of code makes scrolling with emacs a lot nicer.
(setq scroll-conservatively 100)
In order to use emacs, you don’t need to know how to use emacs. It’s self documenting, and coupled with this insanely useful package, it’s even easier. In short, after you start the input of a command and stop, pondering what key must follow, it will automatically open a non-intrusive buffer at the bottom of the screen offering you suggestions for completing the command, that’s it, nothing else.
It’s beautiful
(use-package which-key
:ensure t
:config
(which-key-mode))
Some of us have large displays, others have tiny netbook screens, but regardless of your hardware
you probably use more than 2 panes/windows at times, cycling through all of them with
C-c o
is annoying to say the least, it’s a lot of keystrokes and takes time, time you could spend doing something more productive.
This magnificent package takes care of this issue.
It’s unnoticeable if you have <3 panes open, but with 3 or more, upon pressing C-x o
you will notice how your buffers turn a solid color and each buffer is asigned a letter
(the list below shows the letters, you can modify them to suit your liking), upon pressing
a letter asigned to a window, your will be taken to said window, easy to remember, quick to use
and most importantly, it annihilates a big issue I had with emacs. An alternative is ace-window
,
however by default it also changes the behaviour of C-x o
even if only 2 windows are open,
this is bad, it also works less well with exwm
for some reason.
(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" "i" "o"))
:bind
([remap other-window] . switch-window))
After you split a window, your focus remains in the previous one. This annoyed me so much I wrote these two, they take care of it.
(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)
I like me some searching, the default search is very meh. In emacs, you mostly use search to get around your buffer, much like with avy, but sometimes it doesn’t hurt to search for entire words or mode, swiper makes sure this is more efficient.
(use-package swiper
:ensure t
:bind ("C-s" . 'swiper))
Another big thing is, buffers. If you use emacs, you use buffers, everyone loves them. Having many buffers is useful, but can be tedious to work with, let us see how we can improve it.
Doing C-x k
should kill the current buffer at all times, we have ibuffer
for more sophisticated thing.
(defun kill-current-buffer ()
"Kills the current buffer."
(interactive)
(kill-buffer (current-buffer)))
(global-set-key (kbd "C-x k") 'kill-current-buffer)
Unless you have the muscle memory, I recommend omitting this bit, as you may lose progress for no reason when working.
(setq kill-buffer-query-functions (delq 'process-kill-buffer-query-function kill-buffer-query-functions))
I don’t understand how ibuffer isn’t the default option by now. It’s vastly superior in terms of ergonomics and functionality, you can delete buffers, rename buffer, move buffers, organize buffers etc.
(global-set-key (kbd "C-x b") 'ibuffer)
If you feel like you know how ibuffer works and need not to be asked for confirmation after every serious command, enable this as follows.
(setq ibuffer-expert t)
It’s one of those things where I genuinely have to wonder why there is no built in functionality for it. Once in a blue moon I need to kill all buffers, and having ~150 of them open would mean I’d need to spend a few too many seconds doing this than I’d like, here’s a solution.
This can be invoked using C-M-s-k
. This keybinding makes sure you don’t hit it unless you really want to.
(defun close-all-buffers ()
"Kill all buffers without regard for their origin."
(interactive)
(mapc 'kill-buffer (buffer-list)))
(global-set-key (kbd "C-M-s-k") 'close-all-buffers)
Every now and then all of us feel the urge to be productive and write some code.
In the event that this happens, the following bit of configuration makes sure that
we have access to relative line numbering in programming-related modes.
I highly recommend not enabling linum-relative-mode
globally, as it messed up
something like ansi-term
for instance.
(use-package linum-relative
:ensure t
:config
(setq linum-relative-current-symbol "")
(add-hook 'prog-mode-hook 'linum-relative-mode))
Sometimes, you don’t realize how good something is until you try it extensively. I give in, helm is awesome. I’ll end up customizing it more eventually, it’s rather similar to ido-vertical though.
(use-package helm
:ensure t
:bind
("C-x C-f" . 'helm-find-files)
("C-x C-b" . 'helm-buffers-list)
("M-x" . 'helm-M-x)
:config
(defun daedreth/helm-hide-minibuffer ()
(when (with-helm-buffer helm-echo-input-in-header-line)
(let ((ov (make-overlay (point-min) (point-max) nil nil t)))
(overlay-put ov 'window (selected-window))
(overlay-put ov 'face
(let ((bg-color (face-background 'default nil)))
`(:background ,bg-color :foreground ,bg-color)))
(setq-local cursor-type nil))))
(add-hook 'helm-minibuffer-set-up-hook 'daedreth/helm-hide-minibuffer)
(setq helm-autoresize-max-height 0
helm-autoresize-min-height 40
helm-M-x-fuzzy-match t
helm-buffers-fuzzy-matching t
helm-recentf-fuzzy-match t
helm-semantic-fuzzy-match t
helm-imenu-fuzzy-match t
helm-split-window-in-side-p nil
helm-move-to-line-cycle-in-source nil
helm-ff-search-library-in-sexp t
helm-scroll-amount 8
helm-echo-input-in-header-line t)
:init
(helm-mode 1))
(require 'helm-config)
(helm-autoresize-mode 1)
(define-key helm-find-files-map (kbd "C-b") 'helm-find-files-up-one-level)
(define-key helm-find-files-map (kbd "C-f") 'helm-execute-persistent-action)
Many times have I pondered how I can move around buffers even quicker. I’m glad to say, that avy is precisely what I needed, and it’s precisely what you need as well. In short, as you invoke one of avy’s functions, you will be prompted for a character that you’d like to jump to in the visible portion of the current buffer. Afterwards you will notice how all instances of said character have additional letter on top of them. Pressing those letters, that are next to your desired character will move your cursor over there. Admittedly, this sounds overly complicated and complex, but in reality takes a split second and improves your life tremendously.
I like M-s
for it, same as C-s
is for moving by searching string, now M-s
is moving by searching characters.
(use-package avy
:ensure t
:bind
("M-s" . avy-goto-char))
Here I shall collect self-made functions that make editing text easier.
I can barely contain my joy. This extension allows you to quickly mark the next occurence of a region and edit them all at once. Wow!
(use-package mark-multiple
:ensure t
:bind ("C-c q" . 'mark-next-like-this))
Why on earth does a function called kill-word
not .. kill a word.
It instead deletes characters from your cursors position to the end of the word,
let’s make a quick fix and bind it properly.
(defun daedreth/kill-inner-word ()
"Kills the entire word your cursor is in. Equivalent to 'ciw' in vim."
(interactive)
(forward-char 1)
(backward-word)
(kill-word 1))
(global-set-key (kbd "C-c w k") 'daedreth/kill-inner-word)
And again, the same as above but we make sure to not delete the source word.
(defun daedreth/copy-whole-word ()
(interactive)
(save-excursion
(forward-char 1)
(backward-word)
(kill-word 1)
(yank)))
(global-set-key (kbd "C-c w c") 'daedreth/copy-whole-word)
Regardless of where your cursor is, this quickly copies a line.
(defun daedreth/copy-whole-line ()
"Copies a line without regard for cursor position."
(interactive)
(save-excursion
(kill-new
(buffer-substring
(point-at-bol)
(point-at-eol)))))
(global-set-key (kbd "C-c l c") 'daedreth/copy-whole-line)
And this quickly deletes a line.
(global-set-key (kbd "C-c l k") 'kill-whole-line)
Emacs is at it’s best when it just does things for you, shows you the way, guides you so to speak. This can be best achieved using a number of small extensions. While on their own they might not be particularly impressive. Together they create a nice environment for you to work in.
Quickly edit ~/.emacs.d/config.org
(defun config-visit ()
(interactive)
(find-file "~/.emacs.d/config.org"))
(global-set-key (kbd "C-c e") 'config-visit)
Simply pressing Control-c r
will reload this file, very handy.
You can also manually invoke config-reload
.
(defun config-reload ()
"Reloads ~/.emacs.d/config.org at runtime"
(interactive)
(org-babel-load-file (expand-file-name "~/.emacs.d/config.org")))
(global-set-key (kbd "C-c r") 'config-reload)
Emacs treats camelCase strings as a single word by default, this changes said behaviour.
(global-subword-mode 1)
If you write any code, you may enjoy this. Typing the first character in a set of 2, completes the second one after your cursor. Opening a bracket? It’s closed for you already. Quoting something? It’s closed for you already.
You can easily add and remove pairs yourself, have a look.
(setq electric-pair-pairs '(
(?\{ . ?\})
(?\( . ?\))
(?\[ . ?\])
(?\" . ?\")
))
And now to enable it
(electric-pair-mode t)
While changing buffers or workspaces, the first thing you do is look for your cursor. Unless you know its position, you can not move it efficiently. Every time you change buffers, the current position of your cursor will be briefly highlighted now.
(use-package beacon
:ensure t
:config
(beacon-mode 1))
Mostly useful if you are into web development or game development. Every time emacs encounters a hexadecimal code that resembles a color, it will automatically highlight it in the appropriate color. This is a lot cooler than you may think.
(use-package rainbow-mode
:ensure t
:init
(add-hook 'prog-mode-hook 'rainbow-mode))
I forgot about that initially, it highlights matching parens when the cursor is just behind one of them.
(show-paren-mode 1)
Colors parentheses and other delimiters depending on their depth, useful for any language using them, especially lisp.
(use-package rainbow-delimiters
:ensure t
:init
(add-hook 'prog-mode-hook #'rainbow-delimiters-mode))
A pretty simple package, takes your cursor and semantically expands the region, so words, sentences, maybe the contents of some parentheses, it’s awesome, try it out.
(use-package expand-region
:ensure t
:bind ("C-q" . er/expand-region))
On the list of things I like doing, deleting big whitespaces is pretty close to the bottom. Backspace or Delete will get rid of all whitespace until the next non-whitespace character is encountered. You may not like it, thus disable it if you must, but it’s pretty decent.
(use-package hungry-delete
:ensure t
:config
(global-hungry-delete-mode))
A nifty little package that kills all text between your cursor and a selected character.
A lot more useful than you might think. If you wish to include the selected character in the killed region,
change zzz-up-to-char
into zzz-to-char
.
(use-package zzz-to-char
:ensure t
:bind ("M-z" . zzz-up-to-char))
There is a lot of customization to the kill ring, and while I have not used it much before, I decided that it was time to change that.
The default is 60, I personally need more sometimes.
(setq kill-ring-max 100)
Out of all the packages I tried out, this one, being the simplest, appealed to me most. With a simple M-y you can now browse your kill-ring like browsing autocompletion items. C-n and C-p totally work for this.
(use-package popup-kill-ring
:ensure t
:bind ("M-y" . popup-kill-ring))
Minor, non-completion related settings and plugins for writing code.
(use-package yasnippet
:ensure t
:config
(use-package yasnippet-snippets
:ensure t)
(yas-reload-all))
(use-package flycheck
:ensure t)
I set the delay for company mode to kick in to half a second, I also make sure that it starts doing its magic after typing in only 2 characters.
I prefer C-n
and C-p
to move around the items, so I remap those accordingly.
(use-package company
:ensure t
:config
(setq company-idle-delay 0)
(setq company-minimum-prefix-length 3))
(with-eval-after-load 'company
(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))
Be it for code or prose, completion is a must.
After messing around with auto-completion
and company
for a while I decided to .. use both?
AC is for Lua/LÖVE and Company for the rest.
Each category also has additional settings.
(add-hook 'c++-mode-hook 'yas-minor-mode)
(add-hook 'c-mode-hook 'yas-minor-mode)
(use-package flycheck-clang-analyzer
:ensure t
:config
(with-eval-after-load 'flycheck
(require 'flycheck-clang-analyzer)
(flycheck-clang-analyzer-setup)))
(with-eval-after-load 'company
(add-hook 'c++-mode-hook 'company-mode)
(add-hook 'c-mode-hook 'company-mode))
(use-package company-c-headers
:ensure t)
(use-package company-irony
:ensure t
:config
(setq company-backends '((company-c-headers
company-dabbrev-code
company-irony))))
(use-package irony
:ensure t
:config
(add-hook 'c++-mode-hook 'irony-mode)
(add-hook 'c-mode-hook 'irony-mode)
(add-hook 'irony-mode-hook 'irony-cdb-autosetup-compile-options))
(add-hook 'python-mode-hook 'yas-minor-mode)
(add-hook 'python-mode-hook 'flycheck-mode)
(with-eval-after-load 'company
(add-hook 'python-mode-hook 'company-mode))
(use-package company-jedi
:ensure t
:config
(require 'company)
(add-to-list 'company-backends 'company-jedi))
(defun python-mode-company-init ()
(setq-local company-backends '((company-jedi
company-etags
company-dabbrev-code))))
(use-package company-jedi
:ensure t
:config
(require 'company)
(add-hook 'python-mode-hook 'python-mode-company-init))
(add-hook 'emacs-lisp-mode-hook 'eldoc-mode)
(add-hook 'emacs-lisp-mode-hook 'yas-minor-mode)
(add-hook 'emacs-lisp-mode-hook 'company-mode)
(use-package slime
:ensure t
:config
(setq inferior-lisp-program "/usr/bin/sbcl")
(setq slime-contribs '(slime-fancy)))
(use-package slime-company
:ensure t
:init
(require 'company)
(slime-setup '(slime-fancy slime-company)))
(add-hook 'shell-mode-hook 'yas-minor-mode)
(add-hook 'shell-mode-hook 'flycheck-mode)
(add-hook 'shell-mode-hook 'company-mode)
(defun shell-mode-company-init ()
(setq-local company-backends '((company-shell
company-shell-env
company-etags
company-dabbrev-code))))
(use-package company-shell
:ensure t
:config
(require 'company)
(add-hook 'shell-mode-hook 'shell-mode-company-init))
I must regrettably admit that company
sucks with Lua/LÖVE.
Which is also why I have AC
now.
I needed to do some shenanigans to get great auto-completion but here goes.
(add-hook 'lua-mode-hook 'yas-minor-mode)
(add-hook 'lua-mode-hook 'flycheck-mode)
;;; this will download the necessary modules from git
(let (value)
(dolist (element '("love" "lua") value)
(unless (file-directory-p (concatenate 'string (getenv "HOME") "/.emacs.d/auto-complete-" element))
(shell-command (format "git clone %s %s" (concatenate 'string
"https://github.com/rolpereira/auto-complete-" element ".el")
(concatenate 'string (getenv "HOME") "/.emacs.d/auto-complete-" element) nil)))
(add-to-list 'load-path (expand-file-name (concatenate 'string "~/.emacs.d/auto-complete-" element)))))
(require 'auto-complete-love)
(require 'auto-complete-lua)
;;; repl!
(add-hook 'lua-mode-hook '(lambda ()
(local-set-key (kbd "C-c C-s") 'lua-show-process-buffer)
(local-set-key (kbd "C-c C-h") 'lua-hide-process-buffer)))
;;; ac > company
(use-package auto-complete
:ensure t
:config
(setq ac-use-menu-map t)
(setq ac-ignore-case nil)
(define-key ac-menu-map "\C-n" 'ac-next)
(define-key ac-menu-map "\C-p" 'ac-previous))
;;; this will be changed, it's good enough for now
(add-hook 'lua-mode-hook '(lambda ()
(setq ac-sources '(ac-source-love
ac-source-lua
ac-source-abbrev
ac-source-words-in-same-mode-buffers))
(auto-complete-mode)))
(add-hook 'lua-mode-hook 'auto-complete-mode)
;;; I don't even know all the functionality
(use-package love-minor-mode
:ensure t
:config
(add-hook 'lua-mode-hook 'love-minor-mode))
;;; behold, perfection :°)
(global-set-key (kbd "<f9>") '(lambda () (interactive) (start-process "love-play-game" nil "love" default-directory)))
Countless are the times where I opened ansi-term to use git
on something.
These times are also something that I’d prefer stay in the past, since magit
is
great. It’s easy and intuitive to use, shows its options at a keypress and much more.
(use-package magit
:ensure t
:config
(setq magit-push-always-verify nil)
(setq git-commit-summary-max-length 50)
:bind
("M-g" . magit-status))
I have no need to directly edit files over SSH, but what I do need is a way to edit files as root. Opening up nano in a terminal as root to play around with grubs default settings is a no-no, this solves that.
Pretty self-explanatory, useful as hell if you use exwm.
(use-package sudo-edit
:ensure t
:bind
("s-e" . sudo-edit))
One of the absolute greatest features of emacs is called “org-mode”. This very file has been written in org-mode, a lot of other configurations are written in org-mode, same goes for academic papers, presentations, schedules, blogposts and guides. Org-mode is one of the most complex things ever, lets make it a bit more usable with some basic configuration.
Those are all rather self-explanatory.
(setq org-ellipsis " ")
(setq org-src-fontify-natively t)
(setq org-src-tab-acts-natively t)
(setq org-confirm-babel-evaluate nil)
(setq org-export-with-smart-quotes t)
(setq org-src-window-setup 'current-window)
(add-hook 'org-mode-hook 'org-indent-mode)
(use-package htmlize
:ensure t)
(add-hook 'org-mode-hook
'(lambda ()
(visual-line-mode 1)))
(global-set-key (kbd "C-c '") 'org-edit-src-code)
Makes it all look a bit nicer, I hate looking at asterisks.
(use-package org-bullets
:ensure t
:config
(add-hook 'org-mode-hook (lambda () (org-bullets-mode))))
Hitting tab after an “<el” in an org-mode file will create a template for elisp insertion.
(add-to-list 'org-structure-template-alist
'("el" "#+BEGIN_SRC emacs-lisp\n?\n#+END_SRC"))
One of the best things about org is the ability to export your file to many formats. Here is how we add more of them!
(when (file-directory-p "/usr/share/emacs/site-lisp/tex-utils")
(add-to-list 'load-path "/usr/share/emacs/site-lisp/tex-utils")
(require 'xdvi-search))
(use-package ox-twbs
:ensure t)
Your modeline is sacred, and if you have a lot of modes enabled, as you will if you use this config,
you might end up with a lot of clutter there, the package diminish
disables modes on the mode line but keeps
them running, it just prevents them from showing up and taking up space.
THIS WILL BE REMOVED SOON AS USE-PACKAGE HAS THE FUNCTIONALITY BUILT IN
Edit this list as you see fit!
(use-package diminish
:ensure t
:init
(diminish 'which-key-mode)
(diminish 'linum-relative-mode)
(diminish 'hungry-delete-mode)
(diminish 'visual-line-mode)
(diminish 'subword-mode)
(diminish 'beacon-mode)
(diminish 'irony-mode)
(diminish 'page-break-lines-mode)
(diminish 'auto-revert-mode)
(diminish 'rainbow-delimiters-mode)
(diminish 'rainbow-mode)
(diminish 'yas-minor-mode)
(diminish 'flycheck-mode)
(diminish 'helm-mode))
I like IRC, I also like other protocols but I enjoy IRC most, it’s obvious that I long
for a way to do my messaging from within emacs.
There is plenty of IRC clients in the repositories, and some more in the emacs repositories
but I find that the default erc
does the job best, it’s easy to use and offers some conveniences
that more sophisticated ones don’t, so I use it.
You might want to edit the default nick, it’s password protected anyway so don’t bother.
This also hides some of the channel messages to avoid cluttering the buffer. The other line changes the prompt for each channel buffer to match the channel name, this way you always know who you are typing to.
(setq erc-nick "daedreth")
(setq erc-prompt (lambda () (concat "[" (buffer-name) "]")))
(setq erc-hide-list '("JOIN" "PART" "QUIT"))
What it says on the tin, this changes the erc
history to include the server I connect to often.
(setq erc-server-history-list '("irc.freenode.net"
"localhost"))
You can even highlight nicks to make the buffers a bit more visually pleasing and easier to look at.
(use-package erc-hl-nicks
:ensure t
:config
(erc-update-modules))
Memes, but it’s fun and tiny.
(use-package elcord
:ensure t)
Why bother with an external media manager when emacs is a thing. EMMS is huge, incredibly powerful and luckily well documented. All I need it for is to play music and video, that’s it. I also need it to display metadata on the modeline correctly, which it does with mpd automatically.
There is many backends, many players and codecs for EMMS, we use mpd now.
The non XF86 keys are made to be somewhat logical to follow and easy to remember. At the bottom part of the configuration, you will notice how XF86 keys are used by default, so unless you keyboard is broken it should work out of the box. Obviously you might have to adjust server-name and server-port to fit your configuration.
(use-package emms
:ensure t
:config
(require 'emms-setup)
(require 'emms-player-mpd)
(emms-all) ; don't change this to values you see on stackoverflow questions if you expect emms to work
(setq emms-seek-seconds 5)
(setq emms-player-list '(emms-player-mpd))
(setq emms-info-functions '(emms-info-mpd))
(setq emms-player-mpd-server-name "localhost")
(setq emms-player-mpd-server-port "6601")
:bind
("s-m p" . emms)
("s-m b" . emms-smart-browse)
("s-m r" . emms-player-mpd-update-all-reset-cache)
("<XF86AudioPrev>" . emms-previous)
("<XF86AudioNext>" . emms-next)
("<XF86AudioPlay>" . emms-pause)
("<XF86AudioStop>" . emms-stop))
We use non-default settings for the socket, to use the built in mpc
functionality we need to set up a variable.
Adjust according to your setup.
(setq mpc-host "localhost:6601")
If you have an absolutely massive music library, it might be a good idea to get rid of mpc-update
and only invoke it manually when needed.
(defun mpd/start-music-daemon ()
"Start MPD, connects to it and syncs the metadata cache."
(interactive)
(shell-command "mpd")
(mpd/update-database)
(emms-player-mpd-connect)
(emms-cache-set-from-mpd-all)
(message "MPD Started!"))
(global-set-key (kbd "s-m c") 'mpd/start-music-daemon)
(defun mpd/kill-music-daemon ()
"Stops playback and kill the music daemon."
(interactive)
(emms-stop)
(call-process "killall" nil nil nil "mpd")
(message "MPD Killed!"))
(global-set-key (kbd "s-m k") 'mpd/kill-music-daemon)
(defun mpd/update-database ()
"Updates the MPD database synchronously."
(interactive)
(call-process "mpc" nil nil nil "update")
(message "MPD Database Updated!"))
(global-set-key (kbd "s-m u") 'mpd/update-database)