diff --git a/src/move.c b/src/move.c
index b36b4bca..f0cb3c32 100644
--- a/src/move.c
+++ b/src/move.c
@@ -64,51 +64,6 @@ void get_edge_and_target(size_t *leftedge, size_t *target_column)
     }
 }
 
-/* Return the index in line->data that corresponds to the given column on the
- * chunk that starts at the given leftedge.  If the index lands on a tab, and
- * this tab starts on an earlier chunk, and the tab ends on this row OR we're
- * going forward, then increment the index and recalculate leftedge. */
-size_t proper_x(filestruct *line, size_t *leftedge, bool forward,
-		size_t column, bool *shifted)
-{
-    size_t index = actual_x(line->data, column);
-
-#ifndef NANO_TINY
-    if (ISSET(SOFTWRAP) && line->data[index] == '\t' && (forward ||
-		*leftedge / tabsize < (*leftedge + editwincols) / tabsize) &&
-		*leftedge % tabsize != 0 && column < *leftedge + tabsize) {
-	index++;
-
-	*leftedge = leftedge_for(strnlenpt(line->data, index), line);
-
-	if (shifted != NULL)
-	    *shifted = TRUE;
-    }
-#endif
-
-    return index;
-}
-
-/* Adjust the values for current_x and placewewant in case we have landed in
- * the middle of a tab that crosses a row boundary. */
-void set_proper_index_and_pww(size_t *leftedge, size_t target, bool forward)
-{
-    bool shifted = FALSE;
-
-    openfile->current_x = proper_x(openfile->current, leftedge, forward,
-			actual_last_column(*leftedge, target), &shifted);
-
-    /* If the index was incremented, try going to the target column. */
-    if (shifted) {
-	size_t newer_x = actual_x(openfile->current->data, *leftedge + target);
-
-	if (newer_x > openfile->current_x)
-	    openfile->current_x = newer_x;
-    }
-
-    openfile->placewewant = *leftedge + target;
-}
-
 /* Move up nearly one screenful. */
 void do_page_up(void)
 {
@@ -132,7 +87,9 @@ void do_page_up(void)
 	return;
     }
 
-    set_proper_index_and_pww(&leftedge, target_column, FALSE);
+    openfile->current_x = actual_x(openfile->current->data,
+				actual_last_column(leftedge, target_column));
+    openfile->placewewant = leftedge + target_column;
 
     /* Move the viewport so that the cursor stays immobile, if possible. */
     adjust_viewport(STATIONARY);
@@ -162,7 +119,9 @@ void do_page_down(void)
 	return;
     }
 
-    set_proper_index_and_pww(&leftedge, target_column, TRUE);
+    openfile->current_x = actual_x(openfile->current->data,
+				actual_last_column(leftedge, target_column));
+    openfile->placewewant = leftedge + target_column;
 
     /* Move the viewport so that the cursor stays immobile, if possible. */
     adjust_viewport(STATIONARY);
