package edu.ucsd.arcum.ui.editor; import static edu.ucsd.arcum.ArcumPlugin.DEBUG; import java.util.ArrayList; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.jface.action.*; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; import org.eclipse.jface.viewers.*; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Menu; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.part.FileEditorInput; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.views.contentoutline.ContentOutlinePage; import edu.ucsd.arcum.interpreter.ast.TopLevelConstruct; import edu.ucsd.arcum.interpreter.ast.Option; import edu.ucsd.arcum.interpreter.parser.ArcumSourceFileParser; import edu.ucsd.arcum.interpreter.query.ArcumDeclarationTable; import edu.ucsd.arcum.ui.UIUtil; import edu.ucsd.arcum.ui.actions.DisabledAction; import edu.ucsd.arcum.ui.actions.TransformAction; public class ArcumFileContentPage extends ContentOutlinePage implements IDocumentListener { private static final String MENU_ID = "edu.ucsd.arcum.arcumFileContentPage"; private IDocumentProvider documentProvider; private IEditorInput editorInput; private List<TopLevelSourceEntry> entries; private long previousEdit; // initialized after createControl is called private IDocument document; // // Next to do is to add in the darn menu items: but only if we can find the // associated project with the document: then, it's refactor time! // public ArcumFileContentPage(ArcumEditorPart editor, IDocumentProvider documentProvider, IEditorInput input) { this.documentProvider = documentProvider; this.editorInput = input; this.entries = new ArrayList<TopLevelSourceEntry>(); this.previousEdit = -1; } @Override public void createControl(Composite parent) { super.createControl(parent); this.document = documentProvider.getDocument(editorInput); document.addDocumentListener(this); TreeViewer viewer = getTreeViewer(); viewer.setContentProvider(new ProgramConstructs()); viewer.setLabelProvider(new LabelProvider()); viewer.addSelectionChangedListener(this); viewer.setInput(editorInput); createContextMenu(viewer); } private void createContextMenu(final TreeViewer viewer) { MenuManager menuMgr = new MenuManager("#PopupMenu"); menuMgr.setRemoveAllWhenShown(true); menuMgr.addMenuListener(new IMenuListener() { public void menuAboutToShow(IMenuManager manager) { IStructuredSelection selection; selection = (IStructuredSelection)viewer.getSelection(); if (!selection.isEmpty()) { Object object = selection.getFirstElement(); if (object instanceof ConceptMapEntry) { ConceptMapEntry entry = (ConceptMapEntry)object; addOptionActions(manager, entry); } } // Other plug-ins can contribute their actions here manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS)); } private void addOptionActions(IMenuManager manager, final ConceptMapEntry entry) { if (UIUtil.existsUnsavedEditor()) { DisabledAction message = new DisabledAction( "You must save all editors first"); manager.add(message); return; } if (editorInput instanceof FileEditorInput) { FileEditorInput fileEditorInput; IFile file; final IProject proj; fileEditorInput = (FileEditorInput)editorInput; file = fileEditorInput.getFile(); proj = file.getProject(); ArcumDeclarationTable declTable; Option option; TopLevelConstruct optionInterface; List<Option> options; declTable = ArcumDeclarationTable.lookupSymbolTable(proj); option = declTable.lookup(entry.getOptionName(), Option.class); if (option == null) { DisabledAction message = new DisabledAction( String.format("The option \'%s\' has not been defined", entry.getOptionName())); manager.add(message); return; } optionInterface = option.getOptionInterface(); options = declTable.getImplementingOptions(optionInterface); options.remove(option); for (Option alternative: options) { TransformAction action; action = new TransformAction(declTable, option, entry, alternative, documentProvider); manager.add(action); } if (options.size() == 0) { DisabledAction message = new DisabledAction( String.format("The option \'%s\' has no alternatives", option.getName())); manager.add(message); } } else { String msg = "No project is associated with this document"; Action action = new DisabledAction(msg); manager.add(action); } } }); Menu menu = menuMgr.createContextMenu(viewer.getControl()); viewer.getControl().setMenu(menu); getSite().registerContextMenu(MENU_ID, menuMgr, viewer); } @Override public void selectionChanged(SelectionChangedEvent event) { super.selectionChanged(event); } private final class ProgramConstructs implements ITreeContentProvider { public Object[] getChildren(Object parentElement) { return new Object[0]; } public Object getParent(Object element) { return null; } public boolean hasChildren(Object element) { return false; } public Object[] getElements(Object inputElement) { return entries.toArray(); } public void dispose() { } public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { quickParseDocument(); } } public void documentAboutToBeChanged(DocumentEvent event) { // intentionally left blank } public void documentChanged(DocumentEvent event) { //// quickParseDocument(); // if (bigEvent(event)) { // quickParseDocument(); // } } // MACNEIL: this is certainly not ideal: also should look at where the edits // are made and whatever other tricks JDT uses private boolean bigEvent(DocumentEvent event) { long currentTime = System.currentTimeMillis(); if (previousEdit == -1) { previousEdit = currentTime; return true; } final long ONE_SECOND = 1 * 1000; if (currentTime - previousEdit > ONE_SECOND) { previousEdit = currentTime; return true; } return false; } int numParsed = 0; private synchronized void quickParseDocument() { entries.clear(); entries.addAll(ArcumSourceFileParser.quickParse(document)); if (DEBUG) System.out.printf("I've been parsed %s times%n", numParsed++); getTreeViewer().refresh(); } }