Blob


1 ;; get rid of custom from my init file
2 (setq custom-file "~/.emacs.d/emacs-custom.el")
3 (load custom-file)
5 (setq-default abbrev-mode t)
6 (setq abbrev-file-name
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
16 x-stretch-cursor t
17 require-final-newline t
18 visible-bell nil
19 load-prefer-newer t
20 tab-bar-show 1
21 enable-recursive-minibuffers t
22 imenu-auto-rescan 1
23 use-short-answers 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)
37 (defun op/imenu ()
38 "Just like `imenu', but always flattened!"
39 (interactive ))
41 ;; mg-like
42 (define-key minibuffer-mode-map (kbd "C-w") #'backward-kill-word)
44 (defun op/reverse-other-window ()
45 "Like `other-window', but reverse."
46 (interactive "")
47 (other-window -1))
48 (define-key global-map (kbd "C-x O") #'op/reverse-other-window)
50 (define-key global-map (kbd "C-x v f") #'vc-pull)
52 (setq uniquify-buffer-name-style 'forward
53 uniquify-strip-common-suffix t)
55 (setq-default scroll-up-aggressively 0.0
56 scroll-down-aggressively 0.0
57 scroll-preserve-screen-position t
58 next-screen-context-lines 1)
60 (define-key global-map (kbd "M-z") #'zap-up-to-char)
62 (require 'whitespace)
63 (setq whitespace-style '(face trailing)
64 backward-delete-char-untabify-method 'hungry
65 tab-always-indent 'complete
66 tab-width 8
67 sentence-end-double-space t)
68 (setq-default indent-tabs-mode t)
70 (defun op/enable-tabs ()
71 "Enable `indent-tabs-mode' in the current buffer."
72 (interactive)
73 (setq-local indent-tabs-mode t))
75 (defun op/disable-tabs ()
76 "Disable `indent-tabs-mode' in the current buffer."
77 (interactive)
78 (setq-local indent-tabs-mode nil))
80 (add-hook 'conf-mode-hook #'op/enable-tabs)
81 (add-hook 'text-mode-hook #'op/enable-tabs)
82 (add-hook 'prog-mode-hook #'op/enable-tabs)
83 (add-hook 'prog-mode-hook #'whitespace-mode)
84 (add-hook 'text-mode-hook #'whitespace-mode)
86 (dolist (hook '(emacs-lisp-mode-hook
87 clojure-mode-hook
88 clojurescript-mode-hook
89 clojurec-mode-hook
90 scss-mode-hook))
91 (add-hook hook #'op/disable-tabs))
93 (with-eval-after-load 'log-edit
94 (add-hook 'log-edit-mode #'auto-fill-mode))
96 ;; free the c-z binding
97 (define-key global-map (kbd "C-z") nil)
98 (define-key global-map (kbd "C-z V") #'variable-pitch-mode)
99 (define-key global-map (kbd "C-z n") #'display-line-numbers-mode)
101 (define-key global-map (kbd "M-SPC") #'cycle-spacing)
102 (define-key global-map (kbd "M-u") #'upcase-dwim)
103 (define-key global-map (kbd "M-l") #'downcase-dwim)
104 (define-key global-map (kbd "M-c") #'capitalize-dwim)
106 (let ((font "-misc-fixed-medium-r-semicondensed--13-120-75-75-c-60-iso10646-1"))
107 (set-frame-font font nil t)
108 (add-to-list 'default-frame-alist `(font . ,font)))
110 ;; fix the emojis too
111 (set-fontset-font t 'emoji '("Noto Emoji" . "iso10646-1")
112 nil 'prepend)
114 ;; some cool stuff
115 (save-place-mode +1)
116 (savehist-mode +1)
117 (setq history-delete-duplicates t
118 history-length 1000
119 savehist-save-minibuffer-history t)
120 (electric-pair-mode +1)
122 (define-key global-map (kbd "M-/") #'hippie-expand)
123 (setq hippie-expand-try-functions-list
124 '(try-expand-dabbrev
125 try-expand-dabbrev-all-buffers
126 try-expand-dabbrev-from-kill
127 try-complete-file-name-partially
128 try-complete-file-name
129 try-expand-all-abbrevs
130 try-expand-list
131 try-expand-line
132 try-complete-lisp-symbol-partially
133 try-complete-lisp-symbol))
135 (setq isearch-lazy-count t
136 search-whitespace-regexp ".*?"
137 isearch-allow-scroll 'unlimited)
139 (defun op/buffer-to-side-window (place)
140 "Place the current buffer in the side window at PLACE."
141 (interactive (list (intern
142 (completing-read "Which side: "
143 '(top left right bottom)))))
144 (let ((buf (current-buffer)))
145 (display-buffer-in-side-window
146 buf `((window-height . 0.15)
147 (side . ,place)
148 (slot . -1)
149 (window-parameters . ((no-delete-other-windows . t)))))
150 (delete-window)))
152 (defun op/visit-new-migration-file (name)
153 "Visit a new SQL migration file named after NAME."
154 (interactive "Mname: ")
155 (let* ((name (replace-regexp-in-string " " "-" (string-trim name)))
156 (f (format "%s-%s.sql"
157 (format-time-string "%Y%m%d%H%M")
158 name)))
159 (find-file f)))
161 (defun op/fill-or-unfill (fn &optional justify region)
162 "Meant to be an adviced :around `fill-paragraph'.
163 FN is the original `fill-column'. If `last-command' is
164 `fill-paragraph', unfill it, fill it otherwise. Inspired from a
165 post on endless parentheses. Optional argument JUSTIFY and
166 REGION are passed to `fill-paragraph'."
167 (let ((fill-column
168 (if (eq last-command 'fill-paragraph)
169 (progn (setq this-command nil)
170 (point-max))
171 fill-column)))
172 (funcall fn justify region)))
173 (advice-add 'fill-paragraph :around #'op/fill-or-unfill)
175 (defmacro op/deftranspose (name scope key doc)
176 "Macro to produce transposition functions.
177 NAME is the function's symbol. SCOPE is the text object to
178 operate on. Optional DOC is the function's docstring.
180 Transposition over an active region will swap the object at
181 mark (region beginning) with the one at point (region end).
183 It can optionally define a key for the defined function in the
184 `global-map' if KEY is passed.
186 Originally from protesilaos' dotemacs."
187 (declare (indent defun))
188 `(progn
189 (defun ,name (arg)
190 ,doc
191 (interactive "p")
192 (let ((x (intern (format "transpose-%s" ,scope))))
193 (if (use-region-p)
194 (funcall x 0)
195 (funcall x arg))))
196 ,(when key
197 `(define-key global-map (kbd ,key) #',name))))
199 (op/deftranspose op/transpose-lines "lines" "C-x C-t"
200 "Transpose lines or swap over active region.")
202 (op/deftranspose op/transpose-paragraphs "paragraphs" "C-S-t"
203 "Transpose paragraph or swap over active region.")
205 (op/deftranspose op/transpose-sentences "sentences" "C-x M-t"
206 "Transpose sentences or swap over active region.")
208 (op/deftranspose op/transpose-sexps "sexps" "C-M-t"
209 "Transpose sexps or swap over active region.")
211 (op/deftranspose op/transpose-words "words" "M-t"
212 "Transpose words or swap over active region.")
214 (defun op/narrow-or-widen-dwim (p)
215 "Widen if the buffer is narrowed, narrow-dwim otherwise.
216 Dwim means: region, org-src-block, org-subtree or defun,
217 whichever applies first. Narrowing to org-src-blocks actually
218 calls `org-edit-src-code'.
220 With prefix P, don't widen, just narrow even if buffer is already
221 narrowed. With P being -, narrow to page instead of to defun.
223 Taken from endless parentheses."
224 (interactive "P")
225 (declare (interactive-only))
226 (cond ((and (buffer-narrowed-p) (not p)) (widen))
227 ((region-active-p)
228 (narrow-to-region (region-beginning)
229 (region-end)))
230 ((derived-mode-p 'org-mode)
231 ;; `org-edit-src-code' isn't a real narrowing
232 (cond ((ignore-errors (org-edit-src-code) t))
233 ((ignore-errors (org-narrow-to-block) t))
234 (t (org-narrow-to-subtree))))
235 ((eql p '-) (narrow-to-page))
236 (t (narrow-to-defun))))
238 (define-key global-map (kbd "C-c w") #'op/narrow-or-widen-dwim)
240 (with-eval-after-load 'dired
241 (add-hook 'dired-mode-hook #'dired-hide-details-mode)
242 (add-hook 'dired-mode-hook #'dired-omit-mode)
244 (define-key dired-mode-map (kbd "C-c w") #'wdired-change-to-wdired-mode)
246 (require 'dired-x)
247 (setq dired-listing-switches "-lahF"
248 dired-dwim-target t
249 dired-deletion-confirmer #'y-or-n-p
250 dired-do-revert-buffer t))
252 ;; just like telescope!
253 (with-eval-after-load 'diff-mode
254 (define-key diff-mode-map (kbd "M-SPC") #'scroll-down-command))
256 (with-eval-after-load 'elisp-mode
257 (add-hook 'emacs-lisp-mode-hook #'checkdoc-minor-mode)
258 (add-hook 'emacs-lisp-mode-hook #'prettify-symbols-mode)
259 (let ((map emacs-lisp-mode-map))
260 (define-key map (kbd "C-c C-k") #'eval-buffer)
261 (define-key map (kbd "C-c k") #'op/ert-all)
262 (define-key map (kbd "C-c C-z") #'op/ielm-repl)))
264 (with-eval-after-load 'help
265 (add-hook 'help-mode-hook #'visual-line-mode))
267 ;; add melpa
268 (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
270 ;; packages that i want to be installed
271 (dolist (pkg '(vc-got pdf-tools eglot nameless sly cider go-mode web-mode
272 lua-mode markdown-mode yaml-mode gemini-mode elfeed
273 form-feed shackle embark consult mct puni))
274 (unless (package-installed-p pkg)
275 (message "Installing %s" pkg)
276 (package-install pkg)))
278 (global-form-feed-mode +1)
280 (add-hook 'text-mode-hook #'puni-mode)
281 (add-hook 'prog-mode-hook #'puni-mode)
282 (define-key puni-mode-map (kbd "C-)") #'puni-slurp-forward)
283 (define-key puni-mode-map (kbd "C-(") #'puni-barf-forward)
285 (setq completion-styles '(basic substring initials flex partial-completion))
287 (require 'consult) ;; some stuff lacks an autoload and i don't want to debug it
289 (cl-loop for (key . func) in '(("C-x :" . consult-complex-command)
290 ("C-x b" . consult-buffer)
291 ("C-x 4 b" . consult-buffer-other-window)
292 ("C-x 5 b" . consult-buffer-other-frame)
293 ("C-x r b" . consult-bookmark)
294 ("C-x p b" . consult-project-buffer)
295 ("M-y" . consult-yank-pop)
296 ("M-g g" . consult-goto-line)
297 ("M-g M-g" . consult-goto-line)
298 ("M-g m" . consult-mark)
299 ("M-g i" . consult-imenu)
300 ("M-s l" . consult-line)
301 ("M-s L" . consult-line-multi)
302 ("M-s m" . consult-multi-occur))
303 do (define-key global-map (kbd key) func))
304 (add-hook 'completion-list-mode-hook #'consult-preview-at-point-mode)
306 (setq completions-detailed t)
307 (mct-minibuffer-mode +1)
308 (mct-region-mode +1)
310 ;; override the binding for the annoying mct-backward-updir.
311 (define-key mct-minibuffer-local-filename-completion-map
312 (kbd "DEL") #'backward-delete-char)
314 (setq mct-remove-shadowed-file-names t
315 mct-completions-format 'one-column
316 mct-completion-passlist '(Info-goto-node
317 Info-index
318 Info-menu
319 vc-retrieve-tag
320 imenu
321 file
322 buffer
323 consult-project-buffer
324 kill-ring
325 consult-buffer))
327 (with-eval-after-load 'cider
328 (define-key cider-repl-mode-map (kbd "C-c M-o") #'cider-repl-clear-buffer))
330 (with-eval-after-load 'go-mode
331 (add-hook 'go-mode-hook #'subword-mode))
333 (with-eval-after-load 'eglot
334 (define-key eglot-mode-map (kbd "<f1>") #'eglot-code-actions)
335 (define-key eglot-mode-map (kbd "<f2>") #'eglot-format)
336 (add-to-list 'eglot-ignored-server-capabilites
337 :documentHighlightProvider)
338 (add-to-list 'eglot-server-programs
339 '(c-mode . ("clangd" "--header-insertion=never"))))
341 (add-hook 'emacs-lisp-mode #'nameless-mode)
342 (with-eval-after-load 'nameless
343 (setq nameless-private-prefix t
344 nameless-affect-indentation-and-filling nil)
345 (define-key emacs-lisp-mode-map (kbd "_") #'nameless-insert-name-or-self-insert))
347 (add-to-list 'auto-mode-alist '("\\.html\\'" . web-mode))
348 (with-eval-after-load 'web-mode
349 (setq web-mode-markup-indent-offset 2
350 web-mode-css-indent-offset 2
351 web-mode-style-padding 0
352 web-mode-enable-engine-detection t)
353 (add-hook 'web-mode-hook #'op/disable-tabs)
355 ;; fix .dir-locals.el
356 (defun op/web-mode-fix-dir-locals ()
357 (when (derived-mode-p major-mode 'web-mode)
358 (web-mode-guess-engine-and-content-type)))
359 (add-hook 'hack-local-variables-hook #'op/web-mode-fix-dir-locals))
361 (with-eval-after-load 'css-mode
362 (add-hook 'css-mode-hook #'op/disable-tabs))
364 (with-eval-after-load 'flymake
365 (define-key prog-mode-map (kbd "C-c ! n") #'flymake-goto-next-error)
366 (define-key prog-mode-map (kbd "C-c ! p") #'flymake-goto-prev-error))
368 (with-eval-after-load 'cc-mode
369 (setq c-basic-offset 8
370 c-default-style "K&R")
371 (dolist (hook '(c-mode-hook c++-mode-hook))
372 (add-hook hook #'abbrev-mode)
373 (add-hook hook #'subword-mode))
374 (defun op/c-indent ()
375 (interactive)
376 (c-set-offset 'arglist-intro '+)
377 (c-set-offset 'arglist-cont-nonempty '*))
378 (add-hook 'c-mode-hook #'op/c-indent)
379 ;; TODO: improve it!
380 (defun op/c-add-include (path &optional localp)
381 "Include PATH at the start of the file.
382 If LOCALP is non-nil, the include will be \"local\"."
383 (interactive "Mheader to include: \nP")
384 (save-excursion
385 (let ((re (if localp
386 "^#[ \t]*include[ \t]*\""
387 "^#[ \t]*include[ \t]*<"))
388 (ignore-re "^#include \"compat.h\"")
389 start)
390 (goto-char (point-min))
391 (while (not (or (and (looking-at re)
392 (not (looking-at ignore-re)))
393 (eobp)))
394 (forward-line))
395 (when (eobp)
396 (error "Don't know where to insert the header"))
397 (open-line 1)
398 (insert "#include " (if localp "\"\"" "<>"))
399 (backward-char)
400 (insert path)
401 (move-beginning-of-line 1)
402 (setq start (point))
403 (forward-line)
404 (while (and (looking-at re)
405 (not (eobp)))
406 (forward-line))
407 (sort-lines nil start (point)))))
408 (define-key c-mode-map (kbd "C-c C-a") #'op/c-add-include))
410 (with-eval-after-load 'perl-mode
411 (setq perl-indent-level 8))
413 (with-eval-after-load 'sh-script
414 (setq sh-basic-offset 8
415 sh-indent-after-loop-construct 8
416 sh-indent-after-continuation nil))
420 (setq eshell-hist-ignoredups t)
422 (defun op/eshell-bufname (dir)
423 (concat "*eshell " (expand-file-name dir) "*"))
425 (defun op/eshell (arg)
426 "Run or jump to eshell in current project.
427 If called with prefix argument ARG always create a new eshell
428 buffer."
429 (interactive "P")
430 (let* ((proj (project-current))
431 (dir (if (and proj (not arg))
432 (project-root proj)
433 default-directory))
434 (default-directory dir)
435 (eshell-buffer-name (let ((name (op/eshell-bufname dir)))
436 (if arg
437 (generate-new-buffer name)
438 name))))
439 (eshell)))
440 (define-key global-map (kbd "C-c e") #'op/eshell)
442 (with-eval-after-load 'eshell
443 (setq eshell-save-history-on-exit t
444 eshell-history-size 1024
446 eshell-compl-dir-ignore
447 "\\`\\(\\.\\.?\\|CVS\\|\\.svn\\|\\.git\\|\\.got\\)/\\'")
449 (defun op/eshell-after-cd (&rest _)
450 (rename-buffer (op/eshell-bufname default-directory) t))
452 (advice-add #'eshell/cd :after #'op/eshell-after-cd))
454 (with-eval-after-load 'esh-mode
455 (defun op/clear-eshell ()
456 (interactive "")
457 (let ((inhibit-read-only t))
458 (erase-buffer)
459 (eshell-send-input)))
461 (define-key eshell-command-map (kbd "M-o") #'op/clear-eshell))
464 ;; sndio.el
465 (unless (package-installed-p 'sndio)
466 (package-install-file "~/w/sndio.el/sndio.el"))
469 ;; saturn
470 (unless (package-installed-p 'saturn)
471 (package-install-file "~/w/saturn/GUI/saturn.el"))
474 ;; simple-pass
475 (unless (package-installed-p 'simple-pass)
476 (package-install-file "~/.emacs.d/simple-pass.el"))
477 (define-key global-map (kbd "C-z p") #'simple-pass-copy)
481 ;; elfeed
483 (define-key global-map (kbd "C-x w") #'elfeed)
484 (with-eval-after-load 'elfeed
485 (define-key elfeed-show-mode-map (kbd "q") #'delete-window)
486 (define-key elfeed-show-mode-map (kbd "S-SPC") #'scroll-down-command)
487 (define-key elfeed-show-mode-map (kbd "M-SPC") #'scroll-down-command)
488 (setq elfeed-show-entry-switch #'pop-to-buffer
489 elfeed-feeds
490 '("https://undeadly.org/cgi?action=rss&full=yes&items=10"
491 "http://www.tedunangst.com/flak/rss"
492 "https://www.dragonflydigest.com/feed"
493 "https://www.mirbsd.org/news.rss"
494 "https://www.mirbsd.org/announce.rss"
495 "https://bentsukun.ch/index.xml"
496 "https://drewdevault.com/feed.xml"
497 "https://www.cambus.net/atom.xml"
498 "https://dataswamp.org/~solene/rss.xml"
499 "https://briancallahan.net/blog/feed.xml"
500 "https://www.poolp.org/index.xml"
501 "https://jcs.org/rss"
502 "https://sanctum.geek.nz/arabesque/feed/"
503 "https://tech.toryanderson.com/"
504 "https://alexschroeder.ch/wiki?action=journal;search=-tag:rpg -tag:rsp;lang=en;title=English Diary without RPG Pages"
505 "http://boston.conman.org/bostondiaries.rss"
506 "https://emacsninja.com/feed.atom"
507 "https://bsdly.blogspot.com/feeds/posts/default"
508 "https://crawshaw.io/atom.xml"
509 "https://nullprogram.com/feed/"
510 "http://pragmaticemacs.com/feed/"
511 "https://emacsnotes.wordpress.com/feed/"
512 "https://metaredux.com/feed.xml"
513 "https://emacsredux.com/atom.xml"
514 "https://endlessparentheses.com/atom.xml"
515 "https://www.masteringemacs.org/feed"
516 "https://cestlaz.github.io/rss.xml"
517 "https://utcc.utoronto.ca/~cks/space/blog/?atom"
518 "https://irreal.org/blog/?feed=rss2"
519 "https://jao.io/blog/rss.xml"
520 "https://planet.lisp.org/rss20.xml"
521 "https://insideclojure.org/feed.xml"
522 "https://tech.toryanderson.com/index.xml"
523 "https://vermaden.wordpress.com/feed/"
524 "https://www.arp242.net/feed.xml"
525 "https://tymoon.eu/api/reader/atom"
526 "https://venam.nixers.net/blog/feed.xml"
527 "https://www.omarpolo.com/rss.xml"
528 "https://owarisubs.lacumpa.biz/feed/"
529 "https://asenshi.moe/feed/"
530 "https://godotengine.org/rss.xml"
532 "https://adventofcomputing.libsyn.com/rss"
534 "https://github.com/go-gitea/gitea/releases.atom"
536 "https://nitter.pussthecat.org/NanoRaptor/rss"
538 "https://github.com/yshui/picom/releases.atom"
539 "https://github.com/vslavik/poedit/releases.atom"
540 "https://github.com/TokTok/c-toxcore/releases.atom"
541 "https://github.com/alexander-akhmetov/python-telegram/releases.atom"
542 "https://github.com/paul-nameless/tg/releases.atom"
543 "https://github.com/YACReader/yacreader/releases.atom"
544 "https://github.com/luarocks/luarocks/releases.atom"
545 "https://github.com/okbob/pspg/releases.atom"
546 "https://github.com/taisei-project/taisei/releases.atom"
547 "https://github.com/recp/cglm/releases.atom"
548 "https://github.com/SCons/scons/releases.atom"
549 "https://git.sr.ht/~rjarry/aerc/refs/rss.xml"
551 "https://causal.agency/list/pounce.atom"
553 "https://www.crimsonmagic.me/feed/"
554 "https://fullybookedtls.wordpress.com/feed/"
556 "https://draculadaily.substack.com/feed")))
558 (setq shackle-default-rule nil
559 shackle-rules
560 (let ((repls "\\*\\(cider-repl\\|sly-mrepl\\|ielm\\)")
561 (godot "\\*godot - .*\\*")
562 (vcs "\\*\\(Flymake\\|Package-Lint\\|vc-\\(git\\|got\\) :\\).*")
563 (elfeed "\\*elfeed-entry\\*")
564 (vmd "\\*vmd console .*"))
565 `(("*Async Shell Command*" :ignore t)
566 (,repls :regexp t
567 :align below
568 :size 0.3)
569 (,godot :regexp t
570 :align t
571 :size 0.3)
572 (occur-mode :select t
573 :align right
574 :size 0.3)
575 (diff-mode :select t)
576 (help-mode :select t
577 :align left
578 :size 0.3)
579 (,vcs :regexp t
580 :align above
581 :size 0.15
582 :select t)
583 (,elfeed :regexp t
584 :align t
585 :select t
586 :size 0.75)
587 (,vmd :regexp t
588 :align below
589 :select t
590 :size 0.3))))
591 (shackle-mode +1)
593 ;; (setq display-buffer-alist nil)
595 (define-key global-map (kbd "M-g e") #'embark-act)
597 (defun op/target-filename+line ()
598 "Target a file with optional line number: file[:number]."
599 (save-excursion
600 (let* ((beg (progn (skip-chars-backward "^[:space:]\n")
601 (point)))
602 (end (progn (skip-chars-forward "^[:space:]\n")
603 (point)))
604 (str (buffer-substring-no-properties beg end)))
605 (save-match-data
606 (when (and (progn (goto-char beg)
607 (ffap-file-at-point))
608 (string-match ".+\\(:[[:digit:]]+\\)?:?" str))
609 `(file ,str ,beg . ,end))))))
611 (add-to-list 'embark-target-finders #'op/target-filename+line)
613 (defun op/acme-find-file (filename)
614 "Visit FILENAME like `find-file', but also jump to line if provided."
615 (save-match-data
616 (if (not (string-match "\\(.+\\):\\([[:digit:]]+\\)" filename))
617 (find-file filename)
618 (let ((path (match-string 1 filename))
619 (line (string-to-number (match-string 2 filename))))
620 (with-current-buffer (find-file path)
621 (goto-line line))))))
623 (define-key embark-file-map (kbd "RET") #'op/acme-find-file)