Skip to content

Latest commit

 

History

History
2248 lines (1869 loc) · 65.7 KB

init-kr.org

File metadata and controls

2248 lines (1869 loc) · 65.7 KB

DotEmacs

Bootstrap

Add Melpa

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
(unless package-archive-contents
  (package-refresh-contents))

(require 'use-package)

(setq use-package-always-ensure t)

(use-package use-package-ensure-system-package
  :ensure nil)

Utilities

Utility packages that add functions that make configuration easier or configure some aspect of Emacs so that I don’t have to.

Measuring start-up

I like to know how fast Emacs has started.

(add-hook 'emacs-startup-hook
          #'(lambda ()
              (message "Emacs ready in %s with %d garbage collections."
                       (emacs-init-time)
                       gcs-done)))

Blackout

For keeping mode line clean.

(use-package blackout)

General

Helper functions for configuring keybindings, hooks and many more. There is general-def that’s is used for keybinding, general-setq that is used for setting “customize” variables, general-add-hook, general-remove-hook for adding or removing hooks in bulk (can add to more that one hook at a time and more than one function at the time), general-advice-add, general-advice-remove for adding and removing advice… There are some others but I don’t use them. There is also use-package integration for all of these functions and they are: :general for general-def :ghook and :gfhook for general-add-hook

(use-package general)

Garbage collector magic hack

What the title says.

