vtcode_core/prompts/
sections.rs1#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2pub(crate) enum SectionBoundaryMode {
3 BracketOnly,
4 BracketOrMarkdown,
5}
6
7pub(crate) fn find_prompt_section_bounds(
8 prompt: &str,
9 section_header: &str,
10 boundary_mode: SectionBoundaryMode,
11) -> Option<(usize, usize)> {
12 fn is_section_header_line(line: &str, boundary_mode: SectionBoundaryMode) -> bool {
13 let trimmed = line.trim();
14 (trimmed.starts_with('[') && trimmed.ends_with(']'))
15 || matches!(boundary_mode, SectionBoundaryMode::BracketOrMarkdown)
16 && trimmed.starts_with("## ")
17 }
18
19 let mut offset = 0usize;
20 let mut section_start = None;
21
22 for line in prompt.split_inclusive('\n') {
23 let trimmed = line.trim();
24 if section_start.is_none() && trimmed == section_header {
25 section_start = Some(offset);
26 offset += line.len();
27 continue;
28 }
29
30 if let Some(start) = section_start
31 && is_section_header_line(line, boundary_mode)
32 {
33 return Some((start, offset));
34 }
35
36 offset += line.len();
37 }
38
39 section_start.map(|start| (start, prompt.len()))
40}
41
42#[cfg(test)]
43mod tests {
44 use super::{SectionBoundaryMode, find_prompt_section_bounds};
45
46 #[test]
47 fn markdown_boundary_mode_stops_before_next_markdown_heading() {
48 let prompt = "Base\n## Active Tools\n- a\n## Environment\n- b\n";
49 let bounds = find_prompt_section_bounds(
50 prompt,
51 "## Active Tools",
52 SectionBoundaryMode::BracketOrMarkdown,
53 )
54 .expect("section bounds");
55
56 assert_eq!(&prompt[bounds.0..bounds.1], "## Active Tools\n- a\n");
57 }
58
59 #[test]
60 fn bracket_only_mode_ignores_markdown_headings() {
61 let prompt = "Base\n[Harness Limits]\n- a\n## Environment\n- b\n";
62 let bounds = find_prompt_section_bounds(
63 prompt,
64 "[Harness Limits]",
65 SectionBoundaryMode::BracketOnly,
66 )
67 .expect("section bounds");
68
69 assert_eq!(
70 &prompt[bounds.0..bounds.1],
71 "[Harness Limits]\n- a\n## Environment\n- b\n"
72 );
73 }
74}