├── LICENSE
├── META-INF
└── plugin.xml
├── README.md
├── options-completion-phpstorm-plugin.iml
├── src
└── com
│ └── github
│ └── woru
│ └── phpoptionscompletion
│ ├── OptionsCompletionContributor.java
│ ├── OptionsParam.java
│ └── PhpDocCommentParser.java
└── test
└── com
└── github
└── woru
└── phpoptionscompletion
└── PhpDocCommentParserTest.java
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2013-2014 Ouzo contributors, http://ouzoframework.org
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | com.github.woru.options-completion-phpstorm-plugin
3 | Options completion plugin
4 | 0.0.7
5 | Ouzo contributors
6 |
7 | Options completion plugin
9 |
10 |
11 | Parses phpDocumentor's hash description (https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md#7-describing-hashes) and shows supported keys.
12 |
13 |
14 | What's new:
15 |
16 |
17 | - Support for multiple types in key definition
18 |
19 |
20 |
21 | Features:
22 |
23 |
24 | - Complete array keys for function parameters
25 | - Show elements type in completion
26 |
27 | ]]>
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | com.jetbrains.php
39 | com.intellij.modules.platform
40 |
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Options completion plugin
2 | ====================
3 |
4 | https://plugins.jetbrains.com/plugin/7822
5 |
6 | Parses [phpDocumentor's hash description](https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md#examples-12) and shows supported keys.
7 |
8 | ```php
9 | class Element {
10 | /**
11 | * Initializes this class with the given options.
12 | *
13 | * @param array $options {
14 | * @var bool $required Whether this element is required
15 | * @var string $label The display name for this element
16 | * }
17 | */
18 | public function __construct(array $options = array())
19 | {
20 | <...>
21 | }
22 | <...>
23 | }
24 |
25 |
26 | new Element(['label' => 'Bob', '|' ]);
27 | // | ctrl+space will show supported attributes
28 | ```
29 |
--------------------------------------------------------------------------------
/options-completion-phpstorm-plugin.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/com/github/woru/phpoptionscompletion/OptionsCompletionContributor.java:
--------------------------------------------------------------------------------
1 | package com.github.woru.phpoptionscompletion;
2 |
3 | import com.google.common.collect.Sets;
4 | import com.intellij.codeInsight.completion.CompletionContributor;
5 | import com.intellij.codeInsight.completion.CompletionParameters;
6 | import com.intellij.codeInsight.completion.CompletionResultSet;
7 | import com.intellij.codeInsight.lookup.LookupElementBuilder;
8 | import com.intellij.patterns.PlatformPatterns;
9 | import com.intellij.psi.PsiElement;
10 | import com.intellij.psi.util.PsiTreeUtil;
11 | import com.jetbrains.php.PhpIndex;
12 | import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment;
13 | import com.jetbrains.php.lang.parser.PhpElementTypes;
14 | import com.jetbrains.php.lang.psi.elements.*;
15 | import com.jetbrains.php.lang.psi.elements.impl.PhpExpressionImpl;
16 | import com.jetbrains.php.lang.psi.resolve.types.PhpType;
17 | import org.jetbrains.annotations.NotNull;
18 |
19 | import java.util.Collection;
20 | import java.util.HashSet;
21 | import java.util.Map;
22 | import java.util.Set;
23 |
24 |
25 | public class OptionsCompletionContributor extends CompletionContributor {
26 | @Override
27 | public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) {
28 | PsiElement element = parameters.getPosition().getParent();
29 |
30 | ParameterList parameterList = PsiTreeUtil.getParentOfType(element, ParameterList.class);
31 | if (parameterList == null) {
32 | return;
33 | }
34 | PsiElement[] givenParameters = parameterList.getParameters();
35 | PsiElement context = parameterList.getContext();
36 | if (context instanceof FunctionReference) {
37 | FunctionReference function = (FunctionReference) context;
38 | addCompletionForFunctionOptions(function, element, givenParameters, result);
39 | } else if (context instanceof NewExpression) {
40 | NewExpression newExpression = (NewExpression) context;
41 | addCompletionForConstructorOptions(newExpression, element, givenParameters, result);
42 | }
43 | }
44 |
45 | private void addCompletionForConstructorOptions(NewExpression newExpression, PsiElement element, PsiElement[] givenParameters, CompletionResultSet result) {
46 | ClassReference classReference = newExpression.getClassReference();
47 | if (classReference != null) {
48 | PsiElement resolvedReference = classReference.resolve();
49 | Method constructor = resolveConstructor(resolvedReference);
50 | if (constructor != null) {
51 | PhpDocComment docComment = constructor.getDocComment();
52 | if (docComment != null) {
53 | addCompletionForOptions(result, element, givenParameters, docComment.getText());
54 | }
55 | }
56 | }
57 | }
58 |
59 | private Method resolveConstructor(PsiElement resolvedReference) {
60 | if (resolvedReference instanceof Method) {
61 | return (Method) resolvedReference;
62 | }
63 |
64 | if (resolvedReference instanceof PhpClass) {
65 | PhpClass phpClass = (PhpClass) resolvedReference;
66 | return phpClass.getConstructor();
67 | }
68 | return null;
69 | }
70 |
71 | private void addCompletionForFunctionOptions(FunctionReference function, PsiElement element, PsiElement[] givenParameters, CompletionResultSet result) {
72 | PhpIndex phpIndex = PhpIndex.getInstance(element.getProject());
73 | String signature = function.getSignature();
74 | String[] variants = signature.split("\\|");
75 | for (String variant : variants) {
76 | Collection extends PhpNamedElement> bySignature = phpIndex.getBySignature(variant);
77 | for (PhpNamedElement phpNamedElement : bySignature) {
78 | PhpDocComment docComment = phpNamedElement.getDocComment();
79 | if (docComment != null) {
80 | addCompletionForOptions(result, element, givenParameters, docComment.getText());
81 | }
82 | }
83 | }
84 | }
85 |
86 | private void addCompletionForOptions(CompletionResultSet result, PsiElement element, PsiElement[] givenParameters, String docCommentText) {
87 | ArrayCreationExpression arrayCreation = PsiTreeUtil.getParentOfType(element, ArrayCreationExpression.class);
88 | if (arrayCreation != null && canBecomeKey(element)) {
89 | Integer parameterIndex = getParameterIndex(givenParameters, arrayCreation);
90 | if (parameterIndex != null) {
91 | Map optionsParams = new PhpDocCommentParser().parse(docCommentText);
92 | OptionsParam optionsParam = optionsParams.get(parameterIndex);
93 | if (optionsParam != null) {
94 | Map options = optionsParam.getOptions();
95 | for (String name : Sets.difference(options.keySet(), getAllKeys(arrayCreation))) {
96 | result.addElement(LookupElementBuilder.create(name).withTypeText(options.get(name)));
97 | }
98 | }
99 | }
100 | }
101 | }
102 |
103 | private Set getAllKeys(ArrayCreationExpression arrayCreation) {
104 | Set keys = new HashSet();
105 | ArrayHashElement[] hashElements = PsiTreeUtil.getChildrenOfType(arrayCreation, ArrayHashElement.class);
106 | if (hashElements == null) {
107 | return keys;
108 | }
109 | for (ArrayHashElement hashElement : hashElements) {
110 | keys.add(getContent(hashElement.getKey()));
111 | }
112 | return keys;
113 | }
114 |
115 | private boolean canBecomeKey(PsiElement element) {
116 | return (PsiTreeUtil.getParentOfType(element, ArrayHashElement.class) == null || PlatformPatterns.psiElement(PhpElementTypes.ARRAY_KEY).accepts(element.getParent()));
117 | }
118 |
119 | private Integer getParameterIndex(PsiElement[] methodParameters, PsiElement parameterElement) {
120 | for (int i = 0; i < methodParameters.length; i++) {
121 | if (methodParameters[i].equals(parameterElement)) {
122 | return i;
123 | }
124 | }
125 | return null;
126 | }
127 |
128 | public static String getContent(PsiElement value) {
129 | if (value instanceof StringLiteralExpression) {
130 | return ((StringLiteralExpression) value).getContents();
131 | }
132 | if (value instanceof PhpExpressionImpl && ((PhpExpressionImpl) value).getType().equals(PhpType.INT)) {
133 | return value.getText();
134 | }
135 |
136 | if (value instanceof ConstantReference || value instanceof ClassConstantReference) {
137 | return value.getText(); //we cannot resolve constant value when this method is called from index because indices are not accessible then.
138 | }
139 | return null;
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/com/github/woru/phpoptionscompletion/OptionsParam.java:
--------------------------------------------------------------------------------
1 | package com.github.woru.phpoptionscompletion;
2 |
3 |
4 | import java.util.Map;
5 |
6 | public class OptionsParam {
7 | private final int position;
8 | private final Map options;
9 |
10 | public OptionsParam(int position, Map options) {
11 | this.position = position;
12 | this.options = options;
13 | }
14 |
15 | public int getPosition() {
16 | return position;
17 | }
18 |
19 | public Map getOptions() {
20 | return options;
21 | }
22 |
23 | @Override
24 | public boolean equals(Object o) {
25 | if (this == o) return true;
26 | if (o == null || getClass() != o.getClass()) return false;
27 |
28 | OptionsParam that = (OptionsParam) o;
29 |
30 | if (position != that.position) return false;
31 | if (!options.equals(that.options)) return false;
32 |
33 | return true;
34 | }
35 |
36 | @Override
37 | public int hashCode() {
38 | int result = position;
39 | result = 31 * result + options.hashCode();
40 | return result;
41 | }
42 |
43 | @Override
44 | public String toString() {
45 | return "OptionsParam{" +
46 | "position=" + position +
47 | ", options=" + options +
48 | '}';
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/com/github/woru/phpoptionscompletion/PhpDocCommentParser.java:
--------------------------------------------------------------------------------
1 | package com.github.woru.phpoptionscompletion;
2 |
3 |
4 | import java.util.HashMap;
5 | import java.util.Map;
6 | import java.util.regex.Matcher;
7 | import java.util.regex.Pattern;
8 |
9 | public class PhpDocCommentParser {
10 | private static final Pattern paramPattern = Pattern.compile("(?:@param\\s+array\\s+\\$\\w+\\s+\\{([^}]+)\\})|(?:@param[^}\n]+)");
11 | private static final Pattern optionPattern = Pattern.compile("@(?:var|type)\\s+(\\w+(?:\\|\\w+)*)\\s+\\$(\\w+)[^\n]*");
12 |
13 | public Map parse(String comment) {
14 | int position = 0;
15 | Map optionsParams = new HashMap();
16 | Matcher matcher = paramPattern.matcher(comment);
17 | while (matcher.find()) {
18 | String optionsString = matcher.group(1);
19 | Map options = parseOptions(optionsString);
20 | if (!options.isEmpty()) {
21 | optionsParams.put(position, new OptionsParam(position, options));
22 | }
23 | position++;
24 | }
25 | return optionsParams;
26 | }
27 |
28 | private Map parseOptions(String optionsString) {
29 | Map options = new HashMap();
30 | if (optionsString == null) {
31 | return options;
32 | }
33 | Matcher optionsMatcher = optionPattern.matcher(optionsString);
34 |
35 | while (optionsMatcher.find()) {
36 | String type = optionsMatcher.group(1);
37 | String name = optionsMatcher.group(2);
38 | options.put(name, type);
39 | }
40 | return options;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/test/com/github/woru/phpoptionscompletion/PhpDocCommentParserTest.java:
--------------------------------------------------------------------------------
1 | package com.github.woru.phpoptionscompletion;
2 |
3 | import com.google.common.collect.ImmutableMap;
4 | import org.junit.Test;
5 |
6 | import java.util.Map;
7 |
8 | import static org.junit.Assert.assertEquals;
9 |
10 | public class PhpDocCommentParserTest {
11 | @Test
12 | public void parseSingleOptionsParameterDeclaredWithVarAnnotation() {
13 | String comment = " Initializes this class with the given options.\n" +
14 | " \n" +
15 | " @param array $options {\n" +
16 | " @var bool $required Whether this element is required\n" +
17 | " @var string $label The display name for this element\n" +
18 | " }";
19 |
20 | Map optionsParams = new PhpDocCommentParser().parse(comment);
21 |
22 | assertEquals(1, optionsParams.size());
23 | assertEquals(new OptionsParam(0, ImmutableMap.of("required", "bool", "label", "string")), optionsParams.get(0));
24 | }
25 |
26 | @Test
27 | public void parseSingleOptionsParameterDeclaredWithTypeAnnotation() {
28 | String comment = " Initializes this class with the given options.\n" +
29 | " \n" +
30 | " @param array $options {\n" +
31 | " @type bool $required Whether this element is required\n" +
32 | " @type string $label The display name for this element\n" +
33 | " }";
34 |
35 | Map optionsParams = new PhpDocCommentParser().parse(comment);
36 |
37 | assertEquals(1, optionsParams.size());
38 | assertEquals(new OptionsParam(0, ImmutableMap.of("required", "bool", "label", "string")), optionsParams.get(0));
39 | }
40 |
41 | @Test
42 | public void parseMultipleOptionsParameter() {
43 | String comment = " Initializes this class with the given options.\n" +
44 | " @param mixed[] $param1 Array structure to count the elements of. \n" +
45 | " @param array $options1 {\n" +
46 | " @var bool $required Whether this element is required\n" +
47 | " @var string $label The display name for this element\n" +
48 | " } \n" +
49 | " @param string $param2 Array structure to count the elements of. \n" +
50 | " @param array $options2 {\n" +
51 | " @var int $size\n" +
52 | " @var string $name The display name for this element\n" +
53 | " }";
54 |
55 | Map optionsParams = new PhpDocCommentParser().parse(comment);
56 |
57 | assertEquals(2, optionsParams.size());
58 | assertEquals(new OptionsParam(1, ImmutableMap.of("required", "bool", "label", "string")), optionsParams.get(1));
59 | assertEquals(new OptionsParam(3, ImmutableMap.of("size", "int", "name", "string")), optionsParams.get(3));
60 | }
61 |
62 | @Test
63 | public void parseMultipleOptionsParameterWithMultilineDescription() {
64 | String comment = " Initializes this class with the given options.\n" +
65 | " @param array $options2 {\n" +
66 | " @var int $size description\n description line2\n" +
67 | " @var string $name The display name for this element\nor something else\n" +
68 | " }";
69 |
70 | Map optionsParams = new PhpDocCommentParser().parse(comment);
71 |
72 | assertEquals(1, optionsParams.size());
73 | assertEquals(new OptionsParam(0, ImmutableMap.of("size", "int", "name", "string")), optionsParams.get(0));
74 | }
75 |
76 | @Test
77 | public void parseMultiTypeParameterDeclaredWithVarAnnotation() {
78 | String comment = " Initializes this class with the given options.\n" +
79 | " \n" +
80 | " @param array $options {\n" +
81 | " @var bool $required Whether this element is required\n" +
82 | " @var string|Description $label\n" +
83 | " }";
84 |
85 | Map optionsParams = new PhpDocCommentParser().parse(comment);
86 |
87 | assertEquals(1, optionsParams.size());
88 | assertEquals(new OptionsParam(0, ImmutableMap.of("required", "bool", "label", "string|Description")), optionsParams.get(0));
89 | }
90 |
91 | @Test
92 | public void parseMultiTypeParameterDeclaredWithThreeTypes() {
93 | String comment = " Initializes this class with the given options.\n" +
94 | " \n" +
95 | " @param array $options {\n" +
96 | " @var bool $required Whether this element is required\n" +
97 | " @var string|Description|int $label\n" +
98 | " }";
99 |
100 | Map optionsParams = new PhpDocCommentParser().parse(comment);
101 |
102 | assertEquals(1, optionsParams.size());
103 | assertEquals(new OptionsParam(0, ImmutableMap.of("required", "bool", "label", "string|Description|int")), optionsParams.get(0));
104 | }
105 | }
--------------------------------------------------------------------------------