diff --git a/cypress/integration/code-highlighting.spec.js b/cypress/integration/code-highlighting.spec.js index 881619c9..12c304e2 100644 --- a/cypress/integration/code-highlighting.spec.js +++ b/cypress/integration/code-highlighting.spec.js @@ -1,94 +1,94 @@ /** * Copyright (C) 2019 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ import {random} from '../utils'; const $ = Cypress.$; let origin; const lineStart = 32; const lineEnd = 42; let url; describe('Code highlighting tests', function() { before(function() { origin = this.origin[0]; url = this.Urls.browse_origin_content(origin.url, origin.content[0].path); }); it('should highlight source code and add line numbers', function() { cy.visit(url); cy.get('.hljs-ln-numbers').then(lnNumbers => { cy.get('.hljs-ln-code') .should('have.length', lnNumbers.length); }); }); it('should emphasize source code lines based on url fragment', function() { cy.visit(`${url}/#L${lineStart}-L${lineEnd}`); cy.get('.hljs-ln-line').then(lines => { for (let line of lines) { const lineElt = $(line); const lineNumber = parseInt(lineElt.data('line-number')); if (lineNumber >= lineStart && lineNumber <= lineEnd) { assert.notEqual(lineElt.css('background-color'), 'rgba(0, 0, 0, 0)'); } else { assert.equal(lineElt.css('background-color'), 'rgba(0, 0, 0, 0)'); } } }); }); it('should emphasize a line by clicking on its number', function() { cy.visit(url); cy.get('.hljs-ln-numbers').then(lnNumbers => { const lnNumber = lnNumbers[random(0, lnNumbers.length)]; const lnNumberElt = $(lnNumber); assert.equal(lnNumberElt.css('background-color'), 'rgba(0, 0, 0, 0)'); const line = parseInt(lnNumberElt.data('line-number')); cy.get(`.hljs-ln-numbers[data-line-number="${line}"]`) .click() .then(() => { assert.notEqual(lnNumberElt.css('background-color'), 'rgba(0, 0, 0, 0)'); }); }); }); it('should emphasize a range of lines by clicking on two line numbers and holding shift', function() { cy.visit(url); cy.get(`.hljs-ln-numbers[data-line-number="${lineStart}"]`) .click() .get(`body`) - .type(`{shift}`, { release: false }) + .type(`{shift}`, {release: false}) .get(`.hljs-ln-numbers[data-line-number="${lineEnd}"]`) .click() .get('.hljs-ln-line') .then(lines => { for (let line of lines) { const lineElt = $(line); const lineNumber = parseInt(lineElt.data('line-number')); if (lineNumber >= lineStart && lineNumber <= lineEnd) { assert.notEqual(lineElt.css('background-color'), 'rgba(0, 0, 0, 0)'); } else { assert.equal(lineElt.css('background-color'), 'rgba(0, 0, 0, 0)'); } } }); }); it('should remove emphasized lines when clicking anywhere in code', function() { cy.visit(`${url}/#L${lineStart}-L${lineEnd}`); cy.get(`.hljs-ln-code[data-line-number="1"]`) .click() .get('.hljs-ln-line') .should('have.css', 'background-color', 'rgba(0, 0, 0, 0)'); }); }); diff --git a/swh/web/assets/src/bundles/webapp/notebook-rendering.js b/swh/web/assets/src/bundles/webapp/notebook-rendering.js index 300ce38e..bb84059c 100644 --- a/swh/web/assets/src/bundles/webapp/notebook-rendering.js +++ b/swh/web/assets/src/bundles/webapp/notebook-rendering.js @@ -1,171 +1,171 @@ /** * Copyright (C) 2019 The Software Heritage developers * See the AUTHORS file at the top-level directory of this distribution * License: GNU Affero General Public License version 3, or any later version * See top-level LICENSE file for more information */ import 'script-loader!notebookjs'; import AnsiUp from 'ansi_up'; import './notebook.css'; const ansiup = new AnsiUp(); ansiup.escape_for_html = false; function escapeHTML(text) { text = text.replace(//g, '>'); return text; } function unescapeHTML(text) { text = text.replace(/</g, '<'); text = text.replace(/>/g, '>'); return text; } function escapeLaTeX(text) { let blockMath = /\$\$(.+?)\$\$|\\\\\[(.+?)\\\\\]/msg; let inlineMath = /\$(.+?)\$|\\\\\((.+?)\\\\\)/g; let latexEnvironment = /\\begin\{([a-z]*\*?)\}(.+?)\\end\{\1\}/msg; let mathTextFound = []; let bm; while ((bm = blockMath.exec(text)) !== null) { mathTextFound.push(bm[1]); } let im; while ((im = inlineMath.exec(text)) !== null) { mathTextFound.push(im[1]); } let le; while ((le = latexEnvironment.exec(text)) !== null) { mathTextFound.push(le[1]); } for (let mathText of mathTextFound) { // showdown will remove line breaks in LaTex array and // some escaping sequences when converting md to html. // So we use the following escaping hacks to keep them in the html // output and avoid MathJax typesetting errors. let escapedText = mathText.replace('\\\\', '\\\\\\\\'); for (let specialLaTexChar of ['{', '}', '#', '%', '&', '_']) { escapedText = escapedText.replace(new RegExp(`\\\\${specialLaTexChar}`, 'g'), `\\\\${specialLaTexChar}`); } // some html escaping is also needed escapedText = escapeHTML(escapedText); // hack to prevent showdown to replace _ characters // by html em tags as it will break some math typesetting // (setting the literalMidWordUnderscores option is not // enough as iy only works for _ characters contained in words) escapedText = escapedText.replace(/_/g, '{@}underscore{@}'); if (mathText !== escapedText) { text = text.replace(mathText, escapedText); } } return text; } export async function renderNotebook(nbJsonUrl, domElt) { let showdown = await import(/* webpackChunkName: "showdown" */ 'utils/showdown'); await import(/* webpackChunkName: "highlightjs" */ 'utils/highlightjs'); function renderMarkdown(text) { let converter = new showdown.Converter({ tables: true, simplifiedAutoLink: true, rawHeaderId: true, literalMidWordUnderscores: true }); // some LaTeX escaping is required to get correct math typesetting text = escapeLaTeX(text); // render markdown let rendered = converter.makeHtml(text); // restore underscores in rendered HTML (see escapeLaTeX function) rendered = rendered.replace(/{@}underscore{@}/g, '_'); return rendered; } function highlightCode(text, preElt, codeElt, lang) { // no need to unescape text processed by ansiup if (text.indexOf('