Skip to content

Commit

Permalink
Merge pull request #17 from its-luca/featureFlexiblePrettyPrinting
Browse files Browse the repository at this point in the history
Pretty print individual BibEntry and allow custom key order
  • Loading branch information
nickng authored Jul 24, 2022
2 parents c84f200 + daa5043 commit c4b3d54
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 27 deletions.
108 changes: 81 additions & 27 deletions bibtex.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,79 @@ func (entry *BibEntry) AddField(name string, value BibString) {
entry.Fields[strings.TrimSpace(name)] = value
}

// prettyStringConfig controls the formatting/printing behaviour of the BibTex's and BibEntry's PrettyPrint functions
type prettyStringConfig struct {
// priority controls the order in which fields are printed. Keys with lower values are printed earlier.
//See keyOrderToPriorityMap
priority map[string]int
}

// keyOrderToPriorityMap is a helper function for WithKeyOrder, converting the user facing key order slice
// into the map format that is internally used by the sort function
func keyOrderToPriorityMap(keyOrder []string) map[string]int {
priority := make(map[string]int)
offset := len(keyOrder)
for i, v := range keyOrder {
priority[v] = i - offset
}
return priority
}

var defaultPrettyStringConfig = prettyStringConfig{priority: keyOrderToPriorityMap([]string{"title", "author", "url"})}

// PrettyStringOpt allows to change the pretty print format for BibEntry and BibTex
type PrettyStringOpt func(config *prettyStringConfig)

// WithKeyOrder changes the order in which BibEntry keys are printed to the order in which they appear in keyOrder
func WithKeyOrder(keyOrder []string) PrettyStringOpt {
return func(config *prettyStringConfig) {
config.priority = make(map[string]int)
offset := len(keyOrder)
for i, v := range keyOrder {
config.priority[v] = i - offset
}
}
}

// prettyStringAppend appends the pretty print string for BibEntry using config to configure the formatting
func (entry *BibEntry) prettyStringAppend(buf *bytes.Buffer, config prettyStringConfig) {
fmt.Fprintf(buf, "@%s{%s,\n", entry.Type, entry.CiteName)

// Determine key order.
keys := []string{}
for key := range entry.Fields {
keys = append(keys, key)
}

sort.Slice(keys, func(i, j int) bool {
pi, pj := config.priority[keys[i]], config.priority[keys[j]]
return pi < pj || (pi == pj && keys[i] < keys[j])
})

// Write fields.
tw := tabwriter.NewWriter(buf, 1, 4, 1, ' ', 0)
for _, key := range keys {
value := entry.Fields[key].String()
format := stringformat(value)
fmt.Fprintf(tw, " %s\t=\t"+format+",\n", key, value)
}
tw.Flush()
buf.WriteString("}\n")

}

// PrettyString pretty prints a BibEntry
func (entry *BibEntry) PrettyString(options ...PrettyStringOpt) string {
config := defaultPrettyStringConfig
for _, option := range options {
option(&config)
}
var buf bytes.Buffer
entry.prettyStringAppend(&buf, config)

return buf.String()
}

// String returns a BibTex entry as a simplified BibTex string.
func (entry *BibEntry) String() string {
var bibtex bytes.Buffer
Expand Down Expand Up @@ -247,41 +320,22 @@ func (bib *BibTex) RawString() string {
return bibtex.String()
}

// PrettyString pretty prints a BibTex.
func (bib *BibTex) PrettyString() string {
// PrettyString pretty prints a BibTex
func (bib *BibTex) PrettyString(options ...PrettyStringOpt) string {
config := defaultPrettyStringConfig
for _, option := range options {
option(&config)
}

var buf bytes.Buffer
for i, entry := range bib.Entries {
if i != 0 {
fmt.Fprint(&buf, "\n")
}
fmt.Fprintf(&buf, "@%s{%s,\n", entry.Type, entry.CiteName)

// Determine key order.
keys := []string{}
for key := range entry.Fields {
keys = append(keys, key)
}
entry.prettyStringAppend(&buf, config)

priority := map[string]int{"title": -3, "author": -2, "url": -1}
sort.Slice(keys, func(i, j int) bool {
pi, pj := priority[keys[i]], priority[keys[j]]
return pi < pj || (pi == pj && keys[i] < keys[j])
})

// Write fields.
tw := tabwriter.NewWriter(&buf, 1, 4, 1, ' ', 0)
for _, key := range keys {
value := entry.Fields[key].String()
format := stringformat(value)
fmt.Fprintf(tw, " %s\t=\t"+format+",\n", key, value)
}
tw.Flush()

// Close.
buf.WriteString("}\n")
}
return buf.String()

}

// stringformat determines the correct formatting verb for the given BibTeX field value.
Expand Down
33 changes: 33 additions & 0 deletions bibtex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,3 +205,36 @@ func BenchmarkStringPerformance(b *testing.B) {
_ = bib.String()
}
}

func TestBibEntry_PrettyStringCustomOrder(t *testing.T) {
wantPrettyString := `@inproceedings{bibtexKey,
author = "A a and B b and C c",
editor = "D d and E e",
title = "Some title",
booktitle = "Some booktitle",
}
`

entry := NewBibEntry("inproceedings", "bibtexKey")
entry.AddField("author", NewBibConst("A a and B b and C c"))
entry.AddField("editor", NewBibConst("D d and E e"))
entry.AddField("title", NewBibConst("Some title"))
entry.AddField("booktitle", NewBibConst("Some booktitle"))

keyOrder := []string{"author", "editor", "title", "booktitle"}
gotPrettyString := entry.PrettyString(WithKeyOrder(keyOrder))

if wantPrettyString != gotPrettyString {
t.Errorf("Format error\nWant: %s\nGot:%s\n", wantPrettyString, gotPrettyString)
}

// pretty print same entry with different order and check that result no longer matchnes
// wantPrettyString
errorKeyOrder := []string{"editor", "author", "title", "booktitle"}
gotPrettyString = entry.PrettyString(WithKeyOrder(errorKeyOrder))

if wantPrettyString == gotPrettyString {
t.Errorf("Format error. Expected missmatch but got match")
}

}

0 comments on commit c4b3d54

Please sign in to comment.