Hello,

    This is an updated version of the completion patch.   The only
difference is a fix for the bug that Marek reported and the ChangeLog
entries.


Index: mcs/cs-tokenizer.cs
===================================================================
--- mcs/cs-tokenizer.cs	(revision 129881)
+++ mcs/cs-tokenizer.cs	(working copy)
@@ -113,6 +113,14 @@
 		bool tokens_seen = false;
 
 		//
+		// Set to true once the GENERATE_COMPLETION token has bee
+		// returned.   This helps produce one GENERATE_COMPLETION,
+		// as many COMPLETE_COMPLETION as necessary to complete the
+		// AST tree and one final EOF.
+		//
+		bool generated;
+		
+		//
 		// Whether a token has been seen on the file
 		// This is needed because `define' is not allowed to be used
 		// after a token has been seen.
@@ -152,6 +160,10 @@
 			}
 		}
 
+		//
+		// This is used to trigger completin generation on the parser
+		public bool CompleteOnEOF;
+		
 		void AddEscapedIdentifier (LocatedToken lt)
 		{
 			if (escaped_identifiers == null)
@@ -1542,7 +1554,7 @@
 
 		public bool advance ()
 		{
-			return peek_char () != -1;
+			return peek_char () != -1 || CompleteOnEOF;
 		}
 
 		public Object Value {
@@ -2599,7 +2611,7 @@
 					}
 
 					return Token.OP_GT;
-				
+
 				case '+':
 					d = peek_char ();
 					if (d == '+') {
@@ -2870,7 +2882,16 @@
 				error_details = ((char)c).ToString ();
 				return Token.ERROR;
 			}
+
+			if (CompleteOnEOF){
+				if (generated)
+					return Token.COMPLETE_COMPLETION;
+				
+				generated = true;
+				return Token.GENERATE_COMPLETION;
+			}
 			
+
 			return Token.EOF;
 		}
 
Index: mcs/mcs.exe.sources
===================================================================
--- mcs/mcs.exe.sources	(revision 129881)
+++ mcs/mcs.exe.sources	(working copy)
@@ -7,6 +7,7 @@
 cfold.cs
 class.cs
 codegen.cs
+complete.cs
 const.cs
 constant.cs
 convert.cs
Index: mcs/eval.cs
===================================================================
--- mcs/eval.cs	(revision 129881)
+++ mcs/eval.cs	(working copy)
@@ -37,6 +37,18 @@
 	/// </remarks>
 	public class Evaluator {
 
+		enum ParseMode {
+			// Parse silently, do not output any error messages
+			Silent,
+
+			// Report errors during parse
+			ReportErrors,
+
+			// Auto-complete, means that the tokenizer will start producing
+			// GETCOMPLETIONS tokens when it reaches a certain point.
+			GetCompletions
+		}
+
 		static object evaluator_lock = new object ();
 		
 		static string current_debug_name;
@@ -190,13 +202,13 @@
 					Init ();
 				
 				bool partial_input;
-				CSharpParser parser = ParseString (true, input, out partial_input);
+				CSharpParser parser = ParseString (ParseMode.Silent, input, out partial_input);
 				if (parser == null){
 					compiled = null;
 					if (partial_input)
 						return input;
 					
-					ParseString (false, input, out partial_input);
+					ParseString (ParseMode.ReportErrors, input, out partial_input);
 					return null;
 				}
 				
@@ -317,7 +329,68 @@
 
 			return null;
 		}
