Skip to content

Commit

Permalink
Cache code-line elements for md scroll-sync
Browse files Browse the repository at this point in the history
Fixes #19092
  • Loading branch information
mjbvz committed Dec 18, 2017
1 parent dcaee0a commit 55382a1
Showing 1 changed file with 48 additions and 26 deletions.
74 changes: 48 additions & 26 deletions extensions/markdown/media/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,31 +29,58 @@
};
}

/**
* @param {string} command
* @param {any[]} args
*/
function postMessage(command, args) {
window.parent.postMessage({
command: 'did-click-link',
data: `command:${command}?${encodeURIComponent(JSON.stringify(args))}`
}, 'file://');
}

/**
* @typedef {{ element: Element, line: number }} CodeLineElement
*/

/**
* @return {CodeLineElement[]}
*/
const getCodeLineElements = (() => {
/** @type {CodeLineElement[]} */
let elements;
return () => {
if (!elements) {
elements = Array.prototype.map.call(
document.getElementsByClassName('code-line'),
element => {
const line = +element.getAttribute('data-line');
return { element, line }
})
.filter(x => !isNaN(x.line));
}
return elements;
};
})()

/**
* Find the html elements that map to a specific target line in the editor.
*
* If an exact match, returns a single element. If the line is between elements,
* returns the element prior to and the element after the given line.
*
* @param {number} targetLine
*
* @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
*/
function getElementsForSourceLine(targetLine) {
const lines = document.getElementsByClassName('code-line');
let previous = lines[0] && +lines[0].getAttribute('data-line') ? { line: +lines[0].getAttribute('data-line'), element: lines[0] } : null;
for (const element of lines) {
const lineNumber = +element.getAttribute('data-line');
if (isNaN(lineNumber)) {
continue;
}
const entry = { line: lineNumber, element: element };
if (lineNumber === targetLine) {
const lines = getCodeLineElements();
let previous = lines[0] || null;
for (const entry of lines) {
if (entry.line === targetLine) {
return { previous: entry, next: null };
} else if (lineNumber > targetLine) {
} else if (entry.line > targetLine) {
return { previous, next: entry };
}
previous = entry;
Expand All @@ -64,23 +91,18 @@
/**
* Find the html elements that are at a specific pixel offset on the page.
*
* @returns {{ previous: { element: any, line: number }, next?: { element: any, line: number } }}
* @returns {{ previous: CodeLineElement, next?: CodeLineElement }}
*/
function getLineElementsAtPageOffset(offset) {
const allLines = document.getElementsByClassName('code-line');
/** @type {Element[]} */
const lines = Array.prototype.filter.call(allLines, element => {
const line = +element.getAttribute('data-line');
return !isNaN(line)
});
const lines = getCodeLineElements()

const position = offset - window.scrollY;

let lo = -1;
let hi = lines.length - 1;
while (lo + 1 < hi) {
const mid = Math.floor((lo + hi) / 2);
const bounds = lines[mid].getBoundingClientRect();
const bounds = lines[mid].element.getBoundingClientRect();
if (bounds.top + bounds.height >= position) {
hi = mid;
} else {
Expand All @@ -89,18 +111,16 @@
}

const hiElement = lines[hi];
const hiLine = +hiElement.getAttribute('data-line');
if (hi >= 1 && hiElement.getBoundingClientRect().top > position) {
if (hi >= 1 && hiElement.element.getBoundingClientRect().top > position) {
const loElement = lines[lo];
const loLine = +loElement.getAttribute('data-line');
const bounds = loElement.getBoundingClientRect();
const previous = { element: loElement, line: loLine + (position - bounds.top) / (bounds.height) };
const next = { element: hiElement, line: hiLine, fractional: 0 };
const bounds = loElement.element.getBoundingClientRect();
const previous = { element: loElement.element, line: loElement.line + (position - bounds.top) / (bounds.height) };
const next = { element: hiElement.element, line: hiElement.line, fractional: 0 };
return { previous, next };
}

const bounds = hiElement.getBoundingClientRect();
const previous = { element: hiElement, line: hiLine + (position - bounds.top) / (bounds.height) };
const bounds = hiElement.element.getBoundingClientRect();
const previous = { element: hiElement.element, line: hiElement.line + (position - bounds.top) / (bounds.height) };
return { previous };
}

Expand All @@ -110,6 +130,8 @@

/**
* Attempt to reveal the element for a source line in the editor.
*
* @param {number} line
*/
function scrollToRevealSourceLine(line) {
const { previous, next } = getElementsForSourceLine(line);
Expand Down

0 comments on commit 55382a1

Please sign in to comment.