Skip to content

Commit 561f216

Browse files
#37: Resolve custom option references
Resolve custom options declared in imported proto files.
1 parent 4dd864e commit 561f216

File tree

11 files changed

+145
-72
lines changed

11 files changed

+145
-72
lines changed

src/main/java/io/protostuff/jetbrains/plugin/psi/ProtoPsiFileRoot.java

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,14 @@
66
import com.intellij.psi.FileViewProvider;
77
import com.intellij.psi.PsiElement;
88
import com.intellij.psi.PsiNamedElement;
9-
import com.intellij.psi.PsiReference;
109
import io.protostuff.jetbrains.plugin.Icons;
1110
import io.protostuff.jetbrains.plugin.ProtoFileType;
1211
import io.protostuff.jetbrains.plugin.ProtoLanguage;
13-
import java.util.ArrayDeque;
1412
import java.util.ArrayList;
1513
import java.util.Collection;
1614
import java.util.Collections;
17-
import java.util.Deque;
1815
import java.util.List;
1916
import java.util.Objects;
20-
import java.util.stream.Collectors;
2117
import javax.swing.Icon;
2218
import org.antlr.jetbrains.adapter.psi.ScopeNode;
2319
import org.jetbrains.annotations.NotNull;
@@ -84,46 +80,6 @@ private ProtoRootNode getProtoRoot() {
8480
return findChildByClass(ProtoRootNode.class);
8581
}
8682

