org.fife.rsta.ac.java.JavaLinkGenerator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of languagesupport Show documentation
Show all versions of languagesupport Show documentation
A library adding code completion and other advanced features for Java, JavaScript, Perl, and other languages to RSyntaxTextArea.
/*
* 02/17/2013
*
* Copyright (C) 2013 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is distributed under a modified BSD license. See the included
* RSTALanguageSupport.License.txt file for details.
*/
package org.fife.rsta.ac.java;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import javax.swing.text.BadLocationException;
import org.fife.rsta.ac.java.rjc.ast.CodeBlock;
import org.fife.rsta.ac.java.rjc.ast.CompilationUnit;
import org.fife.rsta.ac.java.rjc.ast.FormalParameter;
import org.fife.rsta.ac.java.rjc.ast.LocalVariable;
import org.fife.rsta.ac.java.rjc.ast.Member;
import org.fife.rsta.ac.java.rjc.ast.Method;
import org.fife.rsta.ac.java.rjc.ast.TypeDeclaration;
import org.fife.ui.rsyntaxtextarea.LinkGenerator;
import org.fife.ui.rsyntaxtextarea.LinkGeneratorResult;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.RSyntaxUtilities;
import org.fife.ui.rsyntaxtextarea.SelectRegionLinkGeneratorResult;
import org.fife.ui.rsyntaxtextarea.Token;
/**
* Checks for hyperlink-able tokens under the mouse position when Ctrl is
* pressed (Cmd on OS X). Currently this class only checks for accessible
* members in the current file only (e.g. no members in super classes, no other
* classes on the classpath, etc.). So naturally, there is a lot of room for
* improvement. IDE-style applications, for example, would want to check
* for members in super-classes, and open their source on click events.
*
* @author Robert Futrell
* @version 1.0
*/
// TODO: Anonymous inner classes probably aren't handled well.
class JavaLinkGenerator implements LinkGenerator {
private JavaLanguageSupport jls;
JavaLinkGenerator(JavaLanguageSupport jls) {
this.jls = jls;
}
/**
* Checks if the token at the specified offset is possibly a "click-able"
* region.
*
* @param textArea The text area.
* @param offs The offset, presumably at the mouse position.
* @return A result object.
*/
private IsLinkableCheckResult checkForLinkableToken(
RSyntaxTextArea textArea, int offs) {
IsLinkableCheckResult result = null;
if (offs>=0) {
try {
int line = textArea.getLineOfOffset(offs);
Token first = textArea.getTokenListForLine(line);
Token prev = null;
for (Token t=first; t!=null && t.isPaintable(); t=t.getNextToken()) {
if (t.containsPosition(offs)) {
// RSTA's tokens are pooled and re-used, so we must
// defensively make a copy of the one we want to keep!
Token token = new Token(t);
boolean isMethod = false;
if (prev==null) {
prev = RSyntaxUtilities.getPreviousImportantToken(
textArea, line-1);
}
if (prev!=null && prev.isSingleChar('.')) {
// Not a field or method defined in this class.
break;
}
Token next = RSyntaxUtilities.getNextImportantToken(
t.getNextToken(), textArea, line);
if (next!=null && next.isSingleChar(Token.SEPARATOR, '(')) {
isMethod = true;
}
result = new IsLinkableCheckResult(token, isMethod);
break;
}
else if (!t.isCommentOrWhitespace()) {
prev = t;
}
}
} catch (BadLocationException ble) {
ble.printStackTrace(); // Never happens
}
}
return result;
}
/**
* {@inheritDoc}
*/
public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea,
int offs) {
int start = -1;
int end = -1;
IsLinkableCheckResult result = checkForLinkableToken(textArea, offs);
if (result!=null) {
JavaParser parser = jls.getParser(textArea);
CompilationUnit cu = parser.getCompilationUnit();
Token t = result.token;
boolean method = result.method;
if (cu!=null) {
TypeDeclaration td = cu.getDeepestTypeDeclarationAtOffset(offs);
boolean staticFieldsOnly = false;
boolean deepestTypeDec = true;
boolean deepestContainingMemberStatic = false;
while (td!=null && start==-1) {
// First, check for a local variable in methods/static blocks
if (!method && deepestTypeDec) {
for (Iterator i=td.getMemberIterator(); i.hasNext(); ) {
Method m = null; // Nasty! Clean this code up
Member member = (Member)i.next();
CodeBlock block = null;
// Check if a method or static block contains offs
if (member instanceof Method) {
m = (Method)member;
if (m.getBodyContainsOffset(offs) && m.getBody()!=null) {
deepestContainingMemberStatic = m.isStatic();
block = m.getBody().getDeepestCodeBlockContaining(offs);
}
}
else if (member instanceof CodeBlock) {
block = (CodeBlock)member;
deepestContainingMemberStatic = block.isStatic();
block = block.getDeepestCodeBlockContaining(offs);
}
// If so, scan its locals
if (block!=null) {
String varName = t.getLexeme();
// Local variables first, in reverse order
List locals = block.getLocalVarsBefore(offs);
Collections.reverse(locals);
for (Iterator j=locals.iterator(); j.hasNext(); ) {
LocalVariable local = (LocalVariable)j.next();
if (varName.equals(local.getName())) {
start = local.getNameStartOffset();
end = local.getNameEndOffset();
}
}
// Then arguments, if any.
if (start==-1 && m!=null) {
for (int j=0; j-1) {
return new SelectRegionLinkGeneratorResult(textArea, t.offset,
start, end);
}
}
return null;
}
/**
* The result of checking whether a region of code under the mouse is
* possibly link-able.
*/
private static class IsLinkableCheckResult {
/**
* The token under the mouse position.
*/
private Token token;
/**
* Whether the token is a method invocation (as opposed to a local
* variable or field).
*/
private boolean method;
private IsLinkableCheckResult(Token token, boolean method) {
this.token = token;
this.method = method;
}
}
}