You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							811 lines
						
					
					
						
							22 KiB
						
					
					
				
			
		
		
	
	
							811 lines
						
					
					
						
							22 KiB
						
					
					
				//  | 
						|
// CSharpCompletionEngineBase.cs | 
						|
//   | 
						|
// Author: | 
						|
//       Mike Krüger <mkrueger@xamarin.com> | 
						|
//  | 
						|
// Copyright (c) 2011 Xamarin Inc. (http://xamarin.com) | 
						|
//  | 
						|
// Permission is hereby granted, free of charge, to any person obtaining a copy | 
						|
// of this software and associated documentation files (the "Software"), to deal | 
						|
// in the Software without restriction, including without limitation the rights | 
						|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
						|
// copies of the Software, and to permit persons to whom the Software is | 
						|
// furnished to do so, subject to the following conditions: | 
						|
//  | 
						|
// The above copyright notice and this permission notice shall be included in | 
						|
// all copies or substantial portions of the Software. | 
						|
//  | 
						|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
						|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
						|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
						|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
						|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
						|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
						|
// THE SOFTWARE. | 
						|
using System; | 
						|
using System.Collections.Generic; | 
						|
using System.Linq; | 
						|
using System.Text; | 
						|
 | 
						|
using ICSharpCode.NRefactory.CSharp.Resolver; | 
						|
using ICSharpCode.NRefactory.Editor; | 
						|
using ICSharpCode.NRefactory.TypeSystem; | 
						|
using ICSharpCode.NRefactory.Semantics; | 
						|
using ICSharpCode.NRefactory.TypeSystem.Implementation; | 
						|
using ICSharpCode.NRefactory.CSharp.TypeSystem; | 
						|
 | 
						|
