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

Support for IntelliJ IDEA? #33

Open
saamalik opened this issue Dec 22, 2016 · 8 comments
Open

Support for IntelliJ IDEA? #33

saamalik opened this issue Dec 22, 2016 · 8 comments

Comments

@saamalik
Copy link

saamalik commented Dec 22, 2016

Hi,
This truly is a fantastic library which enables some really removes a bunch of boilerplate code! I was wondering if there was any plan (now or later) to support IntellIJ IDEA support so we don't see those squiggly error lines (cannot resolve symbol)?

IntelliJ added support for plugins using Macros to write their own plugins: https://blog.jetbrains.com/scala/2015/10/14/intellij-api-to-build-scala-macros-support/

Thanks!

@adamw
Copy link
Member

adamw commented Dec 22, 2016

Quicklens macros are "blackbox", which means they return precise types, so this should be already IDE-friendly, with autocomplete etc., as long as there's the import import com.softwaremill.quicklens._.

Could you maybe give an example of what is incorrectly highlighted by IntelliJ?

@aneeshde
Copy link

Using your own examples (street, address, person),

import com.softwaremill.quicklens._

case class Street(name: String)
case class Address(street: Option[Street])
case class Person(addresses: List[Address])

val person = Person(List(
  Address(Some(Street("1 Functional Rd."))),
  Address(Some(Street("2 Imperative Dr.")))
))
person.modify(_.addresses.at(1).street.each.name).using(_.toUpperCase)

The last statement yields errors at .at ("Cannot resolve symbol at") and toUpperCase("Cannot resolve symbol toUpperCase")

It executes perfectly and without issue, but IntelliJ does NOT like the syntax.

The issue has also been added to IntelliJ's bug tracker.

@adamw
Copy link
Member

adamw commented Dec 23, 2016

Ah true, thanks! Though the issue is with IntelliJ's type checker, doesn't really involve macros.

@DaniRey
Copy link

DaniRey commented Nov 1, 2018

The problem is not with the macro but rather with the implicit resolution. When we use

implicit def listQuickLensFunctor[A]: QuicklensFunctor[List, A] =
    new QuicklensFunctor[List, A] {
      override def map(fa: List[A])(f: A => A): List[A] = fa.map(f)
}

instead of

implicit def traversableQuicklensFunctor[F[_], A](implicit cbf: CanBuildFrom[F[A], A, F[A]], ev: F[A] => TraversableLike[A, F[A]]) =
    new QuicklensFunctor[F, A] {
      override def map(fa: F[A])(f: A => A) = fa.map(f)
}

then Intellij is happy to. I will file an according bug with JetBrains.

If a quicklens user adds my function to his code, the user has to be more specific with the imports. Because if the compiler sees two implicits which could be used, it cannot choose which one to use and fails.

For a basic example the following imports should be fine.

import com.softwaremill.quicklens.{ModifyPimp, QuicklensEach, QuicklensFunctor}

@ninadvps
Copy link

ninadvps commented May 2, 2019

was this ever implemented in intellij?

@soujiro32167
Copy link

Came here with that question too...

@DaniRey
Copy link

DaniRey commented Nov 6, 2020

Here https://youtrack.jetbrains.com/issue/SCL-14526 you can find the bug which I opened. It wasn't resolved yet.

If you can upgrade to Scala 2.13 the problem will most probably go away. The reason is, that the collection implementation is more straight forward (i.e. CanBuildFrom isn't used anymore). At least we could remove our workaround in our code base after upgrading to 2.13.

@soujiro32167
Copy link

Thank you, @DaniRey. I figured out what the problem was: these was another .each extension method coming from sttp.tapir.internal:

trait ModifyMacroFunctorSupport {
  implicit class ModifyEach[F[_], T](t: F[T])(implicit f: ModifyFunctor[F, T]) {
    @compileTimeOnly(canOnlyBeUsedInsideModify("each"))
    def each: T = sys.error("")
  }

  trait ModifyFunctor[F[_], A] {
    @compileTimeOnly(canOnlyBeUsedInsideModify("each"))
    def each(fa: F[A])(f: A => A): F[A] = sys.error("")
  }

  implicit def optionModifyFunctor[A]: ModifyFunctor[Option, A] =
    new ModifyFunctor[Option, A] {}

  private[tapir] def canOnlyBeUsedInsideModify(method: String) =
    s"$method can only be used inside ignore"
}

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

6 participants