diff --git a/src/move.c b/src/move.c
index 69181b45c1fd87a667bb9b520e101c05e347e150..883e41b3985e96920c9898720e250763766d22c5 100644
--- a/src/move.c
+++ b/src/move.c
@@ -53,13 +53,8 @@ void get_edge_and_target(size_t *leftedge, size_t *target_column)
 {
 #ifndef NANO_TINY
     if (ISSET(SOFTWRAP)) {
-	size_t realspan = strlenpt(openfile->current->data);
-
-	if (realspan > openfile->placewewant)
-	    realspan = openfile->placewewant;
-
-	*leftedge = get_chunk_leftedge(openfile->current, realspan);
-	*target_column = openfile->placewewant % editwincols;
+	*leftedge = get_chunk_leftedge(openfile->current, xplustabs());
+	*target_column = openfile->placewewant - *leftedge;
     } else
 #endif
     {
@@ -346,15 +341,14 @@ void do_next_word_void(void)
 void do_home(bool be_clever)
 {
     filestruct *was_current = openfile->current;
-    size_t was_column = openfile->placewewant;
+    size_t was_column = xplustabs();
     bool moved_off_chunk = TRUE;
 #ifndef NANO_TINY
     bool moved = FALSE;
-    size_t leftedge_x = 0;
+    size_t leftedge = 0, leftedge_x = 0;
 
     if (ISSET(SOFTWRAP)) {
-	size_t leftedge = get_chunk_leftedge(openfile->current, was_column);
-
+	leftedge = get_chunk_leftedge(openfile->current, was_column);
 	leftedge_x = actual_x(openfile->current->data, leftedge);
     }
 
@@ -378,11 +372,11 @@ void do_home(bool be_clever)
     if (!moved && ISSET(SOFTWRAP)) {
 	/* If already at the left edge of the screen, move fully home.
 	 * Otherwise, move to the left edge. */
-	if (was_column % editwincols == 0 && be_clever)
+	if (openfile->current_x == leftedge_x && be_clever)
 	    openfile->current_x = 0;
 	else {
 	    openfile->current_x = leftedge_x;
-	    openfile->placewewant = (was_column / editwincols) * editwincols;
+	    openfile->placewewant = leftedge;
 	    moved_off_chunk = FALSE;
 	}
     } else if (!moved)
@@ -413,15 +407,26 @@ void do_home_void(void)
 void do_end(bool be_clever)
 {
     filestruct *was_current = openfile->current;
-    size_t was_column = openfile->placewewant;
+    size_t was_column = xplustabs();
     size_t line_len = strlen(openfile->current->data);
     bool moved_off_chunk = TRUE;
 
 #ifndef NANO_TINY
     if (ISSET(SOFTWRAP)) {
+	bool last_chunk;
 	size_t leftedge = get_chunk_leftedge(openfile->current, was_column);
-	size_t rightedge_x = actual_x(openfile->current->data,
-				leftedge + (editwincols - 1));
+	size_t rightedge = get_softwrap_breakpoint(openfile->current->data,
+							leftedge,
+							&last_chunk);
+	size_t rightedge_x;
+	/* If we're on the last chunk, we're already at the end of the line.
+	 * Otherwise, we're one column past the end of the line.  Shifting
+	 * backwards one column might put us in the middle of a multi-column
+	 * character, but actual_x() will fix that. */
+	if (!last_chunk)
+	    rightedge--;
+
+	rightedge_x = actual_x(openfile->current->data, rightedge);
 
 	/* If already at the right edge of the screen, move fully to
 	 * the end of the line.  Otherwise, move to the right edge. */
@@ -429,13 +434,15 @@ void do_end(bool be_clever)
 	    openfile->current_x = line_len;
 	else {
 	    openfile->current_x = rightedge_x;
+	    openfile->placewewant = rightedge;
 	    moved_off_chunk = FALSE;
 	}
     } else
 #endif
 	openfile->current_x = line_len;
 
-    openfile->placewewant = xplustabs();
+    if (moved_off_chunk)
+	openfile->placewewant = xplustabs();
 
     /* If we changed chunk, we might be offscreen.  Otherwise,
      * update current if the mark is on or we changed "page". */
diff --git a/src/winio.c b/src/winio.c
index df571583d99db4d014c95dab2e679a845a608582..e1adccb1e8cd545c9d01f4e3eec1faaf73f2a531 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -2294,6 +2294,7 @@ void place_the_cursor(bool forreal)
 #ifndef NANO_TINY
     if (ISSET(SOFTWRAP)) {
 	filestruct *line = openfile->edittop;
+	size_t leftedge;
 
 	row -= get_chunk_row(openfile->edittop, openfile->firstcolumn);
 
@@ -2304,14 +2305,8 @@ void place_the_cursor(bool forreal)
 	}
 
 	/* Add the number of wraps in the current line before the cursor. */
-	row += get_chunk_row(openfile->current, xpt);
-	col = xpt % editwincols;
-
-	/* If the cursor ought to be in column zero, nudge it there. */
-	if (forreal && openfile->placewewant % editwincols == 0 && col != 0) {
-	    row++;
-	    col = 0;
-	}
+	row += get_chunk(openfile->current, xpt, &leftedge);
+	col = xpt - leftedge;
     } else
 #endif
     {
@@ -2745,10 +2740,10 @@ int update_softwrapped_line(filestruct *fileptr)
 	/* The first row in the edit window that gets updated. */
     size_t from_col = 0;
 	/* The starting column of the current chunk. */
+    size_t to_col = 0;
+	/* To which column a line is displayed. */
     char *converted;
 	/* The data of the chunk with tabs and control characters expanded. */
-    size_t full_length;
-	/* The length of the expanded line. */
 
     if (fileptr == openfile->edittop)
 	from_col = openfile->firstcolumn;
@@ -2768,18 +2763,30 @@ int update_softwrapped_line(filestruct *fileptr)
 	return 0;
     }
 
-    full_length = strlenpt(fileptr->data);
     starting_row = row;
 
-    while (from_col <= full_length && row < editwinrows) {
+    while (row < editwinrows) {
+	bool end_of_line;
+
+	to_col = get_softwrap_breakpoint(fileptr->data, from_col, &end_of_line);
+
 	blank_row(edit, row, 0, COLS);
 
 	/* Convert the chunk to its displayable form and draw it. */
-	converted = display_string(fileptr->data, from_col, editwincols, TRUE);
+	converted = display_string(fileptr->data, from_col, to_col - from_col,
+					TRUE);
 	edit_draw(fileptr, converted, row++, from_col);
 	free(converted);
 
-	from_col += editwincols;
+	if (end_of_line)
+	    break;
+
+	/* If the line is softwrapped before its last column, add a ">" just
+	 * after its softwrap breakpoint. */
+	if (to_col - from_col < editwincols)
+	    mvwaddch(edit, row - 1, to_col - from_col, '>');
+
+	from_col = to_col;
     }
 
     return (row - starting_row);
@@ -2813,12 +2820,13 @@ int go_back_chunks(int nrows, filestruct **line, size_t *leftedge)
 
 #ifndef NANO_TINY
     if (ISSET(SOFTWRAP)) {
-	size_t current_chunk = get_chunk_row(*line, *leftedge);
+	size_t current_leftedge = *leftedge;
 
 	/* Recede through the requested number of chunks. */
 	for (i = nrows; i > 0; i--) {
-	    if (current_chunk > 0) {
-		current_chunk--;
+	    if (current_leftedge > 0) {
+		current_leftedge = get_chunk_leftedge(*line,
+							current_leftedge - 1);
 		continue;
 	    }
 
@@ -2826,12 +2834,12 @@ int go_back_chunks(int nrows, filestruct **line, size_t *leftedge)
 		break;
 
 	    *line = (*line)->prev;
-	    current_chunk = get_last_chunk_row(*line);
+	    current_leftedge = get_last_chunk_leftedge(*line);
 	}
 
 	/* Only change leftedge when we actually could move. */
 	if (i < nrows)
-	    *leftedge = current_chunk * editwincols;
+	    *leftedge = current_leftedge;
     } else
 #endif
 	for (i = nrows; i > 0 && (*line)->prev != NULL; i--)
@@ -2854,13 +2862,17 @@ int go_forward_chunks(int nrows, filestruct **line, size_t *leftedge)
 
 #ifndef NANO_TINY
     if (ISSET(SOFTWRAP)) {
-	size_t current_chunk = get_chunk_row(*line, *leftedge);
-	size_t last_chunk = get_last_chunk_row(*line);
+	size_t current_leftedge = *leftedge;
+	size_t last_leftedge = get_last_chunk_leftedge(*line);
 
 	/* Advance through the requested number of chunks. */
 	for (i = nrows; i > 0; i--) {
-	    if (current_chunk < last_chunk) {
-		current_chunk++;
+	    if (current_leftedge < last_leftedge) {
+		bool end_of_line;
+
+		current_leftedge = get_softwrap_breakpoint((*line)->data,
+							current_leftedge,
+							&end_of_line);
 		continue;
 	    }
 
@@ -2868,13 +2880,13 @@ int go_forward_chunks(int nrows, filestruct **line, size_t *leftedge)
 		break;
 
 	    *line = (*line)->next;
-	    current_chunk = 0;
-	    last_chunk = get_last_chunk_row(*line);
+	    current_leftedge = 0;
+	    last_leftedge = get_last_chunk_leftedge(*line);
 	}
 
 	/* Only change leftedge when we actually could move. */
 	if (i < nrows)
-	    *leftedge = current_chunk * editwincols;
+	    *leftedge = current_leftedge;
     } else
 #endif
 	for (i = nrows; i > 0 && (*line)->next != NULL; i--)
@@ -3043,14 +3055,18 @@ size_t get_chunk(filestruct *line, size_t column, size_t *leftedge)
  * relative to the first row (zero-based). */
 size_t get_chunk_row(filestruct *line, size_t column)
 {
-    return strnlenpt(line->data, column) / editwincols;
+    return get_chunk(line, column, NULL);
 }
 
 /* Return the leftmost column of the softwrapped chunk of the given line that
  * column is on. */
 size_t get_chunk_leftedge(filestruct *line, size_t column)
 {
-    return (strnlenpt(line->data, column) / editwincols) * editwincols;
+    size_t leftedge;
+
+    get_chunk(line, column, &leftedge);
+
+    return leftedge;
 }
 
 /* Return the row of the last softwrapped chunk of the given line, relative to
@@ -3072,8 +3088,8 @@ size_t get_last_chunk_leftedge(filestruct *line)
  * has changed, because then the width of softwrapped chunks has changed. */
 void ensure_firstcolumn_is_aligned(void)
 {
-    if (openfile->firstcolumn % editwincols != 0)
-	openfile->firstcolumn -= (openfile->firstcolumn % editwincols);
+    openfile->firstcolumn = get_chunk_leftedge(openfile->edittop,
+						openfile->firstcolumn);
 
     /* If smooth scrolling is on, make sure the viewport doesn't center. */
     focusing = FALSE;