Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion MAINTENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,10 @@ automatically. This will include the changelog generated earlier.
Execute the following gradle task:

```bash
IJ_REPO_USERNAME=youruser IJ_REPO_PASSWORD=yourpassword ./gradlew clean buildPlugin publishPlugin
IJ_TOKEN=yourtoken ./gradlew clean buildPlugin publishPlugin
```

Token documentation:

* http://www.jetbrains.org/intellij/sdk/docs/plugin_repository/api/plugin_upload.html
* https://www.jetbrains.com/help/hub/Manage-Permanent-Tokens.html
3 changes: 1 addition & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,7 @@ patchPluginXml {
}

publishPlugin {
username System.getenv('IJ_REPO_USERNAME')
password System.getenv('IJ_REPO_PASSWORD')
token System.getenv('IJ_TOKEN')
}

group 'fr.adrienbrault.idea.symfony2plugin'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package fr.adrienbrault.idea.symfony2plugin.completion.yaml;

import com.intellij.codeInsight.completion.CompletionContributor;
import com.intellij.codeInsight.completion.CompletionType;
import com.intellij.psi.PsiElement;
import fr.adrienbrault.idea.symfony2plugin.config.yaml.YamlElementPatternHelper;
import fr.adrienbrault.idea.symfony2plugin.util.completion.YamlKeywordsCompletionProvider;
import fr.adrienbrault.idea.symfony2plugin.util.completion.YamlTagCompletionProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.psi.YAMLKeyValue;
import org.jetbrains.yaml.psi.YAMLSequence;
import org.jetbrains.yaml.psi.YAMLSequenceItem;

/**
* @author Thomas Schulz <mail@king2500.net>
*/
public class YamlCompletionContributor extends CompletionContributor {
public YamlCompletionContributor() {
// config:
// key: !<caret>
extend(
CompletionType.BASIC,
YamlElementPatternHelper.getSingleLineTextOrTag(),
new YamlTagCompletionProvider()
);

// config:
// key: <caret>
extend(
CompletionType.BASIC,
YamlElementPatternHelper.getSingleLineText(),
new YamlKeywordsCompletionProvider()
);
}

@Override
public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) {
// Only for Yaml tag places (scalar values)
// key: !<caret>
if (!YamlElementPatternHelper.getSingleLineTextOrTag().accepts(position)
&& !(position.getPrevSibling() instanceof YAMLKeyValue)
&& !(position.getParent() instanceof YAMLSequenceItem)
&& !(position.getParent() instanceof YAMLSequence)
) {
return super.invokeAutoPopup(position, typeChar);
}

return typeChar == '!';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import fr.adrienbrault.idea.symfony2plugin.config.yaml.YamlElementPatternHelper;
import fr.adrienbrault.idea.symfony2plugin.routing.RouteGotoCompletionProvider;
import fr.adrienbrault.idea.symfony2plugin.templating.TemplateGotoCompletionRegistrar;
import fr.adrienbrault.idea.symfony2plugin.util.completion.PhpConstGotoCompletionProvider;
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -40,6 +41,12 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
YamlElementPatternHelper.getSingleLineScalarKey("decorates"),
MyDecoratedServiceCompletionProvider::new
);

// key: !php/const <caret>
registrar.register(
YamlElementPatternHelper.getPhpConstPattern(),
PhpConstGotoCompletionProvider::new
);
}

private static class MyDecoratedServiceCompletionProvider extends DecoratedServiceCompletionProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,37 @@ public static ElementPattern<PsiElement> getSingleLineScalarKey(String... keyNam
);
}

public static ElementPattern<PsiElement> getSingleLineText() {
return
PlatformPatterns
.psiElement(YAMLTokenTypes.TEXT)
.withParent(PlatformPatterns.psiElement(YAMLScalar.class)
.withParent(PlatformPatterns.or(
PlatformPatterns.psiElement(YAMLKeyValue.class),
PlatformPatterns.psiElement(YAMLSequenceItem.class)
)
)
)
.withLanguage(YAMLLanguage.INSTANCE)
;
}

public static ElementPattern<PsiElement> getSingleLineTextOrTag() {
return PlatformPatterns.or(
PlatformPatterns
.psiElement(YAMLTokenTypes.TEXT)
.withParent(PlatformPatterns.psiElement(YAMLScalar.class)
// .withParent(PlatformPatterns
// .psiElement(YAMLKeyValue.class)
// )
)
.withLanguage(YAMLLanguage.INSTANCE),
PlatformPatterns
.psiElement(YAMLTokenTypes.TAG)
.withLanguage(YAMLLanguage.INSTANCE)
);
}

