mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
All: Resolves #443: Various optimisations to make dealing with large notes easier and make Katex re-rendering faster
This commit is contained in:
parent
ef2ffd4e52
commit
b9118a90be
@ -44,4 +44,13 @@ describe('ArrayUtils', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should compare arrays', async (done) => {
|
||||
expect(ArrayUtils.contentEquals([], [])).toBe(true);
|
||||
expect(ArrayUtils.contentEquals(['a'], ['a'])).toBe(true);
|
||||
expect(ArrayUtils.contentEquals(['b', 'a'], ['a', 'b'])).toBe(true);
|
||||
expect(ArrayUtils.contentEquals(['b'], ['a', 'b'])).toBe(false);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
@ -23,6 +23,8 @@ const fs = require('fs-extra');
|
||||
const {clipboard} = require('electron')
|
||||
const md5 = require('md5');
|
||||
const mimeUtils = require('lib/mime-utils.js').mime;
|
||||
const NoteBodyViewer = require('./NoteBodyViewer.min.js');
|
||||
const ArrayUtils = require('lib/ArrayUtils');
|
||||
|
||||
require('brace/mode/markdown');
|
||||
// https://ace.c9.io/build/kitchen-sink.html
|
||||
@ -50,6 +52,7 @@ class NoteTextComponent extends React.Component {
|
||||
// changed by the user, this variable contains that note ID. Used
|
||||
// to automatically set the title.
|
||||
newAndNoTitleChangeNoteId: null,
|
||||
bodyHtml: '',
|
||||
};
|
||||
|
||||
this.lastLoadedNoteId_ = null;
|
||||
@ -58,6 +61,8 @@ class NoteTextComponent extends React.Component {
|
||||
this.ignoreNextEditorScroll_ = false;
|
||||
this.scheduleSaveTimeout_ = null;
|
||||
this.restoreScrollTop_ = null;
|
||||
this.lastSetHtml_ = '';
|
||||
this.lastSetMarkers_ = [];
|
||||
|
||||
// Complicated but reliable method to get editor content height
|
||||
// https://github.com/ajaxorg/ace/issues/2046
|
||||
@ -302,7 +307,12 @@ class NoteTextComponent extends React.Component {
|
||||
newState.newAndNoTitleChangeNoteId = null;
|
||||
}
|
||||
|
||||
this.lastSetHtml_ = '';
|
||||
this.lastSetMarkers_ = [];
|
||||
|
||||
this.setState(newState);
|
||||
|
||||
this.updateHtml(newState.note ? newState.note.body : '');
|
||||
}
|
||||
|
||||
async componentWillReceiveProps(nextProps) {
|
||||
@ -542,9 +552,52 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
aceEditor_change(body) {
|
||||
shared.noteComponent_change(this, 'body', body);
|
||||
this.scheduleHtmlUpdate();
|
||||
this.scheduleSave();
|
||||
}
|
||||
|
||||
scheduleHtmlUpdate(timeout = 500) {
|
||||
if (this.scheduleHtmlUpdateIID_) {
|
||||
clearTimeout(this.scheduleHtmlUpdateIID_);
|
||||
this.scheduleHtmlUpdateIID_ = null;
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
this.scheduleHtmlUpdateIID_ = setTimeout(() => {
|
||||
this.updateHtml();
|
||||
}, timeout);
|
||||
} else {
|
||||
this.updateHtml();
|
||||
}
|
||||
}
|
||||
|
||||
updateHtml(body = null) {
|
||||
const mdOptions = {
|
||||
onResourceLoaded: () => {
|
||||
this.updateHtml();
|
||||
this.forceUpdate();
|
||||
},
|
||||
postMessageSyntax: 'ipcRenderer.sendToHost',
|
||||
};
|
||||
|
||||
const theme = themeStyle(this.props.theme);
|
||||
|
||||
let bodyToRender = body;
|
||||
if (bodyToRender === null) bodyToRender = this.state.note && this.state.note.body ? this.state.note.body : '';
|
||||
let bodyHtml = '';
|
||||
|
||||
const visiblePanes = this.props.visiblePanes || ['editor', 'viewer'];
|
||||
|
||||
if (!bodyToRender.trim() && visiblePanes.indexOf('viewer') >= 0 && visiblePanes.indexOf('editor') < 0) {
|
||||
// Fixes https://github.com/laurent22/joplin/issues/217
|
||||
bodyToRender = '*' + _('This note has no content. Click on "%s" to toggle the editor and edit the note.', _('Layout')) + '*';
|
||||
}
|
||||
|
||||
bodyHtml = this.mdToHtml().render(bodyToRender, theme, mdOptions);
|
||||
|
||||
this.setState({ bodyHtml: bodyHtml });
|
||||
}
|
||||
|
||||
async doCommand(command) {
|
||||
if (!command) return;
|
||||
|
||||
@ -601,6 +654,8 @@ class NoteTextComponent extends React.Component {
|
||||
note: Object.assign({}, note),
|
||||
lastSavedNote: Object.assign({}, note),
|
||||
});
|
||||
|
||||
this.updateHtml(note.body);
|
||||
} catch (error) {
|
||||
reg.logger().error(error);
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
@ -748,25 +803,21 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
|
||||
if (this.state.webviewReady) {
|
||||
const mdOptions = {
|
||||
onResourceLoaded: () => {
|
||||
this.forceUpdate();
|
||||
},
|
||||
postMessageSyntax: 'ipcRenderer.sendToHost',
|
||||
};
|
||||
let html = this.state.bodyHtml;
|
||||
|
||||
let bodyToRender = body;
|
||||
if (!bodyToRender.trim() && visiblePanes.indexOf('viewer') >= 0 && visiblePanes.indexOf('editor') < 0) {
|
||||
// Fixes https://github.com/laurent22/joplin/issues/217
|
||||
bodyToRender = '*' + _('This note has no content. Click on "%s" to toggle the editor and edit the note.', _('Layout')) + '*';
|
||||
const htmlHasChanged = this.lastSetHtml_ !== html;
|
||||
if (htmlHasChanged) {
|
||||
this.webview_.send('setHtml', html);
|
||||
this.lastSetHtml_ = html;
|
||||
}
|
||||
|
||||
const html = this.mdToHtml().render(bodyToRender, theme, mdOptions);
|
||||
this.webview_.send('setHtml', html);
|
||||
|
||||
const search = BaseModel.byId(this.props.searches, this.props.selectedSearchId);
|
||||
const keywords = search ? Search.keywords(search.query_pattern) : [];
|
||||
this.webview_.send('setMarkers', keywords);
|
||||
|
||||
if (htmlHasChanged || !ArrayUtils.contentEquals(this.lastSetMarkers_, keywords)) {
|
||||
this.lastSetMarkers_ = [];
|
||||
this.webview_.send('setMarkers', keywords);
|
||||
}
|
||||
}
|
||||
|
||||
const toolbarItems = [];
|
||||
@ -812,7 +863,7 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
const titleBarDate = <span style={Object.assign({}, theme.textStyle, {color: theme.colorFaded})}>{time.formatMsToLocal(note.user_updated_time)}</span>
|
||||
|
||||
const viewer = <webview
|
||||
const viewer = <webview
|
||||
style={viewerStyle}
|
||||
nodeintegration="1"
|
||||
src="gui/note-viewer/index.html"
|
||||
|
@ -45,4 +45,17 @@ ArrayUtils.findByKey = function(array, key, value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ArrayUtils.contentEquals = function(array1, array2) {
|
||||
if (array1 === array2) return true;
|
||||
if (!array1.length && !array2.length) return true;
|
||||
if (array1.length !== array2.length) return false;
|
||||
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
const a1 = array1[i];
|
||||
if (array2.indexOf(a1) < 0) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = ArrayUtils;
|
@ -157,9 +157,12 @@ class MdToHtml {
|
||||
rendererPlugin_(language) {
|
||||
if (!language) return null;
|
||||
|
||||
const handlers = {};
|
||||
handlers['katex'] = new MdToHtml_Katex();
|
||||
return language in handlers ? handlers[language] : null;
|
||||
if (!this.rendererPlugins_) {
|
||||
this.rendererPlugins_ = {};
|
||||
this.rendererPlugins_['katex'] = new MdToHtml_Katex();
|
||||
}
|
||||
|
||||
return language in this.rendererPlugins_ ? this.rendererPlugins_[language] : null;
|
||||
}
|
||||
|
||||
parseInlineCodeLanguage_(content) {
|
||||
|
@ -5,15 +5,28 @@ const Setting = require('lib/models/Setting');
|
||||
|
||||
class MdToHtml_Katex {
|
||||
|
||||
constructor() {
|
||||
this.cache_ = {};
|
||||
this.assetsLoaded_ = false;
|
||||
}
|
||||
|
||||
name() {
|
||||
return 'katex';
|
||||
}
|
||||
|
||||
processContent(renderedTokens, content, tagType) {
|
||||
try {
|
||||
let renderered = katex.renderToString(content, {
|
||||
displayMode: tagType === 'block',
|
||||
});
|
||||
const cacheKey = tagType + '_' + content;
|
||||
let renderered = null;
|
||||
|
||||
if (this.cache_[cacheKey]) {
|
||||
renderered = this.cache_[cacheKey];
|
||||
} else {
|
||||
renderered = katex.renderToString(content, {
|
||||
displayMode: tagType === 'block',
|
||||
});
|
||||
this.cache_[cacheKey] = renderered;
|
||||
}
|
||||
|
||||
if (tagType === 'block') renderered = '<p>' + renderered + '</p>';
|
||||
|
||||
@ -29,6 +42,8 @@ class MdToHtml_Katex {
|
||||
}
|
||||
|
||||
async loadAssets() {
|
||||
if (this.assetsLoaded_) return;
|
||||
|
||||
// In node, the fonts are simply copied using copycss to where Katex expects to find them, which is under app/gui/note-viewer/fonts
|
||||
|
||||
// In React Native, it's more complicated and we need to download and copy them to the right directory. Ideally, we should embed
|
||||
@ -43,6 +58,8 @@ class MdToHtml_Katex {
|
||||
await shim.fetchBlob('https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/fonts/KaTeX_Math-Italic.woff2', { overwrite: false, path: baseDir + '/fonts/KaTeX_Math-Italic.woff2' });
|
||||
await shim.fetchBlob('https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.9.0-beta1/fonts/KaTeX_Size1-Regular.woff2', { overwrite: false, path: baseDir + '/fonts/KaTeX_Size1-Regular.woff2' });
|
||||
}
|
||||
|
||||
this.assetsLoaded_ = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user