From 51e59817eedaa29ed20c3a0aaea06d398614ca65 Mon Sep 17 00:00:00 2001 From: irengrig Date: Thu, 4 Aug 2011 16:20:15 +0400 Subject: [PATCH 1/5] annotate stacktraces. for file-level now --- .../execution/filters/ExceptionFilter.java | 246 +++++++++++++----- .../intellij/unscramble/ThreadDumpPanel.java | 3 +- .../execution/filters/CompositeFilter.java | 26 +- .../intellij/execution/filters/Filter.java | 1 + .../execution/filters/FilterMixin.java | 51 ++++ .../execution/impl/ConsoleViewImpl.java | 84 +++++- .../unscramble/ShowRecentlyChanged.java | 61 +++++ .../intellij/openapi/ui/LoadingDecorator.java | 12 - .../intellij/openapi/ui/MyLayeredPane.java | 41 +++ .../intellij/ui/SimpleColoredComponent.java | 3 +- .../impl/EditorHyperlinkSupport.java | 92 ++++++- .../src/componentSets/VCS.xml | 5 + .../intellij/openapi/vcs/RichTextItem.java | 43 +++ .../VcsContentAnnotation.java | 62 +++++ .../VcsContentAnnotationImpl.java | 66 +++++ .../VcsContentAnnotationSettings.java | 81 ++++++ .../VcsContentAnnotationConfigurable.java | 106 ++++++++ .../VcsDirectoryConfigurationPanel.java | 17 +- plugins/git4idea/src/META-INF/plugin.xml | 1 - 19 files changed, 900 insertions(+), 101 deletions(-) create mode 100644 platform/lang-api/src/com/intellij/execution/filters/FilterMixin.java create mode 100644 platform/lang-impl/src/com/intellij/unscramble/ShowRecentlyChanged.java create mode 100644 platform/platform-api/src/com/intellij/openapi/ui/MyLayeredPane.java create mode 100644 platform/vcs-api/src/com/intellij/openapi/vcs/RichTextItem.java create mode 100644 platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotation.java create mode 100644 platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationImpl.java create mode 100644 platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationSettings.java create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsContentAnnotationConfigurable.java 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 0d965a038ed1..5e026810b52a 100644 --- a/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java +++ b/java/execution/openapi/src/com/intellij/execution/filters/ExceptionFilter.java @@ -15,6 +15,7 @@ */ 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; @@ -23,14 +24,13 @@ 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.util.io.FileUtil; -import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotation; +import com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationImpl; 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.*; 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; @@ -38,12 +38,14 @@ import org.jetbrains.annotations.Nullable; import java.awt.*; -public class ExceptionFilter implements Filter, DumbAware { +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) { @@ -57,7 +59,7 @@ public class ExceptionFilter implements Filter, DumbAware { } @Nullable - static Trinity parseExceptionLine(final String line) { + static Trinity parseExceptionLine(final String line) { int atIndex; if (line.startsWith(AT_PREFIX)){ atIndex = 0; @@ -74,76 +76,178 @@ public class ExceptionFilter implements Filter, DumbAware { if (lparenthIndex < 0) return null; final int lastDotIndex = line.lastIndexOf('.', lparenthIndex); if (lastDotIndex < 0 || lastDotIndex < atIndex) return null; - String className = line.substring(atIndex + AT.length() + 1, lastDotIndex).trim(); - - String methodName = line.substring(lastDotIndex + 1, lparenthIndex).trim(); final int rparenthIndex = line.indexOf(')', lparenthIndex); if (rparenthIndex < 0) return null; - return Trinity.create(className, methodName, new TextRange(lparenthIndex, rparenthIndex)); + // 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 + } + } } public Result applyFilter(final String line, final int textEndOffset) { - final Trinity info = parseExceptionLine(line); - if (info == null) { - return null; - } - - String className = info.first; - final int dollarIndex = className.indexOf('$'); - if (dollarIndex >= 0){ - className = className.substring(0, dollarIndex); - } - - final int lparenthIndex = info.third.getStartOffset(); - final int rparenthIndex = info.third.getEndOffset(); - final String fileAndLine = line.substring(lparenthIndex + 1, rparenthIndex).trim(); - - final int colonIndex = fileAndLine.lastIndexOf(':'); - if (colonIndex < 0) return null; - - 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()); - PsiClass aClass = psiFacade.findClass(className, mySearchScope); - if (aClass == null) { - aClass = psiFacade.findClass(className, GlobalSearchScope.allScope(myProject)); - if (aClass == null) {//try to find class according to all dollars in package name - aClass = psiFacade.findClass(info.first, GlobalSearchScope.allScope(myProject)); - } - if (aClass == null) return null; - } - final PsiFile file = (PsiFile) aClass.getContainingFile().getNavigationElement(); - if (file == null) return null; - - /* - 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 = file.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); - } - return new Result(highlightStartOffset, highlightEndOffset, linkInfo, attributes); - } - catch(NumberFormatException e){ - return null; - } + final MyWorker worker = new MyWorker(); + 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/java-impl/src/com/intellij/unscramble/ThreadDumpPanel.java b/java/java-impl/src/com/intellij/unscramble/ThreadDumpPanel.java index 90921e32e1fb..0a87d9541f09 100644 --- a/java/java-impl/src/com/intellij/unscramble/ThreadDumpPanel.java +++ b/java/java-impl/src/com/intellij/unscramble/ThreadDumpPanel.java @@ -89,7 +89,8 @@ public class ThreadDumpPanel extends JPanel { }); toolbarActions.add(new CopyToClipboardAction(threadDump, project)); toolbarActions.add(new SortThreadsAction()); - add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarActions,false).getComponent(), BorderLayout.WEST); + //toolbarActions.add(new ShowRecentlyChanged()); + add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, toolbarActions, false).getComponent(), BorderLayout.WEST); final Splitter splitter = new Splitter(false, 0.3f); splitter.setFirstComponent(ScrollPaneFactory.createScrollPane(myThreadList)); 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 67bfb51c47ed..f663a93d2199 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java +++ b/platform/lang-api/src/com/intellij/execution/filters/CompositeFilter.java @@ -17,13 +17,15 @@ package com.intellij.execution.filters; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.Project; +import com.intellij.util.Consumer; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; -public class CompositeFilter implements Filter { +public class CompositeFilter implements Filter, FilterMixin { private final List myFilters = new ArrayList(); + private boolean myIsAnyHeavy; private final DumbService myDumbService; public CompositeFilter(Project project) { @@ -48,11 +50,31 @@ public class CompositeFilter implements Filter { return null; } + @Override + public void applyHeavyFilter(String line, int entireLength, int lineNumber, Consumer consumer) { + final boolean dumb = myDumbService.isDumb(); + List filters = myFilters; + int count = filters.size(); + //noinspection ForLoopReplaceableByForEach + for (int i = 0; i < count; i++) { + Filter filter = filters.get(i); + if (! (filter instanceof FilterMixin)) continue; + if (!dumb || DumbService.isDumbAware(filter)) { + ((FilterMixin) filter).applyHeavyFilter(line, entireLength, lineNumber, consumer); + } + } + } + public boolean isEmpty() { return myFilters.isEmpty(); } + public boolean isAnyHeavy() { + return myIsAnyHeavy; + } + public void addFilter(final Filter filter) { myFilters.add(filter); + myIsAnyHeavy |= filter instanceof FilterMixin; } -} \ No newline at end of file +} diff --git a/platform/lang-api/src/com/intellij/execution/filters/Filter.java b/platform/lang-api/src/com/intellij/execution/filters/Filter.java index 264792ec8723..83ef58fd741f 100644 --- a/platform/lang-api/src/com/intellij/execution/filters/Filter.java +++ b/platform/lang-api/src/com/intellij/execution/filters/Filter.java @@ -44,6 +44,7 @@ public interface Filter { /** * Filters line by creating an instance of {@link Result}. * + * * @param line * The line to be filtered. Note that the line must contain a line * separator at the end. diff --git a/platform/lang-api/src/com/intellij/execution/filters/FilterMixin.java b/platform/lang-api/src/com/intellij/execution/filters/FilterMixin.java new file mode 100644 index 000000000000..175817c59a4f --- /dev/null +++ b/platform/lang-api/src/com/intellij/execution/filters/FilterMixin.java @@ -0,0 +1,51 @@ +/* + * 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.markup.TextAttributes; +import com.intellij.util.Consumer; +import org.jetbrains.annotations.Nullable; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/3/11 + * Time: 4:50 PM + */ +public interface FilterMixin { + @Nullable + void applyHeavyFilter(String line, int entireLength, int lineNumber, Consumer consumer); + + abstract class AdditionalHighlight { + private final int myStart; + private final int myEnd; + + public AdditionalHighlight(int start, int end) { + myStart = start; + myEnd = end; + } + + public int getStart() { + return myStart; + } + + public int getEnd() { + return myEnd; + } + + public abstract TextAttributes getTextAttributes(@Nullable final TextAttributes source); + } +} 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 c98211693584..8a062ad00de0 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -41,7 +41,6 @@ import com.intellij.openapi.editor.actions.ToggleUseSoftWrapsToolbarAction; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.event.*; import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.ex.FoldingModelEx; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.highlighter.EditorHighlighter; import com.intellij.openapi.editor.highlighter.HighlighterClient; @@ -60,6 +59,7 @@ import com.intellij.openapi.keymap.KeymapManager; 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; @@ -75,6 +75,7 @@ import com.intellij.util.Consumer; import com.intellij.util.EditorPopupHandler; import com.intellij.util.LocalTimeCounter; import com.intellij.util.text.CharArrayUtil; +import com.intellij.util.ui.AsyncProcessIcon; import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -91,7 +92,7 @@ import java.util.*; import java.util.List; import java.util.concurrent.CopyOnWriteArraySet; -public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableConsoleView, DataProvider, OccurenceNavigator { +public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, DataProvider, OccurenceNavigator { @NonNls private static final String CONSOLE_VIEW_POPUP_MENU = "ConsoleView.PopupMenu"; private static final Logger LOG = Logger.getInstance("#com.intellij.execution.impl.ConsoleViewImpl"); @@ -115,12 +116,17 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo private Computable myStateForUpdate; private final Alarm mySpareTimeAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, this); + @Nullable + private final Alarm myHeavyAlarm; private final CopyOnWriteArraySet myListeners = new CopyOnWriteArraySet(); private final ArrayList customActions = new ArrayList(); private final ConsoleBuffer myBuffer = new ConsoleBuffer(); private boolean myUpdateFoldingsEnabled = true; private EditorHyperlinkSupport myHyperlinks; + private AsyncProcessIcon myAsyncProcessIcon; + private JLayeredPane myJLayeredPane; + private JPanel myMainPanel; @TestOnly public Editor getEditor() { @@ -272,7 +278,6 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo protected ConsoleViewImpl(final Project project, GlobalSearchScope searchScope, boolean viewer, FileType fileType, @NotNull final ConsoleState initialState) { - super(new BorderLayout()); isViewer = viewer; myState = initialState; myPsiDisposedCheck = new DisposedPsiManagerCheck(project); @@ -289,6 +294,11 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo myPredefinedMessageFilter.addFilter(filter); } } + if (myPredefinedMessageFilter.isAnyHeavy()) { + myHeavyAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD, this); + } else { + myHeavyAlarm = null; + } Disposer.register(project, this); } @@ -368,17 +378,34 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } }, 100, - ModalityState.stateForComponent(this) + ModalityState.stateForComponent(myJLayeredPane) ); } } + public void addToolbar(JComponent component, String constraint) { + myMainPanel.add(component, constraint); + } + public JComponent getComponent() { + myJLayeredPane = new MyLayeredPane(); + myJLayeredPane.setLayout(new BorderLayout()); if (myEditor == null) { myEditor = createEditor(); myHyperlinks = new EditorHyperlinkSupport(myEditor, myProject); requestFlushImmediately(); - add(createCenterComponent(), BorderLayout.CENTER); + myMainPanel = new JPanel(new BorderLayout()); + myMainPanel.add(createCenterComponent(), BorderLayout.CENTER); + myJLayeredPane.add(myMainPanel, BorderLayout.CENTER, JLayeredPane.DEFAULT_LAYER); + + myAsyncProcessIcon = new AsyncProcessIcon(toString()).setUseMask(false); + myAsyncProcessIcon.setOpaque(false); + myAsyncProcessIcon.setPaintPassiveIcon(false); + myAsyncProcessIcon.suspend(); + /*JPanel wrapper = new JPanel(new BorderLayout()); + wrapper.add(myAsyncProcessIcon, BorderLayout.NORTH); + wrapper.setOpaque(false);*/ + myJLayeredPane.add(myAsyncProcessIcon, BorderLayout.NORTH, JLayeredPane.DRAG_LAYER); myEditor.getDocument().addDocumentListener(new DocumentAdapter() { public void documentChanged(DocumentEvent e) { @@ -412,7 +439,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } }); } - return this; + return myJLayeredPane; } protected JComponent createCenterComponent() { @@ -476,7 +503,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo } private ModalityState getStateForUpdate() { - return myStateForUpdate != null ? myStateForUpdate.compute() : ModalityState.stateForComponent(this); + return myStateForUpdate != null ? myStateForUpdate.compute() : ModalityState.stateForComponent(myJLayeredPane); } private void requestFlushImmediately() { @@ -789,6 +816,46 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo 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(); + } + } + }, 0); + } + } if (myUpdateFoldingsEnabled) { updateFoldings(line1, endLine, true); } @@ -1066,7 +1133,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo s = (String)content.getTransferData(DataFlavor.stringFlavor); } catch (Exception e) { - consoleView.getToolkit().beep(); + consoleView.getComponent().getToolkit().beep(); } if (s == null) return; Editor editor = consoleView.myEditor; @@ -1279,6 +1346,7 @@ public class ConsoleViewImpl extends JPanel implements ConsoleView, ObservableCo consoleActions[1] = nextAction; consoleActions[2] = switchSoftWrapsAction; consoleActions[3] = autoScrollToTheEndAction; + //consoleActions[4] = new ShowRecentlyChanged(); for (int i = 0; i < customActions.size(); ++i) { consoleActions[i + 4] = customActions.get(i); } diff --git a/platform/lang-impl/src/com/intellij/unscramble/ShowRecentlyChanged.java b/platform/lang-impl/src/com/intellij/unscramble/ShowRecentlyChanged.java new file mode 100644 index 000000000000..678f8b44f9a0 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/unscramble/ShowRecentlyChanged.java @@ -0,0 +1,61 @@ +/* + * 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.unscramble; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.options.ShowSettingsUtil; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +import com.intellij.openapi.vcs.configurable.VcsContentAnnotationConfigurable; + +/** +* Created by IntelliJ IDEA. +* User: Irina.Chernushina +* Date: 8/4/11 +* Time: 2:29 PM +* To change this template use File | Settings | File Templates. +*/ +public class ShowRecentlyChanged extends DumbAwareAction { + public ShowRecentlyChanged() { + super("Show recently changed", "Show recently changed", IconLoader.getIcon("/general/copy.png")); + } + + @Override + public void actionPerformed(AnActionEvent e) { + if (! enabled(e)) return; + Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext()); + VcsContentAnnotationConfigurable configurable = new VcsContentAnnotationConfigurable(project); + ShowSettingsUtil.getInstance().editConfigurable(project, configurable); + // todo recalculate highlight + } + + private boolean enabled(AnActionEvent e) { + Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext()); + if (project == null) return false; + ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(project); + if (! vcsManager.hasActiveVcss()) return false; + return true; + } + + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(enabled(e)); + } +} diff --git a/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java b/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java index 6e1fd9ca0f2c..22ae559d18ed 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/LoadingDecorator.java @@ -258,16 +258,4 @@ public class LoadingDecorator { frame.setBounds(300, 300, 300, 300); frame.show(); } - - - private static class MyLayeredPane extends JLayeredPane { - @Override - public void doLayout() { - super.doLayout(); - for (int i = 0; i < getComponentCount(); i++) { - final Component each = getComponent(i); - each.setBounds(0, 0, getWidth(), getHeight()); - } - } - } } diff --git a/platform/platform-api/src/com/intellij/openapi/ui/MyLayeredPane.java b/platform/platform-api/src/com/intellij/openapi/ui/MyLayeredPane.java new file mode 100644 index 000000000000..f0dce003be41 --- /dev/null +++ b/platform/platform-api/src/com/intellij/openapi/ui/MyLayeredPane.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.openapi.ui; + +import javax.swing.*; +import java.awt.*; + +/** +* Created by IntelliJ IDEA. +* User: Irina.Chernushina +* Date: 8/4/11 +* Time: 3:59 PM +* To change this template use File | Settings | File Templates. +*/ +public class MyLayeredPane extends JLayeredPane { + @Override + public void doLayout() { + super.doLayout(); + for (int i = 0; i < getComponentCount(); i++) { + final Component each = getComponent(i); + if (each instanceof Icon) { + each.setBounds(0, 0, each.getWidth(), each.getHeight()); + } else { + each.setBounds(0, 0, getWidth(), getHeight()); + } + } + } +} diff --git a/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java b/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java index 9b8725ea98b7..49d7a30a7cf7 100644 --- a/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java +++ b/platform/platform-api/src/com/intellij/ui/SimpleColoredComponent.java @@ -45,6 +45,7 @@ import java.util.Map; */ public class SimpleColoredComponent extends JComponent implements Accessible { private static final Logger LOG = Logger.getInstance("#com.intellij.ui.SimpleColoredComponent"); + public static final Color STYLE_SEARCH_MATCH_BACKGROUND = new Color(250, 250, 250, 140); private final ArrayList myFragments; private final ArrayList myAttributes; @@ -439,7 +440,7 @@ public class SimpleColoredComponent extends JComponent implements Accessible { if (!attributes.isSearchMatch()) { if (shouldDrawMacShadow()) { - g.setColor(new Color(250, 250, 250, 140)); + g.setColor(STYLE_SEARCH_MATCH_BACKGROUND); g.drawString(fragment, xOffset, textBaseline + 1); } 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 85832860109c..68fb46a6368f 100644 --- a/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java +++ b/platform/platform-impl/src/com/intellij/execution/impl/EditorHyperlinkSupport.java @@ -16,6 +16,7 @@ package com.intellij.execution.impl; import com.intellij.execution.filters.Filter; +import com.intellij.execution.filters.FilterMixin; import com.intellij.execution.filters.HyperlinkInfo; import com.intellij.ide.OccurenceNavigator; import com.intellij.openapi.editor.Document; @@ -33,7 +34,9 @@ import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.pom.Navigatable; +import com.intellij.util.BeforeAfter; import com.intellij.util.Consumer; +import com.intellij.util.SmartList; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,14 +53,18 @@ import java.util.List; public class EditorHyperlinkSupport { public static final Key OLD_HYPERLINK_TEXT_ATTRIBUTES = Key.create("OLD_HYPERLINK_TEXT_ATTRIBUTES"); private static final int HYPERLINK_LAYER = HighlighterLayer.SELECTION - 123; + private static final int HIGHLIGHT_LAYER = HighlighterLayer.SELECTION - 111; private static final int NO_INDEX = Integer.MIN_VALUE; private final Editor myEditor; private final Map myHighlighterToMessageInfoMap = new HashMap(); private int myLastIndex = NO_INDEX; + private final Consumer> myRefresher; + private final List myHighlighters; public EditorHyperlinkSupport(@NotNull final Editor editor, @NotNull final Project project) { myEditor = editor; + myHighlighters = new SmartList(); editor.addEditorMouseListener(new EditorMouseAdapter() { public void mouseReleased(final EditorMouseEvent e) { @@ -86,10 +93,78 @@ public class EditorHyperlinkSupport { } } ); + + myRefresher = new Consumer>() { + @Override + public void consume(BeforeAfter resultBeforeAfter) { + if (resultBeforeAfter.getBefore() == null) return; + final RangeHighlighter hyperlinkRange = findHyperlinkRange(resultBeforeAfter.getBefore().hyperlinkInfo); + if (hyperlinkRange != null) { + myHighlighterToMessageInfoMap.remove(hyperlinkRange); + } else { + final Iterator iterator = myHighlighters.iterator(); + while (iterator.hasNext()) { + final RangeHighlighter highlighter = iterator.next(); + if (highlighter.isValid() && containsOffset(resultBeforeAfter.getBefore().highlightStartOffset, highlighter)) { + iterator.remove(); + break; + } + } + } + + if (resultBeforeAfter.getAfter() != null) { + if (resultBeforeAfter.getAfter().hyperlinkInfo != null) { + addHyperlink(resultBeforeAfter.getAfter().highlightStartOffset, resultBeforeAfter.getAfter().highlightEndOffset, + resultBeforeAfter.getAfter().highlightAttributes, resultBeforeAfter.getAfter().hyperlinkInfo); + } else if (resultBeforeAfter.getAfter().highlightAttributes != null) { + addHighlighter(resultBeforeAfter.getAfter().highlightStartOffset, resultBeforeAfter.getAfter().highlightEndOffset, + resultBeforeAfter.getAfter().highlightAttributes); + } + } + } + }; + } + + public void adjustHighlighters(final List highlights) { + for (FilterMixin.AdditionalHighlight highlight : highlights) { + RangeHighlighter found = null; + for (RangeHighlighter rangeHighlighter : myHighlighterToMessageInfoMap.keySet()) { + if (rangeHighlighter.getStartOffset() <= highlight.getStart() && rangeHighlighter.getEndOffset() >= highlight.getEnd()) { + found = rangeHighlighter; + break; + } + } + if (found != null) { + TextAttributes textAttributes = highlight.getTextAttributes(found.getTextAttributes()); + final HyperlinkInfo hyperlinkInfo = myHighlighterToMessageInfoMap.remove(found); + if (found.getStartOffset() != highlight.getStart()) { + addHyperlink(found.getStartOffset(), highlight.getEnd(), found.getTextAttributes(), hyperlinkInfo); + } + if (found.getEndOffset() != highlight.getEnd()) { + addHyperlink(highlight.getEnd(), found.getEndOffset(), found.getTextAttributes(), hyperlinkInfo); + } + addHyperlink(highlight.getStart(), highlight.getEnd(), textAttributes, hyperlinkInfo); + myEditor.getMarkupModel().removeHighlighter(found); + return; + } + final Iterator iterator = myHighlighters.iterator(); + while (iterator.hasNext()) { + final RangeHighlighter highlighter = iterator.next(); + if (highlighter.getStartOffset() == highlight.getStart() && highlighter.getEndOffset() == highlight.getEnd()) { + iterator.remove(); + final TextAttributes textAttributes = highlight.getTextAttributes(highlighter.getTextAttributes()); + addHighlighter(highlight.getStart(), highlight.getEnd(), textAttributes); + return; + } + } + final TextAttributes textAttributes = highlight.getTextAttributes(null); + addHighlighter(highlight.getStart(), highlight.getEnd(), textAttributes); + } } public void clearHyperlinks() { myHighlighterToMessageInfoMap.clear(); + myHighlighters.clear(); myLastIndex = NO_INDEX; } @@ -171,12 +246,25 @@ public class EditorHyperlinkSupport { if (result == null) { result = predefinedMessageFilter.applyFilter(text, endOffset); } - if (result != null && result.hyperlinkInfo != null) { - addHyperlink(result.highlightStartOffset, result.highlightEndOffset, result.highlightAttributes, result.hyperlinkInfo); + if (result != null) { + if (result.hyperlinkInfo != null) { + addHyperlink(result.highlightStartOffset, result.highlightEndOffset, result.highlightAttributes, result.hyperlinkInfo); + } else if (result.highlightAttributes != null) { + addHighlighter(result.highlightStartOffset, result.highlightEndOffset, result.highlightAttributes); + } } } } + private void addHighlighter(int highlightStartOffset, int highlightEndOffset, TextAttributes highlightAttributes) { + final RangeHighlighter highlighter = myEditor.getMarkupModel().addRangeHighlighter(highlightStartOffset, + highlightEndOffset, + HIGHLIGHT_LAYER, + highlightAttributes, + HighlighterTargetArea.EXACT_RANGE); + myHighlighters.add(highlighter); + } + private static TextAttributes getHyperlinkAttributes() { return EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.HYPERLINK_ATTRIBUTES); } diff --git a/platform/platform-resources/src/componentSets/VCS.xml b/platform/platform-resources/src/componentSets/VCS.xml index 2f0f306727ef..4b06ef8b184e 100644 --- a/platform/platform-resources/src/componentSets/VCS.xml +++ b/platform/platform-resources/src/componentSets/VCS.xml @@ -106,5 +106,10 @@ com.intellij.openapi.vcs.impl.VcsBaseContentProvider com.intellij.openapi.vcs.impl.VcsFileStatusProvider + com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationSettings + com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationSettings + + com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotation + com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationImpl diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/RichTextItem.java b/platform/vcs-api/src/com/intellij/openapi/vcs/RichTextItem.java new file mode 100644 index 000000000000..573558853e34 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/RichTextItem.java @@ -0,0 +1,43 @@ +/* + * 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; + +import com.intellij.ui.SimpleTextAttributes; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/3/11 + * Time: 12:56 PM + * To change this template use File | Settings | File Templates. + */ +public class RichTextItem { + private final String myText; + private final SimpleTextAttributes myTextAttributes; + + public RichTextItem(String text, SimpleTextAttributes textAttributes) { + myText = text; + myTextAttributes = textAttributes; + } + + public String getText() { + return myText; + } + + public SimpleTextAttributes getTextAttributes() { + return myTextAttributes; + } +} 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 new file mode 100644 index 000000000000..500c3f93cad3 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotation.java @@ -0,0 +1,62 @@ +/* + * 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.openapi.vcs.RichTextItem; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.BeforeAfter; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/3/11 + * Time: 12:50 PM + */ +public interface VcsContentAnnotation { + @Nullable + Details annotateLine(final VirtualFile vf, final BeforeAfter enclosingRange, final int lineNumber); + + class Details { + private final boolean myLineChanged; + // meaningful enclosing structure + private final boolean myMethodChanged; + private final boolean myFileChanged; + @Nullable + private final List myDetails; + + public Details(boolean lineChanged, boolean methodChanged, boolean fileChanged, List details) { + myLineChanged = lineChanged; + myMethodChanged = methodChanged; + myFileChanged = fileChanged; + myDetails = details; + } + + public boolean isLineChanged() { + return myLineChanged; + } + + public boolean isMethodChanged() { + return myMethodChanged; + } + + public boolean isFileChanged() { + return myFileChanged; + } + } +} 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 new file mode 100644 index 000000000000..f52078c65284 --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationImpl.java @@ -0,0 +1,66 @@ +/* + * 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.openapi.components.ServiceManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; +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; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/3/11 + * Time: 1:04 PM + */ +public class VcsContentAnnotationImpl implements VcsContentAnnotation { + private final Project myProject; + private final VcsContentAnnotationSettings mySettings; + + public static VcsContentAnnotation getInstance(final Project project) { + return ServiceManager.getService(project, VcsContentAnnotation.class); + } + + public VcsContentAnnotationImpl(Project project, VcsContentAnnotationSettings settings) { + myProject = project; + mySettings = settings; + } + + @Nullable + @Override + public Details annotateLine(final VirtualFile vf, final BeforeAfter enclosingRange, final int lineNumber) { + final ProjectLevelVcsManager vcsManager = ProjectLevelVcsManager.getInstance(myProject); + final AbstractVcs vcs = vcsManager.getVcsFor(vf); + if (vcs == null) return null; + if (vcs.getDiffProvider() instanceof DiffMixin) { + boolean fileRecent = false; + final VcsRevisionDescription description = ((DiffMixin)vcs.getDiffProvider()).getCurrentRevisionDescription(vf); + final Date date = description.getRevisionDate(); + if (date.getTime() > (System.currentTimeMillis() - mySettings.getLimit())) { + fileRecent = true; + } + return new Details(false, false, fileRecent, null); + } + return null; + } +} diff --git a/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationSettings.java b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationSettings.java new file mode 100644 index 000000000000..87228feb1dbf --- /dev/null +++ b/platform/vcs-api/src/com/intellij/openapi/vcs/contentAnnotation/VcsContentAnnotationSettings.java @@ -0,0 +1,81 @@ +/* + * 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.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.project.Project; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/3/11 + * Time: 1:13 PM + */ +@State( + name = "VcsContentAnnotationSettings", + storages = {@Storage( file = "$WORKSPACE_FILE$")}) +public class VcsContentAnnotationSettings implements PersistentStateComponent { + // approx + public static final int ourMaxDays = 31; + public final static long ourAbsoluteLimit = ourMaxDays * 24 * 60 * 60 * 1000L; + private State myState = new State(); + + { + myState.myLimit = ourAbsoluteLimit; + } + + public static VcsContentAnnotationSettings getInstance(final Project project) { + return ServiceManager.getService(project, VcsContentAnnotationSettings.class); + } + + public static class State { + public boolean myShow = true; + public long myLimit; + } + + @Override + public State getState() { + return myState; + } + + @Override + public void loadState(State state) { + myState = state; + } + + public long getLimit() { + return myState.myLimit; + } + + public long getLimitDays() { + return myState.myLimit / (24 * 60 * 60 * 1000L); + } + + public void setLimit(long limit) { + myState.myLimit = limit * 24 * 60 * 60 * 1000L; + } + + public boolean isShow() { + return myState.myShow; + } + + public void setShow(final boolean value) { + myState.myShow = value; + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsContentAnnotationConfigurable.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsContentAnnotationConfigurable.java new file mode 100644 index 000000000000..f2d9f4f4b5f4 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsContentAnnotationConfigurable.java @@ -0,0 +1,106 @@ +/* + * 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.configurable; + +import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.vcs.contentAnnotation.VcsContentAnnotationSettings; +import org.jetbrains.annotations.Nls; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +/** + * Created by IntelliJ IDEA. + * User: Irina.Chernushina + * Date: 8/4/11 + * Time: 2:05 PM + */ +public class VcsContentAnnotationConfigurable implements Configurable { + private final Project myProject; + private JCheckBox myHighlightRecentlyChanged; + private JSpinner myHighlightInterval; + + public VcsContentAnnotationConfigurable(Project project) { + myProject = project; + } + + @Nls + @Override + public String getDisplayName() { + return "Show recently changed"; + } + + @Override + public Icon getIcon() { + return null; + } + + @Override + public String getHelpTopic() { + return null; + } + + @Override + public JComponent createComponent() { + JPanel wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT)); + myHighlightRecentlyChanged = new JCheckBox("Show changed in last"); + myHighlightRecentlyChanged.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0)); + myHighlightInterval = new JSpinner(new SpinnerNumberModel(1, 1, VcsContentAnnotationSettings.ourMaxDays, 1)); + wrapper.add(myHighlightRecentlyChanged); + wrapper.add(myHighlightInterval); + wrapper.add(new JLabel("days")); + + myHighlightRecentlyChanged.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + myHighlightInterval.setEnabled(myHighlightRecentlyChanged.isSelected()); + } + }); + return wrapper; + } + + @Override + public boolean isModified() { + VcsContentAnnotationSettings settings = VcsContentAnnotationSettings.getInstance(myProject); + if (myHighlightRecentlyChanged.isSelected() != settings.isShow()) return true; + if (! Comparing.equal(myHighlightInterval.getValue(), settings.getLimitDays())) return true; + return false; + } + + @Override + public void apply() throws ConfigurationException { + VcsContentAnnotationSettings settings = VcsContentAnnotationSettings.getInstance(myProject); + settings.setShow(myHighlightRecentlyChanged.isSelected()); + settings.setLimit(((Number) myHighlightInterval.getValue()).intValue()); + } + + @Override + public void reset() { + VcsContentAnnotationSettings settings = VcsContentAnnotationSettings.getInstance(myProject); + myHighlightRecentlyChanged.setSelected(settings.isShow()); + myHighlightInterval.setValue(settings.getLimitDays()); + myHighlightInterval.setEnabled(myHighlightRecentlyChanged.isSelected()); + } + + @Override + public void disposeUIResources() { + } +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsDirectoryConfigurationPanel.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsDirectoryConfigurationPanel.java index f2b0f790866f..4e5e87c13f45 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsDirectoryConfigurationPanel.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/configurable/VcsDirectoryConfigurationPanel.java @@ -18,6 +18,7 @@ package com.intellij.openapi.vcs.configurable; import com.intellij.CommonBundle; import com.intellij.openapi.options.Configurable; +import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.AbstractVcs; @@ -134,6 +135,7 @@ public class VcsDirectoryConfigurationPanel extends PanelWithButtons implements private JButton myEditButton; private JButton myRemoveButton; private final Map myAllVcss; + private VcsContentAnnotationConfigurable myRecentlyChangedConfigurable; private final boolean myIsDisabled; public VcsDirectoryConfigurationPanel(final Project project) { @@ -147,6 +149,7 @@ public class VcsDirectoryConfigurationPanel extends PanelWithButtons implements } myDirectoryMappingTable = new TableView(); + initPanel(); initializeModel(); final JComboBox comboBox = myVcsComboBox.getComboBox(); @@ -172,7 +175,6 @@ public class VcsDirectoryConfigurationPanel extends PanelWithButtons implements updateButtons(); } }); - initPanel(); updateButtons(); if (myIsDisabled) { myDirectoryMappingTable.setEnabled(false); @@ -187,6 +189,8 @@ public class VcsDirectoryConfigurationPanel extends PanelWithButtons implements } myModel = new ListTableModel(new ColumnInfo[]{DIRECTORY, VCS_SETTING}, mappings, 0); myDirectoryMappingTable.setModel(myModel); + + myRecentlyChangedConfigurable.reset(); } private void updateButtons() { @@ -273,19 +277,26 @@ public class VcsDirectoryConfigurationPanel extends PanelWithButtons implements } protected JComponent createMainComponent() { - return ScrollPaneFactory.createScrollPane(myDirectoryMappingTable); + JPanel panel = new JPanel(new BorderLayout()); + final JScrollPane scroll = ScrollPaneFactory.createScrollPane(myDirectoryMappingTable); + panel.add(scroll, BorderLayout.CENTER); + myRecentlyChangedConfigurable = new VcsContentAnnotationConfigurable(myProject); + panel.add(myRecentlyChangedConfigurable.createComponent(), BorderLayout.SOUTH); + return panel; } public void reset() { initializeModel(); } - public void apply() { + public void apply() throws ConfigurationException { myVcsManager.setDirectoryMappings(myModel.getItems()); + myRecentlyChangedConfigurable.apply(); initializeModel(); } public boolean isModified() { + if (myRecentlyChangedConfigurable.isModified()) return true; return !myModel.getItems().equals(myVcsManager.getDirectoryMappings()); } diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml index a69c0cbd97d0..32679c59b42f 100644 --- a/plugins/git4idea/src/META-INF/plugin.xml +++ b/plugins/git4idea/src/META-INF/plugin.xml @@ -118,7 +118,6 @@ serviceImplementation="git4idea.DialogManager"/> - From 819cd411e8f9de9779bfed82c7151f34a850ce2b Mon Sep 17 00:00:00 2001 From: irengrig Date: Fri, 5 Aug 2011 16:31:17 +0400 Subject: [PATCH 2/5] console view - back from layered pane for now --- .../src/com/intellij/execution/impl/ConsoleViewImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8a062ad00de0..f255923f0cdc 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -439,7 +439,7 @@ public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, Data } }); } - return myJLayeredPane; + return myMainPanel; } protected JComponent createCenterComponent() { From 6fa1c20b46c79081eebfd38851044f04179cb9a4 Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 8 Aug 2011 15:07:33 +0400 Subject: [PATCH 3/5] 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() { From 783f9512f121d5b86208ac8bfbbe7793b0aba03b Mon Sep 17 00:00:00 2001 From: irengrig Date: Mon, 8 Aug 2011 15:58:07 +0400 Subject: [PATCH 4/5] annotate stacktrace - progress --- .../execution/impl/ConsoleViewImpl.java | 36 ++++--- .../execution/impl/MyDiffContainer.java | 96 +++++++++++++++++++ 2 files changed, 113 insertions(+), 19 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/execution/impl/MyDiffContainer.java 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 6ee68e74a2a5..adcd00f0678e 100644 --- a/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/impl/ConsoleViewImpl.java @@ -60,7 +60,6 @@ import com.intellij.openapi.keymap.KeymapManager; 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.*; import com.intellij.openapi.util.text.LineTokenizer; import com.intellij.psi.PsiDocumentManager; @@ -73,7 +72,6 @@ import com.intellij.util.Consumer; import com.intellij.util.EditorPopupHandler; import com.intellij.util.LocalTimeCounter; import com.intellij.util.text.CharArrayUtil; -import com.intellij.util.ui.AsyncProcessIcon; import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -122,9 +120,9 @@ public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, Data private final ConsoleBuffer myBuffer = new ConsoleBuffer(); private boolean myUpdateFoldingsEnabled = true; private EditorHyperlinkSupport myHyperlinks; - private AsyncProcessIcon myAsyncProcessIcon; - private JLayeredPane myJLayeredPane; + private MyDiffContainer myJLayeredPane; private JPanel myMainPanel; + private final Runnable myFinishProgress; @TestOnly public Editor getEditor() { @@ -299,6 +297,12 @@ public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, Data } Disposer.register(project, this); + myFinishProgress = new Runnable() { + @Override + public void run() { + myJLayeredPane.finishUpdating(); + } + }; } public void attachToProcess(final ProcessHandler processHandler) { @@ -386,25 +390,17 @@ public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, Data } public JComponent getComponent() { - myJLayeredPane = new MyLayeredPane(); - myJLayeredPane.setLayout(new BorderLayout()); + if (myMainPanel == null) { + myMainPanel = new JPanel(new BorderLayout()); + myJLayeredPane = new MyDiffContainer(myMainPanel, "Checking recent changes..."); + } + if (myEditor == null) { myEditor = createEditor(); myHyperlinks = new EditorHyperlinkSupport(myEditor, myProject); requestFlushImmediately(); - myMainPanel = new JPanel(new BorderLayout()); myMainPanel.add(createCenterComponent(), BorderLayout.CENTER); - myJLayeredPane.add(myMainPanel, BorderLayout.CENTER, JLayeredPane.DEFAULT_LAYER); - myAsyncProcessIcon = new AsyncProcessIcon(toString()).setUseMask(false); - myAsyncProcessIcon.setOpaque(false); - myAsyncProcessIcon.setPaintPassiveIcon(false); - myAsyncProcessIcon.suspend(); - /*JPanel wrapper = new JPanel(new BorderLayout()); - wrapper.add(myAsyncProcessIcon, BorderLayout.NORTH); - wrapper.setOpaque(false);*/ - - //myJLayeredPane.add(myAsyncProcessIcon, BorderLayout.NORTH, JLayeredPane.DRAG_LAYER); myEditor.getDocument().addDocumentListener(new DocumentAdapter() { public void documentChanged(DocumentEvent e) { @@ -438,7 +434,7 @@ public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, Data } }); } - return myMainPanel; + return myJLayeredPane; } protected JComponent createCenterComponent() { @@ -462,6 +458,7 @@ public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, Data myEditor = null; myHyperlinks = null; } + myJLayeredPane.dispose(); } protected void disposeEditor() { @@ -825,6 +822,7 @@ public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, Data documentCopy.setText(new String(document.getText(new TextRange(startOffset, document.getLineEndOffset(endLine))))); documentCopy.setReadOnly(true); + myJLayeredPane.startUpdating(); myHeavyAlarm.addRequest(new Runnable() { @Override public void run() { @@ -846,7 +844,7 @@ public class ConsoleViewImpl implements ConsoleView, ObservableConsoleView, Data } }); if (myHeavyAlarm.getActiveRequestCount() == 0) { - myAsyncProcessIcon.suspend(); + SwingUtilities.invokeLater(myFinishProgress); } } }, 0); diff --git a/platform/lang-impl/src/com/intellij/execution/impl/MyDiffContainer.java b/platform/lang-impl/src/com/intellij/execution/impl/MyDiffContainer.java new file mode 100644 index 000000000000..7724d6803bbb --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/impl/MyDiffContainer.java @@ -0,0 +1,96 @@ +/* + * 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.impl; + +import com.intellij.openapi.Disposable; +import com.intellij.util.ui.AbstractLayoutManager; +import com.intellij.util.ui.AnimatedIcon; +import com.intellij.util.ui.AsyncProcessIcon; +import com.intellij.util.ui.UIUtil; + +import javax.swing.*; +import java.awt.*; + +/** +* Created by IntelliJ IDEA. +* @author amakeev +* @author Irina.Chernushina +* Date: 8/8/11 +* Time: 3:13 PM +*/ +public class MyDiffContainer extends JLayeredPane implements Disposable { + private AnimatedIcon myIcon = new AsyncProcessIcon(this.getClass().getName()); + + private JComponent myContent; + private JComponent myLoadingPanel; + private final JLabel myJLabel; + + public MyDiffContainer(JComponent content, final String text) { + setLayout(new MyOverlayLayout()); + myContent = content; + myLoadingPanel = new JPanel(new MyPanelLayout()); + myLoadingPanel.setOpaque(false); + myLoadingPanel.add(myIcon); + myJLabel = new JLabel(text); + myJLabel.setForeground(UIUtil.getInactiveTextColor()); + myLoadingPanel.add(myJLabel); + + add(myContent); + add(myLoadingPanel, JLayeredPane.POPUP_LAYER); + + finishUpdating(); + } + + public void dispose() { + myIcon.dispose(); + } + + public void startUpdating() { + myLoadingPanel.setVisible(true); + myIcon.resume(); + } + + public void finishUpdating() { + myIcon.suspend(); + myLoadingPanel.setVisible(false); + } + + private class MyOverlayLayout extends AbstractLayoutManager { + public void layoutContainer(Container parent) { + myContent.setBounds(0, 0, getWidth(), getHeight()); + myLoadingPanel.setBounds(0, 0, getWidth(), getHeight()); + } + + public Dimension preferredLayoutSize(Container parent) { + return myContent.getPreferredSize(); + } + } + + private class MyPanelLayout extends AbstractLayoutManager { + public void layoutContainer(Container parent) { + Dimension size = myIcon.getPreferredSize(); + Dimension preferredSize = myJLabel.getPreferredSize(); + int width = getWidth(); + int offset = width - size.width - 15 - preferredSize.width; + myIcon.setBounds(offset, 0, size.width, size.height); + myJLabel.setBounds(offset + size.width + 3, 0, preferredSize.width, size.height); + } + + public Dimension preferredLayoutSize(Container parent) { + return myContent.getPreferredSize(); + } + } +} From 6c8e85d6e7df9c0c1d57b01646c6b7af8a194942 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Mon, 8 Aug 2011 16:02:33 +0400 Subject: [PATCH 5/5] IOOBE fix --- .../intellij/openapi/editor/impl/EditorMarkupModelImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java index fd4aabc7bdc6..2c86e314ecfb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorMarkupModelImpl.java @@ -384,19 +384,20 @@ public class EditorMarkupModelImpl extends MarkupModelImpl implements EditorMark if (clip.height == 0) return; final Rectangle componentBounds = c.getBounds(); + final TextRange docRange = TextRange.create(0, getEditor().getDocument().getTextLength()); if (myCachedTrack == null || myCachedTrack.getHeight() != componentBounds.getHeight()) { myCachedTrack = new BufferedImage(componentBounds.width, componentBounds.height, BufferedImage.TYPE_INT_ARGB); - myDirtyRange = TextRange.create(0, getEditor().getDocument().getTextLength()); + myDirtyRange = docRange; paintTrackBasement(myCachedTrack.getGraphics(), new Rectangle(0, 0, componentBounds.width, componentBounds.height)); } if (myDirtyRange != null) { final Graphics2D imageGraphics = myCachedTrack.createGraphics(); - ((ApplicationImpl)ApplicationManager.getApplication()).editorPaintStart(); try { + myDirtyRange = myDirtyRange.intersection(docRange); repaint(imageGraphics, componentBounds.width, ERROR_ICON_WIDTH - 1, myDirtyRange.getStartOffset(), myDirtyRange.getEndOffset()); myDirtyRange = null; }