From a60566d046553d57da9bf1d00515acf71f73b502 Mon Sep 17 00:00:00 2001 From: Daneel Date: Mon, 23 Feb 2026 15:23:51 +0100 Subject: [PATCH] fix(org-latex): rewrite tabularx filter using with-temp-buffer + replace-match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Emacs 31 changed behavior of replace-regexp-in-string with lambda replacements containing backslashes: 'Invalid use of backslash in replacement text' error. Fix: use with-temp-buffer + re-search-forward + replace-match (literal t t). replace-match with LITERAL=t never processes backslash sequences. Also fixes \end{tabular} → \end{tabularx} regex to not match existing tabularx. --- config.el | 47 +++++++++++++++++++---------------------------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/config.el b/config.el index cf2bbc9..7b09603 100644 --- a/config.el +++ b/config.el @@ -326,41 +326,32 @@ Bound to cmd+v in org-mode and markdown-mode." ;;; ============================================================ ;; LaTeX table export: tabular → tabularx{\linewidth} with lYYY column spec. -;; Filter on rendered LaTeX string — no buffer modification, no recursion. +;; Uses with-temp-buffer + replace-match (literal t t) — avoids backslash issues +;; in replace-regexp-in-string lambda replacements on Emacs 31. ;; Uses Y column type (defined in document.org template: RaggedRight + auto-width X). -;; Handles: -;; \begin{tabular}{lll} → \begin{tabularx}{\linewidth}{lYY} -;; \begin{tabularx}{lll} → \begin{tabularx}{\linewidth}{lYY} (missing width fix) (defun my/org-latex-col-to-lyyy (spec) - "Convert tabular column SPEC to tabularx lYYY format. -Counts l/r/c columns; first → l, rest → Y (auto-width, defined in template)." - (let* ((ncols (length (replace-regexp-in-string "[^lrcLRCpP]" "" spec))) - (ncols (max 1 ncols))) + "Convert tabular column SPEC to lYYY: first col l, rest Y (auto-width)." + (let ((ncols (max 1 (length (replace-regexp-in-string "[^lrcLRCpP]" "" spec))))) (if (= ncols 1) "Y" (concat "l" (make-string (1- ncols) ?Y))))) (defun my/org-latex-fix-tabularx (table _backend _info) - "Convert tabular → tabularx{\\linewidth}{lYYY} in LaTeX table output." + "Convert tabular/tabularx → tabularx{\\linewidth}{lYYY} in LaTeX output." (when (stringp table) - ;; Fix tabularx{colspec} missing width (some org versions omit \linewidth) - (setq table - (replace-regexp-in-string - "\\\\begin{tabularx}{\\([^\\\\][^}]*\\)}" - (lambda (m) - (format "\\begin{tabularx}{\\linewidth}{%s}" - (my/org-latex-col-to-lyyy (match-string 1 m)))) - table)) - ;; Convert tabular{colspec} → tabularx{\linewidth}{lYYY} - (setq table - (replace-regexp-in-string - "\\\\begin{tabular}{\\([^}]*\\)}" - (lambda (m) - (format "\\begin{tabularx}{\\linewidth}{%s}" - (my/org-latex-col-to-lyyy (match-string 1 m)))) - table)) - (setq table - (replace-regexp-in-string "\\\\end{tabular}" "\\\\end{tabularx}" table))) - table) + (with-temp-buffer + (insert table) + (goto-char (point-min)) + ;; \begin{tabular}{spec} or \begin{tabularx}{spec} → \begin{tabularx}{\linewidth}{lYYY} + (while (re-search-forward "\\\\begin{tabular[x]?}{\\([^}]*\\)}" nil t) + (let* ((spec (match-string 1)) + (new-spec (my/org-latex-col-to-lyyy spec)) + (repl (concat "\\begin{tabularx}{\\linewidth}{" new-spec "}"))) + (replace-match repl t t))) + ;; \end{tabular} → \end{tabularx} (skip if already tabularx) + (goto-char (point-min)) + (while (re-search-forward "\\\\end{tabular}" nil t) + (replace-match "\\end{tabularx}" t t)) + (buffer-string)))) ;; Register filter on ox-latex load AND ensure it via a pre-processing hook ;; (belt+suspenders: whichever fires first wins, both are idempotent).