Browse Source

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

Carsten Dominik 9 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.
 
 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
-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
 @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.}, 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
 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
 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
-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.
 
 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
 @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
-the numeric column reference.
+the numeric column reference or @code{$>}.
 
 Instead of typing an equation into the field, you may also use the
 following command:

+ 44 - 19
lisp/org-table.el

@@ -1133,7 +1133,7 @@ is always the old value."
 	   (eql (org-table-expand-lhs-ranges
 		 (mapcar
 		  (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))))
 	   (dline (org-table-current-dline))
 	   (ref (format "@%d$%d" dline col))
@@ -1975,6 +1975,10 @@ 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)
     (concat
      (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
       (goto-char (org-table-end))
       (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))
-	  (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)
 			   (match-string 2 string)
 			 (match-string 1 string))
+		  scol (if (= (string-to-char scol) ?>) (concat "$" scol) scol)
 		  eq (match-string 3 string)
 		  eq-alist (cons (cons scol eq) eq-alist))
 	    (if (member scol seen)
@@ -2632,25 +2638,36 @@ known that the table will be realigned a little later anyway."
     (org-table-get-specials)
     (let* ((eqlist (sort (org-table-get-stored-formulas)
 			 (lambda (a b) (string< (car a) (car b)))))
+	   (eqlist1 (copy-sequence eqlist))
 	   (inhibit-redisplay (not debug-on-error))
 	   (line-re org-table-dataline-regexp)
 	   (thisline (org-current-line))
 	   (thiscol (org-table-current-column))
-	   seen-fields
+	   seen-fields lhs1
 	   beg end entry eqlnum eqlname eqlname1 eql (cnt 0) eq a name name1)
       ;; Insert constants in all formulas
       (setq eqlist
 	    (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
-		       (org-table-formula-handle-@L (car x))
+		       (org-table-formula-handle-lastrc (car x))
 		       (org-table-formula-substitute-names
-			(org-table-formula-handle-@L (cdr x)))))
+			(org-table-formula-handle-lastrc (cdr x)))))
 		    eqlist))
       ;; Split the equation list
       (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))
       ;; Expand ranges in lhs of formulas
       (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))
 	  (push (append a (list (cdr eq))) eqlname1)
 	  (org-table-put-field-property :org-untouchable t)))
+      (setq eqlname1 (nreverse eqlname1))
       
       ;; Now evaluate the column formulas, but skip fields covered by
       ;; field formulas
@@ -2742,7 +2760,9 @@ known that the table will be realigned a little later anyway."
 	  (and all (message "Re-applying formulas...done"))))))
 
 (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")
   (let ((imax (if arg (prefix-numeric-value arg) 10))
 	(i 0)
@@ -2824,14 +2844,18 @@ them to individual field equations for each field."
 				       :orig-eqn e (caar 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
-	     (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)))
   s)
 
@@ -2955,6 +2979,7 @@ Parameters get priority."
     (setq startline (org-current-line))
     (while (setq entry (pop eql))
       (setq type (cond
+		  ((string-match "\\`$>" (car entry)) 'column)
 		  ((equal (string-to-char (car entry)) ?@) 'field)
 		  ((string-match "^[0-9]" (car entry)) 'column)
 		  (t 'named)))
@@ -2963,7 +2988,7 @@ Parameters get priority."
 	(insert (org-add-props (cdr title) nil 'face font-lock-comment-face))
 	(setq titles (remove title titles)))
       (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"))
       (remove-text-properties 0 (length s) '(face nil) 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)
     (goto-char (point-min))
     (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)
       (setq var (if (match-end 2) (match-string 2) (match-string 1))
 	    form (match-string 3))