Skip to content
quarry Artifact [ff76637c50]
Login

Artifact [ff76637c50]

Artifact ff76637c50bc0c55080bd062af72adc6a9e640eeaf1c6612512b5de9bee94a05:


package highlight

import (
	"strings"
	"testing"
)

// The window carries only the lines asked for. Chroma emits a span per
// token, so a whole-file render of build.c was 40,083 DOM nodes; a
// screenful is a hundred or so.
func TestWindowHoldsOnlyItsLines(t *testing.T) {
	src := []byte(strings.Repeat("int linex = 1;\n", 500))
	lines, total, ok := Lines(src, "", "a.c", 0, 50)
	if !ok {
		t.Fatal("a C file was not coloured")
	}
	if len(lines) != 50 {
		t.Errorf("window holds %d lines, want 50", len(lines))
	}
	// Reaching the end reports the total; stopping early does not
	// pretend to know it.
	if total != 0 {
		t.Errorf("a window that stopped early reported total=%d, want 0", total)
	}
	// A final newline ends the last line; it does not start another.
	if lines, total, _ = Lines(src, "", "a.c", 490, 10000); total != 500 || len(lines) != 10 {
		t.Errorf("total = %d with %d lines, want 500 and 10", total, len(lines))
	}
	if _, total, _ = Lines([]byte("a\nb"), "", "a.c", 0, 10); total != 2 {
		t.Errorf("no final newline: total = %d, want 2", total)
	}
	if lines, total, _ = Lines(nil, "", "a.c", 0, 10); total != 1 || len(lines) != 1 {
		t.Errorf("empty file: total = %d lines %d, want one empty line", total, len(lines))
	}
}

// A window opening inside a block comment must still be coloured as a
// comment. This is why lexing starts at byte zero even though only the
// window is emitted — highlighting a slice in isolation would call the
// comment body ordinary code.
func TestWindowKeepsLexerContext(t *testing.T) {
	src := []byte("int a = 1;\n/* opening a long comment\n" +
		strings.Repeat("still inside the comment\n", 40) +
		"*/\nint b = 2;\n")
	lines, _, _ := Lines(src, "", "a.c", 20, 5)
	// chroma's comment classes begin with "c" (c, c1, cm, ...). The
	// window sits deep inside the comment, so its content must carry
	// one of them rather than being lexed as bare text.
	if !strings.Contains(lines[0], `class="c`) {
		t.Errorf("a window inside a block comment lost its context:\n%s", lines[0])
	}
}

// Windows must tile: consecutive windows reproduce the file's lines in
// order, with nothing dropped or repeated.
func TestWindowsTile(t *testing.T) {
	var b strings.Builder
	for i := 0; i < 30; i++ {
		b.WriteString("x := ")
		b.WriteString(strings.Repeat("9", i%5+1))
		b.WriteString("\n")
	}
	src := []byte(b.String())
	var joined []string
	for from := 0; from < 30; from += 7 {
		lines, _, _ := Lines(src, "", "a.go", from, 7)
		for _, l := range lines {
			joined = append(joined, strip(l))
		}
	}
	if got := strings.Join(joined, "\n") + "\n"; got != string(src) {
		t.Errorf("tiled windows do not reproduce the file:\ngot:  %q\nwant: %q", got, src)
	}
}

// A file no lexer claims still gets its lines, uncoloured, so it can be
// numbered like any other.
func TestLinesForAnUnknownFile(t *testing.T) {
	lines, total, coloured := Lines([]byte("one <two>\nthree\n"), "", "NOTES.zzz", 0, 10)
	if coloured || total != 2 || len(lines) != 2 || lines[0] != "one &lt;two&gt;" {
		t.Errorf("coloured=%v total=%d lines=%q", coloured, total, lines)
	}
}

// strip removes tags, leaving the text a reader would see.
func strip(html string) string {
	var out strings.Builder
	depth := 0
	for _, r := range html {
		switch {
		case r == '<':
			depth++
		case r == '>':
			depth--
		case depth == 0:
			out.WriteRune(r)
		}
	}
	return strings.NewReplacer("&#34;", "\"", "&lt;", "<", "&gt;", ">", "&amp;", "&").Replace(out.String())
}