namespace ICSharpCode.NRefactory.CSharp.Completion | 
						|
{ | 
						|
	/// <summary> | 
						|
	/// Acts as a common base between code completion and parameter completion. | 
						|
	/// </summary> | 
						|
	public class CSharpCompletionEngineBase | 
						|
	{ | 
						|
		protected IDocument document; | 
						|
		protected int offset; | 
						|
		protected TextLocation location; | 
						|
		protected IUnresolvedTypeDefinition currentType; | 
						|
		protected IUnresolvedMember currentMember; | 
						|
		 | 
						|
		#region Input properties | 
						|
		public CSharpTypeResolveContext ctx { get; private set; } | 
						|
 | 
						|
		public IProjectContent ProjectContent { get; private set; } | 
						|
		 | 
						|
		ICompilation compilation; | 
						|
 | 
						|
		protected ICompilation Compilation { | 
						|
			get { | 
						|
				if (compilation == null) | 
						|
					compilation = ProjectContent.Resolve (ctx).Compilation; | 
						|
				return compilation; | 
						|
			} | 
						|
		} | 
						|
		#endregion | 
						|
		 | 
						|
		protected CSharpCompletionEngineBase(IProjectContent content, ICompletionContextProvider completionContextProvider, CSharpTypeResolveContext ctx) | 
						|
		{ | 
						|
			if (content == null) | 
						|
				throw new ArgumentNullException("content"); | 
						|
			if (ctx == null) | 
						|
				throw new ArgumentNullException("ctx"); | 
						|
			if (completionContextProvider == null) | 
						|
				throw new ArgumentNullException("completionContextProvider"); | 
						|
			 | 
						|
			this.ProjectContent = content; | 
						|
			this.CompletionContextProvider = completionContextProvider; | 
						|
			this.ctx = ctx; | 
						|
		} | 
						|
		 | 
						|
		 | 
						|
		public ICompletionContextProvider CompletionContextProvider { | 
						|
			get; | 
						|
			private set; | 
						|
		} | 
						|
		 | 
						|
		public void SetOffset (int offset) | 
						|
		{ | 
						|
			Reset (); | 
						|
			 | 
						|
			this.offset = offset; | 
						|
			this.location = document.GetLocation (offset); | 
						|
			CompletionContextProvider.GetCurrentMembers (offset, out currentType, out currentMember); | 
						|
		} | 
						|
 | 
						|
		public bool GetParameterCompletionCommandOffset (out int cpos) | 
						|
		{ | 
						|
			// Start calculating the parameter offset from the beginning of the | 
						|
			// current member, instead of the beginning of the file.  | 
						|
			cpos = offset - 1; | 
						|
			var mem = currentMember; | 
						|
			if (mem == null || (mem is IType)) { | 
						|
				return false; | 
						|
			} | 
						|
			int startPos = document.GetOffset (mem.Region.BeginLine, mem.Region.BeginColumn); | 
						|
			int parenDepth = 0; | 
						|
			int chevronDepth = 0; | 
						|
			Stack<int> indexStack = new Stack<int> (); | 
						|
			while (cpos > startPos) { | 
						|
				char c = document.GetCharAt (cpos); | 
						|
				if (c == ')') { | 
						|
					parenDepth++; | 
						|
				} | 
						|
				if (c == '>') { | 
						|
					chevronDepth++; | 
						|
				} | 
						|
				if (c == '}') { | 
						|
					if (indexStack.Count > 0) { | 
						|
						parenDepth = indexStack.Pop (); | 
						|
					} else { | 
						|
						parenDepth = 0; | 
						|
					} | 
						|
					chevronDepth = 0; | 
						|
				} | 
						|
				if (indexStack.Count == 0 && (parenDepth == 0 && c == '(' || chevronDepth == 0 && c == '<')) { | 
						|
					int p = GetCurrentParameterIndex (startPos, cpos + 1); | 
						|
					if (p != -1) { | 
						|
						cpos++; | 
						|
						return true; | 
						|
					} else { | 
						|
						return false; | 
						|
					} | 
						|
				} | 
						|
				if (c == '(') { | 
						|
					parenDepth--; | 
						|
				} | 
						|
				if (c == '<') { | 
						|
					chevronDepth--; | 
						|
				} | 
						|
				if (c == '{') { | 
						|
					indexStack.Push (parenDepth); | 
						|
					chevronDepth = 0; | 
						|
				} | 
						|
				cpos--; | 
						|
			} | 
						|
			return false; | 
						|
		} | 
						|
		 | 
						|
		public int GetCurrentParameterIndex (int triggerOffset, int endOffset) | 
						|
		{ | 
						|
			char lastChar = document.GetCharAt (endOffset - 1); | 
						|
			if (lastChar == '(' || lastChar == '<') {  | 
						|
				return 0; | 
						|
			} | 
						|
			var parameter = new Stack<int> (); | 
						|
			var bracketStack = new Stack<Stack<int>> (); | 
						|
			bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; | 
						|
			for (int i = triggerOffset; i < endOffset; i++) { | 
						|
				char ch = document.GetCharAt (i); | 
						|
				char nextCh = i + 1 < document.TextLength ? document.GetCharAt (i + 1) : '\0'; | 
						|
				switch (ch) { | 
						|
				case '{': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { | 
						|
						break; | 
						|
					} | 
						|
					bracketStack.Push (parameter); | 
						|
					parameter = new Stack<int> (); | 
						|
					break; | 
						|
				case '[': | 
						|
				case '(': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { | 
						|
						break; | 
						|
					} | 
						|
					parameter.Push (0); | 
						|
					break; | 
						|
				case '}': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { | 
						|
						break; | 
						|
					} | 
						|
					if (bracketStack.Count > 0) { | 
						|
						parameter = bracketStack.Pop (); | 
						|
					} else { | 
						|
						return -1; | 
						|
					} | 
						|
					break; | 
						|
				case ']': | 
						|
				case ')': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { | 
						|
						break; | 
						|
					} | 
						|
					if (parameter.Count > 0) { | 
						|
						parameter.Pop (); | 
						|
					} else { | 
						|
						return -1; | 
						|
					} | 
						|
					break; | 
						|
				case '<': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { | 
						|
						break; | 
						|
					} | 
						|
					parameter.Push (0); | 
						|
					break; | 
						|
				case '>': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { | 
						|
						break; | 
						|
					} | 
						|
					if (parameter.Count > 0) { | 
						|
						parameter.Pop (); | 
						|
					} | 
						|
					break; | 
						|
				case ',': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { | 
						|
						break; | 
						|
					} | 
						|
					if (parameter.Count > 0) { | 
						|
						parameter.Push (parameter.Pop () + 1); | 
						|
					} | 
						|
					break; | 
						|
				case '/': | 
						|
					if (inString || inChar || inVerbatimString) { | 
						|
						break; | 
						|
					} | 
						|
					if (nextCh == '/') { | 
						|
						i++; | 
						|
						inSingleComment = true; | 
						|
					} | 
						|
					if (nextCh == '*') { | 
						|
						inMultiLineComment = true; | 
						|
					} | 
						|
					break; | 
						|
				case '*': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment) { | 
						|
						break; | 
						|
					} | 
						|
					if (nextCh == '/') { | 
						|
						i++; | 
						|
						inMultiLineComment = false; | 
						|
					} | 
						|
					break; | 
						|
				case '@': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) { | 
						|
						break; | 
						|
					} | 
						|
					if (nextCh == '"') { | 
						|
						i++; | 
						|
						inVerbatimString = true; | 
						|
					} | 
						|
					break; | 
						|
				case '\n': | 
						|
				case '\r': | 
						|
					inSingleComment = false; | 
						|
					inString = false; | 
						|
					inChar = false; | 
						|
					break; | 
						|
				case '\\': | 
						|
					if (inString || inChar) { | 
						|
						i++; | 
						|
					} | 
						|
					break; | 
						|
				case '"': | 
						|
					if (inSingleComment || inMultiLineComment || inChar) { | 
						|
						break; | 
						|
					} | 
						|
					if (inVerbatimString) { | 
						|
						if (nextCh == '"') { | 
						|
							i++; | 
						|
							break; | 
						|
						} | 
						|
						inVerbatimString = false; | 
						|
						break; | 
						|
					} | 
						|
					inString = !inString; | 
						|
					break; | 
						|
				case '\'': | 
						|
					if (inSingleComment || inMultiLineComment || inString || inVerbatimString) { | 
						|
						break; | 
						|
					} | 
						|
					inChar = !inChar; | 
						|
					break; | 
						|
				} | 
						|
			} | 
						|
			if (parameter.Count == 0 || bracketStack.Count > 0) { | 
						|
				return -1; | 
						|
			} | 
						|
 | 
						|
			return parameter.Pop() + 1; | 
						|
		} | 
						|
 | 
						|
		#region Context helper methods | 
						|
		public class MiniLexer | 
						|
		{ | 
						|
			readonly string text; | 
						|
 | 
						|
			public bool IsFistNonWs = true; | 
						|
			public bool IsInSingleComment = false; | 
						|
			public bool IsInString = false; | 
						|
			public bool IsInVerbatimString = false; | 
						|
			public bool IsInChar = false; | 
						|
			public bool IsInMultiLineComment = false; | 
						|
			public bool IsInPreprocessorDirective = false; | 
						|
 | 
						|
			public MiniLexer(string text) | 
						|
			{ | 
						|
				this.text = text; | 
						|
			} | 
						|
 | 
						|
			public void Parse(Action<char> act = null) | 
						|
			{ | 
						|
				Parse(0, text.Length, act); | 
						|
			} | 
						|
 | 
						|
			public void Parse(int start, int length, Action<char> act = null) | 
						|
			{ | 
						|
				for (int i = start; i < length; i++) { | 
						|
					char ch = text [i]; | 
						|
					char nextCh = i + 1 < text.Length ? text [i + 1] : '\0'; | 
						|
					switch (ch) { | 
						|
						case '#': | 
						|
							if (IsFistNonWs) | 
						|
								IsInPreprocessorDirective = true; | 
						|
							break; | 
						|
						case '/': | 
						|
							if (IsInString || IsInChar || IsInVerbatimString) | 
						|
								break; | 
						|
							if (nextCh == '/') { | 
						|
								i++; | 
						|
								IsInSingleComment = true; | 
						|
							} | 
						|
							if (nextCh == '*') | 
						|
								IsInMultiLineComment = true; | 
						|
							break; | 
						|
						case '*': | 
						|
							if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment) | 
						|
								break; | 
						|
							if (nextCh == '/') { | 
						|
								i++; | 
						|
								IsInMultiLineComment = false; | 
						|
							} | 
						|
							break; | 
						|
						case '@': | 
						|
							if (IsInString || IsInChar || IsInVerbatimString || IsInSingleComment || IsInMultiLineComment) | 
						|
								break; | 
						|
							if (nextCh == '"') { | 
						|
								i++; | 
						|
								IsInVerbatimString = true; | 
						|
							} | 
						|
							break; | 
						|
						case '\n': | 
						|
						case '\r': | 
						|
							IsInSingleComment = false; | 
						|
							IsInString = false; | 
						|
							IsInChar = false; | 
						|
							IsFistNonWs = true; | 
						|
							IsInPreprocessorDirective = false; | 
						|
							break; | 
						|
						case '\\': | 
						|
							if (IsInString || IsInChar) | 
						|
								i++; | 
						|
							break; | 
						|
						case '"': | 
						|
							if (IsInSingleComment || IsInMultiLineComment || IsInChar) | 
						|
								break; | 
						|
							if (IsInVerbatimString) { | 
						|
								if (nextCh == '"') { | 
						|
									i++; | 
						|
									break; | 
						|
								} | 
						|
								IsInVerbatimString = false; | 
						|
								break; | 
						|
							} | 
						|
							IsInString = !IsInString; | 
						|
							break; | 
						|
						case '\'': | 
						|
							if (IsInSingleComment || IsInMultiLineComment || IsInString || IsInVerbatimString) | 
						|
								break; | 
						|
							IsInChar = !IsInChar; | 
						|
							break; | 
						|
					} | 
						|
					if (act != null) | 
						|
						act(ch); | 
						|
					IsFistNonWs &= ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'; | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
 | 
						|
		 | 
						|
		protected bool IsInsideCommentStringOrDirective(int offset) | 
						|
		{ | 
						|
			var lexer = new MiniLexer(document.Text); | 
						|
			lexer.Parse(0, offset); | 
						|
			return | 
						|
				lexer.IsInSingleComment ||  | 
						|
				lexer.IsInString || | 
						|
				lexer.IsInVerbatimString || | 
						|
				lexer.IsInChar || | 
						|
				lexer.IsInMultiLineComment ||  | 
						|
				lexer.IsInPreprocessorDirective; | 
						|
		} | 
						|
 | 
						|
 | 
						|
		protected bool IsInsideCommentStringOrDirective() | 
						|
		{ | 
						|
			var text = GetMemberTextToCaret(); | 
						|
			var lexer = new MiniLexer(text.Item1); | 
						|
			lexer.Parse(); | 
						|
			return | 
						|
				lexer.IsInSingleComment ||  | 
						|
				lexer.IsInString || | 
						|
				lexer.IsInVerbatimString || | 
						|
				lexer.IsInChar || | 
						|
				lexer.IsInMultiLineComment ||  | 
						|
				lexer.IsInPreprocessorDirective; | 
						|
		} | 
						|
 | 
						|
		protected bool IsInsideDocComment () | 
						|
		{ | 
						|
			var text = GetMemberTextToCaret (); | 
						|
			bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; | 
						|
			bool singleLineIsDoc = false; | 
						|
			 | 
						|
			for (int i = 0; i < text.Item1.Length - 1; i++) { | 
						|
				char ch = text.Item1 [i]; | 
						|
				char nextCh = text.Item1 [i + 1]; | 
						|
				 | 
						|
				switch (ch) { | 
						|
				case '/': | 
						|
					if (inString || inChar || inVerbatimString) | 
						|
						break; | 
						|
					if (nextCh == '/') { | 
						|
						i++; | 
						|
						inSingleComment = true; | 
						|
						singleLineIsDoc = i + 1 < text.Item1.Length && text.Item1 [i + 1] == '/'; | 
						|
						if (singleLineIsDoc) { | 
						|
							i++; | 
						|
						} | 
						|
					} | 
						|
					if (nextCh == '*') | 
						|
						inMultiLineComment = true; | 
						|
					break; | 
						|
				case '*': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment) | 
						|
						break; | 
						|
					if (nextCh == '/') { | 
						|
						i++; | 
						|
						inMultiLineComment = false; | 
						|
					} | 
						|
					break; | 
						|
				case '@': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) | 
						|
						break; | 
						|
					if (nextCh == '"') { | 
						|
						i++; | 
						|
						inVerbatimString = true; | 
						|
					} | 
						|
					break; | 
						|
				case '\n': | 
						|
				case '\r': | 
						|
					inSingleComment = false; | 
						|
					inString = false; | 
						|
					inChar = false; | 
						|
					break; | 
						|
				case '\\': | 
						|
					if (inString || inChar) | 
						|
						i++; | 
						|
					break; | 
						|
				case '"': | 
						|
					if (inSingleComment || inMultiLineComment || inChar) | 
						|
						break; | 
						|
					if (inVerbatimString) { | 
						|
						if (nextCh == '"') { | 
						|
							i++; | 
						|
							break; | 
						|
						} | 
						|
						inVerbatimString = false; | 
						|
						break; | 
						|
					} | 
						|
					inString = !inString; | 
						|
					break; | 
						|
				case '\'': | 
						|
					if (inSingleComment || inMultiLineComment || inString || inVerbatimString) | 
						|
						break; | 
						|
					inChar = !inChar; | 
						|
					break; | 
						|
				} | 
						|
			} | 
						|
			 | 
						|
			return inSingleComment && singleLineIsDoc; | 
						|
		} | 
						|
 | 
						|
		protected CSharpResolver GetState () | 
						|
		{ | 
						|
			return new CSharpResolver (ctx); | 
						|
			/*var state = new CSharpResolver (ctx); | 
						|
			 | 
						|
			state.CurrentMember = currentMember; | 
						|
			state.CurrentTypeDefinition = currentType; | 
						|
			state.CurrentUsingScope = CSharpUnresolvedFile.GetUsingScope (location); | 
						|
			if (state.CurrentMember != null) { | 
						|
				var node = Unit.GetNodeAt (location); | 
						|
				if (node == null) | 
						|
					return state; | 
						|
				var navigator = new NodeListResolveVisitorNavigator (new[] { node }); | 
						|
				var visitor = new ResolveVisitor (state, CSharpUnresolvedFile, navigator); | 
						|
				Unit.AcceptVisitor (visitor, null); | 
						|
				try { | 
						|
					var newState = visitor.GetResolverStateBefore (node); | 
						|
					if (newState != null) | 
						|
						state = newState; | 
						|
				} catch (Exception) { | 
						|
				} | 
						|
			} | 
						|
			 | 
						|
			return state;*/ | 
						|
		} | 
						|
				#endregion | 
						|
		 | 
						|
		#region Basic parsing/resolving functions | 
						|
		static Stack<Tuple<char, int>> GetBracketStack (string memberText) | 
						|
		{ | 
						|
			var bracketStack = new Stack<Tuple<char, int>> (); | 
						|
			 | 
						|
			bool inSingleComment = false, inString = false, inVerbatimString = false, inChar = false, inMultiLineComment = false; | 
						|
			 | 
						|
			for (int i = 0; i < memberText.Length; i++) { | 
						|
				char ch = memberText [i]; | 
						|
				char nextCh = i + 1 < memberText.Length ? memberText [i + 1] : '\0'; | 
						|
				switch (ch) { | 
						|
				case '(': | 
						|
				case '[': | 
						|
				case '{': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) | 
						|
						break; | 
						|
					bracketStack.Push (Tuple.Create (ch, i)); | 
						|
					break; | 
						|
				case ')': | 
						|
				case ']': | 
						|
				case '}': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) | 
						|
						break; | 
						|
					if (bracketStack.Count > 0) | 
						|
						bracketStack.Pop (); | 
						|
					break; | 
						|
				case '/': | 
						|
					if (inString || inChar || inVerbatimString) | 
						|
						break; | 
						|
					if (nextCh == '/') { | 
						|
						i++; | 
						|
						inSingleComment = true; | 
						|
					} | 
						|
					if (nextCh == '*') | 
						|
						inMultiLineComment = true; | 
						|
					break; | 
						|
				case '*': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment) | 
						|
						break; | 
						|
					if (nextCh == '/') { | 
						|
						i++; | 
						|
						inMultiLineComment = false; | 
						|
					} | 
						|
					break; | 
						|
				case '@': | 
						|
					if (inString || inChar || inVerbatimString || inSingleComment || inMultiLineComment) | 
						|
						break; | 
						|
					if (nextCh == '"') { | 
						|
						i++; | 
						|
						inVerbatimString = true; | 
						|
					} | 
						|
					break; | 
						|
				case '\n': | 
						|
				case '\r': | 
						|
					inSingleComment = false; | 
						|
					inString = false; | 
						|
					inChar = false; | 
						|
					break; | 
						|
				case '\\': | 
						|
					if (inString || inChar) | 
						|
						i++; | 
						|
					break; | 
						|
				case '"': | 
						|
					if (inSingleComment || inMultiLineComment || inChar) | 
						|
						break; | 
						|
					if (inVerbatimString) { | 
						|
						if (nextCh == '"') { | 
						|
							i++; | 
						|
							break; | 
						|
						} | 
						|
						inVerbatimString = false; | 
						|
						break; | 
						|
					} | 
						|
					inString = !inString; | 
						|
					break; | 
						|
				case '\'': | 
						|
					if (inSingleComment || inMultiLineComment || inString || inVerbatimString) | 
						|
						break; | 
						|
					inChar = !inChar; | 
						|
					break; | 
						|
				default : | 
						|
					break; | 
						|
				} | 
						|
			} | 
						|
			return bracketStack; | 
						|
		} | 
						|
		 | 
						|
		public static void AppendMissingClosingBrackets (StringBuilder wrapper, string memberText, bool appendSemicolon) | 
						|
		{ | 
						|
			var bracketStack = GetBracketStack (memberText); | 
						|
			bool didAppendSemicolon = !appendSemicolon; | 
						|
			//char lastBracket = '\0'; | 
						|
			while (bracketStack.Count > 0) { | 
						|
				var t = bracketStack.Pop (); | 
						|
				switch (t.Item1) { | 
						|
				case '(': | 
						|
					wrapper.Append (')'); | 
						|
					if (appendSemicolon) | 
						|
						didAppendSemicolon = false; | 
						|
					//lastBracket = ')'; | 
						|
					break; | 
						|
				case '[': | 
						|
					wrapper.Append (']'); | 
						|
					if (appendSemicolon) | 
						|
						didAppendSemicolon = false; | 
						|
					//lastBracket = ']'; | 
						|
					break; | 
						|
				case '<': | 
						|
					wrapper.Append ('>'); | 
						|
					if (appendSemicolon) | 
						|
						didAppendSemicolon = false; | 
						|
					//lastBracket = '>'; | 
						|
					break; | 
						|
				case '{': | 
						|
					int o = t.Item2 - 1; | 
						|
					if (!didAppendSemicolon) { | 
						|
						didAppendSemicolon = true; | 
						|
						wrapper.Append (';'); | 
						|
					} | 
						|
						 | 
						|
					bool didAppendCatch = false; | 
						|
					while (o >= "try".Length) { | 
						|
						char ch = memberText [o]; | 
						|
						if (!char.IsWhiteSpace (ch)) { | 
						|
							if (ch == 'y' && memberText [o - 1] == 'r' && memberText [o - 2] == 't') { | 
						|
								wrapper.Append ("} catch {}"); | 
						|
								didAppendCatch = true; | 
						|
							} | 
						|
							break; | 
						|
						} | 
						|
						o--; | 
						|
					} | 
						|
					if (!didAppendCatch) | 
						|
						wrapper.Append ('}'); | 
						|
					break; | 
						|
				} | 
						|
			} | 
						|
			if (!didAppendSemicolon) | 
						|
				wrapper.Append (';'); | 
						|
		} | 
						|
 | 
						|
		protected SyntaxTree ParseStub(string continuation, bool appendSemicolon = true, string afterContinuation = null) | 
						|
		{ | 
						|
			var mt = GetMemberTextToCaret(); | 
						|
			if (mt == null) { | 
						|
				return null; | 
						|
			} | 
						|
 | 
						|
			string memberText = mt.Item1; | 
						|
			var memberLocation = mt.Item2; | 
						|
			int closingBrackets = 1; | 
						|
			int generatedLines = 0; | 
						|
			var wrapper = new StringBuilder(); | 
						|
			bool wrapInClass = memberLocation != new TextLocation(1, 1); | 
						|
			if (wrapInClass) { | 
						|
				wrapper.Append("class Stub {"); | 
						|
				wrapper.AppendLine(); | 
						|
				closingBrackets++; | 
						|
				generatedLines++; | 
						|
			} | 
						|
			wrapper.Append(memberText); | 
						|
			wrapper.Append(continuation); | 
						|
			AppendMissingClosingBrackets(wrapper, memberText, appendSemicolon); | 
						|
			wrapper.Append(afterContinuation); | 
						|
			if (closingBrackets > 0) {  | 
						|
				wrapper.Append(new string('}', closingBrackets)); | 
						|
			} | 
						|
			var parser = new CSharpParser (); | 
						|
			parser.InitialLocation = new TextLocation(memberLocation.Line - generatedLines, 1); | 
						|
			var result = parser.Parse(wrapper.ToString ()); | 
						|
			return result; | 
						|
		} | 
						|
		 | 
						|
