@@ -5,11 +5,8 @@ local client = require('CopilotChat.client')
5
5
local constants = require (' CopilotChat.constants' )
6
6
local notify = require (' CopilotChat.notify' )
7
7
local utils = require (' CopilotChat.utils' )
8
+ local prompts = require (' CopilotChat.prompts' )
8
9
9
- local WORD = ' ([^%s:]+)'
10
- local WORD_NO_INPUT = ' ([^%s]+)'
11
- local WORD_WITH_INPUT_QUOTED = WORD .. ' :`([^`]+)`'
12
- local WORD_WITH_INPUT_UNQUOTED = WORD .. ' :?([^%s`]*)'
13
10
local BLOCK_OUTPUT_FORMAT = ' ```%s\n %s\n ```'
14
11
15
12
--- @class CopilotChat
@@ -315,10 +312,11 @@ function M.resolve_functions(prompt, config)
315
312
tools [tool .name ] = tool
316
313
end
317
314
315
+ local refs = prompts .parse (prompt )
316
+ local found_tools = utils .to_table (config .tools )
318
317
local enabled_tools = {}
319
318
local resolved_resources = {}
320
319
local resolved_tools = {}
321
- local matches = utils .to_table (config .tools )
322
320
local tool_calls = {}
323
321
for _ , message in ipairs (M .chat .messages ) do
324
322
if message .tool_calls then
@@ -328,53 +326,30 @@ function M.resolve_functions(prompt, config)
328
326
end
329
327
end
330
328
331
- -- Check for @tool pattern to find enabled tools
332
- prompt = prompt :gsub (' @' .. WORD , function (match )
333
- for name , tool in pairs (M .config .functions ) do
334
- if name == match or tool .group == match then
335
- table.insert (matches , match )
336
- return ' '
329
+ -- Find enabled tools from @ references
330
+ for _ , ref in ipairs (refs ) do
331
+ if ref .type == ' function_reference' then
332
+ for name , tool in pairs (M .config .functions ) do
333
+ if name == ref .value or tool .group == ref .value then
334
+ table.insert (found_tools , ref .value )
335
+ end
337
336
end
338
337
end
339
- return ' @' .. match
340
- end )
341
- for _ , match in ipairs (matches ) do
338
+ end
339
+
340
+ -- Convert tool names to tool objects
341
+ for _ , match in ipairs (found_tools ) do
342
342
for name , tool in pairs (M .config .functions ) do
343
343
if name == match or tool .group == match then
344
344
table.insert (enabled_tools , tools [name ])
345
345
end
346
346
end
347
347
end
348
348
349
- local matches = utils .ordered_map ()
350
-
351
- -- Check for #word:`input` pattern
352
- for word , input in prompt :gmatch (' #' .. WORD_WITH_INPUT_QUOTED ) do
353
- local pattern = string.format (' #%s:`%s`' , word , input )
354
- matches :set (pattern , {
355
- word = word ,
356
- input = input ,
357
- })
358
- end
359
-
360
- -- Check for #word:input pattern
361
- for word , input in prompt :gmatch (' #' .. WORD_WITH_INPUT_UNQUOTED ) do
362
- local pattern = utils .empty (input ) and string.format (' #%s' , word ) or string.format (' #%s:%s' , word , input )
363
- matches :set (pattern , {
364
- word = word ,
365
- input = input ,
366
- })
367
- end
368
-
369
- -- Check for ##word:input pattern
370
- for word in prompt :gmatch (' ##' .. WORD_NO_INPUT ) do
371
- local pattern = string.format (' ##%s' , word )
372
- matches :set (pattern , {
373
- word = word ,
374
- })
375
- end
376
-
377
- -- Resolve each function reference
349
+ -- Helper to resolve function calls
350
+ --- @param name string
351
+ --- @param input string
352
+ --- @return string | nil
378
353
local function expand_function (name , input )
379
354
notify .publish (notify .STATUS , ' Running function: ' .. name )
380
355
@@ -450,15 +425,11 @@ function M.resolve_functions(prompt, config)
450
425
return result
451
426
end
452
427
453
- -- Resolve and process all tools
454
- for _ , pattern in ipairs (matches :keys ()) do
455
- if not utils .empty (pattern ) then
456
- local match = matches :get (pattern )
457
- local out = expand_function (match .word , match .input ) or pattern
458
- out = out :gsub (' %%' , ' %%%%' ) -- Escape percent signs for gsub
459
- prompt = prompt :gsub (vim .pesc (pattern ), out , 1 )
428
+ prompt = prompts .replace (prompt , refs , function (ref )
429
+ if ref .type == ' function_call' then
430
+ return expand_function (ref .value , ref .input )
460
431
end
461
- end
432
+ end )
462
433
463
434
return enabled_tools , resolved_resources , resolved_tools , prompt
464
435
end
@@ -479,45 +450,45 @@ function M.resolve_prompt(prompt, config)
479
450
local depth = 0
480
451
local MAX_DEPTH = 10
481
452
482
- local function resolve (inner_config , inner_prompt )
483
- if depth >= MAX_DEPTH then
453
+ local function resolve_prompt_template (inner_config , inner_prompt )
454
+ if depth >= MAX_DEPTH or not inner_prompt then
484
455
return inner_config , inner_prompt
485
456
end
457
+
486
458
depth = depth + 1
487
459
488
- inner_prompt = string.gsub (inner_prompt , ' /' .. WORD , function (match )
489
- local p = prompts_to_use [match ]
490
- if p then
491
- local resolved_config , resolved_prompt = resolve (p , p .prompt or ' ' )
492
- inner_config = vim .tbl_deep_extend (' force' , inner_config , resolved_config )
493
- return resolved_prompt
460
+ for _ , ref in ipairs (prompts .parse (inner_prompt )) do
461
+ if ref .type == ' prompt' then
462
+ vim .print (ref )
463
+ local template = prompts_to_use [ref .value ]
464
+ if template then
465
+ local resolved_config , resolved_prompt = resolve_prompt_template (template , template .prompt )
466
+ inner_config = vim .tbl_deep_extend (' force' , inner_config , resolved_config )
467
+ if resolved_prompt then
468
+ inner_prompt = inner_prompt :sub (1 , ref .start_pos - 1 )
469
+ .. resolved_prompt
470
+ .. inner_prompt :sub (ref .end_pos + 1 )
471
+ end
472
+ end
494
473
end
495
-
496
- return ' /' .. match
497
- end )
474
+ end
498
475
499
476
depth = depth - 1
500
477
return inner_config , inner_prompt
501
478
end
502
479
503
- local function resolve_system_prompt (system_prompt )
504
- if type (system_prompt ) == ' function' then
505
- local ok , result = pcall (system_prompt )
506
- if not ok then
507
- log .warn (' Failed to resolve system prompt function: ' .. result )
508
- return nil
509
- end
510
- return result
511
- end
512
-
513
- return system_prompt
514
- end
515
-
516
480
config = vim .tbl_deep_extend (' force' , M .config , config or {})
517
- config , prompt = resolve (config , prompt or ' ' )
481
+ config , prompt = resolve_prompt_template (config , prompt )
482
+ prompt = prompt or ' '
518
483
519
484
if config .system_prompt then
520
- config .system_prompt = resolve_system_prompt (config .system_prompt )
485
+ if type (config .system_prompt ) == ' function' then
486
+ --- @diagnostic disable-next-line : param-type-mismatch
487
+ local ok , result = pcall (config .system_prompt )
488
+ if ok then
489
+ config .system_prompt = result
490
+ end
491
+ end
521
492
522
493
if M .config .prompts [config .system_prompt ] then
523
494
-- Name references are good for making system prompt auto sticky
@@ -547,13 +518,16 @@ function M.resolve_model(prompt, config)
547
518
return model .id
548
519
end , list_models ())
549
520
521
+ local refs = prompts .parse (prompt )
550
522
local selected_model = config .model or ' '
551
- prompt = prompt :gsub (' %$' .. WORD , function (match )
552
- if vim .tbl_contains (models , match ) then
553
- selected_model = match
554
- return ' '
523
+
524
+ prompt = prompts .replace (prompt , refs , function (ref )
525
+ if ref .type == ' model' then
526
+ if vim .tbl_contains (models , ref .value ) then
527
+ selected_model = ref .value
528
+ return ' '
529
+ end
555
530
end
556
- return ' $' .. match
557
531
end )
558
532
559
533
return selected_model , prompt
0 commit comments