patch 9.1.1283: quickfix stack is limited to 10 items

Problem:  quickfix and location-list stack is limited to 10 items
Solution: add the 'chistory' and 'lhistory' options to configure a
          larger quickfix/location list stack
          (64-bitman)

closes: #16920

Co-authored-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: 64-bitman <60551350+64-bitman@users.noreply.github.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/errors.h b/src/errors.h
index 6e2782d..90cc2b1 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3718,3 +3718,13 @@
 #endif
 EXTERN char e_unicode_val_too_large[]
 	INIT(= N_("E1541: Value too large, max Unicode codepoint is U+10FFFF"));
+#ifdef FEAT_QUICKFIX
+EXTERN char e_cannot_have_negative_or_zero_number_of_quickfix[]
+	INIT(= N_("E1542: Cannot have a negative or zero number of quickfix/location lists"));
+EXTERN char e_cannot_have_more_than_hundred_quickfix[]
+	INIT(= N_("E1543: Cannot have more than a hundred quickfix/location lists"));
+EXTERN char e_failed_resizing_quickfix_stack[]
+	INIT(= N_("E1544: Failed resizing the quickfix/location list stack"));
+EXTERN char e_no_quickfix_stack[]
+	INIT(= N_("E1545: Quickfix list stack unavailable"));
+#endif
diff --git a/src/main.c b/src/main.c
index 7766946..a3caac8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1024,6 +1024,13 @@
 #ifdef FEAT_SIGNS
     init_signs();
 #endif
+
+#ifdef FEAT_QUICKFIX
+    // initialize global quickfix list
+    // don't send an error message when memory allocation fails
+    // do it when the user tries to access the quickfix list
+    qf_init_quickfix_stack();
+#endif
 }
 
 /*
diff --git a/src/option.c b/src/option.c
index f61d726..5c28c80 100644
--- a/src/option.c
+++ b/src/option.c
@@ -4735,6 +4735,44 @@
     return NULL;
 }
 
+#ifdef FEAT_QUICKFIX
+/*
+ * Process the new 'chistory' or 'lhistory' option value. 'chistory' will
+ * be used if args->os_varp is the same as p_chi, else 'lhistory'.
+ */
+    char *
+did_set_xhistory(optset_T *args)
+{
+    int is_p_chi = (long*)args->os_varp == &p_chi;
+    int err;
+    long *arg = (is_p_chi) ? &p_chi :(long*)args->os_varp;
+
+    // cannot have zero or negative number of quickfix lists in a stack
+    if (*arg < 1)
+    {
+	*arg = args->os_oldval.number;
+	return e_cannot_have_negative_or_zero_number_of_quickfix;
+    }
+
+    // cannot have more than 100 quickfix lists in a stack
+    if (*arg > 100)
+    {
+	*arg = args->os_oldval.number;
+	return e_cannot_have_more_than_hundred_quickfix;
+    }
+
+    if (is_p_chi)
+	err = qf_resize_quickfix_stack(*arg);
+    else
+	err = ll_resize_stack(curwin, *arg);
+
+    if (err == FAIL)
+	return e_failed_resizing_quickfix_stack;
+
+    return NULL;
+}
+#endif
+
 /*
  * Set the value of a boolean option, and take care of side effects.
  * Returns NULL for success, or an error message for an error.
@@ -6653,6 +6691,7 @@
 	case PV_WFW:	return (char_u *)&(curwin->w_p_wfw);
 #if defined(FEAT_QUICKFIX)
 	case PV_PVW:	return (char_u *)&(curwin->w_p_pvw);
+	case PV_LHI:	return (char_u *)&(curwin->w_p_lhi);
 #endif
 #ifdef FEAT_RIGHTLEFT
 	case PV_RL:	return (char_u *)&(curwin->w_p_rl);
@@ -6973,6 +7012,9 @@
 #ifdef FEAT_SIGNS
     to->wo_scl = copy_option_val(from->wo_scl);
 #endif
+#ifdef FEAT_QUICKFIX
+    to->wo_lhi = from->wo_lhi;
+#endif
 
 #ifdef FEAT_EVAL
     // Copy the script context so that we know where the value was last set.
diff --git a/src/option.h b/src/option.h
index 0b568f1..54bdeed 100644
--- a/src/option.h
+++ b/src/option.h
@@ -859,6 +859,7 @@
 EXTERN long	p_report;	// 'report'
 #if defined(FEAT_QUICKFIX)
 EXTERN long	p_pvh;		// 'previewheight'
+EXTERN long     p_chi;          // 'chistory'
 #endif
 #ifdef MSWIN
 EXTERN int	p_rs;		// 'restorescreen'
@@ -1323,6 +1324,7 @@
 #endif
 #if defined(FEAT_QUICKFIX)
     , WV_PVW
+    , WV_LHI
 #endif
 #ifdef FEAT_RIGHTLEFT
     , WV_RL
diff --git a/src/optiondefs.h b/src/optiondefs.h
index b6b5d49..8ed4ebe 100644
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -192,6 +192,7 @@
 #endif
 #if defined(FEAT_QUICKFIX)
 # define PV_PVW		OPT_WIN(WV_PVW)
+# define PV_LHI         OPT_WIN(WV_LHI)
 #endif
 #ifdef FEAT_RIGHTLEFT
 # define PV_RL		OPT_WIN(WV_RL)
@@ -575,6 +576,15 @@
 			    {(char_u *)0L, (char_u *)0L}
 #endif
 			    SCTX_INIT},
+    {"chistory",    "chi",  P_NUM|P_VI_DEF,
+#ifdef FEAT_QUICKFIX
+			    (char_u *)&p_chi, PV_NONE, did_set_xhistory, NULL,
+			    {(char_u *)10L, (char_u *)0L}
+#else
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
+			    {(char_u *)0L, (char_u *)0L}
+#endif
+			    SCTX_INIT},
     {"cindent",	    "cin",  P_BOOL|P_VI_DEF|P_VIM,
 			    (char_u *)&p_cin, PV_CIN, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
@@ -1573,6 +1583,15 @@
     {"lazyredraw",  "lz",   P_BOOL|P_VI_DEF,
 			    (char_u *)&p_lz, PV_NONE, NULL, NULL,
 			    {(char_u *)FALSE, (char_u *)0L} SCTX_INIT},
+    {"lhistory", "lhi",	    P_NUM|P_VI_DEF,
+#ifdef FEAT_QUICKFIX
+			    (char_u *)VAR_WIN, PV_LHI, did_set_xhistory, NULL,
+			    {(char_u *)10L, (char_u *)0L}
+#else
+			    (char_u *)NULL, PV_NONE, NULL, NULL,
+			    {(char_u *)0L, (char_u *)0L}
+#endif
+			    SCTX_INIT},
     {"linebreak",   "lbr",  P_BOOL|P_VI_DEF|P_RWIN,
 #ifdef FEAT_LINEBREAK
 			    (char_u *)VAR_WIN, PV_LBR, NULL, NULL,
diff --git a/src/proto/option.pro b/src/proto/option.pro
index 12be870..c6dfc1b 100644
--- a/src/proto/option.pro
+++ b/src/proto/option.pro
@@ -88,6 +88,7 @@
 char *did_set_winminwidth(optset_T *args);
 char *did_set_winwidth(optset_T *args);
 char *did_set_wrap(optset_T *args);
+char *did_set_xhistory(optset_T *args);
 void check_redraw(long_u flags);
 int findoption(char_u *arg);
 getoption_T get_option_value(char_u *name, long *numval, char_u **stringval, int *flagsp, int scope);
diff --git a/src/proto/quickfix.pro b/src/proto/quickfix.pro
index b708d6b..8102a8f 100644
--- a/src/proto/quickfix.pro
+++ b/src/proto/quickfix.pro
@@ -3,6 +3,9 @@
 int qf_stack_get_bufnr(void);
 void qf_free_all(win_T *wp);
 void check_quickfix_busy(void);
+int qf_resize_quickfix_stack(int n);
+int ll_resize_stack(win_T *wp, int n);
+int qf_init_quickfix_stack(void);
 void copy_loclist_stack(win_T *from, win_T *to);
 void qf_jump(qf_info_T *qi, int dir, int errornr, int forceit);
 void qf_list(exarg_T *eap);
diff --git a/src/quickfix.c b/src/quickfix.c
index 11bfa82..9722058 100644
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -51,7 +51,6 @@
 /*
  * There is a stack of error lists.
  */
