-
-
Notifications
You must be signed in to change notification settings - Fork 351
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
review: feat(sniper): detailed tree of source fragments of compilation unit #2283
Conversation
06f1446
to
ff24212
Compare
@Override | ||
public ElementSourceFragment getRootSourceFragment() { | ||
if (rootFragment == null) { | ||
String originSourceCode = getOriginalSourceCode(); |
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.
getOriginalSourceCode()
might return null if getFile()
returns null. You should check the result to avoid NPE here.
for (CtImport imprt : getImports()) { | ||
rootFragment.addChild(new ElementSourceFragment(imprt, null /*TODO role for import of CU*/)); | ||
} | ||
for (CtType<?> ctType : declaredTypes) { |
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 CU can also be used for package declaration or module declaration: in those cases, you don't create the tree?
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 forgot that cases. As fast solution: I will check them and throw SpoonException(...not supported...)
. Later we can check if we can create the tree for them too - I have no idea yet if it is easily possible or needs more effort.
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 ok for me. Maybe it worth opening an issue to avoid forgetting it.
@@ -474,6 +477,39 @@ public void scan(CtRole role, CtElement element) { | |||
}); | |||
} | |||
|
|||
@Test | |||
public void testSourcePositionTreeIsCorrectlyOrdered() { | |||
List<CtType> types = rootPackage.filterChildren(new TypeFilter<>(CtType.class)).filterChildren((CtType t) -> t.isTopLevel()).list(); |
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.
Could you specify the contract?
API changes: 2 (Detected by Revapi) Old API: fr.inria.gforge.spoon:spoon-core:jar:7.1.0-20180827.060031-119 / New API: fr.inria.gforge.spoon:spoon-core:jar:7.1.0-SNAPSHOT
|
It looks interesting. What's the final goal of this new feature? What do you mean by "ElementSourceFragment is a wrapper around CtElement and its SourcePosition"? |
It is core feature need by #1927 - sniper mode.
yes,
It is not enough to link
|
Excellent!
Do you mean something like |
I would say no. But may be I do not understand your idea. The concept of this PR and core feature for sniper mode is:
So from this concept point of view there is relation from ElementSourceFragment to the CtElement, which was made during building of origin Spoon model. Opposite relation is not needed. When the spoon model is changed later (some CtElements are cloned, added, modified, moved, removed, ... ) then these new CtElements doesn't have source position, etc. So it makes no sense to ask these elements for origin position or origin element. |
Do you mean getRootSourceFragment?
I would propose to rename ElementSourceFragment to CtElementMirror. WDYT?
I expect a field |
I meant
It looks like you look at the concept/feature from wrong direction... There is no reason why EVERY CtElement should know it's ElementSourceFragment, but there is algoritm in CompilationUnit which can search and return ElementSourceFragment for CtElement - if there exist such ElementSourceFragment... @monperrus Is it more clear now? |
It's not clear but it's better. It looks interesting. As we've done in the past, are you OK if I propose some renaming and refactoring by pushing in your branch? |
Yes sure, I am ok with that. But it would be better for me if you would do the refactoring directly in #1927 - branch Otherwise you will refactor only WDYT? By the way I am open to discuss merging of new SourceFragment and old SourcePosition somehow - but before we can discuss it it needs your understanding of SourceFragment concept and implementation of #1927. |
In #2419 this the same code, with a refactoring:
Does it make sense to you? |
I like it a lot! I just do not know how it will behave it this method is called on the brand new CtElement, which was made by cloning or Factory#createXxx call. We should make a test and define some expected behavior. |
I like it a lot!
Super.
We should make a test and define some expected behavior.
Just added a test with contract "on a new element, getOriginalSourceFragment returns the actual
element toString"
|
May be it would be better to return some constant
WDYT? |
You're right, it would be better. |
do you continue based on the design we discussed in #2419? |
OK, I can integrate these change into main PR today or tomorrow evening. |
looking forward to it!
|
9d2116b
to
918aa77
Compare
I do not understand why CI failed here. Is it problem of CI or this PR? I am finished with my changes here. |
Thanks Pavel. I've put the implementation classes in an Let me think about it and add some more documentation. |
and I want to have a look at the tests, which look super interesting!! |
Why this change? ElementSourceFragment getOriginalSourceFragment() { changed to SourceFragment getOriginalSourceFragment() It can never return SourceFragment. It is always ElementSourceFragment. ElementSourceFragment is not just implementation of SourceFragment interface. It means a special Source fragment which is linked to an CtElement. Other SourceFragments might be for example linked to a token. |
Because of the design best practice that interface should only depend on other interfaces, and not
on concrete implementations.
One solution is to extract an interface out of ElementSourceFragment. WDYT?
|
I understand, but If I remember well, you already agreed that in some cases Spoon uses interfaces too much. I do not think that it is helpful to have interface |
Yes, you're right, sometimes there are several design principles that contradict :-)
We can go back to |ElementSourceFragment.
We need anyhow to put them @experimental.
|
|
Pavel, to your opinion, is ElementSourceFragment meant to be used by clients? or is it more an internal class for us? For clients, what about a simple method in the interface:
WDYT? |
short answer: No long answer:
I would say, normally clients doesn't need that, same like they doesn't need SourcePosition. But these clients who wants to play with origin sources, will highly appreciate that detailed source tree. |
then, I moved SourceFragment to package "internal" and we're ready to merge. |
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.
Thanks Pavel, for this new advanced feature.
this PR introduces new types:
SourceFragment
ElementSourceFragment implements SourceFragment
TokenSourceFragment implements SourceFragment
CollectionSourceFragment implements SourceFragment
ElementSourceFragment
is a wrapper aroundCtElement
and it'sSourcePosition
. It also builds hierarchy ofElementSourceFragment
s, which mirrors hierarchy of fragments of source code of compilation unit.It provides 3 views at tree of source fragments
ElementSourceFragment
s. It consists of fragments defined by SourcePosition.start/end ofCtElement
s only. It is basic structure, which must be build after Spoon model is created and before it is modified.ElementSourceFragment
can be optionally expanded to detailed tree which containsTokenSourceFragment
s next to childElementSourceFragment
s. SeeElementSourceFragment#getChildrenFragments()
.CollectionSourceFragment
Notes:
DeclarationSourcePosition