string GetDocumentation(XmlDocumentationProvider docProvider, IMemberRef mr) {
			var sb = new StringBuilder();
			var doc = docProvider.GetDocumentation(XmlDocKeyProvider.GetKey(mr, sb));
			if (doc != null)
				return doc;
			var method = mr as IMethod;
			if (method == null)
				return null;
			string name = method.Name;
			if (name.StartsWith("set_") || name.StartsWith("get_")) {
				var md = Resolve(method) as MethodDef;
				if (md == null)
					return null;
				mr = md.DeclaringType.Properties.FirstOrDefault(p => p.GetMethod == md || p.SetMethod == md);
				return docProvider.GetDocumentation(XmlDocKeyProvider.GetKey(mr, sb));
			}
			else if (name.StartsWith("add_")) {
				var md = Resolve(method) as MethodDef;
				if (md == null)
					return null;
				mr = md.DeclaringType.Events.FirstOrDefault(p => p.AddMethod == md);
				return docProvider.GetDocumentation(XmlDocKeyProvider.GetKey(mr, sb));
			}
			else if (name.StartsWith("remove_")) {
				var md = Resolve(method) as MethodDef;
				if (md == null)
					return null;
				mr = md.DeclaringType.Events.FirstOrDefault(p => p.RemoveMethod == md);
				return docProvider.GetDocumentation(XmlDocKeyProvider.GetKey(mr, sb));
			}
			return null;
		}
        public void GetSummary_ReturnsExpectedSummarys(string id, string expectedSummary)
        {
            // Arrange
            var xmlDocumentationProvider = new XmlDocumentationProvider(XmlTestFileLocation);

            // Act
            var summary = xmlDocumentationProvider.GetSummary(id);

            // Assert
            Assert.Equal(expectedSummary, summary, StringComparer.Ordinal);
        }
Example #3
0
        public override string GetDescription(int index)
        {
            string description = string.Empty;

            NVIdNode idNode = _declarations[index].IdNode;
            if (idNode is NVClassNode)
            {
                NVClassNode classNode = (NVClassNode)idNode;
                XmlDocumentationProvider documentationProvider =
                    new XmlDocumentationProvider(classNode.AssemblyFileName);
                string classSummary = documentationProvider.GetTypeDocumentation(classNode.FullName);

                if (!string.IsNullOrEmpty(classSummary))
                {
                    description = string.Format("class {0}\n{1}", classNode.FullName, classSummary);
                }
                else
                {
                    description = string.Format("class {0}", classNode.FullName);
                }
            }
            else if (idNode is NVLocalNode)
            {
                NVClassNode classNode = (NVClassNode)idNode.Type;
                if (classNode != null)
                {
                    XmlDocumentationProvider documentationProvider =
                        new XmlDocumentationProvider(classNode.AssemblyFileName);
                    description = documentationProvider.GetTypeDocumentation(classNode.FullName);
                }
            }
            else if (idNode is NVMethodNode)
            {
                NVClassNode classNode = (NVClassNode)((NVMethodNode)idNode).Parent;
                if (classNode != null)
                {
                    XmlDocumentationProvider documentationProvider =
                        new XmlDocumentationProvider(classNode.AssemblyFileName);
                    description = documentationProvider.GetMethodDocumentation(classNode.FullName, idNode.Name);
                }
            }

            // Return the documentation or an error message
            if (!string.IsNullOrEmpty(description))
            {
                return description;
            }

            return string.Format("Could not retrieve documentation for AST node '{0}' (because it is not supported).",
                idNode != null ? idNode.GetType().Name : "");
        }
        public void GetRemarks_ReturnsExpectedRemarks(string id, string expectedRemarks)
        {
            // Arrange
            var xmlDocumentationProvider = new XmlDocumentationProvider(XmlTestFileLocation);

            // Act
            var remarks = xmlDocumentationProvider.GetRemarks(id);

            // Assert
            Assert.Equal(expectedRemarks, remarks, StringComparer.Ordinal);
        }
        private static DocumentationProvider GetDocumentationProvider(string assemblyFilePath, string assemblyName)
        {
            var result = DocumentationProvider.Default;
            string xmlFile;
            if (AssemblyNameToXmlDocFileMap.TryGetValue(assemblyName, out xmlFile))
            {
                result = new XmlDocumentationProvider(xmlFile);
            }

            return result;
        }
        private XmlDocumentationProvider GetXmlDocProvider(Assembly assembly)
        {
            var xmlPath = FindXmlDocPath(assembly);

            System.Diagnostics.Debug.Print("GetXmlDocProvider {0}", xmlPath);
            if (xmlPath == null || File.Exists(xmlPath) == false) {
                System.Diagnostics.Debug.Print("GetXmlDocProvider not found");
                return null;
            }

            var key = xmlPath.ToLower();
            XmlDocumentationProvider provider;
            if (_providers.TryGetValue(key, out provider) == false) {
                provider = new XmlDocumentationProvider(xmlPath);
                _providers[key] = provider;
            }

            return provider;
        }
