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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/main/java/com/github/cameltooling/idea/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ public final class Constants {

public static final String OSGI_BLUEPRINT_NAMESPACE = "http://www.osgi.org/xmlns/blueprint/v1.0.0";
public static final String CAMEL_NAMESPACE = "http://camel.apache.org/schema/spring";
public static final String CAMEL_BLUEPRINT_NAMESPACE = "http://camel.apache.org/schema/blueprint";
public static final String[] ACCEPTED_NAMESPACES = new String[]{
CAMEL_NAMESPACE,
"http://camel.apache.org/schema/blueprint",
CAMEL_BLUEPRINT_NAMESPACE,
"http://www.springframework.org/schema/beans",
OSGI_BLUEPRINT_NAMESPACE
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,12 @@
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.PlatformIcons;
import com.intellij.util.ProcessingContext;
import org.apache.commons.io.FilenameUtils;
import org.jetbrains.annotations.NotNull;

import javax.swing.Icon;

import static com.github.cameltooling.idea.util.CamelIdeaUtils.PROPERTY_PLACEHOLDER_START_TAG;

/**
* Completion handler for building property result set. Hook into the process when
* scanning for property files and building the completion list in the {@link CamelPropertyPlaceholderSmartCompletionExtension}
Expand All @@ -64,7 +63,7 @@ default boolean isExcludedFile(@NotNull PsiFile file) {
/**
* Build the property completion result set to be shown in the completion dialog
*/
void buildResultSet(CompletionResultSet resultSet, CompletionQuery query, PsiFile file);
void buildResultSet(@NotNull ProcessingContext context, CompletionResultSet resultSet, CompletionQuery query, PsiFile file);

default void addResult(CompletionResultSet resultSet, String prefix, LookupElement element) {
resultSet.withPrefixMatcher(new PlainPrefixMatcher(prefix))
Expand All @@ -88,16 +87,16 @@ default Icon getFileIcon(VirtualFile vf) {
};
}

default String getPrefix(CompletionQuery query) {
default String getPrefix(CompletionQuery query, @NotNull String placeholderStartTag) {
PsiAnnotation annotation = PsiTreeUtil.getParentOfType(query.element(), PsiAnnotation.class);
if (annotation != null && CamelIdeaUtils.PROPERTY_INJECT_ANNOTATION.equals(annotation.getQualifiedName())) {
return query.valueAtPosition();
}

String prefix;
int beginIndex = query.valueAtPosition().lastIndexOf(PROPERTY_PLACEHOLDER_START_TAG);
int beginIndex = query.valueAtPosition().lastIndexOf(placeholderStartTag);
if (beginIndex >= 0) {
prefix = query.valueAtPosition().substring(beginIndex + 2);
prefix = query.valueAtPosition().substring(beginIndex + placeholderStartTag.length());
} else {
prefix = "";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
import java.util.List;
import java.util.Set;

import com.github.cameltooling.idea.util.BlueprintUtils;
import com.github.cameltooling.idea.util.CamelIdeaUtils;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.CompletionResultSet;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiAnnotation;
Expand All @@ -48,6 +50,7 @@
public class CamelPropertyPlaceholderSmartCompletionExtension implements CamelCompletionExtension {

private static final Logger LOG = Logger.getInstance(CamelPropertyPlaceholderSmartCompletionExtension.class);
static final Key<String> PROP_PLACEHOLDER_START_TAG = new Key<>("placeholderStartTag");

private final List<CamelPropertyCompletion> propertyCompletionProviders = new ArrayList<>();

Expand Down Expand Up @@ -84,7 +87,7 @@ public void addCompletions(@NotNull CompletionParameters parameters, @NotNull Pr
propertyCompletionProviders.stream()
.filter(p -> p.isValidFile(psiFile))
.forEach(p -> {
p.buildResultSet(resultSet, query, psiFile);
p.buildResultSet(context, resultSet, query, psiFile);
});
return true;
});
Expand Down Expand Up @@ -123,7 +126,20 @@ public boolean isValid(@NotNull CompletionParameters parameters, ProcessingConte
return true;
}

return CamelIdeaUtils.getService().hasUnclosedPropertyPlaceholder(query.valueAtPosition());
if (CamelIdeaUtils.getService().hasUnclosedPropertyPlaceholder(query.valueAtPosition())) {
context.put(PROP_PLACEHOLDER_START_TAG, CamelIdeaUtils.PROPERTY_PLACEHOLDER_START_TAG);
return true;
} else if (isAllowedBlueprintPlaceholderAt(query)) {
context.put(PROP_PLACEHOLDER_START_TAG, BlueprintUtils.PROPERTY_PLACEHOLDER_START_TAG);
return true;
} else {
return false;
}
}

private static boolean isAllowedBlueprintPlaceholderAt(CompletionQuery query) {
return BlueprintUtils.getService().hasUnclosedPropertyPlaceholder(query.valueAtPosition())
&& BlueprintUtils.getService().isAllowedPropertyPlaceholderLocation(query.element());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@
import com.intellij.lang.properties.psi.PropertiesFile;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiFile;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;

import static com.github.cameltooling.idea.completion.extension.CamelPropertyPlaceholderSmartCompletionExtension.PROP_PLACEHOLDER_START_TAG;
import static com.intellij.lang.properties.references.PropertiesCompletionContributor.LOOKUP_ELEMENT_RENDERER;

/**
Expand All @@ -50,8 +52,9 @@ public boolean isValidFile(@NotNull PsiFile file) {
}

@Override
public void buildResultSet(CompletionResultSet resultSet, CompletionQuery query, PsiFile file) {
String prefix = getPrefix(query);
public void buildResultSet(@NotNull ProcessingContext context, CompletionResultSet resultSet, CompletionQuery query, PsiFile file) {
String startTag = context.get(PROP_PLACEHOLDER_START_TAG);
String prefix = getPrefix(query, startTag == null ? "" : startTag);
if (file instanceof PropertiesFile pf) {
addPropertyResults(pf.getProperties(),
IProperty::getKey,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
*/
package com.github.cameltooling.idea.completion.extension;

import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
Expand All @@ -28,12 +29,15 @@
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.psi.YAMLFile;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.SafeConstructor;

import static com.github.cameltooling.idea.completion.extension.CamelPropertyPlaceholderSmartCompletionExtension.PROP_PLACEHOLDER_START_TAG;

/**
* To support smart completion where properties are loaded from <tt>.yaml</tt> files.
* <p/>
Expand All @@ -47,11 +51,11 @@ public class YamlPropertyPlaceholdersSmartCompletion implements CamelPropertyCom

@NotNull
private Map<String, Object> getProperties(VirtualFile virtualFile) {
Map<String, Object> result = new HashMap<>();
Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()));
try {
Map<String, Object> result = new HashMap<>();
try (InputStream is = virtualFile.getInputStream()){
// Parse the YAML file and return the output as a series of Maps and Lists
result = yaml.load(virtualFile.getInputStream());
result = yaml.load(is); //TODO: this is a class cast exception when the yaml contains a list at root level, or a scalar
} catch (Exception e) {
LOG.warn("Error loading yaml file: " + virtualFile, e);
}
Expand All @@ -64,12 +68,13 @@ public boolean isValidFile(@NotNull PsiFile file) {
}

@Override
public void buildResultSet(CompletionResultSet resultSet, CompletionQuery query, PsiFile file) {
public void buildResultSet(@NotNull ProcessingContext context, CompletionResultSet resultSet, CompletionQuery query, PsiFile file) {
if (CamelIdeaUtils.getService().isCamelFile(file)) { //do not extract properties from camel route files
return;
}
VirtualFile virtualFile = file.getVirtualFile();
String prefix = getPrefix(query);
String startTag = context.get(PROP_PLACEHOLDER_START_TAG);
String prefix = getPrefix(query, startTag == null ? "" : startTag);
CompletionContext ctx = new CompletionContext(prefix, virtualFile, query, resultSet);
getProperties(virtualFile).forEach((key, value) -> {
if (!isIgnored(key) && haveCommonStart(key, prefix)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.cameltooling.idea.reference;

import com.intellij.lang.properties.references.PropertyReference;
import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Provides references for property placeholders
* Using native IDEA {@link PropertyReference} references automatically enables renaming functionality.
*/
public abstract class AbstractPropertyPlaceholderReferenceContributor extends PsiReferenceContributor {

private final Pattern placeholderPattern;
private final String placeholderStartTag;

protected AbstractPropertyPlaceholderReferenceContributor(Pattern placeholderPattern, String placeholderStartTag) {
this.placeholderPattern = placeholderPattern;
this.placeholderStartTag = placeholderStartTag;
}

protected abstract List<ElementPattern<? extends PsiElement>> getAllowedPropertyPlaceholderLocations();

@Override
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
List<ElementPattern<? extends PsiElement>> patterns = getAllowedPropertyPlaceholderLocations();
if (!patterns.isEmpty()) {
PsiReferenceProvider propertyReferenceProvider = createProvider();
patterns.forEach(pattern -> {
registrar.registerReferenceProvider(pattern, propertyReferenceProvider);
});
}
}

@NotNull
private PsiReferenceProvider createProvider() {
return new CamelPsiReferenceProvider() {
@Override
public PsiReference[] getCamelReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
String text = element.getText();

List<PsiReference> references = new ArrayList<>();
Matcher matcher = placeholderPattern.matcher(text);

while (matcher.find()) {
PropertyReference propertyReference = getPropertyReference(element, matcher);
references.add(propertyReference);
}

return references.toArray(new PsiReference[0]);
}

private @NotNull PropertyReference getPropertyReference(@NotNull PsiElement element, Matcher matcher) {
String propertyName = matcher.group(1);
int startOffset = matcher.start() + placeholderStartTag.length();
int endOffset = startOffset + propertyName.length();

TextRange textRange = new TextRange(startOffset, endOffset);
return new PropertyReference(
propertyName,
element,
null,
true,
textRange
);
}
};
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,10 @@

import com.github.cameltooling.idea.util.CamelIdeaUtils;
import com.intellij.lang.properties.references.PropertyReference;
import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.ElementPattern;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceContributor;
import com.intellij.psi.PsiReferenceProvider;
import com.intellij.psi.PsiReferenceRegistrar;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;

import static com.github.cameltooling.idea.util.CamelIdeaUtils.PROPERTY_PLACEHOLDER_PATTERN;
import static com.github.cameltooling.idea.util.CamelIdeaUtils.PROPERTY_PLACEHOLDER_START_TAG;
Expand All @@ -44,52 +35,15 @@
*
* Using native IDEA {@link PropertyReference} references automatically enables renaming functionality.
*/
public class CamelPropertyPlaceholderReferenceContributor extends PsiReferenceContributor {
public class CamelPropertyPlaceholderReferenceContributor extends AbstractPropertyPlaceholderReferenceContributor {

@Override
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
List<ElementPattern<? extends PsiElement>> patterns = CamelIdeaUtils.getService().getAllowedPropertyPlaceholderLocations();
if (!patterns.isEmpty()) {
PsiReferenceProvider propertyReferenceProvider = createProvider();
patterns.forEach(pattern -> {
registrar.registerReferenceProvider(pattern, propertyReferenceProvider);
});
}
protected List<ElementPattern<? extends PsiElement>> getAllowedPropertyPlaceholderLocations() {
return CamelIdeaUtils.getService().getAllowedPropertyPlaceholderLocations();
}

@NotNull
private PsiReferenceProvider createProvider() {
return new CamelPsiReferenceProvider() {
@Override
public PsiReference[] getCamelReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
String text = element.getText();

List<PsiReference> references = new ArrayList<>();
Matcher matcher = PROPERTY_PLACEHOLDER_PATTERN.matcher(text);

while (matcher.find()) {
PropertyReference propertyReference = getPropertyReference(element, matcher);
references.add(propertyReference);
}

return references.toArray(new PsiReference[0]);
}

private static @NotNull PropertyReference getPropertyReference(@NotNull PsiElement element, Matcher matcher) {
String propertyName = matcher.group(1);
int startOffset = matcher.start() + PROPERTY_PLACEHOLDER_START_TAG.length();
int endOffset = startOffset + propertyName.length();

TextRange textRange = new TextRange(startOffset, endOffset);
return new PropertyReference(
propertyName,
element,
null,
true,
textRange
);
}
};
public CamelPropertyPlaceholderReferenceContributor() {
super(PROPERTY_PLACEHOLDER_PATTERN, PROPERTY_PLACEHOLDER_START_TAG);
}

}
Loading
Loading