mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-03 09:49:23 +02:00
GP-5970 - Scripting - A print/println methods to color console output
This commit is contained in:
parent
6eab6693fc
commit
73bdee2546
13 changed files with 501 additions and 119 deletions
|
@ -17,7 +17,7 @@ package ghidra.app.plugin.core.console;
|
|||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.*;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.text.*;
|
||||
|
@ -29,6 +29,7 @@ import docking.widgets.FindDialog;
|
|||
import docking.widgets.TextComponentSearcher;
|
||||
import generic.theme.GIcon;
|
||||
import generic.theme.Gui;
|
||||
import ghidra.app.script.DecoratingPrintWriter;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.main.ConsoleTextPane;
|
||||
|
@ -95,8 +96,8 @@ public class ConsoleComponentProvider extends ComponentProviderAdapter implement
|
|||
}
|
||||
|
||||
void init() {
|
||||
stderr = new PrintWriter(new ConsoleWriter(this, true));
|
||||
stdin = new PrintWriter(new ConsoleWriter(this, false));
|
||||
stderr = new ConsolePrintWriter(true);
|
||||
stdin = new ConsolePrintWriter(false);
|
||||
|
||||
/* call this before build() -- we get our Font here */
|
||||
setVisible(true);
|
||||
|
@ -230,6 +231,11 @@ public class ConsoleComponentProvider extends ComponentProviderAdapter implement
|
|||
textPane.addPartialMessage(msg);
|
||||
}
|
||||
|
||||
public void print(String msg, Color c) {
|
||||
checkVisible();
|
||||
textPane.addPartialMessage(msg, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void printError(String errmsg) {
|
||||
checkVisible();
|
||||
|
@ -330,6 +336,84 @@ public class ConsoleComponentProvider extends ComponentProviderAdapter implement
|
|||
// Inner Classes
|
||||
//=================================================================================================
|
||||
|
||||
private class ConsolePrintWriter extends DecoratingPrintWriter {
|
||||
|
||||
private ColoringConsoleWriter writer;
|
||||
|
||||
ConsolePrintWriter(boolean error) {
|
||||
this(new ColoringConsoleWriter(error));
|
||||
}
|
||||
|
||||
private ConsolePrintWriter(ColoringConsoleWriter writer) {
|
||||
super(writer);
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(String s, Color c) {
|
||||
try {
|
||||
writer.setColor(c);
|
||||
print(s);
|
||||
println();
|
||||
}
|
||||
finally {
|
||||
writer.setColor(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void print(String s, Color c) {
|
||||
try {
|
||||
writer.setColor(c);
|
||||
print(s);
|
||||
}
|
||||
finally {
|
||||
writer.setColor(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ColoringConsoleWriter extends Writer {
|
||||
|
||||
private Color color;
|
||||
private boolean error;
|
||||
|
||||
public ColoringConsoleWriter(boolean error) {
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
void setColor(Color color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char[] cbuf, int off, int len) throws IOException {
|
||||
String s = new String(cbuf, off, len);
|
||||
if (error) {
|
||||
printError(s);
|
||||
return;
|
||||
}
|
||||
|
||||
if (color == null) {
|
||||
print(s);
|
||||
return;
|
||||
}
|
||||
|
||||
print(s, color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
clearMessages();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class GoToMouseListener extends MouseAdapter {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* 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 ghidra.app.plugin.core.console;
|
||||
|
||||
import ghidra.app.services.ConsoleService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
class ConsoleWriter extends Writer {
|
||||
private ConsoleService console;
|
||||
private boolean error;
|
||||
|
||||
ConsoleWriter(ConsoleService console, boolean error) {
|
||||
super();
|
||||
this.console = console;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.Writer#close()
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
console.clearMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.Writer#flush()
|
||||
*/
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.io.Writer#write(char[], int, int)
|
||||
*/
|
||||
@Override
|
||||
public void write(char[] cbuf, int off, int len) throws IOException {
|
||||
String str = new String(cbuf, off, len);
|
||||
if (error) {
|
||||
console.printError(str);
|
||||
}
|
||||
else {
|
||||
console.print(str);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ import ghidra.app.plugin.core.console.CodeCompletion;
|
|||
*/
|
||||
public class CodeCompletionWindow extends JDialog {
|
||||
|
||||
private static final String FONT_ID = "font.plugin.terminal.completion.list";
|
||||
static final String FONT_ID = "font.plugin.terminal.completion.list";
|
||||
|
||||
protected final InterpreterPanel console;
|
||||
protected final JTextPane outputTextField;
|
||||
|
@ -394,6 +394,10 @@ class CodeCompletionListSelectionModel extends DefaultListSelectionModel {
|
|||
*/
|
||||
class CodeCompletionListCellRenderer extends GListCellRenderer<CodeCompletion> {
|
||||
|
||||
CodeCompletionListCellRenderer() {
|
||||
setBaseFontId(CodeCompletionWindow.FONT_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getItemText(CodeCompletion value) {
|
||||
return value.getDescription();
|
||||
|
@ -421,7 +425,6 @@ class CodeCompletionListCellRenderer extends GListCellRenderer<CodeCompletion> {
|
|||
}
|
||||
|
||||
component.setEnabled(list.isEnabled());
|
||||
component.setFont(list.getFont());
|
||||
component.setComponentOrientation(list.getComponentOrientation());
|
||||
Border border = null;
|
||||
if (cellHasFocus) {
|
||||
|
|
|
@ -26,11 +26,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
import javax.swing.*;
|
||||
import javax.swing.text.*;
|
||||
|
||||
import org.apache.commons.io.output.WriterOutputStream;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.actions.KeyBindingUtils;
|
||||
import generic.theme.*;
|
||||
import generic.util.WindowUtilities;
|
||||
import ghidra.app.plugin.core.console.CodeCompletion;
|
||||
import ghidra.app.script.DecoratingPrintWriter;
|
||||
import ghidra.framework.options.OptionsChangeListener;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
|
@ -69,8 +72,12 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
|||
/* junit */ IPStdin stdin;
|
||||
private OutputStream stdout;
|
||||
private OutputStream stderr;
|
||||
private PrintWriter outWriter;
|
||||
private PrintWriter errWriter;
|
||||
private InterpreterPrintWriter outWriter;
|
||||
private InterpreterPrintWriter errWriter;
|
||||
|
||||
private AnsiRenderer stdErrRenderer = new AnsiRenderer();
|
||||
private AnsiRenderer stdInRenderer = new AnsiRenderer();
|
||||
private AnsiRenderer stdOutRenderer = new AnsiRenderer();
|
||||
|
||||
private SimpleAttributeSet STDOUT_SET;
|
||||
private SimpleAttributeSet STDERR_SET;
|
||||
|
@ -129,11 +136,12 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
|||
outputScrollPane.setFocusable(false);
|
||||
promptTextPane.setFocusable(false);
|
||||
|
||||
outWriter = new InterpreterPrintWriter(TextType.STDOUT);
|
||||
errWriter = new InterpreterPrintWriter(TextType.STDERR);
|
||||
|
||||
stdin = new IPStdin();
|
||||
stdout = new IPOut(TextType.STDOUT);
|
||||
stderr = new IPOut(TextType.STDERR);
|
||||
outWriter = new PrintWriter(stdout, true);
|
||||
errWriter = new PrintWriter(stderr, true);
|
||||
stdout = outWriter.asOutputStream();
|
||||
stderr = errWriter.asOutputStream();
|
||||
|
||||
outputTextPane.setEditable(false);
|
||||
promptTextPane.setEditable(false);
|
||||
|
@ -270,7 +278,6 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
|||
private void updateFontAttributes(Font font) {
|
||||
Font boldFont = font.deriveFont(Font.BOLD);
|
||||
|
||||
STDOUT_SET = new GAttributes(font, NORMAL_COLOR);
|
||||
STDOUT_SET = new GAttributes(font, NORMAL_COLOR);
|
||||
STDERR_SET = new GAttributes(font, ERROR_COLOR);
|
||||
STDIN_SET = new GAttributes(boldFont, NORMAL_COLOR);
|
||||
|
@ -410,11 +417,7 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
|||
outputTextPane.setCaretPosition(Math.max(0, outputTextPane.getDocument().getLength()));
|
||||
}
|
||||
|
||||
AnsiRenderer stdErrRenderer = new AnsiRenderer();
|
||||
AnsiRenderer stdInRenderer = new AnsiRenderer();
|
||||
AnsiRenderer stdOutRenderer = new AnsiRenderer();
|
||||
|
||||
void addText(String text, TextType type) {
|
||||
private void addText(String text, TextType type) {
|
||||
SimpleAttributeSet attributes;
|
||||
AnsiRenderer renderer;
|
||||
switch (type) {
|
||||
|
@ -438,29 +441,23 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
|||
repositionScrollpane();
|
||||
}
|
||||
catch (BadLocationException e) {
|
||||
Msg.error(this, "internal document positioning error", e);
|
||||
// shouldn't happen
|
||||
Msg.error(this, "Document positioning error", e);
|
||||
}
|
||||
}
|
||||
|
||||
private class IPOut extends OutputStream {
|
||||
TextType type;
|
||||
byte[] buffer = new byte[1];
|
||||
private void addText(String text, Color c) {
|
||||
|
||||
IPOut(TextType type) {
|
||||
this.type = type;
|
||||
SimpleAttributeSet attributes = new GAttributes(getFont(), c);
|
||||
|
||||
try {
|
||||
StyledDocument document = outputTextPane.getStyledDocument();
|
||||
stdOutRenderer.renderString(document, text, attributes);
|
||||
repositionScrollpane();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
buffer[0] = (byte) b;
|
||||
String text = new String(buffer);
|
||||
addText(text, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
String text = new String(b, off, len);
|
||||
addText(text, type);
|
||||
catch (BadLocationException e) {
|
||||
// shouldn't happen
|
||||
Msg.error(this, "Document positioning error", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -565,6 +562,102 @@ public class InterpreterPanel extends JPanel implements OptionsChangeListener {
|
|||
// Inner Classes
|
||||
//==================================================================================================
|
||||
|
||||
private class InterpreterPrintWriter extends DecoratingPrintWriter {
|
||||
|
||||
private InterpreterConsoleWriter writer;
|
||||
|
||||
InterpreterPrintWriter(TextType type) {
|
||||
this(new InterpreterConsoleWriter(type));
|
||||
}
|
||||
|
||||
private InterpreterPrintWriter(InterpreterConsoleWriter writer) {
|
||||
super(writer);
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
OutputStream asOutputStream() {
|
||||
try {
|
||||
return WriterOutputStream.builder().setWriter(writer).getOutputStream();
|
||||
}
|
||||
catch (IOException e) {
|
||||
Msg.error(this, "Unable to create output stream", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void println(String s, Color c) {
|
||||
try {
|
||||
writer.setColor(c);
|
||||
print(s);
|
||||
println();
|
||||
}
|
||||
finally {
|
||||
writer.setColor(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void print(String s, Color c) {
|
||||
try {
|
||||
writer.setColor(c);
|
||||
print(s);
|
||||
}
|
||||
finally {
|
||||
writer.setColor(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InterpreterConsoleWriter extends Writer {
|
||||
|
||||
private Color color;
|
||||
|
||||
TextType type;
|
||||
byte[] buffer = new byte[1];
|
||||
|
||||
public InterpreterConsoleWriter(TextType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
void setColor(Color color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
buffer[0] = (byte) b;
|
||||
String text = new String(buffer);
|
||||
if (color != null) {
|
||||
addText(text, color);
|
||||
return;
|
||||
}
|
||||
|
||||
addText(text, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(char[] b, int off, int len) throws IOException {
|
||||
String text = new String(b, off, len);
|
||||
if (color != null) {
|
||||
addText(text, color);
|
||||
return;
|
||||
}
|
||||
|
||||
addText(text, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
// stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link InputStream} that has as its source text strings being pushed into
|
||||
* it by a thread, and being read by another thread.
|
||||
|
|
|
@ -487,7 +487,6 @@ public class GhidraScriptEditorComponentProvider extends ComponentProvider {
|
|||
// this will overwrite any changes--be sure to resolve that before calling this method!
|
||||
try {
|
||||
loadScript(scriptSourceFile);
|
||||
fileHash = MD5Utilities.getMD5Hash(scriptSourceFile.getFile(false));
|
||||
clearChanges();
|
||||
refreshAction();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* 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 ghidra.app.script;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* A print writer that allows clients to specify the text color.
|
||||
*/
|
||||
public abstract class DecoratingPrintWriter extends PrintWriter {
|
||||
|
||||
public DecoratingPrintWriter(Writer out) {
|
||||
super(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a line of text with the given color.
|
||||
* @param s the text
|
||||
* @param c the color
|
||||
*/
|
||||
public abstract void println(String s, Color c);
|
||||
|
||||
/**
|
||||
* Print text with the given color.
|
||||
* @param s the text
|
||||
* @param c the color
|
||||
*/
|
||||
public abstract void print(String s, Color c);
|
||||
}
|
|
@ -1042,6 +1042,58 @@ public abstract class GhidraScript extends FlatProgramAPI {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the {@link #decorateOutput optionally} {@link #decorate(String) decorated} message
|
||||
* followed by a line feed to this script's {@code stdout} {@link PrintWriter}, which is set by
|
||||
* {@link #set(GhidraState, ScriptControls)}.
|
||||
* <p>
|
||||
* Additionally, the always {@link #decorate(String) decorated} message is written to Ghidra's
|
||||
* log.
|
||||
*
|
||||
* @param message the message to print
|
||||
* @param color the color for the text
|
||||
*/
|
||||
public void println(String message, Color color) {
|
||||
|
||||
String decoratedMessage = decorate(message);
|
||||
|
||||
Msg.info(GhidraScript.class, new ScriptMessage(decoratedMessage));
|
||||
|
||||
if (writer instanceof DecoratingPrintWriter scriptWriter) {
|
||||
scriptWriter.println(decorateOutput ? decoratedMessage : message, color);
|
||||
return;
|
||||
}
|
||||
|
||||
if (writer != null) {
|
||||
writer.println(decorateOutput ? decoratedMessage : message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the undecorated message with no newline to this script's {@code stdout}
|
||||
* {@link PrintWriter}, which is set by {@link #set(GhidraState, ScriptControls)}.
|
||||
* <p>
|
||||
* Additionally, the undecorated message is written to Ghidra's log.
|
||||
*
|
||||
* @param message the message to print
|
||||
* @param color the color for the text
|
||||
*/
|
||||
public void print(String message, Color color) {
|
||||
|
||||
String decoratedMessage = decorate(message);
|
||||
|
||||
Msg.info(GhidraScript.class, new ScriptMessage(decoratedMessage));
|
||||
|
||||
if (writer instanceof DecoratingPrintWriter scriptWriter) {
|
||||
scriptWriter.print(decorateOutput ? decoratedMessage : message, color);
|
||||
return;
|
||||
}
|
||||
|
||||
if (writer != null) {
|
||||
writer.print(decorateOutput ? decoratedMessage : message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the undecorated {@link java.util.Formatter formatted message} to this script's
|
||||
* {@code stdout} {@link PrintWriter}, which is set by
|
||||
|
|
|
@ -97,7 +97,7 @@ public class ScriptControls {
|
|||
* @param monitor A cancellable monitor
|
||||
*/
|
||||
public ScriptControls(InterpreterConsole console, TaskMonitor monitor) {
|
||||
this(console.getStdOut(), console.getStdErr(), monitor);
|
||||
this(console.getOutWriter(), console.getErrWriter(), monitor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
*/
|
||||
package ghidra.app.services;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import ghidra.app.plugin.core.console.ConsolePlugin;
|
||||
import ghidra.framework.plugintool.ServiceInfo;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* Generic console interface allowing any plugin to print
|
||||
* messages to console window.
|
||||
|
@ -109,8 +109,6 @@ public interface ConsoleService {
|
|||
* please throw {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @return number of characters >= 0
|
||||
*
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public int getTextLength();
|
||||
|
||||
|
@ -128,8 +126,6 @@ public interface ConsoleService {
|
|||
* @param length the length of the desired string >= 0
|
||||
*
|
||||
* @return the text, in a String of length >= 0
|
||||
*
|
||||
* @throws UnsupportedOperationException
|
||||
*/
|
||||
public String getText(int offset, int length);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,10 @@
|
|||
*/
|
||||
package ghidra.framework.main;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.text.*;
|
||||
|
@ -83,6 +85,10 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
|||
doAddMessage(new MessageWrapper(message));
|
||||
}
|
||||
|
||||
public void addPartialMessage(String message, Color c) {
|
||||
doAddMessage(new MessageWrapper(message, getFont(), c));
|
||||
}
|
||||
|
||||
public void addErrorMessage(String message) {
|
||||
doAddMessage(new ErrorMessage(message));
|
||||
}
|
||||
|
@ -280,15 +286,21 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
|||
//==================================================================================================
|
||||
|
||||
private static class MessageWrapper {
|
||||
private final StringBuilder message;
|
||||
protected final StringBuilder message;
|
||||
private Color color;
|
||||
private Font font;
|
||||
|
||||
private MessageWrapper(String message) {
|
||||
if (message == null) {
|
||||
throw new AssertException("Attempted to log a null message.");
|
||||
}
|
||||
Objects.requireNonNull(message, "Attempted to log a null message");
|
||||
this.message = new StringBuilder(message);
|
||||
}
|
||||
|
||||
public MessageWrapper(String message, Font font, Color color) {
|
||||
this(message);
|
||||
this.font = Objects.requireNonNull(font);
|
||||
this.color = Objects.requireNonNull(color);
|
||||
}
|
||||
|
||||
CharSequence getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
@ -297,13 +309,31 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
|||
if (getClass() != other.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Objects.equals(color, other.color)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
message.append(other.message);
|
||||
return true;
|
||||
}
|
||||
|
||||
AttributeSet getAttributes() {
|
||||
if (color != null) {
|
||||
GAttributes attrs = new GAttributes(font, color);
|
||||
attrs.addAttribute(CUSTOM_ATTRIBUTE_KEY, OUTPUT_ATTRIBUTE_VALUE);
|
||||
return attrs;
|
||||
}
|
||||
return outputAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (color == null) {
|
||||
return message.toString();
|
||||
}
|
||||
return "[color=" + color + "] " + message.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static class ErrorMessage extends MessageWrapper {
|
||||
|
@ -315,6 +345,10 @@ public class ConsoleTextPane extends JTextPane implements OptionsChangeListener
|
|||
AttributeSet getAttributes() {
|
||||
return errorAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[error] " + message.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1199,6 +1199,34 @@ public abstract class AbstractGhidraScriptMgrPluginTest
|
|||
assertTrue("Timed-out waiting for cancelled script to complete", success);
|
||||
}
|
||||
|
||||
protected void runScript(ResourceFile scriptFile) throws Exception {
|
||||
|
||||
GhidraScriptProvider scriptProvider = GhidraScriptUtil.getProvider(scriptFile);
|
||||
GhidraScript script =
|
||||
scriptProvider.getScriptInstance(scriptFile, new PrintWriter(System.err));
|
||||
|
||||
Task task = new RunScriptTask(script, plugin.getCurrentState(), console);
|
||||
task.addTaskListener(provider.getTaskListener());
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
task.addTaskListener(new TaskListener() {
|
||||
|
||||
@Override
|
||||
public void taskCompleted(Task t) {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void taskCancelled(Task t) {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
TaskLauncher.launch(task);
|
||||
|
||||
latch.await(TASK_RUN_SCRIPT_TIMEOUT_SECS, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
protected void startRunScriptTask(GhidraScript script) throws Exception {
|
||||
Task task = new RunScriptTask(script, plugin.getCurrentState(), console);
|
||||
task.addTaskListener(provider.getTaskListener());
|
||||
|
|
|
@ -17,9 +17,12 @@ package ghidra.app.plugin.core.script;
|
|||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.io.*;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import javax.swing.text.*;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -490,6 +493,113 @@ public class GhidraScriptMgrPlugin2Test extends AbstractGhidraScriptMgrPluginTes
|
|||
"*2*", output);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScriptPrintWithColor() throws Exception {
|
||||
|
||||
// create a script
|
||||
ResourceFile newScriptFile = createTempScriptFile("LineColoringScript");
|
||||
String filename = newScriptFile.getName();
|
||||
String className = filename.replaceAll("\\.java", "");
|
||||
|
||||
String text1 = "This is black, ";
|
||||
String text2 = "this is blue, and ";
|
||||
String text3 = "this is red.\\n";
|
||||
String line2 = "This is the default color.";
|
||||
|
||||
//@formatter:off
|
||||
String newScript = """
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import java.awt.Color;
|
||||
|
||||
public class %s extends GhidraScript {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
print("%s");
|
||||
print("%s", Color.BLUE);
|
||||
print("%s", Color.RED);
|
||||
print("%s");
|
||||
}
|
||||
};
|
||||
""".formatted(className, text1, text2, text3, line2);
|
||||
//@formatter:on
|
||||
|
||||
writeStringToFile(newScriptFile, newScript);
|
||||
|
||||
runScript(newScriptFile);
|
||||
waitForSwing();
|
||||
|
||||
assertConsoleTextColor(text1, Color.BLACK);
|
||||
assertConsoleTextColor(text2, Color.BLUE);
|
||||
assertConsoleTextColor(text3, Color.RED);
|
||||
assertConsoleTextColor(text2, Color.BLACK);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScriptPrintlnWithColor() throws Exception {
|
||||
|
||||
// create a script
|
||||
ResourceFile newScriptFile = createTempScriptFile("LineColoringScript");
|
||||
String filename = newScriptFile.getName();
|
||||
String className = filename.replaceAll("\\.java", "");
|
||||
|
||||
String line1 = "1 This is a default line";
|
||||
String line2 = "2 This is a blue line";
|
||||
String line3 = "3 This is a red line";
|
||||
|
||||
//@formatter:off
|
||||
String newScript = """
|
||||
import ghidra.app.script.GhidraScript;
|
||||
import java.awt.Color;
|
||||
|
||||
public class %s extends GhidraScript {
|
||||
|
||||
@Override
|
||||
public void run() throws Exception {
|
||||
|
||||
println("%s");
|
||||
println("%s", Color.BLUE);
|
||||
println("%s", Color.RED);
|
||||
}
|
||||
};
|
||||
""".formatted(className, line1, line2, line3);
|
||||
//@formatter:on
|
||||
|
||||
writeStringToFile(newScriptFile, newScript);
|
||||
|
||||
runScript(newScriptFile);
|
||||
waitForSwing();
|
||||
|
||||
assertConsoleTextColor(line1, Color.BLACK);
|
||||
assertConsoleTextColor(line2, Color.BLUE);
|
||||
assertConsoleTextColor(line3, Color.RED);
|
||||
}
|
||||
|
||||
private void assertConsoleTextColor(String text, Color expectedFgColor) {
|
||||
String fullText = runSwing(() -> consoleTextPane.getText());
|
||||
|
||||
// We have 2 layers of newlines in the test. A '\\n' that gets written to file as Java
|
||||
// code. That then gets compiled and written out as a newline. Our 'text' value passed
|
||||
// here is that original '\\n'. We are trying to compare that against what ends up in the
|
||||
// console, which has gone through 2 string interpretations to end up as a standard newline.
|
||||
// Strip off the '\\n' from the original input text before looking for it in the console.
|
||||
String visibleText = text.replaceAll("\\\\n", "");
|
||||
int start = fullText.indexOf(visibleText);
|
||||
int end = visibleText.length();
|
||||
|
||||
runSwing(() -> {
|
||||
StyledDocument styledDocument = (StyledDocument) consoleTextPane.getDocument();
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
Element element = styledDocument.getCharacterElement(i);
|
||||
AttributeSet attrs = element.getAttributes();
|
||||
Color actualFgColor = (Color) attrs.getAttribute(StyleConstants.Foreground);
|
||||
assertEquals(expectedFgColor, actualFgColor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Path getBinDirFromScriptFile(ResourceFile sourceFile) {
|
||||
ResourceFile tmpSourceDir = sourceFile.getParentFile();
|
||||
String tmpSymbolicName = GhidraSourceBundle.sourceDirHash(tmpSourceDir);
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package generic.theme;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
|
||||
import javax.swing.text.SimpleAttributeSet;
|
||||
|
@ -32,7 +33,7 @@ public class GAttributes extends SimpleAttributeSet {
|
|||
this(f, null);
|
||||
}
|
||||
|
||||
public GAttributes(Font f, GColor c) {
|
||||
public GAttributes(Font f, Color c) {
|
||||
addAttribute(StyleConstants.FontFamily, f.getFamily());
|
||||
addAttribute(StyleConstants.FontSize, f.getSize());
|
||||
addAttribute(StyleConstants.Bold, f.isBold());
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue