medium.rip/pkg/converters/markup_converter.go
Sphericalkat 7b6e7d7edb feat: implement markup rendering
Signed-off-by: Sphericalkat <amolele@gmail.com>
2023-05-28 16:13:44 +05:30

109 lines
2.7 KiB
Go

package converters
import (
"fmt"
"sort"
"strings"
"github.com/medium.rip/pkg/entities"
)
type RangeWithMarkup struct {
Range []int
Markups []entities.Markup
}
func unique(intSlice []int) []int {
keys := make(map[int]bool)
list := []int{}
for _, entry := range intSlice {
if _, value := keys[entry]; !value {
keys[entry] = true
list = append(list, entry)
}
}
return list
}
func ranges(text string, markups []entities.Markup) []RangeWithMarkup {
ranges := make([]RangeWithMarkup, 0)
// first, get all the borders of the markups
markupBoundaries := make([]int, 0)
for _, m := range markups {
markupBoundaries = append(markupBoundaries, []int{int(m.Start), int(m.End)}...)
}
// include the start and end indexes of the text
markupBoundaries = append([]int{0}, markupBoundaries...)
markupBoundaries = append(markupBoundaries, len(text))
// remove duplicates
markupBoundaries = unique(markupBoundaries)
// sort slice
sort.Slice(markupBoundaries, func(i, j int) bool {
return markupBoundaries[i] < markupBoundaries[j]
})
// attach markup to every range
for i := 0; i < len(markupBoundaries)-1; i++ {
start := markupBoundaries[i]
end := markupBoundaries[i+1]
// check if this markup is covered by the range
coveredMarkups := make([]entities.Markup, 0)
for _, m := range markups {
if (int(m.Start) >= start && int(m.Start) < end) || (int(m.End-1) >= start && int(m.End-1) < end) {
coveredMarkups = append(coveredMarkups, m)
}
}
// append the range
ranges = append(ranges, RangeWithMarkup{
Range: []int{start, end},
Markups: coveredMarkups,
})
}
return ranges
}
func ConvertMarkup(text string, markups []entities.Markup) string {
var markedUp strings.Builder
for _, r := range ranges(text, markups) {
textToWrap := string(text[r.Range[0]:r.Range[1]])
markedUp.WriteString(wrapInMarkups(textToWrap, r.Markups))
}
return markedUp.String()
}
func wrapInMarkups(child string, markups []entities.Markup) string {
if len(markups) == 0 {
return child
}
markedUp := markupNodeInContainer(child, markups[0])
return wrapInMarkups(markedUp, markups[1:])
}
func markupNodeInContainer(child string, markup entities.Markup) string {
switch markup.Type {
case "A":
if markup.Href != nil {
return fmt.Sprintf(`<a href="%s">%s</a>`, *markup.Href, child)
} else if markup.UserID != nil {
return fmt.Sprintf(`<a href="https://medium.com/u/%s">%s</a>`, markup.UserID, child)
}
case "CODE":
return fmt.Sprintf(`<code>%s</code>`, child)
case "EM":
return fmt.Sprintf(`<em>%s</em>`, child)
case "STRONG":
return fmt.Sprintf(`<strong>%s</strong>`, child)
default:
return fmt.Sprintf(`<code>%s</code>`, child)
}
return child
}