-#define LISTCOUNT   10
 #define INVALID_QFIDX (-1)
 #define INVALID_QFBUFNR (0)
 
@@ -113,12 +112,14 @@
     int		qf_refcount;
     int		qf_listcount;	    // current number of lists
     int		qf_curlist;	    // current error list
-    qf_list_T	qf_lists[LISTCOUNT];
+    int         qf_maxcount;        // maximum number of lists
+    qf_list_T	*qf_lists;
     qfltype_T	qfl_type;	    // type of list
     int		qf_bufnr;	    // quickfix window buffer number
 };
 
-static qf_info_T ql_info;	// global quickfix list
+static qf_info_T ql_info_actual; // global quickfix list
+static qf_info_T *ql_info;	// points to ql_info_actual if memory allocation is sucessful.
 static int_u last_qf_id = 0;	// Last used quickfix list id
 
 #define FMT_PATTERNS 14		// maximum number of % recognized
@@ -170,14 +171,21 @@
 // callback function for 'quickfixtextfunc'
 static callback_T qftf_cb;
 
+static void     qf_pop_stack(qf_info_T *qi, int adjust);
 static void	qf_new_list(qf_info_T *qi, char_u *qf_title);
 static int	qf_add_entry(qf_list_T *qfl, char_u *dir, char_u *fname, char_u *module, int bufnum, char_u *mesg, long lnum, long end_lnum, int col, int end_col, int vis_col, char_u *pattern, int nr, int type, typval_T *user_data, int valid);
