You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

59 KiB

Emacs configuration file

TODOS

  • early-init.el? What to outsource here?

  • Paket exec-path-from-shell, um PATH aus Linux auch in emacs zu haben

  • Smart mode line?

  • Theme

  • flymake instead of flycheck?

  • Hydra

  • General

  • (defalias 'list-buffers 'ibuffer) ;; change default to ibuffer

  • ido?

  • treemacs (for linux) windmove?

  • tramp (in linux)

  • visual-regexp

  • org configuration: paths

  • org custom agenda

  • org-ql (related to org agendas)

  • org configuration: everything else

  • beancount configuration from config.org

  • CONTINUE TODO from config.org at Programming

  • all-the-icons?

  • lispy? https://github.com/abo-abo/lispy

Header

Emacs variables are dynamically scoped. That's unusual for most languages, so disable it here, too

;;; init.el --- -*- lexical-binding: t -*-

First start

These functions updates config.el whenever changes in config.org are made. The update will be active after saving.

(defun my/tangle-config ()
  "Export code blocks from the literate config file."
  (interactive)
  ;; prevent emacs from killing until tangle-process has finished
  (add-to-list 'kill-emacs-query-functions
               (lambda ()
                 (or (not (process-live-p (get-process "tangle-process")))
                     (y-or-n-p "\"my/tangle-config\" is running; kill it? "))))
  (org-babel-tangle-file config-org init-el)
  (message "reloading user-init-file")
  (load-file init-el))

(add-hook 'org-mode-hook
          (lambda ()
            (if (equal (buffer-file-name) config-org)
                (my--add-local-hook 'after-save-hook 'my/tangle-config))))

(defun my--add-local-hook (hook function)
  "Add buffer-local hook."
  (add-hook hook function :local t))

A small function to measure start up time. Compare that to emacs -q –eval='(message "%s" (emacs-init-time))' (roughly 0.27s) https://blog.d46.us/advanced-emacs-startup/

(add-hook 'emacs-startup-hook
          (lambda ()
            (message "Emacs ready in %s with %d garbage collections."
                     (format "%.2f seconds"
                             (float-time
                              (time-subtract after-init-time before-init-time)))
                     gcs-done)))

;(setq gc-cons-threshold (* 50 1000 1000))

Default settings

paths

(defconst *sys/gui*
  (display-graphic-p)
  "Is emacs running in a gui?")

