mirror of https://github.com/icsharpcode/ILSpy.git
				
				
			
			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.
		
		
		
		
		
			
		
			
				
					
					
						
							270 lines
						
					
					
						
							9.2 KiB
						
					
					
				
			
		
		
	
	
							270 lines
						
					
					
						
							9.2 KiB
						
					
					
				using System; | 
						|
using System.Collections.Generic; | 
						|
using System.Text; | 
						|
using ICSharpCode.Decompiler.CSharp.Syntax; | 
						|
 | 
						|
namespace ICSharpCode.ILSpy.AddIn | 
						|
{ | 
						|
	/// <summary> | 
						|
	/// Provides XML documentation tags for Visual Studio CodeElements. | 
						|
	/// </summary> | 
						|
	/// <remarks> | 
						|
	/// Used to support the "/navigateTo" command line option when opening ILSpy. Must match | 
						|
	/// the logic of ICSharpCode.ILSpy.XmlDoc.XmlDocKeyProvider, which does the same thing for | 
						|
	/// a Mono.Cecil.MemberReference. See "ID string format" in Appendix A of the C# language | 
						|
	/// specification for formatting requirements, and Samples/ILSpyAddInSamples.cs for examples. | 
						|
	/// </remarks> | 
						|
	public static class CodeElementXmlDocKeyProvider | 
						|
	{ | 
						|
		#region GetKey | 
						|
		public static string GetKey(EnvDTE.CodeElement member) | 
						|
		{ | 
						|
			StringBuilder b = new StringBuilder(); | 
						|
			if ((member.Kind == EnvDTE.vsCMElement.vsCMElementDelegate) || | 
						|
				(member.Kind == EnvDTE.vsCMElement.vsCMElementEnum) || | 
						|
				(member.Kind == EnvDTE.vsCMElement.vsCMElementInterface) || | 
						|
				(member.Kind == EnvDTE.vsCMElement.vsCMElementStruct) || | 
						|
				(member.Kind == EnvDTE.vsCMElement.vsCMElementClass)) { | 
						|
				b.Append("T:"); | 
						|
				AppendTypeName(b, member.FullName, true, false); | 
						|
			} | 
						|
			else if (member.Kind == EnvDTE.vsCMElement.vsCMElementNamespace){ | 
						|
				b.Append("N:"); | 
						|
				b.Append(member.FullName); | 
						|
			} | 
						|
			else { | 
						|
				if (member.Kind == EnvDTE.vsCMElement.vsCMElementVariable) | 
						|
					b.Append("F:"); | 
						|
				else if (member.Kind == EnvDTE.vsCMElement.vsCMElementProperty) | 
						|
					b.Append("P:"); | 
						|
				else if (member.Kind == EnvDTE.vsCMElement.vsCMElementEvent) | 
						|
					b.Append("E:"); | 
						|
				else if (member.Kind == EnvDTE.vsCMElement.vsCMElementFunction) | 
						|
					b.Append("M:"); | 
						|
 | 
						|
				int nameIndex = member.FullName.LastIndexOf(member.Name); | 
						|
				string typeName = member.FullName.Substring(0, nameIndex - 1); | 
						|
				string memberName = member.FullName.Substring(nameIndex); | 
						|
 | 
						|
				// Name substitutions for special cases. | 
						|
				if (member.Kind == EnvDTE.vsCMElement.vsCMElementFunction) { | 
						|
					EnvDTE80.CodeFunction2 mr = (EnvDTE80.CodeFunction2)member; | 
						|
					if (mr.FunctionKind == EnvDTE.vsCMFunction.vsCMFunctionConstructor) { | 
						|
						memberName = memberName.Replace(member.Name, "#ctor"); | 
						|
					} | 
						|
					else if (mr.FunctionKind == EnvDTE.vsCMFunction.vsCMFunctionDestructor) { | 
						|
						memberName = memberName.Replace(member.Name, "Finalize"); | 
						|
					} | 
						|
					else if (mr.FunctionKind == EnvDTE.vsCMFunction.vsCMFunctionOperator) { | 
						|
						if (memberName.StartsWith("implicit operator")) { | 
						|
							memberName = "op_Implicit"; | 
						|
						} | 
						|
						else if (memberName.StartsWith("explicit operator")) { | 
						|
							memberName = "op_Explicit"; | 
						|
						} | 
						|
						else { | 
						|
							// NRefactory has a handy mapping we can make use of, just need to extract the operator symbol first. | 
						|
							string[] memberNameWords = member.Name.Split(' '); | 
						|
							if (memberNameWords.Length >= 2) { | 
						|
								string operatorSymbol = memberNameWords[1]; | 
						|
								string operatorName = OperatorDeclaration.GetName(OperatorDeclaration.GetOperatorType(operatorSymbol)); | 
						|
								if (operatorName != null) { | 
						|
									memberName = memberName.Replace(member.Name, operatorName); | 
						|
								} | 
						|
							} | 
						|
						} | 
						|
					} | 
						|
				} | 
						|
				else if (member.Kind == EnvDTE.vsCMElement.vsCMElementProperty) { | 
						|
					if (member.Name == "this") { | 
						|
						memberName = memberName.Replace(member.Name, "Item"); | 
						|
					} | 
						|
				} | 
						|
 | 
						|
				string[] genericTypeParameters = AppendTypeName(b, typeName, true, false); | 
						|
				b.Append('.'); | 
						|
				string[] genericMethodParameters = AppendTypeName(b, memberName.Replace('.', '#'), true, true); | 
						|
				EnvDTE.CodeElements parameters; | 
						|
				EnvDTE.CodeTypeRef explicitReturnType = null; | 
						|
				if (member.Kind == EnvDTE.vsCMElement.vsCMElementProperty) { | 
						|
					parameters = ((EnvDTE.CodeProperty)member).Getter.Parameters; | 
						|
				} | 
						|
				else if (member.Kind == EnvDTE.vsCMElement.vsCMElementFunction) { | 
						|
					EnvDTE80.CodeFunction2 mr = (EnvDTE80.CodeFunction2)member; | 
						|
					parameters = mr.Parameters; | 
						|
					if (memberName == "op_Implicit" || memberName == "op_Explicit") { | 
						|
						explicitReturnType = mr.Type; | 
						|
					} | 
						|
				} | 
						|
				else { | 
						|
					parameters = null; | 
						|
				} | 
						|
				if (parameters != null && parameters.Count > 0) { | 
						|
					b.Append('('); | 
						|
					int i = 0; | 
						|
					foreach (EnvDTE80.CodeParameter2 parameter in parameters) { | 
						|
						if (i > 0) b.Append(','); | 
						|
						AppendParameterTypeName(b, parameter, genericTypeParameters, genericMethodParameters); | 
						|
						++i; | 
						|
					} | 
						|
					b.Append(')'); | 
						|
				} | 
						|
				if (explicitReturnType != null) { | 
						|
					b.Append('~'); | 
						|
					AppendTypeName(b, explicitReturnType.AsFullName, true, false); | 
						|
				} | 
						|
			} | 
						|
			return b.ToString(); | 
						|
		} | 
						|
 | 
						|
		static string[] AppendTypeName(StringBuilder b, string typeName, bool appendGenericParameterCount, bool isMethod) | 
						|
		{ | 
						|
			List<string> allGenericParameters = new List<string>(); | 
						|
			StringBuilder genericParameterName = new StringBuilder(); | 
						|
 | 
						|
			bool inGenericParameters = false; | 
						|
			int genericParameterCount = 0; | 
						|
			foreach (char ch in typeName) { | 
						|
				if (inGenericParameters) { | 
						|
					switch (ch) { | 
						|
						case ',': | 
						|
							++genericParameterCount; | 
						|
							allGenericParameters.Add(genericParameterName.ToString()); | 
						|
							genericParameterName.Clear(); | 
						|
							break; | 
						|
						case '>': | 
						|
							++genericParameterCount; | 
						|
							allGenericParameters.Add(genericParameterName.ToString()); | 
						|
							genericParameterName.Clear(); | 
						|
							if (appendGenericParameterCount) { | 
						|
								b.Append(genericParameterCount); | 
						|
							} | 
						|
							inGenericParameters = false; | 
						|
							break; | 
						|
						case ' ': | 
						|
							break; | 
						|
						default: | 
						|
							genericParameterName.Append(ch); | 
						|
							break; | 
						|
					} | 
						|
				} | 
						|
				else { | 
						|
					switch (ch) { | 
						|
						case '<': | 
						|
							if (appendGenericParameterCount) { | 
						|
								b.Append('`'); | 
						|
								if (isMethod) { | 
						|
									b.Append('`'); | 
						|
								} | 
						|
							} | 
						|
							inGenericParameters = true; | 
						|
							genericParameterCount = 0; | 
						|
							break; | 
						|
						case '[': | 
						|
						case ']': | 
						|
							break; | 
						|
						default: | 
						|
							b.Append(ch); | 
						|
							break; | 
						|
					} | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			return allGenericParameters.ToArray(); | 
						|
		} | 
						|
 | 
						|
		private static void AppendParameterTypeName(StringBuilder b, EnvDTE80.CodeParameter2 parameter, string[] genericTypeParameters, string[] genericMethodParameters) | 
						|
		{ | 
						|
			EnvDTE80.CodeTypeRef2 parameterTypeRef = (EnvDTE80.CodeTypeRef2)parameter.Type; | 
						|
			string parameterTypeString = parameterTypeRef.AsFullName; | 
						|
 | 
						|
			int substringStart = 0; | 
						|
			for (int i = 0; i < parameterTypeString.Length; ++i) { | 
						|
				char ch = parameterTypeString[i]; | 
						|
				switch (ch) { | 
						|
					case '<': | 
						|
						AppendParameterTypeSubstring(b, parameterTypeString, substringStart, i, genericTypeParameters, genericMethodParameters); | 
						|
						substringStart = i + 1; | 
						|
						b.Append('{'); | 
						|
						break; | 
						|
					case '>': | 
						|
						AppendParameterTypeSubstring(b, parameterTypeString, substringStart, i, genericTypeParameters, genericMethodParameters); | 
						|
						substringStart = i + 1; | 
						|
						b.Append('}'); | 
						|
						break; | 
						|
 | 
						|
					case '[': | 
						|
						AppendParameterTypeSubstring(b, parameterTypeString, substringStart, i, genericTypeParameters, genericMethodParameters); | 
						|
						b.Append('['); | 
						|
 | 
						|
						// Skip ahead to the closing bracket, counting commas to determine array rank. | 
						|
						int rank = 1; | 
						|
						do { | 
						|
							++i; | 
						|
							ch = parameterTypeString[i]; | 
						|
							if (ch == ',') { | 
						|
								++rank; | 
						|
							} | 
						|
						} | 
						|
						while (ch != ']'); | 
						|
						substringStart = i + 1; | 
						|
 | 
						|
						// For multi-dimensional arrays, add "0:" default array bounds. Note that non-standard bounds are not possible via C# declaration. | 
						|
						if (rank > 1) { | 
						|
							for (int r = 0; r < rank; ++r) { | 
						|
								if (r != 0) { | 
						|
									b.Append(','); | 
						|
								} | 
						|
								b.Append("0:"); | 
						|
							} | 
						|
						} | 
						|
 | 
						|
						b.Append(']'); | 
						|
						break; | 
						|
 | 
						|
					case ',': | 
						|
						AppendParameterTypeSubstring(b, parameterTypeString, substringStart, i, genericTypeParameters, genericMethodParameters); | 
						|
						substringStart = i + 1; | 
						|
						// Skip space after comma if present. (e.g. System.Collections.Generic.KeyValuePair`2{System.String,System.String}.) | 
						|
						if (parameterTypeString[substringStart] == ' ') { | 
						|
							++substringStart; | 
						|
						} | 
						|
						b.Append(','); | 
						|
						break; | 
						|
				} | 
						|
			} | 
						|
 | 
						|
			AppendParameterTypeSubstring(b, parameterTypeString, substringStart, parameterTypeString.Length, genericTypeParameters, genericMethodParameters); | 
						|
 | 
						|
			// Append ref / out indicator if needed. | 
						|
			if ((parameter.ParameterKind == EnvDTE80.vsCMParameterKind.vsCMParameterKindRef) || | 
						|
				(parameter.ParameterKind == EnvDTE80.vsCMParameterKind.vsCMParameterKindOut)) { | 
						|
				b.Append('@'); | 
						|
			} | 
						|
 | 
						|
			// Note there is no need to append a '*' for pointers, as this is included in the full name of the type. | 
						|
			// Multi-dimensional and jagged arrays are handled above during string parsing. | 
						|
		} | 
						|
 | 
						|
		private static void AppendParameterTypeSubstring(StringBuilder b, string parameterTypeString, int substringStart, int substringStop, string[] genericTypeParameters, string[] genericMethodParameters) | 
						|
		{ | 
						|
			if (substringStart < substringStop) { | 
						|
				string substring = parameterTypeString.Substring(substringStart, substringStop - substringStart); | 
						|
				int indexOfGenericTypeParameter = Array.IndexOf(genericTypeParameters, substring); | 
						|
				int indexOfGenericMethodParameter = Array.IndexOf(genericMethodParameters, substring); | 
						|
				if (indexOfGenericTypeParameter >= 0) { | 
						|
					b.Append("`"); | 
						|
					b.Append(indexOfGenericTypeParameter); | 
						|
				} | 
						|
				else if (indexOfGenericMethodParameter >= 0) { | 
						|
					b.Append("``"); | 
						|
					b.Append(indexOfGenericMethodParameter); | 
						|
				} | 
						|
				else { | 
						|
					b.Append(substring); | 
						|
				} | 
						|
			} | 
						|
		} | 
						|
		#endregion | 
						|
	} | 
						|
} |