2

Is there a way to dynamically hide lines that are indented more than the current line?

For example, if I am looking at this file

{
  "a": 1
  "b": {
    "c": {
      "d": 2
    }
  }
}

and I am on the line with "b", I would see something like

{
  "a": 1
  "b": {
  }
}

and if I move down one real line, I then see

{
  "a": 1
  "b": {
    "c": {
    }
  }
}

I suspect that a minor mode that does (set-selective-display (indentation-of-current-line)) after each movement of the point is what I want. Although I would also want a simple what to go to the first line of the hidden region. Which is probably simple to do, but I'm not immediately seeing it.

Troy Daniels
  • 487
  • 2
  • 13

1 Answers1

1

It sounded good, so I made a quick implementation of the idea.

If the indentation of the current line only is used, navigation becomes difficult, so I chose to consider also the indentation of the next line.

Implementation wise:

  • (current-indentation) gives the indentation of the line at point.
  • post-command-hook is a suitable place for executing the command. I haven't tested, if set-selective-display avoids the overhead of repeatedly applying the same value.

;; -*- lexical-binding: t; coding: utf-8; -*-

(define-minor-mode auto-set-selective-display-mode 
  "Automatically apply `set-selective-display' at all times based on current indentation." 
  nil "$" nil
  (if auto-set-selective-display-mode
      (add-hook 'post-command-hook #'auto-set-selective-display nil t)
    (remove-hook 'post-command-hook #'auto-set-selective-display t)
    (with-temp-message ""
      (set-selective-display nil))))

(defun auto-set-selective-display ()
  "Apply `set-selective-display' such that current and next line are visible.

Scroll events are excluded in order to prevent wild flickering while navigating."
  (unless (eq last-command #'mwheel-scroll)
    (let*((this-line-indent (current-indentation))
          (next-line-indent (save-excursion (forward-line) (current-indentation))))
      (with-temp-message "" ; Suppress messages.
        (set-selective-display (1+ (max this-line-indent next-line-indent)))))))

Edit (Troy Daniels) Add an option to not look at the next line.

As promised in the comments, M-x auto-set-selective-display-toggle-neighboring-lines will toggle whether it looks at the next line to get the indentation.

;; -*- lexical-binding: t; coding: utf-8; -*-

(define-minor-mode auto-set-selective-display-mode
  "Automatically apply `set-selective-display' at all times based on current indentation."
  nil "$" nil
  (if auto-set-selective-display-mode
      (add-hook 'post-command-hook #'auto-set-selective-display nil t)
    (remove-hook 'post-command-hook #'auto-set-selective-display t)
    (with-temp-message ""
      (set-selective-display nil))))

(defcustom auto-set-selective-display-neighboring-lines t
  "Should neighboring lines be considered when looking at the selective display depth."
  :type 'boolean
  :safe t)

(defun auto-set-selective-display-toggle-neighboring-lines ()
  "Toggle whether neighboring lines should be show."
  (interactive)
  (setq auto-set-selective-display-neighboring-lines (not auto-set-selective-display-neighboring-lines)))

(defun auto-set-selective-display ()
  "Apply `set-selective-display' such that current and next line are visible.

Scroll events are excluded in order to prevent wild flickering while navigating."
  (unless (eq last-command #'mwheel-scroll)
    (let*((this-line-indent (current-indentation))
          (next-line-indent (if auto-set-selective-display-neighboring-lines
                                (save-excursion (forward-line) (current-indentation))
                              -1)))
      (with-temp-message "" ; Suppress messages.
        (set-selective-display (1+ (max this-line-indent next-line-indent)))))))
kdb
  • 1,561
  • 12
  • 21
  • The one issue that I see with this is if I try to walk down the file, I keep going deeper into the structure. If I am at `b`, `C-n` puts me at `c` and another `C-n` puts me at `d`, with no direct way to go straight to the end of `b`. I could do that with `C-M-f` if the point is in the right column. I'll probably extend this to have a flag to toggle using the indent of the next line as well. – Troy Daniels Sep 26 '19 at 13:59