All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.fife.rsta.ac.js.JavaScriptLinkGenerator Maven / Gradle / Ivy

Go to download

A library adding code completion and other advanced features for Java, JavaScript, Perl, and other languages to RSyntaxTextArea.

There is a newer version: 3.3.0
Show newest version
package org.fife.rsta.ac.js;

import javax.swing.text.BadLocationException;

import org.fife.rsta.ac.js.ast.JavaScriptDeclaration;
import org.fife.rsta.ac.js.ast.VariableResolver;
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;
import org.fife.ui.rsyntaxtextarea.TokenImpl;


public class JavaScriptLinkGenerator implements LinkGenerator {

	private JavaScriptLanguageSupport language;
	private boolean findLocal;
	private boolean findPreprocessed;
	private boolean findSystem;


	public JavaScriptLinkGenerator(JavaScriptLanguageSupport language) {
		this.language = language;
		this.findLocal = true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, int offs) {
		
		JavaScriptDeclaration dec = null;
		IsLinkableCheckResult result = checkForLinkableToken(textArea, offs);
		if (result != null) {
			//re-parse the document to resolve any variables local to the offs
			Token t = result.token;
			boolean function = result.function;
			String name = t.getLexeme();
			if(name != null && name.length() > 0)
			{
				//only re-parse the document if there is a character that could potentially be a variable or function 
				if(name.length() > 1 || (name.length() == 1 && Character.isJavaIdentifierPart(name.charAt(0))))
				{
					language.reparseDocument(offs);
				}
			}
			JavaScriptParser parser = language.getJavaScriptParser();
			VariableResolver variableResolver = parser.getVariablesAndFunctions();

			if (variableResolver != null) {
				
				if (!function) { // must be a variable
					dec = variableResolver.findDeclaration(name, offs, findLocal, findPreprocessed, findSystem);
				}
				else {
					String lookup = getLookupNameForFunction(textArea, offs, name);
					// lookup Function based on the name
					dec = variableResolver.findFunctionDeclaration(lookup, findLocal, findPreprocessed);
					if(dec == null) {
						dec = variableResolver.findFunctionDeclarationByFunctionName(name, findLocal, findPreprocessed);
					}
				}

			}

			if (dec != null) {
				return createSelectedRegionResult(textArea, t, dec);
			}
		}
		return null;
	}
	
	/**
	 * @return LinkGeneratorResult based on the JavaScriptDeclaraton and the position 
	 */
	protected LinkGeneratorResult createSelectedRegionResult(RSyntaxTextArea textArea, Token t, JavaScriptDeclaration dec) {
		if(dec.getTypeDeclarationOptions() != null && !dec.getTypeDeclarationOptions().isSupportsLinks()) {
			return null;
		}
		return new SelectRegionLinkGeneratorResult(textArea, t.getOffset(), dec.getStartOffSet(), dec.getEndOffset());
	}
	
	/**
	 * @param find flag to state whether to look in the RSTA editing script for variable/function completions
	 */
	public void setFindLocal(boolean find) {
		this.findLocal = find;
	}
	
	/**
	 * @param find flag to state whether to look in the pre-processed scripts for variable/function completions
	 */
	public void setFindPreprocessed(boolean find) {
		this.findPreprocessed = find;
	}
	
	/**
	 * @param find flag to state whether to look in the system scripts for variable/function completions
	 */
	public void setFindSystem(boolean find) {
		this.findSystem = find;
	}


	/**
	 * Convert the function Token to JavaScript variable resolver lookup name by replacing any parameters with 'p' and stripping any whitespace between the parameters:
	 * e.g
	 * Token may contain the function:
	 * addTwoNumbers(num1, num2);
	 * 
	 * The return result will be:
	 * addTwoNumbers(p,p);
	 * 
	 * @return converted function name to variable resolver lookup name
	 */
	private String getLookupNameForFunction(RSyntaxTextArea textArea, int offs, String name) {
		
		StringBuilder temp = new StringBuilder();
		if (offs>=0) {
			
			try {
				int line = textArea.getLineOfOffset(offs);
				
				Token first = wrapToken(textArea.getTokenListForLine(line));
				for (Token t=first; t!=null && t.isPaintable(); t=wrapToken(t.getNextToken())) {
					if (t.containsPosition(offs)) {
						for (Token tt=t; tt!=null && tt.isPaintable(); tt=wrapToken(tt.getNextToken())) {
							temp.append(tt.getLexeme());
							if (tt.isSingleChar(Token.SEPARATOR, ')')) {
								break;
							}
						}
					}
				}
			} catch (BadLocationException ble) {
				ble.printStackTrace(); // Never happens
			}
		}
		
		//now replace all the variables with lookup 'p'
		String function = temp.toString().replaceAll("\\s", ""); //remove all whitespace
		boolean params = false;
		int count = 0;
		StringBuilder sb = new StringBuilder();
		for(int i=0; i= 0) {

			try {

				int line = textArea.getLineOfOffset(offs);
				Token first = wrapToken(textArea.getTokenListForLine(line));
				Token prev = null;

				for (Token t = first; t != null && t.isPaintable(); t = wrapToken(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 = wrapToken(t);

						boolean isFunction = false;

						if (prev != null && prev.isSingleChar('.')) {
							// Not a field or method defined in this.
							break;
						}

						Token next = wrapToken(RSyntaxUtilities
								.getNextImportantToken(t.getNextToken(),
										textArea, line));
						if (next != null
								&& next.isSingleChar(Token.SEPARATOR, '(')) {
							isFunction = true;
						}

						result = new IsLinkableCheckResult(token, isFunction);
						break;

					}

					else if (!t.isCommentOrWhitespace()) {
						prev = t;
					}

				}

			} catch (BadLocationException ble) {
				ble.printStackTrace(); // Never happens
			}

		}

		return result;

	}

	/**
	 * Due to the Tokens being reused, this can cause problems when finding the
	 * next token associated with a token.  Wrap the tokens to stop this
	 * problem.
	 * 
	 * @param token to wrap
	 * @return copy of the original token
	 */
	private Token wrapToken(Token token) {
		if (token != null)
			return new TokenImpl(token);
		return token;
	}
	
	/**
	 * @return JavaScriptLanguage support
	 */
	public JavaScriptLanguageSupport getLanguage() {
		return language;
	}


	/**
	 * 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 function invocation (as opposed to a local
		 * variable or object).
		 */
		private boolean function;


		private IsLinkableCheckResult(Token token, boolean function) {
			this.token = token;
			this.function = function;
		}

	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy