Skip to content

Commit 91d1ec5

Browse files
committed
feat(cursors): migrate to single cursor prop
1 parent 62afb5a commit 91d1ec5

File tree

8 files changed

+86
-248
lines changed

8 files changed

+86
-248
lines changed

packages/qwik/src/core/shared/cursor/chore-execution.ts

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import type { ValueOrPromise } from '../utils/types';
2424
import type { Container, HostElement } from '../types';
2525
import type { VNode } from '../vnode/vnode';
2626
import { VNodeFlags, type ClientContainer } from '../../client/types';
27-
import type { Cursor } from './cursor';
2827
import type { NodeProp } from '../../reactive-primitives/subscription-data';
2928
import { isSignal, scheduleEffects } from '../../reactive-primitives/utils';
3029
import type { Signal } from '../../reactive-primitives/signal.public';
@@ -33,7 +32,7 @@ import type { ISsrNode, SSRContainer } from '../../ssr/ssr-types';
3332
import type { ElementVNode } from '../vnode/element-vnode';
3433
import { VNodeOperationType } from '../vnode/enums/vnode-operation-type.enum';
3534
import type { JSXOutput } from '../jsx/types/jsx-node';
36-
import { getAfterFlushTasks, setAfterFlushTasks, setExtraPromises } from './cursor-props';
35+
import { type CursorData } from './cursor-props';
3736
import { invoke, newInvokeContext } from '../../use/use-core';
3837
import type { WrappedSignalImpl } from '../../reactive-primitives/impl/wrapped-signal-impl';
3938
import { SignalFlags } from '../../reactive-primitives/types';
@@ -58,7 +57,7 @@ import { SignalFlags } from '../../reactive-primitives/types';
5857
export function executeTasks(
5958
vNode: VNode,
6059
container: Container,
61-
cursor: Cursor
60+
cursorData: CursorData
6261
): ValueOrPromise<void> {
6362
vNode.dirty &= ~ChoreBits.TASKS;
6463

@@ -71,7 +70,6 @@ export function executeTasks(
7170

7271
// Execute all tasks in sequence
7372
let taskPromise: Promise<void> | undefined;
74-
let extraPromises: Promise<void>[] | undefined;
7573

7674
for (const item of elementSeq) {
7775
if (item instanceof Task) {
@@ -88,12 +86,7 @@ export function executeTasks(
8886
runResource(task as ResourceDescriptor<TaskFn>, container, vNode);
8987
} else if (task.$flags$ & TaskFlags.VISIBLE_TASK) {
9088
// VisibleTasks: store for execution after flush (don't execute now)
91-
let visibleTasks = getAfterFlushTasks(cursor);
92-
if (!visibleTasks) {
93-
visibleTasks = [];
94-
setAfterFlushTasks(cursor, visibleTasks);
95-
}
96-
visibleTasks.push(task);
89+
(cursorData.afterFlushTasks ||= []).push(task);
9790
} else {
9891
// Regular tasks: chain promises only between each other
9992
const result = runTask(task, container, vNode);
@@ -103,17 +96,15 @@ export function executeTasks(
10396
? taskPromise.then(() => result as Promise<void>)
10497
: (result as Promise<void>);
10598
} else {
106-
extraPromises ||= [];
99+
// TODO: set extrapromises on vNode instead of cursorData if server
100+
const extraPromises = (cursorData.extraPromises ||= []);
107101
extraPromises.push(result as Promise<void>);
108102
}
109103
}
110104
}
111105
}
112106
}
113107

114-
if (extraPromises) {
115-
setExtraPromises(isServerPlatform() ? vNode : cursor, extraPromises);
116-
}
117108
return taskPromise;
118109
}
119110

packages/qwik/src/core/shared/cursor/cursor-flush.ts

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,8 @@ import { runTask } from '../../use/use-task';
22
import { QContainerValue, type Container } from '../types';
33
import { dangerouslySetInnerHTML, QContainerAttr } from '../utils/markers';
44
import { VNodeOperationType } from '../vnode/enums/vnode-operation-type.enum';
5-
import type { VNode } from '../vnode/vnode';
65
import type { Cursor } from './cursor';
7-
import {
8-
getAfterFlushTasks,
9-
getCursorJournal,
10-
setAfterFlushTasks,
11-
setCursorJournal,
12-
} from './cursor-props';
6+
import { getCursorData, type CursorData } from './cursor-props';
137

