-
Notifications
You must be signed in to change notification settings - Fork 72
Conversation
}.asPatch | ||
removeTokensPatch + addTokensPatch | ||
}.asPatch | ||
|
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 had to do this at the token level as suggested by @olafurpg due to a limitation in scalafix that is explained here: https://gitter.im/scalacenter/scalafix?at=59ff60b35a1758ed0f90981b
xs.lazyZip(ys).lazyZip(zs) | ||
coll(1).lazyZip(coll(2)).lazyZip(coll(3)) | ||
List(1, 2, 3).lazyZip(Set(1, 2, 3)).lazyZip(LazyList.from(1)) | ||
} |
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.
Apparently there isn't a consensus on the syntax for lazyZip
in the discussions in #223. I chose xs.lazyZip(ys).xxx(f)
over (xs lazyZip ys).xxx(f)
. Please let me know if you prefer the second option ;)
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 would go for a.lazyZip(b)
, it's harder to get the parentheses right for infix operators. For example, (a, (b, c)).zipped
becomes a lazyZip ((b, c))
under -Yno-adapted-args. You might also have to add wrapping parentheses depending on the parent tree node (a lazyZip b).c
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.
Nice work!
xs.lazyZip(ys).lazyZip(zs) | ||
coll(1).lazyZip(coll(2)).lazyZip(coll(3)) | ||
List(1, 2, 3).lazyZip(Set(1, 2, 3)).lazyZip(LazyList.from(1)) | ||
} |
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 would go for a.lazyZip(b)
, it's harder to get the parentheses right for infix operators. For example, (a, (b, c)).zipped
becomes a lazyZip ((b, c))
under -Yno-adapted-args. You might also have to add wrapping parentheses depending on the parent tree node (a lazyZip b).c
def replaceTupleZipped(ctx: RuleCtx) = | ||
ctx.tree.collect { | ||
case tupleZipped(t @ Term.Select(Term.Tuple(args), _)) => | ||
val argTokens = args.flatMap(_.tokens).toSet |
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.
tupleElementTokens
is more descriptive than argTokens
ctx.tree.collect { | ||
case tupleZipped(t @ Term.Select(Term.Tuple(args), _)) => | ||
val argTokens = args.flatMap(_.tokens).toSet | ||
val removeTokensPatch = t.tokens.filter(!argTokens.contains(_)).map(ctx.removeToken).asPatch |
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.
nonTupleElementTokens
t.tokens.filterNot(tupleElementTokens)
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.
PS, you might be removing comments here, I would keep comments
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 tokens that need to be removed a leading (
, trailing )
, commas ,
and .zipped
.
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.
@olafurpg yeah, you are right. Everything that is not a tuple element gets removed. I'm changing the approach to remove only the necessary tokens.
Also, I think it would be nice to get rid of empty spaces between the comma and the first non-empty space token that follows it. Otherwise the output will be something like:
xs.lazyZip( ys)
val removeTokensPatch = t.tokens.filter(!argTokens.contains(_)).map(ctx.removeToken).asPatch | ||
val addTokensPatch = args.tail.map { arg => | ||
val tokens = arg.tokens | ||
ctx.addLeft(tokens.head, ".lazyZip(") + ctx.addRight(tokens.last, ")") |
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 addRight/addLeft
overloads for trees: ctx.addLeft(arg, ".lazyZip(") + ctx.addRight(arg, ")")
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.
cool, I missed that
object Collectionstrawman_v0_Tuple2Zipped { | ||
def zipped(xs: List[Int], ys: List[Int]): Unit = { | ||
(xs, ys).zipped | ||
(coll(1), coll(2)).zipped |
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 recommend you add at least one test with comments inside of the tuple, the biggest reason scalafix exposes low-level token APIs is so that trivia like comments and formatting can be preserved.
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.
done
c9f04b0
to
91ff009
Compare
case _ => Patch.empty | ||
} | ||
|
||
removeTokensPatch + replaceCommasPatch | ||
}.asPatch | ||
|
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.
Handling comments makes the rewrite a lot more complicated compared to the previous version 😅.
Note that in lines 153-155 I'm preserving the original closing )
in order to allow comments on the right side of the last tuple element to be enclosed by the correct parenthesis. Similar care needs to be taken in regards to comments on the right side of a middle triple element (that's the reason for the additional lines in line 174-175).
@olafurpg thank you for your awesome suggestions! I updated the PR addressing all the comments. Could you take a look at it again? |
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.
Manually handling comments is indeed tricky, and this PR does a great job at it LGTM 👍
Things may change soon with a new tree printer I'm working on that has the ability to attach comments to synthetic tree nodes https://github.com/olafurpg/scala-syntax/pull/10/files#diff-e7e24ee6fee3b6a67316ebd60f6d7fd8R7 that opens possibilities for syntax preserving tree transforms.
ctx.removeToken(zipped) | ||
}).asPatch | ||
|
||
def removeSurroundingWhiteSpaces(tk: Token) = |
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.
Utilities for trimming whitespace are severely missing in scalafix. I'd love to hear if you have idea how this can be generalized
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 would definitely be handy to have such utility in the api. Maybe something like ctx.trimLeft(token)
/ ctx.trimRight(token)
or ctx.removeTokens(tokenList.spacesBefore(token))
/ ctx.removeTokens(tokenList.spacesAfter(token))
. I can put more thoughts on this and create a PR in scalafix with a proposal.
zipped <- name.tokens.headOption | ||
closeTuple <- ctx.tokenList.leading(zipped).find(_.is[Token.RightParen]) | ||
openTuple <- ctx.matchingParens.open(closeTuple.asInstanceOf[Token.RightParen]) | ||
maybeDot = ctx.tokenList.slice(closeTuple, zipped).find(_.is[Token.Dot]) |
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.
Utilities to find dots is missing in scalafix, I've written something similar to this many times 😅
One thing I realized that this PR is missing is replacing the old types in case someone assigned the zipped result to a explicitly typed variable or parameter before invoking operations on it (not sure if this really happens in practice as the result would be rather verbose): Replacing the symbols wouldn't be enough I guess. The new types have fewer type parameters. |
I don’t think lots of people have explicitly used the |
I agree with @julienrf I would not worry about it. If you want you can report a warning if |
ok, I'm going to keep the PR as is. @julienrf you can merge the PR if you are happy with it. |
Thanks a lot @marcelocenerine ! |
Enhancing the scalafix rule to rewrite
(xs, ys).zipped
toxs.lazyZip(ys)
(likewise for triples).There is one caveat here:
LazyZipN.filter
is not equivalent toTupleNZipped.filter
. This will likely require manual intervention to be fully fixed. Choosing not to rewrite(xs, ys).zipped.filter(p)
would leave it in a broken state anyway because aTupleNZipped
cannot be created with iterables from strawman./cc @olafurpg