package proxy import ( "log/slog" "os" "testing" lsp "github.com/a-h/templ/lsp/protocol" "github.com/google/go-cmp/cmp" ) func TestDocument(t *testing.T) { tests := []struct { name string start string operations []func(d *Document) expected string }{ { name: "Replace all content if the range is nil", start: "0\n1\n2", operations: []func(d *Document){ func(d *Document) { d.Apply(nil, "replaced") }, }, expected: "replaced", }, { name: "If the range matches the length of the file, all of it is replaced", start: "0\n1\n2", operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 0, }, End: lsp.Position{ Line: 2, Character: 1, }, }, "replaced") }, }, expected: "replaced", }, { name: "Can insert new text", start: ``, operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 0, }, End: lsp.Position{ Line: 0, Character: 0, }, }, "abc") }, }, expected: "abc", }, { name: "Can insert new text that ends with a newline", start: ``, operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 0, }, End: lsp.Position{ Line: 0, Character: 0, }, }, "abc\n") }, }, expected: `abc `, }, { name: "Can insert a new line at the end of existing text", start: `abc `, operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 3, }, End: lsp.Position{ Line: 0, Character: 3, }, }, "\n") }, }, expected: `abc `, }, { name: "Can insert a word at the start of existing text", start: `bc`, operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 0, }, End: lsp.Position{ Line: 0, Character: 0, }, }, "a") }, }, expected: `abc`, }, { name: "Can remove whole line", start: "0\n1\n2", operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 1, Character: 0, }, End: lsp.Position{ Line: 2, Character: 0, }, }, "") }, }, expected: "0\n2", }, { name: "Can remove line prefix", start: "abcdef", operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 0, }, End: lsp.Position{ Line: 0, Character: 3, }, }, "") }, }, expected: "def", }, { name: "Can remove line substring", start: "abcdef", operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 2, }, End: lsp.Position{ Line: 0, Character: 3, }, }, "") }, }, expected: "abdef", }, { name: "Can remove line suffix", start: "abcdef", operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 4, }, End: lsp.Position{ Line: 0, Character: 6, }, }, "") }, }, expected: "abcd", }, { name: "Can remove across lines", start: "0\n1\n22", operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 1, Character: 0, }, End: lsp.Position{ Line: 2, Character: 1, }, }, "") }, }, expected: "0\n2", }, { name: "Can remove part of two lines", start: "Line one\nLine two\nLine three", operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 4, }, End: lsp.Position{ Line: 2, Character: 4, }, }, "") }, }, expected: "Line three", }, { name: "Can remove all lines", start: "0\n1\n2", operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 0, }, End: lsp.Position{ Line: 2, Character: 1, }, }, "") }, }, expected: "", }, { name: "Can replace line prefix", start: "012345", operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 0, }, End: lsp.Position{ Line: 0, Character: 3, }, }, "ABCDEFG") }, }, expected: "ABCDEFG345", }, { name: "Can replace text across line boundaries", start: "Line one\nLine two\nLine three", operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 4, }, End: lsp.Position{ Line: 2, Character: 4, }, }, " one test\nNew Line 2\nNew line") }, }, expected: "Line one test\nNew Line 2\nNew line three", }, { name: "Can add new line to end of single line", start: `a`, operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 1, }, End: lsp.Position{ Line: 0, Character: 1, }, }, "\nb") }, }, expected: "a\nb", }, { name: "Exceeding the col and line count rounds down to the end of the file", start: `a`, operations: []func(d *Document){ func(d *Document) { d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 200, Character: 600, }, End: lsp.Position{ Line: 300, Character: 1200, }, }, "\nb") }, }, expected: "a\nb", }, { name: "Can remove a line and add it back from the end of the previous line (insert)", start: "a\nb\nc", operations: []func(d *Document){ func(d *Document) { // Delete. d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 1, Character: 0, }, End: lsp.Position{ Line: 2, Character: 0, }, }, "") // Put it back. d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 1, }, End: lsp.Position{ Line: 0, Character: 1, }, }, "\nb") }, }, expected: "a\nb\nc", }, { name: "Can remove a line and add it back from the end of the previous line (overwrite)", start: "a\nb\nc", operations: []func(d *Document){ func(d *Document) { // Delete. d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 1, Character: 0, }, End: lsp.Position{ Line: 2, Character: 0, }, }, "") // Put it back. d.Apply(&lsp.Range{ Start: lsp.Position{ Line: 0, Character: 1, }, End: lsp.Position{ Line: 1, Character: 0, }, }, "\nb\n") }, }, expected: "a\nb\nc", }, { name: "Add new line with indent to the end of the line", // Based on log entry. // {"level":"info","ts":"2022-06-04T20:55:15+01:00","caller":"proxy/server.go:391","msg":"client -> server: DidChange","params":{"textDocument":{"uri":"file:///Users/adrian/github.com/a-h/templ/generator/test-call/template.templ","version":2},"contentChanges":[{"range":{"start":{"line":4,"character":21},"end":{"line":4,"character":21}},"text":"\n\t\t"}]}} start: `package testcall templ personTemplate(p person) {