Browse Source

org-element: Add a function to fill an element

* contrib/lisp/org-element.el (org-element-fill-paragraph): New function.
* testing/lisp/test-org-element.el: Add tests.
Nicolas Goaziou 13 years ago
parent
commit
e4c2540b68
2 changed files with 121 additions and 0 deletions
  1. 77 0
      contrib/lisp/org-element.el
  2. 44 0
      testing/lisp/test-org-element.el

+ 77 - 0
contrib/lisp/org-element.el

@@ -4135,6 +4135,83 @@ modified."
 	      (reverse contents))))))
 	      (reverse contents))))))
     (funcall unindent-tree (org-element-contents parse-tree))))
     (funcall unindent-tree (org-element-contents parse-tree))))
 
 
+(defun org-element-fill-paragraph (&optional justify)
+  "Fill element at point, when applicable.
+
+This function only applies to paragraph, comment blocks, example
+blocks and fixed-width areas.  Also, as a special case, re-align
+table when point is at one.
+
+If JUSTIFY is non-nil (interactively, with prefix argument),
+justify as well.  If `sentence-end-double-space' is non-nil, then
+period followed by one space does not end a sentence, so don't
+break a line there.  The variable `fill-column' controls the
+width for filling."
+  (let ((element (org-element-at-point)))
+    (case (org-element-type element)
+      ;; Align Org tables, leave table.el tables as-is.
+      (table-row (org-table-align) t)
+      (table
+       (when (eq (org-element-property :type element) 'org) (org-table-align))
+       t)
+      ;; Elements that may contain `line-break' type objects.
+      ((paragraph verse-block)
+       (let ((beg (org-element-property :contents-begin element))
+             (end (org-element-property :contents-end element)))
+         ;; Do nothing if point is at an affiliated keyword or at
+         ;; verse block markers.
+         (if (or (< (point) beg) (>= (point) end)) t
+           ;; At a verse block, first narrow to current "paragraph"
+           ;; and set current element to that paragraph.
+           (save-restriction
+             (when (eq (org-element-type element) 'verse-block)
+               (narrow-to-region beg end)
+               (save-excursion
+                 (end-of-line)
+                 (let ((bol-pos (point-at-bol)))
+                   (re-search-backward org-element-paragraph-separate nil 'move)
+                   (unless (or (bobp) (= (point-at-bol) bol-pos))
+                     (forward-line))
+                   (setq element (org-element-paragraph-parser)
+                         beg (org-element-property :contents-begin element)
+                         end (org-element-property :contents-end element)))))
+             ;; Fill paragraph, taking line breaks into consideration.
+             ;; For that, slice the paragraph using line breaks as
+             ;; separators, and fill the parts in reverse order to
+             ;; avoid messing with markers.
+             (save-excursion
+               (goto-char end)
+               (mapc
+                (lambda (pos)
+                  (fill-region-as-paragraph pos (point) justify)
+                  (goto-char pos))
+                ;; Find the list of ending positions for line breaks
+                ;; in the current paragraph.  Add paragraph beginning
+                ;; to include first slice.
+                (nreverse
+                 (cons beg
+                       (org-element-map
+                        (org-element-parse-objects
+                         beg end nil org-element-all-objects)
+                        'line-break
+                        (lambda (lb) (org-element-property :end lb)))))))) t)))
+      ;; Elements whose contents should be filled as plain text.
+      ((comment-block example-block)
+       (save-restriction
+         (narrow-to-region
+          (save-excursion
+            (goto-char (org-element-property :begin element))
+            (while (looking-at org-element--affiliated-re) (forward-line))
+	    (forward-line)
+            (point))
+          (save-excursion
+	    (goto-char (org-element-property :end element))
+	    (if (bolp) (forward-line -1) (beginning-of-line))
+	    (point)))
+         (fill-paragraph justify) t))
+      ;; Ignore every other element.
+      (otherwise t))))
+
 
 
 (provide 'org-element)
 (provide 'org-element)
 ;;; org-element.el ends here
 ;;; org-element.el ends here

+ 44 - 0
testing/lisp/test-org-element.el

@@ -1999,6 +1999,50 @@ Outside."
     (should (equal (buffer-string) "Para2\n\n\nParagraph 1\n\nPara3"))
     (should (equal (buffer-string) "Para2\n\n\nParagraph 1\n\nPara3"))
     (should (looking-at " 1"))))
     (should (looking-at " 1"))))
 
 
+(ert-deftest test-org-element/fill-paragraph ()
+  "Test `org-element-fill-paragraph' specifications."
+  ;; At an Org table, align it.
+  (org-test-with-temp-text "|a|"
+    (org-element-fill-paragraph)
+    (should (equal (buffer-string) "| a |\n")))
+  ;; At a paragraph, preserve line breaks.
+  (org-test-with-temp-text "some \\\\\nlong\ntext"
+    (let ((fill-column 20))
+      (org-element-fill-paragraph)
+      (should (equal (buffer-string) "some \\\\\nlong text"))))
+  ;; At a verse block, fill paragraph at point, also preserving line
+  ;; breaks.  Though, do nothing when point is at the block
+  ;; boundaries.
+  (org-test-with-temp-text "#+BEGIN_VERSE\nSome \\\\\nlong\ntext\n#+END_VERSE"
+    (forward-line)
+    (let ((fill-column 20))
+      (org-element-fill-paragraph)
+      (should (equal (buffer-string)
+		     "#+BEGIN_VERSE\nSome \\\\\nlong text\n#+END_VERSE"))))
+  (org-test-with-temp-text "#+BEGIN_VERSE\nSome \\\\\nlong\ntext\n#+END_VERSE"
+    (let ((fill-column 20))
+      (org-element-fill-paragraph)
+      (should (equal (buffer-string)
+		     "#+BEGIN_VERSE\nSome \\\\\nlong\ntext\n#+END_VERSE"))))
+  ;; Fill contents of `comment-block' and `example-block' elements.
+  (org-test-with-temp-text "#+BEGIN_COMMENT\nSome\ntext\n#+END_COMMENT"
+    (let ((fill-column 20))
+      (forward-line)
+      (org-element-fill-paragraph)
+      (should (equal (buffer-string)
+		     "#+BEGIN_COMMENT\nSome text\n#+END_COMMENT"))))
+  (org-test-with-temp-text "#+BEGIN_EXAMPLE\nSome\ntext\n#+END_EXAMPLE"
+    (let ((fill-column 20))
+      (forward-line)
+      (org-element-fill-paragraph)
+      (should (equal (buffer-string)
+		     "#+BEGIN_EXAMPLE\nSome text\n#+END_EXAMPLE"))))
+  ;; Do nothing at affiliated keywords.
+  (org-test-with-temp-text "#+NAME: para\nSome\ntext."
+    (let ((fill-column 20))
+      (org-element-fill-paragraph)
+      (should (equal (buffer-string) "#+NAME: para\nSome\ntext.")))))
+
 
 
 (provide 'test-org-element)
 (provide 'test-org-element)
 ;;; test-org-element.el ends here
 ;;; test-org-element.el ends here