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
|
package syntax
import "testing"
// wantHTML is wantLine for HTML.
func wantHTML(t *testing.T, src string, line int, want ...string) {
t.Helper()
wantLine(t, LanguageHTML, src, line, want...)
}
func TestHTMLColoursATag(t *testing.T) {
wantHTML(t, "<p>", 0, "<p:tag", ">:tag")
wantHTML(t, "</p>", 0, "</p:tag", ">:tag")
wantHTML(t, "<br/>", 0, "<br:tag", "/>:tag")
}
func TestHTMLColoursAttributesAndTheirValues(t *testing.T) {
wantHTML(t, `<div class="box">`, 0,
"<div:tag", "class:attribute", "=:operator", `"box":string`, ">:tag")
}
func TestHTMLColoursTheAttributeSpellingsInTheWild(t *testing.T) {
wantHTML(t, `<a data-id="1" xlink:href="#" @click="go">`, 0,
"<a:tag",
"data-id:attribute", "=:operator", `"1":string`,
"xlink:href:attribute", "=:operator", `"#":string`,
"@click:attribute", "=:operator", `"go":string`,
">:tag")
}
func TestTextBetweenTagsIsNotColoured(t *testing.T) {
got := spansOf(t, LanguageHTML, "<p>hello</p>", 0)
want := []string{"<p:tag", ">:tag", "</p:tag", ">:tag"}
if len(got) != len(want) {
t.Fatalf("got %v, want %v", got, want)
}
}
func TestHTMLColoursAComment(t *testing.T) {
wantHTML(t, "<!-- a note -->", 0, "<!-- a note -->:comment")
wantHTML(t, "<p><!-- x --></p>", 0, "<p:tag", ">:tag", "<!-- x -->:comment", "</p:tag", ">:tag")
}
func TestAnHTMLCommentCarriesAcrossLines(t *testing.T) {
src := "<!-- first\n second\n third --><p>"
wantHTML(t, src, 0, "<!-- first:comment")
wantHTML(t, src, 1, " second:comment")
wantHTML(t, src, 2, " third -->:comment", "<p:tag", ">:tag")
}
func TestTagsInsideACommentAreNotColoured(t *testing.T) {
src := "<!--\n<div class=\"x\">\n-->"
wantHTML(t, src, 1, `<div class="x">:comment`)
}
func TestHTMLColoursTheDoctype(t *testing.T) {
wantHTML(t, "<!DOCTYPE html>", 0, "<!DOCTYPE html>:keyword")
}
func TestHTMLColoursEntities(t *testing.T) {
wantHTML(t, "a & b", 0, "&:constant")
wantHTML(t, "© 2026", 0, "©:constant")
}
func TestABareAmpersandIsLeftAlone(t *testing.T) {
// It is legal text, and colouring the rest of a paragraph after one would
// be worse than colouring nothing.
if got := spansOf(t, LanguageHTML, "Marks & Spencer", 0); len(got) != 0 {
t.Errorf("a bare ampersand gave %v, want nothing coloured", got)
}
}
func TestScriptContentsAreNotColouredAsJavaScript(t *testing.T) {
// Documented as out of scope: it needs the element followed across lines
// and another scanner's columns mapped back out.
src := "<script>\nconst x = 1\n</script>"
if got := spansOf(t, LanguageHTML, src, 1); len(got) != 0 {
t.Errorf("the body of a script gave %v, want plain text", got)
}
}
func TestAnUnclosedTagIsColouredToWhatIsThere(t *testing.T) {
// A tag is unfinished most of the time it is being typed.
wantHTML(t, `<div class="bo`, 0, "<div:tag", "class:attribute", "=:operator", `"bo:string`)
}
func TestHTMLGivesOneEntryPerLine(t *testing.T) {
if got := len(Highlight(LanguageHTML, "a\nb\n\nc\n")); got != 5 {
t.Errorf("Highlight() returned %d lines for a 5-line document", got)
}
}
func TestHTMLSpansStayInsideTheirLine(t *testing.T) {
src := "<!DOCTYPE html>\n<html lang=\"en\">\n<!-- c\nd -->\n<p>& text</p>\n"
assertSpansFit(t, LanguageHTML, src)
}
func TestHTMLColoursAPageWithAccents(t *testing.T) {
// A byte offset would put this line's spans out of step with what is drawn.
wantHTML(t, `<p title="éléphant">évidemment</p>`, 0,
"<p:tag", "title:attribute", "=:operator", `"éléphant":string`, ">:tag", "</p:tag", ">:tag")
}
|