diff options
| author | cashmere <cashmere@autistici.org> | 2026-08-13 16:24:59 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-08-13 16:24:59 +0200 |
| commit | e82a2d3fb339b787440f5581a0426925b7780d6a (patch) | |
| tree | 8ad7243379678baa58b6652db84514a294708a37 | |
| parent | 894fe6ec74947627966384190f501c450789198f (diff) | |
| download | emacs-e82a2d3fb339b787440f5581a0426925b7780d6a.tar.gz | |
md: 2026-08-13T16:25:03+02:00
| -rw-r--r-- | config.el | 412 | ||||
| -rw-r--r-- | config.org | 459 | ||||
| -rw-r--r-- | themes/tinty-theme.el | 2 |
3 files changed, 418 insertions, 455 deletions
@@ -1,8 +1,4 @@ ;;; config.el --- Emacs-Kick --- A feature rich Emacs config for (neo)vi(m)mers -*- lexical-binding: t; -*- -(setenv "LSP_USE_PLISTS" "true") -;; (setq debug-on-error t) -;; (setenv "LSP_USE_PLISTS" "true") -;; (setq lsp-use-plists t) (setq pgtk-wait-for-event-timeout 0.001) (setq package-enable-at-startup nil) ;; (setq-default mode-line-format t) ;; disabled: boolean t is not valid for mode-line-format @@ -1115,23 +1111,6 @@ TITLE names the new denote-style file in ~/org." -(use-package kitty-graphics - :ensure (:host github :repo "cashmeredev/kitty-graphics.el") - :demand t - :custom - (kitty-graphics-enable-video t) - (kitty-graphics-shr-scale 'fit) - (kitty-graphics-shr-fit-width 0.4) - (kitty-graphics-shr-fit-height 20) - (kitty-graphics-doc-view-resolution-scale 2.0) - :hook (dired-mode . kitty-graphics-dired-auto-preview-mode) - :config - (setq kitty-graphics-enable-browser t - kitty-graphics-casty-program "~/projects/casty/bin/casty.js" - kitty-graphics-casty-chrome "helium-browser") - - (kitty-graphics-setup)) - (with-eval-after-load 'org (setq org-confirm-babel-evaluate nil) (org-babel-do-load-languages @@ -1492,10 +1471,6 @@ Skips capture tasks and projects." (defun my/org-capture-meeting-to-journal () (my/org-capture-to-journal "\n* Meeting: ")) -(with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(org-mode . ("harper-ls" "--stdio")))) - (defun my/org-capture-task-to-journal () (let* ((context (read-string "Task: ")) (todo-state (completing-read "TODO state: " @@ -2239,16 +2214,22 @@ Timers that expired while Emacs was closed fire immediately." (use-package company :ensure t + :demand t :hook (after-init . global-company-mode) :custom - (company-idle-delay 0) - (company-tooltip-idle-delay 0) - (company-minimum-prefix-length 2) + (company-idle-delay nil) + ;; (company-minimum-prefix-length 2) (company-require-match nil) - (company-frontends - '(company-pseudo-tooltip-unless-just-one-frontend - company-echo-metadata-frontend)) - (company-backends '(company-capf))) + (company-transformers '(my/company-limit-candidates)) + (company-backends '(company-capf)) + :config + (defun my/company-limit-candidates (candidates) + (seq-take candidates 4)) + + (define-key company-mode-map [remap indent-for-tab-command] + #'company-manual-begin) + (define-key company-mode-map [remap c-indent-line-or-region] + #'company-manual-begin)) (use-package which-key :ensure t @@ -2273,13 +2254,7 @@ Timers that expired while Emacs was closed fire immediately." ;; used by `completion-at-point'. The order of the functions matters, the ;; first function returning a result wins. Note that the list of buffer-local ;; completion functions takes precedence over the global list. - (add-hook 'completion-at-point-functions #'cape-file) - ;; (add-hook 'completion-at-point-functions #'cape-history) - ;; ... - :config - (with-eval-after-load 'eglot - (advice-add 'eglot-completion-at-point :around #'cape-wrap-noninterruptible) - (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster))) + (add-hook 'completion-at-point-functions #'cape-file)) (use-package embark :ensure (:wait t) @@ -2319,7 +2294,6 @@ Timers that expired while Emacs was closed fire immediately." "\\`\\*scratch\\*\\'" "\\`\\*Completions\\*\\'" "\\`\\*Flymake" - "\\`\\*lsp-help\\*\\'" "\\`\\*eldoc" "\\`\\*elpaca-log\\*\\'" "\\`\\*Native-compile-Log\\*\\'" @@ -2336,47 +2310,191 @@ Timers that expired while Emacs was closed fire immediately." (use-package citre :ensure t - :defer t + :demand t :init (require 'citre-config) :custom (citre-default-create-tags-file-location 'in-dir) (citre-edit-ctags-options-manually nil) (citre-auto-enable-citre-mode-modes '(prog-mode)) - :bind (("C-c e" . citre-jump) - ("C-c u" . citre-update-this-tags-file))) - -(use-package eglot - :ensure nil :config - ;; nil: LSP server stays alive when last managed buffer closes. - ;; Verhindert churn bei org src edit buffers (C-c ' macht buffer auf/zu - ;; → mit t neuer pyrefly process jedes Mal). - (setq eglot-autoshutdown nil) - (setq eglot-send-changes-idle-time 0.1) - (setq eglot-sync-connect nil) - (setq eglot-connect-timeout 30) - (setq eglot-events-buffer-size 0) - (setq eglot-report-progress nil) - (setq jsonrpc-default-request-timeout 10)) - - ;; (add-hook 'eglot-managed-mode-hook - ;; (lambda () - ;; (eglot-inlay-hints-mode -1))) + (defconst my/project-ctags-nix-options + '("--langdef=nix" + "--map-nix=+.nix" + "--kinddef-nix=a,attribute,attributes" + "--regex-nix=/^[[:space:]]*([A-Za-z_][A-Za-z0-9_'-]*(\\.[A-Za-z_][A-Za-z0-9_'-]+)+)[[:space:]]*=[^=]/\\1/a/" + "--regex-nix=/^[[:space:]]*([A-Za-z_][A-Za-z0-9_'-]*)[[:space:]]*=[^=]/\\1/a/")) + + (defun my/project-ctags-root () + (or (projectile-project-root) + (user-error "Not in a Projectile project"))) + + (defun my/project-ctags-nixpkgs (root) + (when (and (file-directory-p (expand-file-name "npins" root)) + (executable-find "npins")) + (let ((default-directory root) + (output (generate-new-buffer " *npins*"))) + (unwind-protect + (when (zerop (process-file "npins" nil output nil + "get-path" "nixpkgs")) + (with-current-buffer output + (string-trim (buffer-string)))) + (kill-buffer output))))) + + (defun my/project-ctags-nixos-options (nixpkgs output) + (let ((expression + (format + "let + nixpkgs = %S; + evaluated = import (nixpkgs + \"/nixos/lib/eval-config.nix\") { + system = builtins.currentSystem; + modules = [ ]; + }; + collect = prefix: attrs: + builtins.concatLists (builtins.map + (name: + let value = attrs.${name}; in + if builtins.isAttrs value && (value._type or null) == \"option\" + then + if value.declarations == [ ] then [ ] else [ + [ + (builtins.concatStringsSep \".\" (prefix ++ [ name ])) + (builtins.toString (builtins.head value.declarations)) + ] + ] + else if builtins.isAttrs value then collect (prefix ++ [ name ]) value + else [ ]) + (builtins.attrNames attrs)); +in + builtins.concatStringsSep \"\\n\" (builtins.map builtins.toJSON (collect [ ] evaluated.options))" + nixpkgs))) + (with-current-buffer output + (erase-buffer)) + (if (zerop (process-file "nix-instantiate" nil output nil + "--eval" "--raw" "--strict" + "--expr" expression)) + (mapcar (lambda (line) + (json-parse-string line :array-type 'list)) + (split-string (with-current-buffer output + (buffer-string)) + "\n" t)) + (user-error "Could not evaluate NixOS options")))) - ;; (setq eglot-code-action-indications '(eldoc-hint)) + (defun my/project-ctags-add-nixos-options (tags-file root output) + (when-let* ((nixpkgs (my/project-ctags-nixpkgs root))) + (condition-case err + (let (headers tags) + (dolist (line (with-temp-buffer + (insert-file-contents tags-file) + (split-string (buffer-string) "\n" t))) + (if (string-prefix-p "!" line) + (push line headers) + (push line tags))) + (dolist (option (my/project-ctags-nixos-options nixpkgs output)) + (pcase-let ((`(,name ,file) option)) + (push (format "%s\t%s\t1;\"\tkind:attribute\tlanguage:nix\troles:def" + name file) + tags))) + (with-temp-file tags-file + (dolist (line (nreverse headers)) + (insert line "\n")) + (dolist (line (sort (delete-dups tags) #'string<)) + (insert line "\n")))) + (error + (message "NixOS option tags unavailable: %s" + (error-message-string err)))))) - ;; (setq eglot-ignored-server-capabilities - ;; '(:inlayHintProvider :documentHighlightProvider))) + (defun my/project-ctags-files (root) + (let ((default-directory root) + files) + (dolist (file (projectile-current-project-files)) + (unless (string= file ".tags") + (when (file-regular-p (expand-file-name file root)) + (push (expand-file-name file root) files)))) + (nreverse files))) + + + (defun my/project-ctags-enable-citre (root) + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when (and buffer-file-name + (file-in-directory-p buffer-file-name root)) + (citre-mode 1))))) + (defun my/project-ctags-start (root) + (let* ((ctags (executable-find "ctags")) + (tags-file (expand-file-name ".tags" root)) + (list-file (make-temp-file "citre-files-")) + (temporary-tags-file (make-temp-file "citre-tags-")) + (output (get-buffer-create "*citre-ctags*")) + (files (my/project-ctags-files root))) + (unless ctags + (user-error "Universal Ctags is not available")) + (unless files + (user-error "No project files to tag")) + (with-temp-file list-file + (dolist (file files) + (insert file "\n"))) + (with-current-buffer output + (erase-buffer)) + (make-process + :name (format "ctags:%s" (file-name-nondirectory (directory-file-name root))) + :buffer output + :command (append + (list ctags + "--options=NONE" + "--output-format=e-ctags") + my/project-ctags-nix-options + (list "--languages=all" + "--kinds-all=*" + "--fields=*" + "--extras=*" + "-f" + temporary-tags-file + "-L" + list-file)) + :connection-type 'pipe + :sentinel + (lambda (process _event) + (when (memq (process-status process) '(exit signal)) + (unwind-protect + (if (and (eq (process-status process) 'exit) + (zerop (process-exit-status process))) + (progn + (my/project-ctags-add-nixos-options + temporary-tags-file root output) + (rename-file temporary-tags-file tags-file t) + (citre-clear-tags-file-cache) + (my/project-ctags-enable-citre root) + (message "Ctags ready for %s" root)) + (message "Ctags failed for %s; see %s" + root + (buffer-name output))) + (when (file-exists-p list-file) + (delete-file list-file)) + (when (file-exists-p temporary-tags-file) + (delete-file temporary-tags-file)))))) + (message "Generating Ctags for %s..." root))) + + (defun my/project-ctags-setup () + (interactive) + (let* ((root (my/project-ctags-root)) + (tags-file (expand-file-name ".tags" root))) + (if (file-exists-p tags-file) + (my/project-ctags-start root) + (when (y-or-n-p (format "Set up Ctags for %s? " root)) + (my/project-ctags-start root))))) + + (with-eval-after-load 'evil + (evil-define-key '(normal motion) 'global + (kbd "g d") #'citre-jump + (kbd "g D") #'citre-query-jump + (kbd "g p") #'citre-peek + (kbd "g r") #'citre-jump-to-reference + (kbd "g b") #'citre-jump-back))) (use-package typst-ts-mode :ensure t - :mode "\\.typ\\'" - :hook (typst-ts-mode . eglot-ensure) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - `(typst-ts-mode . ,(eglot-alternatives '("tinymist" "lsp")))))) + :mode "\\.typ\\'") (use-package yasnippet :ensure t @@ -2391,12 +2509,7 @@ Timers that expired while Emacs was closed fire immediately." :ensure t :mode "\\.nix\\'" :config - (modify-syntax-entry ?. "_" nix-ts-mode--syntax-table) - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(nix-ts-mode . ("nixd"))))) - -(put 'eglot-workspace-configuration 'safe-local-variable #'listp) + (modify-syntax-entry ?. "_" nix-ts-mode--syntax-table)) (with-eval-after-load 'compile (dolist (rule '((nix-def "^\\s-*- In [`']\\(/[^'\n]+\\)'" 1) @@ -2428,25 +2541,15 @@ Timers that expired while Emacs was closed fire immediately." :ensure nil :mode (("\\.c\\'" . c-ts-mode) ("\\.h\\'" . c-ts-mode)) - :hook (c-ts-mode . eglot-ensure) :custom (c-ts-mode-indent-offset 2) - (c-ts-mode-indent-style 'k&r) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '((c-ts-mode c-mode) . - ("clangd" - "--background-index" - "--clang-tidy" - "--header-insertion=never"))))) + (c-ts-mode-indent-style 'k&r)) (use-package c3-ts-mode :ensure (:host github :repo "c3lang/c3-ts-mode") :mode (("\\.c3\\'" . c3-ts-mode) ("\\.c3i\\'" . c3-ts-mode) ("\\.c3t\\'" . c3-ts-mode)) - :hook (c3-ts-mode . eglot-ensure) :custom (c3-ts-mode-indent-offset 2) (setq treesit-font-lock-level 4) @@ -2455,24 +2558,7 @@ Timers that expired while Emacs was closed fire immediately." '(c3 "https://github.com/c3lang/tree-sitter-c3")) :config (unless (treesit-language-available-p 'c3) - (treesit-install-language-grammar 'c3)) - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(c3-ts-mode . ("c3-lsp"))))) - -(use-package python - :ensure nil - :mode ("\\.py\\'" . python-ts-mode) - :hook (python-ts-mode . eglot-ensure) - :custom - (python-indent-offset 4) - (python-indent-guess-indent-offset-verbose nil) - (python-shell-interpreter "python3") - (python-shell-completion-native-enable nil) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '((python-ts-mode python-mode) . ("rass" "--" "pyrefly" "lsp" "--" "ruff" "server"))))) + (treesit-install-language-grammar 'c3))) (use-package pyvenv :ensure t @@ -2483,37 +2569,11 @@ Timers that expired while Emacs was closed fire immediately." (lambda () (pyvenv-mode 1)))) -(use-package rust-ts-mode - :ensure nil - :mode "\\.rs\\'" - :hook (rust-ts-mode . eglot-ensure) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(rust-ts-mode . - ("rust-analyzer" :initializationOptions - (:check (:command "clippy"))))))) - (use-package js :ensure nil :mode ("\\.js\\'" . js-ts-mode) - :hook (js-ts-mode . eglot-ensure) - :config - (setq js-indent-level 2) - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(js-ts-mode . ("rass" "--" "typescript-language-server" "--stdio" "--" "vscode-eslint-language-server" "--stdio"))))) - -(use-package typescript-ts-mode - :ensure nil - :mode (("\\.ts\\'" . typescript-ts-mode) - ("\\.tsx\\'" . tsx-ts-mode)) - :hook ((typescript-ts-mode . eglot-ensure) - (tsx-ts-mode . eglot-ensure)) :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '((typescript-ts-mode tsx-ts-mode) . ("rass" "--" "typescript-language-server" "--stdio" "--" "vscode-eslint-language-server" "--stdio"))))) + (setq js-indent-level 2)) (use-package xonsh-mode :ensure (:host github :repo "seanfarley/xonsh-mode") @@ -2530,38 +2590,6 @@ Xonsh is a python superset; reuse python tree-sitter parser." (add-to-list 'auto-mode-alist '("\\.xsh\\'" . xonsh-ts-mode)) (add-to-list 'auto-mode-alist '("\\.xonshrc\\'" . xonsh-ts-mode)))) -(defun my/org-src-eglot-python () - "Give org src edit buffers a stable temp file name so eglot can attach. -Name derived from owning org file path → identical across re-opens of -the same src block file, so eglot reuses the existing connection -instead of churning." - (when (and (derived-mode-p 'python-ts-mode 'python-mode) - (not buffer-file-name) - (bound-and-true-p org-src-mode)) - (let* ((parent (or (buffer-file-name (org-src-source-buffer)) - (buffer-name (org-src-source-buffer)) - "scratch")) - (root (or (and (project-current) - (project-root (project-current))) - temporary-file-directory)) - (name (format "ob-src-%s.py" - (substring (md5 parent) 0 8)))) - (setq-local buffer-file-name (expand-file-name name root)) - (set-buffer-modified-p nil) - (eglot-ensure)))) - -(add-hook 'org-src-mode-hook #'my/org-src-eglot-python) - -(with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '((xonsh-mode xonsh-ts-mode) - . ("rass" "--" "pyrefly" "lsp" "--" "ruff" "server")))) - -;; kein auto-eglot fuer xonsh — pyrefly.toml waere noetig. Manuell via -;; M-x my/pyrefly-here-xonsh (legt temp pyrefly.toml an + startet eglot). -;; (add-hook 'xonsh-mode-hook #'eglot-ensure) -;; (add-hook 'xonsh-ts-mode-hook #'eglot-ensure) - (defvar my/ob-xonsh-installer (lambda () (defun org-babel-execute:xonsh (body params) @@ -2590,38 +2618,18 @@ BODY is the xonsh script. PARAMS may include :dir and :cmdline." (use-package yaml-ts-mode :ensure nil - :mode "\\.ya?ml\\'" - :hook (yaml-ts-mode . eglot-ensure)) + :mode "\\.ya?ml\\'") (use-package bash-ts-mode :ensure nil :mode (("\\.sh\\'" . bash-ts-mode) ("\\.bash\\'" . bash-ts-mode) ("\\.bashrc\\'" . bash-ts-mode) - ("\\.bash_profile\\'" . bash-ts-mode)) - :hook (bash-ts-mode . eglot-ensure) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(bash-ts-mode . ("bash-language-server" "start")))) - - (defun sh-mode-setup () - (add-hook 'before-save-hook #'eglot-format-buffer -10 t)) - - (add-hook 'bash-ts-mode-hook #'sh-mode-setup)) + ("\\.bash_profile\\'" . bash-ts-mode))) (use-package just-mode :ensure t - :mode ("[Jj]ustfile\\'" "\\.just\\'") - :hook (just-mode . eglot-ensure) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(just-mode . ("just-lsp"))))) - -;; (use-package justl -;; :ensure t -;; :after just-mode) + :mode ("[Jj]ustfile\\'" "\\.just\\'")) (use-package olivetti :ensure t @@ -3144,14 +3152,7 @@ still require a restart since elpaca queues run at init time." "^\\*Help\\*$" "^\\*Apropos\\*$" "^\\*info\\*$" - "^\\*Async-native-compile-log\\*$" - - ;; LSP Buffers - "^\\*lsp-log\\*$" - "^\\*clojure-lsp\\*$" - "^\\*clojure-lsp::stderr\\*$" - "^\\*ts-ls\\*$" - "^\\*ts-ls::stderr\\*$")) + "^\\*Async-native-compile-log\\*$")) (use-package projectile :ensure (:wait t) @@ -3171,7 +3172,7 @@ still require a restart since elpaca queues run at init time." :after (helm projectile) :commands (helm-projectile-find-file helm-projectile-switch-project) :custom - (helm-projectile-fuzzy-match t)) + (helm-projectile-fuzzy-match nil)) (use-package wgrep :ensure t @@ -3568,12 +3569,7 @@ place. `C-c C-c' commits, `C-c C-k' aborts." (defun my/format-buffer () (interactive) - (cond - ((eq major-mode 'rust-ts-mode) (eglot-format-buffer)) - ((eq major-mode 'python-ts-mode) (eglot-format-buffer)) - ((eq major-mode 'c-mode) (eglot-format-buffer)) - ((bound-and-true-p eglot--managed-mode) (eglot-format-buffer)) - (t (message "No formatter for %s" major-mode)))) + (user-error "No formatter configured for %s" major-mode)) (defun my/find-file-or-switch-project () "Find a file in the current project; outside one, pick a project first. @@ -3640,6 +3636,7 @@ workspace (e.g. *scratch*)." "pa" '(projectile-add-known-project :wk "add project") "pi" '(projectile-invalidate-cache :wk "invalidate cache") "pt" '(projectile-run-task :wk "tasks") + "pc" '(my/project-ctags-setup :wk "setup/update ctags") "g" '(:ignore t :wk "git") "gc" '(magit-clone :wk "clone") @@ -3651,11 +3648,8 @@ workspace (e.g. *scratch*)." (let ((current-prefix-arg 4)) (call-interactively #'xref-find-definitions))) :wk "definition other window") - "gI" '((lambda () (interactive) - (let ((current-prefix-arg 4)) - (call-interactively #'eglot-find-implementation))) - :wk "implementation other window") - "gt" '(eglot-find-typeDefinition :wk "go to type definition") + "gI" '(citre-query-jump :wk "find definition") + "gt" '(citre-peek :wk "peek definition") "gr" '(xref-find-references :wk "find references") "gs" '(magit-file-stage :wk "stage file") "gu" '(my/magit-uncommit :wk "uncommit (keep & unstage)") @@ -3699,9 +3693,9 @@ workspace (e.g. *scratch*)." "c" '(:ignore t :wk "code") "cc" '(my/compile-or-recompile :wk "compile") "cC" '(ghostel-compile :wk "recompile") - "ca" '(eglot-code-actions :wk "code actions") - "cr" '(eglot-rename :wk "lsp rename") - "cf" '(eglot-format :wk "format buffer") + "ca" '(citre-query-jump :wk "find definition") + "cr" '(query-replace :wk "replace") + "cf" '(my/format-buffer :wk "format buffer") "cs" '(yas-insert-snippet :wk "snippets") "cl" '(flycheck-list-errors :wk "list errors") @@ -7,10 +7,6 @@ #+begin_src emacs-lisp ;;; config.el --- Emacs-Kick --- A feature rich Emacs config for (neo)vi(m)mers -*- lexical-binding: t; -*- -(setenv "LSP_USE_PLISTS" "true") -;; (setq debug-on-error t) -;; (setenv "LSP_USE_PLISTS" "true") -;; (setq lsp-use-plists t) (setq pgtk-wait-for-event-timeout 0.001) (setq package-enable-at-startup nil) ;; (setq-default mode-line-format t) ;; disabled: boolean t is not valid for mode-line-format @@ -1271,7 +1267,7 @@ TITLE names the new denote-style file in ~/org." #+begin_src emacs-lisp #+end_src -** kitty-graphics +** COMMENT kitty-graphics Display inline images in terminal Emacs via the Kitty graphics protocol. Only activates when running in a Kitty terminal (not in GUI Emacs). @@ -1727,11 +1723,6 @@ Skips capture tasks and projects." ** harper -#+begin_src emacs-lisp -(with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(org-mode . ("harper-ls" "--stdio")))) -#+end_src * org-capture @@ -2588,16 +2579,22 @@ Auto switch silos (different workspaces/folders) based on preconfigured director #+begin_src emacs-lisp :tangle yes (use-package company :ensure t + :demand t :hook (after-init . global-company-mode) :custom - (company-idle-delay 0) - (company-tooltip-idle-delay 0) - (company-minimum-prefix-length 2) + (company-idle-delay nil) + ;; (company-minimum-prefix-length 2) (company-require-match nil) - (company-frontends - '(company-pseudo-tooltip-unless-just-one-frontend - company-echo-metadata-frontend)) - (company-backends '(company-capf))) + (company-transformers '(my/company-limit-candidates)) + (company-backends '(company-capf)) + :config + (defun my/company-limit-candidates (candidates) + (seq-take candidates 4)) + + (define-key company-mode-map [remap indent-for-tab-command] + #'company-manual-begin) + (define-key company-mode-map [remap c-indent-line-or-region] + #'company-manual-begin)) #+end_src * which-key @@ -2630,13 +2627,7 @@ Auto switch silos (different workspaces/folders) based on preconfigured director ;; used by `completion-at-point'. The order of the functions matters, the ;; first function returning a result wins. Note that the list of buffer-local ;; completion functions takes precedence over the global list. - (add-hook 'completion-at-point-functions #'cape-file) - ;; (add-hook 'completion-at-point-functions #'cape-history) - ;; ... - :config - (with-eval-after-load 'eglot - (advice-add 'eglot-completion-at-point :around #'cape-wrap-noninterruptible) - (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster))) + (add-hook 'completion-at-point-functions #'cape-file)) #+end_src * embark @@ -2684,7 +2675,6 @@ Auto switch silos (different workspaces/folders) based on preconfigured director "\\`\\*scratch\\*\\'" "\\`\\*Completions\\*\\'" "\\`\\*Flymake" - "\\`\\*lsp-help\\*\\'" "\\`\\*eldoc" "\\`\\*elpaca-log\\*\\'" "\\`\\*Native-compile-Log\\*\\'" @@ -2709,57 +2699,198 @@ Auto switch silos (different workspaces/folders) based on preconfigured director #+begin_src emacs-lisp (use-package citre :ensure t - :defer t + :demand t :init (require 'citre-config) :custom (citre-default-create-tags-file-location 'in-dir) (citre-edit-ctags-options-manually nil) (citre-auto-enable-citre-mode-modes '(prog-mode)) - :bind (("C-c e" . citre-jump) - ("C-c u" . citre-update-this-tags-file))) -#+end_src + :config + (defconst my/project-ctags-nix-options + '("--langdef=nix" + "--map-nix=+.nix" + "--kinddef-nix=a,attribute,attributes" + "--regex-nix=/^[[:space:]]*([A-Za-z_][A-Za-z0-9_'-]*(\\.[A-Za-z_][A-Za-z0-9_'-]+)+)[[:space:]]*=[^=]/\\1/a/" + "--regex-nix=/^[[:space:]]*([A-Za-z_][A-Za-z0-9_'-]*)[[:space:]]*=[^=]/\\1/a/")) -* programming + (defun my/project-ctags-root () + (or (projectile-project-root) + (user-error "Not in a Projectile project"))) -** eglot + (defun my/project-ctags-nixpkgs (root) + (when (and (file-directory-p (expand-file-name "npins" root)) + (executable-find "npins")) + (let ((default-directory root) + (output (generate-new-buffer " *npins*"))) + (unwind-protect + (when (zerop (process-file "npins" nil output nil + "get-path" "nixpkgs")) + (with-current-buffer output + (string-trim (buffer-string)))) + (kill-buffer output))))) -#+begin_src emacs-lisp -(use-package eglot - :ensure nil - :config - ;; nil: LSP server stays alive when last managed buffer closes. - ;; Verhindert churn bei org src edit buffers (C-c ' macht buffer auf/zu - ;; → mit t neuer pyrefly process jedes Mal). - (setq eglot-autoshutdown nil) - (setq eglot-send-changes-idle-time 0.1) - (setq eglot-sync-connect nil) - (setq eglot-connect-timeout 30) - (setq eglot-events-buffer-size 0) - (setq eglot-report-progress nil) - (setq jsonrpc-default-request-timeout 10)) - - ;; (add-hook 'eglot-managed-mode-hook - ;; (lambda () - ;; (eglot-inlay-hints-mode -1))) + (defun my/project-ctags-nixos-options (nixpkgs output) + (let ((expression + (format + "let + nixpkgs = %S; + evaluated = import (nixpkgs + \"/nixos/lib/eval-config.nix\") { + system = builtins.currentSystem; + modules = [ ]; + }; + collect = prefix: attrs: + builtins.concatLists (builtins.map + (name: + let value = attrs.${name}; in + if builtins.isAttrs value && (value._type or null) == \"option\" + then + if value.declarations == [ ] then [ ] else [ + [ + (builtins.concatStringsSep \".\" (prefix ++ [ name ])) + (builtins.toString (builtins.head value.declarations)) + ] + ] + else if builtins.isAttrs value then collect (prefix ++ [ name ]) value + else [ ]) + (builtins.attrNames attrs)); +in + builtins.concatStringsSep \"\\n\" (builtins.map builtins.toJSON (collect [ ] evaluated.options))" + nixpkgs))) + (with-current-buffer output + (erase-buffer)) + (if (zerop (process-file "nix-instantiate" nil output nil + "--eval" "--raw" "--strict" + "--expr" expression)) + (mapcar (lambda (line) + (json-parse-string line :array-type 'list)) + (split-string (with-current-buffer output + (buffer-string)) + "\n" t)) + (user-error "Could not evaluate NixOS options")))) - ;; (setq eglot-code-action-indications '(eldoc-hint)) + (defun my/project-ctags-add-nixos-options (tags-file root output) + (when-let* ((nixpkgs (my/project-ctags-nixpkgs root))) + (condition-case err + (let (headers tags) + (dolist (line (with-temp-buffer + (insert-file-contents tags-file) + (split-string (buffer-string) "\n" t))) + (if (string-prefix-p "!" line) + (push line headers) + (push line tags))) + (dolist (option (my/project-ctags-nixos-options nixpkgs output)) + (pcase-let ((`(,name ,file) option)) + (push (format "%s\t%s\t1;\"\tkind:attribute\tlanguage:nix\troles:def" + name file) + tags))) + (with-temp-file tags-file + (dolist (line (nreverse headers)) + (insert line "\n")) + (dolist (line (sort (delete-dups tags) #'string<)) + (insert line "\n")))) + (error + (message "NixOS option tags unavailable: %s" + (error-message-string err)))))) - ;; (setq eglot-ignored-server-capabilities - ;; '(:inlayHintProvider :documentHighlightProvider))) + (defun my/project-ctags-files (root) + (let ((default-directory root) + files) + (dolist (file (projectile-current-project-files)) + (unless (string= file ".tags") + (when (file-regular-p (expand-file-name file root)) + (push (expand-file-name file root) files)))) + (nreverse files))) + + + (defun my/project-ctags-enable-citre (root) + (dolist (buffer (buffer-list)) + (with-current-buffer buffer + (when (and buffer-file-name + (file-in-directory-p buffer-file-name root)) + (citre-mode 1))))) + (defun my/project-ctags-start (root) + (let* ((ctags (executable-find "ctags")) + (tags-file (expand-file-name ".tags" root)) + (list-file (make-temp-file "citre-files-")) + (temporary-tags-file (make-temp-file "citre-tags-")) + (output (get-buffer-create "*citre-ctags*")) + (files (my/project-ctags-files root))) + (unless ctags + (user-error "Universal Ctags is not available")) + (unless files + (user-error "No project files to tag")) + (with-temp-file list-file + (dolist (file files) + (insert file "\n"))) + (with-current-buffer output + (erase-buffer)) + (make-process + :name (format "ctags:%s" (file-name-nondirectory (directory-file-name root))) + :buffer output + :command (append + (list ctags + "--options=NONE" + "--output-format=e-ctags") + my/project-ctags-nix-options + (list "--languages=all" + "--kinds-all=*" + "--fields=*" + "--extras=*" + "-f" + temporary-tags-file + "-L" + list-file)) + :connection-type 'pipe + :sentinel + (lambda (process _event) + (when (memq (process-status process) '(exit signal)) + (unwind-protect + (if (and (eq (process-status process) 'exit) + (zerop (process-exit-status process))) + (progn + (my/project-ctags-add-nixos-options + temporary-tags-file root output) + (rename-file temporary-tags-file tags-file t) + (citre-clear-tags-file-cache) + (my/project-ctags-enable-citre root) + (message "Ctags ready for %s" root)) + (message "Ctags failed for %s; see %s" + root + (buffer-name output))) + (when (file-exists-p list-file) + (delete-file list-file)) + (when (file-exists-p temporary-tags-file) + (delete-file temporary-tags-file)))))) + (message "Generating Ctags for %s..." root))) + + (defun my/project-ctags-setup () + (interactive) + (let* ((root (my/project-ctags-root)) + (tags-file (expand-file-name ".tags" root))) + (if (file-exists-p tags-file) + (my/project-ctags-start root) + (when (y-or-n-p (format "Set up Ctags for %s? " root)) + (my/project-ctags-start root))))) + + (with-eval-after-load 'evil + (evil-define-key '(normal motion) 'global + (kbd "g d") #'citre-jump + (kbd "g D") #'citre-query-jump + (kbd "g p") #'citre-peek + (kbd "g r") #'citre-jump-to-reference + (kbd "g b") #'citre-jump-back))) #+end_src +* programming + + ** typst-mode #+begin_src emacs-lisp (use-package typst-ts-mode :ensure t - :mode "\\.typ\\'" - :hook (typst-ts-mode . eglot-ensure) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - `(typst-ts-mode . ,(eglot-alternatives '("tinymist" "lsp")))))) + :mode "\\.typ\\'") #+end_src ** yasnippet @@ -2782,17 +2913,9 @@ Auto switch silos (different workspaces/folders) based on preconfigured director :ensure t :mode "\\.nix\\'" :config - (modify-syntax-entry ?. "_" nix-ts-mode--syntax-table) - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(nix-ts-mode . ("nixd"))))) + (modify-syntax-entry ?. "_" nix-ts-mode--syntax-table)) #+end_src -*** mark eglot-workspace-configuration safe - -#+begin_src emacs-lisp -(put 'eglot-workspace-configuration 'safe-local-variable #'listp) -#+end_src *** compilation enhancements @@ -2826,30 +2949,21 @@ Auto switch silos (different workspaces/folders) based on preconfigured director ** c-ts-mode -Built-in tree-sitter mode (Emacs 29+) driven by =clangd=. The =c= grammar ships via =treesit-grammars.with-all-grammars= in =emacs.nix=, so no on-the-fly compilation is needed. =clangd= picks up include paths and flags from a =compile_commands.json= in the project root (generate it with =bear -- make=, =cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON=, or =meson=); without one it falls back to sane defaults but cross-file navigation is limited. +Built-in tree-sitter mode (Emacs 29+). The =c= grammar ships via =treesit-grammars.with-all-grammars= in =emacs.nix=, so no on-the-fly compilation is needed. #+begin_src emacs-lisp (use-package c-ts-mode :ensure nil :mode (("\\.c\\'" . c-ts-mode) ("\\.h\\'" . c-ts-mode)) - :hook (c-ts-mode . eglot-ensure) :custom (c-ts-mode-indent-offset 2) - (c-ts-mode-indent-style 'k&r) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '((c-ts-mode c-mode) . - ("clangd" - "--background-index" - "--clang-tidy" - "--header-insertion=never"))))) + (c-ts-mode-indent-style 'k&r)) #+end_src ** c3-ts-mode -Not on MELPA, so installed straight from GitHub. The tree-sitter grammar is built on first load via =treesit-install-language-grammar= (needs =cc= / =gcc= on PATH), and eglot drives the =c3-lsp= server. On NixOS install =c3c=, =c3-lsp= and =gcc=. +Not on MELPA, so installed straight from GitHub. The tree-sitter grammar is built on first load via =treesit-install-language-grammar= (needs =cc= / =gcc= on PATH). #+begin_src emacs-lisp (use-package c3-ts-mode @@ -2857,7 +2971,6 @@ Not on MELPA, so installed straight from GitHub. The tree-sitter grammar is buil :mode (("\\.c3\\'" . c3-ts-mode) ("\\.c3i\\'" . c3-ts-mode) ("\\.c3t\\'" . c3-ts-mode)) - :hook (c3-ts-mode . eglot-ensure) :custom (c3-ts-mode-indent-offset 2) (setq treesit-font-lock-level 4) @@ -2866,30 +2979,21 @@ Not on MELPA, so installed straight from GitHub. The tree-sitter grammar is buil '(c3 "https://github.com/c3lang/tree-sitter-c3")) :config (unless (treesit-language-available-p 'c3) - (treesit-install-language-grammar 'c3)) - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(c3-ts-mode . ("c3-lsp"))))) + (treesit-install-language-grammar 'c3))) #+end_src ** python-ts-mode Because I have to and not want to. -#+begin_src emacs-lisp (use-package python :ensure nil :mode ("\\.py\\'" . python-ts-mode) - :hook (python-ts-mode . eglot-ensure) :custom (python-indent-offset 4) (python-indent-guess-indent-offset-verbose nil) (python-shell-interpreter "python3") - (python-shell-completion-native-enable nil) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '((python-ts-mode python-mode) . ("rass" "--" "pyrefly" "lsp" "--" "ruff" "server"))))) + (python-shell-completion-native-enable nil)) #+end_src *** pyvenv @@ -2909,23 +3013,15 @@ Because I have to and not want to. ily <3. -#+begin_src emacs-lisp (use-package rust-ts-mode :ensure nil - :mode "\\.rs\\'" - :hook (rust-ts-mode . eglot-ensure) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(rust-ts-mode . - ("rust-analyzer" :initializationOptions - (:check (:command "clippy"))))))) + :mode "\\.rs\\'") #+end_src ** JavaScript / TypeScript / Vue -JavaScript and TypeScript support via tree-sitter modes and [[https://github.com/joaotavora/rassumfrassum][rassumfrassum]]. The =rass tslint= preset multiplexes =typescript-language-server= + =eslint=. -Vue files use =web-mode= with the =rass vuetail= preset (=vue-language-server= + =tailwindcss-language-server=). +JavaScript and TypeScript support via tree-sitter modes. +Vue files use =web-mode=. *** js-ts-mode @@ -2933,29 +3029,18 @@ Vue files use =web-mode= with the =rass vuetail= preset (=vue-language-server= + (use-package js :ensure nil :mode ("\\.js\\'" . js-ts-mode) - :hook (js-ts-mode . eglot-ensure) :config - (setq js-indent-level 2) - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(js-ts-mode . ("rass" "--" "typescript-language-server" "--stdio" "--" "vscode-eslint-language-server" "--stdio"))))) + (setq js-indent-level 2)) #+end_src *** typescript-ts-mode Uses built-in tree-sitter modes for =.ts= and =.tsx= files. -#+begin_src emacs-lisp (use-package typescript-ts-mode :ensure nil :mode (("\\.ts\\'" . typescript-ts-mode) - ("\\.tsx\\'" . tsx-ts-mode)) - :hook ((typescript-ts-mode . eglot-ensure) - (tsx-ts-mode . eglot-ensure)) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '((typescript-ts-mode tsx-ts-mode) . ("rass" "--" "typescript-language-server" "--stdio" "--" "vscode-eslint-language-server" "--stdio"))))) + ("\\.tsx\\'" . tsx-ts-mode))) #+end_src ** Xonsh @@ -2989,55 +3074,7 @@ Xonsh is a python superset; reuse python tree-sitter parser." (add-to-list 'auto-mode-alist '("\\.xonshrc\\'" . xonsh-ts-mode)))) #+end_src -*** LSP in org src edit buffers - -=C-c '= opens indirect buffer without =buffer-file-name= → eglot -refuses to start. Hack: assign temp =.py= path so pyrefly/ruff -attach. Works for python AND xonsh src blocks (both route to -python-ts-mode LSP). - -#+begin_src emacs-lisp -(defun my/org-src-eglot-python () - "Give org src edit buffers a stable temp file name so eglot can attach. -Name derived from owning org file path → identical across re-opens of -the same src block file, so eglot reuses the existing connection -instead of churning." - (when (and (derived-mode-p 'python-ts-mode 'python-mode) - (not buffer-file-name) - (bound-and-true-p org-src-mode)) - (let* ((parent (or (buffer-file-name (org-src-source-buffer)) - (buffer-name (org-src-source-buffer)) - "scratch")) - (root (or (and (project-current) - (project-root (project-current))) - temporary-file-directory)) - (name (format "ob-src-%s.py" - (substring (md5 parent) 0 8)))) - (setq-local buffer-file-name (expand-file-name name root)) - (set-buffer-modified-p nil) - (eglot-ensure)))) - -(add-hook 'org-src-mode-hook #'my/org-src-eglot-python) -#+end_src - -*** xonsh LSP via eglot - -No xonsh language server. Body mostly valid python → reuse python -LSP stack (=pyrefly= + =ruff server= via =rass=, same as -=python-ts-mode=). Subshell syntax flags as errors — known frickelig -trade-off. Hook both =xonsh-mode= and =xonsh-ts-mode=. - -#+begin_src emacs-lisp -(with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '((xonsh-mode xonsh-ts-mode) - . ("rass" "--" "pyrefly" "lsp" "--" "ruff" "server")))) -;; kein auto-eglot fuer xonsh — pyrefly.toml waere noetig. Manuell via -;; M-x my/pyrefly-here-xonsh (legt temp pyrefly.toml an + startet eglot). -;; (add-hook 'xonsh-mode-hook #'eglot-ensure) -;; (add-hook 'xonsh-ts-mode-hook #'eglot-ensure) -#+end_src *** xonsh org babel @@ -3080,43 +3117,13 @@ BODY is the xonsh script. PARAMS may include :dir and :cmdline." (add-hook 'ob-async-pre-execute-src-block-hook my/ob-xonsh-installer)) #+end_src -** COMMENT Go mode - -#+begin_src emacs-lisp -(use-package go-mode - :ensure t - :mode "\\.go\\'" - :hook (go-mode . eglot-ensure) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(go-mode . ("gopls" :initializationOptions - (:staticcheck t - :matcher "CaseSensitive" - :usePlaceholders t))))) - - (defun go-mode-setup () - (add-hook 'before-save-hook #'eglot-format-buffer -10 t) - (add-hook 'before-save-hook - (lambda () - (when (eglot-managed-p) - (eglot-code-action-organize-imports nil t))) - nil t)) - - (add-hook 'go-mode-hook #'go-mode-setup)) -#+end_src ** COMMENT json-ts-mode #+begin_src emacs-lisp (use-package json-ts-mode :ensure nil - :mode "\\.json\\'" - :hook (json-ts-mode . eglot-ensure) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(json-ts-mode . ("vscode-json-language-server" "--stdio"))))) + :mode "\\.json\\'") #+end_src ** yaml-ts-mode @@ -3124,8 +3131,7 @@ BODY is the xonsh script. PARAMS may include :dir and :cmdline." #+begin_src emacs-lisp (use-package yaml-ts-mode :ensure nil - :mode "\\.ya?ml\\'" - :hook (yaml-ts-mode . eglot-ensure)) + :mode "\\.ya?ml\\'") #+end_src ** bash-ts-mode @@ -3136,17 +3142,7 @@ BODY is the xonsh script. PARAMS may include :dir and :cmdline." :mode (("\\.sh\\'" . bash-ts-mode) ("\\.bash\\'" . bash-ts-mode) ("\\.bashrc\\'" . bash-ts-mode) - ("\\.bash_profile\\'" . bash-ts-mode)) - :hook (bash-ts-mode . eglot-ensure) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(bash-ts-mode . ("bash-language-server" "start")))) - - (defun sh-mode-setup () - (add-hook 'before-save-hook #'eglot-format-buffer -10 t)) - - (add-hook 'bash-ts-mode-hook #'sh-mode-setup)) + ("\\.bash_profile\\'" . bash-ts-mode))) #+end_src ** just-mode @@ -3154,16 +3150,7 @@ BODY is the xonsh script. PARAMS may include :dir and :cmdline." #+begin_src emacs-lisp (use-package just-mode :ensure t - :mode ("[Jj]ustfile\\'" "\\.just\\'") - :hook (just-mode . eglot-ensure) - :config - (with-eval-after-load 'eglot - (add-to-list 'eglot-server-programs - '(just-mode . ("just-lsp"))))) - -;; (use-package justl -;; :ensure t -;; :after just-mode) + :mode ("[Jj]ustfile\\'" "\\.just\\'")) #+end_src * terminals @@ -3869,14 +3856,7 @@ still require a restart since elpaca queues run at init time." "^\\*Help\\*$" "^\\*Apropos\\*$" "^\\*info\\*$" - "^\\*Async-native-compile-log\\*$" - - ;; LSP Buffers - "^\\*lsp-log\\*$" - "^\\*clojure-lsp\\*$" - "^\\*clojure-lsp::stderr\\*$" - "^\\*ts-ls\\*$" - "^\\*ts-ls::stderr\\*$")) + "^\\*Async-native-compile-log\\*$")) #+end_src * projectile @@ -3904,7 +3884,7 @@ still require a restart since elpaca queues run at init time." :after (helm projectile) :commands (helm-projectile-find-file helm-projectile-switch-project) :custom - (helm-projectile-fuzzy-match t)) + (helm-projectile-fuzzy-match nil)) #+end_src ** wgrep @@ -4345,12 +4325,7 @@ place. `C-c C-c' commits, `C-c C-k' aborts." #+begin_src emacs-lisp (defun my/format-buffer () (interactive) - (cond - ((eq major-mode 'rust-ts-mode) (eglot-format-buffer)) - ((eq major-mode 'python-ts-mode) (eglot-format-buffer)) - ((eq major-mode 'c-mode) (eglot-format-buffer)) - ((bound-and-true-p eglot--managed-mode) (eglot-format-buffer)) - (t (message "No formatter for %s" major-mode)))) + (user-error "No formatter configured for %s" major-mode)) (defun my/find-file-or-switch-project () "Find a file in the current project; outside one, pick a project first. @@ -4417,6 +4392,7 @@ workspace (e.g. *scratch*)." "pa" '(projectile-add-known-project :wk "add project") "pi" '(projectile-invalidate-cache :wk "invalidate cache") "pt" '(projectile-run-task :wk "tasks") + "pc" '(my/project-ctags-setup :wk "setup/update ctags") "g" '(:ignore t :wk "git") "gc" '(magit-clone :wk "clone") @@ -4428,11 +4404,8 @@ workspace (e.g. *scratch*)." (let ((current-prefix-arg 4)) (call-interactively #'xref-find-definitions))) :wk "definition other window") - "gI" '((lambda () (interactive) - (let ((current-prefix-arg 4)) - (call-interactively #'eglot-find-implementation))) - :wk "implementation other window") - "gt" '(eglot-find-typeDefinition :wk "go to type definition") + "gI" '(citre-query-jump :wk "find definition") + "gt" '(citre-peek :wk "peek definition") "gr" '(xref-find-references :wk "find references") "gs" '(magit-file-stage :wk "stage file") "gu" '(my/magit-uncommit :wk "uncommit (keep & unstage)") @@ -4476,9 +4449,9 @@ workspace (e.g. *scratch*)." "c" '(:ignore t :wk "code") "cc" '(my/compile-or-recompile :wk "compile") "cC" '(ghostel-compile :wk "recompile") - "ca" '(eglot-code-actions :wk "code actions") - "cr" '(eglot-rename :wk "lsp rename") - "cf" '(eglot-format :wk "format buffer") + "ca" '(citre-query-jump :wk "find definition") + "cr" '(query-replace :wk "replace") + "cf" '(my/format-buffer :wk "format buffer") "cs" '(yas-insert-snippet :wk "snippets") "cl" '(flycheck-list-errors :wk "list errors") @@ -5083,11 +5056,7 @@ reset is unnecessary, so do the handler resolution ourselves and skip #+begin_src emacs-lisp (use-package gleam-ts-mode :ensure t - :mode "\\.gleam\\'" - :hook (gleam-ts-mode . eglot-ensure) - :config - (add-to-list 'eglot-server-programs - '(gleam-ts-mode . ("gleam" "lsp")))) + :mode "\\.gleam\\'") #+end_src * envrc diff --git a/themes/tinty-theme.el b/themes/tinty-theme.el index e322d68..4fc283d 100644 --- a/themes/tinty-theme.el +++ b/themes/tinty-theme.el @@ -30,7 +30,7 @@ (ui-bg-light "#45475a") ;; Tinted8 UI accent/status colors - (ui-accent "#94e2d5") + (ui-accent "#b4befe") (ui-link "#89b4fa") (ui-info "#fab387") (ui-error "#f38ba8") |