+
+		public static string [] GetCompletions (string input, out string prefix)
+		{
+			prefix = "";
+			if (input == null || input.Length == 0)
+				return null;
 			
+			lock (evaluator_lock){
+				if (!inited)
+					Init ();
+				
+				bool partial_input;
+				CSharpParser parser = ParseString (ParseMode.GetCompletions, input, out partial_input);
+				if (parser == null){
+					Console.WriteLine ("DEBUG: No completions available");
+					return null;
+				}
+				
+				Class parser_result = parser.InteractiveResult as Class;
+				
+				if (parser_result == null){
+					Console.WriteLine ("Do not know how to cope with !Class yet");
+					return null;
+				}
+
+				try {
+					RootContext.ResolveTree ();
+					if (Report.Errors != 0)
+						return null;
+					
+					RootContext.PopulateTypes ();
+					if (Report.Errors != 0)
+						return null;
+
+					MethodOrOperator method = null;
+					foreach (MemberCore member in parser_result.Methods){
+						if (member.Name != "Host")
+							continue;
+						
+						method = (MethodOrOperator) member;
+						break;
+					}
+					if (method == null)
+						throw new InternalErrorException ("did not find the the Host method");
+
+					EmitContext ec = method.CreateEmitContext (method.Parent, null);
+					bool unreach;
+
+					try {
+						ec.ResolveTopBlock (null, method.Block, method.ParameterInfo, method, out unreach);
+					} catch (CompletionResult cr){
+						prefix = cr.BaseText;
+						return cr.Result;
+					}
+				} finally {
+					parser.undo.ExecuteUndo ();
+				}
+				
+			}
+			return null;
+		}
+		
 		/// <summary>
 		///   Executes the given expression or statement.
 		/// </summary>
@@ -364,7 +437,7 @@
 
 			return result;
 		}
-		
+	
 		enum InputKind {
 			EOF,
 			StatementOrExpression,
@@ -486,7 +559,7 @@
 		// @partial_input: if @silent is true, then it returns whether the
 		// parsed expression was partial, and more data is needed
 		//
-		static CSharpParser ParseString (bool silent, string input, out bool partial_input)
+		static CSharpParser ParseString (ParseMode mode, string input, out bool partial_input)
 		{
 			partial_input = false;
 			Reset ();
@@ -497,14 +570,14 @@
 
 			InputKind kind = ToplevelOrStatement (seekable);
 			if (kind == InputKind.Error){
-				if (!silent)
+				if (mode == ParseMode.ReportErrors)
 					Report.Error (-25, "Detection Parsing Error");
 				partial_input = false;
 				return null;
 			}
 
 			if (kind == InputKind.EOF){
-				if (silent == false)
+				if (mode == ParseMode.ReportErrors)
 					Console.Error.WriteLine ("Internal error: EOF condition should have been detected in a previous call with silent=true");
 				partial_input = true;
 				return null;
@@ -529,20 +602,23 @@
 				RootContext.StatementMode = false;
 			}
 
-			if (silent)
+			if (mode == ParseMode.GetCompletions)
+				parser.Lexer.CompleteOnEOF = true;
+			
+			if (mode == ParseMode.Silent)
 				Report.DisableReporting ();
 			try {
 				parser.parse ();
 			} finally {
 				if (Report.Errors != 0){
-					if (silent && parser.UnexpectedEOF)
+					if (mode != ParseMode.ReportErrors  && parser.UnexpectedEOF)
 						partial_input = true;
 
 					parser.undo.ExecuteUndo ();
 					parser = null;
 				}
 
-				if (silent)
+				if (mode == ParseMode.Silent)
 					Report.EnableReporting ();
 			}
 			return parser;
@@ -702,6 +778,13 @@
 			}
 		}
 