148
/**
159
* Executes the flush phase for a cursor.
@@ -18,12 +12,13 @@ import {
1812
* @param container - The container to execute the flush phase for
1913
*/
2014
export function executeFlushPhase(cursor: Cursor, container: Container): void {
21-
flushChanges(cursor);
22-
executeAfterFlush(container, cursor);
15+
const cursorData = getCursorData(cursor)!;
16+
flushChanges(cursorData);
17+
executeAfterFlush(container, cursorData);
2318
}
2419

25-
function flushChanges(vNode: VNode): void {
26-
const journal = getCursorJournal(vNode);
20+
function flushChanges(cursorData: CursorData): void {
21+
const journal = cursorData.journal;
2722
if (!journal || journal.length === 0) {
2823
return;
2924
}
@@ -74,19 +69,19 @@ function flushChanges(vNode: VNode): void {
7469
}
7570
}
7671
}
77-
setCursorJournal(vNode, null);
72+
cursorData.journal = null;
7873
}
7974

80-
function executeAfterFlush(container: Container, cursor: Cursor): void {
81-
const visibleTasks = getAfterFlushTasks(cursor);
75+
function executeAfterFlush(container: Container, cursorData: CursorData): void {
76+
const visibleTasks = cursorData.afterFlushTasks;
8277
if (!visibleTasks || visibleTasks.length === 0) {
8378
return;
8479
}
8580
for (const visibleTask of visibleTasks) {
8681
const task = visibleTask;
8782
runTask(task, container, task.$el$);
8883
}
89-
setAfterFlushTasks(cursor, null);
84+
cursorData.afterFlushTasks = null;
9085
}
9186

9287
const isBooleanAttr = (element: Element, key: string): boolean => {
Lines changed: 31 additions & 176 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import { VNodeFlags } from '../../client/types';
21
import type { VNode } from '../vnode/vnode';
3-
import type { Props } from '../jsx/jsx-runtime';
42
import { isCursor } from './cursor';
53
import { removeCursorFromQueue } from './cursor-queue';
64
import type { Container } from '../types';
@@ -12,71 +10,16 @@ import { resolveCursor } from './cursor-walker';
1210
* Keys used to store cursor-related data in vNode props. These are internal properties that should
1311
* not conflict with user props.
1412
*/
15-
const CURSOR_PRIORITY_KEY = ':priority';
16-
const CURSOR_POSITION_KEY = ':position';
17-
const CURSOR_CHILD_KEY = ':childIndex';
18-
const CURSOR_CONTAINER_KEY = ':cursorContainer';
19-
const VNODE_PROMISE_KEY = ':promise';
20-
const CURSOR_EXTRA_PROMISES_KEY = ':extraPromises';
21-
const CURSOR_JOURNAL_KEY = ':journal';
22-
const CURSOR_AFTER_FLUSH_TASKS_KEY = ':afterFlushTasks';
13+
const CURSOR_DATA_KEY = ':cursor';
2314

24-
/**
25-
* Gets the priority of a cursor vNode.
26-
*
27-
* @param vNode - The cursor vNode
28-
* @returns The priority, or null if not a cursor
29-
*/
30-
export function getCursorPriority(vNode: VNode): number | null {
31-
if (!(vNode.flags & VNodeFlags.Cursor)) {
32-
return null;
33-
}
34-
const props = vNode.props as Props;
35-
return (props[CURSOR_PRIORITY_KEY] as number) ?? null;
36-
}
37-
38-
/**
39-
* Sets the priority of a cursor vNode.
40-
*
41-
* @param vNode - The vNode to set priority on
42-
* @param priority - The priority value
43-
*/
44-
export function setCursorPriority(vNode: VNode, priority: number): void {
45-
const props = (vNode.props ||= {});
46-
props[CURSOR_PRIORITY_KEY] = priority;
47-
}
48-
49-
/**
50-
* Gets the current cursor position from a cursor vNode.
51-
*
52-
* @param vNode - The cursor vNode
53-
* @returns The cursor position, or null if at root or not a cursor
54-
*/
55-
export function getCursorPosition(vNode: VNode): VNode | null {
56-
if (!(vNode.flags & VNodeFlags.Cursor)) {
57-
return null;
58-
}
59-
const props = vNode.props;
60-
return (props?.[CURSOR_POSITION_KEY] as VNode | null) ?? null;
61-
}
62-
63-
/**
64-
* Set the next child to process index in a vNode.
65-
*
66-
* @param vNode - The vNode
67-
* @param childIndex - The child index to set
68-
*/
69-
export function setNextChildIndex(vNode: VNode, childIndex: number): void {
70-
const props = vNode.props as Props;
71-
// We could also add a dirtycount to avoid checking all children after completion
72-
// perf: we could also use dirtychild index 0 for the index instead
73-
props[CURSOR_CHILD_KEY] = childIndex;
74-
}
75-
76-
/** Gets the next child to process index from a vNode. */
77-
export function getNextChildIndex(vNode: VNode): number | null {
78-
const props = vNode.props as Props;
79-
return (props[CURSOR_CHILD_KEY] as number) ?? null;
15+
export interface CursorData {
16+
afterFlushTasks: Task[] | null;
17+
extraPromises: Promise<void>[] | null;
18+
journal: VNodeJournal | null;
19+
container: Container;
20+
position: VNode | null;
21+
priority: number;
22+
promise: Promise<void> | null;
8023
}
8124

8225
/**
@@ -87,158 +30,70 @@ export function getNextChildIndex(vNode: VNode): number | null {
8730
*/
8831
export function setCursorPosition(
8932
container: Container,
90-
vNode: VNode,
33+
cursorData: CursorData,
9134
position: VNode | null
9235
): void {
93-
const props = vNode.props as Props;
94-
props[CURSOR_POSITION_KEY] = position;
36+
cursorData.position = position;
9537
if (position && isCursor(position)) {
96-
mergeCursors(container, vNode, position);
38+
mergeCursors(container, cursorData, position);
9739
}
9840
}
9941

100-
function mergeCursors(container: Container, newCursor: VNode, oldCursor: VNode): void {
42+
function mergeCursors(container: Container, newCursorData: CursorData, oldCursor: VNode): void {
10143
// delete from global cursors queue
10244
removeCursorFromQueue(oldCursor);
10345
resolveCursor(container);
46+
const oldCursorData = getCursorData(oldCursor)!;
10447
// merge after flush tasks
105-
const oldAfterFlushTasks = getAfterFlushTasks(oldCursor);
48+
const oldAfterFlushTasks = oldCursorData.afterFlushTasks;
10649
if (oldAfterFlushTasks && oldAfterFlushTasks.length > 0) {
107-
const newAfterFlushTasks = getAfterFlushTasks(newCursor);
50+
const newAfterFlushTasks = newCursorData.afterFlushTasks;
10851
if (newAfterFlushTasks) {
10952
newAfterFlushTasks.push(...oldAfterFlushTasks);
11053
} else {
111-
setAfterFlushTasks(newCursor, oldAfterFlushTasks);
54+
newCursorData.afterFlushTasks = oldAfterFlushTasks;
11255
}
11356
}
11457
// merge extra promises
115-
const oldExtraPromises = getExtraPromises(oldCursor);
58+
const oldExtraPromises = oldCursorData.extraPromises;
11659
if (oldExtraPromises && oldExtraPromises.length > 0) {
117-
const newExtraPromises = getExtraPromises(newCursor);
60+
const newExtraPromises = newCursorData.extraPromises;
11861
if (newExtraPromises) {
11962
newExtraPromises.push(...oldExtraPromises);
12063
} else {
121-
setExtraPromises(newCursor, oldExtraPromises);
64+
newCursorData.extraPromises = oldExtraPromises;
12265
}
12366
}
12467
// merge journal
125-
const oldJournal = getCursorJournal(oldCursor);
68+
const oldJournal = oldCursorData.journal;
12669
if (oldJournal && oldJournal.length > 0) {
127-
const newJournal = getCursorJournal(newCursor);
70+
const newJournal = newCursorData.journal;
12871
if (newJournal) {
12972
newJournal.push(...oldJournal);
13073
} else {
131-
setCursorJournal(newCursor, oldJournal);
74+
newCursorData.journal = oldJournal;
13275
}
13376
}
13477
}
13578

13679
/**
137-
* Gets the blocking promise from a vNode.
138-
*
139-
* @param vNode - The vNode
140-
* @returns The promise, or null if none or not a cursor
141-
*/
142-
export function getVNodePromise(vNode: VNode): Promise<void> | null {
143-
const props = vNode.props;
144-
return (props?.[VNODE_PROMISE_KEY] as Promise<void> | null) ?? null;
145-
}
146-
147-
/**
148-
* Sets the blocking promise on a vNode.
149-
*
150-
* @param vNode - The vNode
151-
* @param promise - The promise to set, or null to clear
152-
*/
153-
export function setVNodePromise(vNode: VNode, promise: Promise<void> | null): void {
154-
const props = (vNode.props ||= {});
155-
props[VNODE_PROMISE_KEY] = promise;
156-
}
157-
158-
/**
159-
* Gets extra promises from a vNode.
160-
*
161-
* @param vNode - The vNode
162-
* @returns The extra promises set
163-
*/
164-
export function getExtraPromises(vNode: VNode): Promise<void>[] | null {
165-
const props = vNode.props;
166-
return (props?.[CURSOR_EXTRA_PROMISES_KEY] as Promise<void>[] | null) ?? null;
167-
}
168-
169-
/**
170-
* Sets extra promises on a vNode.
171-
*
172-
* @param vNode - The vNode
173-
* @param extraPromises - The extra promises set, or null to clear
174-
*/
175-
export function setExtraPromises(vNode: VNode, extraPromises: Promise<void>[] | null): void {
176-
const props = (vNode.props ||= {});
177-
props[CURSOR_EXTRA_PROMISES_KEY] = extraPromises;
178-
}
179-
180-
/**
181-
* Sets the cursor container on a vNode.
182-
*
183-
* @param vNode - The vNode
184-
* @param container - The container to set
185-
*/
186-
export function setCursorContainer(vNode: VNode, container: Container): void {
187-
const props = (vNode.props ||= {});
188-
props[CURSOR_CONTAINER_KEY] = container;
189-
}
190-
191-
/**
192-
* Gets the cursor container from a vNode.
193-
*
194-
* @param vNode - The vNode
195-
* @returns The container, or null if none or not a cursor
196-
*/
197-
export function getCursorContainer(vNode: VNode): Container | null {
198-
const props = vNode.props;
199-
return (props?.[CURSOR_CONTAINER_KEY] as Container | null) ?? null;
200-
}
201-
202-
/**
203-
* Sets the cursor journal on a vNode.
204-
*
205-
* @param vNode - The vNode
206-
* @param journal - The journal to set
207-
*/
208-
export function setCursorJournal(vNode: VNode, journal: VNodeJournal | null): void {
209-
const props = (vNode.props ||= {});
210-
props[CURSOR_JOURNAL_KEY] = journal;
211-
}
212-
213-
/**
214-
* Gets the cursor journal from a vNode.
215-
*
216-
* @param vNode - The vNode
217-
* @returns The journal, or null if none or not a cursor
218-
*/
219-
export function getCursorJournal(vNode: VNode): VNodeJournal | null {
220-
const props = vNode.props;
221-
return (props?.[CURSOR_JOURNAL_KEY] as VNodeJournal | null) ?? null;
222-
}
223-
224-
/**
225-
* Gets the after flush tasks from a vNode.
80+
* Gets the cursor data from a vNode.
22681
*
22782
* @param vNode - The vNode
228-
* @returns The after flush tasks, or null if none or not a cursor
83+
* @returns The cursor data, or null if none or not a cursor
22984
*/
230-
export function getAfterFlushTasks(vNode: VNode): Task[] | null {
85+
export function getCursorData(vNode: VNode): CursorData | null {
23186
const props = vNode.props;
232-
return (props?.[CURSOR_AFTER_FLUSH_TASKS_KEY] as Task[] | null) ?? null;
87+
return (props?.[CURSOR_DATA_KEY] as CursorData | null) ?? null;
23388
}
23489

23590
/**
236-
* Sets the after flush tasks on a vNode.
91+
* Sets the cursor data on a vNode.
23792
*
23893
* @param vNode - The vNode
239-
* @param afterFlushTasks - The after flush tasks to set, or null to clear
94+
* @param cursorData - The cursor data to set, or null to clear
24095
*/
241-
export function setAfterFlushTasks(vNode: VNode, afterFlushTasks: Task[] | null): void {
96+
export function setCursorData(vNode: VNode, cursorData: CursorData | null): void {
24297
const props = (vNode.props ||= {});
243-
props[CURSOR_AFTER_FLUSH_TASKS_KEY] = afterFlushTasks;
98+
props[CURSOR_DATA_KEY] = cursorData;
24499
}

0 commit comments

Comments
 (0)