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