+		static internal string [] GetVarNames ()
+		{
+			lock (evaluator_lock){
+				return (string []) new ArrayList (fields.Keys).ToArray (typeof (string));
+			}
+		}
+		
 		static public string GetVars ()
 		{
 			lock (evaluator_lock){
Index: mcs/gmcs.exe.sources
===================================================================
--- mcs/gmcs.exe.sources	(revision 129881)
+++ mcs/gmcs.exe.sources	(working copy)
@@ -6,6 +6,7 @@
 cfold.cs
 class.cs
 codegen.cs
+complete.cs
 const.cs
 constant.cs
 convert.cs
Index: mcs/smcs.exe.sources
===================================================================
--- mcs/smcs.exe.sources	(revision 129881)
+++ mcs/smcs.exe.sources	(working copy)
@@ -7,6 +7,7 @@
 cfold.cs
 class.cs
 codegen.cs
+complete.cs
 const.cs
 constant.cs
 convert.cs
Index: mcs/Makefile
===================================================================
--- mcs/Makefile	(revision 129881)
+++ mcs/Makefile	(working copy)
@@ -143,6 +143,9 @@
 	MONO_PATH="../class/lib/net_2_0$(PLATFORM_PATH_SEPARATOR)$$MONO_PATH" $(RUNTIME) $(RUNTIME_FLAGS) ../class/lib/net_2_0/gmcs.exe  /codepage:65001 -d:GMCS_SOURCE  -d:NET_1_1 -d:NET_2_0 -debug -target:exe -out:gmcs.exe cs-parser.cs  @gmcs.exe.sources
 	@ cp $(COMPILER_NAME).exe* $(topdir)/class/lib/$(PROFILE)/
 
+pa: cs-parser.cs
+	MONO_PATH="../class/lib/net_2_0$(PLATFORM_PATH_SEPARATOR)$$MONO_PATH" $(RUNTIME) $(RUNTIME_FLAGS) ../class/lib/net_2_0/gmcs.exe  /codepage:65001 -d:GMCS_SOURCE  -d:NET_1_1 -d:NET_2_0 -debug -target:exe -out:foo.exe cs-parser.cs foo.cs -main:X  @gmcs.exe.sources
+
 q: cs-parser.cs qh
 	echo 'System.Console.WriteLine ("Hello");' | mono csharp.exe
 	echo -e 'using System;\nConsole.WriteLine ("hello");' | mono csharp.exe
Index: mcs/ChangeLog
===================================================================
--- mcs/ChangeLog	(revision 129990)
+++ mcs/ChangeLog	(working copy)
@@ -1,5 +1,70 @@
 2009-03-22  Miguel de Icaza  <[email protected]>
 
+	Initial support to provide code completion facilities to consumers
+	of the evaluator API.
+	
+	* cs-tokenizer.cs (CompleteOnEOF): this new property is used to
+	support the completion engine.   When we reach the end of the
+	input stream instead of returning EOF, when this flag is true the
+	tokenizer instead produces:
+
+		One GENERATE_COMPLETION token: this token then must be
+		handled in the grammar at every point where the user
+		would likely request a completion.
+
+		As many COMPLETE_COMPLETION tokens as necessary.   These
+		tokens are generated to assist the parser in unwinding and
+		producing a valid parse tree.    
+
+	The parser rules do not have to be perfect, the parser needs to be
+	augmented with judicious use of GENERATE_COMPLETION tokens to
+	improve the areas where we can provide completion and the parser
+	needs to add support for COMPLETE_COMPLETION tokens in productions
+	to make them work.
+
+	It is common to not have enough support for COMPLETE_COMPLETION
+	under certain rules and that even if we generated the
+	GENERATE_COMPLETION token that the resulting tree will be invalid
+	due to the missing rules that support COMPLETE_COMPLETION.
+
+	The final EOF token is produced by having the parser notify the
+	tokenizer when it reaches the root production that the next token
+	should be EOF.
+
+	* support.cs (CompletionResult): New Exception.   This exception
+	is thrown to return the completion results when one of the special
+	completion expressions is reached.
+
+	This exception is thrown by the completing ExpressionStatements
+	classes that live in complete.cs
+
+	* complete.cs (CompletingExpression): a new base class for
+	completing expressions.   This derives from the
+	ExpressionStatement class and not from Expression as it allows
+	completion to happen not only where expressions are expected in
+	the grammar, but also where statements are expected.
+
+	(CompletionSimpleName): A new class used to provide completions
+	for SimpleNames.     This currently only resolves to local
+	variables from the evaluator context (GetVars call).
+
+	(CompletionMemberAccess): Implements support for completing member
+	access patterns. 
+
+	* cs-parser.jay: Add support for completion in a few places. 
+
+	* eval.cs (GetCompletions): New public API for the evaluator that
+	returns a list of possible completions given the input.   The
+	return value is an array of completions 
+
+	* anonymous.cs (Compatible): If the exception thrown from the
+	resolved expression is a CompletionResult exception let that one
+	through instead of printing a diagnostic error in the try/catch. 
+	
+2009-03-22  Miguel de Icaza  <[email protected]>
+
+	* 
+
 	* driver.cs (Main): Use Environment.Exit to quit quickly and
 	prevent the compiler from doing the usual wait for helper thread
 	to terminate.  
Index: mcs/anonymous.cs
===================================================================
--- mcs/anonymous.cs	(revision 129881)
+++ mcs/anonymous.cs	(working copy)
@@ -1064,6 +1064,8 @@
 					compatibles.Add (type, am == null ? EmptyExpression.Null : am);
 
 				return am;
+			} catch (CompletionResult){
+				throw;
 			} catch (Exception e) {
 				throw new InternalErrorException (e, loc);
 			}
Index: mcs/support.cs
===================================================================
--- mcs/support.cs	(revision 129881)
+++ mcs/support.cs	(working copy)
@@ -480,4 +480,30 @@
 			}
 		}
 	}
