void (@NotNull final AnActionEvent e) { myIsLoading = true; Project project = e.getProject(); ConsoleViewImpl consoleView = (ConsoleViewImpl)e.getData(LangDataKeys.CONSOLE_VIEW); Editor editor = consoleView != null ? consoleView.getEditor() : null; if (project == null || editor == null) return; EditorHyperlinkSupport hyperlinks = consoleView.getHyperlinks(); ProgressManager.getInstance().run(new Task.Backgroundable( project, LangBundle.message("progress.title.getting.file.history"), true) { private final Object LOCK = new Object(); private final MergingUpdateQueue myUpdateQueue = new MergingUpdateQueue("AnnotateStackTraceAction", 200, true, null); private MyActiveAnnotationGutter myGutter; @Override public void onCancel() { editor.getGutter().closeAllAnnotations(); } @Override public void onFinished() { myIsLoading = false; Disposer.dispose(myUpdateQueue); } @Override public void run(@NotNull ProgressIndicator indicator) { Map<VirtualFile, IntList> files2lines = CollectionFactory.createSmallMemoryFootprintMap(); Int2ObjectMap<LastRevision> revisions = new Int2ObjectOpenHashMap<>(); ApplicationManager.getApplication().runReadAction(() -> { for (int line = 0; line < editor.getDocument().getLineCount(); line++) { indicator.checkCanceled(); VirtualFile file = getHyperlinkVirtualFile(hyperlinks.findAllHyperlinksOnLine(line)); if (file == null) continue; files2lines.computeIfAbsent(file, __ -> new IntArrayList()).add(line); } }); for (Map.Entry<VirtualFile, IntList> entry : files2lines.entrySet()) { VirtualFile file = entry.getKey(); IntList value = entry.getValue(); indicator.checkCanceled(); LastRevision revision = getLastRevision(file); if (revision == null) { continue; } synchronized (LOCK) { for (IntListIterator iterator = value.iterator(); iterator.hasNext(); ) { revisions.put(iterator.nextInt(), revision); } } myUpdateQueue.queue(new Update("update") { @Override public void run() { updateGutter(indicator, revisions); } }); } // myUpdateQueue can be disposed before the last revisions are passed to the gutter ApplicationManager.getApplication().invokeLater(() -> updateGutter(indicator, revisions)); } @RequiresEdt private void updateGutter(@NotNull ProgressIndicator indicator, @NotNull Map<Integer, LastRevision> revisions) { if (indicator.isCanceled()) return; if (myGutter == null) { myGutter = new MyActiveAnnotationGutter(getProject(), hyperlinks, indicator); editor.getGutter().registerTextAnnotation(myGutter, myGutter); } Map<Integer, LastRevision> revisionsCopy; synchronized (LOCK) { revisionsCopy = new HashMap<>(revisions); } myGutter.updateData(revisionsCopy); ((EditorGutterComponentEx)editor.getGutter()).revalidateMarkup(); } @Nullable private LastRevision getLastRevision(@NotNull VirtualFile file) { try { AbstractVcs vcs = VcsUtil.getVcsFor(project, file); if (vcs == null) return null; VcsHistoryProvider historyProvider = vcs.getVcsHistoryProvider(); if (historyProvider == null) return null; FilePath filePath = VcsContextFactory.getInstance().createFilePathOn(file); if (historyProvider instanceof VcsHistoryProviderEx) { VcsFileRevision revision = ((VcsHistoryProviderEx)historyProvider).getLastRevision(filePath); if (revision == null) return null; return LastRevision.create(revision); } else { VcsHistorySession session = historyProvider.createSessionFor(filePath); if (session == null) return null; List<VcsFileRevision> list = session.getRevisionList(); if (list == null || list.isEmpty()) return null; return LastRevision.create(list.get(0)); } } catch (VcsException e) { LOG.warn(e); return null; } } }); }