Skip to content

Commit 76ab409

Browse files
committed
wip: Work on rewriting the walker in dom-mode
1 parent 198aca6 commit 76ab409

2 files changed

Lines changed: 171 additions & 79 deletions

File tree

packages/debugger/src/main/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ export type Rect = {
143143
height: number
144144
}
145145

146+
export type ElementChildren<T extends object> = Iterable<T> & ArrayLike<T>
147+
146148
/**
147149
* When using a custom solid renderer, you should provide a custom element interface.
148150
* By default the debugger assumes that rendered elements are DOM elements.
@@ -151,7 +153,7 @@ export type ElementInterface<T extends object> = {
151153
isElement: (obj: object | T) => obj is T,
152154
getElementAt: (e: MouseEvent) => T | null,
153155
getName: (el: T) => string | null,
154-
getChildren: (el: T) => Iterable<T>,
156+
getChildren: (el: T) => ElementChildren<T>,
155157
getParent: (el: T) => T | null,
156158
getLocation: (el: T) => SourceLocation | null,
157159
getRect: (el: T) => Rect | null,

packages/debugger/src/structure/walker.ts

Lines changed: 168 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {untrackedCallback} from '@solid-devtools/shared/primitives'
22
import {ObjectType, getSdtId} from '../main/id.ts'
33
import {observeComputationUpdate} from '../main/observe.ts'
44
import {
5+
type ElementChildren,
56
type ElementInterface,
67
type Mapped,
78
type NodeID,
@@ -214,8 +215,6 @@ export type TreeWalkerConfig<TEl extends object> = {
214215
eli: ElementInterface<TEl>
215216
}
216217

217-
const ElementsMap = new Map<Mapped.Owner, {el: object; component: Mapped.Owner}>()
218-
219218
const $WALKER = Symbol('tree-walker')
220219

221220
function observeComputation<TEl extends object>(
@@ -273,68 +272,33 @@ function pushResolvedElements<TEl extends object>(list: TEl[], value: unknown, e
273272
}
274273
}
275274

276-
let MappedOwnerNode: Mapped.Owner
277-
let AddedToParentElements = false
278-
279275
/**
280-
* @param els elements to map
281-
* @param parent_children parent owner children.
282-
* Will be checked for existing elements, and if found, `MappedOwnerNode` will be injected in the place of the element.
283-
* Passing `undefined` will skip this check.
276+
* Updates a map of Element to Component_Owner by traversing the owner tree
277+
*
278+
* @param owner owner to start traversal from
279+
* @param eli Element interface
280+
* @param map Optional existing map to update
281+
*
282+
* The elements are resolved shallowly,
283+
* so only top-level elements will be mapped to their components.
284284
*/
285-
function mapElements<TEl extends object>(
286-
els: Iterable<TEl>,
287-
parent_children: Mapped.Owner[] | undefined,
288-
eli: ElementInterface<TEl>,
289-
out: Mapped.Owner[] = [],
290-
): Mapped.Owner[] {
291-
292-
els: for (let el of els) {
293-
if (!eli.isElement(el)) continue
294-
295-
if (parent_children) {
296-
297-
// find el in parent els and remove it
298-
let to_check = [parent_children]
299-
let index = 0
300-
let el_nodes = to_check[index++]
301-
302-
while (el_nodes) {
303-
for (let i = 0; i < el_nodes.length; i++) {
304-
let el_node = el_nodes[i]!
305-
let el_node_data = ElementsMap.get(el_node)
306-
if (el_node_data && el_node_data.el === el) {
307-
if (AddedToParentElements) {
308-
// if the element is already added to the parent, just remove the element
309-
el_nodes.splice(i, 1)
310-
} else {
311-
// otherwise, we can just replace it with the component
312-
el_nodes[i] = MappedOwnerNode
313-
AddedToParentElements = true
314-
}
315-
out.push(el_node)
316-
el_node_data.component = MappedOwnerNode
317-
continue els
318-
}
319-
if (el_node.children.length) to_check.push(el_node.children)
320-
}
321-
el_nodes = to_check[index++]
322-
}
285+
export function gatherElementMap(
286+
owner: Solid.Owner,
287+
map: Map<Element, Solid.Component> = new Map(),
288+
eli: ElementInterface<Element>,
289+
): Map<Element, Solid.Component> {
290+
291+
if (markOwnerType(owner) === NodeType.Component) {
292+
for (let el of resolveElements(owner.value, eli)) {
293+
map.set(el, owner as Solid.Component)
323294
}
324-
325-
let el_json: Mapped.Owner = {
326-
id: getSdtId(el, ObjectType.Element),
327-
type: NodeType.Element,
328-
name: eli.getName(el) ?? UNKNOWN,
329-
children: [],
330-
}
331-
out.push(el_json)
332-
ElementsMap.set(el_json, {el, component: MappedOwnerNode})
333-
334-
mapElements(eli.getChildren(el), parent_children, eli, el_json.children)
335295
}
336-
337-
return out
296+
297+
for (let child of owner_each_child(owner)) {
298+
gatherElementMap(child, map, eli)
299+
}
300+
301+
return map
338302
}
339303

340304
function mapChildren<TEl extends object>(
@@ -360,6 +324,8 @@ function mapChildren<TEl extends object>(
360324
return children
361325
}
362326

327+
let element_set = new Set<any>()
328+
363329
function mapOwner<TEl extends object>(
364330
owner: Solid.Owner,
365331
parent: Mapped.Owner | null,
@@ -390,7 +356,7 @@ function mapOwner<TEl extends object>(
390356
The provider component will be omitted
391357
*/
392358
if (name === 'provider' &&
393-
owner.owned &&
359+
owner.owned != null &&
394360
owner.owned.length === 1 &&
395361
markOwnerType(first_owned = owner.owned[0]!) === NodeType.Context
396362
) {
@@ -404,7 +370,7 @@ function mapOwner<TEl extends object>(
404370
// Refresh
405371
// omitting refresh memo — map it's children instead
406372
let refresh = getComponentRefreshNode(owner as Solid.Component)
407-
if (refresh) {
373+
if (refresh != null) {
408374
mapped.hmr = true
409375
owner = refresh
410376
}
@@ -417,24 +383,153 @@ function mapOwner<TEl extends object>(
417383
}
418384
}
419385

420-
AddedToParentElements = false as boolean
421-
MappedOwnerNode = mapped
422-
386+
mapChildren(owner, mapped, config, mapped.children)
387+
423388
// Map html elements in DOM mode
424389
if (config.mode === TreeWalkerMode.DOM) {
425390
// elements might already be resolved when mapping components
426391
resolved_els ??= resolveElements(owner.value, config.eli)
427-
mapElements(resolved_els, parent?.children, config.eli, mapped.children)
428-
}
429392

430-
// global `AddedToParentElements` will be changed in mapChildren
431-
let addedToParent = AddedToParentElements
393+
let elements_stack_arr = [resolved_els] as (ElementChildren<TEl>)[]
394+
let elements_stack_idx = [0]
395+
let elements_stack_owner = [mapped]
396+
let elements_stack_len = 1
432397

433-
mapChildren(owner, mapped, config, mapped.children)
398+
let children_stack_arr = [mapped.children]
399+
let children_stack_idx = [0]
400+
let children_stack_len = 1
434401

435-
return addedToParent ? undefined : mapped
436-
}
402+
while (children_stack_len > 0) {
403+
404+
let children = children_stack_arr[children_stack_len-1]!
405+
let child_idx = children_stack_idx[children_stack_len-1]!
406+
407+
if (child_idx >= children.length) {
408+
children_stack_len -= 1
409+
continue
410+
}
411+
412+
children_stack_idx[children_stack_len-1]! += 1
413+
414+
let child = children[child_idx]!
415+
416+
if (child.type === NodeType.Element) {
417+
418+
// Don't go over added element children
419+
// TODO: add children cap stack
420+
if (children_stack_len-1 === 0) {
421+
continue
422+
}
423+
424+
while (elements_stack_len > 0) {
425+
426+
let elements = elements_stack_arr [elements_stack_len-1]!
427+
let el_idx = elements_stack_idx [elements_stack_len-1]!
428+
let el_owner = elements_stack_owner[elements_stack_len-1]!
429+
430+
if (el_idx >= elements.length) {
431+
elements_stack_len -= 1
432+
continue
433+
}
434+
435+
elements_stack_idx[elements_stack_len-1]! += 1
436+
437+
let el = elements[el_idx]!
438+
let el_id = getSdtId(el, ObjectType.Element)
439+
440+
// Child has this element
441+
if (el_id === child.id) {
442+
if (elements_stack_len > 1) {
443+
el_owner.children.push(children_stack_arr[0]![children_stack_idx[0]!-1]!)
444+
children_stack_arr[0]!.splice(children_stack_idx[0]!-1, 1)
445+
children_stack_idx[0]! -= 1
446+
}
447+
children_stack_len = 0
437448

449+
// Skip remaining elements from the child
450+
for (let skip_child_idx = child_idx + 1;;) {
451+
let el_idx = elements_stack_idx[elements_stack_len-1]!
452+
453+
if (el_idx >= elements.length ||
454+
skip_child_idx >= children.length ||
455+
children[skip_child_idx]!.id !== getSdtId(elements[el_idx]!, ObjectType.Element)
456+
) {
457+
break
458+
}
459+
460+
elements_stack_idx[elements_stack_len-1]! += 1
461+
skip_child_idx += 1
462+
}
463+
464+
break
465+
}
466+
467+
if (element_set.has(el)) {
468+
continue
469+
}
470+
471+
else {
472+
let el_json: Mapped.Owner = {
473+
id: el_id,
474+
type: NodeType.Element,
475+
name: config.eli.getName(el) ?? UNKNOWN,
476+
children: [],
477+
}
478+
el_owner.children.push(el_json)
479+
element_set.add(el)
480+
481+
elements_stack_arr [elements_stack_len] = config.eli.getChildren(el)
482+
elements_stack_idx [elements_stack_len] = 0
483+
elements_stack_owner[elements_stack_len] = el_json
484+
elements_stack_len += 1
485+
}
486+
}
487+
488+
} else {
489+
children_stack_arr[children_stack_len] = child.children
490+
children_stack_idx[children_stack_len] = 0
491+
children_stack_len += 1
492+
continue
493+
}
494+
}
495+
496+
// append remaining elements to children
497+
while (elements_stack_len > 0) {
498+
let elements = elements_stack_arr [elements_stack_len-1]!
499+
let idx = elements_stack_idx [elements_stack_len-1]!
500+
let el_owner = elements_stack_owner[elements_stack_len-1]!
501+
502+
if (idx >= elements.length) {
503+
elements_stack_len -= 1
504+
continue
505+
}
506+
507+
elements_stack_idx[elements_stack_len-1]! += 1
508+
509+
let el = elements[idx]!
510+
511+
if (element_set.has(el)) {
512+
continue
513+
}
514+
515+
let el_json: Mapped.Owner = {
516+
id: getSdtId(el, ObjectType.Element),
517+
type: NodeType.Element,
518+
name: config.eli.getName(el) ?? UNKNOWN,
519+
children: [],
520+
}
521+
el_owner.children.push(el_json)
522+
element_set.add(el)
523+
524+
elements_stack_arr [elements_stack_len] = config.eli.getChildren(el)
525+
elements_stack_idx [elements_stack_len] = 0
526+
elements_stack_owner[elements_stack_len] = el_json
527+
elements_stack_len += 1
528+
}
529+
}
530+
531+
return mapped
532+
}
438533

439534
export const walkSolidTree = /*#__PURE__*/ untrackedCallback(function <TEl extends object>(
440535
owner: Solid.Owner | Solid.Root,
@@ -444,12 +539,7 @@ export const walkSolidTree = /*#__PURE__*/ untrackedCallback(function <TEl exten
444539
const r = mapOwner(owner, null, config)!
445540

446541
if (config.mode === TreeWalkerMode.DOM) {
447-
// Register all mapped element nodes to their components
448-
for (let [elNode, {el, component}] of ElementsMap) {
449-
registerElement(config.registry, component.id, elNode.id, el as TEl)
450-
}
451-
452-
ElementsMap.clear()
542+
element_set.clear()
453543
}
454544

455545
return r

0 commit comments

Comments
 (0)