Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move Pants output to console view instead of notification channel #242

Merged
merged 27 commits into from
Jan 13, 2017
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
3 changes: 0 additions & 3 deletions common/com/twitter/intellij/pants/util/PantsUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.intellij.execution.CommonProgramRunConfigurationParameters;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.RunConfiguration;
import com.intellij.execution.process.CapturingProcessHandler;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessOutput;
Expand Down Expand Up @@ -71,7 +69,6 @@
import org.jetbrains.jps.model.library.JpsLibrary;
import org.jetbrains.jps.model.library.impl.sdk.JpsSdkImpl;
import org.jetbrains.jps.model.library.sdk.JpsSdkReference;
import org.jetbrains.plugins.scala.testingSupport.test.AbstractTestRunConfiguration;

import java.io.File;
import java.io.IOException;
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
186 changes: 141 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,21 @@
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.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.util.containers.ContainerUtil;
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 +56,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 +71,9 @@
public class PantsMakeBeforeRun extends ExternalSystemBeforeRunTaskProvider {

public static final Key<ExternalSystemBeforeRunTask> ID = Key.create("Pants.BeforeRunTask");
public static final String ERROR_TAG = "[error]";
public static final Set<String> KNOWN_EXT_LIST = ContainerUtil.newHashSet(".java", ".scala");


public PantsMakeBeforeRun(@NotNull Project project) {
super(PantsConstants.SYSTEM_ID, project, ID);
Expand Down Expand Up @@ -177,7 +184,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 +199,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 +227,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 +243,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 +293,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 +329,124 @@ 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) {
// Scan for any tag related to known file extensions.
for (String ext : KNOWN_EXT_LIST) {
Optional<ParseResult> result = parseErrorLocation(line, ext, ERROR_TAG);
if (result.isPresent() && result.get().getVirtualFile().isPresent()) {

OpenFileHyperlinkInfo linkInfo = new OpenFileHyperlinkInfo(
project,
result.get().getVirtualFile().get(),
result.get().lineNumber - 1, // line number needs to be 0 indexed
result.get().columnNumber - 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 {
String filePath;
int lineNumber;
int columnNumber;

ParseResult(String filePath, int lineNumber, int columnNumber) {
this.filePath = filePath;
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(filePath, other.filePath)
&& Objects.equals(lineNumber, other.lineNumber)
&& Objects.equals(columnNumber, other.columnNumber);
}

public String getFilePath() {
return filePath;
}

public Optional<VirtualFile> getVirtualFile() {
return Optional.ofNullable(VirtualFileManager.getInstance().findFileByUrl("file://" + filePath));
}

public int getLineNumber() {
return lineNumber;
}

public int getColumnNumber() {
return columnNumber;
}
}

/**
* This function parses Pants output against known file extension and tag,
* and returns (Path to mentioned file, line number, column number)
* encapsulated in `ParseResult` object.
*
* @param line original Pants output
* @param ext known file extension. e.g. ".java", ".scala"
* @param tag known tag. e.g. [error]
* @return `ParseResult` instance
*/
public static Optional<ParseResult> parseErrorLocation(String line, String ext, String tag) {
if (!line.contains(ext) || !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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some negative examples from old log

[error] Compile failed at Dec 9, 2016 6:33:30 AM [0.080s]
[error]         Assert.assertEquals("0:00:00.000", ManagementUtils.getAverageAge(d0));

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's okay for String filePath to be Assert.assertEquals("0 for example, because the hyperlink construction will check if it is an actual file. If not, it will there will be no hyperlinks.

I will add more doc to this to be more clear.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More doc added, as well as test case to cover this.

// line number is between first and second colon
int lineNumber = Integer.valueOf(splitByColon[1]);
// column number is between second and thrid colon
int columnNumber = Integer.valueOf(splitByColon[2]);
return Optional.of(new ParseResult(filePath, lineNumber, columnNumber));
}
catch (NumberFormatException e) {
return Optional.empty();
}
}
}
Loading