|
From: <caw...@us...> - 2007-05-02 13:17:42
|
Revision: 2416
http://svn.sourceforge.net/rubyeclipse/?rev=2416&view=rev
Author: cawilliams
Date: 2007-05-02 06:17:38 -0700 (Wed, 02 May 2007)
Log Message:
-----------
some core work to support new hovers
Modified Paths:
--------------
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/codeassist/SelectionEngine.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/compiler/parser/ScannerHelper.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElement.java
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/util/Util.java
Added Paths:
-----------
trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/formatter/IndentManipulation.java
Added: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/formatter/IndentManipulation.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/formatter/IndentManipulation.java (rev 0)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/formatter/IndentManipulation.java 2007-05-02 13:17:38 UTC (rev 2416)
@@ -0,0 +1,427 @@
+/*******************************************************************************
+ * Copyright (c) 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.core.formatter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Map;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DefaultLineTracker;
+import org.eclipse.jface.text.ILineTracker;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.rubypeople.rdt.internal.compiler.parser.ScannerHelper;
+
+/**
+ * Helper class to provide String manipulation functions dealing with indentations.
+ *
+ * @since 1.0
+ */
+public final class IndentManipulation {
+
+ public static final String EMPTY_STRING = ""; //$NON-NLS-1$
+
+ private IndentManipulation() {
+ // don't instantiate
+ }
+
+ /**
+ * Returns <code>true</code> if the given character is an indentation character. Indentation character are all whitespace characters
+ * except the line delimiter characters.
+ *
+ * @param ch the given character
+ * @return Returns <code>true</code> if this the character is a indent character, <code>false</code> otherwise
+ */
+ public static boolean isIndentChar(char ch) {
+ return ScannerHelper.isWhitespace(ch) && !isLineDelimiterChar(ch);
+ }
+
+ /**
+ * Returns <code>true</code> if the given character is a line delimiter character.
+ *
+ * @param ch the given character
+ * @return Returns <code>true</code> if this the character is a line delimiter character, <code>false</code> otherwise
+ */
+ public static boolean isLineDelimiterChar(char ch) {
+ return ch == '\n' || ch == '\r';
+ }
+
+ /**
+ * Returns the indentation of the given line in indentation units. Odd spaces are
+ * not counted. This method only analyzes the content of <code>line</code> up to the first
+ * non-whitespace character.
+ *
+ * @param line the string to measure the indent of
+ * @param tabWidth the width of one tab character in space equivalents
+ * @param indentWidth the width of one indentation unit in space equivalents
+ * @return the number of indentation units that line is indented by
+ * @exception IllegalArgumentException if:
+ * <ul>
+ * <li>the given <code>indentWidth</code> is lower or equals to zero</li>
+ * <li>the given <code>tabWidth</code> is lower than zero</li>
+ * <li>the given <code>line</code> is null</li>
+ * </ul>
+ */
+ public static int measureIndentUnits(CharSequence line, int tabWidth, int indentWidth) {
+ if (indentWidth <= 0 || tabWidth < 0 || line == null) {
+ throw new IllegalArgumentException();
+ }
+
+ int visualLength= measureIndentInSpaces(line, tabWidth);
+ return visualLength / indentWidth;
+ }
+
+ /**
+ * Returns the indentation of the given line in space equivalents.
+ *
+ * <p>Tab characters are counted using the given <code>tabWidth</code> and every other indent
+ * character as one. This method analyzes the content of <code>line</code> up to the first
+ * non-whitespace character.</p>
+ *
+ * @param line the string to measure the indent of
+ * @param tabWidth the width of one tab in space equivalents
+ * @return the measured indent width in space equivalents
+ * @exception IllegalArgumentException if:
+ * <ul>
+ * <li>the given <code>line</code> is null</li>
+ * <li>the given <code>tabWidth</code> is lower than zero</li>
+ * </ul>
+ */
+ public static int measureIndentInSpaces(CharSequence line, int tabWidth) {
+ if (tabWidth < 0 || line == null) {
+ throw new IllegalArgumentException();
+ }
+
+ int length= 0;
+ int max= line.length();
+ for (int i= 0; i < max; i++) {
+ char ch= line.charAt(i);
+ if (ch == '\t') {
+ int reminder= length % tabWidth;
+ length += tabWidth - reminder;
+ } else if (isIndentChar(ch)) {
+ length++;
+ } else {
+ return length;
+ }
+ }
+ return length;
+ }
+
+ /**
+ * Returns the leading indentation string of the given line. Note that the returned string
+ * need not be equal to the leading whitespace as odd spaces are not considered part of the
+ * indentation.
+ *
+ * @param line the line to scan
+ * @param tabWidth the size of one tab in space equivalents
+ * @param indentWidth the width of one indentation unit in space equivalents
+ * @return the indent part of <code>line</code>, but no odd spaces
+ * @exception IllegalArgumentException if:
+ * <ul>
+ * <li>the given <code>indentWidth</code> is lower or equals to zero</li>
+ * <li>the given <code>tabWidth</code> is lower than zero</li>
+ * <li>the given <code>line</code> is null</li>
+ * </ul>
+ */
+ public static String extractIndentString(String line, int tabWidth, int indentWidth) {
+ if (tabWidth < 0 || indentWidth <= 0 || line == null) {
+ throw new IllegalArgumentException();
+ }
+
+ int size= line.length();
+ int end= 0;
+
+ int spaceEquivs= 0;
+ int characters= 0;
+ for (int i= 0; i < size; i++) {
+ char c= line.charAt(i);
+ if (c == '\t') {
+ int remainder= spaceEquivs % tabWidth;
+ spaceEquivs += tabWidth - remainder;
+ characters++;
+ } else if (isIndentChar(c)) {
+ spaceEquivs++;
+ characters++;
+ } else {
+ break;
+ }
+ if (spaceEquivs >= indentWidth) {
+ end += characters;
+ characters= 0;
+ spaceEquivs= spaceEquivs % indentWidth;
+ }
+ }
+ if (end == 0) {
+ return EMPTY_STRING;
+ } else if (end == size) {
+ return line;
+ } else {
+ return line.substring(0, end);
+ }
+ }
+
+
+ /**
+ * Removes the given number of indentation units from a given line. If the line
+ * has less than the given indent, all the available indentation is removed.
+ * If <code>indentsToRemove <= 0</code> the line is returned.
+ *
+ * @param line the line to trim
+ * @param tabWidth the width of one tab in space equivalents
+ * @param indentWidth the width of one indentation unit in space equivalents
+ * @return the trimmed string
+ * @exception IllegalArgumentException if:
+ * <ul>
+ * <li>the given <code>indentWidth</code> is lower or equals to zero</li>
+ * <li>the given <code>tabWidth</code> is lower than zero</li>
+ * <li>the given <code>line</code> is null</li>
+ * </ul>
+ */
+ public static String trimIndent(String line, int indentUnitsToRemove, int tabWidth, int indentWidth) {
+ if (tabWidth < 0 || indentWidth <= 0 || line == null) {
+ throw new IllegalArgumentException();
+ }
+
+ if (indentUnitsToRemove <= 0)
+ return line;
+
+ final int spaceEquivalentsToRemove= indentUnitsToRemove * indentWidth;
+
+ int start= 0;
+ int spaceEquivalents= 0;
+ int size= line.length();
+ String prefix= null;
+ for (int i= 0; i < size; i++) {
+ char c= line.charAt(i);
+ if (c == '\t') {
+ int remainder= spaceEquivalents % tabWidth;
+ spaceEquivalents += tabWidth - remainder;
+ } else if (isIndentChar(c)) {
+ spaceEquivalents++;
+ } else {
+ // Assert.isTrue(false, "Line does not have requested number of indents");
+ start= i;
+ break;
+ }
+ if (spaceEquivalents == spaceEquivalentsToRemove) {
+ start= i + 1;
+ break;
+ }
+ if (spaceEquivalents > spaceEquivalentsToRemove) {
+ // can happen if tabSize > indentSize, e.g tabsize==8, indent==4, indentsToRemove==1, line prefixed with one tab
+ // this implements the third option
+ start= i + 1; // remove the tab
+ // and add the missing spaces
+ char[] missing= new char[spaceEquivalents - spaceEquivalentsToRemove];
+ Arrays.fill(missing, ' ');
+ prefix= new String(missing);
+ break;
+ }
+ }
+ String trimmed;
+ if (start == size)
+ trimmed= EMPTY_STRING;
+ else
+ trimmed= line.substring(start);
+
+ if (prefix == null)
+ return trimmed;
+ return prefix + trimmed;
+ }
+
+ /**
+ * Change the indent of a, possible multiple line, code string. The given number of indent units is removed,
+ * and a new indent string is added.
+ * <p>The first line of the code will not be changed (It is considered to have no indent as it might start in
+ * the middle of a line).</p>
+ *
+ * @param code the code to change the indent of
+ * @param indentUnitsToRemove the number of indent units to remove from each line (except the first) of the given code
+ * @param tabWidth the size of one tab in space equivalents
+ * @param indentWidth the width of one indentation unit in space equivalents
+ * @param newIndentString the new indent string to be added to all lines (except the first)
+ * @param lineDelim the new line delimiter to be used. The returned code will contain only this line delimiter.
+ * @return the newly indent code, containing only the given line delimiters.
+ * @exception IllegalArgumentException if:
+ * <ul>
+ * <li>the given <code>indentWidth</code> is lower or equals to zero</li>
+ * <li>the given <code>tabWidth</code> is lower than zero</li>
+ * <li>the given <code>code</code> is null</li>
+ * <li>the given <code>indentUnitsToRemove</code> is lower than zero</li>
+ * <li>the given <code>newIndentString</code> is null</li>
+ * <li>the given <code>lineDelim</code> is null</li>
+ * </ul>
+ */
+ public static String changeIndent(String code, int indentUnitsToRemove, int tabWidth, int indentWidth, String newIndentString, String lineDelim) {
+ if (tabWidth < 0 || indentWidth <= 0 || code == null || indentUnitsToRemove < 0 || newIndentString == null || lineDelim == null) {
+ throw new IllegalArgumentException();
+ }
+
+ try {
+ ILineTracker tracker= new DefaultLineTracker();
+ tracker.set(code);
+ int nLines= tracker.getNumberOfLines();
+ if (nLines == 1) {
+ return code;
+ }
+
+ StringBuffer buf= new StringBuffer();
+
+ for (int i= 0; i < nLines; i++) {
+ IRegion region= tracker.getLineInformation(i);
+ int start= region.getOffset();
+ int end= start + region.getLength();
+ String line= code.substring(start, end);
+
+ if (i == 0) { // no indent for first line (contained in the formatted string)
+ buf.append(line);
+ } else { // no new line after last line
+ buf.append(lineDelim);
+ buf.append(newIndentString);
+ buf.append(trimIndent(line, indentUnitsToRemove, tabWidth, indentWidth));
+ }
+ }
+ return buf.toString();
+ } catch (BadLocationException e) {
+ // can not happen
+ return code;
+ }
+ }
+
+ /**
+ * Returns the text edits retrieved after changing the indentation of a, possible multi-line, code string.
+ *
+ * <p>The given number of indent units is removed, and a new indent string is added.</p>
+ * <p>The first line of the code will not be changed (It is considered to have no indent as it might start in
+ * the middle of a line).</p>
+ *
+ * @param source The code to change the indent of
+ * @param indentUnitsToRemove the number of indent units to remove from each line (except the first) of the given code
+ * @param tabWidth the size of one tab in space equivalents
+ * @param indentWidth the width of one indentation unit in space equivalents
+ * @param newIndentString the new indent string to be added to all lines (except the first)
+ * @return returns the resulting text edits
+ * @exception IllegalArgumentException if:
+ * <ul>
+ * <li>the given <code>indentWidth</code> is lower or equals to zero</li>
+ * <li>the given <code>tabWidth</code> is lower than zero</li>
+ * <li>the given <code>source</code> is null</li>
+ * <li>the given <code>indentUnitsToRemove</code> is lower than zero</li>
+ * <li>the given <code>newIndentString</code> is null</li>
+ * </ul>
+ */
+ public static ReplaceEdit[] getChangeIndentEdits(String source, int indentUnitsToRemove, int tabWidth, int indentWidth, String newIndentString) {
+ if (tabWidth < 0 || indentWidth <= 0 || source == null || indentUnitsToRemove < 0 || newIndentString == null) {
+ throw new IllegalArgumentException();
+ }
+
+ ArrayList result= new ArrayList();
+ try {
+ ILineTracker tracker= new DefaultLineTracker();
+ tracker.set(source);
+ int nLines= tracker.getNumberOfLines();
+ if (nLines == 1)
+ return (ReplaceEdit[])result.toArray(new ReplaceEdit[result.size()]);
+ for (int i= 1; i < nLines; i++) {
+ IRegion region= tracker.getLineInformation(i);
+ int offset= region.getOffset();
+ String line= source.substring(offset, offset + region.getLength());
+ int length= indexOfIndent(line, indentUnitsToRemove, tabWidth, indentWidth);
+ if (length >= 0) {
+ result.add(new ReplaceEdit(offset, length, newIndentString));
+ } else {
+ length= measureIndentUnits(line, tabWidth, indentWidth);
+ result.add(new ReplaceEdit(offset, length, "")); //$NON-NLS-1$
+ }
+ }
+ } catch (BadLocationException cannotHappen) {
+ // can not happen
+ }
+ return (ReplaceEdit[])result.toArray(new ReplaceEdit[result.size()]);
+ }
+
+ /*
+ * Returns the index where the indent of the given size ends.
+ * Returns <code>-1<code> if the line isn't prefixed with an indent of
+ * the given number of indents.
+ */
+ private static int indexOfIndent(CharSequence line, int numberOfIndentUnits, int tabWidth, int indentWidth) {
+
+ int spaceEquivalents= numberOfIndentUnits * indentWidth;
+
+ int size= line.length();
+ int result= -1;
+ int blanks= 0;
+ for (int i= 0; i < size && blanks < spaceEquivalents; i++) {
+ char c= line.charAt(i);
+ if (c == '\t') {
+ int remainder= blanks % tabWidth;
+ blanks += tabWidth - remainder;
+ } else if (isIndentChar(c)) {
+ blanks++;
+ } else {
+ break;
+ }
+ result= i;
+ }
+ if (blanks < spaceEquivalents)
+ return -1;
+ return result + 1;
+ }
+
+ /**
+ * Returns the tab width as configured in the given map.
+ * <p>Use {@link org.eclipse.jdt.core.IJavaProject#getOptions(boolean)} to get the most current project options.</p>
+ *
+ * @param options the map to get the formatter settings from.
+ *
+ * @return the tab width
+ * @exception IllegalArgumentException if the given <code>options</code> is null
+ */
+ public static int getTabWidth(Map options) {
+ if (options == null) {
+ throw new IllegalArgumentException();
+ }
+ return getIntValue(options, DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, 4);
+ }
+
+ /**
+ * Returns the tab width as configured in the given map.
+ * <p>Use {@link org.eclipse.jdt.core.IJavaProject#getOptions(boolean)} to get the most current project options.</p>
+ *
+ * @param options the map to get the formatter settings from
+ *
+ * @return the indent width
+ * @exception IllegalArgumentException if the given <code>options</code> is null
+ */
+ public static int getIndentWidth(Map options) {
+ if (options == null) {
+ throw new IllegalArgumentException();
+ }
+ int tabWidth=getTabWidth(options);
+ boolean isMixedMode= DefaultCodeFormatterConstants.MIXED.equals(options.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR));
+ if (isMixedMode) {
+ return getIntValue(options, DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE, tabWidth);
+ }
+ return tabWidth;
+ }
+
+ private static int getIntValue(Map options, String key, int def) {
+ try {
+ return Integer.parseInt((String) options.get(key));
+ } catch (NumberFormatException e) {
+ return def;
+ }
+ }
+}
+
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/codeassist/SelectionEngine.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/codeassist/SelectionEngine.java 2007-05-02 13:17:08 UTC (rev 2415)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/codeassist/SelectionEngine.java 2007-05-02 13:17:38 UTC (rev 2416)
@@ -16,9 +16,9 @@
import org.jruby.ast.ClassVarDeclNode;
import org.jruby.ast.ClassVarNode;
import org.jruby.ast.Colon2Node;
+import org.jruby.ast.ConstDeclNode;
import org.jruby.ast.ConstNode;
import org.jruby.ast.DefnNode;
-import org.jruby.ast.DefsNode;
import org.jruby.ast.FCallNode;
import org.jruby.ast.InstAsgnNode;
import org.jruby.ast.InstVarNode;
@@ -54,7 +54,6 @@
import org.rubypeople.rdt.internal.ti.util.ClosestSpanningNodeLocator;
import org.rubypeople.rdt.internal.ti.util.INodeAcceptor;
import org.rubypeople.rdt.internal.ti.util.OffsetNodeLocator;
-import org.rubypeople.rdt.internal.ti.util.ScopedNodeLocator;
public class SelectionEngine {
@@ -117,6 +116,11 @@
.getChildren(), IRubyElement.CLASS_VAR, getName(selected));
return possible.toArray(new IRubyElement[possible.size()]);
}
+ // We're already on the declaration, just return it
+ if ((selected instanceof DefnNode) || (selected instanceof ConstDeclNode)) {
+ IRubyElement element = ((RubyScript)script).getElementAt(start);
+ return new IRubyElement[] {element};
+ }
if (isMethodCall(selected)) {
String methodName = getName(selected);
Set<IRubyElement> possible = new HashSet<IRubyElement>();
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/compiler/parser/ScannerHelper.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/compiler/parser/ScannerHelper.java 2007-05-02 13:17:08 UTC (rev 2415)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/compiler/parser/ScannerHelper.java 2007-05-02 13:17:38 UTC (rev 2416)
@@ -1,6 +1,7 @@
package org.rubypeople.rdt.internal.compiler.parser;
+
public class ScannerHelper {
public final static int MAX_OBVIOUS = 128;
public final static int[] OBVIOUS_IDENT_CHAR_NATURES = new int[MAX_OBVIOUS];
@@ -115,4 +116,16 @@
}
return Character.isJavaIdentifierStart(c);
}
+
+ /**
+ * Include also non JLS whitespaces.
+ *
+ * return true if Character.isWhitespace(c) would return true
+ */
+ public static boolean isWhitespace(char c) {
+ if (c < MAX_OBVIOUS) {
+ return (ScannerHelper.OBVIOUS_IDENT_CHAR_NATURES[c] & ScannerHelper.C_SPACE) != 0;
+ }
+ return Character.isWhitespace(c);
+ }
}
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElement.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElement.java 2007-05-02 13:17:08 UTC (rev 2415)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/RubyElement.java 2007-05-02 13:17:38 UTC (rev 2416)
@@ -477,10 +477,33 @@
}
this.toStringChildren(tab, buffer, info);
}
+ /**
+ * Debugging purposes
+ */
+ public String toStringWithAncestors(boolean showResolvedInfo) {
+ StringBuffer buffer = new StringBuffer();
+ this.toStringInfo(0, buffer, NO_INFO, showResolvedInfo);
+ this.toStringAncestors(buffer);
+ return buffer.toString();
+ }
/**
* Debugging purposes
+ *
+ * @param showResolvedInfo
+ * TODO
*/
+ protected void toStringInfo(int tab, StringBuffer buffer, Object info,
+ boolean showResolvedInfo) {
+ buffer.append(this.tabString(tab));
+ toStringName(buffer);
+ if (info == null) {
+ buffer.append(" (not open)"); //$NON-NLS-1$
+ }
+ }
+ /**
+ * Debugging purposes
+ */
public String toStringWithAncestors() {
StringBuffer buffer = new StringBuffer();
this.toStringInfo(0, buffer, NO_INFO);
Modified: trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/util/Util.java
===================================================================
--- trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/util/Util.java 2007-05-02 13:17:08 UTC (rev 2415)
+++ trunk/org.rubypeople.rdt.core/src/org/rubypeople/rdt/internal/core/util/Util.java 2007-05-02 13:17:38 UTC (rev 2416)
@@ -37,6 +37,7 @@
import org.rubypeople.rdt.core.RubyConventions;
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.core.RubyModelException;
+import org.rubypeople.rdt.internal.core.RubyElement;
/**
* @author Chris
@@ -1000,4 +1001,21 @@
return new String(result);
}
+ /**
+ * Sorts an array of Ruby elements based on their toStringWithAncestors(),
+ * returning a new array with the sorted items.
+ * The original array is left untouched.
+ */
+ public static IRubyElement[] sortCopy(IRubyElement[] elements) {
+ int len = elements.length;
+ IRubyElement[] copy = new IRubyElement[len];
+ System.arraycopy(elements, 0, copy, 0, len);
+ sort(copy, new Comparer() {
+ public int compare(Object a, Object b) {
+ return ((RubyElement) a).toStringWithAncestors().compareTo(((RubyElement) b).toStringWithAncestors());
+ }
+ });
+ return copy;
+ }
+
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|