diff options
Diffstat (limited to 'emacs/.emacs.d/init.org')
| -rw-r--r-- | emacs/.emacs.d/init.org | 522 |
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 |
