-
Notifications
You must be signed in to change notification settings - Fork 465
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
Quotes in C-API Strings #1143
Comments
I guess you're talking about |
@mgreter Right. As you can see in the source code: https://github.com/sass/libsass/blob/master/sass_values.cpp#L39-L42 A string only has a value. So quoted strings include the quotes in the value. But since the semantics of Sass are that strings are equal regardless of whether they are using single, double, or no quotes. As such, the value object should not include the quotes in the value of the object. This will prevent native function code interacting with the sass values from having to remember to disregard the quotes in equality checks, hash values, etc. |
IMO it depends from where the string comes from. I.e. in perl-libsass I can put a perl-string into a sass_string value, and I don't want it to unquote the value everytime I do this. So IMO it's correct for sass_values to not be unquoted by default. But IMO this depends on your application logic. To be clear: if I have a programatically allocated string, I don't want it to be unquoted when used as a Sass_String value. The C-API exposes the quote/unquote functions, so you can use them if you need to unquote before creating a Sass_Value. IMO it doesn't matter if single or double quotes are used, as this will only be preserved on static values. Quote will automatically chose the best quotemark. BTW. libsass internally always unquotes strings in the parsing state and only quotes them, when emitting previousely quoted string ( |
@mgreter I don't understand why the perl value would come with quotes in the first place? It seems like we definitely keep that quoted value in the actual strings... shouldn't be hard to expose that to the API, right? Line 1442 in c8885dc
Internally, we use that all over the place... https://github.com/sass/libsass/search?utf8=%E2%9C%93&q=quote_mark |
No, this is wrong. The value of Sass string should have the same equality semantics as the string has in sass itself. Without this, client code will consistently break expected semantics by accidentally considering the quotes as part of the value of the string instead of an aspect of how it is output into the css file.
Agree. The client code should not include the quotes in the value set into the object. It's not hard to do the unquote operation. It's hard to know you should. It's easy to forget and break sass's semantics. |
I'm pretty confused because I don't know if you are talking about strings comming from an AST_Node or by creating one via the C-API. IMO the ones from the parser (AST_Node) will be unquoted already, but the C-API does not unquote it by itself. IMO if we unquote strings in the C-API when Sass_Strings are created, we need to quote anything we want to be taken literally (we may have another quoted string inside a quoted string which will get further unquoted with every new Sass_String instance). @hcatlin yep, we keep that one around, altough we (AFAIR) only use it in a boolean context. But yes, should be pretty easy to add and expose this flag at the C-API level. @chriseppstein I hope I got your point now. You cannot set if the Sass_String you create should be quoted or not when emitted. That should be solved by adding the flag mentioned above (so you could set it to have libsass add quotes when emitting the string). I agree that this was overlooked and it starts to make sense what you have written above and your reference to the output seem to make it clear! |
I'm assuming that he's trying to use the C-API to define a function like Obviously, if we are passing a I definitely agree with @chriseppstein on this one... since we really want it to be easy to take values from Sass->Lib. |
👍 |
@hcatlin if you define such a I just checked this is true, and the ast debug for
The interesting part is |
@mgreter I haven't repro'd what Chris says, but it sounded to me like the Sass_Value passed in did include the quotes, maybe re-attaching them with getValue()? Chris, can you show what you mean in some code that we can write a test based on? |
Ok. It turns out I was mistaken about what was happening here. The quotes from the sass value are indeed stripped off -- this is a good thing. But this API is definitely broken. Using node-sass I wrote a test case like this: it("should not change whether strings passed through are quoted", function (done) {
var input = "#test1 { uquoted: testing(foo); }\n" +
"#test2 { squoted: testing('bar'); }\n" +
'#test3 { dquoted: testing("baz"); }\n';
var options = {
data: input,
functions: {
"testing($value)": function(value) {
console.log(value.getValue());
return value;
}
}
};
var expected = "#test1 { uquoted: foo; }\n" +
"#test2 { squoted: 'bar'; }\n" +
'#test3 { dquoted: "baz"; }\n';
testutils.assertCompiles(options, expected, done);
}); I get the following failure:
And the console prints:
So yes, the quotes are not passed from Sass to the host language. But as @mgreter said, there's no way to return a quoted string back unless you put quotes in the value itself. Furthermore, passing a value through should not change it like it does here. |
Cool, then it looks like @mgreter's patch should fix this... because we can now pass the quoted-ness both directions in a civilized manner (and maintaining the original, if you don't change the Value) |
Agree. This fixes my issues. Thank you @mgreter ❤️! |
Glad we could figure this one out. I was pretty sure we had a misunderstanding somewhere. I'm currently considering, if we really need this option and use autodetection only. At least I'm going to do this in |
I read the API changes, I'm not a direct consumer but it seems good to me. https://github.com/sass/sass/blob/stable/lib/sass/script/value/helpers.rb is the nicest ruby sass API I could devise and it also provides different creation methods for unquoted and quoted strings. |
@chriseppstein I think I know what's happening in node-sass. The API has changed in this regard and I also forgot to adjust for it in the lastest Also for your example I would argue that
Is also a valid result. From my findings, we also only preserve the original quotemarks if the string involved is a static value, which is IMO never true for function arguments. As a side note, it only uses single quotes if the string contains double quotes, but no other single quote. See this example: @function test($a) {
@return $a;
}
div {
foo: test(a);
foo: test('a');
foo: test("a");
foo: test("\"");
} Which compile to this with ruby sass: div {
foo: a;
foo: "a";
foo: "a";
foo: '"';
} |
@mgreter If you meant:
Then we agree. |
I agree that this would be the best way, but IMHO both are semantically the same in css context, but maybe I'm wrong? As I said, to get this behaviour would make it much more complex for me and |
Sass only unquotes strings when they are output via interpolation. Our policy has been to let the author decide when a string is quoted or not, any other behavior requires Sass to have intimate knowledge of what the css context is and that is a direction we actively avoid for future compatibility reasons.
You can make things as simple as they are, but not more simple. This is essential complexity.
Sass's handling of string concatenation (the
Not even Sass does this.
I get why you think it's appealing, but in my experience dumb code with obvious rules is easier for users to work with than smart code when the smart code cannot be guaranteed to be correct. |
That's why I asked :) Two new things I learned with your last comment. Somewhat counter intuitive for me, but that's why I like to see code examples, makes it much easier to understand the issues at hand (specially for non native english people)! I will see if and how I can add this to BTW. I wonder why node-sass hasn't choosen to use the native js data-types for most sass values, since with Null, Boolean, Number, String, Array, Object and Error they seems to have everything ready to be converted to native data types ... so far I only had to differentiate between two list types, which basically only differ by beeing separate classes, inheriting from the base array implementation (in perl-libsass). Now with the quotemark I probably will do the same, since the type of the class is something I can attach easily to the native data type, if I use a reference. In a real object oriented language I would expect to have specific sass value classes, that inherit from the base object classes (and probably also from a base sass_value class, if polymorphysm is possible). |
I'm not a fan of making units seem natural between two languages that are interop, when they aren't. I'm fine mutating on something like SassValue as a thin wrapper on the C-API. It seems to kind of put things in a corner. |
@mgreter They do expose a js class for each type but those classes are written in c++ because it allows them to keep a hidden reference to the libsass struct and to use it as a backing store for the objects' values. It's actually how JS is meant to be used in an embedded way like this. |
@hcatlin units? you lost me. |
@chriseppstein what I mean is I don't understand why a Sass_String is not really a js Maybe give it a thought. If that's technically achievable, I would prefer it that way. Otherwise you probably will not get around to use syntax like Since I talked about code examples, I would like to be able to return this from custom functions: return null; // sass_make_null();
return 2; // sass_make_number(2, 0);
return false; // sass_make_boolean(false);
return 'a b'; // sass_make_qstring("a b"); => autodetect q(uote)mark
return { a: 1, b: 2 }; // sass_make_map(..); => recursive resolve keys, values
return ['a', 3, false, '2px']; // sass_make_list(comma) => recursive resolve values
// this might be against core sass principles?
return '2px'; // sass_make_number(2, 'px')
return '#000'; // sass_make_color(0, 0, 0, 1) I hope you agree with most parts!? IMO node-sass will probably not be able to support native stuff like Sorry for the long post. In the end it got more like a brain-storming, but it documents quite a bit of the sass_value internals from the perl-libsass implementor perspective. Let's see how node-sass can improve in this area and I hope they can manage to bring it to a usefull and good level! But I somehow believe that it is not possible to support |
|
@mgreter The semantics of the strings are different, they cannot be the same type. Specifically, the equality semantics of quoted vs. unquoted strings. Also it would be very strange API to sometimes pass a native type and sometimes pass a wrapper type. It's much more consistent to always pass a sass value that can be unwrapped. This would allow us to implement common methods that are part of a Sass Value contract API. |
Am I correct in understanding this issue is now solved 3.2.1? |
IMO this issue here can be closed. It has been noted that we currently don't expose enough functionallity to implement all sass value related tasks in bindings neatly. It doesn't make much sense to hack all the features together from existing code, since it is scattered around our code base (and even using different input types, ast_nodes vs sass_values). So a refactor seems to be the right way to solve this correctly, which I cannot give any ETA (I currently plan to do this as one major change for 3.3) To be clear: Bindings should be able to implement all OPs themselve, the information is there, but it makes a lot of sense to let libsass do the whole work (so you get the same logic as libsass uses). |
@mgreter, could you please explain when should we use Should we always expect LibSass to callback the CustomFunction with verbatim string (with inclusive quotes as is) and just use Or is it so that we should inspect the string values returned by consumer in wrapper code and then call |
LibSass's SassScript string values include the quotes. This makes implementing Sass's string semantics in a consistent way very awkward.
Sass treats all strings as equal regardless of whether they have quotes or have used single or double quotes. But
stringA.getValue() != stringB.getValue()
if stringA is"foo"
and stringB isfoo
. because the value of stringA includes the double quotes.Instead the string's value should omit the quotes and there should be a string.getQuote() method that either returns a boolean or the quote character.
The text was updated successfully, but these errors were encountered: