#+TITLE: Emacs configuration file #+AUTHOR: Marc #+BABEL: :cache yes #+PROPERTY: header-args :tangle init.el #+OPTIONS: ^:nil * 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 #+begin_src emacs-lisp ;;; init.el --- -*- lexical-binding: t -*- #+end_src * First start These functions updates config.el whenever changes in config.org are made. The update will be active after saving. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC 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/ #+begin_src emacs-lisp (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)) #+end_src * Default settings ** paths #+BEGIN_SRC emacs-lisp (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?") #+END_SRC #+BEGIN_SRC emacs-lisp (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"))) #+end_src ** Browser #+begin_src emacs-lisp (setq browse-url-function 'browse-url-generic browse-url-generic-program "firefox") #+end_src* Package Management ** Elpaca Boilerplate for Elpaca #+begin_src emacs-lisp (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) #+end_src stolen from https://github.com/progfolio/.emacs.d/blob/cff07d4454d327a4df1915a2cdf8ac6bc5dfde23/init.org?plain=1#L276 usage e.g. here. Not sure though what the benefits are https://github.com/justinbarclay/.emacs.d #+begin_src emacs-lisp (defmacro use-feature (name &rest args) "Like `use-package' but accounting for asynchronous installation. NAME and ARGS are in `use-package'." (declare (indent defun)) `(use-package ,name :ensure nil ,@args)) #+end_src * use-package keywords general / diminish Needs to be loaded before any other package which uses the :general keyword #+BEGIN_SRC emacs-lisp (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) #+END_SRC * sane defaults #+begin_src emacs-lisp (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 #+END_SRC * 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. #+BEGIN_SRC emacs-lisp :tangle early-init.el (setq gc-cons-threshold most-positive-fixnum) #+END_SRC Restore it to reasonable value after init. Also stop garbage collection during minibuffer interaction (helm etc.) #+begin_src emacs-lisp (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 #+end_src ** File Handler #+begin_src emacs-lisp :tangle early-init.el (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) #+end_src ** Others #+begin_src emacs-lisp :tangle early-init.el ;; 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) #+end_src * Appearance ** Defaults #+begin_src emacs-lisp (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) #+end_src ** Remove redundant UI #+begin_src emacs-lisp :tangle early-init.el (menu-bar-mode -1) ;; disable menu bar (tool-bar-mode -1) ;; disable tool bar (scroll-bar-mode -1) ;; disable scroll bar #+end_src ** Font #+BEGIN_SRC emacs-lisp (when *sys/linux* (set-face-font 'default "Hack-10")) (when *work_remote* (set-face-font 'default "Lucida Sans Typewriter-11")) #+END_SRC ** Themes #+BEGIN_SRC emacs-lisp (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) #+END_SRC Windows Theme: #+BEGIN_SRC emacs-lisp (when *sys/windows* (mapcar #'disable-theme custom-enabled-themes) (load-theme 'tango)) (when *sys/linux* (mapcar #'disable-theme custom-enabled-themes) (load-theme 'plastic)) #+END_SRC ** line wrappings #+BEGIN_SRC emacs-lisp (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))) #+END_SRC ** line numbers #+BEGIN_SRC emacs-lisp (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)) #+END_SRC ** misc Delight can replace mode names with custom names , e.g. python-mode with just "π ". #+BEGIN_SRC emacs-lisp (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)) #+END_SRC * dired #+begin_src emacs-lisp (use-package dired :ensure nil :custom (dired-kill-when-opening-new-dired-buffer t)) #+end_src * 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 #+begin_src emacs-lisp (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")) #+end_src Some windows specific stuff #+BEGIN_SRC emacs-lisp (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))) #+END_SRC * burly [[https://github.com/alphapapa/burly.el][Github]] Store window configuration and save them as a bookmark burly-bookmark-windows: bookmarks the current window layout #+begin_src emacs-lisp (use-package burly :ensure t :config (burly-tabs-mode) ;;open a burly window bookbark in a new tab ) #+end_src * recentf Exclude some dirs from spamming recentf #+begin_src emacs-lisp (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)) #+end_src * savehist #+begin_src emacs-lisp (use-package savehist :ensure nil :config (savehist-mode) :custom (savehist-file (concat MY--PATH_USER_LOCAL "history"))) #+end_src * undo #+BEGIN_SRC emacs-lisp (use-package undo-tree :ensure t :diminish undo-tree-mode :init (global-undo-tree-mode 1) :custom (undo-tree-auto-save-history nil)) #+END_SRC * COMMENT ace-window (now avy) #+begin_src emacs-lisp (use-package ace-window :ensure t :bind (:map global-map ("C-x o" . ace-window))) #+end_src * which-key #+BEGIN_SRC emacs-lisp (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)) #+END_SRC * abbrev #+begin_src emacs-lisp (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 #+end_src * imenu-list A minor mode to show imenu in a sidebar. Call imenu-list-smart-toggle. [[https://github.com/bmag/imenu-list][Source]] #+BEGIN_SRC emacs-lisp (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)) #+END_SRC * 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. #+BEGIN_SRC emacs-lisp (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)) #+END_SRC * Eldoc use builtin version #+begin_src emacs-lisp (use-package eldoc :ensure nil :diminish eldoc-mode :defer t) #+end_src * COMMENT Eldoc Box Currently corfu-popupinfo displays eldoc in highlighted completion candidate. Maybe that's good enough. #+begin_src emacs-lisp (use-package eldoc-box :ensure t) #+end_src * Meow #+begin_src emacs-lisp (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) '("" . 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) '("" . ignore)) ; :config (meow-global-mode t)) #+end_src * avy Search, move, copy, delete text within all visible buffers. Also replaces ace-window for buffer switching. [[https://github.com/abo-abo/avy]] #+BEGIN_SRC emacs-lisp (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"))) #+END_SRC * Vertico Vertico is a completion ui for the minibuffer and replaced selectrum. [[https://github.com/minad/vertico][Vertico Github]] #+begin_src emacs-lisp ;; completion ui (use-package vertico :ensure t :init (vertico-mode)) #+end_src * Corfu Completion ui, replaces company. [[https://github.com/minad/corfu][Corfu Github]] #+begin_src emacs-lisp (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) ; ("" . 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)) #+end_src * Cape Adds completions for corfu [[https://github.com/minad/cape][Cape Github]] Available functions: dabbrev, file, history, keyword, tex, sgml, rfc1345, abbrev, ispell, dict, symbol, line #+begin_src emacs-lisp (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)) #+end_src * kind-icon Make corfu pretty [[https://github.com/jdtsmith/kind-icon][kind-icon Github]] #+begin_src emacs-lisp (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)) #+end_src * Orderless [[https://github.com/oantolin/orderless][Orderless Github]] Orderless orders the suggestions by recency. The package prescient orders by frequency. #+begin_src emacs-lisp (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))))) #+end_src * Consult [[https://github.com/minad/consult][Github]] Default preview key: M-. #+begin_src emacs-lisp (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))) #+end_src * Marginalia [[https://github.com/minad/marginalia/][Github]] Adds additional information to the minibuffer #+begin_src emacs-lisp (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))) #+end_src * Embark Does stuff in the minibuffer results #+begin_src emacs-lisp (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)) #+end_src * Tree-sitter #+begin_src emacs-lisp (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) ) #+end_src * Org-ql [[https://github.com/alphapapa/org-ql][org-ql]] Run queries on org files #+begin_src emacs-lisp (use-package org-ql :ensure t ) #+end_src * COMMENT Xeft (needs xapian, not really windows compatible) Fast full text search for stuff org-ql cannot cover #+begin_src emacs-lisp (use-package xeft :ensure t :custom (xeft-recursive 'follow-symlinks)) #+end_src * COMMENT Helm As an alternative if I'm not happy with selectrum & co #+begin_src emacs-lisp (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) ) #+end_src * Emails Requires on system - isync to sync emails between host and local - mu / mu4e4e (apt nstall mu4e/bookworm-backports) After installing mu4e it needs to be initialized #+begin_src shell :tangle no mu init --maildir=/path/to/mail/folder --my-address=mu@adress.com --my-address=another@adress.com... mu index #+end_src #+begin_src emacs-lisp ;(use-package notmuch ; :ensure t) (use-feature mu4e :if *sys/linux* ; :ensure nil :after (org) :init (require 'mu4e) :config ;; this is set to 't' to avoid mail syncing issues when using mbsync (setq mu4e-change-filenames-when-moving t) (setq mu4e-update-interval (* 10 60)) (setq mu4e-get-mail-command "mbsync -a") (setq mu4e-maildir "/mnt/archiv/Dokumente/email") (setq mu4e-headers-fields '((:human-date . 12) (:flags . 6) (:from . 22) (:to . 25) (:subject))) (setq mu4e-contexts `( ,(make-mu4e-context :name "mail.de" :enter-func (lambda () (mu4e-message "switch to mail.de context")) :match-func (lambda (msg) (when msg (string-match-p "^/mailde" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "marc.pohling@mail.de") (user-full-name . "Marc Pohling") (mu4e-drafts-folder . "/mailde/drafts") (mu4e-sent-folder . "/mailde/sent") (mu4e-refile-folder . "/mailde/archive") (mu4e-trash-folder . "/mailde/trash"))) ,(make-mu4e-context :name "web.de" :enter-func (lambda () (mu4e-message "switch to web.de context")) :match-func (lambda (msg) (when msg (string-match-p "^/mailde" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "marc.pohling@web.de") (user-full-name . "Marc Pohling") (mu4e-drafts-folder . "/webde/drafts") (mu4e-sent-folder . "/webde/sent") (mu4e-refile-folder . "/webde/archive") (mu4e-trash-folder . "/webde/trash"))) ,(make-mu4e-context :name "gmail loui" :enter-func (lambda () (mu4e-message "switch to gmail loui context")) :match-func (lambda (msg) (when msg (string-match-p "^/gmailloui" (mu4e-message-field msg :maildir)))) :vars '((user-mail-address . "louithelou1@gmail.com") (user-full-name . "Loui") (mu4e-drafts-folder . "/gmailloui/drafts") (mu4e-sent-folder . "/gmailloui/sent") (mu4e-refile-folder . "/gmailloui/archive") (mu4e-trash-folder . "/gmailloui/trash"))))) ) #+end_src mu4e-dashboard https://github.com/rougier/mu4e-dashboard #+begin_src emacs-lisp (push 'mu4e elpaca-ignored-dependencies) (use-package mu4e-dashboard :if *sys/linux* :ensure (:host github :repo "rougier/mu4e-dashboard") :after mu4e :hook (mu4e-dashboard-mode . (lambda () (display-line-numbers-mode -1))) :custom (mu4e-dashboard-file (concat MY--PATH_USER_GLOBAL "mu4e-dashboard.org")) :config ; (require 'mu4e) (defun mu4e-dashboard-edit () (interactive) (let ((edit-buffer "*edit-mu4e-dashboard*")) (when (get-buffer edit-buffer) (kill-buffer (get-buffer edit-buffer))) (make-indirect-buffer (current-buffer) edit-buffer) (switch-to-buffer-other-window (get-buffer edit-buffer)) (org-mode 1))) (display-line-numbers-mode -1) (flyspell-mode -1)) #+end_src * 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: #+BEGIN_SRC 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 #+END_SRC #+BEGIN_SRC emacs-lisp ;(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"))' #+END_SRC * misc #+begin_src emacs-lisp (use-package autorevert :diminish auto-revert-mode) #+end_src * 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. #+begin_src org :tangle no ,* FOO :PROPERTIES: :ARCHIVE: %s_archive::* FOO ,** DONE BAR ,** TODO BAZ #+end_src When moving BAR to archive, it will go to FILENAME.org_archive below the heading FOO. [[http://doc.endlessparentheses.com/Var/org-archive-location.html][Other examples]] ** org This seems necessary to prevent 'org is already installed' error https://github.com/jwiegley/use-package/issues/319 #+begin_src emacs-lisp ;(assq-delete-all 'org package--builtins)' ;(assq-delete-all 'org package--builtin-versions) #+end_src #+BEGIN_SRC emacs-lisp (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-" . org-shiftright) ("S-" . 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:")))))))) #+END_SRC ** 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 #+begin_src emacs-lisp (use-package ob-python ; :ensure nil :defer t :after org ; :ensure org-contrib :commands (org-babel-execute:python)) #+end_src ** COMMENT habits #+BEGIN_SRC emacs-lisp (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) #+END_SRC ** contacts #+BEGIN_SRC emacs-lisp (use-package org-contacts :ensure (:host nil :repo "https://repo.or.cz/org-contacts.git") :after org :custom (org-contacts-files (list (concat MY--PATH_ORG_ROAM "contacts. org")))) #+END_SRC ** *TODO* [[https://github.com/nobiot/org-transclusion][org-transclusion]]? ** COMMENT journal [[https://github.com/bastibe/org-journal][Source]] Ggf. durch org-roam-journal ersetzen #+BEGIN_SRC emacs-lisp (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))) #+END_SRC ** org-roam [[https://github.com/org-roam/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 #+BEGIN_SRC emacs-lisp (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)) #+END_SRC *** 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 #+BEGIN_SRC emacs-lisp ;; 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))) #+END_SRC ** 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 #+begin_src emacs-lisp (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) #+end_src ** LSP-Mode #+begin_src emacs-lisp (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)))) #+end_src ** flymake python in venv: pip install pyflake (or ruff?) TODO: if ruff active, sideline stops working #+begin_src emacs-lisp (setq python-flymake-command '("ruff" "--quiet" "--stdin-filename=stdin" "-")) #+end_src ** sideline show flymake errors on the right of code window #+begin_src emacs-lisp (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))) #+end_src ** yasnippet For useful snippet either install yasnippet-snippets or get them from here [[https://github.com/AndreaCrotti/yasnippet-snippets][Github]] #+begin_src emacs-lisp (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 "" yas-minor-mode-map)) #+end_src ** hippie expand With hippie expand I am able to use yasnippet and emmet at the same time with the same key. #+begin_src emacs-lisp (use-package hippie-exp :ensure nil :defer t :bind ("C-" . hippie-expand) :config (setq hippie-expand-try-functions-list '(yas-hippie-try-expand emmet-expand-line))) #+end_src ** COMMENT flycheck (now flymake) #+BEGIN_SRC emacs-lisp (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 #+END_SRC ** smartparens #+BEGIN_SRC emacs-lisp (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-" . sp-slice-sexp-killing-backward) ("C-S-" . 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)) #+END_SRC ** lisp #+BEGIN_SRC emacs-lisp (use-package elisp-mode :ensure nil :defer t) #+END_SRC ** 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\to\node;%path% *** web-mode #+BEGIN_SRC emacs-lisp (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)) #+END_SRC Emmet offers snippets, similar to yasnippet. Default completion is C-j [[https://github.com/smihica/emmet-mode#usage][Github]] #+begin_src emacs-lisp (use-package emmet-mode :ensure t :defer t :hook ((web-mode . emmet-mode) (css-mode . emmet-mode)) :config (unbind-key "C-" emmet-mode-keymap)) #+end_src *** JavaScript npm install -g typescript-language-server typescript maybe only typescript? npm install -g prettier #+begin_src emacs-lisp (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)) #+end_src ** YAML #+begin_src emacs-lisp (use-package yaml-mode :if *sys/linux* :ensure t :defer t :mode ("\\.yml$" . yaml-mode)) #+end_src ** R #+BEGIN_SRC emacs-lisp (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"))) #+END_SRC ** project.el #+begin_src emacs-lisp (use-package project :custom (project-vc-extra-root-markers '(".project.el" ".project" ))) #+end_src ** 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" "..." )) #+begin_src emacs-lisp (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) #+end_src TODO python mode hook: - activate venv - activate eglot with proper ls - activate tree-sitter? - have some fallback if activations fail * beancount ** Installation #+BEGIN_SRC shell :tangle no sudo su cd /opt python3 -m venv beancount source ./beancount/bin/activate pip3 install wheel pip3 install beancount sleep 100 echo "shell running!" deactivate #+END_SRC #+begin_src emacs-lisp (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")) #+end_src +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 #+BEGIN_SRC shell :tangle no 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 #+END_SRC Fava is strongly recommended. #+BEGIN_SRC shell :tangle no cd /opt python3 -m venv fava source ./fava/bin/activate pip3 install wheel pip3 install fava deactivate #+END_SRC Start fava with fava my_file.beancount It is accessable on this URL: [[http://127.0.0.1:5000][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? #+begin_src emacs-lisp ;(setq gc-cons-threshold (* 2 1000 1000)) #+end_src Rest of early-init.el #+begin_src emacs-lisp :tangle 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)) #+end_src