sass
sass copied to clipboard
[request] str-split function
It would be really useful to have a str-split($string, $delimiter:" ")
function akin to JavaScript, or Ruby.
The primary use case I run into is splitting selector strings i.e.
.foo .bar { /* code */ }
It's rather difficult to get .bar
from &
without boilerplate string functions or importing something like sassdash.
A another use case I run into is remove part of selector. As it stands selector-replace
does not allow you to remove part of a selector (i.e. replace it with ""
or null
). Being able to easily split selector strings would makes this much easier.
Selectors aren't strings. They're lists of lists. If you want to remove or capture part of a selector, you use a loop.
.foo .bar, .one .two {
@each $sel in & {
@debug nth($sel, -1);
}
}
Output:
>> DEBUG: .bar
>> DEBUG: .two
Selectors aren't strings. They're lists of lists.
Yes I understand.
If you want to remove or capture part of a selector, you use a loop.
You're correct.
As it turns out, in my experimentation, where I was mistaken was that even if there is one selector,&
is still a nested list. This is where I was getting tripped up.
i.e.
.foo .bar {
a: length(&); // 1
b: length(nth(&, 1)); // 2
}
I was expecting a
to be b
. I understand why it works this way, it was just a detail I had missed.
I guess, assuming certain constraints on the input string, and only wanting " "
as the $delimiter
you can fake str-split
with nth(parse-selector(".foo .bar"), 1)
. Which granted is the usecase is proposed.
@davidkpiano True. Sorry I wasn't more clear.
As it turns out, in my experimentation, where I was mistaken was that even if there is one selector,& is still a nested list.
.foo .bar {
test: nth(nth(&, 1), 2);
}
note: selector-parse() isn't required when you're dealing with &
With regards to comment on selector-parse
you can split a string on white space.
.foo .bar {
test: nth(nth(selector-parse(".foo .bar"), 1), 2); // .bar
}
I suspect there may be limitations on the types of strings this works with i.e. valid selectors (maybe?)
(Sorry, deleted my comment as it was redundant.)
Maybe a selector-split
function would be more appropriate? Something that splits based on combinators:
$test: selector-split(".foo .bar", " ");
// => ((.foo,), (.bar,))
$test: selector-split(".foo .bar > .baz", ">");
// => ((.foo, .bar), (.baz,))
Something that splits based on combinators
whitespace is a combinator. it is the "descendant combinator".
Anyways, a str-split
function seems generally useful and is easy enough to add.
+1 for a str-split or selector-split function
@nex3 should this also come with a feature check flag?
Generally we don't add feature flags for functions, since they can be detected with function-exists()
.
Makes sense
On Sat., 19 May 2018, 9:02 pm Natalie Weizenbaum, [email protected] wrote:
Generally we don't add feature flags for functions, since they can be detected with function-exists().
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/sass/sass/issues/1950#issuecomment-390425705, or mute the thread https://github.com/notifications/unsubscribe-auth/AAjZWIqGbgmUxzqKV-oW_qnP6xAUo9Qaks5t0GwygaJpZM4G-jlh .
something like this seems like a fairly elegant work around to me:
.foo .bar {
// this spoofs the selectors of `&` into a Sass list
$selectors: str-replace(inspect(&), ' ', ', ');
// selector is now split - do whatever you need to do
...
// when finished, rebuild selector
$selectors: selector-parse($selectors);
}
Sorry if this was already clear from previous comments.
The proposal has landed. We'll keep it open for comment for a month, then land it.
Looking over this again as we start to move towards implementation, I had a thought: should this return a bracketed list?
The main reason to return an unbracketed list is historical precedent. No existing function returns an unbracketed list by default. However, I think this might be a less compelling argument than it seems at first glance. Here are all the functions that currently return lists:
-
list.append()
,list.join()
, andlist.zip()
all explicitly return a list style that matches their inputs, so that doesn't really set a precedent. -
list.slash()
is explicitly intended to return a value that's usable directly as a slash in CSS, so that has a compelling external reason to return an unbracketed list and shouldn't be considered a strong precedent. -
map.keys()
andmap.values()
return unbracketed, comma-separated lists from an input that isn't a list. However, maps can be interpreted as lists, in which case they are unbracketed and comma-separated so this only sort of counts as precedent. -
selector.parse()
returns an unbracketed, comma-separated list as well. However, this is specifically because we want its string representation to match the original selector, so this definitely doesn't count as precedent. -
This leaves
selector.simple-selectors()
as the only built-in function that could just as easily return a bracketed list but still chooses not to do so. And even then, you could make the argument that it's that way to match the output ofselector.parse()
.
Given that the precedent is not actually very strong for returning unbracketed lists, I think we should consider making all new functions that return lists default to returning bracketed lists unless there's a specific reason not to. Similarly to how we default to returning quoted rather than unquoted strings, more explicit delineators of values eases debugging by making it clearer to users what types they're working with.
To generalize this principle: Sass design should encourage code that's not directly emitting CSS to use more explicit syntax for constructs that CSS natively supports implicit syntax for.
@dvdherron What do you think?
@nex3 I think it makes perfect sense to have all functions that return lists default to returning bracketed lists. I'll make another draft of the proposal with the relevant changes. Will have a draft of tests ready soon too.