package com.redhat.ceylon.eclipse.core.launch; import static com.redhat.ceylon.compiler.java.Util.declClassName; import static com.redhat.ceylon.eclipse.code.complete.CodeCompletions.getLabelDescriptionFor; import static com.redhat.ceylon.eclipse.code.complete.CodeCompletions.getStyledDescriptionFor; import static com.redhat.ceylon.eclipse.code.outline.CeylonLabelProvider.getImageForDeclaration; import static com.redhat.ceylon.eclipse.code.outline.CeylonLabelProvider.getPackageLabel; import static com.redhat.ceylon.eclipse.core.launch.ICeylonLaunchConfigurationConstants.ID_CEYLON_APPLICATION; import static com.redhat.ceylon.eclipse.ui.CeylonResources.PACKAGE; import static org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants.ATTR_MAIN_TYPE_NAME; import static org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.modelJ2C; import static com.redhat.ceylon.eclipse.java2ceylon.Java2CeylonProxies.vfsJ2C; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationType; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.debug.ui.IDebugModelPresentation; import org.eclipse.debug.ui.ILaunchShortcut; import org.eclipse.jface.dialogs.IDialogSettings; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.viewers.DelegatingStyledCellLabelProvider; import org.eclipse.jface.viewers.ILabelDecorator; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StyledCellLabelProvider; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.window.Window; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.dialogs.ElementListSelectionDialog; import org.eclipse.ui.dialogs.FilteredItemsSelectionDialog; import com.redhat.ceylon.compiler.typechecker.TypeChecker; import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit; import com.redhat.ceylon.model.typechecker.model.Class; import com.redhat.ceylon.model.typechecker.model.Declaration; import com.redhat.ceylon.model.typechecker.model.Function; import com.redhat.ceylon.model.typechecker.model.TypeDeclaration; import com.redhat.ceylon.model.typechecker.model.TypedDeclaration; import com.redhat.ceylon.compiler.typechecker.tree.Node; import com.redhat.ceylon.compiler.typechecker.tree.Tree; import com.redhat.ceylon.eclipse.code.editor.CeylonEditor; import com.redhat.ceylon.eclipse.code.parse.CeylonParseController; import com.redhat.ceylon.eclipse.core.builder.CeylonBuilder; import com.redhat.ceylon.eclipse.ui.CeylonPlugin; import com.redhat.ceylon.eclipse.util.EditorUtil; import com.redhat.ceylon.eclipse.util.Nodes; import com.redhat.ceylon.ide.common.model.CeylonProject; public class CeylonApplicationLaunchShortcut implements ILaunchShortcut { @Override public void launch(ISelection selection, String mode) { if (! (selection instanceof IStructuredSelection)) { return; } IStructuredSelection structuredSelection = (IStructuredSelection) selection; List<IFile> files = new LinkedList<IFile>(); for (Object object : structuredSelection.toList()) { if (object instanceof IAdaptable) { IResource resource = (IResource) ((IAdaptable)object).getAdapter(IResource.class); if (resource != null) { addFiles(files, resource); } } } searchAndLaunch(files, mode); } public void addFiles(List<IFile> files, IResource resource) { switch (resource.getType()) { case IResource.FILE: IFile file = (IFile) resource; IPath path = file.getFullPath(); //getProjectRelativePath(); if (path!=null && "ceylon".equals(path.getFileExtension()) ) { files.add(file); } break; case IResource.FOLDER: case IResource.PROJECT: IContainer folder = (IContainer) resource; try { for (IResource child: folder.members()) { addFiles(files, child); } } catch (CoreException e) { e.printStackTrace(); } break; } } @Override public void launch(IEditorPart editor, String mode) { IFile file = EditorUtil.getFile(editor.getEditorInput()); if (editor instanceof CeylonEditor) { CeylonEditor ce = (CeylonEditor) editor; CeylonParseController cpc = ce.getParseController(); if (cpc!=null) { Tree.CompilationUnit cu = cpc.getLastCompilationUnit(); if (cu!=null) { ISelection selection = ce.getSelectionProvider().getSelection(); if (selection instanceof ITextSelection) { Node node = Nodes.findToplevelStatement(cu, Nodes.findNode(cu, cpc.getTokens(), (ITextSelection) selection)); if (node instanceof Tree.AnyMethod) { Function method = ((Tree.AnyMethod) node).getDeclarationModel(); if (method.isToplevel() && method.isShared() && !method.getParameterLists().isEmpty() && method.getParameterLists().get(0).getParameters().isEmpty()) { launch(method, file, mode); return; } } if (node instanceof Tree.AnyClass) { Class clazz = ((Tree.AnyClass) node).getDeclarationModel(); if (clazz.isToplevel() && clazz.isShared() && !clazz.isAbstract() && clazz.getParameterList()!=null && clazz.getParameterList().getParameters().isEmpty()) { launch(clazz, file, mode); return; } } } } } } searchAndLaunch(Arrays.asList(file), mode); } private void searchAndLaunch(List<IFile> files, String mode) { List<Declaration> topLevelDeclarations = new LinkedList<Declaration>(); List<IFile> correspondingfiles = new LinkedList<IFile>(); for (IFile file : files) { IProject project = file.getProject(); CeylonProject<IProject, IResource, IFolder, IFile> ceylonProject = modelJ2C().ceylonModel().getProject(project); TypeChecker typeChecker = CeylonBuilder.getProjectTypeChecker(project); if (typeChecker != null) { PhasedUnit phasedUnit = typeChecker.getPhasedUnits() .getPhasedUnit(vfsJ2C().createVirtualFile(file, ceylonProject.getIdeArtifact())); if (phasedUnit!=null) { List<Declaration> declarations = phasedUnit.getDeclarations(); for (Declaration d : declarations) { boolean candidateDeclaration = true; if (!d.isToplevel() || !d.isShared()) { candidateDeclaration = false; } if (d instanceof Function) { Function methodDecl = (Function) d; if (!methodDecl.getParameterLists().isEmpty() && !methodDecl.getParameterLists().get(0).getParameters().isEmpty()) { candidateDeclaration = false; } } else if (d instanceof Class) { Class classDecl = (Class) d; if (classDecl.isAbstract() || classDecl.getParameterList()==null || !classDecl.getParameterList().getParameters().isEmpty()) { candidateDeclaration = false; } } else { candidateDeclaration = false; } if (candidateDeclaration) { topLevelDeclarations.add(d); correspondingfiles.add(file); } } } } } Declaration declarationToRun = null; IFile fileToRun = null; if (topLevelDeclarations.size() == 0) { MessageDialog.openError(EditorUtil.getShell(), "Ceylon Launcher", "No runnable function or class.\n(Only shared toplevel functions and classes with no parameters are runnable.)"); } else if (topLevelDeclarations.size() > 1) { declarationToRun = chooseDeclaration(topLevelDeclarations); if (declarationToRun!=null) { fileToRun = correspondingfiles.get(topLevelDeclarations.indexOf(declarationToRun)); } } else { declarationToRun = topLevelDeclarations.get(0); fileToRun = correspondingfiles.get(0); } if (declarationToRun != null) { launch(declarationToRun, fileToRun, mode); } } private static final String SETTINGS_ID = CeylonPlugin.PLUGIN_ID + ".TOPLEVEL_DECLARATION_SELECTION_DIALOG"; public static Declaration chooseDeclaration(final List<Declaration> declarations) { FilteredItemsSelectionDialog sd = new FilteredItemsSelectionDialog(EditorUtil.getShell()) { { setTitle("Ceylon Launcher"); setMessage("Select the toplevel method or class to launch:"); setListLabelProvider(new LabelProvider()); setDetailsLabelProvider(new DetailsLabelProvider()); setListSelectionLabelDecorator(new SelectionLabelDecorator()); } @Override protected Control createExtendedContentArea(Composite parent) { return null; } @Override protected IDialogSettings getDialogSettings() { IDialogSettings settings = CeylonPlugin.getInstance().getDialogSettings(); IDialogSettings section = settings.getSection(SETTINGS_ID); if (section == null) { section = settings.addNewSection(SETTINGS_ID); } return section; } @Override protected IStatus validateItem(Object item) { return Status.OK_STATUS; } @Override protected ItemsFilter createFilter() { return new ItemsFilter() { @Override public boolean matchItem(Object item) { return matches(getElementName(item)); } @Override public boolean isConsistentItem(Object item) { return true; } @Override public String getPattern() { String pattern = super.getPattern(); return pattern.isEmpty() ? "**" : pattern; } }; } @Override protected Comparator<?> getItemsComparator() { Comparator<Object> comp = new Comparator<Object>() { public int compare(Object o1, Object o2) { if(o1 instanceof Declaration && o2 instanceof Declaration) { if (o1 instanceof TypedDeclaration && o2 instanceof TypeDeclaration) { return -1; } else if (o2 instanceof TypedDeclaration && o1 instanceof TypeDeclaration) { return 1; } else { return ((Declaration)o1).getName().compareTo(((Declaration)o2).getName()); } } return 0; } }; return comp; } @Override protected void fillContentProvider( AbstractContentProvider contentProvider, ItemsFilter itemsFilter, IProgressMonitor progressMonitor) throws CoreException { if(declarations != null) { for(Declaration d : declarations) { if(itemsFilter.isConsistentItem(d)) { contentProvider.add(d, itemsFilter); } } } } @Override public String getElementName(Object item) { return ((Declaration) item).getName(); } }; if (sd.open() == Window.OK) { return (Declaration)sd.getFirstResult(); } return null; } static class LabelProvider extends StyledCellLabelProvider implements DelegatingStyledCellLabelProvider.IStyledLabelProvider, ILabelProvider { @Override public void addListener(ILabelProviderListener listener) {} @Override public void dispose() {} @Override public boolean isLabelProperty(Object element, String property) { return false; } @Override public void removeListener(ILabelProviderListener listener) {} @Override public Image getImage(Object element) { Declaration d = (Declaration) element; return d==null ? null : getImageForDeclaration(d); } @Override public String getText(Object element) { Declaration d = (Declaration) element; return d==null ? null : getLabelDescriptionFor(d); } @Override public StyledString getStyledText(Object element) { if (element==null) { return new StyledString(); } else { Declaration d = (Declaration) element; return getStyledDescriptionFor(d); } } @Override public void update(ViewerCell cell) { Object element = cell.getElement(); if (element!=null) { StyledString styledText = getStyledText(element); cell.setText(styledText.toString()); cell.setStyleRanges(styledText.getStyleRanges()); cell.setImage(getImage(element)); super.update(cell); } } } static class DetailsLabelProvider implements ILabelProvider { @Override public void removeListener(ILabelProviderListener listener) {} @Override public boolean isLabelProperty(Object element, String property) { return false; } @Override public void dispose() {} @Override public void addListener(ILabelProviderListener listener) {} @Override public String getText(Object element) { return getPackageLabel((Declaration) element); } @Override public Image getImage(Object element) { return PACKAGE; } } static class SelectionLabelDecorator implements ILabelDecorator { @Override public void removeListener(ILabelProviderListener listener) {} @Override public boolean isLabelProperty(Object element, String property) { return false; } @Override public void dispose() {} @Override public void addListener(ILabelProviderListener listener) {} @Override public String decorateText(String text, Object element) { return text + " \u2014 " + getPackageLabel((Declaration) element); } @Override public Image decorateImage(Image image, Object element) { return null; } } protected String canLaunch(Declaration declarationToRun, IFile fileToRun, String mode) { if (!CeylonBuilder.compileToJava(fileToRun.getProject())) { return "JVM compilation is not enabled for this project"; } return null; } private void launch(Declaration declarationToRun, IFile fileToRun, String mode) { String err = canLaunch(declarationToRun, fileToRun, mode); if (err != null) { MessageDialog.openError(EditorUtil.getShell(), "Ceylon Launcher Error", err); return; } ILaunchConfiguration config = findLaunchConfiguration(declarationToRun, fileToRun, getConfigurationType()); if (config == null) { config = createConfiguration(declarationToRun, fileToRun); } if (config != null) { DebugUITools.launch(config, mode); } } protected ILaunchConfigurationType getConfigurationType() { return getLaunchManager().getLaunchConfigurationType(ID_CEYLON_APPLICATION); } protected ILaunchManager getLaunchManager() { return DebugPlugin.getDefault().getLaunchManager(); } /** * Finds and returns an <b>existing</b> configuration to re-launch for the given type, * or <code>null</code> if there is no existing configuration. * * @return a configuration to use for launching the given type or <code>null</code> if none */ protected ILaunchConfiguration findLaunchConfiguration(Declaration declaration, IFile file, ILaunchConfigurationType configType) { List<ILaunchConfiguration> candidateConfigs = Collections.<ILaunchConfiguration>emptyList(); try { ILaunchConfiguration[] configs = DebugPlugin.getDefault().getLaunchManager() .getLaunchConfigurations(configType); candidateConfigs = new ArrayList<ILaunchConfiguration>(configs.length); String mainClass = getJavaClassName(declaration); for (int i = 0; i < configs.length; i++) { ILaunchConfiguration config = configs[i]; if (config.getAttribute(ATTR_MAIN_TYPE_NAME, "") .equals(mainClass)) { if (config.getAttribute(ATTR_PROJECT_NAME, "") .equals(file.getProject().getName())) { candidateConfigs.add(config); } } } } catch (CoreException e) { e.printStackTrace(); // TODO : Use a logger } int candidateCount = candidateConfigs.size(); if (candidateCount == 1) { return candidateConfigs.get(0); } else if (candidateCount > 1) { return chooseConfiguration(candidateConfigs); } return null; } /** * Returns a configuration from the given collection of configurations that should be launched, * or <code>null</code> to cancel. Default implementation opens a selection dialog that allows * the user to choose one of the specified launch configurations. Returns the chosen configuration, * or <code>null</code> if the user cancels. * * @param configList list of configurations to choose from * @return configuration to launch or <code>null</code> to cancel */ protected ILaunchConfiguration chooseConfiguration(List<ILaunchConfiguration> configList) { IDebugModelPresentation labelProvider = DebugUITools.newDebugModelPresentation(); ElementListSelectionDialog dialog= new ElementListSelectionDialog(EditorUtil.getShell(), labelProvider); dialog.setElements(configList.toArray()); dialog.setTitle("Ceylon Launcher"); dialog.setMessage("Please choose a configuration to start the Ceylon application"); dialog.setMultipleSelection(false); int result = dialog.open(); labelProvider.dispose(); if (result == Window.OK) { return (ILaunchConfiguration) dialog.getFirstResult(); } return null; } protected ILaunchConfiguration createConfiguration(Declaration declarationToRun, IFile file) { try { ILaunchConfigurationType configType = getConfigurationType(); String configurationName = ""; if (declarationToRun instanceof Class) { configurationName += "class "; } else { if (declarationToRun instanceof Function) { Function method = (Function) declarationToRun; if (method.isDeclaredVoid()) { configurationName += "void "; } else { configurationName += "function "; } } } configurationName += declarationToRun.getName() + "() \u2014 "; String packageName = declarationToRun.getContainer().getQualifiedNameString(); configurationName += packageName.isEmpty() ? "default package" : packageName; // configurationName = configurationName.replaceAll("[\u00c0-\ufffe]", "_"); String lcn = getLaunchManager() .generateLaunchConfigurationName(configurationName); ILaunchConfigurationWorkingCopy wc = configType.newInstance(null, lcn); wc.setAttribute(ATTR_MAIN_TYPE_NAME, getJavaClassName(declarationToRun)); wc.setAttribute(ATTR_PROJECT_NAME, file.getProject().getName()); wc.setMappedResources(new IResource[] {file}); return wc.doSave(); } catch (CoreException exception) { MessageDialog.openError(EditorUtil.getShell(), "Ceylon Launcher Error", exception.getStatus().getMessage()); return null; } } private String getJavaClassName(Declaration declaration) { String name = declClassName(declaration.getQualifiedNameString()); if(declaration instanceof Function) name += "_"; return name; } }