Browse Source

Use @> and $> for referring to last row and column

Carsten Dominik 14 years ago
parent
commit
5631a309ab
2 changed files with 60 additions and 33 deletions
  1. 16 14
      doc/org.texi
  2. 44 19
      lisp/org-table.el

+ 16 - 14
doc/org.texi

@@ -2286,23 +2286,25 @@ references make it possible to store a formula only once and use it in many
 fields without copying and modifying it.
 fields without copying and modifying it.
 
 
 Column references can be absolute like @samp{1}, @samp{2},...@samp{@var{N}},
 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}.
+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.
 
 
 The row specification only counts data lines and ignores horizontal separator
 The row specification only counts data lines and ignores horizontal separator
-lines (hlines).  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}.  A
-special case is @code{@@L} which stands for the last row in the
+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
 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
 @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
 field in the last row of the table.  However, this syntax is deprecated, it
-should not be used for new documents.}, and you can use offsets from it like
-@code{@@L-2}.  Or 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 after the third hline in the table.
+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
+after the third hline in the table.
 
 
 @samp{0} refers to the current row and column.  Also, if you omit
 @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
 either the column or the row part of the reference, the current
@@ -2603,7 +2605,7 @@ same formula will be used in all fields of that column, with the following
 very convenient exceptions: (i) If the table contains horizontal separator
 very convenient exceptions: (i) If the table contains horizontal separator
 hlines, everything before the first such line is considered part of the table
 hlines, everything before the first such line is considered part of the table
 @emph{header} and will not be modified by column formulas.  (ii) Fields that
 @emph{header} and will not be modified by column formulas.  (ii) Fields that
-already get a value from a fild/range formula will be left alone by column
+already get a value from a field/range formula will be left alone by column
 formulas.  These conditions make column formulas very easy to use.
 formulas.  These conditions make column formulas very easy to use.
 
 
 To assign a formula to a column, type it directly into any field in the
 To assign a formula to a column, type it directly into any field in the