+
+	public class CompletionResult : Exception {
+		string [] result;
+		string base_text;
+		
+		public CompletionResult (string base_text, string [] res)
+		{
+			if (base_text == null)
+				throw new ArgumentNullException ("base_text");
+			this.base_text = base_text;
+			
+			result = res;
+		}
+
+		public string [] Result {
+			get {
+				return result;
+			}
+		}
+
+		public string BaseText {
+			get {
+				return base_text;
+			}
+		}
+	}
 }
Index: mcs/complete.cs
===================================================================
--- mcs/complete.cs	(revision 0)
+++ mcs/complete.cs	(revision 0)
@@ -0,0 +1,174 @@
+//
+// complete.cs: Expression that are used for completion suggestions.
+//
+// Author:
+//   Miguel de Icaza ([email protected])
+//   Marek Safar ([email protected])
+//
+// Copyright 2001, 2002, 2003 Ximian, Inc.
+// Copyright 2003-2009 Novell, Inc.
+//
+// Completion* classes derive from ExpressionStatement as this allows
+// them to pass through the parser in many conditions that require
+// statements even when the expression is incomplete (for example
+// completing inside a lambda
+//
+namespace Mono.CSharp {
+	using System;
+	using System.Collections;
+	using System.Reflection;
+	using System.Reflection.Emit;
+	using System.Text;
+
+	//
+	// A common base class for Completing expressions, it
+	// is just a very simple ExpressionStatement
+	//
+	public abstract class CompletingExpression : ExpressionStatement {
+		public override void EmitStatement (EmitContext ec)
+		{
+			// Do nothing
+		}
+
+		public override void Emit (EmitContext ec)
+		{
+			// Do nothing
+		}
+
+		public override Expression CreateExpressionTree (EmitContext ec)
+		{
+			return null;
+		}
+	}
+	
+	public class CompletionSimpleName : CompletingExpression {
+		string prefix;
+		
+		public CompletionSimpleName (string prefix, Location l)
+		{
+			this.loc = l;
+			this.prefix = prefix;
+		}
+
+		public override Expression DoResolve (EmitContext ec)
+		{
+			string [] names = Evaluator.GetVarNames ();
+
+			ArrayList results = new ArrayList ();
+			foreach (string name in names){
+				if (!name.StartsWith (prefix))
+					continue;
+
+				if (results.Contains (name))
+					continue;
+
+				results.Add (name);
+			}
+			throw new CompletionResult (prefix, (string []) results.ToArray (typeof (string)));
+		}
+
+		protected override void CloneTo (CloneContext clonectx, Expression t)
+		{
+			// Nothing
+		}
+	}
+	
+	public class CompletionMemberAccess : CompletingExpression {
+		Expression expr;
+		string partial_name;
+		TypeArguments targs;
+
+		static MemberFilter CollectingFilter = new MemberFilter (Match);
+
+		static bool Match (MemberInfo m, object filter_criteria)
+		{
+			if (m is FieldInfo){
+				if (((FieldInfo) m).IsSpecialName)
+					return false;
+				
+			}
+			if (m is MethodInfo){
+				if (((MethodInfo) m).IsSpecialName)
+					return false;
+			}
+
+			if (filter_criteria == null)
+				return true;
+			
+			string n = (string) filter_criteria;
+			if (m.Name.StartsWith (n))
+				return true;
+			
+			return false;
+		}
+		
+		public CompletionMemberAccess (Expression e, string partial_name, Location l)
+		{
+			this.expr = e;
+			this.loc = l;
+			this.partial_name = partial_name;
+		}
+
+		public CompletionMemberAccess (Expression e, string partial_name, TypeArguments targs, Location l)
+		{
+			this.expr = e;
+			this.loc = l;
+			this.partial_name = partial_name;
+			this.targs = targs;
+		}
+		
+		public override Expression DoResolve (EmitContext ec)
+		{
+			SimpleName original = expr as SimpleName;
+			Expression expr_resolved = expr.Resolve (ec,
+				ResolveFlags.VariableOrValue | ResolveFlags.Type |
+				ResolveFlags.Intermediate | ResolveFlags.DisableStructFlowAnalysis);
+
+			if (expr_resolved == null)
+				return null;
+
+			Type expr_type = expr_resolved.Type;
+			if (expr_type.IsPointer || expr_type == TypeManager.void_type || expr_type == TypeManager.null_type || expr_type == TypeManager.anonymous_method_type) {
+				Unary.Error_OperatorCannotBeApplied (loc, ".", expr_type);
+				return null;
+			}
+
+			if (targs != null) {
+				if (!targs.Resolve (ec))
+					return null;
+			}
+
+			ArrayList results = new ArrayList ();
+			MemberInfo [] result = expr_type.FindMembers (MemberTypes.All, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public, CollectingFilter, partial_name);
+			foreach (MemberInfo r in result){
+				string name;
+
+				MethodBase rasb = r as MethodBase;
+				if (rasb != null && rasb.IsSpecialName)
+					continue;
+				
+				if (partial_name == null)
+					name = r.Name;
+				else {
+					name = r.Name.Substring (partial_name.Length);
+				}
+				
+				if (results.Contains (name))
+					continue;
+				results.Add (name);
+			}
+
+			throw new CompletionResult (partial_name == null ? "" : partial_name, (string []) results.ToArray (typeof (string)));
+		}
+
+		protected override void CloneTo (CloneContext clonectx, Expression t)
+		{
+			CompletionMemberAccess target = (CompletionMemberAccess) t;
+
+			if (targs != null)
+				target.targs = targs.Clone ();
+
+			target.expr = expr.Clone (clonectx);
+		}
+	}
+}
\ No newline at end of file

