From 6fa1c20b46c79081eebfd38851044f04179cb9a4 Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 8 Aug 2011 15:07:33 +0400 Subject: [PATCH] annotate stacktrace - on methods level --- .../debugger/engine/DebugProcessImpl.java | 8 +- .../debugger/ui/DebuggerSessionTab.java | 8 +- .../DefaultConsoleFiltersProvider.java | 8 +- .../filters/ExceptionBaseFilterFactory.java | 31 ++ .../execution/filters/ExceptionFilter.java | 229 +-------------- .../filters/ExceptionFilterFactory.java | 31 ++ .../execution/filters/ExceptionFilters.java | 41 +++ .../VcsContentAnnotationExceptionFilter.java | 278 ++++++++++++++++++ ...ntentAnnotationExceptionFilterFactory.java | 33 +++ .../execution/filters/ExceptionWorker.java | 198 +++++++++++++ .../execution/filters/CompositeFilter.java | 8 +- .../execution/filters/FilterMixin.java | 5 +- .../execution/impl/ConsoleViewImpl.java | 75 +++-- .../impl/EditorHyperlinkSupport.java | 2 + .../src/META-INF/PlatformExtensionPoints.xml | 3 +- .../src/META-INF/PlatformExtensions.xml | 3 + .../localVcs/UpToDateLineNumberProvider.java | 2 + .../openapi/vcs/annotate/FileAnnotation.java | 4 + .../vcs/changes/ChangeListManager.java | 8 + .../VcsContentAnnotation.java | 9 +- .../VcsContentAnnotationImpl.java | 49 ++- .../vcs/actions/AnnotateToggleAction.java | 2 +- .../impl/UpToDateLineNumberProviderImpl.java | 42 ++- .../annotate/CvsFileAnnotation.java | 8 + .../git4idea/annotate/GitFileAnnotation.java | 17 +- .../provider/annotate/HgAnnotation.java | 11 + .../idea/maven/project/MavenConsoleImpl.java | 5 +- .../idea/svn/annotate/SvnFileAnnotation.java | 10 + 28 files changed, 840 insertions(+), 288 deletions(-) create mode 100644 java/execution/openapi/src/com/intellij/execution/filters/ExceptionBaseFilterFactory.java create mode 100644 java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilterFactory.java create mode 100644 java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilters.java create mode 100644 java/java-impl/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationExceptionFilter.java create mode 100644 java/java-impl/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationExceptionFilterFactory.java create mode 100644 java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java index 238a794759f4..1edb4b3d8954 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/DebugProcessImpl.java @@ -50,7 +50,8 @@ import com.intellij.execution.configurations.CommandLineState; import com.intellij.execution.configurations.RemoteConnection; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.configurations.RunProfileState; -import com.intellij.execution.filters.ExceptionFilter; +import com.intellij.execution.filters.ExceptionFilters; +import com.intellij.execution.filters.Filter; import com.intellij.execution.filters.TextConsoleBuilder; import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; @@ -1669,7 +1670,10 @@ public abstract class DebugProcessImpl implements DebugProcess { if (state instanceof CommandLineState) { final TextConsoleBuilder consoleBuilder = ((CommandLineState)state).getConsoleBuilder(); if (consoleBuilder != null) { - consoleBuilder.addFilter(new ExceptionFilter(session.getSearchScope())); + List filters = ExceptionFilters.getFilters(session.getSearchScope()); + for (Filter filter : filters) { + consoleBuilder.addFilter(filter); + } } } myExecutionResult = state.execute(executor, runner); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java index a024d125154f..fba455e7a9db 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java @@ -33,7 +33,8 @@ import com.intellij.debugger.ui.impl.watch.*; import com.intellij.execution.*; import com.intellij.execution.configurations.RunProfile; import com.intellij.execution.executors.DefaultDebugExecutor; -import com.intellij.execution.filters.ExceptionFilter; +import com.intellij.execution.filters.ExceptionFilters; +import com.intellij.execution.filters.Filter; import com.intellij.execution.filters.TextConsoleBuilder; import com.intellij.execution.filters.TextConsoleBuilderFactory; import com.intellij.execution.runners.ExecutionEnvironment; @@ -498,7 +499,10 @@ public class DebuggerSessionTab extends DebuggerSessionTabBase implements Dispos public void addThreadDump(List threads) { final Project project = getProject(); final TextConsoleBuilder consoleBuilder = TextConsoleBuilderFactory.getInstance().createBuilder(project); - consoleBuilder.addFilter(new ExceptionFilter(myDebuggerSession.getSearchScope())); + List filters = ExceptionFilters.getFilters(myDebuggerSession.getSearchScope()); + for (Filter filter : filters) { + consoleBuilder.addFilter(filter); + } final ConsoleView consoleView = consoleBuilder.getConsole(); final DefaultActionGroup toolbarActions = new DefaultActionGroup(); final ThreadDumpPanel panel = new ThreadDumpPanel(project, consoleView, toolbarActions, threads); diff --git a/java/execution/impl/src/com/intellij/execution/filters/DefaultConsoleFiltersProvider.java b/java/execution/impl/src/com/intellij/execution/filters/DefaultConsoleFiltersProvider.java index 1f85d105a237..38041d3fc7b8 100644 --- a/java/execution/impl/src/com/intellij/execution/filters/DefaultConsoleFiltersProvider.java +++ b/java/execution/impl/src/com/intellij/execution/filters/DefaultConsoleFiltersProvider.java @@ -24,12 +24,16 @@ import com.intellij.openapi.project.Project; import com.intellij.psi.search.GlobalSearchScope; import org.jetbrains.annotations.NotNull; +import java.util.List; + public class DefaultConsoleFiltersProvider implements ConsoleFilterProviderEx { public Filter[] getDefaultFilters(@NotNull Project project) { - return new Filter[]{new ExceptionFilter(project), new YourkitFilter(project)}; + return getDefaultFilters(project, GlobalSearchScope.allScope(project)); } public Filter[] getDefaultFilters(@NotNull Project project, @NotNull GlobalSearchScope scope) { - return new Filter[]{new ExceptionFilter(scope), new YourkitFilter(project)}; + List filters = ExceptionFilters.getFilters(scope); + filters.add(new YourkitFilter(project)); + return filters.toArray(new Filter[filters.size()]); } } \ No newline at end of file diff --git a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionBaseFilterFactory.java b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionBaseFilterFactory.java new file mode 100644 index 000000000000..ce12a88468d2 --- /dev/null +++ b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionBaseFilterFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2011 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.execution.filters; + +import com.intellij.psi.search.GlobalSearchScope; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/8/11 + * Time: 12:11 PM + */ +public class ExceptionBaseFilterFactory implements ExceptionFilterFactory { + @Override + public Filter create(GlobalSearchScope searchScope) { + return new ExceptionFilter(searchScope); + } +} diff --git a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java index 5e026810b52a..38a8a0180084 100644 --- a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java +++ b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java @@ -15,239 +15,20 @@ */ package com.intellij.execution.filters; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.colors.CodeInsightColors; -import com.intellij.openapi.editor.colors.EditorColorsManager; -import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.util.Trinity; -import com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotation; -import com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationImpl; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.util.BeforeAfter; -import com.intellij.util.Consumer; -import com.intellij.util.ui.UIUtil; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.awt.*; - -public class ExceptionFilter implements Filter, DumbAware, FilterMixin { - public static final Color CHANGED_BACKGROUND = new Color(188, 237, 201); - private final Project myProject; - @NonNls private static final String AT = "at"; - private static final String AT_PREFIX = AT + " "; - private static final String STANDALONE_AT = " " + AT + " "; - private static final TextAttributes HYPERLINK_ATTRIBUTES = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES); - - private final GlobalSearchScope mySearchScope; - - public ExceptionFilter(@NotNull final Project project) { - myProject = project; - mySearchScope = GlobalSearchScope.allScope(myProject); - } +public class ExceptionFilter implements Filter, DumbAware { + private final GlobalSearchScope myScope; public ExceptionFilter(@NotNull final GlobalSearchScope scope) { - myProject = scope.getProject(); - mySearchScope = scope; - } - - @Nullable - static Trinity parseExceptionLine(final String line) { - int atIndex; - if (line.startsWith(AT_PREFIX)){ - atIndex = 0; - } - else{ - atIndex = line.indexOf(STANDALONE_AT); - if (atIndex < 0) { - atIndex = line.indexOf(AT_PREFIX); - } - if (atIndex < 0) return null; - } - - final int lparenthIndex = line.indexOf('(', atIndex); - if (lparenthIndex < 0) return null; - final int lastDotIndex = line.lastIndexOf('.', lparenthIndex); - if (lastDotIndex < 0 || lastDotIndex < atIndex) return null; - - final int rparenthIndex = line.indexOf(')', lparenthIndex); - if (rparenthIndex < 0) return null; - - // class, method, link - return Trinity.create(adjustedRange(line, atIndex + AT.length() + 1, lastDotIndex), - adjustedRange(line, lastDotIndex + 1, lparenthIndex), new TextRange(lparenthIndex, rparenthIndex)); - } - - private static TextRange adjustedRange(final String line, final int start, final int end) { - String sub = line.substring(start, end); - return new TextRange(start, end - spacesEnd(sub)); - } - - private static int spacesStart(final String s) { - int cnt = 0; - for (int i = 0; i < s.length(); i++) { - final char c = s.charAt(i); - if (! Character.isSpaceChar(c)) return cnt; - ++ cnt; - } - return 0; - } - private static int spacesEnd(final String s) { - int cnt = 0; - for (int i = s.length() - 1; i >= 0; i--) { - final char c = s.charAt(i); - if (! Character.isSpaceChar(c)) return cnt; - ++ cnt; - } - return 0; - } - - // todo do not work internal code - @Override - public void applyHeavyFilter(final String line, final int entireLength, int lineNumber, Consumer consumer) { - final MyWorker worker = new MyWorker(); - ApplicationManager.getApplication().runReadAction(new Runnable() { - @Override - public void run() { - worker.execute(line, entireLength); - } - }); - if (worker.getResult() != null) { - // find method range - final PsiMethod[] methodsByName = worker.getPsiClass().findMethodsByName(worker.getMethod(), false); - // todo also go up etc. now just take first - if (methodsByName.length > 0) { - - } - VcsContentAnnotation.Details details = VcsContentAnnotationImpl.getInstance(myProject) - .annotateLine(worker.getFile().getVirtualFile(), new BeforeAfter(-1, -1), lineNumber); - if (details != null) { - if (details.isFileChanged()) { - final int textStartOffset = entireLength - line.length(); - int idx = line.indexOf(':', worker.getInfo().getThird().getStartOffset()); - int endIdx = idx == -1 ? worker.getInfo().getThird().getEndOffset() : idx; - consumer.consume(new AdditionalHighlight(textStartOffset + worker.getInfo().getThird().getStartOffset() + 1, - textStartOffset + endIdx) { - @Override - public TextAttributes getTextAttributes(@Nullable TextAttributes source) { - if (source == null) { - TextAttributes atts = - EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.CLASS_NAME_ATTRIBUTES).clone(); - atts.setBackgroundColor(CHANGED_BACKGROUND); - return atts; - } - TextAttributes clone = source.clone(); - clone.setBackgroundColor(CHANGED_BACKGROUND); - return clone; - } - }); - } - // todo also other - } - } + myScope = scope; } public Result applyFilter(final String line, final int textEndOffset) { - final MyWorker worker = new MyWorker(); + ExceptionWorker worker = new ExceptionWorker(myScope.getProject(), myScope); worker.execute(line, textEndOffset); return worker.getResult(); } - - private class MyWorker { - private Result myResult; - private PsiClass myClass; - private PsiFile myFile; - private String myMethod; - private Trinity myInfo; - - public void execute(final String line, final int textEndOffset) { - myInfo = parseExceptionLine(line); - if (myInfo == null) { - return; - } - - myMethod = myInfo.getSecond().substring(line); - String className = myInfo.first.substring(line).trim(); - final int dollarIndex = className.indexOf('$'); - if (dollarIndex >= 0){ - className = className.substring(0, dollarIndex); - } - - final int lparenthIndex = myInfo.third.getStartOffset(); - final int rparenthIndex = myInfo.third.getEndOffset(); - final String fileAndLine = line.substring(lparenthIndex + 1, rparenthIndex).trim(); - - final int colonIndex = fileAndLine.lastIndexOf(':'); - if (colonIndex < 0) return; - - final String lineString = fileAndLine.substring(colonIndex + 1); - try{ - final int lineNumber = Integer.parseInt(lineString); - final PsiManager manager = PsiManager.getInstance(myProject); - final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(manager.getProject()); - myClass = psiFacade.findClass(className, mySearchScope); - if (myClass == null) { - myClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject)); - if (myClass == null) {//try to find class according to all dollars in package name - myClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject)); - } - if (myClass == null) return; - } - myFile = (PsiFile) myClass.getContainingFile().getNavigationElement(); - if (myFile == null) return; - - /* - IDEADEV-4976: Some scramblers put something like SourceFile mock instead of real class name. - final String filePath = fileAndLine.substring(0, colonIndex).replace('/', File.separatorChar); - final int slashIndex = filePath.lastIndexOf(File.separatorChar); - final String shortFileName = slashIndex < 0 ? filePath : filePath.substring(slashIndex + 1); - if (!file.getName().equalsIgnoreCase(shortFileName)) return null; - */ - - final int textStartOffset = textEndOffset - line.length(); - - final int highlightStartOffset = textStartOffset + lparenthIndex + 1; - final int highlightEndOffset = textStartOffset + rparenthIndex; - VirtualFile virtualFile = myFile.getVirtualFile(); - final OpenFileHyperlinkInfo linkInfo = new OpenFileHyperlinkInfo(myProject, virtualFile, lineNumber - 1); - TextAttributes attributes = HYPERLINK_ATTRIBUTES.clone(); - if (!ProjectRootManager.getInstance(myProject).getFileIndex().isInContent(virtualFile)) { - Color color = UIUtil.getInactiveTextColor(); - attributes.setForegroundColor(color); - attributes.setEffectColor(color); - } - myResult = new Result(highlightStartOffset, highlightEndOffset, linkInfo, attributes); - } - catch(NumberFormatException e){ - // - } - } - - public Result getResult() { - return myResult; - } - - public PsiClass getPsiClass() { - return myClass; - } - - public String getMethod() { - return myMethod; - } - - public PsiFile getFile() { - return myFile; - } - - public Trinity getInfo() { - return myInfo; - } - } -} \ No newline at end of file +} diff --git a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilterFactory.java b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilterFactory.java new file mode 100644 index 000000000000..fd57dfe7cf89 --- /dev/null +++ b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilterFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2011 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.execution.filters; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.psi.search.GlobalSearchScope; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/5/11 + * Time: 7:46 PM + */ +public interface ExceptionFilterFactory { + ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.exceptionFilter"); + + Filter create(final GlobalSearchScope searchScope); +} diff --git a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilters.java b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilters.java new file mode 100644 index 000000000000..2c79ed095130 --- /dev/null +++ b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilters.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2011 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.execution.filters; + +import com.intellij.psi.search.GlobalSearchScope; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/5/11 + * Time: 7:54 PM + */ +public class ExceptionFilters { + private ExceptionFilters() { + } + + public static List getFilters(final GlobalSearchScope searchScope) { + List filters = new ArrayList(); + ExceptionFilterFactory[] extensions = ExceptionFilterFactory.EP_NAME.getExtensions(); + for (ExceptionFilterFactory extension : extensions) { + filters.add(extension.create(searchScope)); + } + return filters; + } +} diff --git a/java/java-impl/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationExceptionFilter.java b/java/java-impl/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationExceptionFilter.java new file mode 100644 index 000000000000..b3b653dea8c1 --- /dev/null +++ b/java/java-impl/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationExceptionFilter.java @@ -0,0 +1,278 @@ +/* + * Copyright 2000-2011 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.contentAnnotation; + +import com.intellij.execution.filters.ExceptionWorker; +import com.intellij.execution.filters.Filter; +import com.intellij.execution.filters.FilterMixin; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.diff.DiffColors; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.colors.CodeInsightColors; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.localVcs.UpToDateLineNumberProvider; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.Trinity; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.impl.UpToDateLineNumberProviderImpl; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.Consumer; +import com.intellij.util.SmartList; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/5/11 + * Time: 8:39 PM + */ +public class VcsContentAnnotationExceptionFilter implements Filter, FilterMixin { + private final Project myProject; + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationExceptionFilter"); + private final GlobalSearchScope myScope; + + public VcsContentAnnotationExceptionFilter(GlobalSearchScope scope) { + myScope = scope; + myProject = scope.getProject(); + } + + private static class MyAdditionalHighlight extends AdditionalHighlight { + private MyAdditionalHighlight(int start, int end) { + super(start, end); + } + + @Override + public TextAttributes getTextAttributes(@Nullable TextAttributes source) { + EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme(); + final TextAttributes changedColor = globalScheme.getAttributes(DiffColors.DIFF_MODIFIED); + if (source == null) { + TextAttributes atts = + globalScheme.getAttributes(CodeInsightColors.CLASS_NAME_ATTRIBUTES).clone(); + atts.setBackgroundColor(changedColor.getBackgroundColor()); + return atts; + } + TextAttributes clone = source.clone(); + clone.setBackgroundColor(changedColor.getBackgroundColor()); + return clone; + } + } + + @Override + public void applyHeavyFilter(final Document copiedFragment, + int startOffset, + int startLineNumber, + Consumer consumer) { + VcsContentAnnotation vcsContentAnnotation = VcsContentAnnotationImpl.getInstance(myProject); + final LocalChangesCorrector localChangesCorrector = new LocalChangesCorrector(myProject); + Trinity previousLineResult = null; + + for (int i = 0; i < copiedFragment.getLineCount(); i++) { + final int lineStartOffset = copiedFragment.getLineStartOffset(i); + final int lineEndOffset = copiedFragment.getLineEndOffset(i); + final ExceptionWorker worker = new ExceptionWorker(myProject, myScope); + final String[] lineText = new String[1]; + ApplicationManager.getApplication().runReadAction(new Runnable() { + @Override + public void run() { + lineText[0] = copiedFragment.getText(new TextRange(lineStartOffset, lineEndOffset)); + worker.execute(lineText[0], lineEndOffset); + } + }); + if (worker.getResult() != null) { + VirtualFile vf = worker.getFile().getVirtualFile(); + if (localChangesCorrector.isFileAlreadyIdentifiedAsChanged(vf) || ChangeListManager.isFileChanged(myProject, vf) || + vcsContentAnnotation.fileRecentlyChanged(vf)) { + final Document document = getDocumentForFile(worker); + if (document == null) return; + + int startFileOffset = worker.getInfo().getThird().getStartOffset(); + int idx = lineText[0].indexOf(':', startFileOffset); + int endIdx = idx == -1 ? worker.getInfo().getThird().getEndOffset() : idx; + consumer.consume(new MyAdditionalHighlight(startOffset + lineStartOffset + startFileOffset + 1, startOffset + lineStartOffset + endIdx)); + + // also check method + final List ranges = findMethodRange(worker, document, previousLineResult); + if (ranges != null) { + boolean methodChanged = false; + for (TextRange range : ranges) { + if (localChangesCorrector.isRangeChangedLocally(vf, document, range)) { + methodChanged = true; + break; + } + final TextRange correctedRange = localChangesCorrector.getCorrectedRange(vf, document, range); + if (vcsContentAnnotation.intervalRecentlyChanged(vf, correctedRange)) { + methodChanged = true; + break; + } + } + if (methodChanged) { + consumer.consume(new MyAdditionalHighlight(startOffset + lineStartOffset + worker.getInfo().getSecond().getStartOffset(), + startOffset + lineStartOffset + worker.getInfo().getSecond().getEndOffset())); + } + } + } + } + previousLineResult = worker.getResult() == null ? null : + new Trinity(worker.getPsiClass(), worker.getFile(), worker.getMethod()); + } + } + + private static class LocalChangesCorrector { + private final Map myRecentlyChanged; + private final Project myProject; + + private LocalChangesCorrector(final Project project) { + myProject = project; + myRecentlyChanged = new HashMap(); + } + + public boolean isFileAlreadyIdentifiedAsChanged(final VirtualFile vf) { + return myRecentlyChanged.containsKey(vf); + } + + public boolean isRangeChangedLocally(final VirtualFile vf, final Document document, final TextRange range) { + final UpToDateLineNumberProvider provider = getProvider(vf, document); + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Boolean compute() { + return provider.isRangeChanged(range.getStartOffset(), range.getEndOffset()); + } + }); + } + + public TextRange getCorrectedRange(final VirtualFile vf, final Document document, final TextRange range) { + final UpToDateLineNumberProvider provider = getProvider(vf, document); + if (provider == null) return range; + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public TextRange compute() { + return new TextRange(provider.getLineNumber(range.getStartOffset()), provider.getLineNumber(range.getEndOffset())); + } + }); + } + + private UpToDateLineNumberProvider getProvider(VirtualFile vf, Document document) { + UpToDateLineNumberProvider provider = myRecentlyChanged.get(vf); + if (provider == null) { + provider = new UpToDateLineNumberProviderImpl(document, myProject); + myRecentlyChanged.put(vf, provider); + } + return provider; + } + } + + private Document getDocumentForFile(final ExceptionWorker worker) { + return ApplicationManager.getApplication().runReadAction(new Computable() { + @Override + public Document compute() { + final Document document = FileDocumentManager.getInstance().getDocument(worker.getFile().getVirtualFile()); + if (document == null) { + LOG.info("can not get document for file: " + worker.getFile().getVirtualFile()); + return null; + } + return document; + } + }); + } + + /* final UpToDateLineNumberProvider getUpToDateLineNumber = new UpToDateLineNumberProviderImpl(editor.getDocument(), project, upToDateContent); + /**/ + + // line numbers + private List findMethodRange(final ExceptionWorker worker, final Document document, final Trinity previousLineResult) { + return ApplicationManager.getApplication().runReadAction(new Computable>() { + @Override + public List compute() { + List ranges = getTextRangeForMethod(worker, previousLineResult); + if (ranges == null) return null; + final List result = new ArrayList(); + for (TextRange range : ranges) { + result.add(new TextRange(document.getLineNumber(range.getStartOffset()), + document.getLineNumber(range.getEndOffset()))); + } + return result; + } + }); + } + + // null - check all + @Nullable + private List selectMethod(final PsiMethod[] methods, final Trinity previousLineResult) { + if (previousLineResult == null || previousLineResult.getThird() == null) return null; + + final List result = new SmartList(); + for (final PsiMethod method : methods) { + method.accept(new JavaRecursiveElementWalkingVisitor() { + @Override + public void visitCallExpression(PsiCallExpression callExpression) { + final PsiMethod resolved = callExpression.resolveMethod(); + if (resolved != null) { + if (resolved.getName().equals(previousLineResult.getThird())) { + result.add(method); + } + } + } + }); + } + + return result; + } + + private List getTextRangeForMethod(final ExceptionWorker worker, Trinity previousLineResult) { + String method = worker.getMethod(); + PsiClass psiClass = worker.getPsiClass(); + PsiMethod[] methods; + if (method.contains("")) { + // constructor + methods = psiClass.getConstructors(); + } else if (method.contains("$")) { + // access$100 + return null; + } else { + methods = psiClass.findMethodsByName(method, false); + } + if (methods.length > 0) { + if (methods.length == 1) { + final TextRange range = methods[0].getTextRange(); + return Collections.singletonList(range); + } else { + List selectedMethods = selectMethod(methods, previousLineResult); + final List toIterate = selectedMethods == null ? Arrays.asList(methods) : selectedMethods; + final List result = new ArrayList(); + for (PsiMethod psiMethod : toIterate) { + result.add(psiMethod.getTextRange()); + } + return result; + } + } + return null; + } + + @Override + public Result applyFilter(String line, int entireLength) { + return null; + } +} diff --git a/java/java-impl/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationExceptionFilterFactory.java b/java/java-impl/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationExceptionFilterFactory.java new file mode 100644 index 000000000000..fc000c8b57f3 --- /dev/null +++ b/java/java-impl/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationExceptionFilterFactory.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2011 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.contentAnnotation; + +import com.intellij.execution.filters.ExceptionFilterFactory; +import com.intellij.execution.filters.Filter; +import com.intellij.psi.search.GlobalSearchScope; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/5/11 + * Time: 8:03 PM + */ +public class VcsContentAnnotationExceptionFilterFactory implements ExceptionFilterFactory { + @Override + public Filter create(GlobalSearchScope searchScope) { + return new VcsContentAnnotationExceptionFilter(searchScope); + } +} diff --git a/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java b/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java new file mode 100644 index 000000000000..e3a57cd722bc --- /dev/null +++ b/java/openapi/src/com/intellij/execution/filters/ExceptionWorker.java @@ -0,0 +1,198 @@ +/* + * Copyright 2000-2011 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.execution.filters; + +import com.intellij.openapi.editor.colors.CodeInsightColors; +import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.markup.TextAttributes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.Trinity; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; + +import java.awt.*; + +/** +* Created by IntelliJ IDEA. +* User: Irina.Chernushina +* Date: 8/5/11 +* Time: 8:36 PM +* To change this template use File | Settings | File Templates. +*/ +public class ExceptionWorker { + @NonNls private static final String AT = "at"; + private static final String AT_PREFIX = AT + " "; + private static final String STANDALONE_AT = " " + AT + " "; + + private static final TextAttributes HYPERLINK_ATTRIBUTES = EditorColorsManager + .getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES); + + private final Project myProject; + private final GlobalSearchScope mySearchScope; + private Filter.Result myResult; + private PsiClass myClass; + private PsiFile myFile; + private String myMethod; + private Trinity myInfo; + + public ExceptionWorker(Project project, final GlobalSearchScope searchScope) { + myProject = project; + mySearchScope = searchScope; + } + + public void execute(final String line, final int textEndOffset) { + myInfo = parseExceptionLine(line); + if (myInfo == null) { + return; + } + + myMethod = myInfo.getSecond().substring(line); + String className = myInfo.first.substring(line).trim(); + final int dollarIndex = className.indexOf('$'); + if (dollarIndex >= 0){ + className = className.substring(0, dollarIndex); + } + + final int lparenthIndex = myInfo.third.getStartOffset(); + final int rparenthIndex = myInfo.third.getEndOffset(); + final String fileAndLine = line.substring(lparenthIndex + 1, rparenthIndex).trim(); + + final int colonIndex = fileAndLine.lastIndexOf(':'); + if (colonIndex < 0) return; + + final String lineString = fileAndLine.substring(colonIndex + 1); + try{ + final int lineNumber = Integer.parseInt(lineString); + final PsiManager manager = PsiManager.getInstance(myProject); + final JavaPsiFacade psiFacade = JavaPsiFacade.getInstance(manager.getProject()); + myClass = psiFacade.findClass(className, mySearchScope); + if (myClass == null) { + myClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject)); + if (myClass == null) {//try to find class according to all dollars in package name + myClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject)); + } + if (myClass == null) return; + } + myFile = (PsiFile) myClass.getContainingFile().getNavigationElement(); + if (myFile == null) return; + + /* + IDEADEV-4976: Some scramblers put something like SourceFile mock instead of real class name. + final String filePath = fileAndLine.substring(0, colonIndex).replace('/', File.separatorChar); + final int slashIndex = filePath.lastIndexOf(File.separatorChar); + final String shortFileName = slashIndex < 0 ? filePath : filePath.substring(slashIndex + 1); + if (!file.getName().equalsIgnoreCase(shortFileName)) return null; + */ + + final int textStartOffset = textEndOffset - line.length(); + + final int highlightStartOffset = textStartOffset + lparenthIndex + 1; + final int highlightEndOffset = textStartOffset + rparenthIndex; + VirtualFile virtualFile = myFile.getVirtualFile(); + final OpenFileHyperlinkInfo linkInfo = new OpenFileHyperlinkInfo(myProject, virtualFile, lineNumber - 1); + TextAttributes attributes = HYPERLINK_ATTRIBUTES.clone(); + if (!ProjectRootManager.getInstance(myProject).getFileIndex().isInContent(virtualFile)) { + Color color = UIUtil.getInactiveTextColor(); + attributes.setForegroundColor(color); + attributes.setEffectColor(color); + } + myResult = new Filter.Result(highlightStartOffset, highlightEndOffset, linkInfo, attributes); + } + catch(NumberFormatException e){ + // + } + } + + public Filter.Result getResult() { + return myResult; + } + + public PsiClass getPsiClass() { + return myClass; + } + + public String getMethod() { + return myMethod; + } + + public PsiFile getFile() { + return myFile; + } + + public Trinity getInfo() { + return myInfo; + } + + @Nullable + static Trinity parseExceptionLine(final String line) { + int atIndex; + if (line.startsWith(AT_PREFIX)){ + atIndex = 0; + } + else{ + atIndex = line.indexOf(STANDALONE_AT); + if (atIndex < 0) { + atIndex = line.indexOf(AT_PREFIX); + } + if (atIndex < 0) return null; + } + + final int lparenthIndex = line.indexOf('(', atIndex); + if (lparenthIndex < 0) return null; + final int lastDotIndex = line.lastIndexOf('.', lparenthIndex); + if (lastDotIndex < 0 || lastDotIndex < atIndex) return null; + + final int rparenthIndex = line.indexOf(')', lparenthIndex); + if (rparenthIndex < 0) return null; + + // class, method, link + return Trinity.create(adjustedRange(line, atIndex + AT.length() + 1, lastDotIndex), + adjustedRange(line, lastDotIndex + 1, lparenthIndex), new TextRange(lparenthIndex, rparenthIndex)); + } + + private static TextRange adjustedRange(final String line, final int start, final int end) { + String sub = line.substring(start, end); + return new TextRange(start, end - spacesEnd(sub)); + } + + private static int spacesStart(final String s) { + int cnt = 0; + for (int i = 0; i < s.length(); i++) { + final char c = s.charAt(i); + if (! Character.isSpaceChar(c)) return cnt; + ++ cnt; + } + return 0; + } + private static int spacesEnd(final String s) { + int cnt = 0; + for (int i = s.length() - 1; i >= 0; i--) { + final char c = s.charAt(i); + if (! Character.isSpaceChar(c)) return cnt; + ++ cnt; + } + return 0; + } +} diff --git a/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java b/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java index f663a93d2199..01ca873f687a 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java +++ b/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java @@ -15,6 +15,7 @@ */ package com.intellij.execution.filters; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; import com.intellij.util.Consumer; @@ -51,7 +52,10 @@ public class CompositeFilter implements Filter, FilterMixin { } @Override - public void applyHeavyFilter(String line, int entireLength, int lineNumber, Consumer consumer) { + public void applyHeavyFilter(Document copiedFragment, + int startOffset, + int startLineNumber, + Consumer consumer) { final boolean dumb = myDumbService.isDumb(); List filters = myFilters; int count = filters.size(); @@ -60,7 +64,7 @@ public class CompositeFilter implements Filter, FilterMixin { Filter filter = filters.get(i); if (! (filter instanceof FilterMixin)) continue; if (!dumb || DumbService.isDumbAware(filter)) { - ((FilterMixin) filter).applyHeavyFilter(line, entireLength, lineNumber, consumer); + ((FilterMixin) filter).applyHeavyFilter(copiedFragment, startOffset, startLineNumber, consumer); } } } diff --git a/platform/lang-api/src/com/intellij/execution/filters/FilterMixin.java b/platform/lang-api/src/com/intellij/execution/filters/FilterMixin.java index 175817c59a4f..c4b4f61b5e5d 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/FilterMixin.java +++ b/platform/lang-api/src/com/intellij/execution/filters/FilterMixin.java @@ -15,6 +15,7 @@ */ package com.intellij.execution.filters; +import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.util.Consumer; import org.jetbrains.annotations.Nullable; @@ -27,8 +28,8 @@ import org.jetbrains.annotations.Nullable; */ public interface FilterMixin { @Nullable - void applyHeavyFilter(String line, int entireLength, int lineNumber, Consumer consumer); - + void applyHeavyFilter(Document copiedFragment, int startOffset, int startLineNumber, Consumer consumer); + abstract class AdditionalHighlight { private final int myStart; private final int myEnd; diff --git a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java index f255923f0cdc..6ee68e74a2a5 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -45,6 +45,7 @@ import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.HighlighterClient; import com.intellij.openapi.editor.highlighter.HighlighterIterator; +import com.intellij.openapi.editor.impl.DocumentImpl; import com.intellij.openapi.editor.impl.softwrap.SoftWrapAppliancePlaces; import com.intellij.openapi.editor.markup.HighlighterLayer; import com.intellij.openapi.editor.markup.HighlighterTargetArea; @@ -60,10 +61,7 @@ import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.MyLayeredPane; -import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.text.LineTokenizer; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; @@ -405,7 +403,8 @@ public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, Data /*JPanel wrapper = new JPanel(new BorderLayout()); wrapper.add(myAsyncProcessIcon, BorderLayout.NORTH); wrapper.setOpaque(false);*/ - myJLayeredPane.add(myAsyncProcessIcon, BorderLayout.NORTH, JLayeredPane.DRAG_LAYER); + + //myJLayeredPane.add(myAsyncProcessIcon, BorderLayout.NORTH, JLayeredPane.DRAG_LAYER); myEditor.getDocument().addDocumentListener(new DocumentAdapter() { public void documentChanged(DocumentEvent e) { @@ -816,45 +815,41 @@ public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, Data if (canHighlightHyperlinks) { myHyperlinks.highlightHyperlinks(myCustomFilter, myPredefinedMessageFilter, line1, endLine); } + if (myPredefinedMessageFilter.isAnyHeavy()) { - final Document document = getEditor().getDocument(); final int startLine = Math.max(0, line1); - for (int line = startLine; line <= endLine; line++) { - int endOffset = document.getLineEndOffset(line); - if (endOffset < document.getTextLength()) { - endOffset++; // add '\n' - } - final String lineText = EditorHyperlinkSupport.getLineText(document, line, true); - assert myHeavyAlarm != null; - final int finalEndOffset = endOffset; - final int finalLine = line; - myAsyncProcessIcon.resume(); - myHeavyAlarm.addRequest(new Runnable() { - @Override - public void run() { - myPredefinedMessageFilter.applyHeavyFilter(lineText, finalEndOffset, finalLine, new Consumer() { - @Override - public void consume(final FilterMixin.AdditionalHighlight additionalHighlight) { - SwingUtilities.invokeLater( - new Runnable() { - @Override - public void run() { - myFlushAlarm.addRequest(new Runnable() { - @Override - public void run() { - myHyperlinks.adjustHighlighters(Collections.singletonList(additionalHighlight)); - } - }, 0); - } - }); - } - }); - if (myHeavyAlarm.getActiveRequestCount() == 0) { - myAsyncProcessIcon.suspend(); + + final Document document = getEditor().getDocument(); + final Document documentCopy = new DocumentImpl(true); + final int startOffset = document.getLineStartOffset(startLine); + documentCopy.setText(new String(document.getText(new TextRange(startOffset, document.getLineEndOffset(endLine))))); + documentCopy.setReadOnly(true); + + myHeavyAlarm.addRequest(new Runnable() { + @Override + public void run() { + myPredefinedMessageFilter.applyHeavyFilter(documentCopy, startOffset, startLine, new Consumer() { + @Override + public void consume(final FilterMixin.AdditionalHighlight additionalHighlight) { + SwingUtilities.invokeLater( + new Runnable() { + @Override + public void run() { + myFlushAlarm.addRequest(new Runnable() { + @Override + public void run() { + myHyperlinks.adjustHighlighters(Collections.singletonList(additionalHighlight)); + } + }, 0); + } + }); } + }); + if (myHeavyAlarm.getActiveRequestCount() == 0) { + myAsyncProcessIcon.suspend(); } - }, 0); - } + } + }, 0); } if (myUpdateFoldingsEnabled) { updateFoldings(line1, endLine, true); diff --git a/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java b/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java index 68fb46a6368f..1b4645a66f3b 100644 --- a/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java +++ b/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java @@ -308,6 +308,7 @@ public class EditorHyperlinkSupport { }, i, ranges.size()); } + // todo fix link followed here! private static void linkFollowed(Editor editor, Collection ranges, final RangeHighlighter link) { MarkupModelEx markupModel = (MarkupModelEx)editor.getMarkupModel(); for (RangeHighlighter range : ranges) { @@ -325,6 +326,7 @@ public class EditorHyperlinkSupport { attributes.setEffectType(oldAttributes.getEffectType()); attributes.setEffectColor(oldAttributes.getEffectColor()); attributes.setForegroundColor(oldAttributes.getForegroundColor()); + attributes.setBackgroundColor(oldAttributes.getBackgroundColor()); markupModel.setRangeHighlighterAttributes(range, attributes); } } diff --git a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml index 5b7c408c25be..b44e2be72b46 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensionPoints.xml @@ -119,5 +119,6 @@ - + + diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index 26aeee11a962..46314fd97894 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -226,4 +226,7 @@ + + + diff --git a/platform/vcs-api/src/com/intellij/openapi/localVcs/UpToDateLineNumberProvider.java b/platform/vcs-api/src/com/intellij/openapi/localVcs/UpToDateLineNumberProvider.java index a927e1ed9acc..bacb8ba9e42e 100644 --- a/platform/vcs-api/src/com/intellij/openapi/localVcs/UpToDateLineNumberProvider.java +++ b/platform/vcs-api/src/com/intellij/openapi/localVcs/UpToDateLineNumberProvider.java @@ -19,4 +19,6 @@ public interface UpToDateLineNumberProvider { int ABSENT_LINE_NUMBER = -1; int getLineNumber(int currentNumber); + boolean isLineChanged(int currentNumber); + boolean isRangeChanged(final int start, final int end); } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/FileAnnotation.java b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/FileAnnotation.java index 43c890723391..e6acc734770e 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/FileAnnotation.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/annotate/FileAnnotation.java @@ -19,6 +19,7 @@ import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import org.jetbrains.annotations.Nullable; +import java.util.Date; import java.util.List; /** @@ -81,6 +82,9 @@ public interface FileAnnotation { @Nullable VcsRevisionNumber getLineRevisionNumber(int lineNumber); + @Nullable + Date getLineDate(int lineNumber); + /** * Get revision number for the line. */ diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListManager.java b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListManager.java index c19ae587f2e5..8147780363e9 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListManager.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/changes/ChangeListManager.java @@ -150,4 +150,12 @@ public abstract class ChangeListManager implements ChangeListModification { public abstract void letGo(); public abstract String isFreezed(); public abstract boolean isFreezedWithNotification(@Nullable String modalTitle); + + public static boolean isFileChanged(final Project project, final VirtualFile vf) { + FileStatus status = getInstance(project).getStatus(vf); + if (status == null || FileStatus.NOT_CHANGED.equals(status) || FileStatus.UNKNOWN.equals(status) || FileStatus.IGNORED.equals(status)) { + return false; + } + return true; + } } diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotation.java b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotation.java index 500c3f93cad3..b39340ba32da 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotation.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotation.java @@ -15,9 +15,10 @@ */ package com.intellij.openapi.vcs.contentAnnotation; +import com.intellij.openapi.util.Getter; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vcs.RichTextItem; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.BeforeAfter; import org.jetbrains.annotations.Nullable; import java.util.List; @@ -29,8 +30,12 @@ import java.util.List; * Time: 12:50 PM */ public interface VcsContentAnnotation { + boolean fileRecentlyChanged(final VirtualFile vf); + + boolean intervalRecentlyChanged(VirtualFile file, final TextRange lineInterval); + @Nullable - Details annotateLine(final VirtualFile vf, final BeforeAfter enclosingRange, final int lineNumber); + Details annotateLine(final VirtualFile vf, final Getter enclosingRange, final int lineNumber); class Details { private final boolean myLineChanged; diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationImpl.java b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationImpl.java index f52078c65284..f8394311bc54 100644 --- a/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationImpl.java +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationImpl.java @@ -16,13 +16,17 @@ package com.intellij.openapi.vcs.contentAnnotation; import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Getter; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vcs.AbstractVcs; import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.diff.DiffMixin; import com.intellij.openapi.vcs.history.VcsRevisionDescription; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.BeforeAfter; import org.jetbrains.annotations.Nullable; import java.util.Date; @@ -36,6 +40,7 @@ import java.util.Date; public class VcsContentAnnotationImpl implements VcsContentAnnotation { private final Project myProject; private final VcsContentAnnotationSettings mySettings; + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationImpl"); public static VcsContentAnnotation getInstance(final Project project) { return ServiceManager.getService(project, VcsContentAnnotation.class); @@ -46,9 +51,46 @@ public class VcsContentAnnotationImpl implements VcsContentAnnotation { mySettings = settings; } + @Override + public boolean fileRecentlyChanged(VirtualFile vf) { + final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject); + final AbstractVcs vcs = vcsManager.getVcsFor(vf); + if (vcs == null) return false; + if (vcs.getDiffProvider() instanceof DiffMixin) { + final VcsRevisionDescription description = ((DiffMixin)vcs.getDiffProvider()).getCurrentRevisionDescription(vf); + final Date date = description.getRevisionDate(); + return isRecent(date); + } + return false; + } + + private boolean isRecent(Date date) { + return date.getTime() > (System.currentTimeMillis() - mySettings.getLimit()); + } + + @Override + public boolean intervalRecentlyChanged(VirtualFile file, TextRange lineInterval) { + final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject); + final AbstractVcs vcs = vcsManager.getVcsFor(file); + if (vcs == null) return false; + final FileAnnotation fileAnnotation; + try { + fileAnnotation = vcs.getCachingAnnotationProvider().annotate(file); + } + catch (VcsException e) { + LOG.info(e); + return false; + } + for (int i = lineInterval.getStartOffset(); i <= lineInterval.getEndOffset(); i++) { + Date lineDate = fileAnnotation.getLineDate(i); + if (lineDate != null && isRecent(lineDate)) return true; + } + return false; + } + @Nullable @Override - public Details annotateLine(final VirtualFile vf, final BeforeAfter enclosingRange, final int lineNumber) { + public Details annotateLine(final VirtualFile vf, final Getter enclosingRange, final int lineNumber) { final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject); final AbstractVcs vcs = vcsManager.getVcsFor(vf); if (vcs == null) return null; @@ -56,8 +98,9 @@ public class VcsContentAnnotationImpl implements VcsContentAnnotation { boolean fileRecent = false; final VcsRevisionDescription description = ((DiffMixin)vcs.getDiffProvider()).getCurrentRevisionDescription(vf); final Date date = description.getRevisionDate(); - if (date.getTime() > (System.currentTimeMillis() - mySettings.getLimit())) { + if (isRecent(date)) { fileRecent = true; + enclosingRange.get(); } return new Details(false, false, fileRecent, null); } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java index abb32edb3356..7f609421585a 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java @@ -188,7 +188,7 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware, Ann final AbstractVcs vcs) { final String upToDateContent = fileAnnotation.getAnnotatedContent(); - final UpToDateLineNumberProvider getUpToDateLineNumber = new UpToDateLineNumberProviderImpl(editor.getDocument(), project, upToDateContent); + final UpToDateLineNumberProvider getUpToDateLineNumber = new UpToDateLineNumberProviderImpl(editor.getDocument(), project); editor.getGutter().closeAllAnnotations(); // be careful, not proxies but original items are put there (since only their presence not behaviour is important) diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java index 857437149d69..795652f0b773 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/impl/UpToDateLineNumberProviderImpl.java @@ -29,21 +29,55 @@ import java.util.List; public class UpToDateLineNumberProviderImpl implements UpToDateLineNumberProvider { private final Document myDocument; private final Project myProject; - private final String myUpToDateContent; + private final LineStatusTrackerManagerI myLineStatusTrackerManagerI; - public UpToDateLineNumberProviderImpl(Document document, Project project, String upToDateContent) { + public UpToDateLineNumberProviderImpl(Document document, Project project) { myDocument = document; myProject = project; - myUpToDateContent = upToDateContent; + myLineStatusTrackerManagerI = LineStatusTrackerManager.getInstance(myProject); } public int getLineNumber(int currentNumber) { - LineStatusTracker tracker = LineStatusTrackerManager.getInstance(myProject).getLineStatusTracker(myDocument); + LineStatusTracker tracker = myLineStatusTrackerManagerI.getLineStatusTracker(myDocument); if (tracker == null) { return currentNumber; } return calcLineNumber(tracker, currentNumber); } + + public boolean isRangeChanged(final int start, final int end) { + LineStatusTracker tracker = LineStatusTrackerManager.getInstance(myProject).getLineStatusTracker(myDocument); + if (tracker == null) { + return false; + } + for (Range range : tracker.getRanges()) { + if (lineInRange(range, start) || lineInRange(range, end)) { + return true; + } + if (range.getOffset1() > start) { + return range.getOffset1() < end; + } + } + return false; + } + + private static boolean lineInRange(final Range range, final int currentNumber) { + return range.getOffset1() <= currentNumber && range.getOffset2() >= currentNumber; + } + + @Override + public boolean isLineChanged(int currentNumber) { + LineStatusTracker tracker = LineStatusTrackerManager.getInstance(myProject).getLineStatusTracker(myDocument); + if (tracker == null) { + return false; + } + for (Range range : tracker.getRanges()) { + if (range.getOffset1() <= currentNumber && range.getOffset2() >= currentNumber) { + return true; + } + } + return false; + } private boolean endsWithSeparator(final CharSequence string) { if ((string == null) || (string.length() == 0)) { diff --git a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java index 4a699b857442..aa6d449df696 100644 --- a/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java +++ b/plugins/cvs/cvs-plugin/src/com/intellij/cvsSupport2/annotate/CvsFileAnnotation.java @@ -155,6 +155,14 @@ public class CvsFileAnnotation implements FileAnnotation{ return null; } + @Override + public Date getLineDate(int lineNumber) { + if (lineNumber < 0 || lineNumber >= myAnnotations.length) { + return null; + } + return myAnnotations[lineNumber].getDate(); + } + public VcsRevisionNumber originalRevision(int lineNumber) { return getLineRevisionNumber(lineNumber); } diff --git a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java index b5335be80f33..da98e8703b24 100644 --- a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java +++ b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java @@ -237,13 +237,26 @@ public class GitFileAnnotation implements FileAnnotation { * {@inheritDoc} */ public VcsRevisionNumber getLineRevisionNumber(final int lineNumber) { - if (myLines.size() <= lineNumber || lineNumber < 0 || myLines.get(lineNumber) == null) { + if (lineNumberCheck(lineNumber)) { return null; } final LineInfo lineInfo = myLines.get(lineNumber); return lineInfo == null ? null : lineInfo.getRevision(); } + private boolean lineNumberCheck(int lineNumber) { + return myLines.size() <= lineNumber || lineNumber < 0 || myLines.get(lineNumber) == null; + } + + @Override + public Date getLineDate(int lineNumber) { + if (lineNumberCheck(lineNumber)) { + return null; + } + final LineInfo lineInfo = myLines.get(lineNumber); + return lineInfo == null ? null : lineInfo.getDate(); + } + /** * Get revision number for the line. */ @@ -287,7 +300,7 @@ public class GitFileAnnotation implements FileAnnotation { } public String getValue(int lineNumber) { - if (myLines.size() <= lineNumber || lineNumber < 0 || myLines.get(lineNumber) == null) { + if (lineNumberCheck(lineNumber)) { return ""; } else { diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java index 02addf7afa86..0d6075ecfe45 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java @@ -22,6 +22,7 @@ import org.apache.commons.lang.StringUtils; import org.zmlx.hg4idea.HgFile; import org.zmlx.hg4idea.HgFileRevision; +import java.util.Date; import java.util.LinkedList; import java.util.List; @@ -99,6 +100,16 @@ public class HgAnnotation implements FileAnnotation { return annotationLine.getVcsRevisionNumber(); } + @Override + public Date getLineDate(int lineNumber) { + if (lineNumber >= lines.size() || lineNumber < 0) { + return null; + } + //lines.get(lineNumber).get(HgAnnotation.FIELD.DATE) + // todo : parse date + return null; + } + public List getRevisions() { List result = new LinkedList(); result.addAll(vcsFileRevisions); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenConsoleImpl.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenConsoleImpl.java index ef641814e4d7..5a7c50d9bd92 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenConsoleImpl.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/project/MavenConsoleImpl.java @@ -27,6 +27,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.psi.search.GlobalSearchScope; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; import com.intellij.ui.content.MessageView; @@ -34,6 +35,7 @@ import org.jetbrains.idea.maven.execution.MavenRunnerParameters; import org.jetbrains.idea.maven.execution.MavenRunnerSettings; import org.jetbrains.idea.maven.utils.MavenUtil; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; public class MavenConsoleImpl extends MavenConsole { @@ -73,10 +75,11 @@ public class MavenConsoleImpl extends MavenConsole { public static TextConsoleBuilder createConsoleBuilder(Project project) { TextConsoleBuilder builder = TextConsoleBuilderFactory.getInstance().createBuilder(project); - Filter[] filters = {new ExceptionFilter(project), new RegexpFilter(project, CONSOLE_FILTER_REGEXP)}; + List filters = ExceptionFilters.getFilters(GlobalSearchScope.allScope(project)); for (Filter filter : filters) { builder.addFilter(filter); } + builder.addFilter(new RegexpFilter(project, CONSOLE_FILTER_REGEXP)); return builder; } diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java index d2f89e47af21..4e211f432c79 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/SvnFileAnnotation.java @@ -246,6 +246,16 @@ public class SvnFileAnnotation implements FileAnnotation { return null; } + @Override + public Date getLineDate(int lineNumber) { + if (myInfos.size() <= lineNumber || lineNumber < 0) { + return null; + } + final LineInfo info = myInfos.get(lineNumber); + if (info == null) return null; + return info.getDate(); + } + public List getRevisions() { final List result = new ArrayList(myRevisionMap.values()); Collections.sort(result, new Comparator() {