-
Notifications
You must be signed in to change notification settings - Fork 823
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
Allow pseudo classes in nested tcss #4163
Conversation
'rule: value' and 'selector:pseudo-class' look too similar to the tokenizer.
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.
Does this impact error messages at all?
i.e. will the following generate a helpful "did you mean" error?
Screen {
Label:locus {
border red;
}
}
@willmcgugan that does not generate a helpful “did you mean” error. You get that error because of the ambiguity of this situation. The tokenizer has to try the "rule:value" path first and because "locus" is not a valid pseudoclass, the negative lookahead won't prevent it from thinking it's a declaration. |
P.S. it will if you add the nested |
This is the error you get before and after the PR. 👇 I can't add the helpful error message in this case without either:
|
I think we can do better. I think we can resolve the ambiguity while maintaining helpful error messages. We might be able to exploit the fact that there are a limited set of rule names. i.e. in the case above Up for the challenge? @rodrigogiraoserrao |
Before this commit we couldn't use pseudo-classes inside nested TCSS because in a nested context the colon can be used to separate a rule name from its value OR a selector from a pseudo-class. To disambiguate the two, we try to tokenize rule names by only accepting correct rule names and we only parse selectors if, when they are followed by a colon, the thing after the colon is a valid pseudo-class. This lookahead enables near-perfect disambiguation when the user types their CSS correctly. However, the user might introduce typos in their CSS and we still want to provide error messages as helpful as possible. Previously, we could “guess” whether the user was trying to type a pseudo-class or a rule name because there was no context in which both could appear at the same time. This enabled pretty lenient tokenizing, and then if the token value wasn't valid, we'd issue the error message. Because we can no longer be that lenient, we provide a final “catch all” rule that will accept situations where a colon is used and we neither tokenize a rule name nor a selector followed by a future pseudo-class. We handle this bottom rule as a special case and we use the matched contents to try and figure out if the user made a typo in a rule name or a pseudo-class. If we can't know, we'll just say that the CSS that was typed is invalid.
@willmcgugan I've updated the top comment to describe what I'm doing and show some of the error messages we get. Images 2-6 are probably what you expect and image 1 (top-left) is probably the most unexpected one. On the one hand, for a Human reading it it's “obvious” that the dev was trying to define a nested selector + pseudo-class, even though “bananas” is a ridiculous pseudo-class. I thought of looking at the end of the line:
But then I'd also need to check for comments (multi-line and single line), check if there were loads of CSS inlined or not, etc. |
I think we can do better than "Invalid CSS". What if we were to simply mandate that types begin with a capital letter? If that were the case, there would be no ambiguity with a declaration versus a selector. |
I'm assuming all future declarations would then start with a lower-case letter. |
Declarations will always be lower case. Go for it! |
To be able to disambiguate between selector:pseudo-class and declaration:rule-value in nested TCSS (see #4039 for the original issue and #4163 for a first attempt at solving this) we establish that selectors with widget type names always start with an upper case letter A-Z or an underscore _ whereas declarations always start with a lower case letter a-z. When a user creates a widget subclass that doesn't conform to this, we issue a 'SyntaxWarning' to let the user know. Because we do this with the standard module 'warnings', the warning can easily be supressed if the user has a good reason to create a widget subclass with a name starting with a lower case letter (which is valid Python, just unhelpful to Textual).
Superseded by #4252 given that the trade-off suggested by Will simplifies the implementation. |
This PR allows the usage of pseudo classes in nested TCSS while doing the best possible to preserve helpful error messages when the dev writes invalid CSS.
Fixes #4039.
Before this PR we couldn't use pseudo-classes inside nested TCSS because in a nested context the colon can be used to separate a rule name from its value OR a selector from a pseudo-class.
This would throw off the tokenizer.
To disambiguate the two, we try to tokenize rule names by only accepting correct rule names and we only parse selectors if, when they are followed by a colon, the thing after the colon is a valid pseudo-class.
This lookahead enables near-perfect disambiguation when the user types their CSS correctly.
However, the user might introduce typos in their CSS and we still want to provide error messages as helpful as possible.
Previously, we could “guess” whether the user was trying to type a pseudo-class or a rule name because there was no context in which both could appear at the same time.
This enabled pretty lenient tokenizing, and then if the token value wasn't valid, we'd issue the error message.
Because we can no longer be that lenient, we provide a final “catch all” rule that will accept situations where a colon is used and we neither tokenize a rule name nor a selector followed by a future pseudo-class.
We handle this bottom rule as a special case and we use the matched contents to try and figure out if the user made a typo in a rule name or a pseudo-class.
If we can't know, we'll just say that the CSS that was typed is invalid.
The screenshot below shows examples of the error messages we show with different CSS sets:
Each terminal shows some CSS pasted and then the error produced by Textual when tokenizing said CSS.
Demo app below. Just change the widget
W?
being yielded from within the app.