File: Compilation\DocumentationComments\DocumentationCommentCompiler.TextUtils.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
 
Imports System.Reflection
Imports System.Resources
Imports System.Runtime.InteropServices
Imports System.Xml
Imports Microsoft.CodeAnalysis.VisualBasic
 
Namespace Microsoft.CodeAnalysis.VisualBasic
    Partial Public Class VisualBasicCompilation
 
        Partial Friend Class DocumentationCommentCompiler
            Inherits VisualBasicSymbolVisitor
 
            Private Sub Indent()
                Me._writer.Indent()
            End Sub
 
            Private Sub Unindent()
                Me._writer.Unindent()
            End Sub
 
            Private Sub WriteLine(message As String)
                Me._writer.WriteLine(message)
            End Sub
 
            Private Sub Write(message As String)
                Me._writer.Write(message)
            End Sub
 
            ''' <summary>
            ''' Given the full text of a documentation comment, strip off the comment 
            ''' punctuation (''') and add appropriate indentations.
            ''' </summary>
            Private Function FormatComment(substitutedText As String) As String
                Me._writer.BeginTemporaryString()
                WriteFormattedComment(substitutedText)
                Return Me._writer.GetAndEndTemporaryString()
            End Function
 
            ''' <summary>
            ''' Find the first non-whitespace character in a given substring.
            ''' </summary>
            ''' <param name="str">The string to search</param>
            ''' <param name="start">The start index</param>
            ''' <param name="end">The last index (non-inclusive)</param>
            ''' <returns>The index of the first non-whitespace char after index 
            ''' start in the string up to, but not including the end index</returns>
            Private Shared Function GetIndexOfFirstNonWhitespaceChar(str As String, start As Integer, [end] As Integer) As Integer
                Debug.Assert(start >= 0)
                Debug.Assert(start <= str.Length)
                Debug.Assert([end] >= 0)
                Debug.Assert([end] <= str.Length)
                Debug.Assert([end] >= start)
 
                While start < [end] And Char.IsWhiteSpace(str(start))
                    start += 1
                End While
 
                Return start
            End Function
 
            ''' <summary>
            ''' Given a string which may contain newline sequences, get the index of the first newline
            ''' sequence beginning at the given starting index.
            ''' </summary>
            ''' <param name="str">The string to split.</param>
            ''' <param name="start">The starting index within the string.</param>
            ''' <param name="newLineLength">The length of the newline sequence discovered. 0 if the end of the string was reached, otherwise either 1 or 2 chars</param>
            ''' <returns>The index of the start of the first newline sequence following the start index</returns>
            Private Shared Function IndexOfNewLine(str As String, start As Integer, <Out> ByRef newLineLength As Integer) As Integer
                Dim len As Integer = str.Length
                While start < len
                    Select Case str(start)
                        Case ChrW(13)
                            If (start + 1) < str.Length AndAlso str(start + 1) = ChrW(10) Then
                                newLineLength = 2
                            Else
                                newLineLength = 1
                            End If
                            Return start
 
                        Case ChrW(10)
                            newLineLength = 1
                            Return start
                    End Select
                    start += 1
                End While
 
                newLineLength = 0
                Return start
            End Function
 
            ''' <summary>
            ''' Given the full text of a single-line style documentation comment, for each line, strip off
            ''' the comment punctuation (''') and flatten the text.
            ''' </summary>
            Private Sub WriteFormattedComment(text As String)
                ' PERF: Avoid allocating intermediate strings e.g. via Split, Trim or Substring
                Dim substringStart As Integer = 3
                Dim start As Integer = 0
                Dim len As Integer = text.Length
                While start < len
                    Dim newLineLength As Integer = 0
                    Dim [end] As Integer = IndexOfNewLine(text, start, newLineLength)
 
                    Dim trimStart As Integer = GetIndexOfFirstNonWhitespaceChar(text, start, [end])
                    If trimStart < [end] AndAlso text(trimStart) = "'"c Then
                        trimStart += substringStart
                    End If
 
                    Me._writer.WriteSubString(text, trimStart, [end] - trimStart, appendNewLine:=True)
                    start = [end] + newLineLength
                End While
            End Sub
 
            ''' <remarks>
            ''' WORKAROUND:
            ''' We're taking a dependency on the location and structure of a framework assembly resource.  This is not a robust solution.
            ''' 
            ''' Possible alternatives:
            ''' 1) Polish our XML parser until it matches MSXML.  We don't want to reinvent the wheel.
            ''' 2) Build a map that lets us go from XML string positions back to source positions.  
            ''' This is what the native compiler did, and it was a lot of work.  We'd also still need to modify the message.
            ''' 3) Do not report a diagnostic.  This is very unhelpful.
            ''' 4) Report a vague diagnostic (i.e. there's a problem somewhere in this doc comment).  This is relatively unhelpful.
            ''' 5) Always report the message in English, so that we can pull it apart without needing to mess with resource files.
            ''' This engenders a lot of ill will.
            ''' 6) Report the exception message without modification and (optionally) include the text with respect to which the
            ''' position is specified.  This looks amateurish.            
            ''' </remarks>
            Private Shared Function GetDescription(e As XmlException) As String
                Dim message As String = e.Message
                Try
                    Dim manager As New ResourceManager("System.Xml", GetType(XmlException).GetTypeInfo().Assembly)
                    Dim locationTemplate As String = manager.GetString("Xml_MessageWithErrorPosition")
                    Dim locationString As String =
                        String.Format(locationTemplate, "", e.LineNumber, e.LinePosition) ' first arg is where the problem description goes
 
                    Dim position As Integer = message.IndexOf(locationString, StringComparison.Ordinal) ' Expect exact match
                    Return If(position < 0, message, message.Remove(position, locationString.Length))
                Catch ex As Exception
                    Debug.Assert(False, "If we hit this, then we might need to think about a different workaround " +
                                        "for stripping the location out of the message.")
 
                    ' If anything at all goes wrong, just return the message verbatim.  It probably
                    ' contains an invalid position, but it's better than nothing.
                    Return message
                End Try
            End Function
 
        End Class
 
    End Class
End Namespace