diff --git a/src/proto.h b/src/proto.h
index 3a917a4091cff5b5c71353f3f2fbee9a05763157..66fb9eac318db3c9815b81d57d054c4cabf1db7c 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -707,6 +707,8 @@ void edit_draw(filestruct *fileptr, const char *converted,
 	int line, size_t from_col);
 int update_line(filestruct *fileptr, size_t index);
 bool need_horizontal_scroll(const size_t old_column, const size_t new_column);
+int go_back_chunks(int nrows, filestruct **line, size_t *leftedge);
+int go_forward_chunks(int nrows, filestruct **line, size_t *leftedge);
 void edit_scroll(scroll_dir direction, int nrows);
 void edit_redraw(filestruct *old_current);
 void edit_refresh(void);
diff --git a/src/winio.c b/src/winio.c
index 8f99fa8eb1d7449ce47e6e6b01d8fdd84a73c902..522bdae908d7534e9699ed6d1181c66964b402fa 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -2753,6 +2753,88 @@ void compute_maxlines(void)
 	maxlines = editwinrows;
 }
 
+/* Try to move up nrows softwrapped chunks from the given line and the
+ * given column (leftedge).  After moving, leftedge will be set to the
+ * starting column of the current chunk.  Return the number of chunks we
+ * couldn't move up, which will be zero if we completely succeeded. */
+int go_back_chunks(int nrows, filestruct **line, size_t *leftedge)
+{
+    int i;
+
+    /* Don't move more chunks than the window can hold. */
+    if (nrows > editwinrows - 1)
+	nrows = (editwinrows < 2) ? 1 : editwinrows - 1;
+
+#ifndef NANO_TINY
+    if (ISSET(SOFTWRAP)) {
+	size_t current_chunk = (*leftedge) / editwincols;
+
+	for (i = nrows; i > 0; i--) {
+	    if (current_chunk > 0) {
+		current_chunk--;
+		continue;
+	    }
+
+	    if (*line == openfile->fileage)
+		break;
+
+	    *line = (*line)->prev;
+	    current_chunk = strlenpt((*line)->data) / editwincols;
+	}
+
+	/* Only change leftedge when we actually could move. */
+	if (i < nrows)
+	    *leftedge = current_chunk * editwincols;
+    } else
+#endif
+	for (i = nrows; i > 0 && (*line)->prev != NULL; i--)
+	    *line = (*line)->prev;
+
+    return i;
+}
+
+/* Try to move down nrows softwrapped chunks from the given line and the
+ * given column (leftedge).  After moving, leftedge will be set to the
+ * starting column of the current chunk.  Return the number of chunks we
+ * couldn't move down, which will be zero if we completely succeeded. */
+int go_forward_chunks(int nrows, filestruct **line, size_t *leftedge)
+{
+    int i;
+
+    /* Don't move more chunks than the window can hold. */
+    if (nrows > editwinrows - 1)
+	nrows = (editwinrows < 2) ? 1 : editwinrows - 1;
+
+#ifndef NANO_TINY
+    if (ISSET(SOFTWRAP)) {
+	size_t current_chunk = (*leftedge) / editwincols;
+	size_t last_chunk = strlenpt((*line)->data) / editwincols;
+
+	for (i = nrows; i > 0; i--) {
+	    if (current_chunk < last_chunk) {
+		current_chunk++;
+		continue;
+	    }
+
+	    if (*line == openfile->filebot)
+		break;
+
+	    *line = (*line)->next;
+	    current_chunk = 0;
+	    last_chunk = strlenpt((*line)->data) / editwincols;
+	}
+
+	/* Only change leftedge when we actually could move. */
+	if (i < nrows)
+	    *leftedge = current_chunk * editwincols;
+    } else
+#endif
+	for (i = nrows; i > 0 && (*line)->next != NULL; i--)
+	    *line = (*line)->next;
+
+    return i;
+}
+
 /* Scroll the edit window in the given direction and the given number of rows,
  * and draw new lines on the blank lines left after the scrolling.  We change
  * edittop, and assume that current and current_x are up to date. */