-
Notifications
You must be signed in to change notification settings - Fork 185
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
Add methods to scalafix api to be able to retrieve patches #1223
Conversation
187005b
to
412e463
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks very good to me!
scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixArgumentsImpl.scala
Outdated
Show resolved
Hide resolved
scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixOutputtImpl.scala
Outdated
Show resolved
Hide resolved
scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixOutputtImpl.scala
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixOutput.java
Outdated
Show resolved
Hide resolved
@@ -158,7 +160,7 @@ class MavenFuzzSuite extends AnyFunSuite with DiffAssertions { | |||
.withPaths(paths.asJava) | |||
.withRules(List(rule).asJava) | |||
.withClasspath(classfiles.asJava) | |||
// .withMode(ScalafixMainMode.CHECK) | |||
.withMode(ScalafixMainMode.CHECK) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are 3 modes. this one only checks and doesn't write to file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I finally found some time to look at this promising PR! I only focused on the API declaration & tests without looking at the implementation to keep a naive consumer standpoint as much as possible.
scalafix-tests/unit/src/test/scala/scalafix/tests/interfaces/ScalafixArgumentsSuite.scala
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixResult.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixOutput.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixOutput.java
Outdated
Show resolved
Hide resolved
@@ -205,4 +205,14 @@ ScalafixArguments withToolClasspath( | |||
*/ | |||
ScalafixError[] run(); | |||
|
|||
/** | |||
* | |||
* Compared to {@link #run()}, it doesn't write the diff to the requested file, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
run()
does not necessarily have that side effect, it depends on the ScalafixMainMode
.
I guess only a few modes make sense with this new method, I think it should be documented.
@@ -0,0 +1,9 @@ | |||
package scalafix.interfaces; | |||
|
|||
public interface ScalafixPatch { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to expose a patch as single unit? it's coupled to a file and usually use together with others, so instead I would take a kind or an id as parameter on the apply
overloads (since I guess the only purpose of getting them is to filter before applying them?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
discussed offline: this PR is driven by a need to try to apply some patches until files reach a certain state (via previewSelectivePatches
), but we don't know beforehand which patch will be tried, so we need to be able to address them individually as we try and iterate.
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixOutput.java
Outdated
Show resolved
Hide resolved
import java.nio.file.Path; | ||
import java.util.Optional; | ||
|
||
public interface ScalafixResult { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was not expecting to see side-effect methods on a Result
object, which seems like the end of the process. I can't really find the right term for it, but I was thinking about something along
public interface ScalafixResult { | |
public interface ScalafixEvaluation { |
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixArguments.java
Outdated
Show resolved
Hide resolved
* Compared to {@link #run()}, it doesn't write the diff to the requested file, | ||
* but instead return the list of patches and diagnostics, which can be written to file using | ||
* {@link ScalafixResult#writeResult()}}. | ||
* withCallback doesn't have any effect, all diagnostics are directly stored in ScalafixResult |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we fail or feed both rather than ignoring?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if the name is evaluate, it make sense that the Mode or callback are not called ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should fail if evaluate
is called on a ScalafixArguments
that was explicitly set a mode or a callback, since they have no effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Evaluate should not fail from my point of view. It's a new way to interact with scalafix, that requires fewer arguments.
scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixOutputtImpl.scala
Outdated
Show resolved
Hide resolved
scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixOutputtImpl.scala
Outdated
Show resolved
Hide resolved
scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixOutputtImpl.scala
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixResult.java
Outdated
Show resolved
Hide resolved
scalafix-cli/src/main/scala/scalafix/internal/interfaces/ScalafixOutputtImpl.scala
Outdated
Show resolved
Hide resolved
The interfaces look like that
|
|
||
public interface ScalafixPatch { | ||
/** | ||
* Can be RemoveGlobalImport, RemoveImportee, AddGlobalImport, AddGlobalSymbol, ReplaceSymbol |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it looks like an enum
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are right, but it's not corresponding to all possible cases in Patch sealed trait.
Creating an Enum may add too much constraint for this field, especially that the only use case that I see right now is applying only patches relate to Import
My opinion is that String is enough for now, but maybe later we can add an enum.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the only use case that I see right now is applying only patches relate to Import
Can you elaborate the use-case? maybe we don't need to expose the path class hierarchy as-is, but just what we need - I am concerned we are leaking internals here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we apply a rule that looks like explicitResultType: When a type is added, sometimes an import is also added.
We would like to be able to apply all import patches first.
I understand your concern. We can maybe expose 3 kinds: ReplaceSymbol, AddImport, RemoveImport?
I can also remove kind
until it's clearer what we would need, and add this in a second PR later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would like to be able to apply all import patches first.
I am not very familiar with the patch internals, but doesn't https://scalacenter.github.io/scalafix/docs/developers/patch.html#atomic allow to group patches that should be applied "together" - maybe we need to expose patches as group of atomic patches?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can also remove kind until it's clearer what we would need, and add this in a second PR later.
That would probably be better as there is already a lot of surface in the new APIs. ScalafixPatch
would then become an empty trait for now, highlighting the fact that it's used for its identify (to iterate and converge on a proper state), rather than for its attributes.
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixEvaluation.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixEvaluation.java
Outdated
Show resolved
Hide resolved
* | ||
* @return the output content after evaluating scalafix if no error. The original file will stay unchanged. | ||
*/ | ||
Optional<String> getOutputFixed(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
output
was not really clear to me since it's the name of the class itself, while I think here it means the content of the path after the fix is applied. Also, the fix does not really show that this is a dry-run version of applyPatches
. What about
interface ScalafixTargetEvaluation {
Path getTarget();
Optional<String> previewPatches();
(we should stick to one wording for file/target/path to designate what scalafix runs on by the way - not sure what's the best or what's already used)
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixArguments.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixEvaluation.java
Outdated
Show resolved
Hide resolved
/** | ||
* @return A message error is provided if the run is not successful | ||
*/ | ||
Optional<String> getMessageError(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how does this relate to the getErrors
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a global message error that can be empty. Errors are a concatenation of code errors that result from a merge function.
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixEvaluation.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixEvaluation.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixOutput.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixOutput.java
Outdated
Show resolved
Hide resolved
ScalafixError[] applyPatches(); | ||
|
||
/** | ||
* @param patches the patches should belong to this scalafixOutput's patches. If not, no patch will be applied to the file. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean by "if not"?
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixOutput.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixOutput.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixOutput.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixOutput.java
Outdated
Show resolved
Hide resolved
* be able to apply selectives patches
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a few last comments on the API, but overall LGTM. I haven't looked at the implementation, but this can/will evolve as you use it I guess, so please don't wait for me.
I am still a bit concerned about the opaque ScalafixPatch
object that is exposed to be able to apply/preview a random subset of patches, but I don't have a better idea to tackle this use-case.
* Similar to {@link #run()}, but without any side effects on the source files. Via the returned {@link ScalafixEvaluation}, | ||
* for each file, diagnostics can be inspected, and patches can be previewed and applied. | ||
* <p> | ||
* Incompatible with {@link #withMainCallback} and {@link #withMode}. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can this be tested? I don't see a check in the implementation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I will add test for this.
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixFileEvaluation.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixFileEvaluation.java
Outdated
Show resolved
Hide resolved
scalafix-interfaces/src/main/java/scalafix/interfaces/ScalafixFileEvaluation.java
Outdated
Show resolved
Hide resolved
…o account when evaluate is called
Thank you for all reviews. I will merge it now. |
I added a ScalafixResult that contains:
Applying patches still consist on writing the file in the desired Path if there is a diff.
You can also apply selective patches if needed. The idea is to enable another project that needs this feature.