diff --git a/d2ir/compile.go b/d2ir/compile.go
index 3ae17822f..8dfe1765a 100644
--- a/d2ir/compile.go
+++ b/d2ir/compile.go
@@ -1,6 +1,7 @@
package d2ir
import (
+ "fmt"
"html"
"io/fs"
"net/url"
@@ -43,10 +44,10 @@ type compiler struct {
// Used to prevent field globs causing infinite loops.
globRefContextStack []*RefContext
// Used to check whether ampersands are allowed in the current map.
- mapRefContextStack []*RefContext
- lazyGlobBeingApplied bool
- markedFieldsForDeletion map[*Map]map[string]struct{}
- markedEdgesForDeletion map[*Map][]*EdgeID
+ mapRefContextStack []*RefContext
+ lazyGlobBeingApplied bool
+ deletedFieldsPool map[*Map]map[string]*Field
+ deletedEdgesPool map[*Map]map[string]*Edge
}
type CompileOptions struct {
@@ -67,10 +68,10 @@ func Compile(ast *d2ast.Map, opts *CompileOptions) (*Map, []string, error) {
err: &d2parser.ParseError{},
fs: opts.FS,
- seenImports: make(map[string]struct{}),
- utf16Pos: opts.UTF16Pos,
- markedFieldsForDeletion: make(map[*Map]map[string]struct{}),
- markedEdgesForDeletion: make(map[*Map][]*EdgeID),
+ seenImports: make(map[string]struct{}),
+ utf16Pos: opts.UTF16Pos,
+ deletedFieldsPool: make(map[*Map]map[string]*Field),
+ deletedEdgesPool: make(map[*Map]map[string]*Edge),
}
m := &Map{}
m.initRoot()
@@ -85,7 +86,6 @@ func Compile(ast *d2ast.Map, opts *CompileOptions) (*Map, []string, error) {
c.compileMap(m, ast, ast)
c.compileSubstitutions(m, nil)
c.overlayClasses(m)
- c.processMarkedDeletions(m)
if !c.err.Empty() {
return nil, nil, c.err
@@ -868,14 +868,30 @@ func (c *compiler) _compileField(f *Field, refctx *RefContext) {
// For vars, if we delete the field, it may just resolve to an outer scope var of the same name
// Instead we keep it around, so that resolveSubstitutions can find it
if !IsVar(ParentMap(f)) {
- c.markFieldForDeletion(ParentMap(f), f.Name.ScalarString())
+ parentMap := ParentMap(f)
+ key := f.Name.ScalarString()
+
+ // Save to pool before deletion
+ c.saveDeletedField(parentMap, key, f)
+
+ // Now delete
+ parentMap.DeleteField(key)
}
}
return
}
if len(refctx.Key.Edges) == 0 && (refctx.Key.Primary.NotNull != nil || refctx.Key.Value.NotNull != nil) {
- c.unmarkFieldForDeletion(ParentMap(f), f.Name.ScalarString())
+ println("\033[1;31m--- DEBUG:", "=======================", "\033[m")
+ parentMap := ParentMap(f)
+ key := f.Name.ScalarString()
+
+ // Try to restore from pool
+ restoredField := c.restoreDeletedField(parentMap, key)
+ if restoredField != nil {
+ // Add the field back to the parent map
+ parentMap.Fields = append(parentMap.Fields, restoredField)
+ }
return
}
@@ -1157,12 +1173,23 @@ func (c *compiler) _compileEdges(refctx *RefContext) {
for i, eid := range eida {
if !eid.Glob && (refctx.Key.Primary.Null != nil || refctx.Key.Value.Null != nil) {
if !c.lazyGlobBeingApplied {
- c.markEdgeForDeletion(refctx.ScopeMap, eid)
+ edges := refctx.ScopeMap.GetEdges(eid, nil, nil)
+ if len(edges) > 0 {
+ // Save to pool before deletion
+ c.saveDeletedEdge(refctx.ScopeMap, eid, edges[0])
+ }
+
+ // Now delete
+ refctx.ScopeMap.DeleteEdge(eid)
}
continue
}
if !eid.Glob && (refctx.Key.Primary.NotNull != nil || refctx.Key.Value.NotNull != nil) {
- c.unmarkEdgeForDeletion(refctx.ScopeMap, eid)
+ restoredEdge := c.restoreDeletedEdge(refctx.ScopeMap, eid)
+ if restoredEdge != nil {
+ // Add the edge back to the scope map
+ refctx.ScopeMap.Edges = append(refctx.ScopeMap.Edges, restoredEdge)
+ }
continue
}
@@ -1311,50 +1338,93 @@ func (c *compiler) compileArray(dst *Array, a *d2ast.Array, scopeAST *d2ast.Map)
}
}
-func (c *compiler) markFieldForDeletion(parent *Map, key string) {
- if c.markedFieldsForDeletion[parent] == nil {
- c.markedFieldsForDeletion[parent] = make(map[string]struct{})
+func (c *compiler) saveDeletedField(parent *Map, key string, field *Field) {
+ fmt.Printf("DEBUG: Saving deleted field: %s in map %p\n", key, parent)
+ if c.deletedFieldsPool == nil {
+ c.deletedFieldsPool = make(map[*Map]map[string]*Field)
}
- c.markedFieldsForDeletion[parent][key] = struct{}{}
+ if c.deletedFieldsPool[parent] == nil {
+ c.deletedFieldsPool[parent] = make(map[string]*Field)
+ }
+ c.deletedFieldsPool[parent][key] = field
}
-func (c *compiler) unmarkFieldForDeletion(parent *Map, key string) {
- if c.markedFieldsForDeletion[parent] != nil {
- delete(c.markedFieldsForDeletion[parent], key)
- }
-}
-
-func (c *compiler) markEdgeForDeletion(parent *Map, eid *EdgeID) {
- c.markedEdgesForDeletion[parent] = append(c.markedEdgesForDeletion[parent], eid.Copy())
-}
-
-func (c *compiler) unmarkEdgeForDeletion(parent *Map, eid *EdgeID) {
- edges := c.markedEdgesForDeletion[parent]
- for i, e := range edges {
- if e.Match(eid) {
- // Remove this edge from the slice
- c.markedEdgesForDeletion[parent] = append(edges[:i], edges[i+1:]...)
- break
- }
- }
-}
-
-func (c *compiler) processMarkedDeletions(m *Map) {
- // Process field deletions
- for parent, keys := range c.markedFieldsForDeletion {
- for key := range keys {
- parent.DeleteField(key)
- }
+func (c *compiler) restoreDeletedField(parent *Map, key string) *Field {
+ if c.deletedFieldsPool == nil || c.deletedFieldsPool[parent] == nil {
+ return nil
}
- // Process edge deletions
- for parent, edges := range c.markedEdgesForDeletion {
- for _, eid := range edges {
- parent.DeleteEdge(eid)
- }
+ field, ok := c.deletedFieldsPool[parent][key]
+ if !ok {
+ return nil
}
- // Clear the tracking maps
- c.markedFieldsForDeletion = make(map[*Map]map[string]struct{})
- c.markedEdgesForDeletion = make(map[*Map][]*EdgeID)
+ fmt.Printf("DEBUG: Restoring deleted field: %s in map %p\n", key, parent)
+ delete(c.deletedFieldsPool[parent], key)
+ return field
+}
+
+func (c *compiler) saveDeletedEdge(parent *Map, eid *EdgeID, edge *Edge) {
+ fmt.Printf("DEBUG: Saving deleted edge in map %p\n", parent)
+ if c.deletedEdgesPool == nil {
+ c.deletedEdgesPool = make(map[*Map]map[string]*Edge)
+ }
+ if c.deletedEdgesPool[parent] == nil {
+ c.deletedEdgesPool[parent] = make(map[string]*Edge)
+ }
+
+ // Create a string key for the edge
+ var key string
+ if eid.SrcArrow {
+ key = "<-"
+ }
+ key += strings.Join(stringifyPath(eid.SrcPath), ".")
+ key += "->"
+ if eid.DstArrow {
+ key += ">"
+ }
+ key += strings.Join(stringifyPath(eid.DstPath), ".")
+ if eid.Index != nil {
+ key += fmt.Sprintf("[%d]", *eid.Index)
+ }
+
+ c.deletedEdgesPool[parent][key] = edge
+}
+
+func stringifyPath(path []d2ast.String) []string {
+ result := make([]string, len(path))
+ for i, s := range path {
+ result[i] = s.ScalarString()
+ }
+ return result
+}
+
+func (c *compiler) restoreDeletedEdge(parent *Map, eid *EdgeID) *Edge {
+ if c.deletedEdgesPool == nil || c.deletedEdgesPool[parent] == nil {
+ return nil
+ }
+
+ // Create a string key for the edge
+ var key string
+ if eid.SrcArrow {
+ key = "<-"
+ }
+ key += strings.Join(stringifyPath(eid.SrcPath), ".")
+ key += "->"
+ if eid.DstArrow {
+ key += ">"
+ }
+ key += strings.Join(stringifyPath(eid.DstPath), ".")
+ if eid.Index != nil {
+ key += fmt.Sprintf("[%d]", *eid.Index)
+ }
+
+ edge, ok := c.deletedEdgesPool[parent][key]
+ if !ok {
+ return nil
+ }
+
+ fmt.Printf("DEBUG: Restoring deleted edge in map %p\n", parent)
+ delete(c.deletedEdgesPool[parent], key)
+ return edge
}
diff --git a/e2etests/testdata/txtar/model-nulling/dagre/board.exp.json b/e2etests/testdata/txtar/model-nulling/dagre/board.exp.json
index 163137c4c..7fd786ea8 100644
--- a/e2etests/testdata/txtar/model-nulling/dagre/board.exp.json
+++ b/e2etests/testdata/txtar/model-nulling/dagre/board.exp.json
@@ -8,136 +8,9 @@
"center": null,
"layoutEngine": null
},
- "isFolderOnly": false,
+ "isFolderOnly": true,
"fontFamily": "SourceSansPro",
- "shapes": [
- {
- "id": "user",
- "type": "rectangle",
- "pos": {
- "x": 0,
- "y": 0
- },
- "width": 77,
- "height": 66,
- "opacity": 1,
- "strokeDash": 0,
- "strokeWidth": 2,
- "borderRadius": 0,
- "fill": "blue",
- "stroke": "B1",
- "animated": false,
- "shadow": false,
- "3d": false,
- "multiple": false,
- "double-border": false,
- "tooltip": "",
- "link": "",
- "icon": null,
- "iconPosition": "",
- "blend": false,
- "fields": null,
- "methods": null,
- "columns": null,
- "label": "user",
- "fontSize": 16,
- "fontFamily": "DEFAULT",
- "language": "",
- "color": "N1",
- "italic": false,
- "bold": true,
- "underline": false,
- "labelWidth": 32,
- "labelHeight": 21,
- "labelPosition": "INSIDE_MIDDLE_CENTER",
- "zIndex": 0,
- "level": 1
- },
- {
- "id": "softwareSystem",
- "type": "rectangle",
- "pos": {
- "x": 137,
- "y": 0
- },
- "width": 160,
- "height": 66,
- "opacity": 1,
- "strokeDash": 0,
- "strokeWidth": 2,
- "borderRadius": 0,
- "fill": "B6",
- "stroke": "B1",
- "animated": false,
- "shadow": false,
- "3d": false,
- "multiple": false,
- "double-border": false,
- "tooltip": "",
- "link": "",
- "icon": null,
- "iconPosition": "",
- "blend": false,
- "fields": null,
- "methods": null,
- "columns": null,
- "label": "softwareSystem",
- "fontSize": 16,
- "fontFamily": "DEFAULT",
- "language": "",
- "color": "N1",
- "italic": false,
- "bold": true,
- "underline": false,
- "labelWidth": 115,
- "labelHeight": 21,
- "labelPosition": "INSIDE_MIDDLE_CENTER",
- "zIndex": 0,
- "level": 1
- },
- {
- "id": "externalSystem",
- "type": "rectangle",
- "pos": {
- "x": 357,
- "y": 0
- },
- "width": 157,
- "height": 66,
- "opacity": 1,
- "strokeDash": 0,
- "strokeWidth": 2,
- "borderRadius": 0,
- "fill": "B6",
- "stroke": "B1",
- "animated": false,
- "shadow": false,
- "3d": false,
- "multiple": false,
- "double-border": false,
- "tooltip": "",
- "link": "",
- "icon": null,
- "iconPosition": "",
- "blend": false,
- "fields": null,
- "methods": null,
- "columns": null,
- "label": "externalSystem",
- "fontSize": 16,
- "fontFamily": "DEFAULT",
- "language": "",
- "color": "N1",
- "italic": false,
- "bold": true,
- "underline": false,
- "labelWidth": 112,
- "labelHeight": 21,
- "labelPosition": "INSIDE_MIDDLE_CENTER",
- "zIndex": 0,
- "level": 1
- }
- ],
+ "shapes": [],
"connections": [],
"root": {
"id": "",
diff --git a/e2etests/testdata/txtar/model-nulling/dagre/sketch.exp.svg b/e2etests/testdata/txtar/model-nulling/dagre/sketch.exp.svg
index 6497c3fbd..eeaf68f0a 100644
--- a/e2etests/testdata/txtar/model-nulling/dagre/sketch.exp.svg
+++ b/e2etests/testdata/txtar/model-nulling/dagre/sketch.exp.svg
@@ -1,11 +1,4 @@
-
\ No newline at end of file
+ .d2-3003375094 .fill-N1{fill:#0A0F25;}
+ .d2-3003375094 .fill-N2{fill:#676C7E;}
+ .d2-3003375094 .fill-N3{fill:#9499AB;}
+ .d2-3003375094 .fill-N4{fill:#CFD2DD;}
+ .d2-3003375094 .fill-N5{fill:#DEE1EB;}
+ .d2-3003375094 .fill-N6{fill:#EEF1F8;}
+ .d2-3003375094 .fill-N7{fill:#FFFFFF;}
+ .d2-3003375094 .fill-B1{fill:#0D32B2;}
+ .d2-3003375094 .fill-B2{fill:#0D32B2;}
+ .d2-3003375094 .fill-B3{fill:#E3E9FD;}
+ .d2-3003375094 .fill-B4{fill:#E3E9FD;}
+ .d2-3003375094 .fill-B5{fill:#EDF0FD;}
+ .d2-3003375094 .fill-B6{fill:#F7F8FE;}
+ .d2-3003375094 .fill-AA2{fill:#4A6FF3;}
+ .d2-3003375094 .fill-AA4{fill:#EDF0FD;}
+ .d2-3003375094 .fill-AA5{fill:#F7F8FE;}
+ .d2-3003375094 .fill-AB4{fill:#EDF0FD;}
+ .d2-3003375094 .fill-AB5{fill:#F7F8FE;}
+ .d2-3003375094 .stroke-N1{stroke:#0A0F25;}
+ .d2-3003375094 .stroke-N2{stroke:#676C7E;}
+ .d2-3003375094 .stroke-N3{stroke:#9499AB;}
+ .d2-3003375094 .stroke-N4{stroke:#CFD2DD;}
+ .d2-3003375094 .stroke-N5{stroke:#DEE1EB;}
+ .d2-3003375094 .stroke-N6{stroke:#EEF1F8;}
+ .d2-3003375094 .stroke-N7{stroke:#FFFFFF;}
+ .d2-3003375094 .stroke-B1{stroke:#0D32B2;}
+ .d2-3003375094 .stroke-B2{stroke:#0D32B2;}
+ .d2-3003375094 .stroke-B3{stroke:#E3E9FD;}
+ .d2-3003375094 .stroke-B4{stroke:#E3E9FD;}
+ .d2-3003375094 .stroke-B5{stroke:#EDF0FD;}
+ .d2-3003375094 .stroke-B6{stroke:#F7F8FE;}
+ .d2-3003375094 .stroke-AA2{stroke:#4A6FF3;}
+ .d2-3003375094 .stroke-AA4{stroke:#EDF0FD;}
+ .d2-3003375094 .stroke-AA5{stroke:#F7F8FE;}
+ .d2-3003375094 .stroke-AB4{stroke:#EDF0FD;}
+ .d2-3003375094 .stroke-AB5{stroke:#F7F8FE;}
+ .d2-3003375094 .background-color-N1{background-color:#0A0F25;}
+ .d2-3003375094 .background-color-N2{background-color:#676C7E;}
+ .d2-3003375094 .background-color-N3{background-color:#9499AB;}
+ .d2-3003375094 .background-color-N4{background-color:#CFD2DD;}
+ .d2-3003375094 .background-color-N5{background-color:#DEE1EB;}
+ .d2-3003375094 .background-color-N6{background-color:#EEF1F8;}
+ .d2-3003375094 .background-color-N7{background-color:#FFFFFF;}
+ .d2-3003375094 .background-color-B1{background-color:#0D32B2;}
+ .d2-3003375094 .background-color-B2{background-color:#0D32B2;}
+ .d2-3003375094 .background-color-B3{background-color:#E3E9FD;}
+ .d2-3003375094 .background-color-B4{background-color:#E3E9FD;}
+ .d2-3003375094 .background-color-B5{background-color:#EDF0FD;}
+ .d2-3003375094 .background-color-B6{background-color:#F7F8FE;}
+ .d2-3003375094 .background-color-AA2{background-color:#4A6FF3;}
+ .d2-3003375094 .background-color-AA4{background-color:#EDF0FD;}
+ .d2-3003375094 .background-color-AA5{background-color:#F7F8FE;}
+ .d2-3003375094 .background-color-AB4{background-color:#EDF0FD;}
+ .d2-3003375094 .background-color-AB5{background-color:#F7F8FE;}
+ .d2-3003375094 .color-N1{color:#0A0F25;}
+ .d2-3003375094 .color-N2{color:#676C7E;}
+ .d2-3003375094 .color-N3{color:#9499AB;}
+ .d2-3003375094 .color-N4{color:#CFD2DD;}
+ .d2-3003375094 .color-N5{color:#DEE1EB;}
+ .d2-3003375094 .color-N6{color:#EEF1F8;}
+ .d2-3003375094 .color-N7{color:#FFFFFF;}
+ .d2-3003375094 .color-B1{color:#0D32B2;}
+ .d2-3003375094 .color-B2{color:#0D32B2;}
+ .d2-3003375094 .color-B3{color:#E3E9FD;}
+ .d2-3003375094 .color-B4{color:#E3E9FD;}
+ .d2-3003375094 .color-B5{color:#EDF0FD;}
+ .d2-3003375094 .color-B6{color:#F7F8FE;}
+ .d2-3003375094 .color-AA2{color:#4A6FF3;}
+ .d2-3003375094 .color-AA4{color:#EDF0FD;}
+ .d2-3003375094 .color-AA5{color:#F7F8FE;}
+ .d2-3003375094 .color-AB4{color:#EDF0FD;}
+ .d2-3003375094 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker-d2-3003375094);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker-d2-3003375094);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark-d2-3003375094);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker-d2-3003375094);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark-d2-3003375094);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal-d2-3003375094);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal-d2-3003375094);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>
\ No newline at end of file
diff --git a/e2etests/testdata/txtar/model-nulling/elk/board.exp.json b/e2etests/testdata/txtar/model-nulling/elk/board.exp.json
index 6c8f563a2..7fd786ea8 100644
--- a/e2etests/testdata/txtar/model-nulling/elk/board.exp.json
+++ b/e2etests/testdata/txtar/model-nulling/elk/board.exp.json
@@ -8,136 +8,9 @@
"center": null,
"layoutEngine": null
},
- "isFolderOnly": false,
+ "isFolderOnly": true,
"fontFamily": "SourceSansPro",
- "shapes": [
- {
- "id": "user",
- "type": "rectangle",
- "pos": {
- "x": 12,
- "y": 12
- },
- "width": 77,
- "height": 66,
- "opacity": 1,
- "strokeDash": 0,
- "strokeWidth": 2,
- "borderRadius": 0,
- "fill": "blue",
- "stroke": "B1",
- "animated": false,
- "shadow": false,
- "3d": false,
- "multiple": false,
- "double-border": false,
- "tooltip": "",
- "link": "",
- "icon": null,
- "iconPosition": "",
- "blend": false,
- "fields": null,
- "methods": null,
- "columns": null,
- "label": "user",
- "fontSize": 16,
- "fontFamily": "DEFAULT",
- "language": "",
- "color": "N1",
- "italic": false,
- "bold": true,
- "underline": false,
- "labelWidth": 32,
- "labelHeight": 21,
- "labelPosition": "INSIDE_MIDDLE_CENTER",
- "zIndex": 0,
- "level": 1
- },
- {
- "id": "softwareSystem",
- "type": "rectangle",
- "pos": {
- "x": 109,
- "y": 12
- },
- "width": 160,
- "height": 66,
- "opacity": 1,
- "strokeDash": 0,
- "strokeWidth": 2,
- "borderRadius": 0,
- "fill": "B6",
- "stroke": "B1",
- "animated": false,
- "shadow": false,
- "3d": false,
- "multiple": false,
- "double-border": false,
- "tooltip": "",
- "link": "",
- "icon": null,
- "iconPosition": "",
- "blend": false,
- "fields": null,
- "methods": null,
- "columns": null,
- "label": "softwareSystem",
- "fontSize": 16,
- "fontFamily": "DEFAULT",
- "language": "",
- "color": "N1",
- "italic": false,
- "bold": true,
- "underline": false,
- "labelWidth": 115,
- "labelHeight": 21,
- "labelPosition": "INSIDE_MIDDLE_CENTER",
- "zIndex": 0,
- "level": 1
- },
- {
- "id": "externalSystem",
- "type": "rectangle",
- "pos": {
- "x": 289,
- "y": 12
- },
- "width": 157,
- "height": 66,
- "opacity": 1,
- "strokeDash": 0,
- "strokeWidth": 2,
- "borderRadius": 0,
- "fill": "B6",
- "stroke": "B1",
- "animated": false,
- "shadow": false,
- "3d": false,
- "multiple": false,
- "double-border": false,
- "tooltip": "",
- "link": "",
- "icon": null,
- "iconPosition": "",
- "blend": false,
- "fields": null,
- "methods": null,
- "columns": null,
- "label": "externalSystem",
- "fontSize": 16,
- "fontFamily": "DEFAULT",
- "language": "",
- "color": "N1",
- "italic": false,
- "bold": true,
- "underline": false,
- "labelWidth": 112,
- "labelHeight": 21,
- "labelPosition": "INSIDE_MIDDLE_CENTER",
- "zIndex": 0,
- "level": 1
- }
- ],
+ "shapes": [],
"connections": [],
"root": {
"id": "",
diff --git a/e2etests/testdata/txtar/model-nulling/elk/sketch.exp.svg b/e2etests/testdata/txtar/model-nulling/elk/sketch.exp.svg
index 589759fb5..eeaf68f0a 100644
--- a/e2etests/testdata/txtar/model-nulling/elk/sketch.exp.svg
+++ b/e2etests/testdata/txtar/model-nulling/elk/sketch.exp.svg
@@ -1,11 +1,4 @@
-usersoftwareSystemexternalSystem
-
-
-
-
-
\ No newline at end of file
+ .d2-3003375094 .fill-N1{fill:#0A0F25;}
+ .d2-3003375094 .fill-N2{fill:#676C7E;}
+ .d2-3003375094 .fill-N3{fill:#9499AB;}
+ .d2-3003375094 .fill-N4{fill:#CFD2DD;}
+ .d2-3003375094 .fill-N5{fill:#DEE1EB;}
+ .d2-3003375094 .fill-N6{fill:#EEF1F8;}
+ .d2-3003375094 .fill-N7{fill:#FFFFFF;}
+ .d2-3003375094 .fill-B1{fill:#0D32B2;}
+ .d2-3003375094 .fill-B2{fill:#0D32B2;}
+ .d2-3003375094 .fill-B3{fill:#E3E9FD;}
+ .d2-3003375094 .fill-B4{fill:#E3E9FD;}
+ .d2-3003375094 .fill-B5{fill:#EDF0FD;}
+ .d2-3003375094 .fill-B6{fill:#F7F8FE;}
+ .d2-3003375094 .fill-AA2{fill:#4A6FF3;}
+ .d2-3003375094 .fill-AA4{fill:#EDF0FD;}
+ .d2-3003375094 .fill-AA5{fill:#F7F8FE;}
+ .d2-3003375094 .fill-AB4{fill:#EDF0FD;}
+ .d2-3003375094 .fill-AB5{fill:#F7F8FE;}
+ .d2-3003375094 .stroke-N1{stroke:#0A0F25;}
+ .d2-3003375094 .stroke-N2{stroke:#676C7E;}
+ .d2-3003375094 .stroke-N3{stroke:#9499AB;}
+ .d2-3003375094 .stroke-N4{stroke:#CFD2DD;}
+ .d2-3003375094 .stroke-N5{stroke:#DEE1EB;}
+ .d2-3003375094 .stroke-N6{stroke:#EEF1F8;}
+ .d2-3003375094 .stroke-N7{stroke:#FFFFFF;}
+ .d2-3003375094 .stroke-B1{stroke:#0D32B2;}
+ .d2-3003375094 .stroke-B2{stroke:#0D32B2;}
+ .d2-3003375094 .stroke-B3{stroke:#E3E9FD;}
+ .d2-3003375094 .stroke-B4{stroke:#E3E9FD;}
+ .d2-3003375094 .stroke-B5{stroke:#EDF0FD;}
+ .d2-3003375094 .stroke-B6{stroke:#F7F8FE;}
+ .d2-3003375094 .stroke-AA2{stroke:#4A6FF3;}
+ .d2-3003375094 .stroke-AA4{stroke:#EDF0FD;}
+ .d2-3003375094 .stroke-AA5{stroke:#F7F8FE;}
+ .d2-3003375094 .stroke-AB4{stroke:#EDF0FD;}
+ .d2-3003375094 .stroke-AB5{stroke:#F7F8FE;}
+ .d2-3003375094 .background-color-N1{background-color:#0A0F25;}
+ .d2-3003375094 .background-color-N2{background-color:#676C7E;}
+ .d2-3003375094 .background-color-N3{background-color:#9499AB;}
+ .d2-3003375094 .background-color-N4{background-color:#CFD2DD;}
+ .d2-3003375094 .background-color-N5{background-color:#DEE1EB;}
+ .d2-3003375094 .background-color-N6{background-color:#EEF1F8;}
+ .d2-3003375094 .background-color-N7{background-color:#FFFFFF;}
+ .d2-3003375094 .background-color-B1{background-color:#0D32B2;}
+ .d2-3003375094 .background-color-B2{background-color:#0D32B2;}
+ .d2-3003375094 .background-color-B3{background-color:#E3E9FD;}
+ .d2-3003375094 .background-color-B4{background-color:#E3E9FD;}
+ .d2-3003375094 .background-color-B5{background-color:#EDF0FD;}
+ .d2-3003375094 .background-color-B6{background-color:#F7F8FE;}
+ .d2-3003375094 .background-color-AA2{background-color:#4A6FF3;}
+ .d2-3003375094 .background-color-AA4{background-color:#EDF0FD;}
+ .d2-3003375094 .background-color-AA5{background-color:#F7F8FE;}
+ .d2-3003375094 .background-color-AB4{background-color:#EDF0FD;}
+ .d2-3003375094 .background-color-AB5{background-color:#F7F8FE;}
+ .d2-3003375094 .color-N1{color:#0A0F25;}
+ .d2-3003375094 .color-N2{color:#676C7E;}
+ .d2-3003375094 .color-N3{color:#9499AB;}
+ .d2-3003375094 .color-N4{color:#CFD2DD;}
+ .d2-3003375094 .color-N5{color:#DEE1EB;}
+ .d2-3003375094 .color-N6{color:#EEF1F8;}
+ .d2-3003375094 .color-N7{color:#FFFFFF;}
+ .d2-3003375094 .color-B1{color:#0D32B2;}
+ .d2-3003375094 .color-B2{color:#0D32B2;}
+ .d2-3003375094 .color-B3{color:#E3E9FD;}
+ .d2-3003375094 .color-B4{color:#E3E9FD;}
+ .d2-3003375094 .color-B5{color:#EDF0FD;}
+ .d2-3003375094 .color-B6{color:#F7F8FE;}
+ .d2-3003375094 .color-AA2{color:#4A6FF3;}
+ .d2-3003375094 .color-AA4{color:#EDF0FD;}
+ .d2-3003375094 .color-AA5{color:#F7F8FE;}
+ .d2-3003375094 .color-AB4{color:#EDF0FD;}
+ .d2-3003375094 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker-d2-3003375094);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker-d2-3003375094);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-B4{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark-d2-3003375094);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker-d2-3003375094);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark-d2-3003375094);mix-blend-mode:overlay}.sketch-overlay-N3{fill:url(#streaks-normal-d2-3003375094);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal-d2-3003375094);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-N6{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright-d2-3003375094);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]>
\ No newline at end of file
diff --git a/testdata/d2compiler/TestCompile2/nulls/implicit/no-delete-connection.exp.json b/testdata/d2compiler/TestCompile2/nulls/implicit/no-delete-connection.exp.json
index d787ba546..c0b0e4fc1 100644
--- a/testdata/d2compiler/TestCompile2/nulls/implicit/no-delete-connection.exp.json
+++ b/testdata/d2compiler/TestCompile2/nulls/implicit/no-delete-connection.exp.json
@@ -103,7 +103,38 @@
},
"zIndex": 0
},
- "edges": null,
+ "edges": [
+ {
+ "index": 0,
+ "isCurve": false,
+ "src_arrow": false,
+ "dst_arrow": true,
+ "references": [
+ {
+ "map_key_edge_index": 0
+ }
+ ],
+ "attributes": {
+ "label": {
+ "value": ""
+ },
+ "labelDimensions": {
+ "width": 0,
+ "height": 0
+ },
+ "style": {},
+ "near_key": null,
+ "shape": {
+ "value": ""
+ },
+ "direction": {
+ "value": ""
+ },
+ "constraint": null
+ },
+ "zIndex": 0
+ }
+ ],
"objects": [
{
"id": "x",
@@ -149,6 +180,51 @@
"constraint": null
},
"zIndex": 0
+ },
+ {
+ "id": "y",
+ "id_val": "y",
+ "references": [
+ {
+ "key": {
+ "range": "d2/testdata/d2compiler/TestCompile2/nulls/implicit/no-delete-connection.d2,2:5:14-2:6:15",
+ "path": [
+ {
+ "unquoted_string": {
+ "range": "d2/testdata/d2compiler/TestCompile2/nulls/implicit/no-delete-connection.d2,2:5:14-2:6:15",
+ "value": [
+ {
+ "string": "y",
+ "raw_string": "y"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "key_path_index": 0,
+ "map_key_edge_index": 0
+ }
+ ],
+ "attributes": {
+ "label": {
+ "value": "y"
+ },
+ "labelDimensions": {
+ "width": 0,
+ "height": 0
+ },
+ "style": {},
+ "near_key": null,
+ "shape": {
+ "value": "rectangle"
+ },
+ "direction": {
+ "value": ""
+ },
+ "constraint": null
+ },
+ "zIndex": 0
}
]
},
diff --git a/testdata/d2compiler/TestCompile2/nulls/reappear/attribute-reset.exp.json b/testdata/d2compiler/TestCompile2/nulls/reappear/attribute-reset.exp.json
index 45dfdd32d..92f2e464a 100644
--- a/testdata/d2compiler/TestCompile2/nulls/reappear/attribute-reset.exp.json
+++ b/testdata/d2compiler/TestCompile2/nulls/reappear/attribute-reset.exp.json
@@ -1,7 +1,7 @@
{
"graph": {
"name": "",
- "isFolderOnly": true,
+ "isFolderOnly": false,
"ast": {
"range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/attribute-reset.d2,0:0:0-4:0:32",
"nodes": [
@@ -132,7 +132,53 @@
"zIndex": 0
},
"edges": null,
- "objects": null
+ "objects": [
+ {
+ "id": "a",
+ "id_val": "a",
+ "references": [
+ {
+ "key": {
+ "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/attribute-reset.d2,3:0:30-3:1:31",
+ "path": [
+ {
+ "unquoted_string": {
+ "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/attribute-reset.d2,3:0:30-3:1:31",
+ "value": [
+ {
+ "string": "a",
+ "raw_string": "a"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "key_path_index": 0,
+ "map_key_edge_index": -1
+ }
+ ],
+ "attributes": {
+ "label": {
+ "value": "a"
+ },
+ "labelDimensions": {
+ "width": 0,
+ "height": 0
+ },
+ "style": {},
+ "near_key": null,
+ "shape": {
+ "value": "rectangle"
+ },
+ "direction": {
+ "value": ""
+ },
+ "constraint": null
+ },
+ "zIndex": 0
+ }
+ ]
},
"err": null
}
diff --git a/testdata/d2compiler/TestCompile2/nulls/reappear/children-reset.exp.json b/testdata/d2compiler/TestCompile2/nulls/reappear/children-reset.exp.json
index 8d39010c9..6af8f1845 100644
--- a/testdata/d2compiler/TestCompile2/nulls/reappear/children-reset.exp.json
+++ b/testdata/d2compiler/TestCompile2/nulls/reappear/children-reset.exp.json
@@ -277,6 +277,62 @@
"constraint": null
},
"zIndex": 0
+ },
+ {
+ "id": "b",
+ "id_val": "b",
+ "references": [
+ {
+ "key": {
+ "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/children-reset.d2,3:0:17-3:3:20",
+ "path": [
+ {
+ "unquoted_string": {
+ "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/children-reset.d2,3:0:17-3:1:18",
+ "value": [
+ {
+ "string": "a",
+ "raw_string": "a"
+ }
+ ]
+ }
+ },
+ {
+ "unquoted_string": {
+ "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/children-reset.d2,3:2:19-3:3:20",
+ "value": [
+ {
+ "string": "b",
+ "raw_string": "b"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "key_path_index": 1,
+ "map_key_edge_index": -1
+ }
+ ],
+ "attributes": {
+ "label": {
+ "value": "b"
+ },
+ "labelDimensions": {
+ "width": 0,
+ "height": 0
+ },
+ "style": {},
+ "near_key": null,
+ "shape": {
+ "value": "rectangle"
+ },
+ "direction": {
+ "value": ""
+ },
+ "constraint": null
+ },
+ "zIndex": 0
}
]
},
diff --git a/testdata/d2compiler/TestCompile2/nulls/reappear/edge-reset.exp.json b/testdata/d2compiler/TestCompile2/nulls/reappear/edge-reset.exp.json
index 3d5dfed15..d6e3860e1 100644
--- a/testdata/d2compiler/TestCompile2/nulls/reappear/edge-reset.exp.json
+++ b/testdata/d2compiler/TestCompile2/nulls/reappear/edge-reset.exp.json
@@ -172,6 +172,51 @@
"constraint": null
},
"zIndex": 0
+ },
+ {
+ "id": "a",
+ "id_val": "a",
+ "references": [
+ {
+ "key": {
+ "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/edge-reset.d2,3:0:16-3:1:17",
+ "path": [
+ {
+ "unquoted_string": {
+ "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/edge-reset.d2,3:0:16-3:1:17",
+ "value": [
+ {
+ "string": "a",
+ "raw_string": "a"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "key_path_index": 0,
+ "map_key_edge_index": -1
+ }
+ ],
+ "attributes": {
+ "label": {
+ "value": "a"
+ },
+ "labelDimensions": {
+ "width": 0,
+ "height": 0
+ },
+ "style": {},
+ "near_key": null,
+ "shape": {
+ "value": "rectangle"
+ },
+ "direction": {
+ "value": ""
+ },
+ "constraint": null
+ },
+ "zIndex": 0
}
]
},
diff --git a/testdata/d2compiler/TestCompile2/nulls/reappear/shape.exp.json b/testdata/d2compiler/TestCompile2/nulls/reappear/shape.exp.json
index d4773e351..744b757f3 100644
--- a/testdata/d2compiler/TestCompile2/nulls/reappear/shape.exp.json
+++ b/testdata/d2compiler/TestCompile2/nulls/reappear/shape.exp.json
@@ -1,7 +1,7 @@
{
"graph": {
"name": "",
- "isFolderOnly": true,
+ "isFolderOnly": false,
"ast": {
"range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,0:0:0-4:0:13",
"nodes": [
@@ -104,7 +104,53 @@
"zIndex": 0
},
"edges": null,
- "objects": null
+ "objects": [
+ {
+ "id": "a",
+ "id_val": "a",
+ "references": [
+ {
+ "key": {
+ "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,3:0:11-3:1:12",
+ "path": [
+ {
+ "unquoted_string": {
+ "range": "d2/testdata/d2compiler/TestCompile2/nulls/reappear/shape.d2,3:0:11-3:1:12",
+ "value": [
+ {
+ "string": "a",
+ "raw_string": "a"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "key_path_index": 0,
+ "map_key_edge_index": -1
+ }
+ ],
+ "attributes": {
+ "label": {
+ "value": "a"
+ },
+ "labelDimensions": {
+ "width": 0,
+ "height": 0
+ },
+ "style": {},
+ "near_key": null,
+ "shape": {
+ "value": "rectangle"
+ },
+ "direction": {
+ "value": ""
+ },
+ "constraint": null
+ },
+ "zIndex": 0
+ }
+ ]
},
"err": null
}