@@ -384,8 +343,13 @@ void do_home(void)
 
     if (ISSET(SOFTWRAP)) {
 	leftedge = leftedge_for(was_column, openfile->current);
-	leftedge_x = proper_x(openfile->current, &leftedge, FALSE, leftedge,
-				NULL);
+	leftedge_x = actual_x(openfile->current->data, leftedge);
+
+	/* If the line begins on a straddling tab on the previous row, shift
+	 * forward to the next row. */
+	if (openfile->current->data[leftedge_x] == '\t' &&
+		strnlenpt(openfile->current->data, leftedge_x) < leftedge)
+	    leftedge_x++;
     }
 
     if (ISSET(SMART_HOME)) {
@@ -442,10 +406,12 @@ void do_end(void)
 
 #ifndef NANO_TINY
     if (ISSET(SOFTWRAP)) {
+	int line_rows;
 	bool last_chunk = FALSE;
 	size_t leftedge = leftedge_for(was_column, openfile->current);
 	size_t rightedge = get_softwrap_breakpoint(openfile->current->data,
-						leftedge, &last_chunk);
+						leftedge, &line_rows,
+						&last_chunk);
 	size_t rightedge_x;
 
 	/* If we're on the last chunk, we're already at the end of the line.
@@ -499,7 +465,9 @@ void do_up(bool scroll_only)
     if (go_back_chunks(1, &openfile->current, &leftedge) > 0)
 	return;
 
-    set_proper_index_and_pww(&leftedge, target_column, FALSE);
+    openfile->current_x = actual_x(openfile->current->data,
+				actual_last_column(leftedge, target_column));
+    openfile->placewewant = leftedge + target_column;
 
     if (scroll_only)
 	edit_scroll(BACKWARD, 1);
@@ -523,7 +491,9 @@ void do_down(bool scroll_only)
     if (go_forward_chunks(1, &openfile->current, &leftedge) > 0)
 	return;
 
-    set_proper_index_and_pww(&leftedge, target_column, TRUE);
+    openfile->current_x = actual_x(openfile->current->data,
+				actual_last_column(leftedge, target_column));
+    openfile->placewewant = leftedge + target_column;
 
     if (scroll_only)
 	edit_scroll(FORWARD, 1);
diff --git a/src/proto.h b/src/proto.h
index 71c12d40..993a1a35 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -669,7 +669,7 @@ bool less_than_a_screenful(size_t was_lineno, size_t was_leftedge);
 void edit_scroll(bool direction, int nrows);
 #ifndef NANO_TINY
 size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
-				bool *end_of_line);
+				int *line_rows, bool *end_of_line);
 size_t get_chunk_and_edge(size_t column, filestruct *line, size_t *leftedge);
 size_t chunk_for(size_t column, filestruct *line);
 size_t leftedge_for(size_t column, filestruct *line);
diff --git a/src/winio.c b/src/winio.c
index f90d23dc..3bc04905 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -2779,15 +2779,18 @@ int update_softwrapped_line(filestruct *fileptr)
     starting_row = row;
 
     while (row < editwinrows) {
+	int line_rows = 0;
 	bool end_of_line = FALSE;
 
-	to_col = get_softwrap_breakpoint(fileptr->data, from_col, &end_of_line);
+	to_col = get_softwrap_breakpoint(fileptr->data, from_col, &line_rows,
+						&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, to_col - from_col, TRUE);
-	edit_draw(fileptr, converted, row++, from_col);
+	edit_draw(fileptr, converted, row, from_col);
+	row += line_rows;
 	free(converted);
 
 	if (end_of_line)
@@ -2868,12 +2871,18 @@ int go_forward_chunks(int nrows, filestruct **line, size_t *leftedge)
     if (ISSET(SOFTWRAP)) {
 	size_t current_leftedge = *leftedge;
 
+	i = nrows;
+
 	/* Advance through the requested number of chunks. */
-	for (i = nrows; i > 0; i--) {
+	while (i > 0) {
+	    int line_rows = 0;
 	    bool end_of_line = FALSE;
 
 	    current_leftedge = get_softwrap_breakpoint((*line)->data,
-					current_leftedge, &end_of_line);
+					current_leftedge, &line_rows,
+					&end_of_line);
+
+	    i -= line_rows;
 
 	    if (!end_of_line)
 		continue;
@@ -2990,11 +2999,11 @@ void edit_scroll(bool direction, int nrows)
 
 #ifndef NANO_TINY
 /* Get the column number after leftedge where we can break the given text, and
- * return it.  This will always be editwincols or less after leftedge.  Set
- * end_of_line to TRUE if we reach the end of the line while searching the
+ * return it.  Set rows to the number of full rows the chunk of text takes up.
+ * Set end_of_line to TRUE if we reach the end of the line while searching the
  * text.  Assume leftedge is the leftmost column of a softwrapped chunk. */
 size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
-				bool *end_of_line)
+				int *line_rows, bool *end_of_line)
 {
     size_t column = 0;
 	/* Current column position in text. */
@@ -3009,6 +3018,9 @@ size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
     int char_len = 0;
 	/* Length of the current character, in bytes. */
 
+    /* The text always takes up one full row, except when dealing with tabs. */
+    *line_rows = 1;
+
     while (*text != '\0' && column < leftedge) {
 	char_len = parse_mbchar(text, NULL, &column);
 	text += char_len;
@@ -3047,9 +3059,18 @@ size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
     }
 
     /* If a tab is split over two chunks, break at the screen's edge. */
-    if (*(text - char_len) == '\t')
+    if (*(text - char_len) == '\t') {
+	size_t after_tab_col = goal_column + tabsize;
+
 	previous_col = goal_column;
 
+	/* If a tab takes up multiple rows' worth of chunks, account for it. */
+	while (after_tab_col > previous_col + editwincols) {
+	    previous_col += editwincols;
+	    (*line_rows)++;
+	}
+    }
+
     /* Otherwise, return the column of the last character that doesn't
      * overshoot the limit, since we can't break the text anywhere else. */
     return (editwincols > 1) ? previous_col : column - 1;
@@ -3061,10 +3082,12 @@ size_t get_softwrap_breakpoint(const char *text, size_t leftedge,
 size_t get_chunk_and_edge(size_t column, filestruct *line, size_t *leftedge)
 {
     size_t current_chunk = 0, start_col = 0, end_col;
+    int chunk_rows = 0;
     bool end_of_line = FALSE;
 
     while (TRUE) {
-	end_col = get_softwrap_breakpoint(line->data, start_col, &end_of_line);
+	end_col = get_softwrap_breakpoint(line->data, start_col, &chunk_rows,
+						&end_of_line);
 
 	/* We reached the end of the line and/or found column, so get out. */
 	if (end_of_line || (column >= start_col && column < end_col)) {
@@ -3073,7 +3096,7 @@ size_t get_chunk_and_edge(size_t column, filestruct *line, size_t *leftedge)
 	    return current_chunk;
 	}
 
-	current_chunk++;
+	current_chunk += chunk_rows;
 	start_col = end_col;
     }
 }
@@ -3130,9 +3153,11 @@ size_t actual_last_column(size_t leftedge, size_t column)
 {
 #ifndef NANO_TINY
     if (ISSET(SOFTWRAP)) {
+	int line_rows;
 	bool last_chunk;
 	size_t end_col = get_softwrap_breakpoint(openfile->current->data,
-					leftedge, &last_chunk) - leftedge;
+					leftedge, &line_rows,
+					&last_chunk) - leftedge;
 
 	/* If we're not on the last chunk, we're one column past the end of
 	 * the row.  Shifting back one column might put us in the middle of
@@ -3461,12 +3486,14 @@ void spotlight_softwrapped(bool active, size_t from_col, size_t to_col)
     ssize_t row = openfile->current_y;
     size_t leftedge = leftedge_for(from_col, openfile->current);
     size_t break_col;
+    int line_rows;
     bool end_of_line = FALSE;
     char *word;
 
     while (row < editwinrows) {
 	break_col = get_softwrap_breakpoint(openfile->current->data,
-						leftedge, &end_of_line);
+						leftedge, &line_rows,
+						&end_of_line);
 
 	/* Stop after the end of the word, by pretending the end of the word is
 	 * the end of the line. */
@@ -3496,7 +3523,8 @@ void spotlight_softwrapped(bool active, size_t from_col, size_t to_col)
 	if (end_of_line)
 	    break;
 
-	wmove(edit, ++row, 0);
+	row += line_rows;
+	wmove(edit, row, 0);
 
 	leftedge = break_col;
 	from_col = break_col;
