/*******************************************************************************
 * Copyright (c) 2017 TypeFox GmbH (http://www.typefox.io) 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
 *******************************************************************************/
package org.eclipse.xtext.ide.editor.contentassist;

import org.eclipse.xtext.AbstractElement;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.nodemodel.BidiTreeIterator;
import org.eclipse.xtext.nodemodel.ILeafNode;
import org.eclipse.xtext.nodemodel.INode;

/**
 * Small utility to extract the parsable content of a document when 
 * content assist is invoked at a given location.
 * 
 * @author Sebastian Zarnekow - Initial contribution and API
 * @since 2.13
 */
public class CompletionPrefixProvider {

	/**
	 * Standard languages only need to parse the text up to the last hidden token sequence
	 * in the document. Thus the string from start to the normalized offset is sufficient.
	 */
	public String getInputToParse(String completeInput, int offset, int completionOffset) {
		return completeInput.substring(0, offset);
	}
	
	/**
	 * Returns the last node that appears to be part of the prefix. This will be used to determine
	 * the current model object that'll be the most special context instance in the proposal provider.
	 */
	public INode getLastCompleteNodeByOffset(INode node, int offset, int completionOffset) {
		BidiTreeIterator<INode> iterator = node.getRootNode().getAsTreeIterable().iterator();
		INode result = node;
		while (iterator.hasNext()) {
			INode candidate = iterator.next();
			if (candidate.getOffset() >= offset) {
				break;
			} else if ((candidate instanceof ILeafNode) && (candidate.getGrammarElement() == null
					|| candidate.getGrammarElement() instanceof AbstractElement
					|| candidate.getGrammarElement() instanceof ParserRule)) {
				result = candidate;
			}
		}
		return result;
	}
	
}

@@ -640,32 +640,30 @@
 demodel.
-BidiTreeIterator
+ICompositeNode
 ;%0Aimport
@@ -817,20 +817,16 @@
 nt when 
-%0A * 
 content 
@@ -1081,20 +1081,16 @@
 sequence
-%0A%09 *
  in the 
@@ -1107,16 +1107,20 @@
 Thus the
+%0A%09 *
  string 
@@ -1312,17 +1312,16 @@
 et);%0A%09%7D%0A
-%09
 %0A%09/**%0A%09 
@@ -1413,20 +1413,16 @@
 etermine
-%0A%09 *
  the cur
@@ -1431,16 +1431,20 @@
 nt model
+%0A%09 *
  object 
@@ -1575,32 +1575,40 @@
 node, int offset
+Position
 , int completion
@@ -1623,185 +1623,238 @@
 %7B%0A%09%09
-BidiTreeIterator%3CINode%3E iterator = node.getRootNode().getAsTreeIterable().iterator();%0A%09%09INode result = node;%0A%09%09while (iterator.hasNext()
+return internalGetLastCompleteNodeByOffset(node.getRootNode(), offsetPosition);%0A%09%7D%0A%0A%09private INode internalGetLastCompleteNodeByOffset(ICompositeNode node, int offsetPosition
 ) %7B%0A%09%09
-%09
+for (
 INode c
-andidate = iterator.next();
+hild : node.getChildren().reverse()) %7B
 %0A%09%09%09
@@ -1858,24 +1858,20 @@
 %09%09%09if (c
-andidate
+hild
 .getOffs
@@ -1879,32 +1879,161 @@
 t() 
-%3E=
+%3C
  offset
-) %7B%0A%09%09%09%09break
+Position) %7B%0A%09%09%09%09if (child instanceof ICompositeNode) %7B%0A%09%09%09%09%09return internalGetLastCompleteNodeByOffset((ICompositeNode) child, offsetPosition)
 ;%0A
+%09
 %09%09%09%7D
@@ -2044,24 +2044,20 @@
 e if ((c
-andidate
+hild
  instanc
@@ -2074,22 +2074,24 @@
 ode)
- && (candidate
+%0A%09%09%09%09%09%09&& (child
 .get
@@ -2114,34 +2114,25 @@
  == null
-%0A%09%09%09%09%09%7C%7C candidate
+ %7C%7C child
 .getGram
@@ -2180,20 +2180,19 @@
 %09%09%09%09
+%09%09%09
 %7C%7C c
-andidate
+hild
 .get
@@ -2242,27 +2242,28 @@
 %09%09%09%09
+%09
 re
-sult = candidate;
+turn child;%0A%09%09%09%09%7D
 %0A%09%09%09
@@ -2281,19 +2281,16 @@
 urn 
-result
+node
 ;%0A%09%7D%0A
-%09
 %0A%7D%0A

Use more efficient algorithm.