Skip to content

Commit

Permalink
Merge branch 'master' into no_compiler_form
Browse files Browse the repository at this point in the history
  • Loading branch information
wisechengyi committed Jan 14, 2017
2 parents fa253e0 + 9cce5de commit c8de1ad
Show file tree
Hide file tree
Showing 9 changed files with 353 additions and 46 deletions.
2 changes: 1 addition & 1 deletion common/com/twitter/intellij/pants/util/PantsConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

public class PantsConstants {
public static final String PANTS = "pants";
public static final String PLUGIN = "pants_plugin";
public static final String PANTS_CONSOLE_NAME = "PantsConsole";
public static final String PLUGIN_ID = "com.intellij.plugins.pants";

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.twitter.intellij.pants.service.project.PantsResolver;
import com.twitter.intellij.pants.settings.PantsProjectSettings;
import com.twitter.intellij.pants.settings.PantsSettings;
import com.twitter.intellij.pants.ui.PantsConsoleManager;
import com.twitter.intellij.pants.util.PantsConstants;
import com.twitter.intellij.pants.util.PantsUtil;
import icons.PantsIcons;
Expand All @@ -46,12 +47,14 @@ protected PantsProjectComponentImpl(Project project) {
public void projectClosed() {
PantsMetrics.report();
FileChangeTracker.unregisterProject(myProject);
PantsConsoleManager.unregisterConsole(myProject);
super.projectClosed();
}

@Override
public void projectOpened() {
PantsMetrics.initialize();
PantsConsoleManager.registerConsole(myProject);
super.projectOpened();
if (myProject.isDefault()) {
return;
Expand Down
181 changes: 136 additions & 45 deletions src/com/twitter/intellij/pants/execution/PantsMakeBeforeRun.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.configurations.RunProfileWithCompileBeforeLaunchOption;
import com.intellij.execution.filters.Filter;
import com.intellij.execution.filters.OpenFileHyperlinkInfo;
import com.intellij.execution.impl.RunManagerImpl;
import com.intellij.execution.process.CapturingAnsiEscapesAwareProcessHandler;
import com.intellij.execution.process.CapturingProcessHandler;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.execution.ui.ConsoleViewContentType;
import com.intellij.notification.Notification;
import com.intellij.notification.NotificationType;
import com.intellij.notification.Notifications;
Expand All @@ -25,22 +29,20 @@
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemBeforeRunTask;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemBeforeRunTaskProvider;
import com.intellij.openapi.externalSystem.service.notification.ExternalSystemNotificationManager;
import com.intellij.openapi.externalSystem.service.notification.NotificationCategory;
import com.intellij.openapi.externalSystem.service.notification.NotificationData;
import com.intellij.openapi.externalSystem.service.notification.NotificationSource;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindowManager;
import com.twitter.intellij.pants.PantsBundle;
import com.twitter.intellij.pants.file.FileChangeTracker;
import com.twitter.intellij.pants.model.PantsOptions;
import com.twitter.intellij.pants.settings.PantsSettings;
import com.twitter.intellij.pants.ui.PantsConsoleManager;
import com.twitter.intellij.pants.util.PantsConstants;
import com.twitter.intellij.pants.util.PantsUtil;
import icons.PantsIcons;
Expand All @@ -53,6 +55,7 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

Expand All @@ -67,6 +70,8 @@
public class PantsMakeBeforeRun extends ExternalSystemBeforeRunTaskProvider {

public static final Key<ExternalSystemBeforeRunTask> ID = Key.create("Pants.BeforeRunTask");
public static final String ERROR_TAG = "[error]";


public PantsMakeBeforeRun(@NotNull Project project) {
super(PantsConstants.SYSTEM_ID, project, ID);
Expand Down Expand Up @@ -177,7 +182,7 @@ public Pair<Boolean, Optional<String>> executeTask(Project currentProject, Set<S

prepareIDE(currentProject);
if (targetAddressesToCompile.isEmpty()) {
showPantsMakeTaskMessage("No target found in configuration.", NotificationCategory.INFO, currentProject);
showPantsMakeTaskMessage("No target found in configuration.\n", ConsoleViewContentType.SYSTEM_OUTPUT, currentProject);
return Pair.create(true, Optional.empty());
}

Expand All @@ -192,10 +197,10 @@ public Pair<Boolean, Optional<String>> executeTask(Project currentProject, Set<S
}
final GeneralCommandLine commandLine = PantsUtil.defaultCommandLine(pantsExecutable.get().getPath());

showPantsMakeTaskMessage("Checking Pants options...", NotificationCategory.INFO, currentProject);
showPantsMakeTaskMessage("Checking Pants options...\n", ConsoleViewContentType.SYSTEM_OUTPUT, currentProject);
Optional<PantsOptions> pantsOptional = PantsOptions.getPantsOptions(currentProject);
if (!pantsOptional.isPresent()) {
showPantsMakeTaskMessage("Pants Options not found.", NotificationCategory.ERROR, currentProject);
showPantsMakeTaskMessage("Pants Options not found.\n", ConsoleViewContentType.ERROR_OUTPUT, currentProject);
return Pair.create(false, Optional.empty());
}

Expand All @@ -220,7 +225,7 @@ public Pair<Boolean, Optional<String>> executeTask(Project currentProject, Set<S
commandLine.addParameter(PantsUtil.getJvmDistributionPathParameter(PantsUtil.getJdkPathFromIntelliJCore()));
}
catch (Exception e) {
showPantsMakeTaskMessage(e.getMessage(), NotificationCategory.ERROR, currentProject);
showPantsMakeTaskMessage(e.getMessage(), ConsoleViewContentType.ERROR_OUTPUT, currentProject);
return Pair.create(false, Optional.empty());
}
}
Expand All @@ -236,17 +241,17 @@ public Pair<Boolean, Optional<String>> executeTask(Project currentProject, Set<S
process = commandLine.createProcess();
}
catch (ExecutionException e) {
showPantsMakeTaskMessage(e.getMessage(), NotificationCategory.ERROR, currentProject);
showPantsMakeTaskMessage(e.getMessage(), ConsoleViewContentType.ERROR_OUTPUT, currentProject);
return Pair.create(false, Optional.empty());
}

final CapturingProcessHandler processHandler = new CapturingAnsiEscapesAwareProcessHandler(process, commandLine.getCommandLineString());
addMessageHandler(processHandler, currentProject);
final List<String> output = new ArrayList<>();
processHandler.addProcessListener(new ProcessAdapter() {
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
super.onTextAvailable(event, outputType);
showPantsMakeTaskMessage(event.getText(), ConsoleViewContentType.NORMAL_OUTPUT, currentProject);
output.add(event.getText());
}
});
Expand Down Expand Up @@ -286,41 +291,15 @@ private void prepareIDE(Project project) {
@Override
public void run() {
/* Clear message window. */
ExternalSystemNotificationManager.getInstance(project)
.clearNotifications(NotificationSource.TASK_EXECUTION, PantsConstants.SYSTEM_ID);
ConsoleView executionConsole = PantsConsoleManager.getOrMakeNewConsole(project);
executionConsole.getComponent().setVisible(true);
executionConsole.clear();
ToolWindowManager.getInstance(project).getToolWindow(PantsConstants.PANTS_CONSOLE_NAME).activate(null);
/* Force cached changes to disk. */
FileDocumentManager.getInstance().saveAllDocuments();
project.save();
}
}, ModalityState.NON_MODAL);

ExternalSystemNotificationManager.getInstance(project).openMessageView(PantsConstants.SYSTEM_ID, NotificationSource.TASK_EXECUTION);
}

private void addMessageHandler(CapturingProcessHandler processHandler, Project project) {
processHandler.addProcessListener(
new ProcessAdapter() {
@Override
public void onTextAvailable(ProcessEvent event, Key outputType) {
super.onTextAvailable(event, outputType);
String output = event.getText();
if (StringUtil.isEmptyOrSpaces(output)) {
return;
}
NotificationCategory notificationCategory = NotificationCategory.INFO;
if (output.contains("[warn]")) {
notificationCategory = NotificationCategory.WARNING;
}
else if (output.contains("[error]")) {
notificationCategory = NotificationCategory.ERROR;
}

NotificationData notification =
new NotificationData(PantsConstants.PANTS, output, notificationCategory, NotificationSource.TASK_EXECUTION);
ExternalSystemNotificationManager.getInstance(project).showNotification(PantsConstants.SYSTEM_ID, notification);
}
}
);
}

@NotNull
Expand Down Expand Up @@ -348,9 +327,121 @@ private Set<String> getTargetAddressesToCompile(Module[] targetModules) {
return result;
}

private void showPantsMakeTaskMessage(String message, NotificationCategory type, Project project) {
NotificationData notification =
new NotificationData(PantsConstants.PANTS, message, type, NotificationSource.TASK_EXECUTION);
ExternalSystemNotificationManager.getInstance(project).showNotification(PantsConstants.SYSTEM_ID, notification);

private void showPantsMakeTaskMessage(String message, ConsoleViewContentType type, Project project) {
ConsoleView executionConsole = PantsConsoleManager.getOrMakeNewConsole(project);
// Create a filter that monitors console outputs, and turns them into a hyperlink if applicable.
Filter filter = new Filter() {
@Nullable
@Override
public Result applyFilter(String line, int entireLength) {
Optional<ParseResult> result = ParseResult.parseErrorLocation(line, ERROR_TAG);
if (result.isPresent()) {

OpenFileHyperlinkInfo linkInfo = new OpenFileHyperlinkInfo(
project,
result.get().getFile(),
result.get().getLineNumber() - 1, // line number needs to be 0 indexed
result.get().getColumnNumber() - 1 // column number needs to be 0 indexed
);
int startHyperlink = entireLength - line.length() + line.indexOf(ERROR_TAG);

return new Result(
startHyperlink,
entireLength,
linkInfo,
null // TextAttributes, going with default hence null
);
}
return null;
}
};

ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
executionConsole.addMessageFilter(filter);
executionConsole.print(message, type);
}
}, ModalityState.NON_MODAL);
}

/**
* Encapsulate the result of parsed data.
*/
static class ParseResult {
private VirtualFile file;
private int lineNumber;
private int columnNumber;


/**
* This function parses Pants output against known file and tag,
* and returns (file, line number, column number)
* encapsulated in `ParseResult` object if the output contains valid information.
*
* @param line original Pants output
* @param tag known tag. e.g. [error]
* @return `ParseResult` instance
*/
public static Optional<ParseResult> parseErrorLocation(String line, String tag) {
if (!line.contains(tag)) {
return Optional.empty();
}

String[] splitByColon = line.split(":");
if (splitByColon.length < 3) {
return Optional.empty();
}

try {
// filePath path is between tag and first colon
String filePath = splitByColon[0].substring(splitByColon[0].indexOf(tag) + tag.length()).trim();
VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(filePath);
if (virtualFile == null) {
return Optional.empty();
}
// line number is between first and second colon
int lineNumber = Integer.valueOf(splitByColon[1]);
// column number is between second and third colon
int columnNumber = Integer.valueOf(splitByColon[2]);
return Optional.of(new ParseResult(virtualFile, lineNumber, columnNumber));
}
catch (NumberFormatException e) {
return Optional.empty();
}
}

private ParseResult(VirtualFile file, int lineNumber, int columnNumber) {
this.file = file;
this.lineNumber = lineNumber;
this.columnNumber = columnNumber;
}

@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ParseResult other = (ParseResult) obj;
return Objects.equals(file, other.file)
&& Objects.equals(lineNumber, other.lineNumber)
&& Objects.equals(columnNumber, other.columnNumber);
}

public VirtualFile getFile() {
return file;
}

public int getLineNumber() {
return lineNumber;
}

public int getColumnNumber() {
return columnNumber;
}
}
}
78 changes: 78 additions & 0 deletions src/com/twitter/intellij/pants/ui/PantsConsoleManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2017 Pants project contributors (see CONTRIBUTORS.md).
// Licensed under the Apache License, Version 2.0 (see LICENSE).

