d2fmt: lowercase reserved keywords
This commit is contained in:
parent
5e96290e45
commit
de518f5c5e
9 changed files with 296 additions and 261 deletions
201
d2ast/keywords.go
Normal file
201
d2ast/keywords.go
Normal 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{}{}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
17
d2ir/d2ir.go
17
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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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[`)
|
||||
|
|
|
|||
Loading…
Reference in a new issue