diff options
author | ZyX <kp-pav@yandex.ru> | 2017-12-23 15:27:42 +0300 |
---|---|---|
committer | ZyX <kp-pav@yandex.ru> | 2017-12-23 15:27:42 +0300 |
commit | c9ab209f9e70b2ebd91ef4cf3857c30822a6af57 (patch) | |
tree | eb39e7370cf72c89b589cac204194ad68b9dfc15 /src/nvim/quickfix.c | |
parent | edccf18df56d982dea350770ea42ffa25759b43f (diff) | |
parent | aa951b14893d8a30ec3bad6fb4b7064ccda16d5c (diff) | |
download | rneovim-c9ab209f9e70b2ebd91ef4cf3857c30822a6af57.tar.gz rneovim-c9ab209f9e70b2ebd91ef4cf3857c30822a6af57.tar.bz2 rneovim-c9ab209f9e70b2ebd91ef4cf3857c30822a6af57.zip |
Merge branch 'master' into hide-container-impl
Diffstat (limited to 'src/nvim/quickfix.c')
-rw-r--r-- | src/nvim/quickfix.c | 275 |
1 files changed, 228 insertions, 47 deletions
diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 7e2193b8bb..f85009dca8 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -85,6 +85,7 @@ typedef struct qf_list_S { int qf_nonevalid; // TRUE if not a single valid entry found char_u *qf_title; // title derived from the command that created // the error list + typval_T *qf_ctx; // context set by setqflist/setloclist } qf_list_T; struct qf_info_S { @@ -161,6 +162,7 @@ typedef struct { buf_T *buf; linenr_T buflnum; linenr_T lnumlast; + vimconv_T vc; } qfstate_T; typedef struct { @@ -194,19 +196,19 @@ typedef struct { static char_u *qf_last_bufname = NULL; static bufref_T qf_last_bufref = { NULL, 0, 0 }; -/* - * Read the errorfile "efile" into memory, line by line, building the error - * list. Set the error list's title to qf_title. - * Return -1 for error, number of errors for success. - */ -int -qf_init ( - win_T *wp, - char_u *efile, - char_u *errorformat, - int newlist, /* TRUE: start a new error list */ - char_u *qf_title -) +/// Read the errorfile "efile" into memory, line by line, building the error +/// list. Set the error list's title to qf_title. +/// +/// @params wp If non-NULL, make a location list +/// @params efile If non-NULL, errorfile to parse +/// @params errorformat 'errorformat' string used to parse the error lines +/// @params newlist If true, create a new error list +/// @params qf_title If non-NULL, title of the error list +/// @params enc If non-NULL, encoding used to parse errors +/// +/// @returns -1 for error, number of errors for success. +int qf_init(win_T *wp, char_u *efile, char_u *errorformat, int newlist, + char_u *qf_title, char_u *enc) { qf_info_T *qi = &ql_info; @@ -215,8 +217,8 @@ qf_init ( } return qf_init_ext(qi, efile, curbuf, NULL, errorformat, newlist, - (linenr_T)0, (linenr_T)0, - qf_title); + (linenr_T)0, (linenr_T)0, + qf_title, enc); } // Maximum number of bytes allowed per line while reading an errorfile. @@ -640,6 +642,22 @@ retry: } else { state->linebuf = IObuff; } + + // Convert a line if it contains a non-ASCII character + if (state->vc.vc_type != CONV_NONE && has_non_ascii(state->linebuf)) { + char_u *line = string_convert(&state->vc, state->linebuf, &state->linelen); + if (line != NULL) { + if (state->linelen < IOSIZE) { + STRLCPY(state->linebuf, line, state->linelen + 1); + xfree(line); + } else { + xfree(state->growbuf); + state->linebuf = state->growbuf = line; + state->growbufsiz = state->linelen < LINE_MAXLEN + ? state->linelen : LINE_MAXLEN; + } + } + } return QF_OK; } @@ -785,7 +803,7 @@ restofline: fields->type = *regmatch.startp[i]; } if (fmt_ptr->flags == '+' && !qi->qf_multiscan) { // %+ - if (linelen > fields->errmsglen) { + if (linelen >= fields->errmsglen) { // linelen + null terminator fields->errmsg = xrealloc(fields->errmsg, linelen + 1); fields->errmsglen = linelen + 1; @@ -796,7 +814,7 @@ restofline: continue; } len = (size_t)(regmatch.endp[i] - regmatch.startp[i]); - if (len > fields->errmsglen) { + if (len >= fields->errmsglen) { // len + null terminator fields->errmsg = xrealloc(fields->errmsg, len + 1); fields->errmsglen = len + 1; @@ -873,7 +891,7 @@ restofline: fields->namebuf[0] = NUL; // no match found, remove file name fields->lnum = 0; // don't jump to this line fields->valid = false; - if (linelen > fields->errmsglen) { + if (linelen >= fields->errmsglen) { // linelen + null terminator fields->errmsg = xrealloc(fields->errmsg, linelen + 1); fields->errmsglen = linelen + 1; @@ -978,15 +996,15 @@ qf_init_ext( buf_T *buf, typval_T *tv, char_u *errorformat, - int newlist, /* TRUE: start a new error list */ - linenr_T lnumfirst, /* first line number to use */ - linenr_T lnumlast, /* last line number to use */ - char_u *qf_title + int newlist, // TRUE: start a new error list + linenr_T lnumfirst, // first line number to use + linenr_T lnumlast, // last line number to use + char_u *qf_title, + char_u *enc ) { - qfstate_T state = { NULL, 0, NULL, 0, NULL, NULL, NULL, NULL, - NULL, NULL, 0, 0 }; - qffields_T fields = { NULL, NULL, 0, 0L, 0, false, NULL, 0, 0, 0 }; + qfstate_T state; + qffields_T fields; qfline_T *old_last = NULL; bool adding = false; static efm_T *fmt_first = NULL; @@ -999,6 +1017,13 @@ qf_init_ext( xfree(qf_last_bufname); qf_last_bufname = NULL; + memset(&state, 0, sizeof(state)); + memset(&fields, 0, sizeof(fields)); + state.vc.vc_type = CONV_NONE; + if (enc != NULL && *enc != NUL) { + convert_setup(&state.vc, enc, p_enc); + } + fields.namebuf = xmalloc(CMDBUFFSIZE + 1); fields.errmsglen = CMDBUFFSIZE + 1; fields.errmsg = xmalloc(fields.errmsglen); @@ -1150,6 +1175,10 @@ qf_init_end: qf_update_buffer(qi, old_last); + if (state.vc.vc_type != CONV_NONE) { + convert_setup(&state.vc, NULL, NULL); + } + return retval; } @@ -1384,6 +1413,13 @@ void copy_loclist(win_T *from, win_T *to) else to_qfl->qf_title = NULL; + if (from_qfl->qf_ctx != NULL) { + to_qfl->qf_ctx = xcalloc(1, sizeof(typval_T)); + tv_copy(from_qfl->qf_ctx, to_qfl->qf_ctx); + } else { + to_qfl->qf_ctx = NULL; + } + if (from_qfl->qf_count) { qfline_T *from_qfp; qfline_T *prevp; @@ -2385,12 +2421,21 @@ static void qf_free(qf_info_T *qi, int idx) qi->qf_lists[idx].qf_start = NULL; qi->qf_lists[idx].qf_ptr = NULL; qi->qf_lists[idx].qf_title = NULL; + tv_free(qi->qf_lists[idx].qf_ctx); + qi->qf_lists[idx].qf_ctx = NULL; qi->qf_lists[idx].qf_index = 0; + qi->qf_lists[idx].qf_start = NULL; + qi->qf_lists[idx].qf_last = NULL; + qi->qf_lists[idx].qf_ptr = NULL; + qi->qf_lists[idx].qf_nonevalid = true; qf_clean_dir_stack(&qi->qf_dir_stack); qi->qf_directory = NULL; qf_clean_dir_stack(&qi->qf_file_stack); qi->qf_currfile = NULL; + qi->qf_multiline = false; + qi->qf_multiignore = false; + qi->qf_multiscan = false; } /* @@ -3027,6 +3072,7 @@ void ex_make(exarg_T *eap) qf_info_T *qi = &ql_info; int res; char_u *au_name = NULL; + char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc; /* Redirect ":grep" to ":vimgrep" if 'grepprg' is "internal". */ if (grep_internal(eap->cmdidx)) { @@ -3086,11 +3132,11 @@ void ex_make(exarg_T *eap) res = qf_init(wp, fname, (eap->cmdidx != CMD_make && eap->cmdidx != CMD_lmake) ? p_gefm : p_efm, - (eap->cmdidx != CMD_grepadd - && eap->cmdidx != CMD_lgrepadd), - *eap->cmdlinep); - if (wp != NULL) + (eap->cmdidx != CMD_grepadd && eap->cmdidx != CMD_lgrepadd), + *eap->cmdlinep, enc); + if (wp != NULL) { qi = GET_LOC_LIST(wp); + } if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, curbuf->b_fname, TRUE, curbuf); @@ -3432,19 +3478,18 @@ void ex_cfile(exarg_T *eap) if (*eap->arg != NUL) set_string_option_direct((char_u *)"ef", -1, eap->arg, OPT_FREE, 0); - /* - * This function is used by the :cfile, :cgetfile and :caddfile - * commands. - * :cfile always creates a new quickfix list and jumps to the - * first error. - * :cgetfile creates a new quickfix list but doesn't jump to the - * first error. - * :caddfile adds to an existing quickfix list. If there is no - * quickfix list then a new list is created. - */ + char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc; + // This function is used by the :cfile, :cgetfile and :caddfile + // commands. + // :cfile always creates a new quickfix list and jumps to the + // first error. + // :cgetfile creates a new quickfix list but doesn't jump to the + // first error. + // :caddfile adds to an existing quickfix list. If there is no + // quickfix list then a new list is created. if (qf_init(wp, p_ef, p_efm, (eap->cmdidx != CMD_caddfile && eap->cmdidx != CMD_laddfile), - *eap->cmdlinep) > 0 + *eap->cmdlinep, enc) > 0 && (eap->cmdidx == CMD_cfile || eap->cmdidx == CMD_lfile)) { if (au_name != NULL) @@ -4028,6 +4073,7 @@ enum { QF_GETLIST_ITEMS = 0x2, QF_GETLIST_NR = 0x4, QF_GETLIST_WINID = 0x8, + QF_GETLIST_CONTEXT = 0x10, QF_GETLIST_ALL = 0xFF }; @@ -4078,6 +4124,10 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) flags |= QF_GETLIST_WINID; } + if (tv_dict_find(what, S_LEN("context")) != NULL) { + flags |= QF_GETLIST_CONTEXT; + } + if (flags & QF_GETLIST_TITLE) { char_u *t = qi->qf_lists[qf_idx].qf_title; if (t == NULL) { @@ -4095,6 +4145,20 @@ int get_errorlist_properties(win_T *wp, dict_T *what, dict_T *retdict) } } + if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) { + if (qi->qf_lists[qf_idx].qf_ctx != NULL) { + di = tv_dict_item_alloc_len(S_LEN("context")); + if (di != NULL) { + tv_copy(qi->qf_lists[qf_idx].qf_ctx, &di->di_tv); + if (tv_dict_add(retdict, di) == FAIL) { + tv_dict_item_free(di); + } + } + } else { + status = tv_dict_add_str(retdict, S_LEN("context"), ""); + } + } + return status; } @@ -4158,6 +4222,11 @@ static int qf_add_entries(qf_info_T *qi, list_T *list, char_u *title, bufnum = 0; } + // If the 'valid' field is present it overrules the detected value. + if (tv_dict_find(d, "valid", -1) != NULL) { + valid = (int)tv_dict_get_number(d, "valid"); + } + int status = qf_add_entry(qi, NULL, // dir (char_u *)filename, @@ -4213,7 +4282,10 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) if ((di = tv_dict_find(what, S_LEN("nr"))) != NULL) { // Use the specified quickfix/location list if (di->di_tv.v_type == VAR_NUMBER) { - qf_idx = (int)di->di_tv.vval.v_number - 1; + // for zero use the current list + if (di->di_tv.vval.v_number != 0) { + qf_idx = (int)di->di_tv.vval.v_number - 1; + } if (qf_idx < 0 || qf_idx >= qi->qf_listcount) { return FAIL; } @@ -4240,9 +4312,75 @@ static int qf_set_properties(qf_info_T *qi, dict_T *what, int action) } } + if ((di = tv_dict_find(what, S_LEN("context"))) != NULL) { + tv_free(qi->qf_lists[qf_idx].qf_ctx); + + typval_T *ctx = xcalloc(1, sizeof(typval_T)); + tv_copy(&di->di_tv, ctx); + qi->qf_lists[qf_idx].qf_ctx = ctx; + } + return retval; } +// Find the non-location list window with the specified location list. +static win_T * find_win_with_ll(qf_info_T *qi) +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if ((wp->w_llist == qi) && !bt_quickfix(wp->w_buffer)) { + return wp; + } + } + + return NULL; +} + +// Free the entire quickfix/location list stack. +// If the quickfix/location list window is open, then clear it. +static void qf_free_stack(win_T *wp, qf_info_T *qi) +{ + win_T *qfwin = qf_find_win(qi); + + if (qfwin != NULL) { + // If the quickfix/location list window is open, then clear it + if (qi->qf_curlist < qi->qf_listcount) { + qf_free(qi, qi->qf_curlist); + } + qf_update_buffer(qi, NULL); + } + + win_T *llwin = NULL; + win_T *orig_wp = wp; + if (wp != NULL && IS_LL_WINDOW(wp)) { + // If in the location list window, then use the non-location list + // window with this location list (if present) + llwin = find_win_with_ll(qi); + if (llwin != NULL) { + wp = llwin; + } + } + + qf_free_all(wp); + if (wp == NULL) { + // quickfix list + qi->qf_curlist = 0; + qi->qf_listcount = 0; + } else if (IS_LL_WINDOW(orig_wp)) { + // If the location list window is open, then create a new empty location + // list + qf_info_T *new_ll = ll_new_list(); + + // first free the list reference in the location list window + ll_free_all(&orig_wp->w_llist_ref); + + orig_wp->w_llist_ref = new_ll; + if (llwin != NULL) { + llwin->w_llist = new_ll; + new_ll->qf_refcount++; + } + } +} + // Populate the quickfix list with the items supplied in the list // of dictionaries. "title" will be copied to w:quickfix_title // "action" is 'a' for add, 'r' for replace. Otherwise create a new list. @@ -4256,7 +4394,10 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title, qi = ll_get_or_alloc_list(wp); } - if (what != NULL) { + if (action == 'f') { + // Free the entire quickfix or location list stack + qf_free_stack(wp, qi); + } else if (what != NULL) { retval = qf_set_properties(qi, what, action); } else { retval = qf_add_entries(qi, list, title, action); @@ -4265,6 +4406,42 @@ int set_errorlist(win_T *wp, list_T *list, int action, char_u *title, return retval; } +static bool mark_quickfix_ctx(qf_info_T *qi, int copyID) +{ + bool abort = false; + + for (int i = 0; i < LISTCOUNT && !abort; i++) { + typval_T *ctx = qi->qf_lists[i].qf_ctx; + if (ctx != NULL && ctx->v_type != VAR_NUMBER + && ctx->v_type != VAR_STRING && ctx->v_type != VAR_FLOAT) { + abort = set_ref_in_item(ctx, copyID, NULL, NULL); + } + } + + return abort; +} + +/// Mark the context of the quickfix list and the location lists (if present) as +/// "in use". So that garabage collection doesn't free the context. +bool set_ref_in_quickfix(int copyID) +{ + bool abort = mark_quickfix_ctx(&ql_info, copyID); + if (abort) { + return abort; + } + + FOR_ALL_TAB_WINDOWS(tp, win) { + if (win->w_llist != NULL) { + abort = mark_quickfix_ctx(win->w_llist, copyID); + if (abort) { + return abort; + } + } + } + + return abort; +} + /* * ":[range]cbuffer [bufnr]" command. * ":[range]caddbuffer [bufnr]" command. @@ -4342,7 +4519,7 @@ void ex_cbuffer(exarg_T *eap) if (qf_init_ext(qi, NULL, buf, NULL, p_efm, (eap->cmdidx != CMD_caddbuffer && eap->cmdidx != CMD_laddbuffer), - eap->line1, eap->line2, qf_title) > 0) { + eap->line1, eap->line2, qf_title, NULL) > 0) { if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name, curbuf->b_fname, true, curbuf); @@ -4407,7 +4584,7 @@ void ex_cexpr(exarg_T *eap) if (qf_init_ext(qi, NULL, NULL, &tv, p_efm, (eap->cmdidx != CMD_caddexpr && eap->cmdidx != CMD_laddexpr), - (linenr_T)0, (linenr_T)0, *eap->cmdlinep) > 0) { + (linenr_T)0, (linenr_T)0, *eap->cmdlinep, NULL) > 0) { if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)au_name, curbuf->b_fname, true, curbuf); @@ -4478,6 +4655,9 @@ void ex_helpgrep(exarg_T *eap) } } + // Autocommands may change the list. Save it for later comparison + qf_info_T *save_qi = qi; + regmatch.regprog = vim_regcomp(eap->arg, RE_MAGIC + RE_STRING); regmatch.rm_ic = FALSE; if (regmatch.regprog != NULL) { @@ -4590,10 +4770,11 @@ void ex_helpgrep(exarg_T *eap) if (au_name != NULL) { apply_autocmds(EVENT_QUICKFIXCMDPOST, au_name, - curbuf->b_fname, TRUE, curbuf); - if (!new_qi && qi != &ql_info && qf_find_buf(qi) == NULL) - /* autocommands made "qi" invalid */ + curbuf->b_fname, true, curbuf); + if (!new_qi && qi != save_qi && qf_find_buf(qi) == NULL) { + // autocommands made "qi" invalid return; + } } /* Jump to first match. */ |