Example #7
0
        public IEnumerable <IDocument> Execute(IReadOnlyList <IDocument> inputs, IExecutionContext context)
        {
            List <ISymbol> symbols = new List <ISymbol>();

            // Create the compilation (have to supply an XmlReferenceResolver to handle include XML doc comments)
            MetadataReference mscorlib    = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
            Compilation       compilation = CSharpCompilation
                                            .Create(CompilationAssemblyName)
                                            .WithReferences(mscorlib)
                                            .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
                                                                                      xmlReferenceResolver: new XmlFileResolver(context.FileSystem.RootPath.FullPath)));

            // Handle input documents
            if (_inputDocuments)
            {
                // Get syntax trees (supply path so that XML doc includes can be resolved)
                ConcurrentBag <SyntaxTree> syntaxTrees = new ConcurrentBag <SyntaxTree>();
                Parallel.ForEach(inputs, input =>
                {
                    using (Stream stream = input.GetStream())
                    {
                        SourceText sourceText = SourceText.From(stream);
                        syntaxTrees.Add(CSharpSyntaxTree.ParseText(sourceText,
                                                                   path: input.String(Keys.SourceFilePath, string.Empty)));
                    }
                });

                compilation = compilation.AddSyntaxTrees(syntaxTrees);
            }

            // Handle assemblies
            IEnumerable <IFile> assemblyFiles = context.FileSystem.GetInputFiles(_assemblyGlobs)
                                                .Where(x => (x.Path.Extension == ".dll" || x.Path.Extension == ".exe") && x.Exists);

            MetadataReference[] assemblyReferences = assemblyFiles.Select(assemblyFile =>
            {
                // Create the metadata reference for the compilation
                IFile xmlFile = context.FileSystem.GetFile(assemblyFile.Path.ChangeExtension("xml"));
                if (xmlFile.Exists)
                {
                    Trace.Verbose($"Creating metadata reference for assembly {assemblyFile.Path.FullPath} with XML documentation file");
                    using (Stream xmlStream = xmlFile.OpenRead())
                    {
                        using (MemoryStream xmlBytes = new MemoryStream())
                        {
                            xmlStream.CopyTo(xmlBytes);
                            return(MetadataReference.CreateFromStream(assemblyFile.OpenRead(),
                                                                      documentation: XmlDocumentationProvider.CreateFromBytes(xmlBytes.ToArray())));
                        }
                    }
                }
                Trace.Verbose($"Creating metadata reference for assembly {assemblyFile.Path.FullPath} without XML documentation file");
                return((MetadataReference)MetadataReference.CreateFromStream(assemblyFile.OpenRead()));
            }).ToArray();
            if (assemblyReferences.Length > 0)
            {
                compilation = compilation.AddReferences(assemblyReferences);
                symbols.AddRange(assemblyReferences
                                 .Select(x => (IAssemblySymbol)compilation.GetAssemblyOrModuleSymbol(x))
                                 .Select(x => _assemblySymbols ? x : (ISymbol)x.GlobalNamespace));
            }

            // Get and return the document tree
            symbols.Add(compilation.Assembly.GlobalNamespace);
            AnalyzeSymbolVisitor visitor = new AnalyzeSymbolVisitor(compilation, context, _symbolPredicate,
                                                                    _writePath ?? (x => DefaultWritePath(x, _writePathPrefix)),
                                                                    _cssClasses, _docsForImplicitSymbols, _assemblySymbols);

            foreach (ISymbol symbol in symbols)
            {
                visitor.Visit(symbol);
            }
            return(visitor.Finish());
        }
