Bug 141864 - Ant content assist suggest nested target
diff --git a/ant/org.eclipse.ant.tests.ui/Ant Editor Tests/org/eclipse/ant/tests/ui/editor/CodeCompletionTest.java b/ant/org.eclipse.ant.tests.ui/Ant Editor Tests/org/eclipse/ant/tests/ui/editor/CodeCompletionTest.java
index 23ceac8..9174c02 100644
--- a/ant/org.eclipse.ant.tests.ui/Ant Editor Tests/org/eclipse/ant/tests/ui/editor/CodeCompletionTest.java
+++ b/ant/org.eclipse.ant.tests.ui/Ant Editor Tests/org/eclipse/ant/tests/ui/editor/CodeCompletionTest.java
@@ -501,7 +501,6 @@
      */
     public void testTaskProposals() {
 		TestTextCompletionProcessor processor = new TestTextCompletionProcessor(getAntModel("buildtest1.xml"));
-
        
         ICompletionProposal[] proposals = processor.getTaskProposals("         <", "rename", "");
         assertEquals(0, proposals.length);
@@ -524,6 +523,40 @@
         processor.dispose();
     }
     
+    public void testTargetTemplateProposals() throws BadLocationException, PartInitException {
+    	try {
+			IFile file= getIFile("buildtest1.xml");
+			AntEditor editor= (AntEditor)EditorTestHelper.openInEditor(file, ANT_EDITOR_ID, true);
+			TestTextCompletionProcessor processor= new TestTextCompletionProcessor(editor);
+			int lineNumber= 7;
+	    	int columnNumber= 6;
+	    	int lineOffset= editor.getDocumentProvider().getDocument(editor.getEditorInput()).getLineOffset(lineNumber);
+	    	processor.setLineNumber(lineNumber);
+	    	processor.setColumnNumber(columnNumber);
+	    	processor.setCursorPosition(lineOffset + columnNumber);
+	    	
+	    	//complete inside a target
+	    	ICompletionProposal[] proposals= processor.determineTemplateProposals();
+	    	assertDoesNotContain("target - public target", proposals);
+	    	
+	    	//complete outside of a target
+	    	lineNumber= 8;
+	    	columnNumber= 13;
+	    	lineOffset= editor.getDocumentProvider().getDocument(editor.getEditorInput()).getLineOffset(lineNumber);
+	    	processor.setLineNumber(lineNumber);
+	    	processor.setColumnNumber(columnNumber);
+	    	processor.setCursorPosition(lineOffset + columnNumber);
+	    	
+	    	proposals= processor.determineTemplateProposals();
+	    	assertContains("target - public target", proposals);
+	    	//ensure all the tasks are still there
+	    	proposals = processor.getProposalsFromDocument(editor.getDocumentProvider().getDocument(editor.getEditorInput()), "");
+	    	assertContains("ant", proposals);
+		} finally {
+			EditorTestHelper.closeAllEditors();
+		}
+    	
+    }
     /**
      * Tests the code completion for the fail task
      * bug 73637
diff --git a/ant/org.eclipse.ant.ui/Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorCompletionProcessor.java b/ant/org.eclipse.ant.ui/Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorCompletionProcessor.java
index 67140d8..67fa44b 100644
--- a/ant/org.eclipse.ant.ui/Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorCompletionProcessor.java
+++ b/ant/org.eclipse.ant.ui/Ant Editor/org/eclipse/ant/internal/ui/editor/AntEditorCompletionProcessor.java
@@ -22,9 +22,9 @@
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.lang.reflect.InvocationTargetException;
-import com.ibm.icu.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Comparator;
 import java.util.Enumeration;
 import java.util.HashMap;
@@ -56,6 +56,7 @@
 import org.eclipse.ant.internal.ui.editor.templates.AntTemplateInformationControlCreator;
 import org.eclipse.ant.internal.ui.editor.templates.AntTemplateProposal;
 import org.eclipse.ant.internal.ui.editor.templates.BuildFileContextType;
+import org.eclipse.ant.internal.ui.editor.templates.TargetContextType;
 import org.eclipse.ant.internal.ui.editor.templates.TaskContextType;
 import org.eclipse.ant.internal.ui.model.AntDefiningTaskNode;
 import org.eclipse.ant.internal.ui.model.AntElementNode;
@@ -74,6 +75,7 @@
 import org.eclipse.jface.text.ITextSelection;
 import org.eclipse.jface.text.ITextViewer;
 import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.Region;
 import org.eclipse.jface.text.contentassist.ContentAssistEvent;
 import org.eclipse.jface.text.contentassist.ICompletionListener;
 import org.eclipse.jface.text.contentassist.ICompletionProposal;
@@ -86,6 +88,8 @@
 import org.eclipse.jface.text.templates.TemplateCompletionProcessor;
 import org.eclipse.jface.text.templates.TemplateContext;
 import org.eclipse.jface.text.templates.TemplateContextType;
+import org.eclipse.jface.text.templates.TemplateException;
+import org.eclipse.jface.text.templates.TemplateProposal;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.ui.IEditorPart;
@@ -100,11 +104,21 @@
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
+import com.ibm.icu.text.MessageFormat;
+
 /**
  * The completion processor for the Ant Editor.
  */
 public class AntEditorCompletionProcessor  extends TemplateCompletionProcessor implements IContentAssistProcessor, ICompletionListener  {       
  
+	private static final class ProposalComparator implements Comparator {
+		public int compare(Object o1, Object o2) {
+			return ((TemplateProposal) o2).getRelevance() - ((TemplateProposal) o1).getRelevance();
+		}
+	}
+
+	private static final Comparator fgProposalComparator= new ProposalComparator();
+	
  	private Comparator proposalComparator= new Comparator() {
 		public int compare(Object o1, Object o2) {
 		    
@@ -265,9 +279,9 @@
         String prefix = getCurrentPrefix();
         ICompletionProposal[] matchingTemplateProposals;
         if (prefix.length() == 0) {
-            matchingTemplateProposals = super.computeCompletionProposals(refViewer, documentOffset);
+            matchingTemplateProposals = determineTemplateProposalsForContext(documentOffset);
         } else {
-            ICompletionProposal[] templateProposals = super.computeCompletionProposals(refViewer, documentOffset);
+            ICompletionProposal[] templateProposals = determineTemplateProposalsForContext(documentOffset);
             List templateProposalList = new ArrayList(templateProposals.length);
             for (int i = 0; i < templateProposals.length; i++) {
                 if (templateProposals[i].getDisplayString().toLowerCase().startsWith(prefix)) {
@@ -280,6 +294,56 @@
 		return matchingTemplateProposals;
 	}
 
+	//essentially a copy of super.computeCompletionProposals but we need to have both context types work
+	//for target (task and target) context type in a backwards compatible way
+	private ICompletionProposal[] determineTemplateProposalsForContext(int offset) {
+		ITextSelection selection= (ITextSelection) viewer.getSelectionProvider().getSelection();
+
+		// adjust offset to end of normalized selection
+		if (selection.getOffset() == offset) {
+			offset= selection.getOffset() + selection.getLength();
+		}
+
+		String prefix= extractPrefix(viewer, offset);
+		Region region= new Region(offset - prefix.length(), prefix.length());
+		TemplateContext context= createContext(viewer, region);
+		if (context == null) {
+			return new ICompletionProposal[0];
+		}
+		
+		context.setVariable("selection", selection.getText()); // name of the selection variables {line, word}_selection //$NON-NLS-1$
+
+		Template[] templates;
+		String contextTypeId = context.getContextType().getId();
+		boolean isTargetContextType = contextTypeId.equals(TargetContextType.TARGET_CONTEXT_TYPE);
+		if (isTargetContextType) {
+			Template[] tasks = AntTemplateAccess.getDefault().getTemplateStore().getTemplates(TaskContextType.TASK_CONTEXT_TYPE);
+			Template[] targets = getTemplates(contextTypeId);
+			templates = new Template[tasks.length + targets.length];
+			System.arraycopy(tasks, 0, templates, 0, tasks.length);
+			System.arraycopy(targets, 0, templates, tasks.length, targets.length);
+		} else {
+			templates = getTemplates(contextTypeId);
+		}
+
+		List matches= new ArrayList();
+		for (int i= 0; i < templates.length; i++) {
+			Template template= templates[i];
+			try {
+				context.getContextType().validate(template.getPattern());
+			} catch (TemplateException e) {
+				continue;
+			}
+			if (template.matches(prefix, contextTypeId) || (isTargetContextType && template.matches(prefix, TaskContextType.TASK_CONTEXT_TYPE))) {
+				matches.add(createProposal(template, context, (IRegion) region, getRelevance(template, prefix)));
+			}
+		}
+
+		Collections.sort(matches, fgProposalComparator);
+
+		return (ICompletionProposal[]) matches.toArray(new ICompletionProposal[matches.size()]);
+	}
+	
 	private ICompletionProposal[] mergeProposals(ICompletionProposal[] proposals1, ICompletionProposal[] proposals2) {
 
         ICompletionProposal[] combinedProposals = new ICompletionProposal[proposals1.length + proposals2.length];
@@ -1641,6 +1705,9 @@
 	protected TemplateContextType getContextType(ITextViewer textViewer, IRegion region) {
 		 switch (determineProposalMode(textViewer.getDocument(), cursorPosition, getCurrentPrefix())) {
             case PROPOSAL_MODE_TASK_PROPOSAL:
+            	if (getEnclosingTargetName(textViewer.getDocument(), lineNumber, columnNumber) == null) {
+            		return AntTemplateAccess.getDefault().getContextTypeRegistry().getContextType(TargetContextType.TARGET_CONTEXT_TYPE);
+            	}
             	return AntTemplateAccess.getDefault().getContextTypeRegistry().getContextType(TaskContextType.TASK_CONTEXT_TYPE);
             case PROPOSAL_MODE_BUILDFILE:
             	return AntTemplateAccess.getDefault().getContextTypeRegistry().getContextType(BuildFileContextType.BUILDFILE_CONTEXT_TYPE);
diff --git a/ant/org.eclipse.ant.ui/Ant Editor/org/eclipse/ant/internal/ui/editor/templates/AntTemplateAccess.java b/ant/org.eclipse.ant.ui/Ant Editor/org/eclipse/ant/internal/ui/editor/templates/AntTemplateAccess.java
index ffa9532..f9da791 100644
--- a/ant/org.eclipse.ant.ui/Ant Editor/org/eclipse/ant/internal/ui/editor/templates/AntTemplateAccess.java
+++ b/ant/org.eclipse.ant.ui/Ant Editor/org/eclipse/ant/internal/ui/editor/templates/AntTemplateAccess.java
@@ -74,6 +74,7 @@
 			// create and configure the contexts available in the template editor
 			fRegistry= new ContributionContextTypeRegistry();
 			fRegistry.addContextType(BuildFileContextType.BUILDFILE_CONTEXT_TYPE);
+			fRegistry.addContextType(TargetContextType.TARGET_CONTEXT_TYPE);
 			fRegistry.addContextType(TaskContextType.TASK_CONTEXT_TYPE);
 		}
 		return fRegistry;
diff --git a/ant/org.eclipse.ant.ui/Ant Editor/org/eclipse/ant/internal/ui/editor/templates/TargetContextType.java b/ant/org.eclipse.ant.ui/Ant Editor/org/eclipse/ant/internal/ui/editor/templates/TargetContextType.java
new file mode 100644
index 0000000..8dfa2b0
--- /dev/null
+++ b/ant/org.eclipse.ant.ui/Ant Editor/org/eclipse/ant/internal/ui/editor/templates/TargetContextType.java
@@ -0,0 +1,16 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.eclipse.ant.internal.ui.editor.templates;
+
+public class TargetContextType extends BuildFileContextType {
+	public static final String TARGET_CONTEXT_TYPE= "org.eclipse.ant.ui.templateContextType.target"; //$NON-NLS-1$
+}
diff --git a/ant/org.eclipse.ant.ui/plugin.properties b/ant/org.eclipse.ant.ui/plugin.properties
index 63752bf..d3d745f 100644
--- a/ant/org.eclipse.ant.ui/plugin.properties
+++ b/ant/org.eclipse.ant.ui/plugin.properties
@@ -24,7 +24,6 @@
 AddTask.label=Add &Task...
 AddTask.tooltip=Add Task...
 
-
 CategoryView.antViews = Ant
 
 View.antView = Ant
@@ -38,9 +37,9 @@
 PreferencePage.antTemplatePreferences = Templates
 PreferencePage.antCodeAssistPreferences = Content Assist
 
-
 buildFile.contextType.name= Ant
 task.contextType.name= Ant Task
+target.contextType.name= Ant Target
 
 antDocumentSetupParticipant.name= Ant Document Setup Participant
 
diff --git a/ant/org.eclipse.ant.ui/plugin.xml b/ant/org.eclipse.ant.ui/plugin.xml
index ad871db..2ecbb22 100644
--- a/ant/org.eclipse.ant.ui/plugin.xml
+++ b/ant/org.eclipse.ant.ui/plugin.xml
@@ -529,6 +529,11 @@
             id="org.eclipse.ant.ui.templateContextType.buildFile">
       </contextType>
        <contextType
+            name="%target.contextType.name"
+            class="org.eclipse.ant.internal.ui.editor.templates.TargetContextType"
+            id="org.eclipse.ant.ui.templateContextType.target">
+      </contextType>
+       <contextType
             name="%task.contextType.name"
             class="org.eclipse.ant.internal.ui.editor.templates.TaskContextType"
             id="org.eclipse.ant.ui.templateContextType.task">
diff --git a/ant/org.eclipse.ant.ui/templates/ant.xml b/ant/org.eclipse.ant.ui/templates/ant.xml
index d824d43..6e9c90b 100644
--- a/ant/org.eclipse.ant.ui/templates/ant.xml
+++ b/ant/org.eclipse.ant.ui/templates/ant.xml
@@ -71,7 +71,7 @@
 </template>
 
 <template 
-	context="org.eclipse.ant.ui.templateContextType.task" 
+	context="org.eclipse.ant.ui.templateContextType.target" 
 	description="%targetTemplate1.description" 
 	id="org.eclipse.ant.ui.templates.target1" 
 	name="target"><![CDATA[<!-- ================================= 
@@ -83,7 +83,7 @@
 ]]></template>
 
 <template 
-    context="org.eclipse.ant.ui.templateContextType.task" 
+    context="org.eclipse.ant.ui.templateContextType.target" 
     description="%targetTemplate2.description" 
     id="org.eclipse.ant.ui.templates.target2" 
     name="target"><![CDATA[<!-- - - - - - - - - - - - - - - - - -