Initial public release of Warp.

Repo-Sync-Origin: warpdotdev/warp-internal@12af1d983b
This commit is contained in:
David Stern
2026-04-28 08:43:33 -05:00
commit 0dbd3d567a
4982 changed files with 1431549 additions and 0 deletions
+263
View File
@@ -0,0 +1,263 @@
use std::ops::Range;
use ai::diff_validation::DiffDelta;
use rangemap::RangeSet;
use warp_editor::content::text::LineCount;
use warp_editor::render::model::LineCount as RenderLineCount;
/// The number of context lines to show before and after each change
const CONTEXT_LINES: usize = 4;
/// Calculate which lines should be hidden in a file after applying diffs.
///
/// This function takes a list of diff deltas and comment line numbers, then calculates which lines should be hidden
/// (everything except for CONTEXT_LINES before and after each change and comment).
///
/// These are 0-indexed line numbers BEFORE the diffs are applied, so the first line is 0.
///
/// # Arguments
///
/// * `diffs` - The list of diff deltas that will be applied to the file
/// * `line_count` - The total number of lines in the file before any diffs are applied
/// * `comment_line_numbers` - Line numbers where comments exist (0-indexed)
///
/// # Returns
///
/// A `RangeSet<usize>` containing the line ranges that should be hidden (0-indexed).
///
/// Note that DiffDelta uses 1-indexed line ranges, so we convert them to 0-indexed
/// ```
pub fn calculate_hidden_lines(
diffs: &[DiffDelta],
line_count: usize,
comment_line_numbers: &[RenderLineCount],
) -> RangeSet<LineCount> {
// Calculate the visible line ranges (with context)
let mut visible_ranges: RangeSet<LineCount> = RangeSet::new();
// Add ranges for diffs
for diff in diffs {
// Convert 1-indexed line ranges to 0-indexed
let start_line = diff.replacement_line_range.start.saturating_sub(1);
let end_line = diff.replacement_line_range.end.saturating_sub(1);
let context_start = start_line.saturating_sub(CONTEXT_LINES);
let context_end = end_line + CONTEXT_LINES;
if context_start < context_end {
visible_ranges.insert(context_start.into()..context_end.into());
}
}
// Add ranges for comments
for &comment_line in comment_line_numbers {
let line_number = comment_line.as_usize();
let context_start = line_number.saturating_sub(CONTEXT_LINES);
let context_end = (line_number + CONTEXT_LINES + 1).min(line_count); // +1 because we want to include the line itself, clamped to file bounds
if context_start < context_end {
visible_ranges.insert(LineCount::from(context_start)..LineCount::from(context_end));
}
}
// Calculate hidden ranges as the complement of visible ranges
let all_lines: Range<LineCount> = LineCount::from(0)..LineCount::from(line_count);
// Find gaps in the visible ranges
visible_ranges
.gaps(&all_lines)
.collect::<RangeSet<LineCount>>()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_diffs() {
let hidden_lines = calculate_hidden_lines(&[], 20, &[]);
let expected_hidden = [LineCount::from(0)..LineCount::from(20)]
.into_iter()
.collect::<RangeSet<LineCount>>();
assert_eq!(hidden_lines, expected_hidden);
}
#[test]
fn test_single_diff_middle_of_file() {
// File with 20 lines, change at lines 10-12 (1-indexed)
let diffs = vec![DiffDelta {
replacement_line_range: 10..13, // 1-indexed, replacing lines 10, 11, 12
insertion: "new line 1\nnew line 2\nnew line 3".to_string(), // 3 lines
}];
let hidden_lines = calculate_hidden_lines(&diffs, 20, &[]);
let mut expected_hidden = RangeSet::new();
expected_hidden.insert(0.into()..5.into()); // lines 1-5 (1-indexed)
expected_hidden.insert(16.into()..20.into()); // lines 17-20 (1-indexed)
assert_eq!(hidden_lines, expected_hidden);
}
#[test]
fn test_insertion_at_beginning() {
// Insert at the very beginning of file
let diffs = vec![DiffDelta {
replacement_line_range: 1..1, // Insert at beginning
insertion: "new line 1\nnew line 2".to_string(), // 2 lines
}];
let hidden_lines = calculate_hidden_lines(&diffs, 20, &[]);
let mut expected_hidden = RangeSet::new();
expected_hidden.insert(4.into()..20.into());
assert_eq!(hidden_lines, expected_hidden);
}
#[test]
fn test_multiple_diffs_overlapping_context() {
// Two changes close enough that their context overlaps
let diffs = vec![
DiffDelta {
replacement_line_range: 5..6, // 1-indexed, replace line 5
insertion: "change 1".to_string(), // 1 line
},
DiffDelta {
replacement_line_range: 8..9, // 1-indexed, replace line 8
insertion: "change 2".to_string(), // 1 line
},
];
let hidden_lines = calculate_hidden_lines(&diffs, 20, &[]);
let mut expected_hidden = RangeSet::new();
expected_hidden.insert(12.into()..20.into());
assert_eq!(hidden_lines, expected_hidden);
}
#[test]
fn test_multiple_diffs_separate_context() {
// Two changes far apart
let diffs = vec![
DiffDelta {
replacement_line_range: 3..4, // 1-indexed, replace line 3
insertion: "change 1".to_string(), // 1 line
},
DiffDelta {
replacement_line_range: 15..16, // 1-indexed, replace line 15
insertion: "change 2".to_string(), // 1 line
},
];
let hidden_lines = calculate_hidden_lines(&diffs, 20, &[]);
let mut expected_hidden = RangeSet::new();
expected_hidden.insert(7.into()..10.into());
expected_hidden.insert(19.into()..20.into());
assert_eq!(hidden_lines, expected_hidden);
}
#[test]
fn test_comments_only_no_diffs() {
// File with 20 lines, comments at lines 5 and 15 (0-indexed)
let comment_lines = vec![RenderLineCount::from(5), RenderLineCount::from(15)];
let hidden_lines = calculate_hidden_lines(&[], 20, &comment_lines);
let mut expected_hidden = RangeSet::new();
// Comment at line 5 shows lines 1-9 (0-indexed), so hide lines 0 and 10-14
expected_hidden.insert(0.into()..1.into());
expected_hidden.insert(10.into()..11.into());
// Comment at line 15 shows lines 11-19 (0-indexed), so hide line 20
// Note: lines 10-19 are visible due to comment at line 15
assert_eq!(hidden_lines, expected_hidden);
}
#[test]
fn test_comment_overlapping_with_diff_context() {
// File with 20 lines, diff at lines 8-9 (1-indexed) and comment at line 10 (0-indexed)
let diffs = vec![DiffDelta {
replacement_line_range: 8..10, // 1-indexed, replacing lines 8, 9
insertion: "new line 1\nnew line 2".to_string(),
}];
let comment_lines = vec![RenderLineCount::from(10)];
let hidden_lines = calculate_hidden_lines(&diffs, 20, &comment_lines);
// Diff at lines 8-9 (1-indexed) = lines 7-8 (0-indexed) with ±4 context = lines 3-12
// Comment at line 10 (0-indexed) with ±4 context = lines 6-14
// Combined visible range: lines 3-14
let mut expected_hidden = RangeSet::new();
expected_hidden.insert(0.into()..3.into());
expected_hidden.insert(15.into()..20.into());
assert_eq!(hidden_lines, expected_hidden);
}
#[test]
fn test_comment_separate_from_diffs() {
// File with 30 lines, diff at lines 5-6 (1-indexed) and comment at line 20 (0-indexed)
let diffs = vec![DiffDelta {
replacement_line_range: 5..7, // 1-indexed, replacing lines 5, 6
insertion: "changed line".to_string(),
}];
let comment_lines = vec![RenderLineCount::from(20)];
let hidden_lines = calculate_hidden_lines(&diffs, 30, &comment_lines);
// Diff at lines 5-6 (1-indexed) = lines 4-5 (0-indexed) with ±4 context = lines 0-9
// Comment at line 20 (0-indexed) with ±4 context = lines 16-24
let mut expected_hidden = RangeSet::new();
expected_hidden.insert(10.into()..16.into());
expected_hidden.insert(25.into()..30.into());
assert_eq!(hidden_lines, expected_hidden);
}
#[test]
fn test_comment_outside_file_bounds() {
// File with 10 lines, comment at line 15 (0-indexed) - outside bounds
let comment_lines = vec![RenderLineCount::from(15)];
let hidden_lines = calculate_hidden_lines(&[], 10, &comment_lines);
// Comment is outside file bounds, so everything should be hidden
let mut expected_hidden = RangeSet::new();
expected_hidden.insert(0.into()..10.into());
assert_eq!(hidden_lines, expected_hidden);
}
#[test]
fn test_comment_at_file_beginning() {
// File with 20 lines, comment at line 1 (0-indexed)
let comment_lines = vec![RenderLineCount::from(1)];
let hidden_lines = calculate_hidden_lines(&[], 20, &comment_lines);
// Comment at line 1 with ±4 context = lines 0-5 (saturating_sub handles negative)
let mut expected_hidden = RangeSet::new();
expected_hidden.insert(6.into()..20.into());
assert_eq!(hidden_lines, expected_hidden);
}
#[test]
fn test_comment_at_file_end() {
// File with 20 lines, comment at line 18 (0-indexed)
let comment_lines = vec![RenderLineCount::from(18)];
let hidden_lines = calculate_hidden_lines(&[], 20, &comment_lines);
// Comment at line 18 with ±4 context = lines 14-19 (clamped to file bounds)
let mut expected_hidden = RangeSet::new();
expected_hidden.insert(0.into()..14.into());
assert_eq!(hidden_lines, expected_hidden);
}
}