| /******************************************************************************* |
| * Copyright (c) 2000, 2009 QNX Software Systems 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: |
| * QNX Software Systems - Initial API and implementation |
| * Sergey Prigogin (Google) |
| *******************************************************************************/ |
| package org.eclipse.cdt.internal.ui.text; |
| |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.Region; |
| |
| |
| /** |
| * This is a helper class for the text editor to be able to determine, given a |
| * particular offset in a document, various candidates segments for things like |
| * context help, proposals and hovering. |
| */ |
| public class CWordFinder { |
| |
| private static final char CBRACE_L = '{'; |
| private static final char CBRACE_R = '}'; |
| private static final char PAREN_R = ')'; |
| |
| /** |
| * This method determines for a given offset into a given document what the |
| * region is which defines the current word. A word is defined as a contiguous |
| * sequence of C-identifier characters. So assuming that | indicates the current |
| * cursor position: |
| * <pre> |
| * |afunction(int a, int b) --> word = afunction |
| * afunc|tion(int a, int b) --> word = afunction |
| * afunction|(int a, int b) --> word = afunction |
| * afunction(|int a, int b) --> word = int |
| * afunction(int a,| int b) --> word = length 0 |
| * afunction(|) --> word = length 0 |
| * </pre> |
| * |
| * @param document |
| * The document to be examined |
| * @param offset |
| * The offset into the document where a word should be |
| * identified. |
| * @return The region defining the current word, which may be a region of |
| * length 0 if the offset is not in a word, or null if there is an |
| * error accessing the document data. |
| */ |
| public static IRegion findWord(IDocument document, int offset) { |
| int start = -2; |
| int end = -1; |
| |
| try { |
| int pos = offset; |
| char c; |
| |
| while (--pos >= 0) { |
| c = document.getChar(pos); |
| if (!Character.isJavaIdentifierPart(c)) { |
| break; |
| } |
| } |
| |
| start = pos; |
| |
| pos = offset; |
| int length = document.getLength(); |
| |
| while (pos < length) { |
| c = document.getChar(pos); |
| if (!Character.isJavaIdentifierPart(c)) |
| break; |
| ++pos; |
| } |
| |
| end = pos; |
| } catch (BadLocationException x) { |
| } |
| |
| if (start >= -1 && end > -1) { |
| if (start == offset && end == offset) |
| return new Region(offset, 0); |
| else if (start == offset) |
| return new Region(start, end - start); |
| else |
| return new Region(start + 1, end - start - 1); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * This method will determine the region for the name of the function within |
| * which the current offset is contained. |
| * |
| * @param document |
| * The document to be examined |
| * @param offset |
| * The offset into the document where a word should be |
| * identified. |
| * @return The region defining the current word, which may be a region of |
| * length 0 if the offset is not in a function, or null if there is |
| * an error accessing the document data. |
| */ |
| public static IRegion findFunction(IDocument document, int offset) { |
| int leftbracket = -1; |
| int leftbracketcount = 0; |
| int rightbracket = -1; |
| int rightbracketcount = 0; |
| int functionstart = -1; |
| int functionend = -1; |
| |
| try { |
| int length = document.getLength(); |
| int pos; |
| char c; |
| |
| //Find most relevant right bracket from our position |
| pos = offset; |
| rightbracketcount = leftbracketcount = 0; |
| while (pos < length) { |
| c = document.getChar(pos); |
| |
| if (c == ')') { |
| rightbracketcount++; |
| if (rightbracketcount >= leftbracketcount) { |
| rightbracket = pos; |
| break; |
| } |
| } |
| |
| if (c == '(') { |
| leftbracketcount++; |
| } |
| |
| if (c == ';') { |
| break; |
| } |
| |
| pos++; |
| } |
| |
| if (rightbracket == -1) { |
| return new Region(offset, 0); |
| } |
| |
| // Now backtrack our way from the right bracket to the left |
| pos = rightbracket; |
| rightbracketcount = leftbracketcount = 0; |
| while (pos >= 0) { |
| c = document.getChar(pos); |
| |
| if (c == ')') { |
| rightbracketcount++; |
| } |
| |
| if (c == '(') { |
| leftbracketcount++; |
| if (leftbracketcount >= rightbracketcount) { |
| leftbracket = pos; |
| break; |
| } |
| } |
| |
| if (c == ';') { |
| break; |
| } |
| |
| pos--; |
| } |
| |
| if (leftbracket == -1) { |
| return new Region(offset, 0); |
| } |
| |
| // Now work our way to the function name |
| pos = leftbracket - 1; |
| while (pos >= 0) { |
| c = document.getChar(pos); |
| if (functionend == -1 && c == ' ') { |
| pos--; |
| continue; |
| } |
| |
| if (!Character.isJavaIdentifierPart(c)) { |
| break; |
| } |
| |
| functionstart = pos; |
| if (functionend == -1) { |
| functionend = pos; |
| } |
| |
| pos--; |
| } |
| } catch (BadLocationException x) { |
| /* Ignore */ |
| } |
| |
| if (functionstart > -1 && functionend > -1) { |
| return new Region(functionstart, functionend - functionstart + 1); |
| } |
| |
| return null; |
| } |
| |
| /** |
| * This method will determine whether current offset is contained |
| * in any function's body or it's outside it. |
| * |
| * @param document |
| * The document to be examined |
| * @param offset |
| * The offset into the document |
| * @return |
| * <code>true</code> if there is no function body around offset |
| * <code>false</code> otherwise |
| */ |
| public static boolean isGlobal(IDocument document, int offset) { |
| try { |
| int pos = offset; |
| int bracketcount = 0; |
| char c; |
| |
| // Find left curled brace from our position |
| while (pos > 0) { |
| c = document.getChar(pos--); |
| |
| if (c == CBRACE_R) { |
| bracketcount++; // take into account nested blocks |
| } else if (c == CBRACE_L) { |
| if (bracketcount-- == 0) { |
| do { |
| c = document.getChar(pos--); |
| if (c == PAREN_R) |
| return false; |
| } while (Character.isWhitespace(c)); |
| // Container block seems to be not a function or statement body |
| pos++; // step back one symbol |
| bracketcount = 0; // let's search for upper block |
| } |
| } |
| } |
| } catch (BadLocationException x) { |
| // Ignore |
| } |
| return true; // return true in case of unknown result or exception |
| } |
| |
| /** |
| * Searches for line feed symbols in string. |
| * First met '\r' or '\n' is treated as LF symbol |
| * |
| * @param s |
| * string to search in. |
| * @return number of LFs met. |
| */ |
| public static int countLFs (String s) { |
| int counter = 0; |
| char lf = 0; |
| char c; |
| for (int i= 0; i < s.length(); i++) { |
| c = s.charAt(i); |
| if (lf == 0) { |
| if (c == '\n' || c == '\r') { |
| lf = c; |
| counter++; |
| } |
| } else if (lf == c) { |
| counter++; |
| } |
| } |
| return counter; |
| } |
| |
| /** |
| * Checks whether the string contains any C-block delimiters ( { } ) |
| * |
| * @param s |
| * text to check |
| * @return true if curled brace found. |
| */ |
| public static boolean hasCBraces (String s) { |
| if (s.indexOf(CBRACE_L) > -1 || s.indexOf(CBRACE_R) > -1) |
| return true; |
| return false; |
| } |
| } |