Example #8
0
            public DocumentationProvider GetDocumentationProvider(string assemblyPath)
            {
                if (assemblyPath == null)
                {
                    throw new ArgumentNullException(nameof(assemblyPath));
                }

                assemblyPath = Path.ChangeExtension(assemblyPath, "xml");
                if (!_assemblyPathToDocumentationProviderMap.TryGetValue(assemblyPath, out var provider))
                {
                    provider = _assemblyPathToDocumentationProviderMap.GetOrAdd(assemblyPath, _path => XmlDocumentationProvider.CreateFromFile(_path));
                }

                return(provider);
            }
Example #9
0
            /// <summary>
            /// Creates documentation provider for assembly <i>asmPath</i>.
            /// Returns null if its xml file does not exist.
            /// Returns _DocumentationProvider if xml file is big and found or successfully created and successfully loaded database for it.
            /// Else returns XmlDocumentationProvider.
            /// </summary>
            public static DocumentationProvider Create(string asmPath)
            {
                if (s_d.TryGetValue(asmPath, out var dp))
                {
                    return(dp);
                }

                var xmlPath = Path.ChangeExtension(asmPath, "xml");

                if (!AFile.GetProperties(xmlPath, out var px))
                {
                    return(null);
                }

                if (px.Size >= 10_000)
                {
                    var md5    = new Au.Util.AHash.MD5(); md5.Add(xmlPath.Lower());
                    var dbPath = AFolders.ThisAppTemp + md5.Hash.ToString() + ".db";
                    try {
                        if (!AFile.GetProperties(dbPath, out var pd) || pd.LastWriteTimeUtc != px.LastWriteTimeUtc)
                        {
                            //ADebug.Print($"creating db: {asmPath}  ->  {dbPath}");
                            AFile.Delete(dbPath);
                            using (var d = new ASqlite(dbPath)) {
                                using var trans = d.Transaction();
                                d.Execute("CREATE TABLE doc (name TEXT PRIMARY KEY, xml TEXT)");
                                using var statInsert = d.Statement("INSERT INTO doc VALUES (?, ?)");

                                var xr = AExtXml.LoadElem(xmlPath);
                                foreach (var e in xr.Descendants("member"))
                                {
                                    var name = e.Attr("name");

                                    //remove <remarks> and <example>.
                                    foreach (var v in e.Descendants("remarks").ToArray())
                                    {
                                        v.Remove();
                                    }
                                    foreach (var v in e.Descendants("example").ToArray())
                                    {
                                        v.Remove();
                                    }

                                    using var reader = e.CreateReader();
                                    reader.MoveToContent();
                                    var xml = reader.ReadInnerXml();
                                    //AOutput.Write(name, xml);

                                    statInsert.BindAll(name, xml).Step();
                                    statInsert.Reset();
                                }
                                trans.Commit();
                                d.Execute("VACUUM");
                            }
                            File.SetLastWriteTimeUtc(dbPath, px.LastWriteTimeUtc);
                        }
                        var db = new ASqlite(dbPath, SLFlags.SQLITE_OPEN_READONLY);                         //never mind: we don't dispose it on process exit
                        s_d[asmPath] = dp = new _DocumentationProvider {
                            _db = db
                        };
                        return(dp);
                    }
                    catch (Exception ex) { ADebug.Print(ex.ToStringWithoutStack()); }
                }
                return(XmlDocumentationProvider.CreateFromFile(xmlPath));
            }
Example #10
0
            public DocumentationProvider GetDocumentationProvider(string assemblyPath)
            {
                if (assemblyPath == null)
                {
                    throw new ArgumentNullException("assemblyPath");
                }

                assemblyPath = Path.ChangeExtension(assemblyPath, "xml");

                DocumentationProvider provider;

                if (!this.assemblyPathToDocumentationProviderMap.TryGetValue(assemblyPath, out provider))
                {
                    provider = this.assemblyPathToDocumentationProviderMap.GetOrAdd(assemblyPath, _path => XmlDocumentationProvider.Create(_path));
                }

                return(provider);
            }
