mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 14:32:24 -05: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
 | |
| }
 |