private void AddReferencesToOverriddenMembers(
            Classification.Range range,
            SyntaxToken token,
            ISymbol declaredSymbol)
        {
            if (!declaredSymbol.IsOverride)
            {
                return;
            }

            IMethodSymbol method = declaredSymbol as IMethodSymbol;
            if (method != null)
            {
                var overriddenMethod = method.OverriddenMethod;
                if (overriddenMethod != null)
                {
                    ProcessReference(
                        range,
                        overriddenMethod,
                        ReferenceKind.Override);
                    projectGenerator.AddBaseMember(method, overriddenMethod);
                }
            }

            IPropertySymbol property = declaredSymbol as IPropertySymbol;
            if (property != null)
            {
                var overriddenProperty = property.OverriddenProperty;
                if (overriddenProperty != null)
                {
                    ProcessReference(
                        range,
                        overriddenProperty,
                        ReferenceKind.Override);
                    projectGenerator.AddBaseMember(property, overriddenProperty);
                }
            }

            IEventSymbol eventSymbol = declaredSymbol as IEventSymbol;
            if (eventSymbol != null)
            {
                var overriddenEvent = eventSymbol.OverriddenEvent;
                if (overriddenEvent != null)
                {
                    ProcessReference(
                        range,
                        overriddenEvent,
                        ReferenceKind.Override);
                    projectGenerator.AddBaseMember(eventSymbol, overriddenEvent);
                }
            }
        }
 private void AddReferencesToImplementedMembers(
     Classification.Range range,
     SyntaxToken token,
     ISymbol declaredSymbol)
 {
     var declaringType = declaredSymbol.ContainingType;
     var interfaces = declaringType.AllInterfaces;
     foreach (var implementedInterface in interfaces)
     {
         foreach (var member in implementedInterface.GetMembers())
         {
             if (declaringType.FindImplementationForInterfaceMember(member) == declaredSymbol)
             {
                 ProcessReference(
                     range,
                     member,
                     ReferenceKind.InterfaceMemberImplementation);
                 projectGenerator.AddImplementedInterfaceMember(declaredSymbol, member);
             }
         }
     }
 }
        public async Task Generate()
        {
            if (Configuration.CalculateRoslynSemantics)
            {
                this.Text = await Document.GetTextAsync();

                this.Root = await Document.GetSyntaxRootAsync();

                this.SemanticModel = await Document.GetSemanticModelAsync();

                this.SemanticFactsService = WorkspaceHacks.GetSemanticFactsService(this.Document);
                this.SyntaxFactsService   = WorkspaceHacks.GetSyntaxFactsService(this.Document);

                var semanticFactsServiceType = SemanticFactsService.GetType();
                var isWrittenTo = semanticFactsServiceType.GetMethod("IsWrittenTo");
                this.isWrittenToDelegate = (Func <SemanticModel, SyntaxNode, CancellationToken, bool>)
                                           Delegate.CreateDelegate(typeof(Func <SemanticModel, SyntaxNode, CancellationToken, bool>), SemanticFactsService, isWrittenTo);

                var syntaxFactsServiceType = SyntaxFactsService.GetType();
                var getBindableParent      = syntaxFactsServiceType.GetMethod("GetBindableParent");
                this.getBindableParentDelegate = (Func <SyntaxToken, SyntaxNode>)
                                                 Delegate.CreateDelegate(typeof(Func <SyntaxToken, SyntaxNode>), SyntaxFactsService, getBindableParent);

                this.DeclaredSymbols = new HashSet <ISymbol>();

                Interlocked.Increment(ref projectGenerator.DocumentCount);
                Interlocked.Add(ref projectGenerator.LinesOfCode, Text.Lines.Count);
                Interlocked.Add(ref projectGenerator.BytesOfCode, Text.Length);
            }

            CalculateDocumentDestinationPath();
            CalculateRelativePathToRoot();

            // add the file itself as a "declared symbol", so that clicking on document in search
            // results redirects to the document
            ProjectGenerator.AddDeclaredSymbolToRedirectMap(
                this.projectGenerator.SymbolIDToListOfLocationsMap,
                SymbolIdService.GetId(this.Document),
                documentRelativeFilePathWithoutHtmlExtension,
                0);

            if (File.Exists(documentDestinationFilePath))
            {
                // someone already generated this file, likely a shared linked file from elsewhere
                return;
            }

            this.classifier = new Classification();

            Log.Write(documentDestinationFilePath);

            try
            {
                var directoryName = Path.GetDirectoryName(documentDestinationFilePath);
                var sanitized     = Paths.SanitizeFolder(directoryName);
                if (directoryName != sanitized)
                {
                    Log.Exception("Illegal characters in path: " + directoryName + " Project: " + this.projectGenerator.AssemblyName);
                }

                if (Configuration.CreateFoldersOnDisk)
                {
                    Directory.CreateDirectory(directoryName);
                }
            }
            catch (PathTooLongException)
            {
                // there's one case where a path is too long - we don't care enough about it
                return;
            }

            if (Configuration.WriteDocumentsToDisk)
            {
                using (var streamWriter = new StreamWriter(
                           documentDestinationFilePath,
                           append: false,
                           encoding: Encoding.UTF8))
                {
                    await GenerateHtml(streamWriter);
                }
            }
            else
            {
                using (var memoryStream = new MemoryStream())
                    using (var streamWriter = new StreamWriter(memoryStream))
                    {
                        await GeneratePre(streamWriter);
                    }
            }
        }
        private HtmlElementInfo ProcessReference(Classification.Range range, ISymbol symbol, ReferenceKind kind, bool isLargeFile = false)
        {
            ClassifiedSpan classifiedSpan = range.ClassifiedSpan;
            var methodSymbol = symbol as IMethodSymbol;
            if (methodSymbol != null && methodSymbol.ReducedFrom != null)
            {
                symbol = methodSymbol.ReducedFrom;
            }

            HtmlElementInfo result = null;

            if (symbol.IsImplicitlyDeclared)
            {
                if (methodSymbol != null &&
                    methodSymbol.MethodKind == MethodKind.Constructor &&
                    symbol.ContainingSymbol != null)
                {
                    return ProcessReference(range, symbol.ContainingSymbol, ReferenceKind.Instantiation);
                }
            }

            if (symbol.Kind == SymbolKind.Local ||
                symbol.Kind == SymbolKind.Parameter ||
                symbol.Kind == SymbolKind.TypeParameter)
            {
                if (isLargeFile)
                {
                    return null;
                }

                return HighlightReference(symbol);
            }

            if (methodSymbol != null &&
                methodSymbol.MethodKind == MethodKind.Constructor &&
                methodSymbol.ContainingType != null)
            {
                ProcessReference(range, methodSymbol.ContainingType, ReferenceKind.Instantiation);
            }

            if ((symbol.Kind == SymbolKind.Event ||
                 symbol.Kind == SymbolKind.Field ||
                 symbol.Kind == SymbolKind.Method ||
                 symbol.Kind == SymbolKind.NamedType ||
                 symbol.Kind == SymbolKind.Property) &&
                 symbol.Locations.Length >= 1)
            {
                var typeSymbol = symbol as ITypeSymbol;
                string symbolId = SymbolIdService.GetId(symbol);
                var location = symbol.Locations[0];
                string destinationAssemblyName = null;
                if (location.IsInSource)
                {
                    result = GenerateHyperlink(symbol, symbolId, location.SourceTree, out destinationAssemblyName);
                }
                else if (location.IsInMetadata && location.MetadataModule != null)
                {
                    var metadataModule = location.MetadataModule;
                    result = GenerateHyperlink(symbolId, symbol, metadataModule, isLargeFile, out destinationAssemblyName);
                }

                if (result == null)
                {
                    return result;
                }

                string target;
                if (result.Attributes == null ||
                    !result.Attributes.TryGetValue("href", out target) ||
                    !target.Contains("@"))
                {
                    // only register a reference to the symbol if it's not a symbol from an external assembly.
                    // if this links to a symbol in a different index, link target contain @.
                    projectGenerator.AddReference(
                        this.documentDestinationFilePath,
                        Text,
                        destinationAssemblyName,
                        symbol,
                        symbolId,
                        classifiedSpan.TextSpan.Start,
                        classifiedSpan.TextSpan.End,
                        kind);
                }
            }

            // don't make this and var into hyperlinks in large files to save space
            if (isLargeFile && (range.Text == "this" || range.Text == "var"))
            {
                result = null;
            }

            return result;
        }
        private HtmlElementInfo ProcessReference(Classification.Range range, SyntaxToken token, bool isLargeFile = false)
        {
            ClassifiedSpan classifiedSpan = range.ClassifiedSpan;
            var kind = ReferenceKind.Reference;
            var node = GetBindableParent(token);

            if (token.RawKind == (int)Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.NewKeyword &&
                node is Microsoft.CodeAnalysis.VisualBasic.Syntax.ObjectCreationExpressionSyntax)
            {
                // don't count New in New Foo() as a reference to the constructor
                return null;
            }

            if (token.ToString() == "[" &&
                token.Parent is Microsoft.CodeAnalysis.CSharp.Syntax.BracketedArgumentListSyntax &&
                token.Parent.Parent is Microsoft.CodeAnalysis.CSharp.Syntax.ElementAccessExpressionSyntax)
            {
                node = token.Parent.Parent;
            }

            if (node == null)
            {
                return null;
            }

            var symbol = GetSymbol(node);
            if (symbol == null)
            {
                return null;
            }

            //Diagnostics(classifiedSpan, token, symbol);

            kind = DetermineReferenceKind(token, node, symbol);

            return ProcessReference(range, symbol, kind, isLargeFile);
        }
        private string GetClassAttribute(string rangeText, Classification.Range range, bool isLargeFile = false)
        {
            string classificationType = range.ClassificationType;

            if (classificationType == null ||
                classificationType == Constants.ClassificationPunctuation)
            {
                return null;
            }

            if (range.ClassificationType == Constants.ClassificationLiteral)
            {
                return classificationType;
            }

            if (range.ClassificationType != Constants.ClassificationIdentifier &&
                range.ClassificationType != Constants.ClassificationTypeName &&
                rangeText != "this" &&
                rangeText != "base" &&
                rangeText != "var" &&
                rangeText != "New" &&
                rangeText != "new" &&
                rangeText != "[" &&
                rangeText != "partial" &&
                rangeText != "Partial")
            {
                return classificationType;
            }

            if (range.ClassificationType == Constants.ClassificationKeyword)
            {
                return classificationType;
            }

            var position = range.ClassifiedSpan.TextSpan.Start;
            var token = Root.FindToken(position, findInsideTrivia: true);

            var declaredSymbol = SemanticModel.GetDeclaredSymbol(token.Parent);
            if (declaredSymbol is IParameterSymbol && rangeText == "this")
            {
                return classificationType;
            }

            if (declaredSymbol != null)
            {
                return ClassFromSymbol(declaredSymbol, classificationType);
            }

            var node = GetBindableParent(token);
            if (token.ToString() == "[" &&
                token.Parent is Microsoft.CodeAnalysis.CSharp.Syntax.BracketedArgumentListSyntax &&
                token.Parent.Parent is Microsoft.CodeAnalysis.CSharp.Syntax.ElementAccessExpressionSyntax)
            {
                node = token.Parent.Parent;
            }

            if (node == null)
            {
                return classificationType;
            }

            var symbol = GetSymbol(node);
            if (symbol == null)
            {
                return classificationType;
            }

            return ClassFromSymbol(symbol, classificationType);
        }