/**
* provides auto complete on
*
Expand Down Expand Up @@ -721,6 +752,25 @@ public static ElementPattern<PsiElement> getTaggedServicePattern() {
);
}

/**
* !php/const <caret>
*/
public static ElementPattern<PsiElement> getPhpConstPattern() {
return
PlatformPatterns
.psiElement()
.afterLeafSkipping(
PlatformPatterns.or(
PlatformPatterns.psiElement(PsiWhiteSpace.class),
PlatformPatterns.psiElement(YAMLTokenTypes.WHITESPACE)
),
PlatformPatterns.or(
PlatformPatterns.psiElement().withText("!php/const"),
PlatformPatterns.psiElement().withText("!php/const:")
)
);
}

/**
* services:
* _defaults:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,10 @@ static public ArrayList<Method> getClassPublicMethod(PhpClass phpClass) {
return methods;
}

static public boolean hasClassConstantFields(@NotNull PhpClass phpClass) {
return phpClass.getFields().stream().anyMatch(Field::isConstant);
}

@Nullable
static public String getArrayHashValue(ArrayCreationExpression arrayCreationExpression, String keyName) {
ArrayHashElement translationArrayHashElement = PsiElementUtils.getChildrenOfType(arrayCreationExpression, PlatformPatterns.psiElement(ArrayHashElement.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package fr.adrienbrault.idea.symfony2plugin.util.completion;

import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionUtil;
import com.intellij.codeInsight.completion.InsertHandler;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.psi.PsiElement;
import com.jetbrains.php.PhpBundle;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.completion.PhpCompletionUtil;
import com.jetbrains.php.completion.PhpLookupElement;
import com.jetbrains.php.completion.PhpVariantsUtil;
import com.jetbrains.php.completion.insert.PhpInsertHandlerUtil;
import com.jetbrains.php.lang.psi.elements.Field;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import com.jetbrains.php.lang.psi.elements.PhpNamedElement;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProvider;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProviderLookupArguments;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class PhpConstGotoCompletionProvider extends GotoCompletionProvider {

private static final String[] SPECIAL_STUB_CONSTANTS = new String[]{"true", "false", "null"};
private static final String SCOPE_OPERATOR = "::";

public PhpConstGotoCompletionProvider(@NotNull PsiElement element) {
super(element);
}

@Override
public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arguments) {
PhpIndex phpIndex = PhpIndex.getInstance(this.getProject());
CompletionResultSet resultSet = arguments.getResultSet();

final String prefix = getElement().getText().replace(CompletionUtil.DUMMY_IDENTIFIER_TRIMMED, "");

// Class constants: !php/const Foo::<caret>
if (prefix.contains(SCOPE_OPERATOR)) {
String classFQN = prefix.substring(0, getElement().getText().indexOf(SCOPE_OPERATOR));
PhpClass phpClass = PhpElementsUtil.getClassInterface(this.getProject(), classFQN);
if (phpClass != null) {
// reset the prefix matcher, starting after ::
resultSet = resultSet.withPrefixMatcher(prefix.substring(prefix.indexOf(SCOPE_OPERATOR) + 2));
resultSet.addAllElements(PhpVariantsUtil.getLookupItems(phpClass.getFields().stream().filter(Field::isConstant).collect(Collectors.toList()), false, null));
}
return;
}

Collection<LookupElement> elements = new ArrayList<>();

// Global constants: !php/const BAR
for (String constantName : phpIndex.getAllConstantNames(resultSet.getPrefixMatcher())) {
if (Arrays.asList(SPECIAL_STUB_CONSTANTS).contains(constantName)) {
continue;
}
elements.addAll(PhpVariantsUtil.getLookupItems(phpIndex.getConstantsByName(constantName), false, null));
}

if (arguments.getParameters().getInvocationCount() <= 1) {
String completionShortcut = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("CodeCompletion"));
resultSet.addLookupAdvertisement(PhpBundle.message("completion.press.again.to.see.more.variants", completionShortcut));

// Classes and interfaces: !php/const Foo
for (String className : phpIndex.getAllClassNames(resultSet.getPrefixMatcher())) {
addAllClasses(elements, phpIndex.getClassesByName(className));
addAllClasses(elements, phpIndex.getInterfacesByName(className));
}
} else {
// Constants from all classes and interfaces: !php/const Foo::BAR
for (String className : phpIndex.getAllClassNames(null)) {
addAllClassConstants(elements, phpIndex.getClassesByName(className));
addAllClassConstants(elements, phpIndex.getInterfacesByName(className));
}
}

arguments.addAllElements(elements);
}

private void addAllClasses(Collection<LookupElement> elements, Collection<PhpClass> classes) {
for (PhpClass phpClass : classes) {
// Filter by classes only with constants (including inherited constants)
if (PhpElementsUtil.hasClassConstantFields(phpClass)) {
elements.add(wrapClassInsertHandler(new MyPhpLookupElement(phpClass)));
}
}
}

private void addAllClassConstants(Collection<LookupElement> elements, Collection<PhpClass> classes) {
for (PhpClass phpClass : classes) {
// All class constants
List<Field> fields = Arrays.stream(phpClass.getOwnFields()).filter(Field::isConstant).collect(Collectors.toList());
for (PhpNamedElement field : fields) {
// Foo::BAR
String lookupString = phpClass.getName() + SCOPE_OPERATOR + field.getName();
elements.add(wrapClassConstInsertHandler(new MyPhpLookupElement(field, lookupString)));
}
}
}

private static MyPhpLookupElement wrapClassInsertHandler(MyPhpLookupElement lookupElement) {
return lookupElement.withInsertHandler(PhpClassWithScopeOperatorInsertHandler.getInstance());
}

private static MyPhpLookupElement wrapClassConstInsertHandler(MyPhpLookupElement lookupElement) {
return lookupElement.withInsertHandler(PhpReferenceTrimBackslashInsertHandler.getInstance());
}

private static class MyPhpLookupElement extends PhpLookupElement {

MyPhpLookupElement(@NotNull PhpNamedElement namedElement) {
super(namedElement);
}

MyPhpLookupElement(@NotNull PhpNamedElement namedElement, @NotNull String lookupString) {
super(namedElement);
this.lookupString = lookupString;
}

MyPhpLookupElement withInsertHandler(InsertHandler insertHandler) {
this.handler = insertHandler;
return this;
}
}

public static class PhpClassWithScopeOperatorInsertHandler extends PhpReferenceTrimBackslashInsertHandler {
private static final PhpClassWithScopeOperatorInsertHandler instance = new PhpClassWithScopeOperatorInsertHandler();

public void handleInsert(@NotNull InsertionContext context, @NotNull LookupElement lookupElement) {
super.handleInsert(context, lookupElement);

if (context.getCompletionChar() == ':') {
context.setAddCompletionChar(false);
}

if (!PhpInsertHandlerUtil.isStringAtCaret(context.getEditor(), SCOPE_OPERATOR)) {
PhpInsertHandlerUtil.insertStringAtCaret(context.getEditor(), SCOPE_OPERATOR);
}

PhpCompletionUtil.showCompletion(context);
}

public static PhpClassWithScopeOperatorInsertHandler getInstance() {
return instance;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package fr.adrienbrault.idea.symfony2plugin.util.completion;

import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionProvider;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.codeInsight.completion.CompletionUtil;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.util.ProcessingContext;
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.Map;

/**
* @author Thomas Schulz <mail@king2500.net>
*/
public class YamlKeywordsCompletionProvider extends CompletionProvider<CompletionParameters> {
private final static Map<String, String> YAML_KEYWORDS = new HashMap<String, String>() {{
put("~", "null");
put("null", "null");
put("true", "bool");
put("false", "bool");
put(".inf", "double");
}};

@Override
protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) {
PsiElement psiElement = parameters.getPosition();

if (psiElement instanceof LeafPsiElement) {
// Don't complete after tag (at end of line)
// key: !my_tag <caret>\n
if (YamlHelper.isElementAfterYamlTag(psiElement)) {
return;
}

// Don't complete after End Of Line:
// key: foo\n
// <caret>
if (YamlHelper.isElementAfterEol(psiElement)) {
return;
}

String prefix = psiElement.getText();

if (prefix.contains(CompletionUtil.DUMMY_IDENTIFIER_TRIMMED)) {
prefix = prefix.substring(0, prefix.indexOf(CompletionUtil.DUMMY_IDENTIFIER_TRIMMED));
}

result = result.withPrefixMatcher(prefix);
}

for (Map.Entry<String, String> entry : YAML_KEYWORDS.entrySet()) {
String yamlKeyword = entry.getKey();
String yamlType = entry.getValue();

LookupElementBuilder lookupElement = LookupElementBuilder.create(yamlKeyword)
.withTypeText(yamlType);

result.addElement(lookupElement);
}
}
}
Loading