diff --git a/mode-key.c b/mode-key.c
index 77e7b695..72d66f37 100644
--- a/mode-key.c
+++ b/mode-key.c
@@ -289,6 +289,7 @@ const struct mode_key_entry mode_key_vi_copy[] = {
 	{ 'M',			    0, MODEKEYCOPY_MIDDLELINE },
 	{ 'N',			    0, MODEKEYCOPY_SEARCHREVERSE },
 	{ 'T',			    0, MODEKEYCOPY_JUMPTOBACK },
+	{ 'V',			    0, MODEKEYCOPY_SELECTLINE },
 	{ 'W',			    0, MODEKEYCOPY_NEXTSPACE },
 	{ '\002' /* C-b */,	    0, MODEKEYCOPY_PREVIOUSPAGE },
 	{ '\003' /* C-c */,	    0, MODEKEYCOPY_CANCEL },
diff --git a/screen.c b/screen.c
index 5ef18a27..3e3cac53 100644
--- a/screen.c
+++ b/screen.c
@@ -269,6 +269,7 @@ screen_clear_selection(struct screen *s)
 	struct screen_sel	*sel = &s->sel;
 
 	sel->flag = 0;
+	sel->lineflag = LINE_SEL_NONE;
 }
 
 /* Check if cell in selection. */
diff --git a/tmux.1 b/tmux.1
index 367536a1..978cf548 100644
--- a/tmux.1
+++ b/tmux.1
@@ -912,6 +912,7 @@ The following keys are supported as appropriate for the mode:
 .It Li "Rectangle toggle" Ta "v" Ta "R"
 .It Li "Scroll down" Ta "C-Down or C-e" Ta "C-Down"
 .It Li "Scroll up" Ta "C-Up or C-y" Ta "C-Up"
+.It Li "Select line" Ta "" Ta "V"
 .It Li "Search again" Ta "n" Ta "n"
 .It Li "Search again in reverse" Ta "N" Ta "N"
 .It Li "Search backward" Ta "?" Ta "C-r"
diff --git a/tmux.h b/tmux.h
index d310c7af..7e520641 100644
--- a/tmux.h
+++ b/tmux.h
@@ -712,6 +712,12 @@ LIST_HEAD(joblist, job);
 struct screen_sel {
 	int		 flag;
 	int		 rectflag;
+	enum {
+		LINE_SEL_NONE,
+		LINE_SEL_LEFT_RIGHT,
+		LINE_SEL_RIGHT_LEFT,
+	} lineflag;
+
 	int		 modekeys;
 
 	u_int		 sx;
diff --git a/window-copy.c b/window-copy.c
index b1301676..51710eb2 100644
--- a/window-copy.c
+++ b/window-copy.c
@@ -30,23 +30,23 @@ void	window_copy_resize(struct window_pane *, u_int, u_int);
 void	window_copy_key(struct window_pane *, struct session *, int);
 int	window_copy_key_input(struct window_pane *, int);
 int	window_copy_key_numeric_prefix(struct window_pane *, int);
-void	window_copy_mouse(
-	    struct window_pane *, struct session *, struct mouse_event *);
+void	window_copy_mouse(struct window_pane *, struct session *,
+	    struct mouse_event *);
 
 void	window_copy_redraw_lines(struct window_pane *, u_int, u_int);
 void	window_copy_redraw_screen(struct window_pane *);
-void	window_copy_write_line(
-	    struct window_pane *, struct screen_write_ctx *, u_int);
-void	window_copy_write_lines(
-	    struct window_pane *, struct screen_write_ctx *, u_int, u_int);
+void	window_copy_write_line(struct window_pane *, struct screen_write_ctx *,
+	    u_int);
+void	window_copy_write_lines(struct window_pane *,
+	    struct screen_write_ctx *, u_int, u_int);
 
 void	window_copy_scroll_to(struct window_pane *, u_int, u_int);
-int	window_copy_search_compare(
-	    struct grid *, u_int, u_int, struct grid *, u_int, int);
-int	window_copy_search_lr(
-	    struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int);
-int	window_copy_search_rl(
-	    struct grid *, struct grid *, u_int *, u_int, u_int, u_int, int);
+int	window_copy_search_compare(struct grid *, u_int, u_int, struct grid *,
+	    u_int, int);
+int	window_copy_search_lr(struct grid *, struct grid *, u_int *, u_int,
+	    u_int, u_int, int);
+int	window_copy_search_rl(struct grid *, struct grid *, u_int *, u_int,
+	    u_int, u_int, int);
 void	window_copy_search_up(struct window_pane *, const char *);
 void	window_copy_search_down(struct window_pane *, const char *);
 void	window_copy_goto_line(struct window_pane *, const char *);
@@ -374,7 +374,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key)
 	u_int				 n;
 	int				 np, keys;
 	enum mode_key_cmd		 cmd;
