mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-03 18:22:25 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			152 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			152 lines
		
	
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package parser
 | 
						|
 | 
						|
import (
 | 
						|
	"github.com/yuin/goldmark/ast"
 | 
						|
	"github.com/yuin/goldmark/text"
 | 
						|
	"github.com/yuin/goldmark/util"
 | 
						|
)
 | 
						|
 | 
						|
type linkReferenceParagraphTransformer struct {
 | 
						|
}
 | 
						|
 | 
						|
// LinkReferenceParagraphTransformer is a ParagraphTransformer implementation
 | 
						|
// that parses and extracts link reference from paragraphs.
 | 
						|
var LinkReferenceParagraphTransformer = &linkReferenceParagraphTransformer{}
 | 
						|
 | 
						|
func (p *linkReferenceParagraphTransformer) Transform(node *ast.Paragraph, reader text.Reader, pc Context) {
 | 
						|
	lines := node.Lines()
 | 
						|
	block := text.NewBlockReader(reader.Source(), lines)
 | 
						|
	removes := [][2]int{}
 | 
						|
	for {
 | 
						|
		start, end := parseLinkReferenceDefinition(block, pc)
 | 
						|
		if start > -1 {
 | 
						|
			if start == end {
 | 
						|
				end++
 | 
						|
			}
 | 
						|
			removes = append(removes, [2]int{start, end})
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		break
 | 
						|
	}
 | 
						|
 | 
						|
	offset := 0
 | 
						|
	for _, remove := range removes {
 | 
						|
		if lines.Len() == 0 {
 | 
						|
			break
 | 
						|
		}
 | 
						|
		s := lines.Sliced(remove[1]-offset, lines.Len())
 | 
						|
		lines.SetSliced(0, remove[0]-offset)
 | 
						|
		lines.AppendAll(s)
 | 
						|
		offset = remove[1]
 | 
						|
	}
 | 
						|
 | 
						|
	if lines.Len() == 0 {
 | 
						|
		t := ast.NewTextBlock()
 | 
						|
		t.SetBlankPreviousLines(node.HasBlankPreviousLines())
 | 
						|
		node.Parent().ReplaceChild(node.Parent(), node, t)
 | 
						|
		return
 | 
						|
	}
 | 
						|
 | 
						|
	node.SetLines(lines)
 | 
						|
}
 | 
						|
 | 
						|
func parseLinkReferenceDefinition(block text.Reader, pc Context) (int, int) {
 | 
						|
	block.SkipSpaces()
 | 
						|
	line, _ := block.PeekLine()
 | 
						|
	if line == nil {
 | 
						|
		return -1, -1
 | 
						|
	}
 | 
						|
	startLine, _ := block.Position()
 | 
						|
	width, pos := util.IndentWidth(line, 0)
 | 
						|
	if width > 3 {
 | 
						|
		return -1, -1
 | 
						|
	}
 | 
						|
	if width != 0 {
 | 
						|
		pos++
 | 
						|
	}
 | 
						|
	if line[pos] != '[' {
 | 
						|
		return -1, -1
 | 
						|
	}
 | 
						|
	block.Advance(pos + 1)
 | 
						|
	segments, found := block.FindClosure('[', ']', linkFindClosureOptions)
 | 
						|
	if !found {
 | 
						|
		return -1, -1
 | 
						|
	}
 | 
						|
	var label []byte
 | 
						|
	if segments.Len() == 1 {
 | 
						|
		label = block.Value(segments.At(0))
 | 
						|
	} else {
 | 
						|
		for i := 0; i < segments.Len(); i++ {
 | 
						|
			s := segments.At(i)
 | 
						|
			label = append(label, block.Value(s)...)
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if util.IsBlank(label) {
 | 
						|
		return -1, -1
 | 
						|
	}
 | 
						|
	if block.Peek() != ':' {
 | 
						|
		return -1, -1
 | 
						|
	}
 | 
						|
	block.Advance(1)
 | 
						|
	block.SkipSpaces()
 | 
						|
	destination, ok := parseLinkDestination(block)
 | 
						|
	if !ok {
 | 
						|
		return -1, -1
 | 
						|
	}
 | 
						|
	line, _ = block.PeekLine()
 | 
						|
	isNewLine := line == nil || util.IsBlank(line)
 | 
						|
 | 
						|
	endLine, _ := block.Position()
 | 
						|
	_, spaces, _ := block.SkipSpaces()
 | 
						|
	opener := block.Peek()
 | 
						|
	if opener != '"' && opener != '\'' && opener != '(' {
 | 
						|
		if !isNewLine {
 | 
						|
			return -1, -1
 | 
						|
		}
 | 
						|
		ref := NewReference(label, destination, nil)
 | 
						|
		pc.AddReference(ref)
 | 
						|
		return startLine, endLine + 1
 | 
						|
	}
 | 
						|
	if spaces == 0 {
 | 
						|
		return -1, -1
 | 
						|
	}
 | 
						|
	block.Advance(1)
 | 
						|
	closer := opener
 | 
						|
	if opener == '(' {
 | 
						|
		closer = ')'
 | 
						|
	}
 | 
						|
	segments, found = block.FindClosure(opener, closer, linkFindClosureOptions)
 | 
						|
	if !found {
 | 
						|
		if !isNewLine {
 | 
						|
			return -1, -1
 | 
						|
		}
 | 
						|
		ref := NewReference(label, destination, nil)
 | 
						|
		pc.AddReference(ref)
 | 
						|
		block.AdvanceLine()
 | 
						|
		return startLine, endLine + 1
 | 
						|
	}
 | 
						|
	var title []byte
 | 
						|
	if segments.Len() == 1 {
 | 
						|
		title = block.Value(segments.At(0))
 | 
						|
	} else {
 | 
						|
		for i := 0; i < segments.Len(); i++ {
 | 
						|
			s := segments.At(i)
 | 
						|
			title = append(title, block.Value(s)...)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	line, _ = block.PeekLine()
 | 
						|
	if line != nil && !util.IsBlank(line) {
 | 
						|
		if !isNewLine {
 | 
						|
			return -1, -1
 | 
						|
		}
 | 
						|
		ref := NewReference(label, destination, title)
 | 
						|
		pc.AddReference(ref)
 | 
						|
		return startLine, endLine
 | 
						|
	}
 | 
						|
 | 
						|
	endLine, _ = block.Position()
 | 
						|
	ref := NewReference(label, destination, title)
 | 
						|
	pc.AddReference(ref)
 | 
						|
	return startLine, endLine + 1
 | 
						|
}
 |