aboutsummaryrefslogtreecommitdiffstats
path: root/emacs/.emacs.d/init.org
diff options
context:
space:
mode:
Diffstat (limited to 'emacs/.emacs.d/init.org')
-rw-r--r--emacs/.emacs.d/init.org522
1 files changed, 496 insertions, 26 deletions
diff --git a/emacs/.emacs.d/init.org b/emacs/.emacs.d/init.org
index 71ca9cc..10e1a98 100644
--- a/emacs/.emacs.d/init.org
+++ b/emacs/.emacs.d/init.org
@@ -74,12 +74,12 @@ Add MELPA to package archives, this lets me install more packages. Also my own o
(add-to-list 'package-archives '("oni" . "https://ryuslash.srht.site/emacs-config/packages/")))
#+end_src
-Make use-package default to ensuring everything. This way each =use-package= doesn't need to have its own ~:ensure t~ and when I use Guix to install these modules they don't try to download everything from everywhere else.
+Make use-package default to ensuring everything on macos. This way each =use-package= doesn't need to have its own ~:ensure t~ and when I use Guix to install these modules they don't try to download everything from everywhere else.
#+begin_src emacs-lisp
(eval-and-compile
(require 'use-package-ensure)
- (setq use-package-always-ensure t))
+ (setq use-package-always-ensure (eq system-type 'darwin)))
#+end_src
* exec-path-from-shell
@@ -98,8 +98,10 @@ This is necessary to make sure that packages installed through bundle and such a
Add the =use-package-ensure-system-package= extension for =use-package=.
#+begin_src emacs-lisp
- (use-package use-package-ensure-system-package)
- (use-package system-packages)
+ (use-package use-package-ensure-system-package
+ :when (eq system-type 'darwin))
+ (use-package system-packages
+ :when (eq system-type 'darwin))
#+end_src
* Docker
@@ -116,7 +118,357 @@ I currently develop in PHP.
#+begin_src emacs-lisp
(use-package php-mode
- :ensure-system-package php)
+ :ensure-system-package php
+ :config
+ (require 'align)
+ (require 'grep)
+ (require 'hydra)
+ (require 'map)
+ (require 'php-mode)
+ (require 'project)
+ (require 'whitespace)
+
+ (defconst oni-php-scripts-dir
+ (expand-file-name "scripts" user-emacs-directory)
+ "The directory where I store scripts.")
+
+ (defun oni-php--set-require-final-newline ()
+ "Set `require-final-newline' to t.
+ This is necessary because the PHP mode configuration sets this to nil
+ for some reason."
+ (setq require-final-newline t))
+
+ (defun oni-php--whitespace-mode ()
+ "Enable whitespace mode with only tabs showing."
+ (setq-local whitespace-style '(face tabs))
+ (whitespace-mode))
+
+ (defun oni-php--auto-fill-mode ()
+ "Enable ‘auto-fill-mode’ only for comments."
+ (setq-local comment-auto-fill-only-comments t)
+ (auto-fill-mode))
+
+ (defun oni-php-set-rainbow-identifier-faces ()
+ "Set the faces to override for ‘rainbow-identifiers-mode’."
+ (setq-local rainbow-identifiers-faces-to-override
+ '(font-lock-type-face php-property-name php-function-call-traditional
+ php-doc-variable-sigil php-method-call-traditional
+ php-variable-sigil php-variable-name)))
+
+ (defun oni-php-add-use (class)
+ "Try to add a use statement for CLASS under point."
+ (interactive (list (thing-at-point 'symbol)))
+ (let* ((default-directory (project-root (project-current)))
+ (classes (read (shell-command-to-string (concat (expand-file-name "find-php-class" oni-php-scripts-dir) " " class))))
+ (class (cond
+ ((null classes)
+ (user-error "Class ‘%s’ not found" class))
+ ((> (length classes) 1)
+ (completing-read "Use: " classes nil t))
+ (t (car classes)))))
+ (save-excursion
+ (goto-char (point-min))
+
+ (if (search-forward "use " nil t)
+ (forward-paragraph)
+ (search-forward "namespace ")
+ (forward-line)
+ (insert "\n"))
+
+ (unless (re-search-backward (rx "use " (literal class) ";") nil t)
+ (let ((start (point)))
+ (insert "use " class ";\n")
+ (pulse-momentary-highlight-region start (point)))))))
+
+ (defun oni-php--syntax-in-string-p (syntax)
+ "Does SYNTAX indicate point is inside a string?"
+ (nth 3 syntax))
+
+ (defun oni-php--syntax-in-comment-p (syntax)
+ "Does SYNTAX indicate point is inside a comment?"
+ (nth 4 syntax))
+
+ (defun oni-php--in-string-or-comment-p ()
+ "Return whether or not point is within a string or comment."
+ (let ((syntax (syntax-ppss)))
+ (or (oni-php--syntax-in-string-p syntax)
+ (oni-php--syntax-in-comment-p syntax))))
+
+ (defun oni-php-insert-dot-dwim (N)
+ "Insert either a concatenation or access operator depending on context.
+
+ Do the insert N times."
+ (interactive "p")
+ (if (or (oni-php--in-string-or-comment-p)
+ (save-excursion
+ (skip-syntax-backward " ")
+ (nth 3 (syntax-ppss (1- (point)))))
+ (save-excursion
+ (skip-syntax-forward " ")
+ (nth 3 (syntax-ppss (1+ (point))))))
+ (self-insert-command N)
+ (let ((op (if (looking-back (rx (or "$" ")") (zero-or-more (any whitespace "\n" alnum "(" ")" "->")))
+ (save-excursion (backward-up-list) (1+ (point))))
+ "->"
+ "::")))
+ (dotimes (_ N) (insert op)))))
+
+ (defun oni-php-doc-comment ()
+ "Insert a PHP documentation comment at point."
+ (interactive)
+ (let ((start (point)))
+ (insert "/**\n * ")
+ (let ((insert-marker (point-marker)))
+ (insert "\n */")
+ (indent-region start (point))
+ (goto-char insert-marker))))
+
+ (defun oni-php-doc-use-comment ()
+ "Insert a PHP documentation comment for a use statement."
+ (interactive)
+ (let ((start (point))
+ (class (save-excursion
+ (save-match-data
+ (search-forward-regexp (rx "use " (group (minimal-match (zero-or-more alnum))) ";"))
+ (match-string 1)))))
+ (insert "/**\n * @use " class)
+ (let ((insert-marker (point-marker)))
+ (insert "\n */")
+ (indent-region start (point))
+ (goto-char insert-marker))))
+
+ (defun oni-php-return-belongs-to-comment ()
+ "Insert a PHP documentation comment for a BelongsTo return type."
+ (interactive)
+ (let ((start (point)))
+ (insert "/**\n * @return BelongsTo<")
+ (save-excursion
+ (insert ", $this>\n */")
+ (indent-region start (point)))))
+
+ (defun oni-php-comment-dwim (func &rest args)
+ "See if a PHP documentation comment should be added and add it.
+ Otherwise call FUNC with ARGS. This is meant as advice around
+ ‘comment-dwim’ to make it smarter for PHP code."
+ (cond ((looking-at (rx (minimal-match (zero-or-more (any whitespace "\n")))
+ "use "))
+ (oni-php-doc-use-comment))
+ ((looking-at (rx (minimal-match (zero-or-more (any whitespace "\n")))
+ (one-or-more nonl) ": BelongsTo" eol))
+ (oni-php-return-belongs-to-comment))
+ ((and (derived-mode-p 'php-mode)
+ (not (region-active-p))
+ (looking-back (rx (minimal-match (zero-or-more blank))) (line-beginning-position))
+ (looking-at (rx (minimal-match (zero-or-more (any whitespace "\n")))
+ (or (regexp php-beginning-of-defun-regexp)
+ (regexp php--re-classlike-pattern)))))
+ (oni-php-doc-comment))
+ (t (apply func args))))
+
+ (defun oni-php-generate-namespace ()
+ (string-join
+ (mapcar
+ (lambda (s)
+ (let ((case-fold-search nil))
+ (if (string-match-p (rx upper-case) s)
+ s
+ (capitalize s))))
+ (cdr (split-string
+ (directory-file-name
+ (file-name-directory
+ (file-relative-name
+ buffer-file-name
+ (project-root (project-current)))))
+ "/")))
+ "\\"))
+
+ (defun oni-php-set-dabbrev-settings ()
+ "Set any settings for dabbrev that make sense for PHP code."
+ (setq-local dabbrev-abbrev-skip-leading-regexp (rx "$")))
+
+ (defun oni-php-ignore-error-at-point (error-type)
+ (interactive
+ (list (let* ((error-types
+ (seq-uniq
+ (mapcar (lambda (e)
+ (let ((str (flycheck-error-message e)))
+ (string-match (rx "🪪" (one-or-more whitespace) (group (one-or-more (any alnum ".")))) str)
+ (match-string 1 str)))
+ (seq-filter (lambda (e) (not (flycheck-error-checker e)))
+ (flycheck-overlay-errors-at (point))))
+ 'string=)))
+ (if (> 1 (length error-types))
+ (completing-read "Ignore type: " error-types)
+ (car error-types)))))
+ (save-excursion
+ (goto-char (line-beginning-position))
+ (indent-for-tab-command)
+ (insert "/** @phpstan-ignore " error-type " */\n")
+ (indent-for-tab-command)))
+
+ (defconst +oni-php-import-display+ "…"
+ "What to display instead of imports.")
+
+ (defun oni-php-toggle-import-display ()
+ (interactive)
+ (let* ((overlay (seq-find (lambda (o) (overlay-get o 'oni-php-hidden-imports))
+ (overlays-at (point))))
+ (visiblep (not (string= +oni-php-import-display+
+ (overlay-get overlay 'display)))))
+ (overlay-put overlay 'display (if visiblep +oni-php-import-display+ nil))))
+
+ (defvar oni-php-hidden-imports-keymap
+ (let ((keymap (make-sparse-keymap)))
+ (define-key keymap (kbd "TAB") 'oni-php-toggle-import-display)
+ keymap))
+
+ (defun oni-php-hide-imports ()
+ "Hide the imports at the start of the file."
+ (save-excursion
+ (let (start end)
+ (goto-char (point-min))
+ (when (re-search-forward (rx bol "use ") nil t)
+ (setq start (point))
+ (while (re-search-forward (rx bol "use ") nil t))
+ (setq end (line-end-position))
+ (dolist (o (overlays-in start end))
+ (when (overlay-get o 'oni-php-hidden-imports)
+ (delete-overlay o)))
+ (let ((overlay (make-overlay start end nil t t)))
+ (overlay-put overlay 'oni-php-hidden-imports t)
+ (overlay-put overlay 'display +oni-php-import-display+)
+ (overlay-put overlay 'keymap oni-php-hidden-imports-keymap))))))
+
+ (defhydra php-mode-hydra (:color blue)
+ ("a" align-current "Align current selection"))
+
+ (advice-add 'comment-dwim :around #'oni-php-comment-dwim)
+
+ (add-hook 'php-mode-hook #'oni-php--set-require-final-newline)
+ (add-hook 'php-mode-hook #'oni-php--whitespace-mode)
+ (add-hook 'php-mode-hook 'corfu-mode)
+ (add-hook 'php-mode-hook 'display-fill-column-indicator-mode)
+ (add-hook 'php-mode-hook 'electric-indent-local-mode)
+ (add-hook 'php-mode-hook 'fic-mode)
+ (add-hook 'php-mode-hook 'flycheck-mode)
+ (add-hook 'php-mode-hook 'oni-php--auto-fill-mode)
+ (add-hook 'php-mode-hook 'oni-php-hide-imports)
+ (add-hook 'php-mode-hook 'oni-php-set-dabbrev-settings)
+ (add-hook 'php-mode-hook 'oni-php-set-rainbow-identifier-faces)
+ (add-hook 'php-mode-hook 'rainbow-delimiters-mode)
+ (add-hook 'php-mode-hook 'rainbow-identifiers-mode)
+ (add-hook 'php-mode-hook 'smartparens-mode)
+ (add-hook 'php-mode-hook 'subword-mode)
+ (add-hook 'php-mode-hook 'yas-minor-mode)
+
+ (define-key php-mode-map (kbd "C-c m") #'php-mode-hydra/body)
+ (define-key php-mode-map (kbd ".") #'oni-php-insert-dot-dwim)
+
+ ;; In PHP code it's nice to have any ~=>~ aligned.
+
+ ;; <?php
+ ;; array(
+ ;; 'foo' => 'bar',
+ ;; 'frob' => 'baz'
+ ;; );
+ ;; ?>
+
+ (add-to-list 'align-rules-list
+ `(php-array-arrow
+ (regexp . ,(rx any (group (zero-or-more whitespace)) "=>" any))
+ (group . (1))
+ (modes . '(php-mode web-mode php-mode))
+ (repeat . t)))
+
+ ;; The WordPress coding standards specify that multiple assignments
+ ;; should have their assignment operators aligned.
+
+ ;; <?php
+ ;; $variable = "value";
+ ;; $other_variable = "other value";
+ ;; $one_more_variable = "one more variable";
+
+ (add-to-list 'align-rules-list
+ `(php-assignment-equals
+ (regexp . ,(rx any (group (zero-or-more whitespace)) "="
+ (zero-or-more whitespace) any))
+ (group . (1))
+ (modes . '(php-mode web-mode php-ts-mode))
+ (repeat . t)))
+
+ ;;;###autoload
+ (add-to-list 'auto-mode-alist '("\\.inc\\'" . php-mode))
+
+ ;;;###autoload
+ (add-to-list 'auto-mode-alist '("\\.module\\'" . php-mode))
+
+ ;;;###autoload
+ (with-eval-after-load 'grep
+ (add-to-list 'grep-files-aliases '("php" . "*.php *.inc *.module")))
+
+ (with-eval-after-load 'autoinsert
+ (setf (map-elt auto-insert-alist (rx ".php" eos))
+ '(nil
+ "<?php\n"
+ "\n"
+ "declare(strict_types=1);\n"
+ "\n"
+ "namespace " (oni-php-generate-namespace) ";\n"
+ "\n"
+ "class " (file-name-sans-extension (file-name-nondirectory (buffer-file-name))) "\n"
+ "{\n"
+ > _ "\n"
+ "}\n")
+ (map-elt auto-insert-alist (rx "Test.php" eos))
+ '(nil
+ "<?php\n"
+ "\n"
+ "declare(strict_types=1);\n"
+ "\n"
+ "namespace " (oni-php-generate-namespace) ";\n"
+ "\n"
+ "use Illuminate\\Foundation\\Testing\\RefreshDatabase;\n"
+ "use Illuminate\\Foundation\\Testing\\WithFaker;\n"
+ "use Tests\\TestCase;\n"
+ "\n"
+ "class " (file-name-sans-extension (file-name-nondirectory (buffer-file-name))) " extends TestCase\n"
+ "{\n"
+ > "use RefreshDatabase;\n"
+ > "use WithFaker;\n"
+ "\n"
+ > "/** @test */\n"
+ > "public function " (replace-regexp-in-string (rx whitespace) "_" (downcase (skeleton-read "Test function name: "))) "(): void\n"
+ "{" > "\n"
+ > _ "\n"
+ "}" > "\n"
+ "}")))
+
+ (defun oni-php-in-expression-context-p ()
+ (not (oni-php-in-static-call-context-p)))
+
+ (defun oni-php-in-test-file ()
+ (string-suffix-p "Test.php" buffer-file-name))
+
+ (defun oni-php-in-static-call-context-p ()
+ (looking-back "::\\w+" (- (point) (line-beginning-position))))
+
+ (defun oni-php-grep-symbol (symbol)
+ "Use ‘rgrep’ to find SYMBOL.
+ If there is an active region when this command is called interactively
+ SYMBOL will be the text in the region. Otherwise it's the symbol at
+ point."
+ (interactive (list
+ (if (region-active-p)
+ (buffer-substring-no-properties (region-beginning) (region-end))
+ (thing-at-point 'symbol))))
+ (rgrep (rx (literal symbol))
+ (map-elt grep-files-aliases "php")
+ (project-root (project-current))))
+
+ (define-key php-mode-map (kbd "C-c .") nil t)
+ (define-key php-mode-map (kbd "C-c . g") '("Search for symbol at point" . oni-php-grep-symbol))
+ (define-key php-mode-map (kbd "C-c . a") '("Import symbol at point" . oni-php-add-use))
+ (define-key php-mode-map (kbd "C-c . i") '("Ignore error at point" . oni-php-ignore-error-at-point)))
#+end_src
* Magit
@@ -164,39 +516,40 @@ Also install =markdown-indent-mode= which is very much like =org-indent-mode= (b
* Org Mode
#+begin_src emacs-lisp
- (use-package org
- :bind (("C-c o c" . org-capture)
- ("C-c o a" . org-agenda)
- ("C-c o l" . org-store-link)
- ("C-c o b" . org-switchb))
- :config
- (add-hook 'org-mode-hook 'visual-line-mode)
- (add-hook 'org-mode-hook 'org-indent-mode))
+ (use-package org
+ :init (define-key global-map (kbd "C-c o") (make-sparse-keymap))
+ :bind (("C-c o c" . org-capture)
+ ("C-c o a" . org-agenda)
+ ("C-c o l" . org-store-link)
+ ("C-c o b" . org-switchb))
+ :config
+ (add-hook 'org-mode-hook 'visual-line-mode)
+ (add-hook 'org-mode-hook 'org-indent-mode))
- (use-package org-capture
- :config
- (map-put org-capture-templates "t"
- '("A simple TODO item" entry (file "") "* TODO %?
+ (use-package org-capture
+ :config
+ (map-put org-capture-templates "t"
+ '("A simple TODO item" entry (file "") "* TODO %?
:PROPERTIES:
:CREATED: %U
:END:"))
- (map-put org-capture-templates "d"
- '("A simple DONE item" entry (file "") "* DONE %?
+ (map-put org-capture-templates "d"
+ '("A simple DONE item" entry (file "") "* DONE %?
:PROPERTIES:
:CREATED: %U
:END:"))
- (map-put org-capture-templates "n"
- '("A simple note" entry (file "") "* Note taken on %U
+ (map-put org-capture-templates "n"
+ '("A simple note" entry (file "") "* Note taken on %U
:PROPERTIES:
:CREATED: %U
:END:
%?"))
- (map-put org-capture-templates "N"
- '("A simple note (on current task)" item (clock) "- Note taken on %U: \\\\
+ (map-put org-capture-templates "N"
+ '("A simple note (on current task)" item (clock) "- Note taken on %U: \\\\
%?"))
- (map-put org-capture-templates "r"
- '("An Operational Request came in" entry (file "") "* TODO Operation Request %^U
+ (map-put org-capture-templates "r"
+ '("An Operational Request came in" entry (file "") "* TODO Operation Request %^U
:PROPERTIES:
:CREATED: %U
:END:
@@ -831,7 +1184,7 @@ Highlight diffs in the fringe.
#+begin_src emacs-lisp
(use-package rainbow-delimiters
- :hook (php-mode emacs-lisp-mode))
+ :hook (prog-mode))
#+end_src
* Mixed Pitch Mode
@@ -928,6 +1281,39 @@ Now the pull app.
'pull-app))
#+end_src
+social api:
+
+#+begin_src emacs-lisp
+ (use-package files
+ :ensure nil
+ :config
+ (dir-locals-set-class-variables
+ 'social-api
+ '((nil . ((grep-find-ignored-directories . (".git" "vendor" "cdk.out" "storage" "punt/tmp" "chanced/tmp"))
+ (eglot-workspace-configuration
+ . (:language_server_phpstan (:enabled t
+ :mem_limit "2G"
+ :bin "chanced/vendor/bin/phpstan")))))
+ (php-mode . ((fill-column . 80)
+ (eglot-server-programs . (((php-mode phps-mode php-ts-mode)
+ "localhost" 4564)))
+ (php-project-root . git)
+ (phpstan-executable . (root . "../chanced-scripts/phpstan"))
+ (phpstan-config-file . "/app/chanced/phpstan.neon")
+ (phpstan-memory-limit . "4G")
+ (phpstan-level . 9)
+ (eval . (setq-local flycheck-phpstan-executable (expand-file-name "../chanced-scripts/phpstan" (project-root (project-current)))))
+ (php-ts-mode-php-executable . "docker exec -it chanced-backend php artisan tinker")))
+ (typescript-mode . ((typescript-indent-level . 2)))
+ ("punt"
+ . ((php-mode . ((phpstan-config-file . "/app/punt/phpstan.neon")))))
+ ("filament"
+ . ((php-mode . ((phpstan-config-file . "/app/filament/phpstan.neon")))))))
+ (dir-locals-set-directory-class
+ (expand-file-name "~/code/diamond-interactive/social-api")
+ 'social-api))
+#+end_src
+
* Eglot
Set up a server program for Eglot for org-mode.
@@ -959,6 +1345,7 @@ This includes cape for extra completion-at-point functions, and corfu-prescient
#+begin_src emacs-lisp
(use-package corfu
+ :hook (prog-mode)
:config
(setq corfu-auto t))
@@ -996,3 +1383,86 @@ Phel is a Clojure-inspired lisp that compiles to PHP.
(use-package phel-mode
:mode (rx ".phel" eos))
#+end_src
+
+* Paredit
+
+The only way to edit lispy languages. This turns Emacs into a structural editor for s-expression-based languages.
+
+#+begin_src emacs-lisp
+ (use-package paredit
+ :hook (lisp-data-mode fennel-mode clojure-mode))
+#+end_src
+
+* Yasnippet
+
+Snippets!
+
+#+begin_src emacs-lisp
+ (use-package yasnippet
+ :diminish yasnippet-minor-mode
+ :init
+ (yas-global-mode)
+ :bind (:map yas-minor-mode-map
+ ("<tab>")
+ ("C-\\" . yas-expand)
+ ("TAB"))
+ :config
+ (define-key yas-minor-mode-map (kbd "SPC") yas-maybe-expand)
+ (setq-default yas-buffer-local-condition
+ yas-not-string-or-comment-condition))
+#+end_src
+
+* Help
+
+I've always been annoyed that calling any of the help functions never select the help buffer. Thankfully due to [[https://irreal.org/blog/?p=13752][Irreal: A Short Report On Help Focus]] I know how to fix that now.
+
+That got me looking at [[elisp:(customize-group "help")]] and discover [[help:help-enable-variable-value-editing]].
+
+#+begin_src emacs-lisp
+ (use-package help
+ :custom
+ (help-window-select t)
+ (help-enable-variable-value-editing t))
+#+end_src
+
+* Denote
+
+I need to try taking notes with Denote.
+
+#+begin_src emacs-lisp
+ (use-package denote
+ :hook (dired-mode . denote-dired-mode)
+ :bind
+ (("C-c n n" . denote)
+ ("C-c n r" . denote-rename-file)
+ ("C-c n l" . denote-link)
+ ("C-c n b" . denote-backlinks)
+ ("C-c n d" . denote-dired)
+ ("C-c n g" . denote-grep))
+ :config
+ (setq denote-directory (expand-file-name "~/Documents/denote-notes/"))
+ (denote-rename-buffer-mode 1))
+#+end_src
+
+* Dabbrev
+
+Dynamic abbreviations are one of the completion mechanisms that Emacs supports. I mainly use this with corfu and cape-dabbrev, but when I'm looking for completions I want to see any variant with different letter casings, and also maintain the letter casing of which option I pick.
+
+#+begin_src emacs-lisp
+ (use-package dabbrev
+ :ensure nil
+ :config
+ (setq dabbrev-case-replace nil)
+ (setq dabbrev-case-distinction nil)
+ (setq dabbrev-case-fold-search t))
+#+end_src
+
+* Typescript
+
+Emacs comes with built-in typescript mode.
+
+#+begin_src emacs-lisp
+ (use-package typescript-ts-mode
+ :ensure nil
+ :mode (rx ".ts" eos))
+#+end_src