Example #11
0
        void TextViewMouseHover(object sender, MouseEventArgs e)
        {
            TextViewPosition?position = textEditor.TextArea.TextView.GetPosition(e.GetPosition(textEditor.TextArea.TextView) + textEditor.TextArea.TextView.ScrollOffset);

            if (position == null)
            {
                return;
            }
            int offset = textEditor.Document.GetOffset(position.Value.Location);

            if (referenceElementGenerator.References == null)
            {
                return;
            }
            ReferenceSegment seg = referenceElementGenerator.References.FindSegmentsContaining(offset).FirstOrDefault();

            if (seg == null)
            {
                return;
            }
            object content = GenerateTooltip(seg);

            if (tooltip != null)
            {
                tooltip.IsOpen = false;
            }
            if (content != null)
            {
                tooltip = new ToolTip()
                {
                    Content = content, IsOpen = true
                }
            }
            ;
        }

        object GenerateTooltip(ReferenceSegment segment)
        {
            if (segment.Reference is Mono.Cecil.Cil.OpCode)
            {
                Mono.Cecil.Cil.OpCode code           = (Mono.Cecil.Cil.OpCode)segment.Reference;
                string encodedName                   = code.Code.ToString();
                string opCodeHex                     = code.Size > 1 ? string.Format("0x{0:x2}{1:x2}", code.Op1, code.Op2) : string.Format("0x{0:x2}", code.Op2);
                XmlDocumentationProvider docProvider = XmlDocLoader.MscorlibDocumentation;
                if (docProvider != null)
                {
                    string documentation = docProvider.GetDocumentation("F:System.Reflection.Emit.OpCodes." + encodedName);
                    if (documentation != null)
                    {
                        XmlDocRenderer renderer = new XmlDocRenderer();
                        renderer.AppendText(string.Format("{0} ({1}) - ", code.Name, opCodeHex));
                        renderer.AddXmlDocumentation(documentation);
                        return(renderer.CreateTextBlock());
                    }
                }
                return(string.Format("{0} ({1})", code.Name, opCodeHex));
            }
            else if (segment.Reference is MemberReference)
            {
                MemberReference mr = (MemberReference)segment.Reference;
                // if possible, resolve the reference
                if (mr is TypeReference)
                {
                    mr = ((TypeReference)mr).Resolve() ?? mr;
                }
                else if (mr is MethodReference)
                {
                    mr = ((MethodReference)mr).Resolve() ?? mr;
                }
                XmlDocRenderer renderer = new XmlDocRenderer();
                renderer.AppendText(MainWindow.Instance.CurrentLanguage.GetTooltip(mr));
                try {
                    XmlDocumentationProvider docProvider = XmlDocLoader.LoadDocumentation(mr.Module);
                    if (docProvider != null)
                    {
                        string documentation = docProvider.GetDocumentation(XmlDocKeyProvider.GetKey(mr));
                        if (documentation != null)
                        {
                            renderer.AppendText(Environment.NewLine);
                            renderer.AddXmlDocumentation(documentation);
                        }
                    }
                } catch (XmlException) {
                    // ignore
                }
                return(renderer.CreateTextBlock());
            }
            return(null);
        }
Example #12
0
        void TextViewMouseHover(object sender, MouseEventArgs e)
        {
            TextViewPosition?position = GetPositionFromMousePosition();

            if (position == null)
            {
                return;
            }
            int offset = textEditor.Document.GetOffset(position.Value.Location);

            if (referenceElementGenerator.References == null)
            {
                return;
            }
            ReferenceSegment seg = referenceElementGenerator.References.FindSegmentsContaining(offset).FirstOrDefault();

            if (seg == null)
            {
                return;
            }
            object content = GenerateTooltip(seg);

            if (tooltip != null)
            {
                tooltip.IsOpen = false;
            }
            if (content != null)
            {
                tooltip = new ToolTip()
                {
                    Content = content, IsOpen = true
                }
            }
            ;
        }

        object GenerateTooltip(ReferenceSegment segment)
        {
            if (segment.Reference is ICSharpCode.Decompiler.Disassembler.OpCodeInfo code)
            {
                XmlDocumentationProvider docProvider = XmlDocLoader.MscorlibDocumentation;
                if (docProvider != null)
                {
                    string documentation = docProvider.GetDocumentation("F:System.Reflection.Emit.OpCodes." + code.EncodedName);
                    if (documentation != null)
                    {
                        XmlDocRenderer renderer = new XmlDocRenderer();
                        renderer.AppendText($"{code.Name} (0x{code.Code:x}) - ");
                        renderer.AddXmlDocumentation(documentation);
                        return(renderer.CreateTextBlock());
                    }
                }
                return($"{code.Name} (0x{code.Code:x})");
            }
            else if (segment.Reference is IEntity entity)
            {
                return(CreateTextBlockForEntity(entity));
            }
            else if (segment.Reference is ValueTuple <PEFile, System.Reflection.Metadata.EntityHandle> unresolvedEntity)
            {
                var typeSystem = new DecompilerTypeSystem(unresolvedEntity.Item1, unresolvedEntity.Item1.GetAssemblyResolver(), TypeSystemOptions.Default | TypeSystemOptions.Uncached);
                try {
                    IEntity resolved = typeSystem.MainModule.ResolveEntity(unresolvedEntity.Item2);
                    if (resolved == null)
                    {
                        return(null);
                    }
                    return(CreateTextBlockForEntity(resolved));
                } catch (BadImageFormatException) {
                    return(null);
                }
            }
            return(null);
        }
