summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarsten Dominik <carsten.dominik@gmail.com>2011-03-08 14:49:53 +0100
committerCarsten Dominik <carsten.dominik@gmail.com>2011-03-17 06:50:39 +0100
commit3dd474575205d3808390fc6ea2d5feccdb3d4305 (patch)
treed5d2ca6bc6ae574af8914a90b9718021e1703e85
parent706a0b509c693b49d0d515c02c2e8ed0343e21bc (diff)
downloadorg-mode-3dd474575205d3808390fc6ea2d5feccdb3d4305.tar.gz
Tables: Make @< and $< point to row/column 1 in a stable way
-rw-r--r--doc/org.texi102
-rw-r--r--lisp/org-table.el80
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))