turbo-editors/turbo-corepublic Fork 0
main
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

🛟 Updated. 28d5985 · on main · k33g · 3h ago
markdown_inline.go · 110 lines · 2.7 KBGo Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
package syntax

// The inline half of the Markdown scanner: what can appear inside a line of
// prose, once the line's own shape has been decided by markdown.go.

// scanMarkdownInline colours what can appear inside a line of prose.
func scanMarkdownInline(s *LineScanner) {
	for !s.AtEnd() {
		switch {
		case s.Peek(0) == '`':
			takeCodeSpan(s)
		case s.Peek(0) == '!' && s.Peek(1) == '[':
			takeLink(s, 1)
		case s.Peek(0) == '[':
			takeLink(s, 0)
		case isEmphasisMarker(s.Peek(0)):
			takeEmphasis(s)
		default:
			s.pos++
		}
	}
}

// isEmphasisMarker reports whether a rune can begin bold or italic text.
func isEmphasisMarker(r rune) bool { return r == '*' || r == '_' }

// takeCodeSpan colours `code`, or the lone backtick when nothing closes it.
func takeCodeSpan(s *LineScanner) {
	start := s.pos
	for at := s.pos + 1; at < len(s.line); at++ {
		if s.line[at] == '`' {
			s.pos = at + 1
			s.Emit(start, s.pos, ClassString)
			return
		}
	}
	s.pos++
}

// takeEmphasis colours *italic*, **bold** and their underscore spellings.
//
// The run of markers at the start has to be matched by the same run at the
// end, so that **bold** is one span rather than an empty italic followed by
// stray text.
func takeEmphasis(s *LineScanner) {
	marker := s.Peek(0)
	run := 0
	for s.Peek(run) == marker {
		run++
	}

	if end := findRun(s.line, s.pos+run, marker, run); end > 0 {
		start := s.pos
		s.pos = end + run
		s.Emit(start, s.pos, ClassEmphasis)
		return
	}
	s.pos += run
}

// findRun returns where a run of exactly n markers starts at or after from, or
// -1 when the line holds none.
func findRun(line []rune, from int, marker rune, n int) int {
	for at := from; at+n <= len(line); at++ {
		if line[at] != marker {
			continue
		}
		count := 0
		for at+count < len(line) && line[at+count] == marker {
			count++
		}
		if count == n {
			return at
		}
		at += count - 1
	}
	return -1
}

// takeLink colours [text](target) and its image form ![alt](src).
//
// Both halves are one span: a link is one thing to the eye, and splitting the
// text from the target would put two colours on something read as a unit.
func takeLink(s *LineScanner, offset int) {
	closeBracket := indexFrom(s.line, s.pos+offset+1, ']')
	if closeBracket < 0 || closeBracket+1 >= len(s.line) || s.line[closeBracket+1] != '(' {
		s.pos += offset + 1
		return
	}

	closeParen := indexFrom(s.line, closeBracket+2, ')')
	if closeParen < 0 {
		s.pos += offset + 1
		return
	}

	start := s.pos
	s.pos = closeParen + 1
	s.Emit(start, s.pos, ClassLink)
}

// indexFrom returns where a rune first appears at or after an offset, or -1.
func indexFrom(line []rune, from int, want rune) int {
	for at := max(from, 0); at < len(line); at++ {
		if line[at] == want {
			return at
		}
	}
	return -1
}