From 30a7f2d436b50645614b7734bacb104cf155437d Mon Sep 17 00:00:00 2001 From: Mathew <45mm.cartridge421@slmail.me> Date: Sat, 10 Feb 2024 21:57:22 +0000 Subject: [PATCH 01/12] fix(lib): doom/sudo-find-file: expand given path It's possible for the user to type shell variables (something like `$MYVAR/dir/filename`) as part of the filepath, so we need to call `expand-file-name`. --- lisp/lib/files.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 () From d6db0312fdf8c3a69d1aac243808b824ec9491e7 Mon Sep 17 00:00:00 2001 From: Tim Ruffing Date: Sat, 10 Feb 2024 09:53:27 +0100 Subject: [PATCH 02/12] tweak(org): honor default command when archiving Change the binding for archiving to `org-archive-subtree-default`, which is the recommend "catch-all" command in the org manual. The user can specify the actual command in `org-archive-default-command`. The default for this variable is `org-archive-subtree`, which we previously used for the binding, so this commit changes behavior only for users who have set `org-archive-default-command` explicitly. --- modules/lang/org/config.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index c7f7a422f..c0606dec4 100644 --- a/modules/lang/org/config.el +++ b/modules/lang/org/config.el @@ -957,7 +957,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 +1090,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") From 52355c6131fdddea578bf3de737a46c60852a127 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Fri, 9 Feb 2024 00:25:05 -0500 Subject: [PATCH 03/12] fix(org): initialize eldoc in org-mode buffers A recent change upstream (see emacsmirror/org-contrib@6e208c87bf6e) removed the autoload for adding org-eldoc-load to org-mode-hook, so we have to add the hook ourselves (the function is still autoloaded, fortunately). Also moves org-eldoc config into its own use-package! block. Fix: #7633 Ref: emacsmirror/org-contrib@6e208c87bf6e --- modules/lang/org/config.el | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/modules/lang/org/config.el b/modules/lang/org/config.el index c0606dec4..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)) @@ -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 From 3986ee6c2b1b2629a12733017d675c9ac94a84ba Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 13 Feb 2024 12:36:20 -0500 Subject: [PATCH 04/12] fix: exclude indent detection in derived modes Changes what major modes we exclude from dtrt-indent's auto-detection. Any mode in doom-detect-indentation-excluded-modes, plus derived modes, will be excluded instead of only the parent modes. This indirectly fixes an issue where org-mode derivatives (like org-journal-mode) have their tab-width changed (#7670), causing the `org-current-text-column` macro to throw the following error: Tab width in Org files must be 8, not N. Please adjust your `tab-width' settings for Org mode. I opted for this solution instead rather than adding all possibly derivatives to `doom-detect-indentation-excluded-modes`. Fix: #7670 Ref: https://github.com/emacs-straight/org-mode/blob/38dd882685e3cc5843a9cf30155432b4ebce8514/lisp/org-macs.el#L1154 --- lisp/doom-editor.el | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) 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)))) From fe776f8d84027759482c59bcb360b8eaef756864 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Tue, 13 Feb 2024 20:41:37 -0500 Subject: [PATCH 05/12] fix(vertico): use remote fd in tramp buffers --- modules/completion/vertico/config.el | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/modules/completion/vertico/config.el b/modules/completion/vertico/config.el index f92bd85a2..59c8d284d 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 From 771051886912f934cdbc21a22abdc7c173c9f170 Mon Sep 17 00:00:00 2001 From: "Kirill A. Korinsky" Date: Wed, 14 Feb 2024 08:52:58 +0100 Subject: [PATCH 06/12] bump: :email wanderlust wanderlust/wanderlust@9fd2c65e8d69 -> wanderlust/wanderlust@c15e8ece4f34 --- modules/email/wanderlust/packages.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 8651651125f8009b669f0297c63683968a8f014e Mon Sep 17 00:00:00 2001 From: "Kirill A. Korinsky" Date: Wed, 14 Feb 2024 08:55:24 +0100 Subject: [PATCH 07/12] tweak(wanderlust): don't show DomainKey-Signature --- modules/email/wanderlust/config.el | 1 + 1 file changed, 1 insertion(+) 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-.*:" From 20de3d0f29c1f4924611bfd72d39aae4fa51e3eb Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Wed, 14 Feb 2024 17:44:21 -0500 Subject: [PATCH 08/12] refactor(format): introduce +format-functions --- modules/editor/format/autoload/format.el | 67 ++++++++++++++++++++---- modules/editor/format/config.el | 11 ++++ 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/modules/editor/format/autoload/format.el b/modules/editor/format/autoload/format.el index 63b932f4e..a581e2560 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,55 @@ 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." + (when (and +format-with-lsp + (bound-and-true-p lsp-mode) + (lsp-feature? + (if (eq op 'buffer) + "textDocument/formatting" + "textDocument/rangeFormatting"))) + (call-interactively + (if (eq op 'buffer) + #'lsp-format-buffer + #'lsp-format-region)) + t)) + +;;;###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..397dc92db 100644 --- a/modules/editor/format/config.el +++ b/modules/editor/format/config.el @@ -27,6 +27,17 @@ 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) + "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 From e9ea3cc5913cb0a996158a3cc855fe94c15a2b7d Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Wed, 14 Feb 2024 17:44:38 -0500 Subject: [PATCH 09/12] feat(format): add eglot support Fix: #7673 --- modules/editor/format/autoload/format.el | 17 +++++++++++++++++ modules/editor/format/config.el | 3 ++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/modules/editor/format/autoload/format.el b/modules/editor/format/autoload/format.el index a581e2560..b341da4d1 100644 --- a/modules/editor/format/autoload/format.el +++ b/modules/editor/format/autoload/format.el @@ -107,6 +107,23 @@ the requested feature." #'lsp-format-region)) t)) +;;;###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." + (when (and +format-with-lsp + (bound-and-true-p eglot-managed-mode) + (eglot--server-capable + (if (eq op 'buffer) + :documentFormattingProvider + :documentRangeFormattingProvider))) + (if (eq op 'buffer) + (eglot-format-buffer) + (eglot-format beg end)) + t)) + ;;;###autoload (defun +format-in-org-src-blocks-fn (beg end _op) "TODO" diff --git a/modules/editor/format/config.el b/modules/editor/format/config.el index 397dc92db..0af781fb0 100644 --- a/modules/editor/format/config.el +++ b/modules/editor/format/config.el @@ -29,7 +29,8 @@ This has no effect on the +onsave flag, apheleia will always be used there.") (defvar +format-functions '(+format-in-org-src-blocks-fn - +format-with-lsp-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 From 9ba6d7191ccb964584f0e392606ff6e5ccaa73db Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Wed, 14 Feb 2024 18:09:15 -0500 Subject: [PATCH 10/12] refactor(format): improve lsp/eglot formatter dispatchers --- modules/editor/format/autoload/format.el | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/modules/editor/format/autoload/format.el b/modules/editor/format/autoload/format.el index b341da4d1..ab49403bd 100644 --- a/modules/editor/format/autoload/format.el +++ b/modules/editor/format/autoload/format.el @@ -95,17 +95,19 @@ is selected)." Does nothing if `+format-with-lsp' is nil or the active server doesn't support the requested feature." - (when (and +format-with-lsp - (bound-and-true-p lsp-mode) - (lsp-feature? - (if (eq op 'buffer) - "textDocument/formatting" - "textDocument/rangeFormatting"))) - (call-interactively - (if (eq op 'buffer) - #'lsp-format-buffer - #'lsp-format-region)) - t)) + (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) @@ -113,16 +115,14 @@ the requested feature." Does nothing if `+format-with-lsp' is nil or the active server doesn't support the requested feature." - (when (and +format-with-lsp - (bound-and-true-p eglot-managed-mode) - (eglot--server-capable - (if (eq op 'buffer) - :documentFormattingProvider - :documentRangeFormattingProvider))) - (if (eq op 'buffer) - (eglot-format-buffer) - (eglot-format beg end)) - t)) + (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) From 3acb75cb166fcd947c7f87510377fc0fec4d458e Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Thu, 15 Feb 2024 15:51:35 -0500 Subject: [PATCH 11/12] bump: :lang dart emacs-lsp/lsp-dart@e7ee6afc2e16 -> emacs-lsp/lsp-dart@f51c80f5458d Includes a fix for the lsp-dart daemon failing to start (emacs-lsp/lsp-dart#209). Ref: emacs-lsp/lsp-dart#209 Close: #7677 Co-authored-by: abdulhaq-e --- modules/lang/dart/packages.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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") From 7984cd8e0fee24738fe911365738f2fe634854bb Mon Sep 17 00:00:00 2001 From: TEC Date: Fri, 16 Feb 2024 14:45:09 +0800 Subject: [PATCH 12/12] tweak(cli): use fancier string-dist suggestion alg To improve the quality of "did you mean?"-style suggestions, shift from using Ratcliff-Obershelp similarity to the Restricted Damerau-Levenshtein string distance (also known as Optimal String Alignment). This code is a translation of a Julia implementation that I wrote a while ago: https://github.com/tecosaur/DataToolkitBase.jl/blob/v0.4.1/src/model/utils.jl#L40-L107 See https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance#Optimal_string_alignment_distance and https://en.wikipedia.org/wiki/Gestalt_pattern_matching for more information on these algorithms. --- lisp/cli/meta.el | 89 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 26 deletions(-) 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'