diff --git a/ChangeLog b/ChangeLog
index 2efbbd4c4fe43996839fce939d293e4cc1866edf..2319c28cf98efc319869e2572c7d18695bf9f5ea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -21,14 +21,14 @@ CVS code -
 	  do_gotolinecolumn_void()), nano.1, and nano.texi. (DLR,
 	  suggested by PFTank)
 	- Overhaul the history code to work more consistently, and clean
-	  up various parts of it.  Note that history tab completion has
-	  been removed.  New function history_has_changed(); changes to
-	  load_history(), writehist(), thanks_for_all_the_fish(),
-	  history_init(), find_node() (renamed find_history()),
-	  update_history(), get_history_older(), get_history_newer(),
-	  do_search(), do_replace(), nanogetstr(), and statusq();
-	  removal of remove_node(), insert_node(), and
-	  get_history_completion(). (DLR)
+	  up various parts of it.  New function history_has_changed();
+	  changes to load_history(), writehist(),
+	  thanks_for_all_the_fish(), history_init(), find_node()
+	  (renamed find_history()), update_history(),
+	  get_history_older(), get_history_newer(),
+	  get_history_completion(), do_search(), do_replace(),
+	  nanogetstr(), and statusq(); removal of remove_node() and
+	  insert_node(). (DLR)
 	- Replace all instances of strncpy() with charcpy(), since the
 	  only difference between them is that the former pads strings
 	  with nulls when they're longer than the number of characters
diff --git a/src/files.c b/src/files.c
index 78294072e6afa5b18073bb32ac9cb40b33f65625..c3d7af199f027dc70eb44da33fa39658bd044e4d 100644
--- a/src/files.c
+++ b/src/files.c
@@ -2915,8 +2915,6 @@ void load_history(void)
 	     * replace history) from oldest to newest.  Assume the last
 	     * history entry is a blank line. */
 	    filestruct **history = &search_history;
-	    filestruct **historyage = &searchage;
-	    filestruct **historybot = &searchbot;
 	    char *line = NULL;
 	    size_t buflen = 0;
 	    ssize_t read;
@@ -2928,13 +2926,9 @@ void load_history(void)
 		}
 		if (read > 0) {
 		    unsunder(line, read);
-		    update_history(history, historyage, historybot,
-			line);
-		} else {
+		    update_history(history, line);
+		} else
 		    history = &replace_history;
-		    historyage = &replaceage;
-		    historybot = &replacebot;
-		}
 	    }
 
 	    fclose(hist);
diff --git a/src/proto.h b/src/proto.h
index c6049bf3bdc8c6df1e480aedec8d81b80b1d5948..7efa41e2d6d6beb6abf13e48abe309166251d2d0 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -322,6 +322,9 @@ char *histfilename(void);
 void load_history(void);
 bool writehist(FILE *hist, filestruct *histhead);
 void save_history(void);
+#ifndef DISABLE_TABCOMP
+char *get_history_completion(filestruct **h, char *s, size_t len);
+#endif
 #endif
 
 /* Public functions in global.c. */
@@ -527,9 +530,9 @@ void do_find_bracket(void);
 #if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
 bool history_has_changed(void);
 void history_init(void);
-filestruct *find_history(filestruct *h, const char *s);
-void update_history(filestruct **h, filestruct **hage, filestruct
-	**hbot, const char *s);
+filestruct *find_history(filestruct *h_start, filestruct *h_end, const
+	char *s, size_t len);
+void update_history(filestruct **h, const char *s);
 char *get_history_older(filestruct **h);
 char *get_history_newer(filestruct **h);
 #endif
@@ -649,7 +652,7 @@ char *display_string(const char *buf, size_t start_col, size_t len, bool
 void nanoget_repaint(const char *buf, const char *inputbuf, size_t x);
 int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
 #if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
-	filestruct *history_list,
+	filestruct **history_list,
 #endif
 	const shortcut *s
 #ifndef DISABLE_TABCOMP
@@ -658,7 +661,7 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
 	);
 int statusq(bool allow_tabs, const shortcut *s, const char *curranswer,
 #if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
-	filestruct *history_list,
+	filestruct **history_list,
 #endif
 	const char *msg, ...);
 void statusq_abort(void);
