-
Notifications
You must be signed in to change notification settings - Fork 12.4k
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
Fix organize imports overlap #43228
Fix organize imports overlap #43228
Conversation
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 you're on the right track - I'm pretty sure the overlap issue is just coming from newlines at this point.
src/services/textChanges.ts
Outdated
if (hasTrailingComment) { | ||
const comment = getTrailingCommentRanges(sourceFile.text, fullStart)?.[0]; | ||
if (comment) { | ||
return comment.end; |
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.
return comment.end; | |
return skipTrivia(sourceFile.text, comment.end, /*stopAfterLineBreak*/ true, /*stopAtComments*/ 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.
Since you call skipTrivia
when you do getAdjustedEndPosition
, it looks like you would need to do the same for the comment range for getAdjustedStartPosition
, otherwise the carriage return / new lines will overlap.
This seems to fix the issue except when the trailing comment is on the first import, so there might be another place where you have to do this.
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'm not sure I understand in which condition this is an issue. Do you have a sample code I can test?
src/services/textChanges.ts
Outdated
const commentEndLine = getLineOfLocalPosition(sourceFile, comment.end); | ||
if (commentEndLine > nodeEndLine) { | ||
const newEnd = skipTrivia(sourceFile.text, comment.end, /*stopAfterLineBreak*/ true, /*stopAtComments*/ true); | ||
return { end: newEnd, hasTrailingComment: 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.
Can hasTrailingComment
be determined by the trivia option outside of this function, such as in the deleteNodes
loop? It seems awkward to return this new object if we're almost always interested in only the end position.
If refactoring is too difficult I would at minimum change the name of the function since getAdjustedEndPosition
implies we're still just getting the 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.
Done. The new method name is a little confusing but I wasn't sure how to name it. Suggestions are welcome.
for (const comment of comments) { | ||
// Single line can break the loop as trivia will only be this line. | ||
// Comments on subsequest lines are also ignored. | ||
if (comment.kind === SyntaxKind.SingleLineCommentTrivia || getLineOfLocalPosition(sourceFile, comment.pos) > nodeEndLine) { |
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 warrants a test example.
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.
// When deleting multiple nodes we need to track if the end position is including multiline trailing comments. | ||
let hasTrailingComment = false; | ||
|
||
for (const node of nodes) { |
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 seems pretty specific to import declaration nodes. What happens when a node with a trailing multiline comment is between two import statements? for example:
import * as b from "b";
console.log();/**
* what happens to me?
*/
import * as a from "a"
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, the result would be console.log();/**
as we have no way of recognizing what the previous node is about.
I think this is a design flaw and suggest addressing it on another ticket. So far this PR will not break that scenario.
//// import * as AnotherThingElse from "someotherpath"; | ||
|
||
|
||
verify.organizeImports(''); |
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.
All of these imports are unused so they will just be deleted. We probably do want to test that but you will want to also add tests in which the imports are used, and with non-imports between the imports, also with trailing trivia.
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.
Good catch I was so focused on the issue that I didn't even considered. I did and the crash still happens. Working on it.
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.
Fixed.
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 couple more small comments from me.
src/services/textChanges.ts
Outdated
@@ -345,6 405,13 @@ namespace ts.textChanges { | |||
this.replaceRangeWithNodes(sourceFile, getAdjustedRange(sourceFile, startNode, endNode, options), newNodes, options); | |||
} | |||
|
|||
public replaceAndDeleteNodeWithNodes(sourceFile: SourceFile, oldNode: Node, newNodes: readonly Node[], deleteNodes: Node[], replaceOptions: ChangeNodeOptions = useNonAdjustedPositions, deleteOptions: ConfigurableStartEnd = { leadingTriviaOption: LeadingTriviaOption.IncludeAll }): void { |
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 name is a bit confusing - I probably wouldn't even extract this to a function. It's pretty simple and it looks like you only use it once 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.
Agree, done.
|
||
// Regression test for GH#43107 | ||
|
||
//// import * as something from "path"; /* small comment */ // single line one. |
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 you want this single line comment test to also not delete the import so you can see that the comment is preserved (by adding usages of something
and somethingElse
below).
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 was actually meant to test that the single line is deleted. I found I used to have that problem. It also makes sense to test the contrary so I have updated to do it for both.
Fixes #43107
Text changes will now include multiline comments if they are considered trailing comments from the previous node.