//		string cachedText = null; | 
						|
		 | 
						|
		protected virtual void Reset () | 
						|
		{ | 
						|
//			cachedText = null; | 
						|
		} | 
						|
		 | 
						|
		protected Tuple<string, TextLocation> GetMemberTextToCaret() | 
						|
		{ | 
						|
			return CompletionContextProvider.GetMemberTextToCaret(offset, currentType, currentMember); | 
						|
		} | 
						|
		 | 
						|
		protected ExpressionResult GetInvocationBeforeCursor(bool afterBracket) | 
						|
		{ | 
						|
			SyntaxTree baseUnit; | 
						|
			baseUnit = ParseStub("a", false); | 
						|
			 | 
						|
			var section = baseUnit.GetNodeAt<AttributeSection>(location.Line, location.Column - 2); | 
						|
			var attr = section != null ? section.Attributes.LastOrDefault() : null; | 
						|
			if (attr != null) { | 
						|
				return new ExpressionResult((AstNode)attr, baseUnit); | 
						|
			} | 
						|
 | 
						|
			//var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; | 
						|
			var mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression);  | 
						|
			AstNode expr = null; | 
						|
			if (mref is InvocationExpression) { | 
						|
				expr = ((InvocationExpression)mref).Target; | 
						|
			} else if (mref is ObjectCreateExpression) { | 
						|
				expr = mref; | 
						|
			} else { | 
						|
				baseUnit = ParseStub(")};", false); | 
						|
				mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression);  | 
						|
				if (mref is InvocationExpression) { | 
						|
					expr = ((InvocationExpression)mref).Target; | 
						|
				} else if (mref is ObjectCreateExpression) { | 
						|
					expr = mref; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			if (expr == null) { | 
						|
				// work around for missing ';' bug in mcs: | 
						|
				baseUnit = ParseStub("a", true); | 
						|
			 | 
						|
				section = baseUnit.GetNodeAt<AttributeSection>(location.Line, location.Column - 2); | 
						|
				attr = section != null ? section.Attributes.LastOrDefault() : null; | 
						|
				if (attr != null) { | 
						|
					return new ExpressionResult((AstNode)attr, baseUnit); | 
						|
				} | 
						|
	 | 
						|
				//var memberLocation = currentMember != null ? currentMember.Region.Begin : currentType.Region.Begin; | 
						|
				mref = baseUnit.GetNodeAt(location.Line, location.Column - 1, n => n is InvocationExpression || n is ObjectCreateExpression);  | 
						|
				expr = null; | 
						|
				if (mref is InvocationExpression) { | 
						|
					expr = ((InvocationExpression)mref).Target; | 
						|
				} else if (mref is ObjectCreateExpression) { | 
						|
					expr = mref; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			if (expr == null) { | 
						|
				return null; | 
						|
			} | 
						|
			return new ExpressionResult ((AstNode)expr, baseUnit); | 
						|
		} | 
						|
		 | 
						|
		public class ExpressionResult | 
						|
		{ | 
						|
			public AstNode Node { get; private set; } | 
						|
			public SyntaxTree Unit  { get; private set; } | 
						|
			 | 
						|
			 | 
						|
			public ExpressionResult (AstNode item2, SyntaxTree item3) | 
						|
			{ | 
						|
				this.Node = item2; | 
						|
				this.Unit = item3; | 
						|
			} | 
						|
			 | 
						|
			public override string ToString () | 
						|
			{ | 
						|
				return string.Format ("[ExpressionResult: Node={0}, Unit={1}]", Node, Unit); | 
						|
			} | 
						|
		} | 
						|
		 | 
						|
		protected Tuple<ResolveResult, CSharpResolver> ResolveExpression (ExpressionResult tuple) | 
						|
		{ | 
						|
			return ResolveExpression (tuple.Node); | 
						|
		} | 
						|
 | 
						|
		protected Tuple<ResolveResult, CSharpResolver> ResolveExpression(AstNode expr) | 
						|
		{ | 
						|
			if (expr == null) { | 
						|
				return null; | 
						|
			} | 
						|
			AstNode resolveNode; | 
						|
			if (expr is Expression || expr is AstType) { | 
						|
				resolveNode = expr; | 
						|
			} else if (expr is VariableDeclarationStatement) { | 
						|
				resolveNode = ((VariableDeclarationStatement)expr).Type; | 
						|
			} else { | 
						|
				resolveNode = expr; | 
						|
			} | 
						|
			try { | 
						|
				var root = expr.AncestorsAndSelf.FirstOrDefault(n => n is EntityDeclaration || n is SyntaxTree); | 
						|
				if (root == null) { | 
						|
					return null; | 
						|
				} | 
						|
				if (root is Accessor) | 
						|
					root = root.Parent; | 
						|
				var csResolver = CompletionContextProvider.GetResolver (GetState(), root); | 
						|
				var result = csResolver.Resolve(resolveNode); | 
						|
				var state = csResolver.GetResolverStateBefore(resolveNode); | 
						|
				return Tuple.Create(result, state); | 
						|
			} catch (Exception e) { | 
						|
				Console.WriteLine(e); | 
						|
				return null; | 
						|
			} | 
						|
		} | 
						|
		 | 
						|
		#endregion | 
						|
	} | 
						|
} |