cursor_excerpt.rs

 1use language::{BufferSnapshot, Point};
 2use std::ops::Range;
 3
 4pub fn editable_and_context_ranges_for_cursor_position(
 5    position: Point,
 6    snapshot: &BufferSnapshot,
 7    editable_region_token_limit: usize,
 8    context_token_limit: usize,
 9) -> (Range<Point>, Range<Point>) {
10    let mut scope_range = position..position;
11    let mut remaining_edit_tokens = editable_region_token_limit;
12
13    while let Some(parent) = snapshot.syntax_ancestor(scope_range.clone()) {
14        let parent_tokens = guess_token_count(parent.byte_range().len());
15        let parent_point_range = Point::new(
16            parent.start_position().row as u32,
17            parent.start_position().column as u32,
18        )
19            ..Point::new(
20                parent.end_position().row as u32,
21                parent.end_position().column as u32,
22            );
23        if parent_point_range == scope_range {
24            break;
25        } else if parent_tokens <= editable_region_token_limit {
26            scope_range = parent_point_range;
27            remaining_edit_tokens = editable_region_token_limit - parent_tokens;
28        } else {
29            break;
30        }
31    }
32
33    let editable_range = expand_range(snapshot, scope_range, remaining_edit_tokens);
34    let context_range = expand_range(snapshot, editable_range.clone(), context_token_limit);
35    (editable_range, context_range)
36}
37
38fn expand_range(
39    snapshot: &BufferSnapshot,
40    range: Range<Point>,
41    mut remaining_tokens: usize,
42) -> Range<Point> {
43    let mut expanded_range = range;
44    expanded_range.start.column = 0;
45    expanded_range.end.column = snapshot.line_len(expanded_range.end.row);
46    loop {
47        let mut expanded = false;
48
49        if remaining_tokens > 0 && expanded_range.start.row > 0 {
50            expanded_range.start.row -= 1;
51            let line_tokens =
52                guess_token_count(snapshot.line_len(expanded_range.start.row) as usize);
53            remaining_tokens = remaining_tokens.saturating_sub(line_tokens);
54            expanded = true;
55        }
56
57        if remaining_tokens > 0 && expanded_range.end.row < snapshot.max_point().row {
58            expanded_range.end.row += 1;
59            expanded_range.end.column = snapshot.line_len(expanded_range.end.row);
60            let line_tokens = guess_token_count(expanded_range.end.column as usize);
61            remaining_tokens = remaining_tokens.saturating_sub(line_tokens);
62            expanded = true;
63        }
64
65        if !expanded {
66            break;
67        }
68    }
69    expanded_range
70}
71
72/// Typical number of string bytes per token for the purposes of limiting model input. This is
73/// intentionally low to err on the side of underestimating limits.
74pub(crate) const BYTES_PER_TOKEN_GUESS: usize = 3;
75
76pub fn guess_token_count(bytes: usize) -> usize {
77    bytes / BYTES_PER_TOKEN_GUESS
78}