Alignment links

This commit is contained in:
2026-05-24 18:06:28 +02:00
parent a0c3c89049
commit 5fa5246e9f
7 changed files with 66 additions and 4 deletions
+12
View File
@@ -283,6 +283,18 @@ topdata:
value: 0 value: 0
``` ```
`topdata.wiki.alignment_links.target_pattern` controls whether formatted
alignment names become internal wiki links. The pattern may use `{alignment}`
for the title-shaped alignment segment with spaces converted to underscores, and
`{label}` for the display label:
```yaml
topdata:
wiki:
alignment_links:
target_pattern: Guides/Alignment/{alignment}
```
`paths.source` and `paths.assets` must name real repository subtrees when they `paths.source` and `paths.assets` must name real repository subtrees when they
are configured. They may be omitted for repositories that do not own that class are configured. They may be omitted for repositories that do not own that class
of source, but they must not resolve to the repository root (`.`). Output and of source, but they must not resolve to the repository root (`.`). Output and
+1
View File
@@ -256,6 +256,7 @@ func (p *Project) EffectiveConfig() EffectiveConfig {
VisibilityFile: defaultString(p.Config.TopData.Wiki.VisibilityFile, DefaultTopDataWikiVisibilityFile), VisibilityFile: defaultString(p.Config.TopData.Wiki.VisibilityFile, DefaultTopDataWikiVisibilityFile),
TemplatesDir: defaultString(p.Config.TopData.Wiki.TemplatesDir, DefaultTopDataWikiTemplatesDir), TemplatesDir: defaultString(p.Config.TopData.Wiki.TemplatesDir, DefaultTopDataWikiTemplatesDir),
ManualSectionsDir: defaultString(p.Config.TopData.Wiki.ManualSectionsDir, DefaultTopDataWikiManualSectionsDir), ManualSectionsDir: defaultString(p.Config.TopData.Wiki.ManualSectionsDir, DefaultTopDataWikiManualSectionsDir),
AlignmentLinks: p.Config.TopData.Wiki.AlignmentLinks,
TitlePrefixMinLength: defaultInt(p.Config.TopData.Wiki.TitlePrefixMinLength, DefaultTopDataWikiTitlePrefixMinLength), TitlePrefixMinLength: defaultInt(p.Config.TopData.Wiki.TitlePrefixMinLength, DefaultTopDataWikiTitlePrefixMinLength),
StatusPages: defaultBoolPointer(p.Config.TopData.Wiki.StatusPages, DefaultTopDataWikiStatusPages), StatusPages: defaultBoolPointer(p.Config.TopData.Wiki.StatusPages, DefaultTopDataWikiStatusPages),
StatusListingScope: defaultString(p.Config.TopData.Wiki.StatusListingScope, DefaultTopDataWikiStatusListingScope), StatusListingScope: defaultString(p.Config.TopData.Wiki.StatusListingScope, DefaultTopDataWikiStatusListingScope),
+5
View File
@@ -249,6 +249,7 @@ type TopDataWikiConfig struct {
VisibilityFile string `json:"visibility_file" yaml:"visibility_file"` VisibilityFile string `json:"visibility_file" yaml:"visibility_file"`
TemplatesDir string `json:"templates_dir" yaml:"templates_dir"` TemplatesDir string `json:"templates_dir" yaml:"templates_dir"`
ManualSectionsDir string `json:"manual_sections_dir" yaml:"manual_sections_dir"` ManualSectionsDir string `json:"manual_sections_dir" yaml:"manual_sections_dir"`
AlignmentLinks TopDataWikiAlignmentLinks `json:"alignment_links" yaml:"alignment_links"`
TitlePrefixMinLength int `json:"title_prefix_min_length" yaml:"title_prefix_min_length"` TitlePrefixMinLength int `json:"title_prefix_min_length" yaml:"title_prefix_min_length"`
StatusPages *bool `json:"status_pages,omitempty" yaml:"status_pages,omitempty"` StatusPages *bool `json:"status_pages,omitempty" yaml:"status_pages,omitempty"`
StatusListingScope string `json:"status_listing_scope" yaml:"status_listing_scope"` StatusListingScope string `json:"status_listing_scope" yaml:"status_listing_scope"`
@@ -256,6 +257,10 @@ type TopDataWikiConfig struct {
ManagedRegion TopDataWikiManagedRegionConfig `json:"managed_region" yaml:"managed_region"` ManagedRegion TopDataWikiManagedRegionConfig `json:"managed_region" yaml:"managed_region"`
} }
type TopDataWikiAlignmentLinks struct {
TargetPattern string `json:"target_pattern" yaml:"target_pattern"`
}
type TopDataWikiStalePagesConfig struct { type TopDataWikiStalePagesConfig struct {
Default string `json:"default" yaml:"default"` Default string `json:"default" yaml:"default"`
LiveCleanup string `json:"live_cleanup" yaml:"live_cleanup"` LiveCleanup string `json:"live_cleanup" yaml:"live_cleanup"`
+2
View File
@@ -120,6 +120,7 @@ type wikiContext struct {
visibleWikiKeys map[string]bool visibleWikiKeys map[string]bool
namespaceTitles map[string]string namespaceTitles map[string]string
namespacePaths map[string]string namespacePaths map[string]string
alignmentLinkPattern string
} }
func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progress func(string)) (wikiResult, error) { func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progress func(string)) (wikiResult, error) {
@@ -153,6 +154,7 @@ func buildWiki(p *project.Project, nativeResult BuildResult, force bool, progres
manualSections := loadWikiManualSections(p) manualSections := loadWikiManualSections(p)
ctx.manualSections = manualSections ctx.manualSections = manualSections
ctx.templateDir = filepath.Join(p.TopDataWikiSourceDir(), filepath.FromSlash(p.EffectiveConfig().TopData.Wiki.TemplatesDir), "pages") ctx.templateDir = filepath.Join(p.TopDataWikiSourceDir(), filepath.FromSlash(p.EffectiveConfig().TopData.Wiki.TemplatesDir), "pages")
ctx.alignmentLinkPattern = p.EffectiveConfig().TopData.Wiki.AlignmentLinks.TargetPattern
ctx.wikiTablesPath = filepath.Join(p.TopDataWikiSourceDir(), filepath.FromSlash(p.EffectiveConfig().TopData.Wiki.TablesFile)) ctx.wikiTablesPath = filepath.Join(p.TopDataWikiSourceDir(), filepath.FromSlash(p.EffectiveConfig().TopData.Wiki.TablesFile))
tableDefinitions, err := loadWikiTableDefinitions(ctx.wikiTablesPath) tableDefinitions, err := loadWikiTableDefinitions(ctx.wikiTablesPath)
if err != nil { if err != nil {
+22
View File
@@ -1288,6 +1288,28 @@ func TestWikiTemplateFormatsPreferredAlignments(t *testing.T) {
} }
} }
func TestWikiTemplateFormatsPreferredAlignmentsWithConfiguredLinks(t *testing.T) {
ctx := &wikiContext{alignmentLinkPattern: "Guides/Alignment/{alignment}"}
page := wikiTemplatePage{
PageID: "racialtypes:tiefling",
Category: "racialtypes",
Key: "racialtypes:tiefling",
Title: "Tiefling",
Row: map[string]any{
"PreferredAlignments": map[string]any{"list": []any{"le", "ne", "ce"}},
},
}
got, err := ctx.renderWikiTemplateString("racialtypes.html", `{{alignments(PreferredAlignments)}}`, page)
if err != nil {
t.Fatalf("render template: %v", err)
}
want := "[[Guides/Alignment/Lawful_Evil|Lawful Evil]], [[Guides/Alignment/Neutral_Evil|Neutral Evil]], [[Guides/Alignment/Chaotic_Evil|Chaotic Evil]]"
if got != want {
t.Fatalf("expected %q, got %q", want, got)
}
}
func TestWikiTemplateDescriptionFiltersExtractParagraphsAndSections(t *testing.T) { func TestWikiTemplateDescriptionFiltersExtractParagraphsAndSections(t *testing.T) {
ctx := &wikiContext{} ctx := &wikiContext{}
page := wikiTemplatePage{ page := wikiTemplatePage{
+1 -1
View File
@@ -1059,7 +1059,7 @@ func (p *wikiExprParser) callHelper(name string, args []any) (any, error) {
if len(args) != 1 { if len(args) != 1 {
return nil, fmt.Errorf("alignments expects 1 argument") return nil, fmt.Errorf("alignments expects 1 argument")
} }
return formatWikiTemplateAlignments(args[0]) return p.ctx.formatWikiTemplateAlignments(args[0])
case "ability_adjustments": case "ability_adjustments":
if len(args) != 0 { if len(args) != 0 {
return nil, fmt.Errorf("ability_adjustments expects 0 arguments") return nil, fmt.Errorf("ability_adjustments expects 0 arguments")
+23 -3
View File
@@ -188,7 +188,7 @@ func (ctx *wikiContext) applyWikiTemplateFilter(value any, filter string, state
case "count": case "count":
return countWikiTemplateValue(value), nil return countWikiTemplateValue(value), nil
case "alignments": case "alignments":
return formatWikiTemplateAlignments(value) return ctx.formatWikiTemplateAlignments(value)
case "list": case "list":
return wikiTemplateHTML(renderWikiTemplateList(value, firstArg(args))), nil return wikiTemplateHTML(renderWikiTemplateList(value, firstArg(args))), nil
case "ordinal": case "ordinal":
@@ -367,7 +367,7 @@ func joinWikiTemplateValue(value any, sep string) string {
} }
} }
func formatWikiTemplateAlignments(value any) (string, error) { func (ctx *wikiContext) formatWikiTemplateAlignments(value any) (string, error) {
values, err := wikiTemplateAlignmentValues(value) values, err := wikiTemplateAlignmentValues(value)
if err != nil { if err != nil {
return "", err return "", err
@@ -384,7 +384,7 @@ func formatWikiTemplateAlignments(value any) (string, error) {
if !ok { if !ok {
return "", fmt.Errorf("unknown alignment value 0x%02X", value) return "", fmt.Errorf("unknown alignment value 0x%02X", value)
} }
labels = append(labels, label) labels = append(labels, ctx.renderWikiTemplateAlignmentLabel(label))
} }
if len(labels) == 0 { if len(labels) == 0 {
return "Any", nil return "Any", nil
@@ -392,6 +392,26 @@ func formatWikiTemplateAlignments(value any) (string, error) {
return strings.Join(labels, ", "), nil return strings.Join(labels, ", "), nil
} }
func (ctx *wikiContext) renderWikiTemplateAlignmentLabel(label string) string {
pattern := ""
if ctx != nil {
pattern = strings.TrimSpace(ctx.alignmentLinkPattern)
}
if pattern == "" {
return label
}
target := strings.ReplaceAll(pattern, "{alignment}", wikiTemplateAlignmentTargetSegment(label))
target = strings.ReplaceAll(target, "{label}", label)
if strings.TrimSpace(target) == "" {
return label
}
return "[[" + target + "|" + label + "]]"
}
func wikiTemplateAlignmentTargetSegment(label string) string {
return strings.ReplaceAll(strings.TrimSpace(label), " ", "_")
}
func wikiTemplateAlignmentValues(value any) ([]int, error) { func wikiTemplateAlignmentValues(value any) ([]int, error) {
if isNullLike(value) { if isNullLike(value) {
return []int{0}, nil return []int{0}, nil