+static int      qf_resize_stack(qf_info_T *qi, int n);
+static void     qf_sync_llw_to_win(win_T *llw);
+static void     qf_sync_win_to_llw(win_T *pwp);
+static qf_info_T *qf_alloc_stack(qfltype_T qfltype, int n);
+static qf_list_T *qf_alloc_list_stack(int n);
 static void	qf_free(qf_list_T *qfl);
 static char_u	*qf_types(int, int);
 static int	qf_get_fnum(qf_list_T *qfl, char_u *, char_u *);
 static char_u	*qf_push_dir(char_u *, struct dir_stack_T **, int is_file_stack);
 static char_u	*qf_pop_dir(struct dir_stack_T **);
 static char_u	*qf_guess_filepath(qf_list_T *qfl, char_u *);
+static win_T    *qf_find_win_with_loclist(qf_info_T *ll);
 static void	qf_jump_newwin(qf_info_T *qi, int dir, int errornr, int forceit, int newwin);
 static void	qf_fmt_text(garray_T *gap, char_u *text);
 static void	qf_range_text(garray_T *gap, qfline_T *qfp);
@@ -1902,14 +1910,12 @@
 	char_u	    *qf_title,
 	char_u	    *enc)
 {
-    qf_info_T	    *qi = &ql_info;
+    qf_info_T	    *qi = ql_info;
 
     if (wp != NULL)
-    {
 	qi = ll_get_or_alloc_list(wp);
-	if (qi == NULL)
-	    return FAIL;
-    }
+    if (qi == NULL)
+	return FAIL;
 
     return qf_init_ext(qi, qi->qf_curlist, efile, curbuf, NULL, errorformat,
 	    newlist, (linenr_T)0, (linenr_T)0, qf_title, enc);
@@ -1959,6 +1965,34 @@
 }
 
 /*
+ * Pop a quickfix list from the quickfix/location list stack
+ * Automatically adjust qf_curlist so that it stays pointed
+ * to the same list, unless it is deleted, if so then use the
+ * newest created list instead. qf_listcount will be set correctly.
+ * The above will only happen if <adjust> is TRUE.
+ */
+    static void
+qf_pop_stack(qf_info_T *qi, int adjust)
+{
+    int i;
+    qf_free(&qi->qf_lists[0]);
+    for (i = 1; i < qi->qf_listcount; ++i)
+	qi->qf_lists[i - 1] = qi->qf_lists[i];
+
+    // fill with zeroes now unused list at the top
+    vim_memset(qi->qf_lists + qi->qf_listcount - 1, 0, sizeof(*qi->qf_lists));
+
+    if (adjust)
+    {
+	qi->qf_listcount--;
+	if (qi->qf_curlist == 0)
+	    qi->qf_curlist = qi->qf_listcount - 1;
+	else
+	    qi->qf_curlist--;
+    }
+}
+
+/*
  * Prepare for adding a new quickfix list. If the current list is in the
  * middle of the stack, then all the following lists are freed and then
  * the new list is added.
@@ -1966,7 +2000,6 @@
     static void
 qf_new_list(qf_info_T *qi, char_u *qf_title)
 {
-    int		i;
     qf_list_T	*qfl;
 
     // If the current entry is not the last entry, delete entries beyond
@@ -1977,15 +2010,14 @@
 
     // When the stack is full, remove to oldest entry
     // Otherwise, add a new entry.
-    if (qi->qf_listcount == LISTCOUNT)
+    if (qi->qf_listcount == qi->qf_maxcount)
     {
-	qf_free(&qi->qf_lists[0]);
-	for (i = 1; i < LISTCOUNT; ++i)
-	    qi->qf_lists[i - 1] = qi->qf_lists[i];
-	qi->qf_curlist = LISTCOUNT - 1;
+	qf_pop_stack(qi, FALSE);
+	qi->qf_curlist = qi->qf_listcount - 1; // point to new empty list
     }
     else
 	qi->qf_curlist = qi->qf_listcount++;
+
     qfl = qf_get_curlist(qi);
     CLEAR_POINTER(qfl);
     qf_store_title(qfl, qf_title);
@@ -2017,7 +2049,9 @@
     int
 qf_stack_get_bufnr(void)
 {
-    return ql_info.qf_bufnr;
+    if (ql_info == NULL)
+	return INVALID_QFBUFNR;
+    return ql_info->qf_bufnr;
 }
 
 /*
@@ -2056,13 +2090,35 @@
     }
 }
 
+
+/*
+ * Free all lists in the stack (not including the stack)
+ */
+    static void
+qf_free_list_stack_items(qf_info_T *qi)
+{
+    for (int i = 0; i < qi->qf_listcount; ++i)
+	qf_free(qf_get_list(qi, i));
+}
+
+/*
+ * Free a qf_ifo_T struct completely
+ */
+    static void
+qf_free_lists(qf_info_T *qi)
+{
+    qf_free_list_stack_items(qi);
+
+    vim_free(qi->qf_lists);
+    vim_free(qi);
+}
+
 /*
  * Free a location list stack
  */
     static void
 ll_free_all(qf_info_T **pqi)
 {
-    int		i;
     qf_info_T	*qi;
 
     qi = *pqi;
@@ -2085,9 +2141,7 @@
 	// If the quickfix window buffer is loaded, then wipe it
 	wipe_qf_buffer(qi);
 
-	for (i = 0; i < qi->qf_listcount; ++i)
-	    qf_free(qf_get_list(qi, i));
-	vim_free(qi);
+	qf_free_lists(qi);
     }
 }
 
