From de518f5c5e4464a53788f2c512804b9c356ba414 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Sun, 15 Sep 2024 10:43:10 -0600 Subject: [PATCH] d2fmt: lowercase reserved keywords --- d2ast/keywords.go | 201 +++++++++++++++++++++++++++++++++ d2compiler/compile.go | 30 ++--- d2format/format.go | 6 + d2format/format_test.go | 30 ++++- d2graph/d2graph.go | 221 ++----------------------------------- d2ir/d2ir.go | 17 ++- d2ir/pattern.go | 11 +- d2oracle/edit.go | 38 +++---- d2renderers/d2svg/d2svg.go | 3 +- 9 files changed, 296 insertions(+), 261 deletions(-) create mode 100644 d2ast/keywords.go diff --git a/d2ast/keywords.go b/d2ast/keywords.go new file mode 100644 index 000000000..7ad1fae65 --- /dev/null +++ b/d2ast/keywords.go @@ -0,0 +1,201 @@ +package d2ast + +import "oss.terrastruct.com/d2/lib/label" + +// All reserved keywords. See init below. +var ReservedKeywords map[string]struct{} + +// Non Style/Holder keywords. +var SimpleReservedKeywords = map[string]struct{}{ + "label": {}, + "desc": {}, + "shape": {}, + "icon": {}, + "constraint": {}, + "tooltip": {}, + "link": {}, + "near": {}, + "width": {}, + "height": {}, + "direction": {}, + "top": {}, + "left": {}, + "grid-rows": {}, + "grid-columns": {}, + "grid-gap": {}, + "vertical-gap": {}, + "horizontal-gap": {}, + "class": {}, + "vars": {}, +} + +// ReservedKeywordHolders are reserved keywords that are meaningless on its own and must hold composites +var ReservedKeywordHolders = map[string]struct{}{ + "style": {}, + "source-arrowhead": {}, + "target-arrowhead": {}, +} + +// CompositeReservedKeywords are reserved keywords that can hold composites +var CompositeReservedKeywords = map[string]struct{}{ + "classes": {}, + "constraint": {}, + "label": {}, + "icon": {}, +} + +// StyleKeywords are reserved keywords which cannot exist outside of the "style" keyword +var StyleKeywords = map[string]struct{}{ + "opacity": {}, + "stroke": {}, + "fill": {}, + "fill-pattern": {}, + "stroke-width": {}, + "stroke-dash": {}, + "border-radius": {}, + + // Only for text + "font": {}, + "font-size": {}, + "font-color": {}, + "bold": {}, + "italic": {}, + "underline": {}, + "text-transform": {}, + + // Only for shapes + "shadow": {}, + "multiple": {}, + "double-border": {}, + + // Only for squares + "3d": {}, + + // Only for edges + "animated": {}, + "filled": {}, +} + +// TODO maybe autofmt should allow other values, and transform them to conform +// e.g. left-center becomes center-left +var NearConstantsArray = []string{ + "top-left", + "top-center", + "top-right", + + "center-left", + "center-right", + + "bottom-left", + "bottom-center", + "bottom-right", +} +var NearConstants map[string]struct{} + +// LabelPositionsArray are the values that labels and icons can set `near` to +var LabelPositionsArray = []string{ + "top-left", + "top-center", + "top-right", + + "center-left", + "center-center", + "center-right", + + "bottom-left", + "bottom-center", + "bottom-right", + + "outside-top-left", + "outside-top-center", + "outside-top-right", + + "outside-left-top", + "outside-left-center", + "outside-left-bottom", + + "outside-right-top", + "outside-right-center", + "outside-right-bottom", + + "outside-bottom-left", + "outside-bottom-center", + "outside-bottom-right", +} +var LabelPositions map[string]struct{} + +var LabelPositionsMapping = map[string]label.Position{ + "top-left": label.InsideTopLeft, + "top-center": label.InsideTopCenter, + "top-right": label.InsideTopRight, + + "center-left": label.InsideMiddleLeft, + "center-center": label.InsideMiddleCenter, + "center-right": label.InsideMiddleRight, + + "bottom-left": label.InsideBottomLeft, + "bottom-center": label.InsideBottomCenter, + "bottom-right": label.InsideBottomRight, + + "outside-top-left": label.OutsideTopLeft, + "outside-top-center": label.OutsideTopCenter, + "outside-top-right": label.OutsideTopRight, + + "outside-left-top": label.OutsideLeftTop, + "outside-left-center": label.OutsideLeftMiddle, + "outside-left-bottom": label.OutsideLeftBottom, + + "outside-right-top": label.OutsideRightTop, + "outside-right-center": label.OutsideRightMiddle, + "outside-right-bottom": label.OutsideRightBottom, + + "outside-bottom-left": label.OutsideBottomLeft, + "outside-bottom-center": label.OutsideBottomCenter, + "outside-bottom-right": label.OutsideBottomRight, +} + +var FillPatterns = []string{ + "none", + "dots", + "lines", + "grain", + "paper", +} + +var TextTransforms = []string{"none", "uppercase", "lowercase", "capitalize"} + +// BoardKeywords contains the keywords that create new boards. +var BoardKeywords = map[string]struct{}{ + "layers": {}, + "scenarios": {}, + "steps": {}, +} + +func init() { + ReservedKeywords = make(map[string]struct{}) + for k, v := range SimpleReservedKeywords { + ReservedKeywords[k] = v + } + for k, v := range StyleKeywords { + ReservedKeywords[k] = v + } + for k, v := range ReservedKeywordHolders { + CompositeReservedKeywords[k] = v + } + for k, v := range BoardKeywords { + CompositeReservedKeywords[k] = v + } + for k, v := range CompositeReservedKeywords { + ReservedKeywords[k] = v + } + + NearConstants = make(map[string]struct{}, len(NearConstantsArray)) + for _, k := range NearConstantsArray { + NearConstants[k] = struct{}{} + } + + LabelPositions = make(map[string]struct{}, len(LabelPositionsArray)) + for _, k := range LabelPositionsArray { + LabelPositions[k] = struct{}{} + } +} diff --git a/d2compiler/compile.go b/d2compiler/compile.go index be155a4c3..43be72f03 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -271,7 +271,7 @@ func (c *compiler) compileMap(obj *d2graph.Object, m *d2ir.Map) { if f.Name == "shape" { continue } - if _, ok := d2graph.BoardKeywords[f.Name]; ok { + if _, ok := d2ast.BoardKeywords[f.Name]; ok { continue } c.compileField(obj, f) @@ -293,12 +293,12 @@ func (c *compiler) compileMap(obj *d2graph.Object, m *d2ir.Map) { func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) { keyword := strings.ToLower(f.Name) - _, isStyleReserved := d2graph.StyleKeywords[keyword] + _, isStyleReserved := d2ast.StyleKeywords[keyword] if isStyleReserved { c.errorf(f.LastRef().AST(), "%v must be style.%v", f.Name, f.Name) return } - _, isReserved := d2graph.SimpleReservedKeywords[keyword] + _, isReserved := d2ast.SimpleReservedKeywords[keyword] if f.Name == "classes" { if f.Map() != nil { if len(f.Map().Edges) > 0 { @@ -309,7 +309,7 @@ func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) { continue } for _, cf := range classesField.Map().Fields { - if _, ok := d2graph.ReservedKeywords[cf.Name]; !ok { + if _, ok := d2ast.ReservedKeywords[cf.Name]; !ok { c.errorf(cf.LastRef().AST(), "%s is an invalid class field, must be reserved keyword", cf.Name) } if cf.Name == "class" { @@ -440,7 +440,7 @@ func (c *compiler) compilePosition(attrs *d2graph.Attributes, f *d2ir.Field) { case *d2ast.Null: attrs.LabelPosition = nil default: - if _, ok := d2graph.LabelPositions[scalar.ScalarString()]; !ok { + if _, ok := d2ast.LabelPositions[scalar.ScalarString()]; !ok { c.errorf(f.LastPrimaryKey(), `invalid "near" field`) } else { switch name { @@ -686,7 +686,7 @@ func (c *compiler) compileStyle(attrs *d2graph.Attributes, m *d2ir.Map) { } func (c *compiler) compileStyleField(attrs *d2graph.Attributes, f *d2ir.Field) { - if _, ok := d2graph.StyleKeywords[strings.ToLower(f.Name)]; !ok { + if _, ok := d2ast.StyleKeywords[strings.ToLower(f.Name)]; !ok { c.errorf(f.LastRef().AST(), `invalid style keyword: "%s"`, f.Name) return } @@ -814,7 +814,7 @@ func (c *compiler) compileEdgeMap(edge *d2graph.Edge, m *d2ir.Map) { } } for _, f := range m.Fields { - _, ok := d2graph.ReservedKeywords[f.Name] + _, ok := d2ast.ReservedKeywords[f.Name] if !ok { c.errorf(f.References[0].AST(), `edge map keys must be reserved keywords`) continue @@ -825,12 +825,12 @@ func (c *compiler) compileEdgeMap(edge *d2graph.Edge, m *d2ir.Map) { func (c *compiler) compileEdgeField(edge *d2graph.Edge, f *d2ir.Field) { keyword := strings.ToLower(f.Name) - _, isStyleReserved := d2graph.StyleKeywords[keyword] + _, isStyleReserved := d2ast.StyleKeywords[keyword] if isStyleReserved { c.errorf(f.LastRef().AST(), "%v must be style.%v", f.Name, f.Name) return } - _, isReserved := d2graph.SimpleReservedKeywords[keyword] + _, isReserved := d2ast.SimpleReservedKeywords[keyword] if isReserved { c.compileReserved(&edge.Attributes, f) return @@ -868,7 +868,7 @@ func (c *compiler) compileArrowheads(edge *d2graph.Edge, f *d2ir.Field) { if f.Map() != nil { for _, f2 := range f.Map().Fields { keyword := strings.ToLower(f2.Name) - _, isReserved := d2graph.SimpleReservedKeywords[keyword] + _, isReserved := d2ast.SimpleReservedKeywords[keyword] if isReserved { c.compileReserved(attrs, f2) continue @@ -985,7 +985,7 @@ func (c *compiler) compileSQLTable(obj *d2graph.Object) { func (c *compiler) validateKeys(obj *d2graph.Object, m *d2ir.Map) { for _, f := range m.Fields { - if _, ok := d2graph.BoardKeywords[f.Name]; ok { + if _, ok := d2ast.BoardKeywords[f.Name]; ok { continue } c.validateKey(obj, f) @@ -994,7 +994,7 @@ func (c *compiler) validateKeys(obj *d2graph.Object, m *d2ir.Map) { func (c *compiler) validateKey(obj *d2graph.Object, f *d2ir.Field) { keyword := strings.ToLower(f.Name) - _, isReserved := d2graph.ReservedKeywords[keyword] + _, isReserved := d2ast.ReservedKeywords[keyword] if isReserved { switch obj.Shape.Value { case d2target.ShapeCircle, d2target.ShapeSquare: @@ -1067,7 +1067,7 @@ func (c *compiler) validateNear(g *d2graph.Graph) { for _, obj := range g.Objects { if obj.NearKey != nil { nearObj, isKey := g.Root.HasChild(d2graph.Key(obj.NearKey)) - _, isConst := d2graph.NearConstants[d2graph.Key(obj.NearKey)[0]] + _, isConst := d2ast.NearConstants[d2graph.Key(obj.NearKey)[0]] if isKey { // Doesn't make sense to set near to an ancestor or descendant nearIsAncestor := false @@ -1097,7 +1097,7 @@ func (c *compiler) validateNear(g *d2graph.Graph) { continue } if nearObj.NearKey != nil { - _, nearObjNearIsConst := d2graph.NearConstants[d2graph.Key(nearObj.NearKey)[0]] + _, nearObjNearIsConst := d2ast.NearConstants[d2graph.Key(nearObj.NearKey)[0]] if nearObjNearIsConst { c.errorf(obj.NearKey, "near keys cannot be set to an object with a constant near key") continue @@ -1117,7 +1117,7 @@ func (c *compiler) validateNear(g *d2graph.Graph) { continue } } else { - c.errorf(obj.NearKey, "near key %#v must be the absolute path to a shape or one of the following constants: %s", d2format.Format(obj.NearKey), strings.Join(d2graph.NearConstantsArray, ", ")) + c.errorf(obj.NearKey, "near key %#v must be the absolute path to a shape or one of the following constants: %s", d2format.Format(obj.NearKey), strings.Join(d2ast.NearConstantsArray, ", ")) continue } } diff --git a/d2format/format.go b/d2format/format.go index dd1a42996..09a932e46 100644 --- a/d2format/format.go +++ b/d2format/format.go @@ -135,6 +135,12 @@ func (p *printer) interpolationBoxes(boxes []d2ast.InterpolationBox, isDoubleStr } b.StringRaw = &s } + if !isDoubleString { + if _, ok := d2ast.ReservedKeywords[strings.ToLower(*b.StringRaw)]; ok { + s := strings.ToLower(*b.StringRaw) + b.StringRaw = &s + } + } p.sb.WriteString(*b.StringRaw) } } diff --git a/d2format/format_test.go b/d2format/format_test.go index 369361f2c..b87b3eb91 100644 --- a/d2format/format_test.go +++ b/d2format/format_test.go @@ -27,7 +27,6 @@ x -> y exp: `x -> y `, }, - { name: "complex", in: ` @@ -853,6 +852,35 @@ jeremy: { !&shape: rectangle label: I'm not a rectangle } +`, + }, + { + name: "lowercase-reserved", + in: `jacob: { + SHAPE: circle +} +jeremy.SHAPE: rectangle +alice.STYLE.fill: red +bob.style.FILL: red +carmen.STYLE.FILL: red +coop: { + STYLE: { + FILL: blue + } +} +`, + exp: `jacob: { + shape: circle +} +jeremy.shape: rectangle +alice.style.fill: red +bob.style.fill: red +carmen.style.fill: red +coop: { + style: { + fill: blue + } +} `, }, } diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index 93a567c29..d3c717644 100644 --- a/d2graph/d2graph.go +++ b/d2graph/d2graph.go @@ -268,8 +268,8 @@ func (s *Style) Apply(key, value string) error { if s.FillPattern == nil { break } - if !go2.Contains(FillPatterns, strings.ToLower(value)) { - return fmt.Errorf(`expected "fill-pattern" to be one of: %s`, strings.Join(FillPatterns, ", ")) + if !go2.Contains(d2ast.FillPatterns, strings.ToLower(value)) { + return fmt.Errorf(`expected "fill-pattern" to be one of: %s`, strings.Join(d2ast.FillPatterns, ", ")) } s.FillPattern.Value = value case "stroke-width": @@ -409,8 +409,8 @@ func (s *Style) Apply(key, value string) error { if s.TextTransform == nil { break } - if !go2.Contains(textTransforms, strings.ToLower(value)) { - return fmt.Errorf(`expected "text-transform" to be one of (%s)`, strings.Join(textTransforms, ", ")) + if !go2.Contains(d2ast.TextTransforms, strings.ToLower(value)) { + return fmt.Errorf(`expected "text-transform" to be one of (%s)`, strings.Join(d2ast.TextTransforms, ", ")) } s.TextTransform.Value = value default: @@ -667,7 +667,7 @@ func (obj *Object) HasChild(ids []string) (*Object, bool) { return obj, true } if len(ids) == 1 && ids[0] != "style" { - _, ok := ReservedKeywords[ids[0]] + _, ok := d2ast.ReservedKeywords[ids[0]] if ok { return obj, true } @@ -818,9 +818,9 @@ func (obj *Object) EnsureChild(ida []string) *Object { return obj } - _, is := ReservedKeywordHolders[ida[0]] + _, is := d2ast.ReservedKeywordHolders[ida[0]] if len(ida) == 1 && !is { - _, ok := ReservedKeywords[ida[0]] + _, ok := d2ast.ReservedKeywords[ida[0]] if ok { return obj } @@ -1120,7 +1120,7 @@ func (obj *Object) IsConstantNear() bool { if isKey { return false } - _, isConst := NearConstants[keyPath[0]] + _, isConst := d2ast.NearConstants[keyPath[0]] return isConst } @@ -1235,7 +1235,7 @@ func (e *Edge) AbsID() string { func (obj *Object) Connect(srcID, dstID []string, srcArrow, dstArrow bool, label string) (*Edge, error) { for _, id := range [][]string{srcID, dstID} { for _, p := range id { - if _, ok := ReservedKeywords[p]; ok { + if _, ok := d2ast.ReservedKeywords[p]; ok { return nil, errors.New("cannot connect to reserved keyword") } } @@ -1428,12 +1428,12 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler // user-specified label/icon positions if obj.HasLabel() && obj.Attributes.LabelPosition != nil { scalar := *obj.Attributes.LabelPosition - position := LabelPositionsMapping[scalar.Value] + position := d2ast.LabelPositionsMapping[scalar.Value] obj.LabelPosition = go2.Pointer(position.String()) } if obj.Icon != nil && obj.Attributes.IconPosition != nil { scalar := *obj.Attributes.IconPosition - position := LabelPositionsMapping[scalar.Value] + position := d2ast.LabelPositionsMapping[scalar.Value] obj.IconPosition = go2.Pointer(position.String()) } @@ -1673,205 +1673,6 @@ func Key(k *d2ast.KeyPath) []string { return d2format.KeyPath(k) } -// All reserved keywords. See init below. -var ReservedKeywords map[string]struct{} - -// Non Style/Holder keywords. -var SimpleReservedKeywords = map[string]struct{}{ - "label": {}, - "desc": {}, - "shape": {}, - "icon": {}, - "constraint": {}, - "tooltip": {}, - "link": {}, - "near": {}, - "width": {}, - "height": {}, - "direction": {}, - "top": {}, - "left": {}, - "grid-rows": {}, - "grid-columns": {}, - "grid-gap": {}, - "vertical-gap": {}, - "horizontal-gap": {}, - "class": {}, - "vars": {}, -} - -// ReservedKeywordHolders are reserved keywords that are meaningless on its own and must hold composites -var ReservedKeywordHolders = map[string]struct{}{ - "style": {}, - "source-arrowhead": {}, - "target-arrowhead": {}, -} - -// CompositeReservedKeywords are reserved keywords that can hold composites -var CompositeReservedKeywords = map[string]struct{}{ - "classes": {}, - "constraint": {}, - "label": {}, - "icon": {}, -} - -// StyleKeywords are reserved keywords which cannot exist outside of the "style" keyword -var StyleKeywords = map[string]struct{}{ - "opacity": {}, - "stroke": {}, - "fill": {}, - "fill-pattern": {}, - "stroke-width": {}, - "stroke-dash": {}, - "border-radius": {}, - - // Only for text - "font": {}, - "font-size": {}, - "font-color": {}, - "bold": {}, - "italic": {}, - "underline": {}, - "text-transform": {}, - - // Only for shapes - "shadow": {}, - "multiple": {}, - "double-border": {}, - - // Only for squares - "3d": {}, - - // Only for edges - "animated": {}, - "filled": {}, -} - -// TODO maybe autofmt should allow other values, and transform them to conform -// e.g. left-center becomes center-left -var NearConstantsArray = []string{ - "top-left", - "top-center", - "top-right", - - "center-left", - "center-right", - - "bottom-left", - "bottom-center", - "bottom-right", -} -var NearConstants map[string]struct{} - -// LabelPositionsArray are the values that labels and icons can set `near` to -var LabelPositionsArray = []string{ - "top-left", - "top-center", - "top-right", - - "center-left", - "center-center", - "center-right", - - "bottom-left", - "bottom-center", - "bottom-right", - - "outside-top-left", - "outside-top-center", - "outside-top-right", - - "outside-left-top", - "outside-left-center", - "outside-left-bottom", - - "outside-right-top", - "outside-right-center", - "outside-right-bottom", - - "outside-bottom-left", - "outside-bottom-center", - "outside-bottom-right", -} -var LabelPositions map[string]struct{} - -// convert to label.Position -var LabelPositionsMapping = map[string]label.Position{ - "top-left": label.InsideTopLeft, - "top-center": label.InsideTopCenter, - "top-right": label.InsideTopRight, - - "center-left": label.InsideMiddleLeft, - "center-center": label.InsideMiddleCenter, - "center-right": label.InsideMiddleRight, - - "bottom-left": label.InsideBottomLeft, - "bottom-center": label.InsideBottomCenter, - "bottom-right": label.InsideBottomRight, - - "outside-top-left": label.OutsideTopLeft, - "outside-top-center": label.OutsideTopCenter, - "outside-top-right": label.OutsideTopRight, - - "outside-left-top": label.OutsideLeftTop, - "outside-left-center": label.OutsideLeftMiddle, - "outside-left-bottom": label.OutsideLeftBottom, - - "outside-right-top": label.OutsideRightTop, - "outside-right-center": label.OutsideRightMiddle, - "outside-right-bottom": label.OutsideRightBottom, - - "outside-bottom-left": label.OutsideBottomLeft, - "outside-bottom-center": label.OutsideBottomCenter, - "outside-bottom-right": label.OutsideBottomRight, -} - -var FillPatterns = []string{ - "none", - "dots", - "lines", - "grain", - "paper", -} - -var textTransforms = []string{"none", "uppercase", "lowercase", "capitalize"} - -// BoardKeywords contains the keywords that create new boards. -var BoardKeywords = map[string]struct{}{ - "layers": {}, - "scenarios": {}, - "steps": {}, -} - -func init() { - ReservedKeywords = make(map[string]struct{}) - for k, v := range SimpleReservedKeywords { - ReservedKeywords[k] = v - } - for k, v := range StyleKeywords { - ReservedKeywords[k] = v - } - for k, v := range ReservedKeywordHolders { - CompositeReservedKeywords[k] = v - } - for k, v := range BoardKeywords { - CompositeReservedKeywords[k] = v - } - for k, v := range CompositeReservedKeywords { - ReservedKeywords[k] = v - } - - NearConstants = make(map[string]struct{}, len(NearConstantsArray)) - for _, k := range NearConstantsArray { - NearConstants[k] = struct{}{} - } - - LabelPositions = make(map[string]struct{}, len(LabelPositionsArray)) - for _, k := range LabelPositionsArray { - LabelPositions[k] = struct{}{} - } -} - func (g *Graph) GetBoard(name string) *Graph { for _, l := range g.Layers { if l.Name == name { diff --git a/d2ir/d2ir.go b/d2ir/d2ir.go index 236e36c18..8c8ac90c6 100644 --- a/d2ir/d2ir.go +++ b/d2ir/d2ir.go @@ -11,7 +11,6 @@ import ( "oss.terrastruct.com/d2/d2ast" "oss.terrastruct.com/d2/d2format" - "oss.terrastruct.com/d2/d2graph" "oss.terrastruct.com/d2/d2parser" "oss.terrastruct.com/d2/d2target" ) @@ -624,7 +623,7 @@ func (m *Map) IsContainer() bool { return false } for _, f := range m.Fields { - _, isReserved := d2graph.ReservedKeywords[f.Name] + _, isReserved := d2ast.ReservedKeywords[f.Name] if !isReserved { return true } @@ -815,9 +814,9 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b head := kp.Path[i].Unbox().ScalarString() - if _, ok := d2graph.ReservedKeywords[strings.ToLower(head)]; ok { + if _, ok := d2ast.ReservedKeywords[strings.ToLower(head)]; ok { head = strings.ToLower(head) - if _, ok := d2graph.CompositeReservedKeywords[head]; !ok && i < len(kp.Path)-1 { + if _, ok := d2ast.CompositeReservedKeywords[head]; !ok && i < len(kp.Path)-1 { return d2parser.Errorf(kp.Path[i].Unbox(), fmt.Sprintf(`"%s" must be the last part of the key`, head)) } } @@ -872,7 +871,7 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create b return nil } shape := ParentShape(m) - if _, ok := d2graph.ReservedKeywords[strings.ToLower(head)]; !ok && len(c.globRefContextStack) > 0 { + if _, ok := d2ast.ReservedKeywords[strings.ToLower(head)]; !ok && len(c.globRefContextStack) > 0 { if shape == d2target.ShapeClass || shape == d2target.ShapeSQLTable { return nil } @@ -971,7 +970,7 @@ func (m *Map) DeleteField(ida ...string) *Field { // If a field was deleted from a keyword-holder keyword and that holder is empty, // then that holder becomes meaningless and should be deleted too parent := ParentField(f) - for keywordHolder := range d2graph.ReservedKeywordHolders { + for keywordHolder := range d2ast.ReservedKeywordHolders { if parent != nil && parent.Name == keywordHolder && len(parent.Map().Fields) == 0 { keywordHolderParentMap := ParentMap(parent) for i, f := range keywordHolderParentMap.Fields { @@ -1527,7 +1526,7 @@ func countUnderscores(p []string) int { func findBoardKeyword(ida ...string) int { for i := range ida { - if _, ok := d2graph.BoardKeywords[ida[i]]; ok { + if _, ok := d2ast.BoardKeywords[ida[i]]; ok { return i } } @@ -1536,10 +1535,10 @@ func findBoardKeyword(ida ...string) int { func findProhibitedEdgeKeyword(ida ...string) int { for i := range ida { - if _, ok := d2graph.SimpleReservedKeywords[ida[i]]; ok { + if _, ok := d2ast.SimpleReservedKeywords[ida[i]]; ok { return i } - if _, ok := d2graph.ReservedKeywordHolders[ida[i]]; ok { + if _, ok := d2ast.ReservedKeywordHolders[ida[i]]; ok { return i } } diff --git a/d2ir/pattern.go b/d2ir/pattern.go index f73f15d45..474c99d38 100644 --- a/d2ir/pattern.go +++ b/d2ir/pattern.go @@ -4,7 +4,6 @@ import ( "strings" "oss.terrastruct.com/d2/d2ast" - "oss.terrastruct.com/d2/d2graph" ) func (m *Map) multiGlob(pattern []string) ([]*Field, bool) { @@ -22,11 +21,11 @@ func (m *Map) multiGlob(pattern []string) ([]*Field, bool) { func (m *Map) _doubleGlob(fa *[]*Field) { for _, f := range m.Fields { - if _, ok := d2graph.ReservedKeywords[f.Name]; ok { + if _, ok := d2ast.ReservedKeywords[f.Name]; ok { if f.Name == "layers" { continue } - if _, ok := d2graph.BoardKeywords[f.Name]; !ok { + if _, ok := d2ast.BoardKeywords[f.Name]; !ok { continue } // We don't ever want to append layers, scenarios or steps directly. @@ -46,8 +45,8 @@ func (m *Map) _doubleGlob(fa *[]*Field) { func (m *Map) _tripleGlob(fa *[]*Field) { for _, f := range m.Fields { - if _, ok := d2graph.ReservedKeywords[f.Name]; ok { - if _, ok := d2graph.BoardKeywords[f.Name]; !ok { + if _, ok := d2ast.ReservedKeywords[f.Name]; ok { + if _, ok := d2ast.BoardKeywords[f.Name]; !ok { continue } // We don't ever want to append layers, scenarios or steps directly. @@ -69,7 +68,7 @@ func matchPattern(s string, pattern []string) bool { if len(pattern) == 0 { return true } - if _, ok := d2graph.ReservedKeywords[s]; ok { + if _, ok := d2ast.ReservedKeywords[s]; ok { return false } diff --git a/d2oracle/edit.go b/d2oracle/edit.go index cc84983d9..0bbdc9400 100644 --- a/d2oracle/edit.go +++ b/d2oracle/edit.go @@ -372,7 +372,7 @@ func _set(g *d2graph.Graph, baseAST *d2ast.Map, key string, tag, value *string) if mk.Key != nil { found := true for _, idel := range d2graph.Key(mk.Key) { - _, ok := d2graph.ReservedKeywords[idel] + _, ok := d2ast.ReservedKeywords[idel] if ok { reserved = true break @@ -544,7 +544,7 @@ func _set(g *d2graph.Graph, baseAST *d2ast.Map, key string, tag, value *string) attrs = edge.Attributes if mk.EdgeKey != nil { - if _, ok := d2graph.ReservedKeywords[mk.EdgeKey.Path[0].Unbox().ScalarString()]; !ok { + if _, ok := d2ast.ReservedKeywords[mk.EdgeKey.Path[0].Unbox().ScalarString()]; !ok { return errors.New("edge key must be reserved") } reserved = true @@ -856,7 +856,7 @@ func appendMapKey(m *d2ast.Map, mk *d2ast.Key) { if len(m.Nodes) == 1 && mk.Key != nil && len(mk.Key.Path) > 0 { - _, ok := d2graph.ReservedKeywords[mk.Key.Path[0].Unbox().ScalarString()] + _, ok := d2ast.ReservedKeywords[mk.Key.Path[0].Unbox().ScalarString()] if ok { // Allow one line reserved key (like shape) maps. // TODO: This needs to be smarter as certain keys are only reserved in context. @@ -1077,7 +1077,7 @@ func hoistRefChildren(g *d2graph.Graph, key *d2ast.KeyPath, ref d2graph.Referenc continue } if n.MapKey.Key != nil { - _, ok := d2graph.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()] + _, ok := d2ast.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()] if ok { continue } @@ -1130,7 +1130,7 @@ func renameConflictsToParent(g *d2graph.Graph, key *d2ast.KeyPath) (*d2graph.Gra continue } if n.MapKey.Key != nil { - _, ok := d2graph.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()] + _, ok := d2ast.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()] if ok { continue } @@ -1150,7 +1150,7 @@ func renameConflictsToParent(g *d2graph.Graph, key *d2ast.KeyPath) (*d2graph.Gra absKey.Path = append(absKey.Path, k.Path[0]) absKeys = append(absKeys, absKey) } - } else if _, ok := d2graph.ReservedKeywords[ref.Key.Path[len(ref.Key.Path)-1].Unbox().ScalarString()]; !ok { + } else if _, ok := d2ast.ReservedKeywords[ref.Key.Path[len(ref.Key.Path)-1].Unbox().ScalarString()]; !ok { absKey, err := d2parser.ParseKey(ref.ScopeObj.AbsID()) if err != nil { absKey = &d2ast.KeyPath{} @@ -1254,7 +1254,7 @@ func deleteReserved(g *d2graph.Graph, boardPath []string, baseAST *d2ast.Map, mk } targetKey = mk.EdgeKey } - _, ok := d2graph.ReservedKeywords[targetKey.Path[len(targetKey.Path)-1].Unbox().ScalarString()] + _, ok := d2ast.ReservedKeywords[targetKey.Path[len(targetKey.Path)-1].Unbox().ScalarString()] if !ok { return g, nil } @@ -1297,7 +1297,7 @@ func deleteReserved(g *d2graph.Graph, boardPath []string, baseAST *d2ast.Map, mk imported := false parts := d2graph.Key(targetKey) for i, id := range parts { - _, ok := d2graph.ReservedKeywords[id] + _, ok := d2ast.ReservedKeywords[id] if ok { if id == "style" { isNestedKey = true @@ -1475,7 +1475,7 @@ func deleteObject(g *d2graph.Graph, baseAST *d2ast.Map, key *d2ast.KeyPath, obj } ref.Key.Path = append(ref.Key.Path[:ref.KeyPathIndex], ref.Key.Path[ref.KeyPathIndex+1:]...) withoutSpecial := go2.Filter(ref.Key.Path, func(x *d2ast.StringBox) bool { - _, isReserved := d2graph.ReservedKeywords[x.Unbox().ScalarString()] + _, isReserved := d2ast.ReservedKeywords[x.Unbox().ScalarString()] isSpecial := isReserved || x.Unbox().ScalarString() == "_" return !isSpecial }) @@ -1494,7 +1494,7 @@ func deleteObject(g *d2graph.Graph, baseAST *d2ast.Map, key *d2ast.KeyPath, obj for i := 0; i < len(ref.MapKey.Value.Map.Nodes); i++ { n := ref.MapKey.Value.Map.Nodes[i] if n.MapKey != nil && n.MapKey.Key != nil { - _, ok := d2graph.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()] + _, ok := d2ast.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()] if ok { deleteFromMap(ref.MapKey.Value.Map, n.MapKey) i-- @@ -1661,7 +1661,7 @@ func Rename(g *d2graph.Graph, boardPath []string, key, newName string) (_ *d2gra mk2.Key = mk.Key mk = mk2 } else { - _, ok := d2graph.ReservedKeywords[newName] + _, ok := d2ast.ReservedKeywords[newName] if ok { return nil, "", fmt.Errorf("cannot rename to reserved keyword: %#v", newName) } @@ -1686,7 +1686,7 @@ func Rename(g *d2graph.Graph, boardPath []string, key, newName string) (_ *d2gra func trimReservedSuffix(path []*d2ast.StringBox) []*d2ast.StringBox { for i, p := range path { - if _, ok := d2graph.ReservedKeywords[p.Unbox().ScalarString()]; ok { + if _, ok := d2ast.ReservedKeywords[p.Unbox().ScalarString()]; ok { return path[:i] } } @@ -1933,9 +1933,9 @@ func move(g *d2graph.Graph, boardPath []string, key, newKey string, includeDesce ida = resolvedIDA } // e.g. "a.b.shape: circle" - _, endsWithReserved := d2graph.ReservedKeywords[ida[len(ida)-1]] + _, endsWithReserved := d2ast.ReservedKeywords[ida[len(ida)-1]] ida = go2.Filter(ida, func(x string) bool { - _, ok := d2graph.ReservedKeywords[x] + _, ok := d2ast.ReservedKeywords[x] return !ok }) @@ -1976,7 +1976,7 @@ func move(g *d2graph.Graph, boardPath []string, key, newKey string, includeDesce continue } if n.MapKey.Key != nil { - _, ok := d2graph.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()] + _, ok := d2ast.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()] if ok { detachedMK.Value.Map.Nodes = append(detachedMK.Value.Map.Nodes, n) } @@ -2222,7 +2222,7 @@ func filterReserved(value d2ast.ValueBox) (with, without d2ast.ValueBox) { forWithout = append(forWithout, n) continue } - _, ok := d2graph.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()] + _, ok := d2ast.ReservedKeywords[n.MapKey.Key.Path[0].Unbox().ScalarString()] if !ok { flushComments(&forWithout) forWithout = append(forWithout, n) @@ -2299,7 +2299,7 @@ func updateNear(prevG, g *d2graph.Graph, from, to *string, includeDescendants bo } if n.MapKey.Key.Path[len(n.MapKey.Key.Path)-1].Unbox().ScalarString() == "near" { k := n.MapKey.Value.ScalarBox().Unbox().ScalarString() - if _, ok := d2graph.NearConstants[k]; ok { + if _, ok := d2ast.NearConstants[k]; ok { continue } if strings.EqualFold(k, *from) && to == nil { @@ -2925,7 +2925,7 @@ func DeleteIDDeltas(g *d2graph.Graph, boardPath []string, key string) (deltas ma if mk.Key != nil { ida := d2graph.Key(mk.Key) // Deleting a reserved field cannot possibly have any deltas - if _, ok := d2graph.ReservedKeywords[ida[len(ida)-1]]; ok { + if _, ok := d2ast.ReservedKeywords[ida[len(ida)-1]]; ok { return nil, nil } @@ -3256,7 +3256,7 @@ func getMostNestedRefs(obj *d2graph.Object) []d2graph.Reference { func filterReservedPath(path []*d2ast.StringBox) (filtered []*d2ast.StringBox) { for _, box := range path { - if _, ok := d2graph.ReservedKeywords[strings.ToLower(box.Unbox().ScalarString())]; ok { + if _, ok := d2ast.ReservedKeywords[strings.ToLower(box.Unbox().ScalarString())]; ok { return } filtered = append(filtered, box) diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index 039d330d5..7ffa5c1c7 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -20,6 +20,7 @@ import ( "github.com/alecthomas/chroma/v2/lexers" "github.com/alecthomas/chroma/v2/styles" + "oss.terrastruct.com/d2/d2ast" "oss.terrastruct.com/d2/d2graph" "oss.terrastruct.com/d2/d2renderers/d2fonts" "oss.terrastruct.com/d2/d2renderers/d2latex" @@ -1975,7 +1976,7 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) { bufStr := buf.String() patternDefs := "" - for _, pattern := range d2graph.FillPatterns { + for _, pattern := range d2ast.FillPatterns { if strings.Contains(bufStr, fmt.Sprintf("%s-overlay", pattern)) || diagram.Root.FillPattern == pattern { if patternDefs == "" { fmt.Fprint(upperBuf, `