1 package org.simantics.scl.ui.console;
3 import java.nio.file.Files;
4 import java.nio.file.Path;
5 import java.nio.file.Paths;
6 import java.util.ArrayList;
7 import java.util.Arrays;
9 import java.util.concurrent.atomic.AtomicReference;
11 import org.eclipse.core.runtime.IProgressMonitor;
12 import org.eclipse.core.runtime.IStatus;
13 import org.eclipse.core.runtime.Platform;
14 import org.eclipse.core.runtime.Status;
15 import org.eclipse.core.runtime.jobs.Job;
16 import org.eclipse.core.runtime.preferences.InstanceScope;
17 import org.eclipse.jface.action.Action;
18 import org.eclipse.jface.action.IAction;
19 import org.eclipse.jface.action.IMenuCreator;
20 import org.eclipse.jface.action.IToolBarManager;
21 import org.eclipse.jface.dialogs.Dialog;
22 import org.eclipse.jface.preference.IPersistentPreferenceStore;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.dnd.DND;
25 import org.eclipse.swt.dnd.DropTarget;
26 import org.eclipse.swt.dnd.DropTargetAdapter;
27 import org.eclipse.swt.dnd.DropTargetEvent;
28 import org.eclipse.swt.dnd.FileTransfer;
29 import org.eclipse.swt.dnd.Transfer;
30 import org.eclipse.swt.events.SelectionAdapter;
31 import org.eclipse.swt.events.SelectionEvent;
32 import org.eclipse.swt.widgets.Composite;
33 import org.eclipse.swt.widgets.Control;
34 import org.eclipse.swt.widgets.Menu;
35 import org.eclipse.swt.widgets.MenuItem;
36 import org.eclipse.ui.PlatformUI;
37 import org.eclipse.ui.part.ViewPart;
38 import org.eclipse.ui.preferences.ScopedPreferenceStore;
39 import org.simantics.scl.compiler.commands.CommandSession;
40 import org.simantics.scl.compiler.commands.CommandSessionImportEntry;
41 import org.simantics.scl.compiler.commands.SCLConsoleListener;
42 import org.simantics.scl.compiler.module.repository.UpdateListener;
43 import org.simantics.scl.compiler.testing.TestRunnable;
44 import org.simantics.scl.osgi.internal.TestUtils;
45 import org.simantics.scl.runtime.reporting.SCLReportingHandler;
46 import org.simantics.scl.ui.Activator;
47 import org.simantics.scl.ui.imports.internal.ManageImportsDialog;
48 import org.simantics.scl.ui.tests.SCLTestsDialog;
50 public class SCLConsoleView extends ViewPart {
52 public static final String PLUGIN_ID = "org.simantics.scl.ui"; //$NON-NLS-1$
53 public static final String IMPORTS = "imports"; //$NON-NLS-1$
54 public static final String REFRESH_AUTOMATICALLY = "refresh-automatically"; //$NON-NLS-1$
55 public static final String SEPARATOR = ";"; //$NON-NLS-1$
56 public static final String DISABLED_TAG = "[DISABLED]"; //$NON-NLS-1$
58 IPersistentPreferenceStore store;
60 boolean refreshAutomatically = false;
61 MenuItem refreshAutomaticallyItem;
63 private ArrayList<CommandSessionImportEntry> readImportPreferences() {
64 String importsString = store.getString(IMPORTS);
66 String[] splitted = importsString.split(SEPARATOR);
67 ArrayList<CommandSessionImportEntry> result = new ArrayList<CommandSessionImportEntry>(splitted.length);
68 for(String entryString : splitted) {
69 if(entryString.isEmpty())
71 boolean disabled = false;
72 if(entryString.startsWith(DISABLED_TAG)) {
74 entryString = entryString.substring(DISABLED_TAG.length());
76 String[] parts = entryString.split("="); //$NON-NLS-1$
77 CommandSessionImportEntry entry;
79 entry = new CommandSessionImportEntry(parts[0], "", true); //$NON-NLS-1$
81 entry = new CommandSessionImportEntry(parts[1], parts[0], true);
82 entry.disabled = disabled;
88 private void writeImportPreferences(ArrayList<CommandSessionImportEntry> entries) {
89 StringBuilder b = new StringBuilder();
92 for(CommandSessionImportEntry entry : entries)
93 if(entry.persistent) {
99 b.append(DISABLED_TAG);
100 if(!entry.localName.isEmpty()) {
101 b.append(entry.localName);
102 b.append("="); //$NON-NLS-1$
104 b.append(entry.moduleName);
107 IPersistentPreferenceStore store = new ScopedPreferenceStore(InstanceScope.INSTANCE, PLUGIN_ID);
108 store.setValue(IMPORTS, b.toString());
111 private ArrayList<CommandSessionImportEntry> getCurrentImports() {
112 return console.getSession().getImportEntries();
115 private void setCurrentImports(ArrayList<CommandSessionImportEntry> entries) {
116 console.getSession().setImportEntries(entries);
119 AtomicReference<ArrayList<CommandSessionImportEntry>> assignedImports = new AtomicReference<>();
121 private class SetImportsJob extends Job {
123 public SetImportsJob() {
124 super(Messages.SCLConsoleView_SetImports);
129 protected IStatus run(IProgressMonitor monitor) {
131 ArrayList<CommandSessionImportEntry> entries = assignedImports.getAndSet(null);
133 setCurrentImports(entries);
134 return Status.OK_STATUS;
141 public boolean shouldSchedule() {
142 return PlatformUI.isWorkbenchRunning();
146 public boolean shouldRun() {
147 return PlatformUI.isWorkbenchRunning();
152 SetImportsJob setImportsJob = new SetImportsJob();
154 private void scheduleSetCurrentImports(ArrayList<CommandSessionImportEntry> entries) {
155 boolean scheduled = assignedImports.getAndSet(entries) != null;
157 setImportsJob.schedule();
160 private void manageImports() {
161 ManageImportsDialog dialog = new ManageImportsDialog(
162 getSite().getShell(),
163 getCurrentImports());
164 if(dialog.open() == Dialog.OK) {
165 writeImportPreferences(dialog.getImports());
166 scheduleSetCurrentImports(dialog.getImports());
170 private void sclTestDialog() {
171 List<TestRunnable> tests = TestUtils.getTests();
172 SCLTestsDialog dialog = new SCLTestsDialog(
173 getSite().getShell(),
175 if(dialog.open() == Dialog.OK) {
176 for(Object result : dialog.getResult()) {
177 TestRunnable test = (TestRunnable) result;
179 // Bit of a haxx solution to get around a deadlock caused by simply
180 // running the test with test.run()
181 console.execute("import \"Commands/Tests\""); //$NON-NLS-1$
182 console.execute("runByName \"" + test.getName() + "\""); //$NON-NLS-1$ //$NON-NLS-2$
184 } catch (Exception e) {
191 private UpdateListener dependencyListener = new UpdateListener() {
193 public void notifyAboutUpdate() {
194 if(refreshAutomatically)
195 console.getSession().updateRuntimeEnvironment(true);
199 private void setRefreshAutomatically(boolean refreshAutomatically, boolean refreshAlso) {
200 this.refreshAutomatically = refreshAutomatically;
201 if(refreshAutomaticallyItem != null)
202 refreshAutomaticallyItem.setSelection(refreshAutomatically);
204 store.setValue(REFRESH_AUTOMATICALLY, refreshAutomatically);
206 if(refreshAutomatically) {
207 console.getSession().setDependenciesListener(dependencyListener);
209 console.getSession().updateRuntimeEnvironment(true);
212 console.getSession().setDependenciesListener(null);
213 dependencyListener.stopListening();
218 public void createPartControl(Composite parent) {
219 store = new ScopedPreferenceStore(InstanceScope.INSTANCE, PLUGIN_ID);
220 store.setDefault(REFRESH_AUTOMATICALLY, true);
222 this.console = new SCLConsole(parent, SWT.NONE);
224 IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
227 Action interruptAction = ConsoleActions.createInterruptAction(console);
228 toolBarManager.add(interruptAction);
230 // Clear console action
231 Action clearAction = ConsoleActions.createClearAction(console);
232 toolBarManager.add(clearAction);
234 console.addListener(new SCLConsoleListener() {
236 public void startedExecution() {
237 interruptAction.setEnabled(true);
240 public void finishedExecution() {
241 interruptAction.setEnabled(false);
244 public void consoleIsNotEmptyAnymore() {
245 clearAction.setEnabled(true);
250 toolBarManager.add(new Action(Messages.SCLConsoleView_RefreshModules, IAction.AS_DROP_DOWN_MENU) {
252 setImageDescriptor(Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/arrow_refresh.png")); //$NON-NLS-1$ //$NON-NLS-2$
253 setMenuCreator(new IMenuCreator() {
256 public Menu getMenu(Menu parent) {
257 throw new UnsupportedOperationException();
261 public Menu getMenu(Control parent) {
263 menu = new Menu(parent);
264 refreshAutomaticallyItem = new MenuItem(menu, SWT.CHECK);
265 refreshAutomaticallyItem.setText(Messages.SCLConsoleView_RefreshAutomatically);
266 refreshAutomaticallyItem.setSelection(refreshAutomatically);
267 refreshAutomaticallyItem.addSelectionListener(new SelectionAdapter() {
269 public void widgetSelected(SelectionEvent e) {
270 setRefreshAutomatically(!refreshAutomatically, true);
278 public void dispose() {
286 console.getSession().getModuleRepository().getSourceRepository().checkUpdates();
287 console.getSession().updateRuntimeEnvironment(true);
288 console.appendOutput(Messages.SCLConsoleView_RefreshCompleted, console.greenColor, null);
291 toolBarManager.add(new Action(Messages.SCLConsoleView_ManageImports,
292 Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/configure_imports.png")) { //$NON-NLS-1$ //$NON-NLS-2$
299 // Show action for running SCL tests if in development mode
300 if (Platform.inDevelopmentMode()) {
301 toolBarManager.add(new Action(Messages.SCLConsoleView_RunTests,
302 Activator.imageDescriptorFromPlugin("org.simantics.scl.ui", "icons/run_tests.png")) { //$NON-NLS-1$ //$NON-NLS-2$
310 toolBarManager.update(true);
312 setRefreshAutomatically(store.getBoolean(REFRESH_AUTOMATICALLY), false);
313 addScriptDropSupport(console);
315 // Do this after the actions and SCLConsoleListener are
316 // registered because it can cause output to the console.
317 scheduleSetCurrentImports(readImportPreferences());
320 private class ScriptRunningDropTarget extends DropTargetAdapter {
322 public void dragEnter(DropTargetEvent event) {
323 if (event.detail == DND.DROP_DEFAULT) {
324 event.detail = DND.DROP_LINK;
329 public void dragOperationChanged(DropTargetEvent event) {
330 if (event.detail == DND.DROP_DEFAULT) {
331 event.detail = DND.DROP_LINK;
335 public void drop(DropTargetEvent event) {
336 if (FileTransfer.getInstance().isSupportedType(event.currentDataType)) {
337 String[] files = ((String[]) event.data).clone();
338 // Sort files by name to allow deterministic multi-file drop
340 for (String file : files) {
341 Path p = Paths.get(file).toAbsolutePath();
342 if (isScriptFile(p)) {
343 console.execute("runFromFile \"" + p.toString().replace('\\', '/') + "\""); //$NON-NLS-1$ //$NON-NLS-2$
349 private boolean isScriptFile(Path p) {
350 return Files.isRegularFile(p)
351 //&& p.toString().toLowerCase().endsWith(".scl")
356 private void addScriptDropSupport(SCLConsole console) {
357 DropTarget target = new DropTarget(console.getOutputWidget(), DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK | DND.DROP_DEFAULT);
358 target.setTransfer(new Transfer[] { FileTransfer.getInstance() });
359 target.addDropListener(new ScriptRunningDropTarget());
363 public void setFocus() {
368 public void dispose() {
373 @SuppressWarnings("unchecked")
375 public <T> T getAdapter(Class<T> adapter) {
376 if (adapter == CommandSession.class)
377 return (T) console.getSession();
378 if (adapter == SCLReportingHandler.class)
379 return (T) console.getHandler();
380 return super.getAdapter(adapter);