From f23a18c1c1cb5571fedab5f2b3f1e6fbb7938c6b Mon Sep 17 00:00:00 2001
From: David Lawrence Ramsey <pooka109@gmail.com>
Date: Wed, 25 Jul 2018 23:39:28 -0500
Subject: [PATCH] justify: refactor to use justify_paragraph()

This new function is called once for ^J, and multiple times for M-J.
It was originally moved out of do_justify().  Accordingly, in
do_justify(), a few redundant checks for when the user justified
something have been removed.

Major problem: it calls backup_lines(), but since it doesn't know
whether it's being used for ^J or M-J, backup_lines() will back up only
one paragraph at a time.  The code in extract_buffer() that tacks new
lines onto the old is not enough to deal with this, as the unjustified
text between paragraphs is not saved properly.  This leads to a crash
when using M-J.  Commenting out the backup_lines() call makes M-J work
properly, but makes unjustify crash instead.

Making the function justify text in its own buffer instead of the
current buffer will eliminate the need for backup_lines() and fix this,
but doing so is nontrivial.
---
 src/text.c | 167 ++++++++++++++++++++++++++++-------------------------
 1 file changed, 88 insertions(+), 79 deletions(-)

diff --git a/src/text.c b/src/text.c
index b2516209..1ffbe7a7 100644
--- a/src/text.c
+++ b/src/text.c
@@ -2165,44 +2165,13 @@ bool find_paragraph(size_t *const quote, size_t *const par)
 	return TRUE;
 }
 
