blob: 218e64910cd836e2cc0ee27804aa029081ae722d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 xored software, Inc.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* xored software, Inc. - initial API and Implementation (Alex Panchenko)
*******************************************************************************/
package org.eclipse.dltk.internal.javascript.typeinference;
import java.util.Stack;
public class CompletionString {
private static class Bracket {
final char ch;
final int position;
public Bracket(char ch, int position) {
this.ch = ch;
this.position = position;
}
}
public static String parse(String id, boolean dotBeforeBrackets) {
return parse(id, dotBeforeBrackets, false);
}
public static String parse(String id, boolean dotBeforeBrackets,
boolean functionCallParenthesis) {
StringBuffer sb = new StringBuffer();
int start = 0;
int current = id.length();
final Stack<Bracket> inBrackStack = new Stack<Bracket>();
boolean inStringSingle = false;
boolean inStringDouble = false;
outer: for (int i = id.length(); --i >= 0;) {
char c = id.charAt(i);
if (c == '\'') {
if (inStringSingle) {
inStringSingle = false;
continue;
}
// end of a string try to skip this.
if (!inStringDouble)
inStringSingle = true;
}
if (c == '\"') {
if (inStringDouble) {
inStringDouble = false;
continue;
}
// end of a string try to skip this.
if (!inStringSingle)
inStringDouble = true;
}
if (inStringSingle || inStringDouble)
continue;
if (c == ']') {
if (inBrackStack.isEmpty()) {
String brackets = "[]";
if (dotBeforeBrackets && i > 0
&& ((i - 2) < 0 || id.charAt(i - 2) != '.')) {
brackets = ".[]";
}
sb.insert(0, brackets + id.substring(i + 1, current));
}
inBrackStack.push(new Bracket('[', i));
continue;
}
if (c == ')') {
if (inBrackStack.isEmpty()) {
if (functionCallParenthesis) {
String parens = "()";
if (dotBeforeBrackets && i > 0
&& ((i - 2) < 0 || id.charAt(i - 2) != '.')) {
parens = ".()";
}
sb.insert(0, parens + id.substring(i + 1, current));
} else {
sb.insert(0, id.substring(i + 1, current));
}
}
inBrackStack.push(new Bracket('(', i));
continue;
}
if (c == '[' || c == '(') {
if (inBrackStack.isEmpty()) {
if (i + 1 < id.length() && id.charAt(i + 1) == c) {
// illegal code like [[xx]. try best guess
id = id.substring(0, i) + id.substring(i + 1);
return parse(id, dotBeforeBrackets,
functionCallParenthesis);
}
return id.substring(i + 1, current) + sb.toString();
}
if (c == inBrackStack.peek().ch) {
current = i;
inBrackStack.pop();
}
continue;
}
if (c == ':') {
if (i >= 1 && id.charAt(i - 1) == ':') {
// "::" is part of XML expressions
--i;
continue;
} else {
// label, object literal, etc.
start = i + 1;
break;
}
}
// for now only support {}
if (c == '}' && inBrackStack.isEmpty() && i >= 1
&& id.charAt(i - 1) == '{') {
return "{}.";
}
if (c != '.'
&& c != '@'
&& inBrackStack.isEmpty()
&& (Character.isWhitespace(c) || !Character
.isJavaIdentifierPart(c))) {
int k = i;
while (k-- > 0) {
if (!Character.isWhitespace(id.charAt(k))) {
if (id.charAt(k) == '.') {
int lastLineBreak = id.lastIndexOf('\n', k);
int nextCommentTag = id
.indexOf("//", lastLineBreak);
if (nextCommentTag == -1 || nextCommentTag > k) {
i = k + 1;
continue outer;
}
}
break;
}
}
start = i + 1;
break;
}
if (c == '.') {
// skip white space
while (--i >= 0) {
if (!Character.isWhitespace(id.charAt(i))) {
i++;
break;
}
}
}
}
if (start == 0 && current == id.length() && inBrackStack.isEmpty())
return id;
if (!inBrackStack.isEmpty()) { // illegal code like []]
Bracket last = inBrackStack.pop();
id = id.substring(start, last.position)
+ id.substring(last.position + 1, id.length());
return parse(id, dotBeforeBrackets, functionCallParenthesis);
}
sb.insert(0, id.substring(start, current));
if (dotBeforeBrackets && sb.length() > 1 && sb.charAt(0) == '.'
&& sb.charAt(1) == '[') {
// don't return a . before the brackets when it starts with this (so
// the start of the completion is the array itself)
return sb.substring(1);
}
return sb.toString().replaceAll("\\s", "");
}
}