- Installation
- Initialization
- Performance
- Package management
- Interface
- Built-in
- Hooks
- Window management
- Completion
- VCS
- Project management
- projectile
- Search
- Tags
- dumb-jump
- DevOps
- Docker
- Languages
- Templates
- Org
Create emacs data volume.
docker volume create emacs_data
Add alias:
# ~/.bash_aliases or etc ...
alias dockemacs='
docker run -it --rm --net=host \
--cpuset-cpus 0-1 \
--env-file $HOME/.dockemacs \
--entrypoint initialize "$@" \
-v $HOME:/mnt/workspace \
-v emacs_data:/home/emacser/.emacs.d \
-v /etc/localtime:/etc/localtime:ro \
afsmnghr/dockemacs:1.15.0 startup
'
Prepare $HOME/.dockemacs
, check your env
.
# default by
## transparent permissions
echo "UID=$(id -u)" >> $HOME/.dockemacs # 1000
echo "GID=$(id -g)" >> $HOME/.dockemacs # 100
## user & group name in container
echo "UNAME=emacser" >> $HOME/.dockemacs
echo "GNAME=emacs" >> $HOME/.dockemacs
## rewrite home path for new user
echo "HOME=/home/emacser" >> $HOME/.dockemacs
## mount path from host
echo "WORKSPACE=/mnt/workspace" >> $HOME/.dockemacs
# required
## set terminal env
echo "TERM=xterm-256color" >> $HOME/.dockemacs
## only relative path from workspace path
echo "ORG_FILES=Documents/org/" >> $HOME/.dockemacs
## remote management through ssh
echo "HOST_USER=afsmnghr" >> $HOME/.dockemacs
echo "HOST_IP=127.1" >> $HOME/.dockemacs # only work with --net=host
echo "HOST_PORT=22" >> $HOME/.dockemacs
## required for GUI application
echo "DISPLAY=:0.0" >> $HOME/.dockemacs
## setup browser for emacs
echo "WEB_BROWSER=chromium" >> $HOME/.dockemacs
## our repository dotemacs (first clone)
echo "REPOSITORY=https://github.com/AfsmNGhr/dockemacs.git" >> $HOME/.dockemacs
## our active branch
echo "BRANCH=master" >> $HOME/.dockemacs
# optional
## force update our branch
echo "HEAD_FORCE=true" >> $HOME/.dockemacs # git reset --hard
Setup ssh server and restart.
# /etc/ssh/sshd_config
ListenAddress 127.1
Setup ssh client. Create sockets path.
mkdir ~/.ssh/sockets
Speedup local connection.
# ~/.ssh/config
Host *
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h:%p
ControlPersist 4h
PreferredAuthentications publickey
Host 127.1
Hostname 127.1
User "$HOST_USER"
Port "$HOST_PORT"
Compression no
Ciphers chacha20-poly1305@openssh.com
ForwardX11 no
Check permissions of config file.
sudo chmod 600 ~/.ssh/config
Add our ssh pub key to authorized_keys.
ssh-copy-id "$HOST_USER@$HOST_IP" -p "$HOST_PORT"
For SSH_CONNECTION
set TERM
. Fixed tramp issues.
# ~/.bashrc
if [ "$SSH_CONNECTION" ]; then
TERM='dumb'
fi
case "$TERM" in
*)
PS1='> '
;;
esac
Prepare gpg settings.
# ~/.gnupg/gpg.conf
use-agent
pinentry-mode loopback
Run and wait until the boot.
$ dockemacs
Tangling with emacs script. See emacs script pitfalls.
#!/usr/bin/env sh
":"; exec emacs --quick --script "$0" "$@" # -*-emacs-lisp-*-
(require 'org)
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.6)
(find-file (concat user-emacs-directory "init.org"))
(org-babel-tangle)
(load-file (concat user-emacs-directory "init.el"))
(byte-compile-file (concat user-emacs-directory "init.el"))
(setq gc-cons-threshold 5000000
gc-cons-percentage 0.1)
(defmacro k-time (&rest body)
"Measure and return the time it takes evaluating BODY."
`(let ((time (current-time)))
,@body
(float-time (time-since time))))
;; When idle for 15sec run the GC no matter what.
(defvar k-gc-timer
(run-with-idle-timer 15 t
(lambda ()
(message "Garbage collector has run for %.06f sec"
(k-time (garbage-collect))))))
Don’t auto-initialize!
(setq package-enable-at-startup nil
package--init-file-ensured t)
The use-package
declarative and performance-oriented.
(require 'package)
(package-initialize)
(setq package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
("org" . "http://orgmode.org/elpa/")
("melpa" . "https://melpa.org/packages/")
("melpa-stable" . "https://stable.melpa.org/packages/")))
(unless (version< emacs-version "25.1")
(setq package-archive-priorities '(("melpa-stable" . 30)
("gnu" . 10)
("melpa" . 20))))
(unless package-archive-contents
(package-refresh-contents))
(let ((afsmnghr/packages '(use-package)))
(dolist (p afsmnghr/packages)
(unless (package-installed-p p)
(package-install p))))
(eval-when-compile
(require 'use-package))
Diminished modes.
(use-package delight :ensure t)
Key-bindings.
(use-package bind-key :ensure t)
Don’t store customizations.
(use-package cus-edit :defer t
:commands (customize-set-variable)
:custom (custom-file null-device))
Short, answering yes or no.
(fset 'yes-or-no-p 'y-or-n-p)
Clear UI.
(menu-bar-mode -1)
(if tool-bar-mode
(tool-bar-mode -1))
(column-number-mode -1)
(blink-cursor-mode -1)
(line-number-mode -1)
(size-indication-mode -1)
(setq ring-bell-function 'ignore)
Time in the modeline.
(setq display-time-interval 1
display-time-format "%H:%M"
display-time-default-load-average nil)
(display-time-mode)
Dialogs stay in emacs.
(setq use-dialog-box nil
use-file-dialog nil
epg-pinentry-mode 'loopback)
Unsorted settings.
(setq show-paren-style 'mixed
word-wrap t
search-highlight t
query-replace-highlight t
select-enable-clipboard t
echo-keystrokes 0.1
enable-local-eval t)
Load my themes. Enable theme on the frame type.
(defun afsmnghr/load-theme ()
"Load a theme"
(add-to-list 'custom-theme-load-path "~/.emacs.d/themes")
(if (display-graphic-p)
(load-theme 'spolsky t)
(load-theme 'spolsky-term t)))
(defun afsmnghr/enable-theme (frame)
"Enable theme the current frame depending on the frame type"
(with-selected-frame frame
(if (window-system)
(progn
(unless (custom-theme-enabled-p 'spolsky)
(if (custom-theme-enabled-p 'spolsky-term)
(disable-theme 'spolsky-term))
(enable-theme 'spolsky)))
(progn
(unless (custom-theme-enabled-p 'spolsky-term)
(if (custom-theme-enabled-p 'spolsky)
(disable-theme 'spolsky))
(enable-theme 'spolsky-term))))))
(add-hook 'after-init-hook 'afsmnghr/load-theme)
;; don't change theme inside docker container
(unless (file-exists-p "/.dockerenv")
(add-hook 'after-make-frame-functions 'afsmnghr/enable-theme))
Enable built-in modes.
(global-visual-line-mode t)
(global-font-lock-mode t)
(global-auto-revert-mode t)
(delete-selection-mode t)
Dired listing settings.
(setq dired-listing-switches "-lhvA")
Set utf-8 everywhere.
(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(setq buffer-file-coding-system 'utf-8
file-name-coding-system 'utf-8)
Prefer space indentation.
(setq-default tab-width 2
tab-always-indent 'complete
indent-tabs-mode nil)
(use-package elec-pair
:commands electric-pair-mode
:config (electric-pair-mode 1))
(use-package whitespace
:hook (prog-mode . whitespace-mode)
:custom
((whitespace-line-column 120)
(whitespace-style '(face lines-tail))))
Add comment fn.
(defun comment-or-uncomment-region-or-line ()
"Un / Comments the region or the current line if there's no active region."
(interactive)
(let (beg end)
(if (region-active-p)
(setq beg (region-beginning) end (region-end))
(setq beg (line-beginning-position) end (line-end-position)))
(comment-or-uncomment-region beg end)
(forward-line)))
My keybindings almost defaulted.
(global-set-key (kbd "C-x w") 'kill-buffer-and-window)
(global-set-key (kbd "C-z") 'undo)
(global-set-key (kbd "C-x o") 'ace-window)
(global-set-key (kbd "C-w") 'clipboard-kill-region)
(global-set-key (kbd "M-w") 'clipboard-kill-ring-save)
(global-set-key (kbd "C-y") 'clipboard-yank)
(global-set-key (kbd "M-q") 'query-replace-regexp)
(global-set-key [remap comment-dwim] 'comment-or-uncomment-region-or-line)
Reverse input.
(use-package reverse-im :ensure t :defer 1
:commands reverse-im-activate
:config (reverse-im-activate "russian-computer"))
(setq history-length t
history-delete-duplicates t
savehist-save-minibuffer-history 1
savehist-autosave-interval 60
savehist-additional-variables '(search-ring regexp-search-ring comint-input-ring))
(savehist-mode 1)
(setq backup-directory-alist '(("." . "~/.emacs.d/backups"))
auto-save-file-name-transforms '((".*" "~/.emacs.d/auto-save-list/" t))
delete-old-versions t
version-control t
vc-make-backup-files t
backup-by-copying t
kept-new-versions 2
kept-old-versions 2)
(use-package recentf :defer t
:after ido
:init (recentf-mode 1)
:commands recentf-mode
:custom ((recentf-max-saved-items 30)
(recentf-keep '(file-remote-p file-readable-p)))
:config (run-with-idle-timer 10 t 'recentf-save-list))
(use-package bookmark :defer t
:after ido
:custom (bookmark-save-flag t)
:commands (bookmark-jump bookmark-all-names)
:preface
(defun jump-to-bookmark ()
(interactive)
(bookmark-jump
(ido-completing-read "Jump to bookmark: "
(bookmark-all-names))))
:bind
(:map global-map ("C-x r b" . jump-to-bookmark)))
(defadvice save-buffers-kill-emacs (around no-query-kill-emacs activate)
"Prevent annoying \"Active processes exist\" query when you quit Emacs."
(cl-letf (((symbol-function #'process-list) (lambda ())))
ad-do-it))
(defun tangle-init ()
"If the current buffer is 'init.org' the code-blocks are
tangled, and the tangled file is compiled."
(when (equal (buffer-file-name)
(expand-file-name (concat user-emacs-directory "init.org")))
;; Avoid running hooks when tangling.
(let ((prog-mode-hook nil))
(org-babel-tangle)
(byte-compile-file (concat user-emacs-directory "init.el")))))
(defun afsmnghr/minibuffer-setup-hook ()
(setq gc-cons-threshold most-positive-fixnum
gc-cons-percentage 0.6))
(defun afsmnghr/minibuffer-exit-hook ()
(setq gc-cons-threshold 5000000
gc-cons-percentage 0.1))
(add-hook 'minibuffer-setup-hook #'afsmnghr/minibuffer-setup-hook)
(add-hook 'minibuffer-exit-hook #'afsmnghr/minibuffer-exit-hook)
(add-hook 'after-save-hook #'tangle-init)
(add-hook 'before-save-hook #'delete-trailing-whitespace)
Named buffers.
(use-package ace-window :ensure t :defer t
:custom ((aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
(aw-background nil)))
Enable ido (or “Interactively DO things”) everywhere.
(use-package ido-hacks :ensure t :defer 1)
(use-package flx-ido :ensure t
:after ido
:commands (flx-ido-mode ido-everywhere flx-ido-mode)
:init
(ido-mode 1)
(ido-everywhere 1)
(flx-ido-mode 1)
:custom
((flx-ido-threshold 1000)
(ido-enable-flex-matching t)
(ido-use-faces t)
(ido-virtual-buffers t)
(ido-auto-merge-work-directories-length -1)))
(use-package ido-completing-read+ :ensure t :pin melpa-stable
:after ido
:commands ido-ubiquitous-mode
:init (ido-ubiquitous-mode 1))
Use modern completion framework.
(use-package company :ensure t :defer 30
:init (global-company-mode t)
:commands global-company-mode
:custom ((company-backends '((company-files company-keywords company-capf company-dabbrev-code)))
(company-idle-delay 0.5)
(company-tooltip-flip-when-above t)
(company-dabbrev-downcase nil)))
(use-package company-flx :ensure t :defer t
:after company
:commands company-flx-mode
:init (with-eval-after-load 'company
(company-flx-mode +1)))
It’s Magit! A Git porcelain inside Emacs.
(use-package magit :ensure t :defer 1
:unless (version< emacs-version "24.4")
:custom
((magit-completing-read-function 'magit-ido-completing-read)
(magit-branch-arguments nil)
(magit-status-margin '(t "%Y-%m-%d %H:%M " magit-log-margin-width t 18))
(magit-default-tracking-name-function 'magit-default-tracking-name-branch-only)
(magit-set-upstream-on-push t)
(magit-push-always-verify nil)
(magit-restore-window-configuration t)
(vc-handled-backends nil)))
Travel back and forward in git history with git time machine.
(use-package git-timemachine :ensure t :defer t
:unless (version< emacs-version "24.4"))
Merging conflicts.
(use-package smerge-mode :defer t)
Setup projectile.
(use-package projectile :ensure t :defer 1
:delight '(:eval
(propertize (concat " " (projectile-project-name))
'face '(:foreground "#FD971F")))
:commands projectile-mode
:init
(projectile-mode)
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)
:custom
((projectile-enable-caching t)
(projectile-use-git-grep t)
(projectile-indexing-method 'native)
(projectile-sort-order 'recentf)
(projectile-switch-project-action 'projectile-dired)
(projectile-file-exists-remote-cache-expire (* 10 60))
(projectile-file-exists-local-cache-expire (* 5 60))
(projectile-require-project-root nil)
(projectile-idle-timer-seconds 60)
(projectile-completion-system 'ido)))
Use it for projectile and dumb-jump.
(use-package ag :ensure t :defer t)
Grepping tags.
(use-package dumb-jump :ensure t :defer t
:bind (("M-g o" . dumb-jump-go-other-window)
("M-g j" . dumb-jump-go)
("M-g i" . dumb-jump-go-prompt)
("M-g x" . dumb-jump-go-prefer-external)
("M-g z" . dumb-jump-go-prefer-external-other-window))
:custom ((dumb-jump-selector 'ido)
(dumb-jump-prefer-searcher 'git-grep)
(dumb-jump-force-searcher 'ag)))
Simple management docker containers.
(use-package docker :ensure t :defer t
:unless (version< emacs-version "24.4"))
Major mode for Dockerfile
.
(use-package dockerfile-mode :ensure t :defer t
:mode (("Dockerfile\\'" . dockerfile-mode)))
(use-package elixir-mode :ensure t :defer t)
(use-package python :defer t)
(use-package typescript-mode :ensure t :defer t
:custom (typescript-indent-level 2))
(use-package json :ensure t :defer t
:custom (js-indent-level 2))
(use-package js2-mode :ensure t :defer t
:mode (("\\.js\\'" . js2-mode)
("\\.json\\'" . javascript-mode))
:commands js2-mode
:custom
((js2-basic-offset 2)
(js2-indent-switch-body t)
(js2-auto-indent-p t)
(js2-highlight-level 3)
(js2-indent-on-enter-key t)))
(use-package markdown-mode :ensure t :defer t)
(use-package css-mode :ensure t :defer t)
(use-package sass-mode :ensure t :defer t
:mode (("\\.scss" . sass-mode)))
(use-package yaml-mode :ensure t :defer t)
(use-package web-mode :ensure t :defer t
:commands web-mode
:mode (("\\.html?\\'" . web-mode)
("\\.erb\\'" . web-mode)
("\\.vue" . web-mode)
("\\.jsx" . web-mode)
("\\.tsx" . web-mode))
:custom ((web-mode-markup-indent-offset 2)
(web-mode-enable-auto-pairing t)
(web-mode-enable-current-element-highlight t)
(web-mode-enable-block-face t)
(web-mode-enable-part-face t)))
Save org
buffers.
(defun afsmnghr/before-kill-emacs ()
(if (fboundp 'org-save-all-org-buffers)
(org-save-all-org-buffers)))
(add-hook 'kill-emacs-hook #'afsmnghr/before-kill-emacs)
Main org
.
(use-package org :defer 3
:config
(custom-set-variables
'(org-babel-load-languages
(quote ((emacs-lisp . t) (python . t) (shell . t) (js . t) (sql . t))))
'(org-confirm-babel-evaluate nil))
:custom
((org-log-done t)
(org-directory (getenv "ORG_PATH"))
(org-startup-indented t)
(org-indent-mode-turns-on-hiding-stars nil)
(org-todo-keywords
'((sequence "TODO(t!)" "NEXT(n@/!)" "INPROGRESS(i!)" "HOLD(h@/!)"
"DONE(d!)" "CANCELLED(c@/!)"))))
:bind
(:map global-map ("C-c a" . org-agenda)))
Org faces. Prepare colors for to do list.
(use-package org-faces
:after org
:custom
((org-todo-keyword-faces
'(("INPROGRESS" :foreground "DodgerBlue2" :weight bold)
("HOLD" :foreground "firebrick2" :weight bold)
("NEXT" :foreground "OrangeRed2" :weight bold)))
(org-priority-faces '((?A . (:foreground "firebrick2" :weight 'bold))
(?B . (:foreground "OrangeRed2"))
(?C . (:foreground "DodgerBlue2"))))))
Org blocks of sources.
(use-package org-src
:after org
:custom
((org-src-fontify-natively t)
(org-edit-src-content-indentation 2)
(org-src-tab-acts-natively t)
(org-src-preserve-indentation t)
(org-src-window-setup 'current-window)
(org-src-ask-before-returning-to-edit-buffer nil)))
Org agenda settings.
(use-package org-agenda
:after org
:custom
((org-agenda-files (list org-directory (concat org-directory "orgzly")))
(org-agenda-start-on-weekday 1)
(org-agenda-dim-blocked-tasks nil)
(org-agenda-block-separator nil)
(org-agenda-compact-blocks t)
(org-agenda-skip-scheduled-if-done t)
(org-agenda-skip-deadline-if-done t)
(org-agenda-clockreport-parameter-plist
(quote (:link t :maxlevel 9 :fileskip0 t :compact t :narrow 80)))))
Org protocol.
(use-package org-protocol :defer t
:after org
:custom (org-protocol-default-template-key "L"))
Org capturing.
(use-package org-capture :defer t
:after org
:preface
(defconst afsmnghr/org-capture-templates
'(("L" "Links" entry (file+olp+datetree afsmnghr/org-links)
"* %c \n%U %?%:initial")
("d" "Diary" entry (file+olp+datetree afsmnghr/org-diary)
"* %?\n%U\n"
:clock-in t :jump-to-captured t)))
:custom
((afsmnghr/org-diary (concat org-directory "diary.org"))
(afsmnghr/org-links (concat org-directory "links.org"))
(org-capture-templates afsmnghr/org-capture-templates))
:bind
(:map global-map ("C-c c" . org-capture)))
Timetracking.
(use-package org-clock :defer t
:after org
:commands org-clock-persistence-insinuate
:custom
((org-clock-history-length 30)
(org-clock-in-switch-to-state "INPROGRESS")
(org-clock-continuously t)
(org-clock-in-resume t)
(org-clock-into-drawer t)
(org-clock-out-remove-zero-time-clocks t)
(org-clock-out-when-done t)
(org-clock-auto-clock-resolution 'when-no-clock-is-running)
(org-clock-persist 'history)
(org-clock-clocked-in-display 'mode-line)
(org-clock-persist-query-resume nil)
(org-clock-report-include-clocking-task t))
:config
(org-clock-persistence-insinuate))
Notes.
(use-package deft :ensure t :defer t
:commands deft
:bind
(:map global-map ("C-c d" . deft)
:map deft-mode-map ("C-c d f" . deft-find-file))
:custom
((deft-extensions '("md" "org"))
(deft-default-extension "md")
(deft-recursive t)
(deft-directory (concat org-directory "notes"))
(deft-use-filename-as-title nil)
(deft-use-filter-string-for-filename t)
(deft-auto-save-interval -1.0)
(deft-file-naming-rules
'((noslash . "-")
(nospace . "-")
(case-fn . downcase)))))