Commit 4d98851a authored by Phil Hughes's avatar Phil Hughes

Merge branch 'ipython_katex_fixes' into 'master'

Jupyter/IPython katex math fixes

Closes #216741, #216744, and #212837

See merge request gitlab-org/gitlab!31433
parents 96adae97 a7d9d536
...@@ -36,9 +36,9 @@ const katexRegexString = `( ...@@ -36,9 +36,9 @@ const katexRegexString = `(
.replace(/\s/g, '') .replace(/\s/g, '')
.trim(); .trim();
renderer.paragraph = t => { function renderKatex(t) {
let text = t; let text = t;
let inline = false; let numInline = 0; // number of successfull converted math formulas
if (typeof katex !== 'undefined') { if (typeof katex !== 'undefined') {
const katexString = text const katexString = text
...@@ -50,24 +50,40 @@ renderer.paragraph = t => { ...@@ -50,24 +50,40 @@ renderer.paragraph = t => {
const numberOfMatches = katexString.match(regex); const numberOfMatches = katexString.match(regex);
if (numberOfMatches && numberOfMatches.length !== 0) { if (numberOfMatches && numberOfMatches.length !== 0) {
let matches = regex.exec(katexString);
if (matchLocation > 0) { if (matchLocation > 0) {
let matches = regex.exec(katexString); numInline += 1;
inline = true;
while (matches !== null) { while (matches !== null) {
const renderedKatex = katex.renderToString(matches[0].replace(/\$/g, '')); try {
text = `${text.replace(matches[0], ` ${renderedKatex}`)}`; const renderedKatex = katex.renderToString(
matches[0].replace(/\$/g, '').replace(/'/g, "'"),
); // get the tick ' back again from HTMLified string
text = `${text.replace(matches[0], ` ${renderedKatex}`)}`;
} catch {
numInline -= 1;
}
matches = regex.exec(katexString); matches = regex.exec(katexString);
} }
} else { } else {
const matches = regex.exec(katexString); try {
text = katex.renderToString(matches[2]); text = katex.renderToString(matches[2].replace(/'/g, "'"));
} catch (error) {
numInline -= 1;
}
} }
} }
} }
return [text, numInline > 0];
}
renderer.paragraph = t => {
const [text, inline] = renderKatex(t);
return `<p class="${inline ? 'inline-katex' : ''}">${text}</p>`; return `<p class="${inline ? 'inline-katex' : ''}">${text}</p>`;
}; };
renderer.listitem = t => {
const [text, inline] = renderKatex(t);
return `<li class="${inline ? 'inline-katex' : ''}">${text}</li>`;
};
marked.setOptions({ marked.setOptions({
renderer, renderer,
......
...@@ -63,6 +63,9 @@ export default { ...@@ -63,6 +63,9 @@ export default {
}, },
rawCode(output) { rawCode(output) {
if (output.text) { if (output.text) {
if (typeof output.text === 'string') {
return output.text;
}
return output.text.join(''); return output.text.join('');
} }
......
---
title: Katex render and vscode output improvements for markdown
merge_request: 31433
author: Reinhold Gschweicher <pyro4hell@gmail.com>
type: fixed
...@@ -53,16 +53,32 @@ describe('Code component', () => { ...@@ -53,16 +53,32 @@ describe('Code component', () => {
}); });
}); });
describe('with string for output', () => {
// NBFormat Version 4.1 allows outputs.text to be a string
beforeEach(() => {
const cell = json.cells[2];
cell.outputs[0].text = cell.outputs[0].text.join('');
vm = setupComponent(cell);
return vm.$nextTick();
});
it('does not render output prompt', () => {
expect(vm.$el.querySelectorAll('.prompt').length).toBe(2);
});
it('renders output cell', () => {
expect(vm.$el.querySelector('.output')).toBeDefined();
});
});
describe('with string for cell.source', () => { describe('with string for cell.source', () => {
beforeEach(done => { beforeEach(() => {
const cell = json.cells[0]; const cell = json.cells[0];
cell.source = cell.source.join(''); cell.source = cell.source.join('');
vm = setupComponent(cell); vm = setupComponent(cell);
return vm.$nextTick();
setImmediate(() => {
done();
});
}); });
it('renders the same input as when cell.source is an array', () => { it('renders the same input as when cell.source is an array', () => {
......
...@@ -11,7 +11,7 @@ describe('Markdown component', () => { ...@@ -11,7 +11,7 @@ describe('Markdown component', () => {
let cell; let cell;
let json; let json;
beforeEach(done => { beforeEach(() => {
json = getJSONFixture('blob/notebook/basic.json'); json = getJSONFixture('blob/notebook/basic.json');
// eslint-disable-next-line prefer-destructuring // eslint-disable-next-line prefer-destructuring
...@@ -24,9 +24,7 @@ describe('Markdown component', () => { ...@@ -24,9 +24,7 @@ describe('Markdown component', () => {
}); });
vm.$mount(); vm.$mount();
setImmediate(() => { return vm.$nextTick();
done();
});
}); });
it('does not render promot', () => { it('does not render promot', () => {
...@@ -41,17 +39,15 @@ describe('Markdown component', () => { ...@@ -41,17 +39,15 @@ describe('Markdown component', () => {
expect(vm.$el.querySelector('.markdown h1')).not.toBeNull(); expect(vm.$el.querySelector('.markdown h1')).not.toBeNull();
}); });
it('sanitizes output', done => { it('sanitizes output', () => {
Object.assign(cell, { Object.assign(cell, {
source: [ source: [
'[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n', '[XSS](data:text/html;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5kb21haW4pPC9zY3JpcHQ+Cg==)\n',
], ],
}); });
Vue.nextTick(() => { return vm.$nextTick().then(() => {
expect(vm.$el.querySelector('a').getAttribute('href')).toBeNull(); expect(vm.$el.querySelector('a').getAttribute('href')).toBeNull();
done();
}); });
}); });
...@@ -60,45 +56,111 @@ describe('Markdown component', () => { ...@@ -60,45 +56,111 @@ describe('Markdown component', () => {
json = getJSONFixture('blob/notebook/math.json'); json = getJSONFixture('blob/notebook/math.json');
}); });
it('renders multi-line katex', done => { it('renders multi-line katex', () => {
vm = new Component({ vm = new Component({
propsData: { propsData: {
cell: json.cells[0], cell: json.cells[0],
}, },
}).$mount(); }).$mount();
Vue.nextTick(() => { return vm.$nextTick().then(() => {
expect(vm.$el.querySelector('.katex')).not.toBeNull(); expect(vm.$el.querySelector('.katex')).not.toBeNull();
done();
}); });
}); });
it('renders inline katex', done => { it('renders inline katex', () => {
vm = new Component({ vm = new Component({
propsData: { propsData: {
cell: json.cells[1], cell: json.cells[1],
}, },
}).$mount(); }).$mount();
Vue.nextTick(() => { return vm.$nextTick().then(() => {
expect(vm.$el.querySelector('p:first-child .katex')).not.toBeNull(); expect(vm.$el.querySelector('p:first-child .katex')).not.toBeNull();
done();
}); });
}); });
it('renders multiple inline katex', done => { it('renders multiple inline katex', () => {
vm = new Component({ vm = new Component({
propsData: { propsData: {
cell: json.cells[1], cell: json.cells[1],
}, },
}).$mount(); }).$mount();
Vue.nextTick(() => { return vm.$nextTick().then(() => {
expect(vm.$el.querySelectorAll('p:nth-child(2) .katex').length).toBe(4); expect(vm.$el.querySelectorAll('p:nth-child(2) .katex').length).toBe(4);
});
});
it('output cell in case of katex error', () => {
vm = new Component({
propsData: {
cell: {
cell_type: 'markdown',
metadata: {},
source: ['Some invalid $a & b$ inline formula $b & c$\n', '\n'],
},
},
}).$mount();
return vm.$nextTick().then(() => {
// expect one paragraph with no katex formula in it
expect(vm.$el.querySelectorAll('p').length).toBe(1);
expect(vm.$el.querySelectorAll('p .katex').length).toBe(0);
});
});
it('output cell and render remaining formula in case of katex error', () => {
vm = new Component({
propsData: {
cell: {
cell_type: 'markdown',
metadata: {},
source: ['An invalid $a & b$ inline formula and a vaild one $b = c$\n', '\n'],
},
},
}).$mount();
return vm.$nextTick().then(() => {
// expect one paragraph with no katex formula in it
expect(vm.$el.querySelectorAll('p').length).toBe(1);
expect(vm.$el.querySelectorAll('p .katex').length).toBe(1);
});
});
it('renders math formula in list object', () => {
vm = new Component({
propsData: {
cell: {
cell_type: 'markdown',
metadata: {},
source: ["- list with inline $a=2$ inline formula $a' + b = c$\n", '\n'],
},
},
}).$mount();
return vm.$nextTick().then(() => {
// expect one list with a katex formula in it
expect(vm.$el.querySelectorAll('li').length).toBe(1);
expect(vm.$el.querySelectorAll('li .katex').length).toBe(2);
});
});
it("renders math formula with tick ' in it", () => {
vm = new Component({
propsData: {
cell: {
cell_type: 'markdown',
metadata: {},
source: ["- list with inline $a=2$ inline formula $a' + b = c$\n", '\n'],
},
},
}).$mount();
done(); return vm.$nextTick().then(() => {
// expect one list with a katex formula in it
expect(vm.$el.querySelectorAll('li').length).toBe(1);
expect(vm.$el.querySelectorAll('li .katex').length).toBe(2);
}); });
}); });
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment