From 2b07b5c6fdc49598c553b920b88f0edb5a565344 Mon Sep 17 00:00:00 2001 From: Greg Sexton Date: Sun, 26 Apr 2015 11:40:34 +0100 Subject: [PATCH 1/7] Create basic indent parser --- origami-parsers.el | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/origami-parsers.el b/origami-parsers.el index 57161d5..be829f6 100644 --- a/origami-parsers.el +++ b/origami-parsers.el @@ -65,6 +65,70 @@ position in the CONTENT." acc)))) (reverse acc)))) +;;; TODO: support tabs +(defun origami-indent-parser (create) + (cl-labels ((lines (string) + (-> string + (origami-get-positions ".*?\r?\n") + (->> (-filter (lambda (line) + (not (s-blank? (s-trim (car line))))))))) + (annotate-levels (lines) + (-map (lambda (line) + (let ((beg (cdr line)) + (end (+ (cdr line) (length (car line)) -1))) + (vector (length (car (s-match "^ *" (car line)))) + beg + end + (- end beg)))) + lines)) + (indent (line) (aref line 0)) + (beg (line) (aref line 1)) + (end (line) (aref line 2)) + (offset (line) (aref line 3)) + (collapse-same-level (levels) + (reverse (-reduce-from (lambda (acc level) + (if (= (indent level) (indent (car acc))) + (cons (vector (indent (car acc)) + (beg (car acc)) + (end level) + (offset (car acc))) + (cdr acc)) + (cons level acc))) + (list (car levels)) + (cdr levels)))) + (create-tree (levels) + (if (null levels) + levels + (let ((curr-indent (indent (car levels)))) + (->> levels + (-partition-by (lambda (l) (= (indent l) curr-indent))) + (-partition-all 2) + (-map (lambda (x) (cons (caar x) (create-tree (cadr x))))))))) + (build-nodes (tree) + (if (null tree) (cons 0 nil) + ;; complexity here is due to having to find the end of the children so that the + ;; parent encompasses them + (-reduce-r-from (lambda (nodes acc) + (destructuring-bind (children-end . children) (build-nodes (cdr nodes)) + (let ((this-end (max children-end (end (car nodes))))) + (cons (max this-end (car acc)) + (cons (funcall create + (beg (car nodes)) + this-end + (offset (car nodes)) + children) + (cdr acc)))))) + '(0 . nil) + tree)))) + (lambda (content) + (-> content + lines + annotate-levels + collapse-same-level + create-tree + build-nodes + cdr)))) + (defun origami-build-pair-tree (create open close positions) (cl-labels ((build (positions) ;; this is so horrible, but fast From a8eb81e09c290b36193a194a933a9df9028728c5 Mon Sep 17 00:00:00 2001 From: Greg Sexton Date: Sun, 26 Apr 2015 13:21:16 +0100 Subject: [PATCH 2/7] Factor in newlines --- origami-parsers.el | 50 +++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/origami-parsers.el b/origami-parsers.el index be829f6..3973a5b 100644 --- a/origami-parsers.el +++ b/origami-parsers.el @@ -67,35 +67,35 @@ position in the CONTENT." ;;; TODO: support tabs (defun origami-indent-parser (create) - (cl-labels ((lines (string) - (-> string - (origami-get-positions ".*?\r?\n") - (->> (-filter (lambda (line) - (not (s-blank? (s-trim (car line))))))))) + (cl-labels ((lines (string) (origami-get-positions string ".*?\r?\n")) (annotate-levels (lines) (-map (lambda (line) - (let ((beg (cdr line)) + (let ((indent (length (car (s-match "^ *" (car line))))) + (beg (cdr line)) (end (+ (cdr line) (length (car line)) -1))) - (vector (length (car (s-match "^ *" (car line)))) - beg - end - (- end beg)))) + (if (s-blank? (s-trim (car line))) + 'newline ;sentinel representing line break + (vector indent beg end (- end beg))))) lines)) - (indent (line) (aref line 0)) + (indent (line) (if (eq line 'newline) -1 (aref line 0))) (beg (line) (aref line 1)) (end (line) (aref line 2)) (offset (line) (aref line 3)) - (collapse-same-level (levels) - (reverse (-reduce-from (lambda (acc level) - (if (= (indent level) (indent (car acc))) - (cons (vector (indent (car acc)) - (beg (car acc)) - (end level) - (offset (car acc))) - (cdr acc)) - (cons level acc))) - (list (car levels)) - (cdr levels)))) + (collapse-same-level (lines) + (->> + (cdr lines) + (-reduce-from (lambda (acc line) + (cond ((and (eq line 'newline) (eq (car acc) 'newline)) acc) + ((= (indent line) (indent (car acc))) + (cons (vector (indent (car acc)) + (beg (car acc)) + (end line) + (offset (car acc))) + (cdr acc))) + (t (cons line acc)))) + (list (car lines))) + (remove 'newline) + reverse)) (create-tree (levels) (if (null levels) levels @@ -103,7 +103,11 @@ position in the CONTENT." (->> levels (-partition-by (lambda (l) (= (indent l) curr-indent))) (-partition-all 2) - (-map (lambda (x) (cons (caar x) (create-tree (cadr x))))))))) + (-mapcat (lambda (x) + ;takes care of multiple identical levels, introduced when there are newlines + (-concat + (-map 'list (butlast (car x))) + (list (cons (-last-item (car x)) (create-tree (cadr x))))))))))) (build-nodes (tree) (if (null tree) (cons 0 nil) ;; complexity here is due to having to find the end of the children so that the From 579112c4064a72e59b7b78f4bc2aceca102f4563 Mon Sep 17 00:00:00 2001 From: Greg Sexton Date: Sun, 26 Apr 2015 14:25:41 +0100 Subject: [PATCH 3/7] Make indent-parser the default --- origami.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/origami.el b/origami.el index 99e5437..412cf2d 100644 --- a/origami.el +++ b/origami.el @@ -377,11 +377,12 @@ was last built." children (or (-> (origami-fold-find-path-with-range (origami-get-cached-tree buffer) beg end) - -last-item - origami-fold-data) + -last-item + origami-fold-data) (origami-create-overlay beg end offset buffer))))))) - (-when-let (parser-gen (cdr (assoc (buffer-local-value 'major-mode buffer) - origami-parser-alist))) + (-when-let (parser-gen (or (cdr (assoc (buffer-local-value 'major-mode buffer) + origami-parser-alist)) + 'origami-indent-parser)) (funcall parser-gen create)))) (defun origami-get-fold-tree (buffer) From 54e7502b118b6b62236b31990785139f4425763d Mon Sep 17 00:00:00 2001 From: Greg Sexton Date: Sun, 26 Apr 2015 14:41:57 +0100 Subject: [PATCH 4/7] Add explicit python-mode support --- origami-parsers.el | 1 + 1 file changed, 1 insertion(+) diff --git a/origami-parsers.el b/origami-parsers.el index 3973a5b..f043ae3 100644 --- a/origami-parsers.el +++ b/origami-parsers.el @@ -44,6 +44,7 @@ (js3-mode . origami-c-style-parser) (go-mode . origami-c-style-parser) (php-mode . origami-c-style-parser) + (python-mode . origami-indent-parser) (emacs-lisp-mode . origami-elisp-parser) (lisp-interaction-mode . origami-elisp-parser) (clojure-mode . origami-clj-parser)) From 6677218b96468999f1797d0c2bfaafdd2fb20267 Mon Sep 17 00:00:00 2001 From: Greg Sexton Date: Sun, 26 Apr 2015 14:42:10 +0100 Subject: [PATCH 5/7] Update docs --- README.md | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 96ca418..4fd6b93 100644 --- a/README.md +++ b/README.md @@ -42,13 +42,15 @@ In a buffer run `M-x origami-mode`, and start experimenting with any of the supplied origami interactive functions. I recommend binding these to keys of your choice in the `origami-mode-map`. -This has been tested on Emacs 24.3 and 24.4. +There is also `global-origami-mode` if you just want to enable origami +everywhere. For any major-mode that doesn't have explicit support, +origami will use the indentation of the buffer to determine folds. + +Origami has been tested on Emacs 24.3, 24.4 and 24.5. # What can it do? Origami works by parsing the buffer to determine a fold structure. -(Currently there is only support for determining the fold structure -using a parser.) The following commands are supplied to manipulate folds in the buffer: @@ -141,31 +143,39 @@ The following commands are supplied to manipulate folds in the buffer: # Does it support my favourite major-mode? -Probably not. Currently out of the box support is provided for: +To some degree, yes. Currently out of the box support is provided for: * C * C++ * Clojure +* Go * Java * Javascript -* Perl -* elisp -* Go * PHP +* Perl +* Python +* elisp + +Anything not in this list will be folded using +indentation. This works surprisingly well for most +major-modes and is great for folding text. It should be trivial to add support for any language that uses braces to delimit blocks. Just add to `origami-parser-alist` something like: `(mode-name . origami-c-style-parser)`. Adding support for another -lisp dialect should be almost as simple. +lisp dialect should be almost as simple. With a little bit of effort +it shouldn't be too hard to create a parser for anything with start +and end delimiters (similar to braces). See the +`origami-c-style-parser` function for how to define this. -I'm happy to work on parsers for other languages if enough interest is -expressed. +I'm happy to work on parsers for other languages if interest is +expressed. Cut an issue and I'll see what I can do. -It should be fairly easy to write a parser. An origami parser is a -function that takes a 'create function' and returns a function taking -the string to be parsed. The returned function should return a list of +You can write your own parser too. An origami parser is a function +that takes a 'create function' and returns a function taking the +string to be parsed. The returned function should return a list of fold nodes. Fold nodes are created using the passed-in create -function. Best to use an example: +function. Here is an example that creates a single fold node: (defun my-amazing-parser (create) (lambda (content) @@ -174,6 +184,10 @@ function. Best to use an example: offset ; this allows you to show some of the start of the folded text child-nodes)))) +While I work on writing better documentation for parsing, I suggest +starting by looking at the current parsers in origami-parsers.el to +see how they work. + # How is this different from [yafolding](https://github.com/zenozeng/yafolding.el)? I wasn't aware of yafolding before writing this. It looks like origami From fb1db05c3e8e13579d75fe1598d1bdd4bc60c795 Mon Sep 17 00:00:00 2001 From: Greg Sexton Date: Sun, 26 Apr 2015 14:49:24 +0100 Subject: [PATCH 6/7] Improve doc --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4fd6b93..7ba52df 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,8 @@ Origami has been tested on Emacs 24.3, 24.4 and 24.5. Origami works by parsing the buffer to determine a fold structure. -The following commands are supplied to manipulate folds in the buffer: +The following commands are supplied to manipulate folds in the +buffer - those in bold are particularly useful: @@ -91,7 +92,7 @@ The following commands are supplied to manipulate folds in the buffer: - + @@ -111,7 +112,7 @@ The following commands are supplied to manipulate folds in the buffer: - + @@ -126,12 +127,12 @@ The following commands are supplied to manipulate folds in the buffer: - + - + @@ -156,9 +157,8 @@ To some degree, yes. Currently out of the box support is provided for: * Python * elisp -Anything not in this list will be folded using -indentation. This works surprisingly well for most -major-modes and is great for folding text. +Anything not in this list will be folded using indentation. This works +surprisingly well for most major-modes and is great for folding text. It should be trivial to add support for any language that uses braces to delimit blocks. Just add to `origami-parser-alist` something like: From 5398602a977807e004bf6122dd5032269aa84463 Mon Sep 17 00:00:00 2001 From: Greg Sexton Date: Sun, 26 Apr 2015 14:57:27 +0100 Subject: [PATCH 7/7] Add todo --- origami-parsers.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/origami-parsers.el b/origami-parsers.el index f043ae3..6f05296 100644 --- a/origami-parsers.el +++ b/origami-parsers.el @@ -66,11 +66,11 @@ position in the CONTENT." acc)))) (reverse acc)))) -;;; TODO: support tabs (defun origami-indent-parser (create) (cl-labels ((lines (string) (origami-get-positions string ".*?\r?\n")) (annotate-levels (lines) (-map (lambda (line) + ;; TODO: support tabs (let ((indent (length (car (s-match "^ *" (car line))))) (beg (cdr line)) (end (+ (cdr line) (length (car line)) -1)))
origami-recursively-toggle-nodeorigami-recursively-toggle-node Acts like org-mode header collapsing. Cycle a fold between open, recursively open, closed.
origami-show-only-nodeorigami-show-only-node Close everything but the folds necessary to see the point. Very useful for concentrating on an area of code.
origami-undoorigami-undo Undo the last folding operation.
origami-redoorigami-redo Redo the last undone folding operation.