diff options
author | Carsten Dominik <carsten.dominik@gmail.com> | 2011-03-08 14:49:53 +0100 |
---|---|---|
committer | Carsten Dominik <carsten.dominik@gmail.com> | 2011-03-17 06:50:39 +0100 |
commit | 3dd474575205d3808390fc6ea2d5feccdb3d4305 (patch) | |
tree | d5d2ca6bc6ae574af8914a90b9718021e1703e85 | |
parent | 706a0b509c693b49d0d515c02c2e8ed0343e21bc (diff) | |
download | org-mode-3dd474575205d3808390fc6ea2d5feccdb3d4305.tar.gz |
Tables: Make @< and $< point to row/column 1 in a stable way
-rw-r--r-- | doc/org.texi | 102 | ||||
-rw-r--r-- | lisp/org-table.el | 80 |
2 files changed, 99 insertions, 83 deletions
diff --git a/doc/org.texi b/doc/org.texi index 4cfa94f..6fc848b 100644 --- a/doc/org.texi +++ b/doc/org.texi @@ -2263,48 +2263,42 @@ field, or press @kbd{C-c @}} to toggle the display of a grid. Formulas can reference the value of another field in two ways. Like in any other spreadsheet, you may reference fields with a letter/number combination like @code{B3}, meaning the 2nd field in the 3rd row. - -@noindent @vindex org-table-use-standard-references -Org prefers@footnote{Org will understand references typed by the user as -@samp{B4}, but it will not use this syntax when offering a formula for -editing. You can customize this behavior using the variable +However, Org prefers@footnote{Org will understand references typed by the +user as @samp{B4}, but it will not use this syntax when offering a formula +for editing. You can customize this behavior using the variable @code{org-table-use-standard-references}.} to use another, more general -operator that looks like this: +representation that looks like this: @example @@@var{row}$@var{column} @end example -@noindent -and allows relative references, i.e. references relative to the -row/column of the field whose value is being computed. These relative -references make it possible to store a formula only once and use it in many -fields without copying and modifying it. - -Column references can be absolute like @samp{1}, @samp{2},...@samp{@var{N}}, -or relative to the current column like @samp{+1} or @samp{-2}. @code{$>} -references the last column in the table, and you can use offsets like -@code{$>-2}, meaning the third column from the right. +Column specifications can be absolute like @code{$1}, +@code{$2},...@code{$@var{N}}, or relative to the current column (i.e.@: the +column of the field which is being computed) like @code{$+1} or @code{$-2}. +@code{$<} and @code{$>} are immutable references to the first and last +column, respectively, and you can use @code{$>>>} to indicate the third +column from the right. The row specification only counts data lines and ignores horizontal separator lines (hlines). Like with columns, you can use absolute row numbers -@samp{1}...@samp{@var{N}}, and row numbers relative to the current row like -@samp{+3} or @samp{-1}, and @code{@@>} references the last row in the -table@footnote{For backward compatibility you can also use special names like -@samp{$LR5} and @samp{$LR12} to refer in a stable way to the 5th and 12th -field in the last row of the table. However, this syntax is deprecated, it -should not be used for new documents.}. You may also specify the row -relative to one of the hlines: @samp{I} refers to the first -hline@footnote{Note that only hlines are counted that @emph{separate} table -lines. If the table starts with a hline above the header, it does not -count.}, @samp{II} to the second, etc@. @samp{-I} refers to the first such -line above the current line, @samp{+I} to the first such line below the -current line. You can also write @samp{III+2} which is the second data line +@code{@@1}, @code{@@2},...@code{@@@var{N}}, and row numbers relative to the +current row like @code{@@+3} or @code{@@-1}. @code{@@<} and @code{@@>} are +immutable references the first and last@footnote{For backward compatibility +you can also use special names like @code{$LR5} and @code{$LR12} to refer in +a stable way to the 5th and 12th field in the last row of the table. +However, this syntax is deprecated, it should not be used for new documents. +Use @code{@@>$} instead.} row in the table, respectively. You may also +specify the row relative to one of the hlines: @code{@@I} refers to the first +hline, @code{@@II} to the second, etc@. @code{@@-I} refers to the first such +line above the current line, @code{@@+I} to the first such line below the +current line. You can also write @code{@@III+2} which is the second data line after the third hline in the table. -@samp{0} refers to the current row and column. Also, if you omit -either the column or the row part of the reference, the current -row/column is implied. +@code{@@0} and @code{$0} refer to the current row and column, respectively, +i.e. to the row/column for the field being computed. Also, if you omit +either the column or the row part of the reference, the current row/column is +implied. Org's references with @emph{unsigned} numbers are fixed references in the sense that if you use the same reference in the formula for two @@ -2313,17 +2307,15 @@ Org's references with @emph{signed} numbers are floating references because the same reference operator can reference different fields depending on the field being calculated by the formula. - Here are a few examples: @example -@@2$3 @r{2nd row, 3rd column} -C2 @r{same as previous} -$5 @r{column 5 in the current row} -E& @r{same as previous} +@@2$3 @r{2nd row, 3rd column (same as @code{C2})} +$5 @r{column 5 in the current row (same as @code{E&})} @@2 @r{current column, row 2} @@-1$-3 @r{the field one row up, three columns to the left} @@-I$2 @r{field just under hline above current row, column 2} +@@>$5 @r{field in the last row, in column 5} @end example @subsubheading Range references @@ -2338,12 +2330,12 @@ format at least for the first field (i.e the reference must start with @samp{@@} in order to be interpreted correctly). Examples: @example -$1..$3 @r{First three fields in the current row} -$P..$Q @r{Range, using column names (see under Advanced)} -@@2$1..@@4$3 @r{6 fields between these two fields} -A2..C4 @r{Same as above} +$1..$3 @r{first three fields in the current row} +$P..$Q @r{range, using column names (see under Advanced)} +$<<<..$>> @r{start in third column, continue to the one but last} +@@2$1..@@4$3 @r{6 fields between these two fields (same as @code{A2..C4})} @@-1$-2..@@-1 @r{3 numbers from the column to the left, 2 up to current row} -@@I..II @r{Between first and second hline, short for @code{@@I..@@II}} +@@I..II @r{between first and second hline, short for @code{@@I..@@II}} @end example @noindent Range references return a vector of values that can be fed @@ -2505,7 +2497,7 @@ taylor($3,x=7,2) @r{Taylor series of $3, at x=7, second degree} Calc also contains a complete set of logical operations. For example @example -if($1<20,teen,string("")) @r{``teen'' if age $1 less than 20, else empty} +if($1<20,teen,string("")) @r{"teen" if age $1 less than 20, else empty} @end example @node Formula syntax for Lisp, Field and range formulas, Formula syntax for Calc, The spreadsheet @@ -2553,16 +2545,20 @@ the formula will be stored as the formula for this field, evaluated, and the current field will be replaced with the result. @cindex #+TBLFM -Formulas are stored in a special line starting with @samp{#+TBLFM:} -directly below the table. If you type the equation in the 4th field of -the 3rd data line in the table, the formula will look like -@samp{@@3$4=$1+$2}. When inserting/deleting/swapping column and rows -with the appropriate commands, @i{absolute references} (but not relative -ones) in stored formulas are modified in order to still reference the -same field. Of course this is not true if you edit the table structure -with normal editing commands---then you must fix the equations yourself. -Instead of typing an equation into the field, you may also use the -following command +Formulas are stored in a special line starting with @samp{#+TBLFM:} directly +below the table. If you type the equation in the 4th field of the 3rd data +line in the table, the formula will look like @samp{@@3$4=$1+$2}. When +inserting/deleting/swapping column and rows with the appropriate commands, +@i{absolute references} (but not relative ones) in stored formulas are +modified in order to still reference the same field. To avoid this from +happening, in particular in range references, anchor ranges at the table +borders (using @code{@@<}, @code{@@>}, @code{$<}, @code{$>}), or at hlines +using the @code{@@I} notation. Automatic adaptation of field references does +of cause not happen if you edit the table structure with normal editing +commands---then you must fix the equations yourself. + +Instead of typing an equation into the field, you may also use the following +command @table @kbd @orgcmd{C-u C-c =,org-table-eval-formula} @@ -2582,7 +2578,7 @@ directly. Column formula, valid for the entire column. This is so common that Org treats these formulas in a special way, see @ref{Column formulas}. @item @@3= -Row formula, applies to all fields in the specified row. @code{@@L=} means +Row formula, applies to all fields in the specified row. @code{@@>=} means the last row. @item @@1$2..@@4$3= Range formula, applies to all fields in the given rectangular range. This diff --git a/lisp/org-table.el b/lisp/org-table.el index 54bc3ae..458c95e 100644 --- a/lisp/org-table.el +++ b/lisp/org-table.el @@ -1144,7 +1144,8 @@ is always the old value." (eql (org-table-expand-lhs-ranges (mapcar (lambda (e) - (cons (org-table-formula-handle-lastrc (car e)) (cdr e))) + (cons (org-table-formula-handle-first/last-rc + (car e)) (cdr e))) (org-table-get-stored-formulas)))) (dline (org-table-current-dline)) (ref (format "@%d$%d" dline col)) @@ -1999,15 +2000,23 @@ When NAMED is non-nil, look for a named equation." "\n"))) (defsubst org-table-formula-make-cmp-string (a) - (when (string-match "\\`$>" a) - ;; Fake a high number to make sure this is sorted at the end. - (setq a (org-table-formula-handle-lastrc a)) - (setq a (format "$%d" (+ 10000 (string-to-number (substring a 1)))))) - (when (string-match "^\\(@\\([0-9]+\\)\\)?\\(\\$?\\([0-9]+\\)\\)?\\(\\$?[a-zA-Z0-9]+\\)?" a) + (when (string-match "\\`$[<>]" a) + (let ((arrow (string-to-char (substring a 1)))) + ;; Fake a high number to make sure this is sorted at the end. + (setq a (org-table-formula-handle-first/last-rc a)) + (setq a (format "$%d" (+ 10000 + (if (= arrow ?<) -1000 0) + (string-to-number (substring a 1))))))) + (when (string-match + "^\\(@\\([0-9]+\\)\\)?\\(\\$?\\([0-9]+\\)\\)?\\(\\$?[a-zA-Z0-9]+\\)?" + a) (concat - (if (match-end 2) (format "@%05d" (string-to-number (match-string 2 a))) "") - (if (match-end 4) (format "$%05d" (string-to-number (match-string 4 a))) "") - (if (match-end 5) (concat "@@" (match-string 5 a)))))) + (if (match-end 2) + (format "@%05d" (string-to-number (match-string 2 a))) "") + (if (match-end 4) + (format "$%05d" (string-to-number (match-string 4 a))) "") + (if (match-end 5) + (concat "@@" (match-string 5 a)))))) (defun org-table-formula-less-p (a b) "Compare two formulas for sorting." @@ -2025,11 +2034,12 @@ When NAMED is non-nil, look for a named equation." (setq strings (org-split-string (org-match-string-no-properties 2) " *:: *")) (while (setq string (pop strings)) - (when (string-match "\\`\\(@[-+I>0-9.$@]+\\|@?[0-9]+\\|\\$\\([a-zA-Z0-9]+\\|>\\(?:[-+][0-9]+\\)?\\)\\) *= *\\(.*[^ \t]\\)" string) + (when (string-match "\\`\\(@[-+I<>0-9.$@]+\\|@?[0-9]+\\|\\$\\([a-zA-Z0-9]+\\|[<>]+\\)\\) *= *\\(.*[^ \t]\\)" string) (setq scol (if (match-end 2) (match-string 2 string) (match-string 1 string)) - scol (if (= (string-to-char scol) ?>) (concat "$" scol) scol) + scol (if (member (string-to-char scol) '(?< ?>)) + (concat "$" scol) scol) eq (match-string 3 string) eq-alist (cons (cons scol eq) eq-alist)) (if (member scol seen) @@ -2673,19 +2683,19 @@ known that the table will be realigned a little later anyway." ;; Insert constants in all formulas (setq eqlist (mapcar (lambda (x) - (when (string-match "\\`$>" (car x)) + (when (string-match "\\`$[<>]" (car x)) (setq lhs1 (car x)) (setq x (cons (substring - (org-table-formula-handle-lastrc + (org-table-formula-handle-first/last-rc (car x)) 1) (cdr x))) (if (assoc (car x) eqlist1) (error "\"%s=\" formula tries to overwrite existing formula for column %s" lhs1 (car x)))) (cons - (org-table-formula-handle-lastrc (car x)) + (org-table-formula-handle-first/last-rc (car x)) (org-table-formula-substitute-names - (org-table-formula-handle-lastrc (cdr x))))) + (org-table-formula-handle-first/last-rc (cdr x))))) eqlist)) ;; Split the equation list (while (setq eq (pop eqlist)) @@ -2868,19 +2878,29 @@ them to individual field equations for each field." :orig-eqn e (caar res))))))) (nreverse res))) -(defun org-table-formula-handle-lastrc (s) - "Replace @> / $> with the last row/column of the table." - (while (string-match "\\([@$]\\)>\\(-[0-9]+\\)?" s) - (setq s (replace-match - (format "%s%d" - (match-string 1 s) - (+ (if (equal (match-string 1 s) "@") - (1- (length org-table-dlines)) - org-table-current-ncol) - (if (match-end 2) - (string-to-number (match-string 2 s)) - 0))) - t t s))) +(defun org-table-formula-handle-first/last-rc (s) + "Replace @<, @>, $<, $> with first/last row/column of the table. +So @< and $< will always be replaced with @1 and $1, respectively. +The advantage of these special markers are that structure editing of +the table will not change them, while @1 and $1 will be modified +when a line/row is swaped out of that privileged position. So for +formulas that use a range of rows or columns, it may often be better +to anchor the formula with \"I\" row markers, or to offset from the +borders of the table using the @< @> $< $> makers." + (let (n nmax len) + (while (string-match "\\([@$]\\)\\(<+\\|>+\\)" s) + (setq nmax (if (equal (match-string 1 s) "@") + (1- (length org-table-dlines)) + org-table-current-ncol) + len (- (match-end 2) (match-beginning 2)) + char (string-to-char (match-string 2 s)) + n (if (= char ?<) + len + (- nmax len -1))) + (if (or (< n 1) (> n nmax)) + (error "Reference \"%s\" in expression \"%s\" points outside table" + (match-string 0 s) s)) + (setq s (replace-match (format "%s%d" (match-string 1 s) n) t t s)))) s) (defun org-table-formula-substitute-names (f) @@ -3003,7 +3023,7 @@ Parameters get priority." (setq startline (org-current-line)) (while (setq entry (pop eql)) (setq type (cond - ((string-match "\\`$>" (car entry)) 'column) + ((string-match "\\`$[<>]" (car entry)) 'column) ((equal (string-to-char (car entry)) ?@) 'field) ((string-match "^[0-9]" (car entry)) 'column) (t 'named))) @@ -3227,7 +3247,7 @@ With prefix ARG, apply the new formulas to the table." (let ((pos org-pos) (sel-win org-selected-window) eql var form) (goto-char (point-min)) (while (re-search-forward - "^\\(@[-+I>0-9.$@]+\\|@?[0-9]+\\|\\$\\([a-zA-Z0-9]+\\|>[-+0-9]*\\)\\) *= *\\(.*\\(\n[ \t]+.*$\\)*\\)" + "^\\(@[-+I<>0-9.$@]+\\|@?[0-9]+\\|\\$\\([a-zA-Z0-9]+\\|[<>]+\\)\\) *= *\\(.*\\(\n[ \t]+.*$\\)*\\)" nil t) (setq var (if (match-end 2) (match-string 2) (match-string 1)) form (match-string 3)) |