diff --git a/asciidoc/table.go b/asciidoc/table.go index 153657b..ad87c36 100644 --- a/asciidoc/table.go +++ b/asciidoc/table.go @@ -363,7 +363,7 @@ type Table struct { Set } -func (Table) Type() ElementType { +func (*Table) Type() ElementType { return ElementTypeBlock } diff --git a/disco/references.go b/disco/references.go index bb98ab6..0a6d35e 100644 --- a/disco/references.go +++ b/disco/references.go @@ -8,10 +8,12 @@ import ( "github.com/project-chip/alchemy/internal/log" "github.com/project-chip/alchemy/internal/parse" "github.com/project-chip/alchemy/internal/text" + "github.com/project-chip/alchemy/matter" "github.com/project-chip/alchemy/matter/spec" + "github.com/project-chip/alchemy/matter/types" ) -func (p AnchorNormalizer) rewriteCrossReferences(doc *spec.Doc) { +func (an AnchorNormalizer) rewriteCrossReferences(doc *spec.Doc) { for id, xrefs := range doc.CrossReferences() { anchor := doc.FindAnchor(id) if anchor == nil { @@ -63,43 +65,93 @@ func (p AnchorNormalizer) rewriteCrossReferences(doc *spec.Doc) { } } } - if p.options.normalizeAnchors { - parse.Traverse(nil, doc.Base.Elements(), func(icr *asciidoc.CrossReference, parent parse.HasElements, index int) parse.SearchShould { - if len(icr.Set) > 0 { - return parse.SearchShouldContinue - } - anchor := doc.FindAnchor(icr.ID) - if anchor == nil { - return parse.SearchShouldContinue - } - section, isSection := anchor.Element.(*asciidoc.Section) - if !isSection { - return parse.SearchShouldContinue - } - sectionName := section.Name() - elements := parent.Elements() - if index >= len(elements)-1 { - return parse.SearchShouldContinue - } - nextElement := elements[index+1] - nextString, ok := nextElement.(*asciidoc.String) - if !ok { - return parse.SearchShouldContinue + if an.options.normalizeAnchors { + parse.Traverse(nil, doc.Base.Elements(), func(el asciidoc.Element, parent parse.HasElements, index int) parse.SearchShould { + if se, ok := el.(*spec.Element); ok { + el = se.Base } - lastSpaceIndex := strings.LastIndex(sectionName, " ") - if lastSpaceIndex == -1 { + switch el := el.(type) { + case *asciidoc.CrossReference: + removeCrossReferenceStutter(doc, el, parent, index) return parse.SearchShouldContinue - } - suffix := sectionName[lastSpaceIndex:] - if !text.HasCaseInsensitivePrefix(nextString.Value, suffix) { + case *asciidoc.Table: + an.normalizeTypeCrossReferencesInTable(doc, el) + return parse.SearchShouldSkip + default: return parse.SearchShouldContinue } - replacement := text.TrimCaseInsensitivePrefix(nextString.Value, suffix) - nextString.Value = replacement - return parse.SearchShouldContinue }) + } +} +func removeCrossReferenceStutter(doc *spec.Doc, icr *asciidoc.CrossReference, parent parse.HasElements, index int) { + if len(icr.Set) > 0 { + return + } + anchor := doc.FindAnchor(icr.ID) + if anchor == nil { + return + } + section, isSection := anchor.Element.(*asciidoc.Section) + if !isSection { + return + } + sectionName := section.Name() + elements := parent.Elements() + if index >= len(elements)-1 { + return + } + nextElement := elements[index+1] + nextString, ok := nextElement.(*asciidoc.String) + if !ok { + return + } + lastSpaceIndex := strings.LastIndex(sectionName, " ") + if lastSpaceIndex == -1 { + return } + suffix := sectionName[lastSpaceIndex:] + if !text.HasCaseInsensitivePrefix(nextString.Value, suffix) { + return + } + replacement := text.TrimCaseInsensitivePrefix(nextString.Value, suffix) + nextString.Value = replacement +} + +func (an AnchorNormalizer) normalizeTypeCrossReferencesInTable(doc *spec.Doc, table *asciidoc.Table) { + parse.Traverse(table, table.Set, func(icr *asciidoc.CrossReference, parent parse.HasElements, index int) parse.SearchShould { + if len(icr.Set) > 0 { + // Don't touch existing labels + return parse.SearchShouldContinue + } + anchor := doc.FindAnchor(icr.ID) + if anchor == nil { + return parse.SearchShouldContinue + } + section, isSection := anchor.Element.(*asciidoc.Section) + if !isSection { + return parse.SearchShouldContinue + } + entities, ok := anchor.Document.EntitiesForSection(section) + if !ok { + return parse.SearchShouldContinue + } + if len(entities) != 1 { + return parse.SearchShouldContinue + } + entity := entities[0] + if !types.IsDataTypeEntity(entity.EntityType()) { + return parse.SearchShouldContinue + } + normalizedLabel := normalizeAnchorLabel(section.Name(), section) + if labelText(normalizedLabel) != section.Name() { + icr.Set = normalizedLabel + slog.Debug("Added label to type xref in table", matter.LogEntity("type", entity), "label", labelText(icr.Set)) + } + + return parse.SearchShouldContinue + }) + } func findRefSection(parent any) *spec.Section { diff --git a/matter/spec/datatypes.go b/matter/spec/datatypes.go index afc8405..38f6238 100644 --- a/matter/spec/datatypes.go +++ b/matter/spec/datatypes.go @@ -166,7 +166,7 @@ func getCustomDataTypeFromReference(spec *Specification, cluster *matter.Cluster } switch el := anchor.Element.(type) { case *asciidoc.Section: - entities := doc.entitiesBySection[el] + entities := anchor.Document.entitiesBySection[el] if len(entities) == 1 { e = entities[0] return diff --git a/matter/spec/doc.go b/matter/spec/doc.go index 42d034e..6b618d2 100644 --- a/matter/spec/doc.go +++ b/matter/spec/doc.go @@ -135,6 +135,11 @@ func (doc *Doc) OrderedEntities() (entities []types.Entity, err error) { return doc.orderedEntities, nil } +func (d *Doc) EntitiesForSection(section *asciidoc.Section) ([]types.Entity, bool) { + e, ok := d.entitiesBySection[section] + return e, ok +} + func (doc *Doc) Reference(ref string) (types.Entity, bool) { a := doc.FindAnchor(ref) diff --git a/matter/spec/table_info.go b/matter/spec/table_info.go index d992bba..65f1072 100644 --- a/matter/spec/table_info.go +++ b/matter/spec/table_info.go @@ -312,7 +312,7 @@ func (ti *TableInfo) buildConstraintValue(els asciidoc.Set, sb *strings.Builder) case asciidoc.AttributeReference: sb.WriteString(fmt.Sprintf("{%s}", v.Name())) default: - slog.Warn("unknown constraint value element", log.Element("path", ti.Doc.Path, el), "type", fmt.Sprintf("%T", el)) + slog.Warn("unknown constraint value element", log.Element("source", ti.Doc.Path, el), "type", fmt.Sprintf("%T", el)) } } return @@ -483,7 +483,7 @@ func buildDataTypeString(d *Doc, cellElements asciidoc.Set, sb *strings.Builder) case *asciidoc.Paragraph: source = buildDataTypeString(d, v.Elements(), sb) default: - slog.Warn("unknown data type value element", log.Element("path", d.Path, el), "type", fmt.Sprintf("%T", v)) + slog.Warn("unknown data type value element", log.Element("source", d.Path, el), "type", fmt.Sprintf("%T", v)) } } return diff --git a/matter/types/entity.go b/matter/types/entity.go index 493a642..73fe722 100644 --- a/matter/types/entity.go +++ b/matter/types/entity.go @@ -72,3 +72,11 @@ func (et EntityType) MarshalJSON() ([]byte, error) { type EntityStore interface { Entities() ([]Entity, error) } + +func IsDataTypeEntity(entityType EntityType) bool { + switch entityType { + case EntityTypeBitmap, EntityTypeEnum, EntityTypeStruct, EntityTypeCommand, EntityTypeEvent: + return true + } + return false +}