Add indent parser

This commit is contained in:
Greg Sexton 2015-04-26 14:57:43 +01:00
commit 49ae22e18a
3 changed files with 107 additions and 23 deletions

View File

@ -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 of the supplied origami interactive functions. I recommend binding
these to keys of your choice in the `origami-mode-map`. 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? # What can it do?
Origami works by parsing the buffer to determine a fold structure. 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:
<table> <table>
<tr> <tr>
@ -89,7 +92,7 @@ The following commands are supplied to manipulate folds in the buffer:
</tr> </tr>
<tr> <tr>
<td>origami-recursively-toggle-node</td> <td><strong>origami-recursively-toggle-node</strong></td>
<td>Acts like org-mode header collapsing. Cycle a fold between open, recursively open, closed.</td> <td>Acts like org-mode header collapsing. Cycle a fold between open, recursively open, closed.</td>
</tr> </tr>
@ -109,7 +112,7 @@ The following commands are supplied to manipulate folds in the buffer:
</tr> </tr>
<tr> <tr>
<td>origami-show-only-node</td> <td><strong>origami-show-only-node</strong></td>
<td>Close everything but the folds necessary to see the point. Very useful for concentrating on an area of code.</td> <td>Close everything but the folds necessary to see the point. Very useful for concentrating on an area of code.</td>
</tr> </tr>
@ -124,12 +127,12 @@ The following commands are supplied to manipulate folds in the buffer:
</tr> </tr>
<tr> <tr>
<td>origami-undo</td> <td><strong>origami-undo</strong></td>
<td>Undo the last folding operation.</td> <td>Undo the last folding operation.</td>
</tr> </tr>
<tr> <tr>
<td>origami-redo</td> <td><strong>origami-redo</strong></td>
<td>Redo the last undone folding operation.</td> <td>Redo the last undone folding operation.</td>
</tr> </tr>
@ -141,31 +144,38 @@ The following commands are supplied to manipulate folds in the buffer:
# Does it support my favourite major-mode? # 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
* C++ * C++
* Clojure * Clojure
* Go
* Java * Java
* Javascript * Javascript
* Perl
* elisp
* Go
* PHP * 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 It should be trivial to add support for any language that uses braces
to delimit blocks. Just add to `origami-parser-alist` something like: to delimit blocks. Just add to `origami-parser-alist` something like:
`(mode-name . origami-c-style-parser)`. Adding support for another `(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 I'm happy to work on parsers for other languages if interest is
expressed. 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 You can write your own parser too. An origami parser is a function
function that takes a 'create function' and returns a function taking that takes a 'create function' and returns a function taking the
the string to be parsed. The returned function should return a list of string to be parsed. The returned function should return a list of
fold nodes. Fold nodes are created using the passed-in create 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) (defun my-amazing-parser (create)
(lambda (content) (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 offset ; this allows you to show some of the start of the folded text
child-nodes)))) 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)? # 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 I wasn't aware of yafolding before writing this. It looks like origami

View File

@ -44,6 +44,7 @@
(js3-mode . origami-c-style-parser) (js3-mode . origami-c-style-parser)
(go-mode . origami-c-style-parser) (go-mode . origami-c-style-parser)
(php-mode . origami-c-style-parser) (php-mode . origami-c-style-parser)
(python-mode . origami-indent-parser)
(emacs-lisp-mode . origami-elisp-parser) (emacs-lisp-mode . origami-elisp-parser)
(lisp-interaction-mode . origami-elisp-parser) (lisp-interaction-mode . origami-elisp-parser)
(clojure-mode . origami-clj-parser)) (clojure-mode . origami-clj-parser))
@ -65,6 +66,74 @@ position in the CONTENT."
acc)))) acc))))
(reverse 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) (defun origami-build-pair-tree (create open close positions)
(cl-labels ((build (positions) (cl-labels ((build (positions)
;; this is so horrible, but fast ;; this is so horrible, but fast

View File

@ -380,8 +380,9 @@ was last built."
-last-item -last-item
origami-fold-data) origami-fold-data)
(origami-create-overlay beg end offset buffer))))))) (origami-create-overlay beg end offset buffer)))))))
(-when-let (parser-gen (cdr (assoc (buffer-local-value 'major-mode buffer) (-when-let (parser-gen (or (cdr (assoc (buffer-local-value 'major-mode buffer)
origami-parser-alist))) origami-parser-alist))
'origami-indent-parser))
(funcall parser-gen create)))) (funcall parser-gen create))))
(defun origami-get-fold-tree (buffer) (defun origami-get-fold-tree (buffer)