mirror of
				https://github.com/SphericalKat/medium.rip.git
				synced 2025-10-31 10:55:57 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			121 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package converters
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"html"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 	"unicode/utf16"
 | |
| 
 | |
| 	"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(utf16.Encode([]rune(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 {
 | |
| 	if len(markups) == 0 {
 | |
| 		return html.EscapeString(text)
 | |
| 	}
 | |
| 
 | |
| 	var markedUp strings.Builder
 | |
| 	for _, r := range ranges(text, markups) {
 | |
| 		// handle utf-16
 | |
| 		utf16Text := utf16.Encode([]rune(text))
 | |
| 		ranged := utf16Text[r.Range[0]:r.Range[1]]
 | |
| 		textToWrap := string(utf16.Decode(ranged))
 | |
| 		markedUp.WriteString(wrapInMarkups(textToWrap, r.Markups, false))
 | |
| 	}
 | |
| 
 | |
| 	return markedUp.String()
 | |
| }
 | |
| 
 | |
| func wrapInMarkups(child string, markups []entities.Markup, childIsMarkup bool) string {
 | |
| 	if len(markups) == 0 {
 | |
| 		return child
 | |
| 	}
 | |
| 	if !childIsMarkup {
 | |
| 		child = html.EscapeString(child)
 | |
| 	}
 | |
| 	markedUp := markupNodeInContainer(child, markups[0])
 | |
| 	return wrapInMarkups(markedUp, markups[1:], true)
 | |
| }
 | |
| 
 | |
| 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
 | |
| }
 |