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: - + @@ -109,7 +112,7 @@ The following commands are supplied to manipulate folds in the buffer: - + @@ -124,12 +127,12 @@ The following commands are supplied to manipulate folds in the buffer: - + - + @@ -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)
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.