Org-mode имеет ограниченную возможность автоматического пересчета таблиц : первый столбец можно использовать для добавления специального символа (#
), который выполняет то, что вы хотите, но только когда TAB
, S-TAB
или RET
вводятся в то время, как в строке, которую вы хотите пересчитать (обратите внимание, что это также добавляет эту строку к глобальному пересчету для буфера, что также может быть полезно).
Проблема в том, что режим org не знает, когда ему следует пересчитать таблицу, если вы не скажете это (например, с помощью команды C-c *
(org-ctrl-c-star
)). Одним из вариантов может быть повторное связывание некоторых или всех комбинаций клавиш перемещения в org-mode
, чтобы увидеть, начался ли курсор и вышел ли он из записи таблицы, и если да, то пересчитать всю таблицу, но я бы рекомендовал не делать этого.
На мой взгляд, лучшее и более простое решение состоит в том, чтобы в режиме org автоматически обновлять таблицу при вставке новой строки (но не обязательно при каждом изменении записи). Вот функция Emacs Lisp (my-org-table-insert-row-and-recalculate-table
), которая создает новую строку над текущей, запрашивая пользователя для каждого столбца, а затем пересчитывает все формулы для таблицы. Несколько замечаний об этой функции:
- Эту функцию необходимо запустить в первой строке, к которой применяется формула, или под ней, а также над последней строкой, к которой применяется формула. Если нет, формула может не обновляться, как вы ожидаете (например, используемая нижняя строка может выпасть из диапазона формулы). Это основано на том, как в режиме org обновляются формулы для вставленных строк.
- Если какие-либо формулы являются недопустимыми, процесс обновления может не завершиться (например, формула
#+TBLFM: $2=vsum(@2..@-1)
не будет работать для любой вставки, поскольку режим org пытается применить ее к строке 1, столбцу 2, для которого ссылка @-1
неверно).
- Если вы обновите запись в таблице вручную, вам все равно придется вручную запускать процесс пересчета для формул таблицы.
(require 'subr-x)
(defun remove-empty-strings-list (list)
(if (null list)
'()
(let ((rest (remove-empty-strings-list (cdr list))))
(if (string= (car list) "")
rest
(cons (car list) rest)))))
(defun my-org-table--get-row-as-list ()
(unless (org-at-table-p) (user-error "Not at a table"))
(let ((line (buffer-substring-no-properties (line-beginning-position) (line-end-position))))
(mapcar 'string-trim (remove-empty-strings-list (split-string line "|")))))
(defun my-org-table--get-top-row-as-list ()
(save-excursion
(org-table-goto-line 1)
(my-org-table--get-row-as-list)))
(defun my-org-table--insert-string-row-and-recalculate-table (string)
(org-table-with-shrunk-columns
(beginning-of-line 1)
(insert-before-markers string "\n")
(org-table-align)
(org-table-fix-formulas "@" nil (1- (org-table-current-dline)) 1)
(org-table-iterate)))
(defun my-org-table--prompt-row ()
(unless (org-at-table-p) (user-error "Not at a table"))
(let ((top-row-list (my-org-table--get-top-row-as-list))
(new-str "|")
(cur-col 1))
(dolist (top-item top-row-list)
(let ((new-entry
(read-string (concat "Enter entry (column #"
(number-to-string cur-col)
" - first entry: "
top-item
"): "))))
(setq new-str (concat new-str new-entry "|")) ;; test placement
(setq cur-col (+ 1 cur-col))))
new-str))
(defun my-org-table-insert-row-and-recalculate-table ()
"Interactively inserts row above point and then recalculates table"
(interactive)
(unless (org-at-table-p) (user-error "Not at a table"))
(my-org-table--insert-string-row-and-recalculate-table (my-org-table--prompt-row)))
Пример использования («!!» обозначает точку в буфере, а «>>» - приглашение минибуфера):
| Element | Price |
| first | 1 |
| second | 2 |!!
| Total | 3 |
#+TBLFM: @4$2=vsum(@2..@-1)
Выполнить M-x my-org-table-insert-row-and-recalculate-table
.
>> Enter entry (column #1 - first entry: Element):
inserted
>> Enter entry (column #2 - first entry: Price):
100
Выход:
| Element | Price |
| first | 1 |
| inserted | 100 |
| second | 2 |
| Total | 103 |
#+TBLFM: @5$2=vsum(@2..@-1)