@@ -2097,8 +2151,7 @@
     void
 qf_free_all(win_T *wp)
 {
-    int		i;
-    qf_info_T	*qi = &ql_info;
+    qf_info_T	*qi = ql_info;
 
     if (wp != NULL)
     {
@@ -2106,10 +2159,8 @@
 	ll_free_all(&wp->w_llist);
 	ll_free_all(&wp->w_llist_ref);
     }
-    else
-	// quickfix list
-	for (i = 0; i < qi->qf_listcount; ++i)
-	    qf_free(qf_get_list(qi, i));
+    else if (qi != NULL)
+	qf_free_list_stack_items(qi); // quickfix list
 }
 
 /*
@@ -2292,10 +2343,140 @@
 }
 
 /*
- * Allocate a new quickfix/location list stack
+ * Resize global quickfix stack to be able to hold n amount of lists.
+ * returns FAIL on failure and OK on success.
+ */
+    int
+qf_resize_quickfix_stack(int n)
+{
+    if (ql_info == NULL)
+    {
+	emsg(_(e_no_quickfix_stack));
+	return FAIL;
+    }
+
+    if (qf_resize_stack(ql_info, n) == FAIL)
+	return FAIL;
+
+    return OK;
+}
+
+/*
+ * Resize location list stack for window 'wp' to be able to
+ * hold n amount of lists. Returns FAIL on failure and OK on success
+ */
+    int
+ll_resize_stack(win_T *wp, int n)
+{
+    // check if current window is a location list window;
+    // if so then sync its 'lhistory' to the parent window or vice versa
+    if (IS_LL_WINDOW(curwin))
+	qf_sync_llw_to_win(wp);
+    else
+	qf_sync_win_to_llw(wp);
+
+    qf_info_T *qi = ll_get_or_alloc_list(wp);
+
+    if (qf_resize_stack(qi, n) == FAIL)
+	return FAIL;
+
+    return OK;
+}
+
+/*
+ * Resize quickfix stack to be able to hold n amount of quickfix lists.
+ * Returns FAIL on failure and OK on success.
+ */
+    static int
+qf_resize_stack(qf_info_T *qi, int n)
+{
+    qf_list_T *new;
+    int amount_to_rm = 0, i;
+    size_t lsz = sizeof(*qi->qf_lists);
+
+    if (n == qi->qf_maxcount)
+	return OK;
+    else if (n < qi->qf_maxcount && n < qi->qf_listcount)
+    {
+	// We have too many lists to store them all in the new stack,
+	// pop lists until we can fit them all in the newly resized stack
+	amount_to_rm = qi->qf_listcount - n;
+
+	for (i = 0; i < amount_to_rm; i++)
+	    qf_pop_stack(qi, TRUE);
+    }
+
+    new = vim_realloc(qi->qf_lists, lsz * n);
+
+    if (new == NULL)
+	return FAIL;
+
+    // fill with zeroes any newly allocated memory
+    if (n > qi->qf_maxcount)
+	vim_memset(new + qi->qf_maxcount, 0, lsz * (n - qi->qf_maxcount));
+
+    qi->qf_lists = new;
+    qi->qf_maxcount = n;
+
+    qf_update_buffer(qi, NULL);
+
+    return OK;
+}
+
+/*
+ * Initialize global quickfix list, should only be called once.
+ * Returns FAIL on failure and OK on success.
+ */
+   int
+qf_init_quickfix_stack(void)
+{
+    ql_info_actual.qf_lists = qf_alloc_list_stack(p_chi);
+
+    if (ql_info_actual.qf_lists == NULL)
+	return FAIL;
+
+    ql_info = &ql_info_actual;
+    ql_info->qfl_type = QFLT_QUICKFIX;
+    ql_info->qf_maxcount = p_chi;
+    return OK;
+}
+
+/*
+ * Sync a location list window's 'lhistory' value to the parent window
+ */
+    static void
+qf_sync_llw_to_win(win_T *llw)
+{
+    win_T *wp = qf_find_win_with_loclist(llw->w_llist_ref);
+
+    if (wp != NULL)
+	wp->w_p_lhi = llw->w_p_lhi;
+}
+
+/*
+ * Sync a window's 'lhistory' value to its location list window, if any
+ */
+    static void
+qf_sync_win_to_llw(win_T *pwp)
+{
+    win_T *wp;
+    qf_info_T  *llw = pwp->w_llist;
+
+    if (llw != NULL)
+	FOR_ALL_WINDOWS(wp)
+	    if (wp->w_llist_ref == llw && bt_quickfix(wp->w_buffer))
+	    {
+		wp->w_p_lhi = pwp->w_p_lhi;
+		return;
+	    }
+}
+
+/*
+ * Allocate a new quickfix/location list stack that is able to hold
+ * up to n amount of lists
  */
     static qf_info_T *
