Ctrl K
ComunidadGuías…

markdown/link_reference.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package markdown

import (
	"github.com/yuin/goldmark/ast"
	"github.com/yuin/goldmark/text"
	"github.com/yuin/goldmark/util"
)

// maskInvalidLinkReferenceDefinitions hides reference definitions that
// Goldmark accepts with an unclosed parenthesis in a bare destination.
// CommonMark treats those lines as ordinary Markdown text.
func maskInvalidLinkReferenceDefinitions(root ast.Node, source []byte) []byte {
	var masked []byte
	_ = ast.Walk(root, func(node ast.Node, entering bool) (ast.WalkStatus, error) {
		definition, ok := node.(*ast.LinkReferenceDefinition)
		if !entering || !ok || !hasUnclosedParenthesis(definition.Destination) {
			return ast.WalkContinue, nil
		}

		colon, bare := linkReferenceDestination(source, definition)
		if !bare {
			return ast.WalkContinue, nil
		}
		if masked == nil {
			masked = append([]byte(nil), source...)
		}
		masked[colon] = ';'
		return ast.WalkContinue, nil
	})
	return masked
}

func hasUnclosedParenthesis(destination []byte) bool {
	depth := 0
	for i := 0; i < len(destination); i++ {
		switch destination[i] {
		case '\\':
			if i+1 < len(destination) && util.IsPunct(destination[i+1]) {
				i++
			}
		case '(':
			depth++
		case ')':
			depth--
		default:
			// Other destination bytes do not affect parenthesis balance.
		}
	}
	return depth > 0
}

// linkReferenceDestination returns the definition colon and whether its
// destination uses the bare form whose parentheses must balance.
func linkReferenceDestination(source []byte, definition *ast.LinkReferenceDefinition) (int, bool) {
	reader := text.NewBlockReader(source, definition.Lines())
	reader.SkipSpaces()
	if reader.Peek() != '[' {
		return 0, false
	}

	reader.Advance(1)
	closureOptions := text.FindClosureOptions{Nesting: false, Newline: true, Advance: true}
	if _, found := reader.FindClosure('[', ']', closureOptions); !found || reader.Peek() != ':' {
		return 0, false
	}

	_, position := reader.Position()
	colon := position.Start
	if colon < 0 || colon >= len(source) || source[colon] != ':' {
		return 0, false
	}

	reader.Advance(1)
	reader.SkipSpaces()
	return colon, reader.Peek() != '<'
}