@@ -200,6 +200,10 @@ export class ActiveCode extends RunestoneBase {
200200 }
201201 let baseCode = this . trimLockedCode ( this . code ) ;
202202 this . history = [ baseCode ] ;
203+ this . highlightedLines = [ ] ;
204+ this . lockedLines = [ ] ;
205+ this . lockTextMarkers = [ ] ;
206+ this . lockGutterLines = [ ] ;
203207
204208 this . createEditor ( ) ;
205209 this . createOutput ( ) ;
@@ -220,9 +224,6 @@ export class ActiveCode extends RunestoneBase {
220224 setTimeout (
221225 function ( ) {
222226 this . editor . refresh ( ) ;
223- // need to regen/highlight locked decoration
224- this . setLockedRegions ( ) ;
225- this . setHighlightLines ( ) ;
226227 } . bind ( this ) ,
227228 1000
228229 ) ;
@@ -299,8 +300,6 @@ export class ActiveCode extends RunestoneBase {
299300 // Handle hidden codemirror (in tab) coming into view
300301 CodeMirror . on ( editor , "refresh" , ( cm ) => {
301302 window . requestAnimationFrame ( ( ) => {
302- this . setLockedRegions ( ) ;
303- this . setHighlightLines ( ) ;
304303 // make sure vscrollbar does not overlap the resize handle
305304 editor . display . scrollbars . vert . style . bottom = "16px" ;
306305 } ) ;
@@ -430,7 +429,6 @@ export class ActiveCode extends RunestoneBase {
430429
431430 // lock down code prefix/suffix
432431 this . setLockedRegions ( ) ;
433-
434432 this . setHighlightLines ( ) ;
435433
436434 if ( this . hidecode ) {
@@ -439,90 +437,137 @@ export class ActiveCode extends RunestoneBase {
439437 }
440438
441439 async setHighlightLines ( ) {
442- if ( this . highlightLines ) {
443- if ( typeof this . highlightLines === "number" )
444- this . highlightLines = this . highlightLines . toString ( ) ;
445-
446- let highlightList = this . highlightLines . split ( "," ) ;
447- let lines = this . containerDiv . querySelectorAll ( ".CodeMirror-code > div" ) ;
448- highlightList . forEach ( ( line ) => {
449- // addLineClass not used here for reason described in setLockedRegions
450- line = line . trim ( ) ;
451- let lineNum = line . split ( "-" ) ;
452- if ( lineNum . length > 1 ) {
453- for ( let i = parseInt ( lineNum [ 0 ] ) ; i <= parseInt ( lineNum [ 1 ] ) ; i ++ ) {
454- lines [ i - 1 ] . classList . add ( "CodeMirror__highlight-line" ) ;
455- }
456- } else {
457- lines [ lineNum - 1 ] . classList . add ( "CodeMirror__highlight-line" ) ;
458- }
440+ this . editor . operation ( ( ) => {
441+ this . highlightedLines . forEach ( ( lineNumber ) => {
442+ this . editor . removeLineClass (
443+ lineNumber ,
444+ "wrap" ,
445+ "CodeMirror__highlight-line"
446+ ) ;
459447 } ) ;
460- }
448+ this . highlightedLines = [ ] ;
449+
450+ if ( this . highlightLines ) {
451+ if ( typeof this . highlightLines === "number" )
452+ this . highlightLines = this . highlightLines . toString ( ) ;
453+
454+ let highlightList = this . highlightLines . split ( "," ) ;
455+ highlightList . forEach ( ( line ) => {
456+ line = line . trim ( ) ;
457+ let lineNum = line . split ( "-" ) ;
458+ if ( lineNum . length > 1 ) {
459+ let startLine = parseInt ( lineNum [ 0 ] ) - 1 ;
460+ let endLine = parseInt ( lineNum [ 1 ] ) - 1 ;
461+ for ( let i = startLine ; i <= endLine ; i ++ ) {
462+ if ( i >= 0 && i <= this . editor . doc . lastLine ( ) ) {
463+ this . editor . addLineClass (
464+ i ,
465+ "wrap" ,
466+ "CodeMirror__highlight-line"
467+ ) ;
468+ this . highlightedLines . push ( i ) ;
469+ }
470+ }
471+ } else {
472+ let highlightLine = parseInt ( lineNum [ 0 ] ) - 1 ;
473+ if ( highlightLine >= 0 && highlightLine <= this . editor . doc . lastLine ( ) ) {
474+ this . editor . addLineClass (
475+ highlightLine ,
476+ "wrap" ,
477+ "CodeMirror__highlight-line"
478+ ) ;
479+ this . highlightedLines . push ( highlightLine ) ;
480+ }
481+ }
482+ } ) ;
483+ }
484+ } ) ;
461485 }
462486
463487
464488 async setLockedRegions ( ) {
465- function decorateLines ( start , end ) {
466- let lines = this . containerDiv . querySelectorAll ( ".CodeMirror-code > div" ) ;
467- for ( let i = start ; i <= end ; i ++ ) {
468- // addLineClass looks like the way this "should" be done
469- // codemirror appears to remove the line and insert a modified one
470- // causing a lot of rerendering. Can slow page load down substantially
471- //this.editor.addLineClass(i, "behind", "CodeMirror__locked-line");
472- // So manually just go add a class after verifying component is rendered
473- if ( lines [ i ] )
474- lines [ i ] . classList . add ( "CodeMirror__locked-line" ) ;
475- // downside is that this is not preserved on editor.refresh()
476- // so setLockedRegions() must be called again
477- }
478- let midLine = Math . floor ( ( start + end ) / 2 ) ;
479- var marker = document . createElement ( "div" ) ;
480- marker . className = "CodeMirror__gutter-locked-marker" ;
481- this . editor . setGutterMarker ( midLine , "CodeMirror-lock-markers" , marker ) ;
482- }
489+ this . editor . operation ( ( ) => {
490+ this . lockedLines . forEach ( ( lineNumber ) => {
491+ this . editor . removeLineClass (
492+ lineNumber ,
493+ "wrap" ,
494+ "CodeMirror__locked-line"
495+ ) ;
496+ } ) ;
497+ this . lockedLines = [ ] ;
498+
499+ this . lockGutterLines . forEach ( ( lineNumber ) => {
500+ this . editor . setGutterMarker ( lineNumber , "CodeMirror-lock-markers" , null ) ;
501+ } ) ;
502+ this . lockGutterLines = [ ] ;
483503
484- this . containerDiv . querySelectorAll ( ".CodeMirror-code > div" ) . forEach (
485- ( line ) => {
486- line . classList . remove ( "CodeMirror__locked-line" ) ;
504+ this . lockTextMarkers . forEach ( ( marker ) => marker . clear ( ) ) ;
505+ this . lockTextMarkers = [ ] ;
506+
507+ function placeLock ( lineNumber ) {
508+ var marker = document . createElement ( "div" ) ;
509+ marker . className = "CodeMirror__gutter-locked-marker" ;
510+ this . editor . setGutterMarker ( lineNumber , "CodeMirror-lock-markers" , marker ) ;
511+ this . lockGutterLines . push ( lineNumber ) ;
487512 }
488- ) ;
489513
490- if ( this . visiblePrefixEnd ) {
491- let lastLine = this . editor . posFromIndex (
492- this . visiblePrefixEnd - 1
493- ) . line ;
494- decorateLines . call ( this , 0 , lastLine ) ;
495- let endPos = this . editor . posFromIndex ( this . visiblePrefixEnd ) ;
496- this . editor . markText (
497- { line : 0 , ch : 0 } ,
498- { line : endPos . line , ch : endPos . ch } ,
499- {
500- readOnly : true ,
501- atomic : false ,
502- inclusiveLeft : true ,
503- inclusiveRight : false ,
514+ if ( this . visiblePrefixEnd ) {
515+ let lastLine = this . editor . posFromIndex (
516+ this . visiblePrefixEnd - 1
517+ ) . line ;
518+ placeLock . call ( this , Math . floor ( ( 0 + lastLine ) / 2 ) ) ;
519+ for ( let lineNumber = 0 ; lineNumber <= lastLine ; lineNumber ++ ) {
520+ this . editor . addLineClass (
521+ lineNumber ,
522+ "wrap" ,
523+ "CodeMirror__locked-line"
524+ ) ;
525+ this . lockedLines . push ( lineNumber ) ;
504526 }
505- ) ;
506- }
507- if ( this . visibleSuffixLength ) {
508- let endIndex =
509- this . editor . doc . getValue ( ) . length - this . visibleSuffixLength ;
510- let endPos = this . editor . posFromIndex ( endIndex ) ;
511- let lastLine = this . editor . doc . lastLine ( ) ;
512- decorateLines . call ( this , endPos . line , lastLine ) ;
513- // include preceeding newline
514- let endPos2 = this . editor . posFromIndex ( endIndex - 1 ) ;
515- this . editor . markText (
516- { line : endPos2 . line , ch : endPos2 . ch } ,
517- { line : this . editor . doc . lastLine ( ) + 1 } ,
518- {
519- readOnly : true ,
520- atomic : false ,
521- inclusiveLeft : false ,
522- inclusiveRight : true ,
527+ let endPos = this . editor . posFromIndex ( this . visiblePrefixEnd ) ;
528+ this . lockTextMarkers . push (
529+ this . editor . markText (
530+ { line : 0 , ch : 0 } ,
531+ { line : endPos . line , ch : endPos . ch } ,
532+ {
533+ readOnly : true ,
534+ atomic : false ,
535+ inclusiveLeft : true ,
536+ inclusiveRight : false ,
537+ }
538+ )
539+ ) ;
540+ }
541+ if ( this . visibleSuffixLength ) {
542+ let endIndex =
543+ this . editor . doc . getValue ( ) . length - this . visibleSuffixLength ;
544+ let endPos = this . editor . posFromIndex ( endIndex ) ;
545+ let lastLine = this . editor . doc . lastLine ( ) ;
546+ placeLock . call ( this , Math . floor ( ( endPos . line + lastLine ) / 2 ) ) ;
547+ for ( let lineNumber = endPos . line ; lineNumber <= lastLine ; lineNumber ++ ) {
548+ this . editor . addLineClass (
549+ lineNumber ,
550+ "wrap" ,
551+ "CodeMirror__locked-line"
552+ ) ;
553+ this . lockedLines . push ( lineNumber ) ;
523554 }
524- ) ;
525- }
555+ // include preceeding newline
556+ let endPos2 = this . editor . posFromIndex ( endIndex - 1 ) ;
557+ this . lockTextMarkers . push (
558+ this . editor . markText (
559+ { line : endPos2 . line , ch : endPos2 . ch } ,
560+ { line : this . editor . doc . lastLine ( ) + 1 } ,
561+ {
562+ readOnly : true ,
563+ atomic : false ,
564+ inclusiveLeft : false ,
565+ inclusiveRight : true ,
566+ }
567+ )
568+ ) ;
569+ }
570+ } ) ;
526571 } ;
527572
528573 async runButtonHandler ( ) {
@@ -1498,10 +1543,7 @@ export class ActiveCode extends RunestoneBase {
14981543 div_id : this . divid ,
14991544 } ) ;
15001545 }
1501- // Only re-highlight lines if we are at initial position
1502- // otherwise may be highlighting wrong ones
1503- if ( pos === 0 )
1504- this . setHighlightLines ( ) ;
1546+ this . setHighlightLines ( ) ;
15051547 } ;
15061548 $ ( scrubber ) . slider ( {
15071549 max : this . history . length - 1 ,
0 commit comments