/*
* Copyright 2000-2012 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.angularjs;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.find.FindManager;
import com.intellij.find.FindModel;
import com.intellij.find.impl.FindInProjectUtil;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.actions.GotoActionBase;
import com.intellij.ide.util.gotoByName.ChooseByNameFilter;
import com.intellij.ide.util.gotoByName.ChooseByNamePopup;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.PlatformDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Segment;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.PsiUtilBase;
import com.intellij.usageView.UsageInfo;
import com.intellij.usages.FindUsagesProcessPresentation;
import com.intellij.usages.Usage;
import com.intellij.usages.UsageInfo2UsageAdapter;
import com.intellij.util.AdapterProcessor;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class GotoAngularAction extends GotoActionBase {
private static final int MODULE_METHODS = 0;
private static final int CTRL_CONVENTIONS = 1;
private static final int NG_CONTROLLER = 2;
public GotoAngularAction() {
getTemplatePresentation().setText(IdeBundle.message("goto.inspection.action.text"));
}
@Override
protected void gotoActionPerformed(final AnActionEvent e) {
final Project project = e.getData(PlatformDataKeys.PROJECT);
if (project == null) return;
PsiDocumentManager.getInstance(project).commitAllDocuments();
final DataContext dataContext = e.getDataContext();
final FindManager findManager = FindManager.getInstance(project);
final FindModel findModel = (FindModel) findManager.getFindInFileModel().clone();
final List<AngularItem> validResults = new ArrayList<AngularItem>();
findModel.setRegularExpressions(true);
findModel.setFileFilter("*.js, *.html");
findModel.setStringToFind("\\.(controller|filter|service|factory|module|value|constant|directive|provider)\\(\\s*(\"|')([^(\"|')]+)(\"|')");
findModel.setStringToReplace("$3");
final Collection<Usage> moduleMethodUsages = getAngularUsages(project, dataContext, findModel);
List<AngularItem> moduleMethodResults = getValidResults(project, findModel, moduleMethodUsages, MODULE_METHODS);
validResults.addAll(moduleMethodResults);
findModel.setStringToFind("Ctrl\\(\\s*\\$scope");
findModel.setStringToReplace("$0");
final Collection<Usage> ctrlByConventionUsages = getAngularUsages(project, dataContext, findModel);
List<AngularItem> ctrlByConventionResults = getValidResults(project, findModel, ctrlByConventionUsages, CTRL_CONVENTIONS);
validResults.addAll(ctrlByConventionResults);
findModel.setStringToFind("ng\\-controller\\=\"([^(\"]+)\"");
findModel.setStringToReplace("$1");
final Collection<Usage> ngControllerUsages = getAngularUsages(project, dataContext, findModel);
List<AngularItem> ngControllerResults = getValidResults(project, findModel, ngControllerUsages, NG_CONTROLLER);
validResults.addAll(ngControllerResults);
final GotoAngularModel model = new GotoAngularModel(project, validResults);
showNavigationPopup(e, model, new GotoActionBase.GotoActionCallback<Object>() {
@Override
protected ChooseByNameFilter<Object> createFilter(@NotNull ChooseByNamePopup popup) {
popup.setSearchInAnyPlace(true);
popup.setShowListForEmptyPattern(true);
popup.setMaximumListSizeLimit(255);
return super.createFilter(popup);
}
@Override
public void elementChosen(ChooseByNamePopup popup, final Object element) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
public void run() {
PsiElement psi = ((AngularItem) element).getElement();
NavigationUtil.activateFileWithPsiElement(psi.getNavigationElement());
}
});
}
});
}
private List<AngularItem> getValidResults(final Project project, final FindModel findModel, final Collection<Usage> usages, final int type) {
final List<AngularItem> validResults = new ArrayList<AngularItem>();
//todo: needs code review. There must be a better way to do this
Runnable runnable = new Runnable() {
public void run() {
for (final Usage result : usages) {
final UsageInfo2UsageAdapter usage = (UsageInfo2UsageAdapter) result;
//avoid angular source files. Is there a better way to do this?
if (usage.getFile().getName().startsWith("angular")) continue;
usage.processRangeMarkers(new Processor<Segment>() {
@Override
public boolean process(Segment segment) {
try {
final int textOffset = segment.getStartOffset();
final int textEndOffset = segment.getEndOffset();
Document document = usage.getDocument();
CharSequence charsSequence = document.getCharsSequence();
final CharSequence foundString = charsSequence.subSequence(textOffset, textEndOffset);
String s = foundString.toString();
String regExMatch = FindManager.getInstance(project).getStringToReplace(s, findModel, textOffset, document.getText());
System.out.println(regExMatch);
PsiElement element = PsiUtilBase.getElementAtOffset(((UsageInfo2UsageAdapter) result).getUsageInfo().getFile(), textOffset + 1);
String elementText = element.getText();
System.out.println(elementText + ": " + regExMatch + " - " + s);
//hack to block weird css matches (I have no idea how many edge cases I'll have :/ )
// if(regExMatch.length() > 20) return true;
switch (type) {
case CTRL_CONVENTIONS:
validResults.add(new AngularItem(s, elementText, result, element, "controller"));
break;
case MODULE_METHODS:
validResults.add(new AngularItem(s, regExMatch, result, element, element.getText()));
break;
case NG_CONTROLLER:
validResults.add(new AngularItem(s, regExMatch, result, element, "ng-controller"));
break;
}
return true;
} catch (FindManager.MalformedReplacementStringException e1) {
e1.printStackTrace();
}
return false;
}
});
}
}
};
ApplicationManager.getApplication().runReadAction(runnable);
return validResults;
}
private Collection<Usage> getAngularUsages(Project project, DataContext dataContext, FindModel findModel) {
FindInProjectUtil.setDirectoryName(findModel, dataContext);
CommonProcessors.CollectProcessor<Usage> collectProcessor = new CommonProcessors.CollectProcessor<Usage>();
PsiDirectory directory = PsiManager.getInstance(project).findDirectory(project.getBaseDir());
FindInProjectUtil.findUsages(findModel, directory, project,
true, new AdapterProcessor<UsageInfo, Usage>(collectProcessor, UsageInfo2UsageAdapter.CONVERTER), new FindUsagesProcessPresentation());
return collectProcessor.getResults();
}
}