1 ;; get rid of custom from my init file
2 (setq custom-file "~/.emacs.d/emacs-custom.el")
5 (defconst op/backup-dir
6 (expand-file-name "backups" user-emacs-directory))
7 (unless (file-exists-p op/backup-dir)
8 (make-directory op/backup-dir))
9 (setq backup-directory-alist `(("." . ,op/backup-dir)))
11 (setq use-dialog-box nil
13 require-final-newline t
17 enable-recursive-minibuffers t
20 next-error-message-highlight t
21 read-minibuffer-restore-windows nil
22 isearch-allow-motion t
23 calc-make-windows-dedicated t
24 user-mail-address "op@omarpolo.com")
26 (setq completion-ignore-case t
27 read-file-name-completion-ignore-case t
28 read-buffer-completion-ignore-case t)
30 (define-key global-map (kbd "C-x C-b") #'ibuffer)
31 (define-key global-map (kbd "M-g i") #'imenu)
34 "Just like `imenu', but always flattened!"
38 (define-key minibuffer-mode-map (kbd "C-w") #'backward-kill-word)
40 (defun op/reverse-other-window ()
41 "Like `other-window', but reverse."
44 (define-key global-map (kbd "C-x O") #'op/reverse-other-window)
46 (setq uniquify-buffer-name-style 'forward
47 uniquify-strip-common-suffix t)
49 (setq-default scroll-up-aggressively 0.0
50 scroll-down-aggressively 0.0
51 scroll-preserve-screen-position t
52 next-screen-context-lines 1)
54 (define-key global-map (kbd "M-z") #'zap-up-to-char)
57 (setq whitespace-style '(face trailing)
58 backward-delete-char-untabify-method 'hungry
59 tab-always-indent 'complete
61 sentence-end-double-space t)
62 (setq-default indent-tabs-mode t)
64 (defun op/enable-tabs ()
65 "Enable `indent-tabs-mode' in the current buffer."
67 (setq-local indent-tabs-mode t))
69 (defun op/disable-tabs ()
70 "Disable `indent-tabs-mode' in the current buffer."
72 (setq-local indent-tabs-mode nil))
74 (add-hook 'conf-mode-hook #'op/enable-tabs)
75 (add-hook 'text-mode-hook #'op/enable-tabs)
76 (add-hook 'prog-mode-hook #'op/enable-tabs)
77 (add-hook 'prog-mode-hook #'whitespace-mode)
78 (add-hook 'text-mode-hook #'whitespace-mode)
80 (dolist (hook '(emacs-lisp-mode-hook
82 clojurescript-mode-hook
85 (add-hook hook #'op/disable-tabs))
87 (with-eval-after-load 'log-edit
88 (add-hook 'log-edit-mode #'auto-fill-mode))
90 ;; free the c-z binding
91 (define-key global-map (kbd "C-z") nil)
92 (define-key global-map (kbd "C-z V") #'variable-pitch-mode)
93 (define-key global-map (kbd "C-z n") #'display-line-numbers-mode)
95 (define-key global-map (kbd "M-SPC") #'cycle-spacing)
96 (define-key global-map (kbd "M-u") #'upcase-dwim)
97 (define-key global-map (kbd "M-l") #'downcase-dwim)
98 (define-key global-map (kbd "M-c") #'capitalize-dwim)
100 (let ((font "-misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso10646-1"))
101 (set-frame-font font nil t)
102 (add-to-list 'default-frame-alist `(font . ,font)))
104 ;; fix the emojis too
105 (set-fontset-font t 'emoji '("Noto Emoji" . "iso10646-1")
111 (setq history-delete-duplicates t
113 savehist-save-minibuffer-history t)
114 (electric-pair-mode +1)
116 (define-key global-map (kbd "M-/") #'hippie-expand)
117 (setq hippie-expand-try-functions-list
119 try-expand-dabbrev-all-buffers
120 try-expand-dabbrev-from-kill
121 try-complete-file-name-partially
122 try-complete-file-name
123 try-expand-all-abbrevs
126 try-complete-lisp-symbol-partially
127 try-complete-lisp-symbol))
129 (setq isearch-lazy-count t
130 search-whitespace-regexp ".*?"
131 isearch-allow-scroll 'unlimited)
133 (defun op/buffer-to-side-window (place)
134 "Place the current buffer in the side window at PLACE."
135 (interactive (list (intern
136 (completing-read "Which side: "
137 '(top left right bottom)))))
138 (let ((buf (current-buffer)))
139 (display-buffer-in-side-window
140 buf `((window-height . 0.15)
143 (window-parameters . ((no-delete-other-windows . t)))))
146 (defun op/visit-new-migration-file (name)
147 "Visit a new SQL migration file named after NAME."
148 (interactive "Mname: ")
149 (let* ((name (replace-regexp-in-string " " "-" (string-trim name)))
150 (f (format "%s-%s.sql"
151 (format-time-string "%Y%m%d%H%M")
155 (defun op/fill-or-unfill (fn &optional justify region)
156 "Meant to be an adviced :around `fill-paragraph'.
157 FN is the original `fill-column'. If `last-command' is
158 `fill-paragraph', unfill it, fill it otherwise. Inspired from a
159 post on endless parentheses. Optional argument JUSTIFY and
160 REGION are passed to `fill-paragraph'."
162 (if (eq last-command 'fill-paragraph)
163 (progn (setq this-command nil)
166 (funcall fn justify region)))
167 (advice-add 'fill-paragraph :around #'op/fill-or-unfill)
169 (defmacro op/deftranspose (name scope key doc)
170 "Macro to produce transposition functions.
171 NAME is the function's symbol. SCOPE is the text object to
172 operate on. Optional DOC is the function's docstring.
174 Transposition over an active region will swap the object at
175 mark (region beginning) with the one at point (region end).
177 It can optionally define a key for the defined function in the
178 `global-map' if KEY is passed.
180 Originally from protesilaos' dotemacs."
181 (declare (indent defun))
186 (let ((x (intern (format "transpose-%s" ,scope))))
191 `(define-key global-map (kbd ,key) #',name))))
193 (op/deftranspose op/transpose-lines "lines" "C-x C-t"
194 "Transpose lines or swap over active region.")
196 (op/deftranspose op/transpose-paragraphs "paragraphs" "C-S-t"
197 "Transpose paragraph or swap over active region.")
199 (op/deftranspose op/transpose-sentences "sentences" "C-x M-t"
200 "Transpose sentences or swap over active region.")
202 (op/deftranspose op/transpose-sexps "sexps" "C-M-t"
203 "Transpose sexps or swap over active region.")
205 (op/deftranspose op/transpose-words "words" "M-t"
206 "Transpose words or swap over active region.")
208 (defun op/narrow-or-widen-dwim (p)
209 "Widen if the buffer is narrowed, narrow-dwim otherwise.
210 Dwim means: region, org-src-block, org-subtree or defun,
211 whichever applies first. Narrowing to org-src-blocks actually
212 calls `org-edit-src-code'.
214 With prefix P, don't widen, just narrow even if buffer is already
215 narrowed. With P being -, narrow to page instead of to defun.
217 Taken from endless parentheses."
219 (declare (interactive-only))
220 (cond ((and (buffer-narrowed-p) (not p)) (widen))
222 (narrow-to-region (region-beginning)
224 ((derived-mode-p 'org-mode)
225 ;; `org-edit-src-code' isn't a real narrowing
226 (cond ((ignore-errors (org-edit-src-code) t))
227 ((ignore-errors (org-narrow-to-block) t))
228 (t (org-narrow-to-subtree))))
229 ((eql p '-) (narrow-to-page))
230 (t (narrow-to-defun))))
232 (define-key global-map (kbd "C-c w") #'op/narrow-or-widen-dwim)
234 (with-eval-after-load 'dired
235 (add-hook 'dired-mode-hook #'dired-hide-details-mode)
236 (add-hook 'dired-mode-hook #'dired-omit-mode)
238 (define-key dired-mode-map (kbd "C-c w") #'wdired-change-to-wdired-mode)
241 (setq dired-listing-switches "-lahF"
243 dired-deletion-confirmer #'y-or-n-p
244 dired-do-revert-buffer t))
246 ;; just like telescope!
247 (with-eval-after-load 'diff-mode
248 (define-key diff-mode-map (kbd "M-SPC") #'scroll-down-command))
250 (with-eval-after-load 'elisp-mode
251 (add-hook 'emacs-lisp-mode-hook #'checkdoc-minor-mode)
252 (add-hook 'emacs-lisp-mode-hook #'prettify-symbols-mode)
253 (let ((map emacs-lisp-mode-map))
254 (define-key map (kbd "C-c C-k") #'eval-buffer)
255 (define-key map (kbd "C-c k") #'op/ert-all)
256 (define-key map (kbd "C-c C-z") #'op/ielm-repl)))
258 (with-eval-after-load 'help
259 (add-hook 'help-mode-hook #'visual-line-mode))
262 (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
264 ;; packages that i want to be installed
265 (dolist (pkg '(vc-got pdf-tools eglot nameless sly cider go-mode web-mode
266 lua-mode markdown-mode yaml-mode gemini-mode elfeed
267 form-feed shackle embark consult mct puni))
268 (unless (package-installed-p pkg)
269 (message "Installing %s" pkg)
270 (package-install pkg)))
272 (global-form-feed-mode +1)
274 (add-hook 'text-mode-hook #'puni-mode)
275 (add-hook 'prog-mode-hook #'puni-mode)
276 (define-key puni-mode-map (kbd "C-)") #'puni-slurp-forward)
277 (define-key puni-mode-map (kbd "C-(") #'puni-barf-forward)
279 (setq completion-styles '(basic substring initials flex partial-completion))
281 (require 'consult) ;; some stuff lacks an autoload and i don't want to debug it
283 (cl-loop for (key . func) in '(("C-x :" . consult-complex-command)
284 ("C-x b" . consult-buffer)
285 ("C-x 4 b" . consult-buffer-other-window)
286 ("C-x 5 b" . consult-buffer-other-frame)
287 ("C-x r b" . consult-bookmark)
288 ("C-x p b" . consult-project-buffer)
289 ("M-y" . consult-yank-pop)
290 ("M-g g" . consult-goto-line)
291 ("M-g M-g" . consult-goto-line)
292 ("M-g m" . consult-mark)
293 ("M-g i" . consult-imenu)
294 ("M-s l" . consult-line)
295 ("M-s L" . consult-line-multi)
296 ("M-s m" . consult-multi-occur))
297 do (define-key global-map (kbd key) func))
298 (add-hook 'completion-list-mode-hook #'consult-preview-at-point-mode)
300 (setq completions-detailed t)
301 (mct-minibuffer-mode +1)
304 ;; override the binding for the annoying mct-backward-updir.
305 (define-key mct-minibuffer-local-filename-completion-map
306 (kbd "DEL") #'backward-delete-char)
308 (setq mct-remove-shadowed-file-names t
309 mct-completions-format 'one-column
310 mct-completion-passlist '(Info-goto-node
320 (with-eval-after-load 'go-mode
321 (add-hook 'go-mode-hook #'subword-mode))
323 (with-eval-after-load 'eglot
324 (define-key eglot-mode-map (kbd "<f1>") #'eglot-code-actions)
325 (define-key eglot-mode-map (kbd "<f2>") #'eglot-format)
326 (add-to-list 'eglot-ignored-server-capabilites
327 :documentHighlightProvider)
328 (add-to-list 'eglot-server-programs
329 '(c-mode . ("clangd" "--header-insertion=never"))))
331 (add-hook 'emacs-lisp-mode #'nameless-mode)
332 (with-eval-after-load 'nameless
333 (setq nameless-private-prefix t
334 nameless-affect-indentation-and-filling nil)
335 (define-key emacs-lisp-mode-map (kbd "_") #'nameless-insert-name-or-self-insert))
337 (add-to-list 'auto-mode-alist '("\\.html\\'" . web-mode))
338 (with-eval-after-load 'web-mode
339 (setq web-mode-markup-indent-offset 2
340 web-mode-css-indent-offset 2
341 web-mode-style-padding 0
342 web-mode-enable-engine-detection t)
343 (add-hook 'web-mode-hook #'op/disable-tabs)
345 ;; fix .dir-locals.el
346 (defun op/web-mode-fix-dir-locals ()
347 (when (derived-mode-p major-mode 'web-mode)
348 (web-mode-guess-engine-and-content-type)))
349 (add-hook 'hack-local-variables-hook #'op/web-mode-fix-dir-locals))
351 (with-eval-after-load 'css-mode
352 (add-hook 'css-mode-hook #'op/disable-tabs))
354 (with-eval-after-load 'flymake
355 (define-key prog-mode-map (kbd "C-c ! n") #'flymake-goto-next-error)
356 (define-key prog-mode-map (kbd "C-c ! p") #'flymake-goto-prev-error))
358 (with-eval-after-load 'cc-mode
359 (setq c-basic-offset 8
360 c-default-style "K&R")
361 (dolist (hook '(c-mode-hook c++-mode-hook))
362 (add-hook hook #'abbrev-mode)
363 (add-hook hook #'subword-mode))
364 (defun op/c-indent ()
366 (c-set-offset 'arglist-intro '+)
367 (c-set-offset 'arglist-cont-nonempty '*))
368 (add-hook 'c-mode-hook #'op/c-indent)
370 (defun op/c-add-include (path &optional localp)
371 "Include PATH at the start of the file.
372 If LOCALP is non-nil, the include will be \"local\"."
373 (interactive "Mheader to include: \nP")
376 "^#[ \t]*include[ \t]*\""
377 "^#[ \t]*include[ \t]*<"))
378 (ignore-re "^#include \"compat.h\"")
380 (goto-char (point-min))
381 (while (not (or (and (looking-at re)
382 (not (looking-at ignore-re)))
386 (error "Don't know where to insert the header"))
388 (insert "#include " (if localp "\"\"" "<>"))
391 (move-beginning-of-line 1)
394 (while (and (looking-at re)
397 (sort-lines nil start (point)))))
398 (define-key c-mode-map (kbd "C-c C-a") #'op/c-add-include))
400 (with-eval-after-load 'perl-mode
401 (setq perl-indent-level 8))
403 (with-eval-after-load 'sh-script
404 (setq sh-basic-offset 8
405 sh-indent-after-loop-construct 8
406 sh-indent-after-continuation nil))
410 (setq eshell-hist-ignoredups t)
412 (defun op/eshell-bufname (dir)
413 (concat "*eshell " (expand-file-name dir) "*"))
415 (defun op/eshell (arg)
416 "Run or jump to eshell in current project.
417 If called with prefix argument ARG always create a new eshell
420 (let* ((proj (project-current))
421 (dir (if (and proj (not arg))
424 (default-directory dir)
425 (eshell-buffer-name (let ((name (op/eshell-bufname dir)))
427 (generate-new-buffer name)
430 (define-key global-map (kbd "C-c e") #'op/eshell)
432 (with-eval-after-load 'eshell
433 (setq eshell-save-history-on-exit t
434 eshell-history-size 1024
436 eshell-compl-dir-ignore
437 "\\`\\(\\.\\.?\\|CVS\\|\\.svn\\|\\.git\\|\\.got\\)/\\'")
439 (defun op/eshell-after-cd (&rest _)
440 (rename-buffer (op/eshell-bufname default-directory) t))
442 (advice-add #'eshell/cd :after #'op/eshell-after-cd))
444 (with-eval-after-load 'esh-mode
445 (defun op/clear-eshell ()
447 (let ((inhibit-read-only t))
449 (eshell-send-input)))
451 (define-key eshell-command-map (kbd "M-o") #'op/clear-eshell))
455 (unless (package-installed-p 'sndio)
456 (package-install-file "~/w/sndio.el/sndio.el"))
460 (unless (package-installed-p 'saturn)
461 (package-install-file "~/w/saturn/GUI/saturn.el"))
465 (unless (package-installed-p 'simple-pass)
466 (package-install-file "~/.emacs.d/simple-pass.el"))
467 (define-key global-map (kbd "C-z p") #'simple-pass-copy)
473 (define-key global-map (kbd "C-x w") #'elfeed)
474 (with-eval-after-load 'elfeed
475 (define-key elfeed-show-mode-map (kbd "q") #'delete-window)
476 (define-key elfeed-show-mode-map (kbd "S-SPC") #'scroll-down-command)
477 (define-key elfeed-show-mode-map (kbd "M-SPC") #'scroll-down-command)
478 (setq elfeed-show-entry-switch #'pop-to-buffer
480 '("https://undeadly.org/cgi?action=rss&full=yes&items=10"
481 "http://www.tedunangst.com/flak/rss"
482 "https://www.dragonflydigest.com/feed"
483 "https://www.mirbsd.org/news.rss"
484 "https://www.mirbsd.org/announce.rss"
485 "https://bentsukun.ch/index.xml"
486 "https://drewdevault.com/feed.xml"
487 "https://www.cambus.net/atom.xml"
488 "https://dataswamp.org/~solene/rss.xml"
489 "https://briancallahan.net/blog/feed.xml"
490 "https://www.poolp.org/index.xml"
491 "https://jcs.org/rss"
492 "https://sanctum.geek.nz/arabesque/feed/"
493 "https://tech.toryanderson.com/"
494 "https://alexschroeder.ch/wiki?action=journal;search=-tag:rpg -tag:rsp;lang=en;title=English Diary without RPG Pages"
495 "http://boston.conman.org/bostondiaries.rss"
496 "https://emacsninja.com/feed.atom"
497 "https://bsdly.blogspot.com/feeds/posts/default"
498 "https://crawshaw.io/atom.xml"
499 "https://nullprogram.com/feed/"
500 "http://pragmaticemacs.com/feed/"
501 "https://emacsnotes.wordpress.com/feed/"
502 "https://metaredux.com/feed.xml"
503 "https://emacsredux.com/atom.xml"
504 "https://endlessparentheses.com/atom.xml"
505 "https://www.masteringemacs.org/feed"
506 "https://cestlaz.github.io/rss.xml"
507 "https://utcc.utoronto.ca/~cks/space/blog/?atom"
508 "https://irreal.org/blog/?feed=rss2"
509 "https://jao.io/blog/rss.xml"
510 "https://planet.lisp.org/rss20.xml"
511 "https://insideclojure.org/feed.xml"
512 "https://tech.toryanderson.com/index.xml"
513 "https://vermaden.wordpress.com/feed/"
514 "https://www.arp242.net/feed.xml"
515 "https://tymoon.eu/api/reader/atom"
516 "https://venam.nixers.net/blog/feed.xml"
517 "https://www.omarpolo.com/rss.xml"
518 "https://owarisubs.lacumpa.biz/feed/"
519 "https://asenshi.moe/feed/"
520 "https://godotengine.org/rss.xml"
522 "https://adventofcomputing.libsyn.com/rss"
524 "https://github.com/go-gitea/gitea/releases.atom"
526 "https://nitter.pussthecat.org/NanoRaptor/rss"
528 "https://github.com/yshui/picom/releases.atom"
529 "https://github.com/vslavik/poedit/releases.atom"
530 "https://github.com/TokTok/c-toxcore/releases.atom"
531 "https://github.com/alexander-akhmetov/python-telegram/releases.atom"
532 "https://github.com/paul-nameless/tg/releases.atom"
533 "https://github.com/YACReader/yacreader/releases.atom"
534 "https://github.com/luarocks/luarocks/releases.atom"
535 "https://github.com/okbob/pspg/releases.atom"
536 "https://github.com/taisei-project/taisei/releases.atom"
537 "https://github.com/recp/cglm/releases.atom"
538 "https://git.sr.ht/~rjarry/aerc/refs/rss.xml"
540 "https://causal.agency/list/pounce.atom"
542 "https://www.crimsonmagic.me/feed/"
543 "https://fullybookedtls.wordpress.com/feed/"
545 "https://draculadaily.substack.com/feed")))
547 (setq shackle-default-rule nil
549 (let ((repls "\\*\\(cider-repl\\|sly-mrepl\\|ielm\\)")
550 (godot "\\*godot - .*\\*")
551 (vcs "\\*\\(Flymake\\|Package-Lint\\|vc-\\(git\\|got\\) :\\).*")
552 (elfeed "\\*elfeed-entry\\*")
553 (vmd "\\*vmd console .*"))
554 `((compilation-mode :noselect t
557 ("*Async Shell Command*" :ignore t)
564 (occur-mode :select t
567 (diff-mode :select t)
585 (define-key global-map (kbd "M-g e") #'embark-act)