diff --git a/hugolib/index.go b/hugolib/index.go index f39723e4e..0189eccb3 100644 --- a/hugolib/index.go +++ b/hugolib/index.go @@ -18,18 +18,148 @@ import ( "sort" ) -type WeightedIndexEntry struct { +/* + * An index list is a list of all indexes and their values + * EG. List['tags'] => TagIndex (from above) + */ +type IndexList map[string]Index + +/* + * An index is a map of keywords to a list of pages. + * For example + * TagIndex['technology'] = WeightedPages + * TagIndex['golang'] = WeightedPages2 + */ +type Index map[string]WeightedPages + +/* + * A list of Pages with their corresponding (and relative) weight + * [{Weight: 30, Page: *1}, {Weight: 40, Page: *2}] + */ +type WeightedPages []WeightedPage +type WeightedPage struct { Weight int Page *Page } -type IndexedPages []WeightedIndexEntry +/* + * This is another representation of an Index using an array rather than a map. + * Important because you can't order a map. + */ +type OrderedIndex []OrderedIndexEntry -func (p IndexedPages) Len() int { return len(p) } -func (p IndexedPages) Swap(i, j int) { p[i], p[j] = p[j], p[i] } -func (p IndexedPages) Sort() { sort.Sort(p) } -func (p IndexedPages) Count() int { return len(p) } -func (p IndexedPages) Less(i, j int) bool { +/* + * Similar to an element of an Index, but with the key embedded (as name) + * Eg: {Name: Technology, WeightedPages: Indexedpages} + */ +type OrderedIndexEntry struct { + Name string + WeightedPages WeightedPages +} + +// KeyPrep... Indexes should be case insensitive. Can make it easily conditional later. +func kp(in string) string { + return helpers.Urlize(in) +} + +func (i Index) Get(key string) WeightedPages { return i[kp(key)] } +func (i Index) Count(key string) int { return len(i[kp(key)]) } +func (i Index) Add(key string, w WeightedPage) { + key = kp(key) + i[key] = append(i[key], w) +} + +// Returns an ordered index with a non defined order +func (i Index) IndexArray() OrderedIndex { + ies := make([]OrderedIndexEntry, len(i)) + count := 0 + for k, v := range i { + ies[count] = OrderedIndexEntry{Name: k, WeightedPages: v} + count++ + } + return ies +} + +// Returns an ordered index sorted by key name +func (i Index) Alphabetical() OrderedIndex { + name := func(i1, i2 *OrderedIndexEntry) bool { + return i1.Name < i2.Name + } + + ia := i.IndexArray() + OIby(name).Sort(ia) + return ia +} + +// Returns an ordered index sorted by # of pages per key +func (i Index) ByCount() OrderedIndex { + count := func(i1, i2 *OrderedIndexEntry) bool { + return len(i1.WeightedPages) > len(i2.WeightedPages) + } + + ia := i.IndexArray() + OIby(count).Sort(ia) + return ia +} + +// Helper to move the page access up a level +func (ie OrderedIndexEntry) Pages() []*Page { + return ie.WeightedPages.Pages() +} + +func (ie OrderedIndexEntry) Count() int { + return len(ie.WeightedPages) +} + +/* + * Implementation of a custom sorter for OrderedIndexes + */ + +// A type to implement the sort interface for IndexEntries. +type orderedIndexSorter struct { + index OrderedIndex + by OIby +} + +// Closure used in the Sort.Less method. +type OIby func(i1, i2 *OrderedIndexEntry) bool + +func (by OIby) Sort(index OrderedIndex) { + ps := &orderedIndexSorter{ + index: index, + by: by, // The Sort method's receiver is the function (closure) that defines the sort order. + } + sort.Sort(ps) +} + +// Len is part of sort.Interface. +func (s *orderedIndexSorter) Len() int { + return len(s.index) +} + +// Swap is part of sort.Interface. +func (s *orderedIndexSorter) Swap(i, j int) { + s.index[i], s.index[j] = s.index[j], s.index[i] +} + +// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. +func (s *orderedIndexSorter) Less(i, j int) bool { + return s.by(&s.index[i], &s.index[j]) +} + +func (wp WeightedPages) Pages() Pages { + pages := make(Pages, len(wp)) + for i := range wp { + pages[i] = wp[i].Page + } + return pages +} + +func (p WeightedPages) Len() int { return len(p) } +func (p WeightedPages) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p WeightedPages) Sort() { sort.Sort(p) } +func (p WeightedPages) Count() int { return len(p) } +func (p WeightedPages) Less(i, j int) bool { if p[i].Weight == p[j].Weight { return p[i].Page.Date.Unix() > p[j].Page.Date.Unix() } else { @@ -37,131 +167,4 @@ func (p IndexedPages) Less(i, j int) bool { } } -func (ip IndexedPages) Pages() Pages { - pages := make(Pages, len(ip)) - for i := range ip { - pages[i] = ip[i].Page - } - return pages -} - -func (ip IndexedPages) PagesByDate(asc bool) Pages { - by := func(p1, p2 *Page) bool { - return p1.Date.Unix() < p2.Date.Unix() - } - - if asc == false { - by = func(p1, p2 *Page) bool { - return p1.Date.Unix() > p2.Date.Unix() - } - } - - ps := &PageSorter{ - pages: ip.Pages(), - by: by, - } - - sort.Sort(ps) - - return ps.pages -} - -type Index map[string]IndexedPages -type IndexList map[string]Index - -// KeyPrep... Indexes should be case insensitive. Can make it easily conditional later. -func kp(in string) string { - return helpers.Urlize(in) -} - -func (i Index) Get(key string) IndexedPages { return i[kp(key)] } -func (i Index) Count(key string) int { return len(i[kp(key)]) } -func (i Index) Add(key string, w WeightedIndexEntry) { - key = kp(key) - i[key] = append(i[key], w) -} - -func (i Index) IndexArray() IndexEntries { - ies := make([]IndexEntry, len(i)) - count := 0 - for k, v := range i { - ies[count] = IndexEntry{Name: k, WeightedPages: v} - count++ - } - return ies -} - -func (i Index) Alphabetical() IndexEntries { - name := func(i1, i2 *IndexEntry) bool { - return i1.Name < i2.Name - } - - ia := i.IndexArray() - By(name).Sort(ia) - return ia -} - -func (i Index) ByCount() IndexEntries { - count := func(i1, i2 *IndexEntry) bool { - return len(i1.WeightedPages) > len(i2.WeightedPages) - } - - ia := i.IndexArray() - By(count).Sort(ia) - return ia -} - -type IndexEntry struct { - Name string - WeightedPages IndexedPages -} - -func (ie IndexEntry) Pages() []*Page { - return ie.WeightedPages.Pages() -} - -func (ie IndexEntry) Count() int { - return len(ie.WeightedPages) -} - -type IndexEntries []IndexEntry - -type By func(i1, i2 *IndexEntry) bool - -func (by By) Sort(indexEntrys []IndexEntry) { - ps := &indexEntrySorter{ - indexEntrys: indexEntrys, - by: by, // The Sort method's receiver is the function (closure) that defines the sort order. - } - sort.Sort(ps) -} - -type indexEntrySorter struct { - indexEntrys []IndexEntry - by func(p1, p2 *IndexEntry) bool // Closure used in the Less method. -} - -// Len is part of sort.Interface. -func (s *indexEntrySorter) Len() int { - return len(s.indexEntrys) -} - -// Swap is part of sort.Interface. -func (s *indexEntrySorter) Swap(i, j int) { - s.indexEntrys[i], s.indexEntrys[j] = s.indexEntrys[j], s.indexEntrys[i] -} - -// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. -func (s *indexEntrySorter) Less(i, j int) bool { - return s.by(&s.indexEntrys[i], &s.indexEntrys[j]) -} - -// Sorting pages -type PageSorter struct { - pages Pages - by func(p1, p2 *Page) bool -} - -func (ps *PageSorter) Len() int { return len(ps.pages) } -func (ps *PageSorter) Swap(i, j int) { ps.pages[i], ps.pages[j] = ps.pages[j], ps.pages[i] } -func (ps *PageSorter) Less(i, j int) bool { return ps.by(ps.pages[i], ps.pages[j]) } +// TODO mimic PagesSorter for WeightedPages diff --git a/hugolib/page.go b/hugolib/page.go index 50d8786f3..8856c43d9 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -74,20 +74,78 @@ type Position struct { type Pages []*Page -func (p Pages) Len() int { return len(p) } -func (p Pages) Less(i, j int) bool { - if p[i].Weight == p[j].Weight { - return p[i].Date.Unix() > p[j].Date.Unix() +/* + * Implementation of a custom sorter for Pages + */ + +// A type to implement the sort interface for Pages +type PageSorter struct { + pages Pages + by PageBy +} + +// Closure used in the Sort.Less method. +type PageBy func(p1, p2 *Page) bool + +func (by PageBy) Sort(pages Pages) { + ps := &PageSorter{ + pages: pages, + by: by, // The Sort method's receiver is the function (closure) that defines the sort order. + } + sort.Sort(ps) +} + +var DefaultPageSort = func(p1, p2 *Page) bool { + if p1.Weight == p2.Weight { + return p1.Date.Unix() > p2.Date.Unix() } else { - return p[i].Weight > p[j].Weight + return p1.Weight < p2.Weight } } -func (p Pages) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (ps *PageSorter) Len() int { return len(ps.pages) } +func (ps *PageSorter) Swap(i, j int) { ps.pages[i], ps.pages[j] = ps.pages[j], ps.pages[i] } -// TODO eliminate unnecessary things -func (p Pages) Sort() { sort.Sort(p) } -func (p Pages) Limit(n int) Pages { return p[0:n] } +// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. +func (ps *PageSorter) Less(i, j int) bool { return ps.by(ps.pages[i], ps.pages[j]) } + +func (p Pages) Sort() { + PageBy(DefaultPageSort).Sort(p) +} + +func (p Pages) Limit(n int) Pages { + if len(p) < n { + return p[0:n] + } else { + return p + } +} + +func (p Pages) ByDate() Pages { + date := func(p1, p2 *Page) bool { + return p1.Date.Unix() < p2.Date.Unix() + } + + PageBy(date).Sort(p) + return p +} + +func (p Pages) ByLength() Pages { + length := func(p1, p2 *Page) bool { + return len(p1.Content) < len(p2.Content) + } + + PageBy(length).Sort(p) + return p +} + +func (p Pages) Reverse() Pages { + for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 { + p[i], p[j] = p[j], p[i] + } + + return p +} func (p Page) Plain() string { if len(p.plain) == 0 { diff --git a/hugolib/site.go b/hugolib/site.go index d34fa5acf..3bc122e0a 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -315,7 +315,7 @@ func (s *Site) BuildSiteMeta() (err error) { v, ok := vals.([]string) if ok { for _, idx := range v { - x := WeightedIndexEntry{weight.(int), p} + x := WeightedPage{weight.(int), p} s.Indexes[plural].Add(idx, x) } @@ -332,7 +332,7 @@ func (s *Site) BuildSiteMeta() (err error) { } for i, p := range s.Pages { - s.Sections.Add(p.Section, WeightedIndexEntry{s.Pages[i].Weight, s.Pages[i]}) + s.Sections.Add(p.Section, WeightedPage{s.Pages[i].Weight, s.Pages[i]}) } for k := range s.Sections { diff --git a/hugolib/site_test.go b/hugolib/site_test.go index 3f78f7b3e..5f6fe6e7f 100644 --- a/hugolib/site_test.go +++ b/hugolib/site_test.go @@ -355,7 +355,7 @@ weight = "4" title = "Four" date = "2012-01-01" +++ -Front Matter with Ordered Pages 4`) +Front Matter with Ordered Pages 4. This is longer content`) var WEIGHTED_SOURCES = []source.ByteSource{ {"sect/doc1.md", WEIGHTED_PAGE_1, "sect"}, @@ -389,6 +389,27 @@ func TestOrderedPages(t *testing.T) { if s.Sections["sect"][1].Page.Title != "Three" || s.Sections["sect"][2].Page.Title != "Four" { t.Errorf("Pages in unexpected order. Second should be '%s', got '%s'", "Three", s.Sections["sect"][1].Page.Title) } + + bydate := s.Pages.ByDate() + + if bydate[0].Title != "One" { + t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bydate[0].Title) + } + + rev := bydate.Reverse() + if rev[0].Title != "Three" { + t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Three", rev[0].Title) + } + + bylength := s.Pages.ByLength() + if bylength[0].Title != "One" { + t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bylength[0].Title) + } + + rbylength := bylength.Reverse() + if rbylength[0].Title != "Four" { + t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Four", rbylength[0].Title) + } } var PAGE_WITH_WEIGHTED_INDEXES_2 = []byte(`+++