-
-
Notifications
You must be signed in to change notification settings - Fork 277
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: HTML/CSS/JS ContextWriter #636
base: main
Are you sure you want to change the base?
Changes from all commits
e320d95
62382ba
71e3a34
6ae9a5e
bc5092f
7891bda
ee13b51
da94fcf
a04c725
e153d93
761d675
9656641
ee298d2
ec259fd
f0051c9
d972e09
808514f
4810026
0edebff
556439a
601e8f1
59c9985
bae606c
2d46172
53a7446
4c7959b
cee405d
6de021c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
0.2.648 | ||
0.2.651 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
package parser | ||
|
||
import ( | ||
"bytes" | ||
"testing" | ||
|
||
"github.com/a-h/parse" | ||
|
@@ -9,13 +10,15 @@ import ( | |
|
||
func TestExpressionCSSPropertyParser(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
input string | ||
expected ExpressionCSSProperty | ||
name string | ||
input string | ||
expected ExpressionCSSProperty | ||
expectedCSS string | ||
}{ | ||
{ | ||
name: "css: single constant property", | ||
input: `background-color: { constants.BackgroundColor };`, | ||
name: "css: single constant property", | ||
input: `background-color: { constants.BackgroundColor };`, | ||
expectedCSS: "background-color: ' ';\n", | ||
expected: ExpressionCSSProperty{ | ||
Name: "background-color", | ||
Value: StringExpression{ | ||
|
@@ -38,8 +41,9 @@ func TestExpressionCSSPropertyParser(t *testing.T) { | |
}, | ||
}, | ||
{ | ||
name: "css: single constant property with windows newlines", | ||
input: "background-color:\r\n{ constants.BackgroundColor };\r\n", | ||
name: "css: single constant property with windows newlines", | ||
input: "background-color:\r\n{ constants.BackgroundColor };\r\n", | ||
expectedCSS: "background-color: ' ';\n", | ||
expected: ExpressionCSSProperty{ | ||
Name: "background-color", | ||
Value: StringExpression{ | ||
|
@@ -76,27 +80,47 @@ func TestExpressionCSSPropertyParser(t *testing.T) { | |
if diff := cmp.Diff(tt.expected, result); diff != "" { | ||
t.Errorf(diff) | ||
} | ||
|
||
w := new(bytes.Buffer) | ||
cw := NewContextWriter(w, WriteContextCSS) | ||
if err := result.Write(cw, 0); err != nil { | ||
t.Fatalf("unexpected error: %v", err) | ||
} | ||
actualCSS := w.String() | ||
if diff := cmp.Diff(tt.expectedCSS, actualCSS); diff != "" { | ||
t.Error(diff) | ||
|
||
t.Errorf("input:\n%s", displayWhitespaceChars(tt.input)) | ||
t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedCSS)) | ||
t.Errorf("got:\n%s", displayWhitespaceChars(actualCSS)) | ||
} | ||
if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualCSS)); diff != "" { | ||
t.Errorf(diff) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestConstantCSSPropertyParser(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
input string | ||
expected ConstantCSSProperty | ||
name string | ||
input string | ||
expected ConstantCSSProperty | ||
expectedCSS string | ||
}{ | ||
{ | ||
name: "css: single constant property", | ||
input: `background-color: #ffffff;`, | ||
name: "css: single constant property", | ||
input: `background-color: #ffffff;`, | ||
expectedCSS: "background-color: #ffffff;\n", | ||
expected: ConstantCSSProperty{ | ||
Name: "background-color", | ||
Value: "#ffffff", | ||
}, | ||
}, | ||
{ | ||
name: "css: single constant webkit property", | ||
input: `-webkit-text-stroke-color: #ffffff;`, | ||
name: "css: single constant webkit property", | ||
input: `-webkit-text-stroke-color: #ffffff;`, | ||
expectedCSS: "-webkit-text-stroke-color: #ffffff;\n", | ||
expected: ConstantCSSProperty{ | ||
Name: "-webkit-text-stroke-color", | ||
Value: "#ffffff", | ||
|
@@ -117,20 +141,40 @@ func TestConstantCSSPropertyParser(t *testing.T) { | |
if diff := cmp.Diff(tt.expected, result); diff != "" { | ||
t.Errorf(diff) | ||
} | ||
|
||
w := new(bytes.Buffer) | ||
cw := NewContextWriter(w, WriteContextCSS) | ||
if err := result.Write(cw, 0); err != nil { | ||
t.Fatalf("unexpected error: %v", err) | ||
} | ||
actualCSS := w.String() | ||
if diff := cmp.Diff(tt.expectedCSS, actualCSS); diff != "" { | ||
t.Error(diff) | ||
|
||
t.Errorf("input:\n%s", displayWhitespaceChars(tt.input)) | ||
t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedCSS)) | ||
t.Errorf("got:\n%s", displayWhitespaceChars(actualCSS)) | ||
} | ||
if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualCSS)); diff != "" { | ||
t.Errorf(diff) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestCSSParser(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
input string | ||
expected CSSTemplate | ||
name string | ||
input string | ||
expected CSSTemplate | ||
expectedCSS string | ||
}{ | ||
{ | ||
name: "css: no parameters, no content", | ||
input: `css Name() { | ||
}`, | ||
expectedCSS: ` | ||
`, | ||
expected: CSSTemplate{ | ||
Name: "Name", | ||
Expression: Expression{ | ||
|
@@ -155,6 +199,8 @@ func TestCSSParser(t *testing.T) { | |
name: "css: without spaces", | ||
input: `css Name() { | ||
}`, | ||
expectedCSS: ` | ||
`, | ||
expected: CSSTemplate{ | ||
Name: "Name", | ||
Expression: Expression{ | ||
|
@@ -180,6 +226,9 @@ func TestCSSParser(t *testing.T) { | |
input: `css Name() { | ||
background-color: #ffffff; | ||
}`, | ||
expectedCSS: ` | ||
background-color: #ffffff; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, here, we've got a bit of extra spacing. |
||
`, | ||
expected: CSSTemplate{ | ||
Name: "Name", | ||
Expression: Expression{ | ||
|
@@ -210,6 +259,9 @@ background-color: #ffffff; | |
input: `css Name() { | ||
background-color: { constants.BackgroundColor }; | ||
}`, | ||
expectedCSS: ` | ||
background-color: ' '; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And here too. |
||
`, | ||
expected: CSSTemplate{ | ||
Name: "Name", | ||
Expression: Expression{ | ||
|
@@ -256,6 +308,9 @@ background-color: { constants.BackgroundColor }; | |
input: `css Name(prop string) { | ||
background-color: { prop }; | ||
}`, | ||
expectedCSS: ` | ||
background-color: ' '; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More additional space. |
||
`, | ||
expected: CSSTemplate{ | ||
Name: "Name", | ||
Expression: Expression{ | ||
|
@@ -312,6 +367,23 @@ background-color: { prop }; | |
if diff := cmp.Diff(tt.expected, result); diff != "" { | ||
t.Errorf(diff) | ||
} | ||
|
||
w := new(bytes.Buffer) | ||
cw := NewContextWriter(w, WriteContextCSS) | ||
if err := result.Write(cw, 0); err != nil { | ||
t.Fatalf("unexpected error: %v", err) | ||
} | ||
actualCSS := w.String() | ||
if diff := cmp.Diff(tt.expectedCSS, actualCSS); diff != "" { | ||
t.Error(diff) | ||
|
||
t.Errorf("input:\n%s", displayWhitespaceChars(tt.input)) | ||
t.Errorf("expected:\n%s", displayWhitespaceChars(tt.expectedCSS)) | ||
t.Errorf("got:\n%s", displayWhitespaceChars(actualCSS)) | ||
} | ||
if diff := cmp.Diff(getLineLengths(tt.input), getLineLengths(actualCSS)); diff != "" { | ||
t.Errorf(diff) | ||
} | ||
}) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For target LSPs to work well, the character positions can't change in the file.
Our goal is to be able to send the current file position (line, col) to the LSP and have it send text insertions and other things back to templ. When we receive those changes, ideally, I'd want to be able to put them into the file without having to remap their positions.
However, this change removes the
\r\n
and\r
chars, therefore changing the line pos.Here, we're losing
\r\n
, so the line positionThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The expected CSS is presumably the output after parsing, which is essentially the same as what we get if we've formatted the code.
However, in our case, we want to support the user continuously typing into the editor, before they've formatted the code, so I'm not sure this is the right behaviour. Maybe it is, and I'm not understanding the context of the test though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this to be the behavior that is causing all of these length mismatches. Since the entire document is parsed into a series of nodes and then built back up with the formatting specified in all of the
Write()
methods, this is the default output after the changes I've made with the ContextWriter.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the suggested line length checking to all of the unit tests that I updated. There were quite a few mismatches outside of the ones you caught so those are failing as of 4c7959b.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've started on a solution in another branch on my fork: alehechka/templ@feature/html-lsp...alehechka:templ:fix/mismatch-line-lengths.
The rough idea is to include a
perservePositions
bool on the ContextWriter that will determine how certain nodes get formatted. As of this comment, this is resolving the manipulation of self-closing tags. The next step is reconciling extraneous white space between the source and output.