Skip to content

Commit 1be6770

Browse files
committed
Move to struct declaration: handle struct declaration via _var_ case
Replace single struct declaration with short var declaration, or add to existing struct literal in statement
1 parent 00f9d70 commit 1be6770

14 files changed

+224
-13
lines changed

src/com/goide/intentions/GoMoveToStructInitializationIntention.java

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,32 @@ private static Data getData(@NotNull PsiElement element) {
6161
if (!element.isValid() || !element.isWritable()) return null;
6262
GoAssignmentStatement assignment = getValidAssignmentParent(element);
6363
GoReferenceExpression selectedFieldReference = assignment != null ? getFieldReferenceExpression(element, assignment) : null;
64-
GoCompositeLit compositeLit = selectedFieldReference != null ? getStructLiteralByReference(selectedFieldReference, assignment) : null;
65-
if (compositeLit == null) return null;
64+
if (selectedFieldReference == null) return null;
6665

67-
List<GoReferenceExpression> references = getUninitializedSingleFieldReferences(assignment, selectedFieldReference, compositeLit);
68-
return !references.isEmpty() ? new Data(assignment, compositeLit, references) : null;
66+
GoVarDefinition structDefinition = getDefinition(selectedFieldReference);
67+
GoStatement previousStatement = PsiTreeUtil.getPrevSiblingOfType(assignment, GoStatement.class);
68+
boolean needReplaceDeclarationWithShortVar = isUnassigned(getSingleVarSpecByDefinition(previousStatement, structDefinition));
69+
70+
GoCompositeLit compositeLit = needReplaceDeclarationWithShortVar
71+
? createStructLiteral(structDefinition, previousStatement.getProject())
72+
: getStructLiteralByReference(selectedFieldReference, assignment);
73+
if (compositeLit == null || structDefinition == null) return null;
74+
75+
List<GoReferenceExpression> references = getUninitializedSingleFieldReferences(assignment, structDefinition, compositeLit);
76+
return !references.isEmpty() ? new Data(assignment, compositeLit, references, previousStatement, structDefinition.getText(),
77+
needReplaceDeclarationWithShortVar) : null;
78+
}
79+
80+
@Nullable
81+
private static GoCompositeLit createStructLiteral(@NotNull GoVarDefinition definition, @NotNull Project project) {
82+
GoType type = definition.getGoType(null);
83+
return type != null ? GoElementFactory.createCompositeLit(project, type) : null;
84+
}
85+
86+
@Nullable
87+
@Contract("null -> null")
88+
private static GoVarDefinition getDefinition(@Nullable GoReferenceExpression referenceExpressions) {
89+
return ObjectUtils.tryCast(resolveQualifier(referenceExpressions), GoVarDefinition.class);
6990
}
7091

7192
@Nullable
@@ -104,6 +125,22 @@ private static GoReferenceExpression unwrapParensAndCast(@Nullable PsiElement e)
104125
return ObjectUtils.tryCast(e, GoReferenceExpression.class);
105126
}
106127

