diff options
25 files changed, 590 insertions, 277 deletions
diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim index f875c8b797..bd99a0e104 100644 --- a/runtime/autoload/health.vim +++ b/runtime/autoload/health.vim @@ -90,7 +90,7 @@ endfunction  " Changes ':h clipboard' to ':help |clipboard|'.  function! s:help_to_link(s) abort -  return substitute(a:s, '\v:h%[elp] ([^|][^"\r\n]+)', ':help |\1|', 'g') +  return substitute(a:s, '\v:h%[elp] ([^|][^"\r\n ]+)', ':help |\1|', 'g')  endfunction  " Format a message for a specific report item diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index ec20615f69..26db5b77b7 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -121,14 +121,14 @@ function! s:check_clipboard() abort    call health#report_start('Clipboard (optional)')    let clipboard_tool = provider#clipboard#Executable() -  if empty(clipboard_tool) +  if exists('g:clipboard') && empty(clipboard_tool) +    call health#report_error( +          \ provider#clipboard#Error(), +          \ ["Use the example in :help g:clipboard as a template, or don't set g:clipboard at all."]) +  elseif empty(clipboard_tool)      call health#report_warn( -          \ 'No clipboard tool found. Clipboard registers will not work.', +          \ 'No clipboard tool found. Clipboard registers (`"+` and `"*`) will not work.',            \ [':help clipboard']) -  elseif exists('g:clipboard') && (type({}) != type(g:clipboard) -        \ || !has_key(g:clipboard, 'copy') || !has_key(g:clipboard, 'paste')) -    call health#report_error( -          \ 'g:clipboard exists but is malformed. It must be a dictionary with the keys documented at :help g:clipboard')    else      call health#report_ok('Clipboard tool found: '. clipboard_tool)    endif diff --git a/runtime/autoload/provider.vim b/runtime/autoload/provider.vim index b46ae12b3c..7439b638c2 100644 --- a/runtime/autoload/provider.vim +++ b/runtime/autoload/provider.vim @@ -10,7 +10,9 @@ function! provider#stderr_collector(chan_id, data, event) dict  endfunction  function! provider#clear_stderr(chan_id) -   silent! call remove(s:stderr, a:chan_id) +  if has_key(s:stderr, a:chan_id) +    call remove(s:stderr, a:chan_id) +  endif  endfunction  function! provider#get_stderr(chan_id) diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 8eb694e9fa..6454a01c2a 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -3,6 +3,7 @@  " available.  let s:copy = {}  let s:paste = {} +let s:clipboard = {}  " When caching is enabled, store the jobid of the xclip/xsel process keeping  " ownership of the selection, so we know how long the cache is valid. @@ -23,7 +24,7 @@ function! s:selection.on_exit(jobid, data, event) abort    call provider#clear_stderr(a:jobid)  endfunction -let s:selections = { '*': s:selection, '+': copy(s:selection)} +let s:selections = { '*': s:selection, '+': copy(s:selection) }  function! s:try_cmd(cmd, ...) abort    let argv = split(a:cmd, " ") @@ -31,7 +32,7 @@ function! s:try_cmd(cmd, ...) abort    if v:shell_error      if !exists('s:did_error_try_cmd')        echohl WarningMsg -      echomsg "clipboard: error: ".(len(out) ? out[0] : '') +      echomsg "clipboard: error: ".(len(out) ? out[0] : v:shell_error)        echohl None        let s:did_error_try_cmd = 1      endif @@ -55,9 +56,15 @@ endfunction  function! provider#clipboard#Executable() abort    if exists('g:clipboard') +    if type({}) isnot# type(g:clipboard) +          \ || type({}) isnot# type(get(g:clipboard, 'copy', v:null)) +          \ || type({}) isnot# type(get(g:clipboard, 'paste', v:null)) +      let s:err = 'clipboard: invalid g:clipboard' +      return '' +    endif      let s:copy = get(g:clipboard, 'copy', { '+': v:null, '*': v:null })      let s:paste = get(g:clipboard, 'paste', { '+': v:null, '*': v:null }) -    let s:cache_enabled = get(g:clipboard, 'cache_enabled', 1) +    let s:cache_enabled = get(g:clipboard, 'cache_enabled', 0)      return get(g:clipboard, 'name', 'g:clipboard')    elseif has('mac') && executable('pbcopy')      let s:copy['+'] = 'pbcopy' @@ -104,16 +111,17 @@ function! provider#clipboard#Executable() abort      return 'tmux'    endif -  let s:err = 'clipboard: No clipboard tool available. :help clipboard' +  let s:err = 'clipboard: No clipboard tool. :help clipboard'    return ''  endfunction  if empty(provider#clipboard#Executable()) +  " provider#clipboard#Call() *must not* be defined if the provider is broken. +  " Otherwise eval_has_provider() thinks the clipboard provider is +  " functioning, and eval_call_provider() will happily call it.    finish  endif -let s:clipboard = {} -  function! s:clipboard.get(reg) abort    if s:selections[a:reg].owner > 0      return s:selections[a:reg].data @@ -154,9 +162,19 @@ function! s:clipboard.set(lines, regtype, reg) abort      echohl WarningMsg      echomsg 'clipboard: failed to execute: '.(s:copy[a:reg])      echohl None +    return 0    endif +  return 1  endfunction  function! provider#clipboard#Call(method, args) abort -  return call(s:clipboard[a:method],a:args,s:clipboard) +  if get(s:, 'here', v:false)  " Clipboard provider must not recurse. #7184 +    return 0 +  endif +  let s:here = v:true +  try +    return call(s:clipboard[a:method],a:args,s:clipboard) +  finally +    let s:here = v:false +  endtry  endfunction diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index f06c062ee3..223a0135b2 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -212,7 +212,6 @@ g8			Print the hex values of the bytes used in the  			Equivalent to: >  			      :enew  			      :call termopen('{cmd}') -			      :startinsert  <  			See |termopen()|. diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 688912eda6..e2f1f16635 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -10,6 +10,11 @@ if(USE_GCOV)  endif()  endif() +if(WIN32) +  # tell MinGW compiler to enable wmain +  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode") +endif() +  set(TOUCHES_DIR ${PROJECT_BINARY_DIR}/touches)  set(GENERATOR_DIR ${CMAKE_CURRENT_LIST_DIR}/generators)  set(GENERATED_DIR ${PROJECT_BINARY_DIR}/src/nvim/auto) diff --git a/src/nvim/eval.c b/src/nvim/eval.c index ac22d75a83..d6ee13857a 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -22775,7 +22775,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments)  bool eval_has_provider(const char *name)  { -#define check_provider(name) \ +#define CHECK_PROVIDER(name) \    if (has_##name == -1) { \      has_##name = !!find_func((char_u *)"provider#" #name "#Call"); \      if (!has_##name) { \ @@ -22791,17 +22791,17 @@ bool eval_has_provider(const char *name)    static int has_python3 = -1;    static int has_ruby = -1; -  if (!strcmp(name, "clipboard")) { -    check_provider(clipboard); +  if (strequal(name, "clipboard")) { +    CHECK_PROVIDER(clipboard);      return has_clipboard; -  } else if (!strcmp(name, "python3")) { -    check_provider(python3); +  } else if (strequal(name, "python3")) { +    CHECK_PROVIDER(python3);      return has_python3; -  } else if (!strcmp(name, "python")) { -    check_provider(python); +  } else if (strequal(name, "python")) { +    CHECK_PROVIDER(python);      return has_python; -  } else if (!strcmp(name, "ruby")) { -    check_provider(ruby); +  } else if (strequal(name, "ruby")) { +    CHECK_PROVIDER(ruby);      return has_ruby;    } diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 33fe30cd5a..371f7b3bce 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -2319,16 +2319,6 @@ static void source_callback(char_u *fname, void *cookie)    (void)do_source(fname, false, DOSO_NONE);  } -/// Source the file "name" from all directories in 'runtimepath'. -/// "name" can contain wildcards. -/// When "flags" has DIP_ALL: source all files, otherwise only the first one. -/// -/// return FAIL when no file could be sourced, OK otherwise. -int source_runtime(char_u *name, int flags) -{ -  return do_in_runtimepath(name, flags, source_callback, NULL); -} -  /// Find the file "name" in all directories in "path" and invoke  /// "callback(fname, cookie)".  /// "name" can contain wildcards. @@ -2434,21 +2424,21 @@ int do_in_path(char_u *path, char_u *name, int flags,    return did_one ? OK : FAIL;  } -/// Find "name" in 'runtimepath'.  When found, invoke the callback function for +/// Find "name" in "path".  When found, invoke the callback function for  /// it: callback(fname, "cookie")  /// When "flags" has DIP_ALL repeat for all matches, otherwise only the first  /// one is used.  /// Returns OK when at least one match found, FAIL otherwise. -/// If "name" is NULL calls callback for each entry in runtimepath. Cookie is +/// If "name" is NULL calls callback for each entry in "path". Cookie is  /// passed by reference in this case, setting it to NULL indicates that callback  /// has done its job. -int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, -                      void *cookie) +int do_in_path_and_pp(char_u *path, char_u *name, int flags, +                      DoInRuntimepathCB callback, void *cookie)  {    int done = FAIL;    if ((flags & DIP_NORTP) == 0) { -    done = do_in_path(p_rtp, name, flags, callback, cookie); +    done = do_in_path(path, name, flags, callback, cookie);    }    if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) { @@ -2476,6 +2466,29 @@ int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback,    return done;  } +/// Just like do_in_path_and_pp(), using 'runtimepath' for "path". +int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, +                      void *cookie) +{ +  return do_in_path_and_pp(p_rtp, name, flags, callback, cookie); +} + +/// Source the file "name" from all directories in 'runtimepath'. +/// "name" can contain wildcards. +/// When "flags" has DIP_ALL: source all files, otherwise only the first one. +/// +/// return FAIL when no file could be sourced, OK otherwise. +int source_runtime(char_u *name, int flags) +{ +  return source_in_path(p_rtp, name, flags); +} + +/// Just like source_runtime(), but use "path" instead of 'runtimepath'. +int source_in_path(char_u *path, char_u *name, int flags) +{ +  return do_in_path_and_pp(path, name, flags, source_callback, NULL); +} +  // Expand wildcards in "pat" and invoke do_source() for each match.  static void source_all_matches(char_u *pat)  { @@ -2498,6 +2511,7 @@ static int APP_BOTH;  static void add_pack_plugin(char_u *fname, void *cookie)  {    char_u *p4, *p3, *p2, *p1, *p; +  char_u *buf = NULL;    char *const ffname = fix_fname((char *)fname); @@ -2525,26 +2539,30 @@ static void add_pack_plugin(char_u *fname, void *cookie)      // Find "ffname" in "p_rtp", ignoring '/' vs '\' differences      size_t fname_len = strlen(ffname);      const char *insp = (const char *)p_rtp; -    for (;;) { -      if (path_fnamencmp(insp, ffname, fname_len) == 0) { -        break; +    buf = try_malloc(MAXPATHL); +    if (buf == NULL) { +      goto theend; +    } +    while (*insp != NUL) { +      copy_option_part((char_u **)&insp, buf, MAXPATHL, ","); +      add_pathsep((char *)buf); +      char *const rtp_ffname = fix_fname((char *)buf); +      if (rtp_ffname == NULL) { +        goto theend;        } -      insp = strchr(insp, ','); -      if (insp == NULL) { +      bool match = path_fnamencmp(rtp_ffname, ffname, fname_len) == 0; +      xfree(rtp_ffname); +      if (match) {          break;        } -      insp++;      } -    if (insp == NULL) { +    if (*insp == NUL) {        // not found, append at the end        insp = (const char *)p_rtp + STRLEN(p_rtp);      } else {        // append after the matching directory. -      insp += strlen(ffname); -      while (*insp != NUL && *insp != ',') { -        insp++; -      } +      insp--;      }      *p4 = c; @@ -2614,26 +2632,35 @@ static void add_pack_plugin(char_u *fname, void *cookie)    }  theend: +  xfree(buf);    xfree(ffname);  } -static bool did_source_packages = false; +/// Add all packages in the "start" directory to 'runtimepath'. +void add_pack_start_dirs(void) +{ +  do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,  // NOLINT +             add_pack_plugin, &APP_ADD_DIR); +} + +/// Load plugins from all packages in the "start" directory. +void load_start_packages(void) +{ +  did_source_packages = true; +  do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,  // NOLINT +             add_pack_plugin, &APP_LOAD); +}  // ":packloadall"  // Find plugins in the package directories and source them. -// "eap" is NULL when invoked during startup.  void ex_packloadall(exarg_T *eap)  { -  if (!did_source_packages || (eap != NULL && eap->forceit)) { -    did_source_packages = true; - +  if (!did_source_packages || eap->forceit) {      // First do a round to add all directories to 'runtimepath', then load      // the plugins. This allows for plugins to use an autoload directory      // of another plugin. -    do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,  // NOLINT -               add_pack_plugin, &APP_ADD_DIR); -    do_in_path(p_pp, (char_u *)"pack/*/start/*", DIP_ALL + DIP_DIR,  // NOLINT -               add_pack_plugin, &APP_LOAD); +    add_pack_start_dirs(); +    load_start_packages();    }  } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6e7938046a..d1405978b3 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -9835,7 +9835,7 @@ static void ex_terminal(exarg_T *eap)    if (*eap->arg != NUL) {  // Run {cmd} in 'shell'.      char *name = (char *)vim_strsave_escaped(eap->arg, (char_u *)"\"\\");      snprintf(ex_cmd, sizeof(ex_cmd), -             ":enew%s | call termopen(\"%s\") | startinsert", +             ":enew%s | call termopen(\"%s\")",               eap->forceit ? "!" : "", name);      xfree(name);    } else {  // No {cmd}: run the job with tokenized 'shell'. @@ -9857,7 +9857,7 @@ static void ex_terminal(exarg_T *eap)      shell_free_argv(argv);      snprintf(ex_cmd, sizeof(ex_cmd), -             ":enew%s | call termopen([%s]) | startinsert", +             ":enew%s | call termopen([%s])",               eap->forceit ? "!" : "", shell_argv + 1);    } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 13ecafcbe3..2ee72cdb6a 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -407,6 +407,9 @@ EXTERN int garbage_collect_at_exit INIT(= FALSE);  /* ID of script being sourced or was sourced to define the current function. */  EXTERN scid_T current_SID INIT(= 0); + +EXTERN bool did_source_packages INIT(= false); +  // Scope information for the code that indirectly triggered the current  // provider function call  EXTERN struct caller_scope { diff --git a/src/nvim/main.c b/src/nvim/main.c index a665ad1de2..024c56dd05 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -7,6 +7,11 @@  #include <string.h>  #include <stdbool.h> +#ifdef WIN32 +# include <wchar.h> +# include <winnls.h> +#endif +  #include <msgpack.h>  #include "nvim/ascii.h" @@ -215,10 +220,22 @@ void early_init(void)  #ifdef MAKE_LIB  int nvim_main(int argc, char **argv) +#elif defined(WIN32) +int wmain(int argc, wchar_t **argv_w)  // multibyte args on Windows. #7060  #else  int main(int argc, char **argv)  #endif  { +#if defined(WIN32) && !defined(MAKE_LIB) +  char *argv[argc]; +  for (int i = 0; i < argc; i++) { +    char *buf = NULL; +    utf16_to_utf8(argv_w[i], &buf); +    assert(buf); +    argv[i] = buf; +  } +#endif +    argv0 = argv[0];    char_u *fname = NULL;   // file name from command line @@ -1291,10 +1308,29 @@ static void set_window_layout(mparm_T *paramp)  static void load_plugins(void)  {    if (p_lpl) { -    source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_NOAFTER);  // NOLINT +    char_u *rtp_copy = NULL; + +    // First add all package directories to 'runtimepath', so that their +    // autoload directories can be found.  Only if not done already with a +    // :packloadall command. +    // Make a copy of 'runtimepath', so that source_runtime does not use the +    // pack directories. +    if (!did_source_packages) { +      rtp_copy = vim_strsave(p_rtp); +      add_pack_start_dirs(); +    } + +    source_in_path(rtp_copy == NULL ? p_rtp : rtp_copy, +                   (char_u *)"plugin/**/*.vim",  // NOLINT +                   DIP_ALL | DIP_NOAFTER);      TIME_MSG("loading plugins"); +    xfree(rtp_copy); -    ex_packloadall(NULL); +    // Only source "start" packages if not done already with a :packloadall +    // command. +    if (!did_source_packages) { +      load_start_packages(); +    }      TIME_MSG("loading packages");      source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_AFTER); diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 5c6f4d0d07..c01840cfd0 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -55,12 +55,11 @@ static yankreg_T y_regs[NUM_REGISTERS];  static yankreg_T *y_previous = NULL; /* ptr to last written yankreg */ -static bool clipboard_didwarn_unnamed = false; -  // for behavior between start_batch_changes() and end_batch_changes()) -static bool clipboard_delay_update = false;  // delay clipboard update  static int batch_change_count = 0;           // inside a script +static bool clipboard_delay_update = false;  // delay clipboard update  static bool clipboard_needs_update = false;  // clipboard was updated +static bool clipboard_didwarn = false;  /*   * structure used by block_prep, op_delete and op_yank for blockwise operators @@ -5524,7 +5523,7 @@ int get_default_register_name(void)  }  /// Determine if register `*name` should be used as a clipboard. -/// In an unnammed operation, `*name` is `NUL` and will be adjusted to `'*'/'+'` if +/// In an unnamed operation, `*name` is `NUL` and will be adjusted to */+ if  /// `clipboard=unnamed[plus]` is set.  ///  /// @param name The name of register, or `NUL` if unnamed. @@ -5535,33 +5534,41 @@ int get_default_register_name(void)  /// if the register isn't a clipboard or provider isn't available.  static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)  { -  if (*name == '*' || *name == '+') { -    if(!eval_has_provider("clipboard")) { -      if (!quiet) { -        EMSG("clipboard: No provider. Try \":CheckHealth\" or " -             "\":h clipboard\"."); -      } -      return NULL; -    } -    return &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER]; -  } else if ((*name == NUL) && (cb_flags & CB_UNNAMEDMASK)) { -    if(!eval_has_provider("clipboard")) { -      if (!quiet && !clipboard_didwarn_unnamed) { -        msg((char_u *)"clipboard: No provider. Try \":CheckHealth\" or " -            "\":h clipboard\"."); -        clipboard_didwarn_unnamed = true; -      } -      return NULL; +#define MSG_NO_CLIP "clipboard: No provider. " \ +  "Try \":CheckHealth\" or \":h clipboard\"." + +  yankreg_T *target = NULL; +  bool explicit_cb_reg = (*name == '*' || *name == '+'); +  bool implicit_cb_reg = (*name == NUL) && (cb_flags & CB_UNNAMEDMASK); +  if (!explicit_cb_reg && !implicit_cb_reg) { +    goto end; +  } + +  if (!eval_has_provider("clipboard")) { +    if (batch_change_count == 1 && !quiet +        && (!clipboard_didwarn || (explicit_cb_reg && !redirecting()))) { +      clipboard_didwarn = true; +      // Do NOT error (emsg()) here--if it interrupts :redir we get into +      // a weird state, stuck in "redirect mode". +      msg((char_u *)MSG_NO_CLIP);      } +    // ... else, be silent (don't flood during :while, :redir, etc.). +    goto end; +  } + +  if (explicit_cb_reg) { +    target = &y_regs[*name == '*' ? STAR_REGISTER : PLUS_REGISTER]; +    goto end; +  } else {  // unnamed register: "implicit" clipboard      if (writing && clipboard_delay_update) { +      // For "set" (copy), defer the clipboard call.        clipboard_needs_update = true; -      return NULL; +      goto end;      } else if (!writing && clipboard_needs_update) { -      // use the internal value -      return NULL; +      // For "get" (paste), use the internal value. +      goto end;      } -    yankreg_T *target;      if (cb_flags & CB_UNNAMEDPLUS) {        *name = (cb_flags & CB_UNNAMED && writing) ? '"': '+';        target = &y_regs[PLUS_REGISTER]; @@ -5569,10 +5576,11 @@ static yankreg_T *adjust_clipboard_name(int *name, bool quiet, bool writing)        *name = '*';        target = &y_regs[STAR_REGISTER];      } -    return target; // unnamed register +    goto end;    } -  // don't do anything for other register names -  return NULL; + +end: +  return target;  }  static bool get_clipboard(int name, yankreg_T **target, bool quiet) @@ -5740,7 +5748,7 @@ static void set_clipboard(int name, yankreg_T *reg)    (void)eval_call_provider("clipboard", "set", args);  } -/// Avoid clipboard (slow) during batch operations (i.e., a script). +/// Avoid slow things (clipboard) during batch operations (while/for-loops).  void start_batch_changes(void)  {    if (++batch_change_count > 1) { @@ -5750,7 +5758,7 @@ void start_batch_changes(void)    clipboard_needs_update = false;  } -/// Update the clipboard after batch changes finished. +/// Counterpart to start_batch_changes().  void end_batch_changes(void)  {    if (--batch_change_count > 0) { @@ -5759,6 +5767,7 @@ void end_batch_changes(void)    }    clipboard_delay_update = false;    if (clipboard_needs_update) { +    // unnamed ("implicit" clipboard)      set_clipboard(NUL, y_previous);      clipboard_needs_update = false;    } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index f0171fa525..65c0e2464a 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -81,7 +81,10 @@ struct hl_group {  // highlight groups for 'highlight' option  static garray_T highlight_ga = GA_EMPTY_INIT_VALUE; -#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data))) +static inline struct hl_group * HL_TABLE(void) +{ +  return ((struct hl_group *)((highlight_ga.ga_data))); +}  #define MAX_HL_ID       20000   /* maximum value for a highlight ID. */ @@ -100,10 +103,8 @@ static int include_none = 0;    /* when 1 include "nvim/None" */  static int include_default = 0; /* when 1 include "nvim/default" */  static int include_link = 0;    /* when 2 include "nvim/link" and "clear" */ -/* - * The "term", "cterm" and "gui" arguments can be any combination of the - * following names, separated by commas (but no spaces!). - */ +/// The "term", "cterm" and "gui" arguments can be any combination of the +/// following names, separated by commas (but no spaces!).  static char *(hl_name_table[]) =  {"bold", "standout", "underline", "undercurl",   "italic", "reverse", "inverse", "NONE"}; @@ -1775,8 +1776,9 @@ syn_current_attr (                    cur_si->si_trans_id = CUR_STATE(                        current_state.ga_len - 2).si_trans_id;                  } -              } else +              } else {                  cur_si->si_attr = syn_id2attr(syn_id); +              }                cur_si->si_cont_list = NULL;                cur_si->si_next_list = next_list;                check_keepend(); @@ -5252,12 +5254,10 @@ get_id_list (          /*           * Handle full group name.           */ -        if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL) +        if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL) {            id = syn_check_group(name + 1, (int)(end - p)); -        else { -          /* -           * Handle match of regexp with group names. -           */ +        } else { +          // Handle match of regexp with group names.            *name = '^';            STRCAT(name, "$");            regmatch.regprog = vim_regcomp(name, RE_MAGIC); @@ -5958,6 +5958,7 @@ static char *highlight_init_light[] =    "Title        ctermfg=DarkMagenta gui=bold guifg=Magenta",    "Visual       guibg=LightGrey",    "WarningMsg   ctermfg=DarkRed guifg=Red", +  "Normal       gui=NONE",    NULL  }; @@ -5991,23 +5992,25 @@ static char *highlight_init_dark[] =    "Title        ctermfg=LightMagenta gui=bold guifg=Magenta",    "Visual       guibg=DarkGrey",    "WarningMsg   ctermfg=LightRed guifg=Red", +  "Normal       gui=NONE",    NULL  }; -void  -init_highlight ( -    int both,                   /* include groups where 'bg' doesn't matter */ -    int reset                  /* clear group first */ -) + +/// Load colors from a file if "g:colors_name" is set, otherwise load builtin +/// colors +/// +/// @param both include groups where 'bg' doesn't matter +/// @param reset clear groups first +void +init_highlight(int both, int reset)  {    int i;    char        **pp;    static int had_both = FALSE; -  /* -   * Try finding the color scheme file.  Used when a color file was loaded -   * and 'background' or 't_Co' is changed. -   */ +  // Try finding the color scheme file.  Used when a color file was loaded +  // and 'background' or 't_Co' is changed.    char_u *p = get_var_value("g:colors_name");    if (p != NULL) {      // Value of g:colors_name could be freed in load_colors() and make @@ -6026,33 +6029,34 @@ init_highlight (    if (both) {      had_both = TRUE;      pp = highlight_init_both; -    for (i = 0; pp[i] != NULL; ++i) -      do_highlight((char_u *)pp[i], reset, TRUE); -  } else if (!had_both) -    /* Don't do anything before the call with both == TRUE from main(). -     * Not everything has been setup then, and that call will overrule -     * everything anyway. */ +    for (i = 0; pp[i] != NULL; i++) { +      do_highlight((char_u *)pp[i], reset, true); +    } +  } else if (!had_both) { +    // Don't do anything before the call with both == TRUE from main(). +    // Not everything has been setup then, and that call will overrule +    // everything anyway.      return; +  } -  if (*p_bg == 'l') -    pp = highlight_init_light; -  else -    pp = highlight_init_dark; -  for (i = 0; pp[i] != NULL; ++i) -    do_highlight((char_u *)pp[i], reset, TRUE); +  pp = (*p_bg == 'l') ?  highlight_init_light : highlight_init_dark; + +  for (i = 0; pp[i] != NULL; i++) { +    do_highlight((char_u *)pp[i], reset, true); +  }    /* Reverse looks ugly, but grey may not work for 8 colors.  Thus let it     * depend on the number of colors available.     * With 8 colors brown is equal to yellow, need to use black for Search fg     * to avoid Statement highlighted text disappears.     * Clear the attributes, needed when changing the t_Co value. */ -  if (t_colors > 8) +  if (t_colors > 8) {      do_highlight(          (char_u *)(*p_bg == 'l'                     ? "Visual cterm=NONE ctermbg=LightGrey" -                   : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, -        TRUE); -  else { +                   : "Visual cterm=NONE ctermbg=DarkGrey"), false, +        true); +  } else {      do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",          FALSE, TRUE);      if (*p_bg == 'l') @@ -6112,12 +6116,7 @@ int load_colors(char_u *name)  /// "forceit" and "init" both TRUE.  /// @param init TRUE when called for initializing  void -do_highlight( -    char_u *line, -    int forceit, -    int init -) -{ +do_highlight(char_u *line, int forceit, int init) {    char_u      *name_end;    char_u      *linep;    char_u      *key_start; @@ -6134,15 +6133,16 @@ do_highlight(    int dolink = FALSE;    int error = FALSE;    int color; -  int is_normal_group = FALSE;                  /* "Normal" group */ +  bool is_normal_group = false;   // "Normal" group    /*     * If no argument, list current highlighting.     */    if (ends_excmd(*line)) { -    for (int i = 1; i <= highlight_ga.ga_len && !got_int; ++i) -      /* TODO: only call when the group has attributes set */ +    for (int i = 1; i <= highlight_ga.ga_len && !got_int; i++) { +      // todo(vim): only call when the group has attributes set        highlight_list_one(i); +    }      return;    } @@ -6270,12 +6270,12 @@ do_highlight(      return;    idx = id - 1;                         /* index is ID minus one */ -  /* Return if "default" was used and the group already has settings. */ -  if (dodefault && hl_has_settings(idx, TRUE)) +  // Return if "default" was used and the group already has settings +  if (dodefault && hl_has_settings(idx, true)) {      return; +  } -  if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0) -    is_normal_group = TRUE; +  is_normal_group = (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0);    /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */    if (doclear || (forceit && init)) { @@ -6284,7 +6284,7 @@ do_highlight(        HL_TABLE()[idx].sg_set = 0;    } -  if (!doclear) +  if (!doclear) {      while (!ends_excmd(*linep)) {        key_start = linep;        if (*linep == '=') { @@ -6390,12 +6390,12 @@ do_highlight(            }          }        } else if (STRCMP(key, "FONT") == 0)   { -        /* in non-GUI fonts are simply ignored */ -      } else if (STRCMP(key, -                     "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)   { +        // in non-GUI fonts are simply ignored +      } else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0) {          if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM)) { -          if (!init) +          if (!init) {              HL_TABLE()[idx].sg_set |= SG_CTERM; +          }            /* When setting the foreground color, and previously the "bold"             * flag was set for a light color, reset it now */ @@ -6489,9 +6489,10 @@ do_highlight(                     * colors (on some terminals, e.g. "linux") */                    if (color & 8) {                      HL_TABLE()[idx].sg_cterm |= HL_BOLD; -                    HL_TABLE()[idx].sg_cterm_bold = TRUE; -                  } else +                    HL_TABLE()[idx].sg_cterm_bold = true; +                  } else {                      HL_TABLE()[idx].sg_cterm &= ~HL_BOLD; +                  }                  }                  color &= 7;             // truncate to 8 colors                } else if (t_colors == 16 || t_colors == 88 || t_colors >= 256) { @@ -6603,21 +6604,23 @@ do_highlight(        /*         * When highlighting has been given for a group, don't link it.         */ -      if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) +      if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK)) {          HL_TABLE()[idx].sg_link = 0; +      }        /*         * Continue with next argument.         */        linep = skipwhite(linep);      } +  }    /*     * If there is an error, and it's a new entry, remove it from the table.     */ -  if (error && idx == highlight_ga.ga_len) +  if (error && idx == highlight_ga.ga_len) {      syn_unadd_group(); -  else { +  } else {      if (is_normal_group) {        HL_TABLE()[idx].sg_attr = 0;        // Need to update all groups, because they might be using "bg" and/or @@ -6625,16 +6628,17 @@ do_highlight(        highlight_attr_set_all();        // If the normal group has changed, it is simpler to refresh every UI        ui_refresh(); -    } else +    } else {        set_hl_attr(idx); +    }      HL_TABLE()[idx].sg_scriptID = current_SID;      redraw_all_later(NOT_VALID);    }    xfree(key);    xfree(arg); -  /* Only call highlight_changed() once, after sourcing a syntax file */ -  need_highlight_changed = TRUE; +  // Only call highlight_changed() once, after sourcing a syntax file +  need_highlight_changed = true;  }  #if defined(EXITFREE) @@ -6707,14 +6711,15 @@ static void highlight_clear(int idx)  } -/* - * Table with the specifications for an attribute number. - * Note that this table is used by ALL buffers.  This is required because the - * GUI can redraw at any time for any buffer. - */ +/// Table with the specifications for an attribute number. +/// Note that this table is used by ALL buffers.  This is required because the +/// GUI can redraw at any time for any buffer.  static garray_T attr_table = GA_EMPTY_INIT_VALUE; -#define ATTR_ENTRY(idx) ((attrentry_T *)attr_table.ga_data)[idx] +static inline attrentry_T * ATTR_ENTRY(int idx) +{ +  return &((attrentry_T *)attr_table.ga_data)[idx]; +}  /// Return the attr number for a set of colors and font. @@ -6804,7 +6809,7 @@ int hl_combine_attr(int char_attr, int prim_attr)  {    attrentry_T *char_aep = NULL;    attrentry_T *spell_aep; -  attrentry_T new_en; +  attrentry_T new_en = ATTRENTRY_INIT;    if (char_attr == 0) {      return prim_attr; @@ -6852,17 +6857,24 @@ int hl_combine_attr(int char_attr, int prim_attr)    return get_attr_entry(&new_en);  } +/// \note this function does not apply exclusively to cterm attr contrary +/// to what its name implies  attrentry_T *syn_cterm_attr2entry(int attr)  {    attr -= ATTR_OFF; -  if (attr >= attr_table.ga_len)          /* did ":syntax clear" */ +  if (attr >= attr_table.ga_len) { +    // did ":syntax clear"      return NULL; -  return &(ATTR_ENTRY(attr)); +  } +  return ATTR_ENTRY(attr);  } +/// \addtogroup LIST_XXX +/// @{  #define LIST_ATTR   1  #define LIST_STRING 2  #define LIST_INT    3 +/// @}  static void highlight_list_one(int id)  { @@ -6901,7 +6913,13 @@ static void highlight_list_one(int id)      last_set_msg(sgp->sg_scriptID);  } -static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name) +/// Outputs a highlight when doing ":hi MyHighlight" +/// +/// @param type one of \ref LIST_XXX +/// @param iarg integer argument used if \p type == LIST_INT +/// @param sarg string used if \p type == LIST_STRING +static int highlight_list_arg(int id, int didh, int type, int iarg, +                              char_u *sarg, const char *name)  {    char_u buf[100];    char_u      *ts; @@ -7041,24 +7059,23 @@ const char *highlight_color(const int id, const char *const what,    return NULL;  } -/* - * Output the syntax list header. - * Return TRUE when started a new line. - */ -static int  -syn_list_header ( -    int did_header,                 /* did header already */ -    int outlen,                     /* length of string that comes */ -    int id                         /* highlight group id */ -) +/// Output the syntax list header. +/// +/// @param did_header did header already +/// @param outlen length of string that comes +/// @param id highlight group id +/// @return true when started a new line. +static int +syn_list_header(int did_header, int outlen, int id)  {    int endcol = 19;    int newline = TRUE;    if (!did_header) {      msg_putchar('\n'); -    if (got_int) -      return TRUE; +    if (got_int) { +      return true; +    }      msg_outtrans(HL_TABLE()[id - 1].sg_name);      endcol = 15;    } else if (msg_col + outlen + 1 >= Columns)   { @@ -7095,12 +7112,13 @@ set_hl_attr (      int idx                    /* index in array */  )  { -  attrentry_T at_en; +  attrentry_T at_en = ATTRENTRY_INIT;    struct hl_group     *sgp = HL_TABLE() + idx; -  /* The "Normal" group doesn't need an attribute number */ -  if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0) +  // The "Normal" group doesn't need an attribute number +  if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0) {      return; +  }    at_en.cterm_ae_attr = sgp->sg_cterm;    at_en.cterm_fg_color = sgp->sg_cterm_fg; @@ -7124,10 +7142,10 @@ set_hl_attr (    }  } -/* - * Lookup a highlight group name and return it's ID. - * If it is not found, 0 is returned. - */ +/// Lookup a highlight group name and return its ID. +/// +/// @param highlight name e.g. 'Cursor', 'Normal' +/// @return the highlight id, else 0 if \p name does not exist  int syn_name2id(const char_u *name)  {    int i; @@ -7176,7 +7194,7 @@ int syn_namen2id(char_u *linep, int len)    return id;  } -/// Find highlight group name in the table and return it's ID. +/// Find highlight group name in the table and return its ID.  /// If it doesn't exist yet, a new entry is created.  ///  /// @param pp Highlight group name @@ -7195,11 +7213,11 @@ int syn_check_group(char_u *pp, int len)    return id;  } -/* - * Add new highlight group and return it's ID. - * "name" must be an allocated string, it will be consumed. - * Return 0 for failure. - */ +/// Add new highlight group and return it's ID. +/// +/// @param name must be an allocated string, it will be consumed. +/// @return 0 for failure, else the allocated group id +/// @see syn_check_group syn_unadd_group  static int syn_add_group(char_u *name)  {    char_u      *p; @@ -7237,25 +7255,26 @@ static int syn_add_group(char_u *name)    struct hl_group* hlgp = GA_APPEND_VIA_PTR(struct hl_group, &highlight_ga);    memset(hlgp, 0, sizeof(*hlgp));    hlgp->sg_name = name; +  hlgp->sg_rgb_bg = -1; +  hlgp->sg_rgb_fg = -1; +  hlgp->sg_rgb_sp = -1;    hlgp->sg_name_u = vim_strsave_up(name);    return highlight_ga.ga_len;               /* ID is index plus one */  } -/* - * When, just after calling syn_add_group(), an error is discovered, this - * function deletes the new name. - */ +/// When, just after calling syn_add_group(), an error is discovered, this +/// function deletes the new name.  static void syn_unadd_group(void)  { -  --highlight_ga.ga_len; +  highlight_ga.ga_len--;    xfree(HL_TABLE()[highlight_ga.ga_len].sg_name);    xfree(HL_TABLE()[highlight_ga.ga_len].sg_name_u);  } -/* - * Translate a group ID to highlight attributes. - */ + +/// Translate a group ID to highlight attributes. +/// @see syn_cterm_attr2entry  int syn_id2attr(int hl_id)  {    struct hl_group     *sgp; diff --git a/src/nvim/syntax_defs.h b/src/nvim/syntax_defs.h index 9f309451b0..56fadbe7f6 100644 --- a/src/nvim/syntax_defs.h +++ b/src/nvim/syntax_defs.h @@ -73,4 +73,14 @@ typedef struct attr_entry {    int cterm_fg_color, cterm_bg_color;  } attrentry_T; +#define ATTRENTRY_INIT { \ +  .rgb_ae_attr = 0, \ +  .cterm_ae_attr = 0, \ +  .rgb_fg_color = -1, \ +  .rgb_bg_color = -1, \ +  .rgb_sp_color = -1, \ +  .cterm_fg_color = 0, \ +  .cterm_bg_color = 0, \ +} +  #endif // NVIM_SYNTAX_DEFS_H diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index 5a38178bd5..11e26d03aa 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -24,28 +24,34 @@ func Test_after_comes_later()  	\ 'set guioptions+=M',  	\ 'let $HOME = "/does/not/exist"',  	\ 'set loadplugins', -	\ 'set rtp=Xhere,Xafter', +	\ 'set rtp=Xhere,Xafter,Xanother',  	\ 'set packpath=Xhere,Xafter',  	\ 'set nomore', +	\ 'let g:sequence = ""',  	\ ]    let after = [  	\ 'redir! > Xtestout',  	\ 'scriptnames',  	\ 'redir END', +	\ 'redir! > Xsequence', +	\ 'echo g:sequence', +	\ 'redir END',  	\ 'quit',  	\ ]    call mkdir('Xhere/plugin', 'p') -  call writefile(['let done = 1'], 'Xhere/plugin/here.vim') +  call writefile(['let g:sequence .= "here "'], 'Xhere/plugin/here.vim') +  call mkdir('Xanother/plugin', 'p') +  call writefile(['let g:sequence .= "another "'], 'Xanother/plugin/another.vim')    call mkdir('Xhere/pack/foo/start/foobar/plugin', 'p') -  call writefile(['let done = 1'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim') +  call writefile(['let g:sequence .= "pack "'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim')    call mkdir('Xafter/plugin', 'p') -  call writefile(['let done = 1'], 'Xafter/plugin/later.vim') +  call writefile(['let g:sequence .= "after "'], 'Xafter/plugin/later.vim')    if RunVim(before, after, '')      let lines = readfile('Xtestout') -    let expected = ['Xbefore.vim', 'here.vim', 'foo.vim', 'later.vim', 'Xafter.vim'] +    let expected = ['Xbefore.vim', 'here.vim', 'another.vim', 'foo.vim', 'later.vim', 'Xafter.vim']      let found = []      for line in lines        for one in expected @@ -57,11 +63,47 @@ func Test_after_comes_later()      call assert_equal(expected, found)    endif +  call assert_equal('here another pack after', substitute(join(readfile('Xsequence', 1), ''), '\s\+$', '', '')) +    call delete('Xtestout') +  call delete('Xsequence')    call delete('Xhere', 'rf') +  call delete('Xanother', 'rf')    call delete('Xafter', 'rf')  endfunc +func Test_pack_in_rtp_when_plugins_run() +  if !has('packages') +    return +  endif +  let before = [ +	\ 'set nocp viminfo+=nviminfo', +	\ 'set guioptions+=M', +	\ 'let $HOME = "/does/not/exist"', +	\ 'set loadplugins', +	\ 'set rtp=Xhere', +	\ 'set packpath=Xhere', +	\ 'set nomore', +	\ ] +  let after = [ +	\ 'quit', +	\ ] +  call mkdir('Xhere/plugin', 'p') +  call writefile(['redir! > Xtestout', 'silent set runtimepath?', 'silent! call foo#Trigger()', 'redir END'], 'Xhere/plugin/here.vim') +  call mkdir('Xhere/pack/foo/start/foobar/autoload', 'p') +  call writefile(['function! foo#Trigger()', 'echo "autoloaded foo"', 'endfunction'], 'Xhere/pack/foo/start/foobar/autoload/foo.vim') + +  if RunVim(before, after, '') + +    let lines = filter(readfile('Xtestout'), '!empty(v:val)') +    call assert_match('Xhere[/\\]pack[/\\]foo[/\\]start[/\\]foobar', get(lines, 0)) +    call assert_match('autoloaded foo', get(lines, 1)) +  endif + +  call delete('Xtestout') +  call delete('Xhere', 'rf') +endfunc +  func Test_help_arg()    if !has('unix') && has('gui')      " this doesn't work with gvim on MS-Windows diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index b5af5b0333..e3eab72695 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -234,9 +234,9 @@ static void terminfo_start(UI *ui)    unibi_out(ui, unibi_keypad_xmit);    unibi_out(ui, unibi_clear_screen);    // Enable bracketed paste -  unibi_out(ui, data->unibi_ext.enable_bracketed_paste); +  unibi_out_ext(ui, data->unibi_ext.enable_bracketed_paste);    // Enable focus reporting -  unibi_out(ui, data->unibi_ext.enable_focus_reporting); +  unibi_out_ext(ui, data->unibi_ext.enable_focus_reporting);    uv_loop_init(&data->write_loop);    if (data->out_isatty) {      uv_tty_init(&data->write_loop, &data->output_handle.tty, data->out_fd, 0); @@ -263,9 +263,9 @@ static void terminfo_stop(UI *ui)    unibi_out(ui, unibi_keypad_local);    unibi_out(ui, unibi_exit_ca_mode);    // Disable bracketed paste -  unibi_out(ui, data->unibi_ext.disable_bracketed_paste); +  unibi_out_ext(ui, data->unibi_ext.disable_bracketed_paste);    // Disable focus reporting -  unibi_out(ui, data->unibi_ext.disable_focus_reporting); +  unibi_out_ext(ui, data->unibi_ext.disable_focus_reporting);    flush_buf(ui, true);    uv_tty_reset_mode();    uv_close((uv_handle_t *)&data->output_handle, NULL); @@ -426,14 +426,14 @@ static void update_attrs(UI *ui, HlAttrs attrs)        data->params[0].i = (fg >> 16) & 0xff;  // red        data->params[1].i = (fg >> 8) & 0xff;   // green        data->params[2].i = fg & 0xff;          // blue -      unibi_out(ui, data->unibi_ext.set_rgb_foreground); +      unibi_out_ext(ui, data->unibi_ext.set_rgb_foreground);      }      if (bg != -1) {        data->params[0].i = (bg >> 16) & 0xff;  // red        data->params[1].i = (bg >> 8) & 0xff;   // green        data->params[2].i = bg & 0xff;          // blue -      unibi_out(ui, data->unibi_ext.set_rgb_background); +      unibi_out_ext(ui, data->unibi_ext.set_rgb_background);      }    } else {      if (fg != -1) { @@ -679,7 +679,7 @@ static void set_scroll_region(UI *ui)    data->params[1].i = grid->bot;    unibi_out(ui, unibi_change_scroll_region);    if (grid->left != 0 || grid->right != ui->width - 1) { -    unibi_out(ui, data->unibi_ext.enable_lr_margin); +    unibi_out_ext(ui, data->unibi_ext.enable_lr_margin);      if (data->can_set_lr_margin) {        data->params[0].i = grid->left;        data->params[1].i = grid->right; @@ -700,7 +700,7 @@ static void reset_scroll_region(UI *ui)    UGrid *grid = &data->grid;    if (0 <= data->unibi_ext.reset_scroll_region) { -    unibi_out(ui, data->unibi_ext.reset_scroll_region); +    unibi_out_ext(ui, data->unibi_ext.reset_scroll_region);    } else {      data->params[0].i = 0;      data->params[1].i = ui->height - 1; @@ -717,7 +717,7 @@ static void reset_scroll_region(UI *ui)        data->params[0].i = ui->width - 1;        unibi_out(ui, unibi_set_right_margin_parm);      } -    unibi_out(ui, data->unibi_ext.disable_lr_margin); +    unibi_out_ext(ui, data->unibi_ext.disable_lr_margin);    }    unibi_goto(ui, grid->row, grid->col);  } @@ -730,7 +730,7 @@ static void tui_resize(UI *ui, Integer width, Integer height)    if (!got_winch) {  // Try to resize the terminal window.      data->params[0].i = (int)height;      data->params[1].i = (int)width; -    unibi_out(ui, data->unibi_ext.resize_screen); +    unibi_out_ext(ui, data->unibi_ext.resize_screen);      // DECSLPP does not reset the scroll region.      if (data->scroll_region_is_full_screen) {        reset_scroll_region(ui); @@ -836,7 +836,7 @@ static void tui_mouse_on(UI *ui)  {    TUIData *data = ui->data;    if (!data->mouse_enabled) { -    unibi_out(ui, data->unibi_ext.enable_mouse); +    unibi_out_ext(ui, data->unibi_ext.enable_mouse);      data->mouse_enabled = true;    }  } @@ -845,7 +845,7 @@ static void tui_mouse_off(UI *ui)  {    TUIData *data = ui->data;    if (data->mouse_enabled) { -    unibi_out(ui, data->unibi_ext.disable_mouse); +    unibi_out_ext(ui, data->unibi_ext.disable_mouse);      data->mouse_enabled = false;    }  } @@ -864,7 +864,7 @@ static void tui_set_mode(UI *ui, ModeShape mode)      if (attr > 0) {        attrentry_T *aep = syn_cterm_attr2entry(attr);        data->params[0].i = aep->rgb_bg_color; -      unibi_out(ui, data->unibi_ext.set_cursor_color); +      unibi_out_ext(ui, data->unibi_ext.set_cursor_color);      }    } @@ -875,7 +875,7 @@ static void tui_set_mode(UI *ui, ModeShape mode)      default: WLOG("Unknown shape value %d", shape); break;    }    data->params[0].i = shape + (int)(c.blinkon == 0); -  unibi_out(ui, data->unibi_ext.set_cursor_style); +  unibi_out_ext(ui, data->unibi_ext.set_cursor_style);  }  /// @param mode editor mode @@ -1182,25 +1182,27 @@ static void unibi_goto(UI *ui, int row, int col)    unibi_out(ui, unibi_cursor_address);  } +#define UNIBI_OUT(fn) \ +  do { \ +    TUIData *data = ui->data; \ +    const char *str = NULL; \ +    if (unibi_index >= 0) { \ +      str = fn(data->ut, (unsigned)unibi_index); \ +    } \ +    if (str) { \ +      unibi_var_t vars[26 + 26] = { { 0 } }; \ +      unibi_format(vars, vars + 26, str, data->params, out, ui, NULL, NULL); \ +    } \ +  } while (0)  static void unibi_out(UI *ui, int unibi_index)  { -  TUIData *data = ui->data; - -  const char *str = NULL; - -  if (unibi_index >= 0) { -    if (unibi_index < unibi_string_begin_) { -      str = unibi_get_ext_str(data->ut, (unsigned)unibi_index); -    } else { -      str = unibi_get_str(data->ut, (unsigned)unibi_index); -    } -  } - -  if (str) { -    unibi_var_t vars[26 + 26] = {{0}}; -    unibi_format(vars, vars + 26, str, data->params, out, ui, NULL, NULL); -  } +  UNIBI_OUT(unibi_get_str); +} +static void unibi_out_ext(UI *ui, int unibi_index) +{ +  UNIBI_OUT(unibi_get_ext_str);  } +#undef UNIBI_OUT  static void out(void *ctx, const char *str, size_t len)  { diff --git a/src/nvim/version.c b/src/nvim/version.c index 68c41cee88..53b44f8029 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -272,7 +272,7 @@ static const int included_patches[] = {    // 683,    // 682,    // 681, -  // 680, +  680,    679,    678,    // 677, @@ -340,7 +340,7 @@ static const int included_patches[] = {    // 615,    614,    // 613, -  // 612, +  612,    // 611,    // 610,    // 609, @@ -515,7 +515,7 @@ static const int included_patches[] = {    // 440,    // 439,    // 438, -  // 437, +  437,    // 436,    // 435,    // 434, @@ -626,8 +626,8 @@ static const int included_patches[] = {    // 329,    // 328,    // 327, -  // 326, -  // 325, +  326, +  325,    // 324,    // 323,    322, @@ -644,7 +644,7 @@ static const int included_patches[] = {    311,    // 310,    // 309, -  // 308, +  308,    307,    // 306,    // 305, diff --git a/src/nvim/window.c b/src/nvim/window.c index 081fc98816..c2d0a9b3b1 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -5515,11 +5515,14 @@ void restore_buffer(bufref_T *save_curbuf)  } -// Add match to the match list of window 'wp'.  The pattern 'pat' will be -// highlighted with the group 'grp' with priority 'prio'. -// Optionally, a desired ID 'id' can be specified (greater than or equal to 1). -// If no particular ID is desired, -1 must be specified for 'id'. -// Return ID of added match, -1 on failure. +/// Add match to the match list of window 'wp'.  The pattern 'pat' will be +/// highlighted with the group 'grp' with priority 'prio'. +/// Optionally, a desired ID 'id' can be specified (greater than or equal to 1). +/// +/// @param[in] id a desired ID 'id' can be specified +///               (greater than or equal to 1). -1 must be specified if no +///               particular ID is desired +/// @return ID of added match, -1 on failure.  int match_add(win_T *wp, const char *const grp, const char *const pat,                int prio, int id, list_T *pos_list,                const char *const conceal_char) @@ -5697,10 +5700,9 @@ fail:    return -1;  } -/* - * Delete match with ID 'id' in the match list of window 'wp'. - * Print error messages if 'perr' is TRUE. - */ + +/// Delete match with ID 'id' in the match list of window 'wp'. +/// Print error messages if 'perr' is TRUE.  int match_delete(win_T *wp, int id, int perr)  {    matchitem_T *cur = wp->w_match_head; diff --git a/test/functional/clipboard/clipboard_provider_spec.lua b/test/functional/clipboard/clipboard_provider_spec.lua index eb2eeee0da..f66fbf7c94 100644 --- a/test/functional/clipboard/clipboard_provider_spec.lua +++ b/test/functional/clipboard/clipboard_provider_spec.lua @@ -4,6 +4,8 @@ local helpers = require('test.functional.helpers')(after_each)  local Screen = require('test.functional.ui.screen')  local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert  local feed_command, expect, eq, eval = helpers.feed_command, helpers.expect, helpers.eq, helpers.eval +local command = helpers.command +local meths = helpers.meths  local function basic_register_test(noblock)    insert("some words") @@ -80,15 +82,73 @@ local function basic_register_test(noblock)    expect("two and three and one")  end -describe('the unnamed register', function() +describe('clipboard', function()    before_each(clear) -  it('works without provider', function() + +  it('unnamed register works without provider', function()      eq('"', eval('v:register'))      basic_register_test()    end) + +  it('`:redir @+>` with invalid g:clipboard shows exactly one error #7184', +  function() +    local screen = Screen.new(72, 4) +    screen:attach() +    command("let g:clipboard = 'bogus'") +    feed_command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END') +    screen:expect([[ +      ^                                                                        | +      ~                                                                       | +      ~                                                                       | +      clipboard: No provider. Try ":CheckHealth" or ":h clipboard".           | +    ]], nil, {{bold = true, foreground = Screen.colors.Blue}}) +  end) + +  it('`:redir @+>|bogus_cmd|redir END` + invalid g:clipboard must not recurse #7184', +  function() +    local screen = Screen.new(72, 4) +    screen:attach() +    command("let g:clipboard = 'bogus'") +    feed_command('redir @+> | bogus_cmd | redir END') +    screen:expect([[ +      ~                                                                       | +      clipboard: No provider. Try ":CheckHealth" or ":h clipboard".           | +      E492: Not an editor command: bogus_cmd | redir END                      | +      Press ENTER or type command to continue^                                 | +    ]], nil, {{bold = true, foreground = Screen.colors.Blue}}) +  end) + +  it('invalid g:clipboard shows hint if :redir is not active', function() +    command("let g:clipboard = 'bogus'") +    eq('', eval('provider#clipboard#Executable()')) +    eq('clipboard: invalid g:clipboard', eval('provider#clipboard#Error()')) + +    local screen = Screen.new(72, 4) +    screen:attach() +    command("let g:clipboard = 'bogus'") +    -- Explicit clipboard attempt, should show a hint message. +    feed_command('let @+="foo"') +    screen:expect([[ +      ^                                                                        | +      ~                                                                       | +      ~                                                                       | +      clipboard: No provider. Try ":CheckHealth" or ":h clipboard".           | +    ]], nil, {{bold = true, foreground = Screen.colors.Blue}}) +  end) + +  it('valid g:clipboard', function() +    -- provider#clipboard#Executable() only checks the structure. +    meths.set_var('clipboard', { +      ['name'] = 'clippy!', +      ['copy'] = { ['+'] = 'any command', ['*'] = 'some other' }, +      ['paste'] = { ['+'] = 'any command', ['*'] = 'some other' }, +    }) +    eq('clippy!', eval('provider#clipboard#Executable()')) +    eq('', eval('provider#clipboard#Error()')) +  end)  end) -describe('clipboard usage', function() +describe('clipboard', function()    local function reset(...)      clear('--cmd', 'let &rtp = "test/functional/fixtures,".&rtp', ...)    end @@ -98,7 +158,36 @@ describe('clipboard usage', function()      feed_command('call getreg("*")') -- force load of provider    end) -   it('has independent "* and unnamed registers per default', function() +  it('`:redir @+>` invokes clipboard once-per-message', function() +    eq(0, eval("g:clip_called_set")) +    feed_command('redir @+> | :silent echo system("cat CONTRIBUTING.md") | redir END') +    -- Assuming CONTRIBUTING.md has >100 lines. +    assert(eval("g:clip_called_set") > 100) +  end) + +  it('`:redir @">` does NOT invoke clipboard', function() +    -- :redir to a non-clipboard register, with `:set clipboard=unnamed` does +    -- NOT propagate to the clipboard. This is consistent with Vim. +    command("set clipboard=unnamedplus") +    eq(0, eval("g:clip_called_set")) +    feed_command('redir @"> | :silent echo system("cat CONTRIBUTING.md") | redir END') +    eq(0, eval("g:clip_called_set")) +  end) + +  it('`:redir @+>|bogus_cmd|redir END` must not recurse #7184', +  function() +    local screen = Screen.new(72, 4) +    screen:attach() +    feed_command('redir @+> | bogus_cmd | redir END') +    screen:expect([[ +      ^                                                                        | +      ~                                                                       | +      ~                                                                       | +      E492: Not an editor command: bogus_cmd | redir END                      | +    ]], nil, {{bold = true, foreground = Screen.colors.Blue}}) +  end) + +  it('has independent "* and unnamed registers by default', function()      insert("some words")      feed('^"*dwdw"*P')      expect('some ') @@ -139,7 +228,7 @@ describe('clipboard usage', function()      eq({'some\ntext', '\nvery binary\n'}, eval("getreg('*', 1, 1)"))    end) -  it('support autodectection of regtype', function() +  it('autodetects regtype', function()      feed_command("let g:test_clip['*'] = ['linewise stuff','']")      feed_command("let g:test_clip['+'] = ['charwise','stuff']")      eq("V", eval("getregtype('*')")) @@ -169,7 +258,7 @@ describe('clipboard usage', function()      eq({{' much', 'ktext', ''}, 'b'}, eval("g:test_clip['+']"))    end) -  it('supports setreg', function() +  it('supports setreg()', function()      feed_command('call setreg("*", "setted\\ntext", "c")')      feed_command('call setreg("+", "explicitly\\nlines", "l")')      feed('"+P"*p') @@ -187,7 +276,7 @@ describe('clipboard usage', function()          ]])    end) -  it('supports let @+ (issue #1427)', function() +  it('supports :let @+ (issue #1427)', function()      feed_command("let @+ = 'some'")      feed_command("let @* = ' other stuff'")      eq({{'some'}, 'v'}, eval("g:test_clip['+']")) @@ -305,7 +394,7 @@ describe('clipboard usage', function()    end) -  describe('with clipboard=unnamedplus', function() +  describe('clipboard=unnamedplus', function()      before_each(function()        feed_command('set clipboard=unnamedplus')      end) @@ -349,6 +438,7 @@ describe('clipboard usage', function()          really unnamed          the plus]])      end) +      it('is updated on global changes', function()        insert([[  	text diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 54e56f7f41..1b8a5b1b95 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -677,12 +677,12 @@ describe("pty process teardown", function()      -- Exiting should terminate all descendants (PTY, its children, ...).      screen:expect([[ -                                    | +      ^                              |        [Process exited 0]            |                                      |                                      |                                      | -      -- TERMINAL --                | +                                    |      ]])    end)  end) diff --git a/test/functional/fixtures/autoload/provider/clipboard.vim b/test/functional/fixtures/autoload/provider/clipboard.vim index 411e095c71..6d777255c8 100644 --- a/test/functional/fixtures/autoload/provider/clipboard.vim +++ b/test/functional/fixtures/autoload/provider/clipboard.vim @@ -5,7 +5,13 @@ let s:methods = {}  let g:cliplossy = 0  let g:cliperror = 0 +" Count how many times the clipboard was invoked. +let g:clip_called_get = 0 +let g:clip_called_set = 0 +  function! s:methods.get(reg) +  let g:clip_called_get += 1 +    if g:cliperror      return 0    end @@ -19,6 +25,8 @@ function! s:methods.get(reg)  endfunction  function! s:methods.set(lines, regtype, reg) +  let g:clip_called_set += 1 +    if a:reg == '"'      call s:methods.set(a:lines,a:regtype,'+')      call s:methods.set(a:lines,a:regtype,'*') diff --git a/test/functional/legacy/051_highlight_spec.lua b/test/functional/legacy/051_highlight_spec.lua index 60d29246ff..2ef74196ee 100644 --- a/test/functional/legacy/051_highlight_spec.lua +++ b/test/functional/legacy/051_highlight_spec.lua @@ -37,6 +37,7 @@ describe(':highlight', function()      feed('q')      wait() -- wait until we're back to normal      command('hi Search') +    command('hi Normal')      -- Test setting colors.      -- Test clearing one color and all doesn't generate error or warning diff --git a/test/functional/legacy/packadd_spec.lua b/test/functional/legacy/packadd_spec.lua index c280888dda..7c44353aec 100644 --- a/test/functional/legacy/packadd_spec.lua +++ b/test/functional/legacy/packadd_spec.lua @@ -83,6 +83,41 @@ describe('packadd', function()          call assert_equal(new_rtp, &rtp)        endfunc +      func Test_packadd_symlink_dir() +        if !has('unix') +          return +        endif +        let top2_dir = s:topdir . '/Xdir2' +        let real_dir = s:topdir . '/Xsym' +        call mkdir(real_dir, 'p') +        exec "silent! !ln -s Xsym" top2_dir +        let &rtp = top2_dir . ',' . top2_dir . '/after' +        let &packpath = &rtp + +        let s:plugdir = top2_dir . '/pack/mine/opt/mytest' +        call mkdir(s:plugdir . '/plugin', 'p') + +        exe 'split ' . s:plugdir . '/plugin/test.vim' +        call setline(1, 'let g:plugin_works = 44') +        wq +        let g:plugin_works = 0 + +        packadd mytest + +        " Must have been inserted in the middle, not at the end +        call assert_true(&rtp =~ '/pack/mine/opt/mytest,') +        call assert_equal(44, g:plugin_works) + +        " No change when doing it again. +        let rtp_before = &rtp +        packadd mytest +        call assert_equal(rtp_before, &rtp) + +        set rtp& +        let rtp = &rtp +        exec "silent !rm" top2_dir +      endfunc +        func Test_packloadall()          " plugin foo with an autoload directory          let fooplugindir = &packpath . '/pack/mine/start/foo/plugin' @@ -227,6 +262,11 @@ describe('packadd', function()      expected_empty()    end) +  it('works with symlinks', function() +    call('Test_packadd_symlink_dir') +    expected_empty() +  end) +    it('works with :packloadall', function()      call('Test_packloadall')      expected_empty() diff --git a/test/functional/terminal/ex_terminal_spec.lua b/test/functional/terminal/ex_terminal_spec.lua index 9930efc402..7522f073c4 100644 --- a/test/functional/terminal/ex_terminal_spec.lua +++ b/test/functional/terminal/ex_terminal_spec.lua @@ -72,10 +72,10 @@ describe(':terminal (with fake shell)', function()      terminal_with_fake_shell()      wait()      screen:expect([[ -      ready $                                           | +      ^ready $                                           |        [Process exited 0]                                |                                                          | -      -- TERMINAL --                                    | +      :terminal                                         |      ]])    end) @@ -96,10 +96,10 @@ describe(':terminal (with fake shell)', function()      terminal_with_fake_shell()      wait()      screen:expect([[ -      jeff $                                            | +      ^jeff $                                            |        [Process exited 0]                                |                                                          | -      -- TERMINAL --                                    | +      :terminal                                         |      ]])    end) @@ -107,10 +107,10 @@ describe(':terminal (with fake shell)', function()      terminal_with_fake_shell('echo hi')      wait()      screen:expect([[ -      ready $ echo hi                                   | +      ^ready $ echo hi                                   |                                                          |        [Process exited 0]                                | -      -- TERMINAL --                                    | +      :terminal echo hi                                 |      ]])    end) @@ -119,10 +119,10 @@ describe(':terminal (with fake shell)', function()      terminal_with_fake_shell('echo hi')      wait()      screen:expect([[ -      jeff $ echo hi                                    | +      ^jeff $ echo hi                                    |                                                          |        [Process exited 0]                                | -      -- TERMINAL --                                    | +      :terminal echo hi                                 |      ]])    end) @@ -130,10 +130,10 @@ describe(':terminal (with fake shell)', function()      terminal_with_fake_shell([[echo 'hello' \ "world"]])      wait()      screen:expect([[ -      ready $ echo 'hello' \ "world"                    | +      ^ready $ echo 'hello' \ "world"                    |                                                          |        [Process exited 0]                                | -      -- TERMINAL --                                    | +      :terminal echo 'hello' \ "world"                  |      ]])    end) @@ -166,10 +166,10 @@ describe(':terminal (with fake shell)', function()      terminal_with_fake_shell()      wait()      screen:expect([[ -      ready $                                           | +      ^ready $                                           |        [Process exited 0]                                |                                                          | -      -- TERMINAL --                                    | +      :terminal                                         |      ]])      eq('term://', string.match(eval('bufname("%")'), "^term://"))      helpers.feed([[<C-\><C-N>]]) @@ -184,10 +184,10 @@ describe(':terminal (with fake shell)', function()    it('works with gf', function()      terminal_with_fake_shell([[echo "scripts/shadacat.py"]])      screen:expect([[ -      ready $ echo "scripts/shadacat.py"                | +      ^ready $ echo "scripts/shadacat.py"                |                                                          |        [Process exited 0]                                | -      -- TERMINAL --                                    | +      :terminal echo "scripts/shadacat.py"              |      ]])      helpers.feed([[<C-\><C-N>]])      eq('term://', string.match(eval('bufname("%")'), "^term://")) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 21b907c8f7..34a5ac0a49 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -287,8 +287,8 @@ describe('tui focus event handling', function()        feed_data(':terminal\n')        feed_data('\027[I')        screen:expect([[ -        ready $                                           | -        [Process exited 0]{1: }                               | +        {1:r}eady $                                           | +        [Process exited 0]                                |                                                            |                                                            |                                                            | @@ -297,8 +297,8 @@ describe('tui focus event handling', function()        ]])        feed_data('\027[O')        screen:expect([[ -        ready $                                           | -        [Process exited 0]{1: }                               | +        {1:r}eady $                                           | +        [Process exited 0]                                |                                                            |                                                            |                                                            |  | 
