882 lines
31 KiB
EmacsLisp
882 lines
31 KiB
EmacsLisp
(setopt save-interprogram-paste-before-kill t)
|
|
(setopt enable-recursive-minibuffers t)
|
|
(setopt line-spacing 0.15)
|
|
|
|
(setopt mouse-wheel-tilt-scroll t)
|
|
(setq-default truncate-lines t)
|
|
|
|
(setopt tab-bar-show nil)
|
|
(tab-bar-mode)
|
|
|
|
(setopt inhibit-startup-screen t)
|
|
(setopt initial-scratch-message nil)
|
|
|
|
(menu-bar-mode -1)
|
|
(scroll-bar-mode -1)
|
|
(tool-bar-mode -1)
|
|
|
|
(setopt make-backup-files nil)
|
|
(setopt create-lockfiles nil)
|
|
(setopt auto-save-default nil)
|
|
|
|
(setopt view-read-only t)
|
|
|
|
(setopt sentence-end-double-space nil)
|
|
|
|
(setopt kill-whole-line t)
|
|
|
|
(column-number-mode t)
|
|
|
|
(which-key-mode)
|
|
|
|
(setopt dired-auto-revert-buffer t)
|
|
|
|
(global-hl-line-mode)
|
|
|
|
(setopt tab-always-indent 'complete)
|
|
|
|
(setq-default cursor-type 'bar)
|
|
|
|
(repeat-mode)
|
|
|
|
(savehist-mode)
|
|
|
|
(require 'bind-key)
|
|
(unbind-key "C-x RET")
|
|
(unbind-key "C-x #")
|
|
(unbind-key "C-x 5")
|
|
(unbind-key "C-x 6")
|
|
(unbind-key "C-x \\")
|
|
(unbind-key "C-x `")
|
|
(unbind-key "C-x m")
|
|
(unbind-key "C-x l")
|
|
(unbind-key "C-x i")
|
|
(unbind-key "C-x u")
|
|
(unbind-key "C-x @")
|
|
(unbind-key "C-x C-b")
|
|
(unbind-key "C-x C-d")
|
|
(unbind-key "C-x C-l")
|
|
(unbind-key "C-x C-p")
|
|
(unbind-key "C-x C-u")
|
|
(unbind-key "C-x C-z")
|
|
|
|
(setopt doom-modeline-irc nil)
|
|
(setopt doom-modeline-workspace-name nil)
|
|
(setopt doom-modeline-buffer-encoding nil)
|
|
(setopt doom-modeline-vcs-max-length 30)
|
|
(doom-modeline-mode)
|
|
|
|
(ligature-set-ligatures 'prog-mode
|
|
'("--" "---" "==" "===" "!=" "!==" "=!="
|
|
"=:=" "=/=" "<=" ">=" "&&" "&&&" "&=" "++" "+++" "***" ";;" "!!"
|
|
"??" "???" "?:" "?." "?=" "<:" ":<" ":>" ">:" "<:<" "<>" "<<<" ">>>"
|
|
"<<" ">>" "||" "-|" "_|_" "|-" "||-" "|=" "||=" "##" "###" "####"
|
|
"#{" "#[" "]#" "#(" "#?" "#_" "#_(" "#:" "#!" "#=" "^=" "<$>" "<$"
|
|
"$>" "<+>" "<+" "+>" "<*>" "<*" "*>" "</" "</>" "/>" "<!--" "<#--"
|
|
"-->" "->" "->>" "<<-" "<-" "<=<" "=<<" "<<=" "<==" "<=>" "<==>"
|
|
"==>" "=>" "=>>" ">=>" ">>=" ">>-" ">-" "-<" "-<<" ">->" "<-<" "<-|"
|
|
"<=|" "|=>" "|->" "<->" "<~~" "<~" "<~>" "~~" "~~>" "~>" "~-" "-~"
|
|
"~@" "[||]" "|]" "[|" "|}" "{|" "[<" ">]" "|>" "<|" "||>" "<||"
|
|
"|||>" "<|||" "<|>" "..." ".." ".=" "..<" ".?" "::" ":::" ":=" "::="
|
|
":?" ":?>" "//" "///" "/*" "*/" "/=" "//=" "/==" "@_" "__" "???"
|
|
"<:<" ";;;"))
|
|
(global-ligature-mode t)
|
|
|
|
(add-hook 'text-mode-hook 'mixed-pitch-mode)
|
|
|
|
(defun no-mixed-pitch ()
|
|
(mixed-pitch-mode -1))
|
|
|
|
(add-hook 'yaml-ts-mode-hook 'no-mixed-pitch)
|
|
(add-hook 'mhtml-mode 'no-mixed-pitch)
|
|
(add-hook 'nxml-mode 'no-mixed-pitch)
|
|
|
|
(bind-keys ("M-o" . ace-window)
|
|
("s-o" . ace-window)
|
|
("C-c o" . ace-window))
|
|
(setopt aw-keys '(?a ?s ?d ?f ?g ?h ?j ?k ?l))
|
|
(setopt aw-dispatch-always t)
|
|
|
|
(bind-keys ("s-f" . project-find-file)
|
|
("s-F" . project-switch-project)
|
|
;; ("s-s" . persp-switch)
|
|
)
|
|
|
|
(with-eval-after-load 'tab-bar
|
|
(bind-keys :map tab-bar-mode-map
|
|
("s-s" . tab-bar-switch-to-tab)))
|
|
|
|
(server-mode -1)
|
|
|
|
(setopt visible-bell t)
|
|
|
|
(transient-mark-mode -1)
|
|
|
|
(bind-keys :repeat-map tab-bar-repeat-map
|
|
("<left>" . tab-bar-history-back)
|
|
("<right>" . tab-bar-history-forward))
|
|
|
|
(tab-bar-history-mode)
|
|
|
|
(setopt compilation-scroll-output 'first-error)
|
|
|
|
(defun jf/window-children (window)
|
|
(let ((result nil)
|
|
(child (window-child window)))
|
|
(while child
|
|
(push child result)
|
|
(setq child (window-next-sibling child)))
|
|
(reverse result)))
|
|
|
|
(defun jf/window-main-root (&optional frame)
|
|
"Return FRAME's main (non-side) window or window subtree."
|
|
(let* ((root (frame-root-window (or frame (selected-frame))))
|
|
(children (jf/window-children root)))
|
|
(if (seq-some (lambda (w) (window-parameter w 'window-side)) children)
|
|
(seq-find (lambda (w) (not (window-parameter w 'window-side))) children)
|
|
root)))
|
|
|
|
(defun jf/window-leaves (window)
|
|
"Return the live leaf windows in WINDOW's subtree."
|
|
(if (window-live-p window)
|
|
(list window)
|
|
(mapcan #'jf/window-leaves (jf/window-children window))))
|
|
|
|
(defun jf/topright-window (windows)
|
|
"Return whichever of WINDOWS sits furthest up, then furthest right."
|
|
(car (sort (copy-sequence windows)
|
|
(lambda (a b)
|
|
(let ((top-a (nth 1 (window-edges a)))
|
|
(top-b (nth 1 (window-edges b))))
|
|
(if (= top-a top-b)
|
|
(> (nth 0 (window-edges a)) (nth 0 (window-edges b)))
|
|
(< top-a top-b)))))))
|
|
|
|
(defun jf/display-buffer-right (buffer alist)
|
|
"Display BUFFER on the right of the main editing area.
|
|
|
|
Applies three rules, based on the shape of the frame's non-side windows:
|
|
1. One window -> split it and show BUFFER in the new right window.
|
|
2. Two windows side-by-side -> show BUFFER in the right one. If the
|
|
right window is selected, swap the two windows first (keeping
|
|
focus on the buffer that was active) so BUFFER doesn't evict it.
|
|
3. Any other layout -> show BUFFER in the upper-rightmost window."
|
|
(let ((main (jf/window-main-root)))
|
|
(cond
|
|
((window-live-p main)
|
|
(let* ((new (split-window main nil 'right))
|
|
(width (max 1 (- (frame-width) 120)))
|
|
(delta (window-resizable new (- width (window-width new)) t)))
|
|
(window-resize new delta t)
|
|
(window--display-buffer buffer new 'window alist)))
|
|
((and (window-left-child main)
|
|
(= (length (jf/window-children main)) 2)
|
|
(seq-every-p #'window-live-p (jf/window-children main)))
|
|
(let* ((left (window-left-child main))
|
|
(right (window-next-sibling left)))
|
|
(when (eq (selected-window) right)
|
|
(window-swap-states left right)
|
|
(select-window left))
|
|
(window--display-buffer buffer right 'reuse alist)))
|
|
(t
|
|
(window--display-buffer buffer (jf/topright-window (jf/window-leaves main)) 'reuse alist)))))
|
|
|
|
|
|
(setopt popper-reference-buffers
|
|
'("\\*Flymake diagnostics .*"
|
|
"\\*Embark Export: .*"
|
|
"\\*Embark Collect .*"
|
|
"\\*xref\\*"
|
|
"\\*compilation\\*"
|
|
"\\*pnpm:.*"
|
|
"\\*sly-mrepl *"
|
|
"\\*lispy-message\\*"
|
|
"\\*occur\\*"
|
|
".*eshell\\*"
|
|
"\\*SQL: .*"
|
|
"magit-revision: .*"
|
|
"\\*Help\\*"
|
|
"\\*rustfmt\\*"
|
|
"\\*slack: .*"
|
|
"\\*forge: .*"
|
|
"new-pullreq"
|
|
"\\*claude:.*"
|
|
"\\*Man .*"
|
|
"\\*List .*"
|
|
"\\*eldoc\\*"))
|
|
(setopt popper-display-control nil)
|
|
|
|
(popper-mode)
|
|
(popper-echo-mode)
|
|
|
|
(bind-keys ("C-q" . popper-toggle)
|
|
("C-;" . popper-cycle))
|
|
|
|
(defvar jf/fullscreen-popup--param 'jf/fullscreen-saved-configuration
|
|
"Key used to stash the window configuration a fullscreen popup replaced.
|
|
Stored per-tab when `tab-bar-mode' is active, otherwise as a plain frame
|
|
parameter; see `jf/tab-or-frame-parameter'.")
|
|
|
|
(defvar-local jf/fullscreen-popup-p nil
|
|
"Non-nil in a buffer currently being displayed as a fullscreen popup.")
|
|
|
|
(defun jf/current-tab-cell (&optional frame)
|
|
"Return FRAME's current tab's raw data cell, or nil without `tab-bar-mode'."
|
|
(and (bound-and-true-p tab-bar-mode)
|
|
(assq 'current-tab (tab-bar-tabs frame))))
|
|
|
|
(defun jf/tab-or-frame-parameter (parameter &optional frame)
|
|
"Like `frame-parameter', but scoped to FRAME's selected tab when tabs are in use.
|
|
|
|
A plain frame parameter is shared by every tab on that frame, which is wrong
|
|
for state like `jf/fullscreen-popup--param': switching tabs swaps in an
|
|
entirely different window layout, so \"the layout to restore\" has to travel
|
|
with the tab, not the frame."
|
|
(if-let ((tab (jf/current-tab-cell frame)))
|
|
(alist-get parameter tab)
|
|
(frame-parameter frame parameter)))
|
|
|
|
(defun jf/set-tab-or-frame-parameter (parameter value &optional frame)
|
|
"Set PARAMETER to VALUE, scoped like `jf/tab-or-frame-parameter'."
|
|
(if-let ((tab (jf/current-tab-cell frame)))
|
|
(let ((cell (assq parameter tab)))
|
|
(if cell
|
|
(setcdr cell value)
|
|
(setcdr tab (cons (cons parameter value) (cdr tab)))))
|
|
(set-frame-parameter frame parameter value)))
|
|
|
|
(defun jf/display-buffer-fullscreen (buffer alist)
|
|
"Display BUFFER, deleting other windows, saving the prior layout.
|
|
|
|
Intended for use in `display-buffer-alist' for popups (e.g. Magit
|
|
status/log) that should take over the frame while displayed, but
|
|
restore the previous window configuration once closed via either
|
|
`q' or `popper-toggle' -- both end up calling `quit-window', which
|
|
is where the restore actually happens; see
|
|
`jf/quit-window-restore-fullscreen'."
|
|
(unless (jf/tab-or-frame-parameter jf/fullscreen-popup--param)
|
|
(jf/set-tab-or-frame-parameter jf/fullscreen-popup--param
|
|
(current-window-configuration)))
|
|
(when-let ((window (or (display-buffer-reuse-window buffer alist)
|
|
(display-buffer-same-window buffer alist)
|
|
(display-buffer-pop-up-window buffer alist)
|
|
(display-buffer-use-some-window buffer alist))))
|
|
(let ((ignore-window-parameters t))
|
|
(delete-other-windows window))
|
|
(with-current-buffer buffer
|
|
(setq jf/fullscreen-popup-p t))
|
|
window))
|
|
|
|
(defun jf/quit-window-restore-fullscreen (orig-fn &optional kill window)
|
|
"Restore the saved window configuration after quitting a fullscreen popup.
|
|
|
|
Advice around `quit-window' so both `q' (which Magit and other
|
|
special-mode buffers bind to buffer-quitting commands that call
|
|
`quit-window') and Popper's close/toggle commands (which also call
|
|
`quit-window' directly) trigger the restore. Only restores once the
|
|
last fullscreen popup on the tab (or frame, without `tab-bar-mode')
|
|
has closed, so opening one fullscreen popup from within another
|
|
falls back to the outer one first."
|
|
(let* ((window (or window (selected-window)))
|
|
(was-fullscreen (buffer-local-value 'jf/fullscreen-popup-p
|
|
(window-buffer window)))
|
|
(frame (window-frame window)))
|
|
(funcall orig-fn kill window)
|
|
(when (and was-fullscreen
|
|
(not (and (window-live-p window)
|
|
(buffer-local-value 'jf/fullscreen-popup-p
|
|
(window-buffer window))))
|
|
(jf/tab-or-frame-parameter jf/fullscreen-popup--param frame))
|
|
(let ((conf (jf/tab-or-frame-parameter jf/fullscreen-popup--param frame)))
|
|
(jf/set-tab-or-frame-parameter jf/fullscreen-popup--param nil frame)
|
|
(set-window-configuration conf)))))
|
|
|
|
(advice-add 'quit-window :around #'jf/quit-window-restore-fullscreen)
|
|
|
|
(setopt split-width-threshold 200)
|
|
(setopt split-height-threshold nil)
|
|
(setopt display-buffer-base-action
|
|
'((display-buffer-reuse-window
|
|
display-buffer-reuse-mode-window
|
|
display-buffer-use-some-window)))
|
|
|
|
(setq display-buffer-alist
|
|
'(((or "\\*Flymake diagnostics .*"
|
|
"\\*Embark Export: .*"
|
|
"\\*Embark Collect .*"
|
|
"\\*xref\\*"
|
|
"\\*compilation\\*"
|
|
"\\*pnpm:.*"
|
|
"\\*sly-mrepl *"
|
|
"\\*lispy-message\\*"
|
|
"\\*occur\\*"
|
|
".*eshell\\*"
|
|
"\\*SQL: .*")
|
|
(display-buffer-in-side-window)
|
|
(side . bottom)
|
|
(slot . 0)
|
|
(window-height . 0.33))
|
|
((or "magit: *"
|
|
"magit-log*"
|
|
"\\*Messages\\*"
|
|
"\\*trace-output\\*"
|
|
"\\*EGLOT connections\\*"
|
|
"magit-revision: .*"
|
|
"\\*Help\\*"
|
|
"\\*rustfmt\\*"
|
|
"\\*slack: .*"
|
|
"\\*forge: .*"
|
|
"new-pullreq"
|
|
"\\*claude:.*"
|
|
"\\*Man .*"
|
|
"\\*List .*"
|
|
"\\*Embark Actions\\*"
|
|
"\\*eldoc\\*")
|
|
(display-buffer-reuse-window
|
|
display-buffer-reuse-mode-window
|
|
jf/display-buffer-right))))
|
|
|
|
(setopt window-persistent-parameters
|
|
(cl-list* '(no-delete-other-windows . writable)
|
|
'(no-other-window . writable)
|
|
window-persistent-parameters))
|
|
|
|
;; (setopt evil-want-keybinding nil)
|
|
;; (require 'evil)
|
|
;; (evil-mode 1)
|
|
;; (require 'evil-collection)
|
|
;; (evil-collection-init)
|
|
|
|
;; (require 'evil-org)
|
|
;; (add-hook 'org-mode-hook 'evil-org-mode)
|
|
;; (evil-org-set-key-theme '(navigation insert textobjects additional calendar))
|
|
;; (require 'evil-org-agenda)
|
|
;; (evil-org-agenda-set-keys)
|
|
(require 'god-mode)
|
|
(require 'which-key)
|
|
|
|
(bind-keys :map god-local-mode-map
|
|
("i" . (lambda () (interactive) (god-mode-all -1)))
|
|
("z" . repeat))
|
|
(bind-keys ("<escape>" . (lambda () (interactive) (god-mode-all 1))))
|
|
(defun my-god-mode-update-cursor-type ()
|
|
(setq cursor-type (cond
|
|
((and (featurep 'org-present) org-present-mode) nil)
|
|
((or (and (featurep 'god-mode) god-local-mode) buffer-read-only) 'box)
|
|
(t 'bar))))
|
|
|
|
(add-hook 'post-command-hook 'my-god-mode-update-cursor-type)
|
|
(which-key-enable-god-mode-support)
|
|
|
|
(defun avy-goto-char-timer-embark ()
|
|
(interactive)
|
|
(require 'avy)
|
|
(let ((avy-action-oneshot
|
|
(lambda (pt)
|
|
(unwind-protect
|
|
(save-excursion
|
|
(goto-char pt)
|
|
(embark-act))
|
|
(select-window
|
|
(cdr (ring-ref avy-ring 0)))))))
|
|
(avy-goto-char-timer)))
|
|
|
|
(bind-keys
|
|
("C-j" . avy-goto-char-timer)
|
|
("M-j" . avy-goto-char-timer-embark)
|
|
("C-." . embark-act))
|
|
|
|
(with-eval-after-load 'lispy
|
|
(bind-keys :map lispy-mode-map
|
|
("C-j" . avy-goto-char-timer)))
|
|
|
|
(with-eval-after-load 'org
|
|
(bind-keys :map org-mode-map
|
|
("C-j" . avy-goto-char-timer)))
|
|
|
|
(setopt isearch-wrap-pause 'no-ding)
|
|
|
|
(defun string-inflection-rust-style-cycle-function (str)
|
|
(cond
|
|
((string-inflection-snake-case-p str)
|
|
(string-inflection-upcase-function str))
|
|
((string-inflection-upcase-p str)
|
|
(string-inflection-pascal-case-function str))
|
|
(t
|
|
(string-inflection-snake-case-function str))))
|
|
|
|
(defun string-inflection-rust-style-cycle ()
|
|
(interactive)
|
|
(require 'string-inflection)
|
|
(string-inflection--symbol-or-region #'string-inflection-rust-style-cycle-function))
|
|
|
|
(bind-keys :repeat-map string-inflection-java-map
|
|
("u" . string-inflection-java-style-cycle))
|
|
|
|
(bind-keys :repeat-map string-inflection-rust-map
|
|
("u" . string-inflection-rust-style-cycle))
|
|
|
|
(bind-keys ("C-x 4 n" . edit-indirect-region))
|
|
|
|
(with-eval-after-load 'apheleia
|
|
(push '(prisma-format "prisma" "format" "--schema" filepath) apheleia-formatters))
|
|
|
|
(buffer-terminator-mode 1)
|
|
|
|
(bind-keys
|
|
("s-b" . consult-project-buffer))
|
|
|
|
(electric-pair-mode 1)
|
|
|
|
(setq-default indent-tabs-mode nil)
|
|
(setq-default tab-width 4)
|
|
(global-tree-sitter-mode 1)
|
|
|
|
(setopt ediff-split-window-function 'split-window-horizontally)
|
|
|
|
(defun project-nix-flake ()
|
|
(interactive)
|
|
(nix-flake (project-root (project-current t))))
|
|
(bind-keys ("C-x p n" . project-nix-flake))
|
|
|
|
(setq-default fill-column 120)
|
|
|
|
(defun jf-init-fill-column ()
|
|
(display-fill-column-indicator-mode 1))
|
|
(add-hook 'prog-mode-hook 'jf-init-fill-column)
|
|
|
|
(with-eval-after-load 'prog-mode
|
|
(defun my-prog-fill-reindent-defun (&optional argument)
|
|
"Refill or reindent the paragraph or defun that contains point.
|
|
|
|
If the point is in a string or a comment, fill the paragraph that
|
|
contains point or follows point.
|
|
|
|
Otherwise, reindent the function definition that contains point
|
|
or follows point."
|
|
(interactive "P")
|
|
(save-excursion
|
|
(let ((treesit-text-node
|
|
(and (treesit-available-p)
|
|
(treesit-parser-list)
|
|
(treesit-node-match-p
|
|
(treesit-node-at (point)) 'text t))))
|
|
(if (or treesit-text-node
|
|
(and (not (and (treesit-available-p) (treesit-parser-list))) (nth 8 (syntax-ppss)))
|
|
(re-search-forward "\\s-*\\s<" (line-end-position) t))
|
|
(fill-paragraph argument (region-active-p))
|
|
(beginning-of-defun)
|
|
(let ((start (point)))
|
|
(end-of-defun)
|
|
(indent-region start (point) nil))))))
|
|
|
|
(advice-add 'prog-fill-reindent-defun :override #'my-prog-fill-reindent-defun))
|
|
|
|
(add-hook 'compilation-filter-hook 'ansi-color-compilation-filter)
|
|
|
|
(autoload 'flymake-show-project-diagnostics "flymake")
|
|
(autoload 'flymake-goto-next-error "flymake")
|
|
(autoload 'flymake-goto-prev-error "flymake")
|
|
|
|
(with-eval-after-load 'prog-mode
|
|
(bind-keys :map prog-mode-map
|
|
("C-c D" . flymake-show-project-diagnostics)
|
|
("M-n" . flymake-goto-next-error)
|
|
("M-p" . flymake-goto-prev-error)))
|
|
|
|
(with-eval-after-load 'flymake
|
|
(bind-keys :map flymake-project-diagnostics-mode-map
|
|
("q" . quit-window)))
|
|
|
|
(setopt eglot-ignored-server-capabilities '(:inlayHintProvider :semanticTokensProvider))
|
|
(add-hook 'eglot-managed-mode-hook (lambda () (eglot-inlay-hints-mode -1)) nil t)
|
|
(setopt eglot-code-action-indications nil)
|
|
(setopt eglot-events-buffer-config '(:size 300000 :format full))
|
|
|
|
(with-eval-after-load 'eglot
|
|
(bind-keys :map eglot-mode-map
|
|
("C-c a" . eglot-code-actions)
|
|
("C-c r" . eglot-rename)
|
|
("C-c i" . eglot-find-implementation)))
|
|
|
|
(with-eval-after-load 'eglot
|
|
(add-to-list 'eglot-stay-out-of 'yasnippet))
|
|
|
|
(setq major-mode-remap-alist '((rust-mode . rust-mode)))
|
|
|
|
(setopt rust-mode-treesitter-derive t)
|
|
|
|
(with-eval-after-load 'rust-prog-mode
|
|
(bind-keys :map rust-mode-map
|
|
("C-c u" . string-inflection-rust-style-cycle)))
|
|
|
|
(defun yaml-validate ()
|
|
(interactive)
|
|
(compile (concat "yamllint -d '{extends: default, rules: {indentation: {indent-sequences: false}, braces: {max-spaces-inside: 1}}}' " (buffer-file-name)) t))
|
|
|
|
(add-hook 'yaml-ts-mode-hook 'yaml-pro-ts-mode)
|
|
|
|
(setopt inferior-lisp-program "sbcl")
|
|
|
|
(setopt lispy-compat '(god-mode edebug))
|
|
(add-hook 'emacs-lisp-mode-hook (lambda () (lispy-mode 1)))
|
|
(add-hook 'lisp-mode-hook (lambda () (lispy-mode 1)))
|
|
(add-hook 'lisp-data-mode-hook (lambda () (lispy-mode 1)))
|
|
(with-eval-after-load 'lispy
|
|
;; (require 'lispyville)
|
|
;; (add-hook 'lispy-mode-hook #'lispyville-mode)
|
|
(bind-keys :map lispy-mode-map
|
|
("i" . (lambda ()
|
|
(interactive)
|
|
(if (and (featurep 'god-mode) god-global-mode)
|
|
(god-mode-all -1)
|
|
(special-lispy-tab))))))
|
|
|
|
(add-to-list 'auto-mode-alist '("\\.ts\\'" . typescript-mode))
|
|
(add-to-list 'major-mode-remap-alist '(typescript-mode . typescript-ts-mode))
|
|
(add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-mode))
|
|
(add-to-list 'major-mode-remap-alist '(tsx-mode . tsx-ts-mode))
|
|
(setopt typescript-ts-mode-indent-offset 2)
|
|
(with-eval-after-load 'typescript-ts-mode
|
|
(defun my-tsx-ts-mode--indent-compatibility-b893426 ()
|
|
"Indent rules helper, to handle different releases of tree-sitter-tsx.
|
|
Check if a node type is available, then return the right indent rules."
|
|
;; handle https://github.com/tree-sitter/tree-sitter-typescript/commit/b893426b82492e59388a326b824a346d829487e8
|
|
(condition-case nil
|
|
(progn (treesit-query-capture 'tsx '((jsx_fragment) @capture))
|
|
`(((match "<" "jsx_fragment") parent 0)
|
|
((parent-is "jsx_fragment") parent typescript-ts-mode-indent-offset)))
|
|
(treesit-query-error
|
|
`(((match "<" "jsx_text") parent 0)
|
|
((parent-is "jsx_text") grand-parent typescript-ts-mode-indent-offset)))))
|
|
(advice-add 'tsx-ts-mode--indent-compatibility-b893426 :override #'my-tsx-ts-mode--indent-compatibility-b893426))
|
|
|
|
(defvar my/edit-indirect-tag-modes
|
|
'(("graphql" . graphql-ts-mode)
|
|
("gql" . graphql-ts-mode)
|
|
("sql" . sql-mode)
|
|
("css" . css-mode))
|
|
"Map of tagged-template tag names to major modes.")
|
|
|
|
(defun my/edit-indirect--node-bounds (node)
|
|
"Return (BEG . END) for NODE's contents, excluding delimiters."
|
|
(cons (1+ (treesit-node-start node))
|
|
(1- (treesit-node-end node))))
|
|
|
|
(defun my/edit-indirect-tagged-template-at-point ()
|
|
(interactive)
|
|
(let* ((node (treesit-node-at (point)))
|
|
(tmpl-node (treesit-parent-until
|
|
node
|
|
(lambda (n) (equal (treesit-node-type n) "template_string"))))
|
|
(str-node (unless tmpl-node
|
|
(treesit-parent-until
|
|
node
|
|
(lambda (n) (equal (treesit-node-type n) "string")))))
|
|
(target-node (or tmpl-node str-node)))
|
|
(unless target-node
|
|
(user-error "Point is not inside a string or template literal"))
|
|
(let* ((tagged-node (and tmpl-node (treesit-node-parent tmpl-node)))
|
|
(tag-node (and tagged-node
|
|
(equal (treesit-node-type tagged-node) "call_expression")
|
|
(treesit-node-child-by-field-name tagged-node "function")))
|
|
(tag-name (and tag-node (treesit-node-text tag-node t)))
|
|
(mode (cond
|
|
(tag-name (or (cdr (assoc tag-name my/edit-indirect-tag-modes))
|
|
major-mode))
|
|
(t 'text-mode)))
|
|
(bounds (my/edit-indirect--node-bounds target-node))
|
|
(buf (edit-indirect-region (car bounds) (cdr bounds) t)))
|
|
(with-current-buffer buf
|
|
(funcall mode)))))
|
|
|
|
(with-eval-after-load 'typescript-ts-mode
|
|
(define-key typescript-ts-mode-map (kbd "C-c '") #'my/edit-indirect-tagged-template-at-point)
|
|
(define-key tsx-ts-mode-map (kbd "C-c '") #'my/edit-indirect-tagged-template-at-point))
|
|
|
|
(with-eval-after-load 'eglot
|
|
(add-to-list 'eglot-server-programs '((prisma-ts-mode) "prisma-language-server" "--stdio")))
|
|
|
|
(push '(java-mode . java-ts-mode) major-mode-remap-alist)
|
|
(with-eval-after-load 'java-ts-mode
|
|
(bind-keys :map java-ts-mode-map
|
|
("C-c u" . string-inflection-java-style-cycle))
|
|
(push `((n-p-gp nil "block" "lambda_expression") parent-bol java-ts-mode-indent-offset)
|
|
(cdar java-ts-mode--indent-rules))
|
|
(push `((n-p-gp "}" "block" "lambda_expression") parent-bol 0)
|
|
(cdar java-ts-mode--indent-rules))
|
|
|
|
(add-hook 'java-mode-hook (lambda () (c-set-offset 'arglist-intro '+)))
|
|
|
|
(defvar jf/class-name-to-file-search-path (list "src/main/java" "src/test/java"))
|
|
|
|
(defun jf/class-name-to-file (class-name)
|
|
(let ((root (project-root (project-current t)))
|
|
(fragment (format "%s.java" (replace-regexp-in-string "\\." "/" class-name)))
|
|
(result))
|
|
(dolist (p jf/class-name-to-file-search-path result)
|
|
(unless result
|
|
(let ((path (format "%s/%s" p fragment)))
|
|
(when (file-exists-p (concat root path))
|
|
(setq result path)))))))
|
|
|
|
(defun jf/compile-class-to-file ()
|
|
(jf/class-name-to-file (concat (match-string 1) (match-string 2)))))
|
|
|
|
;(add-to-list 'compilation-error-regexp-alist-alist
|
|
; '(java-stack-trace .
|
|
; ("^[[:space:]]*at \\(\\(?:[[:lower:]]+\\.\\)+\\)[^(]+(\\([[:alnum:]]+\\)\\.java:\\([[:digit:]]+\\))"
|
|
; jf/compile-class-to-file 3)))
|
|
;(add-to-list 'compilation-error-regexp-alist 'java-stack-trace)
|
|
|
|
(add-hook 'sql-mode-hook 'sqlind-minor-mode)
|
|
|
|
(setopt haskell-ts-use-indent t)
|
|
|
|
(add-hook 'after-init-hook 'envrc-global-mode)
|
|
(setopt envrc-show-summary-in-minibuffer nil)
|
|
|
|
(setopt magit-define-global-key-bindings 'recommended)
|
|
(bind-keys ("s-g" . magit))
|
|
|
|
(setopt magit-save-repository-buffers 'dontask)
|
|
(setopt magit-commit-show-diff nil)
|
|
(with-eval-after-load 'magit
|
|
(set-face-attribute 'git-commit-summary nil :inherit nil)
|
|
(defun magit-fetch-all-on-load ()
|
|
(magit-run-git-async "fetch" "--all"))
|
|
(add-hook 'magit-status-mode-hook 'magit-fetch-all-on-load)
|
|
(remove-hook 'magit-status-headers-hook 'magit-insert-tags-header)
|
|
(require 'forge)
|
|
(add-hook 'magit-post-refresh-hook 'diff-hl-magit-post-refresh))
|
|
|
|
(with-eval-after-load 'forge
|
|
(add-hook 'forge-edit-post-hook 'forge-create-pullreq-insert-single-commit-message)
|
|
(setopt forge-status-buffer-default-topic-filters
|
|
(forge--topics-spec :type 'topic :active nil :state 'open :order 'newest)))
|
|
|
|
(global-diff-hl-mode)
|
|
|
|
(bind-keys
|
|
;; ("C-c M-x" . consult-mode-command)
|
|
("C-c h" . consult-history)
|
|
;; ("C-c k" . consult-kmacro)
|
|
;; ("C-c m" . consult-man)
|
|
;; ([remap Info-search] . consult-info)
|
|
|
|
;; ("C-x M-:" . consult-complex-command)
|
|
("C-x b" . consult-buffer)
|
|
("s-B" . consult-buffer)
|
|
("C-x 4 b" . consult-buffer-other-window)
|
|
("C-x 5 b" . consult-buffer-other-frame)
|
|
;; ("C-x t b" . consult-buffer-other-tab)
|
|
("C-x r b" . consult-bookmark)
|
|
("C-x p b" . consult-project-buffer)
|
|
|
|
("M-#" . consult-register-load)
|
|
("M-'" . consult-register-store)
|
|
("C-M-#" . consult-register)
|
|
("M-y" . consult-yank-pop)
|
|
("M-g e" . consult-compile-error)
|
|
("M-g f" . consult-flymake)
|
|
("M-g g" . consult-goto-line)
|
|
("M-g M-g" . consult-goto-line)
|
|
("M-g o" . consult-org-heading)
|
|
("M-g m" . consult-mark)
|
|
("M-g k" . consult-global-mark)
|
|
("M-g i" . consult-imenu)
|
|
("M-g I" . consult-imenu-multi)
|
|
("M-s r" . consult-ripgrep)
|
|
("M-s l" . consult-line)
|
|
("M-s L" . consult-line-multi)
|
|
("M-s e" . consult-isearch-history)
|
|
:map isearch-mode-map
|
|
("M-e" . consult-isearch-history)
|
|
("M-s e" . consult-isearch-history)
|
|
("M-s l" . consult-line)
|
|
("M-s L" . consult-line-multi)
|
|
:map minibuffer-local-map
|
|
("M-s" . consult-history)
|
|
("M-r" . consult-history))
|
|
|
|
(advice-add 'register-preview :override #'consult-register-window)
|
|
(setopt register-preview-delay 0.5)
|
|
|
|
(setopt xref-show-xrefs-function 'consult-xref)
|
|
(setopt xref-show-definitions-function 'consult-xref)
|
|
|
|
(eval-when-compile
|
|
(require 'consult))
|
|
|
|
(with-eval-after-load 'consult
|
|
(consult-customize
|
|
consult-theme :preview-key '(:debounce 0.2 any)
|
|
consult-ripgrep consult-git-grep consult-grep consult-man
|
|
consult-bookmark consult-recent-file consult-xref
|
|
consult-source-bookmark consult-source-file-register
|
|
consult-source-recent-file
|
|
consult-source-buffer
|
|
;; :preview-key "M-."
|
|
consult-source-project-recent-file
|
|
:preview-key '(:debounce 0.4 any)))
|
|
|
|
(setopt consult-narrow-key "<") ;; "C-+"
|
|
|
|
(use-package vertico
|
|
:custom
|
|
(vertico-count 25)
|
|
:config
|
|
(vertico-mode))
|
|
|
|
(setopt completion-styles '(orderless basic))
|
|
|
|
(use-package marginalia
|
|
:config
|
|
(marginalia-mode))
|
|
|
|
(use-package corfu
|
|
:custom
|
|
(corfu-cycle t)
|
|
(corfu-popupinfo-mode nil)
|
|
(corfu-quit-at-boundary nil)
|
|
(corfu-on-exact-match 'show)
|
|
:config
|
|
(global-corfu-mode))
|
|
;; (setq completion-in-region-function #'consult-completion-in-region)
|
|
|
|
(use-package cape
|
|
:custom
|
|
(add-hook 'completion-at-point-functions 'cape-file))
|
|
|
|
(use-package org
|
|
:bind (("C-c A" . org-agenda)
|
|
("C-c C" . org-capture)
|
|
("C-c l" . org-id-store-link))
|
|
:custom
|
|
(org-babel-load-languages '((emacs-lisp . t) (sql . t)))
|
|
(org-confirm-babel-evaluate nil)
|
|
(org-link-keep-stored-after-insertion t)
|
|
(org-capture-templates
|
|
'(("t" "Todo" entry
|
|
(here)
|
|
"** TODO %?\n:LOGBOOK:\n- State \"TODO\" %U\n:END:\n")
|
|
("w" "Work stuff")
|
|
("wp" "Work Project" entry
|
|
(file+headline "~/org/todo/work.org.age" "Next Actions")
|
|
"*** PROJ %?\n- State \"PROJ\" %U\n")
|
|
("wa" "Work Next Action" entry
|
|
(file+headline "~/org/todo/work.org.age" "Next Actions")
|
|
"*** TODO %?\n- State \"TODO\" %U\n")
|
|
("p" "Personal stuff")
|
|
("pp" "Personal Project" entry
|
|
(file+headline "~/org/todo/personal.org.age" "Next Actions")
|
|
"*** PROJ %?\n- State \"PROJ\" %U\n")
|
|
("pa" "Personal Next Action" entry
|
|
(file+headline "~/org/todo/personal.org.age" "Next Actions")
|
|
"*** TODO %?\n- State \"TODO\" %U\n")))
|
|
(org-agenda-files '("~/org/todo/work.org" "~/org/todo/personal.org.age"))
|
|
(org-agenda-todo-ignore-scheduled 'future)
|
|
(org-agenda-tags-todo-honor-ignore-options t)
|
|
(org-enforce-todo-dependencies t)
|
|
(org-agenda-dim-blocked-tasks 'invisible)
|
|
:config
|
|
(add-hook 'org-mode-hook 'visual-line-mode)
|
|
(set-face-attribute 'org-level-1 nil :font "Iosevka Aile")
|
|
(set-face-attribute 'org-level-2 nil :font "Iosevka Aile")
|
|
(set-face-attribute 'org-level-3 nil :font "Iosevka Aile")
|
|
(set-face-attribute 'org-level-4 nil :font "Iosevka Aile")
|
|
(set-face-attribute 'org-level-5 nil :font "Iosevka Aile")
|
|
(set-face-attribute 'org-level-6 nil :font "Iosevka Aile")
|
|
(set-face-attribute 'org-level-7 nil :font "Iosevka Aile")
|
|
(set-face-attribute 'org-level-8 nil :font "Iosevka Aile"))
|
|
|
|
(defvar-local my/org-present-face-remapping-cookies nil)
|
|
(use-package org-present
|
|
:config
|
|
(require 'face-remap)
|
|
(require 'visual-fill-column)
|
|
|
|
(defun my/org-present-start ()
|
|
(setq-local my/org-present-face-remapping-cookies nil)
|
|
;; Tweak font sizes
|
|
(dolist (face '((default :height 2.0)
|
|
(header-line :height 4.0 :background "#303446")
|
|
(org-document-title :height 1.75)
|
|
(org-code :height 2.0)
|
|
(org-verbatim :height 2.0)
|
|
(org-block :height 1.25)
|
|
(org-block-begin-line :height 0.7)
|
|
(org-level-1 :height 3.0)
|
|
(org-level-2 :height 3.0)
|
|
(org-level-3 :height 3.0)
|
|
(org-level-4 :height 3.0)
|
|
(org-level-5 :height 3.0)
|
|
(org-level-6 :height 3.0)
|
|
(org-level-7 :height 3.0)
|
|
(org-level-8 :height 3.0)))
|
|
(push (apply #'face-remap-add-relative face) my/org-present-face-remapping-cookies))
|
|
|
|
;; Set a blank header line string to create blank space at the top
|
|
(setq header-line-format " ")
|
|
|
|
;; Display inline images automatically
|
|
(org-display-inline-images)
|
|
(org-present-hide-cursor)
|
|
|
|
;; Center the presentation and wrap lines
|
|
(visual-fill-column-mode 1)
|
|
(setq visual-fill-column-center-text t)
|
|
;; (visual-line-mode 1)
|
|
)
|
|
|
|
(defun my/org-present-end ()
|
|
;; Reset font customizations
|
|
(dolist (cookie my/org-present-face-remapping-cookies)
|
|
(face-remap-remove-relative cookie))
|
|
|
|
;; Clear the header line string so that it isn't displayed
|
|
(setq header-line-format nil)
|
|
|
|
;; Stop displaying inline images
|
|
(org-remove-inline-images)
|
|
(org-present-show-cursor)
|
|
|
|
;; Stop centering the document
|
|
(visual-fill-column-mode 0)
|
|
;; (visual-line-mode 0)
|
|
)
|
|
|
|
(add-hook 'org-present-mode-hook 'my/org-present-start)
|
|
(add-hook 'org-present-mode-quit-hook 'my/org-present-end))
|
|
|
|
;; (require 'org-roam)
|
|
;; (bind-keys ("C-c n l" . org-roam-buffer-toggle)
|
|
;; ("C-c n f" . org-roam-node-find)
|
|
;; ("C-c n g" . org-roam-graph)
|
|
;; ("C-c n i" . org-roam-node-insert)
|
|
;; ("C-c n c" . org-roam-capture))
|
|
|
|
;; (setq org-roam-node-display-template (concat "${title:*} " (propertize "${tags:10}" 'face 'org-tag)))
|
|
;; (org-roam-db-autosync-mode)
|
|
|
|
(use-package org-modern
|
|
:hook org-mode)
|
|
|
|
;; (define-key org-mode-map (kbd "C-c C-r") verb-command-map)
|
|
|
|
(use-package eat
|
|
:commands (eat)
|
|
:hook (eshell-load-hook . eat-eshell-mode))
|
|
|
|
(use-package age
|
|
:custom
|
|
(age-default-identity "~/source/dotfiles/keys/yubi1age.txt")
|
|
(age-default-recipient "~/source/dotfiles/keys/myself.txt")
|
|
:config
|
|
(setenv "PINENTRY_PROGRAM" "pinentry-mac")
|
|
(age-file-enable))
|
|
|
|
(use-package claude-code
|
|
:bind-keymap ("C-c c" . claude-code-command-map)
|
|
:custom (claude-code-optimize-window-resize nil))
|