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

1309 lines
44 KiB

;;; init.el --- -*- lexical-binding: t -*-
(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))
(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)
'("<escape>" . ignore))
(meow-leader-define-key
;; SPC j/k will run the original command in MOTION state.
'("j" . "H-j")
'("k" . "H-k")
;; Use SPC (0-9) for digit arguments.
'("1" . meow-digit-argument)
'("2" . meow-digit-argument)
'("3" . meow-digit-argument)
'("4" . meow-digit-argument)
'("5" . meow-digit-argument)
'("6" . meow-digit-argument)
'("7" . meow-digit-argument)
'("8" . meow-digit-argument)
'("9" . meow-digit-argument)
'("0" . meow-digit-argument)
'("/" . meow-keypad-describe-key)
'("?" . meow-cheatsheet))
(meow-normal-define-key
'("0" . meow-expand-0)
'("9" . meow-expand-9)
'("8" . meow-expand-8)
'("7" . meow-expand-7)
'("6" . meow-expand-6)
'("5" . meow-expand-5)
'("4" . meow-expand-4)
'("3" . meow-expand-3)
'("2" . meow-expand-2)
'("1" . meow-expand-1)
'("-" . negative-argument)
'(";" . meow-reverse)
'("," . meow-inner-of-thing)
'("." . meow-bounds-of-thing)
'("[" . meow-beginning-of-thing)
'("]" . meow-end-of-thing)
'("a" . meow-append)
'("A" . meow-open-below)
'("b" . meow-back-word)
'("B" . meow-back-symbol)
'("c" . meow-change)
'("d" . meow-delete)
'("D" . meow-backward-delete)
'("e" . meow-next-word)
'("E" . meow-next-symbol)
'("f" . meow-find)
'("g" . meow-cancel-selection)
'("G" . meow-grab)
'("h" . meow-left)
'("H" . meow-left-expand)
'("i" . meow-insert)
'("I" . meow-open-above)
'("j" . meow-next)
'("J" . meow-next-expand)
'("k" . meow-prev)
'("K" . meow-prev-expand)
'("l" . meow-right)
'("L" . meow-right-expand)
'("m" . meow-join)
'("n" . meow-search)
'("o" . meow-block)
'("O" . meow-to-block)
'("p" . meow-yank)
'("q" . meow-quit)
'("Q" . meow-goto-line)
'("r" . meow-replace)
'("R" . meow-swap-grab)
'("s" . meow-kill)
'("t" . meow-till)
'("u" . meow-undo)
'("U" . meow-undo-in-selection)
'("v" . meow-visit)
'("w" . meow-mark-word)
'("W" . meow-mark-symbol)
'("x" . meow-line)
'("X" . meow-goto-line)
'("y" . meow-save)
'("Y" . meow-sync-grab)
'("z" . meow-pop-selection)
'("'" . repeat)
'("<escape>" . ignore))
; :config
(meow-global-mode t))
(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)
; ("<C-return>" . corfu-insert)
; ("C-TAB" . corfu-popupinfo-toggle)))
;; (general-define-key
;; :states 'insert
;; :definer 'minor-mode
;; :keymaps 'completion-in-region-mode
;; :predicate 'corfu-mode
;; "C-d" 'corfu-info-documentation)
(use-package emacs
:ensure nil
:init
;; hide commands in M-x which do not apply to current mode
(setq read-extended-command-predicate #'command-completion-default-include-p)
;; enable indentation + completion using TAB
(setq tab-always-indent 'complete))
(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-<right>" . org-shiftright)
("S-<left>" . org-shiftleft))
:init
(defun my--org-agenda-files-set ()
"Sets default agenda files.
Necessary when updating roam agenda todos."
(setq org-agenda-files (list (concat MY--PATH_ORG_FILES "notes.org")
(concat MY--PATH_ORG_FILES "projects.org")
(concat MY--PATH_ORG_FILES "tasks.org")))
(when *sys/linux*
(nconc org-agenda-files
(directory-files-recursively MY--PATH_ORG_FILES_MOBILE "\\.org$"))))
(my--org-agenda-files-set)
(defun my--org-skip-subtree-if-priority (priority)
"Skip an agenda subtree if it has a priority of PRIORITY.
PRIORITY may be one of the characters ?A, ?B, or ?C."
(let ((subtree-end (save-excursion (org-end-of-subtree t)))
(pri-value (* 1000 (- org-lowest-priority priority)))
(pri-current (org-get-priority (thing-at-point 'line t))))
(if (= pri-value pri-current)
subtree-end
nil)))
:config
(when *work_remote*
(org-add-link-type "outlook" 'my--org-outlook-open)
(org-add-link-type "outlooknewmail" 'my--org-outlook-new-mail)
(setq org-todo-keywords
'((sequence "OPEN" "TODO" "UNCLEAR" "|" "DONE" "IMPOSSIBLE" "CANCELLED")))
(setq org-capture-templates
'(("t" "telephone call" entry
; (file+olp+datetree (concat MY--PATH_ORG_FILES "phone_calls.org"))
(file+datetree "p:/Eigene Dateien/Notizen/phone_calls.org")
"* [%<%Y-%m-%d %H:%M>] %?"
:empty-lines 0 :jump-to-captured t))))
(when *sys/linux*
(setq org-pretty-entities t))
:custom
(org-startup-truncated t)
(org-startup-align-all-tables t)
(org-src-fontify-natively t) ;; use syntax highlighting in code blocks
(org-src-preserve-indentation t) ;; no extra indentation
(org-src-window-setup 'current-window) ;; C-c ' opens in current window
(org-modules (quote (org-id
org-habit
org-tempo))) ;; easy templates
(org-default-notes-file (concat MY--PATH_ORG_FILES "notes.org"))
(org-id-locations-file (concat MY--PATH_USER_LOCAL ".org-id-locations"))
(org-log-into-drawer "LOGBOOK")
(org-log-done 'time) ;; create timestamp when task is done
(org-blank-before-new-entry '((heading) (plain-list-item))) ;; prevent new line before new item
(org-src-tab-acts-natively t)
;;Sort agenda by deadline and priority
(org-agenda-sorting-strategy
(quote
((agenda deadline-up priority-down)
(todo priority-down category-keep)
(tags priority-down category-keep)
(search category-keep))))
(org-agenda-custom-commands
'(("c" "Simple agenda view"
((tags "PRIORITY=\"A\""
((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
(org-agenda-overriding-header "Hohe Priorität:")))
(agenda ""
((org-agenda-skip-function '(org-agenda-skip-entry-if 'todo 'done))
(org-agenda-span 7)
(org-agenda-start-on-weekday nil)
(org-agenda-overriding-header "Nächste 7 Tage:")))
(alltodo ""
((org-agenda-skip-function '(or (my--org-skip-subtree-if-priority ?A)
(org-agenda-skip-if nil '(scheduled deadline))))
(org-agenda-overriding-header "Sonstige Aufgaben:"))))))))
(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 "<tab>" yas-minor-mode-map))
(use-package hippie-exp
:ensure nil
:defer t
:bind
("C-<return>" . hippie-expand)
:config
(setq hippie-expand-try-functions-list
'(yas-hippie-try-expand emmet-expand-line)))
(use-package smartparens
:ensure t
:diminish smartparens-mode
:bind
(:map smartparens-mode-map
("C-M-f" . sp-forward-sexp)
("C-M-b" . sp-backward-sexp)
("C-M-a" . sp-backward-down-sexp)
("C-M-e" . sp-up-sexp)
("C-M-w" . sp-copy-sexp)
("M-k" . sp-kill-sexp)
("C-M-<backspace>" . sp-slice-sexp-killing-backward)
("C-S-<backspace>" . sp-slice-sexp-killing-around)
("C-]" . sp-select-next-thing-exchange))
:config
(setq sp-show-pair-from-inside nil
sp-escape-quotes-after-insert nil)
(require 'smartparens-config))
(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-<return>" 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))