To: vim_dev@googlegroups.com Subject: Patch 9.0.1238 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.1238 Problem: :runtime completion can be further improved. Solution: Also complete the {where} argument values and adjust the completion for that. (closes #11874) Files: runtime/doc/builtin.txt, src/cmdexpand.c, src/proto/scriptfile.pro, src/scriptfile.c, src/vim.h, src/testdir/test_packadd.vim *** ../vim-9.0.1237/runtime/doc/builtin.txt 2023-01-22 18:38:45.498261340 +0000 --- runtime/doc/builtin.txt 2023-01-24 12:29:11.366896269 +0000 *************** *** 3528,3534 **** messages |:messages| suboptions option options packadd optional package |pack-add| names ! runtime runtime file names |:runtime| scriptnames sourced script names |:scriptnames| shellcmd Shell command sign |:sign| suboptions --- 3528,3534 ---- messages |:messages| suboptions option options packadd optional package |pack-add| names ! runtime |:runtime| completion scriptnames sourced script names |:scriptnames| shellcmd Shell command sign |:sign| suboptions *** ../vim-9.0.1237/src/cmdexpand.c 2023-01-22 18:38:45.498261340 +0000 --- src/cmdexpand.c 2023-01-24 12:29:11.366896269 +0000 *************** *** 3025,3034 **** return ExpandRTDir(pat, DIP_START + DIP_OPT, numMatches, matches, directories); } - if (xp->xp_context == EXPAND_RUNTIME) - { - return expand_runtime_cmd(pat, numMatches, matches); - } if (xp->xp_context == EXPAND_COMPILER) { char *directories[] = {"compiler", NULL}; --- 3025,3030 ---- *************** *** 3050,3055 **** --- 3046,3053 ---- #endif if (xp->xp_context == EXPAND_PACKADD) return ExpandPackAddDir(pat, numMatches, matches); + if (xp->xp_context == EXPAND_RUNTIME) + return expand_runtime_cmd(pat, numMatches, matches); // When expanding a function name starting with s:, match the nr_ // prefix. *** ../vim-9.0.1237/src/proto/scriptfile.pro 2023-01-22 18:38:45.502261340 +0000 --- src/proto/scriptfile.pro 2023-01-24 12:29:11.366896269 +0000 *************** *** 7,13 **** char_u *estack_sfile(estack_arg_T which); void ex_runtime(exarg_T *eap); void set_context_in_runtime_cmd(expand_T *xp, char_u *arg); - int expand_runtime_cmd(char_u *pat, int *numMatches, char_u ***matches); int find_script_by_name(char_u *name); int get_new_scriptitem_for_fname(int *error, char_u *fname); int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie); --- 7,12 ---- *************** *** 21,26 **** --- 20,26 ---- void ex_packadd(exarg_T *eap); void remove_duplicates(garray_T *gap); int ExpandRTDir(char_u *pat, int flags, int *num_file, char_u ***file, char *dirnames[]); + int expand_runtime_cmd(char_u *pat, int *numMatches, char_u ***matches); int ExpandPackAddDir(char_u *pat, int *num_file, char_u ***file); void ex_source(exarg_T *eap); void ex_options(exarg_T *eap); *** ../vim-9.0.1237/src/scriptfile.c 2023-01-23 20:46:16.162493149 +0000 --- src/scriptfile.c 2023-01-24 12:29:11.366896269 +0000 *************** *** 230,266 **** } /* ! * Get DIP_ flags from the [what] argument of a :runtime command. ! * "*argp" is advanced to after the [what] argument. */ static int ! get_runtime_cmd_flags(char_u **argp) { char_u *arg = *argp; - char_u *p = skiptowhite(arg); - int what_len = (int)(p - arg); ! if (what_len == 0) return 0; ! if (STRNCMP(arg, "START", what_len) == 0) { ! *argp = skipwhite(arg + what_len); return DIP_START + DIP_NORTP; } ! if (STRNCMP(arg, "OPT", what_len) == 0) { ! *argp = skipwhite(arg + what_len); return DIP_OPT + DIP_NORTP; } ! if (STRNCMP(arg, "PACK", what_len) == 0) { ! *argp = skipwhite(arg + what_len); return DIP_START + DIP_OPT + DIP_NORTP; } ! if (STRNCMP(arg, "ALL", what_len) == 0) { ! *argp = skipwhite(arg + what_len); return DIP_START + DIP_OPT; } --- 230,264 ---- } /* ! * Get DIP_ flags from the [where] argument of a :runtime command. ! * "*argp" is advanced to after the [where] argument if it is found. */ static int ! get_runtime_cmd_flags(char_u **argp, size_t where_len) { char_u *arg = *argp; ! if (where_len == 0) return 0; ! if (STRNCMP(arg, "START", where_len) == 0) { ! *argp = skipwhite(arg + where_len); return DIP_START + DIP_NORTP; } ! if (STRNCMP(arg, "OPT", where_len) == 0) { ! *argp = skipwhite(arg + where_len); return DIP_OPT + DIP_NORTP; } ! if (STRNCMP(arg, "PACK", where_len) == 0) { ! *argp = skipwhite(arg + where_len); return DIP_START + DIP_OPT + DIP_NORTP; } ! if (STRNCMP(arg, "ALL", where_len) == 0) { ! *argp = skipwhite(arg + where_len); return DIP_START + DIP_OPT; } *************** *** 268,282 **** } /* ! * ":runtime [what] {name}" */ void ex_runtime(exarg_T *eap) { char_u *arg = eap->arg; int flags = eap->forceit ? DIP_ALL : 0; ! ! flags += get_runtime_cmd_flags(&arg); source_runtime(arg, flags); } --- 266,280 ---- } /* ! * ":runtime [where] {name}" */ void ex_runtime(exarg_T *eap) { char_u *arg = eap->arg; int flags = eap->forceit ? DIP_ALL : 0; ! char_u *p = skiptowhite(arg); ! flags += get_runtime_cmd_flags(&arg, p - arg); source_runtime(arg, flags); } *************** *** 288,309 **** void set_context_in_runtime_cmd(expand_T *xp, char_u *arg) { ! runtime_expand_flags = DIP_KEEPEXT + get_runtime_cmd_flags(&arg); xp->xp_context = EXPAND_RUNTIME; xp->xp_pattern = arg; } - /* - * Handle command line completion for :runtime command. - */ - int - expand_runtime_cmd(char_u *pat, int *numMatches, char_u ***matches) - { - char *directories[] = {"", NULL}; - return ExpandRTDir(pat, runtime_expand_flags, numMatches, matches, - directories); - } - static void source_callback(char_u *fname, void *cookie) { --- 286,298 ---- void set_context_in_runtime_cmd(expand_T *xp, char_u *arg) { ! char_u *p = skiptowhite(arg); ! runtime_expand_flags ! = *p != NUL ? get_runtime_cmd_flags(&arg, p - arg) : 0; xp->xp_context = EXPAND_RUNTIME; xp->xp_pattern = arg; } static void source_callback(char_u *fname, void *cookie) { *************** *** 997,1040 **** } } ! /* ! * Expand runtime file 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. ! */ ! int ! ExpandRTDir( char_u *pat, int flags, ! int *num_file, ! char_u ***file, char *dirnames[]) { ! char_u *s; ! char_u *e; ! char_u *match; ! garray_T ga; ! int i; ! int pat_len; ! ! *num_file = 0; ! *file = NULL; ! pat_len = (int)STRLEN(pat); ! ga_init2(&ga, sizeof(char *), 10); ! ! for (i = 0; dirnames[i] != NULL; ++i) { size_t buf_len = STRLEN(dirnames[i]) + pat_len + 22; char *buf = alloc(buf_len); if (buf == NULL) { ! ga_clear_strings(&ga); ! return FAIL; } char *tail = buf + 15; size_t tail_buflen = buf_len - 15; --- 986,1008 ---- } } ! static void ! ExpandRTDir_int( char_u *pat, + size_t pat_len, int flags, ! int keep_ext, ! garray_T *gap, char *dirnames[]) { ! for (int i = 0; dirnames[i] != NULL; ++i) { size_t buf_len = STRLEN(dirnames[i]) + pat_len + 22; char *buf = alloc(buf_len); if (buf == NULL) { ! ga_clear_strings(gap); ! return; } char *tail = buf + 15; size_t tail_buflen = buf_len - 15; *************** *** 1048,1065 **** expand: if ((flags & DIP_NORTP) == 0) ! globpath(p_rtp, (char_u *)tail, &ga, glob_flags, expand_dirs); if (flags & DIP_START) { memcpy(tail - 15, "pack/*/start/*/", 15); ! globpath(p_pp, (char_u *)tail - 15, &ga, glob_flags, expand_dirs); } if (flags & DIP_OPT) { memcpy(tail - 13, "pack/*/opt/*/", 13); ! globpath(p_pp, (char_u *)tail - 13, &ga, glob_flags, expand_dirs); } if (*(dirnames[i]) == NUL && !expand_dirs) --- 1016,1033 ---- expand: if ((flags & DIP_NORTP) == 0) ! globpath(p_rtp, (char_u *)tail, gap, glob_flags, expand_dirs); if (flags & DIP_START) { memcpy(tail - 15, "pack/*/start/*/", 15); ! globpath(p_pp, (char_u *)tail - 15, gap, glob_flags, expand_dirs); } if (flags & DIP_OPT) { memcpy(tail - 13, "pack/*/opt/*/", 13); ! globpath(p_pp, (char_u *)tail - 13, gap, glob_flags, expand_dirs); } if (*(dirnames[i]) == NUL && !expand_dirs) *************** *** 1075,1091 **** } int pat_pathsep_cnt = 0; ! for (i = 0; i < pat_len; ++i) if (vim_ispathsep(pat[i])) ++pat_pathsep_cnt; ! for (i = 0; i < ga.ga_len; ++i) { ! match = ((char_u **)ga.ga_data)[i]; ! s = match; ! e = s + STRLEN(s); ! if (e - 4 > s && (flags & DIP_KEEPEXT) == 0 ! && STRNICMP(e - 4, ".vim", 4) == 0) { e -= 4; *e = NUL; --- 1043,1058 ---- } int pat_pathsep_cnt = 0; ! for (size_t i = 0; i < pat_len; ++i) if (vim_ispathsep(pat[i])) ++pat_pathsep_cnt; ! for (int i = 0; i < gap->ga_len; ++i) { ! char_u *match = ((char_u **)gap->ga_data)[i]; ! char_u *s = match; ! char_u *e = s + STRLEN(s); ! if (e - 4 > s && !keep_ext && STRNICMP(e - 4, ".vim", 4) == 0) { e -= 4; *e = NUL; *************** *** 1097,1118 **** && ++match_pathsep_cnt > pat_pathsep_cnt)) break; ++s; ! *e = NUL; ! mch_memmove(match, s, e - s + 1); } ! if (ga.ga_len == 0) ! return FAIL; // Sort and remove duplicates which can happen when specifying multiple // directories in dirnames. ! remove_duplicates(&ga); *file = ga.ga_data; *num_file = ga.ga_len; return OK; } /* * Expand loadplugin names: * 'packpath'/pack/ * /opt/{pat} --- 1064,1152 ---- && ++match_pathsep_cnt > pat_pathsep_cnt)) break; ++s; ! if (s != match) ! mch_memmove(match, s, e - s + 1); } ! if (gap->ga_len == 0) ! return; // Sort and remove duplicates which can happen when specifying multiple // directories in dirnames. ! remove_duplicates(gap); ! } ! ! /* ! * Expand runtime file 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. ! */ ! int ! ExpandRTDir( ! char_u *pat, ! int flags, ! int *num_file, ! char_u ***file, ! char *dirnames[]) ! { ! *num_file = 0; ! *file = NULL; ! ! garray_T ga; ! ga_init2(&ga, sizeof(char *), 10); ! ! ExpandRTDir_int(pat, STRLEN(pat), flags, FALSE, &ga, dirnames); ! ! if (ga.ga_len == 0) ! return FAIL; *file = ga.ga_data; *num_file = ga.ga_len; return OK; } + /* + * Handle command line completion for :runtime command. + */ + int + expand_runtime_cmd(char_u *pat, int *numMatches, char_u ***matches) + { + *numMatches = 0; + *matches = NULL; + + garray_T ga; + ga_init2(&ga, sizeof(char *), 10); + + size_t pat_len = (int)STRLEN(pat); + char *dirnames[] = {"", NULL}; + ExpandRTDir_int(pat, pat_len, runtime_expand_flags, TRUE, &ga, dirnames); + + // Try to complete values for [where] argument when none was found. + if (runtime_expand_flags == 0) + { + char *where_values[] = {"START", "OPT", "PACK", "ALL"}; + for (size_t i = 0; i < ARRAY_LENGTH(where_values); ++i) + if (STRNCMP(pat, where_values[i], pat_len) == 0) + { + char_u *p = vim_strsave((char_u *)where_values[i]); + if (p != NULL && ga_add_string(&ga, p) == FAIL) + vim_free(p); + } + } + + if (ga.ga_len == 0) + return FAIL; + + *matches = ga.ga_data; + *numMatches = ga.ga_len; + return OK; + } + /* * Expand loadplugin names: * 'packpath'/pack/ * /opt/{pat} *** ../vim-9.0.1237/src/vim.h 2023-01-22 21:14:32.621863614 +0000 --- src/vim.h 2023-01-24 12:29:11.366896269 +0000 *************** *** 2672,2678 **** #define DIP_NORTP 0x20 // do not use 'runtimepath' #define DIP_NOAFTER 0x40 // skip "after" directories #define DIP_AFTER 0x80 // only use "after" directories - #define DIP_KEEPEXT 0x100 // for completion: include file extension // Lowest number used for window ID. Cannot have this many windows. #define LOWEST_WIN_ID 1000 --- 2672,2677 ---- *** ../vim-9.0.1237/src/testdir/test_packadd.vim 2023-01-22 18:38:45.502261340 +0000 --- src/testdir/test_packadd.vim 2023-01-24 12:29:11.366896269 +0000 *************** *** 370,432 **** endfunc func Test_runtime_completion() ! let rundir = &packpath . '/runtime/Xextra' ! let startdir = &packpath . '/pack/mine/start/foo/Xextra' ! let optdir = &packpath . '/pack/mine/opt/bar/Xextra' ! call mkdir(rundir . '/Xrunbaz', 'p') ! call mkdir(startdir . '/Xstartbaz', 'p') ! call mkdir(optdir . '/Xoptbaz', 'p') ! call writefile([], rundir . '/../Xrunfoo.vim') ! call writefile([], rundir . '/Xrunbar.vim') ! call writefile([], rundir . '/Xunrelated') ! call writefile([], rundir . '/../Xunrelated') ! call writefile([], startdir . '/../Xstartfoo.vim') ! call writefile([], startdir . '/Xstartbar.vim') ! call writefile([], startdir . '/Xunrelated') ! call writefile([], startdir . '/../Xunrelated') ! call writefile([], optdir . '/../Xoptfoo.vim') ! call writefile([], optdir . '/Xoptbar.vim') ! call writefile([], optdir . '/Xunrelated') ! call writefile([], optdir . '/../Xunrelated') exe 'set rtp=' . &packpath . '/runtime' func Check_runtime_completion(arg, arg1, res) call feedkeys(':runtime ' .. a:arg .. "\\\"\", 'xt') call assert_equal('"runtime ' .. a:arg1 .. join(a:res), @:) call assert_equal(a:res, getcompletion(a:arg, 'runtime')) - - call feedkeys(':runtime ' .. a:arg .. "X\\\"\", 'xt') - call assert_equal('"runtime ' .. a:arg1 .. join(a:res), @:) - call assert_equal(a:res, getcompletion(a:arg .. 'X', 'runtime')) endfunc call Check_runtime_completion('', '', ! \ ['Xextra/', 'Xrunfoo.vim']) ! call Check_runtime_completion('Xextra/', '', ! \ ['Xextra/Xrunbar.vim', 'Xextra/Xrunbaz/']) call Check_runtime_completion('START ', 'START ', ! \ ['Xextra/', 'Xstartfoo.vim']) ! call Check_runtime_completion('START Xextra/', 'START ', ! \ ['Xextra/Xstartbar.vim', 'Xextra/Xstartbaz/']) call Check_runtime_completion('OPT ', 'OPT ', ! \ ['Xextra/', 'Xoptfoo.vim']) ! call Check_runtime_completion('OPT Xextra/', 'OPT ', ! \ ['Xextra/Xoptbar.vim', 'Xextra/Xoptbaz/']) call Check_runtime_completion('PACK ', 'PACK ', ! \ ['Xextra/', 'Xoptfoo.vim', 'Xstartfoo.vim']) ! call Check_runtime_completion('PACK Xextra/', 'PACK ', ! \ ['Xextra/Xoptbar.vim', 'Xextra/Xoptbaz/', ! \ 'Xextra/Xstartbar.vim', 'Xextra/Xstartbaz/']) call Check_runtime_completion('ALL ', 'ALL ', ! \ ['Xextra/', 'Xoptfoo.vim', 'Xrunfoo.vim', 'Xstartfoo.vim']) ! call Check_runtime_completion('ALL Xextra/', 'ALL ', ! \ ['Xextra/Xoptbar.vim', 'Xextra/Xoptbaz/', ! \ 'Xextra/Xrunbar.vim', 'Xextra/Xrunbaz/', ! \ 'Xextra/Xstartbar.vim', 'Xextra/Xstartbaz/']) delfunc Check_runtime_completion endfunc --- 370,444 ---- endfunc func Test_runtime_completion() ! let rundir = &packpath . '/runtime/Aextra' ! let startdir = &packpath . '/pack/mine/start/foo/Aextra' ! let optdir = &packpath . '/pack/mine/opt/bar/Aextra' ! call mkdir(rundir . '/Arunbaz', 'p') ! call mkdir(startdir . '/Astartbaz', 'p') ! call mkdir(optdir . '/Aoptbaz', 'p') ! call writefile([], rundir . '/../Arunfoo.vim') ! call writefile([], rundir . '/Arunbar.vim') ! call writefile([], rundir . '/Aunrelated') ! call writefile([], rundir . '/../Aunrelated') ! call writefile([], startdir . '/../Astartfoo.vim') ! call writefile([], startdir . '/Astartbar.vim') ! call writefile([], startdir . '/Aunrelated') ! call writefile([], startdir . '/../Aunrelated') ! call writefile([], optdir . '/../Aoptfoo.vim') ! call writefile([], optdir . '/Aoptbar.vim') ! call writefile([], optdir . '/Aunrelated') ! call writefile([], optdir . '/../Aunrelated') exe 'set rtp=' . &packpath . '/runtime' func Check_runtime_completion(arg, arg1, res) call feedkeys(':runtime ' .. a:arg .. "\\\"\", 'xt') call assert_equal('"runtime ' .. a:arg1 .. join(a:res), @:) call assert_equal(a:res, getcompletion(a:arg, 'runtime')) endfunc call Check_runtime_completion('', '', ! \ ['Aextra/', 'Arunfoo.vim', 'START', 'OPT', 'PACK', 'ALL']) ! call Check_runtime_completion('S', '', ! \ ['START']) ! call Check_runtime_completion('O', '', ! \ ['OPT']) ! call Check_runtime_completion('P', '', ! \ ['PACK']) ! call Check_runtime_completion('A', '', ! \ ['Aextra/', 'Arunfoo.vim', 'ALL']) ! call Check_runtime_completion('Aextra/', '', ! \ ['Aextra/Arunbar.vim', 'Aextra/Arunbaz/']) call Check_runtime_completion('START ', 'START ', ! \ ['Aextra/', 'Astartfoo.vim']) ! call Check_runtime_completion('START A', 'START ', ! \ ['Aextra/', 'Astartfoo.vim']) ! call Check_runtime_completion('START Aextra/', 'START ', ! \ ['Aextra/Astartbar.vim', 'Aextra/Astartbaz/']) call Check_runtime_completion('OPT ', 'OPT ', ! \ ['Aextra/', 'Aoptfoo.vim']) ! call Check_runtime_completion('OPT A', 'OPT ', ! \ ['Aextra/', 'Aoptfoo.vim']) ! call Check_runtime_completion('OPT Aextra/', 'OPT ', ! \ ['Aextra/Aoptbar.vim', 'Aextra/Aoptbaz/']) call Check_runtime_completion('PACK ', 'PACK ', ! \ ['Aextra/', 'Aoptfoo.vim', 'Astartfoo.vim']) ! call Check_runtime_completion('PACK A', 'PACK ', ! \ ['Aextra/', 'Aoptfoo.vim', 'Astartfoo.vim']) ! call Check_runtime_completion('PACK Aextra/', 'PACK ', ! \ ['Aextra/Aoptbar.vim', 'Aextra/Aoptbaz/', ! \ 'Aextra/Astartbar.vim', 'Aextra/Astartbaz/']) call Check_runtime_completion('ALL ', 'ALL ', ! \ ['Aextra/', 'Aoptfoo.vim', 'Arunfoo.vim', 'Astartfoo.vim']) ! call Check_runtime_completion('ALL A', 'ALL ', ! \ ['Aextra/', 'Aoptfoo.vim', 'Arunfoo.vim', 'Astartfoo.vim']) ! call Check_runtime_completion('ALL Aextra/', 'ALL ', ! \ ['Aextra/Aoptbar.vim', 'Aextra/Aoptbaz/', ! \ 'Aextra/Arunbar.vim', 'Aextra/Arunbaz/', ! \ 'Aextra/Astartbar.vim', 'Aextra/Astartbaz/']) delfunc Check_runtime_completion endfunc *** ../vim-9.0.1237/src/version.c 2023-01-23 20:46:16.170493148 +0000 --- src/version.c 2023-01-24 12:31:06.286854347 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 1238, /**/ -- Don't believe everything you hear or anything you say. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///