5

I often find I want to close the nearest parent subtree, but don't know of a better way of doing this than navigating to the header first and then pressing tab. I use evil-mode, so properly this should be the effect of zc, but it is not.

  • Having added that answer I now notice that zc does work for me, and is bound to evil-close-fold. Try it by starting emacs with emacs -q, and then only enable the minimum configuration to test that? If that works the problem is with your emacs initialization, and you can start to narrow it down, e.g. using eval-region on your init file. – Croad Langshan Sep 4 '16 at 0:46
  • @CroadLangshan, sorry, I think my problem was that I expected it to work on plain lists too. – Toothrot Sep 4 '16 at 7:22
  • @CroadLangshan, does repeated zc close everything for you, if you are in a subsubsubtree? – Toothrot Sep 4 '16 at 7:33
  • Repeated zc doesn't keep closing parents for me, but it works even if point is inside a list. I'm not sure how to test for tree node visibility in org, so not sure how to make a function that keeps closing parents... – Croad Langshan Sep 4 '16 at 11:52
3

I tried implementing the 'keep closing parent' behaviour (please edit the question to say more explicitly what you wanted). I'm quite sure this can be drastically simplified and is likely buggy, but this seems to work for me:

(defun my/org-get-folded-state ()
    (cond
        ((not (or (org-at-item-p) (org-at-heading-p)))
            'not-at-node)
        ((org-before-first-heading-p)
            'not-at-node)
        (t
            (let (eoh eol eos has-children children-skipped struct)
                ;; First, determine end of headline (EOH), end of subtree or item
                ;; (EOS), and if item or heading has children (HAS-CHILDREN).
                (save-excursion
                    (if (org-at-item-p)
                        (progn
                            (beginning-of-line)
                            (setq struct (org-list-struct))
                            (setq eoh (point-at-eol))
                            (setq eos (org-list-get-item-end-before-blank (point) struct))
                            (setq has-children (org-list-has-child-p (point) struct)))
                        (org-back-to-heading)
                        (setq eoh (save-excursion (outline-end-of-heading) (point)))
                        (setq eos (save-excursion (org-end-of-subtree t t)
                                      (when (bolp) (backward-char)) (point)))
                        (setq has-children
                            (or (save-excursion
                                    (let ((level (funcall outline-level)))
                                        (outline-next-heading)
                                        (and (org-at-heading-p t)
                                            (> (funcall outline-level) level))))
                                (save-excursion
                                    (org-list-search-forward (org-item-beginning-re) eos t)))))
                    ;; Determine end invisible part of buffer (EOL)
                    (beginning-of-line 2)
                    (while (and (not (eobp)) ;; this is like `next-line'
                               (get-char-property (1- (point)) 'invisible))
                        (goto-char (next-single-char-property-change (point) 'invisible))
                        (and (eolp) (beginning-of-line 2)))
                    (setq eol (point)))
                (cond
                    ((= eos eoh)
                        'empty-node)
                    ((or (>= eol eos)
                         (not (string-match "\\S-" (buffer-substring eol eos))))
                        'folded)
                    (t
                        'not-folded))))))

(defun my/org-at-list-root ()
    ;; (org-at-item-p) must be true when you call this
    (save-excursion
        (org-beginning-of-item-list)
        (let ((old (point)))
            (org-back-to-heading)
            (forward-line)
            (eq (point) old))))

(defun my/org-jump-to-parent-item ()
    (org-beginning-of-item-list)
    (org-up-element))

(defun my/org-tree-can-fold-p ()
    (not (member (my/org-get-folded-state) (list 'folded 'empty-node))))

(defun my/org-cycle-until-folded ()
    (while (my/org-tree-can-fold-p)
        (org-cycle)))

(defun my/org-close-next-parent ()
    (interactive)
    (cond
        ((org-before-first-heading-p)
            nil)
        ((org-at-item-p)
            (if (my/org-tree-can-fold-p)
                  (my/org-cycle-until-folded)
                (if (my/org-at-list-root)
                      (progn
                          (org-back-to-heading)
                          (my/org-close-next-parent))
                    (my/org-jump-to-parent-item)
                    (my/org-close-next-parent))))
        ((org-at-heading-p)
            (if (my/org-tree-can-fold-p)
                  (my/org-cycle-until-folded)
                (if (not (eq (org-outline-level) 1))
                      (progn
                          (call-interactively #'outline-up-heading)
                          (my/org-close-next-parent)))))
        ((org-in-item-p)
            (org-beginning-of-item)
            (my/org-cycle-until-folded))
        (t
            ;; in heading
            (org-back-to-heading)
            (my/org-cycle-until-folded))))
  • This doesn't seem to work anymore. – Toothrot Dec 29 '17 at 16:24
2

How about something like this?

(defun my/org-cycle-parent-heading ()
    (interactive)
    (org-back-to-heading)
    (org-cycle)))

I'm using use-package, general and evil-org-mode, so my key binding configuration for that looks like this:

(use-package org
    :general
    (:states '(normal) :keymaps 'evil-org-mode-map
         "zc" 'my/org-cycle-parent-heading))
0

Since the long answer doesn't seem to work anymore, I did this.

(defun org-close-fold () (interactive)
             (if (and
                        (org-at-heading-p)
                        (> (org-current-level) 1)
                        (or
                         (invisible-p (point-at-eol))
                         (org-entry-empty-p)))
                        ;; if run at level 1, #'evil-close-fold will not run!
                     (outline-up-heading 1))
             (evil-close-fold)
             (move-beginning-of-line nil))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.