Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement {{#-in-element API. #331

Merged
merged 2 commits into from
Sep 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/glimmer-runtime/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ export {
default as WithDynamicVarsSyntax
} from './lib/syntax/builtins/with-dynamic-vars';

export {
default as InElementSyntax
} from './lib/syntax/builtins/in-element';

export { PublicVM as VM, UpdatingVM, RenderResult } from './lib/vm';

export { SafeString, isSafeString } from './lib/upsert';
Expand Down
54 changes: 38 additions & 16 deletions packages/glimmer-runtime/lib/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,15 @@ export interface LastNode {
}

class First {
private node: Node;

constructor(node) {
this.node = node;
}
constructor(private node: Node) { }

firstNode(): Node {
return this.node;
}
}

class Last {
private node: Node;

constructor(node) {
this.node = node;
}
constructor(private node: Node) { }

lastNode(): Node {
return this.node;
Expand Down Expand Up @@ -127,7 +119,7 @@ export class ElementStack implements Cursor {
return this.blockStack.current;
}

private popElement() {
popElement() {
let { elementStack, nextSiblingStack } = this;

let topElement = elementStack.pop();
Expand All @@ -151,12 +143,15 @@ export class ElementStack implements Cursor {
return tracker;
}

private pushBlockTracker(tracker: Tracker) {
private pushBlockTracker(tracker: Tracker, isRemote = false) {
let current = this.blockStack.current;

if (current !== null) {
current.newDestroyable(tracker);
current.newBounds(tracker);

if (!isRemote) {
current.newBounds(tracker);
}
}

this.blockStack.push(tracker);
Expand Down Expand Up @@ -193,16 +188,35 @@ export class ElementStack implements Cursor {

flushElement() {
let parent = this.element;
let element = this.element = this.constructing;
let element = this.constructing;

this.dom.insertBefore(parent, element, this.nextSibling);

this.constructing = null;
this.operations = null;
this.nextSibling = null;

this.pushElement(element);
this.blockStack.current.openElement(element);
}

pushRemoteElement(element: Simple.Element) {
this.pushElement(element);

let tracker = new RemoteBlockTracker(element);
this.pushBlockTracker(tracker, true);
}

popRemoteElement() {
this.popBlock();
this.popElement();
}

private pushElement(element: Simple.Element) {
this.element = element;
this.elementStack.push(element);

this.nextSibling = null;
this.nextSiblingStack.push(null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can make this configurable for bonus points, which would allow inserting into the middle of an element 😉

but we can do it as a follow-up.

this.blockStack.current.openElement(element);
}

newDestroyable(d: Destroyable) {
Expand Down Expand Up @@ -331,6 +345,14 @@ export class SimpleBlockTracker implements Tracker {
}
}

class RemoteBlockTracker extends SimpleBlockTracker {
destroy() {
super.destroy();

clear(this);
}
}

export interface UpdatableTracker extends Tracker {
reset(env: Environment);
}
Expand Down
12 changes: 12 additions & 0 deletions packages/glimmer-runtime/lib/compiled/opcodes/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,18 @@ export abstract class BasicOpcodeBuilder extends StatementCompilationBufferProxy

// vm

pushRemoteElement() {
this.append(new dom.PushRemoteElementOpcode());
}

popRemoteElement() {
this.append(new dom.PopRemoteElementOpcode());
}

popElement() {
this.append(new dom.PopElementOpcode());
}

label(name: string) {
this.append(this.labelFor(name));
}
Expand Down
47 changes: 47 additions & 0 deletions packages/glimmer-runtime/lib/compiled/opcodes/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { ValueReference } from '../../compiled/expressions/value';
import { CompiledArgs, EvaluatedArgs } from '../../compiled/expressions/args';
import { AttributeManager } from '../../dom/attribute-managers';
import { ElementOperations } from '../../builder';
import { Assert } from './vm';

export class TextOpcode extends Opcode {
public type = "text";
Expand Down Expand Up @@ -62,6 +63,38 @@ export class OpenPrimitiveElementOpcode extends Opcode {
}
}

export class PushRemoteElementOpcode extends Opcode {
public type = "push-remote-element";

evaluate(vm: VM) {
let reference = vm.frame.getOperand<Simple.Element>();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future readers: this is unsafe but we decided to punt on error-ing since this is still experimental

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should probably remove those comments tho :P

let cache = isConstReference(reference) ? undefined : new ReferenceCache(reference);
let element = cache ? cache.peek() : reference.value();

vm.stack().pushRemoteElement(element);

if (cache) {
vm.updateWith(new Assert(cache));
}
}

toJSON(): OpcodeJSON {
return {
guid: this._guid,
type: this.type,
args: ['$OPERAND']
};
}
}

export class PopRemoteElementOpcode extends Opcode {
public type = "pop-remote-element";

evaluate(vm: VM) {
vm.stack().popRemoteElement();
}
}

export class OpenComponentElementOpcode extends Opcode {
public type = "open-component-element";

Expand Down Expand Up @@ -345,6 +378,20 @@ export class CloseElementOpcode extends Opcode {
}
}

export class PopElementOpcode extends Opcode {
public type = "pop-element";

evaluate(vm: VM) {
vm.stack().popElement();
}
}

export interface StaticAttrOptions {
namespace: string;
name: string;
value: string;
}

export class StaticAttrOpcode extends Opcode {
public type = "static-attr";

Expand Down
34 changes: 34 additions & 0 deletions packages/glimmer-runtime/lib/syntax/builtins/in-element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {
Statement as StatementSyntax
} from '../../syntax';

import OpcodeBuilderDSL from '../../compiled/opcodes/builder';
import * as Syntax from '../core';
import Environment from '../../environment';

export default class InElementSyntax extends StatementSyntax {
type = "in-element-statement";

public args: Syntax.Args;
public templates: Syntax.Templates;
public isStatic = false;

constructor({ args, templates }: { args: Syntax.Args, templates: Syntax.Templates }) {
super();
this.args = args;
this.templates = templates;
}

compile(dsl: OpcodeBuilderDSL, env: Environment) {
let { args, templates } = this;

dsl.block({ templates, args }, (dsl, BEGIN, END) => {
dsl.putArgs(args);
dsl.test('simple');
dsl.jumpUnless(END);
dsl.pushRemoteElement();
dsl.evaluate('default');
dsl.popRemoteElement();
});
}
}
16 changes: 8 additions & 8 deletions packages/glimmer-runtime/tests/ember-component-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { equalTokens, stripTight } from "glimmer-test-helpers";

import { CLASS_META, UpdatableReference, setProperty as set } from 'glimmer-object-reference';

class EmberishRootView extends EmberObject {
export class EmberishRootView extends EmberObject {
private parent: Element;
protected _result: RenderResult;
protected template: Template<{}>;
Expand Down Expand Up @@ -73,7 +73,7 @@ function module(name: string) {

module("Components - generic - props");

function appendViewFor(template: string, context: Object = {}) {
export function appendViewFor(template: string, context: Object = {}) {
class MyRootView extends EmberishRootView {
protected env = env;
protected template = env.compile(template);
Expand All @@ -90,7 +90,7 @@ function appendViewFor(template: string, context: Object = {}) {
return view;
}

function assertAppended(content: string) {
export function assertAppended(content: string) {
equalTokens((<HTMLElement>document.querySelector('#qunit-fixture')), content);
}

Expand Down Expand Up @@ -147,12 +147,12 @@ function assertEmberishElement(...args) {
equalsElement(view.element, tagName, fullAttrs, contents);
}

function assertElementIsEmberishElement(element: Element, tagName: string, attrs: Object, contents: string);
function assertElementIsEmberishElement(element: Element, tagName: string, attrs: Object);
function assertElementIsEmberishElement(element: Element, tagName: string, contents: string);
function assertElementIsEmberishElement(element: Element, tagName: string);
export function assertElementIsEmberishElement(element: Element, tagName: string, attrs: Object, contents: string);
export function assertElementIsEmberishElement(element: Element, tagName: string, attrs: Object);
export function assertElementIsEmberishElement(element: Element, tagName: string, contents: string);
export function assertElementIsEmberishElement(element: Element, tagName: string);

function assertElementIsEmberishElement(element: Element, ...args) {
export function assertElementIsEmberishElement(element: Element, ...args) {
let tagName, attrs, contents;
if (args.length === 2) {
if (typeof args[1] === 'string') [tagName, attrs, contents] = [args[0], {}, args[1]];
Expand Down
Loading