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.

  • Maybe try py-hide-indent, py-show, py-forward-indent from python-mode.el to get some starting point. – Andreas Röhler Sep 10 at 19:05
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)))))))
  • 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 at 13:59

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.