blob: 48d72083b7d6974825576ee404f59bb62bef6d7c [file] [log] [blame]
/*
* Copyright 2000-2009 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 com.intellij.openapi.vcs.impl;
import com.intellij.codeInsight.CodeSmellInfo;
import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
import com.intellij.codeInsight.daemon.impl.*;
import com.intellij.ide.errorTreeView.NewErrorTreeViewPanel;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vcs.AbstractVcsHelper;
import com.intellij.openapi.vcs.CodeSmellDetector;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.util.ui.MessageCategory;
import com.intellij.vcsUtil.Rethrow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
/**
* @author yole
*/
public class CodeSmellDetectorImpl extends CodeSmellDetector {
private final Project myProject;
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.impl.CodeSmellDetectorImpl");
private Exception myException;
public CodeSmellDetectorImpl(final Project project) {
myProject = project;
}
@Override
public void showCodeSmellErrors(final List<CodeSmellInfo> smellList) {
Collections.sort(smellList, new Comparator<CodeSmellInfo>() {
@Override
public int compare(final CodeSmellInfo o1, final CodeSmellInfo o2) {
return o1.getTextRange().getStartOffset() - o2.getTextRange().getStartOffset();
}
});
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (myProject.isDisposed()) return;
if (smellList.isEmpty()) {
return;
}
final VcsErrorViewPanel errorTreeView = new VcsErrorViewPanel(myProject);
AbstractVcsHelperImpl helper = (AbstractVcsHelperImpl)AbstractVcsHelper.getInstance(myProject);
helper.openMessagesView(errorTreeView, VcsBundle.message("code.smells.error.messages.tab.name"));
FileDocumentManager fileManager = FileDocumentManager.getInstance();
for (CodeSmellInfo smellInfo : smellList) {
final VirtualFile file = fileManager.getFile(smellInfo.getDocument());
final OpenFileDescriptor navigatable =
new OpenFileDescriptor(myProject, file, smellInfo.getStartLine(), smellInfo.getStartColumn());
final String exportPrefix = NewErrorTreeViewPanel.createExportPrefix(smellInfo.getStartLine() + 1);
final String rendererPrefix =
NewErrorTreeViewPanel.createRendererPrefix(smellInfo.getStartLine() + 1, smellInfo.getStartColumn() + 1);
if (smellInfo.getSeverity() == HighlightSeverity.ERROR) {
errorTreeView.addMessage(MessageCategory.ERROR, new String[]{smellInfo.getDescription()}, file.getPresentableUrl(), navigatable,
exportPrefix, rendererPrefix, null);
}
else {//if (smellInfo.getSeverity() == HighlightSeverity.WARNING) {
errorTreeView.addMessage(MessageCategory.WARNING, new String[]{smellInfo.getDescription()}, file.getPresentableUrl(),
navigatable, exportPrefix, rendererPrefix, null);
}
}
}
});
}
@Override
public List<CodeSmellInfo> findCodeSmells(final List<VirtualFile> filesToCheck) throws ProcessCanceledException {
final List<CodeSmellInfo> result = new ArrayList<CodeSmellInfo>();
final PsiManager manager = PsiManager.getInstance(myProject);
final FileDocumentManager fileManager = FileDocumentManager.getInstance();
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
if (ApplicationManager.getApplication().isWriteAccessAllowed()) throw new RuntimeException("Must not run under write action");
boolean completed = ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
@Override
public void run() {
try {
@Nullable final ProgressIndicator progress = ProgressManager.getInstance().getProgressIndicator();
for (int i = 0; i < filesToCheck.size(); i++) {
if (progress != null && progress.isCanceled()) throw new ProcessCanceledException();
VirtualFile file = filesToCheck.get(i);
if (progress != null) {
progress.setText(VcsBundle.message("searching.for.code.smells.processing.file.progress.text", file.getPresentableUrl()));
progress.setFraction((double)i / (double)filesToCheck.size());
}
final PsiFile psiFile = manager.findFile(file);
if (psiFile != null) {
final Document document = fileManager.getDocument(file);
if (document != null) {
final List<CodeSmellInfo> codeSmells = findCodeSmells(psiFile, progress, document);
result.addAll(codeSmells);
}
}
}
}
catch (ProcessCanceledException e) {
throw e;
}
catch (Exception e) {
LOG.error(e);
myException = e;
}
}
}, VcsBundle.message("checking.code.smells.progress.title"), true, myProject);
if (!completed) throw new ProcessCanceledException();
if (myException != null) {
Rethrow.reThrowRuntime(myException);
}
return result;
}
@NotNull
private List<CodeSmellInfo> findCodeSmells(@NotNull PsiFile psiFile, final ProgressIndicator progress, @NotNull Document document) {
final List<CodeSmellInfo> result = new ArrayList<CodeSmellInfo>();
DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(myProject);
List<HighlightInfo> infos = codeAnalyzer.runMainPasses(psiFile, document, progress);
collectErrorsAndWarnings(infos, result, document);
return result;
}
private void collectErrorsAndWarnings(final Collection<HighlightInfo> highlights,
final List<CodeSmellInfo> result,
final Document document) {
if (highlights == null) return;
for (HighlightInfo highlightInfo : highlights) {
final HighlightSeverity severity = highlightInfo.getSeverity();
if (SeverityRegistrar.getSeverityRegistrar(myProject).compare(severity, HighlightSeverity.WARNING) >= 0) {
result.add(new CodeSmellInfo(document, getDescription(highlightInfo),
new TextRange(highlightInfo.startOffset, highlightInfo.endOffset), severity));
}
}
}
private static String getDescription(final HighlightInfo highlightInfo) {
final String description = highlightInfo.getDescription();
final HighlightInfoType type = highlightInfo.type;
if (type instanceof HighlightInfoType.HighlightInfoTypeSeverityByKey) {
final HighlightDisplayKey severityKey = ((HighlightInfoType.HighlightInfoTypeSeverityByKey)type).getSeverityKey();
final String id = severityKey.getID();
return "[" + id + "] " + description;
}
return description;
}
}