1- import { type FileContents , File , FileOptions , LineAnnotation , type SelectedLineRange } from "@pierre/diffs"
1+ import {
2+ DEFAULT_VIRTUAL_FILE_METRICS ,
3+ type FileContents ,
4+ File ,
5+ FileOptions ,
6+ LineAnnotation ,
7+ type SelectedLineRange ,
8+ type VirtualFileMetrics ,
9+ VirtualizedFile ,
10+ Virtualizer ,
11+ } from "@pierre/diffs"
212import { ComponentProps , createEffect , createMemo , createSignal , onCleanup , onMount , Show , splitProps } from "solid-js"
313import { Portal } from "solid-js/web"
414import { createDefaultOptions , styleVariables } from "../pierre"
515import { getWorkerPool } from "../pierre/worker"
616import { Icon } from "./icon"
717
18+ const VIRTUALIZE_BYTES = 500_000
19+ const codeMetrics = {
20+ ...DEFAULT_VIRTUAL_FILE_METRICS ,
21+ lineHeight : 24 ,
22+ fileGap : 0 ,
23+ } satisfies Partial < VirtualFileMetrics >
24+
825type SelectionSide = "additions" | "deletions"
926
1027export type CodeProps < T = { } > = FileOptions < T > & {
@@ -160,16 +177,28 @@ export function Code<T>(props: CodeProps<T>) {
160177
161178 const [ findPos , setFindPos ] = createSignal < { top : number ; right : number } > ( { top : 8 , right : 8 } )
162179
163- const file = createMemo (
164- ( ) =>
165- new File < T > (
166- {
167- ...createDefaultOptions < T > ( "unified" ) ,
168- ...others ,
169- } ,
170- getWorkerPool ( "unified" ) ,
171- ) ,
172- )
180+ let instance : File < T > | VirtualizedFile < T > | undefined
181+ let virtualizer : Virtualizer | undefined
182+ let virtualRoot : Document | HTMLElement | undefined
183+
184+ const bytes = createMemo ( ( ) => {
185+ const value = local . file . contents as unknown
186+ if ( typeof value === "string" ) return value . length
187+ if ( Array . isArray ( value ) ) {
188+ return value . reduce (
189+ ( acc , part ) => acc + ( typeof part === "string" ? part . length + 1 : String ( part ) . length + 1 ) ,
190+ 0 ,
191+ )
192+ }
193+ if ( value == null ) return 0
194+ return String ( value ) . length
195+ } )
196+ const virtual = createMemo ( ( ) => bytes ( ) > VIRTUALIZE_BYTES )
197+
198+ const options = createMemo ( ( ) => ( {
199+ ...createDefaultOptions < T > ( "unified" ) ,
200+ ...others ,
201+ } ) )
173202
174203 const getRoot = ( ) => {
175204 const host = container . querySelector ( "diffs-container" )
@@ -577,27 +606,35 @@ export function Code<T>(props: CodeProps<T>) {
577606 }
578607
579608 const applySelection = ( range : SelectedLineRange | null ) => {
609+ const current = instance
610+ if ( ! current ) return false
611+
612+ if ( virtual ( ) ) {
613+ current . setSelectedLines ( range )
614+ return true
615+ }
616+
580617 const root = getRoot ( )
581618 if ( ! root ) return false
582619
583620 const lines = lineCount ( )
584621 if ( root . querySelectorAll ( "[data-line]" ) . length < lines ) return false
585622
586623 if ( ! range ) {
587- file ( ) . setSelectedLines ( null )
624+ current . setSelectedLines ( null )
588625 return true
589626 }
590627
591628 const start = Math . min ( range . start , range . end )
592629 const end = Math . max ( range . start , range . end )
593630
594631 if ( start < 1 || end > lines ) {
595- file ( ) . setSelectedLines ( null )
632+ current . setSelectedLines ( null )
596633 return true
597634 }
598635
599636 if ( ! root . querySelector ( `[data-line="${ start } "]` ) || ! root . querySelector ( `[data-line="${ end } "]` ) ) {
600- file ( ) . setSelectedLines ( null )
637+ current . setSelectedLines ( null )
601638 return true
602639 }
603640
@@ -608,7 +645,7 @@ export function Code<T>(props: CodeProps<T>) {
608645 return { start : range . start , end : range . end }
609646 } ) ( )
610647
611- file ( ) . setSelectedLines ( normalized )
648+ current . setSelectedLines ( normalized )
612649 return true
613650 }
614651
@@ -619,9 +656,12 @@ export function Code<T>(props: CodeProps<T>) {
619656
620657 const token = renderToken
621658
622- const lines = lineCount ( )
659+ const lines = virtual ( ) ? undefined : lineCount ( )
623660
624- const isReady = ( root : ShadowRoot ) => root . querySelectorAll ( "[data-line]" ) . length >= lines
661+ const isReady = ( root : ShadowRoot ) =>
662+ virtual ( )
663+ ? root . querySelector ( "[data-line]" ) != null
664+ : root . querySelectorAll ( "[data-line]" ) . length >= ( lines ?? 0 )
625665
626666 const notify = ( ) => {
627667 if ( token !== renderToken ) return
@@ -844,20 +884,41 @@ export function Code<T>(props: CodeProps<T>) {
844884 }
845885
846886 createEffect ( ( ) => {
847- const current = file ( )
887+ const opts = options ( )
888+ const workerPool = getWorkerPool ( "unified" )
889+ const isVirtual = virtual ( )
848890
849- onCleanup ( ( ) => {
850- current . cleanUp ( )
851- } )
852- } )
853-
854- createEffect ( ( ) => {
855891 observer ?. disconnect ( )
856892 observer = undefined
857893
894+ instance ?. cleanUp ( )
895+ instance = undefined
896+
897+ if ( ! isVirtual && virtualizer ) {
898+ virtualizer . cleanUp ( )
899+ virtualizer = undefined
900+ virtualRoot = undefined
901+ }
902+
903+ const v = ( ( ) => {
904+ if ( ! isVirtual ) return
905+ if ( typeof document === "undefined" ) return
906+
907+ const root = getScrollParent ( wrapper ) ?? document
908+ if ( virtualizer && virtualRoot === root ) return virtualizer
909+
910+ virtualizer ?. cleanUp ( )
911+ virtualizer = new Virtualizer ( )
912+ virtualRoot = root
913+ virtualizer . setup ( root , root instanceof Document ? undefined : wrapper )
914+ return virtualizer
915+ } ) ( )
916+
917+ instance = isVirtual && v ? new VirtualizedFile < T > ( opts , v , codeMetrics , workerPool ) : new File < T > ( opts , workerPool )
918+
858919 container . innerHTML = ""
859920 const value = text ( )
860- file ( ) . render ( {
921+ instance . render ( {
861922 file : typeof local . file . contents === "string" ? local . file : { ...local . file , contents : value } ,
862923 lineAnnotations : local . annotations ,
863924 containerWrapper : container ,
@@ -910,6 +971,13 @@ export function Code<T>(props: CodeProps<T>) {
910971 onCleanup ( ( ) => {
911972 observer ?. disconnect ( )
912973
974+ instance ?. cleanUp ( )
975+ instance = undefined
976+
977+ virtualizer ?. cleanUp ( )
978+ virtualizer = undefined
979+ virtualRoot = undefined
980+
913981 clearOverlayScroll ( )
914982 clearOverlay ( )
915983 if ( findCurrent === host ) {
0 commit comments