Some functionality uses this to identify you, e.g. GPG configuration, email clients, file templates and snippets.
(setq user-full-name "Stefan Lendl"
user-mail-address "ste.lendl@gmail.com")(remove-hook 'org-mode-hook #'+literate-enable-recompile-h)doom/goto-private-config-file goes to config.el which I will never edit by hand.
Opening config.org makes much more sense.
(defun stfl/goto-private-config-file ()
"Open your private config.el file."
(interactive)
(find-file (expand-file-name "config.org" doom-user-dir)))bind it to SPC h d c
(define-key! help-map
"dc" #'stfl/goto-private-config-file
"dC" #'doom/open-private-config);; (global-auto-revert-mode 1)
(setq undo-limit 80000000
evil-want-fine-undo t
inhibit-compacting-font-caches t)(setq auto-save-default t)
(run-with-idle-timer 60 t '(lambda () (save-some-buffers t)))Use Brave browser if availible. The default should actually respect the $BROWSER env
(when (executable-find "brave")
(setq! browse-url-browser-function 'browse-url-chromium
browse-url-chromium-program "brave"))Resizing a window vertically with the mouse in emacs requires you to hit the 1px thick line between windows. This is less of a problem horizontally, because you can drag the modeline.
To make this easier use, Meta + left mouse drag to activate the resizing.
This only works if there is a window to the right. Window is resized, after
passing the border with the mouse.
(global-set-key [M-drag-mouse-2] #'mouse-drag-vertical-line)It would be nice to do that on the left fringe! But this would active again resizing to the right…
;; (defun mouse-drag-left-line (start-event)
;; "Change the width of a window by dragging on a vertical line.
;; START-EVENT is the starting mouse event of the drag action."
;; (interactive "e")
;; (mouse-drag-line start-event 'left))
;; (global-set-key [left-fringe drag-mouse-1] #'mouse-drag-left-line)limit evil-snipe to the bufffer
(after! evil-snipe
(setq evil-snipe-scope 'visible
evil-snipe-repeat-scope 'visible))(map! :leader "f ." #'find-file-at-point)Always indent the line when pressing tab regardles of the cursor position. If the line is already correctly indented, try to complete.
(setq! tab-always-indent 'complete)(after! evil-escape
(setq evil-escape-key-sequence "jk"))(package! drag-stuff)(use-package! drag-stuff
:defer t
:init
(map! "<M-up>" #'drag-stuff-up
"<M-down>" #'drag-stuff-down
"<M-left>" #'drag-stuff-left
"<M-right>" #'drag-stuff-right))Disable automatic garbage collection and run when emacs is idle.
https://jackjamison.xyz/blog/emacs-garbage-collection/
(setq gc-cons-threshold most-positive-fixnum)
(run-with-idle-timer 1.2 t 'garbage-collect)Sway automatically focues a frame when moving the mouse over. Emacs does not register this and mouse clicks are registed on the wrong frame.
(setq! focus-follows-mouse 'auto-raise
mouse-autoselect-window nil)(after! consult
(setq! consult-fd-args '((if (executable-find "fdfind" 'remote) "fdfind" "fd")
"--color=never"
;; "--full-path"
;; "--absolute-path"
"--hidden"
"--exclude .git"
(if (featurep :system 'windows) "--path-separator=/")))
(defun stfl/consult-fd (&optional arg)
(interactive "P")
(let ((consult-fd-args (append consult-fd-args (and arg '("--no-ignore")))))
(consult-fd)))
(defun stfl/projectile-find-file (&optional arg)
(interactive "P")
(if arg (stfl/consult-fd arg)
(projectile-find-file)))
;; (defun stfl/projectile-find-file (&optional arg)
;; (interactive "P")
;; (let ((projectile-git-fd-args (concat projectile-git-fd-args (and arg " --no-ignore-vcs"))))
;; (projectile-find-file)))
(defun stfl/consult-fd-dir (&optional arg)
(interactive "P")
(let ((consult-fd-args (append consult-fd-args '("--type d") (and arg '("--no-ignore")))))
(consult-fd)))
(map! :leader
"SPC" #'stfl/projectile-find-file
:prefix "f"
"l" #'stfl/consult-fd
"L" #'stfl/consult-fd-dir))(setq doom-theme 'doom-one)(setq! display-line-numbers-type t)
(setq! which-key-idle-delay 0.3)Doom exposes five (optional) variables for controlling fonts in Doom. Here are the three important ones:
- `doom-font’
- `doom-variable-pitch-font’
- `doom-big-font’ – used for `doom-big-font-mode’; use this for presentations or streaming.
They all accept either a font-spec, font string (“Input Mono-12”), or xlfd font string. You generally only need these two:
(let ((font "JetBrains Mono Nerd Font Mono"))
(setq doom-font (font-spec :family font :size 13)
doom-variable-pitch-font (font-spec :family font)
doom-big-font (font-spec :family font :size 20)))font detection taken from tecosaur https://tecosaur.github.io/emacs-config/config.html#font-face
(defvar required-fonts
'("JetBrainsMono.*"
;; "Overpass"
;; "JuliaMono"
;; "IBM Plex Mono"
;; "Merriweather"
;; "Alegreya"
))
(defvar available-fonts
(delete-dups (or (font-family-list)
(split-string (shell-command-to-string "fc-list : family")
"[,\n]"))))
(defvar missing-fonts
(delq nil (mapcar
(lambda (font)
(unless (delq nil (mapcar (lambda (f)
(string-match-p (format "^%s$" font) f))
available-fonts))
font))
required-fonts)))
(if missing-fonts
(pp-to-string
`(unless noninteractive
(add-hook! 'doom-init-ui-hook
(run-at-time nil nil
(lambda ()
(message "%s missing the following fonts: %s"
(propertize "Warning!" 'face '(bold warning))
(mapconcat (lambda (font)
(propertize font 'face 'font-lock-variable-name-face))
',missing-fonts
", "))
(sleep-for 0.5))))))
";; No missing fonts detected")(custom-set-faces!
`(whitespace-indentation :background ,(doom-color 'base4)) ; Visually highlight if an indentation issue was discovered which emacs already does for us
`(magit-branch-current :foreground ,(doom-color 'blue) :box t)
'(lsp-inlay-hint-face :height 0.85 :italic t :inherit font-lock-comment-face)
'(lsp-bridge-inlay-hint-face :height 0.85 :italic t :inherit font-lock-comment-face)
)(setq! tab-width 4)Org directory
(setq org-directory "~/.org")always generate an id for a link
(after! org-id
(setq org-id-link-to-org-use-id t
org-id-locations-file (doom-path doom-local-dir "org-id-locations")
org-id-track-globally t))rebuild orgid file on start at the first time emacs is idle for 20 sec
(after! org-id (run-with-idle-timer 20 nil 'org-id-update-id-locations))also rebuild that via org-roam
(after! org-roam (run-with-idle-timer 25 nil 'org-roam-update-org-id-locations));; Local dev — switch to GitHub recipe once published:
(package! agile-gtd
;; :recipe (:local-repo "~/work/agile-gtd" :build (:not compile)))
:recipe (:host github :repo "stfl/agile-gtd.el"))(use-package agile-gtd
:after org
:config
;; Additional agenda files not managed by agile-gtd projects
(setq org-agenda-files (mapcar (lambda (f) (expand-file-name f org-directory))
'("inbox-orgzly.org"
"geschenke.org"
"media.org"
"projects.org"
"versicherung.org"
"ikea.org"
"cafe-glas.org")))
(setq agile-gtd-projects '((:tag "oebb" :name "ÖBB" :key ?o)
(:tag "origina" :name "Origina" :key ?i)
(:tag "pulswerk" :name "Pulswerk" :key ?p)
(:tag "freelance" :name "Freelance")
(:tag "emacs" :name "Emacs")))
(agile-gtd-enable)
)MCP server for Org-mode, exposing agenda files to AI assistants (e.g. Claude Code).
(package! mcp-server-lib)
(package! org-mcp
:recipe (:host github :repo "stfl/org-mcp"))
;; :recipe (:local-repo "~/work/org-mcp" :build (:not compile)))(use-package org-mcp
:after (org agile-gtd)
:custom
(org-mcp-stored-queries-file nil) ;; FIXME rework the stored query functionality
(org-mcp-ql-extra-properties '((parent-priority . agile-gtd--direct-parent-priority)
(rank . agile-gtd--item-rank)))
(org-mcp-query-inbox-fn #'agile-gtd-agenda-query-inbox)
(org-mcp-query-backlog-fn #'agile-gtd-agenda-query-backlog)
(org-mcp-query-next-fn #'agile-gtd-agenda-query-next-actions)
(org-mcp-query-sort-fn #'agile-gtd--item-rank<)
:config
(if mcp-server-lib--running
(message "org-mcp: MCP server already running, skipping start")
(mcp-server-lib-start)))
;; Set org-mcp-allowed-files after agile-gtd-enable has populated org-agenda-files.
;; Must be a separate (after! org …) block registered AFTER the use-package forms
;; above so it fires as a later hook — after agile-gtd-enable completes.
(with-eval-after-load 'org
(setopt org-mcp-allowed-files
(mapcar (lambda (f) (expand-file-name f org-directory)) org-agenda-files)))First I like to add some extra fancy stuff to make org-mode more appealing when i’m using +pretty flag.
- Other options for ellipsis “▼, ↴, ⬎, ⤷,…, and ⋱.”
- Extra options for headline-bullets-list: “◉” “●” “○” “∴”
(after! org
(setq! org-auto-align-tags nil
org-tags-column 0
org-fold-catch-invisible-edits 'show-and-error
org-ellipsis "…"
org-indent-indentation-per-level 2)
(auto-fill-mode)); (custom-declare-face 'org-checkbox-statistics-todo '((t (:inherit (bold font-lock-constant-face org-todo)))) "")
(custom-set-faces!
'(org-document-title :foreground "#c678dd" :weight bold :height 1.8)
'(org-ql-view-due-date :foreground "dark goldenrod")
`(org-code :foreground ,(doom-lighten (doom-color 'warning) 0.3) :extend t)
'(outline-1 :height 1.5)
'(outline-2 :height 1.25)
'(outline-3 :height 1.15)
`(org-column :height 130 :background ,(doom-color 'base4)
:slant normal :weight regular :underline nil :overline nil :strike-through nil :box nil :inverse-video nil)
`(org-column-title :height 150 :background ,(doom-color 'base4) :weight bold :underline t))(after! org
(setq! org-tag-faces `((,agile-gtd-lastmile-tag . (:foreground ,(doom-color 'red) :strike-through t))
(,agile-gtd-habit-tag . (:foreground ,(doom-darken (doom-color 'orange) 0.2)))
(,agile-gtd-someday-tag . (:slant italic :weight bold))
;; ("finance" . (:foreground "goldenrod"))
;; ("#inbox" . (:background ,(doom-color 'base4) :foregorund ,(doom-color 'base8)))
("#inbox" . (:strike-through t))
("3datax" . (:foreground ,(doom-color 'green)))
("oebb" . (:foreground ,(doom-color 'green)))
("pulswerk" . (:foreground ,(doom-color 'dark-blue)))
(,agile-gtd-work-tag . (:foreground ,(doom-color 'blue)))
;; ("#work" . (:foreground ,(doom-color 'blue)))
("@ikea" . (:foreground ,(doom-color 'yellow)))
("@amazon" . (:foreground ,(doom-color 'yellow)))
;; ("emacs" . (:foreground "#c678dd"))
))
)Automatically saving all org-buffers when emacs is idle for 30 seconds.
(after! org (run-with-idle-timer 60 t #'org-save-all-org-buffers))(after! org
(setq org-startup-indented 'indent
org-startup-folded 'fold
org-startup-with-inline-images t
;; org-image-actual-width (round (* (font-get doom-font :size) 25))
org-image-actual-width (* (default-font-width) 40)
))
(add-hook 'org-mode-hook 'org-indent-mode)
;; (add-hook 'org-mode-hook 'turn-off-auto-fill)See doomemacs/doomemacs#3185 - Invalid base64 data
(defadvice! no-errors/+org-inline-image-data-fn (_protocol link _description)
:override #'+org-inline-image-data-fn
"Interpret LINK as base64-encoded image data. Ignore all errors."
(ignore-errors
(base64-decode-string link)))From here we load some extra key bindings that I use often
;; (bind-key "<f6>" #'link-hint-copy-link)
(map! :after org
:map org-mode-map
:leader
:prefix ("n" . "notes")
:desc "Revert all org buffers" "R" #'org-revert-all-org-buffers
)
(map! :after org
:map org-mode-map
:localleader
:desc "Revert all org buffers" "R" #'org-revert-all-org-buffers
"N" #'org-add-note
:prefix ("l" . "links")
"o" #'org-open-at-point
"g" #'eos/org-add-ids-to-headlines-in-file
:prefix ("d" . "dates/deadlines")
"c" #'org-cancel-repeater
)refile target -> build list of someday files dynamically
This is configured in agile-gtd.
refile to roam files by
(defun stfl/build-my-roam-files () (file-expand-wildcards (doom-path org-directory "roam/**/*.org")))
(defun stfl/refile-to-roam ()
(interactive)
(let ((org-refile-targets '((stfl/build-my-roam-files :maxlevel . 1))))
(call-interactively 'org-refile)))Creating an org-roam note from an existing headline
(defun org-roam-create-note-from-headline ()
"Create an Org-roam note from the current headline and jump to it.
Normally, insert the headline’s title using the ’#title:’ file-level property
and delete the Org-mode headline. However, if the current headline has a
Org-mode properties drawer already, keep the headline and don’t insert
‘#+title:'. Org-roam can extract the title from both kinds of notes, but using
‘#+title:’ is a bit cleaner for a short note, which Org-roam encourages."
(interactive)
(let ((title (nth 4 (org-heading-components)))
(has-properties (org-get-property-block)))
(org-cut-subtree)
(org-roam-find-file title nil nil 'no-confirm)
(org-paste-subtree)
(unless has-properties
(kill-line)
(while (outline-next-heading)
(org-promote)))
(goto-char (point-min))
(when has-properties
(kill-line)
(kill-line))))(with-eval-after-load 'org
(setq org-capture-templates
(append
(cl-remove-if (lambda (template)
(equal "v" (car-safe template)))
org-capture-templates)
`(("v" "Versicherung" entry
(file+headline ,(doom-path org-directory "versicherung.org") "Einreichungen")
(function stfl/org-capture-template-versicherung)
:root "~/Documents/Finanzielles/Einreichung Versicherung")))))(setq stfl/org-roam-absolute (doom-path org-directory "roam/"))
(after! org-roam
(setq! org-roam-capture-templates
`(("d" "default" plain "%?"
:target (file+head ,(doom-path stfl/org-roam-absolute "%<%Y%m%d%H%M%S>-${slug}.org")
"#+title: ${title}\n")
:unnarrowed t))))(after! org
(defun stfl/org-capture-versicherung-post ()
(unless org-note-abort
(mkdir (org-capture-get :directory) t)))
(defun stfl/build-versicherung-dir (root date title)
(let ((year (nth 5 (parse-time-string date))))
(format "%s/%d/%s %s" root year date title)))
(defun stfl/org-capture-template-versicherung ()
(interactive)
(let* ((date (org-read-date nil nil nil "Datum der Behandlung" nil nil t))
(title (read-string "Title: "))
(directory (stfl/build-versicherung-dir (org-capture-get :root) date title)))
(org-capture-put :directory directory)
(add-hook! 'org-capture-after-finalize-hook :local #'stfl/org-capture-versicherung-post)
(format "* SVS [%s] %s
:PROPERTIES:
:CREATED: %%U
:date: [%s]
:betrag: %%^{Betrag|0}
:svs: nil
:generali: nil
:category: %%^{Kategorie|nil|Arzt|Alternativ|Internet|Psycho|Besonders|Apotheke|Vorsorge|Heilbehelfe|Brille|Transport}
:END:
[[file:%s]]
%%?" date title date directory)))
)(after! org (setq org-archive-location (doom-path org-directory "archive/%s::datetree")))org-checklist can be used to automatically reset the checkboxes in a recurring task
set the RESET_CHECK_BOXES property to t to reset the checklist on repeat
(after! org (require 'org-checklist))load org-habit because many of the functions in org-helpers.el require it…
(use-package! org-habit
:after org-agenda
:config
(add-to-list 'org-modules 'org-habit)
(setq org-habit-show-habits t
org-habit-preceding-days 14
org-habit-following-days 7
;; org-habit-graph-column 31 ;; Length of the habit graph
))(after! org-clock
(setq! org-clock-rounding-minutes 15 ;; Org clock should clock in and out rounded to 5 minutes.
org-time-stamp-rounding-minutes '(0 15)
org-duration-format 'h:mm ;; format hours and don't Xd (days)
org-clock-report-include-clocking-task t ;; include current task in the clocktable
org-log-note-clock-out t
org-agenda-clockreport-parameter-plist '(:link t :maxlevel 2 :stepskip0 t :fileskip0 t :hidefiles t :tags t)
))Prompt to continue from the last clock-out time if the gap is
(after! org-clock
(setq! org-clock-continuously nil)) ;; org-clock-continuously is handled by the advice
(after! org
(defvar stfl/org-clock-continous-threshold 30)
(defun stfl/org-read-date-time ()
(let ((now (org-current-time org-clock-rounding-minutes t)))
(org-read-date t t nil nil now (format-time-string "%H:%M" now))))
(defun stfl/org-clock-in-at ()
(interactive)
(require 'org-clock)
(let ((time (stfl/org-read-date-time))
(org-clock-continuously (org-clocking-p)))
(when (org-clocking-p)
;; Sanity check to avoid negative clock times -> best resolve manually
(when (> 0 (time-subtract time org-clock-start-time))
(error (format "Manually clocking in while another LATER clock is running! \"%s\" started at %s"
org-clock-heading (format-time-string (org-time-stamp-format 'with-time t) org-clock-start-time))))
(org-clock-out nil nil time))
(org-clock-in nil time)))
(defun stfl/org-clock-out-at ()
(interactive)
(when (org-clocking-p) (org-clock-out nil nil (stfl/org-read-date-time))))
(defun stfl/org-time-minutes-ago-rounded (time)
(/ (org-time-convert-to-integer
(time-subtract (org-current-time org-clock-rounding-minutes t) time))
60))
(defun stfl/org-time-minutes-ago (time)
(/ (org-time-convert-to-integer
(time-subtract (org-current-time) time))
60))
(defun stfl/org-time-format-ago (time)
(format "%s (-%dm) (~%dm)"
(format-time-string (org-time-stamp-format 'with-time t) time)
(stfl/org-time-minutes-ago time)
(stfl/org-time-minutes-ago-rounded time)))
(defadvice! stfl/org-clock-continue? (orig-fn &rest args)
"Prompt to continue on clock on clock out time if longer than `stfl/org-clock-continous-threshold`."
:around #'org-clock-in
(interactive "P")
(let* ((start-time (cadr args))
(org-clock-continuously
(if start-time
org-clock-continuously ;; apply previous value
(or (org-clocking-p)
(and org-clock-out-time
(or (< (stfl/org-time-minutes-ago org-clock-out-time) stfl/org-clock-continous-threshold)
(y-or-n-p (format "You stopped another clock at %s; start this one from then? "
(stfl/org-time-format-ago org-clock-out-time)))))))))
(apply orig-fn args)))
(map! :map org-mode-map
:localleader
:prefix "c"
:desc "clock IN at time" "I" #'stfl/org-clock-in-at
:desc "clock OUT at time" "O" #'stfl/org-clock-out-at))(package! org-clock-csv)(use-package org-clock-csv
:after org
:commands stfl/org-clock-export
:config
(defun stfl/org-clock-csv-row-fmt (plist)
"Default row formatting function."
(mapconcat #'identity
(list (org-clock-csv--escape (plist-get plist ':task))
(org-clock-csv--escape (s-join org-clock-csv-headline-separator (plist-get plist ':parents)))
(org-clock-csv--escape (org-clock-csv--read-property plist "ARCHIVE_OLPATH"))
(org-clock-csv--escape (plist-get plist ':category))
(plist-get plist ':start)
(plist-get plist ':end)
(plist-get plist ':effort)
(plist-get plist ':ishabit)
(plist-get plist ':tags)
(org-clock-csv--read-property plist "ARCHIVE_ITAGS")
(org-clock-csv--read-property plist "AP"))
","))
(setq! org-clock-csv-header "task,parents,archive_parents,category,start,end,effort,ishabit,tags,archive_tags,ap"
org-clock-csv-row-fmt #'stfl/org-clock-csv-row-fmt)
(setq stfl/org-clock-export-dir "~/work/invoice.typ/invoices")
(defun stfl/org-clock-export (project)
(interactive
(list (completing-read "Select project: " agile-gtd-project-files)))
(let* ((org-agenda-files (list (doom-path org-directory project)
(doom-path org-directory "archive" project)))
(filename (format "%s-org-clock-%s.csv" (format-time-string "%Y-%m") (file-name-base project)))
(filepath (doom-path stfl/org-clock-export-dir filename)))
(org-clock-csv-to-file filepath))))
(map! :map org-mode-map
:leader
:prefix "n"
:desc "Export project clock entries" "E" #'stfl/org-clock-export)
(map! :map org-mode-map
:localleader
:prefix "c"
:desc "Export project clock entries" "C" #'stfl/org-clock-export)(package! org-edna)Extensible Dependencies ’N’ Actions (EDNA) for Org Mode tasks
(use-package! org-edna
:after org
;; :hook org-mode-hook ;; load package after hook
;; :config (org-edna-mode) ;; enable after load
)
(add-hook! 'org-mode-hook #'org-edna-mode)Helper commands to wire tasks into sequential dependency chains (defined in agile-gtd).
(map! :after org
:map org-mode-map
:localleader
:prefix ("d" . "date/dateline/dependencies")
:desc "next-sibling NEXT" "n" #'agile-gtd-trigger-next-sibling
:desc "trigger NEXT and block prev" "b" #'agile-gtd-chain-task)After much feedback and discussing with other users, I decided to simplify the keyword list to make it simple. Defining a project will now focus on the tag word :project: so that all child task are treated as part of the project.
| Keyword | Description |
|---|---|
| TODO | |
| PROJ | Task has actionable items defined and ready to be worked. |
| HOLD | Has actionable items, but is on hold due to various reasons. |
| WAIT | Waiting for something |
| NEXT | Is ready to be worked and should be worked on soon. |
| IDEA | Might do, that but most likely drop it |
| DONE | Task is completed and closed. |
| KILL | Abandoned or terminated. |
;; TODO keywords are configured in agile-gtd.(custom-set-faces!
`(agile-gtd-todo-cancel :foreground ,(doom-blend (doom-color 'red) (doom-color 'base5) 0.35) :inherit (bold org-done))
`(agile-gtd-todo-idea :foreground ,(doom-darken (doom-color 'green) 0.4) :inherit (bold org-todo)))For the logging drawers, we like to keep our notes and clock history seperate from our properties drawer…
(after! org (setq org-log-state-notes-insert-after-drawers nil))Next, we like to keep a history of our activity of a task so we track when changes occur, and we also keep our notes logged in their own drawer. Optionally you can also add the following in-buffer settings to override the org-log-into-drawer function. #+STARTUP: logdrawer or #+STARTUP: nologdrawer
(after! org
(setq org-log-into-drawer t
org-log-done 'time+note
org-log-repeat 'time
org-log-redeadline 'time
org-log-reschedule 'time
))(after! org
(setq org-use-property-inheritance t ; We like to inherit properties from their parents
org-catch-invisible-edits 'error ; Catch invisible edits
org-track-ordered-property-with-tag t
org-hierarchical-todo-statistics nil
))(setq org-tag-alist '((:startgrouptag)
("Context" . nil)
(:grouptags)
;; ("@home" . ?h)
;; ("@office". ?o)
("@sarah" . ?s)
("@lena" . ?l)
;; ("@kg" . ?k)
("@jg" . ?j)
("@mfg" . ?m)
;; ("@robert" . ?r)
;; ("@baudock_meeting" . ?b)
;; ("@PC" . ?p)
;; ("@phone" . ?f)
(:endgrouptag)
))Roam directory setup
(after! org-roam
(setq! org-roam-directory org-directory
org-roam-db-location (doom-path doom-local-dir "roam.db")
org-roam-file-exclude-regexp "\\.org/\\(?:jira\\|\\.stversions\\)/"))do not automatically open the roam side-pane
(after! org-roam
(setq +org-roam-open-buffer-on-find-file nil))customize the sections in the org-roam buffer
(after! org-roam-mode
(add-to-list 'org-roam-mode-sections #'org-roam-unlinked-references-section t))(after! org-roam
(setq org-roam-dailies-capture-templates
'(("d" "default"
entry "* %?\n:PROPERTIES:\n:ID: %(org-id-new)\n:END:\n\n"
:target (file+head "%<%Y-%m-%d>.org" "#+title: %<%Y-%m-%d>\n")))));; (package! websocket)
;; (package! org-roam-ui
;; :recipe (:host github
;; :repo "org-roam/org-roam-ui"
;; :files ("*.el" "out")));; (use-package! websocket
;; :after org-roam)
;; (use-package! org-roam-ui
;; :after org-roam ;; or :after org
;; ;; normally we'd recommend hooking orui after org-roam, but since org-roam does not have
;; ;; a hookable mode anymore, you're advised to pick something yourself
;; ;; if you don't care about startup time, use
;; ;; :hook (after-init . org-roam-ui-mode)
;; :config
;; (setq org-roam-ui-sync-theme t
;; org-roam-ui-follow t
;; org-roam-ui-update-on-save t
;; org-roam-ui-open-on-start nil))(after! org-gcal
;; (use-package! org-gcal
(setq org-gcal-client-id (get-auth-info "org-gcal-client-id" "ste.lendl@gmail.com")
org-gcal-client-secret (get-auth-info "org-gcal-client-secret" "ste.lendl@gmail.com")
org-gcal-fetch-file-alist
`(("ste.lendl@gmail.com" . ,(doom-path org-directory "gcal/stefan.org"))
("vthesca8el8rcgto9dodd7k66c@group.calendar.google.com" . ,(doom-path org-directory "gcal/oskar.org")))
org-gcal-token-file "~/.config/authinfo/org-gcal-token.gpg"
org-gcal-down-days 180
;; org-gcal-auto-archive nil ;; workaround for "rx "**" range error" https://github.com/kidd/org-gcal.el/issues/17
))
(map!
:after (org org-gcal)
:map org-mode-map
:leader
(:prefix ("n" . "notes")
(:prefix ("j" . "sync")
:desc "sync Google Calendar" "g" #'org-gcal-sync)))
(map!
:after (org org-gcal)
:map org-mode-map
:localleader
:prefix ("C" . "Google Calendar")
:desc "sync Google Calendar" "g" #'org-gcal-sync
"S" #'org-gcal-sync-buffer
"p" #'org-gcal-post-at-point
"d" #'org-gcal-delete-at-point
"f" #'org-gcal-fetch
"F" #'org-gcal-fetch-buffer)Org babel to generate mermaid diagrams from org src blocks
(package! ob-mermaid
:disable t)(use-package! ob-mermaid
:after org
:init
(setq ob-mermaid-cli-path "/home/stefan/.yarn/bin/mmdc")
:config
(add-to-list 'org-babel-load-languages '(mermaid . t)))https://tecosaur.github.io/emacs-config/config.html#babel
(add-transient-hook! #'org-babel-execute-src-block
(require 'ob-async))
(defvar org-babel-auto-async-languages '()
"Babel languages which should be executed asyncronously by default.")
(defadvice! org-babel-get-src-block-info-eager-async-a (orig-fn &optional light datum)
"Eagarly add an :async parameter to the src information, unless it seems problematic.
This only acts o languages in `org-babel-auto-async-languages'.
Not added when either:
+ session is not \"none\"
+ :sync is set"
:around #'org-babel-get-src-block-info
(let ((result (funcall orig-fn light datum)))
(when (and (string= "none" (cdr (assoc :session (caddr result))))
(member (car result) org-babel-auto-async-languages)
(not (assoc :async (caddr result))) ; don't duplicate
(not (assoc :sync (caddr result))))
(push '(:async) (caddr result)))
result))Individual startup visibility with :hidden
https://emacs.stackexchange.com/a/44923/30180
(after! org
(defun individual-visibility-source-blocks ()
"Fold some blocks in the current buffer with property :hidden"
(interactive)
(org-show-block-all)
(org-block-map
(lambda ()
(let ((case-fold-search t))
(when (and
(save-excursion
(beginning-of-line 1)
(looking-at org-block-regexp))
(cl-assoc
':hidden
(cl-third
(org-babel-get-src-block-info))))
(org-hide-block-toggle))))))
(add-hook 'org-mode-hook #'individual-visibility-source-blocks))https://github.com/tecosaur/org-pandoc-import
;; (package! org-pandoc-import
;; :recipe (:host github
;; :repo "tecosaur/org-pandoc-import"
;; :files ("*.el" "filters" "preprocessors")));; (use-package! org-pandoc-import :after org)Don’t emphasize the heading -> it’s way too big
(after! org-tree-slide (setq org-tree-slide-heading-emphasis nil))disable line numbers in presentations.
(after! org-tree-slide
(add-hook 'org-tree-slide-play-hook #'doom-disable-line-numbers-h)
(add-hook 'org-tree-slide-stop-hook #'doom-disable-line-numbers-h))Starting org-tree-slide fails with an error. doomemacs/doomemacs#7058
(after! org-tree-slide
(remove-hook 'org-tree-slide-play-hook #'+org-present-hide-blocks-h)
(remove-hook 'org-tree-slide-stop-hook #'+org-present-hide-blocks-h))(package! orgzly-formatter
:recipe (:host github :repo "stfl/orgzly-formatter.el"))(use-package orgzly-formatter
:hook (org-mode . orgzly-formatter-mode))orgzly-formatter already removes trailing whitespaces and keeps certain ones in specific cases.
(with-eval-after-load 'ws-butler
(add-to-list 'ws-butler-global-exempt-modes 'org-mode))(package! ox-hugo)(use-package! ox-hugo :after ox)(package! ox-zola
:recipe (:host github :repo "gicrisf/ox-zola"))(use-package! ox-zola
:after ox
:config
(require 'ox-hugo))(map! :after org-agenda
:map org-agenda-mode-map
:desc "Prioity up" "C-S-k" #'org-agenda-priority-up
:desc "Prioity down" "C-S-j" #'org-agenda-priority-down
:localleader
"N" #'org-agenda-add-note
:desc "Filter" "f" #'org-agenda-filter
:desc "Follow" "F" #'org-agenda-follow-mode
"o" #'org-agenda-set-property
"s" #'org-toggle-sticky-agenda
:prefix ("p" . "priorities")
:desc "Prioity" "p" #'org-agenda-priority
:desc "Prioity up" "u" #'org-agenda-priority-up
:desc "Prioity down" "d" #'org-agenda-priority-down
:desc "Someday/Maybe toggle" "s" #'agile-gtd-agenda-toggle-someday
:desc "Add to Someday/Maybe" "S" #'agile-gtd-agenda-set-someday
:desc "Tickler toggle" "t" #'agile-gtd-agenda-toggle-tickler
:desc "Add to Tickler" "T" #'agile-gtd-agenda-set-tickler
:desc "Remove Someday/Maybe" "r" #'agile-gtd-agenda-remove-someday
:prefix ("v" . "View up to priority")
"v" #'agile-gtd-agenda-show-priorities
"l" #'agile-gtd-agenda-show-less-priorities
"m" #'agile-gtd-agenda-show-more-priorities
"r" #'agile-gtd-agenda-reset-show-priorities
)
(map! :after org-ql
:map org-ql-view-map
"z" #'org-ql-view-dispatch);; (after! org
(setq!
;; org-agenda-dim-blocked-tasks t
org-agenda-dim-blocked-tasks 'invisible
org-agenda-use-time-grid t
;; org-agenda-hide-tags-regexp "\\w+"
;; org-agenda-compact-blocks t
;; org-agenda-block-separator ?\n
org-agenda-block-separator ?-
org-agenda-tags-column 0
org-agenda-skip-scheduled-if-done t
org-agenda-skip-unavailable-files t
org-agenda-skip-deadline-if-done t
org-agenda-skip-timestamp-if-done t
org-agenda-window-setup 'current-window
org-agenda-start-on-weekday nil
org-agenda-span 'day
org-agenda-start-day "-0d"
org-deadline-warning-days 7
org-agenda-show-future-repeats t
org-agenda-skip-deadline-prewarning-if-scheduled t
org-agenda-tags-todo-honor-ignore-options t
org-agenda-skip-scheduled-delay-if-deadline t
org-agenda-skip-scheduled-if-deadline-is-shown t
org-agenda-skip-timestamp-if-deadline-is-shown t
;; org-agenda-todo-ignore-with-date nil
;; org-agenda-todo-ignore-deadlines nil
;; org-agenda-todo-ignore-timestamp nil
org-agenda-todo-list-sublevels t
org-agenda-include-deadlines t
org-agenda-sticky t)(after! org
(setq org-enforce-todo-checkbox-dependencies nil
org-enforce-todo-dependencies nil))(package! org-super-agenda)(after! (org-super-agenda evil-org-agenda)
(setq org-super-agenda-header-map evil-org-agenda-mode-map))The priority grouping is generated dynamically by agile-gtd.
The ancestor-priority grouping is implemented in agile-gtd.
This grouping is implemented in agile-gtd.
To mark entries (mainly PROJ) as not relevant at the moment I mark them with the
tag SOMEDAY. If the enty has a SCHEDULED date assigned it’s considered a TICKLER
entry. A TICKLER entry is not relevant right now but will be relevant at some
point in the future. For the time beeing I want it to disapear from the todo
backlog. On the scheduling date it will be added back into the system by
removing the SOMEDAY tag and the schduling date.
Mark an agenda entry
These commands are provided by agile-gtd.
(package! org-ql)Agile GTD requires org-ql directly from the module.
(after! org-contrib
(require 'org-checklist))function to load a secret from an auth-source. letoh/creation-prompt.el
(defun get-auth-info (host user &optional port)
(let ((info (nth 0 (auth-source-search
:host host
:user user
:port port
:require '(:user :secret)))))
(if info
(let ((secret (plist-get info :secret)))
(if (functionp secret)
(funcall secret)
secret))
nil)))get-password
(defun get-password (&rest keys)
(let ((result (apply #'auth-source-search keys)))
(when result
(funcall (plist-get (car result) :secret)))));; (setq! auth-sources 'password-store);; (use-package! define-word
;; :after org
;; :config
;; (map! :after org
;; :map org-mode-map
;; :leader
;; :desc "Define word at point" "@" #'define-word-at-point))(setq org-pandoc-options '((standalone . t) (self-contained . t)))https://tecosaur.github.io/emacs-config/config.html#plaintext
(after! text-mode
(add-hook! 'text-mode-hook
;; Apply ANSI color codes
(with-silent-modifications
(ansi-color-apply-on-region (point-min) (point-max)))))disable vterm from straight because we installed it via nix
(package! vterm :disable t)(after! vterm
(setq! vterm-max-scrollback 200000
;; vterm-min-window-width 5000
)) ;; do not wrap long lines per defaultAllow to insert C-x. otherwise it’s not possible to leave nano if exidently opened in vterm.
Send some keys directly to vterm to trigger zsh functions!
(map!
:after vterm
:map vterm-mode-map
"C-c C-x" #'vterm--self-insert
:n "C-r" #'vterm--self-insert
:n "C-j" #'vterm--self-insert
:i "C-j" #'vterm--self-insert
:i "TAB" #'vterm-send-tab
:i "<tab>" #'vterm-send-tab)For some reason, emacs-vterm tries to send C-j as return to the terminal instead of C-m. This breaks my zsh config, so I am forcing it to send C-m.
Send C-j directly as configured above which then triggers history-substring-search-down
(after! vterm
(defun vterm-send-return ()
"Send `C-m' to the libvterm."
(interactive)
(deactivate-mark)
(when vterm--term
(process-send-string vterm--process "\C-m"))))use bash when opening a vterm in a tramp dir:
(after! vterm
(setq! vterm-tramp-shells '(("docker" "/bin/sh")
("ssh" "/bin/bash"))))(package! eat
:recipe (:type git
:host codeberg
:repo "akib/emacs-eat"
:files ("*.el" ("term" "term/*.el") "*.texi"
"*.ti" ("terminfo/e" "terminfo/e/*")
("terminfo/65" "terminfo/65/*")
("integration" "integration/*")
(:exclude ".dir-locals.el" "*-tests.el"))))(use-package! eat
:config
;; Enable eat with eshell
(eat-eshell-mode)
;; Use semi-char mode by default for better interaction
(setq! eat-term-scrollback-size 50000
eat-enable-yank-to-terminal t
eat-enable-kill-from-terminal t))(package! typst-ts-mode
:recipe (:type git :host codeberg
:repo "meow_king/typst-ts-mode"
:files (:defaults "*.el")))(use-package! typst-ts-mode
:mode ("\\.typ\\'" . typst-ts-mode)
:config
(setq! typst-ts-watch-options "--open"
typst-ts-indent-offset 2
typst-ts-enable-raw-blocks-highlight t)
(map! :map typst-ts-mode-map
"C-c C-c" #'typst-ts-tmenu
:localleader
:desc "Compile" "c" #'typst-ts-compile
:desc "Watch" "w" #'typst-ts-watch-mode
:desc "Menu" "m" #'typst-ts-tmenu)
(add-hook! 'typst-ts-mode-hook #'lsp!))
(with-eval-after-load 'treesit
(add-to-list 'treesit-language-source-alist
'(typst "https://github.com/uben0/tree-sitter-typst")))(after! eglot
(add-to-list 'eglot-server-programs
`(typst-ts-mode . ("tinymist"))))(after! lsp-mode
(add-to-list 'lsp-language-id-configuration '(typst-ts-mode . "typst") t)
(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection "tinymist")
:activation-fn (lsp-activate-on "typst")
:server-id 'tinymist)))(after! org
(add-to-list 'org-src-lang-modes '("typst" . typst-ts))
;; Set up babel support for Typst
(org-babel-do-load-languages 'org-babel-load-languages '((typst . t)))
;; Configure babel execution for Typst
(defun org-babel-execute:typst (body params)
"Execute a block of Typst code with org-babel."
(message "Executing Typst code block")
(let* ((in-file (org-babel-temp-file "typst-" ".typ"))
(out-file (or (cdr (assq :file params))
(org-babel-temp-file "typst-" ".pdf")))
(result-params (cdr (assq :result-params params)))
(cmdline (or (cdr (assq :cmdline params)) "")))
(with-temp-file in-file
(insert body))
(org-babel-eval
(format "typst compile %s %s %s" cmdline in-file out-file)
"")
(when (member "file" result-params)
(org-babel-result-cond result-params
out-file
(format "[[file:%s]]" out-file))))))(package! ox-typst)(use-package! ox-typst :after ox)(package! pdf-tools :built-in 'prefer)(add-to-list 'auto-mode-alist '("\\.service\\'" . conf-space-mode))(defvar stfl/upload-local-mappings nil
"Global alist to store local to remote mappings (local-file . remote-path).
Each entry maps an absolute local file path to its corresponding remote path
for ssh-deploy functionality.")
(defun stfl/upload-unregister-all-remotes ()
"Clear all ssh-deploy mappings and remove buffer-local variables.
Iterates through all stored mappings in stfl/upload-local-mappings and clears
ssh-deploy buffer-local variables for any open buffers, then clears the
global mapping list."
(interactive)
;; For each mapping, find open buffers and clear their local variables
(dolist (mapping stfl/upload-local-mappings)
(let* ((local-file (car mapping))
(buffer (get-file-buffer local-file)))
(when buffer
(message "Unregistering ssh-deploy mapping from %s" buffer)
(with-current-buffer buffer
(setq-local ssh-deploy-root-local nil
ssh-deploy-root-remote nil)))))
;; Clear the global alist
(setq stfl/upload-local-mappings nil))
(defun stfl/upload-register-mapping (&optional remote-path)
"Register or unregister ssh-deploy mapping for current buffer.
With C-u prefix, unregisters the current buffer's mapping and removes it
from the global stfl/upload-local-mappings list. Otherwise prompts for REMOTE-PATH
and registers the mapping, storing it in both buffer-local variables and the
global mapping list. Updates or replaces any existing mapping for the current file."
(interactive (if current-prefix-arg
(list nil) ; Don't prompt when unregistering
(list (expand-file-name (read-file-name "Remote path: ")))))
(require 'ssh-deploy)
(let ((local-file (expand-file-name (buffer-file-name))))
(if current-prefix-arg
(progn
(message "Unregistering ssh-deploy for this buffer")
(setq-local ssh-deploy-root-local nil
ssh-deploy-root-remote nil)
;; Remove mapping from global alist
(setq stfl/upload-local-mappings
(assoc-delete-all local-file stfl/upload-local-mappings)))
(progn
(setq-local ssh-deploy-root-local local-file
ssh-deploy-root-remote remote-path)
(message "registered ssh-deploy for this buffer to %s" ssh-deploy-root-remote)
;; Add/update mapping in global alist
(setq stfl/upload-local-mappings
(cons (cons local-file remote-path)
(assoc-delete-all local-file stfl/upload-local-mappings)))))))(after! ssh-deploy
(setq! ssh-deploy-async 1))
(map! :map ssh-deploy-menu-map
:leader
:prefix "r"
"l" #'stfl/upload-register-mapping
"L" #'stfl/upload-unregister-all-remotes)(when (executable-find "zoxide")
(with-eval-after-load 'dired
(add-hook 'dired-mode-hook (lambda ()
(call-process-shell-command
(format "zoxide add %s" dired-directory) nil 0))))
(add-hook 'find-file-hook (lambda ()
(call-process-shell-command
(format "zoxide add %s" (file-name-directory buffer-file-name))
nil 0)))
(defun find-file-with-zoxide ()
(interactive)
(let ((target (consult--read
(process-lines "zoxide" "query" "-l")
:prompt "Zoxide: "
:require-match nil
:lookup #'consult--lookup-member
:category 'file
:sort t)))
(if target
(let ((default-directory (concat target "/")))
(call-interactively 'find-file))
(call-interactively 'find-file))))
(map! )
)(after! flycheck
(map! :map flycheck-mode-map
:leader
"c x" #'consult-flycheck))map ielm to SPC :
(map! :leader ":" #'ielm)I am not using lsp-mode anymore
(after! lsp-treemacs
(lsp-treemacs-sync-mode 1))(map! :after lsp-mode
:map lsp-mode-map
:leader
:prefix ("c" . "+code")
:desc "Diagnostic for Workspace" "X" #'lsp-treemacs-errors-list)Enable hinlay hints and make the font a little smaller than the rest
(after! lsp-mode
(setq! lsp-inlay-hint-enable t
lsp-headerline-breadcrumb-enable t
lsp-ui-sideline-enable nil)
)(when (executable-find "emacs-lsp-booster")
(after! lsp-mode
(setq! lsp-use-plists t)
(defun lsp-booster--advice-final-command (old-fn cmd &optional test?)
"Prepend emacs-lsp-booster command to lsp CMD."
(let ((orig-result (funcall old-fn cmd test?)))
(if (and (not test?) ;; for check lsp-server-present?
(not (file-remote-p default-directory)) ;; see lsp-resolve-final-command, it would add extra shell wrapper
lsp-use-plists
(not (functionp 'json-rpc-connection))) ;; native json-rpc
(progn
(message "Using emacs-lsp-booster for %s!" orig-result)
(append '("emacs-lsp-booster" "--disable-bytecode" "--") orig-result))
orig-result)))
(advice-add 'lsp-resolve-final-command :around #'lsp-booster--advice-final-command)))(use-package! lsp-bridge
:disabled
:config
(setq! lsp-bridge-user-langserver-dir (doom-path doom-user-dir "langserver")
lsp-bridge-enable-inlay-hint t
lsp-bridge-enable-hover-diagnostic t
lsp-bridge-enable-signature-help nil
lsp-bridge-enable-auto-format-code nil
lsp-bridge-enable-org-babel t
lsp-bridge-log-level 'default
acm-enable-capf t
acm-enable-org-roam t
)
(set-lookup-handlers! 'lsp-bridge-mode
:definition #'lsp-bridge-peek
;; :definition #'lsp-bridge-find-def
:references #'lsp-bridge-find-references
:documentation #'lsp-bridge-popup-documentation
:implementations #'lsp-bridge-find-impl
:type-definition #'lsp-bridge-find-type-def)
(map! :map lsp-bridge-peek-keymap
:g "C-j" #'lsp-bridge-peek-list-next-line
:g "C-k" #'lsp-bridge-peek-list-prev-line
:g "C-S-j" #'lsp-bridge-peek-file-content-next-line
:g "C-S-k" #'lsp-bridge-peek-file-content-prev-line
:g "RET" #'lsp-bridge-peek-jump
:g "C-SPC" #'lsp-bridge-peek-jump
:g "ESC" #'lsp-bridge-peek-abort)
(map! :map lsp-bridge-mode-map
:leader
:n "c r" #'lsp-bridge-rename
:n "c a" #'lsp-bridge-code-action
:n "c f" #'lsp-bridge-code-format
)
(map! :map acm-mode-map
:i "C-j" #'acm-select-next
:i "C-k" #'acm-select-prev)
(acm-mode t)
(global-lsp-bridge-mode)
;; (lsp-bridge-semantic-tokens-mode t)
)https://github.com/manateelazycat/lsp-bridge/blob/master/acm/acm.el#L233
https://github.com/konrad1977/flyover
(package! flyover :recipe (:host github :repo "konrad1977/flyover"))(use-package! flyover
:disabled
:after flycheck
:config
(setq! flyover-checkers '(flycheck)
;; flyover-levels '(error warning info) ; Show all levels
;; flyover-levels '(error warning)
flyover-levels '(error)
flyover-use-theme-colors t ;; Use theme colors for error/warning/info faces
flyover-background-lightness 35; Adjust background lightness (lower values = darker)
;; flyover-percent-darker 40 ;; Make icon background darker than foreground
flyover-text-tint nil ; 'lighter ;; or 'darker or nil
;; flyover-text-tint-percent 50 ;; "Percentage to lighten or darken the text when tinting is enabled."
flyover-debug nil ;; Enable debug messages
;; flyover-debounce-interval 0.2 ;; Time in seconds to wait before checking and displaying errors after a change
;; flyover-wrap-messages t ;; Enable wrapping of long error messages across multiple lines
flyover-max-line-length 100 ;; Maximum length of each line when wrapping messages
flyover-hide-checker-name t
flyover-show-virtual-line t ;;; Show an arrow (or icon of your choice) before the error to highlight the error a bit more.
;; flyover-virtual-line-type 'straight-arrow
flyover-line-position-offset 1
flyover-show-at-eol t ;;; show at end of the line instead.
flyover-hide-when-cursor-is-on-same-line t ;;; Hide overlay when cursor is at same line, good for show-at-eol.
flyover-virtual-line-icon " ──► " ;;; default its nil
)
(add-hook 'flycheck-mode-hook #'flyover-mode)
)mapping doom’s +format/buffer and +format/region
(map! (:when (modulep! :editor format)
:v "g Q" '+format/region
:v "SPC =" '+format/region
:leader
:desc "Format Buffer" "=" #'+format/buffer
(:prefix ("b" "+buffer")
:desc "Format Buffer" "f" #'+format/buffer)))(after! (lsp-mode php-mode)
(setq lsp-intelephense-licence-key (get-auth-info "intelephense" "ste.lendl@gmail.com")
lsp-intelephense-files-associations '["*.php" "*.phtml" "*.inc"]
lsp-intelephense-files-exclude '["**update.php**" "**/js/**" "**/fonts/**" "**/gui/**" "**/upload/**"
"**/.git/**" "**/.svn/**" "**/.hg/**" "**/CVS/**" "**/.DS_Store/**"
"**/node_modules/**" "**/bower_components/**"
"**/vendor/**/{Test,test,Tests,tests}/**"]
lsp-auto-guess-root nil
lsp-idle-delay 0.8))(after! lsp-bridge
(setq! lsp-bridge-python-multi-lsp-server "basedpyright_ruff"))Poetry needs to scan for a project whenever a new file is opened. Tracking via projectile speeds this up significantly.
(after! poetry (setq poetry-tracking-strategy 'projectile))automatically activate a conda environment if present in a project
(after! conda (conda-env-autoactivate-mode))projectile does not recognize conda projects
(after! projectile
(projectile-register-project-type 'python-conda '("environment.yml")
:project-file "environment.yml"
:compile "conda build" ;; does not exist
:test "conda run pytest"
:test-dir "tests"
:test-prefix "test_"
:test-suffix"_test"));; (package! numpydoc);; (use-package! numpydoc
;; :after python-mode
;; :commands numpydoc-generate
;; :config
;; (map! :map python-mode-map
;; :localleader
;; :prefix ("d" . "docstring")
;; :desc "Generate Docstring" "d" #'numpydoc-generate))ein displays images in a separate window by default. Use inline images instead
(after! ein
(setq! ein:output-area-inlined-images t
ein:worksheet-warn-obsolesced-keybinding nil))To enable this in the notebook, configure matplotlib to produce inline images.
%matplotlib inlinedon’t kill the *ein: buffer with ESC which causes the buffer not to work properly
org babel integration for ein
(when (modulep! :tools ein)
(after! org
(require 'ob-ein)))(set-popup-rule! "^\\*ein:" :ignore t :quit nil)header arguments to for jupyter-python to work with plotly
(after! org
(setq org-babel-default-header-args:jupyter-python
'((:results . "value")
(:session . "jupyter")
(:kernel . "python3")
(:pandoc . "t")
(:exports . "both")
(:cache . "no")
(:noweb . "no")
(:hlines . "no")
(:tangle . "no")
(:eval . "never-export"))))debugging python tests at point do not work with the default configuration. might be merged upstream: emacs-lsp/dap-mode#590
(after! (python-mode dap-mode)
(dap-register-debug-template "Python :: Run pytest (at point) -- Workaround"
(list :type "python-test-at-point "
:args ""
:program nil
:module "pytest"
:request "launch"
:name "Python :: Run pytest (at point)")))(map! :mode rustic-mode
:map rustic-mode-map
:localleader
:desc "rerun test" "t r" #'rustic-cargo-test-rerun)(after! rustic
(when (executable-find "cargo-nextest")
(setq! rustic-cargo-test-runner 'nextest)))Configure inlay type hints
This requires lsp-inlay-hint-enable as configured in LSP/Inlay Hints
(after! lsp-rust
(setq! lsp-rust-analyzer-binding-mode-hints t
;; lsp-rust-analyzer-display-chaining-hints t
;; lsp-rust-analyzer-display-closure-return-type-hints t
lsp-rust-analyzer-display-lifetime-elision-hints-enable "skip_trivial"
;; lsp-rust-analyzer-display-parameter-hints t
;; lsp-rust-analyzer-hide-named-constructor t
lsp-rust-analyzer-max-inlay-hint-length 40 ;; otherwise some types can get way out of hand
)
)(after! eglot
(setq eglot-workspace-configuration
(plist-put eglot-workspace-configuration
:rust-analyzer
'(:inlayHints (:maxLength 40)))))configre dap-mode for rust
(after! (rust-mode dap-mode)
(dap-register-debug-template "Rust::GDB Run Configuration"
(list :type "gdb"
:request "launch"
:name "GDB::Run"
:gdbpath "rust-gdb"
:target nil
:cwd nil)))configure alejandra, an alternative nix formatter
(set-formatter! 'alejandra '("alejandra" "--quiet") :modes '(nix-ts-mode))select the formatter for nix
(setq-hook! 'nix-ts-mode-hook +format-with 'alejandra)(add-to-list 'auto-mode-alist '("\\.mq[45h]\\'" . cpp-mode));; (use-package! gitlab-ci-mode
;; :mode ".gitlab-ci.yml"
;; )
;; (use-package! gitlab-ci-mode-flycheck
;; :after flycheck gitlab-ci-mode
;; :init
;; (gitlab-ci-mode-flycheck-enable))(package! kubernetes :disable t)
(package! kubernetes-evil :disable t)
(package! kubernetes-helm :disable t)
(package! k8s-mode :disable t)(use-package! kubernetes
:commands (kubernetes-overview))(use-package! kubernetes-evil
:after kubernetes)(use-package! kubernetes-helm
:commands kubernetes-helm-status)(use-package! k8s-mode
:after yaml-mode
:hook (k8s-mode . yas-minor-mode))(package! sql-indent)(use-package! sql-indent
:after sql-mode)roam:edbi setup
EDBI has some dependencies has some dependencies Installation Instructions
(package! edbi :disable t)
(package! edbi-minor-mode :disable t)(use-package! edbi
:commands 'edbi:open-db-viewer)(use-package! edbi-minor-mode
:after sql-mode
:hook sql-mode-hook)
;; (add-hook 'sql-mode-hook 'edbi-minor-mode)Exercism is a platform for learning various programing languages by solving small exercises. The exercises can can be solved locally.
(package! exercism-mode
:disable t
:recipe (:host github
:repo "timotheosh/exercism-mode"))(use-package! exercism-mode
:after projectile
:if (executable-find "exercism")
:commands exercism
:config (exercism-mode +1)
:custom (exercism-web-browser-function 'browse-url))(package! jest :disable t)(map! :after rjsx-mode
:map rjsx-mode-map
:localleader
:prefix ("t" "test")
"f" #'jest-file
"t" #'jest-function
"k" #'jest-file-dwim
"m" #'jest-repeat
"p" #'jest-popup)The :lang json module handles .json → json-ts-mode via tree-sitter.
json-ts-mode natively supports // and /* */ comments, so .jsonc files can use it too.
(add-to-list 'auto-mode-alist '("\\.jsonc\\'" . json-ts-mode))always use the better cperl-mode instead of perl-mode. This will be the default in Emacs 30 (if I remember correctly)
(add-to-list 'major-mode-remap-alist '(perl-mode . cperl-mode))(package! logview :disable t)(use-package! logview
:commands logview-mode
:config (setq truncate-lines t)
(map! :map logview-mode-map
"j" #'logview-next-entry
"k" #'logview-previous-entry))The default keymap binds directly to f t T a A ...
switch to evil-emacs-mode C-z to use the keybindings.
(package! lsp-ltex :disable t)manually add adoc-mode to the list.. I don’t like that…
;; (add-to-list 'lsp-ltex-active-modes 'adoc-mode t)
(setq lsp-ltex-active-modes '(text-mode
bibtex-mode
context-mode
latex-mode
markdown-mode
org-mode
rst-mode
adoc-mode))(use-package! lsp-ltex
:after lsp-ltex-active-modes
:hook (adoc-mode . (lambda ()
(require 'lsp-ltex)
(lsp-deferred))) ; or lsp-deferred
:init
(setq lsp-ltex-server-store-path "~/.nix-profile/bin/ltex-ls"
lsp-ltex-version "16.0.0"
lsp-ltex-mother-tongue "de-AT"
lsp-ltex-user-rules-path (doom-path doom-user-dir "lsp-ltex")))(after! ispell
(setq! ispell-personal-dictionary (expand-file-name "ispell/" doom-user-dir)))tangle is disabled for the entire section and is only kept as a reference!
;; bbatsov/adoc-mode
(package! adoc-mode)TODO adoc-mode requires org due to inhereting the theme
(use-package! adoc-mode
:defer t
:config
(map! :map adoc-mode-map
:localleader
:desc "consult headers in this file" "." #'consult-imenu
:desc "consult headers in project" "/" #'consult-imenu-multi
"p" #'treemacs-find-tag)
(custom-set-faces!
'(adoc-code-face :inherit org-block)
'(adoc-complex-replacement-face :inherit org-code :weight bold)
'(adoc-meta-face :inherit org-meta-line)
'(adoc-typewriter-face :inherit org-code)
'(adoc-verbatim-face :inherit org-verbatim)
'(adoc-internal-reference-face :inherit org-link)
'(adoc-reference-face :inherit org-link)
`(adoc-emphasis-face :foreground ,(doom-lighten (doom-color 'green) 0.2) :slant italic)
'(adoc-bold-face :weight bold)
`(adoc-command-face :foreground ,(doom-color 'base1) :background ,(doom-color 'base6))
'(adoc-warning-face :inherit org-warning))
)(after! lsp-mode
(add-to-list 'lsp-language-id-configuration '(adoc-mode . "org") t))(after! magit
(setq transient-values '((magit-commit "--signoff"))))This is still broken…
(setq-hook! 'rjsx-mode-hook
indent-tabs-mode t
tab-width 8)
(setq-hook! 'js-mode-hook
indent-tabs-mode t
tab-width 8)
(setq-hook! 'js2-mode-hook
indent-tabs-mode t
tab-width 8)(after! js
(setq js-indent-level 4
js-jsx-indent-level 4
tab-width 8))(setq! cperl-indent-level 4
cperl-close-paren-offset -4
cperl-continued-statement-offset 4
cperl-indent-parens-as-block t)Proxmox specific Perl indentation style using a tab where 8 spaces would be used.
(setq-hook! 'cperl-mode-hook
tab-width 8
indent-tabs-mode t)(after! notmuch
(setq +notmuch-sync-backend 'mbsync
+notmuch-mail-folder "~/Mail"
notmuch-draft-folder "proxmox/Entw&APw-rfe"
notmuch-fcc-dirs "proxmox/Sent"
notmuch-mua-cite-function 'message-cite-original-without-signature
notmuch-mua-compose-in 'current-window
notmuch-show-logo nil
notmuch-hello-indent 0 ;; do not indent because it works better with evil navigation
notmuch-tag-formats '(("unread" (propertize tag 'face 'notmuch-tag-unread)))
notmuch-saved-searches
'((:key "i" :name " inbox" :query "tag:inbox and not tag:archive")
(:key "f" :name " flagged" :query "tag:flagged")
(:key "m" :name " my PRs" :query "tag:my-pr and not tag:archive and not tag:killed and not tag:deleted and not tag:inbox")
(:key "w" :name " watch" :query "tag:watch and not tag:my-pr and not tag:archive and not tag:killed and not tag:deleted and not tag:inbox")
(:key "t" :name " team" :query "tag:lists/team and not tag:archive and not tag:inbox")
(:key "b" :name " My Bugs" :query "tag:bugs and tag:to-me and not tag:archive and not tag:inbox")
(:key "s" :name " support (new)" :query "tag:support-new and not tag:archive and not tag:killed")
(:key "r" :name " review" :query "tag:review and not tag:archive and not tag:killed and not tag:inbox")
(:key "d" :name " drafts" :query "tag:draft and not tag:archive and not tag:deleted")
;; (:key ">" :name " sent" :query "tag:sent and not tag:archive")
(:key "M" :name " my PRs" :query "tag:my-pr and not tag:killed and not tag:deleted and not tag:inbox")
(:key "W" :name " watch" :query "tag:watch and not tag:killed and not tag:deleted and not tag:inbox")
(:key "B" :name " Bugzilla" :query "tag:bugs and not tag:archive and not tag:inbox")
(:key "S" :name " support" :query "tag:support and not tag:archive and not tag:killed and not tag:inbox")
(:key "P" :name " pkgs" :query "tag:lists/pkgs and not tag:archive and not tag:inbox"))
notmuch-archive-tags '("+archive" "-inbox" "-unread")
+notmuch-spam-tags '("+spam" "-inbox" "-unread")
+notmuch-delete-tags '("+trash" "-inbox" "-unread")
stfl/notmuch-unwatch-tags (append notmuch-archive-tags '("-my-pr" "-watch" "-review"))
stfl/notmuch-kill-tags (cons "+killed" stfl/notmuch-unwatch-tags)
message-hidden-headers nil ;; don't hide any headers to verify In-reply-to and Reference headers
notmuch-mua-hidden-headers nil
message-sendmail-f-is-evil 't
message-sendmail-extra-arguments '("--read-envelope-from")
message-send-mail-function 'message-send-mail-with-sendmail
sendmail-program "msmtp")
(add-to-list '+word-wrap-disabled-modes 'notmuch-show-mode)
(add-hook! 'notmuch-hello-mode-hook #'read-only-mode)
(defun +notmuch-get-sync-command ()
"mbsync -a && notmuch new && afew -n -t")
(custom-set-faces!
'(notmuch-message-summary-face :foreground "#848d94") ;; between dooms base6 and base7
`(notmuch-wash-cited-text :foreground ,(doom-color 'base6))
`(notmuch-search-subject :foreground ,(doom-darken (doom-color 'fg) 0.05))
'(notmuch-search-unread-face :weight bold :slant italic)
`(notmuch-tree-match-tree-face :foreground ,(doom-color 'yellow))
`(notmuch-tree-no-match-tree-face :foreground ,(doom-color 'base5))
`(notmuch-tree-no-match-author-face :foreground ,(doom-darken (doom-color 'blue) 0.3))
`(notmuch-tree-no-match-date-face :foreground ,(doom-darken (doom-color 'numbers) 0.3))
`(notmuch-tree-no-match-tag-face :foreground ,(doom-darken (doom-color 'yellow) 0.4)))
)Additional functions to apply tags according to my workflow
(after! notmuch
(defun stfl/notmuch-search-unwatch-thread (&optional unarchive beg end)
(interactive (cons current-prefix-arg (notmuch-interactive-region)))
(let ((notmuch-archive-tags stfl/notmuch-unwatch-tags))
(notmuch-search-archive-thread unarchive beg end)))
(defun stfl/notmuch-search-kill-thread (&optional unarchive beg end)
(interactive (cons current-prefix-arg (notmuch-interactive-region)))
(let ((notmuch-archive-tags stfl/notmuch-kill-tags))
(notmuch-search-archive-thread unarchive beg end)))
)Use the (almost) default keybindings in evil normal-mode
(map! :after notmuch
:map notmuch-common-keymap
:n "?" #'notmuch-help
:map notmuch-show-mode-map
;; :g "<mouse-1>" #'notmuch-show-toggle-message
;; :g "<mouse-2>" #'notmuch-show-toggle-message
;; :desc "toggle show message" :n "<tab>" #'notmuch-show-toggle-message
;; :desc "toggle show message" :n "C-<tab>" #'notmuch-show-open-or-close-all
:g "C-c C-e" #'notmuch-show-resume-message
:n "ge" #'notmuch-show-resume-message
;; :n "A" '(λ! (notmuch-search-tag-all "-archive -my-pr -watch")) ;; TODO need to tag ENTIRE thread oterhwise it will be tagged again with afew
:map notmuch-tree-mode-map
:g "C-c C-e" #'notmuch-tree-resume-message
:n "ge" #'notmuch-tree-resume-message
:n "A" (λ! (notmuch-tree-tag-thread stfl/notmuch-unwatch-tags))
:n "K" (λ! (notmuch-tree-tag-thread stfl/notmuch-kill-tags))
:map notmuch-search-mode-map
:n "A" #'stfl/notmuch-search-unwatch-thread
:n "K" #'stfl/notmuch-search-kill-thread
;; :map notmuch-message-mode-map
;; :n "SPC f s" #'notmuch-draft-save
)(after! notmuch
(defun stfl/notmuch-hello-update-background ()
"Update notmuch-hello buffer. If we are in another frame, allow switch to it so it will be formatted correctly."
(let ((no-display (eq (selected-frame)
(window-frame (display-buffer "*notmuch-hello*")))))
(notmuch-hello no-display)))
(run-with-idle-timer 60 t #'stfl/notmuch-hello-update-background))(package! ssh-config-mode)(use-package! ssh-config-mode :defer t)(package! bitbake-ts-mode)(with-eval-after-load 'treesit
(add-to-list 'treesit-language-source-alist
'(bitbake "https://github.com/tree-sitter-grammars/tree-sitter-bitbake")))(use-package bitbake-ts-mode
:config
(add-to-list 'auto-mode-alist '("\\.inc$" . bitbake-ts-mode))
(add-to-list 'auto-mode-alist '("\\.bbclass" . bitbake-ts-mode)))Setup custom bitbake language server for bitbake-ts-mode via lsp-bridge.
(after! lsp-bridge
(add-to-list 'lsp-bridge-single-lang-server-mode-list
;; '(bitbake-ts-mode . "bitbake-language-server")
'(bitbake-ts-mode . "language-server-bitbake"))
(add-to-list 'lsp-bridge-default-mode-hooks 'bitbake-ts-mode-hook t))lsp-bridge requires a json config file to configure the language server.
NOTE: the :tangle "langserver/<server>.json" needs to match the alist above (<mode> . <server>)
{
"name": "bitbake-language-server",
"languageId": "bitbake",
"command": [ "bitbake-language-server" ],
"settings": {}
}Alternative bitbake lsp from the vscode-bitbake project.
{
"name": "language-server-bitbake",
"languageId": "bitbake",
"command": [ "/home/stefan/node_modules/language-server-bitbake/out/server.js", "--stdio" ],
"settings": {}
}“command”: [ “language-server-bitbake”, “–stdio” ],
(package! meson-mode :disable t)(use-package! meson-mode
:config (add-hook! 'meson-mode-hook #'company-mode))(after! projectile
(add-to-list 'projectile-globally-ignored-directories ".ccls-cache"))(after! lsp-bridge
(setq! lsp-bridge-c-lsp-server "ccls"))(defun run-ctest (arg)
(interactive "P")
(let ((projectile-project-test-cmd "cmake --build build && ctest --test-dir build --output-on-failure --rerun-failed"))
(projectile-test-project arg)))
(map! :mode c++-mode
:map c++-mode-map
:localleader
:prefix ("t" "test")
:n "t" #'run-ctest
;; :n "t" #'gtest-run-at-point
;; :n "T" #'gtest-run
;; :n "l" #'gtest-list
)(package! turbo-log
:recipe (:host github
:repo "artawower/turbo-log"))(use-package! turbo-log
:after prog-mode
:config
(map! :leader
"l l" #'turbo-log-print
"l i" #'turbo-log-print-immediately
"l h" #'turbo-log-comment-all-logs
"l s" #'turbo-log-uncomment-all-logs
"l [" #'turbo-log-paste-as-logger
"l ]" #'turbo-log-paste-as-logger-immediately
"l d" #'turbo-log-delete-all-logs)
(setq! turbo-log-msg-format-template "\"🚀: %s\""
turbo-log-allow-insert-without-treesit-p t))(package! just-mode)(use-package! just-mode
:defer t)(package! ztree :disable t)(use-package! ztree)By default, changes are highlighted line-wise for all but the selected hunk. This has performance reasons. You can enable character-wise highlights for all visible hunks with:
(after! magit
(setq magit-diff-refine-hunk 'all))display more columns in forge list topic
(after! forge (setq forge-topic-list-columns
'(("#" 5 t (:right-align t) number nil)
("Title" 60 t nil title nil)
("State" 6 t nil state nil)
("Marks" 8 t nil marks nil)
("Labels" 8 t nil labels nil)
("Assignees" 10 t nil assignees nil)
("Updated" 10 t nill updated nil))))(package! magit-todos)activate magit-todo to display the TODOs section in magit buffer
(use-package! magit-todos
:after magit
:config
(setq! magit-todos-exclude-globs '(".git/" "node_modules/"))
(magit-todos-mode 1))https://github.com/spwhitton/mailscripts/blob/master/mailscripts.el
The original purpose of this package was to make it easy to use the small mail-handling utilities shipped in Debian’s ‘mailscripts’ package from within Emacs. It now also contains some additional, thematically-related utilities which don’t invoke any of those scripts.
Entry points you might like to look at if you’re new to this package: mailscripts-prepare-patch, notmuch-slurp-debbug, notmuch-extract-{thread,message}-patches{,-to-project}.
;; (package! mailscripts.el
;; :recipe (:host github :repo "spwhitton/mailscripts" :files ("mailscripts.el")));; (set-email-account! "gmail"
;; '((mu4e-sent-folder . "/gmail/[Google Mail]/Gesendet")
;; (mu4e-drafts-folder . "/gmail/[Google Mail]/Entw&APw-rfe")
;; (mu4e-trash-folder . "/gmail/[Google Mail]/Trash")
;; (mu4e-refile-folder . "/gmail/[Google Mail]/Alle Nachrichten")
;; (smtpmail-smtp-user . "ste.lendl@gmail.com")
;; ;; (+mu4e-personal-addresses . "ste.lendl@gmail.com")
;; ;; (mu4e-compose-signature . "---\nStefan Lendl")
;; )
;; t);; (set-email-account! "pulswerk"
;; '((mu4e-sent-folder . "/pulswerk/Sent Items")
;; (mu4e-drafts-folder . "/pulswerk/Drafts")
;; (mu4e-trash-folder . "/pulswerk/Deleted Items")
;; (mu4e-refile-folder . "/pulswerk/Archive")
;; (smtpmail-smtp-user . "lendl@pulswerk.at")
;; ;; (+mu4e-personal-addresses . "lendl@pulswerk.at")
;; ;; (mu4e-compose-signature . "---\nStefan Lendl")
;; )
;; t)(after! mu4e
;; (setq +mu4e-gmail-accounts '(("ste.lendl@gmail.com" . "/gmail")))
(setq mu4e-context-policy 'ask-if-none
mu4e-compose-context-policy 'always-ask)
(setq mu4e-maildir-shortcuts
'((:key ?g :maildir "/gmail/Inbox" )
(:key ?p :maildir "/pulswerk/INBOX")
(:key ?u :maildir "/gmail/Categories/Updates")
(:key ?j :maildir "/pulswerk/Jira" )
(:key ?l :maildir "/pulswerk/Gitlab" :hide t)
))
(setq mu4e-bookmarks
'(
(:key ?i :name "Inboxes" :query "not flag:trashed and (m:/gmail/Inbox or m:/pulswerk/INBOX)")
(:key ?u :name "Unread messages"
:query
"flag:unread and not flag:trashed and (m:/gmail/Inbox or m:/gmail/Categories/* or m:/pulswerk/INBOX or m:\"/pulswerk/Pulswerk Alle\" or m:/pulswerk/Jira or m:/pulswerk/Gitlab)")
(:key ?p :name "pulswerk Relevant Unread" :query "flag:unread not flag:trashed and (m:/pulswerk/INBOX or m:\"/pulswerk/Pulswerk Alle\" or m:/pulswerk/Jira or m:/pulswerk/Gitlab)")
(:key ?g :name "gmail Relevant Unread" :query "flag:unread not flag:trashed and (m:/gmail/Inbox or m:/gmail/Categories/*)")
;; (:key ?t :name "Today's messages" :query "date:today..now" )
;; (:key ?y :name "Yesterday's messages" :query "date:2d..1d")
;; (:key ?7 :name "Last 7 days" :query "date:7d..now" :hide-unread t)
;; ;; (:name "Messages with images" :query "mime:image/*" :key 112)
;; (:key ?f :name "Flagged messages" :query "flag:flagged")
;; (:key ?g :name "Gmail Inbox" :query "maildir:/gmail/Inbox and not flag:trashed")
))
)set up the query for mu4e-alert to also limit the search range
(after! mu4e-alert
(setq mu4e-alert-interesting-mail-query
"flag:unread and not flag:trashed and (m:/gmail/Inbox or m:/gmail/Categories/Updates or m:/pulswerk/INBOX or m:\"/pulswerk/Pulswerk Alle\" or m:/pulswerk/Jira or m:/pulswerk/Gitlab)"))(after! mu4e
(setq mu4e-headers-fields
'((:flags . 6)
(:account-stripe . 2)
(:from-or-to . 25)
(:folder . 10)
(:recipnum . 2)
(:subject . 80)
(:human-date . 8))
+mu4e-min-header-frame-width 142
mu4e-headers-date-format "%d/%m/%y"
mu4e-headers-time-format "⧖ %H:%M"
mu4e-headers-results-limit 1000
mu4e-index-cleanup t)
(defvar +mu4e-header--folder-colors nil)
(appendq! mu4e-header-info-custom
'((:folder .
(:name "Folder" :shortname "Folder" :help "Lowest level folder" :function
(lambda (msg)
(+mu4e-colorize-str
(replace-regexp-in-string "\\`.*/" "" (mu4e-message-field msg :maildir))
'+mu4e-header--folder-colors)))))))(after! mu4e
(setq sendmail-program "/usr/bin/msmtp"
send-mail-function #'smtpmail-send-it
message-sendmail-f-is-evil t
message-sendmail-extra-arguments '("--read-envelope-from") ; , "--read-recipients")
message-send-mail-function #'message-send-mail-with-sendmail))requies emacs compiled with xwidgets
it can still use the browser view.
select via mu4e-views-mu4e-select-view-msg-method
;; (use-package! mu4e-views
;; :after mu4e
;; )(setq +org-msg-accent-color "#1a5fb4"
org-msg-greeting-fmt "\nHi %s,\n\n"
org-msg-signature "\n\n#+begin_signature\n*MfG Stefan Lendl*\n#+end_signature")
(map! :map org-msg-edit-mode-map
:after org-msg
:n "G" #'org-msg-goto-body)forcing text comparison even if diff thinks files are binary
(after! ediff
(setq ediff-diff-options "--text"
ediff-diff3-options "--text"
ediff-toggle-skip-similar t
ediff-diff-options "-w"
;; ediff-window-setup-function 'ediff-setup-windows-plain
ediff-split-window-function 'split-window-horizontally
ediff-floating-control-frame t
))
Render a unified diff (top/bottom) in an easy-to-comprehend side-by-side format. This comes in handy for reading patches from mailing lists (or from whencever you might acquire them).
(package! diffview :disable t)(use-package! diffview
:commands diffview-current
:config
(map!
:after notmuch
:localleader "d" #'diffview-current))https://github.com/Artawower/blamer.el git blame lenses
(package! blamer)(use-package! blamer
:commands global-blamer-mode
:init (map! :leader "t B" #'global-blamer-mode)
:config
(map! :leader "g i" #'blamer-show-posframe-commit-info)
(setq! blamer-idle-time 0.3
blamer-max-commit-message-length 80
;; blamer-max-lines 100
blamer-type 'visual
;; blamer-type 'posframe-popup
;; blamer-type 'overlay-popup
blamer-min-offset 40)
;; (custom-set-faces!
;; `(blamer-face :inherit font-lock-comment-face
;; :slant italic
;; :font "JetBrains Mono"
;; ;; :height 0.9
;; :background unspecified
;; ;; :weight semi-light
;; ;; :foreground ,(doom-color 'base5)
;; ))
(add-hook! org-mode-hook (λ! (blamer-mode 0))))(map!
;; "C-c a" #'aidermacs-transient-menu
:leader
(:prefix ("j" . "AI")
;; "m" #'gptel-menu
;; "j" #'gptel
;; "C-g" #'gptel-abort
;; "C-c" #'gptel-abort
;; :desc "Toggle context" "C" #'gptel-add
;; "s" #'gptel-system-prompt
;; "w" #'gptel-rewrite-menu
;; "t" #'gptel-org-set-topic
;; "P" #'gptel-org-set-properties
;; "a" #'aidermacs-transient-menu
;; "a" #'aider-transient-menu
"o" #'claude-code-ide-menu
(:prefix ("c" . "Copilot Chat")
;; "" #'copilot-chat-reset ;; reset everything including history, buffers and frontend.
"c" #'copilot-chat-display ;; display copilot chat buffers.
"s" #'copilot-chat-explain-symbol-at-line ;; ask Copilot to explain symbol under point.
"e" #'copilot-chat-explain ;; ask copilot to explain selected code.
"r" #'copilot-chat-review ;; ask copilot to review selected code.
"d" #'copilot-chat-doc ;; ask copilot to document selected code.
"f" #'copilot-chat-fix ;; ask copilot to fix selected code.
"o" #'copilot-chat-optimize ;; ask copilot to optimize selected code.
"t" #'copilot-chat-test ;; ask copilot to write tests for selected code.
;; :n "" #'copilot-chat-custom-prompt-selection ;; ask for a prompt in minibuffer and pastes selection after it before sending it to copilot.
"b" #'copilot-chat-add-current-buffer ;; add current buffer to copilot chat. Its content will be sent with every request.
"B" #'copilot-chat-del-current-buffer ;; remove current buffer.
"l" #'copilot-chat-list ;; open buffer list.
;; "" #'copilot-chat-prompt-history-previous ;; insert previous prompt from history in prompt buffer.
;; "" #'copilot-chat-prompt-history-next ;; insert next prompt from history in prompt buffer.
"a" #'copilot-chat-ask-and-insert ;; ask for a custom prompt and write answer in current buffer at point.
"m" #'copilot-chat-insert-commit-message ;; Insert in the current buffer a copilot generated commit message.
)))(defun stfl/setup-api-keys ()
(interactive)
(message "Setting up API keys")
(setenv "OPENAI_API_KEY" (password-store-get "API/OpenAI-emacs"))
(setenv "ANTHROPIC_API_KEY" (password-store-get "API/Claude-emacs"))
(setenv "GEMINI_API_KEY" (password-store-get "API/Gemini-emacs"))
(setenv "PERPLEXITYAI_API_KEY" (password-store-get "API/Perplexity-emacs-pro-ste.lendl"))
(setenv "OPENROUTER_API_KEY" (password-store-get "API/Openrouter-emacs")))(package! copilot
:recipe (:host github
:repo "zerolfx/copilot.el"
:files ("*.el" "dist")))(use-package! copilot
:hook (prog-mode . copilot-mode)
:after prog-mode
:config
;; Define the custom function that either accepts the completion or does the default behavior
(defun +copilot-tab-or-default ()
(interactive)
(if (and (bound-and-true-p copilot-mode)
;; Add any other conditions to check for active copilot suggestions if necessary
)
(copilot-accept-completion)
(evil-insert 1))) ; Default action to insert a tab. Adjust as needed.
;; Bind the custom function to <tab> in Evil's insert state
;; (evil-define-key 'insert 'global (kbd "<tab>") #'+copilot-tab-or-default)
(map! :map copilot-completion-map
"<tab>" #'+copilot-tab-or-default
"TAB" #'+copilot-tab-or-default
;; :i "C-TAB" #'copilot-accept-completion-by-word
;; :i "C-<tab>" #'copilot-accept-completion-by-word
"C-S-n" #'copilot-next-completion
;; :i "C-<tab>" #'copilot-next-completion
"C-S-p" #'copilot-previouse-completion
;; :i "C-<iso-lefttab>" #'copilot-previouse-completion
)
(add-to-list 'copilot-indentation-alist '(org-mode 2))
(setq! copilot-indent-offset-warning-disable t
copilot-max-char-warning-disable t)
(setq copilot-lsp-settings '(:github (:copilot (:selectedCompletionModel "gpt-41-copilot"))))
)
(package! copilot-chat
:recipe (:host github
:repo "chep/copilot-chat.el"
:files ("*.el")))(use-package copilot-chat
:after org
:commands (copilot-chat-insert-commit-message copilot-chat-fix copilot-chat-doc)
:config (setq! copilot-chat-model "claude-3.7-sonnet"
copilot-chat-frontend 'org)
;; (add-hook 'git-commit-setup-hook 'copilot-chat-insert-commit-message)
;; Or call manually (copilot-chat-insert-commit-message) when in the commit message buffer.
)A free alternative to Github Copilot
(package! codeium
:recipe (:host github
:repo "Exafunction/codeium.el")
:disable t)(use-package! codeium
:defer t ;; TODO to start it, manually call codeium-init
;; if you use straight
;; :straight '(:type git :host github :repo "Exafunction/codeium.el")
;; otherwise, make sure that the codeium.el file is on load-path
:init
;; use globally
(add-to-list 'completion-at-point-functions #'codeium-completion-at-point)
;; (add-to-list 'company-frontends #'company-preview-frontend)
(setq company-minimum-prefix-length 0)
;; or on a hook
;; (add-hook 'python-mode-hook
;; (lambda ()
;; (setq-local completion-at-point-functions '(codeium-completion-at-point))))
;; if you want multiple completion backends, use cape (https://github.com/minad/cape):
;; (add-hook 'python-mode-hook
;; (lambda ()
;; (setq-local completion-at-point-functions
;; (list (cape-super-capf #'codeium-completion-at-point #'lsp-completion-at-point)))))
;; TODO for completion at point to work need to add codeium-completion-at-point to completion-at-point-an
;; functions async company-backend is coming soon!
;; codeium-completion-at-point is autoloaded, but you can
;; optionally set a timer, which might speed up things as the
;; codeium local language server takes ~0.2s to start up
;; (add-hook 'emacs-startup-hook
;; (lambda () (run-with-timer 0.1 nil #'codeium-init)))
:config
(setq use-dialog-box nil) ;; do not use popup boxes
;; if you don't want to use customize to save the api-key
(setq codeium/metadata/api_key (password-store-get "API/Codeium"))
;; get codeium status in the modeline
(setq codeium-mode-line-enable
(lambda (api) (not (memq api '(CancelRequest Heartbeat AcceptCompletion)))))
(add-to-list 'mode-line-format '(:eval (car-safe codeium-mode-line)) t)
;; alternatively for a more extensive mode-line
;; (add-to-list 'mode-line-format '(-50 "" codeium-mode-line) t)
;; use M-x codeium-diagnose to see apis/fields that would be sent to the local language server
(setq codeium-api-enabled
(lambda (api)
(memq api '(GetCompletions Heartbeat CancelRequest GetAuthToken RegisterUser auth-redirect AcceptCompletion))))
;; you can also set a config for a single buffer like this:
;; (add-hook 'python-mode-hook
;; (lambda ()
;; (setq-local codeium/editor_options/tab_size 4)))
;; You can overwrite all the codeium configs!
;; for example, we recommend limiting the string sent to codeium for better performance
(defun my-codeium/document/text ()
(buffer-substring-no-properties (max (- (point) 3000) (point-min)) (min (+ (point) 1000) (point-max))))
;; if you change the text, you should also change the cursor_offset
;; warning: this is measured by UTF-8 encoded bytes
(defun my-codeium/document/cursor_offset ()
(codeium-utf8-byte-length
(buffer-substring-no-properties (max (- (point) 3000) (point-min)) (point))))
(setq codeium/document/text 'my-codeium/document/text)
(setq codeium/document/cursor_offset 'my-codeium/document/cursor_offset)
(let ((codeium-exe (executable-find "codeium_language_server")))
(when codeium-exe
(setq codeium-command-executable codeium-exe)))
);; (package! gptel)If get-password does not reply a proper key.. manually clear the auth-source cache with:
;; (auth-source-forget-all-cached)(after! gptel
(setq! gptel-default-mode 'org-mode
;; gptel-response-prefix-alist '((org-mode . "**** Answer"))
gptel-api-key (password-store-get "API/OpenAI-emacs")
;; gptel-model 'gpt-4o
gptel-model 'gemini-pro
;; 'gpt-4.5-preview
gptel-log-level 'info
;; gptel-use-curl nil
gptel-use-curl t
gptel-stream t)
(defun +gptel-font-lock-update (pos pos-end)
;; used with the gptel-post-response-functions hook but swollows the arguments
(font-lock-update))
;; reload font-lock to fix syntax highlighting of org-babel src blocks
(add-hook 'gptel-post-response-functions '+gptel-font-lock-update)
(gptel-make-gemini "Gemini" :stream t
:key (password-store-get "API/Gemini-emacs"))
(gptel-make-anthropic "Claude" ;Any name you want
:stream t ;Streaming responses
:key (password-store-get "API/Claude-emacs"))
(gptel-make-perplexity "Perplexity" ;Any name you want
:stream t ;Streaming responses
:key (password-store-get "API/Perplexity-emacs-pro-ste.lendl"))
(gptel-make-gh-copilot "Copilot")
;; OpenRouter offers an OpenAI compatible API
(gptel-make-openai "OpenRouter" ;Any name you want
:host "openrouter.ai"
:endpoint "/api/v1/chat/completions"
:stream t
:key (password-store-get "API/Openrouter-emacs")
:models '(moonshotai/kimi-k2-thinking))
(setf (alist-get 'cpp gptel-directives) "You are an expert C++ developer using C++20. ONLY use C++20 features availible in gcc12.
Do not use concepts. For functions, methods and variables use the style 'auto method() -> RetType'
Reply concisely. Wrap source code in a ```cpp block.")
)(package! claude-code-ide
:recipe (:host github
:repo "manzaltu/claude-code-ide.el"))(use-package! claude-code-ide
:commands (claude-code-ide-menu)
:config
;; (stfl/setup-api-keys)
(setq! claude-code-ide-terminal-backend 'vterm
claude-code-ide-switch-tab-on-ediff t
claude-code-ide-focus-claude-after-ediff t)
(claude-code-ide-emacs-tools-setup)) ; Optionally enable Emacs MCP tools(package! shell-maker)
(package! acp)
(package! agent-shell)(require 'acp)
(require 'agent-shell)
(use-package agent-shell)