Skip to content

Commit fc9fd3f

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 fc9fd3f

14 files changed

+225
-16
lines changed

src/com/goide/intentions/GoMoveToStructInitializationIntention.java

Lines changed: 87 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.goide.psi.*;
2020
import com.goide.psi.impl.GoElementFactory;
2121
import com.goide.psi.impl.GoPsiImplUtil;
22+
import com.goide.psi.impl.GoStatementImpl;
2223
import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction;
2324
import com.intellij.openapi.editor.Editor;
2425
import com.intellij.openapi.project.Project;
@@ -53,19 +54,29 @@ public String getFamilyName() {
5354

5455
@Override
5556
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement element) {
56-
return getData(element) != null;
57+
return getData(element, project) != null;
5758
}
5859

5960
@Nullable
60-
private static Data getData(@NotNull PsiElement element) {
61+
private static Data getData(@NotNull PsiElement element, @NotNull Project project) {
6162
if (!element.isValid() || !element.isWritable()) return null;
6263
GoAssignmentStatement assignment = getValidAssignmentParent(element);
6364
GoReferenceExpression selectedFieldReference = assignment != null ? getFieldReferenceExpression(element, assignment) : null;
64-
GoCompositeLit compositeLit = selectedFieldReference != null ? getStructLiteralByReference(selectedFieldReference, assignment) : null;
65-
if (compositeLit == null) return null;
65+
GoCompositeLit compositeLit =
66+
selectedFieldReference != null ? getStructLiteralByReference(selectedFieldReference, assignment, project) : null;
67+
GoVarDefinition structDefinition = getDefinition(selectedFieldReference);
68+
if (compositeLit == null || structDefinition == null) return null;
69+
70+
List<GoReferenceExpression> references = getUninitializedSingleFieldReferences(assignment, structDefinition, compositeLit);
71+
GoStatementImpl previousStatement = PsiTreeUtil.getPrevSiblingOfType(assignment, GoStatementImpl.class);
72+
return !references.isEmpty() ? new Data(assignment, compositeLit, references, previousStatement, structDefinition.getText(),
73+
isUnassigned(getSingleVarSpecByDefinition(previousStatement, structDefinition))) : null;
74+
}
6675

67-
List<GoReferenceExpression> references = getUninitializedSingleFieldReferences(assignment, selectedFieldReference, compositeLit);
68-
return !references.isEmpty() ? new Data(assignment, compositeLit, references) : null;
76+
@Nullable
77+
@Contract("null -> null")
78+
private static GoVarDefinition getDefinition(@Nullable GoReferenceExpression referenceExpressions) {
79+
return ObjectUtils.tryCast(resolveQualifier(referenceExpressions), GoVarDefinition.class);
6980
}
7081

7182
@Nullable
@@ -104,6 +115,22 @@ private static GoReferenceExpression unwrapParensAndCast(@Nullable PsiElement e)
104115
return ObjectUtils.tryCast(e, GoReferenceExpression.class);
105116
}
106117

