Browse Source

org-element: Fix indentation removal with line breaks

* lisp/org-element.el (org-element-line-break-parser): Tiny
  refactoring.
(org-element-normalize-contents): Take into consideration line breaks
when removing common indentation.  Small refactoring.

* testing/lisp/test-org-element.el (test-org-element/normalize-contents):
  Add tests.
Nicolas Goaziou 4 years ago
parent
commit
bf4a645cac
2 changed files with 65 additions and 39 deletions
  1. 45 37
      lisp/org-element.el
  2. 20 2
      testing/lisp/test-org-element.el

+ 45 - 37
lisp/org-element.el

@@ -2998,7 +2998,7 @@ Assume point is at the beginning of the line break."
 	     (not (eq (char-before) ?\\)))
     (list 'line-break
 	  (list :begin (point)
-		:end (progn (forward-line) (point))
+		:end (line-beginning-position 2)
 		:post-blank 0))))
 
 (defun org-element-line-break-interpreter (line-break contents)
@@ -4514,25 +4514,29 @@ indentation is not done with TAB characters."
 	 (find-min-ind
 	  ;; Return minimal common indentation within BLOB.  This is
 	  ;; done by walking recursively BLOB and updating MIN-IND
-	  ;; along the way.  FIRST-FLAG is non-nil when the first
-	  ;; string hasn't been seen yet.  It is required as this
-	  ;; string is the only one whose indentation doesn't happen
-	  ;; after a newline character.
+	  ;; along the way.  FIRST-FLAG is non-nil when the next
+	  ;; object is expected to be a string that doesn't start with
+	  ;; a newline character.  It happens for strings at the
+	  ;; beginnings of the contents or right after a line break.
 	  (lambda (blob first-flag)
 	    (dolist (object (org-element-contents blob))
-	      (when (and first-flag (stringp object))
+	      (when first-flag
 		(setq first-flag nil)
-		(string-match "\\` *" object)
-		(let ((len (match-end 0)))
-		  ;; An indentation of zero means no string will be
-		  ;; modified.  Quit the process.
-		  (if (zerop len) (throw 'zero (setq min-ind 0))
-		    (setq min-ind (min len min-ind)))))
+		;; Objects cannot start with spaces: in this case,
+		;; indentation is 0.
+		(if (not (stringp object)) (throw 'zero (setq min-ind 0))
+		  (string-match "\\` *" object)
+		  (let ((len (match-end 0)))
+		    ;; An indentation of zero means no string will be
+		    ;; modified.  Quit the process.
+		    (if (zerop len) (throw 'zero (setq min-ind 0))
+		      (setq min-ind (min len min-ind))))))
 	      (cond
 	       ((stringp object)
 		(dolist (line (cdr (org-split-string object " *\n")))
 		  (unless (string= line "")
 		    (setq min-ind (min (org-get-indentation line) min-ind)))))
+	       ((eq (org-element-type object) 'line-break) (setq first-flag t))
 	       ((memq (org-element-type object) org-element-recursive-objects)
 		(funcall find-min-ind object first-flag)))))))
     ;; Find minimal indentation in ELEMENT.
@@ -4542,31 +4546,35 @@ indentation is not done with TAB characters."
       ;; string minus common indentation.
       (let* (build			; For byte compiler.
 	     (build
-	      (function
-	       (lambda (blob first-flag)
-		 ;; Return BLOB with all its strings indentation
-		 ;; shortened from MIN-IND white spaces.  FIRST-FLAG
-		 ;; is non-nil when the first string hasn't been seen
-		 ;; yet.
-		 (setcdr (cdr blob)
-			 (mapcar
-			  #'(lambda (object)
-			      (when (and first-flag (stringp object))
-				(setq first-flag nil)
-				(setq object
-				      (replace-regexp-in-string
-				       (format "\\` \\{%d\\}" min-ind)
-				       "" object)))
-			      (cond
-			       ((stringp object)
-				(replace-regexp-in-string
-				 (format "\n \\{%d\\}" min-ind) "\n" object))
-			       ((memq (org-element-type object)
-				      org-element-recursive-objects)
-				(funcall build object first-flag))
-			       (t object)))
-			  (org-element-contents blob)))
-		 blob))))
+	      (lambda (blob first-flag)
+		;; Return BLOB with all its strings indentation
+		;; shortened from MIN-IND white spaces.  FIRST-FLAG is
+		;; non-nil when the next object is expected to be
+		;; a string that doesn't start with a newline
+		;; character.
+		(setcdr (cdr blob)
+			(mapcar
+			 (lambda (object)
+			   (when first-flag
+			     (setq first-flag nil)
+			     (when (stringp object)
+			       (setq object
+				     (replace-regexp-in-string
+				      (format "\\` \\{%d\\}" min-ind)
+				      "" object))))
+			   (cond
+			    ((stringp object)
+			     (replace-regexp-in-string
+			      (format "\n \\{%d\\}" min-ind) "\n" object))
+			    ((memq (org-element-type object)
+				   org-element-recursive-objects)
+			     (funcall build object first-flag))
+			    ((eq (org-element-type object) 'line-break)
+			     (setq first-flag t)
+			     object)
+			    (t object)))
+			 (org-element-contents blob)))
+		blob)))
 	(funcall build element (not ignore-first))))))
 
 

+ 20 - 2
testing/lisp/test-org-element.el

@@ -3067,12 +3067,18 @@ Text
      '(paragraph nil "  Two spaces\n   Three spaces"))
     '(paragraph nil "Two spaces\n Three spaces")))
   ;; Ignore objects within contents when computing maximum common
-  ;; indentation.
+  ;; indentation.  However, if contents start with an object, common
+  ;; indentation is 0.
   (should
    (equal
     (org-element-normalize-contents
      '(paragraph nil " One " (emphasis nil "space") "\n  Two spaces"))
     '(paragraph nil "One " (emphasis nil "space") "\n Two spaces")))
+  (should
+   (equal
+    (org-element-normalize-contents
+     '(paragraph nil (verbatim nil "V") "No space\n  Two\n   Three"))
+    '(paragraph nil (verbatim nil "V") "No space\n  Two\n   Three")))
   ;; Ignore blank lines.
   (should
    (equal
@@ -3101,7 +3107,19 @@ Text
    (equal
     (org-element-normalize-contents
      '(paragraph nil "No space\n  Two spaces\n   Three spaces") t)
-    '(paragraph nil "No space\nTwo spaces\n Three spaces"))))
+    '(paragraph nil "No space\nTwo spaces\n Three spaces")))
+  (should
+   (equal
+    (org-element-normalize-contents
+     '(paragraph nil (verbatim nil "V") "No space\n  Two\n   Three") t)
+    '(paragraph nil (verbatim nil "V") "No space\nTwo\n Three")))
+  ;; Corner case: do not ignore indentation of string right after
+  ;; a line break.
+  (should
+   (equal
+    (org-element-normalize-contents
+     '(paragraph nil " 1 space" (line-break) "  2 spaces"))
+    '(paragraph nil "1 space" (line-break) " 2 spaces"))))