Property changes on: mcs/complete.cs
___________________________________________________________________
Added: svn:eol-style
   + native

Index: mcs/cs-parser.jay
===================================================================
--- mcs/cs-parser.jay	(revision 129881)
+++ mcs/cs-parser.jay	(working copy)
@@ -330,6 +330,9 @@
 %token EVAL_COMPILATION_UNIT_PARSER
 %token EVAL_USING_DECLARATIONS_UNIT_PARSER
 
+%token GENERATE_COMPLETION
+%token COMPLETE_COMPLETION
+
 /* Add precedence rules to solve dangling else s/r conflict */
 %nonassoc IF
 %nonassoc ELSE
@@ -359,7 +362,7 @@
         | outer_declarations global_attributes opt_EOF
         | global_attributes opt_EOF
 	| opt_EOF /* allow empty files */
-	| interactive_parsing opt_EOF
+	| interactive_parsing  { Lexer.CompleteOnEOF = false; } opt_EOF
 	;
 
 opt_EOF
@@ -2933,6 +2936,10 @@
 		LocatedToken lt = (LocatedToken) $1;
 		$$ = new SimpleName (MemberName.MakeName (lt.Value, (TypeArguments)$2), (TypeArguments)$2, lt.Location);	  
 	  }
+	| IDENTIFIER GENERATE_COMPLETION {
+		LocatedToken lt = (LocatedToken) $1;
+	       $$ = new CompletionSimpleName (MemberName.MakeName (lt.Value, null), lt.Location);
+	  }
 	| parenthesized_expression
 	| default_value_expression
 	| member_access
@@ -3006,6 +3013,10 @@
 	  {
 		$$ = new ParenthesizedExpression ((Expression) $2);
 	  }
+	| OPEN_PARENS expression COMPLETE_COMPLETION
+	  {
+		$$ = new ParenthesizedExpression ((Expression) $2);
+	  }
 	;
 	
 member_access
@@ -3027,6 +3038,22 @@
 
 		$$ = new QualifiedAliasMember (lt1.Value, lt2.Value, (TypeArguments) $3, lt1.Location);
 	  }