package com.twitter.intellij.pants.ui;

import com.intellij.execution.filters.TextConsoleBuilderFactory;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.ui.content.impl.TabbedContentImpl;
import com.twitter.intellij.pants.util.PantsConstants;
import org.jetbrains.annotations.TestOnly;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


public class PantsConsoleManager {
private static ConcurrentHashMap<Project, ConsoleView> mapper = new ConcurrentHashMap<>();

public static void registerConsole(Project project) {
ToolWindow window =
ToolWindowManager.getInstance(project).registerToolWindow(
PantsConstants.PANTS_CONSOLE_NAME,
true,
ToolWindowAnchor.BOTTOM,
project,
true
);
ConsoleView console = getOrMakeNewConsole(project);
Disposer.register(project, console);
TabbedContentImpl content = new TabbedContentImpl(console.getComponent(), "", true, "");
window.getContentManager().addContent(content);
}

/**
* Creates a `ConsoleView` for the current project, and register it under `PantsConsole` tool window,
* or just retrieve one if there is already one registered.
*
* @param project current project
* @return Pants ConsoleView for the project
*/
public static ConsoleView getOrMakeNewConsole(Project project) {
ConsoleView console = mapper.get(project);
if (console != null) {
return console;
}
ConsoleView newConsole = TextConsoleBuilderFactory.getInstance().createBuilder(project).getConsole();
mapper.put(project, newConsole);
return newConsole;
}

/**
* Close the console for a project.
*
* @param project current project
*/
public static void unregisterConsole(Project project) {
ConsoleView console = mapper.get(project);
if (console != null) {
console.dispose();
}
mapper.remove(project);
}

/**
* TestOnly because some test library is not tearing down properly.
*/
@TestOnly
public static void disposeAll() {
for (Map.Entry<Project, ConsoleView> entrySet : mapper.entrySet()) {
entrySet.getValue().dispose();
}
}
}
Loading

0 comments on commit c8de1ad

Please sign in to comment.