예제 #7
0
        public async Task Generate()
        {
            if (Configuration.CalculateRoslynSemantics)
            {
                this.Text = await Document.GetTextAsync();
                this.Root = await Document.GetSyntaxRootAsync();
                this.SemanticModel = await Document.GetSemanticModelAsync();
                this.SemanticFactsService = WorkspaceHacks.GetSemanticFactsService(this.Document);
                this.SyntaxFactsService = WorkspaceHacks.GetSyntaxFactsService(this.Document);

                var semanticFactsServiceType = SemanticFactsService.GetType();
                var isWrittenTo = semanticFactsServiceType.GetMethod("IsWrittenTo");
                this.isWrittenToDelegate = (Func<SemanticModel, SyntaxNode, CancellationToken, bool>)
                    Delegate.CreateDelegate(typeof(Func<SemanticModel, SyntaxNode, CancellationToken, bool>), SemanticFactsService, isWrittenTo);

                var syntaxFactsServiceType = SyntaxFactsService.GetType();
                var getBindableParent = syntaxFactsServiceType.GetMethod("GetBindableParent");
                this.getBindableParentDelegate = (Func<SyntaxToken, SyntaxNode>)
                    Delegate.CreateDelegate(typeof(Func<SyntaxToken, SyntaxNode>), SyntaxFactsService, getBindableParent);

                this.DeclaredSymbols = new HashSet<ISymbol>();

                Interlocked.Increment(ref projectGenerator.DocumentCount);
                Interlocked.Add(ref projectGenerator.LinesOfCode, Text.Lines.Count);
                Interlocked.Add(ref projectGenerator.BytesOfCode, Text.Length);
            }

            CalculateDocumentDestinationPath();
            CalculateRelativePathToRoot();

            // add the file itself as a "declared symbol", so that clicking on document in search
            // results redirects to the document
            ProjectGenerator.AddDeclaredSymbolToRedirectMap(
                this.projectGenerator.SymbolIDToListOfLocationsMap,
                SymbolIdService.GetId(this.Document),
                documentRelativeFilePathWithoutHtmlExtension,
                0);

            if (File.Exists(documentDestinationFilePath))
            {
                // someone already generated this file, likely a shared linked file from elsewhere
                return;
            }

            this.classifier = new Classification();

            Log.Write(documentDestinationFilePath);

            try
            {
                var directoryName = Path.GetDirectoryName(documentDestinationFilePath);
                var sanitized = Paths.SanitizeFolder(directoryName);
                if (directoryName != sanitized)
                {
                    Log.Exception("Illegal characters in path: " + directoryName + " Project: " + this.projectGenerator.AssemblyName);
                }

                if (Configuration.CreateFoldersOnDisk)
                {
                    Directory.CreateDirectory(directoryName);
                }
            }
            catch (PathTooLongException)
            {
                // there's one case where a path is too long - we don't care enough about it
                return;
            }

            if (Configuration.WriteDocumentsToDisk)
            {
                using (var streamWriter = new StreamWriter(
                    documentDestinationFilePath,
                    append: false,
                    encoding: Encoding.UTF8))
                {
                    await GenerateHtml(streamWriter);
                }
            }
            else
            {
                using (var memoryStream = new MemoryStream())
                using (var streamWriter = new StreamWriter(memoryStream))
                {
                    await GeneratePre(streamWriter);
                }
            }
        }
