use std::fmt;use serde::{Deserialize, Serialize};use super::diff_state::{DiffHunk, DiffLineType};/** * Maximum diff size that we will attempt to render. Diffs larger than this * should not be rendered to avoid performance issues. * * Also reused as the per-file limit for base content in a remote session. * Files larger than this should not be sent over the wire and should not be rendered. */pub const MAX_DIFF_SIZE: usize = 4_375_000; // 4.375MB in decimal/** * Reasonable limit for diff size. Diffs bigger than this _could_ be displayed * but it might cause some slowness. */const MAX_REASONABLE_DIFF_SIZE: usize = 2_187_500; // ~2.1875MB in decimal/** * The longest line length we should try to display. If a diff has a line longer * than this, we don't attempt to render it. */const MAX_CHARACTERS_PER_LINE: usize = 5000;/** * Current line-based limit for auto-expansion in code review. * This exists separately from the new size-based limits. */const DIFF_LINE_RENDER_LIMIT: usize = 10_000;/** * We have a lower deletion line limit since rendering deleted chunks are more * performance intensive. */const DELETION_LINE_RENDER_LIMIT: usize = 8000;#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]pub enum DiffSize { /// Small diff that can be rendered normally Normal, /// Large diff that should be collapsed by default but can be expanded Large, /// Diff that cannot be rendered Unrenderable(UnrenderableReason),}/// Why a [`DiffSize::Unrenderable`] file cannot be rendered.#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]pub enum UnrenderableReason { /// The diff/patch itself is too large to render performantly (computed /// locally from the patch via [`compute_diff_size`]). DiffTooLarge, /// The base file content was withheld because it exceeded the per-file wire /// budget ([`MAX_DIFF_SIZE`]). Only produced when serializing a diff for a /// remote subscriber. FileTooLarge,}impl fmt::Display for UnrenderableReason { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::DiffTooLarge => write!(f, "Diff is too large to render"), Self::FileTooLarge => write!(f, "File is too large to render"), } }}/// Determines if a diff size exceeds the maximum renderable limitfn is_diff_unrenderable(buffer_length: usize) -> bool { buffer_length > MAX_DIFF_SIZE}/// Determines if a diff buffer is too large for reasonable renderingfn is_buffer_too_large(buffer_length: usize) -> bool { buffer_length >= MAX_REASONABLE_DIFF_SIZE}/// Determines if a diff has any line that's too longfn is_diff_too_large(diff: &[DiffHunk]) -> bool { diff.iter() .flat_map(|hunk| &hunk.lines) .any(|line| line.text.len() > MAX_CHARACTERS_PER_LINE)}/// Categorizes a diff based on multiple size heuristicspub fn compute_diff_size(diffs: &[DiffHunk], diff_size: usize) -> DiffSize { if is_diff_unrenderable(diff_size) { return DiffSize::Unrenderable(UnrenderableReason::DiffTooLarge); } let additions = diffs .iter() .flat_map(|hunk| &hunk.lines) .filter(|line| line.line_type == DiffLineType::Add) .count(); let deletions = diffs .iter() .flat_map(|hunk| &hunk.lines) .filter(|line| line.line_type == DiffLineType::Delete) .count(); // To avoid performance issues, set a lower render limit for deletion lines. if deletions > DELETION_LINE_RENDER_LIMIT { return DiffSize::Unrenderable(UnrenderableReason::DiffTooLarge); } if is_buffer_too_large(diff_size) || is_diff_too_large(diffs) || additions > DIFF_LINE_RENDER_LIMIT || deletions > DIFF_LINE_RENDER_LIMIT { return DiffSize::Large; } DiffSize::Normal}
use std::fmt;
use serde::{Deserialize, Serialize};
use super::diff_state::{DiffHunk, DiffLineType};
/**
* Maximum diff size that we will attempt to render. Diffs larger than this
* should not be rendered to avoid performance issues.
*
* Also reused as the per-file limit for base content in a remote session.
* Files larger than this should not be sent over the wire and should not be rendered.
*/
pub const MAX_DIFF_SIZE: usize = 4_375_000; // 4.375MB in decimal
/**
* Reasonable limit for diff size. Diffs bigger than this _could_ be displayed
* but it might cause some slowness.
*/
const MAX_REASONABLE_DIFF_SIZE: usize = 2_187_500; // ~2.1875MB in decimal
/**
* The longest line length we should try to display. If a diff has a line longer
* than this, we don't attempt to render it.
*/
const MAX_CHARACTERS_PER_LINE: usize = 5000;
/**
* Current line-based limit for auto-expansion in code review.
* This exists separately from the new size-based limits.
*/
const DIFF_LINE_RENDER_LIMIT: usize = 10_000;
/**
* We have a lower deletion line limit since rendering deleted chunks are more
* performance intensive.
*/
const DELETION_LINE_RENDER_LIMIT: usize = 8000;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum DiffSize {
/// Small diff that can be rendered normally
Normal,
/// Large diff that should be collapsed by default but can be expanded
Large,
/// Diff that cannot be rendered
Unrenderable(UnrenderableReason),
}
/// Why a [`DiffSize::Unrenderable`] file cannot be rendered.
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum UnrenderableReason {
/// The diff/patch itself is too large to render performantly (computed
/// locally from the patch via [`compute_diff_size`]).
DiffTooLarge,
/// The base file content was withheld because it exceeded the per-file wire
/// budget ([`MAX_DIFF_SIZE`]). Only produced when serializing a diff for a
/// remote subscriber.
FileTooLarge,
}
impl fmt::Display for UnrenderableReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DiffTooLarge => write!(f, "Diff is too large to render"),
Self::FileTooLarge => write!(f, "File is too large to render"),
}
}
}
/// Determines if a diff size exceeds the maximum renderable limit
fn is_diff_unrenderable(buffer_length: usize) -> bool {
buffer_length > MAX_DIFF_SIZE
}
/// Determines if a diff buffer is too large for reasonable rendering
fn is_buffer_too_large(buffer_length: usize) -> bool {
buffer_length >= MAX_REASONABLE_DIFF_SIZE
}
/// Determines if a diff has any line that's too long
fn is_diff_too_large(diff: &[DiffHunk]) -> bool {
diff.iter()
.flat_map(|hunk| &hunk.lines)
.any(|line| line.text.len() > MAX_CHARACTERS_PER_LINE)
}
/// Categorizes a diff based on multiple size heuristics
pub fn compute_diff_size(diffs: &[DiffHunk], diff_size: usize) -> DiffSize {
if is_diff_unrenderable(diff_size) {
return DiffSize::Unrenderable(UnrenderableReason::DiffTooLarge);
}
let additions = diffs
.iter()
.flat_map(|hunk| &hunk.lines)
.filter(|line| line.line_type == DiffLineType::Add)
.count();
let deletions = diffs
.iter()
.flat_map(|hunk| &hunk.lines)
.filter(|line| line.line_type == DiffLineType::Delete)
.count();
// To avoid performance issues, set a lower render limit for deletion lines.
if deletions > DELETION_LINE_RENDER_LIMIT {
return DiffSize::Unrenderable(UnrenderableReason::DiffTooLarge);
}
if is_buffer_too_large(diff_size)
|| is_diff_too_large(diffs)
|| additions > DIFF_LINE_RENDER_LIMIT
|| deletions > DIFF_LINE_RENDER_LIMIT
{
return DiffSize::Large;
}
DiffSize::Normal
}