blob: b301f0496da3ee78528db2761135162b11fcb394 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed 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 org.jetbrains.plugins.groovy.mvc.util;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.lookup.TailTypeDecorator;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.UserDataHolderEx;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiPackage;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.CachedValue;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.util.SystemProperties;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.mvc.MvcFramework;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyNamesUtil;
import java.io.File;
import java.util.*;
/**
* @author Sergey Evdokimov
*/
public class MvcTargetDialogCompletionUtils {
private static final Key<CachedValue<Set<String>>> ALL_TARGET_KEY = Key.create("MvcTargetDialogCompletionUtils");
private static final String[] SYSTEM_PROPERTIES = {
"grails.home",
// System properties from ivy
"ivy.default.ivy.user.dir", "ivy.default.conf.dir",
"ivy.local.default.root", "ivy.local.default.ivy.pattern", "ivy.local.default.artifact.pattern",
"ivy.shared.default.root", "ivy.shared.default.ivy.pattern", "ivy.shared.default.artifact.pattern",
"ivy.ivyrep.default.ivy.root", "ivy.ivyrep.default.ivy.pattern", "ivy.ivyrep.default.artifact.root", "ivy.ivyrep.default.artifact.pattern",
// System properties from grails.util.BuildSettings
"grails.servlet.version", "base.dir", "grails.work.dir", "grails.project.work.dir", "grails.project.war.exploded.dir",
"grails.project.plugins.dir", "grails.global.plugins.dir", "grails.project.resource.dir", "grails.project.source.dir",
"grails.project.web.xml", "grails.project.class.dir", "grails.project.plugin.class.dir", "grails.project.plugin.build.class.dir",
"grails.project.plugin.provided.class.dir", "grails.project.test.class.dir", "grails.project.test.reports.dir",
"grails.project.docs.output.dir", "grails.project.test.source.dir", "grails.project.target.dir", "grails.project.war.file",
"grails.project.war.file", "grails.project.war.osgi.headers", "grails.build.listeners", "grails.project.compile.verbose",
"grails.testing.functional.baseUrl", "grails.compile.artefacts.closures.convert"
};
private static List<LookupElement> SYSTEM_PROPERTIES_VARIANTS;
private MvcTargetDialogCompletionUtils() {
}
public static List<LookupElement> getSystemPropertiesVariants() {
if (SYSTEM_PROPERTIES_VARIANTS == null) {
LookupElement[] res = new LookupElement[SYSTEM_PROPERTIES.length];
for (int i = 0; i < res.length; i++) {
res[i] = TailTypeDecorator.withTail(LookupElementBuilder.create("-D" + SYSTEM_PROPERTIES[i]), MyTailTypeEQ.INSTANCE);
}
SYSTEM_PROPERTIES_VARIANTS = Arrays.asList(res);
}
return SYSTEM_PROPERTIES_VARIANTS;
}
public static Collection<LookupElement> collectVariants(@NotNull Module module, @NotNull String text, int offset, @NotNull String prefix) {
if (prefix.startsWith("-D")) {
return getSystemPropertiesVariants();
}
List<LookupElement> res = new ArrayList<LookupElement>();
if (text.substring(0, offset).matches("\\s*(grails\\s*)?(?:(:?-D\\S+|dev|prod|test)\\s+)*\\S*")) {
// Complete command name because command name is not typed.
for (String completionVariant : getAllTargetNames(module)) {
res.add(TailTypeDecorator.withTail(LookupElementBuilder.create(completionVariant), TailType.SPACE));
}
}
else {
// Command name already typed. Try to complete classes and packages names.
GlobalSearchScope scope = GlobalSearchScope.moduleScope(module);
JavaPsiFacade facade = JavaPsiFacade.getInstance(module.getProject());
// Complete class names if prefix is a package name with dot at end.
if (prefix.endsWith(".") && prefix.length() > 1) {
PsiPackage p = facade.findPackage(prefix.substring(0, prefix.length() - 1));
if (p != null) {
for (PsiClass aClass : p.getClasses(scope)) {
String qualifiedName = aClass.getQualifiedName();
if (qualifiedName != null) {
res.add(LookupElementBuilder.create(aClass, qualifiedName));
}
}
}
}
PsiPackage defaultPackage = facade.findPackage("");
if (defaultPackage != null) {
collectClassesAndPackageNames(res, defaultPackage, scope);
}
}
return res;
}
private static void collectClassesAndPackageNames(Collection<LookupElement> res, @NotNull PsiPackage aPackage, GlobalSearchScope scope) {
PsiPackage[] subPackages = aPackage.getSubPackages(scope);
String qualifiedName = aPackage.getQualifiedName();
if (!qualifiedName.isEmpty()) {
if (subPackages.length == 0 || aPackage.getClasses(scope).length > 0) {
res.add(TailTypeDecorator.withTail(LookupElementBuilder.create(qualifiedName), TailType.DOT));
}
}
for (PsiPackage subPackage : subPackages) {
collectClassesAndPackageNames(res, subPackage, scope);
}
}
public static Set<String> getAllTargetNamesInternal(@NotNull Module module) {
final Set<String> result = new HashSet<String>();
MvcFramework.addAvailableSystemScripts(result, module);
MvcFramework framework = MvcFramework.getInstance(module);
if (framework != null) {
final VirtualFile root = framework.findAppRoot(module);
if (root != null) {
MvcFramework.addAvailableScripts(result, root);
}
for (VirtualFile pluginRoot : framework.getAllPluginRoots(module, false)) {
MvcFramework.addAvailableScripts(result, pluginRoot);
}
}
collectScriptsFromUserHome(result);
return result;
}
private static void collectScriptsFromUserHome(Set<String> result) {
String userHome = SystemProperties.getUserHome();
if (userHome == null) return;
File scriptFolder = new File(userHome, ".grails/scripts");
File[] files = scriptFolder.listFiles();
if (files == null) return;
for (File file : files) {
if (file.getName().startsWith("IdeaPrintProjectSettings")) continue;
if (isScriptFile(file)) {
String name = file.getName();
int idx = name.lastIndexOf('.');
if (idx != -1) {
name = name.substring(0, idx);
}
result.add(GroovyNamesUtil.camelToSnake(name));
}
}
}
public static boolean isScriptFile(File file) {
return file.isFile() && MvcFramework.isScriptFileName(file.getName());
}
public static Set<String> getAllTargetNames(@NotNull final Module module) {
CachedValue<Set<String>> cachedTargets = module.getUserData(ALL_TARGET_KEY);
if (cachedTargets == null) {
cachedTargets = CachedValuesManager.getManager(module.getProject()).createCachedValue(new CachedValueProvider<Set<String>>() {
@Override
public Result<Set<String>> compute() {
return Result.create(getAllTargetNamesInternal(module), PsiModificationTracker.MODIFICATION_COUNT);
}
}, false);
cachedTargets = ((UserDataHolderEx)module).putUserDataIfAbsent(ALL_TARGET_KEY, cachedTargets);
}
return cachedTargets.getValue();
}
private static class MyTailTypeEQ extends TailType.TailTypeEQ {
public static final MyTailTypeEQ INSTANCE = new MyTailTypeEQ();
@Override
protected boolean isSpaceAroundAssignmentOperators(Editor editor, int tailOffset) {
return false;
}
public String toString() {
return "MvcTargetDialogCompletionUtils.TailTypeEQ";
}
}
}