我想正确缩进文本文件中包含的一些VB.NET代码。有没有办法做到这一点?
e.g。 从这开始:
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
完成这个:
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
我搜索到的所有内容到目前为止都引导我进入IndentedTextWriter类,但我发现的唯一示例是手动缩进这样的行:.NET Console TextWriter that Understands Indent/Unindent/IndentLevel
额外信用:如果可能的话,我还想添加正确的间距:
例如Dim i As String="Hello"+"GoodBye"
- > Dim i As String = "Hello" + "GoodBye"
答案 0 :(得分:2)
如果您正在使用Visual Studio(我现在正在关注VS 2010;我不知道早期版本的功能),那么您可以转到Edit-> Advanced-> Format Document and它应该照顾你的压痕和间距。
请注意,这适用于Visual Studio可以理解的任何类型的文档。我经常使用这个技巧将XML文档格式化为易读的东西。
答案 1 :(得分:1)
如果您对使用预发布软件感到满意,可以使用Roslyn:
Dim parsed = Syntax.ParseCompilationUnit(text)
Dim normalized = parsed.NormalizeWhitespace()
Console.WriteLine(normalized)
答案 2 :(得分:0)
我决定自己动手。有一些边缘情况,这不是100%的工作,但它非常可靠:
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
用法:
将一些代码放入TextBox(TextBox1
),然后像这样调用压头:
Dim id As New VBIndenter
id.Indent(TextBox1)
答案 3 :(得分:-3)
实现此目的的一种方法是构建解析器和prettyprinter。 解析器读取源并构建捕获精华的AST 程序结构。漂亮的打印机取树, 并根据结构重新生成输出;因此它“很容易” 得到结构化的输出。作为关键提示,适用于每个级别 语言结构(类,方法,块,循环,条件), prettyprinter可以缩进漂亮的文本 良好的压痕结构。
解析和漂亮都是非常复杂的主题。 你可以看到,而不是重复所有这一切 我的SO answer on how to parse, with follow on discussion on how to build an AST。 Prettyprinting并不是那么出名, 但是this SO answer of mine提供了非常完整的描述 怎么做。
然后你有确定VB.net的实际语法的复杂性。这需要从参考文档中提取大量工作......并且它不太正确,因此您需要针对大量代码验证解析器以说服自己正确。不幸的是,这部分只是汗水。
鉴于一个漂亮的打印机程序,OP可以简单地将其作为格式化文件的过程启动。
如果您这样做,那么您可以格式化VB.net文本。我们的(独立的)VB.net格式化程序(“DMSFormat ...”)执行上述操作以实现漂亮打印。
给定文件“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
以下内容:
C:>DMSFormat VisualBasic~VBdotNet C:\temp\vb_example.net
产生
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
与OP在他的例子中想要的相同。
您可以轻松地将格式化的节目内容定向到文件。
您可以为工具提供项目文件,它将格式化您指定的文件 在项目文件中一次。
格式化程序集成了一个完整的VB.net解析器, 和我们自己的漂亮印刷机器。它解析 精确的源文本(包括奇怪的字符 编码)。因为它使用了可靠的解析器和prettyprinter,所以它不会破坏代码。
eval版本适用于几百行代码的文件。这可能正是你所需要的。
我提供了一个链接但是SO似乎不喜欢这样。你可以通过我的生物找到这个。