Is there any way to automatically indent VB.NET code in a text file

I would like to correctly defer some VB.NET code contained in a text file. Is there any way to do this?

eg. Start with this:

Public Shared Function CanReachPage(page As String) As Boolean Try Using client = New WebClient() Using stream = client.OpenRead(page) Return True End Using End Using Catch Return False End Try End Function 

end up with this:

 Public Shared Function CanReachPage(page As String) As Boolean Try Using client = New WebClient() Using stream = client.OpenRead(page) Return True End Using End Using Catch Return False End Try End Function 

All I was looking for so far has led me to the IndentedTextWriter Class, but the only examples I have found are manual line indentation as follows: .NET Console TextWriter that understands Indent / Fixed / Tint (IndentLevel)

Extra credit: I would also like to add the correct interval, if possible:

eg Dim i As String="Hello"+"GoodBye" β†’ Dim i As String = "Hello" + "GoodBye"

+8
code-formatting indentation codedom
source share
4 answers

I decided to have a crack when rolling in my own way. There are some extreme cases that this does not work 100%, but it is pretty reliable:

 Public Class VBIndenter Private _classIndents As New List(Of Integer) Private _moduleIndents As New List(Of Integer) Private _subIndents As New List(Of Integer) Private _functionIndents As New List(Of Integer) Private _propertyIndents As New List(Of Integer) Private _structureIndents As New List(Of Integer) Private _enumIndents As New List(Of Integer) Private _usingIndents As New List(Of Integer) Private _withIndents As New List(Of Integer) Private _ifIndents As New List(Of Integer) Private _tryIndents As New List(Of Integer) Private _getIndents As New List(Of Integer) Private _setIndents As New List(Of Integer) Private _forIndents As New List(Of Integer) Private _selectIndents As New List(Of Integer) Private _doIndents As New List(Of Integer) Private _whileIndents As New List(Of Integer) Public Property IndentWidth As Integer = 4 Public Property IndentChar As Char = " "c Public Sub Indent(txt As TextBox) Dim lastLabelIndent As Integer = 0 Dim lastRegionIndent As Integer = 0 Dim currentIndent As Integer = 0 Dim inProperty As Boolean = False Dim lineText As String Dim newLineIndent As Integer Dim lines As String() = txt.Lines For i As Integer = 0 To lines.Count - 1 Dim line = lines(i) 'get the trimmed line without any comments lineText = StripComments(line) 'only change the indent on lines that are code If lineText.Length > 0 Then 'special case for regions and labels - they always have zero indent If lineText.StartsWith("#") Then lastRegionIndent = currentIndent currentIndent = 0 ElseIf lineText.EndsWith(":") Then lastLabelIndent = currentIndent currentIndent = 0 End If 'if we are in a property and we see something If (_propertyIndents.Count > 0) Then If Not lineText.StartsWith("End") Then If lineText.StartsWith("Class ") OrElse lineText.Contains(" Class ") Then _propertyIndents.RemoveAt(_propertyIndents.Count - 1) currentIndent -= 1 ElseIf lineText.StartsWith("Module ") OrElse lineText.Contains(" Module ") Then _propertyIndents.RemoveAt(_propertyIndents.Count - 1) currentIndent -= 1 ElseIf lineText.StartsWith("Sub ") OrElse lineText.Contains(" Sub ") Then _propertyIndents.RemoveAt(_propertyIndents.Count - 1) currentIndent -= 1 ElseIf lineText.StartsWith("Function ") OrElse lineText.Contains(" Function ") Then _propertyIndents.RemoveAt(_propertyIndents.Count - 1) currentIndent -= 1 ElseIf lineText.StartsWith("Property ") OrElse lineText.Contains(" Property ") Then _propertyIndents.RemoveAt(_propertyIndents.Count - 1) currentIndent -= 1 ElseIf lineText.StartsWith("Structure ") OrElse lineText.Contains(" Structure ") Then _propertyIndents.RemoveAt(_propertyIndents.Count - 1) currentIndent -= 1 ElseIf lineText.StartsWith("Enum ") OrElse lineText.Contains(" Enum ") Then _propertyIndents.RemoveAt(_propertyIndents.Count - 1) currentIndent -= 1 End If Else If lineText = "End Class" Then _propertyIndents.RemoveAt(_propertyIndents.Count - 1) End If End If End If If lineText = "End Class" Then currentIndent = _classIndents.Item(_classIndents.Count - 1) _classIndents.RemoveAt(_classIndents.Count - 1) ElseIf lineText = "End Module" Then currentIndent = _moduleIndents.Item(_moduleIndents.Count - 1) _moduleIndents.RemoveAt(_moduleIndents.Count - 1) ElseIf lineText = "End Sub" Then currentIndent = _subIndents.Item(_subIndents.Count - 1) _subIndents.RemoveAt(_subIndents.Count - 1) ElseIf lineText = "End Function" Then currentIndent = _functionIndents.Item(_functionIndents.Count - 1) _functionIndents.RemoveAt(_functionIndents.Count - 1) ElseIf lineText = "End Property" Then currentIndent = _propertyIndents.Item(_propertyIndents.Count - 1) _propertyIndents.RemoveAt(_propertyIndents.Count - 1) ElseIf lineText = "End Try" Then currentIndent = _tryIndents.Item(_tryIndents.Count - 1) _tryIndents.RemoveAt(_tryIndents.Count - 1) ElseIf lineText = "End With" Then currentIndent = _withIndents.Item(_withIndents.Count - 1) _withIndents.RemoveAt(_withIndents.Count - 1) ElseIf lineText = "End Get" Then currentIndent = _getIndents.Item(_getIndents.Count - 1) _getIndents.RemoveAt(_getIndents.Count - 1) ElseIf lineText = "End Set" Then currentIndent = _setIndents.Item(_setIndents.Count - 1) _setIndents.RemoveAt(_setIndents.Count - 1) ElseIf lineText = "End If" Then currentIndent = _ifIndents.Item(_ifIndents.Count - 1) _ifIndents.RemoveAt(_ifIndents.Count - 1) ElseIf lineText = "End Using" Then currentIndent = _usingIndents.Item(_usingIndents.Count - 1) _usingIndents.RemoveAt(_usingIndents.Count - 1) ElseIf lineText = "End Structure" Then currentIndent = _structureIndents.Item(_structureIndents.Count - 1) _structureIndents.RemoveAt(_structureIndents.Count - 1) ElseIf lineText = "End Select" Then currentIndent = _selectIndents.Item(_selectIndents.Count - 1) _selectIndents.RemoveAt(_selectIndents.Count - 1) ElseIf lineText = "End Enum" Then currentIndent = _enumIndents.Item(_enumIndents.Count - 1) _enumIndents.RemoveAt(_enumIndents.Count - 1) ElseIf lineText = "End While" OrElse lineText = "Wend" Then currentIndent = _whileIndents.Item(_whileIndents.Count - 1) _whileIndents.RemoveAt(_whileIndents.Count - 1) ElseIf lineText = "Next" OrElse lineText.StartsWith("Next ") Then currentIndent = _forIndents.Item(_forIndents.Count - 1) _forIndents.RemoveAt(_forIndents.Count - 1) ElseIf lineText = "Loop" OrElse lineText.StartsWith("Loop ") Then currentIndent = _doIndents.Item(_doIndents.Count - 1) _doIndents.RemoveAt(_doIndents.Count - 1) ElseIf lineText.StartsWith("Else") Then currentIndent = _ifIndents.Item(_ifIndents.Count - 1) ElseIf lineText.StartsWith("Catch") Then currentIndent = _tryIndents.Item(_tryIndents.Count - 1) ElseIf lineText.StartsWith("Case") Then currentIndent = _selectIndents.Item(_selectIndents.Count - 1) + 1 ElseIf lineText = "Finally" Then currentIndent = _tryIndents.Item(_tryIndents.Count - 1) End If End If 'find the current indent newLineIndent = currentIndent * Me.IndentWidth 'change the indent of the current line line = New String(IndentChar, newLineIndent) & line.TrimStart lines(i) = line If lineText.Length > 0 Then If lineText.StartsWith("#") Then currentIndent = lastRegionIndent ElseIf lineText.EndsWith(":") Then currentIndent = lastLabelIndent End If If Not lineText.StartsWith("End") Then If (lineText.StartsWith("Class ") OrElse lineText.Contains(" Class ")) Then _classIndents.Add(currentIndent) currentIndent += 1 ElseIf (lineText.StartsWith("Module ") OrElse lineText.Contains(" Module ")) Then _moduleIndents.Add(currentIndent) currentIndent += 1 ElseIf (lineText.StartsWith("Sub ") OrElse lineText.Contains(" Sub ")) Then _subIndents.Add(currentIndent) currentIndent += 1 ElseIf (lineText.StartsWith("Function ") OrElse lineText.Contains(" Function ")) Then _functionIndents.Add(currentIndent) currentIndent += 1 ElseIf (lineText.StartsWith("Property ") OrElse lineText.Contains(" Property ")) Then _propertyIndents.Add(currentIndent) currentIndent += 1 ElseIf (lineText.StartsWith("Structure ") OrElse lineText.Contains(" Structure ")) Then _structureIndents.Add(currentIndent) currentIndent += 1 ElseIf (lineText.StartsWith("Enum ") OrElse lineText.Contains(" Enum ")) Then _enumIndents.Add(currentIndent) currentIndent += 1 ElseIf lineText.Contains("Using ") Then _usingIndents.Add(currentIndent) currentIndent += 1 ElseIf lineText.StartsWith("Select Case") Then _selectIndents.Add(currentIndent) currentIndent += 1 ElseIf lineText = "Try" Then _tryIndents.Add(currentIndent) currentIndent += 1 ElseIf lineText = "Get" Then _getIndents.Add(currentIndent) currentIndent += 1 ElseIf lineText.StartsWith("Set") AndAlso Not lineText.Contains("=") Then _setIndents.Add(currentIndent) currentIndent += 1 ElseIf lineText.StartsWith("With") Then _withIndents.Add(currentIndent) currentIndent += 1 ElseIf lineText.StartsWith("If") AndAlso lineText.EndsWith("Then") Then _ifIndents.Add(currentIndent) currentIndent += 1 ElseIf lineText.StartsWith("For") Then _forIndents.Add(currentIndent) currentIndent += 1 ElseIf lineText.StartsWith("While") Then _whileIndents.Add(currentIndent) currentIndent += 1 ElseIf lineText.StartsWith("Do") Then _doIndents.Add(currentIndent) currentIndent += 1 ElseIf lineText.StartsWith("Case") Then currentIndent += 1 ElseIf lineText.StartsWith("Else") Then currentIndent = _ifIndents.Item(_ifIndents.Count - 1) + 1 ElseIf lineText.StartsWith("Catch") Then currentIndent = _tryIndents.Item(_tryIndents.Count - 1) + 1 ElseIf lineText = "Finally" Then currentIndent = _tryIndents.Item(_tryIndents.Count - 1) + 1 End If End If End If Next 'update the textbox txt.Lines = lines End Sub Private Function StripComments(ByVal code As String) As String If code.IndexOf("'"c) >= 0 Then code = code.Substring(0, code.IndexOf("'"c)) End If Return code.Trim End Function End Class 