(use-package gcmh
  :config
  (gcmh-mode 1)
  (setq gcmh-idle-delay 5)
  (blackout 'gcmh-mode))

no littering

Helping me so that I don’t have to configure Emacs not to clutter .emacs.d.

(use-package no-littering
  :demand t
  :config
  (setq auto-save-file-name-transforms
        `((".*" ,(no-littering-expand-var-file-name "auto-save/") t)))
  (setq custom-file (no-littering-expand-etc-file-name "custom.el"))
  ;; here because it needs to be after no-littering
  ;; should be moved somewhere else
  (when (file-exists-p custom-file)
    (load-file custom-file))
  (no-littering-theme-backups)

  (with-eval-after-load 'recentf
    (add-to-list 'recentf-exclude no-littering-var-directory)
    (add-to-list 'recentf-exclude no-littering-etc-directory)))

which-key

Help with discovery of keybindings.

(use-package which-key
  :config
  (setq which-key-idle-delay 0.5)
  (which-key-mode 1)
  (with-eval-after-load 'which-key
    (blackout 'which-key-mode)))

elisp-demos

Small snippets in help buffer that show how a function is used.

(use-package elisp-demos
  :config
  (advice-add 'describe-function-1 :after #'elisp-demos-advice-describe-function-1))
;; (with-eval-after-load 'helpful
;;   (advice-add 'helpful-update :after #'elisp-demos-advice-helpful-update))

Util functions

ansi colors

Ansi collor in buffers. Run command to add ansi collors to buffer.

(defun kr-display-ansi-colors ()
  (interactive)
  (let ((inhibit-read-only t))
    (ansi-color-apply-on-region (point-min) (point-max))))

(defun ansi-color-after-scroll (window start)
  "Used by ansi-color-mode minor mode"
  (ansi-color-apply-on-region start (window-end window t) t))

(define-minor-mode ansi-color-mode
  "A very primitive minor mode to view log files containing ANSI color codes.

Pros: this minor mode runs `ansi-color-apply-on-region' lazily,
i.e. only the visible part of the buffer. Hence, it does NOT
freeze Emacs even if the log file is huge.

Cons: a) when the minor code is toggled off, it does not undo
what has already been ansi colorized. b) assumes the buffer
content etc. does not change. c) jumping to random places within
the buffer may incur incorrect/incomplete colorization.

How to install: put this code into your init.el, then evaluate it or
restart Emacs for the code to take effect.

How to use: in the log buffer of need run `M-x ansi-color-mode'.
Alternatively, feel free to enable this minor mode via mode hooks
so that you needn't enable it manually.

-- lgfang
"
  :global nil
  :lighter ""
  (if ansi-color-mode
      (progn
        (ansi-color-apply-on-region (window-start) (window-end) t)
        (add-hook 'window-scroll-functions 'ansi-color-after-scroll 80 t))
    (remove-hook 'window-scroll-functions 'ansi-color-after-scroll t)))

Unix timestamp

(defun kr-unix-ts-to-str (&optional time zone)
  "Convert unix timestamp integer to human-readable string in RFC3339 format."
  (interactive "nTimestamp: ")
  (setq zone (or zone "UTC"))
  (setq ts-str (format "%s" (or time (current-word))))
  (if (numberp (read ts-str))
      (progn
        (setq ts-int (string-to-number ts-str))
        ;; send message to Message buffer
        ;; copy to kill-ring (clipboard)
        (setq rfc_str (format-time-string "%Y-%m-%dT%H:%M:%S%z" ts-int zone))
        (message (format "%d %s ==> %s" ts-int zone rfc_str))
        (kill-new rfc_str))

    (message "not a number")))

(defun kr-unix-ts-to-str-dwim ()
  (interactive)
  (if mark-active
      (let ((ts (buffer-substring-no-properties (region-beginning) (region-end))))
        (kr-unix-ts-to-str ts))
    (call-interactively #'kr-unix-ts-to-str)))


(with-eval-after-load 'meow
  (meow-define-keys 'normal
    '("gu". kr-unix-ts-to-str-dwim)))

toggle require new line

(defun kr-toggle-final-newline ()
    (interactive)
    (setq require-final-newline (not require-final-newline))
    (message "set require-final-newline to %s" require-final-newline))
(general-def mode-specific-map
  "fN" 'kr-toggle-final-newline)

Keybindings

Setup keybindings for builtin features that are not tied to any mode.

Emacs

Change builtin emacs keybingings that I can’t put in any category

(general-def
  "C-h l" 'find-library)

(general-def
  "M-;" 'comment-line
  "C-x C-;" 'comment-dwim)

(general-def 'global
  "C-<backspace>" 'cycle-spacing)

Help

Help at point key is C-z h. Every mode that implements a help at point needs to change it to point to it’s “help-at-point” function

(define-key global-map (kbd "C-z") nil)
(general-def
  "C-z h" 'describe-symbol)

Scratch

(defun kr-go-to-scratch ()
      (interactive)
      (switch-to-buffer "*scratch*"))

(use-package scratch
  :config
  (general-def mode-specific-map
    "b S" 'scratch
    "b s" 'scratch-buffer
    "b r" 'revert-buffer))

transient mode

This is manly for magit but can be used for some other funcitonality.

(general-def transient-base-map
  "<escape>" 'transient-quit-one)

Leader

Binding for emacs builtin command. Binding it to mode-specific-map (C-c) so that with the help of meow we can call it with SPC. For example call switch-to-buffer with SPC b b.

(general-def mode-specific-map
  "b b" 'switch-to-buffer
  "b d" 'kill-current-buffer
  "b r" 'revert-buffer
  "b D" 'kill-buffer

  "w w" 'other-window
  "w W" 'window-swap-states
  "w v" 'split-window-right
  "w h" 'split-window-below
  "w D" 'delete-other-windows
  "w d" 'delete-window

  "f s" 'save-buffer
  "f S" 'save-some-buffers
  "f d" 'list-directory
  "f f" 'find-file
  "f j" 'dired-jump)

Emacs

Here is configuration that concerns Emacs builtin features. Changing options, enabling and configuring modes etc. Big packages like org-mode will get their own section.

Sane defaults

Inspired by https://github.com/natecox/dotfiles/blob/master/emacs/emacs.d/nathancox.org

To debug a LISP function use debug-on-entry. You step in with d and over with e

(setq confirm-kill-emacs 'y-or-n-p)
(setq use-file-dialog nil)
(setq initial-scratch-message nil
      sentence-end-double-space nil
      ring-bell-function 'ignore
      frame-resize-pixelwise t)

;; (setq user-full-name "Luca Cambiaghi"
;;       user-mail-address "[email protected]")

;; always allow 'y' instead of 'yes'.
(defalias 'yes-or-no-p 'y-or-n-p)

;; default to utf-8 for all the things
(set-language-environment "UTF-8")

;; don't show any extra window chrome
(when (window-system)
  (tool-bar-mode -1)
  (toggle-scroll-bar -1))

;; less noise when compiling elisp
;; (setq byte-compile-warnings '(not free-vars unresolved noruntime lexical make-local))
;; (setq native-comp-async-report-warnings-errors nil)

;; use common convention for indentation by default
(setq-default indent-tabs-mode nil)
(setq-default tab-width 4)

;; Delete files to trash
(setq delete-by-moving-to-trash t)

;; Uniquify buffer names
(setq-default uniquify-buffer-name-style 'forward)

;; Better scrolling behaviour
(setq-default
 hscroll-step 1
 scroll-margin 4
 hscroll-margin 4
 mouse-yank-at-point t
 auto-window-vscroll nil
 mouse-wheel-scroll-amount '(1)
 mouse-wheel-tilt-scroll t
 scroll-conservatively most-positive-fixnum)

;; Better interaction with clipboard
(setq-default save-interprogram-paste-before-kill t)

;; Some usefull builtin minor modes
(column-number-mode 1)
(global-auto-revert-mode 1)

;; Maybe gives some optimization
(add-hook 'focus-out-hook #'garbage-collect)

(tooltip-mode -1)

;; delete whitespace
(add-hook 'before-save-hook #'whitespace-cleanup)

(setq view-read-only t)

help

(add-hook 'help-mode-hook 'visual-line-mode)

(setq help-window-select t)
(setq help-window-keep-selected t)

(setq help-enable-variable-value-editing t)
(put 'help-fns-edit-variable 'disabled nil)

(defun kr-describe-at-point (symbol)
  "Call `describe-symbol' for the SYMBOL at point."
  (interactive (list (symbol-at-point)))
  (if (and symbol (or (fboundp symbol)
                      (boundp symbol)
                      (facep symbol)))
      (describe-symbol symbol)
    (call-interactively #'describe-symbol)))

(general-def
  "C-z h" 'kr-describe-at-point
  "C-h s" 'shortdoc-display-group
  "C-h b" 'describe-keymap
  "C-h B" 'describe-bindings)

Subword

(global-subword-mode 1)
(blackout 'subword-mode)

Visual line mode

(blackout 'visual-line-mode)

eldoc

(use-package eldoc
  :config
  (setq eldoc-echo-area-display-truncation-message t)
  (setq eldoc-echo-area-use-multiline-p nil)
  (setq eldoc-echo-area-prefer-doc-buffer nil))

eldoc box

(use-package eldoc-box
  :general (eglot-mode-map
            "C-h ." 'eldoc-box-help-at-point))

recentf

(recentf-mode 1)
(setq recentf-max-saved-items 75)
(setq recentf-exclude `(,(expand-file-name "straight/build/" user-emacs-directory)
                        ,(expand-file-name "eln-cache/" user-emacs-directory)))
;;                         ,(expand-file-name "etc/" user-emacs-directory)
;;                         ,(expand-file-name "var/" user-emacs-directory)

save-place

(save-place-mode 1)

File registers

Open config

(set-register ?c `(file . ,(expand-file-name kr/config-org user-emacs-directory)))
(set-register ?i `(file . ,(expand-file-name "init.el" user-emacs-directory)))

Written Languages

Input method

I making a custom input method for Serbian language because all the other methods that exist are stupid. Reference how to make custom input method.

(quail-define-package
 "custom-latin" "Custom" "CS" nil
 "Custom keyboard layout."
 nil t nil nil nil nil nil nil nil nil t)

(quail-define-rules
 ("x" )
 ("X" )
 ("w" )
 ("W" )
 ("q" )
 ("Q" )
 ("y" )
 ("Y" )
 ("dj" )
 ("Dj" )
 ("DJ" ))
(setq default-input-method "custom-latin")

Spelling

(add-hook 'emacs-startup-hook
          #'(lambda ()
              (setq ispell-program-name (executable-find "aspell"))))

Calendar

(setq calendar-date-style 'european)
(setq calendar-week-start-day 1)

Ediff

(require 'ediff)
;; (winner-mode 1)
(add-hook 'ediff-after-quit-hook-internal 'winner-undo)
(setq ediff-window-setup-function 'ediff-setup-windows-plain)
(setq ediff-split-window-function 'split-window-horizontally)

(defvar my-ediff-last-windows nil)

(defun my-store-pre-ediff-winconfig ()
  (setq my-ediff-last-windows (current-window-configuration)))

(defun my-restore-pre-ediff-winconfig ()
  (set-window-configuration my-ediff-last-windows))

(add-hook 'ediff-before-setup-hook #'my-store-pre-ediff-winconfig)
(add-hook 'ediff-quit-hook #'my-restore-pre-ediff-winconfig)

iSearch

(setq isearch-lazy-count t)

auto-insert

(add-hook 'lisp-mode-hook #'auto-insert-mode)

Compilation

;; add color codes to compilation mode
(add-hook 'compilation-filter-hook 'ansi-color-compilation-filter)

repeat-mode

(repeat-mode 1)

For Macos

General MacOs specific configuration

Check if we run on Mac

(defvar kr-mac-p (if (string= system-type "darwin") t nil))

exec-path

Setup PATH and other env varables.

(use-package exec-path-from-shell
  :config
  (require 'exec-path-from-shell)

  (setq exec-path-from-shell-arguments '("-l"))
  (when (memq window-system '(mac ns))
    (dolist (var '("NPM_TOKEN" "NVM_DIR" "INFOPATH"))
      (add-to-list 'exec-path-from-shell-variables var))
    (exec-path-from-shell-initialize)))

mouse scroll

(when kr-mac-p
  (setq mouse-wheel-flip-direction t))

rest

(when (string= system-type "darwin")
  (setq mac-option-modifier 'meta))
(when kr-mac-p
  (general-def 'global-map
    "C-<tab>" 'tab-next
    "C-S-<tab>" 'tab-previous))

(setq ns-command-modifier 'super)
(setq ns-option-modifier 'meta)


(when kr-mac-p
  (setq trash-directory  (expand-file-name "~/.Trash/")))

Enhancements

Small enhancements

(defun base64-encode-region-prefix-arg (&rest _args)
  "Pass prefix arg as third arg to `base64-encode-region'."
  (interactive "r\nP"))

(advice-add 'base64-encode-region :before #'base64-encode-region-prefix-arg)


(add-hook 'after-save-hook
          'executable-make-buffer-file-executable-if-script-p)

Make parrent directory when it doesn’t exist. Taken form here

(defun kr-er-auto-create-missing-dirs ()
  (let ((target-dir (file-name-directory buffer-file-name)))
    (unless (file-exists-p target-dir)
      (make-directory target-dir t))))

(add-to-list 'find-file-not-found-functions #'kr-er-auto-create-missing-dirs)

Window management

Emacs

Setup for display-buffer-alist. See this for reference.

This is maybe the most important variable to set, it makes switch-to-buffer obey display-buffer-alist rules.

(setq switch-to-buffer-obey-display-actions t)

(defun kr-swith-to-buffer-obey ()
  (interactive)
  (let ((switch-to-buffer-obey-display-actions nil))
    (call-interactively 'switch-to-buffer)))

    (general-def
      "C-x C-S-b" 'kr-swith-to-buffer-obey)
(setq split-height-threshold 120)

Custom dispaly-buffer funcitons

(defun kr-display-buffer-reuse-window (buffer alist)
  "Same ad `display-buffer-reuse-window' just doesn't respect
'inhibit-same-window' alist variable"
  (let* ((alist-entry (assq 'reusable-frames alist))
         (frames (cond (alist-entry (cdr alist-entry))
                       ((if (eq pop-up-frames 'graphic-only)
                            (display-graphic-p)
                          pop-up-frames)
                        0)
                       (display-buffer-reuse-frames 0)
                       (t (last-nonminibuffer-frame))))
         (window (if (eq buffer (window-buffer))
                     (selected-window)
                   ;; Preferably use a window on the selected frame,
                   ;; if such a window exists (Bug#36680).
                   (let* ((windows (delq (selected-window)
                                         (get-buffer-window-list
                                          buffer 'nomini frames)))
                          (first (car windows))
                          (this-frame (selected-frame)))
                     (cond
                      ((eq (window-frame first) this-frame)
                       first)
                      ((catch 'found
                         (dolist (next (cdr windows))
                           (when (eq (window-frame next) this-frame)
                             (throw 'found next)))))
                      (t first))))))
    (when (window-live-p window)
      (prog1 (window--display-buffer buffer window 'reuse alist)
        (unless (cdr (assq 'inhibit-switch-frame alist))
          (window--maybe-raise-frame (window-frame window)))))))

toggle window select

Function that toggles if a window can be selected with ~~other-window~ C-x o function.

(defun kr-disable-window-select ()
  "Make it so that you can't select this window with `C-x o'."
  (interactive)
  (if (not (window-parameter (selected-window) 'no-other-window))
      (set-window-parameter (selected-window) 'no-other-window t)
    (set-window-parameter (selected-window) 'no-other-window nil)))

sly

Always open sly REPL in other window

(add-to-list 'display-buffer-alist
             `("*sly-mrepl for sbcl*"
               kr-display-buffer-reuse-window
               (inhibit-same-window . t)))

Open sly-db window below sly-mrepl window

(defun kr-sly-db-new-window-direction (buffer alist)
  "Control where sly-db buffer is shown.
BUFFER and ALIST are the same type that are needed
for `display-buffer' funcitons."
  (display-buffer "*sly-mrepl for sbcl*")
  (add-to-list 'alist (cons 'window (get-buffer-window "*sly-mrepl for sbcl*")))
  (display-buffer-in-direction buffer alist))

(add-to-list 'display-buffer-alist
               `("*sly-db for sbcl (thread [0-9]+)*"
                 kr-sly-db-new-window-direction
                 (direction . below)
                 (window-height . 0.5)))

Completion framework

compleiton-style

Enable tab completion

(setq tab-always-indent 'complete)
(setq completion-styles '(basic partial-completion))

Prescient

Prescient is only used for sorting candidates

(use-package prescient
  :config
  (setq prescient-filter-method '(literal prefix literal-prefix regexp fuzzy))
  (prescient-persist-mode 1))

(use-package corfu-prescient
  :config
  (setq corfu-prescient-override-sorting t)
  (setq corfu-prescient-enable-filtering t)
  (add-to-list 'corfu-prescient-completion-category-overrides
               '(eglot
                 (styles prescient basic)))

  (corfu-prescient-mode 1))

(use-package vertico-prescient
  :config
  (setq vertico-prescient-override-sorting t)
  (setq vertico-prescient-enable-filtering nil)
  (vertico-prescient-mode 1))

orderless

Used for filtering candidates

(use-package orderless
  :ensure t
  :custom
  (completion-styles '(orderless basic))
  (completion-category-overrides '((file (styles basic partial-completion)))))

Vertico

(use-package vertico
  :config
  (vertico-mode 1)

  (vertico-mouse-mode 1)

  (setq vertico-cycle t)

  (vertico-multiform-mode 1)

  (setq vertico-multiform-commands
        '((xref-find-references-at-mouse buffer)
          (consult-yank-pop indexed)
          (project-find-regexp buffer)
          (consult-grep buffer)
          (consult-ripgrep buffer)
          (consult-git-grep buffer)
          (consult-imenu buffer)
          (eglot-find-implementation buffer)
          (imenu buffer)))

  ;; (setq vertico-multiform-categories
  ;;       '((file reverse)))

  (add-hook 'minibuffer-setup-hook #'vertico-repeat-save)
  (add-hook 'rfn-eshadow-update-overlay-hook 'vertico-directory-tidy) ; Correct file path when changed)

  (general-def
    "M-c" 'vertico-repeat)
  (general-def 'vertico-map
    "C-j" 'vertico-next
    "C-k" 'vertico-previous
    "C-<backspace>" 'vertico-directory-delete-word
    "<backspace>" 'vertico-directory-delete-char
    "<enter>" 'vertico-directory-enter)

  (general-def 'vertico-reverse-map
    "C-k" 'vertico-next
    "C-j" 'vertico-previous)

  (setq read-extended-command-predicate
        #'command-completion-default-include-p)

  (setq enable-recursive-minibuffers t)

  (set-face-foreground 'vertico-group-title
                       "#65737E"))

corfu

corfu config:

(use-package corfu
  :config
  (setq corfu-cycle t)
  (setq corfu-auto t)
  (setq corfu-auto-prefix 1)
  (setq corfu-auto-delay 0.1)
  (setq corfu-max-width 50)
  (setq corfu-min-width corfu-max-width)
  (setq corfu-preselect-first t)

  (global-corfu-mode 1)

  (general-def 'corfu-map
    "S-SPC" 'corfu-insert-separator
    "M-h" 'corfu-info-documentation
    "C-j" 'corfu-next
    "C-n" 'corfu-next
    "C-k" 'corfu-previous
    "C-p" 'corfu-previous))

cape

(use-package cape
  :config
  (add-hook 'completion-at-point-functions #'cape-file))

dabbrev

(setq dabbrev-case-replace nil)
(general-def
  "M-/" 'dabbrev-completion
  "C-M-/" 'dabbrev-expand)

abbrev

(with-eval-after-load 'abbrev
  (blackout 'abbrev-mode))

UI

Font

(defun kr-font-available-p (font-name)
  (find-font (font-spec :name font-name)))

(cond
 ((kr-font-available-p "Cascadia Code")
  (set-frame-font "Cascadia Code-12"))
 ((kr-font-available-p "Menlo")
  (set-frame-font "Menlo-12"))
 ((kr-font-available-p "DejaVu Sans Mono")
  (set-frame-font "DejaVu Sans Mono-12"))
 ((kr-font-available-p "Inconsolata")
  (set-frame-font "Inconsolata-12")))


  (if kr-mac-p
      (set-face-attribute 'default nil :height 135)
    (set-face-attribute 'default nil :height 115))

Themes

(use-package doom-themes
  :demand t
  :config
  (if kr-mac-p
      (load-theme 'doom-oceanic-next t)
    (load-theme 'doom-xcode t))

  ;; global-hl-line-mode and region have the same color so i change it here
  ;; (set-face-attribute 'region nil :background "#454545")
  (set-face-attribute 'secondary-selection nil :background "#151A2D")
  ;; (set-face-attribute 'highlight nil :background "#454545")

  ;; tab-bar-mode face
  (set-face-attribute 'tab-bar nil :background "#1e2029")
  (set-face-attribute 'tab-bar-tab nil
                      :foreground "#ffffff"
                      :background "#282a36"
                      :overline "gray90"
                      :box nil))

Start-up maximized

(when window-system
  (add-to-list 'initial-frame-alist '(fullscreen . maximized)))

Goggles

alternative package undo-hl.

(use-package goggles
  :hook ((prog-mode text-mode) . goggles-mode)
  :config
  (with-eval-after-load 'goggles
    (blackout 'goggles-mode)))

hl-todo

(use-package hl-todo
  :hook (prog-mode . hl-todo-mode)
  :config

  (general-def 'hl-todo-mode-map
    "C-z [t" 'hl-todo-previous
    "C-z ]t" 'hl-todo-next)

  (with-eval-after-load 'meow-mode
    (meow-define-keys 'normal
      '("[t" . "C-z [t")
      '("]t" . "C-z ]t")))

  (setq hl-todo-highlight-punctuation ":")
  (setq hl-todo-keyword-faces
        '(("TODO"   . "#FF4500")
          ("FIXME"  . "#FF0000")
          ("DEBUG"  . "#A020F0")
          ("GOTCHA" . "#FF4500")
          ("STUB"   . "#1E90FF")
          ("NOTE"   . "#AAD700"))))

Line numbers

(setq display-line-numbers-width 3)
(add-hook 'prog-mode-hook 'display-line-numbers-mode)

Highlight line

(global-hl-line-mode 1)

UX

Editing

evilmatchit

(use-package evil-matchit
  :config
  (with-eval-after-load 'meow
    (general-def meow-normal-state-keymap
      "%" 'evilmi-jump-items-native)))

Smartparen

Smart paren I’m using to pair characters like quotes.

(use-package smartparens
  :config
  (require 'smartparens-config)
  (defun indent-between-pair (&rest _ignored)
    (newline)
    (indent-according-to-mode)
    (forward-line -1)
    (indent-according-to-mode))
  (sp-local-pair 'prog-mode "{" nil :post-handlers '((indent-between-pair "RET")))
  (sp-local-pair 'prog-mode "[" nil :post-handlers '((indent-between-pair "RET")))
  (sp-local-pair 'prog-mode "(" nil :post-handlers '((indent-between-pair "RET")))

  (smartparens-global-mode 1)
  (show-smartparens-global-mode -1) ; alternative to show-paren-mode
  (show-paren-mode 1)
  (set-face-background 'show-paren-match "#7d7b7b")
  (blackout 'smartparens-mode))

Expand region

(use-package expand-region
  :config
  (setq expand-region-subword-enabled t))

Embrace

(use-package embrace
  :config
  (general-def meow-normal-state-keymap
    "C" 'embrace-commander))

Puni

(use-package puni
  :config
  (general-def 'meow-normal-state-keymap
    "D" 'puni-kill-line
    ">" 'k-compine-slurp-and-barf-forward
    "<" 'k-compine-slurp-and-barf-back)

  (defun k-compine-slurp-and-barf-forward (arg)
    (interactive "p")
    (if (> arg 0)
        (puni-slurp-forward arg)
      (puni-barf-forward (- arg))))

  (defun k-compine-slurp-and-barf-back (arg)
    (interactive "p")
    (if (> arg 0)
        (puni-slurp-backward arg)
      (puni-barf-backward (- arg)))))

undo-tree

(use-package undo-tree
  :config
  (global-undo-tree-mode 1)

  (general-def undo-tree-visualizer-mode-map
    "k" 'undo-tree-visualize-undo
    "j" 'undo-tree-visualize-redo
    "h" 'undo-tree-visualize-switch-branch-left
    "l" 'undo-tree-visualize-switch-branch-right)
  ;; changes needed for undo-tree to play nice with meow
  (general-def undo-tree-map
    "C-x r u" nil
    "C-x r U" nil
    "C-x C-r u" 'undo-tree-save-state-to-register
    "C-x C-r U" 'undo-tree-restore-state-from-register
    "C-x r" 'find-file-read-only)

  (blackout 'undo-tree-mode))

Mark ring

(defun kr-unpop-to-mark-command ()
  "Unpop off mark ring. Does nothing if mark ring is empty."
  (interactive)
  (when mark-ring
    (setq mark-ring (cons (copy-marker (mark-marker)) mark-ring))
    (set-marker (mark-marker) (car (last mark-ring)) (current-buffer))
    (when (null (mark t)) (ding))
    (setq mark-ring (nbutlast mark-ring))
    (goto-char (marker-position (car (last mark-ring))))))

find char

(unless (package-installed-p 'find-char)
  (package-vc-install "https://github.com/casouri/find-char"))
(use-package find-char)

marginalia

(use-package marginalia
  :config
  (marginalia-mode 1)
  (setq marginalia-annotators '(marginalia-annotators-heavy
                                marginalia-annotators-light nil)))

Consult

To search for multiple words with consult-ripgrep you should search e.g. for #defun#some words . The first filter is passed to an async ripgrep process and the second filter to the completion-style filtering (?).

(use-package consult
  :config
  (setq xref-show-xrefs-function #'consult-xref
        xref-show-definitions-function #'consult-xref)

  (general-def
    [remap switch-to-buffer] 'consult-buffer
    [remap apropos-command] 'consult-apropos
    [remap yank-pop] 'consult-yank-pop
    [remap goto-line] 'consult-goto-line
    [remap project-switch-to-buffer] 'project-list-buffers
    [remap project-list-buffers] 'consult-project-buffer
    "C-s" 'consult-line)

  (general-def mode-specific-map
    "bB" 'consult-buffer-other-window
    "bf" 'consult-focus-lines
    "i" 'consult-imenu)
  (setq consult-narrow-key "<")

  ;; preview only works with consult commands
  (setq consult-preview-key 'any)
  (with-eval-after-load 'consult
    (consult-customize
     consult-buffer
     :preview-key "C-o")))

embark

  • You can act on candidates with C-l and ask to remind bindings with C-h
  • You can run embark-export on all results (e.g. after a consult-line) with C-l E
    • You can run embark-export-snapshot with C-l S
(use-package embark
  :config
  (general-def
    "C-." 'embark-act)
  (general-def 'mode-specific-map
    "a a" 'embark-act))

(use-package embark-consult
  :after (embark consult))

xref

(use-package xref
  :ensure nil
  :config
  (setq xref-prompt-for-identifier nil)
  (general-def
    "s-[" 'xref-go-back
    "s-]" 'xref-go-forward
    "s-<mouse-1>" 'xref-find-references-at-mouse))
(unless (package-installed-p 'empx)
  (package-vc-install "https://github.com/ISouthRain/empx"))
(use-package empx
  :disabled
  :config
  (empx-mode 1))

Apps

General TUI apps that are emacs.

Dired

Emacs builtin file menager.

dired

(setq dired-dwim-target t)
(setq dired-isearch-filenames 'dwim)
(setq dired-recursive-copies 'always)
(setq dired-recursive-deletes 'always)
(setq dired-create-destination-dirs 'always)
(setq dired-listing-switches "-valh --group-directories-first")

(add-hook 'dired-mode-hook 'toggle-truncate-lines)
(add-hook 'dired-mode-hook #'(lambda () (unless (file-remote-p default-directory)
                                          (auto-revert-mode))))


(when (string= system-type "darwin")
  (setq dired-use-ls-dired t
        insert-directory-program (executable-find "gls")
        dired-listing-switches "-aBhl --group-directories-first"))

(general-def 'dired-mode-map
  "K" 'dired-kill-subdir
  "<mouse-2>" 'dired-mouse-find-file
  "C-c '" 'dired-toggle-read-only
  "/" 'dired-goto-file)

dired-x

(require 'dired-x)
(put 'dired-jump 'repeat-map nil)
(add-hook 'dired-mode-hook
          #'(lambda ()
              (setq dired-clean-confirm-killing-deleted-buffers nil)))

;; dired-x will help to remove buffers that were associated with deleted
;; files/directories

;; to not get y-or-no question for killing buffers when deliting files go here for
;; inspiration on how to do it
;; https://stackoverflow.com/questions/11546639/dired-x-how-to-set-kill-buffer-of-too-to-yes-without-confirmation
;; https://emacs.stackexchange.com/questions/30676/how-to-always-kill-dired-buffer-when-deleting-a-folder
;; https://www.reddit.com/r/emacs/comments/91xnv9/noob_delete_buffer_automatically_after_removing/

all-the-icons-dired

(use-package all-the-icons-dired
  :config
  (when (display-graphic-p)
    (add-hook 'dired-mode-hook #'(lambda () (interactive)
                                   (unless (file-remote-p default-directory)
                                     (all-the-icons-dired-mode))))))

dired-hacks

dired-subtree

(use-package dired-subtree
  :config
  (general-def dired-mode-map
    "TAB" 'dired-subtree-toggle)
  (advice-add 'dired-subtree-toggle
              :after #'(lambda ()
                         (interactive)
                         (call-interactively #'revert-buffer))))

dired-reinbow

(use-package dired-rainbow
  :config
  (require 'dired-rainbow)

  (dired-rainbow-define-chmod directory "#6cb2eb" "d.*")
  (dired-rainbow-define html "#eb5286" ("css" "less" "sass" "scss" "htm" "html" "jhtm" "mht" "eml" "mustache" "xhtml"))
  (dired-rainbow-define xml "#f2d024" ("xml" "xsd" "xsl" "xslt" "wsdl" "bib" "json" "msg" "pgn" "rss" "yaml" "yml" "rdata"))
  (dired-rainbow-define document "#9561e2" ("docm" "doc" "docx" "odb" "odt" "pdb" "pdf" "ps" "rtf" "djvu" "epub" "odp" "ppt" "pptx"))
  (dired-rainbow-define markdown "#ffed4a" ("org" "etx" "info" "markdown" "md" "mkd" "nfo" "pod" "rst" "tex" "textfile" "txt"))
  (dired-rainbow-define database "#6574cd" ("xlsx" "xls" "csv" "accdb" "db" "mdb" "sqlite" "nc"))
  (dired-rainbow-define media "#de751f" ("mp3" "mp4" "MP3" "MP4" "avi" "mpeg" "mpg" "flv" "ogg" "mov" "mid" "midi" "wav" "aiff" "flac"))
  (dired-rainbow-define image "#f66d9b" ("tiff" "tif" "cdr" "gif" "ico" "jpeg" "jpg" "png" "psd" "eps" "svg"))
  (dired-rainbow-define log "#c17d11" ("log"))
  (dired-rainbow-define shell "#f6993f" ("awk" "bash" "bat" "sed" "sh" "zsh" "vim"))
  (dired-rainbow-define interpreted "#38c172" ("py" "ipynb" "rb" "pl" "t" "msql" "mysql" "pgsql" "sql" "r" "clj" "cljs" "scala" "js"))
  (dired-rainbow-define compiled "#4dc0b5" ("asm" "cl" "lisp" "el" "c" "h" "c++" "h++" "hpp" "hxx" "m" "cc" "cs" "cp" "cpp" "go" "f" "for" "ftn" "f90" "f95" "f03" "f08" "s" "rs" "hi" "hs" "pyc" ".java"))
  (dired-rainbow-define executable "#8cc4ff" ("exe" "msi"))
  (dired-rainbow-define compressed "#51d88a" ("7z" "zip" "bz2" "tgz" "txz" "gz" "xz" "z" "Z" "jar" "war" "ear" "rar" "sar" "xpi" "apk" "xz" "tar"))
  (dired-rainbow-define packaged "#faad63" ("deb" "rpm" "apk" "jad" "jar" "cab" "pak" "pk3" "vdf" "vpk" "bsp"))
  (dired-rainbow-define encrypted "#ffed4a" ("gpg" "pgp" "asc" "bfe" "enc" "signature" "sig" "p12" "pem"))
  (dired-rainbow-define fonts "#6cb2eb" ("afm" "fon" "fnt" "pfb" "pfm" "ttf" "otf"))
  (dired-rainbow-define partition "#e3342f" ("dmg" "iso" "bin" "nrg" "qcow" "toast" "vcd" "vmdk" "bak"))
  (dired-rainbow-define vc "#0074d9" ("git" "gitignore" "gitattributes" "gitmodules"))
  (dired-rainbow-define-chmod executable-unix "#38c172" "-.*x.*"))

Git

Magit

Git client in emacs

(use-package transient
  :config
  (setq transient-display-buffer-action
        '(display-buffer-below-selected
          (dedicated . t)
          (inhibit-same-window . t))))

(use-package magit
  :config
  (add-hook 'git-commit-setup-hook #'flyspell-mode)
  (add-hook 'after-save-hook 'magit-after-save-refresh-status t)

  (setq git-commit-fill-column 72)
  (setq magit-process-finish-apply-ansi-colors t)
  (setq magit-display-buffer-function 'magit-display-buffer-same-window-except-diff-v1)
  (setq magit-save-repository-buffers 'dontask)

  (dolist (face '(magit-diff-added
                  magit-diff-added-highlight
                  magit-diff-removed
                  magit-diff-removed-highlight))
    (set-face-background face (face-attribute 'magit-diff-context-highlight :background)))
  (set-face-background 'magit-diff-context-highlight
                       (face-attribute 'default :background))

  (general-def mode-specific-map
    "v f" 'magit-find-file
    "v F" 'magit-find-file-other-window
    "v v" 'magit-status
    "v V" 'magit-status-here)

  (general-def 'magit-status-mode-map
    "S-<tab>" 'magit-section-cycle
    "C-<tab>" 'tab-next)

  (with-eval-after-load 'meow
    (add-hook 'git-commit-setup-hook
              (defun kr-git-commit-start-insert-maybe ()
                (when (and (bound-and-true-p meow-mode)
                           (bobp) (eolp))
                  (meow-insert)))))

  (with-eval-after-load 'project
    (general-def 'project-prefix-map
      "v" 'magit-project-status)
    (remove-hook 'project-switch-commands '(project-vc-dir "VC-Dir"))
    (add-hook 'project-switch-commands '(magit-project-status "Magit") 100)))

Display funciton to open magit additional buffers bellow current status one.

(defun kr-magit-display-buffer-same-window-except-diff-v1 (buffer)
  "Display BUFFER in the selected window except for some modes.
If a buffer's `major-mode' derives from `magit-diff-mode' or
`magit-process-mode', display it in another window bellow the current one. Display all
other buffers in the selected window."
  (display-buffer
   buffer (if (with-current-buffer buffer
                (derived-mode-p 'magit-diff-mode 'magit-process-mode))
              '(display-buffer-below-selected
                (window-height . 0.75)
                (inhibit-same-window . t))
            '(display-buffer-same-window))))

Git-gutter

If I ever need to change to margin I can use this to setup diff-hl in margin https://github.com/jimeh/.emacs.d/blob/master/modules/version-control/siren-diff-hl.el

(use-package git-gutter-fringe
  :config
  (setq git-gutter:update-interval 0.02)

  (require 'git-gutter-fringe) ; don't delete, must be here to style fringe
  (add-hook 'emacs-startup-hook #'global-git-gutter-mode)

  ;; how git-gutter looks in the fringe of the window
  (define-fringe-bitmap 'git-gutter-fr:added [#b11100000] nil nil '(center repeated))
  (define-fringe-bitmap 'git-gutter-fr:modified [#b11100000] nil nil '(center repeated))
  (define-fringe-bitmap 'git-gutter-fr:deleted
    [#b10000000
     #b11000000
     #b11100000
     #b11110000] nil nil 'bottom)

  (with-eval-after-load 'git-gutter
    (blackout 'git-gutter-mode))

  ;; setup repeat map for git-gutter
  (defvar kr-git-gutter-map
    (let ((keymap (make-sparse-keymap)))
      (define-key keymap (kbd "p") 'git-gutter:previous-hunk)
      (define-key keymap (kbd "n") 'git-gutter:next-hunk)
      keymap))

  (put 'git-gutter:next-hunk 'repeat-map 'kr-git-gutter-map)
  (put 'git-gutter:previous-hunk 'repeat-map 'kr-git-gutter-map)

  (general-def
    "C-z g" kr-git-gutter-map
    "<left-fringe> <mouse-3>" 'git-gutter:popup-hunk))

git-timemachine

(use-package git-timemachine
  :config
  (setq git-timemachine-show-minibuffer-details t)
  (general-def 'git-timemachine-mode-map
    "C-k" 'git-timemachine-show-previous-revision
    "C-j" 'git-timemachine-show-next-revision
    "q" 'git-timemachine-quit))

brose at remote

(use-package browse-at-remote
  :config
  (general-def 'mode-specific-map
    "v W" 'browse-at-remote)
  )

kubernetes

(use-package kubernetes
  :config
  (setq kubernetes-overview-custom-views-alist
        '((custom-overview . (context statefulsets deployments))))
  (setq kubernetes-default-overview-view 'custom-overview)

  (add-hook 'kubernetes-logs-mode-hook #'visual-line-mode)
  (add-hook 'kubernetes-logs-mode-hook #'display-line-numbers-mode)
  (add-hook 'kubernetes-logs-mode-hook #'ansi-color-mode)

  (general-def 'kubernetes-overview-mode-map
    "S-<tab>" 'magit-section-cycle
    "C-<tab>" 'tab-next))

Org

;; ;; https://orgmode.org/manual/Labels-and-captions-in-ODT-export.html
;; (setq org-odt-category-map-alist
;;       '(("__Figure__" "Slika" "value" "Figure" org-odt--enumerable-image-p)))
(require 'org-tempo)
(add-to-list 'org-modules 'org-tempo t)
(add-to-list 'org-structure-template-alist
             '("el" . "src emacs-lisp"))

(setq org-startup-indented t)
(setq org-image-actual-width 700)
(setq org-M-RET-may-split-line nil)
(setq org-return-follows-link t)
(setq org-src-window-setup 'current-window)

(with-eval-after-load 'org-indent
  (blackout 'org-indent-mode))

(add-hook 'org-mode-hook #'abbrev-mode)

olivetti

Closely related to org-mode but not really so it goes here with org mode

(use-package olivetti
  :config
  (setq olivetti-body-width 90))

devdocs

(use-package devdocs
  :config
  (add-hook 'devdocs-mode-hook #'olivetti-mode)
  (add-hook 'dart-mode-hook
            #'(lambda () (setq-local devdocs-current-docs '("dart~2")))))

man

(general-def 'Man-mode-map
  "d" 'View-scroll-half-page-forward
  "u" 'View-scroll-half-page-backward)

ibuffer

(general-def
  [remap list-buffers] 'ibuffer)

wgrep

(use-package wgrep
  :config
  (require 'wgrep)

  (set-face-background 'wgrep-face "#B6268"))

Project

(use-package project
  :ensure nil
  :config
  (setq project-vc-extra-root-markers '("go.mod" "requirements.txt"))

  (defun kr-project-grep ()
    (interactive)
    (if mark-active
        (progn
          (meow-save)
          (meow-cancel-selection)))
    (let ((vertico-buffer-mode t))
      (if (or (eql (cadr (project-current)) 'Git) (eql (car (project-current)) 'go-module))
          (call-interactively #'consult-git-grep)
        (call-interactively #'consult-ripgrep))))
  (with-eval-after-load 'consult
    (require 'vertico-buffer)
    (define-key project-prefix-map (kbd "g") 'consult-ripgrep)))

Programming

eglot

(add-hook 'special-mode-hook 'visual-line-mode)
(use-package eglot
  :hook ((go-ts-mode typescript-mode js-mode) . eglot-ensure)
  :config
  (general-def 'eglot-mode-map
    "C-z l r r" 'eglot-rename
    "C-z h" 'eldoc-box-help-at-point)

  (set-face-attribute 'eglot-highlight-symbol-face nil :background nil :underline "#f8f8f8")
  (general-def 'eglot-mode-map
    "C-M-." 'eglot-find-implementation)

  (add-hook 'eglot-managed-mode-hook
            (defun kr-eglot-switch-eldoc-functions ()
              (remove-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function t)
              (add-hook 'eldoc-documentation-functions #'eglot-hover-eldoc-function nil t)))

  (add-hook 'go-ts-mode-hook #'eglot-ensure)
  (add-hook 'rust-mode-hook #'eglot-ensure))

dape

(unless (package-installed-p 'dape)
  (package-vc-install "https://github.com/svaante/dape"))
(use-package dape
  :after meow
  :init
  (setq dape-debug t)
  :commands (dape)
  :config

  (dolist (cmd dape-global-map)
    (unless (eq cmd 'keymap)
      (put (cdr cmd) 'repeat-map nil)))

  (setq dape-inlay-hints t)
  (setf (alist-get 'go-test dape-configs)
        `(modes (go-mode go-ts-mode)
          ensure dape-ensure-command
          fn (dape-config-autoport dape-config-tramp)
          command "dlv"
          command-args ("dap" "--listen" "127.0.0.1::autoport")
          command-cwd (lambda()(if (string-suffix-p "_test.go" (buffer-name))
                                   default-directory (dape-cwd)))
          port :autoport
          :type "debug"
          :request "launch"
          :mode (lambda() (if (string-suffix-p "_test.go" (buffer-name)) "test" "debug"))
          :program "."
          :cwd "."
          :args (lambda()
                  (require 'which-func)
                  (if (string-suffix-p "_test.go" (buffer-name))
                      (when-let* ((test-name (which-function))
                                  (test-regexp (concat "^" test-name "$")))
                        (if test-name `["-test.run" ,test-regexp]
                          (error "No test selected")))
                    []))))

  (defvar meow-dape-state-keymap (make-sparse-keymap))
  (meow-define-state dape
    "meow for dape repl context"
    :lighter " [D]"
    :keymap meow-dape-state-keymap)
  (meow-define-keys 'dape
    '("<" . dape-stack-select-up)
    '(">" . dape-stack-select-down)
    '("B" . dape-breakpoint-remove-all)
    '("D" . dape-disconnect-quit)
    '("R" . dape-repl)
    '("S" . dape-select-stack)
    '("b" . dape-breakpoint-toggle)
    '("c" . dape-continue)
    '("d" . dape)
    '("e" . dape-breakpoint-expression)
    '("h" . dape-breakpoint-hits)
    '("I" . dape-info)
    '("i" . meow-insert)
    '("l" . dape-breakpoint-log)
    '("m" . dape-read-memory)
    '("n" . dape-next)
    '("o" . dape-step-out)
    '("p" . dape-pause)
    '("q" . dape-quit)
    '("r" . dape-restart)
    '("s" . dape-step-in)
    '("t" . dape-select-thread)
    '("w" . dape-watch-dwim)
    '("x" . dape-evaluate-expression))
  (add-to-list 'meow-mode-state-list '(dape-repl-mode . dape)))

treesit

(use-package treesit-auto
  :custom
  (treesit-auto-install 'prompt)
  :config
  (setq treesit-font-lock-level 4)
  (setq treesit-auto-langs
        '(bash c cpp cmake go gomod dockerfile markdown tsx typescript html css javascript json yaml))
  (treesit-auto-add-to-auto-mode-alist 'all)
  (global-treesit-auto-mode))

Formating

Formating code buffers on save.

(use-package apheleia
  :hook (js-mode go-ts-mode)
  :init
  (add-hook 'go-ts-mode-hook
            (defun kr-go-format-buffer ()
              (setq-local apheleia-formatter '(goimports gofmt))))
  :config
  (setf (alist-get 'goimports apheleia-formatters)
        '("goimports" "-srcdir" filepath)))

Languages

Common Lisp

Seting sbcl to be default interpreter for lisp.

(if (executable-find "ros")
    (setq inferior-lisp-program "ros -Q run")
  (setq inferior-lisp-program "sbcl"))
(use-package sly
  :config
  (setq sly-mrepl-prevent-duplicate-history t)

  ;; (setq sly-contribs '(sly-fancy sly-mrepl))
  (general-def 'sly-mode-map
    "C-z h" 'sly-describe-symbol)

  (with-eval-after-load 'meow
    (add-to-list 'meow-mode-state-list '(sly-mrepl-mode . normal))
    (add-to-list 'meow-mode-state-list '(sly-db-mode . motion))
    (add-to-list 'meow-mode-state-list '(sly-xref-mode . motion))
    (add-to-list 'meow-mode-state-list '(sly-stickers--replay-mode . motion))
    (add-to-list 'meow-mode-state-list '(sly-inspector-mode . motion)))
  ;; switch bufers REPL - DB - Source
  (general-def '(lisp-mode-map sly-mrepl-mode-map)
    "C-c d" #'(lambda () (interactive) (switch-to-buffer "*sly-db for sbcl (thread 1)*")))
  (general-def '(lisp-mode-map sly-db-mode-map sly-db-frame-map)
    "C-c '" #'(lambda ()
                (interactive)
                (call-interactively #'sly-mrepl)
                (end-of-buffer)))
  (general-def '(sly-db-mode-map sly-db-frame-map)
    "C-c d" #'sly-switch-to-most-recent)
  (general-def 'sly-mrepl-mode-map
    "C-j" 'sly-mrepl-next-prompt
    "C-k" 'sly-mrepl-previous-prompt
    "C-p" 'sly-mrepl-previous-input-or-button
    "C-n" 'sly-mrepl-next-input-or-button
    "C-c '" #'sly-switch-to-most-recent)

  (general-def 'sly-stickers--replay-mode-map
    "/" 'sly-stickers-replay-jump))

(use-package sly-repl-ansi-color
  :config
  (push 'sly-repl-ansi-color sly-contribs))

JavaScript

(setq js-indent-level 2)

TypeScript

(use-package typescript-mode
  :config
  (add-hook 'typescript-mode-hook #'apheleia-mode)
  (setq typescript-indent-level 2))

JSON

(use-package jsonian
  :config
  (with-eval-after-load 'eglot
    (add-to-list 'eglot-server-programs
                 `(jsonian-mode . ,(eglot-alternatives '(("vscode-json-language-server" "--stdio") ("json-languageserver" "--stdio")))))))

terraform

(use-package terraform-mode)

Golang

(use-package go-ts-mode
  :ensure-system-package
  ((goimports . "go install golang.org/x/tools/cmd/goimports@latest"))
  :config
  (setq go-ts-mode-indent-offset 4))

Yaml

(use-package yaml-mode
  :config
  (add-to-list 'auto-mode-alist '("\\.yml\\'" . yaml-mode))
  (add-hook 'yaml-mode-hook #'toggle-truncate-lines)
  (add-hook 'yaml-mode-hook #'display-line-numbers-mode))

yasnippet

(use-package yasnippet
  :config

  (defun kr-is-corfu-active ()
    (when corfu--candidates t))
  (add-hook 'yas-keymap-disable-hook #'kr-is-corfu-active)

  (setq yas-alias-to-yas/prefix-p nil)    ; don't make yas/prefix commands

  (add-hook 'prog-mode-hook #'yas-minor-mode))

Jenkins

(use-package jenkinsfile-mode)

Docker

(use-package dockerfile-mode)

Hooks for prog mode

(add-hook 'prog-mode-hook #'toggle-truncate-lines)

meow

Meow is a mode for modal edditing inpired by VIM.

Meow

(defun kr-puni-kill-dwim ()
    (interactive)
    (if (use-region-p)
        (puni-kill-region)
      (puni-kill-line)))

(defun meow-setup ()
  "My meow setup thats similar to evil/vim"
  (meow-motion-overwrite-define-key
   '("j" . meow-next)
   '("k" . meow-prev)
   '("M-j" . scroll-up-line)
   '("M-k" . scroll-down-line)
   '("`" . kr-meow-last-buffer)
   '("<escape>" . keyboard-quit))
  (meow-leader-define-key
   ;; SPC j/k will run the original command in MOTION state.
   '("j" . "H-j")
   '("k" . "H-k")
   '("`" . "H-`")
   '("?" . meow-cheatsheet)
   '("/" . meow-keypad-describe-key))
  (meow-normal-define-key
   '("0" . meow-expand-0)
   '("9" . meow-expand-9)
   '("8" . meow-expand-8)
   '("7" . meow-expand-7)
   '("6" . meow-expand-6)
   '("5" . meow-expand-5)
   '("4" . meow-expand-4)
   '("3" . meow-expand-3)
   '("2" . meow-expand-2)
   '("1" . meow-expand-1)
   '("=". scroll-lock-mode)
   '("-" . negative-argument)
   '("`" . kr-meow-last-buffer)
   '("<escape>" . keyboard-quit)
   ;; thing
   '("." . meow-inner-of-thing)
   '("," . meow-bounds-of-thing)
   ;; '("[" . meow-beginning-of-thing)
   ;; '("]" . meow-end-of-thing)

   '("u" . meow-undo)
   '("U" . undo-tree-redo)
   '("y" . meow-save)

   '("p" . meow-yank)
   '("i" . meow-insert)
   '("a" . meow-append)

   '("j" . next-line)
   '("k" . previous-line)
   '("h" . backward-char)
   '("l" . forward-char)
   '("M-j" . scroll-up-line)
   '("M-k" . scroll-down-line)
   '("H" . beginning-of-line-text)
   '("L" . move-end-of-line)

   '("J" . meow-next-expand)
   '("K" . meow-prev-expand)
   '("c" . meow-change)
   '("n" . meow-search)
   '("/" . meow-visit)

   '("D" . puni-kill-line)
   '("d" . kr-puni-kill-dwim)
   '("x" . puni-backward-delete-char)
   '("X" . puni-forward-delete-char)
   '("Z" . puni-force-delete)

   '("e" . meow-next-word)
   '("E" . puni-forward-sexp)
   '(";" . meow-reverse)
   '("b" . meow-back-word)
   '("B" . puni-backward-sexp)
   '("v" . set-mark-command)
   '("V" . meow-line)
   '("f" . find-char)
   '("t" . meow-till)
   '("G" . meow-grab)
   '("m" . meow-join)
   ;; need to think about these bindings
   '("r" . meow-replace)
   '("R" . meow-swap-grab)
   '("P" . meow-sync-grab)

   '("@" . goto-line)
   '("z" . meow-pop-selection)
   '("o" . meow-open-below)
   '("O" . meow-open-above)

   '("q" . meow-quit)
   '("Q" . kill-current-buffer))

  (meow-normal-define-key
   '("{" . backward-paragraph)
   '("}" . forward-paragraph))

  ;; help
  (meow-normal-define-key
   '("M-h" . "C-z h"))
  (meow-motion-overwrite-define-key
   '("M-h" . "C-z h"))

  ;; commands that are not from meow
  (meow-normal-define-key
   '("M" . set-mark-command)
   '("'" . pop-to-mark-command)
   '("\"" . pop-global-mark))
  (meow-leader-define-key
   (cons "p" project-prefix-map)))


(use-package meow
  :config
  (require 'meow)

  (meow-setup)
  (meow-global-mode 1)

  (setq meow-use-clipboard t)

  (setq meow-keypad-leader-dispatch "C-c")

  (add-hook 'ghelp-page-mode-hook 'meow-motion-mode)
  (add-to-list 'meow-mode-state-list '(helpful-mode . motion))
  (add-to-list 'meow-mode-state-list '(ghelp . motion))
  (add-to-list 'meow-mode-state-list '(fundamental-mode . normal))
  (add-to-list 'meow-mode-state-list '(eshell-mode . normal))

  (general-def 'meow-normal-state-keymap
    "Z" 'meow-cancel-selection))

(with-eval-after-load 'corfu
  (add-hook 'meow-insert-exit-hook #'corfu-quit))

(defun kr-meow-last-buffer ()
  (interactive)
  (let ((switch-to-buffer-obey-display-actions nil))
    (call-interactively #'meow-last-buffer)))

Personal extensions

Advice for meow-reverse

For some comands meow-find, meow-till, meow-line… you can press - (negative-argument) to go in reverse. We already have a meow command to go in reverse meow-reverse but it only works if we have a selection so I aviced it to enter negative-argument when there is no selection so that it can be used when there is no selection active.

(defun kr-meow-reverse (fun)
  "Attemt to reverse command when there is no selection."
  (if (region-active-p)
      (funcall fun)
    (call-interactively #'negative-argument)))
(advice-add 'meow-reverse :around #'kr-meow-reverse)

Advice for meow-expand

Normally when in normal-state the number keys 0..9 are bount to meow-expand-[0..9]. This command doesn’t do anything if there is no selection so I made an advice so it calls digit-argument if there is no seleciton, so you can press 9 meow-line or meow-line 9 and you will do the same thing.

(defun kr-meow-maybe-digit (fun n)
  "Advice so that I can get digit arguments if there is no
 selection active and expand selestion if the selection is active."
  (if (region-active-p)
      (funcall fun n)
    (call-interactively #'digit-argument)))
(advice-add 'meow-expand :around #'kr-meow-maybe-digit)

Advice for meow-yank

(defun kr-meow-yank ()
  (save-excursion
    (exchange-point-and-mark t)
    (indent-according-to-mode))
  (indent-according-to-mode))
(advice-add 'meow-yank :after #'kr-meow-yank)

Toogle motion

(defun kr-meow-motion-normal ()
  (interactive)
  (cond ((meow-motion-mode-p)
         (meow-normal-mode 1)(meow-motion-mode -1))
        (t
         (meow-normal-mode -1)(meow-motion-mode 1))))

  (general-def '(meow-motion-state-keymap meow-normal-state-keymap)
    "|" 'kr-meow-motion-normal)

append/insert line

Insert or append on line.

(defun kr-meow-append-to-line ()
  "Append to line."
  (interactive)
  (if (region-active-p)
      (progn
        (unless (= (point) (region-end))
          (meow-reverse))
        (embrace-add))
    (progn
      (end-of-line)
      (call-interactively #'meow-append))))

(defun kr-meow-insert-to-line ()
  "Insert to line."
  (interactive)
  (if (region-active-p)
      (progn
        (unless (= (point) (region-beginning))
          (meow-reverse))
        (embrace-add)
        (forward-char))
    (progn
      (beginning-of-line-text)
      (call-interactively #'meow-insert))))


  (general-def 'meow-normal-state-keymap
    "I" 'kr-meow-insert-to-line
    "A" 'kr-meow-append-to-line)

This makes it work with smartparens surround feature.

;; Smartparens integraion
(defun kr-meow-append-mark ()
  "Move to end of selection and switch to insert state.
Keep mark active."
  (interactive)
  (call-interactively #'meow-append)
  (activate-mark))

(defun kr-meow-insert-mark ()
  "Move to beginign of selection and switch to insert state.
Keep mark active."
  (interactive)
  (call-interactively #'meow-insert)
  (activate-mark))

meow-kill

extend meow-kill so that it kills the whole line if mark is not active

(defun kr-meow-kill-whole-line (old-fun)
  "Delete line if there is no selection but delete selection if there
  is active selection."
  (if (region-active-p)
      (meow-kill)
    (funcall old-fun)))
(advice-add 'meow-kill-whole-line :around 'kr-meow-kill-whole-line)

meow-save

copy line on selection

(defun kr-meow-save-line ()
  (interactive)
  (meow-line 1)
  (call-interactively #'meow-save))


(defun kr-meow-copy-line-or-selection (fun)
  "Copy region if active. Copy line if no region is active."
  (if (region-active-p)
      (funcall fun)
    (kr-meow-save-line)))
(advice-add 'meow-save :around #'kr-meow-copy-line-or-selection)

Bindings for packages

Binding for other packages

gitgutter

(with-eval-after-load 'git-gutter
  (general-def
    "C-z g n" 'git-gutter:next-hunk
    "C-z g p" 'git-gutter:previous-hunk)
  (meow-define-keys 'normal
    '("]g" . "C-z g n")
    '("[g" . "C-z g p")))

Flymake

(with-eval-after-load 'flycheck
  (meow-define-keys 'normal
    '("[e" . "C-z [e")
    '("]e" . "C-z ]e")))

Magit

(meow-define-keys 'normal
  '("gg" . magit-file-dispatch))

LSP

(meow-define-keys 'normal
  '("gr" . "C-z l r r")
  '("gl" . "C-z l"))

expand region

(with-eval-after-load 'meow
  (defun kr-expand-with-meow ()
    "Hellper command so that meow can work with expand region."
    (interactive)
    (call-interactively #'er/expand-region)
    (let* ((beg (region-beginning))
           (end (region-end))
           (search (format "\\_<%s\\_>" (regexp-quote (buffer-substring-no-properties beg end)))))
      (setq meow--selection (list '(expand . word) beg end))
      (meow--push-search search)
      (meow--highlight-regexp-in-buffer search)))

  (meow-define-keys 'normal
    '("w" . kr-expand-with-meow)
    '("W" . er/contract-region)
    '("s" . kr-expand-with-meow)
    '("S" . er/contract-region)))


(use-package expreg
  :config
  (meow-define-keys 'normal
    '("s" . expreg-expand)
    '("S" . expreg-contract)))

Temp

(setq truncate-partial-width-windows 200)
(general-def
  "C-`" 'next-error
  "C-~" 'previous-error)

(advice-add #'meow-kill :after #'cycle-spacing)

Notes

To start emacs with different configuration run:

emacs --init-directory=directory