-qf_alloc_stack(qfltype_T qfltype)
+qf_alloc_stack(qfltype_T qfltype, int n)
 {
     qf_info_T *qi;
 
@@ -2306,10 +2487,32 @@
     qi->qf_refcount++;
     qi->qfl_type = qfltype;
     qi->qf_bufnr = INVALID_QFBUFNR;
+
+    qi->qf_lists = qf_alloc_list_stack(n);
+
+    if (qi->qf_lists == NULL)
+    {
+	vim_free(qi);
+	return NULL;
+    }
+
+    qi->qf_maxcount = n;
+
     return qi;
 }
 
 /*
+ * Allocate memory for qf_lists member of qf_info_T struct.
+ * 'actual' is the actual amount of lists that have been allocated for
+ * (only set when function returns sucessfully)
+ */
+    static qf_list_T *
+qf_alloc_list_stack(int n)
+{
+    return ALLOC_CLEAR_MULT(qf_list_T, n);
+}
+
+/*
  * Return the location list stack for window 'wp'.
  * If not present, allocate a location list stack
  */
@@ -2325,7 +2528,9 @@
     ll_free_all(&wp->w_llist_ref);
 
     if (wp->w_llist == NULL)
-	wp->w_llist = qf_alloc_stack(QFLT_LOCATION);	// new location list
+	// new location list
+	wp->w_llist = qf_alloc_stack(QFLT_LOCATION, wp->w_p_lhi);
+
     return wp->w_llist;
 }
 
@@ -2338,7 +2543,7 @@
     static qf_info_T *
 qf_cmd_get_stack(exarg_T *eap, int print_emsg)
 {
-    qf_info_T	*qi = &ql_info;
+    qf_info_T	*qi = ql_info;
 
     if (is_loclist_cmd(eap->cmdidx))
     {
@@ -2350,6 +2555,8 @@
 	    return NULL;
 	}
     }
+    if (qi == NULL && print_emsg)
+	emsg(_(e_no_quickfix_stack));
 
     return qi;
 }
