Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imenu based fold parser #69

Open
wbolster opened this issue Apr 13, 2018 · 21 comments
Open

imenu based fold parser #69

wbolster opened this issue Apr 13, 2018 · 21 comments

Comments

@wbolster
Copy link

wbolster commented Apr 13, 2018

hi,

i wrote a imenu based parser that creates folds (without any nesting) from imenu entries. i think this is very useful behaviour. let me know what you think, and whether this would make sense to include with origami itself.

i think it can be useful for many languages, e.g. #49, #29, #61, #58 and possibly others may benefit from this.

this parser can be used for any major mode that has sensible imenu entries, by putting it into the origami-parser-alist variable. this can be done in many ways, for instance via Customize M-x customize-variable RET origami-parser-alist, or using lisp code, which is what i do in my init.el using a helper from evil-mode:

(evil-add-to-alist 'origami-parser-alist 'python-mode 'origami-parser-imenu-flat)

i use this for python-mode and rst-mode. it results in very dense (no empty lines) and usable (for me at least) folding.

here's how it looks for python:

image

here's how it looks for restructuredtext:

image

here's the origami-parser-imenu-flat function, which works for me, but i only tested it casually:

  (defun origami-parser-imenu-flat (create)
    "Origami parser producing folds for each imenu entry, without nesting."
    (require 'imenu)
    (lambda (content)
      (let ((orig-major-mode major-mode))
        (with-temp-buffer
          (insert content)
          (funcall orig-major-mode)
          (let* ((items
                  (-as-> (imenu--make-index-alist t) items
                         (-flatten items)
                         (-filter 'listp items)))
                 (positions
                  (-as-> (-map #'cdr items) positions
                         (-filter 'identity positions)
                         (-map-when 'markerp 'marker-position positions)
                         (-filter 'natnump positions)
                         (cons (point-min) positions)
                         (-snoc positions (point-max))
                         (-sort '< positions)
                         (-uniq positions)))
                 (ranges
                  (-zip-pair positions (-map '1- (cdr positions))))
                 (fold-nodes
                  (--map
                   (-let*
                       (((range-beg . range-end) it)
                        (line-beg
                         (progn (goto-char range-beg)
                                (line-beginning-position)))
                        (offset
                         (- (min (line-end-position) range-end) line-beg))
                        (fold-node
                         (funcall create line-beg range-end offset nil)))
                     fold-node)
                   ranges)))
            fold-nodes)))))
@sawan
Copy link

sawan commented Apr 23, 2018

Wow, this looks awesome.

So adding the elisp function + imenu is all that's required?

@wbolster
Copy link
Author

yes, and adding the configuration so that it gets used for a specific major mode. and of course that major mode should have imenu support, which seems the case for quite a few that i tried.

@sawan
Copy link

sawan commented Apr 23, 2018

I couldn't get this to work in Python mode. What entry function should I be invoking? I tried origami-close-all-nodes.

@wbolster
Copy link
Author

@sawan what you describe works for me and is indeed how this is supposed to work.

what does your origami-parser-alist variable look like? does python-mode point to the right helper?

also try origami-reset, or killing the buffer and reopening the file.

@sawan
Copy link

sawan commented Apr 27, 2018

@wbolster thanks, sorry for late reply.

I have added the function it to origami-parser-alist.

Now when I do origami-close-all-nodes I get an error saying let*: Symbol’s value as variable is void: create

@wbolster
Copy link
Author

wbolster commented Apr 30, 2018

@sawan are you sure you set it up correctly?

i have a working config in my init.el which you may use for inspiration: https://github.com/wbolster/dotfiles/blob/master/Emacs/init.el:

(use-package origami
  :custom
  (origami-show-fold-header t)

  :commands
  origami-parser-imenu-flat

  :config
  (defun origami-parser-imenu-flat (create)
    "Origami parser producing folds for each imenu entry, without nesting."
    (lambda (content)
      (let ((orig-major-mode major-mode))
        (with-temp-buffer
          (insert content)
          (delay-mode-hooks
            (funcall orig-major-mode))
          (let* ((items
                  (-as-> (imenu--make-index-alist t) items
                         (-flatten items)
                         (-filter 'listp items)))
                 (positions
                  (-as-> (-map #'cdr items) positions
                         (-filter 'identity positions)
                         (-map-when 'markerp 'marker-position positions)
                         (-filter 'natnump positions)
                         (cons (point-min) positions)
                         (-snoc positions (point-max))
                         (-sort '< positions)
                         (-uniq positions)))
                 (ranges
                  (-zip-pair positions (-map '1- (cdr positions))))
                 (fold-nodes
                  (--map
                   (-let*
                       (((range-beg . range-end) it)
                        (line-beg
                         (progn (goto-char range-beg)
                                (line-beginning-position)))
                        (offset
                         (- (min (line-end-position) range-end) line-beg))
                        (fold-node
                         (funcall create line-beg range-end offset nil)))
                     fold-node)
                   ranges)))
            fold-nodes))))))

@sawan
Copy link

sawan commented May 6, 2018

Hi @wbolster,

I have added the parser function to my configuration:

https://github.com/sawan/emacs-config/blob/279e125344a81d3bf548e64c302829bc3a2efcb2/emacs24.el#L1614-L1651

And here is the value of origami-parser-alist

https://github.com/sawan/emacs-config/blob/279e125344a81d3bf548e64c302829bc3a2efcb2/emacs24.el#L1718-L1746

Am I doing anything wrong?

@gregsexton
Copy link
Owner

Great idea! Do you want to send a pull request?

@wbolster
Copy link
Author

@sawan that is also how i use it. not sure what's wrong at your side, do you have the latest origami? and can you provide more info like a backtrace (M-x toggle-debug-on-error)?

@wbolster
Copy link
Author

@gregsexton sure, would that pr just include that function? or also change default configuration? (imenu and indent based folds are different.)

@sawan
Copy link

sawan commented May 15, 2018

@wbolster I have sent you an email with the stack trace attached.

Origami version is 20180101.753

Thanks again....

@wbolster
Copy link
Author

@sawan i am not sure which email you are referring to... in any case, the only right place to put that stack trace is as a comment in this ticket.

@wbolster
Copy link
Author

ah, found something. here's a trimmed down version

Debugger entered--Lisp error: (void-variable create)
(funcall create line-beg range-end offset nil)
(let* ((--dash-source-31-- it) (range-beg (car-safe (prog1 --dash-source-31-- (setq --dash-source-31-- (cdr --dash-source-31--))))) (range-end --dash-source-31--) (line-beg (progn (goto-char range-beg) (line-beginning-position))) (offset (- (min (line-end-position) range-end) line-beg)) (fold-node (funcall create line-beg range-end offset nil))) fold-node)
(lambda (it) (let* ((--dash-source-31-- it) (range-beg (car-safe (prog1 --dash-source-31-- (setq --dash-source-31-- (cdr --dash-source-31--))))) (range-end --dash-source-31--) (line-beg (progn (goto-char range-beg) (line-beginning-position))) (offset (- (min (line-end-position) range-end) line-beg)) (fold-node (funcall create line-beg range-end offset nil))) fold-node))((1 . 1746))
mapcar((lambda (it) (let* ((--dash-source-31-- it) (range-beg (car-safe (prog1 --dash-source-31-- (setq --dash-source-31-- (cdr --dash-source-31--))))) (range-end --dash-source-31--) (line-beg (progn (goto-char range-beg) (line-beginning-position))) (offset (- (min (line-end-position) range-end) line-beg)) (fold-node (funcall create line-beg range-end offset nil))) fold-node)) ((1 . 1746) (1747 . 2334) (2335 . 3233) (3234 . 3287) (3288 . 3499) (3500 . 4360)))
(let* ((items (let ((items (imenu--make-index-alist t))) (let ((items (-flatten items))) (-filter (quote listp) items)))) (positions (let ((positions (-map (function cdr) items))) (let ((positions (-filter ... positions))) (let ((positions ...)) (let (...) (let ... ...)))))) (ranges (-zip-pair positions (-map (quote 1-) (cdr positions)))) (fold-nodes (mapcar (function (lambda (it) (let* (... ... ... ... ... ...) fold-node))) ranges))) fold-nodes)
(progn (insert content) (funcall orig-major-mode) (let* ((items (let ((items (imenu--make-index-alist t))) (let ((items ...)) (-filter (quote listp) items)))) (positions (let ((positions (-map ... items))) (let ((positions ...)) (let (...) (let ... ...))))) (ranges (-zip-pair positions (-map (quote 1-) (cdr positions)))) (fold-nodes (mapcar (function (lambda (it) (let* ... fold-node))) ranges))) fold-nodes))
(unwind-protect (progn (insert content) (funcall orig-major-mode) (let* ((items (let ((items ...)) (let (...) (-filter ... items)))) (positions (let ((positions ...)) (let (...) (let ... ...)))) (ranges (-zip-pair positions (-map (quote 1-) (cdr positions)))) (fold-nodes (mapcar (function (lambda ... ...)) ranges))) fold-nodes)) (and (buffer-name temp-buffer) (kill-buffer temp-buffer)))
(save-current-buffer (set-buffer temp-buffer) (unwind-protect (progn (insert content) (funcall orig-major-mode) (let* ((items (let (...) (let ... ...))) (positions (let (...) (let ... ...))) (ranges (-zip-pair positions (-map ... ...))) (fold-nodes (mapcar (function ...) ranges))) fold-nodes)) (and (buffer-name temp-buffer) (kill-buffer temp-buffer))))
(let ((temp-buffer (generate-new-buffer " *temp*"))) (save-current-buffer (set-buffer temp-buffer) (unwind-protect (progn (insert content) (funcall orig-major-mode) (let* ((items (let ... ...)) (positions (let ... ...)) (ranges (-zip-pair positions ...)) (fold-nodes (mapcar ... ranges))) fold-nodes)) (and (buffer-name temp-buffer) (kill-buffer temp-buffer)))))
(let ((orig-major-mode major-mode)) (let ((temp-buffer (generate-new-buffer " *temp*"))) (save-current-buffer (set-buffer temp-buffer) (unwind-protect (progn (insert content) (funcall orig-major-mode) (let* ((items ...) (positions ...) (ranges ...) (fold-nodes ...)) fold-nodes)) (and (buffer-name temp-buffer) (kill-buffer temp-buffer))))))
(lambda (content) (let ((orig-major-mode major-mode)) (let
((temp-buffer (generate-new-buffer " *temp*"))) (save-current-buffer
(set-buffer temp-buffer) (unwind-protect (progn (insert content)
(funcall orig-major-mode) (let* (... ... ... ...) fold-nodes)) (and
(buffer-name temp-buffer) (kill-buffer temp-buffer))))))) ...
funcall((lambda (content) (let ((orig-major-mode major-mode)) (let ((temp-buffer (generate-new-buffer " *temp*"))) (save-current-buffer (set-buffer temp-buffer) (unwind-protect (progn (insert content) (funcall orig-major-mode) (let* (... ... ... ...) fold-nodes)) (and (buffer-name temp-buffer) (kill-buffer temp-buffer))))))) ...)
(origami-fold-root-node (funcall parser contents))
(let ((contents (buffer-string))) (origami-fold-root-node (funcall parser contents)))
(save-current-buffer (set-buffer buffer) (let ((contents (buffer-string))) (origami-fold-root-node (funcall parser contents))))
(progn (save-current-buffer (set-buffer buffer) (let ((contents (buffer-string))) (origami-fold-root-node (funcall parser contents)))))
(if parser (progn (save-current-buffer (set-buffer buffer) (let ((contents (buffer-string))) (origami-fold-root-node (funcall parser contents))))))
origami-build-tree(#<buffer aota.py> (lambda (content) (let ((orig-major-mode major-mode)) (let ((temp-buffer (generate-new-buffer " *temp*"))) (save-current-buffer (set-buffer temp-buffer) (unwind-protect (progn (insert content) (funcall orig-major-mode) (let* (... ... ... ...) fold-nodes)) (and (buffer-name temp-buffer) (kill-buffer temp-buffer))))))))
(if (origami-rebuild-tree\? buffer) (origami-build-tree buffer (origami-get-parser buffer)) (origami-get-cached-tree buffer))
(progn (if (origami-rebuild-tree\? buffer) (origami-build-tree buffer (origami-get-parser buffer)) (origami-get-cached-tree buffer)))
(if origami-mode (progn (if (origami-rebuild-tree\? buffer) (origami-build-tree buffer (origami-get-parser buffer)) (origami-get-cached-tree buffer))))
origami-get-fold-tree(#<buffer aota.py>)
(let ((tree (origami-get-fold-tree buffer))) (if tree (progn (origami-apply-new-tree buffer tree (origami-store-cached-tree buffer (origami-fold-map (function (lambda ... ...)) tree))))))
origami-close-all-nodes(#<buffer aota.py>)
funcall-interactively(origami-close-all-nodes #<buffer aota.py>)
#<subr call-interactively>(origami-close-all-nodes record nil)
apply(#<subr call-interactively> origami-close-all-nodes (record nil))
(let ((ido-cr+-current-command command)) (apply orig-fun command args))
call-interactively@ido-cr+-record-current-command(#<subr call-interactively> origami-close-all-nodes record nil)
apply(call-interactively@ido-cr+-record-current-command #<subr call-interactively> (origami-close-all-nodes record nil))
call-interactively(origami-close-all-nodes record nil)
command-execute(origami-close-all-nodes record)
execute-extended-command(nil "origami-close-all-nodes")

@wbolster
Copy link
Author

weird, that create variable is an argument to the parser function, and should be passed in by origami.

which emacs version?

and no idea whether it's related, but do you have lexical-binding on? e.g. first line of my init.el reads

;;; init.el --- emacs configuration -*- lexical-binding: t; -*-

@sawan
Copy link

sawan commented May 16, 2018

@wbolster

Lexical Binding was indeed the problem!!

I put

;;; -*- lexical-binding: t -*-

in my .emacs and it works like a charm -- may I say its a very good approach to solving this problem!

Thank you again and I hope you keep up the good work.

@wbolster
Copy link
Author

@sawan awesome!

millejoh added a commit to millejoh/spacemacs-config that referenced this issue Oct 10, 2018
Based on code published in issue
gregsexton/origami.el#69.
@jimmywongroo
Copy link

@wbolster Care to open a PR for this useful feature?

@wbolster
Copy link
Author

tbh i'm not sure this project is actually maintained. @gregsexton is it?

@jcs090218
Copy link

@gregsexton Are you still maintaining this project, if you are just busy consider adding some collaborator or something that could keep this project maintained! Please keep this awesome project alive! Thank you!

@gregsexton
Copy link
Owner

I like origami-parser-imenu-flat, I've been using it for a few months without a problem. Will merge a PR.

@jcs090218
Copy link

Hi, just want inform people here that have started a new branch here.

If you don't mind using celpa, you can open the PR there instead! Thanks!

For reason why? See elp-revive/origami.el#1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants