sass icon indicating copy to clipboard operation
sass copied to clipboard

[request] str-split function

Open xzyfer opened this issue 9 years ago • 13 comments

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.

xzyfer avatar Jan 05 '16 03:01 xzyfer

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

cimmanon avatar Jan 05 '16 04:01 cimmanon

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.

xzyfer avatar Jan 05 '16 04:01 xzyfer

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.

xzyfer avatar Jan 05 '16 04:01 xzyfer

@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 &

xzyfer avatar Jan 05 '16 04:01 xzyfer

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?)

xzyfer avatar Jan 05 '16 04:01 xzyfer

(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,))

davidkpiano avatar Jan 05 '16 04:01 davidkpiano

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.

chriseppstein avatar Jan 05 '16 17:01 chriseppstein

+1 for a str-split or selector-split function

esr360 avatar Nov 14 '16 20:11 esr360

@nex3 should this also come with a feature check flag?

xzyfer avatar May 04 '18 02:05 xzyfer

Generally we don't add feature flags for functions, since they can be detected with function-exists().

nex3 avatar May 19 '18 19:05 nex3

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 .

xzyfer avatar May 19 '18 19:05 xzyfer

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.

esr360 avatar Jun 19 '18 02:06 esr360

The proposal has landed. We'll keep it open for comment for a month, then land it.

nex3 avatar Aug 29 '22 22:08 nex3

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(), and list.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() and map.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 of selector.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 avatar Oct 28 '22 22:10 nex3

@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.

dvdherron avatar Oct 31 '22 14:10 dvdherron