Skip to content
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

[FEATURE] range_select function #622

Closed
ModProg opened this issue Nov 24, 2022 · 10 comments
Closed

[FEATURE] range_select function #622

ModProg opened this issue Nov 24, 2022 · 10 comments
Labels
enhancement New feature or request

Comments

@ModProg
Copy link
Contributor

ModProg commented Nov 24, 2022

Description of the requested feature

A function that can take a json array and select the first matching entry.

I could imagine two things, a generic one that would be more useful but would also definitly increase complexity by quite a lot, maybe too much (would require something closure like):

select("[1,2,3]","value > 1") ; would return 2

More narrow solution and still quite powerful, take an array of tuples with the first being the range and the second being the returned value:
An example of a Wi-Fi strength visualization. It will select the first entry where the range contains wifi_strength.

ranged_select('[["-45..", "▂▄▆█"], ["-60..", "▂▄▆_"], ["-70..", "▂▄__"], ["-80..", "▂___"], ["..", "____"]]', wifi_strength)

Proposed configuration syntax

Add a new function to expressions range_select that takes an array and a value.

The value is a number.
The array contains tuples, with the first entry being a Range expression and the second being an arbitrary value that will be returned (array, object, string, ...).

My proposed syntax for Range is Rusts: start_inclusive..end_exclusive and start_inclusive..=end_exclusive, with both ends being optional to create an open range, matching any value higher or lower than the specified limit, or any value if no limits are specified.

There are also other possible range syntaxes [start,end], [start,end_exclusive), [start, end_exclusive[ or start - end or we could even use <2, 4<= for open ranges, I'm not fixated on one, as long as open ranges and exclusive ranges are supported.

Additional context

It is currently possible to implement something like this, but it requires using a bunch of ? expressions, which make it very complex and hard to read.

An example of the current syntax: https://dharmx.is-a.dev/eww-powermenu/#battery

(defwidget _battery [battery status one two three
                    four five six seven charge]
  (box :class "bat-box" :space-evenly false :spacing 8
    (label :text {status == 'Charging' ? charge :
      battery < 15 ? seven :
        battery < 30 ? six :
          battery < 45 ? five :
            battery < 60 ? four :
              battery < 75 ? three :
                battery < 95 ? two : one})))
@ModProg ModProg added the enhancement New feature or request label Nov 24, 2022
@ModProg

This comment was marked as outdated.

@ModProg

This comment was marked as outdated.

@ModProg
Copy link
Contributor Author

ModProg commented Nov 24, 2022

Actual working solution I'm using for now:

(defwidget select_ranged [map value ?template]
  (box
    (for entry in map
      (literal :visible {
                         (matches(entry[0], "-?\\d+\\.\\.-?\\d+") && (search(entry[0], "-?\\d+")[0] == "null" ? 0 : search(entry[0], "-?\\d+")[0]) <= value && (search(entry[0], "-?\\d+")[1] == "null" ? 0 : search(entry[0], "-?\\d+")[1]) > value) ||
                         (matches(entry[0], "-?\\d+\\.\\.=-?\\d+") && (search(entry[0], "-?\\d+")[0] == "null" ? 0 : search(entry[0], "-?\\d+")[0]) <= value && (search(entry[0], "-?\\d+")[1] == "null" ? 0 : search(entry[0], "-?\\d+")[1]) >= value) ||
                         (matches(entry[0], "-?\\d+\\.\\.$") && (search(entry[0], "-?\\d+")[0] == "null" ? 0 : search(entry[0], "-?\\d+")[0]) <= value) ||
                         (matches(entry[0], "^\\.\\.-?\\d+") && (search(entry[0], "-?\\d+")[0] == "null" ? 0 : search(entry[0], "-?\\d+")[0]) > value) ||
                         (matches(entry[0], "^\\.\\.=-?\\d+") && (search(entry[0], "-?\\d+")[0] == "null" ? 0 : search(entry[0], "-?\\d+")[0]) >= value)}
             :content {template == ""? entry[1] : replace(template, "\\{}", entry[1])}))))

The template is there as it is not a function but a widget and I therefor cannot wrap the returned value in another widget otherwise.

ModProg added a commit to ModProg/eww that referenced this issue Nov 26, 2022
@ModProg
Copy link
Contributor Author

ModProg commented Nov 26, 2022

I implemented a proof of concept here: #628

@ModProg
Copy link
Contributor Author

ModProg commented Nov 30, 2022

This could be extended to support non-numeric ranges/values as well.
I imagine a set of conditions that could work like so:

  • start?..(=?end)? a range (open, exclusive/inclusive) proposed syntax matches rusts
  • (==|(<|>)=?) value compares the input as lhs using the operator to the value (maybe allow both orders, i.e. value <= as well)
  • /regex/ compares the input to the regex
  • condition_a (&&|\|\|) condition_b matches if the combined condition is true
  • () parenthesis can be used for precedence

Alternative syntaxes

  • [start,end], [start,end_exclusive), [start, end_exclusive[ or start - end
  • {} > 5 to mark where to place the compared value
  • matches("regex")
  • use , for and, and ; for or

@oldwomanjosiah
Copy link
Contributor

I like this idea! In the long run, i wonder if it would make sense to have a case {x} of {pattern} => {value} {pattern} {value} ... syntax.

@ModProg
Copy link
Contributor Author

ModProg commented Dec 12, 2022

I like this idea! In the long run, i wonder if it would make sense to have a case {x} of {pattern} => {value} {pattern} {value} ... syntax.

Would be interesting to consider syntax options. I tried to do this without adding a new language feature, but having actual syntax for this would maybe also help for readability.

Rust-like match would also be an option

match value {
  == "value" => smth,
  /regex/ => smth,
  0..=10 => smth
}

@elkowar
Copy link
Owner

elkowar commented Jan 5, 2023

This kind of plays into a bigger problem I have with the current simplexpr syntax and language... I kinda of really want to have lambdas, but simultaneously feel like it's already grown wayyy further than I should have let it, and adding higher order functions would make it even worse. I get that this sort of feature would be useful, but,... I'll be very careful adding any sort of magic syntax like this, for now, in hopes of finding a good general solution in the future

@ModProg
Copy link
Contributor Author

ModProg commented Jan 6, 2023

Yeah that's why I proposed the purely string based function that would not be adding a new syntax to the simpleexpr language.

@elkowar
Copy link
Owner

elkowar commented Feb 25, 2023

I now added a function jq that allows jq-style json processing, and should also support the string processing functionality there, through the slice operation .[2:10]

@elkowar elkowar closed this as completed Mar 5, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants