-
Notifications
You must be signed in to change notification settings - Fork 46
Organize Imports in Blocks (DefDef) and Templates avoiding tree printer #144
Organize Imports in Blocks (DefDef) and Templates avoiding tree printer #144
Conversation
add Martin Delemotte's test object AnObject {
val a = 3
}
object Test {
def test() : Int = {
import AnObject._
a
}
} |
@wpopielarski did you consider comments (see https://scala-ide-portfolio.assembla.com/spaces/scala-ide/tickets/1001848-automatic-import-deletes-comments-between-import-statements/details#)? I think we have a regression here, as organizing imports on /*
* Please let me live!
*/
import java.util.Date
class Bug {
} now removes the comment at the start of the file. You might want to take a look at |
Generally I don't touch in this PR imports in package scope. So I don't see 2016-03-18 13:56 GMT+01:00 Matthias Langer [email protected]:
|
Hmm, I thought one of the big changes of this PR is that Organize Imports no longer uses tree printing... this would also affect package level imports, or am I wrong? |
This test @Test
def shouldNotRemoveComments() = new FileSet {
"""
/*<-*/
package test
trait Bug {
import java.util.Date
import java.util.ArrayList
// HELP
import scala.util.Try
import scala.concurrent.Future
val d: Date
val l: ArrayList[Int]
val t: Try[Int]
val f: Future[Double]
}
""" becomes {
"""
/*<-*/
package test
trait Bug {
import java.util.ArrayList
import java.util.Date
import scala.concurrent.Future
// HELP
import scala.util.Try
val d: Date
val l: ArrayList[Int]
val t: Try[Int]
val f: Future[Double]
}
"""
}
} applyRefactoring organizeWithTypicalParams fails for me, as the comment is removed. |
You're correct. I talked about it today morning with Simon and Iulian and 2016-03-18 14:35 GMT+01:00 Matthias Langer [email protected]:
|
I see
|
oh man RangePosition is funny, this guy fails: @Test
def shouldRemoveDuplicatedImportFromDefWithComment_v2() = new FileSet {
"""
/*<-*/
package test
object AnObject {
val a = 3
val b = 4
}
object Test {
import AnObject.b
def test(): Int = {
// comment for a
import AnObject.a
// comment for b
import AnObject.b
b
}
}
""" becomes {
"""
/*<-*/
package test
object AnObject {
val a = 3
val b = 4
}
object Test {
import AnObject.b
def test(): Int = {
b
}
}
"""
}
} applyRefactoring organizeWithTypicalParams
it wants to be becomes {
"""
/*<-*/
package test
object AnObject {
val a = 3
val b = 4
}
object Test {
import AnObject.b
def test(): Int = {
//comment for a
b
}
}
"""
}
} applyRefactoring organizeWithTypicalParams```
add it can make sense because first comment can refer to whole block of imports |
Well, the fact that the comment is removed is not a regression... it also happens without this PR; I just filed a bug: https://scala-ide-portfolio.assembla.com/spaces/scala-ide/tickets/1002671 As for range positions: Unfortunately they are not always right :-/ |
As for reviewing the code itself: Can you give me a brief overview of the involved classes/traits and how they interact? This is a rather big PR.... |
@mlangc sure
Design decision:
Observation:
Implementation:
I hope it gives glib entry to PR |
I just tried "Organize Imports" on |
def printImport(imp: Global#Import): String = { | ||
import global._ | ||
val RenameArrow = " => " | ||
val prefix = source.content.slice(imp.pos.start, imp.pos.end).mkString.reverse.dropWhile { _ != '.' }.reverse |
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 breaks with
import scala.util.{Try => `Böse....`}
import scala.concurrent./*Belze.Bub*/Future
Consider using SourceWithMarker
here, like (just a sketch to give you the idea)
val srcAtImportEnd = SourceWithMarker.atEndOf(imp.pos.asInstanceOf[RangePosition])
val srcAtLastPoint = srcAtImportEnd.moveMarkerBack(until('.', skipping = (comment | literalIdentifier)))
assert(srcAtLastPoint.currentOption == Some('.'))
val prefix = source.content.slice(imp.pos.start, srcAtLastPoint.marker + 1).mkString("")
Note that I've tested this with the latest SourceWithMarker
version from PR #141 that includes a significant improvement (backtracking), but I think this should also work with the old version.
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 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 know that this is hell problem, but do we really need to support this devilishly evil comment example (/*Belze.Bub*/
)? Just a question :). Is it common practice to write code in this manner or we just want to as strict as possible? Anyway I try Movements.util
anyway
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.
Well, /*Belze.Bub*/
is indeed a very evil comment ;-)... but what about
import scala.collection./*immutable.*/Seq
I'm sure someone already did it.... Also, take a look at http://doc.akka.io/api/akka/2.4.2/?#akka.http.scaladsl.model.MediaTypes$. Thus I think doing a string search for the last "."
is not an option 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.
ok, thanks a lot for your delving the problem, I didn't realize that it is so common. So generally looks that I need to wrap imports in something conveying print information.
@mlangc I get than that what we need is something like |
assert(imports.nonEmpty) | ||
val source = imports.head.pos.source | ||
def printImport(imp: Global#Import): String = { | ||
import global._ |
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.
problem with this import global._
lays in the way how tree traverser discovers that given type comes from given import. Looks like it is not perfect. This piece of code was not touched by me. But it is bug to fix so I try to face it.
import scala.tools.refactoring.common.TextChange | ||
import scala.util.Properties | ||
|
||
case class Region private (imports: List[Global#Import], owner: Global#Symbol, startPos: Global#Position, endPos: Global#Position, |
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 class can be private[oimports]
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.
not sure, I would allow to leak it. Maybe other packs could use it? If you insist I make it pack private.
Refer to this link for build results (access rights to CI server needed): https://jenkins.scala-ide.org:8496/jenkins/job/ghprb-scala-refactoring-validator/272/ |
(imp, usedSelectors) | ||
}.collect { | ||
case (imp, selectors @ h :: _) => imp.copy(selectors = selectors).setPos(imp.pos) | ||
}.toList |
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.
+1
@wpopielarski All right, adding a switch for the new behaviour is fine for me too. I'd however suggest to be conservative at the beginning and not enable it by default. |
Refer to this link for build results (access rights to CI server needed): https://jenkins.scala-ide.org:8496/jenkins/job/ghprb-scala-refactoring-validator/291/ |
Adds settings to `OrganizeImportsPreferencePage` to control featue implemented by PR scala-ide/scala-refactoring#144
Refer to this link for build results (access rights to CI server needed): https://jenkins.scala-ide.org:8496/jenkins/job/ghprb-scala-refactoring-validator/292/ |
Refer to this link for build results (access rights to CI server needed): https://jenkins.scala-ide.org:8496/jenkins/job/ghprb-scala-refactoring-validator/295/ |
There are some tests (like |
(t, onlyDifferentChildren) match { | ||
case (t: Block , (orig, changed) :: Nil) if changed.pos == NoPosition => | ||
replaceSingleDef(orig, changed) ++ drillDownChildrenAndCollectChangesWithPosition(t) | ||
replaceSingleDef(orig, changed) |
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.
👍
Unfortunately organizing imports on |
@@ -407,7 +407,8 @@ abstract class OrganizeImports extends MultiStageRefactoring with TreeFactory | |||
class RefactoringParameters( | |||
val importsToAdd: List[(String, String)] = Nil, | |||
val options: List[Participant] = DefaultOptions, | |||
val deps: Dependencies.Value = Dependencies.RemoveUnneeded) | |||
val deps: Dependencies.Value = Dependencies.RemoveUnneeded, | |||
val organizeLocalImports: Boolean = true) |
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.
@wpopielarski if you enable this by default (against my suggestion from above - but I don't insist), please make sure that you don't forget about making this configurable in the IDE. This will also help with debugging, as we can then see very quickly if bugs go away as we enable/disable this.
@fommil This is something ENSIME might care about.
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.
here is PR in scala-ide to toggle feature: scala-ide/scala-ide#1089
So long as local imports are not being moved away from their location, I think it sounds good |
|
||
private def cutPrefixSuffix(imp: Global#Import, source: SourceFile): (String, List[String]) = { | ||
val printedImport = source.content.slice(imp.pos.start, imp.pos.end).mkString | ||
val prefixPatternWithCommentInside = """import (((\/\*.*\*\/)*(\w|\d|_|-)+(\/\*.*\*\/)*)\.)+(\/\*.*\*\/)*""".r |
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 is this supposed to match?
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.
import \*comment.comment*\org.acme\*comment*\.
import org.acme.\*comment.comment*\acne.\*comment.comment*\
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 doesn't match the first test case:
scala> val x = """import (((\/\*.*\*\/)*(\w|\d|_|-)+(\/\*.*\*\/)*)\.)+(\/\*.*\*\/)*""".r
x: scala.util.matching.Regex = import (((\/\*.*\*\/)*(\w|\d|_|-)+(\/\*.*\*\/)*)\.)+(\/\*.*\*\/)*
scala> x.findFirstIn("""import \*comment.comment*\org.acme\*comment*\.""")
res3: Option[String] = None
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.
sorry @sschaef it covers cases in shouldNotRemoveCommentsInImportPackagePrefix
so `import comment.comment\ is not included
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.
generally Im not sure about it. Strange but nothing works correctly in REPL. i'm pretty sure your example will work too in test, i verify it on Monday
LGTM. I didn't do a in depth code analysis though. Just a check that the functionality works as expected (in the cases where I tested it). |
@wpopielarski @mlangc I lost track of all the comments here. Any blocking issues left? Or just additional features and bug fixes that could also go in a future PR? |
The only blocking issue left for me is the tests that check for broken behaviour (see my comment above). Apart from that I think that we should merge this as soon as possible and see how it works out in practice. |
working on it, hope today will be ready 2016-04-08 11:36 GMT+02:00 Matthias Langer [email protected]:
|
1. turns Select/Import qualifiers with `fullNameString` instead of `nameString`. This makes better recognition of import usage (by types mainly). 2. ignores some tests for which some imports are promoted to package scope (it is existing defect).
Refer to this link for build results (access rights to CI server needed): https://jenkins.scala-ide.org:8496/jenkins/job/ghprb-scala-refactoring-validator/298/ |
The last commit doesn't break |
Hmm, for me organizing imports on |
I look forward to all the ENSIME integration tests failing when you click the merge button 😝 |
Good that you said it, I just run the tests and they passed, therefore I can merge. ;) |
Adds settings to `OrganizeImportsPreferencePage` to control featue implemented by PR scala-ide/scala-refactoring#144
Work bases on #142. Same concept and code. Done.