@@ -4,6 +4,8 @@ import {EditorView as CMEditorView} from '@codemirror/view';
44import { TextSelection } from 'prosemirror-state' ;
55import type { EditorView as PMEditorView } from 'prosemirror-view' ;
66
7+ import { getDescedantByAttribute } from 'src/utils/node-descedants' ;
8+
79import type { CommonEditor , MarkupString } from '../common' ;
810import {
911 type ActionStorage ,
@@ -516,48 +518,83 @@ export class EditorImpl extends SafeEventEmitter<EventMapInt> implements EditorI
516518
517519 switch ( mode ) {
518520 case 'markup' : {
519- const view = this . markupEditor . cm ;
520-
521- let cmLine = line + 1 ; // lines in codemirror is 1-based
522- cmLine = Math . max ( cmLine , 1 ) ;
523- cmLine = Math . min ( cmLine , view . state . doc . lines ) ;
524-
525- const yMargin = getTopOffset ( view . dom ) ;
526- const anchor = view . state . doc . line ( cmLine ) . from ;
527- view . dispatch ( {
528- scrollIntoView : true ,
529- selection : { anchor} ,
530- effects : [
531- CMEditorView . scrollIntoView ( anchor , { y : 'start' , x : 'start' , yMargin} ) ,
532- ] ,
533- } ) ;
534-
521+ this . markupMoveToLine ( line ) ;
535522 break ;
536-
537- // eslint-disable-next-line no-inner-declarations
538523 }
539524 case 'wysiwyg' : {
540- const elem = this . wysiwygEditor . dom . querySelector ( `[data-line="${ line } "]` ) ;
541-
542- if ( elem ) {
543- const elemTop = elem . getBoundingClientRect ( ) . top ;
544- const topOffset = getTopOffset ( this . wysiwygEditor . dom ) ;
545- window . scrollTo ( { top : elemTop + window . scrollY - topOffset } ) ;
546-
547- const position = this . _wysiwygView . posAtDOM ( elem , 0 ) ;
548- const { tr} = this . _wysiwygView . state ;
549- this . _wysiwygView . dispatch (
550- tr . setSelection ( TextSelection . create ( tr . doc , position ) ) ,
551- ) ;
552- }
553-
525+ this . wysiwygMoveToLine ( line ) ;
554526 break ;
555527 }
556528 default :
557529 throw new Error ( 'Unknown editor mode: ' + mode ) ;
558530 }
559531 }
560532
533+ private markupMoveToLine ( line : number ) : void {
534+ const view = this . markupEditor . cm ;
535+ const isConnected = Boolean ( view . dom . parentElement ) ;
536+
537+ let cmLine = line + 1 ; // lines in codemirror is 1-based
538+ cmLine = Math . max ( cmLine , 1 ) ;
539+ cmLine = Math . min ( cmLine , view . state . doc . lines ) ;
540+
541+ const anchor = view . state . doc . line ( cmLine ) . from ;
542+ view . dispatch ( {
543+ scrollIntoView : true ,
544+ selection : { anchor} ,
545+ effects : isConnected
546+ ? [
547+ CMEditorView . scrollIntoView ( anchor , {
548+ y : 'start' ,
549+ x : 'start' ,
550+ yMargin : getTopOffset ( view . dom ) ,
551+ } ) ,
552+ ]
553+ : undefined ,
554+ } ) ;
555+ }
556+
557+ private wysiwygMoveToLine ( line : number ) : void {
558+ const DATA_LINE = 'data-line' ;
559+ const SELECTOR = `[${ DATA_LINE } ="${ line } "]` as const ;
560+
561+ const view = this . _wysiwygView ;
562+ const isConnected = Boolean ( view . dom . parentElement ) ;
563+
564+ const setSelection = ( pos : number ) => {
565+ const { tr} = view . state ;
566+ view . dispatch ( tr . setSelection ( TextSelection . near ( tr . doc . resolve ( pos ) , 1 ) ) ) ;
567+ } ;
568+
569+ const scrollIntoView = ( elemTop : number ) => {
570+ const topOffset = getTopOffset ( this . wysiwygEditor . dom ) ;
571+ window . scrollTo ( { top : elemTop + window . scrollY - topOffset } ) ;
572+ } ;
573+
574+ const elem = this . wysiwygEditor . dom . querySelector ( SELECTOR ) ;
575+ if ( elem ) {
576+ const position = this . _wysiwygView . posAtDOM ( elem , 0 ) ;
577+ setSelection ( position ) ;
578+
579+ if ( isConnected ) {
580+ const elemTop = elem . getBoundingClientRect ( ) . top ;
581+ scrollIntoView ( elemTop ) ;
582+ }
583+
584+ return ;
585+ }
586+
587+ const node = getDescedantByAttribute ( view . state . doc , DATA_LINE , [ line , String ( line ) ] ) ;
588+ if ( node ) {
589+ setSelection ( node . pos ) ;
590+
591+ if ( isConnected ) {
592+ const elemTop = view . coordsAtPos ( node . pos ) . top ;
593+ scrollIntoView ( elemTop ) ;
594+ }
595+ }
596+ }
597+
561598 private shouldReplaceMarkupEditorValue ( markupValue : string , wysiwygValue : string ) {
562599 const serializedEditorMarkup = this . #wysiwygEditor?. serializer . serialize (
563600 this . #wysiwygEditor. parser . parse ( markupValue ) ,
0 commit comments