Using:

Put some code in a TextBox ( TextBox1 ), then call the indenter as follows:

 Dim id As New VBIndenter id.Indent(TextBox1) 
0
source share

If you are using Visual Studio (I am looking at VS 2010 at the moment, I don’t know what the previous versions did before), then you can go to the Edit-> Advanced-> Format document, and it should take care of the indentation and distance for you .

Note that this works for any type of document that Visual Studio understands. I regularly use this trick to format XML documents into something legible.

+2
source share

If you are using pre-release software, you can use Roslyn :

 Dim parsed = Syntax.ParseCompilationUnit(text) Dim normalized = parsed.NormalizeWhitespace() Console.WriteLine(normalized) 
+1
source share

One way to do this is to create a parser and a beautiful tool. The parser reads the source and builds the AST, fixing the essence of the program structure. A pretty printer takes a tree and regenerates the output based on the structure; thus, it is β€œeasy” to get structured output. As a key hint at each level, the language structure (classes, methods, blocks, loops, conventions), prettyprinter can print text with fingerprints to give a good indentation structure.

Parsing and simpling are quite complex topics. Instead of repeating all of this here, you can see my Answer to the question of how to parse, followed by a discussion of how to build an AST . Prepress is not well known, but this SO answer to my question gives a fairly complete description of how to do this.

