11import { type FileContents , File , FileOptions , LineAnnotation , type SelectedLineRange } from "@pierre/diffs"
2- import { ComponentProps , createEffect , createMemo , onCleanup , splitProps } from "solid-js"
2+ import { ComponentProps , createEffect , createMemo , createSignal , onCleanup , splitProps } from "solid-js"
33import { createDefaultOptions , styleVariables } from "../pierre"
44import { getWorkerPool } from "../pierre/worker"
55
@@ -9,7 +9,9 @@ export type CodeProps<T = {}> = FileOptions<T> & {
99 file : FileContents
1010 annotations ?: LineAnnotation < T > [ ]
1111 selectedLines ?: SelectedLineRange | null
12+ commentedLines ?: SelectedLineRange [ ]
1213 onRendered ?: ( ) => void
14+ onLineSelectionEnd ?: ( selection : SelectedLineRange | null ) => void
1315 class ?: string
1416 classList ?: ComponentProps < "div" > [ "classList" ]
1517}
@@ -53,16 +55,22 @@ export function Code<T>(props: CodeProps<T>) {
5355 let dragStart : number | undefined
5456 let dragEnd : number | undefined
5557 let dragMoved = false
58+ let lastSelection : SelectedLineRange | null = null
59+ let pendingSelectionEnd = false
5660
5761 const [ local , others ] = splitProps ( props , [
5862 "file" ,
5963 "class" ,
6064 "classList" ,
6165 "annotations" ,
6266 "selectedLines" ,
67+ "commentedLines" ,
6368 "onRendered" ,
69+ "onLineSelectionEnd" ,
6470 ] )
6571
72+ const [ rendered , setRendered ] = createSignal ( 0 )
73+
6674 const handleLineClick : FileOptions < T > [ "onLineClick" ] = ( info ) => {
6775 props . onLineClick ?.( info )
6876
@@ -95,6 +103,30 @@ export function Code<T>(props: CodeProps<T>) {
95103 return root
96104 }
97105
106+ const applyCommentedLines = ( ranges : SelectedLineRange [ ] ) => {
107+ const root = getRoot ( )
108+ if ( ! root ) return
109+
110+ const existing = Array . from ( root . querySelectorAll ( "[data-comment-selected]" ) )
111+ for ( const node of existing ) {
112+ if ( ! ( node instanceof HTMLElement ) ) continue
113+ node . removeAttribute ( "data-comment-selected" )
114+ }
115+
116+ for ( const range of ranges ) {
117+ const start = Math . max ( 1 , Math . min ( range . start , range . end ) )
118+ const end = Math . max ( range . start , range . end )
119+
120+ for ( let line = start ; line <= end ; line ++ ) {
121+ const nodes = Array . from ( root . querySelectorAll ( `[data-line="${ line } "]` ) )
122+ for ( const node of nodes ) {
123+ if ( ! ( node instanceof HTMLElement ) ) continue
124+ node . setAttribute ( "data-comment-selected" , "" )
125+ }
126+ }
127+ }
128+ }
129+
98130 const notifyRendered = ( ) => {
99131 if ( ! local . onRendered ) return
100132
@@ -203,7 +235,12 @@ export function Code<T>(props: CodeProps<T>) {
203235 if ( side ) selected . side = side
204236 if ( endSide && side && endSide !== side ) selected . endSide = endSide
205237
206- file ( ) . setSelectedLines ( selected )
238+ setSelectedLines ( selected )
239+ }
240+
241+ const setSelectedLines = ( range : SelectedLineRange | null ) => {
242+ lastSelection = range
243+ file ( ) . setSelectedLines ( range )
207244 }
208245
209246 const scheduleSelectionUpdate = ( ) => {
@@ -212,6 +249,10 @@ export function Code<T>(props: CodeProps<T>) {
212249 selectionFrame = requestAnimationFrame ( ( ) => {
213250 selectionFrame = undefined
214251 updateSelection ( )
252+
253+ if ( ! pendingSelectionEnd ) return
254+ pendingSelectionEnd = false
255+ props . onLineSelectionEnd ?.( lastSelection )
215256 } )
216257 }
217258
@@ -221,7 +262,7 @@ export function Code<T>(props: CodeProps<T>) {
221262 const start = Math . min ( dragStart , dragEnd )
222263 const end = Math . max ( dragStart , dragEnd )
223264
224- file ( ) . setSelectedLines ( { start, end } )
265+ setSelectedLines ( { start, end } )
225266 }
226267
227268 const scheduleDragUpdate = ( ) => {
@@ -289,19 +330,22 @@ export function Code<T>(props: CodeProps<T>) {
289330
290331 const handleMouseUp = ( ) => {
291332 if ( props . enableLineSelection !== true ) return
333+ if ( dragStart === undefined ) return
292334
293- if ( dragStart !== undefined ) {
294- if ( dragMoved ) scheduleDragUpdate ( )
295- dragStart = undefined
296- dragEnd = undefined
297- dragMoved = false
335+ if ( dragMoved ) {
336+ pendingSelectionEnd = true
337+ scheduleDragUpdate ( )
338+ scheduleSelectionUpdate ( )
298339 }
299340
300- scheduleSelectionUpdate ( )
341+ dragStart = undefined
342+ dragEnd = undefined
343+ dragMoved = false
301344 }
302345
303346 const handleSelectionChange = ( ) => {
304347 if ( props . enableLineSelection !== true ) return
348+ if ( dragStart === undefined ) return
305349
306350 const selection = window . getSelection ( )
307351 if ( ! selection || selection . isCollapsed ) return
@@ -328,11 +372,18 @@ export function Code<T>(props: CodeProps<T>) {
328372 containerWrapper : container ,
329373 } )
330374
375+ setRendered ( ( value ) => value + 1 )
331376 notifyRendered ( )
332377 } )
333378
334379 createEffect ( ( ) => {
335- file ( ) . setSelectedLines ( local . selectedLines ?? null )
380+ rendered ( )
381+ const ranges = local . commentedLines ?? [ ]
382+ requestAnimationFrame ( ( ) => applyCommentedLines ( ranges ) )
383+ } )
384+
385+ createEffect ( ( ) => {
386+ setSelectedLines ( local . selectedLines ?? null )
336387 } )
337388
338389 createEffect ( ( ) => {
@@ -367,6 +418,8 @@ export function Code<T>(props: CodeProps<T>) {
367418 dragStart = undefined
368419 dragEnd = undefined
369420 dragMoved = false
421+ lastSelection = null
422+ pendingSelectionEnd = false
370423 } )
371424
372425 return (
0 commit comments