Literate Emacs configuration style. This is part of my journey for Teach myself programming in 10 years. I’m using this configuration on a system running Arch Linux and Emacs 26.3, that’s why this configuration will be using the latest features and not check for the system it’s running on.
Every emacs user should write his own configuration file and steal as many code as he wants from here (or elsewhere).
(unless (equal user-login-name "wand")
(warn "Please don't load Wand init file, it probably won't work for you.")
(with-current-buffer " *load*"
(goto-char (point-max))))
Very nice text about Why should you share your dotfiles. I will try to record all relevant configuration of my current box in the literate style here.
Really trying to avoid another Emacs Bankruptcy. o/
- Packages
- Dependencies
- Operating System
- Aesthetics
- Defaults
- Hydras
- Help and Info
- Editing Text
- Completions
- Window
- Alerts
- Dired
- Bookmark
- TRAMP
- Version Control
- Search
- Shell
- General Programming
- Additional Major Modes
- Org mode
- literate programming
- table of contents
- configuration
- habits
- [[#documentation-repeated-tasks][[documentation] repeated tasks]]
- journal
- presentation
- agenda
- beamer
- tips and tricks
- Second brain
- Tip of the day
- Projects
- Spelling
- Snippets
- Docker
- Social Networks
- Weather
- Pomodoro
- Financial
- Guru
- Advice
- Recording
- Logs
- Custom Functions
- Keys
- Emms
- Exwm
- References
Emacs facility to download and install “packages” that implement
additional features. You can find information about a specific
package by using C-h P
that prompts for the name and shows more
details.
There is a very detail package in Emacs help system that you can find on info:emacs#Packages.
I always start a new configuration setup with a naive mindset that I will not install thousands of external packages, however they are so good and make our life so much easier that is hard to avoid them altogether.
Let’s initialize the package system.
(require 'package)
(unless (bound-and-true-p package--initialized)
(package-initialize))
Despite the fact that GNU Elpa, the standard repository, of Emacs packages maintained by the core team already have many different packages, I like to use another external repository called Melpa which is currently maintained by the community and curated by Purcell’s and his team.
(add-to-list 'package-archives
'("melpa" . "https://melpa.org/packages/") t)
(add-to-list 'package-archives
'("melpa-stable" . "https://stable.melpa.org/packages/") t)
We need to refresh the archives to make this change to take place.
(unless (file-exists-p "~/.emacs.d/elpa/archives/melpa")
(package-refresh-contents))
Also, by default Emacs also automatically loads all installed packages in subsequent Emacs session. I want to disable it.
(setq package-enable-at-startup nil)
When you have more than a dozen packages, it makes the process of
managing them very difficult without any additional help. And by my
experience the only real issue is due to performance because you
will inevitably have many external packages loaded in situations
where you don’t need it. Fortunately, Jon Wiegley made our lives
easier by creating use-package
, please look for C-h P
use-package
to more details.
(unless (package-installed-p 'use-package)
(package-refresh-contents)
(package-install 'use-package)
(package-install 'delight))
(require 'use-package)
We can add new keywords to use-package
, stolen from here.
(defmacro bk-use-package-keywords-add (keyword)
"Add new keyword as placeholder"
`(progn
(add-to-list 'use-package-keywords ,keyword 'append)
(defun ,(intern (format "use-package-normalize/%s" keyword)) (&rest _))
(defun ,(intern (format "use-package-handler/%s" keyword)) (&rest _))))
(bk-use-package-keywords-add :about)
(bk-use-package-keywords-add :homepage)
Some old packages simply are not in any repository, they are only
elisp files distributed over the web. I will place these files
inside a folder called lisps
.
(setq site-lisps-dir (expand-file-name "lisps" user-emacs-directory))
(dolist (project (directory-files site-lisps-dir t "\\w+"))
(when (file-directory-p project)
(add-to-list 'load-path project)))
List of external packages that I rely on in my daily basis
Scrot (SCReenshOT) is a screenshot capturing utility that uses the imlib2 library to acquire and save images. By default, the captured file is saved with a date-stamped filename in the current directory, although you can also explicitly specify the name of the captured images when the command is run.
Generic command to help us out here!
(defun bk/scrot-cmd (cmd name folder)
"Scrot CMD to be executed and saving to the correct picture NAME in the FOLDER.
Folder is a symbol recognizing the folder name."
(interactive)
(let* ((folder-path (cl-case folder
(:window "/home/wand/Pictures/window/")
(:region "/home/wand/Pictures/region/")
))
(filepath (concat folder-path name ".png"))
(scrot-cmd (format "scrot %s %s -e 'xclip -selection c -t image/png < $f'" cmd filepath)))
(start-process-shell-command "pt" nil scrot-cmd)))
Capture the print screen of the current window
(defun bk/print-window ()
"Print current window."
(interactive)
(let ((print-name (read-from-minibuffer "Print name: ")))
(bk/scrot-cmd "" print-name :window)))
Print screens are way to serious, right? Take that region
(defun bk/print-region ()
"Print screen interactively."
(interactive)
(let ((print-name (read-from-minibuffer "Print name: ")))
(bk/scrot-cmd "-s" print-name :region)))
(eval-after-load 'exwm
'(exwm-input-set-key (kbd "<print>") #'bk/print-region))
I also need to go fast to these folders, no more:
C-x C-j /home C-s Pictures RET {window,region}
o.O
(set-register ?w '(file . "~/Pictures/window"))
(set-register ?r '(file . "~/Pictures/region"))
I use the external package called xscreensaver
which is amazing.
You can lock the screen by pressing s-l
or calling M-x
bk/lock-screen
.
Emacs zone is also an happy surprise for me. It seems like this is a default mode to ‘zones’ Emacs out by choosing one of its random modes to obfuscate the current buffer, which can then be used as a screensaver.
I will add some configuration for this.
(use-package zone
:ensure nil
:config
(defvar zone--window-config nil)
(defadvice zone (before zone-ad-clean-ui)
"Maximize window before `zone' starts."
(setq zone--window-config (current-window-configuration))
(delete-other-windows)
(when (and (eq window-system 'x) (executable-find "xtrlock"))
(start-process "xtrlock" nil "xtrlock")))
(defadvice zone (after zone-ad-restore-ui)
"Restore window configuration."
(when zone--window-config
(set-window-configuration zone--window-config)
(setq zone--window-config nil)))
(ad-activate 'zone))
I also installed xtrlock
so when I activate zone
I also lock
my screen. In order to unlock you just need to start typing the
correct password and press RET
.
Dunst is a lightweight replacement for the notification-daemons provided by most desktop environments. Dunst allows for the use of HTML markup in notifications, some examples are bold, italics, strike-though, and underline.
The relevant bits of my .dunstrc
.
[global]
font = Source Code Pro Medium
[urgency_low]
# IMPORTANT: colors have to be defined in quotation marks.
# Otherwise the "#" and following would be interpreted as a comment.
frame_color = "#3B7C87"
foreground = "#3B7C87"
background = "#191311"
timeout = 8
[urgency_normal]
frame_color = "#5B8234"
foreground = "#5B8234"
background = "#191311"
timeout = 10
[urgency_critical]
frame_color = "#B7472A"
foreground = "#B7472A"
background = "#191311"
timeout = 12
mpv is a free (as in freedom) media player for the command line. It supports a wide variety of media file formats, audio, and video codecs, and subtitle types.
On screen controller, while mpv strives for minimalism and provides no real GUI, it has a small controller on top of the video for basic control.
A keyboard-driven, vim-like browser based on PyQt5 web browser with a minimal GUI.
I met this project back at the university in 2012 and is hard to remember but I think it was the first time that I talked to other programmers online with attempts to report bugs and errors for the maintainers of this browser. Very nice project.
The cheat sheet is very important.
The following file is not my complete config.py
file for
qutebrowser, only the diff from defaults. If you want to create a
default config file, you should use :config-write-py --default
.
# Always restore open sites when qutebrowser is reopened.
# Type: Bool
c.auto_save.session = False
# Show javascript alerts
# Type: Bool
c.content.javascript.alert = False
# Allow websites to record audio/video
c.content.media_capture = 'ask'
# Allow websites to lock your mouse
c.content.mouse_lock = True
# Allow websites to show notifications
c.content.notifications = False
## Open a new window for every tab.
## Type: Bool
c.tabs.tabs_are_windows = True
You know, that time when the internet tells you: “you can’t see this page without a google-based product today”
Functionalities that interface directly with the underlying operating system.
Asynchronous bytecode compilation and various other actions makes Emacs look SIGNIFICANTLY less often which is a good thing.
(use-package async
:ensure t
:defer t
:init
(dired-async-mode 1)
(async-bytecomp-package-mode 1)
:custom (async-bytecomp-allowed-packages '(all)))
Teach Emacs about the PATH of the underlying OS.
(setenv "PATH" (concat (getenv "PATH") ":/home/wand/private/scripts"))
(setq exec-path (append exec-path '("/home/wand/private/scripts")))
(setenv "PATH" (concat (getenv "PATH") ":/usr/local/bin"))
(setq exec-path (append exec-path '("/usr/local/bin")))
(setenv "LD_LIBRARY_PATH" (concat (getenv "LD_LIBRARY_PATH") ":/usr/local/lib"))
(setq exec-path (append exec-path '("/usr/local/lib")))
I’ve been using qutebrowser as my main browser for more than one year now. Idk, I like keyboard centric products.
(setq browse-url-browser-function 'browse-url-generic
browse-url-generic-program "qutebrowser")
Fix old security Emacs problems
(eval-after-load "enriched"
'(defun enriched-decode-display-prop (start end &optional param)
(list start end)))
(use-package emacs
:ensure nil
:config
(setq compilation-always-kill t
compilation-ask-about-save nil
compilation-context-lines 10
compilation-window-height 100
compilation-scroll-output 'first-error))
A tiny system monitor that can be enabled or disabled at runtime, useful for checking performance with power-hungry processes in ansi-term.
(use-package symon
:ensure t
:defer t)
Built in htop
.
(setq proced-auto-update-flag t
proced-auto-update-interval 1
proced-descend t)
Garbage collection shouldn’t happen during startup, as what will slow Emacs down. Do it later.
Change the default values.
(defvar file-name-handler-alist-old file-name-handler-alist)
(setq-default gc-cons-threshold 402653184
file-name-handler-alist nil
gc-cons-percentage 0.6
auto-window-vscroll nil
message-log-max 16384)
(add-hook 'after-init-hook
`(lambda ()
(setq file-name-handler-alist file-name-handler-alist-old
gc-cons-threshold 800000
gc-cons-percentage 0.1)
(garbage-collect)) t)
Ease the font caching during GC.
(setq inhibit-compacting-font-caches t)
Emacs can inform us when the garbage collection is happening. I do not want to see this anymore… it was useful to understand the behavior for configuration.
(setq garbage-collection-messages nil)
Enforce a sneaky GC strategy to minimize GC interference with the activity. During normal use a high GC threshold is set, when idling GC is immediately triggered and a low threshold is set.
(use-package gcmh
:ensure t
:disabled t
:init
(setq gcmh-verbose nil)
:config
(gcmh-mode 1))
Very interesting package that help us to have some instances of external processes running and keep track of it all. I often need to enable the VPN of my company to work remotely, this suits nicely.
(use-package prodigy
:ensure t
:config
(prodigy-define-service
:name "Captalys VPN"
:command "captalys-vpn"
:tags '(captalys)
:stop-signal 'sigkill
:kill-process-buffer-on-stop t)
(prodigy-define-service
:name "Blog"
:command "lein ring server"
:cwd "~/bartuka-blog"
:stop-signal 'sigkill
:tags '(blog)
:kill-process-buffer-on-stop t)
(prodigy-define-tag
:name 'captalys
:ready-message "Initialization Sequence Completed")
(prodigy-define-tag
:name 'blog
:ready-message "Started server on port 3000"))
Look and fill of several aspects of Emacs: mode-line, fonts, specific faces, fringe, and more.
Since I never use the mouse with GNU Emacs, I prefer not to use invasive graphical elements.
(when window-system
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1))
Emacs convention is to show help and other inline documentation in the message area. Show help there instead of OS tooltip.
(when (display-graphic-p)
(tooltip-mode -1))
Let’s remove some crunchy messages at startup time.
(setq inhibit-splash-screen t
inhibit-startup-echo-area-message t)
Enabling some builtin modes that are very helpful e.g. highlight the positions of open/close of parenthesis, prettify symbols for now basically converts a fn to a lambda symbol, but I intend to expand the list of converted symbols.
(show-paren-mode t)
(global-prettify-symbols-mode t)
(blink-cursor-mode 0)
(use-package simple
:ensure nil
:delight auto-fill-mode
:config
(add-hook 'text-mode-hook #'auto-fill-mode))
The color theme is always a complicated matter. I’ve been trying
several ones, most recently I had settle with Protesilaos
modus-{operandi,vivendi}
packages, but now I want to try dakrone
for a while. deprecated
already. I will be using the default
white one.
Find out what face something at point have.
(defun what-face (pos)
(interactive "d")
(let ((face (or (get-char-property (point) 'read-face-name)
(get-char-property (point) 'face))))
(if face (message "Face: %s" face) (message "No face at %d" pos))))
Change the highlight color for selection text.
(set-face-attribute 'region nil :background "#D5F0D5")
Make cursor the width of the character it is under.
(setq x-stretch-cursor t)
Allow only one theme at a time
(setq custom-theme-allow-multiple-selections nil)
Set the custom theme path
(setq custom-theme-directory (concat user-emacs-directory "themes"))
(dolist
(path (directory-files custom-theme-directory t "\\w+"))
(when (file-directory-p path)
(add-to-list 'custom-theme-load-path path)))
(use-package load-theme-buffer-local
:ensure nil
:about I am using a custom version located at /lisps folder of this setup.
:commands (load-theme-buffer-local))
A light theme with a light-green background.
I enjoyed so much this theme, that I started contributing to the source code. Right now, I am modernizing the structure and color pallets to be more organized and comprehensive.
Real dark theme.
(use-package cyberpunk-theme
:ensure t
:disabled t
:config
(load-theme 'cyberpunk t))
(use-package zenburn-theme
:ensure t
:disabled t
:config
(load-theme 'zenburn t))
(use-package monokai-theme
:ensure t
:defer t)
(use-package color-theme-sanityinc-tomorrow
:ensure t
:defer t)
Let’s activate the default theme, I might change this very often.
(add-hook 'after-init-hook
(lambda ()
(interactive)
(load-theme 'organic-green t)))
(use-package time
:ensure nil
:init
(setq display-time-default-load-average nil
display-time-format "%Hh%M "
display-time-day-and-date t)
:config
(display-time-mode t))
redefine the size of the font.
(when (member "Monaco" (font-family-list))
(set-face-attribute 'default nil :font "Monaco" :height 120)
(setq default-frame-alist '((font . "Monaco-12"))))
This package implements a menu that lists enabled minor-modes, as well as commonly but not currently enabled minor-modes. It can be used to toggle local and global minor-modes, to access mode-specific menus, and to get help about modes.
(use-package minions
:ensure t
:config
(minions-mode 1))
Control the fringe around the frame.
(fringe-mode '(10 . 1))
Preview line numbers when prompting for line number.
(define-advice goto-line (:before (&rest _) preview-line-number)
"Preview line number when prompting for goto-line."
(interactive
(lambda (spec)
(if (and (boundp 'display-line-numbers)
(not display-line-numbers))
(unwind-protect
(progn (display-line-numbers-mode)
(advice-eval-interactive-spec spec))
(display-line-numbers-mode -1))
(advice-eval-interactive-spec spec)))))
Many changes in the default behavior of Emacs, not able to group anywhere else.
I ran into this little tidbit while reading Sacha Chu’a posts from
Emacs. You can find the whole discussion here but the idea is that
next-line
defun triggers line-move-partial
which leads to
excessive processing. By setting the variable below, the speed of
using next-line
gets very cut down.
(setq auto-window-vscroll nil)
Do not clutter my init.el
file with customized variables.
(setq custom-file (expand-file-name "custom.el" user-emacs-directory))
(when (file-exists-p custom-file)
(load custom-file))
Show current key-sequence in minibuffer, like vim does. Any feedback after typing is better UX than no feedback at all.
(setq echo-keystrokes 0.2)
Allow pasting selection outside of Emacs
(setq x-select-enable-clipboard t)
Say you copied a link from your web browser, then switched to Emacs to paste it somewhere. Before you do that, you notice something you want to kill. Doing that will place the last kill to the clipboard, thus overriding the thing you copied earlier. We can have a kill ring solution:
(setq save-interprogram-paste-before-kill t)
(setq custom-safe-themes t)
(defalias 'cquit 'cider-quit)
(defalias 'yes-or-no-p 'y-or-n-p)
;; built in htop
Don’t use tabs to indent and fix some indentation settings
(use-package emacs
:ensure nil
:config
(setq-default tab-always-indent 'complete)
(setq-default indent-tabs-mode nil
tab-width 4
fill-column 70))
Enable some built in modes to add critical functionality to Emacs. More explanation about them will follow in future.
(delete-selection-mode t)
(pending-delete-mode t)
(column-number-mode 1)
(global-auto-revert-mode)
;; real emacs knights don't use shift to mark things
(setq shift-select-mode nil)
set warning of opening large files to 100MB
(setq-default large-file-warning-threshold 100000000)
do not add double space after periods Real sentence in Emacs : emacs
(setq-default sentence-end-double-space nil)
more defaults
(setq-default user-mail-address "[email protected]"
user-full-name "Wanderson Ferreira"
disabled-command-function nil)
(setq ediff-diff-options "-w")
(setq ediff-split-window-function 'split-window-horizontally)
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
Word wrapping
(setq-default word-wrap t
truncate-lines t
truncate-partial-width-windows nil
sentence-end-double-space nil
delete-trailing-lines nil
require-final-newline t
tabify-regexp "^\t* [ \t]+")
Favor hard-wrapping in text modes
(defun bk/auto-fill ()
"My autofill setup for text buffers."
(auto-fill-mode t)
(delight 'auto-fill-mode))
(add-hook 'text-mode-hook #'bk/auto-fill)
This enables file backups to N versions of saves, as opposed to only backing up the very first save. I don’t re-launch emacs that often so this is necessary to get useful backups.
(setq backup-directory-alist `(("." . ,(concat user-emacs-directory "backups")))
vc-make-backup-files t
version-control t
kept-old-versions 0
kept-new-versions 10
delete-old-versions t
backup-by-copying t)
This is a built-in mode that keeps track of the files you have
opened allowing you go back to them faster. It can also integrate
with a completion framework to populate a virtual buffers
list.
(use-package recentf
:ensure nil
:init
(setq recentf-max-saved-items 50
recentf-max-menu-items 15
recentf-show-file-shortcuts-flag nil
recentf-auto-cleanup 'never)
:config
(recentf-mode t))
Save place remembers your location in a file when saving files.
(require 'saveplace)
(setq save-place-mode (expand-file-name "saveplace" user-emacs-directory))
(save-place-mode 1)
Keeps a record of actions involving the minibuffer. This is of paramount important to a fast and efficient workflow involving any completion framework that leverages the built-in mechanisms.
(use-package savehist
:ensure nil
:config
(setq savehist-file "~/.emacs.d/savehist"
history-length 30000
history-delete-duplicates nil
savehist-additional-variables '(search-ring
regexp-search-ring)
savehist-save-minibuffer-history t)
(savehist-mode 1))
Uniquify buffer names dependent on file name. Emacs’s traditional method for making buffer names unique adds <2>, <3>, etc to the end of (all but one of) the buffers. This settings change the default behavior.
(use-package uniquify
:ensure nil
:config
(setq uniquify-buffer-name-style 'post-forward-angle-brackets
uniquify-separator " * "
uniquify-after-kill-buffer-p t
uniquify-strip-common-suffix t
uniquify-ignore-buffers-re "^\\*"))
smex
is an improved version of extended-command
or M-x
(use-package smex
:ensure t
:config
(smex-initialize))
(use-package fix-word
:ensure t
:config
(global-set-key (kbd "M-u") #'fix-word-upcase)
(global-set-key (kbd "M-l") #'fix-word-downcase)
(global-set-key (kbd "M-c") #'fix-word-capitalize))
Emacs registers are compartments where you can save text, rectangles, positions, and other things for later use. Once you save text or a rectangle in a register, you can copy it into the buffer once or many times; once you save a position in a register, you can jump back to that position once or many times.
For more information: `C-h r’ and then letter i to search for registers and the amazing video from Protesilaos.
The prefix to all commands of registers is C-x r
command | description |
---|---|
M-x view-register R | see what register R contains |
C-x r s | save region to register |
C-x r i | insert text from a register |
C-x r n | record a number defaults to 0 |
C-x r + | increment a number from register |
C-x r SPC | record a position into register |
C-x r j | jump to positions or windows config |
C-x r w | save a window configuration |
C-x r f | save a frame configuration |
Important note: the data saved into the register is persistent as long as you don’t override it.
The way to specify a number, is to use an universal argument e.g. C-u <number> C-x n
Clean all the registers you saved.
(defun bk/clear-registers ()
"Remove all saved registers."
(interactive)
(setq register-alist nil))
(set-register ?e '(file . "~/.emacs.d/README.org"))
(set-register ?t '(file . "~/org/todo.org"))
(set-register ?c '(file . "~/.emacs.d/docs/cheatsheet.org"))
(use-package abbrev
:ensure nil
:delight abbrev-mode
:config
(setq-default abbrev-mode t))
(defun bk/add-region-local-abbrev (start end)
"Go from START to END and add the selected text to a local abbrev."
(interactive "r")
(if (use-region-p)
(let ((num-words (count-words-region start end)))
(add-mode-abbrev num-words)
(deactivate-mark))
(message "No selected region!")))
(global-set-key (kbd "C-x a l") 'bk/add-region-local-abbrev)
(defun bk/add-region-global-abbrev (start end)
"Go from START to END and add the selected text to global abbrev."
(interactive "r")
(if (use-region-p)
(let ((num-words (count-words-region start end)))
(add-abbrev global-abbrev-table "Global" num-words)
(deactivate-mark))
(message "No selected region!")))
(global-set-key (kbd "C-x a g") 'bk/add-region-global-abbrev)
Change some defaults of imenu
.
(require 'imenu)
(setq imenu-auto-rescan 1
imenu-auto-rescan-maxout 600000
imenu-max-item-length 600
imenu-use-markers t
imenu-max-items 200)
The objectives of this package is to provide a way to choose buffer indexes in a specific mode. What is a buffer index? Basically we have a function that will find “interesting” positions in your buffer that you might want to jump there, something like function definitions, headlines in outline mode, class definitions, etc.
(use-package imenu-anywhere
:ensure t)
It provides a way of filtering and then grouping the list of buffers that you currently have open. About the configuration below:
Default | Explanation |
---|---|
ibuffer-expert | Stop asking for confirmation after every action in Ibuffer |
ibyffer-auto-mode | Keeps the buffer list up to date |
(use-package ibuffer
:ensure nil
:init
(setq ibuffer-expert t)
(setq ibuffer-show-empty-filter-groups nil)
(setq ibuffer-saved-filter-groups
'(("Main"
("Directories" (mode . dired-mode))
("Rest" (mode . restclient-mode))
("Docker" (or
(mode . docker-compose-mode)
(mode . dockerfile-mode)))
("Programming" (or
(mode . clojure-mode)
(mode . emacs-lisp-mode)
(mode . sql-mode)
(mode . python-mode)))
("Browser" (or
(name . "qutebrowser:\*")
))
("Slack" (name . "*Slack"))
("Org" (mode . org-mode))
("Markdown" (or
(mode . markdown-mode)
(mode . gfm-mode)))
("Git" (or
(mode . magit-blame-mode)
(mode . magit-cherry-mode)
(mode . magit-diff-mode)
(mode . magit-log-mode)
(mode . magit-process-mode)
(mode . magit-status-mode)))
("Emacs" (or
(name . "^\\*Help\\*$")
(name . "^\\*Custom.*")
(name . "^\\*Org Agenda\\*$")
(name . "^\\*info\\*$")
(name . "^\\*scratch\\*$")
(name . "^\\*Backtrace\\*$")
(name . "^\\*Messages\\*$"))))))
:config
(add-hook 'ibuffer-mode-hook
(lambda ()
(ibuffer-auto-mode 1)
(ibuffer-switch-to-saved-filter-groups "Main"))))
Package ibuffer-vc
let you filter the Ibuffer by projects
definitions (in my case, every folder that has a .git
folder
inside is considered a project).
(use-package ibuffer-vc
:ensure t
:after ibuffer
:config
(define-key ibuffer-mode-map (kbd "/ V") 'ibuffer-vc-set-filter-groups-by-vc-root))
Increasing the width of each column in ibuffer. Some buffers names are very large in EXWM.
(setq ibuffer-formats
'((mark modified read-only " "
(name 60 60 :left :elide) ; change: 60s were originally 18s
" "
(size 9 -1 :right)
" "
(mode 16 16 :left :elide)
" " filename-and-process)
(mark " "
(name 16 -1)
" " filename)))
The following setting prevent the minibuffer to grow, therefore it will be always 1 line height.
(setq resize-mini-windows nil)
(setq max-mini-window-height 1)
Stole this from Sacha Chua’s configuration, sometimes you want to
be able to do fancy things with the text that you are entering
into the minibuffer. Sometimes you just want to be able to read
it, specially when it comes to lots of text. This binds C-M-e
in
a minibuffer.
(use-package miniedit
:ensure t
:config
(miniedit-install))
(use-package calendar
:ensure nil
:hook (calendar-today-visible . calendar-mark-today)
:config
(setq calendar-latitude -23.5475
calendar-longitude -46.63611
calendar-location-name "Sao_Paulo, Brazil"
calendar-mark-holidays-flag t))
Provide a nice keyboard interface to web pages of your choosing.
Adding urban dictionary to webjump.
(eval-after-load "webjump"
'(add-to-list 'webjump-sites '("Urban Dictionary" . [simple-query
"www.urbandictionary.com"
"http://www.urbandictionary.com/define.php?term="
""])))
(global-set-key (kbd "C-c j") 'webjump)
Auth Source is a generic interface for common backends such as your operating sysetm’s keychain and your local ~/.authinfo file. Auth Source solves the problem of mapping passwords and usernames to hosts.
Debugging auth issues
(setq auth-source-debug t)
We need to tell auth-source where to look for secrets.
(setq auth-sources '((:source "~/.emacs.d/secrets/.authinfo")))
GPG
(use-package pinentry :ensure t)
(use-package epa
:ensure nil
:config
(setq epa-pinentry-mode 'loopback)
(pinentry-start))
(put 'downcase-region 'disabled nil)
(put 'upcase-region 'disabled nil)
(put 'narrow-to-region 'disabled nil)
This package has a very nice name once you know what it does! This is a package that can be used to tie related commands into a family of short bindings with a common prefix - a Hydra.
(use-package hydra
:ensure t)
(use-package info
:ensure t
:bind ("C-h C-i" . info-lookup-symbol)
:config
(add-hook 'Info-mode-hook
#'(lambda ()
(setq buffer-face-mode '(:family "Bookerly"))
(buffer-face-mode)
(text-scale-adjust 1))))
(eval-after-load 'Info-mode
'(progn
<<info-hydras>>
<<info-define>>))
(define-key Info-mode-map "w" 'forward-word)
(define-key Info-mode-map "b" 'backward-word)
(define-key Info-mode-map "t" 'hydra-info-to/body)
(define-key Info-mode-map "u" 'Info-history-back)
(define-key Info-mode-map "H" 'Info-history-back)
(defun ora-Info-hook ())
(defun ora-open-info (topic bname)
"Open info on TOPIC in BNAME."
(if (get-buffer bname)
(progn
(switch-to-buffer bname)
(unless (string-match topic Info-current-file)
(Info-goto-node (format "(%s)" topic))))
(info topic bname)))
(defhydra hydra-info-to (:hint nil :color teal)
"
_o_rg e_l_isp _e_macs _h_yperspec"
("o" (ora-open-info "org" "*org info*"))
("l" (ora-open-info "elisp" "*elisp info*"))
("e" (ora-open-info "emacs" "*emacs info*"))
("h" (ora-open-info "gcl" "*hyperspec*")))
See also bidi-paragraph-direction
; setting that non-nil might
speed up redisplay.
(setq bidi-paragraph-direction 'left-to-right)
Default movement keys
Use M-{
and M-}
to move forward or backward by paragraph. Use
M-h
to mark (highlight) the current paragraph.
Since I am using EXWM, I might open very large files, there is a package to help Emacs handle this kind of files.
(use-package vlf
:ensure t
:defer t)
I found a good paper about log files
in Emacs where they mention
vlf
package. This paper is very worth reading nevertheless.
(use-package eldoc
:ensure nil
:delight eldoc-mode
:init
(setq eldoc-idle-delay 0.1
eldoc-echo-area-use-multiline-p nil)
(eldoc-mode 1)
:config
(add-hook 'prog-mode-hook 'turn-on-eldoc-mode))
(use-package subword
:ensure nil
:delight subword-mode)
(use-package change-inner
:homepage https://github.com/magnars/change-inner.el
:about vim's `ci' command, building on expand-region
:ensure t)
Expand or reduce region selection semantically. Supports all languages that I work with inside Emacs.
(use-package expand-region
:homepage https://github.com/magnars/expand-region.el
:about Extension to increase selected region by semantic units
:ensure t
:requires hydra
:init
(setq er--show-expansion-message t
expand-region-fast-keys-enabled nil)
:bind (("C-=" . er/expand-region)
("C-c =" . bk/expand-region/body))
:config
(defhydra bk/expand-region (:color pink :hint nil)
"
^Expand/Discard^ ^Mark^
─^──────────────^────────────────^────^─────────────────
_e_ or _+_: expand region _(_: inside pairs
_r_ or _-_: reduce region _)_: around pairs
_g_: exit hydrant _q_ or _'_: inside quotes
_G_: discard region, exit _Q_ or _\"_: around quotes
^ ^ ^ ^ _p_: paragraph"
("e" er/expand-region)
("+" er/expand-region)
("r" er/contract-region)
("-" er/contract-region)
("p" er/mark-paragraph)
("(" er/mark-inside-pairs)
(")" er/mark-outside-pairs)
("q" er/mark-inside-quotes)
("'" er/mark-inside-quotes)
("Q" er/mark-outside-quotes)
("\"" er/mark-outside-quotes)
("g" ignore :exit t)
("G" #'(lambda () (interactive) (deactivate-mark)) :exit t)))
(use-package avy
:homepage https://github.com/abo-abo/avy
:about Jump to things in Emacs tree-style
:ensure t
:config
(avy-setup-default)
:bind (("C-." . avy-goto-char-timer)))
avy-zap is a nice package to use zap to char with avy. Basically deleting everything from point to another char.
(use-package avy-zap
:ensure t
:bind (("M-z" . avy-zap-to-char-dwim)
("M-Z" . avy-zap-up-to-char-dwim)))
Emacs leaves a trail of breadcrumbs (the mark ring) through which we can navigate to hop around to places you’ve been in the buffer. A nice alternative is to move round through points at which you made edits in a buffer.
(use-package goto-chg
:ensure t
:config
(global-set-key (kbd "C-c b ,") 'goto-last-change)
(global-set-key (kbd "C-c b .") 'goto-last-change-reverse))
Now we can use C-c b ,
and C-c b .
to go back and forth
through the edit points in your buffer. It takes you through your
undo history without undoing anything.
Very often is useful to highlight some symbols.
(use-package highlight-symbol
:ensure t
:delight highlight-symbol-mode
:hook
((highlight-symbol-mode . highlight-symbol-nav-mode)
(prog-mode . highlight-symbol-mode))
:custom
(highlight-symbol-highlight-single-occurrence nil)
(highlight-symbol-idle-delay 0.25)
(highlight-symbol-on-navigation-p t))
(use-package browse-kill-ring
:ensure t
:commands browse-kill-ring)
No one knows why this is not the default already.
(prefer-coding-system 'utf-8)
(setq locale-coding-system 'utf-8)
(set-language-environment "UTF-8")
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
Multiple cursors is a very nice package that lets you create several cursors that all do the same thing as you type.
(use-package multiple-cursors
:ensure t
:bind
(("C->" . mc/mark-next-like-this)
("C-<" . mc/mark-previous-like-this)
("S-<mouse-1>" . mc/add-cursor-on-click)
("C-c m" . bk/hydra-multiple-cursors/body))
:requires hydra
:config
(defhydra bk/hydra-multiple-cursors (:hint nil :color pink)
"
^Select^ ^Discard^ ^Edit^ ^Navigate^
─^──────^─────────────────^───────^─────────────────────^────^───────────────^────────^─────────
_M-s_: split lines _M-SPC_: discard current _&_: align _(_: cycle backward
_s_: select regexp _b_: discard blank lines _#_: insert numbers _)_: cycle forward
_n_: select next _d_: remove duplicated ^ ^ ^ ^
_p_: select previous _q_ or _g_: exit hydrant ^ ^ ^ ^
_C_: select next line _G_: exit mc mode"
("M-s" mc/edit-ends-of-lines)
("s" mc/mark-all-in-region-regexp)
("n" mc/mark-next-like-this-word)
("p" mc/mark-previous-like-this-word)
("&" mc/vertical-align-with-space)
("(" mc/cycle-backward)
(")" mc/cycle-forward)
("M-SPC" mc/remove-current-cursor)
("b" mc/remove-cursors-on-blank-lines)
("d" mc/remove-duplicated-cursors)
("C" mc/mark-next-lines)
("#" mc/insert-numbers)
("q" mc/remove-duplicated-cursors :exit t)
("g" mc/remove-duplicated-cursors :exit t)
("G" mc/keyboard-quit :exit t)))
(use-package mc-extras
:ensure t
:after multiple-cursors)
To use mc/edit-lines
you need to highlight the lines on which you
wish to have cursors and use C-c m c
. Now you can edit away and
press enter when you are done to exit multiple cursors.
There is this amazing video from magnars showing off multiple cursors features.
However, occasionally the best way to get the cursors where you
want them is with the mouse. With the following code, C-S-<left
mouse click>
adds a new cursor.
Several helper functions to ease the day-to-day work of editing text.
Very nice default.
;; `C-a' first takes you to the first non-whitespace char as
;; `back-to-indentation' on a line, and if pressed again takes you to
;; the actual beginning of the line.
(defun smarter-move-beginning-of-line (arg)
"Move depending on ARG to beginning of visible line or not.
From https://emacsredux.com/blog/2013/05/22/smarter-navigation-to-the-beginning-of-a-line/."
(interactive "^p")
(setq arg (or arg 1))
(when (/= arg 1)
(let ((line-move-visual nil))
(forward-line (1- arg))))
(let ((orig-point (point)))
(back-to-indentation)
(when (= orig-point (point))
(move-beginning-of-line 1))))
(global-set-key [remap move-beginning-of-line] 'smarter-move-beginning-of-line)
I used it sometimes when yanking text written in Emacs to paste in other external apps such as gmail and I don’t want the “break line” to be at 70th column there.
(defun unfill-paragraph ()
"Takes a multi-line paragraph and makes it into a single line of text."
(interactive)
(let ((fill-column (point-max)))
(fill-paragraph nil)))
(defun unfill-region (beg end)
"Unfill the region, joining text paragraphs into a single logical line."
(interactive "*r")
(let ((fill-column (point-max)))
(fill-region beg end)))
(defun duplicate-current-line-or-region (arg)
"Duplicates the current line or region ARG times.
If there's no region, the current line will be duplicated."
(interactive "p")
(save-excursion
(if (region-active-p)
(duplicate-region arg)
(duplicate-current-line arg))))
(defun duplicate-region (num &optional start end)
"Duplicates the region bounded by START and END NUM times.
If no START and END is provided, the current region-beginning
region-end is used."
(interactive "p")
(let* ((start (or start (region-beginning)))
(end (or end (region-end)))
(region (buffer-substring start end)))
(goto-char start)
(dotimes (i num)
(insert region))))
(defun duplicate-current-line (num)
"Duplicate the current line NUM times."
(interactive "p")
(when (eq (point-at-eol) (point-max))
(goto-char (point-max))
(newline)
(forward-char -1))
(duplicate-region num (point-at-bol) (1+ (point-at-eol))))
Let’s bind the top level function to a sensible key.
(global-set-key (kbd "C-c 2") 'duplicate-current-line-or-region)
(use-package webpaste
:ensure t
:config
(setq webpaste-provider-priority '("ix.io" "dpaste.org")))
The optimal way of using Emacs is through searching and narrowing selection candidates. Spend less time worrying about where things are on the screen and more on how fast you can bring them into focus.
Minibuffer is the place for extended command interaction. Whether it is about input to a prompt, performing search, executing functions or dealing with buffers.
General config for the minibuffers
(use-package minibuffer
:ensure nil
:init
(setq completion-styles '(basic partial-completion)
completion-category-defaults nil
completion-cycle-threshold 3
completion-flex-nospace nil
completion-ignore-case t
read-buffer-completion-ignore-case t
read-file-name-completion-ignore-case t
completions-format 'vertical
enable-recursive-minibuffers t
read-answer-short t
resize-mini-windows t)
:config
(defun bk/describe-symbol-at-point (&optional arg)
"Get help (documentation) for the symbol at point."
(interactive "P")
(let ((symbol (symbol-at-point)))
(when symbol
(describe-symbol symbol)))
(when arg
(let ((help (get-buffer-window "*Help*")))
(when help
(if (not (eq (selected-window) help))
(select-window help)
(select-window (get-mru-window)))))))
(defun bk/focus-minibuffer ()
"Focus the active minibuffer."
(interactive)
(let ((mini (active-minibuffer-window)))
(when mini
(select-window mini))))
(defun bk/completions-kill-save-symbol ()
"Add symbol-at-point to the kill ring."
(interactive)
(kill-new (thing-at-point 'symbol)))
(file-name-shadow-mode t)
(minibuffer-depth-indicate-mode t)
(minibuffer-electric-default-mode t)
:bind (:map completion-list-mode-map
("h" . bk/describe-symbol-at-point)
("w" . bk/completions-kill-save-symbol)
("n" . next-line)
("p" . previous-line)
("f" . next-completion)
("b" . previous-completion)
("M-v" . bk/focus-minibuffer)))
Yeah, I migrated from Ido to Ivy. Let’s enjoy new features, speed, and comfort that Ivy provides.
(use-package ivy
:ensure t
:init
(setq ivy-use-virtual-buffers t
ivy-height 13
ivy-wrap t
ivy-magic-slash-non-match-action nil
ivy-initial-inputs-alist nil
ivy-on-del-error-function #'ignore
ivy-use-selectable-prompt t
ivy-display-style 'fancy
ivy-count-format "%d/%d "
ivy-virtual-abbreviate 'full
ivy-extra-directories '("./"))
:config
(ivy-mode +1))
(use-package counsel
:ensure t
:bind
(("M-x". counsel-M-x)
("C-x C-m" . counsel-M-x)
("C-x C-i" . counsel-imenu)
("C-c g p" . counsel-git-grep)
("C-x C-r" . counsel-recentf)))
(use-package icomplete
:ensure nil
:config
(icomplete-mode +1))
Company is a text completion framework for Emacs. The name stands for “complete anything”. It uses pluggable back-ends and front-ends to retrieve and display completion candidates.
(use-package company
:ensure t
:delight company-mode
:init
(setq company-show-numbers t
company-idle-delay 0.25
company-require-match 'never
company-dabbrev-downcase nil
company-dabbrev-ignore-case t
company-minimum-prefix-length 2
company-transformers '(company-sort-by-occurrence))
:config
(add-hook 'after-init-hook 'global-company-mode))
Also, we numbered all the candidates and the following code will enable us to choose the candidate based on its number. This solution was stolen from link with some customization and simplification to provide only what I saw useful.
(defun ora-company-number ()
"Choose the candidate based on his number at candidate list."
(interactive)
(let* ((k (this-command-keys))
(re (concat "^" company-prefix k)))
(if (cl-find-if (lambda (s) (string-match re s)) company-candidates)
(self-insert-command)
(company-complete-number (string-to-number k)))))
(defun ora-activate-number ()
"Activate the number-based choices in company."
(interactive)
(let ((map company-active-map))
(mapc
(lambda (x)
(define-key map (format "%d" x) 'ora-company-number))
(number-sequence 0 9))
;; (define-key map (kbd "<return>") nil)
))
(eval-after-load 'company
'(ora-activate-number))
(use-package company-quickhelp
:ensure t
:after company
:init
(setq company-quickhelp-delay nil)
:config
(company-quickhelp-mode t)
:bind (:map company-active-map
("M-h" . company-quickhelp-manual-begin)))
(use-package company-org-roam
:ensure t
:after company
:config
(push 'company-org-roam company-backends))
Hippie Expand is a more feature complete completion engine than the
default dabbrev engine. The main feature I use over dabbrev
is
that is supports a wide range of backends for finding completions -
dabbrev
only looks at currently open buffers.
(setq hippie-expand-try-functions-list
'(try-expand-dabbrev
try-expand-dabbrev-all-buffers
try-expand-dabbrev-from-kill
try-complete-file-name-partially
try-complete-file-name
try-expand-all-abbrevs
try-expand-list
try-expand-line
try-complete-lisp-symbol-partially
try-complete-lisp-symbol))
Then we override dabbrev-expand
’s keybinding to use
hippie-expand
instead.
(define-key (current-global-map) [remap dabbrev-expand] 'hippie-expand)
A window is an area of the screen that is used to display a buffer. In Emacs Lisp, windows are represented by a special Lisp object type.
Windows are grouped into frames. Each frame contains at least one window; the user can subdivide it into multiple, non-overlapping windows to view several buffers at once.
Emacs uses the word “window” with a different meaning than in graphical desktop environments and window systems, such as the X Window System. When Emacs is run on X, each of its graphical X windows is an Emacs frame. When Emacs is run on a text terminal, the frame fills the entire terminal screen.
Improve the scroll experience on Emacs
(defun bk/scroll-up ()
"Scroll only specific amount of lines. I don't like the defaults of whole screen."
(interactive)
(scroll-up-command 8))
(defun bk/scroll-down ()
"Scroll only specific amount of lines. I don't like the defaults of whole screen."
(interactive)
(scroll-down-command 8))
(global-set-key (kbd "C-v") #'bk/scroll-up)
(global-set-key (kbd "M-v") #'bk/scroll-down)
popwin is a popup window manager for Emacs which makes you free
from the hell of annoying buffers such like *Help*
,
*Completions*
, and etc.
(use-package popwin
:ensure t
:config
(push '("*cider-error*" :dedicated t :position bottom :stick t :noselect nil :height 0.4)
popwin:special-display-config)
(push '("*cider-doc*" :dedicated t :position bottom :stick t :noselect nil :height 0.4)
popwin:special-display-config)
(global-set-key (kbd "C-z") popwin:keymap)
(popwin-mode 1))
Key | Command |
---|---|
b | popwin:popup-buffer |
l | popwin:popup-last-buffer |
o | popwin:display-buffer |
C-b | popwin:switch-to-last-buffer |
C-p | popwin:original-pop-to-last-buffer |
C-o | popwin:original-display-last-buffer |
SPC | popwin:select-popup-window |
s | popwin:stick-popup-window |
0 | popwin:close-popup-window |
f, C-f | popwin:find-file |
e | popwin:messages |
C-u | popwin:universal-display |
1 | popwin:one-window |
Ease the task of changing window quickly.
(use-package ace-window
:ensure t
:config
(global-set-key (kbd "C-c o") 'ace-window))
Don’t popup certain buffers
(add-to-list 'display-buffer-alist
(cons "\\*Async Shell Command\\*.*"
(cons #'display-buffer-no-window nil)))
key | Function |
---|---|
s | aw-swap-window |
2 | aw-split-window-vert |
3 | aw-split-window-horz |
x | aw-delete-window |
?? | aw-dispatch-help |
Toggle window from:
Window A
++++++++
Window B
to
Window A : Window B
(defun toggle-window-split ()
(interactive)
(if (= (count-windows) 2)
(let* ((this-win-buffer (window-buffer))
(next-win-buffer (window-buffer (next-window)))
(this-win-edges (window-edges (selected-window)))
(next-win-edges (window-edges (next-window)))
(this-win-2nd (not (and (<= (car this-win-edges)
(car next-win-edges))
(<= (cadr this-win-edges)
(cadr next-win-edges)))))
(splitter
(if (= (car this-win-edges)
(car (window-edges (next-window))))
'split-window-horizontally
'split-window-vertically)))
(delete-other-windows)
(let ((first-win (selected-window)))
(funcall splitter)
(if this-win-2nd (other-window 1))
(set-window-buffer (selected-window) this-win-buffer)
(set-window-buffer (next-window) next-win-buffer)
(select-window first-win)
(if this-win-2nd (other-window 1))))))
(global-set-key (kbd "C-x |") 'toggle-window-split)
When splitting windows open the previous buffer in it.
(defun bk/vsplit-last-buffer ()
"Split the window vertically and display the previous buffer."
(interactive)
(split-window-vertically)
(other-window 1 nil)
(switch-to-next-buffer))
(defun bk/hsplit-last-buffer ()
"Split the window horizontally and display the previous buffer."
(interactive)
(split-window-horizontally)
(other-window 1 nil)
(switch-to-next-buffer))
(global-set-key (kbd "C-x 2") 'bk/vsplit-last-buffer)
(global-set-key (kbd "C-x 3") 'bk/hsplit-last-buffer)
(use-package midnight
:ensure t
:config
(midnight-delay-set 'midnight-delay "4:00am")
(midnight-mode +1))
Perspective offers a new way to manage the display of windows and buffers.
(use-package perspective
:ensure t
:init
(setq persp-sort 'access)
:config
(persp-mode +1))
Same window buffers
(add-to-list 'same-window-buffer-names "*SQL*")
(add-to-list 'same-window-buffer-names "*Help*")
(add-to-list 'same-window-buffer-names "*Apropos*")
(add-to-list 'same-window-buffer-names "*Process List*")
Winner is a built-in tool that keeps a record of buffer and window layout changes. It then allows us to move back and forth in the history of said changes. The mnemonic is related to Emacs default commands to operating on windows (C-x 4) and the undo operations with [uU] letter.
There are some buffers that winner will not restore, I list them in the winner-boring-buffers.
(use-package winner
:ensure nil
:hook (after-init . winner-mode)
:init
(setq winner-dont-bind-my-keys t)
(setq winner-boring-buffers
'("*Completions*"
"*Compile-Log*"
"*inferior-lisp*"
"*Fuzzy Completions*"
"*Apropos*"
"*Help*"
"*cvs*"
"*Buffer List*"
"*Ibuffer*"
"*esh command on file*"))
:bind (("C-x 4 u" . winner-undo)
("C-x 4 U" . winner-redo)))
(use-package windresize
:ensure t
:commands (windresize))
Alert is a growl-workalike for Emacs which uses a common notification interface and multiple, selectable styles, whose use is fully customized by the user.
(use-package alert
:config
(setq alert-default-style 'libnotify
alert-log-messages t))
Several packages uses Alert for sending notifications, so you have
full control over them by customizing alert-user-configuration
.
This was stolen from endless parentheses and adapt accordingly.
Shuts up!
(eval-after-load 'alert
'(add-to-list 'alert-user-configuration
'(((:category . "slack"))
log nil)))
Channels that I wish to only log the messages in the Alert buffer.
(eval-after-load 'alert
'(add-to-list 'alert-user-configuration
'(((:title . "\\(beginners\\|datomic\\|clojure\\|clojurescript\\|off-topic\\|datascript\\|core-async\\)")
(:category . "slack"))
log nil)))
However, there are a couple of important channels I would like to be notified about anything, so add a rule for them.
(eval-after-load 'alert
'(add-to-list 'alert-user-configuration
'(((:title . "\\(reitit\\|sql\\)")
(:category . "slack"))
libnotify nil)))
There are a few channel where I only need to pay attention if explicitly mentioned.
(eval-after-load 'alert
'(add-to-list 'alert-user-configuration
'(((:message . "@bartuka\\|Wanderson")
(:title . "\\(beginners\\)")
(:category . "slack"))
libnotify nil)))
Let’s start by telling alert not to notify anything.
(eval-after-load 'alert
'(add-to-list 'alert-user-configuration
'(((:mode . "telega-chat-mode"))
log nil)))
However, if someone explicitly mention me, tell me pls.
(eval-after-load 'alert
'(add-to-list 'alert-user-configuration
'(((:message . "@bartuka\\|Wanderson")
(:mode . "telega-chat-mode"))
libnotify nil)))
Some packages are too noisy.
(defun suppress-messages (func &rest args)
"Suppress message output from FUNC."
(cl-flet ((silence (&rest args1) (ignore)))
(advice-add 'message :around #'silence)
(unwind-protect
(apply func args)
(advice-remove 'message #'silence))))
Dired is very smart and usually finds the correct intent for some situations, and all of this is able through the DWIM variable. For example, if two buffers are open in the “dired” mode in different folders, if you git M to rename a file, it will move the file from folder A to B.
(setq dired-dwim-target t)
Add the following to have file sizes given in “human-readable” format.
(setq dired-listing-switches "-alh")
Omit certain files.
(setq dired-omit-files
(rx (or (seq bol (? ".") "#")
(seq bol "." eol))))
(add-hook 'dired-mode-hook
(lambda ()
(dired-hide-details-mode)
(dired-sort-toggle-or-edit)))
Group of guidelines to help me remember dired functionalities
A very nice feature is to be able to edit Dired buffers as regular Emacs buffers. You can make several activities bearable using it, for more details follow this guide.
You can mark in Dired buffer based on a search using % m
. By using
the letter t
we can toggle the marked files. There is also the
command k
that hide all the mark file from the current view.
You can always go back by pressing the better g
chord | description |
---|---|
% m | mark files based on search |
t | toggle mark |
k | hide marked files |
g | rebuild the original tree |
i | list the content of a sub-directory |
C-x u | dired undo |
Start dired and mark files as described in Mark files in Dired,
then use Q
to run query-replace
on all marked files.
Some custom functions for Dired.
(require 'dired-x)
(defun bk/dired-xdg-open ()
"Open the file at point with xdg-open."
(interactive)
(let ((file (dired-get-filename nil t)))
(message "Opening %s..." file)
(call-process "xdg-open" nil 0 nil file)
(message "Opening %s done" file)))
(eval-after-load 'dired
'(define-key dired-mode-map (kbd "O") 'bk/dired-xdg-open))
(defun bk/dired-directories-first ()
"Sort dired listings with directories first."
(save-excursion
(let (buffer-read-only)
(forward-line 2)
(sort-regexp-fields t "^.*$" "[ ]*." (point) (point-max)))
(set-buffer-modified-p nil)))
(advice-add 'dired-readin :after #'bk/dired-directories-first)
M-up is nicer in dired if it moves to the third line - straight to the “..”, which M-down is nicer if it moves to the last file and finally C-a moving back to start of files.
(defun dired-back-to-top ()
(interactive)
(beginning-of-buffer)
(next-line 2)
(dired-back-to-start-of-files))
(defun dired-back-to-bottom ()
(interactive)
(end-of-buffer)
(next-line -1)
(dired-back-to-start-of-files))
(defun dired-back-to-start-of-files ()
(interactive)
(backward-char (- (current-column) 2)))
Let’s bind the functions defined above so it can take effect in dired.
(eval-after-load 'dired
'(progn
(define-key dired-mode-map (kbd "M-p") 'dired-back-to-top)
(define-key dired-mode-map (kbd "M-n") 'dired-back-to-bottom)
(define-key dired-mode-map (kbd "C-a") 'dired-back-to-start-of-files)))
(require 'bookmark)
(setq bookmark-default-file (expand-file-name "bookmarks" user-emacs-directory)
bookmark-save-flag 1)
If TRAMP makes backup files, they should be better be kept locally than remote.
(setq tramp-backup-directory-alist backup-directory-alist)
Sane config for ediff
which is basically removing noisy
highlights, avoiding crazy multi-frames setup, ignoring some
whitespaces and windows should be side-by-side.
(use-package ediff
:init
(setq ediff-highlight-all-diffs nil)
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
(setq ediff-diff-options "-w")
(setq ediff-split-window-function 'split-window-horizontally))
A git porcelain inside Emacs. Magit is an interface to the version control system Git, implemented as an Emacs package. Magit aspires to be a complete Git porcelain, look for more info at here.
(use-package magit
:ensure t
:init
<<magit-setup>>
:config
(add-to-list 'magit-no-confirm 'stage-all-changes))
(setq magit-diff-refine-hunk t
magit-revert-buffers 'silent
magit-commit-arguments '("--verbose")
magit-process-popup-time 10)
And what about another hydra? Magit deserves everything.
(defhydra hydra-magit (:color blue)
("q" nil "quit" :column "Magit")
("b" magit-blame "blame" :column "Do")
("c" magit-clone "clone" :column "Do")
("i" magit-init "init" :column "Do")
("s" magit-status "status" :column "Do")
("t" git-timemachine "time-travel" :column "TimeMachine"))
(global-set-key (kbd "C-c g g") 'hydra-magit/body)
Very detailed manual about working with Github and Gitlab from Magit.
(use-package forge
:ensure t)
gitconfig is a major mode for editing gitconfig
files.
(use-package gitconfig-mode
:ensure t
:config
(require 'gitconfig-mode))
git-modes has a major mode for editing gitignore
files.
(use-package gitignore-mode
:ensure t
:config
(require 'gitignore-mode))
(use-package git-timemachine :ensure t)
Show differences between local and remote repo.
(use-package diff-hl
:ensure t
:init
(setq diff-hl-side 'left)
:config
(add-hook 'dired-mode-hook 'diff-hl-dired-mode)
(diff-hl-flydiff-mode)
(add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)
(global-diff-hl-mode)
(custom-set-faces
'(diff-hl-change ((t (:background "#3a81c3"))))
'(diff-hl-insert ((t (:background "#7ccd7c"))))
'(diff-hl-delete ((t (:background "#ee6363"))))))
(use-package browse-at-remote :ensure t)
(use-package gitconfig-mode :ensure t)
(use-package gitignore-templates :ensure t)
(defun bk/visit-pull-request-url ()
"Visit the current branch's PR on Github."
(interactive)
(browse-url
(format "https://github.com/%s/pull/new/%s"
(replace-regexp-in-string
"\\`.+github\\.com:\\(.+\\)\\.git\\'" "\\1"
(magit-get "remote"
(magit-get-remote)
"url"))
(magit-get-current-branch))))
(use-package wgrep
:ensure t)
(use-package visual-regexp
:ensure t
:bind (("C-c r" . vr/replace)
("C-c %" . vr/query-replace)
("<C-m> /" . vr/mark)))
This function help me day by day, every single version of my setup had this beauty in it.
(defun bk/rgrep-fullscreen (regexp &optional files dir confirm)
"Open grep in full screen, saving windows and searching for REGEXP.
in FILES and DIR without CONFIRM."
(interactive
(progn
(grep-compute-defaults)
(let* ((regexp (grep-read-regexp))
(files (grep-read-files regexp))
(dir (ido-read-directory-name "Base directory: "
nil default-directory t))
(confirm (equal current-prefix-arg '(4))))
(list regexp files dir confirm))))
(window-configuration-to-register ?$)
(rgrep regexp files dir confirm)
(switch-to-buffer "*grep*")
(delete-other-windows)
(goto-char (point-min)))
(defun rgrep-quit-window ()
"Simply jump to the register where all your windows are."
(interactive)
(kill-buffer)
(jump-to-register ?$))
(defun rgrep-goto-file-and-close-rgrep ()
"Go to file and close rgrep window."
(interactive)
(compile-goto-error)
(kill-buffer "*grep*")
(delete-other-windows)
(message "Type C-x r j $ to return to pre-rgrep windows."))
(use-package rg
:ensure t
:config
(rg-define-search bk/search-git-root-or-dir
:query ask
:format regexp
:files "everything"
:dir (let ((vc (vc-root-dir)))
(if vc
vc
default-directory))
:confirm prefix
:flags ("--hidden -g !.git"))
:bind
("M-s g" . bk/search-git-root-or-DIR))
You can invoke it using C-s
and typing your desired search
string. Also, if you want to use the regexp flavour you can use
M-C-s
.
Run C-h k C-s
yo get an awesome help menu with all the extra
keys you can use with isearch
. These are the ones I use the
most:
Keybindings | Description |
---|---|
C-s | search forward |
C-r | search backward |
M-C-s | search forward using regexp |
M-C-r | search backward using regexp |
C-s C-w | search word at point |
M-s | is a prefix while in isearch mode |
(while isearch activated) M-r | turn your regular isearch into regexp mode |
M-s . | search for thing at point |
M-s o | get the results in occur buffer |
M-s h r | highlight regexp |
M-s h u | undo the highlight |
C-s M-r | toggle regexp search |
Let’s use an occur
snippet from (or emacs. It will offer as the
default candidate:
- the current region, if it’s active
- the current symbol, otherwise
(defun occur-dwim ()
"Call `occur' with a sane default."
(interactive)
(push (if (region-active-p)
(buffer-substring-no-properties
(region-beginning)
(region-end))
(let ((sym (thing-at-point 'symbol)))
(when (stringp sym)
(regexp-quote sym))))
regexp-history)
(call-interactively 'occur))
(global-set-key (kbd "M-s o") 'occur-dwim)
Artur Malabarba has a nice package called google-this
which
provides a set of functions for querying google from emacs.
(use-package google-this
:ensure t
:delight google-this-mode
:config
(google-this-mode 1))
This package provides a set of functions under the prefix C-c /
.
The simplest is C-c / RET
which prompts you for a search in the
minibuffer, with a default search based on the text around the
point.
Keys | Function |
---|---|
C-c / SPC | google-this-region |
C-c / a | google-this-ray |
C-c / c | google-this-translate-query-or-region |
C-c / e | google-this-error |
C-c / f | google-this-forecast |
C-c / g | google-this-lucky-search |
C-c / i | google-this-lucky-and-insert-url |
C-c / m | google-maps |
C-c / n | google-this-noconfirm |
C-c / r | google-this-cpp-reference |
C-c / s | google-this-symbol |
C-c / t | google-this |
C-c / w | google-this-word |
C-c / <return> | google-this-search |
(use-package eshell
:ensure nil
:config
(setq eshell-hist-ignoredups t
eshell-ls-initial-args "-h"))
(defun eshell-clear-buffer ()
"Clear the terminal buffer."
(interactive)
(let ((inhibit-read-only t))
(erase-buffer)
(eshell-send-input)))
(add-hook 'eshell-mode-hook
(lambda ()
(local-set-key (kbd "C-l") 'eshell-clear-buffer)))
(use-package eshell-bookmark
:ensure t
:config
(add-hook 'eshell-mode-hook 'eshell-bookmark-setup))
(require 'em-alias)
(add-hook 'eshell-mode-hook
(lambda ()
(eshell/alias "e" "find-file $1")
(eshell/alias "ee" "find-file-other-window $1")))
(use-package shell
:bind (:map shell-mode-map
("<s-up>" . comint-previous-input)
("<s-down>" . comint-next-input))
:init
(dirtrack-mode)
(setq explicit-shell-file-name "/bin/bash")
:config
(add-hook 'after-save-hook
'executable-make-buffer-file-executable-if-script-p))
Sometimes I place some TODO and FIXME words in the middle of my code so I can come back to it latter and work on the subjects. The following snippet will highlight these words to help me identify them.
(add-hook 'prog-mode-hook (defun bk--add-watchwords ()
(font-lock-add-keywords
nil `(("\\<\\(FIX\\(ME\\))?\\|TODO\\)"
1 font-lock-warning-face t)))))
There is a package to help cycle quotes when in strings.
(use-package cycle-quotes
:ensure t
:commands (cycle-quotes))
Toggle Test allows you to quickly switch between test and test subject. This is very useful tool to have when you are TDDing.
- Allows you to quickly navigate between test and source files
- It creates the test or source file if it not exists
- If there are many choices, the package present them to us
- This is language agnostic, therefore you need to configure it.
- You can work with multiple projects at the same time.
(use-package toggle-test
:ensure t
:init
(setq tgt-open-in-new-window nil)
:config
(global-set-key (kbd "s-t") 'tgt-toggle)
;; the setup lives in a .dir-locals.el now.
;;(put 'tgt-projects 'safe-local-variable #'listp)
(add-to-list 'tgt-projects '((:root-dir "/home/wand/platform/banker")
(:src-dirs "src")
(:test-dirs "test")
(:test-suffixes "_test"))))
This is an example of .dir-locals.el
content file.
((nil . ((tgt-projects . (((:root-dir "~/foo/bar") (:src-dirs "src") (:test-dirs "test") (:test-suffixes "_test")))))))
Control your whitespaces!
(require 'whitespace)
(setq whitespace-style '(trailing lines space-before-tab
indentation space-after-tab))
(setq whitespace-line-column 100)
(whitespace-mode +1)
A less intrusive ‘delete-trailing-whitespaces’ on save.
(use-package ws-butler
:ensure t
:delight ws-butler-mode
:config
(add-hook 'prog-mode-hook #'ws-butler-mode))
Hungry delete mode
(use-package hungry-delete
:ensure t
:config
(add-hook 'prog-mode-hook 'hungry-delete-mode))
Paredit is great, it brings structural editing to lisps, maintaining the syntactical correctness of your code.
Follow this animated guide to paredit to learn more.
(use-package paredit
:ensure t
:pin melpa
:bind (:map paredit-mode-map
("[")
("M-k" . paredit-raise-sexp)
("M-I" . paredit-splice-sexp)
("C-c ( n" . paredit-add-to-next-list)
("C-c ( p" . paredit-add-to-previous-list))
:bind (:map emacs-lisp-mode-map ("<return>" . paredit-newline))
:bind (:map lisp-mode-map ("<return>" . paredit-newline))
:config
(add-hook 'emacs-lisp-mode-hook 'enable-paredit-mode)
(add-hook 'lisp-mode-hook 'enable-paredit-mode)
(add-hook 'clojure-mode-hook 'enable-paredit-mode)
(add-hook 'cider-mode-hook 'enable-paredit-mode))
Smartparens is a minor mode that deals with parens pairs and tries to be smart about it. I am a paredit user for a long time, I decided to switch to smartparens to experiment this new package which seems to have many more features than paredit. I’ve been here for almost 6 months now, I am yet not sold out in the smartparens idea, for some reason I like the feel of paredit. Still need to dedicate more time to use this in its fullest.
(use-package smartparens
:ensure t
:delight smartparens-strict-mode
:disabled t
:init
(setq sp-highlight-pair-overlay nil
sp-base-key-bindings 'paredit
sp-autoskip-closing-pair 'always
sp-hybrid-kill-entire-symbol nil)
:config
(add-hook 'lisp-mode-hook #'smartparens-strict-mode)
(add-hook 'emacs-lisp-mode-hook #'smartparens-strict-mode)
(with-eval-after-load "smartparens"
;; remove some pairs
(sp-pair "'" nil :actions :rem)
(sp-pair "`" nil :actions :rem)
;; include new wrap of pairs
(sp-pair "(" ")" :wrap "M-(")
(sp-pair "[" "]" :wrap "M-[")
(sp-use-paredit-bindings)
(sp-local-tag 'markdown-mode "c" "```clojure" "```")
(sp-local-tag 'markdown-mode "e" "```elisp" "```")
(sp-local-tag 'markdown-mode "b" "```bash" "```")
(sp-local-tag 'markdown-mode "p" "```python" "```")
(define-key smartparens-mode-map (kbd "M-p") 'sp-prefix-pair-object)))
I stole this from here, where Alvaro Ramirez is exemplifying a nice feature of Xcode about auto-indent after opening parens. For sure we should be able to do that in smartparens:
(defun indent-between-pair (&rest _ignored)
(newline)
(indent-according-to-mode)
(forward-line -1)
(indent-according-to-mode))
(eval-after-load 'smartparens-strict-mode
'(progn
(sp-local-pair 'prog-mode "{" nil :post-handlers '((indent-between-pair "RET")))
(sp-local-pair 'prog-mode "[" nil :post-handlers '((indent-between-pair "RET")))
(sp-local-pair 'prog-mode "(" nil :post-handlers '((indent-between-pair "RET")))))
Origami is a text folding minor mode for Emacs.
(use-package origami
:ensure t
:commands (origami-toggle-node))
There is a vast set of commands to enable the manipulation of folds.
function | desctiption |
---|---|
origami-toggle-node | toggle open or closed a fold node |
origami-show-only-node | close everything but the folds at point |
origami-recursively-toggle-node | acts like org-mode header collapsing |
origami-undo | undo the last undone folding operation |
origami-redo | redo the last undone folding operation |
Let’s build a hydra for it too.
(defhydra bk/hydra-origami (:color red)
"
_o_pen node _n_ext fold toggle _f_orward
_c_lose node _p_revious fold toggle _a_ll
"
("o" origami-open-node)
("c" origami-close-node)
("n" origami-next-fold)
("p" origami-previous-fold)
("f" origami-forward-toggle-node)
("a" origami-toggle-all-nodes)
("t" origami-toggle-node))
(global-set-key (kbd "C-x @") 'bk/hydra-origami/body)
Syntax based folding is great and all but sometimes I need to fold some random piece of text and Vimish fold is great for that.
(use-package vimish-fold
:ensure t
:commands (vimish-fold-toggle
vimish-fold))
Let’s make a hydra for vimish fold.
(defhydra bk/hydra-vimish-fold (:color red :hint nil)
"
_f_: fold _u_: unfold _r_: refold _t_: toggle _d_: delete _n_: next _q_: quit
_U_: Unfold _R_: Refold _T_: Toggle _D_: Delete _p_: previous
"
("f" vimish-fold)
("u" vimish-fold-unfold)
("r" vimish-fold-refold)
("t" vimish-fold-toggle)
("d" vimish-fold-delete)
("U" vimish-fold-unfold-all)
("R" vimish-fold-refold-all)
("T" vimish-fold-toggle-all)
("D" vimish-fold-delete-all)
("n" vimish-fold-next-fold)
("p" vimish-fold-previous-fold)
("q" nil :color blue))
Bind the hydra to a key.
(global-set-key (kbd "C-c @") 'bk/hydra-vimish-fold/body)
Dump jump is a simple package that uses the grep
or ag
to jump
to the source of the symbol at point. This is the fallback when
language specific navigation is not possible.
(use-package dumb-jump
:ensure t
:bind (("C-c ." . dumb-jump-go))
:config
(dumb-jump-mode 1))
Zeal is a nice little app that stores documents offline for reference inspired by Dash which only works on Mac. Let’s bring that to Emacs.
You need to install Zeal in your machine, yaourt -S zeal
.
(use-package zeal-at-point
:ensure t
:bind (("C-c I" . zeal-at-point))
:config
(add-hook 'clojure-mode-hook
(lambda ()
(setq zeal-at-point-docset "clojure"))))
Unfortunately, Emacs does not have a builtin major mode for Clojure, however we have a great community that support any programming language available in the world as a major mode of emacs rsrs.
The intent of a major mode is basically provide font-lock, indentation, navigation and refactoring for the target programming language.
Pretty symbols for Clojure removed from spacemacs clojure layer.
(defun clojure/fancify-symbols (mode)
"Pretty symbols for Clojure's anonymous functions and sets,
like (λ [a] (+ a 5)), ƒ(+ % 5), and ∈{2 4 6}."
(font-lock-add-keywords mode
`(("(\\(fn\\)[\[[:space:]]"
(0 (progn (compose-region (match-beginning 1)
(match-end 1) "λ"))))
("(\\(partial\\)[\[[:space:]]"
(0 (progn (compose-region (match-beginning 1)
(match-end 1) "Ƥ"))))
("(\\(comp\\)[\[[:space:]]"
(0 (progn (compose-region (match-beginning 1)
(match-end 1) "∘"))))
("\\(#\\)("
(0 (progn (compose-region (match-beginning 1)
(match-end 1) "ƒ"))))
("\\(#\\){"
(0 (progn (compose-region (match-beginning 1)
(match-end 1) "∈")))))))
At the clojure-mode
website recommends us to use the MELBA Stable
bundle because the MELPA version is following a development branch of
the library. As this mode is very important for me right now, I would
like to stick to the more stable branch.
(use-package clojure-mode
:ensure t
:delight (clojure-mode "λ")
:init
(setq clojure-align-forms-automatically nil)
:config
(defadvice clojure-test-run-tests (before save-first activate)
(save-buffer))
(defadvice nrepl-load-current-buffer (before save-first activate)
(save-buffer))
(add-hook 'clojure-mode-hook #'eldoc-mode)
(add-hook 'clojure-mode-hook #'subword-mode)
(dolist (m '(clojure-mode clojurescript-mode
clojurec-mode
clojurex-mode))
(clojure/fancify-symbols m)))
The previous setting clojure-align-forms-automatically
makes the
following example a default behavior and you don’t need to manually
align the values. **NOTE**: this is an experiment, 90% of the time
this happened to me, that was the default behavior I wanted. Let’s see
how much the other 10% will annoy me now. EDIT: These 10% are
really bad, I turned it off again.
(def my-map
{:a-key 1
:other-key 2})
;; after C-c SPC
(def my-map
{:a-key 1
:other-key 2})
There are several incredible examples of refactoring in the clojure-mode website.
- TODO: Study refactoring support in clojure-mode.
Provides additional refactoring support, but as we see from the
clojure-mode
github page, all these extra functionalities are
migrating to the clojure mode package.
(use-package clj-refactor
:ensure t
:delight clj-refactor-mode
:after clojure-mode
:init
(setq cljr-favor-prefix-notation nil
cljr-auto-sort-ns nil)
(setq cljr-magic-require-namespaces '(("io" . "clojure.java.io")
("set" . "clojure.set")
("walk" . "clojure.walk")
("zip" . "clojure.zip")
("time" . "clj-time.core")
("log" . "clojure.tools.logging")
("json" . "cheshire.core")
("client" . "org.httpkit.client")
("http" . "clj-http.core")
("a" . "clojure.core.async")
("jdbc" . "next.jdbc")
("s" . "clojure.spec.alpha")
("gen" . "clojure.spec.gen.alpha")))
:config
(add-hook 'clojure-mode-hook (lambda ()
(clj-refactor-mode t)
(cljr-add-keybindings-with-prefix "C-c C-m"))))
(use-package eval-sexp-fu
:ensure t
:config
(use-package cider-eval-sexp-fu
:ensure t))
We also improved the font-locking for built-in methods and macros of clojure.
(use-package clojure-mode-extra-font-locking
:ensure t
:after clojure-mode
:config
(require 'clojure-mode-extra-font-locking))
Now comes the real deal for Clojure development, CIDER extends Emacs with support for interactive programming in Clojure. It basically connects the buffer to a nREPL and communicate back-and-forth to provide functionalities such as code completion, documentation, navigation, debugging, running tests, and many more.
- TODO: Study cider mode
(use-package cider
:ensure t
:pin melpa-stable
:after clojure-mode
:init
(setq cider-mode-line " CIDER"
cider-repl-result-prefix ";; => "
cider-save-file-on-load nil
cider-prompt-for-symbol nil
cider-repl-use-clojure-font-lock t
cider-repl-display-in-current-window t
cider-repl-use-pretty-printing t
cider-auto-select-error-buffer t
cider-auto-select-test-report-buffer nil
cider-test-show-report-on-success t
cider-repl-pop-to-buffer-on-connect nil
cider-stacktrace-default-filters '(tooling dup))
:config
(add-hook 'cider-mode-hook #'eldoc-mode)
(add-hook 'cider-repl-mode-hook #'cider-company-enable-fuzzy-completion)
(add-hook 'cider-mode-hook #'cider-company-enable-fuzzy-completion))
When cider is not connected, I usually use some commands that makes no
sense in clojure-mode
and receive a non-sense error message that I
never understand what is happening or even worse it just hands without
no feedback.
I will borrow the idea from Alex Baranosky and create a dummy function to provide some useful feedback message to my future self.
(defun bk/nrepl-warn-when-not-connected ()
(interactive)
(message "Oops! You're not connected to an nREPL server. Please run M-x cider or M-x cider-jack-in to connect"))
And bind this to the most common keys that requires cider activated.
(define-key clojure-mode-map (kbd "C-x C-e") 'bk/nrepl-warn-when-not-connected)
(define-key clojure-mode-map (kbd "C-c C-k") 'bk/nrepl-warn-when-not-connected)
(define-key clojure-mode-map (kbd "C-c C-z") 'bk/nrepl-warn-when-not-connected)
Package for running Kaocha tests via CIDER
(use-package kaocha-runner
:ensure t
:after clojure-mode)
Often I need to fire a repl and investigate some properties better, I
have a temp
project setup in my machine a simple lein new temp
where I have some libraries already in the project.clj
dependency
available. The following function helps me get there quickly and
require some frequent namespaces.
(defun bk/repl ()
"Start an interactive repl in a temp project"
(interactive)
(cider-jack-in '(:project-dir "/home/wand/temp"))
(add-hook 'cider-connected-hook
(lambda ()
(cider-repl-set-ns "user")
(cider-nrepl-sync-request:eval "(require '[clj-time.core :as t])")
(cider-nrepl-sync-request:eval "(require '[clj-http.core :as client])")
(cider-nrepl-sync-request:eval "(require '[org.httpkit.client :as http])")
(cider-nrepl-sync-request:eval "(require '[clojure.core.async :as a])")
(cider-nrepl-sync-request:eval "(require '[cheshire.core :as json])"))))
Let’s make a nice usage of babashka
scripting for clojure and
print a random doc-string message in the initial of my Emacs
session.
(let ((clj-docstring (shell-command-to-string "docstring.clj")))
(when clj-docstring
(setq initial-scratch-message clj-docstring)))
The docstring.clj
content is pretty small and it required babashka
to be installed, the content:
#!/usr/bin/env bb
(require '[clojure.repl])
(defmacro random-doc []
(let [sym (-> (ns-publics 'clojure.core) keys rand-nth)]
(if (:doc (meta (resolve sym)))
`(clojure.repl/doc ~sym)
`(random-doc))))
(random-doc)
I added the new file to my PATH variable. That’s all.
(defun bk/clj-random-docstring ()
"Random doc-string into new buffer."
(interactive)
(let ((docstring (shell-command-to-string "docstring.clj"))
(buffer-name "*Clojure Random Docs*"))
(when (get-buffer buffer-name)
(kill-buffer buffer-name))
(get-buffer-create buffer-name)
(with-current-buffer buffer-name (insert docstring))
(switch-to-buffer-other-window buffer-name)
(special-mode)))
(defun bk/cider-display-error-buffer (&optional arg)
"Displays the *cider-error* buffer in the current window.
If called with a prefix argument, uses the other window instead."
(interactive "P")
(let ((buffer (get-buffer cider-error-buffer)))
(when buffer
(funcall (if (equal arg '(4))
'switch-to-buffer-other-window
'switch-to-buffer)
buffer))))
Clojure rocks!
(use-package slime
:ensure t
:config
(setq inferior-lisp-program "sbcl")
(slime-setup '(slime-fancy slime-quicklisp slime-asdf)))
Some general packages to improve the already great experience with Elisp. There are already incredible advice’s on code development in Elisp at the official documentation: here.
Hide package namespace in your emacs-lisp code. Check the github page from Malabarba.
(use-package nameless
:ensure t
:config
(add-hook 'emacs-lisp-mode-hook #'nameless-mode))
Most of this section was stolen from John Wiegley talk:
- master
paredit
C-M-x
will evaluate the definition while your cursor is anywhere inside the forms- M-; will open an eval option in the minibuffer
C-x C-e
pp-eval-last-sexp- Enable, debug-on-error if necessary
- enable edebug
- SPC move you forward
- ? will provide which keys you have available
- you can call (debug) inside any form
- enable edebug
(defun bk/elpy-setup ()
(pyvenv-activate "~/miniconda3")
(delete `elpy-module-django elpy-modules)
(delete `elpy-module-highlight-indentation elpy-modules))
(use-package elpy
:ensure t
:config
(add-hook 'python-mode-hook #'elpy-enable)
(add-hook 'python-mode-hook #'bk/elpy-setup))
(use-package py-autopep8
:ensure t
:after elpy
:init
(setq py-autopep8-options '("--max-line-length=250"))
:config
(add-hook 'elpy-mode-hook 'py-autopep8-enable-on-save))
(use-package sqlup-mode
:ensure t
:config
(add-hook 'sql-mode-hook 'sqlup-mode)
(add-hook 'sql-interactive-hook 'sqlup-mode)
(add-to-list 'sqlup-blacklist "name"))
This Emacs library provides commands and a minor mode for easily
reformating SQL using external programs such as pgformatter which can
be installed in Arch Linux using yaourt -S pgformatter-git
(use-package sqlformat
:ensure t
:init
(setq sqlformat-command 'pgformatter)
:config
(add-hook 'sql-mode-hook 'sqlformat-on-save-mode))
Indentation is also important
(use-package sql-indent
:ensure t
:delight sql-mode "Σ "
:after (:any sql sql-interactive-mode)
:config
(add-hook 'sql-mode-hook 'sqlind-minor-mode))
(use-package tex
:defer t
:ensure auctex
:config
(setq TeX-view-program-selection '((output-pdf "PDF Tools"))
TeX-view-program-list '(("PDF Tools" TeX-pdf-tools-sync-view))
TeX-source-correlate-start-server t)
(add-hook 'TeX-after-compilation-finished-functions
#'TeX-revert-document-buffer))
(use-package reftex
:ensure t
:config
(setq reftex-cite-prompt-optional-args t))
(setq TeX-auto-save t
TeX-parse-self t
TeX-save-query nil
TeX-PDF-mode t)
(add-hook 'LaTeX-mode-hook 'visual-line-mode)
(add-hook 'LaTeX-mode-hook 'flyspell-mode)
(add-hook 'LaTeX-mode-hook 'Latex-math-mode)
(add-hook 'LaTeX-mode-hook 'turn-on-reftex)
(with-eval-after-load 'tex
(add-to-list 'safe-local-variable-values
'(TeX-command-extra-options . "-shell-escape")))
Using formal math to specify programs? I have to check this out!!
(use-package tla-mode
:ensure nil
:mode "\.tla$")
Flycheck
is a modern on-the-fly syntax checking extension for GNU
Emacs, intended as replacement for the older Flymake.
(use-package flycheck
:ensure t
:config
(setq flycheck-check-syntax-automatically '(mode-enabled save)))
(use-package flycheck-clj-kondo :ensure t)
A very important command you should remember is C-c ! v
or (M-x
flycheck-verify-setup
) that can help you verify for your current
mode if everything is fine with your linter and it’s backend.
The following package implements a minor-mode for displaying errors from Flycheck right below their reporting location, using overlays.
(use-package flycheck-inline
:ensure t
:after flycheck
:config
(add-hook 'flycheck-mode-hook #'flycheck-inline-mode))
Integrate Unified Modeling Language with flycheck to automatically check the syntax of your plantuml files on the fly.
(use-package flycheck-plantuml
:ensure t
:after flycheck
:config
(flycheck-plantuml-setup))
The UML is a general-purpose, developmental, modeling language in the field of software engineering that is intended to provide a standard way to visualize the design of a system.
- any activities (jobs)
- individual components of the system
- how the system will run
- how entities interact with others
- external user interfaces
The UML diagrams represent two different views of a system model
- Static (or structural) view: emphasizes the static structure of the system using objects, attributes, operations and relationships. It includes class diagrams and composite structure diagrams.
- Dynamic (or behavioral) view: emphasizes the dynamic behavior of the system by showing collaborations among objects and changes to the internal states of objects. This view includes sequence diagrams, activity diagrams and state machine diagrams.
Let’s see a very interesting cheatsheet now:
The internal setup in order to use it will happen though PlantUML
which has an specific syntax but is very easy to pick it up, follow
examples at the official documentation at webpage.
(use-package plantuml-mode
:ensure t
:mode ("\\.plantuml\\'" "\\.puml\\'")
:init
(setq org-plantuml-jar-path "/home/wand/plantuml.jar")
:config
(require 'ob-plantuml))
Do you find yourself constantly Googling for how to do basic
programming tasks? Suppose you want to know how to format a date in
bash. Why open your browser and read through blogs when you can
just M-x howdoi RET format date bash RET
.
(defun bk/howdoi ()
"Call `howdoi' and ask for help"
(interactive)
(let* ((query (read-from-minibuffer "Query: "))
(docstring (shell-command-to-string (concat "howdoi " query)))
(buffer-name "*How do I do it?*"))
(when (get-buffer buffer-name)
(kill-buffer buffer-name))
(get-buffer-create buffer-name)
(with-current-buffer buffer-name (insert docstring))
(switch-to-buffer-other-window buffer-name)
(special-mode)))
Do not forget to install the command line utility.
yaourt -S howdoi
Emacs has the built-in command align-regexp
to align via regular
expression. However, it is not very straightforward, especially
since I don’t know much Emacs regular expressions. Hence, let’s
use some wrapper functions to ease the life in the moment of
danger =)
(defun bk/align-whitespace (start end)
"Align columns by whitespace"
(interactive "r")
(align-regexp start end
"\\(\\s-*\\)\\s-" 1 0 t))
(defun bk/align-ampersand (start end)
"Align columns by ampersand"
(interactive "r")
(align-regexp start end
"\\(\\s-*\\)&" 1 1 t))
(defun bk/align-quote-space (start end)
"Align columns by quote and space"
(interactive "r")
(align-regexp start end
"\\(\\s-*\\).*\\s-\"" 1 0 t))
(defun bk/align-equals (start end)
"Align columns by equals sign"
(interactive "r")
(align-regexp start end
"\\(\\s-*\\)=" 1 0 t))
(defun bk/align-comma (start end)
"Align columns by comma"
(interactive "r")
(align-regexp start end
"\\(\\s-*\\)," 1 1 t))
(defun bk/align-dot (start end)
"Align columns by dot"
(interactive "r")
(align-regexp start end
"\\(\\s-*\\)\\\." 1 1 t))
(defun bk/align-colon (start end)
"Align columns by equals sign"
(interactive "r")
(align-regexp start end
"\\(\\s-*\\):" 1 0 t))
I started to craft a emacs theme, and to do that I had to enable
rainbow-mode
to help me identifying RGB colors. Very useful in
this circumstances.
(use-package rainbow-mode
:ensure t
:commands (rainbow-mode))
(use-package restclient
:ensure t
:config
(add-to-list 'auto-mode-alist '("\\.restclient\\'" . restclient-mode)))
(use-package company-restclient
:ensure t
:after company
:config
(add-to-list 'company-backends 'company-restclient))
(defun bk/restclient ()
"Open the restclient buffer."
(interactive)
(with-current-buffer (get-buffer-create "*restclient*")
(restclient-mode)
(pop-to-buffer (current-buffer))))
Emacs lisp library for reading and writing the data format edn.
(use-package edn
:ensure t)
(use-package markdown-mode
:ensure t
:config
(add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode))
(add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))
(add-to-list 'auto-mode-alist '("README\\.md\\'" . gfm-mode)))
(eval-after-load 'markdown-mode
'(progn
;; `pandoc' is better than obsolete `markdown'
(when (executable-find "pandoc")
(setq markdown-command "pandoc -f markdown"))))
Edit markdown code block like Org.
(use-package edit-indirect
:ensure t
:defer t)
(use-package json-mode
:ensure t
:config
(add-to-list 'auto-mode-alist '("\\.json\\'" . json-mode)))
(require 'nxml-mode)
(push '("<\\?xml" . nxml-mode) magic-mode-alist)
;; pom files should be treated as xml files
(add-to-list 'auto-mode-alist '("\\.pom$" . nxml-mode))
(setq nxml-child-indent 4
nxml-attribute-indent 4
nxml-auto-insert-xml-declaration-flag nil
nxml-bind-meta-tab-to-complete-flag t
nxml-slash-auto-complete-flag t)
Unfortunately, I have to deal with YAML files on my daily basis.
(use-package yaml-mode
:ensure t
:config
(add-hook 'yaml-mode-hook 'whitespace-mode)
(add-hook 'yaml-mode-hook 'subword-mode))
(use-package make-mode
:ensure t
:mode (("Makefile" . makefile-gmake-mode)))
PDF Tools is, among other things, a replacement of DocView for PDF files. The key difference is that pages are not pre-rendered by e.g. ghostscript and stored in the file-system, but rather created on-demand and stored in memory.
(use-package pdf-tools
:ensure t
:defer 1
:magic ("%PDF" . pdf-view-mode)
:init (pdf-tools-install :no-query))
(use-package pdf-view
:ensure nil
:after pdf-tools
:bind (:map pdf-view-mode-map
("C-s" . isearch-forward)
("d" . pdf-annot-delete)
("h" . pdf-annot-add-highlight-markup-annotation)
("t" . pdf-annot-add-text-annotation))
:custom
(pdf-view-display-size 'fit-page)
(pdf-view-resize-factor 1.1)
(pdf-view-use-unicode-ligther nil))
Org mode is for keeping notes, maintaining TODO lists, planning projects, and authoring documents with a fast and effective plain-text system.
Literate Programming is the art of preparing programs for human readers.
“Let us change our traditional attitude to the construction of programs: Instead of imagining that our main task is to instruct a computer what to do, let us concentrate rather on explaining to humans what we want the computer to do.” - Donald E. Knuth, 1984.
noweb is designed to meet the needs of literate programmers while remaining as simple as possible.
The gist of using noweb
is that in your source blocks you have
labels like <<imports>>
, that refer to other named code blocks
that get substituted in place of the label.
(use-package toc-org
:ensure t
:init
(setq toc-org-max-depth 3)
:config
(add-hook 'org-mode-hook 'toc-org-mode))
I need to control the window that pops up when I open the Org Src buffer to edit code.
(setq org-src-window-setup 'current-window)
When using RET
over a link, please go to it.
(setq org-return-follows-link t)
Please, disable flycheck
from org-src buffers. We always have errors
in there related to some emacs-lisp checkers. Here is how to disable
it.
(defun disable-flycheck-in-org-src-block ()
(setq-local flycheck-disabled-checkers '(emacs-lisp-checkdoc)))
(add-hook 'org-src-mode-hook 'disable-flycheck-in-org-src-block)
Let’s enable Org Speed Keys. The main purpose of Speed Keys is to speed up the execution of the most common tasks you do in Org Mode - like outline navigation, visibility cycling, and structure editing. They also support basic clock commands and meta data editing, however, in order to use them, the cursor needs to be at the beginning of a headline.
(setq org-use-speed-commands t)
List of most randy commands:
Key | Description |
---|---|
# | toggle COMMENT-in for an org-header |
s | toggles narrowing to a subtree i.e. hide the rest of the doc |
I/O | clock In/Out to the task defined by the current heading |
u | jumping upwards to the parent heading |
c | for cycling structure below current heading, or C cycling global |
i | insert a new same-level heading below current heading |
w | refile current heading |
t | cycle through the available TODO states |
^ | sort children of the current subtree |
n/p | for next/previous visible heading |
f/b | for next/previous same-level heading |
D/U | move a heading Down/Up |
L/R | recursively promote (move leftwards) or demote (more rightwards) |
1,2,3 | to mark a heading with priority |
Toggle editing org-mode source blocks.
(global-set-key (kbd "s-e") #'org-edit-special)
(define-key org-src-mode-map (kbd "s-e") #'org-edit-src-exit)
(require 'org-capture)
(setq org-directory "/home/wand/org"
org-confirm-babel-evaluate nil
org-agenda-files (list "/home/wand/org/todo.org"
"/home/wand/gcal-captalys.org"))
(setq org-todo-keywords
'((sequence "TODO(t)" "|" "DOING(d)" "|" "DONE(D)" "|" "CANCELLED(C)")
(sequence "STUDY(s)" "|" "STUDIED(S)")
(sequence "ACT(a)" "|" "ACTED(A)")))
(setq org-capture-templates
'(("c" "Capture some concise actionable item and exist" entry
(file+headline "todo.org" "Task list without a defined date")
"* TODO [#B] %^{Title}\n :PROPERTIES:\n :CAPTURED: %U\n :END:\n\n %i %l" :immediate-finish t)
("t" "Task of importance with a tag, deadline, and further editable space" entry
(file+headline "todo.org" "Task list with a date")
"* %^{Scope of task||TODO [#A]|STUDY [#A]|Act on} %^{Title} %^g\n DEADLINE: %^t\n :PROPERTIES:\n :CONTEXT: %a\n:CAPTURED: %U\n :END:\n\n %i %?")))
(setq org-agenda-window-setup 'only-window)
;;; after calling the `org-todo', the org mode tries to store some
;;; sort of a "note" using `org-store-log-note' function. I want that
;;; every modification done in my todo file save the file right after.
(advice-add 'org-deadline :after (lambda (&rest _rest) (org-save-all-org-buffers)))
(advice-add 'org-schedule :after (lambda (&rest _rest) (org-save-all-org-buffers)))
(advice-add 'org-todo :after (lambda (&rest _rest) (org-save-all-org-buffers)))
(advice-add 'org-store-log-note :after (lambda (&rest _rest) (org-save-all-org-buffers)))
(org-babel-do-load-languages
'org-babel-load-languages
'((emacs-lisp . t)
(ledger . t)
(sql . t)
(clojure . t)))
The “Easy Templates” as often is mentioned, is the standard way in Emacs to handle inline code blocks when writing in literate programming style.
You can find all the different available templates by `C-h v org-structure-template-alist`.
This works in Emacs 26, but I am deprecating this now because of my new usage of Emacs 27 and the new way of defining structure templates.
;; (add-to-list 'org-structure-template-alist
;; (list "elisp" (concat "#+BEGIN_SRC emacs-lisp\n"
;; "?\n"
;; "#+END_SRC")))
(add-to-list 'org-structure-template-alist
'("elisp" . "src emacs-lisp"))
There an exhaustive documentation about Reveal.js in the github repository, please follow the link if more is necessary.
(use-package org-re-reveal
:ensure t
:after org
:custom
(org-reveal-mathjax t)
(org-reveal-root "http://cdn.jsdelivr.net/reveal.js/3.0.0/"))
Emacs org-habits function enables you:
- to specify any number of habits, each with different, sometimes less regular frequencies
- to track when you perform a habit and when you should next perform it and finally
- to visualize if you are performing according to the
specifications you have set
(add-to-list 'org-modules 'org-habit t) (setq org-treat-insert-todo-heading-as-state-change t org-log-into-drawer t)
Highlights from the official documentation on repeated tasks. Some
tasks need to be repeated again and again, Org helps to organize
such tasks using a so-called repeater in a DEADLINE
, SCHEDULE
or plain timestamps.
** TODO Pay the rent DEADLINE: <2020-04-21 Tue +1m>
The +1m
is repeater; the intended interpretation is that the
task has a deadline on 2020/04/21 and repeats itself every (one)
month.
repeater | periodicity |
---|---|
y | yearly |
w | weekly |
d | daily |
h | hourly |
Let’s use 750words to write a personal journal. As part of the process, I want my journal entries to be fully encrypted because privacy is important.
org-journal-encrypt-journal
, if set tot
has the effect of transparently encrypting/decrypting the journal files as they are written to disk.org-journal-enable-encryption
, if set tot
, enables integration withorg-crypt
, so it automatically adds a:crypt
tag to new journal entries. This has the effect of automatically encrypting those entries upon save, replacing them with a blob of gpg-encrypted text which has to be further decrypted withorg-decrypt-entry
in order to read or edit them again… too much for now.
(use-package org-journal
:ensure t
:init
(setq org-journal-dir "/home/wand/.journal"
org-journal-file-format "%Y/%m/%Y%m%d"
org-journal-date-format "%A, %Y-%m-%d"
org-journal-encrypt-journal t
org-journal-enable-encryption nil
org-journal-enable-agenda-integration t)
:bind
("C-c j" . org-journal-new-entry))
wc-mode
allows counting characters and words, both on demand and
continuously. It also allows setting up a word/character goal.
(use-package wc-mode
:ensure t
:hook (org-journal-mode . wc-mode)
:config
(setq wc-word-goal 750))
- This is between you and you
- It’s all about writing, and getting into your brain
- It’s not blogging or status updating
- Way to think out loud without having to worry about half-formed ideas, random tangents, private stuff, and all the other things our heads that we often filter out before ever voicing them or writing about them
- It’s a daily brain dump
A package to help us concentrate in the writing by removing everything from the screen and centralizing the text being written.
(use-package writeroom-mode
:ensure t
:init
(setq writeroom-width 150))
epresent is a simple presentation mode for Emacs Org-mode. Epresent leverages exiting Org-mode features like inline image display, inline latex fragments, and in-buffer code fontification to generate very nice looking presentations directly from within Emacs.
(use-package epresent
:ensure t)
- call
epresent-run
on an org-buffer - press
t
/1
to view the top level of the presentation - navigate the presentation with
n/f
,p/b
- scroll with
k
andl
- use
c
andC
to navigate between code blocks,e
to edit them,x
to make it run, ands
/S
to toggle their visibility - quit with
q
(with-eval-after-load "org"
(require 'org-agenda)
(setq org-agenda-fontify-priorities t
org-agenda-include-diary t
org-agenda-inhibit-startup t
org-agenda-log-mode-items '(closed clock state)
org-agenda-ndays 1
org-agenda-persistent-filter t
org-agenda-show-all-dates t))
I am following this post from pragmatic emacs to setup gcalcli
to handle your google agenda. Let’s see if I can make this
through. ==FAILED== I will try something else =)
Let’s see if is possible to sync Google Calendar with Org mode. The Org-gcal library enable you to fetch, post, edit and sync events from your calendar.
There is a bit of setup outside Emacs to make it work, you can follow the step-by-step guide on Org Gcal Readme page.
(use-package org-gcal
:ensure t
:disabled true
:config
(setq org-gcal-client-id (auth-source-pick-first-password
:host "gcal.com"
:user "client-id")
org-gcal-client-secret (auth-source-pick-first-password
:host "gcal.com"
:user "client-secret")
org-gcal-file-alist '(("[email protected]" . "~/gcal-captalys.org"))
org-gcal-notify-p nil))
There are a couple of commands to remember:
command | description |
---|---|
org-gcal-sync | sync between org and gcal, before syncing, execute `org-gcal-fetch’ |
org-gcal-fetch | fetch google calendar events and populate org-gcal-file-alist |
org-gcal-post-at-point | post/edit org block at point to google calendar |
org-gcal-delete-at-point | delete gcal event at point |
org-gcal-refresh-token | refresh the oauth token, it expires in 3600s you should refresh in regular basis. |
I got these two hooks from Zemansky to sync things semi-automatically.
;; (advice-add 'org-gcal-sync :around #'suppress-messages)
;; (add-hook 'org-agenda-mode-hook (lambda () (org-gcal-sync)))
;; not very good integration! many callback errors are getting back.
The other way around, integrating Org bullets to Gmail seems to work but with very basic functionalities. I wish I could create a full appointment entry with invitations, location, and correct duration. For now, I will keep using Google’s UI to do that.
(require 'ox-beamer)
(require 'ox-latex)
(setq org-export-allow-bind-keywords t)
(setq org-latex-listings 'minted)
(add-to-list 'org-latex-packages-alist '("" "minted"))
(org-babel-do-load-languages
'org-babel-load-languages
'((python . t) (C . t) (ruby . t) (js . t)))
(setq org-latex-pdf-process
'("pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"
"pdflatex -shell-escape -interaction nonstopmode -output-directory %o %f"))
From M-x tpis (@iLemming) in Org-mode
- use
C-c !
to enter timestamps - use
S-<arrows>
to move between days - use
M-<arrows>
to move between months - Place
-
between them (to make it a range) C-c C-y
toorg-evaluate-time-range
- with a prefix argument inserts it into the buffer.
(defun bk/meeting-minute ()
"Insert Org template to create a meeting minute."
(interactive)
(insert-file "~/.emacs.d/docs/org-template-meeting.org"))
(use-package ox-asciidoc
:ensure t
:config
(require 'ox-asciidoc))
(use-package org-roam
:ensure t
:hook (after-init . org-roam-mode)
:init
(setq org-roam-directory "/home/wand/all/permanent")
(setq org-roam-dailies-capture-templates
'(("d" "daily" plain (function org-roam-capture--get-point) ""
:file-name "dailies/%<%Y-%m-%d>"
:unnarrowed t
:head "#+TITLE: %<%Y-%m-%d>\n#+STARTUP: showall\n#+roam_tags: fleeting")))
(setq org-roam-capture-templates
'(("l" "literature" plain #'org-roam-capture--get-point "%?"
:file-name "literature/%<%Y%m%d%H%M%S>-${slug}"
:head "#+title: ${title}\n#+created_at: %U\n#+STARTUP: showall\n#+roam_tags: literature"
:unnarrowed t)
("p" "permanent" plain #'org-roam-capture--get-point "%?"
:file-name "%<%Y%m%d%H%M%S>-${slug}"
:head "#+title: ${title}\n#+created_at: %U\n#+STARTUP: showall\n#+roam_tags: permanent"
:unnarrowed t)))
:bind (("C-c n c" . org-roam-capture)
("C-c n t" . org-roam-dailies-today)
:map org-roam-mode-map
("C-c n f" . org-roam-find-file)
:map org-mode-map
("C-c n i" . org-roam-insert)
("C-c n I" . org-roam-insert-immediate)))
(use-package org-roam-server
:ensure nil
:config
(require 'org-roam-protocol))
(setq org-roam-server-enable-access-to-local-files t
org-roam-server-webserver-prefix "/home/wand"
org-roam-server-webserver-address "127.0.0.1:8887/"
org-roam-server-webserver-supported-extensions '("pdf" "mp4" "ogv" "mkv"))
I really like this idea from Prelude, about giving the user some useful tip about Emacs. I will try to reproduce some here.
First, the list of useful tips.
(defvar bk-tips
'("Press <C-c ;> to activate avy char-based navigation."
))
Then the function to display it.
(defun bk-tip-of-the-day ()
"Display a random entry from `bk-tips'."
(interactive)
(when (and bk-tips (not (window-minibuffer-p)))
(random t)
(message
(concat "Bartuka tip: " (nth (random (length bk-tips)) bk-tips)))))
Run from time to time.
(run-at-time 10 (* 10 60) #'bk-tip-of-the-day)
Let’s define a nice hydra that I’ve been using for a while now.
(use-package projectile
:ensure t
:delight '(:eval (concat " " (projectile-project-name)))
:requires hydra
:init
(setq projectile-completion-system 'ivy
projectile-indexing-method 'hybrid
projectile-enable-caching t
projectile-sort-order 'access-time)
:config
(defhydra bk/hydra-projects (:color blue)
("q" nil "quit" :column "Projectile")
("b" projectile-switch-to-buffer "list" :column "Buffers")
("K" projectile-kill-buffers "kill all" :column "Buffers")
("S" projectile-save-project-buffers "save all" :column "Buffers")
("d" projectile-find-dir "directory" :column "Find")
("D" projectile-dired "root" :column "Find")
("f" projectile-find-file "file" :column "Find")
("p" projectile-switch-project "project" :column "Find")
("r" projectile-replace "replace" :column "Search")
("R" projectile-replace-regexp "regexp replace" :column "Search")
("g" bk/rgrep-fullscreen "grep" :column "Search"))
(projectile-mode t)
:bind
("C-c p" . bk/hydra-projects/body))
(defun bk/spell-buffer-pt-BR ()
"Spell check in portuguese."
(interactive)
(ispell-change-dictionary "pt_BR")
(flyspell-buffer))
(defun bk/spell-buffer-en ()
"Spell check in english."
(interactive)
(ispell-change-dictionary "en_US")
(flyspell-buffer))
(use-package flyspell
:ensure nil
:delight flyspell-mode
:config
(add-hook 'prog-mode-hook 'flyspell-prog-mode)
(add-hook 'text-mode-hook 'flyspell-mode)
(define-key flyspell-mode-map (kbd "C-.") nil))
There is a nice package to help correcting previous words that
improve upon the flyspell-auto-correct-previous-word
function
(use-package flyspell-correct
:ensure t
:commands (flyspell-correct-word-generic
flyspell-correct-previous-word-generic)
:config
(require 'flyspell-correct-ido)
(setq flyspell-correct-interface #'flyspell-correct-ido)
:bind (:map flyspell-mode-map
("C-;" . flyspell-correct-wrapper)))
By default the flyspell-correct-wrapper is the most convenient way to use the package because it will jump to the first misspelled word before the point and prompts for correction and gets you back. Calling it with C-u gives ability to correct multiple misspelled words in one run. With C-u C-u changes direction and C-u C-u C-u changes direction and enables multiple corrections.
Let’s install a grammar and style checker. We get the offline tool from the link, then relocate it as follows.
(use-package langtool
:ensure t
:config
(setq langtool-language-tool-jar "/home/wand/.emacs.d/var/LanguageTool-4.5/languagetool-commandline.jar"))
Now we can run langtool-check
on a grammatically incorrect text
which colors errors in red, when we click on them we get the reason
why; then we may invoke langtool-correct-buffer
to quickly use
the suggestions to fix each correction, and finally invoke
language-check-done
to stop any remaining red coloring.
Let’s verify if our installation is working by using a piece of incorrect text from Language Tool website:
LanguageTool offers spell and grammar checking. Just paste your text here and click the 'Check Text' button. Click the colored phrases for details on potential errors. or use this text too see an few of of the problems that LanguageTool can detecd. What do you thinks of grammar checkers? Please not that they are not perfect. Style issues get a blue marker: It's 5 P.M. in the afternoon. The weather was nice on Thursday, 27 June 2017 --uh oh, that's the wrong date ;-)
;; ;; Quickly check, correct, then clean up /region/ with M-^
;; (add-hook 'langtool-error-exists-hook
;; (lambda ()
;; (langtool-correct-buffer)
;; (langtool-check-done)
;; ))
;; (global-set-key "\M-^" 'langtool-check)
Synosaurus is a thesaurus frontend for Emacs with pluggable backends. It has basically three commands:
Key | Commands | Description |
---|---|---|
C-c C-s l | synosaurus-lookup | query you for a word |
C-c C-s r | synosaurus-choose-and-replace | |
C-c C-s i | synosaurus-choose-and-insert |
(use-package synosaurus
:ensure t
:init (synosaurus-mode)
:config
(setq synosaurus-choose-method 'popup)
:bind
("M-#" . synosaurus-choose-and-replace))
The thesaurus is powered by the Wordnet wn
tool, which can be
invoked without an internet connection.
yaourt -S wordnet-common
Let’s use Wordnet as a dictionary via the wordnut package.
(use-package wordnut
:ensure t
:bind
("M-!" . wordnut-lookup-current-word))
Some keys you can use inside the *WordNut*
buffer.
Key | Description |
---|---|
Enter | Lookup a word under the cursor |
o | A tooltip w/ a sense for the current lexical category |
/ | new search |
l, r | move backward/forward in history |
h | view history |
q | hide buffer |
M-up, M-down | move between sections |
Space | Page Down |
b, Backspace | Page Up |
To assist in language learning, it may be nice to have Emacs
interface to Google translate e.g. invoke
google-translate-at-point
.
(use-package google-translate
:ensure t
:config
(require 'google-translate-smooth-ui)
(global-set-key (kbd "s-g t") #'google-translate-smooth-translate))
;; temporary fix for error args-out-of-range
;; https://github.com/atykhonov/google-translate/issues/98
(defun google-translate-json-suggestion (json)
"Retrieve from JSON (which returns by the
`google-translate-request' function) suggestion. This function
does matter when translating misspelled word. So instead of
translation it is possible to get suggestion."
(let ((info (aref json 7)))
(if (and info (> (length info) 0))
(aref info 1)
nil)))
Practice touch typing using speed-type
.
(use-package speed-type
:ensure t)
Running M-x speed-type-region
on a region of text, or M-x
speed-type-buffer
on a whole buffer, or just M-x speed-type-text
will produce the selected region, buffer, or random text for
practice.
A better alternative is to use Typing of Emacs which is far more interactive.
(use-package typing
:ensure t)
There are a few external websites that can help you with that too, Typing.io is the most recommended for Programmers. Check it out!
Yasnippet is a template system for Emacs. It allows you to type an abbreviation and automatically expand it into function templates.
(use-package yasnippet
:ensure t
:delight yas-minor-mode
:config
(yas-global-mode +1)
(define-key yas-minor-mode-map (kbd "<tab>") nil)
(define-key yas-minor-mode-map (kbd "TAB") nil)
(define-key yas-minor-mode-map (kbd "C-c y") #'yas-expand))
But since some specific version, yasnippet does not bundles snippets directly, you have to get them from third-party packages.
;;; a snippet collection maintained by AndreaCrotti.
(use-package yasnippet-snippets :ensure t)
(use-package clojure-snippets :ensure t :defer t)
I want to rely more on snippets on my day-to-day, therefore I need way to visualize if there is an existent snippet for a particular situation. You can do that with `M-x yas/describe-table’.
I will place that in my cheatsheet too and a nice shortcut: C-c s.
(global-set-key (kbd "C-c s")
(lambda ()
(interactive)
(yas/describe-tables)
(other-window 1)))
Jump to end of snippet definition
(define-key yas-keymap (kbd "<return>") 'yas-exit-all-snippets)
(use-package docker
:ensure t
:bind
("C-c d" . docker))
(use-package docker-tramp
:ensure t)
(use-package dockerfile-mode
:ensure t
:config
(add-to-list 'auto-mode-alist '("Dockerfile\\'" . dockerfile-mode))
(add-to-list 'auto-mode-alist '("DockerfileDev\\'" . dockerfile-mode)))
(use-package docker-compose-mode
:ensure t
:config
(add-to-list 'auto-mode-alist '("docker-compose[^/]*\\.yml\\'" . docker-compose-mode)))
(defun bk/dockerfile-add-build-args ()
"Add env variables to your docker build."
(interactive)
(let* ((vars (read-from-minibuffer "sequence of <envName>=<envValue>: "))
(split-vars (split-string vars " ")))
(setq dockerfile-build-args nil)
(dolist (v split-vars)
(add-to-list 'dockerfile-build-args v))
(setq docker-build-history-args vars)))
(defun bk/docker-compose-custom-envs ()
"Add usual env variables to Emacs environment."
(interactive)
(let* ((idu (shell-command-to-string "id -u"))
(idg (shell-command-to-string "id -g"))
(uid (string-join (vector (string-trim idu) ":" (string-trim idg)))))
(setenv "WEBSERVER_PORT" "3000")
(setenv "CURRENT_UID" uid)
(message "setenv WEBSERVER_PORT=3000 CURRENT_UID=$(id -u):$(id -g) done!")))
(defun bk/docker-cleanup-buffers ()
"Delete all the docker buffers created."
(interactive)
(kill-matching-buffers "docker" nil t))
I like to read about programming, but Emacs and Clojure are by far the most interesting communities I know so far, therefore, my feeds have many links from these subjects.
(use-package elfeed
:ensure t
:commands (elfeed elfeed-update)
:bind
("C-x w" . elfeed))
(setq-default elfeed-search-filter "@3-week-ago +unread")
Using the minions
mode I am able to correctly see which modes are
enabled at a specific buffer. I found several global modes enabled
at elfeed-search
which might be consuming resources.
(defun bk/elfeed-disable-mode-setup ()
(interactive)
(abbrev-mode -1)
(company-mode -1)
(smart-shift-mode -1)
(yas-minor-mode -1)
(dired-async-mode -1)
(diff-hl-flydiff-mode -1)
(global-auto-revert-mode -1))
(add-hook 'elfeed-show-mode-hook 'bk/elfeed-disable-mode-setup)
(add-hook 'elfeed-search-update-hook 'bk/elfeed-disable-mode-setup)
Lazy loading all the parts of my elfeed setup.
(require 'elfeed)
(require 'cl-lib)
(require 'elfeed-search)
(require 'elfeed-db)
<<elfeed-basic-config>>
<<elfeed-mode-disabled>>
<<elfeed-newsletters>>
<<elfeed-scoring>>
<<elfeed-filters>>
<<elfeed-automatic-update>>
<<elfeed-starred>>
<<elfeed-youtube>>
Configure the Elfeed RSS reader with an Org-mode file. Yet, I tried to maintain my boring long list of feeds, but this does not make sense at all. Hard to find info, not repeat yourself, and even tagging.
(use-package elfeed-org
:ensure t
:after elfeed
:init
(setq rmh-elfeed-org-files (list "~/.emacs.d/elfeed.org")
rmh-elfeed-org-tree-id "elfeed")
:config
(elfeed-org))
By default, s run a live filter and you can type something like “Xah” to dynamically narrow the list of stories to those containing that string. The only problem is that you need an extra whitespace before the word, ” Xah”, let’s fix that.
(defun bk/elfeed-search-live-filter-space ()
"Insert space when running elfeed filter"
(interactive)
(let ((elfeed-search-filter (concat elfeed-search-filter " ")))
(elfeed-search-live-filter)))
(define-key elfeed-search-mode-map (kbd "/") #'bk/elfeed-search-live-filter-space)
(define-key elfeed-search-mode-map "h"
(lambda ()
(interactive)
(elfeed-search-set-filter (default-value 'elfeed-search-filter))))
Tagging automatic as podcasts
(defun ime-elfeed-pocast-tagger (entry)
(when (elfeed-entry-enclosures entry)
(elfeed-tag entry 'podcast)))
(add-hook 'elfeed-new-entry-hook #'ime-elfeed-pocast-tagger)
Toggle automatic update of elfeed newsletters.
(defvar bk--update-elfeed-timer nil)
(defun bk/toggle-update-elfeed ()
"Toggle automatic elfeed update from 25/25 min."
(interactive)
(let ((repeat-rate (* 60 25)))
(if bk--update-elfeed-timer
(progn
(cancel-timer bk--update-elfeed-timer)
(setq bk--update-elfeed-timer nil)
(message "Elfeed automatic update disabled..."))
(setq bk--update-elfeed-timer
(run-at-time 5 repeat-rate 'elfeed-update))
(message "Elfeed automatic update enabled..."))))
(add-hook 'after-init-hook 'bk/toggle-update-elfeed)
Enable visual-line-mode in elfeed buffers.
(add-hook 'elfeed-show-mode-hook 'visual-line-mode)
This was got from pragmatic emacs.
(defun bk/elfeed-star ()
"Apply starred to all selected entries."
(interactive)
(let* ((entries (elfeed-search-selected))
(tag (intern "starred")))
(cl-loop for entry in entries do (elfeed-tag entry tag))
(mapc #'elfeed-search-update-entry entries)
(unless (use-region-p) (forward-line))))
(defun bk/elfeed-unstar ()
"Remove starred tag from all selected entries."
(interactive)
(let* ((entries (elfeed-search-selected))
(tag (intern "starred")))
(cl-loop for entry in entries do (elfeed-untag entry tag))
(mapc #'elfeed-search-update-entry entries)
(unless (use-region-p) (forward-line))))
(defface elfeed-search-starred-title-face
'((t :foreground "#f77"))
"Marks a starred Elfeed entry.")
(push '(starred elfeed-search-starred-title-face)
elfeed-search-face-alist)
I bind these to the keys “*” to add a star and “8” to remove the star.
(define-key elfeed-search-mode-map (kbd "*") 'bk/elfeed-star)
(define-key elfeed-search-mode-map (kbd "8") 'bk/elfeed-unstar)
Now you can look for the starred feeds by pressing “S”.
(defalias 'elfeed-toggle-star (elfeed-expose #'elfeed-search-toggle-all 'star))
(define-key elfeed-search-mode-map (kbd "S") #'elfeed-toggle-star)
(defun ambrevar/elfeed-play-with-mpv ()
"Play entry link with mpv."
(interactive)
(let ((entry (if (eq major-mode 'elfeed-show-mode) elfeed-show-entry (elfeed-search-selected :single)))
(quality-arg "")
(quality-val (completing-read "Max height resolution (0 for unlimited): " '("0" "480" "720") nil nil)))
(setq quality-val (string-to-number quality-val))
(message "Opening %s with height≤%s with mpv..." (elfeed-entry-link entry) quality-val)
(when (< 0 quality-val)
(setq quality-arg (format "--ytdl-format=[height<=?%s]" quality-val)))
(start-process "elfeed-mpv" nil "mpv" quality-arg (elfeed-entry-link entry))))
(defun ambrevar/elfeed-open-with-eww ()
"Open in eww with `eww-readable'."
(interactive)
(let ((entry (if (eq major-mode 'elfeed-show-mode) elfeed-show-entry (elfeed-search-selected :single))))
(eww (elfeed-entry-link entry))
(add-hook 'eww-after-render-hook 'eww-readable nil t)))
(defvar ambrevar/elfeed-visit-patterns
'(("youtu\\.?be" . ambrevar/elfeed-play-with-mpv)
("phoronix" . ambrevar/elfeed-open-with-eww))
"List of (regexps . function) to match against elfeed entry link to know
whether how to visit the link.")
(defun ambrevar/elfeed-visit-maybe-external ()
"Visit with external function if entry link matches `ambrevar/elfeed-visit-patterns',
visit otherwise."
(interactive)
(let ((entry (if (eq major-mode 'elfeed-show-mode)
elfeed-show-entry
(elfeed-search-selected :single)))
(patterns ambrevar/elfeed-visit-patterns))
(while (and patterns (not (string-match (caar patterns) (elfeed-entry-link entry))))
(setq patterns (cdr patterns)))
(cond
(patterns
(funcall (cdar patterns)))
((eq major-mode 'elfeed-search-mode)
(call-interactively 'elfeed-search-show-entry))
(t (elfeed-show-visit)))))
(define-key elfeed-search-mode-map "v" #'ambrevar/elfeed-play-with-mpv)
(define-key elfeed-search-mode-map "c" 'elfeed-search-untag-all-unread)
(define-key elfeed-show-mode-map "b" #'ambrevar/elfeed-visit-maybe-external)
(defun score-elfeed-entry (entry)
(let ((title (elfeed-entry-title entry))
(content (elfeed-deref (elfeed-entry-content entry)))
(score 0))
(cl-loop for (pattern n) in '(("software\\|programming\\|design\\|systems" 1)
("clojure" 1)
("emacs.*clojure\\|clojure.*emacs" 2))
if (string-match pattern title)
do (incf score n)
if (string-match pattern content)
do (incf score n))
(message "%s - %s" title score)
(setf (elfeed-meta entry :my/score) score)
(cond
((= score 1)
(elfeed-tag entry 'relevant))
((= score 2)
(elfeed-tag entry 'important))
((> score 2)
(elfeed-tag entry 'urgent)))
entry))
(defface relevant-elfeed-entry
`((t :background ,(color-lighten-name "LightBlue1" 40)))
"Maks a relevant Elfeed entry.")
(defface important-elfeed-entry
`((t :background ,(color-lighten-name "orange1" 40)))
"Marks a important Elfeed entry.")
(defface urgent-elfeed-entry
`((t :background ,(color-lighten-name "OrangeRed2" 40)))
"Marks an urgent Elfeed entry.")
(add-hook 'elfeed-new-entry-hook 'score-elfeed-entry)
(push '(relevant relevant-elfeed-entry) elfeed-search-face-alist)
(push '(important important-elfeed-entry) elfeed-search-face-alist)
(push '(urgent urgent-elfeed-entry) elfeed-search-face-alist)
(define-key elfeed-search-mode-map (kbd "U")
(lambda () (interactive)
(elfeed-search-set-filter "@6-months-ago +unread +urgent")))
(define-key elfeed-search-mode-map (kbd "I")
(lambda () (interactive)
(elfeed-search-set-filter "@6-months-ago +unread +important")))
(define-key elfeed-search-mode-map (kbd "R")
(lambda () (interactive)
(elfeed-search-set-filter "@6-months-ago +unread +relevant")))
(define-key elfeed-search-mode-map (kbd "C")
(lambda () (interactive)
(elfeed-search-set-filter "@6-months-ago +unread")))
Slack from Emacs? :O Why not? I am having a terrible time configuring all my workspaces lately. Therefore, it sounds like a perfect opportunity to leverage the best tool for the job once again.
(use-package slack
:ensure t
:disabled t
:init
(setq slack-buffer-emojify t
slack-prefer-current-team t
slack-request-timeout 30
slack-buffer-create-on-notify t
slack-buffer-function #'switch-to-buffer
slack-completing-read-function #'ido-completing-read)
:config
(slack-register-team
:name "captalysdev"
:default t
:modeline-enabled t
:visible-threads t
:token (auth-source-pick-first-password
:host "slack.com"
:user "captalysdev")
:subscribed-channels '(onboarding
geral dev
atlas garantias-e-cobranca)
:full-and-display-names t)
(slack-register-team
:name "clojurians"
:token (auth-source-pick-first-password
:host "slack.com"
:user "clojurians")
:subscribed-channels '(beginners reitit sql))
:config
;; go to any channel with `C-x j`
(define-key ctl-x-map "j" #'slack-select-rooms)
(define-key slack-mode-map (kbd "C-;") ":+1:"))
Bring up the mentions menu with `@’, and insert a space afterwards.
(eval-after-load 'slack
'(define-key slack-mode-map "@"
(defun endless/slack-message-embed-mention ()
(interactive)
(call-interactively #'slack-message-embed-mention)
(insert " "))))
CRUD on messages
(eval-after-load 'slack
'(progn
(define-key slack-mode-map (kbd "C-c C-d") #'slack-message-delete)
(define-key slack-mode-map (kbd "C-c C-e") #'slack-message-edit)
(define-key slack-mode-map (kbd "C-c C-k") #'slack-channel-leave)))
Circe is a client for IRC in Emacs. It tries to have sane defaults, and integrates well with the rest of the editor.
(use-package circe :ensure t)
Emojify is an Emacs extension to display emojis.
(use-package emojify
:ensure t
:delight emojify-mode
:config
(setq emojify-display-style 'image
emojify-emoji-styles '(unicode)
emojify-point-entered-behaviour 'echo)
(global-emojify-mode 1))
How to use Slack on emacs? Some terminology from the website:
Function | Description |
---|---|
im | an IM (instant message) is a direct message between you and exactly one other user |
channel | A channel is a slack channel which you are a member of |
group | Any chat (direct message or channel) which isn’t an IM is a group |
slack-register-team | set team configuration and create team |
slack-change-current-team | change slack-current-team var |
slack-start | do authorize and initialze |
slack-ws-close | turn off websocket connection |
slack-group-select | select group from list |
slack-im-select | select direct message from list |
slack-channel-select | select channel from list |
slack-group-list-update | update group list |
slack-channel-list-update | update channel list |
slack-message-embed-mentio | use to mention to user |
slack-file-upload | uploads a file |
(defun bk/slack-move-direct-to-specific-room (team channel)
"Open the buffer of the specified CHANNEL in a TEAM, without leaving the current TEAM."
(let* ((alist (mapcar #'(lambda (team) (cons (slack-team-name team)
(oref team token)))
(hash-table-values slack-teams-by-token)))
(token (cdr (-first (lambda (v) (equalp (car v) team)) alist)))
(team (slack-team-find-by-token token))
(room (-first (lambda (room) (equalp (slack-room-name room team) channel))
(slack-team-channels team))))
(when team
(slack-team-connect team)
(slack-room-display room team))))
(defun bk/slack-move-to-reitit ()
"Take me to reitit discussions."
(interactive)
(bk/slack-move-direct-to-specific-room "clojurians" "reitit"))
(defun bk/slack-move-to-beginners ()
"Take me to beginners discussions."
(interactive)
(bk/slack-move-direct-to-specific-room "clojurians" "beginners"))
One more chat service that we need to stay in touch with friends and community.
(use-package telega
:ensure t
:delight (telega-chat-mode "Telegram")
:init
(setq telega-animation-play-inline nil
telega-chat-reply-prompt "R>> "
telega-chat-use-markdown-version 2)
:config
(custom-set-faces
'(telega-msg-heading ((t (:overline t :weight bold))))))
I will also enable the contrib packages provides in the github
repo. I copied the interesting ones to me in the lisps/
folder.
(require 'telega-alert)
(telega-alert-mode t)
(require 'telega-dired-dwim)
(use-package all-the-icons :ensure t)
(require 'telega-url-shorten)
(add-hook 'telega-load-hook 'global-telega-url-shorten-mode)
Enabling emoji completions in chat buffer
Take a look at the mode-line in chat buffers
(setq telega-chat-mode-line-format
'((:eval
(telega-chatbuf-mode-line-unread))
(:eval
(telega-chatbuf-mode-line-marked))
(:eval
(telega-chatbuf-mode-line-members nil))
(:eval
(telega-chatbuf-mode-line-pinned-msg 20))))
(use-package helm-spotify-plus
:ensure t
:defer t)
The possible features includes:
- viewing various timelines
- posting tweets
- following and removing users
- marking tweets as favorites
Use Twitter from within Emacs!
(use-package twittering-mode
:ensure t
:config
(setq twittering-timer-interval 3600
twittering-icon-mode t
twittering-use-master-password t))
How to use:
- Execute
M-x twit
to run twittering-mode. - Basic key bindings are as follows:
V
: open or switch to another timeline bytimeline-spec
u
: post a reply to the pointed tweetRET
: post an organic retweetC-c RET
: post an official/native retweetd
: send a direct messageC-c C-w
: delete the pointed tweetj
: go to next tweet
More on usage here.
Weather forecast stolen from pragmatic emacs.
(use-package wttrin
:ensure t
:init
(setq wttrin-default-cities '("São Paulo"
"London"))
:config
(require 'wttrin))
By default wttrin
prompts you to choose the city from your list
when it starts. This function starts with the first city on your
list. I also have a problem with color-theme because I use a too
light one.
(defun bk/weather ()
"Open the weather in your first city in `wttrin-default-cities'."
(interactive)
(wttrin-query (car wttrin-default-cities))
(load-theme-buffer-local 'deeper-blue
(get-buffer "*wttr.in - São Paulo*")))
For many people, time is an enemy. We race against the clock to finish assignments and meet deadlines. The Pomodoro technique teaches you to work with time, instead of struggling against it.
- Choose a task you would like to get done
- Set the pomodoro for 25 minutes
- Work on the task until the Pomodoro rings
- When the Pomodoro rings, put a checkmark on a paper
- Take a short break (5 minutes in my setup)
- Every 4 pomodoros, take a longer break (15 minutes in my setup)
(require 'pomidor)
Ledger mode is a major-mode for editing files in the format used by
the ledger
command-line accounting system. It also provides
automated support for some ledger
workflows, such as reconciling
transactions, or running certain reports.
(use-package ledger-mode
:ensure t
:mode ("\\.dat\\'"
"\\.ledger\\'"
"\\ledger\\'")
:custom (ledger-clear-whole-transactions t)
:config
(require 'ledger-mode))
NOTE: in order to use this mode, ledger
must be installed in your
system.
Some help functions from here.
(defun bk/clean-leader-on-save ()
(interactive)
(if (eq major-mode 'ledger-mode)
(let ((curr-line (line-number-at-pos)))
(ledger-mode-clean-buffer)
(line-move (- curr-line 1)))))
(defun bk/ledger-increment-date ()
(interactive)
(bk/ledger-change-date 1))
(defun bk/ledger-decrement-date ()
(interactive)
(bk/ledger-change-date -1))
(defun bk/ledger-change-date (num)
(save-excursion
(ledger-navigate-beginning-of-xact)
(let* ((beg (point))
(end (re-search-forward ledger-iso-date-regexp))
(xact-date (filter-buffer-substring beg end)))
(delete-region beg end)
(insert
(format-time-string
"%Y/%m/%d"
(time-add (bk/encoded-date xact-date)
(days-to-time num)))))))
(defun bk/encoded-date (date)
(string-match "\\([0-9][0-9][0-9][0-9]\\)/\\([0-9][0-9]\\)/\\([0-9][0-9]\\)" date)
(let* ((fixed-date
(concat (match-string 1 date) "-"
(match-string 2 date) "-"
(match-string 3 date)))
(d (parse-time-string fixed-date)))
(encode-time 0 0 0 (nth 3 d) (nth 4 d) (nth 5 d))))
(add-hook 'before-save-hook 'bk/clean-leader-on-save)
(eval-after-load 'ledger-mode
'(progn
(define-key ledger-mode-map (kbd "C-M-.") 'bk/ledger-increment-date)
(define-key ledger-mode-map (kbd "C-M-,") 'bk/ledger-decrement-date)))
Linter for the ledger mode. Very very useful to understand if you filled everything’s right.
(use-package flycheck-ledger
:ensure t
:config
(add-hook 'ledger-mode-hook 'flycheck-mode))
You can use C-c C-b
to popup the calc
mode and perform some math
with the number at point to fix it.
Register to get into the ledger file quickly.
(set-register ?l '(file . "~/.ledger"))
- Double entry system: All money has a source and destination account
- Five accounts
- Assets : what you have
- Expenses : what you expends
- Income : what you earns
- Liabilities : what you owe
- Equity : what you worth
- Net worth = Assets - Liabilities
- Net income = Income - Expenses
- **Why accounting?**
- To know what you have
- across all accounts
- had at some point in the past
- how much you can spend
- legally required for most businesses
- To know what you have
- **Double-entry accounting**
- account: label describing an amount of something
- assets : bank accounts, wallet, investments
- income : paychecks, dividends, interest
- expenses : groceries, taxes, donations
- liabilities : mortgage, credit cards, student loans
- equity : for everything else like opening balances
- debit
- the deduction of value from an account
- credit
- the addition of value to an account
- transaction
- a collection of credits and debits with a timestamp to describe when the transaction is effective
- all transaction must balance, equals to 0 at the end
- account: label describing an amount of something
- **Why ledger?**
- intimate knowledge of every transaction
- track everything
- its just text
Let’s force learning proper Emacs
(use-package guru-mode
:ensure t
:config
(guru-mode 1))
If you think that this is too aggressive, please don’t! rsrs I am
kidding, you can use the option guru-warn-only
to t
and be
ashamed of yourself.
When you need to modify a function defined in another library.
Adivice feature lets you add to the existing definition of a function, by advising the function. This is a cleaner method than redefining the whole function.
When popping the mark, continue popping until the cursor actually moves. Also, if the last command was a copy - skip past all the expand-region craft.
(defadvice pop-to-mark-command (around ensure-new-position activate)
(let ((p (point)))
(when (eq last-command 'save-region-or-current-line)
ad-do-it
ad-do-it
ad-do-it)
(dotimes (i 10)
(when (= p (point)) ad-do-it))))
(setq set-mark-command-repeat-pop t)
This was stolen from here. The idea is to indent yanked regions in specific modes that you can define.
(defvar yank-indent-modes '(prog-mode
js2-mode)
" Modes in which to indent regions that are yanked (or yank-popped)")
(defvar yank-advised-indent-threshold 1000
" Threshhold (# chars) over which indentation does not automatically occur.")
(defun yank-advise-indent-function (beg eng)
"Do indentation, as long as the region isn't too large."
(if (<= (- end beg) yank-advised-indent-threshold)
(indent-region beg end nil)))
(defadvice yank (after yank-indent activate)
"If current mode is one of `yank-indent-modes', indent yanked text."
(if (and (not (ad-get-arg 0))
(member major-mode yank-indent-modes))
(let ((transient-mark-mode nil))
(yank-advise-indent-function (region-beginning) (region-end)))))
(defadvice yank-pop (after yank-pop-indent activate)
"If the current mode is one of `yank-indent-modes', indent yanked text."
(if (and (not (ad-get-arg 0))
(member major-mode yank-indent-modes))
(let ((transient-mark-mode nil))
(yank-advise-indent-function (region-beginning) (region-end)))))
(defun yank-unindented ()
(interactive)
(yank 1))
Tool for capturing screen-casts directly from Emacs.
- To use it, simply call
M-x camcorder-record
- A new smaller frame will popup and recording starts
- When you’re finished, git
F12
You can also convert the file to a gif
by issuing the command
M-x comcorder-convert-to-gif
(use-package camcorder
:ensure t)
Count up time and show remainder time at mode-line.
;; (use-package stopwatch
;; :ensure nil)
(use-package command-log-mode
:ensure t
:commands command-log-mode)
(defun eshell/kg (&rest args)
"Find status of pods in ARGS."
(let* ((env (car args)))
(with-current-buffer "*eshell*"
(insert "kubectl get pods -n " env)
(eshell-send-input))))
(defun get-pod-name (pod env)
"Get POD name from correct ENV."
(let ((res (eshell-command-result (concat "kubectl get pods -n " env))))
(string-match (concat pod ".*") res 0)
(car (split-string (match-string 0 res) " "))))
(defun eshell/kl (&rest args)
"Get logs from PODS and ENVS in ARGS."
(let* ((pod (car args))
(env (car (cdr args)))
(pod-name (get-pod-name pod env)))
(with-current-buffer "*eshell*"
(insert "kubectl logs -n " env " " pod-name " " pod "-" env " -f")
(eshell-send-input))))
(defun bk/load-private-keys ()
"Personal keys I use to work and whatnot"
(interactive)
(let ((client-key (shell-command-to-string "base64 /home/wand/CaptalysPlatform.key"))
(qi-key (shell-command-to-string "base64 /home/wand/qitech_captalys_homolog.key.pub")))
(setenv "CLIENT_PRIVATE_KEY" client-key)
(setenv "QI_PUBLIC_KEY" qi-key))
(message "CLIENT_PRIVATE_KEY and QI_PUBLIC_KEY were loaded"))
;; load them!!
(bk/load-private-keys)
(defvar url-http-end-of-headers)
(defun bk/ip ()
"Find my current public IP address."
(interactive)
(let* ((endpoint "https://api.ipify.org")
(myip (with-current-buffer (url-retrieve-synchronously endpoint)
(buffer-substring (+ 1 url-http-end-of-headers) (point-max)))))
(kill-new myip)
(message "IP: %s" myip)))
(defun bk/sudo-edit (&optional arg)
"Function to edit file with super-user with optional ARG."
(interactive "P")
(if (or arg (not buffer-file-name))
(find-file (concat "/sudo:root@localhost:" (read-file-name "File: ")))
(find-alternate-file (concat "/sudo:root@localhost:" buffer-file-name))))
(defun eval-and-replace ()
"Replace the preceding sexp with its value."
(interactive)
(backward-kill-sexp)
(condition-case nil
(prin1 (eval (read (current-kill 0)))
(current-buffer))
(error (message "invalid expression")
(insert (current-kill 0)))))
(defun bk/insert-today-date ()
"Insert today date as YYYY-MM-DD."
(interactive)
(insert (format-time-string "%Y/%m/%d")))
(defun comment-kill-all ()
"Function to kill all comments in a buffer."
(interactive)
(save-excursion
(goto-char (point-min))
(comment-kill (save-excursion
(goto-char (point-max))
(line-number-at-pos)))))
(defun bk/scratch-buffer ()
"Function to change buffer to scratch buffer."
(interactive)
(let ((buf (get-buffer "*scratch*")))
(if buf
(switch-to-buffer buf)
(switch-to-buffer (get-buffer-create "*scratch*"))
(lisp-interaction-mode))))
(global-set-key (kbd "C-c b s") #'bk/scratch-buffer)
(defun bk/kill-buffer-and-file (buffer-name)
"Removes file connected to current buffer and kills buffer."
(interactive "bKill buffer and its file:")
(let* ((buffer (get-buffer buffer-name))
(filename (buffer-file-name buffer)))
(if (not (and filename (file-exists-p filename)))
(error "Buffer '%s' is not visiting a file!" buffer-name)
(delete-file filename)
(kill-buffer buffer))))
(defun bk/rename-current-buffer-file ()
"Renames current buffer and file it is visiting."
(interactive)
(let* ((name (buffer-name))
(filename (buffer-file-name))
(new-name (read-file-name "New name: " filename)))
(if (get-buffer new-name)
(error "A buffer named '%s' already exists!" new-name)
(rename-file filename new-name 1)
(rename-buffer new-name)
(set-visited-file-name new-name)
(set-buffer-modified-p nil)
(message "File '%s' sucessfully renamed to '%s'"
name (file-name-nondirectory new-name)))))
(defun generate-password ()
"Generate a 16-digit password."
(interactive)
(kill-new
(s-trim (shell-command-to-string
" openssl rand -base64 32 | tr -d /=+ | cut -c -16")))
(message "Password in kill ring!"))
Which keys are free?
(use-package free-keys
:ensure t
:commands free-keys)
(use-package which-key
:ensure t
:delight which-key-mode
:init
(setq which-key-use-C-h-commands t
which-key-separator " - "
which-key-show-prefix 'echo
which-key-popup-type 'side-window)
:config
(which-key-mode))
(global-set-key (kbd "M-i") 'change-inner)
(global-set-key (kbd "M-o") 'change-outer)
(global-set-key (kbd "C-c e") 'eshell)
(global-set-key (kbd "C-c C-k") 'eval-buffer)
(global-set-key (kbd "C-x C-b") 'ibuffer)
(global-set-key (kbd "C-c t") 'org-capture)
(global-set-key (kbd "C-c a") 'org-agenda)
(global-set-key (kbd "C-x p") 'pop-to-mark-command)
;; by default C-x k prompts to select which buffer should be selected.
(global-set-key (kbd "C-x k") (lambda ()
(interactive)
(kill-buffer (current-buffer))))
Show current command and its key in the mode line
(use-package keycast
:homepage https://github.com/tarsius/keycast
:ensure t
:defer t)
Monitor my new habits key-wise.
(use-package keyfreq
:ensure t
:init
(setq keyfreq-excluded-commands
'(self-insert-command
abort-recursive-edit
forward-char
backward-char
previous-line
next-line
org-self-insert-command))
:config
(keyfreq-mode +1)
(keyfreq-autosave-mode +1))
GNU/Emms is the Emacs multimedia system. Emms displays and plays multimedia from within Emacs using a variety of external players and from different sources.
(use-package emms
:ensure t
:init
(setq emms-seek-seconds 10)
:config
(require 'emms-setup)
(require 'emms-player-mpv)
(emms-standard)
(emms-default-players))
Display the emms mode line as a ticker. I am listening to several podcasts where the whole link of the podcast is displayed at the mode-line. I can’t see the time-elapsed listen to stuff, this is bad.
A package to solve this problem:
(use-package emms-mode-line-cycle
:ensure t
:after emms
:config
(emms-mode-line 1)
(emms-playing-time 1)
(emms-mode-line-cycle 1))
Emacs as my full operating system is just too great. I have a small problem recently with this setup, I use an 60% Anne Pro 2 keyboard and its well known to be very but very buggy. The experience of the keyboard itself is incredible, but the firmware behind it is just unbearable, for some reason after hitting some keys my Emacs was halting. oO yes, complete halt for no reason whatsoever, seems like XKB and this firmware is not getting along nicely.
I found an alternative to fix this issue by using EXWM alongside
with LXDE. There are two files:
~/.config/lxsession/LXDE/autostart
@pcmanfm --desktop-off --profile LXDE
@compton -b &
~/.config/lxsession/LXDE/desktop.conf
[Session]
window_manager=emacs
disable_autostart=no
polkit/command=lxpolkit
clipboard/command=lxclipboard
xsettings_manager/command=build-in
proxy_manager/command=build-in
keyring/command=ssh-agent
quit_manager/command=lxsession-logout
lock_manager/command=lxlock
terminal_manager/command=lxterminal
quit_manager/image=/usr/share/lxde/images/logout-banner.png
quit_manager/layout=top
[GTK]
iXft/Antialias=1
iXft/Hinting=1
sXft/HintStyle=hintslight
sXft/RGBA=rgb
sNet/ThemeName=Clearlooks
sNet/IconThemeName=nuoveXT2
iNet/EnableEventSounds=1
iNet/EnableInputFeedbackSounds=1
sGtk/ColorScheme=
sGtk/FontName=Sans 10
iGtk/ToolbarStyle=3
iGtk/ToolbarIconSize=3
iGtk/ButtonImages=1
iGtk/MenuImages=1
iGtk/CursorThemeSize=18
sGtk/CursorThemeName=DMZ-White
[Mouse]
AccFactor=20
AccThreshold=10
LeftHanded=0
[Keyboard]
Delay=500
Interval=30
Beep=1
[State]
guess_default=true
[Dbus]
lxde=true
[Environment]
menu_prefix=lxde-
And don’t forget to change the content of ~/.xinitrc
to exec
startlxde
.
Ok, now let’s start with EXWM configuration.
(defun bk/keepmenu ()
"Call password manager."
(interactive)
(start-process-shell-command "pwd" nil "keepmenu"))
(defun bk/lock-screen ()
(interactive)
(start-process-shell-command "lock" nil "xscreensaver-command -lock"))
(defun bk/qutebrowse ()
(interactive)
(start-process-shell-command "browser" nil "qutebrowser"))
(use-package exwm
:ensure t
:disabled true
:init
(setq exwm-workspace-number 4
exwm-workspace-show-all-buffers nil
exwm-layout-show-all-buffers t)
:config
(display-battery-mode t)
;; setting floating window boarder
(setq exwm-floating-border-width 3)
(require 'exwm)
(exwm-input-set-simulation-keys
'(([?\C-p] . [up])
([?\C-n] . [down])
([?\C-f] . [right])
([?\C-b] . [left])
([?\C-s] . [\C-f])
([?\M-w] . [\C-c])
([?\C-y] . [\C-v])
([?\C-w] . [\C-x])))
(setq exwm-input-global-keys
`(([?\s-r] . exwm-reset)
([?\s-w] . exwm-workspace-switch)
,@(mapcar (lambda (i)
`(,(kbd (format "s-%d" i)) .
(lambda ()
(interactive)
(exwm-workspace-switch-create ,i))))
(number-sequence 0 9))))
(exwm-input-set-key (kbd "s-p") #'bk/keepmenu)
(exwm-input-set-key (kbd "s-d") #'dmenu)
(exwm-input-set-key (kbd "C-c l") #'bk/lock-screen)
(exwm-enable)
(require 'exwm-config)
(exwm-config-ido)
;; universal Get-me-outta-here
(push ?\C-g exwm-input-prefix-keys)
(exwm-input-set-key (kbd "C-g") #'keyboard-quit))
Now that I am using Emacs as my window manager I can use the meta keys to provide operations over the windows itself, in other days, there were reserved to i3wm operations.
(eval-after-load "exwm"
'(progn
(exwm-input-set-key (kbd "s-x") #'exwm-input-toggle-keyboard)
(exwm-input-set-key (kbd "s-h") #'windmove-left)
(exwm-input-set-key (kbd "s-j") #'windmove-down)
(exwm-input-set-key (kbd "s-k") #'windmove-up)
(exwm-input-set-key (kbd "s-l") #'windmove-right)))
I made a change in qutebrowser
so every tab open is a new window of
qutebrowser, therefore I can search for the tabs using C-x b
from
Emacs.
:set -t tabs.tabs_are_window true
More settings for qutebrowser can be found here.
Disabling floating window
(eval-after-load "exwm"
'(setq exwm-manage-force-tiling t))
Ag has a very nice package to help us out in EXWM, the feature is
similar in usage to Org-SRC-Blocks, therefore you press C-c '
on
input text boxes of other external apps and another window pops up
so you can have all the Emacs under the finger while editing.
(use-package exwm-edit
:ensure t
:after (exwm)
:config
(exwm-input-set-key (kbd "C-s-e") #'exwm-edit--compose))
(use-package dmenu
:ensure t
:after (exwm)
:commands (dmenu))
Stolen from reddit answer, however the original function is heavily customized to the authors setup, therefore I modified the appropriate bits.
After some time working with the code I decided to try to make a package out of it. However, there are several moving peaces yet, however, is already useful for me.
Work in Progress nevertheless…
(eval-after-load "exwm"
'(progn
(require 'exwm-monitors)
(exwm-monitors-define-screen-info
:name "eDP1"
:width 1600
:height 900)
(exwm-monitors-define-spec
:name "home"
:pred (list :only '("eDP1" "HDMI1"))
:action (list '("eDP1" :off)
'("HDMI1" :right)))
(defun bk/turn-x1-carbon-on ()
(interactive)
(exwm-monitors-define-spec
:name "home"
:pred (list :only '("eDP1" "HDMI1"))
:action (list '("eDP1" :auto)
'("HDMI1" :right)))
(exwm-monitors-initial-setup))
(exwm-monitors-initial-setup)))
Oh, this is nice! I can control pacman
from Emacs.
(use-package system-packages
:ensure t
:config
(setq system-packages-use-sudo t))
I need a notification daemon to alert me about all the statefull things changing around me. For now, I will use Dunst.
(call-process-shell-command "nohup dunst >/dev/null &" nil 0)
There is a function to send an alert to the daemon.
(defun dunst-alert (header string &rest objects)
"Send an alert to the Dunst daemon."
(let ((string (funcall #'format string objects))
(command (format "notify-send -a \"%s\" \"%s\"" header string)))
(call-process-shell-command command nil 0)))
In stock Emacs, EXWM uses char mode
and line mode
to
distinguish between using the keyboard to control an application vs
using the keyboard to control the application’s buffer.
Rename buffers to match the X11 window class or title:
(defun exwm-rename-buffer ()
(interactive)
(exwm-workspace-rename-buffer
(concat exwm-class-name ":"
(if (<= (length exwm-title) 30) exwm-title
(concat (substring exwm-title 0 29))))))
(add-hook 'exwm-update-class-hook 'exwm-rename-buffer)
(add-hook 'exwm-update-title-hook 'exwm-rename-buffer)
Window dividers make Emacs look far less sloppy, and provide divisions between windows that are significantly more visible. The color is grabbed from the mode line for consistency.
(eval-after-load "exwm"
'(progn
(setq window-divider-default-right-width 3)
(let ((color (face-background 'mode-line)))
(dolist (face '(window-divider-first-pixel
window-divider-last-pixel
window-divider))
(set-face-foreground face color)))
(window-divider-mode 1)))
“Global key bindings” in EXWM work essentially anywhere, including buffers that are currently in char mode. The bindings below should be fairly straightforward.
Regular keys to control audio using the package pulseaudio.
(use-package pulseaudio-control
:ensure t
:after (exwm)
:config
(exwm-input-set-key
(kbd "<XF86AudioLowerVolume>")
#'pulseaudio-control-decrease-volume)
(exwm-input-set-key
(kbd "<XF86AudioRaiseVolume>")
#'pulseaudio-control-increase-volume)
(exwm-input-set-key
(kbd "<XF86AudioMute>")
#'pulseaudio-control-toggle-current-sink-mute))
Control the backlight level
(use-package emacs
:ensure nil
:after (exwm)
:config
(exwm-input-set-key
(kbd "<XF86MonBrightnessDown>")
(lambda () (interactive)
(start-process-shell-command "bdown" nil "xbacklight -dec 10")))
(exwm-input-set-key
(kbd "<XF86MonBrightnessUp>")
(lambda () (interactive)
(start-process-shell-command "bdown" nil "xbacklight -inc 10"))))
More binding definitions
(defun bk/qutebrowser ()
"Open the browser"
(interactive)
(start-process-shell-command "brw" nil "qutebrowser"))
(defun bk/fix-caps-and-key-rate ()
"Capslock is another ctrl and key rate need to be higher"
(interactive)
(start-process-shell-command "caps" nil "setxkbmap -layout us -variant alt-intl -option ctrl:nocaps")
(start-process-shell-command "krate" nil "xset r rate 300 50")
(message "Ctrl and key rate fixed!"))
(use-package emacs
:ensure nil
:after (exwm)
:config
(exwm-input-set-key (kbd "s-q") #'bk/qutebrowser)
(exwm-input-set-key (kbd "C-c k") #'bk/fix-caps-and-key-rate))
System tray
(defun exwm-bk/nm-applet ()
(interactive)
(start-process-shell-command "applt" nil
"nm-applet"))
(defun exwm-bk/bluetooth ()
(interactive)
(start-process-shell-command "bl-applt" nil
"blueman-applet"))
(defun exwm-bk/xscreensaver ()
(interactive)
(start-process-shell-command "xs" nil
"xscreensaver -no-splash"))
(defun exwm-bk/key-rate ()
(interactive)
(start-process-shell-command "key-rate" nil
"xset r rate 300 50"))
(defun exwm-bk/nocaps ()
(interactive)
(start-process-shell-command "nocaps" nil
"setxkbmap -layout us -variant alt-intl -option ctrl:nocaps"))
(use-package exwm-systemtray
:ensure nil
:after (exwm)
:init
(setq exwm-systemtray-height 20)
:config
(exwm-systemtray-enable)
(add-hook 'exwm-init-hook 'exwm-bk/nm-applet t)
(add-hook 'exwm-init-hook 'exwm-bk/bluetooth t)
(add-hook 'exwm-init-hook 'exwm-bk/xscreensaver)
(add-hook 'exwm-init-hook 'exwm-bk/key-rate)
(add-hook 'exwm-init-hook 'exwm-bk/nocaps))
(defun exwm-logout ()
(interactive)
(recentf-save-list)
(save-some-buffers)
(start-process-shell-command "logout" nil "lxsession-logout"))
Keys | Command | Description |
---|---|---|
- | exwm-workspace-move-window | send current window to a specific workspace |
- EXWM User Guide
- EXWM config with support for external monitor
- Reddit EXWM Configs?
- Ag layer for EXWM
- Another great emacs.d focusing on EXWM
- Emacs Everywhere - by u/ambrevar. Very nice post on reddit.
- https://ambrevar.xyz/emacs/
- Literate emacs - by wasamasa
- Far from sane literate Emacs - by farlado
- Pragmatic Emacs - Amazing blog with lots of Emacs customizations