d2fmt: lowercase reserved keywords

This commit is contained in:
Alexander Wang 2024-09-15 10:43:10 -06:00
parent 5e96290e45
commit de518f5c5e
No known key found for this signature in database
GPG key ID: BE3937D0D52D8927
9 changed files with 296 additions and 261 deletions

201
d2ast/keywords.go Normal file
View file

@ -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{}{}
}
}

View file

@ -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
}
}

View file

@ -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)
}
}

View file

@ -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
}
}
`,
},
}

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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
}

View file

@ -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)

View file

@ -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, `<style type="text/css"><![CDATA[`)