+	| primary_expression DOT GENERATE_COMPLETION {
+		$$ = new CompletionMemberAccess ((Expression) $1, null,GetLocation ($3));
+	  }
+	| primary_expression DOT IDENTIFIER GENERATE_COMPLETION {
+		LocatedToken lt = (LocatedToken) $3;
+		$$ = new CompletionMemberAccess ((Expression) $1, lt.Value, lt.Location);
+	  }
+	| predefined_type DOT GENERATE_COMPLETION
+	  {
+		// TODO: Location is wrong as some predefined types doesn't hold a location
+		$$ = new CompletionMemberAccess ((Expression) $1, null, lexer.Location);
+	  }
+	| predefined_type DOT IDENTIFIER GENERATE_COMPLETION {
+		LocatedToken lt = (LocatedToken) $3;
+		$$ = new CompletionMemberAccess ((Expression) $1, lt.Value, lt.Location);
+ 	  }
 	;
 
 invocation_expression
@@ -4010,8 +4037,8 @@
 	;
 
 expression
-	: assignment_expression
-	| non_assignment_expression
+	: assignment_expression 
+	| non_assignment_expression 
 	;
 	
 non_assignment_expression
@@ -4231,13 +4258,26 @@
 		++lexer.parsing_block;
 		start_block ((Location) $1);
 	  } 
-	  opt_statement_list CLOSE_BRACE 
+	  opt_statement_list block_end
 	  {
+		$$ = $4;
+          }
+	;
+
+block_end 
+	: CLOSE_BRACE 
+	  {
 	 	--lexer.parsing_block;
-		$$ = end_block ((Location) $4);
+		$$ = end_block ((Location) $1);
 	  }
+	| COMPLETE_COMPLETION
+	  {
+	 	--lexer.parsing_block;
+		$$ = end_block (lexer.Location);
+	  }
 	;
 
+
 block_prepared
 	: OPEN_BRACE
 	  {
@@ -4523,10 +4563,12 @@
 
 expression_statement
 	: statement_expression SEMICOLON { $$ = $1; }
+	| statement_expression COMPLETE_COMPLETION { $$ = $1; }
 	;
 
 interactive_expression_statement
 	: interactive_statement_expression SEMICOLON { $$ = $1; }
+	| interactive_statement_expression COMPLETE_COMPLETION { $$ = $1; }
 	;
 
 	//
@@ -5655,7 +5697,7 @@
 	        ++lexer.parsing_block;
 		start_block (lexer.Location);
 	  }		
-	  interactive_statement_list
+	  interactive_statement_list opt_COMPLETE_COMPLETION
 	  {
 		--lexer.parsing_block;
 		Method method = (Method) oob_stack.Pop ();
@@ -5679,6 +5721,11 @@
 	| global_attributes 
 	| /* nothing */
 	;
+
+opt_COMPLETE_COMPLETION
+	: /* nothing */
+	| COMPLETE_COMPLETION
+	;
 %%
 
 // <summary>
Index: mcs/README
===================================================================
--- mcs/README	(revision 129881)
+++ mcs/README	(working copy)
@@ -1,4 +1,16 @@
+Completion support
+==================
 
+	Supported:
+	
+		a.<TAB>
+		a.W<TAB>
+	
+	Unsupported:
+	
+		a<TAB>
+		delegate { FOO.<TAB>
+	
 These are the sources to the Mono C# compiler 
 ---------------------------------------------
 
Index: mcs/codegen.cs
===================================================================
--- mcs/codegen.cs	(revision 129881)
+++ mcs/codegen.cs	(working copy)
@@ -15,7 +15,7 @@
 //
 // Only remove it if you need to debug locally on your tree.
 //
-#define PRODUCTION
+//#define PRODUCTION
 
 using System;
 using System.IO;
@@ -766,7 +766,7 @@
 
 		bool resolved;
 		bool unreachable;
-
+		
 		public bool ResolveTopBlock (EmitContext anonymous_method_host, ToplevelBlock block,
 					     ParametersCompiled ip, IMethodData md, out bool unreachable)
 		{
Index: tools/csharp/repl.cs
===================================================================
--- tools/csharp/repl.cs	(revision 129881)
+++ tools/csharp/repl.cs	(working copy)
@@ -44,6 +44,7 @@
 			} else {
 				try {
 					Evaluator.Init (args);
+					Evaluator.InteractiveBaseClass = typeof (InteractiveBaseShell);
 				} catch {
 					return 1;
 				}
@@ -52,6 +53,36 @@
 			}
 		}
 	}