@@ -2615,7 +2617,7 @@ and the current field replaced with the result.  If the field contains only
 column, Org will only remember the most recently used formula.  In the
 column, Org will only remember the most recently used formula.  In the
 @samp{#+TBLFM:} line, column formulas will look like @samp{$4=$1+$2}.  The
 @samp{#+TBLFM:} line, column formulas will look like @samp{$4=$1+$2}.  The
 left-hand side of a column formula can not be the name of column, it must be
 left-hand side of a column formula can not be the name of column, it must be
-the numeric column reference.
+the numeric column reference or @code{$>}.
 
 
 Instead of typing an equation into the field, you may also use the
 Instead of typing an equation into the field, you may also use the
 following command:
 following command:

+ 44 - 19
lisp/org-table.el

@@ -1133,7 +1133,7 @@ is always the old value."
 	   (eql (org-table-expand-lhs-ranges
 	   (eql (org-table-expand-lhs-ranges
 		 (mapcar
 		 (mapcar
 		  (lambda (e)
 		  (lambda (e)
-		    (cons (org-table-formula-handle-@L (car e)) (cdr e)))
+		    (cons (org-table-formula-handle-lastrc (car e)) (cdr e)))
 		  (org-table-get-stored-formulas))))
 		  (org-table-get-stored-formulas))))
 	   (dline (org-table-current-dline))
 	   (dline (org-table-current-dline))
 	   (ref (format "@%d$%d" dline col))
 	   (ref (format "@%d$%d" dline col))
@@ -1975,6 +1975,10 @@ When NAMED is non-nil, look for a named equation."
 	    "\n")))
 	    "\n")))
 
 
 (defsubst org-table-formula-make-cmp-string (a)
 (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 "^\\(@\\([0-9]+\\)\\)?\\(\\$?\\([0-9]+\\)\\)?\\(\\$?[a-zA-Z0-9]+\\)?" a)
     (concat
     (concat
      (if (match-end 2) (format "@%05d" (string-to-number (match-string 2 a))) "")
      (if (match-end 2) (format "@%05d" (string-to-number (match-string 2 a))) "")
@@ -1994,12 +1998,14 @@ When NAMED is non-nil, look for a named equation."
     (save-excursion
     (save-excursion
       (goto-char (org-table-end))
       (goto-char (org-table-end))
       (when (looking-at "\\([ \t]*\n\\)*[ \t]*#\\+TBLFM: *\\(.*\\)")
       (when (looking-at "\\([ \t]*\n\\)*[ \t]*#\\+TBLFM: *\\(.*\\)")
-	(setq strings (org-split-string (match-string 2) " *:: *"))
+	(setq strings (org-split-string (org-match-string-no-properties 2)
+					" *:: *"))
 	(while (setq string (pop strings))
 	(while (setq string (pop strings))
-	  (when (string-match "\\`\\(@[-+LI0-9.$@]+\\|@?[0-9]+\\|\\$\\([a-zA-Z0-9]+\\)\\) *= *\\(.*[^ \t]\\)" string)
+	  (when (string-match "\\`\\(@[-+I>0-9.$@]+\\|@?[0-9]+\\|\\$\\([a-zA-Z0-9]+\\|>\\(?:[-+][0-9]+\\)?\\)\\) *= *\\(.*[^ \t]\\)" string)
 	    (setq scol (if (match-end 2)
 	    (setq scol (if (match-end 2)
 			   (match-string 2 string)
 			   (match-string 2 string)
 			 (match-string 1 string))
 			 (match-string 1 string))
+		  scol (if (= (string-to-char scol) ?>) (concat "$" scol) scol)
 		  eq (match-string 3 string)
 		  eq (match-string 3 string)
 		  eq-alist (cons (cons scol eq) eq-alist))
 		  eq-alist (cons (cons scol eq) eq-alist))
 	    (if (member scol seen)
 	    (if (member scol seen)
@@ -2632,25 +2638,36 @@ known that the table will be realigned a little later anyway."
     (org-table-get-specials)
     (org-table-get-specials)
     (let* ((eqlist (sort (org-table-get-stored-formulas)
     (let* ((eqlist (sort (org-table-get-stored-formulas)
 			 (lambda (a b) (string< (car a) (car b)))))
 			 (lambda (a b) (string< (car a) (car b)))))
+	   (eqlist1 (copy-sequence eqlist))
 	   (inhibit-redisplay (not debug-on-error))
 	   (inhibit-redisplay (not debug-on-error))
 	   (line-re org-table-dataline-regexp)
 	   (line-re org-table-dataline-regexp)
 	   (thisline (org-current-line))
 	   (thisline (org-current-line))
 	   (thiscol (org-table-current-column))
 	   (thiscol (org-table-current-column))
-	   seen-fields
+	   seen-fields lhs1
 	   beg end entry eqlnum eqlname eqlname1 eql (cnt 0) eq a name name1)
 	   beg end entry eqlnum eqlname eqlname1 eql (cnt 0) eq a name name1)
       ;; Insert constants in all formulas
       ;; Insert constants in all formulas
       (setq eqlist
       (setq eqlist
 	    (mapcar (lambda (x)
 	    (mapcar (lambda (x)
+		      (when (string-match "\\`$>" (car x))
+			(setq lhs1 (car x))
+			(setq x (cons (substring
+				       (org-table-formula-handle-lastrc
+					(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
 		      (cons
-		       (org-table-formula-handle-@L (car x))
+		       (org-table-formula-handle-lastrc (car x))
 		       (org-table-formula-substitute-names
 		       (org-table-formula-substitute-names
-			(org-table-formula-handle-@L (cdr x)))))
+			(org-table-formula-handle-lastrc (cdr x)))))
 		    eqlist))
 		    eqlist))
       ;; Split the equation list
       ;; Split the equation list
       (while (setq eq (pop eqlist))
       (while (setq eq (pop eqlist))
-	(if (<= (string-to-char (car eq)) ?9)
-	    (push eq eqlnum)
-	  (push eq eqlname)))
+	(cond
+	 ((<= (string-to-char (car eq)) ?9)
+	  (push eq eqlnum))
+	 (t (push eq eqlname))))
       (setq eqlnum (nreverse eqlnum) eqlname (nreverse eqlname))
       (setq eqlnum (nreverse eqlnum) eqlname (nreverse eqlname))
       ;; Expand ranges in lhs of formulas
       ;; Expand ranges in lhs of formulas
       (setq eqlname (org-table-expand-lhs-ranges eqlname))
       (setq eqlname (org-table-expand-lhs-ranges eqlname))
@@ -2702,6 +2719,7 @@ known that the table will be realigned a little later anyway."
 	  (org-table-goto-column (nth 2 a))
 	  (org-table-goto-column (nth 2 a))
 	  (push (append a (list (cdr eq))) eqlname1)
 	  (push (append a (list (cdr eq))) eqlname1)
 	  (org-table-put-field-property :org-untouchable t)))
 	  (org-table-put-field-property :org-untouchable t)))
+      (setq eqlname1 (nreverse eqlname1))
       
       
       ;; Now evaluate the column formulas, but skip fields covered by
       ;; Now evaluate the column formulas, but skip fields covered by
       ;; field formulas
       ;; field formulas
@@ -2742,7 +2760,9 @@ known that the table will be realigned a little later anyway."
 	  (and all (message "Re-applying formulas...done"))))))
 	  (and all (message "Re-applying formulas...done"))))))
 
 
 (defun org-table-iterate (&optional arg)
 (defun org-table-iterate (&optional arg)
-  "Recalculate the table until it does not change anymore."
+  "Recalculate the table until it does not change anymore.
+The maximun number of iterations is 10, but you can chose a different value
+with the prefix ARG."
   (interactive "P")
   (interactive "P")
   (let ((imax (if arg (prefix-numeric-value arg) 10))
   (let ((imax (if arg (prefix-numeric-value arg) 10))
 	(i 0)
 	(i 0)
@@ -2824,14 +2844,18 @@ them to individual field equations for each field."
 				       :orig-eqn e (caar res)))))))
 				       :orig-eqn e (caar res)))))))
     (nreverse res)))
     (nreverse res)))
 
 
