@@ -6,30 +6,25 @@ import { symbolHoverTargetsExtension } from "@/ee/features/codeNav/components/sy
66import { useHasEntitlement } from "@/features/entitlements/useHasEntitlement" ;
77import { useCodeMirrorLanguageExtension } from "@/hooks/useCodeMirrorLanguageExtension" ;
88import { useCodeMirrorTheme } from "@/hooks/useCodeMirrorTheme" ;
9+ import { useExtensionWithDependency } from "@/hooks/useExtensionWithDependency" ;
910import { useKeymapExtension } from "@/hooks/useKeymapExtension" ;
1011import { cn } from "@/lib/utils" ;
11- import { Range } from "@codemirror/state" ;
12- import { Decoration , DecorationSet , EditorView } from '@codemirror/view' ;
12+ import { EditorView } from '@codemirror/view' ;
1313import { CodeHostType } from "@sourcebot/db" ;
14- import CodeMirror , { ReactCodeMirrorRef , StateField } from '@uiw/react-codemirror' ;
14+ import CodeMirror , { ReactCodeMirrorRef } from '@uiw/react-codemirror' ;
1515import isEqual from "fast-deep-equal/react" ;
1616import { ChevronDown , ChevronRight } from "lucide-react" ;
17- import { forwardRef , memo , Ref , useCallback , useImperativeHandle , useMemo , useState } from "react" ;
17+ import { forwardRef , memo , Ref , useEffect , useImperativeHandle , useMemo , useState } from "react" ;
1818import { FileReference } from "../../types" ;
1919import { createCodeFoldingExtension } from "./codeFoldingExtension" ;
20+ import { createReferencesHighlightExtension , setHoveredIdEffect , setSelectedIdEffect } from "./referencesHighlightExtension" ;
2021
21- const lineDecoration = Decoration . line ( {
22- attributes : { class : "cm-range-border-radius chat-lineHighlight" } ,
23- } ) ;
24-
25- const selectedLineDecoration = Decoration . line ( {
26- attributes : { class : "cm-range-border-radius cm-range-border-shadow chat-lineHighlight-selected" } ,
27- } ) ;
28-
29- const hoverLineDecoration = Decoration . line ( {
30- attributes : { class : "chat-lineHighlight-hover" } ,
31- } ) ;
32-
22+ const CODEMIRROR_BASIC_SETUP = {
23+ highlightActiveLine : false ,
24+ highlightActiveLineGutter : false ,
25+ foldGutter : false ,
26+ foldKeymap : false ,
27+ } as const ;
3328
3429interface ReferencedFileSourceListItemProps {
3530 id : string ;
@@ -75,47 +70,32 @@ const ReferencedFileSourceListItemComponent = ({
7570 forwardedRef ,
7671 ( ) => editorRef as ReactCodeMirrorRef
7772 ) ;
73+
7874 const keymapExtension = useKeymapExtension ( editorRef ?. view ) ;
7975 const hasCodeNavEntitlement = useHasEntitlement ( "code-nav" ) ;
80-
8176 const languageExtension = useCodeMirrorLanguageExtension ( language , editorRef ?. view ) ;
8277
83- const getReferenceAtPos = useCallback ( ( x : number , y : number , view : EditorView ) : FileReference | undefined => {
84- const pos = view . posAtCoords ( { x, y } ) ;
85- if ( pos === null ) return undefined ;
86-
87- // Check if position is within the main editor content area
88- const rect = view . contentDOM . getBoundingClientRect ( ) ;
89- if ( x < rect . left || x > rect . right || y < rect . top || y > rect . bottom ) {
90- return undefined ;
91- }
92-
93- const line = view . state . doc . lineAt ( pos ) ;
94- const lineNumber = line . number ;
95-
96- // Check if this line is part of any highlighted range
97- const matchingRanges = references . filter ( ( { range } ) =>
98- range && lineNumber >= range . startLine && lineNumber <= range . endLine
99- ) ;
78+ const codeFoldingExtension = useMemo ( ( ) => {
79+ return createCodeFoldingExtension ( references , 3 ) ;
80+ } , [ references ] ) ;
10081
101- // Sort by the length of the range.
102- // Shorter ranges are more specific, so we want to prioritize them.
103- matchingRanges . sort ( ( a , b ) => {
104- const aLength = ( a . range ! . endLine ) - ( a . range ! . startLine ) ;
105- const bLength = ( b . range ! . endLine ) - ( b . range ! . startLine ) ;
106- return aLength - bLength ;
107- } ) ;
82+ const referencesHighlightExtension = useExtensionWithDependency (
83+ editorRef ?. view ?? null ,
84+ ( ) => createReferencesHighlightExtension ( references , onHoveredReferenceChanged , onSelectedReferenceChanged ) ,
85+ [ references ] ,
86+ ) ;
10887
109- if ( matchingRanges . length > 0 ) {
110- return matchingRanges [ 0 ] ;
88+ useEffect ( ( ) => {
89+ if ( editorRef ?. view ) {
90+ editorRef . view . dispatch ( { effects : setHoveredIdEffect . of ( hoveredReference ?. id ) } ) ;
11191 }
92+ } , [ hoveredReference ?. id , editorRef ?. view ] ) ;
11293
113- return undefined ;
114- } , [ references ] ) ;
115-
116- const codeFoldingExtension = useMemo ( ( ) => {
117- return createCodeFoldingExtension ( references , 3 ) ;
118- } , [ references ] ) ;
94+ useEffect ( ( ) => {
95+ if ( editorRef ?. view ) {
96+ editorRef . view . dispatch ( { effects : setSelectedIdEffect . of ( selectedReference ?. id ) } ) ;
97+ }
98+ } , [ selectedReference ?. id , editorRef ?. view ] ) ;
11999
120100 const extensions = useMemo ( ( ) => {
121101 return [
@@ -126,88 +106,14 @@ const ReferencedFileSourceListItemComponent = ({
126106 symbolHoverTargetsExtension ,
127107 ] : [ ] ) ,
128108 codeFoldingExtension ,
129- StateField . define < DecorationSet > ( {
130- create ( state ) {
131- const decorations : Range < Decoration > [ ] = [ ] ;
132-
133- for ( const { range, id } of references ) {
134- if ( ! range ) {
135- continue ;
136- }
137-
138- const isHovered = id === hoveredReference ?. id ;
139- const isSelected = id === selectedReference ?. id ;
140-
141- for ( let line = range . startLine ; line <= range . endLine ; line ++ ) {
142- // Skip lines that are outside the document bounds.
143- if ( line > state . doc . lines ) {
144- continue ;
145- }
146-
147- if ( isSelected ) {
148- decorations . push ( selectedLineDecoration . range ( state . doc . line ( line ) . from ) ) ;
149- } else {
150- decorations . push ( lineDecoration . range ( state . doc . line ( line ) . from ) ) ;
151- if ( isHovered ) {
152- decorations . push ( hoverLineDecoration . range ( state . doc . line ( line ) . from ) ) ;
153- }
154- }
155-
156- }
157- }
158-
159- return Decoration . set ( decorations , /* sort = */ true ) ;
160- } ,
161- update ( deco , tr ) {
162- return deco . map ( tr . changes ) ;
163- } ,
164- provide : ( field ) => EditorView . decorations . from ( field ) ,
165- } ) ,
166- EditorView . domEventHandlers ( {
167- click : ( event , view ) => {
168- const reference = getReferenceAtPos ( event . clientX , event . clientY , view ) ;
169-
170- if ( reference ) {
171- onSelectedReferenceChanged ( reference . id === selectedReference ?. id ? undefined : reference ) ;
172- return true ; // prevent default handling
173- }
174- return false ;
175- } ,
176- mouseover : ( event , view ) => {
177- const reference = getReferenceAtPos ( event . clientX , event . clientY , view ) ;
178- if ( ! reference ) {
179- return false ;
180- }
181-
182- if ( reference . id === selectedReference ?. id || reference . id === hoveredReference ?. id ) {
183- return false ;
184- }
185-
186- onHoveredReferenceChanged ( reference ) ;
187- return true ;
188- } ,
189- mouseout : ( event , view ) => {
190- const reference = getReferenceAtPos ( event . clientX , event . clientY , view ) ;
191- if ( reference ) {
192- return false ;
193- }
194-
195- onHoveredReferenceChanged ( undefined ) ;
196- return true ;
197- }
198- } )
109+ referencesHighlightExtension ,
199110 ] ;
200111 } , [
201112 languageExtension ,
202113 keymapExtension ,
203114 hasCodeNavEntitlement ,
204- references ,
205- hoveredReference ?. id ,
206- selectedReference ?. id ,
207- getReferenceAtPos ,
208- onSelectedReferenceChanged ,
209- onHoveredReferenceChanged ,
210115 codeFoldingExtension ,
116+ referencesHighlightExtension ,
211117 ] ) ;
212118
213119 const ExpandCollapseIcon = useMemo ( ( ) => {
@@ -253,12 +159,7 @@ const ReferencedFileSourceListItemComponent = ({
253159 extensions = { extensions }
254160 readOnly = { true }
255161 theme = { theme }
256- basicSetup = { {
257- highlightActiveLine : false ,
258- highlightActiveLineGutter : false ,
259- foldGutter : false ,
260- foldKeymap : false ,
261- } }
162+ basicSetup = { CODEMIRROR_BASIC_SETUP }
262163 >
263164 { editorRef && hasCodeNavEntitlement && (
264165 < SymbolHoverPopup
0 commit comments