Add indent parser
This commit is contained in:
		
							
								
								
									
										52
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								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 | 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 | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -377,11 +377,12 @@ was last built." | |||||||
|                                         children |                                         children | ||||||
|                                         (or (-> (origami-fold-find-path-with-range |                                         (or (-> (origami-fold-find-path-with-range | ||||||
|                                                  (origami-get-cached-tree buffer) beg end) |                                                  (origami-get-cached-tree buffer) beg end) | ||||||
|                                               -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) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user