Skip to content
This repository has been archived by the owner on Mar 1, 2021. It is now read-only.

Latest commit

 

History

History
3011 lines (2627 loc) · 96 KB

personal.org

File metadata and controls

3011 lines (2627 loc) · 96 KB

Emacs rocks

This file contains my configurations and adaptations to my now graphene based setup.

Lambda

Lambda as λ

(defmacro λ (&rest body)
  `(lambda ()
     (interactive)
     ,@body))

(global-set-key (kbd "C-M-l") (λ (insert "\u03bb")))

Initial Setup

Optimization

 (setq lsp-keymap-prefix "H-y")
(setq read-process-output-max (* 1024 1024))
(defvar tom/gc-cons-threshold (* 100 1024 1024) "Don't make this too big")
(setq gc-cons-threshold most-positive-fixnum) ; don't gc during startup

define `when-let*`

(require 'subr-x)

Packages

Package management and loading of utility packages.

Management

Cask manages the packages installed.

(require 'cask "~/.cask/cask.el")
(cask-initialize)

Pallet

Using pallet allows to use Cask and M-x list-packages together – manual operations performed using packages.el are reflected in the Cask file.

(require 'pallet)
(pallet-mode t)

elpa keyring

(require 'gnu-elpa-keyring-update)
(gnu-elpa-keyring-update)

Code Loading

Recompile elisp code on load or require.

Since auto-compile is installed via cask, we cannot require it before package Management is setup.

(setq load-prefer-newer t)
(require 'auto-compile)
(auto-compile-on-load-mode 1)

Paradox

Github integration.

(require 'netrc)

(let ((github-paradox (netrc-machine (netrc-parse "~/.netrc.gpg") "paradox")))
  (setq paradox-github-token (netrc-get github-paradox "password")))

Home <-> Work

Some things have to be configured differently depending on where I am – at home or at work.

(defun tom/work? ()
  "Returns `T` if the current machine is at work, `NIL` otherwise"
  (file-exists-p (expand-file-name "~/.work")))

Hook helper

(require 'hook-helpers)

Emacs custom

(setq custom-file "~/.emacs.d/custom.el")
(load custom-file)

Helper functions

Escape HTML entities

Basics found on ergoemacs.org.

(defun tom/replace-html-chars (text)
  "Replace “<” to “&lt;” and other chars in TEXT."
  (save-restriction      
    (with-temp-buffer
      (insert text)
      (goto-char (point-min))
      (while (search-forward "&" nil t) (replace-match "&amp;" nil t))
      (goto-char (point-min))
      (while (search-forward "<" nil t) (replace-match "&lt;" nil t))
      (goto-char (point-min))
      (while (search-forward ">" nil t) (replace-match "&gt;" nil t))
      (buffer-string))))

Graphene

Stuff looted from graphene.

helper functions

(defun kill-default-buffer ()
  "Kill the currently active buffer -- set to C-x k so that users are not asked which buffer they want to kill."
  (interactive)
  (let (kill-buffer-query-functions) (kill-buffer)))

(defun kill-buffer-if-file (buf)
  "Kill a buffer only if it is file-based."
  (when (buffer-file-name buf)
    (when (buffer-modified-p buf)
        (when (y-or-n-p (format "Buffer %s is modified - save it?" (buffer-name buf)))
            (save-some-buffers nil buf)))
    (set-buffer-modified-p nil)
    (kill-buffer buf)))

(defun kill-all-buffers ()
    "Kill all file-based buffers."
    (interactive)
    (mapc (lambda (buf) (kill-buffer-if-file buf))
     (buffer-list)))

(defun kill-buffer-and-window ()
  "Close the current window and kill the buffer it's visiting."
  (interactive)
  (progn
    (kill-buffer)
    (delete-window)))

(defun create-new-buffer ()
  "Create a new buffer named *new*[num]."
  (interactive)
  (switch-to-buffer (generate-new-buffer-name "*new*")))

(defun insert-semicolon-at-end-of-line ()
  "Add a closing semicolon from anywhere in the line."
  (interactive)
  (save-excursion
    (end-of-line)
    (insert ";")))

(defun tom/comment-line-dwim (n)
  "Comment or uncomment current line and leave point after
it. With positive prefix, apply to N lines including current
one. With negative prefix, apply to -N lines above."
  (interactive "p")
  (comment-or-uncomment-region
   (line-beginning-position)
   (goto-char (line-end-position n)))
  (forward-line 1) (back-to-indentation))

(defun comment-current-line-dwim ()
  "Comment or uncomment the current line."
  (interactive)
  (save-excursion
    (push-mark (beginning-of-line) t t)
    (end-of-line)
    (comment-dwim nil)))

(defun newline-anywhere ()
  "Add a newline from anywhere in the line."
  (interactive)
  (end-of-line)
  (newline-and-indent))

(defun increase-window-height (&optional arg)
  "Make the window taller by one line. Useful when bound to a repeatable key combination."
  (interactive "p")
  (enlarge-window arg))

(defun decrease-window-height (&optional arg)
  "Make the window shorter by one line. Useful when bound to a repeatable key combination."
  (interactive "p")
  (enlarge-window (- 0 arg)))

(defun decrease-window-width (&optional arg)
  "Make the window narrower by one column. Useful when bound to a repeatable key combination."
  (interactive "p")
  (enlarge-window (- 0 arg) t))

(defun increase-window-width (&optional arg)
  "Make the window wider by one column. Useful when bound to a repeatable key combination."
  (interactive "p")
  (enlarge-window arg t))

;; Create a new instance of emacs
(when window-system
  (defun new-emacs-instance ()
    (interactive)
    (let ((path-to-emacs
           (locate-file invocation-name
                        (list invocation-directory) exec-suffixes)))
      (call-process path-to-emacs nil 0 nil))))

editing

;;(delete-selection-mode t)
(global-visual-line-mode t)
(setq nlinum-format "%4d")
(setq default-tab-width 2)
(setq-default tab-width 2)
(setq-default indent-tabs-mode nil)
(prefer-coding-system 'utf-8)
(set-language-environment 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)

(show-paren-mode t)
(setq blink-matching-paren t)
(electric-pair-mode t)


(push '("\\.json\\'" . json-mode) auto-mode-alist)

;; don't compile sass/scss on saving
(setq scss-compile-at-save nil)

;; 2-space indent for CSS
(setq css-indent-offset 2)

;; Default Ruby filetypes
(dolist (regex
         '("\\.watchr$" "\\.arb$" "\\.rake$" "\\.gemspec$" "\\.ru$" "Rakefile$" "Gemfile$" "Capfile$" "Guardfile$" "Rakefile$" "Cheffile$" "Vagrantfile$"))
  (add-to-list 'auto-mode-alist `(,regex . ruby-mode)))