-/* Justify the current paragraph, and justify the entire file when
- * full_justify is TRUE. */
-void do_justify(bool full_justify)
+/* Justify a single paragraph starting at or after openfile->current.  Return
+ * TRUE if we found a paragraph.  If we did, indicate the starting and ending
+ * lines of the paragraph in first_par_line and last_par_line, and whether
+ * filebot is the last line of the paragraph in filebot_inpar. */
+bool justify_paragraph(filestruct **first_par_line, filestruct **last_par_line,
+						bool *filebot_inpar)
 {
-	filestruct *first_par_line = NULL;
-		/* Will be the first line of the justified paragraph(s), if any.
-		 * For restoring after unjustify. */
-	filestruct *last_par_line = NULL;
-		/* Will be the line after the last line of the justified
-		 * paragraph(s), if any.  Also for restoring after unjustify. */
-	bool filebot_inpar = FALSE;
-		/* Whether the text at filebot is part of the current paragraph. */
-	int kbinput;
-		/* The first keystroke after a justification. */
-	functionptrtype func;
-		/* The function associated with that keystroke. */
-
-	/* We save these variables to be restored if the user unjustifies. */
-	filestruct *edittop_save = openfile->edittop;
-	size_t firstcolumn_save = openfile->firstcolumn;
-	filestruct *current_save = openfile->current;
-	size_t current_x_save = openfile->current_x;
-#ifndef NANO_TINY
-	filestruct *was_mark = openfile->mark;
-	size_t was_mark_x = openfile->mark_x;
-#endif
-	bool modified_save = openfile->modified;
-
-	/* Move to the beginning of the current line, so that justifying at
-	 * the end of the last line of the file, if that line isn't blank,
-	 * will work the first time through. */
-	openfile->current_x = 0;
-
-	/* If we're justifying the entire file, start at the beginning. */
-	if (full_justify)
-		openfile->current = openfile->fileage;
-
-	while (TRUE) {
 		filestruct *firstline;
 			/* The first line of the current paragraph. */
 		filestruct *sampleline;
@@ -2225,39 +2194,24 @@ void do_justify(bool full_justify)
 		 * length (number of lines).  Don't refresh the screen yet,
 		 * since we'll do that after we justify.
 		 *
-		 * If the search failed, we do one of two things.  If we're
-		 * justifying the whole file, and we've found at least one
-		 * paragraph, it means that we should justify all the way to the
-		 * last line of the file, so set the last line of the text to be
-		 * justified to the last line of the file and break out of the
-		 * loop.  Otherwise, it means that there are no paragraph(s) to
-		 * justify, so refresh the screen and get out. */
-		if (!find_paragraph(&quote_len, &par_len)) {
-			if (full_justify && first_par_line != NULL) {
-				last_par_line = openfile->filebot;
-				break;
-			} else {
-				refresh_needed = TRUE;
-				return;
-			}
-		}
+		 * If the search failed, it means that there are no paragraph(s) to
+		 * justify, so get out. */
+		if (!find_paragraph(&quote_len, &par_len))
+			return FALSE;
 
 		/* par_len will be one greater than the number of lines between
 		 * current and filebot if filebot is the last line in the
 		 * paragraph.  Set filebot_inpar to TRUE if this is the case. */
-		filebot_inpar = (openfile->current->lineno + par_len ==
+		*filebot_inpar = (openfile->current->lineno + par_len ==
 								openfile->filebot->lineno + 1);
 
-		/* If we haven't already done it, move the original paragraph(s)
-		 * to the justify buffer, splice a copy of the original
-		 * paragraph(s) into the file in the same place, and set
-		 * first_par_line to the first line of the copy. */
-		if (first_par_line == NULL) {
-			backup_lines(openfile->current, full_justify ?
-				openfile->filebot->lineno - openfile->current->lineno +
-				((openfile->filebot->data[0] != '\0') ? 1 : 0) : par_len);
-			first_par_line = openfile->current;
-		}
+		/* Move the original paragraph to the end of the justify buffer, and
+		 * splice a copy of the original paragraph into the file in the same
+		 * place. */
+		backup_lines(openfile->current, par_len);
+
+		/* We're on the first line of the paragraph.  Save it. */
+		*first_par_line = openfile->current;
 
 		/* Remember the first line of the current paragraph. */
 		firstline = openfile->current;
@@ -2379,23 +2333,81 @@ void do_justify(bool full_justify)
 		else
 			openfile->current_x = strlen(openfile->current->data);
 
+		/* We're on the last line of the paragraph.  Save it. */
+		*last_par_line = openfile->current;
+
 		/* Renumber the now-justified paragraph, since both refreshing the
 		 * edit window and finding a paragraph need correct line numbers. */
 		renumber(firstline);
 
-		/* We've just finished justifying the paragraph.  If we're not
-		 * justifying the entire file, break out of the loop.
-		 * Otherwise, continue the loop so that we justify all the
-		 * paragraphs in the file. */
-		if (!full_justify)
-			break;
+		return TRUE;
+}
+
+/* Justify the current paragraph, and justify the entire file when
+ * full_justify is TRUE. */
+void do_justify(bool full_justify)
+{
+	filestruct *first_par_line = NULL;
+		/* Will be the first line of the justified paragraph(s), if any.
+		 * For restoring after unjustify. */
+	filestruct *last_par_line = NULL;
+		/* Will be the line after the last line of the justified
+		 * paragraph(s), if any.  Also for restoring after unjustify. */
+	bool filebot_inpar = FALSE;
+		/* Whether the text at filebot is part of the current paragraph. */
+	bool justified_one = FALSE;
+		/* Whether we justified one paragraph. */
+	bool justified_any = FALSE;
+		/* Whether we justified any paragraphs. */
+	int kbinput;
+		/* The first keystroke after a justification. */
+	functionptrtype func;
+		/* The function associated with that keystroke. */
+
+	/* We save these variables to be restored if the user unjustifies. */
+	filestruct *edittop_save = openfile->edittop;
+	size_t firstcolumn_save = openfile->firstcolumn;
+	filestruct *current_save = openfile->current;
+	size_t current_x_save = openfile->current_x;
+#ifndef NANO_TINY
+	filestruct *was_mark = openfile->mark;
+	size_t was_mark_x = openfile->mark_x;
+#endif
+	bool modified_save = openfile->modified;
+
+	/* Move to the beginning of the current line, so that justifying at
+	 * the end of the last line of the file, if that line isn't blank,
+	 * will work the first time through. */
+	openfile->current_x = 0;
+
+	/* If we're justifying the entire file, start at the beginning. */
+	if (full_justify)
+		openfile->current = openfile->fileage;
+
+	/* Do the actual justify. */
+	do
+	{
+		if ((justified_one = justify_paragraph(&first_par_line, &last_par_line,
+												&filebot_inpar)))
+			justified_any = TRUE;
 	}
+	while (full_justify && justified_one);
 
-	/* We are now done justifying the paragraph or the file, so clean
-	 * up.  totsize has been maintained above.  If we actually justified
-	 * something, set last_par_line to the new end of the paragraph. */
-	if (first_par_line != NULL)
-		last_par_line = openfile->current;
+	/* If we couldn't justify anything, refresh the screen and get out. */
+	if (!justified_any) {
+		refresh_needed = TRUE;
+		return;
+	}
+
+	/* If we're justifying the entire file, stop at the end.  Also, set the new
+	 * beginning and end of the paragraph to the beginning and end of the file,
+	 * since the justify loop above may have set them differently. */
+	if (full_justify) {
+		openfile->current = openfile->filebot;
+
+		first_par_line = openfile->fileage;
+		last_par_line = openfile->filebot;
+	}
 
 #ifndef NANO_TINY
 	/* Let a justification cancel a soft mark. */
@@ -2432,9 +2444,7 @@ void do_justify(bool full_justify)
 						|| func == do_undo
 #endif
 				) {
-		/* If we actually justified something, then splice the preserved
-		 * unjustified text back into the file, */
-		if (first_par_line != NULL) {
+			/* Splice the preserved unjustified text back into the file, */
 			filestruct *trash = NULL, *dummy = NULL;
 
 			/* Throw away the justified paragraph, and replace it with
@@ -2460,7 +2470,6 @@ void do_justify(bool full_justify)
 				titlebar(NULL);
 
 			refresh_needed = TRUE;
-		}
 	} else {
 		/* Put the keystroke back into the queue. */
 		unget_kbinput(kbinput, meta_key);
-- 
2.17.1

