mirror of
https://github.com/mastodon/mastodon.git
synced 2025-10-06 00:52:42 +00:00
add some time logging
This commit is contained in:
parent
966e9889ee
commit
8befbb2191
|
@ -1,7 +1,6 @@
|
||||||
import { autoPlayGif } from '@/mastodon/initial_state';
|
import { autoPlayGif } from '@/mastodon/initial_state';
|
||||||
import { createLimitedCache } from '@/mastodon/utils/cache';
|
import { createLimitedCache } from '@/mastodon/utils/cache';
|
||||||
import { assetHost } from '@/mastodon/utils/config';
|
import { assetHost } from '@/mastodon/utils/config';
|
||||||
import * as perf from '@/mastodon/utils/performance';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
EMOJI_MODE_NATIVE,
|
EMOJI_MODE_NATIVE,
|
||||||
|
@ -42,16 +41,17 @@ export function emojifyElement<Element extends HTMLElement>(
|
||||||
appState: EmojiAppState,
|
appState: EmojiAppState,
|
||||||
extraEmojis: ExtraCustomEmojiMap = {},
|
extraEmojis: ExtraCustomEmojiMap = {},
|
||||||
): Element | null {
|
): Element | null {
|
||||||
|
const finish = timingMeasurementHelper('emojifyElement');
|
||||||
// Check the cache and return it if we get a hit.
|
// Check the cache and return it if we get a hit.
|
||||||
const cacheKey = createTextCacheKey(element, appState, extraEmojis);
|
const cacheKey = createTextCacheKey(element, appState, extraEmojis);
|
||||||
const cached = textCache.get(cacheKey);
|
const cached = textCache.get(cacheKey);
|
||||||
if (cached !== undefined) {
|
if (cached !== undefined) {
|
||||||
log('Cache hit on %s', element.outerHTML);
|
log('Cache hit on %s', element.outerHTML);
|
||||||
if (cached === null) {
|
if (cached === null) {
|
||||||
return null;
|
// return null;
|
||||||
}
|
}
|
||||||
element.innerHTML = cached;
|
// element.innerHTML = cached;
|
||||||
return element;
|
// return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exit if there are no emoji in the string.
|
// Exit if there are no emoji in the string.
|
||||||
|
@ -60,7 +60,6 @@ export function emojifyElement<Element extends HTMLElement>(
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
perf.start('emojifyElement()');
|
|
||||||
const queue: (HTMLElement | Text)[] = [element];
|
const queue: (HTMLElement | Text)[] = [element];
|
||||||
while (queue.length > 0) {
|
while (queue.length > 0) {
|
||||||
const current = queue.shift();
|
const current = queue.shift();
|
||||||
|
@ -76,8 +75,9 @@ export function emojifyElement<Element extends HTMLElement>(
|
||||||
current.textContent &&
|
current.textContent &&
|
||||||
(current instanceof Text || !current.hasChildNodes())
|
(current instanceof Text || !current.hasChildNodes())
|
||||||
) {
|
) {
|
||||||
const renderedContent = textToElementArray(
|
const tokens = tokenizeText(current.textContent, appState.mode);
|
||||||
current.textContent,
|
const renderedContent = tokensToElementArray(
|
||||||
|
tokens,
|
||||||
appState,
|
appState,
|
||||||
extraEmojis,
|
extraEmojis,
|
||||||
);
|
);
|
||||||
|
@ -97,7 +97,7 @@ export function emojifyElement<Element extends HTMLElement>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
textCache.set(cacheKey, element.innerHTML);
|
textCache.set(cacheKey, element.innerHTML);
|
||||||
perf.stop('emojifyElement()');
|
finish();
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,7 +116,8 @@ export function emojifyText(
|
||||||
textCache.set(cacheKey, null);
|
textCache.set(cacheKey, null);
|
||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
const eleArray = textToElementArray(text, appState, extraEmojis);
|
const tokens = tokenizeText(text, appState.mode);
|
||||||
|
const eleArray = tokensToElementArray(tokens, appState, extraEmojis);
|
||||||
if (!eleArray) {
|
if (!eleArray) {
|
||||||
textCache.set(cacheKey, null);
|
textCache.set(cacheKey, null);
|
||||||
return text;
|
return text;
|
||||||
|
@ -158,51 +159,14 @@ function cacheForType(type: EmojiType) {
|
||||||
return type === EMOJI_TYPE_UNICODE ? unicodeEmojiCache : customEmojiCache;
|
return type === EMOJI_TYPE_UNICODE ? unicodeEmojiCache : customEmojiCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
type EmojifiedTextArray = (string | HTMLImageElement)[];
|
// Tokenization. This takes the HTML string and converts it into an array of emoji types that are cached.
|
||||||
|
|
||||||
function textToElementArray(
|
|
||||||
text: string,
|
|
||||||
appState: EmojiAppState,
|
|
||||||
extraEmojis: ExtraCustomEmojiMap = {},
|
|
||||||
): EmojifiedTextArray | null {
|
|
||||||
// Exit if no text to convert.
|
|
||||||
if (!text.trim()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokens = tokenizeText(text, appState.mode);
|
|
||||||
|
|
||||||
// If only one token and it's a string, exit early.
|
|
||||||
if (tokens.length === 1 && typeof tokens[0] === 'string') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderedFragments: EmojifiedTextArray = [];
|
|
||||||
for (const token of tokens) {
|
|
||||||
// Plain text does not need to be converted.
|
|
||||||
if (typeof token === 'string') {
|
|
||||||
renderedFragments.push(token);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this is a provided custom emoji and use that if so.
|
|
||||||
if (token.type === EMOJI_TYPE_CUSTOM) {
|
|
||||||
const extraEmojiData = extraEmojis[token.code];
|
|
||||||
if (extraEmojiData) {
|
|
||||||
token.data = extraEmojiData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create an image element from the token and add it to the the fragments.
|
|
||||||
const image = stateToImage(token, appState);
|
|
||||||
renderedFragments.push(image);
|
|
||||||
}
|
|
||||||
|
|
||||||
return renderedFragments;
|
|
||||||
}
|
|
||||||
|
|
||||||
type TokenizedText = (string | Exclude<EmojiState, EmojiStateMissing>)[];
|
type TokenizedText = (string | Exclude<EmojiState, EmojiStateMissing>)[];
|
||||||
|
|
||||||
|
const tokenCache = createLimitedCache<TokenizedText>({
|
||||||
|
log: log.extend('tokens'),
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accepts incoming text strings and breaks them into an array of state tokens.
|
* Accepts incoming text strings and breaks them into an array of state tokens.
|
||||||
*/
|
*/
|
||||||
|
@ -211,6 +175,13 @@ export function tokenizeText(text: string, mode: EmojiMode): TokenizedText {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const finish = timingMeasurementHelper('tokenizeText');
|
||||||
|
|
||||||
|
const cached = tokenCache.get(text);
|
||||||
|
if (cached) {
|
||||||
|
// return cached;
|
||||||
|
}
|
||||||
|
|
||||||
const tokens = [];
|
const tokens = [];
|
||||||
let lastIndex = 0;
|
let lastIndex = 0;
|
||||||
for (const match of text.matchAll(anyEmojiRegex())) {
|
for (const match of text.matchAll(anyEmojiRegex())) {
|
||||||
|
@ -238,10 +209,9 @@ export function tokenizeText(text: string, mode: EmojiMode): TokenizedText {
|
||||||
|
|
||||||
if (cachedData === EMOJI_STATE_MISSING) {
|
if (cachedData === EMOJI_STATE_MISSING) {
|
||||||
continue; // Exit if we know this is missing.
|
continue; // Exit if we know this is missing.
|
||||||
} else if (cachedData) {
|
|
||||||
tokens.push(cachedData); // We already cached this token, so just use that.
|
|
||||||
} else {
|
} else {
|
||||||
// This is possibly an emoji!
|
// This is possibly an emoji!
|
||||||
|
// We are not saving the data in here to keep cache sizes small.
|
||||||
tokens.push({
|
tokens.push({
|
||||||
type,
|
type,
|
||||||
code,
|
code,
|
||||||
|
@ -256,6 +226,7 @@ export function tokenizeText(text: string, mode: EmojiMode): TokenizedText {
|
||||||
if (lastIndex < text.length) {
|
if (lastIndex < text.length) {
|
||||||
tokens.push(text.slice(lastIndex));
|
tokens.push(text.slice(lastIndex));
|
||||||
}
|
}
|
||||||
|
finish();
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,9 +244,57 @@ function shouldRenderUnicodeImage(code: string, mode: EmojiMode): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EmojifiedTextArray = (string | HTMLImageElement)[];
|
||||||
|
|
||||||
|
function tokensToElementArray(
|
||||||
|
tokens: TokenizedText,
|
||||||
|
appState: EmojiAppState,
|
||||||
|
extraEmojis: ExtraCustomEmojiMap = {},
|
||||||
|
): EmojifiedTextArray | null {
|
||||||
|
// If only one token and it's a string, exit early.
|
||||||
|
if (tokens.length === 1 && typeof tokens[0] === 'string') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const finish = timingMeasurementHelper('tokensToElementArray');
|
||||||
|
|
||||||
|
const renderedFragments: EmojifiedTextArray = [];
|
||||||
|
for (const token of tokens) {
|
||||||
|
// Plain text does not need to be converted.
|
||||||
|
if (typeof token === 'string') {
|
||||||
|
renderedFragments.push(token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this is a provided custom emoji and use that if so.
|
||||||
|
if (token.type === EMOJI_TYPE_CUSTOM) {
|
||||||
|
const extraEmojiData = extraEmojis[token.code];
|
||||||
|
if (extraEmojiData) {
|
||||||
|
token.data = extraEmojiData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise, load the data from the cache if it exists.
|
||||||
|
if (!token.data) {
|
||||||
|
const cache = cacheForType(token.type);
|
||||||
|
const cached = cache.get(token.code);
|
||||||
|
if (cached !== EMOJI_STATE_MISSING && cached?.data) {
|
||||||
|
token.data = cached.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an image element from the token and add it to the the fragments.
|
||||||
|
const image = stateToImage(token, appState);
|
||||||
|
renderedFragments.push(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
finish();
|
||||||
|
return renderedFragments;
|
||||||
|
}
|
||||||
|
|
||||||
const EMOJI_SIZE = 16;
|
const EMOJI_SIZE = 16;
|
||||||
|
|
||||||
function stateToImage(state: EmojiStateToken, appState: EmojiAppState) {
|
function stateToImage(state: EmojiStateToken, appState: EmojiAppState) {
|
||||||
|
const finish = timingMeasurementHelper('stateToImage');
|
||||||
const image = document.createElement('img');
|
const image = document.createElement('img');
|
||||||
image.draggable = false;
|
image.draggable = false;
|
||||||
image.classList.add('emojione');
|
image.classList.add('emojione');
|
||||||
|
@ -295,6 +314,7 @@ function stateToImage(state: EmojiStateToken, appState: EmojiAppState) {
|
||||||
imageAttributesFromState(image, state, appState.darkTheme);
|
imageAttributesFromState(image, state, appState.darkTheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finish();
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,6 +414,14 @@ function renderedToHTML(
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function timingMeasurementHelper(name: string) {
|
||||||
|
const start = performance.now();
|
||||||
|
return () => {
|
||||||
|
const duration = performance.now() - start;
|
||||||
|
log.extend('timing')('timing for %s: %d', name, duration);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Testing helpers
|
// Testing helpers
|
||||||
export const testCacheClear = () => {
|
export const testCacheClear = () => {
|
||||||
textCache.clear();
|
textCache.clear();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user