128+
@Nullable
129+
@Contract("_, null -> null; null, _ -> null")
130+
private static GoVarSpec getSingleVarSpecByDefinition(@Nullable GoStatement statement,
131+
@Nullable GoVarDefinition definition) {
132+
GoVarDeclaration declaration = statement != null ? statement.getVarDeclaration() : null;
133+
List<GoVarSpec> varSpecs = declaration != null ? declaration.getVarSpecList() : emptyList();
134+
GoVarSpec singleVarSpec = varSpecs.size() == 1 ? getFirstItem(varSpecs) : null;
135+
List<GoVarDefinition> varDefinitions = singleVarSpec != null ? singleVarSpec.getVarDefinitionList() : emptyList();
136+
return varDefinitions.size() == 1 && definition == getFirstItem(varDefinitions) ? singleVarSpec : null;
137+
}
138+
139+
@Contract("null -> false")
140+
private static boolean isUnassigned(@Nullable GoVarSpec varSpec) {
141+
return varSpec != null && varSpec.getExpressionList().isEmpty();
142+
}
143+
107144
@Contract("null -> false")
108145
private static boolean isFieldReferenceExpression(@Nullable PsiElement element) {
109146
return element instanceof GoReferenceExpression && isFieldDefinition(((GoReferenceExpression)element).resolve());
@@ -138,11 +175,10 @@ private static boolean isResolvedTo(@Nullable PsiElement e, @Nullable PsiElement
138175

139176
@NotNull
140177
private static List<GoReferenceExpression> getUninitializedSingleFieldReferences(@NotNull GoAssignmentStatement assignment,
141-
@NotNull GoReferenceExpression fieldReferenceExpression,
178+
@Nullable GoVarDefinition definition,
142179
@NotNull GoCompositeLit compositeLit) {
143-
PsiElement resolve = resolveQualifier(fieldReferenceExpression);
144180
List<GoReferenceExpression> uninitializedFieldReferencesByQualifier =
145-
filter(getUninitializedFieldReferenceExpressions(assignment, compositeLit), e -> isResolvedTo(e.getQualifier(), resolve));
181+
filter(getUninitializedFieldReferenceExpressions(assignment, compositeLit), e -> isResolvedTo(e.getQualifier(), definition));
146182
MultiMap<PsiElement, GoReferenceExpression> resolved = groupBy(uninitializedFieldReferencesByQualifier, GoReferenceExpression::resolve);
147183
return map(filter(resolved.entrySet(), set -> set.getValue().size() == 1), set -> getFirstItem(set.getValue()));
148184
}
@@ -151,13 +187,15 @@ private static List<GoReferenceExpression> getUninitializedSingleFieldReferences
151187
private static GoCompositeLit getStructLiteralByReference(@NotNull GoReferenceExpression fieldReferenceExpression,
152188
@NotNull GoAssignmentStatement assignment) {
153189
GoStatement previousStatement = PsiTreeUtil.getPrevSiblingOfType(assignment, GoStatement.class);
190+
if (previousStatement == null) return null;
191+
154192
if (previousStatement instanceof GoSimpleStatement) {
155193
return getStructLiteral(fieldReferenceExpression, (GoSimpleStatement)previousStatement);
156194
}
157195
if (previousStatement instanceof GoAssignmentStatement) {
158196
return getStructLiteral(fieldReferenceExpression, (GoAssignmentStatement)previousStatement);
159197
}
160-
return null;
198+
return getStructLiteral(previousStatement, fieldReferenceExpression);
161199
}
162200

163201
@Nullable
@@ -172,15 +210,16 @@ private static GoCompositeLit getStructLiteral(@NotNull GoReferenceExpression fi
172210
}
173211

174212
@Nullable
175-
private static PsiElement resolveQualifier(@NotNull GoReferenceExpression fieldReferenceExpression) {
176-
GoReferenceExpression qualifier = fieldReferenceExpression.getQualifier();
213+
@Contract("null -> null")
214+
private static PsiElement resolveQualifier(@Nullable GoReferenceExpression fieldReferenceExpression) {
215+
GoReferenceExpression qualifier = fieldReferenceExpression != null ? fieldReferenceExpression.getQualifier() : null;
177216
return qualifier != null ? qualifier.resolve() : null;
178217
}
179218

180219
@Nullable
181220
private static GoCompositeLit getStructLiteral(@NotNull GoReferenceExpression fieldReferenceExpression,
182221
@NotNull GoAssignmentStatement structAssignment) {
183-
GoVarDefinition varDefinition = ObjectUtils.tryCast(resolveQualifier(fieldReferenceExpression), GoVarDefinition.class);
222+
GoVarDefinition varDefinition = getDefinition(fieldReferenceExpression);
184223
PsiElement field = fieldReferenceExpression.resolve();
185224
if (varDefinition == null || !isFieldDefinition(field) || !hasStructTypeWithField(varDefinition, (GoNamedElement)field)) {
186225
return null;
@@ -193,6 +232,14 @@ private static GoCompositeLit getStructLiteral(@NotNull GoReferenceExpression fi
193232
return ObjectUtils.tryCast(compositeLit, GoCompositeLit.class);
194233
}
195234

235+
@Nullable
236+
private static GoCompositeLit getStructLiteral(@NotNull GoStatement statement,
237+
@NotNull GoReferenceExpression fieldReferenceExpression) {
238+
GoVarDefinition definition = getDefinition(fieldReferenceExpression);
239+
GoVarSpec varSpec = definition != null ? getSingleVarSpecByDefinition(statement, definition) : null;
240+
return varSpec != null ? ObjectUtils.tryCast(getFirstItem(varSpec.getRightExpressionsList()), GoCompositeLit.class) : null;
241+
}
242+
196243
private static boolean hasStructTypeWithField(@NotNull GoVarDefinition structVarDefinition, @NotNull GoNamedElement field) {
197244
GoType type = structVarDefinition.getGoType(null);
198245
GoStructType structType = type != null ? ObjectUtils.tryCast(type.getUnderlyingType(), GoStructType.class) : null;
@@ -239,6 +286,10 @@ public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement
239286
Data data = getData(element);
240287
if (data == null) return;
241288
moveFieldReferenceExpressions(data);
289+
290+
if (!data.needReplaceDeclarationWithShortVar()) return;
291+
data.getStructDeclaration()
292+
.replace(GoElementFactory.createShortVarDeclarationStatement(project, data.getStructVarName(), data.getCompositeLit().getText()));
242293
}
243294

244295
private static void moveFieldReferenceExpressions(@NotNull Data data) {
@@ -272,13 +323,22 @@ private static class Data {
272323
private final GoCompositeLit myCompositeLit;
273324
private final GoAssignmentStatement myAssignment;
274325
private final List<GoReferenceExpression> myReferenceExpressions;
326+
private final GoStatement myStructDeclaration;
327+
private final String myStructVarName;
328+
private final boolean myNeedReplaceDeclarationWithShortVar;
275329

276330
public Data(@NotNull GoAssignmentStatement assignment,
277-
@NotNull GoCompositeLit compositeLit,
278-
@NotNull List<GoReferenceExpression> referenceExpressions) {
331+
@Nullable GoCompositeLit compositeLit,
332+
@NotNull List<GoReferenceExpression> referenceExpressions,
333+
@Nullable GoStatement structDeclaration,
334+
@Nullable String structVarName,
335+
boolean needReplaceDeclarationWithShortVar) {
279336
myCompositeLit = compositeLit;
280337
myAssignment = assignment;
281338
myReferenceExpressions = referenceExpressions;
339+
myStructDeclaration = structDeclaration;
340+
myStructVarName = structVarName;
341+
myNeedReplaceDeclarationWithShortVar = needReplaceDeclarationWithShortVar;
282342
}
283343

284344
public GoCompositeLit getCompositeLit() {
@@ -292,5 +352,19 @@ public GoAssignmentStatement getAssignment() {
292352
public List<GoReferenceExpression> getReferenceExpressions() {
293353
return myReferenceExpressions;
294354
}
355+
356+
public GoStatement getStructDeclaration() {
357+
return myStructDeclaration;
358+
}
359+
360+
public String getStructVarName() {
361+
return myStructVarName;
362+
}
363+
364+
public boolean needReplaceDeclarationWithShortVar() {
365+
return myNeedReplaceDeclarationWithShortVar;
366+
}
295367
}
296368
}
369+
370+

src/com/goide/psi/impl/GoElementFactory.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,4 +266,10 @@ public static GoTypeDeclaration createTypeDeclaration(@NotNull Project project,
266266
GoFile file = createFileFromText(project, "package a; type " + name + " " + type.getText());
267267
return PsiTreeUtil.findChildOfType(file, GoTypeDeclaration.class);
268268
}
269+
270+
@NotNull
271+
public static GoCompositeLit createCompositeLit(@NotNull Project project, @NotNull GoType type) {
272+
GoFile file = createFileFromText(project, "package a; var _ = " + type.getText() + "{}");
273+
return PsiTreeUtil.findChildOfType(file, GoCompositeLit.class);
274+
}
269275
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var s string
9+
s.foo <caret>= "bar"
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
s := S{foo: "bar"}
9+
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var (s S)
9+
s.foo <caret>= "bar"
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
s := S{foo: "bar"}
9+
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var s S
9+
s.foo <caret>= "bar"
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var s, b S
9+
s.foo <caret>= "bar"
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var s S = S{foo: "bar"}
9+
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var s S = S{}
9+
s.foo <caret>= "bar"
10+
print(s.foo)
11+
}

0 commit comments

Comments
 (0)