165 lines
6.8 KiB
VB.net
165 lines
6.8 KiB
VB.net
Imports System.Collections.Generic
|
|
Imports System.Drawing
|
|
Imports System.Windows.Forms
|
|
Imports DevExpress.Office.Utils
|
|
Imports DevExpress.XtraRichEdit.API.Native
|
|
Imports DevExpress.XtraRichEdit.Services
|
|
Imports System.Linq
|
|
Imports System.Text.RegularExpressions
|
|
|
|
Namespace SQLEditor
|
|
Public Class SQLSyntaxHighlightService
|
|
Implements ISyntaxHighlightService
|
|
|
|
Private ReadOnly document As Document
|
|
|
|
Private _keywords As Regex
|
|
Private _quotedString As New Regex("'([^']|'')*'")
|
|
Private _commentedString As New Regex("(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)")
|
|
Private _placeholderString As New Regex("{#(\w+)#([\:\.\w\s_/-]+)}")
|
|
|
|
Private ReadOnly keywords As New List(Of String) From {
|
|
"INSERT",
|
|
"SELECT",
|
|
"CREATE",
|
|
"TABLE",
|
|
"USE",
|
|
"IDENTITY",
|
|
"JOIN",
|
|
"ON",
|
|
"OFF",
|
|
"NOT",
|
|
"NULL",
|
|
"WITH",
|
|
"SET",
|
|
"GO",
|
|
"DECLARE",
|
|
"EXECUTE",
|
|
"NVARCHAR",
|
|
"FROM",
|
|
"INTO",
|
|
"VALUES",
|
|
"WHERE",
|
|
"AND"
|
|
}
|
|
|
|
Public Sub New(ByVal document As Document)
|
|
Me.document = document
|
|
|
|
Me._keywords = New Regex("\b(" & String.Join("|", keywords.Select(Function(w) Regex.Escape(w))) & ")\b")
|
|
End Sub
|
|
Public Sub ForceExecute() Implements ISyntaxHighlightService.ForceExecute
|
|
Execute()
|
|
End Sub
|
|
Public Sub Execute() Implements ISyntaxHighlightService.Execute
|
|
Dim tSqltokens As List(Of SyntaxHighlightToken) = ParseTokens()
|
|
document.ApplySyntaxHighlight(tSqltokens)
|
|
End Sub
|
|
|
|
Private Function ParseTokens() As List(Of SyntaxHighlightToken)
|
|
Dim tokens As New List(Of SyntaxHighlightToken)()
|
|
Dim ranges() As DocumentRange = Nothing
|
|
|
|
' search for quoted strings
|
|
ranges = TryCast(document.FindAll(_quotedString).GetAsFrozen(), DocumentRange())
|
|
For i As Integer = 0 To ranges.Length - 1
|
|
tokens.Add(CreateToken(ranges(i).Start.ToInt(), ranges(i).End.ToInt(), Color.Red))
|
|
Next i
|
|
|
|
'Extract all keywords
|
|
ranges = TryCast(document.FindAll(_keywords).GetAsFrozen(), DocumentRange())
|
|
For j As Integer = 0 To ranges.Length - 1
|
|
If Not IsRangeInTokens(ranges(j), tokens) Then
|
|
tokens.Add(CreateToken(ranges(j).Start.ToInt(), ranges(j).End.ToInt(), Color.Blue))
|
|
End If
|
|
Next j
|
|
|
|
'Find all placeholders
|
|
ranges = TryCast(document.FindAll(_placeholderString).GetAsFrozen(), DocumentRange())
|
|
For j As Integer = 0 To ranges.Length - 1
|
|
If Not IsRangeInTokens(ranges(j), tokens) Then
|
|
tokens.Add(CreateToken(ranges(j).Start.ToInt(), ranges(j).End.ToInt(), Color.DarkTurquoise))
|
|
End If
|
|
Next j
|
|
|
|
'Find all comments
|
|
ranges = TryCast(document.FindAll(_commentedString).GetAsFrozen(), DocumentRange())
|
|
For j As Integer = 0 To ranges.Length - 1
|
|
If Not IsRangeInTokens(ranges(j), tokens) Then
|
|
tokens.Add(CreateToken(ranges(j).Start.ToInt(), ranges(j).End.ToInt(), Color.Green))
|
|
End If
|
|
Next j
|
|
|
|
' order tokens by their start position
|
|
tokens.Sort(New SyntaxHighlightTokenComparer())
|
|
|
|
' fill in gaps in document coverage
|
|
tokens = CombineWithPlainTextTokens(tokens)
|
|
Return tokens
|
|
End Function
|
|
|
|
'Parse the remaining text into tokens:
|
|
Private Function CombineWithPlainTextTokens(ByVal tokens As List(Of SyntaxHighlightToken)) As List(Of SyntaxHighlightToken)
|
|
Dim result As New List(Of SyntaxHighlightToken)(tokens.Count * 2 + 1)
|
|
Dim documentStart As Integer = Me.document.Range.Start.ToInt()
|
|
Dim documentEnd As Integer = Me.document.Range.End.ToInt()
|
|
If tokens.Count = 0 Then
|
|
result.Add(CreateToken(documentStart, documentEnd, Color.Black))
|
|
Else
|
|
Dim firstToken As SyntaxHighlightToken = tokens(0)
|
|
If documentStart < firstToken.Start Then
|
|
result.Add(CreateToken(documentStart, firstToken.Start, Color.Black))
|
|
End If
|
|
result.Add(firstToken)
|
|
For i As Integer = 1 To tokens.Count - 1
|
|
Dim token As SyntaxHighlightToken = tokens(i)
|
|
Dim prevToken As SyntaxHighlightToken = tokens(i - 1)
|
|
If prevToken.End <> token.Start Then
|
|
result.Add(CreateToken(prevToken.End, token.Start, Color.Black))
|
|
End If
|
|
result.Add(token)
|
|
Next i
|
|
Dim lastToken As SyntaxHighlightToken = tokens(tokens.Count - 1)
|
|
If documentEnd > lastToken.End Then
|
|
result.Add(CreateToken(lastToken.End, documentEnd, Color.Black))
|
|
End If
|
|
End If
|
|
Return result
|
|
End Function
|
|
|
|
'Create a token from the retrieved range and specify its forecolor
|
|
Private Function CreateToken(ByVal start As Integer, ByVal [end] As Integer, ByVal foreColor As Color) As SyntaxHighlightToken
|
|
Dim properties As New SyntaxHighlightProperties()
|
|
properties.ForeColor = foreColor
|
|
Return New SyntaxHighlightToken(start, [end] - start, properties)
|
|
End Function
|
|
|
|
'Check whether tokens intersect each other
|
|
Private Function IsRangeInTokens(ByVal range As DocumentRange, ByVal tokens As List(Of SyntaxHighlightToken)) As Boolean
|
|
Return tokens.Any(Function(t) IsIntersect(range, t))
|
|
End Function
|
|
Private Function IsIntersect(ByVal range As DocumentRange, ByVal token As SyntaxHighlightToken) As Boolean
|
|
Dim start As Integer = range.Start.ToInt()
|
|
If start >= token.Start AndAlso start < token.End Then
|
|
Return True
|
|
End If
|
|
Dim [end] As Integer = range.End.ToInt() - 1
|
|
If [end] >= token.Start AndAlso [end] < token.End Then
|
|
Return True
|
|
End If
|
|
If start < token.Start AndAlso [end] >= token.End Then
|
|
Return True
|
|
End If
|
|
Return False
|
|
End Function
|
|
End Class
|
|
|
|
'Compare token's initial positions to sort them
|
|
Public Class SyntaxHighlightTokenComparer
|
|
Implements IComparer(Of SyntaxHighlightToken)
|
|
|
|
Public Function Compare(ByVal x As SyntaxHighlightToken, ByVal y As SyntaxHighlightToken) As Integer Implements IComparer(Of SyntaxHighlightToken).Compare
|
|
Return x.Start - y.Start
|
|
End Function
|
|
End Class
|
|
End Namespace |