aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/ex_getln.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/ex_getln.c')
-rw-r--r--src/nvim/ex_getln.c270
1 files changed, 200 insertions, 70 deletions
diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c
index a4e5a4dcd7..dba7a73814 100644
--- a/src/nvim/ex_getln.c
+++ b/src/nvim/ex_getln.c
@@ -34,7 +34,6 @@
#include "nvim/menu.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
-#include "nvim/misc2.h"
#include "nvim/memory.h"
#include "nvim/cursor_shape.h"
#include "nvim/keymap.h"
@@ -96,19 +95,20 @@ typedef struct command_line_state {
char_u *lookfor; // string to match
int hiscnt; // current history line in use
int histype; // history type to be used
- pos_T old_cursor;
- colnr_T old_curswant;
- colnr_T old_leftcol;
- linenr_T old_topline;
- int old_topfill;
- linenr_T old_botline;
+ pos_T old_cursor;
+ colnr_T old_curswant;
+ colnr_T old_leftcol;
+ linenr_T old_topline;
+ int old_topfill;
+ linenr_T old_botline;
int did_incsearch;
int incsearch_postponed;
int did_wild_list; // did wild_list() recently
int wim_index; // index in wim_flags[]
int res;
- int save_msg_scroll;
- int save_State; // remember State when called
+ int save_msg_scroll;
+ int save_State; // remember State when called
+ char_u *save_p_icm;
int some_key_typed; // one of the keys was typed
// mouse drag and release events are ignored, unless they are
// preceded with a mouse down event
@@ -160,6 +160,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
s->indent = indent;
s->save_msg_scroll = msg_scroll;
s->save_State = State;
+ s->save_p_icm = vim_strsave(p_icm);
s->ignore_drag_release = true;
if (s->firstc == -1) {
@@ -324,9 +325,12 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
need_wait_return = false;
}
+ set_string_option_direct((char_u *)"icm", -1, s->save_p_icm, OPT_FREE,
+ SID_NONE);
State = s->save_State;
setmouse();
ui_cursor_shape(); // may show different cursor shape
+ xfree(s->save_p_icm);
{
char_u *p = ccline.cmdbuff;
@@ -358,7 +362,8 @@ static int command_line_execute(VimState *state, int key)
s->c = key;
if (s->c == K_EVENT) {
- queue_process_events(loop.events);
+ multiqueue_process_events(main_loop.events);
+ redrawcmdline();
return 1;
}
@@ -981,7 +986,6 @@ static int command_line_handle_key(CommandLineState *s)
status_redraw_curbuf();
return command_line_not_changed(s);
- // case '@': only in very old vi
case Ctrl_U:
// delete all characters left of the cursor
s->j = ccline.cmdpos;
@@ -996,7 +1000,6 @@ static int command_line_handle_key(CommandLineState *s)
redrawcmd();
return command_line_changed(s);
-
case ESC: // get here if p_wc != ESC or when ESC typed twice
case Ctrl_C:
// In exmode it doesn't make sense to return. Except when
@@ -1489,11 +1492,11 @@ static int command_line_handle_key(CommandLineState *s)
static int command_line_not_changed(CommandLineState *s)
{
- // This part implements incremental searches for "/" and "?" Jump to
- // cmdline_not_changed when a character has been read but the command line
- // did not change. Then we only search and redraw if something changed in
- // the past. Jump to cmdline_changed when the command line did change.
- // (Sorry for the goto's, I know it is ugly).
+ // Incremental searches for "/" and "?":
+ // Enter command_line_not_changed() when a character has been read but the
+ // command line did not change. Then we only search and redraw if something
+ // changed in the past.
+ // Enter command_line_changed() when the command line did change.
if (!s->incsearch_postponed) {
return 1;
}
@@ -1591,6 +1594,36 @@ static int command_line_changed(CommandLineState *s)
msg_starthere();
redrawcmdline();
s->did_incsearch = true;
+ } else if (s->firstc == ':'
+ && current_SID == 0 // only if interactive
+ && *p_icm != NUL // 'inccommand' is set
+ && curbuf->b_p_ma // buffer is modifiable
+ && cmdline_star == 0 // not typing a password
+ && cmd_can_preview(ccline.cmdbuff)
+ && !vpeekc_any()) {
+ // Show 'inccommand' preview. It works like this:
+ // 1. Do the command.
+ // 2. Command implementation detects CMDPREVIEW state, then:
+ // - Update the screen while the effects are in place.
+ // - Immediately undo the effects.
+ State |= CMDPREVIEW;
+ emsg_silent++; // Block error reporting as the command may be incomplete
+ do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_NOWAIT);
+ emsg_silent--; // Unblock error reporting
+
+ // Restore the window "view".
+ curwin->w_cursor = s->old_cursor;
+ curwin->w_curswant = s->old_curswant;
+ curwin->w_leftcol = s->old_leftcol;
+ curwin->w_topline = s->old_topline;
+ curwin->w_topfill = s->old_topfill;
+ curwin->w_botline = s->old_botline;
+ update_topline();
+
+ redrawcmdline();
+ } else if (State & CMDPREVIEW) {
+ State = (State & ~CMDPREVIEW);
+ update_screen(SOME_VALID); // Clear 'inccommand' preview.
}
if (cmdmsg_rl || (p_arshape && !p_tbidi && enc_utf8)) {
@@ -1688,10 +1721,15 @@ int text_locked(void) {
*/
void text_locked_msg(void)
{
- if (cmdwin_type != 0)
- EMSG(_(e_cmdwin));
- else
- EMSG(_(e_secure));
+ EMSG(_(get_text_locked_msg()));
+}
+
+char_u * get_text_locked_msg(void) {
+ if (cmdwin_type != 0) {
+ return e_cmdwin;
+ } else {
+ return e_secure;
+ }
}
/*
@@ -2548,10 +2586,9 @@ static void cmdline_del(int from)
ccline.cmdpos = from;
}
-/*
- * this function is called when the screen size changes and with incremental
- * search
- */
+// This function is called when the screen size changes and with incremental
+// search and in other situations where the command line may have been
+// overwritten.
void redrawcmdline(void)
{
if (cmd_silent)
@@ -3439,6 +3476,7 @@ addstar (
|| context == EXPAND_COMPILER
|| context == EXPAND_OWNSYNTAX
|| context == EXPAND_FILETYPE
+ || context == EXPAND_PACKADD
|| (context == EXPAND_TAGS && fname[0] == '/'))
retval = vim_strnsave(fname, len);
else {
@@ -3577,15 +3615,16 @@ static void set_expand_context(expand_T *xp)
xp->xp_context = EXPAND_NOTHING;
return;
}
- set_cmd_context(xp, ccline.cmdbuff, ccline.cmdlen, ccline.cmdpos);
+ set_cmd_context(xp, ccline.cmdbuff, ccline.cmdlen, ccline.cmdpos, true);
}
void
set_cmd_context (
expand_T *xp,
- char_u *str, /* start of command line */
- int len, /* length of command line (excl. NUL) */
- int col /* position of cursor */
+ char_u *str, // start of command line
+ int len, // length of command line (excl. NUL)
+ int col, // position of cursor
+ int use_ccline // use ccline for info
)
{
int old_char = NUL;
@@ -3600,10 +3639,10 @@ set_cmd_context (
str[col] = NUL;
nextcomm = str;
- if (ccline.cmdfirstc == '=') {
- /* pass CMD_SIZE because there is no real command */
+ if (use_ccline && ccline.cmdfirstc == '=') {
+ // pass CMD_SIZE because there is no real command
set_context_for_expression(xp, str, CMD_SIZE);
- } else if (ccline.input_fn) {
+ } else if (use_ccline && ccline.input_fn) {
xp->xp_context = ccline.xp_context;
xp->xp_pattern = ccline.cmdbuff;
xp->xp_arg = ccline.xp_arg;
@@ -3668,27 +3707,54 @@ expand_cmdline (
return EXPAND_OK;
}
-/*
- * Cleanup matches for help tags: remove "@en" if "en" is the only language.
- */
-
+// Cleanup matches for help tags:
+// Remove "@ab" if the top of 'helplang' is "ab" and the language of the first
+// tag matches it. Otherwise remove "@en" if "en" is the only language.
static void cleanup_help_tags(int num_file, char_u **file)
{
- int i, j;
- int len;
+ char_u buf[4];
+ char_u *p = buf;
- for (i = 0; i < num_file; ++i) {
- len = (int)STRLEN(file[i]) - 3;
- if (len > 0 && STRCMP(file[i] + len, "@en") == 0) {
- /* Sorting on priority means the same item in another language may
- * be anywhere. Search all items for a match up to the "@en". */
- for (j = 0; j < num_file; ++j)
+ if (p_hlg[0] != NUL && (p_hlg[0] != 'e' || p_hlg[1] != 'n')) {
+ *p++ = '@';
+ *p++ = p_hlg[0];
+ *p++ = p_hlg[1];
+ }
+ *p = NUL;
+
+ for (int i = 0; i < num_file; i++) {
+ int len = (int)STRLEN(file[i]) - 3;
+ if (len <= 0) {
+ continue;
+ }
+ if (STRCMP(file[i] + len, "@en") == 0) {
+ // Sorting on priority means the same item in another language may
+ // be anywhere. Search all items for a match up to the "@en".
+ int j;
+ for (j = 0; j < num_file; j++) {
if (j != i
&& (int)STRLEN(file[j]) == len + 3
- && STRNCMP(file[i], file[j], len + 1) == 0)
+ && STRNCMP(file[i], file[j], len + 1) == 0) {
break;
- if (j == num_file)
+ }
+ }
+ if (j == num_file) {
+ // item only exists with @en, remove it
file[i][len] = NUL;
+ }
+ }
+ }
+
+ if (*buf != NUL) {
+ for (int i = 0; i < num_file; i++) {
+ int len = (int)STRLEN(file[i]) - 3;
+ if (len <= 0) {
+ continue;
+ }
+ if (STRCMP(file[i] + len, buf) == 0) {
+ // remove the default language
+ file[i][len] = NUL;
+ }
}
}
}
@@ -3793,23 +3859,27 @@ ExpandFromContext (
|| xp->xp_context == EXPAND_TAGS_LISTFILES)
return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file);
if (xp->xp_context == EXPAND_COLORS) {
- char *directories[] = {"colors", NULL};
- return ExpandRTDir(pat, num_file, file, directories);
+ char *directories[] = { "colors", NULL };
+ return ExpandRTDir(pat, DIP_START + DIP_OPT, num_file, file, directories);
}
if (xp->xp_context == EXPAND_COMPILER) {
- char *directories[] = {"compiler", NULL};
- return ExpandRTDir(pat, num_file, file, directories);
+ char *directories[] = { "compiler", NULL };
+ return ExpandRTDir(pat, 0, num_file, file, directories);
}
if (xp->xp_context == EXPAND_OWNSYNTAX) {
- char *directories[] = {"syntax", NULL};
- return ExpandRTDir(pat, num_file, file, directories);
+ char *directories[] = { "syntax", NULL };
+ return ExpandRTDir(pat, 0, num_file, file, directories);
}
if (xp->xp_context == EXPAND_FILETYPE) {
- char *directories[] = {"syntax", "indent", "ftplugin", NULL};
- return ExpandRTDir(pat, num_file, file, directories);
+ char *directories[] = { "syntax", "indent", "ftplugin", NULL };
+ return ExpandRTDir(pat, 0, num_file, file, directories);
}
- if (xp->xp_context == EXPAND_USER_LIST)
+ if (xp->xp_context == EXPAND_USER_LIST) {
return ExpandUserList(xp, num_file, file);
+ }
+ if (xp->xp_context == EXPAND_PACKADD) {
+ return ExpandPackAddDir(pat, num_file, file);
+ }
regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0);
if (regmatch.regprog == NULL)
@@ -4189,12 +4259,16 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file)
return OK;
}
-/*
- * Expand color scheme, compiler or filetype names:
- * 'runtimepath'/{dirnames}/{pat}.vim
- * "dirnames" is an array with one or more directory names.
- */
-static int ExpandRTDir(char_u *pat, int *num_file, char_u ***file, char *dirnames[])
+/// Expand color scheme, compiler or filetype names.
+/// Search from 'runtimepath':
+/// 'runtimepath'/{dirnames}/{pat}.vim
+/// When "flags" has DIP_START: search also from 'start' of 'packpath':
+/// 'packpath'/pack/ * /start/ * /{dirnames}/{pat}.vim
+/// When "flags" has DIP_OPT: search also from 'opt' of 'packpath':
+/// 'packpath'/pack/ * /opt/ * /{dirnames}/{pat}.vim
+/// "dirnames" is an array with one or more directory names.
+static int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file,
+ char *dirnames[])
{
*num_file = 0;
*file = NULL;
@@ -4211,6 +4285,26 @@ static int ExpandRTDir(char_u *pat, int *num_file, char_u ***file, char *dirname
xfree(s);
}
+ if (flags & DIP_START) {
+ for (int i = 0; dirnames[i] != NULL; i++) {
+ size_t size = STRLEN(dirnames[i]) + pat_len + 22;
+ char_u *s = xmalloc(size);
+ snprintf((char *)s, size, "pack/*/start/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ xfree(s);
+ }
+ }
+
+ if (flags & DIP_OPT) {
+ for (int i = 0; dirnames[i] != NULL; i++) {
+ size_t size = STRLEN(dirnames[i]) + pat_len + 20;
+ char_u *s = xmalloc(size);
+ snprintf((char *)s, size, "pack/*/opt/*/%s/%s*.vim", dirnames[i], pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ xfree(s);
+ }
+ }
+
for (int i = 0; i < ga.ga_len; i++) {
char_u *match = ((char_u **)ga.ga_data)[i];
char_u *s = match;
@@ -4240,6 +4334,43 @@ static int ExpandRTDir(char_u *pat, int *num_file, char_u ***file, char *dirname
return OK;
}
+/// Expand loadplugin names:
+/// 'packpath'/pack/ * /opt/{pat}
+static int ExpandPackAddDir(char_u *pat, int *num_file, char_u ***file)
+{
+ garray_T ga;
+
+ *num_file = 0;
+ *file = NULL;
+ size_t pat_len = STRLEN(pat);
+ ga_init(&ga, (int)sizeof(char *), 10);
+
+ size_t buflen = pat_len + 26;
+ char_u *s = xmalloc(buflen);
+ snprintf((char *)s, buflen, "pack/*/opt/%s*", pat); // NOLINT
+ globpath(p_pp, s, &ga, 0);
+ xfree(s);
+
+ for (int i = 0; i < ga.ga_len; i++) {
+ char_u *match = ((char_u **)ga.ga_data)[i];
+ s = path_tail(match);
+ char_u *e = s + STRLEN(s);
+ memmove(match, s, e - s + 1);
+ }
+
+ if (GA_EMPTY(&ga)) {
+ return FAIL;
+ }
+
+ // Sort and remove duplicates which can happen when specifying multiple
+ // directories in dirnames.
+ ga_remove_duplicate_strings(&ga);
+
+ *file = ga.ga_data;
+ *num_file = ga.ga_len;
+ return OK;
+}
+
/// Expand `file` for all comma-separated directories in `path`.
/// Adds matches to `ga`.
@@ -4304,6 +4435,7 @@ static HistoryType hist_char2type(const int c)
case '>': {
return HIST_DEBUG;
}
+ case NUL:
case '/':
case '?': {
return HIST_SEARCH;
@@ -5039,16 +5171,14 @@ static int ex_window(void)
}
cmdwin_type = get_cmdline_type();
- /* Create the command-line buffer empty. */
- (void)do_ecmd(0, NULL, NULL, NULL, ECMD_ONE, ECMD_HIDE, NULL);
- (void)setfname(curbuf, (char_u *)"[Command Line]", NULL, TRUE);
- set_option_value((char_u *)"bt", 0L, (char_u *)"nofile", OPT_LOCAL);
- set_option_value((char_u *)"swf", 0L, NULL, OPT_LOCAL);
- curbuf->b_p_ma = TRUE;
- curwin->w_p_fen = FALSE;
+ // Create empty command-line buffer.
+ buf_open_scratch(0, "[Command Line]");
+ // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer.
+ set_option_value((char_u *)"bh", 0L, (char_u *)"wipe", OPT_LOCAL);
curwin->w_p_rl = cmdmsg_rl;
- cmdmsg_rl = FALSE;
- RESET_BINDING(curwin);
+ cmdmsg_rl = false;
+ curbuf->b_p_ma = true;
+ curwin->w_p_fen = false;
/* Do execute autocommands for setting the filetype (load syntax). */
unblock_autocmds();