+
+	public class InteractiveBaseShell : InteractiveBase {
+		static bool tab_at_start_completes;
+		
+		static InteractiveBaseShell ()
+		{
+			tab_at_start_completes = false;
+		}
+
+		internal static Mono.Terminal.LineEditor Editor;
+		
+		public static bool TabAtStartCompletes {
+			get {
+				return tab_at_start_completes;
+			}
+
+			set {
+				tab_at_start_completes = value;
+				if (Editor != null)
+					Editor.TabAtStartCompletes = value;
+			}
+		}
+
+		public static new string help {
+			get {
+				return InteractiveBase.help +
+					"  TabAtStartCompletes - Whether tab will complete even on emtpy lines\n";
+			}
+		}
+	}
 	
 	public class CSharpShell {
 		static bool isatty = true;
@@ -73,6 +104,18 @@
 			dumb = term == "dumb" || term == null || isatty == false;
 			
 			editor = new Mono.Terminal.LineEditor ("csharp", 300);
+			InteractiveBaseShell.Editor = editor;
+
+			editor.AutoCompleteEvent += delegate (string s, int pos){
+				string prefix = null;
+
+				string complete = s.Substring (0, pos);
+				
+				string [] completions = Evaluator.GetCompletions (complete, out prefix);
+				
+				return new Mono.Terminal.LineEditor.Completion (prefix, completions);
+			};
+			
 #if false
 			//
 			// This is a sample of how completions sould be implemented.
Index: tools/csharp/getline.cs
===================================================================
--- tools/csharp/getline.cs	(revision 129881)
+++ tools/csharp/getline.cs	(working copy)
@@ -42,8 +42,19 @@
 
 	public class LineEditor {
 
-		public delegate string [] AutoCompleteHandler (string text, int pos);
+		public class Completion {
+			public string [] Result;
+			public string Prefix;
+
+			public Completion (string prefix, string [] result)
+			{
+				Prefix = prefix;
+				Result = result;
+			}
+		}
 		
+		public delegate Completion AutoCompleteHandler (string text, int pos);
+		
 		//static StreamWriter log;
 		
 		// The text being edited.
@@ -356,22 +367,53 @@
 			bool complete = false;
 
 			if (AutoCompleteEvent != null){
-				for (int i = 0; i < cursor; i++){
-					if (!Char.IsWhiteSpace (text [i])){
-						complete = true;
-						break;
+				if (TabAtStartCompletes)
+					complete = true;
+				else {
+					for (int i = 0; i < cursor; i++){
+						if (!Char.IsWhiteSpace (text [i])){
+							complete = true;
+							break;
+						}
 					}
 				}
+
 				if (complete){
-					string [] completions = AutoCompleteEvent (text.ToString (), cursor);
-					if (completions == null || completions.Length == 0)
+					Completion completion = AutoCompleteEvent (text.ToString (), cursor);
+					string [] completions = completion.Result;
+					if (completions == null)
 						return;
 					
+					int ncompletions = completions.Length;
+					if (ncompletions == 0)
+						return;
+					
 					if (completions.Length == 1){
 						InsertTextAtCursor (completions [0]);
 					} else {
+						int last = -1;
+						
+						for (int p = 0; p < completions [0].Length; p++){
+							char c = completions [0][p];
+
+
+							for (int i = 1; i < ncompletions; i++){
+								if (completions [i].Length < p)
+									goto mismatch;
+							
+								if (completions [i][p] != c){
+									goto mismatch;
+								}
+							}
+							last = p;
+						}
+					mismatch:
+						if (last != -1){
+							InsertTextAtCursor (completions [0].Substring (0, last+1));
+						}
 						Console.WriteLine ();
 						foreach (string s in completions){
+							Console.Write (completion.Prefix);
 							Console.Write (s);
 							Console.Write (' ');
 						}
@@ -827,6 +869,8 @@
 			return result;
 		}
 
+		public bool TabAtStartCompletes { get; set; }
+			
 		//
 		// Emulates the bash-like behavior, where edits done to the
 		// history are recorded
_______________________________________________
Mono-devel-list mailing list
[email protected]
http://lists.ximian.com/mailman/listinfo/mono-devel-list

Reply via email to