Browse Source

org-table: Improve shrinking on right-aligned and centered columns

* lisp/org-table.el (org-table--make-shrinking-overlay): New function.
(org-table--shrink-field): Use new function.
(org-table--shrink-columns): Update function.
* testing/lisp/test-org-table.el (test-org-table/toggle-column-width):
  Update test.
Nicolas Goaziou 7 years ago
parent
commit
e462125cfc
2 changed files with 87 additions and 49 deletions
  1. 84 46
      lisp/org-table.el
  2. 3 3
      testing/lisp/test-org-table.el

+ 84 - 46
lisp/org-table.el

@@ -3879,6 +3879,33 @@ When non-nil, return the overlay narrowing the field."
 	(when (org-table--shrunk-field) (push column shrunk)))
 	(when (org-table--shrunk-field) (push column shrunk)))
       (nreverse shrunk))))
       (nreverse shrunk))))
 
 
+(defun org-table--make-shrinking-overlay (start end display field &optional pre)
+  "Create an overlay to shrink text between START and END.
+
+Use string DISPLAY instead of the real text between the two
+buffer positions.  FIELD is the real contents of the field, as
+a string, or nil.  It is meant to be displayed upon moving the
+mouse onto the overlay.
+
+Return the overlay."
+  (let ((show-before-edit
+	 (lambda (o &rest _)
+	   ;; Removing one overlay removes all other overlays in the
+	   ;; same column.
+	   (mapc #'delete-overlay
+		 (cdr (overlay-get o 'org-table-column-overlays)))))
+	(o (make-overlay start end)))
+    (overlay-put o 'insert-behind-hooks (and (not pre) (list show-before-edit)))
+    (overlay-put o 'insert-in-front-hooks (list show-before-edit))
+    (overlay-put o 'modification-hooks (list show-before-edit))
+    (overlay-put o 'org-overlay-type 'table-column-hide)
+    (when (stringp field) (overlay-put o 'help-echo field))
+    ;; Make sure overlays stays on top of table coordinates overlays.
+    ;; See `org-table-overlay-coordinates'.
+    (overlay-put o 'priority 1)
+    (org-overlay-display o display 'org-table t)
+    o))
+
 (defun org-table--shrink-field (width start end contents)
 (defun org-table--shrink-field (width start end contents)
   "Shrink a table field to a specified width.
   "Shrink a table field to a specified width.
 
 
@@ -3888,13 +3915,13 @@ and END are, respectively, the beginning and ending positions of
 the field.  CONTENTS is its trimmed contents, as a string, or
 the field.  CONTENTS is its trimmed contents, as a string, or
 `hline' for table rules.
 `hline' for table rules.
 
 
-Real field is hidden under an overlay.  The latter has the
+Real field is hidden under one or two overlays.  They have the
 following properties:
 following properties:
 
 
   `org-overlay-type'
   `org-overlay-type'
 
 
     Set to `table-column-hide'.  Used to identify overlays
     Set to `table-column-hide'.  Used to identify overlays
-    responsible for the task.
+    responsible for shrinking columns in a table.
 
 
   `org-table-column-overlays'
   `org-table-column-overlays'
 
 
@@ -3906,48 +3933,58 @@ Whenever the text behind or next to the overlay is modified, all
 the overlays in the column are deleted, effectively displaying
 the overlays in the column are deleted, effectively displaying
 the column again.
 the column again.
 
 
-Return overlay hiding the field."
-  (unless (org-table--shrunk-field)
-    (let* ((overlay-start
-	    (cond
-	     ((= 0 width) start)		 ;hide everything
-	     ((<= (- end start) 1) start)	 ;column too short
-	     ((>= width (- end start)) (1- end)) ;enough room
-	     ((eq contents 'hline) (+ start width))
-	     (t
-	      ;; Find cut location so that WIDTH characters are
-	      ;; visible.
-	      (let* ((begin start)
-		     (lower begin)
-		     (upper (1- end)))
-		(catch :exit
-		  (while (> (- upper lower) 1)
-		    (let ((mean (+ (ash lower -1)
-				   (ash upper -1)
-				   (logand lower upper 1))))
-		      (pcase (org-string-width (buffer-substring begin mean))
-			((pred (= width)) (throw :exit mean))
-			((pred (< width)) (setq upper mean))
-			(_ (setq lower mean)))))
-		  upper)))))
-	   (display org-table-shrunk-column-indicator)
-	   (show-before-edit
-	    (list (lambda (o &rest _)
-		    ;; Removing one overlay removes all other overlays
-		    ;; in the same column.
-		    (mapc #'delete-overlay
-			  (cdr (overlay-get o 'org-table-column-overlays))))))
-	   (o (make-overlay overlay-start end)))
-      (overlay-put o 'insert-behind-hooks show-before-edit)
-      (overlay-put o 'insert-in-front-hooks show-before-edit)
-      (overlay-put o 'modification-hooks show-before-edit)
-      (overlay-put o 'org-overlay-type 'table-column-hide)
-      (when (stringp contents) (overlay-put o 'help-echo contents))
-      ;; Make sure overlays stays on top of table coordinates
-      ;; overlays.  See `org-table-overlay-coordinates'.
-      (overlay-put o 'priority 1)
-      (org-overlay-display o display 'org-table t)
-      o)))
+Return a list of overlays hiding the field, or nil if field is
+already hidden."
+  (cond
+   ((org-table--shrunk-field) nil)	;already shrunk: bail out
+   ((eq contents 'hline)		;no contents to hide
+    (list (org-table--make-shrinking-overlay
+	   (+ start width 1) end org-table-shrunk-column-indicator contents)))
+   ((or (= 0 width)			;shrink to one character
+	(>= 1 (org-string-width (buffer-substring start end))))
+    (list (org-table--make-shrinking-overlay
+	   start end org-table-shrunk-column-indicator contents)))
+   (t
+    ;; If the field is not empty, consider using two overlays: one for
+    ;; the blanks at the beginning of the field, and another one at
+    ;; the end of the field.  The former ensures a shrunk field is
+    ;; always displayed with a single white space character in front
+    ;; of it -- e.g., so that even right-aligned fields appear to the
+    ;; left -- and the latter cuts the field at WIDTH visible
+    ;; characters.
+    (let* ((pre-overlay
+	    (and (not (equal contents ""))
+		 (org-with-point-at start (looking-at "\\( [ \t]+\\)\\S-"))
+		 (org-table--make-shrinking-overlay
+		  start (match-end 1) org-table-separator-space nil 'pre)))
+	   (post-overlay
+	    (let* ((start (if pre-overlay (overlay-end pre-overlay)
+			    (1+ start)))
+		   (w (org-string-width (buffer-substring start (1- end)))))
+	      (if (>= width w)
+		  ;; Field is too short.  Extend its size by adding
+		  ;; white space characters to the right overlay.
+		  (org-table--make-shrinking-overlay
+		   (1- end) end (concat (make-string (- width w) ?\s)
+					org-table-shrunk-column-indicator)
+		   contents)
+		;; Find cut location so that WIDTH characters are visible.
+		(org-table--make-shrinking-overlay
+		 (let* ((begin start)
+			(lower begin)
+			(upper (1- end)))
+		   (catch :exit
+		     (while (> (- upper lower) 1)
+		       (let ((mean (+ (ash lower -1)
+				      (ash upper -1)
+				      (logand lower upper 1))))
+			 (pcase (org-string-width (buffer-substring begin mean))
+			   ((pred (= width)) (throw :exit mean))
+			   ((pred (< width)) (setq upper mean))
+			   (_ (setq lower mean)))))
+		     upper))
+		 end org-table-shrunk-column-indicator contents)))))
+      (delq nil (list pre-overlay post-overlay))))))
 
 
 (defun org-table--read-column-selection (select max)
 (defun org-table--read-column-selection (select max)
   "Read column selection select as a list of numbers.
   "Read column selection select as a list of numbers.
@@ -4015,10 +4052,11 @@ table."
 			  (string-match "\\`<[lrc]?\\([0-9]+\\)>\\'" contents))
 			  (string-match "\\`<[lrc]?\\([0-9]+\\)>\\'" contents))
 		 (setq width (string-to-number (match-string 1 contents)))))))
 		 (setq width (string-to-number (match-string 1 contents)))))))
 	 (forward-line))
 	 (forward-line))
-       ;; Link overlay to the other overlays in the same column.
+       ;; Link overlays for current field to the other overlays in the
+       ;; same column.
        (let ((chain (list 'siblings)))
        (let ((chain (list 'siblings)))
 	 (dolist (field fields)
 	 (dolist (field fields)
-	   (let ((new (apply #'org-table--shrink-field (or width 0) field)))
+	   (dolist (new (apply #'org-table--shrink-field (or width 0) field))
 	     (push new (cdr chain))
 	     (push new (cdr chain))
 	     (overlay-put new 'org-table-column-overlays chain))))))))
 	     (overlay-put new 'org-table-column-overlays chain))))))))
 
 

+ 3 - 3
testing/lisp/test-org-table.el

@@ -2406,7 +2406,7 @@ See also `test-org-table/copy-field'."
   ;; With a column width cookie, limit overlay to the specified number
   ;; With a column width cookie, limit overlay to the specified number
   ;; of characters.
   ;; of characters.
   (should
   (should
-   (equal "| ab"
+   (equal "| abc"
 	  (org-test-with-temp-text "| <3>  |\n| <point>abcd |"
 	  (org-test-with-temp-text "| <3>  |\n| <point>abcd |"
 	    (org-table-toggle-column-width)
 	    (org-table-toggle-column-width)
 	    (buffer-substring (line-beginning-position)
 	    (buffer-substring (line-beginning-position)
@@ -2414,7 +2414,7 @@ See also `test-org-table/copy-field'."
 			       (car (overlays-in (line-beginning-position)
 			       (car (overlays-in (line-beginning-position)
 						 (line-end-position))))))))
 						 (line-end-position))))))))
   (should
   (should
-   (equal "| a "
+   (equal "| a  "
 	  (org-test-with-temp-text "| <3>  |\n| <point>a   |"
 	  (org-test-with-temp-text "| <3>  |\n| <point>a   |"
 	    (org-table-toggle-column-width)
 	    (org-table-toggle-column-width)
 	    (buffer-substring (line-beginning-position)
 	    (buffer-substring (line-beginning-position)
@@ -2423,7 +2423,7 @@ See also `test-org-table/copy-field'."
 						 (line-end-position))))))))
 						 (line-end-position))))))))
   ;; Width only takes into account visible characters.
   ;; Width only takes into account visible characters.
   (should
   (should
-   (equal "| [[htt"
+   (equal "| [[http"
 	  (org-test-with-temp-text "| <4> |\n| <point>[[http://orgmode.org]] |"
 	  (org-test-with-temp-text "| <4> |\n| <point>[[http://orgmode.org]] |"
 	    (org-table-toggle-column-width)
 	    (org-table-toggle-column-width)
 	    (buffer-substring (line-beginning-position)
 	    (buffer-substring (line-beginning-position)