diff options
Diffstat (limited to 'lisp/textmodes/org.el')
-rw-r--r-- | lisp/textmodes/org.el | 1082 |
1 files changed, 696 insertions, 386 deletions
diff --git a/lisp/textmodes/org.el b/lisp/textmodes/org.el index 1709b1554a5..6150ac6cccd 100644 --- a/lisp/textmodes/org.el +++ b/lisp/textmodes/org.el @@ -1,11 +1,11 @@ -;; org.el --- Outline-based notes management and organizer +;;; org.el --- Outline-based notes management and organizer ;; Carstens outline-mode for keeping track of everything. ;; Copyright (c) 2004, 2005 Free Software Foundation ;; ;; Author: Carsten Dominik <dominik at science dot uva dot nl> ;; Keywords: outlines, hypermedia, calendar ;; Homepage: http://www.astro.uva.nl/~dominik/Tools/org/ -;; Version: 3.13 +;; Version: 3.15 ;; ;; This file is part of GNU Emacs. ;; @@ -21,8 +21,8 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -;; Boston, MA 02110-1301, USA. +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;;; Commentary: @@ -80,6 +80,19 @@ ;; ;; Changes: ;; ------- +;; Version 3.15 +;; - QUOTE keyword at the beginning of an entry causes fixed-width export +;; of unmodified entry text. `C-c :' toggles this keyword. +;; - New face `org-special-keyword' which is used for COMMENT, QUOTE, +;; DEADLINE and SCHEDULED, and priority cookies. Default is only a weak +;; color, to reduce the amount of aggressive color in the buffer. +;; +;; Version 3.14 +;; - Formulas for individual fields in table. +;; - Automatic recalculation in calculating tables. +;; - Named fields and columns in tables. +;; - Fixed bug with calling `org-archive' several times in a row. +;; ;; Version 3.13 ;; - Efficiency improvements: Fewer table re-alignments needed. ;; - New special lines in tables, for defining names for individual cells. @@ -180,9 +193,13 @@ (require 'time-date) (require 'easymenu) +(defvar calc-embedded-close-formula) +(defvar calc-embedded-open-formula) +(defvar font-lock-unfontify-region-function) + ;;; Customization variables -(defvar org-version "3.13" +(defvar org-version "3.15" "The version number of the file org.el.") (defun org-version () (interactive) @@ -381,6 +398,15 @@ Changes become only effective after restarting Emacs." :group 'org-keywords :type 'string) +(defcustom org-quote-string "QUOTE" + "Entries starting with this keyword will be exported in fixed-width font. +Quoting applies only to the text in the entry following the headline, and does +not extend beyond the next headline, even if that is lower level. +An entry can be toggled between QUOTE and normal with +\\[org-toggle-fixed-width-section]" + :group 'org-keywords + :type 'string) + (defcustom org-after-todo-state-change-hook nil "Hook which is run after the state of a TODO item was changed. The new state (a string with a todo keyword, or nil) is available in the @@ -1215,6 +1241,20 @@ line will be formatted with <th> tags." :group 'org-table :type 'boolean) +(defcustom org-table-tab-recognizes-table.el t + "Non-nil means, TAB will automatically notice a table.el table. +When it sees such a table, it moves point into it and - if necessary - +calls `table-recognize-table'." + :group 'org-table + :type 'boolean) + +;; FIXME: Should this one be in another group? Which one? +(defcustom org-enable-fixed-width-editor t + "Non-nil means, lines starting with \":\" are treated as fixed-width. +This currently only means, they are never auto-wrapped. +When nil, such lines will be treated like ordinary lines." + :group 'org-table + :type 'boolean) (defgroup org-table-calculation nil "Options concerning tables in Org-mode." @@ -1284,29 +1324,10 @@ in table calculations, including symbolics etc." :group 'org-table-calculation :type 'boolean) -(defcustom org-table-tab-recognizes-table.el t - "Non-nil means, TAB will automatically notice a table.el table. -When it sees such a table, it moves point into it and - if necessary - -calls `table-recognize-table'." - :group 'org-table - :type 'boolean) - -(defcustom org-export-prefer-native-exporter-for-tables nil - "Non-nil means, always export tables created with table.el natively. -Natively means, use the HTML code generator in table.el. -When nil, Org-mode's own HTML generator is used when possible (i.e. if -the table does not use row- or column-spanning). This has the -advantage, that the automatic HTML conversions for math symbols and -sub/superscripts can be applied. Org-mode's HTML generator is also -much faster." - :group 'org-table - :type 'boolean) - -(defcustom org-enable-fixed-width-editor t - "Non-nil means, lines starting with \":\" are treated as fixed-width. -This currently only means, they are never auto-wrapped. -When nil, such lines will be treated like ordinary lines." - :group 'org-table +(defcustom org-table-allow-automatic-line-recalculation t + "Non-nil means, lines makred with |#| or |*| will be recomputed automatically. +Automatically means, when TAB or RET or C-c C-c are pressed in the line." + :group 'org-table-calculation :type 'boolean) (defgroup org-export nil @@ -1425,6 +1446,17 @@ This option can also be set with the +OPTIONS line, e.g. \"|:nil\"." :group 'org-export :type 'boolean) +(defcustom org-export-prefer-native-exporter-for-tables nil + "Non-nil means, always export tables created with table.el natively. +Natively means, use the HTML code generator in table.el. +When nil, Org-mode's own HTML generator is used when possible (i.e. if +the table does not use row- or column-spanning). This has the +advantage, that the automatic HTML conversions for math symbols and +sub/superscripts can be applied. Org-mode's HTML generator is also +much faster." + :group 'org-export + :type 'boolean) + (defcustom org-export-html-table-tag "<table border=1 cellspacing=0 cellpadding=6>" "The HTML tag used to start a table. @@ -1580,6 +1612,14 @@ Otherwise, the buffer will just be saved to a file and stay hidden." "Face used for level 8 headlines." :group 'org-faces) +(defface org-special-keyword ;; font-lock-string-face + '((((type tty) (class color)) (:foreground "green")) + (((class color) (background light)) (:foreground "RosyBrown")) + (((class color) (background dark)) (:foreground "LightSalmon")) + (t (:italic t))) + "Face used for level 8 headlines." + :group 'org-faces) + (defface org-warning ;; font-lock-warning-face '((((type tty) (class color)) (:foreground "red")) (((class color) (background light)) (:foreground "Red" :bold t)) @@ -1906,17 +1946,22 @@ The following commands are available: '(org-activate-dates (0 'org-link)) (list (concat "^\\*+[ \t]*" org-not-done-regexp) '(1 'org-warning t)) - (list (concat "\\[#[A-Z]\\]") '(0 'org-warning t)) - (list (concat "\\<" org-deadline-string) '(0 'org-warning t)) - (list (concat "\\<" org-scheduled-string) '(0 'org-warning t)) + (list (concat "\\[#[A-Z]\\]") '(0 'org-special-keyword t)) +; (list (concat "\\<" org-deadline-string) '(0 'org-warning t)) +; (list (concat "\\<" org-scheduled-string) '(0 'org-warning t)) + (list (concat "\\<" org-deadline-string) '(0 'org-special-keyword t)) + (list (concat "\\<" org-scheduled-string) '(0 'org-special-keyword t)) ;; '("\\(\\s-\\|^\\)\\(\\*\\([a-zA-Z]+\\)\\*\\)\\([^a-zA-Z*]\\|$\\)" ;; (3 'bold)) ;; '("\\(\\s-\\|^\\)\\(/\\([a-zA-Z]+\\)/\\)\\([^a-zA-Z*]\\|$\\)" ;; (3 'italic)) ;; '("\\(\\s-\\|^\\)\\(_\\([a-zA-Z]+\\)_\\)\\([^a-zA-Z*]\\|$\\)" ;; (3 'underline)) - (list (concat "^\\*+[ \t]*\\<\\(" org-comment-string "\\)\\>") - '(1 'org-warning t)) +; (list (concat "^\\*+[ \t]*\\<\\(" org-comment-string "\\)\\>") +; '(1 'org-warning t)) + (list (concat "^\\*+[ \t]*\\<\\(" org-comment-string + "\\|" org-quote-string "\\)\\>") + '(1 'org-special-keyword t)) '("^#.*" (0 'font-lock-comment-face t)) (if org-fontify-done-headline (list (concat "^[*]+ +\\<\\(" org-done-string "\\)\\(.*\\)\\>") @@ -1926,7 +1971,7 @@ The following commands are available: '("^[ \t]*\\(\\(|\\|\\+-[-+]\\).*\\S-\\)" (1 'org-table t)) '("^[ \t]*\\(:.*\\)" (1 'org-table t)) - '("| *\\(=[^|\n]*\\)" (1 'org-formula t)) + '("| *\\(:?=[^|\n]*\\)" (1 'org-formula t)) '("^[ \t]*| *\\([#!$*_^]\\) *|" (1 'org-formula t)) ))) (set (make-local-variable 'org-font-lock-keywords) @@ -2203,7 +2248,7 @@ or nil." (shrink-window-if-larger-than-buffer (get-buffer-window "*Help*")) (setq buffer-read-only nil) (erase-buffer) - (insert-buffer buf) + (insert-buffer-substring buf) (let ((org-startup-truncated t) (org-startup-folded t) (org-startup-with-deadline-check nil)) @@ -2634,7 +2679,10 @@ heading be marked DONE, and the current time will be added." (setq level (match-end 0)) (setq heading nil level 0)) (save-excursion - (org-copy-subtree) ; We first only copy, in case something goes wrong + ;; We first only copy, in case something goes wrong + ;; we need to protect this-command, to avoid kill-region sets it, + ;; which would lead to duplication of subtrees + (let (this-command) (org-copy-subtree)) (set-buffer buffer) ;; Enforce org-mode for the archive buffer (if (not (eq major-mode 'org-mode)) @@ -2691,7 +2739,7 @@ heading be marked DONE, and the current time will be added." (if (not (eq this-buffer buffer)) (save-buffer)))) ;; Here we are back in the original buffer. Everything seems to have ;; worked. So now cut the tree and finish up. - (org-cut-subtree) + (let (this-command) (org-cut-subtree)) (if (looking-at "[ \t]*$") (kill-line)) (message "Subtree archived %s" (if (eq this-buffer buffer) @@ -2717,7 +2765,6 @@ At all other locations, this simply calls `ispell-complete-word'." (skip-chars-backward "a-zA-Z0-9_:$") (point))) (texp (equal (char-before beg) ?\\)) - (form (equal (char-before beg) ?=)) (opt (equal (buffer-substring (max (point-at-bol) (- beg 2)) beg) "#+")) @@ -2734,9 +2781,6 @@ At all other locations, this simply calls `ispell-complete-word'." (texp (setq type :tex) org-html-entities) - (form - (setq type :form) - '(("sum") ("sumv") ("sumh"))) ((string-match "\\`\\*+[ \t]*\\'" (buffer-substring (point-at-bol) beg)) (setq type :todo) @@ -4001,7 +4045,7 @@ With prefix ARG, go back that many times `org-agenda-ndays'." (get-text-property (point) 'org-marker)) (org-agenda-show))) -(defvar org-disable-diary nil) ;Dynamically-scoped param. +(defvar org-disable-agenda-to-diary nil) ;Dynamically-scoped param. (defun org-get-entries-from-diary (date) "Get the (Emacs Calendar) diary entries for DATE." @@ -4009,8 +4053,10 @@ With prefix ARG, go back that many times `org-agenda-ndays'." (diary-display-hook '(fancy-diary-display)) (list-diary-entries-hook (cons 'org-diary-default-entry list-diary-entries-hook)) + (diary-file-name-prefix-function nil) ; turn this feature off + (diary-modify-entry-list-string-function 'org-modify-diary-entry-string) entries - (org-disable-diary t)) + (org-disable-agenda-to-diary t)) (save-excursion (save-window-excursion (list-diary-entries date 1))) @@ -4064,35 +4110,43 @@ date. Itt also removes lines that contain only whitespace." (if (re-search-forward "^Org-mode dummy\n?" nil t) (replace-match ""))) -;; Advise the add-to-diary-list function to allow org to jump to -;; diary entries. Wrapped into eval-after-load to avoid loading -;; advice unnecessarily +;; Make sure entries from the diary have the right text properties. (eval-after-load "diary-lib" - '(defadvice add-to-diary-list (before org-mark-diary-entry activate) - "Make the position visible." - (if (and org-disable-diary ;; called from org-agenda - (stringp string) - (buffer-file-name)) - (add-text-properties - 0 (length string) - (list 'mouse-face 'highlight - 'keymap org-agenda-keymap - 'help-echo - (format - "mouse-2 or RET jump to diary file %s" - (abbreviate-file-name (buffer-file-name))) - 'org-agenda-diary-link t - 'org-marker (org-agenda-new-marker (point-at-bol))) - string)))) + '(if (boundp 'diary-modify-entry-list-string-function) + ;; We can rely on the hook, nothing to do + nil + ;; Hook not avaiable, must use advice to make this work + (defadvice add-to-diary-list (before org-mark-diary-entry activate) + "Make the position visible." + (if (and org-disable-agenda-to-diary ;; called from org-agenda + (stringp string) + (buffer-file-name)) + (setq string (org-modify-diary-entry-string string)))))) + +(defun org-modify-diary-entry-string (string) + "Add text properties to string, allowing org-mode to act on it." + (add-text-properties + 0 (length string) + (list 'mouse-face 'highlight + 'keymap org-agenda-keymap + 'help-echo + (format + "mouse-2 or RET jump to diary file %s" + (abbreviate-file-name (buffer-file-name))) + 'org-agenda-diary-link t + 'org-marker (org-agenda-new-marker (point-at-bol))) + string) + string) (defun org-diary-default-entry () "Add a dummy entry to the diary. Needed to avoid empty dates which mess up holiday display." ;; Catch the error if dealing with the new add-to-diary-alist - (condition-case nil - (add-to-diary-list original-date "Org-mode dummy" "") - (error - (add-to-diary-list original-date "Org-mode dummy" "" nil)))) + (when org-disable-agenda-to-diary + (condition-case nil + (add-to-diary-list original-date "Org-mode dummy" "") + (error + (add-to-diary-list original-date "Org-mode dummy" "" nil))))) (defun org-add-file (&optional file) "Add current file to the list of files in variable `org-agenda-files'. @@ -4226,11 +4280,12 @@ function from a program - use `org-agenda-get-day-entries' instead." file rtn results) ;; If this is called during org-agenda, don't return any entries to ;; the calendar. Org Agenda will list these entries itself. - (if org-disable-diary (setq files nil)) + (if org-disable-agenda-to-diary (setq files nil)) (while (setq file (pop files)) (setq rtn (apply 'org-agenda-get-day-entries file date args)) (setq results (append results rtn))) - (concat (org-finalize-agenda-entries results) "\n"))) + (if results + (concat (org-finalize-agenda-entries results) "\n")))) (defun org-agenda-get-day-entries (file date &rest args) "Does the work for `org-diary' and `org-agenda'. @@ -5816,6 +5871,8 @@ See also the variable `org-reverse-note-order'." "Detects a table line marked for automatic recalculation.") (defconst org-table-recalculate-regexp "^[ \t]*| *[#*] *\\(|\\|$\\)" "Detects a table line marked for automatic recalculation.") +(defconst org-table-calculate-mark-regexp "^[ \t]*| *[!$^_#*] *\\(|\\|$\\)" + "Detects a table line marked for automatic recalculation.") (defconst org-table-hline-regexp "^[ \t]*|-" "Detects an org-type table hline.") (defconst org-table1-hline-regexp "^[ \t]*\\+-[-+]" @@ -6119,7 +6176,7 @@ Optional argument NEW may specify text to replace the current field content." (cond ((and (not new) org-table-may-need-update)) ; Realignment will happen anyway ((org-at-table-hline-p) - ;; FIXME: I use to enforce realign here, but I think this is not needed. + ;; FIXME: I used to enforce realign here, but I think this is not needed. ;; (setq org-table-may-need-update t) ) ((and (not new) @@ -6133,15 +6190,17 @@ Optional argument NEW may specify text to replace the current field content." (let* ((pos (point)) s (col (org-table-current-column)) (num (nth (1- col) org-table-last-alignment)) - l f n o upd) + l f n o e) (when (> col 0) (skip-chars-backward "^|\n") - (if (looking-at " *\\([^|\n]*?\\) *|") + (if (looking-at " *\\([^|\n]*?\\) *\\(|\\|$\\)") (progn (setq s (match-string 1) o (match-string 0) - l (max 1 (- (match-end 0) (match-beginning 0) 3))) - (setq f (format (if num " %%%ds |" " %%-%ds |") l) + l (max 1 (- (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 t t)) (if new (if (<= (length new) l) @@ -6254,7 +6313,7 @@ integer, it will be incremented while copying." (progn (if (and org-table-copy-increment (string-match "^[0-9]+$" txt)) - (setq txt (format "%d" (+ (string-to-int txt) 1)))) + (setq txt (format "%d" (+ (string-to-number txt) 1)))) (insert txt) (org-table-maybe-recalculate-line) (org-table-align)) @@ -6980,91 +7039,186 @@ If NLAST is a number, only the NLAST fields will actually be summed." ((equal n 0) nil) (t n)))) +(defun org-table-get-vertical-vector (desc &optional tbeg col) + "Get a calc vector from a column, accorting to desctiptor DESC. +Optional arguments TBEG and COL can give the beginning of the table and +the current column, to avoid unnecessary parsing." + (save-excursion + (or tbeg (setq tbeg (org-table-begin))) + (or col (setq col (org-table-current-column))) + (let (beg end nn n n1 n2 l (thisline (org-current-line)) hline-list) + (cond + ((string-match "\\(I+\\)\\(-\\(I+\\)\\)?" desc) + (setq n1 (- (match-end 1) (match-beginning 1))) + (if (match-beginning 3) + (setq n2 (- (match-end 2) (match-beginning 3)))) + (setq n (if n2 (max n1 n2) n1)) + (setq n1 (if n2 (min n1 n2))) + (setq nn n) + (while (and (> nn 0) + (re-search-backward org-table-hline-regexp tbeg t)) + (push (org-current-line) hline-list) + (setq nn (1- nn))) + (setq hline-list (nreverse hline-list)) + (goto-line (nth (1- n) hline-list)) + (when (re-search-forward org-table-dataline-regexp) + (org-table-goto-column col) + (setq beg (point))) + (goto-line (if n1 (nth (1- n1) hline-list) thisline)) + (when (re-search-backward org-table-dataline-regexp) + (org-table-goto-column col) + (setq end (point))) + (setq l (apply 'append (org-table-copy-region beg end))) + (concat "[" (mapconcat (lambda (x) (setq x (org-trim x)) + (if (equal x "") "0" x)) + l ",") "]")) + ((string-match "\\([0-9]+\\)-\\([0-9]+\\)" desc) + (setq n1 (string-to-number (match-string 1 desc)) + n2 (string-to-number (match-string 2 desc))) + (beginning-of-line 1) + (save-excursion + (when (re-search-backward org-table-dataline-regexp tbeg t n1) + (org-table-goto-column col) + (setq beg (point)))) + (when (re-search-backward org-table-dataline-regexp tbeg t n2) + (org-table-goto-column col) + (setq end (point))) + (setq l (apply 'append (org-table-copy-region beg end))) + (concat "[" (mapconcat + (lambda (x) (setq x (org-trim x)) + (if (equal x "") "0" x)) + l ",") "]")) + ((string-match "\\([0-9]+\\)" desc) + (beginning-of-line 1) + (when (re-search-backward org-table-dataline-regexp tbeg t + (string-to-number (match-string 0 desc))) + (org-table-goto-column col) + (org-trim (org-table-get-field)))))))) + (defvar org-table-formula-history nil) -(defun org-table-get-formula (&optional equation) +(defvar org-table-column-names nil + "Alist with column names, derived from the `!' line.") +(defvar org-table-column-name-regexp nil + "Regular expression matching the current column names.") +(defvar org-table-local-parameters nil + "Alist with parameter names, derived from the `$' line.") +(defvar org-table-named-field-locations nil + "Alist with locations of named fields.") + +(defun org-table-get-formula (&optional equation named) "Read a formula from the minibuffer, offer stored formula as default." - (let* ((col (org-table-current-column)) + (let* ((name (car (rassoc (list (org-current-line) + (org-table-current-column)) + org-table-named-field-locations))) + (scol (if named + (if name name + (error "Not in a named field")) + (int-to-string (org-table-current-column)))) + (dummy (and name (not named) + (not (y-or-n-p "Replace named-field formula with column equation? " )) + (error "Abort"))) (org-table-may-need-update nil) (stored-list (org-table-get-stored-formulas)) - (stored (cdr (assoc col stored-list))) + (stored (cdr (assoc scol stored-list))) (eq (cond ((and stored equation (string-match "^ *= *$" equation)) stored) ((stringp equation) equation) (t (read-string - "Formula: " (or stored "") 'org-table-formula-history - stored))))) - (if (not (string-match "\\S-" eq)) - (error "Empty formula")) + (format "%s formula $%s=" (if named "Field" "Column") scol) + (or stored "") 'org-table-formula-history + ;stored + )))) + mustsave) + (when (not (string-match "\\S-" eq)) + ;; remove formula + (setq stored-list (delq (assoc scol stored-list) stored-list)) + (org-table-store-formulas stored-list) + (error "Formula removed")) (if (string-match "^ *=?" eq) (setq eq (replace-match "" t t eq))) (if (string-match " *$" eq) (setq eq (replace-match "" t t eq))) + (if (and name (not named)) + ;; We set the column equation, delete the named one. + (setq stored-list (delq (assoc name stored-list) stored-list) + mustsave t)) (if stored - (setcdr (assoc col stored-list) eq) - (setq stored-list (cons (cons col eq) stored-list))) - (if (not (equal stored eq)) + (setcdr (assoc scol stored-list) eq) + (setq stored-list (cons (cons scol eq) stored-list))) + (if (or mustsave (not (equal stored eq))) (org-table-store-formulas stored-list)) eq)) (defun org-table-store-formulas (alist) "Store the list of formulas below the current table." - (setq alist (sort alist (lambda (a b) (< (car a) (car b))))) + (setq alist (sort alist (lambda (a b) (string< (car a) (car b))))) (save-excursion (goto-char (org-table-end)) (if (looking-at "\\([ \t]*\n\\)*#\\+TBLFM:.*\n?") (delete-region (point) (match-end 0))) (insert "#+TBLFM: " (mapconcat (lambda (x) - (concat "$" (int-to-string (car x)) "=" (cdr x))) + (concat "$" (car x) "=" (cdr x))) alist "::") "\n"))) (defun org-table-get-stored-formulas () - "Return an alist withh the t=stored formulas directly after current table." + "Return an alist with the t=stored formulas directly after current table." (interactive) - (let (col eq eq-alist strings string) + (let (scol eq eq-alist strings string seen) (save-excursion (goto-char (org-table-end)) (when (looking-at "\\([ \t]*\n\\)*#\\+TBLFM: *\\(.*\\)") (setq strings (org-split-string (match-string 2) " *:: *")) (while (setq string (pop strings)) - (if (string-match "\\$\\([0-9]+\\) *= *\\(.*[^ \t]\\)" string) - (setq col (string-to-number (match-string 1 string)) - eq (match-string 2 string) - eq-alist (cons (cons col eq) eq-alist)))))) - eq-alist)) + (when (string-match "\\$\\([a-zA-Z0-9]+\\) *= *\\(.*[^ \t]\\)" string) + (setq scol (match-string 1 string) + eq (match-string 2 string) + eq-alist (cons (cons scol eq) eq-alist)) + (if (member scol seen) + (error "Double definition `$%s=' in TBLFM line, please fix by hand" scol) + (push scol seen)))))) + (nreverse eq-alist))) (defun org-table-modify-formulas (action &rest columns) "Modify the formulas stored below the current table. ACTION can be `remove', `insert', `swap'. For `swap', two column numbers are expected, for the other action only a single column number is needed." (let ((list (org-table-get-stored-formulas)) - (nmax (length (org-split-string (buffer-substring (point-at-bol) (point-at-eol)) - "|"))) - col col1 col2) + (nmax (length (org-split-string + (buffer-substring (point-at-bol) (point-at-eol)) + "|"))) + col col1 col2 scol si sc1 sc2) (cond ((null list)) ; No action needed if there are no stored formulas ((eq action 'remove) - (setq col (car columns)) - (org-table-replace-in-formulas list col "INVALID") - (if (assoc col list) (setq list (delq (assoc col list) list))) + (setq col (car columns) + scol (int-to-string col)) + (org-table-replace-in-formulas list scol "INVALID") + (if (assoc scol list) (setq list (delq (assoc scol list) list))) (loop for i from (1+ col) upto nmax by 1 do - (org-table-replace-in-formulas list i (1- i)) - (if (assoc i list) (setcar (assoc i list) (1- i))))) + (setq si (int-to-string i)) + (org-table-replace-in-formulas list si (int-to-string (1- i))) + (if (assoc si list) (setcar (assoc si list) + (int-to-string (1- i)))))) ((eq action 'insert) (setq col (car columns)) (loop for i from nmax downto col by 1 do - (org-table-replace-in-formulas list i (1+ i)) - (if (assoc i list) (setcar (assoc i list) (1+ i))))) + (setq si (int-to-string i)) + (org-table-replace-in-formulas list si (int-to-string (1+ i))) + (if (assoc si list) (setcar (assoc si list) + (int-to-string (1+ i)))))) ((eq action 'swap) - (setq col1 (car columns) col2 (nth 1 columns)) - (org-table-replace-in-formulas list col1 "Z") - (org-table-replace-in-formulas list col2 col1) - (org-table-replace-in-formulas list "Z" col2) - (if (assoc col1 list) (setcar (assoc col1 list) "Z")) - (if (assoc col2 list) (setcar (assoc col2 list) col1)) - (if (assoc "Z" list) (setcar (assoc "Z" list) col2))) + (setq col1 (car columns) col2 (nth 1 columns) + sc1 (int-to-string col1) sc2 (int-to-string col2)) + ;; Hopefully, ZqZ will never be a name in a table... FIXME: + (org-table-replace-in-formulas list sc1 "ZqZ") + (org-table-replace-in-formulas list sc2 sc1) + (org-table-replace-in-formulas list "ZqZ" sc2) + (if (assoc sc1 list) (setcar (assoc sc1 list) "ZqZ")) + (if (assoc sc2 list) (setcar (assoc sc2 list) sc1)) + (if (assoc "ZqZ" list) (setcar (assoc "ZqZ" list) sc2))) (t (error "Invalid action in `org-table-modify-formulas'"))) (if list (org-table-store-formulas list)))) @@ -7079,20 +7233,14 @@ expected, for the other action only a single column number is needed." (setq s (replace-match s2 t t s))) (setcdr elt s)))) -(defvar org-table-column-names nil - "Alist with column names, derived from the `!' line.") -(defvar org-table-column-name-regexp nil - "Regular expression matching the current column names.") -(defvar org-table-local-parameters nil - "Alist with parameter names, derived from the `$' line.") - (defun org-table-get-specials () "Get the column nmaes and local parameters for this table." (save-excursion (let ((beg (org-table-begin)) (end (org-table-end)) - names name fields fields1 field cnt c v) + names name fields fields1 field cnt c v line col) (setq org-table-column-names nil - org-table-local-parameters nil) + org-table-local-parameters nil + org-table-named-field-locations nil) (goto-char beg) (when (re-search-forward "^[ \t]*| *! *\\(|.*\\)" end t) (setq names (org-split-string (match-string 1) " *| *") @@ -7117,13 +7265,15 @@ expected, for the other action only a single column number is needed." fields (org-split-string (match-string 2) " *| *")) (save-excursion (beginning-of-line (if (equal c "_") 2 0)) + (setq line (org-current-line) col 1) (and (looking-at "^[ \t]*|[^|]*\\(|.*\\)") (setq fields1 (org-split-string (match-string 1) " *| *")))) - (while (setq field (pop fields)) - (setq v (pop fields1)) - (if (and (stringp field) (stringp v) - (string-match "^[a-zA-Z][a-zA-Z0-9]*$" field)) - (push (cons field v) org-table-local-parameters))))))) + (while (and fields1 (setq field (pop fields))) + (setq v (pop fields1) col (1+ col)) + (when (and (stringp field) (stringp v) + (string-match "^[a-zA-Z][a-zA-Z0-9]*$" field)) + (push (cons field v) org-table-local-parameters) + (push (list field line col) org-table-named-field-locations))))))) (defun org-this-word () ;; Get the current word @@ -7133,46 +7283,18 @@ expected, for the other action only a single column number is needed." (buffer-substring-no-properties beg end)))) (defun org-table-maybe-eval-formula () - "Check if the current field starts with \"=\" and evaluate the formula." + "Check if the current field starts with \"=\" or \":=\". +If yes, store the formula and apply it." ;; We already know we are in a table. Get field will only return a formula ;; when appropriate. It might return a separator line, but no problem. (when org-table-formula-evaluate-inline (let* ((field (org-trim (or (org-table-get-field) ""))) - (dfield (downcase field)) - col bolpos nlast) - (when (equal (string-to-char field) ?=) - (if (string-match "^\\(=sum[vh]?\\)\\([0-9]+\\)$" dfield) - (setq nlast (1+ (string-to-number (match-string 2 dfield))) - dfield (match-string 1 dfield))) - (cond - ((equal dfield "=sumh") - (org-table-get-field - nil (org-table-sum - (save-excursion (org-table-goto-column 1) (point)) - (point) nlast))) - ((member dfield '("=sum" "=sumv")) - (setq col (org-table-current-column) - bolpos (point-at-bol)) - (org-table-get-field - nil (org-table-sum - (save-excursion - (goto-char (org-table-begin)) - (if (re-search-forward org-table-dataline-regexp bolpos t) - (progn - (goto-char (match-beginning 0)) - (org-table-goto-column col) - (point)) - (error "No datalines above current"))) - (point) nlast))) - ((and (string-match "^ *=" field) - (fboundp 'calc-eval)) - (org-table-eval-formula nil field))))))) - -(defvar org-last-recalc-undo-list nil) -(defcustom org-table-allow-line-recalculation t - "FIXME:" - :group 'org-table - :type 'boolean) + named eq) + (when (string-match "^:?=\\(.+\\)" field) + (setq named (equal (string-to-char field) ?:) + eq (match-string 1 field)) + (if (fboundp 'calc-eval) + (org-table-eval-formula (if named '(4) nil) eq)))))) (defvar org-recalc-commands nil "List of commands triggering the reccalculation of a line. @@ -7210,8 +7332,10 @@ of the new mark." (col (org-table-current-column)) (forcenew (car (assoc newchar org-recalc-marks))) epos new) - (if l1 (setq newchar (char-to-string (read-char-exclusive "Change region to what mark? Type # * ! $ or SPC: ")) - forcenew (car (assoc newchar org-recalc-marks)))) + (when l1 + (message "Change region to what mark? Type # * ! $ or SPC: ") + (setq newchar (char-to-string (read-char-exclusive)) + forcenew (car (assoc newchar org-recalc-marks)))) (if (and newchar (not forcenew)) (error "Invalid NEWCHAR `%s' in `org-table-rotate-recalc-marks'" newchar)) @@ -7248,7 +7372,7 @@ of the new mark." (defun org-table-maybe-recalculate-line () "Recompute the current line if marked for it, and if we haven't just done it." (interactive) - (and org-table-allow-line-recalculation + (and org-table-allow-automatic-line-recalculation (not (and (memq last-command org-recalc-commands) (equal org-last-recalc-line (org-current-line)))) (save-excursion (beginning-of-line 1) @@ -7273,7 +7397,7 @@ When nil, simply write \"#ERROR\" in corrupted fields.") (cons var (cons value modes))) modes) -(defun org-table-eval-formula (&optional ndown equation +(defun org-table-eval-formula (&optional arg equation suppress-align suppress-const suppress-store) "Replace the table field value at the cursor by the result of a calculation. @@ -7283,64 +7407,46 @@ 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. While nowhere near the computation options of a -spreadsheet program, this is still very useful. There is no automatic -updating of a calculated field, but the table will remember the last -formula for each column. The command needs to be applied again after -changing input fields. - -When called, the command first prompts for a formula, which is read in the -minibuffer. Previously entered formulas are available through the history -list, and the last used formula for each column is offered as a default. +result of a formula. It also installes 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 ba a named field, and the +formula is installed as valid in only this specific field. + +When called, the command first prompts for a formula, which is read in +the minibuffer. Previously entered formulas are available through the +history list, and the last used formula is offered as a default. 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. -Before evaluation, variable substitution takes place: \"$\" is replaced by -the field the cursor is currently in, and $1..$n reference the fields in -the current row. Values from a *different* row can *not* be referenced -here, so the command supports only horizontal computing. The formula can -contain an optional printf format specifier after a semicolon, to reformat -the result. - -A few examples for formulas: - $1+$2 Sum of first and second field - $1+$2;%.2f Same, and format result to two digits after dec.point - exp($2)+exp($1) Math functions can be used - $;%.1f Reformat current cell to 1 digit after dec.point - ($3-32)*5/9 degrees F -> C conversion - -When called with a raw \\[universal-argument] prefix, the formula is applied to the current -field, and to the same same column in all following rows, until reaching a -horizontal line or the end of the table. When the command is called with a -numeric prefix argument (like M-3 or C-7 or \\[universal-argument] 24), the formula is applied -to the current row, and to the following n-1 rows (but not beyond a -separator line). - -This function can also be called from Lisp programs and offers two additional -Arguments: EQUATION can be the formula to apply. If this argument is given, -the user will not be prompted. SUPPRESS-ALIGN is used to speed-up -recursive calls by by-passing unnecessary aligns. SUPPRESS-CONST suppresses -the interpretation of constants in the formula. 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." +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 +argument is given, the user will not be prompted. SUPPRESS-ALIGN is +used to speed-up recursive calls by by-passing unnecessary aligns. +SUPPRESS-CONST suppresses the interpretation of constants in the +formula, assuming that this has been done already outside the fuction. +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." (interactive "P") - (setq ndown (if (equal ndown '(4)) 10000 (prefix-numeric-value ndown))) (require 'calc) (org-table-check-inside-data-field) (org-table-get-specials) (let* (fields + (ndown (if (integerp arg) arg 1)) (org-table-automatic-realign nil) (case-fold-search nil) (down (> ndown 1)) (formula (if (and equation suppress-store) equation - (org-table-get-formula equation))) + (org-table-get-formula equation (equal arg '(4))))) (n0 (org-table-current-column)) (modes (copy-sequence org-calc-default-modes)) n form fmt x ev orig c) ;; Parse the format string. Since we have a lot of modes, this is - ;; a lot of work. + ;; a lot of work. However, I think calc still uses most of the time. (if (string-match ";" formula) (let ((tmp (org-split-string formula ";"))) (setq formula (car tmp) @@ -7374,15 +7480,23 @@ it is a modified equation that should not overwrite the stored one." fields))) (setq ndown (1- ndown)) (setq form (copy-sequence formula)) + ;; Insert the references to fields in same row (while (string-match "\\$\\([0-9]+\\)?" form) (setq n (if (match-beginning 1) - (string-to-int (match-string 1 form)) + (string-to-number (match-string 1 form)) n0) x (nth (1- n) fields)) (unless x (error "Invalid field specifier \"%s\"" (match-string 0 form))) (if (equal x "") (setq x "0")) (setq form (replace-match (concat "(" x ")") t t form))) + ;; Insert ranges in current column + (while (string-match "\\&[-I0-9]+" form) + (setq form (replace-match + (save-match-data + (org-table-get-vertical-vector (match-string 0 form) + nil n0)) + t t form))) (setq ev (calc-eval (cons form modes) (if org-table-formula-numbers-only 'num))) @@ -7424,24 +7538,32 @@ $1-> %s\n" orig formula form)) (unless (org-at-table-p) (error "Not at a table")) (org-table-get-specials) (let* ((eqlist (sort (org-table-get-stored-formulas) - (lambda (a b) (< (car a) (car b))))) + (lambda (a b) (string< (car a) (car b))))) (inhibit-redisplay t) (line-re org-table-dataline-regexp) (thisline (+ (if (bolp) 1 0) (count-lines (point-min) (point)))) (thiscol (org-table-current-column)) - beg end entry eql (cnt 0)) + beg end entry eqlnum eqlname eql (cnt 0) eq a name) ;; Insert constants in all formulas (setq eqlist (mapcar (lambda (x) (setcdr x (org-table-formula-substitute-names (cdr x))) x) eqlist)) + ;; Split the equation list + (while (setq eq (pop eqlist)) + (if (<= (string-to-char (car eq)) ?9) + (push eq eqlnum) + (push eq eqlname))) + (setq eqlnum (nreverse eqlnum) eqlname (nreverse eqlname)) (if all (progn (setq end (move-marker (make-marker) (1+ (org-table-end)))) (goto-char (setq beg (org-table-begin))) - (if (re-search-forward org-table-recalculate-regexp end t) + (if (re-search-forward org-table-calculate-mark-regexp end t) + ;; This is a table with marked lines, only compute selected lines (setq line-re org-table-recalculate-regexp) + ;; Move forward to the first non-header line (if (and (re-search-forward org-table-dataline-regexp end t) (re-search-forward org-table-hline-regexp end t) (re-search-forward org-table-dataline-regexp end t)) @@ -7452,23 +7574,37 @@ $1-> %s\n" orig formula form)) (goto-char beg) (and all (message "Re-applying formulas to full table...")) (while (re-search-forward line-re end t) - (unless (string-match "^ *[!$] *$" (org-table-get-field 1)) + (unless (string-match "^ *[_^!$] *$" (org-table-get-field 1)) ;; Unprotected line, recalculate (and all (message "Re-applying formulas to full table...(line %d)" (setq cnt (1+ cnt)))) (setq org-last-recalc-line (org-current-line)) - (setq eql eqlist) + (setq eql eqlnum) (while (setq entry (pop eql)) (goto-line org-last-recalc-line) - (org-table-goto-column (car entry) nil 'force) + (org-table-goto-column (string-to-number (car entry)) nil 'force) (org-table-eval-formula nil (cdr entry) 'noalign 'nocst 'nostore)))) (goto-line thisline) (org-table-goto-column thiscol) (or noalign (and org-table-may-need-update (org-table-align)) - (and all (message "Re-applying formulas to %d lines...done" cnt))))) + (and all (message "Re-applying formulas to %d lines...done" cnt))) + ;; Now do the names fields + (while (setq eq (pop eqlname)) + (setq name (car eq) + a (assoc name org-table-named-field-locations)) + (when a + (message "Re-applying formula to named field: %s" name) + (goto-line (nth 1 a)) + (org-table-goto-column (nth 2 a)) + (org-table-eval-formula nil (cdr eq) 'noalign 'nocst 'nostore))) + ;; back to initial position + (goto-line thisline) + (org-table-goto-column thiscol) + (or noalign (and org-table-may-need-update (org-table-align)) + (and all (message "Re-applying formulas...done"))))) (defun org-table-formula-substitute-names (f) - "Replace $const with values in stirng F." + "Replace $const with values in string F." (let ((start 0) a n1 n2 nn1 nn2 s (f1 f)) ;; First, check for column names (while (setq start (string-match org-table-column-name-regexp f start)) @@ -7505,6 +7641,136 @@ Parameters get priority." (and (fboundp 'constants-get) (constants-get const)) "#UNDEFINED_NAME")) +(defvar org-edit-formulas-map (make-sparse-keymap)) +(define-key org-edit-formulas-map "\C-c\C-c" 'org-finish-edit-formulas) +(define-key org-edit-formulas-map "\C-c\C-q" 'org-abort-edit-formulas) +(define-key org-edit-formulas-map "\C-c?" 'org-show-variable) + +(defvar org-pos) +(defvar org-window-configuration) + +(defun org-table-edit-formulas () + "Edit the formulas of the current table in a separate buffer." + (interactive) + (unless (org-at-table-p) + (error "Not at a table")) + (org-table-get-specials) + (let ((eql (org-table-get-stored-formulas)) + (pos (move-marker (make-marker) (point))) + (wc (current-window-configuration)) + entry loc s) + (switch-to-buffer-other-window "*Edit Formulas*") + (erase-buffer) + (fundamental-mode) + (set (make-local-variable 'org-pos) pos) + (set (make-local-variable 'org-window-configuration) wc) + (use-local-map org-edit-formulas-map) + (setq s "# Edit formulas and finish with `C-c C-c'. +# Use `C-u C-c C-c' to also appy them immediately to the entire table. +# Use `C-c ?' to get information about $name at point. +# To cancel editing, press `C-c C-q'.\n") + (put-text-property 0 (length s) 'face 'font-lock-comment-face s) + (insert s) + (while (setq entry (pop eql)) + (when (setq loc (assoc (car entry) org-table-named-field-locations)) + (setq s (format "# Named formula, referring to column %d in line %d\n" + (nth 2 loc) (nth 1 loc))) + (put-text-property 0 (length s) 'face 'font-lock-comment-face s) + (insert s)) + (setq s (concat "$" (car entry) "=" (cdr entry) "\n")) + (remove-text-properties 0 (length s) '(face nil) s) + (insert s)) + (goto-char (point-min)) + (message "Edit formulas and finish with `C-c C-c'."))) + +(defun org-show-variable () + "Show the location/value of the $ expression at point." + (interactive) + (let (var (pos org-pos) (win (selected-window)) e) + (save-excursion + (or (looking-at "\\$") (skip-chars-backward "$a-zA-Z0-9")) + (if (looking-at "\\$\\([a-zA-Z0-9]+\\)") + (setq var (match-string 1)) + (error "No variable at point"))) + (cond + ((setq e (assoc var org-table-named-field-locations)) + (switch-to-buffer-other-window (marker-buffer pos)) + (goto-line (nth 1 e)) + (org-table-goto-column (nth 2 e)) + (select-window win) + (message "Named field, column %d of line %d" (nth 2 e) (nth 1 e))) + ((setq e (assoc var org-table-column-names)) + (switch-to-buffer-other-window (marker-buffer pos)) + (goto-char pos) + (goto-char (org-table-begin)) + (if (re-search-forward (concat "^[ \t]*| *! *.*?| *\\(" var "\\) *|") + (org-table-end) t) + (progn + (goto-char (match-beginning 1)) + (message "Named column (column %s)" (cdr e))) + (error "Column name not found")) + (select-window win)) + ((string-match "^[0-9]$" var) + ;; column number + (switch-to-buffer-other-window (marker-buffer pos)) + (goto-char pos) + (goto-char (org-table-begin)) + (recenter 1) + (if (re-search-forward org-table-dataline-regexp + (org-table-end) t) + (progn + (goto-char (match-beginning 0)) + (org-table-goto-column (string-to-number var)) + (message "Column %s" var)) + (error "Column name not found")) + (select-window win)) + ((setq e (assoc var org-table-local-parameters)) + (switch-to-buffer-other-window (marker-buffer pos)) + (goto-char pos) + (goto-char (org-table-begin)) + (if (re-search-forward (concat "^[ \t]*| *\\$ *.*?| *\\(" var "=\\)") nil t) + (progn + (goto-char (match-beginning 1)) + (message "Local parameter.")) + (error "Parameter not found")) + (select-window win)) + (t + (cond + ((setq e (assoc var org-table-formula-constants)) + (message "Constant: $%s=%s in `org-table-formula-constants'." var (cdr e))) + ((setq e (and (fboundp 'constants-get) (constants-get var))) + (message "Constant: $%s=%s, retrieved from `constants.el'." var e)) + (t (error "Undefined name $%s" var))))))) + +(defun org-finish-edit-formulas (&optional arg) + "Parse the buffer for formula definitions and install them. +With prefix ARG, apply the new formulas to the table." + (interactive "P") + (let ((pos org-pos) eql) + (goto-char (point-min)) + (while (re-search-forward "^\\$\\([a-zA-Z0-9]+\\) *= *\\(.*?\\) *$" nil t) + (push (cons (match-string 1) (match-string 2)) eql)) + (set-window-configuration org-window-configuration) + (select-window (get-buffer-window (marker-buffer pos))) + (goto-char pos) + (unless (org-at-table-p) + (error "Lost table position - cannot install formulae")) + (org-table-store-formulas eql) + (move-marker pos nil) + (kill-buffer "*Edit Formulas*") + (if arg + (org-table-recalculate 'all) + (message "New formulas installed - press C-u C-c C-c to apply.")))) + +(defun org-abort-edit-formulas () + "Abort editing formulas, without installing the changes." + (interactive) + (let ((pos org-pos)) + (set-window-configuration org-window-configuration) + (select-window (get-buffer-window (marker-buffer pos))) + (goto-char pos) + (message "Formula editing aborted without installing changes"))) + ;;; The orgtbl minor mode ;; Define a minor mode which can be used in other modes in order to @@ -7578,7 +7844,7 @@ table editor in arbitrary modes.") (and c (setq minor-mode-map-alist (cons c (delq c minor-mode-map-alist))))) (set (make-local-variable (quote org-table-may-need-update)) t) - (make-local-hook (quote before-change-functions)) + (make-local-hook (quote before-change-functions)) ; needed for XEmacs (add-hook 'before-change-functions 'org-before-change-function nil 'local) (set (make-local-variable 'org-old-auto-fill-inhibit-regexp) @@ -7657,6 +7923,7 @@ to execute outside of tables." '("\C-c+" org-table-sum) '("\C-c|" org-table-toggle-vline-visibility) '("\C-c=" org-table-eval-formula) + '("\C-c'" org-table-edit-formulas) '("\C-c*" org-table-recalculate) '([(control ?#)] org-table-rotate-recalc-marks))) elt key fun cmd) @@ -7714,8 +7981,9 @@ to execute outside of tables." ["Paste Rectangle" org-paste-special :active (org-at-table-p) :keys "C-c C-y"] ["Fill Rectangle" org-table-wrap-region :active (org-at-table-p) :keys "C-c C-q"]) "--" - ["Eval Formula" org-table-eval-formula :active (org-at-table-p) :keys "C-c ="] - ["Eval Formula Down " (org-table-eval-formula '(4)) :active (org-at-table-p) :keys "C-u C-c ="] + ["Set Column Formula" org-table-eval-formula :active (org-at-table-p) :keys "C-c ="] + ["Set Named Field Formula" (org-table-eval-formula '(4)) :active (org-at-table-p) :keys "C-u C-c ="] + ["Edit Formulas" org-table-edit-formulas :active (org-at-table-p) :keys "C-c '"] ["Recalculate line" org-table-recalculate :active (org-at-table-p) :keys "C-c *"] ["Recalculate all" (org-table-recalculate '(4)) :active (org-at-table-p) :keys "C-u C-c *"] ["Toggle Recalculate Mark" org-table-rotate-recalc-marks :active (org-at-table-p) :keys "C-c #"] @@ -8395,14 +8663,13 @@ Does include HTML export options as well as TODO and CATEGORY stuff." (insert s))) (defun org-toggle-fixed-width-section (arg) - "Toggle the fixed-width indicator at the beginning of lines in the region. -If there is no active region, only acts on the current line. -If the first non-white character in the first line of the region is a -vertical bar \"|\", then the command removes the bar from all lines in -the region. If the first character is not a bar, the command adds a -bar to all lines, in the column given by the beginning of the region. - -If there is a numerical prefix ARG, create ARG new lines starting with \"|\"." + "Toggle the fixed-width export. +If there is no active region, the QUOTE keyword at the current headline is +inserted or removed. When present, it causes the text between this headline +and the next to be exported as fixed-width text, and unmodified. +If there is an active region, this command adds or removes a colon as the +first character of this line. If the first character of a line is a colon, +this line is also exported in fixed-width font." (interactive "P") (let* ((cc 0) (regionp (org-region-active-p)) @@ -8411,23 +8678,33 @@ If there is a numerical prefix ARG, create ARG new lines starting with \"|\"." (nlines (or arg (if (and beg end) (count-lines beg end) 1))) (re "[ \t]*\\(:\\)") off) - (save-excursion - (goto-char beg) - (setq cc (current-column)) - (beginning-of-line 1) - (setq off (looking-at re)) - (while (> nlines 0) - (setq nlines (1- nlines)) - (beginning-of-line 1) - (cond - (arg - (move-to-column cc t) - (insert ":\n") - (forward-line -1)) - ((and off (looking-at re)) - (replace-match "" t t nil 1)) - ((not off) (move-to-column cc t) (insert ":"))) - (forward-line 1))))) + (if regionp + (save-excursion + (goto-char beg) + (setq cc (current-column)) + (beginning-of-line 1) + (setq off (looking-at re)) + (while (> nlines 0) + (setq nlines (1- nlines)) + (beginning-of-line 1) + (cond + (arg + (move-to-column cc t) + (insert ":\n") + (forward-line -1)) + ((and off (looking-at re)) + (replace-match "" t t nil 1)) + ((not off) (move-to-column cc t) (insert ":"))) + (forward-line 1))) + (save-excursion + (org-back-to-heading) + (if (looking-at (concat outline-regexp + "\\( +\\<" org-quote-string "\\>\\)")) + (replace-match "" t t nil 1) + (if (looking-at outline-regexp) + (progn + (goto-char (match-end 0)) + (insert " " org-quote-string)))))))) (defun org-export-as-html-and-open (arg) "Export the outline as HTML and immediately open it with a browser. @@ -8456,28 +8733,30 @@ headlines. The default is 3. Lower levels will become bulleted lists." (setq-default org-deadline-line-regexp org-deadline-line-regexp) (setq-default org-done-string org-done-string) (let* ((region-p (org-region-active-p)) - (region - (buffer-substring - (if region-p (region-beginning) (point-min)) - (if region-p (region-end) (point-max)))) - (all_lines - (org-skip-comments (org-split-string region "[\r\n]"))) - (lines (org-export-find-first-heading-line all_lines)) - (level 0) (line "") (origline "") txt todo - (umax nil) - (filename (concat (file-name-sans-extension (buffer-file-name)) - ".html")) - (buffer (find-file-noselect filename)) - (levels-open (make-vector org-level-max nil)) - (date (format-time-string "%Y/%m/%d" (current-time))) + (region + (buffer-substring + (if region-p (region-beginning) (point-min)) + (if region-p (region-end) (point-max)))) + (all_lines + (org-skip-comments (org-split-string region "[\r\n]"))) + (lines (org-export-find-first-heading-line all_lines)) + (level 0) (line "") (origline "") txt todo + (umax nil) + (filename (concat (file-name-sans-extension (buffer-file-name)) + ".html")) + (buffer (find-file-noselect filename)) + (levels-open (make-vector org-level-max nil)) + (date (format-time-string "%Y/%m/%d" (current-time))) (time (format-time-string "%X" (current-time))) - (author user-full-name) + (author user-full-name) (title (buffer-name)) - (options nil) + (options nil) + (quote-re (concat "^\\*+[ \t]*" org-quote-string "\\>")) + (inquote nil) (email user-mail-address) - (language org-export-default-language) + (language org-export-default-language) (text nil) - (lang-words nil) + (lang-words nil) (head-count 0) cnt (start 0) table-open type @@ -8491,22 +8770,22 @@ headlines. The default is 3. Lower levels will become bulleted lists." ;; Search for the export key lines (org-parse-key-lines) (setq lang-words (or (assoc language org-export-language-setup) - (assoc "en" org-export-language-setup))) + (assoc "en" org-export-language-setup))) ;; Switch to the output buffer (if (or hidden (not org-export-html-show-new-buffer)) - (set-buffer buffer) + (set-buffer buffer) (switch-to-buffer-other-window buffer)) (erase-buffer) (fundamental-mode) (let ((case-fold-search nil)) (if options (org-parse-export-options options)) (setq umax (if arg (prefix-numeric-value arg) - org-export-headline-levels)) + org-export-headline-levels)) ;; File header (insert (format - "<html lang=\"%s\"><head> + "<html lang=\"%s\"><head> <title>%s</title> <meta http-equiv=\"Content-Type\" content=\"text/html\"> <meta name=generator content=\"Org-mode\"> @@ -8514,15 +8793,15 @@ headlines. The default is 3. Lower levels will become bulleted lists." <meta name=author content=\"%s\"> </head><body> " - language (org-html-expand title) date time author)) + language (org-html-expand title) date time author)) (if title (insert (concat "<H1 align=\"center\">" (org-html-expand title) "</H1>\n"))) (if author (insert (concat (nth 1 lang-words) ": " author "\n"))) (if email (insert (concat "<a href=\"mailto:" email "\"><" - email "></a>\n"))) + email "></a>\n"))) (if (or author email) (insert "<br>\n")) (if (and date time) (insert (concat (nth 2 lang-words) ": " - date " " time "<br>\n"))) + date " " time "<br>\n"))) (if text (insert (concat "<p>\n" (org-html-expand text)))) (if org-export-with-toc (progn @@ -8577,123 +8856,141 @@ headlines. The default is 3. Lower levels will become bulleted lists." )) (setq head-count 0) (org-init-section-numbers) + (while (setq line (pop lines) origline line) - ;; Protect the links - (setq start 0) - (while (string-match org-link-maybe-angles-regexp line start) - (setq start (match-end 0)) - (setq line (replace-match - (concat "\000" (match-string 1 line) "\000") - t t line))) - - ;; replace "<" and ">" by "<" and ">" - ;; handle @<..> HTML tags (replace "@>..<" by "<..>") - (setq line (org-html-expand line)) - - ;; Verbatim lines - (if (and org-export-with-fixed-width - (string-match "^[ \t]*:\\(.*\\)" line)) + ;; end of quote? + (when (and inquote (string-match "^\\*+" line)) + (insert "</pre>\n") + (setq inquote nil)) + ;; inquote + (if inquote (progn - (let ((l (match-string 1 line))) - (while (string-match " " l) - (setq l (replace-match " " t t l))) - (insert "\n<span style='font-family:Courier'>" - l "</span>" - (if (and lines - (not (string-match "^[ \t]+\\(:.*\\)" - (car lines)))) - "<br>\n" "\n")))) + (insert line "\n") + (setq line (org-html-expand line))) ;;????? FIXME: not needed? + + ;; Protect the links (setq start 0) - (while (string-match org-protected-link-regexp line start) - (setq start (- (match-end 0) 2)) - (setq type (match-string 1 line)) - (cond - ((member type '("http" "https" "ftp" "mailto" "news")) - ;; standard URL - (setq line (replace-match -; "<a href=\"\\1:\\2\"><\\1:\\2></a>" - "<a href=\"\\1:\\2\">\\1:\\2</a>" - nil nil line))) - ((string= type "file") - ;; FILE link - (let* ((filename (match-string 2 line)) - (abs-p (file-name-absolute-p filename)) - (thefile (if abs-p (expand-file-name filename) filename)) - (thefile (save-match-data - (if (string-match ":[0-9]+$" thefile) - (replace-match "" t t thefile) - thefile))) - (file-is-image-p - (save-match-data - (string-match (org-image-file-name-regexp) thefile)))) + (while (string-match org-link-maybe-angles-regexp line start) + (setq start (match-end 0)) + (setq line (replace-match + (concat "\000" (match-string 1 line) "\000") + t t line))) + + ;; replace "<" and ">" by "<" and ">" + ;; handle @<..> HTML tags (replace "@>..<" by "<..>") + (setq line (org-html-expand line)) + + ;; Verbatim lines + (if (and org-export-with-fixed-width + (string-match "^[ \t]*:\\(.*\\)" line)) + (progn + (let ((l (match-string 1 line))) + (while (string-match " " l) + (setq l (replace-match " " t t l))) + (insert "\n<span style='font-family:Courier'>" + l "</span>" + (if (and lines + (not (string-match "^[ \t]+\\(:.*\\)" + (car lines)))) + "<br>\n" "\n")))) + + (setq start 0) + (while (string-match org-protected-link-regexp line start) + (setq start (- (match-end 0) 2)) + (setq type (match-string 1 line)) + (cond + ((member type '("http" "https" "ftp" "mailto" "news")) + ;; standard URL (setq line (replace-match - (if (and org-export-html-inline-images - file-is-image-p) - (concat "<img src=\"" thefile "\"/>") - (concat "<a href=\"" thefile "\">\\1:\\2</a>")) - nil nil line)))) - - ((member type '("bbdb" "vm" "wl" "rmail" "gnus" "shell")) - (setq line (replace-match - "<i><\\1:\\2></i>" nil nil line))))) - - ;; TODO items - (if (and (string-match org-todo-line-regexp line) - (match-beginning 2)) - (if (equal (match-string 2 line) org-done-string) + ; "<a href=\"\\1:\\2\"><\\1:\\2></a>" + "<a href=\"\\1:\\2\">\\1:\\2</a>" + nil nil line))) + ((string= type "file") + ;; FILE link + (let* ((filename (match-string 2 line)) + (abs-p (file-name-absolute-p filename)) + (thefile (if abs-p (expand-file-name filename) filename)) + (thefile (save-match-data + (if (string-match ":[0-9]+$" thefile) + (replace-match "" t t thefile) + thefile))) + (file-is-image-p + (save-match-data + (string-match (org-image-file-name-regexp) thefile)))) (setq line (replace-match - "<span style='color:green'>\\2</span>" - nil nil line 2)) - (setq line (replace-match "<span style='color:red'>\\2</span>" - nil nil line 2)))) + (if (and org-export-html-inline-images + file-is-image-p) + (concat "<img src=\"" thefile "\"/>") + (concat "<a href=\"" thefile "\">\\1:\\2</a>")) + nil nil line)))) - ;; DEADLINES - (if (string-match org-deadline-line-regexp line) - (progn - (if (save-match-data - (string-match "<a href" - (substring line 0 (match-beginning 0)))) - nil ; Don't do the replacement - it is inside a link - (setq line (replace-match "<span style='color:red'>\\&</span>" - nil nil line 1))))) + ((member type '("bbdb" "vm" "wl" "rmail" "gnus" "shell")) + (setq line (replace-match + "<i><\\1:\\2></i>" nil nil line))))) + + ;; TODO items + (if (and (string-match org-todo-line-regexp line) + (match-beginning 2)) + (if (equal (match-string 2 line) org-done-string) + (setq line (replace-match + "<span style='color:green'>\\2</span>" + nil nil line 2)) + (setq line (replace-match "<span style='color:red'>\\2</span>" + nil nil line 2)))) + + ;; DEADLINES + (if (string-match org-deadline-line-regexp line) + (progn + (if (save-match-data + (string-match "<a href" + (substring line 0 (match-beginning 0)))) + nil ; Don't do the replacement - it is inside a link + (setq line (replace-match "<span style='color:red'>\\&</span>" + nil nil line 1))))) - (cond - ((string-match "^\\(\\*+\\)[ \t]*\\(.*\\)" line) - ;; This is a headline - (setq level (- (match-end 1) (match-beginning 1)) - txt (match-string 2 line)) - (if (<= level umax) (setq head-count (+ head-count 1))) - (org-html-level-start level txt umax - (and org-export-with-toc (<= level umax)) - head-count)) - - ((and org-export-with-tables - (string-match "^\\([ \t]*\\)\\(|\\|\\+-+\\+\\)" line)) - (if (not table-open) - ;; New table starts - (setq table-open t table-buffer nil table-orig-buffer nil)) - ;; Accumulate lines - (setq table-buffer (cons line table-buffer) - table-orig-buffer (cons origline table-orig-buffer)) - (when (or (not lines) - (not (string-match "^\\([ \t]*\\)\\(|\\|\\+-+\\+\\)" - (car lines)))) - (setq table-open nil - table-buffer (nreverse table-buffer) - table-orig-buffer (nreverse table-orig-buffer)) - (insert (org-format-table-html table-buffer table-orig-buffer)))) - (t - ;; Normal lines - ;; Lines starting with "-", and empty lines make new paragraph. - (if (string-match "^ *-\\|^[ \t]*$" line) (insert "<p>")) - (insert line (if org-export-preserve-breaks "<br>\n" "\n")))) - )) - (if org-export-html-with-timestamp - (insert org-export-html-html-helper-timestamp)) - (insert "</body>\n</html>\n") - (normal-mode) - (save-buffer) - (goto-char (point-min))))) + + (cond + ((string-match "^\\(\\*+\\)[ \t]*\\(.*\\)" line) + ;; This is a headline + (setq level (- (match-end 1) (match-beginning 1)) + txt (match-string 2 line)) + (if (<= level umax) (setq head-count (+ head-count 1))) + (org-html-level-start level txt umax + (and org-export-with-toc (<= level umax)) + head-count) + ;; QUOTES + (when (string-match quote-re line) + (insert "<pre>") + (setq inquote t))) + + ((and org-export-with-tables + (string-match "^\\([ \t]*\\)\\(|\\|\\+-+\\+\\)" line)) + (if (not table-open) + ;; New table starts + (setq table-open t table-buffer nil table-orig-buffer nil)) + ;; Accumulate lines + (setq table-buffer (cons line table-buffer) + table-orig-buffer (cons origline table-orig-buffer)) + (when (or (not lines) + (not (string-match "^\\([ \t]*\\)\\(|\\|\\+-+\\+\\)" + (car lines)))) + (setq table-open nil + table-buffer (nreverse table-buffer) + table-orig-buffer (nreverse table-orig-buffer)) + (insert (org-format-table-html table-buffer table-orig-buffer)))) + (t + ;; Normal lines + ;; Lines starting with "-", and empty lines make new paragraph. + ;; FIXME: Should we add + and *? + (if (string-match "^ *-\\|^[ \t]*$" line) (insert "<p>")) + (insert line (if org-export-preserve-breaks "<br>\n" "\n")))) + ))) + (if org-export-html-with-timestamp + (insert org-export-html-html-helper-timestamp)) + (insert "</body>\n</html>\n") + (normal-mode) + (save-buffer) + (goto-char (point-min))))) (defun org-format-table-html (lines olines) "Find out which HTML converter to use and return the HTML code." @@ -9003,7 +9300,7 @@ stacked delimiters is N. Escaping delimiters is not possible." (if (string-match "\\`[A-Z]\\'" number-string) (aset org-section-numbers i (- (string-to-char number-string) ?A -1)) - (aset org-section-numbers i (string-to-int number-string))) + (aset org-section-numbers i (string-to-number number-string))) (pop numbers)) (setq i (1- i))))) @@ -9101,6 +9398,7 @@ When LEVEL is non-nil, increase section numbers on that level." (define-key org-mode-map "\C-c+" 'org-table-sum) (define-key org-mode-map "\C-c|" 'org-table-toggle-vline-visibility) (define-key org-mode-map "\C-c=" 'org-table-eval-formula) +(define-key org-mode-map "\C-c'" 'org-table-edit-formulas) (define-key org-mode-map "\C-c*" 'org-table-recalculate) (define-key org-mode-map [(control ?#)] 'org-table-rotate-recalc-marks) (define-key org-mode-map "\C-c~" 'org-table-create-with-table.el) @@ -9385,11 +9683,14 @@ scanning the buffer for these lines and updating the information." ["Fill Rectangle" org-table-wrap-region (org-at-table-p)]) "--" ("Calculate" - ["Eval Formula" org-table-eval-formula (org-at-table-p)] - ["Eval Formula Down" (org-table-eval-formula '(4)) :active (org-at-table-p) :keys "C-u C-c ="] + ["Set Column Formula" org-table-eval-formula (org-at-table-p)] + ["Set Named Field Formula" (org-table-eval-formula '(4)) :active (org-at-table-p) :keys "C-u C-c ="] + ["Edit Formulas" org-table-edit-formulas (org-at-table-p)] + "--" ["Recalculate line" org-table-recalculate (org-at-table-p)] ["Recalculate all" (lambda () (interactive) (org-table-recalculate '(4))) :active (org-at-table-p) :keys "C-u C-c *"] ["Toggle Recalculate Mark" org-table-rotate-recalc-marks (org-at-table-p)] + "--" ["Sum Column/Rectangle" org-table-sum (or (org-at-table-p) (org-region-active-p))] ["Which Column?" org-table-current-column (org-at-table-p)]) @@ -9768,14 +10069,23 @@ Show the heading too, if it is currently invisible." "\\):[ \t]*" (if org-noutline-p "\\(.+\\)" "\\([^\n\r]+\\)"))) -;; Advise the bookmark-jump function to make jump position visible -;; Wrapped into eval-after-load to avoid loading advice unnecessarily +;; Make `bookmark-jump' show the jump location if it was hidden. (eval-after-load "bookmark" - '(defadvice bookmark-jump (after org-make-visible activate) - "Make the position visible." - (and (eq major-mode 'org-mode) - (org-invisible-p) - (org-show-hierarchy-above)))) + '(if (boundp 'bookmark-after-jump-hook) + ;; We can use the hook + (add-hook 'bookmark-after-jump-hook 'org-bookmark-jump-unhide) + ;; Hook not available, use advice + (defadvice bookmark-jump (after org-make-visible activate) + "Make the position visible." + (org-bookmark-jump-unhide)))) + +(defun org-bookmark-jump-unhide () + "Unhide the current position, to show the bookmark location." + (and (eq major-mode 'org-mode) + (or (org-invisible-p) + (save-excursion (goto-char (max (point-min) (1- (point)))) + (org-invisible-p))) + (org-show-hierarchy-above))) ;;; Finish up |