diff --git a/src/search.c b/src/search.c
index 2ae9c3859bba08921e601ff8de030152644ba703..66e8190f70379f563d3c2cab00e8a2c038ba8d39 100644
--- a/src/search.c
+++ b/src/search.c
@@ -165,7 +165,7 @@ int search_init(bool replacing, bool use_answer)
     i = statusq(FALSE, replacing ? replace_list : whereis_list,
 	backupstring,
 #ifndef NANO_SMALL
-	search_history,
+	&search_history,
 #endif
 	"%s%s%s%s%s%s", _("Search"),
 
@@ -476,7 +476,7 @@ void do_search(void)
     /* If answer is not "", add this search string to the search history
      * list. */
     if (answer[0] != '\0')
-	update_history(&search_history, &searchage, &searchbot, answer);
+	update_history(&search_history, answer);
 #endif
 
     findnextstr_wrap_reset();
@@ -905,14 +905,14 @@ void do_replace(void)
      * copy answer into last_search. */
     if (answer[0] != '\0') {
 #ifndef NANO_SMALL
-	update_history(&search_history, &searchage, &searchbot, answer);
+	update_history(&search_history, answer);
 #endif
 	last_search = mallocstrcpy(last_search, answer);
     }
 
     i = statusq(FALSE, replace_list_2, last_replace,
 #ifndef NANO_SMALL
-	replace_history,
+	&replace_history,
 #endif
 	_("Replace with"));
 
@@ -920,8 +920,7 @@ void do_replace(void)
     /* Add this replace string to the replace history list.  i == 0
      * means that the string is not "". */
     if (i == 0)
-	update_history(&replace_history, &replaceage, &replacebot,
-		answer);
+	update_history(&replace_history, answer);
 #endif
 
     if (i != 0 && i != -2) {
@@ -1140,32 +1139,42 @@ void history_init(void)
     replacebot = replace_history;
 }
 
-/* Return the first node containing the string s in the history list,
- * starting at h, or NULL if there isn't one. */
-filestruct *find_history(filestruct *h, const char *s)
+/* Return the first node containing the first len characters of the
+ * string s in the history list, starting at h_start and ending at
+ * h_end, or NULL if there isn't one. */
+filestruct *find_history(filestruct *h_start, filestruct *h_end, const
+	char *s, size_t len)
 {
-    assert(h != NULL);
+    filestruct *p;
 
-    for (; h->next != NULL; h = h->next) {
-	if (strcmp(s, h->data) == 0)
-	    return h;
+    for (p = h_start; p != h_end->next && p != NULL; p = p->next) {
+	if (strncmp(s, p->data, len) == 0)
+	    return p;
     }
 
     return NULL;
 }
 
-/* Update a history list.  h should be the current position in the list,
- * hage should be the top of the list, and hbot should be the bottom of
- * the list. */
-void update_history(filestruct **h, filestruct **hage, filestruct
-	**hbot, const char *s)
+/* Update a history list.  h should be the current position in the
+ * list. */
+void update_history(filestruct **h, const char *s)
 {
-    filestruct *p;
+    filestruct **hage = NULL, **hbot = NULL, *p;
+
+    assert(h != NULL && s != NULL);
+
+    if (*h == search_history) {
+	hage = &searchage;
+	hbot = &searchbot;
+    } else if (*h == replace_history) {
+	hage = &replaceage;
+	hbot = &replacebot;
+    }
 
-    assert(h != NULL && hage != NULL && hbot != NULL && s != NULL);
+    assert(hage != NULL && hbot != NULL);
 
     /* If this string is already in the history, delete it. */
-    p = find_history(*hage, s);
+    p = find_history(*hage, *hbot, s, (size_t)-1);
 
     if (p != NULL) {
 	filestruct *foo, *bar;
@@ -1239,4 +1248,55 @@ char *get_history_newer(filestruct **h)
 
     return (*h)->data;
 }
+
+#ifndef DISABLE_TABCOMP
+/* Move h to the next string that's a tab completion of the string s,
+ * looking at only the first len characters of s, and return that
+ * string.  If there isn't one, or if len is 0, don't move h, truncate s
+ * to len characters, and return s. */
+char *get_history_completion(filestruct **h, char *s, size_t len)
+{
+    assert(s != NULL);
+
+    if (len > 0) {
+	filestruct *hage = NULL, *hbot = NULL, *p;
+
+	assert(h != NULL);
+
+	if (*h == search_history) {
+	    hage = searchage;
+	    hbot = searchbot;
+	} else if (*h == replace_history) {
+	    hage = replaceage;
+	    hbot = replacebot;
+	}
+
+	assert(hage != NULL && hbot != NULL);
+
+	/* Search the history list from the entry after the current
+	 * position to the bottom for a match of len characters. */
+	p = find_history((*h)->next, hbot, s, len);
+
+	if (p != NULL) {
+	    *h = p;
+	    return (*h)->data;
+	}
+
+	/* Search the history list from the top to the current position
+	 * for a match of len characters. */
+	p = find_history(hage, *h, s, len);
+
+	if (p != NULL) {
+	    *h = p;
+	    return (*h)->data;
+	}
+    }
+
+    /* If we're here, we didn't find a match, or len is 0.  Truncate s
+     * to len characters, and return it. */
+    null_at(&s, len);
+
+    return s;
+}
+#endif
 #endif /* !NANO_SMALL && ENABLE_NANORC */
diff --git a/src/winio.c b/src/winio.c
index 964f44c42447d6c6b9ac965e0b9fd04d3229d5dc..72854df1bc1bf286f6f36a3f5fd65ef0043c7a71 100644
--- a/src/winio.c
+++ b/src/winio.c
@@ -2431,7 +2431,7 @@ void nanoget_repaint(const char *buf, const char *inputbuf, size_t x)
  * statusq(). */
 int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
 #if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
-	filestruct *history_list,
+	filestruct **history_list,
 #endif
 	const shortcut *s
 #ifndef DISABLE_TABCOMP
@@ -2447,6 +2447,13 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
 	/* Whether we've pressed Tab. */
 #endif
 #if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
+#ifndef DISABLE_TABCOMP
+    size_t complete_len = 0;
+	/* The length of the original string that we're trying to
+	 * tab complete, if any. */
+#endif
+    int last_kbinput = ERR;
+	/* The key we pressed before the current key. */
     char *history = NULL;
 	/* The current history string. */
     char *magichistory = NULL;
@@ -2494,6 +2501,19 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
 	switch (kbinput) {
 	    case NANO_TAB_KEY:
 #ifndef DISABLE_TABCOMP
+#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
+		if (history_list != NULL) {
+		    if (last_kbinput != NANO_TAB_KEY)
+			complete_len = strlen(answer);
+
+		    if (complete_len > 0) {
+			answer = mallocstrcpy(answer,
+				get_history_completion(history_list,
+				answer, complete_len));
+			statusbar_x = strlen(answer);
+		    }
+		} else
+#endif
 		if (allow_tabs)
 		    answer = input_tab(answer, &statusbar_x, &tabbed,
 			list);
@@ -2506,7 +2526,7 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
 		     * history list, answer isn't blank, and
 		     * magichistory isn't set, save answer in
 		     * magichistory. */
-		    if (history_list->next == NULL &&
+		    if ((*history_list)->next == NULL &&
 			answer[0] != '\0' && magichistory == NULL)
 			magichistory = mallocstrcpy(NULL, answer);
 
@@ -2514,7 +2534,7 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
 		     * save it in answer.  If there is no older search,
 		     * don't do anything. */
 		    if ((history =
-			get_history_older(&history_list)) != NULL) {
+			get_history_older(history_list)) != NULL) {
 			answer = mallocstrcpy(answer, history);
 			statusbar_x = strlen(answer);
 		    }
@@ -2535,7 +2555,7 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
 		     * save it in answer.  If there is no newer search,
 		     * don't do anything. */
 		    if ((history =
-			get_history_newer(&history_list)) != NULL) {
+			get_history_newer(history_list)) != NULL) {
 			answer = mallocstrcpy(answer, history);
 			statusbar_x = strlen(answer);
 		    }
@@ -2544,7 +2564,7 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
 		     * the history list, answer is blank, and
 		     * magichistory is set, save magichistory in
 		     * answer. */
-		    if (history_list->next == NULL &&
+		    if ((*history_list)->next == NULL &&
 			answer[0] == '\0' && magichistory != NULL) {
 			answer = mallocstrcpy(answer, magichistory);
 			statusbar_x = strlen(answer);
@@ -2560,6 +2580,10 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
 	if (finished)
 	    break;
 
+#if !defined(NANO_SMALL) && defined(ENABLE_NANORC) && !defined(DISABLE_TABCOMP)
+	last_kbinput = kbinput;
+#endif
+
 	nanoget_repaint(buf, answer, statusbar_x);
 	wrefresh(bottomwin);
     }
@@ -2588,7 +2612,7 @@ int nanogetstr(bool allow_tabs, const char *buf, const char *curranswer,
  * interpreted. */
 int statusq(bool allow_tabs, const shortcut *s, const char *curranswer,
 #if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
-	filestruct *history_list,
+	filestruct **history_list,
 #endif
 	const char *msg, ...)
 {