mirror of
https://github.com/vim/vim.git
synced 2025-08-24 19:45:50 -04:00
patch 9.1.1603: completion: cannot use autoloaded funcs in 'complete' F{func}
Problem: completion: cannot use autoloaded funcs in 'complete' F{func} (Maxim Kim) Solution: Make it work (Girish Palya) fixes: #17869 closes: #17885 Signed-off-by: Girish Palya <girishji@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
parent
7132935413
commit
1bfe86a7d3
@ -2509,6 +2509,8 @@ free_buf_options(
|
||||
free_callback(&buf->b_ofu_cb);
|
||||
clear_string_option(&buf->b_p_tsrfu);
|
||||
free_callback(&buf->b_tsrfu_cb);
|
||||
clear_cpt_callbacks(&buf->b_p_cpt_cb, buf->b_p_cpt_count);
|
||||
buf->b_p_cpt_count = 0;
|
||||
#endif
|
||||
#ifdef FEAT_QUICKFIX
|
||||
clear_string_option(&buf->b_p_gefm);
|
||||
|
@ -42,6 +42,9 @@ set_ref_in_buffers(int copyID)
|
||||
abort = abort || set_ref_in_callback(&bp->b_ofu_cb, copyID);
|
||||
if (!abort)
|
||||
abort = abort || set_ref_in_callback(&bp->b_tsrfu_cb, copyID);
|
||||
if (!abort && bp->b_p_cpt_cb != NULL)
|
||||
abort = abort || set_ref_in_cpt_callbacks(bp->b_p_cpt_cb,
|
||||
bp->b_p_cpt_count, copyID);
|
||||
#endif
|
||||
if (!abort)
|
||||
abort = abort || set_ref_in_callback(&bp->b_tfu_cb, copyID);
|
||||
|
246
src/insexpand.c
246
src/insexpand.c
@ -273,7 +273,7 @@ static void ins_compl_add_list(list_T *list);
|
||||
static void ins_compl_add_dict(dict_T *dict);
|
||||
static int get_userdefined_compl_info(colnr_T curs_col, callback_T *cb, int *startcol);
|
||||
static void get_cpt_func_completion_matches(callback_T *cb);
|
||||
static callback_T *get_callback_if_cpt_func(char_u *p);
|
||||
static callback_T *get_callback_if_cpt_func(char_u *p, int idx);
|
||||
# endif
|
||||
static int setup_cpt_sources(void);
|
||||
static int is_cpt_func_refresh_always(void);
|
||||
@ -3191,12 +3191,40 @@ ins_compl_next_buf(buf_T *buf, int flag)
|
||||
return buf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Count the number of entries in the 'complete' option (curbuf->b_p_cpt).
|
||||
* Each non-empty, comma-separated segment is counted as one entry.
|
||||
*/
|
||||
static int
|
||||
get_cpt_sources_count(void)
|
||||
{
|
||||
char_u dummy[LSIZE];
|
||||
int count = 0;
|
||||
char_u *p;
|
||||
|
||||
for (p = curbuf->b_p_cpt; *p != NUL; )
|
||||
{
|
||||
while (*p == ',' || *p == ' ')
|
||||
p++; // Skip delimiters
|
||||
|
||||
if (*p != NUL)
|
||||
{
|
||||
(void)copy_option_part(&p, dummy, LSIZE, ","); // Advance p
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
#ifdef FEAT_COMPL_FUNC
|
||||
|
||||
# ifdef FEAT_EVAL
|
||||
static callback_T cfu_cb; // 'completefunc' callback function
|
||||
static callback_T ofu_cb; // 'omnifunc' callback function
|
||||
static callback_T tsrfu_cb; // 'thesaurusfunc' callback function
|
||||
static callback_T *cpt_cb; // Callback functions associated with F{func}
|
||||
static int cpt_cb_count; // Number of cpt callbacks
|
||||
# endif
|
||||
|
||||
/*
|
||||
@ -3267,6 +3295,124 @@ set_buflocal_ofu_callback(buf_T *buf UNUSED)
|
||||
# endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Free an array of 'complete' F{func} callbacks and set the pointer to NULL.
|
||||
*/
|
||||
void
|
||||
clear_cpt_callbacks(callback_T **callbacks, int count)
|
||||
{
|
||||
if (callbacks == NULL || *callbacks == NULL)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
free_callback(&(*callbacks)[i]);
|
||||
|
||||
VIM_CLEAR(*callbacks);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies a list of callback_T structs from src to *dest, clearing any existing
|
||||
* entries and allocating memory for the destination.
|
||||
*/
|
||||
static int
|
||||
copy_cpt_callbacks(callback_T **dest, int *dest_cnt, callback_T *src, int cnt)
|
||||
{
|
||||
if (cnt == 0)
|
||||
return OK;
|
||||
|
||||
clear_cpt_callbacks(dest, *dest_cnt);
|
||||
*dest_cnt = 0;
|
||||
|
||||
*dest = ALLOC_CLEAR_MULT(callback_T, cnt);
|
||||
if (*dest == NULL)
|
||||
return FAIL;
|
||||
|
||||
*dest_cnt = cnt;
|
||||
|
||||
for (int i = 0; i < cnt; i++)
|
||||
if (src[i].cb_name != NULL && *(src[i].cb_name) != NUL)
|
||||
copy_callback(&(*dest)[i], &src[i]);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy global 'complete' F{func} callbacks into the given buffer's local
|
||||
* callback array. Clears any existing buffer-local callbacks first.
|
||||
*/
|
||||
void
|
||||
set_buflocal_cpt_callbacks(buf_T *buf UNUSED)
|
||||
{
|
||||
#ifdef FEAT_EVAL
|
||||
if (buf == NULL || cpt_cb_count == 0)
|
||||
return;
|
||||
(void)copy_cpt_callbacks(&buf->b_p_cpt_cb, &buf->b_p_cpt_count, cpt_cb,
|
||||
cpt_cb_count);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse 'complete' option and initialize F{func} callbacks.
|
||||
* Frees any existing callbacks and allocates new ones.
|
||||
* Only F{func} entries are processed; others are ignored.
|
||||
*/
|
||||
int
|
||||
set_cpt_callbacks(optset_T *args)
|
||||
{
|
||||
char_u buf[LSIZE];
|
||||
char_u *p;
|
||||
int idx = 0;
|
||||
int slen;
|
||||
int count;
|
||||
int local = (args->os_flags & OPT_LOCAL) != 0;
|
||||
|
||||
if (curbuf == NULL)
|
||||
return FAIL;
|
||||
|
||||
clear_cpt_callbacks(&curbuf->b_p_cpt_cb, curbuf->b_p_cpt_count);
|
||||
curbuf->b_p_cpt_count = 0;
|
||||
|
||||
count = get_cpt_sources_count();
|
||||
if (count == 0)
|
||||
return OK;
|
||||
|
||||
curbuf->b_p_cpt_cb = ALLOC_CLEAR_MULT(callback_T, count);
|
||||
if (curbuf->b_p_cpt_cb == NULL)
|
||||
return FAIL;
|
||||
curbuf->b_p_cpt_count = count;
|
||||
|
||||
for (p = curbuf->b_p_cpt; *p != NUL; )
|
||||
{
|
||||
while (*p == ',' || *p == ' ')
|
||||
p++; // Skip delimiters
|
||||
|
||||
if (*p != NUL)
|
||||
{
|
||||
slen = copy_option_part(&p, buf, LSIZE, ","); // Advance p
|
||||
if (slen > 0 && buf[0] == 'F' && buf[1] != NUL)
|
||||
{
|
||||
char_u *caret;
|
||||
caret = vim_strchr(buf, '^');
|
||||
if (caret != NULL)
|
||||
*caret = NUL;
|
||||
|
||||
if (option_set_callback_func(buf + 1, &curbuf->b_p_cpt_cb[idx])
|
||||
!= OK)
|
||||
curbuf->b_p_cpt_cb[idx].cb_name = NULL;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!local) // ':set' used insted of ':setlocal'
|
||||
// Cache the callback array
|
||||
if (copy_cpt_callbacks(&cpt_cb, &cpt_cb_count, curbuf->b_p_cpt_cb,
|
||||
curbuf->b_p_cpt_count) != OK)
|
||||
return FAIL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the 'thesaurusfunc' option value and set the callback function.
|
||||
* Invoked when the 'thesaurusfunc' option is set. The option value can be a
|
||||
@ -3294,6 +3440,23 @@ did_set_thesaurusfunc(optset_T *args UNUSED)
|
||||
return retval == FAIL ? e_invalid_argument : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark "copyID" references in an array of F{func} callbacks so that they are
|
||||
* not garbage collected.
|
||||
*/
|
||||
int
|
||||
set_ref_in_cpt_callbacks(callback_T *callbacks, int count, int copyID)
|
||||
{
|
||||
int abort = FALSE;
|
||||
|
||||
if (callbacks == NULL)
|
||||
return FALSE;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
abort = abort || set_ref_in_callback(&callbacks[i], copyID);
|
||||
return abort;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the global 'completefunc' 'omnifunc' and 'thesaurusfunc' callbacks with
|
||||
* "copyID" so that they are not garbage collected.
|
||||
@ -3304,6 +3467,7 @@ set_ref_in_insexpand_funcs(int copyID)
|
||||
int abort = set_ref_in_callback(&cfu_cb, copyID);
|
||||
abort = abort || set_ref_in_callback(&ofu_cb, copyID);
|
||||
abort = abort || set_ref_in_callback(&tsrfu_cb, copyID);
|
||||
abort = abort || set_ref_in_cpt_callbacks(cpt_cb, cpt_cb_count, copyID);
|
||||
|
||||
return abort;
|
||||
}
|
||||
@ -4233,7 +4397,7 @@ process_next_cpt_value(
|
||||
else if (*st->e_cpt == 'F' || *st->e_cpt == 'o')
|
||||
{
|
||||
compl_type = CTRL_X_FUNCTION;
|
||||
st->func_cb = get_callback_if_cpt_func(st->e_cpt);
|
||||
st->func_cb = get_callback_if_cpt_func(st->e_cpt, cpt_sources_index);
|
||||
if (!st->func_cb)
|
||||
compl_type = -1;
|
||||
}
|
||||
@ -4915,31 +5079,28 @@ get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
|
||||
|
||||
#ifdef FEAT_COMPL_FUNC
|
||||
/*
|
||||
* Return the callback function associated with "p" if it points to a
|
||||
* userfunc.
|
||||
* Return the callback function associated with "p" if it refers to a
|
||||
* user-defined function in the 'complete' option.
|
||||
* The "idx" parameter is used for indexing callback entries.
|
||||
*/
|
||||
static callback_T *
|
||||
get_callback_if_cpt_func(char_u *p)
|
||||
get_callback_if_cpt_func(char_u *p, int idx)
|
||||
{
|
||||
static callback_T cb;
|
||||
char_u buf[LSIZE];
|
||||
int slen;
|
||||
|
||||
if (*p == 'o')
|
||||
return &curbuf->b_ofu_cb;
|
||||
|
||||
if (*p == 'F')
|
||||
{
|
||||
if (*++p != ',' && *p != NUL)
|
||||
{
|
||||
free_callback(&cb);
|
||||
slen = copy_option_part(&p, buf, LSIZE, ",");
|
||||
if (slen > 0 && option_set_callback_func(buf, &cb))
|
||||
return &cb;
|
||||
return NULL;
|
||||
// 'F{func}' case
|
||||
return curbuf->b_p_cpt_cb[idx].cb_name != NULL
|
||||
? &curbuf->b_p_cpt_cb[idx] : NULL;
|
||||
}
|
||||
else
|
||||
return &curbuf->b_cfu_cb;
|
||||
return &curbuf->b_cfu_cb; // 'cfu'
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
@ -5196,7 +5357,7 @@ prepare_cpt_compl_funcs(void)
|
||||
if (*p == NUL)
|
||||
break;
|
||||
|
||||
cb = get_callback_if_cpt_func(p);
|
||||
cb = get_callback_if_cpt_func(p, idx);
|
||||
if (cb)
|
||||
{
|
||||
if (get_userdefined_compl_info(curwin->w_cursor.col, cb, &startcol)
|
||||
@ -7087,6 +7248,7 @@ free_insexpand_stuff(void)
|
||||
free_callback(&cfu_cb);
|
||||
free_callback(&ofu_cb);
|
||||
free_callback(&tsrfu_cb);
|
||||
clear_cpt_callbacks(&cpt_cb, cpt_cb_count);
|
||||
# endif
|
||||
}
|
||||
#endif
|
||||
@ -7126,55 +7288,39 @@ setup_cpt_sources(void)
|
||||
{
|
||||
char_u buf[LSIZE];
|
||||
int slen;
|
||||
int count = 0, idx = 0;
|
||||
char_u *p, *cpt;
|
||||
|
||||
// Make a copy of 'cpt' in case the buffer gets wiped out
|
||||
cpt = vim_strsave(curbuf->b_p_cpt);
|
||||
if (cpt == NULL)
|
||||
return FAIL;
|
||||
|
||||
for (p = cpt; *p;)
|
||||
{
|
||||
while (*p == ',' || *p == ' ') // Skip delimiters
|
||||
p++;
|
||||
if (*p) // If not end of string, count this segment
|
||||
{
|
||||
(void)copy_option_part(&p, buf, LSIZE, ","); // Advance p
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count == 0)
|
||||
goto theend;
|
||||
int idx = 0;
|
||||
int count;
|
||||
char_u *p;
|
||||
|
||||
cpt_sources_clear();
|
||||
cpt_sources_count = count;
|
||||
|
||||
count = get_cpt_sources_count();
|
||||
if (count == 0)
|
||||
return OK;
|
||||
|
||||
cpt_sources_array = ALLOC_CLEAR_MULT(cpt_source_T, count);
|
||||
if (cpt_sources_array == NULL)
|
||||
{
|
||||
cpt_sources_count = 0;
|
||||
vim_free(cpt);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
for (p = cpt; *p;)
|
||||
for (p = curbuf->b_p_cpt; *p;)
|
||||
{
|
||||
while (*p == ',' || *p == ' ') // Skip delimiters
|
||||
p++;
|
||||
if (*p) // If not end of string, count this segment
|
||||
{
|
||||
char_u *t;
|
||||
|
||||
vim_memset(buf, 0, LSIZE);
|
||||
slen = copy_option_part(&p, buf, LSIZE, ","); // Advance p
|
||||
if (slen > 0 && (t = vim_strchr(buf, '^')) != NULL)
|
||||
cpt_sources_array[idx].cs_max_matches = atoi((char *)t + 1);
|
||||
if (slen > 0)
|
||||
{
|
||||
char_u *caret = vim_strchr(buf, '^');
|
||||
if (caret != NULL)
|
||||
cpt_sources_array[idx].cs_max_matches
|
||||
= atoi((char *)caret + 1);
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
theend:
|
||||
vim_free(cpt);
|
||||
cpt_sources_count = count;
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -7335,7 +7481,7 @@ cpt_compl_refresh(void)
|
||||
|
||||
if (cpt_sources_array[cpt_sources_index].cs_refresh_always)
|
||||
{
|
||||
cb = get_callback_if_cpt_func(p);
|
||||
cb = get_callback_if_cpt_func(p, cpt_sources_index);
|
||||
if (cb)
|
||||
{
|
||||
compl_curr_match = remove_old_matches();
|
||||
|
@ -7358,6 +7358,9 @@ buf_copy_options(buf_T *buf, int flags)
|
||||
}
|
||||
buf->b_p_cpt = vim_strsave(p_cpt);
|
||||
COPY_OPT_SCTX(buf, BV_CPT);
|
||||
#ifdef FEAT_COMPL_FUNC
|
||||
set_buflocal_cpt_callbacks(buf);
|
||||
#endif
|
||||
#ifdef BACKSLASH_IN_FILENAME
|
||||
buf->b_p_csl = vim_strsave(p_csl);
|
||||
COPY_OPT_SCTX(buf, BV_CSL);
|
||||
|
@ -252,6 +252,16 @@ illegal_char(char *errbuf, size_t errbuflen, int c)
|
||||
return errbuf;
|
||||
}
|
||||
|
||||
static char *
|
||||
illegal_char_after_chr(char *errbuf, size_t errbuflen, int c)
|
||||
{
|
||||
if (errbuf == NULL)
|
||||
return "";
|
||||
vim_snprintf(errbuf, errbuflen, _(e_illegal_character_after_chr),
|
||||
(char *)transchar(c));
|
||||
return errbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check string options in a buffer for NULL value.
|
||||
*/
|
||||
@ -1644,19 +1654,18 @@ did_set_complete(optset_T *args)
|
||||
}
|
||||
}
|
||||
if (char_before != NUL)
|
||||
{
|
||||
if (args->os_errbuf)
|
||||
{
|
||||
vim_snprintf((char *)args->os_errbuf, args->os_errbuflen,
|
||||
_(e_illegal_character_after_chr), char_before);
|
||||
return args->os_errbuf;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
return illegal_char_after_chr(args->os_errbuf, args->os_errbuflen,
|
||||
char_before);
|
||||
// Skip comma and spaces
|
||||
while (*p == ',' || *p == ' ')
|
||||
p++;
|
||||
}
|
||||
|
||||
#ifdef FEAT_COMPL_FUNC
|
||||
if (set_cpt_callbacks(args) != OK)
|
||||
return illegal_char_after_chr(args->os_errbuf, args->os_errbuflen,
|
||||
'F');
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -70,5 +70,8 @@ void free_insexpand_stuff(void);
|
||||
int ins_compl_cancel(void);
|
||||
void f_complete_match(typval_T *argvars, typval_T *rettv);
|
||||
int ins_compl_setup_autocompl(int c);
|
||||
// void ins_compl_disable_autocompl(void);
|
||||
void set_buflocal_cpt_callbacks(buf_T *buf);
|
||||
int set_cpt_callbacks(optset_T *args);
|
||||
void clear_cpt_callbacks(callback_T **cb, int count);
|
||||
int set_ref_in_cpt_callbacks(callback_T *callbacks, int count, int copyID);
|
||||
/* vim: set ft=c : */
|
||||
|
@ -3316,6 +3316,8 @@ struct file_buffer
|
||||
char_u *b_p_csl; // 'completeslash'
|
||||
#endif
|
||||
#ifdef FEAT_COMPL_FUNC
|
||||
callback_T *b_p_cpt_cb; // F{func} in 'complete' callback
|
||||
int b_p_cpt_count; // Count of values in 'complete'
|
||||
char_u *b_p_cfu; // 'completefunc'
|
||||
callback_T b_cfu_cb; // 'completefunc' callback
|
||||
char_u *b_p_ofu; // 'omnifunc'
|
||||
|
@ -198,6 +198,10 @@ func Test_omni_autoload()
|
||||
setlocal omnifunc=omni#Func
|
||||
call feedkeys("i\<C-X>\<C-O>\<Esc>", 'xt')
|
||||
|
||||
setlocal complete=.,Fomni#Func
|
||||
call feedkeys("S\<C-N>\<Esc>", 'xt')
|
||||
setlocal complete&
|
||||
|
||||
bwipe!
|
||||
set omnifunc=
|
||||
let &rtp = save_rtp
|
||||
@ -2692,6 +2696,15 @@ func Test_omnifunc_callback()
|
||||
call assert_equal([[1, ''], [0, 'script1']], g:OmniFunc3Args)
|
||||
bw!
|
||||
|
||||
set complete=Fs:OmniFunc3
|
||||
new
|
||||
call setline(1, 'script1')
|
||||
let g:OmniFunc3Args = []
|
||||
call feedkeys("A\<C-N>\<Esc>", 'x')
|
||||
call assert_equal([[1, ''], [0, 'script1']], g:OmniFunc3Args)
|
||||
bw!
|
||||
set complete&
|
||||
|
||||
let &omnifunc = 's:OmniFunc3'
|
||||
new
|
||||
call setline(1, 'script2')
|
||||
@ -5323,4 +5336,51 @@ func Test_autocomplete_timer()
|
||||
unlet g:CallCount
|
||||
endfunc
|
||||
|
||||
func s:TestCompleteScriptLocal(findstart, base)
|
||||
if a:findstart
|
||||
return 1
|
||||
else
|
||||
return ['foo', 'foobar']
|
||||
endif
|
||||
endfunc
|
||||
|
||||
" Issue 17869
|
||||
func Test_scriplocal_autoload_func()
|
||||
let save_rtp = &rtp
|
||||
set rtp=Xruntime/some
|
||||
let dir = 'Xruntime/some/autoload'
|
||||
call mkdir(dir, 'pR')
|
||||
|
||||
let lines =<< trim END
|
||||
vim9script
|
||||
export def Func(findstart: bool, base: string): any
|
||||
if findstart
|
||||
return 1
|
||||
else
|
||||
return ['match', 'matchfoo']
|
||||
endif
|
||||
enddef
|
||||
END
|
||||
call writefile(lines, dir .. '/compl.vim')
|
||||
|
||||
call test_override("char_avail", 1)
|
||||
new
|
||||
inoremap <buffer> <F2> <Cmd>let b:matches = complete_info(["matches"]).matches<CR>
|
||||
set autocomplete
|
||||
|
||||
setlocal complete=.,Fcompl#Func
|
||||
call feedkeys("im\<F2>\<Esc>0", 'xt!')
|
||||
call assert_equal(['match', 'matchfoo'], b:matches->mapnew('v:val.word'))
|
||||
|
||||
setlocal complete=.,F<SID>TestCompleteScriptLocal
|
||||
call feedkeys("Sf\<F2>\<Esc>0", 'xt!')
|
||||
call assert_equal(['foo', 'foobar'], b:matches->mapnew('v:val.word'))
|
||||
|
||||
setlocal complete&
|
||||
set autocomplete&
|
||||
bwipe!
|
||||
call test_override("char_avail", 0)
|
||||
let &rtp = save_rtp
|
||||
endfunc
|
||||
|
||||
" vim: shiftwidth=2 sts=2 expandtab nofoldenable
|
||||
|
@ -281,11 +281,17 @@ func Test_complete()
|
||||
set complete=.,w,b,u,k,s,i,d,],t,U,F,o
|
||||
set complete=.
|
||||
set complete=.^10,t^0
|
||||
set complete+=Ffuncref('foo'\\,\ [10])
|
||||
set complete=Ffuncref('foo'\\,\ [10])^10
|
||||
|
||||
func Foo(a, b)
|
||||
return ''
|
||||
endfunc
|
||||
|
||||
set complete+=Ffuncref('Foo'\\,\ [10])
|
||||
set complete=Ffuncref('Foo'\\,\ [10])^10
|
||||
set complete&
|
||||
set complete+=Ffunction('g:foo'\\,\ [10\\,\ 20])
|
||||
set complete+=Ffunction('g:Foo'\\,\ [10\\,\ 20])
|
||||
set complete&
|
||||
delfunc Foo
|
||||
endfun
|
||||
|
||||
func Test_set_completion()
|
||||
|
@ -719,6 +719,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1603,
|
||||
/**/
|
||||
1602,
|
||||
/**/
|
||||
|
Loading…
x
Reference in New Issue
Block a user