This is an automated email from the ASF dual-hosted git repository. paulk pushed a commit to branch GROOVY_2_5_X in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push: new e0cf78d GROOVY-8180 Support Log To File for Groovy Console (closes #886) e0cf78d is described below commit e0cf78d449a87b8ce785a4df4b5b8829f5ca346c Author: tttao <big...@gmail.com> AuthorDate: Sun Mar 3 17:18:52 2019 +0100 GROOVY-8180 Support Log To File for Groovy Console (closes #886) --- .../src/main/groovy/groovy/ui/Console.groovy | 98 +++++++++++++++------- .../groovy/groovy/ui/ConsolePreferences.groovy | 44 ++++++++-- .../main/resources/groovy/ui/Console.properties | 3 + 3 files changed, 106 insertions(+), 39 deletions(-) diff --git a/subprojects/groovy-console/src/main/groovy/groovy/ui/Console.groovy b/subprojects/groovy-console/src/main/groovy/groovy/ui/Console.groovy index 0d42d74..24f57ea 100644 --- a/subprojects/groovy-console/src/main/groovy/groovy/ui/Console.groovy +++ b/subprojects/groovy-console/src/main/groovy/groovy/ui/Console.groovy @@ -49,6 +49,7 @@ import javax.swing.event.HyperlinkEvent import javax.swing.event.HyperlinkListener import javax.swing.filechooser.FileFilter import javax.swing.text.AttributeSet +import javax.swing.text.Document import javax.swing.text.Element import javax.swing.text.SimpleAttributeSet import javax.swing.text.Style @@ -132,6 +133,9 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo // Maximum number of characters to show on console at any time int maxOutputChars = System.getProperty('groovy.console.output.limit','20000') as int + // File to output stdout & stderr, in addition to console + PrintWriter outputPrintWriter = null + // UI SwingBuilder swing RootPaneContainer frame @@ -280,6 +284,30 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo consolePreferences.show() } + void setOutputPreferences(boolean useOutputFile, File outputFile) { + if (!useOutputFile) { + closeOutputPrintWriter(outputFile) + } else { + if (outputFile != null) { + closeOutputPrintWriter() + createOutputPrintWriter(outputFile) + } + } + } + + void createOutputPrintWriter(File outputFile) { + outputPrintWriter = new PrintWriter(new FileOutputStream( + outputFile, + true)) + } + + void closeOutputPrintWriter() { + if (outputPrintWriter != null) { + outputPrintWriter.close() + outputPrintWriter = null + } + } + Console() { this(new Binding()) } @@ -449,7 +477,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo // Ensure we don't have too much in console (takes too much memory) private ensureNoDocLengthOverflow(doc) { // if it is a case of stackOverFlowError, show the exception details from the front - // as there is no point in showing the repeating details at the back + // as there is no point in showing the repeating details at the back int offset = stackOverFlowError ? maxOutputChars : 0 if (doc.length > maxOutputChars) { doc.remove(offset, doc.length - maxOutputChars) @@ -459,7 +487,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo // Append a string to the output area void appendOutput(String text, AttributeSet style){ def doc = outputArea.styledDocument - doc.insertString(doc.length, text, style) + insertString(doc, doc.length, text, style) ensureNoDocLengthOverflow(doc) } @@ -514,23 +542,33 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo hrefAttr.addAttribute(HTML.Attribute.HREF, 'file://' + fileNameAndLineNumber) style.addAttribute(HTML.Tag.A, hrefAttr); - doc.insertString(initialLength, line[0..<index], stacktraceStyle) - doc.insertString(initialLength + index, line[index..<(index + length)], style) - doc.insertString(initialLength + index + length, line[(index + length)..-1] + '\n', stacktraceStyle) + insertString(doc, initialLength, line[0..<index], stacktraceStyle) + insertString(doc, initialLength + index, line[index..<(index + length)], style) + insertString(doc, initialLength + index + length, line[(index + length)..-1] + '\n', stacktraceStyle) } else { - doc.insertString(initialLength, line + '\n', stacktraceStyle) + insertString(doc, initialLength, line + '\n', stacktraceStyle) } } ensureNoDocLengthOverflow(doc) } + void insertString(Document doc, int offset, String text, AttributeSet attributeSet) { + doc.insertString(offset, text, attributeSet) + + // Output to file if activated + if (outputPrintWriter != null) { + outputPrintWriter.append(text) + outputPrintWriter.flush() + } + } + // Append a string to the output area on a new line void appendOutputNl(text, style) { def doc = outputArea.styledDocument def len = doc.length def alreadyNewLine = (len == 0 || doc.getText(len - 1, 1) == '\n') - doc.insertString(doc.length, ' \n', style) + insertString(doc, doc.length, ' \n', style) if (alreadyNewLine) { doc.remove(len, 2) // windows hack to fix (improve?) line spacing } @@ -541,7 +579,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo appendOutput(text, style) def doc = outputArea.styledDocument def len = doc.length - doc.insertString(len, ' \n', style) + insertString(doc, len, ' \n', style) doc.remove(len, 2) // windows hack to fix (improve?) line spacing } @@ -583,7 +621,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo captureStdErr = evt.source.selected prefs.putBoolean('captureStdErr', captureStdErr) } - + void fullStackTraces(EventObject evt) { fullStackTraces = evt.source.selected System.setProperty('groovy.full.stacktrace', @@ -718,7 +756,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo consoleController.systemOutInterceptor = systemOutInterceptor consoleController.systemErrorInterceptor = systemErrorInterceptor SwingBuilder swing = new SwingBuilder() - consoleController.swing = swing + consoleController.swing = swing frameConsoleDelegates.each {k, v -> swing[k] = v} swing.controller = consoleController swing.build(ConsoleActions) @@ -809,7 +847,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo int errorLine = se.line String message = se.originalMessage - String scriptFileName = scriptFile?.name ?: DEFAULT_SCRIPT_NAME_START + String scriptFileName = scriptFile?.name ?: DEFAULT_SCRIPT_NAME_START def doc = outputArea.styledDocument @@ -819,15 +857,15 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo hrefAttr.addAttribute(HTML.Attribute.HREF, 'file://' + scriptFileName + ':' + errorLine) style.addAttribute(HTML.Tag.A, hrefAttr); - doc.insertString(doc.length, message + ' at ', stacktraceStyle) - doc.insertString(doc.length, "line: ${se.line}, column: ${se.startColumn}\n\n", style) + insertString(doc, doc.length, message + ' at ', stacktraceStyle) + insertString(doc, doc.length, "line: ${se.line}, column: ${se.startColumn}\n\n", style) } else if (error instanceof Throwable) { reportException(error) } else if (error instanceof ExceptionMessage) { - reportException(error.cause) + reportException(error.cause) } else if (error instanceof SimpleMessage) { def doc = outputArea.styledDocument - doc.insertString(doc.length, "${error.message}\n", new SimpleAttributeSet()) + insertString(doc, doc.length, "${error.message}\n", new SimpleAttributeSet()) } } } else { @@ -839,7 +877,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo } // GROOVY-4496: set the output window position to the top-left so the exception details are visible from the start - outputArea.caretPosition = 0 + outputArea.caretPosition = 0 if (detachedOutput) { prepareOutputWindow() @@ -880,7 +918,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo showOutputWindow() } } - + def compileFinishNormal() { statusLabel.text = 'Compilation complete.' } @@ -1173,7 +1211,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo // set the flag that will be used in printing exception details in output pane stackOverFlowError = true clearOutput() - } + } SwingUtilities.invokeLater { finishException(t, true) } } finally { runThread = null @@ -1219,7 +1257,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo } } } - + def selectFilename(name = 'Open') { def fc = new JFileChooser(currentFileChooserDir) fc.fileSelectionMode = JFileChooser.FILES_ONLY @@ -1305,21 +1343,21 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo void replace(EventObject evt = null) { FindReplaceUtility.showDialog(true) } - + void comment(EventObject evt = null) { def rootElement = inputArea.document.defaultRootElement def cursorPos = inputArea.getCaretPosition() int startRow = rootElement.getElementIndex(cursorPos) int endRow = startRow - + if (inputArea.getSelectedText()) { def selectionStart = inputArea.getSelectionStart() startRow = rootElement.getElementIndex(selectionStart) def selectionEnd = inputArea.getSelectionEnd() endRow = rootElement.getElementIndex(selectionEnd) } - - // If multiple commented lines intermix with uncommented lines, consider them uncommented + + // If multiple commented lines intermix with uncommented lines, consider them uncommented def allCommented = true startRow.upto(endRow) { rowIndex -> def rowElement = rootElement.getElement(rowIndex) @@ -1330,7 +1368,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo allCommented = false } } - + startRow.upto(endRow) { rowIndex -> def rowElement = rootElement.getElement(rowIndex) int startOffset = rowElement.getStartOffset() @@ -1345,7 +1383,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo inputArea.document.insertString(startOffset, "//", new SimpleAttributeSet()) } } - + } void selectBlock(EventObject evt = null) { @@ -1427,7 +1465,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo void showCompilingMessage() { statusLabel.text = 'Script compiling now. Please wait.' } - + // Shows the detached 'outputArea' dialog void showOutputWindow(EventObject evt = null) { if (detachedOutput) { @@ -1471,7 +1509,7 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo } else if (newFontSize < 4) { newFontSize = 4 } - + prefs.putInt('fontSize', newFontSize) // don't worry, the fonts won't be changed to this family, the styles will only derive from this @@ -1575,16 +1613,16 @@ class Console implements CaretListener, HyperlinkListener, ComponentListener, Fo } } - public void componentShown(ComponentEvent e) { } + void componentShown(ComponentEvent e) { } - public void focusGained(FocusEvent e) { + void focusGained(FocusEvent e) { // remember component with focus for text-copy functionality if (e.component == outputArea || e.component == inputArea) { copyFromComponent = e.component } } - public void focusLost(FocusEvent e) { } + void focusLost(FocusEvent e) { } private static boolean isWindows() { return getOsName().startsWith("windows") diff --git a/subprojects/groovy-console/src/main/groovy/groovy/ui/ConsolePreferences.groovy b/subprojects/groovy-console/src/main/groovy/groovy/ui/ConsolePreferences.groovy index 2bc00c7..6e804b8 100644 --- a/subprojects/groovy-console/src/main/groovy/groovy/ui/ConsolePreferences.groovy +++ b/subprojects/groovy-console/src/main/groovy/groovy/ui/ConsolePreferences.groovy @@ -36,6 +36,7 @@ class ConsolePreferences { private final MessageSource T private JDialog dialog + File outputFile ConsolePreferences(console) { this.console = console @@ -63,14 +64,26 @@ class ConsolePreferences { title: T['prefs.dialog.title'], owner: console.frame, modal: true ) { vbox { - hbox(border: titledBorder(T['prefs.output.settings.title'])) { - label "${T['prefs.max.characters.output']}:" - - formattedTextField value: maxOutputChars, id: 'txtMaxOutputChars', - text: - bind(target: this, targetProperty: 'maxOutputChars', - validator: this.&isInteger, converter: Integer.&parseInt), - columns: 6 + vbox(border: titledBorder(T['prefs.output.settings.title'])) { + hbox { + label "${T['prefs.max.characters.output']}:" + + formattedTextField value: maxOutputChars, id: 'txtMaxOutputChars', + text: + bind(target: this, targetProperty: 'maxOutputChars', + validator: this.&isInteger, converter: Integer.&parseInt), + columns: 6 + } + + hbox { + checkBox T['prefs.output.file'], id: 'outputFileCheckBox', selected: false + hglue() + label T['prefs.output.file.name'], id: 'outputFileName', + enabled: bind(source: outputFileCheckBox, sourceProperty: 'selected') + button T['prefs.output.file.select'], id: 'outputFileNameButton', + enabled: bind(source: outputFileCheckBox, sourceProperty: 'selected'), + actionPerformed: this.&onChooseFile + } } vglue() @@ -80,10 +93,11 @@ class ConsolePreferences { hglue() button T['prefs.close'], id: 'closePrefsButton', actionPerformed: this.&onClose } + } } - console.swing.txtMaxOutputChars.maximumSize=new Dimension(Integer.MAX_VALUE, (int) console.swing.txtMaxOutputChars.preferredSize.height) + console.swing.txtMaxOutputChars.maximumSize = new Dimension(Integer.MAX_VALUE, (int) console.swing.txtMaxOutputChars.preferredSize.height) } private boolean isInteger(value) { @@ -106,9 +120,21 @@ class ConsolePreferences { if (maxOutputChars != console.maxOutputChars) { console.maxOutputChars = maxOutputChars } + + console.setOutputPreferences(console.swing.outputFileCheckBox.enabled, outputFile) + dialog.dispose() } + private void onChooseFile(EventObject event) { + JFileChooser fileChooser = console.swing.fileChooser() + + if (fileChooser.showOpenDialog(dialog) == JFileChooser.APPROVE_OPTION) { + outputFile = fileChooser.selectedFile + } + console.swing.outputFileName.text = outputFile.path + } + // Useful for testing gui static void main(args) { javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager.getSystemLookAndFeelClassName()) diff --git a/subprojects/groovy-console/src/main/resources/groovy/ui/Console.properties b/subprojects/groovy-console/src/main/resources/groovy/ui/Console.properties index 4574a51..1ca57f8 100644 --- a/subprojects/groovy-console/src/main/resources/groovy/ui/Console.properties +++ b/subprojects/groovy-console/src/main/resources/groovy/ui/Console.properties @@ -41,4 +41,7 @@ prefs.dialog.title=Preferences prefs.output.settings.title=Output Settings prefs.max.characters.output=Maximum Characters to Output prefs.reset.defaults=Reset To Defaults +prefs.output.file=Output to file +prefs.output.file.select=Select +prefs.output.file.name=<Output file name> prefs.close=Close