From d1c23bec0b9900d92fc522429ef5476757a2af93 Mon Sep 17 00:00:00 2001 From: Tuukka Lehtonen Date: Wed, 4 Oct 2017 16:09:02 +0300 Subject: [PATCH] Added SCL Script Output console view. refs #7528 Change-Id: I2a6230c39ca4f292866125911b2b5c74da6362ff --- .../simantics/modeling/ui/scl/SCLScripts.java | 10 +- .../icons/application_xp.png | Bin 0 -> 426 bytes bundles/org.simantics.scl.ui/plugin.xml | 18 +- .../ui/console/AbstractCommandConsole.java | 349 +++++++++--------- .../scl/ui/console/ConsoleActions.java | 41 ++ .../simantics/scl/ui/console/SCLConsole.java | 26 +- .../scl/ui/console/SCLConsoleView.java | 24 +- .../scl/ui/console/SCLScriptConsoleView.java | 76 ++++ 8 files changed, 328 insertions(+), 216 deletions(-) create mode 100644 bundles/org.simantics.scl.ui/icons/application_xp.png create mode 100644 bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/ConsoleActions.java create mode 100644 bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLScriptConsoleView.java diff --git a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/scl/SCLScripts.java b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/scl/SCLScripts.java index 350a819fd..3cd006fce 100644 --- a/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/scl/SCLScripts.java +++ b/bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/scl/SCLScripts.java @@ -39,7 +39,7 @@ public class SCLScripts { private static final Logger LOGGER = LoggerFactory.getLogger(SCLScripts.class); - private static final String SCL_CONSOLE_ID = "org.simantics.scl.ui.console"; + private static final String SCL_SCRIPT_CONSOLE_ID = "org.simantics.scl.ui.scriptConsole"; /** * @param processor database handle @@ -78,16 +78,16 @@ public class SCLScripts { public static Pair getSCLConsoleCommandSession(boolean createIfNecessary) { IWorkbenchPart part; + SCLReportingHandler handler = SCLReportingHandler.DEFAULT_WITHOUT_ECHO; try { part = createIfNecessary - ? WorkbenchUtils.showView(SCL_CONSOLE_ID, IWorkbenchPage.VIEW_VISIBLE) - : WorkbenchUtils.findView(SCL_CONSOLE_ID); + ? WorkbenchUtils.showView(SCL_SCRIPT_CONSOLE_ID, IWorkbenchPage.VIEW_VISIBLE) + : WorkbenchUtils.findView(SCL_SCRIPT_CONSOLE_ID); if (part != null) - return Pair.make(part.getAdapter(CommandSession.class), part.getAdapter(SCLReportingHandler.class)); + return Pair.make(new CommandSession(SCLOsgi.MODULE_REPOSITORY, handler), part.getAdapter(SCLReportingHandler.class)); } catch (PartInitException e) { LOGGER.error("Failed to open SCL Console view. Using new CommandSession, reporting to stdout via Logger.", e); } - SCLReportingHandler handler = SCLReportingHandler.DEFAULT_WITHOUT_ECHO; return Pair.make(new CommandSession(SCLOsgi.MODULE_REPOSITORY, handler), SCLReportingHandler.DEFAULT); } diff --git a/bundles/org.simantics.scl.ui/icons/application_xp.png b/bundles/org.simantics.scl.ui/icons/application_xp.png new file mode 100644 index 0000000000000000000000000000000000000000..d22860a3166820b8fdad71c6505a40580af14b2f GIT binary patch literal 426 zcmV;b0agBqP)wtp^MwsrC67-bFzu!t;6Q^%|k2MJ$kfh*aj@vhNOIJ zWFxBVQIH79u!vw98!Ll~y$OKnm}QK5wtxPNU<+B6{mDR{T_7M%l&baHE>bEbHX04+ zn+OzZ0e^b{APT{9E#RmJ5)l*`)B%#z1i)YI - - @@ -63,6 +54,15 @@ name="SCL Console" restorable="true"> + + commandHistory = new ArrayList(); int commandHistoryPos = 0; @@ -73,29 +81,33 @@ public abstract class AbstractCommandConsole extends Composite { Shell tip = null; Label label = null; */ - - public AbstractCommandConsole(Composite parent, int style) { - super(parent, style); + + public AbstractCommandConsole(Composite parent, int style, int options) { + super(parent, style); + this.options = options; createControl(); } - + @Override public boolean setFocus() { - return input.setFocus(); + return input != null ? input.setFocus() : output.setFocus(); } - + protected boolean canExecuteCommand() { return true; } + private boolean hasOption(int mask) { + return (options & mask) != 0; + } + private void createControl() { resourceManager = new LocalResourceManager(JFaceResources.getResources(), this); greenColor = resourceManager.createColor(new RGB(0, 128, 0)); redColor = resourceManager.createColor(new RGB(172, 0, 0)); - + textFont = resourceManager.createFont( FontDescriptor.createFrom("Courier New", 12, SWT.NONE) ); + setLayout(new FormLayout()); - - Font textFont = new Font(getDisplay(),"Courier New",12,SWT.NONE); // Sash sash = new Sash(this, /*SWT.BORDER |*/ SWT.HORIZONTAL); @@ -115,121 +127,111 @@ public abstract class AbstractCommandConsole extends Composite { // Upper output = new StyledText(this, SWT.MULTI /*| SWT.READ_ONLY*/ | SWT.V_SCROLL | SWT.H_SCROLL); output.setFont(textFont); - { - FormData formData = new FormData(); - formData.top = new FormAttachment(0); - formData.bottom = new FormAttachment(sash); - formData.left = new FormAttachment(0); - formData.right = new FormAttachment(100); - output.setLayoutData(formData); - } + output.setLayoutData( formData(0, sash, 0, 100) ); output.addVerifyListener(new VerifyListener() { @Override public void verifyText(VerifyEvent e) { if(outputModiLock) return; - input.append(e.text); - input.setFocus(); - input.setCaretOffset(input.getText().length()); + if (input != null) { + input.append(e.text); + input.setFocus(); + input.setCaretOffset(input.getText().length()); + } e.doit = false; } }); - // Deco - StyledText deco = new StyledText(this, SWT.MULTI | SWT.READ_ONLY); + if (hasOption(HIDE_INPUT)) { + sash.setLayoutData( formData(new Tuple2(100, 0), null, 0, 100, 0) ); + layout(true); + } else { + createInputArea(); + } + + readPreferences(); + + addListener(SWT.Dispose, event -> { + try { + writePreferences(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + protected void createInputArea() { + // "> " Decoration + StyledText deco = new StyledText(this, SWT.MULTI | SWT.READ_ONLY); deco.setFont(textFont); deco.setEnabled(false); GC gc = new GC(deco); int inputLeftPos = gc.getFontMetrics().getAverageCharWidth()*2; + gc.dispose(); deco.setText(">"); - { - FormData formData = new FormData(); - formData.top = new FormAttachment(sash); - formData.bottom = new FormAttachment(100); - formData.left = new FormAttachment(0); - formData.right = new FormAttachment(0, inputLeftPos); - deco.setLayoutData(formData); - } - - // Input area - input = new StyledText(this, SWT.MULTI); + deco.setLayoutData( formData(sash, 100, 0, new Tuple2(0, inputLeftPos)) ); + + // Input area + input = new StyledText(this, SWT.MULTI); input.setFont(textFont); - { - FormData formData = new FormData(); - formData.top = new FormAttachment(sash); - formData.bottom = new FormAttachment(100); - formData.left = new FormAttachment(0, inputLeftPos); - formData.right = new FormAttachment(100); - input.setLayoutData(formData); - } + input.setLayoutData( formData(sash, 100, new Tuple2(0, inputLeftPos), 100) ); adjustInputSize(""); - input.addVerifyKeyListener(new VerifyKeyListener() { - - @Override - public void verifyKey(VerifyEvent event) { - switch(event.keyCode) { - case SWT.KEYPAD_CR: - case SWT.CR: - if((event.stateMask & SWT.CTRL) == 0) { - if(canExecuteCommand()) - execute(); - event.doit = false; + input.addVerifyKeyListener(event -> { + switch(event.keyCode) { + case SWT.KEYPAD_CR: + case SWT.CR: + if((event.stateMask & SWT.CTRL) == 0) { + if(canExecuteCommand()) + execute(); + event.doit = false; + } + break; + case SWT.ARROW_UP: + case SWT.ARROW_DOWN: + if((event.stateMask & SWT.CTRL) != 0) { + int targetHistoryPos = commandHistoryPos; + if(event.keyCode == SWT.ARROW_UP) { + if(commandHistoryPos <= 0) + return; + --targetHistoryPos; } - break; - case SWT.ARROW_UP: - case SWT.ARROW_DOWN: - if((event.stateMask & SWT.CTRL) != 0) { - int targetHistoryPos = commandHistoryPos; - if(event.keyCode == SWT.ARROW_UP) { - if(commandHistoryPos <= 0) - return; - --targetHistoryPos; - } - else { - if(commandHistoryPos >= commandHistory.size()-1) - return; - ++targetHistoryPos; - } - setInputText(commandHistory.get(targetHistoryPos)); - commandHistoryPos = targetHistoryPos; - event.doit = false; + else { + if(commandHistoryPos >= commandHistory.size()-1) + return; + ++targetHistoryPos; } - break; -// case SWT.ESC: -// setInputText(""); -// commandHistoryPos = commandHistory.size(); -// break; - } - } - - }); - input.addVerifyListener(new VerifyListener() { - @Override - public void verifyText(VerifyEvent e) { - if(e.text.contains("\n")) { - int lineId = input.getLineAtOffset(e.start); - int lineOffset = input.getOffsetAtLine(lineId); - int indentAmount; - for(indentAmount=0; - lineOffset+indentAmount < input.getCharCount() && - input.getTextRange(lineOffset+indentAmount, 1).equals(" "); - ++indentAmount); - StringBuilder indent = new StringBuilder(); - indent.append('\n'); - for(int i=0;i { + if(e.text.contains("\n")) { + int lineId = input.getLineAtOffset(e.start); + int lineOffset = input.getOffsetAtLine(lineId); + int indentAmount; + for(indentAmount=0; + lineOffset+indentAmount < input.getCharCount() && + input.getTextRange(lineOffset+indentAmount, 1).equals(" "); + ++indentAmount); + StringBuilder indent = new StringBuilder(); + indent.append('\n'); + for(int i=0;i { + adjustInputSize(input.getText()); + commandHistoryPos = commandHistory.size(); + //asyncValidate(); + }); Listener hoverListener = new Listener() { DefaultToolTip toolTip = new DefaultToolTip(input, ToolTip.RECREATE, true); @@ -285,30 +287,42 @@ public abstract class AbstractCommandConsole extends Composite { toolTipVisible = false; } return; - } - } + } + } }; input.addListener(SWT.MouseHover, hoverListener); input.addListener(SWT.MouseMove, hoverListener); input.addListener(SWT.MouseExit, hoverListener); - - readPreferences(); - - addListener(SWT.Dispose, new Listener() { - - @Override - public void handleEvent(Event event) { - try { - writePreferences(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - }); - } - + + private FormData formData(Object top, Object bottom, Object left, Object right) { + return formData(top, bottom, left, right, null); + } + + private FormData formData(Object top, Object bottom, Object left, Object right, Integer height) { + FormData d = new FormData(); + d.top = formAttachment(top); + d.bottom = formAttachment(bottom); + d.left = formAttachment(left); + d.right = formAttachment(right); + d.height = height != null ? (Integer) height : SWT.DEFAULT; + return d; + } + + private FormAttachment formAttachment(Object o) { + if (o == null) + return null; + if (o instanceof Control) + return new FormAttachment((Control) o); + if (o instanceof Integer) + return new FormAttachment((Integer) o); + if (o instanceof Tuple2) { + Tuple2 t = (Tuple2) o; + return new FormAttachment((Integer) t.c0, (Integer) t.c1); + } + throw new IllegalArgumentException("argument not supported: " + o); + } + private int getOffsetInInput(int x, int y) { int offset; try { @@ -327,6 +341,8 @@ public abstract class AbstractCommandConsole extends Composite { } public void setInputText(String text) { + if (input == null) + return; input.setText(text); input.setCaretOffset(text.length()); adjustInputSize(text); @@ -345,20 +361,17 @@ public abstract class AbstractCommandConsole extends Composite { }; - Job preValidationJob = new Job("SCL input validation") { + Job preValidationJob = new Job("SCL input validation") { @Override protected IStatus run(IProgressMonitor monitor) { if(!input.isDisposed()) { - input.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - if(!input.isDisposed()) { - validatedText = input.getText(); - validationJob.setPriority(Job.BUILD); - validationJob.schedule(); - } - } - }); + input.getDisplay().asyncExec(() -> { + if(!input.isDisposed()) { + validatedText = input.getText(); + validationJob.setPriority(Job.BUILD); + validationJob.schedule(); + } + }); } return Status.OK_STATUS; @@ -391,19 +404,14 @@ public abstract class AbstractCommandConsole extends Composite { } private void setInputHeight(int inputHeight) { - FormData formData = new FormData(); - formData.top = new FormAttachment(100, -inputHeight); - formData.left = new FormAttachment(0); - formData.right = new FormAttachment(100); - formData.height = SASH_HEIGHT; - sash.setLayoutData(formData); + sash.setLayoutData( formData(new Tuple2(100, -inputHeight), null, 0, 100, SASH_HEIGHT) ); AbstractCommandConsole.this.layout(true); } private StringBuilder outputBuffer = new StringBuilder(); private ArrayList styleRanges = new ArrayList(); private volatile boolean outputScheduled = false; - + public void appendOutput(final String text, final Color foreground, final Color background) { synchronized (outputBuffer) { styleRanges.add(new StyleRange(outputBuffer.length(), text.length(), foreground, background)); @@ -413,40 +421,37 @@ public abstract class AbstractCommandConsole extends Composite { outputScheduled = true; final Display display = Display.getDefault(); if(display.isDisposed()) return; - display.asyncExec(new Runnable() { - @Override - public void run() { - if(output.isDisposed()) return; - String outputText; - StyleRange[] styleRangeArray; - synchronized(outputBuffer) { - outputScheduled = false; - - outputText = outputBuffer.toString(); - outputBuffer = new StringBuilder(); - - styleRangeArray = styleRanges.toArray(new StyleRange[styleRanges.size()]); - styleRanges.clear(); - } - int pos = output.getCharCount(); + display.asyncExec(() -> { + if(output.isDisposed()) return; + String outputText; + StyleRange[] styleRangeArray; + synchronized(outputBuffer) { + outputScheduled = false; - outputModiLock = true; - output.replaceTextRange(pos, 0, outputText); - outputModiLock = false; - - for(StyleRange styleRange : styleRangeArray) { - styleRange.start += pos; - output.setStyleRange(styleRange); - } + outputText = outputBuffer.toString(); + outputBuffer = new StringBuilder(); + + styleRangeArray = styleRanges.toArray(new StyleRange[styleRanges.size()]); + styleRanges.clear(); + } + int pos = output.getCharCount(); - output.setCaretOffset(output.getCharCount()); - output.showSelection(); + outputModiLock = true; + output.replaceTextRange(pos, 0, outputText); + outputModiLock = false; + + for(StyleRange styleRange : styleRangeArray) { + styleRange.start += pos; + output.setStyleRange(styleRange); } + + output.setCaretOffset(output.getCharCount()); + output.showSelection(); }); } } - - private void execute() { + + private void execute() { String command = input.getText().trim(); if(command.isEmpty()) return; @@ -514,7 +519,7 @@ public abstract class AbstractCommandConsole extends Composite { if(input.isDisposed()) return; if(!input.getText().equals(forCommand)) - return; + return; syncSetErrorAnnotations(forCommand, annotations); } }); diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/ConsoleActions.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/ConsoleActions.java new file mode 100644 index 000000000..744b83c9b --- /dev/null +++ b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/ConsoleActions.java @@ -0,0 +1,41 @@ +package org.simantics.scl.ui.console; + +import org.eclipse.jface.action.Action; +import org.simantics.scl.ui.Activator; + +/** + * @author Tuukka Lehtonen + * @since 1.31.0 + */ +class ConsoleActions { + + public static Action createInterruptAction(SCLConsole console) { + Action interruptAction = new Action("Interrupt current command", + Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/stop.png")) { + @Override + public void run() { + console.interruptCurrentCommands(); + } + }; + interruptAction.setDisabledImageDescriptor( + Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/stop_disabled.png")); + interruptAction.setEnabled(false); + return interruptAction; + } + + public static Action createClearAction(SCLConsole console) { + Action clearAction = new Action("Clear console", + Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/clear_console.png")) { + @Override + public void run() { + setEnabled(false); + console.clear(); + } + }; + clearAction.setDisabledImageDescriptor( + Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/clear_console_disabled.png")); + clearAction.setEnabled(false); + return clearAction; + } + +} diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLConsole.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLConsole.java index 16a946fbe..dcb56cc3a 100644 --- a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLConsole.java +++ b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLConsole.java @@ -58,13 +58,23 @@ public class SCLConsole extends AbstractCommandConsole { CommandSession session = new CommandSession(SCLOsgi.MODULE_REPOSITORY, handler); ContentProposalAdapter contentProposalAdapter; - + public SCLConsole(Composite parent, int style) { - super(parent, style); - + this(parent, style, 0); + } + + public SCLConsole(Composite parent, int style, int options) { + super(parent, style, options); + createContentProposalAdapter(); + addContributedListeners(); + } + + protected void createContentProposalAdapter() { + if (input == null) + return; + StyledTextContentAdapter styledTextContentAdapter = new StyledTextContentAdapter(); SCLContentProposalProvider contentProvider = new SCLContentProposalProvider(session); - try { contentProposalAdapter = new ContentProposalAdapter( input, @@ -76,15 +86,13 @@ public class SCLConsole extends AbstractCommandConsole { } catch (ParseException e) { // No content assist then. } - - addContributedListeners(); } @Override protected boolean canExecuteCommand() { - return !contentProposalAdapter.isProposalPopupOpen(); + return contentProposalAdapter == null || !contentProposalAdapter.isProposalPopupOpen(); } - + @Override public ErrorAnnotation[] validate(String command) { if(command.isEmpty()) @@ -216,7 +224,7 @@ public class SCLConsole extends AbstractCommandConsole { consoleIsEmpty = true; } - private void addContributedListeners() { + protected void addContributedListeners() { final BundleContext context = Activator.getInstance().getBundle().getBundleContext(); new ServiceTracker(context, SCLConsoleListener.class, null) { diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLConsoleView.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLConsoleView.java index 8b49ea345..4c0d829f3 100644 --- a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLConsoleView.java +++ b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLConsoleView.java @@ -182,31 +182,13 @@ public class SCLConsoleView extends ViewPart { IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager(); // Interrupt action - final Action interruptAction = new Action("Interrupt current command", - Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/stop.png")) { - @Override - public void run() { - console.interruptCurrentCommands(); - } - }; - interruptAction.setDisabledImageDescriptor( - Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/stop_disabled.png")); - interruptAction.setEnabled(false); + Action interruptAction = ConsoleActions.createInterruptAction(console); toolBarManager.add(interruptAction); // Clear console action - final Action clearAction = new Action("Clear console", - Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/clear_console.png")) { - @Override - public void run() { - setEnabled(false); - console.clear(); - } - }; - clearAction.setDisabledImageDescriptor( - Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/clear_console_disabled.png")); - clearAction.setEnabled(false); + Action clearAction = ConsoleActions.createClearAction(console); toolBarManager.add(clearAction); + console.addListener(new SCLConsoleListener() { @Override public void startedExecution() { diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLScriptConsoleView.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLScriptConsoleView.java new file mode 100644 index 000000000..8df0caa36 --- /dev/null +++ b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/SCLScriptConsoleView.java @@ -0,0 +1,76 @@ +package org.simantics.scl.ui.console; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.part.ViewPart; +import org.simantics.scl.compiler.commands.SCLConsoleListener; +import org.simantics.scl.runtime.reporting.SCLReportingHandler; + +/** + * @author Tuukka Lehtonen + * @since 1.31.0 + */ +public class SCLScriptConsoleView extends ViewPart { + + static class SCLOutputConsole extends SCLConsole { + public SCLOutputConsole(Composite parent, int style) { + super(parent, style, AbstractCommandConsole.HIDE_INPUT); + } + + @Override + protected void addContributedListeners() { + } + } + + SCLOutputConsole console; + + @Override + public void createPartControl(Composite parent) { + this.console = new SCLOutputConsole(parent, SWT.NONE); + + IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager(); + Action interruptAction = ConsoleActions.createInterruptAction(console); + Action clearAction = ConsoleActions.createClearAction(console); + toolBarManager.add(interruptAction); + toolBarManager.add(clearAction); + + console.addListener(new SCLConsoleListener() { + @Override + public void startedExecution() { + interruptAction.setEnabled(true); + } + @Override + public void finishedExecution() { + interruptAction.setEnabled(false); + } + @Override + public void consoleIsNotEmptyAnymore() { + clearAction.setEnabled(true); + } + }); + + toolBarManager.update(true); + } + + @Override + public void setFocus() { + console.setFocus(); + } + + @Override + public void dispose() { + super.dispose(); + console.dispose(); + } + + @SuppressWarnings("unchecked") + @Override + public T getAdapter(Class adapter) { + if (adapter == SCLReportingHandler.class) + return (T) console.getHandler(); + return super.getAdapter(adapter); + } + +} -- 2.43.2