Browse Source

Implement a minor mode for the editor following the cursor through the table

* doc/org.texi (Built-in table editor): Document the table field follow mode.
* lisp/org-table.el (org-table-exit-follow-field-mode-when-leaving-table):
New option.
(org-table-check-inside-data-field): New optional argument `noerror'.
When set, the function will only return nil instead of throwing an
error.
(org-table-edit-field): Interpret double prefix argument, and improve
the properties of the editing window.
(org-table-follow-field-mode): New minor mode.
(org-table-follow-fields-with-editor): New function.

The main purpose of this functionality is to make working with table
with long fields simpler, by always showing the full content of the
current field.  This functionality is based on the following
mailing list thread

http://thread.gmane.org/gmane.emacs.orgmode/41584

and contains ideas by Jonny, Juan Pechiar, and Michael Brand.
Carsten Dominik 9 years ago
parent
commit
bffdfeb7fc
2 changed files with 68 additions and 12 deletions
  1. 4 1
      doc/org.texi
  2. 64 11
      lisp/org-table.el

+ 4 - 1
doc/org.texi

@@ -2064,7 +2064,10 @@ increment.  This key is also used by shift-selection and related modes
 Edit the current field in a separate window.  This is useful for fields that
 are not fully visible (@pxref{Column width and alignment}).  When called with
 a @kbd{C-u} prefix, just make the full field visible, so that it can be
-edited in place.
+edited in place.  When called with two @kbd{C-u} prefixes, make the editor
+window follow the cursor through the table and always show the current
+field.  The follow mode exits automatically when the cursor leaves the table,
+or when you repeat this command with @kbd{C-u C-u C-c `}.
 @c
 @item M-x org-table-import
 Import a file as a table.  The table should be TAB or whitespace

+ 64 - 11
lisp/org-table.el

@@ -162,6 +162,16 @@ Only relevant when `org-enable-table-editor' is equal to `optimized'."
   :group 'org-table-editing
   :type 'boolean)
 
+(defcustom org-table-exit-follow-field-mode-when-leaving-table t
+  "Non-nil means automatically exit the follow mode.
+When nil, the follow mode will stay on and be active in any table
+the cursor enters.  Since the table follow filed mode messes with the
+window configuration, it is not recommended to set this variable to nil,
+except maybe locally in a special file that has mostly tables with long
+fields."
+  :group 'org-table
+  :type 'boolean)
+
 (defcustom org-table-fix-formulas-confirm nil
   "Whether the user should confirm when Org fixes formulas."
   :group 'org-table-editing
@@ -1045,7 +1055,7 @@ copying.  In the case of a timestamp, increment by one day."
 	  (org-move-to-column col))
       (error "No non-empty field found"))))
 
-(defun org-table-check-inside-data-field ()
+(defun org-table-check-inside-data-field (&optional noerror)
   "Is point inside a table data field?
 I.e. not on a hline or before the first or after the last column?
 This actually throws an error, so it aborts the current command."
@@ -1053,7 +1063,10 @@ This actually throws an error, so it aborts the current command."
 	  (= (org-table-current-column) 0)
 	  (org-at-table-hline-p)
 	  (looking-at "[ \t]*$"))
-      (error "Not in table data field")))
+      (if noerror
+	  nil
+	(error "Not in table data field"))
+    t))
 
 (defvar org-table-clip nil
   "Clipboard for table regions.")
@@ -1776,21 +1789,32 @@ This is mainly useful for fields that contain hidden parts.
 When called with a \\[universal-argument] prefix, just make the full field visible so that
 it can be edited in place."
   (interactive "P")
-  (if arg
-      (let ((b (save-excursion (skip-chars-backward "^|") (point)))
-	    (e (save-excursion (skip-chars-forward "^|\r\n") (point))))
-	(remove-text-properties b e '(org-cwidth t invisible t
-						 display t intangible t))
-	(if (and (boundp 'font-lock-mode) font-lock-mode)
-	    (font-lock-fontify-block)))
+  (cond
+   ((equal arg '(16))
+    (org-table-follow-field-mode (if org-table-follow-field-mode -1 1)))
+   (arg
+    (let ((b (save-excursion (skip-chars-backward "^|") (point)))
+	  (e (save-excursion (skip-chars-forward "^|\r\n") (point))))
+      (remove-text-properties b e '(org-cwidth t invisible t
+					       display t intangible t))
+      (if (and (boundp 'font-lock-mode) font-lock-mode)
+	  (font-lock-fontify-block))))
+   (t
     (let ((pos (move-marker (make-marker) (point)))
 	  (field (org-table-get-field))
 	  (cw (current-window-configuration))
 	  p)
-      (org-switch-to-buffer-other-window "*Org tmp*")
+      (goto-char pos)
+      (org-switch-to-buffer-other-window "*Org Table Edit Field*")
+      (when (and (local-variable-p 'org-field-marker)
+		 (markerp org-field-marker))
+	(move-marker org-field-marker nil))
       (erase-buffer)
       (insert "#\n# Edit field and finish with C-c C-c\n#\n")
       (let ((org-inhibit-startup t)) (org-mode))
+      (auto-fill-mode -1)
+      (setq truncate-lines nil)
+      (setq word-wrap t)
       (goto-char (setq p (point-max)))
       (insert (org-trim field))
       (remove-text-properties p (point-max)
@@ -1800,7 +1824,7 @@ it can be edited in place."
       (org-set-local 'org-finish-function 'org-table-finish-edit-field)
       (org-set-local 'org-window-configuration cw)
       (org-set-local 'org-field-marker pos)
-      (message "Edit and finish with C-c C-c"))))
+      (message "Edit and finish with C-c C-c")))))
 
 (defun org-table-finish-edit-field ()
   "Finish editing a table data field.
@@ -1825,6 +1849,35 @@ the table and kill the editing buffer."
     (org-table-align)
     (message "New field value inserted")))
 
+(define-minor-mode org-table-follow-field-mode
+  "Minor mode to make the table field editor window follow the cursor.
+When this mode is active, the field editor window will always show the
+current field.  The mode exits automatically when the cursor leaves the
+table (but see `org-table-exit-follow-field-mode-when-leaving-table')."
+  nil " TblFollow" nil
+  (if org-table-follow-field-mode
+      (org-add-hook 'post-command-hook 'org-table-follow-fields-with-editor
+		    'append 'local)
+    (remove-hook 'post-command-hook 'org-table-follow-fields-with-editor 'local)
+    (let* ((buf (get-buffer "*Org Table Edit Field*"))
+	   (win (and buf (get-buffer-window buf))))
+      (when win (delete-window win))
+      (when buf
+	(with-current-buffer buf
+	  (move-marker org-field-marker nil))
+	(kill-buffer buf)))))
+
+(defun org-table-follow-fields-with-editor ()
+  (if (and org-table-exit-follow-field-mode-when-leaving-table
+	   (not (org-at-table-p)))
+      ;; We have left the table, exit the follow mode
+      (org-table-follow-field-mode -1)
+    (when (org-table-check-inside-data-field 'noerror)
+      (let ((win (selected-window)))
+	(org-table-edit-field nil)
+	(org-fit-window-to-buffer)
+	(select-window win)))))
+
 (defvar org-timecnt) ; dynamically scoped parameter
 
 (defun org-table-sum (&optional beg end nlast)