Blob


1 ;; get rid of custom from my init file
2 (setq custom-file "~/.emacs.d/emacs-custom.el")
3 (load custom-file)
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
12 x-stretch-cursor t
13 require-final-newline t
14 visible-bell nil
15 load-prefer-newer t
16 tab-bar-show 1
17 enable-recursive-minibuffers t
18 imenu-auto-rescan 1
19 use-short-answers 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)
33 (defun op/imenu ()
34 "Just like `imenu', but always flattened!"
35 (interactive ))
37 ;; mg-like
38 (define-key minibuffer-mode-map (kbd "C-w") #'backward-kill-word)
40 (defun op/reverse-other-window ()
41 "Like `other-window', but reverse."
42 (interactive "")
43 (other-window -1))
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)
56 (require 'whitespace)
57 (setq whitespace-style '(face trailing)
58 backward-delete-char-untabify-method 'hungry
59 tab-always-indent 'complete
60 tab-width 8
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."
66 (interactive)
67 (setq-local indent-tabs-mode t))
69 (defun op/disable-tabs ()
70 "Disable `indent-tabs-mode' in the current buffer."
71 (interactive)
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
81 clojure-mode-hook
82 clojurescript-mode-hook
83 clojurec-mode-hook
84 scss-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")
106 nil 'prepend)
108 ;; some cool stuff
109 (save-place-mode +1)
110 (savehist-mode +1)
111 (setq history-delete-duplicates t
112 history-length 1000
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
118 '(try-expand-dabbrev
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
124 try-expand-list
125 try-expand-line
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)
141 (side . ,place)
142 (slot . -1)
143 (window-parameters . ((no-delete-other-windows . t)))))
144 (delete-window)))
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")
152 name)))
153 (find-file f)))
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'."
161 (let ((fill-column
162 (if (eq last-command 'fill-paragraph)
163 (progn (setq this-command nil)
164 (point-max))
165 fill-column)))
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))
182 `(progn
183 (defun ,name (arg)
184 ,doc
185 (interactive "p")
186 (let ((x (intern (format "transpose-%s" ,scope))))
187 (if (use-region-p)
188 (funcall x 0)
189 (funcall x arg))))
190 ,(when key
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."
218 (interactive "P")
219 (declare (interactive-only))
220 (cond ((and (buffer-narrowed-p) (not p)) (widen))
221 ((region-active-p)
222 (narrow-to-region (region-beginning)
223 (region-end)))
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)
240 (require 'dired-x)
241 (setq dired-listing-switches "-lahF"
242 dired-dwim-target t
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))
261 ;; add melpa
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)
302 (mct-region-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
311 Info-index
312 Info-menu
313 vc-retrieve-tag
314 imenu
315 file
316 buffer
317 kill-ring
318 consult-buffer))
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 ()
365 (interactive)
366 (c-set-offset 'arglist-intro '+)
367 (c-set-offset 'arglist-cont-nonempty '*))
368 (add-hook 'c-mode-hook #'op/c-indent)
369 ;; TODO: improve it!
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")
374 (save-excursion
375 (let ((re (if localp
376 "^#[ \t]*include[ \t]*\""
377 "^#[ \t]*include[ \t]*<"))
378 (ignore-re "^#include \"compat.h\"")
379 start)
380 (goto-char (point-min))
381 (while (not (or (and (looking-at re)
382 (not (looking-at ignore-re)))
383 (eobp)))
384 (forward-line))
385 (when (eobp)
386 (error "Don't know where to insert the header"))
387 (open-line 1)
388 (insert "#include " (if localp "\"\"" "<>"))
389 (backward-char)
390 (insert path)
391 (move-beginning-of-line 1)
392 (setq start (point))
393 (forward-line)
394 (while (and (looking-at re)
395 (not (eobp)))
396 (forward-line))
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
418 buffer."
419 (interactive "P")
420 (let* ((proj (project-current))
421 (dir (if (and proj (not arg))
422 (project-root proj)
423 default-directory))
424 (default-directory dir)
425 (eshell-buffer-name (let ((name (op/eshell-bufname dir)))
426 (if arg
427 (generate-new-buffer name)
428 name))))
429 (eshell)))
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 ()
446 (interactive "")
447 (let ((inhibit-read-only t))
448 (erase-buffer)
449 (eshell-send-input)))
451 (define-key eshell-command-map (kbd "M-o") #'op/clear-eshell))
454 ;; sndio.el
455 (unless (package-installed-p 'sndio)
456 (package-install-file "~/w/sndio.el/sndio.el"))
459 ;; saturn
460 (unless (package-installed-p 'saturn)
461 (package-install-file "~/w/saturn/GUI/saturn.el"))
464 ;; simple-pass
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)
471 ;; elfeed
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
479 elfeed-feeds
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
548 shackle-rules
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
555 :align above
556 :size 0.2)
557 ("*Async Shell Command*" :ignore t)
558 (,repls :regexp t
559 :align below
560 :size 0.3)
561 (,godot :regexp t
562 :align t
563 :size 0.3)
564 (occur-mode :select t
565 :align right
566 :size 0.3)
567 (diff-mode :select t)
568 (help-mode :select t
569 :align left
570 :size 0.3)
571 (,vcs :regexp t
572 :align above
573 :size 0.15
574 :select t)
575 (,elfeed :regexp t
576 :align t
577 :select t
578 :size 0.75)
579 (,vmd :regexp t
580 :align below
581 :select t
582 :size 0.3))))
583 (shackle-mode +1)
585 (define-key global-map (kbd "M-g e") #'embark-act)