Example #13
0
        private static async Task <ProjectHandler> TryGetPhpProjectAsync(string projectFile, ILogTarget log)
        {
            try
            {
                Project project = LoadProject(projectFile);

                if (!IsPhpProject(project))
                {
                    return(null);
                }

                SetupMultitargetingIfNecessary(project);

                var buildResult = await ResolveReferencesAsync(project);

                var projectInstance = buildResult.ProjectStateAfterBuild;

                var metadataReferences = GatherReferences(project, projectInstance, buildResult)
                                         .Select(path => MetadataReference.CreateFromFile(path, documentation: XmlDocumentationProvider.CreateFromFile(Path.ChangeExtension(path, "xml"))))
                                         .ToArray();

                if (metadataReferences.Length == 0)
                {
                    // dotnet restore hasn't run yet
                    log?.LogMessage($"{Path.GetFileName(projectFile)}: could not be built; ");
                    return(null);
                }

                var options = new PhpCompilationOptions(
                    outputKind: OutputKind.DynamicallyLinkedLibrary,
                    baseDirectory: Path.GetDirectoryName(projectFile), // compilation expects platform-specific slashes (backslashes on windows)
                    specificDiagnosticOptions: null,
                    sdkDirectory: null);

                // TODO: Get from MSBuild
                string projectName = Path.GetFileNameWithoutExtension(projectFile);

                var encoding = TryParseEncodingName(projectInstance.GetPropertyValue("CodePage")) ?? Encoding.UTF8;

                var nowarn = projectInstance.GetPropertyValue("NoWarn");
                if (nowarn != null)
                {
                    options = options.WithSpecificDiagnosticOptions(ParseNoWarn(nowarn));
                }

                var syntaxTrees = ParseSourceFiles(projectInstance, encoding);

                var compilation = PhpCompilation.Create(
                    projectName,
                    syntaxTrees,
                    references: metadataReferences,
                    options: options);

                return(new ProjectHandler(compilation, projectInstance, encoding));
            }
            catch (Exception ex)
            {
                log?.LogMessage($"{Path.GetFileName(projectFile)}: {ex.Message}");
                return(null);
            }
        }
Example #14
0
 static string LookupLocalizedXmlDoc(string fileName)
 {
     return(XmlDocumentationProvider.LookupLocalizedXmlDoc(fileName));
 }
Example #15
0
        /// <summary>
        /// Gets standard framework dependencies and resolves any package dependencies passed in as
        /// MetadataReferences to provide the actual assembly file and documentation file together.
        /// </summary>
        /// <param name="dependencies">Package dependencies to be located on the file system.</param>
        /// <returns>An array of <see cref="MetadataReference"/> containing all resolved assemblies and documentation.</returns>
        public MetadataReference[] ResolveReferences(Dependency[] dependencies)
        {
            // Ensure we never have a null reference for the dependency list.
            if (dependencies == null)
            {
                throw new ArgumentNullException(nameof(dependencies));
            }

            var assemblies = new[]
            {
                typeof(Uri).Assembly,
                typeof(object).Assembly,
                typeof(Enumerable).Assembly,
                typeof(ConfigurationManager).Assembly,
                typeof(JObject).Assembly,
                typeof(HttpClient).Assembly,
                typeof(HttpContentExtensions).Assembly,
                typeof(Common.RouteRequest).Assembly,
                typeof(XmlElement).Assembly,
                typeof(XObject).Assembly,
                typeof(DataContractAttribute).Assembly,
                typeof(BasicHttpBinding).Assembly,
                typeof(DataSet).Assembly,
                typeof(DataRowExtensions).Assembly,
                typeof(Geography).Assembly,
                typeof(Size).Assembly
            };

            // Pull in cache containing a list of documentation files to make finding an assemblies associated
            // XML doc file to be added to the MetadataReference. This directory search is only done once on
            // first request.
            var fileLookup = GetDocumentationLookup();

            return(assemblies
                   .Select(a =>
            {
                // Locate the name of the assembly to determine what the documentation file name is.
                var assemblyName = a.GetName().Name;

                // Attempt to locate an xml documentation file in the cache.
                if (fileLookup.TryGetValue(assemblyName, out string docPath))
                {
                    return MetadataReference.CreateFromFile(a.Location, documentation: XmlDocumentationProvider.CreateFromFile(docPath));
                }

                // Otherwise we have no documentation file and we'll return it without a doc provider.
                return MetadataReference.CreateFromFile(a.Location);
            })
                   // Iterate through each dependency to get actual MetadataReferences containing the assembly paths
                   // and documentation paths for the assemblies contained with the package folders.
                   // There can be multiple assemblies required for each NuGet package, so the ResolvePackageReferencesAsync()
                   // method returns an array of MetadataReferences for each of the assemblies and matching documentation files.
                   // Add the references to the final output to be included in the compilation.
                   .Concat(dependencies.SelectMany(d => ResolvePackageReferences(d)))
                   .ToArray());
        }
