Skip to content

Commit 81e5a2c

Browse files
committed
Provide auto-generated documentation for stored procedures
1 parent 2c70b8a commit 81e5a2c

File tree

13 files changed

+239
-102
lines changed

13 files changed

+239
-102
lines changed

language/cypher/src/main/gen/com/neueda/jetbrains/plugin/graphdb/language/cypher/psi/CypherPatternElement.java

Lines changed: 10 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

language/cypher/src/main/java/com/neueda/jetbrains/plugin/graphdb/language/cypher/completion/metadata/CypherMetadataProviderService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.neueda.jetbrains.plugin.graphdb.language.cypher.completion.metadata.elements.*;
44

55
import java.util.List;
6+
import java.util.Optional;
67

78
public interface CypherMetadataProviderService {
89

@@ -19,4 +20,6 @@ public interface CypherMetadataProviderService {
1920
List<CypherProcedureElement> getProcedures();
2021

2122
List<CypherUserFunctionElement> getUserFunctions();
23+
24+
Optional<CypherProcedureElement> findProcedure(String fullName);
2225
}

language/cypher/src/main/java/com/neueda/jetbrains/plugin/graphdb/language/cypher/completion/metadata/CypherMetadataProviderServiceImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.HashMap;
66
import java.util.List;
77
import java.util.Map;
8+
import java.util.Optional;
89
import java.util.stream.Collectors;
910

1011
public class CypherMetadataProviderServiceImpl implements CypherMetadataProviderService {
@@ -67,4 +68,13 @@ public List<CypherUserFunctionElement> getUserFunctions() {
6768
.flatMap((container) -> container.getUserFunctions().stream())
6869
.collect(Collectors.toList());
6970
}
71+
72+
@Override
73+
public Optional<CypherProcedureElement> findProcedure(String name) {
74+
return sourceData.entrySet().stream()
75+
.map(Map.Entry::getValue)
76+
.flatMap((container) -> container.getProcedures().stream())
77+
.filter((procedureElement) -> procedureElement.getName().equals(name))
78+
.findFirst();
79+
}
7080
}
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,8 @@
11
package com.neueda.jetbrains.plugin.graphdb.language.cypher.completion.metadata.elements;
22

33
import com.intellij.codeInsight.lookup.LookupElement;
4-
import com.intellij.openapi.util.text.StringUtil;
5-
6-
import java.util.regex.Matcher;
7-
import java.util.regex.Pattern;
84

95
public interface CypherElement {
106

11-
Pattern FULL_SIGNATURE_REGEXP = Pattern.compile("^(\\([^)]*\\)) :: \\(?([^)]*)\\)?$");
12-
137
LookupElement getLookupElement();
14-
15-
default InvokableInformation extractInformation(String fullSignature, String name) {
16-
return new InvokableInformation(fullSignature, name);
17-
}
18-
19-
final class InvokableInformation {
20-
private final String signature;
21-
private final String returnType;
22-
private final boolean hasParameters;
23-
24-
public InvokableInformation(String fullSignature, String name) {
25-
String signatureWithoutName = StringUtil.trimStart(fullSignature, name);
26-
Matcher m = FULL_SIGNATURE_REGEXP.matcher(signatureWithoutName);
27-
if (m.find()) {
28-
signature = m.group(1);
29-
returnType = m.group(2);
30-
} else {
31-
// should never happen, unless Neo4j changes signature syntax
32-
signature = "(?)";
33-
returnType = "procedure";
34-
}
35-
36-
37-
this.hasParameters = !this.signature.startsWith("()");
38-
}
39-
40-
public String getSignature() {
41-
return signature;
42-
}
43-
44-
public String getReturnType() {
45-
return returnType;
46-
}
47-
48-
public boolean isHasParameters() {
49-
return hasParameters;
50-
}
51-
}
528
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.neueda.jetbrains.plugin.graphdb.language.cypher.completion.metadata.elements;
2+
3+
public interface CypherElementWithDocumentation {
4+
5+
String getDocumentation();
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.neueda.jetbrains.plugin.graphdb.language.cypher.completion.metadata.elements;
2+
3+
import com.intellij.openapi.util.text.StringUtil;
4+
5+
import java.util.regex.Matcher;
6+
import java.util.regex.Pattern;
7+
8+
public interface CypherElementWithSignature {
9+
Pattern FULL_SIGNATURE_REGEXP = Pattern.compile("^(\\([^)]*\\)) :: \\(?([^)]*)\\)?$");
10+
11+
InvokableInformation getInvokableInformation();
12+
13+
default InvokableInformation extractInformation(String fullSignature, String name) {
14+
return new InvokableInformation(fullSignature, name);
15+
}
16+
17+
final class InvokableInformation {
18+
private final String signature;
19+
private final String returnType;
20+
private final boolean hasParameters;
21+
22+
public InvokableInformation(String fullSignature, String name) {
23+
String signatureWithoutName = StringUtil.trimStart(fullSignature, name);
24+
Matcher m = FULL_SIGNATURE_REGEXP.matcher(signatureWithoutName);
25+
if (m.find()) {
26+
signature = m.group(1);
27+
returnType = m.group(2);
28+
} else {
29+
// should never happen, unless Neo4j changes signature syntax
30+
signature = fullSignature;
31+
returnType = "<?>";
32+
}
33+
34+
35+
this.hasParameters = !this.signature.startsWith("()");
36+
}
37+
38+
public String getSignature() {
39+
return signature;
40+
}
41+
42+
public String getReturnType() {
43+
return returnType;
44+
}
45+
46+
public boolean isHasParameters() {
47+
return hasParameters;
48+
}
49+
}
50+
}

language/cypher/src/main/java/com/neueda/jetbrains/plugin/graphdb/language/cypher/completion/metadata/elements/CypherProcedureElement.java

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,44 @@
66
import com.neueda.jetbrains.plugin.graphdb.platform.GraphIcons;
77
import org.jetbrains.annotations.Nullable;
88

9-
public class CypherProcedureElement implements CypherElement {
9+
public class CypherProcedureElement implements CypherElement, CypherElementWithSignature, CypherElementWithDocumentation {
1010

1111
private final String name;
12-
private final InvokableInformation information;
12+
@Nullable
13+
private final String description;
14+
private final InvokableInformation invokableInformation;
15+
private String documentation;
1316

1417
public CypherProcedureElement(String name, String fullSignature, @Nullable String description) {
1518
this.name = name;
16-
this.information = extractInformation(fullSignature, name);
19+
this.description = description;
20+
this.invokableInformation = extractInformation(fullSignature, name);
1721
}
1822

19-
public InvokableInformation getInformation() {
20-
return information;
23+
public String getName() {
24+
return name;
25+
}
26+
27+
@Override
28+
public InvokableInformation getInvokableInformation() {
29+
return invokableInformation;
30+
}
31+
32+
public String getDocumentation() {
33+
if (documentation == null) {
34+
documentation = ""
35+
+ "<b>" + name + "</b><br>"
36+
+ "Arguments:<br>"
37+
+ "&nbsp;&nbsp;&nbsp;&nbsp;" + invokableInformation.getSignature() + "<br>"
38+
+ "Return:<br>"
39+
+ "&nbsp;&nbsp;&nbsp;&nbsp;" + invokableInformation.getReturnType();
40+
41+
if (description != null) {
42+
documentation += "<br><br>"
43+
+ description;
44+
}
45+
}
46+
return documentation;
2147
}
2248

2349
@Override
@@ -26,8 +52,8 @@ public LookupElement getLookupElement() {
2652
.create(name)
2753
.bold()
2854
.withIcon(GraphIcons.Nodes.STORED_PROCEDURE)
29-
.withTailText(information.getSignature())
30-
.withTypeText(information.getReturnType())
31-
.withInsertHandler(ParenthesesInsertHandler.getInstance(information.isHasParameters()));
55+
.withTailText(invokableInformation.getSignature())
56+
.withTypeText(invokableInformation.getReturnType())
57+
.withInsertHandler(ParenthesesInsertHandler.getInstance(invokableInformation.isHasParameters()));
3258
}
3359
}

language/cypher/src/main/java/com/neueda/jetbrains/plugin/graphdb/language/cypher/completion/metadata/elements/CypherUserFunctionElement.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,18 @@
66
import com.neueda.jetbrains.plugin.graphdb.platform.GraphIcons;
77
import org.jetbrains.annotations.Nullable;
88

9-
public class CypherUserFunctionElement implements CypherElement {
9+
public class CypherUserFunctionElement implements CypherElement, CypherElementWithSignature {
1010
private final String name;
11-
private final InvokableInformation information;
11+
private final InvokableInformation invokableInformation;
1212

1313
public CypherUserFunctionElement(String name, String signature, @Nullable String description) {
1414
this.name = name;
15-
this.information = extractInformation(signature, name);
15+
this.invokableInformation = extractInformation(signature, name);
16+
}
17+
18+
@Override
19+
public InvokableInformation getInvokableInformation() {
20+
return invokableInformation;
1621
}
1722

1823
@Override
@@ -21,8 +26,8 @@ public LookupElement getLookupElement() {
2126
.create(name)
2227
.bold()
2328
.withIcon(GraphIcons.Nodes.USER_FUNCTION)
24-
.withTailText(information.getSignature())
25-
.withTypeText(information.getReturnType())
26-
.withInsertHandler(ParenthesesInsertHandler.getInstance(information.isHasParameters()));
29+
.withTailText(invokableInformation.getSignature())
30+
.withTypeText(invokableInformation.getReturnType())
31+
.withInsertHandler(ParenthesesInsertHandler.getInstance(invokableInformation.isHasParameters()));
2732
}
2833
}

language/cypher/src/main/java/com/neueda/jetbrains/plugin/graphdb/language/cypher/documentation/CypherDocumentationProvider.java

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@
22

33
import com.google.common.collect.Lists;
44
import com.intellij.lang.documentation.AbstractDocumentationProvider;
5+
import com.intellij.openapi.components.ServiceManager;
56
import com.intellij.openapi.editor.Editor;
67
import com.intellij.psi.PsiElement;
78
import com.intellij.psi.PsiFile;
89
import com.intellij.psi.tree.IElementType;
10+
import com.neueda.jetbrains.plugin.graphdb.language.cypher.completion.metadata.CypherMetadataProviderService;
11+
import com.neueda.jetbrains.plugin.graphdb.language.cypher.completion.metadata.elements.CypherProcedureElement;
912
import com.neueda.jetbrains.plugin.graphdb.language.cypher.documentation.database.CypherDocumentation;
1013
import com.neueda.jetbrains.plugin.graphdb.language.cypher.psi.*;
1114
import com.neueda.jetbrains.plugin.graphdb.language.cypher.util.TraverseUtil;
1215
import org.jetbrains.annotations.NotNull;
1316
import org.jetbrains.annotations.Nullable;
1417

1518
import java.util.List;
19+
import java.util.Optional;
1620

1721
public class CypherDocumentationProvider extends AbstractDocumentationProvider {
1822

@@ -42,53 +46,79 @@ public PsiElement getCustomDocumentationElement(@NotNull Editor editor,
4246
@Nullable
4347
@Override
4448
public String generateDoc(PsiElement element, @Nullable PsiElement originalElement) {
49+
Optional<String> builtInFunctionDocumentation = builtInFunctionDocumentation(element);
50+
if (builtInFunctionDocumentation.isPresent()) {
51+
return builtInFunctionDocumentation.get();
52+
}
53+
54+
Optional<String> specialFunctionDocumentation = specialFunctionDocumentation(element);
55+
if (specialFunctionDocumentation.isPresent()) {
56+
return specialFunctionDocumentation.get();
57+
}
58+
59+
Optional<String> storedProcedureDocumentation = storedProcedureDocumentation(element);
60+
if (storedProcedureDocumentation.isPresent()) {
61+
return storedProcedureDocumentation.get();
62+
}
63+
64+
return null;
65+
}
66+
67+
private Optional<String> builtInFunctionDocumentation(PsiElement element) {
4568
if (element instanceof CypherFunctionInvocation) {
4669
CypherFunctionInvocation invocation = (CypherFunctionInvocation) element;
4770
return CypherDocumentation.BUILT_IN_FUNCTIONS
48-
.lookup(invocation.getFullName())
49-
.orElse(null);
50-
} else if (element instanceof CypherShortestPathFunctionInvocation) {
71+
.lookup(invocation.getFullName());
72+
}
73+
return Optional.empty();
74+
}
75+
76+
private Optional<String> specialFunctionDocumentation(PsiElement element) {
77+
if (element instanceof CypherShortestPathFunctionInvocation) {
5178
return CypherDocumentation.BUILT_IN_FUNCTIONS
52-
.lookup("shortestpath")
53-
.orElse(null);
79+
.lookup("shortestpath");
5480
} else if (element instanceof CypherAllShortestPathsFunctionInvocation) {
5581
return CypherDocumentation.BUILT_IN_FUNCTIONS
56-
.lookup("allshortestpaths")
57-
.orElse(null);
82+
.lookup("allshortestpaths");
5883
} else if (element instanceof CypherFilterFunctionInvocation) {
5984
return CypherDocumentation.BUILT_IN_FUNCTIONS
60-
.lookup("filter")
61-
.orElse(null);
85+
.lookup("filter");
6286
} else if (element instanceof CypherExtractFunctionInvocation) {
6387
return CypherDocumentation.BUILT_IN_FUNCTIONS
64-
.lookup("extract")
65-
.orElse(null);
88+
.lookup("extract");
6689
} else if (element instanceof CypherReduceFunctionInvocation) {
6790
return CypherDocumentation.BUILT_IN_FUNCTIONS
68-
.lookup("reduce")
69-
.orElse(null);
91+
.lookup("reduce");
7092
} else if (element instanceof CypherAllFunctionInvocation) {
7193
return CypherDocumentation.BUILT_IN_FUNCTIONS
72-
.lookup("all")
73-
.orElse(null);
94+
.lookup("all");
7495
} else if (element instanceof CypherAnyFunctionInvocation) {
7596
return CypherDocumentation.BUILT_IN_FUNCTIONS
76-
.lookup("any")
77-
.orElse(null);
97+
.lookup("any");
7898
} else if (element instanceof CypherNoneFunctionInvocation) {
7999
return CypherDocumentation.BUILT_IN_FUNCTIONS
80-
.lookup("none")
81-
.orElse(null);
100+
.lookup("none");
82101
} else if (element instanceof CypherSingleFunctionInvocation) {
83102
return CypherDocumentation.BUILT_IN_FUNCTIONS
84-
.lookup("single")
85-
.orElse(null);
103+
.lookup("single");
86104
} else if (element instanceof CypherExistsFunctionInvocation) {
87105
return CypherDocumentation.BUILT_IN_FUNCTIONS
88-
.lookup("exists")
89-
.orElse(null);
106+
.lookup("exists");
107+
}
108+
return Optional.empty();
109+
}
110+
111+
private Optional<String> storedProcedureDocumentation(PsiElement element) {
112+
if (element instanceof CypherProcedureInvocation) {
113+
CypherProcedureInvocation cypherProcedureInvocation = (CypherProcedureInvocation) element;
114+
return getMetadataService(element)
115+
.findProcedure(cypherProcedureInvocation.getFullName())
116+
.map(CypherProcedureElement::getDocumentation);
90117
}
118+
return Optional.empty();
119+
}
91120

92-
return null;
121+
private CypherMetadataProviderService getMetadataService(PsiElement element) {
122+
return ServiceManager.getService(element.getProject(), CypherMetadataProviderService.class);
93123
}
94124
}

0 commit comments

Comments
 (0)