diff options
author | Sébastien Delafond <sdelafond@gmail.com> | 2016-11-07 10:41:54 +0100 |
---|---|---|
committer | Sébastien Delafond <sdelafond@gmail.com> | 2016-11-07 10:41:54 +0100 |
commit | ec84430cf4e09ba25ec675debdf802bc28111e06 (patch) | |
tree | 9c64bc8a0cd5e8cac82aa5fdf369d40529f140f8 /lisp/org-table.el | |
parent | 84539dca3aa301ecfe48858eceef1ced0505388b (diff) |
Imported Upstream version 9.0
Diffstat (limited to 'lisp/org-table.el')
-rw-r--r-- | lisp/org-table.el | 708 |
1 files changed, 364 insertions, 344 deletions
diff --git a/lisp/org-table.el b/lisp/org-table.el index e43f0f8..e2bbe87 100644 --- a/lisp/org-table.el +++ b/lisp/org-table.el @@ -1,4 +1,4 @@ -;;; org-table.el --- The table editor for Org mode +;;; org-table.el --- The Table Editor for Org -*- lexical-binding: t; -*- ;; Copyright (C) 2004-2016 Free Software Foundation, Inc. @@ -34,8 +34,7 @@ ;;; Code: -(eval-when-compile - (require 'cl)) +(require 'cl-lib) (require 'org) (declare-function org-element-at-point "org-element" ()) @@ -52,20 +51,26 @@ (declare-function org-element-property "org-element" (property element)) (declare-function org-element-type "org-element" (element)) -(declare-function org-export-create-backend "org-export" (&rest rest)) -(declare-function org-export-data-with-backend "org-export" (arg1 arg2 arg3)) -(declare-function org-export-first-sibling-p "org-export" (arg1 arg2)) -(declare-function org-export-get-backend "org-export" (arg1)) -(declare-function org-export-get-environment "org-export" (&optional arg1 arg2 arg3)) -(declare-function org-export-table-has-special-column-p "org-export" (arg1)) -(declare-function org-export-table-row-is-special-p "org-export" (arg1 arg2)) +(declare-function org-export-create-backend "ox" (&rest rest) t) +(declare-function org-export-data-with-backend "ox" (data backend info)) +(declare-function org-export-filter-apply-functions "ox" + (filters value info)) +(declare-function org-export-first-sibling-p "ox" (blob info)) +(declare-function org-export-get-backend "ox" (name)) +(declare-function org-export-get-environment "ox" + (&optional backend subtreep ext-plist)) +(declare-function org-export-install-filters "ox" (info)) +(declare-function org-export-table-has-special-column-p "ox" (table)) +(declare-function org-export-table-row-is-special-p "ox" (table-row info)) (declare-function calc-eval "calc" (str &optional separator &rest args)) (defvar orgtbl-mode) ; defined below (defvar orgtbl-mode-menu) ; defined when orgtbl mode get initialized (defvar constants-unit-system) +(defvar org-export-filters-alist) (defvar org-table-follow-field-mode) +(defvar sort-fold-case) (defvar orgtbl-after-send-table-hook nil "Hook for functions attaching to `C-c C-c', if the table is sent. @@ -84,7 +89,7 @@ for empty fields). Outside tables, the correct binding of the keys is restored. The default for this option is t if the optimized version is also used in -Org-mode. See the variable `org-enable-table-editor' for details. Changing +Org mode. See the variable `org-enable-table-editor' for details. Changing this variable requires a restart of Emacs to become effective." :group 'org-table :type 'boolean) @@ -139,7 +144,7 @@ table, obtained by prompting the user." (string :tag "Format")))) (defgroup org-table-settings nil - "Settings for tables in Org-mode." + "Settings for tables in Org mode." :tag "Org Table Settings" :group 'org-table) @@ -188,13 +193,13 @@ alignment to the right border applies." :type 'number) (defgroup org-table-editing nil - "Behavior of tables during editing in Org-mode." + "Behavior of tables during editing in Org mode." :tag "Org Table Editing" :group 'org-table) (defcustom org-table-automatic-realign t "Non-nil means automatically re-align table when pressing TAB or RETURN. -When nil, aligning is only done with \\[org-table-align], or after column +When nil, aligning is only done with `\\[org-table-align]', or after column removal/insertion." :group 'org-table-editing :type 'boolean) @@ -240,12 +245,12 @@ this line." :type 'boolean) (defgroup org-table-calculation nil - "Options concerning tables in Org-mode." + "Options concerning tables in Org mode." :tag "Org Table Calculation" :group 'org-table) (defcustom org-table-use-standard-references 'from - "Should org-mode work with table references like B3 instead of @3$2? + "Non-nil means using table references like B3 instead of @3$2. Possible values are: nil never use them from accept as input, do not present for editing @@ -257,9 +262,10 @@ t accept as input and present for editing" (const :tag "Convert user input, don't offer during editing" from))) (defcustom org-table-copy-increment t - "Non-nil means increment when copying current field with \\[org-table-copy-down]." + "Non-nil means increment when copying current field with \ +`\\[org-table-copy-down]'." :group 'org-table-calculation - :version "25.1" + :version "25.2" :package-version '(Org . "8.3") :type '(choice (const :tag "Use the difference between the current and the above fields" t) @@ -277,7 +283,7 @@ t accept as input and present for editing" ) "List with Calc mode settings for use in `calc-eval' for table formulas. The list must contain alternating symbols (Calc modes variables and values). -Don't remove any of the default settings, just change the values. Org-mode +Don't remove any of the default settings, just change the values. Org mode relies on the variables to be present in the list." :group 'org-table-calculation :type 'plist) @@ -311,7 +317,7 @@ which should be evaluated as described in the manual and in the documentation string of the command `org-table-eval-formula'. This feature requires the Emacs calc package. When this variable is nil, formula calculation is only available through -the command \\[org-table-eval-formula]." +the command `\\[org-table-eval-formula]'." :group 'org-table-calculation :type 'boolean) @@ -344,7 +350,7 @@ Constants can also be defined on a per-file basis using a line like (defcustom org-table-allow-automatic-line-recalculation t "Non-nil means lines marked with |#| or |*| will be recomputed automatically. \\<org-mode-map>\ -Automatically means when TAB or RET or \\[org-ctrl-c-ctrl-c] \ +Automatically means when `TAB' or `RET' or `\\[org-ctrl-c-ctrl-c]' \ are pressed in the line." :group 'org-table-calculation :type 'boolean) @@ -372,7 +378,7 @@ portability of tables." "Non-nil means that evaluation of a field formula can add new columns if an out-of-bounds field is being set." :group 'org-table-calculation - :version "25.1" + :version "25.2" :package-version '(Org . "8.3") :type '(choice (const :tag "Setting an out-of-bounds field generates an error (default)" nil) @@ -381,7 +387,7 @@ columns if an out-of-bounds field is being set." (const :tag "When setting an out-of-bounds field, the user is prompted" prompt))) (defgroup org-table-import-export nil - "Options concerning table import and export in Org-mode." + "Options concerning table import and export in Org mode." :tag "Org Table Import Export" :group 'org-table) @@ -401,7 +407,7 @@ The function can be slow on larger regions; this safety feature prevents it from hanging emacs." :group 'org-table-import-export :type 'integer - :version "25.1" + :version "25.2" :package-version '(Org . "8.3")) (defconst org-table-auto-recalculate-regexp "^[ \t]*| *# *\\(|\\|$\\)" @@ -494,13 +500,13 @@ Field is restored even in case of abnormal exit." ;;;###autoload (defun org-table-create-with-table.el () "Use the table.el package to insert a new table. -If there is already a table at point, convert between Org-mode tables +If there is already a table at point, convert between Org tables and table.el tables." (interactive) (require 'table) (cond ((org-at-table.el-p) - (if (y-or-n-p "Convert table to Org-mode table? ") + (if (y-or-n-p "Convert table to Org table? ") (org-table-convert))) ((org-at-table-p) (when (y-or-n-p "Convert table to table.el table? ") @@ -544,7 +550,7 @@ SIZE is a string Columns x Rows like for example \"3x2\"." (beginning-of-line 1) (newline)) ;; (mapcar (lambda (x) (insert line)) (make-list rows t)) - (dotimes (i rows) (insert line)) + (dotimes (_ rows) (insert line)) (goto-char pos) (if (> rows 1) ;; Insert a hline after the first row. @@ -665,7 +671,7 @@ extension of the given file name, and finally on the variable (when (file-directory-p file) (user-error "This is a directory path, not a file")) (when (and (buffer-file-name (buffer-base-buffer)) - (org-file-equal-p + (file-equal-p (file-truename file) (file-truename (buffer-file-name (buffer-base-buffer))))) (user-error "Please specify a file name that is different from current")) @@ -684,7 +690,7 @@ extension of the given file name, and finally on the variable (or (car (delq nil (mapcar (lambda (f) - (and (org-string-match-p fileext f) f)) + (and (string-match-p fileext f) f)) formats))) org-table-export-default-format) t t) t t))) @@ -723,13 +729,11 @@ This is being used to correctly align a single field after TAB or RET.") (defvar org-table-last-column-widths nil "List of max width of fields in each column. This is being used to correctly align a single field after TAB or RET.") -(defvar org-table-formula-debug nil +(defvar-local org-table-formula-debug nil "Non-nil means debug table formulas. When nil, simply write \"#ERROR\" in corrupted fields.") -(make-variable-buffer-local 'org-table-formula-debug) -(defvar org-table-overlay-coordinates nil +(defvar-local org-table-overlay-coordinates nil "Overlay coordinates after each align of a table.") -(make-variable-buffer-local 'org-table-overlay-coordinates) (defvar org-last-recalc-line nil) (defvar org-table-do-narrow t) ; for dynamic scoping @@ -753,7 +757,7 @@ When nil, simply write \"#ERROR\" in corrupted fields.") ;; Table's rows. Separators are replaced by nil. Trailing ;; spaces are also removed. (lines (mapcar (lambda (l) - (and (not (org-string-match-p "\\`[ \t]*|-" l)) + (and (not (string-match-p "\\`[ \t]*|-" l)) (let ((l (org-trim l))) (remove-text-properties 0 (length l) '(display t org-cwidth t) l) @@ -795,9 +799,8 @@ When nil, simply write \"#ERROR\" in corrupted fields.") (org-add-props x nil 'help-echo (concat - (substitute-command-keys - "Clipped table field, use \\[org-table-edit-field] to \ -edit. Full value is:\n") + "Clipped table field, use `\\[org-table-edit-field]' to \ +edit. Full value is:\n" (substring-no-properties x))) (let ((l (length x)) (f1 (min fmax @@ -811,7 +814,7 @@ edit. Full value is:\n") (if (= (org-string-width x) l) (setq f2 f1) (setq f2 1) (while (< (org-string-width (substring x 0 f2)) f1) - (incf f2))) + (cl-incf f2))) (add-text-properties f2 l (list 'org-cwidth t) x) (add-text-properties (if (>= (string-width (substring x (1- f2) f2)) 2) (1- f2) @@ -831,10 +834,10 @@ edit. Full value is:\n") (unless (equal x "") (setq frac (/ (+ (* frac cnt) - (if (org-string-match-p org-table-number-regexp x) + (if (string-match-p org-table-number-regexp x) 1 0)) - (incf cnt))))) + (cl-incf cnt))))) (push (>= frac org-table-number-fraction) typenums))))) (setq lengths (nreverse lengths)) (setq typenums (nreverse typenums)) @@ -942,41 +945,40 @@ Optional argument NEW may specify text to replace the current field content." ((and (not new) org-table-may-need-update)) ; Realignment will happen anyway ((org-at-table-hline-p)) ((and (not new) - (or (not (equal (marker-buffer org-table-aligned-begin-marker) - (current-buffer))) + (or (not (eq (marker-buffer org-table-aligned-begin-marker) + (current-buffer))) (< (point) org-table-aligned-begin-marker) (>= (point) org-table-aligned-end-marker))) - ;; This is not the same table, force a full re-align + ;; This is not the same table, force a full re-align. (setq org-table-may-need-update t)) - (t ;; realign the current field, based on previous full realign - (let* ((pos (point)) s - (col (org-table-current-column)) - (num (if (> col 0) (nth (1- col) org-table-last-alignment))) - l f n o e) + (t + ;; Realign the current field, based on previous full realign. + (let ((pos (point)) + (col (org-table-current-column))) (when (> col 0) - (skip-chars-backward "^|\n") - (if (looking-at " *\\([^|\n]*?\\) *\\(|\\|$\\)") - (progn - (setq s (match-string 1) - o (match-string 0) - l (max 1 - (- (org-string-width - (buffer-substring-no-properties - (match-end 0) (match-beginning 0))) 3)) - e (not (= (match-beginning 2) (match-end 2)))) - (setq f (format (if num " %%%ds %s" " %%-%ds %s") - l (if e "|" (setq org-table-may-need-update t) "")) - n (format f s)) - (if new - (if (<= (org-string-width new) l) - (setq n (format f new)) - (setq n (concat new "|") org-table-may-need-update t))) - (if (equal (string-to-char n) ?-) (setq n (concat " " n))) - (or (equal n o) - (let (org-table-may-need-update) - (replace-match n t t)))) - (setq org-table-may-need-update t)) - (goto-char pos)))))) + (skip-chars-backward "^|") + (if (not (looking-at " *\\([^|\n]*?\\) *\\(|\\|$\\)")) + (setq org-table-may-need-update t) + (let* ((numbers? (nth (1- col) org-table-last-alignment)) + (cell (match-string 0)) + (field (match-string 1)) + (len (max 1 (- (org-string-width cell) 3))) + (properly-closed? (/= (match-beginning 2) (match-end 2))) + (fmt (format (if numbers? " %%%ds %s" " %%-%ds %s") + len + (if properly-closed? "|" + (setq org-table-may-need-update t) + ""))) + (new-cell + (cond ((not new) (format fmt field)) + ((<= (org-string-width new) len) (format fmt new)) + (t + (setq org-table-may-need-update t) + (format " %s |" new))))) + (unless (equal new-cell cell) + (let (org-table-may-need-update) + (replace-match new-cell t t))) + (goto-char pos)))))))) ;;;###autoload (defun org-table-next-field () @@ -1161,7 +1163,7 @@ to a number. In the case of a timestamp, increment by days." (user-error "No non-empty field found") (if (and org-table-copy-increment (not (equal orig-n 0)) - (string-match "^[-+^/*0-9eE.]+$" txt) + (string-match-p "^[-+^/*0-9eE.]+$" txt) (< (string-to-number txt) 100000000)) (setq txt (calc-eval (concat txt "+" (number-to-string inc))))) (insert txt) @@ -1232,7 +1234,7 @@ Return t when the line exists, nil if it does not exist." "Blank the current table field or active region." (interactive) (org-table-check-inside-data-field) - (if (and (org-called-interactively-p 'any) (org-region-active-p)) + (if (and (called-interactively-p 'any) (org-region-active-p)) (let (org-table-clip) (org-table-cut-region (region-beginning) (region-end))) (skip-chars-backward "^|") @@ -1264,7 +1266,7 @@ is always the old value." (forward-char 1) "")) ;;;###autoload -(defun org-table-field-info (arg) +(defun org-table-field-info (_arg) "Show info about the current field, and highlight any reference at point." (interactive "P") (unless (org-at-table-p) (user-error "Not at a table")) @@ -1304,19 +1306,22 @@ is always the old value." (concat ", formula: " (org-table-formula-to-user (concat - (if (string-match "^[$@]"(car eqn)) "" "$") + (if (or (string-prefix-p "$" (car eqn)) + (string-prefix-p "@" (car eqn))) + "" + "$") (car eqn) "=" (cdr eqn)))) ""))))) (defun org-table-current-column () "Find out which column we are in." (interactive) - (when (org-called-interactively-p 'any) (org-table-check-inside-data-field)) + (when (called-interactively-p 'any) (org-table-check-inside-data-field)) (save-excursion (let ((column 0) (pos (point))) (beginning-of-line) - (while (search-forward "|" pos t) (incf column)) - (when (org-called-interactively-p 'interactive) + (while (search-forward "|" pos t) (cl-incf column)) + (when (called-interactively-p 'interactive) (message "In table column %d" column)) column))) @@ -1325,16 +1330,16 @@ is always the old value." "Find out what table data line we are in. Only data lines count for this." (interactive) - (when (org-called-interactively-p 'any) + (when (called-interactively-p 'any) (org-table-check-inside-data-field)) (save-excursion (let ((c 0) (pos (point))) (goto-char (org-table-begin)) (while (<= (point) pos) - (when (looking-at org-table-dataline-regexp) (incf c)) + (when (looking-at org-table-dataline-regexp) (cl-incf c)) (forward-line)) - (when (org-called-interactively-p 'any) + (when (called-interactively-p 'any) (message "This is table line %d" c)) c))) @@ -1552,19 +1557,21 @@ non-nil, the one above is used." "Insert a new row above the current line into the table. With prefix ARG, insert below the current line." (interactive "P") - (if (not (org-at-table-p)) - (user-error "Not at a table")) - (let* ((line (buffer-substring (point-at-bol) (point-at-eol))) + (unless (org-at-table-p) (user-error "Not at a table")) + (let* ((line (buffer-substring (line-beginning-position) (line-end-position))) (new (org-table-clean-line line))) ;; Fix the first field if necessary (if (string-match "^[ \t]*| *[#$] *|" line) (setq new (replace-match (match-string 0 line) t t new))) (beginning-of-line (if arg 2 1)) + ;; Buffer may not end of a newline character, so ensure + ;; (beginning-of-line 2) moves point to a new line. + (unless (bolp) (insert "\n")) (let (org-table-may-need-update) (insert-before-markers new "\n")) (beginning-of-line 0) - (re-search-forward "| ?" (point-at-eol) t) - (and (or org-table-may-need-update org-table-overlay-coordinates) - (org-table-align)) + (re-search-forward "| ?" (line-end-position) t) + (when (or org-table-may-need-update org-table-overlay-coordinates) + (org-table-align)) (when (or (not org-table-fix-formulas-confirm) (funcall org-table-fix-formulas-confirm "Fix formulas? ")) (org-table-fix-formulas "@" nil (1- (org-table-current-dline)) 1)))) @@ -1577,7 +1584,7 @@ With prefix ABOVE, insert above the current line." (if (not (org-at-table-p)) (user-error "Not at a table")) (when (eobp) (insert "\n") (backward-char 1)) - (if (not (string-match "|[ \t]*$" (org-current-line-string))) + (if (not (string-match-p "|[ \t]*$" (org-current-line-string))) (org-table-align)) (let ((line (org-table-clean-line (buffer-substring (point-at-bol) (point-at-eol)))) @@ -1676,7 +1683,7 @@ numeric compare based on the type of the first key in the table." ;; Set appropriate case sensitivity and column used for sorting. (let ((column (let ((c (org-table-current-column))) (cond ((> c 0) c) - ((org-called-interactively-p 'any) + ((called-interactively-p 'any) (read-number "Use column N for sorting: ")) (t 1)))) (sorting-type @@ -1711,27 +1718,27 @@ numeric compare based on the type of the first key in the table." (extract-key-from-field ;; Function to be called on the contents of the field ;; used for sorting in the current row. - (case sorting-type + (cl-case sorting-type ((?n ?N) #'string-to-number) ((?a ?A) #'org-sort-remove-invisible) ((?t ?T) (lambda (f) (cond ((string-match org-ts-regexp-both f) - (org-float-time + (float-time (org-time-string-to-time (match-string 0 f)))) ((string-match "[0-9]\\{1,2\\}:[0-9]\\{2\\}" f) (org-hh:mm-string-to-minutes f)) (t 0)))) ((?f ?F) (or getkey-func - (and (org-called-interactively-p 'any) + (and (called-interactively-p 'any) (intern (completing-read "Sort using function: " obarray #'fboundp t))) (error "Missing key extractor to sort rows"))) (t (user-error "Invalid sorting type `%c'" sorting-type)))) (predicate - (case sorting-type + (cl-case sorting-type ((?n ?N ?t ?T) #'<) ((?a ?A) #'string<) ((?f ?F) compare-func)))) @@ -1821,24 +1828,24 @@ lines." (dolist (field row) (org-table-goto-column c nil 'force) (org-table-get-field nil field) - (incf c))) + (cl-incf c))) (forward-line))) (org-table-align))) ;;;###autoload (defun org-table-convert () "Convert from `org-mode' table to table.el and back. -Obviously, this only works within limits. When an Org-mode table is -converted to table.el, all horizontal separator lines get lost, because -table.el uses these as cell boundaries and has no notion of horizontal lines. -A table.el table can be converted to an Org-mode table only if it does not -do row or column spanning. Multiline cells will become multiple cells. -Beware, Org-mode does not test if the table can be successfully converted - it -blindly applies a recipe that works for simple tables." +Obviously, this only works within limits. When an Org table is converted +to table.el, all horizontal separator lines get lost, because table.el uses +these as cell boundaries and has no notion of horizontal lines. A table.el +table can be converted to an Org table only if it does not do row or column +spanning. Multiline cells will become multiple cells. Beware, Org mode +does not test if the table can be successfully converted - it blindly +applies a recipe that works for simple tables." (interactive) (require 'table) (if (org-at-table.el-p) - ;; convert to Org-mode table + ;; convert to Org table (let ((beg (copy-marker (org-table-begin t))) (end (copy-marker (org-table-end t)))) (table-unrecognize-region beg end) @@ -1892,10 +1899,10 @@ Note that horizontal lines disappear." (let* ((table (delete 'hline (org-table-to-lisp))) (dline_old (org-table-current-line)) (col_old (org-table-current-column)) - (contents (mapcar (lambda (p) + (contents (mapcar (lambda (_) (let ((tp table)) (mapcar - (lambda (rown) + (lambda (_) (prog1 (pop (car tp)) (setq tp (cdr tp)))) @@ -1983,9 +1990,10 @@ blank, and the content is appended to the field above." ;;;###autoload (defun org-table-edit-field (arg) "Edit table field in a different window. -This is mainly useful for fields that contain hidden parts. -When called with a \\[universal-argument] prefix, just make the full field visible so that -it can be edited in place." +This is mainly useful for fields that contain hidden parts. When called +with a `\\[universal-argument]' prefix, just make the full field \ +visible so that it can be +edited in place." (interactive "P") (cond ((equal arg '(16)) @@ -2025,9 +2033,9 @@ it can be edited in place." '(invisible t org-cwidth t display t intangible t)) (goto-char p) - (org-set-local 'org-finish-function 'org-table-finish-edit-field) - (org-set-local 'org-window-configuration cw) - (org-set-local 'org-field-marker pos) + (setq-local org-finish-function 'org-table-finish-edit-field) + (setq-local org-window-configuration cw) + (setq-local org-field-marker pos) (message "Edit and finish with C-c C-c"))))) (defun org-table-finish-edit-field () @@ -2060,8 +2068,8 @@ current field. The mode exits automatically when the cursor leaves the table (but see `org-table-exit-follow-field-mode-when-leaving-table')." nil " TblFollow" nil (if org-table-follow-field-mode - (org-add-hook 'post-command-hook 'org-table-follow-fields-with-editor - 'append 'local) + (add-hook 'post-command-hook 'org-table-follow-fields-with-editor + 'append 'local) (remove-hook 'post-command-hook 'org-table-follow-fields-with-editor 'local) (let* ((buf (get-buffer "*Org Table Edit Field*")) (win (and buf (get-buffer-window buf)))) @@ -2136,11 +2144,10 @@ If NLAST is a number, only the NLAST fields will actually be summed." s diff) (format "%.0f:%02.0f:%02.0f" h m s)))) (kill-new sres) - (if (org-called-interactively-p 'interactive) - (message "%s" - (substitute-command-keys - (format "Sum of %d items: %-20s (\\[yank] will insert result into buffer)" - (length numbers) sres)))) + (when (called-interactively-p 'interactive) + (message "%s" (substitute-command-keys + (format "Sum of %d items: %-20s \ +\(\\[yank] will insert result into buffer)" (length numbers) sres)))) sres)))) (defun org-table-get-number-for-summing (s) @@ -2184,7 +2191,7 @@ with \"=\" or \":=\"." (assoc ref stored-list) (assoc scol stored-list)))) (cond (key (car ass)) - (ass (concat (if (string-match "^[0-9]+$" (car ass)) "=" ":=") + (ass (concat (if (string-match-p "^[0-9]+$" (car ass)) "=" ":=") (cdr ass)))))) (noerror nil) (t (error "No formula active for the current field"))))) @@ -2200,22 +2207,15 @@ When NAMED is non-nil, look for a named equation." (ref (format "@%d$%d" (org-table-current-dline) (org-table-current-column))) - (refass (assoc ref stored-list)) - (nameass (assoc name stored-list)) (scol (cond ((not named) (format "$%d" (org-table-current-column))) ((and name (not (string-match "\\`LR[0-9]+\\'" name))) name) (t ref))) - (dummy (and (or nameass refass) - (not named) - (not (y-or-n-p "Replace existing field formula with \ -column formula? " )) - (message "Formula not replaced"))) (name (or name ref)) (org-table-may-need-update nil) (stored (cdr (assoc scol stored-list))) (eq (cond - ((and stored equation (string-match "^ *=? *$" equation)) + ((and stored equation (string-match-p "^ *=? *$" equation)) stored) ((stringp equation) equation) @@ -2307,7 +2307,7 @@ LOCATION is a buffer position, consider the formulas there." (goto-char (org-table-end))) (let ((case-fold-search t)) (when (looking-at "\\([ \t]*\n\\)*[ \t]*#\\+TBLFM: *\\(.*\\)") - (let ((strings (org-split-string (org-match-string-no-properties 2) + (let ((strings (org-split-string (match-string-no-properties 2) " *:: *")) eq-alist seen) (dolist (string strings (nreverse eq-alist)) @@ -2319,7 +2319,7 @@ LOCATION is a buffer position, consider the formulas there." (cond ((not (match-end 2)) m) ;; Is it a column reference? - ((org-string-match-p "\\`$\\([0-9]+\\|[<>]+\\)\\'" m) m) + ((string-match-p "\\`$\\([0-9]+\\|[<>]+\\)\\'" m) m) ;; Since named columns are not possible in ;; LHS, assume this is a named field. (t (match-string 2 string))))) @@ -2386,11 +2386,8 @@ If yes, store the formula and apply it." (when (string-match "^:?=\\(.*[^=]\\)$" field) (setq named (equal (string-to-char field) ?:) eq (match-string 1 field)) - (if (or (fboundp 'calc-eval) - (equal (substring eq 0 (min 2 (length eq))) "'(")) - (org-table-eval-formula (if named '(4) nil) - (org-table-formula-from-user eq)) - (user-error "Calc does not seem to be installed, and is needed to evaluate the formula")))))) + (org-table-eval-formula (and named '(4)) + (org-table-formula-from-user eq)))))) (defvar org-recalc-commands nil "List of commands triggering the recalculation of a line. @@ -2472,7 +2469,7 @@ of the new mark." (when l1 (set-marker l1 nil)) (when l2 (set-marker l2 nil)) (set-marker l nil) - (when (org-called-interactively-p 'interactive) + (when (called-interactively-p 'interactive) (message "%s" (cdr (assoc newchar org-recalc-marks)))))) ;;;###autoload @@ -2500,7 +2497,7 @@ This function sets up the following dynamically scoped variables: (re-search-forward "^[ \t]*| *! *\\(|.*\\)" end t)) (let ((c 1)) (dolist (name (org-split-string (match-string 1) " *| *")) - (incf c) + (cl-incf c) (when (string-match "\\`[a-zA-Z][_a-zA-Z0-9]*\\'" name) (push (cons name (int-to-string c)) org-table-column-names))))) (setq org-table-column-names (nreverse org-table-column-names)) @@ -2529,13 +2526,13 @@ This function sets up the following dynamically scoped variables: (let ((fields1 (and (looking-at "^[ \t]*|[^|]*\\(|.*\\)") (org-split-string (match-string 1) " *| *"))) - (line (incf (cdr last) (count-lines (car last) (point)))) + (line (cl-incf (cdr last) (count-lines (car last) (point)))) (col 1)) (setcar last (point)) ; Update last known position. (while (and fields fields1) (let ((field (pop fields)) (v (pop fields1))) - (incf col) + (cl-incf col) (when (and (stringp field) (stringp v) (string-match "\\`[a-zA-Z][_a-zA-Z0-9]*\\'" @@ -2553,7 +2550,7 @@ This function sets up the following dynamically scoped variables: (push (if (match-end 1) 'hline 'dline) types) (if (match-end 1) (push l hlines) (push l dlines)) (forward-line) - (incf l)) + (cl-incf l)) (push 'hline types) ; Add an imaginary extra hline to the end. (setq org-table-current-line-types (apply #'vector (nreverse types))) (setq org-table-dlines (apply #'vector (cons nil (nreverse dlines)))) @@ -2640,20 +2637,18 @@ This function assumes the table is already analyzed (i.e., using suppress-store suppress-analysis) "Replace the table field value at the cursor by the result of a calculation. -This function makes use of Dave Gillespie's Calc package, in my view the -most exciting program ever written for GNU Emacs. So you need to have Calc -installed in order to use this function. - In a table, this command replaces the value in the current field with the result of a formula. It also installs the formula as the \"current\" column formula, by storing it in a special line below the table. When called -with a `C-u' prefix, the current field must be a named field, and the -formula is installed as valid in only this specific field. +with a `\\[universal-argument]' prefix the formula is installed as a \ +field formula. -When called with two `C-u' prefixes, insert the active equation -for the field back into the current field, so that it can be -edited there. This is useful in order to use \\[org-table-show-reference] -to check the referenced fields. +When called with a `\\[universal-argument] \\[universal-argument]' prefix, \ +insert the active equation for the field +back into the current field, so that it can be edited there. This is \ +useful +in order to use \\<org-table-fedit-map>`\\[org-table-show-reference]' to \ +check the referenced fields. When called, the command first prompts for a formula, which is read in the minibuffer. Previously entered formulas are available through the @@ -2662,7 +2657,7 @@ These stored formulas are adapted correctly when moving, inserting, or deleting columns with the corresponding commands. The formula can be any algebraic expression understood by the Calc package. -For details, see the Org-mode manual. +For details, see the Org mode manual. This function can also be called from Lisp programs and offers additional arguments: EQUATION can be the formula to apply. If this @@ -2672,7 +2667,8 @@ SUPPRESS-CONST suppresses the interpretation of constants in the formula, assuming that this has been done already outside the function. SUPPRESS-STORE means the formula should not be stored, either because it is already stored, or because it is a modified equation that should -not overwrite the stored one." +not overwrite the stored one. SUPPRESS-ANALYSIS prevents any call to +`org-table-analyze'." (interactive "P") (org-table-check-inside-data-field) (or suppress-analysis (org-table-analyze)) @@ -2737,9 +2733,10 @@ not overwrite the stored one." (setq fmt (replace-match "" t t fmt))) (unless (string-match "\\S-" fmt) (setq fmt nil)))) - (if (and (not suppress-const) org-table-formula-use-constants) - (setq formula (org-table-formula-substitute-names formula))) + (when (and (not suppress-const) org-table-formula-use-constants) + (setq formula (org-table-formula-substitute-names formula))) (setq orig (or (get-text-property 1 :orig-formula formula) "?")) + (setq formula (org-table-formula-handle-first/last-rc formula)) (while (> ndown 0) (setq fields (org-split-string (org-trim @@ -2823,11 +2820,12 @@ not overwrite the stored one." (replace-match (save-match-data (org-table-make-reference - (org-sublist fields - (+ (if (match-end 2) n0 0) - (string-to-number (match-string 1 form))) - (+ (if (match-end 4) n0 0) - (string-to-number (match-string 3 form)))) + (cl-subseq fields + (+ (if (match-end 2) n0 0) + (string-to-number (match-string 1 form)) + -1) + (+ (if (match-end 4) n0 0) + (string-to-number (match-string 3 form)))) keep-empty numbers lispp)) t t form))) (setq form0 form) @@ -2854,20 +2852,23 @@ not overwrite the stored one." ev (if duration (org-table-time-seconds-to-string (string-to-number ev) duration-output-format) ev)) - (or (fboundp 'calc-eval) - (user-error "Calc does not seem to be installed, and is needed to evaluate the formula")) - ;; Use <...> time-stamps so that Calc can handle them - (while (string-match (concat "\\[" org-ts-regexp1 "\\]") form) - (setq form (replace-match "<\\1>" nil nil form))) - ;; I18n-ize local time-stamps by setting (system-time-locale "C") - (when (string-match org-ts-regexp2 form) - (let* ((ts (match-string 0 form)) - (tsp (apply 'encode-time (save-match-data (org-parse-time-string ts)))) - (system-time-locale "C") - (tf (or (and (save-match-data (string-match "[0-9]\\{1,2\\}:[0-9]\\{2\\}" ts)) - (cdr org-time-stamp-formats)) - (car org-time-stamp-formats)))) - (setq form (replace-match (format-time-string tf tsp) t t form)))) + + ;; Use <...> time-stamps so that Calc can handle them. + (setq form + (replace-regexp-in-string org-ts-regexp-inactive "<\\1>" form)) + ;; Internationalize local time-stamps by setting locale to + ;; "C". + (setq form + (replace-regexp-in-string + org-ts-regexp + (lambda (ts) + (let ((system-time-locale "C")) + (format-time-string + (org-time-stamp-format + (string-match-p "[0-9]\\{1,2\\}:[0-9]\\{2\\}" ts)) + (apply #'encode-time + (save-match-data (org-parse-time-string ts)))))) + form t t)) (setq ev (if (and duration (string-match "^[0-9]+:[0-9]+\\(?::[0-9]+\\)?$" form)) form @@ -2895,7 +2896,7 @@ $1-> %s\n" orig formula form0 form)) (if fmt (format fmt (string-to-number ev)) ev))))) (setq bw (get-buffer-window "*Substitution History*")) (org-fit-window-to-buffer bw) - (unless (and (org-called-interactively-p 'any) (not ndown)) + (unless (and (called-interactively-p 'any) (not ndown)) (unless (let (inhibit-redisplay) (y-or-n-p "Debugging Formula. Continue to next? ")) (org-table-align) @@ -2931,7 +2932,7 @@ When CORNERS-ONLY is set, only return the corners of the range as a list (line1 column1 line2 column2) where line1 and line2 are line numbers relative to beginning of table, or TBEG, and column1 and column2 are table column numbers." - (let* ((desc (if (org-string-match-p "\\`\\$[0-9]+\\.\\.\\$[0-9]+\\'" desc) + (let* ((desc (if (string-match-p "\\`\\$[0-9]+\\.\\.\\$[0-9]+\\'" desc) (replace-regexp-in-string "\\$" "@0$" desc) desc)) (col (or col (org-table-current-column))) @@ -2979,7 +2980,7 @@ and column2 are table column numbers." (forward-line (- first-row thisline)) (while (not (looking-at org-table-dataline-regexp)) (forward-line) - (incf first-row)) + (cl-incf first-row)) (org-table-goto-column first-column) (let ((beg (point))) (forward-line (- last-row first-row)) @@ -3017,7 +3018,7 @@ The cursor is currently in relative line number CLINE." (when (and hn (not hdir)) (setq cline 0) (setq hdir "+") - (when (eq (aref org-table-current-line-types 0) 'hline) (decf hn))) + (when (eq (aref org-table-current-line-types 0) 'hline) (cl-decf hn))) (when (and (not hn) on (not odir)) (user-error "Should never happen")) (when hn (setq cline @@ -3036,7 +3037,7 @@ search, as a string." (let ((l (length org-table-current-line-types))) (catch :exit (dotimes (_ n) - (while (and (incf i (if backwards -1 1)) + (while (and (cl-incf i (if backwards -1 1)) (>= i 0) (< i l) (not (eq (aref org-table-current-line-types i) type)) @@ -3048,7 +3049,7 @@ search, as a string." ((eq org-table-relative-ref-may-cross-hline t)) ((eq org-table-relative-ref-may-cross-hline 'error) (user-error "Row descriptor %s crosses hline" desc)) - (t (decf i (if backwards -1 1)) ; Step back. + (t (cl-decf i (if backwards -1 1)) ; Step back. (throw :exit nil))))))) (cond ((or (< i 0) (>= i l)) (user-error "Row descriptor %s leads outside table" desc)) @@ -3126,10 +3127,13 @@ T1 is nil, always messages." ;;;###autoload (defun org-table-recalculate (&optional all noalign) "Recalculate the current table line by applying all stored formulas. + With prefix arg ALL, do this for all lines in the table. -With the prefix argument ALL is `(16)' \ -\(a double \\[universal-prefix] \\[universal-prefix] prefix), or if -it is the symbol `iterate', recompute the table until it no longer changes. + +When called with a `\\[universal-argument] \\[universal-argument]' prefix, or \ +if ALL is the symbol `iterate', +recompute the table until it no longer changes. + If NOALIGN is not nil, do not re-align the table after the computations are done. This is typically used internally to save time, if it is known that the table will be realigned a little later anyway." @@ -3172,7 +3176,7 @@ existing formula for column %s" new)) new)) (t old-lhs))))) - (if (org-string-match-p "\\`\\$[0-9]+\\'" lhs) + (if (string-match-p "\\`\\$[0-9]+\\'" lhs) (push (cons lhs rhs) eqlcol) (push (cons lhs rhs) eqlfield)))) (setq eqlcol (nreverse eqlcol)) @@ -3193,8 +3197,8 @@ existing formula for column %s" (re-search-forward org-table-hline-regexp end t) (re-search-forward org-table-dataline-regexp end t)) (setq beg (match-beginning 0))) - ;; Just leave BEG where it is. - (t (setq beg (line-beginning-position))))) + ;; Just leave BEG at the start of the table. + (t nil))) (setq beg (line-beginning-position) end (copy-marker (line-beginning-position 2)))) (goto-char beg) @@ -3233,7 +3237,7 @@ existing formula for column %s" (while (re-search-forward line-re end t) (unless (string-match "\\` *[_^!$/] *\\'" (org-table-get-field 1)) ;; Unprotected line, recalculate. - (incf cnt) + (cl-incf cnt) (when all (setq log-last-time (org-table-message-once-per-second @@ -3264,19 +3268,19 @@ existing formula for column %s" ;; `org-table-formula-create-columns' allows it. (let ((column-count (progn (end-of-line) (1- (org-table-current-column))))) - `(lambda (column) - (when (> column 1000) - (user-error "Formula column target too large")) - (and (> column ,column-count) - (or (eq org-table-formula-create-columns t) - (and (eq org-table-formula-create-columns 'warn) - (progn - (org-display-warning - "Out-of-bounds formula added columns") - t)) - (and (eq org-table-formula-create-columns 'prompt) - (yes-or-no-p - "Out-of-bounds formula. Add columns? "))))))) + (lambda (column) + (when (> column 1000) + (user-error "Formula column target too large")) + (and (> column column-count) + (or (eq org-table-formula-create-columns t) + (and (eq org-table-formula-create-columns 'warn) + (progn + (org-display-warning + "Out-of-bounds formula added columns") + t)) + (and (eq org-table-formula-create-columns 'prompt) + (yes-or-no-p + "Out-of-bounds formula. Add columns? "))))))) (org-table-eval-formula nil formula t t t t)))) ;; Clean up markers and internal text property. (remove-text-properties (point-min) (point-max) '(org-untouchable t)) @@ -3316,10 +3320,15 @@ with the prefix ARG." (defun org-table-recalculate-buffer-tables () "Recalculate all tables in the current buffer." (interactive) - (save-excursion - (save-restriction - (widen) - (org-table-map-tables (lambda () (org-table-recalculate t)) t)))) + (org-with-wide-buffer + (org-table-map-tables + (lambda () + ;; Reason for separate `org-table-align': When repeating + ;; (org-table-recalculate t) `org-table-may-need-update' gets in + ;; the way. + (org-table-recalculate t t) + (org-table-align)) + t))) ;;;###autoload (defun org-table-iterate-buffer-tables () @@ -3329,19 +3338,19 @@ with the prefix ARG." (i imax) (checksum (md5 (buffer-string))) c1) - (save-excursion - (save-restriction - (widen) - (catch 'exit - (while (> i 0) - (setq i (1- i)) - (org-table-map-tables (lambda () (org-table-recalculate t)) t) - (if (equal checksum (setq c1 (md5 (buffer-string)))) - (progn - (message "Convergence after %d iterations" (- imax i)) - (throw 'exit t)) - (setq checksum c1))) - (user-error "No convergence after %d iterations" imax)))))) + (org-with-wide-buffer + (catch 'exit + (while (> i 0) + (setq i (1- i)) + (org-table-map-tables (lambda () (org-table-recalculate t t)) t) + (if (equal checksum (setq c1 (md5 (buffer-string)))) + (progn + (org-table-map-tables #'org-table-align t) + (message "Convergence after %d iterations" (- imax i)) + (throw 'exit t)) + (setq checksum c1))) + (org-table-map-tables #'org-table-align t) + (user-error "No convergence after %d iterations" imax))))) (defun org-table-calc-current-TBLFM (&optional arg) "Apply the #+TBLFM in the line at point to the table." @@ -3385,13 +3394,13 @@ function assumes the table is already analyzed (i.e., using (let ((lhs (car e)) (rhs (cdr e))) (cond - ((org-string-match-p "\\`@-?[-+0-9]+\\$-?[0-9]+\\'" lhs) + ((string-match-p "\\`@-?[-+0-9]+\\$-?[0-9]+\\'" lhs) ;; This just refers to one fixed field. (push e res)) - ((org-string-match-p "\\`[a-zA-Z][_a-zA-Z0-9]*\\'" lhs) + ((string-match-p "\\`[a-zA-Z][_a-zA-Z0-9]*\\'" lhs) ;; This just refers to one fixed named field. (push e res)) - ((org-string-match-p "\\`\\$[0-9]+\\'" lhs) + ((string-match-p "\\`\\$[0-9]+\\'" lhs) ;; Column formulas are treated specially and are not ;; expanded. (push e res)) @@ -3407,12 +3416,12 @@ function assumes the table is already analyzed (i.e., using (c1 (nth 1 range)) (r2 (org-table-line-to-dline (nth 2 range) 'above)) (c2 (nth 3 range))) - (loop for ir from r1 to r2 do - (loop for ic from c1 to c2 do - (push - (cons (propertize (format "@%d$%d" ir ic) :orig-eqn e) - rhs) - res)))))))))) + (cl-loop for ir from r1 to r2 do + (cl-loop for ic from c1 to c2 do + (push (cons (propertize + (format "@%d$%d" ir ic) :orig-eqn e) + rhs) + res)))))))))) (defun org-table-formula-handle-first/last-rc (s) "Replace @<, @>, $<, $> with first/last row/column of the table. @@ -3438,7 +3447,7 @@ borders of the table using the @< @> $< $> makers." (- nmax len -1))) (if (or (< n 1) (> n nmax)) (user-error "Reference \"%s\" in expression \"%s\" points outside table" - (match-string 0 s) s)) + (match-string 0 s) s)) (setq start (match-beginning 0)) (setq s (replace-match (format "%s%d" (match-string 1 s) n) t t s))))) s) @@ -3447,7 +3456,7 @@ borders of the table using the @< @> $< $> makers." "Replace $const with values in string F." (let ((start 0) (pp (/= (string-to-char f) ?')) - (duration (org-string-match-p ";.*[Tt].*\\'" f)) + (duration (string-match-p ";.*[Tt].*\\'" f)) (new (replace-regexp-in-string ; Check for column names. org-table-column-name-regexp (lambda (m) @@ -3460,7 +3469,7 @@ borders of the table using the @< @> $< $> makers." "\\$\\([a-zA-Z][_a-zA-Z0-9]*\\)\\|\\(\\<remote([^)]*)\\)" new start)) (if (match-end 2) (setq start (match-end 2)) - (incf start) + (cl-incf start) ;; When a duration is expected, convert value on the fly. (let ((value (save-match-data @@ -3471,7 +3480,7 @@ borders of the table using the @< @> $< $> makers." (when value (setq new (replace-match (concat (and pp "(") value (and pp ")")) t t new)))))) - (if org-table-formula-debug (org-propertize new :orig-formula f)) new)) + (if org-table-formula-debug (propertize new :orig-formula f) new))) (defun org-table-get-constant (const) "Find the value for a parameter or constant in a formula. @@ -3567,13 +3576,13 @@ Parameters get priority." ;; Keep global-font-lock-mode from turning on font-lock-mode (let ((font-lock-global-modes '(not fundamental-mode))) (fundamental-mode)) - (org-set-local 'font-lock-global-modes (list 'not major-mode)) - (org-set-local 'org-pos pos) - (org-set-local 'org-table--fedit-source source) - (org-set-local 'org-window-configuration wc) - (org-set-local 'org-selected-window sel-win) + (setq-local font-lock-global-modes (list 'not major-mode)) + (setq-local org-pos pos) + (setq-local org-table--fedit-source source) + (setq-local org-window-configuration wc) + (setq-local org-selected-window sel-win) (use-local-map org-table-fedit-map) - (org-add-hook 'post-command-hook #'org-table-fedit-post-command t t) + (add-hook 'post-command-hook #'org-table-fedit-post-command t t) (easy-menu-add org-table-fedit-menu) (setq startline (org-current-line)) (dolist (entry eql) @@ -3597,8 +3606,7 @@ Parameters get priority." (when (eq org-table-use-standard-references t) (org-table-fedit-toggle-ref-type)) (org-goto-line startline) - (message - (substitute-command-keys "\\<org-mode-map>\ + (message "%s" (substitute-command-keys "\\<org-mode-map>\ Edit formulas, finish with `\\[org-ctrl-c-ctrl-c]' or `\\[org-edit-special]'. \ See menu for more commands."))))) @@ -3731,7 +3739,7 @@ minutes or seconds." (format "%.1f" (/ (float secs0) 60))) ((eq output-format 'seconds) (format "%d" secs0)) - (t (org-format-seconds "%.2h:%.2m:%.2s" secs0))))) + (t (format-seconds "%.2h:%.2m:%.2s" secs0))))) (if (< secs 0) (concat "-" res) res))) (defun org-table-fedit-convert-buffer (function) @@ -3748,7 +3756,7 @@ minutes or seconds." (defun org-table-fedit-toggle-ref-type () "Convert all references in the buffer from B3 to @3$2 and back." (interactive) - (org-set-local 'org-table-buffer-is-an (not org-table-buffer-is-an)) + (setq-local org-table-buffer-is-an (not org-table-buffer-is-an)) (org-table-fedit-convert-buffer (if org-table-buffer-is-an 'org-table-convert-refs-to-an 'org-table-convert-refs-to-rc)) @@ -3961,8 +3969,8 @@ When LOCAL is non-nil, show references for the table at point." (when (and match (not (equal (match-beginning 0) (point-at-bol)))) (org-table-add-rectangle-overlay (match-beginning 0) (match-end 0) 'secondary-selection)) - (org-add-hook 'before-change-functions - #'org-table-remove-rectangle-highlight) + (add-hook 'before-change-functions + #'org-table-remove-rectangle-highlight) (when (eq what 'name) (setq var (substring match 1))) (when (eq what 'range) (unless (eq (string-to-char match) ?@) (setq match (concat "@" match))) @@ -3991,10 +3999,10 @@ When LOCAL is non-nil, show references for the table at point." (when dest (setq name (substring dest 1)) (cond - ((org-string-match-p "\\`\\$[a-zA-Z][a-zA-Z0-9]*" dest) + ((string-match-p "\\`\\$[a-zA-Z][a-zA-Z0-9]*" dest) (org-table-goto-field dest)) - ((org-string-match-p "\\`@\\([1-9][0-9]*\\)\\$\\([1-9][0-9]*\\)\\'" - dest) + ((string-match-p "\\`@\\([1-9][0-9]*\\)\\$\\([1-9][0-9]*\\)\\'" + dest) (org-table-goto-field dest)) (t (org-table-goto-column (string-to-number name)))) (move-marker pos (point)) @@ -4148,16 +4156,15 @@ FACE, when non-nil, for the highlight." (goto-char (car start-coordinates))) (add-hook 'before-change-functions #'org-table-remove-rectangle-highlight)) -(defun org-table-remove-rectangle-highlight (&rest ignore) +(defun org-table-remove-rectangle-highlight (&rest _ignore) "Remove the rectangle overlays." (unless org-inhibit-highlight-removal (remove-hook 'before-change-functions 'org-table-remove-rectangle-highlight) (mapc 'delete-overlay org-table-rectangle-overlays) (setq org-table-rectangle-overlays nil))) -(defvar org-table-coordinate-overlays nil +(defvar-local org-table-coordinate-overlays nil "Collects the coordinate grid overlays, so that they can be removed.") -(make-variable-buffer-local 'org-table-coordinate-overlays) (defun org-table-overlay-coordinates () "Add overlays to the table at point, to show row/column coordinates." @@ -4212,19 +4219,20 @@ FACE, when non-nil, for the highlight." ;;; The orgtbl minor mode ;; Define a minor mode which can be used in other modes in order to -;; integrate the org-mode table editor. - -;; This is really a hack, because the org-mode table editor uses several -;; keys which normally belong to the major mode, for example the TAB and -;; RET keys. Here is how it works: The minor mode defines all the keys -;; necessary to operate the table editor, but wraps the commands into a -;; function which tests if the cursor is currently inside a table. If that -;; is the case, the table editor command is executed. However, when any of -;; those keys is used outside a table, the function uses `key-binding' to -;; look up if the key has an associated command in another currently active -;; keymap (minor modes, major mode, global), and executes that command. -;; There might be problems if any of the keys used by the table editor is -;; otherwise used as a prefix key. +;; integrate the Org table editor. + +;; This is really a hack, because the Org table editor uses several +;; keys which normally belong to the major mode, for example the TAB +;; and RET keys. Here is how it works: The minor mode defines all the +;; keys necessary to operate the table editor, but wraps the commands +;; into a function which tests if the cursor is currently inside +;; a table. If that is the case, the table editor command is +;; executed. However, when any of those keys is used outside a table, +;; the function uses `key-binding' to look up if the key has an +;; associated command in another currently active keymap (minor modes, +;; major mode, global), and executes that command. There might be +;; problems if any of the keys used by the table editor is otherwise +;; used as a prefix key. ;; Another challenge is that the key binding for TAB can be tab or \C-i, ;; likewise the binding for RET can be return or \C-m. Orgtbl-mode @@ -4274,16 +4282,16 @@ FACE, when non-nil, for the highlight." ;; FIXME: maybe it should use emulation-mode-map-alists? (and c (setq minor-mode-map-alist (cons c (delq c minor-mode-map-alist))))) - (org-set-local (quote org-table-may-need-update) t) - (org-add-hook 'before-change-functions 'org-before-change-function - nil 'local) - (org-set-local 'org-old-auto-fill-inhibit-regexp - auto-fill-inhibit-regexp) - (org-set-local 'auto-fill-inhibit-regexp - (if auto-fill-inhibit-regexp - (concat orgtbl-line-start-regexp "\\|" - auto-fill-inhibit-regexp) - orgtbl-line-start-regexp)) + (setq-local org-table-may-need-update t) + (add-hook 'before-change-functions 'org-before-change-function + nil 'local) + (setq-local org-old-auto-fill-inhibit-regexp + auto-fill-inhibit-regexp) + (setq-local auto-fill-inhibit-regexp + (if auto-fill-inhibit-regexp + (concat orgtbl-line-start-regexp "\\|" + auto-fill-inhibit-regexp) + orgtbl-line-start-regexp)) (add-to-invisibility-spec '(org-cwidth)) (when (fboundp 'font-lock-add-keywords) (font-lock-add-keywords nil orgtbl-extra-font-lock-keywords) @@ -4383,27 +4391,26 @@ to execute outside of tables." cmd (orgtbl-make-binding fun nfunc key)) (org-defkey orgtbl-mode-map key cmd)) - ;; Special treatment needed for TAB and RET + ;; Special treatment needed for TAB, RET and DEL (org-defkey orgtbl-mode-map [(return)] (orgtbl-make-binding 'orgtbl-ret 100 [(return)] "\C-m")) (org-defkey orgtbl-mode-map "\C-m" (orgtbl-make-binding 'orgtbl-ret 101 "\C-m" [(return)])) - (org-defkey orgtbl-mode-map [(tab)] (orgtbl-make-binding 'orgtbl-tab 102 [(tab)] "\C-i")) (org-defkey orgtbl-mode-map "\C-i" (orgtbl-make-binding 'orgtbl-tab 103 "\C-i" [(tab)])) - (org-defkey orgtbl-mode-map [(shift tab)] (orgtbl-make-binding 'org-table-previous-field 104 [(shift tab)] [(tab)] "\C-i")) + (org-defkey orgtbl-mode-map [backspace] + (orgtbl-make-binding 'org-delete-backward-char 109 + [backspace] (kbd "DEL"))) - - (unless (featurep 'xemacs) - (org-defkey orgtbl-mode-map [S-iso-lefttab] - (orgtbl-make-binding 'org-table-previous-field 107 - [S-iso-lefttab] [backtab] [(shift tab)] - [(tab)] "\C-i"))) + (org-defkey orgtbl-mode-map [S-iso-lefttab] + (orgtbl-make-binding 'org-table-previous-field 107 + [S-iso-lefttab] [backtab] [(shift tab)] + [(tab)] "\C-i")) (org-defkey orgtbl-mode-map [backtab] (orgtbl-make-binding 'org-table-previous-field 108 @@ -4522,7 +4529,7 @@ With prefix arg, also recompute table." (t (let (orgtbl-mode) (call-interactively (key-binding "\C-c\C-c"))))))) -(defun orgtbl-create-or-convert-from-region (arg) +(defun orgtbl-create-or-convert-from-region (_arg) "Create table or convert region to table, if no conflicting binding. This installs the table binding `C-c |', but only if there is no conflicting binding to this key outside orgtbl-mode." @@ -4566,11 +4573,9 @@ overwritten, and the table is not marked as requiring realignment." (org-table-blank-field)) t) (eq N 1) - (looking-at "[^|\n]* +|")) + (looking-at "[^|\n]* \\( \\)|")) (let (org-table-may-need-update) - (goto-char (1- (match-end 0))) - (org-delete-backward-char 1) - (goto-char (match-beginning 0)) + (delete-region (match-beginning 1) (match-end 1)) (self-insert-command N)) (setq org-table-may-need-update t) (let* (orgtbl-mode @@ -4616,20 +4621,24 @@ a radio table." (beginning-of-line 0))) rtn))) -(defun orgtbl-send-replace-tbl (name txt) - "Find and replace table NAME with TXT." +(defun orgtbl-send-replace-tbl (name text) + "Find and replace table NAME with TEXT." (save-excursion (goto-char (point-min)) - (unless (re-search-forward - (concat "BEGIN +RECEIVE +ORGTBL +" name "\\([ \t]\\|$\\)") nil t) - (user-error "Don't know where to insert translated table")) - (let ((beg (line-beginning-position 2))) - (unless (re-search-forward - (concat "END +RECEIVE +ORGTBL +" name) nil t) - (user-error "Cannot find end of insertion region")) - (beginning-of-line) - (delete-region beg (point))) - (insert txt "\n"))) + (let* ((location-flag nil) + (name (regexp-quote name)) + (begin-re (format "BEGIN +RECEIVE +ORGTBL +%s\\([ \t]\\|$\\)" name)) + (end-re (format "END +RECEIVE +ORGTBL +%s\\([ \t]\\|$\\)" name))) + (while (re-search-forward begin-re nil t) + (unless location-flag (setq location-flag t)) + (let ((beg (line-beginning-position 2))) + (unless (re-search-forward end-re nil t) + (user-error "Cannot find end of receiver location at %d" beg)) + (beginning-of-line) + (delete-region beg (point)) + (insert text "\n"))) + (unless location-flag + (user-error "No valid receiver location found in the buffer"))))) ;;;###autoload (defun org-table-to-lisp (&optional txt) @@ -4654,7 +4663,7 @@ for this table." (catch 'exit (unless (org-at-table-p) (user-error "Not at a table")) ;; when non-interactive, we assume align has just happened. - (when (org-called-interactively-p 'any) (org-table-align)) + (when (called-interactively-p 'any) (org-table-align)) (let ((dests (orgtbl-gather-send-defs)) (table (org-table-to-lisp (buffer-substring-no-properties (org-table-begin) @@ -4670,7 +4679,7 @@ for this table." (unless (fboundp transform) (user-error "No such transformation function %s" transform)) (orgtbl-send-replace-tbl name (funcall transform table params))) - (incf ntbl)) + (cl-incf ntbl)) (message "Table converted and installed at %d receiver location%s" ntbl (if (> ntbl 1) "s" "")) (and (> ntbl 0) ntbl)))) @@ -4806,7 +4815,7 @@ strings, or the current cell) returning a string: a property list with column numbers and format strings, or functions, e.g., - \(:fmt (2 \"$%s$\" 4 (lambda (c) (format \"$%s$\" c)))) + (:fmt (2 \"$%s$\" 4 (lambda (c) (format \"$%s$\" c)))) :hlstart :hllstart :hlend :hllend :hsep :hlfmt :hllfmt :hfmt @@ -4852,10 +4861,21 @@ This may be either a string or a function of two arguments: ((consp e) (princ "| ") (dolist (c e) (princ c) (princ " |")) (princ "\n"))))) + ;; Add back-end specific filters, but not user-defined ones. In + ;; particular, make sure to call parse-tree filters on the + ;; table. + (setq info + (let ((org-export-filters-alist nil)) + (org-export-install-filters + (org-combine-plists + (org-export-get-environment backend nil params) + `(:back-end ,(org-export-get-backend backend)))))) (setq data - (org-element-map (org-element-parse-buffer) 'table - #'identity nil t)) - (setq info (org-export-get-environment backend nil params))) + (org-export-filter-apply-functions + (plist-get info :filter-parse-tree) + (org-element-map (org-element-parse-buffer) 'table + #'identity nil t) + info))) (when (and backend (symbolp backend) (not (org-export-get-backend backend))) (user-error "Unknown :backend value")) (when (or (not backend) (plist-get info :raw)) (require 'ox-org)) @@ -4868,7 +4888,7 @@ This may be either a string or a function of two arguments: (lambda (row) (if (>= n skip) t (org-element-extract-element row) - (incf n) + (cl-incf n) nil)) nil t)))) ;; Handle :skipcols parameter. @@ -4885,7 +4905,7 @@ This may be either a string or a function of two arguments: (org-element-contents row))) (when (memq c skipcols) (org-element-extract-element cell)) - (incf c)))))))))) + (cl-incf c)))))))))) ;; Since we are going to export using a low-level mechanism, ;; ignore special column and special rows manually. (let ((special? (org-export-table-has-special-column-p data)) @@ -4898,9 +4918,9 @@ This may be either a string or a function of two arguments: (push datum ignore)))) (setq info (plist-put info :ignore-list ignore))) ;; We use a low-level mechanism to export DATA so as to skip all - ;; usual pre-processing and post-processing, i.e., hooks, filters, - ;; Babel code evaluation, include keywords and macro expansion, - ;; and filters. + ;; usual pre-processing and post-processing, i.e., hooks, Babel + ;; code evaluation, include keywords and macro expansion. Only + ;; back-end specific filters are retained. (let ((output (org-export-data-with-backend data custom-backend info))) ;; Remove final newline. (if (org-string-nw-p output) (substring-no-properties output 0 -1) "")))) @@ -5189,7 +5209,7 @@ supported. It is also possible to use the following one: params))) (columns (let ((w (plist-get params :columns))) (cond ((not w) nil) - ((org-string-match-p "{\\|@columnfractions " w) w) + ((string-match-p "{\\|@columnfractions " w) w) (t (concat "@columnfractions " w)))))) (if (not columns) output (replace-regexp-in-string @@ -5255,7 +5275,7 @@ supported. It is also possible to use the following ones: params))) ;; Put the cursor in a column containing numerical values -;; of an Org-Mode table, +;; of an Org table, ;; type C-c " a ;; A new column is added with a bar plot. ;; When the table is refreshed (C-u C-c *), @@ -5263,35 +5283,38 @@ supported. It is also possible to use the following ones: (defun orgtbl-ascii-draw (value min max &optional width characters) "Draw an ascii bar in a table. -VALUE is a the value to plot, the width of the bar to draw. A -value equal to MIN will be displayed as empty (zero width bar). -A value equal to MAX will draw a bar filling all the WIDTH. -WIDTH is the expected width in characters of the column. -CHARACTERS is a string that will compose the bar, with shades of -grey from pure white to pure black. It defaults to a 10 -characters string of regular ascii characters." - (let* ((characters (or characters " .:;c!lhVHW")) - (width (or width 12)) - (value (if (numberp value) value (string-to-number value))) - (value (* (/ (- (+ value 0.0) min) (- max min)) width))) - (cond - ((< value 0) "too small") - ((> value width) "too large") - (t - (let ((len (1- (length characters)))) - (concat - (make-string (floor value) (elt characters len)) - (string (elt characters - (floor (* (- value (floor value)) len)))))))))) +VALUE is the value to plot, it determines the width of the bar to draw. +MIN is the value that will be displayed as empty (zero width bar). +MAX is the value that will draw a bar filling all the WIDTH. +WIDTH is the span in characters from MIN to MAX. +CHARACTERS is a string that will compose the bar, with shades of grey +from pure white to pure black. It defaults to a 10 characters string +of regular ascii characters." + (let* ((width (ceiling (or width 12))) + (characters (or characters " .:;c!lhVHW")) + (len (1- (length characters))) + (value (float (if (numberp value) + value (string-to-number value)))) + (relative (/ (- value min) (- max min))) + (steps (round (* relative width len)))) + (cond ((< steps 0) "too small") + ((> steps (* width len)) "too large") + (t (let* ((int-division (/ steps len)) + (remainder (- steps (* int-division len)))) + (concat (make-string int-division (elt characters len)) + (string (elt characters remainder)))))))) ;;;###autoload (defun orgtbl-ascii-plot (&optional ask) - "Draw an ascii bar plot in a column. -With cursor in a column containing numerical values, this -function will draw a plot in a new column. + "Draw an ASCII bar plot in a column. + +With cursor in a column containing numerical values, this function +will draw a plot in a new column. + ASK, if given, is a numeric prefix to override the default 12 -characters width of the plot. ASK may also be the -\\[universal-argument] prefix, which will prompt for the width." +characters width of the plot. ASK may also be the `\\[universal-argument]' \ +prefix, +which will prompt for the width." (interactive "P") (let ((col (org-table-current-column)) (min 1e999) ; 1e999 will be converted to infinity @@ -5430,15 +5453,15 @@ distinguished from a plain table name or ID." (save-match-data (let ((eq (org-table-formula-handle-first/last-rc (match-string 1 m)))) (org-table-get-range - (if (org-string-match-p "\\`\\$[0-9]+\\'" eq) + (if (string-match-p "\\`\\$[0-9]+\\'" eq) (concat "@0" eq) eq))))) form t t 1))) (defmacro org-define-lookup-function (mode) (let ((mode-str (symbol-name mode)) - (first-p (equal mode 'first)) - (all-p (equal mode 'all))) + (first-p (eq mode 'first)) + (all-p (eq mode 'all))) (let ((plural-str (if all-p "s" ""))) `(defun ,(intern (format "org-lookup-%s" mode-str)) (val s-list r-list &optional predicate) ,(format "Find %s occurrence%s of VAL in S-LIST; return corresponding element%s of R-LIST. @@ -5451,16 +5474,13 @@ This function is generated by a call to the macro `org-define-lookup-function'." (sl s-list) (rl (or r-list s-list)) (ret nil)))) - (if first-p (add-to-list 'lvars '(match-p nil))) - lvars) + (if first-p (cons '(match-p nil) lvars) lvars)) (while ,(if first-p '(and (not match-p) sl) 'sl) - (progn - (if (funcall p val (car sl)) - (progn - ,(if first-p '(setq match-p t)) - (let ((rval (car rl))) - (setq ret ,(if all-p '(append ret (list rval)) 'rval))))) - (setq sl (cdr sl) rl (cdr rl)))) + (when (funcall p val (car sl)) + ,(when first-p '(setq match-p t)) + (let ((rval (car rl))) + (setq ret ,(if all-p '(append ret (list rval)) 'rval)))) + (setq sl (cdr sl) rl (cdr rl))) ret))))) (org-define-lookup-function first) |