DEV: replace diffhtml with morphlex (#26138)

morphlex is ~20% the size of diffhtml and provides a 2-6x speedup depending on diff size.

See t/113634/13 for benchmarks.
This commit is contained in:
Régis Hanol 2024-04-03 11:27:45 +02:00 committed by GitHub
parent 1e42e86601
commit c5f1fc3a08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 66 additions and 67 deletions

View File

@ -23,6 +23,8 @@
@editingPost={{this.composer.editingPost}}
@disabled={{this.disableTextarea}}
@outletArgs={{hash composer=this.composer editorType="composer"}}
@topicId={{this.composer.topic.id}}
@categoryId={{this.composer.category.id}}
>
{{yield}}
</DEditor>

View File

@ -17,7 +17,6 @@ import { wantsNewWindow } from "discourse/lib/intercept-click";
import { PLATFORM_KEY_MODIFIER } from "discourse/lib/keyboard-shortcuts";
import { linkSeenMentions } from "discourse/lib/link-mentions";
import { loadOneboxes } from "discourse/lib/load-oneboxes";
import loadScript from "discourse/lib/load-script";
import { emojiUrlFor, generateCookFunction } from "discourse/lib/text";
import { siteDir } from "discourse/lib/text-direction";
import {
@ -413,92 +412,84 @@ export default Component.extend(TextareaTextManipulation, {
return toolbar;
},
cachedCookAsync(text) {
if (this._cachedCookFunction) {
return Promise.resolve(this._cachedCookFunction(text));
}
const markdownOptions = this.markdownOptions || {};
return generateCookFunction(markdownOptions).then((cook) => {
this._cachedCookFunction = cook;
return cook(text);
});
async cachedCookAsync(text, options) {
this._cachedCookFunction ||= await generateCookFunction(options || {});
return await this._cachedCookFunction(text);
},
_updatePreview() {
if (this._state !== "inDOM" || !this.processPreview) {
async _updatePreview() {
if (
this._state !== "inDOM" ||
!this.processPreview ||
this.isDestroying ||
this.isDestroyed
) {
return;
}
const value = this.value;
const cooked = await this.cachedCookAsync(this.value, this.markdownOptions);
this.cachedCookAsync(value).then((cooked) => {
if (this.isDestroyed) {
if (this.preview === cooked || this.isDestroying || this.isDestroyed) {
return;
}
this.set("preview", cooked);
if (this.siteSettings.enable_diffhtml_preview) {
const previewElement = this.element.querySelector(".d-editor-preview");
const cookedElement = previewElement.cloneNode(false);
cookedElement.innerHTML = cooked;
// Same order of operation as in the "previewUpdated" method in "composer-editor.js"
linkSeenMentions(cookedElement, this.siteSettings);
linkSeenHashtagsInContext(
this.site.hashtag_configurations["topic-composer"],
cookedElement
);
loadOneboxes(
cookedElement,
ajax,
this.topicId,
this.categoryId,
this.siteSettings.max_oneboxes_per_post,
false,
true
);
resolveCachedShortUrls(this.siteSettings, cookedElement);
(await import("morphlex")).morph(previewElement, cookedElement);
}
schedule("afterRender", () => {
if (
this._state !== "inDOM" ||
!this.element ||
this.isDestroying ||
this.isDestroyed
) {
return;
}
if (this.preview === cooked) {
return;
const previewElement = this.element.querySelector(".d-editor-preview");
if (previewElement && this.previewUpdated) {
this.previewUpdated(previewElement);
}
this.set("preview", cooked);
let previewPromise = Promise.resolve();
if (this.siteSettings.enable_diffhtml_preview) {
const cookedElement = document.createElement("div");
cookedElement.innerHTML = cooked;
linkSeenHashtagsInContext(
this.site.hashtag_configurations["topic-composer"],
cookedElement
);
linkSeenMentions(cookedElement, this.siteSettings);
resolveCachedShortUrls(this.siteSettings, cookedElement);
loadOneboxes(
cookedElement,
ajax,
null,
null,
this.siteSettings.max_oneboxes_per_post,
false,
true
);
previewPromise = loadScript("/javascripts/diffhtml.min.js").then(() => {
const previewElement =
this.element.querySelector(".d-editor-preview");
window.diff.innerHTML(previewElement, cookedElement.innerHTML);
});
}
previewPromise.then(() => {
schedule("afterRender", () => {
if (this._state !== "inDOM" || !this.element) {
return;
}
const preview = this.element.querySelector(".d-editor-preview");
if (!preview) {
return;
}
if (this.previewUpdated) {
this.previewUpdated(preview);
}
});
});
});
},
@observes("ready", "value", "processPreview")
_watchForChanges() {
async _watchForChanges() {
if (!this.ready) {
return;
}
// Debouncing in test mode is complicated
if (isTesting()) {
this._updatePreview();
await this._updatePreview();
} else {
discourseDebounce(this, this._updatePreview, 30);
}

View File

@ -25,6 +25,7 @@
"handlebars": "^4.7.8",
"highlight.js": "^11.9.0",
"jspreadsheet-ce": "^4.13.4",
"morphlex": "^0.0.15",
"pretty-text": "1.0.0"
},
"devDependencies": {

View File

@ -9776,6 +9776,11 @@ morgan@^1.10.0:
on-finished "~2.3.0"
on-headers "~1.0.2"
morphlex@^0.0.15:
version "0.0.15"
resolved "https://registry.yarnpkg.com/morphlex/-/morphlex-0.0.15.tgz#651eaeec36424a980f02ed8a6dada7f2785b71b3"
integrity sha512-XUJykOS7kpwA8byZYp5jYPwnx/MFDESC+rL3vXyETpWNw6Lb5icQZxaoVn/qDLpbpEaawUXielFIsxCjrz+g/g==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"