Memoize list entries and wrap model data

This commit is contained in:
Misode
2021-09-24 03:47:15 +02:00
parent b12e81fff0
commit 6e4850d5a9
7 changed files with 31 additions and 28 deletions

14
package-lock.json generated
View File

@@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"@mcschema/core": "^0.12.7",
"@mcschema/core": "^0.12.8",
"@mcschema/java-1.15": "^0.2.0",
"@mcschema/java-1.16": "^0.6.3",
"@mcschema/java-1.17": "^0.2.23",
@@ -315,9 +315,9 @@
}
},
"node_modules/@mcschema/core": {
"version": "0.12.7",
"resolved": "https://registry.npmjs.org/@mcschema/core/-/core-0.12.7.tgz",
"integrity": "sha512-5YUeH4ywsGgIGI6fRB7jCmclMxrep1z9A1YwyT9VUYGK8CR8oCZzZ6/5Oz1IoRBS1FTLzmzbluxaN1tkxyffXQ=="
"version": "0.12.8",
"resolved": "https://registry.npmjs.org/@mcschema/core/-/core-0.12.8.tgz",
"integrity": "sha512-O7NdYlxBFgK+iH9IE4/A8JX9ESPlxZsGke66NBQ/lFIdMYMOKpTeJBk6SKgEHSneqinf9w3IXGaE3aqDeJMo6A=="
},
"node_modules/@mcschema/java-1.15": {
"version": "0.2.1",
@@ -2790,9 +2790,9 @@
}
},
"@mcschema/core": {
"version": "0.12.7",
"resolved": "https://registry.npmjs.org/@mcschema/core/-/core-0.12.7.tgz",
"integrity": "sha512-5YUeH4ywsGgIGI6fRB7jCmclMxrep1z9A1YwyT9VUYGK8CR8oCZzZ6/5Oz1IoRBS1FTLzmzbluxaN1tkxyffXQ=="
"version": "0.12.8",
"resolved": "https://registry.npmjs.org/@mcschema/core/-/core-0.12.8.tgz",
"integrity": "sha512-O7NdYlxBFgK+iH9IE4/A8JX9ESPlxZsGke66NBQ/lFIdMYMOKpTeJBk6SKgEHSneqinf9w3IXGaE3aqDeJMo6A=="
},
"@mcschema/java-1.15": {
"version": "0.2.1",

View File

@@ -14,7 +14,7 @@
"author": "Misode",
"license": "MIT",
"dependencies": {
"@mcschema/core": "^0.12.7",
"@mcschema/core": "^0.12.8",
"@mcschema/java-1.15": "^0.2.0",
"@mcschema/java-1.16": "^0.6.3",
"@mcschema/java-1.17": "^0.2.23",

View File

@@ -75,13 +75,14 @@ export async function getModel(version: VersionId, id: string): Promise<DataMode
}
try {
const schema = versionData.schemas.get(schemaName)
const model = new DataModel(schema)
const model = new DataModel(schema, { wrapLists: true, verbose: true })
if (Models[id]) {
model.reset(Models[id].model.data, false)
} else {
model.validate(true)
model.history = [JSON.stringify(model.data)]
}
console.log(model.data)
Models[id] = { model, version }
} catch (e) {
throw new Error(`Cannot get generator "${id}" for version "${version}": ${message(e)}`)

View File

@@ -1,5 +1,4 @@
import type { DataModel } from '@mcschema/core'
import { ModelPath } from '@mcschema/core'
import { DataModel, ModelPath } from '@mcschema/core'
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
import { Btn, BtnMenu } from '.'
import { useModel } from '../hooks'
@@ -72,7 +71,7 @@ export function SourcePanel({ lang, name, model, blockStates, doCopy, doDownload
const onImport = () => {
try {
const data = JSON.parse(source.current.value)
model?.reset(data, false)
model?.reset(DataModel.wrapLists(data), false)
} catch (e) {
onError(`Error importing: ${message(e)}`)
console.error(e)

View File

@@ -1,5 +1,4 @@
import type { DataModel } from '@mcschema/core'
import { Path } from '@mcschema/core'
import { DataModel, Path } from '@mcschema/core'
import { getCurrentUrl } from 'preact-router'
import { useEffect, useErrorBoundary, useRef, useState } from 'preact/hooks'
import config from '../../config.json'
@@ -63,7 +62,7 @@ export function Generator({ lang, changeTitle, version, onChangeVersion }: Gener
const reset = () => {
Analytics.generatorEvent('reset')
model?.reset(model.schema.default(), true)
model?.reset(DataModel.wrapLists(model.schema.default()), true)
}
const undo = (e: MouseEvent) => {
e.stopPropagation()
@@ -118,7 +117,7 @@ export function Generator({ lang, changeTitle, version, onChangeVersion }: Gener
preset.generator.biome_source.seed = seed
}
}
model?.reset(preset, false)
model?.reset(DataModel.wrapLists(preset), false)
})
}

View File

@@ -61,7 +61,7 @@ export const renderHtml: RenderHook = {
const choiceContextPath = config?.choiceContext ? new Path([], [config.choiceContext]) : config?.context ? new Path([], [config.context]) : path
const set = (value: string) => {
const c = choices.find(c => c.type === value) ?? choice
path.model.set(path, c.change ? c.change(value) : c.node.default())
path.model.set(path, c.change ? c.change(value, { wrapLists: true }) : DataModel.wrapLists(c.node.default()))
}
const inject = <select value={choice.type} onChange={(e) => set((e.target as HTMLSelectElement).value)}>
{choices.map(c => <option value={c.type}>
@@ -80,7 +80,7 @@ export const renderHtml: RenderHook = {
<div class="fixed-list"></div>
</>
const suffix = <>{[...Array(config.maxLength)].map((_, i) => {
const child = children.hook(this, path.modelPush(i), value?.[i], lang, states)
const child = children.hook(this, path.modelPush(i), value?.[i]?.node, lang, states)
return child[1]
})}</>
return [prefix, suffix, null]
@@ -88,15 +88,17 @@ export const renderHtml: RenderHook = {
const onAdd = () => {
if (!Array.isArray(value)) value = []
path.model.set(path, [children.default(), ...value])
const node = DataModel.wrapLists(children.default())
path.model.set(path, [{ node, id: hexId() }, ...value])
}
const onAddBottom = () => {
if (!Array.isArray(value)) value = []
path.model.set(path, [...value, children.default()])
const node = DataModel.wrapLists(children.default())
path.model.set(path, [...value, { node, id: hexId() }])
}
const suffix = <button class="add" onClick={onAdd}>{Octicon.plus_circle}</button>
const body = <>
{(value && Array.isArray(value)) && value.map((cValue, index) => {
{(value && Array.isArray(value)) && value.map(({ node: cValue, id: cId }, index) => {
if (value.length > LIST_LIMIT && index >= LIST_LIMIT_SHOWN && index < value.length - LIST_LIMIT_SHOWN) {
if (index === LIST_LIMIT_SHOWN) {
return <span class="node-message">{value.length - LIST_LIMIT} hidden entries...</span>
@@ -106,22 +108,22 @@ export const renderHtml: RenderHook = {
const cPath = path.push(index).contextPush('entry')
const onRemove = () => cPath.set(undefined)
const onMoveUp = () => {
const v = [...value];
const v = [...path.get()];
[v[index - 1], v[index]] = [v[index], v[index - 1]]
path.model.set(path, v)
}
const onMoveDown = () => {
const v = [...value];
const v = [...path.get()];
[v[index + 1], v[index]] = [v[index], v[index + 1]]
path.model.set(path, v)
}
return <TreeNode key={index} path={cPath} schema={children} value={cValue} lang={lang} states={states}>
return <MemoedTreeNode key={cId} path={cPath} schema={children} value={cValue} lang={lang} states={states} context={(index === 0 ? 1 : 0) + (index === value.length - 1 ? 2 : 0)}>
<button class="remove" onClick={onRemove}>{Octicon.trashcan}</button>
{value.length > 1 && <div class="node-move">
<button class="move" onClick={onMoveUp} disabled={index === 0}>{Octicon.chevron_up}</button>
<button class="move" onClick={onMoveDown} disabled={index === value.length - 1}>{Octicon.chevron_down}</button>
</div>}
</TreeNode>
</MemoedTreeNode>
})}
{(value && value.length > 2) && <div class="node node-header">
<button class="add" onClick={onAddBottom}>{Octicon.plus_circle}</button>
@@ -134,7 +136,7 @@ export const renderHtml: RenderHook = {
const keyPath = new ModelPath(keysModel, new Path([hashString(path.toString())]))
const onAdd = () => {
const key = keyPath.get()
path.model.set(path.push(key), children.default())
path.model.set(path.push(key), DataModel.wrapLists(children.default()))
}
const blockState = config.validation?.validator === 'block_state_map' ? states?.[relativePath(path, config.validation.params.id).get()] : null
const keysSchema = blockState?.properties
@@ -185,7 +187,7 @@ export const renderHtml: RenderHook = {
let suffix: JSX.Element | null = null
if (node.optional()) {
if (value === undefined) {
const onExpand = () => path.set(node.default())
const onExpand = () => path.set(DataModel.wrapLists(node.default()))
suffix = <button class="collapse closed" onClick={onExpand}>{Octicon.plus_circle}</button>
} else {
const onCollapse = () => path.set(undefined)
@@ -306,6 +308,7 @@ type TreeNodeProps = {
compare?: any,
label?: string,
children?: ComponentChildren,
context?: number,
}
function TreeNode({ label, schema, path, value, lang, states, children }: TreeNodeProps) {
const type = schema.type(path)
@@ -346,6 +349,7 @@ const MemoedTreeNode = memo(TreeNode, (prev, next) => {
&& prev.path.equals(next.path)
&& prev.schema === next.schema
&& prev.lang === next.lang
&& prev.context === next.context
})
function isEnum(value?: ValidationOption | EnumOption): value is EnumOption {

View File

@@ -17,7 +17,7 @@ export const transformOutput: Hook<[any, OutputProps], any> = {
list({ children }, path, value, props) {
if (!Array.isArray(value)) return value
return value.map((obj, index) =>
children.hook(this, path.push(index), obj, props)
children.hook(this, path.push(index), obj.node, props)
)
},