I have multiple tables like below in an org file, but all having identical #+TBLFM lines. Is it possible to have just one #+TBLFM and have all these tables use it?

|  Rate | Units |  Amount |  Charges | Time             |
| 170.3 |    -6 | -1021.8 | -0.86853 | [2018-05-28 Mon] |
| 174.1 |     6 |  1044.6 | -0.88791 | [2018-05-31 Thu] |
| Total |     0 |    22.8 | -1.75644 |                  |
#+TBLFM: $3=$1*$2

(The original #+TBLFM has a large number of simple formulae. Currently if I have to change it, I copy and yank to every place.)

  • AFAIK, unfortunately no.
    – NickD
    Jun 3 '18 at 3:53

With the help of the lisp code at the end of this answer you can define a file-local list org-table-buffer-local-formulas of replacements within org-table formulas which are active during the command org-table-recalculate. Each replacement is a cons (REGEXP . NEWTEXT) of a regular expression string REGEXP that should match the string you want to replace and the new text string NEWTEXT that replaces the match.

The following example demonstrates how you can define org-table-buffer-local-formulas and how you can use the replacements in org table formulas.

* Section with tables
You can use the replacements in ~org-table-buffer-local-formulas~
in org table formulas:

| first column | second column | third column |
|            1 |             2 |            4 |
|            2 |             4 |            8 |

A second table with the same formula:
|              |               |              |
| first column | second column | third column |
|            3 |             6 |           12 |
|            5 |            10 |           20 |

The third table uses another formula from the list:
|              |               |              |
| first column | second column | third column |
|            3 |             9 |           36 |
|            5 |            15 |           60 |

* Local Variables :noexport:
Local Variables:
org-table-buffer-local-formulas: (("{TBLFM:FIRST}" . "$2=2*$1::$3=2*$2") ("{TBLFM:SECOND}" . "$2=3*$1::$3=4*$2"))

There follows the lisp code you need to make the above example work. You can install that lisp code in your init file.

(defun cmdbufmod-prepare (bufmod-list &optional start bound)
  "Prepare buffer for `cmdbufmod' with modifications BUFMOD-LIST.
See `cmdbufmod' for the format of BUFMOD-LIST.
If START is a buffer position the search for the regular expressions in BUFMOD-LIST
starts there. Otherwise it starts at `point-min'.
Optional BOUND limits the search.
Return the list of original text sections.
Each text section is a cons of an insertion marker and the old text
that needs to be restored there."
  (unless start (setq start (point-min)))
  (let (original-list)
      (dolist (bufmod bufmod-list)
    (let ((place (car bufmod))
          (newtext (cdr bufmod)))
      (goto-char start)
      (while (if (functionp place)
               (funcall place bound)
              (re-search-forward place bound t))
        (setq original-list
          (cons (cons (set-marker (make-marker) (match-beginning 0))
                  (match-string 0))
        (replace-match (propertize (if (functionp newtext)
                       (funcall newtext)
                       'cmdbufmod t))))))

(defun cmdbufmod-cleanup (original-list)
  "Restore original text sections from ORIGINAL-LIST.
See the return value of `cmdbufmod-prepare' for the structure of ORIGINAL-LIST."
  (cl-loop for interval being the intervals property 'cmdbufmod
       if (get-text-property (car interval) 'cmdbufmod)
       do (delete-region (car interval) (cdr interval)))
  (cl-loop for original in original-list do
       (goto-char (car original))
       (insert (cdr original))))

(defun cmdbufmod (bufmod-list fun &rest args)
  "After applying BUFMOD-LIST to current buffer run FUN with ARGS like `apply'.
BUFMOD is a list of buffer modifications. Each buffer modification
is a cons \(PLACE . NEWTEXT).
PLACE can be a regular expression or a function.
If PLACE is a function it should search for the next place to be replaced
starting at point. It gets the search bound as an argument,
should set match-data like `re-search-forward',
and return non-nil if a match is found.
If PLACE is a regular expression it is treated like the function
\(lambda () (re-search-forward PLACE nil t))

NEWTEXT can be a replacement string or a function.
A function should return the string for `replace-match'."
  (let (original-list)
      (setq original-list (cmdbufmod-prepare bufmod-list))
      (apply fun args))
      (cmdbufmod-cleanup original-list))))

(defvar-local org-table-buffer-local-formulas nil
  "List of buffer local table formulas.
Each element is a cons of a regexp and a formula (sequence).
The text matching the regexp is replaced by the formula sequence
when the table is iterated.")

(defun org-table-get-stored-formulas-buf-loc (oldfun &rest args)
  "Replace buffer local table formulas when calling OLDFUN with ARGS."
  (cmdbufmod org-table-buffer-local-formulas oldfun args))

(advice-add 'org-table-recalculate :around #'org-table-get-stored-formulas-buf-loc)
  • This works for me. Thanks. It will be useful to add that 'M-x normal-mode' can be used to reload local variables after editing. Jun 10 '18 at 15:02
  • @KarthikeyaKameswaran You can make hack-local-variables interactive and use that instead of normal-mode for re-reading local variables: (defun hack-local-variables-make-interactive (&rest _args) "Just add the 'interactive'-specification to 'hack-local-variables'." (interactive)) (advice-add 'hack-local-variables :before #'hack-local-variables-make-interactive)
    – Tobias
    Jun 10 '18 at 20:59
  • @KarthikeyaKameswaran Is your question still open or can you accept my answer? I want to avoid that this question is repeatedly bumped to the emacs.sx homepage.
    – Tobias
    Jun 28 '18 at 14:13
  • Sorry for the trouble. I upvoted instead of accepting. Still learning sx. Thanks. Jun 30 '18 at 4:04

Not possible. However, you could write a babel src block which takes such tables as input and outputs what you want, including another table but not necessarily so. You could then invoke the babel block using call_XXX(tablename).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.