Example #16
0
        void TextViewMouseHover(object sender, MouseEventArgs e)
        {
            TextViewPosition?position = textEditor.TextArea.TextView.GetPosition(e.GetPosition(textEditor.TextArea.TextView) + textEditor.TextArea.TextView.ScrollOffset);

            if (position == null)
            {
                return;
            }
            int offset           = textEditor.Document.GetOffset(position.Value);
            ReferenceSegment seg = referenceElementGenerator.References.FindSegmentsContaining(offset).FirstOrDefault();

            if (seg == null)
            {
                return;
            }
            object content = GenerateTooltip(seg);

            if (tooltip != null)
            {
                tooltip.IsOpen = false;
            }
            if (content != null)
            {
                tooltip = new ToolTip()
                {
                    Content = content, IsOpen = true
                }
            }
            ;
        }

        object GenerateTooltip(ReferenceSegment segment)
        {
            if (segment.Reference is Mono.Cecil.Cil.OpCode)
            {
                Mono.Cecil.Cil.OpCode code = (Mono.Cecil.Cil.OpCode)segment.Reference;
                string encodedName         = code.Code.ToString();
                string opCodeHex           = code.Size > 1 ? string.Format("0x{0:x2}{1:x2}", code.Op1, code.Op2) : string.Format("0x{0:x2}", code.Op2);
                string documentationFile   = FindDocumentation("mscorlib.xml");
                string text = "";
                if (documentationFile != null)
                {
                    XmlDocumentationProvider provider = new XmlDocumentationProvider(documentationFile);
                    string documentation = provider.GetDocumentation("F:System.Reflection.Emit.OpCodes." + encodedName);
                    if (documentation != null)
                    {
                        text = StripXml(documentation);
                    }
                }
                return(string.Format("{0} ({1}): {2}", code.Name, opCodeHex, text));
            }

            return(null);
        }

        string StripXml(string xmlText)
        {
            try {
                using (XmlTextReader xml = new XmlTextReader(new StringReader(xmlText))) {
                    StringBuilder ret = new StringBuilder();
                    while (xml.Read())
                    {
                        if (xml.NodeType == XmlNodeType.Element)
                        {
                            string elname = xml.Name.ToLowerInvariant();
                            switch (elname)
                            {
                            case "summary":
                                break;

                            case "br":
                            case "para":
                                ret.AppendLine();
                                break;

                            default:
                                xml.Skip();
                                break;
                            }
                        }
                        else if (xml.NodeType == XmlNodeType.Text)
                        {
                            ret.Append(Regex.Replace(xml.Value, @"\s+", " "));
                        }
                    }
                    return(ret.ToString());
                }
            } catch (XmlException) {
                return(null);                // invalid XML docu
            }
        }

        string FindDocumentation(string fileName)
        {
            string        path  = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
            List <string> names = new List <string>();

            EnumerateCultures(CultureInfo.CurrentCulture, names);
            names.Add("en");
            names.Add("en-US");
            names.Add("en-GB");

            foreach (string name in names)
            {
                string location = Path.Combine(path, name, fileName);
                if (File.Exists(location))
                {
                    return(location);
                }
            }

            path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0");

            string loc = Path.Combine(path, fileName);

            if (File.Exists(loc))
            {
                return(loc);
            }

            return(null);
        }

        void EnumerateCultures(CultureInfo info, List <string> names)
        {
            while (info != null)
            {
                names.Add(info.Name);
                info = info.Parent;
                if (info == info.Parent)
                {
                    return;
                }
            }
        }