@@ -273,7 +273,7 @@ static void ins_compl_add_list(list_T *list);
273273static void ins_compl_add_dict (dict_T * dict );
274274static int get_userdefined_compl_info (colnr_T curs_col , callback_T * cb , int * startcol );
275275static void get_cpt_func_completion_matches (callback_T * cb );
276- static callback_T * get_callback_if_cpt_func (char_u * p );
276+ static callback_T * get_callback_if_cpt_func (char_u * p , int idx );
277277# endif
278278static int setup_cpt_sources (void );
279279static int is_cpt_func_refresh_always (void );
@@ -3191,12 +3191,40 @@ ins_compl_next_buf(buf_T *buf, int flag)
31913191 return buf ;
31923192}
31933193
3194+ /*
3195+ * Count the number of entries in the 'complete' option (curbuf->b_p_cpt).
3196+ * Each non-empty, comma-separated segment is counted as one entry.
3197+ */
3198+ static int
3199+ get_cpt_sources_count (void )
3200+ {
3201+ char_u dummy [LSIZE ];
3202+ int count = 0 ;
3203+ char_u * p ;
3204+
3205+ for (p = curbuf -> b_p_cpt ; * p != NUL ; )
3206+ {
3207+ while (* p == ',' || * p == ' ' )
3208+ p ++ ; // Skip delimiters
3209+
3210+ if (* p != NUL )
3211+ {
3212+ (void )copy_option_part (& p , dummy , LSIZE , "," ); // Advance p
3213+ count ++ ;
3214+ }
3215+ }
3216+
3217+ return count ;
3218+ }
3219+
31943220#ifdef FEAT_COMPL_FUNC
31953221
31963222# ifdef FEAT_EVAL
31973223static callback_T cfu_cb ; // 'completefunc' callback function
31983224static callback_T ofu_cb ; // 'omnifunc' callback function
31993225static callback_T tsrfu_cb ; // 'thesaurusfunc' callback function
3226+ static callback_T * cpt_cb ; // Callback functions associated with F{func}
3227+ static int cpt_cb_count ; // Number of cpt callbacks
32003228# endif
32013229
32023230/*
@@ -3267,6 +3295,124 @@ set_buflocal_ofu_callback(buf_T *buf UNUSED)
32673295# endif
32683296}
32693297
3298+ /*
3299+ * Free an array of 'complete' F{func} callbacks and set the pointer to NULL.
3300+ */
3301+ void
3302+ clear_cpt_callbacks (callback_T * * callbacks , int count )
3303+ {
3304+ if (callbacks == NULL || * callbacks == NULL )
3305+ return ;
3306+
3307+ for (int i = 0 ; i < count ; i ++ )
3308+ free_callback (& (* callbacks )[i ]);
3309+
3310+ VIM_CLEAR (* callbacks );
3311+ }
3312+
3313+ /*
3314+ * Copies a list of callback_T structs from src to *dest, clearing any existing
3315+ * entries and allocating memory for the destination.
3316+ */
3317+ static int
3318+ copy_cpt_callbacks (callback_T * * dest , int * dest_cnt , callback_T * src , int cnt )
3319+ {
3320+ if (cnt == 0 )
3321+ return OK ;
3322+
3323+ clear_cpt_callbacks (dest , * dest_cnt );
3324+ * dest_cnt = 0 ;
3325+
3326+ * dest = ALLOC_CLEAR_MULT (callback_T , cnt );
3327+ if (* dest == NULL )
3328+ return FAIL ;
3329+
3330+ * dest_cnt = cnt ;
3331+
3332+ for (int i = 0 ; i < cnt ; i ++ )
3333+ if (src [i ].cb_name != NULL && * (src [i ].cb_name ) != NUL )
3334+ copy_callback (& (* dest )[i ], & src [i ]);
3335+
3336+ return OK ;
3337+ }
3338+
3339+ /*
3340+ * Copy global 'complete' F{func} callbacks into the given buffer's local
3341+ * callback array. Clears any existing buffer-local callbacks first.
3342+ */
3343+ void
3344+ set_buflocal_cpt_callbacks (buf_T * buf UNUSED )
3345+ {
3346+ #ifdef FEAT_EVAL
3347+ if (buf == NULL || cpt_cb_count == 0 )
3348+ return ;
3349+ (void )copy_cpt_callbacks (& buf -> b_p_cpt_cb , & buf -> b_p_cpt_count , cpt_cb ,
3350+ cpt_cb_count );
3351+ #endif
3352+ }
3353+
3354+ /*
3355+ * Parse 'complete' option and initialize F{func} callbacks.
3356+ * Frees any existing callbacks and allocates new ones.
3357+ * Only F{func} entries are processed; others are ignored.
3358+ */
3359+ int
3360+ set_cpt_callbacks (optset_T * args )
3361+ {
3362+ char_u buf [LSIZE ];
3363+ char_u * p ;
3364+ int idx = 0 ;
3365+ int slen ;
3366+ int count ;
3367+ int local = (args -> os_flags & OPT_LOCAL ) != 0 ;
3368+
3369+ if (curbuf == NULL )
3370+ return FAIL ;
3371+
3372+ clear_cpt_callbacks (& curbuf -> b_p_cpt_cb , curbuf -> b_p_cpt_count );
3373+ curbuf -> b_p_cpt_count = 0 ;
3374+
3375+ count = get_cpt_sources_count ();
3376+ if (count == 0 )
3377+ return OK ;
3378+
3379+ curbuf -> b_p_cpt_cb = ALLOC_CLEAR_MULT (callback_T , count );
3380+ if (curbuf -> b_p_cpt_cb == NULL )
3381+ return FAIL ;
3382+ curbuf -> b_p_cpt_count = count ;
3383+
3384+ for (p = curbuf -> b_p_cpt ; * p != NUL ; )
3385+ {
3386+ while (* p == ',' || * p == ' ' )
3387+ p ++ ; // Skip delimiters
3388+
3389+ if (* p != NUL )
3390+ {
3391+ slen = copy_option_part (& p , buf , LSIZE , "," ); // Advance p
3392+ if (slen > 0 && buf [0 ] == 'F' && buf [1 ] != NUL )
3393+ {
3394+ char_u * caret ;
3395+ caret = vim_strchr (buf , '^' );
3396+ if (caret != NULL )
3397+ * caret = NUL ;
3398+
3399+ if (option_set_callback_func (buf + 1 , & curbuf -> b_p_cpt_cb [idx ])
3400+ != OK )
3401+ curbuf -> b_p_cpt_cb [idx ].cb_name = NULL ;
3402+ }
3403+ idx ++ ;
3404+ }
3405+ }
3406+
3407+ if (!local ) // ':set' used insted of ':setlocal'
3408+ // Cache the callback array
3409+ if (copy_cpt_callbacks (& cpt_cb , & cpt_cb_count , curbuf -> b_p_cpt_cb ,
3410+ curbuf -> b_p_cpt_count ) != OK )
3411+ return FAIL ;
3412+
3413+ return OK ;
3414+ }
3415+
32703416/*
32713417 * Parse the 'thesaurusfunc' option value and set the callback function.
32723418 * Invoked when the 'thesaurusfunc' option is set. The option value can be a
@@ -3294,6 +3440,23 @@ did_set_thesaurusfunc(optset_T *args UNUSED)
32943440 return retval == FAIL ? e_invalid_argument : NULL ;
32953441}
32963442
3443+ /*
3444+ * Mark "copyID" references in an array of F{func} callbacks so that they are
3445+ * not garbage collected.
3446+ */
3447+ int
3448+ set_ref_in_cpt_callbacks (callback_T * callbacks , int count , int copyID )
3449+ {
3450+ int abort = FALSE;
3451+
3452+ if (callbacks == NULL )
3453+ return FALSE;
3454+
3455+ for (int i = 0 ; i < count ; i ++ )
3456+ abort = abort || set_ref_in_callback (& callbacks [i ], copyID );
3457+ return abort ;
3458+ }
3459+
32973460/*
32983461 * Mark the global 'completefunc' 'omnifunc' and 'thesaurusfunc' callbacks with
32993462 * "copyID" so that they are not garbage collected.
@@ -3304,6 +3467,7 @@ set_ref_in_insexpand_funcs(int copyID)
33043467 int abort = set_ref_in_callback (& cfu_cb , copyID );
33053468 abort = abort || set_ref_in_callback (& ofu_cb , copyID );
33063469 abort = abort || set_ref_in_callback (& tsrfu_cb , copyID );
3470+ abort = abort || set_ref_in_cpt_callbacks (cpt_cb , cpt_cb_count , copyID );
33073471
33083472 return abort ;
33093473}
@@ -4233,7 +4397,7 @@ process_next_cpt_value(
42334397 else if (* st -> e_cpt == 'F ' || * st -> e_cpt == 'o' )
42344398 {
42354399 compl_type = CTRL_X_FUNCTION ;
4236- st -> func_cb = get_callback_if_cpt_func (st -> e_cpt );
4400+ st -> func_cb = get_callback_if_cpt_func (st -> e_cpt , cpt_sources_index );
42374401 if (!st -> func_cb )
42384402 compl_type = -1 ;
42394403 }
@@ -4915,31 +5079,28 @@ get_next_default_completion(ins_compl_next_state_T *st, pos_T *start_pos)
49155079
49165080#ifdef FEAT_COMPL_FUNC
49175081/*
4918- * Return the callback function associated with "p" if it points to a
4919- * userfunc.
5082+ * Return the callback function associated with "p" if it refers to a
5083+ * user-defined function in the 'complete' option.
5084+ * The "idx" parameter is used for indexing callback entries.
49205085 */
49215086 static callback_T *
4922- get_callback_if_cpt_func (char_u * p )
5087+ get_callback_if_cpt_func (char_u * p , int idx )
49235088{
4924- static callback_T cb ;
4925- char_u buf [LSIZE ];
4926- int slen ;
4927-
49285089 if (* p == 'o' )
49295090 return & curbuf -> b_ofu_cb ;
5091+
49305092 if (* p == 'F' )
49315093 {
49325094 if (* ++ p != ',' && * p != NUL )
49335095 {
4934- free_callback (& cb );
4935- slen = copy_option_part (& p , buf , LSIZE , "," );
4936- if (slen > 0 && option_set_callback_func (buf , & cb ))
4937- return & cb ;
4938- return NULL ;
5096+ // 'F{func}' case
5097+ return curbuf -> b_p_cpt_cb [idx ].cb_name != NULL
5098+ ? & curbuf -> b_p_cpt_cb [idx ] : NULL ;
49395099 }
49405100 else
4941- return & curbuf -> b_cfu_cb ;
5101+ return & curbuf -> b_cfu_cb ; // 'cfu'
49425102 }
5103+
49435104 return NULL ;
49445105}
49455106#endif
@@ -5196,7 +5357,7 @@ prepare_cpt_compl_funcs(void)
51965357 if (* p == NUL )
51975358 break ;
51985359
5199- cb = get_callback_if_cpt_func (p );
5360+ cb = get_callback_if_cpt_func (p , idx );
52005361 if (cb )
52015362 {
52025363 if (get_userdefined_compl_info (curwin -> w_cursor .col , cb , & startcol )
@@ -7087,6 +7248,7 @@ free_insexpand_stuff(void)
70877248 free_callback (& cfu_cb );
70887249 free_callback (& ofu_cb );
70897250 free_callback (& tsrfu_cb );
7251+ clear_cpt_callbacks (& cpt_cb , cpt_cb_count );
70907252# endif
70917253}
70927254#endif
@@ -7126,55 +7288,39 @@ setup_cpt_sources(void)
71267288{
71277289 char_u buf [LSIZE ];
71287290 int slen ;
7129- int count = 0 , idx = 0 ;
7130- char_u * p , * cpt ;
7291+ int idx = 0 ;
7292+ int count ;
7293+ char_u * p ;
71317294
7132- // Make a copy of 'cpt' in case the buffer gets wiped out
7133- cpt = vim_strsave (curbuf -> b_p_cpt );
7134- if (cpt == NULL )
7135- return FAIL ;
7295+ cpt_sources_clear ();
71367296
7137- for (p = cpt ; * p ;)
7138- {
7139- while (* p == ',' || * p == ' ' ) // Skip delimiters
7140- p ++ ;
7141- if (* p ) // If not end of string, count this segment
7142- {
7143- (void )copy_option_part (& p , buf , LSIZE , "," ); // Advance p
7144- count ++ ;
7145- }
7146- }
7297+ count = get_cpt_sources_count ();
71477298 if (count == 0 )
7148- goto theend ;
7299+ return OK ;
71497300
7150- cpt_sources_clear ();
7151- cpt_sources_count = count ;
71527301 cpt_sources_array = ALLOC_CLEAR_MULT (cpt_source_T , count );
71537302 if (cpt_sources_array == NULL )
7154- {
7155- cpt_sources_count = 0 ;
7156- vim_free (cpt );
71577303 return FAIL ;
7158- }
71597304
7160- for (p = cpt ; * p ;)
7305+ for (p = curbuf -> b_p_cpt ; * p ;)
71617306 {
71627307 while (* p == ',' || * p == ' ' ) // Skip delimiters
71637308 p ++ ;
71647309 if (* p ) // If not end of string, count this segment
71657310 {
7166- char_u * t ;
7167-
7168- vim_memset (buf , 0 , LSIZE );
71697311 slen = copy_option_part (& p , buf , LSIZE , "," ); // Advance p
7170- if (slen > 0 && (t = vim_strchr (buf , '^' )) != NULL )
7171- cpt_sources_array [idx ].cs_max_matches = atoi ((char * )t + 1 );
7312+ if (slen > 0 )
7313+ {
7314+ char_u * caret = vim_strchr (buf , '^' );
7315+ if (caret != NULL )
7316+ cpt_sources_array [idx ].cs_max_matches
7317+ = atoi ((char * )caret + 1 );
7318+ }
71727319 idx ++ ;
71737320 }
71747321 }
71757322
7176- theend :
7177- vim_free (cpt );
7323+ cpt_sources_count = count ;
71787324 return OK ;
71797325}
71807326
@@ -7335,7 +7481,7 @@ cpt_compl_refresh(void)
73357481
73367482 if (cpt_sources_array [cpt_sources_index ].cs_refresh_always )
73377483 {
7338- cb = get_callback_if_cpt_func (p );
7484+ cb = get_callback_if_cpt_func (p , cpt_sources_index );
73397485 if (cb )
73407486 {
73417487 compl_curr_match = remove_old_matches ();
0 commit comments