diff --git a/README.md b/README.md
index 96ca418..7ba52df 100644
--- a/README.md
+++ b/README.md
@@ -42,15 +42,18 @@ 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:
+The following commands are supplied to manipulate folds in the
+buffer - those in bold are particularly useful:
@@ -89,7 +92,7 @@ The following commands are supplied to manipulate folds in the buffer:
- origami-recursively-toggle-node |
+ origami-recursively-toggle-node |
Acts like org-mode header collapsing. Cycle a fold between open, recursively open, closed. |
@@ -109,7 +112,7 @@ The following commands are supplied to manipulate folds in the buffer:
- origami-show-only-node |
+ origami-show-only-node |
Close everything but the folds necessary to see the point. Very useful for concentrating on an area of code. |
@@ -124,12 +127,12 @@ The following commands are supplied to manipulate folds in the buffer:
- origami-undo |
+ origami-undo |
Undo the last folding operation. |
- origami-redo |
+ origami-redo |
Redo the last undone folding operation. |
@@ -141,31 +144,38 @@ 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
diff --git a/origami-parsers.el b/origami-parsers.el
index 57161d5..6f05296 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))
@@ -65,6 +66,74 @@ position in the CONTENT."
acc))))
(reverse acc))))
+(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)))
+ (if (s-blank? (s-trim (car line)))
+ 'newline ;sentinel representing line break
+ (vector indent beg end (- end beg)))))
+ lines))
+ (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 (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
+ (let ((curr-indent (indent (car levels))))
+ (->> levels
+ (-partition-by (lambda (l) (= (indent l) curr-indent)))
+ (-partition-all 2)
+ (-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
+ ;; 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
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)