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 idea: modifier/node interpolator #401

Open
kubukoz opened this issue Sep 29, 2024 · 4 comments
Open

Feature idea: modifier/node interpolator #401

kubukoz opened this issue Sep 29, 2024 · 4 comments

Comments

@kubukoz
Copy link
Contributor

kubukoz commented Sep 29, 2024

Sometimes you just want to do:

div(
  s"today is $myDateSignal and I have $appleCountSignal apples"
)

but what you actually have to do is:

div(
  "today is ", myDateSignal, " and I have ", appleCountSignal, " apples"
)

Alternatively, I suppose you can combine signals:

div(
  (myDateSignal, appleCountSignal).mapN { (date, apples) => 
    s"today is $date and I have $apples apples"
  }
)

but it's still a bit cumbersome, works only with text nodes and will result in the entire text updating, rather than just the small pieces that change.

Perhaps it would make it worth adding an interpolator like nodes"..." which will serve as a Modifier-based Show?

@kubukoz
Copy link
Contributor Author

kubukoz commented Sep 29, 2024

This seems to be doable in userland so maybe I'll try making it first to see if it's even possible with the current API...

@kubukoz
Copy link
Contributor Author

kubukoz commented Sep 29, 2024

ok, hear me out:

type IntersperseStrings[T <: Tuple] <: Tuple =
  T match {
    case EmptyTuple => String *: EmptyTuple
    case (t *: ts)  => String *: t *: IntersperseStrings[ts]
  }

inline def intersperseStrings[T <: Tuple](t: T, strings: Seq[String]): IntersperseStrings[T] =
  inline scala.compiletime.erasedValue[T] match {
    case _: EmptyTuple => strings.head *: EmptyTuple
    case _: (head *: tail) =>
      inline t match {
        case v: (`head` *: `tail`) =>
          val (h *: t) = v
          strings.head *: h *: intersperseStrings(t, strings.tail)
      }
  }

extension (sc: StringContext) {

  inline def nodes[M <: Tuple, E <: HtmlElement[IO]](
    arg: M
  )(
    using Modifier[IO, E, M]
  ): IntersperseStrings[M] = {
    StringContext.checkLengths(arg.toList, sc.parts)
    intersperseStrings(arg, sc.parts.map(StringContext.processEscapes))
  }

}


  def go(myDateSignal: Signal[IO, String], appleCountSignal: Signal[IO, String]) = div(
    nodes"today is $myDateSignal and I have $appleCountSignal apples, let me say that again $appleCountSignal apples"
  )


  def go2(myDateSignal: Signal[IO, String], appleCountSignal: Signal[IO, String]) = div(
    nodes"today is $myDateSignal and I have $appleCountSignal apples"
  )

Probably missing some edge cases.

@kubukoz
Copy link
Contributor Author

kubukoz commented Sep 29, 2024

Need a special case for a no-tuple scenario. Also, it doesn't work if you actually try to interpolate a tuple :/

nodes"today is ${(myDateSignal, "foo")}"

The interpolator will see this as one tuple of length 2, and there are 2 parts in the StringContext - the length check will fail.

@armanbilge
Copy link
Owner

I like this idea — similar to what Svelte can do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants