From 8d3e7f32176c7964d600981717976f32dd729e21 Mon Sep 17 00:00:00 2001
From: David Lawrence Ramsey <pooka109@gmail.com>
Date: Thu, 13 May 2004 17:28:03 +0000
Subject: [PATCH] add support for Pico's ability to justify the entire file at
 once ("full justify", accessible via ^W^J)

git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@1731 35c25a1d-7b9e-4130-9fde-d3aeb78583b8
---
 ChangeLog    |  13 ++
 TODO         |   1 +
 src/global.c |   7 +-
 src/nano.c   | 368 ++++++++++++++++++++++++++++-----------------------
 src/nano.h   |   1 +
 src/proto.h  |   4 +-
 src/winio.c  |  11 +-
 7 files changed, 240 insertions(+), 165 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 1353858a..51d29021 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -34,6 +34,10 @@ CVS code -
 	- Clarifications to comments explaining exactly what control
 	  characters and escape sequences are supported. (DLR)
 	- Disable "Where Is Next" in tiny mode. (DLR)
+	- Added the ability to justify the entire file at once, which
+	  Pico has via ^W^J.  Changes to do_justify(); new functions
+	  do_justify_void() and do_full_justify().  Note that this
+	  feature is disabled if justification is disabled. (DLR)
 - files.c:
   add_open_file()
 	- Rearrange the NANO_SMALL #ifdef so that the code to set the
@@ -111,6 +115,11 @@ CVS code -
 	  never be negative. (DLR)
 	- Add proper support for the keypad values and escape sequences
 	  generated by the NumLock glitch. (DLR)
+  bottombars()
+	- Don't display any more than MAIN_VISIBLE shortcuts.  Adding
+	  justification of the entire file above made the search
+	  shortcut list longer than this and hence made the shortcuts in
+	  it so short as to be almost incomprehensible. (DLR)
   edit_add()
 	- Minor cosmetic reformatting.  Also remove unused int
 	  searched_later_lines. (DLR)
@@ -153,6 +162,10 @@ CVS code -
 	  technically also work with automake 1.6c, but that is a CVS
 	  version as opposed to a stable release version, and automake
 	  1.7 requires at least autoconf 2.54 in any case. (DLR)
+- TODO:
+	- Added entry for justifying the entire file at once, since Pico
+	  can do it, and with "[DONE]" since it's already been
+	  implemented. (DLR)
 
 GNU nano 1.3.2 - 2004.03.31
 - General:
diff --git a/TODO b/TODO
index 8f8f51c0..349ae1d6 100644
--- a/TODO
+++ b/TODO
@@ -4,6 +4,7 @@ TODO file (? means the feature may be implemented, but not definitely)
 For version 1.4:
 - UTF-8 support.
 - Support for paragraph searches. [DONE]
+- Support for justifying the entire file at once. [DONE]
 - Support for filename searches in the file browser.
 - Undo/Redo key?
 - Rebindable keys?
diff --git a/src/global.c b/src/global.c
index 3b1a0fa2..f064339b 100644
--- a/src/global.c
+++ b/src/global.c
@@ -473,7 +473,7 @@ void shortcut_init(int unjustify)
 		IFHELP(nano_justify_msg, NANO_NO_KEY),
 		NANO_JUSTIFY_FKEY, NANO_NO_KEY, NOVIEW,
 #ifndef NANO_SMALL
-		do_justify
+		do_justify_void
 #else
 		nano_disabled_msg
 #endif
@@ -681,6 +681,11 @@ void shortcut_init(int unjustify)
     sc_init_one(&whereis_list, NANO_PARAEND_KEY, _("End of Par"),
 		IFHELP(nano_paraend_msg, NANO_NO_KEY), NANO_NO_KEY,
 		NANO_NO_KEY, VIEW, do_para_end);
+
+    /* Translators: try to keep this string under 10 characters long */
+    sc_init_one(&whereis_list, NANO_FULLJUSTIFY_KEY, _("FullJstify"),
+		IFHELP(nano_paraend_msg, NANO_NO_KEY), NANO_NO_KEY,
+		NANO_NO_KEY, VIEW, do_full_justify);
 #endif
 
 #ifndef NANO_SMALL
diff --git a/src/nano.c b/src/nano.c
index 0d7a2ddc..ac9e4b18 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -1922,7 +1922,7 @@ int justify_format(int changes_allowed, filestruct *line, size_t skip)
     back = line->data + skip;
     front = back;
     for (front = back; ; front++) {
-	int remove_space = 0;
+	int remove_space = FALSE;
 	    /* Do we want to remove this space? */
 
 	if (*front == '\t') {
@@ -1934,10 +1934,10 @@ int justify_format(int changes_allowed, filestruct *line, size_t skip)
 	if ((*front == '\0' || *front == ' ') && *(front - 1) == ' ') {
 	    const char *bob = front - 2;
 
-	    remove_space = 1;
+	    remove_space = TRUE;
 	    for (bob = back - 2; bob >= line->data + skip; bob--) {
 		if (strchr(punct, *bob) != NULL) {
-		    remove_space = 0;
+		    remove_space = FALSE;
 		    break;
 		}
 		if (strchr(brackets, *bob) == NULL)
@@ -2063,7 +2063,7 @@ filestruct *backup_lines(filestruct *first_line, size_t par_len,
 	if (alice == mark_beginbuf)
 	    mark_beginbuf = bob;
 #endif
-	justify_format(1, bob,
+	justify_format(TRUE, bob,
 			quote_len + indent_length(bob->data + quote_len));
 
 	assert(alice != NULL && bob != NULL);
@@ -2104,7 +2104,7 @@ int break_line(const char *line, int goal, int force)
 	/* Current tentative return value.  Index of the last space we
 	 * found with short enough display width.  */
     int cur_loc = 0;
-	/* Current index in line */
+	/* Current index in line. */
 
     assert(line != NULL);
     for (; *line != '\0' && goal >= 0; line++, cur_loc++) {
@@ -2400,8 +2400,9 @@ int do_para_end(void)
     return do_para_search(2, NULL, NULL, NULL, TRUE);
 }
 
-/* Justify a paragraph. */
-int do_justify(void)
+/* If full_justify is TRUE, justify the entire file.  Otherwise, justify
+ * the current paragraph. */
+int do_justify(int full_justify)
 {
     size_t quote_len;
 	/* Length of the initial quotation of the paragraph we
@@ -2435,184 +2436,217 @@ int do_justify(void)
     size_t indent_len;	/* Generic indentation length. */
     size_t i;		/* Generic loop variable. */
 
-    /* First, search for the beginning of the current paragraph or, if
-     * we're at the end of it, the beginning of the next paragraph.
-     * Save the quote length, paragraph length, and indentation length
-     * and don't refresh the screen yet (since we'll do that after we
-     * justify).  If the search failed, refresh the screen and get
-     * out. */
-    if (do_para_search(0, &quote_len, &par_len, &indent_len, FALSE) != 0) {
-	edit_refresh();
-	return 0;
+    /* If we're justifying the entire file, start at the beginning of
+     * it and move the first line of the text we're justifying to it. */
+    if (full_justify) {
+	current = fileage;
+	last_par_line = fileage;
     }
 
-/* Next step, we loop through the lines of this paragraph, justifying
- * each one individually. */
-    for (; par_len > 0; current_y++, par_len--) {
-	size_t line_len;
-	size_t display_len;
-	    /* The width of current in screen columns. */
-	int break_pos;
-	    /* Where we will break the line. */
-
-	indent_len = indent_length(current->data + quote_len) +
-			quote_len; 
-	/* justify_format() removes excess spaces from the line, and
-	 * changes tabs to spaces.  The first argument, 0, means don't
-	 * change the line, just say whether there are changes to be
-	 * made.  If there are, we do backup_lines(), which copies the
-	 * original paragraph to the cutbuffer for unjustification, and
-	 * then calls justify_format() on the remaining lines. */
-	if (first_mod_line == NULL && justify_format(0, current, indent_len))
-	    first_mod_line = backup_lines(current, par_len, quote_len);
-
-	line_len = strlen(current->data);
-	display_len = strlenpt(current->data);
-
-	if (display_len > fill) {
-	    /* The line is too long.  Try to wrap it to the next. */
-	    break_pos = break_line(current->data + indent_len,
-			    fill - strnlenpt(current->data, indent_len),
-			    1);
-	    if (break_pos == -1 || break_pos + indent_len == line_len)
-		/* We can't break the line, or don't need to, so just go
-		 * on to the next. */
-		goto continue_loc;
-	    break_pos += indent_len;
-	    assert(break_pos < line_len);
-	    /* If we haven't backed up the paragraph, do it now. */
-	    if (first_mod_line == NULL)
-		first_mod_line = backup_lines(current, par_len, quote_len);
-	    if (par_len == 1) {
-		/* There is no next line in this paragraph.  We make a
-		 * new line and copy text after break_pos into it. */
-		splice_node(current, make_new_node(current),
-				current->next);
-		/* In a non-quoted paragraph, we copy the indent only if
-		   AUTOINDENT is turned on. */
-		if (quote_len == 0)
+    while (TRUE) {
+
+	/* First, search for the beginning of the current paragraph or,
+	 * if we're at the end of it, the beginning of the next
+	 * paragraph.  Save the quote length, paragraph length, and
+	 * indentation length and don't refresh the screen yet (since
+	 * we'll do that after we justify).  If the search failed and
+	 * we're justifying the whole file, move the last line of the
+	 * text we're justifying to just before the magicline, which is
+	 * where it'll be anyway if we've searched the entire file, and
+	 * break out of the loop; otherwise, refresh the screen and get
+	 * out. */
+	if (do_para_search(0, &quote_len, &par_len, &indent_len, FALSE) != 0) {
+	    if (full_justify) {
+		/* This should be safe in the event of filebot->prev's
+		 * being NULL, since only last_par_line->next is used if
+		 * we eventually unjustify. */
+		last_par_line = filebot->prev;
+		break;
+	    } else {
+		edit_refresh();
+		return 0;
+	    }
+	}
+
+	/* Next step, we loop through the lines of this paragraph,
+	 * justifying each one individually. */
+	for (; par_len > 0; current_y++, par_len--) {
+	    size_t line_len;
+	    size_t display_len;
+		/* The width of current in screen columns. */
+	    int break_pos;
+		/* Where we will break the line. */
+
+	    indent_len = indent_length(current->data + quote_len) +
+		quote_len; 
+
+	    /* justify_format() removes excess spaces from the line, and
+	     * changes tabs to spaces.  The first argument, 0, means
+	     * don't change the line, just say whether there are changes
+	     * to be made.  If there are, we do backup_lines(), which
+	     * copies the original paragraph to the cutbuffer for
+	     * unjustification, and then calls justify_format() on the
+	     * remaining lines. */
+	    if (first_mod_line == NULL && justify_format(FALSE, current, indent_len))
+		first_mod_line = backup_lines(current, full_justify ?
+			filebot->lineno - current->lineno : par_len, quote_len);
+
+	    line_len = strlen(current->data);
+	    display_len = strlenpt(current->data);
+
+	    if (display_len > fill) {
+		/* The line is too long.  Try to wrap it to the next. */
+	        break_pos = break_line(current->data + indent_len,
+			fill - strnlenpt(current->data, indent_len), TRUE);
+		if (break_pos == -1 || break_pos + indent_len == line_len)
+		    /* We can't break the line, or don't need to, so
+		     * just go on to the next. */
+		    goto continue_loc;
+		break_pos += indent_len;
+		assert(break_pos < line_len);
+		/* If we haven't backed up the paragraph, do it now. */
+		if (first_mod_line == NULL)
+		    first_mod_line = backup_lines(current, full_justify ?
+			filebot->lineno - current->lineno : par_len, quote_len);
+		if (par_len == 1) {
+		    /* There is no next line in this paragraph.  We make
+		     * a new line and copy text after break_pos into
+		     * it. */
+		    splice_node(current, make_new_node(current), current->next);
+		    /* In a non-quoted paragraph, we copy the indent
+		     * only if AUTOINDENT is turned on. */
+		    if (quote_len == 0)
 #ifndef NANO_SMALL
-		    if (!ISSET(AUTOINDENT))
-#endif
-			indent_len = 0;
-		current->next->data = charalloc(indent_len + line_len -
-						break_pos);
-		strncpy(current->next->data, current->data,
-			indent_len);
-		strcpy(current->next->data + indent_len,
+			if (!ISSET(AUTOINDENT))
+#endif
+			    indent_len = 0;
+		    current->next->data = charalloc(indent_len + line_len -
+			break_pos);
+		    strncpy(current->next->data, current->data, indent_len);
+		    strcpy(current->next->data + indent_len,
 			current->data + break_pos + 1);
-		assert(strlen(current->next->data) ==
+		    assert(strlen(current->next->data) ==
 			indent_len + line_len - break_pos - 1);
-		totlines++;
-		totsize += indent_len;
-		par_len++;
-	    } else {
-		size_t next_line_len = strlen(current->next->data);
+		    totlines++;
+		    totsize += indent_len;
+		    par_len++;
+		} else {
+		    size_t next_line_len = strlen(current->next->data);
 
-		indent_len = quote_len +
+		    indent_len = quote_len +
 			indent_length(current->next->data + quote_len);
-		current->next->data = charealloc(current->next->data,
+		    current->next->data = charealloc(current->next->data,
 			next_line_len + line_len - break_pos + 1);
 
-		charmove(current->next->data + indent_len + line_len - break_pos,
-			current->next->data + indent_len,
+		    charmove(current->next->data + indent_len + line_len -
+			break_pos, current->next->data + indent_len,
 			next_line_len - indent_len + 1);
-		strcpy(current->next->data + indent_len,
+		    strcpy(current->next->data + indent_len,
 			current->data + break_pos + 1);
-		current->next->data[indent_len + line_len - break_pos - 1]
+		    current->next->data[indent_len + line_len - break_pos - 1]
 			= ' ';
 #ifndef NANO_SMALL
-		if (mark_beginbuf == current->next) {
-		    if (mark_beginx < indent_len)
-			mark_beginx = indent_len;
-		    mark_beginx += line_len - break_pos;
-		}
+		    if (mark_beginbuf == current->next) {
+			if (mark_beginx < indent_len)
+			    mark_beginx = indent_len;
+			mark_beginx += line_len - break_pos;
+		    }
 #endif
-	    }
+		}
 #ifndef NANO_SMALL
-	    if (mark_beginbuf == current && mark_beginx > break_pos) {
-		mark_beginbuf = current->next;
-		mark_beginx -= break_pos + 1 - indent_len;
-	    }
+		if (mark_beginbuf == current && mark_beginx > break_pos) {
+		    mark_beginbuf = current->next;
+		    mark_beginx -= break_pos + 1 - indent_len;
+		}
 #endif
-	    null_at(&current->data, break_pos);
-	    current = current->next;
-	} else if (display_len < fill && par_len > 1) {
-	    size_t next_line_len;
+		null_at(&current->data, break_pos);
+		current = current->next;
+	    } else if (display_len < fill && par_len > 1) {
+		size_t next_line_len;
 
-	    indent_len = quote_len +
+		indent_len = quote_len +
 			indent_length(current->next->data + quote_len);
-	    /* If we can't pull a word from the next line up to this
-	     * one, just go on. */
-	    if (!breakable(current->next->data + indent_len,
-		    fill - display_len - 1))
-		goto continue_loc;
-
-	    /* If we haven't backed up the paragraph, do it now. */
-	    if (first_mod_line == NULL)
-		first_mod_line = backup_lines(current, par_len, quote_len);
-
-	    break_pos = break_line(current->next->data + indent_len,
-				fill - display_len - 1, FALSE);
-	    assert(break_pos != -1);
-
-	    current->data = charealloc(current->data,
-					line_len + break_pos + 2);
-	    current->data[line_len] = ' ';
-	    strncpy(current->data + line_len + 1,
+		/* If we can't pull a word from the next line up to this
+		 * one, just go on. */
+		if (!breakable(current->next->data + indent_len,
+			fill - display_len - 1))
+		    goto continue_loc;
+
+		/* If we haven't backed up the paragraph, do it now. */
+		if (first_mod_line == NULL)
+		    first_mod_line = backup_lines(current, full_justify ?
+			filebot->lineno - current->lineno : par_len, quote_len);
+
+		break_pos = break_line(current->next->data + indent_len,
+			fill - display_len - 1, FALSE);
+		assert(break_pos != -1);
+
+		current->data = charealloc(current->data,
+			line_len + break_pos + 2);
+		current->data[line_len] = ' ';
+		strncpy(current->data + line_len + 1,
 			current->next->data + indent_len, break_pos);
-	    current->data[line_len + break_pos + 1] = '\0';
+		current->data[line_len + break_pos + 1] = '\0';
 #ifndef NANO_SMALL
-	    if (mark_beginbuf == current->next) {
-		if (mark_beginx < indent_len + break_pos) {
- 		    mark_beginbuf = current;
-		    if (mark_beginx <= indent_len)
-			mark_beginx = line_len + 1;
-		    else
-			mark_beginx = line_len + 1 + mark_beginx - indent_len;
-		} else
-		    mark_beginx -= break_pos + 1;
-	    }
+		if (mark_beginbuf == current->next) {
+		    if (mark_beginx < indent_len + break_pos) {
+ 			mark_beginbuf = current;
+			if (mark_beginx <= indent_len)
+			    mark_beginx = line_len + 1;
+			else
+			    mark_beginx = line_len + 1 + mark_beginx - indent_len;
+		    } else
+			mark_beginx -= break_pos + 1;
+		}
 #endif
-	    next_line_len = strlen(current->next->data);
-	    if (indent_len + break_pos == next_line_len) {
-		filestruct *line = current->next;
-
-		/* Don't destroy edittop! */
-		if (line == edittop)
-		    edittop = current;
-
-		unlink_node(line);
-		delete_node(line);
-		totlines--;
-		totsize -= indent_len;
-		current_y--;
-	    } else {
-		charmove(current->next->data + indent_len,
+		next_line_len = strlen(current->next->data);
+		if (indent_len + break_pos == next_line_len) {
+		    filestruct *line = current->next;
+
+		    /* Don't destroy edittop! */
+		    if (line == edittop)
+			edittop = current;
+
+		    unlink_node(line);
+		    delete_node(line);
+		    totlines--;
+		    totsize -= indent_len;
+		    current_y--;
+		} else {
+		    charmove(current->next->data + indent_len,
 			current->next->data + indent_len + break_pos + 1,
 			next_line_len - break_pos - indent_len);
-		null_at(&current->next->data,
-			next_line_len - break_pos);
-		current = current->next;
-	    }
-	} else
+		    null_at(&current->next->data, next_line_len - break_pos);
+		    current = current->next;
+		}
+	    } else
   continue_loc:
-	    current = current->next;
-    }
+		current = current->next;
+	}
 
-/* We are now done justifying the paragraph.  There are cleanup things
- * to do, and we check for unjustify. */
+	/* We are now done justifying the paragraph.  There are cleanup
+	 * things to do, and we check for unjustify. */
 
-    /* totlines, totsize, and current_y have been maintained above.  We
-     * now set last_par_line to the new end of the paragraph, update
-     * fileage, set current_x.  Also, edit_refresh() needs the line
-     * numbers to be right, so we renumber(). */
-    last_par_line = current->prev;
-    if (first_mod_line != NULL) {
-	if (first_mod_line->prev == NULL)
-	    fileage = first_mod_line;
-	renumber(first_mod_line);
-    }
+	/* totlines, totsize, and current_y have been maintained above.
+	 * We now set last_par_line to the new end of the paragraph,
+	 * update fileage, set current_x.  Also, edit_refresh() needs
+	 * the line numbers to be right, so we renumber(). */
+	last_par_line = current->prev;
+	if (first_mod_line != NULL) {
+	    if (first_mod_line->prev == NULL)
+		fileage = first_mod_line;
+	    renumber(first_mod_line);
+	}
+
+	/* We've just justified a paragraph and renumbered the lines in
+	 * it.  If we're not justifying the entire file, break out of
+	 * the loop.  Otherwise, continue the loop so that we justify
+	 * and renumber the lines in all paragraphs down to the end of
+	 * the file. */
+	if (!full_justify)
+	    break;
+
+    } /* while (TRUE) */
 
     if (current_y > editwinrows - 1)
 	edit_update(current, CENTER);
@@ -2620,7 +2654,7 @@ int do_justify(void)
 	edit_refresh();
 
     statusbar(_("Can now UnJustify!"));
-    /* Change the shortcut list to display the unjustify code. */
+    /* Display the shortcut list with UnJustify. */
     shortcut_init(1);
     display_main_list();
     reset_cursor();
@@ -2674,7 +2708,7 @@ int do_justify(void)
 	    last_par_line->next = NULL;
 	    free_filestruct(first_mod_line);
 
-	    /* Restore global variables from before justify */
+	    /* Restore global variables from before the justify. */
 	    totsize = totsize_save;
 	    totlines = filebot->lineno;
 #ifndef NANO_SMALL
@@ -2691,12 +2725,22 @@ int do_justify(void)
     }
     cutbuffer = cutbuffer_save;
     blank_statusbar_refresh();
-    /* display shortcut list with UnCut */
+    /* Display the shortcut list with UnCut. */
     shortcut_init(0);
     display_main_list();
 
     return 0;
 }
+
+int do_justify_void(void)
+{
+    return do_justify(FALSE);
+}
+
+int do_full_justify(void)
+{
+    return do_justify(TRUE);
+}
 #endif /* !DISABLE_JUSTIFY */
 
 int do_exit(void)
diff --git a/src/nano.h b/src/nano.h
index 0f1c883e..ffdd6bfd 100644
--- a/src/nano.h
+++ b/src/nano.h
@@ -423,6 +423,7 @@ typedef struct historyheadtype {
 #define NANO_PREVWORD_KEY	NANO_ALT_SPACE
 #define NANO_PARABEGIN_KEY	NANO_CONTROL_W
 #define NANO_PARAEND_KEY	NANO_CONTROL_O
+#define NANO_FULLJUSTIFY_KEY	NANO_CONTROL_J
 #define NANO_VERBATIM_KEY	NANO_ALT_V
 
 #ifndef NANO_SMALL
diff --git a/src/proto.h b/src/proto.h
index 895ac48d..93f45c10 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -326,7 +326,9 @@ int do_para_search(int search_type, size_t *quote, size_t *par, size_t
 	*indent, int do_refresh);
 int do_para_begin(void);
 int do_para_end(void);
-int do_justify(void);
+int do_justify(int justify_all);
+int do_justify_void(void);
+int do_full_justify(void);
 #endif /* !DISABLE_JUSTIFY */
 int do_exit(void);
 void signal_init(void);
diff --git a/src/winio.c b/src/winio.c
index 44a8b981..3283ad3c 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -1609,9 +1609,14 @@ void bottombars(const shortcut *s)
     if (s == main_list) {
 	slen = MAIN_VISIBLE;
 	assert(MAIN_VISIBLE <= length_of_list(s));
-    } else
+    } else {
 	slen = length_of_list(s);
 
+	/* Don't show any more shortcuts than the main list does */
+	if (slen > MAIN_VISIBLE)
+	    slen = MAIN_VISIBLE;
+    }
+
     /* There will be this many columns of shortcuts */
     numcols = (slen + (slen % 2)) / 2;
 
@@ -2229,6 +2234,10 @@ int statusq(int allowtabs, const shortcut *s, const char *def,
 	do_para_end();
 	resetstatuspos = 1;
 	break;
+    case NANO_FULLJUSTIFY_KEY:
+	do_full_justify();
+	resetstatuspos = 1;
+	break;
 #endif
     case NANO_CANCEL_KEY:
 	ret = -1;
-- 
GitLab