Then you have the difficulty of determining the actual grammar of VB.net. It takes a lot of work to extract from the reference documentation ... and this is not entirely correct, so you need to check your parser with a lot of code to convince yourself of this. This part, unfortunately, just sweats.

Given the niceprinter program, OP can simply run it as a file formatting process.

If you do all this, then yes, you can format the text of VB.net. Our (stand-alone) VB.net formatter ("DMSFormat ...") does this above to achieve beautiful printing. A.

Given the file "vb_example.net":

 Module Test Public Shared Function CanReachPage(page As String) As Boolean Try Using client = New WebClient() Using stream = client.OpenRead(page) Return True End Using End Using Catch Return False End Try End Function End Module 

Following:

 C:>DMSFormat VisualBasic~VBdotNet C:\temp\vb_example.net 

gives:

 VisualBasic~VBdotNet Formatter/Obfuscator Version 1.2.1 Copyright (C) 2010 Semantic Designs, Inc Powered by DMS (R) Software Reengineering Toolkit Parsing C:\temp\vb_example.net [encoding ISO-8859-1] Module Test Public Shared Function CanReachPage(page As String) As Boolean Try Using client = New WebClient() Using stream = client.OpenRead(page) Return True End Using End Using Catch Return False End Try End Function End Module 

which is identical to what OP wanted in his example.

You can easily redirect the contents of a formatted program to a file.

You can provide the tool with a project file, and it will format as many files as you specify in the project file at once.

The formatter integrates the full VB.net parser, and our own highly printable mechanism. It analyzes the source text accurately (including the strange character encodings). Since it uses a robust parser and prettyprinter, it will not break the code.

The eval version works with files of several hundred lines of code. This may be exactly what you need.

I would provide a link, but SO didn't seem to like it. You can find it through my biography.

-3
source share

All Articles