diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/new_fields/Doc.ts | 5 | ||||
-rw-r--r-- | src/new_fields/ScriptField.ts | 27 | ||||
-rw-r--r-- | src/new_fields/util.ts | 28 |
3 files changed, 54 insertions, 6 deletions
diff --git a/src/new_fields/Doc.ts b/src/new_fields/Doc.ts index 769c6aa73..59e61023f 100644 --- a/src/new_fields/Doc.ts +++ b/src/new_fields/Doc.ts @@ -10,14 +10,15 @@ import { RefField, FieldId } from "./RefField"; import { ToScriptString, SelfProxy, Parent, OnUpdate, Self, HandleUpdate, Update, Id } from "./FieldSymbols"; import { scriptingGlobal } from "../client/util/Scripting"; import { List } from "./List"; +import { ComputedField } from "./ScriptField"; export namespace Field { export function toKeyValueString(doc: Doc, key: string): string { const onDelegate = Object.keys(doc).includes(key); - let field = FieldValue(doc[key]); + let field = ComputedField.WithoutComputed(() => FieldValue(doc[key])); if (Field.IsField(field)) { - return (onDelegate ? "=" : "") + Field.toScriptString(field); + return (onDelegate ? "=" : "") + (field instanceof ComputedField ? `:=${field.script.originalScript}` : Field.toScriptString(field)); } return ""; } diff --git a/src/new_fields/ScriptField.ts b/src/new_fields/ScriptField.ts index e2994ed70..b5b1595cf 100644 --- a/src/new_fields/ScriptField.ts +++ b/src/new_fields/ScriptField.ts @@ -4,6 +4,7 @@ import { Copy, ToScriptString, Parent, SelfProxy } from "./FieldSymbols"; import { serializable, createSimpleSchema, map, primitive, object, deserialize, PropSchema, custom, SKIP } from "serializr"; import { Deserializable } from "../client/util/SerializationHelper"; import { Doc } from "../new_fields/Doc"; +import { Plugins } from "./util"; function optional(propSchema: PropSchema) { return custom(value => { @@ -93,4 +94,30 @@ export class ComputedField extends ScriptField { } return undefined; } +} + +export namespace ComputedField { + let useComputed = true; + export function DisableComputedFields() { + useComputed = false; + } + + export function EnableComputedFields() { + useComputed = true; + } + + export function WithoutComputed<T>(fn: () => T) { + DisableComputedFields(); + try { + return fn(); + } finally { + EnableComputedFields(); + } + } + + Plugins.addGetterPlugin((doc, _, value) => { + if (useComputed && value instanceof ComputedField) { + return { value: value.value(doc), shouldReturn: true }; + } + }); }
\ No newline at end of file diff --git a/src/new_fields/util.ts b/src/new_fields/util.ts index 47e467041..b59ec9b9a 100644 --- a/src/new_fields/util.ts +++ b/src/new_fields/util.ts @@ -1,5 +1,5 @@ import { UndoManager } from "../client/util/UndoManager"; -import { Doc, Field } from "./Doc"; +import { Doc, Field, FieldResult } from "./Doc"; import { SerializationHelper } from "../client/util/SerializationHelper"; import { ProxyField } from "./Proxy"; import { RefField } from "./RefField"; @@ -11,6 +11,20 @@ import { ComputedField } from "./ScriptField"; function _readOnlySetter(): never { throw new Error("Documents can't be modified in read-only mode"); } + +export interface GetterResult { + value: FieldResult; + shouldReturn: boolean; +} +export type GetterPlugin = (receiver: any, prop: string | number, currentValue: any) => GetterResult | undefined; +const getterPlugins: GetterPlugin[] = []; + +export namespace Plugins { + export function addGetterPlugin(plugin: GetterPlugin) { + getterPlugins.push(plugin); + } +} + const _setterImpl = action(function (target: any, prop: string | symbol | number, value: any, receiver: any): boolean { //console.log("-set " + target[SelfProxy].title + "(" + target[SelfProxy][prop] + ")." + prop.toString() + " = " + value); if (SerializationHelper.IsSerializing()) { @@ -85,12 +99,18 @@ export function getter(target: any, prop: string | symbol | number, receiver: an function getFieldImpl(target: any, prop: string | number, receiver: any, ignoreProto: boolean = false): any { receiver = receiver || target[SelfProxy]; - const field = target.__fields[prop]; + let field = target.__fields[prop]; if (field instanceof ProxyField) { return field.value(); } - if (field instanceof ComputedField) { - return field.value(receiver); + for (const plugin of getterPlugins) { + const res = plugin(receiver, prop, field); + if (res === undefined) continue; + if (res.shouldReturn) { + return res.value; + } else { + field = res.value; + } } if (field === undefined && !ignoreProto && prop !== "proto") { const proto = getFieldImpl(target, "proto", receiver, true);//TODO tfs: instead of receiver we could use target[SelfProxy]... I don't which semantics we want or if it really matters |