Centralize D&D source reference ordering
This commit is contained in:
@@ -3,34 +3,15 @@
|
||||
package npcinteractions
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/core/source"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/npcs/identity"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/shared"
|
||||
)
|
||||
|
||||
// CanonicalizeSourceRefs returns a cloned, document-ordered, de-duplicated
|
||||
// source-reference list.
|
||||
func CanonicalizeSourceRefs(doc *source.SourceDocument, input []source.SourceRef) []source.SourceRef {
|
||||
if input == nil {
|
||||
return nil
|
||||
}
|
||||
canonical := append([]source.SourceRef(nil), input...)
|
||||
sort.SliceStable(canonical, func(left, right int) bool {
|
||||
return SourceRefLess(doc, canonical[left], canonical[right])
|
||||
})
|
||||
unique := make([]source.SourceRef, 0, len(canonical))
|
||||
for _, ref := range canonical {
|
||||
if len(unique) == 0 || unique[len(unique)-1] != ref {
|
||||
unique = append(unique, ref)
|
||||
}
|
||||
}
|
||||
return unique
|
||||
}
|
||||
|
||||
// SourceRefsEqual reports whether two source-reference lists have identical
|
||||
// representations and values.
|
||||
func SourceRefsEqual(left, right []source.SourceRef) bool {
|
||||
@@ -45,39 +26,10 @@ func SourceRefsEqual(left, right []source.SourceRef) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SourceRefLess orders references by source identity and then by the source
|
||||
// document positions of their endpoints. Invalid endpoints sort after valid
|
||||
// endpoints and fall back to their literal IDs for deterministic diagnostics.
|
||||
func SourceRefLess(doc *source.SourceDocument, left, right source.SourceRef) bool {
|
||||
if left.SourceID != right.SourceID {
|
||||
return left.SourceID < right.SourceID
|
||||
}
|
||||
leftStart, leftStartOK := source.UnitIndex(doc, left.StartUnitID)
|
||||
rightStart, rightStartOK := source.UnitIndex(doc, right.StartUnitID)
|
||||
if leftStartOK != rightStartOK {
|
||||
return leftStartOK
|
||||
}
|
||||
if leftStartOK && leftStart != rightStart {
|
||||
return leftStart < rightStart
|
||||
}
|
||||
if left.StartUnitID != right.StartUnitID {
|
||||
return left.StartUnitID < right.StartUnitID
|
||||
}
|
||||
leftEnd, leftEndOK := source.UnitIndex(doc, left.EndUnitID)
|
||||
rightEnd, rightEndOK := source.UnitIndex(doc, right.EndUnitID)
|
||||
if leftEndOK != rightEndOK {
|
||||
return leftEndOK
|
||||
}
|
||||
if leftEndOK && leftEnd != rightEnd {
|
||||
return leftEnd < rightEnd
|
||||
}
|
||||
return left.EndUnitID < right.EndUnitID
|
||||
}
|
||||
|
||||
// Less defines the canonical order for NPC interaction occurrences.
|
||||
func Less(doc *source.SourceDocument, left, right dnd.NPCInteraction) bool {
|
||||
leftPosition, leftHasEvidence := EarliestSourcePosition(doc, left)
|
||||
rightPosition, rightHasEvidence := EarliestSourcePosition(doc, right)
|
||||
func Less(order shared.SourceRefOrder, left, right dnd.NPCInteraction) bool {
|
||||
leftPosition, leftHasEvidence := order.EarliestValid(left.SourceRefs)
|
||||
rightPosition, rightHasEvidence := order.EarliestValid(right.SourceRefs)
|
||||
if leftHasEvidence != rightHasEvidence {
|
||||
return leftHasEvidence
|
||||
}
|
||||
@@ -95,25 +47,7 @@ func Less(doc *source.SourceDocument, left, right dnd.NPCInteraction) bool {
|
||||
if left.Kind != right.Kind {
|
||||
return left.Kind < right.Kind
|
||||
}
|
||||
return sourceRefsLess(doc, left.SourceRefs, right.SourceRefs)
|
||||
}
|
||||
|
||||
// EarliestSourcePosition returns the earliest valid cited position.
|
||||
func EarliestSourcePosition(doc *source.SourceDocument, interaction dnd.NPCInteraction) (int, bool) {
|
||||
found := false
|
||||
earliest := 0
|
||||
for _, ref := range interaction.SourceRefs {
|
||||
if source.ValidateRef(doc, ref) != nil {
|
||||
continue
|
||||
}
|
||||
position, ok := source.UnitIndex(doc, ref.StartUnitID)
|
||||
if !ok || (found && position >= earliest) {
|
||||
continue
|
||||
}
|
||||
earliest = position
|
||||
found = true
|
||||
}
|
||||
return earliest, found
|
||||
return sourceRefsLess(order, left.SourceRefs, right.SourceRefs)
|
||||
}
|
||||
|
||||
// ValidSourceRefs reports whether an interaction has non-empty, valid
|
||||
@@ -144,12 +78,12 @@ func ExactIdentity(interaction dnd.NPCInteraction) string {
|
||||
return key.String()
|
||||
}
|
||||
|
||||
func sourceRefsLess(doc *source.SourceDocument, left, right []source.SourceRef) bool {
|
||||
func sourceRefsLess(order shared.SourceRefOrder, left, right []source.SourceRef) bool {
|
||||
for index := 0; index < len(left) && index < len(right); index++ {
|
||||
if left[index] == right[index] {
|
||||
continue
|
||||
}
|
||||
return SourceRefLess(doc, left[index], right[index])
|
||||
return order.Less(left[index], right[index])
|
||||
}
|
||||
return len(left) < len(right)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user