Hmm... Charles O.Nutter talking about that RDT clone they're throwing
together over at Sun:
http://headius.blogspot.com/2006/11/netbeans-ruby-awesome.html


Odd, really, that he talks about it eclipsing 'other Ruby IDEs' ...
where RDT has everything he mentions, like MarkOccurrences, rename
refactoring (not inline yet), and AutoComplete.

Granted... one thing was missing: code browsing a la JDT (you know, hold
down Ctrl and move over a class or method, and it'll turn into a
hyperlink that'll take to there).
So there's me thinking: how about I invest an hour and see how far I can
implement this. To be honest, it took me 1 1/2 hours, but here's a first
proof of concept of this functionality (I attached the patch against the
current SVN trunk).

It works just like the JDT editors, ie. mouse over code with Ctrl
pressed; this works for:
 - Classes
   move over the name of a class that is defined in your RDT project;
   clicking on the link will open an editor and jump to the class
 - Charles mentions that this Netbeans thing allows you to jump to
method definitions inside the same file
   This patch also allows this, although it's probably in need of a bit
of more work.

NOTE: this isn't production code, but it works quite nicely.

I don't have time to clean it up right now, but I can do that if there's
an interest.
Todo:
 - Charles mentioned jumping to variables and fields, although I'm not
quite sure where to jump _to_
 - create an extension point so that other plugins can contribute
Hyperlink behavior;
  I presume that the RadRails guys might find that useful, same goes for
JRuby support;
 - I haven't looked at the Type Inference stuff, but I guess that with
that, it's possible to even
  jump to the definition of a "hello()" in  this example:
  x = Foo.new()
  x.hello()
 
 
Could someone, with a few minutes leisure time + a current trunk version
give this a try?

murphee
-- Blog @ http://jroller.com/page/murphee Maintainer of EclipseShell @
http://eclipse-shell.sourceforge.net/


Index: 
/home/murphee/projects/coding/workspace/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/text/RubySourceViewerConfiguration.java
===================================================================
--- 
/home/murphee/projects/coding/workspace/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/text/RubySourceViewerConfiguration.java
     (revision 1673)
+++ 
/home/murphee/projects/coding/workspace/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/text/RubySourceViewerConfiguration.java
     (working copy)
@@ -15,6 +15,7 @@
 import org.eclipse.jface.text.contentassist.IContentAssistant;
 import org.eclipse.jface.text.formatter.IContentFormatter;
 import org.eclipse.jface.text.formatter.MultiPassContentFormatter;
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
 import org.eclipse.jface.text.presentation.IPresentationReconciler;
 import org.eclipse.jface.text.presentation.PresentationReconciler;
 import org.eclipse.jface.text.reconciler.IReconciler;
@@ -59,6 +60,7 @@
 import org.rubypeople.rdt.internal.ui.text.ruby.RubyReconcilingStrategy;
 import org.rubypeople.rdt.internal.ui.text.ruby.SingleTokenRubyCodeScanner;
 import org.rubypeople.rdt.internal.ui.text.ruby.hover.RubyCodeTextHover;
+import org.rubypeople.rdt.ui.text.hyperlinks.RubyHyperLinkDetector;
 
 public class RubySourceViewerConfiguration extends 
TextSourceViewerConfiguration {
 
@@ -135,6 +137,14 @@
         return formatter;
     }
 
+    
+    @Override
+    public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer 
sourceViewer) {
+       ITextEditor editor = getEditor();               
+       return new IHyperlinkDetector[]{new 
RubyHyperLinkDetector(editor.getEditorInput())};
+//     return super.getHyperlinkDetectors(sourceViewer);       
+    }
+    
     /**
      * Determines whether the preference change encoded by the given event
      * changes the behavior of one of its contained components.
Index: 
/home/murphee/projects/coding/workspace/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/text/hyperlinks/RubyHyperLinkDetector.java
===================================================================
--- 
/home/murphee/projects/coding/workspace/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/text/hyperlinks/RubyHyperLinkDetector.java
  (revision 0)
+++ 
/home/murphee/projects/coding/workspace/org.rubypeople.rdt.ui/src/org/rubypeople/rdt/ui/text/hyperlinks/RubyHyperLinkDetector.java
  (revision 0)
@@ -0,0 +1,210 @@
+package org.rubypeople.rdt.ui.text.hyperlinks;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IFileEditorInput;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+import org.rubypeople.rdt.core.IMethod;
+import org.rubypeople.rdt.core.IRubyElement;
+import org.rubypeople.rdt.core.IRubyProject;
+import org.rubypeople.rdt.core.IRubyScript;
+import org.rubypeople.rdt.core.ISourceRange;
+import org.rubypeople.rdt.core.IType;
+import org.rubypeople.rdt.core.RubyCore;
+import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.internal.core.RubyType;
+import org.rubypeople.rdt.internal.ui.RubyPlugin;
+import org.rubypeople.rdt.internal.ui.text.RubyWordFinder;
+import org.rubypeople.rdt.ui.IWorkingCopyManager;
+
+public class RubyHyperLinkDetector implements IHyperlinkDetector {
+
+       private final IEditorInput fEditorInput;
+
+       public class RubyHyperlink implements IHyperlink {
+
+               private IRegion fRegion;
+               private final String fSymbol;
+               private final IEditorInput fEditorInput;
+
+               public RubyHyperlink(IEditorInput editorInput, IRegion region, 
String symbol) {
+                       this.fEditorInput = editorInput;
+                       this.fSymbol = symbol;
+//                     fRegion = new Region(region.getOffset(), 5);
+                       fRegion = region;
+               }
+
+               public IRegion getHyperlinkRegion() {
+                       return fRegion;
+               }
+
+               public String getHyperlinkText() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public String getTypeLabel() {
+                       // TODO Auto-generated method stub
+                       return null;
+               }
+
+               public void open() {
+                       String symbol = fSymbol;
+//                     IProject[] rubyProjects = RubyCore.getRubyProjects();
+                       IProject proj = null;
+//                     for (int i = 0; i < rubyProjects.length; i++) {
+//                             IProject project = rubyProjects[i];
+//                             if(project.getName().equals("RubyFormatter")){
+//                                     try {
+//                                             project.open(null);
+//                                     } catch (CoreException e) {
+//                                             // TODO Auto-generated catch 
block
+//                                             e.printStackTrace();
+//                                     }
+//                                     proj = project;
+//                                     break;
+//                             }
+//                     }
+                       if (fEditorInput instanceof IFileEditorInput) {
+                               IFileEditorInput fileInput = (IFileEditorInput) 
fEditorInput;
+                               proj = fileInput.getFile().getProject();
+                       }
+                       
+                       if (proj != null){
+                               try {
+                                       proj.open(null);
+                               } catch (CoreException e1) {
+                                       // TODO Auto-generated catch block
+                                       e1.printStackTrace();
+                               }
+                               IRubyProject rproject = RubyCore.create(proj);
+                               IType type;
+                               try {
+                                       type = rproject.findType(symbol);
+                                       if(type != null){
+                                               IResource resource = 
type.getResource();
+                                               // TODO: use getSource to do a 
source view hover! 
+                                               ISourceRange sourceRange = 
type.getSourceRange();
+                                               IFile file = 
proj.getFile(resource.getProjectRelativePath());
+                                               
openFileAndLocation(sourceRange, file);
+                                       } else {
+                                               // choose a method
+                                               // get the types in the file
+                                               IWorkingCopyManager manager = 
RubyPlugin.getDefault().getWorkingCopyManager();
+                                   IRubyScript rubyscript= 
manager.getWorkingCopy(fEditorInput);
+                                   IType[] types = rubyscript.getTypes();
+                                   
+                                   List theTypes = 
assembleTypesAroundOffset(types);
+                                   // Look for the methods with the name 
fSymbol
+                                   // Primitive: this'll take the first one it 
finds -> proof of concept, needs refinement;
+                                   for (Iterator iterator = 
theTypes.iterator(); iterator
+                                                               .hasNext();) {
+                                                       RubyType currType = 
(RubyType) iterator.next();
+                                                       IMethod[] methods = 
currType.getMethods();
+                                                       for (int i = 0; i < 
methods.length; i++) {
+                                                               IMethod method 
= methods[i];
+                                                               
if(method.getElementName().equals(fSymbol)){
+                                                                       
ISourceRange sourceRange = method.getSourceRange();
+                                                                       if 
(fEditorInput instanceof IFileEditorInput) {
+                                                                               
IFileEditorInput fileInput = (IFileEditorInput) fEditorInput;
+                                                                               
IFile file = fileInput.getFile();
+                                                                               
openFileAndLocation(sourceRange, file);
+                                                                       }
+                                                                       break;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               } catch (RubyModelException e) {
+                                       // TODO Auto-generated catch block
+                                       e.printStackTrace();
+                               } catch (PartInitException e) {
+                                       // TODO Auto-generated catch block
+                                       e.printStackTrace();
+                               } catch (CoreException e) {
+                                       // TODO Auto-generated catch block
+                                       e.printStackTrace();
+                               }
+//                             System.out.println("Hyperlink!!!");
+
+                       }
+               }
+
+               private List assembleTypesAroundOffset(IType[] types) throws 
RubyModelException {
+                       List retValue = new ArrayList();
+            for (int i = 0; i < types.length; i++) {
+                               IType currType = types[i];                      
                                
+                               if(fRegion.getOffset() >= 
currType.getSourceRange().getOffset()){
+                                       if(fRegion.getOffset() + 
fRegion.getLength() <= 
currType.getSourceRange().getOffset()+currType.getSourceRange().getLength()){
+                                               retValue.add(currType);
+                                               if(currType.isModule()){
+                                                       IRubyElement[] children 
= currType.getChildren();
+//                                                     ITypes newTypes = 
+                                                       List newTypes = new 
ArrayList();
+                                                       for (int j = 0; j < 
children.length; j++) {
+                                                               IRubyElement 
element = children[j];
+                                                               if (element 
instanceof RubyType) {
+                                                                       
newTypes.add(element);
+                                                               }
+                                                       }
+                                                       
retValue.addAll(assembleTypesAroundOffset((IType[])newTypes.toArray(new 
IType[newTypes.size()])));
+                                               } else  {
+                                                       retValue.add(currType);
+                                               }
+                                       }
+                               }
+                       }
+            return retValue;
+               }
+
+               private void openFileAndLocation(ISourceRange sourceRange, 
IFile file)
+                               throws PartInitException, CoreException {
+                       IEditorPart editorPart = IDE.openEditor(  
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(), file, 
true);
+
+                       IMarker mark = file.createMarker(IMarker.TEXT);
+                       mark.setAttribute(IMarker.CHAR_START, 
sourceRange.getOffset());
+                       mark.setAttribute(IMarker.CHAR_END, 
sourceRange.getOffset()+sourceRange.getLength());
+                       IDE.gotoMarker(editorPart, mark);
+                       mark.delete();
+                       IDE.gotoMarker(editorPart, mark);
+               }
+
+       }
+
+       public RubyHyperLinkDetector(IEditorInput editorInput) {
+               this.fEditorInput = editorInput;
+               
+       }
+
+       public IHyperlink[] detectHyperlinks(ITextViewer textViewer,
+                       IRegion region, boolean canShowMultipleHyperlinks) {
+               String symbol = "";
+               IRegion newRegion = region;
+               newRegion = RubyWordFinder.findWord(textViewer.getDocument(), 
region.getOffset());
+               try {
+                       symbol = 
textViewer.getDocument().get(newRegion.getOffset(), newRegion.getLength());
+               } catch (BadLocationException e) {
+                       // TODO Auto-generated catch block
+                       e.printStackTrace();
+               }
+               System.out.println("Symbol:" + symbol);
+               return new IHyperlink[]{new RubyHyperlink(fEditorInput, 
newRegion, symbol)};
+       }
+
+}
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Rubyeclipse-development mailing list
Rubyeclipse-development@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/rubyeclipse-development

Reply via email to