-	const char			*arg;
+	const char			*arg, *ss;
 
 	np = data->numprefix;
 	if (np <= 0)
@@ -528,11 +528,15 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key)
 		window_copy_redraw_screen(wp);
 		break;
 	case MODEKEYCOPY_STARTSELECTION:
+		s->sel.lineflag = LINE_SEL_NONE;
 		window_copy_start_selection(wp);
 		window_copy_redraw_screen(wp);
 		break;
-	case MODEKEYCOPY_COPYLINE:
 	case MODEKEYCOPY_SELECTLINE:
+		s->sel.lineflag = LINE_SEL_LEFT_RIGHT;
+		data->rectflag = 0;
+		/* FALLTHROUGH */
+	case MODEKEYCOPY_COPYLINE:
 		window_copy_cursor_start_of_line(wp);
 		/* FALLTHROUGH */
 	case MODEKEYCOPY_COPYENDOFLINE:
@@ -683,29 +687,23 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key)
 		case WINDOW_COPY_NUMERICPREFIX:
 			break;
 		case WINDOW_COPY_SEARCHUP:
+			ss = data->searchstr;
 			if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
-				for (; np != 0; np--) {
-					window_copy_search_up(
-					    wp, data->searchstr);
-				}
+				for (; np != 0; np--)
+					window_copy_search_up(wp, ss);
 			} else {
-				for (; np != 0; np--) {
-					window_copy_search_down(
-					    wp, data->searchstr);
-				}
+				for (; np != 0; np--)
+					window_copy_search_down(wp, ss);
 			}
 			break;
 		case WINDOW_COPY_SEARCHDOWN:
+			ss = data->searchstr;
 			if (cmd == MODEKEYCOPY_SEARCHAGAIN) {
-				for (; np != 0; np--) {
-					window_copy_search_down(
-					    wp, data->searchstr);
-				}
+				for (; np != 0; np--)
+					window_copy_search_down(wp, ss);
 			} else {
-				for (; np != 0; np--) {
-					window_copy_search_up(
-					    wp, data->searchstr);
-				}
+				for (; np != 0; np--)
+					window_copy_search_up(wp, ss);
 			}
 			break;
 		}
@@ -730,6 +728,7 @@ window_copy_key(struct window_pane *wp, struct session *sess, int key)
 		}
 		break;
 	case MODEKEYCOPY_RECTANGLETOGGLE:
+		s->sel.lineflag = LINE_SEL_NONE;
 		window_copy_rectangle_toggle(wp);
 		break;
 	default:
@@ -871,8 +870,8 @@ window_copy_key_numeric_prefix(struct window_pane *wp, int key)
 }
 
 void
-window_copy_mouse(
-    struct window_pane *wp, struct session *sess, struct mouse_event *m)
+window_copy_mouse(struct window_pane *wp, struct session *sess,
+    struct mouse_event *m)
 {
 	struct window_copy_mode_data	*data = wp->modedata;
 	struct screen			*s = &data->screen;
@@ -895,7 +894,8 @@ window_copy_mouse(
 				 * We reached the bottom, leave copy mode, but
 				 * only if no selection is in progress.
 				 */
-				if (data->oy == 0 && !s->sel.flag)
+				if (data->oy == 0 && !s->sel.flag &&
+				    s->sel.lineflag == LINE_SEL_NONE)
 					goto reset_mode;
 			}
 		}
@@ -964,8 +964,8 @@ window_copy_scroll_to(struct window_pane *wp, u_int px, u_int py)
 }
 
 int
-window_copy_search_compare(
-    struct grid *gd, u_int px, u_int py, struct grid *sgd, u_int spx, int cis)
+window_copy_search_compare(struct grid *gd, u_int px, u_int py,
+    struct grid *sgd, u_int spx, int cis)
 {
 	const struct grid_cell	*gc, *sgc;
 	struct utf8_data	 ud, sud;
@@ -1186,8 +1186,8 @@ window_copy_goto_line(struct window_pane *wp, const char *linestr)
 }
 
 void
