From e4c2540b6859562af0928af99d4925466178468a Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sat, 5 May 2012 17:13:32 +0200 Subject: [PATCH] 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. --- contrib/lisp/org-element.el | 77 ++++++++++++++++++++++++++++++++ testing/lisp/test-org-element.el | 44 ++++++++++++++++++ 2 files changed, 121 insertions(+) diff --git a/contrib/lisp/org-element.el b/contrib/lisp/org-element.el index c4ac62887..fd69fa2c2 100644 --- a/contrib/lisp/org-element.el +++ b/contrib/lisp/org-element.el @@ -4135,6 +4135,83 @@ modified." (reverse contents)))))) (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) ;;; org-element.el ends here diff --git a/testing/lisp/test-org-element.el b/testing/lisp/test-org-element.el index da94268b8..0b76e6318 100644 --- a/testing/lisp/test-org-element.el +++ b/testing/lisp/test-org-element.el @@ -1999,6 +1999,50 @@ Outside." (should (equal (buffer-string) "Para2\n\n\nParagraph 1\n\nPara3")) (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) ;;; test-org-element.el ends here