Skip to content

Commit e3564f2

Browse files
authored
Merge pull request #1190 from ascholerChemeketa/activecode-lock-display-improvement
Update activecode locked and highlighted region markings on scroll
2 parents 768a30f + 3d57405 commit e3564f2

1 file changed

Lines changed: 126 additions & 84 deletions

File tree

bases/rsptx/interactives/runestone/activecode/js/activecode.js

Lines changed: 126 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)