예제 #8
0
        private string GenerateRange(StreamWriter writer, Classification.Range range, int lineCount = 0)
        {
            var html = range.Text;
            html = Markup.HtmlEscape(html);
            bool isLargeFile = IsLargeFile(lineCount);
            string classAttributeValue = GetClassAttribute(html, range, isLargeFile);
            HtmlElementInfo hyperlinkInfo = GenerateLinks(range, isLargeFile);

            if (hyperlinkInfo == null)
            {
                if (classAttributeValue == null || isLargeFile)
                {
                    return html;
                }

                if (classAttributeValue == "k")
                {
                    return "<b>" + html + "</b>";
                }

            }

            var sb = new StringBuilder();

            var elementName = "span";
            if (hyperlinkInfo != null)
            {
                elementName = hyperlinkInfo.Name;
            }

            sb.Append("<" + elementName);
            bool overridingClassAttributeSpecified = false;
            if (hyperlinkInfo != null)
            {
                foreach (var attribute in hyperlinkInfo.Attributes)
                {
                    AddAttribute(sb, attribute.Key, attribute.Value);
                    if (attribute.Key == "class")
                    {
                        overridingClassAttributeSpecified = true;
                    }
                }
            }

            if (!overridingClassAttributeSpecified)
            {
                AddAttribute(sb, "class", classAttributeValue);
            }

            sb.Append('>');

            html = AddIdSpanForImplicitConstructorIfNecessary(hyperlinkInfo, html);

            sb.Append(html);
            sb.Append("</" + elementName + ">");

            html = sb.ToString();

            if (hyperlinkInfo != null && hyperlinkInfo.DeclaredSymbol != null)
            {
                writer.Flush();
                long streamPosition = writer.BaseStream.Length;

                streamPosition += html.IndexOf(hyperlinkInfo.Attributes["id"] + ".html");
                projectGenerator.AddDeclaredSymbol(
                    hyperlinkInfo.DeclaredSymbol,
                    hyperlinkInfo.DeclaredSymbolId,
                    documentRelativeFilePathWithoutHtmlExtension,
                    streamPosition);
            }

            return html;
        }
        private HtmlElementInfo GenerateLinks(Classification.Range range, bool isLargeFile = false)
        {
            var text = range.Text;

            if (range.ClassificationType == Constants.ClassificationLiteral)
            {
                return TryProcessGuid(range);
            }

            if (range.ClassificationType != Constants.ClassificationIdentifier &&
                range.ClassificationType != Constants.ClassificationTypeName &&
                text != "this" &&
                text != "base" &&
                text != "var" &&
                text != "New" &&
                text != "new" &&
                text != "[" &&
                text != "partial" &&
                text != "Partial")
            {
                return null;
            }

            var position = range.ClassifiedSpan.TextSpan.Start;
            var token = Root.FindToken(position, findInsideTrivia: true);
            if (IsZeroLengthArrayAllocation(token))
            {
                projectGenerator.AddReference(
                    this.documentDestinationFilePath,
                    Text,
                    "mscorlib",
                    null,
                    "EmptyArrayAllocation",
                    range.ClassifiedSpan.TextSpan.Start,
                    range.ClassifiedSpan.TextSpan.End,
                    ReferenceKind.EmptyArrayAllocation);
                return null;
            }

            // now that we've passed the empty array allocation check, disable all further new keywords
            if (range.ClassificationType == Constants.ClassificationKeyword && text == "new")
            {
                return null;
            }

            var declaredSymbol = SemanticModel.GetDeclaredSymbol(token.Parent);
            if (declaredSymbol is IParameterSymbol && text == "this")
            {
                // it's a 'this' in the first parameter of an extension method - we don't want it to
                // hyperlink to anything
                return null;
            }

            if (declaredSymbol != null)
            {
                if (token.IsKind(Microsoft.CodeAnalysis.CSharp.SyntaxKind.PartialKeyword) ||
                    token.IsKind(Microsoft.CodeAnalysis.VisualBasic.SyntaxKind.PartialKeyword))
                {
                    if (declaredSymbol is INamedTypeSymbol)
                    {
                        return TryProcessPartialKeyword((INamedTypeSymbol)declaredSymbol);
                    }

                    return null;
                }

                var explicitlyImplementedMember = GetExplicitlyImplementedMember(declaredSymbol);
                if (explicitlyImplementedMember == null)
                {
                    if (token.Span.Contains(position) &&
                        (declaredSymbol.Kind == SymbolKind.Event ||
                         declaredSymbol.Kind == SymbolKind.Field ||
                         declaredSymbol.Kind == SymbolKind.Local ||
                         declaredSymbol.Kind == SymbolKind.Method ||
                         declaredSymbol.Kind == SymbolKind.NamedType ||
                         declaredSymbol.Kind == SymbolKind.Parameter ||
                         declaredSymbol.Kind == SymbolKind.Property ||
                         declaredSymbol.Kind == SymbolKind.TypeParameter
                         ) &&
                        DeclaredSymbols.Add(declaredSymbol))
                    {
                        if ((declaredSymbol.Kind == SymbolKind.Method ||
                            declaredSymbol.Kind == SymbolKind.Property ||
                            declaredSymbol.Kind == SymbolKind.Event) &&
                            !declaredSymbol.IsStatic)
                        {
                            // declarations of overridden members are also "references" to their
                            // base members. This is needed for "Find Overridding Members" and
                            // "Find Implementations"
                            AddReferencesToOverriddenMembers(range, token, declaredSymbol);
                            AddReferencesToImplementedMembers(range, token, declaredSymbol);
                        }

                        return ProcessDeclaredSymbol(declaredSymbol, isLargeFile);
                    }
                }
                else
                {
                    projectGenerator.AddImplementedInterfaceMember(
                        declaredSymbol,
                        explicitlyImplementedMember);
                    return ProcessReference(
                        range,
                        explicitlyImplementedMember,
                        ReferenceKind.InterfaceMemberImplementation);
                }
            }
            else
            {
                return ProcessReference(range, token, isLargeFile);
            }

            return null;
        }
        private HtmlElementInfo TryProcessGuid(Classification.Range range)
        {
            var text = range.Text;
            var spanStart = range.ClassifiedSpan.TextSpan.Start;
            var spanEnd = range.ClassifiedSpan.TextSpan.End;

            if (text.StartsWith("@"))
            {
                text = text.Substring(1);
                spanStart++;
            }

            if (text.StartsWith("\"") && text.EndsWith("\"") && text.Length >= 2)
            {
                spanStart++;
                spanEnd--;
                text = text.Substring(1, text.Length - 2);
            }

            // quick check to reject non-Guids even before trying to parse
            if (text.Length != 32 && text.Length != 36 && text.Length != 38)
            {
                return null;
            }

            Guid guid;
            if (!Guid.TryParse(text, out guid))
            {
                return null;
            }

            var symbolId = guid.ToString();

            var referencesFilePath = Path.Combine(
                SolutionDestinationFolder,
                Constants.GuidAssembly,
                Constants.ReferencesFileName,
                symbolId + ".html");
            string href = Paths.MakeRelativeToFile(referencesFilePath, documentDestinationFilePath);
            href = href.Replace('\\', '/');

            var link = new HtmlElementInfo
            {
                Name = "a",
                Attributes =
                {
                    { "href", href },
                    { "target", "n" },
                },
                DeclaredSymbolId = symbolId
            };

            projectGenerator.AddReference(
                this.documentDestinationFilePath,
                Text,
                Constants.GuidAssembly,
                null,
                symbolId,
                spanStart,
                spanEnd,
                ReferenceKind.GuidUsage);

            return link;
        }