I would like to create a global (configuration) variable that ideally would work as a comma separated list but that we could also append to it. The idea is to have the same behavior as when using += when setting &runtimepath.So, it would be possible to do something like:let g:myvar+=newitemAnd the variable contents would become:item1,item2,newitemTwo questions:Can such a variable be implemented in Vim (and how)?Would it be possible to allow and define a list for its tab completion?
Append to global variable and completion
vimscript;variables
You could have your variable be a list and add values with :let g:var += ['item']. However, it won't be possible to prevent the same item to be added several times.That's why I've come up with lh#list#push_if_new()function! lh#list#push_if_new(list, value) abort  let matching = filter(copy(a:list), 'v:val == a:value')  if empty(matching)    call add (a:list, a:value)  endif  return a:listendfunctionIt's quite cumbersome to use. Having a command that calls the function would help though. It would look like this. some plugincommand! -nargs=+ -complete=customlist,lh#let#_push_options_complete      \ PushOptions call lh#let#_push_options(<f-args>)And can be used this way regarding completion:let g:foobar = {'titi': []}:let g:acceptable_options_for['g:foobar.titi'] = ['s', 'foo', 'toto', 'titi' ]:PushOptions foo<tab> -> g:foobar. <tab> again -> g:foobar.titi <space><tab> -> list of possible choices: s, foo, toto, titiCompletion properties:It can complete variables names, and even search for list/dict fields in dictionariesIt can complete values from a list of possible values, which is filtered out of the values already setAnd here are the functions required for the completion: # PushOptions {{{2 Function: lh#let#_push_options(variable, ...) {{{3function! lh#let#_push_options(variable, ...) abort  let var = lh#let#if_undef(a:variable, '[]')  for val in a:000    call lh#list#push_if_new(var, val)  endfor  return varendfunction Function: lh#let#_list_all_list_variables_in_scope(scope) {{{3function! lh#let#_list_all_list_variables_in_scope(scope) abort  let vars = map(keys({a:scope}), 'a:scope.v:val')   Keep only lists and dictionaries  call filter(vars, 'type({v:val}) == type([]) || type({v:val}) == type({})')  return varsendfunction Function: lh#let#_list_variables(lead) {{{3function! lh#let#_list_variables(lead) abort  if empty(a:lead)     No variable spcified yet    let vars           \ = lh#let#_list_all_list_variables_in_scope('g:')          \ + lh#let#_list_all_list_variables_in_scope('b:')          \ + lh#let#_list_all_list_variables_in_scope('w:')          \ + lh#let#_list_all_list_variables_in_scope('t:')  elseif stridx(a:lead, '.') >= 0     Dictionary    let [all, dict, key ; trail] = matchlist(a:lead, '\v(.*)\.(.*)')    let vars = keys({dict})    call filter(vars, 'type({dict}[v:val]) == type([]) || type({dict}[v:val]) == type({})')    call map(vars, 'v:val. (type({dict}[v:val])==type({})?.:)')    call map(vars, 'dict...v:val')    return vars  else     Simple variables    if         (len(a:lead) == 1 && a:lead    =~ '[gbwt]')          \ || (len(a:lead) > 1  && a:lead[1] == ':')      let scope = a:lead[0]      let filter_scope = ''    else      let scope = 'g'      let filter_scope = 'g:'    endif    let vars = lh#let#_list_all_list_variables_in_scope(scope.':')    call filter(vars, 'v:val =~ ^.filter_scope.a:lead')  endif   Add dot to identified dictionaries  call map(vars, 'v:val. (type({v:val})==type({})?.:)')  return varsendfunction Function: lh#let#_push_options_complete(ArgLead, CmdLine, CursorPos) {{{3call lh#let#if_undef('g:acceptable_options_for', '{}')function! lh#let#_push_options_complete(ArgLead, CmdLine, CursorPos) abort  let tmp = substitute(a:CmdLine, '\s*\S*', 'Z', 'g')  let pos = strlen(tmp)  call s:Verbose('complete(lead=%1, cmdline=%2, cursorpos=%3)', a:ArgLead, a:CmdLine, a:CursorPos)  if     2 == pos     First argument: a variable name    return lh#let#_list_variables(a:ArgLead)  elseif pos >= 3     Doesn't handle 'foo\ bar', but we don't need this to fetch a variable     name    let args = split(a:CmdLine, '\s\+')    let varname = args[1]    call s:Verbose('complete: varname=%1', varname)     Other arguments: acceptable values    let acceptable_values = get(g:acceptable_options_for, varname, [])    let crt_val = '\v^'.join(exists(varname)? eval(varname) : [], '|').'$'    let acceptable_values = filter(copy(acceptable_values), 'v:val !~ crt_val')    return acceptable_values  endifendfunction