118+
@Nullable
119+
@Contract("_, null -> null; null, _ -> null")
120+
private static GoVarSpec getSingleVarSpecByDefinition(@Nullable GoStatementImpl statement,
121+
@Nullable GoVarDefinition definition) {
122+
GoVarDeclaration declaration = statement != null ? statement.getVarDeclaration() : null;
123+
List<GoVarSpec> varSpecs = declaration != null ? declaration.getVarSpecList() : emptyList();
124+
GoVarSpec singleVarSpec = varSpecs.size() == 1 ? getFirstItem(varSpecs) : null;
125+
List<GoVarDefinition> varDefinitions = singleVarSpec != null ? singleVarSpec.getVarDefinitionList() : emptyList();
126+
return varDefinitions.size() == 1 && isResolvedTo(definition, getFirstItem(varDefinitions)) ? singleVarSpec : null;
127+
}
128+
129+
@Contract("null -> false")
130+
private static boolean isUnassigned(@Nullable GoVarSpec varSpec) {
131+
return varSpec != null && varSpec.getExpressionList().isEmpty();
132+
}
133+
107134
@Contract("null -> false")
108135
private static boolean isFieldReferenceExpression(@Nullable PsiElement element) {
109136
return element instanceof GoReferenceExpression && isFieldDefinition(((GoReferenceExpression)element).resolve());
@@ -138,25 +165,28 @@ private static boolean isResolvedTo(@Nullable PsiElement e, @Nullable PsiElement
138165

139166
@NotNull
140167
private static List<GoReferenceExpression> getUninitializedSingleFieldReferences(@NotNull GoAssignmentStatement assignment,
141-
@NotNull GoReferenceExpression fieldReferenceExpression,
168+
@Nullable GoVarDefinition definition,
142169
@NotNull GoCompositeLit compositeLit) {
143-
PsiElement resolve = resolveQualifier(fieldReferenceExpression);
144170
List<GoReferenceExpression> uninitializedFieldReferencesByQualifier =
145-
filter(getUninitializedFieldReferenceExpressions(assignment, compositeLit), e -> isResolvedTo(e.getQualifier(), resolve));
171+
filter(getUninitializedFieldReferenceExpressions(assignment, compositeLit), e -> isResolvedTo(e.getQualifier(), definition));
146172
MultiMap<PsiElement, GoReferenceExpression> resolved = groupBy(uninitializedFieldReferencesByQualifier, GoReferenceExpression::resolve);
147173
return map(filter(resolved.entrySet(), set -> set.getValue().size() == 1), set -> getFirstItem(set.getValue()));
148174
}
149175

150176
@Nullable
151177
private static GoCompositeLit getStructLiteralByReference(@NotNull GoReferenceExpression fieldReferenceExpression,
152-
@NotNull GoAssignmentStatement assignment) {
178+
@NotNull GoAssignmentStatement assignment,
179+
@NotNull Project project) {
153180
GoStatement previousStatement = PsiTreeUtil.getPrevSiblingOfType(assignment, GoStatement.class);
154181
if (previousStatement instanceof GoSimpleStatement) {
155182
return getStructLiteral(fieldReferenceExpression, (GoSimpleStatement)previousStatement);
156183
}
157184
if (previousStatement instanceof GoAssignmentStatement) {
158185
return getStructLiteral(fieldReferenceExpression, (GoAssignmentStatement)previousStatement);
159186
}
187+
if (previousStatement instanceof GoStatementImpl) {
188+
return getStructLiteral((GoStatementImpl)previousStatement, fieldReferenceExpression, project);
189+
}
160190
return null;
161191
}
162192

@@ -172,15 +202,16 @@ private static GoCompositeLit getStructLiteral(@NotNull GoReferenceExpression fi
172202
}
173203

174204
@Nullable
175-
private static PsiElement resolveQualifier(@NotNull GoReferenceExpression fieldReferenceExpression) {
176-
GoReferenceExpression qualifier = fieldReferenceExpression.getQualifier();
205+
@Contract("null -> null")
206+
private static PsiElement resolveQualifier(@Nullable GoReferenceExpression fieldReferenceExpression) {
207+
GoReferenceExpression qualifier = fieldReferenceExpression != null ? fieldReferenceExpression.getQualifier() : null;
177208
return qualifier != null ? qualifier.resolve() : null;
178209
}
179210

180211
@Nullable
181212
private static GoCompositeLit getStructLiteral(@NotNull GoReferenceExpression fieldReferenceExpression,
182213
@NotNull GoAssignmentStatement structAssignment) {
183-
GoVarDefinition varDefinition = ObjectUtils.tryCast(resolveQualifier(fieldReferenceExpression), GoVarDefinition.class);
214+
GoVarDefinition varDefinition = getDefinition(fieldReferenceExpression);
184215
PsiElement field = fieldReferenceExpression.resolve();
185216
if (varDefinition == null || !isFieldDefinition(field) || !hasStructTypeWithField(varDefinition, (GoNamedElement)field)) {
186217
return null;
@@ -193,6 +224,19 @@ private static GoCompositeLit getStructLiteral(@NotNull GoReferenceExpression fi
193224
return ObjectUtils.tryCast(compositeLit, GoCompositeLit.class);
194225
}
195226

227+
@Nullable
228+
private static GoCompositeLit getStructLiteral(@NotNull GoStatementImpl statement,
229+
@NotNull GoReferenceExpression fieldReferenceExpression,
230+
@NotNull Project project) {
231+
GoVarDefinition definition = getDefinition(fieldReferenceExpression);
232+
GoVarSpec varSpec = definition != null ? getSingleVarSpecByDefinition(statement, definition) : null;
233+
if (varSpec == null) return null;
234+
GoType structType = isUnassigned(varSpec) ? definition.getGoType(null) : null;
235+
return structType != null
236+
? GoElementFactory.createCompositeLit(project, structType)
237+
: ObjectUtils.tryCast(getFirstItem(varSpec.getRightExpressionsList()), GoCompositeLit.class);
238+
}
239+
196240
private static boolean hasStructTypeWithField(@NotNull GoVarDefinition structVarDefinition, @NotNull GoNamedElement field) {
197241
GoType type = structVarDefinition.getGoType(null);
198242
GoStructType structType = type != null ? ObjectUtils.tryCast(type.getUnderlyingType(), GoStructType.class) : null;
@@ -236,9 +280,13 @@ private static List<? extends PsiElement> getLeftHandElements(@NotNull GoStateme
236280

237281
@Override
238282
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
239-
Data data = getData(element);
283+
Data data = getData(element, project);
240284
if (data == null) return;
241285
moveFieldReferenceExpressions(data);
286+
287+
if (!data.needReplaceDeclarationWithShortVar()) return;
288+
data.getStructDeclaration()
289+
.replace(GoElementFactory.createShortVarDeclarationStatement(project, data.getStructVarName(), data.getCompositeLit().getText()));
242290
}
243291

244292
private static void moveFieldReferenceExpressions(@NotNull Data data) {
@@ -272,13 +320,22 @@ private static class Data {
272320
private final GoCompositeLit myCompositeLit;
273321
private final GoAssignmentStatement myAssignment;
274322
private final List<GoReferenceExpression> myReferenceExpressions;
323+
private final GoStatementImpl myStructDeclaration;
324+
private final String myStructVarName;
325+
private final boolean myNeedReplaceDeclarationWithShortVar;
275326

276327
public Data(@NotNull GoAssignmentStatement assignment,
277-
@NotNull GoCompositeLit compositeLit,
278-
@NotNull List<GoReferenceExpression> referenceExpressions) {
328+
@Nullable GoCompositeLit compositeLit,
329+
@NotNull List<GoReferenceExpression> referenceExpressions,
330+
@Nullable GoStatementImpl structDeclaration,
331+
@Nullable String structVarName,
332+
boolean needReplaceDeclarationWithShortVar) {
279333
myCompositeLit = compositeLit;
280334
myAssignment = assignment;
281335
myReferenceExpressions = referenceExpressions;
336+
myStructDeclaration = structDeclaration;
337+
myStructVarName = structVarName;
338+
myNeedReplaceDeclarationWithShortVar = needReplaceDeclarationWithShortVar;
282339
}
283340

284341
public GoCompositeLit getCompositeLit() {
@@ -292,5 +349,19 @@ public GoAssignmentStatement getAssignment() {
292349
public List<GoReferenceExpression> getReferenceExpressions() {
293350
return myReferenceExpressions;
294351
}
352+
353+
public GoStatementImpl getStructDeclaration() {
354+
return myStructDeclaration;
355+
}
356+
357+
public String getStructVarName() {
358+
return myStructVarName;
359+
}
360+
361+
public boolean needReplaceDeclarationWithShortVar() {
362+
return myNeedReplaceDeclarationWithShortVar;
363+
}
295364
}
296365
}
366+
367+

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,4 +266,11 @@ 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+
String typeText = type.getText();
273+
GoFile file = createFileFromText(project, "package a; struct " + typeText + " {}; var _ = " + typeText + "{}");
274+
return PsiTreeUtil.findChildOfType(file, GoCompositeLit.class);
275+
}
269276
}
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)