@@ -2364,7 +2571,7 @@
     static qf_info_T *
 qf_cmd_get_or_alloc_stack(exarg_T *eap, win_T **pwinp)
 {
-    qf_info_T	*qi = &ql_info;
+    qf_info_T	*qi = ql_info;
 
     if (is_loclist_cmd(eap->cmdidx))
     {
@@ -2494,9 +2701,12 @@
     if (qi == NULL)		    // no location list to copy
 	return;
 
-    // allocate a new location list
-    if ((to->w_llist = qf_alloc_stack(QFLT_LOCATION)) == NULL)
+    // allocate a new location list, set size of stack to 'from' window value
+    if ((to->w_llist = qf_alloc_stack(QFLT_LOCATION, from->w_p_lhi)) == NULL)
 	return;
+    else
+	// set 'to' lhi to reflect new value
+	to->w_p_lhi = to->w_llist->qf_maxcount;
 
     to->w_llist->qf_listcount = qi->qf_listcount;
 
@@ -2760,7 +2970,7 @@
     static int
 qflist_valid(win_T *wp, int_u qf_id)
 {
-    qf_info_T	*qi = &ql_info;
+    qf_info_T	*qi = ql_info;
     int		i;
 
     if (wp != NULL)
@@ -2768,9 +2978,9 @@
 	if (!win_valid(wp))
 	    return FALSE;
 	qi = GET_LOC_LIST(wp);	    // Location list
-	if (qi == NULL)
-	    return FALSE;
     }
+    if (qi == NULL)
+	return FALSE;
 
     for (i = 0; i < qi->qf_listcount; ++i)
 	if (qi->qf_lists[i].qf_id == qf_id)
@@ -3633,7 +3843,14 @@
     int			retval = OK;
 
     if (qi == NULL)
-	qi = &ql_info;
+    {
+	if (ql_info == NULL)
+	{
+	    emsg(_(e_no_quickfix_stack));
+	    return;
+	}
+	qi = ql_info;
+    }
 
     if (qf_stack_empty(qi) || qf_list_empty(qf_get_curlist(qi)))
     {
@@ -4137,7 +4354,7 @@
     int		i;
     qfline_T	*qfp;
     int		idx;
-    qf_info_T	*qi = &ql_info;
+    qf_info_T	*qi = ql_info;
     int		found_one = FALSE;
     int		buf_has_flag = wp == NULL ? BUF_HAS_QF_ENTRY : BUF_HAS_LL_ENTRY;
 
@@ -4149,6 +4366,8 @@
 	    return;
 	qi = wp->w_llist;
     }
+    else if (qi == NULL)
+	return;
 
     for (idx = 0; idx < qi->qf_listcount; ++idx)
     {
@@ -4231,10 +4450,15 @@
     void
 qf_view_result(int split)
 {
-    qf_info_T   *qi = &ql_info;
+    qf_info_T   *qi = ql_info;
 
     if (IS_LL_WINDOW(curwin))
 	qi = GET_LOC_LIST(curwin);
+    else if (qi == NULL)
+    {
+	emsg(_(e_no_quickfix_stack));
+	return;
+    }
 
     if (qf_list_empty(qf_get_curlist(qi)))
     {
@@ -4544,11 +4768,13 @@
      linenr_T
 qf_current_entry(win_T *wp)
 {
-    qf_info_T	*qi = &ql_info;
+    qf_info_T	*qi = ql_info;
 
     if (IS_LL_WINDOW(wp))
 	// In the location list window, use the referenced location list
 	qi = wp->w_llist_ref;
+    else if (qi == NULL)
+	return 0;
 
     return qf_get_curlist(qi)->qf_index;
 }
@@ -5230,7 +5456,7 @@
     char_u	*cmd;
     char_u	*enc = NULL;
     win_T	*wp = NULL;
-    qf_info_T	*qi = &ql_info;
+    qf_info_T	*qi = ql_info;
     int		res;
     char_u	*au_name = NULL;
     int_u	save_qfid;
@@ -5295,6 +5521,9 @@
 	if (qi == NULL)
 	    goto cleanup;
     }
+    else if (qi == NULL)
+	goto cleanup;
+
     if (res >= 0)
 	qf_list_changed(qf_get_curlist(qi));
 
@@ -5968,7 +6197,7 @@
 {
     char_u	*enc = NULL;
     win_T	*wp = NULL;
-    qf_info_T	*qi = &ql_info;
+    qf_info_T	*qi = ql_info;
     char_u	*au_name = NULL;
     int_u	save_qfid = 0;		// init for gcc
     int		res;
@@ -6025,6 +6254,11 @@
 	    return;
 	}
     }
+    else if (qi == NULL)
+    {
+	decr_quickfix_busy();
+	return;
+    }
     if (res >= 0)
 	qf_list_changed(qf_get_curlist(qi));
     save_qfid = qf_get_curlist(qi)->qf_id;
@@ -6953,13 +7187,12 @@
 
     if (qi == NULL)
     {
-	qi = &ql_info;
+	qi = ql_info;
 	if (wp != NULL)
-	{
 	    qi = GET_LOC_LIST(wp);
-	    if (qi == NULL)
-		return FAIL;
-	}
+	if (qi == NULL)
+	    return FAIL;
+
     }
 
     if (eidx < 0)
@@ -7038,7 +7271,7 @@
     if (l == NULL)
 	return FAIL;
 
-    qi = qf_alloc_stack(QFLT_INTERNAL);
+    qi = qf_alloc_stack(QFLT_INTERNAL, 1);
     if (qi != NULL)
     {
 	if (qf_init_ext(qi, 0, NULL, NULL, &di->di_tv, errorformat,
@@ -7047,7 +7280,8 @@
 	    (void)get_errorlist(qi, NULL, 0, 0, l);
 	    qf_free(&qi->qf_lists[0]);
 	}
-	free(qi);
+
+	qf_free_lists(qi);
     }
     dict_add_list(retdict, "items", l);
     status = OK;
@@ -7365,7 +7599,7 @@
     static int
 qf_get_properties(win_T *wp, dict_T *what, dict_T *retdict)
 {
-    qf_info_T	*qi = &ql_info;
+    qf_info_T	*qi = ql_info;
     qf_list_T	*qfl;
     int		status = OK;
     int		qf_idx = INVALID_QFIDX;
@@ -7378,6 +7612,8 @@
 
     if (wp != NULL)
 	qi = GET_LOC_LIST(wp);
+    else if (qi == NULL)
+	return FAIL;
 
     flags = qf_getprop_keys2flags(what, (wp != NULL));
 
@@ -7993,7 +8229,7 @@
     {
 	// If the location list window is open, then create a new empty
 	// location list
-	qf_info_T *new_ll = qf_alloc_stack(QFLT_LOCATION);
+	qf_info_T *new_ll = qf_alloc_stack(QFLT_LOCATION, wp->w_p_lhi);
 
 	if (new_ll != NULL)
 	{
@@ -8023,7 +8259,7 @@
 	char_u	*title,
 	dict_T	*what)
 {
-    qf_info_T	*qi = &ql_info;
+    qf_info_T	*qi = ql_info;
     int		retval = OK;
 
     if (wp != NULL)
@@ -8032,6 +8268,11 @@
 	if (qi == NULL)
 	    return FAIL;
     }
+    else if (qi == NULL)
+    {
+	emsg(_(e_no_quickfix_stack));
+	return FAIL;
+    }
 
     if (action == 'f')
     {
@@ -8067,7 +8308,7 @@
 static int mark_quickfix_user_data(qf_info_T *qi, int copyID)
 {
     int abort = FALSE;
-    for (int i = 0; i < LISTCOUNT && !abort; ++i)
+    for (int i = 0; i < qi->qf_maxcount && !abort; ++i)
     {
 	qf_list_T *qfl = &qi->qf_lists[i];
 	if (!qfl->qf_has_user_data)
@@ -8097,7 +8338,7 @@
     typval_T	*ctx;
     callback_T	*cb;
 
-    for (i = 0; i < LISTCOUNT && !abort; ++i)
+    for (i = 0; i < qi->qf_maxcount && !abort; ++i)
     {
 	ctx = qi->qf_lists[i].qf_ctx;
 	if (ctx != NULL && ctx->v_type != VAR_NUMBER
@@ -8122,11 +8363,14 @@
     tabpage_T	*tp;
     win_T	*win;
 
-    abort = mark_quickfix_ctx(&ql_info, copyID);
+    if (ql_info == NULL)
+	return TRUE;
+
+    abort = mark_quickfix_ctx(ql_info, copyID);
     if (abort)
 	return abort;
 
-    abort = mark_quickfix_user_data(&ql_info, copyID);
+    abort = mark_quickfix_user_data(ql_info, copyID);
     if (abort)
 	return abort;
 
@@ -8454,7 +8698,7 @@
     if (qi == NULL)
     {
 	// Allocate a new location list for help text matches
-	if ((qi = qf_alloc_stack(QFLT_LOCATION)) == NULL)
+	if ((qi = qf_alloc_stack(QFLT_LOCATION, 1)) == NULL)
 	    return NULL;
 	*new_ll = TRUE;
     }
@@ -8625,7 +8869,7 @@
     regmatch_T	regmatch;
     char_u	*save_cpo;
     int		save_cpo_allocated;
-    qf_info_T	*qi = &ql_info;
+    qf_info_T	*qi = ql_info;
     int		new_qi = FALSE;
     char_u	*au_name =  NULL;
     char_u	*lang = NULL;
@@ -8652,6 +8896,11 @@
 	if (qi == NULL)
 	    return;
     }
+    else if (qi == NULL)
+    {
+	emsg(_(e_no_quickfix_stack));
+	return;
+    }
 
     // Make 'cpoptions' empty, the 'l' flag should not be used here.
     save_cpo = p_cpo;
diff --git a/src/structs.h b/src/structs.h
index 0baf543..78f0a70 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -276,6 +276,8 @@
 #if defined(FEAT_QUICKFIX)
     int		wo_pvw;
 # define w_p_pvw w_onebuf_opt.wo_pvw	// 'previewwindow'
+    long        wo_lhi;
+# define w_p_lhi w_onebuf_opt.wo_lhi    // 'lhistory'
 #endif
 #ifdef FEAT_RIGHTLEFT
     int		wo_rl;
diff --git a/src/testdir/gen_opt_test.vim b/src/testdir/gen_opt_test.vim
index 8842454..4721477 100644
--- a/src/testdir/gen_opt_test.vim
+++ b/src/testdir/gen_opt_test.vim
@@ -73,6 +73,7 @@
       \		[]],
       \
       "\ number options
+      \ 'chistory': [[1, 2, 10, 50], [1000, -1]],
       \ 'cmdheight': [[1, 2, 10], [-1, 0]],
       \ 'cmdwinheight': [[1, 2, 10], [-1, 0]],
       \ 'columns': [[12, 80, 10000], [-1, 0, 10]],
@@ -83,6 +84,7 @@
       \ 'iminsert': [[0, 1, 2], [-1, 3, 999]],
       \ 'imsearch': [[-1, 0, 1, 2], [-2, 3, 999]],
       \ 'imstyle': [[0, 1], [-1, 2, 999]],
+      \ 'lhistory': [[1, 2, 10, 50], [1000, -1]],
       \ 'lines': [[2, 24, 1000], [-1, 0, 1]],
       \ 'linespace': [[-1, 0, 2, 4, 999], ['']],
       \ 'numberwidth': [[1, 4, 8, 10, 11, 20], [-1, 0, 21]],
diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim
index 03fbbee..54e3bb7 100644
--- a/src/testdir/test_quickfix.vim
+++ b/src/testdir/test_quickfix.vim
@@ -43,6 +43,8 @@
     command! -count=1 -nargs=0 Xabove <mods><count>cabove
     command! -count=1 -nargs=0 Xbefore <mods><count>cbefore
     command! -count=1 -nargs=0 Xafter <mods><count>cafter
+    command! -nargs=1 Xsethist <mods>set chistory=<args>
+    command! -nargs=0 Xsethistdefault <mods>set chistory&
     let g:Xgetlist = function('getqflist')
     let g:Xsetlist = function('setqflist')
     call setqflist([], 'f')
@@ -80,6 +82,9 @@
     command! -count=1 -nargs=0 Xabove <mods><count>labove
     command! -count=1 -nargs=0 Xbefore <mods><count>lbefore
     command! -count=1 -nargs=0 Xafter <mods><count>lafter
+    command! -nargs=1 Xsethist <mods>set lhistory=<args>
+    command! -nargs=1 Xsetlocalhist <mods>setlocal lhistory=<args>
+    command! -nargs=0 Xsethistdefault <mods>set lhistory&
     let g:Xgetlist = function('getloclist', [0])
     let g:Xsetlist = function('setloclist', [0])
     call setloclist(0, [], 'f')
@@ -6718,6 +6723,174 @@
   call Xtest_hardlink_fname('l')
 endfunc
 
+" Test for checking if correct number of tests are deleted
+" and current list stays the same after setting Xhistory
+" to a smaller number. Do roughly the same for growing the stack.
+func Xtest_resize_list_stack(cchar)
+  call s:setup_commands(a:cchar)
+  Xsethist 100
+
+  for i in range(1, 100)
+    Xexpr string(i)
+  endfor
+  Xopen
+  call assert_equal(g:Xgetlist({'nr': '$'}).nr, 100)
+  call assert_equal("|| 100", getline(1))
+  Xsethist 8
+  call assert_equal("|| 100", getline(1))
+  Xolder 5
+  call assert_equal("|| 95", getline(1))
+  Xsethist 6
+  call assert_equal("|| 95", getline(1))
+  Xsethist 1
+  call assert_equal("|| 100", getline(1))
+
+  " grow array again
+  Xsethist 100
+  for i in range(1, 99)
+    Xexpr string(i)
+  endfor
+  call assert_equal("|| 99", getline(1))
+  Xolder 99
+  call assert_equal("|| 100", getline(1))
+
+  Xsethistdefault
+endfunc
+
+func Test_resize_list_stack()
+  call Xtest_resize_list_stack('c')
+  call Xtest_resize_list_stack('l')
+endfunc
+
+" Test to check if order of lists is from
+" oldest at the bottom to newest at the top
+func Xtest_Xhistory_check_order(cchar)
+
+  Xsethist 100
+
+  for i in range(1, 100)
+    Xexpr string(i)
+  endfor
+
+  Xopen
+  for i in range(100, 1, -1)
+    let l:ret = assert_equal("|| " .. i, getline(1))
+
+    if ret == 1 || i == 1
+      break
+    endif
+    Xolder
+  endfor
+
+  for i in range(1, 50)
+    Xexpr string(i)
+  endfor
+
+  for i in range(50, 1, -1)
+    let l:ret = assert_equal("|| " .. i, getline(1))
+
+    if ret == 1 || i == 50
+      break
+    endif
+    Xolder
+  endfor
+
+  for i in range(50, 1, -1)
+    let l:ret = assert_equal("|| " .. i, getline(1))
+
+    if ret == 1 || i == 50
+      break
+    endif
+    Xolder
+  endfor
+
+  Xsethistdefault
+endfunc
+
+func Test_set_history_to_check_order()
+  call Xtest_Xhistory_check_order('c')
+  call Xtest_Xhistory_check_order('l')
+endfunc
+
+" Check if 'lhistory' is the same between the location list window
+" and associated normal window
+func Test_win_and_loc_synced()
+  new
+  set lhistory=2
+  lexpr "Text"
+  lopen
+
+  " check if lhistory is synced when modified inside the
+  " location list window
+  setlocal lhistory=1
+  wincmd k
+  call assert_equal(&lhistory, 1)
+
+  " check if lhistory is synced when modified inside the
+  " normal window
+  setlocal lhistory=10
+  lopen
+  call assert_equal(&lhistory, 10)
+
+  wincmd k
+  lclose
+  wincmd q
+
+  set lhistory&
+endfunc
+
+" Test if setting the lhistory of one window doesn't affect the other
+func Test_two_win_are_independent_of_history()
+  setlocal lhistory=10
+  new
+  setlocal lhistory=20
+  wincmd  w
+  call assert_equal(&lhistory, 10)
+  wincmd w
+  wincmd q
+
+  set lhistory&
+endfunc
+
+" Test if lhistory is copied over to a new window
+func Test_lhistory_copied_over()
+  setlocal lhistory=3
+  split
+  call assert_equal(&lhistory, 3)
+  wincmd q
+
+  set lhistory&
+endfunc
+
+" Test if error occurs when given invalid history number
+func Xtest_invalid_history_num(cchar)
+  call s:setup_commands(a:cchar)
+
+  call assert_fails('Xsethist -10000', "E1542:")
+  call assert_fails('Xsethist 10000', "E1543:")
+  Xsethistdefault
+endfunc
+
+func Test_invalid_history_num()
+  call Xtest_invalid_history_num('c')
+  call Xtest_invalid_history_num('l')
+endfunc
+
+" Test if chistory and lhistory don't affect each other
+func Test_chi_and_lhi_are_independent()
+  set chistory=100
+  set lhistory=100
+
+  set chistory=10
+  call assert_equal(&lhistory, 100)
+
+  set lhistory=1
+  call assert_equal(&chistory, 10)
+
+  set chistory&
+  set lhistory&
+endfunc
+
 func Test_quickfix_close_buffer_crash()
   new
   lexpr 'test' | lopen
diff --git a/src/version.c b/src/version.c
index bab345d..6f9ae4b 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1283,
+/**/
     1282,
 /**/
     1281,