Skip to content
Snippets Groups Projects
Commit a74c8f1d authored by David Sehnal's avatar David Sehnal
Browse files

mol-state: wip

parent d918eee7
No related branches found
No related tags found
No related merge requests found
...@@ -11,18 +11,20 @@ import { PluginBehavior } from 'mol-plugin/behavior/behavior'; ...@@ -11,18 +11,20 @@ import { PluginBehavior } from 'mol-plugin/behavior/behavior';
export type TypeClass = 'root' | 'data' | 'prop' export type TypeClass = 'root' | 'data' | 'prop'
export namespace PluginStateObject { export namespace PluginStateObject {
export type Any = StateObject<Props, any, TypeInfo>
export type TypeClass = 'Root' | 'Group' | 'Data' | 'Object' | 'Representation3D' | 'Behavior' export type TypeClass = 'Root' | 'Group' | 'Data' | 'Object' | 'Representation3D' | 'Behavior'
export interface TypeInfo { name: string, shortName: string, description: string, typeClass: TypeClass } export interface TypeInfo { name: string, shortName: string, description: string, typeClass: TypeClass }
export interface Props { label: string, description?: string } export interface Props { label: string, description?: string }
export const Create = StateObject.factory<TypeInfo, Props>(); export const Create = StateObject.factory<TypeInfo, Props>();
export function isRepresentation3D(o?: StateObject): o is StateObject<Props, Representation.Any> { export function isRepresentation3D(o?: Any): o is StateObject<Props, Representation.Any, TypeInfo> {
return !!o && (o.type.info as TypeInfo).typeClass === 'Representation3D'; return !!o && o.type.typeClass === 'Representation3D';
} }
export function isBehavior(o?: StateObject): o is StateObject<Props, PluginBehavior> { export function isBehavior(o?: Any): o is StateObject<Props, PluginBehavior, TypeInfo> {
return !!o && (o.type.info as TypeInfo).typeClass === 'Behavior'; return !!o && o.type.typeClass === 'Behavior';
} }
export function CreateRepresentation3D<T extends Representation.Any>(type: { name: string, shortName: string, description: string }) { export function CreateRepresentation3D<T extends Representation.Any>(type: { name: string, shortName: string, description: string }) {
......
...@@ -44,7 +44,7 @@ export class Controls extends React.Component<{ plugin: PluginContext }, { id: s ...@@ -44,7 +44,7 @@ export class Controls extends React.Component<{ plugin: PluginContext }, { id: s
export class _test_CreateTransform extends React.Component<{ plugin: PluginContext, nodeRef: Transform.Ref, transformer: Transformer }, { params: any }> { export class _test_CreateTransform extends React.Component<{ plugin: PluginContext, nodeRef: Transform.Ref, transformer: Transformer }, { params: any }> {
private getObj() { private getObj() {
const obj = this.props.plugin.state.data.objects.get(this.props.nodeRef)!; const obj = this.props.plugin.state.data.cells.get(this.props.nodeRef)!;
return obj; return obj;
} }
......
...@@ -42,7 +42,7 @@ export class _test_CurrentObject extends React.Component<{ plugin: PluginContext ...@@ -42,7 +42,7 @@ export class _test_CurrentObject extends React.Component<{ plugin: PluginContext
render() { render() {
const ref = this.props.plugin.behaviors.state.data.currentObject.value.ref; const ref = this.props.plugin.behaviors.state.data.currentObject.value.ref;
// const n = this.props.plugin.state.data.tree.nodes.get(ref)!; // const n = this.props.plugin.state.data.tree.nodes.get(ref)!;
const obj = this.props.plugin.state.data.objects.get(ref)!; const obj = this.props.plugin.state.data.cells.get(ref)!;
const type = obj && obj.obj ? obj.obj.type : void 0; const type = obj && obj.obj ? obj.obj.type : void 0;
......
...@@ -28,20 +28,21 @@ export class StateTree extends React.Component<{ plugin: PluginContext, state: S ...@@ -28,20 +28,21 @@ export class StateTree extends React.Component<{ plugin: PluginContext, state: S
export class StateTreeNode extends React.Component<{ plugin: PluginContext, nodeRef: string, state: State }, { }> { export class StateTreeNode extends React.Component<{ plugin: PluginContext, nodeRef: string, state: State }, { }> {
render() { render() {
const n = this.props.state.tree.nodes.get(this.props.nodeRef)!; const n = this.props.state.tree.nodes.get(this.props.nodeRef)!;
const obj = this.props.state.objects.get(this.props.nodeRef)!; const cell = this.props.state.cells.get(this.props.nodeRef)!;
const remove = <>[<a href='#' onClick={e => { const remove = <>[<a href='#' onClick={e => {
e.preventDefault(); e.preventDefault();
PluginCommands.Data.RemoveObject.dispatch(this.props.plugin, { ref: this.props.nodeRef }); PluginCommands.Data.RemoveObject.dispatch(this.props.plugin, { ref: this.props.nodeRef });
}}>X</a>]</> }}>X</a>]</>
if (obj.status !== 'ok' || !obj.obj) { if (cell.status !== 'ok' || !cell.obj) {
return <div style={{ borderLeft: '1px solid black', paddingLeft: '7px' }}> return <div style={{ borderLeft: '1px solid black', paddingLeft: '7px' }}>
{remove} {obj.status} {obj.errorText} {remove} {cell.status} {cell.errorText}
</div>; </div>;
} }
const props = obj.obj!.props as PluginStateObject.Props; const obj = cell.obj as PluginStateObject.Any;
const type = obj.obj!.type.info as PluginStateObject.TypeInfo; const props = obj.props;
const type = obj.type;
return <div style={{ borderLeft: '0px solid #999', paddingLeft: '0px' }}> return <div style={{ borderLeft: '0px solid #999', paddingLeft: '0px' }}>
{remove}[<span title={type.description}>{ type.shortName }</span>] <a href='#' onClick={e => { {remove}[<span title={type.description}>{ type.shortName }</span>] <a href='#' onClick={e => {
e.preventDefault(); e.preventDefault();
......
...@@ -7,49 +7,45 @@ ...@@ -7,49 +7,45 @@
import { UUID } from 'mol-util'; import { UUID } from 'mol-util';
import { Transform } from './transform'; import { Transform } from './transform';
export { StateObject, StateObjectBox } export { StateObject, StateObjectCell }
interface StateObject<P = any, D = any, Type = { }> { interface StateObject<P = any, D = any, T = any> {
readonly id: UUID, readonly id: UUID,
readonly type: StateObject.Type, readonly type: StateObject.Type<T>,
readonly props: P, readonly props: P,
readonly data: D readonly data: D
} }
namespace StateObject { namespace StateObject {
export interface Type<Info = any> { export function factory<Type, CommonProps>() {
info: Info return <D = { }, P = {}>(type: Type) => create<P & CommonProps, D, Type>(type);
} }
export function factory<TypeInfo, CommonProps>() { export type Type<I = unknown> = I
return <D = { }, P = {}>(typeInfo: TypeInfo) => create<P & CommonProps, D, TypeInfo>(typeInfo); export type Ctor = { new(...args: any[]): StateObject, type: any }
}
export type Ctor = { new(...args: any[]): StateObject, type: Type }
export function create<Props, Data, TypeInfo>(typeInfo: TypeInfo) { export function create<Props, Data, Type>(type: Type) {
const dataType: Type<TypeInfo> = { info: typeInfo }; return class implements StateObject<Props, Data, Type> {
return class implements StateObject<Props, Data, Type<TypeInfo>> { static type = type;
static type = dataType; static is(obj?: StateObject): obj is StateObject<Props, Data, Type> { return !!obj && type === obj.type; }
static is(obj?: StateObject): obj is StateObject<Props, Data> { return !!obj && dataType === obj.type; }
id = UUID.create(); id = UUID.create();
type = dataType; type = type;
constructor(public props: Props, public data: Data) { } constructor(public props: Props, public data: Data) { }
} }
} }
} }
interface StateObjectBox { interface StateObjectCell {
ref: Transform.Ref, ref: Transform.Ref,
props: unknown, props: unknown,
status: StateObjectBox.Status, status: StateObjectCell.Status,
errorText?: string, errorText?: string,
obj?: StateObject, obj?: StateObject,
version: string version: string
} }
namespace StateObjectBox { namespace StateObjectCell {
export type Status = 'ok' | 'error' | 'pending' | 'processing' export type Status = 'ok' | 'error' | 'pending' | 'processing'
export interface Props { export interface Props {
......
...@@ -4,14 +4,14 @@ ...@@ -4,14 +4,14 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
import { StateObject, StateObjectBox } from './object'; import { StateObject, StateObjectCell } from './object';
import { State } from './state'; import { State } from './state';
import { ImmutableTree } from './immutable-tree'; import { ImmutableTree } from './immutable-tree';
namespace StateSelection { namespace StateSelection {
export type Selector = Query | Builder | string | StateObjectBox; export type Selector = Query | Builder | string | StateObjectCell;
export type NodeSeq = StateObjectBox[] export type CellSeq = StateObjectCell[]
export type Query = (state: State) => NodeSeq; export type Query = (state: State) => CellSeq;
export function select(s: Selector, state: State) { export function select(s: Selector, state: State) {
return compile(s)(state); return compile(s)(state);
...@@ -27,8 +27,8 @@ namespace StateSelection { ...@@ -27,8 +27,8 @@ namespace StateSelection {
return query; return query;
} }
function isObj(arg: any): arg is StateObjectBox { function isObj(arg: any): arg is StateObjectCell {
return (arg as StateObjectBox).version !== void 0; return (arg as StateObjectCell).version !== void 0;
} }
function isBuilder(arg: any): arg is Builder { function isBuilder(arg: any): arg is Builder {
...@@ -40,13 +40,13 @@ namespace StateSelection { ...@@ -40,13 +40,13 @@ namespace StateSelection {
} }
export interface Builder { export interface Builder {
flatMap(f: (n: StateObjectBox) => StateObjectBox[]): Builder; flatMap(f: (n: StateObjectCell) => StateObjectCell[]): Builder;
mapEntity(f: (n: StateObjectBox) => StateObjectBox): Builder; mapEntity(f: (n: StateObjectCell) => StateObjectCell): Builder;
unique(): Builder; unique(): Builder;
parent(): Builder; parent(): Builder;
first(): Builder; first(): Builder;
filter(p: (n: StateObjectBox) => boolean): Builder; filter(p: (n: StateObjectCell) => boolean): Builder;
subtree(): Builder; subtree(): Builder;
children(): Builder; children(): Builder;
ofType(t: StateObject.Type): Builder; ofType(t: StateObject.Type): Builder;
...@@ -63,14 +63,14 @@ namespace StateSelection { ...@@ -63,14 +63,14 @@ namespace StateSelection {
return Object.create(BuilderPrototype, { compile: { writable: false, configurable: false, value: compile } }); return Object.create(BuilderPrototype, { compile: { writable: false, configurable: false, value: compile } });
} }
export function root() { return build(() => (state: State) => [state.objects.get(state.tree.rootRef)!]) } export function root() { return build(() => (state: State) => [state.cells.get(state.tree.rootRef)!]) }
export function byRef(...refs: string[]) { export function byRef(...refs: string[]) {
return build(() => (state: State) => { return build(() => (state: State) => {
const ret: StateObjectBox[] = []; const ret: StateObjectCell[] = [];
for (const ref of refs) { for (const ref of refs) {
const n = state.objects.get(ref); const n = state.cells.get(ref);
if (!n) continue; if (!n) continue;
ret.push(n); ret.push(n);
} }
...@@ -78,13 +78,13 @@ namespace StateSelection { ...@@ -78,13 +78,13 @@ namespace StateSelection {
}); });
} }
export function byValue(...objects: StateObjectBox[]) { return build(() => (state: State) => objects); } export function byValue(...objects: StateObjectCell[]) { return build(() => (state: State) => objects); }
registerModifier('flatMap', flatMap); registerModifier('flatMap', flatMap);
export function flatMap(b: Selector, f: (obj: StateObjectBox, state: State) => NodeSeq) { export function flatMap(b: Selector, f: (obj: StateObjectCell, state: State) => CellSeq) {
const q = compile(b); const q = compile(b);
return build(() => (state: State) => { return build(() => (state: State) => {
const ret: StateObjectBox[] = []; const ret: StateObjectCell[] = [];
for (const n of q(state)) { for (const n of q(state)) {
for (const m of f(n, state)) { for (const m of f(n, state)) {
ret.push(m); ret.push(m);
...@@ -95,10 +95,10 @@ namespace StateSelection { ...@@ -95,10 +95,10 @@ namespace StateSelection {
} }
registerModifier('mapEntity', mapEntity); registerModifier('mapEntity', mapEntity);
export function mapEntity(b: Selector, f: (n: StateObjectBox, state: State) => StateObjectBox | undefined) { export function mapEntity(b: Selector, f: (n: StateObjectCell, state: State) => StateObjectCell | undefined) {
const q = compile(b); const q = compile(b);
return build(() => (state: State) => { return build(() => (state: State) => {
const ret: StateObjectBox[] = []; const ret: StateObjectCell[] = [];
for (const n of q(state)) { for (const n of q(state)) {
const x = f(n, state); const x = f(n, state);
if (x) ret.push(x); if (x) ret.push(x);
...@@ -112,7 +112,7 @@ namespace StateSelection { ...@@ -112,7 +112,7 @@ namespace StateSelection {
const q = compile(b); const q = compile(b);
return build(() => (state: State) => { return build(() => (state: State) => {
const set = new Set<string>(); const set = new Set<string>();
const ret: StateObjectBox[] = []; const ret: StateObjectCell[] = [];
for (const n of q(state)) { for (const n of q(state)) {
if (!set.has(n.ref)) { if (!set.has(n.ref)) {
set.add(n.ref); set.add(n.ref);
...@@ -133,22 +133,22 @@ namespace StateSelection { ...@@ -133,22 +133,22 @@ namespace StateSelection {
} }
registerModifier('filter', filter); registerModifier('filter', filter);
export function filter(b: Selector, p: (n: StateObjectBox) => boolean) { return flatMap(b, n => p(n) ? [n] : []); } export function filter(b: Selector, p: (n: StateObjectCell) => boolean) { return flatMap(b, n => p(n) ? [n] : []); }
registerModifier('subtree', subtree); registerModifier('subtree', subtree);
export function subtree(b: Selector) { export function subtree(b: Selector) {
return flatMap(b, (n, s) => { return flatMap(b, (n, s) => {
const nodes = [] as string[]; const nodes = [] as string[];
ImmutableTree.doPreOrder(s.tree, s.tree.nodes.get(n.ref), nodes, (x, _, ctx) => { ctx.push(x.ref) }); ImmutableTree.doPreOrder(s.tree, s.tree.nodes.get(n.ref), nodes, (x, _, ctx) => { ctx.push(x.ref) });
return nodes.map(x => s.objects.get(x)!); return nodes.map(x => s.cells.get(x)!);
}); });
} }
registerModifier('children', children); registerModifier('children', children);
export function children(b: Selector) { export function children(b: Selector) {
return flatMap(b, (n, s) => { return flatMap(b, (n, s) => {
const nodes: StateObjectBox[] = []; const nodes: StateObjectCell[] = [];
s.tree.nodes.get(n.ref)!.children.forEach(c => nodes.push(s.objects.get(c!)!)); s.tree.nodes.get(n.ref)!.children.forEach(c => nodes.push(s.cells.get(c!)!));
return nodes; return nodes;
}); });
} }
...@@ -160,17 +160,17 @@ namespace StateSelection { ...@@ -160,17 +160,17 @@ namespace StateSelection {
export function ancestorOfType(b: Selector, t: StateObject.Type) { return unique(mapEntity(b, (n, s) => findAncestorOfType(s, n.ref, t))); } export function ancestorOfType(b: Selector, t: StateObject.Type) { return unique(mapEntity(b, (n, s) => findAncestorOfType(s, n.ref, t))); }
registerModifier('parent', parent); registerModifier('parent', parent);
export function parent(b: Selector) { return unique(mapEntity(b, (n, s) => s.objects.get(s.tree.nodes.get(n.ref)!.parent))); } export function parent(b: Selector) { return unique(mapEntity(b, (n, s) => s.cells.get(s.tree.nodes.get(n.ref)!.parent))); }
function findAncestorOfType({ tree, objects }: State, root: string, type: StateObject.Type): StateObjectBox | undefined { function findAncestorOfType({ tree, cells }: State, root: string, type: StateObject.Type): StateObjectCell | undefined {
let current = tree.nodes.get(root)!; let current = tree.nodes.get(root)!;
while (true) { while (true) {
current = tree.nodes.get(current.parent)!; current = tree.nodes.get(current.parent)!;
if (current.ref === tree.rootRef) { if (current.ref === tree.rootRef) {
return objects.get(tree.rootRef); return cells.get(tree.rootRef);
} }
const obj = objects.get(current.ref)!.obj!; const obj = cells.get(current.ref)!.obj!;
if (obj.type === type) return objects.get(current.ref); if (obj.type === type) return cells.get(current.ref);
} }
} }
} }
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
import { StateObject, StateObjectBox } from './object'; import { StateObject, StateObjectCell } from './object';
import { StateTree } from './tree'; import { StateTree } from './tree';
import { Transform } from './transform'; import { Transform } from './transform';
import { ImmutableTree } from './immutable-tree'; import { ImmutableTree } from './immutable-tree';
...@@ -23,16 +23,16 @@ class State { ...@@ -23,16 +23,16 @@ class State {
get tree() { return this._tree; } get tree() { return this._tree; }
get current() { return this._current; } get current() { return this._current; }
readonly objects: State.Objects = new Map(); readonly cells: State.Cells = new Map();
readonly context: StateContext; readonly context: StateContext;
getSnapshot(): State.Snapshot { getSnapshot(): State.Snapshot {
const props = Object.create(null); const props = Object.create(null);
const keys = this.objects.keys(); const keys = this.cells.keys();
while (true) { while (true) {
const key = keys.next(); const key = keys.next();
if (key.done) break; if (key.done) break;
const o = this.objects.get(key.value)!; const o = this.cells.get(key.value)!;
props[key.value] = { ...o.props }; props[key.value] = { ...o.props };
} }
return { return {
...@@ -68,7 +68,7 @@ class State { ...@@ -68,7 +68,7 @@ class State {
taskCtx, taskCtx,
oldTree, oldTree,
tree: tree, tree: tree,
objects: this.objects, objects: this.cells,
transformCache: this.transformCache transformCache: this.transformCache
}; };
// TODO: have "cancelled" error? Or would this be handled automatically? // TODO: have "cancelled" error? Or would this be handled automatically?
...@@ -84,7 +84,7 @@ class State { ...@@ -84,7 +84,7 @@ class State {
const root = tree.getValue(tree.rootRef)!; const root = tree.getValue(tree.rootRef)!;
const defaultObjectProps = (params && params.defaultObjectProps) || { } const defaultObjectProps = (params && params.defaultObjectProps) || { }
this.objects.set(tree.rootRef, { this.cells.set(tree.rootRef, {
ref: tree.rootRef, ref: tree.rootRef,
obj: rootObject, obj: rootObject,
status: 'ok', status: 'ok',
...@@ -101,7 +101,7 @@ class State { ...@@ -101,7 +101,7 @@ class State {
} }
namespace State { namespace State {
export type Objects = Map<Transform.Ref, StateObjectBox> export type Cells = Map<Transform.Ref, StateObjectCell>
export interface Snapshot { export interface Snapshot {
readonly tree: StateTree.Serialized, readonly tree: StateTree.Serialized,
...@@ -120,7 +120,7 @@ namespace State { ...@@ -120,7 +120,7 @@ namespace State {
taskCtx: RuntimeContext, taskCtx: RuntimeContext,
oldTree: StateTree, oldTree: StateTree,
tree: StateTree, tree: StateTree,
objects: State.Objects, objects: State.Cells,
transformCache: Map<Ref, unknown> transformCache: Map<Ref, unknown>
} }
...@@ -142,7 +142,7 @@ namespace State { ...@@ -142,7 +142,7 @@ namespace State {
} }
} }
function findUpdateRoots(objects: State.Objects, tree: StateTree) { function findUpdateRoots(objects: State.Cells, tree: StateTree) {
const findState = { const findState = {
roots: [] as Ref[], roots: [] as Ref[],
objects objects
...@@ -177,7 +177,7 @@ namespace State { ...@@ -177,7 +177,7 @@ namespace State {
return deletes; return deletes;
} }
function setObjectState(ctx: UpdateContext, ref: Ref, status: StateObjectBox.Status, errorText?: string) { function setObjectState(ctx: UpdateContext, ref: Ref, status: StateObjectCell.Status, errorText?: string) {
let changed = false; let changed = false;
if (ctx.objects.has(ref)) { if (ctx.objects.has(ref)) {
const obj = ctx.objects.get(ref)!; const obj = ctx.objects.get(ref)!;
...@@ -185,7 +185,7 @@ namespace State { ...@@ -185,7 +185,7 @@ namespace State {
obj.status = status; obj.status = status;
obj.errorText = errorText; obj.errorText = errorText;
} else { } else {
const obj: StateObjectBox = { ref, status, version: UUID.create(), errorText, props: { ...ctx.stateCtx.defaultObjectProps } }; const obj: StateObjectCell = { ref, status, version: UUID.create(), errorText, props: { ...ctx.stateCtx.defaultObjectProps } };
ctx.objects.set(ref, obj); ctx.objects.set(ref, obj);
changed = true; changed = true;
} }
...@@ -219,7 +219,7 @@ namespace State { ...@@ -219,7 +219,7 @@ namespace State {
} }
} }
function findAncestor(tree: StateTree, objects: State.Objects, root: Ref, types: { type: StateObject.Type }[]): StateObject { function findAncestor(tree: StateTree, objects: State.Cells, root: Ref, types: { type: StateObject.Type }[]): StateObject {
let current = tree.nodes.get(root)!; let current = tree.nodes.get(root)!;
while (true) { while (true) {
current = tree.nodes.get(current.parent)!; current = tree.nodes.get(current.parent)!;
......
...@@ -68,7 +68,7 @@ function hookEvents(state: State) { ...@@ -68,7 +68,7 @@ function hookEvents(state: State) {
state.context.events.object.created.subscribe(e => console.log('created:', e.ref)); state.context.events.object.created.subscribe(e => console.log('created:', e.ref));
state.context.events.object.removed.subscribe(e => console.log('removed:', e.ref)); state.context.events.object.removed.subscribe(e => console.log('removed:', e.ref));
state.context.events.object.replaced.subscribe(e => console.log('replaced:', e.ref)); state.context.events.object.replaced.subscribe(e => console.log('replaced:', e.ref));
state.context.events.object.stateChanged.subscribe(e => console.log('stateChanged:', e.ref, state.objects.get(e.ref)!.status)); state.context.events.object.stateChanged.subscribe(e => console.log('stateChanged:', e.ref, state.cells.get(e.ref)!.status));
state.context.events.object.updated.subscribe(e => console.log('updated:', e.ref)); state.context.events.object.updated.subscribe(e => console.log('updated:', e.ref));
} }
...@@ -91,7 +91,7 @@ export async function testState() { ...@@ -91,7 +91,7 @@ export async function testState() {
await state.update(tree1).run(); await state.update(tree1).run();
console.log('----------------'); console.log('----------------');
console.log(util.inspect(state.objects, true, 3, true)); console.log(util.inspect(state.cells, true, 3, true));
console.log('----------------'); console.log('----------------');
const jsonString = JSON.stringify(StateTree.toJSON(tree2), null, 2); const jsonString = JSON.stringify(StateTree.toJSON(tree2), null, 2);
...@@ -103,7 +103,7 @@ export async function testState() { ...@@ -103,7 +103,7 @@ export async function testState() {
console.log('----------------'); console.log('----------------');
await state.update(treeFromJson).run(); await state.update(treeFromJson).run();
console.log(util.inspect(state.objects, true, 3, true)); console.log(util.inspect(state.cells, true, 3, true));
console.log('----------------'); console.log('----------------');
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment