diff --git a/lisp/cli/meta.el b/lisp/cli/meta.el index 503393339..9d0f01742 100644 --- a/lisp/cli/meta.el +++ b/lisp/cli/meta.el @@ -183,32 +183,69 @@ OPTIONS: input (doom-cli-command-string (cdr command))) command))))) -(defun doom-cli-help--similarity (s1 s2) - ;; Ratcliff-Obershelp similarity - (let* ((s1 (downcase s1)) - (s2 (downcase s2)) - (s1len (length s1)) - (s2len (length s2))) - (if (or (zerop s1len) - (zerop s2len)) - 0.0 - (/ (let ((i 0) (j 0) (score 0) jlast) - (while (< i s1len) - (unless jlast (setq jlast j)) - (if (and (< j s2len) - (= (aref s1 i) (aref s2 j))) - (progn (cl-incf score) - (cl-incf i) - (cl-incf j)) - (setq m 0) - (cl-incf j) - (when (>= j s2len) - (setq j (or jlast j) - jlast nil) - (cl-incf i)))) - (* 2.0 score)) - (+ (length s1) - (length s2)))))) +(defun doom-cli-help--similarity (a b) + (- 1 (/ (float (doom-cli-help--string-distance a b)) + (max (length a) (length b))))) + +(defun doom-cli-help--string-distance (a b) + "Calculate the Restricted Damerau-Levenshtein distance between A and B. +This is also known as the Optimal String Alignment algorithm. + +It is assumed that A and B are both strings, and before processing both are +converted to lowercase. + +This returns the minimum number of edits required to transform A +to B, where each edit is a deletion, insertion, substitution, or +transposition of a character, with the restriction that no +substring is edited more than once." + (let ((a (downcase a)) + (b (downcase b)) + (alen (length a)) + (blen (length b)) + (start 0)) + (when (> alen blen) + (let ((c a) + (clen alen)) + (setq a b alen blen + b c blen clen))) + (while (and (< start (min alen blen)) + (= (aref a start) (aref b start))) + (cl-incf start)) + (cl-decf start) + (if (= (1+ start) alen) + (- blen start) + (let ((v0 (make-vector (- blen start) 0)) + (v1 (make-vector (- blen start) 0)) + (a_i (aref a (max 0 start))) + (current 0) + a_i-1 b_j b_j-1 + left transition-next + above this-transition) + (dotimes (vi (length v0)) + (aset v0 vi (1+ vi))) + (dolist (i (number-sequence (1+ start) (1- alen))) + (setq a_i-1 a_i + a_i (aref a i) + b_j (aref b (max 0 start)) + left (- i start 1) + current (- i start) + transition-next 0) + (dolist (j (number-sequence (1+ start) (1- blen))) + (setq b_j-1 b_j + b_j (aref b j) + above current + current left + this-transition transition-next + transition-next (aref v1 (- j start))) + (aset v1 (- j start) current) + (setq left (aref v0 (- j start))) + (unless (= a_i b_j) + ;; Minimum between substitution, deletion, and insertion + (setq current (min (1+ current) (1+ above) (1+ left))) + (when (and (> i (1+ start)) (> j (1+ start)) (= a_i b_j-1) (= a_i-1 b_j)) + (setq current (min current (cl-incf this-transition))))) + (aset v0 (- j start) current))) + current)))) ;;; Help: printers ;; TODO Parameterize optional args with `cl-defun' diff --git a/lisp/doom-editor.el b/lisp/doom-editor.el index 2159b885c..2827fde85 100644 --- a/lisp/doom-editor.el +++ b/lisp/doom-editor.el @@ -2,10 +2,8 @@ ;;; Commentary: ;;; Code: -(defvar doom-detect-indentation-excluded-modes - '(fundamental-mode pascal-mode so-long-mode doom-docs-org-mode) - "A list of major modes in which indentation should be automatically -detected.") +(defvar doom-detect-indentation-excluded-modes '(pascal-mode so-long-mode) + "A list of major modes where indentation shouldn't be auto-detected.") (defvar-local doom-inhibit-indent-detection nil "A buffer-local flag that indicates whether `dtrt-indent' should try to detect @@ -502,8 +500,9 @@ files, so this replace calls to `pp' with the much faster `prin1'." (unless (or (not after-init-time) doom-inhibit-indent-detection doom-large-file-p - (memq major-mode doom-detect-indentation-excluded-modes) - (member (substring (buffer-name) 0 1) '(" " "*"))) + (eq major-mode 'fundamental-mode) + (member (substring (buffer-name) 0 1) '(" " "*")) + (apply #'derived-mode-p doom-detect-indentation-excluded-modes)) ;; Don't display messages in the echo area, but still log them (let ((inhibit-message (not init-file-debug))) (dtrt-indent-mode +1)))) diff --git a/lisp/lib/files.el b/lisp/lib/files.el index 1feb20a26..2ffcc8c1e 100644 --- a/lisp/lib/files.el +++ b/lisp/lib/files.el @@ -487,7 +487,7 @@ If FORCE-P, overwrite the destination file if it exists, without confirmation." (defun doom/sudo-find-file (file) "Open FILE as root." (interactive "FOpen file as root: ") - (find-file (doom--sudo-file-path file))) + (find-file (doom--sudo-file-path (expand-file-name file)))) ;;;###autoload (defun doom/sudo-this-file () diff --git a/modules/completion/vertico/config.el b/modules/completion/vertico/config.el index f1037741b..f5d6b7218 100644 --- a/modules/completion/vertico/config.el +++ b/modules/completion/vertico/config.el @@ -145,15 +145,14 @@ orderless." consult-async-min-input 2 consult-async-refresh-delay 0.15 consult-async-input-throttle 0.2 - consult-async-input-debounce 0.1) - (if doom-projectile-fd-binary - (setq consult-fd-args - '(doom-projectile-fd-binary - "--color=never" - ;; https://github.com/sharkdp/fd/issues/839 - "--full-path --absolute-path" - "--hidden --exclude .git" - (if (featurep :system 'windows) "--path-separator=/")))) + consult-async-input-debounce 0.1 + consult-fd-args + '((if (executable-find "fdfind" 'remote) "fdfind" "fd") + "--color=never" + ;; https://github.com/sharkdp/fd/issues/839 + "--full-path --absolute-path" + "--hidden --exclude .git" + (if (featurep :system 'windows) "--path-separator=/"))) (consult-customize consult-ripgrep consult-git-grep consult-grep diff --git a/modules/editor/format/autoload/format.el b/modules/editor/format/autoload/format.el index 63b932f4e..ab49403bd 100644 --- a/modules/editor/format/autoload/format.el +++ b/modules/editor/format/autoload/format.el @@ -61,12 +61,8 @@ (defun +format/buffer (&optional arg) "Reformat the current buffer using LSP or `format-all-buffer'." (interactive "P") - (call-interactively - (if (and +format-with-lsp - (bound-and-true-p lsp-mode) - (lsp-feature? "textDocument/formatting")) - #'lsp-format-buffer - #'apheleia-format-buffer))) + (or (run-hook-with-args-until-success '+format-functions (point-min) (point-max) 'buffer) + (call-interactively #'apheleia-format-buffer))) ;;;###autoload (defun +format/region (beg end &optional arg) @@ -76,11 +72,8 @@ WARNING: this may not work everywhere. It will throw errors if the region contains a syntax error in isolation. It is mostly useful for formatting snippets or single lines." (interactive "rP") - (if (and +format-with-lsp - (bound-and-true-p lsp-mode) - (lsp-feature? "textDocument/rangeFormatting")) - (call-interactively #'lsp-format-region) - (+format-region beg end))) + (or (run-hook-with-args-until-success '+format-functions beg end 'region) + (+format-region beg end))) ;;;###autoload (defun +format/region-or-buffer () @@ -91,3 +84,72 @@ is selected)." (if (doom-region-active-p) #'+format/region #'+format/buffer))) + + +;; +;;; Specialized formatters + +;;;###autoload +(defun +format-with-lsp-fn (beg end op) + "Format the region/buffer using any available lsp-mode formatter. + +Does nothing if `+format-with-lsp' is nil or the active server doesn't support +the requested feature." + (and +format-with-lsp + (bound-and-true-p lsp-mode) + (pcase op + ('buffer (condition-case _ + ;; Avoid lsp-feature? checks for this, since + ;; `lsp-format-buffer' does its own, and allows clients + ;; without formatting support (but with rangeFormatting, + ;; for some reason) to work. + (always (lsp-format-buffer)) + ('lsp-capability-not-supported nil))) + ('region (if (lsp-feature? "textDocument/rangeFormatting") + (always (lsp-format-region beg end)))) + (_ (error "Invalid formatter operation: %s" op))))) + +;;;###autoload +(defun +format-with-eglot-fn (beg end op) + "Format the region/buffer using any available eglot formatter. + +Does nothing if `+format-with-lsp' is nil or the active server doesn't support +the requested feature." + (and +format-with-lsp + (bound-and-true-p eglot-managed-mode) + (pcase op + ('buffer (if (eglot--server-capable :documentFormattingProvider) + (always (eglot-format-buffer)))) + ('region (if (eglot--server-capable :documentRangeFormattingProvider) + (always (eglot-format beg end)))) + (_ (error "Invalid formatter operation: %s" op))))) + +;;;###autoload +(defun +format-in-org-src-blocks-fn (beg end _op) + "TODO" + (when (derived-mode-p 'org-mode) + (goto-char beg) + (while (re-search-forward org-babel-src-block-regexp end t) + (let* ((element (org-element-at-point)) + (block-beg (save-excursion + (goto-char (org-babel-where-is-src-block-head element)) + (line-beginning-position 2))) + (block-end (save-excursion + (goto-char (org-element-property :end element)) + (skip-chars-backward " \t\n") + (line-beginning-position))) + (beg (if beg (max beg block-beg) block-beg)) + (end (if end (min end block-end) block-end)) + (lang (org-element-property :language element)) + (major-mode (org-src-get-lang-mode lang))) + (save-excursion + (if (eq major-mode 'org-mode) + (user-error "Cannot reformat an org src block in org-mode") + ;; Determine formatter based on language and format the region + (let ((formatter (apheleia--get-formatters 'interactive))) + (unless formatter + (setq formatter (apheleia--get-formatters 'prompt)) + (unless formatter + (user-error "No formatter configured for language: %s" lang))) + (let ((apheleia-formatter formatter)) + (+format-region beg end))))))))) diff --git a/modules/editor/format/config.el b/modules/editor/format/config.el index 77dd06479..0af781fb0 100644 --- a/modules/editor/format/config.el +++ b/modules/editor/format/config.el @@ -27,6 +27,18 @@ This has no effect on the +onsave flag, apheleia will always be used there.") (defvaralias '+format-with 'apheleia-formatter "Set this to explicitly use a certain formatter for the current buffer.") +(defvar +format-functions + '(+format-in-org-src-blocks-fn + +format-with-lsp-fn + +format-with-eglot-fn) + "A list of functions to run when formatting a buffer or region. + +Each function is given three arguments: the starting point, end point, and a +symbol indicating the type of operation being requested (as a symbol: either +`region' or `buffer'). + +The first function to return non-nil will abort all functions after it, +including Apheleia itself.") ;; ;;; Bootstrap diff --git a/modules/email/wanderlust/config.el b/modules/email/wanderlust/config.el index 3175268c3..db4a5b696 100644 --- a/modules/email/wanderlust/config.el +++ b/modules/email/wanderlust/config.el @@ -50,6 +50,7 @@ "^List-.*:" "^Received-SPF:" "^DKIM-.*:" + "^DomainKey-Signature:" "^SPF-.*:" "^Autocrypt:" "^ARC-.*:" diff --git a/modules/email/wanderlust/packages.el b/modules/email/wanderlust/packages.el index 48203a6bd..d6ec56a78 100644 --- a/modules/email/wanderlust/packages.el +++ b/modules/email/wanderlust/packages.el @@ -8,7 +8,7 @@ (package! flim :recipe (:branch "flim-1_14-wl") :pin "abdd2315006eb31476249223569808adb1c0f7b2") (package! semi :recipe (:branch "semi-1_14-wl") :pin "9063a4485b148a767ea924f0e7cc78d3524ba256") -(package! wanderlust :pin "9fd2c65e8d690625f35035a71e73f51f740dbe04") +(package! wanderlust :pin "c15e8ece4f34f10479e17cda19d10b98f6be3ec1") (when (modulep! +xface) (package! x-face-e21 diff --git a/modules/lang/dart/packages.el b/modules/lang/dart/packages.el index 19830a9b3..60b073187 100644 --- a/modules/lang/dart/packages.el +++ b/modules/lang/dart/packages.el @@ -5,7 +5,7 @@ (when (and (modulep! +lsp) (not (modulep! :tools lsp +eglot))) - (package! lsp-dart :pin "e7ee6afc2e165291360fd35d16648307920837c7")) + (package! lsp-dart :pin "f51c80f5458d8ba4db9dd3781d190c6c32213250")) (when (modulep! +flutter) (package! flutter :pin "004c91e070a9b4a2a5042f5bb20015ec65453acf") diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index c7f7a422f..087f95cf6 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -105,7 +105,6 @@ Is relative to `org-directory', unless it is absolute. Is used in Doom's default (defun +org-init-appearance-h () "Configures the UI for `org-mode'." (setq org-indirect-buffer-display 'current-window - org-eldoc-breadcrumb-separator " → " org-enforce-todo-dependencies t org-entities-user '(("flat" "\\flat" nil "" "" "266D" "♭") @@ -814,14 +813,6 @@ Unlike showNlevels, this will also unfold parent trees." :weight bold)))) (apply fn args))) - (after! org-eldoc - ;; HACK Fix #2972: infinite recursion when eldoc kicks in in 'org' or - ;; 'python' src blocks. - ;; TODO Should be reported upstream! - (puthash "org" #'ignore org-eldoc-local-functions-cache) - (puthash "plantuml" #'ignore org-eldoc-local-functions-cache) - (puthash "python" #'python-eldoc-function org-eldoc-local-functions-cache)) - (defun +org--restart-mode-h () "Restart `org-mode', but only once." (quiet! (org-mode-restart)) @@ -957,7 +948,7 @@ between the two." (:when (modulep! :completion vertico) "." #'consult-org-heading "/" #'consult-org-agenda) - "A" #'org-archive-subtree + "A" #'org-archive-subtree-default "e" #'org-export-dispatch "f" #'org-footnote-action "h" #'org-toggle-heading @@ -1090,7 +1081,7 @@ between the two." "n" #'org-narrow-to-subtree "r" #'org-refile "s" #'org-sparse-tree - "A" #'org-archive-subtree + "A" #'org-archive-subtree-default "N" #'widen "S" #'org-sort) (:prefix ("p" . "priority") @@ -1194,6 +1185,20 @@ between the two." (add-hook 'kill-emacs-hook #'org-clock-save)) +(use-package! org-eldoc + ;; HACK: Fix #7633: this hook is no longer autoloaded by org-eldoc (in + ;; org-contrib), so we have to add it ourselves. + :hook (org-mode . org-eldoc-load) + :init (setq org-eldoc-breadcrumb-separator " → ") + :config + ;; HACK Fix #2972: infinite recursion when eldoc kicks in in 'org' or 'python' + ;; src blocks. + ;; TODO Should be reported upstream! + (puthash "org" #'ignore org-eldoc-local-functions-cache) + (puthash "plantuml" #'ignore org-eldoc-local-functions-cache) + (puthash "python" #'python-eldoc-function org-eldoc-local-functions-cache)) + + (use-package! org-pdftools :when (modulep! :tools pdf) :commands org-pdftools-export