diff --git a/config.org b/config.org index 48252b6..c13938b 100644 --- a/config.org +++ b/config.org @@ -974,6 +974,9 @@ mu init --maildir=/path/to/mail/folder --my-address=mu@adress.com --my-address=a mu index #+end_src +for credentials see +https://www.emacswiki.org/emacs/SmtpAuth + #+begin_src emacs-lisp ;(use-package notmuch ; :ensure t) diff --git a/init.el b/init.el index dedc241..4fb6633 100644 --- a/init.el +++ b/init.el @@ -1,8 +1,1309 @@ -(defconst config-org (expand-file-name "config.org" user-emacs-directory)) -(defconst config-el (expand-file-name "config.el" user-emacs-directory)) +;;; init.el --- -*- lexical-binding: t -*- -(unless (file-exists-p config-el) - (require 'org) - (org-babel-tangle-file config-org config-el)) +(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)) -(load-file config-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)) + +(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)) + +(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"))) + + (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) + +(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)) + +(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) + +(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 + +(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 + +(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) + +(when *sys/linux* + (set-face-font 'default "Hack-10")) +(when *work_remote* + (set-face-font 'default "Lucida Sans Typewriter-11")) + +(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) + +(when *sys/windows* + (mapcar #'disable-theme custom-enabled-themes) + (load-theme 'tango)) +(when *sys/linux* + (mapcar #'disable-theme custom-enabled-themes) + (load-theme 'plastic)) + +(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))) + +(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)) + +(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)) + +(use-package dired + :ensure nil + :custom + (dired-kill-when-opening-new-dired-buffer t)) + +(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")) + +(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))) + +(use-package burly + :ensure t + :config + (burly-tabs-mode) ;;open a burly window bookbark in a new tab + ) + +(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)) + +(use-package savehist + :ensure nil + :config + (savehist-mode) + :custom + (savehist-file (concat MY--PATH_USER_LOCAL "history"))) + +(use-package undo-tree + :ensure t + :diminish undo-tree-mode + :init + (global-undo-tree-mode 1) + :custom + (undo-tree-auto-save-history nil)) + +(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)) + +(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 + +(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)) + + (use-package eldoc + :ensure nil + :diminish eldoc-mode + :defer t) + +(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)) + +(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"))) + +;; completion ui +(use-package vertico + :ensure t + :init + (vertico-mode)) + +(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)) + +(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)) + +(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)) + +(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))))) + +(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))) + +(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))) + +(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)) + + (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) + ) + +(use-package org-ql + :ensure t + ) + +;(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 smtpmail-servers-requiring-authorization ".*") + (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") + (message-send-mail-function . smtpmail-send-it) + (smtpmail-smtp-user . "marc.pohling@mail.de") + (smtpmail-smtp-server . "smtp.mail.de") + (smtpmail-smtp-service . 587) + (smtpmail-stream-type . starttls) + (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") + (message-send-mail-function . smtpmail-send-it) + (smtpmail-smtp-user . "louithelou1@gmail.com") + (smtpmail-smtp-server . "smtp.gmail.com") + (smtpmail-smtp-service . 587) + (smtpmail-stream-type . starttls) + (mu4e-drafts-folder . "/gmailloui/drafts") + (mu4e-sent-folder . "/gmailloui/sent") + (mu4e-refile-folder . "/gmailloui/archive") + (mu4e-trash-folder . "/gmailloui/trash"))))) + ) + +(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)) + +;(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"))' + +(use-package autorevert + :diminish auto-revert-mode) + +;(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-" . 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:")))))))) + +(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")))) + +(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)) + +;; 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))) + +(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)))) + +(setq python-flymake-command '("ruff" "--quiet" "--stdin-filename=stdin" "-")) + +(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))) + +(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)) + +(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))) + +(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)) + +(use-package elisp-mode + :ensure nil + :defer t) + +(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)) + +(use-package emmet-mode + :ensure t + :defer t + :hook + ((web-mode . emmet-mode) + (css-mode . emmet-mode)) + :config + (unbind-key "C-" emmet-mode-keymap)) + +(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)) + +(use-package yaml-mode + :if *sys/linux* + :ensure t + :defer t + :mode ("\\.yml$" . yaml-mode)) + +(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"))) + +(use-package project + :custom + (project-vc-extra-root-markers '(".project.el" ".project" ))) + +(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) + +(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")) + +;(setq gc-cons-threshold (* 2 1000 1000))