;;; $DOOMDIR/config.el -*- lexical-binding: t; -*- ;; -------------------------------------------------- ;; Theme / UI ;; -------------------------------------------------- (setq doom-theme 'modus-vivendi-deuteranopia doom-font (font-spec :family "JetBrains Mono" :size 14) doom-variable-pitch-font nil) (setq display-line-numbers-type t) ;; -------------------------------------------------- ;; macOS / UX ;; -------------------------------------------------- (setq mouse-autoselect-window nil focus-follows-mouse nil select-enable-clipboard t select-enable-primary t inhibit-splash-screen t) ;; ;; -------------------------------------------------- ;; ;; Completion (Company) – minimum, bezpečné ;; ;; -------------------------------------------------- ;; (after! company ;; (setq company-idle-delay 0.1 ;; company-minimum-prefix-length 2 ;; company-selection-wrap-around t ;; company-tooltip-limit 14 ;; company-show-numbers t ;; company-require-match nil) ;; ;; Spolehlivé vyvolání v TTY ;; (map! :i "C-." #'company-complete ;; :n "C-." #'company-complete)) ;; ;; -------------------------------------------------- ;; ;; Robustní file completion "kdekoliv pod kurzorem" ;; ;; -------------------------------------------------- ;; (defun martin/complete-file-name-at-point () ;; "Doplň název souboru kolem kurzoru pomocí standardní file completion tabulky. ;; Funguje v libovolném textu, včetně Markdown linků (např. [x](./...))." ;; (interactive) ;; (let* ((stop-chars '(?\s ?\t ?\n ?\r ?\" ?\' ?\( ?\) ?\[ ?\] ?\< ?\> ?\{ ?\} ?, ?\; )) ;; (start (save-excursion ;; (while (and (> (point) (point-min)) ;; (let ((c (char-before))) ;; (and c (not (memq c stop-chars))))) ;; (backward-char)) ;; (point))) ;; (end (save-excursion ;; (while (and (< (point) (point-max)) ;; (let ((c (char-after))) ;; (and c (not (memq c stop-chars))))) ;; (forward-char)) ;; (point)))) ;; (when (= start end) ;; (setq start (point) end (point))) ;; (let ((completion-category-defaults nil) ;; (completion-category-overrides '((file (styles basic partial-completion))))) ;; (completion-in-region start end #'completion-file-name-table)))) ;; (map! :i "C-c f" #'martin/complete-file-name-at-point ;; :n "C-c f" #'martin/complete-file-name-at-point) ;; (after! markdown-mode ;; (add-hook 'markdown-mode-hook (lambda () (setq-local company-minimum-prefix-length 1))) ;; (add-hook 'gfm-mode-hook (lambda () (setq-local company-minimum-prefix-length 1)))) ;; ------------------------------------------------------------ ;; ORG (Doom-friendly, minimal, bez zbytečných menu) ;; ------------------------------------------------------------ (after! org ;; 0) require packages (require 'ox-hugo) ;; 1) Kde máš org soubory ;; Uprav si cestu podle sebe. Tohle je typický Doom default. (setq org-directory "~/org/") (setq org-default-notes-file (expand-file-name "inbox.org" org-directory)) (setq org-agenda-files (list org-directory (expand-file-name "projects" org-directory) (expand-file-name "roam" org-directory) (expand-file-name "notes" org-directory))) (setq org-todo-keywords '((sequence "TODO(t)" "NEXT(n)" "WAIT(w@/!)" "|" "DONE(d!)" "CANCELLED(c@)"))) (setq org-log-done 'time) (setq org-refile-targets '((org-agenda-files :maxlevel . 5)) org-outline-path-complete-in-steps nil org-refile-use-outline-path 'file) (defun my/project-org-file () "Return path to ./project.org in current Projectile project, if it exists." (when-let ((root (projectile-project-root))) (let ((f (expand-file-name "project.org" root))) (when (file-exists-p f) f)))) ;; Pomocná funkce: vrátí plnou cestu k souboru v org-directory (defun ms/org-file (name) "Return absolute path to NAME inside `org-directory`." (expand-file-name name org-directory)) ;; 2) Org-capture templates ;; Zachovává tvoje i/n/p a jen doplňuje chybějící věci. (setq org-capture-templates `( ;; --- Tvoje původní šablony (beze změn) --- ("i" "Inbox task" entry (file ,(ms/org-file "inbox.org")) "* TODO %?\n%U\n%a\n") ("n" "Note" entry (file+headline ,(ms/org-file "inbox.org") "Notes") "* %?\n%U\n%a\n") ("p" "Project task" entry (file ,(ms/org-file "inbox.org")) "* TODO %? :project:\n%U\n%a\n") ;; --- Doplňky (minimalisticky) --- ;; Subtask do právě clockované položky ("s" "Clocked subtask" entry (clock) "* TODO %?\n%U\n%a\n%i" :empty-lines 1) ;; Journal do journal.org (datetree) + automatické měření času ("j" "Journal" entry (file+olp+datetree ,(ms/org-file "journal.org")) "\n* %<%I:%M %p> - Journal :journal:\n\n%?\n\n" :clock-in :clock-resume :empty-lines 1) ;; Meeting do journal.org (datetree) + měření času ("m" "Meeting" entry (file+olp+datetree ,(ms/org-file "journal.org")) "* %<%I:%M %p> - %^{Meeting title} :meetings:\nContext: %a\n\n%?\n\n" :clock-in :clock-resume :empty-lines 1) ;; Checking Email – do journal datetree ("e" "Checking Email" entry (file+olp+datetree ,(ms/org-file "journal.org")) "* Checking Email :email:\n\n%?" :clock-in :clock-resume :empty-lines 1) ;; Weight metrika do tabulky v metrics.org pod headline "Weight" ("w" "Weight" table-line (file+headline ,(ms/org-file "metrics.org") "Weight") "| %U | %^{Weight} | %^{Notes} |" :kill-buffer t) ))) ;; ------------------------------------------------------------ ;; (Volitelné) Kvalita života – nic navíc, jen užitečné defaulty ;; ------------------------------------------------------------ (after! org ;; ať se po capture vrací do stejného okna (někomu pomáhá) (setq org-capture-restore-window-after-quit t)) (after! org (add-hook 'org-export-before-processing-hook (lambda (_backend) (org-update-all-dblocks)))) ;; -------------------------------------------------- ;; Dired ;; -------------------------------------------------- (after! dired (put 'dired-find-alternate-file 'disabled nil) (map! :map dired-mode-map "RET" #'dired-find-alternate-file "^" #'dired-up-directory)) ;; -------------------------------------------------- ;; PlantUML (server) ;; -------------------------------------------------- (add-to-list 'auto-mode-alist '("\\.puml\\'" . plantuml-mode)) (add-to-list 'auto-mode-alist '("\\.plantuml\\'" . plantuml-mode)) (after! plantuml-mode (setq plantuml-default-exec-mode 'server plantuml-server-url "https://www.plantuml.com/plantuml" plantuml-output-type "svg" plantuml-verbose t)) (defun my/plantuml-encode-hex (text) "PlantUML HEX encoding: ~h + hex(UTF-8 bytes)." (let* ((utf8 (encode-coding-string text 'utf-8 t))) (concat "~h" (apply #'concat (mapcar (lambda (b) (format "%02x" b)) (append utf8 nil)))))) (defun my/plantuml-fix-png-header (file) "Odstraní vše před PNG signaturou." (let ((sig (unibyte-string #x89 ?P ?N ?G ?\r ?\n #x1a ?\n))) (with-temp-buffer (set-buffer-multibyte nil) (insert-file-contents-literally file) (goto-char (point-min)) (unless (looking-at (regexp-quote sig)) (let ((pos (search-forward sig nil t))) (unless pos (user-error "PNG signature nenalezena")) (delete-region (point-min) (- pos (length sig))) (let ((coding-system-for-write 'binary)) (write-region (point-min) (point-max) file nil 'silent))))))) (defun my/plantuml-render-server (type) "Render aktuální .puml přes PlantUML server do PNG nebo SVG." (interactive (list (completing-read "Type: " '("png" "svg") nil t "png"))) (unless buffer-file-name (user-error "Otevři .puml jako soubor")) (let* ((text (buffer-substring-no-properties (point-min) (point-max))) (encoded (my/plantuml-encode-hex text)) (server (string-remove-suffix "/" plantuml-server-url)) (url (format "%s/%s/%s" server type encoded)) (out (concat (file-name-sans-extension buffer-file-name) "." type))) (url-copy-file url out t) (when (string-equal type "png") (my/plantuml-fix-png-header out)) (message "PlantUML uložen: %s" out) out)) (after! plantuml-mode (define-key plantuml-mode-map (kbd "C-c C-p") (lambda () (interactive) (my/plantuml-render-server "png"))) (define-key plantuml-mode-map (kbd "C-c C-s") (lambda () (interactive) (my/plantuml-render-server "svg")))) ;; -------------------------------------------------- ;; PATH fix for MacTeX ;; -------------------------------------------------- (setenv "PATH" (concat "/Library/TeX/texbin:" (getenv "PATH"))) (add-to-list 'exec-path "/Library/TeX/texbin") ;; -------------------------------------------------- ;; Python ;; -------------------------------------------------- (setq python-shell-interpreter "python3") (after! org (setq org-babel-python-command "python3") (require 'ob-python)) ;;; GPTel + OpenWebUI (OpenRouter behind it) (use-package! gptel :config ;; 1) API key z env (bez klíčů v configu) (defun my/openwebui-key () (or (getenv "OPENWEBUI_API_KEY") (user-error "Missing OPENWEBUI_API_KEY env var"))) ;; 2) Stáhnout seznam modelů z OpenWebUI /api/models (defun my/openwebui-fetch-model-ids () "Return list of model ids from OpenWebUI /api/models (field data[].id)." (require 'url) (require 'json) (let* ((url-request-method "GET") (url-request-extra-headers `(("Authorization" . ,(concat "Bearer " (funcall #'my/openwebui-key)))))) (with-current-buffer (url-retrieve-synchronously "https://ai.apps.sukany.cz/api/models" t t 15) (goto-char (point-min)) (re-search-forward "\n\n" nil 'move) ;; přeskočit HTTP hlavičky (let* ((json-object-type 'alist) (json-array-type 'list) (json-key-type 'symbol) (obj (json-read)) (data (alist-get 'data obj)) (ids (delq nil (mapcar (lambda (it) (alist-get 'id it)) data)))) (kill-buffer (current-buffer)) ids)))) (defvar my/openwebui-models-cache nil) (defun my/openwebui-models () "Cached list of model ids. Falls back to a minimal list if API fails." (or my/openwebui-models-cache (setq my/openwebui-models-cache (condition-case err (my/openwebui-fetch-model-ids) (error (message "OpenWebUI models fetch failed: %s" err) ;; fallback – zkus běžné OpenRouter ids (může, ale nemusí být dostupné) '("openai/gpt-4o-mini" "openai/gpt-4.1-mini")))))) (defun my/openwebui-refresh-models () "Clear cache and refetch OpenWebUI model list." (interactive) (setq my/openwebui-models-cache nil) (message "OpenWebUI models refreshed: %d" (length (my/openwebui-models)))) ;; 3) Backend pro OpenWebUI (OpenAI-compatible chat/completions) (setq gptel-backend (gptel-make-openai "OpenWebUI" :host "ai.apps.sukany.cz" :protocol "https" :key #'my/openwebui-key :endpoint "/api/chat/completions" :stream t ;; často stabilnější přes reverzní proxy/ingress: :curl-args '("--http1.1") :models (my/openwebui-models))) ;; 4) Default model: ekonomický a praktický ;; Preferuj openai/gpt-5.2-mini (levný, rychlý), jinak první dostupný model. ;; (GPT-5.2 mini je běžně uváděn jako “fast, affordable” a na OpenRouter má id openai/gpt-4o-mini) :contentReference[oaicite:2]{index=2} (let* ((models (my/openwebui-models)) (preferred "openai/gpt-5-mini")) (setq gptel-model (if (member preferred models) preferred (car models)))) ;; 5) Presety (rychlé přepínání podle úlohy) :contentReference[oaicite:3]{index=3} ;; Pozn.: Presety jen nastavují model/backend/system atd. – žádná magie navíc. (gptel-make-preset 'fast :description "Default (rychlý/levný) – běžná práce" :backend "OpenWebUI" :model "openai/gpt-4o-mini" :system "Odpovídej česky. Buď konkrétní, krokový. Neomáčej to." :temperature 0.2) (gptel-make-preset 'coding :description "Kód / refaktor / review (silnější model, když je potřeba)" :backend "OpenWebUI" ;; nastav si sem model, který opravdu máš v /api/models: :model "openai/gpt-4.1-mini" :system "Jsi přísný code reviewer. Navrhuj konkrétní změny a rizika." :temperature 0.1) (gptel-make-preset 'deep :description "Náročná analýza / architektura" :backend "OpenWebUI" ;; nastav si sem něco silnějšího z tvého seznamu: :model "openai/gpt-4.1" :system "Postupuj systematicky. Dej varianty, tradeoffs a doporučení." :temperature 0.2) ;; 6) (Volitelné) Debug log, když něco zlobí: ;; (setq gptel-log-level 'debug) ) ;; 7) Mini CLI: volání z command line přes `emacs --batch` ;; Použití níže v příkladech (defun my/gptel-cli (prompt &optional model system) "Send PROMPT via gptel and print response to stdout." (require 'gptel) (let* ((done nil) (result nil) (gptel-model (or model gptel-model)) (gptel--system-message (or system gptel--system-message))) (gptel-request prompt :callback (lambda (response _info) (setq result response) (setq done t))) (while (not done) (accept-process-output nil 0.05)) (princ result))) ;; -------------------------------------------------- ;; GPTel keybindings (safe in Doom) SPC o g ... ;; -------------------------------------------------- (after! gptel (map! :leader (:prefix ("o g" . "GPTel") :desc "GPTel send (region or buffer)" "s" #'gptel-send :desc "GPTel menu (model/scope/preset)" "m" #'gptel-menu :desc "GPTel chat buffer" "c" #'gptel :desc "GPTel abort request" "x" #'gptel-abort :desc "Refresh OpenWebUI models" "R" #'my/openwebui-refresh-models))) ;; performance (setq which-key-idle-delay 0) ;; auto save (setq auto-save-default nil) ;; zruší #file# bordel (defun my/save-all-buffers () (save-some-buffers t)) (run-with-idle-timer 30 t #'my/save-all-buffers) ;; centered cursor mode (use-package! centered-cursor-mode :config (setq ccm-vpos-init 0.5) ;; 0.5 = střed okna ;; VYPNUTO — koliduje s macOS Zoom focus follower (způsobuje skákání obrazu) ;; (global-centered-cursor-mode +1) ) ;; Profiling (setq gc-cons-threshold (* 100 1024 1024) ;; 100 MB gc-cons-percentage 0.6) (add-hook 'focus-out-hook #'garbage-collect) (setq doom-modeline-refresh-rate 5.0) ;; zvýšeno — méně redraws pro macOS Zoom (setq which-key-idle-delay 0.8 which-key-idle-secondary-delay 0.05) (setq org-idle-time 15) ;; zvýšeno z 1.0 — org clock idle check méně často ;; -------------------------------------------------- ;; macOS Zoom accessibility — minimalizace redraws ;; -------------------------------------------------- ;; Auto-revert: zvýšit interval, vypnout VC info check (setq auto-revert-interval 30) (setq auto-revert-check-vc-info nil) (setq auto-revert-avoid-polling t) ;; VC gutter — vypnout periodický refresh (after! diff-hl (setq diff-hl-flydiff-delay 30)) ;; Flycheck — zvýšit idle delay (after! flycheck (setq flycheck-idle-change-delay 10 flycheck-idle-buffer-switch-delay 10)) ;; Highlight line — vypnout (redraw při každém idle timeru) (remove-hook 'doom-first-buffer-hook #'global-hl-line-mode) (after! hl-line (global-hl-line-mode -1)) ;; --- macOS clipboard: pbcopy/pbpaste (funguje i v terminal Emacs) --- (defun my/pbcopy (text &optional _push) "Send TEXT to the macOS clipboard using pbcopy." (let ((process-connection-type nil)) (let ((proc (start-process "pbcopy" "*pbcopy*" "pbcopy"))) (process-send-string proc text) (process-send-eof proc)))) (defun my/pbpaste () "Return text from the macOS clipboard using pbpaste." (when (executable-find "pbpaste") (string-trim-right (shell-command-to-string "pbpaste")))) (setq select-enable-clipboard t select-enable-primary t) ;; Emacs -> system clipboard (setq interprogram-cut-function #'my/pbcopy) ;; system clipboard -> Emacs (setq interprogram-paste-function #'my/pbpaste) ;; Ať Evil používá clipboard (y/d/c budou do systému) (after! evil (setq evil-want-clipboard t)) ;; !!! DANGEROUS: disable TLS verification globally !!! (setq gnutls-verify-error nil) (setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3") (after! projectile (setq projectile-enable-caching nil projectile-indexing-method 'alien) (when (executable-find "fd") (setq projectile-generic-command "fd . -0 --type f --hidden --follow --exclude .git --color=never"))) ;; mu4e (add-to-list 'load-path (expand-file-name "/opt/homebrew/opt/mu/share/emacs/site-lisp/mu/mu4e")) (after! mu4e (setq mu4e-maildir "~/.mail" mu4e-get-mail-command "mbsync personal" mu4e-update-interval 300 mu4e-change-filenames-when-moving t mu4e-view-show-images t)) (after! mu4e (setq mu4e-maildir (expand-file-name "~/.mail") mu4e-get-mail-command "mbsync personal" mu4e-update-interval 300 mu4e-change-filenames-when-moving t mu4e-view-show-images t ;; TADY je to důležité: mu4e-sent-folder "/personal/Sent" mu4e-drafts-folder "/personal/Drafts" mu4e-trash-folder "/personal/Trash" mu4e-refile-folder "/personal/Archive")) (after! mu4e (setq sendmail-program "msmtp" message-send-mail-function #'message-send-mail-with-sendmail mail-specify-envelope-from t message-sendmail-envelope-from 'header)) (setq user-mail-address "martin@sukany.cz" user-full-name "Martin Sukany") (after! mu4e ;; thread view (setq mu4e-headers-show-threads t) (setq mu4e-headers-include-related t) ;; pěkné zobrazení (setq mu4e-headers-fields '((:human-date . 12) (:flags . 6) (:from . 22) (:subject))) ;; strom vláken (lepší orientace) (setq mu4e-use-fancy-chars t)) (after! mu4e (setq mu4e-headers-mark-for-thread t)) ;;; ================================ ;;; Emacspeak robust ON/OFF for Doom ;;; Default: OFF (won't auto-restart) ;;; Keys: ;;; SPC t s -> Speech ON ;;; SPC t S -> Speech OFF ;;; ================================ (defconst my/emacspeak-dir (expand-file-name "~/.emacspeak")) (defconst my/emacspeak-wrapper (expand-file-name "~/.local/bin/emacspeak-mac")) ;; Emacspeak uses this to start the speech server. (setq dtk-program my/emacspeak-wrapper) ;;(setq dtk-program "espeak") ;; State flags (defvar my/emacspeak-loaded nil) (defvar my/emacspeak-enabled nil) ;; Hard inhibit: when non-nil, Emacspeak is NOT allowed to start/restart the server. (defvar my/emacspeak-inhibit-server t) (defun my/emacspeak--ensure-loaded () "Load Emacspeak once, safely, without breaking Doom startup." (unless my/emacspeak-loaded (setq my/emacspeak-loaded t) (setq emacspeak-directory my/emacspeak-dir) ;; Load late-ish (but still inside the command that enables it) (load-file (expand-file-name "lisp/emacspeak-setup.el" emacspeak-directory)) ;; After Emacspeak is present, install inhibition advices. ;; These prevent the common 'it restarts by itself' problem. (with-eval-after-load 'dtk-speak (dolist (fn '(dtk-initialize dtk-start-process dtk-speak)) (when (fboundp fn) (advice-add fn :around (lambda (orig &rest args) (if my/emacspeak-inhibit-server ;; OFF mode: do nothing (and crucially: don't restart speaker) nil (apply orig args))))))))) (defun my/emacspeak-on () "Enable speech (and allow server start)." (interactive) (setq my/emacspeak-inhibit-server nil) (my/emacspeak--ensure-loaded) (setq my/emacspeak-enabled t) (when (fboundp 'dtk-restart) (ignore-errors (dtk-restart))) (when (fboundp 'dtk-speak) (ignore-errors (dtk-speak "Emacspeak on."))) (message "Emacspeak ON")) (defun my/emacspeak-off () "Disable speech robustly (stop + prevent auto-restart)." (interactive) ;; First: inhibit any future attempts to start/restart. (setq my/emacspeak-enabled nil) (setq my/emacspeak-inhibit-server t) ;; Stop current speech if any. (when (fboundp 'dtk-stop) (ignore-errors (dtk-stop))) ;; Kill the server process hard (if it exists). (when (boundp 'dtk-speaker-process) (let ((p dtk-speaker-process)) (when (processp p) (ignore-errors (set-process-sentinel p nil)) (ignore-errors (delete-process p)))) (setq dtk-speaker-process nil)) (message "Emacspeak OFF (server restart inhibited)")) ;; Doom leader keys (map! :leader (:prefix ("t" . "toggle") :desc "Speech ON" "s" #'my/emacspeak-on :desc "Speech OFF" "S" #'my/emacspeak-off)) ;; ---------------------------- ;; Emacspeak defaults (global) ;; ---------------------------- (with-eval-after-load 'dtk-speak ;; Default punctuation mode: none / some / all ;; (Emacspeak manual: dtk-set-punctuations supports 'none 'some 'all) (setq dtk-speech-rate-base 300) (setq-default dtk-punctuation-mode 'none)) (with-eval-after-load 'emacspeak ;; Typing feedback: ;; nechceš znaky, chceš slova + řádky ;; (Emacspeak manual: character/word/line echo) (setq-default emacspeak-character-echo nil) (setq-default emacspeak-word-echo t) (setq-default emacspeak-line-echo t)) (map! :leader (:prefix ("h" . "help") :desc "Describe bindings (buffer-local)" "B" #'describe-bindings)) ;; 1) ulož si globální default hodnotu (setq dtk-default-speech-rate 400) ;; 2) aplikuj ji ve chvíli, kdy je TTS už inicializované (with-eval-after-load 'dtk-speak (defun my/dtk-apply-global-default-rate (&rest _) "Apply global default speech rate after TTS init/restart." (when (fboundp 'dtk-set-rate) ;; PREFIX arg => set GLOBAL default (per Emacspeak manual) (ignore-errors (dtk-set-rate dtk-default-speech-rate t)))) ;; po každé inicializaci/restartu TTS (advice-add 'dtk-initialize :after #'my/dtk-apply-global-default-rate)) ;; ElFeed (RSS) (map! :leader :desc "Elfeed" "o r" #'elfeed) (after! org ;; pokud máš org soubory jinde než ~/org, nastav org-directory sem: ;; (setq org-directory "~/org") ;; elfeed-org: řekni mu explicitně, kde je elfeed.org (setq rmh-elfeed-org-files (list (expand-file-name "elfeed.org" org-directory)))) (after! elfeed ;; elfeed-org musí být inicializovaný, jinak se elfeed.org nemusí načítat (require 'elfeed-org) (elfeed-org)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CORFU CONFIGURATION (modern completion UI) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (after! corfu ;; automatické completions (setq corfu-auto t corfu-auto-delay 0.5 corfu-auto-prefix 2 ;; cyklování kandidátů corfu-cycle t ;; předvybrat první kandidát corfu-preselect 'prompt ;; lepší UX corfu-quit-no-match 'separator corfu-preview-current nil) ;; zapnout globálně (global-corfu-mode)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; CAPE — zdroje completion ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Corfu (after! corfu (setq corfu-auto t corfu-auto-delay 0.5 corfu-auto-prefix 2 corfu-cycle t) (global-corfu-mode)) ;;; Cape (zdroje completion-at-point) (use-package! cape :after corfu :config (defun martin/cape-capf-setup () "Zdroje doplňování použitelné téměř všude (bez LSP)." ;; Slova z bufferů (add-to-list 'completion-at-point-functions #'cape-dabbrev 0) ;; Cesty k souborům (add-to-list 'completion-at-point-functions #'cape-file 0) ;; Keywords (užitečné hlavně v prog-mode) (add-to-list 'completion-at-point-functions #'cape-keyword 0) ;; Elisp symboly (jen když píšeš elisp, ale nevadí ani jinde) (add-to-list 'completion-at-point-functions #'cape-elisp-symbol 0)) (add-hook 'prog-mode-hook #'martin/cape-capf-setup) (add-hook 'text-mode-hook #'martin/cape-capf-setup)) ;; Corfu popup i v terminálu (iTerm2 / ssh / tmux) (use-package! corfu-terminal :when (not (display-graphic-p)) :after corfu :config (corfu-terminal-mode +1)) ;; TRAMP (after! tramp (setq projectile-git-command "git ls-files -zco --exclude-standard" projectile-indexing-method 'alien)) ;; Vypnout VC a projectile přes TRAMP — hlavní příčina visení (setq vc-ignore-dir-regexp (format "%s\\|%s" vc-ignore-dir-regexp tramp-file-name-regexp)) ;; Vypnout projectile zcela pro remote (defadvice projectile-project-root (around ignore-remote first activate) (unless (file-remote-p default-directory) ad-do-it)) ;; TRAMP cache — neopakovat drahé remote dotazy (setq remote-file-name-inhibit-cache nil tramp-verbose 1) ;; Latex org export - hack pro spravne tabulky (defun my/org-count-table-columns (line) "Spočítej počet datových sloupců v Org table LINE." (length (cl-remove-if (lambda (s) (string-match-p "^-*$" (string-trim s))) (cdr (butlast (split-string line "|")))))) (defun my/org-table-attr-latex-spec (ncols) "Vygeneruj column spec pro tabularx: první sloupec l, zbytek Y (centered X)." (concat "l" (make-string (max 0 (1- ncols)) ?Y))) (defun my/org-auto-tabularx (backend) "Automaticky přidej #+ATTR_LATEX tabularx před každou tabulku při LaTeX exportu." (when (org-export-derived-backend-p backend 'latex) (save-excursion (goto-char (point-min)) (while (not (eobp)) (cond ;; Řádek začíná | — může být začátek tabulky ((looking-at "^|") (let ((prev-line (save-excursion (forward-line -1) (buffer-substring-no-properties (line-beginning-position) (line-end-position))))) ;; Je to PRVNÍ řádek tabulky? (předchozí řádek NEzačíná |) (when (not (string-match-p "^|" prev-line)) ;; Chybí #+ATTR_LATEX? (when (not (string-match-p "^#\\+ATTR_LATEX" prev-line)) (let* ((table-line (buffer-substring-no-properties (line-beginning-position) (line-end-position))) (ncols (my/org-count-table-columns table-line)) (spec (my/org-table-attr-latex-spec ncols)) (attr (format "#+ATTR_LATEX: :environment tabularx :width \\textwidth :align %s\n" spec))) (when (> ncols 0) (insert attr)))))) (forward-line)) (t (forward-line))))))) ;; Zaregistruj hook — spustí se před každým exportem (add-hook 'org-export-before-processing-hook #'my/org-auto-tabularx) ;; Volitelně: zapni booktabs styl (horizontal rules v tabulkách) ;; (setq org-latex-tables-booktabs t) ;; ;; Jak použít: ;; 1. Zkopíruj tento obsah do ~/.config/doom/config.el ;; 2. Spusť: doom sync (nebo M-x doom/reload) ;; 3. Exportuj dokument: SPC m e l p ;; Tabulky se automaticky obalí do tabularx — nic nemusíš přidávat ručně. ;; ;; Chceš jiný výchozí column spec? Uprav my/org-table-attr-latex-spec. ;; Například pro "všechny sloupce rovnoměrně": (make-string ncols ?Y)