├── 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 | 19 | 20 |
21 |

Features:

22 |
23 | 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 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 | } --------------------------------------------------------------------------------