diff --git a/src/console.c b/src/console.c
index 0b55e7e2c53c2982bd55e3a8b75ccfb301a0ca49..ba5ba71df93258b78af298b8be6bc87c5a8521dd 100644
--- a/src/console.c
+++ b/src/console.c
@@ -32,6 +32,7 @@
 #include "d_main.h"
 #include "m_menu.h"
 #include "filesrch.h"
+#include "m_misc.h"
 
 #ifdef _WINDOWS
 #include "win32/win_main.h"
@@ -806,6 +807,33 @@ boolean CON_Responder(event_t *ev)
 	 || key == KEY_LALT || key == KEY_RALT)
 		return true;
 
+	if (key == KEY_LEFTARROW)
+	{
+		if (input_cur != 0)
+		{
+			if (ctrldown)
+				input_cur = M_JumpWordReverse(inputlines[inputline], input_cur);
+			else
+				--input_cur;
+		}
+		if (!shiftdown)
+			input_sel = input_cur;
+		return true;
+	}
+	else if (key == KEY_RIGHTARROW)
+	{
+		if (input_cur < input_len)
+		{
+			if (ctrldown)
+				input_cur += M_JumpWord(&inputlines[inputline][input_cur]);
+			else
+				++input_cur;
+		}
+		if (!shiftdown)
+			input_sel = input_cur;
+		return true;
+	}
+
 	// ctrl modifier -- changes behavior, adds shortcuts
 	if (ctrldown)
 	{
@@ -958,23 +986,6 @@ boolean CON_Responder(event_t *ev)
 			con_scrollup--;
 		return true;
 	}
-
-	if (key == KEY_LEFTARROW)
-	{
-		if (input_cur != 0)
-			--input_cur;
-		if (!shiftdown)
-			input_sel = input_cur;
-		return true;
-	}
-	else if (key == KEY_RIGHTARROW)
-	{
-		if (input_cur < input_len)
-			++input_cur;
-		if (!shiftdown)
-			input_sel = input_cur;
-		return true;
-	}
 	else if (key == KEY_HOME)
 	{
 		input_cur = 0;
diff --git a/src/doomdef.h b/src/doomdef.h
index 69607159798ba29a70090f9f487ebed64fecd35f..98a28e3dff6d81797737d49406280bf6de64f246 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -541,6 +541,8 @@ INT32 I_GetKey(void);
 	#define PATHSEP "/"
 #endif
 
+#define PUNCTUATION "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
+
 // Compile date and time and revision.
 extern const char *compdate, *comptime, *comprevision, *compbranch;
 
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 14cb481b0dbadc06bd23f735a057e637bdc1cf1d..9bd8be7783f560d5d46c641e44bf71d3f491c33d 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -17,6 +17,7 @@
 
 #include "m_menu.h" // gametype_cons_t
 #include "m_cond.h" // emblems
+#include "m_misc.h" // word jumping
 
 #include "d_clisrv.h"
 
@@ -1337,9 +1338,19 @@ boolean HU_Responder(event_t *ev)
 			chat_scrolltime = 4;
 		}
 		else if (c == KEY_LEFTARROW && c_input != 0 && !OLDCHAT) // i said go back
-			c_input--;
+		{
+			if (ctrldown)
+				c_input = M_JumpWordReverse(w_chat, c_input);
+			else
+				c_input--;
+		}
 		else if (c == KEY_RIGHTARROW && c_input < strlen(w_chat) && !OLDCHAT) // don't need to check for admin or w/e here since the chat won't ever contain anything if it's muted.
-			c_input++;
+		{
+			if (ctrldown)
+				c_input += M_JumpWord(&w_chat[c_input]);
+			else
+				c_input++;
+		}
 		return true;
 	}
 #endif
diff --git a/src/m_misc.c b/src/m_misc.c
index 5ee7b0ac9abcaf7fc72af638501db24d957a2187..43d5311106222b4319f4d520198e9d4c95c69439 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -2544,3 +2544,40 @@ void M_MkdirEach(const char *path, int start, int mode)
 {
 	M_MkdirEachUntil(path, start, -1, mode);
 }
+
+int M_JumpWord(const char *line)
+{
+	int c;
+
+	c = line[0];
+
+	if (isspace(c))
+		return strspn(line, " ");
+	else if (ispunct(c))
+		return strspn(line, PUNCTUATION);
+	else
+	{
+		if (isspace(line[1]))
+			return 1 + strspn(&line[1], " ");
+		else
+			return strcspn(line, " "PUNCTUATION);
+	}
+}
+
+int M_JumpWordReverse(const char *line, int offset)
+{
+	int (*is)(int);
+	int c;
+	c = line[--offset];
+	if (isspace(c))
+		is = isspace;
+	else if (ispunct(c))
+		is = ispunct;
+	else
+		is = isalnum;
+	c = (*is)(line[offset]);
+	while (offset > 0 &&
+			(*is)(line[offset - 1]) == c)
+		offset--;
+	return offset;
+}
diff --git a/src/m_misc.h b/src/m_misc.h
index e0a73e0b7f9d30789931b671c2fe487bdf1910e1..3fb9f031a460f007e4d539837905072547b367fd 100644
--- a/src/m_misc.h
+++ b/src/m_misc.h
@@ -101,6 +101,14 @@ boolean M_IsPathAbsolute (const char *path);
 void    M_MkdirEach      (const char *path, int start, int mode);
 void    M_MkdirEachUntil (const char *path, int start, int end, int mode);
 
+/* Return offset to the first word in a string. */
+/* E.g. cursor += M_JumpWord(line + cursor); */
+int M_JumpWord (const char *s);
+
+/* Return index of the last word behind offset bytes in a string. */
+/* E.g. cursor = M_JumpWordReverse(line, cursor); */
+int M_JumpWordReverse (const char *line, int offset);
+
 // counting bits, for weapon ammo code, usually
 FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size);