Skip to content

AfsmNGhr/dockemacs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dockemacs - the minimal emacs noX

https://coveralls.io/repos/github/AfsmNGhr/dockemacs/badge.svg?branch=master https://travis-ci.com/AfsmNGhr/dockemacs.svg?branch=master https://img.shields.io/docker/v/afsmnghr/dockemacs.svg?style=flat https://img.shields.io/docker/image-size/afsmnghr/dockemacs.svg?style=flat https://img.shields.io/docker/pulls/afsmnghr/dockemacs.svg?style=flat https://img.shields.io/docker/stars/afsmnghr/dockemacs.svg?style=flat

https://i.imgur.com/V6vlv7Q.gif

Table of Contents

Installation

Docker

Install Docker Engine

Create emacs data volume.

docker volume create emacs_data

Executable

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
'

Environment variables

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

Escape in the box

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

Starting

Run and wait until the boot.

$ dockemacs

Initialization

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)

Perfomance

Garbage collector

(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))))))

Package Management

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)

Interface

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)

Themes

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))
Spolsky

images/spolsky-theme.png

Spolsky Term

images/spolsky-term-theme.png

Built-in

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")

Encoding

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)

Indentation

Prefer space indentation.

(setq-default tab-width 2
              tab-always-indent 'complete
              indent-tabs-mode nil)

Autopair

(use-package elec-pair
  :commands electric-pair-mode
  :config (electric-pair-mode 1))

Whitespace

(use-package whitespace
  :hook (prog-mode . whitespace-mode)
  :custom
  ((whitespace-line-column 120)
   (whitespace-style '(face lines-tail))))

Keybindings

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"))

History

(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)

Backups

(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)

Recent files

(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))

Bookmarks

(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)))

Hooks

(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)

Window management

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)))

Completion

IDO

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))

Company

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)))

VCS

Magit

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)))

Git time machine

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"))

Smerge-mode

Merging conflicts.

(use-package smerge-mode :defer t)

Project management

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)))

Search

Ag

Use it for projectile and dumb-jump.

(use-package ag :ensure t :defer t)

Tags

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)))

DevOps

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)))

Languages

Elixir

(use-package elixir-mode :ensure t :defer t)

Python

(use-package python :defer t)

Javascript

(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)))

Templates

(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)))

Org

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)))))