-window_copy_write_line(
-    struct window_pane *wp, struct screen_write_ctx *ctx, u_int py)
+window_copy_write_line(struct window_pane *wp, struct screen_write_ctx *ctx,
+    u_int py)
 {
 	struct window_copy_mode_data	*data = wp->modedata;
 	struct screen			*s = &data->screen;
@@ -1237,8 +1237,8 @@ window_copy_write_line(
 }
 
 void
-window_copy_write_lines(
-    struct window_pane *wp, struct screen_write_ctx *ctx, u_int py, u_int ny)
+window_copy_write_lines(struct window_pane *wp, struct screen_write_ctx *ctx,
+    u_int py, u_int ny)
 {
 	u_int	yy;
 
@@ -1311,7 +1311,7 @@ window_copy_update_selection(struct window_pane *wp, int may_redraw)
 	struct grid_cell		 gc;
 	u_int				 sx, sy, ty, cy;
 
-	if (!s->sel.flag)
+	if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
 		return (0);
 
 	/* Set colours. */
@@ -1365,7 +1365,7 @@ window_copy_get_selection(struct window_pane *wp, size_t *len)
 	u_int				 firstsx, lastex, restex, restsx;
 	int				 keys;
 
-	if (!s->sel.flag)
+	if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
 		return (NULL);
 
 	buf = xmalloc(1);
@@ -1665,12 +1665,14 @@ window_copy_cursor_start_of_line(struct window_pane *wp)
 {
 	struct window_copy_mode_data	*data = wp->modedata;
 	struct screen			*back_s = data->backing;
+	struct screen			*s = &data->screen;
 	struct grid			*gd = back_s->grid;
 	u_int				 py;
 
-	if (data->cx == 0) {
+	if (data->cx == 0 && s->sel.lineflag == LINE_SEL_NONE) {
 		py = screen_hsize(back_s) + data->cy - data->oy;
-		while (py > 0 && gd->linedata[py-1].flags & GRID_LINE_WRAPPED) {
+		while (py > 0 &&
+		    gd->linedata[py-1].flags & GRID_LINE_WRAPPED) {
 			window_copy_cursor_up(wp, 0);
 			py = screen_hsize(back_s) + data->cy - data->oy;
 		}
@@ -1710,13 +1712,14 @@ window_copy_cursor_end_of_line(struct window_pane *wp)
 {
 	struct window_copy_mode_data	*data = wp->modedata;
 	struct screen			*back_s = data->backing;
+	struct screen			*s = &data->screen;
 	struct grid			*gd = back_s->grid;
 	u_int				 px, py;
 
 	py = screen_hsize(back_s) + data->cy - data->oy;
 	px = window_copy_find_length(wp, py);
 
-	if (data->cx == px) {
+	if (data->cx == px && s->sel.lineflag == LINE_SEL_NONE) {
 		if (data->screen.sel.flag && data->rectflag)
 			px = screen_size_x(back_s);
 		if (gd->linedata[py].flags & GRID_LINE_WRAPPED) {
@@ -1742,9 +1745,14 @@ window_copy_other_end(struct window_pane *wp)
 	struct screen			*s = &data->screen;
 	u_int				 selx, sely, cx, cy, yy;
 
-	if (!s->sel.flag)
+	if (!s->sel.flag && s->sel.lineflag == LINE_SEL_NONE)
 		return;
 
+	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
+		s->sel.lineflag = LINE_SEL_RIGHT_LEFT;
+	else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
+		s->sel.lineflag = LINE_SEL_LEFT_RIGHT;
+
 	selx = data->selx;
 	sely = data->sely;
 	cx = data->cx;
@@ -1820,6 +1828,9 @@ window_copy_cursor_up(struct window_pane *wp, int scroll_only)
 		data->lastsx = ox;
 	}
 
+	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT && oy == data->sely)
+		window_copy_other_end(wp);
+
 	data->cx = data->lastcx;
 	if (scroll_only || data->cy == 0) {
 		window_copy_scroll_down(wp, 1);
@@ -1846,6 +1857,11 @@ window_copy_cursor_up(struct window_pane *wp, int scroll_only)
 		    data->cx > px)
 			window_copy_cursor_end_of_line(wp);
 	}
+
+	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
+		window_copy_cursor_end_of_line(wp);
+	else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
+		window_copy_cursor_start_of_line(wp);
 }
 
 void
@@ -1862,6 +1878,9 @@ window_copy_cursor_down(struct window_pane *wp, int scroll_only)
 		data->lastsx = ox;
 	}
 
+	if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT && oy == data->sely)
+		window_copy_other_end(wp);
+
 	data->cx = data->lastcx;
 	if (scroll_only || data->cy == screen_size_y(s) - 1) {
 		window_copy_scroll_up(wp, 1);
@@ -1880,6 +1899,11 @@ window_copy_cursor_down(struct window_pane *wp, int scroll_only)
 		    data->cx > px)
 			window_copy_cursor_end_of_line(wp);
 	}
+
+	if (s->sel.lineflag == LINE_SEL_LEFT_RIGHT)
+		window_copy_cursor_end_of_line(wp);
+	else if (s->sel.lineflag == LINE_SEL_RIGHT_LEFT)
+		window_copy_cursor_start_of_line(wp);
 }
 
 void