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.

Toothrot
  • 3,204
  • 1
  • 12
  • 30
  • 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 04 '16 at 00:46
  • @CroadLangshan, sorry, I think my problem was that I expected it to work on plain lists too. – Toothrot Sep 04 '16 at 07:22
  • @CroadLangshan, does repeated `zc` close everything for you, if you are in a subsubsubtree? – Toothrot Sep 04 '16 at 07: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 04 '16 at 11:52

3 Answers3

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))))
Croad Langshan
  • 3,192
  • 14
  • 42
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))
Croad Langshan
  • 3,192
  • 14
  • 42
0

This seems work.

(defun evil*-close-fold () (interactive)
 (if (and (or (org-at-heading-p)
              (invisible-p (point)))
          (invisible-p (point-at-eol))
          (>= (org-current-level) 2))
     (outline-up-heading 1))
 (evil-close-fold))

(define-key evil-normal-state-map (kbd "zc") 'evil*-close-fold)

The disjunction is necessary because, after evil-close-fold, (org-at-heading-p) will not return t, unless point was at the heading from before.

Toothrot
  • 3,204
  • 1
  • 12
  • 30