120 lines · 3.8 KB · Rust
RawDownload
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}