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 2f6ceacc7b10..4b9a146a7ee4 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/DebuggerSessionTab.java @@ -292,7 +292,7 @@ public class DebuggerSessionTab extends DebuggerSessionTabBase implements Dispos } console.setActions(consoleActions, ActionPlaces.DEBUGGER_TOOLBAR, myConsole.getPreferredFocusableComponent()); - initLogConsoles(myConfiguration, myRunContentDescriptor.getProcessHandler()); + initLogConsoles(myConfiguration, myRunContentDescriptor.getProcessHandler(), myConsole); DefaultActionGroup group = new DefaultActionGroup(); final Executor executor = DefaultDebugExecutor.getDebugExecutorInstance(); diff --git a/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java b/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java index 1cf1c0873c8b..b7c8d435dbb5 100644 --- a/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java +++ b/platform/lang-api/src/com/intellij/execution/configurations/RunConfigurationBase.java @@ -41,9 +41,16 @@ public abstract class RunConfigurationBase extends UserDataHolderBase implements private ArrayList myLogFiles = new ArrayList(); private ArrayList myPredefinedLogFiles = new ArrayList(); + @NonNls private static final String LOG_FILE = "log_file"; @NonNls private static final String PREDEFINED_LOG_FILE_ELEMENT = "predefined_log_file"; + @NonNls private static final String FILE_OUTPUT = "output_file"; + @NonNls private static final String SAVE = "is_save"; + @NonNls private static final String OUTPUT_FILE = "path"; + private final Icon myIcon; + private boolean mySaveOutput = false; + private String myFileOutputPath = null; protected RunConfigurationBase(final Project project, final ConfigurationFactory factory, final String name) { myProject = project; @@ -97,6 +104,8 @@ public abstract class RunConfigurationBase extends UserDataHolderBase implements final RunConfigurationBase runConfiguration = (RunConfigurationBase)super.clone(); runConfiguration.myLogFiles = new ArrayList(myLogFiles); runConfiguration.myPredefinedLogFiles = new ArrayList(myPredefinedLogFiles); + runConfiguration.myFileOutputPath = myFileOutputPath; + runConfiguration.mySaveOutput = mySaveOutput; copyCopyableDataTo(runConfiguration); return runConfiguration; } @@ -167,6 +176,12 @@ public abstract class RunConfigurationBase extends UserDataHolderBase implements logFile.readExternal((Element)fileElement); myPredefinedLogFiles.add(logFile); } + final Element fileOutputElement = element.getChild(FILE_OUTPUT); + if (fileOutputElement != null) { + myFileOutputPath = fileOutputElement.getAttributeValue(OUTPUT_FILE); + final String isSave = fileOutputElement.getAttributeValue(SAVE); + mySaveOutput = isSave != null && Boolean.parseBoolean(isSave); + } } public void writeExternal(Element element) throws WriteExternalException { @@ -180,6 +195,34 @@ public abstract class RunConfigurationBase extends UserDataHolderBase implements predefinedLogFile.writeExternal(fileElement); element.addContent(fileElement); } + final Element fileOutputPathElement = new Element(FILE_OUTPUT); + if (myFileOutputPath != null) { + fileOutputPathElement.setAttribute(OUTPUT_FILE, myFileOutputPath); + } + fileOutputPathElement.setAttribute(SAVE, String.valueOf(mySaveOutput)); + if (myFileOutputPath != null || mySaveOutput) { + element.addContent(fileOutputPathElement); + } + } + + public boolean isSaveOutputToFile() { + return mySaveOutput; + } + + public void setSaveOutputToFile(boolean redirectOutput) { + mySaveOutput = redirectOutput; + } + + public String getOutputFilePath() { + return myFileOutputPath; + } + + public void setFileOutputPath(String fileOutputPath) { + myFileOutputPath = fileOutputPath; + } + + public boolean collectOutputFromProcessHandler() { + return true; } public boolean excludeCompileBeforeLaunchOption() { diff --git a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.form b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.form index 7c6c49816b2f..46bbd8eb0552 100644 --- a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.form +++ b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.form @@ -1,6 +1,6 @@
- + @@ -14,7 +14,7 @@ - + @@ -52,12 +52,32 @@ - + + + + + + + + + + + + + + + + + + + + + diff --git a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.java b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.java index 3f2509d7fee7..e694c228b26e 100644 --- a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.java +++ b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogConfigurationPanel.java @@ -20,13 +20,18 @@ import com.intellij.diagnostic.DiagnosticBundle; import com.intellij.execution.configurations.LogFileOptions; import com.intellij.execution.configurations.PredefinedLogFile; import com.intellij.execution.configurations.RunConfigurationBase; +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SettingsEditor; +import com.intellij.openapi.ui.TextComponentAccessor; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.BooleanTableCellRenderer; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.TableUtil; +import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.table.TableView; import com.intellij.util.ui.AbstractTableCellEditor; import com.intellij.util.ui.CellEditorComponentWithBrowseButton; @@ -58,6 +63,8 @@ public class LogConfigurationPanel extends Setti private JButton myRemoveButton; private JPanel myScrollPanel; private JButton myEditButton; + private JBCheckBox myRedirectOutputCb; + private TextFieldWithBrowseButton myOutputFile; private final Map myLog2Predefined = new HashMap(); private final List myUnresolvedPredefined = new ArrayList(); @@ -144,6 +151,15 @@ public class LogConfigurationPanel extends Setti scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); myScrollPanel.add(scrollPane, BorderLayout.CENTER); myWholePanel.setPreferredSize(new Dimension(-1, 150)); + myOutputFile.addBrowseFolderListener("Choose File to Save Console Output", "Console output would be saved to the specified file", null, + FileChooserDescriptorFactory.createSingleFileOrFolderDescriptor(), + TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT); + myRedirectOutputCb.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + myOutputFile.setEnabled(myRedirectOutputCb.isSelected()); + } + }); } private void setUpColumnWidth(final JTableHeader tableHeader, final int preferredWidth, int columnIdx) { @@ -220,6 +236,11 @@ public class LogConfigurationPanel extends Setti } } myModel.setItems(list); + final boolean redirectOutputToFile = configuration.isSaveOutputToFile(); + myRedirectOutputCb.setSelected(redirectOutputToFile); + final String fileOutputPath = configuration.getOutputFilePath(); + myOutputFile.setText(fileOutputPath != null ? FileUtil.toSystemDependentName(fileOutputPath) : ""); + myOutputFile.setEnabled(redirectOutputToFile); } protected void applyEditorTo(final RunConfigurationBase configuration) throws ConfigurationException { @@ -244,7 +265,10 @@ public class LogConfigurationPanel extends Setti } for (PredefinedLogFile logFile : myUnresolvedPredefined) { configuration.addPredefinedLogFile(logFile); - } + } + final String text = myOutputFile.getText(); + configuration.setFileOutputPath(StringUtil.isEmpty(text) ? null : FileUtil.toSystemIndependentName(text)); + configuration.setSaveOutputToFile(myRedirectOutputCb.isSelected()); } @NotNull diff --git a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogFilesManager.java b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogFilesManager.java index 63f2c53097c5..e68799b6ba44 100644 --- a/platform/lang-impl/src/com/intellij/diagnostic/logging/LogFilesManager.java +++ b/platform/lang-impl/src/com/intellij/diagnostic/logging/LogFilesManager.java @@ -20,6 +20,7 @@ import com.intellij.execution.configurations.LogFileOptions; import com.intellij.execution.configurations.RunConfigurationBase; import com.intellij.execution.process.ProcessHandler; import com.intellij.openapi.Disposable; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; @@ -35,6 +36,8 @@ import java.util.*; * Date: 01-Feb-2006 */ public class LogFilesManager implements Disposable { + public static final Logger LOG = Logger.getInstance("#" + LogFilesManager.class.getName()); + private static final int UPDATE_INTERVAL = 500; private final Map> myLogFileManagerMap = new LinkedHashMap>(); diff --git a/platform/lang-impl/src/com/intellij/diagnostic/logging/OutputFileUtil.java b/platform/lang-impl/src/com/intellij/diagnostic/logging/OutputFileUtil.java new file mode 100644 index 000000000000..765a6b882f18 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/diagnostic/logging/OutputFileUtil.java @@ -0,0 +1,125 @@ +/* + * 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.diagnostic.logging; + +import com.intellij.execution.configurations.RunConfigurationBase; +import com.intellij.execution.filters.Filter; +import com.intellij.execution.filters.HyperlinkInfo; +import com.intellij.execution.process.ProcessAdapter; +import com.intellij.execution.process.ProcessEvent; +import com.intellij.execution.process.ProcessHandler; +import com.intellij.execution.process.ProcessOutputTypes; +import com.intellij.execution.ui.ConsoleView; +import com.intellij.execution.ui.ExecutionConsole; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.OpenFileDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; + +/** + * User: anna + * Date: 10/20/11 + */ +public class OutputFileUtil { + private static final String CONSOLE_OUTPUT_FILE_MESSAGE = "Console output is saving to: "; + + private OutputFileUtil() { + } + + public static void attachDumpListener(final RunConfigurationBase base, final ProcessHandler startedProcess, ExecutionConsole console) { + if (base.isSaveOutputToFile()) { + final String outputFilePath = base.getOutputFilePath(); + if (outputFilePath != null) { + final String filePath = FileUtil.toSystemDependentName(outputFilePath); + startedProcess.addProcessListener(new ProcessAdapter() { + private PrintStream myOutput; + @Override + public void onTextAvailable(ProcessEvent event, Key outputType) { + if (base.collectOutputFromProcessHandler() && myOutput != null && outputType != ProcessOutputTypes.SYSTEM) { + myOutput.print(event.getText()); + } + } + + @Override + public void startNotified(ProcessEvent event) { + try { + myOutput = new PrintStream(new FileOutputStream(new File(filePath))); + } + catch (FileNotFoundException ignored) { + } + startedProcess.notifyTextAvailable(CONSOLE_OUTPUT_FILE_MESSAGE + filePath + "\n", ProcessOutputTypes.SYSTEM); + } + + @Override + public void processTerminated(ProcessEvent event) { + startedProcess.removeProcessListener(this); + if (myOutput != null) { + myOutput.close(); + } + } + }); + if (console instanceof ConsoleView) { + ((ConsoleView)console).addMessageFilter(new ShowOutputFileFilter()); + } + } + } + } + + private static class ShowOutputFileFilter implements Filter { + @Override + public Result applyFilter(String line, int entireLength) { + if (line.startsWith(CONSOLE_OUTPUT_FILE_MESSAGE)) { + final String filePath = StringUtil.trimEnd(line.substring(CONSOLE_OUTPUT_FILE_MESSAGE.length()), "\n"); + + return new Result(entireLength - filePath.length() - 1, entireLength, new HyperlinkInfo() { + @Override + public void navigate(final Project project) { + final VirtualFile file = + ApplicationManager.getApplication().runWriteAction(new Computable() { + @Nullable + @Override + public VirtualFile compute() { + return LocalFileSystem.getInstance().refreshAndFindFileByPath(FileUtil.toSystemIndependentName(filePath)); + } + }); + + if (file != null) { + file.refresh(false, false); + ApplicationManager.getApplication().runReadAction(new Runnable() { + public void run() { + FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, file), true); + } + }); + } + } + }); + } + return null; + } + } +} diff --git a/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java b/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java index a637cc0668a5..5dc8d10083b1 100644 --- a/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java +++ b/platform/lang-impl/src/com/intellij/execution/runners/RunContentBuilder.java @@ -15,10 +15,7 @@ */ package com.intellij.execution.runners; -import com.intellij.diagnostic.logging.AdditionalTabComponent; -import com.intellij.diagnostic.logging.LogConsoleImpl; -import com.intellij.diagnostic.logging.LogConsoleManager; -import com.intellij.diagnostic.logging.LogFilesManager; +import com.intellij.diagnostic.logging.*; import com.intellij.execution.DefaultExecutionResult; import com.intellij.execution.ExecutionResult; import com.intellij.execution.Executor; @@ -140,6 +137,7 @@ public class RunContentBuilder implements LogConsoleManager, Disposable { } if (profile instanceof RunConfigurationBase) { myManager.initLogConsoles((RunConfigurationBase)profile, myExecutionResult.getProcessHandler()); + OutputFileUtil.attachDumpListener((RunConfigurationBase)profile, myExecutionResult.getProcessHandler(), console); } } MyRunContentDescriptor contentDescriptor = new MyRunContentDescriptor(profile, myExecutionResult, myReuseProhibited, myUi.getComponent(), this); diff --git a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java index 26268edc3731..34f025b606ac 100644 --- a/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java +++ b/platform/smRunner/src/com/intellij/execution/testframework/sm/runner/ui/SMTestRunnerResultsForm.java @@ -112,6 +112,7 @@ public class SMTestRunnerResultsForm extends TestResultsPanel implements TestFra //Create tests common suite root //noinspection HardCodedStringLiteral myTestsRootNode = new SMTestProxy.SMRootTestProxy(); + //todo myTestsRootNode.setOutputFilePath(runConfiguration.getOutputFilePath()); // Fire selection changed and move focus on SHIFT+ENTER //TODO[romeo] improve diff --git a/platform/testRunner/src/com/intellij/execution/testframework/CompositePrintable.java b/platform/testRunner/src/com/intellij/execution/testframework/CompositePrintable.java index 5e8960547fd4..efe0d7bfd6aa 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/CompositePrintable.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/CompositePrintable.java @@ -40,6 +40,7 @@ public class CompositePrintable implements Printable, Disposable { private final PrintablesWrapper myWrapper = new PrintablesWrapper(); protected int myExceptionMark; private int myCurrentSize = 0; + private String myOutputFile = null; private static final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD); public void flush() { @@ -119,6 +120,10 @@ public class CompositePrintable implements Printable, Disposable { myExceptionMark = exceptionMark; } + public void setOutputFilePath(String outputFile) { + myOutputFile = outputFile; + } + private static final Logger LOG = Logger.getInstance("#" + PrintablesWrapper.class.getName()); private class PrintablesWrapper { @@ -167,6 +172,41 @@ public class CompositePrintable implements Printable, Disposable { printable.printOn(myPrinter); } myPrinter.close(); + if (myOutputFile != null) { + PrintStream printStream = null; + try { + printStream = new PrintStream(new FileOutputStream(new File(myOutputFile), true)); + final PrintStream finalPrintStream = printStream; + for (Printable currentPrintable : currentPrintables) { + currentPrintable.printOn(new Printer() { + @Override + public void print(String text, ConsoleViewContentType contentType) { + if (contentType != ConsoleViewContentType.SYSTEM_OUTPUT) { + finalPrintStream.print(text); + } + } + + @Override + public void printHyperlink(String text, HyperlinkInfo info) { + finalPrintStream.print(text); + } + + @Override + public void onNewAvailable(@NotNull Printable printable) {} + @Override + public void mark() {} + }); + } + } + catch (FileNotFoundException e) { + LOG.error(e); + } + finally { + if (printStream != null) { + printStream.close(); + } + } + } } }; invokeInAlarm(request, ApplicationManager.getApplication().isUnitTestMode()); diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java index 1b5cd0294d10..8d013ef5e41f 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/DebuggerSessionTabBase.java @@ -80,9 +80,10 @@ public abstract class DebuggerSessionTabBase implements DebuggerLogConsoleManage } } - protected void initLogConsoles(final RunProfile runConfiguration, final ProcessHandler processHandler) { + protected void initLogConsoles(final RunProfile runConfiguration, final ProcessHandler processHandler, ExecutionConsole console) { if (runConfiguration instanceof RunConfigurationBase) { myManager.initLogConsoles((RunConfigurationBase)runConfiguration, processHandler); + OutputFileUtil.attachDumpListener((RunConfigurationBase)runConfiguration, processHandler, console); } } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java index d96830595c0b..efd760d40209 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/XDebugSessionTab.java @@ -198,7 +198,7 @@ public class XDebugSessionTab extends DebuggerSessionTabBase { if (env != null) { final RunProfile runConfiguration = env.getRunProfile(); registerFileMatcher(runConfiguration); - initLogConsoles(runConfiguration, myRunContentDescriptor.getProcessHandler()); + initLogConsoles(runConfiguration, myRunContentDescriptor.getProcessHandler(), myConsole); } rebuildViews(); diff --git a/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java b/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java index dd802ac01253..f83067678aa6 100644 --- a/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java +++ b/plugins/junit/src/com/intellij/execution/junit/JUnitConfiguration.java @@ -357,6 +357,11 @@ public class JUnitConfiguration extends ModuleBasedConfiguration