2

Is there a default way to make tables in org mode foldable? Obviously, I can just advise org-table-create to wrap the new table inside a drawer, but I was wondering whether this is overdesigned and there is some built-in way that I have just not been able to find in the manual.

2 Answers2

2

To get this

Fold / Unfold Tables at point Fold/Unfold Tables at Point

Use this snippet ...

(defun my-table-hide-spec ()
  "Hide portions of table content.
Add `my-table-hide-table' as an invisibility spec for hiding
portions of tables content."
  (add-to-invisibility-spec '(my-table-hide-table . t)))
(add-hook 'org-mode-hook #'my-table-hide-spec)

(defvar-local my-table-hide-overlays nil
  "Overlays hiding tables.")

(defun my-table-fold-all ()
  "Fold all tables in the current buffer."
  (interactive)
  (my-table-unfold-all)
  (save-excursion
    (org-table-map-tables
     #'my-table-toggle-folds-maybe)))

(defun my-table-unfold-all ()
  "Unfold all tables in the current buffer."
  (interactive)
  (mapc 'delete-overlay my-table-hide-overlays)
  (setq my-table-hide-overlays nil))

;;;###autoload
(defun my-table-toggle-folds-maybe ()
  "Toggle visibility of table at point."
  (interactive)
  (my-table-toggle-fold))

(defun my-table-toggle-fold ()
  "Toggle the visibility of the current table."
  (interactive)
  (save-excursion
    (beginning-of-line)
    (unless (org-at-table-p)
      (error "Not looking at a table"))
    (let ((start (progn
                   (goto-char (org-table-begin))
                   (re-search-forward "|")
                   (point)))
          (end (progn
                 (goto-char (org-table-end))
                 (re-search-backward "|")
                 (point)))
          ov)
      (cond
       ((memq t (mapcar (lambda (overlay)
                          (eq (overlay-get overlay 'invisible)
                              'my-table-hide-table))
                        (overlays-at start)))
        (mapc (lambda (ov)
                (when (member ov my-table-hide-overlays)
                  (setq my-table-hide-overlays
                        (delq ov my-table-hide-overlays)))
                (when (eq (overlay-get ov 'invisible)
                          'my-table-hide-table)
                  (delete-overlay ov)))
              (overlays-at start)))
       (t

        (setq ov (make-overlay start end))
        (overlay-put ov 'invisible 'my-table-hide-table)
        ;; make the block accessible to isearch
        (overlay-put
         ov 'isearch-open-invisible
         (lambda (ov)
           (when (member ov my-table-hide-overlays)
             (setq my-table-hide-overlays
                   (delq ov my-table-hide-overlays)))
           (when (eq (overlay-get ov 'invisible)
                     'my-table-hide-table)
             (delete-overlay ov))))
        (push ov my-table-hide-overlays))))))

;; org-tab-after-check-for-cycling-hook
(add-hook 'org-tab-first-hook #'my-table-toggle-folds-maybe)

;; Remove overlays when changing major mode
(add-hook 'org-mode-hook
          (lambda () (add-hook 'change-major-mode-hook
                               #'my-table-unfold-all 'append 'local)))


Here is a sample org file for you to try ...



     #+name: colgroups
     | N | N^2 | N^3 | N^4 | sqrt(n) | sqrt[4](N) |
     |---+-----+-----+-----+---------+------------|
     | / |  <  |     |  >  |       < |          > |
     | 1 |  1  |  1  |  1  |       1 |          1 |
     | 2 |  4  |  8  | 16  |  1.4142 |     1.1892 |
     | 3 |  9  | 27  | 81  |  1.7321 |     1.3161 |
     |---+-----+-----+-----+---------+------------|
     #+TBLFM: $2=$1^2::$3=$1^3::$4=$1^4::$5=sqrt($1)::$6=sqrt(sqrt(($1)))


     
     #+name: Duration and Time Values
     |  Task 1 |   Task 2 |    Total |
     |---------+----------+----------|
     |    2:12 |     1:47 | 03:59:00 |
     |    2:12 |     1:47 |    03:59 |
     | 3:02:20 | -2:07:00 |     0.92 |
     #+TBLFM: @2$3=$1+$2;T::@3$3=$1+$2;U::@4$3=$1+$2;t
     

Some text

   #+name: test
   | a | b | b | c |
   | d | e | f | g |
   |---+---+---+---|
   | h | i | j | k |
   #+TBLFM: 

| a | b | b | c |
| d | e | f | g |
| h | i | j | k |



1

The following function hides the text snippet identified by org-element as element at point.

Tables are such elements. This works only on the #+NAME:-line or the #+TBLFM:-line since otherwise org-table-next-field interferes.

(defun org+-hide-element-at-point-toggle (&optional force)
  "Toggle the visibility of named paragraphs.
  If FORCE is 'off make paragraph visible.
  If FORCE is otherwise non-nil make paragraph invisible.
  Otherwise toggle the visibility."
  (interactive "P")
  (let* ((par (org-element-at-point))
     (start (org-element-property :contents-begin par))
     (end (org-element-property :contents-end par))
     (post (org-element-property :post-affiliated par)))
    (when (and start end post)
      (cond ((eq force 'off)
         (org-flag-region start end nil 'org-hide-block))
        (force
         (org-flag-region start end t 'org-hide-block))
        ((eq (get-char-property start 'invisible) 'org-hide-block)
         (org-flag-region start end nil 'org-hide-block))
        (t
         (org-flag-region start end t 'org-hide-block)))
      ;; When the block is hidden away, make sure point is left in
      ;; a visible part of the buffer.
      (when (and post (invisible-p (max (1- (point)) (point-min))))
    (goto-char post))
      ;; Signal success.
      t)))

(add-hook 'org-tab-after-check-for-cycling-hook #'org+-hide-element-at-point-toggle)

Note, that this makes also paragraphs foldable.

Tobias
  • 32,569
  • 1
  • 34
  • 75