;; Remap newline to newline-and-indent in ruby-mode
(define-hook-helper ruby-mode ()
  (define-key (current-local-map) [remap newline] 'reindent-then-newline-and-indent))

env

(require 'ivy)
(require 'swiper)
(require 'counsel)
(require 'counsel-projectile)

(global-set-key "\C-s" 'swiper)

(ivy-mode 1)
(setq ivy-use-virtual-buffers t)
(setq enable-recursive-minibuffers t)
(setq ivy-re-builders-alist '((swiper . ivy--regex-plus) (t . ivy--regex-fuzzy)))
(global-set-key (kbd "C-c C-r") 'ivy-resume)
(global-set-key (kbd "<f6>") 'ivy-resume)
(all-the-icons-ivy-rich-mode 1)
(ivy-rich-mode 1)
(setq inhibit-startup-message t
      color-theme-is-global t
      uniquify-buffer-name-style 'forward
      backup-directory-alist `((".*" . ,temporary-file-directory))
      auto-save-file-name-transforms `((".*" ,temporary-file-directory t)))

(global-set-key (kbd "M-x") 'counsel-M-x)
(global-set-key (kbd "C-x C-f") 'counsel-find-file)
(global-set-key (kbd "<F1> f") 'counsel-describe-function)
(global-set-key (kbd "<F1> v") 'counsel-describe-variable)
(global-set-key (kbd "<F1> l") 'counsel-find-library)
(global-set-key (kbd "<F2> i") 'counsel-info-lookup-symbol)
(global-set-key (kbd "<F2> u") 'counsel-unicode-char)
(global-set-key (kbd "C-c g") 'counsel-git)
(global-set-key (kbd "C-c j") 'counsel-git-grep)
(global-set-key (kbd "C-c k") 'counsel-ag)
(global-set-key (kbd "C-x l") 'counsel-locate)
(global-set-key (kbd "C-S-o") 'counsel-rhythmbox)
(define-key minibuffer-local-map (kbd "C-r") 'counsel-minibuffer-history)
(define-key ivy-mode-map (kbd "C-x b") 'persp-switch-to-buffer)
(fset 'yes-or-no-p 'y-or-n-p)

(require 'uniquify)
(setq uniquify-buffer-name-style 'forward)

(global-auto-revert-mode t)

(put 'autopair-newline 'disabled nil)
(put 'upcase-region 'disabled nil)
(put 'downcase-region 'disabled nil)
(put 'narrow-to-region 'disabled nil)
(require 'lv)

(defun ivy-display-function-lv (text)
  (let ((lv-force-update t))
    (lv-message
     (if (string-match "\\`\n" text)
         (substring text 1)
       text))))
(all-the-icons-ivy-setup)

keys

(global-set-key (kbd "C-x k")
                'kill-default-buffer)
(global-set-key (kbd "C-x C-k")
                'kill-buffer-and-window)
(global-set-key (kbd "C-c N")
                'new-emacs-instance)
(global-set-key (kbd "C-;")
                'insert-semicolon-at-end-of-line)
(global-set-key (kbd "C-<return>")
                'newline-anywhere)
(global-set-key (kbd "M-C-;")
                'tom/comment-line-dwim)
(global-set-key (kbd "C->")
                'increase-window-height)
(global-set-key (kbd "C-<")
                'decrease-window-height)
(global-set-key (kbd "C-,")
                'decrease-window-width)
(global-set-key (kbd "C-.")
                'increase-window-width)
(global-set-key (kbd "M-x")
                'smex)
(global-set-key (kbd "M-X")
                'smex-major-mode-commands)
(global-set-key (kbd "C-c s")
                'sr-speedbar-select-window)

look

;; Work around Emacs frame sizing bug when line-spacing
;; is non-zero, which impacts e.g. grizzl.
(setq redisplay-dont-pause t)

Global stuff

Single frame execution

(require 'fullframe)

Key bindings

  • Meta-Pause will delete the current frame
  • use f2 as tool-bar toggle (analog to f1 for menu-bar-mode)
(global-set-key (kbd "M-<pause>") 'delete-frame)
(global-set-key (kbd "<f1>") 'menu-bar-mode)
(global-set-key (kbd "<f2>") 'tool-bar-mode)
(global-set-key (kbd "<f5>") 'flyspell-mode)
(global-set-key (kbd "<f6>") 'flyspell-prog-mode)
(global-set-key (kbd "<f9>") 'flymake-mode)

Window switching/handling

Try out swsw.el,

(require 'swsw)
(swsw-mode t)
(global-set-key
 (kbd "H-o")
 (defhydra hydra-window (:color amaranth)
   "window"
   ("h" windmove-left)
   ("j" windmove-down)
   ("k" windmove-up)
   ("l" windmove-right)
   ("V" (lambda ()
          (interactive)
          (split-window-right)
          (windmove-right))
    "vert")
   ("X" (lambda ()
          (interactive)
          (split-window-below)
          (windmove-down))
    "horz")
   (">" enlarge-window-horizontally)
   ("<" shrink-window-horizontally)
   ("v" shrink-window)
   ("^" enlarge-window)
   ("t" transpose-frame "'")
   ("o" delete-other-windows "one" :color blue)
   ("a" (lambda () (interactive) (call-interactively 'swsw-select)) "swsw" :color blue)
   ("s" ace-swap-window "swap")
   ("d" ace-delete-window "del")
   ("i" ace-maximize-window "ace-one" :color blue)
   ("b" ido-switch-buffer "buf")
   ("m" headlong-bookmark-jump "bmk")
   ("q" nil "cancel")))

launcher map

Launch seldom used emacs tools via C-x l <KEY>.

Inspired/copied from endless parentheses blog

(global-set-key
 (kbd "H-L")
 (defhydra hydra-launch (:color blue :timeout 3)
   "launch"
   ("c" calc "calc")
   ("g" git-timemachine "git timemachine")
   ("d" ediff-buffers "ediff")
   ("f" find-dired "find")
   ("r" tom/projectile-ranger)
   ("G" rgrep "grep")
   ("h" man "man")
   ("p" paradox-list-packages "packages")
   ("s" tom/vterm "shell")
   ("t" proced "proced")
   ))

hjkl-navigation

(global-set-key
 (kbd "H-V")
 (defhydra hydra-vim-navi (:color red)
   "navigate"
   ("h" backward-char "left")
   ("j" next-line "down")
   ("k" previous-line "up")
   ("l" forward-char "right")))

Toggle states

(global-set-key
 (kbd "H-t")
 (defhydra hydra-toggle (:color red :timeout 3)
   "toggle"
   ("c" column-number-mode "col-nums")
   ("d" toggle-debug-on-error "debug on error")
   ("f" auto-fill-mode "auto fill")
   ("l" display-line-numbers-mode "show line numbers")
   ("L" toggle-truncate-lines "truncate lines")
   ("g" golden-ratio-mode "1.61803")
   ("q" toggle-debug-on-quit "debug on quit")
   ("n" narrow-or-widen-dwim "narrow")
   ("b" tom/ob-confirm-toggle "babel confirmation")))

Narrowing

(setq narrow-to-defun-include-comments t)
(defun narrow-or-widen-dwim (p)
  "If the buffer is narrowed, it widens. Otherwise, it narrows intelligently.
Intelligently means: region, org-src-block, org-subtree, or defun,
whichever applies first.
Narrowing to org-src-block actually calls `org-edit-src-code'.

With prefix P, don't widen, just narrow even if buffer is already
narrowed."
  (interactive "P")
  (declare (interactive-only))
  (cond ((and (buffer-narrowed-p) (not p)) (widen))
        ((region-active-p)
         (narrow-to-region (region-beginning) (region-end)))
        ((derived-mode-p 'org-mode)
         ;; `org-edit-src-code' is not a real narrowing command.
         ;; Remove this first conditional if you don't want it.
         (cond ((org-in-src-block-p)
                (org-edit-src-code)
                (delete-other-windows))
               ((org-at-block-p)
                (org-narrow-to-block))
               (t (org-narrow-to-subtree))))
        (t (narrow-to-defun))))

Rectangle

(defun tom/rec-ex-point-mark ()
  (interactive)
  (if rectangle-mark-mode
      (exchange-point-and-mark)
    (let ((mk (mark)))
      (rectangle-mark-mode 1)
      (goto-char mk))))

(global-set-key
 (kbd "H-C-r")
 (defhydra hydra-rectangle (:color amaranth
                            :body-pre (rectangle-mark-mode 1)
                            :post (deactivate-mark))
   "
  ^_k_^     _d_elete    _s_tring
_h_   _l_   _o_k        _y_ank
  ^_j_^     _n_ew-copy  _r_eset
^^^^        _e_xchange  _u_ndo
^^^^        ^ ^         _p_aste
"
   ("h" backward-char nil)
   ("l" forward-char nil)
   ("k" previous-line nil)
   ("j" next-line nil)
   ("e" tom/rec-ex-point-mark nil)
   ("n" copy-rectangle-as-kill nil)
   ("d" delete-rectangle nil)
   ("r" (if (region-active-p)
            (deactivate-mark)
          (rectangle-mark-mode 1)) nil)
   ("y" yank-rectangle nil)
   ("u" undo nil)
   ("s" string-rectangle nil)
   ("p" kill-rectangle nil)
   ("o" nil nil)))

Code Folding

(global-set-key
 (kbd "H-C-f")
 (defhydra hydra-folding (:color red)
   "
    _o_pen node    _n_ext fold       toggle _f_orward  _s_how current only
    _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)
   ("s" origami-show-only-node)))

Undo

(require 'undo-fu)

yasnippet Setup

A few variables to be used in snippets.

(setq fb-author "tregner")
(require 'yasnippet)
(require 'warnings)
(yas-reload-all)
(add-to-list 'warning-suppress-types '(yasnippet backquote-change))

Sticky window

(require 'popwin)
(popwin-mode 1)
;; (pop popwin:special-display-config)
(push '("*Flycheck error messages*" :height 0.1 :noselect t :position bottom) popwin:special-display-config)
(push '("\*GEBEN<.*> output\*" :regexp t :position left :width 0.3 :stick t :dedicated t) popwin:special-display-config)
(defun tom/sticky-window (name)
  "Make the window NAME sticky."
  (let ((curr-win (car (get-buffer-window-list name))))
    (set-window-buffer curr-win (get-buffer name))
    (set-window-dedicated-p curr-win t)))

perspectives

(defvar tom/workspaces-last-persp nil
  "A variable that contains the last accessed perspective")

(defun tom/workspace-exists-p (name)
  "Returns t if NAME is the name of an existing workspace."
  (when (symbolp name)
    (setq name (symbol-name name)))
  (unless (stringp name)
    (error "Expected a string, got a %s" (type-of name)))
  (member name (persp-names-current-frame-fast-ordered)))

(defun tom/workspace-switch-last ()
  "Switches to the last workspace"
  (interactive)
  (if (tom/workspace-exists-p tom/workspaces-last-persp)
      (persp-switch tom/workspaces-last-persp)
    (error "No previous workspace.")))

(defun tom/workspace-switch-project ()
  (interactive)
  (ivy-read "Switch to Project Perspective: "
            (if (projectile-ensure-project (projectile-project-p))
                (cons (projectile-default-project-name (projectile-project-root))
                      (projectile-relevant-known-projects))
              projectile-known-projects)
            :action (lambda (project)
                      (let ((-buff (current-buffer)))
                        (persp-switch "none")
                        (let ((projectile-completion-system 'ivy))
                          (projectile-switch-project-by-name project)
                          (persp-switch (file-name-nondirectory (directory-file-name project)))
                          (persp-remove-buffer -buff))))))

(require 'persp-mode)
(with-eval-after-load "persp-mode-projectile-bridge-autoloads"
  (add-hook 'persp-mode-projectile-bridge-mode-hook
            #'(lambda ()
                (if persp-mode-projectile-bridge-mode
                    (persp-mode-projectile-bridge-find-perspectives-for-all-buffers)
                  (persp-mode-projectile-bridge-kill-perspectives))))
  (add-hook 'after-init-hook
            #'(lambda ()
                (persp-mode-projectile-bridge-mode 1))
            t))
(defmacro with-perspective (name &rest body)
  "Switch to the perspective given by NAME while evaluating BODY."
  (declare (indent 1))
  (let ((old (cl-gensym)))
    `(progn
       (let ((,old (when (get-current-persp) (persp-name (get-current-persp))))
             (last-persp-cache persp-last-persp-name))
         (unwind-protect
             (progn
               (persp-switch ,name)
               ,@body)
           (when ,old (persp-switch ,old)))
         (setq persp-last-persp-name last-persp-cache)))))
(setq wg-morph-on nil
      persp-autokill-buffer-on-remove 'kill
      persp-nil-name "nil"
      persp-nil-hidden t
      persp-auto-save-fname "autosave"
      persp-auto-resume-time 1
      persp-auto-save-opt 1
      persp-save-dir (concat tom/--emacs-dir "/workspaces/"))

(defun tom/workspaces*track-last-persp (switch-fun &rest args)
  (let ((before-persp (safe-persp-name (get-current-persp)))
        (after-persp (apply switch-fun args)))
    (when (not (string= before-persp after-persp))
      (setq tom/workspaces-last-persp before-persp))))

(advice-add #'persp-switch :around #'tom/workspaces*track-last-persp)

(global-set-key (kbd "H-P")
                (defhydra hydra-persp (:color red :timeout 3)
                  "Perspective"
                  ("d" tom/workspace-switch-project "Switch dired")
                  ("s" (call-interactively 'persp-switch) "Switch/Create")
                  ("m" (call-interactively 'persp-set-buffer) "Move buffer")
                  ("n" persp-next "Next Perspective")
                  ("p" persp-prev "Previous Perspective")
                  ("i" projectile-invalidate-cache "Invalidate file cache")
                  ("q" nil "Quit")))


Tramp Setup

(setq tramp-shell-prompt-pattern "\\(?:^\\| \\)[^]#$%>❯\n]*#?[]#$%>❯] *\\(�\\[[0-9;]*[a-zA-Z] *\\)*")

GPG setup

GPG is handled almost transparently in emacs nowadays; this setup helps for remote sessions.

<wgreenhouse> tomterl: this assumes emacsclient/emacs –daemon are [15:35] invoked from a shell that is properly setting GPG_AGENT_INFO already <wgreenhouse> but according to documentation, GPG_TTY needs to be adjusted for each terminal > wgreenhouse: thanks – should be the case, I’ll make a note <taylanub> “arc4random_uniform(9000) + 1000” should give me a good [15:36] 4-digit random number, right ? <wgreenhouse> tomterl: also, I really don’t like it because it will [15:38] screw up DISPLAY for any jobs started from the gui emacsclient when I am back at that machine > wgreenhouse: Yepp - we see, when I have the time to tackle gpg, [15:39] maybe I find a cleaner solution <baboon`> how can I call several functions over a single selection without re-selecting between each

(defun tom/kludge-gpg-agent (frame) (unless (display-graphic-p) (setenv “DISPLAY” nil) (setenv “GPG_TTY” (terminal-name frame))))

(add-hook ‘after-make-frame-functions ‘wg/kludge-gpg-agent)

Emacs shell

`vterm`

(defun tom/vterm ()
  "Run projectile-run-vterm in a dedicated frame."
  (interactive)
  (unless
      (and tom/shellframe (framep tom/shellframe) (frame-live-p tom/shellframe))
    (setq tom/shellframe (make-frame)))
  
  (if (projectile-project-p) 
      (let* ((proj (projectile-ensure-project (projectile-project-p)))
             (projectile-switch-project-action #'(lambda () t)))
        (select-frame tom/shellframe)
        (raise-frame)
        (projectile-with-default-dir proj
          (projectile-run-vterm)))
    (projectile-run-vterm)))

(define-hook-helper vterm-mode ()
  :name vterm_hide_modeline
  (setq mode-line-format nil)
  (setq header-line-format nil))
(define-hook-helper vterm-mode ()
  :name vterm_ctrl_y
  (local-set-key (kbd "C-y") 'vterm--self-insert)
  (local-set-key (kbd "TAB") 'vterm--self-insert))

`eshell`

I use vim and htop, so let’s add those to eshell-visual-commands.

(require 'eshell)
(require 'em-term)
(require 'em-smart)
(setq eshell-where-to-jump 'begin)
(setq eshell-review-quick-commands nil)
(setq eshell-smart-space-goes-to-end t)

(add-to-list 'eshell-visual-commands "htop")
(add-to-list 'eshell-visual-commands "vim")
(add-hook 'eshell-mode-hook 'eshell-smart-initialize)
(setq eshell-prompt-regexp "^[^#$]*[#$] ")
(defvar tom/shellframe nil)
(add-to-list 'frame-inherited-parameters 'alpha)
(define-hook-helper eshell-mode ()
  :name eshell_hide_modeline
  (setq mode-line-format nil))

(defun tom/eshell ()
  "Start or switch to an eshell specific to the current
      projectile project, or the global '*eshell*' if not in a
      project"
  (interactive)
  (let ((pers (get-current-persp))
        (proj (if (projectile-project-p) (projectile-project-root))))
    (unless
        (and tom/shellframe (framep tom/shellframe) (frame-live-p tom/shellframe))
      (setq tom/shellframe (make-frame)))
    (select-frame tom/shellframe)
    (raise-frame)
    (if proj
        (let* ((-project (projectile-default-project-name proj))
               (eshell-buffer-name (concat "* " -project " eshell *")))
          (if pers (persp-switch (persp-name pers)))
          (cd proj)
          (if (buffer-live-p (get-buffer eshell-buffer-name))
              (switch-to-buffer eshell-buffer-name)
            (eshell)))
      (eshell))))

(defun tom/short-path (p-lst &optional len)
  (let ((len (or len 3))
        (path (if (listp p-lst) p-lst (split-string p-lst "/"))))
    (if (> (length path) len)
        (concat
         (mapconcat (lambda (elm) (if (zerop (length elm)) ""
                                    (substring elm 0 1)))
                    (butlast path len)
                    "/")
         "/"
         (mapconcat (lambda (elm) elm)
                    (last path len)
                    "/"))
      (mapconcat (lambda (elm) elm)
                 path
                 "/"))))

eshell prompt

(defun eshell/ef (fname-regexp &rest dir) (ef fname-regexp default-directory))

;;; ---- path manipulation

(defun pwd-repl-home (pwd)
  (interactive)
  (let* ((home (expand-file-name (getenv "HOME")))
         (home-len (length home)))
    (if (and
         (>= (length pwd) home-len)
         (equal home (substring pwd 0 home-len)))
        (concat "~" (substring pwd home-len))
      pwd)))

(defun curr-dir-git-branch-string (pwd)
  "Returns current git branch as a string, or the empty string if
PWD is not in a git repo (or the git command is not found)."
  (interactive)
  (when (and (eshell-search-path "git")
             (locate-dominating-file pwd ".git"))
    (let ((git-output (shell-command-to-string (concat "cd " pwd " && git branch | grep '\\*' | sed -e 's/^\\* //'"))))
      (propertize (concat "["
              (if (> (length git-output) 0)
                  (substring git-output 0 -1)
                "(no branch)")
              "]") 'face `(:foreground "darkgreen"))
      )))

(setq eshell-prompt-function
      (lambda ()
        (concat
         (propertize
          (tom/short-path (pwd-repl-home (eshell/pwd)))
          'face `(:foreground "darkorange"))
         (or (curr-dir-git-branch-string (eshell/pwd)))
         (propertize "$ " 'face 'default))))

(setq eshell-highlight-prompt nil)

Completion

I use company-mode as completion system. For most languages I use the newer GNU global – with pygmentize backend – to provide tags for code traversal and (additional) completion.

Configure company-mode

Use company-mode globally.

I live in a case sensitive world, so don’t alter the case of completions, but provide completions without regard for the case fo the stuff I entered.

;(global-auto-complete-mode -1)
(require 'company)
(add-hook 'after-init-hook 'global-company-mode)
(setq company-dabbrev-downcase nil
      company-dabbrev-ignore-case t)
(eval-after-load 'company
  '(define-key company-active-map (kbd "C-c h") #'company-quickhelp-manual-begin))

Key bindings

Fasten seat belts, we enter hyper space…

(global-set-key (kbd "H-SPC") 'company-complete)

company-box

(require 'company-box)
(add-hook 'company-mode-hook 'company-box-mode)

GNU global

(defun gtags-root-dir ()
    "Returns GTAGS root directory or nil if doesn't exist."
    (with-temp-buffer
      (if (zerop (call-process "global" nil t nil "-pr"))
          (buffer-substring (point-min) (1- (point-max)))
        nil)))

(defun gtags-update ()
    "Make GTAGS incremental update"
    (call-process "global" nil nil nil "-u"))

(defun gtags-root-dir ()
    "Returns GTAGS root directory or nil if doesn't exist."
    (with-temp-buffer
      (if (zerop (call-process "global" nil t nil "-pr"))
          (buffer-substring (point-min) (1- (point-max)))
        nil)))

(defun gtags-update-single(filename)  
      "Update Gtags database for changes in a single file"
      (interactive)
      (start-process "update-gtags" "update-gtags" "bash" "-c" (concat "cd " (gtags-root-dir) " ; gtags --single-update " filename )))

(defun gtags-update-current-file()
      (interactive)
      (defvar filename)
      (setq filename (replace-regexp-in-string (gtags-root-dir) "." (buffer-file-name (current-buffer))))
      (gtags-update-single filename)
      (message "Gtags updated for %s" filename))

(defun gtags-update-hook()
      "Update GTAGS file incrementally upon saving a file"
      (when (and (boundp 'ggtags-mode) ggtags-mode)
        (when (gtags-root-dir)
          (gtags-update-current-file))))

(add-hook 'after-save-hook 'gtags-update-hook)

Projects

I use projectile to manage my projects.

(require 'projectile)
(projectile-global-mode)
(define-key projectile-mode-map (kbd "C-c p") 'projectile-command-map)

Use projectile automatically.

(define-hook-helper prog-mode ()
  :name projectile
  (progn (require 'dash)(projectile-mode 1)))

Ignore .git, and especially those in base/includes which are always to be treated as part of the project by projectile.

Marking the root of a project are only

  • RoboFile.php for php-projects
  • manifest.json for node/foxx applications (javascript)
  • .projectile as manually added mark for other project types
(require 'projectile)
(defun tom/projectile-ranger ()
  "Open `ranger' at the root of the project."
  (interactive)
  (golden-ratio-mode -1)
  (ranger (projectile-project-root)))
(add-to-list 'projectile-globally-ignored-directories ".git")
(add-to-list 'projectile-globally-ignored-directories "base/.git")
(add-to-list 'projectile-globally-ignored-directories "includes/.git")
(add-to-list 'projectile-globally-ignored-directories ".cask")
(add-to-list 'projectile-project-root-files "RoboFile.php")
(add-to-list 'projectile-project-root-files "manifest.json")
(add-to-list 'projectile-project-root-files ".projectile")
(setq projectile-project-root-files-functions '(projectile-root-top-down))

(setq projectile-find-dir-includes-top-level t)
(setq projectile-indexing-method 'native)
(setq projectile-enable-caching t)

Perspectives

(when (not (fboundp 'make-variable-frame-local))
  (defun make-variable-frame-local (variable) variable))
(persp-mode)

Keys

(global-set-key (kbd "H-p") 'projectile-commander)

Completion

Use ivy/counsel for Completion

(counsel-projectile-mode 1)
(setq projectile-completion-system 'ivy)

keep project todos in org-mode file(s)

(require 'org-projectile)
(setq org-projectile-projects-file
      (concat tom/--emacs-dir "/projects_todos.org"))
(add-to-list 'org-capture-templates
             (org-projectile-project-todo-entry
              :capture-character "t"))
            
(setq org-agenda-files (append org-agenda-files (org-projectile-todo-files)))
(global-set-key (kbd "C-c o") 'org-capture)
(global-set-key (kbd "C-c n p") 'org-projectile-project-todo-completing-read)

Search for and checkout projects `in the wild`

(require 'elescope)
(setf elescope-root-folder "~/projects"
      elescope-use-full-path t
      elescope-clone-depth nil)

Appearance

Font

-> ==

;;(require 'unicode-fonts)
;;(unicode-fonts-setup)
  (defvar tom/default-font "Fira Code Medium-10"
    "The font to use under normal circumstances")
  (unless (tom/work?)
    (setq tom/default-font "Courier Prime Code-12"))
  (defvar tom/fallback-font "-Free-Symbola-normal-normal-semicondensed-*-12-*-*-*-*-0-iso10646-1"
    "Font to use, if the default font misses a glyph.")
  (require 'composite)
  (defvar composition-ligature-table (make-char-table nil))
  (define-hook-helper prog-mode ()
    :name pm-ligatures
    (progn (setq-local composition-function-table composition-ligature-table)
(let ((alist '((33 . ".\\(?:\\(?:==\\|!!\\)\\|[!=]\\)")
               (35 . ".\\(?:###\\|##\\|_(\\|[#(?[_{]\\)")
               (36 . ".\\(?:>\\)")
               (37 . ".\\(?:\\(?:%%\\)\\|%\\)")
               (38 . ".\\(?:\\(?:&&\\)\\|&\\)")
               (42 . ".\\(?:\\(?:\\*\\*/\\)\\|\\(?:\\*[*/]\\)\\|[*/>]\\)")
               (43 . ".\\(?:\\(?:\\+\\+\\)\\|[+>]\\)")
               (45 . ".\\(?:\\(?:-[>-]\\|<<\\|>>\\)\\|[<>}~-]\\)")
               (46 . ".\\(?:\\(?:\\.[.<]\\)\\|[.=-]\\)")
               (47 . ".\\(?:\\(?:\\*\\*\\|//\\|==\\)\\|[*/=>]\\)")
               (48 . ".\\(?:x[a-zA-Z]\\)")
               (58 . ".\\(?:::\\|[:=]\\)")
               (59 . ".\\(?:;;\\|;\\)")
               (60 . ".\\(?:\\(?:!--\\)\\|\\(?:~~\\|->\\|\\$>\\|\\*>\\|\\+>\\|--\\|<[<=-]\\|=[<=>]\\||>\\)\\|[*$+~/<=>|-]\\)")
               (61 . ".\\(?:\\(?:/=\\|:=\\|<<\\|=[=>]\\|>>\\)\\|[<=>~]\\)")
               (62 . ".\\(?:\\(?:=>\\|>[=>-]\\)\\|[=>-]\\)")
               (63 . ".\\(?:\\(\\?\\?\\)\\|[:=?]\\)")
               (91 . ".\\(?:]\\)")
               (92 . ".\\(?:\\(?:\\\\\\\\\\)\\|\\\\\\)")
               (94 . ".\\(?:=\\)")
               (119 . ".\\(?:ww\\)")
               (123 . ".\\(?:-\\)")
               (124 . ".\\(?:\\(?:|[=|]\\)\\|[=>|]\\)")
               (126 . ".\\(?:~>\\|~~\\|[>=@~-]\\)")
               )
             ))
  (dolist (char-regexp alist)
    (set-char-table-range composition-function-table (car char-regexp)
                          `([,(cdr char-regexp) 0 font-shape-gstring]))))
               ))
  (setq default-frame-alist `((font . ,tom/default-font)))

  ;; (require 'all-the-icons)
  ;; (require 'all-the-icons-ivy)
;(set-fontset-font t 'unicode (font-spec :family "all-the-icons") nil 'append)
;(set-fontset-font t 'unicode (font-spec :family "file-icons") nil 'append)
;(set-fontset-font t 'unicode (font-spec :family "Material Icons") nil 'append)
;(set-fontset-font t 'unicode (font-spec :family "github-octicons") nil 'append)
;(set-fontset-font t 'unicode (font-spec :family "FontAwesome") nil 'append)
;(set-fontset-font t 'unicode (font-spec :family "Weather Icons") nil 'append)

icons-in-terminal integration

(add-to-list 'load-path "~/.local/share/icons-in-terminal/")
(require 'icons-in-terminal)

Fontlock et.al.

(global-font-lock-mode 1)
(global-hl-line-mode -1)
(line-number-mode 1)
(column-number-mode 1)
(setq mouse-buffer-menu-mode-mult 1)
(setq ranger-show-literal nil)

Scrollbar

(when (fboundp 'scroll-bar-mode)
  (scroll-bar-mode -1))

Menu and Toolbar

(when (fboundp tool-bar-mode)
  (tool-bar-mode -1))
(when (fboundp menu-bar-mode)
  (menu-bar-mode -1))

Color Theme

(add-to-list 'custom-theme-load-path "~/.emacs.d/site/themes/")
(load-file "~/.emacs.d/site/elegance.el")
;;;(load-theme 'doom-dark+ t)

Adapt company-mode

    (require 'color)

(let ((bg (face-attribute 'default :background))
      (fg (face-attribute 'default :foreground)))
  (custom-set-faces
   `(company-tooltip ((t (:inherit default :background ,(color-lighten-name bg 20)))))
   `(company-scrollbar-bg ((t (:background ,(color-lighten-name bg 12)))))
   `(company-scrollbar-fg ((t (:background ,(color-lighten-name bg 2)))))
   `(company-tooltip-selection ((t (:inherit default :foreground ,(color-lighten-name bg 12) :background ,(color-lighten-name fg 20)))))
   `(company-tooltip-common ((t (:inherit default :background ,(color-lighten-name bg 12) :foreground ,(color-lighten-name fg 20)))))))

TreeView

Show nice(?) icons

(setq tree-widget-image-enable 1)

Colors on terminals

(require 'color-theme-approximate)

Highlight uncommited changes

Show uncomitted changes in the fringe.

(require 'diff-hl)
(global-diff-hl-mode)
(add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh)

Whitespace visualization

I find it unnecessary to mark normal spaces, but to visualize tab characters and newlines is a possible lifesaver.

(setq whitespace-display-mappings ‘( (newline-mark 10 [8629 10]) (tab-mark 9 [8677 9] [92 9]) ))

(setq whitespace-style ‘(face tabs newline tab-mark newline-mark)) (add-hook ‘prog-mode-hook ‘whitespace-mode) (add-hook ‘text-mode-hook ‘whitespace-mode)

Hide the mode line

This is interesting for presentations (e.g.).

(defvar-local hidden-mode-line-mode nil)
(defvar-local hide-mode-line nil)

(define-minor-mode hidden-mode-line-mode
  "Minor mode to hide the mode-line in the current buffer."
  :init-value nil
  :global nil
  :variable hidden-mode-line-mode
  :group 'editing-basics
  (if hidden-mode-line-mode
      (setq hide-mode-line mode-line-format
            mode-line-format nil)
    (setq mode-line-format hide-mode-line
          hide-mode-line nil))
  (force-mode-line-update)
  ;; Apparently force-mode-line-update is not always enough to
  ;; redisplay the mode-line
  (redraw-display)
  (when (and (called-interactively-p 'interactive)
             hidden-mode-line-mode)
    (run-with-idle-timer
     0 nil 'message
     (concat "Hidden Mode Line Mode enabled.  "
             "Use M-x hidden-mode-line-mode to make the mode-line appear."))))

Minimap

This is a birds eye view of the current buffer.

(global-set-key (kbd "H-M") 'minimap-mode)

Fringe

(fringe-mode (cons 10  8))

Frametitle

(setq frame-title-format
      '(""
        (:eval
         (let ((project-name (projectile-project-name)))
           (if (not (string= "-" project-name))
               project-name
             invocation-name)))
        ""
        (:eval
         (if (buffer-file-name)
             (concat ": " (tom/short-path (abbreviate-file-name (buffer-file-name)) 1))
           ": %b"))))

(define-hook-helper focus-out ()
  (set-frame-parameter (selected-frame)
                       'title (let ((project-name (projectile-project-name)))
                                (if (not (string= "-" project-name))
                                    project-name
                                  invocation-name))))
(define-hook-helper focus-in ()
  (set-frame-parameter (selected-frame)
                       'title nil))

Dashboard

(setq dashboard-set-heading-icons t)
(setq dashboard-set-file-icons t)
(setq dashboard-set-navigator t)
(setq dashboard-items '((projects . 10)(bookmarks . 10)))
(dashboard-setup-startup-hook)

coloured parenthesis

(require 'rainbow-delimiters)
(define-hook-helper prog-mode ()
  :name rainbow
  (rainbow-delimiters-mode 1))

shorten mode names

(setq
 cyphejor-rules
 '(:upcase
   ("bookmark"    "")
   ("buffer"      "β")
   ("diff"        "Δ")
   ("dired"       "δ")
   ("emacs"       "ε")
   ("fundamental" "")
   ("inferior"    "i" :prefix)
   ("interaction" "i" :prefix)
   ("interactive" "i" :prefix)
   ("lisp"        "λ" :postfix)
   ("menu"        "" :postfix)
   ("mode"        "")
   ("package"     "")
   ("python"      "π")
   ("php"         "Ψ")
   ("shell"       "sh" :postfix)
   ("text"        "ξ")
   ("wdired"      "↯δ")))
(cyphejor-mode 1)

modeline

(doom-modeline-mode 1)

Cursor

Allways find the cursor.

(require 'beacon)
(beacon-mode 1)

Alert notifications

(require 'alert)
(setq alert-default-style 'libnotify)

Indentation highlight

(define-hook-helper prog-mode ()
  :name indent-guide
  (indent-guide-mode t))

org-mode

Variables

Basis / Agenda

(setq
 org-directory "~/ownCloud/org-mode"
 org-return-follows-link t
 org-src-fontify-natively t
 org-tags-exclude-from-inheritance '("PROJECT")
 org-list-allow-alphabetical nil
 org-agenda-include-inactive-timestamps t
 org-todo-keywords '((sequence "TODO(t)" "ACTIVE(a)" "PAUSED(p)" "BLOCKED(b)" "DELEGATED(D)" "WAITING(w)" "|" "CANCELED(c)" "DONE(d)"))
 org-todo-keyword-faces '(
                          ("TODO" . org-warning)
                          ("ACTIVE" . "yellow")
                          ("PAUSED" . "goldenrod")
                          ("BLOCKED" . "red")
                          ("CANCELED" . "dimgray")
                          ("DELEGATED" . "purple")
                          ("WAITING" . "salmon")
                          ("DONE" . "darkgreen")))
(if (tom/work?)
    (setq org-agenda-files nil
          org-agenda-file-regexp "^\[0-9\]+")
  (setq org-agenda-files (quote ("~/ownCloud/org-mode/todos.org"
                                 "~/ownCloud/org-mode/joocom.org"))))

owncloud

Use org-cladav to integrate with an owncloud calendar.

x#+BEGIN_SRC emacs-lisp (defvar tom/–org-caldav-dir (expand-file-name “org-caldav” tom/–src-base)) (add-to-list ‘load-path tom/–org-caldav-dir) (require ‘org-caldav)

(defvar tom/–owncloud-base “https://muehlenweg.dyndns-home.com/owncloud/remote.php/”) (setq org-caldav-url (concat tom/–owncloud-base “caldav/calendars/tom”) org-caldav-calendar-id “orgmode” org-caldav-inbox “~/ownCloud/org-mode/incoming.org” org-caldav-files (quote (“~/ownCloud/org-mode/todos.org” “~/ownCloud/org-mode/joocom.org”)) org-icalendar-timezone “Europe/Berlin”)

x#+END_SRC

mobileorg for android

(setq
 org-mobile-directory (expand-file-name "~/ownCloud/org-mode")
 org-mobile-files (quote (org-agenda-files))
 org-mobile-inbox-for-pull (expand-file-name "~/ownCloud/org-mode/mobileorg.org"))

Refile

(setq
    org-refile-targets (quote ((nil :maxlevel . 9)
                               (org-agenda-files :maxlevel . 9)))
    )

babel

Set the converter paths before loading the languages, some packages need them at load time.

The ditaa.jar location;

(setq org-ditaa-jar-path  (concat tom/--emacs-dir "/site/ditaa.jar"))

The plantuml.jar location

(require 'plantuml-mode)
(setq org-plantuml-jar-path (concat tom/--emacs-dir "/site/plantuml.jar"))

The mermaid.cli location

(setq ob-mermaid-cli-path (concat  (expand-file-name "~") "/bin/mmdc"))

I really like org-babel to use zsh

(setq org-babel-sh-command "zsh")

The languages I like to use.

(org-babel-do-load-languages
 'org-babel-load-languages 
 '((emacs-lisp . t)  (shell . t)
   (ditaa . t) (sass . t)
   (lisp . t) (gnuplot . t)
   (http . t) (plantuml .t)
   (sql-mode .t) (mermaid . t)))

Don’t confirm evaluation.

(defun tom/ob-confirm-toggle ()
  "Turn confirmation for babel code block evaluation on/off."
  (interactive)
  (setq org-confirm-babel-evaluate (not org-confirm-babel-evaluate)))
(global-set-key (kbd "C-c c") 'tom/ob-confirm-toggle)

Use inheritance for properties, needed for e.g. my zshorg project.

(setq org-use-property-inheritance t)

Execute code-blocks on publishing

(setq org-export-babel-evaluate t)

Tangle hook

Remove code references in code prior to tangling; that way I can use them anywhere in the code and get nice links/references in the weaved document, but don’t have to hide them in code comments.

I always use the form (ref:label) for code references.

(defun tr/remove-code-labels ()
  "remove (ref:.*) from all lines"
  (goto-char (point-min))
  (let* (
         (lbl-re "[ \t]*(ref:[a-zA-Z0-9_-]*)"
                 ))
    (while (re-search-forward lbl-re nil t)
      (replace-match "")
      )))

;(add-hook 'org-babel-tangle-body-hook
;          (λ () (tr/remove-code-labels)))

agenda views

(setq org-agenda-custom-commands
'(

("P" "Projects"
((tags "PROJECT")))

("H" "Office and Home Lists"
     ((agenda)
          (tags-todo "OFFICE")
          (tags-todo "HOME")
          (tags-todo "COMPUTER")
          (tags-todo "DVD")
          (tags-todo "READING")))
("O" "Office and Home Lists"
     ((agenda)
          (tags-todo "OFFICE")
          ))

("D" "Daily Action List"
     (
          (agenda "" ((org-agenda-ndays 1)
                      (org-agenda-sorting-strategy
                       (quote ((agenda time-up priority-down tag-up)
     )))
                      (org-deadline-warning-days 0)
                      ))))
)
)

org2blog

Currently not functioning correctly.

(require 'netrc)
(setq blog (netrc-machine (netrc-parse "~/.netrc.gpg") "joocomblog" t))
(setq org2blog/wp-blog-alist '(("joocom"
                                :url "http://www.joocom.de/blog/xmlrpc.php"
                                :username (netrc-get blog "login")
                                :password (netrc-get blog "password")
                                        ; :default-title "Toms Discovery: "
                                        ; :default-categories ("Geeks!", "Software Entwicklung", "Systemadministration")
                                        ; :tags-as-categories nil
                                )
                               ))

minted

Settings to set code in latex documents with syntax highlighting.

(setq org-latex-listings 'minted)
(setq org-latex-packages-alist '(("" "minted")))
(setq org-latex-custom-lang-environments
      '(
        (emacs-lisp "common-lispcode")
        (lisp "common-lispcode")
        (R "rcode")))
(setq org-latex-minted-options
      '(("frame" "lines")
        ("fontsize" "\\scriptsize")
        ))
(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"))

Highlight inline code blocks

(font-lock-add-keywords
 'org-mode
 '(("\\(src_\\)\\([^[{]+\\)\\(\\[:.*\\]\\){\\([^}]*\\)}"
    (1 '(:foreground "black" :weight 'normal :height 10)) ; src_ part
    (2 '(:foreground "cyan" :weight 'bold :height 75 :underline "red")) ; "lang" part.
    (3 '(:foreground "#555555" :height 70)) ; [:header arguments] part.
    (4 'org-code) ; "code..." part.
    )))

org-macs

Why the hell do I do this?

(require 'org-macs)

Notes

(require 'org-popnote)
(setq org-popnote-file (concat tom/--emacs-dir "/notes.org"))
(require 'yequake)
(setq yequake-frames
      (list (cons "org-popnote"
                  (list (cons 'buffer-fns '(org-popnote))
                        (cons 'width 0.25)
                        (cons 'height 0.5)))))

Journal

(if (not (tom/work?))
    (setq org-journal-dir (expand-file-name "~/ownCloud/org-mode/journal/"))
  (setq org-journal-dir (expand-file-name "~/Documents/journal/")))

tomsdiner.org

;;  (require 'org-publish)
(if (not (tom/work?))
    (let* ((tdo (netrc-machine (netrc-parse "~/.netrc.gpg") "tdo"))
           (remote-dir (concat (netrc-get tdo "login") (netrc-get tdo "account")))
           (remote-static-dir (concat remote-dir "static/")))
      (setq org-publish-project-alist
            `(("tdo"
               :components ("tdo-content" "tdo-static"))
              ("tdo-content"
               :base-directory "~/Projekte/tomsdiner.org/"
               :base-extension "org"
               :publishing-directory ,remote-dir
               :recursive t
               :publishing-function org-html-publish-to-html
               :export-with-tags nil
               :headline-levels 4             ; Just the default for this project.
               :with-toc nil
               :section-numbers nil
               :with-sub-superscript nil
               :with-todo-keywords nil
               :with-author nil
               :with-creator nil
               :with-title nil
               :html-preamble "<div class=\"navi\">
                           <b>
                             <a href=\"/index.html\" class=\"home\">Me+Myself+I</a>
                           </b>
                           &nbsp;&mdash;&nbsp;
                           <a href=\"/myself/index.html\">myself</a>
                           &nbsp;&mdash;&nbsp;
                           <a href=\"/blog/index.html\">posts</a>
                           &nbsp;&mdash;&nbsp;
                           <a href=\"http://github.com/tomterl\">projects</a>
                         </div>
                         <hr/>"
               :html-postamble "<hr/><div class=\"footer\">
                            <a href=\"/imprint.html\">imprint</a>
                          </div>"
               :html-head "<link rel=\"stylesheet\"
                         href=\"/static/css/style.css\" type=\"text/css\"/><title>tomsdiner.org</title>"
               :html-head-include-default-style nil
               :with-timestamp t
               :exclude-tags ("noexport" "todo")
               :auto-preamble t)
              ("tdo-static"
               :base-directory "~/Projekte/tomsdiner.org/static/"
               :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf\\|otf"
               :publishing-directory ,remote-static-dir
               :recursive t
               :publishing-function org-publish-attachment)))))

Presentations

(setq org-reveal-root "file:///home/tregner/opt/reveal.js")

Books

Sections marked with `:newpage` will start on a new page when exporting to LaTeX.

(defun org/get-headline-string-element  (headline backend info)
  (let ((prop-point (next-property-change 0 headline)))
    (if prop-point (plist-get (text-properties-at prop-point headline) :parent))))

(defun org/ensure-latex-clearpage (headline backend info)
  (when (org-export-derived-backend-p backend 'latex)
    (let ((elmnt (org/get-headline-string-element headline backend info)))
      (when (member "newpage" (org-element-property :tags elmnt))
        (concat "\\clearpage\n" headline)))))

(eval-after-load 'ox 
  '(add-to-list 'org-export-filter-headline-functions
                'org/ensure-latex-clearpage))

Edit src-blocks inline

(add-hook 'org-mode-hook 'poly-org-mode)

Postgres

(require 'company-postgresql)
(require 'ob-sql-mode)
(sql-set-product-feature 'postgres :prompt-regexp "^[-[:alnum:]_]*=[#>] ")
(sql-set-product-feature 'postgres :prompt-cont-regexp
                         "^[-[:alnum:]_]*[-(][#>] ")

(define-hook-helper sql-mode ()
  :name pqsql-completion
  (progn
    (setq company-sql-db-host "postgres.toolserver.fbr")
    (setq company-sql-db-name "fbtools")
    (setq company-sql-db-user "fbtools")
    (set (make-local-variable 'company-backends)
         '((company-postgresql)))))

Bug Tracker

Make it easier to reference tickets. se

(require 'bug-reference)
(define-hook-helper bug-reference-mode ()
  "Setup bug reference links"
  :name bug-reference-org
  (progn
    (let* ((bugs (netrc-machine (netrc-parse "~/.netrc") "bugs" t))
         (bug-url (netrc-get bugs "default")))
    (setq bug-reference-url-format bug-url
          bug-reference-bug-regexp (if (tom/work?)
                                       "\\(##\\)\\([A-Z]\\{3,7\\}\\-[0-9][0-9]*\\)"
                                     "\\(##\\)\\([0-9][0-9]*\\)")))))
(define-hook-helper org-mode
  :name org-bug-reference
  (bug-reference-mode))

      

Behaviour

kill-ring

(setq save-interprogram-paste-before-kill t)

Modal editing

(require 'boon-qwerty)
(boon-mode)

Display available keys

(require 'which-key)
(setq which-key-popup-type 'side-window)
(which-key-setup-side-window-bottom)

minibuffer

(custom-set-variables
 '(mini-frame-show-parameters
   '((top . 10)
     (width . 0.7)
     (left . 0.5))))
(mini-frame-mode)

Generic Version Control interface

(fullframe vc-annotate quit-window nil)

rgrep

(fullframe/split-screen rgrep quit-window "*grep*" 'horizontal 't)

File encoding

Everything should be in utf-8.

(prefer-coding-system 'utf-8)

File renaming/deletion

Both commands are from Bozhidar Batsov.

Renaming

(defun tom/rename-file-and-buffer ()
  "Rename the current buffer and file it is visiting."
  (interactive)
  (let ((filename (buffer-file-name)))
    (if (not (and filename (file-exists-p filename)))
        (message "Buffer is not visiting a file!")
      (let ((new-name (read-file-name "New name: " filename)))
        (cond
         ((vc-backend filename) (vc-rename-file filename new-name))
         (t
          (rename-file filename new-name t)
          (set-visited-file-name new-name t t)))))))

Deletion

(defun tom/delete-file-and-buffer ()
  "Kill the current buffer and deletes the file it is visiting."
  (interactive)
  (let ((filename (buffer-file-name)))
    (when filename
      (if (vc-backend filename)
          (vc-delete-file filename)
        (progn
          (delete-file filename)
          (message "Deleted file %s" filename)
          (kill-buffer))))))

ibuffer as buffer screen

(global-set-key (kbd "C-x C-b") 'ibuffer)
(all-the-icons-ibuffer-mode 1)

Hydra

(defhydra hydra-ibuffer-main (:color pink :hint nil)
  "
 ^Navigation^ | ^Mark^        | ^Actions^        | ^View^
-^----------^-+-^----^--------+-^-------^--------+-^----^-------
  _k_:    ʌ   | _m_: mark     | _D_: delete      | _g_: refresh
 _RET_: visit | _u_: unmark   | _S_: save        | _s_: sort
  _j_:    v   | _*_: specific | _a_: all actions | _/_: filter
-^----------^-+-^----^--------+-^-------^--------+-^----^-------
"
  ("j" ibuffer-forward-line)
  ("RET" ibuffer-visit-buffer :color blue)
  ("k" ibuffer-backward-line)

  ("m" ibuffer-mark-forward)
  ("u" ibuffer-unmark-forward)
  ("*" hydra-ibuffer-mark/body :color blue)

  ("D" ibuffer-do-delete)
  ("S" ibuffer-do-save)
  ("a" hydra-ibuffer-action/body :color blue)

  ("g" ibuffer-update)
  ("s" hydra-ibuffer-sort/body :color blue)
  ("/" hydra-ibuffer-filter/body :color blue)

  ("o" ibuffer-visit-buffer-other-window "other window" :color blue)
  ("q" quit-window "quit ibuffer" :color blue)
  ("." nil "toggle hydra" :color blue))

(defhydra hydra-ibuffer-mark (:color teal :columns 5
                                     :after-exit (hydra-ibuffer-main/body))
  "Mark"
  ("*" ibuffer-unmark-all "unmark all")
  ("M" ibuffer-mark-by-mode "mode")
  ("m" ibuffer-mark-modified-buffers "modified")
  ("u" ibuffer-mark-unsaved-buffers "unsaved")
  ("s" ibuffer-mark-special-buffers "special")
  ("r" ibuffer-mark-read-only-buffers "read-only")
  ("/" ibuffer-mark-dired-buffers "dired")
  ("e" ibuffer-mark-dissociated-buffers "dissociated")
  ("h" ibuffer-mark-help-buffers "help")
  ("z" ibuffer-mark-compressed-file-buffers "compressed")
  ("b" hydra-ibuffer-main/body "back" :color blue))

(defhydra hydra-ibuffer-action (:color teal :columns 4
                                       :after-exit
                                       (if (eq major-mode 'ibuffer-mode)
                                           (hydra-ibuffer-main/body)))
  "Action"
  ("A" ibuffer-do-view "view")
  ("E" ibuffer-do-eval "eval")
  ("F" ibuffer-do-shell-command-file "shell-command-file")
  ("I" ibuffer-do-query-replace-regexp "query-replace-regexp")
  ("H" ibuffer-do-view-other-frame "view-other-frame")
  ("N" ibuffer-do-shell-command-pipe-replace "shell-cmd-pipe-replace")
  ("M" ibuffer-do-toggle-modified "toggle-modified")
  ("O" ibuffer-do-occur "occur")
  ("P" ibuffer-do-print "print")
  ("Q" ibuffer-do-query-replace "query-replace")
  ("R" ibuffer-do-rename-uniquely "rename-uniquely")
  ("T" ibuffer-do-toggle-read-only "toggle-read-only")
  ("U" ibuffer-do-replace-regexp "replace-regexp")
  ("V" ibuffer-do-revert "revert")
  ("W" ibuffer-do-view-and-eval "view-and-eval")
  ("X" ibuffer-do-shell-command-pipe "shell-command-pipe")
  ("b" nil "back"))

(defhydra hydra-ibuffer-sort (:color amaranth :columns 3)
  "Sort"
  ("i" ibuffer-invert-sorting "invert")
  ("a" ibuffer-do-sort-by-alphabetic "alphabetic")
  ("v" ibuffer-do-sort-by-recency "recently used")
  ("s" ibuffer-do-sort-by-size "size")
  ("f" ibuffer-do-sort-by-filename/process "filename")
  ("m" ibuffer-do-sort-by-major-mode "mode")
  ("b" hydra-ibuffer-main/body "back" :color blue))

(defhydra hydra-ibuffer-filter (:color amaranth :columns 4)
  "Filter"
  ("m" ibuffer-filter-by-used-mode "mode")
  ("M" ibuffer-filter-by-derived-mode "derived mode")
  ("n" ibuffer-filter-by-name "name")
  ("c" ibuffer-filter-by-content "content")
  ("e" ibuffer-filter-by-predicate "predicate")
  ("f" ibuffer-filter-by-filename "filename")
  (">" ibuffer-filter-by-size-gt "size")
  ("<" ibuffer-filter-by-size-lt "size")
  ("/" ibuffer-filter-disable "disable")
  ("b" hydra-ibuffer-main/body "back" :color blue))
(define-key ibuffer-mode-map "." 'hydra-ibuffer-main/body)
(add-hook 'ibuffer-hook #'hydra-ibuffer-main/body)

`bufler`

(require 'bufler)
(fullframe bufler quit-window)

vi-like paren-jump

Use % to jump to corresponding parens
(defun goto-match-paren (arg)
  "Go to the matching parenthesis if on parenthesis, otherwise insert
the character typed."
  (interactive "p")
  (cond ((looking-at "\\s\(") (forward-list 1) (backward-char 1))
    ((looking-at "\\s\)") (forward-char 1) (backward-list 1))
    (t                    (self-insert-command (or arg 1))) ))
(global-set-key "%" `goto-match-paren)

indentation

Indent using spaces, 2 spaces for each indentation step.

(setq-default tab-width 2)
(setq-default indent-tabs-mode nil)
(setq-default c-basic-offset 2)

Flyspell: Change dictionary; key-bindings

    (setq flyspell-abbrev-p t
    flyspell-issue-message-flag nil
    flyspell-issue-welcome-flag nil)
    (require 'flyspell-correct-ivy)
    (setq flyspell-correct-interface 'flyspell-correct-ivy)
    (global-set-key
     (kbd "H-s")
     (defhydra hydra-spelling (:color blue)
      "
^
^Spelling^          ^Errors^            ^Checker^
^────────^──────────^──────^────────────^───────^───────
_q_ quit            _<_ cor. previous   _c_ correction
^^                  _>_ cor. next       _d_ dictionary
^^                  _f_ check           _m_ mode
^^                  ^^                  ^^
"
      ("q" nil)
      ("<" flyspell-correct-previous :color pink)
      (">" flyspell-correct-next :color pink)
      ("c" ispell)
      ("d" ispell-change-dictionary)
      ("f" flyspell-buffer)
      ("m" flyspell-mode)))

Multiple Cursors

(global-set-key (kbd "C-S-c C-S-c") 'mc/edit-lines)
(global-set-key (kbd "C-c M-.") 'mc/mark-next-like-this)
(global-set-key (kbd "C-c M-,") 'mc/mark-previous-like-this)
(global-set-key (kbd "C-c M-a") 'mc/mark-all-like-this)

Multicursor mark region

After using expand-region the point remains at the start of the region. Switch point and mark and call multi-cursor.

(defun tom/mcdwim ()
  ""
  (interactive)
  (progn
    (exchange-point-and-mark)
    (mc/mark-all-dwim nil)))
(global-set-key (kbd "\C-c r") 'tom/mcdwim)

Expand region

Context/Language aware region expansion/contraction.

(require 'expand-region)
(global-set-key (kbd "C-=") 'er/expand-region)

REPL toggle

(require 'repl-toggle)
(setq rtog/mode-repl-alist '(
                             (php-mode . tom/psysh) 
                             (emacs-lisp-mode . ielm)
                             (elixir-mode . elixir-mode-iex)
                             (ruby-mode . inf-ruby)
                             (js2-mode . nodejs-repl)
                             (js3-mode . nodejs-repl)))
(setq rtog/fullscreen t)
(setq rtog/split-screen t)

Opening URLs

Send them to firefox, with keysnail much better then anything else.

(setq
 browse-url-browser-function (quote browse-url-default-browser))

Insert current date

Use the ‘calendar’ to get and format the date.

     (require 'calendar)

(defun tom/insert-current-date (&optional omit-day-of-week-p)
  "Insert today's date using the current locale.
      With a prefix argument, the date is inserted without the day of
      the week."
  (interactive "P*")
  (insert (calendar-date-string (calendar-current-date) nil
                                omit-day-of-week-p)))
     (global-set-key (kbd "\C-c d") 'tom/insert-current-date)

Window movement

See hydra above for movement!

Window Layouts / Rotation

      (global-set-key (kbd "H-r") 'rotate-window)
(global-set-key (kbd "H-C-l") 'rotate-layout)

EDiff

Sensible setup found at ‘or emacs(‘.

     (setq ediff-window-setup-function 'ediff-setup-windows-plain)
(setq ediff-split-window-function 'split-window-horizontally)
(setq ediff-diff-options "-w")
(defun tom/ediff-hook ()
  (ediff-setup-keymap)
  (define-key ediff-mode-map "j" 'ediff-next-difference)
  (define-key ediff-mode-map "k" 'ediff-previous-difference))

(add-hook 'ediff-mode-hook 'tom/ediff-hook)

dired

     (require 'dired-x)
(put 'dired-find-alternate-file 'disabled nil)
(setq dired-omit-files "^\\..*$")

highlights

(require 'volatile-highlights)
(define-hook-helper prog-mode ()
  :name volatile-highlight
  (volatile-highlights-mode 1))

Edit as root

(defun tom/sudo ()
  "Use TRAMP to `sudo' the current buffer"
  (interactive)
  (when buffer-file-name
    (find-alternate-file
     (concat "/sudo:root@localhost:"
             buffer-file-name))))

Case handling

(global-set-key (kbd "H-c") 'string-inflection-all-cycle)

Lookup documentation

(global-set-key (kbd "H-z") 'zeal-at-point)

Occur word-at-point

(defun tom/occur-tap ()
  "Call occur with word-at-point"
  (interactive)
  (occur (word-at-point))
  )
(defun tom/kill-occur-window ()
  "Kill the *Occur* buffer/window"
  (interactive)
  (let ((buffer (get-buffer "*Occur*")))
    (when buffer
      (with-current-buffer-window
       buffer
       nil
       nil
       (kill-buffer-and-window)))))
(global-set-key (kbd "H-f") 'tom/occur-tap)
(global-set-key (kbd "H-F") 'tom/kill-occur-window)

View buffers

(global-set-key (kbd "H-v") 'view-mode)

Multiple Major Modes

(require 'polymode)
(require 'aql-mode)

Languagues embedded in `php-mode`-buffers

(define-hostmode poly-php-hostmode
  :mode 'php-mode)
 
(define-innermode poly-php-aql-innermode
  :mode 'aql-mode
  :head-matcher "^[ \t]*\\(protected \\|private \\)?\\(static \\)?$[_a-zA-Z0-9]* = <<<'?AQL'?\n"
  :tail-matcher "^AQL;\n"
  :head-mode 'host
  :tail-mode 'host)
 
(define-innermode poly-php-sql-innermode
  :mode 'sql-mode
  :head-matcher "^[ \t]*protected static $source = ['\"](?\n"
  :tail-matcher "^.*)?['\"];\n"
  :head-mode 'host
  :tail-mode 'host)
 
(define-innermode poly-php-js-innermode
  :mode 'js-mode
  :head-matcher "^[ \t]*\\(protected static \\)?$[_a-zA-Z0-9]* = <<<'?JST'?\n"
  :tail-matcher "^JST;\n"
  :head-mode 'host
  :tail-mode 'host)
 
(define-polymode poly-php-mode
  :hostmode 'poly-php-hostmode
  :innermodes '(poly-php-aql-innermode
                poly-php-sql-innermode
                poly-php-js-innermode))

Languages embedded in `js-mode`-buffers

(define-hostmode poly-js-hostmode
  :mode 'js-mode)
 
(define-innermode poly-js-aql-innermode
  :mode 'aql-mode
  :head-matcher "aql`"
  :tail-matcher "`"
  :head-mode 'host
  :tail-mode 'host)
 
(define-polymode poly-js-mode
  :hostmode 'poly-js-hostmode
  :innermodes '(poly-js-aql-innermode))
    

Languages embedded in `yaml-mode`-buffers

(define-hostmode poly-yaml-hostmode
  :mode 'yaml-mode)
 
(define-innermode poly-yaml-js-innermode
  :mode 'js-mode
  :head-matcher "body: >"
  :tail-matcher "^ *\\(- \\)?[a-z_-]+:"
  :head-mode 'host
  :tail-mode 'host)
 
(define-polymode poly-yaml-mode
  :hostmode 'poly-yaml-hostmode
  :innermodes '(poly-yaml-js-innermode))
    

Help

(global-set-key
 (kbd "H-h")
 (defhydra hydra-help (:color blue :timeout 3)
   "launch"
   ("h" helpful-at-point "Loopup Function/Macro")
   ("v" helpful-variable "Lookup Variable")
   ("g" helpful-update "Refresh Helpful Buffer")
   ))

Popup Imenu

(require 'popup-imenu)
(global-set-key (kbd "H-i") 'popup-imenu)
(setq popup-use-optimized-column-computation nil)
(setq popup-imenu-position 'point)
(setq popup-imenu-force-position t)
(setq popup-imenu-style 'indent)
(define-key popup-isearch-keymap (kbd "H-i") 'popup-isearch-cancel)

Which key?

(require 'which-key)
(require 'which-key-posframe)
(which-key-mode 1)
(which-key-posframe-mode 1)
(setq which-key-posframe-poshandler 'posframe-poshandler-frame-bottom-left-corner)

Search engines

(require 'engine-mode)
(setq engine/browser-function 'eww-browse-url)

(defengine amazon
  "http://www.amazon.com/s/ref=nb_sb_noss?url=search-alias%3Daps&field-keywords=%s"
  :keybinding "a")

(defengine duckduckgo
  "https://duckduckgo.com/?q=%s"
  :keybinding "d")

(defengine github
  "https://github.com/search?ref=simplesearch&q=%s"
  :keybinding "g")

(defengine google-images
  "http://www.google.com/images?hl=en&source=hp&biw=1440&bih=795&gbv=2&aq=f&aqi=&aql=&oq=&q=%s"
  :keybinding "i")

(defengine google-maps
  "http://maps.google.com/maps?q=%s"
  :keybinding "m"
  :docstring "Mappin' it up.")

(defengine stack-overflow
  "https://stackoverflow.com/search?q=%s"
  :keybinding "s")

(defengine youtube
  "http://www.youtube.com/results?aq=f&oq=&search_query=%s"
  :keybinding "y")

(defengine wikipedia
  "http://www.wikipedia.org/search-redirect.php?language=en&go=Go&search=%s"
  :keybinding "w"
  :docstring "Searchin' the wikis.")
(engine-mode t)

On exit

Don’t ask for confirmation to kill running processes on exit; I always answered yes the last couple of years.

(setq confirm-kill-processes nil)

Languages

General

line numbers

(require 'linum-relative)
(setq linum-relative-current-symbol "")
(setq linum-relative-backend 'display-line-numbers-mode)
(add-hook 'prog-mode-hook 'linum-relative-mode)

lsp initialization

(with-eval-after-load 'lsp-intelephense
  (setf (lsp--client-multi-root (gethash 'iph lsp-clients)) nil))
(with-eval-after-load 'lsp-mode
  (add-hook 'lsp-mode-hook #'lsp-enable-which-key-integration))
(setq lsp-enable-file-watchers nil)
(setq lsp-prefer-flymake nil)
(setq lsp-auto-guess-root t)
(require 'lsp-mode)
(require 'lsp)
;(require 'lsp-clients)
;(require 'lsp-php)
(require 'lsp-ui)
(require 'lsp-diagnostics) ; for lsp-ui-flycheck--start
(require 'origami)
(require 'lsp-origami)
(require 'lsp-ivy)
(add-hook 'lsp-mode-hook 'lsp-ui-mode)
(add-hook 'lsp-mode-hook 'lsp-enable-which-key-integration)
(add-hook 'origami-mode-hook #'lsp-origami-mode)

completion

(require 'company-capf)
(defun tom/company-transformer (candidates)
  (let ((completion-ignore-case t))
    (all-completions (company-grab-symbol) candidates)))

hydra

      (global-set-key
       (kbd "H-l")
       (defhydra hydra-lsp (:exit t :hint nil)
         "
 Buffer^^               Server^^                   Symbol
-------------------------------------------------------------------------------------
 [_f_] format           [_M-r_] restart            [_d_] declaration  [_i_] implementation  [_o_] documentation
 [_m_] imenu            [_S_]   shutdown           [_D_] definition   [_t_] type            [_r_] rename
 [_x_] execute action   [_M-s_] describe session   [_R_] references   [_s_] signature"
         ("d" lsp-find-declaration)
         ("D" lsp-ui-peek-find-definitions)
         ("R" lsp-ui-peek-find-references)
         ("i" lsp-ui-peek-find-implementation)
         ("t" lsp-find-type-definition)
         ("s" lsp-signature-activate)
         ("o" lsp-describe-thing-at-point)
         ("r" lsp-rename)

         ("f" lsp-format-buffer)
         ("m" lsp-ui-imenu)
         ("x" lsp-execute-code-action)

         ("M-s" lsp-describe-session)
         ("M-r" lsp-restart-workspace)
         ("S" lsp-shutdown-workspace)))

Flycheck Hydra

(global-set-key
 (kbd "H-e")
 (defhydra hydra-flycheck
   (:pre (progn (setq hydra-lv t) (flycheck-list-errors))
         :post (progn (setq hydra-lv nil) (quit-windows-on "*Flycheck errors*"))
         :hint nil)
   "Errors"
   ("f"  flycheck-error-list-set-filter                            "Filter")
   ("j"  flycheck-next-error                                       "Next")
   ("k"  flycheck-previous-error                                   "Previous")
   ("gg" flycheck-first-error                                      "First")
   ("G"  (progn (goto-char (point-max)) (flycheck-previous-error)) "Last")
   ("q"  nil)))

Elixir

Automatically add end when typing do.

(require 'elixir-mode)
(add-to-list
 'elixir-mode-hook
 (defun auto-activate-ruby-end-mode-for-elixir-mode ()
   (set (make-variable-buffer-local 'ruby-end-expand-keywords-before-re)
        "\\(?:^\\|\\s-+\\)\\(?:do\\)")
   (set (make-variable-buffer-local 'ruby-end-check-statement-modifiers) nil)
   (ruby-end-mode +1)))
(define-hook-helper elixir-mode ()
  (progn 
    (require 'alchemist)
    (set (make-local-variable 'company-backends)
         '((Alchemist-company :with company-yasnippet company-dabbrev-code)))
    (flycheck-mode 1)
    (yas-minor-mode 1)))

Imenu

Add speedbar support for elixir files.

(require 'speedbar)
(speedbar-add-supported-extension ".ex")
(speedbar-add-supported-extension ".exs")

Rust

(setenv "RUST_SRC_PATH" "/home/tom/Projects/rust/src")
(define-hook-helper rust-mode ()
  (progn
    (require 'company-racer)
    (setq tab-width 4
          c-basic-offset 4
          indent-tabs-mode nil
          )
    (set (make-local-variable 'company-backends)
         '((company-racer :with :sorted company-yasnippet)))
    (cargo-minor-mode 1)))

Shell

(define-hook-helper sh-mode ()
  (define-key lsp-ui-mode-map [remap xref-find-definitions] #'lsp-ui-peek-find-definitions)
  (define-key lsp-ui-mode-map [remap xref-find-references] #'lsp-ui-peek-find-references)
  (define-key lsp-ui-mode-map (kbd "M-.") #'lsp-ui-peek-find-definitions))
(lsp-register-client
 (make-lsp-client :new-connection (lsp-stdio-connection "bash-language-server start")
                  :major-modes '(sh-mode)
                  :server-id 'bls))
;;  (add-hook 'sh-mode-hook #'flycheck-mode)

PHP

Use php-mode, not web-mode for PHP-code

(require 'php-mode)
(add-to-list 'auto-mode-alist '("\\.php$" . php-mode))
(add-to-list 'auto-mode-alist '("\\.inc$" . php-mode))

smarty

Use web-mode for smarty templates.

(setq web-mode-engines-alist
      '(("smarty"    . "\\.tpl$")))
(add-to-list 'auto-mode-alist '("\\.tpl$" . web-mode))
(when (tom/work?)
    (setq web-mode-engine "smarty"))

Indentation and behaviour

Indent with 4 spaces. Use flycheck, but dont’t use the “controversial” and “cleancode” rulesets of phpmd. Use ac-php as company backend. Recreate the tags when a file is saved.

(setq lsp-intelephense-licence-key (expand-file-name "~/.config/intelephense/licence.txt"))
(lsp-register-custom-settings
 `(("intelephense.phpdoc.functionTemplate"
    ,(list :summary "$1"
           :tags (vector "@param ${1:$SYMBOL_TYPE} $SYMBOL_NAME $2"
                         ""
                         "@return ${1:$SYMBOL_TYPE} $2"
                         "@throws ${1:$SYMBOL_TYPE} $2"
                         "@author `fb-author`"
                         "@since  `(tom/insert-current-date 4)`")))))
(lsp-register-custom-settings
 `(("intelephense.phpdoc.classTemplate"
    ,(list :summary "$1"
           :tags (vector ""
                         "@author `fb-author`"
                         "@package ${1:$SYMBOL_NAMESPACE}"
                         "@since `(tom/insert-current-date 4)`")))))
(defun tom/dtw ()
  "Delete trailing whitespace in a save excursion."
  (save-excursion
    (delete-trailing-whitespace)))
    
(flycheck-define-generic-checker 'lsp-php-ui
  "A syntax checker using the Language Server Protocol (RLS)
      provided by lsp-mode.
      See https://github.com/emacs-lsp/lsp-mode."
  :start #'lsp-diagnostics--flycheck-start
  :modes '(php-mode) ; Need a default mode
  :predicate (lambda () lsp-mode)
  :error-explainer (lambda (e) (flycheck-error-message e))
  :next-checkers '((warning . php)))

(define-hook-helper php-mode ()
  :name php-personal
  (progn 
    (ggtags-mode -1)
    (repl-toggle-mode 1)
    (yas-minor-mode 1)
    (company-mode 1)
    (origami-mode 1)
    (modify-syntax-entry ?$ "_")
    (make-local-variable 'company-transformers)
    (push 'tom/company-transformer company-transformers)
    (local-set-key (kbd "C-c a") 'tom/edit-sql)
    (php-enable-wordpress-coding-style)
    (flycheck-may-enable-checker 'php-phpcs)
    (flycheck-disable-checker 'phpstan)
    (flycheck-disable-checker 'php-phpmd)
    
    (setq tab-width 4
          c-basic-offset 4
          indent-tabs-mode nil
          php-template-compatibility nil

          php-mode-coding-style (quote wordpress)
          php-lineup-cascaded-calls t
          lsp-enable-semantic-highlighting nil)
    (add-hook 'write-contents-functions 'tom/dtw nil t)
    (define-key lsp-ui-mode-map [remap xref-find-definitions] #'lsp-ui-peek-find-definitions)
    (define-key lsp-ui-mode-map [remap xref-find-references] #'lsp-ui-peek-find-references)
    (poly-php-mode 1)
    (font-lock-mode 1)
    (setq-local flycheck-checker 'lsp-php-ui)))
    
(add-hook 'php-mode-hook 'flycheck-mode)
(add-hook 'php-mode-hook #'lsp)

Remote debugging

dap is the way to go…

(require 'dap-php)
(dap-php-setup)
(dap-tooltip-mode 1)
(tooltip-mode 1)
(setq dap-auto-show-output nil)
(setq dap-inhibit-io nil)
(define-key php-mode-map (kbd "H-d") 'dap-hydra)
(dap-register-debug-template "Php tcp4"
                      (list :type "php"
                            :cwd nil
                            :request "launch"
                            :name "Php Debug"
                            :hostname "0.0.0.0"
                            :sourceMaps t))

Check/correct @param tags

Check the doc comment of the current function for @param tags, create them if not present. This is used in the function-snippet, but needs an overhaul…

(require 'php-doc-block)
(define-hook-helper php-mode ()
  :name phpdoc
  (local-set-key (kbd "<C-tab>") 'php-doc-block))

phpunit

(defun tom/php-unit ()
  ""
  (interactive)
  (let* ((pr (projectile-project-root))
         (confdir (concat pr "/tests"))
         (conf (concat confdir "/phpunit.xml"))
         (wd default-directory))
    (if (and (file-directory-p confdir) (file-exists-p conf))
        (progn
          (cd confdir)
          (phpunit-run '())
          (cd wd)))))

psysh

(defun tom/psysh ()
  "Start or switch to a psysh specific to the current
      projectile project, or the global '*PsySh*' if not in a
      project"
  (interactive)
  (if (projectile-ensure-project (projectile-project-p))
      (let* ((-project-root (projectile-project-root))
             (-project (projectile-default-project-name -project-root))
             (-buffer-name (concat "* " -project " psysh *")))
             
        (projectile-with-default-dir -project-root
          
          (if (and (buffer-live-p (get-buffer -buffer-name)))
              (progn (switch-to-buffer -buffer-name))
            (progn
              (vterm -buffer-name)
              (vterm-send-string "set -k")
              (vterm-send-return)
              (vterm-send-string "if [ -f ./bin/shell.php ]; then ./bin/shell.php; else psysh; fi #")
              (vterm-send-return)))))
    (psysh-run "*PsySh*" "psysh")))

(define-hook-helper psysh-mode () 
  (progn (company-mode -1) (auto-complete-mode)))

C

Indentation

(define-hook-helper c-mode ()
  (setq tab-width 2
        c-basic-offset 2
        indent-tabs-mode nil))

JavaScript

Indentation etc

(if (tom/work?)
  (setq js-indent-level 4
          js2-basic-offset 4
        js2-bounce-indent-p t)
(setq js-indent-level 2
        js2-basic-offset 2
      js2-bounce-indent-p t))
(add-to-list 'auto-mode-alist '("\\.js$" . js-mode))

Completion

    (add-to-list 'load-path "/usr/lib/node_modules/tern/emacs")

    (flycheck-define-generic-checker 'lsp-js-ui
      "A syntax checker using the Language Server Protocol (RLS)
  provided by lsp-mode.
  See https://github.com/emacs-lsp/lsp-mode."
      :start #'lsp-diagnostics--flycheck-start
      :modes '(js-mode) ; Need a default mode
      :predicate (lambda () lsp-mode)
      :error-explainer (lambda (e) (flycheck-error-message e)))
    (define-hook-helper js-mode ()
      :name js-personal
      (progn
        (ggtags-mode -1)
        (repl-toggle-mode 1)
        (yas-minor-mode 1)
        (company-mode 1)
        (make-local-variable 'company-transformers)
        (push 'tom/company-transformer company-transformers)
        (define-key lsp-ui-mode-map [remap xref-find-definitions] #'lsp-ui-peek-find-definitions)
        (define-key lsp-ui-mode-map [remap xref-find-references] #'lsp-ui-peek-find-references)
        (define-key lsp-ui-mode-map (kbd "M-.") #'lsp-ui-peek-find-definitions)
        (add-hook 'js-mode-hook 'flycheck-mode)
        (setq-local flycheck-checker 'lsp-js-ui)
        (local-set-key (kbd "C-c a") 'tom/edit-aql)
        (poly-js-mode 1)))
    (add-hook 'js-mode-hook #'lsp)

    ;;;  (require 'lsp-javascript-typescript)
;;;(add-hook 'js-mode-hook #'lsp-javascript-typescript-enable)

Edit js in YAML-files

(defun tom/edit-js ()
  "If point is between a `^ body: > ... \\(^ - name:
   \\)\\|\\(^ types:\\)' block, create a narrowed, indirect
   buffer in `js2-mode' to edit the function-body.  Press
   `C-c C-c' inside the buffer to close the window and kill
   the indirect buffer."
  (interactive)
  (tom/edit-narrowed 'js2-mode ".js" "^        body: >" "\\(^      - name: \\)\\|\\(^    types:\\)"))

nodejs-repl

(require 'nodejs-repl)

`.aql` files

(add-to-list 'auto-mode-alist '("\\.aql" . js-mode))

YAML

Associate yaml-mode with formular (.frm) and table (.tbl) definitions as used in fb-core et.al.

(require 'yaml-mode)
(add-to-list 'auto-mode-alist '("\\.yml$" . yaml-mode))
(add-to-list 'auto-mode-alist '("\\.frm$" . yaml-mode))
(add-to-list 'auto-mode-alist '("\\.tbl$" . yaml-mode))
(define-hook-helper yaml-mode ()
  (progn
    (setq tab-width 2
          c-basic-offset 2
          indent-tabs-mode nil)
    (yas-minor-mode 1)
    (poly-yaml-mode 1)
  (local-set-key (kbd "C-c a") 'tom/edit-js)))

Groovy

;;;  (require 'inf-groovy)
(add-to-list 'auto-mode-alist '("\\.gvy$" . groovy-mode))
(rtog/add-repl 'groovy-mode (rtog/switch-to-shell-buffer "*groovy*" 'run-groovy))
(define-hook-helper haskell-mode ()
  (progn
    (hindent-mode 1)))

(let ((my-cabal-path (expand-file-name "~/.cabal/bin")))
  (setenv "PATH" (concat my-cabal-path path-separator (getenv "PATH")))
  (add-to-list 'exec-path my-cabal-path))
(custom-set-variables '(haskell-tags-on-save t))
(custom-set-variables
 '(haskell-process-suggest-remove-import-lines t)
 '(haskell-process-auto-import-loaded-modules t)
 '(haskell-process-log t)
 '(haskell-process-type 'cabal-repl)
 '(hindent-style "gibiansky"))

(eval-after-load 'haskell-mode
  '(progn
     (define-key haskell-mode-map (kbd "C-c C-l") 'haskell-process-load-or-reload)
     (define-key haskell-mode-map (kbd "C-c C-z") 'haskell-interactive-switch)
     (define-key haskell-mode-map (kbd "C-c C-n C-t") 'haskell-process-do-type)
     (define-key haskell-mode-map (kbd "C-c C-n C-i") 'haskell-process-do-info)
     (define-key haskell-mode-map (kbd "C-c C-n C-c") 'haskell-process-cabal-build)
     (define-key haskell-mode-map (kbd "C-c C-n c") 'haskell-process-cabal)))
(eval-after-load 'haskell-cabal
  '(progn
     (define-key haskell-cabal-mode-map (kbd "C-c C-z") 'haskell-interactive-switch)
     (define-key haskell-cabal-mode-map (kbd "C-c C-k") 'haskell-interactive-mode-clear)
     (define-key haskell-cabal-mode-map (kbd "C-c C-c") 'haskell-process-cabal-build)
     (define-key haskell-cabal-mode-map (kbd "C-c c") 'haskell-process-cabal)))

AQL

(require 'aql-mode)
(define-hook-helper aql-mode ()
  (progn
    (setq tab-width 2
          c-basic-offset 2
          indent-tabs-mode nil)
    (yas-minor-mode 1)))

SQL

(require 'sql)
(require 'sql-indent)
(sql-set-product "postgres")
(define-hook-helper sql-mode ()
  (progn
    (setq tab-width 2
          c-basic-offset 2
          indent-tabs-mode nil)
    (yas-minor-mode 1)
  (sqlind-minor-mode 1)))

RESTful

Extract json-field data

(defun tom/extract-json (name)
  "Extract the field `NAME' from the restclient-result
   buffer (`*HTTP Response*').  Caution: This parses the
   hole buffer as json-data, so may be costly.

   `NAME' may be given as dotted path to address nested json-objects"
  (with-current-buffer "*HTTP Response*"
    (let* ((json-object-type 'hash-table)
           (json-array-type 'list)
           (json-key-type 'string)
           (names (split-string name "\\." t))
           (result nil)
           (data (json-read-from-string (buffer-string))))
      (reduce #'(lambda (value key) "" (gethash key value)) names :initial-value data))))

Use outline-cycle in rest-buffers

(eval-after-load 'outline
  '(progn
     (require 'outline-magic)
     (define-key outline-minor-mode-map
       (kbd "<C-tab>")
       'outline-cycle)))

Databases

ElasticSearch

(autoload 'es-mode "es-mode.el" "Major mode for editing Elasticsearch queries." t)
(add-to-list 'auto-mode-alist '("\\.es$" . es-mode))
(add-hook 'es-result-mode-hook 'hs-minor-mode)

Slime

;;;(load (expand-file-name "~/quicklisp/slime-helper.el"))
;; Replace "sbcl" with the path to your implementation
(setq inferior-lisp-program "sbcl")

Version control

Magit

Magithub integration.

(setq magithub-feature-autoinject t)
(require 'magithub)

Configuration

(require 'magit)
(setq magit-completing-read-function 'ivy-completing-read)
(setq magit-wip-merge-branch t)
(magit-wip-mode 1)

Github forge

(with-eval-after-load 'magit
  (require 'forge))

Key bindings

(global-set-key (kbd "<f7>") 'magit-status)
(global-set-key (kbd "H-g") 'magit-file-dispatch)

Fullscreen

(fullframe magit-status magit-mode-quit-window nil)

Merging diffs

      (defhydra tom/smerge-hydra
        (:color pink :hint nil :post (smerge-auto-leave))
        "
^Move^       ^Keep^               ^Diff^                 ^Other^
^^-----------^^-------------------^^---------------------^^-------
_n_ext       _b_ase               _<_: upper/base        _C_ombine
_p_rev       _u_pper              _=_: upper/lower       _r_esolve
^^           _l_ower              _>_: base/lower        _k_ill current
^^           _a_ll                _R_efine
^^           _RET_: current       _E_diff
"
        ("n" smerge-next)
        ("p" smerge-prev)
        ("b" smerge-keep-base)
        ("u" smerge-keep-upper)
        ("l" smerge-keep-lower)
        ("a" smerge-keep-all)
        ("RET" smerge-keep-current)
        ("\C-m" smerge-keep-current)
        ("<" smerge-diff-base-upper)
        ("=" smerge-diff-upper-lower)
        (">" smerge-diff-base-lower)
        ("R" smerge-refine)
        ("E" smerge-ediff)
        ("C" smerge-combine-with-next)
        ("r" smerge-resolve)
        ("k" smerge-kill-current)
        ("ZZ" (lambda ()
                (interactive)
                (save-buffer)
                (bury-buffer))
         "Save and bury buffer" :color blue)
        ("q" nil "cancel" :color blue))
      (define-hook-helper magit-diff-visit-file ()
        :name magit-smerge-diff
        (when smerge-mode
          (tom/smerge-hydra/body)))

Services

ssh tunnel

Manage ssh tunnel via prodigy.

(require 'prodigy)

Helper functions

Build the argument list

(defun tom/build-tunnel-args (args)
  "Assemble the ssh tunnel argument list."
  `("-v" ;; allows us to parse for the ready message
    "-N" ;; don't start an interactive shell remotely
    "-L" ,(concat (getf args :localport) ;; the tunnel spec
                  ":"
                  (getf args :tunnel-ip)
                  ":"
                  (getf args :tunnel-port))
    "-l" ,(getf args :user) ;; the username
    "-p" ,(getf args :port) ;; the remote port
    ,(getf args :host)))    ;; the remote host

Tunnel tag

(prodigy-define-tag
  :name 'ssh-tunnel
  :command "ssh"
  :cwd (getenv "HOME")
  :args (prodigy-callback (service)
                          (tom/build-tunnel-args
                           (getf service :tunnel)))
  :ready-message "debug1: Entering interactive session.")

Tunnel services

Tunnel definitions are held outside this config to protect the innocent…

(load-file "~/ownCloud/dotfiles/prodigy.el")

Work Services

(when (tom/work?)
  (load-file "~/ownCloud/dotfiles/workservices.el"))

sourcehut

Set an org-mode html export as readme text for a sourcehut repository. Use bin/srht_repoid.sh to determin the numerical id of the repository. Standard org-mode image links may not work as expected – melpa badges for example– use export-html blocks with the correct html code in these cases.

(require 'json)

(defun tom/srht-set-readme (id)
  "Export the current file to html and set the result as readme for the
sourcehut repo identified by ID."
  (interactive "sRepo id: ")
  (let* ((srht (netrc-machine (netrc-parse "~/.netrc.gpg") "git.sr.ht"))
         (srht-token (netrc-get srht "password"))
         (oauth2-token (concat "Bearer " srht-token))
         (readme.html (org-export-as (org-export-get-backend 'html) nil nil t))
         (json-object-type 'hash-table)
         (json-array-type 'list)
         (json-key-type 'string)
         (query (make-hash-table))
         (variables (make-hash-table)))
    (puthash "id" id variables) ; the sourcehut repo id
    (puthash "readme" readme.html variables)
    (puthash
     "query"
     "mutation UpdateRepo($id: Int!, $readme: String!) {
    updateRepository(id: $id, input: { readme: $readme }) { id }
  }"
     query)
    (puthash
     "variables" variables query)
    
    (request
      "https://git.sr.ht/query"
      :type "POST"
      :data (json-serialize query)
      :headers `(("Content-Type" . "application/json") ("Authorization" . ,oauth2-token))
      :parser 'json-read
      :complete (cl-function (lambda (&key symbol-status &allow-other-keys) (message "Set: %S" symbol-status))))))

Paste services

ix.io

(let ((ix-io (netrc-machine (netrc-parse "~/.netrc.gpg") "ix-io")))
  (setq ix-user (netrc-get ix-io "login"))
  (setq ix-token (netrc-get ix-io "password")))

yagist

(let ((github (netrc-machine (netrc-parse "~/.netrc.gpg") "yagist")))
  (setq yagist-github-token (netrc-get github "password")))

Search

the silver searcher

I use ag.el to interact with ag, a fine replacement for grep. Ignore all ignore-files :-)

(setq ag-arguments (list "-U" "--numbers" "--smart-case" "--nogroup" "--column" "--stats" "--"))

Focus on the current window and the ag-result buffer.

(fullframe/split-screen ag quit-window "*ag search*" 'horizontal 't)

Setup `wgrep-ag`.

(autoload 'wgrep-ag-setup "wgrep-ag")
(add-hook 'ag-mode-hook 'wgrep-ag-setup)

Delete the File: prefix appended by ag to the output, let `compile-find-file` work again.

(define-advice compilation-find-file (:around (orig-fun &rest args) remove-file-prefix -100)
  (let* ((to-find (car (cdr args)))
         (newname (s-replace "File: " "" to-find)))
    (setcar (cdr args) newname)
    (apply orig-fun args)))

Diminish Modes

Do this after everything is setup.

(diminish 'golden-ratio-mode "")
(diminish 'undo-tree-mode "")
(diminish 'whitespace-mode "")
(diminish 'company-mode "")
(diminish 'org-src-mode "")
(diminish 'projectile-mode "")
(diminish 'yas-minor-mode "")
(diminish 'flycheck-mode "")
(diminish 'repl-toggle-mode "")
(diminish 'mmm-mode "") 
(diminish 'persp-mode "")
(diminish 'company-box-mode "")
(diminish 'volatile-highlights-mode "")
(diminish 'eldoc-mode "")
(diminish 'visual-line-mode "")
(diminish 'abbrev-mode "")
(diminish 'auto-revert-mode "")

Finishing

sanity

(load-file "~/.emacs.d/site/sanity.el")

Enable gc

(setq gc-cons-threshold tom/gc-cons-threshold)

httpd server

(setq httpd-port 8081)
(setq httpd-host "0.0.0.0")

emacs server

Always have an emacs server running.

(require 'server)
(unless (server-running-p)
  (org-reload) ; switch to newest org from melpa
  (server-start)
  (message "Server started."))