add cache manager, some caches

cache-manager
Steve Ruiz 2024-05-11 09:35:16 +01:00
rodzic 91903c9761
commit d592995314
8 zmienionych plików z 92 dodań i 37 usunięć

Wyświetl plik

@ -82,6 +82,7 @@ import { useQuickReactor } from '@tldraw/state';
import { useReactor } from '@tldraw/state';
import { useValue } from '@tldraw/state';
import { VecModel } from '@tldraw/tlschema';
import { WeakCache } from '@tldraw/utils';
import { whyAmIRunning } from '@tldraw/state';
// @public
@ -699,6 +700,7 @@ export class Editor extends EventEmitter<TLEventMap> {
};
bringForward(shapes: TLShape[] | TLShapeId[]): this;
bringToFront(shapes: TLShape[] | TLShapeId[]): this;
readonly caches: CacheManager;
cancel(): this;
cancelDoubleClick(): void;
// @internal (undocumented)

Wyświetl plik

@ -123,6 +123,7 @@ import { notVisibleShapes } from './derivations/notVisibleShapes'
import { parentsToChildren } from './derivations/parentsToChildren'
import { deriveShapeIdsInCurrentPage } from './derivations/shapeIdsInCurrentPage'
import { getSvgJsx } from './getSvgJsx'
import { CacheManager } from './managers/CacheManager'
import { ClickManager } from './managers/ClickManager'
import { EnvironmentManager } from './managers/EnvironmentManager'
import { HistoryManager } from './managers/HistoryManager'
@ -297,6 +298,7 @@ export class Editor extends EventEmitter<TLEventMap> {
this.environment = new EnvironmentManager(this)
this.scribbles = new ScribbleManager(this)
this.caches = new CacheManager(this)
// Cleanup
@ -711,6 +713,13 @@ export class Editor extends EventEmitter<TLEventMap> {
*/
readonly sideEffects: SideEffectManager<this>
/**
* A manager for weak map caches.
*
* @public
*/
readonly caches: CacheManager
/**
* The current HTML element containing the editor.
*

Wyświetl plik

@ -0,0 +1,28 @@
import { WeakCache } from '@tldraw/utils'
import { Editor } from '../Editor'
export class CacheManager {
constructor(public editor: Editor) {}
private caches = new Map<string, WeakCache<any, unknown>>()
createCache<T extends object, Q>(name: string) {
const cache = new WeakCache<T, Q>()
this.caches.set(name, cache)
return cache
}
get<T extends object, Q>(name: string): WeakCache<T, Q> {
return this.caches.get(name) as WeakCache<T, Q>
}
clear(name: string) {
const cache = this.caches.get(name)
if (!cache) throw Error(`Cache ${name} not found`)
cache.clear()
}
clearAll() {
this.caches.clear()
}
}

Wyświetl plik

@ -10,7 +10,6 @@ import {
TLOnHandleDragHandler,
TLOnResizeHandler,
Vec,
WeakCache,
getIndexBetween,
getIndices,
lineShapeMigrations,
@ -30,8 +29,6 @@ import {
getSvgPathForLineGeometry,
} from './components/svg'
const handlesCache = new WeakCache<TLLineShape['props'], TLHandle[]>()
/** @public */
export class LineShapeUtil extends ShapeUtil<TLLineShape> {
static override type = 'line' as const
@ -63,33 +60,31 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
}
override getHandles(shape: TLLineShape) {
return handlesCache.get(shape.props, () => {
const spline = getGeometryForLineShape(shape)
const spline = getGeometryForLineShape(shape)
const points = linePointsToArray(shape)
const results: TLHandle[] = points.map((point) => ({
...point,
id: point.index,
type: 'vertex',
const points = linePointsToArray(shape)
const results: TLHandle[] = points.map((point) => ({
...point,
id: point.index,
type: 'vertex',
canSnap: true,
}))
for (let i = 0; i < points.length - 1; i++) {
const index = getIndexBetween(points[i].index, points[i + 1].index)
const segment = spline.segments[i]
const point = segment.midPoint()
results.push({
id: index,
type: 'create',
index,
x: point.x,
y: point.y,
canSnap: true,
}))
})
}
for (let i = 0; i < points.length - 1; i++) {
const index = getIndexBetween(points[i].index, points[i + 1].index)
const segment = spline.segments[i]
const point = segment.midPoint()
results.push({
id: index,
type: 'create',
index,
x: point.x,
y: point.y,
canSnap: true,
})
}
return results.sort(sortByIndex)
})
return results.sort(sortByIndex)
}
// Events
@ -164,7 +159,8 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
return {
points,
getSelfSnapPoints: (handle) => {
const index = this.getHandles(shape)
const index = this.editor
.getShapeHandles<TLLineShape>(shape)!
.filter((h) => h.type === 'vertex')
.findIndex((h) => h.id === handle.id)!
@ -175,7 +171,8 @@ export class LineShapeUtil extends ShapeUtil<TLLineShape> {
// We want to skip the segments that include the handle, so
// find the index of the handle that shares the same index property
// as the initial dragging handle; this catches a quirk of create handles
const index = this.getHandles(shape)
const index = this.editor
.getShapeHandles<TLLineShape>(shape)!
.filter((h) => h.type === 'vertex')
.findIndex((h) => h.id === handle.id)!

Wyświetl plik

@ -11,7 +11,6 @@ import {
TLShape,
TLShapeId,
Vec,
WeakCache,
getDefaultColorTheme,
noteShapeMigrations,
noteShapeProps,
@ -372,10 +371,16 @@ function getNoteLabelSize(editor: Editor, shape: TLNoteShape) {
}
}
const labelSizesForNote = new WeakCache<TLShape, ReturnType<typeof getNoteLabelSize>>()
function getLabelSize(editor: Editor, shape: TLNoteShape) {
return labelSizesForNote.get(shape, () => getNoteLabelSize(editor, shape))
let cache = editor.caches.get<TLShape, ReturnType<typeof getNoteLabelSize>>(
'@tldraw/noteLabelSize'
)
if (!cache) {
cache = editor.caches.createCache<TLShape, ReturnType<typeof getNoteLabelSize>>(
'@tldraw/noteLabelSize'
)
}
return cache.get(shape, () => getNoteLabelSize(editor, shape))
}
function useNoteKeydownHandler(id: TLShapeId) {

Wyświetl plik

@ -11,7 +11,6 @@ import {
TLShapeUtilFlag,
TLTextShape,
Vec,
WeakCache,
getDefaultColorTheme,
preventDefault,
textShapeMigrations,
@ -28,8 +27,6 @@ import { FONT_FAMILIES, FONT_SIZES, TEXT_PROPS } from '../shared/default-shape-c
import { getFontDefForExport } from '../shared/defaultStyleDefs'
import { resizeScaled } from '../shared/resizeScaled'
const sizeCache = new WeakCache<TLTextShape['props'], { height: number; width: number }>()
/** @public */
export class TextShapeUtil extends ShapeUtil<TLTextShape> {
static override type = 'text' as const
@ -50,7 +47,16 @@ export class TextShapeUtil extends ShapeUtil<TLTextShape> {
}
getMinDimensions(shape: TLTextShape) {
return sizeCache.get(shape.props, (props) => getTextSize(this.editor, props))
const { editor } = this
let cache = editor.caches.get<TLTextShape['props'], { height: number; width: number }>(
'@tldraw/textShapeSize'
)
if (!cache) {
cache = editor.caches.createCache<TLTextShape['props'], { height: number; width: number }>(
'@tldraw/textShapeSize'
)
}
return cache.get(shape.props, (props) => getTextSize(this.editor, props))
}
getGeometry(shape: TLTextShape) {

Wyświetl plik

@ -349,6 +349,7 @@ export function warnDeprecatedGetter(name: string): void;
// @public
export class WeakCache<K extends object, V> {
clear(): void;
get<P extends K>(item: P, cb: (item: P) => V): NonNullable<V>;
items: WeakMap<K, V>;
}

Wyświetl plik

@ -20,4 +20,11 @@ export class WeakCache<K extends object, V> {
return this.items.get(item)!
}
/**
* Clear the cache. (Technically we create a new WeakMap, but the old one will get cleaned up by the GC eventually.)
*/
clear() {
this.items = new WeakMap()
}
}