mirror of
https://github.com/enso-org/enso.git
synced 2024-12-27 21:12:48 +03:00
More consistent reactive indices (#8353)
- Renames all `ReactiveIndex` and `ReactiveDb`s into the format `<key>To<value>` - `ReactiveMapping`s (in `graphDatabase.ts`) currently have not been renamed - I'm not sure they need to be - Removes various methods that have been made unnecessary by this change - Simplifies some other methods - Changes `SuggestionDb` to extend `ReactiveDb` # Important Notes None
This commit is contained in:
parent
268e595ec1
commit
1cf5ea96d6
@ -57,7 +57,7 @@ watchEffect(() => {
|
|||||||
const dom = document.createElement('div')
|
const dom = document.createElement('div')
|
||||||
const astSpan = ast.span()
|
const astSpan = ast.span()
|
||||||
let foundNode: ExprId | undefined
|
let foundNode: ExprId | undefined
|
||||||
for (const [id, node] of graphStore.db.allNodes()) {
|
for (const [id, node] of graphStore.db.nodeIdToNode.entries()) {
|
||||||
if (rangeEncloses(node.rootSpan.span(), astSpan)) {
|
if (rangeEncloses(node.rootSpan.span(), astSpan)) {
|
||||||
foundNode = id
|
foundNode = id
|
||||||
break
|
break
|
||||||
|
@ -102,8 +102,8 @@ test.each([
|
|||||||
profilingInfo: [],
|
profilingInfo: [],
|
||||||
})
|
})
|
||||||
const mockGraphDb = GraphDb.Mock(computedValueRegistryMock)
|
const mockGraphDb = GraphDb.Mock(computedValueRegistryMock)
|
||||||
mockGraphDb.nodes.set(operator1Id, mockNode('operator1', operator1Id))
|
mockGraphDb.nodeIdToNode.set(operator1Id, mockNode('operator1', operator1Id))
|
||||||
mockGraphDb.nodes.set(operator2Id, mockNode('operator2', operator2Id))
|
mockGraphDb.nodeIdToNode.set(operator2Id, mockNode('operator2', operator2Id))
|
||||||
|
|
||||||
const input = useComponentBrowserInput(mockGraphDb)
|
const input = useComponentBrowserInput(mockGraphDb)
|
||||||
input.code.value = code
|
input.code.value = code
|
||||||
|
@ -58,7 +58,8 @@ const name = computed<Opt<QualifiedName>>(() => {
|
|||||||
// === Breadcrumbs ===
|
// === Breadcrumbs ===
|
||||||
|
|
||||||
const color = computed(() => {
|
const color = computed(() => {
|
||||||
const groupIndex = db.entries.get(props.selectedEntry)?.groupIndex
|
const groupIndex =
|
||||||
|
props.selectedEntry != null ? db.entries.get(props.selectedEntry)?.groupIndex : undefined
|
||||||
return groupColorStyle(tryGetIndex(db.groups, groupIndex))
|
return groupColorStyle(tryGetIndex(db.groups, groupIndex))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -108,7 +108,7 @@ export function lookupDocumentation(db: SuggestionDb, id: SuggestionId): Docs {
|
|||||||
|
|
||||||
function getChildren(db: SuggestionDb, id: SuggestionId, kind: SuggestionKind): Docs[] {
|
function getChildren(db: SuggestionDb, id: SuggestionId, kind: SuggestionKind): Docs[] {
|
||||||
if (!id) return []
|
if (!id) return []
|
||||||
const children = Array.from(db.parent.reverseLookup(id))
|
const children = Array.from(db.childIdToParentId.reverseLookup(id))
|
||||||
return children.reduce((acc: Docs[], id: SuggestionId) => {
|
return children.reduce((acc: Docs[], id: SuggestionId) => {
|
||||||
if (db.get(id)?.kind === kind) {
|
if (db.get(id)?.kind === kind) {
|
||||||
const docs = lookupDocumentation(db, id)
|
const docs = lookupDocumentation(db, id)
|
||||||
|
@ -54,14 +54,14 @@ const interactionBindingsHandler = interactionBindings.handler({
|
|||||||
click: (e) => (e instanceof MouseEvent ? interaction.handleClick(e) : false),
|
click: (e) => (e instanceof MouseEvent ? interaction.handleClick(e) : false),
|
||||||
})
|
})
|
||||||
|
|
||||||
// This is where the component browser should be placed when it is opened.
|
/** Where the component browser should be placed when it is opened. */
|
||||||
function targetComponentBrowserPosition() {
|
function targetComponentBrowserPosition() {
|
||||||
const editedInfo = graphStore.editedNodeInfo
|
const editedInfo = graphStore.editedNodeInfo
|
||||||
const isEditingNode = editedInfo != null
|
const isEditingNode = editedInfo != null
|
||||||
const hasNodeSelected = nodeSelection.selected.size > 0
|
const hasNodeSelected = nodeSelection.selected.size > 0
|
||||||
const nodeSize = new Vec2(0, 24)
|
const nodeSize = new Vec2(0, 24)
|
||||||
if (isEditingNode) {
|
if (isEditingNode) {
|
||||||
const targetNode = graphStore.db.nodes.get(editedInfo.id)
|
const targetNode = graphStore.db.nodeIdToNode.get(editedInfo.id)
|
||||||
const targetPos = targetNode?.position ?? Vec2.Zero
|
const targetPos = targetNode?.position ?? Vec2.Zero
|
||||||
return targetPos.add(COMPONENT_BROWSER_TO_NODE_OFFSET)
|
return targetPos.add(COMPONENT_BROWSER_TO_NODE_OFFSET)
|
||||||
} else if (hasNodeSelected) {
|
} else if (hasNodeSelected) {
|
||||||
@ -75,7 +75,7 @@ function targetComponentBrowserPosition() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the current position of the component browser.
|
/** The current position of the component browser. */
|
||||||
const componentBrowserPosition = ref<Vec2>(Vec2.Zero)
|
const componentBrowserPosition = ref<Vec2>(Vec2.Zero)
|
||||||
|
|
||||||
const graphEditorSourceNode = computed(() => {
|
const graphEditorSourceNode = computed(() => {
|
||||||
@ -132,7 +132,7 @@ const graphBindingsHandler = graphBindings.handler({
|
|||||||
graphStore.transact(() => {
|
graphStore.transact(() => {
|
||||||
const allVisible = set
|
const allVisible = set
|
||||||
.toArray(nodeSelection.selected)
|
.toArray(nodeSelection.selected)
|
||||||
.every((id) => !(graphStore.db.getNode(id)?.vis?.visible !== true))
|
.every((id) => !(graphStore.db.nodeIdToNode.get(id)?.vis?.visible !== true))
|
||||||
|
|
||||||
for (const nodeId of nodeSelection.selected) {
|
for (const nodeId of nodeSelection.selected) {
|
||||||
graphStore.setNodeVisualizationVisible(nodeId, !allVisible)
|
graphStore.setNodeVisualizationVisible(nodeId, !allVisible)
|
||||||
@ -158,7 +158,7 @@ const codeEditorHandler = codeEditorBindings.handler({
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
/// Track play button presses.
|
/** Track play button presses. */
|
||||||
function onPlayButtonPress() {
|
function onPlayButtonPress() {
|
||||||
projectStore.lsRpcConnection.then(async () => {
|
projectStore.lsRpcConnection.then(async () => {
|
||||||
const modeValue = projectStore.executionMode
|
const modeValue = projectStore.executionMode
|
||||||
@ -169,7 +169,7 @@ function onPlayButtonPress() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Watch for changes in the execution mode.
|
// Watch for changes in the execution mode.
|
||||||
watch(
|
watch(
|
||||||
() => projectStore.executionMode,
|
() => projectStore.executionMode,
|
||||||
(modeValue) => {
|
(modeValue) => {
|
||||||
@ -252,10 +252,10 @@ async function handleFileDrop(event: DragEvent) {
|
|||||||
|
|
||||||
function onComponentBrowserCommit(content: string) {
|
function onComponentBrowserCommit(content: string) {
|
||||||
if (content != null && graphStore.editedNodeInfo != null) {
|
if (content != null && graphStore.editedNodeInfo != null) {
|
||||||
/// We finish editing a node.
|
// We finish editing a node.
|
||||||
graphStore.setNodeContent(graphStore.editedNodeInfo.id, content)
|
graphStore.setNodeContent(graphStore.editedNodeInfo.id, content)
|
||||||
} else if (content != null) {
|
} else if (content != null) {
|
||||||
/// We finish creating a new node.
|
// We finish creating a new node.
|
||||||
const nodePosition = componentBrowserPosition.value
|
const nodePosition = componentBrowserPosition.value
|
||||||
graphStore.createNode(nodePosition.sub(COMPONENT_BROWSER_TO_NODE_OFFSET), content)
|
graphStore.createNode(nodePosition.sub(COMPONENT_BROWSER_TO_NODE_OFFSET), content)
|
||||||
}
|
}
|
||||||
@ -270,16 +270,13 @@ function onComponentBrowserCancel() {
|
|||||||
interaction.setCurrent(undefined)
|
interaction.setCurrent(undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function getNodeContent(id: ExprId): string {
|
function getNodeContent(id: ExprId): string {
|
||||||
const node = graphStore.db.nodes.get(id)
|
const node = graphStore.db.nodeIdToNode.get(id)
|
||||||
if (node == null) return ''
|
if (node == null) return ''
|
||||||
return node.rootSpan.repr()
|
return node.rootSpan.repr()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch the editedNode in the graph store
|
// Watch the `editedNode` in the graph store
|
||||||
watch(
|
watch(
|
||||||
() => graphStore.editedNodeInfo,
|
() => graphStore.editedNodeInfo,
|
||||||
(editedInfo) => {
|
(editedInfo) => {
|
||||||
@ -304,25 +301,25 @@ const breadcrumbs = computed(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
/// === Clipboard ===
|
// === Clipboard ===
|
||||||
|
|
||||||
const ENSO_MIME_TYPE = 'web application/enso'
|
const ENSO_MIME_TYPE = 'web application/enso'
|
||||||
|
|
||||||
/// The data that is copied to the clipboard.
|
/** The data that is copied to the clipboard. */
|
||||||
interface ClipboardData {
|
interface ClipboardData {
|
||||||
nodes: CopiedNode[]
|
nodes: CopiedNode[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Node data that is copied to the clipboard. Used for serializing and deserializing the node information.
|
/** Node data that is copied to the clipboard. Used for serializing and deserializing the node information. */
|
||||||
interface CopiedNode {
|
interface CopiedNode {
|
||||||
expression: string
|
expression: string
|
||||||
metadata: NodeMetadata | undefined
|
metadata: NodeMetadata | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy the content of the selected node to the clipboard.
|
/** Copy the content of the selected node to the clipboard. */
|
||||||
function copyNodeContent() {
|
function copyNodeContent() {
|
||||||
const id = nodeSelection.selected.values().next().value
|
const id = nodeSelection.selected.values().next().value
|
||||||
const node = graphStore.db.nodes.get(id)
|
const node = graphStore.db.nodeIdToNode.get(id)
|
||||||
if (node == null) return
|
if (node == null) return
|
||||||
const content = node.rootSpan.repr()
|
const content = node.rootSpan.repr()
|
||||||
const metadata = projectStore.module?.getNodeMetadata(id) ?? undefined
|
const metadata = projectStore.module?.getNodeMetadata(id) ?? undefined
|
||||||
|
@ -24,7 +24,7 @@ const sourceNode = computed(() => {
|
|||||||
// When the source is not set (i.e. edge is dragged), use the currently hovered over expression
|
// When the source is not set (i.e. edge is dragged), use the currently hovered over expression
|
||||||
// as the source, as long as it is not from the same node as the target.
|
// as the source, as long as it is not from the same node as the target.
|
||||||
if (setSource == null && selection?.hoveredNode != null) {
|
if (setSource == null && selection?.hoveredNode != null) {
|
||||||
const rawTargetNode = graph.db.getExpressionNodeId(props.edge.target)
|
const rawTargetNode = props.edge.target && graph.db.getExpressionNodeId(props.edge.target)
|
||||||
if (selection.hoveredNode != rawTargetNode) return selection.hoveredNode
|
if (selection.hoveredNode != rawTargetNode) return selection.hoveredNode
|
||||||
}
|
}
|
||||||
return setSource
|
return setSource
|
||||||
@ -40,7 +40,9 @@ const targetExpr = computed(() => {
|
|||||||
return setTarget
|
return setTarget
|
||||||
})
|
})
|
||||||
|
|
||||||
const targetNode = computed(() => graph.db.getExpressionNodeId(targetExpr.value))
|
const targetNode = computed(
|
||||||
|
() => targetExpr.value && graph.db.getExpressionNodeId(targetExpr.value),
|
||||||
|
)
|
||||||
const targetNodeRect = computed(() => targetNode.value && graph.nodeRects.get(targetNode.value))
|
const targetNodeRect = computed(() => targetNode.value && graph.nodeRects.get(targetNode.value))
|
||||||
|
|
||||||
const targetRect = computed<Rect | null>(() => {
|
const targetRect = computed<Rect | null>(() => {
|
||||||
|
@ -21,7 +21,7 @@ const editingEdge: Interaction = {
|
|||||||
if (graph.unconnectedEdge == null) return false
|
if (graph.unconnectedEdge == null) return false
|
||||||
const source = graph.unconnectedEdge.source ?? selection?.hoveredNode
|
const source = graph.unconnectedEdge.source ?? selection?.hoveredNode
|
||||||
const target = graph.unconnectedEdge.target ?? selection?.hoveredPort
|
const target = graph.unconnectedEdge.target ?? selection?.hoveredPort
|
||||||
const targetNode = graph.db.getExpressionNodeId(target)
|
const targetNode = target && graph.db.getExpressionNodeId(target)
|
||||||
graph.transact(() => {
|
graph.transact(() => {
|
||||||
if (source != null && source != targetNode) {
|
if (source != null && source != targetNode) {
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
@ -42,11 +42,13 @@ interaction.setWhen(() => graph.unconnectedEdge != null, editingEdge)
|
|||||||
function disconnectEdge(target: ExprId) {
|
function disconnectEdge(target: ExprId) {
|
||||||
graph.setExpressionContent(target, '_')
|
graph.setExpressionContent(target, '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
function createNodeFromEdgeDrop(source: ExprId) {
|
function createNodeFromEdgeDrop(source: ExprId) {
|
||||||
console.log(`TODO: createNodeFromEdgeDrop(${JSON.stringify(source)})`)
|
console.log(`TODO: createNodeFromEdgeDrop(${JSON.stringify(source)})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEdge(source: ExprId, target: ExprId) {
|
function createEdge(source: ExprId, target: ExprId) {
|
||||||
const sourceNode = graph.db.getNode(source)
|
const sourceNode = graph.db.nodeIdToNode.get(source)
|
||||||
if (sourceNode == null) return
|
if (sourceNode == null) return
|
||||||
// TODO: Check alias analysis to see if the binding is shadowed.
|
// TODO: Check alias analysis to see if the binding is shadowed.
|
||||||
graph.setExpressionContent(target, sourceNode.binding)
|
graph.setExpressionContent(target, sourceNode.binding)
|
||||||
|
@ -45,7 +45,7 @@ const uploadingFiles = computed<[FileName, File][]>(() => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<GraphNode
|
<GraphNode
|
||||||
v-for="[id, node] in graphStore.db.allNodes()"
|
v-for="[id, node] in graphStore.db.nodeIdToNode.entries()"
|
||||||
:key="id"
|
:key="id"
|
||||||
:node="node"
|
:node="node"
|
||||||
:edited="id === graphStore.editedNodeInfo?.id"
|
:edited="id === graphStore.editedNodeInfo?.id"
|
||||||
|
@ -105,7 +105,7 @@ export function useDragging() {
|
|||||||
function* draggedNodes(): Generator<[ExprId, DraggedNode]> {
|
function* draggedNodes(): Generator<[ExprId, DraggedNode]> {
|
||||||
const ids = selection?.isSelected(movedId) ? selection.selected : [movedId]
|
const ids = selection?.isSelected(movedId) ? selection.selected : [movedId]
|
||||||
for (const id of ids) {
|
for (const id of ids) {
|
||||||
const node = graphStore.db.nodes.get(id)
|
const node = graphStore.db.nodeIdToNode.get(id)
|
||||||
if (node != null) yield [id, { initialPos: node.position, currentPos: node.position }]
|
if (node != null) yield [id, { initialPos: node.position, currentPos: node.position }]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -125,7 +125,7 @@ export function useDragging() {
|
|||||||
const rects: Rect[] = []
|
const rects: Rect[] = []
|
||||||
for (const [id, { initialPos }] of this.draggedNodes) {
|
for (const [id, { initialPos }] of this.draggedNodes) {
|
||||||
const rect = graphStore.nodeRects.get(id)
|
const rect = graphStore.nodeRects.get(id)
|
||||||
const node = graphStore.db.nodes.get(id)
|
const node = graphStore.db.nodeIdToNode.get(id)
|
||||||
if (rect != null && node != null) rects.push(new Rect(initialPos.add(newOffset), rect.size))
|
if (rect != null && node != null) rects.push(new Rect(initialPos.add(newOffset), rect.size))
|
||||||
}
|
}
|
||||||
const snap = this.grid.snappedMany(rects, DRAG_SNAP_THRESHOLD)
|
const snap = this.grid.snappedMany(rects, DRAG_SNAP_THRESHOLD)
|
||||||
@ -161,7 +161,7 @@ export function useDragging() {
|
|||||||
|
|
||||||
updateNodesPosition() {
|
updateNodesPosition() {
|
||||||
for (const [id, dragged] of this.draggedNodes) {
|
for (const [id, dragged] of this.draggedNodes) {
|
||||||
const node = graphStore.db.nodes.get(id)
|
const node = graphStore.db.nodeIdToNode.get(id)
|
||||||
if (node == null) continue
|
if (node == null) continue
|
||||||
// If node was moved in other way than current dragging, we want to stop dragging it.
|
// If node was moved in other way than current dragging, we want to stop dragging it.
|
||||||
if (node.position.distanceSquared(dragged.currentPos) > 1.0) {
|
if (node.position.distanceSquared(dragged.currentPos) > 1.0) {
|
||||||
|
@ -34,7 +34,9 @@ const selection = injectGraphSelection(true)
|
|||||||
|
|
||||||
const isHovered = ref(false)
|
const isHovered = ref(false)
|
||||||
|
|
||||||
const hasConnection = computed(() => graph.db.connections.reverseLookup(portId.value).size > 0)
|
const hasConnection = computed(
|
||||||
|
() => graph.db.sourceIdToTargetId.reverseLookup(portId.value).size > 0,
|
||||||
|
)
|
||||||
const isCurrentEdgeHoverTarget = computed(
|
const isCurrentEdgeHoverTarget = computed(
|
||||||
() => isHovered.value && graph.unconnectedEdge != null && selection?.hoveredPort === portId.value,
|
() => isHovered.value && graph.unconnectedEdge != null && selection?.hoveredPort === portId.value,
|
||||||
)
|
)
|
||||||
@ -166,6 +168,7 @@ export const widgetDefinition = defineWidget(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<span
|
<span
|
||||||
ref="rootNode"
|
ref="rootNode"
|
||||||
|
@ -23,8 +23,8 @@ import {
|
|||||||
import { ref, type Ref } from 'vue'
|
import { ref, type Ref } from 'vue'
|
||||||
|
|
||||||
export class GraphDb {
|
export class GraphDb {
|
||||||
nodes = new ReactiveDb<ExprId, Node>()
|
nodeIdToNode = new ReactiveDb<ExprId, Node>()
|
||||||
idents = new ReactiveIndex(this.nodes, (_id, entry) => {
|
nodeIdToBinding = new ReactiveIndex(this.nodeIdToNode, (_id, entry) => {
|
||||||
const idents: [ExprId, string][] = []
|
const idents: [ExprId, string][] = []
|
||||||
entry.rootSpan.visitRecursive((span) => {
|
entry.rootSpan.visitRecursive((span) => {
|
||||||
if (span.isTree(Ast.Tree.Type.Ident)) {
|
if (span.isTree(Ast.Tree.Type.Ident)) {
|
||||||
@ -35,30 +35,31 @@ export class GraphDb {
|
|||||||
})
|
})
|
||||||
return idents
|
return idents
|
||||||
})
|
})
|
||||||
private nodeExpressions = new ReactiveIndex(this.nodes, (id, entry) => {
|
nodeIdToExprId = new ReactiveIndex(this.nodeIdToNode, (id, entry) => {
|
||||||
const exprs = new Set<ExprId>()
|
const exprs = new Set<ExprId>()
|
||||||
for (const ast of entry.rootSpan.walkRecursive()) {
|
for (const ast of entry.rootSpan.walkRecursive()) {
|
||||||
exprs.add(ast.astId)
|
exprs.add(ast.astId)
|
||||||
}
|
}
|
||||||
return Array.from(exprs, (expr) => [id, expr])
|
return Array.from(exprs, (expr) => [id, expr])
|
||||||
})
|
})
|
||||||
nodeByBinding = new ReactiveIndex(this.nodes, (id, entry) => [[entry.binding, id]])
|
bindingToNodeId = new ReactiveIndex(this.nodeIdToNode, (id, entry) => [[entry.binding, id]])
|
||||||
connections = new ReactiveIndex(this.nodes, (id, entry) => {
|
sourceIdToTargetId = new ReactiveIndex(this.nodeIdToNode, (id, entry) => {
|
||||||
const usageEntries: [ExprId, ExprId][] = []
|
const usageEntries: [ExprId, ExprId][] = []
|
||||||
const usages = this.idents.reverseLookup(entry.binding)
|
const usages = this.nodeIdToBinding.reverseLookup(entry.binding)
|
||||||
for (const usage of usages) {
|
for (const usage of usages) {
|
||||||
usageEntries.push([id, usage])
|
usageEntries.push([id, usage])
|
||||||
}
|
}
|
||||||
return usageEntries
|
return usageEntries
|
||||||
})
|
})
|
||||||
nodeMainSuggestion = new ReactiveMapping(this.nodes, (id, _entry) => {
|
nodeMainSuggestion = new ReactiveMapping(this.nodeIdToNode, (id, _entry) => {
|
||||||
const expressionInfo = this.getExpressionInfo(id)
|
const expressionInfo = this.getExpressionInfo(id)
|
||||||
const method = expressionInfo?.methodCall?.methodPointer
|
const method = expressionInfo?.methodCall?.methodPointer
|
||||||
if (method == null) return
|
if (method == null) return
|
||||||
const suggestionId = this.suggestionDb.findByMethodPointer(method)
|
const suggestionId = this.suggestionDb.findByMethodPointer(method)
|
||||||
|
if (suggestionId == null) return
|
||||||
return this.suggestionDb.get(suggestionId)
|
return this.suggestionDb.get(suggestionId)
|
||||||
})
|
})
|
||||||
private nodeColors = new ReactiveMapping(this.nodes, (id, _entry) => {
|
nodeColor = new ReactiveMapping(this.nodeIdToNode, (id, _entry) => {
|
||||||
const index = this.nodeMainSuggestion.lookup(id)?.groupIndex
|
const index = this.nodeMainSuggestion.lookup(id)?.groupIndex
|
||||||
const group = tryGetIndex(this.groups.value, index)
|
const group = tryGetIndex(this.groups.value, index)
|
||||||
if (group == null) {
|
if (group == null) {
|
||||||
@ -68,24 +69,12 @@ export class GraphDb {
|
|||||||
return groupColorStyle(group)
|
return groupColorStyle(group)
|
||||||
})
|
})
|
||||||
|
|
||||||
getNode(id: ExprId): Node | undefined {
|
getExpressionNodeId(exprId: ExprId): ExprId | undefined {
|
||||||
return this.nodes.get(id)
|
return set.first(this.nodeIdToExprId.reverseLookup(exprId))
|
||||||
}
|
|
||||||
|
|
||||||
allNodes(): IterableIterator<[ExprId, Node]> {
|
|
||||||
return this.nodes.entries()
|
|
||||||
}
|
|
||||||
|
|
||||||
allNodeIds(): IterableIterator<ExprId> {
|
|
||||||
return this.nodes.keys()
|
|
||||||
}
|
|
||||||
|
|
||||||
getExpressionNodeId(exprId: ExprId | undefined): ExprId | undefined {
|
|
||||||
return exprId && set.first(this.nodeExpressions.reverseLookup(exprId))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getIdentDefiningNode(ident: string): ExprId | undefined {
|
getIdentDefiningNode(ident: string): ExprId | undefined {
|
||||||
return set.first(this.nodeByBinding.lookup(ident))
|
return set.first(this.bindingToNodeId.lookup(ident))
|
||||||
}
|
}
|
||||||
|
|
||||||
getExpressionInfo(id: ExprId): ExpressionInfo | undefined {
|
getExpressionInfo(id: ExprId): ExpressionInfo | undefined {
|
||||||
@ -102,17 +91,18 @@ export class GraphDb {
|
|||||||
const methodCall = this.getExpressionInfo(id)?.methodCall
|
const methodCall = this.getExpressionInfo(id)?.methodCall
|
||||||
if (methodCall == null) return
|
if (methodCall == null) return
|
||||||
const suggestionId = this.suggestionDb.findByMethodPointer(methodCall.methodPointer)
|
const suggestionId = this.suggestionDb.findByMethodPointer(methodCall.methodPointer)
|
||||||
|
if (suggestionId == null) return
|
||||||
const suggestion = this.suggestionDb.get(suggestionId)
|
const suggestion = this.suggestionDb.get(suggestionId)
|
||||||
if (suggestion == null) return
|
if (suggestion == null) return
|
||||||
return { methodCall, suggestion }
|
return { methodCall, suggestion }
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeColorStyle(id: ExprId): string {
|
getNodeColorStyle(id: ExprId): string {
|
||||||
return (id && this.nodeColors.lookup(id)) ?? 'var(--node-color-no-type)'
|
return this.nodeColor.lookup(id) ?? 'var(--node-color-no-type)'
|
||||||
}
|
}
|
||||||
|
|
||||||
moveNodeToTop(id: ExprId) {
|
moveNodeToTop(id: ExprId) {
|
||||||
this.nodes.moveToLast(id)
|
this.nodeIdToNode.moveToLast(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
getNodeWidth(node: Node) {
|
getNodeWidth(node: Node) {
|
||||||
@ -133,11 +123,11 @@ export class GraphDb {
|
|||||||
for (const nodeAst of functionAst.visit(getFunctionNodeExpressions)) {
|
for (const nodeAst of functionAst.visit(getFunctionNodeExpressions)) {
|
||||||
const newNode = nodeFromAst(nodeAst)
|
const newNode = nodeFromAst(nodeAst)
|
||||||
const nodeId = newNode.rootSpan.astId
|
const nodeId = newNode.rootSpan.astId
|
||||||
const node = this.nodes.get(nodeId)
|
const node = this.nodeIdToNode.get(nodeId)
|
||||||
const nodeMeta = getMeta(nodeId)
|
const nodeMeta = getMeta(nodeId)
|
||||||
currentNodeIds.add(nodeId)
|
currentNodeIds.add(nodeId)
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
this.nodes.set(nodeId, newNode)
|
this.nodeIdToNode.set(nodeId, newNode)
|
||||||
} else {
|
} else {
|
||||||
if (node.binding !== newNode.binding) {
|
if (node.binding !== newNode.binding) {
|
||||||
node.binding = newNode.binding
|
node.binding = newNode.binding
|
||||||
@ -170,9 +160,9 @@ export class GraphDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const nodeId of this.allNodeIds()) {
|
for (const nodeId of this.nodeIdToNode.keys()) {
|
||||||
if (!currentNodeIds.has(nodeId)) {
|
if (!currentNodeIds.has(nodeId)) {
|
||||||
this.nodes.delete(nodeId)
|
this.nodeIdToNode.delete(nodeId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,10 +181,10 @@ export class GraphDb {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
let nodeIndex = 0
|
let nodeIndex = 0
|
||||||
for (const nodeId of this.allNodeIds()) {
|
for (const nodeId of this.nodeIdToNode.keys()) {
|
||||||
const meta = getMeta(nodeId)
|
const meta = getMeta(nodeId)
|
||||||
if (meta) continue
|
if (meta) continue
|
||||||
const node = this.nodes.get(nodeId)!
|
const node = this.nodeIdToNode.get(nodeId)!
|
||||||
const size = new Vec2(this.getNodeWidth(node), theme.node.height)
|
const size = new Vec2(this.getNodeWidth(node), theme.node.height)
|
||||||
const position = new Vec2(
|
const position = new Vec2(
|
||||||
rectsPosition.x,
|
rectsPosition.x,
|
||||||
|
@ -109,7 +109,7 @@ export const useGraphStore = defineStore('graph', () => {
|
|||||||
for (const [id, op] of event.changes.keys) {
|
for (const [id, op] of event.changes.keys) {
|
||||||
if (op.action === 'update' || op.action === 'add') {
|
if (op.action === 'update' || op.action === 'add') {
|
||||||
const data = meta.get(id)
|
const data = meta.get(id)
|
||||||
const node = db.getNode(id as ExprId)
|
const node = db.nodeIdToNode.get(id as ExprId)
|
||||||
if (data != null && node != null) {
|
if (data != null && node != null) {
|
||||||
db.assignUpdatedMetadata(node, data)
|
db.assignUpdatedMetadata(node, data)
|
||||||
}
|
}
|
||||||
@ -121,14 +121,14 @@ export const useGraphStore = defineStore('graph', () => {
|
|||||||
let ident: string
|
let ident: string
|
||||||
do {
|
do {
|
||||||
ident = randomString()
|
ident = randomString()
|
||||||
} while (db.idents.hasValue(ident))
|
} while (db.nodeIdToBinding.hasValue(ident))
|
||||||
return ident
|
return ident
|
||||||
}
|
}
|
||||||
|
|
||||||
const edges = computed(() => {
|
const edges = computed(() => {
|
||||||
const disconnectedEdgeTarget = unconnectedEdge.value?.disconnectedEdgeTarget
|
const disconnectedEdgeTarget = unconnectedEdge.value?.disconnectedEdgeTarget
|
||||||
const edges = []
|
const edges = []
|
||||||
for (const [target, sources] of db.connections.allReverse()) {
|
for (const [target, sources] of db.sourceIdToTargetId.allReverse()) {
|
||||||
if (target === disconnectedEdgeTarget) continue
|
if (target === disconnectedEdgeTarget) continue
|
||||||
for (const source of sources) {
|
for (const source of sources) {
|
||||||
edges.push({ source, target })
|
edges.push({ source, target })
|
||||||
@ -177,13 +177,13 @@ export const useGraphStore = defineStore('graph', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function deleteNode(id: ExprId) {
|
function deleteNode(id: ExprId) {
|
||||||
const node = db.getNode(id)
|
const node = db.nodeIdToNode.get(id)
|
||||||
if (node == null) return
|
if (node == null) return
|
||||||
proj.module?.deleteExpression(node.outerExprId)
|
proj.module?.deleteExpression(node.outerExprId)
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNodeContent(id: ExprId, content: string) {
|
function setNodeContent(id: ExprId, content: string) {
|
||||||
const node = db.getNode(id)
|
const node = db.nodeIdToNode.get(id)
|
||||||
if (node == null) return
|
if (node == null) return
|
||||||
setExpressionContent(node.rootSpan.astId, content)
|
setExpressionContent(node.rootSpan.astId, content)
|
||||||
}
|
}
|
||||||
@ -201,13 +201,13 @@ export const useGraphStore = defineStore('graph', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function replaceNodeSubexpression(nodeId: ExprId, range: ContentRange, content: string) {
|
function replaceNodeSubexpression(nodeId: ExprId, range: ContentRange, content: string) {
|
||||||
const node = db.getNode(nodeId)
|
const node = db.nodeIdToNode.get(nodeId)
|
||||||
if (node == null) return
|
if (node == null) return
|
||||||
proj.module?.replaceExpressionContent(node.rootSpan.astId, content, range)
|
proj.module?.replaceExpressionContent(node.rootSpan.astId, content, range)
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNodePosition(nodeId: ExprId, position: Vec2) {
|
function setNodePosition(nodeId: ExprId, position: Vec2) {
|
||||||
const node = db.getNode(nodeId)
|
const node = db.nodeIdToNode.get(nodeId)
|
||||||
if (node == null) return
|
if (node == null) return
|
||||||
proj.module?.updateNodeMetadata(nodeId, { x: position.x, y: -position.y })
|
proj.module?.updateNodeMetadata(nodeId, { x: position.x, y: -position.y })
|
||||||
}
|
}
|
||||||
@ -231,13 +231,13 @@ export const useGraphStore = defineStore('graph', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setNodeVisualizationId(nodeId: ExprId, vis: Opt<VisualizationIdentifier>) {
|
function setNodeVisualizationId(nodeId: ExprId, vis: Opt<VisualizationIdentifier>) {
|
||||||
const node = db.getNode(nodeId)
|
const node = db.nodeIdToNode.get(nodeId)
|
||||||
if (node == null) return
|
if (node == null) return
|
||||||
proj.module?.updateNodeMetadata(nodeId, { vis: normalizeVisMetadata(vis, node.vis?.visible) })
|
proj.module?.updateNodeMetadata(nodeId, { vis: normalizeVisMetadata(vis, node.vis?.visible) })
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNodeVisualizationVisible(nodeId: ExprId, visible: boolean) {
|
function setNodeVisualizationVisible(nodeId: ExprId, visible: boolean) {
|
||||||
const node = db.getNode(nodeId)
|
const node = db.nodeIdToNode.get(nodeId)
|
||||||
if (node == null) return
|
if (node == null) return
|
||||||
proj.module?.updateNodeMetadata(nodeId, { vis: normalizeVisMetadata(node.vis, visible) })
|
proj.module?.updateNodeMetadata(nodeId, { vis: normalizeVisMetadata(node.vis, visible) })
|
||||||
}
|
}
|
||||||
@ -269,7 +269,7 @@ export const useGraphStore = defineStore('graph', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getNodeBinding(id: ExprId): string {
|
function getNodeBinding(id: ExprId): string {
|
||||||
return db.nodes.get(id)?.binding ?? ''
|
return db.nodeIdToNode.get(id)?.binding ?? ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -42,22 +42,22 @@ test('Parent-children indexing', () => {
|
|||||||
const db = new SuggestionDb()
|
const db = new SuggestionDb()
|
||||||
applyUpdates(db, test.addUpdatesForExpected(), test.groups)
|
applyUpdates(db, test.addUpdatesForExpected(), test.groups)
|
||||||
// Parent lookup.
|
// Parent lookup.
|
||||||
expect(db.parent.lookup(1)).toEqual(new Set([]))
|
expect(db.childIdToParentId.lookup(1)).toEqual(new Set([]))
|
||||||
expect(db.parent.lookup(2)).toEqual(new Set([1]))
|
expect(db.childIdToParentId.lookup(2)).toEqual(new Set([1]))
|
||||||
expect(db.parent.lookup(3)).toEqual(new Set([2]))
|
expect(db.childIdToParentId.lookup(3)).toEqual(new Set([2]))
|
||||||
expect(db.parent.lookup(4)).toEqual(new Set([2]))
|
expect(db.childIdToParentId.lookup(4)).toEqual(new Set([2]))
|
||||||
expect(db.parent.lookup(5)).toEqual(new Set([2]))
|
expect(db.childIdToParentId.lookup(5)).toEqual(new Set([2]))
|
||||||
expect(db.parent.lookup(6)).toEqual(new Set([1]))
|
expect(db.childIdToParentId.lookup(6)).toEqual(new Set([1]))
|
||||||
expect(db.parent.lookup(7)).toEqual(new Set([1]))
|
expect(db.childIdToParentId.lookup(7)).toEqual(new Set([1]))
|
||||||
|
|
||||||
// Children lookup.
|
// Children lookup.
|
||||||
expect(db.parent.reverseLookup(1)).toEqual(new Set([2, 6, 7]))
|
expect(db.childIdToParentId.reverseLookup(1)).toEqual(new Set([2, 6, 7]))
|
||||||
expect(db.parent.reverseLookup(2)).toEqual(new Set([3, 4, 5]))
|
expect(db.childIdToParentId.reverseLookup(2)).toEqual(new Set([3, 4, 5]))
|
||||||
expect(db.parent.reverseLookup(3)).toEqual(new Set([]))
|
expect(db.childIdToParentId.reverseLookup(3)).toEqual(new Set([]))
|
||||||
expect(db.parent.reverseLookup(4)).toEqual(new Set([]))
|
expect(db.childIdToParentId.reverseLookup(4)).toEqual(new Set([]))
|
||||||
expect(db.parent.reverseLookup(5)).toEqual(new Set([]))
|
expect(db.childIdToParentId.reverseLookup(5)).toEqual(new Set([]))
|
||||||
expect(db.parent.reverseLookup(6)).toEqual(new Set([]))
|
expect(db.childIdToParentId.reverseLookup(6)).toEqual(new Set([]))
|
||||||
expect(db.parent.reverseLookup(7)).toEqual(new Set([]))
|
expect(db.childIdToParentId.reverseLookup(7)).toEqual(new Set([]))
|
||||||
|
|
||||||
// Add new entry.
|
// Add new entry.
|
||||||
const modifications: lsTypes.SuggestionsDatabaseUpdate[] = [
|
const modifications: lsTypes.SuggestionsDatabaseUpdate[] = [
|
||||||
@ -78,22 +78,22 @@ test('Parent-children indexing', () => {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
applyUpdates(db, modifications, test.groups)
|
applyUpdates(db, modifications, test.groups)
|
||||||
expect(db.parent.lookup(8)).toEqual(new Set([2]))
|
expect(db.childIdToParentId.lookup(8)).toEqual(new Set([2]))
|
||||||
expect(db.parent.reverseLookup(8)).toEqual(new Set([]))
|
expect(db.childIdToParentId.reverseLookup(8)).toEqual(new Set([]))
|
||||||
expect(db.parent.reverseLookup(2)).toEqual(new Set([3, 4, 5, 8]))
|
expect(db.childIdToParentId.reverseLookup(2)).toEqual(new Set([3, 4, 5, 8]))
|
||||||
|
|
||||||
// Remove entry.
|
// Remove entry.
|
||||||
const modifications2: lsTypes.SuggestionsDatabaseUpdate[] = [{ type: 'Remove', id: 3 }]
|
const modifications2: lsTypes.SuggestionsDatabaseUpdate[] = [{ type: 'Remove', id: 3 }]
|
||||||
applyUpdates(db, modifications2, test.groups)
|
applyUpdates(db, modifications2, test.groups)
|
||||||
expect(db.parent.lookup(3)).toEqual(new Set([]))
|
expect(db.childIdToParentId.lookup(3)).toEqual(new Set([]))
|
||||||
expect(db.parent.reverseLookup(2)).toEqual(new Set([4, 5, 8]))
|
expect(db.childIdToParentId.reverseLookup(2)).toEqual(new Set([4, 5, 8]))
|
||||||
|
|
||||||
// Modify entry. Moving new method from `Standard.Base.Type` to `Standard.Base`.
|
// Modify entry. Moving new method from `Standard.Base.Type` to `Standard.Base`.
|
||||||
db.get(8)!.memberOf = 'Standard.Base' as QualifiedName
|
db.get(8)!.memberOf = 'Standard.Base' as QualifiedName
|
||||||
expect(db.parent.reverseLookup(1)).toEqual(new Set([2, 6, 7, 8]))
|
expect(db.childIdToParentId.reverseLookup(1)).toEqual(new Set([2, 6, 7, 8]))
|
||||||
expect(db.parent.lookup(8)).toEqual(new Set([1]))
|
expect(db.childIdToParentId.lookup(8)).toEqual(new Set([1]))
|
||||||
expect(db.parent.reverseLookup(8)).toEqual(new Set([]))
|
expect(db.childIdToParentId.reverseLookup(8)).toEqual(new Set([]))
|
||||||
expect(db.parent.reverseLookup(2)).toEqual(new Set([4, 5]))
|
expect(db.childIdToParentId.reverseLookup(2)).toEqual(new Set([4, 5]))
|
||||||
})
|
})
|
||||||
|
|
||||||
test("Modifying suggestion entries' fields", () => {
|
test("Modifying suggestion entries' fields", () => {
|
||||||
|
@ -10,10 +10,9 @@ import { LanguageServer } from 'shared/languageServer'
|
|||||||
import type { MethodPointer } from 'shared/languageServerTypes'
|
import type { MethodPointer } from 'shared/languageServerTypes'
|
||||||
import { markRaw, ref, type Ref } from 'vue'
|
import { markRaw, ref, type Ref } from 'vue'
|
||||||
|
|
||||||
export class SuggestionDb {
|
export class SuggestionDb extends ReactiveDb<SuggestionId, SuggestionEntry> {
|
||||||
_internal = new ReactiveDb<SuggestionId, SuggestionEntry>()
|
nameToId = new ReactiveIndex(this, (id, entry) => [[entryQn(entry), id]])
|
||||||
nameToId = new ReactiveIndex(this._internal, (id, entry) => [[entryQn(entry), id]])
|
childIdToParentId = new ReactiveIndex(this, (id, entry) => {
|
||||||
parent = new ReactiveIndex(this._internal, (id, entry) => {
|
|
||||||
let qualifiedName: Opt<QualifiedName>
|
let qualifiedName: Opt<QualifiedName>
|
||||||
if (entry.memberOf) {
|
if (entry.memberOf) {
|
||||||
qualifiedName = entry.memberOf
|
qualifiedName = entry.memberOf
|
||||||
@ -27,19 +26,6 @@ export class SuggestionDb {
|
|||||||
return []
|
return []
|
||||||
})
|
})
|
||||||
|
|
||||||
set(id: SuggestionId, entry: SuggestionEntry): void {
|
|
||||||
this._internal.set(id, entry)
|
|
||||||
}
|
|
||||||
get(id: SuggestionId | null | undefined): SuggestionEntry | undefined {
|
|
||||||
return id != null ? this._internal.get(id) : undefined
|
|
||||||
}
|
|
||||||
delete(id: SuggestionId): boolean {
|
|
||||||
return this._internal.delete(id)
|
|
||||||
}
|
|
||||||
entries(): IterableIterator<[SuggestionId, SuggestionEntry]> {
|
|
||||||
return this._internal.entries()
|
|
||||||
}
|
|
||||||
|
|
||||||
findByMethodPointer(method: MethodPointer): SuggestionId | undefined {
|
findByMethodPointer(method: MethodPointer): SuggestionId | undefined {
|
||||||
if (method == null) return
|
if (method == null) return
|
||||||
const moduleName = tryQualifiedName(method.definedOnType)
|
const moduleName = tryQualifiedName(method.definedOnType)
|
||||||
|
@ -36,18 +36,18 @@ test('metadata index', () => {
|
|||||||
const b = toVisualizationId({ module: { kind: 'Builtin' }, name: 'b' })
|
const b = toVisualizationId({ module: { kind: 'Builtin' }, name: 'b' })
|
||||||
db.set(a, { name: 'a', inputType: 'B | C' })
|
db.set(a, { name: 'a', inputType: 'B | C' })
|
||||||
db.set(b, { name: 'b', inputType: 'C | D | E' })
|
db.set(b, { name: 'b', inputType: 'C | D | E' })
|
||||||
expect(db.types.lookup(a)).toEqual(new Set(['B', 'C']))
|
expect(db.visualizationIdToType.lookup(a)).toEqual(new Set(['B', 'C']))
|
||||||
expect(db.types.lookup(b)).toEqual(new Set(['C', 'D', 'E']))
|
expect(db.visualizationIdToType.lookup(b)).toEqual(new Set(['C', 'D', 'E']))
|
||||||
expect(db.types.reverseLookup('B')).toEqual(new Set([a]))
|
expect(db.visualizationIdToType.reverseLookup('B')).toEqual(new Set([a]))
|
||||||
expect(db.types.reverseLookup('C')).toEqual(new Set([a, b]))
|
expect(db.visualizationIdToType.reverseLookup('C')).toEqual(new Set([a, b]))
|
||||||
expect(db.types.reverseLookup('D')).toEqual(new Set([b]))
|
expect(db.visualizationIdToType.reverseLookup('D')).toEqual(new Set([b]))
|
||||||
expect(db.types.reverseLookup('E')).toEqual(new Set([b]))
|
expect(db.visualizationIdToType.reverseLookup('E')).toEqual(new Set([b]))
|
||||||
|
|
||||||
db.delete(b)
|
db.delete(b)
|
||||||
expect(db.types.lookup(a)).toEqual(new Set(['B', 'C']))
|
expect(db.visualizationIdToType.lookup(a)).toEqual(new Set(['B', 'C']))
|
||||||
expect(db.types.lookup(b)).toEqual(new Set())
|
expect(db.visualizationIdToType.lookup(b)).toEqual(new Set())
|
||||||
expect(db.types.reverseLookup('B')).toEqual(new Set([a]))
|
expect(db.visualizationIdToType.reverseLookup('B')).toEqual(new Set([a]))
|
||||||
expect(db.types.reverseLookup('C')).toEqual(new Set([a]))
|
expect(db.visualizationIdToType.reverseLookup('C')).toEqual(new Set([a]))
|
||||||
expect(db.types.reverseLookup('D')).toEqual(new Set())
|
expect(db.visualizationIdToType.reverseLookup('D')).toEqual(new Set())
|
||||||
expect(db.types.reverseLookup('E')).toEqual(new Set())
|
expect(db.visualizationIdToType.reverseLookup('E')).toEqual(new Set())
|
||||||
})
|
})
|
||||||
|
@ -224,8 +224,8 @@ export const useVisualizationStore = defineStore('visualization', () => {
|
|||||||
type == null
|
type == null
|
||||||
? metadata.keys()
|
? metadata.keys()
|
||||||
: new Set([
|
: new Set([
|
||||||
...(metadata.types.reverseLookup(type) ?? []),
|
...(metadata.visualizationIdToType.reverseLookup(type) ?? []),
|
||||||
...(metadata.types.reverseLookup('Any') ?? []),
|
...(metadata.visualizationIdToType.reverseLookup('Any') ?? []),
|
||||||
])
|
])
|
||||||
for (const type of types) yield fromVisualizationId(type)
|
for (const type of types) yield fromVisualizationId(type)
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ export function fromVisualizationId(key: VisualizationId): VisualizationIdentifi
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class VisualizationMetadataDb extends ReactiveDb<VisualizationId, VisualizationMetadata> {
|
export class VisualizationMetadataDb extends ReactiveDb<VisualizationId, VisualizationMetadata> {
|
||||||
types = new ReactiveIndex(this, (key, metadata) =>
|
visualizationIdToType = new ReactiveIndex(this, (key, metadata) =>
|
||||||
getTypesFromUnion(metadata.inputType).map((type) => [key, type]),
|
getTypesFromUnion(metadata.inputType).map((type) => [key, type]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -266,7 +266,7 @@ export class ReactiveIndex<K, V, IK, IV> {
|
|||||||
export type Mapper<K, V, IV> = (key: K, value: V) => IV | undefined
|
export type Mapper<K, V, IV> = (key: K, value: V) => IV | undefined
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A one-to-one mapping for values in a {@link ReactiveDb} instance. Allows only one value per key.
|
* A one-to-one mapping for values in a {@link ReactiveDb} instance. Allows only one value per key.
|
||||||
* It can be thought of as a collection of `computed` values per each key in the `ReactiveDb`. The
|
* It can be thought of as a collection of `computed` values per each key in the `ReactiveDb`. The
|
||||||
* mapping is automatically updated when any of its dependencies change, and is properly cleaned up
|
* mapping is automatically updated when any of its dependencies change, and is properly cleaned up
|
||||||
* when any key is removed from {@link ReactiveDb}. Only accessed keys are ever actually computed.
|
* when any key is removed from {@link ReactiveDb}. Only accessed keys are ever actually computed.
|
||||||
|
Loading…
Reference in New Issue
Block a user