|
From: <caw...@us...> - 2007-03-16 13:23:32
|
Revision: 2189
http://svn.sourceforge.net/rubyeclipse/?rev=2189&view=rev
Author: cawilliams
Date: 2007-03-16 06:23:28 -0700 (Fri, 16 Mar 2007)
Log Message:
-----------
implement some of the quick fix infrastructure
Modified Paths:
--------------
trunk/org.rubypeople.rdt.ui/META-INF/MANIFEST.MF
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/RubyPluginImages.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/rubyeditor/RubyAbstractEditor.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/rubyeditor/RubyEditorActionContributor.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/CorrectionMessages.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/RubyCorrectionProcessor.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/text/RubySourceViewerConfiguration.java
Added Paths:
-----------
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/AssistContext.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ChangeCorrectionProposal.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/CorrectionCommandHandler.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ICommandAccess.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/IStatusLineProposal.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/MarkerResolutionProposal.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ProblemLocation.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/RubyCorrectionAssistant.java
trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/text/ruby/IQuickAssistProcessor.java
Modified: trunk/org.rubypeople.rdt.ui/META-INF/MANIFEST.MF
===================================================================
--- trunk/org.rubypeople.rdt.ui/META-INF/MANIFEST.MF 2007-03-16 13:21:44 UTC (rev 2188)
+++ trunk/org.rubypeople.rdt.ui/META-INF/MANIFEST.MF 2007-03-16 13:23:28 UTC (rev 2189)
@@ -55,5 +55,6 @@
org.eclipse.search,
org.jruby,
org.eclipse.compare,
- org.eclipse.core.expressions
+ org.eclipse.core.expressions,
+ org.eclipse.ltk.core.refactoring
Eclipse-LazyStart: true
Modified: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/RubyPluginImages.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/RubyPluginImages.java 2007-03-16 13:21:44 UTC (rev 2188)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/RubyPluginImages.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -133,9 +133,10 @@
public static final ImageDescriptor DESC_MISC_PRIVATE= createManagedFromKey(T_OBJ, IMG_MISC_PRIVATE);
public static final ImageDescriptor DESC_OBJS_UNKNOWN= createManagedFromKey(T_OBJ, IMG_OBJS_UNKNOWN);
-
+ public static final String IMG_CORRECTION_CHANGE= NAME_PREFIX + "correction_change.gif"; //$NON-NLS-1$
static {
+ createManagedFromKey(T_OBJ, IMG_CORRECTION_CHANGE);
createManagedFromKey(T_OBJ, IMG_OBJS_FIXABLE_ERROR);
createManagedFromKey(T_OBJ, IMG_OBJS_FIXABLE_PROBLEM);
createManagedFromKey(T_OBJ, IMG_OBJS_ERROR);
Modified: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/rubyeditor/RubyAbstractEditor.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/rubyeditor/RubyAbstractEditor.java 2007-03-16 13:21:44 UTC (rev 2188)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/rubyeditor/RubyAbstractEditor.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -11,6 +11,7 @@
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
@@ -94,6 +95,8 @@
public abstract class RubyAbstractEditor extends TextEditor {
+ private static final boolean CODE_ASSIST_DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.rubypeople.rdt.ui/debug/ResultCollector")); //$NON-NLS-1$//$NON-NLS-2$
+
protected RubyTextTools textTools;
private ISourceReference reference;
@@ -1092,7 +1095,47 @@
return false;
return super.requestWidgetToken(requester, priority);
}
+
+ /*
+ * @see ITextOperationTarget#doOperation(int)
+ */
+ public void doOperation(int operation) {
+ if (getTextWidget() == null)
+ return;
+
+ switch (operation) {
+ case CONTENTASSIST_PROPOSALS:
+ long time= CODE_ASSIST_DEBUG ? System.currentTimeMillis() : 0;
+ String msg= fContentAssistant.showPossibleCompletions();
+ if (CODE_ASSIST_DEBUG) {
+ long delta= System.currentTimeMillis() - time;
+ System.err.println("Code Assist (total): " + delta); //$NON-NLS-1$
+ }
+ setStatusLineErrorMessage(msg);
+ return;
+ case QUICK_ASSIST:
+ /*
+ * XXX: We can get rid of this once the SourceViewer has a way to update the status line
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=133787
+ */
+ msg= fQuickAssistAssistant.showPossibleQuickAssists();
+ setStatusLineErrorMessage(msg);
+ return;
+ case UNDO:
+ fIgnoreTextConverters= true;
+ super.doOperation(operation);
+ fIgnoreTextConverters= false;
+ return;
+ case REDO:
+ fIgnoreTextConverters= true;
+ super.doOperation(operation);
+ fIgnoreTextConverters= false;
+ return;
+ }
+
+ super.doOperation(operation);
+ }
}
/**
* Internal activation listener.
Modified: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/rubyeditor/RubyEditorActionContributor.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/rubyeditor/RubyEditorActionContributor.java 2007-03-16 13:21:44 UTC (rev 2188)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/rubyeditor/RubyEditorActionContributor.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -8,6 +8,7 @@
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchActionConstants;
+import org.eclipse.ui.ide.IDEActionFactory;
import org.eclipse.ui.texteditor.BasicTextEditorActionContributor;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
@@ -22,6 +23,7 @@
protected RetargetTextEditorAction contentAssistProposal;
private RetargetTextEditorAction fGotoMatchingBracket;
+ private RetargetTextEditorAction fQuickAssistAction;
public RubyEditorActionContributor() {
super();
@@ -32,6 +34,9 @@
fGotoMatchingBracket = new RetargetTextEditorAction(b, "GotoMatchingBracket."); //$NON-NLS-1$
fGotoMatchingBracket
.setActionDefinitionId(IRubyEditorActionDefinitionIds.GOTO_MATCHING_BRACKET);
+
+ fQuickAssistAction= new RetargetTextEditorAction(RubyEditorMessages.getBundleForConstructedKeys(), "CorrectionAssistProposal."); //$NON-NLS-1$
+ fQuickAssistAction.setActionDefinitionId(ITextEditorActionDefinitionIds.QUICK_ASSIST);
}
@@ -40,6 +45,7 @@
if (editMenu != null) {
editMenu.add(new Separator());
editMenu.add(contentAssistProposal);
+ editMenu.add(fQuickAssistAction);
}
IMenuManager gotoMenu= menuManager.findMenuUsingPath("navigate/goTo"); //$NON-NLS-1$
@@ -47,6 +53,8 @@
gotoMenu.add(new Separator("additions2")); //$NON-NLS-1$
gotoMenu.appendToGroup("additions2", fGotoMatchingBracket); //$NON-NLS-1$
}
+
+
}
public void setActiveEditor(IEditorPart part) {
@@ -58,6 +66,7 @@
contentAssistProposal.setAction(getAction(textEditor, "ContentAssistProposal"));
fGotoMatchingBracket.setAction(getAction(textEditor,
GotoMatchingBracketAction.GOTO_MATCHING_BRACKET));
+ fQuickAssistAction.setAction(getAction(textEditor, ITextEditorActionConstants.QUICK_ASSIST));
if (part instanceof RubyEditor) {
@@ -84,5 +93,9 @@
actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.GOTO_PREVIOUS_ANNOTATION,
action);
actionBars.setGlobalActionHandler(ITextEditorActionConstants.PREVIOUS, action);
+
+
+ actionBars.setGlobalActionHandler(IDEActionFactory.ADD_TASK.getId(), getAction(textEditor, IDEActionFactory.ADD_TASK.getId()));
+ actionBars.setGlobalActionHandler(IDEActionFactory.BOOKMARK.getId(), getAction(textEditor, IDEActionFactory.BOOKMARK.getId()));
}
}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/AssistContext.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/AssistContext.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/AssistContext.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -0,0 +1,103 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.ui.text.correction;
+
+import org.jruby.ast.Node;
+import org.jruby.ast.RootNode;
+import org.rubypeople.rdt.core.IRubyScript;
+import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.internal.core.parser.RubyParser;
+import org.rubypeople.rdt.internal.core.util.DOMFinder;
+import org.rubypeople.rdt.internal.ti.util.OffsetNodeLocator;
+import org.rubypeople.rdt.internal.ui.RubyPlugin;
+import org.rubypeople.rdt.ui.text.ruby.IInvocationContext;
+
+/**
+ */
+public class AssistContext implements IInvocationContext {
+
+ private IRubyScript fRubyScript;
+ private int fOffset;
+ private int fLength;
+
+ private RootNode fASTRoot;
+
+ /*
+ * Constructor for CorrectionContext.
+ */
+ public AssistContext(IRubyScript cu, int offset, int length) {
+ fRubyScript= cu;
+ fOffset= offset;
+ fLength= length;
+
+ fASTRoot= null;
+ }
+
+ /**
+ * Returns the compilation unit.
+ * @return Returns a IRubyScript
+ */
+ public IRubyScript getRubyScript() {
+ return fRubyScript;
+ }
+
+ /**
+ * Returns the length.
+ * @return int
+ */
+ public int getSelectionLength() {
+ return fLength;
+ }
+
+ /**
+ * Returns the offset.
+ * @return int
+ */
+ public int getSelectionOffset() {
+ return fOffset;
+ }
+
+ public RootNode getASTRoot() {
+ if (fASTRoot == null) {
+ try {
+ RubyParser parser = new RubyParser();
+ fASTRoot= (RootNode) parser.parse(fRubyScript.getSource());
+ } catch (RubyModelException e) {
+ RubyPlugin.log(e);
+ }
+ }
+ return fASTRoot;
+ }
+
+
+ /**
+ * @param root The ASTRoot to set.
+ */
+ public void setASTRoot(RootNode root) {
+ fASTRoot= root;
+ }
+
+ /*(non-Javadoc)
+ * @see org.eclipse.jdt.ui.text.java.IInvocationContext#getCoveringNode()
+ */
+ public Node getCoveringNode() {
+ return OffsetNodeLocator.Instance().getNodeAtOffset(getASTRoot(), fOffset);
+ }
+
+ /*(non-Javadoc)
+ * @see org.eclipse.jdt.ui.text.java.IInvocationContext#getCoveredNode()
+ */
+ public Node getCoveredNode() {
+ // FIXME Figure out what the difference is between these two!
+ return getCoveringNode();
+ }
+
+}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ChangeCorrectionProposal.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ChangeCorrectionProposal.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ChangeCorrectionProposal.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -0,0 +1,261 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.rubypeople.rdt.internal.ui.text.correction;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRewriteTarget;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.link.LinkedModeModel;
+import org.eclipse.ltk.core.refactoring.Change;
+import org.eclipse.ltk.core.refactoring.NullChange;
+import org.eclipse.ltk.core.refactoring.RefactoringStatus;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IEditorPart;
+import org.rubypeople.rdt.internal.core.util.Messages;
+import org.rubypeople.rdt.internal.ui.RubyPlugin;
+import org.rubypeople.rdt.internal.ui.util.ExceptionHandler;
+import org.rubypeople.rdt.ui.text.ruby.IRubyCompletionProposal;
+
+/**
+ * Implementation of a Ruby completion proposal to be used for quick fix and quick assist
+ * proposals that invoke a {@link Change}. The proposal offers a proposal information but no context
+ * information.
+ *
+ * @since 3.2
+ */
+public class ChangeCorrectionProposal implements IRubyCompletionProposal, ICommandAccess {
+
+ private Change fChange;
+ private String fName;
+ private int fRelevance;
+ private Image fImage;
+ private String fCommandId;
+
+ /**
+ * Constructs a change correction proposal.
+ *
+ * @param name The name that is displayed in the proposal selection dialog.
+ * @param change The change that is executed when the proposal is applied or <code>null</code>
+ * if the change will be created by implementors of {@link #createChange()}.
+ * @param relevance The relevance of this proposal.
+ * @param image The image that is displayed for this proposal or <code>null</code> if no
+ * image is desired.
+ */
+ public ChangeCorrectionProposal(String name, Change change, int relevance, Image image) {
+ if (name == null) {
+ throw new IllegalArgumentException("Name must not be null"); //$NON-NLS-1$
+ }
+ fName= name;
+ fChange= change;
+ fRelevance= relevance;
+ fImage= image;
+ fCommandId= null;
+ }
+
+ /*
+ * @see ICompletionProposal#apply(IDocument)
+ */
+ public void apply(IDocument document) {
+ try {
+ performChange(RubyPlugin.getActivePage().getActiveEditor(), document);
+ } catch (CoreException e) {
+ ExceptionHandler.handle(e, CorrectionMessages.ChangeCorrectionProposal_error_title, CorrectionMessages.ChangeCorrectionProposal_error_message);
+ }
+ }
+
+ /**
+ * Performs the change associated with this proposal.
+ *
+ * @param activeEditor The editor currently active or <code>null</code> if no
+ * editor is active.
+ * @param document The document of the editor currently active or <code>null</code> if
+ * no editor is visible.
+ * @throws CoreException Thrown when the invocation of the change failed.
+ */
+ protected void performChange(IEditorPart activeEditor, IDocument document) throws CoreException {
+ Change change= null;
+ IRewriteTarget rewriteTarget= null;
+ try {
+ change= getChange();
+ if (change != null) {
+ if (document != null) {
+ LinkedModeModel.closeAllModels(document);
+ }
+ if (activeEditor != null) {
+ rewriteTarget= (IRewriteTarget) activeEditor.getAdapter(IRewriteTarget.class);
+ if (rewriteTarget != null) {
+ rewriteTarget.beginCompoundChange();
+ }
+ }
+
+ change.initializeValidationData(new NullProgressMonitor());
+ RefactoringStatus valid= change.isValid(new NullProgressMonitor());
+ if (valid.hasFatalError()) {
+ IStatus status= new Status(IStatus.ERROR, RubyPlugin.getPluginId(), IStatus.ERROR,
+ valid.getMessageMatchingSeverity(RefactoringStatus.FATAL), null);
+ throw new CoreException(status);
+ } else {
+ change.perform(new NullProgressMonitor());
+ }
+ }
+ } finally {
+ if (rewriteTarget != null) {
+ rewriteTarget.endCompoundChange();
+ }
+
+ if (change != null) {
+ change.dispose();
+ }
+ }
+ }
+
+ /*
+ * @see ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ StringBuffer buf= new StringBuffer();
+ buf.append("<p>"); //$NON-NLS-1$
+ try {
+ Change change= getChange();
+ if (change != null) {
+ String name= change.getName();
+ if (name.length() == 0) {
+ return null;
+ }
+ buf.append(name);
+ } else {
+ return null;
+ }
+ } catch (CoreException e) {
+ buf.append("Unexpected error when accessing this proposal:<p><pre>"); //$NON-NLS-1$
+ buf.append(e.getLocalizedMessage());
+ buf.append("</pre>"); //$NON-NLS-1$
+ }
+ buf.append("</p>"); //$NON-NLS-1$
+ return buf.toString();
+ }
+
+ /*
+ * @see ICompletionProposal#getContextInformation()
+ */
+ public IContextInformation getContextInformation() {
+ return null;
+ }
+
+ /*
+ * @see ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ String shortCutString= CorrectionCommandHandler.getShortCutString(getCommandId());
+ if (shortCutString != null) {
+ return Messages.format(CorrectionMessages.ChangeCorrectionProposal_name_with_shortcut, new String[] { fName, shortCutString });
+ }
+ return fName;
+ }
+
+ /*
+ * @see ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ return fImage;
+ }
+
+ /*
+ * @see ICompletionProposal#getSelection(IDocument)
+ */
+ public Point getSelection(IDocument document) {
+ return null;
+ }
+
+ /**
+ * Sets the proposal's image or <code>null</code> if no image is desired.
+ *
+ * @param image the desired image.
+ */
+ public void setImage(Image image) {
+ fImage= image;
+ }
+
+ /**
+ * Returns the change that will be executed when the proposal is applied.
+ *
+ * @return returns the change for this proposal.
+ */
+ public final Change getChange() throws CoreException {
+ if (fChange == null) {
+ fChange= createChange();
+ }
+ return fChange;
+ }
+
+ /**
+ * Creates the text change for this proposal.
+ * This method is only called once and only when no text change has been passed in
+ * {@link #ChangeCorrectionProposal(String, Change, int, Image)}.
+ *
+ * @return returns the created change.
+ * @throws CoreException thrown if the creation of the change failed.
+ */
+ protected Change createChange() throws CoreException {
+ return new NullChange();
+ }
+
+ /**
+ * Sets the display name.
+ *
+ * @param name the name to set
+ */
+ public void setDisplayName(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("Name must not be null"); //$NON-NLS-1$
+ }
+ fName= name;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jdt.ui.text.java.IRubyCompletionProposal#getRelevance()
+ */
+ public int getRelevance() {
+ return fRelevance;
+ }
+
+ /**
+ * Sets the relevance.
+ * @param relevance the relevance to set
+ */
+ public void setRelevance(int relevance) {
+ fRelevance= relevance;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jdt.internal.ui.text.correction.IShortcutProposal#getProposalId()
+ */
+ public String getCommandId() {
+ return fCommandId;
+ }
+
+ /**
+ * Set the proposal id to allow assigning a shortcut to the correction proposal.
+ *
+ * @param commandId The proposal id for this proposal or <code>null</code> if no command
+ * should be assigned to this proposal.
+ */
+ public void setCommandId(String commandId) {
+ fCommandId= commandId;
+ }
+
+}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/CorrectionCommandHandler.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/CorrectionCommandHandler.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/CorrectionCommandHandler.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -0,0 +1,160 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.rubypeople.rdt.internal.ui.text.correction;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.jface.bindings.TriggerSequence;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.keys.IBindingService;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.jruby.ast.Node;
+import org.rubypeople.rdt.core.IRubyScript;
+import org.rubypeople.rdt.internal.ui.rubyeditor.RubyEditor;
+import org.rubypeople.rdt.ui.RubyUI;
+import org.rubypeople.rdt.ui.text.ruby.IInvocationContext;
+
+
+/**
+ * Handler to be used to run a quick fix or assist by keyboard shortcut
+ */
+public class CorrectionCommandHandler extends AbstractHandler {
+
+ private final ITextEditor fEditor;
+ private final String fId;
+ private final boolean fIsAssist;
+
+ public CorrectionCommandHandler(ITextEditor editor, String id, boolean isAssist) {
+ fEditor= editor;
+ fId= id;
+ fIsAssist= isAssist;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ ISelection selection= fEditor.getSelectionProvider().getSelection();
+ IRubyScript cu= RubyUI.getWorkingCopyManager().getWorkingCopy(fEditor.getEditorInput());
+ IAnnotationModel model= RubyUI.getDocumentProvider().getAnnotationModel(fEditor.getEditorInput());
+ if (selection instanceof ITextSelection && cu != null && model != null) {
+ ICompletionProposal proposal= findCorrection(fId, fIsAssist, (ITextSelection) selection, cu, model);
+ if (proposal != null) {
+ invokeProposal(proposal, ((ITextSelection) selection).getOffset());
+ }
+ }
+ return null;
+ }
+
+ private ICompletionProposal findCorrection(String id, boolean isAssist, ITextSelection selection, IRubyScript cu, IAnnotationModel model) {
+ AssistContext context= new AssistContext(cu, selection.getOffset(), selection.getLength());
+ Collection proposals= new ArrayList(10);
+ if (isAssist) {
+// if (id.equals(LinkedNamesAssistProposal.ASSIST_ID)) {
+// return getLocalRenameProposal(context); // shortcut for local rename
+// }
+ RubyCorrectionProcessor.collectAssists(context, new ProblemLocation[0], proposals);
+ } else {
+ try {
+ boolean goToClosest= selection.getLength() == 0;
+ Annotation[] annotations= getAnnotations(selection.getOffset(), goToClosest);
+ RubyCorrectionProcessor.collectProposals(context, model, annotations, true, false, proposals);
+ } catch (BadLocationException e) {
+ return null;
+ }
+ }
+ for (Iterator iter= proposals.iterator(); iter.hasNext();) {
+ Object curr= iter.next();
+ if (curr instanceof ICommandAccess) {
+ if (id.equals(((ICommandAccess) curr).getCommandId())) {
+ return (ICompletionProposal) curr;
+ }
+ }
+ }
+ return null;
+ }
+
+ private Annotation[] getAnnotations(int offset, boolean goToClosest) throws BadLocationException {
+ ArrayList resultingAnnotations= new ArrayList();
+ RubyCorrectionAssistant.collectQuickFixableAnnotations(fEditor, offset, goToClosest, resultingAnnotations);
+ return (Annotation[]) resultingAnnotations.toArray(new Annotation[resultingAnnotations.size()]);
+ }
+
+ private ICompletionProposal getLocalRenameProposal(IInvocationContext context) {
+ Node node= context.getCoveringNode();
+// if (node instanceof SimpleName) { FIXME What do we do here?
+// return new LinkedNamesAssistProposal(context.getRubyScript(), (SimpleName) node);
+// }
+ return null;
+ }
+
+ private ITextViewer getTextViewer() {
+ if (fEditor instanceof RubyEditor) {
+ return ((RubyEditor) fEditor).getViewer();
+ }
+ return null;
+ }
+
+ private IDocument getDocument() {
+ return RubyUI.getDocumentProvider().getDocument(fEditor.getEditorInput());
+ }
+
+
+ private void invokeProposal(ICompletionProposal proposal, int offset) {
+ if (proposal instanceof ICompletionProposalExtension2) {
+ ITextViewer viewer= getTextViewer();
+ if (viewer != null) {
+ ((ICompletionProposalExtension2) proposal).apply(viewer, (char) 0, 0, offset);
+ return;
+ }
+ } else if (proposal instanceof ICompletionProposalExtension) {
+ IDocument document= getDocument();
+ if (document != null) {
+ ((ICompletionProposalExtension) proposal).apply(document, (char) 0, offset);
+ return;
+ }
+ }
+ IDocument document= getDocument();
+ if (document != null) {
+ proposal.apply(document);
+ }
+ }
+
+ public static String getShortCutString(String proposalId) {
+ if (proposalId != null) {
+ IBindingService bindingService= (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class);
+ if (bindingService != null) {
+ TriggerSequence[] activeBindingsFor= bindingService.getActiveBindingsFor(proposalId);
+ if (activeBindingsFor.length > 0) {
+ return activeBindingsFor[0].format();
+ }
+ }
+ }
+ return null;
+ }
+
+}
Modified: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/CorrectionMessages.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/CorrectionMessages.java 2007-03-16 13:21:44 UTC (rev 2188)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/CorrectionMessages.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -7,6 +7,11 @@
private static final String BUNDLE_NAME = CorrectionMessages.class.getName();
public static String RubyCorrectionProcessor_error_status;
+ public static String RubyCorrectionProcessor_error_quickfix_message;
+ public static String MarkerResolutionProposal_additionaldesc;
+ public static String ChangeCorrectionProposal_error_title;
+ public static String ChangeCorrectionProposal_error_message;
+ public static String ChangeCorrectionProposal_name_with_shortcut;
static {
NLS.initializeMessages(BUNDLE_NAME, CorrectionMessages.class);
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ICommandAccess.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ICommandAccess.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ICommandAccess.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -0,0 +1,26 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.ui.text.correction;
+
+/**
+ * Correction proposals implement this interface to by invokable by a command.
+ * (e.g. keyboard shortcut)
+ */
+public interface ICommandAccess {
+
+ /**
+ * Returns the id of the command that should invoke this correction proposal
+ * @return the id of the command. This id must start with {@link CorrectionCommandInstaller#COMMAND_PREFIX}
+ * to be recognixes as correction command.
+ */
+ String getCommandId();
+
+}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/IStatusLineProposal.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/IStatusLineProposal.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/IStatusLineProposal.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.ui.text.correction;
+
+/**
+ * A proposal which is able to show a message
+ * on the status line of the content assistant
+ * in which this proposal is shown.
+ *
+ * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2
+ */
+public interface IStatusLineProposal {
+
+ /**
+ * The message to show when this proposal is
+ * selected by the user in the content assistant.
+ *
+ * @return The message to show, or null for no message.
+ */
+ public String getStatusMessage();
+
+}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/MarkerResolutionProposal.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/MarkerResolutionProposal.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/MarkerResolutionProposal.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.ui.text.correction;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IMarkerResolution;
+import org.eclipse.ui.IMarkerResolution2;
+import org.rubypeople.rdt.internal.core.util.Messages;
+import org.rubypeople.rdt.internal.ui.RubyPlugin;
+import org.rubypeople.rdt.internal.ui.RubyPluginImages;
+import org.rubypeople.rdt.ui.text.ruby.IRubyCompletionProposal;
+
+/**
+ */
+public class MarkerResolutionProposal implements IRubyCompletionProposal {
+
+ private IMarkerResolution fResolution;
+ private IMarker fMarker;
+
+ /**
+ * Constructor for MarkerResolutionProposal.
+ */
+ public MarkerResolutionProposal(IMarkerResolution resolution, IMarker marker) {
+ fResolution= resolution;
+ fMarker= marker;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#apply(org.eclipse.jface.text.IDocument)
+ */
+ public void apply(IDocument document) {
+ fResolution.run(fMarker);
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ if (fResolution instanceof IMarkerResolution2) {
+ return ((IMarkerResolution2) fResolution).getDescription();
+ }
+ try {
+ String problemDesc= (String) fMarker.getAttribute(IMarker.MESSAGE);
+ return Messages.format(CorrectionMessages.MarkerResolutionProposal_additionaldesc, problemDesc);
+ } catch (CoreException e) {
+ RubyPlugin.log(e);
+ }
+ return null;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getContextInformation()
+ */
+ public IContextInformation getContextInformation() {
+ return null;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ return fResolution.getLabel();
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ if (fResolution instanceof IMarkerResolution2) {
+ return ((IMarkerResolution2) fResolution).getImage();
+ }
+ return RubyPluginImages.get(RubyPluginImages.IMG_CORRECTION_CHANGE);
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jdt.internal.ui.text.java.IRubyCompletionProposal#getRelevance()
+ */
+ public int getRelevance() {
+// if (fResolution instanceof IRubyMarkerResolutionExtension) {
+// return ((IRubyMarkerResolutionExtension) fResolution).getRelevance();
+// }
+ return 10;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getSelection(org.eclipse.jface.text.IDocument)
+ */
+ public Point getSelection(IDocument document) {
+ return null;
+ }
+
+}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ProblemLocation.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ProblemLocation.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/ProblemLocation.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.rubypeople.rdt.internal.ui.text.correction;
+
+import org.jruby.ast.Node;
+import org.jruby.ast.RootNode;
+import org.rubypeople.rdt.core.IRubyModelMarker;
+import org.rubypeople.rdt.core.compiler.CategorizedProblem;
+import org.rubypeople.rdt.core.compiler.IProblem;
+import org.rubypeople.rdt.internal.ti.util.OffsetNodeLocator;
+import org.rubypeople.rdt.internal.ui.rubyeditor.IRubyAnnotation;
+import org.rubypeople.rdt.internal.ui.rubyeditor.RubyMarkerAnnotation;
+import org.rubypeople.rdt.ui.text.ruby.IProblemLocation;
+
+/**
+ *
+ */
+public class ProblemLocation implements IProblemLocation {
+
+ private final int fId;
+// private final String[] fArguments;
+ private final int fOffset;
+ private final int fLength;
+ private final boolean fIsError;
+ private final String fMarkerType;
+
+ public ProblemLocation(int offset, int length, IRubyAnnotation annotation) {
+ fId= annotation.getId();
+// fArguments= annotation.getArguments();
+ fOffset= offset;
+ fLength= length;
+ fIsError= RubyMarkerAnnotation.ERROR_ANNOTATION_TYPE.equals(annotation.getType());
+
+ String markerType= annotation.getMarkerType();
+ fMarkerType= markerType != null ? markerType : IRubyModelMarker.RUBY_MODEL_PROBLEM_MARKER;
+ }
+
+ public ProblemLocation(int offset, int length, int id, String[] arguments, boolean isError, String markerType) {
+ fId= id;
+// fArguments= arguments;
+ fOffset= offset;
+ fLength= length;
+ fIsError= isError;
+ fMarkerType= markerType;
+ }
+
+ public ProblemLocation(IProblem problem) {
+ fId= problem.getID();
+// fArguments= problem.getArguments();
+ fOffset= problem.getSourceStart();
+ fLength= problem.getSourceEnd() - fOffset + 1;
+ fIsError= problem.isError();
+ fMarkerType= problem instanceof CategorizedProblem ? ((CategorizedProblem) problem).getMarkerType() : IRubyModelMarker.RUBY_MODEL_PROBLEM_MARKER;
+ }
+
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jdt.internal.ui.text.correction.IProblemLocation#getProblemId()
+ */
+ public int getProblemId() {
+ return fId;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jdt.internal.ui.text.correction.IProblemLocation#getProblemArguments()
+ */
+ public String[] getProblemArguments() {
+// return fArguments;
+ return new String[0];
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jdt.internal.ui.text.correction.IProblemLocation#getLength()
+ */
+ public int getLength() {
+ return fLength;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jdt.internal.ui.text.correction.IProblemLocation#getOffset()
+ */
+ public int getOffset() {
+ return fOffset;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jdt.ui.text.java.IProblemLocation#isError()
+ */
+ public boolean isError() {
+ return fIsError;
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jdt.ui.text.java.IProblemLocation#getMarkerType()
+ */
+ public String getMarkerType() {
+ return fMarkerType;
+ }
+
+ /*
+ * (non-Rubydoc)
+ * @see org.eclipse.jdt.internal.ui.text.correction.IProblemLocation#getCoveringNode(org.eclipse.jdt.core.dom.CompilationUnit)
+ */
+ public Node getCoveringNode(RootNode astRoot) {
+ return OffsetNodeLocator.Instance().getNodeAtOffset(astRoot, fOffset);
+ }
+
+ /*
+ * (non-Rubydoc)
+ * @see org.eclipse.jdt.internal.ui.text.correction.IProblemLocation#getCoveredNode(org.eclipse.jdt.core.dom.CompilationUnit)
+ */
+ public Node getCoveredNode(RootNode astRoot) {
+ return OffsetNodeLocator.Instance().getNodeAtOffset(astRoot, fOffset);
+ }
+
+ public String toString() {
+ StringBuffer buf= new StringBuffer();
+ buf.append("Id: ").append(getErrorCode(fId)).append('\n'); //$NON-NLS-1$
+ buf.append('[').append(fOffset).append(", ").append(fLength).append(']').append('\n'); //$NON-NLS-1$
+// String[] arg= fArguments;
+// if (arg != null) {
+// for (int i= 0; i < arg.length; i++) {
+// buf.append(arg[i]);
+// buf.append('\n');
+// }
+// }
+ return buf.toString();
+ }
+
+ private String getErrorCode(int code) {
+ StringBuffer buf= new StringBuffer();
+
+ if ((code & IProblem.TypeRelated) != 0) {
+ buf.append("TypeRelated + "); //$NON-NLS-1$
+ }
+ if ((code & IProblem.FieldRelated) != 0) {
+ buf.append("FieldRelated + "); //$NON-NLS-1$
+ }
+ if ((code & IProblem.ConstructorRelated) != 0) {
+ buf.append("ConstructorRelated + "); //$NON-NLS-1$
+ }
+ if ((code & IProblem.MethodRelated) != 0) {
+ buf.append("MethodRelated + "); //$NON-NLS-1$
+ }
+ if ((code & IProblem.ImportRelated) != 0) {
+ buf.append("ImportRelated + "); //$NON-NLS-1$
+ }
+ if ((code & IProblem.Internal) != 0) {
+ buf.append("Internal + "); //$NON-NLS-1$
+ }
+ if ((code & IProblem.Syntax) != 0) {
+ buf.append("Syntax + "); //$NON-NLS-1$
+ }
+ buf.append(code & IProblem.IgnoreCategoriesMask);
+
+ return buf.toString();
+ }
+
+
+}
Added: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/RubyCorrectionAssistant.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/RubyCorrectionAssistant.java (rev 0)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/RubyCorrectionAssistant.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -0,0 +1,329 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.rubypeople.rdt.internal.ui.text.correction;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceConverter;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
+import org.eclipse.jface.text.quickassist.QuickAssistAssistant;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.util.Assert;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.rubypeople.rdt.core.IRubyElement;
+import org.rubypeople.rdt.core.IRubyScript;
+import org.rubypeople.rdt.internal.ui.RubyPlugin;
+import org.rubypeople.rdt.internal.ui.text.HTMLTextPresenter;
+import org.rubypeople.rdt.ui.PreferenceConstants;
+import org.rubypeople.rdt.ui.RubyUI;
+import org.rubypeople.rdt.ui.text.IColorManager;
+import org.rubypeople.rdt.ui.text.RubyTextTools;
+
+
+public class RubyCorrectionAssistant extends QuickAssistAssistant {
+
+ private ITextViewer fViewer;
+ private ITextEditor fEditor;
+ private Position fPosition;
+ private Annotation[] fCurrentAnnotations;
+
+// private QuickAssistLightBulbUpdater fLightBulbUpdater;
+
+
+ /**
+ * Constructor for RubyCorrectionAssistant.
+ */
+ public RubyCorrectionAssistant(ITextEditor editor) {
+ super();
+ Assert.isNotNull(editor);
+ fEditor= editor;
+
+ RubyCorrectionProcessor processor= new RubyCorrectionProcessor(this);
+
+ setQuickAssistProcessor(processor);
+
+ setInformationControlCreator(getInformationControlCreator());
+
+ RubyTextTools textTools= RubyPlugin.getDefault().getRubyTextTools();
+ IColorManager manager= textTools.getColorManager();
+
+ IPreferenceStore store= RubyPlugin.getDefault().getPreferenceStore();
+
+ Color c= getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_FOREGROUND, manager);
+ setProposalSelectorForeground(c);
+
+ c= getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_BACKGROUND, manager);
+ setProposalSelectorBackground(c);
+ }
+
+ public IEditorPart getEditor() {
+ return fEditor;
+ }
+
+
+ private IInformationControlCreator getInformationControlCreator() {
+ return new IInformationControlCreator() {
+ public IInformationControl createInformationControl(Shell parent) {
+ return new DefaultInformationControl(parent, new HTMLTextPresenter());
+ }
+ };
+ }
+
+ private static Color getColor(IPreferenceStore store, String key, IColorManager manager) {
+ RGB rgb= PreferenceConverter.getColor(store, key);
+ return manager.getColor(rgb);
+ }
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jface.text.contentassist.IContentAssistant#install(org.eclipse.jface.text.ITextViewer)
+ */
+ public void install(ISourceViewer sourceViewer) {
+ super.install(sourceViewer);
+ fViewer= sourceViewer;
+
+// fLightBulbUpdater= new QuickAssistLightBulbUpdater(fEditor, sourceViewer);
+// fLightBulbUpdater.install();
+ }
+
+
+
+ /* (non-Rubydoc)
+ * @see org.eclipse.jface.text.contentassist.ContentAssistant#uninstall()
+ */
+ public void uninstall() {
+// if (fLightBulbUpdater != null) {
+// fLightBulbUpdater.uninstall();
+// fLightBulbUpdater= null;
+// }
+ super.uninstall();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.quickassist.QuickAssistAssistant#showPossibleQuickAssists()
+ * @since 3.2
+ */
+
+ /**
+ * Show completions at caret position. If current
+ * position does not contain quick fixes look for
+ * next quick fix on same line by moving from left
+ * to right and restarting at end of line if the
+ * beginning of the line is reached.
+ *
+ * @see IQuickAssistAssistant#showPossibleQuickAssists()
+ */
+ public String showPossibleQuickAssists() {
+ fPosition= null;
+ fCurrentAnnotations= null;
+
+ if (fViewer == null || fViewer.getDocument() == null)
+ // Let superclass deal with this
+ return super.showPossibleQuickAssists();
+
+
+ ArrayList resultingAnnotations= new ArrayList(20);
+ try {
+ Point selectedRange= fViewer.getSelectedRange();
+ int currOffset= selectedRange.x;
+ int currLength= selectedRange.y;
+ boolean goToClosest= (currLength == 0);
+
+ int newOffset= collectQuickFixableAnnotations(fEditor, currOffset, goToClosest, resultingAnnotations);
+ if (newOffset != currOffset) {
+ storePosition(currOffset, currLength);
+ fViewer.setSelectedRange(newOffset, 0);
+ fViewer.revealRange(newOffset, 0);
+ }
+ } catch (BadLocationException e) {
+ RubyPlugin.log(e);
+ }
+ fCurrentAnnotations= (Annotation[]) resultingAnnotations.toArray(new Annotation[resultingAnnotations.size()]);
+
+ return super.showPossibleQuickAssists();
+ }
+
+
+ private static IRegion getRegionOfInterest(ITextEditor editor, int invocationLocation) throws BadLocationException {
+ IDocumentProvider documentProvider= editor.getDocumentProvider();
+ if (documentProvider == null) {
+ return null;
+ }
+ IDocument document= documentProvider.getDocument(editor.getEditorInput());
+ if (document == null) {
+ return null;
+ }
+ return document.getLineInformationOfOffset(invocationLocation);
+ }
+
+ public static int collectQuickFixableAnnotations(ITextEditor editor, int invocationLocation, boolean goToClosest, ArrayList resultingAnnotations) throws BadLocationException {
+ IAnnotationModel model= RubyUI.getDocumentProvider().getAnnotationModel(editor.getEditorInput());
+ if (model == null) {
+ return invocationLocation;
+ }
+
+ ensureUpdatedAnnotations(editor);
+
+ Iterator iter= model.getAnnotationIterator();
+ if (goToClosest) {
+ IRegion lineInfo= getRegionOfInterest(editor, invocationLocation);
+ if (lineInfo == null) {
+ return invocationLocation;
+ }
+ int rangeStart= lineInfo.getOffset();
+ int rangeEnd= rangeStart + lineInfo.getLength();
+
+ ArrayList allAnnotations= new ArrayList();
+ ArrayList allPositions= new ArrayList();
+ int bestOffset= Integer.MAX_VALUE;
+ while (iter.hasNext()) {
+ Annotation annot= (Annotation) iter.next();
+ if (RubyCorrectionProcessor.isQuickFixableType(annot)) {
+ Position pos= model.getPosition(annot);
+ if (pos != null && isInside(pos.offset, rangeStart, rangeEnd)) { // inside our range?
+ allAnnotations.add(annot);
+ allPositions.add(pos);
+ bestOffset= processAnnotation(annot, pos, invocationLocation, bestOffset);
+ }
+ }
+ }
+ if (bestOffset == Integer.MAX_VALUE) {
+ return invocationLocation;
+ }
+ for (int i= 0; i < allPositions.size(); i++) {
+ Position pos= (Position) allPositions.get(i);
+ if (isInside(bestOffset, pos.offset, pos.offset + pos.length)) {
+ resultingAnnotations.add(allAnnotations.get(i));
+ }
+ }
+ return bestOffset;
+ } else {
+ while (iter.hasNext()) {
+ Annotation annot= (Annotation) iter.next();
+ if (RubyCorrectionProcessor.isQuickFixableType(annot)) {
+ Position pos= model.getPosition(annot);
+ if (pos != null && isInside(invocationLocation, pos.offset, pos.offset + pos.length)) {
+ resultingAnnotations.add(annot);
+ }
+ }
+ }
+ return invocationLocation;
+ }
+ }
+
+ private static void ensureUpdatedAnnotations(ITextEditor editor) {
+ Object inputElement= editor.getEditorInput().getAdapter(IRubyElement.class);
+ if (inputElement instanceof IRubyScript) {
+// RubyPlugin.getDefault().getASTProvider().getAST((IRubyScript) inputElement, ASTProvider.WAIT_ACTIVE_ONLY, null);
+ }
+ }
+
+ private static int processAnnotation(Annotation annot, Position pos, int invocationLocation, int bestOffset) {
+ int posBegin= pos.offset;
+ int posEnd= posBegin + pos.length;
+ if (isInside(invocationLocation, posBegin, posEnd)) { // covers invocation location?
+ return invocationLocation;
+ } else if (bestOffset != invocationLocation) {
+ int newClosestPosition= computeBestOffset(posBegin, invocationLocation, bestOffset);
+ if (newClosestPosition != -1) {
+ if (newClosestPosition != bestOffset) { // new best
+ if (RubyCorrectionProcessor.hasCorrections(annot)) { // only jump to it if there are proposals
+ return newClosestPosition;
+ }
+ }
+ }
+ }
+ return bestOffset;
+ }
+
+
+ private static boolean isInside(int offset, int start, int end) {
+ return offset == start || (offset > start && offset < end); // make sure to handle 0-length ranges
+ }
+
+ /**
+ * Computes and returns the invocation offset given a new
+ * position, the initial offset and the best invocation offset
+ * found so far.
+ * <p>
+ * The closest offset to the left of the initial offset is the
+ * best. If there is no offset on the left, the closest on the
+ * right is the best.</p>
+ * @return -1 is returned if the given offset is not closer or the new best offset
+ */
+ private static int computeBestOffset(int newOffset, int invocationLocation, int bestOffset) {
+ if (newOffset <= invocationLocation) {
+ if (bestOffset > invocationLocation) {
+ return newOffset; // closest was on the right, prefer on the left
+ } else if (bestOffset <= newOffset) {
+ return newOffset; // we are closer or equal
+ }
+ return -1; // further away
+ }
+
+ if (newOffset <= bestOffset)
+ return newOffset; // we are closer or equal
+
+ return -1; // further away
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ContentAssistant#possibleCompletionsClosed()
+ */
+ protected void possibleCompletionsClosed() {
+ super.possibleCompletionsClosed();
+ restorePosition();
+ }
+
+ private void storePosition(int currOffset, int currLength) {
+ fPosition= new Position(currOffset, currLength);
+ }
+
+ private void restorePosition() {
+ if (fPosition != null && !fPosition.isDeleted() && fViewer.getDocument() != null) {
+ fViewer.setSelectedRange(fPosition.offset, fPosition.length);
+ fViewer.revealRange(fPosition.offset, fPosition.length);
+ }
+ fPosition= null;
+ }
+
+ /**
+ * Returns true if the last invoked completion was called with an updated offset.
+ */
+ public boolean isUpdatedOffset() {
+ return fPosition != null;
+ }
+
+ /**
+ * Returns the annotations at the current offset
+ */
+ public Annotation[] getAnnotationsAtOffset() {
+ return fCurrentAnnotations;
+ }
+}
Modified: trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/RubyCorrectionProcessor.java
===================================================================
--- trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/RubyCorrectionProcessor.java 2007-03-16 13:21:44 UTC (rev 2188)
+++ trunk/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/internal/ui/text/correction/RubyCorrectionProcessor.java 2007-03-16 13:23:28 UTC (rev 2189)
@@ -1,6 +1,7 @@
package org.rubypeople.rdt.internal.ui.text.correction;
import java.util.ArrayList;
+import java.util.Collection;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.runtime.IConfigurationElement;
@@ -10,26 +11,71 @@
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.SafeRunner;
import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.contentassist.ContentAssistEvent;
+import org.eclipse.jface.text.contentassist.ICompletionListener;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IMarkerHelpRegistry;
+import org.eclipse.ui.IMarkerResolution;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.texteditor.SimpleMarkerAnnotation;
import org.rubypeople.rdt.core.IRubyScript;
import org.rubypeople.rdt.internal.ui.RubyPlugin;
import org.rubypeople.rdt.internal.ui.rubyeditor.IRubyAnnotation;
import org.rubypeople.rdt.ui.RubyUI;
+import org.rubypeople.rdt.ui.text.ruby.IInvocationContext;
+import org.rubypeople.rdt.ui.text.ruby.IProblemLocation;
+import org.rubypeople.rdt.ui.text.ruby.IQuickAssistProcessor;
import org.rubypeople.rdt.ui.text.ruby.IQuickFixProcessor;
+import org.rubypeople.rdt.ui.text.ruby.IRubyCompletionProposal;
public class RubyCorrectionProcessor implements org.eclipse.jface.text.quickassist.IQuickAssistProcessor {
private static final String QUICKFIX_PROCESSOR_CONTRIBUTION_ID= "quickFixProcessors"; //$NON-NLS-1$
+ private static final String QUICKASSIST_PROCESSOR_CONTRIBUTION_ID= "quickAssistProcessors"; //$NON-NLS-1$
+ private static ContributedProcessorDescriptor[] fContributedAssistProcessors= null;
private static ContributedProcessorDescriptor[] fContributedCorrectionProcessors= null;
private String fErrorMessage;
+ private RubyCorrectionAssistant fAssistant;
+ /*
+ * Constructor for RubyCorrectionProcessor.
+ */
+ public RubyCorrectionProcessor(RubyCorrectionAssistant assistant) {
+ fAssistant= assistant;
+ fAssistant.addCompletionListener(new ICompletionListener() {
+
+ public void assistSessionEnded(ContentAssistEvent event) {
+ fAssistant.setStatusLineVisible(false);
+ }
+
+ public void assistSessionStarted(ContentAssistEvent event) {
+ fAssistant.setStatusLineVisible(true);
+ }
+
+ public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) {
+ if (proposal instanceof IStatusLineProposal) {
+ IStatusLineProposal statusLineProposal= (IStatusLineProposal)proposal;
+ String message= statusLineProposal.getStatusMessage();
+ if (message != null) {
+ fAssistant.setStatusMessage(message);
+ } else {
+ fAssistant.setStatusMessage(""); //$NON-NLS-1$
+ }
+ } else {
+ fAssistant.setStatusMessage(""); //$NON-NLS-1$
+ }
+ }
+ });
+ }
+
public boolean canAssist(IQuickAssistInvocationContext invocationContext) {
// TODO Auto-generated method stub
return false;
@@ -39,10 +85,149 @@
return hasCorrections(annotation);
}
- public ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationContext invocationContext) {
- // TODO Auto-generated method stub
- return new ICompletionProposal[0];
+ public ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationContext quickAssistContext) {
+ ITextViewer viewer= quickAssistContext.getSourceViewer();
+ int documentOffset= quickAssistContext.getOffset();
+
+ IEditorPart part= fAssistant.getEditor();
+
+ IRubyScript cu= RubyUI.getWorkingCopyManager().getWorkingCopy(part.getEditorInput());
+ IAnnotationModel model= RubyUI.getDocumentProvider().getAnnotationModel(part.getEditorInput());
+
+ int length= viewer != null ? viewer.getSelectedRange().y : 0;
+ AssistContext context= new AssistContext(cu, documentOffset, length);
+
+ Annotation[] annotations= fAssistant.getAnnotationsAtOffset();
+
+ fErrorMessage= null;
+
+ ICompletionProposal[] res= null;
+ if (model != null && annotations != null) {
+ ArrayList proposals= new ArrayList(10);
+ IStatus status= collectProposals(context, model, annotations, true, !fAssistant.isUpdatedOffset(), proposals);
+ res= (ICompletionProposal[]) proposals.toArray(new ICompletionProposal[proposals.size()]);
+ if (!status.isOK()) {
+ fErrorMessage= status.getMessage();
+ RubyPlugin.log(status);
+ }
+ }
+
+ if (res == null || res.length == 0) {
+ return new ICompletionProposal[0];
+ }
+// if (res.length > 1) {
+// Arrays.sort(res, new CompletionProposalComparator());
+// }
+ return res;
}
+
+ public static IStatus collectProposals(IInvocationContext context, IAnnotationModel model, Annotation[] annotations, boolean addQuickFixes, boolean addQuickAssists, Collection proposals) {
+ ArrayList problems= new ArrayList();
+
+ // collect problem locations and corrections from marker annotations
+ for (int i= 0; i < annotations.length; i++) {
+ Annotation curr= annotations[i];
+ if (curr instanceof IRubyAnnotation) {
+ ProblemLocation problemLocation= getProblemLocation((IRubyAnnotation) curr, model);
+ if (problemLocation != null) {
+ problems.add(problemLocation);
+ }
+ } else if (addQuickFixes && curr instanceof SimpleMarkerAnnotation) {
+ // don't collect if annotation is already a java annotation
+ collectMarkerProposals((SimpleMarkerAnnotation) curr, proposals);
+ }
+ }
+ MultiStatus resStatus= null;
+
+ IProblemLocation[] problemLocations= (IProblemLocation[]) problems.toArray(new IProblemLocation[problems.size()]);
+ if (addQuickFixes) {
+ IStatus status= collectCorrections(context, problemLocations, proposals);
+ if (!status.isOK()) {
+ resStatus= new MultiStatus(RubyUI.ID_PLUGIN, IStatus.ERROR, CorrectionMessages.RubyCorrectionProcessor_error_quickfix_message, null);
+ resStatus.add(status);
+ }
+ }
+// if (addQuickAssists) {
+// IStatus status= collectAssists(context, problemLocations, proposals);
+// if (!status.isOK()) {
+// if (resStatus == null) {
+// resStatus= new MultiStatus(RubyUI.ID_PLUGIN, IStatus.ERROR, CorrectionMessages.JavaCorrectionProcessor_error_quickassist_message, null);
+// }
+// resStatus.add(status);
+// }
+// }
+ if (resStatus != null) {
+ return resStatus;
+ }
+ return Status.OK_STATUS;
+ }
+
+ public static IStatus collectCorrections(IInvocationContext context, IProblemLocation[] locations, Collection proposals) {
+ ContributedProcessorDescriptor[] processors= getCorrectionProcessors();
+ SafeCorrectionCollector collector= new SafeCorrectionCollector(context, proposals);
+ for (int i= 0...
[truncated message content] |