(defconst *sys/linux*
  (string-equal system-type 'gnu/linux)
  "Is the system running Linux?")

(defconst *sys/windows*
  (string-equal system-type 'windows-nt)
  "Is the system running Windows?")

(defconst *home_desktop*
  (string-equal (system-name) "marc")
  "Is emacs running on my desktop?")

(defconst *home_laptop*
  (string-equal (system-name) "laptop")
  "Is emacs running on my laptop?")

(defconst *work_local*
  (string-equal (system-name) "PMPCNEU08")
  "Is emacs running at work on the local system?")

(defconst *work_remote*
  (or (string-equal (system-name) "PMTS01")
      (string-equal (system-name) "PMTSNEU01"))
  "Is emacs running at work on the remote system?")
(defvar MY--PATH_USER_LOCAL  (concat user-emacs-directory "user-local/"))
(defvar MY--PATH_USER_GLOBAL (concat user-emacs-directory "user-global/"))

(add-to-list 'custom-theme-load-path (concat MY--PATH_USER_GLOBAL "themes"))

(when *sys/linux*
  (defconst MY--PATH_ORG_FILES        (expand-file-name "~/Archiv/Organisieren/"))
  (defconst MY--PATH_ORG_FILES_MOBILE (expand-file-name "~/Archiv/Organisieren/mobile/"))
  (defconst MY--PATH_ORG_JOURNAl      (expand-file-name "~/Archiv/Organisieren/Journal/"))
  (defconst MY--PATH_ORG_ROAM         (file-truename "~/Archiv/Organisieren/")))
(when *work_remote*
  (defconst MY--PATH_ORG_FILES        "p:/Eigene Dateien/Notizen/")
  (defconst MY--PATH_ORG_FILES_MOBILE nil)  ;; hacky way to prevent "free variable" compiler error
  (defconst MY--PATH_ORG_JOURNAL      nil)  ;; hacky way to prevent "free variable" compiler error
  (defconst MY--PATH_START            "p:/Eigene Dateien/Notizen/")
  (defconst MY--PATH_ORG_ROAM         (expand-file-name "p:/Eigene Dateien/Notizen/")))

(setq custom-file (concat MY--PATH_USER_LOCAL "custom.el"))  ;; don't spam init.e with saved customization settings
(setq backup-directory-alist `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms `((".*" ,temporary-file-directory)))
(customize-set-variable 'auth-sources (list (concat MY--PATH_USER_LOCAL "authinfo")
                                            (concat MY--PATH_USER_LOCAL "authinfo.gpg")
                                            (concat MY--PATH_USER_LOCAL "netrc")))

Browser

(setq browse-url-function 'browse-url-generic
      browse-url-generic-program "firefox")

Elpaca

Boilerplate for Elpaca

  (defvar elpaca-installer-version 0.7)
  (defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
  (defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
  (defvar elpaca-repos-directory (expand-file-name "repos/" elpaca-directory))
  (defvar elpaca-order '(elpaca :repo "https://github.com/progfolio/elpaca.git"
                                :ref nil :depth 1
                                :files (:defaults "elpaca-test.el" (:exclude "extensions"))
                                :build (:not elpaca--activate-package)))
  (let* ((repo  (expand-file-name "elpaca/" elpaca-repos-directory))
         (build (expand-file-name "elpaca/" elpaca-builds-directory))
         (order (cdr elpaca-order))
         (default-directory repo))
    (add-to-list 'load-path (if (file-exists-p build) build repo))
    (unless (file-exists-p repo)
      (make-directory repo t)
      (when (< emacs-major-version 28) (require 'subr-x))
      (condition-case-unless-debug err
          (if-let ((buffer (pop-to-buffer-same-window "*elpaca-bootstrap*"))
                   ((zerop (apply #'call-process `("git" nil ,buffer t "clone"
                                                   ,@(when-let ((depth (plist-get order :depth)))
                                                       (list (format "--depth=%d" depth) "--no-single-branch"))
                                                   ,(plist-get order :repo) ,repo))))
                   ((zerop (call-process "git" nil buffer t "checkout"
                                         (or (plist-get order :ref) "--"))))
                   (emacs (concat invocation-directory invocation-name))
                   ((zerop (call-process emacs nil buffer nil "-Q" "-L" "." "--batch"
                                         "--eval" "(byte-recompile-directory \".\" 0 'force)")))
                   ((require 'elpaca))
                   ((elpaca-generate-autoloads "elpaca" repo)))
              (progn (message "%s" (buffer-string)) (kill-buffer buffer))
            (error "%s" (with-current-buffer buffer (buffer-string))))
        ((error) (warn "%s" err) (delete-directory repo 'recursive))))
    (unless (require 'elpaca-autoloads nil t)
      (require 'elpaca)
      (elpaca-generate-autoloads "elpaca" repo)
      (load "./elpaca-autoloads")))
  (add-hook 'after-init-hook #'elpaca-process-queues)
  (elpaca `(,@elpaca-order))

;;at work symlinks wont work, and open file limit can be an issue
(when *work_remote*
  (setq elpaca-queue-limit 12)
  (elpaca-no-symlink-mode))

;(setq use-package-always-ensure t)
(elpaca elpaca-use-package
  ;; enable use-package :ensure support for elpaca
  (elpaca-use-package-mode))

(elpaca-wait)

use-package keywords general / diminish

Needs to be loaded before any other package which uses the :general keyword

(use-package general
  :ensure t
  :demand t)

(use-package diminish
  :ensure t
  :demand t)

;;wait for elpaca any time a use-package keyword is added
(elpaca-wait)

sane defaults

(setq-default create-lockfiles nil) ;; disable lock files, can cause trouble in e.g. lsp-mode
(defalias 'yes-or-no-p 'y-or-n-p) ;; answer with y and n
(setq custom-safe-themes t) ;; don't ask me if I want to load a theme
(setq sentence-end-double-space nil) ;; don't coun two spaces after a period as the end of a sentence.
(delete-selection-mode t)  ;; delete selected region when typing
(use-package saveplace
  :ensure nil
  :config
  (save-place-mode 1)  ;; saves position in file when it's closed
  :custom
  (save-place-file (concat MY--PATH_USER_LOCAL "places")))
(setq save-place-forget-unreadable-files nil)  ;; checks if file is readable before saving position
(global-set-key (kbd "RET") 'newline-and-indent)  ;; indent after newline
(setq save-interprogram-paste-before-kill t)  ;; put replaced text into killring

;; https://emacs.stackexchange.com/questions/3673/how-to-make-vc-and-magit-treat-a-symbolic-link-to-a-real-file-in-git-repo-just
(setq find-file-visit-truename t)  ;; some programs like lsp have trouble following symlinks, maybe vc-follow-symlinks would be enough

Performance Optimization

Garbage Collection

Make startup faster by reducing the frequency of garbage collection. Set gc-cons-threshold (default is 800kb) to maximum value available, to prevent any garbage collection from happening during load time.

(setq gc-cons-threshold most-positive-fixnum)

Restore it to reasonable value after init. Also stop garbage collection during minibuffer interaction (helm etc.)

(defconst 1mb 1048576)
(defconst 20mb 20971520)
(defconst 30mb 31457280)
(defconst 50mb 52428800)

(defun my--defer-garbage-collection ()
  (setq gc-cons-threshold most-positive-fixnum))

(defun my--restore-garbage-collection ()
  (run-at-time 1 nil (lambda () (setq gc-cons-threshold 30mb))))

(add-hook 'emacs-startup-hook 'my--restore-garbage-collection 100)
(add-hook 'minibuffer-setup-hook 'my--defer-garbage-collection)
(add-hook 'minibuffer-exit-hook 'my--restore-garbage-collection)

(setq read-process-output-max 1mb)  ;; lsp-mode's performance suggest

File Handler

(defvar default-file-name-handler-alist file-name-handler-alist)
(setq file-name-handler-alist nil)

(add-hook 'emacs-startup-hook
          (lambda ()
            (setq file-name-handler-alist default-file-name-handler-alist)) 100)

Others

;; Resizing the emacs frame can be a terriblu expensive part of changing the font.
;; By inhibiting this, we easily hale startup times with fonts that are larger
;; than the system default.
(setq package-enable-at-startup nil)
(setq frame-inhibit-implied-resize t)

Appearance

Defaults

(set-charset-priority 'unicode)
(setq-default locale-coding-system 'utf-8
              default-process-coding-system '(utf-8-unix . utf-8-unix))
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-selection-coding-system 'utf-8)
(if *sys/windows*
    (progn
      (prefer-coding-system 'utf-8-dos)
      (set-clipboard-coding-system 'utf-16-le)
      (set-selection-coding-system 'utf-16-le))
  (prefer-coding-system 'utf-8))

(setq-default bidi-paragraph-direction 'left-to-right
              bidi-inhibit-bpa t                      ;; both settings reduce line rescans
              uniquify-buffer-name-style 'forward
              indent-tabs-mode nil                    ;; avoid tabs in place of multiple spaces (they look bad in tex)
              indicate-empty-lines t                  ;; show empty lines
              scroll-margin 5                         ;; smooth scrolling
              scroll-conservatively 10000
              scroll-preserve-screen-position 1
              scroll-step 1
              ring-bell-function 'ignore              ;; disable pc speaker bell
              visible-bell t)
(global-hl-line-mode t)                               ;; highlight current line
(blink-cursor-mode -1)                                ;; turn off blinking cursor
(column-number-mode t)

Remove redundant UI

(menu-bar-mode -1)    ;; disable menu bar
(tool-bar-mode -1)    ;; disable tool bar
(scroll-bar-mode -1)  ;; disable scroll bar

Font

(when *sys/linux*
  (set-face-font 'default "Hack-10"))
(when *work_remote*
  (set-face-font 'default "Lucida Sans Typewriter-11"))

Themes

(defun my/toggle-theme ()
  (interactive)
  (when (or *sys/windows* *sys/linux*)
    (if (eq (car custom-enabled-themes) 'plastic)
        (progn (disable-theme 'plastic)
               (load-theme 'leuven))
      (progn
        (disable-theme 'leuven)
        (load-theme 'plastic)))))

(bind-key "C-c t" 'my/toggle-theme)

Windows Theme:

(when *sys/windows*
  (mapcar #'disable-theme custom-enabled-themes)
  (load-theme 'tango))
(when *sys/linux*
  (mapcar #'disable-theme custom-enabled-themes)
  (load-theme 'plastic))

line wrappings

(global-visual-line-mode)
;(diminish 'visual-line-mode)
(use-package adaptive-wrap
  :ensure t
  :hook
  (visual-line-mode . adaptive-wrap-prefix-mode))
  ;  :init
  ;  (when (fboundp 'adaptive-wrap-prefix-mode)
  ;    (defun me/activate-adaptive-wrap-prefix-mode ()
  ;      "Toggle `visual-line-mode' and `adaptive-wrap-prefix-mode' simultaneously."
  ;      (adaptive-wrap-prefix-mode (if visual-line-mode 1 -1)))
  ;    (add-hook 'visual-line-mode-hook 'me/activate-adaptive-wrap-prefix-mode)))

line numbers

(use-package display-line-numbers
  :ensure nil
  :init
  :hook
  ((prog-mode
    org-src-mode) . display-line-numbers-mode)
  :config
  (setq-default display-line-numbers-type 'visual
                display-line-numbers-current-absolute t
                display-line-numbers-with 4
                display-line-numbers-widen t))

misc

Delight can replace mode names with custom names , e.g. python-mode with just "π ".

(use-package rainbow-mode
  :ensure t
  :diminish
  :hook
  ((org-mode
    emacs-lisp-mode) . rainbow-mode))

(use-package delight
  :if *sys/linux*
  :ensure t)

(show-paren-mode t) ;; show other part of brackets
(setq blink-matching-paren nil)  ;; not necessary with show-paren-mode, bugs out on C-s counsel-line
(use-package rainbow-delimiters
  :ensure t
  :hook
  (prog-mode . rainbow-delimiters-mode))

dired

(use-package dired
  :ensure nil
  :custom
  (dired-kill-when-opening-new-dired-buffer t))

Bookmarks

Usage:

  • C-x r m (bookmark-set): add bookmark

  • C-x r l (list-bookmark): list bookmarks

  • C-x r b (bookmark-jump): open bookmark

Edit bookmarks (while in bookmark file):

  • d: mark current item

  • x: delete marked items

  • r: rename current item

  • s: save changes

(use-package bookmark
  :ensure nil
  :custom
  (bookmark-default-file (concat MY--PATH_USER_LOCAL "bookmarks")))

;;do I really want this?
(use-package bookmark+
  :ensure (:host github :repo "emacsmirror/bookmark-plus"))

Some windows specific stuff

(when *sys/windows*
  (remove-hook 'find-file-hook 'vc-refresh-state)
;  (progn
;    (setq gc-cons-threshold (* 511 1024 1024)
;          gc-cons-percentage 0.5
;          garbage-collection-messages t

;    (run-with-idle-timer 5 t #'garbage-collect))
  (when (boundp 'w32-pipe-read-delay)
    (setq w32-pipe-read-delay 0))
  (when (boundp 'w32-get-true-file-attributes)
    (setq w32-get-true-file-attributes nil)))

burly

Github Store window configuration and save them as a bookmark burly-bookmark-windows: bookmarks the current window layout

(use-package burly
  :ensure t
  :config
  (burly-tabs-mode) ;;open a burly window bookbark in a new tab
  )

recentf

Exclude some dirs from spamming recentf

(use-package recentf
  :ensure nil
;  :defer 1
  :config
  (recentf-mode)
  :custom
  (recentf-exclude '(".*-autoloads\\.el\\'"
                      "[/\\]\\elpa/"
                      "COMMIT_EDITMSG\\'"))
  (recentf-save-file (concat MY--PATH_USER_LOCAL "recentf"))
  (recentf-max-menu-items 600)
  (recentf-max-saved-items 600))

savehist

(use-package savehist
  :ensure nil
  :config
  (savehist-mode)
  :custom
  (savehist-file (concat MY--PATH_USER_LOCAL "history")))

undo

(use-package undo-tree
  :ensure t
  :diminish undo-tree-mode
  :init
  (global-undo-tree-mode 1)
  :custom
  (undo-tree-auto-save-history nil))

COMMENT ace-window (now avy)

(use-package ace-window
  :ensure t
  :bind
  (:map global-map
        ("C-x o" . ace-window)))

which-key

(use-package which-key
  :ensure t
  :diminish which-key-mode
  :custom
  (which-key-idle-delay 0.5)
  (which-key-sort-order 'which-key-description-order)
  :config
  (which-key-mode)
  (which-key-setup-side-window-bottom))

abbrev

(use-package abbrev
  :ensure nil
  :diminish abbrev-mode
  :hook
  ((text-mode org-mode) . abbrev-mode)
  :init
  (setq abbrev-file-name (concat MY--PATH_USER_GLOBAL "abbrev_tables.el"))
  :config
  (if (file-exists-p abbrev-file-name)
      (quietly-read-abbrev-file))
  (setq save-abbrevs 'silently))  ;; don't bother me with asking for abbrev saving

imenu-list

A minor mode to show imenu in a sidebar. Call imenu-list-smart-toggle. Source

(use-package imenu-list
  :ensure t
  :demand t  ; otherwise mode loads too late and won't work on first file it's being activated on
  :config
  (setq imenu-list-focus-after-activation t
        imenu-list-auto-resize t
        imenu-list-position 'right)
  :general
  ([f9] 'imenu-list-smart-toggle)
  (:states '(normal insert)
   :keymaps 'imenu-list-major-mode-map
   "RET" '(imenu-list-goto-entry    :which-key "goto")
   "TAB" '(hs-toggle-hiding         :which-key "collapse")
   "v"   '(imenu-list-display-entry :which-key "show")  ; also prevents visual mode
   "q"   '(imenu-list-quit-window   :which-key "quit"))
  :custom
  (org-imenu-depth 4))

COMMENT Evil

See also https://github.com/noctuid/evil-guide

Use C-z (evil-toggle-key) to switch between evil and emacs keybindings, in case evil is messing something up.

(use-package evil
  :ensure t
  :defer .1
  :custom
  (evil-want-C-i-jump nil)  ;; prevent evil from blocking TAB in org tree expanding
  (evil-want-integration t)
  (evil-want-keybinding nil)
  :config
  ;; example for using emacs default key map in a certain mode
  ;; (evil-set-initial-state 'dired-mode 'emacs)
  (evil-mode 1))

Eldoc

use builtin version

    (use-package eldoc
      :ensure nil
      :diminish eldoc-mode
      :defer t)

COMMENT Eldoc Box

Currently corfu-popupinfo displays eldoc in highlighted completion candidate. Maybe that's good enough.

(use-package eldoc-box
  :ensure t)

Meow

(use-package meow
  :ensure t
  :config
  (setq meow-cheatsheet-layout meow-cheatsheet-layout-qwerty)
  (meow-motion-overwrite-define-key
   '("j"          . meow-next)
   '("k"          . meow-prev)
   '("<escape>" . ignore))
  (meow-leader-define-key
   ;; SPC j/k will run the original command in MOTION state.
   '("j"        . "H-j")
   '("k"        . "H-k")
   ;; Use SPC (0-9) for digit arguments.
   '("1"        . meow-digit-argument)
   '("2"        . meow-digit-argument)
   '("3"        . meow-digit-argument)
   '("4"        . meow-digit-argument)
   '("5"        . meow-digit-argument)
   '("6"        . meow-digit-argument)
   '("7"        . meow-digit-argument)
   '("8"        . meow-digit-argument)
   '("9"        . meow-digit-argument)
   '("0"        . meow-digit-argument)
   '("/"        . meow-keypad-describe-key)
   '("?"        . meow-cheatsheet))
  (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)
   '("-"        . negative-argument)
   '(";" . meow-reverse)
   '(","        . meow-inner-of-thing)
   '("."        . meow-bounds-of-thing)
   '("["        . meow-beginning-of-thing)
   '("]"        . meow-end-of-thing)
   '("a"        . meow-append)
   '("A"        . meow-open-below)
   '("b"        . meow-back-word)
   '("B"        . meow-back-symbol)
   '("c"        . meow-change)
   '("d"        . meow-delete)
   '("D"        . meow-backward-delete)
   '("e"        . meow-next-word)
   '("E"        . meow-next-symbol)
   '("f"        . meow-find)
   '("g"        . meow-cancel-selection)
   '("G"        . meow-grab)
   '("h"        . meow-left)
   '("H"        . meow-left-expand)
   '("i"        . meow-insert)
   '("I"        . meow-open-above)
   '("j"        . meow-next)
   '("J"        . meow-next-expand)
   '("k"        . meow-prev)
   '("K"        . meow-prev-expand)
   '("l"        . meow-right)
   '("L"        . meow-right-expand)
   '("m"        . meow-join)
   '("n"        . meow-search)
   '("o"        . meow-block)
   '("O"        . meow-to-block)
   '("p"        . meow-yank)
   '("q"        . meow-quit)
   '("Q"        . meow-goto-line)
   '("r"        . meow-replace)
   '("R"        . meow-swap-grab)
   '("s"        . meow-kill)
   '("t"        . meow-till)
   '("u"        . meow-undo)
   '("U"        . meow-undo-in-selection)
   '("v"        . meow-visit)
   '("w"        . meow-mark-word)
   '("W"        . meow-mark-symbol)
   '("x"        . meow-line)
   '("X"        . meow-goto-line)
   '("y"        . meow-save)
   '("Y"        . meow-sync-grab)
   '("z"        . meow-pop-selection)
   '("'"        . repeat)
   '("<escape>" . ignore))
;  :config
  (meow-global-mode t))

avy

Search, move, copy, delete text within all visible buffers. Also replaces ace-window for buffer switching. https://github.com/abo-abo/avy

(use-package avy
  :ensure t
  :general
  (:prefix "M-s"
   ""    '(:ignore t           :which-key "avy")
   "w"   '(avy-goto-char-2     :which-key "avy-jump")
   "s"   '(avy-goto-char-timer :which-key "avy-timer")
   "c"   '(:ignore t           :which-key "avy copy")
   "c l" '(avy-copy-line       :which-key "avy copy line")
   "c r" '(avy-copy-region     :which-key "avy copy region")
   "m"   '(:ignore t           :which-key "avy move")
   "m l" '(avy-move-line       :which-key "avy move line")
   "m r" '(avy-move-region     :which-key "avy move region")))

Vertico

Vertico is a completion ui for the minibuffer and replaced selectrum. Vertico Github

;; completion ui
(use-package vertico
  :ensure t
  :init
  (vertico-mode))

Corfu

Completion ui, replaces company. Corfu Github

(use-package corfu
  :ensure t
  :after savehist
  :custom
  (corfu-popupinfo-delay t)
  (corfu-auto t)
  (corfu-cycle t)
  (corfu-auto-delay 0.3)
  (corfu-preselect-first nil)
  (corfu-popupinfo-delay '(1.0 . 0.0)) ;1s for first popup, instant for subsequent popups
  (corfu-popupinfo-max-width 70)
  (corfu-popupinfo-max-height 20)
  :init
  (global-corfu-mode)
;  (corfu-popupinfo-mode)  ; causes corfu window to stay
  (corfu-history-mode)
  ;; belongs to emacs
  (add-to-list 'savehist-additional-variables 'corfu-history)
  :hook
  (corfu-mode . corfu-popupinfo-mode))
;  :bind
;  (:map corfu-map
;        ("TAB" . corfu-next)
;        ("<C-return>" . corfu-insert)
;        ("C-TAB" . corfu-popupinfo-toggle)))

;; (general-define-key
;;  :states 'insert
;;  :definer 'minor-mode
;;  :keymaps 'completion-in-region-mode
;;  :predicate 'corfu-mode
;;  "C-d" 'corfu-info-documentation)

(use-package emacs
  :ensure nil
  :init
  ;; hide commands in M-x which do not apply to current mode
  (setq read-extended-command-predicate #'command-completion-default-include-p)
  ;; enable indentation + completion using TAB
  (setq tab-always-indent 'complete))

Cape

Adds completions for corfu Cape Github

Available functions: dabbrev, file, history, keyword, tex, sgml, rfc1345, abbrev, ispell, dict, symbol, line

(use-package cape
  :ensure t
  :bind
  (("C-c p p" . completion-at-point) ;; capf
   ("C-c p t" . complete-tag)   ;; etags
   ("C-c p d" . cape-dabbrev)
   ("C-c p h" . cape-history)
   ("C-c p f" . cape-file))
  :init
  (advice-add #'lsp-completion-at-point :around #'cape-wrap-noninterruptible)  ;; for performance issues with lsp
  (add-to-list 'completion-at-point-functions #'cape-dabbrev)
  (add-to-list 'completion-at-point-functions #'cape-file)
  (add-to-list 'completion-at-point-functions #'cape-history))

kind-icon

Make corfu pretty kind-icon Github

(use-package kind-icon
  :ensure t
  :after corfu
  :custom
  (kind-icon-default-face 'corfu-default)  ;; to compute blended backgrounds correctly
  :config
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter))

Orderless

Orderless Github Orderless orders the suggestions by recency. The package prescient orders by frequency.

(use-package orderless
  :ensure t
  :init
  (setq completion-styles '(orderless partial-completion basic)
        completion-category-defaults nil
        completion-category-overrides nil))
;        completion-category-overrides '((file (styles partial-completion)))))

Consult

Github Default preview key: M-.

(use-package consult
  :ensure t
  :bind
  (("C-x C-r" . consult-recent-file)
   ("C-x b" . consult-buffer)
   ("C-s" . consult-line)
   ("C-x r b" . consult-bookmark))  ;replace bookmark-jump
  :config
  ;; disable preview for some commands and buffers
  ;; and enable it by M-.
  ;; see https://github.com/minad/consult#use-package-example
  (consult-customize
   consult-theme :preview-key '(debounce 0.2 any)
   consult-ripgrep consult-git-grep consult-grep
   consult-bookmark consult-recent-file consult-xref
   consult--source-bookmark consult--source-file-register
   consult--source-recent-file consult--source-project-recent-file
   :preview-key '(:debounce 0.2 any)))

Marginalia

Github Adds additional information to the minibuffer

(use-package marginalia
  :ensure t
  :init
  (marginalia-mode)
  :bind
  (:map minibuffer-local-map
        ("M-A" . marginalia-cycle))
  :custom
  ;; switch by 'marginalia-cycle
  (marginalia-annotators '(marginalia-annotators-heavy
                           marginalia-annotators-light
                           nil)))

Embark

Does stuff in the minibuffer results

(use-package embark
  :ensure t
  :bind
  (("C-S-a" . embark-act)
   ("C-h B" . embark-bindings))
  :init
  (setq prefix-help-command #'embark-prefix-help-command)
  :config
  ;; hide modeline of the embark live/completions buffers
  (add-to-list 'display-buffer-alist
               '("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
                 nil
                 (window-parameters (mode-line-format . none)))))

(use-package embark-consult
  :ensure t
  :after (embark consult)
  :demand t
  :hook
  (embark-collect-mode . embark-consult-preview-minor-mode))

Tree-sitter

  (when *sys/linux*
    (use-package tree-sitter
      :ensure t
      :init
      (global-tree-sitter-mode t)
      :hook
      (tree-sitter-after-on . tree-sitter-hl-mode))

    (use-package tree-sitter-langs
      :ensure t
      :after tree-sitter)
  )

Org-ql

org-ql Run queries on org files

(use-package org-ql
  :ensure t
  )

COMMENT Xeft (needs xapian, not really windows compatible)

Fast full text search for stuff org-ql cannot cover

(use-package xeft
  :ensure t
  :custom
  (xeft-recursive 'follow-symlinks))

COMMENT Helm

As an alternative if I'm not happy with selectrum & co

(use-package helm
  :ensure t
  :hook
  (helm-mode . helm-autoresize-mode)
  ;; :bind
  ;; (("M-x" . helm-M-x)
  ;;  ("C-s" . helm-occur)
  ;;  ("C-x C-f" . helm-find-files)
  ;;  ("C-x C-b" . helm-buffers-list)
  ;;  ("C-x b" . helm-buffers-list)
  ;;  ("C-x C-r" . helm-recentf)
  ;;  ("C-x C-i" . helm-imenu))
  :config
  (helm-mode)
  :custom
  (helm-split-window-inside-p t)  ;; open helm buffer inside current window
  (helm-move-to-line-cycle-in-source t)
  (helm-echo-input-in-header-line t)
  (helm-autoresize-max-height 20)
  (helm-autoresize-min-height 5)
  )

Emails

Requires on system

  • isync to sync emails between host and local

  • notmuch to index emails

  • mu4e (if using mu4e)

;(use-package notmuch
;  :ensure t)
(use-package mu4e
  :if *sys/linux*
  :ensure nil
  :after (org))

COMMENT mu4e

;;https://github.com/progfolio/.emacs.d#mu4e
(use-package mu4e
  :ensure `(mu4e :host github :files ("mu4e/*.el" "build/mu4e/mu4e-meta.el" "build/mu4e/mu4e-config.el" "build/mu4e/mu4e.info") :repo "djcb/mu"
                 :main "mu4e/mu4e.el"
                 :pre-build (("./autogen.sh" "-Dtests=disabled")
                             ("ninja" "-C" "build")
                             (make-symbolic-link (expand-file-name "./build/mu/mu")
                                                 (expand-file-name "~/bin/mu") 'ok-if-exists))
                 :build (:not elpaca--compile-info)
                 :post-build (("mu" "init" "--quiet" "--maildir" ,(concat (getenv "HOME") "/Documents/emails")
;                               "--my-address=" ,secret-personal-email-address
;                               "--my-address=" ,secret-work-email-address)
;                               "--my-address=" ,secret-personal-email-address
                               "--my-address=marc.pohling@mail.de")
                              ("mu" "--quiet" "index")))
  :commands (mu4e mu4e-update-index))

outlook

In outlook a macro is necessary, also a reference to FM20.DLL (Microsoft Forms 2.0 Object Library, in c:\windows\syswow64\fm20.dll) The macro copies the GUID of the email to the clipboard Attention: the GUID changes when the email is moved to another folder!

The macro:

Sub AddLinkToMessageInClipboard()
'Adds a link to the currently selected message to the clipboard
   Dim objMail As Outlook.MailItem
   Dim doClipboard As New DataObject

   'One and ONLY one message muse be selected
   If Application.ActiveExplorer.Selection.Count <> 1 Then
       MsgBox ("Select one and ONLY one message.")
       Exit Sub
   End If

   Set objMail = Application.ActiveExplorer.Selection.Item(1)
   doClipboard.SetText "[[outlook:" + objMail.EntryID + "][MESSAGE: " + objMail.Subject + " (" + objMail.SenderName + ")]]"
   doClipboard.PutInClipboard
End Sub
;(org-add-link-type "outlook" 'my--org-outlook-open)
;(org-add-link-type "outlooknewmail" 'my--org-outlook-new-mail)

(defun my--org-outlook-open (id)
  (w32-shell-execute "open" "outlook" (concat " /select outlook:" id)))

(defun my--org-outlook-new-mail (receipients)
  ; separate RECEIPIENTS for main, cc and bcc by |
  (setq recp (split-string receipients "|"))
  (setq mailstring (concat " /c ipm.note /m " (nth 0 recp)))
  (if (nth 1 recp)
      ; this check has tp be separated because (and..) would stumble over a nil
      (if (not (equal "" (nth 1 recp)))
          (setq mailstring (concat mailstring "&cc=" (nth 1 recp)))))
  (if (nth 2 recp)
      (setq mailstring (concat mailstring "&bcc=" (nth 2 recp))))
  (w32-shell-execute "open" "outlook" mailstring))

(defun my/org-outlook-open-test ()
  (interactive)
  (w32-shell-execute "open" "outlook" " /select outlook:000000008A209C397CEF2C4FBA9E54AEB5B1F97F0700846D043B407C5B43A0C05AFC46DC5C630587BE5E020900006E48FF8F6027694BA6593777F542C19E0002A6434D000000"))'

misc

(use-package autorevert
  :diminish auto-revert-mode)

orgmode

some notes

copy file path within emacs

Enter dired-other-window place cursor on the file M-0 w (copy absolute path) C-u w (copy relative path)

Archiving

C-c C-x C-a

To keep the subheading structure when archiving, set the properties of the superheading.

* FOO
:PROPERTIES:
:ARCHIVE: %s_archive::* FOO
** DONE BAR
** TODO BAZ

When moving BAR to archive, it will go to FILENAME.org_archive below the heading FOO. Other examples

org

This seems necessary to prevent 'org is already installed' error https://github.com/jwiegley/use-package/issues/319

;(assq-delete-all 'org package--builtins)'
;(assq-delete-all 'org package--builtin-versions)
(defun my--buffer-prop-set (name value)
  "Set a file property called NAME to VALUE in buffer file.
If the property is already set, replace its value."
  (setq name (downcase name))
  (org-with-point-at 1
    (let ((case-fold-search t))
      (if (re-search-forward (concat "^#\\+" name ":\\(.*\\)")
                             (point-max) t)
          (replace-match (concat "#+" name ": " value) 'fixedcase)
        (while (and (not (eobp))
                    (looking-at "^[#:]"))
          (if (save-excursion (end-of-line) (eobp))
                        (progn
                          (end-of-line)
                          (insert "\n"))
                      (forward-line)
                      (beginning-of-line)))
                  (insert "#+" name ": " value "\n")))))

          (defun my--buffer-prop-remove (name)
            "Remove a buffer property called NAME."
            (org-with-point-at 1
              (when (re-search-forward (concat "\\(^#\\+" name ":.*\n?\\)")
                                       (point-max) t)
                (replace-match ""))))

(use-package org
  :ensure t
;  :pin gnu
  :mode (("\.org$" . org-mode))
  :diminish org-indent-mode
  :defer 1
  :hook
  (org-mode . org-indent-mode)
  (org-source-mode . smartparens-mode)
  :bind (("C-c l" . org-store-link)
         ("C-c c" . org-capture)
         ("C-c a" . org-agenda)
         :map org-mode-map ("S-<right>" . org-shiftright)
       ("S-<left>" . org-shiftleft))
  :init
  (defun my--org-agenda-files-set ()
    "Sets default agenda files.
Necessary when updating roam agenda todos."
    (setq org-agenda-files (list (concat MY--PATH_ORG_FILES "notes.org")
   (concat MY--PATH_ORG_FILES "projects.org")
   (concat MY--PATH_ORG_FILES "tasks.org")))
    (when *sys/linux*
      (nconc org-agenda-files
   (directory-files-recursively MY--PATH_ORG_FILES_MOBILE "\\.org$"))))
  (my--org-agenda-files-set)

  (defun my--org-skip-subtree-if-priority (priority)
      "Skip an agenda subtree if it has a  priority of PRIORITY.

       PRIORITY may be one of the characters ?A, ?B, or ?C."
      (let ((subtree-end (save-excursion (org-end-of-subtree t)))
  (pri-value (* 1000 (- org-lowest-priority priority)))
  (pri-current (org-get-priority (thing-at-point 'line t))))
        (if (= pri-value pri-current)
  subtree-end
nil)))
  :config
  (when *work_remote*
    (org-add-link-type "outlook" 'my--org-outlook-open)
    (org-add-link-type "outlooknewmail" 'my--org-outlook-new-mail)
    (setq org-todo-keywords
     '((sequence "OPEN" "TODO" "UNCLEAR" "|" "DONE" "IMPOSSIBLE" "CANCELLED")))
    (setq org-capture-templates
   '(("t" "telephone call" entry
;      (file+olp+datetree (concat MY--PATH_ORG_FILES "phone_calls.org"))
      (file+datetree "p:/Eigene Dateien/Notizen/phone_calls.org")
      "* [%<%Y-%m-%d %H:%M>] %?"
      :empty-lines 0 :jump-to-captured t))))
  (when *sys/linux*
    (setq org-pretty-entities t))
  :custom
  (org-startup-truncated t)
  (org-startup-align-all-tables t)
  (org-src-fontify-natively t) ;; use syntax highlighting in code blocks
  (org-src-preserve-indentation t) ;; no extra indentation
  (org-src-window-setup 'current-window) ;; C-c ' opens in current window
  (org-modules (quote (org-id
   org-habit
   org-tempo)))  ;; easy templates
  (org-default-notes-file (concat MY--PATH_ORG_FILES "notes.org"))
  (org-id-locations-file (concat MY--PATH_USER_LOCAL ".org-id-locations"))
  (org-log-into-drawer "LOGBOOK")
  (org-log-done 'time)  ;; create timestamp when task is done
  (org-blank-before-new-entry '((heading) (plain-list-item)))  ;; prevent new line before new item
  (org-src-tab-acts-natively t)
  ;;Sort agenda by deadline and priority
      (org-agenda-sorting-strategy
       (quote
        ((agenda deadline-up priority-down)
         (todo priority-down category-keep)
         (tags priority-down category-keep)
         (search category-keep))))
  (org-agenda-custom-commands
   '(("c" "Simple agenda view"
      ((tags "PRIORITY=\"A\""
   ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
    (org-agenda-overriding-header "Hohe Priorität:")))
       (agenda ""
   ((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
    (org-agenda-span 7)
    (org-agenda-start-on-weekday nil)
    (org-agenda-overriding-header "Nächste 7 Tage:")))
       (alltodo ""
   ((org-agenda-skip-function '(or (my--org-skip-subtree-if-priority ?A)
     (org-agenda-skip-if nil '(scheduled deadline))))
    (org-agenda-overriding-header "Sonstige Aufgaben:"))))))))

COMMENT languages

Set some languages and disable confirmation for evaluating code blocks C-c C-c

Elpaca cant find it, though it's built in org


  (use-package ob-python
  ;  :ensure nil
    :defer t
    :after org
  ;  :ensure org-contrib
    :commands
    (org-babel-execute:python))

COMMENT habits

(require 'org-habit)  ;;TODO Lösung ohne require finden, scheint mir nicht ideal zu sein, nur um ein org-modul zu aktivieren
;;  (add-to-list 'org-modules "org-habit")
(setq org-habit-graph-column 80
     org-habit-preceding-days 30
      org-habit-following-days 7
      org-habit-show-habits-only-for-today nil)

COMMENT journal

Source

Ggf. durch org-roam-journal ersetzen

(use-package org-journal
  :if *sys/linux*
  :ensure t
  :defer t
  :config
  ;; feels hacky, but this way compiler error "assignment to free variable" disappears
  (when (and (boundp 'org-journal-dir)
             (boundp 'org-journal-enable-agenda-integration))
    (setq org-journal-dir MY--PATH_ORG_JOURNAl
          org-journal-enable-agenda-integration t)))

org-roam

Github Um Headings innerhalb einer Datei zu verlinken:

  • org-id-get-create im Heading,

  • org-roam-node-insert in der verweisenden Datei

Bei Problemen wie unique constraint org-roam-db-clear-all org-roam-db-sync

(use-package emacsql-sqlite-builtin
  :ensure t)

(use-package org-roam
    :requires emacsql-sqlite-builtin
    :ensure t
    :defer 2
    :after org
    :init
    (setq org-roam-v2-ack t)

    (defun my--roamtodo-p ()
      "Return non-nil if current buffer has any todo entry.

TODO entries marked as done are ignored, meaning this function
returns nil if current buffer contains only completed tasks."
      (seq-find
       (lambda (type)
         (eq type 'todo))
       (org-element-map
           (org-element-parse-buffer 'headline)
           'headline
         (lambda (h)
           (org-element-property :todo-type h)))))

    (defun my--roamtodo-update-tag ()
      "Update ROAMTODO tag in the current buffer."
      (when (and (not (active-minibuffer-window))
                 (my--buffer-roam-note-p))
        (save-excursion
          (goto-char (point-min))
          (let* ((tags (my--buffer-tags-get))
                 (original-tags tags))
            (if (my--roamtodo-p)
                (setq tags (cons "roamtodo" tags))
              (setq tags (remove "roamtodo" tags)))

            ;;cleanup duplicates
            (when (or (seq-difference tags original-tags)
                      (seq-difference original-tags tags))
              (apply #'my--buffer-tags-set tags))))))

    (defun my--buffer-tags-get ()
      "Return filetags value in current buffer."
      (my--buffer-prop-get-list "filetags" "[ :]"))

    (defun my--buffer-tags-set (&rest tags)
      "Set TAGS in current buffer.
If filetags value is already set, replace it."
      (if tags
          (my--buffer-prop-set
           "filetags" (concat ":" (string-join tags ":") ":"))
        (my--buffer-prop-remove "filetags")))
    
    (defun my--buffer-tags-add (tag)
      "Add a TAG to filetags in current buffer."
      (let* ((tags (my--buffer-tags-get))
             (tags (append tags (list tag))))
        (apply #'my--buffer-tags-set tags)))
    
    (defun my--buffer-tags-remove (tag)
      "Remove a TAG from filetags in current buffer."
      (let* ((tags (my--buffer-tags-get))
             (tags (delete tag tags)))
        (apply #'my--buffer-tags-set tags)))
    
    (defun my--buffer-prop-set (name value)
      "Set a file property called NAME to VALUE in buffer file.
If the property is already set, replace its value."
      (setq name (downcase name))
      (org-with-point-at 1
        (let ((case-fold-search t))
          (if (re-search-forward (concat "^#\\+" name ":\\(.*\\)")
                                 (point-max) t)
              (replace-match (concat "#+" name ": " value) 'fixedcase)
            (while (and (not (eobp))
                        (looking-at "^[#:]"))
              (if (save-excursion (end-of-line) (eobp))
                  (progn
                    (end-of-line)
                    (insert "\n"))
                (forward-line)
                (beginning-of-line)))
            (insert "#+" name ": " value "\n")))))

    (defun my--buffer-prop-set-list (name values &optional separators)
      "Set a file property called NAME to VALUES in current buffer.
VALUES are quoted and combined into single string using
`combine-and-quote-strings'.
If SEPARATORS is non-nil, it should be a regular expression
matching text that separates, but is not part of, the substrings.
If nil it defaults to `split-string-and-unquote', normally
\"[ \f\t\n\r\v]+\", and OMIT-NULLS is forced to t.
If the property is already set, replace its value."
      (my--buffer-prop-set
       name (combine-and-quote-strings values separators)))
    
    (defun my--buffer-prop-get (name)
      "Get a buffer property called NAME as a string."
      (org-with-point-at 1
        (when (re-search-forward (concat "^#\\+" name ": \\(.*\\)")
                                 (point-max) t)
          (buffer-substring-no-properties
           (match-beginning 1)
           (match-end 1)))))
    
    (defun my--buffer-prop-get-list (name &optional separators)
      "Get a buffer property NAME as a list using SEPARATORS.
If SEPARATORS is non-nil, it should be a regular expression
matching text that separates, but is not part of, the substrings.
If nil it defaults to `split-string-default-separators', normally
\"[ \f\t\n\r\v]+\", and OMIT-NULLS is forced to t."
      (let ((value (my--buffer-prop-get name)))
        (when (and value (not (string-empty-p value)))
          (split-string-and-unquote value separators))))
    
    (defun my--buffer-prop-remove (name)
      "Remove a buffer property called NAME."
      (org-with-point-at 1
        (when (re-search-forward (concat "\\(^#\\+" name ":.*\n?\\)")
                                 (point-max) t)
          (replace-match ""))))

    (defun my--buffer-roam-note-p ()
      "Return non-nil if the currently visited buffer is a note."
      (and buffer-file-name
           (string-prefix-p
            (expand-file-name (file-name-as-directory MY--PATH_ORG_ROAM)) 
            (file-name-directory buffer-file-name))))

    (defun my--org-roam-filter-by-tag (tag-name)
      (lambda (node)
        (member tag-name (org-roam-node-tags node))))

    (defun my--org-roam-list-notes-by-tag (tag-name)
      (mapcar #'org-roam-node-file
              (seq-filter
               (my--org-roam-filter-by-tag tag-name)
               (org-roam-node-list))))

    (defun my/org-roam-refresh-agenda-list ()
      "Add all org roam files with #+filetags: roamtodo"
      (interactive)
      (my--org-agenda-files-set)
      (nconc org-agenda-files
             (my--org-roam-list-notes-by-tag "roamtodo"))
      (setq org-agenda-files (delete-dups org-agenda-files)))
    
    (add-hook 'find-file-hook #'my--roamtodo-update-tag)
    (add-hook 'before-save-hook #'my--roamtodo-update-tag)

    (advice-add 'org-agenda :before #'my/org-roam-refresh-agenda-list)
    (advice-add 'org-todo-list :before #'my/org-roam-refresh-agenda-list)

    (add-to-list 'org-tags-exclude-from-inheritance "roamtodo")
    :config
    (require 'org-roam-dailies) ;; ensure the keymap is available
    (org-roam-db-autosync-mode)
    ;; build the agenda list the first ime for the session
    (my/org-roam-refresh-agenda-list)

    (when *work_remote*
      (setq org-roam-capture-templates
       '(("n" "note" plain
          "%?"
          :if-new (file+head "notes/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
          :unnarrowed t)
         ("i" "idea" plain
          "%?"
          :if-new (file+head "ideas/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
          :unnarrowed t)
        ("p" "project" plain
         "%?"
         :target (file+head "projects/${slug}.org" "#+title: ${title}\n#+filetags: :project:\n")
         :unnarrowed t)
        ("s" "Sicherheitenmeldung" plain
         "*** TODO [#A] Sicherheitenmeldung ${title}\n :PROPERTIES:\n :ID: %(org-id-uuid)\n:END:\n%u\n"
         :target (file+olp "tasks.org" ("Todos" "Sicherheitenmeldungen")))
        ("m" "Monatsbericht" plain
         "*** TODO [#A] Monatsbericht ${title}\n :PROPERTIES:\n :ID: %(org-id-uuid)\n:END:\n%u\n"
         :target (file+olp "tasks.org" ("Todos" "Monatsberichte"))))))
    :custom
    (org-roam-database-connector 'sqlite-builtin)
    (org-roam-directory MY--PATH_ORG_ROAM)
    (org-roam-completion-everywhere t)
    (org-roam-capture-templates
     '(("n" "note" plain
        "%?"
        :if-new (file+head "notes/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
        :unnarrowed t)
       ("i" "idea" plain
        "%?"
        :if-new (file+head "ideas/%<%Y%m%d%H%M%S>-${slug}.org" "#+title: ${title}\n")
        :unnarrowed t)
        ))
    :bind (("C-c n l" . org-roam-buffer-toggle)
           ("C-c n f" . org-roam-node-find)
           ("C-c n i" . org-roam-node-insert)
           :map org-mode-map
           ("C-M-i" . completion-at-point)
           :map org-roam-dailies-map
           ("Y" . org-roam-dailies-capture-yesterday)
           ("T" . org-roam-dailies-capture-tomorrow))
    :bind-keymap
    ("C-c n d" . org-roam-dailies-map))

TODO Verzeichnis außerhalb roam zum Archivieren (u.a. für erledigte Monatsmeldungen etc.)

Programming

Magit / Git

Little crash course in magit:

  • magit-init to init a git project

  • magit-status (C-x g) to call the status window

In status buffer:

  • s stage files

  • u unstage files

  • U unstage all files

  • a apply changes to staging

  • c c commit (type commit message, then C-c C-c to commit)

  • b b switch to another branch

  • P u git push

  • F u git pull

;; updated version needed for magit, at least on windows
(use-package transient
  :ensure t)

(use-package magit
  :ensure t
 ; :pin melpa-stable
  :defer t
  :init
                                        ; set git-path in work environment
  (if (string-equal user-login-name "POH")
      (setq magit-git-executable "P:/Tools/Git/bin/git.exe")
    )
  :bind (("C-x g" . magit-status)))

COMMENT Eglot (can't do dap-mode, maybe dape?)

for python pyls (in env: pip install python-language-server) seems to work better than pyright (npm install -g pyright), at least pandas couldnt be resolved in pyright

(use-package eglot
  :ensure t
  :init
  (setq completion-category-overrides '((eglot (styles orderless))))
  :config
  (add-to-list 'eglot-server-programs '(python-mode . ("pyright-langserver" "--stdio")))
  (with-eval-after-load 'eglot
    (load-library "project"))
  :hook
  (python-mode . eglot-ensure)
  :custom
  (eglot-ignored-server-capabilities '(:documentHighlightProvider))
  (eglot-autoshutdown t)
  (eglot-events-buffer-size 0)
  )

;; performance stuff if necessary
;(fset #'jsonrpc--log-event #'ignore)

LSP-Mode

(defun corfu-lsp-setup ()
  (setf (alist-get 'styles (alist-get 'lsp-capf completion-category-defaults))
        '(orderless)))

(use-package lsp-mode
  :ensure t
;  :hook
;  ((python-mode . lsp))
  :custom
  (lsp-completion-provider :none)
  (lsp-enable-suggest-server-download nil)
  :hook
  (lsp-completion-mode #'corfu-lsp-setup))

;(use-package lsp-ui
;  :ensure t
;  :commands lsp-ui-mode)

(use-package lsp-pyright
  :ensure t
  :after (python lsp-mode)
  :custom
  (lsp-pyright-multi-root nil)
  :hook
  (python-mode-hook . (lambda ()
                        (require 'lsp-pyright) (lsp))))

flymake

python in venv: pip install pyflake (or ruff?) TODO: if ruff active, sideline stops working

(setq python-flymake-command '("ruff" "--quiet" "--stdin-filename=stdin" "-"))

sideline

show flymake errors on the right of code window

(use-package sideline
  :ensure t)

(use-package sideline-flymake
  :ensure t
  :requires sideline
  :hook
  (flymake-mode . sideline-mode)
  :init
  (setq sideline-flymake-display-mode 'line ; 'point or 'line
;        sideline-backends-left '(sideline-lsp)
        sideline-backends-right '(sideline-flymake)))

yasnippet

For useful snippet either install yasnippet-snippets or get them from here Github

(use-package yasnippet
  :ensure t
  :defer t
  :diminish yas-minor-mode
  :config
  (setq yas-snippet-dirs (list (concat MY--PATH_USER_GLOBAL "snippets")))
  (yas-global-mode t)
  (yas-reload-all)
  (unbind-key "TAB" yas-minor-mode-map)
  (unbind-key "<tab>" yas-minor-mode-map))

hippie expand

With hippie expand I am able to use yasnippet and emmet at the same time with the same key.

(use-package hippie-exp
  :ensure nil
  :defer t
  :bind
  ("C-<return>" . hippie-expand)
  :config
  (setq hippie-expand-try-functions-list
        '(yas-hippie-try-expand emmet-expand-line)))

COMMENT flycheck (now flymake)

(use-package flycheck
  :ensure t
  :hook
  ((css-mode . flycheck-mode)
   (emacs-lisp-mode . flycheck-mode)
   (python-mode . flycheck-mode))
  :defer 1.0
  :init
  (setq flycheck-emacs-lisp-load-path 'inherit)
  :config
  (setq-default
   flycheck-check-synta-automatically '(save mode-enabled)
   flycheck-disable-checkers '(emacs-lisp-checkdoc)
   eldoc-idle-delay .1                ;; let eldoc echo faster than flycheck
   flycheck-display-errors-delay .3)) ;; this way any errors will override eldoc messages

smartparens

(use-package smartparens
  :ensure t
  :diminish smartparens-mode
  :bind
  (:map smartparens-mode-map
        ("C-M-f"           . sp-forward-sexp)
        ("C-M-b"           . sp-backward-sexp)
        ("C-M-a"           . sp-backward-down-sexp)
        ("C-M-e"           . sp-up-sexp)
        ("C-M-w"           . sp-copy-sexp)
        ("M-k"             . sp-kill-sexp)
        ("C-M-<backspace>" . sp-slice-sexp-killing-backward)
        ("C-S-<backspace>" . sp-slice-sexp-killing-around)
        ("C-]"             . sp-select-next-thing-exchange))
  :config
  (setq sp-show-pair-from-inside nil
        sp-escape-quotes-after-insert nil)
  (require 'smartparens-config))

lisp

(use-package elisp-mode
  :ensure nil
  :defer t)

web

apt install npm sudo npm install -g vscode-html-languageserver-bin evtl alternativ typescript-language-server?

Unter Windows: Hier runterladen: https://nodejs.org/dist/latest/ und in ein Verzeichnis entpacken. Optional: PATH erweitern unter Windows (so kann exec-path-from-shell den Pfad ermitteln): PATH=P:\path→\node;%path%

web-mode

(use-package web-mode
 :ensure t
 :defer t
 :mode
 ("\\.phtml\\'"
  "\\.tpl\\.php\\'"
  "\\.djhtml\\'"
  "\\.[t]?html?\\'")
 :hook
 (web-mode . smartparens-mode)
 :init
 (if *work_remote*
     (setq exec-path (append exec-path '("P:/Tools/node"))))
 :config
 (setq web-mode-enable-auto-closing t
       web-mode-enable-auto-pairing t))

Emmet offers snippets, similar to yasnippet. Default completion is C-j Github

(use-package emmet-mode
  :ensure t
  :defer t
  :hook
  ((web-mode . emmet-mode)
   (css-mode . emmet-mode))
  :config
  (unbind-key "C-<return>" emmet-mode-keymap))

JavaScript

npm install -g typescript-language-server typescript maybe only typescript? npm install -g prettier

(use-package rjsx-mode
  :ensure t
  :mode ("\\.js\\'"
         "\\.jsx'"))
;  :config
;  (setq js2-mode-show-parse-errors nil
;        js2-mode-show-strict-warnings nil
;        js2-basic-offset 2
;        js-indent-level 2)
;  (setq-local flycheck-disabled-checkers (cl-union flycheck-disable-checkers
;                                                   '(javascript-jshint))))  ; jshint doesn"t work for JSX

(use-package tide
  :ensure t
  :after (rjsx-mode company flycheck)
;  :hook (rjsx-mode . setup-tide-mode)
  :config
  (defun setup-tide-mode ()
    "Setup function for tide."
    (interactive)
    (tide-setup)
    (flycheck-mode t)
    (setq flycheck-check-synta-automatically '(save mode-enabled))
    (tide-hl-identifier-mode t)))

;; needs npm install -g prettier
(use-package prettier-js
  :ensure t
  :after (rjsx-mode)
  :defer t
  :diminish prettier-js-mode
  :hook ((js2-mode rsjx-mode) . prettier-js-mode))

YAML

(use-package yaml-mode
  :if *sys/linux*
  :ensure t
  :defer t
  :mode ("\\.yml$" . yaml-mode))

R

(use-package ess
  :ensure t
  :defer t
  :init
  (if *work_remote*
      (setq exec-path (append exec-path '("P:/Tools/R/bin/x64"))
            org-babel-R-command "P:/Tools/R/bin/x64/R --slave --no-save")))

project.el

(use-package project
  :custom
  (project-vc-extra-root-markers '(".project.el" ".project" )))

Python

Preparations:

  • Install language server in each projects venv source ./bin/activate pip install pyright

  • in project root: touch .project.el echo "((nil . (pyvenv-activate . "path/to/project.env")))" >> .dir-locals.el

    für andere language servers https://github.com/emacs-lsp/lsp-mode#install-language-server

    TODO if in a project, set venv automatically (when-let ((project (project-current))) (project-root project)) returns project path from project.el to recognize a project, either have git or place a .project.el file in project root and (setq project-vc-extra-root-markers '(".project.el" "…" ))

    (use-package python
     :if *sys/linux*
     :delight "π "
     :defer t
     :bind (("M-[" . python-nav-backward-block)
            ("M-]" . python-nav-forward-block))
     :mode
     (("\\.py\\'" . python-mode)))
    
    (use-package pyvenv
    ;  :if *sys/linux*
     :ensure t
     :defer t
     :after python
     :hook
     (python-mode . pyvenv-mode)
     :custom
     (pyvenv-default-virtual-env-name ".env")
     (pyvenv-mode-line-indicator '(pyvenv-virtual-env-name ("[venv:" pyvenv-virtual-env-name "]"))))
    
    
    ;; formatting to pep8
    ;; requires pip install black
    ;(use-package blacken
    ;  :ensure t)
    

TODO python mode hook:

  • activate venv

  • activate eglot with proper ls

  • activate tree-sitter?

  • have some fallback if activations fail

beancount

Installation

sudo su
cd /opt
python3 -m venv beancount
source ./beancount/bin/activate
pip3 install wheel
pip3 install beancount
sleep 100
echo "shell running!"
deactivate
(use-package beancount
  :ensure nil
  :if *sys/linux*
  :load-path "user-global/elisp/"
;  :ensure t
  :defer t
  :mode
  ("\\.beancount$" . beancount-mode)
  :hook
  (beancount-mode . my/beancount-company)
  :config
  (defun my/beancount-company ()
    (setq-local completion-at-point-functions #'beancount-completion-at-point))
  (setq beancount-filename-main "/home/marc/Archiv/Finanzen/Transaktionen/transactions.beancount"))

+BEGIN_SRC emacs-lisp (use-package beancount :if sys/linux :load-path "user-global/elisp" ; :ensure t :defer t :mode ("\\.beancount$" . beancount-mode) ; :hook ; (beancount-mode . my/beancount-company) ; :init ; (add-hook 'beancount-mode-hook 'company/beancount-mode-hook) :config (defun my/beancount-company () (setq-local completion-at-point-functions #'beancount-complete-at-point nil t)) ; (mapcar #'cape-company-to-capf ; (list #'company-beancount #'company-dabbrev)))) (defun my–beancount-companyALT () (set (make-local-variable 'company-backends) '(company-beancount))) (setq beancount-filename-main "/home/marc/Archiv/Finanzen/Transaktionen/transactions.beancount")) +END_SRC

To support org-babel, check if it can find the symlink to ob-beancount.el

orgpath=`find /home/marc/.emacs.d/elpa/ -type d -name "org-plus*" -print`
beansym="$orgpath/ob-beancount.el
  bean="/home/marc/Archiv/Programmierprojekte/Lisp/beancount-mode/ob-beancount.el"

  if [ -h "$beansym" ]
  then
    echo "$beansym found"
  elif [ -e "$bean" ]
  then
    echo "creating symlink"
    ln -s "$bean" "$beansym"
  else
    echo "$bean not found, symlink creation aborted"
  fi

Fava is strongly recommended.

cd /opt
python3 -m venv fava
source ./fava/bin/activate
pip3 install wheel
pip3 install fava
deactivate

Start fava with fava my_file.beancount

It is accessable on this URL: Fava Beancount-mode can start fava and open the URL right away.

Stuff after everything else

Set garbage collector to a smaller value to let it kick in faster. Maybe a problem on Windows?

;(setq gc-cons-threshold (* 2 1000 1000))

Rest of early-init.el

(defconst config-org (expand-file-name "config.org" user-emacs-directory))
(defconst init-el (expand-file-name "init.el" user-emacs-directory))

(unless (file-exists-p init-el)
  (require 'org)
  (org-babel-tangle-file config-org init-el))