87-
/**
88-
* Returns extensions defined in this proto file.
89-
*/
90-
public Collection<ExtendNode> getLocalExtensions() {
91-
List<ExtendNode> result = new ArrayList<>();
92-
Deque<DataTypeContainer> queue = new ArrayDeque<>();
93-
queue.push(getProtoRoot());
94-
while (!queue.isEmpty()) {
95-
DataTypeContainer container = queue.poll();
96-
Collection<ExtendNode> extensions = container.getDeclaredExtensions();
97-
result.addAll(extensions);
98-
for (DataType dataType : container.getDeclaredDataTypes()) {
99-
if (dataType instanceof DataTypeContainer) {
100-
DataTypeContainer childContainer = (DataTypeContainer) dataType;
101-
queue.push(childContainer);
102-
}
103-
}
104-
}
105-
return result;
106-
}
107-
108-
/**
109-
* Returns all extension for given target message.
110-
*/
111-
public Collection<ExtendNode> getExtenstions(MessageNode target) {
112-
return getLocalExtensions().stream()
113-
.filter(node -> {
114-
TypeReferenceNode extendTarget = node.getTarget();
115-
if (extendTarget == null) {
116-
return false;
117-
}
118-
PsiReference extendTargetReference = extendTarget.getReference();
119-
if (extendTargetReference == null) {
120-
return false;
121-
}
122-
return extendTargetReference.isReferenceTo(target);
123-
})
124-
.collect(Collectors.toList());
125-
}
126-
12783
private void addChildrenRecursively(Collection<? extends ProtoType> elements, List<ProtoType> result) {
12884
for (ProtoType element : elements) {
12985
if (element instanceof DataTypeContainer) {

src/main/java/io/protostuff/jetbrains/plugin/psi/ProtoRootNode.java

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
11
package io.protostuff.jetbrains.plugin.psi;
22

33
import com.intellij.lang.ASTNode;
4+
import com.intellij.psi.PsiReference;
5+
import java.util.ArrayDeque;
6+
import java.util.ArrayList;
47
import java.util.Arrays;
58
import java.util.Collection;
69
import java.util.Deque;
10+
import java.util.HashSet;
11+
import java.util.List;
712
import java.util.Objects;
13+
import java.util.Queue;
14+
import java.util.Set;
15+
import java.util.stream.Collectors;
816
import org.antlr.jetbrains.adapter.psi.AntlrPsiNode;
917
import org.jetbrains.annotations.NotNull;
1018
import org.jetbrains.annotations.Nullable;
@@ -65,7 +73,7 @@ public DataType resolve(String typeName, Deque<String> scopeLookupList, boolean
6573
if (!resolveInImports) {
6674
return null;
6775
}
68-
ImportNode[] importNodes = findChildrenByClass(ImportNode.class);
76+
List<ImportNode> importNodes = getImports();
6977
for (ImportNode importNode : importNodes) {
7078
ProtoRootNode targetProto = importNode.getTargetProto();
7179
if (targetProto != null) {
@@ -80,6 +88,25 @@ public DataType resolve(String typeName, Deque<String> scopeLookupList, boolean
8088
return null;
8189
}
8290

91+
/**
92+
* Get imports declared in this proto file.
93+
*/
94+
@NotNull
95+
public List<ImportNode> getImports() {
96+
return Arrays.asList(findChildrenByClass(ImportNode.class));
97+
}
98+
99+
/**
100+
* Get public imports declared in this proto file.
101+
*/
102+
@NotNull
103+
public List<ImportNode> getPublicImports() {
104+
return Arrays.stream(findChildrenByClass(ImportNode.class))
105+
.filter(ImportNode::isPublic)
106+
.collect(Collectors.toList());
107+
}
108+
109+
83110
private DataType resolveByQualifiedName(String qualifiedName) {
84111
String prefix = getCurrentProtoPrefix();
85112
if (qualifiedName.startsWith(prefix)) {
@@ -139,4 +166,66 @@ public Collection<ProtoType> getDeclaredTypes() {
139166
return Arrays.asList(findChildrenByClass(ProtoType.class));
140167
}
141168

169+
/**
170+
* Returns all extension for given target message.
171+
*/
172+
public Collection<ExtendNode> getExtenstions(MessageNode target) {
173+
return getExtensions().stream()
174+
.filter(node -> {
175+
TypeReferenceNode extendTarget = node.getTarget();
176+
if (extendTarget == null) {
177+
return false;
178+
}
179+
PsiReference extendTargetReference = extendTarget.getReference();
180+
return extendTargetReference.isReferenceTo(target);
181+
})
182+
.collect(Collectors.toList());
183+
}
184+
185+
/**
186+
* Returns all extensions visible inside of this proto file.
187+
*/
188+
public Collection<ExtendNode> getExtensions() {
189+
List<ExtendNode> result = new ArrayList<>();
190+
result.addAll(getLocalExtensions());
191+
Queue<ImportNode> queue = new ArrayDeque<>();
192+
queue.addAll(getImports());
193+
Set<ProtoRootNode> processedProtos = new HashSet<>();
194+
while (!queue.isEmpty()) {
195+
ImportNode importNode = queue.poll();
196+
ProtoRootNode targetProto = importNode.getTargetProto();
197+
if (processedProtos.contains(targetProto)) {
198+
// do not enter into endless loop
199+
// if proto files refer to each other
200+
continue;
201+
}
202+
processedProtos.add(targetProto);
203+
if (targetProto != null) {
204+
result.addAll(targetProto.getLocalExtensions());
205+
queue.addAll(targetProto.getPublicImports());
206+
}
207+
}
208+
return result;
209+
}
210+
211+
/**
212+
* Returns local extensions declared in this proto file.
213+
*/
214+
public Collection<ExtendNode> getLocalExtensions() {
215+
List<ExtendNode> result = new ArrayList<>();
216+
Deque<DataTypeContainer> queue = new ArrayDeque<>();
217+
queue.push(this);
218+
while (!queue.isEmpty()) {
219+
DataTypeContainer container = queue.poll();
220+
Collection<ExtendNode> extensions = container.getDeclaredExtensions();
221+
result.addAll(extensions);
222+
for (DataType dataType : container.getDeclaredDataTypes()) {
223+
if (dataType instanceof DataTypeContainer) {
224+
DataTypeContainer childContainer = (DataTypeContainer) dataType;
225+
queue.push(childContainer);
226+
}
227+
}
228+
}
229+
return result;
230+
}
142231
}

src/main/java/io/protostuff/jetbrains/plugin/reference/FieldReferenceProviderImpl.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,12 +136,12 @@ private String getTarget(PsiElement element) {
136136
return null;
137137
}
138138

139-
private ProtoPsiFileRoot getProtoRoot(PsiElement element) {
139+
private ProtoRootNode getProtoRoot(PsiElement element) {
140140
PsiElement parent = element.getParent();
141-
while (!(parent instanceof ProtoPsiFileRoot)) {
141+
while (!(parent instanceof ProtoRootNode)) {
142142
parent = parent.getParent();
143143
}
144-
return (ProtoPsiFileRoot) parent;
144+
return (ProtoRootNode) parent;
145145
}
146146

147147
private MessageField resolveStandardOptionReference(PsiElement sourceElement, MessageNode target, String key) {
@@ -178,7 +178,7 @@ private MessageField resolveDefaultOptionReference(PsiElement element) {
178178

179179
@Nullable
180180
private FieldNode resolveCustomOptionReference(PsiElement element, MessageNode target, String key) {
181-
ProtoPsiFileRoot protoRoot = getProtoRoot(element);
181+
ProtoRootNode protoRoot = getProtoRoot(element);
182182
DataTypeContainer container = getContainer(element);
183183
Deque<String> scopeLookupList = TypeReference.createScopeLookupList(container);
184184
// case 1: (.package.field)

src/test/java/io/protostuff/jetbrains/plugin/IconsTest.java

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/test/java/io/protostuff/jetbrains/plugin/reference/CustomOptionReferenceTest.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,35 @@ public void testCustomOptionReference() {
3232
Assert.assertTrue(field.getParent() instanceof ExtendEntryNode);
3333
}
3434

35+
public void testImportedCustomOptionReference() {
36+
myFixture.configureByFile("reference/options/custom/SimpleExtensionField.proto");
37+
PsiReference reference = myFixture.getReferenceAtCaretPositionWithAssertion(
38+
"reference/options/custom/ImportedExtension.proto"
39+
);
40+
PsiElement target = reference.resolve();
41+
Assert.assertNotNull(target);
42+
Assert.assertTrue(target instanceof FieldNode);
43+
FieldNode field = (FieldNode) target;
44+
Assert.assertEquals("bar", field.getFieldName());
45+
Assert.assertTrue(field.getParent() instanceof ExtendEntryNode);
46+
}
47+
48+
public void testImportedImportedCustomOptionReference() {
49+
myFixture.configureByFiles(
50+
"reference/options/custom/SimpleExtensionField.proto",
51+
"reference/options/custom/ImportedExtension.proto"
52+
);
53+
PsiReference reference = myFixture.getReferenceAtCaretPositionWithAssertion(
54+
"reference/options/custom/ImportedImportedExtension.proto"
55+
);
56+
PsiElement target = reference.resolve();
57+
Assert.assertNotNull(target);
58+
Assert.assertTrue(target instanceof FieldNode);
59+
FieldNode field = (FieldNode) target;
60+
Assert.assertEquals("bar", field.getFieldName());
61+
Assert.assertTrue(field.getParent() instanceof ExtendEntryNode);
62+
}
63+
3564
public void testExtensionIsMessage_PointToMessage() {
3665
PsiReference reference = myFixture.getReferenceAtCaretPositionWithAssertion(
3766
"reference/options/custom/ExtensionIsMessage.proto"

src/test/resources/reference/options/custom/ExtensionIsMessage.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
syntax = "proto3";
22

3-
package reference.options;
3+
package reference.options.custom;
44

55
import "google/protobuf/descriptor.proto";
66

src/test/resources/reference/options/custom/ExtensionOfExtension.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
syntax = "proto3";
22

3-
package reference.options;
3+
package reference.options.custom;
44

55
import "google/protobuf/descriptor.proto";
66

src/test/resources/reference/options/custom/FieldOfExtension.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
syntax = "proto3";
22

3-
package reference.options;
3+
package reference.options.custom;
44

55
import "google/protobuf/descriptor.proto";
66

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
syntax = "proto3";
2+
3+
package reference.options.custom;
4+
5+
import public "reference/options/custom/SimpleExtensionField.proto";
6+
7+
message A {
8+
option (ba<caret>r) = 10;
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
syntax = "proto3";
2+
3+
package reference.options.custom;
4+
5+
import public "reference/options/custom/ImportedExtension.proto";
6+
7+
message A {
8+
option (ba<caret>r) = 10;
9+
}

0 commit comments

Comments
 (0)