| /* |
| * Copyright 2016 The Bazel Authors. All rights reserved. |
| * |
| * 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.google.idea.blaze.clwb.problemsview; |
| |
| import com.google.idea.blaze.base.scope.output.IssueOutput; |
| import com.google.idea.blaze.base.ui.BlazeProblemsView; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.ide.errorTreeView.ErrorTreeElement; |
| import com.intellij.ide.errorTreeView.ErrorTreeElementKind; |
| import com.intellij.ide.errorTreeView.ErrorViewStructure; |
| import com.intellij.ide.errorTreeView.GroupingElement; |
| import com.intellij.openapi.components.ServiceManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.fileEditor.OpenFileDescriptor; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Disposer; |
| import com.intellij.openapi.util.IconLoader; |
| import com.intellij.openapi.vfs.VfsUtil; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.openapi.wm.ToolWindow; |
| import com.intellij.openapi.wm.ToolWindowManager; |
| import com.intellij.pom.Navigatable; |
| import com.intellij.ui.content.Content; |
| import com.intellij.ui.content.ContentFactory; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.concurrency.SequentialTaskExecutor; |
| import com.intellij.util.ui.MessageCategory; |
| import com.intellij.util.ui.UIUtil; |
| import java.util.ArrayList; |
| import java.util.EnumSet; |
| import java.util.List; |
| import java.util.StringTokenizer; |
| import java.util.UUID; |
| import javax.swing.Icon; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.ide.PooledThreadExecutor; |
| |
| /** CLion has no built-in 'Problems' view, so we mostly duplicate the IntelliJ code here. */ |
| public class BlazeProblemsViewConsole implements BlazeProblemsView { |
| private static final Logger LOG = Logger.getInstance(BlazeProblemsViewConsole.class); |
| |
| private static final String BLAZE_PROBLEMS_TOOLWINDOW_ID = "Blaze Problems"; |
| private static final EnumSet<ErrorTreeElementKind> ALL_MESSAGE_KINDS = |
| EnumSet.allOf(ErrorTreeElementKind.class); |
| |
| private final ProblemsViewPanel myPanel; |
| private final SequentialTaskExecutor myViewUpdater = |
| new SequentialTaskExecutor(PooledThreadExecutor.INSTANCE); |
| private final Icon myActiveIcon = AllIcons.Toolwindows.Problems; |
| private final Icon myPassiveIcon = IconLoader.getDisabledIcon(myActiveIcon); |
| |
| private final Project myProject; |
| |
| public static BlazeProblemsViewConsole getImpl(Project project) { |
| BlazeProblemsViewConsole blazeProblemsViewConsole = |
| (BlazeProblemsViewConsole) ServiceManager.getService(project, BlazeProblemsView.class); |
| LOG.assertTrue(blazeProblemsViewConsole != null); |
| return blazeProblemsViewConsole; |
| } |
| |
| public BlazeProblemsViewConsole(final Project project) { |
| myProject = project; |
| myPanel = new ProblemsViewPanel(project); |
| Disposer.register(project, () -> Disposer.dispose(myPanel)); |
| updateIcon(); |
| } |
| |
| public void createToolWindowContent(ToolWindow toolWindow) { |
| final Content content = ContentFactory.SERVICE.getInstance().createContent(myPanel, "", false); |
| toolWindow.getContentManager().addContent(content); |
| Disposer.register(myProject, () -> toolWindow.getContentManager().removeAllContents(true)); |
| updateIcon(); |
| } |
| |
| @Override |
| public final void addMessage(IssueOutput issue, @NotNull UUID sessionId) { |
| final VirtualFile file = |
| issue.getFile() != null |
| ? VfsUtil.findFileByIoFile(issue.getFile(), true /* refresh */) |
| : null; |
| Navigatable navigatable = issue.getNavigatable(); |
| if (navigatable == null && file != null) { |
| // convert 1-indexed line/column numbers to 0-indexed |
| navigatable = |
| new OpenFileDescriptor(myProject, file, issue.getLine() - 1, issue.getColumn() - 1); |
| } |
| final IssueOutput.Category category = issue.getCategory(); |
| final int type = translateCategory(category); |
| final String[] text = convertMessage(issue); |
| final String groupName = file != null ? file.getPresentableUrl() : category.name(); |
| addMessage( |
| type, |
| text, |
| groupName, |
| navigatable, |
| getExportTextPrefix(issue), |
| getRenderTextPrefix(issue), |
| sessionId); |
| } |
| |
| private static int translateCategory(IssueOutput.Category category) { |
| switch (category) { |
| case ERROR: |
| return MessageCategory.ERROR; |
| case WARNING: |
| return MessageCategory.WARNING; |
| case STATISTICS: |
| return MessageCategory.STATISTICS; |
| case INFORMATION: |
| return MessageCategory.INFORMATION; |
| default: |
| LOG.error("Unknown message category: " + category); |
| return 0; |
| } |
| } |
| |
| private static String[] convertMessage(final IssueOutput issue) { |
| String text = issue.getMessage(); |
| if (!text.contains("\n")) { |
| return new String[] {text}; |
| } |
| final List<String> lines = new ArrayList<String>(); |
| StringTokenizer tokenizer = new StringTokenizer(text, "\n", false); |
| while (tokenizer.hasMoreTokens()) { |
| lines.add(tokenizer.nextToken()); |
| } |
| return ArrayUtil.toStringArray(lines); |
| } |
| |
| private static String getExportTextPrefix(IssueOutput issue) { |
| int line = issue.getLine(); |
| if (line >= 0) { |
| return String.format("line: %d", line); |
| } |
| return ""; |
| } |
| |
| private static String getRenderTextPrefix(IssueOutput issue) { |
| int line = issue.getLine(); |
| if (line >= 0) { |
| return String.format("(%d, %d)", line, issue.getColumn()); |
| } |
| return ""; |
| } |
| |
| @Override |
| public void clearOldMessages(@NotNull final UUID currentSessionId) { |
| myViewUpdater.execute( |
| new Runnable() { |
| @Override |
| public void run() { |
| cleanupChildrenRecursively( |
| myPanel.getErrorViewStructure().getRootElement(), currentSessionId); |
| updateIcon(); |
| myPanel.reload(); |
| } |
| }); |
| } |
| |
| private void cleanupChildrenRecursively( |
| @NotNull final Object fromElement, @NotNull UUID currentSessionId) { |
| final ErrorViewStructure structure = myPanel.getErrorViewStructure(); |
| for (ErrorTreeElement element : structure.getChildElements(fromElement)) { |
| if (element instanceof GroupingElement) { |
| if (!currentSessionId.equals(element.getData())) { |
| structure.removeElement(element); |
| } else { |
| cleanupChildrenRecursively(element, currentSessionId); |
| } |
| } else { |
| if (!currentSessionId.equals(element.getData())) { |
| structure.removeElement(element); |
| } |
| } |
| } |
| } |
| |
| public void addMessage( |
| final int type, |
| @NotNull final String[] text, |
| @Nullable final String groupName, |
| @Nullable final Navigatable navigatable, |
| @Nullable final String exportTextPrefix, |
| @Nullable final String rendererTextPrefix, |
| @Nullable final UUID sessionId) { |
| |
| myViewUpdater.execute( |
| () -> { |
| final ErrorViewStructure structure = myPanel.getErrorViewStructure(); |
| final GroupingElement group = structure.lookupGroupingElement(groupName); |
| if (group != null && sessionId != null && !sessionId.equals(group.getData())) { |
| structure.removeElement(group); |
| } |
| if (navigatable != null) { |
| myPanel.addMessage( |
| type, |
| text, |
| groupName, |
| navigatable, |
| exportTextPrefix, |
| rendererTextPrefix, |
| sessionId); |
| } else { |
| myPanel.addMessage(type, text, null, -1, -1, sessionId); |
| } |
| updateIcon(); |
| }); |
| } |
| |
| private void updateIcon() { |
| UIUtil.invokeLaterIfNeeded( |
| () -> { |
| if (!myProject.isDisposed()) { |
| final ToolWindow tw = |
| ToolWindowManager.getInstance(myProject) |
| .getToolWindow(BLAZE_PROBLEMS_TOOLWINDOW_ID); |
| if (tw != null) { |
| final boolean active = myPanel.getErrorViewStructure().hasMessages(ALL_MESSAGE_KINDS); |
| tw.setIcon(active ? myActiveIcon : myPassiveIcon); |
| if (active) { |
| tw.show(null); |
| } |
| } |
| } |
| }); |
| } |
| |
| public void setProgress(String text, float fraction) { |
| myPanel.setProgress(text, fraction); |
| } |
| |
| public void setProgress(String text) { |
| myPanel.setProgressText(text); |
| } |
| |
| public void clearProgress() { |
| myPanel.clearProgressData(); |
| } |
| } |