-(defun org-table-formula-handle-@L (s)
-  "Replace @L with the last row data row of the table."
-  (while (string-match "@L\\(-[0-9]+\\)?" s)
+(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
     (setq s (replace-match
-	     (format "@%d" (+ (length org-table-dlines) -1
-			      (if (match-end 1)
-				  (string-to-number (match-string 1 s))
-				0)))
+	     (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)))
 	     t t s)))
   s)
   s)
 
 
@@ -2955,6 +2979,7 @@ Parameters get priority."
     (setq startline (org-current-line))
     (setq startline (org-current-line))
     (while (setq entry (pop eql))
     (while (setq entry (pop eql))
       (setq type (cond
       (setq type (cond
+		  ((string-match "\\`$>" (car entry)) 'column)
 		  ((equal (string-to-char (car entry)) ?@) 'field)
 		  ((equal (string-to-char (car entry)) ?@) 'field)
 		  ((string-match "^[0-9]" (car entry)) 'column)
 		  ((string-match "^[0-9]" (car entry)) 'column)
 		  (t 'named)))
 		  (t 'named)))
@@ -2963,7 +2988,7 @@ Parameters get priority."
 	(insert (org-add-props (cdr title) nil 'face font-lock-comment-face))
 	(insert (org-add-props (cdr title) nil 'face font-lock-comment-face))
 	(setq titles (remove title titles)))
 	(setq titles (remove title titles)))
       (if (equal key (car entry)) (setq startline (org-current-line)))
       (if (equal key (car entry)) (setq startline (org-current-line)))
-      (setq s (concat (if (equal (string-to-char (car entry)) ?@) "" "$")
+      (setq s (concat (if (member (string-to-char (car entry)) '(?@ ?$)) "" "$")
 		      (car entry) " = " (cdr entry) "\n"))
 		      (car entry) " = " (cdr entry) "\n"))
       (remove-text-properties 0 (length s) '(face nil) s)
       (remove-text-properties 0 (length s) '(face nil) s)
       (insert s))
       (insert s))
@@ -3178,7 +3203,7 @@ With prefix ARG, apply the new formulas to the table."
   (let ((pos org-pos) (sel-win org-selected-window) eql var form)
   (let ((pos org-pos) (sel-win org-selected-window) eql var form)
     (goto-char (point-min))
     (goto-char (point-min))
     (while (re-search-forward
     (while (re-search-forward
-	    "^\\(@[-+IL0-9.$@]+\\|@?[0-9]+\\|\\$\\([a-zA-Z0-9]+\\)\\) *= *\\(.*\\(\n[ \t]+.*$\\)*\\)"
+	    "^\\(@[-+I>0-9.$@]+\\|@?[0-9]+\\|\\$\\([a-zA-Z0-9]+\\|>[-+0-9]*\\)\\) *= *\\(.*\\(\n[ \t]+.*$\\)*\\)"
 	    nil t)
 	    nil t)
       (setq var (if (match-end 2) (match-string 2) (match-string 1))
       (setq var (if (match-end 2) (match-string 2) (match-string 1))
 	    form (match-string 3))
 	    form (match-string 3))