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 ;; "diff refinement", i.e. highlighting the changes in a more granular
35 ;; way, is quite awful to have it enabled by default. sometimes is
36 ;; useful, but for me it's more of a visual noise most of the times.
37 (setq diff-refine nil)
39 (define-key global-map (kbd "C-x C-b") #'ibuffer)
40 (define-key global-map (kbd "M-g i") #'imenu)
43 "Just like `imenu', but always flattened!"
47 (define-key minibuffer-mode-map (kbd "C-w") #'backward-kill-word)
49 (defun op/reverse-other-window ()
50 "Like `other-window', but reverse."
53 (define-key global-map (kbd "C-x O") #'op/reverse-other-window)
55 (define-key global-map (kbd "C-x v f") #'vc-pull)
57 (setq uniquify-buffer-name-style 'forward
58 uniquify-strip-common-suffix t)
60 (setq-default scroll-up-aggressively 0.0
61 scroll-down-aggressively 0.0
62 scroll-preserve-screen-position t
63 next-screen-context-lines 1)
65 (define-key global-map (kbd "M-z") #'zap-up-to-char)
68 (setq whitespace-style '(face trailing)
69 backward-delete-char-untabify-method 'hungry
70 tab-always-indent 'complete
72 sentence-end-double-space t)
73 (setq-default indent-tabs-mode t)
75 (defun op/enable-tabs ()
76 "Enable `indent-tabs-mode' in the current buffer."
78 (setq-local indent-tabs-mode t))
80 (defun op/disable-tabs ()
81 "Disable `indent-tabs-mode' in the current buffer."
83 (setq-local indent-tabs-mode nil))
85 (add-hook 'conf-mode-hook #'op/enable-tabs)
86 (add-hook 'text-mode-hook #'op/enable-tabs)
87 (add-hook 'prog-mode-hook #'op/enable-tabs)
88 (add-hook 'prog-mode-hook #'whitespace-mode)
89 (add-hook 'text-mode-hook #'whitespace-mode)
91 (dolist (hook '(emacs-lisp-mode-hook
93 clojurescript-mode-hook
96 (add-hook hook #'op/disable-tabs))
98 (with-eval-after-load 'log-edit
99 (add-hook 'log-edit-mode #'auto-fill-mode))
101 ;; free the c-z binding
102 (define-key global-map (kbd "C-z") nil)
103 (define-key global-map (kbd "C-z V") #'variable-pitch-mode)
104 (define-key global-map (kbd "C-z n") #'display-line-numbers-mode)
106 (define-key global-map (kbd "M-SPC") #'cycle-spacing)
107 (define-key global-map (kbd "M-u") #'upcase-dwim)
108 (define-key global-map (kbd "M-l") #'downcase-dwim)
109 (define-key global-map (kbd "M-c") #'capitalize-dwim)
111 (let ((font "-misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso10646-1"))
112 (set-frame-font font nil t)
113 (add-to-list 'default-frame-alist `(font . ,font)))
115 ;; fix the emojis too
116 (set-fontset-font t 'emoji '("Noto Emoji" . "iso10646-1")
122 (setq history-delete-duplicates t
124 savehist-save-minibuffer-history t)
125 (electric-pair-mode +1)
127 (define-key global-map (kbd "M-/") #'hippie-expand)
128 (setq hippie-expand-try-functions-list
130 try-expand-dabbrev-all-buffers
131 try-expand-dabbrev-from-kill
132 try-complete-file-name-partially
133 try-complete-file-name
134 try-expand-all-abbrevs
137 try-complete-lisp-symbol-partially
138 try-complete-lisp-symbol))
140 (setq isearch-lazy-count t
141 search-whitespace-regexp ".*?"
142 isearch-allow-scroll 'unlimited)
144 (defun op/buffer-to-side-window (place)
145 "Place the current buffer in the side window at PLACE."
146 (interactive (list (intern
147 (completing-read "Which side: "
148 '(top left right bottom)))))
149 (let ((buf (current-buffer)))
150 (display-buffer-in-side-window
151 buf `((window-height . 0.15)
154 (window-parameters . ((no-delete-other-windows . t)))))
157 (defun op/visit-new-migration-file (name)
158 "Visit a new SQL migration file named after NAME."
159 (interactive "Mname: ")
160 (let* ((name (replace-regexp-in-string " " "-" (string-trim name)))
161 (f (format "%s-%s.sql"
162 (format-time-string "%Y%m%d%H%M")
166 (defun op/fill-or-unfill (fn &optional justify region)
167 "Meant to be an adviced :around `fill-paragraph'.
168 FN is the original `fill-column'. If `last-command' is
169 `fill-paragraph', unfill it, fill it otherwise. Inspired from a
170 post on endless parentheses. Optional argument JUSTIFY and
171 REGION are passed to `fill-paragraph'."
173 (if (eq last-command 'fill-paragraph)
174 (progn (setq this-command nil)
177 (funcall fn justify region)))
178 (advice-add 'fill-paragraph :around #'op/fill-or-unfill)
180 (defmacro op/deftranspose (name scope key doc)
181 "Macro to produce transposition functions.
182 NAME is the function's symbol. SCOPE is the text object to
183 operate on. Optional DOC is the function's docstring.
185 Transposition over an active region will swap the object at
186 mark (region beginning) with the one at point (region end).
188 It can optionally define a key for the defined function in the
189 `global-map' if KEY is passed.
191 Originally from protesilaos' dotemacs."
192 (declare (indent defun))
197 (let ((x (intern (format "transpose-%s" ,scope))))
202 `(define-key global-map (kbd ,key) #',name))))
204 (op/deftranspose op/transpose-lines "lines" "C-x C-t"
205 "Transpose lines or swap over active region.")
207 (op/deftranspose op/transpose-paragraphs "paragraphs" "C-S-t"
208 "Transpose paragraph or swap over active region.")
210 (op/deftranspose op/transpose-sentences "sentences" "C-x M-t"
211 "Transpose sentences or swap over active region.")
213 (op/deftranspose op/transpose-sexps "sexps" "C-M-t"
214 "Transpose sexps or swap over active region.")
216 (op/deftranspose op/transpose-words "words" "M-t"
217 "Transpose words or swap over active region.")
219 (defun op/narrow-or-widen-dwim (p)
220 "Widen if the buffer is narrowed, narrow-dwim otherwise.
221 Dwim means: region, org-src-block, org-subtree or defun,
222 whichever applies first. Narrowing to org-src-blocks actually
223 calls `org-edit-src-code'.
225 With prefix P, don't widen, just narrow even if buffer is already
226 narrowed. With P being -, narrow to page instead of to defun.
228 Taken from endless parentheses."
230 (declare (interactive-only))
231 (cond ((and (buffer-narrowed-p) (not p)) (widen))
233 (narrow-to-region (region-beginning)
235 ((derived-mode-p 'org-mode)
236 ;; `org-edit-src-code' isn't a real narrowing
237 (cond ((ignore-errors (org-edit-src-code) t))
238 ((ignore-errors (org-narrow-to-block) t))
239 (t (org-narrow-to-subtree))))
240 ((eql p '-) (narrow-to-page))
241 (t (narrow-to-defun))))
243 (define-key global-map (kbd "C-c w") #'op/narrow-or-widen-dwim)
245 (with-eval-after-load 'dired
246 (add-hook 'dired-mode-hook #'dired-hide-details-mode)
247 (add-hook 'dired-mode-hook #'dired-omit-mode)
249 (define-key dired-mode-map (kbd "C-c w") #'wdired-change-to-wdired-mode)
252 (setq dired-listing-switches "-lahF"
254 dired-deletion-confirmer #'y-or-n-p
255 dired-do-revert-buffer t))
257 ;; just like telescope!
258 (with-eval-after-load 'diff-mode
259 (define-key diff-mode-map (kbd "M-SPC") #'scroll-down-command))
261 (with-eval-after-load 'elisp-mode
262 (add-hook 'emacs-lisp-mode-hook #'checkdoc-minor-mode)
263 (add-hook 'emacs-lisp-mode-hook #'prettify-symbols-mode)
264 (let ((map emacs-lisp-mode-map))
265 (define-key map (kbd "C-c C-k") #'eval-buffer)
266 (define-key map (kbd "C-c k") #'op/ert-all)
267 (define-key map (kbd "C-c C-z") #'op/ielm-repl)))
269 (with-eval-after-load 'help
270 (add-hook 'help-mode-hook #'visual-line-mode))
273 (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
275 ;; packages that i want to be installed
276 (dolist (pkg '(vc-got pdf-tools eglot nameless sly cider go-mode web-mode
277 lua-mode markdown-mode yaml-mode gemini-mode elfeed
278 form-feed shackle embark consult mct puni))
279 (unless (package-installed-p pkg)
280 (message "Installing %s" pkg)
281 (package-install pkg)))
283 (global-form-feed-mode +1)
285 (add-hook 'text-mode-hook #'puni-mode)
286 (add-hook 'prog-mode-hook #'puni-mode)
287 (define-key puni-mode-map (kbd "C-)") #'puni-slurp-forward)
288 (define-key puni-mode-map (kbd "C-(") #'puni-barf-forward)
290 (setq completion-styles '(basic substring initials flex partial-completion))
292 (require 'consult) ;; some stuff lacks an autoload and i don't want to debug it
294 (cl-loop for (key . func) in '(("C-x :" . consult-complex-command)
295 ("C-x b" . consult-buffer)
296 ("C-x 4 b" . consult-buffer-other-window)
297 ("C-x 5 b" . consult-buffer-other-frame)
298 ("C-x r b" . consult-bookmark)
299 ("C-x p b" . consult-project-buffer)
300 ("M-y" . consult-yank-pop)
301 ("M-g g" . consult-goto-line)
302 ("M-g M-g" . consult-goto-line)
303 ("M-g m" . consult-mark)
304 ("M-g i" . consult-imenu)
305 ("M-s l" . consult-line)
306 ("M-s L" . consult-line-multi)
307 ("M-s m" . consult-multi-occur))
308 do (define-key global-map (kbd key) func))
309 (add-hook 'completion-list-mode-hook #'consult-preview-at-point-mode)
311 (setq completions-detailed t)
312 (mct-minibuffer-mode +1)
315 ;; override the binding for the annoying mct-backward-updir.
316 (define-key mct-minibuffer-local-filename-completion-map
317 (kbd "DEL") #'backward-delete-char)
319 (setq mct-remove-shadowed-file-names t
320 mct-completions-format 'one-column
321 mct-completion-passlist '(Info-goto-node
328 consult-project-buffer
332 (with-eval-after-load 'cider
333 (define-key cider-repl-mode-map (kbd "C-c M-o") #'cider-repl-clear-buffer))
335 (with-eval-after-load 'go-mode
336 (add-hook 'go-mode-hook #'subword-mode))
338 (with-eval-after-load 'eglot
339 (define-key eglot-mode-map (kbd "<f1>") #'eglot-code-actions)
340 (define-key eglot-mode-map (kbd "<f2>") #'eglot-format)
341 (add-to-list 'eglot-ignored-server-capabilites
342 :documentHighlightProvider)
343 (add-to-list 'eglot-server-programs
344 '(c-mode . ("clangd" "--header-insertion=never"))))
346 (add-hook 'emacs-lisp-mode #'nameless-mode)
347 (with-eval-after-load 'nameless
348 (setq nameless-private-prefix t
349 nameless-affect-indentation-and-filling nil)
350 (define-key emacs-lisp-mode-map (kbd "_") #'nameless-insert-name-or-self-insert))
352 (add-to-list 'auto-mode-alist '("\\.html\\'" . web-mode))
353 (with-eval-after-load 'web-mode
354 (setq web-mode-markup-indent-offset 2
355 web-mode-css-indent-offset 2
356 web-mode-style-padding 0
357 web-mode-enable-engine-detection t)
358 (add-hook 'web-mode-hook #'op/disable-tabs)
360 ;; fix .dir-locals.el
361 (defun op/web-mode-fix-dir-locals ()
362 (when (derived-mode-p major-mode 'web-mode)
363 (web-mode-guess-engine-and-content-type)))
364 (add-hook 'hack-local-variables-hook #'op/web-mode-fix-dir-locals))
366 (with-eval-after-load 'css-mode
367 (add-hook 'css-mode-hook #'op/disable-tabs))
369 (with-eval-after-load 'flymake
370 (define-key prog-mode-map (kbd "C-c ! n") #'flymake-goto-next-error)
371 (define-key prog-mode-map (kbd "C-c ! p") #'flymake-goto-prev-error))
373 (with-eval-after-load 'cc-mode
374 (setq c-basic-offset 8
375 c-default-style "K&R")
376 (dolist (hook '(c-mode-hook c++-mode-hook))
377 (add-hook hook #'abbrev-mode)
378 (add-hook hook #'subword-mode))
379 (defun op/c-indent ()
381 (c-set-offset 'arglist-intro '+)
382 (c-set-offset 'arglist-cont-nonempty '*))
383 (add-hook 'c-mode-hook #'op/c-indent)
385 (defun op/c-add-include (path &optional localp)
386 "Include PATH at the start of the file.
387 If LOCALP is non-nil, the include will be \"local\"."
388 (interactive "Mheader to include: \nP")
391 "^#[ \t]*include[ \t]*\""
392 "^#[ \t]*include[ \t]*<"))
393 (ignore-re "^#include \"compat.h\"")
395 (goto-char (point-min))
396 (while (not (or (and (looking-at re)
397 (not (looking-at ignore-re)))
401 (error "Don't know where to insert the header"))
403 (insert "#include " (if localp "\"\"" "<>"))
406 (move-beginning-of-line 1)
409 (while (and (looking-at re)
412 (sort-lines nil start (point)))))
413 (define-key c-mode-map (kbd "C-c C-a") #'op/c-add-include))
415 (with-eval-after-load 'perl-mode
416 (setq perl-indent-level 8))
418 (with-eval-after-load 'sh-script
419 (setq sh-basic-offset 8
420 sh-indent-after-loop-construct 8
421 sh-indent-after-continuation nil))
425 (setq eshell-hist-ignoredups t)
427 (defun op/eshell-bufname (dir)
428 (concat "*eshell " (expand-file-name dir) "*"))
430 (defun op/eshell (arg)
431 "Run or jump to eshell in current project.
432 If called with prefix argument ARG always create a new eshell
435 (let* ((proj (project-current))
436 (dir (if (and proj (not arg))
439 (default-directory dir)
440 (eshell-buffer-name (let ((name (op/eshell-bufname dir)))
442 (generate-new-buffer name)
445 (define-key global-map (kbd "C-c e") #'op/eshell)
447 (with-eval-after-load 'eshell
448 (setq eshell-save-history-on-exit t
449 eshell-history-size 1024
451 eshell-compl-dir-ignore
452 "\\`\\(\\.\\.?\\|CVS\\|\\.svn\\|\\.git\\|\\.got\\)/\\'")
454 (defun op/eshell-after-cd (&rest _)
455 (rename-buffer (op/eshell-bufname default-directory) t))
457 (advice-add #'eshell/cd :after #'op/eshell-after-cd))
459 (with-eval-after-load 'esh-mode
460 (defun op/clear-eshell ()
462 (let ((inhibit-read-only t))
464 (eshell-send-input)))
466 (define-key eshell-command-map (kbd "M-o") #'op/clear-eshell))
470 (unless (package-installed-p 'sndio)
471 (package-install-file "~/w/sndio.el/sndio.el"))
475 (unless (package-installed-p 'saturn)
476 (package-install-file "~/w/saturn/GUI/saturn.el"))
480 (unless (package-installed-p 'simple-pass)
481 (package-install-file "~/.emacs.d/simple-pass.el"))
482 (define-key global-map (kbd "C-z p") #'simple-pass-copy)
488 (define-key global-map (kbd "C-x w") #'elfeed)
489 (with-eval-after-load 'elfeed
490 (define-key elfeed-show-mode-map (kbd "q") #'delete-window)
491 (define-key elfeed-show-mode-map (kbd "S-SPC") #'scroll-down-command)
492 (define-key elfeed-show-mode-map (kbd "M-SPC") #'scroll-down-command)
493 (setq elfeed-show-entry-switch #'pop-to-buffer
495 '("https://undeadly.org/cgi?action=rss&full=yes&items=10"
496 "http://www.tedunangst.com/flak/rss"
497 "https://www.dragonflydigest.com/feed"
498 "https://www.mirbsd.org/news.rss"
499 "https://www.mirbsd.org/announce.rss"
500 "https://bentsukun.ch/index.xml"
501 "https://drewdevault.com/feed.xml"
502 "https://www.cambus.net/atom.xml"
503 "https://dataswamp.org/~solene/rss.xml"
504 "https://briancallahan.net/blog/feed.xml"
505 "https://www.poolp.org/index.xml"
506 "https://jcs.org/rss"
507 "https://sanctum.geek.nz/arabesque/feed/"
508 "https://tech.toryanderson.com/"
509 "https://alexschroeder.ch/wiki?action=journal;search=-tag:rpg -tag:rsp;lang=en;title=English Diary without RPG Pages"
510 "http://boston.conman.org/bostondiaries.rss"
511 "https://emacsninja.com/feed.atom"
512 "https://bsdly.blogspot.com/feeds/posts/default"
513 "https://crawshaw.io/atom.xml"
514 "https://nullprogram.com/feed/"
515 "http://pragmaticemacs.com/feed/"
516 "https://emacsnotes.wordpress.com/feed/"
517 "https://metaredux.com/feed.xml"
518 "https://emacsredux.com/atom.xml"
519 "https://endlessparentheses.com/atom.xml"
520 "https://www.masteringemacs.org/feed"
521 "https://cestlaz.github.io/rss.xml"
522 "https://utcc.utoronto.ca/~cks/space/blog/?atom"
523 "https://irreal.org/blog/?feed=rss2"
524 "https://jao.io/blog/rss.xml"
525 "https://planet.lisp.org/rss20.xml"
526 "https://insideclojure.org/feed.xml"
527 "https://tech.toryanderson.com/index.xml"
528 "https://vermaden.wordpress.com/feed/"
529 "https://www.arp242.net/feed.xml"
530 "https://tymoon.eu/api/reader/atom"
531 "https://venam.nixers.net/blog/feed.xml"
532 "https://www.omarpolo.com/rss.xml"
533 "https://owarisubs.lacumpa.biz/feed/"
534 "https://asenshi.moe/feed/"
535 "https://godotengine.org/rss.xml"
537 "https://adventofcomputing.libsyn.com/rss"
539 "https://github.com/go-gitea/gitea/releases.atom"
541 "https://nitter.pussthecat.org/NanoRaptor/rss"
543 "https://github.com/yshui/picom/releases.atom"
544 "https://github.com/vslavik/poedit/releases.atom"
545 "https://github.com/TokTok/c-toxcore/releases.atom"
546 "https://github.com/alexander-akhmetov/python-telegram/releases.atom"
547 "https://github.com/paul-nameless/tg/releases.atom"
548 "https://github.com/YACReader/yacreader/releases.atom"
549 "https://github.com/luarocks/luarocks/releases.atom"
550 "https://github.com/okbob/pspg/releases.atom"
551 "https://github.com/taisei-project/taisei/releases.atom"
552 "https://github.com/recp/cglm/releases.atom"
553 "https://github.com/SCons/scons/releases.atom"
554 "https://git.sr.ht/~rjarry/aerc/refs/rss.xml"
556 "https://causal.agency/list/pounce.atom"
558 "https://www.crimsonmagic.me/feed/"
559 "https://fullybookedtls.wordpress.com/feed/"
561 "https://draculadaily.substack.com/feed")))
563 (setq shackle-default-rule nil
565 (let ((repls "\\*\\(cider-repl\\|sly-mrepl\\|ielm\\)")
566 (godot "\\*godot - .*\\*")
567 (vcs "\\*\\(Flymake\\|Package-Lint\\|vc-\\(git\\|got\\) :\\).*")
568 (elfeed "\\*elfeed-entry\\*")
569 (vmd "\\*vmd console .*"))
570 `(("*Async Shell Command*" :ignore t)
577 (occur-mode :select t
580 (diff-mode :select t)
598 ;; (setq display-buffer-alist nil)
600 (define-key global-map (kbd "M-g e") #'embark-act)
602 (with-eval-after-load 'embark
603 (defun op/target-filename+line ()
604 "Target a file with optional line number: file[:number]."
606 (let* ((beg (progn (skip-chars-backward "^[:space:]\n")
608 (end (progn (skip-chars-forward "^[:space:]\n")
610 (str (buffer-substring-no-properties beg end)))
612 (when (and (progn (goto-char beg)
613 (ffap-file-at-point))
614 (string-match ".+\\(:[[:digit:]]+\\)?:?" str))
615 `(file ,str ,beg . ,end))))))
617 (add-to-list 'embark-target-finders #'op/target-filename+line)
619 (defun op/acme-find-file (filename)
620 "Visit FILENAME like `find-file', but also jump to line if provided."
622 (if (not (string-match "\\(.+\\):\\([[:digit:]]+\\)" filename))
624 (let ((path (match-string 1 filename))
625 (line (string-to-number (match-string 2 filename))))
626 (with-current-buffer (find-file path)
627 (goto-line line))))))
629 (define-key embark-file-map (kbd "RET") #'op/acme-find-file))